]> andersk Git - libfaim.git/blob - utils/faimtest/faimtest.c
377295c597c60950c770edad7a5c90920aabbc34
[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 0
310                                         if (aim_handlerendconnect(&aimsess, waitingconn) < 0) {
311                                                 dprintf("connection error (rend out)\n");
312                                                 aim_conn_kill(&aimsess, &waitingconn);
313                                         }
314 #endif
315                                 } else {
316                                         if (aim_get_command(&aimsess, waitingconn) >= 0) {
317                                                 aim_rxdispatch(&aimsess);
318                                         } else {
319                                                 dvprintf("connection error (type 0x%04x:0x%04x)\n", waitingconn->type, waitingconn->subtype);
320                                                 /* we should have callbacks for all these, else the library will do the conn_kill for us. */
321                                                 if (waitingconn->type == AIM_CONN_TYPE_RENDEZVOUS) {
322                                                         dprintf("connection error: rendezvous connection. you forgot register a disconnect callback, right?\n");          
323                                                         aim_conn_kill(&aimsess, &waitingconn);
324                                                 } else
325                                                         aim_conn_kill(&aimsess, &waitingconn);
326                                                 if (!aim_getconn_type(&aimsess, AIM_CONN_TYPE_BOS)) {
327                                                         dprintf("major connection error\n");
328                                                         if (faimtest_mode == 2)
329                                                                 break;
330                                                 }
331                                         }
332                                 }
333                         }
334                 }
335         }
336
337         /* close up all connections, dead or no */
338         aim_session_kill(&aimsess); 
339
340         if (faimtest_mode < 2) {
341                 printf("\n");
342                 cmd_uninit();
343         }
344
345         free(priv.buddyicon);
346
347         /* Get out */
348         exit(0);
349 }
350
351 int faimtest_serverready(aim_session_t *sess, aim_frame_t *fr, ...)
352 {
353         int famcount, i;
354         fu16_t *families;
355         va_list ap;
356
357         va_start(ap, fr);
358         famcount = va_arg(ap, int);
359         families = va_arg(ap, fu16_t *);
360         va_end(ap);
361
362         dvprintf("faimtest: SNAC families supported by this host (type %d): ", fr->conn->type);
363         for (i = 0; i < famcount; i++)
364                 dvinlineprintf("0x%04x ", families[i]);
365         dinlineprintf("\n");
366
367         if (fr->conn->type == AIM_CONN_TYPE_AUTH) {
368
369                 aim_auth_setversions(sess, fr->conn);
370                 aim_bos_reqrate(sess, fr->conn); /* request rate info */
371
372                 dprintf("done with auth server ready\n");
373
374         } else if (fr->conn->type == AIM_CONN_TYPE_BOS) {
375
376                 aim_setversions(sess, fr->conn);
377                 aim_bos_reqrate(sess, fr->conn); /* request rate info */
378
379                 dprintf("done with BOS server ready\n");
380         }
381
382         return 1;
383 }
384
385 int faimtest_parse_connerr(aim_session_t *sess, aim_frame_t *fr, ...)
386 {
387         struct faimtest_priv *priv = (struct faimtest_priv *)sess->aux_data;
388         va_list ap;
389         fu16_t code;
390         char *msg;
391
392         va_start(ap, fr);
393         code = va_arg(ap, int);
394         msg = va_arg(ap, char *);
395         va_end(ap);
396
397         dvprintf("connerr: Code 0x%04x: %s\n", code, msg);
398         aim_conn_kill(sess, &fr->conn); /* this will break the main loop */
399
400         priv->connected = 0;
401
402         return 1;
403 }
404
405 #if 0
406 static int faimtest_rateresp_auth(aim_session_t *sess, aim_frame_t *fr, ...)
407 {
408
409         aim_bos_ackrateresp(sess, fr->conn);
410         aim_auth_clientready(sess, fr->conn);
411
412         dprintf("faimtest: connected to authorization/admin service\n");
413
414         return 1;
415 }
416
417 int faimtest_accountconfirm(aim_session_t *sess, aim_frame_t *fr, ...)
418 {
419         int status;
420         va_list ap;
421
422         va_start(ap, fr);
423         status = va_arg(ap, int); /* status code of confirmation request */
424         va_end(ap);
425
426         dvprintf("account confirmation returned status 0x%04x (%s)\n", status, (status==0x0000)?"email sent":"unknown");
427
428         return 1;
429 }
430
431
432 #endif
433
434 #if 0
435 /* 
436  * This kind of function is really not legal in the new bstream way...
437  * In fact, clients should never access the aim_frame_t directly in handlers,
438  * since that may leave it in a bizare state for the lower layers.  In fact,
439  * clients should probably not even get passed a pointer like this.
440  *
441  */
442 int faimtest_parse_unknown(aim_session_t *sess, aim_frame_t *fr, ...)
443 {
444         int i;
445
446         aim_bstream_rewind(&fr->data); /* boo! */
447
448         dprintf("\nReceived unknown packet:");
449         for (i = 0; aim_bstream_empty(&fr->data); i++) {
450                 if ((i % 8) == 0)
451                         dinlineprintf("\n\t");
452                 dvinlineprintf("0x%2x ", aimbs_get8(&fr->data));
453         }
454         dinlineprintf("\n\n");
455
456         return 1;
457 }
458 #endif
459
460 int faimtest_handleredirect(aim_session_t *sess, aim_frame_t *fr, ...)
461 {
462         va_list ap;
463         int serviceid;
464         char *ip;
465         fu8_t *cookie;
466
467         va_start(ap, fr);
468         serviceid = va_arg(ap, int);
469         ip = va_arg(ap, char *);
470         cookie = va_arg(ap, fu8_t *);
471
472         if (serviceid == 0x0005) {  /* Adverts */
473 #if 0
474                 aim_conn_t *tstconn;
475
476                 tstconn = aim_newconn(sess, AIM_CONN_TYPE_ADS, ip);
477                 if (!tstconn || (tstconn->status & AIM_CONN_STATUS_RESOLVERR)) {
478                         dprintf("faimtest: unable to reconnect with authorizer\n");
479                 } else {
480                         aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, faimtest_flapversion, 0);
481                         aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE, faimtest_conncomplete, 0);
482                         aim_conn_addhandler(sess, tstconn, 0x0001, 0x0003, faimtest_serverready, 0);
483                         aim_conn_addhandler(sess, tstconn, 0x0001, 0x0007, faimtest_rateresp, 0); /* rate info */
484                         aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_GEN, 0x0018, faimtest_hostversions, 0);
485                         aim_auth_sendcookie(sess, tstconn, cookie);
486                         dprintf("sent cookie to adverts host\n");
487                 }
488 #endif
489         } else if (serviceid == 0x0007) {  /* Authorizer */
490 #if 0
491                 aim_conn_t *tstconn;
492                 
493                 tstconn = aim_newconn(sess, AIM_CONN_TYPE_AUTH, 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_conn_addhandler(sess, tstconn, 0x0007, 0x0007, faimtest_accountconfirm, 0);
503                         aim_conn_addhandler(sess, tstconn, 0x0007, 0x0003, faimtest_infochange, 0);
504                         aim_conn_addhandler(sess, tstconn, 0x0007, 0x0005, faimtest_infochange, 0);
505                         /* Send the cookie to the Auth */
506                         aim_auth_sendcookie(sess, tstconn, cookie);
507                         dprintf("sent cookie to authorizer host\n");
508                 }
509 #endif
510         } else if (serviceid == 0x000d) {  /* ChatNav */
511
512                 chatnav_redirect(sess, ip, cookie);
513                 
514         } else if (serviceid == 0x000e) { /* Chat */
515                 char *roomname = NULL;
516                 int exchange;
517
518                 roomname = va_arg(ap, char *);
519                 exchange = va_arg(ap, int);
520
521                 chat_redirect(sess, ip, cookie, roomname, exchange);
522
523         } else {
524                 dvprintf("uh oh... got redirect for unknown service 0x%04x!!\n", serviceid);
525         }
526
527         va_end(ap);
528
529         return 1;
530 }
531
532 static int faimtest_rateresp_bos(aim_session_t *sess, aim_frame_t *fr, ...)
533 {
534         struct faimtest_priv *priv = (struct faimtest_priv *)sess->aux_data;
535         char buddies[128]; /* this is the new buddy list */
536         char profile[256]; /* this is the new profile */ 
537         char awaymsg[] = {"blah blah blah Ole! blah blah blah"};
538
539         /* Caution: Buddy1 and Buddy2 are real people! (who I don't know) */
540         snprintf(buddies, sizeof(buddies), "Buddy1&Buddy2&%s&", priv->ohcaptainmycaptain ? priv->ohcaptainmycaptain : "blah");
541         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);
542
543         aim_bos_ackrateresp(sess, fr->conn);  /* ack rate info response */
544         aim_bos_reqpersonalinfo(sess, fr->conn);
545         aim_bos_reqlocaterights(sess, fr->conn);
546         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*/);
547         aim_bos_reqbuddyrights(sess, fr->conn);
548
549         /* send the buddy list and profile (required, even if empty) */
550         aim_bos_setbuddylist(sess, fr->conn, buddies);
551
552         aim_reqicbmparams(sess, fr->conn);  
553
554         aim_bos_reqrights(sess, fr->conn);  
555         /* set group permissions -- all user classes */
556         aim_bos_setgroupperm(sess, fr->conn, AIM_FLAG_ALLUSERS);
557         aim_bos_setprivacyflags(sess, fr->conn, AIM_PRIVFLAGS_ALLOWIDLE);
558
559         return 1;
560 }
561
562 static int faimtest_icbmparaminfo(aim_session_t *sess, aim_frame_t *fr, ...)
563 {
564         struct aim_icbmparameters *params;
565         va_list ap;
566
567         va_start(ap, fr);
568         params = va_arg(ap, struct aim_icbmparameters *);
569         va_end(ap);
570
571         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);
572
573         /*
574          * Set these to your taste, or client medium.  Setting minmsginterval
575          * higher is good for keeping yourself from getting flooded (esp
576          * if you're on a slow connection or something where that would be
577          * useful).
578          */
579         params->maxmsglen = 8000;
580         params->minmsginterval = 0; /* in milliseconds */
581
582         aim_seticbmparam(sess, fr->conn, params);
583
584         return 1;
585 }
586
587 static int faimtest_hostversions(aim_session_t *sess, aim_frame_t *fr, ...)
588 {
589         int vercount, i;
590         fu8_t *versions;
591         va_list ap;
592
593         va_start(ap, fr);
594         vercount = va_arg(ap, int); /* number of family/version pairs */
595         versions = va_arg(ap, fu8_t *);
596         va_end(ap);
597
598         dprintf("faimtest: SNAC versions supported by this host: ");
599         for (i = 0; i < vercount*4; i += 4) {
600                 dvinlineprintf("0x%04x:0x%04x ", 
601                         aimutil_get16(versions+i),  /* SNAC family */
602                         aimutil_get16(versions+i+2) /* Version number */);
603         }
604         dinlineprintf("\n");
605
606         return 1;
607 }
608
609 static int faimtest_parse_buddyrights(aim_session_t *sess, aim_frame_t *fr, ...)
610 {
611         va_list ap;
612         fu16_t maxbuddies, maxwatchers;
613
614         va_start(ap, fr);
615         maxbuddies = va_arg(ap, int);
616         maxwatchers = va_arg(ap, int);
617         va_end(ap);
618
619         dvprintf("buddy list rights: Max buddies = %d / Max watchers = %d\n", maxbuddies, maxwatchers);
620
621         return 1;
622 }
623
624 static int faimtest_bosrights(aim_session_t *sess, aim_frame_t *fr, ...)
625 {
626         va_list ap;
627         fu16_t maxpermits, maxdenies;
628
629         va_start(ap, fr);
630         maxpermits = va_arg(ap, int);
631         maxdenies = va_arg(ap, int);
632         va_end(ap);
633
634         dvprintf("BOS rights: Max permit = %d / Max deny = %d\n", maxpermits, maxdenies);
635
636         aim_bos_clientready(sess, fr->conn);
637
638         dprintf("officially connected to BOS.\n");
639
640         return 1;
641 }
642
643 static int faimtest_locrights(aim_session_t *sess, aim_frame_t *fr, ...)
644 {
645         va_list ap;
646         fu16_t maxsiglen;
647
648         va_start(ap, fr);
649         maxsiglen = va_arg(ap, int);
650         va_end(ap);
651
652         dvprintf("locate rights: max signature length = %d\n", maxsiglen);
653
654         return 1;
655 }
656
657 static int faimtest_reportinterval(aim_session_t *sess, aim_frame_t *fr, ...)
658 {
659         struct faimtest_priv *priv = (struct faimtest_priv *)sess->aux_data;
660         va_list ap;
661         fu16_t interval;
662
663         va_start(ap, fr);
664         interval = va_arg(ap, int);
665         va_end(ap);
666
667         dvprintf("minimum report interval: %d (seconds?)\n", interval);
668
669         if (!priv->connected)
670                 priv->connected++;
671
672 #if 0
673         aim_bos_reqservice(sess, fr->conn, 0x0005); /* adverts */
674         aim_bos_reqservice(sess, fr->conn, 0x000f); /* user directory */
675
676         /* Don't know what this does... */
677         /* XXX sess->sn should be normalized by the 0001/000f handler */
678         aim_0002_000b(sess, fr->conn, sess->sn);
679 #endif
680
681         aim_reqicbmparams(sess, fr->conn);
682
683         return 1;
684 }
685
686 static int faimtest_parse_motd(aim_session_t *sess, aim_frame_t *fr, ...)
687 {
688         static char *codes[] = {
689                 "Unknown",
690                 "Mandatory upgrade",
691                 "Advisory upgrade",
692                 "System bulletin",
693                 "Top o' the world!"
694         };
695         static int codeslen = 5;
696         char *msg;
697         fu16_t id;
698         va_list ap;
699
700         va_start(ap, fr);
701         id = va_arg(ap, int);
702         msg = va_arg(ap, char *);
703         va_end(ap);
704
705         dvprintf("motd: %s (%d / %s)\n", msg, id, (id < codeslen)?codes[id]:"unknown");
706
707         return 1;
708 }
709
710 /*
711  * This is a little more complicated than it looks.  The module
712  * name (proto, boscore, etc) may or may not be given.  If it is
713  * not given, then use aim.exe.  If it is given, put ".ocm" on the
714  * end of it.
715  *
716  * Now, if the offset or length requested would cause a read past
717  * the end of the file, then the request is considered invalid.  Invalid
718  * requests are processed specially.  The value hashed is the
719  * the request, put into little-endian (eight bytes: offset followed
720  * by length).  
721  *
722  * Additionally, if the request is valid, the length is mod 4096.  It is
723  * important that the length is checked for validity first before doing
724  * the mod.
725  *
726  * Note to Bosco's Brigade: if you'd like to break this, put the 
727  * module name on an invalid request.
728  *
729  */
730 static int getaimdata(aim_session_t *sess, unsigned char **bufret, int *buflenret, unsigned long offset, unsigned long len, const char *modname)
731 {
732         struct faimtest_priv *priv = (struct faimtest_priv *)sess->aux_data;
733         FILE *f;
734         static const char defaultmod[] = "aim.exe";
735         char *filename = NULL;
736         struct stat st;
737         unsigned char *buf;
738         int invalid = 0;
739
740         if (!bufret || !buflenret)
741                 return -1;
742
743         if (modname) {
744
745                 if (!(filename = malloc(strlen(priv->aimbinarypath)+1+strlen(modname)+4+1))) {
746                         dperror("memrequest: malloc");
747                         return -1;
748                 }
749
750                 sprintf(filename, "%s/%s.ocm", priv->aimbinarypath, modname);
751
752         } else {
753
754                 if (!(filename = malloc(strlen(priv->aimbinarypath)+1+strlen(defaultmod)+1))) {
755                         dperror("memrequest: malloc");
756                         return -1;
757                 }
758
759                 sprintf(filename, "%s/%s", priv->aimbinarypath, defaultmod);
760
761         }
762
763         if (stat(filename, &st) == -1) {
764                 if (!modname) {
765                         dperror("memrequest: stat");
766                         free(filename);
767                         return -1;
768                 }
769                 invalid = 1;
770         }
771
772         if (!invalid) {
773                 if ((offset > st.st_size) || (len > st.st_size))
774                         invalid = 1;
775                 else if ((st.st_size - offset) < len)
776                         len = st.st_size - offset;
777                 else if ((st.st_size - len) < len)
778                         len = st.st_size - len;
779         }
780
781         if (!invalid && len)
782                 len %= 4096;
783
784         if (invalid) {
785                 int i;
786
787                 free(filename); /* not needed */
788
789                 dvprintf("memrequest: recieved invalid request for 0x%08lx bytes at 0x%08lx (file %s)\n", len, offset, modname);
790
791                 i = 8;
792                 if (modname)
793                         i += strlen(modname);
794
795                 if (!(buf = malloc(i)))
796                         return -1;
797
798                 i = 0;
799
800                 if (modname) {
801                         memcpy(buf, modname, strlen(modname));
802                         i += strlen(modname);
803                 }
804
805                 /* Damn endianness. This must be little (LSB first) endian. */
806                 buf[i++] = offset & 0xff;
807                 buf[i++] = (offset >> 8) & 0xff;
808                 buf[i++] = (offset >> 16) & 0xff;
809                 buf[i++] = (offset >> 24) & 0xff;
810                 buf[i++] = len & 0xff;
811                 buf[i++] = (len >> 8) & 0xff;
812                 buf[i++] = (len >> 16) & 0xff;
813                 buf[i++] = (len >> 24) & 0xff;
814
815                 *bufret = buf;
816                 *buflenret = i;
817
818         } else {
819
820                 if (!(buf = malloc(len))) {
821                         free(filename);
822                         return -1;
823                 }
824
825                 dvprintf("memrequest: loading %ld bytes from 0x%08lx in \"%s\"...\n", len, offset, filename);
826
827                 if (!(f = fopen(filename, "r"))) {
828                         dperror("memrequest: fopen");
829                         free(filename);
830                         free(buf);
831                         return -1;
832                 }
833
834                 free(filename);
835
836                 if (fseek(f, offset, SEEK_SET) == -1) {
837                         dperror("memrequest: fseek");
838                         fclose(f);
839                         free(buf);
840                         return -1;
841                 }
842
843                 if (fread(buf, len, 1, f) != 1) {
844                         dperror("memrequest: fread");
845                         fclose(f);
846                         free(buf);
847                         return -1;
848                 }
849
850                 fclose(f);
851
852                 *bufret = buf;
853                 *buflenret = len;
854
855         }
856
857         return 0; /* success! */
858 }
859
860 /*
861  * This will get an offset and a length.  The client should read this
862  * data out of whatever AIM.EXE binary the user has provided (hopefully
863  * it matches the client information thats sent at login) and pass a
864  * buffer back to libfaim so it can hash the data and send it to AOL for
865  * inspection by the client police.
866  */
867 static int faimtest_memrequest(aim_session_t *sess, aim_frame_t *fr, ...)
868 {
869         struct faimtest_priv *priv = (struct faimtest_priv *)sess->aux_data;
870         va_list ap;
871         fu32_t offset, len;
872         char *modname;
873         unsigned char *buf;
874         int buflen;
875
876         va_start(ap, fr);
877         offset = va_arg(ap, fu32_t);
878         len = va_arg(ap, fu32_t);
879         modname = va_arg(ap, char *);
880         va_end(ap);
881
882         if (priv->aimbinarypath && (getaimdata(sess, &buf, &buflen, offset, len, modname) == 0)) {
883
884                 aim_sendmemblock(sess, fr->conn, offset, buflen, buf, AIM_SENDMEMBLOCK_FLAG_ISREQUEST);
885
886                 free(buf);
887
888         } else {
889
890                 dvprintf("memrequest: unable to use AIM binary (\"%s/%s\"), sending defaults...\n", priv->aimbinarypath, modname);
891
892                 aim_sendmemblock(sess, fr->conn, offset, len, NULL, AIM_SENDMEMBLOCK_FLAG_ISREQUEST);
893
894         }
895
896         return 1;
897 }
898
899 static void printuserflags(fu16_t flags)
900 {
901         if (flags & AIM_FLAG_UNCONFIRMED)
902                 dinlineprintf("UNCONFIRMED ");
903         if (flags & AIM_FLAG_ADMINISTRATOR)
904                 dinlineprintf("ADMINISTRATOR ");
905         if (flags & AIM_FLAG_AOL)
906                 dinlineprintf("AOL ");
907         if (flags & AIM_FLAG_OSCAR_PAY)
908                 dinlineprintf("OSCAR_PAY ");
909         if (flags & AIM_FLAG_FREE)
910                 dinlineprintf("FREE ");
911         if (flags & AIM_FLAG_AWAY)
912                 dinlineprintf("AWAY ");
913         if (flags & AIM_FLAG_UNKNOWN40)
914                 dinlineprintf("ICQ? ");
915         if (flags & AIM_FLAG_UNKNOWN80)
916                 dinlineprintf("UNKNOWN80 ");
917         return;
918 }
919
920 static int faimtest_parse_userinfo(aim_session_t *sess, aim_frame_t *fr, ...)
921 {
922         struct aim_userinfo_s *userinfo;
923         char *prof_encoding = NULL;
924         char *prof = NULL;
925         fu16_t inforeq = 0;
926
927         va_list ap;
928         va_start(ap, fr);
929         userinfo = va_arg(ap, struct aim_userinfo_s *);
930         prof_encoding = va_arg(ap, char *);
931         prof = va_arg(ap, char *);
932         inforeq = va_arg(ap, fu16_t);
933         va_end(ap);
934
935         dvprintf("faimtest: userinfo: sn: %s\n", userinfo->sn);
936         dvprintf("faimtest: userinfo: warnlevel: 0x%04x\n", userinfo->warnlevel);
937         dvprintf("faimtest: userinfo: flags: 0x%04x = ", userinfo->flags);
938         printuserflags(userinfo->flags);
939         dinlineprintf("\n");
940
941         dvprintf("faimtest: userinfo: membersince: %lu\n", userinfo->membersince);
942         dvprintf("faimtest: userinfo: onlinesince: %lu\n", userinfo->onlinesince);
943         dvprintf("faimtest: userinfo: idletime: 0x%04x\n", userinfo->idletime);
944
945         if (inforeq == AIM_GETINFO_GENERALINFO) {
946                 dvprintf("faimtest: userinfo: profile_encoding: %s\n", prof_encoding ? prof_encoding : "[none]");
947                 dvprintf("faimtest: userinfo: prof: %s\n", prof ? prof : "[none]");
948         } else if (inforeq == AIM_GETINFO_AWAYMESSAGE) {
949                 dvprintf("faimtest: userinfo: awaymsg_encoding: %s\n", prof_encoding ? prof_encoding : "[none]");
950                 dvprintf("faimtest: userinfo: awaymsg: %s\n", prof ? prof : "[none]");
951         } else 
952                 dprintf("faimtest: userinfo: unknown info request\n");
953
954         return 1;
955 }
956
957 static int faimtest_handlecmd(aim_session_t *sess, aim_conn_t *conn, struct aim_userinfo_s *userinfo, const char *tmpstr)
958 {
959         struct faimtest_priv *priv = (struct faimtest_priv *)sess->aux_data;
960
961         if (!strncmp(tmpstr, "disconnect", 10)) {
962
963                 logout(sess);
964
965         } else if (strstr(tmpstr, "goodday")) {
966
967                 aim_send_im(sess, conn, userinfo->sn, AIM_IMFLAGS_ACK, "Good day to you too.");
968
969         } else if (strstr(tmpstr, "haveicon") && priv->buddyicon) {
970                 struct aim_sendimext_args args;
971                 static const char iconmsg[] = {"I have an icon"};
972
973                 args.destsn = userinfo->sn;
974                 args.flags = AIM_IMFLAGS_HASICON;
975                 args.msg = iconmsg;
976                 args.msglen = strlen(iconmsg);
977                 args.iconlen = priv->buddyiconlen;
978                 args.iconstamp = priv->buddyiconstamp;
979                 args.iconsum = priv->buddyiconsum;
980
981                 aim_send_im_ext(sess, conn, &args);
982
983         } else if (strstr(tmpstr, "havefeat")) {
984                 struct aim_sendimext_args args;
985                 static const char featmsg[] = {"I have nifty features."};
986                 fu8_t features[] = {0x01, 0x01, 0x01, 0x02, 0x42, 0x43, 0x44, 0x45};
987
988                 args.destsn = userinfo->sn;
989                 args.flags = AIM_IMFLAGS_CUSTOMFEATURES;
990                 args.msg = featmsg;
991                 args.msglen = strlen(featmsg);
992                 args.features = features;
993                 args.featureslen = sizeof(features);
994
995                 aim_send_im_ext(sess, conn, &args);
996
997         } else if (strstr(tmpstr, "sendicon") && priv->buddyicon) {
998
999                 aim_send_icon(sess, conn, userinfo->sn, priv->buddyicon, priv->buddyiconlen, priv->buddyiconstamp, priv->buddyiconsum);
1000
1001         } else if (strstr(tmpstr, "warnme")) {
1002
1003                 dprintf("faimtest: icbm: sending non-anon warning\n");
1004                 aim_send_warning(sess, conn, userinfo->sn, 0);
1005
1006         } else if (strstr(tmpstr, "anonwarn")) {
1007
1008                 dprintf("faimtest: icbm: sending anon warning\n");
1009                 aim_send_warning(sess, conn, userinfo->sn, AIM_WARN_ANON);
1010
1011         } else if (strstr(tmpstr, "setdirectoryinfo")) {
1012
1013                 dprintf("faimtest: icbm: sending backwards profile data\n");
1014                 aim_setdirectoryinfo(sess, conn, "tsrif", "elddim", "tsal", "nediam", "emankcin", "teerts", "ytic", "etats", "piz", 0, 1);
1015
1016         } else if (strstr(tmpstr, "setinterests")) {
1017
1018                 dprintf("faimtest: icbm: setting fun interests\n");
1019                 aim_setuserinterests(sess, conn, "interest1", "interest2", "interest3", "interest4", "interest5", 1);
1020
1021         } else if (!strncmp(tmpstr, "getfile", 7)) {
1022
1023                 if (!priv->ohcaptainmycaptain) {
1024
1025                         aim_send_im(sess, conn, userinfo->sn, AIM_IMFLAGS_ACK, "I have no owner!");
1026
1027                 } else 
1028                         getfile_start(sess, conn, (strlen(tmpstr) < 8)?priv->ohcaptainmycaptain:tmpstr+8);
1029                 
1030         } else if (!strncmp(tmpstr, "open chatnav", 12)) {
1031
1032                 aim_bos_reqservice(sess, conn, AIM_CONN_TYPE_CHATNAV);
1033
1034         } else if (!strncmp(tmpstr, "create", 6)) {
1035
1036                 aim_chatnav_createroom(sess,aim_getconn_type(sess, AIM_CONN_TYPE_CHATNAV), (strlen(tmpstr) < 7)?"WorldDomination":tmpstr+7, 0x0004);
1037
1038         } else if (!strncmp(tmpstr, "close chatnav", 13)) {
1039                 aim_conn_t *chatnavconn;
1040
1041                 if ((chatnavconn = aim_getconn_type(sess, AIM_CONN_TYPE_CHATNAV)))
1042                         aim_conn_kill(sess, &chatnavconn);
1043
1044         } else if (!strncmp(tmpstr, "join", 4)) {
1045
1046                 aim_chat_join(sess, conn, 0x0004, "worlddomination", 0x0000);
1047
1048         } else if (!strncmp(tmpstr, "leave", 5)) {
1049
1050                 aim_chat_leaveroom(sess, "worlddomination");
1051
1052         } else if (!strncmp(tmpstr, "getinfo", 7)) {
1053
1054                 aim_getinfo(sess, conn, "75784102", AIM_GETINFO_GENERALINFO);
1055                 aim_getinfo(sess, conn, "15853637", AIM_GETINFO_AWAYMESSAGE);
1056                 aim_getinfo(sess, conn, "midendian", AIM_GETINFO_GENERALINFO);
1057                 aim_getinfo(sess, conn, "midendian", AIM_GETINFO_AWAYMESSAGE);
1058
1059         } else if (!strncmp(tmpstr, "open directim", 13)) {
1060
1061                 directim_start(sess, conn, (strlen(tmpstr) < 14)?userinfo->sn:tmpstr+14);
1062
1063         } else if(!(strncmp(tmpstr, "lookup", 6))) {
1064
1065                 aim_usersearch_address(sess, conn, tmpstr+7);
1066
1067         } else if (!strncmp(tmpstr, "reqsendmsg", 10)) {
1068
1069                 aim_send_im(sess, conn, priv->ohcaptainmycaptain, 0, "sendmsg 7900");
1070
1071         } else if (!strncmp(tmpstr, "reqauth", 7)) {
1072
1073                 aim_bos_reqservice(sess, conn, AIM_CONN_TYPE_AUTH);
1074
1075         } else if (!strncmp(tmpstr, "reqconfirm", 10)) {
1076
1077                 aim_auth_reqconfirm(sess, aim_getconn_type(sess, AIM_CONN_TYPE_AUTH));
1078
1079         } else if (!strncmp(tmpstr, "reqemail", 8)) {
1080
1081                 aim_auth_getinfo(sess, aim_getconn_type(sess, AIM_CONN_TYPE_AUTH), 0x0011);
1082
1083         } else if (!strncmp(tmpstr, "changepass", 8)) {
1084
1085                 aim_auth_changepasswd(sess, aim_getconn_type(sess, AIM_CONN_TYPE_AUTH), "NEWPASSWORD", "OLDPASSWORD");
1086
1087         } else if (!strncmp(tmpstr, "setemail", 8)) {
1088
1089                 aim_auth_setemail(sess, aim_getconn_type(sess, AIM_CONN_TYPE_AUTH), "NEWEMAILADDRESS");
1090
1091         } else if (!strncmp(tmpstr, "sendmsg", 7)) {
1092                 int i;
1093
1094                 i = atoi(tmpstr+8);
1095                 if (i < 10000) {
1096                         char *newbuf;
1097                         int z;
1098
1099                         newbuf = malloc(i+1);
1100                         for (z = 0; z < i; z++)
1101                                 newbuf[z] = (z % 10)+0x30;
1102                         newbuf[i] = '\0';
1103                         aim_send_im(sess, conn, userinfo->sn, 0, newbuf);
1104                         free(newbuf);
1105                 }
1106
1107         } else {
1108
1109                 dprintf("unknown command.\n");
1110                 aim_add_buddy(sess, conn, userinfo->sn);
1111
1112         }  
1113
1114         return 0;
1115 }
1116
1117 /*
1118  * Channel 1: Standard Message
1119  */
1120 static int faimtest_parse_incoming_im_chan1(aim_session_t *sess, aim_conn_t *conn, struct aim_userinfo_s *userinfo, va_list ap)
1121 {
1122         struct faimtest_priv *priv = (struct faimtest_priv *)sess->aux_data;
1123         char *tmpstr;
1124         struct aim_incomingim_ch1_args *args;
1125         int clienttype = AIM_CLIENTTYPE_UNKNOWN;
1126         char realmsg[8192+1] = {""};
1127
1128         args = va_arg(ap, struct aim_incomingim_ch1_args *);
1129         va_end(ap);
1130
1131         clienttype = aim_fingerprintclient(args->features, args->featureslen);
1132
1133         dvprintf("faimtest: icbm: sn = \"%s\"\n", userinfo->sn);
1134         dvprintf("faimtest: icbm: probable client type: %d\n", clienttype);
1135         dvprintf("faimtest: icbm: warnlevel = 0x%04x\n", userinfo->warnlevel);
1136         dvprintf("faimtest: icbm: flags = 0x%04x = ", userinfo->flags);
1137         printuserflags(userinfo->flags);
1138         dinlineprintf("\n");
1139
1140         dvprintf("faimtest: icbm: membersince = %lu\n", userinfo->membersince);
1141         dvprintf("faimtest: icbm: onlinesince = %lu\n", userinfo->onlinesince);
1142         dvprintf("faimtest: icbm: idletime = 0x%04x\n", userinfo->idletime);
1143         dvprintf("faimtest: icbm: capabilities = 0x%04x\n", userinfo->capabilities);
1144
1145         dprintf("faimtest: icbm: icbmflags = ");
1146         if (args->icbmflags & AIM_IMFLAGS_AWAY)
1147                 dinlineprintf("away ");
1148         if (args->icbmflags & AIM_IMFLAGS_ACK)
1149                 dinlineprintf("ackrequest ");
1150         if (args->icbmflags & AIM_IMFLAGS_BUDDYREQ)
1151                 dinlineprintf("buddyreq ");
1152         if (args->icbmflags & AIM_IMFLAGS_HASICON)
1153                 dinlineprintf("hasicon ");
1154         dinlineprintf("\n");
1155
1156         dvprintf("faimtest: icbm: encoding flags = {%04x, %04x}\n", args->flag1, args->flag2);
1157
1158         /*
1159          * Quickly convert it to eight bit format, replacing non-ASCII UNICODE 
1160          * characters with their equivelent HTML entity.
1161          */
1162         if (args->icbmflags & AIM_IMFLAGS_UNICODE) {
1163                 int i;
1164
1165                 for (i = 0; i < args->msglen; i += 2) {
1166                         fu16_t uni;
1167
1168                         uni = ((args->msg[i] & 0xff) << 8) | (args->msg[i+1] & 0xff);
1169
1170                         if ((uni < 128) || ((uni >= 160) && (uni <= 255))) { /* ISO 8859-1 */
1171
1172                                 snprintf(realmsg+strlen(realmsg), sizeof(realmsg)-strlen(realmsg), "%c", uni);
1173
1174                         } else { /* something else, do UNICODE entity */
1175
1176                                 snprintf(realmsg+strlen(realmsg), sizeof(realmsg)-strlen(realmsg), "&#%04x;", uni);
1177
1178                         }
1179
1180                 }
1181
1182         } else {
1183
1184                 /*
1185                  * For non-UNICODE encodings (ASCII and ISO 8859-1), there is 
1186                  * no need to do anything special here.  Most 
1187                  * terminals/whatever will be able to display such characters 
1188                  * unmodified.
1189                  *
1190                  * Beware that PC-ASCII 128 through 159 are _not_ actually 
1191                  * defined in ASCII or ISO 8859-1, and you should send them as 
1192                  * UNICODE.  WinAIM will send these characters in a UNICODE 
1193                  * message, so you need to do so as well.
1194                  *
1195                  * You may not think it necessary to handle UNICODE messages.  
1196                  * You're probably wrong.  For one thing, Microsoft "Smart
1197                  * Quotes" will be sent by WinAIM as UNICODE (not HTML UNICODE,
1198                  * but real UNICODE). If you don't parse UNICODE at all, your 
1199                  * users will get a blank message instead of the message 
1200                  * containing Smart Quotes.
1201                  *
1202                  */
1203                 strncpy(realmsg, args->msg, sizeof(realmsg));
1204         }
1205
1206         dvprintf("faimtest: icbm: message: %s\n", realmsg);
1207
1208         if (args->icbmflags & AIM_IMFLAGS_HASICON)
1209                 aim_send_im(sess, conn, userinfo->sn, AIM_IMFLAGS_BUDDYREQ, "You have an icon");
1210
1211         if (realmsg) {
1212                 int i = 0;
1213
1214                 while (realmsg[i] == '<') {
1215                         if (realmsg[i] == '<') {
1216                                 while (realmsg[i] != '>')
1217                                         i++;
1218                                 i++;
1219                         }
1220                 }
1221                 tmpstr = realmsg+i;
1222
1223                 faimtest_handlecmd(sess, conn, userinfo, tmpstr);
1224
1225         }
1226
1227         if (priv->buddyicon && (args->icbmflags & AIM_IMFLAGS_BUDDYREQ))
1228                 aim_send_icon(sess, conn, userinfo->sn, priv->buddyicon, priv->buddyiconlen, priv->buddyiconstamp, priv->buddyiconsum);
1229
1230         return 1;
1231 }
1232
1233 /*
1234  * Channel 2: Rendevous Request
1235  */
1236 static int faimtest_parse_incoming_im_chan2(aim_session_t *sess, aim_conn_t *conn, struct aim_userinfo_s *userinfo, va_list ap)
1237 {
1238         struct aim_incomingim_ch2_args *args;
1239
1240         args = va_arg(ap, struct aim_incomingim_ch2_args *);
1241         va_end(ap);
1242
1243         if (args->reqclass == AIM_CAPS_VOICE) {
1244
1245                 dvprintf("faimtest: voice invitation: source sn = %s\n", userinfo->sn);
1246                 dvprintf("faimtest: voice invitation: \twarnlevel = 0x%04x\n", userinfo->warnlevel);
1247                 dvprintf("faimtest: voice invitation: \tclass = 0x%04x = ", userinfo->flags);
1248                 printuserflags(userinfo->flags);
1249                 dinlineprintf("\n");
1250
1251                 dvprintf("faimtest: voice invitation: \tonlinesince = %lu\n", userinfo->onlinesince);
1252                 dvprintf("faimtest: voice invitation: \tidletime = 0x%04x\n", userinfo->idletime);
1253
1254         } else if (args->reqclass == AIM_CAPS_GETFILE) {
1255                 
1256                 getfile_requested(sess, conn, userinfo, args);
1257                 
1258         } else if (args->reqclass == AIM_CAPS_SENDFILE) {
1259
1260                 dprintf("faimtest: send file!\n");
1261
1262         } else if (args->reqclass == AIM_CAPS_CHAT) {
1263
1264                 dvprintf("faimtest: chat invitation: source sn = %s\n", userinfo->sn);
1265                 dvprintf("faimtest: chat invitation: \twarnlevel = 0x%04x\n", userinfo->warnlevel);
1266                 dvprintf("faimtest: chat invitation: \tclass = 0x%04x = ", userinfo->flags);
1267                 printuserflags(userinfo->flags);
1268                 dinlineprintf("\n");
1269
1270                 /* we dont get membersince on chat invites! */
1271                 dvprintf("faimtest: chat invitation: \tonlinesince = %lu\n", userinfo->onlinesince);
1272                 dvprintf("faimtest: chat invitation: \tidletime = 0x%04x\n", userinfo->idletime);
1273
1274                 dvprintf("faimtest: chat invitation: message = %s\n", args->info.chat.msg);
1275                 dvprintf("faimtest: chat invitation: room name = %s\n", args->info.chat.roominfo.name);
1276                 dvprintf("faimtest: chat invitation: encoding = %s\n", args->info.chat.encoding);
1277                 dvprintf("faimtest: chat invitation: language = %s\n", args->info.chat.lang);
1278                 dvprintf("faimtest: chat invitation: exchange = 0x%04x\n", args->info.chat.roominfo.exchange);
1279                 dvprintf("faimtest: chat invitation: instance = 0x%04x\n", args->info.chat.roominfo.instance);
1280                 dvprintf("faimtest: chat invitiation: autojoining %s...\n", args->info.chat.roominfo.name);
1281
1282                 /* Automatically join room... */
1283                 aim_chat_join(sess, conn, args->info.chat.roominfo.exchange, args->info.chat.roominfo.name, args->info.chat.roominfo.instance);
1284
1285         } else if (args->reqclass == AIM_CAPS_IMIMAGE) {
1286
1287                 dprintf("faimtest: icbm: rendezvous imimage\n");
1288
1289                 directim_requested(sess, conn, userinfo, args);
1290
1291         } else if (args->reqclass == AIM_CAPS_BUDDYICON) {
1292
1293                 dvprintf("faimtest: Buddy Icon from %s, length = %lu\n", userinfo->sn, args->info.icon.length);
1294
1295         } else {
1296
1297                 dvprintf("faimtest: icbm: unknown reqclass (%d)\n", args->reqclass);
1298         }
1299
1300         return 1;
1301 }
1302
1303 static int faimtest_parse_incoming_im(aim_session_t *sess, aim_frame_t *fr, ...)
1304 {
1305         fu16_t channel;
1306         struct aim_userinfo_s *userinfo;
1307         va_list ap;
1308         int ret = 0;
1309
1310         va_start(ap, fr);
1311         channel = va_arg(ap, fu16_t);
1312         userinfo = va_arg(ap, struct aim_userinfo_s *);
1313
1314         if (channel == 1)
1315                 ret = faimtest_parse_incoming_im_chan1(sess, fr->conn, userinfo, ap);
1316         else if (channel == 2)
1317                 ret = faimtest_parse_incoming_im_chan2(sess, fr->conn, userinfo, ap);
1318         else
1319                 dvprintf("unsupported channel 0x%04x\n", channel);
1320
1321         dvprintf("faimtest: icbm: done with ICBM handling (ret = %d)\n", ret);
1322
1323         return 1;
1324 }
1325
1326 #ifdef MID_REWROTE_ALL_THE_CRAP
1327 static int faimtest_infochange(aim_session_t *sess, aim_frame_t *fr, ...)
1328 {
1329         fu16_t change = 0, perms, type;
1330         int length, str;
1331         char *val;
1332         va_list ap;
1333
1334         va_start(ap, fr);
1335         perms = va_arg(ap, fu16_t);
1336         type = va_arg(ap, fu16_t);
1337         length = va_arg(ap, int);
1338         val = va_arg(ap, char *);
1339         str = va_arg(ap, int);
1340         va_end(ap);
1341
1342         if (aimutil_get16(command->data+2) == 0x0005)
1343                 change = 1;
1344
1345         dvprintf("info%s: perms = %d, type = %x, length = %d, val = %s\n", change?" change":"", perms, type, length, str?val:"(not string)");
1346
1347         return 1;
1348 }
1349 #endif
1350
1351 static int faimtest_parse_oncoming(aim_session_t *sess, aim_frame_t *fr, ...)
1352 {
1353         struct aim_userinfo_s *userinfo;
1354
1355         va_list ap;
1356         va_start(ap, fr);
1357         userinfo = va_arg(ap, struct aim_userinfo_s *);
1358         va_end(ap);
1359
1360         dvprintf("%ld  %s is now online (flags: %04x = %s%s%s%s%s%s%s%s) (caps = 0x%04x)\n",
1361                         time(NULL),
1362                         userinfo->sn, userinfo->flags,
1363                         (userinfo->flags&AIM_FLAG_UNCONFIRMED)?" UNCONFIRMED":"",
1364                         (userinfo->flags&AIM_FLAG_ADMINISTRATOR)?" ADMINISTRATOR":"",
1365                         (userinfo->flags&AIM_FLAG_AOL)?" AOL":"",
1366                         (userinfo->flags&AIM_FLAG_OSCAR_PAY)?" OSCAR_PAY":"",
1367                         (userinfo->flags&AIM_FLAG_FREE)?" FREE":"",
1368                         (userinfo->flags&AIM_FLAG_AWAY)?" AWAY":"",
1369                         (userinfo->flags&AIM_FLAG_UNKNOWN40)?" UNKNOWN40":"",
1370                         (userinfo->flags&AIM_FLAG_UNKNOWN80)?" UNKNOWN80":"",
1371                         userinfo->capabilities);
1372         return 1;
1373 }
1374
1375 static int faimtest_parse_offgoing(aim_session_t *sess, aim_frame_t *fr, ...)
1376 {
1377         struct aim_userinfo_s *userinfo;
1378         va_list ap;
1379         
1380         va_start(ap, fr);
1381         userinfo = va_arg(ap, struct aim_userinfo_s *);
1382         va_end(ap);
1383
1384         dvprintf("%ld  %s is now offline (flags: %04x = %s%s%s%s%s%s%s%s) (caps = 0x%04x)\n",
1385                          time(NULL),
1386                          userinfo->sn, userinfo->flags,
1387                          (userinfo->flags&AIM_FLAG_UNCONFIRMED)?" UNCONFIRMED":"",
1388                          (userinfo->flags&AIM_FLAG_ADMINISTRATOR)?" ADMINISTRATOR":"",
1389                          (userinfo->flags&AIM_FLAG_AOL)?" AOL":"",
1390                          (userinfo->flags&AIM_FLAG_OSCAR_PAY)?" OSCAR_PAY":"",
1391                          (userinfo->flags&AIM_FLAG_FREE)?" FREE":"",
1392                          (userinfo->flags&AIM_FLAG_AWAY)?" AWAY":"",
1393                          (userinfo->flags&AIM_FLAG_UNKNOWN40)?" UNKNOWN40":"",
1394                          (userinfo->flags&AIM_FLAG_UNKNOWN80)?" UNKNOWN80":"",
1395                          userinfo->capabilities);
1396
1397         return 1;
1398 }
1399
1400 static int faimtest_parse_genericerr(aim_session_t *sess, aim_frame_t *fr, ...)
1401 {
1402         va_list ap;
1403         fu16_t reason;
1404
1405         va_start(ap, fr);
1406         reason = va_arg(ap, fu16_t);
1407         va_end(ap);
1408
1409         dvprintf("faimtest: snac threw error (reason 0x%04x: %s)\n", reason, (reason<msgerrreasonslen)?msgerrreasons[reason]:"unknown");
1410
1411         return 1;
1412 }
1413
1414 static int faimtest_parse_msgerr(aim_session_t *sess, aim_frame_t *fr, ...)
1415 {
1416         va_list ap;
1417         char *destsn;
1418         fu16_t reason;
1419
1420         va_start(ap, fr);
1421         reason = va_arg(ap, fu16_t);
1422         destsn = va_arg(ap, char *);
1423         va_end(ap);
1424
1425         dvprintf("faimtest: message to %s bounced (reason 0x%04x: %s)\n", destsn, reason, (reason<msgerrreasonslen)?msgerrreasons[reason]:"unknown");
1426
1427         return 1;
1428 }
1429
1430 static int faimtest_parse_locerr(aim_session_t *sess, aim_frame_t *fr, ...)
1431 {
1432         va_list ap;
1433         char *destsn;
1434         fu16_t reason;
1435
1436         va_start(ap, fr);
1437         reason = va_arg(ap, fu16_t);
1438         destsn = va_arg(ap, char *);
1439         va_end(ap);
1440
1441         dvprintf("faimtest: user information for %s unavailable (reason 0x%04x: %s)\n", destsn, reason, (reason<msgerrreasonslen)?msgerrreasons[reason]:"unknown");
1442
1443         return 1;
1444 }
1445
1446 static int faimtest_parse_misses(aim_session_t *sess, aim_frame_t *fr, ...)
1447 {
1448         static char *missedreasons[] = {
1449                 "Invalid (0)",
1450                 "Message too large",
1451                 "Rate exceeded",
1452                 "Evil Sender",
1453                 "Evil Receiver"
1454         };
1455         static int missedreasonslen = 5;
1456
1457         va_list ap;
1458         fu16_t chan, nummissed, reason;
1459         struct aim_userinfo_s *userinfo;
1460
1461         va_start(ap, fr);
1462         chan = va_arg(ap, fu16_t);
1463         userinfo = va_arg(ap, struct aim_userinfo_s *);
1464         nummissed = va_arg(ap, fu16_t);
1465         reason = va_arg(ap, fu16_t);
1466         va_end(ap);
1467
1468         dvprintf("faimtest: missed %d messages from %s on channel %d (reason %d: %s)\n", nummissed, userinfo->sn, chan, reason, (reason<missedreasonslen)?missedreasons[reason]:"unknown");
1469
1470         return 1;
1471 }
1472
1473 /*
1474  * Received in response to an IM sent with the AIM_IMFLAGS_ACK option.
1475  */
1476 static int faimtest_parse_msgack(aim_session_t *sess, aim_frame_t *fr, ...)
1477 {
1478         va_list ap;
1479         fu16_t type;
1480         char *sn = NULL;
1481
1482         va_start(ap, fr);
1483         type = va_arg(ap, fu16_t);
1484         sn = va_arg(ap, char *);
1485         va_end(ap);
1486
1487         dvprintf("faimtest: msgack: 0x%04x / %s\n", type, sn);
1488
1489         return 1;
1490 }
1491
1492 static int faimtest_parse_ratechange(aim_session_t *sess, aim_frame_t *fr, ...)
1493 {
1494         static char *codes[5] = {
1495                 "invalid",
1496                 "change",
1497                 "warning",
1498                 "limit",
1499                 "limit cleared"
1500         };
1501         va_list ap;
1502         fu16_t code, rateclass;
1503         fu32_t windowsize, clear, alert, limit, disconnect;
1504         fu32_t currentavg, maxavg;
1505
1506         va_start(ap, fr); 
1507
1508         /* See code explanations below */
1509         code = va_arg(ap, fu16_t);
1510
1511         /*
1512          * See comments above aim_parse_ratechange_middle() in aim_rxhandlers.c.
1513          */
1514         rateclass = va_arg(ap, fu16_t);
1515
1516         /*
1517          * Not sure what this is exactly.  I think its the temporal 
1518          * relation factor (ie, how to make the rest of the numbers
1519          * make sense in the real world). 
1520          */
1521         windowsize = va_arg(ap, fu32_t);
1522
1523         /* Explained below */
1524         clear = va_arg(ap, fu32_t);
1525         alert = va_arg(ap, fu32_t);
1526         limit = va_arg(ap, fu32_t);
1527         disconnect = va_arg(ap, fu32_t);
1528         currentavg = va_arg(ap, fu32_t);
1529         maxavg = va_arg(ap, fu32_t);
1530
1531         va_end(ap);
1532
1533
1534         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",
1535                                 (code < 5)?codes[code]:"invalid",
1536                                 rateclass,
1537                                 currentavg, maxavg,
1538                                 alert, clear,
1539                                 limit, disconnect,
1540                                 windowsize);
1541
1542         if (code == AIM_RATE_CODE_CHANGE) {
1543                 /*
1544                  * Not real sure when these get sent.
1545                  */
1546                 if (currentavg >= clear)
1547                         aim_conn_setlatency(fr->conn, 0);
1548
1549         } else if (code == AIM_RATE_CODE_WARNING) {
1550                 /*
1551                  * We start getting WARNINGs the first time we go below the 
1552                  * 'alert' limit (currentavg < alert) and they stop when 
1553                  * either we pause long enough for currentavg to go above 
1554                  * 'clear', or until we flood it bad enough to go below 
1555                  * 'limit' (and start getting LIMITs instead) or even further 
1556                  * and go below 'disconnect' and get disconnected completely 
1557                  * (and won't be able to login right away either).
1558                  */
1559                 aim_conn_setlatency(fr->conn, windowsize/4); /* XXX this is bogus! */ 
1560
1561         } else if (code == AIM_RATE_CODE_LIMIT) {
1562                 /*
1563                  * When we hit LIMIT, messages will start getting dropped.
1564                  */
1565                 aim_conn_setlatency(fr->conn, windowsize/2); /* XXX this is bogus! */ 
1566
1567         } else if (code == AIM_RATE_CODE_CLEARLIMIT) {
1568                 /*
1569                  * The limit is cleared when curavg goes above 'clear'.
1570                  */
1571                 aim_conn_setlatency(fr->conn, 0); 
1572         }
1573
1574         return 1;
1575 }
1576
1577 static int faimtest_parse_evilnotify(aim_session_t *sess, aim_frame_t *fr, ...)
1578 {
1579         va_list ap;
1580         fu16_t newevil;
1581         struct aim_userinfo_s *userinfo;
1582
1583         va_start(ap, fr);
1584         newevil = va_arg(ap, fu16_t);
1585         userinfo = va_arg(ap, struct aim_userinfo_s *);
1586         va_end(ap);
1587
1588         /*
1589          * Evil Notifications that are lacking userinfo->sn are anon-warns
1590          * if they are an evil increases, but are not warnings at all if its
1591          * a decrease (its the natural backoff happening).
1592          *
1593          * newevil is passed as an int representing the new evil value times
1594          * ten.
1595          */
1596         dvprintf("faimtest: evil level change: new value = %2.1f%% (caused by %s)\n", ((float)newevil)/10, (userinfo && strlen(userinfo->sn))?userinfo->sn:"anonymous");
1597
1598         return 1;
1599 }
1600
1601 static int faimtest_parse_searchreply(aim_session_t *sess, aim_frame_t *fr, ...)
1602 {
1603         va_list ap;
1604         char *address, *SNs;
1605         int i, num;
1606
1607         va_start(ap, fr);
1608         address = va_arg(ap, char *);
1609         num = va_arg(ap, int);
1610         SNs = va_arg(ap, char *);
1611         va_end(ap);
1612
1613         dvprintf("faimtest: E-Mail Search Results for %s: ", address);
1614
1615         for(i = 0; i < num; i++)
1616                 dvinlineprintf("%s, ", &SNs[i*(MAXSNLEN+1)]);
1617         dinlineprintf("\n");
1618
1619         return 1;
1620 }
1621
1622 static int faimtest_parse_searcherror(aim_session_t *sess, aim_frame_t *fr, ...)
1623 {
1624         va_list ap;
1625         char *address;
1626
1627         va_start(ap, fr);
1628         address = va_arg(ap, char *);
1629         va_end(ap);
1630
1631         dvprintf("faimtest: E-Mail Search Results for %s: No Results or Invalid Email\n", address);
1632
1633         return 1;
1634 }
1635
1636 void addcb_bos(aim_session_t *sess, aim_conn_t *bosconn)
1637 {
1638
1639         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE, faimtest_conncomplete, 0);
1640
1641         aim_conn_addhandler(sess, bosconn, 0x0009, 0x0003, faimtest_bosrights, 0);
1642         aim_conn_addhandler(sess, bosconn, 0x0001, 0x0007, faimtest_rateresp_bos, 0); /* rate info */
1643         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, 0x0018, faimtest_hostversions, 0);
1644         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_SERVERREADY, faimtest_serverready, 0);
1645         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_RATEINFO, NULL, 0);
1646         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_REDIRECT, faimtest_handleredirect, 0);
1647         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_STS, AIM_CB_STS_SETREPORTINTERVAL, faimtest_reportinterval, 0);
1648         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_RIGHTSINFO, faimtest_parse_buddyrights, 0);
1649         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_MOTD, faimtest_parse_motd, 0);
1650         aim_conn_addhandler(sess, bosconn, 0x0004, 0x0005, faimtest_icbmparaminfo, 0);
1651         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, faimtest_parse_connerr, 0);
1652         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_RIGHTSINFO, faimtest_locrights, 0);
1653         aim_conn_addhandler(sess, bosconn, 0x0001, 0x001f, faimtest_memrequest, 0);
1654         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_ONCOMING, faimtest_parse_oncoming, 0);
1655         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_OFFGOING, faimtest_parse_offgoing, 0);
1656         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_INCOMING, faimtest_parse_incoming_im, 0);
1657         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_ERROR, faimtest_parse_locerr, 0);
1658         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MISSEDCALL, faimtest_parse_misses, 0);
1659         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_RATECHANGE, faimtest_parse_ratechange, 0);
1660         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_EVIL, faimtest_parse_evilnotify, 0);
1661         aim_conn_addhandler(sess, bosconn, 0x000a, 0x0001, faimtest_parse_searcherror, 0);
1662         aim_conn_addhandler(sess, bosconn, 0x000a, 0x0003, faimtest_parse_searchreply, 0);
1663         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ERROR, faimtest_parse_msgerr, 0);
1664         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO, faimtest_parse_userinfo, 0);
1665         aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ACK, faimtest_parse_msgack, 0);
1666
1667         aim_conn_addhandler(sess, bosconn, 0x0001, 0x0001, faimtest_parse_genericerr, 0);
1668         aim_conn_addhandler(sess, bosconn, 0x0003, 0x0001, faimtest_parse_genericerr, 0);
1669         aim_conn_addhandler(sess, bosconn, 0x0009, 0x0001, faimtest_parse_genericerr, 0);
1670         
1671 #ifdef MID_REWROTE_ALL_THE_CRAP
1672         aim_conn_addhandler(sess, bosconn, 0xffff, 0xffff, faimtest_parse_unknown, 0);
1673 #endif
1674
1675         return;
1676 }
1677
This page took 0.163206 seconds and 3 git commands to generate.