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