]> andersk Git - libfaim.git/blob - utils/faimtest/faimtest.c
- Fri Jun 15 17:51:27 EDT 2001
[libfaim.git] / utils / faimtest / faimtest.c
1 /* 
2  *  -----------------------------------------------------------
3  *  ProtoFAIM: v1.xx.xxplxx
4  *  -----------------------------------------------------------
5  *
6  *  This is ProtoFAIM v1.xx.xxplxx!!! Its nearly completely 
7  *  different than that ugly thing called v0.  This app is
8  *  compatible with the latest version of the libfaim library.
9  *  Work is continuing. 
10  *
11  *  ProtoFAIM should only be used for two things...
12  *   1) Testing the libfaim backend.
13  *   2) For reference on the libfaim API when developing clients.
14  * 
15  *  Its very ugly.  Probably always will be.  Nothing is more
16  *  ugly than the backend itself, however.
17  *
18  *  -----------------------------------------------------------
19  *
20  *  I'm releasing this code and all it's associated linkage
21  *  under the GNU General Public License.  For more information,
22  *  please refer to http://www.fsf.org.  For any questions,
23  *  please contact me at the address below.
24  *
25  *  Most everything:
26  *  (c) 1998 Adam Fritzler, PST, mid@zigamoprh.net
27  *
28  *  The password algorithms
29  *  (c) 1998 Brock Wilcox, awwaiid@zigamorph.net
30  *
31  *  THERE IS NO CODE FROM AOL'S AIM IN THIS CODE, NOR
32  *  WAS THERE ANY DISASSEMBLAGE TO DEFINE PROTOCOL.  All
33  *  information was gained through painstakingly comparing
34  *  TCP dumps while the AIM Java client was running.  Nothing
35  *  more than that, except for a lot of experimenting.
36  *
37  *  -----------------------------------------------------------
38  *
39  */
40
41 #include "faimtest.h"
42 #include <sys/stat.h>
43
44 static char *dprintf_ctime(void)
45 {
46   static char retbuf[64];
47   struct tm *lt;
48   struct timeval tv;
49   struct timezone tz;
50
51   gettimeofday(&tv, &tz);
52   lt = localtime((time_t *)&tv.tv_sec);
53   strftime(retbuf, 64, "%a %b %e %H:%M:%S %Z %Y", lt);
54   return retbuf;
55 }
56
57 #define DPRINTF_OUTSTREAM stdout
58 #define dprintf(x) { \
59   fprintf(DPRINTF_OUTSTREAM, "%s  %s: " x, dprintf_ctime(), "faimtest"); \
60   fflush(DPRINTF_OUTSTREAM); \
61 }
62 #define dvprintf(x, y...) { \
63   fprintf(DPRINTF_OUTSTREAM, "%s  %s: " x, dprintf_ctime(), "faimtest", y); \
64   fflush(DPRINTF_OUTSTREAM); \
65 }
66 #define dinlineprintf(x) { \
67   fprintf(DPRINTF_OUTSTREAM, x); \
68   fflush(DPRINTF_OUTSTREAM); \
69 }
70 #define dvinlineprintf(x, y...) { \
71   fprintf(DPRINTF_OUTSTREAM, x, y); \
72   fflush(DPRINTF_OUTSTREAM); \
73 }
74 #define dperror(x) dvprintf("%s: %s\n", x, strerror(errno));
75
76 int faimtest_parse_oncoming(struct aim_session_t *, struct command_rx_struct *, ...);
77 int faimtest_parse_offgoing(struct aim_session_t *, struct command_rx_struct *, ...);
78 int faimtest_parse_login_phase3d_f(struct aim_session_t *, struct command_rx_struct *, ...);
79 static int faimtest_parse_authresp(struct aim_session_t *, struct command_rx_struct *, ...);
80 int faimtest_parse_incoming_im(struct aim_session_t *, struct command_rx_struct *command, ...);
81 int faimtest_parse_userinfo(struct aim_session_t *, struct command_rx_struct *command, ...);
82 int faimtest_handleredirect(struct aim_session_t *, struct command_rx_struct *command, ...);
83 int faimtest_infochange(struct aim_session_t *sess, struct command_rx_struct *command, ...);
84 int faimtest_serverready(struct aim_session_t *, struct command_rx_struct *command, ...);
85 int faimtest_hostversions(struct aim_session_t *sess, struct command_rx_struct *command, ...);
86 int faimtest_parse_misses(struct aim_session_t *, struct command_rx_struct *command, ...);
87 int faimtest_parse_msgack(struct aim_session_t *, struct command_rx_struct *command, ...);
88 int faimtest_parse_motd(struct aim_session_t *, struct command_rx_struct *command, ...);
89 int faimtest_parse_login(struct aim_session_t *, struct command_rx_struct *command, ...);
90 int faimtest_chatnav_info(struct aim_session_t *, struct command_rx_struct *command, ...);
91 int faimtest_chat_incomingmsg(struct aim_session_t *sess, struct command_rx_struct *command, ...);
92 int faimtest_chat_infoupdate(struct aim_session_t *sess, struct command_rx_struct *command, ...);
93 int faimtest_chat_leave(struct aim_session_t *sess, struct command_rx_struct *command, ...);
94 int faimtest_chat_join(struct aim_session_t *sess, struct command_rx_struct *command, ...);
95 int faimtest_parse_connerr(struct aim_session_t *sess, struct command_rx_struct *command, ...);
96 int faimtest_debugconn_connect(struct aim_session_t *sess, struct command_rx_struct *command, ...);
97
98 int faimtest_directim_request(struct aim_session_t *sess, struct command_rx_struct *command, ...);
99 int faimtest_directim_initiate(struct aim_session_t *sess, struct command_rx_struct *command, ...);
100 int faimtest_directim_connect(struct aim_session_t *sess, struct command_rx_struct *command, ...);
101 int faimtest_directim_incoming(struct aim_session_t *sess, struct command_rx_struct *command, ...);
102 int faimtest_directim_disconnect(struct aim_session_t *sess, struct command_rx_struct *command, ...);
103 int faimtest_directim_typing(struct aim_session_t *sess, struct command_rx_struct *command, ...);
104
105 int faimtest_getfile_filereq(struct aim_session_t *sess, struct command_rx_struct *command, ...);
106 int faimtest_getfile_filesend(struct aim_session_t *sess, struct command_rx_struct *command, ...);
107 int faimtest_getfile_complete(struct aim_session_t *sess, struct command_rx_struct *command, ...);
108 int faimtest_getfile_disconnect(struct aim_session_t *sess, struct command_rx_struct *command, ...);
109 int faimtest_getfile_initiate(struct aim_session_t *sess, struct command_rx_struct *command, ...);
110 int faimtest_getfile_listing(struct aim_session_t *sess, struct command_rx_struct *command, ...);
111 int faimtest_getfile_listingreq(struct aim_session_t *sess, struct command_rx_struct *command, ...);
112 int faimtest_getfile_receive(struct aim_session_t *sess, struct command_rx_struct *command, ...);
113 int faimtest_getfile_state4(struct aim_session_t *sess, struct command_rx_struct *command, ...);
114
115 int faimtest_parse_ratechange(struct aim_session_t *sess, struct command_rx_struct *command, ...);
116 int faimtest_parse_evilnotify(struct aim_session_t *sess, struct command_rx_struct *command, ...);
117 int faimtest_parse_searcherror(struct aim_session_t *sess, struct command_rx_struct *command, ...);
118 int faimtest_parse_searchreply(struct aim_session_t *sess, struct command_rx_struct *command, ...);
119 int faimtest_parse_msgerr(struct aim_session_t *sess, struct command_rx_struct *command, ...);
120 int faimtest_parse_buddyrights(struct aim_session_t *sess, struct command_rx_struct *command, ...);
121 int faimtest_parse_locerr(struct aim_session_t *sess, struct command_rx_struct *command, ...);
122 int faimtest_parse_genericerr(struct aim_session_t *sess, struct command_rx_struct *command, ...);
123
124 static char *msgerrreasons[] = {
125   "Invalid error",
126   "Invalid SNAC",
127   "Rate to host",
128   "Rate to client",
129   "Not logged on",
130   "Service unavailable",
131   "Service not defined",
132   "Obsolete SNAC",
133   "Not supported by host",
134   "Not supported by client",
135   "Refused by client",
136   "Reply too big",
137   "Responses lost",
138   "Request denied",
139   "Busted SNAC payload",
140   "Insufficient rights",
141   "In local permit/deny",
142   "Too evil (sender)",
143   "Too evil (receiver)",
144   "User temporarily unavailable",
145   "No match",
146   "List overflow",
147   "Request ambiguous",
148   "Queue full",
149   "Not while on AOL"};
150 static int msgerrreasonslen = 25;
151
152 static char *aimbinarypath = NULL;
153 static char *screenname,*password,*server=NULL;
154 static char *proxy = NULL, *proxyusername = NULL, *proxypass = NULL;
155 static char *ohcaptainmycaptain = NULL;
156 static int connected = 0;
157
158 struct aim_session_t aimsess;
159 int keepgoing = 1;
160
161 static FILE *listingfile;
162 static char *listingpath;
163
164 static unsigned char *buddyicon = NULL;
165 static int buddyiconlen = 0;
166 static time_t buddyiconstamp = 0;
167 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
1377     args = va_arg(ap, struct aim_incomingim_ch1_args *);
1378     va_end(ap);
1379     
1380     clienttype = aim_fingerprintclient(args->fingerprint, args->finlen);
1381
1382     dvprintf("faimtest: icbm: sn = \"%s\"\n", userinfo->sn);
1383     dvprintf("faimtest: icbm: probable client type: %d\n", clienttype);
1384     dvprintf("faimtest: icbm: warnlevel = 0x%04x\n", userinfo->warnlevel);
1385     dvprintf("faimtest: icbm: flags = 0x%04x = ", userinfo->flags);
1386     printuserflags(userinfo->flags);
1387     dinlineprintf("\n");
1388
1389     dvprintf("faimtest: icbm: membersince = %lu\n", userinfo->membersince);
1390     dvprintf("faimtest: icbm: onlinesince = %lu\n", userinfo->onlinesince);
1391     dvprintf("faimtest: icbm: idletime = 0x%04x\n", userinfo->idletime);
1392     dvprintf("faimtest: icbm: capabilities = 0x%04x\n", userinfo->capabilities);
1393     
1394     dprintf("faimtest: icbm: icbmflags = ");
1395     if (args->icbmflags & AIM_IMFLAGS_AWAY)
1396       dinlineprintf("away ");
1397     if (args->icbmflags & AIM_IMFLAGS_ACK)
1398       dinlineprintf("ackrequest ");
1399     if (args->icbmflags & AIM_IMFLAGS_BUDDYREQ)
1400       dinlineprintf("buddyreq ");
1401     if (args->icbmflags & AIM_IMFLAGS_HASICON)
1402       dinlineprintf("hasicon ");
1403     dinlineprintf("\n");
1404     
1405     dvprintf("faimtest: icbm: encoding flags = {%04x, %04x}\n", args->flag1, args->flag2);
1406
1407     dvprintf("faimtest: icbm: message: %s\n", args->msg);
1408
1409     if (args->icbmflags & AIM_IMFLAGS_HASICON)
1410       aim_send_im(sess, command->conn, userinfo->sn, AIM_IMFLAGS_BUDDYREQ, "You have an icon");
1411
1412     if (args->msg) {
1413       int i = 0;
1414
1415       while (args->msg[i] == '<') {
1416         if (args->msg[i] == '<') {
1417           while (args->msg[i] != '>')
1418             i++;
1419           i++;
1420         }
1421       }
1422       tmpstr = args->msg+i;
1423
1424       faimtest_handlecmd(sess, command, userinfo, tmpstr);
1425
1426     }
1427   }
1428   /*
1429    * Channel 2: Rendevous Request
1430    */
1431   else if (channel == 2) {
1432     struct aim_incomingim_ch2_args *args;
1433     
1434     args = va_arg(ap, struct aim_incomingim_ch2_args *);
1435     va_end(ap);
1436
1437     switch (args->reqclass) {
1438     case AIM_CAPS_VOICE: {
1439       
1440       dvprintf("faimtest: voice invitation: source sn = %s\n", userinfo->sn);
1441       dvprintf("faimtest: voice invitation: \twarnlevel = 0x%04x\n", userinfo->warnlevel);
1442       dvprintf("faimtest: voice invitation: \tclass = 0x%04x = ", userinfo->flags);
1443       printuserflags(userinfo->flags);
1444       dinlineprintf("\n");
1445
1446       /* we dont get membersince on chat invites! */
1447       dvprintf("faimtest: voice invitation: \tonlinesince = %lu\n", userinfo->onlinesince);
1448       dvprintf("faimtest: voice invitation: \tidletime = 0x%04x\n", userinfo->idletime);
1449       
1450       break;
1451     }
1452     case AIM_CAPS_GETFILE: {
1453       struct aim_conn_t *newconn;
1454       struct aim_fileheader_t *fh;
1455
1456       dvprintf("faimtest: get file request from %s (at %s) %x\n", userinfo->sn, args->info.getfile.ip, args->reqclass);
1457
1458       fh = aim_getlisting(sess, listingfile);
1459
1460       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);
1461
1462       if ( (!newconn) || (newconn->fd == -1) ) {
1463         dprintf("faimtest: getfile: requestconn: apparent error in accepttransfer\n");
1464         if(newconn)
1465           aim_conn_kill(sess, &newconn);
1466         break;
1467       }
1468
1469       free(fh);
1470
1471       aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTINGREQ, faimtest_getfile_listingreq, 0);
1472       aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILEREQ,  faimtest_getfile_filereq, 0);
1473       aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILESEND, faimtest_getfile_filesend, 0);
1474       aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILECOMPLETE, faimtest_getfile_complete, 0);      
1475
1476       aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEDISCONNECT, faimtest_getfile_disconnect, 0);      
1477
1478       dprintf("faimtest: getfile connect succeeded, handlers added.\n");
1479
1480       break;
1481     }
1482     case AIM_CAPS_SENDFILE: {
1483       dprintf("faimtest: send file!\n");
1484       break;
1485     }
1486     case AIM_CAPS_CHAT: {
1487       
1488       dvprintf("faimtest: chat invitation: source sn = %s\n", userinfo->sn);
1489       dvprintf("faimtest: chat invitation: \twarnlevel = 0x%04x\n", userinfo->warnlevel);
1490       dvprintf("faimtest: chat invitation: \tclass = 0x%04x = ", userinfo->flags);
1491       printuserflags(userinfo->flags);
1492       dinlineprintf("\n");
1493
1494       /* we dont get membersince on chat invites! */
1495       dvprintf("faimtest: chat invitation: \tonlinesince = %lu\n", userinfo->onlinesince);
1496       dvprintf("faimtest: chat invitation: \tidletime = 0x%04x\n", userinfo->idletime);
1497       
1498       dvprintf("faimtest: chat invitation: message = %s\n", args->info.chat.msg);
1499       dvprintf("faimtest: chat invitation: room name = %s\n", args->info.chat.roominfo.name);
1500       dvprintf("faimtest: chat invitation: encoding = %s\n", args->info.chat.encoding);
1501       dvprintf("faimtest: chat invitation: language = %s\n", args->info.chat.lang);
1502       dvprintf("faimtest: chat invitation: exchange = 0x%04x\n", args->info.chat.roominfo.exchange);
1503       dvprintf("faimtest: chat invitation: instance = 0x%04x\n", args->info.chat.roominfo.instance);
1504       dvprintf("faimtest: chat invitiation: autojoining %s...\n", args->info.chat.roominfo.name);
1505
1506       /*
1507        * Automatically join room...
1508        */ 
1509       aim_chat_join(sess, command->conn, args->info.chat.roominfo.exchange, args->info.chat.roominfo.name);
1510       break;
1511     }   
1512     case AIM_CAPS_IMIMAGE: {
1513       struct aim_conn_t *newconn;
1514
1515       dprintf("faimtest: icbm: rendezvous imimage\n");
1516
1517       dvprintf("faimtest: OFT: DirectIM: request from %s (%s)\n", userinfo->sn, args->info.directim->ip);
1518       
1519       newconn = aim_directim_connect(sess, command->conn, args->info.directim);
1520
1521       if ( (!newconn) || (newconn->fd == -1) ) {
1522         dprintf("faimtest: icbm: imimage: could not connect\n");
1523
1524         if (newconn)
1525           aim_conn_kill(sess, &newconn);
1526
1527         break;
1528       }
1529
1530       aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING, faimtest_directim_incoming, 0);
1531       aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMDISCONNECT, faimtest_directim_disconnect, 0);
1532       aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING, faimtest_directim_typing, 0);
1533
1534       dvprintf("faimtest: OFT: DirectIM: connected to %s\n", userinfo->sn);
1535
1536       aim_send_im_direct(sess, newconn, "goodday");
1537
1538       break;
1539     }
1540     case AIM_CAPS_BUDDYICON: {
1541
1542       dvprintf("faimtest: Buddy Icon from %s, length = %u\n", userinfo->sn, args->info.icon.length);
1543       break;
1544     }
1545     default:
1546       dvprintf("faimtest: icbm: unknown reqclass (%d)\n", args->reqclass);
1547     } /* switch */
1548   } else
1549     dvprintf("faimtest does not support channels > 2 (chan = %02x)\n", channel);
1550
1551   dprintf("faimtest: icbm: done with ICBM handling\n");
1552
1553   return 1;
1554 }
1555
1556 int faimtest_directim_initiate(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1557 {
1558   va_list ap;
1559   struct aim_directim_priv *priv;
1560   struct aim_conn_t *newconn, *listenerconn;
1561
1562   va_start(ap, command);
1563   newconn = va_arg(ap, struct aim_conn_t *);
1564   listenerconn = va_arg(ap, struct aim_conn_t *);
1565   va_end(ap);
1566
1567   aim_conn_close(listenerconn);
1568   aim_conn_kill(sess, &listenerconn);
1569
1570   priv = (struct aim_directim_priv *)newconn->priv;
1571
1572   dvprintf("faimtest: OFT: DirectIM: intitiate success to %s\n", priv->ip);
1573   
1574   aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING, faimtest_directim_incoming, 0);
1575   aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMDISCONNECT, faimtest_directim_disconnect, 0);
1576   aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING, faimtest_directim_typing, 0);
1577
1578   aim_send_im_direct(sess, newconn, "goodday");
1579
1580   dvprintf("faimtest: OFT: DirectIM: connected to %s\n", priv->sn);
1581
1582   return 1;
1583 }
1584
1585 int faimtest_directim_connect(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1586 {
1587   va_list ap;
1588   struct aim_directim_priv *priv;
1589   
1590   va_start(ap, command);
1591   priv = va_arg(ap, struct aim_directim_priv *);
1592
1593   va_end(ap);
1594   
1595   dprintf("faimtest: directim_connect\n");
1596
1597   return 1;
1598 }
1599
1600 int faimtest_directim_incoming(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1601 {
1602   va_list ap;
1603   char *msg = NULL;
1604   struct aim_conn_t *conn;
1605   struct aim_directim_priv *priv;
1606
1607   va_start(ap, command);
1608   conn = va_arg(ap, struct aim_conn_t *);
1609   msg = va_arg(ap, char *);
1610   va_end(ap);
1611
1612   if(!(priv = conn->priv)) {
1613     dvprintf("faimtest: directim: no private struct on conn with fd %d\n", conn->fd);
1614     return -1;
1615   }
1616
1617   dvprintf("faimtest: Directim from %s: %s\n", priv->sn, msg);
1618   if (!strncmp(msg, "sendmsg", 7)) {
1619     int i;
1620     i = atoi(msg+8);
1621     if (i < 10000) {
1622       char *newbuf;
1623       int z;
1624       
1625       newbuf = malloc(i+1);
1626       for (z = 0; z < i; z++) {
1627         newbuf[z] = (z % 10)+0x30;
1628       }
1629       newbuf[i] = '\0';
1630       aim_send_im_direct(sess, conn, newbuf);
1631       free(newbuf);
1632     }
1633   } else if (!strncmp(msg, "goodday", 7)) {
1634     aim_send_im_direct(sess, conn, "Good day to you, too");
1635   } else {
1636     char newmsg[1024];
1637     snprintf(newmsg, sizeof(newmsg), "unknown (%s)\n", msg);
1638     aim_send_im_direct(sess, conn, newmsg);
1639   }
1640   return 1;
1641 }
1642
1643 int faimtest_directim_disconnect(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1644 {
1645   va_list ap;
1646   struct aim_conn_t *conn;
1647   char *sn;
1648
1649   va_start(ap, command);
1650   conn = va_arg(ap, struct aim_conn_t *);
1651   sn = va_arg(ap, char *);
1652   va_end(ap);
1653
1654   dvprintf("faimtest: directim: disconnected from %s\n", sn);
1655
1656   aim_conn_kill(sess, &conn);
1657   return 1;
1658 }
1659
1660 int faimtest_directim_typing(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1661 {
1662   va_list ap;
1663   struct aim_conn_t *conn;
1664   struct aim_directim_priv *priv;
1665
1666   va_start(ap, command);
1667   conn = va_arg(ap, struct aim_conn_t *);
1668   va_end(ap);
1669   
1670   if(!(priv = (struct aim_directim_priv *)conn->priv)) {
1671     dvprintf("faimtest: no private struct on conn with fd %d!\n", conn->fd);
1672     return -1;
1673   }
1674
1675   dvprintf("faimtest: ohmigod! %s has started typing (DirectIM). He's going to send you a message! *squeal*\n", priv->sn);
1676   return 1;
1677 }
1678
1679 int faimtest_infochange(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1680 {
1681   unsigned short change = 0;
1682   int perms, type, length, str;
1683   char *val;
1684   va_list ap;
1685
1686   va_start(ap, command);
1687   perms = va_arg(ap, int);
1688   type = va_arg(ap, int);
1689   length = va_arg(ap, int);
1690   val = va_arg(ap, char *);
1691   str = va_arg(ap, int);
1692   va_end(ap);
1693
1694   if (aimutil_get16(command->data+2) == 0x0005)
1695     change = 1;
1696
1697   dvprintf("info%s: perms = %d, type = %x, length = %d, val = %s\n", change?" change":"", perms, type, length, str?val:"(not string)");
1698
1699   return 1;
1700 }
1701
1702 int faimtest_parse_oncoming(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1703 {
1704   struct aim_userinfo_s *userinfo;
1705    
1706   va_list ap;
1707   va_start(ap, command);
1708   userinfo = va_arg(ap, struct aim_userinfo_s *);
1709   va_end(ap);
1710
1711   dvprintf("%ld  %s is now online (flags: %04x = %s%s%s%s%s%s%s%s) (caps = 0x%04x)\n",
1712          time(NULL),
1713          userinfo->sn, userinfo->flags,
1714          (userinfo->flags&AIM_FLAG_UNCONFIRMED)?" UNCONFIRMED":"",
1715          (userinfo->flags&AIM_FLAG_ADMINISTRATOR)?" ADMINISTRATOR":"",
1716          (userinfo->flags&AIM_FLAG_AOL)?" AOL":"",
1717          (userinfo->flags&AIM_FLAG_OSCAR_PAY)?" OSCAR_PAY":"",
1718          (userinfo->flags&AIM_FLAG_FREE)?" FREE":"",
1719          (userinfo->flags&AIM_FLAG_AWAY)?" AWAY":"",
1720          (userinfo->flags&AIM_FLAG_UNKNOWN40)?" UNKNOWN40":"",
1721          (userinfo->flags&AIM_FLAG_UNKNOWN80)?" UNKNOWN80":"",
1722          userinfo->capabilities);
1723   return 1;
1724 }
1725
1726 int faimtest_parse_offgoing(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1727 {
1728   struct aim_userinfo_s *userinfo;
1729    
1730   va_list ap;
1731   va_start(ap, command);
1732   userinfo = va_arg(ap, struct aim_userinfo_s *);
1733   va_end(ap);
1734
1735   dvprintf("%ld  %s is now offline (flags: %04x = %s%s%s%s%s%s%s%s) (caps = 0x%04x)\n",
1736          time(NULL),
1737          userinfo->sn, userinfo->flags,
1738          (userinfo->flags&AIM_FLAG_UNCONFIRMED)?" UNCONFIRMED":"",
1739          (userinfo->flags&AIM_FLAG_ADMINISTRATOR)?" ADMINISTRATOR":"",
1740          (userinfo->flags&AIM_FLAG_AOL)?" AOL":"",
1741          (userinfo->flags&AIM_FLAG_OSCAR_PAY)?" OSCAR_PAY":"",
1742          (userinfo->flags&AIM_FLAG_FREE)?" FREE":"",
1743          (userinfo->flags&AIM_FLAG_AWAY)?" AWAY":"",
1744          (userinfo->flags&AIM_FLAG_UNKNOWN40)?" UNKNOWN40":"",
1745          (userinfo->flags&AIM_FLAG_UNKNOWN80)?" UNKNOWN80":"",
1746          userinfo->capabilities);
1747   return 1;
1748 }
1749
1750 int faimtest_parse_motd(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1751 {
1752   static char *codes[] = {
1753     "Unknown",
1754     "Mandatory upgrade",
1755     "Advisory upgrade",
1756     "System bulletin",
1757     "Top o' the world!"};
1758   static int codeslen = 5;
1759
1760   char *msg;
1761   unsigned short id;
1762   va_list ap;
1763   
1764   va_start(ap, command);
1765   id = va_arg(ap, int);
1766   msg = va_arg(ap, char *);
1767   va_end(ap);
1768
1769   dvprintf("faimtest: motd: %s (%d / %s)\n", msg, id, 
1770            (id < codeslen)?codes[id]:"unknown");
1771
1772   if (!connected)
1773     connected++;
1774
1775 #if 0
1776   aim_bos_reqservice(sess, command->conn, 0x0005); /* adverts */
1777   aim_bos_reqservice(sess, command->conn, 0x000f); /* user directory */
1778
1779   /* Don't know what this does... */
1780   /* XXX sess->sn should be normalized by the 0001/000f handler */
1781   aim_0002_000b(sess, command->conn, sess->sn);
1782 #endif
1783
1784   return 1;
1785 }
1786
1787 int faimtest_parse_genericerr(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1788 {
1789   va_list ap;
1790   unsigned short reason;
1791
1792   va_start(ap, command);
1793   reason = va_arg(ap, int);
1794   va_end(ap);
1795
1796   dvprintf("faimtest: snac threw error (reason 0x%04x: %s)\n", reason, (reason<msgerrreasonslen)?msgerrreasons[reason]:"unknown");
1797   
1798   return 1;
1799 }
1800
1801 int faimtest_parse_msgerr(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1802 {
1803   va_list ap;
1804   char *destsn;
1805   unsigned short reason;
1806
1807   va_start(ap, command);
1808   reason = va_arg(ap, int);
1809   destsn = va_arg(ap, char *);
1810   va_end(ap);
1811
1812   dvprintf("faimtest: message to %s bounced (reason 0x%04x: %s)\n", destsn, reason, (reason<msgerrreasonslen)?msgerrreasons[reason]:"unknown");
1813   
1814   return 1;
1815 }
1816
1817 int faimtest_parse_locerr(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1818 {
1819   va_list ap;
1820   char *destsn;
1821   unsigned short reason;
1822
1823   va_start(ap, command);
1824   reason = va_arg(ap, int);
1825   destsn = va_arg(ap, char *);
1826   va_end(ap);
1827
1828   dvprintf("faimtest: user information for %s unavailable (reason 0x%04x: %s)\n", destsn, reason, (reason<msgerrreasonslen)?msgerrreasons[reason]:"unknown");
1829   
1830   return 1;
1831 }
1832
1833 /* 
1834  * Handles callbacks for AIM_CB_MISSED_CALL.
1835  */
1836 int faimtest_parse_misses(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1837 {
1838   static char *missedreasons[] = {
1839     "Unknown",
1840     "Message too large"};
1841   static int missedreasonslen = 2;
1842
1843   va_list ap;
1844   unsigned short chan, nummissed, reason;
1845   struct aim_userinfo_s *userinfo;
1846   
1847   va_start(ap, command);
1848   chan = va_arg(ap, int);
1849   userinfo = va_arg(ap, struct aim_userinfo_s *);
1850   nummissed = va_arg(ap, int);
1851   reason = va_arg(ap, int);
1852   va_end(ap);
1853
1854   dvprintf("faimtest: missed %d messages from %s (reason %d: %s)\n", nummissed, userinfo->sn, reason, (reason<missedreasonslen)?missedreasons[reason]:"unknown");
1855   
1856   return 1;
1857 }
1858
1859 int faimtest_parse_login(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1860 {
1861   struct client_info_s info = AIM_CLIENTINFO_KNOWNGOOD;
1862   char *key;
1863   va_list ap;
1864   
1865   va_start(ap, command);
1866   key = va_arg(ap, char *);
1867   va_end(ap);
1868
1869   aim_send_login(sess, command->conn, screenname, password, &info, key);
1870  
1871   return 1;
1872 }
1873
1874 int faimtest_chat_join(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1875 {
1876   va_list ap;
1877   struct aim_userinfo_s *userinfo;
1878   int count = 0, i = 0;
1879   
1880   va_start(ap, command);
1881   count = va_arg(ap, int);
1882   userinfo = va_arg(ap, struct aim_userinfo_s *);
1883   va_end(ap);
1884
1885   dvprintf("faimtest: chat: %s:  New occupants have joined:\n", (char *)command->conn->priv);
1886   while (i < count)
1887     dvprintf("faimtest: chat: %s: \t%s\n", (char *)command->conn->priv, userinfo[i++].sn);
1888
1889   return 1;
1890 }
1891
1892 int faimtest_chat_leave(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1893 {
1894   va_list ap;
1895   struct aim_userinfo_s *userinfo;
1896   int count = 0, i = 0;
1897   
1898   va_start(ap, command);
1899   count = va_arg(ap, int);
1900   userinfo = va_arg(ap, struct aim_userinfo_s *);
1901   va_end(ap);
1902
1903   dvprintf("faimtest: chat: %s:  Some occupants have left:\n", (char *)command->conn->priv);
1904
1905   for (i = 0; i < count; )
1906     dvprintf("faimtest: chat: %s: \t%s\n", (char *)command->conn->priv, userinfo[i++].sn);
1907
1908   return 1;
1909 }
1910
1911 int faimtest_chat_infoupdate(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1912 {
1913   va_list ap;
1914   struct aim_userinfo_s *userinfo;
1915   struct aim_chat_roominfo *roominfo;
1916   char *roomname;
1917   int usercount,i;
1918   char *roomdesc;
1919   unsigned short unknown_c9, unknown_d2, unknown_d5, maxmsglen;
1920   unsigned long creationtime;
1921
1922   va_start(ap, command);
1923   roominfo = va_arg(ap, struct aim_chat_roominfo *);
1924   roomname = va_arg(ap, char *);
1925   usercount= va_arg(ap, int);
1926   userinfo = va_arg(ap, struct aim_userinfo_s *);
1927   roomdesc = va_arg(ap, char *);
1928   unknown_c9 = va_arg(ap, int);
1929   creationtime = va_arg(ap, unsigned long);
1930   maxmsglen = va_arg(ap, int);
1931   unknown_d2 = va_arg(ap, int);
1932   unknown_d5 = va_arg(ap, int);
1933   va_end(ap);
1934
1935   dvprintf("faimtest: chat: %s:  info update:\n", (char *)command->conn->priv);
1936   dvprintf("faimtest: chat: %s:  \tRoominfo: {%04x, %s, %04x}\n", 
1937          (char *)command->conn->priv,
1938          roominfo->exchange,
1939          roominfo->name,
1940          roominfo->instance);
1941   dvprintf("faimtest: chat: %s:  \tRoomname: %s\n", (char *)command->conn->priv, roomname);
1942   dvprintf("faimtest: chat: %s:  \tRoomdesc: %s\n", (char *)command->conn->priv, roomdesc);
1943   dvprintf("faimtest: chat: %s:  \tOccupants: (%d)\n", (char *)command->conn->priv, usercount);
1944   
1945   for (i = 0; i < usercount; )
1946     dvprintf("faimtest: chat: %s:  \t\t%s\n", (char *)command->conn->priv, userinfo[i++].sn);
1947
1948   dvprintf("faimtest: chat: %s:  \tUnknown_c9: 0x%04x\n", (char *)command->conn->priv, unknown_c9);
1949   dvprintf("faimtest: chat: %s:  \tCreation time: %lu (time_t)\n", (char *)command->conn->priv, creationtime);
1950   dvprintf("faimtest: chat: %s:  \tMax message length: %d bytes\n", (char *)command->conn->priv, maxmsglen);
1951   dvprintf("faimtest: chat: %s:  \tUnknown_d2: 0x%04x\n", (char *)command->conn->priv, unknown_d2);
1952   dvprintf("faimtest: chat: %s:  \tUnknown_d5: 0x%02x\n", (char *)command->conn->priv, unknown_d5);
1953
1954   return 1;
1955 }
1956
1957 int faimtest_chat_incomingmsg(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1958 {
1959   va_list ap;
1960   struct aim_userinfo_s *userinfo;
1961   char *msg;
1962   char tmpbuf[1152];
1963  
1964   va_start(ap, command);
1965   userinfo = va_arg(ap, struct aim_userinfo_s *);       
1966   msg = va_arg(ap, char *);
1967   va_end(ap);
1968
1969   dvprintf("faimtest: chat: %s: incoming msg from %s: %s\n", (char *)command->conn->priv, userinfo->sn, msg);
1970
1971   /*
1972    * Do an echo for testing purposes.  But not for ourselves ("oops!")
1973    */
1974   if (strcmp(userinfo->sn, sess->sn) != 0)
1975     {
1976       sprintf(tmpbuf, "(%s said \"%s\")", userinfo->sn, msg);
1977       aim_chat_send_im(sess, command->conn, 0, tmpbuf, strlen(tmpbuf));
1978     }
1979
1980   return 1;
1981 }
1982
1983 int faimtest_chatnav_info(struct aim_session_t *sess, struct command_rx_struct *command, ...)
1984 {
1985   unsigned short type;
1986   va_list ap;
1987
1988   va_start(ap, command);
1989   type = va_arg(ap, int);
1990
1991   switch(type) {
1992   case 0x0002: {
1993     int maxrooms;
1994     struct aim_chat_exchangeinfo *exchanges;
1995     int exchangecount,i = 0;
1996     
1997     maxrooms = va_arg(ap, int);
1998     exchangecount = va_arg(ap, int);
1999     exchanges = va_arg(ap, struct aim_chat_exchangeinfo *);
2000     va_end(ap);
2001     
2002     dprintf("faimtest: chat info: Chat Rights:\n");
2003     dvprintf("faimtest: chat info: \tMax Concurrent Rooms: %d\n", maxrooms);
2004     
2005     dvprintf("faimtest: chat info: \tExchange List: (%d total)\n", exchangecount);
2006     for (i = 0; i < exchangecount; i++) {
2007       dvprintf("faimtest: chat info: \t\t%x: %s (%s/%s)\n", 
2008                exchanges[i].number,     
2009                exchanges[i].name,
2010                exchanges[i].charset1,
2011                exchanges[i].lang1);
2012     }
2013     
2014   }
2015   break;
2016   case 0x0008: {
2017     char *fqcn, *name, *ck;
2018     unsigned short instance, flags, maxmsglen, maxoccupancy, unknown, exchange;
2019     unsigned char createperms;
2020     unsigned long createtime;
2021
2022     fqcn = va_arg(ap, char *);
2023     instance = va_arg(ap, int);
2024     exchange = va_arg(ap, int);
2025     flags = va_arg(ap, int);
2026     createtime = va_arg(ap, unsigned long);
2027     maxmsglen = va_arg(ap, int);
2028     maxoccupancy = va_arg(ap, int);
2029     createperms = va_arg(ap, int);
2030     unknown = va_arg(ap, int);
2031     name = va_arg(ap, char *);
2032     ck = va_arg(ap, char *);
2033     va_end(ap);
2034
2035     dvprintf("faimtest: received room create reply for %s/0x%04x\n", fqcn, exchange);
2036   }
2037   break;
2038   default:
2039     va_end(ap);
2040     dvprintf("faimtest: chatnav info: unknown type (%04x)\n", type);
2041   }
2042   return 1;
2043 }
2044
2045 int faimtest_parse_connerr(struct aim_session_t *sess, struct command_rx_struct *command, ...)
2046 {
2047   va_list ap;
2048   unsigned short code;
2049   char *msg = NULL;
2050
2051   va_start(ap, command);
2052   code = va_arg(ap, int);
2053   msg = va_arg(ap, char *);
2054   va_end(ap);
2055
2056   dvprintf("faimtest: connerr: Code 0x%04x: %s\n", code, msg);
2057   aim_conn_kill(sess, &command->conn); /* this will break the main loop */
2058
2059   connected = 0;
2060
2061   return 1;
2062 }
2063
2064 int faimtest_debugconn_connect(struct aim_session_t *sess, struct command_rx_struct *command, ...)
2065 {       
2066   dprintf("faimtest: connecting to an aimdebugd!\n");
2067
2068   /* convert the authorizer connection to a BOS connection */
2069   command->conn->type = AIM_CONN_TYPE_BOS;
2070
2071   aim_conn_addhandler(sess, command->conn, AIM_CB_FAM_MSG, AIM_CB_MSG_INCOMING, faimtest_parse_incoming_im, 0);
2072
2073   /* tell the aimddebugd we're ready */
2074   aim_debugconn_sendconnect(sess, command->conn); 
2075
2076   /* go right into main loop (don't open a BOS connection, etc) */
2077   return 1;
2078 }
2079
2080 /*
2081  * Received in response to an IM sent with the AIM_IMFLAGS_ACK option.
2082  */
2083 int faimtest_parse_msgack(struct aim_session_t *sess, struct command_rx_struct *command, ...)
2084 {
2085   va_list ap;
2086   unsigned short type;
2087   char *sn = NULL;
2088
2089   va_start(ap, command);
2090   type = va_arg(ap, int);
2091   sn = va_arg(ap, char *);
2092   va_end(ap);
2093
2094   dvprintf("faimtest: msgack: 0x%04x / %s\n", type, sn);
2095
2096   return 1;
2097 }
2098
2099 int faimtest_getfile_filereq(struct aim_session_t *ses, struct command_rx_struct *command, ...)
2100 {
2101   va_list ap;
2102   struct aim_conn_t *oftconn;
2103   struct aim_fileheader_t *fh;
2104   char *cookie;
2105
2106   va_start(ap, command);
2107   oftconn = va_arg(ap, struct aim_conn_t *);
2108   fh = va_arg(ap, struct aim_fileheader_t *);
2109   cookie = va_arg(ap, char *);
2110   va_end(ap);
2111
2112   dvprintf("faimtest: request for file %s.\n", fh->name);
2113
2114   return 1;
2115 }
2116
2117
2118 int faimtest_getfile_filesend(struct aim_session_t *sess, struct command_rx_struct *command, ...)
2119 {
2120   va_list ap;
2121   struct aim_conn_t *oftconn;
2122   struct aim_fileheader_t *fh;
2123   char *path, *cookie;
2124   int pos, bufpos = 0, bufsize = 2048, i;
2125   char *buf;
2126
2127   FILE *file;
2128
2129   va_start(ap, command);
2130   oftconn = va_arg(ap, struct aim_conn_t *);
2131   fh = va_arg(ap, struct aim_fileheader_t *);
2132   cookie = va_arg(ap, char *);
2133   va_end(ap);
2134
2135   dvprintf("faimtest: sending file %s(%ld).\n", fh->name, fh->size);
2136
2137   if(!(buf = malloc(2048)))
2138      return -1;
2139
2140   if( (path = (char *)calloc(1, strlen(listingpath) +strlen(fh->name)+2)) == NULL) {
2141     dperror("calloc");
2142     dprintf("faimtest: error in calloc of path\n");
2143     return 0; /* XXX: no idea what winaim expects here =) */
2144   }
2145   
2146   snprintf(path, strlen(listingpath)+strlen(fh->name)+2, "%s/%s", listingpath, fh->name);
2147
2148
2149   if( (file = fopen(path, "r")) == NULL) {
2150     dvprintf("faimtest: getfile_send fopen failed for %s. damn.\n", path);
2151     return 0;
2152   }
2153
2154   /* 
2155    * This is a mess. Remember that faimtest is demonstration code
2156    * only and for the sake of the gods, don't use this code in any
2157    * of your clients. --mid
2158    */
2159   for(pos = 0; pos < fh->size; pos++) {
2160     bufpos = pos % bufsize;
2161
2162     if(bufpos == 0 && pos > 0) { /* filled our buffer. spit it across the wire */
2163       if ( (i = send(oftconn->fd, buf, bufsize, 0)) != bufsize ) {
2164         dperror("faim: getfile_send: write1");
2165         dprintf("faim: getfile_send: whoopsy, didn't write it all...\n");
2166         free(buf);   
2167         return -1;
2168       }
2169     }
2170     if( (buf[bufpos] = fgetc(file)) == EOF) {
2171       if(pos != fh->size) {
2172         dvprintf("faim: getfile_send: hrm... apparent early EOF at pos 0x%x of 0x%lx\n", pos, fh->size);
2173         free(buf);   
2174         return -1;
2175       }
2176     }      
2177     dvprintf("%c(0x%02x) ", buf[pos], buf[pos]);
2178   }
2179
2180   if( (i = send(oftconn->fd, buf, bufpos+1, 0)) != (bufpos+1)) {
2181     dperror("faim: getfile_send: write2");
2182     dprintf("faim: getfile_send cleanup: whoopsy, didn't write it all...\n");
2183     free(buf);   
2184     return -1;
2185   }
2186
2187   free(buf);
2188   free(fh);  
2189   return 1;
2190 }
2191
2192 int faimtest_getfile_complete(struct aim_session_t *sess, struct command_rx_struct *command, ...) 
2193 {
2194   va_list ap;
2195   struct aim_conn_t *conn;
2196   struct aim_fileheader_t *fh;
2197
2198   va_start(ap, command);
2199   conn = va_arg(ap, struct aim_conn_t *);
2200   fh = va_arg(ap, struct aim_fileheader_t *);
2201   va_end(ap);
2202
2203   dvprintf("faimtest: completed file transfer for %s.\n", fh->name);
2204
2205   aim_conn_close(conn);
2206   aim_conn_kill(sess, &conn);
2207   return 1;
2208 }
2209
2210 int faimtest_getfile_disconnect(struct aim_session_t *sess, struct command_rx_struct *command, ...)
2211 {
2212   va_list ap;
2213   struct aim_conn_t *conn;
2214   char *sn;
2215
2216   va_start(ap, command);
2217   conn = va_arg(ap, struct aim_conn_t *);
2218   sn = va_arg(ap, char *);
2219   va_end(ap);
2220
2221   aim_conn_kill(sess, &conn);
2222
2223   dvprintf("faimtest: getfile: disconnected from %s\n", sn);
2224   return 1;
2225 }
2226 int faimtest_getfile_initiate(struct aim_session_t *sess, struct command_rx_struct *command, ...)
2227 {
2228   va_list ap;
2229   struct aim_conn_t *conn, *listenerconn;
2230   struct aim_filetransfer_priv *priv;
2231
2232   va_start(ap, command);
2233   conn = va_arg(ap, struct aim_conn_t *);
2234   listenerconn = va_arg(ap, struct aim_conn_t *);
2235   va_end(ap);
2236
2237   aim_conn_close(listenerconn);
2238   aim_conn_kill(sess, &listenerconn);
2239
2240   aim_conn_addhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILEREQ,  faimtest_getfile_filereq, 0);
2241   aim_conn_addhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILESEND, faimtest_getfile_filesend, 0);
2242   aim_conn_addhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILECOMPLETE, faimtest_getfile_complete, 0);      
2243   aim_conn_addhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEDISCONNECT, faimtest_getfile_disconnect, 0);      
2244   aim_conn_addhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTING, faimtest_getfile_listing, 0);
2245   aim_conn_addhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTINGREQ, faimtest_getfile_listingreq, 0);
2246   aim_conn_addhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILERECEIVE, faimtest_getfile_receive, 0);
2247   aim_conn_addhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILESTATE4, faimtest_getfile_state4, 0);
2248   
2249   priv = (struct aim_filetransfer_priv *)conn->priv;
2250
2251   dvprintf("faimtest: getfile: %s (%s) connected to us on %d\n", priv->sn, priv->ip, conn->fd);
2252   return 1;
2253 }
2254
2255 int faimtest_getfile_listing(struct aim_session_t *sess, struct command_rx_struct *command, ...)
2256 {
2257   va_list ap;
2258   struct aim_conn_t *conn;
2259   char *listing;
2260   struct aim_filetransfer_priv *ft;
2261   char *filename, *nameend, *sizec;
2262   int filesize, namelen;
2263
2264   va_start(ap, command);
2265   conn = va_arg(ap, struct aim_conn_t *);
2266   ft = va_arg(ap, struct aim_filetransfer_priv *);
2267   listing = va_arg(ap, char *);
2268   va_end(ap);
2269
2270   dvprintf("listing on %d==================\n%s\n===========\n", conn->fd, listing);
2271
2272   nameend = strstr(listing+0x1a, "\r");
2273
2274   namelen = nameend - (listing + 0x1a);
2275
2276   filename = malloc(namelen + 1);
2277   strncpy(filename, listing+0x1a, namelen);
2278   filename[namelen] = 0x00;
2279
2280   sizec = malloc(8+1);
2281   memcpy(sizec, listing + 0x11, 8);
2282   sizec[8] = 0x00;
2283
2284   filesize =  strtol(sizec, (char **)NULL, 10);
2285
2286   dvprintf("faimtest: requesting %d %s(%d long)\n", namelen, filename, filesize);
2287
2288   aim_oft_getfile_request(sess, conn, filename, filesize);
2289
2290   free(filename);
2291   free(sizec);
2292
2293   return 0;
2294 }
2295
2296 int faimtest_getfile_listingreq(struct aim_session_t *sess, struct command_rx_struct *command, ...)
2297 {
2298   va_list ap;
2299   struct aim_conn_t *oftconn;
2300   struct aim_fileheader_t *fh;
2301   int pos, bufpos = 0, bufsize = 2048, i;
2302   char *buf;
2303
2304   va_start(ap, command);
2305   oftconn = va_arg(ap, struct aim_conn_t *);
2306   fh = va_arg(ap, struct aim_fileheader_t *);
2307   va_end(ap);
2308
2309   dvprintf("faimtest: sending listing of size %ld\n", fh->size);
2310
2311   if(!(buf = malloc(2048)))
2312     return -1;
2313
2314   for(pos = 0; pos < fh->size; pos++) {
2315     bufpos = pos % bufsize;
2316
2317     if(bufpos == 0 && pos > 0) { /* filled our buffer. spit it across the wire */
2318       if ( (i = send(oftconn->fd, buf, bufsize, 0)) != bufsize ) {
2319         dperror("faim: getfile_send: write1");
2320         dprintf("faim: getfile_send: whoopsy, didn't write it all...\n");
2321         free(buf);   
2322         return -1;
2323       }
2324     }
2325     if( (buf[bufpos] = fgetc(listingfile)) == EOF) {
2326       if(pos != fh->size) {
2327         dvprintf("faim: getfile_send: hrm... apparent early EOF at pos 0x%x of 0x%lx\n", pos, fh->size);
2328         free(buf);   
2329         return -1;
2330       }
2331     }      
2332   }
2333
2334   if( (i = send(oftconn->fd, buf, bufpos+1, 0)) != (bufpos+1)) {
2335     dperror("faim: getfile_send: write2");
2336     dprintf("faim: getfile_send cleanup: whoopsy, didn't write it all...\n");
2337     free(buf);   
2338     return -1;
2339   }
2340
2341   dprintf("faimtest: sent listing\n");
2342   free(buf);
2343   return 0;
2344 }
2345
2346 int faimtest_getfile_receive(struct aim_session_t *sess, struct command_rx_struct *command, ...)
2347 {
2348   va_list ap;
2349   struct aim_conn_t *conn;
2350   struct aim_filetransfer_priv *ft;
2351   unsigned char data;
2352   int pos;
2353
2354   va_start(ap, command);
2355   conn = va_arg(ap, struct aim_conn_t *);
2356   ft = va_arg(ap, struct aim_filetransfer_priv *);
2357   va_end(ap);
2358
2359   dvprintf("faimtest: receiving %ld bytes of file data for %s:\n\t", ft->fh.size, ft->fh.name);
2360
2361   for(pos = 0; pos < ft->fh.size; pos++) {
2362     read(conn->fd, &data, 1);
2363     printf("%c(%02x) ", data, data);
2364   }
2365    
2366   printf("\n");
2367
2368   aim_oft_getfile_end(sess, conn);
2369
2370   return 0;
2371 }
2372
2373 int faimtest_getfile_state4(struct aim_session_t *sess, struct command_rx_struct *command, ...)
2374 {
2375   va_list ap;
2376   struct aim_conn_t *conn;
2377
2378   va_start(ap, command);
2379   conn = va_arg(ap, struct aim_conn_t *);
2380   va_end(ap);
2381
2382   aim_conn_close(conn);
2383   aim_conn_kill(sess, &conn);
2384   return 0;
2385 }
2386
2387
2388 int faimtest_parse_ratechange(struct aim_session_t *sess, struct command_rx_struct *command, ...)
2389 {
2390   static char *codes[5] = {"invalid",
2391                            "change",
2392                            "warning",
2393                            "limit",
2394                            "limit cleared"};
2395   va_list ap;
2396   int code;
2397   unsigned long rateclass, windowsize, clear, alert, limit, disconnect;
2398   unsigned long currentavg, maxavg;
2399
2400   va_start(ap, command); 
2401
2402   /* See code explanations below */
2403   code = va_arg(ap, int);
2404
2405   /*
2406    * See comments above aim_parse_ratechange_middle() in aim_rxhandlers.c.
2407    */
2408   rateclass = va_arg(ap, unsigned long);
2409
2410   /*
2411    * Not sure what this is exactly.  I think its the temporal 
2412    * relation factor (ie, how to make the rest of the numbers
2413    * make sense in the real world). 
2414    */
2415   windowsize = va_arg(ap, unsigned long);
2416
2417   /* Explained below */
2418   clear = va_arg(ap, unsigned long);
2419   alert = va_arg(ap, unsigned long);
2420   limit = va_arg(ap, unsigned long);
2421   disconnect = va_arg(ap, unsigned long);
2422   currentavg = va_arg(ap, unsigned long);
2423   maxavg = va_arg(ap, unsigned long);
2424
2425   va_end(ap);
2426
2427
2428   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",
2429          (code < 5)?codes[code]:"invalid",
2430          rateclass,
2431          currentavg, maxavg,
2432          alert, clear,
2433          limit, disconnect,
2434          windowsize);
2435
2436   if (code == AIM_RATE_CODE_CHANGE) {
2437     /*
2438      * Not real sure when these get sent.
2439      */
2440     if (currentavg >= clear)
2441       aim_conn_setlatency(command->conn, 0);
2442
2443   } else if (code == AIM_RATE_CODE_WARNING) {
2444     /*
2445      * We start getting WARNINGs the first time we go below the 'alert'
2446      * limit (currentavg < alert) and they stop when either we pause
2447      * long enough for currentavg to go above 'clear', or until we
2448      * flood it bad enough to go below 'limit' (and start getting
2449      * LIMITs instead) or even further and go below 'disconnect' and 
2450      * get disconnected completely (and won't be able to login right
2451      * away either).
2452      */
2453     aim_conn_setlatency(command->conn, windowsize/4); /* XXX this is bogus! */ 
2454
2455   } else if (code == AIM_RATE_CODE_LIMIT) {
2456     /*
2457      * When we hit LIMIT, messages will start getting dropped.
2458      */
2459     aim_conn_setlatency(command->conn, windowsize/2); /* XXX this is bogus! */ 
2460
2461   } else if (code == AIM_RATE_CODE_CLEARLIMIT) {
2462     /*
2463      * The limit is cleared when curavg goes above 'clear'.
2464      */
2465     aim_conn_setlatency(command->conn, 0); 
2466   }
2467
2468   return 1;
2469 }
2470
2471 int faimtest_parse_evilnotify(struct aim_session_t *sess, struct command_rx_struct *command, ...)
2472 {
2473   va_list ap;
2474   int newevil;
2475   struct aim_userinfo_s *userinfo;
2476
2477   va_start(ap, command);
2478   newevil = va_arg(ap, int);
2479   userinfo = va_arg(ap, struct aim_userinfo_s *);
2480   va_end(ap);
2481
2482   /*
2483    * Evil Notifications that are lacking userinfo->sn are anon-warns
2484    * if they are an evil increases, but are not warnings at all if its
2485    * a decrease (its the natural backoff happening).
2486    *
2487    * newevil is passed as an int representing the new evil value times
2488    * ten.
2489    */
2490   dvprintf("faimtest: evil level change: new value = %2.1f%% (caused by %s)\n", ((float)newevil)/10, (userinfo && strlen(userinfo->sn))?userinfo->sn:"anonymous");
2491
2492   return 1;
2493 }
2494
2495 int faimtest_parse_searchreply(struct aim_session_t *sess, struct command_rx_struct *command, ...)
2496 {
2497   va_list ap;
2498   char *address, *SNs;
2499   int i, num;
2500   
2501   va_start(ap, command);
2502   address = va_arg(ap, char *);
2503   num = va_arg(ap, int);
2504   SNs = va_arg(ap, char *);
2505   va_end(ap);
2506
2507   dvprintf("faimtest: E-Mail Search Results for %s: ", address);
2508
2509   for(i = 0; i < num; i++)
2510     dvinlineprintf("%s, ", &SNs[i*(MAXSNLEN+1)]);
2511   dinlineprintf("\n");
2512
2513   return 1;
2514 }
2515
2516 int faimtest_parse_searcherror(struct aim_session_t *sess, struct command_rx_struct *command, ...)
2517 {
2518   va_list ap;
2519   char *address;
2520   
2521   va_start(ap, command);
2522   address = va_arg(ap, char *);
2523   va_end(ap);
2524
2525   dvprintf("faimtest: E-Mail Search Results for %s: No Results or Invalid Email\n", address);
2526
2527   return 1;
2528 }
This page took 2.49677 seconds and 5 git commands to generate.