1 /*
2 irc.c - FireTalk IRC protocol driver
3 Copyright (C) 2000 Ian Gulliver
4 Copyright 2002-2006 Daniel Reed <n@ml.org>
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of version 2 of the GNU General Public License as
8 published by the Free Software Foundation.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19 #include <assert.h>
20 #include <ctype.h>
21 #include <errno.h>
22
23 #include <sys/types.h>
24 #include <sys/socket.h>
25 #include <stdlib.h>
26 #include <stdarg.h>
27 #include <string.h>
28 #include <stdio.h>
29 #include <netdb.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 #include <strings.h>
33 #include <sys/time.h>
34 #include <time.h>
35
36 #define ROOMSTARTS "#+&"
37
38
irc_tolower(const char c)39 static char irc_tolower(const char c) {
40 if ((c >= 'A') && (c <= 'Z'))
41 return((c - 'A') + 'a');
42 if (c == '[')
43 return('{');
44 if (c == ']')
45 return('{');
46 if (c == '\\')
47 return('|');
48 return(c);
49 }
50
irc_compare_nicks_int(const char * const nick1,const char * const nick2)51 static int irc_compare_nicks_int(const char *const nick1, const char *const nick2) {
52 int i = 0;
53
54 while (nick1[i] != '\0') {
55 if (irc_tolower(nick1[i]) != irc_tolower(nick2[i]))
56 return(1);
57 i++;
58 }
59 if (nick2[i] != '\0')
60 return(1);
61
62 return(0);
63 }
64
65 struct s_irc_whois {
66 struct s_irc_whois *next;
67 char *nickname,
68 *info;
69 int flags;
70 long online,
71 idle;
72 };
73
74 typedef struct irc_conn_t *client_t;
75 #define _HAVE_CLIENT_T
76 #include "firetalk-int.h"
77
78 typedef struct irc_conn_t {
79 char *nickname,
80 *password,
81 buffer[512+1];
82 struct s_irc_whois
83 *whois_head;
84 int passchange; /* whether we are currently changing our pass */
85 unsigned char
86 usesilence:1, /* are we on a network that understands SILENCE */
87 identified:1; /* are we we identified */
88 } irc_conn_t;
89
90 #if 0
91 static const char *const irc_normalize_user_nick(const char *const name) {
92 static char
93 buf[512];
94 int i;
95
96 if (strchr(name, '!') == NULL)
97 return(name);
98
99 for (i = 0; (i < sizeof(buf)) && (name[i] != '!'); i++)
100 buf[i] = name[i];
101 buf[i] = 0;
102 return(buf);
103 }
104
105 static const char *const irc_normalize_user_mask(const char *const name) {
106 static char
107 buf[512];
108
109 if (strchr(name, '!') != NULL)
110 return(name);
111
112 snprintf(buf, sizeof(buf), "%s!*@*", name);
113 return(buf);
114 }
115 #endif
116
irc_disc_user_rem(irc_conn_t * c,const char * disc,const char * name)117 static void irc_disc_user_rem(irc_conn_t *c, const char *disc, const char *name) {
118 struct s_firetalk_handle *fchandle;
119
120 fchandle = firetalk_find_handle(c);
121
122 if (firetalk_user_visible_but(fchandle, disc, name) == FE_NOMATCH)
123 firetalk_callback_im_buddyonline(c, name, 0);
124 }
125
irc_disc_rem(irc_conn_t * c,const char * disc)126 static void irc_disc_rem(irc_conn_t *c, const char *disc) {
127 struct s_firetalk_handle *fchandle;
128 struct s_firetalk_room *iter;
129 struct s_firetalk_member *mem;
130
131 fchandle = firetalk_find_handle(c);
132 iter = firetalk_find_room(fchandle, disc);
133 assert(iter != NULL);
134
135 for (mem = iter->member_head; mem != NULL; mem = mem->next)
136 irc_disc_user_rem(c, disc, mem->nickname);
137 }
138
irc_normalize_room_name(const char * const name)139 static const char *irc_normalize_room_name(const char *const name) {
140 static char newname[2048];
141
142 if (strchr(ROOMSTARTS, *name))
143 return(name);
144 snprintf(newname, sizeof(newname), "#%s", name);
145 return(newname);
146 }
147
148
149
150 #include "firetalk.h"
151
152
153
154 #ifdef DEBUG_ECHO
155 extern void *curconn;
156 extern void status_echof(void *conn, const unsigned char *format, ...);
157
irc_echof(irc_conn_t * c,const char * const where,const char * const format,...)158 static void irc_echof(irc_conn_t *c, const char *const where, const char *const format, ...) {
159 va_list ap;
160 char buf[513];
161 void statrefresh(void);
162
163 va_start(ap, format);
164 vsnprintf(buf, sizeof(buf), format, ap);
165 va_end(ap);
166
167 while (buf[strlen(buf)-1] == '\n')
168 buf[strlen(buf)-1] = 0;
169 if (*buf != 0)
170 status_echof(curconn, firetalk_htmlentities(buf));
171 // firetalk_callback_chat_getmessage(c, ":RAW", where, 0, buf);
172
173 statrefresh();
174 }
175 #endif
176
irc_compare_nicks(const char * const nick1,const char * const nick2)177 static fte_t irc_compare_nicks(const char *const nick1, const char *const nick2) {
178 if (irc_compare_nicks_int(nick1, nick2) == 0)
179 return(FE_SUCCESS);
180 return(FE_NOMATCH);
181 }
182
183 static fte_t
irc_isprint(const int c)184 irc_isprint(const int c) {
185 if (isprint(c))
186 return(FE_SUCCESS);
187 return(FE_INVALIDFORMAT);
188 }
189
190 static fte_t
irc_isnickfirst(const int c)191 irc_isnickfirst(const int c) {
192 return(isalpha(c) || (c == '[') || (c == ']') || (c == '\\') || (c == '`') || (c == '^') || (c == '{') || (c == '}'));
193 }
194
195 static fte_t
irc_isnick(const int c)196 irc_isnick(const int c) {
197 return(irc_isnickfirst(c) || isdigit(c) || (c == '-'));
198 }
199
irc_html_to_irc(const char * const string)200 static char *irc_html_to_irc(const char *const string) {
201 return string;
202 /*
203 static char *output = NULL;
204 int o = 0;
205 size_t l,i=0;
206
207 l = strlen(string);
208
209 output = realloc(output, (l * 4) + 1);
210 if (output == NULL)
211 abort();
212
213 while (i < l) {
214 assert(o < ((l * 4) + 1));
215 switch(string[i]) {
216 case '&':
217 if (!strncasecmp(&string[i],"&",5)) {
218 output[o++] = '&';
219 i += 5;
220 } else if (!strncasecmp(&string[i],">",4)) {
221 output[o++] = '>';
222 i += 4;
223 } else if (!strncasecmp(&string[i],"<",4)) {
224 output[o++] = '<';
225 i += 4;
226 } else if (!strncasecmp(&string[i]," ",6)) {
227 output[o++] = ' ';
228 i += 6;
229 } else
230 output[o++] = string[i++];
231 break;
232 case '<':
233 if (!strncasecmp(&string[i],"<b>",3)) {
234 output[o++] = (char) 2;
235 i += 3;
236 } else if (!strncasecmp(&string[i],"</b>",4)) {
237 output[o++] = (char) 2;
238 i += 4;
239 } else if (!strncasecmp(&string[i],"<i>",3)) {
240 output[o++] = (char) 22;
241 i += 3;
242 } else if (!strncasecmp(&string[i],"</i>",4)) {
243 output[o++] = (char) 22;
244 i += 4;
245 } else if (!strncasecmp(&string[i],"<u>",3)) {
246 output[o++] = (char) 31;
247 i += 3;
248 } else if (!strncasecmp(&string[i],"</u>",4)) {
249 output[o++] = (char) 31;
250 i += 4;
251 } else if (!strncasecmp(&string[i],"<br>",4)) {
252 output[o++] = '\020';
253 output[o++] = 'r';
254 output[o++] = '\020';
255 output[o++] = 'n';
256 i += 4;
257 } else
258 output[o++] = string[i++];
259 break;
260 case '\r':
261 output[o++] = '\020';
262 output[o++] = 'r';
263 i++;
264 break;
265 case '\n':
266 output[o++] = '\020';
267 output[o++] = 'n';
268 i++;
269 break;
270 case '\020':
271 output[o++] = '\020';
272 output[o++] = '\020';
273 i++;
274 break;
275 default:
276 output[o++] = string[i++];
277 break;
278 }
279 }
280 assert(o <= ((l * 4) + 1));
281 output[o] = '\0';
282 return(output);
283 */
284 }
285
286 static const char *mIRCar[] = {
287 "#FFFFFF", // 0 white
288 "#000000", // 1 black
289 "#0000FF", // 2 blue (navy)
290 "#00FF00", // 3 green
291 "#FF0000", // 4 red
292 "#4E2F2F", // 5 brown (maroon)
293 "#AA00FF", // 6 purple
294 "#FF7700", // 7 orange (olive)
295 "#FFFF00", // 8 yellow
296 "#32CD32", // 9 lt.green (lime)
297 "#349F79", // 10 teal (a kinda green/blue cyan)
298 "#70DB93", // 11 lt.cyan (cyan ?) (aqua)
299 "#3333FF", // 12 lt.blue (royal)
300 "#FF00AA", // 13 pink (light purple) (fuchsia)
301 "#A8A8A8", // 14 grey
302 "#E6E8FA" // 15 lt.grey (silver)
303 };
304
305 static const char *ANSIar[] = {
306 "#000000", // 30 black
307 "#FF0000", // 31 red
308 "#00FF00", // 32 green
309 "#FFFF00", // 33 yellow
310 "#0000FF", // 34 blue
311 "#FF00FF", // 35 magenta (purple)
312 "#00FFFF", // 36 cyan (aqua)
313 "#FFFFFF", // 37 white
314 };
315
irc_mIRC_to_html(const char * const string,size_t * pos)316 static const char *irc_mIRC_to_html(const char *const string, size_t *pos) {
317 int i, col = 0;
318
319 for (i = 0; (i < 2) && isdigit(string[*pos]); i++, (*pos)++) {
320 col *= 10;
321 col += string[*pos] - '0';
322 }
323 if (string[*pos] == ',') {
324 (*pos)++;
325 for (i = 0; (i < 2) && isdigit(string[*pos]); i++, (*pos)++)
326 ;
327 }
328 if ((col >= 0) && (col <= 15))
329 return(mIRCar[col]);
330 else
331 return("#000000");
332 }
333
irc_irc_to_html(const char * const string)334 static char *irc_irc_to_html(const char *const string) {
335 return string;
336 /*
337 static char *output = NULL;
338 int o = 0;
339 size_t l, i = 0, s;
340 int infont = 0, inbold = 0, initalics = 0, inunderline = 0;
341
342 assert(string != NULL);
343
344 l = strlen(string);
345
346 s = l*(sizeof("<font color=\"#RRGGBB\">")-1) + 1;
347 output = realloc(output, s);
348 if (output == NULL)
349 abort();
350
351 while (i < l) {
352 switch(string[i]) {
353 case 2:
354 if (inbold == 1) {
355 memcpy(&output[o],"</B>",4);
356 o += 4;
357 inbold = 0;
358 } else {
359 memcpy(&output[o],"<B>",3);
360 o += 3;
361 inbold = 1;
362 }
363 break;
364 case 3:
365 if (isdigit(string[i+1])) {
366 i++;
367 sprintf(output+o, "<font color=\"%s\">", irc_mIRC_to_html(string, &i));
368 o += sizeof("<font color=\"#RRGGBB\">")-1;
369 infont = 1;
370 i--;
371 } else if (infont == 1) {
372 strcpy(output+o, "</font>");
373 o += sizeof("</font>")-1;
374 infont = 0;
375 }
376 break;
377 case 15:
378 if (infont == 1) {
379 strcpy(output+o, "</font>");
380 o += sizeof("</font>")-1;
381 infont = 0;
382 }
383 if (inbold == 1) {
384 strcpy(output+o, "</B>");
385 o += sizeof("</B>")-1;
386 inbold = 0;
387 }
388 if (initalics == 1) {
389 strcpy(output+o, "</I>");
390 o += sizeof("</I>")-1;
391 initalics = 0;
392 }
393 if (inunderline == 1) {
394 strcpy(output+o, "</U>");
395 o += sizeof("</U>")-1;
396 inunderline = 0;
397 }
398 break;
399 case 27:
400 if (string[i+1] == '[') {
401 i += 2;
402 while ((string[i] != 0) && (string[i] != 'm')) {
403 int num = 0;
404
405 if (!isdigit(string[i]))
406 break;
407 while (isdigit(string[i])) {
408 num *= 10;
409 num += string[i] - '0';
410 i++;
411 }
412 if (string[i] == ';')
413 i++;
414 switch (num) {
415 case 0:
416 if (infont == 1) {
417 strcpy(output+o, "</font>");
418 o += sizeof("</font>")-1;
419 infont = 0;
420 }
421 if (inbold == 1) {
422 strcpy(output+o, "</B>");
423 o += sizeof("</B>")-1;
424 inbold = 0;
425 }
426 if (initalics == 1) {
427 strcpy(output+o, "</I>");
428 o += sizeof("</I>")-1;
429 initalics = 0;
430 }
431 if (inunderline == 1) {
432 strcpy(output+o, "</U>");
433 o += sizeof("</U>")-1;
434 inunderline = 0;
435 }
436 break;
437 case 1:
438 if (inbold == 0) {
439 strcpy(output+o, "<B>");
440 o += sizeof("<B>")-1;
441 inbold = 1;
442 }
443 break;
444 case 2:
445 if (inbold == 1) {
446 strcpy(output+o, "</B>");
447 o += sizeof("</B>")-1;
448 inbold = 0;
449 }
450 break;
451 case 3:
452 if (inunderline == 0) {
453 strcpy(output+o, "<U>");
454 o += sizeof("<U>")-1;
455 inunderline = 1;
456 }
457 break;
458 case 7:
459 if (initalics == 0) {
460 strcpy(output+o, "<I>");
461 o += sizeof("<I>")-1;
462 initalics = 1;
463 }
464 break;
465 case 30: case 31: case 32: case 33:
466 case 34: case 35: case 36: case 37:
467 sprintf(output+o, "<font color=\"%s\">", ANSIar[num-30]);
468 o += sizeof("<font color=\"#RRGGBB\">")-1;
469 infont = 1;
470 break;
471 }
472 }
473 if (string[i] == 0)
474 i--;
475 }
476 break;
477 case 22:
478 if (initalics == 1) {
479 memcpy(&output[o],"</I>",4);
480 o += 4;
481 initalics = 0;
482 } else {
483 memcpy(&output[o],"<I>",3);
484 o += 3;
485 initalics = 1;
486 }
487 break;
488 case 31:
489 if (inunderline == 1) {
490 memcpy(&output[o],"</U>",4);
491 o += 4;
492 inunderline = 0;
493 } else {
494 memcpy(&output[o],"<U>",3);
495 o += 3;
496 inunderline = 1;
497 }
498 break;
499 case '&':
500 memcpy(&output[o],"&",5);
501 o += 5;
502 break;
503 case '<':
504 memcpy(&output[o],"<",4);
505 o += 4;
506 break;
507 case '>':
508 memcpy(&output[o],">",4);
509 o += 4;
510 break;
511 case 16:
512 switch(string[++i]) {
513 case 16:
514 output[o++] = '\020';
515 break;
516 case 'r':
517 if (string[i+1] == '\020' && string[i+2] == 'n') {
518 i += 2;
519 memcpy(&output[o],"<br>",4);
520 o += 4;
521 } else
522 output[o++] = '\r';
523 break;
524 case 'n':
525 output[o++] = '\n';
526 break;
527 default:
528 output[o++] = string[i];
529 break;
530 }
531 break;
532 case ' ':
533 if (string[i+1] == ' ') {
534 memcpy(&output[o], " ", 6);
535 o += 6;
536 break;
537 }
538 default:
539 output[o++] = string[i];
540 break;
541 }
542 i++;
543 }
544
545 output[o] = '\0';
546
547 return(output);*/
548 }
549
irc_internal_disconnect(irc_conn_t * c,const int error)550 static int irc_internal_disconnect(irc_conn_t *c, const int error) {
551 struct s_irc_whois *whois_iter, *whois_iter2;
552
553 if (c->nickname != NULL) {
554 free(c->nickname);
555 c->nickname = NULL;
556 }
557 if (c->password != NULL) {
558 free(c->password);
559 c->password = NULL;
560 }
561 for (whois_iter = c->whois_head; whois_iter != NULL; whois_iter = whois_iter2) {
562 whois_iter2 = whois_iter->next;
563 if (whois_iter->nickname != NULL) {
564 free(whois_iter->nickname);
565 whois_iter->nickname = NULL;
566 }
567 if (whois_iter->info != NULL) {
568 free(whois_iter->info);
569 whois_iter->info = NULL;
570 }
571 free(whois_iter);
572 }
573 c->whois_head = NULL;
574
575 c->passchange = 0;
576 c->usesilence = 1;
577 c->identified = 0;
578
579 firetalk_callback_disconnect(c, error);
580
581 return(FE_SUCCESS);
582 }
583
irc_send_printf(irc_conn_t * c,const char * const format,...)584 static int irc_send_printf(irc_conn_t *c, const char *const format, ...) {
585 va_list ap;
586 size_t i,
587 datai = 0;
588 char data[513];
589
590 va_start(ap, format);
591 for (i = 0; format[i] != 0; i++) {
592 if (format[i] == '%') {
593 switch (format[++i]) {
594
595
596
597
598
599
600
601
602 case 's': {
603 const char
604 *s = irc_html_to_irc(va_arg(ap, char *));
605 size_t slen = strlen(s);
606
607 if ((datai+slen) > (sizeof(data)-2-1))
608 return(FE_PACKETSIZE);
609 strcpy(data+datai, s);
610 datai += slen;
611 break;
612 }
613 case '%':
614 data[datai++] = '%';
615 break;
616 }
617 } else {
618 data[datai++] = format[i];
619 if (datai > (sizeof(data)-2-1))
620 return(FE_PACKETSIZE);
621 }
622 }
623 va_end(ap);
624 data[datai] = 0;
625
626 #ifdef DEBUG_ECHO
627 irc_echof(c, "send_printf", "%s", data);
628 #endif
629
630 strcpy(data+datai, "\r\n");
631 datai += 2;
632
633 {
634 struct s_firetalk_handle
635 *fchandle;
636
637 fchandle = firetalk_find_handle(c);
638 firetalk_internal_send_data(fchandle, data, datai);
639 }
640
641 return(FE_SUCCESS);
642 }
643
irc_recv_parse(irc_conn_t * c,unsigned char * buffer,unsigned short * bufferpos)644 static char **irc_recv_parse(irc_conn_t *c, unsigned char *buffer, unsigned short *bufferpos) {
645 static char *args[256];
646 static char data[513];
647 size_t curarg;
648 char *tempchr;
649 char *tempchr2;
650
651 args[0] = NULL;
652
653 assert(*bufferpos < sizeof(data));
654 memcpy(data, buffer, *bufferpos);
655 data[*bufferpos] = '\0';
656
657 tempchr = strchr(data, '\n');
658 if (tempchr == NULL)
659 return(NULL);
660 if ((tempchr > data) && (tempchr[-1] == '\r'))
661 tempchr[-1] = 0;
662 else
663 tempchr[0] = 0;
664 *bufferpos -= (tempchr - data + 1);
665 memmove(buffer, &buffer[tempchr - data + 1], *bufferpos);
666
667 #ifdef DEBUG_ECHO
668 irc_echof(c, "recv_parse", "%s", data);
669 #endif
670
671 curarg = 0;
672 tempchr = data;
673 if (*tempchr == ':')
674 tempchr++;
675 else
676 args[curarg++] = ":SERVER";
677
678 while ((curarg < sizeof(args)/sizeof(*args)) && ((tempchr2 = strchr(tempchr, ' ')) != NULL)) {
679 args[curarg++] = tempchr;
680 *tempchr2 = 0;
681 tempchr = tempchr2 + 1;
682 if (*tempchr == ':') {
683 tempchr++;
684 break;
685 }
686 }
687 args[curarg++] = tempchr;
688 args[curarg] = NULL;
689 return(args);
690 }
691
irc_get_nickname(const char * const hostmask)692 static char *irc_get_nickname(const char *const hostmask) {
693 static char data[512];
694 char *tempchr;
695
696 strncpy(data, hostmask, sizeof(data)-1);
697 data[sizeof(data)-1] = 0;
698
699 if ((tempchr = strchr(data, '!')) != NULL)
700 *tempchr = 0;
701 return(data);
702 }
703
704 static fte_t
irc_set_nickname(irc_conn_t * c,const char * const nickname)705 irc_set_nickname(irc_conn_t *c, const char *const nickname) {
706 return(irc_send_printf(c,"NICK %s",nickname));
707 }
708
709 static fte_t
irc_set_password(irc_conn_t * c,const char * const oldpass,const char * const newpass)710 irc_set_password(irc_conn_t *c, const char *const oldpass, const char *const newpass) {
711 c->passchange++;
712 return(irc_send_printf(c,"PRIVMSG NickServ :SET PASSWORD %s",newpass));
713 }
714
715 static void
irc_destroy_handle(irc_conn_t * c)716 irc_destroy_handle(irc_conn_t *c) {
717 irc_send_printf(c,"QUIT :Handle destroyed");
718 irc_internal_disconnect(c,FE_USERDISCONNECT);
719 free(c);
720 }
721
722 static fte_t
irc_disconnect(irc_conn_t * c)723 irc_disconnect(irc_conn_t *c) {
724 irc_send_printf(c,"QUIT :User disconnected");
725 return(irc_internal_disconnect(c,FE_USERDISCONNECT));
726 }
727
728 static irc_conn_t
irc_create_handle(void)729 *irc_create_handle(void) {
730 irc_conn_t *c;
731
732 c = calloc(1, sizeof(*c));
733 if (c == NULL)
734 abort();
735 c->usesilence = 1;
736
737 return(c);
738 }
739
740 #if defined(HAVE_GETPWUID) && defined(HAVE_GETUID)
741 # include <pwd.h>
742 #endif
743
744 static fte_t
irc_signon(irc_conn_t * c,const char * const nickname)745 irc_signon(irc_conn_t *c, const char *const nickname) {
746
747 char pass[129];
748 *pass=0;
749
750 firetalk_callback_needpass(c, pass, sizeof(pass));
751
752 if(strlen(pass))
753 if(irc_send_printf(c, "PASS %s", pass) != FE_SUCCESS)
754 return FE_PACKET;
755
756 #if defined(HAVE_GETPWUID) && defined(HAVE_GETUID)
757 struct passwd *pw = getpwuid(getuid());
758 char buf[1024];
759 int i;
760
761 for (i = 0; (pw->pw_gecos[i] != 0) && (pw->pw_gecos[i] != ',') && (i < sizeof(buf)-1); i++)
762 buf[i] = pw->pw_gecos[i];
763 if (i == 0) {
764 snprintf(buf, sizeof(buf), "http://www.centerim.org user");
765 i = strlen(buf);
766 }
767 buf[i] = 0;
768
769 if (irc_send_printf(c, "USER %s %s %s :%s", pw->pw_name, nickname, nickname, buf) != FE_SUCCESS)
770 return(FE_PACKET);
771 #else
772 if (irc_send_printf(c, "USER %s %s %s :%s", nickname, nickname, nickname, nickname) != FE_SUCCESS)
773 return(FE_PACKET);
774 #endif
775
776 if (irc_send_printf(c, "NICK %s", nickname) != FE_SUCCESS)
777 return(FE_PACKET);
778
779 free(c->nickname);
780 c->nickname = strdup(nickname);
781 if (c->nickname == NULL)
782 abort();
783
784 return(FE_SUCCESS);
785 }
786
787 static fte_t
irc_preselect(irc_conn_t * c,fd_set * read,fd_set * write,fd_set * except,int * n)788 irc_preselect(irc_conn_t *c, fd_set *read, fd_set *write, fd_set *except, int *n) {
789 return(FE_SUCCESS);
790 }
791
792 static fte_t
irc_postselect(irc_conn_t * c,fd_set * read,fd_set * write,fd_set * except)793 irc_postselect(irc_conn_t *c, fd_set *read, fd_set *write, fd_set *except) {
794 return(FE_SUCCESS);
795 }
796
irc_addwhois(irc_conn_t * c,const char * const name,const char * const format,...)797 static void irc_addwhois(irc_conn_t *c, const char *const name, const char *const format, ...) {
798 struct s_irc_whois *whoisiter;
799 char buf[1024];
800 va_list msg;
801
802 va_start(msg, format);
803 vsnprintf(buf, sizeof(buf), format, msg);
804 va_end(msg);
805
806 for (whoisiter = c->whois_head; whoisiter != NULL; whoisiter = whoisiter->next)
807 if (irc_compare_nicks(name, whoisiter->nickname) == 0) {
808 int len = whoisiter->info?strlen(whoisiter->info):0;
809
810 whoisiter->info = realloc(whoisiter->info, len+strlen(buf)+1);
811 if (whoisiter->info == NULL)
812 abort();
813 strcpy(whoisiter->info+len, buf);
814 break;
815 }
816 }
817
irc_got_data_parse(irc_conn_t * c,char ** args)818 static fte_t irc_got_data_parse(irc_conn_t *c, char **args) {
819 struct s_irc_whois
820 *whoisiter,
821 *whoisiter2;
822 char *tempchr;
823
824 {
825 static unsigned int inwhois = 0;
826 int handled = 0,
827 numeric;
828
829 handled = 1;
830 if (strcmp(args[1], "PING") == 0) {
831 if (args[2] != NULL) {
832 if (irc_send_printf(c, "PONG :%s", args[2]) != 0) {
833 irc_internal_disconnect(c, FE_PACKET);
834 return(FE_PACKET);
835 }
836 } else {
837 if (irc_send_printf(c, "PONG") != 0) {
838 irc_internal_disconnect(c, FE_PACKET);
839 return(FE_PACKET);
840 }
841 }
842 } else if (strcmp(args[1], "QUIT") == 0) {
843 const char *name = irc_get_nickname(args[0]);
844
845 firetalk_callback_im_buddyonline(c, name, 0);
846 if (irc_compare_nicks(c->nickname, name) == 0)
847 irc_internal_disconnect(c, FE_DISCONNECT);
848 else
849 firetalk_callback_chat_user_quit(c, name, irc_irc_to_html(args[2]));
850 } else
851 handled = 0;
852
853 if (handled)
854 return(FE_SUCCESS);
855
856 numeric = atoi(args[1]);
857
858 if (args[2] == NULL)
859 goto unhandled;
860
861 handled = 1;
862 if (strcmp(args[1], "JOIN") == 0) {
863 const char *name = irc_get_nickname(args[0]);
864
865 firetalk_callback_im_buddyonline(c, name, 1);
866 if (irc_compare_nicks(c->nickname, name) == 0) {
867 firetalk_callback_chat_joined(c, args[2]);
868
869 if (c->identified == 1) {
870 if (irc_send_printf(c, "PRIVMSG ChanServ :OP %s %s", args[2], c->nickname) != FE_SUCCESS) {
871 irc_internal_disconnect(c, FE_PACKET);
872 return(FE_PACKET);
873 }
874 }
875 } else {
876 char *extra = strchr(args[0], '!');
877
878 firetalk_callback_chat_user_joined(c, args[2], name, (extra != NULL)?(extra+1):NULL);
879 }
880 } else if (strcmp(args[1], "PART") == 0) {
881 const char *name = irc_get_nickname(args[0]);
882
883 if (irc_compare_nicks(c->nickname, name) == 0) {
884 irc_disc_rem(c, args[2]);
885 firetalk_callback_chat_left(c, args[2]);
886 } else {
887 irc_disc_user_rem(c, args[2], name);
888 firetalk_callback_chat_user_left(c, args[2], name, (args[3] != NULL)?irc_irc_to_html(args[3]):NULL);
889 }
890 } else if (strcmp(args[1],"NICK") == 0) {
891 const char *name = irc_get_nickname(args[0]);
892
893 if (irc_compare_nicks(c->nickname, name) == 0) {
894 free(c->nickname);
895 c->nickname = strdup(args[2]);
896 if (c->nickname == NULL)
897 abort();
898 firetalk_callback_newnick(c, c->nickname);
899 }
900 firetalk_callback_user_nickchanged(c, name, args[2]);
901 } else
902 handled = 0;
903
904 if (handled)
905 return(FE_SUCCESS);
906
907 if (args[3] == NULL)
908 goto unhandled;
909
910 handled = 1;
911 if (strcmp(args[1],"PRIVMSG") == 0) {
912 /* scan for CTCPs */
913 while ((tempchr = strchr(args[3], 1))) {
914 char *sp;
915
916 if ((sp = strchr(tempchr+1, 1))) {
917 *sp = 0;
918
919 /* we have a ctcp */
920 if (strchr(ROOMSTARTS, args[2][0])) {
921 /* chat room subcode */
922 if (strncasecmp(&tempchr[1],"ACTION ",7) == 0)
923 firetalk_callback_chat_getaction(c, args[2], irc_get_nickname(args[0]), 0, irc_irc_to_html(tempchr+8));
924 } else {
925 char *endcommand;
926
927 endcommand = strchr(&tempchr[1], ' ');
928 if (endcommand) {
929 *endcommand = '\0';
930 endcommand++;
931 firetalk_callback_subcode_request(c, irc_get_nickname(args[0]), &tempchr[1], endcommand);
932 } else
933 firetalk_callback_subcode_request(c, irc_get_nickname(args[0]), &tempchr[1], NULL);
934 }
935 memmove(tempchr, sp+1, strlen(sp+1) + 1);
936 } else
937 break;
938 }
939 if (args[3][0] != '\0') {
940 if (strchr(ROOMSTARTS, args[2][0]))
941 firetalk_callback_chat_getmessage(c, args[2], irc_get_nickname(args[0]), 0, irc_irc_to_html(args[3]));
942 else
943 firetalk_callback_im_getmessage(c, irc_get_nickname(args[0]), 0, irc_irc_to_html(args[3]));
944 }
945 } else if (strcmp(args[1],"NOTICE") == 0) {
946 const char *name = irc_get_nickname(args[0]);
947
948 /* scan for CTCP's */
949 while ((tempchr = strchr(args[3], 1))) {
950 char *sp;
951
952 if ((sp = strchr(tempchr+1, 1)))
953 *sp = 0;
954 /* we have a ctcp */
955 if (strchr(ROOMSTARTS, args[2][0]) == NULL) {
956 char *endcommand;
957 endcommand = strchr(&tempchr[1], ' ');
958 if (endcommand) {
959 *endcommand = '\0';
960 endcommand++;
961 firetalk_callback_subcode_reply(c, name, &tempchr[1], endcommand);
962 } else
963 firetalk_callback_subcode_reply(c, name, &tempchr[1], NULL);
964 }
965 if (sp)
966 memcpy(tempchr, sp+1, strlen(sp+1) + 1);
967 else
968 break;
969 }
970 if (strcasecmp(name, "NickServ") == 0) {
971 if ((strstr(args[3],"IDENTIFY") != NULL) && (strstr(args[3],"/msg") != NULL) && (strstr(args[3],"HELP") == NULL)) {
972 c->identified = 0;
973 /* nickserv seems to be asking us to identify ourselves, and we have a password */
974 if (!c->password) {
975 c->password = calloc(1, 128);
976 if (c->password == NULL)
977 abort();
978 firetalk_callback_needpass(c,c->password,128);
979 }
980 if ((c->password != NULL) && irc_send_printf(c,"PRIVMSG NickServ :IDENTIFY %s",c->password) != 0) {
981 irc_internal_disconnect(c,FE_PACKET);
982 return(FE_PACKET);
983 }
984 } else if ((strstr(args[3],"Password changed") != NULL) && (c->passchange != 0)) {
985 /* successful change */
986 c->passchange--;
987 firetalk_callback_passchanged(c);
988 } else if ((strstr(args[3],"authentication required") != NULL) && (c->passchange != 0)) {
989 /* didn't log in with the right password initially, not happening */
990 c->identified = 0;
991 c->passchange--;
992 firetalk_callback_error(c,FE_NOCHANGEPASS,NULL,args[3]);
993 } else if ((strstr(args[3],"isn't registered") != NULL) && (c->passchange != 0)) {
994 /* nick not registered, fail */
995 c->passchange--;
996 firetalk_callback_error(c,FE_NOCHANGEPASS,NULL,args[3]);
997 } else if (strstr(args[3],"Password accepted") != NULL) {
998 /* we're recognized */
999 c->identified = 1;
1000 if (irc_send_printf(c,"PRIVMSG ChanServ :OP ALL") != FE_SUCCESS) {
1001 irc_internal_disconnect(c,FE_PACKET);
1002 return(FE_PACKET);
1003 }
1004 } else if (strchr(ROOMSTARTS, args[2][0]))
1005 firetalk_callback_chat_getmessage(c, args[2], name, 1, irc_irc_to_html(args[3]));
1006 else
1007 firetalk_callback_im_getmessage(c, name, 1, irc_irc_to_html(args[3]));
1008 } else if (strchr(name, '.') != NULL) {
1009 if (strncmp(args[3], "*** Notice -- ", sizeof("*** Notice -- ")-1) == 0)
1010 firetalk_callback_chat_getmessage(c, ":RAW", name, 1, irc_irc_to_html(args[3]+sizeof("*** Notice -- ")-1));
1011 else
1012 firetalk_callback_chat_getmessage(c, ":RAW", name, 1, irc_irc_to_html(args[3]));
1013 } else if (args[3][0] != '\0') {
1014 if (strchr(ROOMSTARTS, args[2][0]))
1015 firetalk_callback_chat_getmessage(c, args[2], name, 1, irc_irc_to_html(args[3]));
1016 else
1017 firetalk_callback_im_getmessage(c, name, 1, irc_irc_to_html(args[3]));
1018 }
1019 } else if (strcmp(args[1], "TOPIC") == 0) {
1020 firetalk_callback_chat_gottopic(c, args[2], irc_irc_to_html(args[3]), irc_get_nickname(args[0]));
1021 } else if (strcmp(args[1], "KICK") == 0) {
1022 const char *name = irc_get_nickname(args[3]);
1023
1024 if (irc_compare_nicks(c->nickname, name) == 0) {
1025 irc_disc_rem(c, args[2]);
1026 firetalk_callback_chat_kicked(c, args[2], irc_get_nickname(args[0]), irc_irc_to_html(args[4]));
1027 } else {
1028 irc_disc_user_rem(c, args[2], name);
1029
1030 tempchr = strdup(name);
1031 if (tempchr == NULL)
1032 abort();
1033 firetalk_callback_chat_user_kicked(c, args[2], tempchr, irc_get_nickname(args[0]), irc_irc_to_html(args[4]));
1034 free(tempchr);
1035 tempchr = NULL;
1036 }
1037 } else
1038 handled = 0;
1039
1040 if (handled)
1041 return(FE_SUCCESS);
1042
1043 if (numeric == 311) // RPL_WHOISUSER
1044 inwhois = 1;
1045 else if (numeric == 318)// RPL_ENDOFWHOIS
1046 inwhois = 0;
1047
1048 if (!inwhois)
1049 switch (numeric) {
1050 case 333: /* :PREFIX 333 sn channel topicsetuser topicsettime */
1051 case 306: /* :PREFIX 306 sn :You have been marked as being away */
1052 case 305: /* :PREFIX 305 sn :You are no longer marked as being away */
1053 case 396: /* :PREFIX 396 sn whoishost :is now your hidden host */
1054 return(FE_SUCCESS);
1055 }
1056
1057 handled = 1;
1058 switch (numeric) {
1059 case 366: /* :PREFIX 366 sn channel :End of /NAMES list. */
1060 firetalk_callback_chat_user_joined(c, args[3], NULL, NULL);
1061 break;
1062 case 301: /* RPL_AWAY */
1063 break;
1064 case 313: /* RPL_WHOISOPER */
1065 for (whoisiter = c->whois_head; whoisiter != NULL; whoisiter = whoisiter->next)
1066 if (irc_compare_nicks(args[3],whoisiter->nickname) == 0) {
1067 whoisiter->flags |= FF_ADMIN;
1068 break;
1069 }
1070 break;
1071 case 318: /* RPL_ENDOFWHOIS */
1072 whoisiter2 = NULL;
1073 for (whoisiter = c->whois_head; whoisiter != NULL; whoisiter = whoisiter->next) {
1074 if (irc_compare_nicks(args[3], whoisiter->nickname) == 0) {
1075 /* manual whois */
1076 firetalk_callback_gotinfo(c, whoisiter->nickname, whoisiter->info, 0, whoisiter->online, whoisiter->idle, whoisiter->flags);
1077 free(whoisiter->nickname);
1078 whoisiter->nickname = NULL;
1079 if (whoisiter->info != NULL) {
1080 free(whoisiter->info);
1081 whoisiter->info = NULL;
1082 }
1083 if (whoisiter2)
1084 whoisiter2->next = whoisiter->next;
1085 else
1086 c->whois_head = whoisiter->next;
1087 free(whoisiter);
1088 whoisiter = NULL;
1089 break;
1090 }
1091 whoisiter2 = whoisiter;
1092 }
1093 break;
1094 case 401: /* ERR_NOSUCHNICK */
1095 firetalk_callback_im_buddyonline(c, args[3], 0);
1096 case 441: /* ERR_USERNOTINCHANNEL */
1097 case 443: /* ERR_USERONCHANNEL */
1098 if (!strcasecmp(args[3], "NickServ") && c->passchange)
1099 c->passchange--;
1100 whoisiter2 = NULL;
1101 for (whoisiter = c->whois_head; whoisiter != NULL; whoisiter = whoisiter->next) {
1102 if (irc_compare_nicks(args[3], whoisiter->nickname) == 0) {
1103 free(whoisiter->nickname);
1104 whoisiter->nickname = NULL;
1105 if (whoisiter->info != NULL) {
1106 free(whoisiter->info);
1107 whoisiter->info = NULL;
1108 }
1109 if (whoisiter2)
1110 whoisiter2->next = whoisiter->next;
1111 else
1112 c->whois_head = whoisiter->next;
1113 free(whoisiter);
1114 whoisiter = NULL;
1115 firetalk_callback_error(c, FE_BADUSER, args[3], args[4]);
1116 break;
1117 }
1118 whoisiter2 = whoisiter;
1119 }
1120 break;
1121 case 403: /* ERR_NOSUCHCHANNEL */
1122 case 442: /* ERR_NOTONCHANNEL */
1123 firetalk_callback_error(c, FE_BADROOM, args[3], args[4]);
1124 break;
1125 case 404: /* ERR_CANNOTSENDTOCHAN */
1126 case 405: /* ERR_TOOMANYCHANNELS */
1127 case 471: /* ERR_CHANNELISFULL */
1128 case 473: /* ERR_INVITEONLYCHAN */
1129 case 474: /* ERR_BANNEDFROMCHAN */
1130 case 475: /* ERR_BADCHANNELKEY */
1131 firetalk_callback_error(c, FE_ROOMUNAVAILABLE, args[3], args[4]);
1132 break;
1133 case 412: /* ERR_NOTEXTTOSEND */
1134 firetalk_callback_error(c, FE_BADMESSAGE, NULL, args[4]);
1135 break;
1136 case 421: /* ERR_UNKNOWNCOMMAND */
1137 if (strcmp(args[3], "SILENCE") == 0)
1138 c->usesilence = 0;
1139 goto unhandled;
1140 break;
1141 case 433: /* ERR_NICKNAMEINUSE */
1142 firetalk_callback_error(c, FE_BADUSER, NULL, "Nickname in use.");
1143 break;
1144 case 482: /* ERR_CHANOPRIVSNEEDED */
1145 firetalk_callback_error(c, FE_NOPERMS, args[3], "You need to be a channel operator.");
1146 break;
1147 default:
1148 handled = 0;
1149 }
1150
1151 if (handled)
1152 return(FE_SUCCESS);
1153
1154 if (args[4] == NULL)
1155 goto unhandled;
1156
1157 if (strcmp(args[1],"MODE") == 0) {
1158 const char
1159 *source = irc_get_nickname(args[0]);
1160 int loc = 0,
1161 arg = 4,
1162 dir = 1;
1163 #ifdef RAWIRCMODES
1164 int i;
1165 char buf[512];
1166
1167 strcpy(buf, args[3]);
1168 for (i = 4; args[i] != NULL; i++) {
1169 strcat(buf, " ");
1170 strcat(buf, args[i]);
1171 }
1172 firetalk_callback_chat_modechanged(c, args[2], buf, source);
1173 #endif
1174
1175 while ((args[arg] != NULL) && (args[3][loc] != '\0')) {
1176 switch (args[3][loc++]) {
1177 case '+':
1178 dir = 1;
1179 break;
1180 case '-':
1181 dir = -1;
1182 break;
1183 case 'o':
1184 if (dir == 1) {
1185 firetalk_callback_chat_user_opped(c, args[2], args[arg++], source);
1186 if (irc_compare_nicks(args[arg-1], c->nickname) == FE_SUCCESS)
1187 firetalk_callback_chat_opped(c, args[2], source);
1188 } else if (dir == -1) {
1189 firetalk_callback_chat_user_deopped(c, args[2], args[arg++], source);
1190 if (irc_compare_nicks(args[arg-1], c->nickname) == FE_SUCCESS) {
1191 firetalk_callback_chat_deopped(c, args[2], source);
1192 if (c->identified == 1) {
1193 /* this is us, and we're identified, so we can request a reop */
1194 if (irc_send_printf(c,"PRIVMSG ChanServ :OP %s %s",args[2],c->nickname) != FE_SUCCESS) {
1195 irc_internal_disconnect(c,FE_PACKET);
1196 return(FE_PACKET);
1197 }
1198 }
1199 }
1200 }
1201 break;
1202 case 'k':
1203 if (dir == 1)
1204 firetalk_callback_chat_keychanged(c, args[2], args[arg], source);
1205 else
1206 firetalk_callback_chat_keychanged(c, args[2], NULL, source);
1207 arg++;
1208 break;
1209 case 'v':
1210 case 'b':
1211 case 'l':
1212 arg++;
1213 break;
1214 default:
1215 break;
1216 }
1217 }
1218 return(FE_SUCCESS);
1219 }
1220
1221 handled = 1;
1222 switch (numeric) {
1223 case 317: /* RPL_WHOISIDLE */
1224 for (whoisiter = c->whois_head; whoisiter != NULL; whoisiter = whoisiter->next)
1225 if (irc_compare_nicks(args[3], whoisiter->nickname) == 0) {
1226 whoisiter->online = atol(args[5]);
1227 whoisiter->idle = atol(args[4])/60;
1228 }
1229 break;
1230 case 312: /* RPL_WHOISSERVER */
1231 irc_addwhois(c, args[3], "<br>On server %s (%s)", args[4], irc_irc_to_html(args[5]));
1232 break;
1233 case 319: /* RPL_WHOISCHANNELS */
1234 irc_addwhois(c, args[3], "<br>On channels %s", irc_irc_to_html(args[4]));
1235 break;
1236 case 332: /* RPL_TOPIC */
1237 firetalk_callback_chat_gottopic(c, args[3], irc_irc_to_html(args[4]), NULL);
1238 break;
1239 default:
1240 handled = 0;
1241 }
1242
1243 if (handled)
1244 return(FE_SUCCESS);
1245
1246 if (args[5] == NULL)
1247 goto unhandled;
1248
1249 if (numeric == 353) { /* RPL_NAMREPLY */
1250 char *str = args[5];
1251
1252 while ((str != NULL) && (*str != 0)) {
1253 int oped = 0,
1254 voiced = 0;
1255 char *sp;
1256
1257 if ((sp = strchr(str, ' ')) != NULL)
1258 *sp = 0;
1259
1260 while ((*str != 0) && !irc_isnickfirst(*str)) {
1261 if (*str == '@')
1262 oped = 1;
1263 else if (*str == '+')
1264 voiced = 1;
1265 str++;
1266 }
1267
1268 firetalk_callback_chat_user_joined(c, args[4], str, NULL);
1269 firetalk_callback_im_buddyonline(c, str, 1);
1270 if (oped) {
1271 firetalk_callback_chat_user_opped(c, args[4], str, NULL);
1272 if (irc_compare_nicks(str, c->nickname) == FE_SUCCESS)
1273 firetalk_callback_chat_opped(c, args[4], NULL);
1274 }
1275
1276 if (sp != NULL)
1277 str = sp+1;
1278 else
1279 str = NULL;
1280 }
1281 return(FE_SUCCESS);
1282 }
1283
1284 if ((args[6] == NULL) || (args[7] == NULL))
1285 goto unhandled;
1286
1287 if (numeric == 311) { /* RPL_WHOISUSER */
1288 char *gecos = irc_irc_to_html(args[7]);
1289 int i;
1290
1291 for (i = 0; gecos[i] != 0; i++)
1292 if (strncasecmp(gecos+i, "<HTML>", sizeof("<HTML>")-1) == 0)
1293 break;
1294 if (gecos[i] != 0)
1295 irc_addwhois(c, args[3], "%s@%s (%s)", args[4], args[5], firetalk_htmlentities(gecos));
1296 else
1297 irc_addwhois(c, args[3], "%s@%s (<HTML>%s</HTML>)", args[4], args[5], gecos);
1298 return(FE_SUCCESS);
1299 } else if (numeric == 352) { /* WHO output */
1300 char buf[1024], buf2[1024];
1301 int i;
1302
1303 snprintf(buf, sizeof(buf), "%s %s %s %s", args[7], args[4], args[5], args[6]);
1304 for (i = 8; args[i] != NULL; i++)
1305 {
1306 snprintf(buf2, sizeof(buf2), ", %s", args[i]);
1307 strncat(buf, buf2, sizeof(buf)-strlen(buf)-1);
1308 }
1309 firetalk_callback_chat_getmessage(c, ":RAW", args[3], 0, buf);
1310 return(FE_SUCCESS);
1311 }
1312
1313 unhandled:
1314 if (inwhois) {
1315 char buf[1024];
1316 int i;
1317
1318 snprintf(buf, sizeof(buf), "<br>%s", args[3]);
1319 for (i = numeric?4:3; args[i+1] != NULL; i++)
1320 ;
1321 snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), " %s", args[i]);
1322 for (i = numeric?4:3; args[i+1] != NULL; i++)
1323 snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), " %s", args[i]);
1324 snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), " (%s)", args[1]);
1325 irc_addwhois(c, args[3], "%s", buf);
1326 } else {
1327 char buf[1024], buf2[1024];
1328 int i;
1329
1330 *buf = 0;
1331 for (i = 1; args[i+1] != NULL; i++)
1332 {
1333 snprintf(buf2, sizeof(buf2), "<B>%s</B>, ", args[i]);
1334 strncat(buf, buf2, sizeof(buf)-strlen(buf)-1);
1335 }
1336 strncat(buf, args[i], sizeof(buf)-strlen(buf)-1);
1337 firetalk_callback_chat_getmessage(c, ":RAW", irc_get_nickname(args[0]), 0, buf);
1338 }
1339 }
1340
1341 return(FE_SUCCESS);
1342 }
1343
1344 static fte_t
irc_got_data(irc_conn_t * c,unsigned char * buffer,unsigned short * bufferpos)1345 irc_got_data(irc_conn_t *c, unsigned char *buffer, unsigned short *bufferpos) {
1346 char **args;
1347
1348 while (((args = irc_recv_parse(c, buffer, bufferpos)) != NULL) && (args[1] != NULL)) {
1349 fte_t fte;
1350
1351 if ((fte = irc_got_data_parse(c, args)) != FE_SUCCESS)
1352 return(fte);
1353 }
1354
1355 return(FE_SUCCESS);
1356 }
1357
1358 static fte_t
irc_got_data_connecting(irc_conn_t * c,unsigned char * buffer,unsigned short * bufferpos)1359 irc_got_data_connecting(irc_conn_t *c, unsigned char *buffer, unsigned short *bufferpos) {
1360 char **args;
1361
1362 while (((args = irc_recv_parse(c, buffer, bufferpos)) != NULL) && (args[1] != NULL)) {
1363 if (strcmp(args[1], "ERROR") == 0) {
1364 irc_send_printf(c,"QUIT :error");
1365 if (args[2] == NULL)
1366 firetalk_callback_connectfailed(c, FE_PACKET, "Server returned ERROR");
1367 else
1368 firetalk_callback_connectfailed(c, FE_PACKET, args[2]);
1369 return(FE_PACKET);
1370 } else {
1371 switch (atoi(args[1])) {
1372 case 1: /* :PREFIX 001 sn :Welcome message */
1373 if (strcmp(c->nickname, args[2]) != 0) {
1374 firetalk_callback_user_nickchanged(c, c->nickname, args[2]);
1375 free(c->nickname);
1376 c->nickname = strdup(args[2]);
1377 if (c->nickname == NULL)
1378 abort();
1379 firetalk_callback_newnick(c, args[2]);
1380 }
1381 break;
1382 case 376: /* End of MOTD */
1383 case 422: /* MOTD is missing */
1384 firetalk_callback_doinit(c,c->nickname);
1385 firetalk_callback_connected(c);
1386 break;
1387 case 431:
1388 case 432:
1389 case 436:
1390 case 461:
1391 irc_send_printf(c,"QUIT :Invalid nickname");
1392 firetalk_callback_connectfailed(c,FE_BADUSER,"Invalid nickname");
1393 return(FE_BADUSER);
1394 case 433:
1395 irc_send_printf(c,"QUIT :Invalid nickname");
1396 firetalk_callback_connectfailed(c,FE_BADUSER,"Nickname in use");
1397 return(FE_BADUSER);
1398 case 465:
1399 irc_send_printf(c,"QUIT :banned");
1400 firetalk_callback_connectfailed(c,FE_BLOCKED,"You are banned");
1401 return(FE_BLOCKED);
1402 default: {
1403 fte_t fte;
1404
1405 if ((fte = irc_got_data_parse(c, args)) != FE_SUCCESS)
1406 return(fte);
1407 break;
1408 }
1409 }
1410 }
1411 }
1412
1413 return(FE_SUCCESS);
1414 }
1415
irc_chat_join(irc_conn_t * c,const char * const room)1416 static fte_t irc_chat_join(irc_conn_t *c, const char *const room) {
1417 return(irc_send_printf(c,"JOIN %s",room));
1418 }
1419
irc_chat_part(irc_conn_t * c,const char * const room)1420 static fte_t irc_chat_part(irc_conn_t *c, const char *const room) {
1421 return(irc_send_printf(c,"PART %s",room));
1422 }
1423
irc_chat_send_message(irc_conn_t * c,const char * const room,const char * const message,const int auto_flag)1424 static fte_t irc_chat_send_message(irc_conn_t *c, const char *const room, const char *const message, const int auto_flag) {
1425 if (strcasecmp(room, ":RAW") == 0)
1426 return(irc_send_printf(c, "%s", message));
1427
1428 #ifdef DEBUG_ECHO
1429 irc_echof(c, "chat_send_message", "c=%#p, room=%#p \"%s\", message=%#p \"%s\", auto_flag=%i\n", c, room, room, message, message, auto_flag);
1430 #endif
1431
1432 if (auto_flag)
1433 return(irc_send_printf(c, "NOTICE %s :%s", room, message));
1434 else
1435 return(irc_send_printf(c, "PRIVMSG %s :%s", room, message));
1436 }
1437
irc_chat_send_action(irc_conn_t * c,const char * const room,const char * const message,const int auto_flag)1438 static fte_t irc_chat_send_action(irc_conn_t *c, const char *const room, const char *const message, const int auto_flag) {
1439 return(irc_send_printf(c,"PRIVMSG %s :\001ACTION %s\001",room,message));
1440 }
1441
irc_chat_invite(irc_conn_t * c,const char * const room,const char * const who,const char * const message)1442 static fte_t irc_chat_invite(irc_conn_t *c, const char *const room, const char *const who, const char *const message) {
1443 return(irc_send_printf(c,"INVITE %s %s",who,room));
1444 }
1445
irc_im_send_message(irc_conn_t * c,const char * const dest,const char * const message,const int auto_flag)1446 static fte_t irc_im_send_message(irc_conn_t *c, const char *const dest, const char *const message, const int auto_flag) {
1447 struct s_firetalk_handle *fchandle;
1448 char buf[512+1];
1449
1450 if (strcasecmp(dest, ":RAW") == 0)
1451 return(irc_send_printf(c, "%s", message));
1452
1453 fchandle = firetalk_find_handle(c);
1454 if (auto_flag) {
1455 snprintf(buf, sizeof(buf), "NOTICE %s :%s", dest, message);
1456 firetalk_queue_append(buf, sizeof(buf), &(fchandle->subcode_replies), dest);
1457 } else {
1458 snprintf(buf, sizeof(buf), "PRIVMSG %s :%s", dest, message);
1459 firetalk_queue_append(buf, sizeof(buf), &(fchandle->subcode_requests), dest);
1460 }
1461 return(irc_send_printf(c, "%s", buf));
1462 }
1463
irc_im_send_action(irc_conn_t * c,const char * const dest,const char * const message,const int auto_flag)1464 static fte_t irc_im_send_action(irc_conn_t *c, const char *const dest, const char *const message, const int auto_flag) {
1465 return(irc_send_printf(c, "PRIVMSG %s :\001ACTION %s\001", dest, message));
1466 }
1467
irc_chat_set_topic(irc_conn_t * c,const char * const room,const char * const topic)1468 static fte_t irc_chat_set_topic(irc_conn_t *c, const char *const room, const char *const topic) {
1469 return(irc_send_printf(c,"TOPIC %s :%s",room,topic));
1470 }
1471
irc_chat_op(irc_conn_t * c,const char * const room,const char * const who)1472 static fte_t irc_chat_op(irc_conn_t *c, const char *const room, const char *const who) {
1473 return(irc_send_printf(c,"MODE %s +o %s",room,who));
1474 }
1475
irc_chat_deop(irc_conn_t * c,const char * const room,const char * const who)1476 static fte_t irc_chat_deop(irc_conn_t *c, const char *const room, const char *const who) {
1477 return(irc_send_printf(c,"MODE %s -o %s",room,who));
1478 }
1479
irc_chat_kick(irc_conn_t * c,const char * const room,const char * const who,const char * const reason)1480 static fte_t irc_chat_kick(irc_conn_t *c, const char *const room, const char *const who, const char *const reason) {
1481 if (reason)
1482 return(irc_send_printf(c,"KICK %s %s :%s",room,who,reason));
1483 else
1484 return(irc_send_printf(c,"KICK %s %s",room,who));
1485 }
1486
irc_chat_requestextended(client_t c,const char * const room)1487 static fte_t irc_chat_requestextended(client_t c, const char * const room) {
1488 return (irc_send_printf(c,"WHO %s", room));
1489 }
1490
irc_im_add_buddy(irc_conn_t * c,const char * const name,const char * const group,const char * const friendly)1491 static fte_t irc_im_add_buddy(irc_conn_t *c, const char *const name, const char *const group, const char *const friendly) {
1492 struct s_firetalk_handle *fchandle;
1493
1494 fchandle = firetalk_find_handle(c);
1495
1496 if (firetalk_user_visible(fchandle, name) == FE_SUCCESS)
1497 firetalk_callback_im_buddyonline(c, name, 1);
1498 return(FE_SUCCESS);
1499 }
1500
irc_im_remove_buddy(irc_conn_t * c,const char * const name,const char * const group)1501 static fte_t irc_im_remove_buddy(irc_conn_t *c, const char *const name, const char *const group) {
1502 firetalk_callback_im_buddyonline(c, name, 0);
1503
1504 return(FE_SUCCESS);
1505 }
1506
irc_im_add_deny(irc_conn_t * c,const char * const nickname)1507 static fte_t irc_im_add_deny(irc_conn_t *c, const char *const nickname) {
1508 if (c->usesilence == 1)
1509 return(irc_send_printf(c,"SILENCE +%s!*@*",nickname));
1510 else
1511 return(FE_SUCCESS);
1512 }
1513
irc_im_remove_deny(irc_conn_t * c,const char * const nickname)1514 static fte_t irc_im_remove_deny(irc_conn_t *c, const char *const nickname) {
1515 if (c->usesilence == 1)
1516 return(irc_send_printf(c,"SILENCE -%s!*@*",nickname));
1517 else
1518 return(FE_SUCCESS);
1519 }
1520
irc_im_upload_buddies(irc_conn_t * c)1521 static fte_t irc_im_upload_buddies(irc_conn_t *c) {
1522 return(FE_SUCCESS);
1523 }
1524
irc_im_upload_denies(irc_conn_t * c)1525 static fte_t irc_im_upload_denies(irc_conn_t *c) {
1526 return(FE_SUCCESS);
1527 }
1528
irc_im_evil(irc_conn_t * c,const char * const who)1529 static fte_t irc_im_evil(irc_conn_t *c, const char *const who) {
1530 return(FE_SUCCESS);
1531 }
1532
irc_set_privacy(client_t c,const char * const mode)1533 static fte_t irc_set_privacy(client_t c, const char *const mode) {
1534 return(FE_SUCCESS);
1535 }
1536
irc_get_info(irc_conn_t * c,const char * const nickname)1537 static fte_t irc_get_info(irc_conn_t *c, const char *const nickname) {
1538 struct s_irc_whois *whoistemp;
1539
1540 whoistemp = c->whois_head;
1541 c->whois_head = calloc(1, sizeof(struct s_irc_whois));
1542 if (c->whois_head == NULL)
1543 abort();
1544 c->whois_head->nickname = strdup(nickname);
1545 if (c->whois_head->nickname == NULL)
1546 abort();
1547 c->whois_head->flags = 0;
1548 c->whois_head->online = 0;
1549 c->whois_head->idle = 0;
1550 c->whois_head->info = NULL;
1551 c->whois_head->next = whoistemp;
1552 return(irc_send_printf(c, "WHOIS %s", nickname));
1553 }
1554
1555 static fte_t
irc_set_info(irc_conn_t * c,const char * const info)1556 irc_set_info(irc_conn_t *c, const char *const info) {
1557 return(FE_SUCCESS);
1558 }
1559
1560 static fte_t
irc_set_away(irc_conn_t * c,const char * const message,const int auto_flag)1561 irc_set_away(irc_conn_t *c, const char *const message, const int auto_flag) {
1562 if (message)
1563 return(irc_send_printf(c,"AWAY :%s",message));
1564 else
1565 return(irc_send_printf(c,"AWAY"));
1566 }
1567
irc_periodic(struct s_firetalk_handle * const conn)1568 static fte_t irc_periodic(struct s_firetalk_handle *const conn) {
1569 return(FE_SUCCESS);
1570 }
1571
1572 #if 0
1573 static fte_t irc_subcode_send_request(irc_conn_t *c, const char *const to, const char *const command, const char *const args) {
1574 if (args == NULL) {
1575 if (irc_send_printf(c,"PRIVMSG %s :\001%s\001",to,command) != 0) {
1576 irc_internal_disconnect(c,FE_PACKET);
1577 return(FE_PACKET);
1578 }
1579 } else {
1580 if (irc_send_printf(c,"PRIVMSG %s :\001%s %s\001",to,command,args) != 0) {
1581 irc_internal_disconnect(c,FE_PACKET);
1582 return(FE_PACKET);
1583 }
1584 }
1585 return(FE_SUCCESS);
1586 }
1587
1588 static fte_t irc_subcode_send_reply(irc_conn_t *c, const char *const to, const char *const command, const char *const args) {
1589 if (to == NULL)
1590 return(FE_SUCCESS);
1591
1592 if (args == NULL) {
1593 if (irc_send_printf(c,"NOTICE %s :\001%s\001",to,command) != 0) {
1594 irc_internal_disconnect(c,FE_PACKET);
1595 return(FE_PACKET);
1596 }
1597 } else {
1598 if (irc_send_printf(c,"NOTICE %s :\001%s %s\001",to,command,args) != 0) {
1599 irc_internal_disconnect(c,FE_PACKET);
1600 return(FE_PACKET);
1601 }
1602 }
1603 return(FE_SUCCESS);
1604 }
1605 #endif
1606
irc_ctcp_encode(irc_conn_t * c,const char * const command,const char * const message)1607 static char *irc_ctcp_encode(irc_conn_t *c, const char *const command, const char *const message) {
1608 char *str;
1609
1610 if (message != NULL) {
1611 str = malloc(1 + strlen(command) + 1 + strlen(message) + 1 + 1);
1612 if (str == NULL)
1613 abort();
1614 sprintf(str, "\001%s %s\001", command, message);
1615 } else {
1616 str = malloc(1 + strlen(command) + 1 + 1);
1617 if (str == NULL)
1618 abort();
1619 sprintf(str, "\001%s\001", command);
1620 }
1621
1622 return(str);
1623 }
1624
1625 const firetalk_protocol_t firetalk_protocol_irc = {
1626 strprotocol: "IRC",
1627 default_server: "irc.n.ml.org",
1628 default_port: 6667,
1629 default_buffersize: 1024/2,
1630 periodic: irc_periodic,
1631 preselect: irc_preselect,
1632 postselect: irc_postselect,
1633 got_data: irc_got_data,
1634 got_data_connecting: irc_got_data_connecting,
1635 comparenicks: irc_compare_nicks,
1636 isprintable: irc_isprint,
1637 disconnect: irc_disconnect,
1638 signon: irc_signon,
1639 get_info: irc_get_info,
1640 set_info: irc_set_info,
1641 set_away: irc_set_away,
1642 set_nickname: irc_set_nickname,
1643 set_password: irc_set_password,
1644 im_add_buddy: irc_im_add_buddy,
1645 im_remove_buddy: irc_im_remove_buddy,
1646 im_add_deny: irc_im_add_deny,
1647 im_remove_deny: irc_im_remove_deny,
1648 im_upload_buddies: irc_im_upload_buddies,
1649 im_upload_denies: irc_im_upload_denies,
1650 im_send_message: irc_im_send_message,
1651 im_send_action: irc_im_send_action,
1652 im_evil: irc_im_evil,
1653 chat_join: irc_chat_join,
1654 chat_part: irc_chat_part,
1655 chat_invite: irc_chat_invite,
1656 chat_set_topic: irc_chat_set_topic,
1657 chat_op: irc_chat_op,
1658 chat_deop: irc_chat_deop,
1659 chat_kick: irc_chat_kick,
1660 chat_send_message: irc_chat_send_message,
1661 chat_send_action: irc_chat_send_action,
1662 // subcode_send_request: irc_subcode_send_request,
1663 // subcode_send_reply: irc_subcode_send_reply,
1664 subcode_encode: irc_ctcp_encode,
1665 set_privacy: irc_set_privacy,
1666 room_normalize: irc_normalize_room_name,
1667 create_handle: irc_create_handle,
1668 destroy_handle: irc_destroy_handle,
1669 };
1670