# HG changeset patch # User Eric Warmenhoven # Date 1005356897 0 # Node ID 441b84ab7f4e03ffbd4660f5dcffa7116b5d8e25 # Parent 94b4271b956776f01263a00c3db94e822abfa57c [gaim-migrate @ 2716] it's not what you think committer: Tailor Script diff -r 94b4271b9567 -r 441b84ab7f4e src/protocols/oscar/.cvsignore --- a/src/protocols/oscar/.cvsignore Sat Nov 10 01:30:13 2001 +0000 +++ b/src/protocols/oscar/.cvsignore Sat Nov 10 01:48:17 2001 +0000 @@ -32,3 +32,5 @@ popups.lo ssi.lo translate.lo +icq.lo +service.lo diff -r 94b4271b9567 -r 441b84ab7f4e src/protocols/oscar/Makefile.am --- a/src/protocols/oscar/Makefile.am Sat Nov 10 01:30:13 2001 +0000 +++ b/src/protocols/oscar/Makefile.am Sat Nov 10 01:48:17 2001 +0000 @@ -21,10 +21,10 @@ chatnav.c \ conn.c \ ft.c \ + icq.c \ im.c \ info.c \ invite.c \ - login.c \ md5.c \ meta.c \ misc.c \ @@ -33,6 +33,7 @@ rxhandlers.c \ rxqueue.c \ search.c \ + service.c \ snac.c \ ssi.c \ stats.c \ @@ -58,10 +59,10 @@ chatnav.c \ conn.c \ ft.c \ + icq.c \ im.c \ info.c \ invite.c \ - login.c \ md5.c \ meta.c \ misc.c \ @@ -70,6 +71,7 @@ rxhandlers.c \ rxqueue.c \ search.c \ + service.c \ snac.c \ ssi.c \ stats.c \ diff -r 94b4271b9567 -r 441b84ab7f4e src/protocols/oscar/admin.c --- a/src/protocols/oscar/admin.c Sat Nov 10 01:30:13 2001 +0000 +++ b/src/protocols/oscar/admin.c Sat Nov 10 01:48:17 2001 +0000 @@ -87,7 +87,7 @@ return 0; } -faim_export int aim_auth_changepasswd(aim_session_t *sess, aim_conn_t *conn, const char *newpw, const char *curpw) +faim_export int aim_admin_changepasswd(aim_session_t *sess, aim_conn_t *conn, const char *newpw, const char *curpw) { aim_frame_t *tx; aim_tlvlist_t *tl = NULL; @@ -121,7 +121,7 @@ * get the TRIAL flag removed from your account. * */ -faim_export int aim_auth_reqconfirm(aim_session_t *sess, aim_conn_t *conn) +faim_export int aim_admin_reqconfirm(aim_session_t *sess, aim_conn_t *conn) { return aim_genericreq_n(sess, conn, 0x0007, 0x0006); } @@ -132,7 +132,7 @@ * The only known valid tag is 0x0011 (email address). * */ -faim_export int aim_auth_getinfo(aim_session_t *sess, aim_conn_t *conn, fu16_t info) +faim_export int aim_admin_getinfo(aim_session_t *sess, aim_conn_t *conn, fu16_t info) { aim_frame_t *tx; aim_snacid_t snacid; @@ -151,7 +151,7 @@ return 0; } -faim_export int aim_auth_setemail(aim_session_t *sess, aim_conn_t *conn, const char *newemail) +faim_export int aim_admin_setemail(aim_session_t *sess, aim_conn_t *conn, const char *newemail) { aim_frame_t *tx; aim_snacid_t snacid; @@ -172,3 +172,4 @@ return 0; } + diff -r 94b4271b9567 -r 441b84ab7f4e src/protocols/oscar/aim.h --- a/src/protocols/oscar/aim.h Sat Nov 10 01:30:13 2001 +0000 +++ b/src/protocols/oscar/aim.h Sat Nov 10 01:48:17 2001 +0000 @@ -505,10 +505,6 @@ faim_export int aim_request_login(aim_session_t *sess, aim_conn_t *conn, const char *sn); faim_export int aim_send_login(aim_session_t *, aim_conn_t *, const char *, const char *, struct client_info_s *, const char *key); faim_export int aim_encode_password_md5(const char *password, const char *key, unsigned char *digest); -faim_export int aim_sendauthresp(aim_session_t *sess, aim_conn_t *conn, const char *sn, int errorcode, const char *errorurl, const char *bosip, const char *cookie, const char *email, int regstatus); -faim_export int aim_gencookie(unsigned char *buf); -faim_export int aim_sendserverready(aim_session_t *sess, aim_conn_t *conn); -faim_export int aim_sendredirect(aim_session_t *sess, aim_conn_t *conn, fu16_t servid, const char *ip, const char *cookie); faim_export void aim_purge_rxqueue(aim_session_t *); #define AIM_TX_QUEUED 0 /* default */ @@ -558,7 +554,7 @@ faim_export int aim_sendpauseack(aim_session_t *sess, aim_conn_t *conn); faim_export int aim_send_warning(aim_session_t *sess, aim_conn_t *conn, const char *destsn, fu32_t flags); -faim_export int aim_bos_nop(aim_session_t *, aim_conn_t *); +faim_export int aim_nop(aim_session_t *, aim_conn_t *); faim_export int aim_flap_nop(aim_session_t *sess, aim_conn_t *conn); faim_export int aim_bos_setidle(aim_session_t *, aim_conn_t *, fu32_t); faim_export int aim_bos_changevisibility(aim_session_t *, aim_conn_t *, int, const char *); @@ -566,14 +562,14 @@ faim_export int aim_bos_setprofile(aim_session_t *sess, aim_conn_t *conn, const char *profile, const char *awaymsg, fu16_t caps); faim_export int aim_bos_setgroupperm(aim_session_t *, aim_conn_t *, fu32_t mask); faim_export int aim_bos_setprivacyflags(aim_session_t *, aim_conn_t *, fu32_t); -faim_export int aim_bos_reqpersonalinfo(aim_session_t *, aim_conn_t *); -faim_export int aim_bos_reqservice(aim_session_t *, aim_conn_t *, fu16_t); +faim_export int aim_reqpersonalinfo(aim_session_t *, aim_conn_t *); +faim_export int aim_reqservice(aim_session_t *, aim_conn_t *, fu16_t); faim_export int aim_bos_reqrights(aim_session_t *, aim_conn_t *); faim_export int aim_bos_reqbuddyrights(aim_session_t *, aim_conn_t *); faim_export int aim_bos_reqlocaterights(aim_session_t *, aim_conn_t *); faim_export int aim_setdirectoryinfo(aim_session_t *sess, aim_conn_t *conn, const char *first, const char *middle, const char *last, const char *maiden, const char *nickname, const char *street, const char *city, const char *state, const char *zip, int country, fu16_t privacy); faim_export int aim_setuserinterests(aim_session_t *sess, aim_conn_t *conn, const char *interest1, const char *interest2, const char *interest3, const char *interest4, const char *interest5, fu16_t privacy); -faim_export int aim_icq_setstatus(aim_session_t *sess, aim_conn_t *conn, fu16_t status); +faim_export int aim_setextstatus(aim_session_t *sess, aim_conn_t *conn, fu16_t status); faim_export struct aim_fileheader_t *aim_getlisting(aim_session_t *sess, FILE *); @@ -652,6 +648,7 @@ #define AIM_IMFLAGS_EXTDATA 0x0100 #define AIM_IMFLAGS_CUSTOMCHARSET 0x0200 /* charset fields set */ #define AIM_IMFLAGS_MULTIPART 0x0400 /* ->mpmsg section valid */ +#define AIM_IMFLAGS_OFFLINE 0x0800 /* send to offline user */ /* * Multipart message structures. @@ -890,12 +887,12 @@ /* auth.c */ -faim_export int aim_auth_sendcookie(aim_session_t *, aim_conn_t *, const fu8_t *); +faim_export int aim_sendcookie(aim_session_t *, aim_conn_t *, const fu8_t *); -faim_export int aim_auth_changepasswd(aim_session_t *, aim_conn_t *, const char *newpw, const char *curpw); -faim_export int aim_auth_reqconfirm(aim_session_t *sess, aim_conn_t *conn); -faim_export int aim_auth_getinfo(aim_session_t *sess, aim_conn_t *conn, fu16_t info); -faim_export int aim_auth_setemail(aim_session_t *sess, aim_conn_t *conn, const char *newemail); +faim_export int aim_admin_changepasswd(aim_session_t *, aim_conn_t *, const char *newpw, const char *curpw); +faim_export int aim_admin_reqconfirm(aim_session_t *sess, aim_conn_t *conn); +faim_export int aim_admin_getinfo(aim_session_t *sess, aim_conn_t *conn, fu16_t info); +faim_export int aim_admin_setemail(aim_session_t *sess, aim_conn_t *conn, const char *newemail); /* aim_buddylist.c */ faim_export int aim_add_buddy(aim_session_t *, aim_conn_t *, const char *); diff -r 94b4271b9567 -r 441b84ab7f4e src/protocols/oscar/aim_internal.h --- a/src/protocols/oscar/aim_internal.h Sat Nov 10 01:30:13 2001 +0000 +++ b/src/protocols/oscar/aim_internal.h Sat Nov 10 01:48:17 2001 +0000 @@ -51,6 +51,7 @@ faim_internal int translate_modfirst(aim_session_t *sess, aim_module_t *mod); faim_internal int popups_modfirst(aim_session_t *sess, aim_module_t *mod); faim_internal int adverts_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int icq_modfirst(aim_session_t *sess, aim_module_t *mod); faim_internal int aim_genericreq_n(aim_session_t *, aim_conn_t *conn, fu16_t family, fu16_t subtype); faim_internal int aim_genericreq_n_snacid(aim_session_t *, aim_conn_t *conn, fu16_t family, fu16_t subtype); @@ -202,7 +203,8 @@ /* These are all handled internally now. */ faim_internal int aim_setversions(aim_session_t *sess, aim_conn_t *conn); faim_internal int aim_reqrates(aim_session_t *, aim_conn_t *); -faim_internal int aim_ratesack(aim_session_t *, aim_conn_t *); +faim_internal int aim_rates_addparam(aim_session_t *, aim_conn_t *); +faim_internal int aim_rates_delparam(aim_session_t *, aim_conn_t *); #ifndef FAIM_INTERNAL_INSANE #define printf() printf called inside libfaim diff -r 94b4271b9567 -r 441b84ab7f4e src/protocols/oscar/auth.c --- a/src/protocols/oscar/auth.c Sat Nov 10 01:30:13 2001 +0000 +++ b/src/protocols/oscar/auth.c Sat Nov 10 01:48:17 2001 +0000 @@ -1,15 +1,24 @@ /* - * aim_auth.c - * - * Deals with the authorizer. + * Deals with the authorizer (group 0x0017=23, and old-style non-SNAC login). * */ #define FAIM_INTERNAL #include -/* this just pushes the passed cookie onto the passed connection -- NO SNAC! */ -faim_export int aim_auth_sendcookie(aim_session_t *sess, aim_conn_t *conn, const fu8_t *chipsahoy) +#include "md5.h" + +static int aim_encode_password(const char *password, unsigned char *encoded); + +/* + * This just pushes the passed cookie onto the passed connection, without + * the SNAC header or any of that. + * + * Very commonly used, as every connection except auth will require this to + * be the first thing you send. + * + */ +faim_export int aim_sendcookie(aim_session_t *sess, aim_conn_t *conn, const fu8_t *chipsahoy) { aim_frame_t *fr; aim_tlvlist_t *tl = NULL; @@ -28,6 +37,328 @@ } /* + * Normally the FLAP version is sent as the first few bytes of the cookie, + * meaning you generally never call this. + * + * But there are times when something might want it seperate. Specifically, + * libfaim sends this internally when doing SNAC login. + * + */ +faim_export int aim_sendflapver(aim_session_t *sess, aim_conn_t *conn) +{ + aim_frame_t *fr; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x01, 4))) + return -ENOMEM; + + aimbs_put32(&fr->data, 0x00000001); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * This is a bit confusing. + * + * Normal SNAC login goes like this: + * - connect + * - server sends flap version + * - client sends flap version + * - client sends screen name (17/6) + * - server sends hash key (17/7) + * - client sends auth request (17/2 -- aim_send_login) + * - server yells + * + * XOR login (for ICQ) goes like this: + * - connect + * - server sends flap version + * - client sends auth request which contains flap version (aim_send_login) + * - server yells + * + * For the client API, we make them implement the most complicated version, + * and for the simpler version, we fake it and make it look like the more + * complicated process. + * + * This is done by giving the client a faked key, just so we can convince + * them to call aim_send_login right away, which will detect the session + * flag that says this is XOR login and ignore the key, sending an ICQ + * login request instead of the normal SNAC one. + * + * As soon as AOL makes ICQ log in the same way as AIM, this is /gone/. + * + * XXX This may cause problems if the client relies on callbacks only + * being called from the context of aim_rxdispatch()... + * + */ +static int goddamnicq(aim_session_t *sess, aim_conn_t *conn, const char *sn) +{ + aim_frame_t fr; + aim_rxcallback_t userfunc; + + sess->flags &= ~AIM_SESS_FLAGS_SNACLOGIN; + sess->flags |= AIM_SESS_FLAGS_XORLOGIN; + + fr.conn = conn; + + if ((userfunc = aim_callhandler(sess, conn, 0x0017, 0x0007))) + userfunc(sess, &fr, ""); + + return 0; +} + +/* + * In AIM 3.5 protocol, the first stage of login is to request login from the + * Authorizer, passing it the screen name for verification. If the name is + * invalid, a 0017/0003 is spit back, with the standard error contents. If + * valid, a 0017/0007 comes back, which is the signal to send it the main + * login command (0017/0002). + * + */ +faim_export int aim_request_login(aim_session_t *sess, aim_conn_t *conn, const char *sn) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + + if (!sess || !conn || !sn) + return -EINVAL; + + if ((sn[0] >= '0') && (sn[0] <= '9')) + return goddamnicq(sess, conn, sn); + + sess->flags |= AIM_SESS_FLAGS_SNACLOGIN; + + aim_sendflapver(sess, conn); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+2+strlen(sn)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0017, 0x0006, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0017, 0x0006, 0x0000, snacid); + + aim_addtlvtochain_raw(&tl, 0x0001, strlen(sn), sn); + aim_writetlvchain(&fr->data, &tl); + aim_freetlvchain(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Part two of the ICQ hack. Note the ignoring of the key and clientinfo. + */ +static int goddamnicq2(aim_session_t *sess, aim_conn_t *conn, const char *sn, const char *password) +{ + static const char clientstr[] = {"ICQ Inc. - Product of ICQ (TM) 2000b.4.65.1.3281.85"}; + static const char lang[] = {"en"}; + static const char country[] = {"us"}; + aim_frame_t *fr; + aim_tlvlist_t *tl = NULL; + char *password_encoded; + + if (!(password_encoded = (char *) malloc(strlen(password)))) + return -ENOMEM; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x01, 1152))) { + free(password_encoded); + return -ENOMEM; + } + + aim_encode_password(password, password_encoded); + + aimbs_put32(&fr->data, 0x00000001); + aim_addtlvtochain_raw(&tl, 0x0001, strlen(sn), sn); + aim_addtlvtochain_raw(&tl, 0x0002, strlen(password), password_encoded); + aim_addtlvtochain_raw(&tl, 0x0003, strlen(clientstr), clientstr); + aim_addtlvtochain16(&tl, 0x0016, 0x010a); + aim_addtlvtochain16(&tl, 0x0017, 0x0004); + aim_addtlvtochain16(&tl, 0x0018, 0x0041); + aim_addtlvtochain16(&tl, 0x0019, 0x0001); + aim_addtlvtochain16(&tl, 0x001a, 0x0cd1); + aim_addtlvtochain32(&tl, 0x0014, 0x00000055); + aim_addtlvtochain_raw(&tl, 0x000f, strlen(lang), lang); + aim_addtlvtochain_raw(&tl, 0x000e, strlen(country), country); + + aim_writetlvchain(&fr->data, &tl); + + free(password_encoded); + aim_freetlvchain(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * send_login(int socket, char *sn, char *password) + * + * This is the initial login request packet. + * + * NOTE!! If you want/need to make use of the aim_sendmemblock() function, + * then the client information you send here must exactly match the + * executable that you're pulling the data from. + * + * Latest WinAIM: + * clientstring = "AOL Instant Messenger (SM), version 4.3.2188/WIN32" + * major2 = 0x0109 + * major = 0x0400 + * minor = 0x0003 + * minor2 = 0x0000 + * build = 0x088c + * unknown = 0x00000086 + * lang = "en" + * country = "us" + * unknown4a = 0x01 + * + * Latest WinAIM that libfaim can emulate without server-side buddylists: + * clientstring = "AOL Instant Messenger (SM), version 4.1.2010/WIN32" + * major2 = 0x0004 + * major = 0x0004 + * minor = 0x0001 + * minor2 = 0x0000 + * build = 0x07da + * unknown= 0x0000004b + * + * WinAIM 3.5.1670: + * clientstring = "AOL Instant Messenger (SM), version 3.5.1670/WIN32" + * major2 = 0x0004 + * major = 0x0003 + * minor = 0x0005 + * minor2 = 0x0000 + * build = 0x0686 + * unknown =0x0000002a + * + * Java AIM 1.1.19: + * clientstring = "AOL Instant Messenger (TM) version 1.1.19 for Java built 03/24/98, freeMem 215871 totalMem 1048567, i686, Linus, #2 SMP Sun Feb 11 03:41:17 UTC 2001 2.4.1-ac9, IBM Corporation, 1.1.8, 45.3, Tue Mar 27 12:09:17 PST 2001" + * major2 = 0x0001 + * major = 0x0001 + * minor = 0x0001 + * minor2 = (not sent) + * build = 0x0013 + * unknown= (not sent) + * + * AIM for Linux 1.1.112: + * clientstring = "AOL Instant Messenger (SM)" + * major2 = 0x1d09 + * major = 0x0001 + * minor = 0x0001 + * minor2 = 0x0001 + * build = 0x0070 + * unknown= 0x0000008b + * serverstore = 0x01 + * + */ +faim_export int aim_send_login(aim_session_t *sess, aim_conn_t *conn, const char *sn, const char *password, struct client_info_s *clientinfo, const char *key) +{ + aim_frame_t *fr; + aim_tlvlist_t *tl = NULL; + fu8_t digest[16]; + aim_snacid_t snacid; + + if (!clientinfo || !sn || !password) + return -EINVAL; + + if (sess->flags & AIM_SESS_FLAGS_XORLOGIN) + return goddamnicq2(sess, conn, sn, password); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152))) + return -ENOMEM; + + if (sess->flags & AIM_SESS_FLAGS_XORLOGIN) { + fr->hdr.flap.type = 0x01; + + /* Use very specific version numbers to further indicate hack */ + clientinfo->major2 = 0x010a; + clientinfo->major = 0x0004; + clientinfo->minor = 0x003c; + clientinfo->minor2 = 0x0001; + clientinfo->build = 0x0cce; + clientinfo->unknown = 0x00000055; + } + + snacid = aim_cachesnac(sess, 0x0017, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0017, 0x0002, 0x0000, snacid); + + aim_addtlvtochain_raw(&tl, 0x0001, strlen(sn), sn); + + aim_encode_password_md5(password, key, digest); + aim_addtlvtochain_raw(&tl, 0x0025, 16, digest); + + aim_addtlvtochain_raw(&tl, 0x0003, strlen(clientinfo->clientstring), clientinfo->clientstring); + aim_addtlvtochain16(&tl, 0x0016, (fu16_t)clientinfo->major2); + aim_addtlvtochain16(&tl, 0x0017, (fu16_t)clientinfo->major); + aim_addtlvtochain16(&tl, 0x0018, (fu16_t)clientinfo->minor); + aim_addtlvtochain16(&tl, 0x0019, (fu16_t)clientinfo->minor2); + aim_addtlvtochain16(&tl, 0x001a, (fu16_t)clientinfo->build); + aim_addtlvtochain_raw(&tl, 0x000e, strlen(clientinfo->country), clientinfo->country); + aim_addtlvtochain_raw(&tl, 0x000f, strlen(clientinfo->lang), clientinfo->lang); + aim_addtlvtochain16(&tl, 0x0009, 0x0015); + + aim_writetlvchain(&fr->data, &tl); + + aim_freetlvchain(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +faim_export int aim_encode_password_md5(const char *password, const char *key, fu8_t *digest) +{ + md5_state_t state; + + md5_init(&state); + md5_append(&state, (const md5_byte_t *)key, strlen(key)); + md5_append(&state, (const md5_byte_t *)password, strlen(password)); + md5_append(&state, (const md5_byte_t *)AIM_MD5_STRING, strlen(AIM_MD5_STRING)); + md5_finish(&state, (md5_byte_t *)digest); + + return 0; +} + +/** + * aim_encode_password - Encode a password using old XOR method + * @password: incoming password + * @encoded: buffer to put encoded password + * + * This takes a const pointer to a (null terminated) string + * containing the unencoded password. It also gets passed + * an already allocated buffer to store the encoded password. + * This buffer should be the exact length of the password without + * the null. The encoded password buffer /is not %NULL terminated/. + * + * The encoding_table seems to be a fixed set of values. We'll + * hope it doesn't change over time! + * + * This is only used for the XOR method, not the better MD5 method. + * + */ +static int aim_encode_password(const char *password, fu8_t *encoded) +{ + fu8_t encoding_table[] = { +#if 0 /* old v1 table */ + 0xf3, 0xb3, 0x6c, 0x99, + 0x95, 0x3f, 0xac, 0xb6, + 0xc5, 0xfa, 0x6b, 0x63, + 0x69, 0x6c, 0xc3, 0x9f +#else /* v2.1 table, also works for ICQ */ + 0xf3, 0x26, 0x81, 0xc4, + 0x39, 0x86, 0xdb, 0x92, + 0x71, 0xa3, 0xb9, 0xe6, + 0x53, 0x7a, 0x95, 0x7c +#endif + }; + int i; + + for (i = 0; i < strlen(password); i++) + encoded[i] = (password[i] ^ encoding_table[i]); + + return 0; +} + +/* * This is sent back as a general response to the login command. * It can be either an error or a success, depending on the * precense of certain TLVs. diff -r 94b4271b9567 -r 441b84ab7f4e src/protocols/oscar/bos.c --- a/src/protocols/oscar/bos.c Sat Nov 10 01:30:13 2001 +0000 +++ b/src/protocols/oscar/bos.c Sat Nov 10 01:48:17 2001 +0000 @@ -2,21 +2,13 @@ #define FAIM_INTERNAL #include -/* - * aim_bos_setgroupperm(mask) - * - * Set group permisson mask. Normally 0x1f (all classes). - * - * The group permission mask allows you to keep users of a certain - * class or classes from talking to you. The mask should be - * a bitwise OR of all the user classes you want to see you. - * - */ -faim_export int aim_bos_setgroupperm(aim_session_t *sess, aim_conn_t *conn, fu32_t mask) +/* Request BOS rights (group 9, type 2) */ +faim_export int aim_bos_reqrights(aim_session_t *sess, aim_conn_t *conn) { - return aim_genericreq_l(sess, conn, 0x0009, 0x0004, &mask); + return aim_genericreq_n(sess, conn, 0x0009, 0x0002); } +/* BOS Rights (group 9, type 3) */ static int rights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) { aim_rxcallback_t userfunc; @@ -49,6 +41,101 @@ return ret; } +/* + * Set group permisson mask (group 9, type 4) + * + * Normally 0x1f (all classes). + * + * The group permission mask allows you to keep users of a certain + * class or classes from talking to you. The mask should be + * a bitwise OR of all the user classes you want to see you. + * + */ +faim_export int aim_bos_setgroupperm(aim_session_t *sess, aim_conn_t *conn, fu32_t mask) +{ + return aim_genericreq_l(sess, conn, 0x0009, 0x0004, &mask); +} + +/* + * Modify permit/deny lists (group 9, types 5, 6, 7, and 8) + * + * Changes your visibility depending on changetype: + * + * AIM_VISIBILITYCHANGE_PERMITADD: Lets provided list of names see you + * AIM_VISIBILITYCHANGE_PERMIDREMOVE: Removes listed names from permit list + * AIM_VISIBILITYCHANGE_DENYADD: Hides you from provided list of names + * AIM_VISIBILITYCHANGE_DENYREMOVE: Lets list see you again + * + * list should be a list of + * screen names in the form "Screen Name One&ScreenNameTwo&" etc. + * + * Equivelents to options in WinAIM: + * - Allow all users to contact me: Send an AIM_VISIBILITYCHANGE_DENYADD + * with only your name on it. + * - Allow only users on my Buddy List: Send an + * AIM_VISIBILITYCHANGE_PERMITADD with the list the same as your + * buddy list + * - Allow only the uesrs below: Send an AIM_VISIBILITYCHANGE_PERMITADD + * with everyone listed that you want to see you. + * - Block all users: Send an AIM_VISIBILITYCHANGE_PERMITADD with only + * yourself in the list + * - Block the users below: Send an AIM_VISIBILITYCHANGE_DENYADD with + * the list of users to be blocked + * + * XXX ye gods. + */ +faim_export int aim_bos_changevisibility(aim_session_t *sess, aim_conn_t *conn, int changetype, const char *denylist) +{ + aim_frame_t *fr; + int packlen = 0; + fu16_t subtype; + char *localcpy = NULL, *tmpptr = NULL; + int i; + int listcount; + aim_snacid_t snacid; + + if (!denylist) + return -EINVAL; + + if (changetype == AIM_VISIBILITYCHANGE_PERMITADD) + subtype = 0x05; + else if (changetype == AIM_VISIBILITYCHANGE_PERMITREMOVE) + subtype = 0x06; + else if (changetype == AIM_VISIBILITYCHANGE_DENYADD) + subtype = 0x07; + else if (changetype == AIM_VISIBILITYCHANGE_DENYREMOVE) + subtype = 0x08; + else + return -EINVAL; + + localcpy = strdup(denylist); + + listcount = aimutil_itemcnt(localcpy, '&'); + packlen = aimutil_tokslen(localcpy, 99, '&') + listcount + 9; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, packlen))) { + free(localcpy); + return -ENOMEM; + } + + snacid = aim_cachesnac(sess, 0x0009, subtype, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0009, subtype, 0x00, snacid); + + for (i = 0; (i < (listcount - 1)) && (i < 99); i++) { + tmpptr = aimutil_itemidx(localcpy, i, '&'); + + aimbs_put8(&fr->data, strlen(tmpptr)); + aimbs_putraw(&fr->data, tmpptr, strlen(tmpptr)); + + free(tmpptr); + } + free(localcpy); + + aim_tx_enqueue(sess, fr); + + return 0; +} + static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) { diff -r 94b4271b9567 -r 441b84ab7f4e src/protocols/oscar/conn.c --- a/src/protocols/oscar/conn.c Sat Nov 10 01:30:13 2001 +0000 +++ b/src/protocols/oscar/conn.c Sat Nov 10 01:48:17 2001 +0000 @@ -899,7 +899,9 @@ aim__registermodule(sess, chat_modfirst); /* missing 0x0f - 0x12 */ aim__registermodule(sess, ssi_modfirst); - /* missing 0x14 - 0x16 */ + /* missing 0x14 */ + aim__registermodule(sess, icq_modfirst); + /* missing 0x16 */ aim__registermodule(sess, auth_modfirst); return; @@ -1043,3 +1045,22 @@ } +/* + * aim_flap_nop() + * + * No-op. WinAIM 4.x sends these _every minute_ to keep + * the connection alive. + */ +faim_export int aim_flap_nop(aim_session_t *sess, aim_conn_t *conn) +{ + aim_frame_t *fr; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x05, 0))) + return -ENOMEM; + + aim_tx_enqueue(sess, fr); + + return 0; +} + + diff -r 94b4271b9567 -r 441b84ab7f4e src/protocols/oscar/icq.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/oscar/icq.c Sat Nov 10 01:48:17 2001 +0000 @@ -0,0 +1,46 @@ +/* + * Encapsulated ICQ. + * + */ + +#define FAIM_INTERNAL +#include + +/* + * Response to 15/2, contains an ICQ packet. + */ +static int icqresponse(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx); + + return ret; +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if (snac->subtype == 0x0003) + return icqresponse(sess, mod, rx, snac, bs); + + return 0; +} + +faim_internal int icq_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x0015; + mod->version = 0x0001; + mod->toolid = 0x0110; + mod->toolversion = 0x047b; + mod->flags = 0; + strncpy(mod->name, "icq", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} + + diff -r 94b4271b9567 -r 441b84ab7f4e src/protocols/oscar/im.c --- a/src/protocols/oscar/im.c Sat Nov 10 01:30:13 2001 +0000 +++ b/src/protocols/oscar/im.c Sat Nov 10 01:48:17 2001 +0000 @@ -107,6 +107,8 @@ * AIM_IMFLAGS_AWAY -- Marks the message as an autoresponse * AIM_IMFLAGS_ACK -- Requests that the server send an ack * when the message is received (of type 0x0004/0x000c) + * AIM_IMFLAGS_OFFLINE--If destination is offline, store it until they are + * online (probably ICQ only). * AIM_IMFLAGS_UNICODE--Instead of ASCII7, the passed message is * made up of UNICODE duples. If you set * this, you'd better be damn sure you know @@ -305,11 +307,8 @@ aimbs_put16(&fr->data, 0x0000); } - /* - * Set the Buddy Icon Requested flag. - */ - if (args->flags & AIM_IMFLAGS_BUDDYREQ) { - aimbs_put16(&fr->data, 0x0009); + if (args->flags & AIM_IMFLAGS_OFFLINE) { + aimbs_put16(&fr->data, 0x0006); aimbs_put16(&fr->data, 0x0000); } @@ -325,6 +324,14 @@ aimbs_put32(&fr->data, args->iconstamp); } + /* + * Set the Buddy Icon Requested flag. + */ + if (args->flags & AIM_IMFLAGS_BUDDYREQ) { + aimbs_put16(&fr->data, 0x0009); + aimbs_put16(&fr->data, 0x0000); + } + aim_tx_enqueue(sess, fr); #if 1 /* XXX do this with autoconf or something... */ @@ -861,6 +868,11 @@ args.icbmflags |= AIM_IMFLAGS_AWAY; + } else if (type == 0x0006) { /* Message was received offline. */ + + /* XXX not sure if this actually gets sent. */ + args.icbmflags |= AIM_IMFLAGS_OFFLINE; + } else if (type == 0x0008) { /* I-HAVE-A-REALLY-PURTY-ICON Flag */ args.iconlen = aimbs_get32(bs); diff -r 94b4271b9567 -r 441b84ab7f4e src/protocols/oscar/login.c --- a/src/protocols/oscar/login.c Sat Nov 10 01:30:13 2001 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1223 +0,0 @@ -/* - * aim_login.c - * - * This contains all the functions needed to actually login. - * - */ - -#define FAIM_INTERNAL -#include - -#include "md5.h" - -static int aim_encode_password(const char *password, unsigned char *encoded); - -faim_export int aim_sendflapver(aim_session_t *sess, aim_conn_t *conn) -{ - aim_frame_t *fr; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x01, 4))) - return -ENOMEM; - - aimbs_put32(&fr->data, 0x00000001); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * This is a bit confusing. - * - * Normal SNAC login goes like this: - * - connect - * - server sends flap version - * - client sends flap version - * - client sends screen name (17/6) - * - server sends hash key (17/7) - * - client sends auth request (17/2 -- aim_send_login) - * - server yells - * - * XOR login (for ICQ) goes like this: - * - connect - * - server sends flap version - * - client sends auth request which contains flap version (aim_send_login) - * - server yells - * - * For the client API, we make them implement the most complicated version, - * and for the simpler version, we fake it and make it look like the more - * complicated process. - * - * This is done by giving the client a faked key, just so we can convince - * them to call aim_send_login right away, which will detect the session - * flag that says this is XOR login and ignore the key, sending an ICQ - * login request instead of the normal SNAC one. - * - * As soon as AOL makes ICQ log in the same way as AIM, this is /gone/. - * - * XXX This may cause problems if the client relies on callbacks only - * being called from the context of aim_rxdispatch()... - * - */ -static int goddamnicq(aim_session_t *sess, aim_conn_t *conn, const char *sn) -{ - aim_frame_t fr; - aim_rxcallback_t userfunc; - - sess->flags &= ~AIM_SESS_FLAGS_SNACLOGIN; - sess->flags |= AIM_SESS_FLAGS_XORLOGIN; - - fr.conn = conn; - - if ((userfunc = aim_callhandler(sess, conn, 0x0017, 0x0007))) - userfunc(sess, &fr, ""); - - return 0; -} - -/* - * In AIM 3.5 protocol, the first stage of login is to request login from the - * Authorizer, passing it the screen name for verification. If the name is - * invalid, a 0017/0003 is spit back, with the standard error contents. If - * valid, a 0017/0007 comes back, which is the signal to send it the main - * login command (0017/0002). - * - */ -faim_export int aim_request_login(aim_session_t *sess, aim_conn_t *conn, const char *sn) -{ - aim_frame_t *fr; - aim_snacid_t snacid; - aim_tlvlist_t *tl = NULL; - - if (!sess || !conn || !sn) - return -EINVAL; - - if ((sn[0] >= '0') && (sn[0] <= '9')) - return goddamnicq(sess, conn, sn); - - sess->flags |= AIM_SESS_FLAGS_SNACLOGIN; - - aim_sendflapver(sess, conn); - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+2+strlen(sn)))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0017, 0x0006, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0017, 0x0006, 0x0000, snacid); - - aim_addtlvtochain_raw(&tl, 0x0001, strlen(sn), sn); - aim_writetlvchain(&fr->data, &tl); - aim_freetlvchain(&tl); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * Part two of the ICQ hack. Note the ignoring of the key and clientinfo. - */ -static int goddamnicq2(aim_session_t *sess, aim_conn_t *conn, const char *sn, const char *password) -{ - static const char clientstr[] = {"ICQ Inc. - Product of ICQ (TM) 2000b.4.65.1.3281.85"}; - static const char lang[] = {"en"}; - static const char country[] = {"us"}; - aim_frame_t *fr; - aim_tlvlist_t *tl = NULL; - char *password_encoded; - - if (!(password_encoded = (char *) malloc(strlen(password)))) - return -ENOMEM; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x01, 1152))) { - free(password_encoded); - return -ENOMEM; - } - - aim_encode_password(password, password_encoded); - - aimbs_put32(&fr->data, 0x00000001); - aim_addtlvtochain_raw(&tl, 0x0001, strlen(sn), sn); - aim_addtlvtochain_raw(&tl, 0x0002, strlen(password), password_encoded); - aim_addtlvtochain_raw(&tl, 0x0003, strlen(clientstr), clientstr); - aim_addtlvtochain16(&tl, 0x0016, 0x010a); - aim_addtlvtochain16(&tl, 0x0017, 0x0004); - aim_addtlvtochain16(&tl, 0x0018, 0x0041); - aim_addtlvtochain16(&tl, 0x0019, 0x0001); - aim_addtlvtochain16(&tl, 0x001a, 0x0cd1); - aim_addtlvtochain32(&tl, 0x0014, 0x00000055); - aim_addtlvtochain_raw(&tl, 0x000f, strlen(lang), lang); - aim_addtlvtochain_raw(&tl, 0x000e, strlen(country), country); - - aim_writetlvchain(&fr->data, &tl); - - free(password_encoded); - aim_freetlvchain(&tl); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * send_login(int socket, char *sn, char *password) - * - * This is the initial login request packet. - * - * NOTE!! If you want/need to make use of the aim_sendmemblock() function, - * then the client information you send here must exactly match the - * executable that you're pulling the data from. - * - * Latest WinAIM: - * clientstring = "AOL Instant Messenger (SM), version 4.3.2188/WIN32" - * major2 = 0x0109 - * major = 0x0400 - * minor = 0x0003 - * minor2 = 0x0000 - * build = 0x088c - * unknown = 0x00000086 - * lang = "en" - * country = "us" - * unknown4a = 0x01 - * - * Latest WinAIM that libfaim can emulate without server-side buddylists: - * clientstring = "AOL Instant Messenger (SM), version 4.1.2010/WIN32" - * major2 = 0x0004 - * major = 0x0004 - * minor = 0x0001 - * minor2 = 0x0000 - * build = 0x07da - * unknown= 0x0000004b - * - * WinAIM 3.5.1670: - * clientstring = "AOL Instant Messenger (SM), version 3.5.1670/WIN32" - * major2 = 0x0004 - * major = 0x0003 - * minor = 0x0005 - * minor2 = 0x0000 - * build = 0x0686 - * unknown =0x0000002a - * - * Java AIM 1.1.19: - * clientstring = "AOL Instant Messenger (TM) version 1.1.19 for Java built 03/24/98, freeMem 215871 totalMem 1048567, i686, Linus, #2 SMP Sun Feb 11 03:41:17 UTC 2001 2.4.1-ac9, IBM Corporation, 1.1.8, 45.3, Tue Mar 27 12:09:17 PST 2001" - * major2 = 0x0001 - * major = 0x0001 - * minor = 0x0001 - * minor2 = (not sent) - * build = 0x0013 - * unknown= (not sent) - * - * AIM for Linux 1.1.112: - * clientstring = "AOL Instant Messenger (SM)" - * major2 = 0x1d09 - * major = 0x0001 - * minor = 0x0001 - * minor2 = 0x0001 - * build = 0x0070 - * unknown= 0x0000008b - * serverstore = 0x01 - * - */ -faim_export int aim_send_login(aim_session_t *sess, aim_conn_t *conn, const char *sn, const char *password, struct client_info_s *clientinfo, const char *key) -{ - aim_frame_t *fr; - aim_tlvlist_t *tl = NULL; - fu8_t digest[16]; - aim_snacid_t snacid; - - if (!clientinfo || !sn || !password) - return -EINVAL; - - if (sess->flags & AIM_SESS_FLAGS_XORLOGIN) - return goddamnicq2(sess, conn, sn, password); - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152))) - return -ENOMEM; - - if (sess->flags & AIM_SESS_FLAGS_XORLOGIN) { - fr->hdr.flap.type = 0x01; - - /* Use very specific version numbers to further indicate hack */ - clientinfo->major2 = 0x010a; - clientinfo->major = 0x0004; - clientinfo->minor = 0x003c; - clientinfo->minor2 = 0x0001; - clientinfo->build = 0x0cce; - clientinfo->unknown = 0x00000055; - } - - snacid = aim_cachesnac(sess, 0x0017, 0x0002, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0017, 0x0002, 0x0000, snacid); - - aim_addtlvtochain_raw(&tl, 0x0001, strlen(sn), sn); - - aim_encode_password_md5(password, key, digest); - aim_addtlvtochain_raw(&tl, 0x0025, 16, digest); - - aim_addtlvtochain_raw(&tl, 0x0003, strlen(clientinfo->clientstring), clientinfo->clientstring); - aim_addtlvtochain16(&tl, 0x0016, (fu16_t)clientinfo->major2); - aim_addtlvtochain16(&tl, 0x0017, (fu16_t)clientinfo->major); - aim_addtlvtochain16(&tl, 0x0018, (fu16_t)clientinfo->minor); - aim_addtlvtochain16(&tl, 0x0019, (fu16_t)clientinfo->minor2); - aim_addtlvtochain16(&tl, 0x001a, (fu16_t)clientinfo->build); - aim_addtlvtochain_raw(&tl, 0x000e, strlen(clientinfo->country), clientinfo->country); - aim_addtlvtochain_raw(&tl, 0x000f, strlen(clientinfo->lang), clientinfo->lang); - aim_addtlvtochain16(&tl, 0x0009, 0x0015); - - aim_writetlvchain(&fr->data, &tl); - - aim_freetlvchain(&tl); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -faim_export int aim_encode_password_md5(const char *password, const char *key, fu8_t *digest) -{ - md5_state_t state; - - md5_init(&state); - md5_append(&state, (const md5_byte_t *)key, strlen(key)); - md5_append(&state, (const md5_byte_t *)password, strlen(password)); - md5_append(&state, (const md5_byte_t *)AIM_MD5_STRING, strlen(AIM_MD5_STRING)); - md5_finish(&state, (md5_byte_t *)digest); - - return 0; -} - -/** - * aim_encode_password - Encode a password using old XOR method - * @password: incoming password - * @encoded: buffer to put encoded password - * - * This takes a const pointer to a (null terminated) string - * containing the unencoded password. It also gets passed - * an already allocated buffer to store the encoded password. - * This buffer should be the exact length of the password without - * the null. The encoded password buffer /is not %NULL terminated/. - * - * The encoding_table seems to be a fixed set of values. We'll - * hope it doesn't change over time! - * - * This is only used for the XOR method, not the better MD5 method. - * - */ -static int aim_encode_password(const char *password, fu8_t *encoded) -{ - fu8_t encoding_table[] = { -#if 0 /* old v1 table */ - 0xf3, 0xb3, 0x6c, 0x99, - 0x95, 0x3f, 0xac, 0xb6, - 0xc5, 0xfa, 0x6b, 0x63, - 0x69, 0x6c, 0xc3, 0x9f -#else /* v2.1 table, also works for ICQ */ - 0xf3, 0x26, 0x81, 0xc4, - 0x39, 0x86, 0xdb, 0x92, - 0x71, 0xa3, 0xb9, 0xe6, - 0x53, 0x7a, 0x95, 0x7c -#endif - }; - int i; - - for (i = 0; i < strlen(password); i++) - encoded[i] = (password[i] ^ encoding_table[i]); - - return 0; -} - -/* - * Generate an authorization response. - * - * You probably don't want this unless you're writing an AIM server. Which - * I hope you're not doing. Because it's far more difficult than it looks. - * - */ -faim_export int aim_sendauthresp(aim_session_t *sess, aim_conn_t *conn, const char *sn, int errorcode, const char *errorurl, const char *bosip, const char *cookie, const char *email, int regstatus) -{ - aim_tlvlist_t *tlvlist = NULL; - aim_frame_t *fr; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x04, 1152))) - return -ENOMEM; - - if (sn) - aim_addtlvtochain_raw(&tlvlist, 0x0001, strlen(sn), sn); - else - aim_addtlvtochain_raw(&tlvlist, 0x0001, strlen(sess->sn), sess->sn); - - if (errorcode) { - aim_addtlvtochain16(&tlvlist, 0x0008, errorcode); - aim_addtlvtochain_raw(&tlvlist, 0x0004, strlen(errorurl), errorurl); - } else { - aim_addtlvtochain_raw(&tlvlist, 0x0005, strlen(bosip), bosip); - aim_addtlvtochain_raw(&tlvlist, 0x0006, AIM_COOKIELEN, cookie); - aim_addtlvtochain_raw(&tlvlist, 0x0011, strlen(email), email); - aim_addtlvtochain16(&tlvlist, 0x0013, (fu16_t)regstatus); - } - - aim_writetlvchain(&fr->data, &tlvlist); - aim_freetlvchain(&tlvlist); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * Generate a random cookie. (Non-client use only) - */ -faim_export int aim_gencookie(fu8_t *buf) -{ - int i; - - srand(time(NULL)); - - for (i = 0; i < AIM_COOKIELEN; i++) - buf[i] = 1+(int) (256.0*rand()/(RAND_MAX+0.0)); - - return i; -} - -/* - * Send Server Ready. (Non-client) - * - * XXX If anyone cares, this should be made to use the conn-stored group - * system. - * - */ -faim_export int aim_sendserverready(aim_session_t *sess, aim_conn_t *conn) -{ - aim_frame_t *fr; - aim_snacid_t snacid; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+0x22))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0001, 0x0003, 0x0000, NULL, 0); - - aim_putsnac(&fr->data, 0x0001, 0x0003, 0x0000, snacid); - aimbs_put16(&fr->data, 0x0001); - aimbs_put16(&fr->data, 0x0002); - aimbs_put16(&fr->data, 0x0003); - aimbs_put16(&fr->data, 0x0004); - aimbs_put16(&fr->data, 0x0006); - aimbs_put16(&fr->data, 0x0008); - aimbs_put16(&fr->data, 0x0009); - aimbs_put16(&fr->data, 0x000a); - aimbs_put16(&fr->data, 0x000b); - aimbs_put16(&fr->data, 0x000c); - aimbs_put16(&fr->data, 0x0013); - aimbs_put16(&fr->data, 0x0015); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * Send service redirect. (Non-Client) - */ -faim_export int aim_sendredirect(aim_session_t *sess, aim_conn_t *conn, fu16_t servid, const char *ip, const char *cookie) -{ - aim_tlvlist_t *tlvlist = NULL; - aim_frame_t *fr; - aim_snacid_t snacid; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0001, 0x0005, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0001, 0x0005, 0x0000, snacid); - - aim_addtlvtochain16(&tlvlist, 0x000d, servid); - aim_addtlvtochain_raw(&tlvlist, 0x0005, strlen(ip), ip); - aim_addtlvtochain_raw(&tlvlist, 0x0006, AIM_COOKIELEN, cookie); - - aim_writetlvchain(&fr->data, &tlvlist); - aim_freetlvchain(&tlvlist); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * See comments in conn.c about how the group associations are supposed - * to work, and how they really work. - * - * This info probably doesn't even need to make it to the client. - * - * We don't actually call the client here. This starts off the connection - * initialization routine required by all AIM connections. The next time - * the client is called is the CONNINITDONE callback, which should be - * shortly after the rate information is acknowledged. - * - */ -static int hostonline(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) -{ - fu16_t *families; - int famcount; - - - if (!(families = malloc(aim_bstream_empty(bs)))) - return 0; - - for (famcount = 0; aim_bstream_empty(bs); famcount++) { - families[famcount] = aimbs_get16(bs); - aim_conn_addgroup(rx->conn, families[famcount]); - } - - free(families); - - - /* - * Next step is in the Host Versions handler. - * - * Note that we must send this before we request rates, since - * the format of the rate information depends on the versions we - * give it. - * - */ - aim_setversions(sess, rx->conn); - - return 1; -} - -static int redirect(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) -{ - int serviceid; - fu8_t *cookie; - char *ip; - aim_rxcallback_t userfunc; - aim_tlvlist_t *tlvlist; - char *chathack = NULL; - int chathackex = 0; - int ret = 0; - - tlvlist = aim_readtlvchain(bs); - - if (!aim_gettlv(tlvlist, 0x000d, 1) || - !aim_gettlv(tlvlist, 0x0005, 1) || - !aim_gettlv(tlvlist, 0x0006, 1)) { - aim_freetlvchain(&tlvlist); - return 0; - } - - serviceid = aim_gettlv16(tlvlist, 0x000d, 1); - ip = aim_gettlv_str(tlvlist, 0x0005, 1); - cookie = aim_gettlv_str(tlvlist, 0x0006, 1); - - /* - * Chat hack. - */ - if ((serviceid == AIM_CONN_TYPE_CHAT) && sess->pendingjoin) { - chathack = sess->pendingjoin; - chathackex = sess->pendingjoinexchange; - sess->pendingjoin = NULL; - sess->pendingjoinexchange = 0; - } - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, serviceid, ip, cookie, chathack, chathackex); - - free(ip); - free(cookie); - free(chathack); - - aim_freetlvchain(&tlvlist); - - return ret; -} - -/* - * Request Rate Information. - * - */ -faim_internal int aim_reqrates(aim_session_t *sess, aim_conn_t *conn) -{ - return aim_genericreq_n(sess, conn, 0x0001, 0x0006); -} - -/* - * Rate Information Response Acknowledge. - * - */ -faim_internal int aim_ratesack(aim_session_t *sess, aim_conn_t *conn) -{ - aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside; - aim_frame_t *fr; - aim_snacid_t snacid; - struct rateclass *rc; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 512))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0001, 0x0008, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0001, 0x0008, 0x0000, snacid); - - for (rc = ins->rates; rc; rc = rc->next) - aimbs_put16(&fr->data, rc->classid); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * The Rate Limiting System, An Abridged Guide to Nonsense. - * - * OSCAR defines several 'rate classes'. Each class has seperate - * rate limiting properties (limit level, alert level, disconnect - * level, etc), and a set of SNAC family/type pairs associated with - * it. The rate classes, their limiting properties, and the definitions - * of which SNACs are belong to which class, are defined in the - * Rate Response packet at login to each host. - * - * Logically, all rate offenses within one class count against further - * offenses for other SNACs in the same class (ie, sending messages - * too fast will limit the number of user info requests you can send, - * since those two SNACs are in the same rate class). - * - * Since the rate classes are defined dynamically at login, the values - * below may change. But they seem to be fairly constant. - * - * Currently, BOS defines five rate classes, with the commonly used - * members as follows... - * - * Rate class 0x0001: - * - Everything thats not in any of the other classes - * - * Rate class 0x0002: - * - Buddy list add/remove - * - Permit list add/remove - * - Deny list add/remove - * - * Rate class 0x0003: - * - User information requests - * - Outgoing ICBMs - * - * Rate class 0x0004: - * - A few unknowns: 2/9, 2/b, and f/2 - * - * Rate class 0x0005: - * - Chat room create - * - Outgoing chat ICBMs - * - * The only other thing of note is that class 5 (chat) has slightly looser - * limiting properties than class 3 (normal messages). But thats just a - * small bit of trivia for you. - * - * The last thing that needs to be learned about the rate limiting - * system is how the actual numbers relate to the passing of time. This - * seems to be a big mystery. - * - */ - -static void rc_addclass(struct rateclass **head, struct rateclass *inrc) -{ - struct rateclass *rc, *rc2; - - if (!(rc = malloc(sizeof(struct rateclass)))) - return; - - memcpy(rc, inrc, sizeof(struct rateclass)); - rc->next = NULL; - - for (rc2 = *head; rc2 && rc2->next; rc2 = rc2->next) - ; - - if (!rc2) - *head = rc; - else - rc2->next = rc; - - return; -} - -static struct rateclass *rc_findclass(struct rateclass **head, fu16_t id) -{ - struct rateclass *rc; - - for (rc = *head; rc; rc = rc->next) { - if (rc->classid == id) - return rc; - } - - return NULL; -} - -static void rc_addpair(struct rateclass *rc, fu16_t group, fu16_t type) -{ - struct snacpair *sp, *sp2; - - if (!(sp = malloc(sizeof(struct snacpair)))) - return; - memset(sp, 0, sizeof(struct snacpair)); - - sp->group = group; - sp->subtype = type; - sp->next = NULL; - - for (sp2 = rc->members; sp2 && sp2->next; sp2 = sp2->next) - ; - - if (!sp2) - rc->members = sp; - else - sp2->next = sp; - - return; -} - -static int rateresp(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) -{ - aim_conn_inside_t *ins = (aim_conn_inside_t *)rx->conn->inside; - fu16_t numclasses, i; - aim_rxcallback_t userfunc; - - - /* - * First are the parameters for each rate class. - */ - numclasses = aimbs_get16(bs); - for (i = 0; i < numclasses; i++) { - struct rateclass rc; - - memset(&rc, 0, sizeof(struct rateclass)); - - rc.classid = aimbs_get16(bs); - rc.windowsize = aimbs_get32(bs); - rc.clear = aimbs_get32(bs); - rc.alert = aimbs_get32(bs); - rc.limit = aimbs_get32(bs); - rc.disconnect = aimbs_get32(bs); - rc.current = aimbs_get32(bs); - rc.max = aimbs_get32(bs); - - /* - * The server will send an extra five bytes of parameters - * depending on the version we advertised in 1/17. If we - * didn't send 1/17 (evil!), then this will crash and you - * die, as it will default to the old version but we have - * the new version hardcoded here. - */ - if (mod->version >= 3) - aimbs_getrawbuf(bs, rc.unknown, sizeof(rc.unknown)); - - rc_addclass(&ins->rates, &rc); - } - - /* - * Then the members of each class. - */ - for (i = 0; i < numclasses; i++) { - fu16_t classid, count; - struct rateclass *rc; - int j; - - classid = aimbs_get16(bs); - count = aimbs_get16(bs); - - rc = rc_findclass(&ins->rates, classid); - - for (j = 0; j < count; j++) { - fu16_t group, subtype; - - group = aimbs_get16(bs); - subtype = aimbs_get16(bs); - - if (rc) - rc_addpair(rc, group, subtype); - } - } - - /* - * We don't pass the rate information up to the client, as it really - * doesn't care. The information is stored in the connection, however - * so that we can do more fun stuff later (not really). - */ - - /* - * Last step in the conn init procedure is to acknowledge that we - * agree to these draconian limitations. - */ - aim_ratesack(sess, rx->conn); - - /* - * Finally, tell the client it's ready to go... - */ - if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE))) - userfunc(sess, rx); - - - return 1; -} - -static int ratechange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) -{ - aim_rxcallback_t userfunc; - fu16_t code, rateclass; - fu32_t currentavg, maxavg, windowsize, clear, alert, limit, disconnect; - - code = aimbs_get16(bs); - rateclass = aimbs_get16(bs); - - windowsize = aimbs_get32(bs); - clear = aimbs_get32(bs); - alert = aimbs_get32(bs); - limit = aimbs_get32(bs); - disconnect = aimbs_get32(bs); - currentavg = aimbs_get32(bs); - maxavg = aimbs_get32(bs); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - return userfunc(sess, rx, code, rateclass, windowsize, clear, alert, limit, disconnect, currentavg, maxavg); - - return 0; -} - -static int selfinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) -{ - aim_rxcallback_t userfunc; - aim_userinfo_t userinfo; - - aim_extractuserinfo(sess, bs, &userinfo); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - return userfunc(sess, rx, &userinfo); - - return 0; -} - -static int evilnotify(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) -{ - aim_rxcallback_t userfunc; - fu16_t newevil; - aim_userinfo_t userinfo; - - memset(&userinfo, 0, sizeof(aim_userinfo_t)); - - newevil = aimbs_get16(bs); - - if (aim_bstream_empty(bs)) - aim_extractuserinfo(sess, bs, &userinfo); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - return userfunc(sess, rx, newevil, &userinfo); - - return 0; -} - -/* - * How Migrations work. - * - * The server sends a Server Pause message, which the client should respond to - * with a Server Pause Ack, which contains the families it needs on this - * connection. The server will send a Migration Notice with an IP address, and - * then disconnect. Next the client should open the connection and send the - * cookie. Repeat the normal login process and pretend this never happened. - * - * The Server Pause contains no data. - * - */ -static int serverpause(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) -{ - aim_rxcallback_t userfunc; - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - return userfunc(sess, rx); - - return 0; -} - -/* - * It is rather important that aim_sendpauseack() gets called for the exact - * same connection that the Server Pause callback was called for, since - * libfaim extracts the data for the SNAC from the connection structure. - * - * Of course, if you don't do that, more bad things happen than just what - * libfaim can cause. - * - */ -faim_export int aim_sendpauseack(aim_session_t *sess, aim_conn_t *conn) -{ - aim_frame_t *fr; - aim_snacid_t snacid; - aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside; - struct snacgroup *sg; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1024))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0001, 0x000c, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0001, 0x000c, 0x0000, snacid); - - /* - * This list should have all the groups that the original - * Host Online / Server Ready said this host supports. And - * we want them all back after the migration. - */ - for (sg = ins->groups; sg; sg = sg->next) - aimbs_put16(&fr->data, sg->group); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * This is the final SNAC sent on the original connection during a migration. - * It contains the IP and cookie used to connect to the new server, and - * optionally a list of the SNAC groups being migrated. - * - */ -static int migrate(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) -{ - aim_rxcallback_t userfunc; - int ret = 0; - fu16_t groupcount, i; - aim_tlvlist_t *tl; - char *ip = NULL; - aim_tlv_t *cktlv; - - /* - * Apparently there's some fun stuff that can happen right here. The - * migration can actually be quite selective about what groups it - * moves to the new server. When not all the groups for a connection - * are migrated, or they are all migrated but some groups are moved - * to a different server than others, it is called a bifurcated - * migration. - * - * Let's play dumb and not support that. - * - */ - groupcount = aimbs_get16(bs); - for (i = 0; i < groupcount; i++) { - fu16_t group; - - group = aimbs_get16(bs); - - faimdprintf(sess, 0, "bifurcated migration unsupported -- group 0x%04x\n", group); - } - - tl = aim_readtlvchain(bs); - - if (aim_gettlv(tl, 0x0005, 1)) - ip = aim_gettlv_str(tl, 0x0005, 1); - - cktlv = aim_gettlv(tl, 0x0006, 1); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, ip, cktlv ? cktlv->value : NULL); - - aim_freetlvchain(&tl); - free(ip); - - return ret; -} - -static int motd(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) -{ - aim_rxcallback_t userfunc; - char *msg = NULL; - int ret = 0; - aim_tlvlist_t *tlvlist; - fu16_t id; - - /* - * Code. - * - * Valid values: - * 1 Mandatory upgrade - * 2 Advisory upgrade - * 3 System bulletin - * 4 Nothing's wrong ("top o the world" -- normal) - * 5 Lets-break-something. - * - */ - id = aimbs_get16(bs); - - /* - * TLVs follow - */ - tlvlist = aim_readtlvchain(bs); - - msg = aim_gettlv_str(tlvlist, 0x000b, 1); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, id, msg); - - free(msg); - - aim_freetlvchain(&tlvlist); - - return ret; -} - -faim_internal int aim_setversions(aim_session_t *sess, aim_conn_t *conn) -{ - aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside; - struct snacgroup *sg; - aim_frame_t *fr; - aim_snacid_t snacid; - - if (!ins) - return -EINVAL; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0001, 0x0017, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0001, 0x0017, 0x0000, snacid); - - /* - * Send only the versions that the server cares about (that it - * marked as supporting in the server ready SNAC). - */ - for (sg = ins->groups; sg; sg = sg->next) { - aim_module_t *mod; - - if ((mod = aim__findmodulebygroup(sess, sg->group))) { - aimbs_put16(&fr->data, mod->family); - aimbs_put16(&fr->data, mod->version); - } else - faimdprintf(sess, 1, "aim_setversions: server supports group 0x%04x but we don't!\n", sg->group); - } - - aim_tx_enqueue(sess, fr); - - return 0; -} - -static int hostversions(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) -{ - int vercount; - fu8_t *versions; - - /* This is frivolous. (Thank you SmarterChild.) */ - vercount = aim_bstream_empty(bs)/4; - versions = aimbs_getraw(bs, aim_bstream_empty(bs)); - free(versions); - - /* - * Now request rates. - */ - aim_reqrates(sess, rx->conn); - - return 1; -} - -/* - * Starting this past week (26 Mar 2001, say), AOL has started sending - * this nice little extra SNAC. AFAIK, it has never been used until now. - * - * The request contains eight bytes. The first four are an offset, the - * second four are a length. - * - * The offset is an offset into aim.exe when it is mapped during execution - * on Win32. So far, AOL has only been requesting bytes in static regions - * of memory. (I won't put it past them to start requesting data in - * less static regions -- regions that are initialized at run time, but still - * before the client recieves this request.) - * - * When the client recieves the request, it adds it to the current ds - * (0x00400000) and dereferences it, copying the data into a buffer which - * it then runs directly through the MD5 hasher. The 16 byte output of - * the hash is then sent back to the server. - * - * If the client does not send any data back, or the data does not match - * the data that the specific client should have, the client will get the - * following message from "AOL Instant Messenger": - * "You have been disconnected from the AOL Instant Message Service (SM) - * for accessing the AOL network using unauthorized software. You can - * download a FREE, fully featured, and authorized client, here - * http://www.aol.com/aim/download2.html" - * The connection is then closed, recieving disconnect code 1, URL - * http://www.aim.aol.com/errors/USER_LOGGED_OFF_NEW_LOGIN.html. - * - * Note, however, that numerous inconsistencies can cause the above error, - * not just sending back a bad hash. Do not immediatly suspect this code - * if you get disconnected. AOL and the open/free software community have - * played this game for a couple years now, generating the above message - * on numerous ocassions. - * - * Anyway, neener. We win again. - * - */ -static int memrequest(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) -{ - aim_rxcallback_t userfunc; - fu32_t offset, len; - aim_tlvlist_t *list; - char *modname; - - offset = aimbs_get32(bs); - len = aimbs_get32(bs); - list = aim_readtlvchain(bs); - - modname = aim_gettlv_str(list, 0x0001, 1); - - faimdprintf(sess, 1, "data at 0x%08lx (%d bytes) of requested\n", offset, len, modname ? modname : "aim.exe"); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - return userfunc(sess, rx, offset, len, modname); - - free(modname); - aim_freetlvchain(&list); - - return 0; -} - -#if 0 -static void dumpbox(aim_session_t *sess, unsigned char *buf, int len) -{ - int i; - - if (!sess || !buf || !len) - return; - - faimdprintf(sess, 1, "\nDump of %d bytes at %p:", len, buf); - - for (i = 0; i < len; i++) { - if ((i % 8) == 0) - faimdprintf(sess, 1, "\n\t"); - - faimdprintf(sess, 1, "0x%2x ", buf[i]); - } - - faimdprintf(sess, 1, "\n\n"); - - return; -} -#endif - -faim_export int aim_sendmemblock(aim_session_t *sess, aim_conn_t *conn, fu32_t offset, fu32_t len, const fu8_t *buf, fu8_t flag) -{ - aim_frame_t *fr; - aim_snacid_t snacid; - - if (!sess || !conn) - return -EINVAL; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+16))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0001, 0x0020, 0x0000, NULL, 0); - - aim_putsnac(&fr->data, 0x0001, 0x0020, 0x0000, snacid); - aimbs_put16(&fr->data, 0x0010); /* md5 is always 16 bytes */ - - if ((flag == AIM_SENDMEMBLOCK_FLAG_ISHASH) && buf && (len == 0x10)) { /* we're getting a hash */ - - aimbs_putraw(&fr->data, buf, 0x10); - - } else if (buf && (len > 0)) { /* use input buffer */ - md5_state_t state; - md5_byte_t digest[0x10]; - - md5_init(&state); - md5_append(&state, (const md5_byte_t *)buf, len); - md5_finish(&state, digest); - - aimbs_putraw(&fr->data, (fu8_t *)digest, 0x10); - - } else if (len == 0) { /* no length, just hash NULL (buf is optional) */ - md5_state_t state; - fu8_t nil = '\0'; - md5_byte_t digest[0x10]; - - /* - * These MD5 routines are stupid in that you have to have - * at least one append. So thats why this doesn't look - * real logical. - */ - md5_init(&state); - md5_append(&state, (const md5_byte_t *)&nil, 0); - md5_finish(&state, digest); - - aimbs_putraw(&fr->data, (fu8_t *)digest, 0x10); - - } else { - - /* - * This data is correct for AIM 3.5.1670. - * - * Using these blocks is as close to "legal" as you can get - * without using an AIM binary. - * - */ - if ((offset == 0x03ffffff) && (len == 0x03ffffff)) { - -#if 1 /* with "AnrbnrAqhfzcd" */ - aimbs_put32(&fr->data, 0x44a95d26); - aimbs_put32(&fr->data, 0xd2490423); - aimbs_put32(&fr->data, 0x93b8821f); - aimbs_put32(&fr->data, 0x51c54b01); -#else /* no filename */ - aimbs_put32(&fr->data, 0x1df8cbae); - aimbs_put32(&fr->data, 0x5523b839); - aimbs_put32(&fr->data, 0xa0e10db3); - aimbs_put32(&fr->data, 0xa46d3b39); -#endif - - } else if ((offset == 0x00001000) && (len == 0x00000000)) { - - aimbs_put32(&fr->data, 0xd41d8cd9); - aimbs_put32(&fr->data, 0x8f00b204); - aimbs_put32(&fr->data, 0xe9800998); - aimbs_put32(&fr->data, 0xecf8427e); - - } else - faimdprintf(sess, 0, "sendmemblock: WARNING: unknown hash request\n"); - - } - - aim_tx_enqueue(sess, fr); - - return 0; -} - -static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) -{ - - if (snac->subtype == 0x0003) - return hostonline(sess, mod, rx, snac, bs); - else if (snac->subtype == 0x0005) - return redirect(sess, mod, rx, snac, bs); - else if (snac->subtype == 0x0007) - return rateresp(sess, mod, rx, snac, bs); - else if (snac->subtype == 0x000a) - return ratechange(sess, mod, rx, snac, bs); - else if (snac->subtype == 0x000b) - return serverpause(sess, mod, rx, snac, bs); - else if (snac->subtype == 0x000f) - return selfinfo(sess, mod, rx, snac, bs); - else if (snac->subtype == 0x0010) - return evilnotify(sess, mod, rx, snac, bs); - else if (snac->subtype == 0x0012) - return migrate(sess, mod, rx, snac, bs); - else if (snac->subtype == 0x0013) - return motd(sess, mod, rx, snac, bs); - else if (snac->subtype == 0x0018) - return hostversions(sess, mod, rx, snac, bs); - else if (snac->subtype == 0x001f) - return memrequest(sess, mod, rx, snac, bs); - - return 0; -} - -faim_internal int general_modfirst(aim_session_t *sess, aim_module_t *mod) -{ - - mod->family = 0x0001; - mod->version = 0x0003; - mod->toolid = 0x0110; - mod->toolversion = 0x047b; - mod->flags = 0; - strncpy(mod->name, "general", sizeof(mod->name)); - mod->snachandler = snachandler; - - return 0; -} - diff -r 94b4271b9567 -r 441b84ab7f4e src/protocols/oscar/misc.c --- a/src/protocols/oscar/misc.c Sat Nov 10 01:30:13 2001 +0000 +++ b/src/protocols/oscar/misc.c Sat Nov 10 01:48:17 2001 +0000 @@ -15,102 +15,6 @@ #include /* - * aim_bos_setidle() - * - * Should set your current idle time in seconds. Idealy, OSCAR should - * do this for us. But, it doesn't. The client must call this to set idle - * time. - * - */ -faim_export int aim_bos_setidle(aim_session_t *sess, aim_conn_t *conn, fu32_t idletime) -{ - return aim_genericreq_l(sess, conn, 0x0001, 0x0011, &idletime); -} - - -/* - * aim_bos_changevisibility(conn, changtype, namelist) - * - * Changes your visibility depending on changetype: - * - * AIM_VISIBILITYCHANGE_PERMITADD: Lets provided list of names see you - * AIM_VISIBILITYCHANGE_PERMIDREMOVE: Removes listed names from permit list - * AIM_VISIBILITYCHANGE_DENYADD: Hides you from provided list of names - * AIM_VISIBILITYCHANGE_DENYREMOVE: Lets list see you again - * - * list should be a list of - * screen names in the form "Screen Name One&ScreenNameTwo&" etc. - * - * Equivelents to options in WinAIM: - * - Allow all users to contact me: Send an AIM_VISIBILITYCHANGE_DENYADD - * with only your name on it. - * - Allow only users on my Buddy List: Send an - * AIM_VISIBILITYCHANGE_PERMITADD with the list the same as your - * buddy list - * - Allow only the uesrs below: Send an AIM_VISIBILITYCHANGE_PERMITADD - * with everyone listed that you want to see you. - * - Block all users: Send an AIM_VISIBILITYCHANGE_PERMITADD with only - * yourself in the list - * - Block the users below: Send an AIM_VISIBILITYCHANGE_DENYADD with - * the list of users to be blocked - * - * XXX ye gods. - */ -faim_export int aim_bos_changevisibility(aim_session_t *sess, aim_conn_t *conn, int changetype, const char *denylist) -{ - aim_frame_t *fr; - int packlen = 0; - fu16_t subtype; - char *localcpy = NULL, *tmpptr = NULL; - int i; - int listcount; - aim_snacid_t snacid; - - if (!denylist) - return -EINVAL; - - if (changetype == AIM_VISIBILITYCHANGE_PERMITADD) - subtype = 0x05; - else if (changetype == AIM_VISIBILITYCHANGE_PERMITREMOVE) - subtype = 0x06; - else if (changetype == AIM_VISIBILITYCHANGE_DENYADD) - subtype = 0x07; - else if (changetype == AIM_VISIBILITYCHANGE_DENYREMOVE) - subtype = 0x08; - else - return -EINVAL; - - localcpy = strdup(denylist); - - listcount = aimutil_itemcnt(localcpy, '&'); - packlen = aimutil_tokslen(localcpy, 99, '&') + listcount + 9; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, packlen))) { - free(localcpy); - return -ENOMEM; - } - - snacid = aim_cachesnac(sess, 0x0009, subtype, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0009, subtype, 0x00, snacid); - - for (i = 0; (i < (listcount - 1)) && (i < 99); i++) { - tmpptr = aimutil_itemidx(localcpy, i, '&'); - - aimbs_put8(&fr->data, strlen(tmpptr)); - aimbs_putraw(&fr->data, tmpptr, strlen(tmpptr)); - - free(tmpptr); - } - free(localcpy); - - aim_tx_enqueue(sess, fr); - - return 0; -} - - - -/* * aim_bos_setbuddylist(buddylist) * * This just builds the "set buddy list" command then queues it. @@ -172,7 +76,6 @@ * aim_bos_setprofile(profile) * * Gives BOS your profile. - * * */ faim_export int aim_bos_setprofile(aim_session_t *sess, aim_conn_t *conn, const char *profile, const char *awaymsg, fu16_t caps) @@ -187,10 +90,21 @@ aim_addtlvtochain_raw(&tl, 0x0001, strlen(defencoding), defencoding); aim_addtlvtochain_raw(&tl, 0x0002, strlen(profile), profile); } - + + /* + * So here's how this works: + * - You are away when you have a non-zero-length type 4 TLV stored. + * - You become unaway when you clear the TLV with a zero-length + * type 4 TLV. + * - If you do not send the type 4 TLV, your status does not change + * (that is, if you were away, you'll remain away). + */ if (awaymsg) { - aim_addtlvtochain_raw(&tl, 0x0003, strlen(defencoding), defencoding); - aim_addtlvtochain_raw(&tl, 0x0004, strlen(awaymsg), awaymsg); + if (strlen(awaymsg)) { + aim_addtlvtochain_raw(&tl, 0x0003, strlen(defencoding), defencoding); + aim_addtlvtochain_raw(&tl, 0x0004, strlen(awaymsg), awaymsg); + } else + aim_addtlvtochain_noval(&tl, 0x0004); } aim_addtlvtochain_caps(&tl, 0x0005, caps); @@ -210,124 +124,6 @@ } /* - * aim_bos_clientready() - * - * Send Client Ready. - * - */ -faim_export int aim_clientready(aim_session_t *sess, aim_conn_t *conn) -{ - aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside; - struct snacgroup *sg; - aim_frame_t *fr; - aim_snacid_t snacid; - - if (!ins) - return -EINVAL; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0001, 0x0002, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0001, 0x0002, 0x0000, snacid); - - /* - * Send only the tool versions that the server cares about (that it - * marked as supporting in the server ready SNAC). - */ - for (sg = ins->groups; sg; sg = sg->next) { - aim_module_t *mod; - - if ((mod = aim__findmodulebygroup(sess, sg->group))) { - aimbs_put16(&fr->data, mod->family); - aimbs_put16(&fr->data, mod->version); - aimbs_put16(&fr->data, mod->toolid); - aimbs_put16(&fr->data, mod->toolversion); - } else - faimdprintf(sess, 1, "aim_clientready: server supports group 0x%04x but we don't!\n", sg->group); - } - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * aim_bos_setprivacyflags() - * - * Sets privacy flags. Normally 0x03. - * - * Bit 1: Allows other AIM users to see how long you've been idle. - * Bit 2: Allows other AIM users to see how long you've been a member. - * - */ -faim_export int aim_bos_setprivacyflags(aim_session_t *sess, aim_conn_t *conn, fu32_t flags) -{ - return aim_genericreq_l(sess, conn, 0x0001, 0x0014, &flags); -} - -/* - * aim_bos_reqpersonalinfo() - * - */ -faim_export int aim_bos_reqpersonalinfo(aim_session_t *sess, aim_conn_t *conn) -{ - return aim_genericreq_n(sess, conn, 0x0001, 0x000e); -} - -/* - * aim_bos_reqservice(serviceid) - * - * Service request. - * - */ -faim_export int aim_bos_reqservice(aim_session_t *sess, aim_conn_t *conn, fu16_t serviceid) -{ - return aim_genericreq_s(sess, conn, 0x0001, 0x0004, &serviceid); -} - -/* - * aim_bos_nop() - * - * No-op. WinAIM sends these every 4min or so to keep - * the connection alive. Its not real necessary. - * - */ -faim_export int aim_bos_nop(aim_session_t *sess, aim_conn_t *conn) -{ - return aim_genericreq_n(sess, conn, 0x0001, 0x0016); -} - -/* - * aim_flap_nop() - * - * No-op. WinAIM 4.x sends these _every minute_ to keep - * the connection alive. - */ -faim_export int aim_flap_nop(aim_session_t *sess, aim_conn_t *conn) -{ - aim_frame_t *fr; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x05, 0))) - return -ENOMEM; - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * aim_bos_reqrights() - * - * Request BOS rights. - * - */ -faim_export int aim_bos_reqrights(aim_session_t *sess, aim_conn_t *conn) -{ - return aim_genericreq_n(sess, conn, 0x0009, 0x0002); -} - -/* * aim_bos_reqbuddyrights() * * Request Buddy List rights. @@ -425,10 +221,6 @@ return 0; } -/* - * - * - */ faim_internal int aim_genericreq_l(aim_session_t *sess, aim_conn_t *conn, fu16_t family, fu16_t subtype, fu32_t *longdata) { aim_frame_t *fr; @@ -567,32 +359,8 @@ return 0; } -faim_export int aim_icq_setstatus(aim_session_t *sess, aim_conn_t *conn, fu16_t status) -{ - aim_frame_t *fr; - aim_snacid_t snacid; - aim_tlvlist_t *tl = NULL; - fu32_t data; - - data = 0x00030000 | status; /* yay for error checking ;^) */ - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 8))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0001, 0x001e, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0001, 0x001e, 0x0000, snacid); - - aim_addtlvtochain32(&tl, 0x0006, data); - aim_writetlvchain(&fr->data, &tl); - aim_freetlvchain(&tl); - - aim_tx_enqueue(sess, fr); - - return 0; -} - /* - * Should be generic enough to handle the errors for all families... + * Should be generic enough to handle the errors for all groups. * */ static int generror(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) @@ -643,3 +411,5 @@ return 0; } + + diff -r 94b4271b9567 -r 441b84ab7f4e src/protocols/oscar/oscar.c --- a/src/protocols/oscar/oscar.c Sat Nov 10 01:30:13 2001 +0000 +++ b/src/protocols/oscar/oscar.c Sat Nov 10 01:48:17 2001 +0000 @@ -691,7 +691,7 @@ od->killme = TRUE; return 0; } - aim_auth_sendcookie(sess, bosconn, cookie); + aim_sendcookie(sess, bosconn, cookie); gaim_input_remove(gc->inpa); return 1; } @@ -1028,7 +1028,7 @@ g_free(host); return 1; } - aim_auth_sendcookie(sess, tstconn, cookie); + aim_sendcookie(sess, tstconn, cookie); break; case 0xd: /* ChatNav */ tstconn = aim_newconn(sess, AIM_CONN_TYPE_CHATNAV, NULL); @@ -1047,7 +1047,7 @@ g_free(host); return 1; } - aim_auth_sendcookie(sess, tstconn, cookie); + aim_sendcookie(sess, tstconn, cookie); break; case 0xe: /* Chat */ { @@ -1086,7 +1086,7 @@ g_free(ccon); return 1; } - aim_auth_sendcookie(sess, tstconn, cookie); + aim_sendcookie(sess, tstconn, cookie); debug_printf("Connected to chat room %s exchange %d\n", roomname, exchange); } break; @@ -1903,7 +1903,7 @@ static int conninitdone_bos(aim_session_t *sess, aim_frame_t *fr, ...) { struct gaim_connection *gc = sess->aux_data; - aim_bos_reqpersonalinfo(sess, fr->conn); + aim_reqpersonalinfo(sess, fr->conn); aim_bos_reqlocaterights(sess, fr->conn); aim_bos_setprofile(sess, fr->conn, gc->user->user_info, NULL, gaim_caps); aim_bos_reqbuddyrights(sess, fr->conn); @@ -1935,19 +1935,19 @@ if (od->chpass) { debug_printf("changing password\n"); - aim_auth_changepasswd(sess, fr->conn, od->newp, od->oldp); + aim_admin_changepasswd(sess, fr->conn, od->newp, od->oldp); g_free(od->oldp); g_free(od->newp); od->chpass = FALSE; } if (od->conf) { debug_printf("confirming account\n"); - aim_auth_reqconfirm(sess, fr->conn); + aim_admin_reqconfirm(sess, fr->conn); od->conf = FALSE; } if (od->reqemail) { debug_printf("requesting email\n"); - aim_auth_getinfo(sess, fr->conn, 0x0011); + aim_admin_getinfo(sess, fr->conn, 0x0011); od->reqemail = FALSE; } @@ -2007,7 +2007,7 @@ aim_clientready(sess, fr->conn); - aim_bos_reqservice(sess, fr->conn, AIM_CONN_TYPE_CHATNAV); + aim_reqservice(sess, fr->conn, AIM_CONN_TYPE_CHATNAV); return 1; } @@ -2230,31 +2230,31 @@ gc->away = NULL; if (!strcmp(state, "Online")) - aim_icq_setstatus(od->sess, od->conn, AIM_ICQ_STATE_ONLINE); + aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_ONLINE); else if (!strcmp(state, "Away")) { - aim_icq_setstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY); + aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY); gc->away = ""; } else if (!strcmp(state, "Do Not Disturb")) { - aim_icq_setstatus(od->sess, od->conn, AIM_ICQ_STATE_DND); + aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_DND); gc->away = ""; } else if (!strcmp(state, "Not Available")) { - aim_icq_setstatus(od->sess, od->conn, AIM_ICQ_STATE_NA); + aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NA); gc->away = ""; } else if (!strcmp(state, "Occupied")) { - aim_icq_setstatus(od->sess, od->conn, AIM_ICQ_STATE_OCCUPIED); + aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_OCCUPIED); gc->away = ""; } else if (!strcmp(state, "Free For Chat")) { - aim_icq_setstatus(od->sess, od->conn, AIM_ICQ_STATE_CHAT); + aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_CHAT); gc->away = ""; } else if (!strcmp(state, "Invisible")) { - aim_icq_setstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE); + aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE); gc->away = ""; } else if (!strcmp(state, GAIM_AWAY_CUSTOM)) { if (message) { - aim_icq_setstatus(od->sess, od->conn, AIM_ICQ_STATE_NA); + aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NA); gc->away = ""; } else { - aim_icq_setstatus(od->sess, od->conn, AIM_ICQ_STATE_ONLINE); + aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_ONLINE); } } } @@ -2335,7 +2335,7 @@ debug_printf("chatnav does not exist, opening chatnav\n"); odata->create_exchange = *exchange; odata->create_name = g_strdup(name); - aim_bos_reqservice(odata->sess, odata->conn, AIM_CONN_TYPE_CHATNAV); + aim_reqservice(odata->sess, odata->conn, AIM_CONN_TYPE_CHATNAV); } } @@ -2734,16 +2734,16 @@ } else if (!strcmp(act, "Confirm Account")) { if (!conn) { od->conf = TRUE; - aim_bos_reqservice(od->sess, od->conn, AIM_CONN_TYPE_AUTH); + aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_AUTH); } else - aim_auth_reqconfirm(od->sess, conn); + aim_admin_reqconfirm(od->sess, conn); } else if (!strcmp(act, "Change Email")) { } else if (!strcmp(act, "Display Current Registered Address")) { if (!conn) { od->reqemail = TRUE; - aim_bos_reqservice(od->sess, od->conn, AIM_CONN_TYPE_AUTH); + aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_AUTH); } else - aim_auth_getinfo(od->sess, conn, 0x11); + aim_admin_getinfo(od->sess, conn, 0x11); } else if (!strcmp(act, "Search for Buddy by Email")) { show_find_email(gc); } @@ -2774,9 +2774,9 @@ od->chpass = TRUE; od->oldp = g_strdup(old); od->newp = g_strdup(new); - aim_bos_reqservice(od->sess, od->conn, AIM_CONN_TYPE_AUTH); + aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_AUTH); } else { - aim_auth_changepasswd(od->sess, aim_getconn_type(od->sess, AIM_CONN_TYPE_AUTH), + aim_admin_changepasswd(od->sess, aim_getconn_type(od->sess, AIM_CONN_TYPE_AUTH), new, old); } } diff -r 94b4271b9567 -r 441b84ab7f4e src/protocols/oscar/service.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/oscar/service.c Sat Nov 10 01:48:17 2001 +0000 @@ -0,0 +1,966 @@ +/* + * Group 1. This is a very special group. All connections support + * this group, as it does some particularly good things (like rate limiting). + */ + +#define FAIM_INTERNAL +#include + +#include "md5.h" + +/* Client Online (group 1, subtype 2) */ +faim_export int aim_clientready(aim_session_t *sess, aim_conn_t *conn) +{ + aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside; + struct snacgroup *sg; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!ins) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0001, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0001, 0x0002, 0x0000, snacid); + + /* + * Send only the tool versions that the server cares about (that it + * marked as supporting in the server ready SNAC). + */ + for (sg = ins->groups; sg; sg = sg->next) { + aim_module_t *mod; + + if ((mod = aim__findmodulebygroup(sess, sg->group))) { + aimbs_put16(&fr->data, mod->family); + aimbs_put16(&fr->data, mod->version); + aimbs_put16(&fr->data, mod->toolid); + aimbs_put16(&fr->data, mod->toolversion); + } else + faimdprintf(sess, 1, "aim_clientready: server supports group 0x%04x but we don't!\n", sg->group); + } + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Host Online (group 1, type 3) + * + * See comments in conn.c about how the group associations are supposed + * to work, and how they really work. + * + * This info probably doesn't even need to make it to the client. + * + * We don't actually call the client here. This starts off the connection + * initialization routine required by all AIM connections. The next time + * the client is called is the CONNINITDONE callback, which should be + * shortly after the rate information is acknowledged. + * + */ +static int hostonline(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + fu16_t *families; + int famcount; + + + if (!(families = malloc(aim_bstream_empty(bs)))) + return 0; + + for (famcount = 0; aim_bstream_empty(bs); famcount++) { + families[famcount] = aimbs_get16(bs); + aim_conn_addgroup(rx->conn, families[famcount]); + } + + free(families); + + + /* + * Next step is in the Host Versions handler. + * + * Note that we must send this before we request rates, since + * the format of the rate information depends on the versions we + * give it. + * + */ + aim_setversions(sess, rx->conn); + + return 1; +} + +/* Service request (group 1, type 4) */ +faim_export int aim_reqservice(aim_session_t *sess, aim_conn_t *conn, fu16_t serviceid) +{ + return aim_genericreq_s(sess, conn, 0x0001, 0x0004, &serviceid); +} + +/* Redirect (group 1, type 5) */ +static int redirect(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int serviceid; + fu8_t *cookie; + char *ip; + aim_rxcallback_t userfunc; + aim_tlvlist_t *tlvlist; + char *chathack = NULL; + int chathackex = 0; + int ret = 0; + + tlvlist = aim_readtlvchain(bs); + + if (!aim_gettlv(tlvlist, 0x000d, 1) || + !aim_gettlv(tlvlist, 0x0005, 1) || + !aim_gettlv(tlvlist, 0x0006, 1)) { + aim_freetlvchain(&tlvlist); + return 0; + } + + serviceid = aim_gettlv16(tlvlist, 0x000d, 1); + ip = aim_gettlv_str(tlvlist, 0x0005, 1); + cookie = aim_gettlv_str(tlvlist, 0x0006, 1); + + /* + * Chat hack. + */ + if ((serviceid == AIM_CONN_TYPE_CHAT) && sess->pendingjoin) { + chathack = sess->pendingjoin; + chathackex = sess->pendingjoinexchange; + sess->pendingjoin = NULL; + sess->pendingjoinexchange = 0; + } + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, serviceid, ip, cookie, chathack, chathackex); + + free(ip); + free(cookie); + free(chathack); + + aim_freetlvchain(&tlvlist); + + return ret; +} + +/* Request Rate Information. (group 1, type 6) */ +faim_internal int aim_reqrates(aim_session_t *sess, aim_conn_t *conn) +{ + return aim_genericreq_n(sess, conn, 0x0001, 0x0006); +} + +/* + * OSCAR defines several 'rate classes'. Each class has seperate + * rate limiting properties (limit level, alert level, disconnect + * level, etc), and a set of SNAC family/type pairs associated with + * it. The rate classes, their limiting properties, and the definitions + * of which SNACs are belong to which class, are defined in the + * Rate Response packet at login to each host. + * + * Logically, all rate offenses within one class count against further + * offenses for other SNACs in the same class (ie, sending messages + * too fast will limit the number of user info requests you can send, + * since those two SNACs are in the same rate class). + * + * Since the rate classes are defined dynamically at login, the values + * below may change. But they seem to be fairly constant. + * + * Currently, BOS defines five rate classes, with the commonly used + * members as follows... + * + * Rate class 0x0001: + * - Everything thats not in any of the other classes + * + * Rate class 0x0002: + * - Buddy list add/remove + * - Permit list add/remove + * - Deny list add/remove + * + * Rate class 0x0003: + * - User information requests + * - Outgoing ICBMs + * + * Rate class 0x0004: + * - A few unknowns: 2/9, 2/b, and f/2 + * + * Rate class 0x0005: + * - Chat room create + * - Outgoing chat ICBMs + * + * The only other thing of note is that class 5 (chat) has slightly looser + * limiting properties than class 3 (normal messages). But thats just a + * small bit of trivia for you. + * + * The last thing that needs to be learned about the rate limiting + * system is how the actual numbers relate to the passing of time. This + * seems to be a big mystery. + * + */ + +static void rc_addclass(struct rateclass **head, struct rateclass *inrc) +{ + struct rateclass *rc, *rc2; + + if (!(rc = malloc(sizeof(struct rateclass)))) + return; + + memcpy(rc, inrc, sizeof(struct rateclass)); + rc->next = NULL; + + for (rc2 = *head; rc2 && rc2->next; rc2 = rc2->next) + ; + + if (!rc2) + *head = rc; + else + rc2->next = rc; + + return; +} + +static struct rateclass *rc_findclass(struct rateclass **head, fu16_t id) +{ + struct rateclass *rc; + + for (rc = *head; rc; rc = rc->next) { + if (rc->classid == id) + return rc; + } + + return NULL; +} + +static void rc_addpair(struct rateclass *rc, fu16_t group, fu16_t type) +{ + struct snacpair *sp, *sp2; + + if (!(sp = malloc(sizeof(struct snacpair)))) + return; + memset(sp, 0, sizeof(struct snacpair)); + + sp->group = group; + sp->subtype = type; + sp->next = NULL; + + for (sp2 = rc->members; sp2 && sp2->next; sp2 = sp2->next) + ; + + if (!sp2) + rc->members = sp; + else + sp2->next = sp; + + return; +} + +/* Rate Parameters (group 1, type 7) */ +static int rateresp(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_conn_inside_t *ins = (aim_conn_inside_t *)rx->conn->inside; + fu16_t numclasses, i; + aim_rxcallback_t userfunc; + + + /* + * First are the parameters for each rate class. + */ + numclasses = aimbs_get16(bs); + for (i = 0; i < numclasses; i++) { + struct rateclass rc; + + memset(&rc, 0, sizeof(struct rateclass)); + + rc.classid = aimbs_get16(bs); + rc.windowsize = aimbs_get32(bs); + rc.clear = aimbs_get32(bs); + rc.alert = aimbs_get32(bs); + rc.limit = aimbs_get32(bs); + rc.disconnect = aimbs_get32(bs); + rc.current = aimbs_get32(bs); + rc.max = aimbs_get32(bs); + + /* + * The server will send an extra five bytes of parameters + * depending on the version we advertised in 1/17. If we + * didn't send 1/17 (evil!), then this will crash and you + * die, as it will default to the old version but we have + * the new version hardcoded here. + */ + if (mod->version >= 3) + aimbs_getrawbuf(bs, rc.unknown, sizeof(rc.unknown)); + + rc_addclass(&ins->rates, &rc); + } + + /* + * Then the members of each class. + */ + for (i = 0; i < numclasses; i++) { + fu16_t classid, count; + struct rateclass *rc; + int j; + + classid = aimbs_get16(bs); + count = aimbs_get16(bs); + + rc = rc_findclass(&ins->rates, classid); + + for (j = 0; j < count; j++) { + fu16_t group, subtype; + + group = aimbs_get16(bs); + subtype = aimbs_get16(bs); + + if (rc) + rc_addpair(rc, group, subtype); + } + } + + /* + * We don't pass the rate information up to the client, as it really + * doesn't care. The information is stored in the connection, however + * so that we can do more fun stuff later (not really). + */ + + /* + * Last step in the conn init procedure is to acknowledge that we + * agree to these draconian limitations. + */ + aim_rates_addparam(sess, rx->conn); + + /* + * Finally, tell the client it's ready to go... + */ + if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE))) + userfunc(sess, rx); + + + return 1; +} + +/* Add Rate Parameter (group 1, type 8) */ +faim_internal int aim_rates_addparam(aim_session_t *sess, aim_conn_t *conn) +{ + aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside; + aim_frame_t *fr; + aim_snacid_t snacid; + struct rateclass *rc; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 512))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0001, 0x0008, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0001, 0x0008, 0x0000, snacid); + + for (rc = ins->rates; rc; rc = rc->next) + aimbs_put16(&fr->data, rc->classid); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* Delete Rate Parameter (group 1, type 9) */ +faim_internal int aim_rates_delparam(aim_session_t *sess, aim_conn_t *conn) +{ + aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside; + aim_frame_t *fr; + aim_snacid_t snacid; + struct rateclass *rc; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 512))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0001, 0x0009, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0001, 0x0009, 0x0000, snacid); + + for (rc = ins->rates; rc; rc = rc->next) + aimbs_put16(&fr->data, rc->classid); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* Rate Change (group 1, type 0x0a) */ +static int ratechange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_rxcallback_t userfunc; + fu16_t code, rateclass; + fu32_t currentavg, maxavg, windowsize, clear, alert, limit, disconnect; + + code = aimbs_get16(bs); + rateclass = aimbs_get16(bs); + + windowsize = aimbs_get32(bs); + clear = aimbs_get32(bs); + alert = aimbs_get32(bs); + limit = aimbs_get32(bs); + disconnect = aimbs_get32(bs); + currentavg = aimbs_get32(bs); + maxavg = aimbs_get32(bs); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + return userfunc(sess, rx, code, rateclass, windowsize, clear, alert, limit, disconnect, currentavg, maxavg); + + return 0; +} + +/* + * How Migrations work. + * + * The server sends a Server Pause message, which the client should respond to + * with a Server Pause Ack, which contains the families it needs on this + * connection. The server will send a Migration Notice with an IP address, and + * then disconnect. Next the client should open the connection and send the + * cookie. Repeat the normal login process and pretend this never happened. + * + * The Server Pause contains no data. + * + */ + +/* Service Pause (group 1, type 0x0b) */ +static int serverpause(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_rxcallback_t userfunc; + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + return userfunc(sess, rx); + + return 0; +} + +/* + * Service Pause Acknowledgement (group 1, type 0x0c) + * + * It is rather important that aim_sendpauseack() gets called for the exact + * same connection that the Server Pause callback was called for, since + * libfaim extracts the data for the SNAC from the connection structure. + * + * Of course, if you don't do that, more bad things happen than just what + * libfaim can cause. + * + */ +faim_export int aim_sendpauseack(aim_session_t *sess, aim_conn_t *conn) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside; + struct snacgroup *sg; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1024))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0001, 0x000c, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0001, 0x000c, 0x0000, snacid); + + /* + * This list should have all the groups that the original + * Host Online / Server Ready said this host supports. And + * we want them all back after the migration. + */ + for (sg = ins->groups; sg; sg = sg->next) + aimbs_put16(&fr->data, sg->group); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* Service Resume (group 1, type 0x0d) */ +static int serverresume(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_rxcallback_t userfunc; + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + return userfunc(sess, rx); + + return 0; +} + +/* Request self-info (group 1, type 0x0e) */ +faim_export int aim_reqpersonalinfo(aim_session_t *sess, aim_conn_t *conn) +{ + return aim_genericreq_n(sess, conn, 0x0001, 0x000e); +} + +/* Self User Info (group 1, type 0x0f) */ +static int selfinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_rxcallback_t userfunc; + aim_userinfo_t userinfo; + + aim_extractuserinfo(sess, bs, &userinfo); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + return userfunc(sess, rx, &userinfo); + + return 0; +} + +/* Evil Notification (group 1, type 0x10) */ +static int evilnotify(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_rxcallback_t userfunc; + fu16_t newevil; + aim_userinfo_t userinfo; + + memset(&userinfo, 0, sizeof(aim_userinfo_t)); + + newevil = aimbs_get16(bs); + + if (aim_bstream_empty(bs)) + aim_extractuserinfo(sess, bs, &userinfo); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + return userfunc(sess, rx, newevil, &userinfo); + + return 0; +} + +/* + * Idle Notification (group 1, type 0x11) + * + * Should set your current idle time in seconds. Note that this should + * never be called consecutively with a non-zero idle time. That makes + * OSCAR do funny things. Instead, just set it once you go idle, and then + * call it again with zero when you're back. + * + */ +faim_export int aim_bos_setidle(aim_session_t *sess, aim_conn_t *conn, fu32_t idletime) +{ + return aim_genericreq_l(sess, conn, 0x0001, 0x0011, &idletime); +} + +/* + * Service Migrate (group 1, type 0x12) + * + * This is the final SNAC sent on the original connection during a migration. + * It contains the IP and cookie used to connect to the new server, and + * optionally a list of the SNAC groups being migrated. + * + */ +static int migrate(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_rxcallback_t userfunc; + int ret = 0; + fu16_t groupcount, i; + aim_tlvlist_t *tl; + char *ip = NULL; + aim_tlv_t *cktlv; + + /* + * Apparently there's some fun stuff that can happen right here. The + * migration can actually be quite selective about what groups it + * moves to the new server. When not all the groups for a connection + * are migrated, or they are all migrated but some groups are moved + * to a different server than others, it is called a bifurcated + * migration. + * + * Let's play dumb and not support that. + * + */ + groupcount = aimbs_get16(bs); + for (i = 0; i < groupcount; i++) { + fu16_t group; + + group = aimbs_get16(bs); + + faimdprintf(sess, 0, "bifurcated migration unsupported -- group 0x%04x\n", group); + } + + tl = aim_readtlvchain(bs); + + if (aim_gettlv(tl, 0x0005, 1)) + ip = aim_gettlv_str(tl, 0x0005, 1); + + cktlv = aim_gettlv(tl, 0x0006, 1); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, ip, cktlv ? cktlv->value : NULL); + + aim_freetlvchain(&tl); + free(ip); + + return ret; +} + +/* Message of the Day (group 1, type 0x13) */ +static int motd(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_rxcallback_t userfunc; + char *msg = NULL; + int ret = 0; + aim_tlvlist_t *tlvlist; + fu16_t id; + + /* + * Code. + * + * Valid values: + * 1 Mandatory upgrade + * 2 Advisory upgrade + * 3 System bulletin + * 4 Nothing's wrong ("top o the world" -- normal) + * 5 Lets-break-something. + * + */ + id = aimbs_get16(bs); + + /* + * TLVs follow + */ + tlvlist = aim_readtlvchain(bs); + + msg = aim_gettlv_str(tlvlist, 0x000b, 1); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, id, msg); + + free(msg); + + aim_freetlvchain(&tlvlist); + + return ret; +} + +/* + * Set privacy flags (group 1, type 0x14) + * + * Normally 0x03. + * + * Bit 1: Allows other AIM users to see how long you've been idle. + * Bit 2: Allows other AIM users to see how long you've been a member. + * + */ +faim_export int aim_bos_setprivacyflags(aim_session_t *sess, aim_conn_t *conn, fu32_t flags) +{ + return aim_genericreq_l(sess, conn, 0x0001, 0x0014, &flags); +} + +/* + * No-op (group 1, type 0x16) + * + * WinAIM sends these every 4min or so to keep the connection alive. Its not + * real necessary. + * + */ +faim_export int aim_nop(aim_session_t *sess, aim_conn_t *conn) +{ + return aim_genericreq_n(sess, conn, 0x0001, 0x0016); +} + +/* + * Set client versions (group 1, subtype 0x17) + * + * If you've seen the clientonline/clientready SNAC you're probably + * wondering what the point of this one is. And that point seems to be + * that the versions in the client online SNAC are sent too late for the + * server to be able to use them to change the protocol for the earlier + * login packets (client versions are sent right after Host Online is + * received, but client online versions aren't sent until quite a bit later). + * We can see them already making use of this by changing the format of + * the rate information based on what version of group 1 we advertise here. + * + */ +faim_internal int aim_setversions(aim_session_t *sess, aim_conn_t *conn) +{ + aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside; + struct snacgroup *sg; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!ins) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0001, 0x0017, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0001, 0x0017, 0x0000, snacid); + + /* + * Send only the versions that the server cares about (that it + * marked as supporting in the server ready SNAC). + */ + for (sg = ins->groups; sg; sg = sg->next) { + aim_module_t *mod; + + if ((mod = aim__findmodulebygroup(sess, sg->group))) { + aimbs_put16(&fr->data, mod->family); + aimbs_put16(&fr->data, mod->version); + } else + faimdprintf(sess, 1, "aim_setversions: server supports group 0x%04x but we don't!\n", sg->group); + } + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* Host versions (group 1, subtype 0x18) */ +static int hostversions(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int vercount; + fu8_t *versions; + + /* This is frivolous. (Thank you SmarterChild.) */ + vercount = aim_bstream_empty(bs)/4; + versions = aimbs_getraw(bs, aim_bstream_empty(bs)); + free(versions); + + /* + * Now request rates. + */ + aim_reqrates(sess, rx->conn); + + return 1; +} + +/* + * Set Extended Status (group 1, type 0x1e) + * + * Currently only works if using ICQ. + * + */ +faim_export int aim_setextstatus(aim_session_t *sess, aim_conn_t *conn, fu16_t status) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + fu32_t data; + + data = 0x00030000 | status; /* yay for error checking ;^) */ + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 8))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0001, 0x001e, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0001, 0x001e, 0x0000, snacid); + + aim_addtlvtochain32(&tl, 0x0006, data); + aim_writetlvchain(&fr->data, &tl); + aim_freetlvchain(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Starting this past week (26 Mar 2001, say), AOL has started sending + * this nice little extra SNAC. AFAIK, it has never been used until now. + * + * The request contains eight bytes. The first four are an offset, the + * second four are a length. + * + * The offset is an offset into aim.exe when it is mapped during execution + * on Win32. So far, AOL has only been requesting bytes in static regions + * of memory. (I won't put it past them to start requesting data in + * less static regions -- regions that are initialized at run time, but still + * before the client recieves this request.) + * + * When the client recieves the request, it adds it to the current ds + * (0x00400000) and dereferences it, copying the data into a buffer which + * it then runs directly through the MD5 hasher. The 16 byte output of + * the hash is then sent back to the server. + * + * If the client does not send any data back, or the data does not match + * the data that the specific client should have, the client will get the + * following message from "AOL Instant Messenger": + * "You have been disconnected from the AOL Instant Message Service (SM) + * for accessing the AOL network using unauthorized software. You can + * download a FREE, fully featured, and authorized client, here + * http://www.aol.com/aim/download2.html" + * The connection is then closed, recieving disconnect code 1, URL + * http://www.aim.aol.com/errors/USER_LOGGED_OFF_NEW_LOGIN.html. + * + * Note, however, that numerous inconsistencies can cause the above error, + * not just sending back a bad hash. Do not immediatly suspect this code + * if you get disconnected. AOL and the open/free software community have + * played this game for a couple years now, generating the above message + * on numerous ocassions. + * + * Anyway, neener. We win again. + * + */ +/* Client verification (group 1, subtype 0x1f) */ +static int memrequest(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_rxcallback_t userfunc; + fu32_t offset, len; + aim_tlvlist_t *list; + char *modname; + + offset = aimbs_get32(bs); + len = aimbs_get32(bs); + list = aim_readtlvchain(bs); + + modname = aim_gettlv_str(list, 0x0001, 1); + + faimdprintf(sess, 1, "data at 0x%08lx (%d bytes) of requested\n", offset, len, modname ? modname : "aim.exe"); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + return userfunc(sess, rx, offset, len, modname); + + free(modname); + aim_freetlvchain(&list); + + return 0; +} + +#if 0 +static void dumpbox(aim_session_t *sess, unsigned char *buf, int len) +{ + int i; + + if (!sess || !buf || !len) + return; + + faimdprintf(sess, 1, "\nDump of %d bytes at %p:", len, buf); + + for (i = 0; i < len; i++) { + if ((i % 8) == 0) + faimdprintf(sess, 1, "\n\t"); + + faimdprintf(sess, 1, "0x%2x ", buf[i]); + } + + faimdprintf(sess, 1, "\n\n"); + + return; +} +#endif + +/* Client verification reply (group 1, subtype 0x20) */ +faim_export int aim_sendmemblock(aim_session_t *sess, aim_conn_t *conn, fu32_t offset, fu32_t len, const fu8_t *buf, fu8_t flag) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !conn) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+16))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0001, 0x0020, 0x0000, NULL, 0); + + aim_putsnac(&fr->data, 0x0001, 0x0020, 0x0000, snacid); + aimbs_put16(&fr->data, 0x0010); /* md5 is always 16 bytes */ + + if ((flag == AIM_SENDMEMBLOCK_FLAG_ISHASH) && buf && (len == 0x10)) { /* we're getting a hash */ + + aimbs_putraw(&fr->data, buf, 0x10); + + } else if (buf && (len > 0)) { /* use input buffer */ + md5_state_t state; + md5_byte_t digest[0x10]; + + md5_init(&state); + md5_append(&state, (const md5_byte_t *)buf, len); + md5_finish(&state, digest); + + aimbs_putraw(&fr->data, (fu8_t *)digest, 0x10); + + } else if (len == 0) { /* no length, just hash NULL (buf is optional) */ + md5_state_t state; + fu8_t nil = '\0'; + md5_byte_t digest[0x10]; + + /* + * These MD5 routines are stupid in that you have to have + * at least one append. So thats why this doesn't look + * real logical. + */ + md5_init(&state); + md5_append(&state, (const md5_byte_t *)&nil, 0); + md5_finish(&state, digest); + + aimbs_putraw(&fr->data, (fu8_t *)digest, 0x10); + + } else { + + /* + * This data is correct for AIM 3.5.1670. + * + * Using these blocks is as close to "legal" as you can get + * without using an AIM binary. + * + */ + if ((offset == 0x03ffffff) && (len == 0x03ffffff)) { + +#if 1 /* with "AnrbnrAqhfzcd" */ + aimbs_put32(&fr->data, 0x44a95d26); + aimbs_put32(&fr->data, 0xd2490423); + aimbs_put32(&fr->data, 0x93b8821f); + aimbs_put32(&fr->data, 0x51c54b01); +#else /* no filename */ + aimbs_put32(&fr->data, 0x1df8cbae); + aimbs_put32(&fr->data, 0x5523b839); + aimbs_put32(&fr->data, 0xa0e10db3); + aimbs_put32(&fr->data, 0xa46d3b39); +#endif + + } else if ((offset == 0x00001000) && (len == 0x00000000)) { + + aimbs_put32(&fr->data, 0xd41d8cd9); + aimbs_put32(&fr->data, 0x8f00b204); + aimbs_put32(&fr->data, 0xe9800998); + aimbs_put32(&fr->data, 0xecf8427e); + + } else + faimdprintf(sess, 0, "sendmemblock: WARNING: unknown hash request\n"); + + } + + aim_tx_enqueue(sess, fr); + + return 0; +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if (snac->subtype == 0x0003) + return hostonline(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0005) + return redirect(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0007) + return rateresp(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x000a) + return ratechange(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x000b) + return serverpause(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x000d) + return serverresume(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x000f) + return selfinfo(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0010) + return evilnotify(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0012) + return migrate(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0013) + return motd(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0018) + return hostversions(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x001f) + return memrequest(sess, mod, rx, snac, bs); + + return 0; +} + +faim_internal int general_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x0001; + mod->version = 0x0003; + mod->toolid = 0x0110; + mod->toolversion = 0x047b; + mod->flags = 0; + strncpy(mod->name, "general", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} +