summaryrefslogtreecommitdiff
path: root/vchat-protocol.c
diff options
context:
space:
mode:
authorerdgeist <>2003-02-12 17:48:37 +0000
committererdgeist <>2003-02-12 17:48:37 +0000
commitdea6bf757aa9a875eab35b2b650412e7605f1308 (patch)
tree14ed8374c3a3862529313088375693a7de70d3a7 /vchat-protocol.c
CVS moved to erdgeist.org
Diffstat (limited to 'vchat-protocol.c')
-rwxr-xr-xvchat-protocol.c1060
1 files changed, 1060 insertions, 0 deletions
diff --git a/vchat-protocol.c b/vchat-protocol.c
new file mode 100755
index 0000000..05abc26
--- /dev/null
+++ b/vchat-protocol.c
@@ -0,0 +1,1060 @@
1/*
2 * vchat-client - alpha version
3 * vchat-protocol.c - handling of server connection & messages
4 *
5 * Copyright (C) 2001 Andreas Kotes <count@flatline.de>
6 *
7 * This program is free software. It can be redistributed and/or modified,
8 * provided that this copyright notice is kept intact. This program is
9 * distributed in the hope that it will be useful, but without any warranty;
10 * without even the implied warranty of merchantability or fitness for a
11 * particular purpose. In no event shall the copyright holder be liable for
12 * any direct, indirect, incidental or special damages arising in any way out
13 * of the use of this software.
14 *
15 */
16
17/* general includes */
18#include <unistd.h>
19#include <errno.h>
20#include <stdio.h>
21#include <netdb.h>
22#include <string.h>
23#include <sys/types.h>
24#include <sys/socket.h>
25#include <netinet/in.h>
26#include <readline/readline.h>
27#include <openssl/ssl.h>
28#include <openssl/err.h>
29
30/* local includes */
31#include "vchat.h"
32
33/* version of this module */
34unsigned char *vchat_io_version = "$Id$";
35
36/* externally used variables */
37int serverfd = -1;
38unsigned int usingcert = 1;
39
40/* locally global variables */
41/* SSL-connection */
42static SSL *sslconn = NULL;
43
44/* declaration of an OpenSSL function which isn't exported by the includes,
45 * but by the library. we use it to set the accepted list of ciphers */
46STACK_OF(SSL_CIPHER) * ssl_create_cipher_list (const SSL_METHOD * meth, STACK_OF (SSL_CIPHER) ** pref, STACK_OF (SSL_CIPHER) ** sorted, const unsigned char *rule_str);
47
48static unsigned char *sslpubkey = NULL; /* servers public key extracted from X.509 certificate */
49static int sslpubkeybits = 0; /* length of server public key */
50
51/* declaration of local helper functions */
52static void usersignon (unsigned char *);
53static void usersignoff (unsigned char *);
54static void usernickchange (unsigned char *);
55static void userjoin (unsigned char *);
56static void userleave (unsigned char *);
57static void receivenicks (unsigned char *message);
58static void justloggedin (unsigned char *message);
59static void nickerr (unsigned char *message);
60static void login (unsigned char *message);
61static void anonlogin (unsigned char *message);
62static void topicinfo (unsigned char *message);
63static void pubaction (unsigned char *message);
64static void idleprompt (unsigned char *message);
65static void topicchange (unsigned char *message);
66static void pmnotsent (unsigned char *message);
67
68/* declaration of server message array */
69#include "vchat-messages.h"
70
71/* status-variable from vchat-client.c
72 * eventloop is done as long as this is true */
73extern int status;
74
75int usessl = 1;
76
77/* connects to server */
78int
79vcconnect (unsigned char *server, unsigned int port)
80{
81 /* used for tilde expansion of cert & key filenames */
82 unsigned char *tildex = NULL;
83 /* buffer for X.509 subject of server certificate */
84 unsigned char subjbuf[256];
85 /* variables used to split the subject */
86 unsigned char *subjv = NULL;
87 unsigned char *subjn = NULL;
88 unsigned char *subjc = NULL;
89 unsigned char *subjh = NULL;
90 /* pointer to key in certificate */
91 EVP_PKEY *certpubkey = NULL;
92 /* temporary result */
93 int result;
94 /* servers hostentry */
95 struct hostent *serverhe;
96 /* servers sockaddr */
97 struct sockaddr_in serversi;
98 /* SSL-context */
99 SSL_CTX *sslctx = NULL;
100 /* SSL server certificate */
101 X509 *sslserv = NULL;
102 /* SSL method function */
103 SSL_METHOD *sslmeth = NULL;
104
105 /* pointer to tilde-expanded certificate/keyfile-names */
106 unsigned char *certfile = NULL, *keyfile = NULL;
107
108 /* variable for verify return */
109 long verify;
110
111 /* get host-entry for server */
112 if ((serverhe = gethostbyname (server)) == NULL)
113 return 0;
114
115 /* get socket */
116 if ((serverfd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
117 return 0;
118
119 /* initialize datastructure for connect */
120 serversi.sin_family = AF_INET;
121 serversi.sin_port = htons (port);
122 serversi.sin_addr = *((struct in_addr *) serverhe->h_addr);
123 memset (&(serversi.sin_zero), 0, 8);
124
125 /* attempt connect */
126 if (connect (serverfd, (struct sockaddr *) &serversi, sizeof (struct sockaddr)) == -1)
127 return 0;
128
129 /* inform user */
130 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_CONNECTED), server, port);
131 writechan (tmpstr);
132
133 usessl = getintoption(CF_USESSL);
134
135 /* do we want to use SSL? */
136 if (usessl)
137 {
138 /* set ssl method -> SSLv2, SSLv3 & TLSv1 client */
139 sslmeth = SSLv23_client_method ();
140 /* generate new SSL context */
141 sslctx = SSL_CTX_new (sslmeth);
142
143 /* set passphrase-callback to own function from vchat-ui.c */
144 SSL_CTX_set_default_passwd_cb (sslctx, passprompt);
145
146 /* set our list of accepted ciphers */
147 ssl_create_cipher_list (sslctx->method, &(sslctx->cipher_list), &(sslctx->cipher_list_by_id), getstroption (CF_CIPHERSUITE));
148
149 /* get name of certificate file */
150 certfile = getstroption (CF_CERTFILE);
151
152 /* do we have a certificate file? */
153 if (certfile)
154 {
155 /* does the filename start with a tilde? expand it! */
156 if (certfile[0] == '~')
157 tildex = tilde_expand (certfile);
158 else
159 tildex = certfile;
160
161 if (usingcert) {
162 /* try to load certificate */
163 result = SSL_CTX_use_certificate_file (sslctx, tildex, SSL_FILETYPE_PEM);
164 if (!result)
165 {
166 /* failed, inform user */
167 snprintf (tmpstr, TMPSTRSIZE, "!! Loading user certificate fails: %s", ERR_error_string (ERR_get_error (), NULL));
168 writecf (FS_ERR,tmpstr);
169 }
170 else
171 {
172 /* get name of key file */
173 keyfile = getstroption (CF_KEYFILE);
174
175 /* if we don't have a key file, the key may be in the cert file */
176 if (!keyfile)
177 keyfile = certfile;
178
179 /* does the filename start with a tilde? expand it! */
180 if (keyfile[0] == '~')
181 tildex = tilde_expand (keyfile);
182 else
183 tildex = keyfile;
184
185 /* try to load key (automatically asks for passphrase, if encrypted */
186 result = SSL_CTX_use_PrivateKey_file (sslctx, tildex, SSL_FILETYPE_PEM);
187 if (!result)
188 {
189 /* failed, inform user */
190 snprintf (tmpstr, TMPSTRSIZE, "!! Loading private key fails: %s", ERR_error_string (ERR_get_error (), NULL));
191 writecf (FS_ERR,tmpstr);
192 }
193 else
194 {
195 /* check if OpenSSL thinks key & cert belong together */
196 result = SSL_CTX_check_private_key (sslctx);
197 if (!result)
198 {
199 /* they don't, inform user */
200 snprintf (tmpstr, TMPSTRSIZE, "!! Verifying key and certificate fails: %s", ERR_error_string (ERR_get_error (), NULL));
201 writecf (FS_ERR,tmpstr);
202 }
203 }
204 }
205 }
206 }
207
208 /* don't worry to much about servers X.509 certificate chain */
209 SSL_CTX_set_verify_depth (sslctx, 0);
210
211 /* debug massage about verify mode */
212 snprintf (tmpstr, TMPSTRSIZE, "# Connecting with verify depth %d in mode %d", SSL_CTX_get_verify_depth (sslctx), SSL_CTX_get_verify_mode (sslctx));
213 writecf (FS_DBG,tmpstr);
214
215 /* generate new SSL connection object and associate
216 * filedescriptor of server connection */
217 sslconn = SSL_new (sslctx);
218 SSL_set_fd (sslconn, serverfd);
219
220 /* try SSL handshake */
221 if (SSL_connect (sslconn) <= 0)
222 {
223 /* no SSL connection possible */
224 snprintf (tmpstr, TMPSTRSIZE, "!! SSL Connect fails: %s", ERR_error_string (ERR_get_error (), NULL));
225 writecf (FS_ERR,tmpstr);
226 return 0;
227 }
228
229 /* debug message about used symmetric cipher */
230 snprintf (tmpstr, TMPSTRSIZE, "# SSL connection uses %s cipher (%d bits)", SSL_get_cipher_name (sslconn), SSL_get_cipher_bits (sslconn, NULL));
231 writecf (FS_DBG,tmpstr);
232
233 /* if we can't get the servers certificate something is damn wrong */
234 if (!(sslserv = SSL_get_peer_certificate (sslconn)))
235 return 0;
236
237 /* ugly dump of server certificate */
238 /* TODO: make this happen elsewhere, and preferably only when user wants
239 * or a new key needs to be added to the known_hosts */
240 writecf (FS_DBG,"# SSL Server information:");
241 /* copy X.509 to local buffer */
242 strncpy (subjbuf, sslserv->name, 256);
243 /* split a subject line and print the fullname/value pairs */
244 subjv = &subjbuf[1];
245 while (subjv)
246 {
247 subjc = strchr (subjv, '=');
248 subjc[0] = '\0';
249 subjc++;
250 /* yeah, ugly */
251 if (!strcasecmp ("C", subjv))
252 {
253 subjn = "Country ";
254 }
255 else if (!strcasecmp ("ST", subjv))
256 {
257 subjn = "State ";
258 }
259 else if (!strcasecmp ("L", subjv))
260 {
261 subjn = "Location ";
262 }
263 else if (!strcasecmp ("O", subjv))
264 {
265 subjn = "Organization";
266 }
267 else if (!strcasecmp ("OU", subjv))
268 {
269 subjn = "Organiz.unit";
270 }
271 else if (!strcasecmp ("CN", subjv))
272 {
273 subjn = "Common name ";
274 subjh = subjc;
275 }
276 else if (!strcasecmp ("Email", subjv))
277 {
278 subjn = "Emailaddress";
279 }
280 else
281 {
282 subjn = "UNKNOWN ";
283 }
284 subjv = strchr (subjc, '/');
285 if (subjv)
286 {
287 subjv[0] = '\0';
288 subjv++;
289 }
290 /* print value pair */
291 snprintf (tmpstr, TMPSTRSIZE, "# %s: %s", subjn, subjc);
292 writecf (FS_DBG,tmpstr);
293 }
294
295 /* check if verifying the server's certificate yields any errors */
296 verify = SSL_get_verify_result (sslconn);
297 if (verify)
298 {
299 /* it does - yield a warning to the user */
300 snprintf (tmpstr, TMPSTRSIZE, "!! Certificate verification fails: %s", X509_verify_cert_error_string (verify));
301 writecf (FS_ERR,tmpstr);
302 }
303
304 /* check if servers name in certificate matches the hostname we connected to */
305 if (subjh)
306 if (strcasecmp (server, subjh))
307 {
308 /* it doesn't - yield a warning and show difference */
309 writecf (FS_ERR,"!! Server name does not match name in presented certificate!");
310 snprintf (tmpstr, TMPSTRSIZE, "!! '%s' != '%s'", server, subjh);
311 writecf (FS_ERR,tmpstr);
312 }
313
314 /* try to get the servers public key */
315 certpubkey = X509_get_pubkey (sslserv);
316 if (certpubkey == NULL)
317 {
318 /* huh, we can't? */
319 writecf (FS_ERR,"!! Can't get the public key associated to the certificate");
320 }
321 /* check what type of key we've got here */
322 else if (certpubkey->type == EVP_PKEY_RSA)
323 {
324 /* RSA key, convert bignum values from OpenSSL's structures to
325 * something readable */
326 sslpubkey = BN_bn2hex (certpubkey->pkey.rsa->n);
327 sslpubkeybits = BN_num_bits (certpubkey->pkey.rsa->n);
328 /* dump keylength and hexstring of servers key to user */
329 snprintf (tmpstr, TMPSTRSIZE, "# RSA public key%s: %d %s", (certpubkey->pkey.rsa-> d) ? " (private key available)" : "", sslpubkeybits, sslpubkey);
330 writecf (FS_DBG,tmpstr);
331 /* TODO: known_hosts check here ... */
332 }
333 else if (certpubkey->type == EVP_PKEY_DSA)
334 {
335 /* Can't handle (and didn't encounter) DSA keys */
336 writecf (FS_ERR,"# DSA Public Key (output currently not supported)");
337 /* TODO: known_hosts check here ... */
338 }
339 else
340 {
341 writecf (FS_ERR,"# Public Key type not supported");
342 /* TODO: fail known_hosts check ... */
343 }
344 }
345
346 /* if we didn't fail until now, we've got a connection. */
347 return 1;
348}
349
350/* disconnect from server */
351void
352vcdisconnect ()
353{
354 close (serverfd);
355 serverfd = -1;
356}
357
358/* handle a pm not sent error
359 * format: 412 %s */
360static void
361pmnotsent (unsigned char *message)
362{
363 while(*message && *message!=' ') message++;
364 snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_ERR),message+1);
365 writepriv( tmpstr);
366
367}
368
369/* parse and handle an action string
370 * format: 118 %s %s
371 * vars: %s nick
372 * %s action */
373static void
374pubaction (unsigned char *message)
375{
376 unsigned char *nick = NULL, *action = NULL;
377 nick = strchr (message, ' ');
378 nick[0] = '\0';
379 nick++;
380
381 action = strchr (nick, ' ');
382 action[0] = '\0';
383 action++;
384
385 snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_PUBACTION),nick,action);
386 writechan (tmpstr);
387}
388
389/* parse and handle an idle message
390 * format: 305
391 * vars: %s message */
392static void
393idleprompt (unsigned char *message)
394{
395 unsigned char *msg = NULL;
396 msg = strchr (message, ' ');
397 msg[0] = '\0';
398 msg++;
399
400 snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_IDLE),msg);
401 writechan (tmpstr);
402}
403
404/* parse and handle a topic information string
405 * format: 115 %d %s
406 * vars: %d chan - channel number
407 * %s topic - topic */
408static void
409topicinfo (unsigned char *message)
410{
411 unsigned char *channel = NULL, *topic = NULL;
412 int tmpchan = 0;
413
414 /* search start of channel number */
415 channel = strchr (message, ' ');
416 channel++;
417
418 /* search start of topic and terminate channel number */
419 topic = strchr (channel, ' ');
420 topic[0] = '\0';
421 topic++;
422
423 /* convert channel number to integer */
424 tmpchan = atoi (channel);
425
426 if (tmpchan == chan) {
427 /* show change in topic window */
428 if (strlen(topic))
429 snprintf (topicstr, TOPICSTRSIZE, getformatstr(FS_TOPICW), chan, topic);
430 else
431 snprintf (topicstr, TOPICSTRSIZE, getformatstr(FS_NOTOPICW), chan);
432 topicline(NULL);
433 }
434
435 /* announce change in channel window */
436 if (strlen(topic))
437 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_TOPIC), tmpchan, topic);
438 else
439 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_NOTOPIC), tmpchan);
440 writechan (tmpstr);
441}
442
443/* parse and handle a topic change string
444 * format: 114 %s changes topic to '%s'
445 * vars: %s nick
446 * %s topic */
447static void
448topicchange (unsigned char *message)
449{
450 unsigned char *nick = NULL, *topic = NULL;
451 int len;
452
453 /* search start of nickname */
454 nick = strchr (message, ' ');
455 nick++;
456
457 /* search start of message before topic, terminate nick */
458 topic = strchr (nick, ' ');
459 topic[0] = '\0';
460 topic++;
461
462 /* search start of real topic and terminate channel number */
463 topic = strchr (topic, '\'');
464 topic[0] = '\0';
465 topic++;
466
467 /* remove ending '\'', if there */
468 len = strlen (topic);
469 if (topic[len-1] == '\'')
470 topic[len-1] = '\0';
471
472 /* show change in topic window */
473 snprintf (topicstr, TOPICSTRSIZE, getformatstr(FS_TOPICW), chan, topic);
474 topicline(NULL);
475
476 /* announce change in channel window */
477 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_CHGTOPIC), nick, topic);
478 writechan (tmpstr);
479}
480
481/* parse and handle a login message
482 * format: 212 %s %s
483 * vars: %s str1 - nick used to login
484 * %s str2 - servers message */
485static void
486justloggedin (unsigned char *message)
487{
488 unsigned char *str1 = NULL, *str2 = NULL;
489 /* search start of nickname */
490 str1 = strchr (message, ' ');
491 str1++;
492
493 /* search start of message, terminate nick */
494 str2 = strchr (str1, ' ');
495 str2[0] = '\0';
496 str2++;
497
498 /* if we have a new nick, store it */
499 if (!nick || strcasecmp (nick, str1))
500 setstroption(CF_NICK,str1);
501
502 /* show change in console window */
503 snprintf (consolestr, CONSOLESTRSIZE, getformatstr(FS_CONSOLE), nick, getstroption (CF_SERVERHOST), getintoption (CF_SERVERPORT));
504 consoleline (NULL);
505
506 /* announce login as servermessage */
507 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_SIGNON), nick, str2);
508 writechan (tmpstr);
509
510 /* we're not logged in, change status and request nicks */
511 if (!loggedin)
512 {
513 networkoutput (".S");
514 loggedin = 1;
515 }
516}
517
518/* this user joins a different channel */
519void
520ownjoin (int channel)
521{
522 /* change global channel info */
523 chan = channel;
524 networkoutput(".t");
525 snprintf(tmpstr, TMPSTRSIZE, ".S %d",chan);
526 networkoutput(tmpstr);
527}
528
529/* this user leaves a channel */
530void
531ownleave (int channel)
532{
533 /* change global channel info */
534 chan = 0;
535}
536
537/* this user changes his nick */
538void
539ownnickchange (unsigned char *newnick)
540{
541 /* free old nick, store copy of new nick */
542 setstroption(CF_NICK,newnick);
543
544 /* show change in console window */
545 snprintf (consolestr, CONSOLESTRSIZE, getformatstr(FS_CONSOLE), nick, getstroption (CF_SERVERHOST), getintoption (CF_SERVERPORT));
546 consoleline (NULL);
547}
548
549/* parse and handle a nick error message
550 * format: 401 %s
551 * 403 %s
552 * 415 %s
553 * vars: %s - server message */
554static void
555nickerr (unsigned char *message)
556{
557 unsigned char *helpkiller = NULL;
558 /* mutate message for output */
559 message[2] = '!';
560 /* help information found? remove it. */
561 if ((helpkiller = strstr (message, " Type .h for help")))
562 helpkiller[0] = '\0';
563 /* nickchange not done? eliminate message */
564 if (loggedin && (helpkiller = strstr (message, " - Nick not changed")))
565 helpkiller[0] = '\0';
566 /* show errormessage */
567 writecf (FS_ERR,&message[2]);
568
569 /* not logged in? insist on getting a new nick */
570 if (!loggedin)
571 {
572 /* free bogus nick */
573 setstroption(CF_NICK,NULL);
574 /* get new nick via vchat-ui.c */
575 nickprompt ();
576
577 /* form login message and send it to server */
578 snprintf (tmpstr, TMPSTRSIZE, ".l %s %s %d", nick, getstroption (CF_FROM), getintoption (CF_CHANNEL));
579 networkoutput (tmpstr);
580 }
581}
582
583/* parse and handle a registered nick information
584 * format: 120 %s %s
585 * vars: %s - this users registered nick
586 * %s msg - server message */
587static void
588login (unsigned char *message)
589{
590 unsigned char *msg = NULL;
591
592 /* mutate message for output */
593 message[2] = '*';
594 /* show message to user */
595 writecf (FS_SERV,&message[2]);
596
597 /* we don't know our nick? */
598 if (!nick)
599 {
600 /* find message after nick */
601 msg = strchr (&message[4], ' ');
602 if (msg)
603 {
604 /* terminate string before message and copy nick */
605 msg[0] = '\0';
606 setstroption(CF_NICK,&message[4]);
607 }
608 else
609 {
610 /* no string in servers message (huh?), ask user for nick */
611 nickprompt ();
612 }
613 }
614
615 /* form login message and send it to server */
616 snprintf (tmpstr, TMPSTRSIZE, ".l %s %s %d", nick, getstroption (CF_FROM), getintoption (CF_CHANNEL));
617 networkoutput (tmpstr);
618}
619
620/* parse and handle anon login request
621 * format: 121 %s
622 * vars: %s - server message */
623static void
624anonlogin (unsigned char *message)
625{
626 /* mutate message for output */
627 message[2] = '*';
628 /* show message to user */
629 writecf (FS_SERV,&message[2]);
630
631 /* we don't know our nick? ask for it! */
632 if (!nick)
633 nickprompt ();
634
635 /* form login message and send it to server */
636 snprintf (tmpstr, TMPSTRSIZE, ".l %s %s %d", nick, getstroption (CF_FROM), getintoption (CF_CHANNEL));
637 networkoutput (tmpstr);
638}
639
640/* parse and handle list of nicks (from '.S')
641 * format: 119 %s ..
642 * vars: %s nick - a users nick */
643static void
644receivenicks (unsigned char *message)
645{
646 unsigned char *str1 = NULL, *str2 = NULL;
647 int mychan = 0;
648 void (*ul_myfunc)(unsigned char*,int);
649
650 /* show message to user */
651 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_USONLINE), &message[4]);
652 writechan (tmpstr);
653
654 /* search for channelnumber */
655 str1 = strchr (message, ' ');
656 str1++;
657 if (str1[0] == '*') {
658 if (nicks) return;
659 ul_myfunc = ul_add;
660 str1++;
661 } else {
662 str2 = str1;
663 str1 = strchr(str2,' ');
664 str1[0] = '\0';
665 mychan = atoi(str2);
666 ul_myfunc = ul_moveuser;
667 }
668 str1++;
669
670 /* while user .. */
671 while (str1)
672 {
673 /* search next user */
674 str2 = strchr (str1, ' ');
675 /* there is another user? terminate this one */
676 if (str2) {
677 str2[0] = '\0';
678 str2++;
679 }
680
681 /* add this user via vchat-user.c */
682 ul_myfunc (str1,mychan);
683
684 /* next user .. */
685 str1 = str2;
686 }
687}
688
689/* parse and handle a login message
690 * format: 211 %s %s
691 * vars: %s nick - who logged on
692 * %s msg - servers message */
693static void
694usersignon (unsigned char *message)
695{
696 unsigned char *nick = NULL, *msg = NULL;
697 /* search start of nickname */
698 nick = strchr (message, ' ');
699 nick++;
700
701 /* search start of message, terminate nick */
702 msg = strchr (nick, ' ');
703 msg[0] = '\0';
704 msg++;
705
706 /* add this user via vchat-user.c */
707 ul_add (nick, 0);
708
709 /* show message to user */
710 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_SIGNON), nick, msg);
711 writechan (tmpstr);
712}
713
714/* parse and handle a logoff message
715 * format: 221 %s %s
716 * vars: %s nick - who logged off
717 * %s msg - servers message */
718static void
719usersignoff (unsigned char *message)
720{
721 unsigned char *nick = NULL, *msg = NULL;
722 /* search start of nickname */
723 nick = strchr (message, ' ');
724 nick++;
725
726 /* search start of message, terminate nick */
727 msg = strchr (nick, ' ');
728 msg[0] = '\0';
729 msg++;
730
731 /* delete this user via vchat-user.c */
732 ul_del (nick, 0);
733
734 /* show message to user */
735 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_SIGNOFF), nick, msg);
736 writechan (tmpstr);
737}
738
739/* parse and handle a join message
740 * format: 232 %s %s %d
741 * vars: %s nick - who joined
742 * %s msg - servers message
743 * %d chan - channel joined */
744static void
745userjoin (unsigned char *message)
746{
747 unsigned char *nick = NULL, *msg = NULL, *channel = NULL;
748 int chan = 0;
749
750 /* search start of nickname */
751 nick = strchr (message, ' ');
752 nick++;
753
754 /* search start of message, terminate nick */
755 msg = strchr (nick, ' ');
756 msg[0] = '\0';
757 msg++;
758
759 /* search start of channel, terminate message */
760 channel = strrchr (msg, ' ');
761 channel[0] = '\0';
762 channel++;
763
764 /* convert channel to integer */
765 chan = atoi (channel);
766
767 /* notice channel join via vchat-user.c */
768 ul_join (nick, chan);
769
770 /* show message to user */
771 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_JOIN), nick, msg, chan);
772 writechan (tmpstr);
773}
774
775/* parse and handle a leave message
776 * format: 231 %s %s %d
777 * vars: %s nick - who left
778 * %s msg - servers message
779 * %d chan - channel joined */
780static void
781userleave (unsigned char *message)
782{
783 unsigned char *nick = NULL, *msg = NULL, *channel = NULL;
784 int chan = 0;
785
786 /* search start of nickname */
787 nick = strchr (message, ' ');
788 nick++;
789
790 /* search start of message, terminate nick */
791 msg = strchr (nick, ' ');
792 msg[0] = '\0';
793 msg++;
794
795 /* convert channel to integer */
796 channel = strrchr (msg, ' ');
797 channel[0] = '\0';
798 channel++;
799
800 /* convert channel to integer */
801 chan = atoi (channel);
802
803 /* notice channel leave via vchat-user.c */
804 ul_leave (nick, chan);
805
806 /* show message to user */
807 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_LEAVE), nick, msg, chan);
808 writechan (tmpstr);
809}
810
811/* parse and handle a nickchange message
812 * format: 241 %s %s %s
813 * vars: %s oldnick - users old nick
814 * %s newnick - users new nick
815 * %s msg - server message */
816static void
817usernickchange (unsigned char *message)
818{
819 unsigned char *oldnick = NULL, *newnick = NULL, *msg = NULL;
820
821 /* search start of old nickname */
822 oldnick = strchr (message, ' ');
823 oldnick++;
824
825 /* search start of new nickname, terminate old nick */
826 newnick = strchr (oldnick, ' ');
827 newnick[0] = '\0';
828 newnick++;
829
830 /* search start of message, terminate new nick */
831 msg = strchr (newnick, ' ');
832 msg[0] = '\0';
833 msg++;
834
835 /* notice nickchange via vchat-user.c */
836 ul_nickchange (oldnick, newnick);
837
838 /* show message to user */
839 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_NICKCHANGE), oldnick, newnick, msg);
840 writechan (tmpstr);
841}
842
843/* handle received message from server */
844static void
845parsemsg (unsigned char *message)
846{
847 unsigned char *str1, *str2;
848 int i;
849 /* message to short or starts with '<'? must be channel */
850 if (message[0] == '<')
851 {
852 str1 = &message[1];
853 str2 = strchr(str1,'>');
854 str2[0] = '\0';
855 str2++;
856 if (str2[0] == ' ') str2++;
857 if (!strncasecmp(nick,str2,strlen(nick)))
858 snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_MYPUBMSG),str1,str2);
859 else
860 snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_RXPUBMSG),str1,str2);
861 writechan (tmpstr);
862 }
863 else if (message[0] == '[')
864 {
865 str1 = &message[1];
866 str2 = strchr(str1,']');
867 str2[0] = '\0';
868 str2++;
869 if (str2[0] == ' ') str2++;
870 if (!strncasecmp(nick,str2,strlen(nick)))
871 snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_MYPUBURL),str1,str2);
872 else
873 snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_RXPUBURL),str1,str2);
874 writechan (tmpstr);
875 }
876 /* message starts with '*'? must be private */
877 else if (message[0] == '*')
878 {
879 str1 = &message[1];
880 str2 = strchr(str1,'*');
881 str2[0] = '\0';
882 str2++;
883 if (str2[0] == ' ') str2++;
884 snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_RXPRIVMSG),str1,str2);
885 writepriv (tmpstr);
886 ul_msgfrom(str1);
887 }
888 /* message starts with a number? must be a servermessage */
889 else if ((message[0] >= '0') && (message[0] <= '9'))
890 {
891 /* walk server message array */
892 for (i = 0; servermessages[i].id[0]; i++)
893 {
894 /* is this the message? */
895 if (!(strncmp (servermessages[i].id, message, 3)))
896 {
897 /* scripting hook available - call it */
898 if (servermessages[i].hook)
899 servermessages[i].hook (message);
900 /* function available - call it */
901 else if (servermessages[i].funct)
902 servermessages[i].funct (message);
903 /* no function available, but known - give output */
904 else
905 {
906 /* remove continutation mark */
907 if (message[3] == '-')
908 message[3] = ' ';
909
910 /* mutate message for display */
911 message[2] = '*';
912 /* giveout message by type */
913 switch (servermessages[i].type)
914 {
915 case SM_IGNORE:
916 break;
917 case SM_INFO:
918 /* change marker and send as servermessage */
919 message[2] = '#';
920 writecf (FS_SERV,&message[2]);
921 break;
922 case SM_USERINFO:
923 /* keep marker and send as servermessage */
924 writecf (FS_SERV,&message[2]);
925 break;
926 case SM_CHANNEL:
927 /* keep marker and send as channelmessage */
928 writechan (&message[2]);
929 break;
930 case SM_ERROR:
931 /* change marker and send as errormessage */
932 message[2] = '!';
933 writecf (FS_ERR,&message[2]);
934 break;
935 default:
936 /* fallback: keep marker and send as servermessage */
937 writecf (FS_SERV,&message[2]);
938 }
939 }
940 return;
941 }
942 }
943 /* message not in list, raise errormessage */
944 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_UNKNOWNMSG), message);
945 writecf (FS_ERR,tmpstr);
946 }
947 else
948 {
949 /* message neither public, private or server, raise errormessage */
950 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_BOGUSMSG), message);
951 writecf (FS_ERR,tmpstr);
952 }
953}
954
955/* offset in buffer (for linebreaks at packet borders) */
956static int bufoff = 0;
957
958/* get data from servers filedescriptor */
959void
960networkinput (void)
961{
962 int bytes;
963 unsigned char *tmp = NULL;
964#define BUFSIZE 4096
965 unsigned char buf[BUFSIZE]; /* data buffer */
966 unsigned char *ltmp = buf;
967 buf[BUFSIZE-1] = '\0'; /* sanity stop */
968
969 /* check if we use ssl or if we don't and receive data at offset */
970 if (!usessl)
971 bytes = recv (serverfd, &buf[bufoff], BUFSIZE-1 - bufoff, 0);
972 else
973 bytes = SSL_read (sslconn, &buf[bufoff], BUFSIZE-1 - bufoff);
974
975 /* no bytes transferred? raise error message, bail out */
976 if (bytes < 0)
977 {
978 snprintf (tmpstr, TMPSTRSIZE, "Receive fails, %s.", sys_errlist[errno]);
979 strncpy(errstr,tmpstr,TMPSTRSIZE-2);
980 errstr[TMPSTRSIZE-2] = '\0';
981 strcat(errstr,"\n");
982 writecf (FS_ERR,tmpstr);
983 status = 0;
984 }
985 /* end of file from server? */
986 else if (bytes == 0)
987 {
988 /* inform user, bail out */
989 writecf (FS_SERV,"* EOF from server");
990 strncpy(errstr,"* EOF from server",TMPSTRSIZE-2);
991 errstr[TMPSTRSIZE-2] = '\0';
992 strcat(errstr,"\n");
993 status = 0;
994 }
995 else
996 {
997 /* terminate message */
998 buf[bytes + bufoff] = '\0';
999 /* as long as there are lines .. */
1000 while ((tmp = strchr (ltmp, '\n')) != NULL)
1001 {
1002 /* did the server send CR+LF instead of LF with the last line? */
1003 if (tmp[-1] == '\r')
1004 tmp[-1] = '\0';
1005
1006 /* remove newline from previous message, advance pointer of next
1007 * message */
1008 tmp[0] = '\0';
1009 tmp++;
1010
1011 /* we have a last message? give away to line handler! */
1012 if (ltmp[0])
1013 {
1014#ifdef DEBUG
1015 /* debugging? log network input! */
1016 fprintf (stderr, "<| %s\n", ltmp);
1017#endif
1018 parsemsg (ltmp);
1019 }
1020
1021 /* move line along .. */
1022 ltmp = tmp;
1023 }
1024 /* buffer exhausted, move partial line to start of buffer and go on .. */
1025 bufoff = (bytes+bufoff) - (ltmp-buf);
1026 if (bufoff > 0)
1027 memmove (buf, ltmp, bufoff);
1028 else
1029 bufoff = 0;
1030 }
1031}
1032
1033void
1034networkoutput (unsigned char *msg)
1035{
1036#ifdef DEBUG
1037 /* debugging? log network output! */
1038 fprintf (stderr, ">| %s\n", msg);
1039#endif
1040
1041 /* TODO: err .. rework this (blocking) code */
1042
1043 /* check if we use ssl or if we don't and send data to server */
1044 if (!usessl) {
1045 if (send (serverfd, msg, strlen (msg), 0) != strlen (msg)) {
1046 writecf (FS_ERR,"Message sending fuzzy.");
1047 }
1048 } else if (SSL_write (sslconn, msg, strlen (msg)) != strlen (msg)) {
1049 writecf (FS_ERR,"Message sending fuzzy.");
1050 }
1051
1052 /* check if we use ssl or if we don't and send line termination to server */
1053 if (!usessl) {
1054 if (send (serverfd, "\r\n", 2, 0) != 2) {
1055 writecf (FS_ERR,"Message sending fuzzy!");
1056 }
1057 } else if (SSL_write (sslconn, "\r\n", 2) != 2) {
1058 writecf (FS_ERR,"Message sending fuzzy.");
1059 }
1060}