]> andersk Git - libfaim.git/blob - utils/faimtest/faimtest.c
- Wed Sep 19 18:50:34 PDT 2001
[libfaim.git] / utils / faimtest / faimtest.c
1 /* 
2  *  faimtest.
3  *
4  *  The point of faimtest is twofold:
5  *     - Test the functionality of libfaim.
6  *     - Demonstrate the functionality of libfaim and how to use it.
7  *
8  *  It does the latter rather badly, and the first as best it can without a
9  *  more realistic UI.  libfaim has a slightly different event model than
10  *  many C programmers are used to, which is why I provide faimtest as
11  *  documentation instead of attempting to explain how it works in English.
12  *  If you're still in need of more guidance, see the source for the OSCAR
13  *  "plugin" in gaim.  It does it nicely, and in a realistic situation. (Did
14  *  I mention faimtest is a bit idealized?)
15  *
16  *  The faimtest code is very ugly.  Probably always will be.  
17  *
18  *  Note that faimtest does not do a lot of error checking, except perhaps
19  *  on some libfaim funtions.  This is done for clarity, in hopes of
20  *  making this crap ever so slighly more readable.
21  *
22  */
23
24 #include "faimtest.h"
25 #include <sys/stat.h>
26
27 char *dprintf_ctime(void)
28 {
29         static char retbuf[64];
30         struct tm *lt;
31         struct timeval tv;
32         struct timezone tz;
33
34         gettimeofday(&tv, &tz);
35         lt = localtime((time_t *)&tv.tv_sec);
36         strftime(retbuf, 64, "%a %b %e %H:%M:%S %Z %Y", lt);
37
38         return retbuf;
39 }
40
41 static char *msgerrreasons[] = {
42         "Invalid error",
43         "Invalid SNAC",
44         "Rate to host",
45         "Rate to client",
46         "Not logged on",
47         "Service unavailable",
48         "Service not defined",
49         "Obsolete SNAC",
50         "Not supported by host",
51         "Not supported by client",
52         "Refused by client",
53         "Reply too big",
54         "Responses lost",
55         "Request denied",
56         "Busted SNAC payload",
57         "Insufficient rights",
58         "In local permit/deny",
59         "Too evil (sender)",
60         "Too evil (receiver)",
61         "User temporarily unavailable",
62         "No match",
63         "List overflow",
64         "Request ambiguous",
65         "Queue full",
66         "Not while on AOL",
67 };
68 static int msgerrreasonslen = 25;
69
70 aim_session_t aimsess;
71 int keepgoing = 1;
72
73 /* 
74  * This is used to intercept debugging/diagnostic messages from libfaim.
75  *
76  * Note that you should have one of these even if you use a debuglevel of
77  * zero, as libfaim will send serious errors to stderr by default.
78  *
79  */
80 static void faimtest_debugcb(aim_session_t *sess, int level, const char *format, va_list va)
81 {
82
83         vfprintf(stderr, format, va);
84
85         return;
86 }
87
88 int faimtest_flapversion(aim_session_t *sess, aim_frame_t *fr, ...)
89 {
90
91         /* XXX fix libfaim to support this */
92         dvprintf("using FLAP version 0x%08x\n", /* aimutil_get32(fr->data)*/ 0xffffffff);
93
94 #if 0
95         /* 
96          * This is an alternate location for starting the login process.
97          */
98         /* XXX should do more checking to make sure its really the right AUTH conn */
99         if (fr->conn->type == AIM_CONN_TYPE_AUTH) {
100                 /* do NOT send a flapversion, request_login will send it if needed */
101                 aim_request_login(sess, fr->conn, priv->screenname);
102                 dprintf("faimtest: login request sent\n");
103         }
104 #endif
105
106         return 1;
107 }
108
109 /*
110  * This is a frivilous callback. You don't need it. I only used it for
111  * debugging non-blocking connects.
112  *
113  * If packets are sent to a conn before its fully connected, they
114  * will be queued and then transmitted when the connection completes.
115  *
116  */
117 int faimtest_conncomplete(aim_session_t *sess, aim_frame_t *fr, ...)
118 {
119         va_list ap;
120         aim_conn_t *conn;
121
122         va_start(ap, fr);
123         conn = va_arg(ap, aim_conn_t *);
124         va_end(ap);
125
126         if (conn)
127                 dvprintf("faimtest: connection on %d completed\n", conn->fd);
128
129         return 1;
130 }
131
132 #ifdef _WIN32
133 /*
134  * This is really all thats needed to link against libfaim on win32.
135  *
136  * Note that this particular version of faimtest has never been tested
137  * on win32, but I'm fairly sure it should work.
138  */
139 static int initwsa(void)
140 {
141         WORD wVersionRequested;
142         WSADATA wsaData;
143
144         wVersionRequested = MAKEWORD(2,2);
145         return WSAStartup(wVersionRequested, &wsaData);
146 }
147 #endif /* _WIN32 */
148
149 /*
150  * This is unrealistic.  Most clients will not be able to do this.
151  */
152 int faimtest_init(void)
153 {
154         aim_conn_t *stdinconn = NULL;
155
156         if (!(stdinconn = aim_newconn(&aimsess, 0, NULL))) {
157                 dprintf("unable to create connection for stdin!\n");
158                 return -1;
159         }
160
161         stdinconn->fd = STDIN_FILENO;
162
163         return 0;
164 }
165
166 int main(int argc, char **argv)
167 {
168         aim_conn_t *waitingconn = NULL;
169         int i;
170         int selstat = 0;
171         static int faimtest_mode = 0;
172         struct timeval tv;
173         time_t lastnop = 0;
174         const char *buddyiconpath = NULL;
175         struct faimtest_priv priv = {
176                 NULL, NULL, NULL, NULL,
177                 NULL, NULL, NULL, NULL,
178                 0,
179                 NULL, NULL,
180                 NULL, 0, 0, 0
181         };
182
183         priv.screenname = getenv("SCREENNAME");
184         priv.password = getenv("PASSWORD");
185         priv.server = getenv("AUTHSERVER");
186         priv.proxy = getenv("SOCKSPROXY");
187         priv.proxyusername = getenv("SOCKSNAME");
188         priv.proxypass = getenv("SOCKSPASS");
189
190         priv.listingpath = getenv("LISTINGPATH");
191
192         while ((i = getopt(argc, argv, "u:p:a:U:P:A:l:c:hoOb:i:")) != EOF) {
193                 switch (i) {
194                 case 'u': priv.screenname = optarg; break;
195                 case 'p': priv.password = optarg; break;
196                 case 'a': priv.server = optarg; break;
197                 case 'U': priv.proxyusername = optarg; break;
198                 case 'P': priv.proxypass = optarg; break;
199                 case 'A': priv.proxy = optarg; break;
200                 case 'l': priv.listingpath = optarg; break;
201                 case 'c': priv.ohcaptainmycaptain = optarg; break;
202                 case 'o': faimtest_mode = 1; break; /* half old interface */
203                 case 'O': faimtest_mode = 2; break; /* full old interface */
204                 case 'b': priv.aimbinarypath = optarg; break;
205                 case 'i': buddyiconpath = optarg; break;
206                 case 'h':
207                 default:
208                         printf("faimtest\n");
209                         printf(" Options: \n");
210                         printf("    -u name       Screen name ($SCREENNAME)\n");
211                         printf("    -p passwd     Password ($PASSWORD)\n");
212                         printf("    -a host:port  Authorizer ($AUTHSERVER)\n");
213                         printf("    -U name       Proxy user name ($SOCKSPROXY)\n");
214                         printf("    -P passwd     Proxy password ($SOCKSNAME)\n");
215                         printf("    -A host:port  Proxy host ($SOCKSPASS)\n");
216                         printf("    -l path       Path to listing file ($LISTINGPATH)\n");
217                         printf("    -c name       Screen name of owner\n");
218                         printf("    -o            Login at startup, then prompt\n");
219                         printf("    -O            Login, never give prompt\n");
220                         printf("    -b path       Path to AIM 3.5.1670 binaries\n");
221                         printf("    -i file       Buddy Icon to send\n");
222                         exit(0);
223                 }
224         }
225
226 #ifdef _WIN32
227         if (initwsa() != 0) {
228                 dprintf("faimtest: could not initialize windows sockets\n");
229                 return -1;
230         }
231 #endif /* _WIN32 */
232
233         /* Pass zero as flags if you want blocking connects */
234         aim_session_init(&aimsess, AIM_SESS_FLAGS_NONBLOCKCONNECT, 1);
235         aim_setdebuggingcb(&aimsess, faimtest_debugcb); /* still needed even if debuglevel = 0 ! */
236         aimsess.aux_data = &priv;
237
238         if (priv.listingpath) {
239                 char *listingname;
240                 
241                 listingname = (char *)calloc(1, strlen(priv.listingpath)+strlen("/listing.txt"));
242                 sprintf(listingname, "%s/listing.txt", priv.listingpath);
243                 
244                 if ((priv.listingfile = fopen(listingname, "r")) == NULL)
245                         dvprintf("Couldn't open %s... disabling that shit.\n", listingname);
246
247                 free(listingname);
248         }
249
250         if (buddyiconpath) {
251                 struct stat st;
252                 FILE *f;
253
254                 if ((stat(buddyiconpath, &st) != -1) && (st.st_size <= MAXICONLEN) && (f = fopen(buddyiconpath, "r"))) {
255
256                         priv.buddyiconlen = st.st_size;
257                         priv.buddyiconstamp = st.st_mtime;
258                         priv.buddyicon = malloc(priv.buddyiconlen);
259                         fread(priv.buddyicon, 1, st.st_size, f);
260
261                         priv.buddyiconsum = aim_iconsum(priv.buddyicon, priv.buddyiconlen);
262
263                         dvprintf("read %d bytes of %s for buddy icon (sum 0x%08x)\n", priv.buddyiconlen, buddyiconpath, priv.buddyiconsum);
264
265                         fclose(f);
266
267                 } else
268                         dvprintf("could not open buddy icon %s\n", buddyiconpath);
269
270         }
271
272         faimtest_init();
273
274         if (faimtest_mode < 2)
275                 cmd_init();
276
277         if (faimtest_mode >= 1) {
278                 if (login(&aimsess, priv.screenname, priv.password) == -1) {
279                         if (faimtest_mode < 2)
280                                 cmd_uninit();
281                         exit(-1);
282                 }
283         }
284
285         while (keepgoing) {
286
287                 /* XXX uh. */
288                 tv.tv_sec = 5;
289                 tv.tv_usec = 0;
290
291                 waitingconn = aim_select(&aimsess, &tv, &selstat);
292
293                 if (priv.connected && ((time(NULL) - lastnop) > 30)) {
294                         lastnop = time(NULL);
295                         aim_flap_nop(&aimsess, aim_getconn_type(&aimsess, AIM_CONN_TYPE_BOS));
296                 }
297
298                 if (selstat == -1) { /* error */
299                         keepgoing = 0; /* fall through */
300                 } else if (selstat == 0) { /* no events pending */
301                         ;
302                 } else if (selstat == 1) { /* outgoing data pending */
303                         aim_tx_flushqueue(&aimsess);
304                 } else if (selstat == 2) { /* incoming data pending */
305                         if ((faimtest_mode < 2) && (waitingconn->fd == STDIN_FILENO)) {
306                                 cmd_gotkey();
307                         } else {
308                                 if (waitingconn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) {
309                                         if (aim_handlerendconnect(&aimsess, waitingconn) < 0) {
310                                                 dprintf("connection error (rend out)\n");
311                                                 aim_conn_kill(&aimsess, &waitingconn);
312                                         }
313                                 } else {
314                                         if (aim_get_command(&aimsess, waitingconn) >= 0) {
315                                                 aim_rxdispatch(&aimsess);
316                                         } else {
317                                                 dvprintf("connection error (type 0x%04x:0x%04x)\n", waitingconn->type, waitingconn->subtype);
318                                                 /* we should have callbacks for all these, else the library will do the conn_kill for us. */
319                                                 if (waitingconn->type == AIM_CONN_TYPE_RENDEZVOUS) {
320                                                         if (waitingconn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM)
321                                                                 dvprintf("disconnected from %s\n", aim_directim_getsn(waitingconn));
322                                                         aim_conn_kill(&aimsess, &waitingconn);
323                                                 } else
324                                                         aim_conn_kill(&aimsess, &waitingconn);
325                                                 if (!aim_getconn_type(&aimsess, AIM_CONN_TYPE_BOS)) {
326                                                         dprintf("major connection error\n");
327                                                         if (faimtest_mode == 2)
328                                                                 break;
329                                                 }
330                                         }
331                                 }
332                         }
333                 }
334         }
335
336         /* close up all connections, dead or no */
337         aim_session_kill(&aimsess); 
338
339         if (faimtest_mode < 2) {
340                 printf("\n");
341                 cmd_uninit();
342         }
343
344         free(priv.buddyicon);
345
346         /* Get out */
347         exit(0);
348 }
349
350 int faimtest_serverready(aim_session_t *sess, aim_frame_t *fr, ...)
351 {
352         int famcount, i;
353         fu16_t *families;
354         va_list ap;
355
356         va_start(ap, fr);
357         famcount = va_arg(ap, int);
358         families = va_arg(ap, fu16_t *);
359         va_end(ap);
360
361         dvprintf("faimtest: SNAC families supported by this host (type %d): ", fr->conn->type);
362         for (i = 0; i < famcount; i++)
363                 dvinlineprintf("0x%04x ", families[i]);
364         dinlineprintf("\n");
365
366         if (fr->conn->type == AIM_CONN_TYPE_AUTH) {
367
368                 aim_auth_setversions(sess, fr->conn);
369                 aim_bos_reqrate(sess, fr->conn); /* request rate info */
370
371                 dprintf("done with auth server ready\n");
372
373         } else if (fr->conn->type == AIM_CONN_TYPE_BOS) {
374
375                 aim_setversions(sess, fr->conn);
376                 aim_bos_reqrate(sess, fr->conn); /* request rate info */
377
378                 dprintf("done with BOS server ready\n");
379         }
380
381         return 1;
382 }
383
384 int faimtest_parse_connerr(aim_session_t *sess, aim_frame_t *fr, ...)
385 {
386         struct faimtest_priv *priv = (struct faimtest_priv *)sess->aux_data;
387         va_list ap;
388         fu16_t code;
389         char *msg;
390
391         va_start(ap, fr);
392         code = va_arg(ap, int);
393         msg = va_arg(ap, char *);
394         va_end(ap);
395
396         dvprintf("connerr: Code 0x%04x: %s\n", code, msg);
397         aim_conn_kill(sess, &fr->conn); /* this will break the main loop */
398
399         priv->connected = 0;
400
401         return 1;
402 }
403
404 static int faimtest_rateresp_auth(aim_session_t *sess, aim_frame_t *fr, ...)
405 {
406
407         aim_bos_ackrateresp(sess, fr->conn);
408         aim_auth_clientready(sess, fr->conn);
409
410         dprintf("faimtest: connected to authorization/admin service\n");
411
412         return 1;
413 }
414
415 int faimtest_accountconfirm(aim_session_t *sess, aim_frame_t *fr, ...)
416 {
417         int status;
418         va_list ap;
419
420         va_start(ap, fr);
421         status = va_arg(ap, int); /* status code of confirmation request */
422         va_end(ap);
423
424         dvprintf("account confirmation returned status 0x%04x (%s)\n", status, (status==0x0000)?"email sent":"unknown");
425
426         return 1;
427 }
428
429 #if 0
430 /* 
431  * This kind of function is really not legal in the new bstream way...
432  * In fact, clients should never access the aim_frame_t directly in handlers,
433  * since that may leave it in a bizare state for the lower layers.  In fact,
434  * clients should probably not even get passed a pointer like this.
435  *
436  */
437 int faimtest_parse_unknown(aim_session_t *sess, aim_frame_t *fr, ...)
438 {
439         int i;
440
441         aim_bstream_rewind(&fr->data); /* boo! */
442
443         dprintf("\nReceived unknown packet:");
444         for (i = 0; aim_bstream_empty(&fr->data); i++) {
445                 if ((i % 8) == 0)
446                         dinlineprintf("\n\t");
447                 dvinlineprintf("0x%2x ", aimbs_get8(&fr->data));
448         }
449         dinlineprintf("\n\n");
450
451         return 1;
452 }
453 #endif
454
455 static int faimtest_infochange(aim_session_t *sess, aim_frame_t *fr, ...)
456 {
457         fu16_t change = 0, perms, type;
458         int length, str;
459         char *val;
460         va_list ap;
461
462         va_start(ap, fr);
463         change = va_arg(ap, int);
464         perms = (fu16_t)va_arg(ap, unsigned int);
465         type = (fu16_t)va_arg(ap, unsigned int);
466         length = va_arg(ap, int);
467         val = va_arg(ap, char *);
468         str = va_arg(ap, int);
469         va_end(ap);
470
471         dvprintf("info%s: perms = %d, type = %x, length = %d, val = %s\n", change?" change":"", perms, type, length, str?val:"(not string)");
472
473         return 1;
474 }
475
476
477 int faimtest_handleredirect(aim_session_t *sess, aim_frame_t *fr, ...)
478 {
479         va_list ap;
480         int serviceid;
481         char *ip;
482         fu8_t *cookie;
483
484         va_start(ap, fr);
485         serviceid = va_arg(ap, int);
486         ip = va_arg(ap, char *);
487         cookie = va_arg(ap, fu8_t *);
488
489         if (serviceid == 0x0005) {  /* Adverts */
490 #if 0
491                 aim_conn_t *tstconn;
492
493                 tstconn = aim_newconn(sess, AIM_CONN_TYPE_ADS, ip);
494                 if (!tstconn || (tstconn->status & AIM_CONN_STATUS_RESOLVERR)) {
495                         dprintf("faimtest: unable to reconnect with authorizer\n");
496                 } else {
497                         aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, faimtest_flapversion, 0);
498                         aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE, faimtest_conncomplete, 0);
499                         aim_conn_addhandler(sess, tstconn, 0x0001, 0x0003, faimtest_serverready, 0);
500                         aim_conn_addhandler(sess, tstconn, 0x0001, 0x0007, faimtest_rateresp, 0); /* rate info */
501                         aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_GEN, 0x0018, faimtest_hostversions, 0);
502                         aim_auth_sendcookie(sess, tstconn, cookie);
503                         dprintf("sent cookie to adverts host\n");
504                 }
505 #endif
506         } else if (serviceid == 0x0007) {  /* Authorizer */
507                 aim_conn_t *tstconn;
508                 
509                 tstconn = aim_newconn(sess, AIM_CONN_TYPE_AUTH, ip);
510                 if (!tstconn || (tstconn->status & AIM_CONN_STATUS_RESOLVERR)) {
511                         dprintf("faimtest: unable to reconnect with authorizer\n");
512                 } else {
513                         aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, faimtest_flapversion, 0);
514                         aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE, faimtest_conncomplete, 0);
515                         aim_conn_addhandler(sess, tstconn, 0x0001, 0x0003, faimtest_serverready, 0);
516                         aim_conn_addhandler(sess, tstconn, 0x0001, 0x0007, faimtest_rateresp_auth, 0); /* rate info */
517                         //aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_GEN, 0x0018, faimtest_hostversions, 0);
518                         aim_conn_addhandler(sess, tstconn, 0x0007, 0x0007, faimtest_accountconfirm, 0);
519                         aim_conn_addhandler(sess, tstconn, 0x0007, 0x0003, faimtest_infochange, 0);
520                         aim_conn_addhandler(sess, tstconn, 0x0007, 0x0005, faimtest_infochange, 0);
521                         /* Send the cookie to the Auth */
522                         aim_auth_sendcookie(sess, tstconn, cookie);
523                         dprintf("sent cookie to authorizer host\n");
524                 }
525         } else if (serviceid == 0x000d) {  /* ChatNav */
526
527                 chatnav_redirect(sess, ip, cookie);
528                 
529         } else if (serviceid == 0x000e) { /* Chat */
530                 char *roomname = NULL;
531                 int exchange;
532
533                 roomname = va_arg(ap, char *);
534                 exchange = va_arg(ap, int);
535
536                 chat_redirect(sess, ip, cookie, roomname, exchange);
537
538         } else {
539                 dvprintf("uh oh... got redirect for unknown service 0x%04x!!\n", serviceid);
540         }
541
542         va_end(ap);
543
544         return 1;
545 }
546
547 static int faimtest_rateresp_bos(aim_session_t *sess, aim_frame_t *fr, ...)
548 {
549         struct faimtest_priv *priv = (struct faimtest_priv *)sess->aux_data;
550         char buddies[128]; /* this is the new buddy list */
551         char profile[256]; /* this is the new profile */ 
552         char awaymsg[] = {"blah blah blah Ole! blah blah blah"};
553
554         /* Caution: Buddy1 and Buddy2 are real people! (who I don't know) */
555         snprintf(buddies, sizeof(buddies), "Buddy1&Buddy2&%s&", priv->ohcaptainmycaptain ? priv->ohcaptainmycaptain : "blah");
556         snprintf(profile, sizeof(profile), "Hello.<br>My captain is %s.  They were dumb enough to leave this message in their client, or they are using faimtest.  Shame on them.", priv->ohcaptainmycaptain);
557
558         aim_bos_ackrateresp(sess, fr->conn);  /* ack rate info response */
559         aim_bos_reqpersonalinfo(sess, fr->conn);
560         aim_bos_reqlocaterights(sess, fr->conn);
561         aim_bos_setprofile(sess, fr->conn, profile, awaymsg, AIM_CAPS_BUDDYICON | AIM_CAPS_CHAT | AIM_CAPS_GETFILE | AIM_CAPS_SENDFILE | AIM_CAPS_IMIMAGE | AIM_CAPS_GAMES | AIM_CAPS_SAVESTOCKS | AIM_CAPS_SENDBUDDYLIST);
562         aim_bos_reqbuddyrights(sess, fr->conn);
563
564         /* send the buddy list and profile (required, even if empty) */
565         aim_bos_setbuddylist(sess, fr->conn, buddies);
566
567         aim_reqicbmparams(sess, fr->conn);  
568
569         aim_bos_reqrights(sess, fr->conn);  
570         /* set group permissions -- all user classes */
571         aim_bos_setgroupperm(sess, fr->conn, AIM_FLAG_ALLUSERS);
572         aim_bos_setprivacyflags(sess, fr->conn, AIM_PRIVFLAGS_ALLOWIDLE);
573
574         return 1;
575 }
576
577 static int faimtest_icbmparaminfo(aim_session_t *sess, aim_frame_t *fr, ...)
578 {
579         struct aim_icbmparameters *params;
580         va_list ap;
581
582         va_start(ap, fr);
583         params = va_arg(ap, struct aim_icbmparameters *);
584         va_end(ap);
585
586         dvprintf("ICBM Parameters: maxchannel = %d, default flags = 0x%08lx, max msg len = %d, max sender evil = %f, max reciever evil = %f, min msg interval = %ld\n", params->maxchan, params->flags, params->maxmsglen, ((float)params->maxsenderwarn)/10.0, ((float)params->maxrecverwarn)/10.0, params->minmsginterval);
587
588         /*
589          * Set these to your taste, or client medium.  Setting minmsginterval
590          * higher is good for keeping yourself from getting flooded (esp
591          * if you're on a slow connection or something where that would be
592          * useful).
593          */
594         params->maxmsglen = 8000;
595         params->minmsginterval = 0; /* in milliseconds */
596
597         aim_seticbmparam(sess, fr->conn, params);
598
599         return 1;
600 }
601
602 static int faimtest_hostversions(aim_session_t *sess, aim_frame_t *fr, ...)
603 {
604         int vercount, i;
605         fu8_t *versions;
606         va_list ap;
607
608         va_start(ap, fr);
609         vercount = va_arg(ap, int); /* number of family/version pairs */
610         versions = va_arg(ap, fu8_t *);
611         va_end(ap);
612
613         dprintf("faimtest: SNAC versions supported by this host: ");
614         for (i = 0; i < vercount*4; i += 4) {
615                 dvinlineprintf("0x%04x:0x%04x ", 
616                         aimutil_get16(versions+i),  /* SNAC family */
617                         aimutil_get16(versions+i+2) /* Version number */);
618         }
619         dinlineprintf("\n");
620
621         return 1;
622 }
623
624 static int faimtest_parse_buddyrights(aim_session_t *sess, aim_frame_t *fr, ...)
625 {
626         va_list ap;
627         fu16_t maxbuddies, maxwatchers;
628
629         va_start(ap, fr);
630         maxbuddies = va_arg(ap, int);
631         maxwatchers = va_arg(ap, int);
632         va_end(ap);
633
634         dvprintf("buddy list rights: Max buddies = %d / Max watchers = %d\n", maxbuddies, maxwatchers);
635
636         return 1;
637 }
638
639 static int faimtest_bosrights(aim_session_t *sess, aim_frame_t *fr, ...)
640 {
641         va_list ap;
642         fu16_t maxpermits, maxdenies;
643
644         va_start(ap, fr);
645         maxpermits = va_arg(ap, int);
646         maxdenies = va_arg(ap, int);
647         va_end(ap);
648
649         dvprintf("BOS rights: Max permit = %d / Max deny = %d\n", maxpermits, maxdenies);
650
651         aim_bos_clientready(sess, fr->conn);
652
653         dprintf("officially connected to BOS.\n");
654
655         return 1;
656 }
657
658 static int faimtest_locrights(aim_session_t *sess, aim_frame_t *fr, ...)
659 {
660         va_list ap;
661         fu16_t maxsiglen;
662
663         va_start(ap, fr);
664         maxsiglen = va_arg(ap, int);
665         va_end(ap);
666
667         dvprintf("locate rights: max signature length = %d\n", maxsiglen);
668
669         return 1;
670 }
671
672 static int faimtest_reportinterval(aim_session_t *sess, aim_frame_t *fr, ...)
673 {
674         struct faimtest_priv *priv = (struct faimtest_priv *)sess->aux_data;
675         va_list ap;
676         fu16_t interval;
677
678         va_start(ap, fr);
679         interval = va_arg(ap, int);
680         va_end(ap);
681
682         dvprintf("minimum report interval: %d (seconds?)\n", interval);
683
684         if (!priv->connected)
685                 priv->connected++;
686
687 #if 0
688         aim_bos_reqservice(sess, fr->conn, 0x0005); /* adverts */
689         aim_bos_reqservice(sess, fr->conn, 0x000f); /* user directory */
690
691         /* Don't know what this does... */
692         /* XXX sess->sn should be normalized by the 0001/000f handler */
693         aim_0002_000b(sess, fr->conn, sess->sn);
694 #endif
695
696         aim_reqicbmparams(sess, fr->conn);
697
698         return 1;
699 }
700
701 static int faimtest_parse_motd(aim_session_t *sess, aim_frame_t *fr, ...)
702 {
703         static char *codes[] = {
704                 "Unknown",
705                 "Mandatory upgrade",
706                 "Advisory upgrade",
707                 "System bulletin",
708                 "Top o' the world!"
709         };
710         static int codeslen = 5;
711         char *msg;
712         fu16_t id;
713         va_list ap;
714
715         va_start(ap, fr);
716         id = va_arg(ap, int);
717         msg = va_arg(ap, char *);
718         va_end(ap);
719
720         dvprintf("motd: %s (%d / %s)\n", msg, id, (id < codeslen)?codes[id]:"unknown");
721
722         return 1;
723 }
724
725 /*
726  * This is a little more complicated than it looks.  The module
727  * name (proto, boscore, etc) may or may not be given.  If it is
728  * not given, then use aim.exe.  If it is given, put ".ocm" on the
729  * end of it.
730  *
731  * Now, if the offset or length requested would cause a read past
732  * the end of the file, then the request is considered invalid.  Invalid
733  * requests are processed specially.  The value hashed is the
734  * the request, put into little-endian (eight bytes: offset followed
735  * by length).  
736  *
737  * Additionally, if the request is valid, the length is mod 4096.  It is
738  * important that the length is checked for validity first before doing
739  * the mod.
740  *
741  * Note to Bosco's Brigade: if you'd like to break this, put the 
742  * module name on an invalid request.
743  *
744  */
745 static int getaimdata(aim_session_t *sess, unsigned char **bufret, int *buflenret, unsigned long offset, unsigned long len, const char *modname)
746 {
747         struct faimtest_priv *priv = (struct faimtest_priv *)sess->aux_data;
748         FILE *f;
749         static const char defaultmod[] = "aim.exe";
750         char *filename = NULL;
751         struct stat st;
752         unsigned char *buf;
753         int invalid = 0;
754
755         if (!bufret || !buflenret)
756                 return -1;
757
758         if (modname) {
759
760                 if (!(filename = malloc(strlen(priv->aimbinarypath)+1+strlen(modname)+4+1))) {
761                         dperror("memrequest: malloc");
762                         return -1;
763                 }
764
765                 sprintf(filename, "%s/%s.ocm", priv->aimbinarypath, modname);
766
767         } else {
768
769                 if (!(filename = malloc(strlen(priv->aimbinarypath)+1+strlen(defaultmod)+1))) {
770                         dperror("memrequest: malloc");
771                         return -1;
772                 }
773
774                 sprintf(filename, "%s/%s", priv->aimbinarypath, defaultmod);
775
776         }
777
778         if (stat(filename, &st) == -1) {
779                 if (!modname) {
780                         dperror("memrequest: stat");
781                         free(filename);
782                         return -1;
783                 }
784                 invalid = 1;
785         }
786
787         if (!invalid) {
788                 if ((offset > st.st_size) || (len > st.st_size))
789                         invalid = 1;
790                 else if ((st.st_size - offset) < len)
791                         len = st.st_size - offset;
792                 else if ((st.st_size - len) < len)
793                         len = st.st_size - len;
794         }
795
796         if (!invalid && len)
797                 len %= 4096;
798
799         if (invalid) {
800                 int i;
801
802                 free(filename); /* not needed */
803
804                 dvprintf("memrequest: recieved invalid request for 0x%08lx bytes at 0x%08lx (file %s)\n", len, offset, modname);
805
806                 i = 8;
807                 if (modname)
808                         i += strlen(modname);
809
810                 if (!(buf = malloc(i)))
811                         return -1;
812
813                 i = 0;
814
815                 if (modname) {
816                         memcpy(buf, modname, strlen(modname));
817                         i += strlen(modname);
818                 }
819
820                 /* Damn endianness. This must be little (LSB first) endian. */
821                 buf[i++] = offset & 0xff;
822                 buf[i++] = (offset >> 8) & 0xff;
823                 buf[i++] = (offset >> 16) & 0xff;
824                 buf[i++] = (offset >> 24) & 0xff;
825                 buf[i++] = len & 0xff;
826                 buf[i++] = (len >> 8) & 0xff;
827                 buf[i++] = (len >> 16) & 0xff;
828                 buf[i++] = (len >> 24) & 0xff;
829
830                 *bufret = buf;
831                 *buflenret = i;
832
833         } else {
834
835                 if (!(buf = malloc(len))) {
836                         free(filename);
837                         return -1;
838                 }
839
840                 dvprintf("memrequest: loading %ld bytes from 0x%08lx in \"%s\"...\n", len, offset, filename);
841
842                 if (!(f = fopen(filename, "r"))) {
843                         dperror("memrequest: fopen");
844                         free(filename);
845                         free(buf);
846                         return -1;
847                 }
848
849                 free(filename);
850
851                 if (fseek(f, offset, SEEK_SET) == -1) {
852                         dperror("memrequest: fseek");
853                         fclose(f);
854                         free(buf);
855                         return -1;
856                 }
857
858                 if (fread(buf, len, 1, f) != 1) {
859                         dperror("memrequest: fread");
860                         fclose(f);
861                         free(buf);
862                         return -1;
863                 }
864
865                 fclose(f);
866
867                 *bufret = buf;
868                 *buflenret = len;
869
870         }
871
872         return 0; /* success! */
873 }
874
875 /*
876  * This will get an offset and a length.  The client should read this
877  * data out of whatever AIM.EXE binary the user has provided (hopefully
878  * it matches the client information thats sent at login) and pass a
879  * buffer back to libfaim so it can hash the data and send it to AOL for
880  * inspection by the client police.
881  */
882 static int faimtest_memrequest(aim_session_t *sess, aim_frame_t *fr, ...)
883 {
884         struct faimtest_priv *priv = (struct faimtest_priv *)sess->aux_data;
885         va_list ap;
886         fu32_t offset, len;
887         char *modname;
888         unsigned char *buf;
889         int buflen;
890
891         va_start(ap, fr);
892         offset = va_arg(ap, fu32_t);
893         len = va_arg(ap, fu32_t);
894         modname = va_arg(ap, char *);
895         va_end(ap);
896
897         if (priv->aimbinarypath && (getaimdata(sess, &buf, &buflen, offset, len, modname) == 0)) {
898
899                 aim_sendmemblock(sess, fr->conn, offset, buflen, buf, AIM_SENDMEMBLOCK_FLAG_ISREQUEST);
900
901                 free(buf);
902
903         } else {
904
905                 dvprintf("memrequest: unable to use AIM binary (\"%s/%s\"), sending defaults...\n", priv->aimbinarypath, modname);
906
907                 aim_sendmemblock(sess, fr->conn, offset, len, NULL, AIM_SENDMEMBLOCK_FLAG_ISREQUEST);
908
909         }
910
911         return 1;
912 }
913
914 static void printuserflags(fu16_t flags)
915 {
916
917         if (flags & AIM_FLAG_UNCONFIRMED)
918                 dinlineprintf("UNCONFIRMED ");
919         if (flags & AIM_FLAG_ADMINISTRATOR)
920                 dinlineprintf("ADMINISTRATOR ");
921         if (flags & AIM_FLAG_AOL)
922                 dinlineprintf("AOL ");
923         if (flags & AIM_FLAG_OSCAR_PAY)
924                 dinlineprintf("OSCAR_PAY ");
925         if (flags & AIM_FLAG_FREE)
926                 dinlineprintf("FREE ");
927         if (flags & AIM_FLAG_AWAY)
928                 dinlineprintf("AWAY ");
929         if (flags & AIM_FLAG_UNKNOWN40)
930                 dinlineprintf("ICQ? ");
931         if (flags & AIM_FLAG_UNKNOWN80)
932                 dinlineprintf("UNKNOWN80 ");
933         if (flags & AIM_FLAG_ACTIVEBUDDY)
934                 dinlineprintf("ACTIVEBUDDY ");
935
936         return;
937 }
938
939 static int faimtest_parse_userinfo(aim_session_t *sess, aim_frame_t *fr, ...)
940 {
941         struct aim_userinfo_s *userinfo;
942         char *prof_encoding = NULL;
943         char *prof = NULL;
944         fu16_t inforeq = 0;
945
946         va_list ap;
947         va_start(ap, fr);
948         userinfo = va_arg(ap, struct aim_userinfo_s *);
949         prof_encoding = va_arg(ap, char *);
950         prof = va_arg(ap, char *);
951         inforeq = (fu16_t)va_arg(ap, unsigned int);
952         va_end(ap);
953
954         dvprintf("faimtest: userinfo: sn: %s\n", userinfo->sn);
955         dvprintf("faimtest: userinfo: warnlevel: 0x%04x\n", userinfo->warnlevel);
956         dvprintf("faimtest: userinfo: flags: 0x%04x = ", userinfo->flags);
957         printuserflags(userinfo->flags);
958         dinlineprintf("\n");
959
960         dvprintf("faimtest: userinfo: membersince: %lu\n", userinfo->membersince);
961         dvprintf("faimtest: userinfo: onlinesince: %lu\n", userinfo->onlinesince);
962         dvprintf("faimtest: userinfo: idletime: 0x%04x\n", userinfo->idletime);
963
964         if (inforeq == AIM_GETINFO_GENERALINFO) {
965                 dvprintf("faimtest: userinfo: profile_encoding: %s\n", prof_encoding ? prof_encoding : "[none]");
966                 dvprintf("faimtest: userinfo: prof: %s\n", prof ? prof : "[none]");
967         } else if (inforeq == AIM_GETINFO_AWAYMESSAGE) {
968                 dvprintf("faimtest: userinfo: awaymsg_encoding: %s\n", prof_encoding ? prof_encoding : "[none]");
969                 dvprintf("faimtest: userinfo: awaymsg: %s\n", prof ? prof : "[none]");
970         } else 
971                 dprintf("faimtest: userinfo: unknown info request\n");
972
973         return 1;
974 }
975
976 static int faimtest_handlecmd(aim_session_t *sess, aim_conn_t *conn, struct aim_userinfo_s *userinfo, const char *tmpstr)
977 {
978         struct faimtest_priv *priv = (struct faimtest_priv *)sess->aux_data;
979
980         if (!strncmp(tmpstr, "disconnect", 10)) {
981
982                 logout(sess);
983
984         } else if (strstr(tmpstr, "goodday")) {
985
986                 aim_send_im(sess, conn, userinfo->sn, AIM_IMFLAGS_ACK, "Good day to you too.");
987
988         } else if (strstr(tmpstr, "haveicon") && priv->buddyicon) {
989                 struct aim_sendimext_args args;
990                 static const char iconmsg[] = {"I have an icon"};
991
992                 args.destsn = userinfo->sn;
993                 args.flags = AIM_IMFLAGS_HASICON;
994                 args.msg = iconmsg;
995                 args.msglen = strlen(iconmsg);
996                 args.iconlen = priv->buddyiconlen;
997                 args.iconstamp = priv->buddyiconstamp;
998                 args.iconsum = priv->buddyiconsum;
999
1000                 aim_send_im_ext(sess, conn, &args);
1001
1002         } else if (strstr(tmpstr, "sendbin")) {
1003                 struct aim_sendimext_args args;
1004                 static const unsigned char data[] = {
1005                         0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1006                         0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
1007                         0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
1008                         0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
1009                         0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
1010                         0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
1011                 };
1012
1013                 /*
1014                  * I put this here as a demonstration of how to send 
1015                  * arbitrary binary data via OSCAR ICBM's without the need
1016                  * for escape or baseN encoding of any sort.  
1017                  *
1018                  * Apparently if you set the charset to something WinAIM
1019                  * doesn't recognize, it will completly ignore the message.
1020                  * That is, it will not display anything in the conversation
1021                  * window for the user that recieved it.
1022                  *
1023                  * HOWEVER, if they do not have a conversation window open
1024                  * for you, a new one will be created, but it will not have
1025                  * any messages in it.  Therefore sending these things could
1026                  * be a great way to seemingly subliminally convince people
1027                  * to talk to you...
1028                  *
1029                  */
1030                 args.destsn = userinfo->sn;
1031                 args.flags = AIM_IMFLAGS_CUSTOMCHARSET;
1032                 args.charset = args.charsubset = 0x4242;
1033                 args.msg = data;
1034                 args.msglen = sizeof(data);
1035
1036                 aim_send_im_ext(sess, conn, &args);
1037
1038         } else if (strstr(tmpstr, "sendmulti")) {
1039                 struct aim_sendimext_args args;
1040                 aim_mpmsg_t mpm;
1041                 static const fu16_t unidata[] = { /* "UNICODE." */
1042                         0x0055, 0x004e, 0x0049, 0x0043,
1043                         0x004f, 0x0044, 0x0045, 0x002e,
1044                 };
1045                 static const int unidatalen = 8;
1046
1047                 /*
1048                  * This is how multipart messages should be sent.
1049                  *
1050                  * This should render as:
1051                  *        "Part 1, ASCII.  UNICODE.Part 3, ASCII.  "
1052                  */
1053
1054                 aim_mpmsg_init(sess, &mpm);
1055
1056                 aim_mpmsg_addascii(sess, &mpm, "Part 1, ASCII.  ");
1057                 aim_mpmsg_addunicode(sess, &mpm, unidata, unidatalen);
1058                 aim_mpmsg_addascii(sess, &mpm, "Part 3, ASCII.  ");
1059
1060                 args.destsn = userinfo->sn;
1061                 args.flags = AIM_IMFLAGS_MULTIPART;
1062                 args.mpmsg = &mpm;
1063
1064                 aim_send_im_ext(sess, conn, &args);
1065
1066                 aim_mpmsg_free(sess, &mpm);
1067
1068         } else if (strstr(tmpstr, "sendprebin")) {
1069                 static const unsigned char data[] = {
1070                         0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1071                         0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
1072                         0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
1073                         0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
1074                         0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
1075                         0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
1076                 };
1077                 struct aim_sendimext_args args;
1078                 aim_mpmsg_t mpm;
1079
1080                 /*
1081                  * This demonstrates sending a human-readable preamble,
1082                  * and then arbitrary binary data.
1083                  *
1084                  * This means that you can very inconspicuously send binary
1085                  * attachments to other users.  In WinAIM, this appears as
1086                  * though it only had the ASCII portion.
1087                  *
1088                  */
1089
1090                 aim_mpmsg_init(sess, &mpm);
1091
1092                 aim_mpmsg_addascii(sess, &mpm, "This message has binary data.");
1093                 aim_mpmsg_addraw(sess, &mpm, 0x4242, 0x4242, data, sizeof(data));
1094
1095                 args.destsn = userinfo->sn;
1096                 args.flags = AIM_IMFLAGS_MULTIPART;
1097                 args.mpmsg = &mpm;
1098
1099                 aim_send_im_ext(sess, conn, &args);
1100
1101                 aim_mpmsg_free(sess, &mpm);
1102
1103         } else if (strstr(tmpstr, "havefeat")) {
1104                 struct aim_sendimext_args args;
1105                 static const char featmsg[] = {"I have nifty features."};
1106                 fu8_t features[] = {0x01, 0x01, 0x01, 0x02, 0x42, 0x43, 0x44, 0x45};
1107
1108                 args.destsn = userinfo->sn;
1109                 args.flags = AIM_IMFLAGS_CUSTOMFEATURES;
1110                 args.msg = featmsg;
1111                 args.msglen = strlen(featmsg);
1112                 args.features = features;
1113                 args.featureslen = sizeof(features);
1114
1115                 aim_send_im_ext(sess, conn, &args);
1116
1117         } else if (strstr(tmpstr, "sendicon") && priv->buddyicon) {
1118
1119                 aim_send_icon(sess, conn, userinfo->sn, priv->buddyicon, priv->buddyiconlen, priv->buddyiconstamp, priv->buddyiconsum);
1120
1121         } else if (strstr(tmpstr, "warnme")) {
1122
1123                 dprintf("faimtest: icbm: sending non-anon warning\n");
1124                 aim_send_warning(sess, conn, userinfo->sn, 0);
1125
1126         } else if (strstr(tmpstr, "anonwarn")) {
1127
1128                 dprintf("faimtest: icbm: sending anon warning\n");
1129                 aim_send_warning(sess, conn, userinfo->sn, AIM_WARN_ANON);
1130
1131         } else if (strstr(tmpstr, "setdirectoryinfo")) {
1132
1133                 dprintf("faimtest: icbm: sending backwards profile data\n");
1134                 aim_setdirectoryinfo(sess, conn, "tsrif", "elddim", "tsal", "nediam", "emankcin", "teerts", "ytic", "etats", "piz", 0, 1);
1135
1136         } else if (strstr(tmpstr, "setinterests")) {
1137
1138                 dprintf("faimtest: icbm: setting fun interests\n");
1139                 aim_setuserinterests(sess, conn, "interest1", "interest2", "interest3", "interest4", "interest5", 1);
1140
1141         } else if (!strncmp(tmpstr, "getfile", 7)) {
1142
1143                 if (!priv->ohcaptainmycaptain) {
1144
1145                         aim_send_im(sess, conn, userinfo->sn, AIM_IMFLAGS_ACK, "I have no owner!");
1146
1147                 } else 
1148                         getfile_start(sess, conn, (strlen(tmpstr) < 8)?priv->ohcaptainmycaptain:tmpstr+8);
1149                 
1150         } else if (!strncmp(tmpstr, "open chatnav", 12)) {
1151
1152                 aim_bos_reqservice(sess, conn, AIM_CONN_TYPE_CHATNAV);
1153
1154         } else if (!strncmp(tmpstr, "create", 6)) {
1155
1156                 aim_chatnav_createroom(sess,aim_getconn_type(sess, AIM_CONN_TYPE_CHATNAV), (strlen(tmpstr) < 7)?"WorldDomination":tmpstr+7, 0x0004);
1157
1158         } else if (!strncmp(tmpstr, "close chatnav", 13)) {
1159                 aim_conn_t *chatnavconn;
1160
1161                 if ((chatnavconn = aim_getconn_type(sess, AIM_CONN_TYPE_CHATNAV)))
1162                         aim_conn_kill(sess, &chatnavconn);
1163
1164         } else if (!strncmp(tmpstr, "join", 4)) {
1165
1166                 aim_chat_join(sess, conn, 0x0004, "worlddomination", 0x0000);
1167
1168         } else if (!strncmp(tmpstr, "leave", 5)) {
1169
1170                 aim_chat_leaveroom(sess, "worlddomination");
1171
1172         } else if (!strncmp(tmpstr, "getinfo", 7)) {
1173
1174                 aim_getinfo(sess, conn, "75784102", AIM_GETINFO_GENERALINFO);
1175                 aim_getinfo(sess, conn, "15853637", AIM_GETINFO_AWAYMESSAGE);
1176                 aim_getinfo(sess, conn, "midendian", AIM_GETINFO_GENERALINFO);
1177                 aim_getinfo(sess, conn, "midendian", AIM_GETINFO_AWAYMESSAGE);
1178
1179         } else if (strstr(tmpstr, "open directim")) {
1180
1181                 directim_start(sess, conn, userinfo->sn);
1182
1183         } else if(strstr(tmpstr, "lookup")) {
1184
1185                 aim_usersearch_address(sess, conn, "mid@auk.cx");
1186
1187         } else if (!strncmp(tmpstr, "reqsendmsg", 10)) {
1188
1189                 aim_send_im(sess, conn, priv->ohcaptainmycaptain, 0, "sendmsg 7900");
1190
1191         } else if (!strncmp(tmpstr, "reqauth", 7)) {
1192
1193                 aim_bos_reqservice(sess, conn, AIM_CONN_TYPE_AUTH);
1194
1195         } else if (!strncmp(tmpstr, "reqconfirm", 10)) {
1196
1197                 aim_auth_reqconfirm(sess, aim_getconn_type(sess, AIM_CONN_TYPE_AUTH));
1198
1199         } else if (!strncmp(tmpstr, "reqemail", 8)) {
1200
1201                 aim_auth_getinfo(sess, aim_getconn_type(sess, AIM_CONN_TYPE_AUTH), 0x0011);
1202
1203         } else if (!strncmp(tmpstr, "changepass", 8)) {
1204
1205                 aim_auth_changepasswd(sess, aim_getconn_type(sess, AIM_CONN_TYPE_AUTH), "NEWPASSWORD", "OLDPASSWORD");
1206
1207         } else if (!strncmp(tmpstr, "setemail", 8)) {
1208
1209                 aim_auth_setemail(sess, aim_getconn_type(sess, AIM_CONN_TYPE_AUTH), "NEWEMAILADDRESS");
1210
1211         } else if (!strncmp(tmpstr, "sendmsg", 7)) {
1212                 int i;
1213
1214                 i = atoi(tmpstr+8);
1215                 if (i < 10000) {
1216                         char *newbuf;
1217                         int z;
1218
1219                         newbuf = malloc(i+1);
1220                         for (z = 0; z < i; z++)
1221                                 newbuf[z] = (z % 10)+0x30;
1222                         newbuf[i] = '\0';
1223                         aim_send_im(sess, conn, userinfo->sn, 0, newbuf);
1224                         free(newbuf);
1225                 }
1226
1227         } else {
1228
1229                 dprintf("unknown command.\n");
1230                 aim_add_buddy(sess, conn, userinfo->sn);
1231
1232         }  
1233
1234         return 0;
1235 }
1236
1237 /*
1238  * Channel 1: Standard Message
1239  */
1240 static int faimtest_parse_incoming_im_chan1(aim_session_t *sess, aim_conn_t *conn, struct aim_userinfo_s *userinfo, struct aim_incomingim_ch1_args *args)
1241 {
1242         struct faimtest_priv *priv = (struct faimtest_priv *)sess->aux_data;
1243         char *tmpstr;
1244         int clienttype = AIM_CLIENTTYPE_UNKNOWN;
1245         char realmsg[8192+1] = {""};
1246         clienttype = aim_fingerprintclient(args->features, args->featureslen);
1247
1248         dvprintf("faimtest: icbm: sn = \"%s\"\n", userinfo->sn);
1249         dvprintf("faimtest: icbm: probable client type: %d\n", clienttype);
1250         dvprintf("faimtest: icbm: warnlevel = 0x%04x\n", userinfo->warnlevel);
1251         dvprintf("faimtest: icbm: flags = 0x%04x = ", userinfo->flags);
1252         printuserflags(userinfo->flags);
1253         dinlineprintf("\n");
1254
1255         dvprintf("faimtest: icbm: membersince = %lu\n", userinfo->membersince);
1256         dvprintf("faimtest: icbm: onlinesince = %lu\n", userinfo->onlinesince);
1257         dvprintf("faimtest: icbm: idletime = 0x%04x\n", userinfo->idletime);
1258         dvprintf("faimtest: icbm: capabilities = 0x%04x\n", userinfo->capabilities);
1259
1260         dprintf("faimtest: icbm: icbmflags = ");
1261         if (args->icbmflags & AIM_IMFLAGS_AWAY)
1262                 dinlineprintf("away ");
1263         if (args->icbmflags & AIM_IMFLAGS_ACK)
1264                 dinlineprintf("ackrequest ");
1265         if (args->icbmflags & AIM_IMFLAGS_BUDDYREQ)
1266                 dinlineprintf("buddyreq ");
1267         if (args->icbmflags & AIM_IMFLAGS_HASICON)
1268                 dinlineprintf("hasicon ");
1269         dinlineprintf("\n");
1270
1271         if (args->icbmflags & AIM_IMFLAGS_CUSTOMCHARSET)
1272                 dvprintf("faimtest: icbm: encoding flags = {%04x, %04x}\n", args->charset, args->charsubset);
1273
1274         /*
1275          * Quickly convert it to eight bit format, replacing non-ASCII UNICODE 
1276          * characters with their equivelent HTML entity.
1277          */
1278         if (args->icbmflags & AIM_IMFLAGS_UNICODE) {
1279                 int i;
1280
1281                 for (i = 0; i < args->msglen; i += 2) {
1282                         fu16_t uni;
1283
1284                         uni = ((args->msg[i] & 0xff) << 8) | (args->msg[i+1] & 0xff);
1285
1286                         if ((uni < 128) || ((uni >= 160) && (uni <= 255))) { /* ISO 8859-1 */
1287
1288                                 snprintf(realmsg+strlen(realmsg), sizeof(realmsg)-strlen(realmsg), "%c", uni);
1289
1290                         } else { /* something else, do UNICODE entity */
1291
1292                                 snprintf(realmsg+strlen(realmsg), sizeof(realmsg)-strlen(realmsg), "&#%04x;", uni);
1293
1294                         }
1295
1296                 }
1297
1298         } else {
1299
1300                 /*
1301                  * For non-UNICODE encodings (ASCII and ISO 8859-1), there is 
1302                  * no need to do anything special here.  Most 
1303                  * terminals/whatever will be able to display such characters 
1304                  * unmodified.
1305                  *
1306                  * Beware that PC-ASCII 128 through 159 are _not_ actually 
1307                  * defined in ASCII or ISO 8859-1, and you should send them as 
1308                  * UNICODE.  WinAIM will send these characters in a UNICODE 
1309                  * message, so you need to do so as well.
1310                  *
1311                  * You may not think it necessary to handle UNICODE messages.  
1312                  * You're probably wrong.  For one thing, Microsoft "Smart
1313                  * Quotes" will be sent by WinAIM as UNICODE (not HTML UNICODE,
1314                  * but real UNICODE). If you don't parse UNICODE at all, your 
1315                  * users will get a blank message instead of the message 
1316                  * containing Smart Quotes.
1317                  *
1318                  */
1319                 strncpy(realmsg, args->msg, sizeof(realmsg));
1320         }
1321
1322         dvprintf("faimtest: icbm: message: %s\n", realmsg);
1323
1324         if (args->icbmflags & AIM_IMFLAGS_MULTIPART) {
1325                 aim_mpmsg_section_t *sec;
1326                 int z;
1327
1328                 dvprintf("faimtest: icbm: multipart: this message has %d parts\n", args->mpmsg.numparts);
1329
1330                 for (sec = args->mpmsg.parts, z = 0; sec; sec = sec->next, z++) {
1331                         if ((sec->charset == 0x0000) || (sec->charset == 0x0003) || (sec->charset == 0xffff)) {
1332                                 dvprintf("faimtest: icbm: multipart:   part %d: charset 0x%04x, subset 0x%04x, msg = %s\n", z, sec->charset, sec->charsubset, sec->data);
1333                         } else {
1334                                 dvprintf("faimtest: icbm: multipart:   part %d: charset 0x%04x, subset 0x%04x, binary or UNICODE data\n", z, sec->charset, sec->charsubset);
1335                         }
1336                 }
1337         }
1338
1339         if (args->icbmflags & AIM_IMFLAGS_HASICON)
1340                 aim_send_im(sess, conn, userinfo->sn, AIM_IMFLAGS_BUDDYREQ, "You have an icon");
1341
1342         if (realmsg) {
1343                 int i = 0;
1344
1345                 while (realmsg[i] == '<') {
1346                         if (realmsg[i] == '<') {
1347                                 while (realmsg[i] != '>')
1348                                         i++;
1349                                 i++;
1350                         }
1351                 }
1352                 tmpstr = realmsg+i;
1353
1354                 faimtest_handlecmd(sess, conn, userinfo, tmpstr);
1355
1356         }
1357
1358         if (priv->buddyicon && (args->icbmflags & AIM_IMFLAGS_BUDDYREQ))
1359                 aim_send_icon(sess, conn, userinfo->sn, priv->buddyicon, priv->buddyiconlen, priv->buddyiconstamp, priv->buddyiconsum);
1360
1361         return 1;
1362 }
1363
1364 /*
1365  * Channel 2: Rendevous Request
1366  */
1367 static int faimtest_parse_incoming_im_chan2(aim_session_t *sess, aim_conn_t *conn, struct aim_userinfo_s *userinfo, struct aim_incomingim_ch2_args *args)
1368 {
1369
1370         if (args->reqclass == AIM_CAPS_VOICE) {
1371
1372                 dvprintf("faimtest: voice invitation: source sn = %s\n", userinfo->sn);
1373                 dvprintf("faimtest: voice invitation: \twarnlevel = 0x%04x\n", userinfo->warnlevel);
1374                 dvprintf("faimtest: voice invitation: \tclass = 0x%04x = ", userinfo->flags);
1375                 printuserflags(userinfo->flags);
1376                 dinlineprintf("\n");
1377
1378                 dvprintf("faimtest: voice invitation: \tonlinesince = %lu\n", userinfo->onlinesince);
1379                 dvprintf("faimtest: voice invitation: \tidletime = 0x%04x\n", userinfo->idletime);
1380
1381         } else if (args->reqclass == AIM_CAPS_GETFILE) {
1382                 
1383                 getfile_requested(sess, conn, userinfo, args);
1384                 
1385         } else if (args->reqclass == AIM_CAPS_SENDFILE) {
1386
1387                 dprintf("faimtest: send file!\n");
1388
1389         } else if (args->reqclass == AIM_CAPS_CHAT) {
1390
1391                 dvprintf("faimtest: chat invitation: source sn = %s\n", userinfo->sn);
1392                 dvprintf("faimtest: chat invitation: \twarnlevel = 0x%04x\n", userinfo->warnlevel);
1393                 dvprintf("faimtest: chat invitation: \tclass = 0x%04x = ", userinfo->flags);
1394                 printuserflags(userinfo->flags);
1395                 dinlineprintf("\n");
1396
1397                 /* we dont get membersince on chat invites! */
1398                 dvprintf("faimtest: chat invitation: \tonlinesince = %lu\n", userinfo->onlinesince);
1399                 dvprintf("faimtest: chat invitation: \tidletime = 0x%04x\n", userinfo->idletime);
1400
1401                 dvprintf("faimtest: chat invitation: message = %s\n", args->info.chat.msg);
1402                 dvprintf("faimtest: chat invitation: room name = %s\n", args->info.chat.roominfo.name);
1403                 dvprintf("faimtest: chat invitation: encoding = %s\n", args->info.chat.encoding);
1404                 dvprintf("faimtest: chat invitation: language = %s\n", args->info.chat.lang);
1405                 dvprintf("faimtest: chat invitation: exchange = 0x%04x\n", args->info.chat.roominfo.exchange);
1406                 dvprintf("faimtest: chat invitation: instance = 0x%04x\n", args->info.chat.roominfo.instance);
1407                 dvprintf("faimtest: chat invitiation: autojoining %s...\n", args->info.chat.roominfo.name);
1408
1409                 /* Automatically join room... */
1410                 aim_chat_join(sess, conn, args->info.chat.roominfo.exchange, args->info.chat.roominfo.name, args->info.chat.roominfo.instance);
1411
1412         } else if (args->reqclass == AIM_CAPS_IMIMAGE) {
1413
1414                 dprintf("faimtest: icbm: rendezvous imimage\n");
1415
1416                 directim_requested(sess, conn, userinfo, args);
1417
1418         } else if (args->reqclass == AIM_CAPS_BUDDYICON) {
1419
1420                 dvprintf("faimtest: Buddy Icon from %s, length = %lu\n", userinfo->sn, args->info.icon.length);
1421
1422         } else {
1423
1424                 dvprintf("faimtest: icbm: unknown reqclass (%d)\n", args->reqclass);
1425         }
1426
1427         return 1;
1428 }
1429
1430 static int faimtest_parse_incoming_im(aim_session_t *sess, aim_frame_t *fr, ...)
1431 {
1432         fu16_t channel;
1433         struct aim_userinfo_s *userinfo;
1434         va_list ap;
1435         int ret = 0;
1436
1437         va_start(ap, fr);
1438         channel = (fu16_t)va_arg(ap, unsigned int);
1439         userinfo = va_arg(ap, struct aim_userinfo_s *);
1440
1441         if (channel == 1) {
1442                 struct aim_incomingim_ch1_args *args;
1443
1444                 args = va_arg(ap, struct aim_incomingim_ch1_args *);
1445
1446                 ret = faimtest_parse_incoming_im_chan1(sess, fr->conn, userinfo, args);
1447
1448         } else if (channel == 2) {
1449                 struct aim_incomingim_ch2_args *args;
1450
1451                 args = va_arg(ap, struct aim_incomingim_ch2_args *);
1452
1453                 ret = faimtest_parse_incoming_im_chan2(sess, fr->conn, userinfo, args);
1454         } else
1455                 dvprintf("unsupported channel 0x%04x\n", channel);
1456
1457         va_end(ap);
1458
1459         dvprintf("faimtest: icbm: done with ICBM handling (ret = %d)\n", ret);
1460
1461         return 1;
1462 }
1463
1464 static int faimtest_parse_oncoming(aim_session_t *sess, aim_frame_t *fr, ...)
1465 {
1466         struct aim_userinfo_s *userinfo;
1467
1468         va_list ap;
1469         va_start(ap, fr);
1470         userinfo = va_arg(ap, struct aim_userinfo_s *);
1471         va_end(ap);
1472
1473         dvprintf("%ld  %s is now online (flags: %04x = %s%s%s%s%s%s%s%s) (caps = 0x%04x)\n",
1474                         time(NULL),
1475                         userinfo->sn, userinfo->flags,
1476                         (userinfo->flags&AIM_FLAG_UNCONFIRMED)?" UNCONFIRMED":"",
1477                         (userinfo->flags&AIM_FLAG_ADMINISTRATOR)?" ADMINISTRATOR":"",
1478                         (userinfo->flags&AIM_FLAG_AOL)?" AOL":"",
1479                         (userinfo->flags&AIM_FLAG_OSCAR_PAY)?" OSCAR_PAY":"",
1480                         (userinfo->flags&AIM_FLAG_FREE)?" FREE":"",
1481                         (userinfo->flags&AIM_FLAG_AWAY)?" AWAY":"",
1482                         (userinfo->flags&AIM_FLAG_UNKNOWN40)?" UNKNOWN40":"",
1483                         (userinfo->flags&AIM_FLAG_UNKNOWN80)?" UNKNOWN80":"",
1484                         userinfo->capabilities);
1485         return 1;
1486 }
1487
1488 static int faimtest_parse_offgoing(aim_session_t *sess, aim_frame_t *fr, ...)
1489 {
1490         struct aim_userinfo_s *userinfo;
1491         va_list ap;
1492         
1493         va_start(ap, fr);
1494         userinfo = va_arg(ap, struct aim_userinfo_s *);
1495         va_end(ap);
1496
1497         dvprintf("%ld  %s is now offline (flags: %04x = %s%s%s%s%s%s%s%s) (caps = 0x%04x)\n",
1498                          time(NULL),
1499                          userinfo->sn, userinfo->flags,
1500                          (userinfo->flags&AIM_FLAG_UNCONFIRMED)?" UNCONFIRMED":"",
1501                          (userinfo->flags&AIM_FLAG_ADMINISTRATOR)?" ADMINISTRATOR":"",
1502                          (userinfo->flags&AIM_FLAG_AOL)?" AOL":"",
1503                          (userinfo->flags&AIM_FLAG_OSCAR_PAY)?" OSCAR_PAY":"",
1504                          (userinfo->flags&AIM_FLAG_FREE)?" FREE":"",
1505                          (userinfo->flags&AIM_FLAG_AWAY)?" AWAY":"",
1506                          (userinfo->flags&AIM_FLAG_UNKNOWN40)?" UNKNOWN40":"",
1507                          (userinfo->flags&AIM_FLAG_UNKNOWN80)?" UNKNOWN80":"",
1508                          userinfo->capabilities);
1509
1510         return 1;
1511 }
1512
1513 static int faimtest_parse_genericerr(aim_session_t *sess, aim_frame_t *fr, ...)
1514 {
1515         va_list ap;
1516         fu16_t reason;
1517
1518         va_start(ap, fr);
1519         reason = (fu16_t)va_arg(ap, unsigned int);
1520         va_end(ap);
1521
1522         dvprintf("faimtest: snac threw error (reason 0x%04x: %s)\n", reason, (reason<msgerrreasonslen)?msgerrreasons[reason]:"unknown");
1523
1524         return 1;
1525 }
1526
1527 static int faimtest_parse_msgerr(aim_session_t *sess, aim_frame_t *fr, ...)
1528 {
1529         va_list ap;
1530         char *destsn;
1531         fu16_t reason;
1532
1533         va_start(ap, fr);
1534         reason = (fu16_t)va_arg(ap, unsigned int);
1535         destsn = va_arg(ap, char *);
1536         va_end(ap);
1537
1538         dvprintf("faimtest: message to %s bounced (reason 0x%04x: %s)\n", destsn, reason, (reason<msgerrreasonslen)?msgerrreasons[reason]:"unknown");
1539
1540         return 1;
1541 }
1542
1543 static int faimtest_parse_locerr(aim_session_t *sess, aim_frame_t *fr, ...)
1544 {
1545         va_list ap;
1546         char *destsn;
1547         fu16_t reason;
1548
1549         va_start(ap, fr);
1550         reason = (fu16_t)va_arg(ap, unsigned int);
1551         destsn = va_arg(ap, char *);
1552         va_end(ap);
1553
1554         dvprintf("faimtest: user information for %s unavailable (reason 0x%04x: %s)\n", destsn, reason, (reason<msgerrreasonslen)?msgerrreasons[reason]:"unknown");
1555
1556         return 1;
1557 }
1558
1559 static int faimtest_parse_misses(aim_session_t *sess, aim_frame_t *fr, ...)
1560 {
1561         static char *missedreasons[] = {
1562                 "Invalid (0)",
1563                 "Message too large",
1564                 "Rate exceeded",
1565                 "Evil Sender",
1566                 "Evil Receiver"
1567         };
1568         static int missedreasonslen = 5;
1569
1570         va_list ap;
1571         fu16_t chan, nummissed, reason;
1572         struct aim_userinfo_s *userinfo;
1573
1574         va_start(ap, fr);
1575         chan = (fu16_t)va_arg(ap, unsigned int);
1576         userinfo = va_arg(ap, struct aim_userinfo_s *);
1577         nummissed = (fu16_t)va_arg(ap, unsigned int);
1578         reason = (fu16_t)va_arg(ap, unsigned int);
1579         va_end(ap);
1580
1581         dvprintf("faimtest: missed %d messages from %s on channel %d (reason %d: %s)\n", nummissed, userinfo->sn, chan, reason, (reason<missedreasonslen)?missedreasons[reason]:"unknown");
1582
1583         return 1;
1584 }
1585
1586 /*
1587  * Received in response to an IM sent with the AIM_IMFLAGS_ACK option.
1588  */
1589 static int faimtest_parse_msgack(aim_session_t *sess, aim_frame_t *fr, ...)
1590 {
1591         va_list ap;
1592         fu16_t type;
1593         char *sn = NULL;
1594
1595         va_start(ap, fr);
1596         type = (fu16_t)va_arg(ap, unsigned int);
1597         sn = va_arg(ap, char *);
1598         va_end(ap);
1599
1600         dvprintf("faimtest: msgack: 0x%04x / %s\n", type, sn);
1601
1602         return 1;
1603 }
1604
1605 static int faimtest_parse_ratechange(aim_session_t *sess, aim_frame_t *fr, ...)
1606 {
1607         static char *codes[5] = {
1608                 "invalid",
1609                 "change",
1610                 "warning",
1611                 "limit",
1612                 "limit cleared"
1613         };
1614         va_list ap;
1615         fu16_t code, rateclass;
1616         fu32_t windowsize, clear, alert, limit, disconnect;
1617         fu32_t currentavg, maxavg;
1618
1619         va_start(ap, fr); 
1620
1621         /* See code explanations below */
1622         code = (fu16_t)va_arg(ap, unsigned int);
1623
1624         /*
1625          * See comments above aim_parse_ratechange_middle() in aim_rxhandlers.c.
1626          */
1627         rateclass = (fu16_t)va_arg(ap, unsigned int);
1628
1629         /*
1630          * Not sure what this is exactly.  I think its the temporal 
1631          * relation factor (ie, how to make the rest of the numbers
1632          * make sense in the real world). 
1633          */
1634         windowsize = va_arg(ap, fu32_t);
1635
1636         /* Explained below */
1637         clear = va_arg(ap, fu32_t);
1638         alert = va_arg(ap, fu32_t);
1639         limit = va_arg(ap, fu32_t);
1640         disconnect = va_arg(ap, fu32_t);
1641         currentavg = va_arg(ap, fu32_t);
1642         maxavg = va_arg(ap, fu32_t);
1643
1644         va_end(ap);
1645
1646
1647         dvprintf("faimtest: rate %s (rate class 0x%04x): curavg = %ld, maxavg = %ld, alert at %ld, clear warning at %ld, limit at %ld, disconnect at %ld (window size = %ld)\n",
1648                                 (code < 5)?codes[code]:"invalid",
1649                                 rateclass,
1650                                 currentavg, maxavg,
1651                                 alert, clear,
1652                                 limit, disconnect,
1653                                 windowsize);
1654
1655         if (code == AIM_RATE_CODE_CHANGE) {
1656                 /*
1657                  * Not real sure when these get sent.
1658                  */
1659                 if (currentavg >= clear)
1660                         aim_conn_setlatency(fr->conn, 0);
1661
1662         } else if (code == AIM_RATE_CODE_WARNING) {
1663                 /*
1664                  * We start getting WARNINGs the first time we go below the 
1665                  * 'alert' limit (currentavg < alert) and they stop when 
1666                  * either we pause long enough for currentavg to go above 
1667                  * 'clear', or until we flood it bad enough to go below 
1668                  * 'limit' (and start getting LIMITs instead) or even further 
1669                  * and go below 'disconnect' and get disconnected completely 
1670                  * (and won't be able to login right away either).
1671                  */
1672                 aim_conn_setlatency(fr->conn, windowsize/4); /* XXX this is bogus! */ 
1673
1674         } else if (code == AIM_RATE_CODE_LIMIT) {
1675                 /*
1676                  * When we hit LIMIT, messages will start getting dropped.
1677                  */
1678                 aim_conn_setlatency(fr->conn, windowsize/2); /* XXX this is bogus! */ 
1679
1680         } else if (code == AIM_RATE_CODE_CLEARLIMIT) {
1681                 /*
1682                  * The limit is cleared when curavg goes above 'clear'.
1683                  */
1684                 aim_conn_setlatency(fr->conn, 0); 
1685         }
1686
1687         return 1;
1688 }
1689
1690 static int faimtest_parse_evilnotify(aim_session_t *sess, aim_frame_t *fr, ...)
1691 {
1692         va_list ap;
1693         fu16_t newevil;
1694         struct aim_userinfo_s *userinfo;
1695
1696         va_start(ap, fr);
1697         newevil = (fu16_t)va_arg(ap, unsigned int);
1698         userinfo = va_arg(ap, struct aim_userinfo_s *);
1699         va_end(ap);
1700
1701         /*
1702          * Evil Notifications that are lacking userinfo->sn are anon-warns
1703          * if they are an evil increases, but are not warnings at all if its
1704          * a decrease (its the natural backoff happening).
1705          *
1706          * newevil is passed as an int representing the new evil value times
1707          * ten.
1708          */
1709         dvprintf("faimtest: evil level change: new value = %2.1f%% (caused by %s)\n", ((float)newevil)/10, (userinfo && strlen(userinfo->sn))?userinfo->sn:"anonymous");
1710
1711         return 1;
1712 }
1713
1714 static int faimtest_parse_searchreply(aim_session_t *sess, aim_frame_t *fr, ...)
1715 {
1716         va_list ap;
1717         char *address, *SNs;
1718         int i, num;
1719
1720         va_start(ap, fr);
1721         address = va_arg(ap, char *);
1722         num = va_arg(ap, int);
1723         SNs = va_arg(ap, char *);
1724         va_end(ap);
1725
1726         dvprintf("faimtest: E-Mail Search Results for %s: ", address);
1727
1728         for(i = 0; i < num; i++)
1729                 dvinlineprintf("%s, ", &SNs[i*(MAXSNLEN+1)]);
1730         dinlineprintf("\n");
1731
1732         return 1;
1733 }
1734
1735 static int faimtest_parse_searcherror(aim_session_t *sess, aim_frame_t *fr, ...)
1736 {
1737         va_list ap;
1738         char *address;
1739
1740         va_start(ap, fr);
1741         address = va_arg(ap, char *);
1742         va_end(ap);
1743
1744         dvprintf("faimtest: E-Mail Search Results for %s: No Results or Invalid Email\n", address);
1745
1746         return 1;
1747 }
1748
1749 static int serverpause(aim_session_t *sess, aim_frame_t *fr, ...)
1750 {
1751
1752         aim_sendpauseack(sess, fr->conn);
1753
1754         return 1;
1755 }
1756
1757 static int migrate(aim_session_t *sess, aim_frame_t *fr, ...)
1758 {
1759         va_list ap;
1760         aim_conn_t *bosconn;
1761         char *bosip;
1762         fu8_t *cookie;
1763
1764         va_start(ap, fr);
1765         bosip = va_arg(ap, char *);
1766         cookie = va_arg(ap, fu8_t *);
1767         va_end(ap);
1768
1769         dvprintf("migration in progress -- new BOS is %s -- disconnecting\n", bosip);
1770         aim_conn_kill(sess, &fr->conn);
1771
1772         if (!(bosconn = aim_newconn(sess, AIM_CONN_TYPE_BOS, bosip))) {
1773                 dprintf("migrate: could not connect to BOS: internal error\n");
1774                 return 1;
1775         } else if (bosconn->status & AIM_CONN_STATUS_CONNERR) { 
1776                 dprintf("migrate: could not connect to BOS\n");
1777                 aim_conn_kill(sess, &bosconn);
1778                 return 1;
1779         }
1780
1781         /* Login will happen all over again. */
1782         addcb_bos(sess, bosconn);
1783
1784         aim_auth_sendcookie(sess, bosconn, cookie);
1785
1786         return 1;
1787 }
1788
1789 void addcb_bos(aim_session_t *sess, aim_conn_t *bosconn)
1790 {
1791
1792         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE, faimtest_conncomplete, 0);
1793
1794         aim_conn_addhandler(sess, bosconn, 0x0009, 0x0003, faimtest_bosrights, 0);
1795         aim_conn_addhandler(sess, bosconn, 0x0001, 0x0007, faimtest_rateresp_bos, 0); /* rate info */
1796         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, 0x0018, faimtest_hostversions, 0);
1797         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_SERVERREADY, faimtest_serverready, 0);
1798         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_RATEINFO, NULL, 0);
1799         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_REDIRECT, faimtest_handleredirect, 0);
1800         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_STS, AIM_CB_STS_SETREPORTINTERVAL, faimtest_reportinterval, 0);
1801         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_RIGHTSINFO, faimtest_parse_buddyrights, 0);
1802         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_MOTD, faimtest_parse_motd, 0);
1803         aim_conn_addhandler(sess, bosconn, 0x0004, 0x0005, faimtest_icbmparaminfo, 0);
1804         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, faimtest_parse_connerr, 0);
1805         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_RIGHTSINFO, faimtest_locrights, 0);
1806         aim_conn_addhandler(sess, bosconn, 0x0001, 0x001f, faimtest_memrequest, 0);
1807         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_ONCOMING, faimtest_parse_oncoming, 0);
1808         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_OFFGOING, faimtest_parse_offgoing, 0);
1809         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_INCOMING, faimtest_parse_incoming_im, 0);
1810         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_ERROR, faimtest_parse_locerr, 0);
1811         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MISSEDCALL, faimtest_parse_misses, 0);
1812         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_RATECHANGE, faimtest_parse_ratechange, 0);
1813         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_EVIL, faimtest_parse_evilnotify, 0);
1814         aim_conn_addhandler(sess, bosconn, 0x000a, 0x0001, faimtest_parse_searcherror, 0);
1815         aim_conn_addhandler(sess, bosconn, 0x000a, 0x0003, faimtest_parse_searchreply, 0);
1816         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ERROR, faimtest_parse_msgerr, 0);
1817         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO, faimtest_parse_userinfo, 0);
1818         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ACK, faimtest_parse_msgack, 0);
1819
1820         aim_conn_addhandler(sess, bosconn, 0x0001, 0x0001, faimtest_parse_genericerr, 0);
1821         aim_conn_addhandler(sess, bosconn, 0x0003, 0x0001, faimtest_parse_genericerr, 0);
1822         aim_conn_addhandler(sess, bosconn, 0x0009, 0x0001, faimtest_parse_genericerr, 0);
1823         aim_conn_addhandler(sess, bosconn, 0x0001, 0x000b, serverpause, 0);
1824         aim_conn_addhandler(sess, bosconn, 0x0001, 0x0012, migrate, 0);
1825         
1826 #ifdef MID_REWROTE_ALL_THE_CRAP
1827         aim_conn_addhandler(sess, bosconn, 0xffff, 0xffff, faimtest_parse_unknown, 0);
1828 #endif
1829
1830         return;
1831 }
1832
This page took 0.19464 seconds and 5 git commands to generate.