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