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