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