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],"&amp;",5)) {
218 					output[o++] = '&';
219 					i += 5;
220 				} else if (!strncasecmp(&string[i],"&gt;",4)) {
221 					output[o++] = '>';
222 					i += 4;
223 				} else if (!strncasecmp(&string[i],"&lt;",4)) {
224 					output[o++] = '<';
225 					i += 4;
226 				} else if (!strncasecmp(&string[i],"&nbsp;",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],"&amp;",5);
501 			o += 5;
502 			break;
503 		  case '<':
504 			memcpy(&output[o],"&lt;",4);
505 			o += 4;
506 			break;
507 		  case '>':
508 			memcpy(&output[o],"&gt;",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], "&nbsp;", 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