]> andersk Git - libfaim.git/blame_incremental - utils/faimtest/faimtest.c
- Sat Sep 8 07:32:27 PDT 2001
[libfaim.git] / utils / faimtest / faimtest.c
... / ...
CommitLineData
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
27char *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
41static 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};
68static int msgerrreasonslen = 25;
69
70aim_session_t aimsess;
71int 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 */
80static 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
88int 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 */
117int 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 */
139static 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 */
152int 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
166int 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
348int 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
382int 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
403static 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
414int 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 */
439int 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
457int 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
529static 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
559static 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
584static 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
606static 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
621static 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
640static 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
654static 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
683static 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 */
727static 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 */
864static 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
896static 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
917static 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
954static 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 */
1117static 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 */
1233static 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
1300static 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
1324static 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
1348static 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
1372static 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
1397static 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
1411static 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
1427static 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
1443static 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 */
1473static 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
1489static 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
1574static 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
1598static 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
1619static 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
1633void 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.063186 seconds and 5 git commands to generate.