connwrap - initialize gnutls session in cw_connect
[centerim.git] / firetalk / toc.c
blob42126b14a20b24a7fe4f8463f8c31ed4a234180b
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>
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>"
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;
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;
45 typedef struct s_toc_connection *client_t;
46 #define _HAVE_CLIENT_T
48 #include "firetalk-int.h"
49 #include "firetalk.h"
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)
57 #define SIGNON_STRING "FLAPON\r\n\r\n"
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
64 #include "toc2_uuids.h"
66 static char lastinfo[TOC_USERNAME_MAXLEN+1] = "";
68 /* Internal Function Declarations */
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, ...);
81 #include <assert.h>
82 #ifdef DEBUG_ECHO
83 extern void *curconn;
84 extern void status_echof(void *conn, const unsigned char *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);
92 va_start(ap, format);
93 vsnprintf(buf, sizeof(buf), format, ap);
94 va_end(ap);
96 len = strlen(buf);
97 while ((len > 0) && (buf[len-1] == '\n'))
98 buf[--len] = 0;
100 if (*buf != 0)
101 status_echof(curconn, firetalk_htmlentities(buf));
103 statrefresh();
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;
111 assert(_length > 4);
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);
117 assert(length == (_length-6));
119 toc_echof(c, where, "frame=%X, sequence=out:%i, length=%i, value=[%.*s]\n",
120 ft, sequence, length, length, data+TOC_HEADER_LENGTH);
122 #endif
124 /* Internal Function Definitions */
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;
131 if (*bufferpos < TOC_HEADER_LENGTH) /* don't have the whole header yet */
132 return(FE_NOTFOUND);
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);
140 if (*bufferpos < length + TOC_HEADER_LENGTH) /* don't have the whole packet yet */
141 return(FE_NOTFOUND);
143 ft = toc_get_frame_type_from_header(buffer);
144 sequence = toc_get_sequence_from_header(buffer);
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;
152 #ifdef DEBUG_ECHO
153 if (ft != 5) {
154 char buf[1024*8];
155 int j;
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';
163 toc_echof(c, "find_packet", "frame=%X, sequence=in:%i, length=%i, value=[%s]\n",
164 ft, sequence, length, buf);
166 #endif
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);
177 if (ft == frametype)
178 return(FE_SUCCESS);
179 else
180 return(FE_WEIRDPACKET);
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);
195 static unsigned short toc_fill_signon(unsigned char *const signon, const char *const username) {
196 size_t length = strlen(username);
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);
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);
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++] = '%';
277 output[o++] = input[i];
281 output[o] = 0;
282 return(output);
285 static const char *aim_normalize_room_name(const char *const name) {
286 static char newname[2048];
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);
295 strcpy(newname, "4:");
296 strcpy(newname+2, name);
298 return(newname);
301 #define STRNCMP(x,y) (strncmp((x), (y), sizeof(y)-1))
302 static char *htmlclean(const char *str) {
303 static char
304 buf[2048];
305 int i, b = 0;
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;
327 return(buf);
329 #undef STRNCMP
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;
335 while ((ectbegin = strstr(message, ECT_TOKEN)) != NULL) {
336 textbegin = ectbegin+sizeof(ECT_TOKEN)-1;
338 if ((textend = strstr(textbegin, ECT_ENDING)) != NULL) {
339 char *arg;
341 *textend = 0;
342 ectend = textend+sizeof(ECT_ENDING)-1;
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);
356 memmove(ectbegin, ectend, strlen(ectend)+1);
357 } else
358 break;
360 return(message);
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));
370 return(FE_SUCCESS);
373 static fte_t toc_postselect(client_t c, fd_set *read, fd_set *write, fd_set *except) {
374 return(FE_SUCCESS);
377 static unsigned char toc_get_frame_type_from_header(const unsigned char *const header) {
378 return(header[1]);
381 static unsigned short toc_get_sequence_from_header(const unsigned char *const header) {
382 unsigned short sequence;
384 sequence = ntohs(*((unsigned short *)(&header[2])));
385 return(sequence);
388 static unsigned short toc_get_length_from_header(const unsigned char *const header) {
389 unsigned short length;
391 length = ntohs(*((unsigned short *)(&header[4])));
392 return(length);
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;
401 while (*string == ' ')
402 string++;
404 length = strlen(string);
405 if (outside_flag == 1) {
406 newcounter = 1;
407 output[0] = '"';
408 } else
409 newcounter = 0;
411 while ((length > 0) && (string[length-1] == ' '))
412 length--;
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];
426 if (outside_flag == 1)
427 output[newcounter++] = '"';
428 output[newcounter] = 0;
430 return(output);
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;
440 length = strlen(password);
442 output[0] = '0';
443 output[1] = 'x';
445 newcounter = 2;
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;
454 output[newcounter] = 0;
456 return(output);
459 static fte_t toc_compare_nicks (const char *s1, const char *s2) {
460 if ((s1 == NULL) || (s2 == NULL))
461 return(FE_NOMATCH);
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++;
477 if (*s2 != 0)
478 return(FE_NOMATCH);
479 return(FE_SUCCESS);
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;
487 if (c->awaymsg != NULL) {
488 free(c->awaymsg);
489 c->awaymsg = NULL;
491 assert(c->awaysince > 0);
492 c->awaysince = 0;
494 if (c->room_head != NULL) {
495 struct s_toc_room *iter, *iternext;
497 for (iter = c->room_head; iter != NULL; iter = iternext) {
498 iternext = iter->next;
499 free(iter->name);
500 free(iter);
502 c->room_head = NULL;
504 if (c->buddybuflastgroup != NULL) {
505 free(c->buddybuflastgroup);
506 c->buddybuflastgroup = strdup("");
507 c->buddybuflen = 0;
509 toc_send_printf(c, "toc_set_dir %s", "");
510 toc_send_printf(c, "toc_noop");
512 firetalk_callback_disconnect(c, error);
513 return(FE_SUCCESS);
516 static int toc_internal_add_room(client_t c, const char *const name, const int exchange) {
517 struct s_toc_room *iter;
519 iter = c->room_head;
520 c->room_head = calloc(1, sizeof(struct s_toc_room));
521 if (c->room_head == NULL)
522 abort();
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);
532 static int toc_internal_set_joined(client_t c, const long id) {
533 struct s_toc_room *iter;
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);
541 return(FE_NOTFOUND);
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;
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);
553 return(FE_NOTFOUND);
556 static int toc_internal_find_exchange(client_t c, const char *const name) {
557 struct s_toc_room *iter;
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);
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;
572 namepart = toc_internal_split_name(name);
573 exchange = toc_internal_split_exchange(name);
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);
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];
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);
593 firetalkerror = FE_NOTFOUND;
594 return(NULL);
597 static int toc_internal_split_exchange(const char *const string) {
598 return(atoi(string));
601 static char *toc_internal_split_name(const char *const string) {
602 return(strchr(string, ':')+1);
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);
612 static int toc_internal_set_room_invited(client_t c, const char *const name, const int invited) {
613 struct s_toc_room *iter;
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);
621 return(FE_NOTFOUND);
624 static int toc_internal_get_room_invited(client_t c, const char *const name) {
625 struct s_toc_room *iter;
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);
631 return(-1);
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];
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);
648 i = snprintf(data+datai, sizeof(data)-datai, "%i", i);
649 if (i > 0)
650 datai += i;
651 break;
653 case 'x': {
654 int i = va_arg(ap, int);
656 i = snprintf(data+datai, sizeof(data)-datai, "%x", i);
657 if (i > 0)
658 datai += i;
659 break;
661 case 's': {
662 const char
663 *s = toc_quote(va_arg(ap, char *), 1);
664 size_t slen = strlen(s);
666 if ((datai+slen) > (sizeof(data)-1))
667 return(FE_PACKETSIZE);
668 strcpy(data+datai, s);
669 datai += slen;
670 break;
672 case 'S': {
673 const char
674 *s = va_arg(ap, char *);
675 size_t slen = strlen(s);
677 if ((datai+slen) > (sizeof(data)-1))
678 return(FE_PACKETSIZE);
679 strcpy(data+datai, s);
680 datai += slen;
681 break;
683 case '%':
684 data[datai++] = '%';
685 break;
687 } else {
688 data[datai++] = format[i];
689 if (datai > (sizeof(data)-1))
690 return(FE_PACKETSIZE);
693 va_end(ap);
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
702 struct s_firetalk_handle
703 *fchandle;
704 unsigned short
705 length;
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);
712 return(FE_SUCCESS);
715 static char *toc_get_arg0(char *const instring) {
716 static char data[TOC_SERVERSEND_MAXLEN];
717 char *colon;
719 if (strlen(instring) > TOC_SERVERSEND_MAXLEN) {
720 firetalkerror = FE_PACKETSIZE;
721 return(NULL);
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);
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;
737 while ((curarg < (maxargs-1)) && (curarg < 256)
738 && ((colon = strchr(str, sep)) != NULL)) {
739 args[curarg++] = str;
740 *colon = 0;
741 str = colon+1;
743 args[curarg++] = str;
744 args[curarg] = NULL;
745 return(args);
748 /* External Function Definitions */
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);
756 static client_t toc_create_handle() {
757 client_t c;
759 c = calloc(1, sizeof(struct s_toc_connection));
760 if (c == NULL)
761 abort();
763 c->lasttalk = time(NULL);
764 c->buddybuflastgroup = strdup("");
766 return(c);
769 static void toc_destroy_handle(client_t c) {
770 toc_internal_disconnect(c, FE_USERDISCONNECT);
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);
780 static fte_t toc_disconnect(client_t c) {
781 return(toc_internal_disconnect(c, FE_USERDISCONNECT));
784 static fte_t toc_signon(client_t c, const char *const username) {
785 struct s_firetalk_handle *conn;
787 /* fill & send the flap signon packet */
789 conn = firetalk_find_handle(c);
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();
800 /* send the signon string to indicate that we're speaking FLAP here */
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);
807 return(FE_SUCCESS);
810 #ifdef ENABLE_NEWGROUPS
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;
816 fchandle = firetalk_find_handle(c);
818 if (fchandle->buddy_head != NULL) {
819 struct s_firetalk_buddy *iter;
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);
826 if (slen+i+2 >= sizeof(buf))
827 return(FE_PACKET);
829 buf[slen++] = ' ';
830 strcpy(buf+slen, str);
831 slen += i;
832 count++;
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));
841 #endif
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));
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;
851 if (c->gotconfig == 0)
852 return(FE_SUCCESS);
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();
869 slen = strlen(buf);
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);
876 strcpy(c->buddybuf+c->buddybuflen, buf);
877 c->buddybuflen += slen;
879 return(FE_SUCCESS);
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));
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));
890 static fte_t toc_im_upload_buddies(client_t c) {
891 struct s_firetalk_handle *fchandle;
893 fchandle = firetalk_find_handle(c);
895 if (fchandle->buddy_head != NULL) {
896 struct s_firetalk_buddy *buddyiter;
898 for (buddyiter = fchandle->buddy_head; buddyiter != NULL; buddyiter = buddyiter->next)
899 toc_im_add_buddy(c, buddyiter->nickname, buddyiter->group, buddyiter->friendly);
902 return(FE_SUCCESS);
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;
911 fchandle = firetalk_find_handle(c);
913 if (fchandle->deny_head == NULL)
914 return(FE_SUCCESS);
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);
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));
931 length = toc_fill_header((unsigned char *)data, SFLAP_FRAME_DATA, ++c->local_sequence, strlen(&data[TOC_HEADER_LENGTH])+1);
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);
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);
944 assert(c != NULL);
945 assert(dest != NULL);
946 assert(*dest != 0);
947 assert(message != NULL);
949 if (!isauto)
950 c->lasttalk = time(NULL);
952 if (len >= sizeof(buf)-1)
953 return(FE_PACKETSIZE);
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];
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);
967 buf[j] = 0;
969 firetalk_queue_append(buf, sizeof(buf), queue, dest);
971 #ifdef DEBUG_ECHO
972 toc_echof(c, "internal_send_message", "dest=[%s] message=[%s] len=%i", dest, message, len);
973 #endif
975 return(toc_send_printf(c, "toc2_send_im %s %s%S", dest, buf, isauto?" auto":""));
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;
981 assert(dest != NULL);
982 assert(message != NULL);
984 if (strcasecmp(dest, ":RAW") == 0)
985 return(toc_send_printf(c, "%S", message));
987 fchandle = firetalk_find_handle(c);
988 return(toc_internal_send_message(c, dest, aim_interpolate_variables(message, dest), 1, &(fchandle->subcode_replies)));
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;
994 assert(dest != NULL);
995 assert(message != NULL);
997 if (strcasecmp(dest, ":RAW") == 0)
998 return(toc_send_printf(c, "%S", message));
1000 if (auto_flag)
1001 return(toc_im_send_reply(c, dest, message));
1003 fchandle = firetalk_find_handle(c);
1004 return(toc_internal_send_message(c, dest, message, 0, &(fchandle->subcode_requests)));
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];
1011 if (strcasecmp(dest, ":RAW") == 0)
1012 return(toc_send_printf(c, "%S", message));
1014 if (strlen(message) > 2042)
1015 return(FE_PACKETSIZE);
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)));
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);
1025 return(FE_SUCCESS);
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));
1034 static char *toc_ctcp_encode(client_t c, const char *const command, const char *const message) {
1035 char *str;
1037 if (message != NULL) {
1038 const char *encodedmsg = firetalk_htmlentities(message);
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);
1051 return(str);
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;
1061 if (infolen >= sizeof(profile)) {
1062 firetalk_callback_error(c, FE_MESSAGETRUNCATED, NULL, "Profile too long");
1063 return(FE_MESSAGETRUNCATED);
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);
1070 if (infolen+extralen >= 1024) {
1071 firetalk_callback_error(c, FE_MESSAGETRUNCATED, NULL, "Profile+away message too long, truncating");
1072 infolen = 1024-extralen-1;
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));
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);
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;
1097 c->awaysince = 0;
1098 return(toc_send_printf(c, "toc_set_away"));
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));
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));
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);
1127 return(toc_send_printf(c, "toc2_set_pdmode %i", c->permit_mode));
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));
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];
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);
1175 static int toc_cap(int A1, int A2, int B, int C, int D, int E1, int E2, int E3) {
1176 int j;
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);
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.
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.
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.
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 },
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 },
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 },
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.
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 },
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 },
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.
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 },
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.
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.
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.
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 },
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 */
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.
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.
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.
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.
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.
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.
1403 /* 999 */ { FE_UNKNOWN, 0, NULL },
1406 static char *decodeUTF16(const char *const end, char *s) {
1407 int i;
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);
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;
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);
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;
1438 args = toc_parse_args(data, 3, ':');
1439 assert(strcmp(arg0, args[0]) == 0);
1441 if (args[1] == NULL) {
1442 toc_internal_disconnect(c, FE_INVALIDFORMAT);
1443 return(FE_INVALIDFORMAT);
1445 err = atoi(args[1]);
1446 if ((err < 900) || (err > 999)) {
1447 toc_internal_disconnect(c, FE_INVALIDFORMAT);
1448 return(FE_INVALIDFORMAT);
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;
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
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
1491 char *name, *message;
1492 int isauto;
1494 args = toc_parse_args(data, 10, ':');
1495 assert(strcmp(arg0, args[0]) == 0);
1497 if ((args[1] == NULL) || (args[2] == NULL) || (args[9] == NULL)) {
1498 toc_internal_disconnect(c, FE_INVALIDFORMAT);
1499 return(FE_INVALIDFORMAT);
1502 name = args[1];
1503 isauto = (args[2][0]=='T')?1:0;
1504 message = args[9];
1506 aim_handle_ect(c, name, message, isauto);
1507 if (*message != 0) {
1508 char *mestart;
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;
1520 if (args[5][0] == ' ')
1521 A = "";
1522 else if (args[5][0] == 'A')
1523 A = "AOL ";
1524 else
1525 A = "unknown0 ";
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";
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";
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";
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
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;
1583 args = toc_parse_args(data, 9, ':');
1584 assert(strcmp(arg0, args[0]) == 0);
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);
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];
1607 away = info;
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);
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
1640 if (away != NULL) {
1641 firetalk_callback_subcode_reply(c, name, "AWAY", away);
1642 free(away);
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
1650 char *name;
1651 int typinginfo;
1653 args = toc_parse_args(data, 3, ':');
1654 assert(strcmp(arg0, args[0]) == 0);
1656 if (!args[1] || !args[2]) {
1657 toc_internal_disconnect(c, FE_INVALIDFORMAT);
1658 return(FE_INVALIDFORMAT);
1661 name = args[1];
1662 typinginfo = atol(args[2]);
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
1677 char *name;
1678 int isaway;
1679 long online, warn, idle;
1681 args = toc_parse_args(data, 8, ':');
1682 assert(strcmp(arg0, args[0]) == 0);
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);
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]);
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*/
1711 } else if (strcmp(arg0, "BUDDY_CAPS2") == 0) {
1712 /* 1 source
1713 ** 2 capabilities separated by commas
1715 char capstring[1024],
1716 *name, **caps;
1717 int i, firstcap;
1719 args = toc_parse_args(data, 3, ':');
1720 assert(strcmp(arg0, args[0]) == 0);
1722 if (!args[1] || !args[2]) {
1723 toc_internal_disconnect(c, FE_INVALIDFORMAT);
1724 return(FE_INVALIDFORMAT);
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;
1735 if (i == sizeof(toc_firstcaps)/sizeof(*toc_firstcaps))
1736 snprintf(capstring, sizeof(capstring), "UNKNOWN_TYPE_%X", firstcap);
1738 for (i = 1; (caps[i] != NULL) && (*caps[i] != 0); i++) {
1739 int j, A1, A2, B, C, D, E1, E2, E3;
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), "]");
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);
1763 firetalk_callback_capabilities(c, name, capstring);
1764 } else if (strcmp(arg0, "BART2") == 0) {
1765 /* 1 source
1766 ** 2 base64-encoded strings, unidentified
1768 char **barts, *name;
1769 int i;
1771 args = toc_parse_args(data, 3, ':');
1772 assert(strcmp(arg0, args[0]) == 0);
1773 name = strdup(args[1]);
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]);
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;
1787 if (type == 2) {
1788 const char *s = firetalk_debase64(args[i+1]);
1789 int len;
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);
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)
1808 args = toc_parse_args(data, 2, ':');
1809 assert(strcmp(arg0, args[0]) == 0);
1811 if (!args[1]) {
1812 firetalk_callback_error(c, FE_INVALIDFORMAT, NULL, "NICK");
1813 return(FE_SUCCESS);
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.
1825 args = toc_parse_args(data, 3, ':');
1826 assert(strcmp(arg0, args[0]) == 0);
1828 if (!args[1]) {
1829 firetalk_callback_error(c, FE_INVALIDFORMAT, NULL, "EVILED");
1830 return(FE_SUCCESS);
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.
1839 long id;
1840 char *name;
1841 int exchange, ret;
1843 args = toc_parse_args(data, 3, ':');
1844 assert(strcmp(arg0, args[0]) == 0);
1846 if (!args[1] || !args[2]) {
1847 firetalk_callback_error(c, FE_INVALIDFORMAT, NULL, "CHAT_JOIN");
1848 return(FE_SUCCESS);
1850 id = atol(args[1]);
1851 name = args[2];
1852 exchange = toc_internal_find_exchange(c, name);
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.
1864 /* 1 room ID
1865 ** 2 source
1866 ** 3 'T'=private, 'F'=public
1867 ** 4 unknown
1868 ** 5 language
1869 ** 6 message
1871 long id;
1872 char *source, *message, *mestart;
1874 args = toc_parse_args(data, 7, ':');
1875 assert(strcmp(arg0, args[0]) == 0);
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);
1881 id = atol(args[1]);
1882 source = args[2];
1883 message = args[6];
1885 if (strncasecmp(message, "<HTML><PRE>", 11) == 0) {
1886 message += 11;
1887 if ((tempchr1 = strchr(message, '<')))
1888 *tempchr1 = 0;
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.
1908 int joined;
1909 long id;
1910 char *recip,
1911 *source,
1912 *colon;
1914 args = toc_parse_args(data, 4, ':');
1915 assert(strcmp(arg0, args[0]) == 0);
1917 if (!args[1] || !args[2] || !args[3]) {
1918 firetalk_callback_error(c, FE_INVALIDFORMAT, NULL, "CHAT_UPDATE_BUDDY");
1919 return(FE_SUCCESS);
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];
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;
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.
1943 args = toc_parse_args(data, 5, ':');
1944 assert(strcmp(arg0, args[0]) == 0);
1946 if (!args[1] || !args[2] || !args[3] || !args[4]) {
1947 firetalk_callback_error(c, FE_INVALIDFORMAT, NULL, "CHAT_INVITE");
1948 return(FE_SUCCESS);
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
1958 args = toc_parse_args(data, 2, ':');
1959 assert(strcmp(arg0, args[0]) == 0);
1961 if (!args[1]) {
1962 firetalk_callback_error(c, FE_INVALIDFORMAT, NULL, "CHAT_LEFT");
1963 return(FE_SUCCESS);
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);
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];
1979 if ((friendly != NULL) && (*friendly == 0))
1980 friendly = NULL;
1981 firetalk_callback_buddyadded(c, name, group, friendly);
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);
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];
1994 if ((friendly != NULL) && (*friendly == 0))
1995 friendly = NULL;
1996 firetalk_callback_buddyadded(c, name, group, friendly);
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);
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];
2009 if ((group != NULL) && (*group == 0))
2010 group = NULL;
2011 firetalk_callback_buddyremoved(c, name, group);
2013 } else if (strcmp(arg0, "DIR_STATUS") == 0) {
2014 /* DIR_STATUS:<Return Code>:<Optional args>
2015 ** <Return Code> is always 0 for success status.
2017 args = toc_parse_args(data, 2, ':');
2018 assert(strcmp(arg0, args[0]) == 0);
2020 if (args[1] == NULL) {
2021 firetalk_callback_error(c, FE_INVALIDFORMAT, NULL, "DIR_STATUS with no status code");
2022 return(FE_SUCCESS);
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;
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.
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.
2043 c->passchange--;
2044 args = toc_parse_args(data, 3, ':');
2045 assert(strcmp(arg0, args[0]) == 0);
2047 if (!args[1]) {
2048 firetalk_callback_error(c, FE_INVALIDFORMAT, NULL, "ADMIN_PASSWD_STATUS");
2049 return(FE_SUCCESS);
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
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.
2070 int j, A1, A2, B, C, D, E1, E2, E3;
2072 args = toc_parse_args(data, 255, ':');
2073 assert(strcmp(arg0, args[0]) == 0);
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);
2079 #ifdef DEBUG_ECHO
2081 int i;
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]));
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;
2097 if ((message = toc_get_tlv_value(args, 9, 12)) != NULL)
2098 firetalk_callback_im_getmessage(c, args[1], 0, message);
2100 if ((file = toc_get_tlv_value(args, 9, 10001)) != NULL) {
2101 unsigned long size = ntohl(*((uint32_t *)(file+4)));
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);
2112 } else
2113 firetalk_callback_error(c, FE_WEIRDPACKET, NULL, "Unhandled rendezvous UUID, do we have a capability set we do not actually support?");
2115 } else
2116 firetalk_callback_error(c, FE_WEIRDPACKET, NULL, data);
2118 goto got_data_start;
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;
2127 strcpy(buf, "FFFFFFFF-0000-0000-0000-000000000000");
2128 for (i = 0; (i < len) && (i < maxlen); i++) {
2129 char b[3];
2131 sprintf(b, "%02X", str[i]);
2132 memcpy(buf+ar[i], b, 2);
2134 return(buf);
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;
2147 got_data_connecting_start:
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);
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);
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);
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
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));
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);
2180 firetalk_callback_needpass(c, password, sizeof(password));
2182 c->connectstate = 1;
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;
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",
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",
2241 magic);
2243 if (r != FE_SUCCESS) {
2244 firetalk_callback_connectfailed(c,r,NULL);
2245 return(r);
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]);
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);
2263 firetalk_callback_connectfailed(c, FE_UNKNOWN, NULL);
2264 return(FE_UNKNOWN);
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> */
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();
2283 c->connectstate = 3;
2284 } else if (strcmp(arg0, "CONFIG2") == 0) {
2285 /* CONFIG2:<config> */
2286 char *nl, *curgroup = strdup("Saved buddy");
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);
2294 tempchr1 = args[1];
2295 c->permit_mode = 0;
2296 while ((nl = strchr(tempchr1, '\n'))) {
2297 *nl = 0;
2299 if (tempchr1[1] == ':') {
2300 /* b:ACCOUNT:REAL NAME:?:CELL PHONE SLOT NUMBER:w:NOTES */
2302 args = toc_parse_args(tempchr1, 4, ':');
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;
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);
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;
2332 tempchr1 = nl+1;
2334 free(curgroup);
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);
2345 if ((c->gotconfig == 1) && (c->connectstate == 3)) {
2346 #ifdef ENABLE_GETREALNAME
2347 char realname[128];
2348 #endif
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);
2356 if (toc_im_upload_denies(c) != FE_SUCCESS) {
2357 firetalk_callback_connectfailed(c, FE_PACKET, "Error uploading denies");
2358 return(FE_PACKET);
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);
2367 char *name, *version;
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);
2381 #ifdef ENABLE_GETREALNAME
2382 firetalk_getrealname(c, realname, sizeof(realname));
2383 if (*realname != 0) {
2384 char *first, *mid, *last;
2386 first = strtok(realname, " ");
2387 mid = strtok(NULL, " ");
2388 last = strtok(NULL, " ");
2390 if (mid == NULL)
2391 mid = "";
2392 if (last == NULL) {
2393 last = mid;
2394 mid = "";
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);
2404 #endif
2406 firetalk_callback_connected(c);
2407 return(FE_SUCCESS);
2409 break;
2411 goto got_data_connecting_start;
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;
2421 c = conn->handle;
2423 if (firetalk_internal_get_connectstate(c) != FCS_ACTIVE)
2424 return(FE_NOTCONNECTED);
2426 now = time(NULL);
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);
2435 idle = (long)(now - c->lasttalk);
2436 firetalk_callback_setidle(c, &idle);
2438 if (idle < 600)
2439 idle = 0;
2441 if (idle/60 == c->lastidle/60)
2442 return(FE_IDLEFAST);
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);
2449 c->lastidle = idle;
2450 sprintf(data, "%ld", idle);
2451 return(toc_send_printf(c, "toc_set_idle %s", data));
2454 static fte_t toc_chat_join(client_t c, const char *const room) {
2455 int i;
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;
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));
2472 static fte_t toc_chat_part(client_t c, const char *const room) {
2473 int id = toc_internal_find_room_id(c, room);
2475 if (id == 0)
2476 return(FE_ROOMUNAVAILABLE);
2477 return(toc_send_printf(c, "toc_chat_leave %i", id));
2480 static fte_t toc_chat_set_topic(client_t c, const char *const room, const char *const topic) {
2481 return(FE_SUCCESS);
2484 static fte_t toc_chat_op(client_t c, const char *const room, const char *const who) {
2485 return(FE_SUCCESS);
2488 static fte_t toc_chat_deop(client_t c, const char *const room, const char *const who) {
2489 return(FE_SUCCESS);
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);
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);
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);
2505 if (id == 0)
2506 return(FE_ROOMUNAVAILABLE);
2507 return(toc_send_printf(c, "toc_chat_send %i %s", id, message));
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];
2514 if (strlen(message) > 232-4)
2515 return(FE_PACKETSIZE);
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));
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);
2525 if (id != 0)
2526 return(toc_send_printf(c, "toc_chat_invite %i %s %s", id, message, who));
2527 return(FE_NOTFOUND);
2530 #ifdef ENABLE_FILE_OFFER
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];
2538 snprintf(args, sizeof(args), "SEND %s %lu %u %ld", filename, localip, port, size);
2539 return(firetalk_subcode_send_request(fchandle, nickname, "DCC", args));
2541 #endif
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;
2547 if (isdigit(c->nickname[0]))
2548 return(FE_SUCCESS);
2549 if ((ect = toc_ctcp_encode(c, command, args)) == NULL)
2550 return(FE_SUCCESS);
2552 toc_im_send_message(c, to, ect, 0);
2553 free(ect);
2554 return(FE_SUCCESS);
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;
2560 if ((ect = toc_ctcp_encode(c, command, args)) == NULL)
2561 return(FE_SUCCESS);
2563 if (to != NULL) {
2564 fte_t ret;
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);
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