4 * The point of faimtest is twofold:
5 * - Test the functionality of libfaim.
6 * - Demonstrate the functionality of libfaim and how to use it.
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?)
16 * The faimtest code is very ugly. Probably always will be.
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.
27 char *dprintf_ctime(void)
29 static char retbuf[64];
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);
41 static char *msgerrreasons[] = {
47 "Service unavailable",
48 "Service not defined",
50 "Not supported by host",
51 "Not supported by client",
56 "Busted SNAC payload",
57 "Insufficient rights",
58 "In local permit/deny",
60 "Too evil (receiver)",
61 "User temporarily unavailable",
68 static int msgerrreasonslen = 25;
70 aim_session_t aimsess;
74 * This is used to intercept debugging/diagnostic messages from libfaim.
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.
80 static void faimtest_debugcb(aim_session_t *sess, int level, const char *format, va_list va)
83 vfprintf(stderr, format, va);
88 int faimtest_flapversion(aim_session_t *sess, aim_frame_t *fr, ...)
91 /* XXX fix libfaim to support this */
92 dvprintf("using FLAP version 0x%08x\n", /* aimutil_get32(fr->data)*/ 0xffffffff);
96 * This is an alternate location for starting the login process.
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");
110 * This is a frivilous callback. You don't need it. I only used it for
111 * debugging non-blocking connects.
113 * If packets are sent to a conn before its fully connected, they
114 * will be queued and then transmitted when the connection completes.
117 int faimtest_conncomplete(aim_session_t *sess, aim_frame_t *fr, ...)
123 conn = va_arg(ap, aim_conn_t *);
127 dvprintf("faimtest: connection on %d completed\n", conn->fd);
134 * This is really all thats needed to link against libfaim on win32.
136 * Note that this particular version of faimtest has never been tested
137 * on win32, but I'm fairly sure it should work.
139 static int initwsa(void)
141 WORD wVersionRequested;
144 wVersionRequested = MAKEWORD(2,2);
145 return WSAStartup(wVersionRequested, &wsaData);
150 * This is unrealistic. Most clients will not be able to do this.
152 int faimtest_init(void)
154 aim_conn_t *stdinconn = NULL;
156 if (!(stdinconn = aim_newconn(&aimsess, 0, NULL))) {
157 dprintf("unable to create connection for stdin!\n");
161 stdinconn->fd = STDIN_FILENO;
166 int main(int argc, char **argv)
168 aim_conn_t *waitingconn = NULL;
171 static int faimtest_mode = 0;
174 const char *buddyiconpath = NULL;
175 struct faimtest_priv priv = {
176 NULL, NULL, NULL, NULL,
177 NULL, NULL, NULL, NULL,
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");
190 priv.listingpath = getenv("LISTINGPATH");
192 while ((i = getopt(argc, argv, "u:p:a:U:P:A:l:c:hoOb:i:")) != EOF) {
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;
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");
227 if (initwsa() != 0) {
228 dprintf("faimtest: could not initialize windows sockets\n");
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;
238 if (priv.listingpath) {
241 listingname = (char *)calloc(1, strlen(priv.listingpath)+strlen("/listing.txt"));
242 sprintf(listingname, "%s/listing.txt", priv.listingpath);
244 if ((priv.listingfile = fopen(listingname, "r")) == NULL)
245 dvprintf("Couldn't open %s... disabling that shit.\n", listingname);
254 if ((stat(buddyiconpath, &st) != -1) && (st.st_size <= MAXICONLEN) && (f = fopen(buddyiconpath, "r"))) {
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);
261 priv.buddyiconsum = aim_iconsum(priv.buddyicon, priv.buddyiconlen);
263 dvprintf("read %d bytes of %s for buddy icon (sum 0x%08x)\n", priv.buddyiconlen, buddyiconpath, priv.buddyiconsum);
268 dvprintf("could not open buddy icon %s\n", buddyiconpath);
274 if (faimtest_mode < 2)
277 if (faimtest_mode >= 1) {
278 if (login(&aimsess, priv.screenname, priv.password) == -1) {
279 if (faimtest_mode < 2)
291 waitingconn = aim_select(&aimsess, &tv, &selstat);
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));
298 if (selstat == -1) { /* error */
299 keepgoing = 0; /* fall through */
300 } else if (selstat == 0) { /* no events pending */
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)) {
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);
314 if (aim_get_command(&aimsess, waitingconn) >= 0) {
315 aim_rxdispatch(&aimsess);
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);
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)
336 /* close up all connections, dead or no */
337 aim_session_kill(&aimsess);
339 if (faimtest_mode < 2) {
344 free(priv.buddyicon);
350 int faimtest_serverready(aim_session_t *sess, aim_frame_t *fr, ...)
357 famcount = va_arg(ap, int);
358 families = va_arg(ap, fu16_t *);
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]);
366 if (fr->conn->type == AIM_CONN_TYPE_AUTH) {
368 aim_auth_setversions(sess, fr->conn);
369 aim_bos_reqrate(sess, fr->conn); /* request rate info */
371 dprintf("done with auth server ready\n");
373 } else if (fr->conn->type == AIM_CONN_TYPE_BOS) {
375 aim_setversions(sess, fr->conn);
376 aim_bos_reqrate(sess, fr->conn); /* request rate info */
378 dprintf("done with BOS server ready\n");
384 int faimtest_parse_connerr(aim_session_t *sess, aim_frame_t *fr, ...)
386 struct faimtest_priv *priv = (struct faimtest_priv *)sess->aux_data;
392 code = va_arg(ap, int);
393 msg = va_arg(ap, char *);
396 dvprintf("connerr: Code 0x%04x: %s\n", code, msg);
397 aim_conn_kill(sess, &fr->conn); /* this will break the main loop */
405 static int faimtest_rateresp_auth(aim_session_t *sess, aim_frame_t *fr, ...)
408 aim_bos_ackrateresp(sess, fr->conn);
409 aim_auth_clientready(sess, fr->conn);
411 dprintf("faimtest: connected to authorization/admin service\n");
416 int faimtest_accountconfirm(aim_session_t *sess, aim_frame_t *fr, ...)
422 status = va_arg(ap, int); /* status code of confirmation request */
425 dvprintf("account confirmation returned status 0x%04x (%s)\n", status, (status==0x0000)?"email sent":"unknown");
435 * This kind of function is really not legal in the new bstream way...
436 * In fact, clients should never access the aim_frame_t directly in handlers,
437 * since that may leave it in a bizare state for the lower layers. In fact,
438 * clients should probably not even get passed a pointer like this.
441 int faimtest_parse_unknown(aim_session_t *sess, aim_frame_t *fr, ...)
445 aim_bstream_rewind(&fr->data); /* boo! */
447 dprintf("\nReceived unknown packet:");
448 for (i = 0; aim_bstream_empty(&fr->data); i++) {
450 dinlineprintf("\n\t");
451 dvinlineprintf("0x%2x ", aimbs_get8(&fr->data));
453 dinlineprintf("\n\n");
459 int faimtest_handleredirect(aim_session_t *sess, aim_frame_t *fr, ...)
467 serviceid = va_arg(ap, int);
468 ip = va_arg(ap, char *);
469 cookie = va_arg(ap, fu8_t *);
471 if (serviceid == 0x0005) { /* Adverts */
475 tstconn = aim_newconn(sess, AIM_CONN_TYPE_ADS, ip);
476 if (!tstconn || (tstconn->status & AIM_CONN_STATUS_RESOLVERR)) {
477 dprintf("faimtest: unable to reconnect with authorizer\n");
479 aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, faimtest_flapversion, 0);
480 aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE, faimtest_conncomplete, 0);
481 aim_conn_addhandler(sess, tstconn, 0x0001, 0x0003, faimtest_serverready, 0);
482 aim_conn_addhandler(sess, tstconn, 0x0001, 0x0007, faimtest_rateresp, 0); /* rate info */
483 aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_GEN, 0x0018, faimtest_hostversions, 0);
484 aim_auth_sendcookie(sess, tstconn, cookie);
485 dprintf("sent cookie to adverts host\n");
488 } else if (serviceid == 0x0007) { /* Authorizer */
492 tstconn = aim_newconn(sess, AIM_CONN_TYPE_AUTH, ip);
493 if (!tstconn || (tstconn->status & AIM_CONN_STATUS_RESOLVERR)) {
494 dprintf("faimtest: unable to reconnect with authorizer\n");
496 aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, faimtest_flapversion, 0);
497 aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE, faimtest_conncomplete, 0);
498 aim_conn_addhandler(sess, tstconn, 0x0001, 0x0003, faimtest_serverready, 0);
499 aim_conn_addhandler(sess, tstconn, 0x0001, 0x0007, faimtest_rateresp, 0); /* rate info */
500 aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_GEN, 0x0018, faimtest_hostversions, 0);
501 aim_conn_addhandler(sess, tstconn, 0x0007, 0x0007, faimtest_accountconfirm, 0);
502 aim_conn_addhandler(sess, tstconn, 0x0007, 0x0003, faimtest_infochange, 0);
503 aim_conn_addhandler(sess, tstconn, 0x0007, 0x0005, faimtest_infochange, 0);
504 /* Send the cookie to the Auth */
505 aim_auth_sendcookie(sess, tstconn, cookie);
506 dprintf("sent cookie to authorizer host\n");
509 } else if (serviceid == 0x000d) { /* ChatNav */
511 chatnav_redirect(sess, ip, cookie);
513 } else if (serviceid == 0x000e) { /* Chat */
514 char *roomname = NULL;
517 roomname = va_arg(ap, char *);
518 exchange = va_arg(ap, int);
520 chat_redirect(sess, ip, cookie, roomname, exchange);
523 dvprintf("uh oh... got redirect for unknown service 0x%04x!!\n", serviceid);
531 static int faimtest_rateresp_bos(aim_session_t *sess, aim_frame_t *fr, ...)
533 struct faimtest_priv *priv = (struct faimtest_priv *)sess->aux_data;
534 char buddies[128]; /* this is the new buddy list */
535 char profile[256]; /* this is the new profile */
536 char awaymsg[] = {"blah blah blah Ole! blah blah blah"};
538 /* Caution: Buddy1 and Buddy2 are real people! (who I don't know) */
539 snprintf(buddies, sizeof(buddies), "Buddy1&Buddy2&%s&", priv->ohcaptainmycaptain ? priv->ohcaptainmycaptain : "blah");
540 snprintf(profile, sizeof(profile), "Hello.<br>My captain is %s. They were dumb enough to leave this message in their client, or they are using faimtest. Shame on them.", priv->ohcaptainmycaptain);
542 aim_bos_ackrateresp(sess, fr->conn); /* ack rate info response */
543 aim_bos_reqpersonalinfo(sess, fr->conn);
544 aim_bos_reqlocaterights(sess, fr->conn);
545 aim_bos_setprofile(sess, fr->conn, profile, awaymsg, AIM_CAPS_BUDDYICON | AIM_CAPS_CHAT | AIM_CAPS_GETFILE | AIM_CAPS_SENDFILE | AIM_CAPS_IMIMAGE /*| AIM_CAPS_GAMES | AIM_CAPS_SAVESTOCKS*/);
546 aim_bos_reqbuddyrights(sess, fr->conn);
548 /* send the buddy list and profile (required, even if empty) */
549 aim_bos_setbuddylist(sess, fr->conn, buddies);
551 aim_reqicbmparams(sess, fr->conn);
553 aim_bos_reqrights(sess, fr->conn);
554 /* set group permissions -- all user classes */
555 aim_bos_setgroupperm(sess, fr->conn, AIM_FLAG_ALLUSERS);
556 aim_bos_setprivacyflags(sess, fr->conn, AIM_PRIVFLAGS_ALLOWIDLE);
561 static int faimtest_icbmparaminfo(aim_session_t *sess, aim_frame_t *fr, ...)
563 struct aim_icbmparameters *params;
567 params = va_arg(ap, struct aim_icbmparameters *);
570 dvprintf("ICBM Parameters: maxchannel = %d, default flags = 0x%08lx, max msg len = %d, max sender evil = %f, max reciever evil = %f, min msg interval = %ld\n", params->maxchan, params->flags, params->maxmsglen, ((float)params->maxsenderwarn)/10.0, ((float)params->maxrecverwarn)/10.0, params->minmsginterval);
573 * Set these to your taste, or client medium. Setting minmsginterval
574 * higher is good for keeping yourself from getting flooded (esp
575 * if you're on a slow connection or something where that would be
578 params->maxmsglen = 8000;
579 params->minmsginterval = 0; /* in milliseconds */
581 aim_seticbmparam(sess, fr->conn, params);
586 static int faimtest_hostversions(aim_session_t *sess, aim_frame_t *fr, ...)
593 vercount = va_arg(ap, int); /* number of family/version pairs */
594 versions = va_arg(ap, fu8_t *);
597 dprintf("faimtest: SNAC versions supported by this host: ");
598 for (i = 0; i < vercount*4; i += 4) {
599 dvinlineprintf("0x%04x:0x%04x ",
600 aimutil_get16(versions+i), /* SNAC family */
601 aimutil_get16(versions+i+2) /* Version number */);
608 static int faimtest_parse_buddyrights(aim_session_t *sess, aim_frame_t *fr, ...)
611 fu16_t maxbuddies, maxwatchers;
614 maxbuddies = va_arg(ap, int);
615 maxwatchers = va_arg(ap, int);
618 dvprintf("buddy list rights: Max buddies = %d / Max watchers = %d\n", maxbuddies, maxwatchers);
623 static int faimtest_bosrights(aim_session_t *sess, aim_frame_t *fr, ...)
626 fu16_t maxpermits, maxdenies;
629 maxpermits = va_arg(ap, int);
630 maxdenies = va_arg(ap, int);
633 dvprintf("BOS rights: Max permit = %d / Max deny = %d\n", maxpermits, maxdenies);
635 aim_bos_clientready(sess, fr->conn);
637 dprintf("officially connected to BOS.\n");
642 static int faimtest_locrights(aim_session_t *sess, aim_frame_t *fr, ...)
648 maxsiglen = va_arg(ap, int);
651 dvprintf("locate rights: max signature length = %d\n", maxsiglen);
656 static int faimtest_reportinterval(aim_session_t *sess, aim_frame_t *fr, ...)
658 struct faimtest_priv *priv = (struct faimtest_priv *)sess->aux_data;
663 interval = va_arg(ap, int);
666 dvprintf("minimum report interval: %d (seconds?)\n", interval);
668 if (!priv->connected)
672 aim_bos_reqservice(sess, fr->conn, 0x0005); /* adverts */
673 aim_bos_reqservice(sess, fr->conn, 0x000f); /* user directory */
675 /* Don't know what this does... */
676 /* XXX sess->sn should be normalized by the 0001/000f handler */
677 aim_0002_000b(sess, fr->conn, sess->sn);
680 aim_reqicbmparams(sess, fr->conn);
685 static int faimtest_parse_motd(aim_session_t *sess, aim_frame_t *fr, ...)
687 static char *codes[] = {
694 static int codeslen = 5;
700 id = va_arg(ap, int);
701 msg = va_arg(ap, char *);
704 dvprintf("motd: %s (%d / %s)\n", msg, id, (id < codeslen)?codes[id]:"unknown");
710 * This is a little more complicated than it looks. The module
711 * name (proto, boscore, etc) may or may not be given. If it is
712 * not given, then use aim.exe. If it is given, put ".ocm" on the
715 * Now, if the offset or length requested would cause a read past
716 * the end of the file, then the request is considered invalid. Invalid
717 * requests are processed specially. The value hashed is the
718 * the request, put into little-endian (eight bytes: offset followed
721 * Additionally, if the request is valid, the length is mod 4096. It is
722 * important that the length is checked for validity first before doing
725 * Note to Bosco's Brigade: if you'd like to break this, put the
726 * module name on an invalid request.
729 static int getaimdata(aim_session_t *sess, unsigned char **bufret, int *buflenret, unsigned long offset, unsigned long len, const char *modname)
731 struct faimtest_priv *priv = (struct faimtest_priv *)sess->aux_data;
733 static const char defaultmod[] = "aim.exe";
734 char *filename = NULL;
739 if (!bufret || !buflenret)
744 if (!(filename = malloc(strlen(priv->aimbinarypath)+1+strlen(modname)+4+1))) {
745 dperror("memrequest: malloc");
749 sprintf(filename, "%s/%s.ocm", priv->aimbinarypath, modname);
753 if (!(filename = malloc(strlen(priv->aimbinarypath)+1+strlen(defaultmod)+1))) {
754 dperror("memrequest: malloc");
758 sprintf(filename, "%s/%s", priv->aimbinarypath, defaultmod);
762 if (stat(filename, &st) == -1) {
764 dperror("memrequest: stat");
772 if ((offset > st.st_size) || (len > st.st_size))
774 else if ((st.st_size - offset) < len)
775 len = st.st_size - offset;
776 else if ((st.st_size - len) < len)
777 len = st.st_size - len;
786 free(filename); /* not needed */
788 dvprintf("memrequest: recieved invalid request for 0x%08lx bytes at 0x%08lx (file %s)\n", len, offset, modname);
792 i += strlen(modname);
794 if (!(buf = malloc(i)))
800 memcpy(buf, modname, strlen(modname));
801 i += strlen(modname);
804 /* Damn endianness. This must be little (LSB first) endian. */
805 buf[i++] = offset & 0xff;
806 buf[i++] = (offset >> 8) & 0xff;
807 buf[i++] = (offset >> 16) & 0xff;
808 buf[i++] = (offset >> 24) & 0xff;
809 buf[i++] = len & 0xff;
810 buf[i++] = (len >> 8) & 0xff;
811 buf[i++] = (len >> 16) & 0xff;
812 buf[i++] = (len >> 24) & 0xff;
819 if (!(buf = malloc(len))) {
824 dvprintf("memrequest: loading %ld bytes from 0x%08lx in \"%s\"...\n", len, offset, filename);
826 if (!(f = fopen(filename, "r"))) {
827 dperror("memrequest: fopen");
835 if (fseek(f, offset, SEEK_SET) == -1) {
836 dperror("memrequest: fseek");
842 if (fread(buf, len, 1, f) != 1) {
843 dperror("memrequest: fread");
856 return 0; /* success! */
860 * This will get an offset and a length. The client should read this
861 * data out of whatever AIM.EXE binary the user has provided (hopefully
862 * it matches the client information thats sent at login) and pass a
863 * buffer back to libfaim so it can hash the data and send it to AOL for
864 * inspection by the client police.
866 static int faimtest_memrequest(aim_session_t *sess, aim_frame_t *fr, ...)
868 struct faimtest_priv *priv = (struct faimtest_priv *)sess->aux_data;
876 offset = va_arg(ap, fu32_t);
877 len = va_arg(ap, fu32_t);
878 modname = va_arg(ap, char *);
881 if (priv->aimbinarypath && (getaimdata(sess, &buf, &buflen, offset, len, modname) == 0)) {
883 aim_sendmemblock(sess, fr->conn, offset, buflen, buf, AIM_SENDMEMBLOCK_FLAG_ISREQUEST);
889 dvprintf("memrequest: unable to use AIM binary (\"%s/%s\"), sending defaults...\n", priv->aimbinarypath, modname);
891 aim_sendmemblock(sess, fr->conn, offset, len, NULL, AIM_SENDMEMBLOCK_FLAG_ISREQUEST);
898 static void printuserflags(fu16_t flags)
900 if (flags & AIM_FLAG_UNCONFIRMED)
901 dinlineprintf("UNCONFIRMED ");
902 if (flags & AIM_FLAG_ADMINISTRATOR)
903 dinlineprintf("ADMINISTRATOR ");
904 if (flags & AIM_FLAG_AOL)
905 dinlineprintf("AOL ");
906 if (flags & AIM_FLAG_OSCAR_PAY)
907 dinlineprintf("OSCAR_PAY ");
908 if (flags & AIM_FLAG_FREE)
909 dinlineprintf("FREE ");
910 if (flags & AIM_FLAG_AWAY)
911 dinlineprintf("AWAY ");
912 if (flags & AIM_FLAG_UNKNOWN40)
913 dinlineprintf("ICQ? ");
914 if (flags & AIM_FLAG_UNKNOWN80)
915 dinlineprintf("UNKNOWN80 ");
919 static int faimtest_parse_userinfo(aim_session_t *sess, aim_frame_t *fr, ...)
921 struct aim_userinfo_s *userinfo;
922 char *prof_encoding = NULL;
928 userinfo = va_arg(ap, struct aim_userinfo_s *);
929 prof_encoding = va_arg(ap, char *);
930 prof = va_arg(ap, char *);
931 inforeq = va_arg(ap, fu16_t);
934 dvprintf("faimtest: userinfo: sn: %s\n", userinfo->sn);
935 dvprintf("faimtest: userinfo: warnlevel: 0x%04x\n", userinfo->warnlevel);
936 dvprintf("faimtest: userinfo: flags: 0x%04x = ", userinfo->flags);
937 printuserflags(userinfo->flags);
940 dvprintf("faimtest: userinfo: membersince: %lu\n", userinfo->membersince);
941 dvprintf("faimtest: userinfo: onlinesince: %lu\n", userinfo->onlinesince);
942 dvprintf("faimtest: userinfo: idletime: 0x%04x\n", userinfo->idletime);
944 if (inforeq == AIM_GETINFO_GENERALINFO) {
945 dvprintf("faimtest: userinfo: profile_encoding: %s\n", prof_encoding ? prof_encoding : "[none]");
946 dvprintf("faimtest: userinfo: prof: %s\n", prof ? prof : "[none]");
947 } else if (inforeq == AIM_GETINFO_AWAYMESSAGE) {
948 dvprintf("faimtest: userinfo: awaymsg_encoding: %s\n", prof_encoding ? prof_encoding : "[none]");
949 dvprintf("faimtest: userinfo: awaymsg: %s\n", prof ? prof : "[none]");
951 dprintf("faimtest: userinfo: unknown info request\n");
956 static int faimtest_handlecmd(aim_session_t *sess, aim_conn_t *conn, struct aim_userinfo_s *userinfo, const char *tmpstr)
958 struct faimtest_priv *priv = (struct faimtest_priv *)sess->aux_data;
960 if (!strncmp(tmpstr, "disconnect", 10)) {
964 } else if (strstr(tmpstr, "goodday")) {
966 aim_send_im(sess, conn, userinfo->sn, AIM_IMFLAGS_ACK, "Good day to you too.");
968 } else if (strstr(tmpstr, "haveicon") && priv->buddyicon) {
969 struct aim_sendimext_args args;
970 static const char iconmsg[] = {"I have an icon"};
972 args.destsn = userinfo->sn;
973 args.flags = AIM_IMFLAGS_HASICON;
975 args.msglen = strlen(iconmsg);
976 args.iconlen = priv->buddyiconlen;
977 args.iconstamp = priv->buddyiconstamp;
978 args.iconsum = priv->buddyiconsum;
980 aim_send_im_ext(sess, conn, &args);
982 } else if (strstr(tmpstr, "havefeat")) {
983 struct aim_sendimext_args args;
984 static const char featmsg[] = {"I have nifty features."};
985 fu8_t features[] = {0x01, 0x01, 0x01, 0x02, 0x42, 0x43, 0x44, 0x45};
987 args.destsn = userinfo->sn;
988 args.flags = AIM_IMFLAGS_CUSTOMFEATURES;
990 args.msglen = strlen(featmsg);
991 args.features = features;
992 args.featureslen = sizeof(features);
994 aim_send_im_ext(sess, conn, &args);
996 } else if (strstr(tmpstr, "sendicon") && priv->buddyicon) {
998 aim_send_icon(sess, conn, userinfo->sn, priv->buddyicon, priv->buddyiconlen, priv->buddyiconstamp, priv->buddyiconsum);
1000 } else if (strstr(tmpstr, "warnme")) {
1002 dprintf("faimtest: icbm: sending non-anon warning\n");
1003 aim_send_warning(sess, conn, userinfo->sn, 0);
1005 } else if (strstr(tmpstr, "anonwarn")) {
1007 dprintf("faimtest: icbm: sending anon warning\n");
1008 aim_send_warning(sess, conn, userinfo->sn, AIM_WARN_ANON);
1010 } else if (strstr(tmpstr, "setdirectoryinfo")) {
1012 dprintf("faimtest: icbm: sending backwards profile data\n");
1013 aim_setdirectoryinfo(sess, conn, "tsrif", "elddim", "tsal", "nediam", "emankcin", "teerts", "ytic", "etats", "piz", 0, 1);
1015 } else if (strstr(tmpstr, "setinterests")) {
1017 dprintf("faimtest: icbm: setting fun interests\n");
1018 aim_setuserinterests(sess, conn, "interest1", "interest2", "interest3", "interest4", "interest5", 1);
1020 } else if (!strncmp(tmpstr, "getfile", 7)) {
1022 if (!priv->ohcaptainmycaptain) {
1024 aim_send_im(sess, conn, userinfo->sn, AIM_IMFLAGS_ACK, "I have no owner!");
1027 getfile_start(sess, conn, (strlen(tmpstr) < 8)?priv->ohcaptainmycaptain:tmpstr+8);
1029 } else if (!strncmp(tmpstr, "open chatnav", 12)) {
1031 aim_bos_reqservice(sess, conn, AIM_CONN_TYPE_CHATNAV);
1033 } else if (!strncmp(tmpstr, "create", 6)) {
1035 aim_chatnav_createroom(sess,aim_getconn_type(sess, AIM_CONN_TYPE_CHATNAV), (strlen(tmpstr) < 7)?"WorldDomination":tmpstr+7, 0x0004);
1037 } else if (!strncmp(tmpstr, "close chatnav", 13)) {
1038 aim_conn_t *chatnavconn;
1040 if ((chatnavconn = aim_getconn_type(sess, AIM_CONN_TYPE_CHATNAV)))
1041 aim_conn_kill(sess, &chatnavconn);
1043 } else if (!strncmp(tmpstr, "join", 4)) {
1045 aim_chat_join(sess, conn, 0x0004, "worlddomination", 0x0000);
1047 } else if (!strncmp(tmpstr, "leave", 5)) {
1049 aim_chat_leaveroom(sess, "worlddomination");
1051 } else if (!strncmp(tmpstr, "getinfo", 7)) {
1053 aim_getinfo(sess, conn, "75784102", AIM_GETINFO_GENERALINFO);
1054 aim_getinfo(sess, conn, "15853637", AIM_GETINFO_AWAYMESSAGE);
1055 aim_getinfo(sess, conn, "midendian", AIM_GETINFO_GENERALINFO);
1056 aim_getinfo(sess, conn, "midendian", AIM_GETINFO_AWAYMESSAGE);
1058 } else if (strstr(tmpstr, "open directim")) {
1060 directim_start(sess, conn, userinfo->sn);
1062 } else if(strstr(tmpstr, "lookup")) {
1064 aim_usersearch_address(sess, conn, "mid@auk.cx");
1066 } else if (!strncmp(tmpstr, "reqsendmsg", 10)) {
1068 aim_send_im(sess, conn, priv->ohcaptainmycaptain, 0, "sendmsg 7900");
1070 } else if (!strncmp(tmpstr, "reqauth", 7)) {
1072 aim_bos_reqservice(sess, conn, AIM_CONN_TYPE_AUTH);
1074 } else if (!strncmp(tmpstr, "reqconfirm", 10)) {
1076 aim_auth_reqconfirm(sess, aim_getconn_type(sess, AIM_CONN_TYPE_AUTH));
1078 } else if (!strncmp(tmpstr, "reqemail", 8)) {
1080 aim_auth_getinfo(sess, aim_getconn_type(sess, AIM_CONN_TYPE_AUTH), 0x0011);
1082 } else if (!strncmp(tmpstr, "changepass", 8)) {
1084 aim_auth_changepasswd(sess, aim_getconn_type(sess, AIM_CONN_TYPE_AUTH), "NEWPASSWORD", "OLDPASSWORD");
1086 } else if (!strncmp(tmpstr, "setemail", 8)) {
1088 aim_auth_setemail(sess, aim_getconn_type(sess, AIM_CONN_TYPE_AUTH), "NEWEMAILADDRESS");
1090 } else if (!strncmp(tmpstr, "sendmsg", 7)) {
1098 newbuf = malloc(i+1);
1099 for (z = 0; z < i; z++)
1100 newbuf[z] = (z % 10)+0x30;
1102 aim_send_im(sess, conn, userinfo->sn, 0, newbuf);
1108 dprintf("unknown command.\n");
1109 aim_add_buddy(sess, conn, userinfo->sn);
1117 * Channel 1: Standard Message
1119 static int faimtest_parse_incoming_im_chan1(aim_session_t *sess, aim_conn_t *conn, struct aim_userinfo_s *userinfo, va_list ap)
1121 struct faimtest_priv *priv = (struct faimtest_priv *)sess->aux_data;
1123 struct aim_incomingim_ch1_args *args;
1124 int clienttype = AIM_CLIENTTYPE_UNKNOWN;
1125 char realmsg[8192+1] = {""};
1127 args = va_arg(ap, struct aim_incomingim_ch1_args *);
1130 clienttype = aim_fingerprintclient(args->features, args->featureslen);
1132 dvprintf("faimtest: icbm: sn = \"%s\"\n", userinfo->sn);
1133 dvprintf("faimtest: icbm: probable client type: %d\n", clienttype);
1134 dvprintf("faimtest: icbm: warnlevel = 0x%04x\n", userinfo->warnlevel);
1135 dvprintf("faimtest: icbm: flags = 0x%04x = ", userinfo->flags);
1136 printuserflags(userinfo->flags);
1137 dinlineprintf("\n");
1139 dvprintf("faimtest: icbm: membersince = %lu\n", userinfo->membersince);
1140 dvprintf("faimtest: icbm: onlinesince = %lu\n", userinfo->onlinesince);
1141 dvprintf("faimtest: icbm: idletime = 0x%04x\n", userinfo->idletime);
1142 dvprintf("faimtest: icbm: capabilities = 0x%04x\n", userinfo->capabilities);
1144 dprintf("faimtest: icbm: icbmflags = ");
1145 if (args->icbmflags & AIM_IMFLAGS_AWAY)
1146 dinlineprintf("away ");
1147 if (args->icbmflags & AIM_IMFLAGS_ACK)
1148 dinlineprintf("ackrequest ");
1149 if (args->icbmflags & AIM_IMFLAGS_BUDDYREQ)
1150 dinlineprintf("buddyreq ");
1151 if (args->icbmflags & AIM_IMFLAGS_HASICON)
1152 dinlineprintf("hasicon ");
1153 dinlineprintf("\n");
1155 dvprintf("faimtest: icbm: encoding flags = {%04x, %04x}\n", args->flag1, args->flag2);
1158 * Quickly convert it to eight bit format, replacing non-ASCII UNICODE
1159 * characters with their equivelent HTML entity.
1161 if (args->icbmflags & AIM_IMFLAGS_UNICODE) {
1164 for (i = 0; i < args->msglen; i += 2) {
1167 uni = ((args->msg[i] & 0xff) << 8) | (args->msg[i+1] & 0xff);
1169 if ((uni < 128) || ((uni >= 160) && (uni <= 255))) { /* ISO 8859-1 */
1171 snprintf(realmsg+strlen(realmsg), sizeof(realmsg)-strlen(realmsg), "%c", uni);
1173 } else { /* something else, do UNICODE entity */
1175 snprintf(realmsg+strlen(realmsg), sizeof(realmsg)-strlen(realmsg), "&#%04x;", uni);
1184 * For non-UNICODE encodings (ASCII and ISO 8859-1), there is
1185 * no need to do anything special here. Most
1186 * terminals/whatever will be able to display such characters
1189 * Beware that PC-ASCII 128 through 159 are _not_ actually
1190 * defined in ASCII or ISO 8859-1, and you should send them as
1191 * UNICODE. WinAIM will send these characters in a UNICODE
1192 * message, so you need to do so as well.
1194 * You may not think it necessary to handle UNICODE messages.
1195 * You're probably wrong. For one thing, Microsoft "Smart
1196 * Quotes" will be sent by WinAIM as UNICODE (not HTML UNICODE,
1197 * but real UNICODE). If you don't parse UNICODE at all, your
1198 * users will get a blank message instead of the message
1199 * containing Smart Quotes.
1202 strncpy(realmsg, args->msg, sizeof(realmsg));
1205 dvprintf("faimtest: icbm: message: %s\n", realmsg);
1207 if (args->icbmflags & AIM_IMFLAGS_HASICON)
1208 aim_send_im(sess, conn, userinfo->sn, AIM_IMFLAGS_BUDDYREQ, "You have an icon");
1213 while (realmsg[i] == '<') {
1214 if (realmsg[i] == '<') {
1215 while (realmsg[i] != '>')
1222 faimtest_handlecmd(sess, conn, userinfo, tmpstr);
1226 if (priv->buddyicon && (args->icbmflags & AIM_IMFLAGS_BUDDYREQ))
1227 aim_send_icon(sess, conn, userinfo->sn, priv->buddyicon, priv->buddyiconlen, priv->buddyiconstamp, priv->buddyiconsum);
1233 * Channel 2: Rendevous Request
1235 static int faimtest_parse_incoming_im_chan2(aim_session_t *sess, aim_conn_t *conn, struct aim_userinfo_s *userinfo, va_list ap)
1237 struct aim_incomingim_ch2_args *args;
1239 args = va_arg(ap, struct aim_incomingim_ch2_args *);
1242 if (args->reqclass == AIM_CAPS_VOICE) {
1244 dvprintf("faimtest: voice invitation: source sn = %s\n", userinfo->sn);
1245 dvprintf("faimtest: voice invitation: \twarnlevel = 0x%04x\n", userinfo->warnlevel);
1246 dvprintf("faimtest: voice invitation: \tclass = 0x%04x = ", userinfo->flags);
1247 printuserflags(userinfo->flags);
1248 dinlineprintf("\n");
1250 dvprintf("faimtest: voice invitation: \tonlinesince = %lu\n", userinfo->onlinesince);
1251 dvprintf("faimtest: voice invitation: \tidletime = 0x%04x\n", userinfo->idletime);
1253 } else if (args->reqclass == AIM_CAPS_GETFILE) {
1255 getfile_requested(sess, conn, userinfo, args);
1257 } else if (args->reqclass == AIM_CAPS_SENDFILE) {
1259 dprintf("faimtest: send file!\n");
1261 } else if (args->reqclass == AIM_CAPS_CHAT) {
1263 dvprintf("faimtest: chat invitation: source sn = %s\n", userinfo->sn);
1264 dvprintf("faimtest: chat invitation: \twarnlevel = 0x%04x\n", userinfo->warnlevel);
1265 dvprintf("faimtest: chat invitation: \tclass = 0x%04x = ", userinfo->flags);
1266 printuserflags(userinfo->flags);
1267 dinlineprintf("\n");
1269 /* we dont get membersince on chat invites! */
1270 dvprintf("faimtest: chat invitation: \tonlinesince = %lu\n", userinfo->onlinesince);
1271 dvprintf("faimtest: chat invitation: \tidletime = 0x%04x\n", userinfo->idletime);
1273 dvprintf("faimtest: chat invitation: message = %s\n", args->info.chat.msg);
1274 dvprintf("faimtest: chat invitation: room name = %s\n", args->info.chat.roominfo.name);
1275 dvprintf("faimtest: chat invitation: encoding = %s\n", args->info.chat.encoding);
1276 dvprintf("faimtest: chat invitation: language = %s\n", args->info.chat.lang);
1277 dvprintf("faimtest: chat invitation: exchange = 0x%04x\n", args->info.chat.roominfo.exchange);
1278 dvprintf("faimtest: chat invitation: instance = 0x%04x\n", args->info.chat.roominfo.instance);
1279 dvprintf("faimtest: chat invitiation: autojoining %s...\n", args->info.chat.roominfo.name);
1281 /* Automatically join room... */
1282 aim_chat_join(sess, conn, args->info.chat.roominfo.exchange, args->info.chat.roominfo.name, args->info.chat.roominfo.instance);
1284 } else if (args->reqclass == AIM_CAPS_IMIMAGE) {
1286 dprintf("faimtest: icbm: rendezvous imimage\n");
1288 directim_requested(sess, conn, userinfo, args);
1290 } else if (args->reqclass == AIM_CAPS_BUDDYICON) {
1292 dvprintf("faimtest: Buddy Icon from %s, length = %lu\n", userinfo->sn, args->info.icon.length);
1296 dvprintf("faimtest: icbm: unknown reqclass (%d)\n", args->reqclass);
1302 static int faimtest_parse_incoming_im(aim_session_t *sess, aim_frame_t *fr, ...)
1305 struct aim_userinfo_s *userinfo;
1310 channel = va_arg(ap, fu16_t);
1311 userinfo = va_arg(ap, struct aim_userinfo_s *);
1314 ret = faimtest_parse_incoming_im_chan1(sess, fr->conn, userinfo, ap);
1315 else if (channel == 2)
1316 ret = faimtest_parse_incoming_im_chan2(sess, fr->conn, userinfo, ap);
1318 dvprintf("unsupported channel 0x%04x\n", channel);
1320 dvprintf("faimtest: icbm: done with ICBM handling (ret = %d)\n", ret);
1325 #ifdef MID_REWROTE_ALL_THE_CRAP
1326 static int faimtest_infochange(aim_session_t *sess, aim_frame_t *fr, ...)
1328 fu16_t change = 0, perms, type;
1334 perms = va_arg(ap, fu16_t);
1335 type = va_arg(ap, fu16_t);
1336 length = va_arg(ap, int);
1337 val = va_arg(ap, char *);
1338 str = va_arg(ap, int);
1341 if (aimutil_get16(command->data+2) == 0x0005)
1344 dvprintf("info%s: perms = %d, type = %x, length = %d, val = %s\n", change?" change":"", perms, type, length, str?val:"(not string)");
1350 static int faimtest_parse_oncoming(aim_session_t *sess, aim_frame_t *fr, ...)
1352 struct aim_userinfo_s *userinfo;
1356 userinfo = va_arg(ap, struct aim_userinfo_s *);
1359 dvprintf("%ld %s is now online (flags: %04x = %s%s%s%s%s%s%s%s) (caps = 0x%04x)\n",
1361 userinfo->sn, userinfo->flags,
1362 (userinfo->flags&AIM_FLAG_UNCONFIRMED)?" UNCONFIRMED":"",
1363 (userinfo->flags&AIM_FLAG_ADMINISTRATOR)?" ADMINISTRATOR":"",
1364 (userinfo->flags&AIM_FLAG_AOL)?" AOL":"",
1365 (userinfo->flags&AIM_FLAG_OSCAR_PAY)?" OSCAR_PAY":"",
1366 (userinfo->flags&AIM_FLAG_FREE)?" FREE":"",
1367 (userinfo->flags&AIM_FLAG_AWAY)?" AWAY":"",
1368 (userinfo->flags&AIM_FLAG_UNKNOWN40)?" UNKNOWN40":"",
1369 (userinfo->flags&AIM_FLAG_UNKNOWN80)?" UNKNOWN80":"",
1370 userinfo->capabilities);
1374 static int faimtest_parse_offgoing(aim_session_t *sess, aim_frame_t *fr, ...)
1376 struct aim_userinfo_s *userinfo;
1380 userinfo = va_arg(ap, struct aim_userinfo_s *);
1383 dvprintf("%ld %s is now offline (flags: %04x = %s%s%s%s%s%s%s%s) (caps = 0x%04x)\n",
1385 userinfo->sn, userinfo->flags,
1386 (userinfo->flags&AIM_FLAG_UNCONFIRMED)?" UNCONFIRMED":"",
1387 (userinfo->flags&AIM_FLAG_ADMINISTRATOR)?" ADMINISTRATOR":"",
1388 (userinfo->flags&AIM_FLAG_AOL)?" AOL":"",
1389 (userinfo->flags&AIM_FLAG_OSCAR_PAY)?" OSCAR_PAY":"",
1390 (userinfo->flags&AIM_FLAG_FREE)?" FREE":"",
1391 (userinfo->flags&AIM_FLAG_AWAY)?" AWAY":"",
1392 (userinfo->flags&AIM_FLAG_UNKNOWN40)?" UNKNOWN40":"",
1393 (userinfo->flags&AIM_FLAG_UNKNOWN80)?" UNKNOWN80":"",
1394 userinfo->capabilities);
1399 static int faimtest_parse_genericerr(aim_session_t *sess, aim_frame_t *fr, ...)
1405 reason = va_arg(ap, fu16_t);
1408 dvprintf("faimtest: snac threw error (reason 0x%04x: %s)\n", reason, (reason<msgerrreasonslen)?msgerrreasons[reason]:"unknown");
1413 static int faimtest_parse_msgerr(aim_session_t *sess, aim_frame_t *fr, ...)
1420 reason = va_arg(ap, fu16_t);
1421 destsn = va_arg(ap, char *);
1424 dvprintf("faimtest: message to %s bounced (reason 0x%04x: %s)\n", destsn, reason, (reason<msgerrreasonslen)?msgerrreasons[reason]:"unknown");
1429 static int faimtest_parse_locerr(aim_session_t *sess, aim_frame_t *fr, ...)
1436 reason = va_arg(ap, fu16_t);
1437 destsn = va_arg(ap, char *);
1440 dvprintf("faimtest: user information for %s unavailable (reason 0x%04x: %s)\n", destsn, reason, (reason<msgerrreasonslen)?msgerrreasons[reason]:"unknown");
1445 static int faimtest_parse_misses(aim_session_t *sess, aim_frame_t *fr, ...)
1447 static char *missedreasons[] = {
1449 "Message too large",
1454 static int missedreasonslen = 5;
1457 fu16_t chan, nummissed, reason;
1458 struct aim_userinfo_s *userinfo;
1461 chan = va_arg(ap, fu16_t);
1462 userinfo = va_arg(ap, struct aim_userinfo_s *);
1463 nummissed = va_arg(ap, fu16_t);
1464 reason = va_arg(ap, fu16_t);
1467 dvprintf("faimtest: missed %d messages from %s on channel %d (reason %d: %s)\n", nummissed, userinfo->sn, chan, reason, (reason<missedreasonslen)?missedreasons[reason]:"unknown");
1473 * Received in response to an IM sent with the AIM_IMFLAGS_ACK option.
1475 static int faimtest_parse_msgack(aim_session_t *sess, aim_frame_t *fr, ...)
1482 type = va_arg(ap, fu16_t);
1483 sn = va_arg(ap, char *);
1486 dvprintf("faimtest: msgack: 0x%04x / %s\n", type, sn);
1491 static int faimtest_parse_ratechange(aim_session_t *sess, aim_frame_t *fr, ...)
1493 static char *codes[5] = {
1501 fu16_t code, rateclass;
1502 fu32_t windowsize, clear, alert, limit, disconnect;
1503 fu32_t currentavg, maxavg;
1507 /* See code explanations below */
1508 code = va_arg(ap, fu16_t);
1511 * See comments above aim_parse_ratechange_middle() in aim_rxhandlers.c.
1513 rateclass = va_arg(ap, fu16_t);
1516 * Not sure what this is exactly. I think its the temporal
1517 * relation factor (ie, how to make the rest of the numbers
1518 * make sense in the real world).
1520 windowsize = va_arg(ap, fu32_t);
1522 /* Explained below */
1523 clear = va_arg(ap, fu32_t);
1524 alert = va_arg(ap, fu32_t);
1525 limit = va_arg(ap, fu32_t);
1526 disconnect = va_arg(ap, fu32_t);
1527 currentavg = va_arg(ap, fu32_t);
1528 maxavg = va_arg(ap, fu32_t);
1533 dvprintf("faimtest: rate %s (rate class 0x%04x): curavg = %ld, maxavg = %ld, alert at %ld, clear warning at %ld, limit at %ld, disconnect at %ld (window size = %ld)\n",
1534 (code < 5)?codes[code]:"invalid",
1541 if (code == AIM_RATE_CODE_CHANGE) {
1543 * Not real sure when these get sent.
1545 if (currentavg >= clear)
1546 aim_conn_setlatency(fr->conn, 0);
1548 } else if (code == AIM_RATE_CODE_WARNING) {
1550 * We start getting WARNINGs the first time we go below the
1551 * 'alert' limit (currentavg < alert) and they stop when
1552 * either we pause long enough for currentavg to go above
1553 * 'clear', or until we flood it bad enough to go below
1554 * 'limit' (and start getting LIMITs instead) or even further
1555 * and go below 'disconnect' and get disconnected completely
1556 * (and won't be able to login right away either).
1558 aim_conn_setlatency(fr->conn, windowsize/4); /* XXX this is bogus! */
1560 } else if (code == AIM_RATE_CODE_LIMIT) {
1562 * When we hit LIMIT, messages will start getting dropped.
1564 aim_conn_setlatency(fr->conn, windowsize/2); /* XXX this is bogus! */
1566 } else if (code == AIM_RATE_CODE_CLEARLIMIT) {
1568 * The limit is cleared when curavg goes above 'clear'.
1570 aim_conn_setlatency(fr->conn, 0);
1576 static int faimtest_parse_evilnotify(aim_session_t *sess, aim_frame_t *fr, ...)
1580 struct aim_userinfo_s *userinfo;
1583 newevil = va_arg(ap, fu16_t);
1584 userinfo = va_arg(ap, struct aim_userinfo_s *);
1588 * Evil Notifications that are lacking userinfo->sn are anon-warns
1589 * if they are an evil increases, but are not warnings at all if its
1590 * a decrease (its the natural backoff happening).
1592 * newevil is passed as an int representing the new evil value times
1595 dvprintf("faimtest: evil level change: new value = %2.1f%% (caused by %s)\n", ((float)newevil)/10, (userinfo && strlen(userinfo->sn))?userinfo->sn:"anonymous");
1600 static int faimtest_parse_searchreply(aim_session_t *sess, aim_frame_t *fr, ...)
1603 char *address, *SNs;
1607 address = va_arg(ap, char *);
1608 num = va_arg(ap, int);
1609 SNs = va_arg(ap, char *);
1612 dvprintf("faimtest: E-Mail Search Results for %s: ", address);
1614 for(i = 0; i < num; i++)
1615 dvinlineprintf("%s, ", &SNs[i*(MAXSNLEN+1)]);
1616 dinlineprintf("\n");
1621 static int faimtest_parse_searcherror(aim_session_t *sess, aim_frame_t *fr, ...)
1627 address = va_arg(ap, char *);
1630 dvprintf("faimtest: E-Mail Search Results for %s: No Results or Invalid Email\n", address);
1635 void addcb_bos(aim_session_t *sess, aim_conn_t *bosconn)
1638 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE, faimtest_conncomplete, 0);
1640 aim_conn_addhandler(sess, bosconn, 0x0009, 0x0003, faimtest_bosrights, 0);
1641 aim_conn_addhandler(sess, bosconn, 0x0001, 0x0007, faimtest_rateresp_bos, 0); /* rate info */
1642 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, 0x0018, faimtest_hostversions, 0);
1643 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_SERVERREADY, faimtest_serverready, 0);
1644 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_RATEINFO, NULL, 0);
1645 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_REDIRECT, faimtest_handleredirect, 0);
1646 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_STS, AIM_CB_STS_SETREPORTINTERVAL, faimtest_reportinterval, 0);
1647 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_RIGHTSINFO, faimtest_parse_buddyrights, 0);
1648 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_MOTD, faimtest_parse_motd, 0);
1649 aim_conn_addhandler(sess, bosconn, 0x0004, 0x0005, faimtest_icbmparaminfo, 0);
1650 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, faimtest_parse_connerr, 0);
1651 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_RIGHTSINFO, faimtest_locrights, 0);
1652 aim_conn_addhandler(sess, bosconn, 0x0001, 0x001f, faimtest_memrequest, 0);
1653 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_ONCOMING, faimtest_parse_oncoming, 0);
1654 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_OFFGOING, faimtest_parse_offgoing, 0);
1655 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_INCOMING, faimtest_parse_incoming_im, 0);
1656 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_ERROR, faimtest_parse_locerr, 0);
1657 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MISSEDCALL, faimtest_parse_misses, 0);
1658 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_RATECHANGE, faimtest_parse_ratechange, 0);
1659 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_EVIL, faimtest_parse_evilnotify, 0);
1660 aim_conn_addhandler(sess, bosconn, 0x000a, 0x0001, faimtest_parse_searcherror, 0);
1661 aim_conn_addhandler(sess, bosconn, 0x000a, 0x0003, faimtest_parse_searchreply, 0);
1662 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ERROR, faimtest_parse_msgerr, 0);
1663 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO, faimtest_parse_userinfo, 0);
1664 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ACK, faimtest_parse_msgack, 0);
1666 aim_conn_addhandler(sess, bosconn, 0x0001, 0x0001, faimtest_parse_genericerr, 0);
1667 aim_conn_addhandler(sess, bosconn, 0x0003, 0x0001, faimtest_parse_genericerr, 0);
1668 aim_conn_addhandler(sess, bosconn, 0x0009, 0x0001, faimtest_parse_genericerr, 0);
1670 #ifdef MID_REWROTE_ALL_THE_CRAP
1671 aim_conn_addhandler(sess, bosconn, 0xffff, 0xffff, faimtest_parse_unknown, 0);