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>"
16 struct s_toc_room
*next
;
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) */
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 */
36 passchange
:1, /* whether we're changing our password right now */
42 char *buddybuflastgroup
;
45 typedef struct s_toc_connection
*client_t
;
46 #define _HAVE_CLIENT_T
48 #include "firetalk-int.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
, ...);
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
, ...) {
90 void statrefresh(void);
93 vsnprintf(buf
, sizeof(buf
), format
, ap
);
97 while ((len
> 0) && (buf
[len
-1] == '\n'))
101 status_echof(curconn
, firetalk_htmlentities(buf
));
106 static void toc_echo_send(client_t c
, const char *const where
, const unsigned char *const data
, size_t _length
) {
108 unsigned short sequence
,
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
);
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
) {
128 unsigned short sequence
,
131 if (*bufferpos
< TOC_HEADER_LENGTH
) /* don't have the whole header yet */
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 */
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';
157 assert(length
< sizeof(buf
));
158 memmove(buf
, outbuffer
, length
+1);
159 for (j
= 0; j
< length
; j
++)
163 toc_echof(c
, "find_packet", "frame=%X, sequence=in:%i, length=%i, value=[%s]\n",
164 ft
, sequence
, length
, buf
);
168 if (frametype
== SFLAP_FRAME_SIGNON
)
169 c
->remote_sequence
= sequence
;
171 if (sequence
!= ++c
->remote_sequence
) {
172 toc_internal_disconnect(c
, FE_SEQUENCE
);
173 return(FE_DISCONNECT
);
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;
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) */
202 signon
[4] = 0; /* byte 4, length 2, tlv tag (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
);
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;
214 char date
[15],tim
[15];
215 { /* build the date and time */
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
);
238 for (i
= 0; i
< l
; i
++) {
241 if (gotpercent
== 1) {
249 if (gotpercent
== 1) {
251 memcpy(&output
[o
],nickname
,nl
);
257 if (gotpercent
== 1) {
259 memcpy(&output
[o
],date
,dl
);
265 if (gotpercent
== 1) {
267 memcpy(&output
[o
],tim
,tl
);
273 if (gotpercent
== 1) {
277 output
[o
++] = input
[i
];
285 static const char *aim_normalize_room_name(const char *const name
) {
286 static char newname
[2048];
290 if (strchr(name
+1, ':') != NULL
)
292 if (strlen(name
) >= (sizeof(newname
)-2))
295 strcpy(newname
, "4:");
296 strcpy(newname
+2, name
);
301 #define STRNCMP(x,y) (strncmp((x), (y), sizeof(y)-1))
302 static char *htmlclean(const char *str
) {
307 for (i
= 0; (str
[i
] != 0) && (b
< sizeof(buf
)-1); i
++)
308 if (STRNCMP(str
+i
, ">") == 0) {
310 i
+= sizeof(">")-2;
311 } else if (STRNCMP(str
+i
, "<") == 0) {
313 i
+= sizeof("<")-2;
314 } else if (STRNCMP(str
+i
, """) == 0) {
316 i
+= sizeof(""")-2;
317 } else if (STRNCMP(str
+i
, " ") == 0) {
319 i
+= sizeof(" ")-2;
320 } else if (STRNCMP(str
+i
, "&") == 0) {
322 i
+= sizeof("&")-2;
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
) {
342 ectend
= textend
+sizeof(ECT_ENDING
)-1;
344 if ((arg
= strchr(textbegin
, ' ')) != NULL
) {
347 firetalk_callback_subcode_reply(conn
, from
, textbegin
, htmlclean(arg
));
349 firetalk_callback_subcode_request(conn
, from
, textbegin
, htmlclean(arg
));
352 firetalk_callback_subcode_reply(conn
, from
, textbegin
, NULL
);
354 firetalk_callback_subcode_request(conn
, from
, textbegin
, NULL
);
356 memmove(ectbegin
, ectend
, strlen(ectend
)+1);
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("");
368 return(toc_send_printf(c
, "toc2_new_buddies {%S}", c
->buddybuf
));
373 static fte_t
toc_postselect(client_t c
, fd_set
*read
, fd_set
*write
, fd_set
*except
) {
377 static unsigned char toc_get_frame_type_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
;
384 sequence
= ntohs(*((unsigned short *)(&header
[2])));
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])));
395 static char *toc_quote(const char *string
, const int outside_flag
) {
396 static char output
[TOC_CLIENTSEND_MAXLEN
];
401 while (*string
== ' ')
404 length
= strlen(string
);
405 if (outside_flag
== 1) {
411 while ((length
> 0) && (string
[length
-1] == ' '))
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))
418 output
[newcounter
++] = '\\';
419 output
[newcounter
++] = string
[counter
];
421 if (newcounter
> (sizeof(output
)-3))
423 output
[newcounter
++] = string
[counter
];
426 if (outside_flag
== 1)
427 output
[newcounter
++] = '"';
428 output
[newcounter
] = 0;
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
];
440 length
= strlen(password
);
447 for (i
= 0; i
< length
; i
++) {
448 if (newcounter
>= sizeof(output
)-2-1)
450 sprintf(output
+newcounter
, "%02X", password
[i
]^hash
[i
%(sizeof(hash
)-1)]);
454 output
[newcounter
] = 0;
459 static fte_t
toc_compare_nicks (const char *s1
, const char *s2
) {
460 if ((s1
== NULL
) || (s2
== NULL
))
468 if (tolower((unsigned char)(*s1
)) != tolower((unsigned char)(*s2
)))
482 static int toc_internal_disconnect(client_t c
, const int error
) {
483 if (c
->nickname
!= NULL
) {
487 if (c
->awaymsg
!= NULL
) {
491 assert(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
;
504 if (c
->buddybuflastgroup
!= NULL
) {
505 free(c
->buddybuflastgroup
);
506 c
->buddybuflastgroup
= strdup("");
509 toc_send_printf(c
, "toc_set_dir %s", "");
510 toc_send_printf(c
, "toc_noop");
512 firetalk_callback_disconnect(c
, error
);
516 static int toc_internal_add_room(client_t c
, const char *const name
, const int exchange
) {
517 struct s_toc_room
*iter
;
520 c
->room_head
= calloc(1, sizeof(struct s_toc_room
));
521 if (c
->room_head
== NULL
)
524 c
->room_head
->next
= iter
;
525 c
->room_head
->name
= strdup(name
);
526 if (c
->room_head
->name
== NULL
)
528 c
->room_head
->exchange
= exchange
;
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
) {
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)) {
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
;
567 static int toc_internal_find_room_id(client_t c
, const char *const name
) {
568 struct s_toc_room
*iter
;
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)
579 firetalkerror
= FE_NOTFOUND
;
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
);
593 firetalkerror
= FE_NOTFOUND
;
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]));
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
;
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
);
634 static fte_t
toc_send_printf(client_t c
, const char *const format
, ...) {
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
]) {
646 int i
= va_arg(ap
, int);
648 i
= snprintf(data
+datai
, sizeof(data
)-datai
, "%i", i
);
654 int i
= va_arg(ap
, int);
656 i
= snprintf(data
+datai
, sizeof(data
)-datai
, "%x", i
);
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
);
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
);
688 data
[datai
++] = format
[i
];
689 if (datai
> (sizeof(data
)-1))
690 return(FE_PACKETSIZE
);
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
);
702 struct s_firetalk_handle
707 fchandle
= firetalk_find_handle(c
);
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
);
715 static char *toc_get_arg0(char *const instring
) {
716 static char data
[TOC_SERVERSEND_MAXLEN
];
719 if (strlen(instring
) > TOC_SERVERSEND_MAXLEN
) {
720 firetalkerror
= FE_PACKETSIZE
;
724 strncpy(data
, instring
, sizeof(data
)-1);
725 data
[sizeof(data
)-1] = 0;
726 colon
= strchr(data
, ':');
732 static char **toc_parse_args(char *str
, const int maxargs
, const char sep
) {
733 static char *args
[256];
737 while ((curarg
< (maxargs
-1)) && (curarg
< 256)
738 && ((colon
= strchr(str
, sep
)) != NULL
)) {
739 args
[curarg
++] = str
;
743 args
[curarg
++] = str
;
748 /* External Function Definitions */
750 static fte_t
toc_isprint(const int c
) {
751 if ((c
>= 0) && (c
<= 255) && (isprint(c
) || (c
>= 160)))
753 return(FE_INVALIDFORMAT
);
756 static client_t
toc_create_handle() {
759 c
= calloc(1, sizeof(struct s_toc_connection
));
763 c
->lasttalk
= time(NULL
);
764 c
->buddybuflastgroup
= strdup("");
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
);
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
);
796 c
->nickname
= strdup(username
);
797 if (c
->nickname
== NULL
)
800 /* send the signon string to indicate that we're speaking FLAP here */
803 toc_echof(c
, "signon", "frame=0, length=%i, value=[%s]\n", strlen(SIGNON_STRING
), SIGNON_STRING
);
805 firetalk_internal_send_data(conn
, SIGNON_STRING
, sizeof(SIGNON_STRING
)-1);
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);
826 if (slen
+i
+2 >= sizeof(buf
))
830 strcpy(buf
+slen
, str
);
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
));
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
) {
851 if (c
->gotconfig
== 0)
854 if (strcmp(c
->buddybuflastgroup
, group
) == 0) {
855 if (friendly
!= NULL
)
856 snprintf(buf
, sizeof(buf
), "b:%s:%s\n", name
, friendly
);
858 snprintf(buf
, sizeof(buf
), "b:%s\n", name
);
860 if (friendly
!= NULL
)
861 snprintf(buf
, sizeof(buf
), "g:%s\nb:%s:%s\n", group
, name
, friendly
);
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
)
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
))
876 strcpy(c
->buddybuf
+c
->buddybuflen
, buf
);
877 c
->buddybuflen
+= slen
;
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
);
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
)
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);
925 toc_echo_send(c
, "im_upload_denies", data
, length
);
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);
934 toc_echo_send(c
, "im_upload_denies", data
, length
);
936 firetalk_internal_send_data(fchandle
, data
, length
);
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
);
945 assert(dest
!= NULL
);
947 assert(message
!= NULL
);
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
];
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
);
969 firetalk_queue_append(buf
, sizeof(buf
), queue
, dest
);
972 toc_echof(c
, "internal_send_message", "dest=[%s] message=[%s] len=%i", dest
, message
, len
);
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
));
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
);
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
) {
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);
1043 sprintf(str
, ECT_TOKEN
"%s %s" ECT_ENDING
, command
, encodedmsg
);
1045 str
= malloc(sizeof(ECT_TOKEN
)-1+strlen(command
)+sizeof(ECT_ENDING
)-1+1);
1048 sprintf(str
, ECT_TOKEN
"%s" ECT_ENDING
, command
);
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
,
1058 size_t infolen
= strlen(info
),
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
:"");
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
);
1087 c
->awaymsg
= strdup(message
);
1088 if (c
->awaymsg
== NULL
)
1090 c
->awaysince
= time(NULL
);
1091 return(toc_send_printf(c
, "toc_set_away %s", message
));
1093 if (c
->awaymsg
!= NULL
) {
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
) {
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)
1114 else if (strcasecmp(mode
, "BLOCK ALL") == 0)
1116 else if (strcasecmp(mode
, "ALLOW PERMIT") == 0)
1118 else if (strcasecmp(mode
, "BLOCK DENY") == 0)
1120 else if (strcasecmp(mode
, "ALLOW BUDDY") == 0)
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) {
1137 *A2
= strtol(uuid
, NULL
, 16);
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
) {
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)))
1193 unsigned char hastarget
:1;
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
},
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
},
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
},
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
},
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
},
1322 /* 970 */ { FE_SUCCESS
, 0, NULL
},
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 */
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.
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
) {
1409 for (i
= 0; (s
[i
] != -2) && (s
+i
< end
-1); i
+= 2)
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];
1425 r
= toc_find_packet(c
, buffer
, bufferpos
, data
, SFLAP_FRAME_DATA
, &l
);
1426 if (r
== FE_NOTFOUND
)
1428 else if (r
!= FE_SUCCESS
)
1431 arg0
= toc_get_arg0(data
);
1434 if (strcmp(arg0
, "ERROR") == 0) {
1435 /* ERROR:<Error Code>:Var args */
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
);
1451 /* TOC1 Error validating input */
1452 /* TOC2 UNDOCUMENTED */
1453 if (c
->passchange
!= 0) {
1455 firetalk_callback_error(c
, FE_NOCHANGEPASS
, NULL
, NULL
);
1456 goto got_data_start
;
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> */
1467 ** 2 'T'=auto, 'F'=normal
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"
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
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
;
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
);
1503 isauto
= (args
[2][0]=='T')?1:0;
1506 aim_handle_ect(c
, name
, message
, isauto
);
1507 if (*message
!= 0) {
1510 if (strncasecmp(message
, "/me ",4) == 0)
1511 firetalk_callback_im_getaction(c
, name
, isauto
,
1513 else if ((mestart
= strstr(message
, ">/me ")) != NULL
)
1514 firetalk_callback_im_getaction(c
, name
, isauto
,
1517 #if defined(DEBUG_ECHO) && defined(HAVE_ASPRINTF)
1518 char *A
, *B
, *C
, *encoding
, *newmessage
= NULL
;
1520 if (args
[5][0] == ' ')
1522 else if (args
[5][0] == 'A')
1527 if (args
[5][1] == ' ')
1529 else if (args
[5][1] == 'A')
1530 B
= "ADMINISTRATOR";
1531 else if (args
[5][1] == 'C')
1533 else if (args
[5][1] == 'I')
1535 else if (args
[5][1] == 'O')
1537 else if (args
[5][1] == 'U')
1538 B
= "DAMNED_TRANSIENT";
1542 if ((args
[5][2] == ' ') || (args
[5][2] == 0) || (args
[5][2] == ','))
1544 else if (args
[5][2] == 'U')
1549 if (args
[7][0] == 'A')
1551 else if (args
[7][0] == 'L')
1552 encoding
= "Latin1";
1553 else if (args
[7][0] == 'U')
1556 encoding
= "unknown";
1558 asprintf(&newmessage
, "[%s%s%s, %s, %s %s %s %s] %s",
1561 args
[2], args
[3], args
[4], args
[6],
1563 message
= newmessage
;
1565 if (isauto
) /* interpolate only auto-messages */
1566 firetalk_callback_im_getmessage(c
,
1567 name
, 1, aim_interpolate_variables(message
, c
->nickname
));
1569 firetalk_callback_im_getmessage(c
,
1571 #if defined(DEBUG_ECHO) && defined(HAVE_ASPRINTF)
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
;
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
);
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]);
1596 if (args
[2][0] == 'T') {
1597 online
= atol(args
[4]);
1602 isaway
= (args
[6][2]=='U')?1:0;
1603 warning
= atol(args
[3]);
1604 idle
= atol(args
[5]);
1610 info
= decodeUTF16(data
+l
, away
);
1612 info
= strchr(info
, -2);
1613 assert(info
!= NULL
);
1614 assert(*info
== -2);
1617 away
= strdup(aim_interpolate_variables(away
, c
->nickname
));
1621 toc_echof(c
, "got_data", "%i %i %i %i", info
[0], info
[1], info
[2], info
[3]);
1623 assert((info
[0] == -2) || (info
[0] == '<') || ((info
[0] == 0) && (info
[1] == '<')));
1625 third
= decodeUTF16(data
+l
, info
);
1627 third
= strchr(info
, -2);
1628 assert(third
!= NULL
);
1629 assert(*third
== -2);
1631 info
= aim_handle_ect(c
, name
, info
, 1);
1632 info
= aim_interpolate_variables(info
, c
->nickname
);
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
);
1641 firetalk_callback_subcode_reply(c
, name
, "AWAY", away
);
1645 firetalk_callback_gotinfo(c
, name
, info
, warning
, online
, idle
, class);
1646 } else if (strcmp(arg0
, "CLIENT_EVENT2") == 0) {
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
);
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> */
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;
1675 ** 7 status code, used in ICQ
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
);
1691 if (args
[2][0] == 'T') {
1692 online
= atol(args
[4]);
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
);
1703 firetalk_callback_im_buddyaway(c
, name
, isaway
);
1704 firetalk_callback_idleinfo(c
, name
, idle
);
1705 firetalk_callback_warninfo(c
, name
, warn
);
1707 assert(isaway
== 0);
1709 /* assert(warn == 0); Warning levels survive signing off*/
1711 } else if (strcmp(arg0
, "BUDDY_CAPS2") == 0) {
1713 ** 2 capabilities separated by commas
1715 char capstring
[1024],
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
);
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
);
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
);
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
);
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));
1752 snprintf(capstring
+strlen(capstring
), sizeof(capstring
)-strlen(capstring
), "]");
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
);
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) {
1766 ** 2 base64-encoded strings, unidentified
1768 char **barts
, *name
;
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
) {
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]));
1788 const char *s
= firetalk_debase64(args
[i
+1]);
1793 assert(s
[len
+2] == 0);
1794 assert(s
[len
+3] == 0);
1796 toc_echof(c
, "got_data", "BART STATUS_TEXT %s %s\n", name
, s
+2);
1798 firetalk_callback_statusinfo(c
, name
, s
+2);
1803 } else if (strcmp(arg0
, "NICK") == 0) {
1805 ** Tells you your correct nickname (ie how it should be capitalized and
1808 args
= toc_parse_args(data
, 2, ':');
1809 assert(strcmp(arg0
, args
[0]) == 0);
1812 firetalk_callback_error(c
, FE_INVALIDFORMAT
, NULL
, "NICK");
1815 firetalk_callback_user_nickchanged(c
, c
->nickname
, args
[1]);
1817 c
->nickname
= strdup(args
[1]);
1818 if (c
->nickname
== NULL
)
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);
1829 firetalk_callback_error(c
, FE_INVALIDFORMAT
, NULL
, "EVILED");
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
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");
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.
1866 ** 3 'T'=private, 'F'=public
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");
1885 if (strncasecmp(message
, "<HTML><PRE>", 11) == 0) {
1887 if ((tempchr1
= strchr(message
, '<')))
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);
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.
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");
1921 joined
= (args
[2][0]=='T')?1:0;
1923 recip
= toc_internal_find_room_name(c
, id
);
1926 while ((colon
= strchr(source
, ':'))) {
1929 firetalk_callback_chat_user_joined(c
, recip
, source
, NULL
);
1931 firetalk_callback_chat_user_left(c
, recip
, source
, NULL
);
1935 firetalk_callback_chat_user_joined(c
, recip
, source
, NULL
);
1936 firetalk_callback_chat_user_joined(c
, recip
, NULL
, NULL
);
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");
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);
1962 firetalk_callback_error(c
, FE_INVALIDFORMAT
, NULL
, "CHAT_LEFT");
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],
1977 *friendly
= args
[4];
1979 if ((friendly
!= NULL
) && (*friendly
== 0))
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],
1992 *friendly
= args
[5];
1994 if ((friendly
!= NULL
) && (*friendly
== 0))
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],
2009 if ((group
!= NULL
) && (*group
== 0))
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");
2024 switch (atoi(args
[1])) {
2029 firetalk_callback_error(c
, FE_INVALIDFORMAT
, NULL
, "DIR_STATUS failed, invalid format");
2032 firetalk_callback_error(c
, FE_INVALIDFORMAT
, NULL
, "DIR_STATUS with unknown code");
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.
2044 args
= toc_parse_args(data
, 3, ':');
2045 assert(strcmp(arg0
, args
[0]) == 0);
2048 firetalk_callback_error(c
, FE_INVALIDFORMAT
, NULL
, "ADMIN_PASSWD_STATUS");
2051 if (atoi(args
[1]) != 0)
2052 firetalk_callback_error(c
, FE_NOCHANGEPASS
, NULL
, NULL
);
2054 firetalk_callback_passchanged(c
);
2055 } else if (strcmp(arg0
, "PAUSE") == 0) {
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
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");
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]));
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
);
2092 firetalk_callback_error(c
, FE_WEIRDPACKET
, NULL
, "Unknown rendezvous UUID");
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 */
2107 /* ipstring */ args
[7], /* verified_ip */
2108 /* ip6string */ NULL
,
2109 /* port */ (uint16_t)atoi(args
[8]),/* port */
2110 /* type */ FF_TYPE_RAW
);
2113 firetalk_callback_error(c
, FE_WEIRDPACKET
, NULL
, "Unhandled rendezvous UUID, do we have a capability set we do not actually support?");
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;
2127 strcpy(buf
, "FFFFFFFF-0000-0000-0000-000000000000");
2128 for (i
= 0; (i
< len
) && (i
< maxlen
); i
++) {
2131 sprintf(b
, "%02X", str
[i
]);
2132 memcpy(buf
+ar
[i
], b
, 2);
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];
2141 unsigned short length
;
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
)
2152 else if (r
!= FE_SUCCESS
)
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
);
2166 srand((unsigned int) time(NULL
));
2167 c
->local_sequence
= (unsigned short)1+(unsigned short)(65536.0*rand()/(RAND_MAX
+1.0));
2169 c
->local_sequence
= 31337;
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
);
2176 toc_echo_send(c
, "got_data_connecting", data
, length
);
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,
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]
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
2211 toc_hash_password(password
),
2212 /*PACKAGE_NAME*/ ":" /*PACKAGE_VERSION*/ ":contact " /*PACKAGE_BUGREPORT*/,
2218 r
= toc_send_printf(c
, "toc2_login "
2219 "login.icq.com" // authorizer host [login.icq.com]
2220 " 5190" // authorizer port [5190]
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
2236 toc_hash_password(password
),
2237 PACKAGE_NAME
":" PACKAGE_VERSION
":contact " PACKAGE_BUGREPORT
,
2243 if (r
!= FE_SUCCESS
) {
2244 firetalk_callback_connectfailed(c
,r
,NULL
);
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)) {
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
);
2266 c
->connectstate
= 2;
2270 arg0
= toc_get_arg0(data
);
2273 if (strcmp(arg0
, "NICK") == 0) {
2274 /* NICK:<Nickname> */
2276 args
= toc_parse_args(data
, 2, ':');
2279 c
->nickname
= strdup(args
[1]);
2280 if (c
->nickname
== NULL
)
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, ':');
2291 firetalk_callback_connectfailed(c
, FE_INVALIDFORMAT
, "CONFIG2");
2292 return(FE_INVALIDFORMAT
);
2296 while ((nl
= strchr(tempchr1
, '\n'))) {
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.) */
2307 curgroup
= strdup(args
[1]);
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))
2317 if (strcmp(curgroup
, "Mobile Device") != 0)
2318 firetalk_im_add_buddy(fchandle
, args
[1], curgroup
, friendly
);
2321 case 'p': /* Person on permit list */
2322 toc_send_printf(c
, "toc_add_permit %s", args
[1]);
2324 case 'd': /* Person on deny list */
2325 firetalk_im_internal_add_deny(fchandle
, args
[1]);
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]);
2336 if ((c
->permit_mode
< 1) || (c
->permit_mode
> 5))
2338 toc_send_printf(c
, "toc2_set_pdmode %i", c
->permit_mode
);
2341 firetalk_callback_connectfailed(c
, FE_WEIRDPACKET
, data
);
2342 return(FE_WEIRDPACKET
);
2345 if ((c
->gotconfig
== 1) && (c
->connectstate
== 3)) {
2346 #ifdef ENABLE_GETREALNAME
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");
2356 if (toc_im_upload_denies(c
) != FE_SUCCESS
) {
2357 firetalk_callback_connectfailed(c
, FE_PACKET
, "Error uploading denies");
2360 r
= toc_send_printf(c
, "toc_init_done");
2361 if (r
!= FE_SUCCESS
) {
2362 firetalk_callback_connectfailed(c
, r
, "Finalizing initialization");
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
);
2375 if (r
!= FE_SUCCESS
) {
2376 firetalk_callback_connectfailed(c
, r
, "Setting capabilities");
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
, " ");
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");
2406 firetalk_callback_connected(c
);
2411 goto got_data_connecting_start
;
2414 static fte_t
toc_periodic(struct s_firetalk_handle
*const conn
) {
2415 struct s_toc_connection
*c
;
2423 if (firetalk_internal_get_connectstate(c
) != FCS_ACTIVE
)
2424 return(FE_NOTCONNECTED
);
2428 if ((c
->lastself
+60) <= now
) {
2430 r
= toc_send_printf(c
, "toc_get_status %s", c
->nickname
);
2431 if (r
!= FE_SUCCESS
)
2435 idle
= (long)(now
- c
->lasttalk
);
2436 firetalk_callback_setidle(c
, &idle
);
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
);
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
) {
2457 i
= toc_internal_get_room_invited(c
,room
);
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
)));
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
);
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
) {
2484 static fte_t
toc_chat_op(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
) {
2492 static fte_t
toc_chat_kick(client_t c
, const char *const room
, const char *const who
, const char *const reason
) {
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
));
2503 int id
= toc_internal_find_room_id(c
,room
);
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
);
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
);
2538 snprintf(args
, sizeof(args
), "SEND %s %lu %u %ld", filename
, localip
, port
, size
);
2539 return(firetalk_subcode_send_request(fchandle
, nickname
, "DCC", args
));
2544 static fte_t toc_subcode_send_request(client_t c, const char *const to, const char *const command, const char *const args) {
2547 if (isdigit(c->nickname[0]))
2549 if ((ect = toc_ctcp_encode(c, command, args)) == NULL)
2552 toc_im_send_message(c, to, ect, 0);
2557 static fte_t toc_subcode_send_reply(client_t c, const char *const to, const char *const command, const char *const args) {
2560 if ((ect = toc_ctcp_encode(c, command, args)) == NULL)
2566 ret = toc_im_send_message(c, to, ect, 1);
2572 ect_prof(c, command, ect);
2574 ect_prof(c, command, "");
2582 const firetalk_protocol_t firetalk_protocol_toc2
= {
2583 strprotocol
: "TOC2",
2584 default_server
: "toc.n.ml.org",
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
,
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
,
2629 #ifdef ENABLE_FILE_OFFER
2630 file_offer
: toc_file_offer
,