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, ">") == 0) {
309 buf[b++] = '>';
310 i += sizeof(">")-2;
311 } else if (STRNCMP(str+i, "<") == 0) {
312 buf[b++] = '<';
313 i += sizeof("<")-2;
314 } else if (STRNCMP(str+i, """) == 0) {
315 buf[b++] = '"';
316 i += sizeof(""")-2;
317 } else if (STRNCMP(str+i, " ") == 0) {
318 buf[b++] = ' ';
319 i += sizeof(" ")-2;
320 } else if (STRNCMP(str+i, "&") == 0) {
321 buf[b++] = '&';
322 i += sizeof("&")-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