]> andersk Git - libfaim.git/blob - utils/faimtest/faimtest.c
a121fb1e6d4550c30ca9e91060104b5cc54cef4f
[libfaim.git] / utils / faimtest / faimtest.c
1 /* 
2  *  -----------------------------------------------------------
3  *  ProtoFAIM: v1.xx.xxplxx
4  *  -----------------------------------------------------------
5  *
6  *  This is ProtoFAIM v1.xx.xxplxx!!! Its nearly completely 
7  *  different than that ugly thing called v0.  This app is
8  *  compatible with the latest version of the libfaim library.
9  *  Work is continuing. 
10  *
11  *  ProtoFAIM should only be used for two things...
12  *   1) Testing the libfaim backend.
13  *   2) For reference on the libfaim API when developing clients.
14  * 
15  *  Its very ugly.  Probably always will be.  Nothing is more
16  *  ugly than the backend itself, however.
17  *
18  *  -----------------------------------------------------------
19  *
20  *  I'm releasing this code and all it's associated linkage
21  *  under the GNU General Public License.  For more information,
22  *  please refer to http://www.fsf.org.  For any questions,
23  *  please contact me at the address below.
24  *
25  *  Most everything:
26  *  (c) 1998 Adam Fritzler, PST, mid@zigamoprh.net
27  *
28  *  The password algorithms
29  *  (c) 1998 Brock Wilcox, awwaiid@zigamorph.net
30  *
31  *  THERE IS NO CODE FROM AOL'S AIM IN THIS CODE, NOR
32  *  WAS THERE ANY DISASSEMBLAGE TO DEFINE PROTOCOL.  All
33  *  information was gained through painstakingly comparing
34  *  TCP dumps while the AIM Java client was running.  Nothing
35  *  more than that, except for a lot of experimenting.
36  *
37  *  -----------------------------------------------------------
38  *
39  */
40
41 #include "faimtest.h"
42 #include <sys/stat.h>
43
44 static char *dprintf_ctime(void)
45 {
46   static char retbuf[64];
47   struct tm *lt;
48   struct timeval tv;
49   struct timezone tz;
50
51   gettimeofday(&tv, &tz);
52   lt = localtime((time_t *)&tv.tv_sec);
53   strftime(retbuf, 64, "%a %b %e %H:%M:%S %Z %Y", lt);
54   return retbuf;
55 }
56
57 #define DPRINTF_OUTSTREAM stdout
58 #define dprintf(x) { \
59   fprintf(DPRINTF_OUTSTREAM, "%s  %s: " x, dprintf_ctime(), "faimtest"); \
60   fflush(DPRINTF_OUTSTREAM); \
61 }
62 #define dvprintf(x, y...) { \
63   fprintf(DPRINTF_OUTSTREAM, "%s  %s: " x, dprintf_ctime(), "faimtest", y); \
64   fflush(DPRINTF_OUTSTREAM); \
65 }
66 #define dinlineprintf(x) { \
67   fprintf(DPRINTF_OUTSTREAM, x); \
68   fflush(DPRINTF_OUTSTREAM); \
69 }
70 #define dvinlineprintf(x, y...) { \
71   fprintf(DPRINTF_OUTSTREAM, x, y); \
72   fflush(DPRINTF_OUTSTREAM); \
73 }
74 #define dperror(x) dvprintf("%s: %s\n", x, strerror(errno));
75
76 int faimtest_parse_oncoming(struct aim_session_t *, struct command_rx_struct *, ...);
77 int faimtest_parse_offgoing(struct aim_session_t *, struct command_rx_struct *, ...);
78 int faimtest_parse_login_phase3d_f(struct aim_session_t *, struct command_rx_struct *, ...);
79 static int faimtest_parse_authresp(struct aim_session_t *, struct command_rx_struct *, ...);
80 int faimtest_parse_incoming_im(struct aim_session_t *, struct command_rx_struct *command, ...);
81 int faimtest_parse_userinfo(struct aim_session_t *, struct command_rx_struct *command, ...);
82 int faimtest_handleredirect(struct aim_session_t *, struct command_rx_struct *command, ...);
83 int faimtest_infochange(struct aim_session_t *sess, struct command_rx_struct *command, ...);
84 int faimtest_serverready(struct aim_session_t *, struct command_rx_struct *command, ...);
85 int faimtest_hostversions(struct aim_session_t *sess, struct command_rx_struct *command, ...);
86 int faimtest_parse_misses(struct aim_session_t *, struct command_rx_struct *command, ...);
87 int faimtest_parse_msgack(struct aim_session_t *, struct command_rx_struct *command, ...);
88 int faimtest_parse_motd(struct aim_session_t *, struct command_rx_struct *command, ...);
89 int faimtest_parse_login(struct aim_session_t *, struct command_rx_struct *command, ...);
90 int faimtest_chatnav_info(struct aim_session_t *, struct command_rx_struct *command, ...);
91 int faimtest_chat_incomingmsg(struct aim_session_t *sess, struct command_rx_struct *command, ...);
92 int faimtest_chat_infoupdate(struct aim_session_t *sess, struct command_rx_struct *command, ...);
93 int faimtest_chat_leave(struct aim_session_t *sess, struct command_rx_struct *command, ...);
94 int faimtest_chat_join(struct aim_session_t *sess, struct command_rx_struct *command, ...);
95 int faimtest_parse_connerr(struct aim_session_t *sess, struct command_rx_struct *command, ...);
96 int faimtest_debugconn_connect(struct aim_session_t *sess, struct command_rx_struct *command, ...);
97
98 int faimtest_directim_request(struct aim_session_t *sess, struct command_rx_struct *command, ...);
99 int faimtest_directim_initiate(struct aim_session_t *sess, struct command_rx_struct *command, ...);
100 int faimtest_directim_connect(struct aim_session_t *sess, struct command_rx_struct *command, ...);
101 int faimtest_directim_incoming(struct aim_session_t *sess, struct command_rx_struct *command, ...);
102 int faimtest_directim_disconnect(struct aim_session_t *sess, struct command_rx_struct *command, ...);
103 int faimtest_directim_typing(struct aim_session_t *sess, struct command_rx_struct *command, ...);
104
105 int faimtest_getfile_filereq(struct aim_session_t *sess, struct command_rx_struct *command, ...);
106 int faimtest_getfile_filesend(struct aim_session_t *sess, struct command_rx_struct *command, ...);
107 int faimtest_getfile_complete(struct aim_session_t *sess, struct command_rx_struct *command, ...);
108 int faimtest_getfile_disconnect(struct aim_session_t *sess, struct command_rx_struct *command, ...);
109 int faimtest_getfile_initiate(struct aim_session_t *sess, struct command_rx_struct *command, ...);
110 int faimtest_getfile_listing(struct aim_session_t *sess, struct command_rx_struct *command, ...);
111 int faimtest_getfile_listingreq(struct aim_session_t *sess, struct command_rx_struct *command, ...);
112 int faimtest_getfile_receive(struct aim_session_t *sess, struct command_rx_struct *command, ...);
113 int faimtest_getfile_state4(struct aim_session_t *sess, struct command_rx_struct *command, ...);
114
115 int faimtest_parse_ratechange(struct aim_session_t *sess, struct command_rx_struct *command, ...);
116 int faimtest_parse_evilnotify(struct aim_session_t *sess, struct command_rx_struct *command, ...);
117 int faimtest_parse_searcherror(struct aim_session_t *sess, struct command_rx_struct *command, ...);
118 int faimtest_parse_searchreply(struct aim_session_t *sess, struct command_rx_struct *command, ...);
119 int faimtest_parse_msgerr(struct aim_session_t *sess, struct command_rx_struct *command, ...);
120 int faimtest_parse_buddyrights(struct aim_session_t *sess, struct command_rx_struct *command, ...);
121 int faimtest_parse_locerr(struct aim_session_t *sess, struct command_rx_struct *command, ...);
122 int faimtest_parse_genericerr(struct aim_session_t *sess, struct command_rx_struct *command, ...);
123
124 static char *msgerrreasons[] = {
125   "Invalid error",
126   "Invalid SNAC",
127   "Rate to host",
128   "Rate to client",
129   "Not logged on",
130   "Service unavailable",
131   "Service not defined",
132   "Obsolete SNAC",
133   "Not supported by host",
134   "Not supported by client",
135   "Refused by client",
136   "Reply too big",
137   "Responses lost",
138   "Request denied",
139   "Busted SNAC payload",
140   "Insufficient rights",
141   "In local permit/deny",
142   "Too evil (sender)",
143   "Too evil (receiver)",
144   "User temporarily unavailable",
145   "No match",
146   "List overflow",
147   "Request ambiguous",
148   "Queue full",
149   "Not while on AOL"};
150 static int msgerrreasonslen = 25;
151
152 static char *aimbinarypath = NULL;
153 static char *screenname,*password,*server=NULL;
154 static char *proxy = NULL, *proxyusername = NULL, *proxypass = NULL;
155 static char *ohcaptainmycaptain = NULL;
156 static int connected = 0;
157
158 struct aim_session_t aimsess;
159 int keepgoing = 1;
160
161 static FILE *listingfile;
162 static char *listingpath;
163
164 static unsigned char *buddyicon = NULL;
165 static int buddyiconlen = 0;
166 static time_t buddyiconstamp = 0;
167 static unsigned short buddyiconsum = 0;
168
169 static void faimtest_debugcb(struct aim_session_t *sess, int level, const char *format, va_list va)
170 {
171
172   vfprintf(stderr, format, va);
173
174   return;
175 }
176
177 int faimtest_reportinterval(struct aim_session_t *sess, struct command_rx_struct *command, ...)
178 {
179   va_list ap;
180   unsigned short interval = 0;
181
182   va_start(ap, command);
183   interval = va_arg(ap, int);
184   va_end(ap);
185
186   dvprintf("aim: minimum report interval: %d (seconds?)\n", interval);
187
188   return 1;
189 }
190
191 int faimtest_flapversion(struct aim_session_t *sess, struct command_rx_struct *command, ...)
192 {
193
194   dvprintf("faimtest: using FLAP version %u\n", aimutil_get32(command->data));
195
196 #if 0
197   /* 
198    * This is an alternate location for starting the login process.
199    */
200   /* XXX should do more checking to make sure its really the right AUTH conn */
201   if (command->conn->type == AIM_CONN_TYPE_AUTH) {
202     /* do NOT send a connack/flapversion, request_login will send it if needed */
203     aim_request_login(sess, command->conn, screenname);
204     dprintf("faimtest: login request sent\n");
205   }
206 #endif
207
208   return 1;
209 }
210
211 /*
212  * This is a frivilous callback. You don't need it. I only used it for
213  * debugging non-blocking connects.
214  *
215  * If packets are sent to a conn before its fully connected, they
216  * will be queued and then transmitted when the connection completes.
217  *
218  */
219 int faimtest_conncomplete(struct aim_session_t *sess, struct command_rx_struct *command, ...)
220 {
221   va_list ap;
222   struct aim_conn_t *conn;
223
224   va_start(ap, command);
225   conn = va_arg(ap, struct aim_conn_t *);
226   va_end(ap);
227   
228   if (conn)
229     dvprintf("faimtest: connection on %d completed\n", conn->fd);
230
231   return 1;
232 }
233
234 #ifdef _WIN32
235 /*
236  * This is really all thats needed to link against libfaim on win32.
237  *
238  * Note that this particular version of faimtest has never been tested
239  * on win32, but I'm fairly sure it should.
240  */
241 int initwsa(void)
242 {
243   WORD wVersionRequested;
244   WSADATA wsaData;
245
246   wVersionRequested = MAKEWORD(2,2);
247   return WSAStartup(wVersionRequested, &wsaData);
248 }
249 #endif /* _WIN32 */
250
251 int faimtest_init(void)
252 {
253   struct aim_conn_t *stdinconn = NULL;
254
255   if (!(stdinconn = aim_newconn(&aimsess, 0, NULL))) {
256     dprintf("unable to create connection for stdin!\n");
257     return -1;
258   }
259
260   stdinconn->fd = STDIN_FILENO;
261
262   return 0;
263 }
264
265 int logout(void)
266 {
267
268   if (ohcaptainmycaptain)
269     aim_send_im(&aimsess, aim_getconn_type(&aimsess, AIM_CONN_TYPE_BOS), ohcaptainmycaptain, 0, "ta ta...");
270
271   aim_session_kill(&aimsess);
272
273   if (faimtest_init() == -1)
274     dprintf("faimtest_init failed\n");
275
276   return 0;
277 }
278
279 int login(const char *sn, const char *passwd)
280 {
281   struct aim_conn_t *authconn;
282
283   if (sn)
284     screenname = strdup(sn);
285   if (passwd)
286     password = strdup(passwd);
287
288   if (proxy)
289     aim_setupproxy(&aimsess, proxy, proxyusername, proxypass);
290
291   if (!screenname || !password) {
292     dprintf("need SN and password\n");
293     return -1;
294   }
295
296   if (!(authconn = aim_newconn(&aimsess, AIM_CONN_TYPE_AUTH, server?server:FAIM_LOGIN_SERVER))) {
297     dprintf("faimtest: internal connection error while in aim_login.  bailing out.\n");
298     return -1;
299   } else if (authconn->fd == -1) {
300     if (authconn->status & AIM_CONN_STATUS_RESOLVERR) {
301       dprintf("faimtest: could not resolve authorizer name\n");
302     } else if (authconn->status & AIM_CONN_STATUS_CONNERR) {
303       dprintf("faimtest: could not connect to authorizer\n");
304     }
305     aim_conn_kill(&aimsess, &authconn);
306     return -1;
307   }
308
309   aim_conn_addhandler(&aimsess, authconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, faimtest_flapversion, 0);
310   aim_conn_addhandler(&aimsess, authconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE, faimtest_conncomplete, 0);
311   aim_conn_addhandler(&aimsess, authconn, 0x0017, 0x0007, faimtest_parse_login, 0);
312   aim_conn_addhandler(&aimsess, authconn, 0x0017, 0x0003, faimtest_parse_authresp, 0);    
313
314   aim_conn_addhandler(&aimsess, authconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_DEBUGCONN_CONNECT, faimtest_debugconn_connect, 0);
315
316   /* If the connection is in progress, this will just be queued */
317   aim_request_login(&aimsess, authconn, screenname);
318   dprintf("faimtest: login request sent\n");
319
320   return 0;
321 }
322
323 int main(int argc, char **argv)
324 {
325   struct aim_conn_t *waitingconn = NULL;
326   int i;
327   int selstat = 0;
328   static int faimtest_mode = 0;
329   struct timeval tv;
330   time_t lastnop = 0;
331   const char *buddyiconpath = NULL;
332
333   screenname = getenv("SCREENNAME");
334   password = getenv("PASSWORD");
335   server = getenv("AUTHSERVER");
336   proxy = getenv("SOCKSPROXY");
337   proxyusername = getenv("SOCKSNAME");
338   proxypass = getenv("SOCKSPASS");
339
340   listingpath = getenv("LISTINGPATH");
341
342   while ((i = getopt(argc, argv, "u:p:a:U:P:A:l:c:hoOb:i:")) != EOF) {
343     switch (i) {
344     case 'u': screenname = optarg; break;
345     case 'p': password = optarg; break;
346     case 'a': server = optarg; break;
347     case 'U': proxyusername = optarg; break;
348     case 'P': proxypass = optarg; break;
349     case 'A': proxy = optarg; break;
350     case 'l': listingpath = optarg; break;
351     case 'c': ohcaptainmycaptain = optarg; break;
352     case 'o': faimtest_mode = 1; break; /* half old interface */
353     case 'O': faimtest_mode = 2; break; /* full old interface */
354     case 'b': aimbinarypath = optarg; break;
355     case 'i': buddyiconpath = optarg; break;
356     case 'h':
357     default:
358       printf("faimtest\n");
359       printf(" Options: \n");
360       printf("    -u name       Screen name ($SCREENNAME)\n");
361       printf("    -p passwd     Password ($PASSWORD)\n");
362       printf("    -a host:port  Authorizer ($AUTHSERVER)\n");
363       printf("    -U name       Proxy user name ($SOCKSPROXY)\n");
364       printf("    -P passwd     Proxy password ($SOCKSNAME)\n");
365       printf("    -A host:port  Proxy host ($SOCKSPASS)\n");
366       printf("    -l path       Path to listing file ($LISTINGPATH)\n");
367       printf("    -c name       Screen name of owner\n");
368       printf("    -o            Login at startup, then prompt\n");
369       printf("    -O            Login, never give prompt\n");
370       printf("    -b path       Path to AIM 3.5.1670 binaries\n");
371       printf("    -i file       Buddy Icon to send\n");
372       exit(0);
373     }
374   }
375
376 #ifdef _WIN32
377   if (initwsa() != 0) {
378     dprintf("faimtest: could not initialize windows sockets\n");
379     return -1;
380   }
381 #endif /* _WIN32 */
382
383   /* Pass zero as flags if you want blocking connects */
384   aim_session_init(&aimsess, AIM_SESS_FLAGS_NONBLOCKCONNECT, 1);
385   aim_setdebuggingcb(&aimsess, faimtest_debugcb); /* still needed even if debuglevel = 0 ! */
386
387   if(listingpath) {
388     char *listingname;
389     if(!(listingname = (char *)calloc(1, strlen(listingpath)+strlen("/listing.txt")))) {
390       dperror("listingname calloc");
391       exit(-1);
392     }
393     sprintf(listingname, "%s/listing.txt", listingpath);
394     if( (listingfile = fopen(listingname, "r")) == NULL) {
395       dvprintf("Couldn't open %s... disabling that shit.\n", listingname);
396     }
397
398     free(listingname);
399   }
400
401   if (buddyiconpath) {
402     struct stat st;
403     FILE *f;
404
405     if ((stat(buddyiconpath, &st) != -1) && (st.st_size <= MAXICONLEN) && (f = fopen(buddyiconpath, "r"))) {
406
407       buddyiconlen = st.st_size;
408       buddyiconstamp = st.st_mtime;
409       buddyicon = malloc(buddyiconlen);
410       fread(buddyicon, 1, st.st_size, f);
411
412       buddyiconsum = aim_iconsum(buddyicon, buddyiconlen);
413
414       dvprintf("read %d bytes of %s for buddy icon (sum 0x%08x)\n", buddyiconlen, buddyiconpath, buddyiconsum);
415
416       fclose(f);
417
418     } else
419       dvprintf("could not open buddy icon %s\n", buddyiconpath);
420
421   }
422
423   faimtest_init();
424
425   if (faimtest_mode < 2)
426     cmd_init();
427
428   if (faimtest_mode >= 1) {
429     if (login(screenname, password) == -1) {
430       if (faimtest_mode < 2)
431         cmd_uninit();
432       exit(-1);
433     }
434   }
435
436   while (keepgoing) {
437
438     tv.tv_sec = 5;
439     tv.tv_usec = 0;
440
441     waitingconn = aim_select(&aimsess, &tv, &selstat);
442
443     if (connected && ((time(NULL) - lastnop) > 30)) {
444       lastnop = time(NULL);
445       aim_flap_nop(&aimsess, aim_getconn_type(&aimsess, AIM_CONN_TYPE_BOS));
446     }
447
448     if (selstat == -1) { /* error */
449       keepgoing = 0; /* fall through */
450     } else if (selstat == 0) { /* no events pending */
451       ;
452     } else if (selstat == 1) { /* outgoing data pending */
453       aim_tx_flushqueue(&aimsess);
454     } else if (selstat == 2) { /* incoming data pending */
455       if ((faimtest_mode < 2) && (waitingconn->fd == STDIN_FILENO)) {
456         cmd_gotkey();
457       } else {
458         if (waitingconn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) {
459           if (aim_handlerendconnect(&aimsess, waitingconn) < 0) {
460             dprintf("connection error (rend out)\n");
461             aim_conn_kill(&aimsess, &waitingconn);
462           }
463         } else {
464           if (aim_get_command(&aimsess, waitingconn) >= 0) {
465             aim_rxdispatch(&aimsess);
466           } else {
467             dvprintf("connection error (type 0x%04x:0x%04x)\n", waitingconn->type, waitingconn->subtype);
468             /* we should have callbacks for all these, else the library will do the conn_kill for us. */
469             if(waitingconn->type == AIM_CONN_TYPE_RENDEZVOUS) {
470               dprintf("connection error: rendezvous connection. you forgot register a disconnect callback, right?\n");    
471               aim_conn_kill(&aimsess, &waitingconn);
472             } else
473               aim_conn_kill(&aimsess, &waitingconn);
474             if (!aim_getconn_type(&aimsess, AIM_CONN_TYPE_BOS)) {
475               dprintf("major connection error\n");
476               if (faimtest_mode == 2)
477                 break;
478             }
479           }
480         }
481       }
482     }
483   }
484
485   /* close up all connections, dead or no */
486   aim_session_kill(&aimsess); 
487
488   if (faimtest_mode < 2) {
489     printf("\n");
490     cmd_uninit();
491   }
492
493   free(buddyicon);
494
495   /* Get out */
496   exit(0);
497 }
498
499 int faimtest_rateresp(struct aim_session_t *sess, struct command_rx_struct *command, ...)
500 {
501
502   switch(command->conn->type) {
503   case AIM_CONN_TYPE_BOS: {
504     /* this is the new buddy list */
505     char buddies[128];
506     /* this is the new profile */
507     char profile[256];
508
509     /* Caution: Buddy1 and Buddy2 are real people! (who I don't know) */
510     snprintf(buddies, sizeof(buddies), "Buddy1&Buddy2&%s&", ohcaptainmycaptain?ohcaptainmycaptain:"blah");
511     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.", ohcaptainmycaptain);
512
513     aim_bos_ackrateresp(sess, command->conn);  /* ack rate info response */
514     aim_bos_reqpersonalinfo(sess, command->conn);
515     aim_bos_reqlocaterights(sess, command->conn);
516     aim_bos_setprofile(sess, command->conn, profile, NULL, AIM_CAPS_BUDDYICON | AIM_CAPS_CHAT | AIM_CAPS_GETFILE | AIM_CAPS_SENDFILE | AIM_CAPS_IMIMAGE /*| AIM_CAPS_GAMES | AIM_CAPS_SAVESTOCKS*/);
517     aim_bos_reqbuddyrights(sess, command->conn);
518
519     /* send the buddy list and profile (required, even if empty) */
520     aim_bos_setbuddylist(sess, command->conn, buddies);
521
522     /* dont really know what this does */
523     aim_addicbmparam(sess, command->conn);
524     aim_bos_reqicbmparaminfo(sess, command->conn);  
525   
526     aim_bos_reqrights(sess, command->conn);  
527     /* set group permissions -- all user classes */
528     aim_bos_setgroupperm(sess, command->conn, AIM_FLAG_ALLUSERS);
529     aim_bos_setprivacyflags(sess, command->conn, AIM_PRIVFLAGS_ALLOWIDLE);
530
531     break;  
532   }
533   case AIM_CONN_TYPE_AUTH:
534     aim_bos_ackrateresp(sess, command->conn);
535     aim_auth_clientready(sess, command->conn);
536     dprintf("faimtest: connected to authorization/admin service\n");
537     break;
538
539   default: 
540     dvprintf("faimtest: got rate response for unhandled connection type %04x\n", command->conn->type);
541     break;
542   }
543
544   return 1;
545 }
546
547 static int faimtest_icbmparaminfo(struct aim_session_t *sess, struct command_rx_struct *command, ...)
548 {
549   unsigned long defflags, minmsginterval;
550   unsigned short maxicbmlen, maxsenderwarn, maxrecverwarn, maxchannel;
551   va_list ap;
552
553   va_start(ap, command);
554   maxchannel = va_arg(ap, unsigned short);
555   defflags = va_arg(ap, unsigned long);
556   maxicbmlen = va_arg(ap, unsigned short);
557   maxsenderwarn = va_arg(ap, unsigned short);
558   maxrecverwarn = va_arg(ap, unsigned short);
559   minmsginterval = va_arg(ap, unsigned long);
560   va_end(ap);
561
562   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", maxchannel, defflags, maxicbmlen, ((float)maxsenderwarn)/10.0, ((float)maxrecverwarn)/10.0, minmsginterval);
563
564   return 1;
565 }
566
567 int faimtest_hostversions(struct aim_session_t *sess, struct command_rx_struct *command, ...)
568 {
569   int vercount, i;
570   unsigned char *versions;
571   va_list ap;
572
573   va_start(ap, command);
574   vercount = va_arg(ap, int); /* number of family/version pairs */
575   versions = va_arg(ap, unsigned char *);
576   va_end(ap);
577
578   dprintf("faimtest: SNAC versions supported by this host: ");
579   for (i = 0; i < vercount*4; i += 4)
580     dvinlineprintf("0x%04x:0x%04x ", 
581                    aimutil_get16(versions+i),  /* SNAC family */
582                    aimutil_get16(versions+i+2) /* Version number */);
583   dinlineprintf("\n");
584
585   return 1;
586 }
587
588 int faimtest_accountconfirm(struct aim_session_t *sess, struct command_rx_struct *command, ...)
589 {
590   int status;
591   va_list ap;
592
593   va_start(ap, command);
594   status = va_arg(ap, int); /* status code of confirmation request */
595   va_end(ap);
596
597   dvprintf("account confirmation returned status 0x%04x (%s)\n", status, (status==0x0000)?"email sent":"unknown");
598
599   return 1;
600 }
601
602 int faimtest_serverready(struct aim_session_t *sess, struct command_rx_struct *command, ...)
603 {
604   int famcount, i;
605   unsigned short *families;
606   va_list ap;
607
608   va_start(ap, command);
609   famcount = va_arg(ap, int);
610   families = va_arg(ap, unsigned short *);
611   va_end(ap);
612
613   dvprintf("faimtest: SNAC families supported by this host (type %d): ", command->conn->type);
614   for (i = 0; i < famcount; i++)
615     dvinlineprintf("0x%04x ", families[i]);
616   dinlineprintf("\n");
617
618   switch (command->conn->type) {
619   case AIM_CONN_TYPE_AUTH:
620     aim_auth_setversions(sess, command->conn);
621     aim_bos_reqrate(sess, command->conn); /* request rate info */
622
623     dprintf("faimtest: done with auth ServerReady\n");
624     break;
625
626   case AIM_CONN_TYPE_BOS:
627
628     aim_setversions(sess, command->conn);
629     aim_bos_reqrate(sess, command->conn); /* request rate info */
630
631     dprintf("faimtest: done with BOS ServerReady\n");
632     break;
633
634   case AIM_CONN_TYPE_CHATNAV:
635     dprintf("faimtest: chatnav: got server ready\n");
636     aim_conn_addhandler(sess, command->conn, AIM_CB_FAM_CTN, AIM_CB_CTN_INFO, faimtest_chatnav_info, 0);
637     aim_bos_reqrate(sess, command->conn);
638     aim_bos_ackrateresp(sess, command->conn);
639     aim_chatnav_clientready(sess, command->conn);
640     aim_chatnav_reqrights(sess, command->conn);
641
642     break;
643
644   case AIM_CONN_TYPE_CHAT:
645     aim_conn_addhandler(sess, command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERJOIN, faimtest_chat_join, 0);
646     aim_conn_addhandler(sess, command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERLEAVE, faimtest_chat_leave, 0);
647     aim_conn_addhandler(sess, command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_ROOMINFOUPDATE, faimtest_chat_infoupdate, 0);
648     aim_conn_addhandler(sess, command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_INCOMINGMSG, faimtest_chat_incomingmsg, 0);
649     aim_bos_reqrate(sess, command->conn);
650     aim_bos_ackrateresp(sess, command->conn);
651     aim_chat_clientready(sess, command->conn);
652     break;
653
654   case AIM_CONN_TYPE_RENDEZVOUS: /* empty */
655     break;
656
657   default:
658     dvprintf("faimtest: unknown connection type on Host Online (0x%04x)\n", command->conn->type);
659   }
660
661   return 1;
662 }
663
664 int faimtest_parse_buddyrights(struct aim_session_t *sess, struct command_rx_struct *command, ...)
665 {
666   va_list ap;
667   unsigned short maxbuddies, maxwatchers;
668
669   va_start(ap, command);
670   maxbuddies = va_arg(ap, int);
671   maxwatchers = va_arg(ap, int);
672   va_end(ap);
673
674   dvprintf("faimtest: buddy list rights: Max buddies = %d / Max watchers = %d\n", maxbuddies, maxwatchers);
675
676   return 1;
677 }
678
679 int faimtest_bosrights(struct aim_session_t *sess, struct command_rx_struct *command, ...)
680 {
681   unsigned short maxpermits, maxdenies;
682   va_list ap;
683
684   va_start(ap, command);
685   maxpermits = va_arg(ap, int);
686   maxdenies = va_arg(ap, int);
687   va_end(ap);
688
689   dvprintf("faimtest: BOS rights: Max permit = %d / Max deny = %d\n", maxpermits, maxdenies);
690
691   aim_bos_clientready(sess, command->conn);
692
693   dprintf("faimtest: officially connected to BOS.\n");
694
695   return 1;
696 }
697
698 int faimtest_locrights(struct aim_session_t *sess, struct command_rx_struct *command, ...)
699 {
700   unsigned short maxsiglen;
701   va_list ap;
702
703   va_start(ap, command);
704   maxsiglen = va_arg(ap, int);
705   va_end(ap);
706
707   dvprintf("faimtest: locate rights: max signature length = %d\n", maxsiglen);
708
709   return 1;
710 }
711
712 int faimtest_parse_unknown(struct aim_session_t *sess, struct command_rx_struct *command, ...)
713 {
714   int i = 0;
715
716   if (!sess || !command)
717     return 1;
718
719   dprintf("\nReceived unknown packet:");
720   for (i = 0; i < command->commandlen; i++) {
721     if ((i % 8) == 0)
722       dinlineprintf("\n\t");
723     dvinlineprintf("0x%2x ", command->data[i]);
724   }
725   dinlineprintf("\n\n");
726
727   return 1;
728 }
729
730 /*
731   handleredirect()...
732
733   This, of course, handles Service Redirects from OSCAR.
734
735   Should get passed in the following:
736      struct command_rx_struct *command
737        the raw command data
738      int serviceid
739        the destination service ID
740      char *serverip
741        the IP address of the service's server
742      char *cookie
743        the raw auth cookie
744  */
745 int faimtest_handleredirect(struct aim_session_t *sess, struct command_rx_struct *command, ...)
746 {
747   va_list ap;
748   int serviceid;
749   char *ip;
750   unsigned char *cookie;
751
752   va_start(ap, command);
753   serviceid = va_arg(ap, int);
754   ip = va_arg(ap, char *);
755   cookie = va_arg(ap, unsigned char *);
756  
757   switch(serviceid) {
758   case 0x0005: { /* Adverts */
759     struct aim_conn_t *tstconn;
760
761     tstconn = aim_newconn(sess, AIM_CONN_TYPE_ADS, ip);
762     if ((tstconn==NULL) || (tstconn->status & AIM_CONN_STATUS_RESOLVERR)) {
763       dprintf("faimtest: unable to reconnect with authorizer\n");
764     } else {
765       aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, faimtest_flapversion, 0);
766       aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE, faimtest_conncomplete, 0);
767       aim_conn_addhandler(sess, tstconn, 0x0001, 0x0003, faimtest_serverready, 0);
768       aim_conn_addhandler(sess, tstconn, 0x0001, 0x0007, faimtest_rateresp, 0); /* rate info */
769       aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_GEN, 0x0018, faimtest_hostversions, 0);
770       aim_auth_sendcookie(sess, tstconn, cookie);
771       dprintf("sent cookie to adverts host\n");
772     }
773     break;
774   }  
775   case 0x0007: { /* Authorizer */
776     struct aim_conn_t *tstconn;
777     /* Open a connection to the Auth */
778     tstconn = aim_newconn(sess, AIM_CONN_TYPE_AUTH, ip);
779     if ((tstconn==NULL) || (tstconn->status & AIM_CONN_STATUS_RESOLVERR)) {
780       dprintf("faimtest: unable to reconnect with authorizer\n");
781     } else {
782       aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, faimtest_flapversion, 0);
783       aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE, faimtest_conncomplete, 0);
784       aim_conn_addhandler(sess, tstconn, 0x0001, 0x0003, faimtest_serverready, 0);
785       aim_conn_addhandler(sess, tstconn, 0x0001, 0x0007, faimtest_rateresp, 0); /* rate info */
786       aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_GEN, 0x0018, faimtest_hostversions, 0);
787       aim_conn_addhandler(sess, tstconn, 0x0007, 0x0007, faimtest_accountconfirm, 0);
788       aim_conn_addhandler(sess, tstconn, 0x0007, 0x0003, faimtest_infochange, 0);
789       aim_conn_addhandler(sess, tstconn, 0x0007, 0x0005, faimtest_infochange, 0);
790       /* Send the cookie to the Auth */
791       aim_auth_sendcookie(sess, tstconn, cookie);
792       dprintf("sent cookie to authorizer host\n");
793     }
794     break;
795   }  
796   case 0x000d: { /* ChatNav */
797     struct aim_conn_t *tstconn = NULL;
798     tstconn = aim_newconn(sess, AIM_CONN_TYPE_CHATNAV, ip);
799     if ( (tstconn==NULL) || (tstconn->status & AIM_CONN_STATUS_RESOLVERR)) {
800       dprintf("faimtest: unable to connect to chatnav server\n");
801       if (tstconn) aim_conn_kill(sess, &tstconn);
802       return 1;
803     }
804
805     aim_conn_addhandler(sess, tstconn, 0x0001, 0x0003, faimtest_serverready, 0);
806     aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE, faimtest_conncomplete, 0);
807     aim_auth_sendcookie(sess, tstconn, cookie);
808     dprintf("\achatnav: connected\n");
809     break;
810   }
811   case 0x000e: { /* Chat */
812     char *roomname = NULL;
813     int exchange;
814     struct aim_conn_t *tstconn = NULL;
815
816     roomname = va_arg(ap, char *);
817     exchange = va_arg(ap, int);
818
819     tstconn = aim_newconn(sess, AIM_CONN_TYPE_CHAT, ip);
820     if ( (tstconn==NULL) || (tstconn->status & AIM_CONN_STATUS_RESOLVERR)) {
821       dprintf("faimtest: unable to connect to chat server\n");
822       if (tstconn) aim_conn_kill(sess, &tstconn);
823       return 1;
824     }           
825     dvprintf("faimtest: chat: connected to %s on exchange %d\n", roomname, exchange);
826
827     /*
828      * We must do this to attach the stored name to the connection!
829      */
830     aim_chat_attachname(tstconn, roomname);
831
832     aim_conn_addhandler(sess, tstconn, 0x0001, 0x0003, faimtest_serverready, 0);
833     aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE, faimtest_conncomplete, 0);
834     aim_auth_sendcookie(sess, tstconn, cookie);
835
836     break;
837   }
838   default:
839     dvprintf("uh oh... got redirect for unknown service 0x%04x!!\n", serviceid);
840     /* dunno */
841   }
842
843   va_end(ap);
844
845   return 1;
846 }
847
848 /*
849  * This is a little more complicated than it looks.  The module
850  * name (proto, boscore, etc) may or may not be given.  If it is
851  * not given, then use aim.exe.  If it is given, put ".ocm" on the
852  * end of it.
853  *
854  * Now, if the offset or length requested would cause a read past
855  * the end of the file, then the request is considered invalid.  Invalid
856  * requests are processed specially.  The value hashed is the
857  * the request, put into little-endian (eight bytes: offset followed
858  * by length).  
859  *
860  * Additionally, if the request is valid, the length is mod 4096.  It is
861  * important that the length is checked for validity first before doing
862  * the mod.
863  *
864  * Note to Bosco's Brigade: if you'd like to break this, put the 
865  * module name on an invalid request.
866  *
867  */
868 static int getaimdata(unsigned char **bufret, int *buflenret, unsigned long offset, unsigned long len, const char *modname)
869 {
870   FILE *f;
871   static const char defaultmod[] = "aim.exe";
872   char *filename = NULL;
873   struct stat st;
874   unsigned char *buf;
875   int invalid = 0;
876
877   if (!bufret || !buflenret)
878     return -1;
879
880   if (modname) {
881
882     if (!(filename = malloc(strlen(aimbinarypath)+1+strlen(modname)+4+1))) {
883       dperror("memrequest: malloc");
884       return -1;
885     }
886
887     sprintf(filename, "%s/%s.ocm", aimbinarypath, modname);
888
889   } else {
890
891     if (!(filename = malloc(strlen(aimbinarypath)+1+strlen(defaultmod)+1))) {
892       dperror("memrequest: malloc");
893       return -1;
894     }
895
896     sprintf(filename, "%s/%s", aimbinarypath, defaultmod);
897
898   }
899
900   if (stat(filename, &st) == -1) {
901     if (!modname) {
902       dperror("memrequest: stat");
903       free(filename);
904       return -1;
905     }
906     invalid = 1;
907   }
908
909   if (!invalid) {
910     if ((offset > st.st_size) || (len > st.st_size))
911       invalid = 1;
912     else if ((st.st_size - offset) < len)
913       len = st.st_size - offset;
914     else if ((st.st_size - len) < len)
915       len = st.st_size - len;
916   }
917
918   if (!invalid && len)
919     len %= 4096;
920
921   if (invalid) {
922     int i;
923
924     free(filename); /* not needed */
925
926     dvprintf("memrequest: recieved invalid request for 0x%08lx bytes at 0x%08lx (file %s)\n", len, offset, modname);
927
928     i = 8;
929     if (modname)
930       i += strlen(modname);
931
932     if (!(buf = malloc(i)))
933       return -1;
934
935     i = 0;
936
937     if (modname) {
938       memcpy(buf, modname, strlen(modname));
939       i += strlen(modname);
940     }
941
942     /* Damn endianness. This must be little (LSB first) endian. */
943     buf[i++] = offset & 0xff;
944     buf[i++] = (offset >> 8) & 0xff;
945     buf[i++] = (offset >> 16) & 0xff;
946     buf[i++] = (offset >> 24) & 0xff;
947     buf[i++] = len & 0xff;
948     buf[i++] = (len >> 8) & 0xff;
949     buf[i++] = (len >> 16) & 0xff;
950     buf[i++] = (len >> 24) & 0xff;
951
952     *bufret = buf;
953     *buflenret = i;
954
955   } else {
956
957     if (!(buf = malloc(len))) {
958       free(filename);
959       return -1;
960     }
961
962     dvprintf("memrequest: loading %ld bytes from 0x%08lx in \"%s\"...\n", len, offset, filename);
963
964     if (!(f = fopen(filename, "r"))) {
965       dperror("memrequest: fopen");
966       free(filename);
967       free(buf);
968       return -1;
969     }
970
971     free(filename);
972
973     if (fseek(f, offset, SEEK_SET) == -1) {
974       dperror("memrequest: fseek");
975       fclose(f);
976       free(buf);
977       return -1;
978     }
979
980     if (fread(buf, len, 1, f) != 1) {
981       dperror("memrequest: fread");
982       fclose(f);
983       free(buf);
984       return -1;
985     }
986
987     fclose(f);
988
989     *bufret = buf;
990     *buflenret = len;
991
992   }
993
994   return 0; /* success! */
995 }
996
997 /*
998  * This will get an offset and a length.  The client should read this
999  * data out of whatever AIM.EXE binary the user has provided (hopefully
1000  * it matches the client information thats sent at login) and pass a
1001  * buffer back to libfaim so it can hash the data and send it to AOL for
1002  * inspection by the client police.
1003  */
1004 static int faimtest_memrequest(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1005 {
1006   va_list ap;
1007   unsigned long offset, len;
1008   char *modname;
1009   unsigned char *buf;
1010   int buflen;
1011   
1012   va_start(ap, command);
1013   offset = va_arg(ap, unsigned long);
1014   len = va_arg(ap, unsigned long);
1015   modname = va_arg(ap, char *);
1016   va_end(ap);
1017
1018   if (aimbinarypath && (getaimdata(&buf, &buflen, offset, len, modname) == 0)) {
1019
1020     aim_sendmemblock(sess, command->conn, offset, buflen, buf, AIM_SENDMEMBLOCK_FLAG_ISREQUEST);
1021
1022     free(buf);
1023
1024   } else {
1025
1026     dvprintf("memrequest: unable to use AIM binary (\"%s/%s\"), sending defaults...\n", aimbinarypath, modname);
1027
1028     aim_sendmemblock(sess, command->conn, offset, len, NULL, AIM_SENDMEMBLOCK_FLAG_ISREQUEST);
1029
1030   }
1031
1032   return 1;
1033 }
1034
1035 static int faimtest_parse_authresp(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1036 {
1037   va_list ap;
1038   struct aim_conn_t *bosconn = NULL;
1039   char *sn = NULL, *bosip = NULL, *errurl = NULL, *email = NULL;
1040   unsigned char *cookie = NULL;
1041   int errorcode = 0, regstatus = 0;
1042   int latestbuild = 0, latestbetabuild = 0;
1043   char *latestrelease = NULL, *latestbeta = NULL;
1044   char *latestreleaseurl = NULL, *latestbetaurl = NULL;
1045   char *latestreleaseinfo = NULL, *latestbetainfo = NULL;
1046
1047   va_start(ap, command);
1048   sn = va_arg(ap, char *);
1049   errorcode = va_arg(ap, int);
1050   errurl = va_arg(ap, char *);
1051   regstatus = va_arg(ap, int);
1052   email = va_arg(ap, char *);
1053   bosip = va_arg(ap, char *);
1054   cookie = va_arg(ap, unsigned char *);
1055
1056   latestrelease = va_arg(ap, char *);
1057   latestbuild = va_arg(ap, int);
1058   latestreleaseurl = va_arg(ap, char *);
1059   latestreleaseinfo = va_arg(ap, char *);
1060
1061   latestbeta = va_arg(ap, char *);
1062   latestbetabuild = va_arg(ap, int);
1063   latestbetaurl = va_arg(ap, char *);
1064   latestbetainfo = va_arg(ap, char *);
1065
1066   va_end(ap);
1067
1068   dvprintf("Screen name: %s\n", sn);
1069
1070   /*
1071    * Check for error.
1072    */
1073   if (errorcode || !bosip || !cookie) {
1074     dvprintf("Login Error Code 0x%04x\n", errorcode);
1075     dvprintf("Error URL: %s\n", errurl);
1076     aim_conn_kill(sess, &command->conn);
1077     return 1;
1078   }
1079
1080   dvprintf("Reg status: %2d\n", regstatus);
1081   dvprintf("Email: %s\n", email);
1082   dvprintf("BOS IP: %s\n", bosip);
1083
1084   if (latestbeta)
1085     dvprintf("Latest beta version: %s, build %d, at %s (more info at %s)\n", latestbeta, latestbetabuild, latestbetaurl, latestbetainfo);
1086
1087   if (latestrelease)
1088     dvprintf("Latest released version: %s, build %d, at %s (more info at %s)\n", latestrelease, latestbuild, latestreleaseurl, latestreleaseinfo);
1089
1090   dprintf("Closing auth connection...\n");
1091   aim_conn_kill(sess, &command->conn);
1092   if (!(bosconn = aim_newconn(sess, AIM_CONN_TYPE_BOS, bosip))) {
1093     dprintf("faimtest: could not connect to BOS: internal error\n");
1094     return 1;
1095   } else if (bosconn->status & AIM_CONN_STATUS_CONNERR) {       
1096     dprintf("faimtest: could not connect to BOS\n");
1097     aim_conn_kill(sess, &bosconn);
1098     return 1;
1099   }
1100
1101   aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE, faimtest_conncomplete, 0);
1102   aim_conn_addhandler(sess, bosconn, 0x0009, 0x0003, faimtest_bosrights, 0);
1103   aim_conn_addhandler(sess, bosconn, 0x0001, 0x0007, faimtest_rateresp, 0); /* rate info */
1104   aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ACK, AIM_CB_ACK_ACK, NULL, 0);
1105   aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, 0x0018, faimtest_hostversions, 0);
1106   aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_SERVERREADY, faimtest_serverready, 0);
1107   aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_RATEINFO, NULL, 0);
1108   aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_REDIRECT, faimtest_handleredirect, 0);
1109   aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_STS, AIM_CB_STS_SETREPORTINTERVAL, faimtest_reportinterval, 0);
1110   aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_RIGHTSINFO, faimtest_parse_buddyrights, 0);
1111   aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_ONCOMING, faimtest_parse_oncoming, 0);
1112   aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_OFFGOING, faimtest_parse_offgoing, 0);
1113   aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_INCOMING, faimtest_parse_incoming_im, 0);
1114   aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_ERROR, faimtest_parse_locerr, 0);
1115   aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MISSEDCALL, faimtest_parse_misses, 0);
1116   aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_RATECHANGE, faimtest_parse_ratechange, 0);
1117   aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_EVIL, faimtest_parse_evilnotify, 0);
1118   aim_conn_addhandler(sess, bosconn, 0x000a, 0x0001, faimtest_parse_searcherror, 0);
1119   aim_conn_addhandler(sess, bosconn, 0x000a, 0x0003, faimtest_parse_searchreply, 0);
1120   aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ERROR, faimtest_parse_msgerr, 0);
1121   aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO, faimtest_parse_userinfo, 0);
1122   aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_RIGHTSINFO, faimtest_locrights, 0);
1123   aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ACK, faimtest_parse_msgack, 0);
1124
1125   aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_MOTD, faimtest_parse_motd, 0);
1126
1127   aim_conn_addhandler(sess, bosconn, 0x0004, 0x0005, faimtest_icbmparaminfo, 0);
1128   aim_conn_addhandler(sess, bosconn, 0x0001, 0x0001, faimtest_parse_genericerr, 0);
1129   aim_conn_addhandler(sess, bosconn, 0x0003, 0x0001, faimtest_parse_genericerr, 0);
1130   aim_conn_addhandler(sess, bosconn, 0x0009, 0x0001, faimtest_parse_genericerr, 0);
1131
1132   aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, faimtest_parse_connerr, 0);
1133   aim_conn_addhandler(sess, bosconn, 0x0001, 0x001f, faimtest_memrequest, 0);
1134   aim_conn_addhandler(sess, bosconn, 0xffff, 0xffff, faimtest_parse_unknown, 0);
1135
1136   aim_auth_sendcookie(sess, bosconn, cookie);
1137
1138   return 1;
1139 }
1140
1141 static void printuserflags(unsigned short flags)
1142 {
1143   if (flags & AIM_FLAG_UNCONFIRMED)
1144     dinlineprintf("UNCONFIRMED ");
1145   if (flags & AIM_FLAG_ADMINISTRATOR)
1146     dinlineprintf("ADMINISTRATOR ");
1147   if (flags & AIM_FLAG_AOL)
1148     dinlineprintf("AOL ");
1149   if (flags & AIM_FLAG_OSCAR_PAY)
1150     dinlineprintf("OSCAR_PAY ");
1151   if (flags & AIM_FLAG_FREE)
1152     dinlineprintf("FREE ");
1153   if (flags & AIM_FLAG_AWAY)
1154     dinlineprintf("AWAY ");
1155   if (flags & AIM_FLAG_UNKNOWN40)
1156     dinlineprintf("ICQ? ");
1157   if (flags & AIM_FLAG_UNKNOWN80)
1158     dinlineprintf("UNKNOWN80 ");
1159   return;
1160 }
1161
1162 int faimtest_parse_userinfo(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1163 {
1164   struct aim_userinfo_s *userinfo;
1165   char *prof_encoding = NULL;
1166   char *prof = NULL;
1167   unsigned short inforeq = 0;
1168
1169   va_list ap;
1170   va_start(ap, command);
1171   userinfo = va_arg(ap, struct aim_userinfo_s *);
1172   prof_encoding = va_arg(ap, char *);
1173   prof = va_arg(ap, char *);
1174   inforeq = va_arg(ap, int);
1175   va_end(ap);
1176   
1177   dvprintf("faimtest: userinfo: sn: %s\n", userinfo->sn);
1178   dvprintf("faimtest: userinfo: warnlevel: 0x%04x\n", userinfo->warnlevel);
1179   dvprintf("faimtest: userinfo: flags: 0x%04x = ", userinfo->flags);
1180   printuserflags(userinfo->flags);
1181   dinlineprintf("\n");
1182   
1183   dvprintf("faimtest: userinfo: membersince: %lu\n", userinfo->membersince);
1184   dvprintf("faimtest: userinfo: onlinesince: %lu\n", userinfo->onlinesince);
1185   dvprintf("faimtest: userinfo: idletime: 0x%04x\n", userinfo->idletime);
1186   
1187   if (inforeq == AIM_GETINFO_GENERALINFO) {
1188     dvprintf("faimtest: userinfo: profile_encoding: %s\n", prof_encoding ? prof_encoding : "[none]");
1189     dvprintf("faimtest: userinfo: prof: %s\n", prof ? prof : "[none]");
1190   } else if (inforeq == AIM_GETINFO_AWAYMESSAGE) {
1191     dvprintf("faimtest: userinfo: awaymsg_encoding: %s\n", prof_encoding ? prof_encoding : "[none]");
1192     dvprintf("faimtest: userinfo: awaymsg: %s\n", prof ? prof : "[none]");
1193   } else 
1194     dprintf("faimtest: userinfo: unknown info request\n");
1195   
1196   return 1;
1197 }
1198
1199 static int faimtest_handlecmd(struct aim_session_t *sess, struct command_rx_struct *command, struct aim_userinfo_s *userinfo, char *tmpstr)
1200 {
1201
1202   if (!strncmp(tmpstr, "disconnect", 10)) {
1203
1204     logout();
1205
1206   } else if (strstr(tmpstr, "goodday")) {
1207
1208       aim_send_im(sess, command->conn, userinfo->sn, AIM_IMFLAGS_ACK, "Good day to you too.");
1209
1210   } else if (strstr(tmpstr, "haveicon") && buddyicon) {
1211     struct aim_sendimext_args args;
1212     static const char iconmsg[] = {"I have an icon"};
1213
1214     args.destsn = userinfo->sn;
1215     args.flags = AIM_IMFLAGS_HASICON;
1216     args.msg = iconmsg;
1217     args.msglen = strlen(iconmsg);
1218     args.iconlen = buddyiconlen;
1219     args.iconstamp = buddyiconstamp;
1220     args.iconsum = buddyiconsum;
1221
1222     aim_send_im_ext(sess, command->conn, &args);
1223
1224   } else if (strstr(tmpstr, "sendicon") && buddyicon) {
1225
1226     aim_send_icon(sess, command->conn, userinfo->sn, buddyicon, buddyiconlen, buddyiconstamp, buddyiconsum);
1227
1228   } else if (strstr(tmpstr, "warnme")) {
1229
1230     dprintf("faimtest: icbm: sending non-anon warning\n");
1231     aim_send_warning(sess, command->conn, userinfo->sn, 0);
1232
1233   } else if (strstr(tmpstr, "anonwarn")) {
1234
1235     dprintf("faimtest: icbm: sending anon warning\n");
1236     aim_send_warning(sess, command->conn, userinfo->sn, AIM_WARN_ANON);
1237
1238   } else if (strstr(tmpstr, "setdirectoryinfo")) {
1239
1240     dprintf("faimtest: icbm: sending backwards profile data\n");
1241     aim_setdirectoryinfo(sess, command->conn, "tsrif", "elddim", "tsal", "nediam", "emankcin", "teerts", "ytic", "etats", "piz", 0, 1);
1242
1243   } else if (strstr(tmpstr, "setinterests")) {
1244
1245     dprintf("faimtest: icbm: setting fun interests\n");
1246     aim_setuserinterests(sess, command->conn, "interest1", "interest2", "interest3", "interest4", "interest5", 1);
1247
1248   } else if (!strncmp(tmpstr, "getfile", 7)) {
1249
1250     if (!ohcaptainmycaptain) {
1251
1252       aim_send_im(sess, command->conn, userinfo->sn, AIM_IMFLAGS_ACK, "I have no owner!");
1253
1254     } else {
1255       struct aim_conn_t *newconn;
1256
1257       newconn = aim_getfile_initiate(sess, command->conn, (strlen(tmpstr) < 8)?ohcaptainmycaptain:tmpstr+8);
1258       dvprintf("faimtest: getting file listing from %s\n", (strlen(tmpstr) < 8)?ohcaptainmycaptain:tmpstr+8);
1259       aim_conn_addhandler(sess, newconn,  AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEINITIATE, faimtest_getfile_initiate,0);
1260     }
1261
1262   } else if (!strncmp(tmpstr, "open chatnav", 12)) {
1263
1264     aim_bos_reqservice(sess, command->conn, AIM_CONN_TYPE_CHATNAV);
1265
1266   } else if (!strncmp(tmpstr, "create", 6)) {
1267
1268     aim_chatnav_createroom(sess,aim_getconn_type(sess, AIM_CONN_TYPE_CHATNAV), (strlen(tmpstr) < 7)?"WorldDomination":tmpstr+7, 0x0004);
1269
1270   } else if (!strncmp(tmpstr, "close chatnav", 13)) {
1271     struct aim_conn_t *chatnavconn;
1272
1273     chatnavconn = aim_getconn_type(sess, AIM_CONN_TYPE_CHATNAV);
1274     aim_conn_kill(sess, &chatnavconn);
1275
1276   } else if (!strncmp(tmpstr, "join", 4)) {
1277
1278     aim_chat_join(sess, command->conn, 0x0004, "worlddomination");
1279
1280   } else if (!strncmp(tmpstr, "leave", 5)) {
1281
1282     aim_chat_leaveroom(sess, "worlddomination");
1283
1284   } else if (!strncmp(tmpstr, "getinfo", 7)) {
1285
1286     aim_getinfo(sess, command->conn, "75784102", AIM_GETINFO_GENERALINFO);
1287     aim_getinfo(sess, command->conn, "15853637", AIM_GETINFO_AWAYMESSAGE);
1288     aim_getinfo(sess, command->conn, "midendian", AIM_GETINFO_GENERALINFO);
1289     aim_getinfo(sess, command->conn, "midendian", AIM_GETINFO_AWAYMESSAGE);
1290
1291   } else if (!strncmp(tmpstr, "open directim", 13)) {
1292     struct aim_conn_t *newconn;
1293
1294     printf("faimtest: opening directim to %s\n", (strlen(tmpstr) < 14)?userinfo->sn:tmpstr+14);
1295     newconn = aim_directim_initiate(sess, command->conn, NULL, (strlen(tmpstr) < 14)?userinfo->sn:tmpstr+14);
1296     if(!newconn || newconn->fd == -1)
1297       printf("connection failed!\n");
1298     aim_conn_addhandler(sess, newconn,  AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINITIATE, faimtest_directim_initiate,0);
1299
1300   } else if(!(strncmp(tmpstr, "lookup", 6))) {
1301
1302     aim_usersearch_address(sess, command->conn, tmpstr+7);
1303
1304   } else if (!strncmp(tmpstr, "reqsendmsg", 10)) {
1305
1306     aim_send_im(sess, command->conn, ohcaptainmycaptain, 0, "sendmsg 7900");
1307
1308   } else if (!strncmp(tmpstr, "reqauth", 7)) {
1309
1310     aim_bos_reqservice(sess, command->conn, AIM_CONN_TYPE_AUTH);
1311
1312   } else if (!strncmp(tmpstr, "reqconfirm", 10)) {
1313
1314     aim_auth_reqconfirm(sess, aim_getconn_type(sess, AIM_CONN_TYPE_AUTH));
1315
1316   } else if (!strncmp(tmpstr, "reqemail", 8)) {
1317
1318     aim_auth_getinfo(sess, aim_getconn_type(sess, AIM_CONN_TYPE_AUTH), 0x0011);
1319
1320   } else if (!strncmp(tmpstr, "changepass", 8)) {
1321
1322     aim_auth_changepasswd(sess, aim_getconn_type(sess, AIM_CONN_TYPE_AUTH), "NEWPASSWORD", "OLDPASSWORD");
1323
1324   } else if (!strncmp(tmpstr, "setemail", 8)) {
1325
1326     aim_auth_setemail(sess, aim_getconn_type(sess, AIM_CONN_TYPE_AUTH), "NEWEMAILADDRESS");
1327
1328   } else if (!strncmp(tmpstr, "sendmsg", 7)) {
1329     int i;
1330     i = atoi(tmpstr+8);
1331     if (i < 10000) {
1332       char *newbuf;
1333       int z;
1334
1335       newbuf = malloc(i+1);
1336       for (z = 0; z < i; z++) {
1337         newbuf[z] = (z % 10)+0x30;
1338       }
1339       newbuf[i] = '\0';
1340       aim_send_im(sess, command->conn, userinfo->sn, 0, newbuf);
1341       free(newbuf);
1342     }
1343
1344   } else {
1345
1346     dprintf("unknown command.\n");
1347     aim_add_buddy(sess, command->conn, userinfo->sn);
1348
1349   }  
1350
1351   return 0;
1352 }
1353
1354 /*
1355  * The user-level Incoming ICBM callback.
1356  *
1357  */
1358 int faimtest_parse_incoming_im(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1359 {
1360   int channel;
1361   struct aim_userinfo_s *userinfo;
1362   va_list ap;
1363
1364   va_start(ap, command);
1365   channel = va_arg(ap, int);
1366   userinfo = va_arg(ap, struct aim_userinfo_s *);
1367
1368   /*
1369    * Channel 1: Standard Message
1370    */
1371   if (channel == 1) {
1372     char *tmpstr;
1373     struct aim_incomingim_ch1_args *args;
1374     int clienttype = AIM_CLIENTTYPE_UNKNOWN;
1375
1376     args = va_arg(ap, struct aim_incomingim_ch1_args *);
1377     va_end(ap);
1378     
1379     clienttype = aim_fingerprintclient(args->fingerprint, args->finlen);
1380
1381     dvprintf("faimtest: icbm: sn = \"%s\"\n", userinfo->sn);
1382     dvprintf("faimtest: icbm: probable client type: %d\n", clienttype);
1383     dvprintf("faimtest: icbm: warnlevel = 0x%04x\n", userinfo->warnlevel);
1384     dvprintf("faimtest: icbm: flags = 0x%04x = ", userinfo->flags);
1385     printuserflags(userinfo->flags);
1386     dinlineprintf("\n");
1387
1388     dvprintf("faimtest: icbm: membersince = %lu\n", userinfo->membersince);
1389     dvprintf("faimtest: icbm: onlinesince = %lu\n", userinfo->onlinesince);
1390     dvprintf("faimtest: icbm: idletime = 0x%04x\n", userinfo->idletime);
1391     dvprintf("faimtest: icbm: capabilities = 0x%04x\n", userinfo->capabilities);
1392     
1393     dprintf("faimtest: icbm: icbmflags = ");
1394     if (args->icbmflags & AIM_IMFLAGS_AWAY)
1395       dinlineprintf("away ");
1396     if (args->icbmflags & AIM_IMFLAGS_ACK)
1397       dinlineprintf("ackrequest ");
1398     if (args->icbmflags & AIM_IMFLAGS_BUDDYREQ)
1399       dinlineprintf("buddyreq ");
1400     if (args->icbmflags & AIM_IMFLAGS_HASICON)
1401       dinlineprintf("hasicon ");
1402     dinlineprintf("\n");
1403     
1404     dvprintf("faimtest: icbm: encoding flags = {%04x, %04x}\n", args->flag1, args->flag2);
1405
1406     dvprintf("faimtest: icbm: message: %s\n", args->msg);
1407
1408     if (args->icbmflags & AIM_IMFLAGS_HASICON)
1409       aim_send_im(sess, command->conn, userinfo->sn, AIM_IMFLAGS_BUDDYREQ, "You have an icon");
1410
1411     if (args->msg) {
1412       int i = 0;
1413
1414       while (args->msg[i] == '<') {
1415         if (args->msg[i] == '<') {
1416           while (args->msg[i] != '>')
1417             i++;
1418           i++;
1419         }
1420       }
1421       tmpstr = args->msg+i;
1422
1423       faimtest_handlecmd(sess, command, userinfo, tmpstr);
1424
1425     }
1426   }
1427   /*
1428    * Channel 2: Rendevous Request
1429    */
1430   else if (channel == 2) {
1431     struct aim_incomingim_ch2_args *args;
1432     
1433     args = va_arg(ap, struct aim_incomingim_ch2_args *);
1434     va_end(ap);
1435
1436     switch (args->reqclass) {
1437     case AIM_CAPS_VOICE: {
1438       
1439       dvprintf("faimtest: voice invitation: source sn = %s\n", userinfo->sn);
1440       dvprintf("faimtest: voice invitation: \twarnlevel = 0x%04x\n", userinfo->warnlevel);
1441       dvprintf("faimtest: voice invitation: \tclass = 0x%04x = ", userinfo->flags);
1442       printuserflags(userinfo->flags);
1443       dinlineprintf("\n");
1444
1445       /* we dont get membersince on chat invites! */
1446       dvprintf("faimtest: voice invitation: \tonlinesince = %lu\n", userinfo->onlinesince);
1447       dvprintf("faimtest: voice invitation: \tidletime = 0x%04x\n", userinfo->idletime);
1448       
1449       break;
1450     }
1451     case AIM_CAPS_GETFILE: {
1452       struct aim_conn_t *newconn;
1453       struct aim_fileheader_t *fh;
1454
1455       dvprintf("faimtest: get file request from %s (at %s) %x\n", userinfo->sn, args->info.getfile.ip, args->reqclass);
1456
1457       fh = aim_getlisting(sess, listingfile);
1458
1459       newconn = aim_accepttransfer(sess, command->conn, userinfo->sn, args->info.getfile.cookie, args->info.getfile.ip, fh->totfiles, fh->totsize, fh->size, fh->checksum, args->reqclass);
1460
1461       if ( (!newconn) || (newconn->fd == -1) ) {
1462         dprintf("faimtest: getfile: requestconn: apparent error in accepttransfer\n");
1463         if(newconn)
1464           aim_conn_kill(sess, &newconn);
1465         break;
1466       }
1467
1468       free(fh);
1469
1470       aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTINGREQ, faimtest_getfile_listingreq, 0);
1471       aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILEREQ,  faimtest_getfile_filereq, 0);
1472       aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILESEND, faimtest_getfile_filesend, 0);
1473       aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILECOMPLETE, faimtest_getfile_complete, 0);      
1474
1475       aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEDISCONNECT, faimtest_getfile_disconnect, 0);      
1476
1477       dprintf("faimtest: getfile connect succeeded, handlers added.\n");
1478
1479       break;
1480     }
1481     case AIM_CAPS_SENDFILE: {
1482       dprintf("faimtest: send file!\n");
1483       break;
1484     }
1485     case AIM_CAPS_CHAT: {
1486       
1487       dvprintf("faimtest: chat invitation: source sn = %s\n", userinfo->sn);
1488       dvprintf("faimtest: chat invitation: \twarnlevel = 0x%04x\n", userinfo->warnlevel);
1489       dvprintf("faimtest: chat invitation: \tclass = 0x%04x = ", userinfo->flags);
1490       printuserflags(userinfo->flags);
1491       dinlineprintf("\n");
1492
1493       /* we dont get membersince on chat invites! */
1494       dvprintf("faimtest: chat invitation: \tonlinesince = %lu\n", userinfo->onlinesince);
1495       dvprintf("faimtest: chat invitation: \tidletime = 0x%04x\n", userinfo->idletime);
1496       
1497       dvprintf("faimtest: chat invitation: message = %s\n", args->info.chat.msg);
1498       dvprintf("faimtest: chat invitation: room name = %s\n", args->info.chat.roominfo.name);
1499       dvprintf("faimtest: chat invitation: encoding = %s\n", args->info.chat.encoding);
1500       dvprintf("faimtest: chat invitation: language = %s\n", args->info.chat.lang);
1501       dvprintf("faimtest: chat invitation: exchange = 0x%04x\n", args->info.chat.roominfo.exchange);
1502       dvprintf("faimtest: chat invitation: instance = 0x%04x\n", args->info.chat.roominfo.instance);
1503       dvprintf("faimtest: chat invitiation: autojoining %s...\n", args->info.chat.roominfo.name);
1504
1505       /*
1506        * Automatically join room...
1507        */ 
1508       aim_chat_join(sess, command->conn, args->info.chat.roominfo.exchange, args->info.chat.roominfo.name);
1509       break;
1510     }   
1511     case AIM_CAPS_IMIMAGE: {
1512       struct aim_conn_t *newconn;
1513
1514       dprintf("faimtest: icbm: rendezvous imimage\n");
1515
1516       dvprintf("faimtest: OFT: DirectIM: request from %s (%s)\n", userinfo->sn, args->info.directim->ip);
1517       
1518       newconn = aim_directim_connect(sess, command->conn, args->info.directim);
1519
1520       if ( (!newconn) || (newconn->fd == -1) ) {
1521         dprintf("faimtest: icbm: imimage: could not connect\n");
1522
1523         if (newconn)
1524           aim_conn_kill(sess, &newconn);
1525
1526         break;
1527       }
1528
1529       aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING, faimtest_directim_incoming, 0);
1530       aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMDISCONNECT, faimtest_directim_disconnect, 0);
1531       aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING, faimtest_directim_typing, 0);
1532
1533       dvprintf("faimtest: OFT: DirectIM: connected to %s\n", userinfo->sn);
1534
1535       aim_send_im_direct(sess, newconn, "goodday");
1536
1537       break;
1538     }
1539     case AIM_CAPS_BUDDYICON: {
1540
1541       dvprintf("faimtest: Buddy Icon from %s, length = %u\n", userinfo->sn, args->info.icon.length);
1542       break;
1543     }
1544     default:
1545       dvprintf("faimtest: icbm: unknown reqclass (%d)\n", args->reqclass);
1546     } /* switch */
1547   } else
1548     dvprintf("faimtest does not support channels > 2 (chan = %02x)\n", channel);
1549
1550   dprintf("faimtest: icbm: done with ICBM handling\n");
1551
1552   return 1;
1553 }
1554
1555 int faimtest_directim_initiate(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1556 {
1557   va_list ap;
1558   struct aim_directim_priv *priv;
1559   struct aim_conn_t *newconn, *listenerconn;
1560
1561   va_start(ap, command);
1562   newconn = va_arg(ap, struct aim_conn_t *);
1563   listenerconn = va_arg(ap, struct aim_conn_t *);
1564   va_end(ap);
1565
1566   aim_conn_close(listenerconn);
1567   aim_conn_kill(sess, &listenerconn);
1568
1569   priv = (struct aim_directim_priv *)newconn->priv;
1570
1571   dvprintf("faimtest: OFT: DirectIM: intitiate success to %s\n", priv->ip);
1572   
1573   aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING, faimtest_directim_incoming, 0);
1574   aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMDISCONNECT, faimtest_directim_disconnect, 0);
1575   aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING, faimtest_directim_typing, 0);
1576
1577   aim_send_im_direct(sess, newconn, "goodday");
1578
1579   dvprintf("faimtest: OFT: DirectIM: connected to %s\n", priv->sn);
1580
1581   return 1;
1582 }
1583
1584 int faimtest_directim_connect(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1585 {
1586   va_list ap;
1587   struct aim_directim_priv *priv;
1588   
1589   va_start(ap, command);
1590   priv = va_arg(ap, struct aim_directim_priv *);
1591
1592   va_end(ap);
1593   
1594   dprintf("faimtest: directim_connect\n");
1595
1596   return 1;
1597 }
1598
1599 int faimtest_directim_incoming(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1600 {
1601   va_list ap;
1602   char *msg = NULL;
1603   struct aim_conn_t *conn;
1604   struct aim_directim_priv *priv;
1605
1606   va_start(ap, command);
1607   conn = va_arg(ap, struct aim_conn_t *);
1608   msg = va_arg(ap, char *);
1609   va_end(ap);
1610
1611   if(!(priv = conn->priv)) {
1612     dvprintf("faimtest: directim: no private struct on conn with fd %d\n", conn->fd);
1613     return -1;
1614   }
1615
1616   dvprintf("faimtest: Directim from %s: %s\n", priv->sn, msg);
1617   if (!strncmp(msg, "sendmsg", 7)) {
1618     int i;
1619     i = atoi(msg+8);
1620     if (i < 10000) {
1621       char *newbuf;
1622       int z;
1623       
1624       newbuf = malloc(i+1);
1625       for (z = 0; z < i; z++) {
1626         newbuf[z] = (z % 10)+0x30;
1627       }
1628       newbuf[i] = '\0';
1629       aim_send_im_direct(sess, conn, newbuf);
1630       free(newbuf);
1631     }
1632   } else if (!strncmp(msg, "goodday", 7)) {
1633     aim_send_im_direct(sess, conn, "Good day to you, too");
1634   } else {
1635     char newmsg[1024];
1636     snprintf(newmsg, sizeof(newmsg), "unknown (%s)\n", msg);
1637     aim_send_im_direct(sess, conn, newmsg);
1638   }
1639   return 1;
1640 }
1641
1642 int faimtest_directim_disconnect(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1643 {
1644   va_list ap;
1645   struct aim_conn_t *conn;
1646   char *sn;
1647
1648   va_start(ap, command);
1649   conn = va_arg(ap, struct aim_conn_t *);
1650   sn = va_arg(ap, char *);
1651   va_end(ap);
1652
1653   dvprintf("faimtest: directim: disconnected from %s\n", sn);
1654
1655   aim_conn_kill(sess, &conn);
1656   return 1;
1657 }
1658
1659 int faimtest_directim_typing(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1660 {
1661   va_list ap;
1662   struct aim_conn_t *conn;
1663   struct aim_directim_priv *priv;
1664
1665   va_start(ap, command);
1666   conn = va_arg(ap, struct aim_conn_t *);
1667   va_end(ap);
1668   
1669   if(!(priv = (struct aim_directim_priv *)conn->priv)) {
1670     dvprintf("faimtest: no private struct on conn with fd %d!\n", conn->fd);
1671     return -1;
1672   }
1673
1674   dvprintf("faimtest: ohmigod! %s has started typing (DirectIM). He's going to send you a message! *squeal*\n", priv->sn);
1675   return 1;
1676 }
1677
1678 int faimtest_infochange(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1679 {
1680   unsigned short change = 0;
1681   int perms, type, length, str;
1682   char *val;
1683   va_list ap;
1684
1685   va_start(ap, command);
1686   perms = va_arg(ap, int);
1687   type = va_arg(ap, int);
1688   length = va_arg(ap, int);
1689   val = va_arg(ap, char *);
1690   str = va_arg(ap, int);
1691   va_end(ap);
1692
1693   if (aimutil_get16(command->data+2) == 0x0005)
1694     change = 1;
1695
1696   dvprintf("info%s: perms = %d, type = %x, length = %d, val = %s\n", change?" change":"", perms, type, length, str?val:"(not string)");
1697
1698   return 1;
1699 }
1700
1701 int faimtest_parse_oncoming(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1702 {
1703   struct aim_userinfo_s *userinfo;
1704    
1705   va_list ap;
1706   va_start(ap, command);
1707   userinfo = va_arg(ap, struct aim_userinfo_s *);
1708   va_end(ap);
1709
1710   dvprintf("%ld  %s is now online (flags: %04x = %s%s%s%s%s%s%s%s) (caps = 0x%04x)\n",
1711          time(NULL),
1712          userinfo->sn, userinfo->flags,
1713          (userinfo->flags&AIM_FLAG_UNCONFIRMED)?" UNCONFIRMED":"",
1714          (userinfo->flags&AIM_FLAG_ADMINISTRATOR)?" ADMINISTRATOR":"",
1715          (userinfo->flags&AIM_FLAG_AOL)?" AOL":"",
1716          (userinfo->flags&AIM_FLAG_OSCAR_PAY)?" OSCAR_PAY":"",
1717          (userinfo->flags&AIM_FLAG_FREE)?" FREE":"",
1718          (userinfo->flags&AIM_FLAG_AWAY)?" AWAY":"",
1719          (userinfo->flags&AIM_FLAG_UNKNOWN40)?" UNKNOWN40":"",
1720          (userinfo->flags&AIM_FLAG_UNKNOWN80)?" UNKNOWN80":"",
1721          userinfo->capabilities);
1722   return 1;
1723 }
1724
1725 int faimtest_parse_offgoing(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1726 {
1727   struct aim_userinfo_s *userinfo;
1728    
1729   va_list ap;
1730   va_start(ap, command);
1731   userinfo = va_arg(ap, struct aim_userinfo_s *);
1732   va_end(ap);
1733
1734   dvprintf("%ld  %s is now offline (flags: %04x = %s%s%s%s%s%s%s%s) (caps = 0x%04x)\n",
1735          time(NULL),
1736          userinfo->sn, userinfo->flags,
1737          (userinfo->flags&AIM_FLAG_UNCONFIRMED)?" UNCONFIRMED":"",
1738          (userinfo->flags&AIM_FLAG_ADMINISTRATOR)?" ADMINISTRATOR":"",
1739          (userinfo->flags&AIM_FLAG_AOL)?" AOL":"",
1740          (userinfo->flags&AIM_FLAG_OSCAR_PAY)?" OSCAR_PAY":"",
1741          (userinfo->flags&AIM_FLAG_FREE)?" FREE":"",
1742          (userinfo->flags&AIM_FLAG_AWAY)?" AWAY":"",
1743          (userinfo->flags&AIM_FLAG_UNKNOWN40)?" UNKNOWN40":"",
1744          (userinfo->flags&AIM_FLAG_UNKNOWN80)?" UNKNOWN80":"",
1745          userinfo->capabilities);
1746   return 1;
1747 }
1748
1749 int faimtest_parse_motd(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1750 {
1751   static char *codes[] = {
1752     "Unknown",
1753     "Mandatory upgrade",
1754     "Advisory upgrade",
1755     "System bulletin",
1756     "Top o' the world!"};
1757   static int codeslen = 5;
1758
1759   char *msg;
1760   unsigned short id;
1761   va_list ap;
1762   
1763   va_start(ap, command);
1764   id = va_arg(ap, int);
1765   msg = va_arg(ap, char *);
1766   va_end(ap);
1767
1768   dvprintf("faimtest: motd: %s (%d / %s)\n", msg, id, 
1769            (id < codeslen)?codes[id]:"unknown");
1770
1771   if (!connected)
1772     connected++;
1773
1774 #if 0
1775   aim_bos_reqservice(sess, command->conn, 0x0005); /* adverts */
1776   aim_bos_reqservice(sess, command->conn, 0x000f); /* user directory */
1777
1778   /* Don't know what this does... */
1779   /* XXX sess->sn should be normalized by the 0001/000f handler */
1780   aim_0002_000b(sess, command->conn, sess->sn);
1781 #endif
1782
1783   return 1;
1784 }
1785
1786 int faimtest_parse_genericerr(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1787 {
1788   va_list ap;
1789   unsigned short reason;
1790
1791   va_start(ap, command);
1792   reason = va_arg(ap, int);
1793   va_end(ap);
1794
1795   dvprintf("faimtest: snac threw error (reason 0x%04x: %s)\n", reason, (reason<msgerrreasonslen)?msgerrreasons[reason]:"unknown");
1796   
1797   return 1;
1798 }
1799
1800 int faimtest_parse_msgerr(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1801 {
1802   va_list ap;
1803   char *destsn;
1804   unsigned short reason;
1805
1806   va_start(ap, command);
1807   reason = va_arg(ap, int);
1808   destsn = va_arg(ap, char *);
1809   va_end(ap);
1810
1811   dvprintf("faimtest: message to %s bounced (reason 0x%04x: %s)\n", destsn, reason, (reason<msgerrreasonslen)?msgerrreasons[reason]:"unknown");
1812   
1813   return 1;
1814 }
1815
1816 int faimtest_parse_locerr(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1817 {
1818   va_list ap;
1819   char *destsn;
1820   unsigned short reason;
1821
1822   va_start(ap, command);
1823   reason = va_arg(ap, int);
1824   destsn = va_arg(ap, char *);
1825   va_end(ap);
1826
1827   dvprintf("faimtest: user information for %s unavailable (reason 0x%04x: %s)\n", destsn, reason, (reason<msgerrreasonslen)?msgerrreasons[reason]:"unknown");
1828   
1829   return 1;
1830 }
1831
1832 /* 
1833  * Handles callbacks for AIM_CB_MISSED_CALL.
1834  */
1835 int faimtest_parse_misses(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1836 {
1837   static char *missedreasons[] = {
1838     "Unknown",
1839     "Message too large"};
1840   static int missedreasonslen = 2;
1841
1842   va_list ap;
1843   unsigned short chan, nummissed, reason;
1844   struct aim_userinfo_s *userinfo;
1845   
1846   va_start(ap, command);
1847   chan = va_arg(ap, int);
1848   userinfo = va_arg(ap, struct aim_userinfo_s *);
1849   nummissed = va_arg(ap, int);
1850   reason = va_arg(ap, int);
1851   va_end(ap);
1852
1853   dvprintf("faimtest: missed %d messages from %s (reason %d: %s)\n", nummissed, userinfo->sn, reason, (reason<missedreasonslen)?missedreasons[reason]:"unknown");
1854   
1855   return 1;
1856 }
1857
1858 int faimtest_parse_login(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1859 {
1860   struct client_info_s info = AIM_CLIENTINFO_KNOWNGOOD;
1861   char *key;
1862   va_list ap;
1863   
1864   va_start(ap, command);
1865   key = va_arg(ap, char *);
1866   va_end(ap);
1867
1868   aim_send_login(sess, command->conn, screenname, password, &info, key);
1869  
1870   return 1;
1871 }
1872
1873 int faimtest_chat_join(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1874 {
1875   va_list ap;
1876   struct aim_userinfo_s *userinfo;
1877   int count = 0, i = 0;
1878   
1879   va_start(ap, command);
1880   count = va_arg(ap, int);
1881   userinfo = va_arg(ap, struct aim_userinfo_s *);
1882   va_end(ap);
1883
1884   dvprintf("faimtest: chat: %s:  New occupants have joined:\n", (char *)command->conn->priv);
1885   while (i < count)
1886     dvprintf("faimtest: chat: %s: \t%s\n", (char *)command->conn->priv, userinfo[i++].sn);
1887
1888   return 1;
1889 }
1890
1891 int faimtest_chat_leave(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1892 {
1893   va_list ap;
1894   struct aim_userinfo_s *userinfo;
1895   int count = 0, i = 0;
1896   
1897   va_start(ap, command);
1898   count = va_arg(ap, int);
1899   userinfo = va_arg(ap, struct aim_userinfo_s *);
1900   va_end(ap);
1901
1902   dvprintf("faimtest: chat: %s:  Some occupants have left:\n", (char *)command->conn->priv);
1903
1904   for (i = 0; i < count; )
1905     dvprintf("faimtest: chat: %s: \t%s\n", (char *)command->conn->priv, userinfo[i++].sn);
1906
1907   return 1;
1908 }
1909
1910 int faimtest_chat_infoupdate(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1911 {
1912   va_list ap;
1913   struct aim_userinfo_s *userinfo;
1914   struct aim_chat_roominfo *roominfo;
1915   char *roomname;
1916   int usercount,i;
1917   char *roomdesc;
1918   unsigned short unknown_c9, unknown_d2, unknown_d5, maxmsglen;
1919   unsigned long creationtime;
1920
1921   va_start(ap, command);
1922   roominfo = va_arg(ap, struct aim_chat_roominfo *);
1923   roomname = va_arg(ap, char *);
1924   usercount= va_arg(ap, int);
1925   userinfo = va_arg(ap, struct aim_userinfo_s *);
1926   roomdesc = va_arg(ap, char *);
1927   unknown_c9 = va_arg(ap, int);
1928   creationtime = va_arg(ap, unsigned long);
1929   maxmsglen = va_arg(ap, int);
1930   unknown_d2 = va_arg(ap, int);
1931   unknown_d5 = va_arg(ap, int);
1932   va_end(ap);
1933
1934   dvprintf("faimtest: chat: %s:  info update:\n", (char *)command->conn->priv);
1935   dvprintf("faimtest: chat: %s:  \tRoominfo: {%04x, %s, %04x}\n", 
1936          (char *)command->conn->priv,
1937          roominfo->exchange,
1938          roominfo->name,
1939          roominfo->instance);
1940   dvprintf("faimtest: chat: %s:  \tRoomname: %s\n", (char *)command->conn->priv, roomname);
1941   dvprintf("faimtest: chat: %s:  \tRoomdesc: %s\n", (char *)command->conn->priv, roomdesc);
1942   dvprintf("faimtest: chat: %s:  \tOccupants: (%d)\n", (char *)command->conn->priv, usercount);
1943   
1944   for (i = 0; i < usercount; )
1945     dvprintf("faimtest: chat: %s:  \t\t%s\n", (char *)command->conn->priv, userinfo[i++].sn);
1946
1947   dvprintf("faimtest: chat: %s:  \tUnknown_c9: 0x%04x\n", (char *)command->conn->priv, unknown_c9);
1948   dvprintf("faimtest: chat: %s:  \tCreation time: %lu (time_t)\n", (char *)command->conn->priv, creationtime);
1949   dvprintf("faimtest: chat: %s:  \tMax message length: %d bytes\n", (char *)command->conn->priv, maxmsglen);
1950   dvprintf("faimtest: chat: %s:  \tUnknown_d2: 0x%04x\n", (char *)command->conn->priv, unknown_d2);
1951   dvprintf("faimtest: chat: %s:  \tUnknown_d5: 0x%02x\n", (char *)command->conn->priv, unknown_d5);
1952
1953   return 1;
1954 }
1955
1956 int faimtest_chat_incomingmsg(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1957 {
1958   va_list ap;
1959   struct aim_userinfo_s *userinfo;
1960   char *msg;
1961   char tmpbuf[1152];
1962  
1963   va_start(ap, command);
1964   userinfo = va_arg(ap, struct aim_userinfo_s *);       
1965   msg = va_arg(ap, char *);
1966   va_end(ap);
1967
1968   dvprintf("faimtest: chat: %s: incoming msg from %s: %s\n", (char *)command->conn->priv, userinfo->sn, msg);
1969
1970   /*
1971    * Do an echo for testing purposes.  But not for ourselves ("oops!")
1972    */
1973   if (strcmp(userinfo->sn, sess->sn) != 0)
1974     {
1975       sprintf(tmpbuf, "(%s said \"%s\")", userinfo->sn, msg);
1976       aim_chat_send_im(sess, command->conn, 0, tmpbuf, strlen(tmpbuf));
1977     }
1978
1979   return 1;
1980 }
1981
1982 int faimtest_chatnav_info(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1983 {
1984   unsigned short type;
1985   va_list ap;
1986
1987   va_start(ap, command);
1988   type = va_arg(ap, int);
1989
1990   switch(type) {
1991   case 0x0002: {
1992     int maxrooms;
1993     struct aim_chat_exchangeinfo *exchanges;
1994     int exchangecount,i = 0;
1995     
1996     maxrooms = va_arg(ap, int);
1997     exchangecount = va_arg(ap, int);
1998     exchanges = va_arg(ap, struct aim_chat_exchangeinfo *);
1999     va_end(ap);
2000     
2001     dprintf("faimtest: chat info: Chat Rights:\n");
2002     dvprintf("faimtest: chat info: \tMax Concurrent Rooms: %d\n", maxrooms);
2003     
2004     dvprintf("faimtest: chat info: \tExchange List: (%d total)\n", exchangecount);
2005     for (i = 0; i < exchangecount; i++) {
2006       dvprintf("faimtest: chat info: \t\t%x: %s (%s/%s)\n", 
2007                exchanges[i].number,     
2008                exchanges[i].name,
2009                exchanges[i].charset1,
2010                exchanges[i].lang1);
2011     }
2012     
2013   }
2014   break;
2015   case 0x0008: {
2016     char *fqcn, *name, *ck;
2017     unsigned short instance, flags, maxmsglen, maxoccupancy, unknown, exchange;
2018     unsigned char createperms;
2019     unsigned long createtime;
2020
2021     fqcn = va_arg(ap, char *);
2022     instance = va_arg(ap, int);
2023     exchange = va_arg(ap, int);
2024     flags = va_arg(ap, int);
2025     createtime = va_arg(ap, unsigned long);
2026     maxmsglen = va_arg(ap, int);
2027     maxoccupancy = va_arg(ap, int);
2028     createperms = va_arg(ap, int);
2029     unknown = va_arg(ap, int);
2030     name = va_arg(ap, char *);
2031     ck = va_arg(ap, char *);
2032     va_end(ap);
2033
2034     dvprintf("faimtest: received room create reply for %s/0x%04x\n", fqcn, exchange);
2035   }
2036   break;
2037   default:
2038     va_end(ap);
2039     dvprintf("faimtest: chatnav info: unknown type (%04x)\n", type);
2040   }
2041   return 1;
2042 }
2043
2044 int faimtest_parse_connerr(struct aim_session_t *sess, struct command_rx_struct *command, ...)
2045 {
2046   va_list ap;
2047   unsigned short code;
2048   char *msg = NULL;
2049
2050   va_start(ap, command);
2051   code = va_arg(ap, int);
2052   msg = va_arg(ap, char *);
2053   va_end(ap);
2054
2055   dvprintf("faimtest: connerr: Code 0x%04x: %s\n", code, msg);
2056   aim_conn_kill(sess, &command->conn); /* this will break the main loop */
2057
2058   connected = 0;
2059
2060   return 1;
2061 }
2062
2063 int faimtest_debugconn_connect(struct aim_session_t *sess, struct command_rx_struct *command, ...)
2064 {       
2065   dprintf("faimtest: connecting to an aimdebugd!\n");
2066
2067   /* convert the authorizer connection to a BOS connection */
2068   command->conn->type = AIM_CONN_TYPE_BOS;
2069
2070   aim_conn_addhandler(sess, command->conn, AIM_CB_FAM_MSG, AIM_CB_MSG_INCOMING, faimtest_parse_incoming_im, 0);
2071
2072   /* tell the aimddebugd we're ready */
2073   aim_debugconn_sendconnect(sess, command->conn); 
2074
2075   /* go right into main loop (don't open a BOS connection, etc) */
2076   return 1;
2077 }
2078
2079 /*
2080  * Received in response to an IM sent with the AIM_IMFLAGS_ACK option.
2081  */
2082 int faimtest_parse_msgack(struct aim_session_t *sess, struct command_rx_struct *command, ...)
2083 {
2084   va_list ap;
2085   unsigned short type;
2086   char *sn = NULL;
2087
2088   va_start(ap, command);
2089   type = va_arg(ap, int);
2090   sn = va_arg(ap, char *);
2091   va_end(ap);
2092
2093   dvprintf("faimtest: msgack: 0x%04x / %s\n", type, sn);
2094
2095   return 1;
2096 }
2097
2098 int faimtest_getfile_filereq(struct aim_session_t *ses, struct command_rx_struct *command, ...)
2099 {
2100   va_list ap;
2101   struct aim_conn_t *oftconn;
2102   struct aim_fileheader_t *fh;
2103   char *cookie;
2104
2105   va_start(ap, command);
2106   oftconn = va_arg(ap, struct aim_conn_t *);
2107   fh = va_arg(ap, struct aim_fileheader_t *);
2108   cookie = va_arg(ap, char *);
2109   va_end(ap);
2110
2111   dvprintf("faimtest: request for file %s.\n", fh->name);
2112
2113   return 1;
2114 }
2115
2116
2117 int faimtest_getfile_filesend(struct aim_session_t *sess, struct command_rx_struct *command, ...)
2118 {
2119   va_list ap;
2120   struct aim_conn_t *oftconn;
2121   struct aim_fileheader_t *fh;
2122   char *path, *cookie;
2123   int pos, bufpos = 0, bufsize = 2048, i;
2124   char *buf;
2125
2126   FILE *file;
2127
2128   va_start(ap, command);
2129   oftconn = va_arg(ap, struct aim_conn_t *);
2130   fh = va_arg(ap, struct aim_fileheader_t *);
2131   cookie = va_arg(ap, char *);
2132   va_end(ap);
2133
2134   dvprintf("faimtest: sending file %s(%ld).\n", fh->name, fh->size);
2135
2136   if(!(buf = malloc(2048)))
2137      return -1;
2138
2139   if( (path = (char *)calloc(1, strlen(listingpath) +strlen(fh->name)+2)) == NULL) {
2140     dperror("calloc");
2141     dprintf("faimtest: error in calloc of path\n");
2142     return 0; /* XXX: no idea what winaim expects here =) */
2143   }
2144   
2145   snprintf(path, strlen(listingpath)+strlen(fh->name)+2, "%s/%s", listingpath, fh->name);
2146
2147
2148   if( (file = fopen(path, "r")) == NULL) {
2149     dvprintf("faimtest: getfile_send fopen failed for %s. damn.\n", path);
2150     return 0;
2151   }
2152
2153   /* 
2154    * This is a mess. Remember that faimtest is demonstration code
2155    * only and for the sake of the gods, don't use this code in any
2156    * of your clients. --mid
2157    */
2158   for(pos = 0; pos < fh->size; pos++) {
2159     bufpos = pos % bufsize;
2160
2161     if(bufpos == 0 && pos > 0) { /* filled our buffer. spit it across the wire */
2162       if ( (i = send(oftconn->fd, buf, bufsize, 0)) != bufsize ) {
2163         dperror("faim: getfile_send: write1");
2164         dprintf("faim: getfile_send: whoopsy, didn't write it all...\n");
2165         free(buf);   
2166         return -1;
2167       }
2168     }
2169     if( (buf[bufpos] = fgetc(file)) == EOF) {
2170       if(pos != fh->size) {
2171         dvprintf("faim: getfile_send: hrm... apparent early EOF at pos 0x%x of 0x%lx\n", pos, fh->size);
2172         free(buf);   
2173         return -1;
2174       }
2175     }      
2176     dvprintf("%c(0x%02x) ", buf[pos], buf[pos]);
2177   }
2178
2179   if( (i = send(oftconn->fd, buf, bufpos+1, 0)) != (bufpos+1)) {
2180     dperror("faim: getfile_send: write2");
2181     dprintf("faim: getfile_send cleanup: whoopsy, didn't write it all...\n");
2182     free(buf);   
2183     return -1;
2184   }
2185
2186   free(buf);
2187   free(fh);  
2188   return 1;
2189 }
2190
2191 int faimtest_getfile_complete(struct aim_session_t *sess, struct command_rx_struct *command, ...) 
2192 {
2193   va_list ap;
2194   struct aim_conn_t *conn;
2195   struct aim_fileheader_t *fh;
2196
2197   va_start(ap, command);
2198   conn = va_arg(ap, struct aim_conn_t *);
2199   fh = va_arg(ap, struct aim_fileheader_t *);
2200   va_end(ap);
2201
2202   dvprintf("faimtest: completed file transfer for %s.\n", fh->name);
2203
2204   aim_conn_close(conn);
2205   aim_conn_kill(sess, &conn);
2206   return 1;
2207 }
2208
2209 int faimtest_getfile_disconnect(struct aim_session_t *sess, struct command_rx_struct *command, ...)
2210 {
2211   va_list ap;
2212   struct aim_conn_t *conn;
2213   char *sn;
2214
2215   va_start(ap, command);
2216   conn = va_arg(ap, struct aim_conn_t *);
2217   sn = va_arg(ap, char *);
2218   va_end(ap);
2219
2220   aim_conn_kill(sess, &conn);
2221
2222   dvprintf("faimtest: getfile: disconnected from %s\n", sn);
2223   return 1;
2224 }
2225 int faimtest_getfile_initiate(struct aim_session_t *sess, struct command_rx_struct *command, ...)
2226 {
2227   va_list ap;
2228   struct aim_conn_t *conn, *listenerconn;
2229   struct aim_filetransfer_priv *priv;
2230
2231   va_start(ap, command);
2232   conn = va_arg(ap, struct aim_conn_t *);
2233   listenerconn = va_arg(ap, struct aim_conn_t *);
2234   va_end(ap);
2235
2236   aim_conn_close(listenerconn);
2237   aim_conn_kill(sess, &listenerconn);
2238
2239   aim_conn_addhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILEREQ,  faimtest_getfile_filereq, 0);
2240   aim_conn_addhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILESEND, faimtest_getfile_filesend, 0);
2241   aim_conn_addhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILECOMPLETE, faimtest_getfile_complete, 0);      
2242   aim_conn_addhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEDISCONNECT, faimtest_getfile_disconnect, 0);      
2243   aim_conn_addhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTING, faimtest_getfile_listing, 0);
2244   aim_conn_addhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTINGREQ, faimtest_getfile_listingreq, 0);
2245   aim_conn_addhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILERECEIVE, faimtest_getfile_receive, 0);
2246   aim_conn_addhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILESTATE4, faimtest_getfile_state4, 0);
2247   
2248   priv = (struct aim_filetransfer_priv *)conn->priv;
2249
2250   dvprintf("faimtest: getfile: %s (%s) connected to us on %d\n", priv->sn, priv->ip, conn->fd);
2251   return 1;
2252 }
2253
2254 int faimtest_getfile_listing(struct aim_session_t *sess, struct command_rx_struct *command, ...)
2255 {
2256   va_list ap;
2257   struct aim_conn_t *conn;
2258   char *listing;
2259   struct aim_filetransfer_priv *ft;
2260   char *filename, *nameend, *sizec;
2261   int filesize, namelen;
2262
2263   va_start(ap, command);
2264   conn = va_arg(ap, struct aim_conn_t *);
2265   ft = va_arg(ap, struct aim_filetransfer_priv *);
2266   listing = va_arg(ap, char *);
2267   va_end(ap);
2268
2269   dvprintf("listing on %d==================\n%s\n===========\n", conn->fd, listing);
2270
2271   nameend = strstr(listing+0x1a, "\r");
2272
2273   namelen = nameend - (listing + 0x1a);
2274
2275   filename = malloc(namelen + 1);
2276   strncpy(filename, listing+0x1a, namelen);
2277   filename[namelen] = 0x00;
2278
2279   sizec = malloc(8+1);
2280   memcpy(sizec, listing + 0x11, 8);
2281   sizec[8] = 0x00;
2282
2283   filesize =  strtol(sizec, (char **)NULL, 10);
2284
2285   dvprintf("faimtest: requesting %d %s(%d long)\n", namelen, filename, filesize);
2286
2287   aim_oft_getfile_request(sess, conn, filename, filesize);
2288
2289   free(filename);
2290   free(sizec);
2291
2292   return 0;
2293 }
2294
2295 int faimtest_getfile_listingreq(struct aim_session_t *sess, struct command_rx_struct *command, ...)
2296 {
2297   va_list ap;
2298   struct aim_conn_t *oftconn;
2299   struct aim_fileheader_t *fh;
2300   int pos, bufpos = 0, bufsize = 2048, i;
2301   char *buf;
2302
2303   va_start(ap, command);
2304   oftconn = va_arg(ap, struct aim_conn_t *);
2305   fh = va_arg(ap, struct aim_fileheader_t *);
2306   va_end(ap);
2307
2308   dvprintf("faimtest: sending listing of size %ld\n", fh->size);
2309
2310   if(!(buf = malloc(2048)))
2311     return -1;
2312
2313   for(pos = 0; pos < fh->size; pos++) {
2314     bufpos = pos % bufsize;
2315
2316     if(bufpos == 0 && pos > 0) { /* filled our buffer. spit it across the wire */
2317       if ( (i = send(oftconn->fd, buf, bufsize, 0)) != bufsize ) {
2318         dperror("faim: getfile_send: write1");
2319         dprintf("faim: getfile_send: whoopsy, didn't write it all...\n");
2320         free(buf);   
2321         return -1;
2322       }
2323     }
2324     if( (buf[bufpos] = fgetc(listingfile)) == EOF) {
2325       if(pos != fh->size) {
2326         dvprintf("faim: getfile_send: hrm... apparent early EOF at pos 0x%x of 0x%lx\n", pos, fh->size);
2327         free(buf);   
2328         return -1;
2329       }
2330     }      
2331   }
2332
2333   if( (i = send(oftconn->fd, buf, bufpos+1, 0)) != (bufpos+1)) {
2334     dperror("faim: getfile_send: write2");
2335     dprintf("faim: getfile_send cleanup: whoopsy, didn't write it all...\n");
2336     free(buf);   
2337     return -1;
2338   }
2339
2340   dprintf("faimtest: sent listing\n");
2341   free(buf);
2342   return 0;
2343 }
2344
2345 int faimtest_getfile_receive(struct aim_session_t *sess, struct command_rx_struct *command, ...)
2346 {
2347   va_list ap;
2348   struct aim_conn_t *conn;
2349   struct aim_filetransfer_priv *ft;
2350   unsigned char data;
2351   int pos;
2352
2353   va_start(ap, command);
2354   conn = va_arg(ap, struct aim_conn_t *);
2355   ft = va_arg(ap, struct aim_filetransfer_priv *);
2356   va_end(ap);
2357
2358   dvprintf("faimtest: receiving %ld bytes of file data for %s:\n\t", ft->fh.size, ft->fh.name);
2359
2360   for(pos = 0; pos < ft->fh.size; pos++) {
2361     read(conn->fd, &data, 1);
2362     printf("%c(%02x) ", data, data);
2363   }
2364    
2365   printf("\n");
2366
2367   aim_oft_getfile_end(sess, conn);
2368
2369   return 0;
2370 }
2371
2372 int faimtest_getfile_state4(struct aim_session_t *sess, struct command_rx_struct *command, ...)
2373 {
2374   va_list ap;
2375   struct aim_conn_t *conn;
2376
2377   va_start(ap, command);
2378   conn = va_arg(ap, struct aim_conn_t *);
2379   va_end(ap);
2380
2381   aim_conn_close(conn);
2382   aim_conn_kill(sess, &conn);
2383   return 0;
2384 }
2385
2386
2387 int faimtest_parse_ratechange(struct aim_session_t *sess, struct command_rx_struct *command, ...)
2388 {
2389   static char *codes[5] = {"invalid",
2390                            "change",
2391                            "warning",
2392                            "limit",
2393                            "limit cleared"};
2394   va_list ap;
2395   int code;
2396   unsigned long rateclass, windowsize, clear, alert, limit, disconnect;
2397   unsigned long currentavg, maxavg;
2398
2399   va_start(ap, command); 
2400
2401   /* See code explanations below */
2402   code = va_arg(ap, int);
2403
2404   /*
2405    * See comments above aim_parse_ratechange_middle() in aim_rxhandlers.c.
2406    */
2407   rateclass = va_arg(ap, unsigned long);
2408
2409   /*
2410    * Not sure what this is exactly.  I think its the temporal 
2411    * relation factor (ie, how to make the rest of the numbers
2412    * make sense in the real world). 
2413    */
2414   windowsize = va_arg(ap, unsigned long);
2415
2416   /* Explained below */
2417   clear = va_arg(ap, unsigned long);
2418   alert = va_arg(ap, unsigned long);
2419   limit = va_arg(ap, unsigned long);
2420   disconnect = va_arg(ap, unsigned long);
2421   currentavg = va_arg(ap, unsigned long);
2422   maxavg = va_arg(ap, unsigned long);
2423
2424   va_end(ap);
2425
2426
2427   dvprintf("faimtest: rate %s (rate class 0x%04lx): curavg = %ld, maxavg = %ld, alert at %ld, clear warning at %ld, limit at %ld, disconnect at %ld (window size = %ld)\n",
2428          (code < 5)?codes[code]:"invalid",
2429          rateclass,
2430          currentavg, maxavg,
2431          alert, clear,
2432          limit, disconnect,
2433          windowsize);
2434
2435   if (code == AIM_RATE_CODE_CHANGE) {
2436     /*
2437      * Not real sure when these get sent.
2438      */
2439     if (currentavg >= clear)
2440       aim_conn_setlatency(command->conn, 0);
2441
2442   } else if (code == AIM_RATE_CODE_WARNING) {
2443     /*
2444      * We start getting WARNINGs the first time we go below the 'alert'
2445      * limit (currentavg < alert) and they stop when either we pause
2446      * long enough for currentavg to go above 'clear', or until we
2447      * flood it bad enough to go below 'limit' (and start getting
2448      * LIMITs instead) or even further and go below 'disconnect' and 
2449      * get disconnected completely (and won't be able to login right
2450      * away either).
2451      */
2452     aim_conn_setlatency(command->conn, windowsize/4); /* XXX this is bogus! */ 
2453
2454   } else if (code == AIM_RATE_CODE_LIMIT) {
2455     /*
2456      * When we hit LIMIT, messages will start getting dropped.
2457      */
2458     aim_conn_setlatency(command->conn, windowsize/2); /* XXX this is bogus! */ 
2459
2460   } else if (code == AIM_RATE_CODE_CLEARLIMIT) {
2461     /*
2462      * The limit is cleared when curavg goes above 'clear'.
2463      */
2464     aim_conn_setlatency(command->conn, 0); 
2465   }
2466
2467   return 1;
2468 }
2469
2470 int faimtest_parse_evilnotify(struct aim_session_t *sess, struct command_rx_struct *command, ...)
2471 {
2472   va_list ap;
2473   int newevil;
2474   struct aim_userinfo_s *userinfo;
2475
2476   va_start(ap, command);
2477   newevil = va_arg(ap, int);
2478   userinfo = va_arg(ap, struct aim_userinfo_s *);
2479   va_end(ap);
2480
2481   /*
2482    * Evil Notifications that are lacking userinfo->sn are anon-warns
2483    * if they are an evil increases, but are not warnings at all if its
2484    * a decrease (its the natural backoff happening).
2485    *
2486    * newevil is passed as an int representing the new evil value times
2487    * ten.
2488    */
2489   dvprintf("faimtest: evil level change: new value = %2.1f%% (caused by %s)\n", ((float)newevil)/10, (userinfo && strlen(userinfo->sn))?userinfo->sn:"anonymous");
2490
2491   return 1;
2492 }
2493
2494 int faimtest_parse_searchreply(struct aim_session_t *sess, struct command_rx_struct *command, ...)
2495 {
2496   va_list ap;
2497   char *address, *SNs;
2498   int i, num;
2499   
2500   va_start(ap, command);
2501   address = va_arg(ap, char *);
2502   num = va_arg(ap, int);
2503   SNs = va_arg(ap, char *);
2504   va_end(ap);
2505
2506   dvprintf("faimtest: E-Mail Search Results for %s: ", address);
2507
2508   for(i = 0; i < num; i++)
2509     dvinlineprintf("%s, ", &SNs[i*(MAXSNLEN+1)]);
2510   dinlineprintf("\n");
2511
2512   return 1;
2513 }
2514
2515 int faimtest_parse_searcherror(struct aim_session_t *sess, struct command_rx_struct *command, ...)
2516 {
2517   va_list ap;
2518   char *address;
2519   
2520   va_start(ap, command);
2521   address = va_arg(ap, char *);
2522   va_end(ap);
2523
2524   dvprintf("faimtest: E-Mail Search Results for %s: No Results or Invalid Email\n", address);
2525
2526   return 1;
2527 }
This page took 1.154131 seconds and 3 git commands to generate.