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