From: Eric W. <war...@us...> - 2001-11-10 01:48:20
|
Update of /cvsroot/gaim/gaim/src/protocols/oscar In directory usw-pr-cvs1:/tmp/cvs-serv18730/protocols/oscar Modified Files: .cvsignore Makefile.am admin.c aim.h aim_internal.h auth.c bos.c conn.c im.c misc.c oscar.c Added Files: icq.c service.c Removed Files: login.c Log Message: it's not what you think --- NEW FILE: icq.c --- /* * Encapsulated ICQ. * */ #define FAIM_INTERNAL #include <aim.h> /* * 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; } --- NEW FILE: service.c --- /* * 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 <aim.h> #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; } Index: .cvsignore =================================================================== RCS file: /cvsroot/gaim/gaim/src/protocols/oscar/.cvsignore,v retrieving revision 1.2 retrieving revision 1.3 diff -u -d -r1.2 -r1.3 --- .cvsignore 2001/11/05 20:51:12 1.2 +++ .cvsignore 2001/11/10 01:48:16 1.3 @@ -32,3 +32,5 @@ popups.lo ssi.lo translate.lo +icq.lo +service.lo Index: Makefile.am =================================================================== RCS file: /cvsroot/gaim/gaim/src/protocols/oscar/Makefile.am,v retrieving revision 1.5 retrieving revision 1.6 diff -u -d -r1.5 -r1.6 --- Makefile.am 2001/11/05 02:05:06 1.5 +++ Makefile.am 2001/11/10 01:48:17 1.6 @@ -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 \ Index: admin.c =================================================================== RCS file: /cvsroot/gaim/gaim/src/protocols/oscar/admin.c,v retrieving revision 1.3 retrieving revision 1.4 diff -u -d -r1.3 -r1.4 --- admin.c 2001/11/05 02:05:06 1.3 +++ admin.c 2001/11/10 01:48:17 1.4 @@ -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; } + Index: aim.h =================================================================== RCS file: /cvsroot/gaim/gaim/src/protocols/oscar/aim.h,v retrieving revision 1.15 retrieving revision 1.16 diff -u -d -r1.15 -r1.16 --- aim.h 2001/11/05 12:25:22 1.15 +++ aim.h 2001/11/10 01:48:17 1.16 @@ -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 *); Index: aim_internal.h =================================================================== RCS file: /cvsroot/gaim/gaim/src/protocols/oscar/aim_internal.h,v retrieving revision 1.6 retrieving revision 1.7 diff -u -d -r1.6 -r1.7 --- aim_internal.h 2001/11/05 12:25:22 1.6 +++ aim_internal.h 2001/11/10 01:48:17 1.7 @@ -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 Index: auth.c =================================================================== RCS file: /cvsroot/gaim/gaim/src/protocols/oscar/auth.c,v retrieving revision 1.2 retrieving revision 1.3 diff -u -d -r1.2 -r1.3 --- auth.c 2001/09/09 10:07:13 1.2 +++ auth.c 2001/11/10 01:48:17 1.3 @@ -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 <aim.h> -/* 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; @@ -23,6 +32,328 @@ aim_freetlvchain(&tl); aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * 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; } Index: bos.c =================================================================== RCS file: /cvsroot/gaim/gaim/src/protocols/oscar/bos.c,v retrieving revision 1.3 retrieving revision 1.4 diff -u -d -r1.3 -r1.4 --- bos.c 2001/11/05 02:05:06 1.3 +++ bos.c 2001/11/10 01:48:17 1.4 @@ -2,21 +2,13 @@ #define FAIM_INTERNAL #include <aim.h> -/* - * 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; @@ -47,6 +39,101 @@ aim_freetlvchain(&tlvlist); 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) Index: conn.c =================================================================== RCS file: /cvsroot/gaim/gaim/src/protocols/oscar/conn.c,v retrieving revision 1.6 retrieving revision 1.7 diff -u -d -r1.6 -r1.7 --- conn.c 2001/11/05 12:25:22 1.6 +++ conn.c 2001/11/10 01:48:17 1.7 @@ -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; @@ -1042,4 +1044,23 @@ return 0; } + +/* + * 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; +} + Index: im.c =================================================================== RCS file: /cvsroot/gaim/gaim/src/protocols/oscar/im.c,v retrieving revision 1.14 retrieving revision 1.15 diff -u -d -r1.14 -r1.15 --- im.c 2001/11/05 08:25:10 1.14 +++ im.c 2001/11/10 01:48:17 1.15 @@ -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... */ @@ -860,6 +867,11 @@ } else if (type == 0x0004) { /* Message is Auto Response */ 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 */ Index: misc.c =================================================================== RCS file: /cvsroot/gaim/gaim/src/protocols/oscar/misc.c,v retrieving revision 1.8 retrieving revision 1.9 diff -u -d -r1.8 -r1.9 --- misc.c 2001/11/05 12:25:22 1.8 +++ misc.c 2001/11/10 01:48:17 1.9 @@ -15,102 +15,6 @@ #include <aim.h> /* - * 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_e... [truncated message content] |