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