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