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