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 */
404 static int faimtest_rateresp_auth(aim_session_t *sess, aim_frame_t *fr, ...)
407 aim_bos_ackrateresp(sess, fr->conn);
408 aim_auth_clientready(sess, fr->conn);
410 dprintf("faimtest: connected to authorization/admin service\n");
415 int faimtest_accountconfirm(aim_session_t *sess, aim_frame_t *fr, ...)
421 status = va_arg(ap, int); /* status code of confirmation request */
424 dvprintf("account confirmation returned status 0x%04x (%s)\n", status, (status==0x0000)?"email sent":"unknown");
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.
437 int faimtest_parse_unknown(aim_session_t *sess, aim_frame_t *fr, ...)
441 aim_bstream_rewind(&fr->data); /* boo! */
443 dprintf("\nReceived unknown packet:");
444 for (i = 0; aim_bstream_empty(&fr->data); i++) {
446 dinlineprintf("\n\t");
447 dvinlineprintf("0x%2x ", aimbs_get8(&fr->data));
449 dinlineprintf("\n\n");
455 static int faimtest_infochange(aim_session_t *sess, aim_frame_t *fr, ...)
457 fu16_t change = 0, perms, type;
463 change = va_arg(ap, int);
464 perms = va_arg(ap, fu16_t);
465 type = va_arg(ap, fu16_t);
466 length = va_arg(ap, int);
467 val = va_arg(ap, char *);
468 str = va_arg(ap, int);
471 dvprintf("info%s: perms = %d, type = %x, length = %d, val = %s\n", change?" change":"", perms, type, length, str?val:"(not string)");
477 int faimtest_handleredirect(aim_session_t *sess, aim_frame_t *fr, ...)
485 serviceid = va_arg(ap, int);
486 ip = va_arg(ap, char *);
487 cookie = va_arg(ap, fu8_t *);
489 if (serviceid == 0x0005) { /* Adverts */
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");
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");
506 } else if (serviceid == 0x0007) { /* Authorizer */
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");
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");
525 } else if (serviceid == 0x000d) { /* ChatNav */
527 chatnav_redirect(sess, ip, cookie);
529 } else if (serviceid == 0x000e) { /* Chat */
530 char *roomname = NULL;
533 roomname = va_arg(ap, char *);
534 exchange = va_arg(ap, int);
536 chat_redirect(sess, ip, cookie, roomname, exchange);
539 dvprintf("uh oh... got redirect for unknown service 0x%04x!!\n", serviceid);
547 static int faimtest_rateresp_bos(aim_session_t *sess, aim_frame_t *fr, ...)
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"};
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);
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);
564 /* send the buddy list and profile (required, even if empty) */
565 aim_bos_setbuddylist(sess, fr->conn, buddies);
567 aim_reqicbmparams(sess, fr->conn);
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);
577 static int faimtest_icbmparaminfo(aim_session_t *sess, aim_frame_t *fr, ...)
579 struct aim_icbmparameters *params;
583 params = va_arg(ap, struct aim_icbmparameters *);
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);
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
594 params->maxmsglen = 8000;
595 params->minmsginterval = 0; /* in milliseconds */
597 aim_seticbmparam(sess, fr->conn, params);
602 static int faimtest_hostversions(aim_session_t *sess, aim_frame_t *fr, ...)
609 vercount = va_arg(ap, int); /* number of family/version pairs */
610 versions = va_arg(ap, fu8_t *);
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 */);
624 static int faimtest_parse_buddyrights(aim_session_t *sess, aim_frame_t *fr, ...)
627 fu16_t maxbuddies, maxwatchers;
630 maxbuddies = va_arg(ap, int);
631 maxwatchers = va_arg(ap, int);
634 dvprintf("buddy list rights: Max buddies = %d / Max watchers = %d\n", maxbuddies, maxwatchers);
639 static int faimtest_bosrights(aim_session_t *sess, aim_frame_t *fr, ...)
642 fu16_t maxpermits, maxdenies;
645 maxpermits = va_arg(ap, int);
646 maxdenies = va_arg(ap, int);
649 dvprintf("BOS rights: Max permit = %d / Max deny = %d\n", maxpermits, maxdenies);
651 aim_bos_clientready(sess, fr->conn);
653 dprintf("officially connected to BOS.\n");
658 static int faimtest_locrights(aim_session_t *sess, aim_frame_t *fr, ...)
664 maxsiglen = va_arg(ap, int);
667 dvprintf("locate rights: max signature length = %d\n", maxsiglen);
672 static int faimtest_reportinterval(aim_session_t *sess, aim_frame_t *fr, ...)
674 struct faimtest_priv *priv = (struct faimtest_priv *)sess->aux_data;
679 interval = va_arg(ap, int);
682 dvprintf("minimum report interval: %d (seconds?)\n", interval);
684 if (!priv->connected)
688 aim_bos_reqservice(sess, fr->conn, 0x0005); /* adverts */
689 aim_bos_reqservice(sess, fr->conn, 0x000f); /* user directory */
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);
696 aim_reqicbmparams(sess, fr->conn);
701 static int faimtest_parse_motd(aim_session_t *sess, aim_frame_t *fr, ...)
703 static char *codes[] = {
710 static int codeslen = 5;
716 id = va_arg(ap, int);
717 msg = va_arg(ap, char *);
720 dvprintf("motd: %s (%d / %s)\n", msg, id, (id < codeslen)?codes[id]:"unknown");
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
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
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
741 * Note to Bosco's Brigade: if you'd like to break this, put the
742 * module name on an invalid request.
745 static int getaimdata(aim_session_t *sess, unsigned char **bufret, int *buflenret, unsigned long offset, unsigned long len, const char *modname)
747 struct faimtest_priv *priv = (struct faimtest_priv *)sess->aux_data;
749 static const char defaultmod[] = "aim.exe";
750 char *filename = NULL;
755 if (!bufret || !buflenret)
760 if (!(filename = malloc(strlen(priv->aimbinarypath)+1+strlen(modname)+4+1))) {
761 dperror("memrequest: malloc");
765 sprintf(filename, "%s/%s.ocm", priv->aimbinarypath, modname);
769 if (!(filename = malloc(strlen(priv->aimbinarypath)+1+strlen(defaultmod)+1))) {
770 dperror("memrequest: malloc");
774 sprintf(filename, "%s/%s", priv->aimbinarypath, defaultmod);
778 if (stat(filename, &st) == -1) {
780 dperror("memrequest: stat");
788 if ((offset > st.st_size) || (len > st.st_size))
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;
802 free(filename); /* not needed */
804 dvprintf("memrequest: recieved invalid request for 0x%08lx bytes at 0x%08lx (file %s)\n", len, offset, modname);
808 i += strlen(modname);
810 if (!(buf = malloc(i)))
816 memcpy(buf, modname, strlen(modname));
817 i += strlen(modname);
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;
835 if (!(buf = malloc(len))) {
840 dvprintf("memrequest: loading %ld bytes from 0x%08lx in \"%s\"...\n", len, offset, filename);
842 if (!(f = fopen(filename, "r"))) {
843 dperror("memrequest: fopen");
851 if (fseek(f, offset, SEEK_SET) == -1) {
852 dperror("memrequest: fseek");
858 if (fread(buf, len, 1, f) != 1) {
859 dperror("memrequest: fread");
872 return 0; /* success! */
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.
882 static int faimtest_memrequest(aim_session_t *sess, aim_frame_t *fr, ...)
884 struct faimtest_priv *priv = (struct faimtest_priv *)sess->aux_data;
892 offset = va_arg(ap, fu32_t);
893 len = va_arg(ap, fu32_t);
894 modname = va_arg(ap, char *);
897 if (priv->aimbinarypath && (getaimdata(sess, &buf, &buflen, offset, len, modname) == 0)) {
899 aim_sendmemblock(sess, fr->conn, offset, buflen, buf, AIM_SENDMEMBLOCK_FLAG_ISREQUEST);
905 dvprintf("memrequest: unable to use AIM binary (\"%s/%s\"), sending defaults...\n", priv->aimbinarypath, modname);
907 aim_sendmemblock(sess, fr->conn, offset, len, NULL, AIM_SENDMEMBLOCK_FLAG_ISREQUEST);
914 static void printuserflags(fu16_t flags)
916 if (flags & AIM_FLAG_UNCONFIRMED)
917 dinlineprintf("UNCONFIRMED ");
918 if (flags & AIM_FLAG_ADMINISTRATOR)
919 dinlineprintf("ADMINISTRATOR ");
920 if (flags & AIM_FLAG_AOL)
921 dinlineprintf("AOL ");
922 if (flags & AIM_FLAG_OSCAR_PAY)
923 dinlineprintf("OSCAR_PAY ");
924 if (flags & AIM_FLAG_FREE)
925 dinlineprintf("FREE ");
926 if (flags & AIM_FLAG_AWAY)
927 dinlineprintf("AWAY ");
928 if (flags & AIM_FLAG_UNKNOWN40)
929 dinlineprintf("ICQ? ");
930 if (flags & AIM_FLAG_UNKNOWN80)
931 dinlineprintf("UNKNOWN80 ");
935 static int faimtest_parse_userinfo(aim_session_t *sess, aim_frame_t *fr, ...)
937 struct aim_userinfo_s *userinfo;
938 char *prof_encoding = NULL;
944 userinfo = va_arg(ap, struct aim_userinfo_s *);
945 prof_encoding = va_arg(ap, char *);
946 prof = va_arg(ap, char *);
947 inforeq = va_arg(ap, fu16_t);
950 dvprintf("faimtest: userinfo: sn: %s\n", userinfo->sn);
951 dvprintf("faimtest: userinfo: warnlevel: 0x%04x\n", userinfo->warnlevel);
952 dvprintf("faimtest: userinfo: flags: 0x%04x = ", userinfo->flags);
953 printuserflags(userinfo->flags);
956 dvprintf("faimtest: userinfo: membersince: %lu\n", userinfo->membersince);
957 dvprintf("faimtest: userinfo: onlinesince: %lu\n", userinfo->onlinesince);
958 dvprintf("faimtest: userinfo: idletime: 0x%04x\n", userinfo->idletime);
960 if (inforeq == AIM_GETINFO_GENERALINFO) {
961 dvprintf("faimtest: userinfo: profile_encoding: %s\n", prof_encoding ? prof_encoding : "[none]");
962 dvprintf("faimtest: userinfo: prof: %s\n", prof ? prof : "[none]");
963 } else if (inforeq == AIM_GETINFO_AWAYMESSAGE) {
964 dvprintf("faimtest: userinfo: awaymsg_encoding: %s\n", prof_encoding ? prof_encoding : "[none]");
965 dvprintf("faimtest: userinfo: awaymsg: %s\n", prof ? prof : "[none]");
967 dprintf("faimtest: userinfo: unknown info request\n");
972 static int faimtest_handlecmd(aim_session_t *sess, aim_conn_t *conn, struct aim_userinfo_s *userinfo, const char *tmpstr)
974 struct faimtest_priv *priv = (struct faimtest_priv *)sess->aux_data;
976 if (!strncmp(tmpstr, "disconnect", 10)) {
980 } else if (strstr(tmpstr, "goodday")) {
982 aim_send_im(sess, conn, userinfo->sn, AIM_IMFLAGS_ACK, "Good day to you too.");
984 } else if (strstr(tmpstr, "haveicon") && priv->buddyicon) {
985 struct aim_sendimext_args args;
986 static const char iconmsg[] = {"I have an icon"};
988 args.destsn = userinfo->sn;
989 args.flags = AIM_IMFLAGS_HASICON;
991 args.msglen = strlen(iconmsg);
992 args.iconlen = priv->buddyiconlen;
993 args.iconstamp = priv->buddyiconstamp;
994 args.iconsum = priv->buddyiconsum;
996 aim_send_im_ext(sess, conn, &args);
998 } else if (strstr(tmpstr, "sendbin")) {
999 struct aim_sendimext_args args;
1000 static const unsigned char data[] = {
1001 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1002 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
1003 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
1004 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
1005 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
1006 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
1010 * I put this here as a demonstration of how to send
1011 * arbitrary binary data via OSCAR ICBM's without the need
1012 * for escape or baseN encoding of any sort.
1014 * Apparently if you set the charset to something WinAIM
1015 * doesn't recognize, it will completly ignore the message.
1016 * That is, it will not display anything in the conversation
1017 * window for the user that recieved it.
1019 * HOWEVER, if they do not have a conversation window open
1020 * for you, a new one will be created, but it will not have
1021 * any messages in it. Therefore sending these things could
1022 * be a great way to seemingly subliminally convince people
1026 args.destsn = userinfo->sn;
1027 args.flags = AIM_IMFLAGS_CUSTOMCHARSET;
1028 args.charset = args.charsubset = 0x4242;
1030 args.msglen = sizeof(data);
1032 aim_send_im_ext(sess, conn, &args);
1034 } else if (strstr(tmpstr, "sendmulti")) {
1035 struct aim_sendimext_args args;
1037 static const fu16_t unidata[] = { /* "UNICODE." */
1038 0x0055, 0x004e, 0x0049, 0x0043,
1039 0x004f, 0x0044, 0x0045, 0x002e,
1041 static const int unidatalen = 8;
1044 * This is how multipart messages should be sent.
1046 * This should render as:
1047 * "Part 1, ASCII. UNICODE.Part 3, ASCII. "
1050 aim_mpmsg_init(sess, &mpm);
1052 aim_mpmsg_addascii(sess, &mpm, "Part 1, ASCII. ");
1053 aim_mpmsg_addunicode(sess, &mpm, unidata, unidatalen);
1054 aim_mpmsg_addascii(sess, &mpm, "Part 3, ASCII. ");
1056 args.destsn = userinfo->sn;
1057 args.flags = AIM_IMFLAGS_MULTIPART;
1060 aim_send_im_ext(sess, conn, &args);
1062 aim_mpmsg_free(sess, &mpm);
1064 } else if (strstr(tmpstr, "sendprebin")) {
1065 static const unsigned char data[] = {
1066 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1067 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
1068 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
1069 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
1070 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
1071 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
1073 struct aim_sendimext_args args;
1077 * This demonstrates sending a human-readable preamble,
1078 * and then arbitrary binary data.
1080 * This means that you can very inconspicuously send binary
1081 * attachments to other users. In WinAIM, this appears as
1082 * though it only had the ASCII portion.
1086 aim_mpmsg_init(sess, &mpm);
1088 aim_mpmsg_addascii(sess, &mpm, "This message has binary data.");
1089 aim_mpmsg_addraw(sess, &mpm, 0x4242, 0x4242, data, sizeof(data));
1091 args.destsn = userinfo->sn;
1092 args.flags = AIM_IMFLAGS_MULTIPART;
1095 aim_send_im_ext(sess, conn, &args);
1097 aim_mpmsg_free(sess, &mpm);
1099 } else if (strstr(tmpstr, "havefeat")) {
1100 struct aim_sendimext_args args;
1101 static const char featmsg[] = {"I have nifty features."};
1102 fu8_t features[] = {0x01, 0x01, 0x01, 0x02, 0x42, 0x43, 0x44, 0x45};
1104 args.destsn = userinfo->sn;
1105 args.flags = AIM_IMFLAGS_CUSTOMFEATURES;
1107 args.msglen = strlen(featmsg);
1108 args.features = features;
1109 args.featureslen = sizeof(features);
1111 aim_send_im_ext(sess, conn, &args);
1113 } else if (strstr(tmpstr, "sendicon") && priv->buddyicon) {
1115 aim_send_icon(sess, conn, userinfo->sn, priv->buddyicon, priv->buddyiconlen, priv->buddyiconstamp, priv->buddyiconsum);
1117 } else if (strstr(tmpstr, "warnme")) {
1119 dprintf("faimtest: icbm: sending non-anon warning\n");
1120 aim_send_warning(sess, conn, userinfo->sn, 0);
1122 } else if (strstr(tmpstr, "anonwarn")) {
1124 dprintf("faimtest: icbm: sending anon warning\n");
1125 aim_send_warning(sess, conn, userinfo->sn, AIM_WARN_ANON);
1127 } else if (strstr(tmpstr, "setdirectoryinfo")) {
1129 dprintf("faimtest: icbm: sending backwards profile data\n");
1130 aim_setdirectoryinfo(sess, conn, "tsrif", "elddim", "tsal", "nediam", "emankcin", "teerts", "ytic", "etats", "piz", 0, 1);
1132 } else if (strstr(tmpstr, "setinterests")) {
1134 dprintf("faimtest: icbm: setting fun interests\n");
1135 aim_setuserinterests(sess, conn, "interest1", "interest2", "interest3", "interest4", "interest5", 1);
1137 } else if (!strncmp(tmpstr, "getfile", 7)) {
1139 if (!priv->ohcaptainmycaptain) {
1141 aim_send_im(sess, conn, userinfo->sn, AIM_IMFLAGS_ACK, "I have no owner!");
1144 getfile_start(sess, conn, (strlen(tmpstr) < 8)?priv->ohcaptainmycaptain:tmpstr+8);
1146 } else if (!strncmp(tmpstr, "open chatnav", 12)) {
1148 aim_bos_reqservice(sess, conn, AIM_CONN_TYPE_CHATNAV);
1150 } else if (!strncmp(tmpstr, "create", 6)) {
1152 aim_chatnav_createroom(sess,aim_getconn_type(sess, AIM_CONN_TYPE_CHATNAV), (strlen(tmpstr) < 7)?"WorldDomination":tmpstr+7, 0x0004);
1154 } else if (!strncmp(tmpstr, "close chatnav", 13)) {
1155 aim_conn_t *chatnavconn;
1157 if ((chatnavconn = aim_getconn_type(sess, AIM_CONN_TYPE_CHATNAV)))
1158 aim_conn_kill(sess, &chatnavconn);
1160 } else if (!strncmp(tmpstr, "join", 4)) {
1162 aim_chat_join(sess, conn, 0x0004, "worlddomination", 0x0000);
1164 } else if (!strncmp(tmpstr, "leave", 5)) {
1166 aim_chat_leaveroom(sess, "worlddomination");
1168 } else if (!strncmp(tmpstr, "getinfo", 7)) {
1170 aim_getinfo(sess, conn, "75784102", AIM_GETINFO_GENERALINFO);
1171 aim_getinfo(sess, conn, "15853637", AIM_GETINFO_AWAYMESSAGE);
1172 aim_getinfo(sess, conn, "midendian", AIM_GETINFO_GENERALINFO);
1173 aim_getinfo(sess, conn, "midendian", AIM_GETINFO_AWAYMESSAGE);
1175 } else if (strstr(tmpstr, "open directim")) {
1177 directim_start(sess, conn, userinfo->sn);
1179 } else if(strstr(tmpstr, "lookup")) {
1181 aim_usersearch_address(sess, conn, "mid@auk.cx");
1183 } else if (!strncmp(tmpstr, "reqsendmsg", 10)) {
1185 aim_send_im(sess, conn, priv->ohcaptainmycaptain, 0, "sendmsg 7900");
1187 } else if (!strncmp(tmpstr, "reqauth", 7)) {
1189 aim_bos_reqservice(sess, conn, AIM_CONN_TYPE_AUTH);
1191 } else if (!strncmp(tmpstr, "reqconfirm", 10)) {
1193 aim_auth_reqconfirm(sess, aim_getconn_type(sess, AIM_CONN_TYPE_AUTH));
1195 } else if (!strncmp(tmpstr, "reqemail", 8)) {
1197 aim_auth_getinfo(sess, aim_getconn_type(sess, AIM_CONN_TYPE_AUTH), 0x0011);
1199 } else if (!strncmp(tmpstr, "changepass", 8)) {
1201 aim_auth_changepasswd(sess, aim_getconn_type(sess, AIM_CONN_TYPE_AUTH), "NEWPASSWORD", "OLDPASSWORD");
1203 } else if (!strncmp(tmpstr, "setemail", 8)) {
1205 aim_auth_setemail(sess, aim_getconn_type(sess, AIM_CONN_TYPE_AUTH), "NEWEMAILADDRESS");
1207 } else if (!strncmp(tmpstr, "sendmsg", 7)) {
1215 newbuf = malloc(i+1);
1216 for (z = 0; z < i; z++)
1217 newbuf[z] = (z % 10)+0x30;
1219 aim_send_im(sess, conn, userinfo->sn, 0, newbuf);
1225 dprintf("unknown command.\n");
1226 aim_add_buddy(sess, conn, userinfo->sn);
1234 * Channel 1: Standard Message
1236 static int faimtest_parse_incoming_im_chan1(aim_session_t *sess, aim_conn_t *conn, struct aim_userinfo_s *userinfo, va_list ap)
1238 struct faimtest_priv *priv = (struct faimtest_priv *)sess->aux_data;
1240 struct aim_incomingim_ch1_args *args;
1241 int clienttype = AIM_CLIENTTYPE_UNKNOWN;
1242 char realmsg[8192+1] = {""};
1244 args = va_arg(ap, struct aim_incomingim_ch1_args *);
1247 clienttype = aim_fingerprintclient(args->features, args->featureslen);
1249 dvprintf("faimtest: icbm: sn = \"%s\"\n", userinfo->sn);
1250 dvprintf("faimtest: icbm: probable client type: %d\n", clienttype);
1251 dvprintf("faimtest: icbm: warnlevel = 0x%04x\n", userinfo->warnlevel);
1252 dvprintf("faimtest: icbm: flags = 0x%04x = ", userinfo->flags);
1253 printuserflags(userinfo->flags);
1254 dinlineprintf("\n");
1256 dvprintf("faimtest: icbm: membersince = %lu\n", userinfo->membersince);
1257 dvprintf("faimtest: icbm: onlinesince = %lu\n", userinfo->onlinesince);
1258 dvprintf("faimtest: icbm: idletime = 0x%04x\n", userinfo->idletime);
1259 dvprintf("faimtest: icbm: capabilities = 0x%04x\n", userinfo->capabilities);
1261 dprintf("faimtest: icbm: icbmflags = ");
1262 if (args->icbmflags & AIM_IMFLAGS_AWAY)
1263 dinlineprintf("away ");
1264 if (args->icbmflags & AIM_IMFLAGS_ACK)
1265 dinlineprintf("ackrequest ");
1266 if (args->icbmflags & AIM_IMFLAGS_BUDDYREQ)
1267 dinlineprintf("buddyreq ");
1268 if (args->icbmflags & AIM_IMFLAGS_HASICON)
1269 dinlineprintf("hasicon ");
1270 dinlineprintf("\n");
1272 if (args->icbmflags & AIM_IMFLAGS_CUSTOMCHARSET)
1273 dvprintf("faimtest: icbm: encoding flags = {%04x, %04x}\n", args->charset, args->charsubset);
1276 * Quickly convert it to eight bit format, replacing non-ASCII UNICODE
1277 * characters with their equivelent HTML entity.
1279 if (args->icbmflags & AIM_IMFLAGS_UNICODE) {
1282 for (i = 0; i < args->msglen; i += 2) {
1285 uni = ((args->msg[i] & 0xff) << 8) | (args->msg[i+1] & 0xff);
1287 if ((uni < 128) || ((uni >= 160) && (uni <= 255))) { /* ISO 8859-1 */
1289 snprintf(realmsg+strlen(realmsg), sizeof(realmsg)-strlen(realmsg), "%c", uni);
1291 } else { /* something else, do UNICODE entity */
1293 snprintf(realmsg+strlen(realmsg), sizeof(realmsg)-strlen(realmsg), "&#%04x;", uni);
1302 * For non-UNICODE encodings (ASCII and ISO 8859-1), there is
1303 * no need to do anything special here. Most
1304 * terminals/whatever will be able to display such characters
1307 * Beware that PC-ASCII 128 through 159 are _not_ actually
1308 * defined in ASCII or ISO 8859-1, and you should send them as
1309 * UNICODE. WinAIM will send these characters in a UNICODE
1310 * message, so you need to do so as well.
1312 * You may not think it necessary to handle UNICODE messages.
1313 * You're probably wrong. For one thing, Microsoft "Smart
1314 * Quotes" will be sent by WinAIM as UNICODE (not HTML UNICODE,
1315 * but real UNICODE). If you don't parse UNICODE at all, your
1316 * users will get a blank message instead of the message
1317 * containing Smart Quotes.
1320 strncpy(realmsg, args->msg, sizeof(realmsg));
1323 dvprintf("faimtest: icbm: message: %s\n", realmsg);
1325 if (args->icbmflags & AIM_IMFLAGS_MULTIPART) {
1326 aim_mpmsg_section_t *sec;
1329 dvprintf("faimtest: icbm: multipart: this message has %d parts\n", args->mpmsg.numparts);
1331 for (sec = args->mpmsg.parts, z = 0; sec; sec = sec->next, z++) {
1332 if ((sec->charset == 0x0000) || (sec->charset == 0x0003) || (sec->charset == 0xffff)) {
1333 dvprintf("faimtest: icbm: multipart: part %d: charset 0x%04x, subset 0x%04x, msg = %s\n", z, sec->charset, sec->charsubset, sec->data);
1335 dvprintf("faimtest: icbm: multipart: part %d: charset 0x%04x, subset 0x%04x, binary or UNICODE data\n", z, sec->charset, sec->charsubset);
1340 if (args->icbmflags & AIM_IMFLAGS_HASICON)
1341 aim_send_im(sess, conn, userinfo->sn, AIM_IMFLAGS_BUDDYREQ, "You have an icon");
1346 while (realmsg[i] == '<') {
1347 if (realmsg[i] == '<') {
1348 while (realmsg[i] != '>')
1355 faimtest_handlecmd(sess, conn, userinfo, tmpstr);
1359 if (priv->buddyicon && (args->icbmflags & AIM_IMFLAGS_BUDDYREQ))
1360 aim_send_icon(sess, conn, userinfo->sn, priv->buddyicon, priv->buddyiconlen, priv->buddyiconstamp, priv->buddyiconsum);
1366 * Channel 2: Rendevous Request
1368 static int faimtest_parse_incoming_im_chan2(aim_session_t *sess, aim_conn_t *conn, struct aim_userinfo_s *userinfo, va_list ap)
1370 struct aim_incomingim_ch2_args *args;
1372 args = va_arg(ap, struct aim_incomingim_ch2_args *);
1375 if (args->reqclass == AIM_CAPS_VOICE) {
1377 dvprintf("faimtest: voice invitation: source sn = %s\n", userinfo->sn);
1378 dvprintf("faimtest: voice invitation: \twarnlevel = 0x%04x\n", userinfo->warnlevel);
1379 dvprintf("faimtest: voice invitation: \tclass = 0x%04x = ", userinfo->flags);
1380 printuserflags(userinfo->flags);
1381 dinlineprintf("\n");
1383 dvprintf("faimtest: voice invitation: \tonlinesince = %lu\n", userinfo->onlinesince);
1384 dvprintf("faimtest: voice invitation: \tidletime = 0x%04x\n", userinfo->idletime);
1386 } else if (args->reqclass == AIM_CAPS_GETFILE) {
1388 getfile_requested(sess, conn, userinfo, args);
1390 } else if (args->reqclass == AIM_CAPS_SENDFILE) {
1392 dprintf("faimtest: send file!\n");
1394 } else if (args->reqclass == AIM_CAPS_CHAT) {
1396 dvprintf("faimtest: chat invitation: source sn = %s\n", userinfo->sn);
1397 dvprintf("faimtest: chat invitation: \twarnlevel = 0x%04x\n", userinfo->warnlevel);
1398 dvprintf("faimtest: chat invitation: \tclass = 0x%04x = ", userinfo->flags);
1399 printuserflags(userinfo->flags);
1400 dinlineprintf("\n");
1402 /* we dont get membersince on chat invites! */
1403 dvprintf("faimtest: chat invitation: \tonlinesince = %lu\n", userinfo->onlinesince);
1404 dvprintf("faimtest: chat invitation: \tidletime = 0x%04x\n", userinfo->idletime);
1406 dvprintf("faimtest: chat invitation: message = %s\n", args->info.chat.msg);
1407 dvprintf("faimtest: chat invitation: room name = %s\n", args->info.chat.roominfo.name);
1408 dvprintf("faimtest: chat invitation: encoding = %s\n", args->info.chat.encoding);
1409 dvprintf("faimtest: chat invitation: language = %s\n", args->info.chat.lang);
1410 dvprintf("faimtest: chat invitation: exchange = 0x%04x\n", args->info.chat.roominfo.exchange);
1411 dvprintf("faimtest: chat invitation: instance = 0x%04x\n", args->info.chat.roominfo.instance);
1412 dvprintf("faimtest: chat invitiation: autojoining %s...\n", args->info.chat.roominfo.name);
1414 /* Automatically join room... */
1415 aim_chat_join(sess, conn, args->info.chat.roominfo.exchange, args->info.chat.roominfo.name, args->info.chat.roominfo.instance);
1417 } else if (args->reqclass == AIM_CAPS_IMIMAGE) {
1419 dprintf("faimtest: icbm: rendezvous imimage\n");
1421 directim_requested(sess, conn, userinfo, args);
1423 } else if (args->reqclass == AIM_CAPS_BUDDYICON) {
1425 dvprintf("faimtest: Buddy Icon from %s, length = %lu\n", userinfo->sn, args->info.icon.length);
1429 dvprintf("faimtest: icbm: unknown reqclass (%d)\n", args->reqclass);
1435 static int faimtest_parse_incoming_im(aim_session_t *sess, aim_frame_t *fr, ...)
1438 struct aim_userinfo_s *userinfo;
1443 channel = va_arg(ap, fu16_t);
1444 userinfo = va_arg(ap, struct aim_userinfo_s *);
1447 ret = faimtest_parse_incoming_im_chan1(sess, fr->conn, userinfo, ap);
1448 else if (channel == 2)
1449 ret = faimtest_parse_incoming_im_chan2(sess, fr->conn, userinfo, ap);
1451 dvprintf("unsupported channel 0x%04x\n", channel);
1453 dvprintf("faimtest: icbm: done with ICBM handling (ret = %d)\n", ret);
1458 static int faimtest_parse_oncoming(aim_session_t *sess, aim_frame_t *fr, ...)
1460 struct aim_userinfo_s *userinfo;
1464 userinfo = va_arg(ap, struct aim_userinfo_s *);
1467 dvprintf("%ld %s is now online (flags: %04x = %s%s%s%s%s%s%s%s) (caps = 0x%04x)\n",
1469 userinfo->sn, userinfo->flags,
1470 (userinfo->flags&AIM_FLAG_UNCONFIRMED)?" UNCONFIRMED":"",
1471 (userinfo->flags&AIM_FLAG_ADMINISTRATOR)?" ADMINISTRATOR":"",
1472 (userinfo->flags&AIM_FLAG_AOL)?" AOL":"",
1473 (userinfo->flags&AIM_FLAG_OSCAR_PAY)?" OSCAR_PAY":"",
1474 (userinfo->flags&AIM_FLAG_FREE)?" FREE":"",
1475 (userinfo->flags&AIM_FLAG_AWAY)?" AWAY":"",
1476 (userinfo->flags&AIM_FLAG_UNKNOWN40)?" UNKNOWN40":"",
1477 (userinfo->flags&AIM_FLAG_UNKNOWN80)?" UNKNOWN80":"",
1478 userinfo->capabilities);
1482 static int faimtest_parse_offgoing(aim_session_t *sess, aim_frame_t *fr, ...)
1484 struct aim_userinfo_s *userinfo;
1488 userinfo = va_arg(ap, struct aim_userinfo_s *);
1491 dvprintf("%ld %s is now offline (flags: %04x = %s%s%s%s%s%s%s%s) (caps = 0x%04x)\n",
1493 userinfo->sn, userinfo->flags,
1494 (userinfo->flags&AIM_FLAG_UNCONFIRMED)?" UNCONFIRMED":"",
1495 (userinfo->flags&AIM_FLAG_ADMINISTRATOR)?" ADMINISTRATOR":"",
1496 (userinfo->flags&AIM_FLAG_AOL)?" AOL":"",
1497 (userinfo->flags&AIM_FLAG_OSCAR_PAY)?" OSCAR_PAY":"",
1498 (userinfo->flags&AIM_FLAG_FREE)?" FREE":"",
1499 (userinfo->flags&AIM_FLAG_AWAY)?" AWAY":"",
1500 (userinfo->flags&AIM_FLAG_UNKNOWN40)?" UNKNOWN40":"",
1501 (userinfo->flags&AIM_FLAG_UNKNOWN80)?" UNKNOWN80":"",
1502 userinfo->capabilities);
1507 static int faimtest_parse_genericerr(aim_session_t *sess, aim_frame_t *fr, ...)
1513 reason = va_arg(ap, fu16_t);
1516 dvprintf("faimtest: snac threw error (reason 0x%04x: %s)\n", reason, (reason<msgerrreasonslen)?msgerrreasons[reason]:"unknown");
1521 static int faimtest_parse_msgerr(aim_session_t *sess, aim_frame_t *fr, ...)
1528 reason = va_arg(ap, fu16_t);
1529 destsn = va_arg(ap, char *);
1532 dvprintf("faimtest: message to %s bounced (reason 0x%04x: %s)\n", destsn, reason, (reason<msgerrreasonslen)?msgerrreasons[reason]:"unknown");
1537 static int faimtest_parse_locerr(aim_session_t *sess, aim_frame_t *fr, ...)
1544 reason = va_arg(ap, fu16_t);
1545 destsn = va_arg(ap, char *);
1548 dvprintf("faimtest: user information for %s unavailable (reason 0x%04x: %s)\n", destsn, reason, (reason<msgerrreasonslen)?msgerrreasons[reason]:"unknown");
1553 static int faimtest_parse_misses(aim_session_t *sess, aim_frame_t *fr, ...)
1555 static char *missedreasons[] = {
1557 "Message too large",
1562 static int missedreasonslen = 5;
1565 fu16_t chan, nummissed, reason;
1566 struct aim_userinfo_s *userinfo;
1569 chan = va_arg(ap, fu16_t);
1570 userinfo = va_arg(ap, struct aim_userinfo_s *);
1571 nummissed = va_arg(ap, fu16_t);
1572 reason = va_arg(ap, fu16_t);
1575 dvprintf("faimtest: missed %d messages from %s on channel %d (reason %d: %s)\n", nummissed, userinfo->sn, chan, reason, (reason<missedreasonslen)?missedreasons[reason]:"unknown");
1581 * Received in response to an IM sent with the AIM_IMFLAGS_ACK option.
1583 static int faimtest_parse_msgack(aim_session_t *sess, aim_frame_t *fr, ...)
1590 type = va_arg(ap, fu16_t);
1591 sn = va_arg(ap, char *);
1594 dvprintf("faimtest: msgack: 0x%04x / %s\n", type, sn);
1599 static int faimtest_parse_ratechange(aim_session_t *sess, aim_frame_t *fr, ...)
1601 static char *codes[5] = {
1609 fu16_t code, rateclass;
1610 fu32_t windowsize, clear, alert, limit, disconnect;
1611 fu32_t currentavg, maxavg;
1615 /* See code explanations below */
1616 code = va_arg(ap, fu16_t);
1619 * See comments above aim_parse_ratechange_middle() in aim_rxhandlers.c.
1621 rateclass = va_arg(ap, fu16_t);
1624 * Not sure what this is exactly. I think its the temporal
1625 * relation factor (ie, how to make the rest of the numbers
1626 * make sense in the real world).
1628 windowsize = va_arg(ap, fu32_t);
1630 /* Explained below */
1631 clear = va_arg(ap, fu32_t);
1632 alert = va_arg(ap, fu32_t);
1633 limit = va_arg(ap, fu32_t);
1634 disconnect = va_arg(ap, fu32_t);
1635 currentavg = va_arg(ap, fu32_t);
1636 maxavg = va_arg(ap, fu32_t);
1641 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",
1642 (code < 5)?codes[code]:"invalid",
1649 if (code == AIM_RATE_CODE_CHANGE) {
1651 * Not real sure when these get sent.
1653 if (currentavg >= clear)
1654 aim_conn_setlatency(fr->conn, 0);
1656 } else if (code == AIM_RATE_CODE_WARNING) {
1658 * We start getting WARNINGs the first time we go below the
1659 * 'alert' limit (currentavg < alert) and they stop when
1660 * either we pause long enough for currentavg to go above
1661 * 'clear', or until we flood it bad enough to go below
1662 * 'limit' (and start getting LIMITs instead) or even further
1663 * and go below 'disconnect' and get disconnected completely
1664 * (and won't be able to login right away either).
1666 aim_conn_setlatency(fr->conn, windowsize/4); /* XXX this is bogus! */
1668 } else if (code == AIM_RATE_CODE_LIMIT) {
1670 * When we hit LIMIT, messages will start getting dropped.
1672 aim_conn_setlatency(fr->conn, windowsize/2); /* XXX this is bogus! */
1674 } else if (code == AIM_RATE_CODE_CLEARLIMIT) {
1676 * The limit is cleared when curavg goes above 'clear'.
1678 aim_conn_setlatency(fr->conn, 0);
1684 static int faimtest_parse_evilnotify(aim_session_t *sess, aim_frame_t *fr, ...)
1688 struct aim_userinfo_s *userinfo;
1691 newevil = va_arg(ap, fu16_t);
1692 userinfo = va_arg(ap, struct aim_userinfo_s *);
1696 * Evil Notifications that are lacking userinfo->sn are anon-warns
1697 * if they are an evil increases, but are not warnings at all if its
1698 * a decrease (its the natural backoff happening).
1700 * newevil is passed as an int representing the new evil value times
1703 dvprintf("faimtest: evil level change: new value = %2.1f%% (caused by %s)\n", ((float)newevil)/10, (userinfo && strlen(userinfo->sn))?userinfo->sn:"anonymous");
1708 static int faimtest_parse_searchreply(aim_session_t *sess, aim_frame_t *fr, ...)
1711 char *address, *SNs;
1715 address = va_arg(ap, char *);
1716 num = va_arg(ap, int);
1717 SNs = va_arg(ap, char *);
1720 dvprintf("faimtest: E-Mail Search Results for %s: ", address);
1722 for(i = 0; i < num; i++)
1723 dvinlineprintf("%s, ", &SNs[i*(MAXSNLEN+1)]);
1724 dinlineprintf("\n");
1729 static int faimtest_parse_searcherror(aim_session_t *sess, aim_frame_t *fr, ...)
1735 address = va_arg(ap, char *);
1738 dvprintf("faimtest: E-Mail Search Results for %s: No Results or Invalid Email\n", address);
1743 static int serverpause(aim_session_t *sess, aim_frame_t *fr, ...)
1746 aim_sendpauseack(sess, fr->conn);
1751 static int migrate(aim_session_t *sess, aim_frame_t *fr, ...)
1754 aim_conn_t *bosconn;
1759 bosip = va_arg(ap, char *);
1760 cookie = va_arg(ap, fu8_t *);
1763 dvprintf("migration in progress -- new BOS is %s -- disconnecting\n", bosip);
1764 aim_conn_kill(sess, &fr->conn);
1766 if (!(bosconn = aim_newconn(sess, AIM_CONN_TYPE_BOS, bosip))) {
1767 dprintf("migrate: could not connect to BOS: internal error\n");
1769 } else if (bosconn->status & AIM_CONN_STATUS_CONNERR) {
1770 dprintf("migrate: could not connect to BOS\n");
1771 aim_conn_kill(sess, &bosconn);
1775 /* Login will happen all over again. */
1776 addcb_bos(sess, bosconn);
1778 aim_auth_sendcookie(sess, bosconn, cookie);
1783 void addcb_bos(aim_session_t *sess, aim_conn_t *bosconn)
1786 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE, faimtest_conncomplete, 0);
1788 aim_conn_addhandler(sess, bosconn, 0x0009, 0x0003, faimtest_bosrights, 0);
1789 aim_conn_addhandler(sess, bosconn, 0x0001, 0x0007, faimtest_rateresp_bos, 0); /* rate info */
1790 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, 0x0018, faimtest_hostversions, 0);
1791 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_SERVERREADY, faimtest_serverready, 0);
1792 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_RATEINFO, NULL, 0);
1793 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_REDIRECT, faimtest_handleredirect, 0);
1794 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_STS, AIM_CB_STS_SETREPORTINTERVAL, faimtest_reportinterval, 0);
1795 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_RIGHTSINFO, faimtest_parse_buddyrights, 0);
1796 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_MOTD, faimtest_parse_motd, 0);
1797 aim_conn_addhandler(sess, bosconn, 0x0004, 0x0005, faimtest_icbmparaminfo, 0);
1798 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, faimtest_parse_connerr, 0);
1799 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_RIGHTSINFO, faimtest_locrights, 0);
1800 aim_conn_addhandler(sess, bosconn, 0x0001, 0x001f, faimtest_memrequest, 0);
1801 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_ONCOMING, faimtest_parse_oncoming, 0);
1802 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_OFFGOING, faimtest_parse_offgoing, 0);
1803 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_INCOMING, faimtest_parse_incoming_im, 0);
1804 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_ERROR, faimtest_parse_locerr, 0);
1805 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MISSEDCALL, faimtest_parse_misses, 0);
1806 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_RATECHANGE, faimtest_parse_ratechange, 0);
1807 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_EVIL, faimtest_parse_evilnotify, 0);
1808 aim_conn_addhandler(sess, bosconn, 0x000a, 0x0001, faimtest_parse_searcherror, 0);
1809 aim_conn_addhandler(sess, bosconn, 0x000a, 0x0003, faimtest_parse_searchreply, 0);
1810 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ERROR, faimtest_parse_msgerr, 0);
1811 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO, faimtest_parse_userinfo, 0);
1812 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ACK, faimtest_parse_msgack, 0);
1814 aim_conn_addhandler(sess, bosconn, 0x0001, 0x0001, faimtest_parse_genericerr, 0);
1815 aim_conn_addhandler(sess, bosconn, 0x0003, 0x0001, faimtest_parse_genericerr, 0);
1816 aim_conn_addhandler(sess, bosconn, 0x0009, 0x0001, faimtest_parse_genericerr, 0);
1817 aim_conn_addhandler(sess, bosconn, 0x0001, 0x000b, serverpause, 0);
1818 aim_conn_addhandler(sess, bosconn, 0x0001, 0x0012, migrate, 0);
1820 #ifdef MID_REWROTE_ALL_THE_CRAP
1821 aim_conn_addhandler(sess, bosconn, 0xffff, 0xffff, faimtest_parse_unknown, 0);