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