1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <ctype.h>
5 #include <stdarg.h>
6 #include <time.h>
7 #include <errno.h>
8 
9 #define TOC_HTML_MAXLEN 65536
10 #define TOC_CLIENTSEND_MAXLEN (2*1024)
11 #define TOC_SERVERSEND_MAXLEN (8*1024)
12 #define ECT_TOKEN	"<font ECT=\""
13 #define ECT_ENDING	"\"></font>"
14 
15 struct s_toc_room {
16 	struct s_toc_room *next;
17 	int	exchange;
18 	char	*name;
19 	long	id;
20 	unsigned char
21 		invited:1,
22 		joined:1;
23 };
24 
25 struct s_toc_connection {
26 	unsigned short local_sequence;		/* our sequence number */
27 	unsigned short remote_sequence;		/* toc's sequence number */
28 	char	*nickname,			/* our nickname (correctly spaced) */
29 		*awaymsg;
30 	time_t	awaysince;
31 	struct s_toc_room *room_head;
32 	time_t	lastself,			/* last time we checked our own status */
33 		lasttalk;			/* last time we talked */
34 	long	lastidle;			/* last idle that we told the server */
35 	unsigned char
36 		passchange:1,			/* whether we're changing our password right now */
37 		gotconfig:1;
38 	int	connectstate,
39 		permit_mode;
40 	char	buddybuf[1024];
41 	int	buddybuflen;
42 	char	*buddybuflastgroup;
43 };
44 
45 typedef struct s_toc_connection *client_t;
46 #define _HAVE_CLIENT_T
47 
48 #include "firetalk-int.h"
49 #include "firetalk.h"
50 
51 #define SFLAP_FRAME_SIGNON ((unsigned char)1)
52 #define SFLAP_FRAME_DATA ((unsigned char)2)
53 #define SFLAP_FRAME_ERROR ((unsigned char)3)
54 #define SFLAP_FRAME_SIGNOFF ((unsigned char)4)
55 #define SFLAP_FRAME_KEEPALIVE ((unsigned char)5)
56 
57 #define SIGNON_STRING "FLAPON\r\n\r\n"
58 
59 #define TOC_HEADER_LENGTH 6
60 #define TOC_SIGNON_LENGTH 24
61 #define TOC_HOST_SIGNON_LENGTH 4
62 #define TOC_USERNAME_MAXLEN 16
63 
64 #include "toc2_uuids.h"
65 
66 static char lastinfo[TOC_USERNAME_MAXLEN+1] = "";
67 
68 /* Internal Function Declarations */
69 
70 static unsigned short toc_fill_header(unsigned char *const header, unsigned char const frame_type, unsigned short const sequence, unsigned short const length);
71 static unsigned short toc_fill_signon(unsigned char *const signon, const char *const username);
72 static unsigned char toc_get_frame_type_from_header(const unsigned char *const header);
73 static unsigned short toc_get_sequence_from_header(const unsigned char *const header);
74 static unsigned short toc_get_length_from_header(const unsigned char *const header);
75 static char *toc_quote(const char *const string, const int outside_flag);
76 static int toc_internal_disconnect(client_t c, const int error);
77 static int toc_internal_split_exchange(const char *const string);
78 static char *toc_internal_split_name(const char *const string);
79 static fte_t toc_send_printf(client_t c, const char *const format, ...);
80 
81 #include <assert.h>
82 #ifdef DEBUG_ECHO
83 extern void *curconn;
84 extern void status_echof(void *conn, const unsigned char *format, ...);
85 
toc_echof(client_t c,const char * const where,const char * const format,...)86 static void toc_echof(client_t c, const char *const where, const char *const format, ...) {
87 	va_list ap;
88 	char	buf[8*1024];
89 	int	len;
90 	void	statrefresh(void);
91 
92 	va_start(ap, format);
93 	vsnprintf(buf, sizeof(buf), format, ap);
94 	va_end(ap);
95 
96 	len = strlen(buf);
97 	while ((len > 0) && (buf[len-1] == '\n'))
98 		buf[--len] = 0;
99 
100 	if (*buf != 0)
101 		status_echof(curconn, firetalk_htmlentities(buf));
102 
103 	statrefresh();
104 }
105 
toc_echo_send(client_t c,const char * const where,const unsigned char * const data,size_t _length)106 static void toc_echo_send(client_t c, const char *const where, const unsigned char *const data, size_t _length) {
107 	unsigned char	ft;
108 	unsigned short	sequence,
109 			length;
110 
111 	assert(_length > 4);
112 
113 	length = toc_get_length_from_header(data);
114 	ft = toc_get_frame_type_from_header(data);
115 	sequence = toc_get_sequence_from_header(data);
116 
117 	assert(length == (_length-6));
118 
119 	toc_echof(c, where, "frame=%X, sequence=out:%i, length=%i, value=[%.*s]\n",
120 		ft, sequence, length, length, data+TOC_HEADER_LENGTH);
121 }
122 #endif
123 
124 /* Internal Function Definitions */
125 
toc_find_packet(client_t c,unsigned char * buffer,unsigned short * bufferpos,char * outbuffer,const int frametype,unsigned short * l)126 static fte_t toc_find_packet(client_t c, unsigned char *buffer, unsigned short *bufferpos, char *outbuffer, const int frametype, unsigned short *l) {
127 	unsigned char	ft;
128 	unsigned short	sequence,
129 			length;
130 
131 	if (*bufferpos < TOC_HEADER_LENGTH) /* don't have the whole header yet */
132 		return(FE_NOTFOUND);
133 
134 	length = toc_get_length_from_header(buffer);
135 	if (length > (TOC_SERVERSEND_MAXLEN - TOC_HEADER_LENGTH)) {
136 		toc_internal_disconnect(c, FE_PACKETSIZE);
137 		return(FE_DISCONNECT);
138 	}
139 
140 	if (*bufferpos < length + TOC_HEADER_LENGTH) /* don't have the whole packet yet */
141 		return(FE_NOTFOUND);
142 
143 	ft = toc_get_frame_type_from_header(buffer);
144 	sequence = toc_get_sequence_from_header(buffer);
145 
146 	memcpy(outbuffer, &buffer[TOC_HEADER_LENGTH], length);
147 	*bufferpos -= length + TOC_HEADER_LENGTH;
148 	memmove(buffer, &buffer[TOC_HEADER_LENGTH + length], *bufferpos);
149 	outbuffer[length] = '\0';
150 	*l = length;
151 
152 #ifdef DEBUG_ECHO
153 	if (ft != 5) {
154 		char	buf[1024*8];
155 		int	j;
156 
157 		assert(length < sizeof(buf));
158 		memmove(buf, outbuffer, length+1);
159 		for (j = 0; j < length; j++)
160 			if (buf[j] == 0)
161 				buf[j] = '0';
162 
163 		toc_echof(c, "find_packet", "frame=%X, sequence=in:%i, length=%i, value=[%s]\n",
164 			ft, sequence, length, buf);
165 	}
166 #endif
167 
168 	if (frametype == SFLAP_FRAME_SIGNON)
169 		c->remote_sequence = sequence;
170 	else {
171 		if (sequence != ++c->remote_sequence) {
172 			toc_internal_disconnect(c, FE_SEQUENCE);
173 			return(FE_DISCONNECT);
174 		}
175 	}
176 
177 	if (ft == frametype)
178 		return(FE_SUCCESS);
179 	else
180 		return(FE_WEIRDPACKET);
181 }
182 
toc_fill_header(unsigned char * const header,unsigned char const frame_type,unsigned short const sequence,unsigned short const length)183 static unsigned short toc_fill_header(unsigned char *const header,
184 	unsigned char const frame_type, unsigned short const sequence,
185 	unsigned short const length) {
186 	header[0] = '*';		/* byte 0, length 1, magic 42 */
187 	header[1] = frame_type;		/* byte 1, length 1, frame type (defined above SFLAP_FRAME_* */
188 	header[2] = sequence/256;	/* byte 2, length 2, sequence number, network byte order */
189 	header[3] = (unsigned char) sequence%256;
190 	header[4] = length/256;		/* byte 4, length 2, length, network byte order */
191 	header[5] = (unsigned char) length%256;
192 	return(6+length);
193 }
194 
toc_fill_signon(unsigned char * const signon,const char * const username)195 static unsigned short toc_fill_signon(unsigned char *const signon, const char *const username) {
196 	size_t	length = strlen(username);
197 
198 	signon[0] = 0;		/* byte 0, length 4, flap version (1) */
199 	signon[1] = 0;
200 	signon[2] = 0;
201 	signon[3] = 1;
202 	signon[4] = 0;		/* byte 4, length 2, tlv tag (1) */
203 	signon[5] = 1;
204 	signon[6] = length/256;	/* byte 6, length 2, username length, network byte order */
205 	signon[7] = (unsigned char)length%256;
206 	memcpy(signon+8, username, length);
207 	return(length + 8);
208 }
209 
aim_interpolate_variables(const char * const input,const char * const nickname)210 static char *aim_interpolate_variables(const char *const input, const char *const nickname) {
211 	static char output[16384]; /* 2048 / 2 * 16 + 1 (max size with a string full of %n's, a 16-char nick and a null at the end) */
212 	int o = 0,gotpercent = 0;
213 	size_t nl,dl,tl,l,i;
214 	char date[15],tim[15];
215 	{ /* build the date and time */
216 		int hour;
217 		int am = 1;
218 		struct tm *t;
219 		time_t b;
220 		b = time(NULL);
221 		t = localtime(&b);
222 		if (t == NULL)
223 			return(NULL);
224 		hour = t->tm_hour;
225 		if (hour >= 12)
226 			am = 0;
227 		if (hour > 12)
228 			hour -= 12;
229 		if (hour == 0)
230 			hour = 12;
231 		sprintf(tim,"%d:%02d:%02d %s",hour,t->tm_min,t->tm_sec,am == 1 ? "AM" : "PM");
232 		snprintf(date,15,"%d/%d/%d",t->tm_mon + 1,t->tm_mday,t->tm_year + 1900);
233 	}
234 	nl = strlen(nickname);
235 	dl = strlen(date);
236 	tl = strlen(tim);
237 	l = strlen(input);
238 	for (i = 0; i < l; i++) {
239 		switch (input[i]) {
240 			case '%':
241 				if (gotpercent == 1) {
242 					gotpercent = 0;
243 					output[o++] = '%';
244 					output[o++] = '%';
245 				} else
246 					gotpercent = 1;
247 				break;
248 			case 'n':
249 				if (gotpercent == 1) {
250 					gotpercent = 0;
251 					memcpy(&output[o],nickname,nl);
252 					o += nl;
253 				} else
254 					output[o++] = 'n';
255 				break;
256 			case 'd':
257 				if (gotpercent == 1) {
258 					gotpercent = 0;
259 					memcpy(&output[o],date,dl);
260 					o += dl;
261 				} else
262 					output[o++] = 'd';
263 				break;
264 			case 't':
265 				if (gotpercent == 1) {
266 					gotpercent = 0;
267 					memcpy(&output[o],tim,tl);
268 					o += tl;
269 				} else
270 					output[o++] = 't';
271 				break;
272 			default:
273 				if (gotpercent == 1) {
274 					gotpercent = 0;
275 					output[o++] = '%';
276 				}
277 				output[o++] = input[i];
278 
279 		}
280 	}
281 	output[o] = 0;
282 	return(output);
283 }
284 
aim_normalize_room_name(const char * const name)285 static const char *aim_normalize_room_name(const char *const name) {
286 	static char newname[2048];
287 
288 	if (name == NULL)
289 		return(NULL);
290 	if (strchr(name+1, ':') != NULL)
291 		return(name);
292 	if (strlen(name) >= (sizeof(newname)-2))
293 		return(NULL);
294 
295 	strcpy(newname, "4:");
296 	strcpy(newname+2, name);
297 
298 	return(newname);
299 }
300 
301 #define STRNCMP(x,y)	(strncmp((x), (y), sizeof(y)-1))
htmlclean(const char * str)302 static char *htmlclean(const char *str) {
303 	static char
304 		buf[2048];
305 	int	i, b = 0;
306 
307 	for (i = 0; (str[i] != 0) && (b < sizeof(buf)-1); i++)
308 		if (STRNCMP(str+i, "&gt;") == 0) {
309 			buf[b++] = '>';
310 			i += sizeof("&gt;")-2;
311 		} else if (STRNCMP(str+i, "&lt;") == 0) {
312 			buf[b++] = '<';
313 			i += sizeof("&lt;")-2;
314 		} else if (STRNCMP(str+i, "&quot;") == 0) {
315 			buf[b++] = '"';
316 			i += sizeof("&quot;")-2;
317 		} else if (STRNCMP(str+i, "&nbsp;") == 0) {
318 			buf[b++] = ' ';
319 			i += sizeof("&nbsp;")-2;
320 		} else if (STRNCMP(str+i, "&amp;") == 0) {
321 			buf[b++] = '&';
322 			i += sizeof("&amp;")-2;
323 		} else
324 			buf[b++] = str[i];
325 	buf[b] = 0;
326 
327 	return(buf);
328 }
329 #undef STRNCMP
330 
aim_handle_ect(void * conn,const char * const from,char * const message,const int reply)331 static char *aim_handle_ect(void *conn, const char *const from,
332 		char *const message, const int reply) {
333 	char	*ectbegin, *ectend, *textbegin, *textend;
334 
335 	while ((ectbegin = strstr(message, ECT_TOKEN)) != NULL) {
336 		textbegin = ectbegin+sizeof(ECT_TOKEN)-1;
337 
338 		if ((textend = strstr(textbegin, ECT_ENDING)) != NULL) {
339 			char	*arg;
340 
341 			*textend = 0;
342 			ectend = textend+sizeof(ECT_ENDING)-1;
343 
344 			if ((arg = strchr(textbegin, ' ')) != NULL) {
345 				*arg++ = 0;
346 				if (reply == 1)
347 					firetalk_callback_subcode_reply(conn, from, textbegin, htmlclean(arg));
348 				else
349 					firetalk_callback_subcode_request(conn, from, textbegin, htmlclean(arg));
350 			} else {
351 				if (reply == 1)
352 					firetalk_callback_subcode_reply(conn, from, textbegin, NULL);
353 				else
354 					firetalk_callback_subcode_request(conn, from, textbegin, NULL);
355 			}
356 			memmove(ectbegin, ectend, strlen(ectend)+1);
357 		} else
358 			break;
359 	}
360 	return(message);
361 }
362 
toc_im_add_buddy_flush(client_t c)363 static fte_t toc_im_add_buddy_flush(client_t c) {
364 	if (c->buddybuflen > 0) {
365 		free(c->buddybuflastgroup);
366 		c->buddybuflastgroup = strdup("");
367 		c->buddybuflen = 0;
368 		return(toc_send_printf(c, "toc2_new_buddies {%S}", c->buddybuf));
369 	}
370 	return(FE_SUCCESS);
371 }
372 
toc_postselect(client_t c,fd_set * read,fd_set * write,fd_set * except)373 static fte_t toc_postselect(client_t c, fd_set *read, fd_set *write, fd_set *except) {
374 	return(FE_SUCCESS);
375 }
376 
toc_get_frame_type_from_header(const unsigned char * const header)377 static unsigned char toc_get_frame_type_from_header(const unsigned char *const header) {
378 	return(header[1]);
379 }
380 
toc_get_sequence_from_header(const unsigned char * const header)381 static unsigned short toc_get_sequence_from_header(const unsigned char *const header) {
382 	unsigned short sequence;
383 
384 	sequence = ntohs(*((unsigned short *)(&header[2])));
385 	return(sequence);
386 }
387 
toc_get_length_from_header(const unsigned char * const header)388 static unsigned short toc_get_length_from_header(const unsigned char *const header) {
389 	unsigned short length;
390 
391 	length = ntohs(*((unsigned short *)(&header[4])));
392 	return(length);
393 }
394 
toc_quote(const char * string,const int outside_flag)395 static char *toc_quote(const char *string, const int outside_flag) {
396 	static char output[TOC_CLIENTSEND_MAXLEN];
397 	size_t	length,
398 		counter;
399 	int	newcounter;
400 
401 	while (*string == ' ')
402 		string++;
403 
404 	length = strlen(string);
405 	if (outside_flag == 1) {
406  		newcounter = 1;
407 		output[0] = '"';
408 	} else
409 		newcounter = 0;
410 
411 	while ((length > 0) && (string[length-1] == ' '))
412 		length--;
413 
414 	for (counter = 0; counter < length; counter++)
415 		if (string[counter] == '$' || string[counter] == '{' || string[counter] == '}' || string[counter] == '[' || string[counter] == ']' || string[counter] == '(' || string[counter] == ')' || string[counter] == '\'' || string[counter] == '`' || string[counter] == '"' || string[counter] == '\\') {
416 			if (newcounter > (sizeof(output)-4))
417 				return(NULL);
418 			output[newcounter++] = '\\';
419 			output[newcounter++] = string[counter];
420 		} else {
421 			if (newcounter > (sizeof(output)-3))
422 				return(NULL);
423 			output[newcounter++] = string[counter];
424 		}
425 
426 	if (outside_flag == 1)
427 		output[newcounter++] = '"';
428 	output[newcounter] = 0;
429 
430 	return(output);
431 }
432 
toc_hash_password(const char * const password)433 static char *toc_hash_password(const char *const password) {
434 #define HASH "Tic/Toc"
435 	const unsigned char hash[] = HASH;
436 	static char output[TOC_CLIENTSEND_MAXLEN];
437 	size_t	length;
438 	int	i, newcounter;
439 
440 	length = strlen(password);
441 
442 	output[0] = '0';
443 	output[1] = 'x';
444 
445 	newcounter = 2;
446 
447 	for (i = 0; i < length; i++) {
448 		if (newcounter >= sizeof(output)-2-1)
449 			return(NULL);
450 		sprintf(output+newcounter, "%02X", password[i]^hash[i%(sizeof(hash)-1)]);
451 		newcounter += 2;
452 	}
453 
454 	output[newcounter] = 0;
455 
456 	return(output);
457 }
458 
toc_compare_nicks(const char * s1,const char * s2)459 static fte_t toc_compare_nicks (const char *s1, const char *s2) {
460 	if ((s1 == NULL) || (s2 == NULL))
461 		return(FE_NOMATCH);
462 
463 	while (*s1 == ' ')
464 		s1++;
465 	while (*s2 == ' ')
466 		s2++;
467 	while (*s1 != 0) {
468 		if (tolower((unsigned char)(*s1)) != tolower((unsigned char)(*s2)))
469 			return(FE_NOMATCH);
470 		s1++;
471 		s2++;
472 		while (*s1 == ' ')
473 			s1++;
474 		while (*s2 == ' ')
475 			s2++;
476 	}
477 	if (*s2 != 0)
478 		return(FE_NOMATCH);
479 	return(FE_SUCCESS);
480 }
481 
toc_internal_disconnect(client_t c,const int error)482 static int toc_internal_disconnect(client_t c, const int error) {
483 	if (c->nickname != NULL) {
484 		free(c->nickname);
485 		c->nickname = NULL;
486 	}
487 	if (c->awaymsg != NULL) {
488 		free(c->awaymsg);
489 		c->awaymsg = NULL;
490 
491 		assert(c->awaysince > 0);
492 		c->awaysince = 0;
493 	}
494 	if (c->room_head != NULL) {
495 		struct s_toc_room *iter, *iternext;
496 
497 		for (iter = c->room_head; iter != NULL; iter = iternext) {
498 			iternext = iter->next;
499 			free(iter->name);
500 			free(iter);
501 		}
502 		c->room_head = NULL;
503 	}
504 	if (c->buddybuflastgroup != NULL) {
505 		free(c->buddybuflastgroup);
506 		c->buddybuflastgroup = strdup("");
507 		c->buddybuflen = 0;
508 	}
509 	toc_send_printf(c, "toc_set_dir %s", "");
510 	toc_send_printf(c, "toc_noop");
511 
512 	firetalk_callback_disconnect(c, error);
513 	return(FE_SUCCESS);
514 }
515 
toc_internal_add_room(client_t c,const char * const name,const int exchange)516 static int toc_internal_add_room(client_t c, const char *const name, const int exchange) {
517 	struct s_toc_room *iter;
518 
519 	iter = c->room_head;
520 	c->room_head = calloc(1, sizeof(struct s_toc_room));
521 	if (c->room_head == NULL)
522 		abort();
523 
524 	c->room_head->next = iter;
525 	c->room_head->name = strdup(name);
526 	if (c->room_head->name == NULL)
527 		abort();
528 	c->room_head->exchange = exchange;
529 	return(FE_SUCCESS);
530 }
531 
toc_internal_set_joined(client_t c,const long id)532 static int toc_internal_set_joined(client_t c, const long id) {
533 	struct s_toc_room *iter;
534 
535 	for (iter = c->room_head; iter != NULL; iter = iter->next)
536 		if (iter->joined == 0)
537 			if (iter->id == id) {
538 				iter->joined = 1;
539 				return(FE_SUCCESS);
540 			}
541 	return(FE_NOTFOUND);
542 }
543 
toc_internal_set_id(client_t c,const char * const name,const int exchange,const long id)544 static int toc_internal_set_id(client_t c, const char *const name, const int exchange, const long id) {
545 	struct s_toc_room *iter;
546 
547 	for (iter = c->room_head; iter != NULL; iter = iter->next)
548 		if (iter->joined == 0)
549 			if ((iter->exchange == exchange) && (toc_compare_nicks(iter->name, name) == 0)) {
550 				iter->id = id;
551 				return(FE_SUCCESS);
552 			}
553 	return(FE_NOTFOUND);
554 }
555 
toc_internal_find_exchange(client_t c,const char * const name)556 static int toc_internal_find_exchange(client_t c, const char *const name) {
557 	struct s_toc_room *iter;
558 
559 	for (iter = c->room_head; iter != NULL; iter = iter->next)
560 		if (iter->joined == 0)
561 			if (toc_compare_nicks(iter->name, name) == 0)
562 				return(iter->exchange);
563 	firetalkerror = FE_NOTFOUND;
564 	return(0);
565 }
566 
toc_internal_find_room_id(client_t c,const char * const name)567 static int toc_internal_find_room_id(client_t c, const char *const name) {
568 	struct s_toc_room *iter;
569 	char	*namepart;
570 	int	exchange;
571 
572 	namepart = toc_internal_split_name(name);
573 	exchange = toc_internal_split_exchange(name);
574 
575 	for (iter = c->room_head; iter != NULL; iter = iter->next)
576 		if (iter->exchange == exchange)
577 			if (toc_compare_nicks(iter->name, namepart) == 0)
578 				return(iter->id);
579 	firetalkerror = FE_NOTFOUND;
580 	return(0);
581 }
582 
toc_internal_find_room_name(client_t c,const long id)583 static char *toc_internal_find_room_name(client_t c, const long id) {
584 	struct s_toc_room *iter;
585 	static char newname[TOC_CLIENTSEND_MAXLEN];
586 
587 	for (iter = c->room_head; iter != NULL; iter = iter->next)
588 		if (iter->id == id) {
589 			snprintf(newname, sizeof(newname), "%d:%s",
590 				iter->exchange, iter->name);
591 			return(newname);
592 		}
593 	firetalkerror = FE_NOTFOUND;
594 	return(NULL);
595 }
596 
toc_internal_split_exchange(const char * const string)597 static int toc_internal_split_exchange(const char *const string) {
598 	return(atoi(string));
599 }
600 
toc_internal_split_name(const char * const string)601 static char *toc_internal_split_name(const char *const string) {
602 	return(strchr(string, ':')+1);
603 }
604 
toc_get_tlv_value(char ** args,int arg,const int type)605 static const char *toc_get_tlv_value(char **args, int arg, const int type) {
606 	for (; args[arg] && args[arg+1]; arg += 2)
607 		if (atoi(args[arg]) == type)
608 			return(firetalk_debase64(args[arg+1]));
609 	return(NULL);
610 }
611 
toc_internal_set_room_invited(client_t c,const char * const name,const int invited)612 static int toc_internal_set_room_invited(client_t c, const char *const name, const int invited) {
613 	struct s_toc_room *iter;
614 
615 	for (iter = c->room_head; iter != NULL; iter = iter->next)
616 		if (toc_compare_nicks(iter->name,name) == 0) {
617 			iter->invited = invited;
618 			return(FE_SUCCESS);
619 		}
620 
621 	return(FE_NOTFOUND);
622 }
623 
toc_internal_get_room_invited(client_t c,const char * const name)624 static int toc_internal_get_room_invited(client_t c, const char *const name) {
625 	struct s_toc_room *iter;
626 
627 	for (iter = c->room_head; iter != NULL; iter = iter->next)
628 		if (toc_compare_nicks(aim_normalize_room_name(iter->name),name) == 0)
629 			return(iter->invited);
630 
631 	return(-1);
632 }
633 
toc_send_printf(client_t c,const char * const format,...)634 static fte_t toc_send_printf(client_t c, const char *const format, ...) {
635 	va_list	ap;
636 	size_t	i,
637 		datai = TOC_HEADER_LENGTH;
638 	char	data[TOC_CLIENTSEND_MAXLEN];
639 
640 	va_start(ap, format);
641 	for (i = 0; format[i] != 0; i++) {
642 		if (format[i] == '%') {
643 			switch (format[++i]) {
644 			  case 'd':
645 			  case 'i': {
646 					int	i = va_arg(ap, int);
647 
648 					i = snprintf(data+datai, sizeof(data)-datai, "%i", i);
649 					if (i > 0)
650 						datai += i;
651 					break;
652 				}
653 			  case 'x': {
654 					int	i = va_arg(ap, int);
655 
656 					i = snprintf(data+datai, sizeof(data)-datai, "%x", i);
657 					if (i > 0)
658 						datai += i;
659 					break;
660 				}
661 			  case 's': {
662 					const char
663 						*s = toc_quote(va_arg(ap, char *), 1);
664 					size_t	slen = strlen(s);
665 
666 					if ((datai+slen) > (sizeof(data)-1))
667 						return(FE_PACKETSIZE);
668 					strcpy(data+datai, s);
669 					datai += slen;
670 					break;
671 				}
672 			  case 'S': {
673 					const char
674 						*s = va_arg(ap, char *);
675 					size_t	slen = strlen(s);
676 
677 					if ((datai+slen) > (sizeof(data)-1))
678 						return(FE_PACKETSIZE);
679 					strcpy(data+datai, s);
680 					datai += slen;
681 					break;
682 				}
683 			  case '%':
684 				data[datai++] = '%';
685 				break;
686 			}
687 		} else {
688 			data[datai++] = format[i];
689 			if (datai > (sizeof(data)-1))
690 				return(FE_PACKETSIZE);
691 		}
692 	}
693 	va_end(ap);
694 
695 #ifdef DEBUG_ECHO
696 	toc_echof(c, "send_printf", "frame=%X, sequence=out:%i, length=%i, value=[%.*s]\n",
697 		SFLAP_FRAME_DATA, c->local_sequence+1,
698 		datai-TOC_HEADER_LENGTH, datai-TOC_HEADER_LENGTH, data+TOC_HEADER_LENGTH);
699 #endif
700 
701 	{
702 		struct s_firetalk_handle
703 			*fchandle;
704 		unsigned short
705 			length;
706 
707 		fchandle = firetalk_find_handle(c);
708 		data[datai] = 0;
709 		length = toc_fill_header((unsigned char *)data, SFLAP_FRAME_DATA, ++c->local_sequence, datai-TOC_HEADER_LENGTH+1);
710 		firetalk_internal_send_data(fchandle, data, length);
711 	}
712 	return(FE_SUCCESS);
713 }
714 
toc_get_arg0(char * const instring)715 static char *toc_get_arg0(char *const instring) {
716 	static char data[TOC_SERVERSEND_MAXLEN];
717 	char	*colon;
718 
719 	if (strlen(instring) > TOC_SERVERSEND_MAXLEN) {
720 		firetalkerror = FE_PACKETSIZE;
721 		return(NULL);
722 	}
723 
724 	strncpy(data, instring, sizeof(data)-1);
725 	data[sizeof(data)-1] = 0;
726 	colon = strchr(data, ':');
727 	if (colon != NULL)
728 		*colon = 0;
729 	return(data);
730 }
731 
toc_parse_args(char * str,const int maxargs,const char sep)732 static char **toc_parse_args(char *str, const int maxargs, const char sep) {
733 	static char *args[256];
734 	int	curarg = 0;
735 	char	*colon;
736 
737 	while ((curarg < (maxargs-1)) && (curarg < 256)
738 		&& ((colon = strchr(str, sep)) != NULL)) {
739 		args[curarg++] = str;
740 		*colon = 0;
741 		str = colon+1;
742 	}
743 	args[curarg++] = str;
744 	args[curarg] = NULL;
745 	return(args);
746 }
747 
748 /* External Function Definitions */
749 
toc_isprint(const int c)750 static fte_t toc_isprint(const int c) {
751 	if ((c >= 0) && (c <= 255) && (isprint(c) || (c >= 160)))
752 		return(FE_SUCCESS);
753 	return(FE_INVALIDFORMAT);
754 }
755 
toc_create_handle()756 static client_t toc_create_handle() {
757 	client_t c;
758 
759 	c = calloc(1, sizeof(struct s_toc_connection));
760 	if (c == NULL)
761 		abort();
762 
763 	c->lasttalk = time(NULL);
764 	c->buddybuflastgroup = strdup("");
765 
766 	return(c);
767 }
768 
toc_destroy_handle(client_t c)769 static void toc_destroy_handle(client_t c) {
770 	toc_internal_disconnect(c, FE_USERDISCONNECT);
771 
772 	assert(c->nickname == NULL);
773 	assert(c->awaymsg == NULL);
774 	assert(c->awaysince == 0);
775 	assert(c->room_head == NULL);
776 	free(c->buddybuflastgroup);
777 	free(c);
778 }
779 
toc_disconnect(client_t c)780 static fte_t toc_disconnect(client_t c) {
781 	return(toc_internal_disconnect(c, FE_USERDISCONNECT));
782 }
783 
toc_signon(client_t c,const char * const username)784 static fte_t toc_signon(client_t c, const char *const username) {
785 	struct s_firetalk_handle *conn;
786 
787 	/* fill & send the flap signon packet */
788 
789 	conn = firetalk_find_handle(c);
790 
791 	c->lasttalk = time(NULL);
792 	c->connectstate = 0;
793 	c->permit_mode = 0;
794 	c->gotconfig = 0;
795 	free(c->nickname);
796 	c->nickname = strdup(username);
797 	if (c->nickname == NULL)
798 		abort();
799 
800 	/* send the signon string to indicate that we're speaking FLAP here */
801 
802 #ifdef DEBUG_ECHO
803 	toc_echof(c, "signon", "frame=0, length=%i, value=[%s]\n", strlen(SIGNON_STRING), SIGNON_STRING);
804 #endif
805 	firetalk_internal_send_data(conn, SIGNON_STRING, sizeof(SIGNON_STRING)-1);
806 
807 	return(FE_SUCCESS);
808 }
809 
810 #ifdef ENABLE_NEWGROUPS
toc_im_remove_group(client_t c,const char * const group)811 static fte_t toc_im_remove_group(client_t c, const char *const group) {
812 	struct s_firetalk_handle *fchandle;
813 	char	buf[TOC_CLIENTSEND_MAXLEN];
814 	int	count = 0, slen = 0;
815 
816 	fchandle = firetalk_find_handle(c);
817 
818 	if (fchandle->buddy_head != NULL) {
819 		struct s_firetalk_buddy *iter;
820 
821 		for (iter = fchandle->buddy_head; iter != NULL; iter = iter->next)
822 			if (toc_compare_nicks(iter->group, group) != FE_NOMATCH) {
823 				char	*str = toc_quote(iter->nickname, 1);
824 				int	i = strlen(str);
825 
826 				if (slen+i+2 >= sizeof(buf))
827 					return(FE_PACKET);
828 
829 				buf[slen++] = ' ';
830 				strcpy(buf+slen, str);
831 				slen += i;
832 				count++;
833 			}
834 	}
835 
836 	if (count > 0)
837 		toc_send_printf(c, "toc2_remove_buddy%S %s", buf, group);
838 	toc_send_printf(c, "toc2_del_group %s", group);
839 	return(toc_send_printf(c, "toc_get_status %s", c->nickname));
840 }
841 #endif
842 
toc_im_remove_buddy(client_t c,const char * const name,const char * const group)843 static fte_t toc_im_remove_buddy(client_t c, const char *const name, const char *const group) {
844 	return(toc_send_printf(c, "toc2_remove_buddy %s %s", name, group));
845 }
846 
toc_im_add_buddy(client_t c,const char * const name,const char * const group,const char * const friendly)847 static fte_t toc_im_add_buddy(client_t c, const char *const name, const char *const group, const char *const friendly) {
848 	char	buf[1024];
849 	int	slen;
850 
851 	if (c->gotconfig == 0)
852 		return(FE_SUCCESS);
853 
854 	if (strcmp(c->buddybuflastgroup, group) == 0) {
855 		if (friendly != NULL)
856 			snprintf(buf, sizeof(buf), "b:%s:%s\n", name, friendly);
857 		else
858 			snprintf(buf, sizeof(buf), "b:%s\n", name);
859 	} else {
860 		if (friendly != NULL)
861 			snprintf(buf, sizeof(buf), "g:%s\nb:%s:%s\n", group, name, friendly);
862 		else
863 			snprintf(buf, sizeof(buf), "g:%s\nb:%s\n", group, name);
864 		free(c->buddybuflastgroup);
865 		c->buddybuflastgroup = strdup(group);
866 		if (c->buddybuflastgroup == NULL)
867 			abort();
868 	}
869 	slen = strlen(buf);
870 
871 	if ((c->buddybuflen+slen+1) >= sizeof(c->buddybuf))
872 		toc_im_add_buddy_flush(c);
873 	if ((c->buddybuflen+slen+1) >= sizeof(c->buddybuf))
874 		return(FE_PACKET);
875 
876 	strcpy(c->buddybuf+c->buddybuflen, buf);
877 	c->buddybuflen += slen;
878 
879 	return(FE_SUCCESS);
880 }
881 
toc_im_add_deny(client_t c,const char * const name)882 static fte_t toc_im_add_deny(client_t c, const char *const name) {
883 	return(toc_send_printf(c, "toc2_add_deny %s", name));
884 }
885 
toc_im_remove_deny(client_t c,const char * const name)886 static fte_t toc_im_remove_deny(client_t c, const char *const name) {
887 	return(toc_send_printf(c, "toc2_remove_deny %s", name));
888 }
889 
toc_im_upload_buddies(client_t c)890 static fte_t toc_im_upload_buddies(client_t c) {
891 	struct s_firetalk_handle *fchandle;
892 
893 	fchandle = firetalk_find_handle(c);
894 
895 	if (fchandle->buddy_head != NULL) {
896 		struct s_firetalk_buddy *buddyiter;
897 
898 		for (buddyiter = fchandle->buddy_head; buddyiter != NULL; buddyiter = buddyiter->next)
899 			toc_im_add_buddy(c, buddyiter->nickname, buddyiter->group, buddyiter->friendly);
900 	}
901 
902 	return(FE_SUCCESS);
903 }
904 
toc_im_upload_denies(client_t c)905 static fte_t toc_im_upload_denies(client_t c) {
906 	char	data[TOC_CLIENTSEND_MAXLEN];
907 	struct s_firetalk_deny *denyiter;
908 	unsigned short length;
909 	struct s_firetalk_handle *fchandle;
910 
911 	fchandle = firetalk_find_handle(c);
912 
913 	if (fchandle->deny_head == NULL)
914 		return(FE_SUCCESS);
915 
916 	data[sizeof(data)-1] = 0;
917 	strncpy(data+TOC_HEADER_LENGTH, "toc_add_deny", (size_t)(sizeof(data)-TOC_HEADER_LENGTH-1));
918 	for (denyiter = fchandle->deny_head; denyiter != NULL; denyiter = denyiter->next) {
919 		strncat(data+TOC_HEADER_LENGTH, " ", (size_t)(sizeof(data)-TOC_HEADER_LENGTH-1));
920 		strncat(data+TOC_HEADER_LENGTH, toc_quote(denyiter->nickname, 0), (size_t)(sizeof(data)-TOC_HEADER_LENGTH-1));
921 		if (strlen(data+TOC_HEADER_LENGTH) > 2000) {
922 			length = toc_fill_header((unsigned char *)data, SFLAP_FRAME_DATA, ++c->local_sequence, strlen(&data[TOC_HEADER_LENGTH])+1);
923 
924 #ifdef DEBUG_ECHO
925 			toc_echo_send(c, "im_upload_denies", data, length);
926 #endif
927 			firetalk_internal_send_data(fchandle, data, length);
928 			strncpy(data+TOC_HEADER_LENGTH, "toc_add_deny", (size_t)(sizeof(data)-TOC_HEADER_LENGTH-1));
929 		}
930 	}
931 	length = toc_fill_header((unsigned char *)data, SFLAP_FRAME_DATA, ++c->local_sequence, strlen(&data[TOC_HEADER_LENGTH])+1);
932 
933 #ifdef DEBUG_ECHO
934 	toc_echo_send(c, "im_upload_denies", data, length);
935 #endif
936 	firetalk_internal_send_data(fchandle, data, length);
937 	return(FE_SUCCESS);
938 }
939 
toc_internal_send_message(client_t c,const char * const dest,const unsigned char * const message,const int isauto,firetalk_queue_t * queue)940 static fte_t toc_internal_send_message(client_t c, const char *const dest, const unsigned char *const message, const int isauto, firetalk_queue_t *queue) {
941 	char	buf[TOC_CLIENTSEND_MAXLEN];
942 	int	i, j, len = strlen(message);
943 
944 	assert(c != NULL);
945 	assert(dest != NULL);
946 	assert(*dest != 0);
947 	assert(message != NULL);
948 
949 	if (!isauto)
950 		c->lasttalk = time(NULL);
951 
952 	if (len >= sizeof(buf)-1)
953 		return(FE_PACKETSIZE);
954 
955 	for (j = i = 0; (i < len) && (j < sizeof(buf)-1); i++)
956 		if (toc_isprint(message[i]) == FE_SUCCESS)
957 			buf[j++] = message[i];
958 		else {
959 			char	numbuf[10];
960 
961 			snprintf(numbuf, sizeof(numbuf), "&#%i;", message[i]);
962 			if (j+strlen(numbuf) >= sizeof(buf)-1)
963 				return(FE_PACKETSIZE);
964 			strcpy(buf+j, numbuf);
965 			j += strlen(numbuf);
966 		}
967 	buf[j] = 0;
968 
969 	firetalk_queue_append(buf, sizeof(buf), queue, dest);
970 
971 #ifdef DEBUG_ECHO
972 	toc_echof(c, "internal_send_message", "dest=[%s] message=[%s] len=%i", dest, message, len);
973 #endif
974 
975 	return(toc_send_printf(c, "toc2_send_im %s %s%S", dest, buf, isauto?" auto":""));
976 }
977 
toc_im_send_reply(client_t c,const char * const dest,const char * const message)978 static fte_t toc_im_send_reply(client_t c, const char *const dest, const char *const message) {
979 	struct s_firetalk_handle *fchandle;
980 
981 	assert(dest != NULL);
982 	assert(message != NULL);
983 
984 	if (strcasecmp(dest, ":RAW") == 0)
985 		return(toc_send_printf(c, "%S", message));
986 
987 	fchandle = firetalk_find_handle(c);
988 	return(toc_internal_send_message(c, dest, aim_interpolate_variables(message, dest), 1, &(fchandle->subcode_replies)));
989 }
990 
toc_im_send_message(client_t c,const char * const dest,const char * const message,const int auto_flag)991 static fte_t toc_im_send_message(client_t c, const char *const dest, const char *const message, const int auto_flag) {
992 	struct s_firetalk_handle *fchandle;
993 
994 	assert(dest != NULL);
995 	assert(message != NULL);
996 
997 	if (strcasecmp(dest, ":RAW") == 0)
998 		return(toc_send_printf(c, "%S", message));
999 
1000 	if (auto_flag)
1001 		return(toc_im_send_reply(c, dest, message));
1002 
1003 	fchandle = firetalk_find_handle(c);
1004 	return(toc_internal_send_message(c, dest, message, 0, &(fchandle->subcode_requests)));
1005 }
1006 
toc_im_send_action(client_t c,const char * const dest,const char * const message,const int auto_flag)1007 static fte_t toc_im_send_action(client_t c, const char *const dest, const char *const message, const int auto_flag) {
1008 	struct s_firetalk_handle *fchandle;
1009 	char	tempbuf[TOC_CLIENTSEND_MAXLEN];
1010 
1011 	if (strcasecmp(dest, ":RAW") == 0)
1012 		return(toc_send_printf(c, "%S", message));
1013 
1014 	if (strlen(message) > 2042)
1015 		return(FE_PACKETSIZE);
1016 
1017 	fchandle = firetalk_find_handle(c);
1018 	snprintf(tempbuf, sizeof(tempbuf), "/me %s", message);
1019 	return(toc_internal_send_message(c, dest, tempbuf, 0, &(fchandle->subcode_requests)));
1020 }
1021 
toc_preselect(client_t c,fd_set * read,fd_set * write,fd_set * except,int * n)1022 static fte_t toc_preselect(client_t c, fd_set *read, fd_set *write, fd_set *except, int *n) {
1023 	toc_im_add_buddy_flush(c);
1024 
1025 	return(FE_SUCCESS);
1026 }
1027 
toc_get_info(client_t c,const char * const nickname)1028 static fte_t toc_get_info(client_t c, const char *const nickname) {
1029 	strncpy(lastinfo, nickname, (size_t)TOC_USERNAME_MAXLEN);
1030 	lastinfo[TOC_USERNAME_MAXLEN] = 0;
1031 	return(toc_send_printf(c, "toc_locate_user %s", nickname));
1032 }
1033 
toc_ctcp_encode(client_t c,const char * const command,const char * const message)1034 static char *toc_ctcp_encode(client_t c, const char *const command, const char *const message) {
1035 	char	*str;
1036 
1037 	if (message != NULL) {
1038 		const char *encodedmsg = firetalk_htmlentities(message);
1039 
1040 		str = malloc(sizeof(ECT_TOKEN)-1+strlen(command)+1+strlen(encodedmsg)+sizeof(ECT_ENDING)-1+1);
1041 		if (str == NULL)
1042 			abort();
1043 		sprintf(str, ECT_TOKEN "%s %s" ECT_ENDING, command, encodedmsg);
1044 	} else {
1045 		str = malloc(sizeof(ECT_TOKEN)-1+strlen(command)+sizeof(ECT_ENDING)-1+1);
1046 		if (str == NULL)
1047 			abort();
1048 		sprintf(str, ECT_TOKEN "%s" ECT_ENDING, command);
1049 	}
1050 
1051 	return(str);
1052 }
1053 
toc_set_info(client_t c,const char * const info)1054 static fte_t toc_set_info(client_t c, const char *const info) {
1055 	char	profile[1024],	/* profiles can only be a maximum of 1023 characters long, so this is good */
1056 		*versionctcp = NULL,
1057 		*awayctcp = NULL;
1058 	size_t	infolen = strlen(info),
1059 		extralen = 0;
1060 
1061 	if (infolen >= sizeof(profile)) {
1062 		firetalk_callback_error(c, FE_MESSAGETRUNCATED, NULL, "Profile too long");
1063 		return(FE_MESSAGETRUNCATED);
1064 	}
1065 	if ((versionctcp = firetalk_subcode_get_request_reply(c, "VERSION")) == NULL)
1066 		versionctcp = PACKAGE_NAME ":" PACKAGE_VERSION ":unknown";
1067 	if ((versionctcp = toc_ctcp_encode(c, "VERSION", versionctcp)) != NULL)
1068 		extralen += strlen(versionctcp);
1069 
1070 	if (infolen+extralen >= 1024) {
1071 		firetalk_callback_error(c, FE_MESSAGETRUNCATED, NULL, "Profile+away message too long, truncating");
1072 		infolen = 1024-extralen-1;
1073 	}
1074 	snprintf(profile, sizeof(profile), "%.*s%s%s", infolen, info, versionctcp?versionctcp:"", awayctcp?awayctcp:"");
1075 	free(versionctcp);
1076 	free(awayctcp);
1077 	return(toc_send_printf(c, "toc_set_info %s", profile));
1078 }
1079 
toc_set_away(client_t c,const char * const message,const int auto_flag)1080 static fte_t toc_set_away(client_t c, const char *const message, const int auto_flag) {
1081 	if (message != NULL) {
1082 		if (strlen(message) >= 1024) {
1083 			firetalk_callback_error(c, FE_MESSAGETRUNCATED, NULL, "Message too long");
1084 			return(FE_MESSAGETRUNCATED);
1085 		}
1086 		free(c->awaymsg);
1087 		c->awaymsg = strdup(message);
1088 		if (c->awaymsg == NULL)
1089 			abort();
1090 		c->awaysince = time(NULL);
1091 		return(toc_send_printf(c, "toc_set_away %s", message));
1092 	} else {
1093 		if (c->awaymsg != NULL) {
1094 			free(c->awaymsg);
1095 			c->awaymsg = NULL;
1096 		}
1097 		c->awaysince = 0;
1098 		return(toc_send_printf(c, "toc_set_away"));
1099 	}
1100 }
1101 
toc_set_nickname(client_t c,const char * const nickname)1102 static fte_t toc_set_nickname(client_t c, const char *const nickname) {
1103 	return(toc_send_printf(c, "toc_format_nickname %s", nickname));
1104 }
1105 
toc_set_password(client_t c,const char * const oldpass,const char * const newpass)1106 static fte_t toc_set_password(client_t c, const char *const oldpass, const char *const newpass) {
1107 	c->passchange++;
1108 	return(toc_send_printf(c, "toc_change_passwd %s %s", oldpass, newpass));
1109 }
1110 
toc_set_privacy(client_t c,const char * const mode)1111 static fte_t toc_set_privacy(client_t c, const char *const mode) {
1112 	if (strcasecmp(mode, "ALLOW ALL") == 0)
1113 		c->permit_mode = 1;
1114 	else if (strcasecmp(mode, "BLOCK ALL") == 0)
1115 		c->permit_mode = 2;
1116 	else if (strcasecmp(mode, "ALLOW PERMIT") == 0)
1117 		c->permit_mode = 3;
1118 	else if (strcasecmp(mode, "BLOCK DENY") == 0)
1119 		c->permit_mode = 4;
1120 	else if (strcasecmp(mode, "ALLOW BUDDY") == 0)
1121 		c->permit_mode = 5;
1122 	else {
1123 		firetalk_callback_error(c, FE_BADMESSAGE, NULL, "Supported modes: ALLOW ALL, BLOCK ALL, ALLOW PERMIT, BLOCK DENY, ALLOW BUDDY");
1124 		return(FE_BADMESSAGE);
1125 	}
1126 
1127 	return(toc_send_printf(c, "toc2_set_pdmode %i", c->permit_mode));
1128 }
1129 
toc_im_evil(client_t c,const char * const who)1130 static fte_t toc_im_evil(client_t c, const char *const who) {
1131 	return(toc_send_printf(c, "toc_evil %s norm", who));
1132 }
1133 
toc_uuid(const char * const uuid,int * A1,int * A2,int * B,int * C,int * D,int * E1,int * E2,int * E3)1134 static void toc_uuid(const char *const uuid, int *A1, int *A2, int *B, int *C, int *D, int *E1, int *E2, int *E3) {
1135 	if (strlen(uuid) <= 4) {
1136 		*A1 = 0x0946;
1137 		*A2 = strtol(uuid, NULL, 16);
1138 		*B = 0x4C7F;
1139 		*C = 0x11D1;
1140 		*D = 0x8222;
1141 		*E1 = 0x4445;
1142 		*E2 = 0x5354;
1143 		*E3 = 0x0000;
1144 	} else {
1145 		char	buf[5];
1146 
1147 		buf[4] = 0;
1148 		strncpy(buf, uuid+0, 4);
1149 		assert((buf[4] == 0) && (strlen(buf) == 4));
1150 		*A1 = strtol(buf, NULL, 16);
1151 		strncpy(buf, uuid+4, 4);
1152 		assert((buf[4] == 0) && (strlen(buf) == 4));
1153 		*A2 = strtol(buf, NULL, 16);
1154 		strncpy(buf, uuid+9, 4);
1155 		assert((buf[4] == 0) && (strlen(buf) == 4));
1156 		*B = strtol(buf, NULL, 16);
1157 		strncpy(buf, uuid+14, 4);
1158 		assert((buf[4] == 0) && (strlen(buf) == 4));
1159 		*C = strtol(buf, NULL, 16);
1160 		strncpy(buf, uuid+19, 4);
1161 		assert((buf[4] == 0) && (strlen(buf) == 4));
1162 		*D = strtol(buf, NULL, 16);
1163 		strncpy(buf, uuid+24, 4);
1164 		assert((buf[4] == 0) && (strlen(buf) == 4));
1165 		*E1 = strtol(buf, NULL, 16);
1166 		strncpy(buf, uuid+28, 4);
1167 		assert((buf[4] == 0) && (strlen(buf) == 4));
1168 		*E2 = strtol(buf, NULL, 16);
1169 		strncpy(buf, uuid+32, 4);
1170 		assert((buf[4] == 0) && (strlen(buf) == 4));
1171 		*E3 = strtol(buf, NULL, 16);
1172 	}
1173 }
1174 
toc_cap(int A1,int A2,int B,int C,int D,int E1,int E2,int E3)1175 static int toc_cap(int A1, int A2, int B, int C, int D, int E1, int E2, int E3) {
1176 	int	j;
1177 
1178 	for (j = 0; j < sizeof(toc_uuids)/sizeof(*toc_uuids); j++)
1179 		if (((toc_uuids[j].A1 == A1) || (toc_uuids[j].A1 == -1))
1180 		 && ((toc_uuids[j].A2 == A2) || (toc_uuids[j].A2 == -1))
1181 		 && ((toc_uuids[j].B  == B)  || (toc_uuids[j].B  == -1))
1182 		 && ((toc_uuids[j].C  == C)  || (toc_uuids[j].C  == -1))
1183 		 && ((toc_uuids[j].D  == D)  || (toc_uuids[j].D  == -1))
1184 		 && ((toc_uuids[j].E1 == E1) || (toc_uuids[j].E1 == -1))
1185 		 && ((toc_uuids[j].E2 == E2) || (toc_uuids[j].E2 == -1))
1186 		 && ((toc_uuids[j].E3 == E3) || (toc_uuids[j].E3 == -1)))
1187 		return(j);
1188 	return(-1);
1189 }
1190 
1191 static struct {
1192 	fte_t		fte;
1193 	unsigned char	hastarget:1;
1194 	const char	*str;
1195 } toc2_errors[] = {
1196   /* General Errors */
1197 	/* 900 */ {	FE_UNKNOWN,		0, NULL },
1198 	/* 901 */ {	FE_USERUNAVAILABLE,	1, NULL },
1199 			/* TOC1 $1 not currently available */
1200 			/* TOC2 User Not Logged In
1201 				You can only send Instant Messanges to, or Chat with, buddies who are currently signed on. You need to wait and try again later when your buddy is signed on.
1202 			*/
1203 	/* 902 */ {	FE_USERUNAVAILABLE,	1, "You have either tried to warn someone you haven't sent a message to yet, or you have already warned them too many times today." },
1204 			/* TOC1 Warning of $1 not currently available */
1205 			/* TOC2 Warning Not Allowed
1206 				You have either tried to warn someone you haven't sent an Instant Message to yet; or, you have already warned them too many times today.
1207 			*/
1208 	/* 903 */ {	FE_TOOFAST,		0, "You are sending commands too fast." },
1209 			/* TOC1 A message has been dropped, you are exceeding the server speed limit */
1210 			/* TOC2 Server Limit Exceeded
1211 				If you send messages too fast, you will exceed the server's capacity to handle them. Please try to send your message again.
1212 			*/
1213 	/* 904 */ {	FE_UNKNOWN,		0, NULL },
1214 	/* 905 */ {	FE_UNKNOWN,		0, NULL },
1215 	/* 906 */ {	FE_UNKNOWN,		0, NULL },
1216 	/* 907 */ {	FE_UNKNOWN,		0, NULL },
1217 	/* 908 */ {	FE_UNKNOWN,		0, NULL },
1218 	/* 909 */ {	FE_UNKNOWN,		0, NULL },
1219 
1220 
1221   /* Admin Errors */
1222 	/* 910 */ {	FE_UNKNOWN,		0, NULL },
1223 	/* 911 */ {	FE_BADUSER,		0, NULL },
1224 	/* 912 */ {	FE_UNKNOWN,		0, NULL },
1225 	/* 913 */ {	FE_UNKNOWN,		0, NULL },
1226 	/* 914 */ {	FE_UNKNOWN,		0, NULL },
1227 	/* 915 */ {	FE_UNKNOWN,		0, NULL },
1228 	/* 916 */ {	FE_UNKNOWN,		0, NULL },
1229 	/* 917 */ {	FE_UNKNOWN,		0, NULL },
1230 	/* 918 */ {	FE_UNKNOWN,		0, NULL },
1231 	/* 919 */ {	FE_UNKNOWN,		0, NULL },
1232 
1233 
1234   /* Unknown */
1235 	/* 920 */ {	FE_UNKNOWN,		0, NULL },
1236 	/* 921 */ {	FE_UNKNOWN,		0, NULL },
1237 	/* 922 */ {	FE_UNKNOWN,		0, NULL },
1238 	/* 923 */ {	FE_UNKNOWN,		0, NULL },
1239 	/* 924 */ {	FE_UNKNOWN,		0, NULL },
1240 	/* 925 */ {	FE_UNKNOWN,		0, NULL },
1241 	/* 926 */ {	FE_UNKNOWN,		0, NULL },
1242 	/* 927 */ {	FE_UNKNOWN,		0, NULL },
1243 	/* 928 */ {	FE_UNKNOWN,		0, NULL },
1244 	/* 929 */ {	FE_UNKNOWN,		0, NULL },
1245 
1246 
1247   /* Buddy List Errors */
1248 	/* 930 */ {	FE_UNKNOWN,		0, "The Buddy List server is unavailable at this time. Wait a few minutes, and try to sign on again." },
1249 			/* TOC1 UNDOCUMENTED */
1250 			/* TOC2 Buddy List Server is Unavailable
1251 				The Buddy List server is unavailable at this time, and your AIM Express (TM) connection will be closed.
1252 				Wait a few minutes, and try to sign on again.
1253 			*/
1254 	/* 931 */ {	FE_UNKNOWN,		0, "Unable to add buddy or group. You may have the max allowed buddies or groups or are trying to add a buddy to a group that doesn't exist and cannot be created." },
1255 	/* 932 */ {	FE_UNKNOWN,		0, NULL },
1256 	/* 933 */ {	FE_UNKNOWN,		0, NULL },
1257 	/* 934 */ {	FE_UNKNOWN,		0, NULL },
1258 	/* 935 */ {	FE_UNKNOWN,		0, NULL },
1259 	/* 936 */ {	FE_UNKNOWN,		0, NULL },
1260 	/* 937 */ {	FE_UNKNOWN,		0, NULL },
1261 	/* 938 */ {	FE_UNKNOWN,		0, NULL },
1262 	/* 939 */ {	FE_UNKNOWN,		0, NULL },
1263 
1264 
1265   /* Unknown */
1266 	/* 940 */ {	FE_UNKNOWN,		0, NULL },
1267 	/* 941 */ {	FE_UNKNOWN,		0, NULL },
1268 	/* 942 */ {	FE_UNKNOWN,		0, NULL },
1269 	/* 943 */ {	FE_UNKNOWN,		0, NULL },
1270 	/* 944 */ {	FE_UNKNOWN,		0, NULL },
1271 	/* 945 */ {	FE_UNKNOWN,		0, NULL },
1272 	/* 946 */ {	FE_UNKNOWN,		0, NULL },
1273 	/* 947 */ {	FE_UNKNOWN,		0, NULL },
1274 	/* 948 */ {	FE_UNKNOWN,		0, NULL },
1275 	/* 949 */ {	FE_UNKNOWN,		0, NULL },
1276 
1277 
1278   /* Chat Errors */
1279 	/* 950 */ {	FE_ROOMUNAVAILABLE,	1, NULL },
1280 			/* TOC1 Chat in $1 is unavailable. */
1281 			/* TOC2 User Unavailable
1282 				You can only Chat with buddies who are currently signed on and available. AOL Instant Messenger users have the option to put up an "Unavailable" notice, indicating that they are away from their computer or simply too busy to answer messages.
1283 				You need to wait and try again later when your buddy is available.
1284 			*/
1285 	/* 951 */ {	FE_UNKNOWN,		0, NULL },
1286 	/* 952 */ {	FE_UNKNOWN,		0, NULL },
1287 	/* 953 */ {	FE_UNKNOWN,		0, NULL },
1288 	/* 954 */ {	FE_UNKNOWN,		0, NULL },
1289 	/* 955 */ {	FE_UNKNOWN,		0, NULL },
1290 	/* 956 */ {	FE_UNKNOWN,		0, NULL },
1291 	/* 957 */ {	FE_UNKNOWN,		0, NULL },
1292 	/* 958 */ {	FE_UNKNOWN,		0, NULL },
1293 	/* 959 */ {	FE_UNKNOWN,		0, NULL },
1294 
1295 
1296   /* IM & Info Errors */
1297 	/* 960 */ {	FE_TOOFAST,		1, "You are sending messages too fast." },
1298 			/* TOC1 You are sending message too fast to $1 */
1299 			/* TOC2 Server Limit Exceeded
1300 				Sending messages too fast will exceed the server's capacity to handle them.
1301 			*/
1302 	/* 961 */ {	FE_INCOMINGERROR,	1, "You missed a message because it was too big." },
1303 			/* TOC1 You missed an im from $1 because it was too big. */
1304 			/* TOC2 Server Limit Exceeded
1305 				The sender sent their messages too fast, exceeding the server's capacity to handle them.
1306 			*/
1307 	/* 962 */ {	FE_INCOMINGERROR,	1, "You missed a message because it was sent too fast." },
1308 			/* TOC1 You missed an im from $1 because it was sent too fast. */
1309 			/* TOC2 Server Limit Exceeded
1310 				The Instant Messenging system is designed to accommodate normal conversions, consisting of a few lines of text at a time. Larger messages, like a magazine article or a full-length letter, might get dropped. Please ask the sender to send their message through e-mail instead.
1311 			*/
1312 	/* 963 */ {	FE_UNKNOWN,		0, NULL },
1313 	/* 964 */ {	FE_UNKNOWN,		0, NULL },
1314 	/* 965 */ {	FE_UNKNOWN,		0, NULL },
1315 	/* 966 */ {	FE_UNKNOWN,		0, NULL },
1316 	/* 967 */ {	FE_UNKNOWN,		0, NULL },
1317 	/* 968 */ {	FE_UNKNOWN,		0, NULL },
1318 	/* 969 */ {	FE_UNKNOWN,		0, NULL },
1319 
1320 
1321   /* Dir Errors */
1322 	/* 970 */ {	FE_SUCCESS,		0, NULL },
1323 			/* TOC1 Failure */
1324 			/* TOC2 UNDOCUMENTED */
1325 	/* 971 */ {	FE_USERINFOUNAVAILABLE,	0, "Too many matches." },
1326 			/* TOC1 Too many matches */
1327 			/* TOC2 UNDOCUMENTED */
1328 	/* 972 */ {	FE_USERINFOUNAVAILABLE,	0, "Need more qualifiers." },
1329 			/* TOC1 Need more qualifiers */
1330 			/* TOC2 UNDOCUMENTED */
1331 	/* 973 */ {	FE_USERINFOUNAVAILABLE,	0, "Directory service unavailable." },
1332 			/* TOC1 Dir service temporarily unavailable */
1333 			/* TOC2 UNDOCUMENTED */
1334 	/* 974 */ {	FE_USERINFOUNAVAILABLE,	0, "Email lookup restricted." },
1335 			/* TOC1 Email lookup restricted */
1336 			/* TOC2 UNDOCMENTED */
1337 	/* 975 */ {	FE_USERINFOUNAVAILABLE,	0, "Keyword ignored." },
1338 			/* TOC1 Keyword Ignored */
1339 			/* TOC2 UNDOCUMENTED */
1340 	/* 976 */ {	FE_USERINFOUNAVAILABLE,	0, "No keywords." },
1341 			/* TOC1 No Keywords */
1342 			/* TOC2 UNDOCUMENTED */
1343 	/* 977 */ {	FE_SUCCESS,		0, NULL },
1344 			/* TOC1 Language not supported */
1345 			/* TOC2 UNDOCUMENTED */
1346 	/* 978 */ {	FE_USERINFOUNAVAILABLE,	0, "Country not supported." },
1347 			/* TOC1 Country not supported */
1348 			/* TOC2 UNDOCUMENTED */
1349 	/* 979 */ {	FE_USERINFOUNAVAILABLE,	0, "Failure unknown." },
1350 			/* TOC1 Failure unknown $1 */
1351 			/* TOC2 UNDOCUMENTED */
1352 
1353 
1354   /* Auth errors */
1355 	/* 980 */ {	FE_BADUSERPASS,		0, "The Screen Name or Password was incorrect. Passwords are case sensitive, so make sure your caps lock key isn't on." },
1356 			/* TOC1 Incorrect nickname or password. */
1357 			/* TOC2 Incorrect Screen Name or Password
1358 				The Screen Name or Password was incorrect. Passwords are case sensitive, so make sure your caps lock key isn't on.
1359 				AIM Express (TM) uses your AIM Screen Name and Password, not your AOL Password. If you need an AIM password please visit AOL Instant Messenger.
1360 				Forgot your password? Click here.
1361 			*/
1362 	/* 981 */ {	FE_SERVER,		0, "The service is unavailable currently. Please try again in a few minutes." },
1363 			/* TOC1 The service is temporarily unavailable. */
1364 			/* TOC2 Service Offline
1365 				The service is unavailable currently. Please try again in a few minutes.
1366 			*/
1367 	/* 982 */ {	FE_BLOCKED,		0, "Your warning level is too high to sign on." },
1368 			/* TOC1 Your warning level is currently too high to sign on. */
1369 			/* TOC2 Warning Level too High
1370 				Your warning level is current too high to sign on. You will need to wait between a few minutes and several hours before you can log back in.
1371 			*/
1372 	/* 983 */ {	FE_BLOCKED,		0, "You have been connected and disconnecting too frequently. Wait 10 minutes and try again. If you continue to try, you will need to wait even longer." },
1373 			/* TOC1 You have been connecting and disconnecting too frequently.  Wait 10 minutes and try again. If you continue to try, you will need to wait even longer. */
1374 			/* TOC2 Connecting too Frequently
1375 				You have been connecting and disconnecting too frequently. You will need to wait around 10 minutes before you will be allowed to sign on again.
1376 			*/
1377 	/* 984 */ {	FE_UNKNOWN,		0, NULL },
1378 	/* 985 */ {	FE_UNKNOWN,		0, NULL },
1379 	/* 986 */ {	FE_UNKNOWN,		0, NULL },
1380 	/* 987 */ {	FE_UNKNOWN,		0, NULL },
1381 	/* 988 */ {	FE_UNKNOWN,		0, NULL },
1382 	/* 989 */ {	FE_UNKNOWN,		0, "An unknown signon error has occured. Please wait 10 minutes and try again." },
1383 			/* TOC1 An unknown signon error has occurred $1 */
1384 			/* TOC2 Unknown Error
1385 				An unknown signon error has occured. Please wait 10 minutes and try again.
1386 			*/
1387 
1388 
1389   /* Client Errors */
1390 	/* 990 */ {	FE_UNKNOWN,		0, NULL },
1391 	/* 991 */ {	FE_UNKNOWN,		0, NULL },
1392 	/* 992 */ {	FE_UNKNOWN,		0, NULL },
1393 	/* 993 */ {	FE_UNKNOWN,		0, NULL },
1394 	/* 994 */ {	FE_UNKNOWN,		0, NULL },
1395 	/* 995 */ {	FE_UNKNOWN,		0, NULL },
1396 	/* 996 */ {	FE_UNKNOWN,		0, NULL },
1397 	/* 997 */ {	FE_UNKNOWN,		0, NULL },
1398 	/* 998 */ {	FE_UNKNOWN,		0, "The service believes you are using an outdated client; this is possibly an attempt to block third-party programs such as the one you are using. Check your software's web site for an updated version." },
1399 			/* TOC1 UNDOCUMENTED */
1400 			/* TOC2 AIM Express (TM) Update
1401 				Your browser is using a cached version of Quick Buddy that is now outdated. To remedy this, simply quit or close your browser and re-launch it. The next time you load Quick Buddy, it will be automatically updated.
1402 			*/
1403 	/* 999 */ {	FE_UNKNOWN,		0, NULL },
1404 };
1405 
decodeUTF16(const char * const end,char * s)1406 static char *decodeUTF16(const char *const end, char *s) {
1407 	int	i;
1408 
1409 	for (i = 0; (s[i] != -2) && (s+i < end-1); i += 2)
1410 		if (s[i] == 0)
1411 			s[i/2] = s[i+1];
1412 		else
1413 			s[i/2] = '.';
1414 	s[i/2] = 0;
1415 	return(s+i);
1416 }
1417 
toc_got_data(client_t c,unsigned char * buffer,unsigned short * bufferpos)1418 static fte_t toc_got_data(client_t c, unsigned char *buffer, unsigned short *bufferpos) {
1419 	char	*tempchr1, *arg0, **args,
1420 		data[TOC_SERVERSEND_MAXLEN - TOC_HEADER_LENGTH + 1];
1421 	fte_t	r;
1422 	unsigned short l;
1423 
1424   got_data_start:
1425 	r = toc_find_packet(c, buffer, bufferpos, data, SFLAP_FRAME_DATA, &l);
1426 	if (r == FE_NOTFOUND)
1427 		return(FE_SUCCESS);
1428 	else if (r != FE_SUCCESS)
1429 		return(r);
1430 
1431 	arg0 = toc_get_arg0(data);
1432 	if (arg0 == NULL)
1433 		return(FE_SUCCESS);
1434 	if (strcmp(arg0, "ERROR") == 0) {
1435 		/* ERROR:<Error Code>:Var args */
1436 		int	err;
1437 
1438 		args = toc_parse_args(data, 3, ':');
1439 		assert(strcmp(arg0, args[0]) == 0);
1440 
1441 		if (args[1] == NULL) {
1442 			toc_internal_disconnect(c, FE_INVALIDFORMAT);
1443 			return(FE_INVALIDFORMAT);
1444 		}
1445 		err = atoi(args[1]);
1446 		if ((err < 900) || (err > 999)) {
1447 			toc_internal_disconnect(c, FE_INVALIDFORMAT);
1448 			return(FE_INVALIDFORMAT);
1449 		}
1450 		if (err == 911) {
1451 			/* TOC1 Error validating input */
1452 			/* TOC2 UNDOCUMENTED */
1453 			if (c->passchange != 0) {
1454 				c->passchange--;
1455 				firetalk_callback_error(c, FE_NOCHANGEPASS, NULL, NULL);
1456 				goto got_data_start;
1457 			}
1458 		}
1459 		err -= 900;
1460 		if (toc2_errors[err].fte != FE_SUCCESS)
1461 			firetalk_callback_error(c, toc2_errors[err].fte,
1462 				toc2_errors[err].hastarget?args[2]:NULL,
1463 				toc2_errors[err].str?toc2_errors[err].str:(toc2_errors[err].fte == FE_UNKNOWN)?args[1]:NULL);
1464 	} else if (strcmp(arg0, "IM_IN_ENC2") == 0) {
1465 		/* IM_IN:<Source User>:<Auto Response T/F?>:<Message> */
1466 		/* 1 source
1467 		** 2 'T'=auto, 'F'=normal
1468 		** 3 UNKNOWN TOGGLE
1469 		** 4 UNKNORN TOGGLE
1470 		** 5 user class
1471 		** 6 UNKNOWN TOGGLE from toc2_send_im_enc arg 2
1472 		** 7 encoding [toc2_send_im_enc arg 3] "A"=ASCII "L"=Latin1 "U"=UTF8
1473 		** 8 language, for example "en"
1474 		** 9 message
1475 		**
1476 		** Cell phone	IM_IN_ENC2:+number:F:F:F: C,:F:L:en:message
1477 		** Spam bot	IM_IN_ENC2:buddysn:F:F:F: U,:F:A:en:message
1478 		** naim 0.11.6	IM_IN_ENC2:buddysn:F:F:F: O,:F:A:en:message
1479 		** naim 0.12.0	IM_IN_ENC2:buddysn:F:F:T: O,:F:A:en:message
1480 		** WinAIM	IM_IN_ENC2:buddysn:F:F:T: O,:F:A:en:<HTML><BODY BGCOLOR="#ffffff"><FONT LANG="0">message</FONT></BODY></HTML>
1481 		** ICQ user	IM_IN_ENC2:useruin:F:F:T: I,:F:A:en:message
1482 		**                                 | | |  |  | |  |
1483 		**                                 | | |  |  | |  language, limited to real languages (either two-letter code or "x-bad"), set by sender in toc2_send_im_enc
1484 		**                                 | | |  |  | character encoding, can be A L or U, set by sender in toc2_send_im_enc
1485 		**                                 | | |  |  unknown meaning, can be T or F, set by sender in toc2_send_im_enc
1486 		**                                 | | |  class, see UPDATE_BUDDY2 below
1487 		**                                 | | seems to be T for TOC2/Oscar and F for cell phones and TOC1
1488 		**                                 | unknown, always seems to be F
1489 		**                                 away status, T when away and F normally
1490 		*/
1491 		char	*name, *message;
1492 		int	isauto;
1493 
1494 		args = toc_parse_args(data, 10, ':');
1495 		assert(strcmp(arg0, args[0]) == 0);
1496 
1497 		if ((args[1] == NULL) || (args[2] == NULL) || (args[9] == NULL)) {
1498 			toc_internal_disconnect(c, FE_INVALIDFORMAT);
1499 			return(FE_INVALIDFORMAT);
1500 		}
1501 
1502 		name = args[1];
1503 		isauto = (args[2][0]=='T')?1:0;
1504 		message = args[9];
1505 
1506 		aim_handle_ect(c, name, message, isauto);
1507 		if (*message != 0) {
1508 			char	*mestart;
1509 
1510 			if (strncasecmp(message, "/me ",4) == 0)
1511 				firetalk_callback_im_getaction(c, name, isauto,
1512 					message+4);
1513 			else if ((mestart = strstr(message, ">/me ")) != NULL)
1514 				firetalk_callback_im_getaction(c, name, isauto,
1515 					mestart+5);
1516 			else {
1517 #if defined(DEBUG_ECHO) && defined(HAVE_ASPRINTF)
1518 				char	*A, *B, *C, *encoding, *newmessage = NULL;
1519 
1520 				if (args[5][0] == ' ')
1521 					A = "";
1522 				else if (args[5][0] == 'A')
1523 					A = "AOL ";
1524 				else
1525 					A = "unknown0 ";
1526 
1527 				if (args[5][1] == ' ')
1528 					B = "";
1529 				else if (args[5][1] == 'A')
1530 					B = "ADMINISTRATOR";
1531 				else if (args[5][1] == 'C')
1532 					B = "WIRELESS";
1533 				else if (args[5][1] == 'I')
1534 					B = "ICQ";
1535 				else if (args[5][1] == 'O')
1536 					B = "OSCAR_FREE";
1537 				else if (args[5][1] == 'U')
1538 					B = "DAMNED_TRANSIENT";
1539 				else
1540 					B = "unknown1";
1541 
1542 				if ((args[5][2] == ' ') || (args[5][2] == 0) || (args[5][2] == ','))
1543 					C = "";
1544 				else if (args[5][2] == 'U')
1545 					C = " UNAVAILABLE";
1546 				else
1547 					C = " unknown2";
1548 
1549 				if (args[7][0] == 'A')
1550 					encoding = "ASCII";
1551 				else if (args[7][0] == 'L')
1552 					encoding = "Latin1";
1553 				else if (args[7][0] == 'U')
1554 					encoding = "UTF8";
1555 				else
1556 					encoding = "unknown";
1557 
1558 				asprintf(&newmessage, "[%s%s%s, %s, %s %s %s %s] %s",
1559 					A, B, C,
1560 					encoding,
1561 					args[2], args[3], args[4], args[6],
1562 					message);
1563 				message = newmessage;
1564 #endif
1565 				if (isauto) /* interpolate only auto-messages */
1566 					firetalk_callback_im_getmessage(c,
1567 						name, 1, aim_interpolate_variables(message, c->nickname));
1568 				else
1569 					firetalk_callback_im_getmessage(c,
1570 						name, 0, message);
1571 #if defined(DEBUG_ECHO) && defined(HAVE_ASPRINTF)
1572 				free(message);
1573 #endif
1574 			}
1575 		}
1576 		firetalk_callback_im_buddyonline(c, name, 1);
1577 		firetalk_callback_typing(c, name, 0);
1578 	} else if (strcmp(arg0, "USER_INFO") == 0) {
1579 		char	*name, *info, *away, *third;
1580 		int	class = 0, warning, isaway;
1581 		long	online, idle;
1582 
1583 		args = toc_parse_args(data, 9, ':');
1584 		assert(strcmp(arg0, args[0]) == 0);
1585 
1586 		if (!args[1] || !args[2] || !args[3] || !args[4] || !args[5]
1587 			|| !args[6] || !args[7] || !args[8]) {
1588 			toc_internal_disconnect(c, FE_INVALIDFORMAT);
1589 			return(FE_INVALIDFORMAT);
1590 		}
1591 #ifdef DEBUG_ECHO
1592 		toc_echof(c, "got_data", "USER_INFO '%s' '%s' '%s' '%s' '%s' '%s' '%s'\n", args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
1593 		toc_echof(c, "got_data", "%s", args[8]);
1594 #endif
1595 		name = args[1];
1596 		if (args[2][0] == 'T') {
1597 			online = atol(args[4]);
1598 			if (online == 0)
1599 				online = 1;
1600 		} else
1601 			online = 0;
1602 		isaway = (args[6][2]=='U')?1:0;
1603 		warning = atol(args[3]);
1604 		idle = atol(args[5]);
1605 		info = args[8];
1606 
1607 		away = info;
1608 
1609 		if (away[0] == 0)
1610 			info = decodeUTF16(data+l, away);
1611 		else
1612 			info = strchr(info, -2);
1613 		assert(info != NULL);
1614 		assert(*info == -2);
1615 		*info++ = 0;
1616 		if (*away != 0)
1617 			away = strdup(aim_interpolate_variables(away, c->nickname));
1618 		else
1619 			away = NULL;
1620 #ifdef DEBUG_ECHO
1621 		toc_echof(c, "got_data", "%i %i %i %i", info[0], info[1], info[2], info[3]);
1622 #endif
1623 		assert((info[0] == -2) || (info[0] == '<') || ((info[0] == 0) && (info[1] == '<')));
1624 		if (info[0] == 0)
1625 			third = decodeUTF16(data+l, info);
1626 		else
1627 			third = strchr(info, -2);
1628 		assert(third != NULL);
1629 		assert(*third == -2);
1630 		*third++ = 0;
1631 		info = aim_handle_ect(c, name, info, 1);
1632 		info = aim_interpolate_variables(info, c->nickname);
1633 
1634 #ifdef DEBUG_ECHO
1635 		toc_echof(c, "got_data", "USER_INFO 1 %i %s\n", isaway, away);
1636 		toc_echof(c, "got_data", "USER_INFO 2 %s\n", info);
1637 		toc_echof(c, "got_data", "USER_INFO 3 %s\n", third);
1638 #endif
1639 
1640 		if (away != NULL) {
1641 			firetalk_callback_subcode_reply(c, name, "AWAY", away);
1642 			free(away);
1643 		}
1644 
1645 		firetalk_callback_gotinfo(c, name, info, warning, online, idle, class);
1646 	} else if (strcmp(arg0, "CLIENT_EVENT2") == 0) {
1647 		/* 1 source
1648 		** 2 status
1649 		*/
1650 		char	*name;
1651 		int	typinginfo;
1652 
1653 		args = toc_parse_args(data, 3, ':');
1654 		assert(strcmp(arg0, args[0]) == 0);
1655 
1656 		if (!args[1] || !args[2]) {
1657 			toc_internal_disconnect(c, FE_INVALIDFORMAT);
1658 			return(FE_INVALIDFORMAT);
1659 		}
1660 
1661 		name = args[1];
1662 		typinginfo = atol(args[2]);
1663 
1664 		firetalk_callback_typing(c, name, typinginfo);
1665 	} else if (strcmp(arg0, "UPDATE_BUDDY2") == 0) {
1666 		/* UPDATE_BUDDY:<Buddy User>:<Online? T/F>:<Evil Amount>:<Signon Time>:<IdleTime>:<UC>:<status code> */
1667 		/* 1 source
1668 		** 2 'T'=online, 'F'=offline
1669 		** 3 warning level out of 100
1670 		** 4 signon time in seconds since epoch
1671 		** 5 idle time in minutes
1672 		** 6 flags: ABC; A in 'A'=AOL user;
1673 		** 	B in 'A'=admin, 'C'=cell phone, 'I'=ICQ, 'O'=normal, 'U'=unconfirmed;
1674 		** 	C in 'U'=away
1675 		** 7 status code, used in ICQ
1676 		*/
1677 		char	*name;
1678 		int	isaway;
1679 		long	online, warn, idle;
1680 
1681 		args = toc_parse_args(data, 8, ':');
1682 		assert(strcmp(arg0, args[0]) == 0);
1683 
1684 		if (!args[1] || !args[2] || !args[3] || !args[4] || !args[5]
1685 			|| !args[6] || !args[7]) {
1686 			toc_internal_disconnect(c, FE_INVALIDFORMAT);
1687 			return(FE_INVALIDFORMAT);
1688 		}
1689 
1690 		name = args[1];
1691 		if (args[2][0] == 'T') {
1692 			online = atol(args[4]);
1693 			if (online == 0)
1694 				online = 1;
1695 		} else
1696 			online = 0;
1697 		isaway = (args[6][2]=='U')?1:0;
1698 		warn = atol(args[3]);
1699 		idle = atol(args[5]);
1700 
1701 		firetalk_callback_im_buddyonline(c, name, online);
1702 		if (online != 0) {
1703 			firetalk_callback_im_buddyaway(c, name, isaway);
1704 			firetalk_callback_idleinfo(c, name, idle);
1705 			firetalk_callback_warninfo(c, name, warn);
1706 		} else {
1707 			assert(isaway == 0);
1708 			assert(idle == 0);
1709 /*			assert(warn == 0);	Warning levels survive signing off*/
1710 		}
1711 	} else if (strcmp(arg0, "BUDDY_CAPS2") == 0) {
1712 		/* 1 source
1713 		** 2 capabilities separated by commas
1714 		*/
1715 		char	capstring[1024],
1716 			*name, **caps;
1717 		int	i, firstcap;
1718 
1719 		args = toc_parse_args(data, 3, ':');
1720 		assert(strcmp(arg0, args[0]) == 0);
1721 
1722 		if (!args[1] || !args[2]) {
1723 			toc_internal_disconnect(c, FE_INVALIDFORMAT);
1724 			return(FE_INVALIDFORMAT);
1725 		}
1726 
1727 		name = args[1];
1728 		caps = toc_parse_args(args[2], 255, ',');
1729 		firstcap = strtol(caps[0], NULL, 16);
1730 		for (i = 0; i < sizeof(toc_firstcaps)/sizeof(*toc_firstcaps); i++)
1731 			if (toc_firstcaps[i].val == firstcap) {
1732 				snprintf(capstring, sizeof(capstring), "%s", toc_firstcaps[i].name);
1733 				break;
1734 			}
1735 		if (i == sizeof(toc_firstcaps)/sizeof(*toc_firstcaps))
1736 			snprintf(capstring, sizeof(capstring), "UNKNOWN_TYPE_%X", firstcap);
1737 
1738 		for (i = 1; (caps[i] != NULL) && (*caps[i] != 0); i++) {
1739 			int	j, A1, A2, B, C, D, E1, E2, E3;
1740 
1741 			toc_uuid(caps[i], &A1, &A2, &B, &C, &D, &E1, &E2, &E3);
1742 			j = toc_cap(A1, A2, B, C, D, E1, E2, E3);
1743 			if (j != -1) {
1744 				if (strcmp(toc_uuids[j].name, "THIRD_PARTY_RANGE") != 0)
1745 					snprintf(capstring+strlen(capstring), sizeof(capstring)-strlen(capstring),
1746 						" %s", toc_uuids[j].name);
1747 				else {
1748 #define O(x)	(((x) == 0)?0:isspace(x)?'_':(x))
1749 					snprintf(capstring+strlen(capstring), sizeof(capstring)-strlen(capstring),
1750 						" [%c%c%c%c%c%c%c%c%c%c%c%c", O(B>>8), O(B&0xFF), O(C>>8), O(C&0xFF), O(D>>8), O(D&0xFF), O(E1>>8), O(E1&0xFF), O(E2>>8), O(E2&0xFF), O(E3>>8), O(E3&0xFF));
1751 #undef O
1752 					snprintf(capstring+strlen(capstring), sizeof(capstring)-strlen(capstring), "]");
1753 				}
1754 			} else {
1755 				if (strlen(caps[i]) > 4)
1756 					snprintf(capstring+strlen(capstring), sizeof(capstring)-strlen(capstring),
1757 						" [%04X%04X-%04X-%04X-%04X-%04X%04X%04X]", A1, A2, B, C, D, E1, E2, E3);
1758 				else
1759 					snprintf(capstring+strlen(capstring), sizeof(capstring)-strlen(capstring),
1760 						" UNKNOWN_CAP_%04X", A2);
1761 			}
1762 		}
1763 		firetalk_callback_capabilities(c, name, capstring);
1764 	} else if (strcmp(arg0, "BART2") == 0) {
1765 		/* 1 source
1766 		** 2 base64-encoded strings, unidentified
1767 		*/
1768 		char	**barts, *name;
1769 		int	i;
1770 
1771 		args = toc_parse_args(data, 3, ':');
1772 		assert(strcmp(arg0, args[0]) == 0);
1773 		name = strdup(args[1]);
1774 
1775 		barts = toc_parse_args(args[2], 255, ' ');
1776 		for (i = 0; (barts[i] != NULL) && (barts[i+1] != NULL) && (barts[i+2] != NULL); i += 3) {
1777 			int	j, flag = atoi(barts[i]), type = atoi(barts[i+1]);
1778 
1779 			for (j = 0; j < sizeof(toc_barts)/sizeof(*toc_barts); j++)
1780 				if (toc_barts[j].val == type) {
1781 #ifdef DEBUG_ECHO
1782 					toc_echof(c, "got_data", "BART %i %i: %s: %s [%s]\n", flag, type, toc_barts[j].name, barts[i+2], firetalk_debase64(args[i+2]));
1783 #endif
1784 					break;
1785 				}
1786 
1787 			if (type == 2) {
1788 				const char *s = firetalk_debase64(args[i+1]);
1789 				int	len;
1790 
1791 				assert(*s == 0);
1792 				len = s[1];
1793 				assert(s[len+2] == 0);
1794 				assert(s[len+3] == 0);
1795 #ifdef DEBUG_ECHO
1796 				toc_echof(c, "got_data", "BART STATUS_TEXT %s %s\n", name, s+2);
1797 #endif
1798 				firetalk_callback_statusinfo(c, name, s+2);
1799 			}
1800 		}
1801 
1802 		free(name);
1803 	} else if (strcmp(arg0, "NICK") == 0) {
1804 		/* NICK:<Nickname>
1805 		**    Tells you your correct nickname (ie how it should be capitalized and
1806 		**    spacing)
1807 		*/
1808 		args = toc_parse_args(data, 2, ':');
1809 		assert(strcmp(arg0, args[0]) == 0);
1810 
1811 		if (!args[1]) {
1812 			firetalk_callback_error(c, FE_INVALIDFORMAT, NULL, "NICK");
1813 			return(FE_SUCCESS);
1814 		}
1815 		firetalk_callback_user_nickchanged(c, c->nickname, args[1]);
1816 		free(c->nickname);
1817 		c->nickname = strdup(args[1]);
1818 		if (c->nickname == NULL)
1819 			abort();
1820 		firetalk_callback_newnick(c, args[1]);
1821 	} else if (strcmp(arg0, "EVILED") == 0) {
1822 		/* EVILED:<new evil>:<name of eviler, blank if anonymous>
1823 		**    The user was just eviled.
1824 		*/
1825 		args = toc_parse_args(data, 3, ':');
1826 		assert(strcmp(arg0, args[0]) == 0);
1827 
1828 		if (!args[1]) {
1829 			firetalk_callback_error(c, FE_INVALIDFORMAT, NULL, "EVILED");
1830 			return(FE_SUCCESS);
1831 		}
1832 
1833 		firetalk_callback_eviled(c, atoi(args[1]), args[2]);
1834 	} else if (strcmp(arg0, "CHAT_JOIN") == 0) {
1835 		/* CHAT_JOIN:<Chat Room Id>:<Chat Room Name>
1836 		**    We were able to join this chat room.  The Chat Room Id is
1837 		**    internal to TOC.
1838 		*/
1839 		long	id;
1840 		char	*name;
1841 		int	exchange, ret;
1842 
1843 		args = toc_parse_args(data, 3, ':');
1844 		assert(strcmp(arg0, args[0]) == 0);
1845 
1846 		if (!args[1] || !args[2]) {
1847 			firetalk_callback_error(c, FE_INVALIDFORMAT, NULL, "CHAT_JOIN");
1848 			return(FE_SUCCESS);
1849 		}
1850 		id = atol(args[1]);
1851 		name = args[2];
1852 		exchange = toc_internal_find_exchange(c, name);
1853 
1854 		assert(exchange != 0);
1855 		ret = toc_internal_set_id(c, name, exchange, id);
1856 		assert(ret == FE_SUCCESS);
1857 		ret = toc_internal_set_joined(c, id);
1858 		assert(ret == FE_SUCCESS);
1859 		firetalk_callback_chat_joined(c, toc_internal_find_room_name(c, id));
1860 	} else if (strcmp(arg0, "CHAT_IN_ENC") == 0) {
1861 		/* CHAT_IN:<Chat Room Id>:<Source User>:<Whisper? T/F>:<Message>
1862 		**    A chat message was sent in a chat room.
1863 		*/
1864 		/* 1 room ID
1865 		** 2 source
1866 		** 3 'T'=private, 'F'=public
1867 		** 4 unknown
1868 		** 5 language
1869 		** 6 message
1870 		*/
1871 		long	id;
1872 		char	*source, *message, *mestart;
1873 
1874 		args = toc_parse_args(data, 7, ':');
1875 		assert(strcmp(arg0, args[0]) == 0);
1876 
1877 		if (!args[1] || !args[2] || !args[3] || !args[4] || !args[5] || !args[6]) {
1878 			firetalk_callback_error(c, FE_INVALIDFORMAT, NULL, "CHAT_IN_ENC");
1879 			return(FE_SUCCESS);
1880 		}
1881 		id = atol(args[1]);
1882 		source = args[2];
1883 		message = args[6];
1884 
1885 		if (strncasecmp(message, "<HTML><PRE>", 11) == 0) {
1886 			message += 11;
1887 			if ((tempchr1 = strchr(message, '<')))
1888 				*tempchr1 = 0;
1889 		}
1890 		if (strncasecmp(message, "/me ", 4) == 0)
1891 			firetalk_callback_chat_getaction(c,
1892 				toc_internal_find_room_name(c, id),
1893 				source, 0, message+4);
1894 		else if ((mestart = strstr(message, ">/me ")) != NULL)
1895 			firetalk_callback_chat_getaction(c,
1896 				toc_internal_find_room_name(c, id),
1897 				source, 0, mestart+5);
1898 		else
1899 			firetalk_callback_chat_getmessage(c,
1900 				toc_internal_find_room_name(c, id),
1901 				source, 0, message);
1902 	} else if (strcmp(arg0, "CHAT_UPDATE_BUDDY") == 0) {
1903 		/* CHAT_UPDATE_BUDDY:<Chat Room Id>:<Inside? T/F>:<User 1>:<User 2>...
1904 		**    This one command handles arrival/departs from a chat room.  The
1905 		**    very first message of this type for each chat room contains the
1906 		**    users already in the room.
1907 		*/
1908 		int	joined;
1909 		long	id;
1910 		char	*recip,
1911 			*source,
1912 			*colon;
1913 
1914 		args = toc_parse_args(data, 4, ':');
1915 		assert(strcmp(arg0, args[0]) == 0);
1916 
1917 		if (!args[1] || !args[2] || !args[3]) {
1918 			firetalk_callback_error(c, FE_INVALIDFORMAT, NULL, "CHAT_UPDATE_BUDDY");
1919 			return(FE_SUCCESS);
1920 		}
1921 		joined = (args[2][0]=='T')?1:0;
1922 		id = atol(args[1]);
1923 		recip = toc_internal_find_room_name(c, id);
1924 		source = args[3];
1925 
1926 		while ((colon = strchr(source, ':'))) {
1927 			*colon = 0;
1928 			if (joined)
1929 				firetalk_callback_chat_user_joined(c, recip, source, NULL);
1930 			else
1931 				firetalk_callback_chat_user_left(c, recip, source, NULL);
1932 			source = colon+1;
1933 		}
1934 		if (joined) {
1935 			firetalk_callback_chat_user_joined(c, recip, source, NULL);
1936 			firetalk_callback_chat_user_joined(c, recip, NULL, NULL);
1937 		} else
1938 			firetalk_callback_chat_user_left(c, recip, source, NULL);
1939 	} else if (strcmp(arg0, "CHAT_INVITE") == 0) {
1940 		/* CHAT_INVITE:<Chat Room Name>:<Chat Room Id>:<Invite Sender>:<Message>
1941 		**    We are being invited to a chat room.
1942 		*/
1943 		args = toc_parse_args(data, 5, ':');
1944 		assert(strcmp(arg0, args[0]) == 0);
1945 
1946 		if (!args[1] || !args[2] || !args[3] || !args[4]) {
1947 			firetalk_callback_error(c, FE_INVALIDFORMAT, NULL, "CHAT_INVITE");
1948 			return(FE_SUCCESS);
1949 		}
1950 		if (toc_internal_add_room(c, args[1], 4) == FE_SUCCESS)
1951 			if (toc_internal_set_room_invited(c, args[1], 1) == FE_SUCCESS)
1952 				if (toc_internal_set_id(c, args[1], 4, atol(args[2])) == FE_SUCCESS)
1953 					firetalk_callback_chat_invited(c, args[1], args[3], args[4]);
1954 	} else if (strcmp(arg0, "CHAT_LEFT") == 0) {
1955 		/* CHAT_LEFT:<Chat Room Id>
1956 		**    Tells tic connection to chat room has been dropped
1957 		*/
1958 		args = toc_parse_args(data, 2, ':');
1959 		assert(strcmp(arg0, args[0]) == 0);
1960 
1961 		if (!args[1]) {
1962 			firetalk_callback_error(c, FE_INVALIDFORMAT, NULL, "CHAT_LEFT");
1963 			return(FE_SUCCESS);
1964 		}
1965 		firetalk_callback_chat_left(c, toc_internal_find_room_name(c, atol(args[1])));
1966 	} else if (strcmp(arg0, "NEW_BUDDY_REPLY2") == 0) {
1967 		/* NEW_BUDDY_REPLY2:19033926:added */
1968 	} else if (strcmp(arg0, "UPDATED2") == 0) {
1969 		/* UPDATED2:a:19033926:Buddy */
1970 		/* UPDATED2:b:nmlorg:groupname:Dan */
1971 		args = toc_parse_args(data, 255, ':');
1972 		assert(strcmp(arg0, args[0]) == 0);
1973 
1974 		if ((args[1] != NULL) && (args[2] != NULL) && (args[3] != NULL) && (strcmp(args[1], "b") == 0)) {
1975 			char	*name = args[2],
1976 				*group = args[3],
1977 				*friendly = args[4];
1978 
1979 			if ((friendly != NULL) && (*friendly == 0))
1980 				friendly = NULL;
1981 			firetalk_callback_buddyadded(c, name, group, friendly);
1982 		}
1983 	} else if (strcmp(arg0, "INSERTED2") == 0) {
1984 		/* INSERTED2:25:76: */
1985 		/* INSERTED2:b::yankeegurl680997:Recent Buddies */
1986 		args = toc_parse_args(data, 255, ':');
1987 		assert(strcmp(arg0, args[0]) == 0);
1988 
1989 		if ((args[1] != NULL) && (args[2] != NULL) && (args[3] != NULL) && (args[4] != NULL) && (strcmp(args[1], "b") == 0)) {
1990 			char	*name = args[3],
1991 				*group = args[4],
1992 				*friendly = args[5];
1993 
1994 			if ((friendly != NULL) && (*friendly == 0))
1995 				friendly = NULL;
1996 			firetalk_callback_buddyadded(c, name, group, friendly);
1997 		}
1998 	} else if (strcmp(arg0, "DELETED2") == 0) {
1999 		/* DELETED2:b:yankeegurl680997: */
2000 		/* DELETED2:b:yankeegurl680997:Recent Buddies */
2001 		/* DELETED2:M-^@T*80���M-^BGM-^Ayankeegurl680997: */
2002 		args = toc_parse_args(data, 255, ':');
2003 		assert(strcmp(arg0, args[0]) == 0);
2004 
2005 		if ((args[1] != NULL) && (args[2] != NULL) && (args[3] != NULL) && (strcmp(args[1], "b") == 0)) {
2006 			char	*name = args[2],
2007 				*group = args[4];
2008 
2009 			if ((group != NULL) && (*group == 0))
2010 				group = NULL;
2011 			firetalk_callback_buddyremoved(c, name, group);
2012 		}
2013 	} else if (strcmp(arg0, "DIR_STATUS") == 0) {
2014 		/* DIR_STATUS:<Return Code>:<Optional args>
2015 		**    <Return Code> is always 0 for success status.
2016 		*/
2017 		args = toc_parse_args(data, 2, ':');
2018 		assert(strcmp(arg0, args[0]) == 0);
2019 
2020 		if (args[1] == NULL) {
2021 			firetalk_callback_error(c, FE_INVALIDFORMAT, NULL, "DIR_STATUS with no status code");
2022 			return(FE_SUCCESS);
2023 		}
2024 		switch (atoi(args[1])) {
2025 		  case 0:
2026 		  case 1:
2027 			break;
2028 		  case 2:
2029 			firetalk_callback_error(c, FE_INVALIDFORMAT, NULL, "DIR_STATUS failed, invalid format");
2030 			break;
2031 		  default:
2032 			firetalk_callback_error(c, FE_INVALIDFORMAT, NULL, "DIR_STATUS with unknown code");
2033 			break;
2034 		}
2035 	} else if (strcmp(arg0, "ADMIN_NICK_STATUS") == 0) {
2036 		/* ADMIN_NICK_STATUS:<Return Code>:<Optional args>
2037 		**    <Return Code> is always 0 for success status.
2038 		*/
2039 	} else if (strcmp(arg0, "ADMIN_PASSWD_STATUS") == 0) {
2040 		/* ADMIN_PASSWD_STATUS:<Return Code>:<Optional args>
2041 		**    <Return Code> is always 0 for success status.
2042 		*/
2043 		c->passchange--;
2044 		args = toc_parse_args(data, 3, ':');
2045 		assert(strcmp(arg0, args[0]) == 0);
2046 
2047 		if (!args[1]) {
2048 			firetalk_callback_error(c, FE_INVALIDFORMAT, NULL, "ADMIN_PASSWD_STATUS");
2049 			return(FE_SUCCESS);
2050 		}
2051 		if (atoi(args[1]) != 0)
2052 			firetalk_callback_error(c, FE_NOCHANGEPASS, NULL, NULL);
2053 		else
2054 			firetalk_callback_passchanged(c);
2055 	} else if (strcmp(arg0, "PAUSE") == 0) {
2056 		/* PAUSE
2057 		**    Tells TIC to pause so we can do migration
2058 		*/
2059 		c->connectstate = 1;
2060 		firetalk_internal_set_connectstate(c, FCS_WAITING_SIGNON);
2061 	} else if (strcmp(arg0, "RVOUS_PROPOSE") == 0) {
2062 		/* RVOUS_PROPOSE:<user>:<uuid>:<cookie>:<seq>:<rip>:<pip>:<vip>:<port>
2063 		**               [:tlv tag1:tlv value1[:tlv tag2:tlv value2[:...]]]
2064 		**    Another user has proposed that we rendezvous with them to
2065 		**    perform the service specified by <uuid>.  They want us
2066 		**    to connect to them, we have their rendezvous ip, their
2067 		**    proposer_ip, and their verified_ip. The tlv values are
2068 		**    base64 encoded.
2069 		*/
2070 		int	j, A1, A2, B, C, D, E1, E2, E3;
2071 
2072 		args = toc_parse_args(data, 255, ':');
2073 		assert(strcmp(arg0, args[0]) == 0);
2074 
2075 		if (!args[1] || !args[2] || !args[3] || !args[4] || !args[5] || !args[6] || !args[7] || !args[8]) {
2076 			firetalk_callback_error(c, FE_INVALIDFORMAT, NULL, "RVOUS_PROPOSE");
2077 			return(FE_SUCCESS);
2078 		}
2079 #ifdef DEBUG_ECHO
2080 		{
2081 			int	i;
2082 
2083 			toc_echof(c, "got_data", "RVOUS_PROPOSE\n1user=[%s]\n2uuid=%s\n3cookie=[%s]\n4seq=%s\n5rendezvous_ip=%s\n6proposer_ip=%s\n7verified_ip=%s\n8port=%s\n",
2084 				args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]);
2085 			for (i = 9; args[i] && args[i+1]; i += 2)
2086 				toc_echof(c, "got_data", "RVOUS_PROPOSE\n%itype=%s\n%ivalue=%s [%s]\n", i, args[i], i+1, args[i+1], firetalk_debase64(args[i+1]));
2087 		}
2088 #endif
2089 		toc_uuid(args[2], &A1, &A2, &B, &C, &D, &E1, &E2, &E3);
2090 		j = toc_cap(A1, A2, B, C, D, E1, E2, E3);
2091 		if (j == -1)
2092 			firetalk_callback_error(c, FE_WEIRDPACKET, NULL, "Unknown rendezvous UUID");
2093 		else {
2094 			if (strcmp(toc_uuids[j].name, "FILE_TRANSFER") == 0) {
2095 				const char *message, *file;
2096 
2097 				if ((message = toc_get_tlv_value(args, 9, 12)) != NULL)
2098 					firetalk_callback_im_getmessage(c, args[1], 0, message);
2099 
2100 				if ((file = toc_get_tlv_value(args, 9, 10001)) != NULL) {
2101 					unsigned long	size = ntohl(*((uint32_t *)(file+4)));
2102 
2103 					firetalk_callback_file_offer(c,
2104 						/* from */	args[1],		/* user */
2105 						/* filename */	file+8,			/* filename */
2106 						/* size */	size,
2107 						/* ipstring */	args[7],		/* verified_ip */
2108 						/* ip6string */	NULL,
2109 						/* port */	(uint16_t)atoi(args[8]),/* port */
2110 						/* type */	FF_TYPE_RAW);
2111 				}
2112 			} else
2113 				firetalk_callback_error(c, FE_WEIRDPACKET, NULL, "Unhandled rendezvous UUID, do we have a capability set we do not actually support?");
2114 		}
2115 	} else
2116 		firetalk_callback_error(c, FE_WEIRDPACKET, NULL, data);
2117 
2118 	goto got_data_start;
2119 }
2120 
toc_make_fake_cap(const unsigned char * const str,const int len)2121 static const char *toc_make_fake_cap(const unsigned char *const str, const int len) {
2122 	static char buf[sizeof("FFFFFFFF-cccc-dddd-eeee-ffffgggghhhh")];
2123 	const int ar[] = { 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34 };
2124 	const int maxlen = 12;
2125 	int	i;
2126 
2127 	strcpy(buf, "FFFFFFFF-0000-0000-0000-000000000000");
2128 	for (i = 0; (i < len) && (i < maxlen); i++) {
2129 		char	b[3];
2130 
2131 		sprintf(b, "%02X", str[i]);
2132 		memcpy(buf+ar[i], b, 2);
2133 	}
2134 	return(buf);
2135 }
2136 
toc_got_data_connecting(client_t c,unsigned char * buffer,unsigned short * bufferpos)2137 static fte_t toc_got_data_connecting(client_t c, unsigned char *buffer, unsigned short *bufferpos) {
2138 	char data[TOC_SERVERSEND_MAXLEN - TOC_HEADER_LENGTH + 1];
2139 	char password[128];
2140 	fte_t	r;
2141 	unsigned short length;
2142 	char *arg0;
2143 	char **args;
2144 	char *tempchr1;
2145 	firetalk_t fchandle;
2146 
2147 got_data_connecting_start:
2148 
2149 	r = toc_find_packet(c, buffer, bufferpos, data, (c->connectstate==0)?SFLAP_FRAME_SIGNON:SFLAP_FRAME_DATA, &length);
2150 	if (r == FE_NOTFOUND)
2151 		return(FE_SUCCESS);
2152 	else if (r != FE_SUCCESS)
2153 		return(r);
2154 
2155 	switch (c->connectstate) {
2156 	  case 0: /* we're waiting for the flap version number */
2157 		if (length != TOC_HOST_SIGNON_LENGTH) {
2158 			firetalk_callback_connectfailed(c, FE_PACKETSIZE, "Host signon length incorrect");
2159 			return(FE_PACKETSIZE);
2160 		}
2161 		if ((data[0] != 0) || (data[1] != 0) || (data[2] != 0) || (data[3] != 1)) {
2162 			firetalk_callback_connectfailed(c, FE_VERSION, NULL);
2163 			return(FE_VERSION);
2164 		}
2165 #if 0
2166 		srand((unsigned int) time(NULL));
2167 		c->local_sequence = (unsigned short)1+(unsigned short)(65536.0*rand()/(RAND_MAX+1.0));
2168 #else
2169 		c->local_sequence = 31337;
2170 #endif
2171 
2172 		length = toc_fill_header((unsigned char *)data, SFLAP_FRAME_SIGNON, ++c->local_sequence, toc_fill_signon((unsigned char *)&data[TOC_HEADER_LENGTH], c->nickname));
2173 
2174 		fchandle = firetalk_find_handle(c);
2175 #ifdef DEBUG_ECHO
2176 		toc_echo_send(c, "got_data_connecting", data, length);
2177 #endif
2178 		firetalk_internal_send_data(fchandle, data, length);
2179 
2180 		firetalk_callback_needpass(c, password, sizeof(password));
2181 
2182 		c->connectstate = 1;
2183 		{
2184 			int	sn = tolower(c->nickname[0]) - 'a' + 1,
2185 				pw = tolower(password[0]) - 'a' + 1,
2186 				A = sn*7696 + 738816,
2187 				B = sn*746512,
2188 				C = pw*A,
2189 				magic = C - A + B + 71665152;
2190 
2191 			if (!isdigit(c->nickname[0]))			/* AIM Express */
2192 			  r = toc_send_printf(c, "toc2_login "
2193 				 "login.oscar.aol.com"	// authorizer host	[login.oscar.aol.com]
2194 				" 29999"		// authorizer port	[29999]
2195 				" %s"			// username
2196 				" %S"			// roasted, armored password
2197 				" English"		// language		[English]
2198 				" %s"			// client version
2199 				" 160"			// unknown number	[160]
2200 				" %S"			// country code		[US]
2201 				" %s"			// unknown string	[""]
2202 				" %s"			// unknown string	[""]
2203 				" 3"			// unknown number	[3]
2204 				" 0"			// unknown number	[0]
2205 				" 30303"		// unknown number	[30303]
2206 				" -kentucky"		// unknown flag		[-kentucky]
2207 				" -utf8"		// unknown flag		[-utf8]
2208 				" -preakness"		// this enables us to talk to ICQ users
2209 				" %i",			// magic number based on username and password
2210 					c->nickname,
2211 					toc_hash_password(password),
2212 					/*PACKAGE_NAME*/ ":" /*PACKAGE_VERSION*/ ":contact " /*PACKAGE_BUGREPORT*/,
2213 					"US",
2214 					"",
2215 					"",
2216 					magic);
2217 			else						/* ICQ2Go */
2218 			  r = toc_send_printf(c, "toc2_login "
2219 				 "login.icq.com"	// authorizer host	[login.icq.com]
2220 				" 5190"			// authorizer port	[5190]
2221 				" %S"			// username
2222 				" %S"			// roasted, armored password
2223 				" en"			// language		[en]
2224 				" %s"			// client version
2225 				" 135"			// unknown number	[135]
2226 				" %s"			// country code		["US"]
2227 				" %s"			// unknown string	[""]
2228 				" %s"			// unknown string	[""]
2229 				" 30"			// unknown number	[30]
2230 				" 2"			// unknown number	[2]
2231 				" 321"			// unknown number	[321]
2232 				" -utf8"		// unknown flag		[-utf8]
2233 				" -preakness"		// this enables us to talk to ICQ users
2234 				" %i",			// magic number based on username and password
2235 					c->nickname,
2236 					toc_hash_password(password),
2237 					PACKAGE_NAME ":" PACKAGE_VERSION ":contact " PACKAGE_BUGREPORT,
2238 					"US",
2239 					"",
2240 					"",
2241 					magic);
2242 		}
2243 		if (r != FE_SUCCESS) {
2244 			firetalk_callback_connectfailed(c,r,NULL);
2245 			return(r);
2246 		}
2247 		break;
2248 	  case 1:
2249 		arg0 = toc_get_arg0(data);
2250 		if (strcmp(arg0, "SIGN_ON") != 0) {
2251 			if (strcmp(arg0, "ERROR") == 0) {
2252 				args = toc_parse_args(data, 3, ':');
2253 				if (args[1] != NULL) {
2254 					int	err = atoi(args[1]);
2255 
2256 					if ((err >= 900) && (err <= 999)) {
2257 						err -= 900;
2258 						firetalk_callback_connectfailed(c, toc2_errors[err].fte, toc2_errors[err].str);
2259 						return(toc2_errors[err].fte);
2260 					}
2261 				}
2262 			}
2263 			firetalk_callback_connectfailed(c, FE_UNKNOWN, NULL);
2264 			return(FE_UNKNOWN);
2265 		}
2266 		c->connectstate = 2;
2267 		break;
2268 	  case 2:
2269 	  case 3:
2270 		arg0 = toc_get_arg0(data);
2271 		if (arg0 == NULL)
2272 			return(FE_SUCCESS);
2273 		if (strcmp(arg0, "NICK") == 0) {
2274 			/* NICK:<Nickname> */
2275 
2276 			args = toc_parse_args(data, 2, ':');
2277 			if (args[1]) {
2278 				free(c->nickname);
2279 				c->nickname = strdup(args[1]);
2280 				if (c->nickname == NULL)
2281 					abort();
2282 			}
2283 			c->connectstate = 3;
2284 		} else if (strcmp(arg0, "CONFIG2") == 0) {
2285 			/* CONFIG2:<config> */
2286 			char	*nl, *curgroup = strdup("Saved buddy");
2287 
2288 			fchandle = firetalk_find_handle(c);
2289 			args = toc_parse_args(data, 2, ':');
2290 			if (!args[1]) {
2291 				firetalk_callback_connectfailed(c, FE_INVALIDFORMAT, "CONFIG2");
2292 				return(FE_INVALIDFORMAT);
2293 			}
2294 			tempchr1 = args[1];
2295 			c->permit_mode = 0;
2296 			while ((nl = strchr(tempchr1, '\n'))) {
2297 				*nl = 0;
2298 
2299 				if (tempchr1[1] == ':') {
2300 					/* b:ACCOUNT:REAL NAME:?:CELL PHONE SLOT NUMBER:w:NOTES */
2301 
2302 					args = toc_parse_args(tempchr1, 4, ':');
2303 
2304 					switch (args[0][0]) {
2305 					  case 'g':	/* Buddy Group (All Buddies until the next g or the end of config are in this group.) */
2306 						free(curgroup);
2307 						curgroup = strdup(args[1]);
2308 						break;
2309 					  case 'b':	/* A Buddy */
2310 					  case 'a': {	/* another kind of buddy */
2311 							char	*friendly = NULL;
2312 
2313 							if ((args[2] != NULL) && (args[2][0] != 0))
2314 								friendly = args[2];
2315 							else
2316 								friendly = NULL;
2317 							if (strcmp(curgroup, "Mobile Device") != 0)
2318 								firetalk_im_add_buddy(fchandle, args[1], curgroup, friendly);
2319 						}
2320 						break;
2321 					  case 'p':	/* Person on permit list */
2322 						toc_send_printf(c, "toc_add_permit %s", args[1]);
2323 						break;
2324 					  case 'd':	/* Person on deny list */
2325 						firetalk_im_internal_add_deny(fchandle, args[1]);
2326 						break;
2327 					  case 'm':	/* Permit/Deny Mode.  Possible values are 1 - Permit All 2 - Deny All 3 - Permit Some 4 - Deny Some */
2328 						c->permit_mode = atoi(args[1]);
2329 						break;
2330 					}
2331 				}
2332 				tempchr1 = nl+1;
2333 			}
2334 			free(curgroup);
2335 
2336 			if ((c->permit_mode < 1) || (c->permit_mode > 5))
2337 				c->permit_mode = 4;
2338 			toc_send_printf(c, "toc2_set_pdmode %i", c->permit_mode);
2339 			c->gotconfig = 1;
2340 		} else {
2341 			firetalk_callback_connectfailed(c, FE_WEIRDPACKET, data);
2342 			return(FE_WEIRDPACKET);
2343 		}
2344 
2345 		if ((c->gotconfig == 1) && (c->connectstate == 3)) {
2346 #ifdef ENABLE_GETREALNAME
2347 			char	realname[128];
2348 #endif
2349 
2350 			/* ask the client to handle its init */
2351 			firetalk_callback_doinit(c, c->nickname);
2352 			if (toc_im_upload_buddies(c) != FE_SUCCESS) {
2353 				firetalk_callback_connectfailed(c, FE_PACKET, "Error uploading buddies");
2354 				return(FE_PACKET);
2355 			}
2356 			if (toc_im_upload_denies(c) != FE_SUCCESS) {
2357 				firetalk_callback_connectfailed(c, FE_PACKET, "Error uploading denies");
2358 				return(FE_PACKET);
2359 			}
2360 			r = toc_send_printf(c, "toc_init_done");
2361 			if (r != FE_SUCCESS) {
2362 				firetalk_callback_connectfailed(c, r, "Finalizing initialization");
2363 				return(r);
2364 			}
2365 
2366 			{
2367 				char	*name, *version;
2368 
2369 				name = strdup(toc_make_fake_cap(PACKAGE_NAME, strlen(PACKAGE_NAME)));
2370 				version = strdup(toc_make_fake_cap(PACKAGE_VERSION, strlen(PACKAGE_VERSION)));
2371 				r = toc_send_printf(c, "toc_set_caps %S %S %S",
2372 					"094613494C7F11D18222444553540000", name, version);
2373 				free(name);
2374 				free(version);
2375 				if (r != FE_SUCCESS) {
2376 					firetalk_callback_connectfailed(c, r, "Setting capabilities");
2377 					return(r);
2378 				}
2379 			}
2380 
2381 #ifdef ENABLE_GETREALNAME
2382 			firetalk_getrealname(c, realname, sizeof(realname));
2383 			if (*realname != 0) {
2384 				char	*first, *mid, *last;
2385 
2386 				first = strtok(realname, " ");
2387 				mid = strtok(NULL, " ");
2388 				last = strtok(NULL, " ");
2389 
2390 				if (mid == NULL)
2391 					mid = "";
2392 				if (last == NULL) {
2393 					last = mid;
2394 					mid = "";
2395 				}
2396 
2397 				/* first name:middle name:last name:maiden name:city:state:country:email:allow web searches */
2398 				r = toc_send_printf(c, "toc_set_dir \"%S:%S:%S\"", first, mid, last);
2399 				if (r != FE_SUCCESS) {
2400 					firetalk_callback_connectfailed(c, r, "Setting directory information");
2401 					return(r);
2402 				}
2403 			}
2404 #endif
2405 
2406 			firetalk_callback_connected(c);
2407 			return(FE_SUCCESS);
2408 		}
2409 		break;
2410 	}
2411 	goto got_data_connecting_start;
2412 }
2413 
toc_periodic(struct s_firetalk_handle * const conn)2414 static fte_t toc_periodic(struct s_firetalk_handle *const conn) {
2415 	struct s_toc_connection *c;
2416 	time_t	now;
2417 	long	idle;
2418 	char	data[32];
2419 	fte_t	r;
2420 
2421 	c = conn->handle;
2422 
2423 	if (firetalk_internal_get_connectstate(c) != FCS_ACTIVE)
2424 		return(FE_NOTCONNECTED);
2425 
2426 	now = time(NULL);
2427 
2428 	if ((c->lastself+60) <= now) {
2429 		c->lastself = now;
2430 		r = toc_send_printf(c, "toc_get_status %s", c->nickname);
2431 		if (r != FE_SUCCESS)
2432 			return(r);
2433 	}
2434 
2435 	idle = (long)(now - c->lasttalk);
2436 	firetalk_callback_setidle(c, &idle);
2437 
2438 	if (idle < 600)
2439 		idle = 0;
2440 
2441 	if (idle/60 == c->lastidle/60)
2442 		return(FE_IDLEFAST);
2443 
2444 	if ((c->lastidle/60 == 0) && (idle/60 == 0))
2445 		return(FE_IDLEFAST);
2446 	if ((c->lastidle/60 != 0) && (idle/60 != 0))
2447 		return(FE_IDLEFAST);
2448 
2449 	c->lastidle = idle;
2450 	sprintf(data, "%ld", idle);
2451 	return(toc_send_printf(c, "toc_set_idle %s", data));
2452 }
2453 
toc_chat_join(client_t c,const char * const room)2454 static fte_t toc_chat_join(client_t c, const char *const room) {
2455 	int	i;
2456 
2457 	i = toc_internal_get_room_invited(c,room);
2458 	if (i == 1) {
2459 		toc_internal_set_room_invited(c, room, 0);
2460 		return(toc_send_printf(c, "toc_chat_accept %i", toc_internal_find_room_id(c, room)));
2461 	} else {
2462 		int	m;
2463 		char	*s;
2464 
2465 		s = toc_internal_split_name(room);
2466 		m = toc_internal_split_exchange(room);
2467 		toc_internal_add_room(c, s, m);
2468 		return(toc_send_printf(c, "toc_chat_join %d %s", m, s));
2469 	}
2470 }
2471 
toc_chat_part(client_t c,const char * const room)2472 static fte_t toc_chat_part(client_t c, const char *const room) {
2473 	int	id = toc_internal_find_room_id(c, room);
2474 
2475 	if (id == 0)
2476 		return(FE_ROOMUNAVAILABLE);
2477 	return(toc_send_printf(c, "toc_chat_leave %i", id));
2478 }
2479 
toc_chat_set_topic(client_t c,const char * const room,const char * const topic)2480 static fte_t toc_chat_set_topic(client_t c, const char *const room, const char *const topic) {
2481 	return(FE_SUCCESS);
2482 }
2483 
toc_chat_op(client_t c,const char * const room,const char * const who)2484 static fte_t toc_chat_op(client_t c, const char *const room, const char *const who) {
2485 	return(FE_SUCCESS);
2486 }
2487 
toc_chat_deop(client_t c,const char * const room,const char * const who)2488 static fte_t toc_chat_deop(client_t c, const char *const room, const char *const who) {
2489 	return(FE_SUCCESS);
2490 }
2491 
toc_chat_kick(client_t c,const char * const room,const char * const who,const char * const reason)2492 static fte_t toc_chat_kick(client_t c, const char *const room, const char *const who, const char *const reason) {
2493 	return(FE_SUCCESS);
2494 }
2495 
toc_chat_send_message(client_t c,const char * const room,const char * const message,const int auto_flag)2496 static fte_t toc_chat_send_message(client_t c, const char *const room, const char *const message, const int auto_flag) {
2497 	if (strlen(message) > 232)
2498 		return(FE_PACKETSIZE);
2499 
2500 	if (strcasecmp(room, ":RAW") == 0)
2501 		return(toc_send_printf(c, "%S", message));
2502 	else {
2503 		int	id = toc_internal_find_room_id(c,room);
2504 
2505 		if (id == 0)
2506 			return(FE_ROOMUNAVAILABLE);
2507 		return(toc_send_printf(c, "toc_chat_send %i %s", id, message));
2508 	}
2509 }
2510 
toc_chat_send_action(client_t c,const char * const room,const char * const message,const int auto_flag)2511 static fte_t toc_chat_send_action(client_t c, const char *const room, const char *const message, const int auto_flag) {
2512 	char	tempbuf[TOC_CLIENTSEND_MAXLEN];
2513 
2514 	if (strlen(message) > 232-4)
2515 		return(FE_PACKETSIZE);
2516 
2517 	snprintf(tempbuf, sizeof(tempbuf), "/me %s", message);
2518 	return(toc_send_printf(c, "toc_chat_send %i %s",
2519 		toc_internal_find_room_id(c, room), tempbuf));
2520 }
2521 
toc_chat_invite(client_t c,const char * const room,const char * const who,const char * const message)2522 static fte_t toc_chat_invite(client_t c, const char *const room, const char *const who, const char *const message) {
2523 	int	id = toc_internal_find_room_id(c,room);
2524 
2525 	if (id != 0)
2526 		return(toc_send_printf(c, "toc_chat_invite %i %s %s", id, message, who));
2527 	return(FE_NOTFOUND);
2528 }
2529 
2530 #ifdef ENABLE_FILE_OFFER
toc_file_offer(client_t c,const char * const nickname,const char * const filename,const unsigned long localip,const uint16_t port,const long size)2531 static fte_t toc_file_offer(client_t c, const char *const nickname,
2532 		const char *const filename, const unsigned long localip,
2533 		const uint16_t port, const long size) {
2534 	struct s_firetalk_handle
2535 		*fchandle = firetalk_find_handle(c);
2536 	char	args[256];
2537 
2538 	snprintf(args, sizeof(args), "SEND %s %lu %u %ld", filename, localip, port, size);
2539 	return(firetalk_subcode_send_request(fchandle, nickname, "DCC", args));
2540 }
2541 #endif
2542 
2543 /*
2544 static fte_t toc_subcode_send_request(client_t c, const char *const to, const char *const command, const char *const args) {
2545 	char	*ect;
2546 
2547 	if (isdigit(c->nickname[0]))
2548 		return(FE_SUCCESS);
2549 	if ((ect = toc_ctcp_encode(c, command, args)) == NULL)
2550 		return(FE_SUCCESS);
2551 
2552 	toc_im_send_message(c, to, ect, 0);
2553 	free(ect);
2554 	return(FE_SUCCESS);
2555 }
2556 
2557 static fte_t toc_subcode_send_reply(client_t c, const char *const to, const char *const command, const char *const args) {
2558 	char	*ect;
2559 
2560 	if ((ect = toc_ctcp_encode(c, command, args)) == NULL)
2561 		return(FE_SUCCESS);
2562 
2563 	if (to != NULL) {
2564 		fte_t	ret;
2565 
2566 		ret = toc_im_send_message(c, to, ect, 1);
2567 		free(ect);
2568 		return(ret);
2569 	} else {
2570 #if 0
2571 		if (args != NULL)
2572 			ect_prof(c, command, ect);
2573 		else
2574 			ect_prof(c, command, "");
2575 #endif
2576 		free(ect);
2577 		return(FE_SUCCESS);
2578 	}
2579 }
2580 */
2581 
2582 const firetalk_protocol_t firetalk_protocol_toc2 = {
2583 	strprotocol:		"TOC2",
2584 	default_server:		"toc.n.ml.org",
2585 	default_port:		9898,
2586 	default_buffersize:	1024*8,
2587 	periodic:		toc_periodic,
2588 	preselect:		toc_preselect,
2589 	postselect:		toc_postselect,
2590 	got_data:		toc_got_data,
2591 	got_data_connecting:	toc_got_data_connecting,
2592 	comparenicks:		toc_compare_nicks,
2593 	isprintable:		toc_isprint,
2594 	disconnect:		toc_disconnect,
2595 	signon:			toc_signon,
2596 	get_info:		toc_get_info,
2597 	set_info:		toc_set_info,
2598 	set_away:		toc_set_away,
2599 	set_nickname:		toc_set_nickname,
2600 	set_password:		toc_set_password,
2601 	set_privacy:		toc_set_privacy,
2602 	im_add_buddy:		toc_im_add_buddy,
2603 	im_remove_buddy:	toc_im_remove_buddy,
2604 	im_add_deny:		toc_im_add_deny,
2605 	im_remove_deny:		toc_im_remove_deny,
2606 	im_upload_buddies:	toc_im_upload_buddies,
2607 	im_upload_denies:	toc_im_upload_denies,
2608 	im_send_message:	toc_im_send_message,
2609 	im_send_action:		toc_im_send_action,
2610 	im_evil:		toc_im_evil,
2611 	chat_join:		toc_chat_join,
2612 	chat_part:		toc_chat_part,
2613 	chat_invite:		toc_chat_invite,
2614 	chat_set_topic:		toc_chat_set_topic,
2615 	chat_op:		toc_chat_op,
2616 	chat_deop:		toc_chat_deop,
2617 	chat_kick:		toc_chat_kick,
2618 	chat_send_message:	toc_chat_send_message,
2619 	chat_send_action:	toc_chat_send_action,
2620 //	subcode_send_request:	toc_subcode_send_request,
2621 //	subcode_send_reply:	toc_subcode_send_reply,
2622 	subcode_encode:		toc_ctcp_encode,
2623 	room_normalize:		aim_normalize_room_name,
2624 	create_handle:		toc_create_handle,
2625 	destroy_handle:		toc_destroy_handle,
2626 #ifdef ENABLE_NEWGROUPS
2627 	im_remove_group:	toc_im_remove_group,
2628 #endif
2629 #ifdef ENABLE_FILE_OFFER
2630 	file_offer:		toc_file_offer,
2631 #endif
2632 };
2633