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