# HG changeset patch # User Mark Doliner # Date 1139694318 0 # Node ID f2431a7e33aa824c14e6588ec4c0c81d8d578fc4 # Parent f09c6e8df82c34e235554de9d48b0ecc6749a2af [gaim-migrate @ 15600] Massive oscar shuffling. No change in functionality. I renamed each of the files that contains stuff for a SNAC family. I started splitting the file transfer/direct connect stuff into peer.c and peer.h. I stopped using fu8_t, fu16_t and fu32_t and switched to guint8, guint16 and guint32 instead. I changed the SNAC family and subtype defines so they are more meaningful. Added LGPL copyright header to each file. Added myself to the AUTHORS file. committer: Tailor Script diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/AUTHORS --- a/src/protocols/oscar/AUTHORS Sat Feb 11 19:16:38 2006 +0000 +++ b/src/protocols/oscar/AUTHORS Sat Feb 11 21:45:18 2006 +0000 @@ -3,29 +3,40 @@ --- +N: Mark Doliner +T: 2001-2006 +H: markdoliner +E: mark a.t kingant d.o.t net +W: http://kingant.net/ + N: Adam Fritzler +T: 1998-2001 H: mid -E: mid@auk.cx +E: mid a.t auk d.o.t cx W: http://www.auk.cx/~mid,http://www.auk.cx/faim D: Wrote most of the wap of crap that you see before you. N: Josh Myer -E: josh@joshisanerd.com +T: 1998-2001 +E: josh a.t joshisanerd d.o.t com D: OFT/ODC (not quite finished yet..), random little things, Munger-At-Large, compile-time warnings. N: Daniel Reed +T: 1998-2001 H: n, linuxkitty -E: n@ml.org +E: n a.t ml d.o.t org W: http://users.n.ml.org/n/ D: Fixed aim_snac.c N: Eric Warmenhoven -E: warmenhoven@linux.com +T: 1998-2001 +E: warmenhoven a.t linux d.o.t com D: Some OFT info, author of the faim interface for gaim N: Brock Wilcox +T: 1998-2001 H: awwaiid -E: awwaiid@auk.cx +E: awwaiid a.t auk d.o.t cx D: Figured out original password roasting diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/Makefile.am --- a/src/protocols/oscar/Makefile.am Sat Feb 11 19:16:38 2006 +0000 +++ b/src/protocols/oscar/Makefile.am Sat Feb 11 21:45:18 2006 +0000 @@ -6,41 +6,42 @@ pkgdir = $(libdir)/gaim OSCARSOURCES = \ - admin.c \ - adverts.c \ - aim.h \ - aim_cbtypes.h \ - aim_internal.h \ - auth.c \ - bart.c \ - bos.c \ - bstream.c \ - buddylist.c \ - chat.c \ - chatnav.c \ - conn.c \ - email.c \ - ft.c \ - icq.c \ - im.c \ - invite.c \ - locate.c \ + bstream.c \ + conn.c \ + family_admin.c \ + family_advert.c \ + family_alert.c \ + family_auth.c \ + family_bart.c \ + family_bos.c \ + family_buddy.c \ + family_chat.c \ + family_chatnav.c \ + family_icq.c \ + family_icbm.c \ + family_invite.c \ + family_locate.c \ + family_odir.c \ + family_oservice.c \ + family_popup.c \ + family_feedbag.c \ + family_stats.c \ + family_translate.c \ + family_userlookup.c \ misc.c \ - msgcookie.c \ - odir.c \ - popups.c \ - rxhandlers.c \ - rxqueue.c \ - search.c \ - service.c \ - snac.c \ - ssi.c \ - stats.c \ - tlv.c \ - translate.c \ - txqueue.c \ - util.c \ - oscar.c + msgcookie.c \ + oscar.c \ + oscar.h \ + oscar_internal.h \ + peer.c \ + peer.h \ + rxhandlers.c \ + rxqueue.c \ + snac.c \ + snactypes.h \ + tlv.c \ + txqueue.c \ + util.c AM_CFLAGS = $(st) diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/Makefile.mingw --- a/src/protocols/oscar/Makefile.mingw Sat Feb 11 19:16:38 2006 +0000 +++ b/src/protocols/oscar/Makefile.mingw Sat Feb 11 21:45:18 2006 +0000 @@ -67,38 +67,39 @@ ## SOURCES, OBJECTS ## -C_SRC = admin.c \ - adverts.c \ - auth.c \ - bart.c \ - bos.c \ - bstream.c \ - buddylist.c \ - chat.c \ - chatnav.c \ - conn.c \ - email.c \ - ft.c \ - icq.c \ - im.c \ - invite.c \ - locate.c \ - misc.c \ - msgcookie.c \ - odir.c \ - popups.c \ - rxhandlers.c \ - rxqueue.c \ - search.c \ - service.c \ - snac.c \ - ssi.c \ - stats.c \ - tlv.c \ - translate.c \ - txqueue.c \ - util.c \ - oscar.c +C_SRC = \ + bstream.c \ + conn.c \ + family_admin.c \ + family_advert.c \ + family_alert.c \ + family_auth.c \ + family_bart.c \ + family_bos.c \ + family_buddylist.c \ + family_chat.c \ + family_chatnav.c \ + family_icq.c \ + family_icbm.c \ + family_invite.c \ + family_locate.c \ + family_odir.c \ + family_popup.c \ + family_oservice.c \ + family_feedbag.c \ + family_stats.c \ + family_translate.c \ + family_userlookup.c \ + misc.c \ + msgcookie.c \ + oscar.c \ + peer.c \ + rxhandlers.c \ + rxqueue.c \ + snac.c \ + tlv.c \ + txqueue.c \ + util.c OBJECTS = $(C_SRC:%.c=%.o) diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/admin.c --- a/src/protocols/oscar/admin.c Sat Feb 11 19:16:38 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,238 +0,0 @@ -/* - * Family 0x0007 - Account Administration. - * - * Used for stuff like changing the formating of your screen name, changing your - * email address, requesting an account confirmation email, getting account info, - * - */ - -#define FAIM_INTERNAL -#include - -/* - * Subtype 0x0002 - Request a bit of account info. - * - * Info should be one of the following: - * 0x0001 - Screen name formatting - * 0x0011 - Email address - * 0x0013 - Unknown - * - */ -faim_export int aim_admin_getinfo(aim_session_t *sess, aim_conn_t *conn, fu16_t info) -{ - aim_frame_t *fr; - aim_snacid_t snacid; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 14))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0007, 0x0002, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0007, 0x0002, 0x0000, snacid); - - aimbs_put16(&fr->data, info); - aimbs_put16(&fr->data, 0x0000); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * Subtypes 0x0003 and 0x0005 - Parse account info. - * - * Called in reply to both an information request (subtype 0x0002) and - * an information change (subtype 0x0004). - * - */ -static int infochange(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 *url=NULL, *sn=NULL, *email=NULL; - fu16_t perms, tlvcount, err=0; - - perms = aimbs_get16(bs); - tlvcount = aimbs_get16(bs); - - while (tlvcount && aim_bstream_empty(bs)) { - fu16_t type, length; - - type = aimbs_get16(bs); - length = aimbs_get16(bs); - - switch (type) { - case 0x0001: { - sn = aimbs_getstr(bs, length); - } break; - - case 0x0004: { - url = aimbs_getstr(bs, length); - } break; - - case 0x0008: { - err = aimbs_get16(bs); - } break; - - case 0x0011: { - if (length == 0) { - email = (char*)malloc(13*sizeof(char)); - strcpy(email, "*suppressed*"); - } else - email = aimbs_getstr(bs, length); - } break; - } - - tlvcount--; - } - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - userfunc(sess, rx, (snac->subtype == 0x0005) ? 1 : 0, perms, err, url, sn, email); - - if (sn) free(sn); - if (url) free(url); - if (email) free(email); - - return 1; -} - -/* - * Subtype 0x0004 - Set screenname formatting. - * - */ -faim_export int aim_admin_setnick(aim_session_t *sess, aim_conn_t *conn, const char *newnick) -{ - aim_frame_t *fr; - aim_snacid_t snacid; - aim_tlvlist_t *tl = NULL; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+2+strlen(newnick)))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0007, 0x0004, 0x0000, snacid); - - aim_tlvlist_add_str(&tl, 0x0001, newnick); - - aim_tlvlist_write(&fr->data, &tl); - aim_tlvlist_free(&tl); - - aim_tx_enqueue(sess, fr); - - - return 0; -} - -/* - * Subtype 0x0004 - Change password. - * - */ -faim_export int aim_admin_changepasswd(aim_session_t *sess, aim_conn_t *conn, const char *newpw, const char *curpw) -{ - aim_frame_t *fr; - aim_tlvlist_t *tl = NULL; - aim_snacid_t snacid; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4+strlen(curpw)+4+strlen(newpw)))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0007, 0x0004, 0x0000, snacid); - - /* new password TLV t(0002) */ - aim_tlvlist_add_str(&tl, 0x0002, newpw); - - /* current password TLV t(0012) */ - aim_tlvlist_add_str(&tl, 0x0012, curpw); - - aim_tlvlist_write(&fr->data, &tl); - aim_tlvlist_free(&tl); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * Subtype 0x0004 - Change email address. - * - */ -faim_export int aim_admin_setemail(aim_session_t *sess, aim_conn_t *conn, const char *newemail) -{ - aim_frame_t *fr; - aim_snacid_t snacid; - aim_tlvlist_t *tl = NULL; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+2+strlen(newemail)))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0007, 0x0004, 0x0000, snacid); - - aim_tlvlist_add_str(&tl, 0x0011, newemail); - - aim_tlvlist_write(&fr->data, &tl); - aim_tlvlist_free(&tl); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * Subtype 0x0006 - Request account confirmation. - * - * This will cause an email to be sent to the address associated with - * the account. By following the instructions in the mail, you can - * get the TRIAL flag removed from your account. - * - */ -faim_export int aim_admin_reqconfirm(aim_session_t *sess, aim_conn_t *conn) -{ - return aim_genericreq_n(sess, conn, 0x0007, 0x0006); -} - -/* - * Subtype 0x0007 - Account confirmation request acknowledgement. - * - */ -static int accountconfirm(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; - fu16_t status; - aim_tlvlist_t *tl; - - status = aimbs_get16(bs); - /* This is 0x0013 if unable to confirm at this time */ - - tl = aim_tlvlist_read(bs); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, status); - - 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) || (snac->subtype == 0x0005)) - return infochange(sess, mod, rx, snac, bs); - else if (snac->subtype == 0x0007) - return accountconfirm(sess, mod, rx, snac, bs); - - return 0; -} - -faim_internal int admin_modfirst(aim_session_t *sess, aim_module_t *mod) -{ - - mod->family = 0x0007; - mod->version = 0x0001; - mod->toolid = 0x0010; - mod->toolversion = 0x0629; - mod->flags = 0; - strncpy(mod->name, "admin", sizeof(mod->name)); - mod->snachandler = snachandler; - - return 0; -} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/adverts.c --- a/src/protocols/oscar/adverts.c Sat Feb 11 19:16:38 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ -/* - * Family 0x0005 - Advertisements. - * - */ - -#define FAIM_INTERNAL -#include - -faim_export int aim_ads_requestads(aim_session_t *sess, aim_conn_t *conn) -{ - return aim_genericreq_n(sess, conn, 0x0005, 0x0002); -} - -static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) -{ - return 0; -} - -faim_internal int adverts_modfirst(aim_session_t *sess, aim_module_t *mod) -{ - - mod->family = 0x0005; - mod->version = 0x0001; - mod->toolid = 0x0001; - mod->toolversion = 0x0001; - mod->flags = 0; - strncpy(mod->name, "adverts", sizeof(mod->name)); - mod->snachandler = snachandler; - - return 0; -} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/aim.h --- a/src/protocols/oscar/aim.h Sat Feb 11 19:16:38 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1582 +0,0 @@ -/* - * Main libfaim header. Must be included in client for prototypes/macros. - * - * "come on, i turned a chick lesbian; i think this is the hackish equivalent" - * -- Josh Myer - * - */ - -#ifndef __AIM_H__ -#define __AIM_H__ - -#include "aim_cbtypes.h" - -#include "debug.h" -#include "internal.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef _WIN32 -#include -#include -#include -#include -#include -#else -#include "libc_interface.h" -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/* TODO: Remove these three typedefs and use guints everywhere. */ -typedef guint8 fu8_t; -typedef guint16 fu16_t; -typedef guint32 fu32_t; - -typedef guint32 aim_snacid_t; -typedef guint16 flap_seqnum_t; - -#define WIN32_STATIC -#if defined(_WIN32) && !defined(WIN32_STATIC) -/* - * For a win32 DLL, we define WIN32_INDLL if this file - * is included while compiling the DLL. If it's not - * defined (it's included in a client app), the symbols - * will be imported instead of exported. - */ -#ifdef WIN32_INDLL -#define faim_export __declspec(dllexport) -#else -#define faim_export __declspec(dllimport) -#endif /* WIN32_INDLL */ -#define faim_internal -#else -/* - * Nothing normally needed for unix... - */ -#define faim_export -#define faim_internal -#endif - -#ifndef FALSE -#define FALSE (0) -#endif - -#ifndef TRUE -#define TRUE (!FALSE) -#endif - -#define FAIM_SNAC_HASH_SIZE 16 - -/* - * Current Maximum Length for Screen Names (not including NULL) - * - * Currently only names up to 16 characters can be registered - * however it is apparently legal for them to be larger. - */ -#define MAXSNLEN 97 - -/* - * Current Maximum Length for Instant Messages - * - * This was found basically by experiment, but not wholly - * accurate experiment. It should not be regarded - * as completely correct. But its a decent approximation. - * - * Note that although we can send this much, its impossible - * for WinAIM clients (up through the latest (4.0.1957)) to - * send any more than 1kb. Amaze all your windows friends - * with utterly oversized instant messages! - * - * XXX: the real limit is the total SNAC size at 8192. Fix this. - * - */ -#define MAXMSGLEN 7987 - -/* - * Maximum size of a Buddy Icon. - */ -#define MAXICONLEN 7168 -#define AIM_ICONIDENT "AVT1picture.id" - -/* - * Current Maximum Length for Chat Room Messages - * - * This is actually defined by the protocol to be - * dynamic, but I have yet to see due cause to - * define it dynamically here. Maybe later. - * - */ -#define MAXCHATMSGLEN 512 - -/** - * Maximum length for the password of an ICQ account - */ -#define MAXICQPASSLEN 8 - -#define AIM_MD5_STRING "AOL Instant Messenger (SM)" - -/* - * Client info. Filled in by the client and passed in to - * aim_send_login(). The information ends up getting passed to OSCAR - * through the initial login command. - * - */ -struct client_info_s { - const char *clientstring; - fu16_t clientid; - fu16_t major; - fu16_t minor; - fu16_t point; - fu16_t build; - fu32_t distrib; - const char *country; /* two-letter abbrev */ - const char *lang; /* two-letter abbrev */ -}; - -/* Needs to be checked */ -#define CLIENTINFO_AIM_3_5_1670 { \ - "AOL Instant Messenger (SM), version 3.5.1670/WIN32", \ - 0x0004, \ - 0x0003, 0x0005, \ - 0x0000, 0x0686, \ - 0x0000002a, \ - "us", "en", \ -} - -/* Needs to be checked */ -/* Latest winaim without ssi */ -#define CLIENTINFO_AIM_4_1_2010 { \ - "AOL Instant Messenger (SM), version 4.1.2010/WIN32", \ - 0x0004, \ - 0x0004, 0x0001, \ - 0x0000, 0x07da, \ - 0x0000004b, \ - "us", "en", \ -} - -/* Needs to be checked */ -#define CLIENTINFO_AIM_4_3_2188 { \ - "AOL Instant Messenger (SM), version 4.3.2188/WIN32", \ - 0x0109, \ - 0x0400, 0x0003, \ - 0x0000, 0x088c, \ - 0x00000086, \ - "us", "en", \ -} - -/* Needs to be checked */ -#define CLIENTINFO_AIM_4_8_2540 { \ - "AOL Instant Messenger (SM), version 4.8.2540/WIN32", \ - 0x0109, \ - 0x0004, 0x0008, \ - 0x0000, 0x09ec, \ - 0x000000af, \ - "us", "en", \ -} - -/* Needs to be checked */ -#define CLIENTINFO_AIM_5_0_2938 { \ - "AOL Instant Messenger, version 5.0.2938/WIN32", \ - 0x0109, \ - 0x0005, 0x0000, \ - 0x0000, 0x0b7a, \ - 0x00000000, \ - "us", "en", \ -} - -#define CLIENTINFO_AIM_5_1_3036 { \ - "AOL Instant Messenger, version 5.1.3036/WIN32", \ - 0x0109, \ - 0x0005, 0x0001, \ - 0x0000, 0x0bdc, \ - 0x000000d2, \ - "us", "en", \ -} - -#define CLIENTINFO_AIM_5_5_3415 { \ - "AOL Instant Messenger, version 5.5.3415/WIN32", \ - 0x0109, \ - 0x0005, 0x0005, \ - 0x0000, 0x0057, \ - 0x000000ef, \ - "us", "en", \ -} - -#define CLIENTINFO_AIM_5_9_3702 { \ - "AOL Instant Messenger, version 5.9.3702/WIN32", \ - 0x0109, \ - 0x0005, 0x0009, \ - 0x0000, 0x0e76, \ - 0x00000111, \ - "us", "en", \ -} - -#define CLIENTINFO_ICHAT_1_0 { \ - "Apple iChat", \ - 0x311a, \ - 0x0001, 0x0000, \ - 0x0000, 0x003c, \ - 0x000000c6, \ - "us", "en", \ -} - -/* Needs to be checked */ -#define CLIENTINFO_ICQ_4_65_3281 { \ - "ICQ Inc. - Product of ICQ (TM) 2000b.4.65.1.3281.85", \ - 0x010a, \ - 0x0004, 0x0041, \ - 0x0001, 0x0cd1, \ - 0x00000055, \ - "us", "en", \ -} - -/* Needs to be checked */ -#define CLIENTINFO_ICQ_5_34_3728 { \ - "ICQ Inc. - Product of ICQ (TM).2002a.5.34.1.3728.85", \ - 0x010a, \ - 0x0005, 0x0022, \ - 0x0001, 0x0e8f, \ - 0x00000055, \ - "us", "en", \ -} - -#define CLIENTINFO_ICQ_5_45_3777 { \ - "ICQ Inc. - Product of ICQ (TM).2003a.5.45.1.3777.85", \ - 0x010a, \ - 0x0005, 0x002d, \ - 0x0001, 0x0ec1, \ - 0x00000055, \ - "us", "en", \ -} - -#define CLIENTINFO_ICQBASIC_14_3_1068 { \ - "ICQBasic", \ - 0x010a, \ - 0x0014, 0x0003, \ - 0x0000, 0x042c, \ - 0x0000043d, \ - "us", "en", \ -} - -#define CLIENTINFO_NETSCAPE_7_0_1 { \ - "Netscape 2000 an approved user of AOL Instant Messenger (SM)", \ - 0x1d0d, \ - 0x0007, 0x0000, \ - 0x0001, 0x0000, \ - 0x00000058, \ - "us", "en", \ -} - -#define CLIENTINFO_GAIM { \ - "Gaim/" VERSION, \ - 0x0109, \ - 0x0005, 0x0001, \ - 0x0000, 0x0bdc, \ - 0x000000d2, \ - "us", "en", \ -} - -#define CLIENTINFO_AIM_KNOWNGOOD CLIENTINFO_AIM_5_1_3036 -#define CLIENTINFO_ICQ_KNOWNGOOD CLIENTINFO_ICQ_5_45_3777 - -/* - * These could be arbitrary, but its easier to use the actual AIM values - */ -#define AIM_CONN_TYPE_BOS 0x0002 -#define AIM_CONN_TYPE_ADS 0x0005 -#define AIM_CONN_TYPE_AUTH 0x0007 -#define AIM_CONN_TYPE_CHATNAV 0x000d -#define AIM_CONN_TYPE_CHAT 0x000e -#define AIM_CONN_TYPE_SEARCH 0x000f -#define AIM_CONN_TYPE_ICON 0x0010 -#define AIM_CONN_TYPE_EMAIL 0x0018 - -/* they start getting arbitrary for rendezvous stuff =) */ -#define AIM_CONN_TYPE_RENDEZVOUS_PROXY 0xfffd /* these speak a strange language */ -#define AIM_CONN_TYPE_RENDEZVOUS 0xfffe /* these do not speak FLAP! */ -#define AIM_CONN_TYPE_LISTENER 0xffff /* socket waiting for accept() */ - -/* Command types for doing a rendezvous proxy login - * Thanks to Keith Lea and the Joust project for documenting these commands well */ -#define AIM_RV_PROXY_PACKETVER_DFLT 0x044a -#define AIM_RV_PROXY_ERROR 0x0001 -#define AIM_RV_PROXY_INIT_SEND 0x0002 /* First command sent when creating a connection */ -#define AIM_RV_PROXY_INIT_RECV 0x0004 /* First command sent when receiving existing connection */ -#define AIM_RV_PROXY_ACK 0x0003 -#define AIM_RV_PROXY_READY 0x0005 - -/* Number of bytes expected in each of the above packets, including the 2 bytes specifying length */ -#define AIM_RV_PROXY_ERROR_LEN 14 -#define AIM_RV_PROXY_INIT_SEND_LEN 55 -#define AIM_RV_PROXY_INIT_RECV_LEN 57 -#define AIM_RV_PROXY_ACK_LEN 18 -#define AIM_RV_PROXY_READY_LEN 12 -#define AIM_RV_PROXY_HDR_LEN 12 /* Bytes in just the header alone */ - -/* Default values for unknown/unused values in rendezvous proxy negotiation packets */ -#define AIM_RV_PROXY_SERVER_FLAGS 0x0220 /* Default flags sent by proxy server */ -#define AIM_RV_PROXY_CLIENT_FLAGS 0x0000 /* Default flags sent by client */ -#define AIM_RV_PROXY_UNKNOWNA_DFLT 0x00000000 /* Default value for an unknown block */ -#define AIM_RV_PROXY_SERVER_URL "ars.oscar.aol.com" -#define AIM_RV_PROXY_CONNECT_PORT 5190 /* The port we should always connect to */ - -/* What is the purpose of this transfer? (Who will end up with a new file?) - * These values are used in oft_info->send_or_recv */ -#define AIM_XFER_SEND 0x0001 -#define AIM_XFER_RECV 0x0002 - -/* Via what method is the data getting routed? - * These values are used in oft_info->method */ -#define AIM_XFER_DIRECT 0x0001 /* Direct connection; receiver connects to sender */ -#define AIM_XFER_REDIR 0x0002 /* Redirected connection; sender connects to receiver */ -#define AIM_XFER_PROXY 0x0003 /* Proxied connection */ - -/* Who requested the proxy? - * The difference between a stage 2 and stage 3 proxied transfer is that the receiver does the - * initial login for a stage 2, but the sender must do it for a stage 3. - * These values are used in oft_info->stage */ -#define AIM_XFER_PROXY_NONE 0x0001 -#define AIM_XFER_PROXY_STG1 0x0002 /* Sender requested a proxy be used (stage1) */ -#define AIM_XFER_PROXY_STG2 0x0003 /* Receiver requested a proxy be used (stage2) */ -#define AIM_XFER_PROXY_STG3 0x0004 /* Receiver requested a proxy be used (stage3) */ - -/* - * Subtypes, we need these for OFT stuff. - */ -#define AIM_CONN_SUBTYPE_OFT_DIRECTIM 0x0001 -#define AIM_CONN_SUBTYPE_OFT_GETFILE 0x0002 -#define AIM_CONN_SUBTYPE_OFT_SENDFILE 0x0003 -#define AIM_CONN_SUBTYPE_OFT_BUDDYICON 0x0004 -#define AIM_CONN_SUBTYPE_OFT_VOICE 0x0005 - -/* - * Status values returned from aim_conn_new(). ORed together. - */ -#define AIM_CONN_STATUS_READY 0x0001 -#define AIM_CONN_STATUS_INTERNALERR 0x0002 -#define AIM_CONN_STATUS_RESOLVERR 0x0040 -#define AIM_CONN_STATUS_CONNERR 0x0080 -#define AIM_CONN_STATUS_INPROGRESS 0x0100 - -#define AIM_FRAMETYPE_FLAP 0x0000 -#define AIM_FRAMETYPE_OFT 0x0001 - -typedef struct aim_conn_s { - int fd; - fu16_t type; - fu16_t subtype; - flap_seqnum_t seqnum; - fu32_t status; - void *priv; /* misc data the client may want to store */ - void *internal; /* internal conn-specific libfaim data */ - time_t lastactivity; /* time of last transmit */ - int forcedlatency; - void *handlerlist; - void *sessv; /* pointer to parent session */ - void *inside; /* only accessible from inside libfaim */ - struct aim_conn_s *next; -} aim_conn_t; - -/* - * Byte Stream type. Sort of. - * - * Use of this type serves a couple purposes: - * - Buffer/buflen pairs are passed all around everywhere. This turns - * that into one value, as well as abstracting it slightly. - * - Through the abstraction, it is possible to enable bounds checking - * for robustness at the cost of performance. But a clean failure on - * weird packets is much better than a segfault. - * - I like having variables named "bs". - * - * Don't touch the insides of this struct. Or I'll have to kill you. - * - */ -typedef struct aim_bstream_s { - fu8_t *data; - fu32_t len; - fu32_t offset; -} aim_bstream_t; - -typedef struct aim_frame_s { - fu8_t hdrtype; /* defines which piece of the union to use */ - union { - struct { - fu8_t channel; - flap_seqnum_t seqnum; - } flap; - struct { - fu8_t magic[4]; /* ODC2 or OFT2 */ - fu16_t hdrlen; - fu16_t type; - } rend; - } hdr; - aim_bstream_t data; /* payload stream */ - aim_conn_t *conn; /* the connection it came in on/is going out on */ - fu8_t handled; /* 0 = new, !0 = been handled */ - struct aim_frame_s *next; -} aim_frame_t; - -typedef struct aim_msgcookie_s { - guchar cookie[8]; - int type; - void *data; - time_t addtime; - struct aim_msgcookie_s *next; -} aim_msgcookie_t; - -/* - * AIM Session: The main client-data interface. - * - */ -typedef struct aim_session_s { - - /* ---- Client Accessible ------------------------ */ - - /* Our screen name. */ - char sn[MAXSNLEN+1]; - - /* - * Pointer to anything the client wants to - * explicitly associate with this session. - * - * This is for use in the callbacks mainly. In any - * callback, you can access this with sess->aux_data. - * - */ - void *aux_data; - - /* ---- Internal Use Only ------------------------ */ - - /* Connection information */ - aim_conn_t *connlist; - - /* - * Transmit/receive queues. - * - * These are only used when you don't use your own lowlevel - * I/O. I don't suggest that you use libfaim's internal I/O. - * Its really bad and the API/event model is quirky at best. - * - */ - aim_frame_t *queue_outgoing; - aim_frame_t *queue_incoming; - - /* - * Tx Enqueuing function. - * - * This is how you override the transmit direction of libfaim's - * internal I/O. This function will be called whenever it needs - * to send something. - * - */ - int (*tx_enqueue)(struct aim_session_s *, aim_frame_t *); - - void *modlistv; - - struct { - char server[128]; - char username[128]; - char password[128]; - } socksproxy; - - fu8_t nonblocking; - - /* - * Outstanding snac handling - * - * XXX: Should these be per-connection? -mid - */ - void *snac_hash[FAIM_SNAC_HASH_SIZE]; - aim_snacid_t snacid_next; - - aim_msgcookie_t *msgcookies; - struct aim_icq_info *icq_info; - struct aim_oft_info *oft_info; - struct aim_authresp_info *authinfo; - struct aim_emailinfo *emailinfo; - - struct { - struct aim_userinfo_s *userinfo; - struct userinfo_node *torequest; - struct userinfo_node *requested; - int waiting_for_response; - } locate; - - /* Server-stored information (ssi) */ - struct { - int received_data; - fu16_t numitems; - struct aim_ssi_item *official; - struct aim_ssi_item *local; - struct aim_ssi_tmp *pending; - time_t timestamp; - int waiting_for_ack; - } ssi; -} aim_session_t; - -/* Valid for calling aim_icq_setstatus() and for aim_userinfo_t->icqinfo.status */ -#define AIM_ICQ_STATE_NORMAL 0x00000000 -#define AIM_ICQ_STATE_AWAY 0x00000001 -#define AIM_ICQ_STATE_DND 0x00000002 -#define AIM_ICQ_STATE_OUT 0x00000004 -#define AIM_ICQ_STATE_BUSY 0x00000010 -#define AIM_ICQ_STATE_CHAT 0x00000020 -#define AIM_ICQ_STATE_INVISIBLE 0x00000100 -#define AIM_ICQ_STATE_WEBAWARE 0x00010000 -#define AIM_ICQ_STATE_HIDEIP 0x00020000 -#define AIM_ICQ_STATE_BIRTHDAY 0x00080000 -#define AIM_ICQ_STATE_DIRECTDISABLED 0x00100000 -#define AIM_ICQ_STATE_ICQHOMEPAGE 0x00200000 -#define AIM_ICQ_STATE_DIRECTREQUIREAUTH 0x10000000 -#define AIM_ICQ_STATE_DIRECTCONTACTLIST 0x20000000 - -/* - * Get command from connections - * - * aim_get_commmand() is the libfaim lowlevel I/O in the receive direction. - * XXX Make this easily overridable. - * - */ -faim_export int aim_get_command(aim_session_t *, aim_conn_t *); - -/* - * Dispatch commands that are in the rx queue. - */ -faim_export void aim_rxdispatch(aim_session_t *); - -faim_export int aim_debugconn_sendconnect(aim_session_t *sess, aim_conn_t *conn); - -faim_export int aim_logoff(aim_session_t *); - -#if !defined(FAIM_INTERNAL) -/* the library should never call aim_conn_kill */ -faim_export void aim_conn_kill(aim_session_t *sess, aim_conn_t **deadconn); -#endif - -typedef int (*aim_rxcallback_t)(aim_session_t *, aim_frame_t *, ...); - - -/* auth.c */ -struct aim_clientrelease { - char *name; - fu32_t build; - char *url; - char *info; -}; - -struct aim_authresp_info { - char *sn; - fu16_t errorcode; - char *errorurl; - fu16_t regstatus; - char *email; - char *bosip; - fu16_t cookielen; - fu8_t *cookie; - char *chpassurl; - struct aim_clientrelease latestrelease; - struct aim_clientrelease latestbeta; -}; - -/* Callback data for redirect. */ -struct aim_redirect_data { - fu16_t group; - const char *ip; - fu16_t cookielen; - const fu8_t *cookie; - struct { /* group == AIM_CONN_TYPE_CHAT */ - fu16_t exchange; - const char *room; - fu16_t instance; - } chat; -}; - -faim_export int aim_clientready(aim_session_t *sess, aim_conn_t *conn); -faim_export int aim_sendflapver(aim_session_t *sess, aim_conn_t *conn); -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); -/* 0x000b */ faim_export int aim_auth_securid_send(aim_session_t *sess, const char *securid); - -faim_export void aim_purge_rxqueue(aim_session_t *); -faim_export void aim_cleansnacs(aim_session_t *, int maxage); - -#define AIM_TX_QUEUED 0 /* default */ -#define AIM_TX_IMMEDIATE 1 -#define AIM_TX_USER 2 -faim_export int aim_tx_setenqueue(aim_session_t *sess, int what, int (*func)(aim_session_t *, aim_frame_t *)); - -faim_export int aim_tx_flushqueue(aim_session_t *); -faim_export void aim_tx_purgequeue(aim_session_t *); - -faim_export int aim_conn_setlatency(aim_conn_t *conn, int newval); - -faim_export int aim_conn_addhandler(aim_session_t *, aim_conn_t *conn, fu16_t family, fu16_t type, aim_rxcallback_t newhandler, fu16_t flags); -faim_export int aim_clearhandlers(aim_conn_t *conn); - -faim_export aim_conn_t *aim_conn_findbygroup(aim_session_t *sess, fu16_t group); -faim_export aim_session_t *aim_conn_getsess(aim_conn_t *conn); -faim_export void aim_conn_close(aim_conn_t *deadconn); -faim_export aim_conn_t *aim_newconn(aim_session_t *, int type); -faim_export int aim_conn_in_sess(aim_session_t *sess, aim_conn_t *conn); -faim_export int aim_conn_isready(aim_conn_t *); -faim_export int aim_conn_setstatus(aim_conn_t *, int); -faim_export int aim_conn_completeconnect(aim_session_t *sess, aim_conn_t *conn); -faim_export int aim_conn_isconnecting(aim_conn_t *conn); - -faim_export void aim_session_init(aim_session_t *, fu8_t nonblocking); -faim_export void aim_session_kill(aim_session_t *); -faim_export aim_conn_t *aim_getconn_type(aim_session_t *, int type); -faim_export aim_conn_t *aim_getconn_type_all(aim_session_t *, int type); -faim_export aim_conn_t *aim_getconn_fd(aim_session_t *, int fd); - -/* 0x0001 - service.c */ -faim_export int aim_srv_setstatusmsg(aim_session_t *sess, const char *msg); -faim_export int aim_srv_setidle(aim_session_t *sess, fu32_t idletime); - -/* misc.c */ - -#define AIM_VISIBILITYCHANGE_PERMITADD 0x05 -#define AIM_VISIBILITYCHANGE_PERMITREMOVE 0x06 -#define AIM_VISIBILITYCHANGE_DENYADD 0x07 -#define AIM_VISIBILITYCHANGE_DENYREMOVE 0x08 - -#define AIM_PRIVFLAGS_ALLOWIDLE 0x01 -#define AIM_PRIVFLAGS_ALLOWMEMBERSINCE 0x02 - -#define AIM_WARN_ANON 0x01 - -faim_export int aim_sendpauseack(aim_session_t *sess, aim_conn_t *conn); -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_changevisibility(aim_session_t *, aim_conn_t *, int, const char *); -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_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_setextstatus(aim_session_t *sess, fu32_t status); - -#define AIM_CLIENTTYPE_UNKNOWN 0x0000 -#define AIM_CLIENTTYPE_MC 0x0001 -#define AIM_CLIENTTYPE_WINAIM 0x0002 -#define AIM_CLIENTTYPE_WINAIM41 0x0003 -#define AIM_CLIENTTYPE_AOL_TOC 0x0004 -faim_export fu16_t aim_im_fingerprint(const fu8_t *msghdr, int len); - -#define AIM_RATE_CODE_CHANGE 0x0001 -#define AIM_RATE_CODE_WARNING 0x0002 -#define AIM_RATE_CODE_LIMIT 0x0003 -#define AIM_RATE_CODE_CLEARLIMIT 0x0004 -faim_export int aim_ads_requestads(aim_session_t *sess, aim_conn_t *conn); - - - -/* im.c */ -#define AIM_OFT_SUBTYPE_SEND_FILE 0x0001 -#define AIM_OFT_SUBTYPE_SEND_DIR 0x0002 -#define AIM_OFT_SUBTYPE_GET_FILE 0x0011 -#define AIM_OPT_SUBTYPE_GET_LIST 0x0012 - -#define AIM_TRANSFER_DENY_NOTSUPPORTED 0x0000 -#define AIM_TRANSFER_DENY_DECLINE 0x0001 -#define AIM_TRANSFER_DENY_NOTACCEPTING 0x0002 - -#define AIM_IMPARAM_FLAG_CHANMSGS_ALLOWED 0x00000001 -#define AIM_IMPARAM_FLAG_MISSEDCALLS_ENABLED 0x00000002 - -/* This is what the server will give you if you don't set them yourself. */ -#define AIM_IMPARAM_DEFAULTS { \ - 0, \ - AIM_IMPARAM_FLAG_CHANMSGS_ALLOWED | AIM_IMPARAM_FLAG_MISSEDCALLS_ENABLED, \ - 512, /* !! Note how small this is. */ \ - (99.9)*10, (99.9)*10, \ - 1000 /* !! And how large this is. */ \ -} - -/* This is what most AIM versions use. */ -#define AIM_IMPARAM_REASONABLE { \ - 0, \ - AIM_IMPARAM_FLAG_CHANMSGS_ALLOWED | AIM_IMPARAM_FLAG_MISSEDCALLS_ENABLED, \ - 8000, \ - (99.9)*10, (99.9)*10, \ - 0 \ -} - -struct aim_icbmparameters { - fu16_t maxchan; - fu32_t flags; /* AIM_IMPARAM_FLAG_ */ - fu16_t maxmsglen; /* message size that you will accept */ - fu16_t maxsenderwarn; /* this and below are *10 (999=99.9%) */ - fu16_t maxrecverwarn; - fu32_t minmsginterval; /* in milliseconds? */ -}; - -struct aim_chat_roominfo { - fu16_t exchange; - char *name; - fu16_t instance; -}; - -#define AIM_IMFLAGS_AWAY 0x0001 /* mark as an autoreply */ -#define AIM_IMFLAGS_ACK 0x0002 /* request a receipt notice */ -#define AIM_IMFLAGS_BUDDYREQ 0x0010 /* buddy icon requested */ -#define AIM_IMFLAGS_HASICON 0x0020 /* already has icon */ -#define AIM_IMFLAGS_SUBENC_MACINTOSH 0x0040 /* damn that Steve Jobs! */ -#define AIM_IMFLAGS_CUSTOMFEATURES 0x0080 /* features field present */ -#define AIM_IMFLAGS_EXTDATA 0x0100 -#define AIM_IMFLAGS_X 0x0200 -#define AIM_IMFLAGS_MULTIPART 0x0400 /* ->mpmsg section valid */ -#define AIM_IMFLAGS_OFFLINE 0x0800 /* send to offline user */ -#define AIM_IMFLAGS_TYPINGNOT 0x1000 /* typing notification */ - -#define AIM_CHARSET_ASCII 0x0000 -#define AIM_CHARSET_UNICODE 0x0002 /* UCS-2BE */ -#define AIM_CHARSET_CUSTOM 0x0003 - -/* - * Multipart message structures. - */ -typedef struct aim_mpmsg_section_s { - fu16_t charset; - fu16_t charsubset; - gchar *data; - fu16_t datalen; - struct aim_mpmsg_section_s *next; -} aim_mpmsg_section_t; - -typedef struct aim_mpmsg_s { - unsigned int numparts; - aim_mpmsg_section_t *parts; -} aim_mpmsg_t; - -faim_export int aim_mpmsg_init(aim_session_t *sess, aim_mpmsg_t *mpm); -faim_export int aim_mpmsg_addraw(aim_session_t *sess, aim_mpmsg_t *mpm, fu16_t charset, fu16_t charsubset, const gchar *data, fu16_t datalen); -faim_export int aim_mpmsg_addascii(aim_session_t *sess, aim_mpmsg_t *mpm, const char *ascii); -faim_export int aim_mpmsg_addunicode(aim_session_t *sess, aim_mpmsg_t *mpm, const fu16_t *unicode, fu16_t unicodelen); -faim_export void aim_mpmsg_free(aim_session_t *sess, aim_mpmsg_t *mpm); - -/* - * Arguments to aim_send_im_ext(). - * - * This is really complicated. But immensely versatile. - * - */ -struct aim_sendimext_args { - - /* These are _required_ */ - const char *destsn; - fu32_t flags; /* often 0 */ - - /* Only required if not using multipart messages */ - const char *msg; - int msglen; - - /* Required if ->msg is not provided */ - aim_mpmsg_t *mpmsg; - - /* Only used if AIM_IMFLAGS_HASICON is set */ - fu32_t iconlen; - time_t iconstamp; - fu32_t iconsum; - - /* Only used if AIM_IMFLAGS_CUSTOMFEATURES is set */ - fu16_t featureslen; - fu8_t *features; - - /* Only used if AIM_IMFLAGS_CUSTOMCHARSET is set and mpmsg not used */ - fu16_t charset; - fu16_t charsubset; -}; - -/* - * Arguments to aim_send_rtfmsg(). - */ -struct aim_sendrtfmsg_args { - const char *destsn; - fu32_t fgcolor; - fu32_t bgcolor; - const char *rtfmsg; /* must be in RTF */ -}; - -/* - * This information is provided in the Incoming ICBM callback for - * Channel 1 ICBM's. - * - * Note that although CUSTOMFEATURES and CUSTOMCHARSET say they - * are optional, both are always set by the current libfaim code. - * That may or may not change in the future. It is mainly for - * consistency with aim_sendimext_args. - * - * Multipart messages require some explanation. If you want to use them, - * I suggest you read all the comments in im.c. - * - */ -struct aim_incomingim_ch1_args { - - /* Always provided */ - aim_mpmsg_t mpmsg; - fu32_t icbmflags; /* some flags apply only to ->msg, not all mpmsg */ - - /* Only provided if message has a human-readable section */ - gchar *msg; - int msglen; - - /* Only provided if AIM_IMFLAGS_HASICON is set */ - time_t iconstamp; - fu32_t iconlen; - fu16_t iconsum; - - /* Only provided if AIM_IMFLAGS_CUSTOMFEATURES is set */ - fu8_t *features; - fu8_t featureslen; - - /* Only provided if AIM_IMFLAGS_EXTDATA is set */ - fu8_t extdatalen; - fu8_t *extdata; - - /* Only used if AIM_IMFLAGS_CUSTOMCHARSET is set */ - fu16_t charset; - fu16_t charsubset; -}; - -/* Valid values for channel 2 args->status */ -#define AIM_RENDEZVOUS_PROPOSE 0x0000 -#define AIM_RENDEZVOUS_CANCEL 0x0001 -#define AIM_RENDEZVOUS_ACCEPT 0x0002 - -struct aim_incomingim_ch2_args { - fu16_t status; - guchar cookie[8]; - int reqclass; - const char *proxyip; - const char *clientip; - const char *verifiedip; - fu16_t port; - fu16_t errorcode; - const char *msg; /* invite message or file description */ - fu16_t msglen; - const char *encoding; - const char *language; - union { - struct { - fu32_t checksum; - fu32_t length; - time_t timestamp; - fu8_t *icon; - } icon; - struct { - struct aim_chat_roominfo roominfo; - } chat; - struct { - fu16_t msgtype; - fu32_t fgcolor; - fu32_t bgcolor; - const char *rtfmsg; - } rtfmsg; - struct { - fu16_t subtype; - fu16_t totfiles; - fu32_t totsize; - char *filename; - /* reqnum: 0x0001 usually; 0x0002 = reply request for stage 2 proxy transfer */ - fu16_t reqnum; - fu8_t use_proxy; /* Did the user request that we use a rv proxy? */ - } sendfile; - } info; - void *destructor; /* used internally only */ -}; - -/* Valid values for channel 4 args->type */ -#define AIM_ICQMSG_AUTHREQUEST 0x0006 -#define AIM_ICQMSG_AUTHDENIED 0x0007 -#define AIM_ICQMSG_AUTHGRANTED 0x0008 - -struct aim_incomingim_ch4_args { - fu32_t uin; /* Of the sender of the ICBM */ - fu8_t type; - fu8_t flags; - gchar *msg; /* Reason for auth request, deny, or accept */ - int msglen; -}; - -/* SNAC sending functions */ -/* 0x0002 */ faim_export int aim_im_setparams(aim_session_t *sess, struct aim_icbmparameters *params); -/* 0x0004 */ faim_export int aim_im_reqparams(aim_session_t *sess); -/* 0x0006 */ faim_export int aim_im_sendch1_ext(aim_session_t *sess, struct aim_sendimext_args *args); -/* 0x0006 */ faim_export int aim_im_sendch1(aim_session_t *, const char *destsn, fu16_t flags, const char *msg); -/* 0x0006 */ faim_export int aim_im_sendch2_chatinvite(aim_session_t *sess, const char *sn, const char *msg, fu16_t exchange, const char *roomname, fu16_t instance); -/* 0x0006 */ faim_export int aim_im_sendch2_icon(aim_session_t *sess, const char *sn, const fu8_t *icon, int iconlen, time_t stamp, fu16_t iconsum); -/* 0x0006 */ faim_export int aim_im_sendch2_rtfmsg(aim_session_t *sess, struct aim_sendrtfmsg_args *args); -/* 0x0006 */ faim_export int aim_im_sendch2_odcrequest(aim_session_t *sess, guchar *cookie, gboolean usecookie, const char *sn, const fu8_t *ip, fu16_t port); -/* 0x0006 */ faim_export int aim_im_sendch2_sendfile_ask(aim_session_t *sess, struct aim_oft_info *oft_info); -/* 0x0006 */ faim_export int aim_im_sendch2_sendfile_accept(aim_session_t *sess, struct aim_oft_info *info); -/* 0x0006 */ faim_export int aim_im_sendch2_sendfile_cancel(aim_session_t *sess, struct aim_oft_info *oft_info); -/* 0x0006 */ faim_export int aim_im_sendch2_geticqaway(aim_session_t *sess, const char *sn, int type); -/* 0x0006 */ faim_export int aim_im_sendch4(aim_session_t *sess, const char *sn, fu16_t type, const char *message); -/* 0x0008 */ faim_export int aim_im_warn(aim_session_t *sess, aim_conn_t *conn, const char *destsn, fu32_t flags); -/* 0x000b */ faim_export int aim_im_denytransfer(aim_session_t *sess, const char *sender, const guchar *cookie, fu16_t code); -/* 0x0014 */ faim_export int aim_im_sendmtn(aim_session_t *sess, fu16_t type1, const char *sn, fu16_t type2); -faim_export void aim_icbm_makecookie(guchar* cookie); - - -/* ft.c */ -struct aim_fileheader_t { -#if 0 - char magic[4]; /* 0 */ - fu16_t hdrlen; /* 4 */ - fu16_t hdrtype; /* 6 */ -#endif - guchar bcookie[8]; /* 8 */ - fu16_t encrypt; /* 16 */ - fu16_t compress; /* 18 */ - fu16_t totfiles; /* 20 */ - fu16_t filesleft; /* 22 */ - fu16_t totparts; /* 24 */ - fu16_t partsleft; /* 26 */ - fu32_t totsize; /* 28 */ - fu32_t size; /* 32 */ - fu32_t modtime; /* 36 */ - fu32_t checksum; /* 40 */ - fu32_t rfrcsum; /* 44 */ - fu32_t rfsize; /* 48 */ - fu32_t cretime; /* 52 */ - fu32_t rfcsum; /* 56 */ - fu32_t nrecvd; /* 60 */ - fu32_t recvcsum; /* 64 */ - char idstring[32]; /* 68 */ - fu8_t flags; /* 100 */ - fu8_t lnameoffset; /* 101 */ - fu8_t lsizeoffset; /* 102 */ - char dummy[69]; /* 103 */ - char macfileinfo[16]; /* 172 */ - fu16_t nencode; /* 188 */ - fu16_t nlanguage; /* 190 */ - char name[64]; /* 192 */ - /* 256 */ -}; - -struct aim_rv_proxy_info { - fu16_t packet_ver; - fu16_t cmd_type; - fu16_t flags; - char* ip; /* IP address sent along with this packet */ - fu16_t port; /* This is NOT the port we should use to connect. Always connect to 5190 */ - guchar cookie[8]; - fu32_t unknownA; - fu16_t err_code; /* Valid only for cmd_type of AIM_RV_PROXY_ERROR */ - aim_conn_t *conn; - aim_session_t *sess; -}; - -struct aim_oft_info { - guchar cookie[8]; - char *sn; - char *proxyip; - char *clientip; - char *verifiedip; - fu16_t port; - - int send_or_recv; /* Send or receive */ - int method; /* What method is being used to transfer this file? DIRECT, REDIR, or PROXY */ - int stage; /* At what stage was a proxy requested? NONE, STG1, STG2*/ - int xfer_reffed; /* There are many timers, but we should only ref the xfer once */ - int redir_attempted; /* Have we previously attempted to redirect the connection? */ - fu32_t res_bytes; /* The bytes already received for resuming a transfer */ - - aim_conn_t *conn; - aim_session_t *sess; - int success; /* Was the connection successful? Used for timing out the transfer. */ - struct aim_fileheader_t fh; - struct aim_oft_info *next; - struct aim_rv_proxy_info *proxy_info; -}; - -faim_export fu32_t aim_oft_checksum_chunk(const fu8_t *buffer, int bufferlen, fu32_t prevcheck); -faim_export fu32_t aim_oft_checksum_file(char *filename); -faim_export int aim_handlerendconnect(aim_session_t *sess, aim_conn_t *cur); -faim_export int aim_odc_send_typing(aim_session_t *sess, aim_conn_t *conn, int typing); -faim_export int aim_odc_send_im(aim_session_t *sess, aim_conn_t *conn, const char *msg, int len, int encoding, int isawaymsg); -faim_export const char *aim_odc_getsn(aim_conn_t *conn); -faim_export const guchar *aim_odc_getcookie(aim_conn_t *conn); -faim_export aim_conn_t *aim_odc_getconn(aim_session_t *sess, const char *sn); -faim_export aim_conn_t *aim_odc_initiate(aim_session_t *sess, const char *sn, int listenfd, - const fu8_t *localip, fu16_t port, const guchar *mycookie); -faim_export aim_conn_t *aim_odc_connect(aim_session_t *sess, const char *sn, const char *addr, const guchar *cookie); - -faim_export struct aim_oft_info *aim_oft_createinfo(aim_session_t *sess, const guchar *cookie, const char *sn, - const char *ip, fu16_t port, fu32_t size, fu32_t modtime, char *filename, int send_or_recv, - int method, int stage); -faim_export int aim_oft_destroyinfo(struct aim_oft_info *oft_info); -faim_export struct aim_rv_proxy_info *aim_rv_proxy_createinfo(aim_session_t *sess, const guchar *cookie, fu16_t port); -faim_export int aim_sendfile_listen(aim_session_t *sess, struct aim_oft_info *oft_info, int listenfd); -faim_export int aim_oft_sendheader(aim_session_t *sess, fu16_t type, struct aim_oft_info *oft_info); - -faim_export int aim_rv_proxy_init_recv(struct aim_rv_proxy_info *proxy_info); -faim_export int aim_rv_proxy_init_send(struct aim_rv_proxy_info *proxy_info); - -faim_export int aim_sendfile_listen(aim_session_t *sess, struct aim_oft_info *oft_info, int listenfd); -faim_export int aim_oft_sendheader(aim_session_t *sess, fu16_t type, struct aim_oft_info *oft_info); -faim_internal struct aim_rv_proxy_info *aim_rv_proxy_read(aim_session_t *sess, aim_conn_t *conn); - -/* 0x0002 - locate.c */ -/* - * AIM User Info, Standard Form. - */ -#define AIM_FLAG_UNCONFIRMED 0x0001 /* "damned transients" */ -#define AIM_FLAG_ADMINISTRATOR 0x0002 -#define AIM_FLAG_AOL 0x0004 -#define AIM_FLAG_OSCAR_PAY 0x0008 -#define AIM_FLAG_FREE 0x0010 -#define AIM_FLAG_AWAY 0x0020 -#define AIM_FLAG_ICQ 0x0040 -#define AIM_FLAG_WIRELESS 0x0080 -#define AIM_FLAG_UNKNOWN100 0x0100 -#define AIM_FLAG_UNKNOWN200 0x0200 -#define AIM_FLAG_ACTIVEBUDDY 0x0400 -#define AIM_FLAG_UNKNOWN800 0x0800 -#define AIM_FLAG_ABINTERNAL 0x1000 -#define AIM_FLAG_ALLUSERS 0x001f - -#define AIM_USERINFO_PRESENT_FLAGS 0x00000001 -#define AIM_USERINFO_PRESENT_MEMBERSINCE 0x00000002 -#define AIM_USERINFO_PRESENT_ONLINESINCE 0x00000004 -#define AIM_USERINFO_PRESENT_IDLE 0x00000008 -#define AIM_USERINFO_PRESENT_ICQEXTSTATUS 0x00000010 -#define AIM_USERINFO_PRESENT_ICQIPADDR 0x00000020 -#define AIM_USERINFO_PRESENT_ICQDATA 0x00000040 -#define AIM_USERINFO_PRESENT_CAPABILITIES 0x00000080 -#define AIM_USERINFO_PRESENT_SESSIONLEN 0x00000100 -#define AIM_USERINFO_PRESENT_CREATETIME 0x00000200 - -struct userinfo_node { - char *sn; - struct userinfo_node *next; -}; - -typedef struct aim_userinfo_s { - char *sn; - fu16_t warnlevel; /* evil percent * 10 (999 = 99.9%) */ - fu16_t idletime; /* in seconds */ - fu16_t flags; - fu32_t createtime; /* time_t */ - fu32_t membersince; /* time_t */ - fu32_t onlinesince; /* time_t */ - fu32_t sessionlen; /* in seconds */ - fu32_t capabilities; - struct { - fu32_t status; - fu32_t ipaddr; - fu8_t crap[0x25]; /* until we figure it out... */ - } icqinfo; - fu32_t present; - - fu8_t iconcsumtype; - fu16_t iconcsumlen; - fu8_t *iconcsum; - - char *info; - char *info_encoding; - fu16_t info_len; - - char *status; - char *status_encoding; - fu16_t status_len; - - char *away; - char *away_encoding; - fu16_t away_len; - - struct aim_userinfo_s *next; -} aim_userinfo_t; - -#define AIM_CAPS_BUDDYICON 0x00000001 -#define AIM_CAPS_TALK 0x00000002 -#define AIM_CAPS_DIRECTIM 0x00000004 -#define AIM_CAPS_CHAT 0x00000008 -#define AIM_CAPS_GETFILE 0x00000010 -#define AIM_CAPS_SENDFILE 0x00000020 -#define AIM_CAPS_GAMES 0x00000040 -#define AIM_CAPS_ADDINS 0x00000080 -#define AIM_CAPS_SENDBUDDYLIST 0x00000100 -#define AIM_CAPS_GAMES2 0x00000200 -#define AIM_CAPS_ICQ_DIRECT 0x00000400 -#define AIM_CAPS_APINFO 0x00000800 -#define AIM_CAPS_ICQRTF 0x00001000 -#define AIM_CAPS_EMPTY 0x00002000 -#define AIM_CAPS_ICQSERVERRELAY 0x00004000 -#define AIM_CAPS_ICQUTF8OLD 0x00008000 -#define AIM_CAPS_TRILLIANCRYPT 0x00010000 -#define AIM_CAPS_ICQUTF8 0x00020000 -#define AIM_CAPS_INTEROPERATE 0x00040000 -#define AIM_CAPS_ICHAT 0x00080000 -#define AIM_CAPS_HIPTOP 0x00100000 -#define AIM_CAPS_SECUREIM 0x00200000 -#define AIM_CAPS_SMS 0x00400000 -#define AIM_CAPS_GENERICUNKNOWN 0x00800000 -#define AIM_CAPS_VIDEO 0x01000000 -#define AIM_CAPS_ICHATAV 0x02000000 -#define AIM_CAPS_LIVEVIDEO 0x04000000 -#define AIM_CAPS_CAMERA 0x08000000 -#define AIM_CAPS_LAST 0x10000000 - -#define AIM_SENDMEMBLOCK_FLAG_ISREQUEST 0 -#define AIM_SENDMEMBLOCK_FLAG_ISHASH 1 - -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); - -struct aim_invite_priv { - char *sn; - char *roomname; - fu16_t exchange; - fu16_t instance; -}; - -#define AIM_COOKIETYPE_UNKNOWN 0x00 -#define AIM_COOKIETYPE_ICBM 0x01 -#define AIM_COOKIETYPE_ADS 0x02 -#define AIM_COOKIETYPE_BOS 0x03 -#define AIM_COOKIETYPE_IM 0x04 -#define AIM_COOKIETYPE_CHAT 0x05 -#define AIM_COOKIETYPE_CHATNAV 0x06 -#define AIM_COOKIETYPE_INVITE 0x07 -/* we'll move OFT up a bit to give breathing room. not like it really - * matters. */ -#define AIM_COOKIETYPE_OFTIM 0x10 -#define AIM_COOKIETYPE_OFTGET 0x11 -#define AIM_COOKIETYPE_OFTSEND 0x12 -#define AIM_COOKIETYPE_OFTVOICE 0x13 -#define AIM_COOKIETYPE_OFTIMAGE 0x14 -#define AIM_COOKIETYPE_OFTICON 0x15 - -faim_export aim_userinfo_t *aim_locate_finduserinfo(aim_session_t *sess, const char *sn); -faim_export void aim_locate_dorequest(aim_session_t *sess); - -/* 0x0002 */ faim_export int aim_locate_reqrights(aim_session_t *sess); -/* 0x0004 */ faim_export int aim_locate_setcaps(aim_session_t *sess, fu32_t caps); -/* 0x0004 */ faim_export int aim_locate_setprofile(aim_session_t *sess, const char *profile_encoding, const gchar *profile, const int profile_len, const char *awaymsg_encoding, const gchar *awaymsg, const int awaymsg_len); -/* 0x0005 */ faim_export int aim_locate_getinfo(aim_session_t *sess, const char *, fu16_t); -/* 0x0009 */ faim_export int aim_locate_setdirinfo(aim_session_t *sess, 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); -/* 0x000b */ faim_export int aim_locate_000b(aim_session_t *sess, const char *sn); -/* 0x000f */ faim_export int aim_locate_setinterests(aim_session_t *sess, const char *interest1, const char *interest2, const char *interest3, const char *interest4, const char *interest5, fu16_t privacy); -/* 0x0015 */ faim_export int aim_locate_getinfoshort(aim_session_t *sess, const char *sn, fu32_t flags); - - - -/* 0x0003 - buddylist.c */ -/* 0x0002 */ faim_export int aim_buddylist_reqrights(aim_session_t *, aim_conn_t *); -/* 0x0004 */ faim_export int aim_buddylist_set(aim_session_t *, aim_conn_t *, const char *); -/* 0x0004 */ faim_export int aim_buddylist_addbuddy(aim_session_t *, aim_conn_t *, const char *); -/* 0x0005 */ faim_export int aim_buddylist_removebuddy(aim_session_t *, aim_conn_t *, const char *); -/* 0x000b */ faim_export int aim_buddylist_oncoming(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *info); -/* 0x000c */ faim_export int aim_buddylist_offgoing(aim_session_t *sess, aim_conn_t *conn, const char *sn); - - - -/* 0x000a - search.c */ -faim_export int aim_search_address(aim_session_t *, aim_conn_t *, const char *); - - - -/* 0x000d - chatnav.c */ -/* 0x000e - chat.c */ -/* These apply to exchanges as well. */ -#define AIM_CHATROOM_FLAG_EVILABLE 0x0001 -#define AIM_CHATROOM_FLAG_NAV_ONLY 0x0002 -#define AIM_CHATROOM_FLAG_INSTANCING_ALLOWED 0x0004 -#define AIM_CHATROOM_FLAG_OCCUPANT_PEEK_ALLOWED 0x0008 - -struct aim_chat_exchangeinfo { - fu16_t number; - fu16_t flags; - char *name; - char *charset1; - char *lang1; - char *charset2; - char *lang2; -}; - -#define AIM_CHATFLAGS_NOREFLECT 0x0001 -#define AIM_CHATFLAGS_AWAY 0x0002 -faim_export int aim_chat_send_im(aim_session_t *sess, aim_conn_t *conn, fu16_t flags, const gchar *msg, int msglen, const char *encoding, const char *language); -faim_export int aim_chat_join(aim_session_t *sess, aim_conn_t *conn, fu16_t exchange, const char *roomname, fu16_t instance); -faim_export int aim_chat_attachname(aim_conn_t *conn, fu16_t exchange, const char *roomname, fu16_t instance); -faim_export char *aim_chat_getname(aim_conn_t *conn); -faim_export aim_conn_t *aim_chat_getconn(aim_session_t *, const char *name); - -faim_export int aim_chatnav_reqrights(aim_session_t *sess, aim_conn_t *conn); - -faim_export int aim_chatnav_createroom(aim_session_t *sess, aim_conn_t *conn, const char *name, fu16_t exchange); -faim_export int aim_chat_leaveroom(aim_session_t *sess, const char *name); - - - -/* 0x000f - odir.c */ -struct aim_odir { - char *first; - char *last; - char *middle; - char *maiden; - char *email; - char *country; - char *state; - char *city; - char *sn; - char *interest; - char *nick; - char *zip; - char *region; - char *address; - struct aim_odir *next; -}; - -faim_export int aim_odir_email(aim_session_t *, const char *, const char *); -faim_export int aim_odir_name(aim_session_t *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *); -faim_export int aim_odir_interest(aim_session_t *, const char *, const char *); - - - -/* 0x0010 - icon.c */ -faim_export int aim_bart_upload(aim_session_t *sess, const fu8_t *icon, fu16_t iconlen); -faim_export int aim_bart_request(aim_session_t *sess, const char *sn, fu8_t iconcsumtype, const fu8_t *iconstr, fu16_t iconstrlen); - - - -/* 0x0013 - ssi.c */ -#define AIM_SSI_TYPE_BUDDY 0x0000 -#define AIM_SSI_TYPE_GROUP 0x0001 -#define AIM_SSI_TYPE_PERMIT 0x0002 -#define AIM_SSI_TYPE_DENY 0x0003 -#define AIM_SSI_TYPE_PDINFO 0x0004 -#define AIM_SSI_TYPE_PRESENCEPREFS 0x0005 -#define AIM_SSI_TYPE_ICONINFO 0x0014 - -#define AIM_SSI_ACK_SUCCESS 0x0000 -#define AIM_SSI_ACK_ITEMNOTFOUND 0x0002 -#define AIM_SSI_ACK_IDNUMINUSE 0x000a -#define AIM_SSI_ACK_ATMAX 0x000c -#define AIM_SSI_ACK_INVALIDNAME 0x000d -#define AIM_SSI_ACK_AUTHREQUIRED 0x000e - -/* These flags are set in the 0x00c9 TLV of SSI teyp 0x0005 */ -#define AIM_SSI_PRESENCE_FLAG_SHOWIDLE 0x00000400 -#define AIM_SSI_PRESENCE_FLAG_NORECENTBUDDIES 0x00020000 - -struct aim_ssi_item { - char *name; - fu16_t gid; - fu16_t bid; - fu16_t type; - struct aim_tlvlist_s *data; - struct aim_ssi_item *next; -}; - -struct aim_ssi_tmp { - fu16_t action; - fu16_t ack; - char *name; - struct aim_ssi_item *item; - struct aim_ssi_tmp *next; -}; - -/* These build the actual SNACs and queue them to be sent */ -/* 0x0002 */ faim_export int aim_ssi_reqrights(aim_session_t *sess); -/* 0x0004 */ faim_export int aim_ssi_reqdata(aim_session_t *sess); -/* 0x0005 */ faim_export int aim_ssi_reqifchanged(aim_session_t *sess, time_t localstamp, fu16_t localrev); -/* 0x0007 */ faim_export int aim_ssi_enable(aim_session_t *sess); -/* 0x0008 */ faim_export int aim_ssi_addmoddel(aim_session_t *sess); -/* 0x0011 */ faim_export int aim_ssi_modbegin(aim_session_t *sess); -/* 0x0012 */ faim_export int aim_ssi_modend(aim_session_t *sess); -/* 0x0014 */ faim_export int aim_ssi_sendauth(aim_session_t *sess, char *sn, char *msg); -/* 0x0018 */ faim_export int aim_ssi_sendauthrequest(aim_session_t *sess, char *sn, const char *msg); -/* 0x001a */ faim_export int aim_ssi_sendauthreply(aim_session_t *sess, char *sn, fu8_t reply, const char *msg); - -/* Client functions for retrieving SSI data */ -faim_export struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_item *list, fu16_t gid, fu16_t bid); -faim_export struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_item *list, const char *gn, const char *sn, fu16_t type); -faim_export struct aim_ssi_item *aim_ssi_itemlist_exists(struct aim_ssi_item *list, const char *sn); -faim_export char *aim_ssi_itemlist_findparentname(struct aim_ssi_item *list, const char *sn); -faim_export int aim_ssi_getpermdeny(struct aim_ssi_item *list); -faim_export fu32_t aim_ssi_getpresence(struct aim_ssi_item *list); -faim_export char *aim_ssi_getalias(struct aim_ssi_item *list, const char *gn, const char *sn); -faim_export char *aim_ssi_getcomment(struct aim_ssi_item *list, const char *gn, const char *sn); -faim_export int aim_ssi_waitingforauth(struct aim_ssi_item *list, const char *gn, const char *sn); - -/* Client functions for changing SSI data */ -faim_export int aim_ssi_addbuddy(aim_session_t *sess, const char *name, const char *group, const char *alias, const char *comment, const char *smsnum, int needauth); -faim_export int aim_ssi_addpermit(aim_session_t *sess, const char *name); -faim_export int aim_ssi_adddeny(aim_session_t *sess, const char *name); -faim_export int aim_ssi_delbuddy(aim_session_t *sess, const char *name, const char *group); -faim_export int aim_ssi_delpermit(aim_session_t *sess, const char *name); -faim_export int aim_ssi_deldeny(aim_session_t *sess, const char *name); -faim_export int aim_ssi_movebuddy(aim_session_t *sess, const char *oldgn, const char *newgn, const char *sn); -faim_export int aim_ssi_aliasbuddy(aim_session_t *sess, const char *gn, const char *sn, const char *alias); -faim_export int aim_ssi_editcomment(aim_session_t *sess, const char *gn, const char *sn, const char *alias); -faim_export int aim_ssi_rename_group(aim_session_t *sess, const char *oldgn, const char *newgn); -faim_export int aim_ssi_cleanlist(aim_session_t *sess); -faim_export int aim_ssi_deletelist(aim_session_t *sess); -faim_export int aim_ssi_setpermdeny(aim_session_t *sess, fu8_t permdeny, fu32_t vismask); -faim_export int aim_ssi_setpresence(aim_session_t *sess, fu32_t presence); -faim_export int aim_ssi_seticon(aim_session_t *sess, fu8_t *iconsum, fu16_t iconsumlen); -faim_export int aim_ssi_delicon(aim_session_t *sess); - - - -/* 0x0015 - icq.c */ -#define AIM_ICQ_INFO_SIMPLE 0x001 -#define AIM_ICQ_INFO_SUMMARY 0x002 -#define AIM_ICQ_INFO_EMAIL 0x004 -#define AIM_ICQ_INFO_PERSONAL 0x008 -#define AIM_ICQ_INFO_ADDITIONAL 0x010 -#define AIM_ICQ_INFO_WORK 0x020 -#define AIM_ICQ_INFO_INTERESTS 0x040 -#define AIM_ICQ_INFO_ORGS 0x080 -#define AIM_ICQ_INFO_UNKNOWN 0x100 -#define AIM_ICQ_INFO_HAVEALL 0x1ff - -struct aim_icq_offlinemsg { - fu32_t sender; - fu16_t year; - fu8_t month, day, hour, minute; - fu8_t type; - fu8_t flags; - char *msg; - int msglen; -}; - -struct aim_icq_info { - fu16_t reqid; - - /* simple */ - fu32_t uin; - - /* general and "home" information (0x00c8) */ - char *nick; - char *first; - char *last; - char *email; - char *homecity; - char *homestate; - char *homephone; - char *homefax; - char *homeaddr; - char *mobile; - char *homezip; - fu16_t homecountry; -/* fu8_t timezone; - fu8_t hideemail; */ - - /* personal (0x00dc) */ - fu8_t age; - fu8_t unknown; - fu8_t gender; - char *personalwebpage; - fu16_t birthyear; - fu8_t birthmonth; - fu8_t birthday; - fu8_t language1; - fu8_t language2; - fu8_t language3; - - /* work (0x00d2) */ - char *workcity; - char *workstate; - char *workphone; - char *workfax; - char *workaddr; - char *workzip; - fu16_t workcountry; - char *workcompany; - char *workdivision; - char *workposition; - char *workwebpage; - - /* additional personal information (0x00e6) */ - char *info; - - /* email (0x00eb) */ - fu16_t numaddresses; - char **email2; - - /* we keep track of these in a linked list because we're 1337 */ - struct aim_icq_info *next; -}; - -faim_export int aim_icq_reqofflinemsgs(aim_session_t *sess); -faim_export int aim_icq_ackofflinemsgs(aim_session_t *sess); -faim_export int aim_icq_setsecurity(aim_session_t *sess, gboolean auth_required, gboolean webaware); -faim_export int aim_icq_changepasswd(aim_session_t *sess, const char *passwd); -faim_export int aim_icq_getsimpleinfo(aim_session_t *sess, const char *uin); -faim_export int aim_icq_getalias(aim_session_t *sess, const char *uin); -faim_export int aim_icq_getallinfo(aim_session_t *sess, const char *uin); - - - -/* 0x0017 - auth.c */ -faim_export int aim_sendcookie(aim_session_t *, aim_conn_t *, const fu16_t length, const fu8_t *); -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); -faim_export int aim_admin_setnick(aim_session_t *sess, aim_conn_t *conn, const char *newnick); - - - -/* 0x0018 - email.c */ -struct aim_emailinfo { - fu8_t *cookie16; - fu8_t *cookie8; - char *url; - fu16_t nummsgs; - fu8_t unread; - char *domain; - fu16_t flag; - struct aim_emailinfo *next; -}; - -faim_export int aim_email_sendcookies(aim_session_t *sess); -faim_export int aim_email_activate(aim_session_t *sess); - - - -#if defined(FAIM_INTERNAL) || defined(FAIM_NEED_TLV) -/* tlv.c - TLV handling */ - -/* TLV structure */ -typedef struct aim_tlv_s { - fu16_t type; - fu16_t length; - fu8_t *value; -} aim_tlv_t; - -/* TLV List structure */ -typedef struct aim_tlvlist_s { - aim_tlv_t *tlv; - struct aim_tlvlist_s *next; -} aim_tlvlist_t; - -/* TLV handling functions */ -faim_internal aim_tlv_t *aim_tlv_gettlv(aim_tlvlist_t *list, fu16_t type, const int nth); -faim_internal int aim_tlv_getlength(aim_tlvlist_t *list, fu16_t type, const int nth); -faim_internal char *aim_tlv_getstr(aim_tlvlist_t *list, const fu16_t type, const int nth); -faim_internal fu8_t aim_tlv_get8(aim_tlvlist_t *list, const fu16_t type, const int nth); -faim_internal fu16_t aim_tlv_get16(aim_tlvlist_t *list, const fu16_t type, const int nth); -faim_internal fu32_t aim_tlv_get32(aim_tlvlist_t *list, const fu16_t type, const int nth); - -/* TLV list handling functions */ -faim_internal aim_tlvlist_t *aim_tlvlist_read(aim_bstream_t *bs); -faim_internal aim_tlvlist_t *aim_tlvlist_readnum(aim_bstream_t *bs, fu16_t num); -faim_internal aim_tlvlist_t *aim_tlvlist_readlen(aim_bstream_t *bs, fu16_t len); -faim_internal aim_tlvlist_t *aim_tlvlist_copy(aim_tlvlist_t *orig); - -faim_internal int aim_tlvlist_count(aim_tlvlist_t **list); -faim_internal int aim_tlvlist_size(aim_tlvlist_t **list); -faim_internal int aim_tlvlist_cmp(aim_tlvlist_t *one, aim_tlvlist_t *two); -faim_internal int aim_tlvlist_write(aim_bstream_t *bs, aim_tlvlist_t **list); -faim_internal void aim_tlvlist_free(aim_tlvlist_t **list); - -faim_internal int aim_tlvlist_add_raw(aim_tlvlist_t **list, const fu16_t type, const fu16_t length, const fu8_t *value); -faim_internal int aim_tlvlist_add_noval(aim_tlvlist_t **list, const fu16_t type); -faim_internal int aim_tlvlist_add_8(aim_tlvlist_t **list, const fu16_t type, const fu8_t value); -faim_internal int aim_tlvlist_add_16(aim_tlvlist_t **list, const fu16_t type, const fu16_t value); -faim_internal int aim_tlvlist_add_32(aim_tlvlist_t **list, const fu16_t type, const fu32_t value); -faim_internal int aim_tlvlist_add_str(aim_tlvlist_t **list, const fu16_t type, const char *value); -faim_internal int aim_tlvlist_add_caps(aim_tlvlist_t **list, const fu16_t type, const fu32_t caps); -faim_internal int aim_tlvlist_add_userinfo(aim_tlvlist_t **list, fu16_t type, aim_userinfo_t *userinfo); -faim_internal int aim_tlvlist_add_chatroom(aim_tlvlist_t **list, fu16_t type, fu16_t exchange, const char *roomname, fu16_t instance); -faim_internal int aim_tlvlist_add_frozentlvlist(aim_tlvlist_t **list, fu16_t type, aim_tlvlist_t **tl); - -faim_internal int aim_tlvlist_replace_raw(aim_tlvlist_t **list, const fu16_t type, const fu16_t lenth, const fu8_t *value); -faim_internal int aim_tlvlist_replace_str(aim_tlvlist_t **list, const fu16_t type, const char *str); -faim_internal int aim_tlvlist_replace_noval(aim_tlvlist_t **list, const fu16_t type); -faim_internal int aim_tlvlist_replace_8(aim_tlvlist_t **list, const fu16_t type, const fu8_t value); -faim_internal int aim_tlvlist_replace_16(aim_tlvlist_t **list, const fu16_t type, const fu16_t value); -faim_internal int aim_tlvlist_replace_32(aim_tlvlist_t **list, const fu16_t type, const fu32_t value); - -faim_internal void aim_tlvlist_remove(aim_tlvlist_t **list, const fu16_t type); -#endif /* FAIM_INTERNAL */ - - - -/* util.c */ -/* - * These are really ugly. You'd think this was LISP. I wish it was. - * - * XXX With the advent of bstream's, these should be removed to enforce - * their use. - * - */ -#define aimutil_put8(buf, data) ((*(buf) = (fu8_t)(data)&0xff),1) -#define aimutil_get8(buf) ((*(buf))&0xff) -#define aimutil_put16(buf, data) ( \ - (*(buf) = (fu8_t)((data)>>8)&0xff), \ - (*((buf)+1) = (fu8_t)(data)&0xff), \ - 2) -#define aimutil_get16(buf) ((((*(buf))<<8)&0xff00) + ((*((buf)+1)) & 0xff)) -#define aimutil_put32(buf, data) ( \ - (*((buf)) = (fu8_t)((data)>>24)&0xff), \ - (*((buf)+1) = (fu8_t)((data)>>16)&0xff), \ - (*((buf)+2) = (fu8_t)((data)>>8)&0xff), \ - (*((buf)+3) = (fu8_t)(data)&0xff), \ - 4) -#define aimutil_get32(buf) ((((*(buf))<<24)&0xff000000) + \ - (((*((buf)+1))<<16)&0x00ff0000) + \ - (((*((buf)+2))<< 8)&0x0000ff00) + \ - (((*((buf)+3) )&0x000000ff))) - -/* Little-endian versions (damn ICQ) */ -#define aimutil_putle8(buf, data) ( \ - (*(buf) = (fu8_t)(data) & 0xff), \ - 1) -#define aimutil_getle8(buf) ( \ - (*(buf)) & 0xff \ - ) -#define aimutil_putle16(buf, data) ( \ - (*((buf)+0) = (fu8_t)((data) >> 0) & 0xff), \ - (*((buf)+1) = (fu8_t)((data) >> 8) & 0xff), \ - 2) -#define aimutil_getle16(buf) ( \ - (((*((buf)+0)) << 0) & 0x00ff) + \ - (((*((buf)+1)) << 8) & 0xff00) \ - ) -#define aimutil_putle32(buf, data) ( \ - (*((buf)+0) = (fu8_t)((data) >> 0) & 0xff), \ - (*((buf)+1) = (fu8_t)((data) >> 8) & 0xff), \ - (*((buf)+2) = (fu8_t)((data) >> 16) & 0xff), \ - (*((buf)+3) = (fu8_t)((data) >> 24) & 0xff), \ - 4) -#define aimutil_getle32(buf) ( \ - (((*((buf)+0)) << 0) & 0x000000ff) + \ - (((*((buf)+1)) << 8) & 0x0000ff00) + \ - (((*((buf)+2)) << 16) & 0x00ff0000) + \ - (((*((buf)+3)) << 24) & 0xff000000)) - - -faim_export int aimutil_putstr(char *, const char *, int); -faim_export fu16_t aimutil_iconsum(const fu8_t *buf, int buflen); -faim_export int aimutil_tokslen(char *toSearch, int theindex, char dl); -faim_export int aimutil_itemcnt(char *toSearch, char dl); -faim_export char *aimutil_itemindex(char *toSearch, int theindex, char dl); - -faim_export int aim_snvalid(const char *sn); -faim_export int aim_sn_is_icq(const char *sn); -faim_export int aim_sn_is_sms(const char *sn); -faim_export int aim_snlen(const char *sn); -faim_export int aim_sncmp(const char *sn1, const char *sn2); - -#include - -#ifdef __cplusplus -} -#endif - -#endif /* __AIM_H__ */ diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/aim_cbtypes.h --- a/src/protocols/oscar/aim_cbtypes.h Sat Feb 11 19:16:38 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,314 +0,0 @@ -/* - * AIM Callback Types - * - */ -#ifndef __AIM_CBTYPES_H__ -#define __AIM_CBTYPES_H__ - -/* - * SNAC Families. - */ -#define AIM_CB_FAM_ACK 0x0000 -#define AIM_CB_FAM_GEN 0x0001 -#define AIM_CB_FAM_LOC 0x0002 -#define AIM_CB_FAM_BUD 0x0003 -#define AIM_CB_FAM_MSG 0x0004 -#define AIM_CB_FAM_ADS 0x0005 -#define AIM_CB_FAM_INV 0x0006 -#define AIM_CB_FAM_ADM 0x0007 -#define AIM_CB_FAM_POP 0x0008 -#define AIM_CB_FAM_BOS 0x0009 -#define AIM_CB_FAM_LOK 0x000a -#define AIM_CB_FAM_STS 0x000b -#define AIM_CB_FAM_TRN 0x000c -#define AIM_CB_FAM_CTN 0x000d /* ChatNav */ -#define AIM_CB_FAM_CHT 0x000e /* Chat */ -#define AIM_CB_FAM_SCH 0x000f /* "New" search */ -#define AIM_CB_FAM_ICO 0x0010 /* Used for uploading buddy icons */ -#define AIM_CB_FAM_SSI 0x0013 /* Server stored information */ -#define AIM_CB_FAM_ICQ 0x0015 -#define AIM_CB_FAM_ATH 0x0017 -#define AIM_CB_FAM_EML 0x0018 -#define AIM_CB_FAM_OFT 0xfffe /* OFT/Rvous */ -#define AIM_CB_FAM_SPECIAL 0xffff /* Internal libfaim use */ - -/* - * SNAC Family: Ack. - * - * Not really a family, but treating it as one really - * helps it fit into the libfaim callback structure better. - * - */ -#define AIM_CB_ACK_ACK 0x0001 - -/* - * SNAC Family: General. - */ -#define AIM_CB_GEN_ERROR 0x0001 -#define AIM_CB_GEN_CLIENTREADY 0x0002 -#define AIM_CB_GEN_SERVERREADY 0x0003 -#define AIM_CB_GEN_SERVICEREQ 0x0004 -#define AIM_CB_GEN_REDIRECT 0x0005 -#define AIM_CB_GEN_RATEINFOREQ 0x0006 -#define AIM_CB_GEN_RATEINFO 0x0007 -#define AIM_CB_GEN_RATEINFOACK 0x0008 -#define AIM_CB_GEN_RATECHANGE 0x000a -#define AIM_CB_GEN_SERVERPAUSE 0x000b -#define AIM_CB_GEN_SERVERRESUME 0x000d -#define AIM_CB_GEN_REQSELFINFO 0x000e -#define AIM_CB_GEN_SELFINFO 0x000f -#define AIM_CB_GEN_EVIL 0x0010 -#define AIM_CB_GEN_SETIDLE 0x0011 -#define AIM_CB_GEN_MIGRATIONREQ 0x0012 -#define AIM_CB_GEN_MOTD 0x0013 -#define AIM_CB_GEN_SETPRIVFLAGS 0x0014 -#define AIM_CB_GEN_WELLKNOWNURL 0x0015 -#define AIM_CB_GEN_NOP 0x0016 -#define AIM_CB_GEN_DEFAULT 0xffff - -/* - * SNAC Family: Location Services. - */ -#define AIM_CB_LOC_ERROR 0x0001 -#define AIM_CB_LOC_REQRIGHTS 0x0002 -#define AIM_CB_LOC_RIGHTSINFO 0x0003 -#define AIM_CB_LOC_SETUSERINFO 0x0004 -#define AIM_CB_LOC_REQUSERINFO 0x0005 -#define AIM_CB_LOC_USERINFO 0x0006 -#define AIM_CB_LOC_WATCHERSUBREQ 0x0007 -#define AIM_CB_LOC_WATCHERNOT 0x0008 -#define AIM_CB_LOC_GOTINFOBLOCK 0xfffd -#define AIM_CB_LOC_REQUESTINFOTIMEOUT 0xfffe -#define AIM_CB_LOC_DEFAULT 0xffff - -/* - * SNAC Family: Buddy List Management Services. - */ -#define AIM_CB_BUD_ERROR 0x0001 -#define AIM_CB_BUD_REQRIGHTS 0x0002 -#define AIM_CB_BUD_RIGHTSINFO 0x0003 -#define AIM_CB_BUD_ADDBUDDY 0x0004 -#define AIM_CB_BUD_REMBUDDY 0x0005 -#define AIM_CB_BUD_REJECT 0x000a -#define AIM_CB_BUD_ONCOMING 0x000b -#define AIM_CB_BUD_OFFGOING 0x000c -#define AIM_CB_BUD_DEFAULT 0xffff - -/* - * SNAC Family: Messaging Services. - */ -#define AIM_CB_MSG_ERROR 0x0001 -#define AIM_CB_MSG_PARAMINFO 0x0005 -#define AIM_CB_MSG_INCOMING 0x0007 -#define AIM_CB_MSG_EVIL 0x0009 -#define AIM_CB_MSG_MISSEDCALL 0x000a -#define AIM_CB_MSG_CLIENTAUTORESP 0x000b -#define AIM_CB_MSG_ACK 0x000c -#define AIM_CB_MSG_MTN 0x0014 -#define AIM_CB_MSG_DEFAULT 0xffff - -/* - * SNAC Family: Advertisement Services - */ -#define AIM_CB_ADS_ERROR 0x0001 -#define AIM_CB_ADS_DEFAULT 0xffff - -/* - * SNAC Family: Invitation Services. - */ -#define AIM_CB_INV_ERROR 0x0001 -#define AIM_CB_INV_DEFAULT 0xffff - -/* - * SNAC Family: Administrative Services. - */ -#define AIM_CB_ADM_ERROR 0x0001 -#define AIM_CB_ADM_INFOCHANGE_REPLY 0x0005 -#define AIM_CB_ADM_DEFAULT 0xffff - -/* - * SNAC Family: Popup Messages - */ -#define AIM_CB_POP_ERROR 0x0001 -#define AIM_CB_POP_DEFAULT 0xffff - -/* - * SNAC Family: Misc BOS Services. - */ -#define AIM_CB_BOS_ERROR 0x0001 -#define AIM_CB_BOS_RIGHTSQUERY 0x0002 -#define AIM_CB_BOS_RIGHTS 0x0003 -#define AIM_CB_BOS_DEFAULT 0xffff - -/* - * SNAC Family: User Lookup Services - */ -#define AIM_CB_LOK_ERROR 0x0001 -#define AIM_CB_LOK_DEFAULT 0xffff - -/* - * SNAC Family: User Status Services - */ -#define AIM_CB_STS_ERROR 0x0001 -#define AIM_CB_STS_SETREPORTINTERVAL 0x0002 -#define AIM_CB_STS_REPORTACK 0x0004 -#define AIM_CB_STS_DEFAULT 0xffff - -/* - * SNAC Family: Translation Services - */ -#define AIM_CB_TRN_ERROR 0x0001 -#define AIM_CB_TRN_DEFAULT 0xffff - -/* - * SNAC Family: Chat Navigation Services - */ -#define AIM_CB_CTN_ERROR 0x0001 -#define AIM_CB_CTN_CREATE 0x0008 -#define AIM_CB_CTN_INFO 0x0009 -#define AIM_CB_CTN_DEFAULT 0xffff - -/* - * SNAC Family: Chat Services - */ -#define AIM_CB_CHT_ERROR 0x0001 -#define AIM_CB_CHT_ROOMINFOUPDATE 0x0002 -#define AIM_CB_CHT_USERJOIN 0x0003 -#define AIM_CB_CHT_USERLEAVE 0x0004 -#define AIM_CB_CHT_OUTGOINGMSG 0x0005 -#define AIM_CB_CHT_INCOMINGMSG 0x0006 -#define AIM_CB_CHT_DEFAULT 0xffff - -/* - * SNAC Family: "New" Search - */ -#define AIM_CB_SCH_ERROR 0x0001 -#define AIM_CB_SCH_SEARCH 0x0002 -#define AIM_CB_SCH_RESULTS 0x0003 - -/* - * SNAC Family: Buddy icons - */ -#define AIM_CB_ICO_ERROR 0x0001 -#define AIM_CB_ICO_REQUEST 0x0004 -#define AIM_CB_ICO_RESPONSE 0x0005 - -/* - * SNAC Family: ICQ - * - * Most of these are actually special. - */ -#define AIM_CB_ICQ_ERROR 0x0001 -#define AIM_CB_ICQ_OFFLINEMSG 0x00f0 -#define AIM_CB_ICQ_OFFLINEMSGCOMPLETE 0x00f1 -#define AIM_CB_ICQ_INFO 0x00f2 -#define AIM_CB_ICQ_ALIAS 0x00f3 -#define AIM_CB_ICQ_DEFAULT 0xffff - -/* - * SNAC Family: Server-Stored Buddy Lists - */ -#define AIM_CB_SSI_ERROR 0x0001 -#define AIM_CB_SSI_REQRIGHTS 0x0002 -#define AIM_CB_SSI_RIGHTSINFO 0x0003 -#define AIM_CB_SSI_REQDATA 0x0004 -#define AIM_CB_SSI_REQIFCHANGED 0x0005 -#define AIM_CB_SSI_LIST 0x0006 -#define AIM_CB_SSI_ACTIVATE 0x0007 -#define AIM_CB_SSI_ADD 0x0008 -#define AIM_CB_SSI_MOD 0x0009 -#define AIM_CB_SSI_DEL 0x000A -#define AIM_CB_SSI_SRVACK 0x000E -#define AIM_CB_SSI_NOLIST 0x000F -#define AIM_CB_SSI_EDITSTART 0x0011 -#define AIM_CB_SSI_EDITSTOP 0x0012 -#define AIM_CB_SSI_SENDAUTH 0x0014 -#define AIM_CB_SSI_RECVAUTH 0x0015 -#define AIM_CB_SSI_SENDAUTHREQ 0x0018 -#define AIM_CB_SSI_RECVAUTHREQ 0x0019 -#define AIM_CB_SSI_SENDAUTHREP 0x001a -#define AIM_CB_SSI_RECVAUTHREP 0x001b -#define AIM_CB_SSI_ADDED 0x001c - -/* - * SNAC Family: Authorizer - * - * Used only in protocol versions three and above. - * - */ -#define AIM_CB_ATH_ERROR 0x0001 -#define AIM_CB_ATH_LOGINREQEST 0x0002 -#define AIM_CB_ATH_LOGINRESPONSE 0x0003 -#define AIM_CB_ATH_AUTHREQ 0x0006 -#define AIM_CB_ATH_AUTHRESPONSE 0x0007 -#define AIM_CB_ATH_SECURID_REQUEST 0x000a -#define AIM_CB_ATH_SECURID_RESPONSE 0x000b - -/* - * SNAC Family: Email - * - * Used for getting information on the email address - * associated with your screen name. - * - */ -#define AIM_CB_EML_ERROR 0x0001 -#define AIM_CB_EML_SENDCOOKIES 0x0006 -#define AIM_CB_EML_MAILSTATUS 0x0007 -#define AIM_CB_EML_INIT 0x0016 - -/* - * OFT Services - * - * For all of the above #defines, the number is the subtype - * of the SNAC. For OFT #defines, the number is the - * "hdrtype" which comes after the magic string and OFT - * packet length. - * - * I'm pretty sure the ODC ones are arbitrary right now, - * that should be changed. - */ -#define AIM_CB_OFT_DIRECTIMCONNECTREQ 0x0001 /* connect request -- actually an OSCAR CAP */ -#define AIM_CB_OFT_DIRECTIMINCOMING 0x0002 -#define AIM_CB_OFT_DIRECTIMDISCONNECT 0x0003 -#define AIM_CB_OFT_DIRECTIMTYPING 0x0004 -#define AIM_CB_OFT_DIRECTIM_ESTABLISHED 0x0005 - -#define AIM_CB_OFT_PROMPT 0x0101 /* "I am going to send you this file, is that ok?" */ -#define AIM_CB_OFT_RESUMESOMETHING 0x0106 /* I really don't know */ -#define AIM_CB_OFT_ACK 0x0202 /* "Yes, it is ok for you to send me that file" */ -#define AIM_CB_OFT_DONE 0x0204 /* "I received that file with no problems, thanks a bunch" */ -#define AIM_CB_OFT_RESUME 0x0205 /* Resume transferring, sent by whoever paused? */ -#define AIM_CB_OFT_RESUMEACK 0x0207 /* Not really sure */ - -#define AIM_CB_OFT_GETFILE_REQUESTLISTING 0x1108 /* "I have a listing.txt file, do you want it?" */ -#define AIM_CB_OFT_GETFILE_RECEIVELISTING 0x1209 /* "Yes, please send me your listing.txt file" */ -#define AIM_CB_OFT_GETFILE_RECEIVEDLISTING 0x120a /* received corrupt listing.txt file? I'm just guessing about this one... */ -#define AIM_CB_OFT_GETFILE_ACKLISTING 0x120b /* "I received the listing.txt file successfully" */ -#define AIM_CB_OFT_GETFILE_REQUESTFILE 0x120c /* "Please send me this file" */ - -#define AIM_CB_OFT_ESTABLISHED 0xFFFF /* connection to buddy initiated */ - -/* - * SNAC Family: Internal Messages - * - * This isn't truly a SNAC family either, but using - * these, we can integrated non-SNAC services into - * the SNAC-centered libfaim callback structure. - * - */ -#define AIM_CB_SPECIAL_AUTHSUCCESS 0x0001 -#define AIM_CB_SPECIAL_AUTHOTHER 0x0002 -#define AIM_CB_SPECIAL_CONNERR 0x0003 -#define AIM_CB_SPECIAL_CONNCOMPLETE 0x0004 -#define AIM_CB_SPECIAL_FLAPVER 0x0005 -#define AIM_CB_SPECIAL_CONNINITDONE 0x0006 -#define AIM_CB_SPECIAL_IMAGETRANSFER 0x0007 -#define AIM_CB_SPECIAL_MSGTIMEOUT 0x0008 -#define AIM_CB_SPECIAL_CONNDEAD 0x0009 -#define AIM_CB_SPECIAL_UNKNOWN 0xffff -#define AIM_CB_SPECIAL_DEFAULT AIM_CB_SPECIAL_UNKNOWN - -/* SNAC flags */ -#define AIM_SNACFLAGS_DESTRUCTOR 0x0001 - -#endif/*__AIM_CBTYPES_H__ */ diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/aim_internal.h --- a/src/protocols/oscar/aim_internal.h Sat Feb 11 19:16:38 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,218 +0,0 @@ -/* - * aim_internal.h -- prototypes/structs for the guts of libfaim - * - */ - -#ifdef FAIM_INTERNAL -#ifndef __AIM_INTERNAL_H__ -#define __AIM_INTERNAL_H__ 1 - -typedef struct { - fu16_t family; - fu16_t subtype; - fu16_t flags; - fu32_t id; -} aim_modsnac_t; - -#define AIM_MODULENAME_MAXLEN 16 -#define AIM_MODFLAG_MULTIFAMILY 0x0001 -typedef struct aim_module_s { - fu16_t family; - fu16_t version; - fu16_t toolid; - fu16_t toolversion; - fu16_t flags; - char name[AIM_MODULENAME_MAXLEN+1]; - int (*snachandler)(aim_session_t *sess, struct aim_module_s *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs); - - void (*shutdown)(aim_session_t *sess, struct aim_module_s *mod); - void *priv; - struct aim_module_s *next; -} aim_module_t; - -faim_internal int aim__registermodule(aim_session_t *sess, int (*modfirst)(aim_session_t *, aim_module_t *)); -faim_internal void aim__shutdownmodules(aim_session_t *sess); -faim_internal aim_module_t *aim__findmodulebygroup(aim_session_t *sess, fu16_t group); -faim_internal aim_module_t *aim__findmodule(aim_session_t *sess, const char *name); - -faim_internal int admin_modfirst(aim_session_t *sess, aim_module_t *mod); -faim_internal int buddylist_modfirst(aim_session_t *sess, aim_module_t *mod); -faim_internal int bos_modfirst(aim_session_t *sess, aim_module_t *mod); -faim_internal int search_modfirst(aim_session_t *sess, aim_module_t *mod); -faim_internal int stats_modfirst(aim_session_t *sess, aim_module_t *mod); -faim_internal int auth_modfirst(aim_session_t *sess, aim_module_t *mod); -faim_internal int msg_modfirst(aim_session_t *sess, aim_module_t *mod); -faim_internal int misc_modfirst(aim_session_t *sess, aim_module_t *mod); -faim_internal int chatnav_modfirst(aim_session_t *sess, aim_module_t *mod); -faim_internal int chat_modfirst(aim_session_t *sess, aim_module_t *mod); -faim_internal int locate_modfirst(aim_session_t *sess, aim_module_t *mod); -faim_internal int service_modfirst(aim_session_t *sess, aim_module_t *mod); -faim_internal int invite_modfirst(aim_session_t *sess, aim_module_t *mod); -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 odir_modfirst(aim_session_t *sess, aim_module_t *mod); -faim_internal int bart_modfirst(aim_session_t *sess, aim_module_t *mod); -faim_internal int ssi_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 email_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); -faim_internal int aim_genericreq_l(aim_session_t *, aim_conn_t *conn, fu16_t family, fu16_t subtype, fu32_t *); -faim_internal int aim_genericreq_s(aim_session_t *, aim_conn_t *conn, fu16_t family, fu16_t subtype, fu16_t *); - -#define AIMBS_CURPOSPAIR(x) ((x)->data + (x)->offset), ((x)->len - (x)->offset) - -/* bstream.c */ -faim_internal int aim_bstream_init(aim_bstream_t *bs, fu8_t *data, int len); -faim_internal int aim_bstream_empty(aim_bstream_t *bs); -faim_internal int aim_bstream_curpos(aim_bstream_t *bs); -faim_internal int aim_bstream_setpos(aim_bstream_t *bs, unsigned int off); -faim_internal void aim_bstream_rewind(aim_bstream_t *bs); -faim_internal int aim_bstream_advance(aim_bstream_t *bs, int n); -faim_internal fu8_t aimbs_get8(aim_bstream_t *bs); -faim_internal fu16_t aimbs_get16(aim_bstream_t *bs); -faim_internal fu32_t aimbs_get32(aim_bstream_t *bs); -faim_internal fu8_t aimbs_getle8(aim_bstream_t *bs); -faim_internal fu16_t aimbs_getle16(aim_bstream_t *bs); -faim_internal fu32_t aimbs_getle32(aim_bstream_t *bs); -faim_internal int aimbs_getrawbuf(aim_bstream_t *bs, fu8_t *buf, int len); -faim_internal fu8_t *aimbs_getraw(aim_bstream_t *bs, int len); -faim_internal char *aimbs_getstr(aim_bstream_t *bs, int len); -faim_internal int aimbs_put8(aim_bstream_t *bs, fu8_t v); -faim_internal int aimbs_put16(aim_bstream_t *bs, fu16_t v); -faim_internal int aimbs_put32(aim_bstream_t *bs, fu32_t v); -faim_internal int aimbs_putle8(aim_bstream_t *bs, fu8_t v); -faim_internal int aimbs_putle16(aim_bstream_t *bs, fu16_t v); -faim_internal int aimbs_putle32(aim_bstream_t *bs, fu32_t v); -faim_internal int aimbs_putraw(aim_bstream_t *bs, const fu8_t *v, int len); -faim_internal int aimbs_putstr(aim_bstream_t *bs, const char *str); -faim_internal int aimbs_putbs(aim_bstream_t *bs, aim_bstream_t *srcbs, int len); -faim_internal int aimbs_putcaps(aim_bstream_t *bs, fu32_t caps); - -/* conn.c */ -faim_internal aim_conn_t *aim_cloneconn(aim_session_t *sess, aim_conn_t *src); - -/* ft.c */ -faim_internal int aim_rxdispatch_rendezvous(aim_session_t *sess, aim_frame_t *fr); - -/* rxhandlers.c */ -faim_internal aim_rxcallback_t aim_callhandler(aim_session_t *sess, aim_conn_t *conn, fu16_t family, fu16_t type); -faim_internal int aim_callhandler_noparam(aim_session_t *sess, aim_conn_t *conn, fu16_t family, fu16_t type, aim_frame_t *ptr); -faim_internal int aim_parse_unknown(aim_session_t *, aim_frame_t *, ...); -faim_internal void aim_clonehandlers(aim_session_t *sess, aim_conn_t *dest, aim_conn_t *src); - -/* rxqueue.c */ -faim_internal int aim_recv(int fd, void *buf, size_t count); -faim_internal int aim_bstream_recv(aim_bstream_t *bs, int fd, size_t count); -faim_internal void aim_rxqueue_cleanbyconn(aim_session_t *sess, aim_conn_t *conn); -faim_internal void aim_frame_destroy(aim_frame_t *); - -/* txqueue.c */ -faim_internal aim_frame_t *aim_tx_new(aim_session_t *sess, aim_conn_t *conn, fu8_t framing, fu16_t chan, int datalen); -faim_internal int aim_tx_enqueue(aim_session_t *, aim_frame_t *); -faim_internal int aim_bstream_send(aim_bstream_t *bs, aim_conn_t *conn, size_t count); -faim_internal int aim_tx_sendframe(aim_session_t *sess, aim_frame_t *cur); -faim_internal void aim_tx_cleanqueue(aim_session_t *, aim_conn_t *); - -/* - * Generic SNAC structure. Rarely if ever used. - */ -typedef struct aim_snac_s { - aim_snacid_t id; - fu16_t family; - fu16_t type; - fu16_t flags; - void *data; - time_t issuetime; - struct aim_snac_s *next; -} aim_snac_t; - -/* snac.c */ -faim_internal void aim_initsnachash(aim_session_t *sess); -faim_internal aim_snacid_t aim_newsnac(aim_session_t *, aim_snac_t *newsnac); -faim_internal aim_snacid_t aim_cachesnac(aim_session_t *sess, const fu16_t family, const fu16_t type, const fu16_t flags, const void *data, const int datalen); -faim_internal aim_snac_t *aim_remsnac(aim_session_t *, aim_snacid_t id); -faim_internal int aim_putsnac(aim_bstream_t *, fu16_t family, fu16_t type, fu16_t flags, aim_snacid_t id); - -/* Stored in ->priv of the service request SNAC for chats. */ -struct chatsnacinfo { - fu16_t exchange; - char name[128]; - fu16_t instance; -}; - -/* - * In SNACland, the terms 'family' and 'group' are synonymous -- the former - * is my term, the latter is AOL's. - */ -struct snacgroup { - fu16_t group; - struct snacgroup *next; -}; - -#ifdef FAIM_NEED_CONN_INTERNAL -struct snacpair { - fu16_t group; - fu16_t subtype; - struct snacpair *next; -}; - -struct rateclass { - fu16_t classid; - fu32_t windowsize; - fu32_t clear; - fu32_t alert; - fu32_t limit; - fu32_t disconnect; - fu32_t current; - fu32_t max; - fu8_t unknown[5]; /* only present in versions >= 3 */ - struct snacpair *members; - struct rateclass *next; -}; -#endif /* FAIM_NEED_CONN_INTERNAL */ - -/* - * This is inside every connection. But it is a void * to anything - * outside of libfaim. It should remain that way. It's called data - * abstraction. Maybe you've heard of it. (Probably not if you're a - * libfaim user.) - * - */ -typedef struct aim_conn_inside_s { - struct snacgroup *groups; - struct rateclass *rates; -} aim_conn_inside_t; - -faim_internal void aim_conn_addgroup(aim_conn_t *conn, fu16_t group); - -faim_internal int aim_cachecookie(aim_session_t *sess, aim_msgcookie_t *cookie); -faim_internal aim_msgcookie_t *aim_uncachecookie(aim_session_t *sess, fu8_t *cookie, int type); -faim_internal aim_msgcookie_t *aim_mkcookie(fu8_t *, int, void *); -faim_internal aim_msgcookie_t *aim_checkcookie(aim_session_t *, const unsigned char *, const int); -faim_internal int aim_freecookie(aim_session_t *sess, aim_msgcookie_t *cookie); -faim_internal int aim_msgcookie_gettype(int reqclass); -faim_internal int aim_cookie_free(aim_session_t *sess, aim_msgcookie_t *cookie); - -/* 0x0002 - locate.c */ -faim_internal void aim_locate_requestuserinfo(aim_session_t *sess, const char *sn); -faim_internal fu32_t aim_locate_getcaps(aim_session_t *sess, aim_bstream_t *bs, int len); -faim_internal fu32_t aim_locate_getcaps_short(aim_session_t *sess, aim_bstream_t *bs, int len); -faim_internal void aim_info_free(aim_userinfo_t *); -faim_internal int aim_info_extract(aim_session_t *sess, aim_bstream_t *bs, aim_userinfo_t *); -faim_internal int aim_putuserinfo(aim_bstream_t *bs, aim_userinfo_t *info); - -faim_internal int aim_chat_readroominfo(aim_bstream_t *bs, struct aim_chat_roominfo *outinfo); - -faim_internal void aim_conn_kill_chat(aim_session_t *sess, aim_conn_t *conn); - -/* 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_rates_addparam(aim_session_t *, aim_conn_t *); -faim_internal int aim_rates_delparam(aim_session_t *, aim_conn_t *); - -#endif /* __AIM_INTERNAL_H__ */ -#endif /* FAIM_INTERNAL */ diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/auth.c --- a/src/protocols/oscar/auth.c Sat Feb 11 19:16:38 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,654 +0,0 @@ -/* - * Family 0x0017 - Authentication. - * - * Deals with the authorizer for SNAC-based login, and also old-style - * non-SNAC login. - * - */ - -#define FAIM_INTERNAL -#include "aim.h" - -#include "cipher.h" - -#include - -#define USE_XOR_FOR_ICQ - -#ifdef USE_XOR_FOR_ICQ -/** - * Encode a password using old XOR method - * - * 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. - * - * @param password Incoming password. - * @param encoded Buffer to put encoded password. - */ -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 - }; - unsigned int i; - - for (i = 0; i < strlen(password); i++) - encoded[i] = (password[i] ^ encoding_table[i]); - - return 0; -} -#endif - -#ifdef USE_OLD_MD5 -static int aim_encode_password_md5(const char *password, const char *key, fu8_t *digest) -{ - GaimCipher *cipher; - GaimCipherContext *context; - - cipher = gaim_ciphers_find_cipher("md5"); - - context = gaim_cipher_context_new(cipher, NULL); - gaim_cipher_context_append(context, (const guchar *)key, strlen(key)); - gaim_cipher_context_append(context, (const guchar *)password, strlen(password)); - gaim_cipher_context_append(context, (const guchar *)AIM_MD5_STRING, strlen(AIM_MD5_STRING)); - gaim_cipher_context_digest(context, 16, digest, NULL); - gaim_cipher_context_destroy(context); - - return 0; -} -#else -static int aim_encode_password_md5(const char *password, const char *key, fu8_t *digest) -{ - GaimCipher *cipher; - GaimCipherContext *context; - guchar passdigest[16]; - - cipher = gaim_ciphers_find_cipher("md5"); - - context = gaim_cipher_context_new(cipher, NULL); - gaim_cipher_context_append(context, (const guchar *)password, strlen(password)); - gaim_cipher_context_digest(context, 16, passdigest, NULL); - gaim_cipher_context_destroy(context); - - context = gaim_cipher_context_new(cipher, NULL); - gaim_cipher_context_append(context, (const guchar *)key, strlen(key)); - gaim_cipher_context_append(context, passdigest, 16); - gaim_cipher_context_append(context, (const guchar *)AIM_MD5_STRING, strlen(AIM_MD5_STRING)); - gaim_cipher_context_digest(context, 16, digest, NULL); - gaim_cipher_context_destroy(context); - - return 0; -} -#endif - -/* - * The FLAP version is sent by itself at the beginning of authorization - * connections. The FLAP version is also sent before the cookie when connecting - * for other services (BOS, chatnav, chat, etc.). - */ -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 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 fu16_t length, const fu8_t *chipsahoy) -{ - aim_frame_t *fr; - aim_tlvlist_t *tl = NULL; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x01, 4+2+2+length))) - return -ENOMEM; - - aimbs_put32(&fr->data, 0x00000001); - aim_tlvlist_add_raw(&tl, 0x0006, length, chipsahoy); - aim_tlvlist_write(&fr->data, &tl); - aim_tlvlist_free(&tl); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -#ifdef USE_XOR_FOR_ICQ -/* - * Part two of the ICQ hack. Note the ignoring of the key. - */ -static int goddamnicq2(aim_session_t *sess, aim_conn_t *conn, const char *sn, const char *password, struct client_info_s *ci) -{ - aim_frame_t *fr; - aim_tlvlist_t *tl = NULL; - int passwdlen; - fu8_t *password_encoded; - - passwdlen = strlen(password); - if (!(password_encoded = (fu8_t *)malloc(passwdlen+1))) - return -ENOMEM; - if (passwdlen > MAXICQPASSLEN) - passwdlen = MAXICQPASSLEN; - - 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); /* FLAP Version */ - aim_tlvlist_add_str(&tl, 0x0001, sn); - aim_tlvlist_add_raw(&tl, 0x0002, passwdlen, password_encoded); - - if (ci->clientstring) - aim_tlvlist_add_str(&tl, 0x0003, ci->clientstring); - aim_tlvlist_add_16(&tl, 0x0016, (fu16_t)ci->clientid); - aim_tlvlist_add_16(&tl, 0x0017, (fu16_t)ci->major); - aim_tlvlist_add_16(&tl, 0x0018, (fu16_t)ci->minor); - aim_tlvlist_add_16(&tl, 0x0019, (fu16_t)ci->point); - aim_tlvlist_add_16(&tl, 0x001a, (fu16_t)ci->build); - aim_tlvlist_add_32(&tl, 0x0014, (fu32_t)ci->distrib); /* distribution chan */ - aim_tlvlist_add_str(&tl, 0x000f, ci->lang); - aim_tlvlist_add_str(&tl, 0x000e, ci->country); - - aim_tlvlist_write(&fr->data, &tl); - - free(password_encoded); - aim_tlvlist_free(&tl); - - aim_tx_enqueue(sess, fr); - - return 0; -} -#endif - -/* - * Subtype 0x0002 - * - * 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. - * - * 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" - * clientid = 0x0001 - * major = 0x0001 - * minor = 0x0001 - * point = (not sent) - * build = 0x0013 - * unknown= (not sent) - * - * AIM for Linux 1.1.112: - * clientstring = "AOL Instant Messenger (SM)" - * clientid = 0x1d09 - * major = 0x0001 - * minor = 0x0001 - * point = 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 *ci, const char *key) -{ - aim_frame_t *fr; - aim_tlvlist_t *tl = NULL; - fu8_t digest[16]; - aim_snacid_t snacid; - - if (!ci || !sn || !password) - return -EINVAL; - -#ifdef USE_XOR_FOR_ICQ - /* If we're signing on an ICQ account then use the older, XOR login method */ - if (isdigit(sn[0])) - return goddamnicq2(sess, conn, sn, password, ci); -#endif - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0017, 0x0002, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0017, 0x0002, 0x0000, snacid); - - aim_tlvlist_add_str(&tl, 0x0001, sn); - - /* Truncate ICQ passwords, if necessary */ - if (isdigit(sn[0]) && (strlen(password) > MAXICQPASSLEN)) - { - char truncated[MAXICQPASSLEN + 1]; - strncpy(truncated, password, MAXICQPASSLEN); - truncated[MAXICQPASSLEN] = 0; - aim_encode_password_md5(truncated, key, digest); - } - else - { - aim_encode_password_md5(password, key, digest); - } - - aim_tlvlist_add_raw(&tl, 0x0025, 16, digest); - -#ifndef USE_OLD_MD5 - aim_tlvlist_add_noval(&tl, 0x004c); -#endif - - if (ci->clientstring) - aim_tlvlist_add_str(&tl, 0x0003, ci->clientstring); - aim_tlvlist_add_16(&tl, 0x0016, (fu16_t)ci->clientid); - aim_tlvlist_add_16(&tl, 0x0017, (fu16_t)ci->major); - aim_tlvlist_add_16(&tl, 0x0018, (fu16_t)ci->minor); - aim_tlvlist_add_16(&tl, 0x0019, (fu16_t)ci->point); - aim_tlvlist_add_16(&tl, 0x001a, (fu16_t)ci->build); - aim_tlvlist_add_32(&tl, 0x0014, (fu32_t)ci->distrib); - aim_tlvlist_add_str(&tl, 0x000f, ci->lang); - aim_tlvlist_add_str(&tl, 0x000e, ci->country); - - /* - * If set, old-fashioned buddy lists will not work. You will need - * to use SSI. - */ - aim_tlvlist_add_8(&tl, 0x004a, 0x01); - - aim_tlvlist_write(&fr->data, &tl); - - aim_tlvlist_free(&tl); - - aim_tx_enqueue(sess, fr); - - 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 - * presence of certain TLVs. - * - * The client should check the value passed as errorcode. If - * its nonzero, there was an error. - */ -static int parse(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) -{ - aim_tlvlist_t *tlvlist; - aim_rxcallback_t userfunc; - struct aim_authresp_info *info; - int ret = 0; - - info = (struct aim_authresp_info *)malloc(sizeof(struct aim_authresp_info)); - memset(info, 0, sizeof(struct aim_authresp_info)); - - /* - * Read block of TLVs. All further data is derived - * from what is parsed here. - */ - tlvlist = aim_tlvlist_read(bs); - - /* - * No matter what, we should have a screen name. - */ - memset(sess->sn, 0, sizeof(sess->sn)); - if (aim_tlv_gettlv(tlvlist, 0x0001, 1)) { - info->sn = aim_tlv_getstr(tlvlist, 0x0001, 1); - strncpy(sess->sn, info->sn, sizeof(sess->sn)); - } - - /* - * Check for an error code. If so, we should also - * have an error url. - */ - if (aim_tlv_gettlv(tlvlist, 0x0008, 1)) - info->errorcode = aim_tlv_get16(tlvlist, 0x0008, 1); - if (aim_tlv_gettlv(tlvlist, 0x0004, 1)) - info->errorurl = aim_tlv_getstr(tlvlist, 0x0004, 1); - - /* - * BOS server address. - */ - if (aim_tlv_gettlv(tlvlist, 0x0005, 1)) - info->bosip = aim_tlv_getstr(tlvlist, 0x0005, 1); - - /* - * Authorization cookie. - */ - if (aim_tlv_gettlv(tlvlist, 0x0006, 1)) { - aim_tlv_t *tmptlv; - - tmptlv = aim_tlv_gettlv(tlvlist, 0x0006, 1); - - info->cookielen = tmptlv->length; - info->cookie = tmptlv->value; - } - - /* - * The email address attached to this account - * Not available for ICQ or @mac.com logins. - * If you receive this TLV, then you are allowed to use - * family 0x0018 to check the status of your email. - * XXX - Not really true! - */ - if (aim_tlv_gettlv(tlvlist, 0x0011, 1)) - info->email = aim_tlv_getstr(tlvlist, 0x0011, 1); - - /* - * The registration status. (Not real sure what it means.) - * Not available for ICQ or @mac.com logins. - * - * 1 = No disclosure - * 2 = Limited disclosure - * 3 = Full disclosure - * - * This has to do with whether your email address is available - * to other users or not. AFAIK, this feature is no longer used. - * - * Means you can use the admin family? (0x0007) - * - */ - if (aim_tlv_gettlv(tlvlist, 0x0013, 1)) - info->regstatus = aim_tlv_get16(tlvlist, 0x0013, 1); - - if (aim_tlv_gettlv(tlvlist, 0x0040, 1)) - info->latestbeta.build = aim_tlv_get32(tlvlist, 0x0040, 1); - if (aim_tlv_gettlv(tlvlist, 0x0041, 1)) - info->latestbeta.url = aim_tlv_getstr(tlvlist, 0x0041, 1); - if (aim_tlv_gettlv(tlvlist, 0x0042, 1)) - info->latestbeta.info = aim_tlv_getstr(tlvlist, 0x0042, 1); - if (aim_tlv_gettlv(tlvlist, 0x0043, 1)) - info->latestbeta.name = aim_tlv_getstr(tlvlist, 0x0043, 1); - -#if 0 - if (aim_tlv_gettlv(tlvlist, 0x0048, 1)) { - /* beta serial */ - } -#endif - - if (aim_tlv_gettlv(tlvlist, 0x0044, 1)) - info->latestrelease.build = aim_tlv_get32(tlvlist, 0x0044, 1); - if (aim_tlv_gettlv(tlvlist, 0x0045, 1)) - info->latestrelease.url = aim_tlv_getstr(tlvlist, 0x0045, 1); - if (aim_tlv_gettlv(tlvlist, 0x0046, 1)) - info->latestrelease.info = aim_tlv_getstr(tlvlist, 0x0046, 1); - if (aim_tlv_gettlv(tlvlist, 0x0047, 1)) - info->latestrelease.name = aim_tlv_getstr(tlvlist, 0x0047, 1); - -#if 0 - if (aim_tlv_gettlv(tlvlist, 0x0049, 1)) { - /* lastest release serial */ - } -#endif - - /* - * URL to change password. - */ - if (aim_tlv_gettlv(tlvlist, 0x0054, 1)) - info->chpassurl = aim_tlv_getstr(tlvlist, 0x0054, 1); - -#if 0 - /* - * Unknown. Seen on an @mac.com screen name with value of 0x003f - */ - if (aim_tlv_gettlv(tlvlist, 0x0055, 1)) { - /* Unhandled */ - } -#endif - - sess->authinfo = info; - - if ((userfunc = aim_callhandler(sess, rx->conn, snac ? snac->family : 0x0017, snac ? snac->subtype : 0x0003))) - ret = userfunc(sess, rx, info); - - aim_tlvlist_free(&tlvlist); - - return ret; -} - -#ifdef USE_XOR_FOR_ICQ -/* - * Subtype 0x0007 (kind of) - Send a fake type 0x0007 SNAC to the client - * - * 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; - - fr.conn = conn; - - if ((userfunc = aim_callhandler(sess, conn, 0x0017, 0x0007))) - userfunc(sess, &fr, ""); - - return 0; -} -#endif - -/* - * Subtype 0x0006 - * - * 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; - -#ifdef USE_XOR_FOR_ICQ - if (isdigit(sn[0])) - return goddamnicq(sess, conn, sn); -#endif - - aim_sendflapver(sess, conn); - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+2+strlen(sn)+8 ))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0017, 0x0006, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0017, 0x0006, 0x0000, snacid); - - aim_tlvlist_add_str(&tl, 0x0001, sn); - - /* Tell the server we support SecurID logins. */ - aim_tlvlist_add_noval(&tl, 0x004b); - - /* Unknown. Sent in recent WinAIM clients.*/ - aim_tlvlist_add_noval(&tl, 0x005a); - - aim_tlvlist_write(&fr->data, &tl); - aim_tlvlist_free(&tl); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * Subtype 0x0007 - * - * Middle handler for 0017/0007 SNACs. Contains the auth key prefixed - * by only its length in a two byte word. - * - * Calls the client, which should then use the value to call aim_send_login. - * - */ -static int keyparse(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) -{ - int keylen, ret = 1; - aim_rxcallback_t userfunc; - char *keystr; - - keylen = aimbs_get16(bs); - keystr = aimbs_getstr(bs, keylen); - - /* XXX - When GiantGrayPanda signed on AIM I got a thing asking me to register - * for the netscape network. This SNAC had a type 0x0058 TLV with length 10. - * Data is 0x0007 0004 3e19 ae1e 0006 0004 0000 0005 */ - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, keystr); - - free(keystr); - - return ret; -} - -/** - * Subtype 0x000a - * - * Receive SecurID request. - */ -static int got_securid_request(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; -} - -/** - * Subtype 0x000b - * - * Send SecurID response. - */ -faim_export int aim_auth_securid_send(aim_session_t *sess, const char *securid) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - int len; - - if (!sess || !(conn = aim_getconn_type_all(sess, AIM_CONN_TYPE_AUTH)) || !securid) - return -EINVAL; - - len = strlen(securid); - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+len))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, AIM_CB_FAM_ATH, AIM_CB_ATH_SECURID_RESPONSE, 0x0000, NULL, 0); - aim_putsnac(&fr->data, AIM_CB_FAM_ATH, AIM_CB_ATH_SECURID_RESPONSE, 0x0000, 0); - - aimbs_put16(&fr->data, len); - aimbs_putstr(&fr->data, securid); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -static void auth_shutdown(aim_session_t *sess, aim_module_t *mod) -{ - if (sess->authinfo) { - free(sess->authinfo->sn); - free(sess->authinfo->bosip); - free(sess->authinfo->errorurl); - free(sess->authinfo->email); - free(sess->authinfo->chpassurl); - free(sess->authinfo->latestrelease.name); - free(sess->authinfo->latestrelease.url); - free(sess->authinfo->latestrelease.info); - free(sess->authinfo->latestbeta.name); - free(sess->authinfo->latestbeta.url); - free(sess->authinfo->latestbeta.info); - free(sess->authinfo); - } -} - -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 parse(sess, mod, rx, snac, bs); - else if (snac->subtype == 0x0007) - return keyparse(sess, mod, rx, snac, bs); - else if (snac->subtype == 0x000a) - return got_securid_request(sess, mod, rx, snac, bs); - - return 0; -} - -faim_internal int auth_modfirst(aim_session_t *sess, aim_module_t *mod) -{ - - mod->family = 0x0017; - mod->version = 0x0000; - mod->flags = 0; - strncpy(mod->name, "auth", sizeof(mod->name)); - mod->snachandler = snachandler; - mod->shutdown = auth_shutdown; - - return 0; -} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/bart.c --- a/src/protocols/oscar/bart.c Sat Feb 11 19:16:38 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,165 +0,0 @@ -/* - * Family 0x0010 - Server stored buddy art - * - * Used for storing and retrieving your cute little buddy icon - * from the AIM servers. - * - */ - -#define FAIM_INTERNAL -#include - -/** - * Subtype 0x0002 - Upload your icon. - * - * @param sess The oscar session. - * @param conn The icon connection for this session. - * @param icon The raw data of the icon image file. - * @param iconlen Length of the raw data of the icon image file. - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_bart_upload(aim_session_t *sess, const fu8_t *icon, fu16_t iconlen) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - - if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0010)) || !icon || !iconlen) - return -EINVAL; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 2 + 2+iconlen))) - return -ENOMEM; - snacid = aim_cachesnac(sess, 0x0010, 0x0002, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0010, 0x0002, 0x0000, snacid); - - /* The reference number for the icon */ - aimbs_put16(&fr->data, 1); - - /* The icon */ - aimbs_put16(&fr->data, iconlen); - aimbs_putraw(&fr->data, icon, iconlen); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/** - * Subtype 0x0003 - Acknowledgement for uploading a buddy icon. - * - * You get this honky after you upload a buddy icon. - */ -static int uploadack(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; - fu16_t something, somethingelse; - fu8_t onemorething; - - something = aimbs_get16(bs); - somethingelse = aimbs_get16(bs); - onemorething = aimbs_get8(bs); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx); - - return ret; -} - -/** - * Subtype 0x0004 - Request someone's icon. - * - * @param sess The oscar session. - * @param conn The icon connection for this session. - * @param sn The screen name of the person who's icon you are requesting. - * @param iconcsum The MD5 checksum of the icon you are requesting. - * @param iconcsumlen Length of the MD5 checksum given above. Should be 10 bytes. - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_bart_request(aim_session_t *sess, const char *sn, fu8_t iconcsumtype, const fu8_t *iconcsum, fu16_t iconcsumlen) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - - if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0010)) || !sn || !strlen(sn) || !iconcsum || !iconcsumlen) - return -EINVAL; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 1+strlen(sn) + 4 + 1+iconcsumlen))) - return -ENOMEM; - snacid = aim_cachesnac(sess, 0x0010, 0x0004, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0010, 0x0004, 0x0000, snacid); - - /* Screen name */ - aimbs_put8(&fr->data, strlen(sn)); - aimbs_putstr(&fr->data, sn); - - /* Some numbers. You like numbers, right? */ - aimbs_put8(&fr->data, 0x01); - aimbs_put16(&fr->data, 0x0001); - aimbs_put8(&fr->data, iconcsumtype); - - /* Icon string */ - aimbs_put8(&fr->data, iconcsumlen); - aimbs_putraw(&fr->data, iconcsum, iconcsumlen); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/** - * Subtype 0x0005 - Receive a buddy icon. - * - * This is sent in response to a buddy icon request. - */ -static int parseicon(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; - char *sn; - fu16_t flags, iconlen; - fu8_t iconcsumtype, iconcsumlen, *iconcsum, *icon; - - sn = aimbs_getstr(bs, aimbs_get8(bs)); - flags = aimbs_get16(bs); - iconcsumtype = aimbs_get8(bs); - iconcsumlen = aimbs_get8(bs); - iconcsum = aimbs_getraw(bs, iconcsumlen); - iconlen = aimbs_get16(bs); - icon = aimbs_getraw(bs, iconlen); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, sn, iconcsumtype, iconcsum, iconcsumlen, icon, iconlen); - - free(sn); - free(iconcsum); - free(icon); - - 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 uploadack(sess, mod, rx, snac, bs); - else if (snac->subtype == 0x0005) - return parseicon(sess, mod, rx, snac, bs); - - return 0; -} - -faim_internal int bart_modfirst(aim_session_t *sess, aim_module_t *mod) -{ - - mod->family = 0x0010; - mod->version = 0x0001; - mod->toolid = 0x0010; - mod->toolversion = 0x0629; - mod->flags = 0; - strncpy(mod->name, "bart", sizeof(mod->name)); - mod->snachandler = snachandler; - - return 0; -} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/bos.c --- a/src/protocols/oscar/bos.c Sat Feb 11 19:16:38 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,167 +0,0 @@ -/* - * Family 0x0009 - Basic Oscar Service. - * - * The functionality of this family has been replaced by SSI. - */ - -#define FAIM_INTERNAL -#include - -#include - -/* Subtype 0x0002 - Request BOS rights. */ -faim_export int aim_bos_reqrights(aim_session_t *sess, aim_conn_t *conn) -{ - return aim_genericreq_n_snacid(sess, conn, 0x0009, 0x0002); -} - -/* Subtype 0x0003 - BOS Rights. */ -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; - aim_tlvlist_t *tlvlist; - fu16_t maxpermits = 0, maxdenies = 0; - int ret = 0; - - /* - * TLVs follow - */ - tlvlist = aim_tlvlist_read(bs); - - /* - * TLV type 0x0001: Maximum number of buddies on permit list. - */ - if (aim_tlv_gettlv(tlvlist, 0x0001, 1)) - maxpermits = aim_tlv_get16(tlvlist, 0x0001, 1); - - /* - * TLV type 0x0002: Maximum number of buddies on deny list. - */ - if (aim_tlv_gettlv(tlvlist, 0x0002, 1)) - maxdenies = aim_tlv_get16(tlvlist, 0x0002, 1); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, maxpermits, maxdenies); - - aim_tlvlist_free(&tlvlist); - - return ret; -} - -/* - * Subtype 0x0004 - Set group permission 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) -{ - return aim_genericreq_l(sess, conn, 0x0009, 0x0004, &mask); -} - -/* - * Stubtypes 0x0005, 0x0006, 0x0007, and 0x0008 - Modify permit/deny lists. - * - * 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_itemindex(localcpy, i, '&'); - - aimbs_put8(&fr->data, strlen(tmpptr)); - aimbs_putstr(&fr->data, 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) -{ - - if (snac->subtype == 0x0003) - return rights(sess, mod, rx, snac, bs); - - return 0; -} - -faim_internal int bos_modfirst(aim_session_t *sess, aim_module_t *mod) -{ - - mod->family = 0x0009; - mod->version = 0x0001; - mod->toolid = 0x0110; - mod->toolversion = 0x0629; - mod->flags = 0; - strncpy(mod->name, "bos", sizeof(mod->name)); - mod->snachandler = snachandler; - - return 0; -} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/bstream.c --- a/src/protocols/oscar/bstream.c Sat Feb 11 19:16:38 2006 +0000 +++ b/src/protocols/oscar/bstream.c Sat Feb 11 21:45:18 2006 +0000 @@ -1,13 +1,30 @@ /* - * bstream.c + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* * This file contains all functions needed to use bstreams. */ -#define FAIM_INTERNAL -#include +#include "oscar.h" -faim_internal int aim_bstream_init(aim_bstream_t *bs, fu8_t *data, int len) +faim_internal int aim_bstream_init(aim_bstream_t *bs, guint8 *data, int len) { if (!bs) @@ -65,7 +82,7 @@ return n; } -faim_internal fu8_t aimbs_get8(aim_bstream_t *bs) +faim_internal guint8 aimbs_get8(aim_bstream_t *bs) { if (aim_bstream_empty(bs) < 1) @@ -76,7 +93,7 @@ return aimutil_get8(bs->data + bs->offset - 1); } -faim_internal fu16_t aimbs_get16(aim_bstream_t *bs) +faim_internal guint16 aimbs_get16(aim_bstream_t *bs) { if (aim_bstream_empty(bs) < 2) @@ -87,7 +104,7 @@ return aimutil_get16(bs->data + bs->offset - 2); } -faim_internal fu32_t aimbs_get32(aim_bstream_t *bs) +faim_internal guint32 aimbs_get32(aim_bstream_t *bs) { if (aim_bstream_empty(bs) < 4) @@ -98,7 +115,7 @@ return aimutil_get32(bs->data + bs->offset - 4); } -faim_internal fu8_t aimbs_getle8(aim_bstream_t *bs) +faim_internal guint8 aimbs_getle8(aim_bstream_t *bs) { if (aim_bstream_empty(bs) < 1) @@ -109,7 +126,7 @@ return aimutil_getle8(bs->data + bs->offset - 1); } -faim_internal fu16_t aimbs_getle16(aim_bstream_t *bs) +faim_internal guint16 aimbs_getle16(aim_bstream_t *bs) { if (aim_bstream_empty(bs) < 2) @@ -120,7 +137,7 @@ return aimutil_getle16(bs->data + bs->offset - 2); } -faim_internal fu32_t aimbs_getle32(aim_bstream_t *bs) +faim_internal guint32 aimbs_getle32(aim_bstream_t *bs) { if (aim_bstream_empty(bs) < 4) @@ -131,7 +148,7 @@ return aimutil_getle32(bs->data + bs->offset - 4); } -faim_internal int aimbs_getrawbuf(aim_bstream_t *bs, fu8_t *buf, int len) +faim_internal int aimbs_getrawbuf(aim_bstream_t *bs, guint8 *buf, int len) { if (aim_bstream_empty(bs) < len) @@ -143,9 +160,9 @@ return len; } -faim_internal fu8_t *aimbs_getraw(aim_bstream_t *bs, int len) +faim_internal guint8 *aimbs_getraw(aim_bstream_t *bs, int len) { - fu8_t *ob; + guint8 *ob; if (!(ob = malloc(len))) return NULL; @@ -165,7 +182,7 @@ if (!(ob = malloc(len + 1))) return NULL; - if (aimbs_getrawbuf(bs, (fu8_t *)ob, len) < len) { + if (aimbs_getrawbuf(bs, (guint8 *)ob, len) < len) { free(ob); return NULL; } @@ -175,7 +192,7 @@ return ob; } -faim_internal int aimbs_put8(aim_bstream_t *bs, fu8_t v) +faim_internal int aimbs_put8(aim_bstream_t *bs, guint8 v) { if (aim_bstream_empty(bs) < 1) @@ -186,7 +203,7 @@ return 1; } -faim_internal int aimbs_put16(aim_bstream_t *bs, fu16_t v) +faim_internal int aimbs_put16(aim_bstream_t *bs, guint16 v) { if (aim_bstream_empty(bs) < 2) @@ -197,7 +214,7 @@ return 2; } -faim_internal int aimbs_put32(aim_bstream_t *bs, fu32_t v) +faim_internal int aimbs_put32(aim_bstream_t *bs, guint32 v) { if (aim_bstream_empty(bs) < 4) @@ -208,7 +225,7 @@ return 1; } -faim_internal int aimbs_putle8(aim_bstream_t *bs, fu8_t v) +faim_internal int aimbs_putle8(aim_bstream_t *bs, guint8 v) { if (aim_bstream_empty(bs) < 1) @@ -219,7 +236,7 @@ return 1; } -faim_internal int aimbs_putle16(aim_bstream_t *bs, fu16_t v) +faim_internal int aimbs_putle16(aim_bstream_t *bs, guint16 v) { if (aim_bstream_empty(bs) < 2) @@ -230,7 +247,7 @@ return 2; } -faim_internal int aimbs_putle32(aim_bstream_t *bs, fu32_t v) +faim_internal int aimbs_putle32(aim_bstream_t *bs, guint32 v) { if (aim_bstream_empty(bs) < 4) @@ -242,7 +259,7 @@ } -faim_internal int aimbs_putraw(aim_bstream_t *bs, const fu8_t *v, int len) +faim_internal int aimbs_putraw(aim_bstream_t *bs, const guint8 *v, int len) { if (aim_bstream_empty(bs) < len) @@ -256,7 +273,7 @@ faim_internal int aimbs_putstr(aim_bstream_t *bs, const char *str) { - return aimbs_putraw(bs, (fu8_t *)str, strlen(str)); + return aimbs_putraw(bs, (guint8 *)str, strlen(str)); } faim_internal int aimbs_putbs(aim_bstream_t *bs, aim_bstream_t *srcbs, int len) diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/buddylist.c --- a/src/protocols/oscar/buddylist.c Sat Feb 11 19:16:38 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,285 +0,0 @@ -/* - * Family 0x0003 - Old-style Buddylist Management (non-SSI). - * - */ - -#define FAIM_INTERNAL -#include - -#include - -/* - * Subtype 0x0002 - Request rights. - * - * Request Buddy List rights. - * - */ -faim_export int aim_buddylist_reqrights(aim_session_t *sess, aim_conn_t *conn) -{ - return aim_genericreq_n_snacid(sess, conn, 0x0003, 0x0002); -} - -/* - * Subtype 0x0003 - Rights. - * - */ -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; - aim_tlvlist_t *tlvlist; - fu16_t maxbuddies = 0, maxwatchers = 0; - int ret = 0; - - /* - * TLVs follow - */ - tlvlist = aim_tlvlist_read(bs); - - /* - * TLV type 0x0001: Maximum number of buddies. - */ - if (aim_tlv_gettlv(tlvlist, 0x0001, 1)) - maxbuddies = aim_tlv_get16(tlvlist, 0x0001, 1); - - /* - * TLV type 0x0002: Maximum number of watchers. - * - * Watchers are other users who have you on their buddy - * list. (This is called the "reverse list" by a certain - * other IM protocol.) - * - */ - if (aim_tlv_gettlv(tlvlist, 0x0002, 1)) - maxwatchers = aim_tlv_get16(tlvlist, 0x0002, 1); - - /* - * TLV type 0x0003: Unknown. - * - * ICQ only? - */ - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, maxbuddies, maxwatchers); - - aim_tlvlist_free(&tlvlist); - - return ret; -} - -/* - * Subtype 0x0004 - Add buddy to list. - * - * Adds a single buddy to your buddy list after login. - * XXX This should just be an extension of setbuddylist() - * - */ -faim_export int aim_buddylist_addbuddy(aim_session_t *sess, aim_conn_t *conn, const char *sn) -{ - aim_frame_t *fr; - aim_snacid_t snacid; - - if (!sn || !strlen(sn)) - return -EINVAL; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0003, 0x0004, 0x0000, sn, strlen(sn)+1); - aim_putsnac(&fr->data, 0x0003, 0x0004, 0x0000, snacid); - - aimbs_put8(&fr->data, strlen(sn)); - aimbs_putstr(&fr->data, sn); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * Subtype 0x0004 - Add multiple buddies to your buddy list. - * - * This just builds the "set buddy list" command then queues it. - * - * buddy_list = "Screen Name One&ScreenNameTwo&"; - * - * XXX Clean this up. - * - */ -faim_export int aim_buddylist_set(aim_session_t *sess, aim_conn_t *conn, const char *buddy_list) -{ - aim_frame_t *fr; - aim_snacid_t snacid; - int len = 0; - char *localcpy = NULL; - char *tmpptr = NULL; - - if (!buddy_list || !(localcpy = strdup(buddy_list))) - return -EINVAL; - - for (tmpptr = strtok(localcpy, "&"); tmpptr; ) { - gaim_debug_misc("oscar", "---adding: %s (%d)\n", tmpptr, strlen(tmpptr)); - len += 1 + strlen(tmpptr); - tmpptr = strtok(NULL, "&"); - } - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+len))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0003, 0x0004, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0003, 0x0004, 0x0000, snacid); - - strncpy(localcpy, buddy_list, strlen(buddy_list) + 1); - - for (tmpptr = strtok(localcpy, "&"); tmpptr; ) { - - gaim_debug_misc("oscar", "---adding: %s (%d)\n", tmpptr, strlen(tmpptr)); - - aimbs_put8(&fr->data, strlen(tmpptr)); - aimbs_putstr(&fr->data, tmpptr); - tmpptr = strtok(NULL, "&"); - } - - aim_tx_enqueue(sess, fr); - - free(localcpy); - - return 0; -} - -/* - * Subtype 0x0005 - Remove buddy from list. - * - * XXX generalise to support removing multiple buddies (basically, its - * the same as setbuddylist() but with a different snac subtype). - * - */ -faim_export int aim_buddylist_removebuddy(aim_session_t *sess, aim_conn_t *conn, const char *sn) -{ - aim_frame_t *fr; - aim_snacid_t snacid; - - if (!sn || !strlen(sn)) - return -EINVAL; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0003, 0x0005, 0x0000, sn, strlen(sn)+1); - aim_putsnac(&fr->data, 0x0003, 0x0005, 0x0000, snacid); - - aimbs_put8(&fr->data, strlen(sn)); - aimbs_putstr(&fr->data, sn); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * Subtype 0x000b - * - * XXX Why would we send this? - * - */ -faim_export int aim_buddylist_oncoming(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *info) -{ - aim_frame_t *fr; - aim_snacid_t snacid; - - if (!sess || !conn || !info) - return -EINVAL; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0003, 0x000b, 0x0000, NULL, 0); - - aim_putsnac(&fr->data, 0x0003, 0x000b, 0x0000, snacid); - aim_putuserinfo(&fr->data, info); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * Subtype 0x000c - * - * XXX Why would we send this? - * - */ -faim_export int aim_buddylist_offgoing(aim_session_t *sess, aim_conn_t *conn, const char *sn) -{ - aim_frame_t *fr; - aim_snacid_t snacid; - - if (!sess || !conn || !sn) - return -EINVAL; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0003, 0x000c, 0x0000, NULL, 0); - - aim_putsnac(&fr->data, 0x0003, 0x000c, 0x0000, snacid); - aimbs_put8(&fr->data, strlen(sn)); - aimbs_putstr(&fr->data, sn); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * Subtypes 0x000b and 0x000c - Change in buddy status - * - * Oncoming Buddy notifications contain a subset of the - * user information structure. It's close enough to run - * through aim_info_extract() however. - * - * Although the offgoing notification contains no information, - * it is still in a format parsable by aim_info_extract(). - * - */ -static int buddychange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) -{ - int ret = 0; - aim_userinfo_t userinfo; - aim_rxcallback_t userfunc; - - aim_info_extract(sess, bs, &userinfo); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, &userinfo); - - if (snac->subtype == 0x000b) - aim_locate_requestuserinfo(sess, userinfo.sn); - aim_info_free(&userinfo); - - 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 rights(sess, mod, rx, snac, bs); - else if ((snac->subtype == 0x000b) || (snac->subtype == 0x000c)) - return buddychange(sess, mod, rx, snac, bs); - - return 0; -} - -faim_internal int buddylist_modfirst(aim_session_t *sess, aim_module_t *mod) -{ - - mod->family = 0x0003; - mod->version = 0x0001; - mod->toolid = 0x0110; - mod->toolversion = 0x0629; - mod->flags = 0; - strncpy(mod->name, "buddylist", sizeof(mod->name)); - mod->snachandler = snachandler; - - return 0; -} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/chat.c --- a/src/protocols/oscar/chat.c Sat Feb 11 19:16:38 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,582 +0,0 @@ -/* - * Family 0x000e - Routines for the Chat service. - * - */ - -#define FAIM_INTERNAL -#include - -#include - -/* Stored in the ->internal of chat connections */ -struct chatconnpriv { - fu16_t exchange; - char *name; - fu16_t instance; -}; - -faim_internal void aim_conn_kill_chat(aim_session_t *sess, aim_conn_t *conn) -{ - struct chatconnpriv *ccp = (struct chatconnpriv *)conn->internal; - - if (ccp) - free(ccp->name); - free(ccp); - - return; -} - -faim_export char *aim_chat_getname(aim_conn_t *conn) -{ - struct chatconnpriv *ccp; - - if (!conn) - return NULL; - - if (conn->type != AIM_CONN_TYPE_CHAT) - return NULL; - - ccp = (struct chatconnpriv *)conn->internal; - - return ccp->name; -} - -/* XXX get this into conn.c -- evil!! */ -faim_export aim_conn_t *aim_chat_getconn(aim_session_t *sess, const char *name) -{ - aim_conn_t *cur; - - for (cur = sess->connlist; cur; cur = cur->next) { - struct chatconnpriv *ccp = (struct chatconnpriv *)cur->internal; - - if (cur->type != AIM_CONN_TYPE_CHAT) - continue; - if (!cur->internal) { - gaim_debug_misc("oscar", "faim: chat: chat connection with no name! (fd = %d)\n", cur->fd); - continue; - } - - if (strcmp(ccp->name, name) == 0) - break; - } - - return cur; -} - -faim_export int aim_chat_attachname(aim_conn_t *conn, fu16_t exchange, const char *roomname, fu16_t instance) -{ - struct chatconnpriv *ccp; - - if (!conn || !roomname) - return -EINVAL; - - if (conn->internal) - free(conn->internal); - - if (!(ccp = malloc(sizeof(struct chatconnpriv)))) - return -ENOMEM; - - ccp->exchange = exchange; - ccp->name = strdup(roomname); - ccp->instance = instance; - - conn->internal = (void *)ccp; - - return 0; -} - -faim_internal int aim_chat_readroominfo(aim_bstream_t *bs, struct aim_chat_roominfo *outinfo) -{ - int namelen; - - if (!bs || !outinfo) - return 0; - - outinfo->exchange = aimbs_get16(bs); - namelen = aimbs_get8(bs); - outinfo->name = aimbs_getstr(bs, namelen); - outinfo->instance = aimbs_get16(bs); - - return 0; -} - -faim_export int aim_chat_leaveroom(aim_session_t *sess, const char *name) -{ - aim_conn_t *conn; - - if (!(conn = aim_chat_getconn(sess, name))) - return -ENOENT; - - aim_conn_close(conn); - - return 0; -} - -/* - * Subtype 0x0002 - General room information. Lots of stuff. - * - * Values I know are in here but I haven't attached - * them to any of the 'Unknown's: - * - Language (English) - * - */ -static int infoupdate(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) -{ - aim_userinfo_t *userinfo = NULL; - aim_rxcallback_t userfunc; - int ret = 0; - int usercount = 0; - fu8_t detaillevel = 0; - char *roomname = NULL; - struct aim_chat_roominfo roominfo; - fu16_t tlvcount = 0; - aim_tlvlist_t *tlvlist; - char *roomdesc = NULL; - fu16_t flags = 0; - fu32_t creationtime = 0; - fu16_t maxmsglen = 0, maxvisiblemsglen = 0; - fu16_t unknown_d2 = 0, unknown_d5 = 0; - - aim_chat_readroominfo(bs, &roominfo); - - detaillevel = aimbs_get8(bs); - - if (detaillevel != 0x02) { - gaim_debug_misc("oscar", "faim: chat_roomupdateinfo: detail level %d not supported\n", detaillevel); - return 1; - } - - tlvcount = aimbs_get16(bs); - - /* - * Everything else are TLVs. - */ - tlvlist = aim_tlvlist_read(bs); - - /* - * TLV type 0x006a is the room name in Human Readable Form. - */ - if (aim_tlv_gettlv(tlvlist, 0x006a, 1)) - roomname = aim_tlv_getstr(tlvlist, 0x006a, 1); - - /* - * Type 0x006f: Number of occupants. - */ - if (aim_tlv_gettlv(tlvlist, 0x006f, 1)) - usercount = aim_tlv_get16(tlvlist, 0x006f, 1); - - /* - * Type 0x0073: Occupant list. - */ - if (aim_tlv_gettlv(tlvlist, 0x0073, 1)) { - int curoccupant = 0; - aim_tlv_t *tmptlv; - aim_bstream_t occbs; - - tmptlv = aim_tlv_gettlv(tlvlist, 0x0073, 1); - - /* Allocate enough userinfo structs for all occupants */ - userinfo = calloc(usercount, sizeof(aim_userinfo_t)); - - aim_bstream_init(&occbs, tmptlv->value, tmptlv->length); - - while (curoccupant < usercount) - aim_info_extract(sess, &occbs, &userinfo[curoccupant++]); - } - - /* - * Type 0x00c9: Flags. (AIM_CHATROOM_FLAG) - */ - if (aim_tlv_gettlv(tlvlist, 0x00c9, 1)) - flags = aim_tlv_get16(tlvlist, 0x00c9, 1); - - /* - * Type 0x00ca: Creation time (4 bytes) - */ - if (aim_tlv_gettlv(tlvlist, 0x00ca, 1)) - creationtime = aim_tlv_get32(tlvlist, 0x00ca, 1); - - /* - * Type 0x00d1: Maximum Message Length - */ - if (aim_tlv_gettlv(tlvlist, 0x00d1, 1)) - maxmsglen = aim_tlv_get16(tlvlist, 0x00d1, 1); - - /* - * Type 0x00d2: Unknown. (2 bytes) - */ - if (aim_tlv_gettlv(tlvlist, 0x00d2, 1)) - unknown_d2 = aim_tlv_get16(tlvlist, 0x00d2, 1); - - /* - * Type 0x00d3: Room Description - */ - if (aim_tlv_gettlv(tlvlist, 0x00d3, 1)) - roomdesc = aim_tlv_getstr(tlvlist, 0x00d3, 1); - -#if 0 - /* - * Type 0x000d4: Unknown (flag only) - */ - if (aim_tlv_gettlv(tlvlist, 0x000d4, 1)) { - /* Unhandled */ - } -#endif - - /* - * Type 0x00d5: Unknown. (1 byte) - */ - if (aim_tlv_gettlv(tlvlist, 0x00d5, 1)) - unknown_d5 = aim_tlv_get8(tlvlist, 0x00d5, 1); - -#if 0 - /* - * Type 0x00d6: Encoding 1 ("us-ascii") - */ - if (aim_tlv_gettlv(tlvlist, 0x000d6, 1)) { - /* Unhandled */ - } - - /* - * Type 0x00d7: Language 1 ("en") - */ - if (aim_tlv_gettlv(tlvlist, 0x000d7, 1)) { - /* Unhandled */ - } - - /* - * Type 0x00d8: Encoding 2 ("us-ascii") - */ - if (aim_tlv_gettlv(tlvlist, 0x000d8, 1)) { - /* Unhandled */ - } - - /* - * Type 0x00d9: Language 2 ("en") - */ - if (aim_tlv_gettlv(tlvlist, 0x000d9, 1)) { - /* Unhandled */ - } -#endif - - /* - * Type 0x00da: Maximum visible message length - */ - if (aim_tlv_gettlv(tlvlist, 0x000da, 1)) - maxvisiblemsglen = aim_tlv_get16(tlvlist, 0x00da, 1); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) { - ret = userfunc(sess, - rx, - &roominfo, - roomname, - usercount, - userinfo, - roomdesc, - flags, - creationtime, - maxmsglen, - unknown_d2, - unknown_d5, - maxvisiblemsglen); - } - - free(roominfo.name); - - while (usercount > 0) - aim_info_free(&userinfo[--usercount]); - - free(userinfo); - free(roomname); - free(roomdesc); - aim_tlvlist_free(&tlvlist); - - return ret; -} - -/* Subtypes 0x0003 and 0x0004 */ -static int userlistchange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) -{ - aim_userinfo_t *userinfo = NULL; - aim_rxcallback_t userfunc; - int curcount = 0, ret = 0; - - while (aim_bstream_empty(bs)) { - curcount++; - userinfo = realloc(userinfo, curcount * sizeof(aim_userinfo_t)); - aim_info_extract(sess, bs, &userinfo[curcount-1]); - } - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, curcount, userinfo); - - aim_info_free(userinfo); - free(userinfo); - - return ret; -} - -/* - * Subtype 0x0005 - Send a Chat Message. - * - * Possible flags: - * AIM_CHATFLAGS_NOREFLECT -- Unset the flag that requests messages - * should be sent to their sender. - * AIM_CHATFLAGS_AWAY -- Mark the message as an autoresponse - * (Note that WinAIM does not honor this, - * and displays the message as normal.) - * - * XXX convert this to use tlvchains - */ -faim_export int aim_chat_send_im(aim_session_t *sess, aim_conn_t *conn, fu16_t flags, const gchar *msg, int msglen, const char *encoding, const char *language) -{ - int i; - aim_frame_t *fr; - aim_msgcookie_t *cookie; - aim_snacid_t snacid; - fu8_t ckstr[8]; - aim_tlvlist_t *otl = NULL, *itl = NULL; - - if (!sess || !conn || !msg || (msglen <= 0)) - return 0; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x000e, 0x0005, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x000e, 0x0005, 0x0000, snacid); - - /* - * Cookie - * - * XXX mkcookie should generate the cookie and cache it in one - * operation to preserve uniqueness. - */ - for (i = 0; i < 8; i++) - ckstr[i] = (fu8_t)rand(); - - cookie = aim_mkcookie(ckstr, AIM_COOKIETYPE_CHAT, NULL); - cookie->data = NULL; /* XXX store something useful here */ - - aim_cachecookie(sess, cookie); - - /* ICBM Header */ - aimbs_putraw(&fr->data, ckstr, 8); /* Cookie */ - aimbs_put16(&fr->data, 0x0003); /* Channel */ - - /* - * Type 1: Flag meaning this message is destined to the room. - */ - aim_tlvlist_add_noval(&otl, 0x0001); - - /* - * Type 6: Reflect - */ - if (!(flags & AIM_CHATFLAGS_NOREFLECT)) - aim_tlvlist_add_noval(&otl, 0x0006); - - /* - * Type 7: Autoresponse - */ - if (flags & AIM_CHATFLAGS_AWAY) - aim_tlvlist_add_noval(&otl, 0x0007); - - /* - * SubTLV: Type 1: Message - */ - aim_tlvlist_add_raw(&itl, 0x0001, msglen, (guchar *)msg); - - /* - * SubTLV: Type 2: Encoding - */ - if (encoding != NULL) - aim_tlvlist_add_str(&itl, 0x0002, encoding); - - /* - * SubTLV: Type 3: Language - */ - if (language != NULL) - aim_tlvlist_add_str(&itl, 0x0003, language); - - /* - * Type 5: Message block. Contains more TLVs. - * - * This could include other information... We just - * put in a message TLV however. - * - */ - aim_tlvlist_add_frozentlvlist(&otl, 0x0005, &itl); - - aim_tlvlist_write(&fr->data, &otl); - - aim_tlvlist_free(&itl); - aim_tlvlist_free(&otl); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * Subtype 0x0006 - * - * We could probably include this in the normal ICBM parsing - * code as channel 0x0003, however, since only the start - * would be the same, we might as well do it here. - * - * General outline of this SNAC: - * snac - * cookie - * channel id - * tlvlist - * unknown - * source user info - * name - * evility - * userinfo tlvs - * online time - * etc - * message metatlv - * message tlv - * message string - * possibly others - * - */ -static int incomingim_ch3(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) -{ - int ret = 0, i; - aim_rxcallback_t userfunc; - aim_userinfo_t userinfo; - fu8_t cookie[8]; - fu16_t channel; - aim_tlvlist_t *otl; - char *msg = NULL; - int len = 0; - char *encoding = NULL, *language = NULL; - aim_msgcookie_t *ck; - - memset(&userinfo, 0, sizeof(aim_userinfo_t)); - - /* - * Read ICBM Cookie. - */ - for (i = 0; i < 8; i++) - cookie[i] = aimbs_get8(bs); - - if ((ck = aim_uncachecookie(sess, cookie, AIM_COOKIETYPE_CHAT))) { - free(ck->data); - free(ck); - } - - /* - * Channel ID - * - * Channel 0x0003 is used for chat messages. - * - */ - channel = aimbs_get16(bs); - - if (channel != 0x0003) { - gaim_debug_misc("oscar", "faim: chat_incoming: unknown channel! (0x%04x)\n", channel); - return 0; - } - - /* - * Start parsing TLVs right away. - */ - otl = aim_tlvlist_read(bs); - - /* - * Type 0x0003: Source User Information - */ - if (aim_tlv_gettlv(otl, 0x0003, 1)) { - aim_tlv_t *userinfotlv; - aim_bstream_t tbs; - - userinfotlv = aim_tlv_gettlv(otl, 0x0003, 1); - - aim_bstream_init(&tbs, userinfotlv->value, userinfotlv->length); - aim_info_extract(sess, &tbs, &userinfo); - } - -#if 0 - /* - * Type 0x0001: If present, it means it was a message to the - * room (as opposed to a whisper). - */ - if (aim_tlv_gettlv(otl, 0x0001, 1)) { - /* Unhandled */ - } -#endif - - /* - * Type 0x0005: Message Block. Conains more TLVs. - */ - if (aim_tlv_gettlv(otl, 0x0005, 1)) { - aim_tlvlist_t *itl; - aim_tlv_t *msgblock; - aim_bstream_t tbs; - - msgblock = aim_tlv_gettlv(otl, 0x0005, 1); - aim_bstream_init(&tbs, msgblock->value, msgblock->length); - itl = aim_tlvlist_read(&tbs); - - /* - * Type 0x0001: Message. - */ - if (aim_tlv_gettlv(itl, 0x0001, 1)) { - msg = aim_tlv_getstr(itl, 0x0001, 1); - len = aim_tlv_gettlv(itl, 0x0001, 1)->length; - } - - /* - * Type 0x0002: Encoding. - */ - if (aim_tlv_gettlv(itl, 0x0002, 1)) - encoding = aim_tlv_getstr(itl, 0x0002, 1); - - /* - * Type 0x0003: Language. - */ - if (aim_tlv_gettlv(itl, 0x0003, 1)) - language = aim_tlv_getstr(itl, 0x0003, 1); - - aim_tlvlist_free(&itl); - } - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, &userinfo, len, msg, encoding, language); - - aim_info_free(&userinfo); - free(msg); - aim_tlvlist_free(&otl); - - 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 == 0x0002) - return infoupdate(sess, mod, rx, snac, bs); - else if ((snac->subtype == 0x0003) || (snac->subtype == 0x0004)) - return userlistchange(sess, mod, rx, snac, bs); - else if (snac->subtype == 0x0006) - return incomingim_ch3(sess, mod, rx, snac, bs); - - return 0; -} - -faim_internal int chat_modfirst(aim_session_t *sess, aim_module_t *mod) -{ - - mod->family = 0x000e; - mod->version = 0x0001; - mod->toolid = 0x0010; - mod->toolversion = 0x0629; - mod->flags = 0; - strncpy(mod->name, "chat", sizeof(mod->name)); - mod->snachandler = snachandler; - - return 0; -} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/chatnav.c --- a/src/protocols/oscar/chatnav.c Sat Feb 11 19:16:38 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,451 +0,0 @@ -/* - * Family 0x000d - Handle ChatNav. - * - * The ChatNav(igation) service does various things to keep chat - * alive. It provides room information, room searching and creating, - * as well as giving users the right ("permission") to use chat. - * - */ - -#define FAIM_INTERNAL -#include - -/* - * Subtype 0x0002 - * - * conn must be a chatnav connection! - * - */ -faim_export int aim_chatnav_reqrights(aim_session_t *sess, aim_conn_t *conn) -{ - return aim_genericreq_n_snacid(sess, conn, 0x000d, 0x0002); -} - -/* - * Subtype 0x0008 - */ -faim_export int aim_chatnav_createroom(aim_session_t *sess, aim_conn_t *conn, const char *name, fu16_t exchange) -{ - static const char ck[] = {"create"}; - static const char lang[] = {"en"}; - static const char charset[] = {"us-ascii"}; - aim_frame_t *fr; - aim_snacid_t snacid; - aim_tlvlist_t *tl = NULL; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x000d, 0x0008, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x000d, 0x0008, 0x0000, snacid); - - /* exchange */ - aimbs_put16(&fr->data, exchange); - - /* - * This looks to be a big hack. You'll note that this entire - * SNAC is just a room info structure, but the hard room name, - * here, is set to "create". - * - * Either this goes on the "list of questions concerning - * why-the-hell-did-you-do-that", or this value is completely - * ignored. Without experimental evidence, but a good knowledge of - * AOL style, I'm going to guess that it is the latter, and that - * the value of the room name in create requests is ignored. - */ - aimbs_put8(&fr->data, strlen(ck)); - aimbs_putstr(&fr->data, ck); - - /* - * instance - * - * Setting this to 0xffff apparently assigns the last instance. - * - */ - aimbs_put16(&fr->data, 0xffff); - - /* detail level */ - aimbs_put8(&fr->data, 0x01); - - aim_tlvlist_add_str(&tl, 0x00d3, name); - aim_tlvlist_add_str(&tl, 0x00d6, charset); - aim_tlvlist_add_str(&tl, 0x00d7, lang); - - /* tlvcount */ - aimbs_put16(&fr->data, aim_tlvlist_count(&tl)); - aim_tlvlist_write(&fr->data, &tl); - - aim_tlvlist_free(&tl); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -static int parseinfo_perms(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs, aim_snac_t *snac2) -{ - aim_rxcallback_t userfunc; - int ret = 0; - struct aim_chat_exchangeinfo *exchanges = NULL; - int curexchange; - aim_tlv_t *exchangetlv; - fu8_t maxrooms = 0; - aim_tlvlist_t *tlvlist, *innerlist; - - tlvlist = aim_tlvlist_read(bs); - - /* - * Type 0x0002: Maximum concurrent rooms. - */ - if (aim_tlv_gettlv(tlvlist, 0x0002, 1)) - maxrooms = aim_tlv_get8(tlvlist, 0x0002, 1); - - /* - * Type 0x0003: Exchange information - * - * There can be any number of these, each one - * representing another exchange. - * - */ - for (curexchange = 0; ((exchangetlv = aim_tlv_gettlv(tlvlist, 0x0003, curexchange+1))); ) { - aim_bstream_t tbs; - - aim_bstream_init(&tbs, exchangetlv->value, exchangetlv->length); - - curexchange++; - - exchanges = realloc(exchanges, curexchange * sizeof(struct aim_chat_exchangeinfo)); - - /* exchange number */ - exchanges[curexchange-1].number = aimbs_get16(&tbs); - innerlist = aim_tlvlist_read(&tbs); - -#if 0 - /* - * Type 0x000a: Unknown. - * - * Usually three bytes: 0x0114 (exchange 1) or 0x010f (others). - * - */ - if (aim_tlv_gettlv(innerlist, 0x000a, 1)) { - /* Unhandled */ - } - - /* - * Type 0x000d: Unknown. - */ - if (aim_tlv_gettlv(innerlist, 0x000d, 1)) { - /* Unhandled */ - } - - /* - * Type 0x0004: Unknown - */ - if (aim_tlv_gettlv(innerlist, 0x0004, 1)) { - /* Unhandled */ - } -#endif - - /* - * Type 0x0002: Unknown - */ - if (aim_tlv_gettlv(innerlist, 0x0002, 1)) { - fu16_t classperms; - - classperms = aim_tlv_get16(innerlist, 0x0002, 1); - - gaim_debug_misc("oscar", "faim: class permissions %x\n", classperms); - } - - /* - * Type 0x00c9: Flags - * - * 1 Evilable - * 2 Nav Only - * 4 Instancing Allowed - * 8 Occupant Peek Allowed - * - */ - if (aim_tlv_gettlv(innerlist, 0x00c9, 1)) - exchanges[curexchange-1].flags = aim_tlv_get16(innerlist, 0x00c9, 1); - -#if 0 - /* - * Type 0x00ca: Creation Date - */ - if (aim_tlv_gettlv(innerlist, 0x00ca, 1)) { - /* Unhandled */ - } - - /* - * Type 0x00d0: Mandatory Channels? - */ - if (aim_tlv_gettlv(innerlist, 0x00d0, 1)) { - /* Unhandled */ - } - - /* - * Type 0x00d1: Maximum Message length - */ - if (aim_tlv_gettlv(innerlist, 0x00d1, 1)) { - /* Unhandled */ - } - - /* - * Type 0x00d2: Maximum Occupancy? - */ - if (aim_tlv_gettlv(innerlist, 0x00d2, 1)) { - /* Unhandled */ - } -#endif - - /* - * Type 0x00d3: Exchange Description - */ - if (aim_tlv_gettlv(innerlist, 0x00d3, 1)) - exchanges[curexchange-1].name = aim_tlv_getstr(innerlist, 0x00d3, 1); - else - exchanges[curexchange-1].name = NULL; - -#if 0 - /* - * Type 0x00d4: Exchange Description URL - */ - if (aim_tlv_gettlv(innerlist, 0x00d4, 1)) { - /* Unhandled */ - } -#endif - - /* - * Type 0x00d5: Creation Permissions - * - * 0 Creation not allowed - * 1 Room creation allowed - * 2 Exchange creation allowed - * - */ - if (aim_tlv_gettlv(innerlist, 0x00d5, 1)) { - fu8_t createperms; - - createperms = aim_tlv_get8(innerlist, 0x00d5, 1); - } - - /* - * Type 0x00d6: Character Set (First Time) - */ - if (aim_tlv_gettlv(innerlist, 0x00d6, 1)) - exchanges[curexchange-1].charset1 = aim_tlv_getstr(innerlist, 0x00d6, 1); - else - exchanges[curexchange-1].charset1 = NULL; - - /* - * Type 0x00d7: Language (First Time) - */ - if (aim_tlv_gettlv(innerlist, 0x00d7, 1)) - exchanges[curexchange-1].lang1 = aim_tlv_getstr(innerlist, 0x00d7, 1); - else - exchanges[curexchange-1].lang1 = NULL; - - /* - * Type 0x00d8: Character Set (Second Time) - */ - if (aim_tlv_gettlv(innerlist, 0x00d8, 1)) - exchanges[curexchange-1].charset2 = aim_tlv_getstr(innerlist, 0x00d8, 1); - else - exchanges[curexchange-1].charset2 = NULL; - - /* - * Type 0x00d9: Language (Second Time) - */ - if (aim_tlv_gettlv(innerlist, 0x00d9, 1)) - exchanges[curexchange-1].lang2 = aim_tlv_getstr(innerlist, 0x00d9, 1); - else - exchanges[curexchange-1].lang2 = NULL; - -#if 0 - /* - * Type 0x00da: Unknown - */ - if (aim_tlv_gettlv(innerlist, 0x00da, 1)) { - /* Unhandled */ - } -#endif - - aim_tlvlist_free(&innerlist); - } - - /* - * Call client. - */ - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, snac2->type, maxrooms, curexchange, exchanges); - - for (curexchange--; curexchange >= 0; curexchange--) { - free(exchanges[curexchange].name); - free(exchanges[curexchange].charset1); - free(exchanges[curexchange].lang1); - free(exchanges[curexchange].charset2); - free(exchanges[curexchange].lang2); - } - free(exchanges); - aim_tlvlist_free(&tlvlist); - - return ret; -} - -static int parseinfo_create(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs, aim_snac_t *snac2) -{ - aim_rxcallback_t userfunc; - aim_tlvlist_t *tlvlist, *innerlist; - char *ck = NULL, *fqcn = NULL, *name = NULL; - fu16_t exchange = 0, instance = 0, unknown = 0, flags = 0, maxmsglen = 0, maxoccupancy = 0; - fu32_t createtime = 0; - fu8_t createperms = 0, detaillevel; - int cklen; - aim_tlv_t *bigblock; - int ret = 0; - aim_bstream_t bbbs; - - tlvlist = aim_tlvlist_read(bs); - - if (!(bigblock = aim_tlv_gettlv(tlvlist, 0x0004, 1))) { - gaim_debug_misc("oscar", "no bigblock in top tlv in create room response\n"); - aim_tlvlist_free(&tlvlist); - return 0; - } - - aim_bstream_init(&bbbs, bigblock->value, bigblock->length); - - exchange = aimbs_get16(&bbbs); - cklen = aimbs_get8(&bbbs); - ck = aimbs_getstr(&bbbs, cklen); - instance = aimbs_get16(&bbbs); - detaillevel = aimbs_get8(&bbbs); - - if (detaillevel != 0x02) { - gaim_debug_misc("oscar", "unknown detaillevel in create room response (0x%02x)\n", detaillevel); - aim_tlvlist_free(&tlvlist); - free(ck); - return 0; - } - - unknown = aimbs_get16(&bbbs); - - innerlist = aim_tlvlist_read(&bbbs); - - if (aim_tlv_gettlv(innerlist, 0x006a, 1)) - fqcn = aim_tlv_getstr(innerlist, 0x006a, 1); - - if (aim_tlv_gettlv(innerlist, 0x00c9, 1)) - flags = aim_tlv_get16(innerlist, 0x00c9, 1); - - if (aim_tlv_gettlv(innerlist, 0x00ca, 1)) - createtime = aim_tlv_get32(innerlist, 0x00ca, 1); - - if (aim_tlv_gettlv(innerlist, 0x00d1, 1)) - maxmsglen = aim_tlv_get16(innerlist, 0x00d1, 1); - - if (aim_tlv_gettlv(innerlist, 0x00d2, 1)) - maxoccupancy = aim_tlv_get16(innerlist, 0x00d2, 1); - - if (aim_tlv_gettlv(innerlist, 0x00d3, 1)) - name = aim_tlv_getstr(innerlist, 0x00d3, 1); - - if (aim_tlv_gettlv(innerlist, 0x00d5, 1)) - createperms = aim_tlv_get8(innerlist, 0x00d5, 1); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) { - ret = userfunc(sess, rx, snac2->type, fqcn, instance, exchange, flags, createtime, maxmsglen, maxoccupancy, createperms, unknown, name, ck); - } - - free(ck); - free(name); - free(fqcn); - aim_tlvlist_free(&innerlist); - aim_tlvlist_free(&tlvlist); - - return ret; -} - -/* - * Subtype 0x0009 - * - * Since multiple things can trigger this callback, we must lookup the - * snacid to determine the original snac subtype that was called. - * - * XXX This isn't really how this works. But this is: Every d/9 response - * has a 16bit value at the beginning. That matches to: - * Short Desc = 1 - * Full Desc = 2 - * Instance Info = 4 - * Nav Short Desc = 8 - * Nav Instance Info = 16 - * And then everything is really asynchronous. There is no specific - * attachment of a response to a create room request, for example. Creating - * the room yields no different a response than requesting the room's info. - * - */ -static int parseinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) -{ - aim_snac_t *snac2; - int ret = 0; - - if (!(snac2 = aim_remsnac(sess, snac->id))) { - gaim_debug_misc("oscar", "faim: chatnav_parse_info: received response to unknown request! (%08lx)\n", snac->id); - return 0; - } - - if (snac2->family != 0x000d) { - gaim_debug_misc("oscar", "faim: chatnav_parse_info: recieved response that maps to corrupt request! (fam=%04x)\n", snac2->family); - return 0; - } - - /* - * We now know what the original SNAC subtype was. - */ - if (snac2->type == 0x0002) /* request chat rights */ - ret = parseinfo_perms(sess, mod, rx, snac, bs, snac2); - else if (snac2->type == 0x0003) /* request exchange info */ - gaim_debug_misc("oscar", "chatnav_parse_info: resposne to exchange info\n"); - else if (snac2->type == 0x0004) /* request room info */ - gaim_debug_misc("oscar", "chatnav_parse_info: response to room info\n"); - else if (snac2->type == 0x0005) /* request more room info */ - gaim_debug_misc("oscar", "chatnav_parse_info: response to more room info\n"); - else if (snac2->type == 0x0006) /* request occupant list */ - gaim_debug_misc("oscar", "chatnav_parse_info: response to occupant info\n"); - else if (snac2->type == 0x0007) /* search for a room */ - gaim_debug_misc("oscar", "chatnav_parse_info: search results\n"); - else if (snac2->type == 0x0008) /* create room */ - ret = parseinfo_create(sess, mod, rx, snac, bs, snac2); - else - gaim_debug_misc("oscar", "chatnav_parse_info: unknown request subtype (%04x)\n", snac2->type); - - if (snac2) - free(snac2->data); - free(snac2); - - 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 == 0x0009) - return parseinfo(sess, mod, rx, snac, bs); - - return 0; -} - -faim_internal int chatnav_modfirst(aim_session_t *sess, aim_module_t *mod) -{ - - mod->family = 0x000d; - mod->version = 0x0001; - mod->toolid = 0x0010; - mod->toolversion = 0x0629; - mod->flags = 0; - strncpy(mod->name, "chatnav", sizeof(mod->name)); - mod->snachandler = snachandler; - - return 0; -} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/conn.c --- a/src/protocols/oscar/conn.c Sat Feb 11 19:16:38 2006 +0000 +++ b/src/protocols/oscar/conn.c Sat Feb 11 21:45:18 2006 +0000 @@ -1,3 +1,23 @@ +/* + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + /** * Low-level connection handling. * @@ -5,11 +25,9 @@ * */ -#define FAIM_INTERNAL -#define FAIM_NEED_CONN_INTERNAL -#include +#include "oscar.h" -/* This is defined in aim.h, but only when !FAIM_INTERNAL, since the rest of +/* This is defined in oscar.h, but only when !FAIM_INTERNAL, since the rest of * the library is not allowed to call it. */ faim_export void aim_conn_kill(aim_session_t *sess, aim_conn_t **deadconn); @@ -78,7 +96,7 @@ * about such inane things. * */ -faim_internal void aim_conn_addgroup(aim_conn_t *conn, fu16_t group) +faim_internal void aim_conn_addgroup(aim_conn_t *conn, guint16 group) { aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside; struct snacgroup *sg; @@ -95,7 +113,7 @@ return; } -faim_export aim_conn_t *aim_conn_findbygroup(aim_session_t *sess, fu16_t group) +faim_export aim_conn_t *aim_conn_findbygroup(aim_session_t *sess, guint16 group) { aim_conn_t *cur; @@ -519,7 +537,7 @@ * @param sess Session to initialize. * @param nonblocking Set to true if you want connections to be non-blocking. */ -faim_export void aim_session_init(aim_session_t *sess, fu8_t nonblocking) +faim_export void aim_session_init(aim_session_t *sess, guint8 nonblocking) { if (!sess) diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/email.c --- a/src/protocols/oscar/email.c Sat Feb 11 19:16:38 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,216 +0,0 @@ -/* - * Family 0x0018 - Email notification - * - * Used for being alerted when the email address(es) associated with - * your screen name get new electronic-m. For normal AIM accounts, you - * get the email address screenname@netscape.net. AOL accounts have - * screenname@aol.com, and can also activate a netscape.net account. - * - */ - -#define FAIM_INTERNAL -#include - -/** - * Subtype 0x0006 - Request information about your email account - * - * @param sess The oscar session. - * @param conn The email connection for this session. - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_email_sendcookies(aim_session_t *sess) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - - if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_EML))) - return -EINVAL; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+16+16))) - return -ENOMEM; - snacid = aim_cachesnac(sess, 0x0018, 0x0006, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0018, 0x0006, 0x0000, snacid); - - /* Number of cookies to follow */ - aimbs_put16(&fr->data, 0x0002); - - /* Cookie */ - aimbs_put16(&fr->data, 0x5d5e); - aimbs_put16(&fr->data, 0x1708); - aimbs_put16(&fr->data, 0x55aa); - aimbs_put16(&fr->data, 0x11d3); - aimbs_put16(&fr->data, 0xb143); - aimbs_put16(&fr->data, 0x0060); - aimbs_put16(&fr->data, 0xb0fb); - aimbs_put16(&fr->data, 0x1ecb); - - /* Cookie */ - aimbs_put16(&fr->data, 0xb380); - aimbs_put16(&fr->data, 0x9ad8); - aimbs_put16(&fr->data, 0x0dba); - aimbs_put16(&fr->data, 0x11d5); - aimbs_put16(&fr->data, 0x9f8a); - aimbs_put16(&fr->data, 0x0060); - aimbs_put16(&fr->data, 0xb0ee); - aimbs_put16(&fr->data, 0x0631); - - aim_tx_enqueue(sess, fr); - - return 0; -} - - -/** - * Subtype 0x0007 - Receive information about your email account - * - * So I don't even know if you can have multiple 16 byte keys, - * but this is coded so it will handle that, and handle it well. - * This tells you if you have unread mail or not, the URL you - * should use to access that mail, and the domain name for the - * email account (screenname@domainname.com). If this is the - * first 0x0007 SNAC you've received since you signed on, or if - * this is just a periodic status update, this will also contain - * the number of unread emails that you have. - */ -static int parseinfo(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; - struct aim_emailinfo *new; - aim_tlvlist_t *tlvlist; - fu8_t *cookie8, *cookie16; - int tmp, havenewmail = 0; /* Used to tell the client we have _new_ mail */ - - char *alertitle = NULL, *alerturl = NULL; - - cookie8 = aimbs_getraw(bs, 8); /* Possibly the code used to log you in to mail? */ - cookie16 = aimbs_getraw(bs, 16); /* Mail cookie sent above */ - - /* See if we already have some info associated with this cookie */ - for (new = sess->emailinfo; (new && memcmp(cookie16, new->cookie16, 16)); new = new->next); - if (new) { - /* Free some of the old info, if it exists */ - free(new->cookie8); - free(new->cookie16); - free(new->url); - free(new->domain); - } else { - /* We don't already have info, so create a new struct for it */ - if (!(new = malloc(sizeof(struct aim_emailinfo)))) - return -ENOMEM; - memset(new, 0, sizeof(struct aim_emailinfo)); - new->next = sess->emailinfo; - sess->emailinfo = new; - } - - new->cookie8 = cookie8; - new->cookie16 = cookie16; - - tlvlist = aim_tlvlist_readnum(bs, aimbs_get16(bs)); - - tmp = aim_tlv_get16(tlvlist, 0x0080, 1); - if (tmp) { - if (new->nummsgs < tmp) - havenewmail = 1; - new->nummsgs = tmp; - } else { - /* If they don't send a 0x0080 TLV, it means we definitely have new mail */ - /* (ie. this is not just another status update) */ - havenewmail = 1; - new->nummsgs++; /* We know we have at least 1 new email */ - } - new->url = aim_tlv_getstr(tlvlist, 0x0007, 1); - if (!(new->unread = aim_tlv_get8(tlvlist, 0x0081, 1))) { - havenewmail = 0; - new->nummsgs = 0; - } - new->domain = aim_tlv_getstr(tlvlist, 0x0082, 1); - new->flag = aim_tlv_get16(tlvlist, 0x0084, 1); - - alertitle = aim_tlv_getstr(tlvlist, 0x0005, 1); - alerturl = aim_tlv_getstr(tlvlist, 0x000d, 1); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, new, havenewmail, alertitle, (alerturl ? alerturl + 2 : NULL)); - - aim_tlvlist_free(&tlvlist); - - free(alertitle); - free(alerturl); - - return ret; -} - -/** - * Subtype 0x0016 - Send something or other - * - * @param sess The oscar session. - * @param conn The email connection for this session. - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_email_activate(aim_session_t *sess) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - - if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_EML))) - return -EINVAL; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+16))) - return -ENOMEM; - snacid = aim_cachesnac(sess, 0x0018, 0x0016, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0018, 0x0016, 0x0000, snacid); - - /* I would guess this tells AIM that you want updates for your mail accounts */ - /* ...but I really have no idea */ - aimbs_put8(&fr->data, 0x02); - aimbs_put32(&fr->data, 0x04000000); - aimbs_put32(&fr->data, 0x04000000); - aimbs_put32(&fr->data, 0x04000000); - aimbs_put32(&fr->data, 0x00000000); - - 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 == 0x0007) - return parseinfo(sess, mod, rx, snac, bs); - - return 0; -} - -static void email_shutdown(aim_session_t *sess, aim_module_t *mod) -{ - while (sess->emailinfo) { - struct aim_emailinfo *tmp = sess->emailinfo; - sess->emailinfo = sess->emailinfo->next; - free(tmp->cookie16); - free(tmp->cookie8); - free(tmp->url); - free(tmp->domain); - free(tmp); - } - - return; -} - -faim_internal int email_modfirst(aim_session_t *sess, aim_module_t *mod) -{ - - mod->family = 0x0018; - mod->version = 0x0001; - mod->toolid = 0x0010; - mod->toolversion = 0x0629; - mod->flags = 0; - strncpy(mod->name, "email", sizeof(mod->name)); - mod->snachandler = snachandler; - mod->shutdown = email_shutdown; - - return 0; -} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/family_admin.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/oscar/family_admin.c Sat Feb 11 21:45:18 2006 +0000 @@ -0,0 +1,257 @@ +/* + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * Family 0x0007 - Account Administration. + * + * Used for stuff like changing the formating of your screen name, changing your + * email address, requesting an account confirmation email, getting account info, + * + */ + +#include "oscar.h" + +/* + * Subtype 0x0002 - Request a bit of account info. + * + * Info should be one of the following: + * 0x0001 - Screen name formatting + * 0x0011 - Email address + * 0x0013 - Unknown + * + */ +faim_export int aim_admin_getinfo(aim_session_t *sess, aim_conn_t *conn, guint16 info) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 14))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0007, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0007, 0x0002, 0x0000, snacid); + + aimbs_put16(&fr->data, info); + aimbs_put16(&fr->data, 0x0000); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtypes 0x0003 and 0x0005 - Parse account info. + * + * Called in reply to both an information request (subtype 0x0002) and + * an information change (subtype 0x0004). + * + */ +static int infochange(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 *url=NULL, *sn=NULL, *email=NULL; + guint16 perms, tlvcount, err=0; + + perms = aimbs_get16(bs); + tlvcount = aimbs_get16(bs); + + while (tlvcount && aim_bstream_empty(bs)) { + guint16 type, length; + + type = aimbs_get16(bs); + length = aimbs_get16(bs); + + switch (type) { + case 0x0001: { + sn = aimbs_getstr(bs, length); + } break; + + case 0x0004: { + url = aimbs_getstr(bs, length); + } break; + + case 0x0008: { + err = aimbs_get16(bs); + } break; + + case 0x0011: { + if (length == 0) { + email = (char*)malloc(13*sizeof(char)); + strcpy(email, "*suppressed*"); + } else + email = aimbs_getstr(bs, length); + } break; + } + + tlvcount--; + } + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + userfunc(sess, rx, (snac->subtype == 0x0005) ? 1 : 0, perms, err, url, sn, email); + + free(sn); + free(url); + free(email); + + return 1; +} + +/* + * Subtype 0x0004 - Set screenname formatting. + * + */ +faim_export int aim_admin_setnick(aim_session_t *sess, aim_conn_t *conn, const char *newnick) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+2+strlen(newnick)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0007, 0x0004, 0x0000, snacid); + + aim_tlvlist_add_str(&tl, 0x0001, newnick); + + aim_tlvlist_write(&fr->data, &tl); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + + return 0; +} + +/* + * Subtype 0x0004 - Change password. + * + */ +faim_export int aim_admin_changepasswd(aim_session_t *sess, aim_conn_t *conn, const char *newpw, const char *curpw) +{ + aim_frame_t *fr; + aim_tlvlist_t *tl = NULL; + aim_snacid_t snacid; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4+strlen(curpw)+4+strlen(newpw)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0007, 0x0004, 0x0000, snacid); + + /* new password TLV t(0002) */ + aim_tlvlist_add_str(&tl, 0x0002, newpw); + + /* current password TLV t(0012) */ + aim_tlvlist_add_str(&tl, 0x0012, curpw); + + aim_tlvlist_write(&fr->data, &tl); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0004 - Change email address. + * + */ +faim_export int aim_admin_setemail(aim_session_t *sess, aim_conn_t *conn, const char *newemail) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+2+strlen(newemail)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0007, 0x0004, 0x0000, snacid); + + aim_tlvlist_add_str(&tl, 0x0011, newemail); + + aim_tlvlist_write(&fr->data, &tl); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0006 - Request account confirmation. + * + * This will cause an email to be sent to the address associated with + * the account. By following the instructions in the mail, you can + * get the TRIAL flag removed from your account. + * + */ +faim_export int aim_admin_reqconfirm(aim_session_t *sess, aim_conn_t *conn) +{ + return aim_genericreq_n(sess, conn, 0x0007, 0x0006); +} + +/* + * Subtype 0x0007 - Account confirmation request acknowledgement. + * + */ +static int accountconfirm(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; + guint16 status; + aim_tlvlist_t *tl; + + status = aimbs_get16(bs); + /* This is 0x0013 if unable to confirm at this time */ + + tl = aim_tlvlist_read(bs); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, status); + + 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) || (snac->subtype == 0x0005)) + return infochange(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0007) + return accountconfirm(sess, mod, rx, snac, bs); + + return 0; +} + +faim_internal int admin_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x0007; + mod->version = 0x0001; + mod->toolid = 0x0010; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "admin", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/family_advert.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/oscar/family_advert.c Sat Feb 11 21:45:18 2006 +0000 @@ -0,0 +1,50 @@ +/* + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * Family 0x0005 - Advertisements. + * + */ + +#include "oscar.h" + +faim_export int aim_ads_requestads(aim_session_t *sess, aim_conn_t *conn) +{ + return aim_genericreq_n(sess, conn, 0x0005, 0x0002); +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + return 0; +} + +faim_internal int adverts_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x0005; + mod->version = 0x0001; + mod->toolid = 0x0001; + mod->toolversion = 0x0001; + mod->flags = 0; + strncpy(mod->name, "advert", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/family_alert.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/oscar/family_alert.c Sat Feb 11 21:45:18 2006 +0000 @@ -0,0 +1,235 @@ +/* + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * Family 0x0018 - Email notification + * + * Used for being alerted when the email address(es) associated with + * your screen name get new electronic-m. For normal AIM accounts, you + * get the email address screenname@netscape.net. AOL accounts have + * screenname@aol.com, and can also activate a netscape.net account. + * + */ + +#include "oscar.h" + +/** + * Subtype 0x0006 - Request information about your email account + * + * @param sess The oscar session. + * @param conn The email connection for this session. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_email_sendcookies(aim_session_t *sess) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_ALERT))) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+16+16))) + return -ENOMEM; + snacid = aim_cachesnac(sess, 0x0018, 0x0006, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0018, 0x0006, 0x0000, snacid); + + /* Number of cookies to follow */ + aimbs_put16(&fr->data, 0x0002); + + /* Cookie */ + aimbs_put16(&fr->data, 0x5d5e); + aimbs_put16(&fr->data, 0x1708); + aimbs_put16(&fr->data, 0x55aa); + aimbs_put16(&fr->data, 0x11d3); + aimbs_put16(&fr->data, 0xb143); + aimbs_put16(&fr->data, 0x0060); + aimbs_put16(&fr->data, 0xb0fb); + aimbs_put16(&fr->data, 0x1ecb); + + /* Cookie */ + aimbs_put16(&fr->data, 0xb380); + aimbs_put16(&fr->data, 0x9ad8); + aimbs_put16(&fr->data, 0x0dba); + aimbs_put16(&fr->data, 0x11d5); + aimbs_put16(&fr->data, 0x9f8a); + aimbs_put16(&fr->data, 0x0060); + aimbs_put16(&fr->data, 0xb0ee); + aimbs_put16(&fr->data, 0x0631); + + aim_tx_enqueue(sess, fr); + + return 0; +} + + +/** + * Subtype 0x0007 - Receive information about your email account + * + * So I don't even know if you can have multiple 16 byte keys, + * but this is coded so it will handle that, and handle it well. + * This tells you if you have unread mail or not, the URL you + * should use to access that mail, and the domain name for the + * email account (screenname@domainname.com). If this is the + * first 0x0007 SNAC you've received since you signed on, or if + * this is just a periodic status update, this will also contain + * the number of unread emails that you have. + */ +static int parseinfo(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; + struct aim_emailinfo *new; + aim_tlvlist_t *tlvlist; + guint8 *cookie8, *cookie16; + int tmp, havenewmail = 0; /* Used to tell the client we have _new_ mail */ + + char *alertitle = NULL, *alerturl = NULL; + + cookie8 = aimbs_getraw(bs, 8); /* Possibly the code used to log you in to mail? */ + cookie16 = aimbs_getraw(bs, 16); /* Mail cookie sent above */ + + /* See if we already have some info associated with this cookie */ + for (new = sess->emailinfo; (new && memcmp(cookie16, new->cookie16, 16)); new = new->next); + if (new) { + /* Free some of the old info, if it exists */ + free(new->cookie8); + free(new->cookie16); + free(new->url); + free(new->domain); + } else { + /* We don't already have info, so create a new struct for it */ + if (!(new = malloc(sizeof(struct aim_emailinfo)))) + return -ENOMEM; + memset(new, 0, sizeof(struct aim_emailinfo)); + new->next = sess->emailinfo; + sess->emailinfo = new; + } + + new->cookie8 = cookie8; + new->cookie16 = cookie16; + + tlvlist = aim_tlvlist_readnum(bs, aimbs_get16(bs)); + + tmp = aim_tlv_get16(tlvlist, 0x0080, 1); + if (tmp) { + if (new->nummsgs < tmp) + havenewmail = 1; + new->nummsgs = tmp; + } else { + /* If they don't send a 0x0080 TLV, it means we definitely have new mail */ + /* (ie. this is not just another status update) */ + havenewmail = 1; + new->nummsgs++; /* We know we have at least 1 new email */ + } + new->url = aim_tlv_getstr(tlvlist, 0x0007, 1); + if (!(new->unread = aim_tlv_get8(tlvlist, 0x0081, 1))) { + havenewmail = 0; + new->nummsgs = 0; + } + new->domain = aim_tlv_getstr(tlvlist, 0x0082, 1); + new->flag = aim_tlv_get16(tlvlist, 0x0084, 1); + + alertitle = aim_tlv_getstr(tlvlist, 0x0005, 1); + alerturl = aim_tlv_getstr(tlvlist, 0x000d, 1); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, new, havenewmail, alertitle, (alerturl ? alerturl + 2 : NULL)); + + aim_tlvlist_free(&tlvlist); + + free(alertitle); + free(alerturl); + + return ret; +} + +/** + * Subtype 0x0016 - Send something or other + * + * @param sess The oscar session. + * @param conn The email connection for this session. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_email_activate(aim_session_t *sess) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_ALERT))) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+16))) + return -ENOMEM; + snacid = aim_cachesnac(sess, 0x0018, 0x0016, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0018, 0x0016, 0x0000, snacid); + + /* I would guess this tells AIM that you want updates for your mail accounts */ + /* ...but I really have no idea */ + aimbs_put8(&fr->data, 0x02); + aimbs_put32(&fr->data, 0x04000000); + aimbs_put32(&fr->data, 0x04000000); + aimbs_put32(&fr->data, 0x04000000); + aimbs_put32(&fr->data, 0x00000000); + + 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 == 0x0007) + return parseinfo(sess, mod, rx, snac, bs); + + return 0; +} + +static void email_shutdown(aim_session_t *sess, aim_module_t *mod) +{ + while (sess->emailinfo) { + struct aim_emailinfo *tmp = sess->emailinfo; + sess->emailinfo = sess->emailinfo->next; + free(tmp->cookie16); + free(tmp->cookie8); + free(tmp->url); + free(tmp->domain); + free(tmp); + } + + return; +} + +faim_internal int email_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x0018; + mod->version = 0x0001; + mod->toolid = 0x0010; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "alert", sizeof(mod->name)); + mod->snachandler = snachandler; + mod->shutdown = email_shutdown; + + return 0; +} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/family_auth.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/oscar/family_auth.c Sat Feb 11 21:45:18 2006 +0000 @@ -0,0 +1,673 @@ +/* + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * Family 0x0017 - Authentication. + * + * Deals with the authorizer for SNAC-based login, and also old-style + * non-SNAC login. + * + */ + +#include "oscar.h" + +#include "cipher.h" + +#include + +#define USE_XOR_FOR_ICQ + +#ifdef USE_XOR_FOR_ICQ +/** + * Encode a password using old XOR method + * + * 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. + * + * @param password Incoming password. + * @param encoded Buffer to put encoded password. + */ +static int aim_encode_password(const char *password, guint8 *encoded) +{ + guint8 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 + }; + unsigned int i; + + for (i = 0; i < strlen(password); i++) + encoded[i] = (password[i] ^ encoding_table[i]); + + return 0; +} +#endif + +#ifdef USE_OLD_MD5 +static int aim_encode_password_md5(const char *password, const char *key, guint8 *digest) +{ + GaimCipher *cipher; + GaimCipherContext *context; + + cipher = gaim_ciphers_find_cipher("md5"); + + context = gaim_cipher_context_new(cipher, NULL); + gaim_cipher_context_append(context, (const guchar *)key, strlen(key)); + gaim_cipher_context_append(context, (const guchar *)password, strlen(password)); + gaim_cipher_context_append(context, (const guchar *)AIM_MD5_STRING, strlen(AIM_MD5_STRING)); + gaim_cipher_context_digest(context, 16, digest, NULL); + gaim_cipher_context_destroy(context); + + return 0; +} +#else +static int aim_encode_password_md5(const char *password, const char *key, guint8 *digest) +{ + GaimCipher *cipher; + GaimCipherContext *context; + guchar passdigest[16]; + + cipher = gaim_ciphers_find_cipher("md5"); + + context = gaim_cipher_context_new(cipher, NULL); + gaim_cipher_context_append(context, (const guchar *)password, strlen(password)); + gaim_cipher_context_digest(context, 16, passdigest, NULL); + gaim_cipher_context_destroy(context); + + context = gaim_cipher_context_new(cipher, NULL); + gaim_cipher_context_append(context, (const guchar *)key, strlen(key)); + gaim_cipher_context_append(context, passdigest, 16); + gaim_cipher_context_append(context, (const guchar *)AIM_MD5_STRING, strlen(AIM_MD5_STRING)); + gaim_cipher_context_digest(context, 16, digest, NULL); + gaim_cipher_context_destroy(context); + + return 0; +} +#endif + +/* + * The FLAP version is sent by itself at the beginning of authorization + * connections. The FLAP version is also sent before the cookie when connecting + * for other services (BOS, chatnav, chat, etc.). + */ +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 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 guint16 length, const guint8 *chipsahoy) +{ + aim_frame_t *fr; + aim_tlvlist_t *tl = NULL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x01, 4+2+2+length))) + return -ENOMEM; + + aimbs_put32(&fr->data, 0x00000001); + aim_tlvlist_add_raw(&tl, 0x0006, length, chipsahoy); + aim_tlvlist_write(&fr->data, &tl); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +#ifdef USE_XOR_FOR_ICQ +/* + * Part two of the ICQ hack. Note the ignoring of the key. + */ +static int goddamnicq2(aim_session_t *sess, aim_conn_t *conn, const char *sn, const char *password, struct client_info_s *ci) +{ + aim_frame_t *fr; + aim_tlvlist_t *tl = NULL; + int passwdlen; + guint8 *password_encoded; + + passwdlen = strlen(password); + if (!(password_encoded = (guint8 *)malloc(passwdlen+1))) + return -ENOMEM; + if (passwdlen > MAXICQPASSLEN) + passwdlen = MAXICQPASSLEN; + + 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); /* FLAP Version */ + aim_tlvlist_add_str(&tl, 0x0001, sn); + aim_tlvlist_add_raw(&tl, 0x0002, passwdlen, password_encoded); + + if (ci->clientstring) + aim_tlvlist_add_str(&tl, 0x0003, ci->clientstring); + aim_tlvlist_add_16(&tl, 0x0016, (guint16)ci->clientid); + aim_tlvlist_add_16(&tl, 0x0017, (guint16)ci->major); + aim_tlvlist_add_16(&tl, 0x0018, (guint16)ci->minor); + aim_tlvlist_add_16(&tl, 0x0019, (guint16)ci->point); + aim_tlvlist_add_16(&tl, 0x001a, (guint16)ci->build); + aim_tlvlist_add_32(&tl, 0x0014, (guint32)ci->distrib); /* distribution chan */ + aim_tlvlist_add_str(&tl, 0x000f, ci->lang); + aim_tlvlist_add_str(&tl, 0x000e, ci->country); + + aim_tlvlist_write(&fr->data, &tl); + + free(password_encoded); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} +#endif + +/* + * Subtype 0x0002 + * + * 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. + * + * 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" + * clientid = 0x0001 + * major = 0x0001 + * minor = 0x0001 + * point = (not sent) + * build = 0x0013 + * unknown= (not sent) + * + * AIM for Linux 1.1.112: + * clientstring = "AOL Instant Messenger (SM)" + * clientid = 0x1d09 + * major = 0x0001 + * minor = 0x0001 + * point = 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 *ci, const char *key) +{ + aim_frame_t *fr; + aim_tlvlist_t *tl = NULL; + guint8 digest[16]; + aim_snacid_t snacid; + + if (!ci || !sn || !password) + return -EINVAL; + +#ifdef USE_XOR_FOR_ICQ + /* If we're signing on an ICQ account then use the older, XOR login method */ + if (isdigit(sn[0])) + return goddamnicq2(sess, conn, sn, password, ci); +#endif + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0017, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0017, 0x0002, 0x0000, snacid); + + aim_tlvlist_add_str(&tl, 0x0001, sn); + + /* Truncate ICQ passwords, if necessary */ + if (isdigit(sn[0]) && (strlen(password) > MAXICQPASSLEN)) + { + char truncated[MAXICQPASSLEN + 1]; + strncpy(truncated, password, MAXICQPASSLEN); + truncated[MAXICQPASSLEN] = 0; + aim_encode_password_md5(truncated, key, digest); + } + else + { + aim_encode_password_md5(password, key, digest); + } + + aim_tlvlist_add_raw(&tl, 0x0025, 16, digest); + +#ifndef USE_OLD_MD5 + aim_tlvlist_add_noval(&tl, 0x004c); +#endif + + if (ci->clientstring) + aim_tlvlist_add_str(&tl, 0x0003, ci->clientstring); + aim_tlvlist_add_16(&tl, 0x0016, (guint16)ci->clientid); + aim_tlvlist_add_16(&tl, 0x0017, (guint16)ci->major); + aim_tlvlist_add_16(&tl, 0x0018, (guint16)ci->minor); + aim_tlvlist_add_16(&tl, 0x0019, (guint16)ci->point); + aim_tlvlist_add_16(&tl, 0x001a, (guint16)ci->build); + aim_tlvlist_add_32(&tl, 0x0014, (guint32)ci->distrib); + aim_tlvlist_add_str(&tl, 0x000f, ci->lang); + aim_tlvlist_add_str(&tl, 0x000e, ci->country); + + /* + * If set, old-fashioned buddy lists will not work. You will need + * to use SSI. + */ + aim_tlvlist_add_8(&tl, 0x004a, 0x01); + + aim_tlvlist_write(&fr->data, &tl); + + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + 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 + * presence of certain TLVs. + * + * The client should check the value passed as errorcode. If + * its nonzero, there was an error. + */ +static int parse(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_tlvlist_t *tlvlist; + aim_rxcallback_t userfunc; + struct aim_authresp_info *info; + int ret = 0; + + info = (struct aim_authresp_info *)malloc(sizeof(struct aim_authresp_info)); + memset(info, 0, sizeof(struct aim_authresp_info)); + + /* + * Read block of TLVs. All further data is derived + * from what is parsed here. + */ + tlvlist = aim_tlvlist_read(bs); + + /* + * No matter what, we should have a screen name. + */ + memset(sess->sn, 0, sizeof(sess->sn)); + if (aim_tlv_gettlv(tlvlist, 0x0001, 1)) { + info->sn = aim_tlv_getstr(tlvlist, 0x0001, 1); + strncpy(sess->sn, info->sn, sizeof(sess->sn)); + } + + /* + * Check for an error code. If so, we should also + * have an error url. + */ + if (aim_tlv_gettlv(tlvlist, 0x0008, 1)) + info->errorcode = aim_tlv_get16(tlvlist, 0x0008, 1); + if (aim_tlv_gettlv(tlvlist, 0x0004, 1)) + info->errorurl = aim_tlv_getstr(tlvlist, 0x0004, 1); + + /* + * BOS server address. + */ + if (aim_tlv_gettlv(tlvlist, 0x0005, 1)) + info->bosip = aim_tlv_getstr(tlvlist, 0x0005, 1); + + /* + * Authorization cookie. + */ + if (aim_tlv_gettlv(tlvlist, 0x0006, 1)) { + aim_tlv_t *tmptlv; + + tmptlv = aim_tlv_gettlv(tlvlist, 0x0006, 1); + + info->cookielen = tmptlv->length; + info->cookie = tmptlv->value; + } + + /* + * The email address attached to this account + * Not available for ICQ or @mac.com logins. + * If you receive this TLV, then you are allowed to use + * family 0x0018 to check the status of your email. + * XXX - Not really true! + */ + if (aim_tlv_gettlv(tlvlist, 0x0011, 1)) + info->email = aim_tlv_getstr(tlvlist, 0x0011, 1); + + /* + * The registration status. (Not real sure what it means.) + * Not available for ICQ or @mac.com logins. + * + * 1 = No disclosure + * 2 = Limited disclosure + * 3 = Full disclosure + * + * This has to do with whether your email address is available + * to other users or not. AFAIK, this feature is no longer used. + * + * Means you can use the admin family? (0x0007) + * + */ + if (aim_tlv_gettlv(tlvlist, 0x0013, 1)) + info->regstatus = aim_tlv_get16(tlvlist, 0x0013, 1); + + if (aim_tlv_gettlv(tlvlist, 0x0040, 1)) + info->latestbeta.build = aim_tlv_get32(tlvlist, 0x0040, 1); + if (aim_tlv_gettlv(tlvlist, 0x0041, 1)) + info->latestbeta.url = aim_tlv_getstr(tlvlist, 0x0041, 1); + if (aim_tlv_gettlv(tlvlist, 0x0042, 1)) + info->latestbeta.info = aim_tlv_getstr(tlvlist, 0x0042, 1); + if (aim_tlv_gettlv(tlvlist, 0x0043, 1)) + info->latestbeta.name = aim_tlv_getstr(tlvlist, 0x0043, 1); + +#if 0 + if (aim_tlv_gettlv(tlvlist, 0x0048, 1)) { + /* beta serial */ + } +#endif + + if (aim_tlv_gettlv(tlvlist, 0x0044, 1)) + info->latestrelease.build = aim_tlv_get32(tlvlist, 0x0044, 1); + if (aim_tlv_gettlv(tlvlist, 0x0045, 1)) + info->latestrelease.url = aim_tlv_getstr(tlvlist, 0x0045, 1); + if (aim_tlv_gettlv(tlvlist, 0x0046, 1)) + info->latestrelease.info = aim_tlv_getstr(tlvlist, 0x0046, 1); + if (aim_tlv_gettlv(tlvlist, 0x0047, 1)) + info->latestrelease.name = aim_tlv_getstr(tlvlist, 0x0047, 1); + +#if 0 + if (aim_tlv_gettlv(tlvlist, 0x0049, 1)) { + /* lastest release serial */ + } +#endif + + /* + * URL to change password. + */ + if (aim_tlv_gettlv(tlvlist, 0x0054, 1)) + info->chpassurl = aim_tlv_getstr(tlvlist, 0x0054, 1); + +#if 0 + /* + * Unknown. Seen on an @mac.com screen name with value of 0x003f + */ + if (aim_tlv_gettlv(tlvlist, 0x0055, 1)) { + /* Unhandled */ + } +#endif + + sess->authinfo = info; + + if ((userfunc = aim_callhandler(sess, rx->conn, snac ? snac->family : 0x0017, snac ? snac->subtype : 0x0003))) + ret = userfunc(sess, rx, info); + + aim_tlvlist_free(&tlvlist); + + return ret; +} + +#ifdef USE_XOR_FOR_ICQ +/* + * Subtype 0x0007 (kind of) - Send a fake type 0x0007 SNAC to the client + * + * 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; + + fr.conn = conn; + + if ((userfunc = aim_callhandler(sess, conn, 0x0017, 0x0007))) + userfunc(sess, &fr, ""); + + return 0; +} +#endif + +/* + * Subtype 0x0006 + * + * 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; + +#ifdef USE_XOR_FOR_ICQ + if (isdigit(sn[0])) + return goddamnicq(sess, conn, sn); +#endif + + aim_sendflapver(sess, conn); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+2+strlen(sn)+8 ))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0017, 0x0006, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0017, 0x0006, 0x0000, snacid); + + aim_tlvlist_add_str(&tl, 0x0001, sn); + + /* Tell the server we support SecurID logins. */ + aim_tlvlist_add_noval(&tl, 0x004b); + + /* Unknown. Sent in recent WinAIM clients.*/ + aim_tlvlist_add_noval(&tl, 0x005a); + + aim_tlvlist_write(&fr->data, &tl); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0007 + * + * Middle handler for 0017/0007 SNACs. Contains the auth key prefixed + * by only its length in a two byte word. + * + * Calls the client, which should then use the value to call aim_send_login. + * + */ +static int keyparse(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int keylen, ret = 1; + aim_rxcallback_t userfunc; + char *keystr; + + keylen = aimbs_get16(bs); + keystr = aimbs_getstr(bs, keylen); + + /* XXX - When GiantGrayPanda signed on AIM I got a thing asking me to register + * for the netscape network. This SNAC had a type 0x0058 TLV with length 10. + * Data is 0x0007 0004 3e19 ae1e 0006 0004 0000 0005 */ + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, keystr); + + free(keystr); + + return ret; +} + +/** + * Subtype 0x000a + * + * Receive SecurID request. + */ +static int got_securid_request(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; +} + +/** + * Subtype 0x000b + * + * Send SecurID response. + */ +faim_export int aim_auth_securid_send(aim_session_t *sess, const char *securid) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + int len; + + if (!sess || !(conn = aim_getconn_type_all(sess, AIM_CONN_TYPE_AUTH)) || !securid) + return -EINVAL; + + len = strlen(securid); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+len))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, OSCAR_FAMILY_AUTH, OSCAR_SUBTYPE_AUTH_SECURID_RESPONSE, 0x0000, NULL, 0); + aim_putsnac(&fr->data, OSCAR_FAMILY_AUTH, OSCAR_SUBTYPE_AUTH_SECURID_RESPONSE, 0x0000, 0); + + aimbs_put16(&fr->data, len); + aimbs_putstr(&fr->data, securid); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +static void auth_shutdown(aim_session_t *sess, aim_module_t *mod) +{ + if (sess->authinfo) { + free(sess->authinfo->sn); + free(sess->authinfo->bosip); + free(sess->authinfo->errorurl); + free(sess->authinfo->email); + free(sess->authinfo->chpassurl); + free(sess->authinfo->latestrelease.name); + free(sess->authinfo->latestrelease.url); + free(sess->authinfo->latestrelease.info); + free(sess->authinfo->latestbeta.name); + free(sess->authinfo->latestbeta.url); + free(sess->authinfo->latestbeta.info); + free(sess->authinfo); + } +} + +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 parse(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0007) + return keyparse(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x000a) + return got_securid_request(sess, mod, rx, snac, bs); + + return 0; +} + +faim_internal int auth_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x0017; + mod->version = 0x0000; + mod->flags = 0; + strncpy(mod->name, "auth", sizeof(mod->name)); + mod->snachandler = snachandler; + mod->shutdown = auth_shutdown; + + return 0; +} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/family_bart.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/oscar/family_bart.c Sat Feb 11 21:45:18 2006 +0000 @@ -0,0 +1,184 @@ +/* + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * Family 0x0010 - Server stored buddy art + * + * Used for storing and retrieving your cute little buddy icon + * from the AIM servers. + * + */ + +#include "oscar.h" + +/** + * Subtype 0x0002 - Upload your icon. + * + * @param sess The oscar session. + * @param conn The icon connection for this session. + * @param icon The raw data of the icon image file. + * @param iconlen Length of the raw data of the icon image file. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_bart_upload(aim_session_t *sess, const guint8 *icon, guint16 iconlen) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0010)) || !icon || !iconlen) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 2 + 2+iconlen))) + return -ENOMEM; + snacid = aim_cachesnac(sess, 0x0010, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0010, 0x0002, 0x0000, snacid); + + /* The reference number for the icon */ + aimbs_put16(&fr->data, 1); + + /* The icon */ + aimbs_put16(&fr->data, iconlen); + aimbs_putraw(&fr->data, icon, iconlen); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/** + * Subtype 0x0003 - Acknowledgement for uploading a buddy icon. + * + * You get this honky after you upload a buddy icon. + */ +static int uploadack(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; + guint16 something, somethingelse; + guint8 onemorething; + + something = aimbs_get16(bs); + somethingelse = aimbs_get16(bs); + onemorething = aimbs_get8(bs); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx); + + return ret; +} + +/** + * Subtype 0x0004 - Request someone's icon. + * + * @param sess The oscar session. + * @param conn The icon connection for this session. + * @param sn The screen name of the person who's icon you are requesting. + * @param iconcsum The MD5 checksum of the icon you are requesting. + * @param iconcsumlen Length of the MD5 checksum given above. Should be 10 bytes. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_bart_request(aim_session_t *sess, const char *sn, guint8 iconcsumtype, const guint8 *iconcsum, guint16 iconcsumlen) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0010)) || !sn || !strlen(sn) || !iconcsum || !iconcsumlen) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 1+strlen(sn) + 4 + 1+iconcsumlen))) + return -ENOMEM; + snacid = aim_cachesnac(sess, 0x0010, 0x0004, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0010, 0x0004, 0x0000, snacid); + + /* Screen name */ + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putstr(&fr->data, sn); + + /* Some numbers. You like numbers, right? */ + aimbs_put8(&fr->data, 0x01); + aimbs_put16(&fr->data, 0x0001); + aimbs_put8(&fr->data, iconcsumtype); + + /* Icon string */ + aimbs_put8(&fr->data, iconcsumlen); + aimbs_putraw(&fr->data, iconcsum, iconcsumlen); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/** + * Subtype 0x0005 - Receive a buddy icon. + * + * This is sent in response to a buddy icon request. + */ +static int parseicon(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; + char *sn; + guint16 flags, iconlen; + guint8 iconcsumtype, iconcsumlen, *iconcsum, *icon; + + sn = aimbs_getstr(bs, aimbs_get8(bs)); + flags = aimbs_get16(bs); + iconcsumtype = aimbs_get8(bs); + iconcsumlen = aimbs_get8(bs); + iconcsum = aimbs_getraw(bs, iconcsumlen); + iconlen = aimbs_get16(bs); + icon = aimbs_getraw(bs, iconlen); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, sn, iconcsumtype, iconcsum, iconcsumlen, icon, iconlen); + + free(sn); + free(iconcsum); + free(icon); + + 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 uploadack(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0005) + return parseicon(sess, mod, rx, snac, bs); + + return 0; +} + +faim_internal int bart_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x0010; + mod->version = 0x0001; + mod->toolid = 0x0010; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "bart", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/family_bos.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/oscar/family_bos.c Sat Feb 11 21:45:18 2006 +0000 @@ -0,0 +1,186 @@ +/* + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * Family 0x0009 - Basic Oscar Service. + * + * The functionality of this family has been replaced by SSI. + */ + +#include "oscar.h" + +#include + +/* Subtype 0x0002 - Request BOS rights. */ +faim_export int aim_bos_reqrights(aim_session_t *sess, aim_conn_t *conn) +{ + return aim_genericreq_n_snacid(sess, conn, 0x0009, 0x0002); +} + +/* Subtype 0x0003 - BOS Rights. */ +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; + aim_tlvlist_t *tlvlist; + guint16 maxpermits = 0, maxdenies = 0; + int ret = 0; + + /* + * TLVs follow + */ + tlvlist = aim_tlvlist_read(bs); + + /* + * TLV type 0x0001: Maximum number of buddies on permit list. + */ + if (aim_tlv_gettlv(tlvlist, 0x0001, 1)) + maxpermits = aim_tlv_get16(tlvlist, 0x0001, 1); + + /* + * TLV type 0x0002: Maximum number of buddies on deny list. + */ + if (aim_tlv_gettlv(tlvlist, 0x0002, 1)) + maxdenies = aim_tlv_get16(tlvlist, 0x0002, 1); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, maxpermits, maxdenies); + + aim_tlvlist_free(&tlvlist); + + return ret; +} + +/* + * Subtype 0x0004 - Set group permission 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, guint32 mask) +{ + return aim_genericreq_l(sess, conn, 0x0009, 0x0004, &mask); +} + +/* + * Stubtypes 0x0005, 0x0006, 0x0007, and 0x0008 - Modify permit/deny lists. + * + * 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; + guint16 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_itemindex(localcpy, i, '&'); + + aimbs_put8(&fr->data, strlen(tmpptr)); + aimbs_putstr(&fr->data, 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) +{ + + if (snac->subtype == 0x0003) + return rights(sess, mod, rx, snac, bs); + + return 0; +} + +faim_internal int bos_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x0009; + mod->version = 0x0001; + mod->toolid = 0x0110; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "bos", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/family_buddy.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/oscar/family_buddy.c Sat Feb 11 21:45:18 2006 +0000 @@ -0,0 +1,304 @@ +/* + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * Family 0x0003 - Old-style Buddylist Management (non-SSI). + * + */ + +#include "oscar.h" + +#include + +/* + * Subtype 0x0002 - Request rights. + * + * Request Buddy List rights. + * + */ +faim_export int aim_buddylist_reqrights(aim_session_t *sess, aim_conn_t *conn) +{ + return aim_genericreq_n_snacid(sess, conn, 0x0003, 0x0002); +} + +/* + * Subtype 0x0003 - Rights. + * + */ +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; + aim_tlvlist_t *tlvlist; + guint16 maxbuddies = 0, maxwatchers = 0; + int ret = 0; + + /* + * TLVs follow + */ + tlvlist = aim_tlvlist_read(bs); + + /* + * TLV type 0x0001: Maximum number of buddies. + */ + if (aim_tlv_gettlv(tlvlist, 0x0001, 1)) + maxbuddies = aim_tlv_get16(tlvlist, 0x0001, 1); + + /* + * TLV type 0x0002: Maximum number of watchers. + * + * Watchers are other users who have you on their buddy + * list. (This is called the "reverse list" by a certain + * other IM protocol.) + * + */ + if (aim_tlv_gettlv(tlvlist, 0x0002, 1)) + maxwatchers = aim_tlv_get16(tlvlist, 0x0002, 1); + + /* + * TLV type 0x0003: Unknown. + * + * ICQ only? + */ + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, maxbuddies, maxwatchers); + + aim_tlvlist_free(&tlvlist); + + return ret; +} + +/* + * Subtype 0x0004 - Add buddy to list. + * + * Adds a single buddy to your buddy list after login. + * XXX This should just be an extension of setbuddylist() + * + */ +faim_export int aim_buddylist_addbuddy(aim_session_t *sess, aim_conn_t *conn, const char *sn) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sn || !strlen(sn)) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0003, 0x0004, 0x0000, sn, strlen(sn)+1); + aim_putsnac(&fr->data, 0x0003, 0x0004, 0x0000, snacid); + + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putstr(&fr->data, sn); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0004 - Add multiple buddies to your buddy list. + * + * This just builds the "set buddy list" command then queues it. + * + * buddy_list = "Screen Name One&ScreenNameTwo&"; + * + * XXX Clean this up. + * + */ +faim_export int aim_buddylist_set(aim_session_t *sess, aim_conn_t *conn, const char *buddy_list) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + int len = 0; + char *localcpy = NULL; + char *tmpptr = NULL; + + if (!buddy_list || !(localcpy = strdup(buddy_list))) + return -EINVAL; + + for (tmpptr = strtok(localcpy, "&"); tmpptr; ) { + gaim_debug_misc("oscar", "---adding: %s (%d)\n", tmpptr, strlen(tmpptr)); + len += 1 + strlen(tmpptr); + tmpptr = strtok(NULL, "&"); + } + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+len))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0003, 0x0004, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0003, 0x0004, 0x0000, snacid); + + strncpy(localcpy, buddy_list, strlen(buddy_list) + 1); + + for (tmpptr = strtok(localcpy, "&"); tmpptr; ) { + + gaim_debug_misc("oscar", "---adding: %s (%d)\n", tmpptr, strlen(tmpptr)); + + aimbs_put8(&fr->data, strlen(tmpptr)); + aimbs_putstr(&fr->data, tmpptr); + tmpptr = strtok(NULL, "&"); + } + + aim_tx_enqueue(sess, fr); + + free(localcpy); + + return 0; +} + +/* + * Subtype 0x0005 - Remove buddy from list. + * + * XXX generalise to support removing multiple buddies (basically, its + * the same as setbuddylist() but with a different snac subtype). + * + */ +faim_export int aim_buddylist_removebuddy(aim_session_t *sess, aim_conn_t *conn, const char *sn) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sn || !strlen(sn)) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0003, 0x0005, 0x0000, sn, strlen(sn)+1); + aim_putsnac(&fr->data, 0x0003, 0x0005, 0x0000, snacid); + + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putstr(&fr->data, sn); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x000b + * + * XXX Why would we send this? + * + */ +faim_export int aim_buddylist_oncoming(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *info) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !conn || !info) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0003, 0x000b, 0x0000, NULL, 0); + + aim_putsnac(&fr->data, 0x0003, 0x000b, 0x0000, snacid); + aim_putuserinfo(&fr->data, info); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x000c + * + * XXX Why would we send this? + * + */ +faim_export int aim_buddylist_offgoing(aim_session_t *sess, aim_conn_t *conn, const char *sn) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !conn || !sn) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0003, 0x000c, 0x0000, NULL, 0); + + aim_putsnac(&fr->data, 0x0003, 0x000c, 0x0000, snacid); + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putstr(&fr->data, sn); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtypes 0x000b and 0x000c - Change in buddy status + * + * Oncoming Buddy notifications contain a subset of the + * user information structure. It's close enough to run + * through aim_info_extract() however. + * + * Although the offgoing notification contains no information, + * it is still in a format parsable by aim_info_extract(). + * + */ +static int buddychange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_userinfo_t userinfo; + aim_rxcallback_t userfunc; + + aim_info_extract(sess, bs, &userinfo); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, &userinfo); + + if (snac->subtype == 0x000b) + aim_locate_requestuserinfo(sess, userinfo.sn); + aim_info_free(&userinfo); + + 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 rights(sess, mod, rx, snac, bs); + else if ((snac->subtype == 0x000b) || (snac->subtype == 0x000c)) + return buddychange(sess, mod, rx, snac, bs); + + return 0; +} + +faim_internal int buddylist_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x0003; + mod->version = 0x0001; + mod->toolid = 0x0110; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "buddy", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/family_chat.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/oscar/family_chat.c Sat Feb 11 21:45:18 2006 +0000 @@ -0,0 +1,601 @@ +/* + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * Family 0x000e - Routines for the Chat service. + * + */ + +#include "oscar.h" + +#include + +/* Stored in the ->internal of chat connections */ +struct chatconnpriv { + guint16 exchange; + char *name; + guint16 instance; +}; + +faim_internal void aim_conn_kill_chat(aim_session_t *sess, aim_conn_t *conn) +{ + struct chatconnpriv *ccp = (struct chatconnpriv *)conn->internal; + + if (ccp) + free(ccp->name); + free(ccp); + + return; +} + +faim_export char *aim_chat_getname(aim_conn_t *conn) +{ + struct chatconnpriv *ccp; + + if (!conn) + return NULL; + + if (conn->type != AIM_CONN_TYPE_CHAT) + return NULL; + + ccp = (struct chatconnpriv *)conn->internal; + + return ccp->name; +} + +/* XXX get this into conn.c -- evil!! */ +faim_export aim_conn_t *aim_chat_getconn(aim_session_t *sess, const char *name) +{ + aim_conn_t *cur; + + for (cur = sess->connlist; cur; cur = cur->next) { + struct chatconnpriv *ccp = (struct chatconnpriv *)cur->internal; + + if (cur->type != AIM_CONN_TYPE_CHAT) + continue; + if (!cur->internal) { + gaim_debug_misc("oscar", "faim: chat: chat connection with no name! (fd = %d)\n", cur->fd); + continue; + } + + if (strcmp(ccp->name, name) == 0) + break; + } + + return cur; +} + +faim_export int aim_chat_attachname(aim_conn_t *conn, guint16 exchange, const char *roomname, guint16 instance) +{ + struct chatconnpriv *ccp; + + if (!conn || !roomname) + return -EINVAL; + + if (conn->internal) + free(conn->internal); + + if (!(ccp = malloc(sizeof(struct chatconnpriv)))) + return -ENOMEM; + + ccp->exchange = exchange; + ccp->name = strdup(roomname); + ccp->instance = instance; + + conn->internal = (void *)ccp; + + return 0; +} + +faim_internal int aim_chat_readroominfo(aim_bstream_t *bs, struct aim_chat_roominfo *outinfo) +{ + int namelen; + + if (!bs || !outinfo) + return 0; + + outinfo->exchange = aimbs_get16(bs); + namelen = aimbs_get8(bs); + outinfo->name = aimbs_getstr(bs, namelen); + outinfo->instance = aimbs_get16(bs); + + return 0; +} + +faim_export int aim_chat_leaveroom(aim_session_t *sess, const char *name) +{ + aim_conn_t *conn; + + if (!(conn = aim_chat_getconn(sess, name))) + return -ENOENT; + + aim_conn_close(conn); + + return 0; +} + +/* + * Subtype 0x0002 - General room information. Lots of stuff. + * + * Values I know are in here but I haven't attached + * them to any of the 'Unknown's: + * - Language (English) + * + */ +static int infoupdate(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_userinfo_t *userinfo = NULL; + aim_rxcallback_t userfunc; + int ret = 0; + int usercount = 0; + guint8 detaillevel = 0; + char *roomname = NULL; + struct aim_chat_roominfo roominfo; + guint16 tlvcount = 0; + aim_tlvlist_t *tlvlist; + char *roomdesc = NULL; + guint16 flags = 0; + guint32 creationtime = 0; + guint16 maxmsglen = 0, maxvisiblemsglen = 0; + guint16 unknown_d2 = 0, unknown_d5 = 0; + + aim_chat_readroominfo(bs, &roominfo); + + detaillevel = aimbs_get8(bs); + + if (detaillevel != 0x02) { + gaim_debug_misc("oscar", "faim: chat_roomupdateinfo: detail level %d not supported\n", detaillevel); + return 1; + } + + tlvcount = aimbs_get16(bs); + + /* + * Everything else are TLVs. + */ + tlvlist = aim_tlvlist_read(bs); + + /* + * TLV type 0x006a is the room name in Human Readable Form. + */ + if (aim_tlv_gettlv(tlvlist, 0x006a, 1)) + roomname = aim_tlv_getstr(tlvlist, 0x006a, 1); + + /* + * Type 0x006f: Number of occupants. + */ + if (aim_tlv_gettlv(tlvlist, 0x006f, 1)) + usercount = aim_tlv_get16(tlvlist, 0x006f, 1); + + /* + * Type 0x0073: Occupant list. + */ + if (aim_tlv_gettlv(tlvlist, 0x0073, 1)) { + int curoccupant = 0; + aim_tlv_t *tmptlv; + aim_bstream_t occbs; + + tmptlv = aim_tlv_gettlv(tlvlist, 0x0073, 1); + + /* Allocate enough userinfo structs for all occupants */ + userinfo = calloc(usercount, sizeof(aim_userinfo_t)); + + aim_bstream_init(&occbs, tmptlv->value, tmptlv->length); + + while (curoccupant < usercount) + aim_info_extract(sess, &occbs, &userinfo[curoccupant++]); + } + + /* + * Type 0x00c9: Flags. (AIM_CHATROOM_FLAG) + */ + if (aim_tlv_gettlv(tlvlist, 0x00c9, 1)) + flags = aim_tlv_get16(tlvlist, 0x00c9, 1); + + /* + * Type 0x00ca: Creation time (4 bytes) + */ + if (aim_tlv_gettlv(tlvlist, 0x00ca, 1)) + creationtime = aim_tlv_get32(tlvlist, 0x00ca, 1); + + /* + * Type 0x00d1: Maximum Message Length + */ + if (aim_tlv_gettlv(tlvlist, 0x00d1, 1)) + maxmsglen = aim_tlv_get16(tlvlist, 0x00d1, 1); + + /* + * Type 0x00d2: Unknown. (2 bytes) + */ + if (aim_tlv_gettlv(tlvlist, 0x00d2, 1)) + unknown_d2 = aim_tlv_get16(tlvlist, 0x00d2, 1); + + /* + * Type 0x00d3: Room Description + */ + if (aim_tlv_gettlv(tlvlist, 0x00d3, 1)) + roomdesc = aim_tlv_getstr(tlvlist, 0x00d3, 1); + +#if 0 + /* + * Type 0x000d4: Unknown (flag only) + */ + if (aim_tlv_gettlv(tlvlist, 0x000d4, 1)) { + /* Unhandled */ + } +#endif + + /* + * Type 0x00d5: Unknown. (1 byte) + */ + if (aim_tlv_gettlv(tlvlist, 0x00d5, 1)) + unknown_d5 = aim_tlv_get8(tlvlist, 0x00d5, 1); + +#if 0 + /* + * Type 0x00d6: Encoding 1 ("us-ascii") + */ + if (aim_tlv_gettlv(tlvlist, 0x000d6, 1)) { + /* Unhandled */ + } + + /* + * Type 0x00d7: Language 1 ("en") + */ + if (aim_tlv_gettlv(tlvlist, 0x000d7, 1)) { + /* Unhandled */ + } + + /* + * Type 0x00d8: Encoding 2 ("us-ascii") + */ + if (aim_tlv_gettlv(tlvlist, 0x000d8, 1)) { + /* Unhandled */ + } + + /* + * Type 0x00d9: Language 2 ("en") + */ + if (aim_tlv_gettlv(tlvlist, 0x000d9, 1)) { + /* Unhandled */ + } +#endif + + /* + * Type 0x00da: Maximum visible message length + */ + if (aim_tlv_gettlv(tlvlist, 0x000da, 1)) + maxvisiblemsglen = aim_tlv_get16(tlvlist, 0x00da, 1); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) { + ret = userfunc(sess, + rx, + &roominfo, + roomname, + usercount, + userinfo, + roomdesc, + flags, + creationtime, + maxmsglen, + unknown_d2, + unknown_d5, + maxvisiblemsglen); + } + + free(roominfo.name); + + while (usercount > 0) + aim_info_free(&userinfo[--usercount]); + + free(userinfo); + free(roomname); + free(roomdesc); + aim_tlvlist_free(&tlvlist); + + return ret; +} + +/* Subtypes 0x0003 and 0x0004 */ +static int userlistchange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_userinfo_t *userinfo = NULL; + aim_rxcallback_t userfunc; + int curcount = 0, ret = 0; + + while (aim_bstream_empty(bs)) { + curcount++; + userinfo = realloc(userinfo, curcount * sizeof(aim_userinfo_t)); + aim_info_extract(sess, bs, &userinfo[curcount-1]); + } + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, curcount, userinfo); + + aim_info_free(userinfo); + free(userinfo); + + return ret; +} + +/* + * Subtype 0x0005 - Send a Chat Message. + * + * Possible flags: + * AIM_CHATFLAGS_NOREFLECT -- Unset the flag that requests messages + * should be sent to their sender. + * AIM_CHATFLAGS_AWAY -- Mark the message as an autoresponse + * (Note that WinAIM does not honor this, + * and displays the message as normal.) + * + * XXX convert this to use tlvchains + */ +faim_export int aim_chat_send_im(aim_session_t *sess, aim_conn_t *conn, guint16 flags, const gchar *msg, int msglen, const char *encoding, const char *language) +{ + int i; + aim_frame_t *fr; + aim_msgcookie_t *cookie; + aim_snacid_t snacid; + guint8 ckstr[8]; + aim_tlvlist_t *otl = NULL, *itl = NULL; + + if (!sess || !conn || !msg || (msglen <= 0)) + return 0; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x000e, 0x0005, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x000e, 0x0005, 0x0000, snacid); + + /* + * Cookie + * + * XXX mkcookie should generate the cookie and cache it in one + * operation to preserve uniqueness. + */ + for (i = 0; i < 8; i++) + ckstr[i] = (guint8)rand(); + + cookie = aim_mkcookie(ckstr, AIM_COOKIETYPE_CHAT, NULL); + cookie->data = NULL; /* XXX store something useful here */ + + aim_cachecookie(sess, cookie); + + /* ICBM Header */ + aimbs_putraw(&fr->data, ckstr, 8); /* Cookie */ + aimbs_put16(&fr->data, 0x0003); /* Channel */ + + /* + * Type 1: Flag meaning this message is destined to the room. + */ + aim_tlvlist_add_noval(&otl, 0x0001); + + /* + * Type 6: Reflect + */ + if (!(flags & AIM_CHATFLAGS_NOREFLECT)) + aim_tlvlist_add_noval(&otl, 0x0006); + + /* + * Type 7: Autoresponse + */ + if (flags & AIM_CHATFLAGS_AWAY) + aim_tlvlist_add_noval(&otl, 0x0007); + + /* + * SubTLV: Type 1: Message + */ + aim_tlvlist_add_raw(&itl, 0x0001, msglen, (guchar *)msg); + + /* + * SubTLV: Type 2: Encoding + */ + if (encoding != NULL) + aim_tlvlist_add_str(&itl, 0x0002, encoding); + + /* + * SubTLV: Type 3: Language + */ + if (language != NULL) + aim_tlvlist_add_str(&itl, 0x0003, language); + + /* + * Type 5: Message block. Contains more TLVs. + * + * This could include other information... We just + * put in a message TLV however. + * + */ + aim_tlvlist_add_frozentlvlist(&otl, 0x0005, &itl); + + aim_tlvlist_write(&fr->data, &otl); + + aim_tlvlist_free(&itl); + aim_tlvlist_free(&otl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0006 + * + * We could probably include this in the normal ICBM parsing + * code as channel 0x0003, however, since only the start + * would be the same, we might as well do it here. + * + * General outline of this SNAC: + * snac + * cookie + * channel id + * tlvlist + * unknown + * source user info + * name + * evility + * userinfo tlvs + * online time + * etc + * message metatlv + * message tlv + * message string + * possibly others + * + */ +static int incomingim_ch3(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0, i; + aim_rxcallback_t userfunc; + aim_userinfo_t userinfo; + guint8 cookie[8]; + guint16 channel; + aim_tlvlist_t *otl; + char *msg = NULL; + int len = 0; + char *encoding = NULL, *language = NULL; + aim_msgcookie_t *ck; + + memset(&userinfo, 0, sizeof(aim_userinfo_t)); + + /* + * Read ICBM Cookie. + */ + for (i = 0; i < 8; i++) + cookie[i] = aimbs_get8(bs); + + if ((ck = aim_uncachecookie(sess, cookie, AIM_COOKIETYPE_CHAT))) { + free(ck->data); + free(ck); + } + + /* + * Channel ID + * + * Channel 0x0003 is used for chat messages. + * + */ + channel = aimbs_get16(bs); + + if (channel != 0x0003) { + gaim_debug_misc("oscar", "faim: chat_incoming: unknown channel! (0x%04x)\n", channel); + return 0; + } + + /* + * Start parsing TLVs right away. + */ + otl = aim_tlvlist_read(bs); + + /* + * Type 0x0003: Source User Information + */ + if (aim_tlv_gettlv(otl, 0x0003, 1)) { + aim_tlv_t *userinfotlv; + aim_bstream_t tbs; + + userinfotlv = aim_tlv_gettlv(otl, 0x0003, 1); + + aim_bstream_init(&tbs, userinfotlv->value, userinfotlv->length); + aim_info_extract(sess, &tbs, &userinfo); + } + +#if 0 + /* + * Type 0x0001: If present, it means it was a message to the + * room (as opposed to a whisper). + */ + if (aim_tlv_gettlv(otl, 0x0001, 1)) { + /* Unhandled */ + } +#endif + + /* + * Type 0x0005: Message Block. Conains more TLVs. + */ + if (aim_tlv_gettlv(otl, 0x0005, 1)) { + aim_tlvlist_t *itl; + aim_tlv_t *msgblock; + aim_bstream_t tbs; + + msgblock = aim_tlv_gettlv(otl, 0x0005, 1); + aim_bstream_init(&tbs, msgblock->value, msgblock->length); + itl = aim_tlvlist_read(&tbs); + + /* + * Type 0x0001: Message. + */ + if (aim_tlv_gettlv(itl, 0x0001, 1)) { + msg = aim_tlv_getstr(itl, 0x0001, 1); + len = aim_tlv_gettlv(itl, 0x0001, 1)->length; + } + + /* + * Type 0x0002: Encoding. + */ + if (aim_tlv_gettlv(itl, 0x0002, 1)) + encoding = aim_tlv_getstr(itl, 0x0002, 1); + + /* + * Type 0x0003: Language. + */ + if (aim_tlv_gettlv(itl, 0x0003, 1)) + language = aim_tlv_getstr(itl, 0x0003, 1); + + aim_tlvlist_free(&itl); + } + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, &userinfo, len, msg, encoding, language); + + aim_info_free(&userinfo); + free(msg); + aim_tlvlist_free(&otl); + + 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 == 0x0002) + return infoupdate(sess, mod, rx, snac, bs); + else if ((snac->subtype == 0x0003) || (snac->subtype == 0x0004)) + return userlistchange(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0006) + return incomingim_ch3(sess, mod, rx, snac, bs); + + return 0; +} + +faim_internal int chat_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x000e; + mod->version = 0x0001; + mod->toolid = 0x0010; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "chat", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/family_chatnav.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/oscar/family_chatnav.c Sat Feb 11 21:45:18 2006 +0000 @@ -0,0 +1,470 @@ +/* + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * Family 0x000d - Handle ChatNav. + * + * The ChatNav(igation) service does various things to keep chat + * alive. It provides room information, room searching and creating, + * as well as giving users the right ("permission") to use chat. + * + */ + +#include "oscar.h" + +/* + * Subtype 0x0002 + * + * conn must be a chatnav connection! + * + */ +faim_export int aim_chatnav_reqrights(aim_session_t *sess, aim_conn_t *conn) +{ + return aim_genericreq_n_snacid(sess, conn, 0x000d, 0x0002); +} + +/* + * Subtype 0x0008 + */ +faim_export int aim_chatnav_createroom(aim_session_t *sess, aim_conn_t *conn, const char *name, guint16 exchange) +{ + static const char ck[] = {"create"}; + static const char lang[] = {"en"}; + static const char charset[] = {"us-ascii"}; + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x000d, 0x0008, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x000d, 0x0008, 0x0000, snacid); + + /* exchange */ + aimbs_put16(&fr->data, exchange); + + /* + * This looks to be a big hack. You'll note that this entire + * SNAC is just a room info structure, but the hard room name, + * here, is set to "create". + * + * Either this goes on the "list of questions concerning + * why-the-hell-did-you-do-that", or this value is completely + * ignored. Without experimental evidence, but a good knowledge of + * AOL style, I'm going to guess that it is the latter, and that + * the value of the room name in create requests is ignored. + */ + aimbs_put8(&fr->data, strlen(ck)); + aimbs_putstr(&fr->data, ck); + + /* + * instance + * + * Setting this to 0xffff apparently assigns the last instance. + * + */ + aimbs_put16(&fr->data, 0xffff); + + /* detail level */ + aimbs_put8(&fr->data, 0x01); + + aim_tlvlist_add_str(&tl, 0x00d3, name); + aim_tlvlist_add_str(&tl, 0x00d6, charset); + aim_tlvlist_add_str(&tl, 0x00d7, lang); + + /* tlvcount */ + aimbs_put16(&fr->data, aim_tlvlist_count(&tl)); + aim_tlvlist_write(&fr->data, &tl); + + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +static int parseinfo_perms(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs, aim_snac_t *snac2) +{ + aim_rxcallback_t userfunc; + int ret = 0; + struct aim_chat_exchangeinfo *exchanges = NULL; + int curexchange; + aim_tlv_t *exchangetlv; + guint8 maxrooms = 0; + aim_tlvlist_t *tlvlist, *innerlist; + + tlvlist = aim_tlvlist_read(bs); + + /* + * Type 0x0002: Maximum concurrent rooms. + */ + if (aim_tlv_gettlv(tlvlist, 0x0002, 1)) + maxrooms = aim_tlv_get8(tlvlist, 0x0002, 1); + + /* + * Type 0x0003: Exchange information + * + * There can be any number of these, each one + * representing another exchange. + * + */ + for (curexchange = 0; ((exchangetlv = aim_tlv_gettlv(tlvlist, 0x0003, curexchange+1))); ) { + aim_bstream_t tbs; + + aim_bstream_init(&tbs, exchangetlv->value, exchangetlv->length); + + curexchange++; + + exchanges = realloc(exchanges, curexchange * sizeof(struct aim_chat_exchangeinfo)); + + /* exchange number */ + exchanges[curexchange-1].number = aimbs_get16(&tbs); + innerlist = aim_tlvlist_read(&tbs); + +#if 0 + /* + * Type 0x000a: Unknown. + * + * Usually three bytes: 0x0114 (exchange 1) or 0x010f (others). + * + */ + if (aim_tlv_gettlv(innerlist, 0x000a, 1)) { + /* Unhandled */ + } + + /* + * Type 0x000d: Unknown. + */ + if (aim_tlv_gettlv(innerlist, 0x000d, 1)) { + /* Unhandled */ + } + + /* + * Type 0x0004: Unknown + */ + if (aim_tlv_gettlv(innerlist, 0x0004, 1)) { + /* Unhandled */ + } +#endif + + /* + * Type 0x0002: Unknown + */ + if (aim_tlv_gettlv(innerlist, 0x0002, 1)) { + guint16 classperms; + + classperms = aim_tlv_get16(innerlist, 0x0002, 1); + + gaim_debug_misc("oscar", "faim: class permissions %x\n", classperms); + } + + /* + * Type 0x00c9: Flags + * + * 1 Evilable + * 2 Nav Only + * 4 Instancing Allowed + * 8 Occupant Peek Allowed + * + */ + if (aim_tlv_gettlv(innerlist, 0x00c9, 1)) + exchanges[curexchange-1].flags = aim_tlv_get16(innerlist, 0x00c9, 1); + +#if 0 + /* + * Type 0x00ca: Creation Date + */ + if (aim_tlv_gettlv(innerlist, 0x00ca, 1)) { + /* Unhandled */ + } + + /* + * Type 0x00d0: Mandatory Channels? + */ + if (aim_tlv_gettlv(innerlist, 0x00d0, 1)) { + /* Unhandled */ + } + + /* + * Type 0x00d1: Maximum Message length + */ + if (aim_tlv_gettlv(innerlist, 0x00d1, 1)) { + /* Unhandled */ + } + + /* + * Type 0x00d2: Maximum Occupancy? + */ + if (aim_tlv_gettlv(innerlist, 0x00d2, 1)) { + /* Unhandled */ + } +#endif + + /* + * Type 0x00d3: Exchange Description + */ + if (aim_tlv_gettlv(innerlist, 0x00d3, 1)) + exchanges[curexchange-1].name = aim_tlv_getstr(innerlist, 0x00d3, 1); + else + exchanges[curexchange-1].name = NULL; + +#if 0 + /* + * Type 0x00d4: Exchange Description URL + */ + if (aim_tlv_gettlv(innerlist, 0x00d4, 1)) { + /* Unhandled */ + } +#endif + + /* + * Type 0x00d5: Creation Permissions + * + * 0 Creation not allowed + * 1 Room creation allowed + * 2 Exchange creation allowed + * + */ + if (aim_tlv_gettlv(innerlist, 0x00d5, 1)) { + guint8 createperms; + + createperms = aim_tlv_get8(innerlist, 0x00d5, 1); + } + + /* + * Type 0x00d6: Character Set (First Time) + */ + if (aim_tlv_gettlv(innerlist, 0x00d6, 1)) + exchanges[curexchange-1].charset1 = aim_tlv_getstr(innerlist, 0x00d6, 1); + else + exchanges[curexchange-1].charset1 = NULL; + + /* + * Type 0x00d7: Language (First Time) + */ + if (aim_tlv_gettlv(innerlist, 0x00d7, 1)) + exchanges[curexchange-1].lang1 = aim_tlv_getstr(innerlist, 0x00d7, 1); + else + exchanges[curexchange-1].lang1 = NULL; + + /* + * Type 0x00d8: Character Set (Second Time) + */ + if (aim_tlv_gettlv(innerlist, 0x00d8, 1)) + exchanges[curexchange-1].charset2 = aim_tlv_getstr(innerlist, 0x00d8, 1); + else + exchanges[curexchange-1].charset2 = NULL; + + /* + * Type 0x00d9: Language (Second Time) + */ + if (aim_tlv_gettlv(innerlist, 0x00d9, 1)) + exchanges[curexchange-1].lang2 = aim_tlv_getstr(innerlist, 0x00d9, 1); + else + exchanges[curexchange-1].lang2 = NULL; + +#if 0 + /* + * Type 0x00da: Unknown + */ + if (aim_tlv_gettlv(innerlist, 0x00da, 1)) { + /* Unhandled */ + } +#endif + + aim_tlvlist_free(&innerlist); + } + + /* + * Call client. + */ + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, snac2->type, maxrooms, curexchange, exchanges); + + for (curexchange--; curexchange >= 0; curexchange--) { + free(exchanges[curexchange].name); + free(exchanges[curexchange].charset1); + free(exchanges[curexchange].lang1); + free(exchanges[curexchange].charset2); + free(exchanges[curexchange].lang2); + } + free(exchanges); + aim_tlvlist_free(&tlvlist); + + return ret; +} + +static int parseinfo_create(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs, aim_snac_t *snac2) +{ + aim_rxcallback_t userfunc; + aim_tlvlist_t *tlvlist, *innerlist; + char *ck = NULL, *fqcn = NULL, *name = NULL; + guint16 exchange = 0, instance = 0, unknown = 0, flags = 0, maxmsglen = 0, maxoccupancy = 0; + guint32 createtime = 0; + guint8 createperms = 0, detaillevel; + int cklen; + aim_tlv_t *bigblock; + int ret = 0; + aim_bstream_t bbbs; + + tlvlist = aim_tlvlist_read(bs); + + if (!(bigblock = aim_tlv_gettlv(tlvlist, 0x0004, 1))) { + gaim_debug_misc("oscar", "no bigblock in top tlv in create room response\n"); + aim_tlvlist_free(&tlvlist); + return 0; + } + + aim_bstream_init(&bbbs, bigblock->value, bigblock->length); + + exchange = aimbs_get16(&bbbs); + cklen = aimbs_get8(&bbbs); + ck = aimbs_getstr(&bbbs, cklen); + instance = aimbs_get16(&bbbs); + detaillevel = aimbs_get8(&bbbs); + + if (detaillevel != 0x02) { + gaim_debug_misc("oscar", "unknown detaillevel in create room response (0x%02x)\n", detaillevel); + aim_tlvlist_free(&tlvlist); + free(ck); + return 0; + } + + unknown = aimbs_get16(&bbbs); + + innerlist = aim_tlvlist_read(&bbbs); + + if (aim_tlv_gettlv(innerlist, 0x006a, 1)) + fqcn = aim_tlv_getstr(innerlist, 0x006a, 1); + + if (aim_tlv_gettlv(innerlist, 0x00c9, 1)) + flags = aim_tlv_get16(innerlist, 0x00c9, 1); + + if (aim_tlv_gettlv(innerlist, 0x00ca, 1)) + createtime = aim_tlv_get32(innerlist, 0x00ca, 1); + + if (aim_tlv_gettlv(innerlist, 0x00d1, 1)) + maxmsglen = aim_tlv_get16(innerlist, 0x00d1, 1); + + if (aim_tlv_gettlv(innerlist, 0x00d2, 1)) + maxoccupancy = aim_tlv_get16(innerlist, 0x00d2, 1); + + if (aim_tlv_gettlv(innerlist, 0x00d3, 1)) + name = aim_tlv_getstr(innerlist, 0x00d3, 1); + + if (aim_tlv_gettlv(innerlist, 0x00d5, 1)) + createperms = aim_tlv_get8(innerlist, 0x00d5, 1); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) { + ret = userfunc(sess, rx, snac2->type, fqcn, instance, exchange, flags, createtime, maxmsglen, maxoccupancy, createperms, unknown, name, ck); + } + + free(ck); + free(name); + free(fqcn); + aim_tlvlist_free(&innerlist); + aim_tlvlist_free(&tlvlist); + + return ret; +} + +/* + * Subtype 0x0009 + * + * Since multiple things can trigger this callback, we must lookup the + * snacid to determine the original snac subtype that was called. + * + * XXX This isn't really how this works. But this is: Every d/9 response + * has a 16bit value at the beginning. That matches to: + * Short Desc = 1 + * Full Desc = 2 + * Instance Info = 4 + * Nav Short Desc = 8 + * Nav Instance Info = 16 + * And then everything is really asynchronous. There is no specific + * attachment of a response to a create room request, for example. Creating + * the room yields no different a response than requesting the room's info. + * + */ +static int parseinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_snac_t *snac2; + int ret = 0; + + if (!(snac2 = aim_remsnac(sess, snac->id))) { + gaim_debug_misc("oscar", "faim: chatnav_parse_info: received response to unknown request! (%08lx)\n", snac->id); + return 0; + } + + if (snac2->family != 0x000d) { + gaim_debug_misc("oscar", "faim: chatnav_parse_info: recieved response that maps to corrupt request! (fam=%04x)\n", snac2->family); + return 0; + } + + /* + * We now know what the original SNAC subtype was. + */ + if (snac2->type == 0x0002) /* request chat rights */ + ret = parseinfo_perms(sess, mod, rx, snac, bs, snac2); + else if (snac2->type == 0x0003) /* request exchange info */ + gaim_debug_misc("oscar", "chatnav_parse_info: resposne to exchange info\n"); + else if (snac2->type == 0x0004) /* request room info */ + gaim_debug_misc("oscar", "chatnav_parse_info: response to room info\n"); + else if (snac2->type == 0x0005) /* request more room info */ + gaim_debug_misc("oscar", "chatnav_parse_info: response to more room info\n"); + else if (snac2->type == 0x0006) /* request occupant list */ + gaim_debug_misc("oscar", "chatnav_parse_info: response to occupant info\n"); + else if (snac2->type == 0x0007) /* search for a room */ + gaim_debug_misc("oscar", "chatnav_parse_info: search results\n"); + else if (snac2->type == 0x0008) /* create room */ + ret = parseinfo_create(sess, mod, rx, snac, bs, snac2); + else + gaim_debug_misc("oscar", "chatnav_parse_info: unknown request subtype (%04x)\n", snac2->type); + + if (snac2) + free(snac2->data); + free(snac2); + + 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 == 0x0009) + return parseinfo(sess, mod, rx, snac, bs); + + return 0; +} + +faim_internal int chatnav_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x000d; + mod->version = 0x0001; + mod->toolid = 0x0010; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "chatnav", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/family_feedbag.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/oscar/family_feedbag.c Sat Feb 11 21:45:18 2006 +0000 @@ -0,0 +1,2007 @@ +/* + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * Family 0x0013 - Server-Side/Stored Information. + * + * Relatively new facility that allows certain types of information, such as + * a user's buddy list, permit/deny list, and permit/deny preferences, to be + * stored on the server, so that they can be accessed from any client. + * + * We keep 2 copies of SSI data: + * 1) An exact copy of what is stored on the AIM servers. + * 2) A local copy that we make changes to, and then send diffs + * between this and the exact copy to keep them in sync. + * + * All the "aim_ssi_itemlist_bleh" functions near the top just modify the list + * that is given to them (i.e. they don't send SNACs). + * + * The SNAC sending and receiving functions are lower down in the file, and + * they're simpler. They are in the order of the subtypes they deal with, + * starting with the request rights function (subtype 0x0002), then parse + * rights (subtype 0x0003), then--well, you get the idea. + * + * This is entirely too complicated. + * You don't know the half of it. + * + */ + +#include "oscar.h" + +/** + * Locally rebuild the 0x00c8 TLV in the additional data of the given group. + * + * @param list A pointer to a pointer to the current list of items. + * @param name A null terminated string containing the group name, or NULL + * if you want to modify the master group. + * @return Return a pointer to the modified item. + */ +static struct aim_ssi_item *aim_ssi_itemlist_rebuildgroup(struct aim_ssi_item *list, const char *name) +{ + int newlen; + struct aim_ssi_item *cur, *group; + + if (!list) + return NULL; + + /* Find the group */ + if (!(group = aim_ssi_itemlist_finditem(list, name, NULL, AIM_SSI_TYPE_GROUP))) + return NULL; + + /* Find the length for the new additional data */ + newlen = 0; + if (group->gid == 0x0000) { + for (cur=list; cur; cur=cur->next) + if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid != 0x0000)) + newlen += 2; + } else { + for (cur=list; cur; cur=cur->next) + if ((cur->gid == group->gid) && (cur->type == AIM_SSI_TYPE_BUDDY)) + newlen += 2; + } + + /* Build the new TLV list */ + if (newlen > 0) { + guint8 *newdata; + + if (!(newdata = (guint8 *)malloc((newlen)*sizeof(guint8)))) + return NULL; + newlen = 0; + if (group->gid == 0x0000) { + for (cur=list; cur; cur=cur->next) + if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid != 0x0000)) + newlen += aimutil_put16(newdata+newlen, cur->gid); + } else { + for (cur=list; cur; cur=cur->next) + if ((cur->gid == group->gid) && (cur->type == AIM_SSI_TYPE_BUDDY)) + newlen += aimutil_put16(newdata+newlen, cur->bid); + } + aim_tlvlist_replace_raw(&group->data, 0x00c8, newlen, newdata); + + free(newdata); + } + + return group; +} + +/** + * Locally add a new item to the given item list. + * + * @param list A pointer to a pointer to the current list of items. + * @param name A null terminated string of the name of the new item, or NULL if the + * item should have no name. + * @param gid The group ID# you want the new item to have, or 0xFFFF if we should pick something. + * @param bid The buddy ID# you want the new item to have, or 0xFFFF if we should pick something. + * @param type The type of the item, 0x0000 for a contact, 0x0001 for a group, etc. + * @param data The additional data for the new item. + * @return A pointer to the newly created item. + */ +static struct aim_ssi_item *aim_ssi_itemlist_add(struct aim_ssi_item **list, const char *name, guint16 gid, guint16 bid, guint16 type, aim_tlvlist_t *data) +{ + int i; + struct aim_ssi_item *cur, *new; + + if (!list) + return NULL; + + if (!(new = (struct aim_ssi_item *)malloc(sizeof(struct aim_ssi_item)))) + return NULL; + + /* Set the name */ + if (name) { + new->name = (char *)malloc((strlen(name)+1)*sizeof(char)); + strcpy(new->name, name); + } else + new->name = NULL; + + /* Set the group ID# and buddy ID# */ + new->gid = gid; + new->bid = bid; + if (type == AIM_SSI_TYPE_GROUP) { + if ((new->gid == 0xFFFF) && name) { + do { + new->gid += 0x0001; + for (cur=*list, i=0; ((cur) && (!i)); cur=cur->next) + if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid == new->gid)) + i=1; + } while (i); + } + } else { + if (new->bid == 0xFFFF) { + do { + new->bid += 0x0001; + for (cur=*list, i=0; ((cur) && (!i)); cur=cur->next) + if ((cur->bid == new->bid) && (cur->gid == new->gid)) + i=1; + } while (i); + } + } + + /* Set the type */ + new->type = type; + + /* Set the TLV list */ + new->data = aim_tlvlist_copy(data); + + /* Add the item to the list in the correct numerical position. Fancy, eh? */ + if (*list) { + if ((new->gid < (*list)->gid) || ((new->gid == (*list)->gid) && (new->bid < (*list)->bid))) { + new->next = *list; + *list = new; + } else { + struct aim_ssi_item *prev; + for ((prev=*list, cur=(*list)->next); (cur && ((new->gid > cur->gid) || ((new->gid == cur->gid) && (new->bid > cur->bid)))); prev=cur, cur=cur->next); + new->next = prev->next; + prev->next = new; + } + } else { + new->next = *list; + *list = new; + } + + return new; +} + +/** + * Locally delete an item from the given item list. + * + * @param list A pointer to a pointer to the current list of items. + * @param del A pointer to the item you want to remove from the list. + * @return Return 0 if no errors, otherwise return the error number. + */ +static int aim_ssi_itemlist_del(struct aim_ssi_item **list, struct aim_ssi_item *del) +{ + if (!list || !(*list) || !del) + return -EINVAL; + + /* Remove the item from the list */ + if (*list == del) { + *list = (*list)->next; + } else { + struct aim_ssi_item *cur; + for (cur=*list; (cur->next && (cur->next!=del)); cur=cur->next); + if (cur->next) + cur->next = del->next; + } + + /* Free the removed item */ + free(del->name); + aim_tlvlist_free(&del->data); + free(del); + + return 0; +} + +/** + * Compare two items to see if they have the same data. + * + * @param cur1 A pointer to a pointer to the first item. + * @param cur2 A pointer to a pointer to the second item. + * @return Return 0 if no differences, or a number if there are differences. + */ +static int aim_ssi_itemlist_cmp(struct aim_ssi_item *cur1, struct aim_ssi_item *cur2) +{ + if (!cur1 || !cur2) + return 1; + + if (cur1->data && !cur2->data) + return 2; + + if (!cur1->data && cur2->data) + return 3; + + if ((cur1->data && cur2->data) && (aim_tlvlist_cmp(cur1->data, cur2->data))) + return 4; + + if (cur1->name && !cur2->name) + return 5; + + if (!cur1->name && cur2->name) + return 6; + + if (cur1->name && cur2->name && aim_sncmp(cur1->name, cur2->name)) + return 7; + + if (cur1->gid != cur2->gid) + return 8; + + if (cur1->bid != cur2->bid) + return 9; + + if (cur1->type != cur2->type) + return 10; + + return 0; +} + +static int aim_ssi_itemlist_valid(struct aim_ssi_item *list, struct aim_ssi_item *item) +{ + struct aim_ssi_item *cur; + for (cur=list; cur; cur=cur->next) + if (cur == item) + return 1; + return 0; +} + +/** + * Locally find an item given a group ID# and a buddy ID#. + * + * @param list A pointer to the current list of items. + * @param gid The group ID# of the desired item. + * @param bid The buddy ID# of the desired item. + * @return Return a pointer to the item if found, else return NULL; + */ +faim_export struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_item *list, guint16 gid, guint16 bid) +{ + struct aim_ssi_item *cur; + for (cur=list; cur; cur=cur->next) + if ((cur->gid == gid) && (cur->bid == bid)) + return cur; + return NULL; +} + +/** + * Locally find an item given a group name, screen name, and type. If group name + * and screen name are null, then just return the first item of the given type. + * + * @param list A pointer to the current list of items. + * @param gn The group name of the desired item. + * @param bn The buddy name of the desired item. + * @param type The type of the desired item. + * @return Return a pointer to the item if found, else return NULL. + */ +faim_export struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_item *list, const char *gn, const char *sn, guint16 type) +{ + struct aim_ssi_item *cur; + if (!list) + return NULL; + + if (gn && sn) { /* For finding buddies in groups */ + for (cur=list; cur; cur=cur->next) + if ((cur->type == type) && (cur->name) && !(aim_sncmp(cur->name, sn))) { + struct aim_ssi_item *curg; + for (curg=list; curg; curg=curg->next) + if ((curg->type == AIM_SSI_TYPE_GROUP) && (curg->gid == cur->gid) && (curg->name) && !(aim_sncmp(curg->name, gn))) + return cur; + } + + } else if (gn) { /* For finding groups */ + for (cur=list; cur; cur=cur->next) { + if ((cur->type == type) && (cur->bid == 0x0000) && (cur->name) && !(aim_sncmp(cur->name, gn))) { + return cur; + } + } + + } else if (sn) { /* For finding permits, denies, and ignores */ + for (cur=list; cur; cur=cur->next) { + if ((cur->type == type) && (cur->name) && !(aim_sncmp(cur->name, sn))) { + return cur; + } + } + + /* For stuff without names--permit deny setting, visibility mask, etc. */ + } else for (cur=list; cur; cur=cur->next) { + if ((cur->type == type) && (!cur->name)) + return cur; + } + + return NULL; +} + +/** + * Check if the given buddy exists in any group in the buddy list. + * + * @param list A pointer to the current list of items. + * @param sn The group name of the desired item. + * @return Return a pointer to the name of the item if found, else return NULL; + */ +faim_export struct aim_ssi_item *aim_ssi_itemlist_exists(struct aim_ssi_item *list, const char *sn) +{ + struct aim_ssi_item *cur; + if (!list || !sn) + return NULL; + for (cur=list; cur; cur=cur->next) + if ((cur->type == AIM_SSI_TYPE_BUDDY) && (cur->name) && (!aim_sncmp(cur->name, sn))) + return cur; + return NULL; +} + +/** + * Locally find the parent item of the given buddy name. + * + * @param list A pointer to the current list of items. + * @param bn The buddy name of the desired item. + * @return Return a pointer to the name of the item if found, else return NULL; + */ +faim_export char *aim_ssi_itemlist_findparentname(struct aim_ssi_item *list, const char *sn) +{ + struct aim_ssi_item *cur, *curg; + if (!list || !sn) + return NULL; + if (!(cur = aim_ssi_itemlist_exists(list, sn))) + return NULL; + if (!(curg = aim_ssi_itemlist_find(list, cur->gid, 0x0000))) + return NULL; + return curg->name; +} + +/** + * Locally find the permit/deny setting item, and return the setting. + * + * @param list A pointer to the current list of items. + * @return Return the current SSI permit deny setting, or 0 if no setting was found. + */ +faim_export int aim_ssi_getpermdeny(struct aim_ssi_item *list) +{ + struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PDINFO); + if (cur) { + aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x00ca, 1); + if (tlv && tlv->value) + return aimutil_get8(tlv->value); + } + return 0; +} + +/** + * Locally find the presence flag item, and return the setting. The returned setting is a + * bitmask of the user flags that you are visible to. See the AIM_FLAG_* #defines + * in oscar.h + * + * @param list A pointer to the current list of items. + * @return Return the current visibility mask. + */ +faim_export guint32 aim_ssi_getpresence(struct aim_ssi_item *list) +{ + struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS); + if (cur) { + aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x00c9, 1); + if (tlv && tlv->length) + return aimutil_get32(tlv->value); + } + return 0xFFFFFFFF; +} + +/** + * Locally find the alias of the given buddy. + * + * @param list A pointer to the current list of items. + * @param gn The group of the buddy. + * @param sn The name of the buddy. + * @return A pointer to a NULL terminated string that is the buddy's + * alias, or NULL if the buddy has no alias. You should free + * this returned value! + */ +faim_export char *aim_ssi_getalias(struct aim_ssi_item *list, const char *gn, const char *sn) +{ + struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, sn, AIM_SSI_TYPE_BUDDY); + if (cur) { + aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x0131, 1); + if (tlv && tlv->length) { + char *alias = (char *)malloc((tlv->length+1)*sizeof(char)); + strncpy(alias, (char *)tlv->value, tlv->length); + alias[tlv->length] = 0; + return alias; + } + } + return NULL; +} + +/** + * Locally find the comment of the given buddy. + * + * @param list A pointer to the current list of items. + * @param gn The group of the buddy. + * @param sn The name of the buddy. + * @return A pointer to a NULL terminated string that is the buddy's + * comment, or NULL if the buddy has no comment. You should free + * this returned value! + */ +faim_export char *aim_ssi_getcomment(struct aim_ssi_item *list, const char *gn, const char *sn) +{ + struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, sn, AIM_SSI_TYPE_BUDDY); + if (cur) { + aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x013c, 1); + if (tlv && tlv->length) { + char *alias = (char *)malloc((tlv->length+1)*sizeof(char)); + strncpy(alias, (char *)tlv->value, tlv->length); + alias[tlv->length] = 0; + return alias; + } + } + return NULL; +} + +/** + * Locally find if you are waiting for authorization for a buddy. + * + * @param list A pointer to the current list of items. + * @param gn The group of the buddy. + * @param sn The name of the buddy. + * @return 1 if you are waiting for authorization; 0 if you are not + */ +faim_export int aim_ssi_waitingforauth(struct aim_ssi_item *list, const char *gn, const char *sn) +{ + struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, sn, AIM_SSI_TYPE_BUDDY); + if (cur) { + if (aim_tlv_gettlv(cur->data, 0x0066, 1)) + return 1; + } + return 0; +} + +/** + * If there are changes, then create temporary items and + * call addmoddel. + * + * @param sess The oscar session. + * @return Return 0 if no errors, otherwise return the error number. + */ +static int aim_ssi_sync(aim_session_t *sess) +{ + struct aim_ssi_item *cur1, *cur2; + struct aim_ssi_tmp *cur, *new; + + if (!sess) + return -EINVAL; + + /* If we're waiting for an ack, we shouldn't do anything else */ + if (sess->ssi.waiting_for_ack) + return 0; + + /* + * Compare the 2 lists and create an aim_ssi_tmp for each difference. + * We should only send either additions, modifications, or deletions + * before waiting for an acknowledgement. So first do deletions, then + * additions, then modifications. Also, both the official and the local + * list should be in ascending numerical order for the group ID#s and the + * buddy ID#s, which makes things more efficient. I think. + */ + + /* Additions */ + if (!sess->ssi.pending) { + for (cur1=sess->ssi.local; cur1; cur1=cur1->next) { + if (!aim_ssi_itemlist_find(sess->ssi.official, cur1->gid, cur1->bid)) { + new = (struct aim_ssi_tmp *)malloc(sizeof(struct aim_ssi_tmp)); + new->action = OSCAR_SUBTYPE_FEEDBAG_ADD; + new->ack = 0xffff; + new->name = NULL; + new->item = cur1; + new->next = NULL; + if (sess->ssi.pending) { + for (cur=sess->ssi.pending; cur->next; cur=cur->next); + cur->next = new; + } else + sess->ssi.pending = new; + } + } + } + + /* Deletions */ + if (!sess->ssi.pending) { + for (cur1=sess->ssi.official; cur1; cur1=cur1->next) { + if (!aim_ssi_itemlist_find(sess->ssi.local, cur1->gid, cur1->bid)) { + new = (struct aim_ssi_tmp *)malloc(sizeof(struct aim_ssi_tmp)); + new->action = OSCAR_SUBTYPE_FEEDBAG_DEL; + new->ack = 0xffff; + new->name = NULL; + new->item = cur1; + new->next = NULL; + if (sess->ssi.pending) { + for (cur=sess->ssi.pending; cur->next; cur=cur->next); + cur->next = new; + } else + sess->ssi.pending = new; + } + } + } + + /* Modifications */ + if (!sess->ssi.pending) { + for (cur1=sess->ssi.local; cur1; cur1=cur1->next) { + cur2 = aim_ssi_itemlist_find(sess->ssi.official, cur1->gid, cur1->bid); + if (cur2 && (aim_ssi_itemlist_cmp(cur1, cur2))) { + new = (struct aim_ssi_tmp *)malloc(sizeof(struct aim_ssi_tmp)); + new->action = OSCAR_SUBTYPE_FEEDBAG_MOD; + new->ack = 0xffff; + new->name = NULL; + new->item = cur1; + new->next = NULL; + if (sess->ssi.pending) { + for (cur=sess->ssi.pending; cur->next; cur=cur->next); + cur->next = new; + } else + sess->ssi.pending = new; + } + } + } + + /* We're out of stuff to do, so tell the AIM servers we're done and exit */ + if (!sess->ssi.pending) { + aim_ssi_modend(sess); + return 0; + } + + /* Make sure we don't send anything else between now + * and when we receive the ack for the following operation */ + sess->ssi.waiting_for_ack = 1; + + /* Now go mail off our data and wait 4 to 6 weeks */ + aim_ssi_addmoddel(sess); + + return 0; +} + +/** + * Free all SSI data. + * + * This doesn't remove it from the server, that's different. + * + * @param sess The oscar session. + * @return Return 0 if no errors, otherwise return the error number. + */ +static int aim_ssi_freelist(aim_session_t *sess) +{ + struct aim_ssi_item *cur, *del; + struct aim_ssi_tmp *curtmp, *deltmp; + + cur = sess->ssi.official; + while (cur) { + del = cur; + cur = cur->next; + free(del->name); + aim_tlvlist_free(&del->data); + free(del); + } + + cur = sess->ssi.local; + while (cur) { + del = cur; + cur = cur->next; + free(del->name); + aim_tlvlist_free(&del->data); + free(del); + } + + curtmp = sess->ssi.pending; + while (curtmp) { + deltmp = curtmp; + curtmp = curtmp->next; + free(deltmp); + } + + sess->ssi.numitems = 0; + sess->ssi.official = NULL; + sess->ssi.local = NULL; + sess->ssi.pending = NULL; + sess->ssi.timestamp = (time_t)0; + + return 0; +} + +/** + * Delete all SSI data. + * + * @param sess The oscar session. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_ssi_deletelist(aim_session_t *sess) +{ + struct aim_ssi_item *cur, *del; + + if (!sess) + return -EINVAL; + + /* Free the local list */ + cur = sess->ssi.local; + while (cur) { + del = cur; + cur = cur->next; + free(del->name); + aim_tlvlist_free(&del->data); + free(del); + } + sess->ssi.local = NULL; + + /* Sync our local list with the server list */ + aim_ssi_sync(sess); + + return 0; +} + +/** + * This "cleans" the ssi list. It does the following: + * 1) Makes sure all buddies, permits, and denies have names. + * 2) Makes sure that all buddies are in a group that exist. + * 3) Deletes any empty groups + * + * @param sess The oscar session. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_ssi_cleanlist(aim_session_t *sess) +{ + struct aim_ssi_item *cur, *next; + + if (!sess) + return -EINVAL; + + /* Delete any buddies, permits, or denies with empty names. */ + /* If there are any buddies directly in the master group, add them to a real group. */ + /* DESTROY any buddies that are directly in the master group. */ + /* Do the same for buddies that are in a non-existant group. */ + /* This will kind of mess up if you hit the item limit, but this function isn't too critical */ + cur = sess->ssi.local; + while (cur) { + next = cur->next; + if (!cur->name) { + if (cur->type == AIM_SSI_TYPE_BUDDY) + aim_ssi_delbuddy(sess, NULL, NULL); + else if (cur->type == AIM_SSI_TYPE_PERMIT) + aim_ssi_delpermit(sess, NULL); + else if (cur->type == AIM_SSI_TYPE_DENY) + aim_ssi_deldeny(sess, NULL); + } else if ((cur->type == AIM_SSI_TYPE_BUDDY) && ((cur->gid == 0x0000) || (!aim_ssi_itemlist_find(sess->ssi.local, cur->gid, 0x0000)))) { + char *alias = aim_ssi_getalias(sess->ssi.local, NULL, cur->name); + aim_ssi_addbuddy(sess, cur->name, "orphans", alias, NULL, NULL, 0); + aim_ssi_delbuddy(sess, cur->name, NULL); + free(alias); + } + cur = next; + } + + /* Make sure there aren't any duplicate buddies in a group, or duplicate permits or denies */ + cur = sess->ssi.local; + while (cur) { + if ((cur->type == AIM_SSI_TYPE_BUDDY) || (cur->type == AIM_SSI_TYPE_PERMIT) || (cur->type == AIM_SSI_TYPE_DENY)) + { + struct aim_ssi_item *cur2, *next2; + cur2 = cur->next; + while (cur2) { + next2 = cur2->next; + if ((cur->type == cur2->type) && (cur->gid == cur2->gid) && (cur->name != NULL) && (cur2->name != NULL) && (!strcmp(cur->name, cur2->name))) { + aim_ssi_itemlist_del(&sess->ssi.local, cur2); + } + cur2 = next2; + } + } + cur = cur->next; + } + + /* Check if there are empty groups and delete them */ + cur = sess->ssi.local; + while (cur) { + next = cur->next; + if (cur->type == AIM_SSI_TYPE_GROUP) { + aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x00c8, 1); + if (!tlv || !tlv->length) + aim_ssi_itemlist_del(&sess->ssi.local, cur); + } + cur = next; + } + + /* Check if the master group is empty */ + if ((cur = aim_ssi_itemlist_find(sess->ssi.local, 0x0000, 0x0000)) && (!cur->data)) + aim_ssi_itemlist_del(&sess->ssi.local, cur); + + /* If we've made any changes then sync our list with the server's */ + aim_ssi_sync(sess); + + return 0; +} + +/** + * Add a buddy to the list. + * + * @param sess The oscar session. + * @param name The name of the item. + * @param group The group of the item. + * @param alias The alias/nickname of the item, or NULL. + * @param comment The buddy comment for the item, or NULL. + * @param smsnum The locally assigned SMS number, or NULL. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_ssi_addbuddy(aim_session_t *sess, const char *name, const char *group, const char *alias, const char *comment, const char *smsnum, int needauth) +{ + struct aim_ssi_item *parent; + aim_tlvlist_t *data = NULL; + + if (!sess || !name || !group) + return -EINVAL; + + /* Find the parent */ + if (!(parent = aim_ssi_itemlist_finditem(sess->ssi.local, group, NULL, AIM_SSI_TYPE_GROUP))) { + /* Find the parent's parent (the master group) */ + if (!(parent = aim_ssi_itemlist_find(sess->ssi.local, 0x0000, 0x0000))) + if (!(parent = aim_ssi_itemlist_add(&sess->ssi.local, NULL, 0x0000, 0x0000, AIM_SSI_TYPE_GROUP, NULL))) + return -ENOMEM; + /* Add the parent */ + if (!(parent = aim_ssi_itemlist_add(&sess->ssi.local, group, 0xFFFF, 0x0000, AIM_SSI_TYPE_GROUP, NULL))) + return -ENOMEM; + + /* Modify the parent's parent (the master group) */ + aim_ssi_itemlist_rebuildgroup(sess->ssi.local, NULL); + } + + /* Create a TLV list for the new buddy */ + if (needauth) + aim_tlvlist_add_noval(&data, 0x0066); + if (alias) + aim_tlvlist_add_str(&data, 0x0131, alias); + if (smsnum) + aim_tlvlist_add_str(&data, 0x013a, smsnum); + if (comment) + aim_tlvlist_add_str(&data, 0x013c, comment); + + /* Add that bad boy */ + aim_ssi_itemlist_add(&sess->ssi.local, name, parent->gid, 0xFFFF, AIM_SSI_TYPE_BUDDY, data); + aim_tlvlist_free(&data); + + /* Modify the parent group */ + aim_ssi_itemlist_rebuildgroup(sess->ssi.local, group); + + /* Sync our local list with the server list */ + aim_ssi_sync(sess); + + return 0; +} + +/** + * Add a permit buddy to the list. + * + * @param sess The oscar session. + * @param name The name of the item.. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_ssi_addpermit(aim_session_t *sess, const char *name) +{ + + if (!sess || !name) + return -EINVAL; + + /* Add that bad boy */ + aim_ssi_itemlist_add(&sess->ssi.local, name, 0x0000, 0xFFFF, AIM_SSI_TYPE_PERMIT, NULL); + + /* Sync our local list with the server list */ + aim_ssi_sync(sess); + + return 0; +} + +/** + * Add a deny buddy to the list. + * + * @param sess The oscar session. + * @param name The name of the item.. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_ssi_adddeny(aim_session_t *sess, const char *name) +{ + + if (!sess || !name) + return -EINVAL; + + /* Add that bad boy */ + aim_ssi_itemlist_add(&sess->ssi.local, name, 0x0000, 0xFFFF, AIM_SSI_TYPE_DENY, NULL); + + /* Sync our local list with the server list */ + aim_ssi_sync(sess); + + return 0; +} + +/** + * Deletes a buddy from the list. + * + * @param sess The oscar session. + * @param name The name of the item, or NULL. + * @param group The group of the item, or NULL. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_ssi_delbuddy(aim_session_t *sess, const char *name, const char *group) +{ + struct aim_ssi_item *del; + + if (!sess) + return -EINVAL; + + /* Find the buddy */ + if (!(del = aim_ssi_itemlist_finditem(sess->ssi.local, group, name, AIM_SSI_TYPE_BUDDY))) + return -EINVAL; + + /* Remove the item from the list */ + aim_ssi_itemlist_del(&sess->ssi.local, del); + + /* Modify the parent group */ + aim_ssi_itemlist_rebuildgroup(sess->ssi.local, group); + + /* Check if we should delete the parent group */ + if ((del = aim_ssi_itemlist_finditem(sess->ssi.local, group, NULL, AIM_SSI_TYPE_GROUP)) && (!del->data)) { + aim_ssi_itemlist_del(&sess->ssi.local, del); + + /* Modify the parent group */ + aim_ssi_itemlist_rebuildgroup(sess->ssi.local, NULL); + + /* Check if we should delete the parent's parent (the master group) */ + if ((del = aim_ssi_itemlist_find(sess->ssi.local, 0x0000, 0x0000)) && (!del->data)) { + aim_ssi_itemlist_del(&sess->ssi.local, del); + } + } + + /* Sync our local list with the server list */ + aim_ssi_sync(sess); + + return 0; +} + +/** + * Deletes a permit buddy from the list. + * + * @param sess The oscar session. + * @param name The name of the item, or NULL. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_ssi_delpermit(aim_session_t *sess, const char *name) +{ + struct aim_ssi_item *del; + + if (!sess) + return -EINVAL; + + /* Find the item */ + if (!(del = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, name, AIM_SSI_TYPE_PERMIT))) + return -EINVAL; + + /* Remove the item from the list */ + aim_ssi_itemlist_del(&sess->ssi.local, del); + + /* Sync our local list with the server list */ + aim_ssi_sync(sess); + + return 0; +} + +/** + * Deletes a deny buddy from the list. + * + * @param sess The oscar session. + * @param name The name of the item, or NULL. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_ssi_deldeny(aim_session_t *sess, const char *name) +{ + struct aim_ssi_item *del; + + if (!sess) + return -EINVAL; + + /* Find the item */ + if (!(del = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, name, AIM_SSI_TYPE_DENY))) + return -EINVAL; + + /* Remove the item from the list */ + aim_ssi_itemlist_del(&sess->ssi.local, del); + + /* Sync our local list with the server list */ + aim_ssi_sync(sess); + + return 0; +} + +/** + * Move a buddy from one group to another group. This basically just deletes the + * buddy and re-adds it. + * + * @param sess The oscar session. + * @param oldgn The group that the buddy is currently in. + * @param newgn The group that the buddy should be moved in to. + * @param sn The name of the buddy to be moved. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_ssi_movebuddy(aim_session_t *sess, const char *oldgn, const char *newgn, const char *sn) +{ + char *alias = aim_ssi_getalias(sess->ssi.local, oldgn, sn); + aim_ssi_addbuddy(sess, sn, newgn, alias, NULL, NULL, aim_ssi_waitingforauth(sess->ssi.local, oldgn, sn)); + aim_ssi_delbuddy(sess, sn, oldgn); + free(alias); + return 0; +} + +/** + * Change the alias stored on the server for a given buddy. + * + * @param sess The oscar session. + * @param gn The group that the buddy is currently in. + * @param sn The screen name of the buddy. + * @param alias The new alias for the buddy, or NULL if you want to remove + * a buddy's comment. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_ssi_aliasbuddy(aim_session_t *sess, const char *gn, const char *sn, const char *alias) +{ + struct aim_ssi_item *tmp; + + if (!sess || !gn || !sn) + return -EINVAL; + + if (!(tmp = aim_ssi_itemlist_finditem(sess->ssi.local, gn, sn, AIM_SSI_TYPE_BUDDY))) + return -EINVAL; + + /* Either add or remove the 0x0131 TLV from the TLV chain */ + if ((alias != NULL) && (strlen(alias) > 0)) + aim_tlvlist_replace_str(&tmp->data, 0x0131, alias); + else + aim_tlvlist_remove(&tmp->data, 0x0131); + + /* Sync our local list with the server list */ + aim_ssi_sync(sess); + + return 0; +} + +/** + * Change the comment stored on the server for a given buddy. + * + * @param sess The oscar session. + * @param gn The group that the buddy is currently in. + * @param sn The screen name of the buddy. + * @param alias The new comment for the buddy, or NULL if you want to remove + * a buddy's comment. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_ssi_editcomment(aim_session_t *sess, const char *gn, const char *sn, const char *comment) +{ + struct aim_ssi_item *tmp; + + if (!sess || !gn || !sn) + return -EINVAL; + + if (!(tmp = aim_ssi_itemlist_finditem(sess->ssi.local, gn, sn, AIM_SSI_TYPE_BUDDY))) + return -EINVAL; + + /* Either add or remove the 0x0131 TLV from the TLV chain */ + if ((comment != NULL) && (strlen(comment) > 0)) + aim_tlvlist_replace_str(&tmp->data, 0x013c, comment); + else + aim_tlvlist_remove(&tmp->data, 0x013c); + + /* Sync our local list with the server list */ + aim_ssi_sync(sess); + + return 0; +} + +/** + * Rename a group. + * + * @param sess The oscar session. + * @param oldgn The old group name. + * @param newgn The new group name. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_ssi_rename_group(aim_session_t *sess, const char *oldgn, const char *newgn) +{ + struct aim_ssi_item *group; + + if (!sess || !oldgn || !newgn) + return -EINVAL; + + if (!(group = aim_ssi_itemlist_finditem(sess->ssi.local, oldgn, NULL, AIM_SSI_TYPE_GROUP))) + return -EINVAL; + + free(group->name); + group->name = (char *)malloc((strlen(newgn)+1)*sizeof(char)); + strcpy(group->name, newgn); + + /* Sync our local list with the server list */ + aim_ssi_sync(sess); + + return 0; +} + +/** + * Stores your permit/deny setting on the server, and starts using it. + * + * @param sess The oscar session. + * @param permdeny Your permit/deny setting. Can be one of the following: + * 1 - Allow all users + * 2 - Block all users + * 3 - Allow only the users below + * 4 - Block only the users below + * 5 - Allow only users on my buddy list + * @param vismask A bitmask of the class of users to whom you want to be + * visible. See the AIM_FLAG_BLEH #defines in oscar.h + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_ssi_setpermdeny(aim_session_t *sess, guint8 permdeny, guint32 vismask) +{ + struct aim_ssi_item *tmp; + + if (!sess) + return -EINVAL; + + /* Find the PDINFO item, or add it if it does not exist */ + if (!(tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, NULL, AIM_SSI_TYPE_PDINFO))) + tmp = aim_ssi_itemlist_add(&sess->ssi.local, NULL, 0x0000, 0xFFFF, AIM_SSI_TYPE_PDINFO, NULL); + + /* Need to add the 0x00ca TLV to the TLV chain */ + aim_tlvlist_replace_8(&tmp->data, 0x00ca, permdeny); + + /* Need to add the 0x00cb TLV to the TLV chain */ + aim_tlvlist_replace_32(&tmp->data, 0x00cb, vismask); + + /* Sync our local list with the server list */ + aim_ssi_sync(sess); + + return 0; +} + +/** + * Set buddy icon information + * + * @param sess The oscar session. + * @param iconcsum The MD5 checksum of the icon you are using. + * @param iconcsumlen Length of the MD5 checksum given above. Should be 0x10 bytes. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_ssi_seticon(aim_session_t *sess, guint8 *iconsum, guint16 iconsumlen) +{ + struct aim_ssi_item *tmp; + guint8 *csumdata; + + if (!sess || !iconsum || !iconsumlen) + return -EINVAL; + + /* Find the ICONINFO item, or add it if it does not exist */ + if (!(tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, "1", AIM_SSI_TYPE_ICONINFO))) { + tmp = aim_ssi_itemlist_add(&sess->ssi.local, "1", 0x0000, 0x51F4, AIM_SSI_TYPE_ICONINFO, NULL); + } + + /* Need to add the 0x00d5 TLV to the TLV chain */ + if (!(csumdata = (guint8 *)malloc((iconsumlen+2)*sizeof(guint8)))) + return -ENOMEM; + csumdata[0] = 0x00; + csumdata[1] = 0x10; + memcpy(&csumdata[2], iconsum, iconsumlen); + aim_tlvlist_replace_raw(&tmp->data, 0x00d5, (iconsumlen+2) * sizeof(guint8), csumdata); + free(csumdata); + + /* Need to add the 0x0131 TLV to the TLV chain, used to cache the icon */ + aim_tlvlist_replace_noval(&tmp->data, 0x0131); + + /* Sync our local list with the server list */ + aim_ssi_sync(sess); + return 0; +} + +/** + * Remove a reference to a server stored buddy icon. This will make your + * icon stop showing up to other people. + * + * @param sess The oscar session. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_ssi_delicon(aim_session_t *sess) +{ + struct aim_ssi_item *tmp; + + if (!sess) + return -EINVAL; + + /* Find the ICONINFO item and delete it if it exists*/ + if ((tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, "1", AIM_SSI_TYPE_ICONINFO))) + aim_ssi_itemlist_del(&sess->ssi.local, tmp); + + /* Sync our local list with the server list */ + aim_ssi_sync(sess); + return 0; +} + +/** + * Stores your setting for various SSI settings. Whether you + * should show up as idle or not, etc. + * + * @param sess The oscar session. + * @param presence I think it's a bitmask, but I only know what one of the bits is: + * 0x00000002 - Hide wireless? + * 0x00000400 - Allow others to see your idle time + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_ssi_setpresence(aim_session_t *sess, guint32 presence) { + struct aim_ssi_item *tmp; + + if (!sess) + return -EINVAL; + + /* Find the PRESENCEPREFS item, or add it if it does not exist */ + if (!(tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS))) + tmp = aim_ssi_itemlist_add(&sess->ssi.local, NULL, 0x0000, 0xFFFF, AIM_SSI_TYPE_PRESENCEPREFS, NULL); + + /* Need to add the x00c9 TLV to the TLV chain */ + aim_tlvlist_replace_32(&tmp->data, 0x00c9, presence); + + /* Sync our local list with the server list */ + aim_ssi_sync(sess); + + return 0; +} + +/* + * Subtype 0x0002 - Request SSI Rights. + */ +faim_export int aim_ssi_reqrights(aim_session_t *sess) +{ + aim_conn_t *conn; + + if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_FEEDBAG))) + return -EINVAL; + + return aim_genericreq_n_snacid(sess, conn, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_REQRIGHTS); +} + +/* + * Subtype 0x0003 - SSI Rights Information. + */ +static int parserights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0, i; + aim_rxcallback_t userfunc; + aim_tlvlist_t *tlvlist; + aim_tlv_t *tlv; + aim_bstream_t bstream; + guint16 *maxitems; + + /* This SNAC is made up of a bunch of TLVs */ + tlvlist = aim_tlvlist_read(bs); + + /* TLV 0x0004 contains the maximum number of each item */ + if (!(tlv = aim_tlv_gettlv(tlvlist, 0x0004, 1))) { + aim_tlvlist_free(&tlvlist); + return 0; + } + + aim_bstream_init(&bstream, tlv->value, tlv->length); + + if (!(maxitems = (guint16 *)malloc((tlv->length/2)*sizeof(guint16)))) { + aim_tlvlist_free(&tlvlist); + return 0; + } + + for (i=0; i<(tlv->length/2); i++) + maxitems[i] = aimbs_get16(&bstream); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, tlv->length/2, maxitems); + + aim_tlvlist_free(&tlvlist); + free(maxitems); + + return ret; +} + +/* + * Subtype 0x0004 - Request SSI Data when you don't have a timestamp and + * revision number. + * + */ +faim_export int aim_ssi_reqdata(aim_session_t *sess) +{ + aim_conn_t *conn; + + if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_FEEDBAG))) + return -EINVAL; + + /* Free any current data, just in case */ + aim_ssi_freelist(sess); + + return aim_genericreq_n_snacid(sess, conn, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_REQDATA); +} + +/* + * Subtype 0x0005 - Request SSI Data when you have a timestamp and revision + * number. + * + * The data will only be sent if it is newer than the posted local + * timestamp and revision. + * + * Note that the client should never increment the revision, only the server. + * + */ +faim_export int aim_ssi_reqifchanged(aim_session_t *sess, time_t timestamp, guint16 numitems) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_FEEDBAG))) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4+2))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_REQIFCHANGED, 0x0000, NULL, 0); + + aim_putsnac(&fr->data, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_REQIFCHANGED, 0x0000, snacid); + aimbs_put32(&fr->data, timestamp); + aimbs_put16(&fr->data, numitems); + + aim_tx_enqueue(sess, fr); + + /* Free any current data, just in case */ + aim_ssi_freelist(sess); + + return 0; +} + +/* + * Subtype 0x0006 - SSI Data. + */ +static int parsedata(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; + guint8 fmtver; /* guess */ + guint16 namelen, gid, bid, type; + char *name; + aim_tlvlist_t *data; + + fmtver = aimbs_get8(bs); /* Version of ssi data. Should be 0x00 */ + sess->ssi.numitems += aimbs_get16(bs); /* # of items in this SSI SNAC */ + + /* Read in the list */ + while (aim_bstream_empty(bs) > 4) { /* last four bytes are timestamp */ + if ((namelen = aimbs_get16(bs))) + name = aimbs_getstr(bs, namelen); + else + name = NULL; + gid = aimbs_get16(bs); + bid = aimbs_get16(bs); + type = aimbs_get16(bs); + data = aim_tlvlist_readlen(bs, aimbs_get16(bs)); + aim_ssi_itemlist_add(&sess->ssi.official, name, gid, bid, type, data); + free(name); + aim_tlvlist_free(&data); + } + + /* Read in the timestamp */ + sess->ssi.timestamp = aimbs_get32(bs); + + if (!(snac->flags & 0x0001)) { + /* Make a copy of the list */ + struct aim_ssi_item *cur; + for (cur=sess->ssi.official; cur; cur=cur->next) + aim_ssi_itemlist_add(&sess->ssi.local, cur->name, cur->gid, cur->bid, cur->type, cur->data); + + sess->ssi.received_data = 1; + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, fmtver, sess->ssi.numitems, sess->ssi.official, sess->ssi.timestamp); + } + + return ret; +} + +/* + * Subtype 0x0007 - SSI Activate Data. + * + * Should be sent after receiving 13/6 or 13/f to tell the server you + * are ready to begin using the list. It will promptly give you the + * presence information for everyone in your list and put your permit/deny + * settings into effect. + * + */ +faim_export int aim_ssi_enable(aim_session_t *sess) +{ + aim_conn_t *conn; + + if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_FEEDBAG))) + return -EINVAL; + + return aim_genericreq_n(sess, conn, OSCAR_FAMILY_FEEDBAG, 0x0007); +} + +/* + * Subtype 0x0008/0x0009/0x000a - SSI Add/Mod/Del Item(s). + * + * Sends the SNAC to add, modify, or delete an item from the server-stored + * information. These 3 SNACs all have an identical structure. The only + * difference is the subtype that is set for the SNAC. + * + */ +faim_export int aim_ssi_addmoddel(aim_session_t *sess) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + int snaclen; + struct aim_ssi_tmp *cur; + + if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_FEEDBAG)) || !sess->ssi.pending || !sess->ssi.pending->item) + return -EINVAL; + + /* Calculate total SNAC size */ + snaclen = 10; /* For family, subtype, flags, and SNAC ID */ + for (cur=sess->ssi.pending; cur; cur=cur->next) { + snaclen += 10; /* For length, GID, BID, type, and length */ + if (cur->item->name) + snaclen += strlen(cur->item->name); + if (cur->item->data) + snaclen += aim_tlvlist_size(&cur->item->data); + } + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, snaclen))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, OSCAR_FAMILY_FEEDBAG, sess->ssi.pending->action, 0x0000, NULL, 0); + aim_putsnac(&fr->data, OSCAR_FAMILY_FEEDBAG, sess->ssi.pending->action, 0x0000, snacid); + + for (cur=sess->ssi.pending; cur; cur=cur->next) { + aimbs_put16(&fr->data, cur->item->name ? strlen(cur->item->name) : 0); + if (cur->item->name) + aimbs_putstr(&fr->data, cur->item->name); + aimbs_put16(&fr->data, cur->item->gid); + aimbs_put16(&fr->data, cur->item->bid); + aimbs_put16(&fr->data, cur->item->type); + aimbs_put16(&fr->data, cur->item->data ? aim_tlvlist_size(&cur->item->data) : 0); + if (cur->item->data) + aim_tlvlist_write(&fr->data, &cur->item->data); + } + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0008 - Incoming SSI add. + * + * Sent by the server, for example, when someone is added to + * your "Recent Buddies" group. + */ +static int parseadd(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; + char *name; + guint16 len, gid, bid, type; + aim_tlvlist_t *data; + + while (aim_bstream_empty(bs)) { + if ((len = aimbs_get16(bs))) + name = aimbs_getstr(bs, len); + else + name = NULL; + gid = aimbs_get16(bs); + bid = aimbs_get16(bs); + type = aimbs_get16(bs); + if ((len = aimbs_get16(bs))) + data = aim_tlvlist_readlen(bs, len); + else + data = NULL; + + aim_ssi_itemlist_add(&sess->ssi.local, name, gid, bid, type, data); + aim_ssi_itemlist_add(&sess->ssi.official, name, gid, bid, type, data); + aim_tlvlist_free(&data); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, type, name); + + free(name); + } + + return ret; +} + +/* + * Subtype 0x0009 - Incoming SSI mod. + * + * XXX - It would probably be good for the client to actually do something when it gets this. + */ +static int parsemod(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; + char *name; + guint16 len, gid, bid, type; + aim_tlvlist_t *data; + struct aim_ssi_item *item; + + while (aim_bstream_empty(bs)) { + if ((len = aimbs_get16(bs))) + name = aimbs_getstr(bs, len); + else + name = NULL; + gid = aimbs_get16(bs); + bid = aimbs_get16(bs); + type = aimbs_get16(bs); + if ((len = aimbs_get16(bs))) + data = aim_tlvlist_readlen(bs, len); + else + data = NULL; + + /* Replace the 2 local items with the given one */ + if ((item = aim_ssi_itemlist_find(sess->ssi.local, gid, bid))) { + item->type = type; + free(item->name); + if (name) { + item->name = (char *)malloc((strlen(name)+1)*sizeof(char)); + strcpy(item->name, name); + } else + item->name = NULL; + aim_tlvlist_free(&item->data); + item->data = aim_tlvlist_copy(data); + } + + if ((item = aim_ssi_itemlist_find(sess->ssi.official, gid, bid))) { + item->type = type; + free(item->name); + if (name) { + item->name = (char *)malloc((strlen(name)+1)*sizeof(char)); + strcpy(item->name, name); + } else + item->name = NULL; + aim_tlvlist_free(&item->data); + item->data = aim_tlvlist_copy(data); + } + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx); + + free(name); + aim_tlvlist_free(&data); + } + + return ret; +} + +/* + * Subtype 0x000a - Incoming SSI del. + * + * XXX - It would probably be good for the client to actually do something when it gets this. + */ +static int parsedel(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; + guint16 gid, bid; + struct aim_ssi_item *del; + + while (aim_bstream_empty(bs)) { + aim_bstream_advance(bs, aimbs_get16(bs)); + gid = aimbs_get16(bs); + bid = aimbs_get16(bs); + aimbs_get16(bs); + aim_bstream_advance(bs, aimbs_get16(bs)); + + if ((del = aim_ssi_itemlist_find(sess->ssi.local, gid, bid))) + aim_ssi_itemlist_del(&sess->ssi.local, del); + if ((del = aim_ssi_itemlist_find(sess->ssi.official, gid, bid))) + aim_ssi_itemlist_del(&sess->ssi.official, del); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx); + } + + return ret; +} + +/* + * Subtype 0x000e - SSI Add/Mod/Del Ack. + * + * Response to add, modify, or delete SNAC (sent with aim_ssi_addmoddel). + * + */ +static int parseack(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; + struct aim_ssi_tmp *cur, *del; + + /* Read in the success/failure flags from the ack SNAC */ + cur = sess->ssi.pending; + while (cur && (aim_bstream_empty(bs)>0)) { + cur->ack = aimbs_get16(bs); + cur = cur->next; + } + + /* + * If outcome is 0, then add the item to the item list, or replace the other item, + * or remove the old item. If outcome is non-zero, then remove the item from the + * local list, or unmodify it, or add it. + */ + for (cur=sess->ssi.pending; (cur && (cur->ack != 0xffff)); cur=cur->next) { + if (cur->item) { + if (cur->ack) { + /* Our action was unsuccessful, so change the local list back to how it was */ + if (cur->action == OSCAR_SUBTYPE_FEEDBAG_ADD) { + /* Remove the item from the local list */ + /* Make sure cur->item is still valid memory */ + if (aim_ssi_itemlist_valid(sess->ssi.local, cur->item)) { + if (cur->item->name) { + cur->name = (char *)malloc((strlen(cur->item->name)+1)*sizeof(char)); + strcpy(cur->name, cur->item->name); + } + aim_ssi_itemlist_del(&sess->ssi.local, cur->item); + } + cur->item = NULL; + + } else if (cur->action == OSCAR_SUBTYPE_FEEDBAG_MOD) { + /* Replace the local item with the item from the official list */ + if (aim_ssi_itemlist_valid(sess->ssi.local, cur->item)) { + struct aim_ssi_item *cur1; + if ((cur1 = aim_ssi_itemlist_find(sess->ssi.official, cur->item->gid, cur->item->bid))) { + free(cur->item->name); + if (cur1->name) { + cur->item->name = (char *)malloc((strlen(cur1->name)+1)*sizeof(char)); + strcpy(cur->item->name, cur1->name); + } else + cur->item->name = NULL; + aim_tlvlist_free(&cur->item->data); + cur->item->data = aim_tlvlist_copy(cur1->data); + } + } else + cur->item = NULL; + + } else if (cur->action == OSCAR_SUBTYPE_FEEDBAG_DEL) { + /* Add the item back into the local list */ + if (aim_ssi_itemlist_valid(sess->ssi.official, cur->item)) { + aim_ssi_itemlist_add(&sess->ssi.local, cur->item->name, cur->item->gid, cur->item->bid, cur->item->type, cur->item->data); + } else + cur->item = NULL; + } + + } else { + /* Do the exact opposite */ + if (cur->action == OSCAR_SUBTYPE_FEEDBAG_ADD) { + /* Add the local item to the official list */ + if (aim_ssi_itemlist_valid(sess->ssi.local, cur->item)) { + aim_ssi_itemlist_add(&sess->ssi.official, cur->item->name, cur->item->gid, cur->item->bid, cur->item->type, cur->item->data); + } else + cur->item = NULL; + + } else if (cur->action == OSCAR_SUBTYPE_FEEDBAG_MOD) { + /* Replace the official item with the item from the local list */ + if (aim_ssi_itemlist_valid(sess->ssi.local, cur->item)) { + struct aim_ssi_item *cur1; + if ((cur1 = aim_ssi_itemlist_find(sess->ssi.official, cur->item->gid, cur->item->bid))) { + free(cur1->name); + if (cur->item->name) { + cur1->name = (char *)malloc((strlen(cur->item->name)+1)*sizeof(char)); + strcpy(cur1->name, cur->item->name); + } else + cur1->name = NULL; + aim_tlvlist_free(&cur1->data); + cur1->data = aim_tlvlist_copy(cur->item->data); + } + } else + cur->item = NULL; + + } else if (cur->action == OSCAR_SUBTYPE_FEEDBAG_DEL) { + /* Remove the item from the official list */ + if (aim_ssi_itemlist_valid(sess->ssi.official, cur->item)) + aim_ssi_itemlist_del(&sess->ssi.official, cur->item); + cur->item = NULL; + } + + } + } /* End if (cur->item) */ + } /* End for loop */ + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, sess->ssi.pending); + + /* Free all aim_ssi_tmp's with an outcome */ + cur = sess->ssi.pending; + while (cur && (cur->ack != 0xffff)) { + del = cur; + cur = cur->next; + free(del->name); + free(del); + } + sess->ssi.pending = cur; + + /* If we're not waiting for any more acks, then send more SNACs */ + if (!sess->ssi.pending) { + sess->ssi.pending = NULL; + sess->ssi.waiting_for_ack = 0; + aim_ssi_sync(sess); + } + + return ret; +} + +/* + * Subtype 0x000f - SSI Data Unchanged. + * + * Response to aim_ssi_reqifchanged() if the server-side data is not newer than + * posted local stamp/revision. + * + */ +static int parsedataunchanged(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; + + sess->ssi.received_data = 1; + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx); + + return ret; +} + +/* + * Subtype 0x0011 - SSI Begin Data Modification. + * + * Tells the server you're going to start modifying data. + * + */ +faim_export int aim_ssi_modbegin(aim_session_t *sess) +{ + aim_conn_t *conn; + + if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_FEEDBAG))) + return -EINVAL; + + return aim_genericreq_n(sess, conn, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_EDITSTART); +} + +/* + * Subtype 0x0012 - SSI End Data Modification. + * + * Tells the server you're finished modifying data. + * + */ +faim_export int aim_ssi_modend(aim_session_t *sess) +{ + aim_conn_t *conn; + + if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_FEEDBAG))) + return -EINVAL; + + return aim_genericreq_n(sess, conn, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_EDITSTOP); +} + +/* + * Subtype 0x0014 - Grant authorization + * + * Authorizes a contact so they can add you to their contact list. + * + */ +faim_export int aim_ssi_sendauth(aim_session_t *sess, char *sn, char *msg) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_FEEDBAG)) || !sn) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)+2+(msg ? strlen(msg)+1 : 0)+2))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_SENDAUTH, 0x0000, NULL, 0); + aim_putsnac(&fr->data, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_SENDAUTH, 0x0000, snacid); + + /* Screen name */ + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putstr(&fr->data, sn); + + /* Message (null terminated) */ + aimbs_put16(&fr->data, msg ? strlen(msg) : 0); + if (msg) { + aimbs_putstr(&fr->data, msg); + aimbs_put8(&fr->data, 0x00); + } + + /* Unknown */ + aimbs_put16(&fr->data, 0x0000); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0015 - Receive an authorization grant + */ +static int receiveauthgrant(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; + guint16 tmp; + char *sn, *msg; + + /* Read screen name */ + if ((tmp = aimbs_get8(bs))) + sn = aimbs_getstr(bs, tmp); + else + sn = NULL; + + /* Read message (null terminated) */ + if ((tmp = aimbs_get16(bs))) + msg = aimbs_getstr(bs, tmp); + else + msg = NULL; + + /* Unknown */ + tmp = aimbs_get16(bs); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, sn, msg); + + free(sn); + free(msg); + + return ret; +} + +/* + * Subtype 0x0018 - Send authorization request + * + * Sends a request for authorization to the given contact. The request will either be + * granted, denied, or dropped. + * + */ +faim_export int aim_ssi_sendauthrequest(aim_session_t *sess, char *sn, const char *msg) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_FEEDBAG)) || !sn) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)+2+(msg ? strlen(msg)+1 : 0)+2))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_SENDAUTHREQ, 0x0000, NULL, 0); + aim_putsnac(&fr->data, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_SENDAUTHREQ, 0x0000, snacid); + + /* Screen name */ + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putstr(&fr->data, sn); + + /* Message (null terminated) */ + aimbs_put16(&fr->data, msg ? strlen(msg) : 0); + if (msg) { + aimbs_putstr(&fr->data, msg); + aimbs_put8(&fr->data, 0x00); + } + + /* Unknown */ + aimbs_put16(&fr->data, 0x0000); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0019 - Receive an authorization request + */ +static int receiveauthrequest(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; + guint16 tmp; + char *sn, *msg; + + /* Read screen name */ + if ((tmp = aimbs_get8(bs))) + sn = aimbs_getstr(bs, tmp); + else + sn = NULL; + + /* Read message (null terminated) */ + if ((tmp = aimbs_get16(bs))) + msg = aimbs_getstr(bs, tmp); + else + msg = NULL; + + /* Unknown */ + tmp = aimbs_get16(bs); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, sn, msg); + + free(sn); + free(msg); + + return ret; +} + +/* + * Subtype 0x001a - Send authorization reply + * + * Sends a reply to a request for authorization. The reply can either + * grant authorization or deny authorization. + * + * if reply=0x00 then deny + * if reply=0x01 then grant + * + */ +faim_export int aim_ssi_sendauthreply(aim_session_t *sess, char *sn, guint8 reply, const char *msg) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_FEEDBAG)) || !sn) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 1+strlen(sn) + 1 + 2+(msg ? strlen(msg)+1 : 0) + 2))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_SENDAUTHREP, 0x0000, NULL, 0); + aim_putsnac(&fr->data, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_SENDAUTHREP, 0x0000, snacid); + + /* Screen name */ + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putstr(&fr->data, sn); + + /* Grant or deny */ + aimbs_put8(&fr->data, reply); + + /* Message (null terminated) */ + aimbs_put16(&fr->data, msg ? (strlen(msg)+1) : 0); + if (msg) { + aimbs_putstr(&fr->data, msg); + aimbs_put8(&fr->data, 0x00); + } + + /* Unknown */ + aimbs_put16(&fr->data, 0x0000); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x001b - Receive an authorization reply + * You get this bad boy when other people respond to the authorization + * request that you have previously sent them. + */ +static int receiveauthreply(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; + guint16 tmp; + guint8 reply; + char *sn, *msg; + + /* Read screen name */ + if ((tmp = aimbs_get8(bs))) + sn = aimbs_getstr(bs, tmp); + else + sn = NULL; + + /* Read reply */ + reply = aimbs_get8(bs); + + /* Read message (null terminated) */ + if ((tmp = aimbs_get16(bs))) + msg = aimbs_getstr(bs, tmp); + else + msg = NULL; + + /* Unknown */ + tmp = aimbs_get16(bs); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, sn, reply, msg); + + free(sn); + free(msg); + + return ret; +} + +/* + * Subtype 0x001c - Receive a message telling you someone added you to their list. + */ +static int receiveadded(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; + guint16 tmp; + char *sn; + + /* Read screen name */ + if ((tmp = aimbs_get8(bs))) + sn = aimbs_getstr(bs, tmp); + else + sn = NULL; + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, sn); + + free(sn); + + 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 == OSCAR_SUBTYPE_FEEDBAG_RIGHTSINFO) + return parserights(sess, mod, rx, snac, bs); + else if (snac->subtype == OSCAR_SUBTYPE_FEEDBAG_LIST) + return parsedata(sess, mod, rx, snac, bs); + else if (snac->subtype == OSCAR_SUBTYPE_FEEDBAG_ADD) + return parseadd(sess, mod, rx, snac, bs); + else if (snac->subtype == OSCAR_SUBTYPE_FEEDBAG_MOD) + return parsemod(sess, mod, rx, snac, bs); + else if (snac->subtype == OSCAR_SUBTYPE_FEEDBAG_DEL) + return parsedel(sess, mod, rx, snac, bs); + else if (snac->subtype == OSCAR_SUBTYPE_FEEDBAG_SRVACK) + return parseack(sess, mod, rx, snac, bs); + else if (snac->subtype == OSCAR_SUBTYPE_FEEDBAG_NOLIST) + return parsedataunchanged(sess, mod, rx, snac, bs); + else if (snac->subtype == OSCAR_SUBTYPE_FEEDBAG_RECVAUTH) + return receiveauthgrant(sess, mod, rx, snac, bs); + else if (snac->subtype == OSCAR_SUBTYPE_FEEDBAG_RECVAUTHREQ) + return receiveauthrequest(sess, mod, rx, snac, bs); + else if (snac->subtype == OSCAR_SUBTYPE_FEEDBAG_RECVAUTHREP) + return receiveauthreply(sess, mod, rx, snac, bs); + else if (snac->subtype == OSCAR_SUBTYPE_FEEDBAG_ADDED) + return receiveadded(sess, mod, rx, snac, bs); + + return 0; +} + +static void ssi_shutdown(aim_session_t *sess, aim_module_t *mod) +{ + aim_ssi_freelist(sess); +} + +faim_internal int ssi_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = OSCAR_FAMILY_FEEDBAG; + mod->version = 0x0004; + mod->toolid = 0x0110; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "feedbag", sizeof(mod->name)); + mod->snachandler = snachandler; + mod->shutdown = ssi_shutdown; + + return 0; +} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/family_icbm.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/oscar/family_icbm.c Sat Feb 11 21:45:18 2006 +0000 @@ -0,0 +1,2500 @@ +/* + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * Family 0x0004 - Routines for sending/receiving Instant Messages. + * + * Note the term ICBM (Inter-Client Basic Message) which blankets + * all types of generically routed through-server messages. Within + * the ICBM types (family 4), a channel is defined. Each channel + * represents a different type of message. Channel 1 is used for + * what would commonly be called an "instant message". Channel 2 + * is used for negotiating "rendezvous". These transactions end in + * something more complex happening, such as a chat invitation, or + * a file transfer. Channel 3 is used for chat messages (not in + * the same family as these channels). Channel 4 is used for + * various ICQ messages. Examples are normal messages, URLs, and + * old-style authorization. + * + * In addition to the channel, every ICBM contains a cookie. For + * standard IMs, these are only used for error messages. However, + * the more complex rendezvous messages make suitably more complex + * use of this field. + * + * TODO: Split this up into an im.c file an an icbm.c file. It + * will be beautiful, you'll see. + * + * Need to rename all the mpmsg messages to aim_im_bleh. + * + * Make sure aim_conn_findbygroup is used by all functions. + */ + +#include "oscar.h" +#include "peer.h" + +#ifdef _WIN32 +#include "win32dep.h" +#endif + +/** + * Add a standard ICBM header to the given bstream with the given + * information. + * + * @param bs The bstream to write the ICBM header to. + * @param c c is for cookie, and cookie is for me. + * @param channel The ICBM channel (1 through 4). + * @param sn Null-terminated scrizeen nizame. + * @return The number of bytes written. It's really not useful. + */ +static int aim_im_puticbm(aim_bstream_t *bs, const guchar *c, guint16 channel, const char *sn) +{ + aimbs_putraw(bs, c, 8); + aimbs_put16(bs, channel); + aimbs_put8(bs, strlen(sn)); + aimbs_putstr(bs, sn); + return 8+2+1+strlen(sn); +} + +/* + * Extracted from aim_im_sendch2_sendfile_ask + * Generates a random ICBM cookie in a character array of length 8 + * and copies it into the variable passed as cookie + */ +faim_export void aim_icbm_makecookie(guchar *cookie) +{ + int i; + + /* Should be like "21CBF95" and null terminated */ + for (i = 0; i < 7; i++) + cookie[i] = 0x30 + ((guchar)rand() % 10); + cookie[7] = '\0'; +} + +/* + * Takes a msghdr (and a length) and returns a client type + * code. Note that this is *only a guess* and has a low likelihood + * of actually being accurate. + * + * Its based on experimental data, with the help of Eric Warmenhoven + * who seems to have collected a wide variety of different AIM clients. + * + * + * Heres the current collection: + * 0501 0003 0101 0101 01 AOL Mobile Communicator, WinAIM 1.0.414 + * 0501 0003 0101 0201 01 WinAIM 2.0.847, 2.1.1187, 3.0.1464, + * 4.3.2229, 4.4.2286 + * 0501 0004 0101 0102 0101 WinAIM 4.1.2010, libfaim (right here) + * 0501 0003 0101 02 WinAIM 5 + * 0501 0001 01 iChat x.x, mobile buddies + * 0501 0001 0101 01 AOL v6.0, CompuServe 2000 v6.0, any TOC client + * 0501 0002 0106 WinICQ 5.45.1.3777.85 + * + * Note that in this function, only the feature bytes are tested, since + * the rest will always be the same. + * + */ +faim_export guint16 aim_im_fingerprint(const guint8 *msghdr, int len) +{ + static const struct { + guint16 clientid; + int len; + guint8 data[10]; + } fingerprints[] = { + /* AOL Mobile Communicator, WinAIM 1.0.414 */ + { AIM_CLIENTTYPE_MC, + 3, {0x01, 0x01, 0x01}}, + + /* WinAIM 2.0.847, 2.1.1187, 3.0.1464, 4.3.2229, 4.4.2286 */ + { AIM_CLIENTTYPE_WINAIM, + 3, {0x01, 0x01, 0x02}}, + + /* WinAIM 4.1.2010, libfaim */ + { AIM_CLIENTTYPE_WINAIM41, + 4, {0x01, 0x01, 0x01, 0x02}}, + + /* AOL v6.0, CompuServe 2000 v6.0, any TOC client */ + { AIM_CLIENTTYPE_AOL_TOC, + 1, {0x01}}, + + { 0, 0, {0x00}} + }; + int i; + + if (!msghdr || (len <= 0)) + return AIM_CLIENTTYPE_UNKNOWN; + + for (i = 0; fingerprints[i].len; i++) { + if (fingerprints[i].len != len) + continue; + if (memcmp(fingerprints[i].data, msghdr, fingerprints[i].len) == 0) + return fingerprints[i].clientid; + } + + return AIM_CLIENTTYPE_UNKNOWN; +} + +/** + * Subtype 0x0002 - Set ICBM parameters. + * + * I definitely recommend sending this. If you don't, you'll be stuck + * with the rather unreasonable defaults. + * + */ +faim_export int aim_im_setparams(aim_session_t *sess, struct aim_icbmparameters *params) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) + return -EINVAL; + + if (!params) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+16))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0004, 0x0002, 0x0000, snacid); + + /* This is read-only (see Parameter Reply). Must be set to zero here. */ + aimbs_put16(&fr->data, 0x0000); + + /* These are all read-write */ + aimbs_put32(&fr->data, params->flags); + aimbs_put16(&fr->data, params->maxmsglen); + aimbs_put16(&fr->data, params->maxsenderwarn); + aimbs_put16(&fr->data, params->maxrecverwarn); + aimbs_put32(&fr->data, params->minmsginterval); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/** + * Subtype 0x0004 - Request ICBM parameter information. + * + */ +faim_export int aim_im_reqparams(aim_session_t *sess) +{ + aim_conn_t *conn; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) + return -EINVAL; + + return aim_genericreq_n_snacid(sess, conn, 0x0004, 0x0004); +} + +/** + * Subtype 0x0005 - Receive parameter information. + * + */ +static int aim_im_paraminfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_rxcallback_t userfunc; + struct aim_icbmparameters params; + + params.maxchan = aimbs_get16(bs); + params.flags = aimbs_get32(bs); + params.maxmsglen = aimbs_get16(bs); + params.maxsenderwarn = aimbs_get16(bs); + params.maxrecverwarn = aimbs_get16(bs); + params.minmsginterval = aimbs_get32(bs); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + return userfunc(sess, rx, ¶ms); + + return 0; +} + +/** + * Subtype 0x0006 - Send an ICBM (instant message). + * + * + * Possible flags: + * 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). + * + * Generally, you should use the lowest encoding possible to send + * your message. If you only use basic punctuation and the generic + * Latin alphabet, use ASCII7 (no flags). If you happen to use non-ASCII7 + * characters, but they are all clearly defined in ISO-8859-1, then + * use that. Keep in mind that not all characters in the PC ASCII8 + * character set are defined in the ISO standard. For those cases (most + * notably when the (r) symbol is used), you must use the full UNICODE + * encoding for your message. In UNICODE mode, _all_ characters must + * occupy 16bits, including ones that are not special. (Remember that + * the first 128 UNICODE symbols are equivalent to ASCII7, however they + * must be prefixed with a zero high order byte.) + * + * I strongly discourage the use of UNICODE mode, mainly because none + * of the clients I use can parse those messages (and besides that, + * wchars are difficult and non-portable to handle in most UNIX environments). + * If you really need to include special characters, use the HTML UNICODE + * entities. These are of the form ߪ where 2026 is the hex + * representation of the UNICODE index (in this case, UNICODE + * "Horizontal Ellipsis", or 133 in in ASCII8). + * + * Implementation note: Since this is one of the most-used functions + * in all of libfaim, it is written with performance in mind. As such, + * it is not as clear as it could be in respect to how this message is + * supposed to be layed out. Most obviously, tlvlists should be used + * instead of writing out the bytes manually. + * + * XXX - more precise verification that we never send SNACs larger than 8192 + * XXX - check SNAC size for multipart + * + */ +faim_export int aim_im_sendch1_ext(aim_session_t *sess, struct aim_sendimext_args *args) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + guchar cookie[8]; + int msgtlvlen; + static const guint8 deffeatures[] = { 0x01, 0x01, 0x01, 0x02 }; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) + return -EINVAL; + + if (!args) + return -EINVAL; + + if (args->flags & AIM_IMFLAGS_MULTIPART) { + if (args->mpmsg->numparts == 0) + return -EINVAL; + } else { + if (!args->msg || (args->msglen <= 0)) + return -EINVAL; + + if (args->msglen >= MAXMSGLEN) + return -E2BIG; + } + + /* Painfully calculate the size of the message TLV */ + msgtlvlen = 1 + 1; /* 0501 */ + + if (args->flags & AIM_IMFLAGS_CUSTOMFEATURES) + msgtlvlen += 2 + args->featureslen; + else + msgtlvlen += 2 + sizeof(deffeatures); + + if (args->flags & AIM_IMFLAGS_MULTIPART) { + aim_mpmsg_section_t *sec; + + for (sec = args->mpmsg->parts; sec; sec = sec->next) { + msgtlvlen += 2 /* 0101 */ + 2 /* block len */; + msgtlvlen += 4 /* charset */ + sec->datalen; + } + + } else { + msgtlvlen += 2 /* 0101 */ + 2 /* block len */; + msgtlvlen += 4 /* charset */ + args->msglen; + } + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, msgtlvlen+128))) + return -ENOMEM; + + /* XXX - should be optional */ + snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, args->destsn, strlen(args->destsn)+1); + aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); + + /* Generate an ICBM cookie */ + aim_icbm_makecookie(cookie); + + /* ICBM header */ + aim_im_puticbm(&fr->data, cookie, 0x0001, args->destsn); + + /* Message TLV (type 0x0002) */ + aimbs_put16(&fr->data, 0x0002); + aimbs_put16(&fr->data, msgtlvlen); + + /* Features TLV (type 0x0501) */ + aimbs_put16(&fr->data, 0x0501); + if (args->flags & AIM_IMFLAGS_CUSTOMFEATURES) { + aimbs_put16(&fr->data, args->featureslen); + aimbs_putraw(&fr->data, args->features, args->featureslen); + } else { + aimbs_put16(&fr->data, sizeof(deffeatures)); + aimbs_putraw(&fr->data, deffeatures, sizeof(deffeatures)); + } + + if (args->flags & AIM_IMFLAGS_MULTIPART) { + aim_mpmsg_section_t *sec; + + /* Insert each message part in a TLV (type 0x0101) */ + for (sec = args->mpmsg->parts; sec; sec = sec->next) { + aimbs_put16(&fr->data, 0x0101); + aimbs_put16(&fr->data, sec->datalen + 4); + aimbs_put16(&fr->data, sec->charset); + aimbs_put16(&fr->data, sec->charsubset); + aimbs_putraw(&fr->data, (guchar *)sec->data, sec->datalen); + } + + } else { + + /* Insert message text in a TLV (type 0x0101) */ + aimbs_put16(&fr->data, 0x0101); + + /* Message block length */ + aimbs_put16(&fr->data, args->msglen + 0x04); + + /* Character set */ + aimbs_put16(&fr->data, args->charset); + aimbs_put16(&fr->data, args->charsubset); + + /* Message. Not terminated */ + aimbs_putraw(&fr->data, (guchar *)args->msg, args->msglen); + } + + /* Set the Autoresponse flag */ + if (args->flags & AIM_IMFLAGS_AWAY) { + aimbs_put16(&fr->data, 0x0004); + aimbs_put16(&fr->data, 0x0000); + } else if (args->flags & AIM_IMFLAGS_ACK) { + /* Set the Request Acknowledge flag */ + aimbs_put16(&fr->data, 0x0003); + aimbs_put16(&fr->data, 0x0000); + } + + if (args->flags & AIM_IMFLAGS_OFFLINE) { + aimbs_put16(&fr->data, 0x0006); + aimbs_put16(&fr->data, 0x0000); + } + + /* + * Set the I HAVE A REALLY PURTY ICON flag. + * XXX - This should really only be sent on initial + * IMs and when you change your icon. + */ + if (args->flags & AIM_IMFLAGS_HASICON) { + aimbs_put16(&fr->data, 0x0008); + aimbs_put16(&fr->data, 0x000c); + aimbs_put32(&fr->data, args->iconlen); + aimbs_put16(&fr->data, 0x0001); + aimbs_put16(&fr->data, args->iconsum); + aimbs_put32(&fr->data, args->iconstamp); + } + + /* + * Set the Buddy Icon Requested flag. + * XXX - Every time? Surely not... + */ + if (args->flags & AIM_IMFLAGS_BUDDYREQ) { + aimbs_put16(&fr->data, 0x0009); + aimbs_put16(&fr->data, 0x0000); + } + + aim_tx_enqueue(sess, fr); + + /* clean out SNACs over 60sec old */ + aim_cleansnacs(sess, 60); + + return 0; +} + +/* + * Simple wrapper for aim_im_sendch1_ext() + * + * You cannot use aim_send_im if you need the HASICON flag. You must + * use aim_im_sendch1_ext directly for that. + * + * aim_send_im also cannot be used if you require UNICODE messages, because + * that requires an explicit message length. Use aim_im_sendch1_ext(). + * + */ +faim_export int aim_im_sendch1(aim_session_t *sess, const char *sn, guint16 flags, const char *msg) +{ + struct aim_sendimext_args args; + + args.destsn = sn; + args.flags = flags; + args.msg = msg; + args.msglen = strlen(msg); + args.charset = 0x0000; + args.charsubset = 0x0000; + + /* Make these don't get set by accident -- they need aim_im_sendch1_ext */ + args.flags &= ~(AIM_IMFLAGS_CUSTOMFEATURES | AIM_IMFLAGS_HASICON | AIM_IMFLAGS_MULTIPART); + + return aim_im_sendch1_ext(sess, &args); +} + +/* + * Subtype 0x0006 - Send a chat invitation. + */ +faim_export int aim_im_sendch2_chatinvite(aim_session_t *sess, const char *sn, const char *msg, guint16 exchange, const char *roomname, guint16 instance) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + aim_msgcookie_t *msgcookie; + struct aim_invite_priv *priv; + guchar cookie[8]; + aim_tlvlist_t *otl = NULL, *itl = NULL; + guint8 *hdr; + int hdrlen; + aim_bstream_t hdrbs; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) + return -EINVAL; + + if (!sn || !msg || !roomname) + return -EINVAL; + + aim_icbm_makecookie(cookie); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152+strlen(sn)+strlen(roomname)+strlen(msg)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, sn, strlen(sn)+1); + aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); + + /* XXX should be uncached by an unwritten 'invite accept' handler */ + if ((priv = malloc(sizeof(struct aim_invite_priv)))) { + priv->sn = strdup(sn); + priv->roomname = strdup(roomname); + priv->exchange = exchange; + priv->instance = instance; + } + + if ((msgcookie = aim_mkcookie(cookie, AIM_COOKIETYPE_INVITE, priv))) + aim_cachecookie(sess, msgcookie); + else + free(priv); + + /* ICBM Header */ + aim_im_puticbm(&fr->data, cookie, 0x0002, sn); + + /* + * TLV t(0005) + * + * Everything else is inside this TLV. + * + * Sigh. AOL was rather inconsistent right here. So we have + * to play some minor tricks. Right inside the type 5 is some + * raw data, followed by a series of TLVs. + * + */ + hdrlen = 2+8+16+6+4+4+strlen(msg)+4+2+1+strlen(roomname)+2; + hdr = malloc(hdrlen); + aim_bstream_init(&hdrbs, hdr, hdrlen); + + aimbs_put16(&hdrbs, 0x0000); /* Unknown! */ + aimbs_putraw(&hdrbs, cookie, sizeof(cookie)); /* I think... */ + aimbs_putcaps(&hdrbs, AIM_CAPS_CHAT); + + aim_tlvlist_add_16(&itl, 0x000a, 0x0001); + aim_tlvlist_add_noval(&itl, 0x000f); + aim_tlvlist_add_str(&itl, 0x000c, msg); + aim_tlvlist_add_chatroom(&itl, 0x2711, exchange, roomname, instance); + aim_tlvlist_write(&hdrbs, &itl); + + aim_tlvlist_add_raw(&otl, 0x0005, aim_bstream_curpos(&hdrbs), hdr); + + aim_tlvlist_write(&fr->data, &otl); + + free(hdr); + aim_tlvlist_free(&itl); + aim_tlvlist_free(&otl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/** + * Subtype 0x0006 - Send your icon to a given user. + * + * This is also performance sensitive. (If you can believe it...) + * + */ +faim_export int aim_im_sendch2_icon(aim_session_t *sess, const char *sn, const guint8 *icon, int iconlen, time_t stamp, guint16 iconsum) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + guchar cookie[8]; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) + return -EINVAL; + + if (!sn || !icon || (iconlen <= 0) || (iconlen >= MAXICONLEN)) + return -EINVAL; + + aim_icbm_makecookie(cookie); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sn)+2+2+2+8+16+2+2+2+2+2+2+2+4+4+4+iconlen+strlen(AIM_ICONIDENT)+2+2))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); + + /* ICBM header */ + aim_im_puticbm(&fr->data, cookie, 0x0002, sn); + + /* + * TLV t(0005) + * + * Encompasses everything below. + */ + aimbs_put16(&fr->data, 0x0005); + aimbs_put16(&fr->data, 2+8+16+6+4+4+iconlen+4+4+4+strlen(AIM_ICONIDENT)); + + aimbs_put16(&fr->data, 0x0000); + aimbs_putraw(&fr->data, cookie, 8); + aimbs_putcaps(&fr->data, AIM_CAPS_BUDDYICON); + + /* TLV t(000a) */ + aimbs_put16(&fr->data, 0x000a); + aimbs_put16(&fr->data, 0x0002); + aimbs_put16(&fr->data, 0x0001); + + /* TLV t(000f) */ + aimbs_put16(&fr->data, 0x000f); + aimbs_put16(&fr->data, 0x0000); + + /* TLV t(2711) */ + aimbs_put16(&fr->data, 0x2711); + aimbs_put16(&fr->data, 4+4+4+iconlen+strlen(AIM_ICONIDENT)); + aimbs_put16(&fr->data, 0x0000); + aimbs_put16(&fr->data, iconsum); + aimbs_put32(&fr->data, iconlen); + aimbs_put32(&fr->data, stamp); + aimbs_putraw(&fr->data, icon, iconlen); + aimbs_putstr(&fr->data, AIM_ICONIDENT); + + /* TLV t(0003) */ + aimbs_put16(&fr->data, 0x0003); + aimbs_put16(&fr->data, 0x0000); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0006 - Send a rich text message. + * + * This only works for ICQ 2001b (thats 2001 not 2000). Better, only + * send it to clients advertising the RTF capability. In fact, if you send + * it to a client that doesn't support that capability, the server will gladly + * bounce it back to you. + * + * You'd think this would be in icq.c, but, well, I'm trying to stick with + * the one-group-per-file scheme as much as possible. This could easily + * be an exception, since Rendezvous IMs are external of the Oscar core, + * and therefore are undefined. Really I just need to think of a good way to + * make an interface similar to what AOL actually uses. But I'm not using COM. + * + */ +faim_export int aim_im_sendch2_rtfmsg(aim_session_t *sess, struct aim_sendrtfmsg_args *args) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + guchar cookie[8]; + const char rtfcap[] = {"{97B12751-243C-4334-AD22-D6ABF73F1492}"}; /* AIM_CAPS_ICQRTF capability in string form */ + int servdatalen; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) + return -EINVAL; + + if (!args || !args->destsn || !args->rtfmsg) + return -EINVAL; + + servdatalen = 2+2+16+2+4+1+2 + 2+2+4+4+4 + 2+4+2+strlen(args->rtfmsg)+1 + 4+4+4+strlen(rtfcap)+1; + + aim_icbm_makecookie(cookie); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+128+servdatalen))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); + + /* ICBM header */ + aim_im_puticbm(&fr->data, cookie, 0x0002, args->destsn); + + /* TLV t(0005) - Encompasses everything below. */ + aimbs_put16(&fr->data, 0x0005); + aimbs_put16(&fr->data, 2+8+16 + 2+2+2 + 2+2 + 2+2+servdatalen); + + aimbs_put16(&fr->data, 0x0000); + aimbs_putraw(&fr->data, cookie, 8); + aimbs_putcaps(&fr->data, AIM_CAPS_ICQSERVERRELAY); + + /* t(000a) l(0002) v(0001) */ + aimbs_put16(&fr->data, 0x000a); + aimbs_put16(&fr->data, 0x0002); + aimbs_put16(&fr->data, 0x0001); + + /* t(000f) l(0000) v() */ + aimbs_put16(&fr->data, 0x000f); + aimbs_put16(&fr->data, 0x0000); + + /* Service Data TLV */ + aimbs_put16(&fr->data, 0x2711); + aimbs_put16(&fr->data, servdatalen); + + aimbs_putle16(&fr->data, 11 + 16 /* 11 + (sizeof CLSID) */); + aimbs_putle16(&fr->data, 9); + aimbs_putcaps(&fr->data, AIM_CAPS_EMPTY); + aimbs_putle16(&fr->data, 0); + aimbs_putle32(&fr->data, 0); + aimbs_putle8(&fr->data, 0); + aimbs_putle16(&fr->data, 0x03ea); /* trid1 */ + + aimbs_putle16(&fr->data, 14); + aimbs_putle16(&fr->data, 0x03eb); /* trid2 */ + aimbs_putle32(&fr->data, 0); + aimbs_putle32(&fr->data, 0); + aimbs_putle32(&fr->data, 0); + + aimbs_putle16(&fr->data, 0x0001); + aimbs_putle32(&fr->data, 0); + aimbs_putle16(&fr->data, strlen(args->rtfmsg)+1); + aimbs_putraw(&fr->data, (const guint8 *)args->rtfmsg, strlen(args->rtfmsg)+1); + + aimbs_putle32(&fr->data, args->fgcolor); + aimbs_putle32(&fr->data, args->bgcolor); + aimbs_putle32(&fr->data, strlen(rtfcap)+1); + aimbs_putraw(&fr->data, (const guint8 *)rtfcap, strlen(rtfcap)+1); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/** + * Subtype 0x0006 - Send an "I want to directly connect to you" message + * + */ +faim_export int aim_im_sendch2_odcrequest(aim_session_t *sess, guchar *usercookie, gboolean usecookie, const char *sn, const guint8 *ip, guint16 port) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + guchar cookie[8]; + aim_tlvlist_t *tl = NULL, *itl = NULL; + int hdrlen; + guint8 *hdr; + aim_bstream_t hdrbs; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 256+strlen(sn)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); + + /* + * Generate a random message cookie + * + * This cookie needs to be alphanumeric and NULL-terminated to be + * TOC-compatible. + * + * XXX - have I mentioned these should be generated in msgcookie.c? + * + */ + + if (usercookie && usecookie) /* allow user-specified cookie */ + memcpy(cookie, usercookie, 8); + else + aim_icbm_makecookie(cookie); + cookie[7] = '\0'; + + if (usercookie && !usecookie) + memcpy(cookie, usercookie, 8); + + /* ICBM header */ + aim_im_puticbm(&fr->data, cookie, 0x0002, sn); + + aim_tlvlist_add_noval(&tl, 0x0003); + + hdrlen = 2+8+16+6+8+6+4; + hdr = malloc(hdrlen); + aim_bstream_init(&hdrbs, hdr, hdrlen); + + aimbs_put16(&hdrbs, 0x0000); + aimbs_putraw(&hdrbs, cookie, 8); + aimbs_putcaps(&hdrbs, AIM_CAPS_DIRECTIM); + + aim_tlvlist_add_16(&itl, 0x000a, 0x0001); + aim_tlvlist_add_raw(&itl, 0x0003, 4, ip); + aim_tlvlist_add_16(&itl, 0x0005, port); + aim_tlvlist_add_noval(&itl, 0x000f); + + aim_tlvlist_write(&hdrbs, &itl); + + aim_tlvlist_add_raw(&tl, 0x0005, aim_bstream_curpos(&hdrbs), hdr); + + aim_tlvlist_write(&fr->data, &tl); + + free(hdr); + aim_tlvlist_free(&itl); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/** + * Subtype 0x0006 - Send an "I want to send you this file" message + * + */ +faim_export int aim_im_sendch2_sendfile_ask(aim_session_t *sess, struct aim_oft_info *oft_info) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl=NULL, *subtl=NULL; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !oft_info) + return -EINVAL; + + /* The cookie must already have been generated by this point */ + + { /* Create the subTLV chain */ + guint8 *buf; + int buflen; + aim_bstream_t bs; + guint8 ip[4]; + guint8 ip_comp[4]; /* The bitwise complement of the ip */ + char *nexttoken; + int i; + + /* In a stage 2 proxied transfer & a transfer redirect, we send a second "reply request" + * Being the second request for this transfer, its request number is 2 + * You can fill in the blank for a stage 3's request number... */ + if ((oft_info->send_or_recv == AIM_XFER_RECV && oft_info->stage == AIM_XFER_PROXY_STG2) + || (oft_info->send_or_recv == AIM_XFER_RECV + && oft_info->stage == AIM_XFER_PROXY_STG3) + || oft_info->method == AIM_XFER_REDIR) + aim_tlvlist_add_16(&subtl, 0x000a, 0x0002); + else if(oft_info->send_or_recv == AIM_XFER_SEND && oft_info->stage == AIM_XFER_PROXY_STG3) + aim_tlvlist_add_16(&subtl, 0x000a, 0x0003); + else + aim_tlvlist_add_16(&subtl, 0x000a, 0x0001); + + /* This is usually necessary, but ruins a redirect and a stg3 proxy request */ + if(!(oft_info->send_or_recv == AIM_XFER_RECV + && (oft_info->method == AIM_XFER_REDIR || oft_info->stage == AIM_XFER_PROXY_STG3))) { + aim_tlvlist_add_noval(&subtl, 0x000f); + } + + /* If the following is ever enabled, ensure that it is not sent with a receive redirect + * or stage 3 proxy redirect for a file receive (same conditions for sending 0x000f above) */ +/* aim_tlvlist_add_raw(&subtl, 0x000e, 2, "en"); + aim_tlvlist_add_raw(&subtl, 0x000d, 8, "us-ascii"); + aim_tlvlist_add_raw(&subtl, 0x000c, 24, "Please accept this file."); */ + /* XXX - Change oft_info->clientip to an array of 4 bytes */ + if (oft_info->clientip) { + i = 0; + nexttoken = strtok(oft_info->clientip, "."); + while (nexttoken && i<4) { + ip[i] = atoi(nexttoken); + ip_comp[i] = ~ip[i]; + nexttoken = strtok(NULL, "."); + i++; + } + + /* If there is no proxyip, we must fill it in with the clientip */ + if(!oft_info->proxyip) { + aim_tlvlist_add_raw(&subtl, 0x0002, 4, ip); + aim_tlvlist_add_raw(&subtl, 0x0016, 4, ip_comp); /* check? value */ + } + + aim_tlvlist_add_raw(&subtl, 0x0003, 4, ip); + } + + /* Don't send the proxyip & accompanying info during a receive redirect or stg3 proxy request */ + if(!(oft_info->send_or_recv == AIM_XFER_RECV + && (oft_info->method == AIM_XFER_REDIR || oft_info->stage == AIM_XFER_PROXY_STG3))) { + if (oft_info->proxyip) { /* Generate the proxyip */ + i = 0; + nexttoken = strtok(oft_info->proxyip, "."); + while (nexttoken && i<4) { + ip[i] = atoi(nexttoken); + ip_comp[i] = ~ip[i]; + nexttoken = strtok(NULL, "."); + i++; + } + aim_tlvlist_add_raw(&subtl, 0x0002, 4, ip); + /* This zero-length TLV specifies a proxy will be used */ + aim_tlvlist_add_noval(&subtl, 0x0010); + + /* Proxied transfers fail without this next (check?) value */ + aim_tlvlist_add_raw(&subtl, 0x0016, 4, ip_comp); + } + } + + /* Don't send the port & its check during a stage 3 proxy request */ + if(!(oft_info->send_or_recv == AIM_XFER_RECV && oft_info->stage == AIM_XFER_PROXY_STG3)) { + aim_tlvlist_add_16(&subtl, 0x0005, oft_info->port); + + /* Check value? Bitwise complement of the port */ + aim_tlvlist_add_16(&subtl, 0x0017, ~(oft_info->port)); + } + + /* winAIM gets mad at us if we send too much info during a send redirect or stg3 proxy request */ + if(!(oft_info->send_or_recv == AIM_XFER_RECV + && (oft_info->method == AIM_XFER_REDIR || oft_info->stage == AIM_XFER_PROXY_STG3))) { + /* TLV t(2711) */ + buflen = 2+2+4+strlen(oft_info->fh.name)+1; + buf = malloc(buflen); + aim_bstream_init(&bs, buf, buflen); + aimbs_put16(&bs, (oft_info->fh.totfiles > 1) ? 0x0002 : 0x0001); + aimbs_put16(&bs, oft_info->fh.totfiles); + aimbs_put32(&bs, oft_info->fh.totsize); + + /* Filename - NULL terminated, for some odd reason */ + aimbs_putstr(&bs, oft_info->fh.name); + aimbs_put8(&bs, 0x00); + + aim_tlvlist_add_raw(&subtl, 0x2711, bs.len, bs.data); + free(buf); + } + } + + { /* Create the main TLV chain */ + guint8 *buf; + int buflen; + aim_bstream_t bs; + + /* TLV t(0005) - Encompasses everything from above. Gee. */ + buflen = 2+8+16+aim_tlvlist_size(&subtl); + buf = malloc(buflen); + aim_bstream_init(&bs, buf, buflen); + aimbs_put16(&bs, AIM_RENDEZVOUS_PROPOSE); + aimbs_putraw(&bs, oft_info->cookie, 8); + aimbs_putcaps(&bs, AIM_CAPS_SENDFILE); + aim_tlvlist_write(&bs, &subtl); + aim_tlvlist_free(&subtl); + aim_tlvlist_add_raw(&tl, 0x0005, bs.len, bs.data); + free(buf); + + /* TLV t(0003) - Request an ack */ + aim_tlvlist_add_noval(&tl, 0x0003); + } + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 11+strlen(oft_info->sn) + aim_tlvlist_size(&tl)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0006, AIM_SNACFLAGS_DESTRUCTOR, oft_info->cookie, sizeof(oft_info->cookie)); + aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); + + /* ICBM header */ + aim_im_puticbm(&fr->data, oft_info->cookie, 0x0002, oft_info->sn); + + /* All that crap from above (the 0x0005 TLV and the 0x0003 TLV) */ + aim_tlvlist_write(&fr->data, &tl); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/** + * Subtype 0x0006 - Send an "I will accept this file" message? + * + * @param rendid Capability type (AIM_CAPS_GETFILE or AIM_CAPS_SENDFILE) + */ +faim_export int aim_im_sendch2_sendfile_accept(aim_session_t *sess, struct aim_oft_info *oft_info) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !oft_info) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 11+strlen(oft_info->sn) + 4+2+8+16))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); + + /* ICBM header */ + aim_im_puticbm(&fr->data, oft_info->cookie, 0x0002, oft_info->sn); + + aimbs_put16(&fr->data, 0x0005); + aimbs_put16(&fr->data, 0x001a); + aimbs_put16(&fr->data, AIM_RENDEZVOUS_ACCEPT); + aimbs_putraw(&fr->data, oft_info->cookie, 8); + aimbs_putcaps(&fr->data, AIM_CAPS_SENDFILE); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/** + * Subtype 0x0006 - Send a "cancel this file transfer" message? + * + */ +faim_export int aim_im_sendch2_sendfile_cancel(aim_session_t *sess, struct aim_oft_info *oft_info) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !oft_info) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 11+strlen(oft_info->sn) + 4+2+8+16))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); + + /* ICBM header */ + aim_im_puticbm(&fr->data, oft_info->cookie, 0x0002, oft_info->sn); + + aimbs_put16(&fr->data, 0x0005); + aimbs_put16(&fr->data, 0x001a); + aimbs_put16(&fr->data, AIM_RENDEZVOUS_CANCEL); + aimbs_putraw(&fr->data, (const guchar *)oft_info->cookie, 8); + aimbs_putcaps(&fr->data, AIM_CAPS_SENDFILE); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/** + * Subtype 0x0006 - Request the status message of the given ICQ user. + * + * @param sess The oscar session. + * @param sn The UIN of the user of whom you wish to request info. + * @param type The type of info you wish to request. This should be the current + * state of the user, as one of the AIM_ICQ_STATE_* defines. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_im_sendch2_geticqaway(aim_session_t *sess, const char *sn, int type) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + guchar cookie[8]; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !sn) + return -EINVAL; + + aim_icbm_makecookie(cookie); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sn) + 4+0x5e + 4))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); + + /* ICBM header */ + aim_im_puticbm(&fr->data, cookie, 0x0002, sn); + + /* TLV t(0005) - Encompasses almost everything below. */ + aimbs_put16(&fr->data, 0x0005); /* T */ + aimbs_put16(&fr->data, 0x005e); /* L */ + { /* V */ + aimbs_put16(&fr->data, 0x0000); + + /* Cookie */ + aimbs_putraw(&fr->data, cookie, 8); + + /* Put the 16 byte server relay capability */ + aimbs_putcaps(&fr->data, AIM_CAPS_ICQSERVERRELAY); + + /* TLV t(000a) */ + aimbs_put16(&fr->data, 0x000a); + aimbs_put16(&fr->data, 0x0002); + aimbs_put16(&fr->data, 0x0001); + + /* TLV t(000f) */ + aimbs_put16(&fr->data, 0x000f); + aimbs_put16(&fr->data, 0x0000); + + /* TLV t(2711) */ + aimbs_put16(&fr->data, 0x2711); + aimbs_put16(&fr->data, 0x0036); + { /* V */ + aimbs_putle16(&fr->data, 0x001b); /* L */ + aimbs_putle16(&fr->data, 0x0009); /* Protocol version */ + aimbs_putcaps(&fr->data, AIM_CAPS_EMPTY); + aimbs_putle16(&fr->data, 0x0000); /* Unknown */ + aimbs_putle16(&fr->data, 0x0001); /* Client features? */ + aimbs_putle16(&fr->data, 0x0000); /* Unknown */ + aimbs_putle8(&fr->data, 0x00); /* Unkizown */ + aimbs_putle16(&fr->data, 0xffff); /* Sequence number? XXX - This should decrement by 1 with each request */ + + aimbs_putle16(&fr->data, 0x000e); /* L */ + aimbs_putle16(&fr->data, 0xffff); /* Sequence number? XXX - This should decrement by 1 with each request */ + aimbs_putle32(&fr->data, 0x00000000); /* Unknown */ + aimbs_putle32(&fr->data, 0x00000000); /* Unknown */ + aimbs_putle32(&fr->data, 0x00000000); /* Unknown */ + + /* The type of status message being requested */ + if (type & AIM_ICQ_STATE_CHAT) + aimbs_putle16(&fr->data, 0x03ec); + else if(type & AIM_ICQ_STATE_DND) + aimbs_putle16(&fr->data, 0x03eb); + else if(type & AIM_ICQ_STATE_OUT) + aimbs_putle16(&fr->data, 0x03ea); + else if(type & AIM_ICQ_STATE_BUSY) + aimbs_putle16(&fr->data, 0x03e9); + else if(type & AIM_ICQ_STATE_AWAY) + aimbs_putle16(&fr->data, 0x03e8); + + aimbs_putle16(&fr->data, 0x0001); /* Status? */ + aimbs_putle16(&fr->data, 0x0001); /* Priority of this message? */ + aimbs_putle16(&fr->data, 0x0001); /* L */ + aimbs_putle8(&fr->data, 0x00); /* String of length L */ + } /* End TLV t(2711) */ + } /* End TLV t(0005) */ + + /* TLV t(0003) */ + aimbs_put16(&fr->data, 0x0003); + aimbs_put16(&fr->data, 0x0000); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/** + * Subtype 0x0006 - Send an ICQ-esque ICBM. + * + * This can be used to send an ICQ authorization reply (deny or grant). It is the "old way." + * The new way is to use SSI. I like the new way a lot better. This seems like such a hack, + * mostly because it's in network byte order. Figuring this stuff out sometimes takes a while, + * but thats ok, because it gives me time to try to figure out what kind of drugs the AOL people + * were taking when they merged the two protocols. + * + * @param sn The destination screen name. + * @param type The type of message. 0x0007 for authorization denied. 0x0008 for authorization granted. + * @param message The message you want to send, it should be null terminated. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_im_sendch4(aim_session_t *sess, const char *sn, guint16 type, const char *message) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + guchar cookie[8]; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0002))) + return -EINVAL; + + if (!sn || !type || !message) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+3+strlen(sn)+12+strlen(message)+1+4))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); + + aim_icbm_makecookie(cookie); + + /* ICBM header */ + aim_im_puticbm(&fr->data, cookie, 0x0004, sn); + + /* + * TLV t(0005) + * + * ICQ data (the UIN and the message). + */ + aimbs_put16(&fr->data, 0x0005); + aimbs_put16(&fr->data, 4 + 2+2+strlen(message)+1); + + /* + * Your UIN + */ + aimbs_putle32(&fr->data, atoi(sess->sn)); + + /* + * TLV t(type) l(strlen(message)+1) v(message+NULL) + */ + aimbs_putle16(&fr->data, type); + aimbs_putle16(&fr->data, strlen(message)+1); + aimbs_putraw(&fr->data, (const guint8 *)message, strlen(message)+1); + + /* + * TLV t(0006) l(0000) v() + */ + aimbs_put16(&fr->data, 0x0006); + aimbs_put16(&fr->data, 0x0000); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * XXX - I don't see when this would ever get called... + */ +static int outgoingim(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; + guchar cookie[8]; + guint16 channel; + aim_tlvlist_t *tlvlist; + char *sn; + int snlen; + guint16 icbmflags = 0; + guint8 flag1 = 0, flag2 = 0; + gchar *msg = NULL; + aim_tlv_t *msgblock; + + /* ICBM Cookie. */ + aim_icbm_makecookie(cookie); + + /* Channel ID */ + channel = aimbs_get16(bs); + + if (channel != 0x01) { + gaim_debug_misc("oscar", "icbm: ICBM recieved on unsupported channel. Ignoring. (chan = %04x)\n", channel); + return 0; + } + + snlen = aimbs_get8(bs); + sn = aimbs_getstr(bs, snlen); + + tlvlist = aim_tlvlist_read(bs); + + if (aim_tlv_gettlv(tlvlist, 0x0003, 1)) + icbmflags |= AIM_IMFLAGS_ACK; + if (aim_tlv_gettlv(tlvlist, 0x0004, 1)) + icbmflags |= AIM_IMFLAGS_AWAY; + + if ((msgblock = aim_tlv_gettlv(tlvlist, 0x0002, 1))) { + aim_bstream_t mbs; + int featurelen, msglen; + + aim_bstream_init(&mbs, msgblock->value, msgblock->length); + + aimbs_get8(&mbs); + aimbs_get8(&mbs); + for (featurelen = aimbs_get16(&mbs); featurelen; featurelen--) + aimbs_get8(&mbs); + aimbs_get8(&mbs); + aimbs_get8(&mbs); + + msglen = aimbs_get16(&mbs) - 4; /* final block length */ + + flag1 = aimbs_get16(&mbs); + flag2 = aimbs_get16(&mbs); + + msg = aimbs_getstr(&mbs, msglen); + } + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, channel, sn, msg, icbmflags, flag1, flag2); + + free(sn); + aim_tlvlist_free(&tlvlist); + + return ret; +} + +/* + * Ahh, the joys of nearly ridiculous over-engineering. + * + * Not only do AIM ICBM's support multiple channels. Not only do they + * support multiple character sets. But they support multiple character + * sets / encodings within the same ICBM. + * + * These multipart messages allow for complex space savings techniques, which + * seem utterly unnecessary by today's standards. In fact, there is only + * one client still in popular use that still uses this method: AOL for the + * Macintosh, Version 5.0. Obscure, yes, I know. + * + * In modern (non-"legacy") clients, if the user tries to send a character + * that is not ISO-8859-1 or ASCII, the client will send the entire message + * as UNICODE, meaning that every character in the message will occupy the + * full 16 bit UNICODE field, even if the high order byte would be zero. + * Multipart messages prevent this wasted space by allowing the client to + * only send the characters in UNICODE that need to be sent that way, and + * the rest of the message can be sent in whatever the native character + * set is (probably ASCII). + * + * An important note is that sections will be displayed in the order that + * they appear in the ICBM. There is no facility for merging or rearranging + * sections at run time. So if you have, say, ASCII then UNICODE then ASCII, + * you must supply two ASCII sections with a UNICODE in the middle, and incur + * the associated overhead. + * + * Normally I would have laughed and given a firm 'no' to supporting this + * seldom-used feature, but something is attracting me to it. In the future, + * it may be possible to abuse this to send mixed-media messages to other + * open source clients (like encryption or something) -- see faimtest for + * examples of how to do this. + * + * I would definitely recommend avoiding this feature unless you really + * know what you are doing, and/or you have something neat to do with it. + * + */ +faim_export int aim_mpmsg_init(aim_session_t *sess, aim_mpmsg_t *mpm) +{ + + memset(mpm, 0, sizeof(aim_mpmsg_t)); + + return 0; +} + +static int mpmsg_addsection(aim_session_t *sess, aim_mpmsg_t *mpm, guint16 charset, guint16 charsubset, gchar *data, guint16 datalen) +{ + aim_mpmsg_section_t *sec; + + if (!(sec = malloc(sizeof(aim_mpmsg_section_t)))) + return -1; + + sec->charset = charset; + sec->charsubset = charsubset; + sec->data = data; + sec->datalen = datalen; + sec->next = NULL; + + if (!mpm->parts) + mpm->parts = sec; + else { + aim_mpmsg_section_t *cur; + + for (cur = mpm->parts; cur->next; cur = cur->next) + ; + cur->next = sec; + } + + mpm->numparts++; + + return 0; +} + +faim_export int aim_mpmsg_addraw(aim_session_t *sess, aim_mpmsg_t *mpm, guint16 charset, guint16 charsubset, const gchar *data, guint16 datalen) +{ + gchar *dup; + + if (!(dup = malloc(datalen))) + return -1; + memcpy(dup, data, datalen); + + if (mpmsg_addsection(sess, mpm, charset, charsubset, dup, datalen) == -1) { + free(dup); + return -1; + } + + return 0; +} + +/* XXX - should provide a way of saying ISO-8859-1 specifically */ +faim_export int aim_mpmsg_addascii(aim_session_t *sess, aim_mpmsg_t *mpm, const char *ascii) +{ + gchar *dup; + + if (!(dup = strdup(ascii))) + return -1; + + if (mpmsg_addsection(sess, mpm, 0x0000, 0x0000, dup, strlen(ascii)) == -1) { + free(dup); + return -1; + } + + return 0; +} + +faim_export int aim_mpmsg_addunicode(aim_session_t *sess, aim_mpmsg_t *mpm, const guint16 *unicode, guint16 unicodelen) +{ + gchar *buf; + aim_bstream_t bs; + int i; + + if (!(buf = malloc(unicodelen * 2))) + return -1; + + aim_bstream_init(&bs, (guchar *)buf, unicodelen * 2); + + /* We assume unicode is in /host/ byte order -- convert to network */ + for (i = 0; i < unicodelen; i++) + aimbs_put16(&bs, unicode[i]); + + if (mpmsg_addsection(sess, mpm, 0x0002, 0x0000, buf, aim_bstream_curpos(&bs)) == -1) { + free(buf); + return -1; + } + + return 0; +} + +faim_export void aim_mpmsg_free(aim_session_t *sess, aim_mpmsg_t *mpm) +{ + aim_mpmsg_section_t *cur; + + for (cur = mpm->parts; cur; ) { + aim_mpmsg_section_t *tmp; + + tmp = cur->next; + free(cur->data); + free(cur); + cur = tmp; + } + + mpm->numparts = 0; + mpm->parts = NULL; + + return; +} + +/* + * Start by building the multipart structures, then pick the first + * human-readable section and stuff it into args->msg so no one gets + * suspicious. + * + */ +static int incomingim_ch1_parsemsgs(aim_session_t *sess, aim_userinfo_t *userinfo, guint8 *data, int len, struct aim_incomingim_ch1_args *args) +{ + /* Should this be ASCII -> UNICODE -> Custom */ + static const guint16 charsetpri[] = { + AIM_CHARSET_ASCII, /* ASCII first */ + AIM_CHARSET_CUSTOM, /* then ISO-8859-1 */ + AIM_CHARSET_UNICODE, /* UNICODE as last resort */ + }; + static const int charsetpricount = 3; + int i; + aim_bstream_t mbs; + aim_mpmsg_section_t *sec; + + aim_bstream_init(&mbs, data, len); + + while (aim_bstream_empty(&mbs)) { + guint16 msglen, flag1, flag2; + gchar *msgbuf; + + aimbs_get8(&mbs); /* 01 */ + aimbs_get8(&mbs); /* 01 */ + + /* Message string length, including character set info. */ + msglen = aimbs_get16(&mbs); + if (msglen > aim_bstream_empty(&mbs)) + { + gaim_debug_misc("oscar", "Received an IM containing an invalid message part from %s. They are probably trying to do something malicious.", userinfo->sn); + break; + } + + /* Character set info */ + flag1 = aimbs_get16(&mbs); + flag2 = aimbs_get16(&mbs); + + /* Message. */ + msglen -= 4; + + /* + * For now, we don't care what the encoding is. Just copy + * it into a multipart struct and deal with it later. However, + * always pad the ending with a NULL. This makes it easier + * to treat ASCII sections as strings. It won't matter for + * UNICODE or binary data, as you should never read past + * the specified data length, which will not include the pad. + * + * XXX - There's an API bug here. For sending, the UNICODE is + * given in host byte order (aim_mpmsg_addunicode), but here + * the received messages are given in network byte order. + * + */ + msgbuf = (gchar *)aimbs_getraw(&mbs, msglen); + mpmsg_addsection(sess, &args->mpmsg, flag1, flag2, msgbuf, msglen); + + } /* while */ + + args->icbmflags |= AIM_IMFLAGS_MULTIPART; /* always set */ + + /* + * Clients that support multiparts should never use args->msg, as it + * will point to an arbitrary section. + * + * Here, we attempt to provide clients that do not support multipart + * messages with something to look at -- hopefully a human-readable + * string. But, failing that, a UNICODE message, or nothing at all. + * + * Which means that even if args->msg is NULL, it does not mean the + * message was blank. + * + */ + for (i = 0; i < charsetpricount; i++) { + for (sec = args->mpmsg.parts; sec; sec = sec->next) { + + if (sec->charset != charsetpri[i]) + continue; + + /* Great. We found one. Fill it in. */ + args->charset = sec->charset; + args->charsubset = sec->charsubset; + + /* Set up the simple flags */ + switch (args->charsubset) + { + case 0x0000: + /* standard subencoding? */ + break; + case 0x000b: + args->icbmflags |= AIM_IMFLAGS_SUBENC_MACINTOSH; + break; + case 0xffff: + /* no subencoding */ + break; + default: + break; + } + + args->msg = sec->data; + args->msglen = sec->datalen; + + return 0; + } + } + + /* No human-readable sections found. Oh well. */ + args->charset = args->charsubset = 0xffff; + args->msg = NULL; + args->msglen = 0; + + return 0; +} + +static int incomingim_ch1(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, aim_bstream_t *bs, guint8 *cookie) +{ + guint16 type, length; + aim_rxcallback_t userfunc; + int ret = 0; + struct aim_incomingim_ch1_args args; + unsigned int endpos; + + memset(&args, 0, sizeof(args)); + + aim_mpmsg_init(sess, &args.mpmsg); + + /* + * This used to be done using tlvchains. For performance reasons, + * I've changed it to process the TLVs in-place. This avoids lots + * of per-IM memory allocations. + */ + while (aim_bstream_empty(bs)) + { + type = aimbs_get16(bs); + length = aimbs_get16(bs); + + if (length > aim_bstream_empty(bs)) + { + gaim_debug_misc("oscar", "Received an IM containing an invalid message part from %s. They are probably trying to do something malicious.\n", userinfo->sn); + break; + } + + endpos = aim_bstream_curpos(bs) + length; + + if (type == 0x0002) { /* Message Block */ + + /* + * This TLV consists of the following: + * - 0501 -- Unknown + * - Features: Don't know how to interpret these + * - 0101 -- Unknown + * - Message + * + */ + + aimbs_get8(bs); /* 05 */ + aimbs_get8(bs); /* 01 */ + + args.featureslen = aimbs_get16(bs); + if (args.featureslen > aim_bstream_empty(bs)) + { + gaim_debug_misc("oscar", "Received an IM containing an invalid message part from %s. They are probably trying to do something malicious.\n", userinfo->sn); + break; + } + if (args.featureslen == 0) + { + args.features = NULL; + } + else + { + args.features = aimbs_getraw(bs, args.featureslen); + args.icbmflags |= AIM_IMFLAGS_CUSTOMFEATURES; + } + + /* + * The rest of the TLV contains one or more message + * blocks... + */ + incomingim_ch1_parsemsgs(sess, userinfo, bs->data + bs->offset /* XXX evil!!! */, length - 2 - 2 - args.featureslen, &args); + + } else if (type == 0x0003) { /* Server Ack Requested */ + + args.icbmflags |= AIM_IMFLAGS_ACK; + + } 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 */ + + args.iconlen = aimbs_get32(bs); + aimbs_get16(bs); /* 0x0001 */ + args.iconsum = aimbs_get16(bs); + args.iconstamp = aimbs_get32(bs); + + /* + * This looks to be a client bug. MacAIM 4.3 will + * send this tag, but with all zero values, in the + * first message of a conversation. This makes no + * sense whatsoever, so I'm going to say its a bug. + * + * You really shouldn't advertise a zero-length icon + * anyway. + * + */ + if (args.iconlen) + args.icbmflags |= AIM_IMFLAGS_HASICON; + + } else if (type == 0x0009) { + + args.icbmflags |= AIM_IMFLAGS_BUDDYREQ; + + } else if (type == 0x000b) { /* Non-direct connect typing notification */ + + args.icbmflags |= AIM_IMFLAGS_TYPINGNOT; + + } else if (type == 0x0017) { + + free(args.extdata); + args.extdatalen = length; + if (args.extdatalen > aim_bstream_empty(bs)) + { + gaim_debug_misc("oscar", "Received an IM containing an invalid message part from %s. They are probably trying to do something malicious.\n", userinfo->sn); + break; + } + if (args.extdatalen == 0) + args.extdata = NULL; + else + args.extdata = aimbs_getraw(bs, args.extdatalen); + + } else { + gaim_debug_misc("oscar", "incomingim_ch1: unknown TLV 0x%04x (len %d)\n", type, length); + } + + /* + * This is here to protect ourselves from ourselves. That + * is, if something above doesn't completely parse its value + * section, or, worse, overparses it, this will set the + * stream where it needs to be in order to land on the next + * TLV when the loop continues. + * + */ + aim_bstream_setpos(bs, endpos); + } + + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, channel, userinfo, &args); + + aim_mpmsg_free(sess, &args.mpmsg); + free(args.features); + free(args.extdata); + + return ret; +} + +static void incomingim_ch2_buddylist(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_bstream_t *servdata) +{ + + /* + * This goes like this... + * + * group name length + * group name + * num of buddies in group + * buddy name length + * buddy name + * buddy name length + * buddy name + * ... + * group name length + * group name + * num of buddies in group + * buddy name length + * buddy name + * ... + * ... + */ + while (servdata && aim_bstream_empty(servdata)) { + guint16 gnlen, numb; + int i; + char *gn; + + gnlen = aimbs_get16(servdata); + gn = aimbs_getstr(servdata, gnlen); + numb = aimbs_get16(servdata); + + for (i = 0; i < numb; i++) { + guint16 bnlen; + char *bn; + + bnlen = aimbs_get16(servdata); + bn = aimbs_getstr(servdata, bnlen); + + gaim_debug_misc("oscar", "got a buddy list from %s: group %s, buddy %s\n", userinfo->sn, gn, bn); + + free(bn); + } + + free(gn); + } + + return; +} + +static void incomingim_ch2_buddyicon_free(aim_session_t *sess, struct aim_incomingim_ch2_args *args) +{ + + free(args->info.icon.icon); + + return; +} + +static void incomingim_ch2_buddyicon(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_bstream_t *servdata) +{ + + if (servdata) { + args->info.icon.checksum = aimbs_get32(servdata); + args->info.icon.length = aimbs_get32(servdata); + args->info.icon.timestamp = aimbs_get32(servdata); + args->info.icon.icon = aimbs_getraw(servdata, args->info.icon.length); + } + + args->destructor = (void *)incomingim_ch2_buddyicon_free; + + return; +} + +static void incomingim_ch2_chat_free(aim_session_t *sess, struct aim_incomingim_ch2_args *args) +{ + + /* XXX - aim_chat_roominfo_free() */ + free(args->info.chat.roominfo.name); + + return; +} + +static void incomingim_ch2_chat(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_bstream_t *servdata) +{ + + /* + * Chat room info. + */ + if (servdata) + aim_chat_readroominfo(servdata, &args->info.chat.roominfo); + + args->destructor = (void *)incomingim_ch2_chat_free; + + return; +} + +static void incomingim_ch2_icqserverrelay_free(aim_session_t *sess, struct aim_incomingim_ch2_args *args) +{ + + free((char *)args->info.rtfmsg.rtfmsg); + + return; +} + +/* + * The relationship between AIM_CAPS_ICQSERVERRELAY and AIM_CAPS_ICQRTF is + * kind of odd. This sends the client ICQRTF since that is all that I've seen + * SERVERRELAY used for. + * + * Note that this is all little-endian. Cringe. + * + */ +static void incomingim_ch2_icqserverrelay(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_bstream_t *servdata) +{ + guint16 hdrlen, anslen, msglen; + + hdrlen = aimbs_getle16(servdata); + aim_bstream_advance(servdata, hdrlen); + + hdrlen = aimbs_getle16(servdata); + aim_bstream_advance(servdata, hdrlen); + + args->info.rtfmsg.msgtype = aimbs_getle16(servdata); + + anslen = aimbs_getle32(servdata); + aim_bstream_advance(servdata, anslen); + + msglen = aimbs_getle16(servdata); + args->info.rtfmsg.rtfmsg = aimbs_getstr(servdata, msglen); + + args->info.rtfmsg.fgcolor = aimbs_getle32(servdata); + args->info.rtfmsg.bgcolor = aimbs_getle32(servdata); + + hdrlen = aimbs_getle32(servdata); + aim_bstream_advance(servdata, hdrlen); + + args->destructor = (void *)incomingim_ch2_icqserverrelay_free; + + return; +} + +static void incomingim_ch2_sendfile_free(aim_session_t *sess, struct aim_incomingim_ch2_args *args) +{ + free(args->info.sendfile.filename); +} + +static void incomingim_ch2_sendfile(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_bstream_t *servdata) +{ + + args->destructor = (void *)incomingim_ch2_sendfile_free; + + /* Maybe there is a better way to tell what kind of sendfile + * this is? Maybe TLV t(000a)? */ + if (servdata) { /* Someone is sending us a file */ + int flen; + + /* subtype is one of AIM_OFT_SUBTYPE_* */ + args->info.sendfile.subtype = aimbs_get16(servdata); + args->info.sendfile.totfiles = aimbs_get16(servdata); + args->info.sendfile.totsize = aimbs_get32(servdata); + + /* + * I hope to God I'm right when I guess that there is a + * 32 char max filename length for single files. I think + * OFT tends to do that. Gotta love inconsistency. I saw + * a 26 byte filename? + */ + /* AAA - create an aimbs_getnullstr function (don't anymore)(maybe) */ + /* Use an inelegant way of getting the null-terminated filename, + * since there's no easy bstream routine. */ + for (flen = 0; aimbs_get8(servdata); flen++); + aim_bstream_advance(servdata, -flen -1); + args->info.sendfile.filename = aimbs_getstr(servdata, flen); + + /* There is sometimes more after the null-terminated filename, + * but I'm unsure of its format. */ + /* I don't believe him. */ + /* There is sometimes a null byte inside a unicode filename, + * but as far as I can tell the filename is the last + * piece of data that will be in this message. --Jonathan */ + } + + return; +} + +typedef void (*ch2_args_destructor_t)(aim_session_t *sess, struct aim_incomingim_ch2_args *args); + +static int incomingim_ch2(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, aim_tlvlist_t *tlvlist, guint8 *cookie) +{ + aim_rxcallback_t userfunc; + aim_tlv_t *block1, *servdatatlv; + aim_tlvlist_t *list2; + struct aim_incomingim_ch2_args args; + aim_bstream_t bbs, sdbs, *sdbsptr = NULL; + guint8 *cookie2; + int ret = 0; + + char proxyip[30] = {""}; + char clientip[30] = {""}; + char verifiedip[30] = {""}; + + memset(&args, 0, sizeof(args)); + + /* + * There's another block of TLVs embedded in the type 5 here. + */ + block1 = aim_tlv_gettlv(tlvlist, 0x0005, 1); + aim_bstream_init(&bbs, block1->value, block1->length); + + /* + * First two bytes represent the status of the connection. + * + * 0 is a request, 1 is a cancel, 2 is an accept + */ + args.status = aimbs_get16(&bbs); + + /* + * Next comes the cookie. Should match the ICBM cookie. + */ + cookie2 = aimbs_getraw(&bbs, 8); + if (memcmp(cookie, cookie2, 8) != 0) + gaim_debug_misc("oscar", "rend: warning cookies don't match!\n"); + memcpy(args.cookie, cookie2, 8); + free(cookie2); + + /* + * The next 16bytes are a capability block so we can + * identify what type of rendezvous this is. + */ + args.reqclass = aim_locate_getcaps(sess, &bbs, 0x10); + + /* + * What follows may be TLVs or nothing, depending on the + * purpose of the message. + * + * Ack packets for instance have nothing more to them. + */ + list2 = aim_tlvlist_read(&bbs); + + /* + * IP address to proxy the file transfer through. + * + * XXX - I don't like this. Maybe just read in an int? Or inet_ntoa... + */ + if (aim_tlv_gettlv(list2, 0x0002, 1)) { + aim_tlv_t *iptlv; + + iptlv = aim_tlv_gettlv(list2, 0x0002, 1); + if (iptlv->length == 4) + snprintf(proxyip, sizeof(proxyip), "%hhu.%hhu.%hhu.%hhu", + iptlv->value[0], iptlv->value[1], + iptlv->value[2], iptlv->value[3]); + } + + /* + * IP address from the perspective of the client. + */ + if (aim_tlv_gettlv(list2, 0x0003, 1)) { + aim_tlv_t *iptlv; + + iptlv = aim_tlv_gettlv(list2, 0x0003, 1); + if (iptlv->length == 4) + snprintf(clientip, sizeof(clientip), "%hhu.%hhu.%hhu.%hhu", + iptlv->value[0], iptlv->value[1], + iptlv->value[2], iptlv->value[3]); + } + + /* + * Verified IP address (from the perspective of Oscar). + * + * This is added by the server. + */ + if (aim_tlv_gettlv(list2, 0x0004, 1)) { + aim_tlv_t *iptlv; + + iptlv = aim_tlv_gettlv(list2, 0x0004, 1); + if (iptlv->length == 4) + snprintf(verifiedip, sizeof(verifiedip), "%hhu.%hhu.%hhu.%hhu", + iptlv->value[0], iptlv->value[1], + iptlv->value[2], iptlv->value[3]); + } + + /* + * Port number for something. + */ + if (aim_tlv_gettlv(list2, 0x0005, 1)) + args.port = aim_tlv_get16(list2, 0x0005, 1); + + /* + * Something to do with ft? -- two bytes + * 0x0001 - "I want to send you this file" + * 0x0002 - "I will accept this file from you" + * 0x0002 - Also used in ICQ Lite Beta 4.0 URLs + */ + /* + * This is what I call the request number of the file transfer + * 0x0001 - Initial file transfer request for no proxy or stage 1 proxy + * 0x0002 - "Reply request" for a stage 2 proxy (receiver wants to use proxy) + * 0x0003 - A third request has been sent; applies only to stage 3 proxied transfers + * -- Jonathan + */ + if (aim_tlv_gettlv(list2, 0x000a, 1)) + args.info.sendfile.reqnum = aim_tlv_get16(list2, 0x000a, 1); + + /* + * Error code. + */ + if (aim_tlv_gettlv(list2, 0x000b, 1)) + args.errorcode = aim_tlv_get16(list2, 0x000b, 1); + + /* + * Invitation message / chat description. + */ + if (aim_tlv_gettlv(list2, 0x000c, 1)) { + args.msg = aim_tlv_getstr(list2, 0x000c, 1); + args.msglen = aim_tlv_getlength(list2, 0x000c, 1); + } + + /* + * Character set. + */ + if (aim_tlv_gettlv(list2, 0x000d, 1)) + args.encoding = aim_tlv_getstr(list2, 0x000d, 1); + + /* + * Language. + */ + if (aim_tlv_gettlv(list2, 0x000e, 1)) + args.language = aim_tlv_getstr(list2, 0x000e, 1); + +#if 0 + /* + * Unknown -- no value + * + * Maybe means we should connect directly to transfer the file? + * Also used in ICQ Lite Beta 4.0 URLs. Also empty. + */ + /* I don't think this indicates a direct transfer; this flag is + * also present in a stage 1 proxied file send request -- Jonathan */ + if (aim_tlv_gettlv(list2, 0x000f, 1)) { + /* Unhandled */ + } +#endif + + /* + * Flag meaning we should proxy the file transfer through an AIM server + */ + if (aim_tlv_gettlv(list2, 0x0010, 1)) + args.info.sendfile.use_proxy = TRUE; + else + args.info.sendfile.use_proxy = FALSE; + + if (strlen(proxyip)) + args.proxyip = (char *)proxyip; + if (strlen(clientip)) + args.clientip = (char *)clientip; + if (strlen(verifiedip)) + args.verifiedip = (char *)verifiedip; + + /* + * This must be present in PROPOSALs, but will probably not + * exist in CANCELs and ACCEPTs. Also exists in ICQ Lite + * Beta 4.0 URLs (AIM_CAPS_ICQSERVERRELAY). + * + * Service Data blocks are module-specific in format. + */ + if ((servdatatlv = aim_tlv_gettlv(list2, 0x2711 /* 10001 */, 1))) { + + aim_bstream_init(&sdbs, servdatatlv->value, servdatatlv->length); + sdbsptr = &sdbs; + } + + /* + * The rest of the handling depends on what type it is. + * + * Not all of them have special handling (yet). + */ + if (args.reqclass & AIM_CAPS_BUDDYICON) + incomingim_ch2_buddyicon(sess, mod, rx, snac, userinfo, &args, sdbsptr); + else if (args.reqclass & AIM_CAPS_SENDBUDDYLIST) + incomingim_ch2_buddylist(sess, mod, rx, snac, userinfo, &args, sdbsptr); + else if (args.reqclass & AIM_CAPS_CHAT) + incomingim_ch2_chat(sess, mod, rx, snac, userinfo, &args, sdbsptr); + else if (args.reqclass & AIM_CAPS_ICQSERVERRELAY) + incomingim_ch2_icqserverrelay(sess, mod, rx, snac, userinfo, &args, sdbsptr); + else if (args.reqclass & AIM_CAPS_SENDFILE) + incomingim_ch2_sendfile(sess, mod, rx, snac, userinfo, &args, sdbsptr); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, channel, userinfo, &args); + + + if (args.destructor) + ((ch2_args_destructor_t)args.destructor)(sess, &args); + + free((char *)args.msg); + free((char *)args.encoding); + free((char *)args.language); + + aim_tlvlist_free(&list2); + + return ret; +} + +static int incomingim_ch4(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, aim_tlvlist_t *tlvlist, guint8 *cookie) +{ + aim_bstream_t meat; + aim_rxcallback_t userfunc; + aim_tlv_t *block; + struct aim_incomingim_ch4_args args; + int ret = 0; + + /* + * Make a bstream for the meaty part. Yum. Meat. + */ + if (!(block = aim_tlv_gettlv(tlvlist, 0x0005, 1))) + return -1; + aim_bstream_init(&meat, block->value, block->length); + + args.uin = aimbs_getle32(&meat); + args.type = aimbs_getle8(&meat); + args.flags = aimbs_getle8(&meat); + args.msglen = aimbs_getle16(&meat); + args.msg = (gchar *)aimbs_getraw(&meat, args.msglen); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, channel, userinfo, &args); + + free(args.msg); + + return ret; +} + +/* + * Subtype 0x0007 + * + * It can easily be said that parsing ICBMs is THE single + * most difficult thing to do in the in AIM protocol. In + * fact, I think I just did say that. + * + * Below is the best damned solution I've come up with + * over the past sixteen months of battling with it. This + * can parse both away and normal messages from every client + * I have access to. Its not fast, its not clean. But it works. + * + */ +static int incomingim(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + guchar *cookie; + guint16 channel; + aim_userinfo_t userinfo; + + memset(&userinfo, 0x00, sizeof(aim_userinfo_t)); + + /* + * Read ICBM Cookie. + */ + cookie = aimbs_getraw(bs, 8); + + /* + * Channel ID. + * + * Channel 0x0001 is the message channel. It is + * used to send basic ICBMs. + * + * Channel 0x0002 is the Rendezvous channel, which + * is where Chat Invitiations and various client-client + * connection negotiations come from. + * + * Channel 0x0003 is used for chat messages. + * + * Channel 0x0004 is used for ICQ authorization, or + * possibly any system notice. + * + */ + channel = aimbs_get16(bs); + + /* + * Extract the standard user info block. + * + * Note that although this contains TLVs that appear contiguous + * with the TLVs read below, they are two different pieces. The + * userinfo block contains the number of TLVs that contain user + * information, the rest are not even though there is no separation. + * You can start reading the message TLVs after aim_info_extract() + * parses out the standard userinfo block. + * + * That also means that TLV types can be duplicated between the + * userinfo block and the rest of the message, however there should + * never be two TLVs of the same type in one block. + * + */ + aim_info_extract(sess, bs, &userinfo); + + /* + * From here on, its depends on what channel we're on. + * + * Technically all channels have a TLV list have this, however, + * for the common channel 1 case, in-place parsing is used for + * performance reasons (less memory allocation). + */ + if (channel == 1) { + + ret = incomingim_ch1(sess, mod, rx, snac, channel, &userinfo, bs, cookie); + + } else if (channel == 2) { + aim_tlvlist_t *tlvlist; + + /* + * Read block of TLVs (not including the userinfo data). All + * further data is derived from what is parsed here. + */ + tlvlist = aim_tlvlist_read(bs); + + ret = incomingim_ch2(sess, mod, rx, snac, channel, &userinfo, tlvlist, cookie); + + aim_tlvlist_free(&tlvlist); + + } else if (channel == 4) { + aim_tlvlist_t *tlvlist; + + tlvlist = aim_tlvlist_read(bs); + ret = incomingim_ch4(sess, mod, rx, snac, channel, &userinfo, tlvlist, cookie); + aim_tlvlist_free(&tlvlist); + + } else { + gaim_debug_misc("oscar", "icbm: ICBM received on an unsupported channel. Ignoring. (chan = %04x)\n", channel); + } + + aim_info_free(&userinfo); + free(cookie); + + return ret; +} + +/* + * Subtype 0x0008 - Send a warning to sn. + * + * Flags: + * AIM_WARN_ANON Send as an anonymous (doesn't count as much) + * + * returns -1 on error (couldn't alloc packet), 0 on success. + * + */ +faim_export int aim_im_warn(aim_session_t *sess, aim_conn_t *conn, const char *sn, guint32 flags) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !conn || !sn) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, strlen(sn)+13))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0008, 0x0000, sn, strlen(sn)+1); + aim_putsnac(&fr->data, 0x0004, 0x0008, 0x0000, snacid); + + aimbs_put16(&fr->data, (flags & AIM_WARN_ANON) ? 0x0001 : 0x0000); + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putstr(&fr->data, sn); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* Subtype 0x000a */ +static int missedcall(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; + guint16 channel, nummissed, reason; + aim_userinfo_t userinfo; + + while (aim_bstream_empty(bs)) { + + channel = aimbs_get16(bs); + aim_info_extract(sess, bs, &userinfo); + nummissed = aimbs_get16(bs); + reason = aimbs_get16(bs); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, channel, &userinfo, nummissed, reason); + + aim_info_free(&userinfo); + } + + return ret; +} + +/* + * Subtype 0x000b + * + * Possible codes: + * AIM_TRANSFER_DENY_NOTSUPPORTED -- "client does not support" + * AIM_TRANSFER_DENY_DECLINE -- "client has declined transfer" + * AIM_TRANSFER_DENY_NOTACCEPTING -- "client is not accepting transfers" + * + */ +faim_export int aim_im_denytransfer(aim_session_t *sess, const char *sender, const guchar *cookie, guint16 code) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sender)+6))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x000b, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0004, 0x000b, 0x0000, snacid); + + aimbs_putraw(&fr->data, cookie, 8); + + aimbs_put16(&fr->data, 0x0002); /* channel */ + aimbs_put8(&fr->data, strlen(sender)); + aimbs_putstr(&fr->data, sender); + + aim_tlvlist_add_16(&tl, 0x0003, code); + aim_tlvlist_write(&fr->data, &tl); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x000b - Receive the response from an ICQ status message request. + * + * This contains the ICQ status message. Go figure. + * + */ +static int clientautoresp(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; + guint16 channel, reason; + char *sn; + guchar *cookie; + guint8 snlen; + + cookie = aimbs_getraw(bs, 8); + channel = aimbs_get16(bs); + snlen = aimbs_get8(bs); + sn = aimbs_getstr(bs, snlen); + reason = aimbs_get16(bs); + + if (channel == 0x0002) { /* File transfer declined */ + aimbs_get16(bs); /* Unknown */ + aimbs_get16(bs); /* Unknown */ + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, channel, sn, reason, cookie); + } else if (channel == 0x0004) { /* ICQ message */ + switch (reason) { + case 0x0003: { /* ICQ status message. Maybe other stuff too, you never know with these people. */ + guint8 statusmsgtype, *msg; + guint16 len; + guint32 state; + + len = aimbs_getle16(bs); /* Should be 0x001b */ + aim_bstream_advance(bs, len); /* Unknown */ + + len = aimbs_getle16(bs); /* Should be 0x000e */ + aim_bstream_advance(bs, len); /* Unknown */ + + statusmsgtype = aimbs_getle8(bs); + switch (statusmsgtype) { + case 0xe8: + state = AIM_ICQ_STATE_AWAY; + break; + case 0xe9: + state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY; + break; + case 0xea: + state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_OUT; + break; + case 0xeb: + state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY; + break; + case 0xec: + state = AIM_ICQ_STATE_CHAT; + break; + default: + state = 0; + break; + } + + aimbs_getle8(bs); /* Unknown - 0x03 Maybe this means this is an auto-reply */ + aimbs_getle16(bs); /* Unknown - 0x0000 */ + aimbs_getle16(bs); /* Unknown - 0x0000 */ + + len = aimbs_getle16(bs); + msg = aimbs_getraw(bs, len); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, channel, sn, reason, state, msg); + + free(msg); + } break; + + default: { + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, channel, sn, reason); + } break; + } /* end switch */ + } + + free(cookie); + free(sn); + + return ret; +} + +/* + * Subtype 0x000c - Receive an ack after sending an ICBM. + * + * You have to have send the message with the AIM_IMFLAGS_ACK flag set + * (TLV t(0003)). The ack contains the ICBM header of the message you + * sent. + * + */ +static int msgack(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_rxcallback_t userfunc; + guint16 ch; + guchar *cookie; + char *sn; + int ret = 0; + + cookie = aimbs_getraw(bs, 8); + ch = aimbs_get16(bs); + sn = aimbs_getstr(bs, aimbs_get8(bs)); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, ch, sn); + + free(sn); + free(cookie); + + return ret; +} + +/* + * Subtype 0x0014 - Send a mini typing notification (mtn) packet. + * + * This is supported by winaim5 and newer, MacAIM bleh and newer, iChat bleh and newer, + * and Gaim 0.60 and newer. + * + */ +faim_export int aim_im_sendmtn(aim_session_t *sess, guint16 type1, const char *sn, guint16 type2) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0002))) + return -EINVAL; + + if (!sn) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+11+strlen(sn)+2))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0014, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0004, 0x0014, 0x0000, snacid); + + /* + * 8 days of light + * Er, that is to say, 8 bytes of 0's + */ + aimbs_put16(&fr->data, 0x0000); + aimbs_put16(&fr->data, 0x0000); + aimbs_put16(&fr->data, 0x0000); + aimbs_put16(&fr->data, 0x0000); + + /* + * Type 1 (should be 0x0001 for mtn) + */ + aimbs_put16(&fr->data, type1); + + /* + * Dest sn + */ + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putstr(&fr->data, sn); + + /* + * Type 2 (should be 0x0000, 0x0001, or 0x0002 for mtn) + */ + aimbs_put16(&fr->data, type2); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0014 - Receive a mini typing notification (mtn) packet. + * + * This is supported by winaim5 and newer, MacAIM bleh and newer, iChat bleh and newer, + * and Gaim 0.60 and newer. + * + */ +static int mtn_receive(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; + char *sn; + guint8 snlen; + guint16 type1, type2; + + aim_bstream_advance(bs, 8); /* Unknown - All 0's */ + type1 = aimbs_get16(bs); + snlen = aimbs_get8(bs); + sn = aimbs_getstr(bs, snlen); + type2 = aimbs_get16(bs); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, type1, sn, type2); + + free(sn); + + 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 == 0x0005) + return aim_im_paraminfo(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0006) + return outgoingim(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0007) + return incomingim(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x000a) + return missedcall(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x000b) + return clientautoresp(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x000c) + return msgack(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0014) + return mtn_receive(sess, mod, rx, snac, bs); + + return 0; +} + +faim_internal int msg_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x0004; + mod->version = 0x0001; + mod->toolid = 0x0110; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "messaging", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/family_icq.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/oscar/family_icq.c Sat Feb 11 21:45:18 2006 +0000 @@ -0,0 +1,715 @@ +/* + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * Family 0x0015 - Encapsulated ICQ. + * + */ + +#include "oscar.h" + +faim_export int aim_icq_reqofflinemsgs(aim_session_t *sess) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + int bslen; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) + return -EINVAL; + + bslen = 2 + 4 + 2 + 2; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); + + /* For simplicity, don't bother using a tlvlist */ + aimbs_put16(&fr->data, 0x0001); + aimbs_put16(&fr->data, bslen); + + aimbs_putle16(&fr->data, bslen - 2); + aimbs_putle32(&fr->data, atoi(sess->sn)); + aimbs_putle16(&fr->data, 0x003c); /* I command thee. */ + aimbs_putle16(&fr->data, snacid); /* eh. */ + + aim_tx_enqueue(sess, fr); + + return 0; +} + +faim_export int aim_icq_ackofflinemsgs(aim_session_t *sess) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + int bslen; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) + return -EINVAL; + + bslen = 2 + 4 + 2 + 2; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); + + /* For simplicity, don't bother using a tlvlist */ + aimbs_put16(&fr->data, 0x0001); + aimbs_put16(&fr->data, bslen); + + aimbs_putle16(&fr->data, bslen - 2); + aimbs_putle32(&fr->data, atoi(sess->sn)); + aimbs_putle16(&fr->data, 0x003e); /* I command thee. */ + aimbs_putle16(&fr->data, snacid); /* eh. */ + + aim_tx_enqueue(sess, fr); + + return 0; +} + +faim_export int +aim_icq_setsecurity(aim_session_t *sess, gboolean auth_required, gboolean webaware) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + int bslen; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) + return -EINVAL; + + bslen = 2+4+2+2+2+2+2+1+1+1+1+1+1; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); + + /* For simplicity, don't bother using a tlvlist */ + aimbs_put16(&fr->data, 0x0001); + aimbs_put16(&fr->data, bslen); + + aimbs_putle16(&fr->data, bslen - 2); + aimbs_putle32(&fr->data, atoi(sess->sn)); + aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */ + aimbs_putle16(&fr->data, snacid); /* eh. */ + aimbs_putle16(&fr->data, 0x0c3a); /* shrug. */ + aimbs_putle16(&fr->data, 0x030c); + aimbs_putle16(&fr->data, 0x0001); + aimbs_putle8(&fr->data, webaware); + aimbs_putle8(&fr->data, 0xf8); + aimbs_putle8(&fr->data, 0x02); + aimbs_putle8(&fr->data, 0x01); + aimbs_putle8(&fr->data, 0x00); + aimbs_putle8(&fr->data, !auth_required); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/** + * Change your ICQ password. + * + * @param sess The oscar session + * @param passwd The new password. If this is longer than 8 characters it + * will be truncated. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_icq_changepasswd(aim_session_t *sess, const char *passwd) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + int bslen, passwdlen; + + if (!passwd) + return -EINVAL; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) + return -EINVAL; + + passwdlen = strlen(passwd); + if (passwdlen > MAXICQPASSLEN) + passwdlen = MAXICQPASSLEN; + bslen = 2+4+2+2+2+2+passwdlen+1; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); + + /* For simplicity, don't bother using a tlvlist */ + aimbs_put16(&fr->data, 0x0001); + aimbs_put16(&fr->data, bslen); + + aimbs_putle16(&fr->data, bslen - 2); + aimbs_putle32(&fr->data, atoi(sess->sn)); + aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */ + aimbs_putle16(&fr->data, snacid); /* eh. */ + aimbs_putle16(&fr->data, 0x042e); /* shrug. */ + aimbs_putle16(&fr->data, passwdlen+1); + aimbs_putstr(&fr->data, passwd); + aimbs_putle8(&fr->data, '\0'); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +faim_export int aim_icq_getallinfo(aim_session_t *sess, const char *uin) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + int bslen; + struct aim_icq_info *info; + + if (!uin || uin[0] < '0' || uin[0] > '9') + return -EINVAL; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) + return -EINVAL; + + bslen = 2 + 4 + 2 + 2 + 2 + 4; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); + + /* For simplicity, don't bother using a tlvlist */ + aimbs_put16(&fr->data, 0x0001); + aimbs_put16(&fr->data, bslen); + + aimbs_putle16(&fr->data, bslen - 2); + aimbs_putle32(&fr->data, atoi(sess->sn)); + aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */ + aimbs_putle16(&fr->data, snacid); /* eh. */ + aimbs_putle16(&fr->data, 0x04b2); /* shrug. */ + aimbs_putle32(&fr->data, atoi(uin)); + + aim_tx_enqueue(sess, fr); + + /* Keep track of this request and the ICQ number and request ID */ + info = (struct aim_icq_info *)calloc(1, sizeof(struct aim_icq_info)); + info->reqid = snacid; + info->uin = atoi(uin); + info->next = sess->icq_info; + sess->icq_info = info; + + return 0; +} + +faim_export int aim_icq_getalias(aim_session_t *sess, const char *uin) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + int bslen; + struct aim_icq_info *info; + + if (!uin || uin[0] < '0' || uin[0] > '9') + return -EINVAL; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) + return -EINVAL; + + bslen = 2 + 4 + 2 + 2 + 2 + 4; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); + + /* For simplicity, don't bother using a tlvlist */ + aimbs_put16(&fr->data, 0x0001); + aimbs_put16(&fr->data, bslen); + + aimbs_putle16(&fr->data, bslen - 2); + aimbs_putle32(&fr->data, atoi(sess->sn)); + aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */ + aimbs_putle16(&fr->data, snacid); /* eh. */ + aimbs_putle16(&fr->data, 0x04ba); /* shrug. */ + aimbs_putle32(&fr->data, atoi(uin)); + + aim_tx_enqueue(sess, fr); + + /* Keep track of this request and the ICQ number and request ID */ + info = (struct aim_icq_info *)calloc(1, sizeof(struct aim_icq_info)); + info->reqid = snacid; + info->uin = atoi(uin); + info->next = sess->icq_info; + sess->icq_info = info; + + return 0; +} + +faim_export int aim_icq_getsimpleinfo(aim_session_t *sess, const char *uin) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + int bslen; + + if (!uin || uin[0] < '0' || uin[0] > '9') + return -EINVAL; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) + return -EINVAL; + + bslen = 2 + 4 + 2 + 2 + 2 + 4; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); + + /* For simplicity, don't bother using a tlvlist */ + aimbs_put16(&fr->data, 0x0001); + aimbs_put16(&fr->data, bslen); + + aimbs_putle16(&fr->data, bslen - 2); + aimbs_putle32(&fr->data, atoi(sess->sn)); + aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */ + aimbs_putle16(&fr->data, snacid); /* eh. */ + aimbs_putle16(&fr->data, 0x051f); /* shrug. */ + aimbs_putle32(&fr->data, atoi(uin)); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +#if 0 +faim_export int aim_icq_sendxmlreq(aim_session_t *sess, const char *xml) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + int bslen; + + if (!xml || !strlen(xml)) + return -EINVAL; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) + return -EINVAL; + + bslen = 2 + 10 + 2 + strlen(xml) + 1; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); + + /* For simplicity, don't bother using a tlvlist */ + aimbs_put16(&fr->data, 0x0001); + aimbs_put16(&fr->data, bslen); + + aimbs_putle16(&fr->data, bslen - 2); + aimbs_putle32(&fr->data, atoi(sess->sn)); + aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */ + aimbs_putle16(&fr->data, snacid); /* eh. */ + aimbs_putle16(&fr->data, 0x0998); /* shrug. */ + aimbs_putle16(&fr->data, strlen(xml) + 1); + aimbs_putraw(&fr->data, (guint8 *)xml, strlen(xml) + 1); + + aim_tx_enqueue(sess, fr); + + return 0; +} +#endif + +#if 0 +/* + * Send an SMS message. This is the non-US way. The US-way is to IM + * their cell phone number (+19195551234). + * + * We basically construct and send an XML message. The format is: + * + * full_phone_without_leading_+ + * message + * 1252 + * self_uin + * self_name + * Yes|No + * + * + * + * Yeah hi Peter, whaaaat's happening. If there's any way to use + * a codepage other than 1252 that would be great. Thaaaanks. + */ +faim_export int aim_icq_sendsms(aim_session_t *sess, const char *name, const char *msg, const char *alias) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + int bslen, xmllen; + char *xml; + const char *timestr; + time_t t; + struct tm *tm; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) + return -EINVAL; + + if (!name || !msg || !alias) + return -EINVAL; + + time(&t); + tm = gmtime(&t); + timestr = gaim_utf8_strftime("%a, %d %b %Y %T %Z", tm); + + /* The length of xml included the null terminating character */ + xmllen = 225 + strlen(name) + strlen(msg) + strlen(sess->sn) + strlen(alias) + strlen(timestr) + 1; + + if (!(xml = (char *)malloc(xmllen*sizeof(char)))) + return -ENOMEM; + snprintf(xml, xmllen, "\n" + "\t%s\n" + "\t%s\n" + "\t1252\n" + "\t%s\n" + "\t%s\n" + "\tYes\n" + "\t\n" + "\n", + name, msg, sess->sn, alias, timestr); + + bslen = 37 + xmllen; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) { + free(xml); + return -ENOMEM; + } + + snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); + + /* For simplicity, don't bother using a tlvlist */ + aimbs_put16(&fr->data, 0x0001); + aimbs_put16(&fr->data, bslen); + + aimbs_putle16(&fr->data, bslen - 2); + aimbs_putle32(&fr->data, atoi(sess->sn)); + aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */ + aimbs_putle16(&fr->data, snacid); /* eh. */ + + /* From libicq200-0.3.2/src/SNAC-SRV.cpp */ + aimbs_putle16(&fr->data, 0x8214); + aimbs_put16(&fr->data, 0x0001); + aimbs_put16(&fr->data, 0x0016); + aimbs_put32(&fr->data, 0x00000000); + aimbs_put32(&fr->data, 0x00000000); + aimbs_put32(&fr->data, 0x00000000); + aimbs_put32(&fr->data, 0x00000000); + + aimbs_put16(&fr->data, 0x0000); + aimbs_put16(&fr->data, xmllen); + aimbs_putstr(&fr->data, xml); + + aim_tx_enqueue(sess, fr); + + free(xml); + + return 0; +} +#endif + +static void aim_icq_freeinfo(struct aim_icq_info *info) { + int i; + + if (!info) + return; + free(info->nick); + free(info->first); + free(info->last); + free(info->email); + free(info->homecity); + free(info->homestate); + free(info->homephone); + free(info->homefax); + free(info->homeaddr); + free(info->mobile); + free(info->homezip); + free(info->personalwebpage); + if (info->email2) + for (i = 0; i < info->numaddresses; i++) + free(info->email2[i]); + free(info->email2); + free(info->workcity); + free(info->workstate); + free(info->workphone); + free(info->workfax); + free(info->workaddr); + free(info->workzip); + free(info->workcompany); + free(info->workdivision); + free(info->workposition); + free(info->workwebpage); + free(info->info); + free(info); +} + +/** + * Subtype 0x0003 - Response to 0x0015/0x002, contains an ICQesque 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_tlvlist_t *tl; + aim_tlv_t *datatlv; + aim_bstream_t qbs; + guint32 ouruin; + guint16 cmdlen, cmd, reqid; + + if (!(tl = aim_tlvlist_read(bs)) || !(datatlv = aim_tlv_gettlv(tl, 0x0001, 1))) { + aim_tlvlist_free(&tl); + gaim_debug_misc("oscar", "corrupt ICQ response\n"); + return 0; + } + + aim_bstream_init(&qbs, datatlv->value, datatlv->length); + + cmdlen = aimbs_getle16(&qbs); + ouruin = aimbs_getle32(&qbs); + cmd = aimbs_getle16(&qbs); + reqid = aimbs_getle16(&qbs); + + gaim_debug_misc("oscar", "icq response: %d bytes, %ld, 0x%04x, 0x%04x\n", cmdlen, ouruin, cmd, reqid); + + if (cmd == 0x0041) { /* offline message */ + struct aim_icq_offlinemsg msg; + aim_rxcallback_t userfunc; + + memset(&msg, 0, sizeof(msg)); + + msg.sender = aimbs_getle32(&qbs); + msg.year = aimbs_getle16(&qbs); + msg.month = aimbs_getle8(&qbs); + msg.day = aimbs_getle8(&qbs); + msg.hour = aimbs_getle8(&qbs); + msg.minute = aimbs_getle8(&qbs); + msg.type = aimbs_getle8(&qbs); + msg.flags = aimbs_getle8(&qbs); + msg.msglen = aimbs_getle16(&qbs); + msg.msg = aimbs_getstr(&qbs, msg.msglen); + + if ((userfunc = aim_callhandler(sess, rx->conn, OSCAR_FAMILY_ICQ, OSCAR_SUBTYPE_ICQ_OFFLINEMSG))) + ret = userfunc(sess, rx, &msg); + + free(msg.msg); + + } else if (cmd == 0x0042) { + aim_rxcallback_t userfunc; + + if ((userfunc = aim_callhandler(sess, rx->conn, OSCAR_FAMILY_ICQ, OSCAR_SUBTYPE_ICQ_OFFLINEMSGCOMPLETE))) + ret = userfunc(sess, rx); + + } else if (cmd == 0x07da) { /* information */ + guint16 subtype; + struct aim_icq_info *info; + aim_rxcallback_t userfunc; + + subtype = aimbs_getle16(&qbs); + aim_bstream_advance(&qbs, 1); /* 0x0a */ + + /* find other data from the same request */ + for (info = sess->icq_info; info && (info->reqid != reqid); info = info->next); + if (!info) { + info = (struct aim_icq_info *)calloc(1, sizeof(struct aim_icq_info)); + info->reqid = reqid; + info->next = sess->icq_info; + sess->icq_info = info; + } + + switch (subtype) { + case 0x00a0: { /* hide ip status */ + /* nothing */ + } break; + + case 0x00aa: { /* password change status */ + /* nothing */ + } break; + + case 0x00c8: { /* general and "home" information */ + info->nick = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->first = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->last = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->email = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->homecity = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->homestate = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->homephone = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->homefax = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->homeaddr = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->mobile = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->homezip = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->homecountry = aimbs_getle16(&qbs); + /* 0x0a 00 02 00 */ + /* 1 byte timezone? */ + /* 1 byte hide email flag? */ + } break; + + case 0x00dc: { /* personal information */ + info->age = aimbs_getle8(&qbs); + info->unknown = aimbs_getle8(&qbs); + info->gender = aimbs_getle8(&qbs); /* Not specified=0x00, Female=0x01, Male=0x02 */ + info->personalwebpage = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->birthyear = aimbs_getle16(&qbs); + info->birthmonth = aimbs_getle8(&qbs); + info->birthday = aimbs_getle8(&qbs); + info->language1 = aimbs_getle8(&qbs); + info->language2 = aimbs_getle8(&qbs); + info->language3 = aimbs_getle8(&qbs); + /* 0x00 00 01 00 00 01 00 00 00 00 00 */ + } break; + + case 0x00d2: { /* work information */ + info->workcity = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->workstate = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->workphone = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->workfax = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->workaddr = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->workzip = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->workcountry = aimbs_getle16(&qbs); + info->workcompany = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->workdivision = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->workposition = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + aim_bstream_advance(&qbs, 2); /* 0x01 00 */ + info->workwebpage = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + } break; + + case 0x00e6: { /* additional personal information */ + info->info = aimbs_getstr(&qbs, aimbs_getle16(&qbs)-1); + } break; + + case 0x00eb: { /* email address(es) */ + int i; + info->numaddresses = aimbs_getle16(&qbs); + info->email2 = (char **)calloc(info->numaddresses, sizeof(char *)); + for (i = 0; i < info->numaddresses; i++) { + info->email2[i] = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + if (i+1 != info->numaddresses) + aim_bstream_advance(&qbs, 1); /* 0x00 */ + } + } break; + + case 0x00f0: { /* personal interests */ + } break; + + case 0x00fa: { /* past background and current organizations */ + } break; + + case 0x0104: { /* alias info */ + info->nick = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->first = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->last = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + aim_bstream_advance(&qbs, aimbs_getle16(&qbs)); /* email address? */ + /* Then 0x00 02 00 */ + } break; + + case 0x010e: { /* unknown */ + /* 0x00 00 */ + } break; + + case 0x019a: { /* simple info */ + aim_bstream_advance(&qbs, 2); + info->uin = aimbs_getle32(&qbs); + info->nick = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->first = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->last = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->email = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + /* Then 0x00 02 00 00 00 00 00 */ + } break; + } /* End switch statement */ + + if (!(snac->flags & 0x0001)) { + if (subtype != 0x0104) + if ((userfunc = aim_callhandler(sess, rx->conn, OSCAR_FAMILY_ICQ, OSCAR_SUBTYPE_ICQ_INFO))) + ret = userfunc(sess, rx, info); + + if (info->uin && info->nick) + if ((userfunc = aim_callhandler(sess, rx->conn, OSCAR_FAMILY_ICQ, OSCAR_SUBTYPE_ICQ_ALIAS))) + ret = userfunc(sess, rx, info); + + if (sess->icq_info == info) { + sess->icq_info = info->next; + } else { + struct aim_icq_info *cur; + for (cur=sess->icq_info; (cur->next && (cur->next!=info)); cur=cur->next); + if (cur->next) + cur->next = cur->next->next; + } + aim_icq_freeinfo(info); + } + } + + aim_tlvlist_free(&tl); + + 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; +} + +static void icq_shutdown(aim_session_t *sess, aim_module_t *mod) +{ + struct aim_icq_info *del; + + while (sess->icq_info) { + del = sess->icq_info; + sess->icq_info = sess->icq_info->next; + aim_icq_freeinfo(del); + } + + return; +} + +faim_internal int icq_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x0015; + mod->version = 0x0001; + mod->toolid = 0x0110; + mod->toolversion = 0x047c; + mod->flags = 0; + strncpy(mod->name, "icq", sizeof(mod->name)); + mod->snachandler = snachandler; + mod->shutdown = icq_shutdown; + + return 0; +} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/family_invite.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/oscar/family_invite.c Sat Feb 11 21:45:18 2006 +0000 @@ -0,0 +1,53 @@ +/* + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * Family 0x0006 - This isn't really ever used by anyone anymore. + * + * Once upon a time, there used to be a menu item in AIM clients that + * said something like "Invite a friend to use AIM..." and then it would + * ask for an email address and it would sent a mail to them saying + * how perfectly wonderful the AIM service is and why you should use it + * and click here if you hate the person who sent this to you and want to + * complain and yell at them in a small box with pretty fonts. + * + * I could've sworn libfaim had this implemented once, a long long time ago, + * but I can't find it. + * + * I'm mainly adding this so that I can keep advertising that we support + * group 6, even though we don't. + * + */ + +#include "oscar.h" + +faim_internal int invite_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x0006; + mod->version = 0x0001; + mod->toolid = 0x0110; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "invite", sizeof(mod->name)); + mod->snachandler = NULL; + + return 0; +} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/family_locate.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/oscar/family_locate.c Sat Feb 11 21:45:18 2006 +0000 @@ -0,0 +1,1409 @@ +/* + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * Family 0x0002 - Locate. + * + * The functions here are responsible for requesting and parsing information- + * gathering SNACs. Or something like that. This family contains the SNACs + * for getting and setting info, away messages, directory profile thingy, etc. + */ + +#include "oscar.h" +#ifdef _WIN32 +#include "win32dep.h" +#endif + +/* + * Capability blocks. + * + * These are CLSIDs. They should actually be of the form: + * + * {0x0946134b, 0x4c7f, 0x11d1, + * {0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}}, + * + * But, eh. + */ +static const struct { + guint32 flag; + guint8 data[16]; +} aim_caps[] = { + + /* + * These are in ascending numerical order. + */ + + /* + * Perhaps better called AIM_CAPS_SHORTCAPS + */ + {AIM_CAPS_ICHAT, + {0x09, 0x46, 0x00, 0x00, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_SECUREIM, + {0x09, 0x46, 0x00, 0x01, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_VIDEO, + {0x09, 0x46, 0x01, 0x00, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + /* "Live Video" support in Windows AIM 5.5.3501 and newer */ + {AIM_CAPS_LIVEVIDEO, + {0x09, 0x46, 0x01, 0x01, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + /* "Camera" support in Windows AIM 5.5.3501 and newer */ + {AIM_CAPS_CAMERA, + {0x09, 0x46, 0x01, 0x02, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + /* In Windows AIM 5.5.3501 and newer */ + {AIM_CAPS_GENERICUNKNOWN, + {0x09, 0x46, 0x01, 0x03, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + /* In iChatAV (version numbers...?) */ + {AIM_CAPS_ICHATAV, + {0x09, 0x46, 0x01, 0x05, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x45, 0x53, 0x54, 0x00}}, + + /* + * Not really sure about this one. In an email from + * 26 Sep 2003, Matthew Sachs suggested that, "this + * is probably the capability for the SMS features." + */ + {AIM_CAPS_SMS, + {0x09, 0x46, 0x01, 0xff, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_GENERICUNKNOWN, + {0x09, 0x46, 0xf0, 0x03, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_GENERICUNKNOWN, + {0x09, 0x46, 0xf0, 0x04, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_GENERICUNKNOWN, + {0x09, 0x46, 0xf0, 0x05, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_HIPTOP, + {0x09, 0x46, 0x13, 0x23, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_TALK, + {0x09, 0x46, 0x13, 0x41, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_SENDFILE, + {0x09, 0x46, 0x13, 0x43, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_ICQ_DIRECT, + {0x09, 0x46, 0x13, 0x44, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_DIRECTIM, + {0x09, 0x46, 0x13, 0x45, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_BUDDYICON, + {0x09, 0x46, 0x13, 0x46, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_ADDINS, + {0x09, 0x46, 0x13, 0x47, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_GETFILE, + {0x09, 0x46, 0x13, 0x48, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_ICQSERVERRELAY, + {0x09, 0x46, 0x13, 0x49, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + /* + * Indeed, there are two of these. The former appears to be correct, + * but in some versions of winaim, the second one is set. Either they + * forgot to fix endianness, or they made a typo. It really doesn't + * matter which. + */ + {AIM_CAPS_GAMES, + {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + {AIM_CAPS_GAMES2, + {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1, + 0x22, 0x82, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_SENDBUDDYLIST, + {0x09, 0x46, 0x13, 0x4b, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + /* + * Setting this lets AIM users receive messages from ICQ users, and ICQ + * users receive messages from AIM users. It also lets ICQ users show + * up in buddy lists for AIM users, and AIM users show up in buddy lists + * for ICQ users. And ICQ privacy/invisibility acts like AIM privacy, + * in that if you add a user to your deny list, you will not be able to + * see them as online (previous you could still see them, but they + * couldn't see you. + */ + {AIM_CAPS_INTEROPERATE, + {0x09, 0x46, 0x13, 0x4d, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_ICQUTF8, + {0x09, 0x46, 0x13, 0x4e, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_ICQUTF8OLD, + {0x2e, 0x7a, 0x64, 0x75, 0xfa, 0xdf, 0x4d, 0xc8, + 0x88, 0x6f, 0xea, 0x35, 0x95, 0xfd, 0xb6, 0xdf}}, + + /* + * Chat is oddball. + */ + {AIM_CAPS_CHAT, + {0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + /* + {AIM_CAPS_ICQ2GO, + {0x56, 0x3f, 0xc8, 0x09, 0x0b, 0x6f, 0x41, 0xbd, + 0x9f, 0x79, 0x42, 0x26, 0x09, 0xdf, 0xa2, 0xf3}}, + */ + + {AIM_CAPS_ICQRTF, + {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34, + 0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x92}}, + + /* This is added by the servers and it only shows up for ourselves... */ + {AIM_CAPS_GENERICUNKNOWN, + {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34, + 0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x09}}, + + {AIM_CAPS_APINFO, + {0xaa, 0x4a, 0x32, 0xb5, 0xf8, 0x84, 0x48, 0xc6, + 0xa3, 0xd7, 0x8c, 0x50, 0x97, 0x19, 0xfd, 0x5b}}, + + {AIM_CAPS_TRILLIANCRYPT, + {0xf2, 0xe7, 0xc7, 0xf4, 0xfe, 0xad, 0x4d, 0xfb, + 0xb2, 0x35, 0x36, 0x79, 0x8b, 0xdf, 0x00, 0x00}}, + + {AIM_CAPS_EMPTY, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + + {AIM_CAPS_LAST, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, +}; + +/* + * Add the userinfo to our linked list. If we already have userinfo + * for this buddy, then just overwrite parts of the old data. + * + * @param userinfo Contains the new information for the buddy. + */ +static void aim_locate_adduserinfo(aim_session_t *sess, aim_userinfo_t *userinfo) { + aim_userinfo_t *cur; + aim_conn_t *conn; + aim_rxcallback_t userfunc; + + cur = aim_locate_finduserinfo(sess, userinfo->sn); + + if (cur == NULL) { + cur = (aim_userinfo_t *)calloc(1, sizeof(aim_userinfo_t)); + cur->sn = strdup(userinfo->sn); + cur->next = sess->locate.userinfo; + sess->locate.userinfo = cur; + } + + cur->warnlevel = userinfo->warnlevel; + cur->idletime = userinfo->idletime; + if (userinfo->flags != 0) + cur->flags = userinfo->flags; + if (userinfo->createtime != 0) + cur->createtime = userinfo->createtime; + if (userinfo->membersince != 0) + cur->membersince = userinfo->membersince; + if (userinfo->onlinesince != 0) + cur->onlinesince = userinfo->onlinesince; + if (userinfo->sessionlen != 0) + cur->sessionlen = userinfo->sessionlen; + if (userinfo->capabilities != 0) + cur->capabilities = userinfo->capabilities; + cur->present |= userinfo->present; + + if (userinfo->iconcsumlen > 0) { + free(cur->iconcsum); + cur->iconcsum = (guint8 *)malloc(userinfo->iconcsumlen); + memcpy(cur->iconcsum, userinfo->iconcsum, userinfo->iconcsumlen); + cur->iconcsumlen = userinfo->iconcsumlen; + } + + if (userinfo->info != NULL) { + free(cur->info); + free(cur->info_encoding); + if (userinfo->info_len > 0) { + cur->info = (char *)malloc(userinfo->info_len); + memcpy(cur->info, userinfo->info, userinfo->info_len); + } else + cur->info = NULL; + cur->info_encoding = strdup(userinfo->info_encoding); + cur->info_len = userinfo->info_len; + } + + if (userinfo->status != NULL) { + free(cur->status); + free(cur->status_encoding); + if (userinfo->status_len > 0) { + cur->status = (char *)malloc(userinfo->status_len); + memcpy(cur->status, userinfo->status, userinfo->status_len); + } else + cur->status = NULL; + if (userinfo->status_encoding != NULL) + cur->status_encoding = strdup(userinfo->status_encoding); + else + cur->status_encoding = NULL; + cur->status_len = userinfo->status_len; + } + + if (userinfo->away != NULL) { + free(cur->away); + free(cur->away_encoding); + if (userinfo->away_len > 0) { + cur->away = (char *)malloc(userinfo->away_len); + memcpy(cur->away, userinfo->away, userinfo->away_len); + } else + cur->away = NULL; + cur->away_encoding = strdup(userinfo->away_encoding); + cur->away_len = userinfo->away_len; + } + + /* + * This callback can be used by a client if they want to know whenever + * info for a buddy is updated. For example, if a client shows away + * messages in its buddy list, then it would need to know if a user's + * away message changes. + */ + conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_LOCATE); + if ((userfunc = aim_callhandler(sess, conn, OSCAR_FAMILY_LOCATE, OSCAR_SUBTYPE_LOCATE_GOTINFOBLOCK))) + userfunc(sess, NULL, cur); +} + +faim_export void aim_locate_dorequest(aim_session_t *sess) { + struct userinfo_node *cur = sess->locate.torequest; + + if (cur == NULL) + return; + + if (sess->locate.waiting_for_response == TRUE) + return; + + sess->locate.waiting_for_response = TRUE; + aim_locate_getinfoshort(sess, cur->sn, 0x00000003); + + /* Move this node to the "requested" queue */ + sess->locate.torequest = cur->next; + cur->next = sess->locate.requested; + sess->locate.requested = cur; +} + +/** + * Remove this screen name from our queue. If this info was requested + * by our info request queue, then pop the next element off of the queue. + * + * @param sess The aim session. + * @param sn Screen name of the info we just received. + * @return True if the request was explicit (client requested the info), + * false if the request was implicit (libfaim request the info). + */ +static int aim_locate_gotuserinfo(aim_session_t *sess, const char *sn) { + struct userinfo_node *cur, *del; + int was_explicit = TRUE; + + while ((sess->locate.requested != NULL) && (aim_sncmp(sn, sess->locate.requested->sn) == 0)) { + del = sess->locate.requested; + sess->locate.requested = del->next; + was_explicit = FALSE; + free(del->sn); + free(del); + } + + cur = sess->locate.requested; + while ((cur != NULL) && (cur->next != NULL)) { + if (aim_sncmp(sn, cur->next->sn) == 0) { + del = cur->next; + cur->next = del->next; + was_explicit = FALSE; + free(del->sn); + free(del); + } else + cur = cur->next; + } + + if (!was_explicit) { + aim_conn_t *conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_LOCATE); + aim_rxcallback_t userfunc; + + sess->locate.waiting_for_response = FALSE; + + if ((userfunc = aim_callhandler(sess, conn, OSCAR_FAMILY_LOCATE, OSCAR_SUBTYPE_LOCATE_REQUESTINFOTIMEOUT))) + userfunc(sess, NULL); + else + aim_locate_dorequest(sess); + } + + return was_explicit; +} + +faim_internal void aim_locate_requestuserinfo(aim_session_t *sess, const char *sn) { + struct userinfo_node *cur; + + /* Make sure we aren't already requesting info for this buddy */ + cur = sess->locate.torequest; + while (cur != NULL) { + if (aim_sncmp(sn, cur->sn) == 0) + return; + cur = cur->next; + } + + /* Add a new node to our request queue */ + cur = (struct userinfo_node *)malloc(sizeof(struct userinfo_node)); + cur->sn = strdup(sn); + cur->next = sess->locate.torequest; + sess->locate.torequest = cur; + + /* Actually request some info up in this piece */ + aim_locate_dorequest(sess); +} + +faim_export aim_userinfo_t *aim_locate_finduserinfo(aim_session_t *sess, const char *sn) { + aim_userinfo_t *cur = NULL; + + if (sn == NULL) + return NULL; + + cur = sess->locate.userinfo; + + while (cur != NULL) { + if (aim_sncmp(cur->sn, sn) == 0) + return cur; + cur = cur->next; + } + + return NULL; +} + +faim_internal guint32 aim_locate_getcaps(aim_session_t *sess, aim_bstream_t *bs, int len) +{ + guint32 flags = 0; + int offset; + + for (offset = 0; aim_bstream_empty(bs) && (offset < len); offset += 0x10) { + guint8 *cap; + int i, identified; + + cap = aimbs_getraw(bs, 0x10); + + for (i = 0, identified = 0; !(aim_caps[i].flag & AIM_CAPS_LAST); i++) { + if (memcmp(&aim_caps[i].data, cap, 0x10) == 0) { + flags |= aim_caps[i].flag; + identified++; + break; /* should only match once... */ + } + } + + if (!identified) + gaim_debug_misc("oscar", "unknown capability: {%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n", + cap[0], cap[1], cap[2], cap[3], + cap[4], cap[5], + cap[6], cap[7], + cap[8], cap[9], + cap[10], cap[11], cap[12], cap[13], + cap[14], cap[15]); + + free(cap); + } + + return flags; +} + +faim_internal guint32 aim_locate_getcaps_short(aim_session_t *sess, aim_bstream_t *bs, int len) +{ + guint32 flags = 0; + int offset; + + for (offset = 0; aim_bstream_empty(bs) && (offset < len); offset += 0x02) { + guint8 *cap; + int i, identified; + + cap = aimbs_getraw(bs, 0x02); + + for (i = 0, identified = 0; !(aim_caps[i].flag & AIM_CAPS_LAST); i++) { + if (memcmp(&aim_caps[i].data[2], cap, 0x02) == 0) { + flags |= aim_caps[i].flag; + identified++; + break; /* should only match once... */ + } + } + + if (!identified) + gaim_debug_misc("oscar", "unknown short capability: {%02x%02x}\n", cap[0], cap[1]); + + free(cap); + } + + return flags; +} + +faim_internal int aimbs_putcaps(aim_bstream_t *bs, guint32 caps) +{ + int i; + + if (!bs) + return -EINVAL; + + for (i = 0; aim_bstream_empty(bs); i++) { + + if (aim_caps[i].flag == AIM_CAPS_LAST) + break; + + if (caps & aim_caps[i].flag) + aimbs_putraw(bs, aim_caps[i].data, 0x10); + + } + + return 0; +} + +static void dumptlv(aim_session_t *sess, guint16 type, aim_bstream_t *bs, guint8 len) +{ + int i; + + if (!sess || !bs || !len) + return; + + gaim_debug_misc("oscar", "userinfo: type =0x%04x\n", type); + gaim_debug_misc("oscar", "userinfo: length=0x%04x\n", len); + gaim_debug_misc("oscar", "userinfo: value:\n"); + + for (i = 0; i < len; i++) { + if ((i % 8) == 0) + gaim_debug_misc("oscar", "\nuserinfo: "); + gaim_debug_misc("oscar", "0x%2x ", aimbs_get8(bs)); + } + + gaim_debug_misc("oscar", "\n"); + + return; +} + +faim_internal void aim_info_free(aim_userinfo_t *info) +{ + free(info->sn); + free(info->iconcsum); + free(info->info); + free(info->info_encoding); + free(info->status); + free(info->status_encoding); + free(info->away); + free(info->away_encoding); +} + +/* + * AIM is fairly regular about providing user info. This is a generic + * routine to extract it in its standard form. + */ +faim_internal int aim_info_extract(aim_session_t *sess, aim_bstream_t *bs, aim_userinfo_t *outinfo) +{ + int curtlv, tlvcnt; + guint8 snlen; + + if (!bs || !outinfo) + return -EINVAL; + + /* Clear out old data first */ + memset(outinfo, 0x00, sizeof(aim_userinfo_t)); + + /* + * Screen name. Stored as an unterminated string prepended with a + * byte containing its length. + */ + snlen = aimbs_get8(bs); + outinfo->sn = aimbs_getstr(bs, snlen); + + /* + * Warning Level. Stored as an unsigned short. + */ + outinfo->warnlevel = aimbs_get16(bs); + + /* + * TLV Count. Unsigned short representing the number of + * Type-Length-Value triples that follow. + */ + tlvcnt = aimbs_get16(bs); + + /* + * Parse out the Type-Length-Value triples as they're found. + */ + for (curtlv = 0; curtlv < tlvcnt; curtlv++) { + int endpos; + guint16 type, length; + + type = aimbs_get16(bs); + length = aimbs_get16(bs); + + endpos = aim_bstream_curpos(bs) + length; + + if (type == 0x0001) { + /* + * Type = 0x0001: User flags + * + * Specified as any of the following ORed together: + * 0x0001 Trial (user less than 60days) + * 0x0002 Unknown bit 2 + * 0x0004 AOL Main Service user + * 0x0008 Unknown bit 4 + * 0x0010 Free (AIM) user + * 0x0020 Away + * 0x0400 ActiveBuddy + * + */ + outinfo->flags = aimbs_get16(bs); + outinfo->present |= AIM_USERINFO_PRESENT_FLAGS; + + } else if (type == 0x0002) { + /* + * Type = 0x0002: Account creation time. + * + * The time/date that the user originally registered for + * the service, stored in time_t format. + * + * I'm not sure how this differs from type 5 ("member + * since"). + * + * Note: This is the field formerly known as "member + * since". All these years and I finally found out + * that I got the name wrong. + */ + outinfo->createtime = aimbs_get32(bs); + outinfo->present |= AIM_USERINFO_PRESENT_CREATETIME; + + } else if (type == 0x0003) { + /* + * Type = 0x0003: On-Since date. + * + * The time/date that the user started their current + * session, stored in time_t format. + */ + outinfo->onlinesince = aimbs_get32(bs); + outinfo->present |= AIM_USERINFO_PRESENT_ONLINESINCE; + + } else if (type == 0x0004) { + /* + * Type = 0x0004: Idle time. + * + * Number of minutes since the user actively used the + * service. + * + * Note that the client tells the server when to start + * counting idle times, so this may or may not be + * related to reality. + */ + outinfo->idletime = aimbs_get16(bs); + outinfo->present |= AIM_USERINFO_PRESENT_IDLE; + + } else if (type == 0x0005) { + /* + * Type = 0x0005: Member since date. + * + * The time/date that the user originally registered for + * the service, stored in time_t format. + * + * This is sometimes sent instead of type 2 ("account + * creation time"), particularly in the self-info. + * And particularly for ICQ? + */ + outinfo->membersince = aimbs_get32(bs); + outinfo->present |= AIM_USERINFO_PRESENT_MEMBERSINCE; + + } else if (type == 0x0006) { + /* + * Type = 0x0006: ICQ Online Status + * + * ICQ's Away/DND/etc "enriched" status. Some decoding + * of values done by Scott + */ + aimbs_get16(bs); + outinfo->icqinfo.status = aimbs_get16(bs); + outinfo->present |= AIM_USERINFO_PRESENT_ICQEXTSTATUS; + + } else if (type == 0x0008) { + /* + * Type = 0x0008 + * + * Client type, or some such. + */ + + } else if (type == 0x000a) { + /* + * Type = 0x000a + * + * ICQ User IP Address. + * Ahh, the joy of ICQ security. + */ + outinfo->icqinfo.ipaddr = aimbs_get32(bs); + outinfo->present |= AIM_USERINFO_PRESENT_ICQIPADDR; + + } else if (type == 0x000c) { + /* + * Type = 0x000c + * + * random crap containing the IP address, + * apparently a port number, and some Other Stuff. + * + * Format is: + * 4 bytes - Our IP address, 0xc0 a8 01 2b for 192.168.1.43 + * + * + */ + aimbs_getrawbuf(bs, outinfo->icqinfo.crap, 0x25); + outinfo->present |= AIM_USERINFO_PRESENT_ICQDATA; + + } else if (type == 0x000d) { + /* + * Type = 0x000d + * + * OSCAR Capability information. + * + */ + outinfo->capabilities |= aim_locate_getcaps(sess, bs, length); + outinfo->present |= AIM_USERINFO_PRESENT_CAPABILITIES; + + } else if (type == 0x000e) { + /* + * Type = 0x000e + * + * AOL capability information. + * + */ + + } else if ((type == 0x000f) || (type == 0x0010)) { + /* + * Type = 0x000f: Session Length. (AIM) + * Type = 0x0010: Session Length. (AOL) + * + * The duration, in seconds, of the user's current + * session. + * + * Which TLV type this comes in depends on the + * service the user is using (AIM or AOL). + * + */ + outinfo->sessionlen = aimbs_get32(bs); + outinfo->present |= AIM_USERINFO_PRESENT_SESSIONLEN; + + } else if (type == 0x0019) { + /* + * Type = 0x0019 + * + * OSCAR short capability information. A shortened + * form of the normal capabilities. + */ + outinfo->capabilities |= aim_locate_getcaps_short(sess, bs, length); + outinfo->present |= AIM_USERINFO_PRESENT_CAPABILITIES; + + } else if (type == 0x001b) { + /* + * Type = 0x001a + * + * AOL short capability information. A shortened + * form of the normal capabilities. + */ + + } else if (type == 0x001b) { + /* + * Type = 0x0019 + * + * Encryption certification MD5 checksum. + */ + + } else if (type == 0x001d) { + /* + * Type = 0x001d + * + * Buddy icon information and status/available messages. + * + * This almost seems like the AIM protocol guys gave + * the iChat guys a Type, and the iChat guys tried to + * cram as much cool shit into it as possible. Then + * the Windows AIM guys were like, "hey, that's + * pretty neat, let's copy those prawns." + * + * In that spirit, this can contain a custom message, + * kind of like an away message, but you're not away + * (it's called an "available" message). Or it can + * contain information about the buddy icon the user + * has stored on the server. + */ + int type2, number, length2; + + while (aim_bstream_curpos(bs) < endpos) { + type2 = aimbs_get16(bs); + number = aimbs_get8(bs); + length2 = aimbs_get8(bs); + + switch (type2) { + case 0x0000: { /* This is an official buddy icon? */ + /* This is always 5 bytes of "0x02 01 d2 04 72"? */ + aim_bstream_advance(bs, length2); + } break; + + case 0x0001: { /* A buddy icon checksum */ + if ((length2 > 0) && ((number == 0x00) || (number == 0x01))) { + free(outinfo->iconcsum); + outinfo->iconcsumtype = number; + outinfo->iconcsum = aimbs_getraw(bs, length2); + outinfo->iconcsumlen = length2; + } else + aim_bstream_advance(bs, length2); + } break; + + case 0x0002: { /* A status/available message */ + free(outinfo->status); + free(outinfo->status_encoding); + if (length2 >= 4) { + outinfo->status_len = aimbs_get16(bs); + outinfo->status = aimbs_getstr(bs, outinfo->status_len); + if (aimbs_get16(bs) == 0x0001) { /* We have an encoding */ + aimbs_get16(bs); + outinfo->status_encoding = aimbs_getstr(bs, aimbs_get16(bs)); + } else { + /* No explicit encoding, client should use UTF-8 */ + outinfo->status_encoding = NULL; + } + } else { + aim_bstream_advance(bs, length2); + outinfo->status_len = 0; + outinfo->status = g_strdup(""); + outinfo->status_encoding = NULL; + } + } break; + + default: { + aim_bstream_advance(bs, length2); + } break; + } + } + + } else if (type == 0x001e) { + /* + * Type 30: Unknown. + * + * Always four bytes, but it doesn't look like an int. + */ + + } else if (type == 0x001f) { + /* + * Type 31: Unknown. + * + * Seen on a buddy using DeadAIM. Data was 4 bytes: + * 0x00 00 00 10 + */ + + } else { + + /* + * Reaching here indicates that either AOL has + * added yet another TLV for us to deal with, + * or the parsing has gone Terribly Wrong. + * + * Either way, inform the owner and attempt + * recovery. + * + */ + gaim_debug_misc("oscar", "userinfo: **warning: unexpected TLV:\n"); + gaim_debug_misc("oscar", "userinfo: sn =%s\n", outinfo->sn); + dumptlv(sess, type, bs, length); + } + + /* Save ourselves. */ + aim_bstream_setpos(bs, endpos); + } + + aim_locate_adduserinfo(sess, outinfo); + + return 0; +} + +/* + * Inverse of aim_info_extract() + */ +faim_internal int aim_putuserinfo(aim_bstream_t *bs, aim_userinfo_t *info) +{ + aim_tlvlist_t *tlvlist = NULL; + + if (!bs || !info) + return -EINVAL; + + aimbs_put8(bs, strlen(info->sn)); + aimbs_putstr(bs, info->sn); + + aimbs_put16(bs, info->warnlevel); + + if (info->present & AIM_USERINFO_PRESENT_FLAGS) + aim_tlvlist_add_16(&tlvlist, 0x0001, info->flags); + if (info->present & AIM_USERINFO_PRESENT_MEMBERSINCE) + aim_tlvlist_add_32(&tlvlist, 0x0002, info->membersince); + if (info->present & AIM_USERINFO_PRESENT_ONLINESINCE) + aim_tlvlist_add_32(&tlvlist, 0x0003, info->onlinesince); + if (info->present & AIM_USERINFO_PRESENT_IDLE) + aim_tlvlist_add_16(&tlvlist, 0x0004, info->idletime); + +/* XXX - So, ICQ_OSCAR_SUPPORT is never defined anywhere... */ +#ifdef ICQ_OSCAR_SUPPORT + if (atoi(info->sn) != 0) { + if (info->present & AIM_USERINFO_PRESENT_ICQEXTSTATUS) + aim_tlvlist_add_16(&tlvlist, 0x0006, info->icqinfo.status); + if (info->present & AIM_USERINFO_PRESENT_ICQIPADDR) + aim_tlvlist_add_32(&tlvlist, 0x000a, info->icqinfo.ipaddr); + } +#endif + + if (info->present & AIM_USERINFO_PRESENT_CAPABILITIES) + aim_tlvlist_add_caps(&tlvlist, 0x000d, info->capabilities); + + if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN) + aim_tlvlist_add_32(&tlvlist, (guint16)((info->flags & AIM_FLAG_AOL) ? 0x0010 : 0x000f), info->sessionlen); + + aimbs_put16(bs, aim_tlvlist_count(&tlvlist)); + aim_tlvlist_write(bs, &tlvlist); + aim_tlvlist_free(&tlvlist); + + return 0; +} + +/* + * Subtype 0x0001 + */ +static int error(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; + aim_snac_t *snac2; + guint16 reason; + char *sn; + int was_explicit; + + if (!(snac2 = aim_remsnac(sess, snac->id))) { + gaim_debug_misc("oscar", "faim: locate.c, error(): received response from unknown request!\n"); + return 0; + } + + if ((snac2->family != 0x0002) && (snac2->type != 0x0015)) { + gaim_debug_misc("oscar", "faim: locate.c, error(): received response from invalid request! %d\n", snac2->family); + return 0; + } + + if (!(sn = snac2->data)) { + gaim_debug_misc("oscar", "faim: locate.c, error(): received response from request without a screen name!\n"); + return 0; + } + + reason = aimbs_get16(bs); + + /* + * Remove this screen name from our queue. If the client requested + * this buddy's info explicitly, then notify them that we do not have + * info for this buddy. + */ + was_explicit = aim_locate_gotuserinfo(sess, sn); + if (was_explicit == TRUE) + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, reason, sn); + + if (snac2) + free(snac2->data); + free(snac2); + + return ret; +} + +/* + * Subtype 0x0002 + * + * Request Location services rights. + * + */ +faim_export int aim_locate_reqrights(aim_session_t *sess) +{ + aim_conn_t *conn; + + if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_LOCATE))) + return -EINVAL; + + return aim_genericreq_n_snacid(sess, conn, OSCAR_FAMILY_LOCATE, OSCAR_SUBTYPE_LOCATE_REQRIGHTS); +} + +/* + * Subtype 0x0003 + * + * Normally contains: + * t(0001) - short containing max profile length (value = 1024) + * t(0002) - short - unknown (value = 16) [max MIME type length?] + * t(0003) - short - unknown (value = 10) + * t(0004) - short - unknown (value = 2048) [ICQ only?] + */ +static int rights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_tlvlist_t *tlvlist; + aim_rxcallback_t userfunc; + int ret = 0; + guint16 maxsiglen = 0; + + tlvlist = aim_tlvlist_read(bs); + + if (aim_tlv_gettlv(tlvlist, 0x0001, 1)) + maxsiglen = aim_tlv_get16(tlvlist, 0x0001, 1); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, maxsiglen); + + aim_tlvlist_free(&tlvlist); + + return ret; +} + +/* + * Subtype 0x0004 + * + * Gives BOS your profile. + * + * profile_encoding and awaymsg_encoding MUST be set if profile or + * away are set, respectively, and their value may or may not be + * restricted to a few choices. I am currently aware of: + * + * us-ascii Just that + * unicode-2-0 UCS2-BE + * + * profile_len and awaymsg_len MUST be set similarly, and they MUST + * be the length of their respective strings in bytes. + * + * To get the previous behavior of awaymsg == "" un-setting the away + * message, set awaymsg non-NULL and awaymsg_len to 0 (this is the + * obvious equivalent). + * + */ +faim_export int aim_locate_setprofile(aim_session_t *sess, + const char *profile_encoding, const gchar *profile, const int profile_len, + const char *awaymsg_encoding, const gchar *awaymsg, const int awaymsg_len) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + char *encoding; + static const char defencoding[] = {"text/aolrtf; charset=\"%s\""}; + + if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_LOCATE))) + return -EINVAL; + + if (!profile && !awaymsg) + return -EINVAL; + + if ((profile && profile_encoding == NULL) || (awaymsg && awaymsg_len && awaymsg_encoding == NULL)) { + return -EINVAL; + } + + /* Build the packet first to get real length */ + if (profile) { + /* no + 1 here because of %s */ + encoding = malloc(strlen(defencoding) + strlen(profile_encoding)); + if (encoding == NULL) { + return -ENOMEM; + } + snprintf(encoding, strlen(defencoding) + strlen(profile_encoding), defencoding, profile_encoding); + aim_tlvlist_add_str(&tl, 0x0001, encoding); + aim_tlvlist_add_raw(&tl, 0x0002, profile_len, (const guchar *)profile); + free(encoding); + } + + /* + * 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) { + if (awaymsg_len) { + encoding = malloc(strlen(defencoding) + strlen(awaymsg_encoding)); + if (encoding == NULL) { + return -ENOMEM; + } + snprintf(encoding, strlen(defencoding) + strlen(awaymsg_encoding), defencoding, awaymsg_encoding); + aim_tlvlist_add_str(&tl, 0x0003, encoding); + aim_tlvlist_add_raw(&tl, 0x0004, awaymsg_len, (const guchar *)awaymsg); + free(encoding); + } else + aim_tlvlist_add_noval(&tl, 0x0004); + } + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + aim_tlvlist_size(&tl)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0002, 0x0004, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0002, 0x004, 0x0000, snacid); + + aim_tlvlist_write(&fr->data, &tl); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0004 - Set your client's capabilities. + */ +faim_export int aim_locate_setcaps(aim_session_t *sess, guint32 caps) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + + if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_LOCATE))) + return -EINVAL; + + aim_tlvlist_add_caps(&tl, 0x0005, caps); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + aim_tlvlist_size(&tl)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0002, 0x0004, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0002, 0x004, 0x0000, snacid); + + aim_tlvlist_write(&fr->data, &tl); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0005 - Request info of another AIM user. + * + * @param sn The screenname whose info you wish to request. + * @param infotype The type of info you wish to request. + * 0x0001 - Info/profile + * 0x0003 - Away message + * 0x0004 - Capabilities + */ +faim_export int aim_locate_getinfo(aim_session_t *sess, const char *sn, guint16 infotype) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_LOCATE)) || !sn) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 12+1+strlen(sn)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0002, 0x0005, 0x0000, NULL, 0); + + aim_putsnac(&fr->data, 0x0002, 0x0005, 0x0000, snacid); + aimbs_put16(&fr->data, infotype); + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putstr(&fr->data, sn); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* Subtype 0x0006 */ +static int userinfo(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; + aim_userinfo_t *userinfo, *userinfo2; + aim_tlvlist_t *tlvlist; + aim_tlv_t *tlv = NULL; + int was_explicit; + + userinfo = (aim_userinfo_t *)malloc(sizeof(aim_userinfo_t)); + aim_info_extract(sess, bs, userinfo); + tlvlist = aim_tlvlist_read(bs); + + /* Profile will be 1 and 2 */ + userinfo->info_encoding = aim_tlv_getstr(tlvlist, 0x0001, 1); + if ((tlv = aim_tlv_gettlv(tlvlist, 0x0002, 1))) { + userinfo->info = (char *)malloc(tlv->length); + memcpy(userinfo->info, tlv->value, tlv->length); + userinfo->info_len = tlv->length; + } + + /* Away message will be 3 and 4 */ + userinfo->away_encoding = aim_tlv_getstr(tlvlist, 0x0003, 1); + if ((tlv = aim_tlv_gettlv(tlvlist, 0x0004, 1))) { + userinfo->away = (char *)malloc(tlv->length); + memcpy(userinfo->away, tlv->value, tlv->length); + userinfo->away_len = tlv->length; + } + + /* Caps will be 5 */ + if ((tlv = aim_tlv_gettlv(tlvlist, 0x0005, 1))) { + aim_bstream_t cbs; + aim_bstream_init(&cbs, tlv->value, tlv->length); + userinfo->capabilities = aim_locate_getcaps(sess, &cbs, tlv->length); + userinfo->present = AIM_USERINFO_PRESENT_CAPABILITIES; + } + aim_tlvlist_free(&tlvlist); + + aim_locate_adduserinfo(sess, userinfo); + userinfo2 = aim_locate_finduserinfo(sess, userinfo->sn); + aim_info_free(userinfo); + free(userinfo); + + /* + * Remove this screen name from our queue. If the client requested + * this buddy's info explicitly, then notify them that we have info + * for this buddy. + */ + was_explicit = aim_locate_gotuserinfo(sess, userinfo2->sn); + if (was_explicit == TRUE) + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, userinfo2); + + return ret; +} + +/* + * Subtype 0x0009 - Set directory profile data. + * + * This is not the same as aim_location_setprofile! + * privacy: 1 to allow searching, 0 to disallow. + * + */ +faim_export int aim_locate_setdirinfo(aim_session_t *sess, 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, guint16 privacy) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + + if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_LOCATE))) + return -EINVAL; + + aim_tlvlist_add_16(&tl, 0x000a, privacy); + + if (first) + aim_tlvlist_add_str(&tl, 0x0001, first); + if (last) + aim_tlvlist_add_str(&tl, 0x0002, last); + if (middle) + aim_tlvlist_add_str(&tl, 0x0003, middle); + if (maiden) + aim_tlvlist_add_str(&tl, 0x0004, maiden); + + if (state) + aim_tlvlist_add_str(&tl, 0x0007, state); + if (city) + aim_tlvlist_add_str(&tl, 0x0008, city); + + if (nickname) + aim_tlvlist_add_str(&tl, 0x000c, nickname); + if (zip) + aim_tlvlist_add_str(&tl, 0x000d, zip); + + if (street) + aim_tlvlist_add_str(&tl, 0x0021, street); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_tlvlist_size(&tl)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0002, 0x0009, 0x0000, NULL, 0); + + aim_putsnac(&fr->data, 0x0002, 0x0009, 0x0000, snacid); + aim_tlvlist_write(&fr->data, &tl); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x000b - Huh? What is this? + */ +faim_export int aim_locate_000b(aim_session_t *sess, const char *sn) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + return -EINVAL; + + if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_LOCATE)) || !sn) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0002, 0x000b, 0x0000, NULL, 0); + + aim_putsnac(&fr->data, 0x0002, 0x000b, 0x0000, snacid); + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putstr(&fr->data, sn); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x000f + * + * XXX pass these in better + * + */ +faim_export int aim_locate_setinterests(aim_session_t *sess, const char *interest1, const char *interest2, const char *interest3, const char *interest4, const char *interest5, guint16 privacy) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + + if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_LOCATE))) + return -EINVAL; + + /* ?? privacy ?? */ + aim_tlvlist_add_16(&tl, 0x000a, privacy); + + if (interest1) + aim_tlvlist_add_str(&tl, 0x0000b, interest1); + if (interest2) + aim_tlvlist_add_str(&tl, 0x0000b, interest2); + if (interest3) + aim_tlvlist_add_str(&tl, 0x0000b, interest3); + if (interest4) + aim_tlvlist_add_str(&tl, 0x0000b, interest4); + if (interest5) + aim_tlvlist_add_str(&tl, 0x0000b, interest5); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_tlvlist_size(&tl)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0002, 0x000f, 0x0000, NULL, 0); + + aim_putsnac(&fr->data, 0x0002, 0x000f, 0x0000, 0); + aim_tlvlist_write(&fr->data, &tl); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0015 - Request the info a user using the short method. This is + * what iChat uses. It normally is VERY leniently rate limited. + * + * @param sn The screen name whose info you wish to request. + * @param flags The bitmask which specifies the type of info you wish to request. + * 0x00000001 - Info/profile. + * 0x00000002 - Away message. + * 0x00000004 - Capabilities. + * 0x00000008 - Certification. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_locate_getinfoshort(aim_session_t *sess, const char *sn, guint32 flags) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_LOCATE)) || !sn) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4+1+strlen(sn)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0002, 0x0015, 0x0000, sn, strlen(sn)+1); + + aim_putsnac(&fr->data, 0x0002, 0x0015, 0x0000, snacid); + aimbs_put32(&fr->data, flags); + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putstr(&fr->data, sn); + + 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 == 0x0001) + return error(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0003) + return rights(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0006) + return userinfo(sess, mod, rx, snac, bs); + + return 0; +} + +static void locate_shutdown(aim_session_t *sess, aim_module_t *mod) +{ + aim_userinfo_t *del; + + while (sess->locate.userinfo) { + del = sess->locate.userinfo; + sess->locate.userinfo = sess->locate.userinfo->next; + aim_info_free(del); + free(del); + } +} + +faim_internal int locate_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = OSCAR_FAMILY_LOCATE; + mod->version = 0x0001; + mod->toolid = 0x0110; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "locate", sizeof(mod->name)); + mod->snachandler = snachandler; + mod->shutdown = locate_shutdown; + + return 0; +} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/family_odir.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/oscar/family_odir.c Sat Feb 11 21:45:18 2006 +0000 @@ -0,0 +1,264 @@ +/* + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * Family 0x000f - Newer Search Method + * + * Used for searching for other AIM users by email address, name, + * location, commmon interests, and a few other similar things. + * + */ + +#include "oscar.h" + +/** + * Subtype 0x0002 - Submit a User Search Request + * + * Search for an AIM screen name based on their email address. + * + * @param sess The oscar session. + * @param region Should be "us-ascii" unless you know what you're doing. + * @param email The email address you want to search for. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_odir_email(aim_session_t *sess, const char *region, const char *email) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x000f)) || !region || !email) + return -EINVAL; + + /* Create a TLV chain, write it to the outgoing frame, then free the chain */ + aim_tlvlist_add_str(&tl, 0x001c, region); + aim_tlvlist_add_16(&tl, 0x000a, 0x0001); /* Type of search */ + aim_tlvlist_add_str(&tl, 0x0005, email); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_tlvlist_size(&tl)))) + return -ENOMEM; + snacid = aim_cachesnac(sess, 0x000f, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x000f, 0x0002, 0x0000, snacid); + + aim_tlvlist_write(&fr->data, &tl); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + + +/** + * Subtype 0x0002 - Submit a User Search Request + * + * Search for an AIM screen name based on various info + * about the person. + * + * @param sess The oscar session. + * @param region Should be "us-ascii" unless you know what you're doing. + * @param first The first name of the person you want to search for. + * @param middle The middle name of the person you want to search for. + * @param last The last name of the person you want to search for. + * @param maiden The maiden name of the person you want to search for. + * @param nick The nick name of the person you want to search for. + * @param city The city where the person you want to search for resides. + * @param state The state where the person you want to search for resides. + * @param country The country where the person you want to search for resides. + * @param zip The zip code where the person you want to search for resides. + * @param address The street address where the person you want to seach for resides. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_odir_name(aim_session_t *sess, const char *region, const char *first, const char *middle, const char *last, const char *maiden, const char *nick, const char *city, const char *state, const char *country, const char *zip, const char *address) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x000f)) || !region) + return -EINVAL; + + /* Create a TLV chain, write it to the outgoing frame, then free the chain */ + aim_tlvlist_add_str(&tl, 0x001c, region); + aim_tlvlist_add_16(&tl, 0x000a, 0x0000); /* Type of search */ + if (first) + aim_tlvlist_add_str(&tl, 0x0001, first); + if (last) + aim_tlvlist_add_str(&tl, 0x0002, last); + if (middle) + aim_tlvlist_add_str(&tl, 0x0003, middle); + if (maiden) + aim_tlvlist_add_str(&tl, 0x0004, maiden); + if (country) + aim_tlvlist_add_str(&tl, 0x0006, country); + if (state) + aim_tlvlist_add_str(&tl, 0x0007, state); + if (city) + aim_tlvlist_add_str(&tl, 0x0008, city); + if (nick) + aim_tlvlist_add_str(&tl, 0x000c, nick); + if (zip) + aim_tlvlist_add_str(&tl, 0x000d, zip); + if (address) + aim_tlvlist_add_str(&tl, 0x0021, address); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_tlvlist_size(&tl)))) + return -ENOMEM; + snacid = aim_cachesnac(sess, 0x000f, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x000f, 0x0002, 0x0000, snacid); + + aim_tlvlist_write(&fr->data, &tl); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + + +/** + * Subtype 0x0002 - Submit a User Search Request + * + * @param sess The oscar session. + * @param interest1 An interest you want to search for. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_odir_interest(aim_session_t *sess, const char *region, const char *interest) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x000f)) || !region) + return -EINVAL; + + /* Create a TLV chain, write it to the outgoing frame, then free the chain */ + aim_tlvlist_add_str(&tl, 0x001c, region); + aim_tlvlist_add_16(&tl, 0x000a, 0x0001); /* Type of search */ + if (interest) + aim_tlvlist_add_str(&tl, 0x0001, interest); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_tlvlist_size(&tl)))) + return -ENOMEM; + snacid = aim_cachesnac(sess, 0x000f, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x000f, 0x0002, 0x0000, snacid); + + aim_tlvlist_write(&fr->data, &tl); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + + +/** + * Subtype 0x0003 - Receive Reply From a User Search + * + */ +static int parseresults(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; + guint16 tmp, numresults; + struct aim_odir *results = NULL; + + tmp = aimbs_get16(bs); /* Unknown */ + tmp = aimbs_get16(bs); /* Unknown */ + aim_bstream_advance(bs, tmp); + + numresults = aimbs_get16(bs); /* Number of results to follow */ + + /* Allocate a linked list, 1 node per result */ + while (numresults) { + struct aim_odir *new; + aim_tlvlist_t *tl = aim_tlvlist_readnum(bs, aimbs_get16(bs)); + new = (struct aim_odir *)malloc(sizeof(struct aim_odir)); + new->first = aim_tlv_getstr(tl, 0x0001, 1); + new->last = aim_tlv_getstr(tl, 0x0002, 1); + new->middle = aim_tlv_getstr(tl, 0x0003, 1); + new->maiden = aim_tlv_getstr(tl, 0x0004, 1); + new->email = aim_tlv_getstr(tl, 0x0005, 1); + new->country = aim_tlv_getstr(tl, 0x0006, 1); + new->state = aim_tlv_getstr(tl, 0x0007, 1); + new->city = aim_tlv_getstr(tl, 0x0008, 1); + new->sn = aim_tlv_getstr(tl, 0x0009, 1); + new->interest = aim_tlv_getstr(tl, 0x000b, 1); + new->nick = aim_tlv_getstr(tl, 0x000c, 1); + new->zip = aim_tlv_getstr(tl, 0x000d, 1); + new->region = aim_tlv_getstr(tl, 0x001c, 1); + new->address = aim_tlv_getstr(tl, 0x0021, 1); + new->next = results; + results = new; + numresults--; + } + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, results); + + /* Now free everything from above */ + while (results) { + struct aim_odir *del = results; + results = results->next; + free(del->first); + free(del->last); + free(del->middle); + free(del->maiden); + free(del->email); + free(del->country); + free(del->state); + free(del->city); + free(del->sn); + free(del->interest); + free(del->nick); + free(del->zip); + free(del->region); + free(del->address); + free(del); + } + + 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 parseresults(sess, mod, rx, snac, bs); + + return 0; +} + +faim_internal int odir_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x000f; + mod->version = 0x0001; + mod->toolid = 0x0010; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "odir", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/family_oservice.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/oscar/family_oservice.c Sat Feb 11 21:45:18 2006 +0000 @@ -0,0 +1,1144 @@ +/* + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * Family 0x0001 - This is a very special group. All connections support + * this group, as it does some particularly good things (like rate limiting). + */ + +#include "oscar.h" + +#include "cipher.h" + +/* Subtype 0x0002 - Client Online */ +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 + gaim_debug_misc("oscar", "aim_clientready: server supports group 0x%04x but we don't!\n", sg->group); + } + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0003 - Host Online + * + * 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) +{ + guint16 *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; +} + +/* Subtype 0x0004 - Service request */ +faim_export int aim_reqservice(aim_session_t *sess, aim_conn_t *conn, guint16 serviceid) +{ + return aim_genericreq_s(sess, conn, 0x0001, 0x0004, &serviceid); +} + +/* + * Join a room of name roomname. This is the first step to joining an + * already created room. It's basically a Service Request for + * family 0x000e, with a little added on to specify the exchange and room + * name. + */ +faim_export int aim_chat_join(aim_session_t *sess, aim_conn_t *conn, guint16 exchange, const char *roomname, guint16 instance) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + struct chatsnacinfo csi; + + if (!sess || !conn || !roomname || !strlen(roomname)) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 512))) + return -ENOMEM; + + memset(&csi, 0, sizeof(csi)); + csi.exchange = exchange; + strncpy(csi.name, roomname, sizeof(csi.name)); + csi.instance = instance; + + snacid = aim_cachesnac(sess, 0x0001, 0x0004, 0x0000, &csi, sizeof(csi)); + aim_putsnac(&fr->data, 0x0001, 0x0004, 0x0000, snacid); + + /* + * Requesting service chat (0x000e) + */ + aimbs_put16(&fr->data, 0x000e); + + aim_tlvlist_add_chatroom(&tl, 0x0001, exchange, roomname, instance); + aim_tlvlist_write(&fr->data, &tl); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* Subtype 0x0005 - Redirect */ +static int redirect(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + struct aim_redirect_data redir; + aim_rxcallback_t userfunc; + aim_tlvlist_t *tlvlist; + aim_snac_t *origsnac = NULL; + int ret = 0; + + memset(&redir, 0, sizeof(redir)); + + tlvlist = aim_tlvlist_read(bs); + + if (!aim_tlv_gettlv(tlvlist, 0x000d, 1) || + !aim_tlv_gettlv(tlvlist, 0x0005, 1) || + !aim_tlv_gettlv(tlvlist, 0x0006, 1)) { + aim_tlvlist_free(&tlvlist); + return 0; + } + + redir.group = aim_tlv_get16(tlvlist, 0x000d, 1); + redir.ip = aim_tlv_getstr(tlvlist, 0x0005, 1); + redir.cookielen = aim_tlv_gettlv(tlvlist, 0x0006, 1)->length; + redir.cookie = (guchar *)aim_tlv_getstr(tlvlist, 0x0006, 1); + + /* Fetch original SNAC so we can get csi if needed */ + origsnac = aim_remsnac(sess, snac->id); + + if ((redir.group == AIM_CONN_TYPE_CHAT) && origsnac) { + struct chatsnacinfo *csi = (struct chatsnacinfo *)origsnac->data; + + redir.chat.exchange = csi->exchange; + redir.chat.room = csi->name; + redir.chat.instance = csi->instance; + } + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, &redir); + + free((void *)redir.ip); + free((void *)redir.cookie); + + if (origsnac) + free(origsnac->data); + free(origsnac); + + aim_tlvlist_free(&tlvlist); + + return ret; +} + +/* Subtype 0x0006 - Request Rate Information. */ +faim_internal int aim_reqrates(aim_session_t *sess, aim_conn_t *conn) +{ + return aim_genericreq_n_snacid(sess, conn, 0x0001, 0x0006); +} + +/* + * OSCAR defines several 'rate classes'. Each class has separate + * 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, guint16 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, guint16 group, guint16 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; +} + +/* Subtype 0x0007 - Rate Parameters */ +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; + guint16 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)); + + gaim_debug_misc("oscar", "--- Adding rate class %d to connection type %d: window size = %ld, clear = %ld, alert = %ld, limit = %ld, disconnect = %ld, current = %ld, max = %ld\n", rx->conn->type, rc.classid, rc.windowsize, rc.clear, rc.alert, rc.limit, rc.disconnect, rc.current, rc.max); + + rc_addclass(&ins->rates, &rc); + } + + /* + * Then the members of each class. + */ + for (i = 0; i < numclasses; i++) { + guint16 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++) { + guint16 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; +} + +/* Subtype 0x0008 - Add Rate Parameter */ +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; +} + +/* Subtype 0x0009 - Delete Rate Parameter */ +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; +} + +/* Subtype 0x000a - Rate Change */ +static int ratechange(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; + guint16 code, rateclass; + guint32 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))) + ret = userfunc(sess, rx, code, rateclass, windowsize, clear, alert, limit, disconnect, currentavg, maxavg); + + return ret; +} + +/* + * 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. + * + */ + +/* Subtype 0x000b - Service Pause */ +static int serverpause(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; +} + +/* + * Subtype 0x000c - Service Pause Acknowledgement + * + * 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; +} + +/* Subtype 0x000d - Service Resume */ +static int serverresume(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; +} + +/* Subtype 0x000e - Request self-info */ +faim_export int aim_reqpersonalinfo(aim_session_t *sess, aim_conn_t *conn) +{ + return aim_genericreq_n_snacid(sess, conn, 0x0001, 0x000e); +} + +/* Subtype 0x000f - Self User Info */ +static int selfinfo(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; + aim_userinfo_t userinfo; + + aim_info_extract(sess, bs, &userinfo); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, &userinfo); + + aim_info_free(&userinfo); + + return ret; +} + +/* Subtype 0x0010 - Evil Notification */ +static int evilnotify(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; + guint16 newevil; + aim_userinfo_t userinfo; + + memset(&userinfo, 0, sizeof(aim_userinfo_t)); + + newevil = aimbs_get16(bs); + + if (aim_bstream_empty(bs)) + aim_info_extract(sess, bs, &userinfo); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, newevil, &userinfo); + + aim_info_free(&userinfo); + + return ret; +} + +/* + * Subtype 0x0011 - Idle Notification + * + * 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_srv_setidle(aim_session_t *sess, guint32 idletime) +{ + aim_conn_t *conn; + + if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_BOS))) + return -EINVAL; + + return aim_genericreq_l(sess, conn, 0x0001, 0x0011, &idletime); +} + +/* + * Subtype 0x0012 - Service Migrate + * + * 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; + guint16 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++) { + guint16 group; + + group = aimbs_get16(bs); + + gaim_debug_misc("oscar", "bifurcated migration unsupported -- group 0x%04x\n", group); + } + + tl = aim_tlvlist_read(bs); + + if (aim_tlv_gettlv(tl, 0x0005, 1)) + ip = aim_tlv_getstr(tl, 0x0005, 1); + + cktlv = aim_tlv_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_tlvlist_free(&tl); + free(ip); + + return ret; +} + +/* Subtype 0x0013 - Message of the Day */ +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; + guint16 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_tlvlist_read(bs); + + msg = aim_tlv_getstr(tlvlist, 0x000b, 1); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, id, msg); + + free(msg); + + aim_tlvlist_free(&tlvlist); + + return ret; +} + +/* + * Subtype 0x0014 - Set 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, guint32 flags) +{ + return aim_genericreq_l(sess, conn, 0x0001, 0x0014, &flags); +} + +/* + * Subtype 0x0016 - No-op + * + * WinAIM sends these every 4min or so to keep the connection alive. Its not + * really necessary. + * + * Wha? No? Since when? I think WinAIM sends an empty channel 3 + * SNAC as a no-op... + */ +faim_export int aim_nop(aim_session_t *sess, aim_conn_t *conn) +{ + return aim_genericreq_n(sess, conn, 0x0001, 0x0016); +} + +/* + * Subtype 0x0017 - Set client versions + * + * 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 + gaim_debug_misc("oscar", "aim_setversions: server supports group 0x%04x but we don't!\n", sg->group); + } + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* Subtype 0x0018 - Host versions */ +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; + guint8 *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; +} + +/* + * Subtype 0x001e - Set various account settings (mostly ICQ related). + * + * These settings are transient, not server-stored (i.e. they only + * apply to this session, and must be re-set the next time you sign + * on). + * + * You can set your ICQ status (available, away, do not disturb, + * etc.), or whether your IP address should be hidden or not, or + * if your status is visible on ICQ web sites, and you can set + * your IP address info and what not. + * + * These are the same TLVs seen in user info. You can + * also set 0x0008 and 0x000c. + */ +faim_export int aim_setextstatus(aim_session_t *sess, guint32 status) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + guint32 data; + + if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_ICBM))) + return -EINVAL; + + data = AIM_ICQ_STATE_HIDEIP | AIM_ICQ_STATE_DIRECTREQUIREAUTH | status; + + 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_tlvlist_add_32(&tl, 0x0006, data); +#if 0 + aim_tlvlist_add_raw(&tl, 0x000c, 0x0025, chunk_of_x25_bytes_with_ip_address_etc); + aim_tlvlist_add_raw(&tl, 0x0011, 0x0005, unknown 0x01 61 10 f6 41); + aim_tlvlist_add_16(&tl, 0x0012, unknown 0x00 00); +#endif + aim_tlvlist_write(&fr->data, &tl); + aim_tlvlist_free(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x001e - Extended Status. + * + * Sets your "available" message. This is currently only supported by iChat + * and Gaim. + * + * These are the same TLVs seen in user info. You can + * also set 0x0008 and 0x000c. + */ +faim_export int aim_srv_setstatusmsg(aim_session_t *sess, const char *msg) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) + return -EINVAL; + + if ((msg != NULL) && *msg != '\0') { + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + strlen(msg) + 8))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0001, 0x001e, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0001, 0x001e, 0x0000, snacid); + + aimbs_put16(&fr->data, 0x001d); /* userinfo TLV type */ + aimbs_put16(&fr->data, strlen(msg)+8); /* total length of userinfo TLV data */ + aimbs_put16(&fr->data, 0x0002); + aimbs_put8(&fr->data, 0x04); + aimbs_put8(&fr->data, strlen(msg)+4); + aimbs_put16(&fr->data, strlen(msg)); + aimbs_putstr(&fr->data, msg); + aimbs_put16(&fr->data, 0x0000); + } else { + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + 8))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0001, 0x001e, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0001, 0x001e, 0x0000, snacid); + + aimbs_put16(&fr->data, 0x001d); + aimbs_put16(&fr->data, 0x0008); + aimbs_put16(&fr->data, 0x0002); + aimbs_put16(&fr->data, 0x0404); + aimbs_put16(&fr->data, 0x0000); + aimbs_put16(&fr->data, 0x0000); + } + + 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 receives this request.) + * + * When the client receives 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, receiving 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. + * + */ +/* Subtype 0x001f - Client verification */ +static int memrequest(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; + guint32 offset, len; + aim_tlvlist_t *list; + char *modname; + + offset = aimbs_get32(bs); + len = aimbs_get32(bs); + list = aim_tlvlist_read(bs); + + modname = aim_tlv_getstr(list, 0x0001, 1); + + gaim_debug_info("oscar", "Got memory request for data at 0x%08lx (%d bytes) of requested %s\n", offset, len, modname ? modname : "aim.exe"); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, offset, len, modname); + + free(modname); + aim_tlvlist_free(&list); + + return ret; +} + +/* Subtype 0x0020 - Client verification reply */ +faim_export int aim_sendmemblock(aim_session_t *sess, aim_conn_t *conn, guint32 offset, guint32 len, const guint8 *buf, guint8 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 */ + GaimCipher *cipher; + GaimCipherContext *context; + guchar digest[16]; + + cipher = gaim_ciphers_find_cipher("md5"); + + context = gaim_cipher_context_new(cipher, NULL); + gaim_cipher_context_append(context, buf, len); + gaim_cipher_context_digest(context, 16, digest, NULL); + gaim_cipher_context_destroy(context); + + aimbs_putraw(&fr->data, digest, 0x10); + + } else if (len == 0) { /* no length, just hash NULL (buf is optional) */ + GaimCipher *cipher; + GaimCipherContext *context; + guchar digest[16]; + guint8 nil = '\0'; + + /* + * I'm not sure if we really need the empty append with the + * new MD5 functions, so I'll leave it in, just in case. + */ + cipher = gaim_ciphers_find_cipher("md5"); + + context = gaim_cipher_context_new(cipher, NULL); + gaim_cipher_context_append(context, &nil, 0); + gaim_cipher_context_digest(context, 16, digest, NULL); + gaim_cipher_context_destroy(context); + + aimbs_putraw(&fr->data, 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 + gaim_debug_warning("oscar", "sendmemblock: unknown hash request\n"); + + } + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0021 - Receive our extended status + * + * This is used for iChat's "available" messages, and maybe ICQ extended + * status messages? It's also used to tell the client whether or not it + * needs to upload an SSI buddy icon... who engineers this stuff, anyway? + */ +static int aim_parse_extstatus(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; + guint16 type; + guint8 flags, length; + + type = aimbs_get16(bs); + flags = aimbs_get8(bs); + length = aimbs_get8(bs); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) { + switch (type) { + case 0x0000: + case 0x0001: { /* buddy icon checksum */ + /* not sure what the difference between 1 and 0 is */ + guint8 *md5 = aimbs_getraw(bs, length); + ret = userfunc(sess, rx, type, flags, length, md5); + free(md5); + } break; + case 0x0002: { /* available message */ + /* there is a second length that is just for the message */ + char *msg = aimbs_getstr(bs, aimbs_get16(bs)); + ret = userfunc(sess, rx, msg); + free(msg); + } break; + } + } + + 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 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); + else if (snac->subtype == 0x0021) + return aim_parse_extstatus(sess, mod, rx, snac, bs); + + return 0; +} + +faim_internal int service_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x0001; + mod->version = 0x0003; + mod->toolid = 0x0110; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "oservice", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/family_popup.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/oscar/family_popup.c Sat Feb 11 21:45:18 2006 +0000 @@ -0,0 +1,83 @@ +/* + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * Family 0x0008 - Popups. + * + * Popups are just what it sounds like. They're a way for the server to + * open up an informative box on the client's screen. + */ + +#include + +/* + * This is all there is to it. + * + * The message is probably HTML. + * + */ +static int parsepopup(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_tlvlist_t *tl; + int ret = 0; + char *msg, *url; + guint16 width, height, delay; + + tl = aim_tlvlist_read(bs); + + msg = aim_tlv_getstr(tl, 0x0001, 1); + url = aim_tlv_getstr(tl, 0x0002, 1); + width = aim_tlv_get16(tl, 0x0003, 1); + height = aim_tlv_get16(tl, 0x0004, 1); + delay = aim_tlv_get16(tl, 0x0005, 1); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, msg, url, width, height, delay); + + aim_tlvlist_free(&tl); + free(msg); + free(url); + + 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 == 0x0002) + return parsepopup(sess, mod, rx, snac, bs); + + return 0; +} + +faim_internal int popups_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x0008; + mod->version = 0x0001; + mod->toolid = 0x0104; + mod->toolversion = 0x0001; + mod->flags = 0; + strncpy(mod->name, "popup", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/family_stats.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/oscar/family_stats.c Sat Feb 11 21:45:18 2006 +0000 @@ -0,0 +1,63 @@ +/* + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * Family 0x000b - Statistics. + * + */ + +#include + +static int reportinterval(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; + guint16 interval; + + interval = aimbs_get16(bs); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, interval); + + 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 == 0x0002) + return reportinterval(sess, mod, rx, snac, bs); + + return 0; +} + +faim_internal int stats_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x000b; + mod->version = 0x0001; + mod->toolid = 0x0104; + mod->toolversion = 0x0001; + mod->flags = 0; + strncpy(mod->name, "stats", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/family_translate.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/oscar/family_translate.c Sat Feb 11 21:45:18 2006 +0000 @@ -0,0 +1,46 @@ +/* + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * Family 0x000c - Translation. + * + * I have no idea why this group was issued. I have never seen anything + * that uses it. From what I remember, the last time I tried to poke at + * the server with this group, it whined about not supporting it. + * + * But we advertise it anyway, because its fun. + * + */ + +#include "oscar.h" + +faim_internal int translate_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x000c; + mod->version = 0x0001; + mod->toolid = 0x0104; + mod->toolversion = 0x0001; + mod->flags = 0; + strncpy(mod->name, "translate", sizeof(mod->name)); + mod->snachandler = NULL; + + return 0; +} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/family_userlookup.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/oscar/family_userlookup.c Sat Feb 11 21:45:18 2006 +0000 @@ -0,0 +1,153 @@ +/* + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * Family 0x000a - User Search. + * + * TODO: Add aim_usersearch_name() + * + */ + +#include "oscar.h" + +/* + * Subtype 0x0001 + * + * XXX can this be integrated with the rest of the error handling? + */ +static int error(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; + aim_snac_t *snac2; + + /* XXX the modules interface should have already retrieved this for us */ + if (!(snac2 = aim_remsnac(sess, snac->id))) { + gaim_debug_misc("oscar", "search error: couldn't get a snac for 0x%08lx\n", snac->id); + return 0; + } + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, snac2->data /* address */); + + /* XXX freesnac()? */ + if (snac2) + free(snac2->data); + free(snac2); + + return ret; +} + +/* + * Subtype 0x0002 + * + */ +faim_export int aim_search_address(aim_session_t *sess, aim_conn_t *conn, const char *address) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !conn || !address) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+strlen(address)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x000a, 0x0002, 0x0000, strdup(address), strlen(address)+1); + aim_putsnac(&fr->data, 0x000a, 0x0002, 0x0000, snacid); + + aimbs_putstr(&fr->data, address); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Subtype 0x0003 + * + */ +static int reply(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int j = 0, m, ret = 0; + aim_tlvlist_t *tlvlist; + char *cur = NULL, *buf = NULL; + aim_rxcallback_t userfunc; + aim_snac_t *snac2; + char *searchaddr = NULL; + + if ((snac2 = aim_remsnac(sess, snac->id))) + searchaddr = (char *)snac2->data; + + tlvlist = aim_tlvlist_read(bs); + m = aim_tlvlist_count(&tlvlist); + + /* XXX uhm. + * This is the only place that uses something other than 1 for the 3rd + * parameter to aim_tlv_gettlv_whatever(). + */ + while ((cur = aim_tlv_getstr(tlvlist, 0x0001, j+1)) && j < m) { + buf = realloc(buf, (j+1) * (MAXSNLEN+1)); + + strncpy(&buf[j * (MAXSNLEN+1)], cur, MAXSNLEN); + free(cur); + + j++; + } + + aim_tlvlist_free(&tlvlist); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, searchaddr, j, buf); + + /* XXX freesnac()? */ + if (snac2) + free(snac2->data); + free(snac2); + + free(buf); + + 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 == 0x0001) + return error(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0003) + return reply(sess, mod, rx, snac, bs); + + return 0; +} + +faim_internal int search_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x000a; + mod->version = 0x0001; + mod->toolid = 0x0110; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "userlookup", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/ft.c --- a/src/protocols/oscar/ft.c Sat Feb 11 19:16:38 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1208 +0,0 @@ -/* - * Oscar File transfer (OFT) and Oscar Direct Connect (ODC). - * (ODC is also referred to as DirectIM and IM Image.) - * - * There are a few static helper functions at the top, then - * ODC stuff, then ft stuff. - * - * I feel like this is a good place to explain OFT, so I'm going to - * do just that. Each OFT packet has a header type. I guess this - * is pretty similar to the subtype of a SNAC packet. The type - * basically tells the other client the meaning of the OFT packet. - * There are two distinct types of file transfer, which I usually - * call "sendfile" and "getfile." Sendfile is when you send a file - * to another AIM user. Getfile is when you share a group of files, - * and other users request that you send them the files. - * - * A typical sendfile file transfer goes like this: - * 1) Sender sends a channel 2 ICBM telling the other user that - * we want to send them a file. At the same time, we open a - * listener socket (this should be done before sending the - * ICBM) on some port, and wait for them to connect to us. - * The ICBM we sent should contain our IP address and the port - * number that we're listening on. - * 2) The receiver connects to the sender on the given IP address - * and port. After the connection is established, the receiver - * sends an ICBM signifying that we are ready and waiting. - * 3) The sender sends an OFT PROMPT message over the OFT - * connection. - * 4) The receiver of the file sends back an exact copy of this - * OFT packet, except the cookie is filled in with the cookie - * from the ICBM. I think this might be an attempt to verify - * that the user that is connected is actually the guy that - * we sent the ICBM to. Oh, I've been calling this the ACK. - * 5) The sender starts sending raw data across the connection - * until the entire file has been sent. - * 6) The receiver knows the file is finished because the sender - * sent the file size in an earlier OFT packet. So then the - * receiver sends the DONE thingy (after filling in the - * "received" checksum and size) and closes the connection. - */ - -#define FAIM_INTERNAL -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -#ifndef _WIN32 -#include -#include -#include -#include -#include /* for aim_odc_initiate */ -#include /* for inet_ntoa */ -#include /* for UINT_MAX */ -#define G_DIR_SEPARATOR '/' -#endif - -#ifdef _WIN32 -#include "win32dep.h" -#endif - -/* - * I really want to switch all our networking code to using IPv6 only, - * but that really isn't a good idea at all. Evan S. of Adium says - * OS X sets all connections as "AF_INET6/PF_INET6," even if there is - * nothing inherently IPv6 about them. And I feel like Linux kernel - * 2.6.5 is doing the same thing. So we REALLY should accept - * connections if they're showing up as IPv6. Old OSes (Solaris?) - * that might not have full IPv6 support yet will fail if we try - * to use PF_INET6 but it isn't defined. --Mark Doliner - */ -#ifndef PF_INET6 -#define PF_INET6 PF_INET -#endif - -struct aim_odc_intdata { - fu8_t cookie[8]; - char sn[MAXSNLEN+1]; - char ip[22]; -}; - -/** - * Convert the directory separator from / (0x2f) to ^A (0x01) - * - * @param name The filename to convert. - */ -static void aim_oft_dirconvert_tostupid(char *name) -{ - while (name[0]) { - if (name[0] == 0x01) - name[0] = G_DIR_SEPARATOR; - name++; - } -} - -/** - * Convert the directory separator from ^A (0x01) to / (0x2f) - * - * @param name The filename to convert. - */ -static void aim_oft_dirconvert_fromstupid(char *name) -{ - while (name[0]) { - if (name[0] == G_DIR_SEPARATOR) - name[0] = 0x01; - name++; - } -} - -/** - * Calculate oft checksum of buffer - * - * Prevcheck should be 0xFFFF0000 when starting a checksum of a file. The - * checksum is kind of a rolling checksum thing, so each time you get bytes - * of a file you just call this puppy and it updates the checksum. You can - * calculate the checksum of an entire file by calling this in a while or a - * for loop, or something. - * - * Thanks to Graham Booker for providing this improved checksum routine, - * which is simpler and should be more accurate than Josh Myer's original - * code. -- wtm - * - * This algorithm works every time I have tried it. The other fails - * sometimes. So, AOL who thought this up? It has got to be the weirdest - * checksum I have ever seen. - * - * @param buffer Buffer of data to checksum. Man I'd like to buff her... - * @param bufsize Size of buffer. - * @param prevcheck Previous checksum. - */ -faim_export fu32_t aim_oft_checksum_chunk(const fu8_t *buffer, int bufferlen, fu32_t prevcheck) -{ - fu32_t check = (prevcheck >> 16) & 0xffff, oldcheck; - int i; - unsigned short val; - - for (i=0; i oldcheck) - check--; - } - check = ((check & 0x0000ffff) + (check >> 16)); - check = ((check & 0x0000ffff) + (check >> 16)); - return check << 16; -} - -faim_export fu32_t aim_oft_checksum_file(char *filename) { - FILE *fd; - fu32_t checksum = 0xffff0000; - - if ((fd = fopen(filename, "rb"))) { - int bytes; - fu8_t buffer[1024]; - - while ((bytes = fread(buffer, 1, 1024, fd))) - checksum = aim_oft_checksum_chunk(buffer, bytes, checksum); - fclose(fd); - } - - return checksum; -} - -/** - * After establishing a listening socket, this is called to accept a connection. It - * clones the conn used by the listener, and passes both of these to a signal handler. - * The signal handler should close the listener conn and keep track of the new conn, - * since this is what is used for file transfers and what not. - * - * @param sess The session. - * @param cur The conn the incoming connection is on. - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_handlerendconnect(aim_session_t *sess, aim_conn_t *cur) -{ - int acceptfd = 0; - struct sockaddr addr; - socklen_t addrlen = sizeof(addr); - int ret = 0; - aim_conn_t *newconn; - char ip[20]; - unsigned short port; - - if ((acceptfd = accept(cur->fd, &addr, &addrlen)) == -1) - return 0; /* not an error */ - - if ((addr.sa_family != PF_INET) && (addr.sa_family != PF_INET6)) { - close(acceptfd); - aim_conn_close(cur); - return -1; - } - - strncpy(ip, inet_ntoa(((struct sockaddr_in *)&addr)->sin_addr), sizeof(ip)); - port = ntohs(((struct sockaddr_in *)&addr)->sin_port); - - if (!(newconn = aim_cloneconn(sess, cur))) { - close(acceptfd); - aim_conn_close(cur); - return -ENOMEM; - } - - newconn->type = AIM_CONN_TYPE_RENDEZVOUS; - newconn->fd = acceptfd; - - if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) { - aim_rxcallback_t userfunc; - struct aim_odc_intdata *priv; - - priv = (struct aim_odc_intdata *)(newconn->internal = cur->internal); - cur->internal = NULL; - snprintf(priv->ip, sizeof(priv->ip), "%s:%hu", ip, port); - - if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIM_ESTABLISHED))) - ret = userfunc(sess, NULL, newconn, cur); - - } else if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE) { - } else if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_SENDFILE) { - aim_rxcallback_t userfunc; - - if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_ESTABLISHED))) - ret = userfunc(sess, NULL, newconn, cur); - - } else { - gaim_debug_warning("oscar", "Got a connection on a listener that's not rendezvous. Closing connection.\n"); - aim_conn_close(newconn); - ret = -1; - } - - return ret; -} - -/** - * Send client-to-client typing notification over an established direct connection. - * - * @param sess The session. - * @param conn The already-connected ODC connection. - * @param typing If 0x0002, sends a "typing" message, 0x0001 sends "typed," and - * 0x0000 sends "stopped." - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_odc_send_typing(aim_session_t *sess, aim_conn_t *conn, int typing) -{ - struct aim_odc_intdata *intdata = (struct aim_odc_intdata *)conn->internal; - aim_frame_t *fr; - aim_bstream_t *hdrbs; - fu8_t *hdr; - int hdrlen = 0x44; - - if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS)) - return -EINVAL; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0001, 0))) - return -ENOMEM; - memcpy(fr->hdr.rend.magic, "ODC2", 4); - fr->hdr.rend.hdrlen = hdrlen + 8; - - if (!(hdr = calloc(1, hdrlen))) { - aim_frame_destroy(fr); - return -ENOMEM; - } - - hdrbs = &(fr->data); - aim_bstream_init(hdrbs, hdr, hdrlen); - - aimbs_put16(hdrbs, 0x0006); - aimbs_put16(hdrbs, 0x0000); - aimbs_putraw(hdrbs, intdata->cookie, 8); - aimbs_put16(hdrbs, 0x0000); - aimbs_put16(hdrbs, 0x0000); - aimbs_put16(hdrbs, 0x0000); - aimbs_put16(hdrbs, 0x0000); - aimbs_put32(hdrbs, 0x00000000); - aimbs_put16(hdrbs, 0x0000); - aimbs_put16(hdrbs, 0x0000); - aimbs_put16(hdrbs, 0x0000); - - if (typing == 0x0002) - aimbs_put16(hdrbs, 0x0002 | 0x0008); - else if (typing == 0x0001) - aimbs_put16(hdrbs, 0x0002 | 0x0004); - else - aimbs_put16(hdrbs, 0x0002); - - aimbs_put16(hdrbs, 0x0000); - aimbs_put16(hdrbs, 0x0000); - aimbs_putstr(hdrbs, sess->sn); - - aim_bstream_setpos(hdrbs, 52); /* bleeehh */ - - aimbs_put8(hdrbs, 0x00); - aimbs_put16(hdrbs, 0x0000); - aimbs_put16(hdrbs, 0x0000); - aimbs_put16(hdrbs, 0x0000); - aimbs_put16(hdrbs, 0x0000); - aimbs_put16(hdrbs, 0x0000); - aimbs_put16(hdrbs, 0x0000); - aimbs_put16(hdrbs, 0x0000); - aimbs_put8(hdrbs, 0x00); - - /* end of hdr */ - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/** - * Send client-to-client IM over an established direct connection. - * Call this just like you would aim_send_im, to send a directim. - * - * @param sess The session. - * @param conn The already-connected ODC connection. - * @param msg Null-terminated string to send. - * @param len The length of the message to send, including binary data. - * @param encoding See the AIM_CHARSET_* defines in aim.h - * @param isawaymsg 0 if this is not an auto-response, 1 if it is. - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_odc_send_im(aim_session_t *sess, aim_conn_t *conn, const char *msg, int len, int encoding, int isawaymsg) -{ - aim_frame_t *fr; - aim_bstream_t *hdrbs; - struct aim_odc_intdata *intdata = (struct aim_odc_intdata *)conn->internal; - int hdrlen = 0x44; - fu8_t *hdr; - - if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS) || !msg) - return -EINVAL; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x01, 0))) - return -ENOMEM; - - memcpy(fr->hdr.rend.magic, "ODC2", 4); - fr->hdr.rend.hdrlen = hdrlen + 8; - - if (!(hdr = calloc(1, hdrlen + len))) { - aim_frame_destroy(fr); - return -ENOMEM; - } - - hdrbs = &(fr->data); - aim_bstream_init(hdrbs, hdr, hdrlen + len); - - aimbs_put16(hdrbs, 0x0006); - aimbs_put16(hdrbs, 0x0000); - aimbs_putraw(hdrbs, intdata->cookie, 8); - aimbs_put16(hdrbs, 0x0000); - aimbs_put16(hdrbs, 0x0000); - aimbs_put16(hdrbs, 0x0000); - aimbs_put16(hdrbs, 0x0000); - aimbs_put32(hdrbs, len); - aimbs_put16(hdrbs, encoding); - aimbs_put16(hdrbs, 0x0000); - aimbs_put16(hdrbs, 0x0000); - - /* flags - used for typing notification and to mark if this is an away message */ - aimbs_put16(hdrbs, 0x0000 | isawaymsg); - - aimbs_put16(hdrbs, 0x0000); - aimbs_put16(hdrbs, 0x0000); - aimbs_putstr(hdrbs, sess->sn); - - aim_bstream_setpos(hdrbs, 52); /* bleeehh */ - - aimbs_put8(hdrbs, 0x00); - aimbs_put16(hdrbs, 0x0000); - aimbs_put16(hdrbs, 0x0000); - aimbs_put16(hdrbs, 0x0000); - aimbs_put16(hdrbs, 0x0000); - aimbs_put16(hdrbs, 0x0000); - aimbs_put16(hdrbs, 0x0000); - aimbs_put16(hdrbs, 0x0000); - aimbs_put8(hdrbs, 0x00); - - /* end of hdr2 */ - -#if 0 /* XXX - this is how you send buddy icon info... */ - aimbs_put16(hdrbs, 0x0008); - aimbs_put16(hdrbs, 0x000c); - aimbs_put16(hdrbs, 0x0000); - aimbs_put16(hdrbs, 0x1466); - aimbs_put16(hdrbs, 0x0001); - aimbs_put16(hdrbs, 0x2e0f); - aimbs_put16(hdrbs, 0x393e); - aimbs_put16(hdrbs, 0xcac8); -#endif - aimbs_putraw(hdrbs, (guchar *)msg, len); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/** - * Get the screen name of the peer of a direct connection. - * - * @param conn The ODC connection. - * @return The screen name of the dude, or NULL if there was an anomaly. - */ -faim_export const char *aim_odc_getsn(aim_conn_t *conn) -{ - struct aim_odc_intdata *intdata; - - if (!conn || !conn->internal) - return NULL; - - if ((conn->type != AIM_CONN_TYPE_RENDEZVOUS) || - (conn->subtype != AIM_CONN_SUBTYPE_OFT_DIRECTIM)) - return NULL; - - intdata = (struct aim_odc_intdata *)conn->internal; - - return intdata->sn; -} - -/** - * Get the cookie of a direct connection. - * - * @param conn The ODC connection. - * @return The cookie, an 8 byte unterminated string, or NULL if there was an anomaly. - */ -faim_export const guchar *aim_odc_getcookie(aim_conn_t *conn) -{ - struct aim_odc_intdata *intdata; - - if (!conn || !conn->internal) - return NULL; - - intdata = (struct aim_odc_intdata *)conn->internal; - - return intdata->cookie; -} - -/** - * Find the conn of a direct connection with the given buddy. - * - * @param sess The session. - * @param sn The screen name of the buddy whose direct connection you want to find. - * @return The conn for the direct connection with the given buddy, or NULL if no - * connection was found. - */ -faim_export aim_conn_t *aim_odc_getconn(aim_session_t *sess, const char *sn) -{ - aim_conn_t *cur; - struct aim_odc_intdata *intdata; - - if (!sess || !sn || !strlen(sn)) - return NULL; - - for (cur = sess->connlist; cur; cur = cur->next) { - if ((cur->type == AIM_CONN_TYPE_RENDEZVOUS) && (cur->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM)) { - intdata = cur->internal; - if (!aim_sncmp(intdata->sn, sn)) - return cur; - } - } - - return NULL; -} - -/** - * For those times when we want to open up the direct connection channel ourselves. - * - * You'll want to set up some kind of watcher on this socket. - * When the state changes, call aim_handlerendconnection with - * the connection returned by this. aim_handlerendconnection - * will accept the pending connection and stop listening. - * - * @param sess The session - * @param sn The screen name to connect to. - * @return The new connection. - */ -faim_export aim_conn_t *aim_odc_initiate(aim_session_t *sess, const char *sn, int listenfd, - const fu8_t *localip, fu16_t port, const fu8_t *mycookie) -{ - aim_conn_t *newconn; - aim_msgcookie_t *cookie; - struct aim_odc_intdata *priv; - fu8_t ck[8]; - - if (!localip) - return NULL; - - if (mycookie) { - memcpy(ck, mycookie, 8); - aim_im_sendch2_odcrequest(sess, ck, TRUE, sn, localip, port); - } else - aim_im_sendch2_odcrequest(sess, ck, FALSE, sn, localip, port); - - cookie = (aim_msgcookie_t *)calloc(1, sizeof(aim_msgcookie_t)); - memcpy(cookie->cookie, ck, 8); - cookie->type = AIM_COOKIETYPE_OFTIM; - - /* this one is for the cookie */ - priv = (struct aim_odc_intdata *)calloc(1, sizeof(struct aim_odc_intdata)); - - memcpy(priv->cookie, ck, 8); - strncpy(priv->sn, sn, sizeof(priv->sn)); - cookie->data = priv; - aim_cachecookie(sess, cookie); - - /* XXX - switch to aim_cloneconn()? */ - if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_LISTENER))) { - close(listenfd); - return NULL; - } - - /* this one is for the conn */ - priv = (struct aim_odc_intdata *)calloc(1, sizeof(struct aim_odc_intdata)); - - memcpy(priv->cookie, ck, 8); - strncpy(priv->sn, sn, sizeof(priv->sn)); - - newconn->fd = listenfd; - newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM; - newconn->internal = priv; - newconn->lastactivity = time(NULL); - - return newconn; -} - -/** - * Connect directly to the given buddy for directim. - * - * This is a wrapper for aim_newconn. - * - * If addr is NULL, the socket is not created, but the connection is - * allocated and setup to connect. - * - * @param sess The Godly session. - * @param sn The screen name we're connecting to. I hope it's a girl... - * @param addr Address to connect to. - * @return The new connection. - */ -faim_export aim_conn_t *aim_odc_connect(aim_session_t *sess, const char *sn, const char *addr, const fu8_t *cookie) -{ - aim_conn_t *newconn; - struct aim_odc_intdata *intdata; - - if (!sess || !sn) - return NULL; - - if (!(intdata = calloc(1, sizeof(struct aim_odc_intdata)))) - return NULL; - memcpy(intdata->cookie, cookie, 8); - strncpy(intdata->sn, sn, sizeof(intdata->sn)); - if (addr) - strncpy(intdata->ip, addr, sizeof(intdata->ip)); - - /* XXX - verify that non-blocking connects actually work */ - if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS))) { - free(intdata); - return NULL; - } - - newconn->internal = intdata; - newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM; - - return newconn; -} - -/** - * Sometimes you just don't know with these kinds of people. - * - * @param sess The session. - * @param conn The ODC connection of the incoming data. - * @param frr The frame allocated for the incoming data. - * @param bs It stands for "bologna sandwich." - * @return Return 0 if no errors, otherwise return the error number. - */ -static int handlehdr_odc(aim_session_t *sess, aim_conn_t *conn, aim_frame_t *frr, aim_bstream_t *bs) -{ - aim_frame_t fr; - int ret = 0; - aim_rxcallback_t userfunc; - fu32_t payloadlength; - fu16_t flags, encoding; - char *snptr = NULL; - - fr.conn = conn; - - /* AAA - ugly */ - aim_bstream_setpos(bs, 20); - payloadlength = aimbs_get32(bs); - - aim_bstream_setpos(bs, 24); - encoding = aimbs_get16(bs); - - aim_bstream_setpos(bs, 30); - flags = aimbs_get16(bs); - - aim_bstream_setpos(bs, 36); - /* XXX - create an aimbs_getnullstr function? */ - snptr = aimbs_getstr(bs, 32); /* Next 32 bytes contain the sn, padded with null chars */ - - gaim_debug_misc("oscar", "faim: OFT frame: handlehdr_odc: %04x / %04x / %s\n", payloadlength, flags, snptr); - - if (flags & 0x0008) { - if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING))) - ret = userfunc(sess, &fr, snptr, 2); - } else if (flags & 0x0004) { - if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING))) - ret = userfunc(sess, &fr, snptr, 1); - } else { - if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING))) - ret = userfunc(sess, &fr, snptr, 0); - } - - if ((payloadlength != 0) && (payloadlength != UINT_MAX)) { - char *msg; - int recvd = 0; - int i, isawaymsg; - - isawaymsg = flags & 0x0001; - - if (!(msg = calloc(1, payloadlength+1))) { - free(snptr); - return -ENOMEM; - } - - while (payloadlength - recvd) { - if (payloadlength - recvd >= 1024) - i = aim_recv(conn->fd, &msg[recvd], 1024); - else - i = aim_recv(conn->fd, &msg[recvd], payloadlength - recvd); - if (i <= 0) { - free(msg); - free(snptr); - return -1; - } - recvd = recvd + i; - if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER))) - ret = userfunc(sess, &fr, snptr, (double)recvd / payloadlength); - } - - if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING))) - ret = userfunc(sess, &fr, snptr, msg, payloadlength, encoding, isawaymsg); - - free(msg); - } - - free(snptr); - - return ret; -} - -faim_export struct aim_oft_info *aim_oft_createinfo(aim_session_t *sess, const fu8_t *cookie, const char *sn, const char *ip, fu16_t port, fu32_t size, fu32_t modtime, char *filename, int send_or_recv, int method, int stage) -{ - struct aim_oft_info *new; - - if (!sess) - return NULL; - - if (!(new = (struct aim_oft_info *)calloc(1, sizeof(struct aim_oft_info)))) - return NULL; - - new->sess = sess; - if (cookie) - memcpy(new->cookie, cookie, 8); - else - aim_icbm_makecookie(new->cookie); - if (ip) - new->clientip = strdup(ip); - else - new->clientip = NULL; - if (sn) - new->sn = strdup(sn); - else - new->sn = NULL; - new->method = method; - new->send_or_recv = send_or_recv; - new->stage = stage; - new->port = port; - new->xfer_reffed = FALSE; - new->success = FALSE; - new->fh.totfiles = 1; - new->fh.filesleft = 1; - new->fh.totparts = 1; - new->fh.partsleft = 1; - new->fh.totsize = size; - new->fh.size = size; - new->fh.modtime = modtime; - new->fh.checksum = 0xffff0000; - new->fh.rfrcsum = 0xffff0000; - new->fh.rfcsum = 0xffff0000; - new->fh.recvcsum = 0xffff0000; - strncpy(new->fh.idstring, "OFT_Windows ICBMFT V1.1 32", 31); - if (filename) { - strncpy(new->fh.name, filename, 63); - new->fh.name[63] = '\0'; - } - - new->next = sess->oft_info; - sess->oft_info = new; - - return new; -} - -faim_export struct aim_rv_proxy_info *aim_rv_proxy_createinfo(aim_session_t *sess, const fu8_t *cookie, - fu16_t port) -{ - struct aim_rv_proxy_info *proxy_info; - - if (!(proxy_info = (struct aim_rv_proxy_info*)calloc(1, sizeof(struct aim_rv_proxy_info)))) - return NULL; - - proxy_info->sess = sess; - proxy_info->port = port; - proxy_info->packet_ver = AIM_RV_PROXY_PACKETVER_DFLT; - proxy_info->unknownA = AIM_RV_PROXY_UNKNOWNA_DFLT; - - if (cookie) - memcpy(proxy_info->cookie, cookie, 8); - - return proxy_info; -} - -/** - * Remove the given oft_info struct from the oft_info linked list, and - * then free its memory. - * - * @param sess The session. - * @param oft_info The aim_oft_info struct that we're destroying. - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_oft_destroyinfo(struct aim_oft_info *oft_info) -{ - aim_session_t *sess; - - if (!oft_info || !(sess = oft_info->sess)) - return -EINVAL; - - if (sess->oft_info && (sess->oft_info == oft_info)) { - sess->oft_info = sess->oft_info->next; - } else { - struct aim_oft_info *cur; - for (cur=sess->oft_info; (cur->next && (cur->next!=oft_info)); cur=cur->next); - if (cur->next) - cur->next = cur->next->next; - } - - free(oft_info->sn); - free(oft_info->proxyip); - free(oft_info->clientip); - free(oft_info->verifiedip); - free(oft_info); - - return 0; -} - -/** - * Creates a listener socket so the other dude can connect to us. - * - * You'll want to set up some kind of watcher on this socket. - * When the state changes, call aim_handlerendconnection with - * the connection returned by this. aim_handlerendconnection - * will accept the pending connection and stop listening. - * - * @param sess The session. - * @param oft_info File transfer information associated with this - * connection. - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_sendfile_listen(aim_session_t *sess, struct aim_oft_info *oft_info, int listenfd) -{ - if (!oft_info) - return -EINVAL; - - if (!(oft_info->conn = aim_newconn(sess, AIM_CONN_TYPE_LISTENER))) { - close(listenfd); - return -ENOMEM; - } - - oft_info->conn->fd = listenfd; - oft_info->conn->subtype = AIM_CONN_SUBTYPE_OFT_SENDFILE; - oft_info->conn->lastactivity = time(NULL); - - return 0; -} - -/** - * Extract an &aim_fileheader_t from the given buffer. - * - * @param bs The should be from an incoming rendezvous packet. - * @return A pointer to new struct on success, or NULL on error. - */ -static struct aim_fileheader_t *aim_oft_getheader(aim_bstream_t *bs) -{ - struct aim_fileheader_t *fh; - - if (!(fh = calloc(1, sizeof(struct aim_fileheader_t)))) - return NULL; - - /* The bstream should be positioned after the hdrtype. */ - aimbs_getrawbuf(bs, fh->bcookie, 8); - fh->encrypt = aimbs_get16(bs); - fh->compress = aimbs_get16(bs); - fh->totfiles = aimbs_get16(bs); - fh->filesleft = aimbs_get16(bs); - fh->totparts = aimbs_get16(bs); - fh->partsleft = aimbs_get16(bs); - fh->totsize = aimbs_get32(bs); - fh->size = aimbs_get32(bs); - fh->modtime = aimbs_get32(bs); - fh->checksum = aimbs_get32(bs); - fh->rfrcsum = aimbs_get32(bs); - fh->rfsize = aimbs_get32(bs); - fh->cretime = aimbs_get32(bs); - fh->rfcsum = aimbs_get32(bs); - fh->nrecvd = aimbs_get32(bs); - fh->recvcsum = aimbs_get32(bs); - aimbs_getrawbuf(bs, (guchar *)fh->idstring, 32); - fh->flags = aimbs_get8(bs); - fh->lnameoffset = aimbs_get8(bs); - fh->lsizeoffset = aimbs_get8(bs); - aimbs_getrawbuf(bs, (guchar *)fh->dummy, 69); - aimbs_getrawbuf(bs, (guchar *)fh->macfileinfo, 16); - fh->nencode = aimbs_get16(bs); - fh->nlanguage = aimbs_get16(bs); - aimbs_getrawbuf(bs, (guchar *)fh->name, 64); /* XXX - filenames longer than 64B */ - fh->name[63] = '\0'; - - return fh; -} - -/** - * Fills a buffer with network-order fh data - * - * @param bs A bstream to fill -- automatically initialized - * @param fh A struct aim_fileheader_t to get data from. - * @return Return non-zero on error. - */ -static int aim_oft_buildheader(aim_bstream_t *bs, struct aim_fileheader_t *fh) -{ - fu8_t *hdr; - - if (!bs || !fh) - return -EINVAL; - - if (!(hdr = (unsigned char *)calloc(1, 0x100 - 8))) - return -ENOMEM; - - aim_bstream_init(bs, hdr, 0x100 - 8); - aimbs_putraw(bs, fh->bcookie, 8); - aimbs_put16(bs, fh->encrypt); - aimbs_put16(bs, fh->compress); - aimbs_put16(bs, fh->totfiles); - aimbs_put16(bs, fh->filesleft); - aimbs_put16(bs, fh->totparts); - aimbs_put16(bs, fh->partsleft); - aimbs_put32(bs, fh->totsize); - aimbs_put32(bs, fh->size); - aimbs_put32(bs, fh->modtime); - aimbs_put32(bs, fh->checksum); - aimbs_put32(bs, fh->rfrcsum); - aimbs_put32(bs, fh->rfsize); - aimbs_put32(bs, fh->cretime); - aimbs_put32(bs, fh->rfcsum); - aimbs_put32(bs, fh->nrecvd); - aimbs_put32(bs, fh->recvcsum); - aimbs_putraw(bs, (guchar *)fh->idstring, 32); - aimbs_put8(bs, fh->flags); - aimbs_put8(bs, fh->lnameoffset); - aimbs_put8(bs, fh->lsizeoffset); - aimbs_putraw(bs, (guchar *)fh->dummy, 69); - aimbs_putraw(bs, (guchar *)fh->macfileinfo, 16); - aimbs_put16(bs, fh->nencode); - aimbs_put16(bs, fh->nlanguage); - aimbs_putraw(bs, (guchar *)fh->name, 64); /* XXX - filenames longer than 64B */ - - return 0; -} - -/** - * Create an OFT packet based on the given information, and send it on its merry way. - * - * @param sess The session. - * @param type The subtype of the OFT packet we're sending. - * @param oft_info The aim_oft_info struct with the connection and OFT - * info we're sending. - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_oft_sendheader(aim_session_t *sess, fu16_t type, struct aim_oft_info *oft_info) -{ - aim_frame_t *fr; - - if (!sess || !oft_info || !oft_info->conn || (oft_info->conn->type != AIM_CONN_TYPE_RENDEZVOUS)) - return -EINVAL; - -#if 0 - /* - * If you are receiving a file, the cookie should be null, if you are sending a - * file, the cookie should be the same as the one used in the ICBM negotiation - * SNACs. - */ - fh->lnameoffset = 0x1a; - fh->lsizeoffset = 0x10; - - /* These should be the same as charset and charsubset in ICBMs */ - fh->nencode = 0x0000; - fh->nlanguage = 0x0000; -#endif - - aim_oft_dirconvert_tostupid(oft_info->fh.name); - - if (!(fr = aim_tx_new(sess, oft_info->conn, AIM_FRAMETYPE_OFT, type, 0))) - return -ENOMEM; - - if (aim_oft_buildheader(&fr->data, &oft_info->fh) == -1) { - aim_frame_destroy(fr); - return -ENOMEM; - } - - memcpy(fr->hdr.rend.magic, "OFT2", 4); - fr->hdr.rend.hdrlen = aim_bstream_curpos(&fr->data) + 8; - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/** - * Create a rendezvous "init recv" packet and send it on its merry way. - * This is the first packet sent to the proxy server by the second client - * involved in this rendezvous proxy session. - * - * @param sess The session. - * @param proxy_info Changable pieces of data for this packet - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_rv_proxy_init_recv(struct aim_rv_proxy_info *proxy_info) -{ -#if 0 - aim_tlvlist_t *tlvlist_sendfile; -#endif - aim_bstream_t bs; - fu8_t *bs_raw; - fu16_t packet_len; - fu8_t sn_len; - int err; - - err = 0; - - if (!proxy_info) - return -EINVAL; - - sn_len = strlen(proxy_info->sess->sn); - packet_len = 2 + 2 /* packet_len, packet_ver */ - + 2 + 4 /* cmd_type, unknownA */ - + 2 /* flags */ - + 1 + sn_len /* Length/value pair for screenname */ - + 8 /* ICBM Cookie */ - + 2 /* port */ - + 2 + 2 + 16; /* TLV for Filesend capability block */ - - if (!(bs_raw = malloc(packet_len))) - return -ENOMEM; - - aim_bstream_init(&bs, bs_raw, packet_len); - aimbs_put16(&bs, packet_len - 2); /* Length includes only packets after length marker */ - aimbs_put16(&bs, proxy_info->packet_ver); - aimbs_put16(&bs, AIM_RV_PROXY_INIT_RECV); - aimbs_put32(&bs, proxy_info->unknownA); - aimbs_put16(&bs, proxy_info->flags); - aimbs_put8(&bs, sn_len); - aimbs_putraw(&bs, (const guchar *)proxy_info->sess->sn, sn_len); - aimbs_put16(&bs, proxy_info->port); - aimbs_putraw(&bs, proxy_info->cookie, 8); - - aimbs_put16(&bs, 0x0001); /* Type */ - aimbs_put16(&bs, 16); /* Length */ - aimbs_putcaps(&bs, AIM_CAPS_SENDFILE); /* Value */ - - -#if 0 - /* TODO: Use built-in TLV */ - aim_tlvlist_add_caps(&tlvlist_sendfile, 0x0001, AIM_CAPS_SENDFILE); - aim_tlvlist_write(&bs, &tlvlist_sendfile); -#endif - - aim_bstream_rewind(&bs); - if (aim_bstream_send(&bs, proxy_info->conn, packet_len) != packet_len) - err = errno; - proxy_info->conn->lastactivity = time(NULL); - -#if 0 - aim_tlvlist_free(tlvlist_sendfile); -#endif - free(bs_raw); - - return err; -} - - -/** - * Create a rendezvous "init send" packet and send it on its merry way. - * This is the first packet sent to the proxy server by the client - * first indicating that this will be a proxied connection - * - * @param sess The session. - * @param proxy_info Changable pieces of data for this packet - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_rv_proxy_init_send(struct aim_rv_proxy_info *proxy_info) -{ -#if 0 - aim_tlvlist_t *tlvlist_sendfile; -#endif - aim_bstream_t bs; - fu8_t *bs_raw; - fu16_t packet_len; - fu8_t sn_len; - int err; - - err = 0; - - if (!proxy_info) - return -EINVAL; - - sn_len = strlen(proxy_info->sess->sn); - packet_len = 2 + 2 /* packet_len, packet_ver */ - + 2 + 4 /* cmd_type, unknownA */ - + 2 /* flags */ - + 1 + sn_len /* Length/value pair for screenname */ - + 8 /* ICBM Cookie */ - + 2 + 2 + 16; /* TLV for Filesend capability block */ - - if (!(bs_raw = malloc(packet_len))) - return -ENOMEM; - - aim_bstream_init(&bs, bs_raw, packet_len); - aimbs_put16(&bs, packet_len - 2); /* Length includes only packets after length marker */ - aimbs_put16(&bs, proxy_info->packet_ver); - aimbs_put16(&bs, AIM_RV_PROXY_INIT_SEND); - aimbs_put32(&bs, proxy_info->unknownA); - aimbs_put16(&bs, proxy_info->flags); - aimbs_put8(&bs, sn_len); - aimbs_putraw(&bs, (const guchar *)proxy_info->sess->sn, sn_len); - aimbs_putraw(&bs, proxy_info->cookie, 8); - - aimbs_put16(&bs, 0x0001); /* Type */ - aimbs_put16(&bs, 16); /* Length */ - aimbs_putcaps(&bs, AIM_CAPS_SENDFILE); /* Value */ - - /* TODO: Use built-in TLV */ -#if 0 - aim_tlvlist_add_caps(&tlvlist_sendfile, 0x0001, AIM_CAPS_SENDFILE); - aim_tlvlist_write(&bs, &tlvlist_sendfile); -#endif - - aim_bstream_rewind(&bs); - if (aim_bstream_send(&bs, proxy_info->conn, packet_len) != packet_len) - err = errno; - proxy_info->conn->lastactivity = time(NULL); - -#if 0 - aim_tlvlist_free(tlvlist_sendfile); -#endif - free(bs_raw); - - return err; -} - -/** - * Handle incoming data on a rendezvous connection. This is analogous to the - * consumesnac function in rxhandlers.c, and I really think this should probably - * be in rxhandlers.c as well, but I haven't finished cleaning everything up yet. - * - * @param sess The session. - * @param fr The frame allocated for the incoming data. - * @return Return 0 if the packet was handled correctly, otherwise return the - * error number. - */ -faim_internal int aim_rxdispatch_rendezvous(aim_session_t *sess, aim_frame_t *fr) -{ - aim_conn_t *conn = fr->conn; - int ret = 1; - - if (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) { - if (fr->hdr.rend.type == 0x0001) - ret = handlehdr_odc(sess, conn, fr, &fr->data); - else - gaim_debug_info("oscar", "ODC directim frame unknown, type is %04x\n", fr->hdr.rend.type); - - } else { - aim_rxcallback_t userfunc; - struct aim_fileheader_t *header = aim_oft_getheader(&fr->data); - aim_oft_dirconvert_fromstupid(header->name); /* XXX - This should be client-side */ - - if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, fr->hdr.rend.type))) - ret = userfunc(sess, fr, conn, header->bcookie, header); - - free(header); - } - - if (ret == -1) - aim_conn_close(conn); - - return ret; -} - -/** - * Handle incoming data on a rendezvous proxy connection. This is similar to - * aim_rxdispatch_rendezvous above and should probably be kept with that function. - * - * @param sess The session. - * @param fr The frame allocated for the incoming data. - * @return Return 0 if the packet was handled correctly, otherwise return the - * error number. - */ -faim_internal struct aim_rv_proxy_info *aim_rv_proxy_read(aim_session_t *sess, aim_conn_t *conn) -{ - aim_bstream_t bs_hdr; - fu8_t hdr_buf[AIM_RV_PROXY_HDR_LEN]; - aim_bstream_t bs_body; /* The body (everything but the header) of the packet */ - fu8_t *body_buf = NULL; - fu8_t body_len; - - char str_ip[30] = {""}; - fu8_t ip_temp[4]; - - fu16_t len; - struct aim_rv_proxy_info *proxy_info; - - if(!(proxy_info = malloc(sizeof(struct aim_rv_proxy_info)))) - return NULL; - - aim_bstream_init(&bs_hdr, hdr_buf, AIM_RV_PROXY_HDR_LEN); - if (aim_bstream_recv(&bs_hdr, conn->fd, AIM_RV_PROXY_HDR_LEN) == AIM_RV_PROXY_HDR_LEN) { - aim_bstream_rewind(&bs_hdr); - len = aimbs_get16(&bs_hdr); - proxy_info->packet_ver = aimbs_get16(&bs_hdr); - proxy_info->cmd_type = aimbs_get16(&bs_hdr); - proxy_info->unknownA = aimbs_get32(&bs_hdr); - proxy_info->flags = aimbs_get16(&bs_hdr); - if(proxy_info->cmd_type == AIM_RV_PROXY_READY) { - /* Do a little victory dance - * A ready packet contains no additional information */ - } else if(proxy_info->cmd_type == AIM_RV_PROXY_ERROR) { - if(len == AIM_RV_PROXY_ERROR_LEN - 2) { - body_len = AIM_RV_PROXY_ERROR_LEN - AIM_RV_PROXY_HDR_LEN; - body_buf = malloc(body_len); - aim_bstream_init(&bs_body, body_buf, body_len); - if (aim_bstream_recv(&bs_body, conn->fd, body_len) == body_len) { - aim_bstream_rewind(&bs_body); - proxy_info->err_code = aimbs_get16(&bs_body); - } else { - gaim_debug_warning("oscar","error reading rv proxy error packet\n"); - aim_conn_close(conn); - free(proxy_info); - proxy_info = NULL; - } - } else { - gaim_debug_warning("oscar","invalid length for proxy error packet\n"); - free(proxy_info); - proxy_info = NULL; - } - } else if(proxy_info->cmd_type == AIM_RV_PROXY_ACK) { - if(len == AIM_RV_PROXY_ACK_LEN - 2) { - body_len = AIM_RV_PROXY_ACK_LEN - AIM_RV_PROXY_HDR_LEN; - body_buf = malloc(body_len); - aim_bstream_init(&bs_body, body_buf, body_len); - if (aim_bstream_recv(&bs_body, conn->fd, body_len) == body_len) { - int i; - aim_bstream_rewind(&bs_body); - proxy_info->port = aimbs_get16(&bs_body); - for(i=0; i<4; i++) - ip_temp[i] = aimbs_get8(&bs_body); - snprintf(str_ip, sizeof(str_ip), "%hhu.%hhu.%hhu.%hhu", - ip_temp[0], ip_temp[1], - ip_temp[2], ip_temp[3]); - proxy_info->ip = strdup(str_ip); - } else { - gaim_debug_warning("oscar","error reading rv proxy error packet\n"); - aim_conn_close(conn); - free(proxy_info); - proxy_info = NULL; - } - } else { - gaim_debug_warning("oscar","invalid length for proxy error packet\n"); - free(proxy_info); - proxy_info = NULL; - } - } else { - gaim_debug_warning("oscar","unknown type for aim rendezvous proxy packet\n"); - } - } else { - gaim_debug_warning("oscar","error reading header of rv proxy packet\n"); - aim_conn_close(conn); - free(proxy_info); - proxy_info = NULL; - } - if(body_buf) { - free(body_buf); - body_buf = NULL; - } - return proxy_info; -} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/icq.c --- a/src/protocols/oscar/icq.c Sat Feb 11 19:16:38 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,696 +0,0 @@ -/* - * Family 0x0015 - Encapsulated ICQ. - * - */ - -#define FAIM_INTERNAL -#include - -faim_export int aim_icq_reqofflinemsgs(aim_session_t *sess) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - int bslen; - - if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) - return -EINVAL; - - bslen = 2 + 4 + 2 + 2; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); - - /* For simplicity, don't bother using a tlvlist */ - aimbs_put16(&fr->data, 0x0001); - aimbs_put16(&fr->data, bslen); - - aimbs_putle16(&fr->data, bslen - 2); - aimbs_putle32(&fr->data, atoi(sess->sn)); - aimbs_putle16(&fr->data, 0x003c); /* I command thee. */ - aimbs_putle16(&fr->data, snacid); /* eh. */ - - aim_tx_enqueue(sess, fr); - - return 0; -} - -faim_export int aim_icq_ackofflinemsgs(aim_session_t *sess) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - int bslen; - - if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) - return -EINVAL; - - bslen = 2 + 4 + 2 + 2; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); - - /* For simplicity, don't bother using a tlvlist */ - aimbs_put16(&fr->data, 0x0001); - aimbs_put16(&fr->data, bslen); - - aimbs_putle16(&fr->data, bslen - 2); - aimbs_putle32(&fr->data, atoi(sess->sn)); - aimbs_putle16(&fr->data, 0x003e); /* I command thee. */ - aimbs_putle16(&fr->data, snacid); /* eh. */ - - aim_tx_enqueue(sess, fr); - - return 0; -} - -faim_export int -aim_icq_setsecurity(aim_session_t *sess, gboolean auth_required, gboolean webaware) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - int bslen; - - if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) - return -EINVAL; - - bslen = 2+4+2+2+2+2+2+1+1+1+1+1+1; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); - - /* For simplicity, don't bother using a tlvlist */ - aimbs_put16(&fr->data, 0x0001); - aimbs_put16(&fr->data, bslen); - - aimbs_putle16(&fr->data, bslen - 2); - aimbs_putle32(&fr->data, atoi(sess->sn)); - aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */ - aimbs_putle16(&fr->data, snacid); /* eh. */ - aimbs_putle16(&fr->data, 0x0c3a); /* shrug. */ - aimbs_putle16(&fr->data, 0x030c); - aimbs_putle16(&fr->data, 0x0001); - aimbs_putle8(&fr->data, webaware); - aimbs_putle8(&fr->data, 0xf8); - aimbs_putle8(&fr->data, 0x02); - aimbs_putle8(&fr->data, 0x01); - aimbs_putle8(&fr->data, 0x00); - aimbs_putle8(&fr->data, !auth_required); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/** - * Change your ICQ password. - * - * @param sess The oscar session - * @param passwd The new password. If this is longer than 8 characters it - * will be truncated. - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_icq_changepasswd(aim_session_t *sess, const char *passwd) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - int bslen, passwdlen; - - if (!passwd) - return -EINVAL; - - if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) - return -EINVAL; - - passwdlen = strlen(passwd); - if (passwdlen > MAXICQPASSLEN) - passwdlen = MAXICQPASSLEN; - bslen = 2+4+2+2+2+2+passwdlen+1; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); - - /* For simplicity, don't bother using a tlvlist */ - aimbs_put16(&fr->data, 0x0001); - aimbs_put16(&fr->data, bslen); - - aimbs_putle16(&fr->data, bslen - 2); - aimbs_putle32(&fr->data, atoi(sess->sn)); - aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */ - aimbs_putle16(&fr->data, snacid); /* eh. */ - aimbs_putle16(&fr->data, 0x042e); /* shrug. */ - aimbs_putle16(&fr->data, passwdlen+1); - aimbs_putstr(&fr->data, passwd); - aimbs_putle8(&fr->data, '\0'); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -faim_export int aim_icq_getallinfo(aim_session_t *sess, const char *uin) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - int bslen; - struct aim_icq_info *info; - - if (!uin || uin[0] < '0' || uin[0] > '9') - return -EINVAL; - - if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) - return -EINVAL; - - bslen = 2 + 4 + 2 + 2 + 2 + 4; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); - - /* For simplicity, don't bother using a tlvlist */ - aimbs_put16(&fr->data, 0x0001); - aimbs_put16(&fr->data, bslen); - - aimbs_putle16(&fr->data, bslen - 2); - aimbs_putle32(&fr->data, atoi(sess->sn)); - aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */ - aimbs_putle16(&fr->data, snacid); /* eh. */ - aimbs_putle16(&fr->data, 0x04b2); /* shrug. */ - aimbs_putle32(&fr->data, atoi(uin)); - - aim_tx_enqueue(sess, fr); - - /* Keep track of this request and the ICQ number and request ID */ - info = (struct aim_icq_info *)calloc(1, sizeof(struct aim_icq_info)); - info->reqid = snacid; - info->uin = atoi(uin); - info->next = sess->icq_info; - sess->icq_info = info; - - return 0; -} - -faim_export int aim_icq_getalias(aim_session_t *sess, const char *uin) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - int bslen; - struct aim_icq_info *info; - - if (!uin || uin[0] < '0' || uin[0] > '9') - return -EINVAL; - - if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) - return -EINVAL; - - bslen = 2 + 4 + 2 + 2 + 2 + 4; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); - - /* For simplicity, don't bother using a tlvlist */ - aimbs_put16(&fr->data, 0x0001); - aimbs_put16(&fr->data, bslen); - - aimbs_putle16(&fr->data, bslen - 2); - aimbs_putle32(&fr->data, atoi(sess->sn)); - aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */ - aimbs_putle16(&fr->data, snacid); /* eh. */ - aimbs_putle16(&fr->data, 0x04ba); /* shrug. */ - aimbs_putle32(&fr->data, atoi(uin)); - - aim_tx_enqueue(sess, fr); - - /* Keep track of this request and the ICQ number and request ID */ - info = (struct aim_icq_info *)calloc(1, sizeof(struct aim_icq_info)); - info->reqid = snacid; - info->uin = atoi(uin); - info->next = sess->icq_info; - sess->icq_info = info; - - return 0; -} - -faim_export int aim_icq_getsimpleinfo(aim_session_t *sess, const char *uin) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - int bslen; - - if (!uin || uin[0] < '0' || uin[0] > '9') - return -EINVAL; - - if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) - return -EINVAL; - - bslen = 2 + 4 + 2 + 2 + 2 + 4; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); - - /* For simplicity, don't bother using a tlvlist */ - aimbs_put16(&fr->data, 0x0001); - aimbs_put16(&fr->data, bslen); - - aimbs_putle16(&fr->data, bslen - 2); - aimbs_putle32(&fr->data, atoi(sess->sn)); - aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */ - aimbs_putle16(&fr->data, snacid); /* eh. */ - aimbs_putle16(&fr->data, 0x051f); /* shrug. */ - aimbs_putle32(&fr->data, atoi(uin)); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -#if 0 -faim_export int aim_icq_sendxmlreq(aim_session_t *sess, const char *xml) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - int bslen; - - if (!xml || !strlen(xml)) - return -EINVAL; - - if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) - return -EINVAL; - - bslen = 2 + 10 + 2 + strlen(xml) + 1; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); - - /* For simplicity, don't bother using a tlvlist */ - aimbs_put16(&fr->data, 0x0001); - aimbs_put16(&fr->data, bslen); - - aimbs_putle16(&fr->data, bslen - 2); - aimbs_putle32(&fr->data, atoi(sess->sn)); - aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */ - aimbs_putle16(&fr->data, snacid); /* eh. */ - aimbs_putle16(&fr->data, 0x0998); /* shrug. */ - aimbs_putle16(&fr->data, strlen(xml) + 1); - aimbs_putraw(&fr->data, (fu8_t *)xml, strlen(xml) + 1); - - aim_tx_enqueue(sess, fr); - - return 0; -} -#endif - -#if 0 -/* - * Send an SMS message. This is the non-US way. The US-way is to IM - * their cell phone number (+19195551234). - * - * We basically construct and send an XML message. The format is: - * - * full_phone_without_leading_+ - * message - * 1252 - * self_uin - * self_name - * Yes|No - * - * - * - * Yeah hi Peter, whaaaat's happening. If there's any way to use - * a codepage other than 1252 that would be great. Thaaaanks. - */ -faim_export int aim_icq_sendsms(aim_session_t *sess, const char *name, const char *msg, const char *alias) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - int bslen, xmllen; - char *xml; - const char *timestr; - time_t t; - struct tm *tm; - - if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) - return -EINVAL; - - if (!name || !msg || !alias) - return -EINVAL; - - time(&t); - tm = gmtime(&t); - timestr = gaim_utf8_strftime("%a, %d %b %Y %T %Z", tm); - - /* The length of xml included the null terminating character */ - xmllen = 225 + strlen(name) + strlen(msg) + strlen(sess->sn) + strlen(alias) + strlen(timestr) + 1; - - if (!(xml = (char *)malloc(xmllen*sizeof(char)))) - return -ENOMEM; - snprintf(xml, xmllen, "\n" - "\t%s\n" - "\t%s\n" - "\t1252\n" - "\t%s\n" - "\t%s\n" - "\tYes\n" - "\t\n" - "\n", - name, msg, sess->sn, alias, timestr); - - bslen = 37 + xmllen; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) { - free(xml); - return -ENOMEM; - } - - snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); - - /* For simplicity, don't bother using a tlvlist */ - aimbs_put16(&fr->data, 0x0001); - aimbs_put16(&fr->data, bslen); - - aimbs_putle16(&fr->data, bslen - 2); - aimbs_putle32(&fr->data, atoi(sess->sn)); - aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */ - aimbs_putle16(&fr->data, snacid); /* eh. */ - - /* From libicq200-0.3.2/src/SNAC-SRV.cpp */ - aimbs_putle16(&fr->data, 0x8214); - aimbs_put16(&fr->data, 0x0001); - aimbs_put16(&fr->data, 0x0016); - aimbs_put32(&fr->data, 0x00000000); - aimbs_put32(&fr->data, 0x00000000); - aimbs_put32(&fr->data, 0x00000000); - aimbs_put32(&fr->data, 0x00000000); - - aimbs_put16(&fr->data, 0x0000); - aimbs_put16(&fr->data, xmllen); - aimbs_putstr(&fr->data, xml); - - aim_tx_enqueue(sess, fr); - - free(xml); - - return 0; -} -#endif - -static void aim_icq_freeinfo(struct aim_icq_info *info) { - int i; - - if (!info) - return; - free(info->nick); - free(info->first); - free(info->last); - free(info->email); - free(info->homecity); - free(info->homestate); - free(info->homephone); - free(info->homefax); - free(info->homeaddr); - free(info->mobile); - free(info->homezip); - free(info->personalwebpage); - if (info->email2) - for (i = 0; i < info->numaddresses; i++) - free(info->email2[i]); - free(info->email2); - free(info->workcity); - free(info->workstate); - free(info->workphone); - free(info->workfax); - free(info->workaddr); - free(info->workzip); - free(info->workcompany); - free(info->workdivision); - free(info->workposition); - free(info->workwebpage); - free(info->info); - free(info); -} - -/** - * Subtype 0x0003 - Response to 0x0015/0x002, contains an ICQesque 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_tlvlist_t *tl; - aim_tlv_t *datatlv; - aim_bstream_t qbs; - fu32_t ouruin; - fu16_t cmdlen, cmd, reqid; - - if (!(tl = aim_tlvlist_read(bs)) || !(datatlv = aim_tlv_gettlv(tl, 0x0001, 1))) { - aim_tlvlist_free(&tl); - gaim_debug_misc("oscar", "corrupt ICQ response\n"); - return 0; - } - - aim_bstream_init(&qbs, datatlv->value, datatlv->length); - - cmdlen = aimbs_getle16(&qbs); - ouruin = aimbs_getle32(&qbs); - cmd = aimbs_getle16(&qbs); - reqid = aimbs_getle16(&qbs); - - gaim_debug_misc("oscar", "icq response: %d bytes, %ld, 0x%04x, 0x%04x\n", cmdlen, ouruin, cmd, reqid); - - if (cmd == 0x0041) { /* offline message */ - struct aim_icq_offlinemsg msg; - aim_rxcallback_t userfunc; - - memset(&msg, 0, sizeof(msg)); - - msg.sender = aimbs_getle32(&qbs); - msg.year = aimbs_getle16(&qbs); - msg.month = aimbs_getle8(&qbs); - msg.day = aimbs_getle8(&qbs); - msg.hour = aimbs_getle8(&qbs); - msg.minute = aimbs_getle8(&qbs); - msg.type = aimbs_getle8(&qbs); - msg.flags = aimbs_getle8(&qbs); - msg.msglen = aimbs_getle16(&qbs); - msg.msg = aimbs_getstr(&qbs, msg.msglen); - - if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSG))) - ret = userfunc(sess, rx, &msg); - - free(msg.msg); - - } else if (cmd == 0x0042) { - aim_rxcallback_t userfunc; - - if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSGCOMPLETE))) - ret = userfunc(sess, rx); - - } else if (cmd == 0x07da) { /* information */ - fu16_t subtype; - struct aim_icq_info *info; - aim_rxcallback_t userfunc; - - subtype = aimbs_getle16(&qbs); - aim_bstream_advance(&qbs, 1); /* 0x0a */ - - /* find other data from the same request */ - for (info = sess->icq_info; info && (info->reqid != reqid); info = info->next); - if (!info) { - info = (struct aim_icq_info *)calloc(1, sizeof(struct aim_icq_info)); - info->reqid = reqid; - info->next = sess->icq_info; - sess->icq_info = info; - } - - switch (subtype) { - case 0x00a0: { /* hide ip status */ - /* nothing */ - } break; - - case 0x00aa: { /* password change status */ - /* nothing */ - } break; - - case 0x00c8: { /* general and "home" information */ - info->nick = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); - info->first = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); - info->last = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); - info->email = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); - info->homecity = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); - info->homestate = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); - info->homephone = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); - info->homefax = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); - info->homeaddr = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); - info->mobile = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); - info->homezip = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); - info->homecountry = aimbs_getle16(&qbs); - /* 0x0a 00 02 00 */ - /* 1 byte timezone? */ - /* 1 byte hide email flag? */ - } break; - - case 0x00dc: { /* personal information */ - info->age = aimbs_getle8(&qbs); - info->unknown = aimbs_getle8(&qbs); - info->gender = aimbs_getle8(&qbs); /* Not specified=0x00, Female=0x01, Male=0x02 */ - info->personalwebpage = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); - info->birthyear = aimbs_getle16(&qbs); - info->birthmonth = aimbs_getle8(&qbs); - info->birthday = aimbs_getle8(&qbs); - info->language1 = aimbs_getle8(&qbs); - info->language2 = aimbs_getle8(&qbs); - info->language3 = aimbs_getle8(&qbs); - /* 0x00 00 01 00 00 01 00 00 00 00 00 */ - } break; - - case 0x00d2: { /* work information */ - info->workcity = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); - info->workstate = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); - info->workphone = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); - info->workfax = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); - info->workaddr = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); - info->workzip = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); - info->workcountry = aimbs_getle16(&qbs); - info->workcompany = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); - info->workdivision = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); - info->workposition = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); - aim_bstream_advance(&qbs, 2); /* 0x01 00 */ - info->workwebpage = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); - } break; - - case 0x00e6: { /* additional personal information */ - info->info = aimbs_getstr(&qbs, aimbs_getle16(&qbs)-1); - } break; - - case 0x00eb: { /* email address(es) */ - int i; - info->numaddresses = aimbs_getle16(&qbs); - info->email2 = (char **)calloc(info->numaddresses, sizeof(char *)); - for (i = 0; i < info->numaddresses; i++) { - info->email2[i] = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); - if (i+1 != info->numaddresses) - aim_bstream_advance(&qbs, 1); /* 0x00 */ - } - } break; - - case 0x00f0: { /* personal interests */ - } break; - - case 0x00fa: { /* past background and current organizations */ - } break; - - case 0x0104: { /* alias info */ - info->nick = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); - info->first = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); - info->last = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); - aim_bstream_advance(&qbs, aimbs_getle16(&qbs)); /* email address? */ - /* Then 0x00 02 00 */ - } break; - - case 0x010e: { /* unknown */ - /* 0x00 00 */ - } break; - - case 0x019a: { /* simple info */ - aim_bstream_advance(&qbs, 2); - info->uin = aimbs_getle32(&qbs); - info->nick = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); - info->first = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); - info->last = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); - info->email = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); - /* Then 0x00 02 00 00 00 00 00 */ - } break; - } /* End switch statement */ - - if (!(snac->flags & 0x0001)) { - if (subtype != 0x0104) - if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_INFO))) - ret = userfunc(sess, rx, info); - - if (info->uin && info->nick) - if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_ALIAS))) - ret = userfunc(sess, rx, info); - - if (sess->icq_info == info) { - sess->icq_info = info->next; - } else { - struct aim_icq_info *cur; - for (cur=sess->icq_info; (cur->next && (cur->next!=info)); cur=cur->next); - if (cur->next) - cur->next = cur->next->next; - } - aim_icq_freeinfo(info); - } - } - - aim_tlvlist_free(&tl); - - 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; -} - -static void icq_shutdown(aim_session_t *sess, aim_module_t *mod) -{ - struct aim_icq_info *del; - - while (sess->icq_info) { - del = sess->icq_info; - sess->icq_info = sess->icq_info->next; - aim_icq_freeinfo(del); - } - - return; -} - -faim_internal int icq_modfirst(aim_session_t *sess, aim_module_t *mod) -{ - - mod->family = 0x0015; - mod->version = 0x0001; - mod->toolid = 0x0110; - mod->toolversion = 0x047c; - mod->flags = 0; - strncpy(mod->name, "icq", sizeof(mod->name)); - mod->snachandler = snachandler; - mod->shutdown = icq_shutdown; - - return 0; -} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/im.c --- a/src/protocols/oscar/im.c Sat Feb 11 19:16:38 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2480 +0,0 @@ -/* - * Family 0x0004 - Routines for sending/receiving Instant Messages. - * - * Note the term ICBM (Inter-Client Basic Message) which blankets - * all types of generically routed through-server messages. Within - * the ICBM types (family 4), a channel is defined. Each channel - * represents a different type of message. Channel 1 is used for - * what would commonly be called an "instant message". Channel 2 - * is used for negotiating "rendezvous". These transactions end in - * something more complex happening, such as a chat invitation, or - * a file transfer. Channel 3 is used for chat messages (not in - * the same family as these channels). Channel 4 is used for - * various ICQ messages. Examples are normal messages, URLs, and - * old-style authorization. - * - * In addition to the channel, every ICBM contains a cookie. For - * standard IMs, these are only used for error messages. However, - * the more complex rendezvous messages make suitably more complex - * use of this field. - * - * TODO: Split this up into an im.c file an an icbm.c file. It - * will be beautiful, you'll see. - * - * Need to rename all the mpmsg messages to aim_im_bleh. - * - * Make sure aim_conn_findbygroup is used by all functions. - */ - -#define FAIM_INTERNAL -#include - -#ifdef _WIN32 -#include "win32dep.h" -#endif - -/** - * Add a standard ICBM header to the given bstream with the given - * information. - * - * @param bs The bstream to write the ICBM header to. - * @param c c is for cookie, and cookie is for me. - * @param channel The ICBM channel (1 through 4). - * @param sn Null-terminated scrizeen nizame. - * @return The number of bytes written. It's really not useful. - */ -static int aim_im_puticbm(aim_bstream_t *bs, const guchar *c, fu16_t channel, const char *sn) -{ - aimbs_putraw(bs, c, 8); - aimbs_put16(bs, channel); - aimbs_put8(bs, strlen(sn)); - aimbs_putstr(bs, sn); - return 8+2+1+strlen(sn); -} - -/* - * Extracted from aim_im_sendch2_sendfile_ask - * Generates a random ICBM cookie in a character array of length 8 - * and copies it into the variable passed as cookie - */ -faim_export void aim_icbm_makecookie(guchar *cookie) -{ - int i; - - /* Should be like "21CBF95" and null terminated */ - for (i = 0; i < 7; i++) - cookie[i] = 0x30 + ((guchar)rand() % 10); - cookie[7] = '\0'; -} - -/* - * Takes a msghdr (and a length) and returns a client type - * code. Note that this is *only a guess* and has a low likelihood - * of actually being accurate. - * - * Its based on experimental data, with the help of Eric Warmenhoven - * who seems to have collected a wide variety of different AIM clients. - * - * - * Heres the current collection: - * 0501 0003 0101 0101 01 AOL Mobile Communicator, WinAIM 1.0.414 - * 0501 0003 0101 0201 01 WinAIM 2.0.847, 2.1.1187, 3.0.1464, - * 4.3.2229, 4.4.2286 - * 0501 0004 0101 0102 0101 WinAIM 4.1.2010, libfaim (right here) - * 0501 0003 0101 02 WinAIM 5 - * 0501 0001 01 iChat x.x, mobile buddies - * 0501 0001 0101 01 AOL v6.0, CompuServe 2000 v6.0, any TOC client - * 0501 0002 0106 WinICQ 5.45.1.3777.85 - * - * Note that in this function, only the feature bytes are tested, since - * the rest will always be the same. - * - */ -faim_export fu16_t aim_im_fingerprint(const fu8_t *msghdr, int len) -{ - static const struct { - fu16_t clientid; - int len; - fu8_t data[10]; - } fingerprints[] = { - /* AOL Mobile Communicator, WinAIM 1.0.414 */ - { AIM_CLIENTTYPE_MC, - 3, {0x01, 0x01, 0x01}}, - - /* WinAIM 2.0.847, 2.1.1187, 3.0.1464, 4.3.2229, 4.4.2286 */ - { AIM_CLIENTTYPE_WINAIM, - 3, {0x01, 0x01, 0x02}}, - - /* WinAIM 4.1.2010, libfaim */ - { AIM_CLIENTTYPE_WINAIM41, - 4, {0x01, 0x01, 0x01, 0x02}}, - - /* AOL v6.0, CompuServe 2000 v6.0, any TOC client */ - { AIM_CLIENTTYPE_AOL_TOC, - 1, {0x01}}, - - { 0, 0, {0x00}} - }; - int i; - - if (!msghdr || (len <= 0)) - return AIM_CLIENTTYPE_UNKNOWN; - - for (i = 0; fingerprints[i].len; i++) { - if (fingerprints[i].len != len) - continue; - if (memcmp(fingerprints[i].data, msghdr, fingerprints[i].len) == 0) - return fingerprints[i].clientid; - } - - return AIM_CLIENTTYPE_UNKNOWN; -} - -/** - * Subtype 0x0002 - Set ICBM parameters. - * - * I definitely recommend sending this. If you don't, you'll be stuck - * with the rather unreasonable defaults. - * - */ -faim_export int aim_im_setparams(aim_session_t *sess, struct aim_icbmparameters *params) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - - if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) - return -EINVAL; - - if (!params) - return -EINVAL; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+16))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0004, 0x0002, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0004, 0x0002, 0x0000, snacid); - - /* This is read-only (see Parameter Reply). Must be set to zero here. */ - aimbs_put16(&fr->data, 0x0000); - - /* These are all read-write */ - aimbs_put32(&fr->data, params->flags); - aimbs_put16(&fr->data, params->maxmsglen); - aimbs_put16(&fr->data, params->maxsenderwarn); - aimbs_put16(&fr->data, params->maxrecverwarn); - aimbs_put32(&fr->data, params->minmsginterval); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/** - * Subtype 0x0004 - Request ICBM parameter information. - * - */ -faim_export int aim_im_reqparams(aim_session_t *sess) -{ - aim_conn_t *conn; - - if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) - return -EINVAL; - - return aim_genericreq_n_snacid(sess, conn, 0x0004, 0x0004); -} - -/** - * Subtype 0x0005 - Receive parameter information. - * - */ -static int aim_im_paraminfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) -{ - aim_rxcallback_t userfunc; - struct aim_icbmparameters params; - - params.maxchan = aimbs_get16(bs); - params.flags = aimbs_get32(bs); - params.maxmsglen = aimbs_get16(bs); - params.maxsenderwarn = aimbs_get16(bs); - params.maxrecverwarn = aimbs_get16(bs); - params.minmsginterval = aimbs_get32(bs); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - return userfunc(sess, rx, ¶ms); - - return 0; -} - -/** - * Subtype 0x0006 - Send an ICBM (instant message). - * - * - * Possible flags: - * 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). - * - * Generally, you should use the lowest encoding possible to send - * your message. If you only use basic punctuation and the generic - * Latin alphabet, use ASCII7 (no flags). If you happen to use non-ASCII7 - * characters, but they are all clearly defined in ISO-8859-1, then - * use that. Keep in mind that not all characters in the PC ASCII8 - * character set are defined in the ISO standard. For those cases (most - * notably when the (r) symbol is used), you must use the full UNICODE - * encoding for your message. In UNICODE mode, _all_ characters must - * occupy 16bits, including ones that are not special. (Remember that - * the first 128 UNICODE symbols are equivalent to ASCII7, however they - * must be prefixed with a zero high order byte.) - * - * I strongly discourage the use of UNICODE mode, mainly because none - * of the clients I use can parse those messages (and besides that, - * wchars are difficult and non-portable to handle in most UNIX environments). - * If you really need to include special characters, use the HTML UNICODE - * entities. These are of the form ߪ where 2026 is the hex - * representation of the UNICODE index (in this case, UNICODE - * "Horizontal Ellipsis", or 133 in in ASCII8). - * - * Implementation note: Since this is one of the most-used functions - * in all of libfaim, it is written with performance in mind. As such, - * it is not as clear as it could be in respect to how this message is - * supposed to be layed out. Most obviously, tlvlists should be used - * instead of writing out the bytes manually. - * - * XXX - more precise verification that we never send SNACs larger than 8192 - * XXX - check SNAC size for multipart - * - */ -faim_export int aim_im_sendch1_ext(aim_session_t *sess, struct aim_sendimext_args *args) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - guchar cookie[8]; - int msgtlvlen; - static const fu8_t deffeatures[] = { 0x01, 0x01, 0x01, 0x02 }; - - if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) - return -EINVAL; - - if (!args) - return -EINVAL; - - if (args->flags & AIM_IMFLAGS_MULTIPART) { - if (args->mpmsg->numparts == 0) - return -EINVAL; - } else { - if (!args->msg || (args->msglen <= 0)) - return -EINVAL; - - if (args->msglen >= MAXMSGLEN) - return -E2BIG; - } - - /* Painfully calculate the size of the message TLV */ - msgtlvlen = 1 + 1; /* 0501 */ - - if (args->flags & AIM_IMFLAGS_CUSTOMFEATURES) - msgtlvlen += 2 + args->featureslen; - else - msgtlvlen += 2 + sizeof(deffeatures); - - if (args->flags & AIM_IMFLAGS_MULTIPART) { - aim_mpmsg_section_t *sec; - - for (sec = args->mpmsg->parts; sec; sec = sec->next) { - msgtlvlen += 2 /* 0101 */ + 2 /* block len */; - msgtlvlen += 4 /* charset */ + sec->datalen; - } - - } else { - msgtlvlen += 2 /* 0101 */ + 2 /* block len */; - msgtlvlen += 4 /* charset */ + args->msglen; - } - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, msgtlvlen+128))) - return -ENOMEM; - - /* XXX - should be optional */ - snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, args->destsn, strlen(args->destsn)+1); - aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); - - /* Generate an ICBM cookie */ - aim_icbm_makecookie(cookie); - - /* ICBM header */ - aim_im_puticbm(&fr->data, cookie, 0x0001, args->destsn); - - /* Message TLV (type 0x0002) */ - aimbs_put16(&fr->data, 0x0002); - aimbs_put16(&fr->data, msgtlvlen); - - /* Features TLV (type 0x0501) */ - aimbs_put16(&fr->data, 0x0501); - if (args->flags & AIM_IMFLAGS_CUSTOMFEATURES) { - aimbs_put16(&fr->data, args->featureslen); - aimbs_putraw(&fr->data, args->features, args->featureslen); - } else { - aimbs_put16(&fr->data, sizeof(deffeatures)); - aimbs_putraw(&fr->data, deffeatures, sizeof(deffeatures)); - } - - if (args->flags & AIM_IMFLAGS_MULTIPART) { - aim_mpmsg_section_t *sec; - - /* Insert each message part in a TLV (type 0x0101) */ - for (sec = args->mpmsg->parts; sec; sec = sec->next) { - aimbs_put16(&fr->data, 0x0101); - aimbs_put16(&fr->data, sec->datalen + 4); - aimbs_put16(&fr->data, sec->charset); - aimbs_put16(&fr->data, sec->charsubset); - aimbs_putraw(&fr->data, (guchar *)sec->data, sec->datalen); - } - - } else { - - /* Insert message text in a TLV (type 0x0101) */ - aimbs_put16(&fr->data, 0x0101); - - /* Message block length */ - aimbs_put16(&fr->data, args->msglen + 0x04); - - /* Character set */ - aimbs_put16(&fr->data, args->charset); - aimbs_put16(&fr->data, args->charsubset); - - /* Message. Not terminated */ - aimbs_putraw(&fr->data, (guchar *)args->msg, args->msglen); - } - - /* Set the Autoresponse flag */ - if (args->flags & AIM_IMFLAGS_AWAY) { - aimbs_put16(&fr->data, 0x0004); - aimbs_put16(&fr->data, 0x0000); - } else if (args->flags & AIM_IMFLAGS_ACK) { - /* Set the Request Acknowledge flag */ - aimbs_put16(&fr->data, 0x0003); - aimbs_put16(&fr->data, 0x0000); - } - - if (args->flags & AIM_IMFLAGS_OFFLINE) { - aimbs_put16(&fr->data, 0x0006); - aimbs_put16(&fr->data, 0x0000); - } - - /* - * Set the I HAVE A REALLY PURTY ICON flag. - * XXX - This should really only be sent on initial - * IMs and when you change your icon. - */ - if (args->flags & AIM_IMFLAGS_HASICON) { - aimbs_put16(&fr->data, 0x0008); - aimbs_put16(&fr->data, 0x000c); - aimbs_put32(&fr->data, args->iconlen); - aimbs_put16(&fr->data, 0x0001); - aimbs_put16(&fr->data, args->iconsum); - aimbs_put32(&fr->data, args->iconstamp); - } - - /* - * Set the Buddy Icon Requested flag. - * XXX - Every time? Surely not... - */ - if (args->flags & AIM_IMFLAGS_BUDDYREQ) { - aimbs_put16(&fr->data, 0x0009); - aimbs_put16(&fr->data, 0x0000); - } - - aim_tx_enqueue(sess, fr); - - /* clean out SNACs over 60sec old */ - aim_cleansnacs(sess, 60); - - return 0; -} - -/* - * Simple wrapper for aim_im_sendch1_ext() - * - * You cannot use aim_send_im if you need the HASICON flag. You must - * use aim_im_sendch1_ext directly for that. - * - * aim_send_im also cannot be used if you require UNICODE messages, because - * that requires an explicit message length. Use aim_im_sendch1_ext(). - * - */ -faim_export int aim_im_sendch1(aim_session_t *sess, const char *sn, fu16_t flags, const char *msg) -{ - struct aim_sendimext_args args; - - args.destsn = sn; - args.flags = flags; - args.msg = msg; - args.msglen = strlen(msg); - args.charset = 0x0000; - args.charsubset = 0x0000; - - /* Make these don't get set by accident -- they need aim_im_sendch1_ext */ - args.flags &= ~(AIM_IMFLAGS_CUSTOMFEATURES | AIM_IMFLAGS_HASICON | AIM_IMFLAGS_MULTIPART); - - return aim_im_sendch1_ext(sess, &args); -} - -/* - * Subtype 0x0006 - Send a chat invitation. - */ -faim_export int aim_im_sendch2_chatinvite(aim_session_t *sess, const char *sn, const char *msg, fu16_t exchange, const char *roomname, fu16_t instance) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - aim_msgcookie_t *msgcookie; - struct aim_invite_priv *priv; - guchar cookie[8]; - aim_tlvlist_t *otl = NULL, *itl = NULL; - fu8_t *hdr; - int hdrlen; - aim_bstream_t hdrbs; - - if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) - return -EINVAL; - - if (!sn || !msg || !roomname) - return -EINVAL; - - aim_icbm_makecookie(cookie); - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152+strlen(sn)+strlen(roomname)+strlen(msg)))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, sn, strlen(sn)+1); - aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); - - /* XXX should be uncached by an unwritten 'invite accept' handler */ - if ((priv = malloc(sizeof(struct aim_invite_priv)))) { - priv->sn = strdup(sn); - priv->roomname = strdup(roomname); - priv->exchange = exchange; - priv->instance = instance; - } - - if ((msgcookie = aim_mkcookie(cookie, AIM_COOKIETYPE_INVITE, priv))) - aim_cachecookie(sess, msgcookie); - else - free(priv); - - /* ICBM Header */ - aim_im_puticbm(&fr->data, cookie, 0x0002, sn); - - /* - * TLV t(0005) - * - * Everything else is inside this TLV. - * - * Sigh. AOL was rather inconsistent right here. So we have - * to play some minor tricks. Right inside the type 5 is some - * raw data, followed by a series of TLVs. - * - */ - hdrlen = 2+8+16+6+4+4+strlen(msg)+4+2+1+strlen(roomname)+2; - hdr = malloc(hdrlen); - aim_bstream_init(&hdrbs, hdr, hdrlen); - - aimbs_put16(&hdrbs, 0x0000); /* Unknown! */ - aimbs_putraw(&hdrbs, cookie, sizeof(cookie)); /* I think... */ - aimbs_putcaps(&hdrbs, AIM_CAPS_CHAT); - - aim_tlvlist_add_16(&itl, 0x000a, 0x0001); - aim_tlvlist_add_noval(&itl, 0x000f); - aim_tlvlist_add_str(&itl, 0x000c, msg); - aim_tlvlist_add_chatroom(&itl, 0x2711, exchange, roomname, instance); - aim_tlvlist_write(&hdrbs, &itl); - - aim_tlvlist_add_raw(&otl, 0x0005, aim_bstream_curpos(&hdrbs), hdr); - - aim_tlvlist_write(&fr->data, &otl); - - free(hdr); - aim_tlvlist_free(&itl); - aim_tlvlist_free(&otl); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/** - * Subtype 0x0006 - Send your icon to a given user. - * - * This is also performance sensitive. (If you can believe it...) - * - */ -faim_export int aim_im_sendch2_icon(aim_session_t *sess, const char *sn, const fu8_t *icon, int iconlen, time_t stamp, fu16_t iconsum) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - guchar cookie[8]; - - if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) - return -EINVAL; - - if (!sn || !icon || (iconlen <= 0) || (iconlen >= MAXICONLEN)) - return -EINVAL; - - aim_icbm_makecookie(cookie); - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sn)+2+2+2+8+16+2+2+2+2+2+2+2+4+4+4+iconlen+strlen(AIM_ICONIDENT)+2+2))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); - - /* ICBM header */ - aim_im_puticbm(&fr->data, cookie, 0x0002, sn); - - /* - * TLV t(0005) - * - * Encompasses everything below. - */ - aimbs_put16(&fr->data, 0x0005); - aimbs_put16(&fr->data, 2+8+16+6+4+4+iconlen+4+4+4+strlen(AIM_ICONIDENT)); - - aimbs_put16(&fr->data, 0x0000); - aimbs_putraw(&fr->data, cookie, 8); - aimbs_putcaps(&fr->data, AIM_CAPS_BUDDYICON); - - /* TLV t(000a) */ - aimbs_put16(&fr->data, 0x000a); - aimbs_put16(&fr->data, 0x0002); - aimbs_put16(&fr->data, 0x0001); - - /* TLV t(000f) */ - aimbs_put16(&fr->data, 0x000f); - aimbs_put16(&fr->data, 0x0000); - - /* TLV t(2711) */ - aimbs_put16(&fr->data, 0x2711); - aimbs_put16(&fr->data, 4+4+4+iconlen+strlen(AIM_ICONIDENT)); - aimbs_put16(&fr->data, 0x0000); - aimbs_put16(&fr->data, iconsum); - aimbs_put32(&fr->data, iconlen); - aimbs_put32(&fr->data, stamp); - aimbs_putraw(&fr->data, icon, iconlen); - aimbs_putstr(&fr->data, AIM_ICONIDENT); - - /* TLV t(0003) */ - aimbs_put16(&fr->data, 0x0003); - aimbs_put16(&fr->data, 0x0000); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * Subtype 0x0006 - Send a rich text message. - * - * This only works for ICQ 2001b (thats 2001 not 2000). Better, only - * send it to clients advertising the RTF capability. In fact, if you send - * it to a client that doesn't support that capability, the server will gladly - * bounce it back to you. - * - * You'd think this would be in icq.c, but, well, I'm trying to stick with - * the one-group-per-file scheme as much as possible. This could easily - * be an exception, since Rendezvous IMs are external of the Oscar core, - * and therefore are undefined. Really I just need to think of a good way to - * make an interface similar to what AOL actually uses. But I'm not using COM. - * - */ -faim_export int aim_im_sendch2_rtfmsg(aim_session_t *sess, struct aim_sendrtfmsg_args *args) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - guchar cookie[8]; - const char rtfcap[] = {"{97B12751-243C-4334-AD22-D6ABF73F1492}"}; /* AIM_CAPS_ICQRTF capability in string form */ - int servdatalen; - - if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) - return -EINVAL; - - if (!args || !args->destsn || !args->rtfmsg) - return -EINVAL; - - servdatalen = 2+2+16+2+4+1+2 + 2+2+4+4+4 + 2+4+2+strlen(args->rtfmsg)+1 + 4+4+4+strlen(rtfcap)+1; - - aim_icbm_makecookie(cookie); - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+128+servdatalen))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); - - /* ICBM header */ - aim_im_puticbm(&fr->data, cookie, 0x0002, args->destsn); - - /* TLV t(0005) - Encompasses everything below. */ - aimbs_put16(&fr->data, 0x0005); - aimbs_put16(&fr->data, 2+8+16 + 2+2+2 + 2+2 + 2+2+servdatalen); - - aimbs_put16(&fr->data, 0x0000); - aimbs_putraw(&fr->data, cookie, 8); - aimbs_putcaps(&fr->data, AIM_CAPS_ICQSERVERRELAY); - - /* t(000a) l(0002) v(0001) */ - aimbs_put16(&fr->data, 0x000a); - aimbs_put16(&fr->data, 0x0002); - aimbs_put16(&fr->data, 0x0001); - - /* t(000f) l(0000) v() */ - aimbs_put16(&fr->data, 0x000f); - aimbs_put16(&fr->data, 0x0000); - - /* Service Data TLV */ - aimbs_put16(&fr->data, 0x2711); - aimbs_put16(&fr->data, servdatalen); - - aimbs_putle16(&fr->data, 11 + 16 /* 11 + (sizeof CLSID) */); - aimbs_putle16(&fr->data, 9); - aimbs_putcaps(&fr->data, AIM_CAPS_EMPTY); - aimbs_putle16(&fr->data, 0); - aimbs_putle32(&fr->data, 0); - aimbs_putle8(&fr->data, 0); - aimbs_putle16(&fr->data, 0x03ea); /* trid1 */ - - aimbs_putle16(&fr->data, 14); - aimbs_putle16(&fr->data, 0x03eb); /* trid2 */ - aimbs_putle32(&fr->data, 0); - aimbs_putle32(&fr->data, 0); - aimbs_putle32(&fr->data, 0); - - aimbs_putle16(&fr->data, 0x0001); - aimbs_putle32(&fr->data, 0); - aimbs_putle16(&fr->data, strlen(args->rtfmsg)+1); - aimbs_putraw(&fr->data, (const fu8_t *)args->rtfmsg, strlen(args->rtfmsg)+1); - - aimbs_putle32(&fr->data, args->fgcolor); - aimbs_putle32(&fr->data, args->bgcolor); - aimbs_putle32(&fr->data, strlen(rtfcap)+1); - aimbs_putraw(&fr->data, (const fu8_t *)rtfcap, strlen(rtfcap)+1); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/** - * Subtype 0x0006 - Send an "I want to directly connect to you" message - * - */ -faim_export int aim_im_sendch2_odcrequest(aim_session_t *sess, guchar *usercookie, gboolean usecookie, const char *sn, const fu8_t *ip, fu16_t port) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - guchar cookie[8]; - aim_tlvlist_t *tl = NULL, *itl = NULL; - int hdrlen; - fu8_t *hdr; - aim_bstream_t hdrbs; - - if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) - return -EINVAL; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 256+strlen(sn)))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); - - /* - * Generate a random message cookie - * - * This cookie needs to be alphanumeric and NULL-terminated to be - * TOC-compatible. - * - * XXX - have I mentioned these should be generated in msgcookie.c? - * - */ - - if (usercookie && usecookie) /* allow user-specified cookie */ - memcpy(cookie, usercookie, 8); - else - aim_icbm_makecookie(cookie); - cookie[7] = '\0'; - - if (usercookie && !usecookie) - memcpy(cookie, usercookie, 8); - - /* ICBM header */ - aim_im_puticbm(&fr->data, cookie, 0x0002, sn); - - aim_tlvlist_add_noval(&tl, 0x0003); - - hdrlen = 2+8+16+6+8+6+4; - hdr = malloc(hdrlen); - aim_bstream_init(&hdrbs, hdr, hdrlen); - - aimbs_put16(&hdrbs, 0x0000); - aimbs_putraw(&hdrbs, cookie, 8); - aimbs_putcaps(&hdrbs, AIM_CAPS_DIRECTIM); - - aim_tlvlist_add_16(&itl, 0x000a, 0x0001); - aim_tlvlist_add_raw(&itl, 0x0003, 4, ip); - aim_tlvlist_add_16(&itl, 0x0005, port); - aim_tlvlist_add_noval(&itl, 0x000f); - - aim_tlvlist_write(&hdrbs, &itl); - - aim_tlvlist_add_raw(&tl, 0x0005, aim_bstream_curpos(&hdrbs), hdr); - - aim_tlvlist_write(&fr->data, &tl); - - free(hdr); - aim_tlvlist_free(&itl); - aim_tlvlist_free(&tl); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/** - * Subtype 0x0006 - Send an "I want to send you this file" message - * - */ -faim_export int aim_im_sendch2_sendfile_ask(aim_session_t *sess, struct aim_oft_info *oft_info) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - aim_tlvlist_t *tl=NULL, *subtl=NULL; - - if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !oft_info) - return -EINVAL; - - /* The cookie must already have been generated by this point */ - - { /* Create the subTLV chain */ - fu8_t *buf; - int buflen; - aim_bstream_t bs; - fu8_t ip[4]; - fu8_t ip_comp[4]; /* The bitwise complement of the ip */ - char *nexttoken; - int i; - - /* In a stage 2 proxied transfer & a transfer redirect, we send a second "reply request" - * Being the second request for this transfer, its request number is 2 - * You can fill in the blank for a stage 3's request number... */ - if( (oft_info->send_or_recv == AIM_XFER_RECV && oft_info->stage == AIM_XFER_PROXY_STG2) - || (oft_info->send_or_recv == AIM_XFER_RECV - && oft_info->stage == AIM_XFER_PROXY_STG3) - || oft_info->method == AIM_XFER_REDIR) - aim_tlvlist_add_16(&subtl, 0x000a, 0x0002); - else if(oft_info->send_or_recv == AIM_XFER_SEND && oft_info->stage == AIM_XFER_PROXY_STG3) - aim_tlvlist_add_16(&subtl, 0x000a, 0x0003); - else - aim_tlvlist_add_16(&subtl, 0x000a, 0x0001); - - /* This is usually necessary, but ruins a redirect and a stg3 proxy request */ - if(!(oft_info->send_or_recv == AIM_XFER_RECV - && (oft_info->method == AIM_XFER_REDIR || oft_info->stage == AIM_XFER_PROXY_STG3))) { - aim_tlvlist_add_noval(&subtl, 0x000f); - } - - /* If the following is ever enabled, ensure that it is not sent with a receive redirect - * or stage 3 proxy redirect for a file receive (same conditions for sending 0x000f above) */ -/* aim_tlvlist_add_raw(&subtl, 0x000e, 2, "en"); - aim_tlvlist_add_raw(&subtl, 0x000d, 8, "us-ascii"); - aim_tlvlist_add_raw(&subtl, 0x000c, 24, "Please accept this file."); */ - /* XXX - Change oft_info->clientip to an array of 4 bytes */ - if (oft_info->clientip) { - i = 0; - nexttoken = strtok(oft_info->clientip, "."); - while (nexttoken && i<4) { - ip[i] = atoi(nexttoken); - ip_comp[i] = ~ip[i]; - nexttoken = strtok(NULL, "."); - i++; - } - - /* If there is no proxyip, we must fill it in with the clientip */ - if(!oft_info->proxyip) { - aim_tlvlist_add_raw(&subtl, 0x0002, 4, ip); - aim_tlvlist_add_raw(&subtl, 0x0016, 4, ip_comp); /* check? value */ - } - - aim_tlvlist_add_raw(&subtl, 0x0003, 4, ip); - } - - /* Don't send the proxyip & accompanying info during a receive redirect or stg3 proxy request */ - if(!(oft_info->send_or_recv == AIM_XFER_RECV - && (oft_info->method == AIM_XFER_REDIR || oft_info->stage == AIM_XFER_PROXY_STG3))) { - if (oft_info->proxyip) { /* Generate the proxyip */ - i = 0; - nexttoken = strtok(oft_info->proxyip, "."); - while (nexttoken && i<4) { - ip[i] = atoi(nexttoken); - ip_comp[i] = ~ip[i]; - nexttoken = strtok(NULL, "."); - i++; - } - aim_tlvlist_add_raw(&subtl, 0x0002, 4, ip); - /* This zero-length TLV specifies a proxy will be used */ - aim_tlvlist_add_noval(&subtl, 0x0010); - - /* Proxied transfers fail without this next (check?) value */ - aim_tlvlist_add_raw(&subtl, 0x0016, 4, ip_comp); - } - } - - /* Don't send the port & its check during a stage 3 proxy request */ - if(!(oft_info->send_or_recv == AIM_XFER_RECV && oft_info->stage == AIM_XFER_PROXY_STG3)) { - aim_tlvlist_add_16(&subtl, 0x0005, oft_info->port); - - /* Check value? Bitwise complement of the port */ - aim_tlvlist_add_16(&subtl, 0x0017, ~(oft_info->port)); - } - - /* winAIM gets mad at us if we send too much info during a send redirect or stg3 proxy request */ - if(!(oft_info->send_or_recv == AIM_XFER_RECV - && (oft_info->method == AIM_XFER_REDIR || oft_info->stage == AIM_XFER_PROXY_STG3))) { - /* TLV t(2711) */ - buflen = 2+2+4+strlen(oft_info->fh.name)+1; - buf = malloc(buflen); - aim_bstream_init(&bs, buf, buflen); - aimbs_put16(&bs, (oft_info->fh.totfiles > 1) ? 0x0002 : 0x0001); - aimbs_put16(&bs, oft_info->fh.totfiles); - aimbs_put32(&bs, oft_info->fh.totsize); - - /* Filename - NULL terminated, for some odd reason */ - aimbs_putstr(&bs, oft_info->fh.name); - aimbs_put8(&bs, 0x00); - - aim_tlvlist_add_raw(&subtl, 0x2711, bs.len, bs.data); - free(buf); - } - } - - { /* Create the main TLV chain */ - fu8_t *buf; - int buflen; - aim_bstream_t bs; - - /* TLV t(0005) - Encompasses everything from above. Gee. */ - buflen = 2+8+16+aim_tlvlist_size(&subtl); - buf = malloc(buflen); - aim_bstream_init(&bs, buf, buflen); - aimbs_put16(&bs, AIM_RENDEZVOUS_PROPOSE); - aimbs_putraw(&bs, oft_info->cookie, 8); - aimbs_putcaps(&bs, AIM_CAPS_SENDFILE); - aim_tlvlist_write(&bs, &subtl); - aim_tlvlist_free(&subtl); - aim_tlvlist_add_raw(&tl, 0x0005, bs.len, bs.data); - free(buf); - - /* TLV t(0003) - Request an ack */ - aim_tlvlist_add_noval(&tl, 0x0003); - } - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 11+strlen(oft_info->sn) + aim_tlvlist_size(&tl)))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0004, 0x0006, AIM_SNACFLAGS_DESTRUCTOR, oft_info->cookie, sizeof(oft_info->cookie)); - aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); - - /* ICBM header */ - aim_im_puticbm(&fr->data, oft_info->cookie, 0x0002, oft_info->sn); - - /* All that crap from above (the 0x0005 TLV and the 0x0003 TLV) */ - aim_tlvlist_write(&fr->data, &tl); - aim_tlvlist_free(&tl); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/** - * Subtype 0x0006 - Send an "I will accept this file" message? - * - * @param rendid Capability type (AIM_CAPS_GETFILE or AIM_CAPS_SENDFILE) - */ -faim_export int aim_im_sendch2_sendfile_accept(aim_session_t *sess, struct aim_oft_info *oft_info) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - - if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !oft_info) - return -EINVAL; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 11+strlen(oft_info->sn) + 4+2+8+16))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); - - /* ICBM header */ - aim_im_puticbm(&fr->data, oft_info->cookie, 0x0002, oft_info->sn); - - aimbs_put16(&fr->data, 0x0005); - aimbs_put16(&fr->data, 0x001a); - aimbs_put16(&fr->data, AIM_RENDEZVOUS_ACCEPT); - aimbs_putraw(&fr->data, oft_info->cookie, 8); - aimbs_putcaps(&fr->data, AIM_CAPS_SENDFILE); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/** - * Subtype 0x0006 - Send a "cancel this file transfer" message? - * - */ -faim_export int aim_im_sendch2_sendfile_cancel(aim_session_t *sess, struct aim_oft_info *oft_info) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - - if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !oft_info) - return -EINVAL; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 11+strlen(oft_info->sn) + 4+2+8+16))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); - - /* ICBM header */ - aim_im_puticbm(&fr->data, oft_info->cookie, 0x0002, oft_info->sn); - - aimbs_put16(&fr->data, 0x0005); - aimbs_put16(&fr->data, 0x001a); - aimbs_put16(&fr->data, AIM_RENDEZVOUS_CANCEL); - aimbs_putraw(&fr->data, (const guchar *)oft_info->cookie, 8); - aimbs_putcaps(&fr->data, AIM_CAPS_SENDFILE); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/** - * Subtype 0x0006 - Request the status message of the given ICQ user. - * - * @param sess The oscar session. - * @param sn The UIN of the user of whom you wish to request info. - * @param type The type of info you wish to request. This should be the current - * state of the user, as one of the AIM_ICQ_STATE_* defines. - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_im_sendch2_geticqaway(aim_session_t *sess, const char *sn, int type) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - guchar cookie[8]; - - if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !sn) - return -EINVAL; - - aim_icbm_makecookie(cookie); - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sn) + 4+0x5e + 4))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); - - /* ICBM header */ - aim_im_puticbm(&fr->data, cookie, 0x0002, sn); - - /* TLV t(0005) - Encompasses almost everything below. */ - aimbs_put16(&fr->data, 0x0005); /* T */ - aimbs_put16(&fr->data, 0x005e); /* L */ - { /* V */ - aimbs_put16(&fr->data, 0x0000); - - /* Cookie */ - aimbs_putraw(&fr->data, cookie, 8); - - /* Put the 16 byte server relay capability */ - aimbs_putcaps(&fr->data, AIM_CAPS_ICQSERVERRELAY); - - /* TLV t(000a) */ - aimbs_put16(&fr->data, 0x000a); - aimbs_put16(&fr->data, 0x0002); - aimbs_put16(&fr->data, 0x0001); - - /* TLV t(000f) */ - aimbs_put16(&fr->data, 0x000f); - aimbs_put16(&fr->data, 0x0000); - - /* TLV t(2711) */ - aimbs_put16(&fr->data, 0x2711); - aimbs_put16(&fr->data, 0x0036); - { /* V */ - aimbs_putle16(&fr->data, 0x001b); /* L */ - aimbs_putle16(&fr->data, 0x0009); /* Protocol version */ - aimbs_putcaps(&fr->data, AIM_CAPS_EMPTY); - aimbs_putle16(&fr->data, 0x0000); /* Unknown */ - aimbs_putle16(&fr->data, 0x0001); /* Client features? */ - aimbs_putle16(&fr->data, 0x0000); /* Unknown */ - aimbs_putle8(&fr->data, 0x00); /* Unkizown */ - aimbs_putle16(&fr->data, 0xffff); /* Sequence number? XXX - This should decrement by 1 with each request */ - - aimbs_putle16(&fr->data, 0x000e); /* L */ - aimbs_putle16(&fr->data, 0xffff); /* Sequence number? XXX - This should decrement by 1 with each request */ - aimbs_putle32(&fr->data, 0x00000000); /* Unknown */ - aimbs_putle32(&fr->data, 0x00000000); /* Unknown */ - aimbs_putle32(&fr->data, 0x00000000); /* Unknown */ - - /* The type of status message being requested */ - if (type & AIM_ICQ_STATE_CHAT) - aimbs_putle16(&fr->data, 0x03ec); - else if(type & AIM_ICQ_STATE_DND) - aimbs_putle16(&fr->data, 0x03eb); - else if(type & AIM_ICQ_STATE_OUT) - aimbs_putle16(&fr->data, 0x03ea); - else if(type & AIM_ICQ_STATE_BUSY) - aimbs_putle16(&fr->data, 0x03e9); - else if(type & AIM_ICQ_STATE_AWAY) - aimbs_putle16(&fr->data, 0x03e8); - - aimbs_putle16(&fr->data, 0x0001); /* Status? */ - aimbs_putle16(&fr->data, 0x0001); /* Priority of this message? */ - aimbs_putle16(&fr->data, 0x0001); /* L */ - aimbs_putle8(&fr->data, 0x00); /* String of length L */ - } /* End TLV t(2711) */ - } /* End TLV t(0005) */ - - /* TLV t(0003) */ - aimbs_put16(&fr->data, 0x0003); - aimbs_put16(&fr->data, 0x0000); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/** - * Subtype 0x0006 - Send an ICQ-esque ICBM. - * - * This can be used to send an ICQ authorization reply (deny or grant). It is the "old way." - * The new way is to use SSI. I like the new way a lot better. This seems like such a hack, - * mostly because it's in network byte order. Figuring this stuff out sometimes takes a while, - * but thats ok, because it gives me time to try to figure out what kind of drugs the AOL people - * were taking when they merged the two protocols. - * - * @param sn The destination screen name. - * @param type The type of message. 0x0007 for authorization denied. 0x0008 for authorization granted. - * @param message The message you want to send, it should be null terminated. - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_im_sendch4(aim_session_t *sess, const char *sn, fu16_t type, const char *message) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - guchar cookie[8]; - - if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0002))) - return -EINVAL; - - if (!sn || !type || !message) - return -EINVAL; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+3+strlen(sn)+12+strlen(message)+1+4))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); - - aim_icbm_makecookie(cookie); - - /* ICBM header */ - aim_im_puticbm(&fr->data, cookie, 0x0004, sn); - - /* - * TLV t(0005) - * - * ICQ data (the UIN and the message). - */ - aimbs_put16(&fr->data, 0x0005); - aimbs_put16(&fr->data, 4 + 2+2+strlen(message)+1); - - /* - * Your UIN - */ - aimbs_putle32(&fr->data, atoi(sess->sn)); - - /* - * TLV t(type) l(strlen(message)+1) v(message+NULL) - */ - aimbs_putle16(&fr->data, type); - aimbs_putle16(&fr->data, strlen(message)+1); - aimbs_putraw(&fr->data, (const fu8_t *)message, strlen(message)+1); - - /* - * TLV t(0006) l(0000) v() - */ - aimbs_put16(&fr->data, 0x0006); - aimbs_put16(&fr->data, 0x0000); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * XXX - I don't see when this would ever get called... - */ -static int outgoingim(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; - guchar cookie[8]; - fu16_t channel; - aim_tlvlist_t *tlvlist; - char *sn; - int snlen; - fu16_t icbmflags = 0; - fu8_t flag1 = 0, flag2 = 0; - gchar *msg = NULL; - aim_tlv_t *msgblock; - - /* ICBM Cookie. */ - aim_icbm_makecookie(cookie); - - /* Channel ID */ - channel = aimbs_get16(bs); - - if (channel != 0x01) { - gaim_debug_misc("oscar", "icbm: ICBM recieved on unsupported channel. Ignoring. (chan = %04x)\n", channel); - return 0; - } - - snlen = aimbs_get8(bs); - sn = aimbs_getstr(bs, snlen); - - tlvlist = aim_tlvlist_read(bs); - - if (aim_tlv_gettlv(tlvlist, 0x0003, 1)) - icbmflags |= AIM_IMFLAGS_ACK; - if (aim_tlv_gettlv(tlvlist, 0x0004, 1)) - icbmflags |= AIM_IMFLAGS_AWAY; - - if ((msgblock = aim_tlv_gettlv(tlvlist, 0x0002, 1))) { - aim_bstream_t mbs; - int featurelen, msglen; - - aim_bstream_init(&mbs, msgblock->value, msgblock->length); - - aimbs_get8(&mbs); - aimbs_get8(&mbs); - for (featurelen = aimbs_get16(&mbs); featurelen; featurelen--) - aimbs_get8(&mbs); - aimbs_get8(&mbs); - aimbs_get8(&mbs); - - msglen = aimbs_get16(&mbs) - 4; /* final block length */ - - flag1 = aimbs_get16(&mbs); - flag2 = aimbs_get16(&mbs); - - msg = aimbs_getstr(&mbs, msglen); - } - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, channel, sn, msg, icbmflags, flag1, flag2); - - free(sn); - aim_tlvlist_free(&tlvlist); - - return ret; -} - -/* - * Ahh, the joys of nearly ridiculous over-engineering. - * - * Not only do AIM ICBM's support multiple channels. Not only do they - * support multiple character sets. But they support multiple character - * sets / encodings within the same ICBM. - * - * These multipart messages allow for complex space savings techniques, which - * seem utterly unnecessary by today's standards. In fact, there is only - * one client still in popular use that still uses this method: AOL for the - * Macintosh, Version 5.0. Obscure, yes, I know. - * - * In modern (non-"legacy") clients, if the user tries to send a character - * that is not ISO-8859-1 or ASCII, the client will send the entire message - * as UNICODE, meaning that every character in the message will occupy the - * full 16 bit UNICODE field, even if the high order byte would be zero. - * Multipart messages prevent this wasted space by allowing the client to - * only send the characters in UNICODE that need to be sent that way, and - * the rest of the message can be sent in whatever the native character - * set is (probably ASCII). - * - * An important note is that sections will be displayed in the order that - * they appear in the ICBM. There is no facility for merging or rearranging - * sections at run time. So if you have, say, ASCII then UNICODE then ASCII, - * you must supply two ASCII sections with a UNICODE in the middle, and incur - * the associated overhead. - * - * Normally I would have laughed and given a firm 'no' to supporting this - * seldom-used feature, but something is attracting me to it. In the future, - * it may be possible to abuse this to send mixed-media messages to other - * open source clients (like encryption or something) -- see faimtest for - * examples of how to do this. - * - * I would definitely recommend avoiding this feature unless you really - * know what you are doing, and/or you have something neat to do with it. - * - */ -faim_export int aim_mpmsg_init(aim_session_t *sess, aim_mpmsg_t *mpm) -{ - - memset(mpm, 0, sizeof(aim_mpmsg_t)); - - return 0; -} - -static int mpmsg_addsection(aim_session_t *sess, aim_mpmsg_t *mpm, fu16_t charset, fu16_t charsubset, gchar *data, fu16_t datalen) -{ - aim_mpmsg_section_t *sec; - - if (!(sec = malloc(sizeof(aim_mpmsg_section_t)))) - return -1; - - sec->charset = charset; - sec->charsubset = charsubset; - sec->data = data; - sec->datalen = datalen; - sec->next = NULL; - - if (!mpm->parts) - mpm->parts = sec; - else { - aim_mpmsg_section_t *cur; - - for (cur = mpm->parts; cur->next; cur = cur->next) - ; - cur->next = sec; - } - - mpm->numparts++; - - return 0; -} - -faim_export int aim_mpmsg_addraw(aim_session_t *sess, aim_mpmsg_t *mpm, fu16_t charset, fu16_t charsubset, const gchar *data, fu16_t datalen) -{ - gchar *dup; - - if (!(dup = malloc(datalen))) - return -1; - memcpy(dup, data, datalen); - - if (mpmsg_addsection(sess, mpm, charset, charsubset, dup, datalen) == -1) { - free(dup); - return -1; - } - - return 0; -} - -/* XXX - should provide a way of saying ISO-8859-1 specifically */ -faim_export int aim_mpmsg_addascii(aim_session_t *sess, aim_mpmsg_t *mpm, const char *ascii) -{ - gchar *dup; - - if (!(dup = strdup(ascii))) - return -1; - - if (mpmsg_addsection(sess, mpm, 0x0000, 0x0000, dup, strlen(ascii)) == -1) { - free(dup); - return -1; - } - - return 0; -} - -faim_export int aim_mpmsg_addunicode(aim_session_t *sess, aim_mpmsg_t *mpm, const fu16_t *unicode, fu16_t unicodelen) -{ - gchar *buf; - aim_bstream_t bs; - int i; - - if (!(buf = malloc(unicodelen * 2))) - return -1; - - aim_bstream_init(&bs, (guchar *)buf, unicodelen * 2); - - /* We assume unicode is in /host/ byte order -- convert to network */ - for (i = 0; i < unicodelen; i++) - aimbs_put16(&bs, unicode[i]); - - if (mpmsg_addsection(sess, mpm, 0x0002, 0x0000, buf, aim_bstream_curpos(&bs)) == -1) { - free(buf); - return -1; - } - - return 0; -} - -faim_export void aim_mpmsg_free(aim_session_t *sess, aim_mpmsg_t *mpm) -{ - aim_mpmsg_section_t *cur; - - for (cur = mpm->parts; cur; ) { - aim_mpmsg_section_t *tmp; - - tmp = cur->next; - free(cur->data); - free(cur); - cur = tmp; - } - - mpm->numparts = 0; - mpm->parts = NULL; - - return; -} - -/* - * Start by building the multipart structures, then pick the first - * human-readable section and stuff it into args->msg so no one gets - * suspicious. - * - */ -static int incomingim_ch1_parsemsgs(aim_session_t *sess, aim_userinfo_t *userinfo, fu8_t *data, int len, struct aim_incomingim_ch1_args *args) -{ - /* Should this be ASCII -> UNICODE -> Custom */ - static const fu16_t charsetpri[] = { - AIM_CHARSET_ASCII, /* ASCII first */ - AIM_CHARSET_CUSTOM, /* then ISO-8859-1 */ - AIM_CHARSET_UNICODE, /* UNICODE as last resort */ - }; - static const int charsetpricount = 3; - int i; - aim_bstream_t mbs; - aim_mpmsg_section_t *sec; - - aim_bstream_init(&mbs, data, len); - - while (aim_bstream_empty(&mbs)) { - fu16_t msglen, flag1, flag2; - gchar *msgbuf; - - aimbs_get8(&mbs); /* 01 */ - aimbs_get8(&mbs); /* 01 */ - - /* Message string length, including character set info. */ - msglen = aimbs_get16(&mbs); - if (msglen > aim_bstream_empty(&mbs)) - { - gaim_debug_misc("oscar", "Received an IM containing an invalid message part from %s. They are probably trying to do something malicious.", userinfo->sn); - break; - } - - /* Character set info */ - flag1 = aimbs_get16(&mbs); - flag2 = aimbs_get16(&mbs); - - /* Message. */ - msglen -= 4; - - /* - * For now, we don't care what the encoding is. Just copy - * it into a multipart struct and deal with it later. However, - * always pad the ending with a NULL. This makes it easier - * to treat ASCII sections as strings. It won't matter for - * UNICODE or binary data, as you should never read past - * the specified data length, which will not include the pad. - * - * XXX - There's an API bug here. For sending, the UNICODE is - * given in host byte order (aim_mpmsg_addunicode), but here - * the received messages are given in network byte order. - * - */ - msgbuf = (gchar *)aimbs_getraw(&mbs, msglen); - mpmsg_addsection(sess, &args->mpmsg, flag1, flag2, msgbuf, msglen); - - } /* while */ - - args->icbmflags |= AIM_IMFLAGS_MULTIPART; /* always set */ - - /* - * Clients that support multiparts should never use args->msg, as it - * will point to an arbitrary section. - * - * Here, we attempt to provide clients that do not support multipart - * messages with something to look at -- hopefully a human-readable - * string. But, failing that, a UNICODE message, or nothing at all. - * - * Which means that even if args->msg is NULL, it does not mean the - * message was blank. - * - */ - for (i = 0; i < charsetpricount; i++) { - for (sec = args->mpmsg.parts; sec; sec = sec->next) { - - if (sec->charset != charsetpri[i]) - continue; - - /* Great. We found one. Fill it in. */ - args->charset = sec->charset; - args->charsubset = sec->charsubset; - - /* Set up the simple flags */ - switch (args->charsubset) - { - case 0x0000: - /* standard subencoding? */ - break; - case 0x000b: - args->icbmflags |= AIM_IMFLAGS_SUBENC_MACINTOSH; - break; - case 0xffff: - /* no subencoding */ - break; - default: - break; - } - - args->msg = sec->data; - args->msglen = sec->datalen; - - return 0; - } - } - - /* No human-readable sections found. Oh well. */ - args->charset = args->charsubset = 0xffff; - args->msg = NULL; - args->msglen = 0; - - return 0; -} - -static int incomingim_ch1(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, fu16_t channel, aim_userinfo_t *userinfo, aim_bstream_t *bs, fu8_t *cookie) -{ - fu16_t type, length; - aim_rxcallback_t userfunc; - int ret = 0; - struct aim_incomingim_ch1_args args; - unsigned int endpos; - - memset(&args, 0, sizeof(args)); - - aim_mpmsg_init(sess, &args.mpmsg); - - /* - * This used to be done using tlvchains. For performance reasons, - * I've changed it to process the TLVs in-place. This avoids lots - * of per-IM memory allocations. - */ - while (aim_bstream_empty(bs)) - { - type = aimbs_get16(bs); - length = aimbs_get16(bs); - - if (length > aim_bstream_empty(bs)) - { - gaim_debug_misc("oscar", "Received an IM containing an invalid message part from %s. They are probably trying to do something malicious.\n", userinfo->sn); - break; - } - - endpos = aim_bstream_curpos(bs) + length; - - if (type == 0x0002) { /* Message Block */ - - /* - * This TLV consists of the following: - * - 0501 -- Unknown - * - Features: Don't know how to interpret these - * - 0101 -- Unknown - * - Message - * - */ - - aimbs_get8(bs); /* 05 */ - aimbs_get8(bs); /* 01 */ - - args.featureslen = aimbs_get16(bs); - if (args.featureslen > aim_bstream_empty(bs)) - { - gaim_debug_misc("oscar", "Received an IM containing an invalid message part from %s. They are probably trying to do something malicious.\n", userinfo->sn); - break; - } - if (args.featureslen == 0) - { - args.features = NULL; - } - else - { - args.features = aimbs_getraw(bs, args.featureslen); - args.icbmflags |= AIM_IMFLAGS_CUSTOMFEATURES; - } - - /* - * The rest of the TLV contains one or more message - * blocks... - */ - incomingim_ch1_parsemsgs(sess, userinfo, bs->data + bs->offset /* XXX evil!!! */, length - 2 - 2 - args.featureslen, &args); - - } else if (type == 0x0003) { /* Server Ack Requested */ - - args.icbmflags |= AIM_IMFLAGS_ACK; - - } 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 */ - - args.iconlen = aimbs_get32(bs); - aimbs_get16(bs); /* 0x0001 */ - args.iconsum = aimbs_get16(bs); - args.iconstamp = aimbs_get32(bs); - - /* - * This looks to be a client bug. MacAIM 4.3 will - * send this tag, but with all zero values, in the - * first message of a conversation. This makes no - * sense whatsoever, so I'm going to say its a bug. - * - * You really shouldn't advertise a zero-length icon - * anyway. - * - */ - if (args.iconlen) - args.icbmflags |= AIM_IMFLAGS_HASICON; - - } else if (type == 0x0009) { - - args.icbmflags |= AIM_IMFLAGS_BUDDYREQ; - - } else if (type == 0x000b) { /* Non-direct connect typing notification */ - - args.icbmflags |= AIM_IMFLAGS_TYPINGNOT; - - } else if (type == 0x0017) { - - free(args.extdata); - args.extdatalen = length; - if (args.extdatalen > aim_bstream_empty(bs)) - { - gaim_debug_misc("oscar", "Received an IM containing an invalid message part from %s. They are probably trying to do something malicious.\n", userinfo->sn); - break; - } - if (args.extdatalen == 0) - args.extdata = NULL; - else - args.extdata = aimbs_getraw(bs, args.extdatalen); - - } else { - gaim_debug_misc("oscar", "incomingim_ch1: unknown TLV 0x%04x (len %d)\n", type, length); - } - - /* - * This is here to protect ourselves from ourselves. That - * is, if something above doesn't completely parse its value - * section, or, worse, overparses it, this will set the - * stream where it needs to be in order to land on the next - * TLV when the loop continues. - * - */ - aim_bstream_setpos(bs, endpos); - } - - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, channel, userinfo, &args); - - aim_mpmsg_free(sess, &args.mpmsg); - free(args.features); - free(args.extdata); - - return ret; -} - -static void incomingim_ch2_buddylist(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_bstream_t *servdata) -{ - - /* - * This goes like this... - * - * group name length - * group name - * num of buddies in group - * buddy name length - * buddy name - * buddy name length - * buddy name - * ... - * group name length - * group name - * num of buddies in group - * buddy name length - * buddy name - * ... - * ... - */ - while (servdata && aim_bstream_empty(servdata)) { - fu16_t gnlen, numb; - int i; - char *gn; - - gnlen = aimbs_get16(servdata); - gn = aimbs_getstr(servdata, gnlen); - numb = aimbs_get16(servdata); - - for (i = 0; i < numb; i++) { - fu16_t bnlen; - char *bn; - - bnlen = aimbs_get16(servdata); - bn = aimbs_getstr(servdata, bnlen); - - gaim_debug_misc("oscar", "got a buddy list from %s: group %s, buddy %s\n", userinfo->sn, gn, bn); - - free(bn); - } - - free(gn); - } - - return; -} - -static void incomingim_ch2_buddyicon_free(aim_session_t *sess, struct aim_incomingim_ch2_args *args) -{ - - free(args->info.icon.icon); - - return; -} - -static void incomingim_ch2_buddyicon(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_bstream_t *servdata) -{ - - if (servdata) { - args->info.icon.checksum = aimbs_get32(servdata); - args->info.icon.length = aimbs_get32(servdata); - args->info.icon.timestamp = aimbs_get32(servdata); - args->info.icon.icon = aimbs_getraw(servdata, args->info.icon.length); - } - - args->destructor = (void *)incomingim_ch2_buddyicon_free; - - return; -} - -static void incomingim_ch2_chat_free(aim_session_t *sess, struct aim_incomingim_ch2_args *args) -{ - - /* XXX - aim_chat_roominfo_free() */ - free(args->info.chat.roominfo.name); - - return; -} - -static void incomingim_ch2_chat(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_bstream_t *servdata) -{ - - /* - * Chat room info. - */ - if (servdata) - aim_chat_readroominfo(servdata, &args->info.chat.roominfo); - - args->destructor = (void *)incomingim_ch2_chat_free; - - return; -} - -static void incomingim_ch2_icqserverrelay_free(aim_session_t *sess, struct aim_incomingim_ch2_args *args) -{ - - free((char *)args->info.rtfmsg.rtfmsg); - - return; -} - -/* - * The relationship between AIM_CAPS_ICQSERVERRELAY and AIM_CAPS_ICQRTF is - * kind of odd. This sends the client ICQRTF since that is all that I've seen - * SERVERRELAY used for. - * - * Note that this is all little-endian. Cringe. - * - */ -static void incomingim_ch2_icqserverrelay(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_bstream_t *servdata) -{ - fu16_t hdrlen, anslen, msglen; - - hdrlen = aimbs_getle16(servdata); - aim_bstream_advance(servdata, hdrlen); - - hdrlen = aimbs_getle16(servdata); - aim_bstream_advance(servdata, hdrlen); - - args->info.rtfmsg.msgtype = aimbs_getle16(servdata); - - anslen = aimbs_getle32(servdata); - aim_bstream_advance(servdata, anslen); - - msglen = aimbs_getle16(servdata); - args->info.rtfmsg.rtfmsg = aimbs_getstr(servdata, msglen); - - args->info.rtfmsg.fgcolor = aimbs_getle32(servdata); - args->info.rtfmsg.bgcolor = aimbs_getle32(servdata); - - hdrlen = aimbs_getle32(servdata); - aim_bstream_advance(servdata, hdrlen); - - args->destructor = (void *)incomingim_ch2_icqserverrelay_free; - - return; -} - -static void incomingim_ch2_sendfile_free(aim_session_t *sess, struct aim_incomingim_ch2_args *args) -{ - free(args->info.sendfile.filename); -} - -static void incomingim_ch2_sendfile(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_bstream_t *servdata) -{ - - args->destructor = (void *)incomingim_ch2_sendfile_free; - - /* Maybe there is a better way to tell what kind of sendfile - * this is? Maybe TLV t(000a)? */ - if (servdata) { /* Someone is sending us a file */ - int flen; - - /* subtype is one of AIM_OFT_SUBTYPE_* */ - args->info.sendfile.subtype = aimbs_get16(servdata); - args->info.sendfile.totfiles = aimbs_get16(servdata); - args->info.sendfile.totsize = aimbs_get32(servdata); - - /* - * I hope to God I'm right when I guess that there is a - * 32 char max filename length for single files. I think - * OFT tends to do that. Gotta love inconsistency. I saw - * a 26 byte filename? - */ - /* AAA - create an aimbs_getnullstr function (don't anymore)(maybe) */ - /* Use an inelegant way of getting the null-terminated filename, - * since there's no easy bstream routine. */ - for (flen = 0; aimbs_get8(servdata); flen++); - aim_bstream_advance(servdata, -flen -1); - args->info.sendfile.filename = aimbs_getstr(servdata, flen); - - /* There is sometimes more after the null-terminated filename, - * but I'm unsure of its format. */ - /* I don't believe him. */ - /* There is sometimes a null byte inside a unicode filename, - * but as far as I can tell the filename is the last - * piece of data that will be in this message. --Jonathan */ - } - - return; -} - -typedef void (*ch2_args_destructor_t)(aim_session_t *sess, struct aim_incomingim_ch2_args *args); - -static int incomingim_ch2(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, fu16_t channel, aim_userinfo_t *userinfo, aim_tlvlist_t *tlvlist, fu8_t *cookie) -{ - aim_rxcallback_t userfunc; - aim_tlv_t *block1, *servdatatlv; - aim_tlvlist_t *list2; - struct aim_incomingim_ch2_args args; - aim_bstream_t bbs, sdbs, *sdbsptr = NULL; - fu8_t *cookie2; - int ret = 0; - - char proxyip[30] = {""}; - char clientip[30] = {""}; - char verifiedip[30] = {""}; - - memset(&args, 0, sizeof(args)); - - /* - * There's another block of TLVs embedded in the type 5 here. - */ - block1 = aim_tlv_gettlv(tlvlist, 0x0005, 1); - aim_bstream_init(&bbs, block1->value, block1->length); - - /* - * First two bytes represent the status of the connection. - * - * 0 is a request, 1 is a cancel, 2 is an accept - */ - args.status = aimbs_get16(&bbs); - - /* - * Next comes the cookie. Should match the ICBM cookie. - */ - cookie2 = aimbs_getraw(&bbs, 8); - if (memcmp(cookie, cookie2, 8) != 0) - gaim_debug_misc("oscar", "rend: warning cookies don't match!\n"); - memcpy(args.cookie, cookie2, 8); - free(cookie2); - - /* - * The next 16bytes are a capability block so we can - * identify what type of rendezvous this is. - */ - args.reqclass = aim_locate_getcaps(sess, &bbs, 0x10); - - /* - * What follows may be TLVs or nothing, depending on the - * purpose of the message. - * - * Ack packets for instance have nothing more to them. - */ - list2 = aim_tlvlist_read(&bbs); - - /* - * IP address to proxy the file transfer through. - * - * XXX - I don't like this. Maybe just read in an int? Or inet_ntoa... - */ - if (aim_tlv_gettlv(list2, 0x0002, 1)) { - aim_tlv_t *iptlv; - - iptlv = aim_tlv_gettlv(list2, 0x0002, 1); - if (iptlv->length == 4) - snprintf(proxyip, sizeof(proxyip), "%hhu.%hhu.%hhu.%hhu", - iptlv->value[0], iptlv->value[1], - iptlv->value[2], iptlv->value[3]); - } - - /* - * IP address from the perspective of the client. - */ - if (aim_tlv_gettlv(list2, 0x0003, 1)) { - aim_tlv_t *iptlv; - - iptlv = aim_tlv_gettlv(list2, 0x0003, 1); - if (iptlv->length == 4) - snprintf(clientip, sizeof(clientip), "%hhu.%hhu.%hhu.%hhu", - iptlv->value[0], iptlv->value[1], - iptlv->value[2], iptlv->value[3]); - } - - /* - * Verified IP address (from the perspective of Oscar). - * - * This is added by the server. - */ - if (aim_tlv_gettlv(list2, 0x0004, 1)) { - aim_tlv_t *iptlv; - - iptlv = aim_tlv_gettlv(list2, 0x0004, 1); - if (iptlv->length == 4) - snprintf(verifiedip, sizeof(verifiedip), "%hhu.%hhu.%hhu.%hhu", - iptlv->value[0], iptlv->value[1], - iptlv->value[2], iptlv->value[3]); - } - - /* - * Port number for something. - */ - if (aim_tlv_gettlv(list2, 0x0005, 1)) - args.port = aim_tlv_get16(list2, 0x0005, 1); - - /* - * Something to do with ft? -- two bytes - * 0x0001 - "I want to send you this file" - * 0x0002 - "I will accept this file from you" - * 0x0002 - Also used in ICQ Lite Beta 4.0 URLs - */ - /* - * This is what I call the request number of the file transfer - * 0x0001 - Initial file transfer request for no proxy or stage 1 proxy - * 0x0002 - "Reply request" for a stage 2 proxy (receiver wants to use proxy) - * 0x0003 - A third request has been sent; applies only to stage 3 proxied transfers - * -- Jonathan - */ - if (aim_tlv_gettlv(list2, 0x000a, 1)) - args.info.sendfile.reqnum = aim_tlv_get16(list2, 0x000a, 1); - - /* - * Error code. - */ - if (aim_tlv_gettlv(list2, 0x000b, 1)) - args.errorcode = aim_tlv_get16(list2, 0x000b, 1); - - /* - * Invitation message / chat description. - */ - if (aim_tlv_gettlv(list2, 0x000c, 1)) { - args.msg = aim_tlv_getstr(list2, 0x000c, 1); - args.msglen = aim_tlv_getlength(list2, 0x000c, 1); - } - - /* - * Character set. - */ - if (aim_tlv_gettlv(list2, 0x000d, 1)) - args.encoding = aim_tlv_getstr(list2, 0x000d, 1); - - /* - * Language. - */ - if (aim_tlv_gettlv(list2, 0x000e, 1)) - args.language = aim_tlv_getstr(list2, 0x000e, 1); - -#if 0 - /* - * Unknown -- no value - * - * Maybe means we should connect directly to transfer the file? - * Also used in ICQ Lite Beta 4.0 URLs. Also empty. - */ - /* I don't think this indicates a direct transfer; this flag is - * also present in a stage 1 proxied file send request -- Jonathan */ - if (aim_tlv_gettlv(list2, 0x000f, 1)) { - /* Unhandled */ - } -#endif - - /* - * Flag meaning we should proxy the file transfer through an AIM server - */ - if (aim_tlv_gettlv(list2, 0x0010, 1)) - args.info.sendfile.use_proxy = TRUE; - else - args.info.sendfile.use_proxy = FALSE; - - if (strlen(proxyip)) - args.proxyip = (char *)proxyip; - if (strlen(clientip)) - args.clientip = (char *)clientip; - if (strlen(verifiedip)) - args.verifiedip = (char *)verifiedip; - - /* - * This must be present in PROPOSALs, but will probably not - * exist in CANCELs and ACCEPTs. Also exists in ICQ Lite - * Beta 4.0 URLs (AIM_CAPS_ICQSERVERRELAY). - * - * Service Data blocks are module-specific in format. - */ - if ((servdatatlv = aim_tlv_gettlv(list2, 0x2711 /* 10001 */, 1))) { - - aim_bstream_init(&sdbs, servdatatlv->value, servdatatlv->length); - sdbsptr = &sdbs; - } - - /* - * The rest of the handling depends on what type it is. - * - * Not all of them have special handling (yet). - */ - if (args.reqclass & AIM_CAPS_BUDDYICON) - incomingim_ch2_buddyicon(sess, mod, rx, snac, userinfo, &args, sdbsptr); - else if (args.reqclass & AIM_CAPS_SENDBUDDYLIST) - incomingim_ch2_buddylist(sess, mod, rx, snac, userinfo, &args, sdbsptr); - else if (args.reqclass & AIM_CAPS_CHAT) - incomingim_ch2_chat(sess, mod, rx, snac, userinfo, &args, sdbsptr); - else if (args.reqclass & AIM_CAPS_ICQSERVERRELAY) - incomingim_ch2_icqserverrelay(sess, mod, rx, snac, userinfo, &args, sdbsptr); - else if (args.reqclass & AIM_CAPS_SENDFILE) - incomingim_ch2_sendfile(sess, mod, rx, snac, userinfo, &args, sdbsptr); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, channel, userinfo, &args); - - - if (args.destructor) - ((ch2_args_destructor_t)args.destructor)(sess, &args); - - free((char *)args.msg); - free((char *)args.encoding); - free((char *)args.language); - - aim_tlvlist_free(&list2); - - return ret; -} - -static int incomingim_ch4(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, fu16_t channel, aim_userinfo_t *userinfo, aim_tlvlist_t *tlvlist, fu8_t *cookie) -{ - aim_bstream_t meat; - aim_rxcallback_t userfunc; - aim_tlv_t *block; - struct aim_incomingim_ch4_args args; - int ret = 0; - - /* - * Make a bstream for the meaty part. Yum. Meat. - */ - if (!(block = aim_tlv_gettlv(tlvlist, 0x0005, 1))) - return -1; - aim_bstream_init(&meat, block->value, block->length); - - args.uin = aimbs_getle32(&meat); - args.type = aimbs_getle8(&meat); - args.flags = aimbs_getle8(&meat); - args.msglen = aimbs_getle16(&meat); - args.msg = (gchar *)aimbs_getraw(&meat, args.msglen); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, channel, userinfo, &args); - - free(args.msg); - - return ret; -} - -/* - * Subtype 0x0007 - * - * It can easily be said that parsing ICBMs is THE single - * most difficult thing to do in the in AIM protocol. In - * fact, I think I just did say that. - * - * Below is the best damned solution I've come up with - * over the past sixteen months of battling with it. This - * can parse both away and normal messages from every client - * I have access to. Its not fast, its not clean. But it works. - * - */ -static int incomingim(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) -{ - int ret = 0; - guchar *cookie; - fu16_t channel; - aim_userinfo_t userinfo; - - memset(&userinfo, 0x00, sizeof(aim_userinfo_t)); - - /* - * Read ICBM Cookie. - */ - cookie = aimbs_getraw(bs, 8); - - /* - * Channel ID. - * - * Channel 0x0001 is the message channel. It is - * used to send basic ICBMs. - * - * Channel 0x0002 is the Rendezvous channel, which - * is where Chat Invitiations and various client-client - * connection negotiations come from. - * - * Channel 0x0003 is used for chat messages. - * - * Channel 0x0004 is used for ICQ authorization, or - * possibly any system notice. - * - */ - channel = aimbs_get16(bs); - - /* - * Extract the standard user info block. - * - * Note that although this contains TLVs that appear contiguous - * with the TLVs read below, they are two different pieces. The - * userinfo block contains the number of TLVs that contain user - * information, the rest are not even though there is no separation. - * You can start reading the message TLVs after aim_info_extract() - * parses out the standard userinfo block. - * - * That also means that TLV types can be duplicated between the - * userinfo block and the rest of the message, however there should - * never be two TLVs of the same type in one block. - * - */ - aim_info_extract(sess, bs, &userinfo); - - /* - * From here on, its depends on what channel we're on. - * - * Technically all channels have a TLV list have this, however, - * for the common channel 1 case, in-place parsing is used for - * performance reasons (less memory allocation). - */ - if (channel == 1) { - - ret = incomingim_ch1(sess, mod, rx, snac, channel, &userinfo, bs, cookie); - - } else if (channel == 2) { - aim_tlvlist_t *tlvlist; - - /* - * Read block of TLVs (not including the userinfo data). All - * further data is derived from what is parsed here. - */ - tlvlist = aim_tlvlist_read(bs); - - ret = incomingim_ch2(sess, mod, rx, snac, channel, &userinfo, tlvlist, cookie); - - aim_tlvlist_free(&tlvlist); - - } else if (channel == 4) { - aim_tlvlist_t *tlvlist; - - tlvlist = aim_tlvlist_read(bs); - ret = incomingim_ch4(sess, mod, rx, snac, channel, &userinfo, tlvlist, cookie); - aim_tlvlist_free(&tlvlist); - - } else { - gaim_debug_misc("oscar", "icbm: ICBM received on an unsupported channel. Ignoring. (chan = %04x)\n", channel); - } - - aim_info_free(&userinfo); - free(cookie); - - return ret; -} - -/* - * Subtype 0x0008 - Send a warning to sn. - * - * Flags: - * AIM_WARN_ANON Send as an anonymous (doesn't count as much) - * - * returns -1 on error (couldn't alloc packet), 0 on success. - * - */ -faim_export int aim_im_warn(aim_session_t *sess, aim_conn_t *conn, const char *sn, fu32_t flags) -{ - aim_frame_t *fr; - aim_snacid_t snacid; - - if (!sess || !conn || !sn) - return -EINVAL; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, strlen(sn)+13))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0004, 0x0008, 0x0000, sn, strlen(sn)+1); - aim_putsnac(&fr->data, 0x0004, 0x0008, 0x0000, snacid); - - aimbs_put16(&fr->data, (flags & AIM_WARN_ANON) ? 0x0001 : 0x0000); - aimbs_put8(&fr->data, strlen(sn)); - aimbs_putstr(&fr->data, sn); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* Subtype 0x000a */ -static int missedcall(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; - fu16_t channel, nummissed, reason; - aim_userinfo_t userinfo; - - while (aim_bstream_empty(bs)) { - - channel = aimbs_get16(bs); - aim_info_extract(sess, bs, &userinfo); - nummissed = aimbs_get16(bs); - reason = aimbs_get16(bs); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, channel, &userinfo, nummissed, reason); - - aim_info_free(&userinfo); - } - - return ret; -} - -/* - * Subtype 0x000b - * - * Possible codes: - * AIM_TRANSFER_DENY_NOTSUPPORTED -- "client does not support" - * AIM_TRANSFER_DENY_DECLINE -- "client has declined transfer" - * AIM_TRANSFER_DENY_NOTACCEPTING -- "client is not accepting transfers" - * - */ -faim_export int aim_im_denytransfer(aim_session_t *sess, const char *sender, const guchar *cookie, fu16_t code) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - aim_tlvlist_t *tl = NULL; - - if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) - return -EINVAL; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sender)+6))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0004, 0x000b, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0004, 0x000b, 0x0000, snacid); - - aimbs_putraw(&fr->data, cookie, 8); - - aimbs_put16(&fr->data, 0x0002); /* channel */ - aimbs_put8(&fr->data, strlen(sender)); - aimbs_putstr(&fr->data, sender); - - aim_tlvlist_add_16(&tl, 0x0003, code); - aim_tlvlist_write(&fr->data, &tl); - aim_tlvlist_free(&tl); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * Subtype 0x000b - Receive the response from an ICQ status message request. - * - * This contains the ICQ status message. Go figure. - * - */ -static int clientautoresp(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; - fu16_t channel, reason; - char *sn; - guchar *cookie; - guint8 snlen; - - cookie = aimbs_getraw(bs, 8); - channel = aimbs_get16(bs); - snlen = aimbs_get8(bs); - sn = aimbs_getstr(bs, snlen); - reason = aimbs_get16(bs); - - if (channel == 0x0002) { /* File transfer declined */ - aimbs_get16(bs); /* Unknown */ - aimbs_get16(bs); /* Unknown */ - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, channel, sn, reason, cookie); - } else if (channel == 0x0004) { /* ICQ message */ - switch (reason) { - case 0x0003: { /* ICQ status message. Maybe other stuff too, you never know with these people. */ - fu8_t statusmsgtype, *msg; - fu16_t len; - fu32_t state; - - len = aimbs_getle16(bs); /* Should be 0x001b */ - aim_bstream_advance(bs, len); /* Unknown */ - - len = aimbs_getle16(bs); /* Should be 0x000e */ - aim_bstream_advance(bs, len); /* Unknown */ - - statusmsgtype = aimbs_getle8(bs); - switch (statusmsgtype) { - case 0xe8: - state = AIM_ICQ_STATE_AWAY; - break; - case 0xe9: - state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY; - break; - case 0xea: - state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_OUT; - break; - case 0xeb: - state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY; - break; - case 0xec: - state = AIM_ICQ_STATE_CHAT; - break; - default: - state = 0; - break; - } - - aimbs_getle8(bs); /* Unknown - 0x03 Maybe this means this is an auto-reply */ - aimbs_getle16(bs); /* Unknown - 0x0000 */ - aimbs_getle16(bs); /* Unknown - 0x0000 */ - - len = aimbs_getle16(bs); - msg = aimbs_getraw(bs, len); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, channel, sn, reason, state, msg); - - free(msg); - } break; - - default: { - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, channel, sn, reason); - } break; - } /* end switch */ - } - - free(cookie); - free(sn); - - return ret; -} - -/* - * Subtype 0x000c - Receive an ack after sending an ICBM. - * - * You have to have send the message with the AIM_IMFLAGS_ACK flag set - * (TLV t(0003)). The ack contains the ICBM header of the message you - * sent. - * - */ -static int msgack(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 ch; - guchar *cookie; - char *sn; - int ret = 0; - - cookie = aimbs_getraw(bs, 8); - ch = aimbs_get16(bs); - sn = aimbs_getstr(bs, aimbs_get8(bs)); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, ch, sn); - - free(sn); - free(cookie); - - return ret; -} - -/* - * Subtype 0x0014 - Send a mini typing notification (mtn) packet. - * - * This is supported by winaim5 and newer, MacAIM bleh and newer, iChat bleh and newer, - * and Gaim 0.60 and newer. - * - */ -faim_export int aim_im_sendmtn(aim_session_t *sess, fu16_t type1, const char *sn, fu16_t type2) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - - if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0002))) - return -EINVAL; - - if (!sn) - return -EINVAL; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+11+strlen(sn)+2))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0004, 0x0014, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0004, 0x0014, 0x0000, snacid); - - /* - * 8 days of light - * Er, that is to say, 8 bytes of 0's - */ - aimbs_put16(&fr->data, 0x0000); - aimbs_put16(&fr->data, 0x0000); - aimbs_put16(&fr->data, 0x0000); - aimbs_put16(&fr->data, 0x0000); - - /* - * Type 1 (should be 0x0001 for mtn) - */ - aimbs_put16(&fr->data, type1); - - /* - * Dest sn - */ - aimbs_put8(&fr->data, strlen(sn)); - aimbs_putstr(&fr->data, sn); - - /* - * Type 2 (should be 0x0000, 0x0001, or 0x0002 for mtn) - */ - aimbs_put16(&fr->data, type2); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * Subtype 0x0014 - Receive a mini typing notification (mtn) packet. - * - * This is supported by winaim5 and newer, MacAIM bleh and newer, iChat bleh and newer, - * and Gaim 0.60 and newer. - * - */ -static int mtn_receive(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; - char *sn; - fu8_t snlen; - fu16_t type1, type2; - - aim_bstream_advance(bs, 8); /* Unknown - All 0's */ - type1 = aimbs_get16(bs); - snlen = aimbs_get8(bs); - sn = aimbs_getstr(bs, snlen); - type2 = aimbs_get16(bs); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, type1, sn, type2); - - free(sn); - - 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 == 0x0005) - return aim_im_paraminfo(sess, mod, rx, snac, bs); - else if (snac->subtype == 0x0006) - return outgoingim(sess, mod, rx, snac, bs); - else if (snac->subtype == 0x0007) - return incomingim(sess, mod, rx, snac, bs); - else if (snac->subtype == 0x000a) - return missedcall(sess, mod, rx, snac, bs); - else if (snac->subtype == 0x000b) - return clientautoresp(sess, mod, rx, snac, bs); - else if (snac->subtype == 0x000c) - return msgack(sess, mod, rx, snac, bs); - else if (snac->subtype == 0x0014) - return mtn_receive(sess, mod, rx, snac, bs); - - return 0; -} - -faim_internal int msg_modfirst(aim_session_t *sess, aim_module_t *mod) -{ - - mod->family = 0x0004; - mod->version = 0x0001; - mod->toolid = 0x0110; - mod->toolversion = 0x0629; - mod->flags = 0; - strncpy(mod->name, "messaging", sizeof(mod->name)); - mod->snachandler = snachandler; - - return 0; -} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/invite.c --- a/src/protocols/oscar/invite.c Sat Feb 11 19:16:38 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -/* - * Family 0x0006 - This isn't really ever used by anyone anymore. - * - * Once upon a time, there used to be a menu item in AIM clients that - * said something like "Invite a friend to use AIM..." and then it would - * ask for an email address and it would sent a mail to them saying - * how perfectly wonderful the AIM service is and why you should use it - * and click here if you hate the person who sent this to you and want to - * complain and yell at them in a small box with pretty fonts. - * - * I could've sworn libfaim had this implemented once, a long long time ago, - * but I can't find it. - * - * I'm mainly adding this so that I can keep advertising that we support - * group 6, even though we don't. - * - */ - -#define FAIM_INTERNAL -#include - -faim_internal int invite_modfirst(aim_session_t *sess, aim_module_t *mod) -{ - - mod->family = 0x0006; - mod->version = 0x0001; - mod->toolid = 0x0110; - mod->toolversion = 0x0629; - mod->flags = 0; - strncpy(mod->name, "invite", sizeof(mod->name)); - mod->snachandler = NULL; - - return 0; -} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/locate.c --- a/src/protocols/oscar/locate.c Sat Feb 11 19:16:38 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1390 +0,0 @@ -/* - * Family 0x0002 - Locate. - * - * The functions here are responsible for requesting and parsing information- - * gathering SNACs. Or something like that. This family contains the SNACs - * for getting and setting info, away messages, directory profile thingy, etc. - */ - -#define FAIM_INTERNAL -#include -#ifdef _WIN32 -#include "win32dep.h" -#endif - -/* - * Capability blocks. - * - * These are CLSIDs. They should actually be of the form: - * - * {0x0946134b, 0x4c7f, 0x11d1, - * {0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}}, - * - * But, eh. - */ -static const struct { - fu32_t flag; - fu8_t data[16]; -} aim_caps[] = { - - /* - * These are in ascending numerical order. - */ - - /* - * Perhaps better called AIM_CAPS_SHORTCAPS - */ - {AIM_CAPS_ICHAT, - {0x09, 0x46, 0x00, 0x00, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - {AIM_CAPS_SECUREIM, - {0x09, 0x46, 0x00, 0x01, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - {AIM_CAPS_VIDEO, - {0x09, 0x46, 0x01, 0x00, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - /* "Live Video" support in Windows AIM 5.5.3501 and newer */ - {AIM_CAPS_LIVEVIDEO, - {0x09, 0x46, 0x01, 0x01, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - /* "Camera" support in Windows AIM 5.5.3501 and newer */ - {AIM_CAPS_CAMERA, - {0x09, 0x46, 0x01, 0x02, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - /* In Windows AIM 5.5.3501 and newer */ - {AIM_CAPS_GENERICUNKNOWN, - {0x09, 0x46, 0x01, 0x03, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - /* In iChatAV (version numbers...?) */ - {AIM_CAPS_ICHATAV, - {0x09, 0x46, 0x01, 0x05, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x45, 0x53, 0x54, 0x00}}, - - /* - * Not really sure about this one. In an email from - * 26 Sep 2003, Matthew Sachs suggested that, "this - * is probably the capability for the SMS features." - */ - {AIM_CAPS_SMS, - {0x09, 0x46, 0x01, 0xff, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - {AIM_CAPS_GENERICUNKNOWN, - {0x09, 0x46, 0xf0, 0x03, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - {AIM_CAPS_GENERICUNKNOWN, - {0x09, 0x46, 0xf0, 0x04, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - {AIM_CAPS_GENERICUNKNOWN, - {0x09, 0x46, 0xf0, 0x05, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - {AIM_CAPS_HIPTOP, - {0x09, 0x46, 0x13, 0x23, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - {AIM_CAPS_TALK, - {0x09, 0x46, 0x13, 0x41, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - {AIM_CAPS_SENDFILE, - {0x09, 0x46, 0x13, 0x43, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - {AIM_CAPS_ICQ_DIRECT, - {0x09, 0x46, 0x13, 0x44, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - {AIM_CAPS_DIRECTIM, - {0x09, 0x46, 0x13, 0x45, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - {AIM_CAPS_BUDDYICON, - {0x09, 0x46, 0x13, 0x46, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - {AIM_CAPS_ADDINS, - {0x09, 0x46, 0x13, 0x47, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - {AIM_CAPS_GETFILE, - {0x09, 0x46, 0x13, 0x48, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - {AIM_CAPS_ICQSERVERRELAY, - {0x09, 0x46, 0x13, 0x49, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - /* - * Indeed, there are two of these. The former appears to be correct, - * but in some versions of winaim, the second one is set. Either they - * forgot to fix endianness, or they made a typo. It really doesn't - * matter which. - */ - {AIM_CAPS_GAMES, - {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - {AIM_CAPS_GAMES2, - {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1, - 0x22, 0x82, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - {AIM_CAPS_SENDBUDDYLIST, - {0x09, 0x46, 0x13, 0x4b, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - /* - * Setting this lets AIM users receive messages from ICQ users, and ICQ - * users receive messages from AIM users. It also lets ICQ users show - * up in buddy lists for AIM users, and AIM users show up in buddy lists - * for ICQ users. And ICQ privacy/invisibility acts like AIM privacy, - * in that if you add a user to your deny list, you will not be able to - * see them as online (previous you could still see them, but they - * couldn't see you. - */ - {AIM_CAPS_INTEROPERATE, - {0x09, 0x46, 0x13, 0x4d, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - {AIM_CAPS_ICQUTF8, - {0x09, 0x46, 0x13, 0x4e, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - {AIM_CAPS_ICQUTF8OLD, - {0x2e, 0x7a, 0x64, 0x75, 0xfa, 0xdf, 0x4d, 0xc8, - 0x88, 0x6f, 0xea, 0x35, 0x95, 0xfd, 0xb6, 0xdf}}, - - /* - * Chat is oddball. - */ - {AIM_CAPS_CHAT, - {0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - /* - {AIM_CAPS_ICQ2GO, - {0x56, 0x3f, 0xc8, 0x09, 0x0b, 0x6f, 0x41, 0xbd, - 0x9f, 0x79, 0x42, 0x26, 0x09, 0xdf, 0xa2, 0xf3}}, - */ - - {AIM_CAPS_ICQRTF, - {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34, - 0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x92}}, - - /* This is added by the servers and it only shows up for ourselves... */ - {AIM_CAPS_GENERICUNKNOWN, - {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34, - 0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x09}}, - - {AIM_CAPS_APINFO, - {0xaa, 0x4a, 0x32, 0xb5, 0xf8, 0x84, 0x48, 0xc6, - 0xa3, 0xd7, 0x8c, 0x50, 0x97, 0x19, 0xfd, 0x5b}}, - - {AIM_CAPS_TRILLIANCRYPT, - {0xf2, 0xe7, 0xc7, 0xf4, 0xfe, 0xad, 0x4d, 0xfb, - 0xb2, 0x35, 0x36, 0x79, 0x8b, 0xdf, 0x00, 0x00}}, - - {AIM_CAPS_EMPTY, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - - {AIM_CAPS_LAST, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, -}; - -/* - * Add the userinfo to our linked list. If we already have userinfo - * for this buddy, then just overwrite parts of the old data. - * - * @param userinfo Contains the new information for the buddy. - */ -static void aim_locate_adduserinfo(aim_session_t *sess, aim_userinfo_t *userinfo) { - aim_userinfo_t *cur; - aim_conn_t *conn; - aim_rxcallback_t userfunc; - - cur = aim_locate_finduserinfo(sess, userinfo->sn); - - if (cur == NULL) { - cur = (aim_userinfo_t *)calloc(1, sizeof(aim_userinfo_t)); - cur->sn = strdup(userinfo->sn); - cur->next = sess->locate.userinfo; - sess->locate.userinfo = cur; - } - - cur->warnlevel = userinfo->warnlevel; - cur->idletime = userinfo->idletime; - if (userinfo->flags != 0) - cur->flags = userinfo->flags; - if (userinfo->createtime != 0) - cur->createtime = userinfo->createtime; - if (userinfo->membersince != 0) - cur->membersince = userinfo->membersince; - if (userinfo->onlinesince != 0) - cur->onlinesince = userinfo->onlinesince; - if (userinfo->sessionlen != 0) - cur->sessionlen = userinfo->sessionlen; - if (userinfo->capabilities != 0) - cur->capabilities = userinfo->capabilities; - cur->present |= userinfo->present; - - if (userinfo->iconcsumlen > 0) { - free(cur->iconcsum); - cur->iconcsum = (fu8_t *)malloc(userinfo->iconcsumlen); - memcpy(cur->iconcsum, userinfo->iconcsum, userinfo->iconcsumlen); - cur->iconcsumlen = userinfo->iconcsumlen; - } - - if (userinfo->info != NULL) { - free(cur->info); - free(cur->info_encoding); - if (userinfo->info_len > 0) { - cur->info = (char *)malloc(userinfo->info_len); - memcpy(cur->info, userinfo->info, userinfo->info_len); - } else - cur->info = NULL; - cur->info_encoding = strdup(userinfo->info_encoding); - cur->info_len = userinfo->info_len; - } - - if (userinfo->status != NULL) { - free(cur->status); - free(cur->status_encoding); - if (userinfo->status_len > 0) { - cur->status = (char *)malloc(userinfo->status_len); - memcpy(cur->status, userinfo->status, userinfo->status_len); - } else - cur->status = NULL; - if (userinfo->status_encoding != NULL) - cur->status_encoding = strdup(userinfo->status_encoding); - else - cur->status_encoding = NULL; - cur->status_len = userinfo->status_len; - } - - if (userinfo->away != NULL) { - free(cur->away); - free(cur->away_encoding); - if (userinfo->away_len > 0) { - cur->away = (char *)malloc(userinfo->away_len); - memcpy(cur->away, userinfo->away, userinfo->away_len); - } else - cur->away = NULL; - cur->away_encoding = strdup(userinfo->away_encoding); - cur->away_len = userinfo->away_len; - } - - /* - * This callback can be used by a client if they want to know whenever - * info for a buddy is updated. For example, if a client shows away - * messages in its buddy list, then it would need to know if a user's - * away message changes. - */ - conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC); - if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_LOC, AIM_CB_LOC_GOTINFOBLOCK))) - userfunc(sess, NULL, cur); -} - -faim_export void aim_locate_dorequest(aim_session_t *sess) { - struct userinfo_node *cur = sess->locate.torequest; - - if (cur == NULL) - return; - - if (sess->locate.waiting_for_response == TRUE) - return; - - sess->locate.waiting_for_response = TRUE; - aim_locate_getinfoshort(sess, cur->sn, 0x00000003); - - /* Move this node to the "requested" queue */ - sess->locate.torequest = cur->next; - cur->next = sess->locate.requested; - sess->locate.requested = cur; -} - -/** - * Remove this screen name from our queue. If this info was requested - * by our info request queue, then pop the next element off of the queue. - * - * @param sess The aim session. - * @param sn Screen name of the info we just received. - * @return True if the request was explicit (client requested the info), - * false if the request was implicit (libfaim request the info). - */ -static int aim_locate_gotuserinfo(aim_session_t *sess, const char *sn) { - struct userinfo_node *cur, *del; - int was_explicit = TRUE; - - while ((sess->locate.requested != NULL) && (aim_sncmp(sn, sess->locate.requested->sn) == 0)) { - del = sess->locate.requested; - sess->locate.requested = del->next; - was_explicit = FALSE; - free(del->sn); - free(del); - } - - cur = sess->locate.requested; - while ((cur != NULL) && (cur->next != NULL)) { - if (aim_sncmp(sn, cur->next->sn) == 0) { - del = cur->next; - cur->next = del->next; - was_explicit = FALSE; - free(del->sn); - free(del); - } else - cur = cur->next; - } - - if (!was_explicit) { - aim_conn_t *conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC); - aim_rxcallback_t userfunc; - - sess->locate.waiting_for_response = FALSE; - - if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_LOC, AIM_CB_LOC_REQUESTINFOTIMEOUT))) - userfunc(sess, NULL); - else - aim_locate_dorequest(sess); - } - - return was_explicit; -} - -faim_internal void aim_locate_requestuserinfo(aim_session_t *sess, const char *sn) { - struct userinfo_node *cur; - - /* Make sure we aren't already requesting info for this buddy */ - cur = sess->locate.torequest; - while (cur != NULL) { - if (aim_sncmp(sn, cur->sn) == 0) - return; - cur = cur->next; - } - - /* Add a new node to our request queue */ - cur = (struct userinfo_node *)malloc(sizeof(struct userinfo_node)); - cur->sn = strdup(sn); - cur->next = sess->locate.torequest; - sess->locate.torequest = cur; - - /* Actually request some info up in this piece */ - aim_locate_dorequest(sess); -} - -faim_export aim_userinfo_t *aim_locate_finduserinfo(aim_session_t *sess, const char *sn) { - aim_userinfo_t *cur = NULL; - - if (sn == NULL) - return NULL; - - cur = sess->locate.userinfo; - - while (cur != NULL) { - if (aim_sncmp(cur->sn, sn) == 0) - return cur; - cur = cur->next; - } - - return NULL; -} - -faim_internal fu32_t aim_locate_getcaps(aim_session_t *sess, aim_bstream_t *bs, int len) -{ - fu32_t flags = 0; - int offset; - - for (offset = 0; aim_bstream_empty(bs) && (offset < len); offset += 0x10) { - fu8_t *cap; - int i, identified; - - cap = aimbs_getraw(bs, 0x10); - - for (i = 0, identified = 0; !(aim_caps[i].flag & AIM_CAPS_LAST); i++) { - if (memcmp(&aim_caps[i].data, cap, 0x10) == 0) { - flags |= aim_caps[i].flag; - identified++; - break; /* should only match once... */ - } - } - - if (!identified) - gaim_debug_misc("oscar", "unknown capability: {%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n", - cap[0], cap[1], cap[2], cap[3], - cap[4], cap[5], - cap[6], cap[7], - cap[8], cap[9], - cap[10], cap[11], cap[12], cap[13], - cap[14], cap[15]); - - free(cap); - } - - return flags; -} - -faim_internal fu32_t aim_locate_getcaps_short(aim_session_t *sess, aim_bstream_t *bs, int len) -{ - fu32_t flags = 0; - int offset; - - for (offset = 0; aim_bstream_empty(bs) && (offset < len); offset += 0x02) { - fu8_t *cap; - int i, identified; - - cap = aimbs_getraw(bs, 0x02); - - for (i = 0, identified = 0; !(aim_caps[i].flag & AIM_CAPS_LAST); i++) { - if (memcmp(&aim_caps[i].data[2], cap, 0x02) == 0) { - flags |= aim_caps[i].flag; - identified++; - break; /* should only match once... */ - } - } - - if (!identified) - gaim_debug_misc("oscar", "unknown short capability: {%02x%02x}\n", cap[0], cap[1]); - - free(cap); - } - - return flags; -} - -faim_internal int aimbs_putcaps(aim_bstream_t *bs, fu32_t caps) -{ - int i; - - if (!bs) - return -EINVAL; - - for (i = 0; aim_bstream_empty(bs); i++) { - - if (aim_caps[i].flag == AIM_CAPS_LAST) - break; - - if (caps & aim_caps[i].flag) - aimbs_putraw(bs, aim_caps[i].data, 0x10); - - } - - return 0; -} - -static void dumptlv(aim_session_t *sess, fu16_t type, aim_bstream_t *bs, fu8_t len) -{ - int i; - - if (!sess || !bs || !len) - return; - - gaim_debug_misc("oscar", "userinfo: type =0x%04x\n", type); - gaim_debug_misc("oscar", "userinfo: length=0x%04x\n", len); - gaim_debug_misc("oscar", "userinfo: value:\n"); - - for (i = 0; i < len; i++) { - if ((i % 8) == 0) - gaim_debug_misc("oscar", "\nuserinfo: "); - gaim_debug_misc("oscar", "0x%2x ", aimbs_get8(bs)); - } - - gaim_debug_misc("oscar", "\n"); - - return; -} - -faim_internal void aim_info_free(aim_userinfo_t *info) -{ - free(info->sn); - free(info->iconcsum); - free(info->info); - free(info->info_encoding); - free(info->status); - free(info->status_encoding); - free(info->away); - free(info->away_encoding); -} - -/* - * AIM is fairly regular about providing user info. This is a generic - * routine to extract it in its standard form. - */ -faim_internal int aim_info_extract(aim_session_t *sess, aim_bstream_t *bs, aim_userinfo_t *outinfo) -{ - int curtlv, tlvcnt; - fu8_t snlen; - - if (!bs || !outinfo) - return -EINVAL; - - /* Clear out old data first */ - memset(outinfo, 0x00, sizeof(aim_userinfo_t)); - - /* - * Screen name. Stored as an unterminated string prepended with a - * byte containing its length. - */ - snlen = aimbs_get8(bs); - outinfo->sn = aimbs_getstr(bs, snlen); - - /* - * Warning Level. Stored as an unsigned short. - */ - outinfo->warnlevel = aimbs_get16(bs); - - /* - * TLV Count. Unsigned short representing the number of - * Type-Length-Value triples that follow. - */ - tlvcnt = aimbs_get16(bs); - - /* - * Parse out the Type-Length-Value triples as they're found. - */ - for (curtlv = 0; curtlv < tlvcnt; curtlv++) { - int endpos; - fu16_t type, length; - - type = aimbs_get16(bs); - length = aimbs_get16(bs); - - endpos = aim_bstream_curpos(bs) + length; - - if (type == 0x0001) { - /* - * Type = 0x0001: User flags - * - * Specified as any of the following ORed together: - * 0x0001 Trial (user less than 60days) - * 0x0002 Unknown bit 2 - * 0x0004 AOL Main Service user - * 0x0008 Unknown bit 4 - * 0x0010 Free (AIM) user - * 0x0020 Away - * 0x0400 ActiveBuddy - * - */ - outinfo->flags = aimbs_get16(bs); - outinfo->present |= AIM_USERINFO_PRESENT_FLAGS; - - } else if (type == 0x0002) { - /* - * Type = 0x0002: Account creation time. - * - * The time/date that the user originally registered for - * the service, stored in time_t format. - * - * I'm not sure how this differs from type 5 ("member - * since"). - * - * Note: This is the field formerly known as "member - * since". All these years and I finally found out - * that I got the name wrong. - */ - outinfo->createtime = aimbs_get32(bs); - outinfo->present |= AIM_USERINFO_PRESENT_CREATETIME; - - } else if (type == 0x0003) { - /* - * Type = 0x0003: On-Since date. - * - * The time/date that the user started their current - * session, stored in time_t format. - */ - outinfo->onlinesince = aimbs_get32(bs); - outinfo->present |= AIM_USERINFO_PRESENT_ONLINESINCE; - - } else if (type == 0x0004) { - /* - * Type = 0x0004: Idle time. - * - * Number of minutes since the user actively used the - * service. - * - * Note that the client tells the server when to start - * counting idle times, so this may or may not be - * related to reality. - */ - outinfo->idletime = aimbs_get16(bs); - outinfo->present |= AIM_USERINFO_PRESENT_IDLE; - - } else if (type == 0x0005) { - /* - * Type = 0x0005: Member since date. - * - * The time/date that the user originally registered for - * the service, stored in time_t format. - * - * This is sometimes sent instead of type 2 ("account - * creation time"), particularly in the self-info. - * And particularly for ICQ? - */ - outinfo->membersince = aimbs_get32(bs); - outinfo->present |= AIM_USERINFO_PRESENT_MEMBERSINCE; - - } else if (type == 0x0006) { - /* - * Type = 0x0006: ICQ Online Status - * - * ICQ's Away/DND/etc "enriched" status. Some decoding - * of values done by Scott - */ - aimbs_get16(bs); - outinfo->icqinfo.status = aimbs_get16(bs); - outinfo->present |= AIM_USERINFO_PRESENT_ICQEXTSTATUS; - - } else if (type == 0x0008) { - /* - * Type = 0x0008 - * - * Client type, or some such. - */ - - } else if (type == 0x000a) { - /* - * Type = 0x000a - * - * ICQ User IP Address. - * Ahh, the joy of ICQ security. - */ - outinfo->icqinfo.ipaddr = aimbs_get32(bs); - outinfo->present |= AIM_USERINFO_PRESENT_ICQIPADDR; - - } else if (type == 0x000c) { - /* - * Type = 0x000c - * - * random crap containing the IP address, - * apparently a port number, and some Other Stuff. - * - * Format is: - * 4 bytes - Our IP address, 0xc0 a8 01 2b for 192.168.1.43 - * - * - */ - aimbs_getrawbuf(bs, outinfo->icqinfo.crap, 0x25); - outinfo->present |= AIM_USERINFO_PRESENT_ICQDATA; - - } else if (type == 0x000d) { - /* - * Type = 0x000d - * - * OSCAR Capability information. - * - */ - outinfo->capabilities |= aim_locate_getcaps(sess, bs, length); - outinfo->present |= AIM_USERINFO_PRESENT_CAPABILITIES; - - } else if (type == 0x000e) { - /* - * Type = 0x000e - * - * AOL capability information. - * - */ - - } else if ((type == 0x000f) || (type == 0x0010)) { - /* - * Type = 0x000f: Session Length. (AIM) - * Type = 0x0010: Session Length. (AOL) - * - * The duration, in seconds, of the user's current - * session. - * - * Which TLV type this comes in depends on the - * service the user is using (AIM or AOL). - * - */ - outinfo->sessionlen = aimbs_get32(bs); - outinfo->present |= AIM_USERINFO_PRESENT_SESSIONLEN; - - } else if (type == 0x0019) { - /* - * Type = 0x0019 - * - * OSCAR short capability information. A shortened - * form of the normal capabilities. - */ - outinfo->capabilities |= aim_locate_getcaps_short(sess, bs, length); - outinfo->present |= AIM_USERINFO_PRESENT_CAPABILITIES; - - } else if (type == 0x001b) { - /* - * Type = 0x001a - * - * AOL short capability information. A shortened - * form of the normal capabilities. - */ - - } else if (type == 0x001b) { - /* - * Type = 0x0019 - * - * Encryption certification MD5 checksum. - */ - - } else if (type == 0x001d) { - /* - * Type = 0x001d - * - * Buddy icon information and status/available messages. - * - * This almost seems like the AIM protocol guys gave - * the iChat guys a Type, and the iChat guys tried to - * cram as much cool shit into it as possible. Then - * the Windows AIM guys were like, "hey, that's - * pretty neat, let's copy those prawns." - * - * In that spirit, this can contain a custom message, - * kind of like an away message, but you're not away - * (it's called an "available" message). Or it can - * contain information about the buddy icon the user - * has stored on the server. - */ - int type2, number, length2; - - while (aim_bstream_curpos(bs) < endpos) { - type2 = aimbs_get16(bs); - number = aimbs_get8(bs); - length2 = aimbs_get8(bs); - - switch (type2) { - case 0x0000: { /* This is an official buddy icon? */ - /* This is always 5 bytes of "0x02 01 d2 04 72"? */ - aim_bstream_advance(bs, length2); - } break; - - case 0x0001: { /* A buddy icon checksum */ - if ((length2 > 0) && ((number == 0x00) || (number == 0x01))) { - free(outinfo->iconcsum); - outinfo->iconcsumtype = number; - outinfo->iconcsum = aimbs_getraw(bs, length2); - outinfo->iconcsumlen = length2; - } else - aim_bstream_advance(bs, length2); - } break; - - case 0x0002: { /* A status/available message */ - free(outinfo->status); - free(outinfo->status_encoding); - if (length2 >= 4) { - outinfo->status_len = aimbs_get16(bs); - outinfo->status = aimbs_getstr(bs, outinfo->status_len); - if (aimbs_get16(bs) == 0x0001) { /* We have an encoding */ - aimbs_get16(bs); - outinfo->status_encoding = aimbs_getstr(bs, aimbs_get16(bs)); - } else { - /* No explicit encoding, client should use UTF-8 */ - outinfo->status_encoding = NULL; - } - } else { - aim_bstream_advance(bs, length2); - outinfo->status_len = 0; - outinfo->status = g_strdup(""); - outinfo->status_encoding = NULL; - } - } break; - - default: { - aim_bstream_advance(bs, length2); - } break; - } - } - - } else if (type == 0x001e) { - /* - * Type 30: Unknown. - * - * Always four bytes, but it doesn't look like an int. - */ - - } else if (type == 0x001f) { - /* - * Type 31: Unknown. - * - * Seen on a buddy using DeadAIM. Data was 4 bytes: - * 0x00 00 00 10 - */ - - } else { - - /* - * Reaching here indicates that either AOL has - * added yet another TLV for us to deal with, - * or the parsing has gone Terribly Wrong. - * - * Either way, inform the owner and attempt - * recovery. - * - */ - gaim_debug_misc("oscar", "userinfo: **warning: unexpected TLV:\n"); - gaim_debug_misc("oscar", "userinfo: sn =%s\n", outinfo->sn); - dumptlv(sess, type, bs, length); - } - - /* Save ourselves. */ - aim_bstream_setpos(bs, endpos); - } - - aim_locate_adduserinfo(sess, outinfo); - - return 0; -} - -/* - * Inverse of aim_info_extract() - */ -faim_internal int aim_putuserinfo(aim_bstream_t *bs, aim_userinfo_t *info) -{ - aim_tlvlist_t *tlvlist = NULL; - - if (!bs || !info) - return -EINVAL; - - aimbs_put8(bs, strlen(info->sn)); - aimbs_putstr(bs, info->sn); - - aimbs_put16(bs, info->warnlevel); - - if (info->present & AIM_USERINFO_PRESENT_FLAGS) - aim_tlvlist_add_16(&tlvlist, 0x0001, info->flags); - if (info->present & AIM_USERINFO_PRESENT_MEMBERSINCE) - aim_tlvlist_add_32(&tlvlist, 0x0002, info->membersince); - if (info->present & AIM_USERINFO_PRESENT_ONLINESINCE) - aim_tlvlist_add_32(&tlvlist, 0x0003, info->onlinesince); - if (info->present & AIM_USERINFO_PRESENT_IDLE) - aim_tlvlist_add_16(&tlvlist, 0x0004, info->idletime); - -/* XXX - So, ICQ_OSCAR_SUPPORT is never defined anywhere... */ -#ifdef ICQ_OSCAR_SUPPORT - if (atoi(info->sn) != 0) { - if (info->present & AIM_USERINFO_PRESENT_ICQEXTSTATUS) - aim_tlvlist_add_16(&tlvlist, 0x0006, info->icqinfo.status); - if (info->present & AIM_USERINFO_PRESENT_ICQIPADDR) - aim_tlvlist_add_32(&tlvlist, 0x000a, info->icqinfo.ipaddr); - } -#endif - - if (info->present & AIM_USERINFO_PRESENT_CAPABILITIES) - aim_tlvlist_add_caps(&tlvlist, 0x000d, info->capabilities); - - if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN) - aim_tlvlist_add_32(&tlvlist, (fu16_t)((info->flags & AIM_FLAG_AOL) ? 0x0010 : 0x000f), info->sessionlen); - - aimbs_put16(bs, aim_tlvlist_count(&tlvlist)); - aim_tlvlist_write(bs, &tlvlist); - aim_tlvlist_free(&tlvlist); - - return 0; -} - -/* - * Subtype 0x0001 - */ -static int error(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; - aim_snac_t *snac2; - fu16_t reason; - char *sn; - int was_explicit; - - if (!(snac2 = aim_remsnac(sess, snac->id))) { - gaim_debug_misc("oscar", "faim: locate.c, error(): received response from unknown request!\n"); - return 0; - } - - if ((snac2->family != 0x0002) && (snac2->type != 0x0015)) { - gaim_debug_misc("oscar", "faim: locate.c, error(): received response from invalid request! %d\n", snac2->family); - return 0; - } - - if (!(sn = snac2->data)) { - gaim_debug_misc("oscar", "faim: locate.c, error(): received response from request without a screen name!\n"); - return 0; - } - - reason = aimbs_get16(bs); - - /* - * Remove this screen name from our queue. If the client requested - * this buddy's info explicitly, then notify them that we do not have - * info for this buddy. - */ - was_explicit = aim_locate_gotuserinfo(sess, sn); - if (was_explicit == TRUE) - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, reason, sn); - - if (snac2) - free(snac2->data); - free(snac2); - - return ret; -} - -/* - * Subtype 0x0002 - * - * Request Location services rights. - * - */ -faim_export int aim_locate_reqrights(aim_session_t *sess) -{ - aim_conn_t *conn; - - if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC))) - return -EINVAL; - - return aim_genericreq_n_snacid(sess, conn, AIM_CB_FAM_LOC, AIM_CB_LOC_REQRIGHTS); -} - -/* - * Subtype 0x0003 - * - * Normally contains: - * t(0001) - short containing max profile length (value = 1024) - * t(0002) - short - unknown (value = 16) [max MIME type length?] - * t(0003) - short - unknown (value = 10) - * t(0004) - short - unknown (value = 2048) [ICQ only?] - */ -static int rights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) -{ - aim_tlvlist_t *tlvlist; - aim_rxcallback_t userfunc; - int ret = 0; - fu16_t maxsiglen = 0; - - tlvlist = aim_tlvlist_read(bs); - - if (aim_tlv_gettlv(tlvlist, 0x0001, 1)) - maxsiglen = aim_tlv_get16(tlvlist, 0x0001, 1); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, maxsiglen); - - aim_tlvlist_free(&tlvlist); - - return ret; -} - -/* - * Subtype 0x0004 - * - * Gives BOS your profile. - * - * profile_encoding and awaymsg_encoding MUST be set if profile or - * away are set, respectively, and their value may or may not be - * restricted to a few choices. I am currently aware of: - * - * us-ascii Just that - * unicode-2-0 UCS2-BE - * - * profile_len and awaymsg_len MUST be set similarly, and they MUST - * be the length of their respective strings in bytes. - * - * To get the previous behavior of awaymsg == "" un-setting the away - * message, set awaymsg non-NULL and awaymsg_len to 0 (this is the - * obvious equivalent). - * - */ -faim_export int aim_locate_setprofile(aim_session_t *sess, - const char *profile_encoding, const gchar *profile, const int profile_len, - const char *awaymsg_encoding, const gchar *awaymsg, const int awaymsg_len) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - aim_tlvlist_t *tl = NULL; - char *encoding; - static const char defencoding[] = {"text/aolrtf; charset=\"%s\""}; - - if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC))) - return -EINVAL; - - if (!profile && !awaymsg) - return -EINVAL; - - if ((profile && profile_encoding == NULL) || (awaymsg && awaymsg_len && awaymsg_encoding == NULL)) { - return -EINVAL; - } - - /* Build the packet first to get real length */ - if (profile) { - /* no + 1 here because of %s */ - encoding = malloc(strlen(defencoding) + strlen(profile_encoding)); - if (encoding == NULL) { - return -ENOMEM; - } - snprintf(encoding, strlen(defencoding) + strlen(profile_encoding), defencoding, profile_encoding); - aim_tlvlist_add_str(&tl, 0x0001, encoding); - aim_tlvlist_add_raw(&tl, 0x0002, profile_len, (const guchar *)profile); - free(encoding); - } - - /* - * 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) { - if (awaymsg_len) { - encoding = malloc(strlen(defencoding) + strlen(awaymsg_encoding)); - if (encoding == NULL) { - return -ENOMEM; - } - snprintf(encoding, strlen(defencoding) + strlen(awaymsg_encoding), defencoding, awaymsg_encoding); - aim_tlvlist_add_str(&tl, 0x0003, encoding); - aim_tlvlist_add_raw(&tl, 0x0004, awaymsg_len, (const guchar *)awaymsg); - free(encoding); - } else - aim_tlvlist_add_noval(&tl, 0x0004); - } - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + aim_tlvlist_size(&tl)))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0002, 0x0004, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0002, 0x004, 0x0000, snacid); - - aim_tlvlist_write(&fr->data, &tl); - aim_tlvlist_free(&tl); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * Subtype 0x0004 - Set your client's capabilities. - */ -faim_export int aim_locate_setcaps(aim_session_t *sess, fu32_t caps) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - aim_tlvlist_t *tl = NULL; - - if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC))) - return -EINVAL; - - aim_tlvlist_add_caps(&tl, 0x0005, caps); - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + aim_tlvlist_size(&tl)))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0002, 0x0004, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0002, 0x004, 0x0000, snacid); - - aim_tlvlist_write(&fr->data, &tl); - aim_tlvlist_free(&tl); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * Subtype 0x0005 - Request info of another AIM user. - * - * @param sn The screenname whose info you wish to request. - * @param infotype The type of info you wish to request. - * 0x0001 - Info/profile - * 0x0003 - Away message - * 0x0004 - Capabilities - */ -faim_export int aim_locate_getinfo(aim_session_t *sess, const char *sn, fu16_t infotype) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - - if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC)) || !sn) - return -EINVAL; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 12+1+strlen(sn)))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0002, 0x0005, 0x0000, NULL, 0); - - aim_putsnac(&fr->data, 0x0002, 0x0005, 0x0000, snacid); - aimbs_put16(&fr->data, infotype); - aimbs_put8(&fr->data, strlen(sn)); - aimbs_putstr(&fr->data, sn); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* Subtype 0x0006 */ -static int userinfo(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; - aim_userinfo_t *userinfo, *userinfo2; - aim_tlvlist_t *tlvlist; - aim_tlv_t *tlv = NULL; - int was_explicit; - - userinfo = (aim_userinfo_t *)malloc(sizeof(aim_userinfo_t)); - aim_info_extract(sess, bs, userinfo); - tlvlist = aim_tlvlist_read(bs); - - /* Profile will be 1 and 2 */ - userinfo->info_encoding = aim_tlv_getstr(tlvlist, 0x0001, 1); - if ((tlv = aim_tlv_gettlv(tlvlist, 0x0002, 1))) { - userinfo->info = (char *)malloc(tlv->length); - memcpy(userinfo->info, tlv->value, tlv->length); - userinfo->info_len = tlv->length; - } - - /* Away message will be 3 and 4 */ - userinfo->away_encoding = aim_tlv_getstr(tlvlist, 0x0003, 1); - if ((tlv = aim_tlv_gettlv(tlvlist, 0x0004, 1))) { - userinfo->away = (char *)malloc(tlv->length); - memcpy(userinfo->away, tlv->value, tlv->length); - userinfo->away_len = tlv->length; - } - - /* Caps will be 5 */ - if ((tlv = aim_tlv_gettlv(tlvlist, 0x0005, 1))) { - aim_bstream_t cbs; - aim_bstream_init(&cbs, tlv->value, tlv->length); - userinfo->capabilities = aim_locate_getcaps(sess, &cbs, tlv->length); - userinfo->present = AIM_USERINFO_PRESENT_CAPABILITIES; - } - aim_tlvlist_free(&tlvlist); - - aim_locate_adduserinfo(sess, userinfo); - userinfo2 = aim_locate_finduserinfo(sess, userinfo->sn); - aim_info_free(userinfo); - free(userinfo); - - /* - * Remove this screen name from our queue. If the client requested - * this buddy's info explicitly, then notify them that we have info - * for this buddy. - */ - was_explicit = aim_locate_gotuserinfo(sess, userinfo2->sn); - if (was_explicit == TRUE) - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, userinfo2); - - return ret; -} - -/* - * Subtype 0x0009 - Set directory profile data. - * - * This is not the same as aim_location_setprofile! - * privacy: 1 to allow searching, 0 to disallow. - * - */ -faim_export int aim_locate_setdirinfo(aim_session_t *sess, 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) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - aim_tlvlist_t *tl = NULL; - - if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC))) - return -EINVAL; - - aim_tlvlist_add_16(&tl, 0x000a, privacy); - - if (first) - aim_tlvlist_add_str(&tl, 0x0001, first); - if (last) - aim_tlvlist_add_str(&tl, 0x0002, last); - if (middle) - aim_tlvlist_add_str(&tl, 0x0003, middle); - if (maiden) - aim_tlvlist_add_str(&tl, 0x0004, maiden); - - if (state) - aim_tlvlist_add_str(&tl, 0x0007, state); - if (city) - aim_tlvlist_add_str(&tl, 0x0008, city); - - if (nickname) - aim_tlvlist_add_str(&tl, 0x000c, nickname); - if (zip) - aim_tlvlist_add_str(&tl, 0x000d, zip); - - if (street) - aim_tlvlist_add_str(&tl, 0x0021, street); - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_tlvlist_size(&tl)))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0002, 0x0009, 0x0000, NULL, 0); - - aim_putsnac(&fr->data, 0x0002, 0x0009, 0x0000, snacid); - aim_tlvlist_write(&fr->data, &tl); - aim_tlvlist_free(&tl); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * Subtype 0x000b - Huh? What is this? - */ -faim_export int aim_locate_000b(aim_session_t *sess, const char *sn) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - - return -EINVAL; - - if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC)) || !sn) - return -EINVAL; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0002, 0x000b, 0x0000, NULL, 0); - - aim_putsnac(&fr->data, 0x0002, 0x000b, 0x0000, snacid); - aimbs_put8(&fr->data, strlen(sn)); - aimbs_putstr(&fr->data, sn); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * Subtype 0x000f - * - * XXX pass these in better - * - */ -faim_export int aim_locate_setinterests(aim_session_t *sess, const char *interest1, const char *interest2, const char *interest3, const char *interest4, const char *interest5, fu16_t privacy) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - aim_tlvlist_t *tl = NULL; - - if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC))) - return -EINVAL; - - /* ?? privacy ?? */ - aim_tlvlist_add_16(&tl, 0x000a, privacy); - - if (interest1) - aim_tlvlist_add_str(&tl, 0x0000b, interest1); - if (interest2) - aim_tlvlist_add_str(&tl, 0x0000b, interest2); - if (interest3) - aim_tlvlist_add_str(&tl, 0x0000b, interest3); - if (interest4) - aim_tlvlist_add_str(&tl, 0x0000b, interest4); - if (interest5) - aim_tlvlist_add_str(&tl, 0x0000b, interest5); - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_tlvlist_size(&tl)))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0002, 0x000f, 0x0000, NULL, 0); - - aim_putsnac(&fr->data, 0x0002, 0x000f, 0x0000, 0); - aim_tlvlist_write(&fr->data, &tl); - aim_tlvlist_free(&tl); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * Subtype 0x0015 - Request the info a user using the short method. This is - * what iChat uses. It normally is VERY leniently rate limited. - * - * @param sn The screen name whose info you wish to request. - * @param flags The bitmask which specifies the type of info you wish to request. - * 0x00000001 - Info/profile. - * 0x00000002 - Away message. - * 0x00000004 - Capabilities. - * 0x00000008 - Certification. - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_locate_getinfoshort(aim_session_t *sess, const char *sn, fu32_t flags) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - - if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC)) || !sn) - return -EINVAL; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4+1+strlen(sn)))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0002, 0x0015, 0x0000, sn, strlen(sn)+1); - - aim_putsnac(&fr->data, 0x0002, 0x0015, 0x0000, snacid); - aimbs_put32(&fr->data, flags); - aimbs_put8(&fr->data, strlen(sn)); - aimbs_putstr(&fr->data, sn); - - 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 == 0x0001) - return error(sess, mod, rx, snac, bs); - else if (snac->subtype == 0x0003) - return rights(sess, mod, rx, snac, bs); - else if (snac->subtype == 0x0006) - return userinfo(sess, mod, rx, snac, bs); - - return 0; -} - -static void locate_shutdown(aim_session_t *sess, aim_module_t *mod) -{ - aim_userinfo_t *del; - - while (sess->locate.userinfo) { - del = sess->locate.userinfo; - sess->locate.userinfo = sess->locate.userinfo->next; - aim_info_free(del); - free(del); - } -} - -faim_internal int locate_modfirst(aim_session_t *sess, aim_module_t *mod) -{ - - mod->family = AIM_CB_FAM_LOC; - mod->version = 0x0001; - mod->toolid = 0x0110; - mod->toolversion = 0x0629; - mod->flags = 0; - strncpy(mod->name, "locate", sizeof(mod->name)); - mod->snachandler = snachandler; - mod->shutdown = locate_shutdown; - - return 0; -} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/misc.c --- a/src/protocols/oscar/misc.c Sat Feb 11 19:16:38 2006 +0000 +++ b/src/protocols/oscar/misc.c Sat Feb 11 21:45:18 2006 +0000 @@ -1,13 +1,32 @@ +/* + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + /* * misc.c * - * Random stuff. Basically just a few functions for sending + * Random stuff. Basically just a few functions for sending * simple SNACs, and then the generic error handler. * */ -#define FAIM_INTERNAL -#include +#include "oscar.h" /* * Generic routine for sending commands. @@ -20,7 +39,7 @@ * back to the single. I don't see any advantage to doing it either way. * */ -faim_internal int aim_genericreq_n(aim_session_t *sess, aim_conn_t *conn, fu16_t family, fu16_t subtype) +faim_internal int aim_genericreq_n(aim_session_t *sess, aim_conn_t *conn, guint16 family, guint16 subtype) { aim_frame_t *fr; aim_snacid_t snacid = 0x00000000; @@ -35,7 +54,7 @@ return 0; } -faim_internal int aim_genericreq_n_snacid(aim_session_t *sess, aim_conn_t *conn, fu16_t family, fu16_t subtype) +faim_internal int aim_genericreq_n_snacid(aim_session_t *sess, aim_conn_t *conn, guint16 family, guint16 subtype) { aim_frame_t *fr; aim_snacid_t snacid; @@ -51,7 +70,7 @@ 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) +faim_internal int aim_genericreq_l(aim_session_t *sess, aim_conn_t *conn, guint16 family, guint16 subtype, guint32 *longdata) { aim_frame_t *fr; aim_snacid_t snacid; @@ -72,7 +91,7 @@ return 0; } -faim_internal int aim_genericreq_s(aim_session_t *sess, aim_conn_t *conn, fu16_t family, fu16_t subtype, fu16_t *shortdata) +faim_internal int aim_genericreq_s(aim_session_t *sess, aim_conn_t *conn, guint16 family, guint16 subtype, guint16 *shortdata) { aim_frame_t *fr; aim_snacid_t snacid; diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/msgcookie.c --- a/src/protocols/oscar/msgcookie.c Sat Feb 11 19:16:38 2006 +0000 +++ b/src/protocols/oscar/msgcookie.c Sat Feb 11 21:45:18 2006 +0000 @@ -1,3 +1,23 @@ +/* + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + /* * Cookie Caching stuff. Adam wrote this, apparently just some * derivatives of n's SNAC work. I cleaned it up, added comments. @@ -11,8 +31,7 @@ * wrong, we get quirky behavior when cookies step on each others' toes. */ -#define FAIM_INTERNAL -#include +#include "oscar.h" /** * aim_cachecookie - appends a cookie to the cookie list @@ -59,7 +78,7 @@ * @param type cookie type to look for * @return if found, returns the struct; if none found (or on error), returns NULL: */ -faim_internal aim_msgcookie_t *aim_uncachecookie(aim_session_t *sess, fu8_t *cookie, int type) +faim_internal aim_msgcookie_t *aim_uncachecookie(aim_session_t *sess, guint8 *cookie, int type) { aim_msgcookie_t *cur, **prev; @@ -87,7 +106,7 @@ * @return returns NULL on error, a pointer to the newly-allocated * cookie on success. */ -faim_internal aim_msgcookie_t *aim_mkcookie(fu8_t *c, int type, void *data) +faim_internal aim_msgcookie_t *aim_mkcookie(guint8 *c, int type, void *data) { aim_msgcookie_t *cookie; @@ -114,7 +133,7 @@ * on success; returns NULL on error/not found */ -faim_internal aim_msgcookie_t *aim_checkcookie(aim_session_t *sess, const fu8_t *cookie, int type) +faim_internal aim_msgcookie_t *aim_checkcookie(aim_session_t *sess, const guint8 *cookie, int type) { aim_msgcookie_t *cur; diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/odir.c --- a/src/protocols/oscar/odir.c Sat Feb 11 19:16:38 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,245 +0,0 @@ -/* - * Family 0x000f - Newer Search Method - * - * Used for searching for other AIM users by email address, name, - * location, commmon interests, and a few other similar things. - * - */ - -#define FAIM_INTERNAL -#include - -/** - * Subtype 0x0002 - Submit a User Search Request - * - * Search for an AIM screen name based on their email address. - * - * @param sess The oscar session. - * @param region Should be "us-ascii" unless you know what you're doing. - * @param email The email address you want to search for. - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_odir_email(aim_session_t *sess, const char *region, const char *email) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - aim_tlvlist_t *tl = NULL; - - if (!sess || !(conn = aim_conn_findbygroup(sess, 0x000f)) || !region || !email) - return -EINVAL; - - /* Create a TLV chain, write it to the outgoing frame, then free the chain */ - aim_tlvlist_add_str(&tl, 0x001c, region); - aim_tlvlist_add_16(&tl, 0x000a, 0x0001); /* Type of search */ - aim_tlvlist_add_str(&tl, 0x0005, email); - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_tlvlist_size(&tl)))) - return -ENOMEM; - snacid = aim_cachesnac(sess, 0x000f, 0x0002, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x000f, 0x0002, 0x0000, snacid); - - aim_tlvlist_write(&fr->data, &tl); - aim_tlvlist_free(&tl); - - aim_tx_enqueue(sess, fr); - - return 0; -} - - -/** - * Subtype 0x0002 - Submit a User Search Request - * - * Search for an AIM screen name based on various info - * about the person. - * - * @param sess The oscar session. - * @param region Should be "us-ascii" unless you know what you're doing. - * @param first The first name of the person you want to search for. - * @param middle The middle name of the person you want to search for. - * @param last The last name of the person you want to search for. - * @param maiden The maiden name of the person you want to search for. - * @param nick The nick name of the person you want to search for. - * @param city The city where the person you want to search for resides. - * @param state The state where the person you want to search for resides. - * @param country The country where the person you want to search for resides. - * @param zip The zip code where the person you want to search for resides. - * @param address The street address where the person you want to seach for resides. - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_odir_name(aim_session_t *sess, const char *region, const char *first, const char *middle, const char *last, const char *maiden, const char *nick, const char *city, const char *state, const char *country, const char *zip, const char *address) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - aim_tlvlist_t *tl = NULL; - - if (!sess || !(conn = aim_conn_findbygroup(sess, 0x000f)) || !region) - return -EINVAL; - - /* Create a TLV chain, write it to the outgoing frame, then free the chain */ - aim_tlvlist_add_str(&tl, 0x001c, region); - aim_tlvlist_add_16(&tl, 0x000a, 0x0000); /* Type of search */ - if (first) - aim_tlvlist_add_str(&tl, 0x0001, first); - if (last) - aim_tlvlist_add_str(&tl, 0x0002, last); - if (middle) - aim_tlvlist_add_str(&tl, 0x0003, middle); - if (maiden) - aim_tlvlist_add_str(&tl, 0x0004, maiden); - if (country) - aim_tlvlist_add_str(&tl, 0x0006, country); - if (state) - aim_tlvlist_add_str(&tl, 0x0007, state); - if (city) - aim_tlvlist_add_str(&tl, 0x0008, city); - if (nick) - aim_tlvlist_add_str(&tl, 0x000c, nick); - if (zip) - aim_tlvlist_add_str(&tl, 0x000d, zip); - if (address) - aim_tlvlist_add_str(&tl, 0x0021, address); - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_tlvlist_size(&tl)))) - return -ENOMEM; - snacid = aim_cachesnac(sess, 0x000f, 0x0002, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x000f, 0x0002, 0x0000, snacid); - - aim_tlvlist_write(&fr->data, &tl); - aim_tlvlist_free(&tl); - - aim_tx_enqueue(sess, fr); - - return 0; -} - - -/** - * Subtype 0x0002 - Submit a User Search Request - * - * @param sess The oscar session. - * @param interest1 An interest you want to search for. - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_odir_interest(aim_session_t *sess, const char *region, const char *interest) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - aim_tlvlist_t *tl = NULL; - - if (!sess || !(conn = aim_conn_findbygroup(sess, 0x000f)) || !region) - return -EINVAL; - - /* Create a TLV chain, write it to the outgoing frame, then free the chain */ - aim_tlvlist_add_str(&tl, 0x001c, region); - aim_tlvlist_add_16(&tl, 0x000a, 0x0001); /* Type of search */ - if (interest) - aim_tlvlist_add_str(&tl, 0x0001, interest); - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_tlvlist_size(&tl)))) - return -ENOMEM; - snacid = aim_cachesnac(sess, 0x000f, 0x0002, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x000f, 0x0002, 0x0000, snacid); - - aim_tlvlist_write(&fr->data, &tl); - aim_tlvlist_free(&tl); - - aim_tx_enqueue(sess, fr); - - return 0; -} - - -/** - * Subtype 0x0003 - Receive Reply From a User Search - * - */ -static int parseresults(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; - fu16_t tmp, numresults; - struct aim_odir *results = NULL; - - tmp = aimbs_get16(bs); /* Unknown */ - tmp = aimbs_get16(bs); /* Unknown */ - aim_bstream_advance(bs, tmp); - - numresults = aimbs_get16(bs); /* Number of results to follow */ - - /* Allocate a linked list, 1 node per result */ - while (numresults) { - struct aim_odir *new; - aim_tlvlist_t *tl = aim_tlvlist_readnum(bs, aimbs_get16(bs)); - new = (struct aim_odir *)malloc(sizeof(struct aim_odir)); - new->first = aim_tlv_getstr(tl, 0x0001, 1); - new->last = aim_tlv_getstr(tl, 0x0002, 1); - new->middle = aim_tlv_getstr(tl, 0x0003, 1); - new->maiden = aim_tlv_getstr(tl, 0x0004, 1); - new->email = aim_tlv_getstr(tl, 0x0005, 1); - new->country = aim_tlv_getstr(tl, 0x0006, 1); - new->state = aim_tlv_getstr(tl, 0x0007, 1); - new->city = aim_tlv_getstr(tl, 0x0008, 1); - new->sn = aim_tlv_getstr(tl, 0x0009, 1); - new->interest = aim_tlv_getstr(tl, 0x000b, 1); - new->nick = aim_tlv_getstr(tl, 0x000c, 1); - new->zip = aim_tlv_getstr(tl, 0x000d, 1); - new->region = aim_tlv_getstr(tl, 0x001c, 1); - new->address = aim_tlv_getstr(tl, 0x0021, 1); - new->next = results; - results = new; - numresults--; - } - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, results); - - /* Now free everything from above */ - while (results) { - struct aim_odir *del = results; - results = results->next; - free(del->first); - free(del->last); - free(del->middle); - free(del->maiden); - free(del->email); - free(del->country); - free(del->state); - free(del->city); - free(del->sn); - free(del->interest); - free(del->nick); - free(del->zip); - free(del->region); - free(del->address); - free(del); - } - - 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 parseresults(sess, mod, rx, snac, bs); - - return 0; -} - -faim_internal int odir_modfirst(aim_session_t *sess, aim_module_t *mod) -{ - - mod->family = 0x000f; - mod->version = 0x0001; - mod->toolid = 0x0010; - mod->toolversion = 0x0629; - mod->flags = 0; - strncpy(mod->name, "odir", sizeof(mod->name)); - mod->snachandler = snachandler; - - return 0; -} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/oscar.c --- a/src/protocols/oscar/oscar.c Sat Feb 11 19:16:38 2006 +0000 +++ b/src/protocols/oscar/oscar.c Sat Feb 11 21:45:18 2006 +0000 @@ -45,7 +45,8 @@ #include "util.h" #include "version.h" -#include "aim.h" +#include "oscar.h" +#include "peer.h" #define OSCAR_STATUS_ID_INVISIBLE "invisible" #define OSCAR_STATUS_ID_OFFLINE "offline" @@ -77,10 +78,10 @@ static int caps_aim = AIM_CAPS_CHAT | AIM_CAPS_BUDDYICON | AIM_CAPS_DIRECTIM | AIM_CAPS_SENDFILE | AIM_CAPS_INTEROPERATE | AIM_CAPS_ICHAT; static int caps_icq = AIM_CAPS_BUDDYICON | AIM_CAPS_DIRECTIM | AIM_CAPS_SENDFILE | AIM_CAPS_ICQUTF8 | AIM_CAPS_INTEROPERATE | AIM_CAPS_ICHAT; -static fu8_t features_aim[] = {0x01, 0x01, 0x01, 0x02}; -static fu8_t features_icq[] = {0x01, 0x06}; -static fu8_t features_icq_offline[] = {0x01}; -static fu8_t ck[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +static guint8 features_aim[] = {0x01, 0x01, 0x01, 0x02}; +static guint8 features_icq[] = {0x01, 0x06}; +static guint8 features_icq_offline[] = {0x01}; +static guint8 ck[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; typedef struct _OscarData OscarData; struct _OscarData { @@ -139,8 +140,8 @@ struct chat_connection { char *name; char *show; /* AOL did something funny to us */ - fu16_t exchange; - fu16_t instance; + guint16 exchange; + guint16 instance; aim_conn_t *conn; int inpa; int id; @@ -165,7 +166,7 @@ GaimConnection *gc; char *sn; char ip[64]; - fu8_t cookie[8]; + guint8 cookie[8]; gboolean donttryagain; }; @@ -178,7 +179,7 @@ */ struct buddyinfo { gboolean typingnot; - fu32_t ipaddr; + guint32 ipaddr; unsigned long ico_me_len; unsigned long ico_me_csum; @@ -325,7 +326,7 @@ g_free(bi); } -static fu32_t oscar_charset_check(const char *utf8) +static guint32 oscar_charset_check(const char *utf8) { int i = 0; int charset = AIM_CHARSET_ASCII; @@ -482,7 +483,7 @@ * charsetstr1 is always set to what the correct encoding should be. */ static gchar * -gaim_plugin_oscar_decode_im_part(GaimAccount *account, const char *sourcesn, fu16_t charset, fu16_t charsubset, const gchar *data, gsize datalen) +gaim_plugin_oscar_decode_im_part(GaimAccount *account, const char *sourcesn, guint16 charset, guint16 charsubset, const gchar *data, gsize datalen) { gchar *ret = NULL; const gchar *charsetstr1, *charsetstr2; @@ -531,7 +532,7 @@ gaim_plugin_oscar_convert_to_best_encoding(GaimConnection *gc, const char *destsn, const gchar *from, gchar **msg, int *msglen_int, - fu16_t *charset, fu16_t *charsubset) + guint16 *charset, guint16 *charsubset) { OscarData *od = gc->proto_data; GaimAccount *account = gaim_connection_get_account(gc); @@ -1120,9 +1121,9 @@ } aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_OFT, - AIM_CB_OFT_DIRECTIMINCOMING, gaim_odc_incoming, 0); + OFT_TYPE_DIRECTIMINCOMING, gaim_odc_incoming, 0); aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_OFT, - AIM_CB_OFT_DIRECTIMTYPING, gaim_odc_typing, 0); + OFT_TYPE_DIRECTIMTYPING, gaim_odc_typing, 0); aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER, gaim_odc_update_ui, 0); @@ -1192,8 +1193,8 @@ g_free(sn); gaim_conversation_write(conv, NULL, buf, GAIM_MESSAGE_SYSTEM, time(NULL)); - aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING, gaim_odc_incoming, 0); - aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING, gaim_odc_typing, 0); + aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, OFT_TYPE_DIRECTIMINCOMING, gaim_odc_incoming, 0); + aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, OFT_TYPE_DIRECTIMTYPING, gaim_odc_typing, 0); aim_conn_addhandler(sess, newconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER, gaim_odc_update_ui, 0); return 1; @@ -1539,7 +1540,7 @@ od->direct_ims = g_slist_append(od->direct_ims, dim); dim->watcher = gaim_input_add(dim->conn->fd, GAIM_INPUT_READ, oscar_callback, dim->conn); - aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIM_ESTABLISHED, + aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_OFT, OFT_TYPE_DIRECTIM_ESTABLISHED, gaim_odc_initiate, 0); conv = gaim_conversation_new(GAIM_CONV_TYPE_IM, dim->gc->account, dim->name); @@ -1864,7 +1865,7 @@ aim_conn_addhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, gaim_connerr, 0); aim_conn_addhandler(sess, conn, 0x0017, 0x0003, gaim_parse_auth_resp, 0); aim_conn_addhandler(sess, conn, 0x0017, 0x0007, gaim_parse_login, 0); - aim_conn_addhandler(sess, conn, AIM_CB_FAM_ATH, AIM_CB_ATH_SECURID_REQUEST, gaim_parse_auth_securid_request, 0); + aim_conn_addhandler(sess, conn, OSCAR_FAMILY_AUTH, OSCAR_SUBTYPE_AUTH_SECURID_REQUEST, gaim_parse_auth_securid_request, 0); conn->status |= AIM_CONN_STATUS_INPROGRESS; if (gaim_proxy_connect(account, gaim_account_get_string(account, "server", OSCAR_DEFAULT_LOGIN_SERVER), @@ -1987,10 +1988,10 @@ * -User chooses a file and oscar_xfer_init is called. It establishes a * listening socket, then asks the remote user to connect to us (and * gives them the file name, port, IP, etc.) - * -They connect to us and we send them an AIM_CB_OFT_PROMPT (this happens + * -They connect to us and we send them an OFT_TYPE_PROMPT (this happens * in oscar_sendfile_estblsh) - * -They send us an AIM_CB_OFT_ACK and then we start sending data - * -When we finish, they send us an AIM_CB_OFT_DONE and they close the + * -They send us an OFT_TYPE_ACK and then we start sending data + * -When we finish, they send us an OFT_TYPE_DONE and they close the * connection. * -We get drunk because file transfer kicks ass. * @@ -1999,10 +2000,10 @@ * -Gaim user selects file to name and location to save file to and * oscar_xfer_init is called * -It connects to the remote user using the IP they gave us earlier - * -After connecting, they send us an AIM_CB_OFT_PROMPT. In reply, we send - * them an AIM_CB_OFT_ACK. + * -After connecting, they send us an OFT_TYPE_PROMPT. In reply, we send + * them an OFT_TYPE_ACK. * -They begin to send us lots of raw data. - * -When they finish sending data we send an AIM_CB_OFT_DONE and then close + * -When they finish sending data we send an OFT_TYPE_DONE and then close * the connection. * * Update August 2005: @@ -2019,7 +2020,7 @@ /* * Miscellaneous xfer functions */ -static GaimXfer *oscar_find_xfer_by_cookie(GSList *fts, const fu8_t *ck) +static GaimXfer *oscar_find_xfer_by_cookie(GSList *fts, const guint8 *ck) { GaimXfer *xfer; struct aim_oft_info *oft_info; @@ -2070,7 +2071,7 @@ if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) { oft_info->fh.nrecvd = gaim_xfer_get_bytes_sent(xfer); - aim_oft_sendheader(oft_info->sess, AIM_CB_OFT_DONE, oft_info); + aim_oft_sendheader(oft_info->sess, OFT_TYPE_DONE, oft_info); } aim_conn_kill(oft_info->sess, &oft_info->conn); @@ -2269,7 +2270,7 @@ if (oft_info->conn) { oft_info->conn->subtype = AIM_CONN_SUBTYPE_OFT_SENDFILE; - aim_conn_addhandler(od->sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_PROMPT, + aim_conn_addhandler(od->sess, oft_info->conn, AIM_CB_FAM_OFT, OFT_TYPE_PROMPT, oscar_sendfile_prompt, 0); rc = gaim_proxy_connect(gaim_connection_get_account(gc), xfer->remote_ip, xfer->remote_port, nextstop_cb, xfer); @@ -2359,15 +2360,15 @@ } /* The following is taken from oscar_sendfile_estblsh */ - aim_conn_addhandler(oft_info->sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_ACK, + aim_conn_addhandler(oft_info->sess, oft_info->conn, AIM_CB_FAM_OFT, OFT_TYPE_ACK, oscar_sendfile_ack, 0); - aim_conn_addhandler(oft_info->sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DONE, + aim_conn_addhandler(oft_info->sess, oft_info->conn, AIM_CB_FAM_OFT, OFT_TYPE_DONE, oscar_sendfile_done, 0); xfer->watcher = gaim_input_add(oft_info->conn->fd, GAIM_INPUT_READ, oscar_callback, oft_info->conn); /* Inform the other user that we are connected and ready to transfer */ - aim_oft_sendheader(oft_info->sess, AIM_CB_OFT_PROMPT, oft_info); + aim_oft_sendheader(oft_info->sess, OFT_TYPE_PROMPT, oft_info); } else if(oft_info->send_or_recv == AIM_XFER_RECV) { oscar_sendfile_connected(xfer, fd, GAIM_INPUT_READ); } else { @@ -2601,7 +2602,7 @@ aim_im_sendch2_sendfile_ask(od->sess, oft_info); aim_conn_addhandler(od->sess, oft_info->conn, AIM_CB_FAM_OFT, - AIM_CB_OFT_ESTABLISHED, oscar_sendfile_estblsh, 0); + OFT_TYPE_ESTABLISHED, oscar_sendfile_estblsh, 0); } else { gaim_xfer_error(GAIM_XFER_SEND, gaim_xfer_get_account(xfer), xfer->who, _("Unable to establish listener socket or no AOL proxy connection present.")); @@ -2898,26 +2899,26 @@ aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_bos, 0); aim_conn_addhandler(sess, bosconn, 0x0009, 0x0003, gaim_bosrights, 0); aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ACK, AIM_CB_ACK_ACK, NULL, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_REDIRECT, gaim_handle_redirect, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_RIGHTSINFO, gaim_parse_locaterights, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_RIGHTSINFO, gaim_parse_buddyrights, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_ONCOMING, gaim_parse_oncoming, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_OFFGOING, gaim_parse_offgoing, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_INCOMING, gaim_parse_incoming_im, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_ERROR, gaim_parse_locerr, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MISSEDCALL, gaim_parse_misses, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_CLIENTAUTORESP, gaim_parse_clientauto, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_RATECHANGE, gaim_parse_ratechange, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_EVIL, gaim_parse_evilnotify, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOK, AIM_CB_LOK_ERROR, gaim_parse_searcherror, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOK, 0x0003, gaim_parse_searchreply, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ERROR, gaim_parse_msgerr, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MTN, gaim_parse_mtn, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO, gaim_parse_userinfo, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_REQUESTINFOTIMEOUT, gaim_reqinfo_timeout, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_GOTINFOBLOCK, gaim_got_infoblock, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ACK, gaim_parse_msgack, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_MOTD, gaim_parse_motd, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_OSERVICE, OSCAR_SUBTYPE_OSERVICE_REDIRECT, gaim_handle_redirect, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_LOCATE, OSCAR_SUBTYPE_LOCATE_RIGHTSINFO, gaim_parse_locaterights, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_BUDDY, OSCAR_SUBTYPE_BUDDY_RIGHTSINFO, gaim_parse_buddyrights, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_BUDDY, OSCAR_SUBTYPE_BUDDY_ONCOMING, gaim_parse_oncoming, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_BUDDY, OSCAR_SUBTYPE_BUDDY_OFFGOING, gaim_parse_offgoing, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_ICBM, OSCAR_SUBTYPE_ICBM_INCOMING, gaim_parse_incoming_im, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_LOCATE, OSCAR_SUBTYPE_LOCATE_ERROR, gaim_parse_locerr, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_ICBM, OSCAR_SUBTYPE_ICBM_MISSEDCALL, gaim_parse_misses, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_ICBM, OSCAR_SUBTYPE_ICBM_CLIENTAUTORESP, gaim_parse_clientauto, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_OSERVICE, OSCAR_SUBTYPE_OSERVICE_RATECHANGE, gaim_parse_ratechange, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_OSERVICE, OSCAR_SUBTYPE_OSERVICE_EVIL, gaim_parse_evilnotify, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_USERLOOKUP, OSCAR_SUBTYPE_USERLOOKUP_ERROR, gaim_parse_searcherror, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_USERLOOKUP, 0x0003, gaim_parse_searchreply, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_ICBM, OSCAR_SUBTYPE_ICBM_ERROR, gaim_parse_msgerr, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_ICBM, OSCAR_SUBTYPE_ICBM_MTN, gaim_parse_mtn, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_LOCATE, OSCAR_SUBTYPE_LOCATE_USERINFO, gaim_parse_userinfo, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_LOCATE, OSCAR_SUBTYPE_LOCATE_REQUESTINFOTIMEOUT, gaim_reqinfo_timeout, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_LOCATE, OSCAR_SUBTYPE_LOCATE_GOTINFOBLOCK, gaim_got_infoblock, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_ICBM, OSCAR_SUBTYPE_ICBM_ACK, gaim_parse_msgack, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_OSERVICE, OSCAR_SUBTYPE_OSERVICE_MOTD, gaim_parse_motd, 0); aim_conn_addhandler(sess, bosconn, 0x0004, 0x0005, gaim_icbm_param_info, 0); aim_conn_addhandler(sess, bosconn, 0x0001, 0x0001, gaim_parse_genericerr, 0); aim_conn_addhandler(sess, bosconn, 0x0003, 0x0001, gaim_parse_genericerr, 0); @@ -2925,21 +2926,21 @@ aim_conn_addhandler(sess, bosconn, 0x0001, 0x001f, gaim_memrequest, 0); aim_conn_addhandler(sess, bosconn, 0x0001, 0x000f, gaim_selfinfo, 0); aim_conn_addhandler(sess, bosconn, 0x0001, 0x0021, oscar_icon_req,0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSG, gaim_offlinemsg, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSGCOMPLETE, gaim_offlinemsgdone, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_POP, 0x0002, gaim_popup, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_ALIAS, gaim_icqalias, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_INFO, gaim_icqinfo, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_ERROR, gaim_ssi_parseerr, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_RIGHTSINFO, gaim_ssi_parserights, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_LIST, gaim_ssi_parselist, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_NOLIST, gaim_ssi_parselist, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_SRVACK, gaim_ssi_parseack, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_ADD, gaim_ssi_parseadd, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_RECVAUTH, gaim_ssi_authgiven, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_RECVAUTHREQ, gaim_ssi_authrequest, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_RECVAUTHREP, gaim_ssi_authreply, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_ADDED, gaim_ssi_gotadded, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_ICQ, OSCAR_SUBTYPE_ICQ_OFFLINEMSG, gaim_offlinemsg, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_ICQ, OSCAR_SUBTYPE_ICQ_OFFLINEMSGCOMPLETE, gaim_offlinemsgdone, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_POPUP, 0x0002, gaim_popup, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_ICQ, OSCAR_SUBTYPE_ICQ_ALIAS, gaim_icqalias, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_ICQ, OSCAR_SUBTYPE_ICQ_INFO, gaim_icqinfo, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_ERROR, gaim_ssi_parseerr, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_RIGHTSINFO, gaim_ssi_parserights, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_LIST, gaim_ssi_parselist, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_NOLIST, gaim_ssi_parselist, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_SRVACK, gaim_ssi_parseack, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_ADD, gaim_ssi_parseadd, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_RECVAUTH, gaim_ssi_authgiven, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_RECVAUTHREQ, gaim_ssi_authrequest, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_RECVAUTHREP, gaim_ssi_authreply, 0); + aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_ADDED, gaim_ssi_gotadded, 0); od->conn = bosconn; for (i = 0; i < (int)strlen(info->bosip); i++) { @@ -3100,12 +3101,12 @@ int gaim_memrequest(aim_session_t *sess, aim_frame_t *fr, ...) { va_list ap; struct pieceofcrap *pos; - fu32_t offset, len; + guint32 offset, len; char *modname; va_start(ap, fr); - offset = va_arg(ap, fu32_t); - len = va_arg(ap, fu32_t); + offset = va_arg(ap, guint32); + len = va_arg(ap, guint32); modname = va_arg(ap, char *); va_end(ap); @@ -3195,11 +3196,11 @@ struct chat_connection *chatcon; static int id = 1; - aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, 0x0001, gaim_parse_genericerr, 0); - aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERJOIN, gaim_conv_chat_join, 0); - aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERLEAVE, gaim_conv_chat_leave, 0); - aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_ROOMINFOUPDATE, gaim_conv_chat_info_update, 0); - aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_INCOMINGMSG, gaim_conv_chat_incoming_msg, 0); + aim_conn_addhandler(sess, fr->conn, OSCAR_FAMILY_CHAT, 0x0001, gaim_parse_genericerr, 0); + aim_conn_addhandler(sess, fr->conn, OSCAR_FAMILY_CHAT, OSCAR_SUBTYPE_CHAT_USERJOIN, gaim_conv_chat_join, 0); + aim_conn_addhandler(sess, fr->conn, OSCAR_FAMILY_CHAT, OSCAR_SUBTYPE_CHAT_USERLEAVE, gaim_conv_chat_leave, 0); + aim_conn_addhandler(sess, fr->conn, OSCAR_FAMILY_CHAT, OSCAR_SUBTYPE_CHAT_ROOMINFOUPDATE, gaim_conv_chat_info_update, 0); + aim_conn_addhandler(sess, fr->conn, OSCAR_FAMILY_CHAT, OSCAR_SUBTYPE_CHAT_INCOMINGMSG, gaim_conv_chat_incoming_msg, 0); aim_clientready(sess, fr->conn); @@ -3213,7 +3214,7 @@ static int conninitdone_chatnav(aim_session_t *sess, aim_frame_t *fr, ...) { aim_conn_addhandler(sess, fr->conn, 0x000d, 0x0001, gaim_parse_genericerr, 0); - aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CTN, AIM_CB_CTN_INFO, gaim_chatnav_info, 0); + aim_conn_addhandler(sess, fr->conn, OSCAR_FAMILY_CHATNAV, OSCAR_SUBTYPE_CHATNAV_INFO, gaim_chatnav_info, 0); aim_clientready(sess, fr->conn); @@ -3225,7 +3226,7 @@ static int conninitdone_email(aim_session_t *sess, aim_frame_t *fr, ...) { aim_conn_addhandler(sess, fr->conn, 0x0018, 0x0001, gaim_parse_genericerr, 0); - aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_EML, AIM_CB_EML_MAILSTATUS, gaim_email_parseupdate, 0); + aim_conn_addhandler(sess, fr->conn, OSCAR_FAMILY_ALERT, OSCAR_SUBTYPE_ALERT_MAILSTATUS, gaim_email_parseupdate, 0); aim_email_sendcookies(sess); aim_email_activate(sess); @@ -3239,8 +3240,8 @@ OscarData *od = gc->proto_data; aim_conn_addhandler(sess, fr->conn, 0x0018, 0x0001, gaim_parse_genericerr, 0); - aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_ICO, AIM_CB_ICO_ERROR, gaim_icon_error, 0); - aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_ICO, AIM_CB_ICO_RESPONSE, gaim_icon_parseicon, 0); + aim_conn_addhandler(sess, fr->conn, OSCAR_FAMILY_BART, OSCAR_SUBTYPE_BART_ERROR, gaim_icon_error, 0); + aim_conn_addhandler(sess, fr->conn, OSCAR_FAMILY_BART, OSCAR_SUBTYPE_BART_RESPONSE, gaim_icon_parseicon, 0); aim_clientready(sess, fr->conn); @@ -3765,7 +3766,7 @@ * This is called after a remote AIM user has connected to us. * If not using a rendezvous proxy, then we want to do some * voodoo with the socket file descriptors. Then we always - * add a callback or two, and then send the AIM_CB_OFT_PROMPT. + * add a callback or two, and then send the OFT_TYPE_PROMPT. */ static int oscar_sendfile_estblsh(aim_session_t *sess, aim_frame_t *fr, ...) { GaimConnection *gc = sess->aux_data; @@ -3812,13 +3813,13 @@ xfer->watcher = gaim_input_add(oft_info->conn->fd, GAIM_INPUT_READ, oscar_callback, oft_info->conn); if(oft_info->send_or_recv == AIM_XFER_SEND) { - aim_conn_addhandler(oft_info->sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_ACK, + aim_conn_addhandler(oft_info->sess, oft_info->conn, AIM_CB_FAM_OFT, OFT_TYPE_ACK, oscar_sendfile_ack, 0); - aim_conn_addhandler(oft_info->sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DONE, + aim_conn_addhandler(oft_info->sess, oft_info->conn, AIM_CB_FAM_OFT, OFT_TYPE_DONE, oscar_sendfile_done, 0); /* Inform the other user that we are connected and ready to transfer */ - aim_oft_sendheader(sess, AIM_CB_OFT_PROMPT, oft_info); + aim_oft_sendheader(sess, OFT_TYPE_PROMPT, oft_info); } /* For a file send, we'll hopefully end up in oscar_sendfile_ack next @@ -3874,13 +3875,13 @@ /* Don't wait around if this is a redirected send */ if(oft_info->send_or_recv == AIM_XFER_SEND) { /* We should only get here if this is a redirected file send */ - aim_conn_addhandler(oft_info->sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_ACK, + aim_conn_addhandler(oft_info->sess, oft_info->conn, AIM_CB_FAM_OFT, OFT_TYPE_ACK, oscar_sendfile_ack, 0); - aim_conn_addhandler(oft_info->sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DONE, + aim_conn_addhandler(oft_info->sess, oft_info->conn, AIM_CB_FAM_OFT, OFT_TYPE_DONE, oscar_sendfile_done, 0); /* Inform the other user that we are ready to transfer */ - aim_oft_sendheader(oft_info->sess, AIM_CB_OFT_PROMPT, oft_info); + aim_oft_sendheader(oft_info->sess, OFT_TYPE_PROMPT, oft_info); } } @@ -3897,14 +3898,14 @@ struct aim_oft_info *oft_info; va_list ap; aim_conn_t *conn; - fu8_t *cookie; + guint8 *cookie; struct aim_fileheader_t *fh; gaim_debug_info("oscar", "AAA - in oscar_sendfile_prompt\n"); va_start(ap, fr); conn = va_arg(ap, aim_conn_t *); - cookie = va_arg(ap, fu8_t *); + cookie = va_arg(ap, guint8 *); fh = va_arg(ap, struct aim_fileheader_t *); va_end(ap); @@ -3925,7 +3926,7 @@ memcpy(&oft_info->fh.bcookie, oft_info->cookie, 8); /* XXX - convert the name from UTF-8 to UCS-2 if necessary, and pass the encoding to the call below */ - aim_oft_sendheader(oft_info->sess, AIM_CB_OFT_ACK, oft_info); + aim_oft_sendheader(oft_info->sess, OFT_TYPE_ACK, oft_info); gaim_xfer_start(xfer, xfer->fd, NULL, 0); return 0; @@ -3941,13 +3942,13 @@ GaimXfer *xfer; va_list ap; aim_conn_t *conn; - fu8_t *cookie; + guint8 *cookie; struct aim_fileheader_t *fh; gaim_debug_info("oscar", "AAA - in oscar_sendfile_ack\n"); va_start(ap, fr); conn = va_arg(ap, aim_conn_t *); - cookie = va_arg(ap, fu8_t *); + cookie = va_arg(ap, guint8 *); fh = va_arg(ap, struct aim_fileheader_t *); va_end(ap); @@ -3973,14 +3974,14 @@ GaimXfer *xfer; va_list ap; aim_conn_t *conn; - fu8_t *cookie; + guint8 *cookie; struct aim_fileheader_t *fh; struct aim_oft_info *oft_info; gaim_debug_info("oscar", "AAA - in oscar_sendfile_done\n"); va_start(ap, fr); conn = va_arg(ap, aim_conn_t *); - cookie = va_arg(ap, fu8_t *); + cookie = va_arg(ap, guint8 *); fh = va_arg(ap, struct aim_fileheader_t *); va_end(ap); @@ -4840,13 +4841,13 @@ } static int gaim_parse_incoming_im(aim_session_t *sess, aim_frame_t *fr, ...) { - fu16_t channel; + guint16 channel; int ret = 0; aim_userinfo_t *userinfo; va_list ap; va_start(ap, fr); - channel = (fu16_t)va_arg(ap, unsigned int); + channel = (guint16)va_arg(ap, unsigned int); userinfo = va_arg(ap, aim_userinfo_t *); switch (channel) { @@ -4885,14 +4886,14 @@ GaimAccount *account = gaim_connection_get_account(gc); char *buf; va_list ap; - fu16_t chan, nummissed, reason; + guint16 chan, nummissed, reason; aim_userinfo_t *userinfo; va_start(ap, fr); - chan = (fu16_t)va_arg(ap, unsigned int); + chan = (guint16)va_arg(ap, unsigned int); userinfo = va_arg(ap, aim_userinfo_t *); - nummissed = (fu16_t)va_arg(ap, unsigned int); - reason = (fu16_t)va_arg(ap, unsigned int); + nummissed = (guint16)va_arg(ap, unsigned int); + reason = (guint16)va_arg(ap, unsigned int); va_end(ap); switch(reason) { @@ -4959,7 +4960,7 @@ return 1; } -static int gaim_parse_clientauto_ch2(aim_session_t *sess, const char *who, fu16_t reason, const guchar *cookie) { +static int gaim_parse_clientauto_ch2(aim_session_t *sess, const char *who, guint16 reason, const guchar *cookie) { GaimConnection *gc = sess->aux_data; OscarData *od = gc->proto_data; @@ -4991,7 +4992,7 @@ return 0; } -static int gaim_parse_clientauto_ch4(aim_session_t *sess, char *who, fu16_t reason, fu32_t state, char *msg) { +static int gaim_parse_clientauto_ch4(aim_session_t *sess, char *who, guint16 reason, guint32 state, char *msg) { GaimConnection *gc = sess->aux_data; switch(reason) { @@ -5025,22 +5026,22 @@ static int gaim_parse_clientauto(aim_session_t *sess, aim_frame_t *fr, ...) { va_list ap; - fu16_t chan, reason; + guint16 chan, reason; char *who; va_start(ap, fr); - chan = (fu16_t)va_arg(ap, unsigned int); + chan = (guint16)va_arg(ap, unsigned int); who = va_arg(ap, char *); - reason = (fu16_t)va_arg(ap, unsigned int); + reason = (guint16)va_arg(ap, unsigned int); if (chan == 0x0002) { /* File transfer declined */ guchar *cookie = va_arg(ap, guchar *); return gaim_parse_clientauto_ch2(sess, who, reason, cookie); } else if (chan == 0x0004) { /* ICQ message */ - fu32_t state = 0; + guint32 state = 0; char *msg = NULL; if (reason == 0x0003) { - state = va_arg(ap, fu32_t); + state = va_arg(ap, guint32); msg = va_arg(ap, char *); } return gaim_parse_clientauto_ch4(sess, who, reason, state, msg); @@ -5053,11 +5054,11 @@ static int gaim_parse_genericerr(aim_session_t *sess, aim_frame_t *fr, ...) { va_list ap; - fu16_t reason; + guint16 reason; char *m; va_start(ap, fr); - reason = (fu16_t) va_arg(ap, unsigned int); + reason = (guint16) va_arg(ap, unsigned int); va_end(ap); gaim_debug_error("oscar", @@ -5079,11 +5080,11 @@ GaimXfer *xfer; #endif va_list ap; - fu16_t reason; + guint16 reason; char *data, *buf; va_start(ap, fr); - reason = (fu16_t)va_arg(ap, unsigned int); + reason = (guint16)va_arg(ap, unsigned int); data = va_arg(ap, char *); va_end(ap); @@ -5116,13 +5117,13 @@ static int gaim_parse_mtn(aim_session_t *sess, aim_frame_t *fr, ...) { GaimConnection *gc = sess->aux_data; va_list ap; - fu16_t type1, type2; + guint16 type1, type2; char *sn; va_start(ap, fr); - type1 = (fu16_t) va_arg(ap, unsigned int); + type1 = (guint16) va_arg(ap, unsigned int); sn = va_arg(ap, char *); - type2 = (fu16_t) va_arg(ap, unsigned int); + type2 = (guint16) va_arg(ap, unsigned int); va_end(ap); switch (type2) { @@ -5153,11 +5154,11 @@ static int gaim_parse_locerr(aim_session_t *sess, aim_frame_t *fr, ...) { gchar *buf; va_list ap; - fu16_t reason; + guint16 reason; char *destn; va_start(ap, fr); - reason = (fu16_t) va_arg(ap, unsigned int); + reason = (guint16) va_arg(ap, unsigned int); destn = va_arg(ap, char *); va_end(ap); @@ -5327,11 +5328,11 @@ static int gaim_parse_motd(aim_session_t *sess, aim_frame_t *fr, ...) { char *msg; - fu16_t id; + guint16 id; va_list ap; va_start(ap, fr); - id = (fu16_t) va_arg(ap, unsigned int); + id = (guint16) va_arg(ap, unsigned int); msg = va_arg(ap, char *); va_end(ap); @@ -5346,20 +5347,20 @@ static int gaim_chatnav_info(aim_session_t *sess, aim_frame_t *fr, ...) { va_list ap; - fu16_t type; + guint16 type; GaimConnection *gc = sess->aux_data; OscarData *od = (OscarData *)gc->proto_data; va_start(ap, fr); - type = (fu16_t) va_arg(ap, unsigned int); + type = (guint16) va_arg(ap, unsigned int); switch(type) { case 0x0002: { - fu8_t maxrooms; + guint8 maxrooms; struct aim_chat_exchangeinfo *exchanges; int exchangecount, i; - maxrooms = (fu8_t) va_arg(ap, unsigned int); + maxrooms = (guint8) va_arg(ap, unsigned int); exchangecount = va_arg(ap, int); exchanges = va_arg(ap, struct aim_chat_exchangeinfo *); @@ -5386,19 +5387,19 @@ break; case 0x0008: { char *fqcn, *name, *ck; - fu16_t instance, flags, maxmsglen, maxoccupancy, unknown, exchange; - fu8_t createperms; - fu32_t createtime; + guint16 instance, flags, maxmsglen, maxoccupancy, unknown, exchange; + guint8 createperms; + guint32 createtime; fqcn = va_arg(ap, char *); - instance = (fu16_t)va_arg(ap, unsigned int); - exchange = (fu16_t)va_arg(ap, unsigned int); - flags = (fu16_t)va_arg(ap, unsigned int); - createtime = va_arg(ap, fu32_t); - maxmsglen = (fu16_t)va_arg(ap, unsigned int); - maxoccupancy = (fu16_t)va_arg(ap, unsigned int); - createperms = (fu8_t)va_arg(ap, unsigned int); - unknown = (fu16_t)va_arg(ap, unsigned int); + instance = (guint16)va_arg(ap, unsigned int); + exchange = (guint16)va_arg(ap, unsigned int); + flags = (guint16)va_arg(ap, unsigned int); + createtime = va_arg(ap, guint32); + maxmsglen = (guint16)va_arg(ap, unsigned int); + maxoccupancy = (guint16)va_arg(ap, unsigned int); + createperms = (guint8)va_arg(ap, unsigned int); + unknown = (guint16)va_arg(ap, unsigned int); name = va_arg(ap, char *); ck = va_arg(ap, char *); @@ -5476,8 +5477,8 @@ char *roomname; int usercount; char *roomdesc; - fu16_t unknown_c9, unknown_d2, unknown_d5, maxmsglen, maxvisiblemsglen; - fu32_t creationtime; + guint16 unknown_c9, unknown_d2, unknown_d5, maxmsglen, maxvisiblemsglen; + guint32 creationtime; GaimConnection *gc = sess->aux_data; struct chat_connection *ccon = find_oscar_chat_by_conn(gc, fr->conn); @@ -5487,12 +5488,12 @@ usercount= va_arg(ap, int); userinfo = va_arg(ap, aim_userinfo_t *); roomdesc = va_arg(ap, char *); - unknown_c9 = (fu16_t)va_arg(ap, unsigned int); - creationtime = va_arg(ap, fu32_t); - maxmsglen = (fu16_t)va_arg(ap, unsigned int); - unknown_d2 = (fu16_t)va_arg(ap, unsigned int); - unknown_d5 = (fu16_t)va_arg(ap, unsigned int); - maxvisiblemsglen = (fu16_t)va_arg(ap, unsigned int); + unknown_c9 = (guint16)va_arg(ap, unsigned int); + creationtime = va_arg(ap, guint32); + maxmsglen = (guint16)va_arg(ap, unsigned int); + unknown_d2 = (guint16)va_arg(ap, unsigned int); + unknown_d5 = (guint16)va_arg(ap, unsigned int); + maxvisiblemsglen = (guint16)va_arg(ap, unsigned int); va_end(ap); gaim_debug_misc("oscar", @@ -5585,15 +5586,15 @@ GSList *cur; va_list ap; char *sn; - fu8_t iconcsumtype, *iconcsum, *icon; - fu16_t iconcsumlen, iconlen; + guint8 iconcsumtype, *iconcsum, *icon; + guint16 iconcsumlen, iconlen; va_start(ap, fr); sn = va_arg(ap, char *); iconcsumtype = va_arg(ap, int); - iconcsum = va_arg(ap, fu8_t *); + iconcsum = va_arg(ap, guint8 *); iconcsumlen = va_arg(ap, int); - icon = va_arg(ap, fu8_t *); + icon = va_arg(ap, guint8 *); iconlen = va_arg(ap, int); va_end(ap); @@ -5697,11 +5698,11 @@ */ static int gaim_parse_msgack(aim_session_t *sess, aim_frame_t *fr, ...) { va_list ap; - fu16_t type; + guint16 type; char *sn; va_start(ap, fr); - type = (fu16_t) va_arg(ap, unsigned int); + type = (guint16) va_arg(ap, unsigned int); sn = va_arg(ap, char *); va_end(ap); @@ -5719,19 +5720,19 @@ "limit cleared", }; va_list ap; - fu16_t code, rateclass; - fu32_t windowsize, clear, alert, limit, disconnect, currentavg, maxavg; + guint16 code, rateclass; + guint32 windowsize, clear, alert, limit, disconnect, currentavg, maxavg; va_start(ap, fr); - code = (fu16_t)va_arg(ap, unsigned int); - rateclass= (fu16_t)va_arg(ap, unsigned int); - windowsize = va_arg(ap, fu32_t); - clear = va_arg(ap, fu32_t); - alert = va_arg(ap, fu32_t); - limit = va_arg(ap, fu32_t); - disconnect = va_arg(ap, fu32_t); - currentavg = va_arg(ap, fu32_t); - maxavg = va_arg(ap, fu32_t); + code = (guint16)va_arg(ap, unsigned int); + rateclass= (guint16)va_arg(ap, unsigned int); + windowsize = va_arg(ap, guint32); + clear = va_arg(ap, guint32); + alert = va_arg(ap, guint32); + limit = va_arg(ap, guint32); + disconnect = va_arg(ap, guint32); + currentavg = va_arg(ap, guint32); + maxavg = va_arg(ap, guint32); va_end(ap); gaim_debug_misc("oscar", @@ -5765,11 +5766,11 @@ static int gaim_parse_evilnotify(aim_session_t *sess, aim_frame_t *fr, ...) { va_list ap; - fu16_t newevil; + guint16 newevil; aim_userinfo_t *userinfo; va_start(ap, fr); - newevil = (fu16_t) va_arg(ap, unsigned int); + newevil = (guint16) va_arg(ap, unsigned int); userinfo = va_arg(ap, aim_userinfo_t *); va_end(ap); @@ -5809,11 +5810,11 @@ GaimConnection *gc = sess->aux_data; OscarData *od = gc->proto_data; va_list ap; - fu16_t code; + guint16 code; char *msg; va_start(ap, fr); - code = (fu16_t)va_arg(ap, int); + code = (guint16)va_arg(ap, int); msg = va_arg(ap, char *); va_end(ap); @@ -5879,9 +5880,9 @@ GaimConnection *gc = sess->aux_data; OscarData *od = gc->proto_data; - aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_ADM, 0x0003, gaim_info_change, 0); - aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_ADM, 0x0005, gaim_info_change, 0); - aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_ADM, 0x0007, gaim_account_confirm, 0); + aim_conn_addhandler(sess, fr->conn, OSCAR_FAMILY_ADMIN, 0x0003, gaim_info_change, 0); + aim_conn_addhandler(sess, fr->conn, OSCAR_FAMILY_ADMIN, 0x0005, gaim_info_change, 0); + aim_conn_addhandler(sess, fr->conn, OSCAR_FAMILY_ADMIN, 0x0007, gaim_account_confirm, 0); aim_clientready(sess, fr->conn); gaim_debug_info("oscar", "connected to admin\n"); @@ -5955,10 +5956,10 @@ GaimAccount *account = gaim_connection_get_account(gc); OscarData *od = (OscarData *)gc->proto_data; va_list ap; - fu16_t maxsiglen; + guint16 maxsiglen; va_start(ap, fr); - maxsiglen = (fu16_t) va_arg(ap, int); + maxsiglen = (guint16) va_arg(ap, int); va_end(ap); gaim_debug_misc("oscar", @@ -5978,13 +5979,13 @@ static int gaim_parse_buddyrights(aim_session_t *sess, aim_frame_t *fr, ...) { va_list ap; - fu16_t maxbuddies, maxwatchers; + guint16 maxbuddies, maxwatchers; GaimConnection *gc = sess->aux_data; OscarData *od = (OscarData *)gc->proto_data; va_start(ap, fr); - maxbuddies = (fu16_t) va_arg(ap, unsigned int); - maxwatchers = (fu16_t) va_arg(ap, unsigned int); + maxbuddies = (guint16) va_arg(ap, unsigned int); + maxwatchers = (guint16) va_arg(ap, unsigned int); va_end(ap); gaim_debug_misc("oscar", @@ -6004,15 +6005,15 @@ const char *message; char *tmp; va_list ap; - fu16_t maxpermits, maxdenies; + guint16 maxpermits, maxdenies; gc = sess->aux_data; od = (OscarData *)gc->proto_data; account = gaim_connection_get_account(gc); va_start(ap, fr); - maxpermits = (fu16_t) va_arg(ap, unsigned int); - maxdenies = (fu16_t) va_arg(ap, unsigned int); + maxpermits = (guint16) va_arg(ap, unsigned int); + maxdenies = (guint16) va_arg(ap, unsigned int); va_end(ap); gaim_debug_misc("oscar", @@ -6229,14 +6230,14 @@ gchar *text; va_list ap; char *msg, *url; - fu16_t wid, hei, delay; + guint16 wid, hei, delay; va_start(ap, fr); msg = va_arg(ap, char *); url = va_arg(ap, char *); - wid = (fu16_t) va_arg(ap, int); - hei = (fu16_t) va_arg(ap, int); - delay = (fu16_t) va_arg(ap, int); + wid = (guint16) va_arg(ap, int); + hei = (guint16) va_arg(ap, int); + delay = (guint16) va_arg(ap, int); va_end(ap); text = g_strdup_printf("%s
%s", msg, url, url); @@ -6320,12 +6321,12 @@ static int gaim_account_confirm(aim_session_t *sess, aim_frame_t *fr, ...) { GaimConnection *gc = sess->aux_data; - fu16_t status; + guint16 status; va_list ap; char msg[256]; va_start(ap, fr); - status = (fu16_t) va_arg(ap, unsigned int); /* status code of confirmation request */ + status = (guint16) va_arg(ap, unsigned int); /* status code of confirmation request */ va_end(ap); gaim_debug_info("oscar", @@ -6343,14 +6344,14 @@ static int gaim_info_change(aim_session_t *sess, aim_frame_t *fr, ...) { GaimConnection *gc = sess->aux_data; va_list ap; - fu16_t perms, err; + guint16 perms, err; char *url, *sn, *email; int change; va_start(ap, fr); change = va_arg(ap, int); - perms = (fu16_t) va_arg(ap, unsigned int); - err = (fu16_t) va_arg(ap, unsigned int); + perms = (guint16) va_arg(ap, unsigned int); + err = (guint16) va_arg(ap, unsigned int); url = va_arg(ap, char *); sn = va_arg(ap, char *); email = va_arg(ap, char *); @@ -6651,7 +6652,7 @@ GaimAccount *account; GaimStatus *status; const gchar *status_id; - fu32_t data = 0x00000000; + guint32 data = 0x00000000; od = gc->proto_data; account = gaim_connection_get_account(gc); @@ -6950,10 +6951,10 @@ GaimConnection *gc = sess->aux_data; OscarData *od = gc->proto_data; va_list ap; - fu16_t reason; + guint16 reason; va_start(ap, fr); - reason = (fu16_t)va_arg(ap, unsigned int); + reason = (guint16)va_arg(ap, unsigned int); va_end(ap); gaim_debug_error("oscar", "ssi: SNAC error %hu\n", reason); @@ -6983,11 +6984,11 @@ int i; va_list ap; int numtypes; - fu16_t *maxitems; + guint16 *maxitems; va_start(ap, fr); numtypes = va_arg(ap, int); - maxitems = va_arg(ap, fu16_t *); + maxitems = va_arg(ap, guint16 *); va_end(ap); gaim_debug_misc("oscar", "ssi rights:"); @@ -7018,21 +7019,21 @@ GaimGroup *g; GaimBuddy *b; struct aim_ssi_item *curitem; - fu32_t tmp; + guint32 tmp; va_list ap; - fu16_t fmtver, numitems; + guint16 fmtver, numitems; struct aim_ssi_item *items; - fu32_t timestamp; + guint32 timestamp; gc = sess->aux_data; od = gc->proto_data; account = gaim_connection_get_account(gc); va_start(ap, fr); - fmtver = (fu16_t)va_arg(ap, int); - numitems = (fu16_t)va_arg(ap, int); + fmtver = (guint16)va_arg(ap, int); + numitems = (guint16)va_arg(ap, int); items = va_arg(ap, struct aim_ssi_item *); - timestamp = va_arg(ap, fu32_t); + timestamp = va_arg(ap, guint32); va_end(ap); /* Don't attempt to re-request our buddy list later */ @@ -7207,7 +7208,7 @@ case 0x0004: { /* Permit/deny setting */ if (curitem->data) { - fu8_t permdeny; + guint8 permdeny; if ((permdeny = aim_ssi_getpermdeny(sess->ssi.local)) && (permdeny != account->perm_deny)) { gaim_debug_info("oscar", "ssi: changing permdeny from %d to %hhu\n", account->perm_deny, permdeny); @@ -7264,7 +7265,7 @@ } case 0x000e: { /* buddy requires authorization */ - if ((retval->action == AIM_CB_SSI_ADD) && (retval->name)) + if ((retval->action == OSCAR_SUBTYPE_FEEDBAG_ADD) && (retval->name)) gaim_auth_sendrequest(gc, retval->name); } break; @@ -7290,11 +7291,11 @@ GaimBuddy *b; GaimGroup *g; va_list ap; - fu16_t type; + guint16 type; const char *name; va_start(ap, fr); - type = (fu16_t)va_arg(ap, int); + type = (guint16)va_arg(ap, int); name = va_arg(ap, char *); va_end(ap); @@ -7429,12 +7430,12 @@ va_list ap; char *sn, *msg; gchar *dialog_msg, *nombre; - fu8_t reply; + guint8 reply; GaimBuddy *buddy; va_start(ap, fr); sn = va_arg(ap, char *); - reply = (fu8_t)va_arg(ap, int); + reply = (guint8)va_arg(ap, int); msg = va_arg(ap, char *); va_end(ap); @@ -7582,7 +7583,7 @@ GaimConversation *conv = NULL; struct chat_connection *c = NULL; char *buf, *buf2; - fu16_t charset, charsubset; + guint16 charset, charsubset; char *charsetstr = NULL; int len; @@ -7824,8 +7825,8 @@ GaimConnection *gc = sess->aux_data; OscarData *od = gc->proto_data; va_list ap; - fu16_t type; - fu8_t flags = 0, length = 0; + guint16 type; + guint8 flags = 0, length = 0; guchar *md5 = NULL; va_start(ap, fr); @@ -8527,7 +8528,7 @@ GaimConnection *gc = data; OscarData *od = gc->proto_data; aim_session_t *sess = od->sess; - fu32_t presence; + guint32 presence; presence = aim_ssi_getpresence(sess->ssi.local); diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/oscar.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/oscar/oscar.h Sat Feb 11 21:45:18 2006 +0000 @@ -0,0 +1,1494 @@ +/* + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * Main libfaim header. Must be included in client for prototypes/macros. + * + * "come on, i turned a chick lesbian; i think this is the hackish equivalent" + * -- Josh Myer + * + */ + +#ifndef _OSCAR_H_ +#define _OSCAR_H_ + +#include "snactypes.h" + +#include "debug.h" +#include "internal.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#include +#include +#include +#include +#else +#include "libc_interface.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef guint32 aim_snacid_t; +typedef guint16 flap_seqnum_t; + +#define WIN32_STATIC +#if defined(_WIN32) && !defined(WIN32_STATIC) +/* + * For a win32 DLL, we define WIN32_INDLL if this file + * is included while compiling the DLL. If it's not + * defined (it's included in a client app), the symbols + * will be imported instead of exported. + */ +#ifdef WIN32_INDLL +#define faim_export __declspec(dllexport) +#else +#define faim_export __declspec(dllimport) +#endif /* WIN32_INDLL */ +#define faim_internal +#else +/* + * Nothing normally needed for unix... + */ +#define faim_export +#define faim_internal +#endif + +#ifndef FALSE +#define FALSE (0) +#endif + +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +#define FAIM_SNAC_HASH_SIZE 16 + +/* + * Current Maximum Length for Screen Names (not including NULL) + * + * Currently only names up to 16 characters can be registered + * however it is apparently legal for them to be larger. + */ +#define MAXSNLEN 97 + +/* + * Current Maximum Length for Instant Messages + * + * This was found basically by experiment, but not wholly + * accurate experiment. It should not be regarded + * as completely correct. But its a decent approximation. + * + * Note that although we can send this much, its impossible + * for WinAIM clients (up through the latest (4.0.1957)) to + * send any more than 1kb. Amaze all your windows friends + * with utterly oversized instant messages! + * + * XXX: the real limit is the total SNAC size at 8192. Fix this. + * + */ +#define MAXMSGLEN 7987 + +/* + * Maximum size of a Buddy Icon. + */ +#define MAXICONLEN 7168 +#define AIM_ICONIDENT "AVT1picture.id" + +/* + * Current Maximum Length for Chat Room Messages + * + * This is actually defined by the protocol to be + * dynamic, but I have yet to see due cause to + * define it dynamically here. Maybe later. + * + */ +#define MAXCHATMSGLEN 512 + +/** + * Maximum length for the password of an ICQ account + */ +#define MAXICQPASSLEN 8 + +#define AIM_MD5_STRING "AOL Instant Messenger (SM)" + +/* + * Client info. Filled in by the client and passed in to + * aim_send_login(). The information ends up getting passed to OSCAR + * through the initial login command. + * + */ +struct client_info_s { + const char *clientstring; + guint16 clientid; + guint16 major; + guint16 minor; + guint16 point; + guint16 build; + guint32 distrib; + const char *country; /* two-letter abbrev */ + const char *lang; /* two-letter abbrev */ +}; + +/* Needs to be checked */ +#define CLIENTINFO_AIM_3_5_1670 { \ + "AOL Instant Messenger (SM), version 3.5.1670/WIN32", \ + 0x0004, \ + 0x0003, 0x0005, \ + 0x0000, 0x0686, \ + 0x0000002a, \ + "us", "en", \ +} + +/* Needs to be checked */ +/* Latest winaim without ssi */ +#define CLIENTINFO_AIM_4_1_2010 { \ + "AOL Instant Messenger (SM), version 4.1.2010/WIN32", \ + 0x0004, \ + 0x0004, 0x0001, \ + 0x0000, 0x07da, \ + 0x0000004b, \ + "us", "en", \ +} + +/* Needs to be checked */ +#define CLIENTINFO_AIM_4_3_2188 { \ + "AOL Instant Messenger (SM), version 4.3.2188/WIN32", \ + 0x0109, \ + 0x0400, 0x0003, \ + 0x0000, 0x088c, \ + 0x00000086, \ + "us", "en", \ +} + +/* Needs to be checked */ +#define CLIENTINFO_AIM_4_8_2540 { \ + "AOL Instant Messenger (SM), version 4.8.2540/WIN32", \ + 0x0109, \ + 0x0004, 0x0008, \ + 0x0000, 0x09ec, \ + 0x000000af, \ + "us", "en", \ +} + +/* Needs to be checked */ +#define CLIENTINFO_AIM_5_0_2938 { \ + "AOL Instant Messenger, version 5.0.2938/WIN32", \ + 0x0109, \ + 0x0005, 0x0000, \ + 0x0000, 0x0b7a, \ + 0x00000000, \ + "us", "en", \ +} + +#define CLIENTINFO_AIM_5_1_3036 { \ + "AOL Instant Messenger, version 5.1.3036/WIN32", \ + 0x0109, \ + 0x0005, 0x0001, \ + 0x0000, 0x0bdc, \ + 0x000000d2, \ + "us", "en", \ +} + +#define CLIENTINFO_AIM_5_5_3415 { \ + "AOL Instant Messenger, version 5.5.3415/WIN32", \ + 0x0109, \ + 0x0005, 0x0005, \ + 0x0000, 0x0057, \ + 0x000000ef, \ + "us", "en", \ +} + +#define CLIENTINFO_AIM_5_9_3702 { \ + "AOL Instant Messenger, version 5.9.3702/WIN32", \ + 0x0109, \ + 0x0005, 0x0009, \ + 0x0000, 0x0e76, \ + 0x00000111, \ + "us", "en", \ +} + +#define CLIENTINFO_ICHAT_1_0 { \ + "Apple iChat", \ + 0x311a, \ + 0x0001, 0x0000, \ + 0x0000, 0x003c, \ + 0x000000c6, \ + "us", "en", \ +} + +/* Needs to be checked */ +#define CLIENTINFO_ICQ_4_65_3281 { \ + "ICQ Inc. - Product of ICQ (TM) 2000b.4.65.1.3281.85", \ + 0x010a, \ + 0x0004, 0x0041, \ + 0x0001, 0x0cd1, \ + 0x00000055, \ + "us", "en", \ +} + +/* Needs to be checked */ +#define CLIENTINFO_ICQ_5_34_3728 { \ + "ICQ Inc. - Product of ICQ (TM).2002a.5.34.1.3728.85", \ + 0x010a, \ + 0x0005, 0x0022, \ + 0x0001, 0x0e8f, \ + 0x00000055, \ + "us", "en", \ +} + +#define CLIENTINFO_ICQ_5_45_3777 { \ + "ICQ Inc. - Product of ICQ (TM).2003a.5.45.1.3777.85", \ + 0x010a, \ + 0x0005, 0x002d, \ + 0x0001, 0x0ec1, \ + 0x00000055, \ + "us", "en", \ +} + +#define CLIENTINFO_ICQBASIC_14_3_1068 { \ + "ICQBasic", \ + 0x010a, \ + 0x0014, 0x0003, \ + 0x0000, 0x042c, \ + 0x0000043d, \ + "us", "en", \ +} + +#define CLIENTINFO_NETSCAPE_7_0_1 { \ + "Netscape 2000 an approved user of AOL Instant Messenger (SM)", \ + 0x1d0d, \ + 0x0007, 0x0000, \ + 0x0001, 0x0000, \ + 0x00000058, \ + "us", "en", \ +} + +#define CLIENTINFO_GAIM { \ + "Gaim/" VERSION, \ + 0x0109, \ + 0x0005, 0x0001, \ + 0x0000, 0x0bdc, \ + 0x000000d2, \ + "us", "en", \ +} + +#define CLIENTINFO_AIM_KNOWNGOOD CLIENTINFO_AIM_5_1_3036 +#define CLIENTINFO_ICQ_KNOWNGOOD CLIENTINFO_ICQ_5_45_3777 + +/* + * These could be arbitrary, but its easier to use the actual AIM values + */ +#define AIM_CONN_TYPE_BOS 0x0002 +#define AIM_CONN_TYPE_ADS 0x0005 +#define AIM_CONN_TYPE_AUTH 0x0007 +#define AIM_CONN_TYPE_CHATNAV 0x000d +#define AIM_CONN_TYPE_CHAT 0x000e +#define AIM_CONN_TYPE_SEARCH 0x000f +#define AIM_CONN_TYPE_ICON 0x0010 +#define AIM_CONN_TYPE_EMAIL 0x0018 + +/* they start getting arbitrary for rendezvous stuff =) */ +#define AIM_CONN_TYPE_RENDEZVOUS_PROXY 0xfffd /* these speak a strange language */ +#define AIM_CONN_TYPE_RENDEZVOUS 0xfffe /* these do not speak FLAP! */ +#define AIM_CONN_TYPE_LISTENER 0xffff /* socket waiting for accept() */ + +/* Command types for doing a rendezvous proxy login + * Thanks to Keith Lea and the Joust project for documenting these commands well */ +#define AIM_RV_PROXY_PACKETVER_DFLT 0x044a +#define AIM_RV_PROXY_ERROR 0x0001 +#define AIM_RV_PROXY_INIT_SEND 0x0002 /* First command sent when creating a connection */ +#define AIM_RV_PROXY_INIT_RECV 0x0004 /* First command sent when receiving existing connection */ +#define AIM_RV_PROXY_ACK 0x0003 +#define AIM_RV_PROXY_READY 0x0005 + +/* Number of bytes expected in each of the above packets, including the 2 bytes specifying length */ +#define AIM_RV_PROXY_ERROR_LEN 14 +#define AIM_RV_PROXY_INIT_SEND_LEN 55 +#define AIM_RV_PROXY_INIT_RECV_LEN 57 +#define AIM_RV_PROXY_ACK_LEN 18 +#define AIM_RV_PROXY_READY_LEN 12 +#define AIM_RV_PROXY_HDR_LEN 12 /* Bytes in just the header alone */ + +/* Default values for unknown/unused values in rendezvous proxy negotiation packets */ +#define AIM_RV_PROXY_SERVER_FLAGS 0x0220 /* Default flags sent by proxy server */ +#define AIM_RV_PROXY_CLIENT_FLAGS 0x0000 /* Default flags sent by client */ +#define AIM_RV_PROXY_UNKNOWNA_DFLT 0x00000000 /* Default value for an unknown block */ +#define AIM_RV_PROXY_SERVER_URL "ars.oscar.aol.com" +#define AIM_RV_PROXY_CONNECT_PORT 5190 /* The port we should always connect to */ + +/* What is the purpose of this transfer? (Who will end up with a new file?) + * These values are used in oft_info->send_or_recv */ +#define AIM_XFER_SEND 0x0001 +#define AIM_XFER_RECV 0x0002 + +/* Via what method is the data getting routed? + * These values are used in oft_info->method */ +#define AIM_XFER_DIRECT 0x0001 /* Direct connection; receiver connects to sender */ +#define AIM_XFER_REDIR 0x0002 /* Redirected connection; sender connects to receiver */ +#define AIM_XFER_PROXY 0x0003 /* Proxied connection */ + +/* Who requested the proxy? + * The difference between a stage 2 and stage 3 proxied transfer is that the receiver does the + * initial login for a stage 2, but the sender must do it for a stage 3. + * These values are used in oft_info->stage */ +#define AIM_XFER_PROXY_NONE 0x0001 +#define AIM_XFER_PROXY_STG1 0x0002 /* Sender requested a proxy be used (stage1) */ +#define AIM_XFER_PROXY_STG2 0x0003 /* Receiver requested a proxy be used (stage2) */ +#define AIM_XFER_PROXY_STG3 0x0004 /* Receiver requested a proxy be used (stage3) */ + +/* + * Subtypes, we need these for OFT stuff. + */ +#define AIM_CONN_SUBTYPE_OFT_DIRECTIM 0x0001 +#define AIM_CONN_SUBTYPE_OFT_GETFILE 0x0002 +#define AIM_CONN_SUBTYPE_OFT_SENDFILE 0x0003 +#define AIM_CONN_SUBTYPE_OFT_BUDDYICON 0x0004 +#define AIM_CONN_SUBTYPE_OFT_VOICE 0x0005 + +/* + * Status values returned from aim_conn_new(). ORed together. + */ +#define AIM_CONN_STATUS_READY 0x0001 +#define AIM_CONN_STATUS_INTERNALERR 0x0002 +#define AIM_CONN_STATUS_RESOLVERR 0x0040 +#define AIM_CONN_STATUS_CONNERR 0x0080 +#define AIM_CONN_STATUS_INPROGRESS 0x0100 + +#define AIM_FRAMETYPE_FLAP 0x0000 +#define AIM_FRAMETYPE_OFT 0x0001 + +typedef struct aim_conn_s { + int fd; + guint16 type; + guint16 subtype; + flap_seqnum_t seqnum; + guint32 status; + void *priv; /* misc data the client may want to store */ + void *internal; /* internal conn-specific libfaim data */ + time_t lastactivity; /* time of last transmit */ + int forcedlatency; + void *handlerlist; + void *sessv; /* pointer to parent session */ + void *inside; /* only accessible from inside libfaim */ + struct aim_conn_s *next; +} aim_conn_t; + +/* + * Byte Stream type. Sort of. + * + * Use of this type serves a couple purposes: + * - Buffer/buflen pairs are passed all around everywhere. This turns + * that into one value, as well as abstracting it slightly. + * - Through the abstraction, it is possible to enable bounds checking + * for robustness at the cost of performance. But a clean failure on + * weird packets is much better than a segfault. + * - I like having variables named "bs". + * + * Don't touch the insides of this struct. Or I'll have to kill you. + * + */ +typedef struct aim_bstream_s { + guint8 *data; + guint32 len; + guint32 offset; +} aim_bstream_t; + +typedef struct aim_frame_s { + guint8 hdrtype; /* defines which piece of the union to use */ + union { + struct { + guint8 channel; + flap_seqnum_t seqnum; + } flap; + struct { + guint8 magic[4]; /* ODC2 or OFT2 */ + guint16 hdrlen; + guint16 type; + } rend; + } hdr; + aim_bstream_t data; /* payload stream */ + aim_conn_t *conn; /* the connection it came in on/is going out on */ + guint8 handled; /* 0 = new, !0 = been handled */ + struct aim_frame_s *next; +} aim_frame_t; + +typedef struct aim_msgcookie_s { + guchar cookie[8]; + int type; + void *data; + time_t addtime; + struct aim_msgcookie_s *next; +} aim_msgcookie_t; + +/* + * AIM Session: The main client-data interface. + * + */ +typedef struct aim_session_s { + + /* ---- Client Accessible ------------------------ */ + + /* Our screen name. */ + char sn[MAXSNLEN+1]; + + /* + * Pointer to anything the client wants to + * explicitly associate with this session. + * + * This is for use in the callbacks mainly. In any + * callback, you can access this with sess->aux_data. + * + */ + void *aux_data; + + /* ---- Internal Use Only ------------------------ */ + + /* Connection information */ + aim_conn_t *connlist; + + /* + * Transmit/receive queues. + * + * These are only used when you don't use your own lowlevel + * I/O. I don't suggest that you use libfaim's internal I/O. + * Its really bad and the API/event model is quirky at best. + * + */ + aim_frame_t *queue_outgoing; + aim_frame_t *queue_incoming; + + /* + * Tx Enqueuing function. + * + * This is how you override the transmit direction of libfaim's + * internal I/O. This function will be called whenever it needs + * to send something. + * + */ + int (*tx_enqueue)(struct aim_session_s *, aim_frame_t *); + + void *modlistv; + + struct { + char server[128]; + char username[128]; + char password[128]; + } socksproxy; + + guint8 nonblocking; + + /* + * Outstanding snac handling + * + * XXX: Should these be per-connection? -mid + */ + void *snac_hash[FAIM_SNAC_HASH_SIZE]; + aim_snacid_t snacid_next; + + aim_msgcookie_t *msgcookies; + struct aim_icq_info *icq_info; + struct aim_oft_info *oft_info; + struct aim_authresp_info *authinfo; + struct aim_emailinfo *emailinfo; + + struct { + struct aim_userinfo_s *userinfo; + struct userinfo_node *torequest; + struct userinfo_node *requested; + int waiting_for_response; + } locate; + + /* Server-stored information (ssi) */ + struct { + int received_data; + guint16 numitems; + struct aim_ssi_item *official; + struct aim_ssi_item *local; + struct aim_ssi_tmp *pending; + time_t timestamp; + int waiting_for_ack; + } ssi; +} aim_session_t; + +/* Valid for calling aim_icq_setstatus() and for aim_userinfo_t->icqinfo.status */ +#define AIM_ICQ_STATE_NORMAL 0x00000000 +#define AIM_ICQ_STATE_AWAY 0x00000001 +#define AIM_ICQ_STATE_DND 0x00000002 +#define AIM_ICQ_STATE_OUT 0x00000004 +#define AIM_ICQ_STATE_BUSY 0x00000010 +#define AIM_ICQ_STATE_CHAT 0x00000020 +#define AIM_ICQ_STATE_INVISIBLE 0x00000100 +#define AIM_ICQ_STATE_WEBAWARE 0x00010000 +#define AIM_ICQ_STATE_HIDEIP 0x00020000 +#define AIM_ICQ_STATE_BIRTHDAY 0x00080000 +#define AIM_ICQ_STATE_DIRECTDISABLED 0x00100000 +#define AIM_ICQ_STATE_ICQHOMEPAGE 0x00200000 +#define AIM_ICQ_STATE_DIRECTREQUIREAUTH 0x10000000 +#define AIM_ICQ_STATE_DIRECTCONTACTLIST 0x20000000 + +/* + * Get command from connections + * + * aim_get_commmand() is the libfaim lowlevel I/O in the receive direction. + * XXX Make this easily overridable. + * + */ +faim_export int aim_get_command(aim_session_t *, aim_conn_t *); + +/* + * Dispatch commands that are in the rx queue. + */ +faim_export void aim_rxdispatch(aim_session_t *); + +faim_export int aim_debugconn_sendconnect(aim_session_t *sess, aim_conn_t *conn); + +faim_export int aim_logoff(aim_session_t *); + +/* the library should never call aim_conn_kill */ +faim_export void aim_conn_kill(aim_session_t *sess, aim_conn_t **deadconn); + +typedef int (*aim_rxcallback_t)(aim_session_t *, aim_frame_t *, ...); + + +/* auth.c */ +struct aim_clientrelease { + char *name; + guint32 build; + char *url; + char *info; +}; + +struct aim_authresp_info { + char *sn; + guint16 errorcode; + char *errorurl; + guint16 regstatus; + char *email; + char *bosip; + guint16 cookielen; + guint8 *cookie; + char *chpassurl; + struct aim_clientrelease latestrelease; + struct aim_clientrelease latestbeta; +}; + +/* Callback data for redirect. */ +struct aim_redirect_data { + guint16 group; + const char *ip; + guint16 cookielen; + const guint8 *cookie; + struct { /* group == AIM_CONN_TYPE_CHAT */ + guint16 exchange; + const char *room; + guint16 instance; + } chat; +}; + +faim_export int aim_clientready(aim_session_t *sess, aim_conn_t *conn); +faim_export int aim_sendflapver(aim_session_t *sess, aim_conn_t *conn); +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); +/* 0x000b */ faim_export int aim_auth_securid_send(aim_session_t *sess, const char *securid); + +faim_export void aim_purge_rxqueue(aim_session_t *); +faim_export void aim_cleansnacs(aim_session_t *, int maxage); + +#define AIM_TX_QUEUED 0 /* default */ +#define AIM_TX_IMMEDIATE 1 +#define AIM_TX_USER 2 +faim_export int aim_tx_setenqueue(aim_session_t *sess, int what, int (*func)(aim_session_t *, aim_frame_t *)); + +faim_export int aim_tx_flushqueue(aim_session_t *); +faim_export void aim_tx_purgequeue(aim_session_t *); + +faim_export int aim_conn_setlatency(aim_conn_t *conn, int newval); + +faim_export int aim_conn_addhandler(aim_session_t *, aim_conn_t *conn, guint16 family, guint16 type, aim_rxcallback_t newhandler, guint16 flags); +faim_export int aim_clearhandlers(aim_conn_t *conn); + +faim_export aim_conn_t *aim_conn_findbygroup(aim_session_t *sess, guint16 group); +faim_export aim_session_t *aim_conn_getsess(aim_conn_t *conn); +faim_export void aim_conn_close(aim_conn_t *deadconn); +faim_export aim_conn_t *aim_newconn(aim_session_t *, int type); +faim_export int aim_conn_in_sess(aim_session_t *sess, aim_conn_t *conn); +faim_export int aim_conn_isready(aim_conn_t *); +faim_export int aim_conn_setstatus(aim_conn_t *, int); +faim_export int aim_conn_completeconnect(aim_session_t *sess, aim_conn_t *conn); +faim_export int aim_conn_isconnecting(aim_conn_t *conn); + +faim_export void aim_session_init(aim_session_t *, guint8 nonblocking); +faim_export void aim_session_kill(aim_session_t *); +faim_export aim_conn_t *aim_getconn_type(aim_session_t *, int type); +faim_export aim_conn_t *aim_getconn_type_all(aim_session_t *, int type); +faim_export aim_conn_t *aim_getconn_fd(aim_session_t *, int fd); + +/* 0x0001 - service.c */ +faim_export int aim_srv_setstatusmsg(aim_session_t *sess, const char *msg); +faim_export int aim_srv_setidle(aim_session_t *sess, guint32 idletime); + +/* misc.c */ + +#define AIM_VISIBILITYCHANGE_PERMITADD 0x05 +#define AIM_VISIBILITYCHANGE_PERMITREMOVE 0x06 +#define AIM_VISIBILITYCHANGE_DENYADD 0x07 +#define AIM_VISIBILITYCHANGE_DENYREMOVE 0x08 + +#define AIM_PRIVFLAGS_ALLOWIDLE 0x01 +#define AIM_PRIVFLAGS_ALLOWMEMBERSINCE 0x02 + +#define AIM_WARN_ANON 0x01 + +faim_export int aim_sendpauseack(aim_session_t *sess, aim_conn_t *conn); +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_changevisibility(aim_session_t *, aim_conn_t *, int, const char *); +faim_export int aim_bos_setgroupperm(aim_session_t *, aim_conn_t *, guint32 mask); +faim_export int aim_bos_setprivacyflags(aim_session_t *, aim_conn_t *, guint32); +faim_export int aim_reqpersonalinfo(aim_session_t *, aim_conn_t *); +faim_export int aim_reqservice(aim_session_t *, aim_conn_t *, guint16); +faim_export int aim_bos_reqrights(aim_session_t *, aim_conn_t *); +faim_export int aim_setextstatus(aim_session_t *sess, guint32 status); + +#define AIM_CLIENTTYPE_UNKNOWN 0x0000 +#define AIM_CLIENTTYPE_MC 0x0001 +#define AIM_CLIENTTYPE_WINAIM 0x0002 +#define AIM_CLIENTTYPE_WINAIM41 0x0003 +#define AIM_CLIENTTYPE_AOL_TOC 0x0004 +faim_export guint16 aim_im_fingerprint(const guint8 *msghdr, int len); + +#define AIM_RATE_CODE_CHANGE 0x0001 +#define AIM_RATE_CODE_WARNING 0x0002 +#define AIM_RATE_CODE_LIMIT 0x0003 +#define AIM_RATE_CODE_CLEARLIMIT 0x0004 +faim_export int aim_ads_requestads(aim_session_t *sess, aim_conn_t *conn); + + + +/* im.c */ +#define AIM_OFT_SUBTYPE_SEND_FILE 0x0001 +#define AIM_OFT_SUBTYPE_SEND_DIR 0x0002 +#define AIM_OFT_SUBTYPE_GET_FILE 0x0011 +#define AIM_OPT_SUBTYPE_GET_LIST 0x0012 + +#define AIM_TRANSFER_DENY_NOTSUPPORTED 0x0000 +#define AIM_TRANSFER_DENY_DECLINE 0x0001 +#define AIM_TRANSFER_DENY_NOTACCEPTING 0x0002 + +#define AIM_IMPARAM_FLAG_CHANMSGS_ALLOWED 0x00000001 +#define AIM_IMPARAM_FLAG_MISSEDCALLS_ENABLED 0x00000002 + +/* This is what the server will give you if you don't set them yourself. */ +#define AIM_IMPARAM_DEFAULTS { \ + 0, \ + AIM_IMPARAM_FLAG_CHANMSGS_ALLOWED | AIM_IMPARAM_FLAG_MISSEDCALLS_ENABLED, \ + 512, /* !! Note how small this is. */ \ + (99.9)*10, (99.9)*10, \ + 1000 /* !! And how large this is. */ \ +} + +/* This is what most AIM versions use. */ +#define AIM_IMPARAM_REASONABLE { \ + 0, \ + AIM_IMPARAM_FLAG_CHANMSGS_ALLOWED | AIM_IMPARAM_FLAG_MISSEDCALLS_ENABLED, \ + 8000, \ + (99.9)*10, (99.9)*10, \ + 0 \ +} + +struct aim_icbmparameters { + guint16 maxchan; + guint32 flags; /* AIM_IMPARAM_FLAG_ */ + guint16 maxmsglen; /* message size that you will accept */ + guint16 maxsenderwarn; /* this and below are *10 (999=99.9%) */ + guint16 maxrecverwarn; + guint32 minmsginterval; /* in milliseconds? */ +}; + +struct aim_chat_roominfo { + guint16 exchange; + char *name; + guint16 instance; +}; + +#define AIM_IMFLAGS_AWAY 0x0001 /* mark as an autoreply */ +#define AIM_IMFLAGS_ACK 0x0002 /* request a receipt notice */ +#define AIM_IMFLAGS_BUDDYREQ 0x0010 /* buddy icon requested */ +#define AIM_IMFLAGS_HASICON 0x0020 /* already has icon */ +#define AIM_IMFLAGS_SUBENC_MACINTOSH 0x0040 /* damn that Steve Jobs! */ +#define AIM_IMFLAGS_CUSTOMFEATURES 0x0080 /* features field present */ +#define AIM_IMFLAGS_EXTDATA 0x0100 +#define AIM_IMFLAGS_X 0x0200 +#define AIM_IMFLAGS_MULTIPART 0x0400 /* ->mpmsg section valid */ +#define AIM_IMFLAGS_OFFLINE 0x0800 /* send to offline user */ +#define AIM_IMFLAGS_TYPINGNOT 0x1000 /* typing notification */ + +#define AIM_CHARSET_ASCII 0x0000 +#define AIM_CHARSET_UNICODE 0x0002 /* UCS-2BE */ +#define AIM_CHARSET_CUSTOM 0x0003 + +/* + * Multipart message structures. + */ +typedef struct aim_mpmsg_section_s { + guint16 charset; + guint16 charsubset; + gchar *data; + guint16 datalen; + struct aim_mpmsg_section_s *next; +} aim_mpmsg_section_t; + +typedef struct aim_mpmsg_s { + unsigned int numparts; + aim_mpmsg_section_t *parts; +} aim_mpmsg_t; + +faim_export int aim_mpmsg_init(aim_session_t *sess, aim_mpmsg_t *mpm); +faim_export int aim_mpmsg_addraw(aim_session_t *sess, aim_mpmsg_t *mpm, guint16 charset, guint16 charsubset, const gchar *data, guint16 datalen); +faim_export int aim_mpmsg_addascii(aim_session_t *sess, aim_mpmsg_t *mpm, const char *ascii); +faim_export int aim_mpmsg_addunicode(aim_session_t *sess, aim_mpmsg_t *mpm, const guint16 *unicode, guint16 unicodelen); +faim_export void aim_mpmsg_free(aim_session_t *sess, aim_mpmsg_t *mpm); + +/* + * Arguments to aim_send_im_ext(). + * + * This is really complicated. But immensely versatile. + * + */ +struct aim_sendimext_args { + + /* These are _required_ */ + const char *destsn; + guint32 flags; /* often 0 */ + + /* Only required if not using multipart messages */ + const char *msg; + int msglen; + + /* Required if ->msg is not provided */ + aim_mpmsg_t *mpmsg; + + /* Only used if AIM_IMFLAGS_HASICON is set */ + guint32 iconlen; + time_t iconstamp; + guint32 iconsum; + + /* Only used if AIM_IMFLAGS_CUSTOMFEATURES is set */ + guint16 featureslen; + guint8 *features; + + /* Only used if AIM_IMFLAGS_CUSTOMCHARSET is set and mpmsg not used */ + guint16 charset; + guint16 charsubset; +}; + +/* + * Arguments to aim_send_rtfmsg(). + */ +struct aim_sendrtfmsg_args { + const char *destsn; + guint32 fgcolor; + guint32 bgcolor; + const char *rtfmsg; /* must be in RTF */ +}; + +/* + * This information is provided in the Incoming ICBM callback for + * Channel 1 ICBM's. + * + * Note that although CUSTOMFEATURES and CUSTOMCHARSET say they + * are optional, both are always set by the current libfaim code. + * That may or may not change in the future. It is mainly for + * consistency with aim_sendimext_args. + * + * Multipart messages require some explanation. If you want to use them, + * I suggest you read all the comments in im.c. + * + */ +struct aim_incomingim_ch1_args { + + /* Always provided */ + aim_mpmsg_t mpmsg; + guint32 icbmflags; /* some flags apply only to ->msg, not all mpmsg */ + + /* Only provided if message has a human-readable section */ + gchar *msg; + int msglen; + + /* Only provided if AIM_IMFLAGS_HASICON is set */ + time_t iconstamp; + guint32 iconlen; + guint16 iconsum; + + /* Only provided if AIM_IMFLAGS_CUSTOMFEATURES is set */ + guint8 *features; + guint8 featureslen; + + /* Only provided if AIM_IMFLAGS_EXTDATA is set */ + guint8 extdatalen; + guint8 *extdata; + + /* Only used if AIM_IMFLAGS_CUSTOMCHARSET is set */ + guint16 charset; + guint16 charsubset; +}; + +/* Valid values for channel 2 args->status */ +#define AIM_RENDEZVOUS_PROPOSE 0x0000 +#define AIM_RENDEZVOUS_CANCEL 0x0001 +#define AIM_RENDEZVOUS_ACCEPT 0x0002 + +struct aim_incomingim_ch2_args { + guint16 status; + guchar cookie[8]; + int reqclass; + const char *proxyip; + const char *clientip; + const char *verifiedip; + guint16 port; + guint16 errorcode; + const char *msg; /* invite message or file description */ + guint16 msglen; + const char *encoding; + const char *language; + union { + struct { + guint32 checksum; + guint32 length; + time_t timestamp; + guint8 *icon; + } icon; + struct { + struct aim_chat_roominfo roominfo; + } chat; + struct { + guint16 msgtype; + guint32 fgcolor; + guint32 bgcolor; + const char *rtfmsg; + } rtfmsg; + struct { + guint16 subtype; + guint16 totfiles; + guint32 totsize; + char *filename; + /* reqnum: 0x0001 usually; 0x0002 = reply request for stage 2 proxy transfer */ + guint16 reqnum; + guint8 use_proxy; /* Did the user request that we use a rv proxy? */ + } sendfile; + } info; + void *destructor; /* used internally only */ +}; + +/* Valid values for channel 4 args->type */ +#define AIM_ICQMSG_AUTHREQUEST 0x0006 +#define AIM_ICQMSG_AUTHDENIED 0x0007 +#define AIM_ICQMSG_AUTHGRANTED 0x0008 + +struct aim_incomingim_ch4_args { + guint32 uin; /* Of the sender of the ICBM */ + guint8 type; + guint8 flags; + gchar *msg; /* Reason for auth request, deny, or accept */ + int msglen; +}; + +/* SNAC sending functions */ +/* 0x0002 */ faim_export int aim_im_setparams(aim_session_t *sess, struct aim_icbmparameters *params); +/* 0x0004 */ faim_export int aim_im_reqparams(aim_session_t *sess); +/* 0x0006 */ faim_export int aim_im_sendch1_ext(aim_session_t *sess, struct aim_sendimext_args *args); +/* 0x0006 */ faim_export int aim_im_sendch1(aim_session_t *, const char *destsn, guint16 flags, const char *msg); +/* 0x0006 */ faim_export int aim_im_sendch2_chatinvite(aim_session_t *sess, const char *sn, const char *msg, guint16 exchange, const char *roomname, guint16 instance); +/* 0x0006 */ faim_export int aim_im_sendch2_icon(aim_session_t *sess, const char *sn, const guint8 *icon, int iconlen, time_t stamp, guint16 iconsum); +/* 0x0006 */ faim_export int aim_im_sendch2_rtfmsg(aim_session_t *sess, struct aim_sendrtfmsg_args *args); +/* 0x0006 */ faim_export int aim_im_sendch2_odcrequest(aim_session_t *sess, guchar *cookie, gboolean usecookie, const char *sn, const guint8 *ip, guint16 port); +/* 0x0006 */ faim_export int aim_im_sendch2_sendfile_ask(aim_session_t *sess, struct aim_oft_info *oft_info); +/* 0x0006 */ faim_export int aim_im_sendch2_sendfile_accept(aim_session_t *sess, struct aim_oft_info *info); +/* 0x0006 */ faim_export int aim_im_sendch2_sendfile_cancel(aim_session_t *sess, struct aim_oft_info *oft_info); +/* 0x0006 */ faim_export int aim_im_sendch2_geticqaway(aim_session_t *sess, const char *sn, int type); +/* 0x0006 */ faim_export int aim_im_sendch4(aim_session_t *sess, const char *sn, guint16 type, const char *message); +/* 0x0008 */ faim_export int aim_im_warn(aim_session_t *sess, aim_conn_t *conn, const char *destsn, guint32 flags); +/* 0x000b */ faim_export int aim_im_denytransfer(aim_session_t *sess, const char *sender, const guchar *cookie, guint16 code); +/* 0x0014 */ faim_export int aim_im_sendmtn(aim_session_t *sess, guint16 type1, const char *sn, guint16 type2); +faim_export void aim_icbm_makecookie(guchar* cookie); + + +/* 0x0002 - locate.c */ +/* + * AIM User Info, Standard Form. + */ +#define AIM_FLAG_UNCONFIRMED 0x0001 /* "damned transients" */ +#define AIM_FLAG_ADMINISTRATOR 0x0002 +#define AIM_FLAG_AOL 0x0004 +#define AIM_FLAG_OSCAR_PAY 0x0008 +#define AIM_FLAG_FREE 0x0010 +#define AIM_FLAG_AWAY 0x0020 +#define AIM_FLAG_ICQ 0x0040 +#define AIM_FLAG_WIRELESS 0x0080 +#define AIM_FLAG_UNKNOWN100 0x0100 +#define AIM_FLAG_UNKNOWN200 0x0200 +#define AIM_FLAG_ACTIVEBUDDY 0x0400 +#define AIM_FLAG_UNKNOWN800 0x0800 +#define AIM_FLAG_ABINTERNAL 0x1000 +#define AIM_FLAG_ALLUSERS 0x001f + +#define AIM_USERINFO_PRESENT_FLAGS 0x00000001 +#define AIM_USERINFO_PRESENT_MEMBERSINCE 0x00000002 +#define AIM_USERINFO_PRESENT_ONLINESINCE 0x00000004 +#define AIM_USERINFO_PRESENT_IDLE 0x00000008 +#define AIM_USERINFO_PRESENT_ICQEXTSTATUS 0x00000010 +#define AIM_USERINFO_PRESENT_ICQIPADDR 0x00000020 +#define AIM_USERINFO_PRESENT_ICQDATA 0x00000040 +#define AIM_USERINFO_PRESENT_CAPABILITIES 0x00000080 +#define AIM_USERINFO_PRESENT_SESSIONLEN 0x00000100 +#define AIM_USERINFO_PRESENT_CREATETIME 0x00000200 + +struct userinfo_node { + char *sn; + struct userinfo_node *next; +}; + +typedef struct aim_userinfo_s { + char *sn; + guint16 warnlevel; /* evil percent * 10 (999 = 99.9%) */ + guint16 idletime; /* in seconds */ + guint16 flags; + guint32 createtime; /* time_t */ + guint32 membersince; /* time_t */ + guint32 onlinesince; /* time_t */ + guint32 sessionlen; /* in seconds */ + guint32 capabilities; + struct { + guint32 status; + guint32 ipaddr; + guint8 crap[0x25]; /* until we figure it out... */ + } icqinfo; + guint32 present; + + guint8 iconcsumtype; + guint16 iconcsumlen; + guint8 *iconcsum; + + char *info; + char *info_encoding; + guint16 info_len; + + char *status; + char *status_encoding; + guint16 status_len; + + char *away; + char *away_encoding; + guint16 away_len; + + struct aim_userinfo_s *next; +} aim_userinfo_t; + +#define AIM_CAPS_BUDDYICON 0x00000001 +#define AIM_CAPS_TALK 0x00000002 +#define AIM_CAPS_DIRECTIM 0x00000004 +#define AIM_CAPS_CHAT 0x00000008 +#define AIM_CAPS_GETFILE 0x00000010 +#define AIM_CAPS_SENDFILE 0x00000020 +#define AIM_CAPS_GAMES 0x00000040 +#define AIM_CAPS_ADDINS 0x00000080 +#define AIM_CAPS_SENDBUDDYLIST 0x00000100 +#define AIM_CAPS_GAMES2 0x00000200 +#define AIM_CAPS_ICQ_DIRECT 0x00000400 +#define AIM_CAPS_APINFO 0x00000800 +#define AIM_CAPS_ICQRTF 0x00001000 +#define AIM_CAPS_EMPTY 0x00002000 +#define AIM_CAPS_ICQSERVERRELAY 0x00004000 +#define AIM_CAPS_ICQUTF8OLD 0x00008000 +#define AIM_CAPS_TRILLIANCRYPT 0x00010000 +#define AIM_CAPS_ICQUTF8 0x00020000 +#define AIM_CAPS_INTEROPERATE 0x00040000 +#define AIM_CAPS_ICHAT 0x00080000 +#define AIM_CAPS_HIPTOP 0x00100000 +#define AIM_CAPS_SECUREIM 0x00200000 +#define AIM_CAPS_SMS 0x00400000 +#define AIM_CAPS_GENERICUNKNOWN 0x00800000 +#define AIM_CAPS_VIDEO 0x01000000 +#define AIM_CAPS_ICHATAV 0x02000000 +#define AIM_CAPS_LIVEVIDEO 0x04000000 +#define AIM_CAPS_CAMERA 0x08000000 +#define AIM_CAPS_LAST 0x10000000 + +#define AIM_SENDMEMBLOCK_FLAG_ISREQUEST 0 +#define AIM_SENDMEMBLOCK_FLAG_ISHASH 1 + +faim_export int aim_sendmemblock(aim_session_t *sess, aim_conn_t *conn, guint32 offset, guint32 len, const guint8 *buf, guint8 flag); + +struct aim_invite_priv { + char *sn; + char *roomname; + guint16 exchange; + guint16 instance; +}; + +#define AIM_COOKIETYPE_UNKNOWN 0x00 +#define AIM_COOKIETYPE_ICBM 0x01 +#define AIM_COOKIETYPE_ADS 0x02 +#define AIM_COOKIETYPE_BOS 0x03 +#define AIM_COOKIETYPE_IM 0x04 +#define AIM_COOKIETYPE_CHAT 0x05 +#define AIM_COOKIETYPE_CHATNAV 0x06 +#define AIM_COOKIETYPE_INVITE 0x07 +/* we'll move OFT up a bit to give breathing room. not like it really + * matters. */ +#define AIM_COOKIETYPE_OFTIM 0x10 +#define AIM_COOKIETYPE_OFTGET 0x11 +#define AIM_COOKIETYPE_OFTSEND 0x12 +#define AIM_COOKIETYPE_OFTVOICE 0x13 +#define AIM_COOKIETYPE_OFTIMAGE 0x14 +#define AIM_COOKIETYPE_OFTICON 0x15 + +faim_export aim_userinfo_t *aim_locate_finduserinfo(aim_session_t *sess, const char *sn); +faim_export void aim_locate_dorequest(aim_session_t *sess); + +/* 0x0002 */ faim_export int aim_locate_reqrights(aim_session_t *sess); +/* 0x0004 */ faim_export int aim_locate_setcaps(aim_session_t *sess, guint32 caps); +/* 0x0004 */ faim_export int aim_locate_setprofile(aim_session_t *sess, const char *profile_encoding, const gchar *profile, const int profile_len, const char *awaymsg_encoding, const gchar *awaymsg, const int awaymsg_len); +/* 0x0005 */ faim_export int aim_locate_getinfo(aim_session_t *sess, const char *, guint16); +/* 0x0009 */ faim_export int aim_locate_setdirinfo(aim_session_t *sess, 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, guint16 privacy); +/* 0x000b */ faim_export int aim_locate_000b(aim_session_t *sess, const char *sn); +/* 0x000f */ faim_export int aim_locate_setinterests(aim_session_t *sess, const char *interest1, const char *interest2, const char *interest3, const char *interest4, const char *interest5, guint16 privacy); +/* 0x0015 */ faim_export int aim_locate_getinfoshort(aim_session_t *sess, const char *sn, guint32 flags); + + + +/* 0x0003 - buddylist.c */ +/* 0x0002 */ faim_export int aim_buddylist_reqrights(aim_session_t *, aim_conn_t *); +/* 0x0004 */ faim_export int aim_buddylist_set(aim_session_t *, aim_conn_t *, const char *); +/* 0x0004 */ faim_export int aim_buddylist_addbuddy(aim_session_t *, aim_conn_t *, const char *); +/* 0x0005 */ faim_export int aim_buddylist_removebuddy(aim_session_t *, aim_conn_t *, const char *); +/* 0x000b */ faim_export int aim_buddylist_oncoming(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *info); +/* 0x000c */ faim_export int aim_buddylist_offgoing(aim_session_t *sess, aim_conn_t *conn, const char *sn); + + + +/* 0x000a - search.c */ +faim_export int aim_search_address(aim_session_t *, aim_conn_t *, const char *); + + + +/* 0x000d - chatnav.c */ +/* 0x000e - chat.c */ +/* These apply to exchanges as well. */ +#define AIM_CHATROOM_FLAG_EVILABLE 0x0001 +#define AIM_CHATROOM_FLAG_NAV_ONLY 0x0002 +#define AIM_CHATROOM_FLAG_INSTANCING_ALLOWED 0x0004 +#define AIM_CHATROOM_FLAG_OCCUPANT_PEEK_ALLOWED 0x0008 + +struct aim_chat_exchangeinfo { + guint16 number; + guint16 flags; + char *name; + char *charset1; + char *lang1; + char *charset2; + char *lang2; +}; + +#define AIM_CHATFLAGS_NOREFLECT 0x0001 +#define AIM_CHATFLAGS_AWAY 0x0002 +faim_export int aim_chat_send_im(aim_session_t *sess, aim_conn_t *conn, guint16 flags, const gchar *msg, int msglen, const char *encoding, const char *language); +faim_export int aim_chat_join(aim_session_t *sess, aim_conn_t *conn, guint16 exchange, const char *roomname, guint16 instance); +faim_export int aim_chat_attachname(aim_conn_t *conn, guint16 exchange, const char *roomname, guint16 instance); +faim_export char *aim_chat_getname(aim_conn_t *conn); +faim_export aim_conn_t *aim_chat_getconn(aim_session_t *, const char *name); + +faim_export int aim_chatnav_reqrights(aim_session_t *sess, aim_conn_t *conn); + +faim_export int aim_chatnav_createroom(aim_session_t *sess, aim_conn_t *conn, const char *name, guint16 exchange); +faim_export int aim_chat_leaveroom(aim_session_t *sess, const char *name); + + + +/* 0x000f - odir.c */ +struct aim_odir { + char *first; + char *last; + char *middle; + char *maiden; + char *email; + char *country; + char *state; + char *city; + char *sn; + char *interest; + char *nick; + char *zip; + char *region; + char *address; + struct aim_odir *next; +}; + +faim_export int aim_odir_email(aim_session_t *, const char *, const char *); +faim_export int aim_odir_name(aim_session_t *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *); +faim_export int aim_odir_interest(aim_session_t *, const char *, const char *); + + + +/* 0x0010 - icon.c */ +faim_export int aim_bart_upload(aim_session_t *sess, const guint8 *icon, guint16 iconlen); +faim_export int aim_bart_request(aim_session_t *sess, const char *sn, guint8 iconcsumtype, const guint8 *iconstr, guint16 iconstrlen); + + + +/* 0x0013 - ssi.c */ +#define AIM_SSI_TYPE_BUDDY 0x0000 +#define AIM_SSI_TYPE_GROUP 0x0001 +#define AIM_SSI_TYPE_PERMIT 0x0002 +#define AIM_SSI_TYPE_DENY 0x0003 +#define AIM_SSI_TYPE_PDINFO 0x0004 +#define AIM_SSI_TYPE_PRESENCEPREFS 0x0005 +#define AIM_SSI_TYPE_ICONINFO 0x0014 + +#define AIM_SSI_ACK_SUCCESS 0x0000 +#define AIM_SSI_ACK_ITEMNOTFOUND 0x0002 +#define AIM_SSI_ACK_IDNUMINUSE 0x000a +#define AIM_SSI_ACK_ATMAX 0x000c +#define AIM_SSI_ACK_INVALIDNAME 0x000d +#define AIM_SSI_ACK_AUTHREQUIRED 0x000e + +/* These flags are set in the 0x00c9 TLV of SSI teyp 0x0005 */ +#define AIM_SSI_PRESENCE_FLAG_SHOWIDLE 0x00000400 +#define AIM_SSI_PRESENCE_FLAG_NORECENTBUDDIES 0x00020000 + +struct aim_ssi_item { + char *name; + guint16 gid; + guint16 bid; + guint16 type; + struct aim_tlvlist_s *data; + struct aim_ssi_item *next; +}; + +struct aim_ssi_tmp { + guint16 action; + guint16 ack; + char *name; + struct aim_ssi_item *item; + struct aim_ssi_tmp *next; +}; + +/* These build the actual SNACs and queue them to be sent */ +/* 0x0002 */ faim_export int aim_ssi_reqrights(aim_session_t *sess); +/* 0x0004 */ faim_export int aim_ssi_reqdata(aim_session_t *sess); +/* 0x0005 */ faim_export int aim_ssi_reqifchanged(aim_session_t *sess, time_t localstamp, guint16 localrev); +/* 0x0007 */ faim_export int aim_ssi_enable(aim_session_t *sess); +/* 0x0008 */ faim_export int aim_ssi_addmoddel(aim_session_t *sess); +/* 0x0011 */ faim_export int aim_ssi_modbegin(aim_session_t *sess); +/* 0x0012 */ faim_export int aim_ssi_modend(aim_session_t *sess); +/* 0x0014 */ faim_export int aim_ssi_sendauth(aim_session_t *sess, char *sn, char *msg); +/* 0x0018 */ faim_export int aim_ssi_sendauthrequest(aim_session_t *sess, char *sn, const char *msg); +/* 0x001a */ faim_export int aim_ssi_sendauthreply(aim_session_t *sess, char *sn, guint8 reply, const char *msg); + +/* Client functions for retrieving SSI data */ +faim_export struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_item *list, guint16 gid, guint16 bid); +faim_export struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_item *list, const char *gn, const char *sn, guint16 type); +faim_export struct aim_ssi_item *aim_ssi_itemlist_exists(struct aim_ssi_item *list, const char *sn); +faim_export char *aim_ssi_itemlist_findparentname(struct aim_ssi_item *list, const char *sn); +faim_export int aim_ssi_getpermdeny(struct aim_ssi_item *list); +faim_export guint32 aim_ssi_getpresence(struct aim_ssi_item *list); +faim_export char *aim_ssi_getalias(struct aim_ssi_item *list, const char *gn, const char *sn); +faim_export char *aim_ssi_getcomment(struct aim_ssi_item *list, const char *gn, const char *sn); +faim_export int aim_ssi_waitingforauth(struct aim_ssi_item *list, const char *gn, const char *sn); + +/* Client functions for changing SSI data */ +faim_export int aim_ssi_addbuddy(aim_session_t *sess, const char *name, const char *group, const char *alias, const char *comment, const char *smsnum, int needauth); +faim_export int aim_ssi_addpermit(aim_session_t *sess, const char *name); +faim_export int aim_ssi_adddeny(aim_session_t *sess, const char *name); +faim_export int aim_ssi_delbuddy(aim_session_t *sess, const char *name, const char *group); +faim_export int aim_ssi_delpermit(aim_session_t *sess, const char *name); +faim_export int aim_ssi_deldeny(aim_session_t *sess, const char *name); +faim_export int aim_ssi_movebuddy(aim_session_t *sess, const char *oldgn, const char *newgn, const char *sn); +faim_export int aim_ssi_aliasbuddy(aim_session_t *sess, const char *gn, const char *sn, const char *alias); +faim_export int aim_ssi_editcomment(aim_session_t *sess, const char *gn, const char *sn, const char *alias); +faim_export int aim_ssi_rename_group(aim_session_t *sess, const char *oldgn, const char *newgn); +faim_export int aim_ssi_cleanlist(aim_session_t *sess); +faim_export int aim_ssi_deletelist(aim_session_t *sess); +faim_export int aim_ssi_setpermdeny(aim_session_t *sess, guint8 permdeny, guint32 vismask); +faim_export int aim_ssi_setpresence(aim_session_t *sess, guint32 presence); +faim_export int aim_ssi_seticon(aim_session_t *sess, guint8 *iconsum, guint16 iconsumlen); +faim_export int aim_ssi_delicon(aim_session_t *sess); + + + +/* 0x0015 - icq.c */ +#define AIM_ICQ_INFO_SIMPLE 0x001 +#define AIM_ICQ_INFO_SUMMARY 0x002 +#define AIM_ICQ_INFO_EMAIL 0x004 +#define AIM_ICQ_INFO_PERSONAL 0x008 +#define AIM_ICQ_INFO_ADDITIONAL 0x010 +#define AIM_ICQ_INFO_WORK 0x020 +#define AIM_ICQ_INFO_INTERESTS 0x040 +#define AIM_ICQ_INFO_ORGS 0x080 +#define AIM_ICQ_INFO_UNKNOWN 0x100 +#define AIM_ICQ_INFO_HAVEALL 0x1ff + +struct aim_icq_offlinemsg { + guint32 sender; + guint16 year; + guint8 month, day, hour, minute; + guint8 type; + guint8 flags; + char *msg; + int msglen; +}; + +struct aim_icq_info { + guint16 reqid; + + /* simple */ + guint32 uin; + + /* general and "home" information (0x00c8) */ + char *nick; + char *first; + char *last; + char *email; + char *homecity; + char *homestate; + char *homephone; + char *homefax; + char *homeaddr; + char *mobile; + char *homezip; + guint16 homecountry; +/* guint8 timezone; + guint8 hideemail; */ + + /* personal (0x00dc) */ + guint8 age; + guint8 unknown; + guint8 gender; + char *personalwebpage; + guint16 birthyear; + guint8 birthmonth; + guint8 birthday; + guint8 language1; + guint8 language2; + guint8 language3; + + /* work (0x00d2) */ + char *workcity; + char *workstate; + char *workphone; + char *workfax; + char *workaddr; + char *workzip; + guint16 workcountry; + char *workcompany; + char *workdivision; + char *workposition; + char *workwebpage; + + /* additional personal information (0x00e6) */ + char *info; + + /* email (0x00eb) */ + guint16 numaddresses; + char **email2; + + /* we keep track of these in a linked list because we're 1337 */ + struct aim_icq_info *next; +}; + +faim_export int aim_icq_reqofflinemsgs(aim_session_t *sess); +faim_export int aim_icq_ackofflinemsgs(aim_session_t *sess); +faim_export int aim_icq_setsecurity(aim_session_t *sess, gboolean auth_required, gboolean webaware); +faim_export int aim_icq_changepasswd(aim_session_t *sess, const char *passwd); +faim_export int aim_icq_getsimpleinfo(aim_session_t *sess, const char *uin); +faim_export int aim_icq_getalias(aim_session_t *sess, const char *uin); +faim_export int aim_icq_getallinfo(aim_session_t *sess, const char *uin); + + + +/* 0x0017 - auth.c */ +faim_export int aim_sendcookie(aim_session_t *, aim_conn_t *, const guint16 length, const guint8 *); +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, guint16 info); +faim_export int aim_admin_setemail(aim_session_t *sess, aim_conn_t *conn, const char *newemail); +faim_export int aim_admin_setnick(aim_session_t *sess, aim_conn_t *conn, const char *newnick); + + + +/* 0x0018 - email.c */ +struct aim_emailinfo { + guint8 *cookie16; + guint8 *cookie8; + char *url; + guint16 nummsgs; + guint8 unread; + char *domain; + guint16 flag; + struct aim_emailinfo *next; +}; + +faim_export int aim_email_sendcookies(aim_session_t *sess); +faim_export int aim_email_activate(aim_session_t *sess); + + + +/* tlv.c - TLV handling */ + +/* TLV structure */ +typedef struct aim_tlv_s { + guint16 type; + guint16 length; + guint8 *value; +} aim_tlv_t; + +/* TLV List structure */ +typedef struct aim_tlvlist_s { + aim_tlv_t *tlv; + struct aim_tlvlist_s *next; +} aim_tlvlist_t; + +/* TLV handling functions */ +faim_internal aim_tlv_t *aim_tlv_gettlv(aim_tlvlist_t *list, guint16 type, const int nth); +faim_internal int aim_tlv_getlength(aim_tlvlist_t *list, guint16 type, const int nth); +faim_internal char *aim_tlv_getstr(aim_tlvlist_t *list, const guint16 type, const int nth); +faim_internal guint8 aim_tlv_get8(aim_tlvlist_t *list, const guint16 type, const int nth); +faim_internal guint16 aim_tlv_get16(aim_tlvlist_t *list, const guint16 type, const int nth); +faim_internal guint32 aim_tlv_get32(aim_tlvlist_t *list, const guint16 type, const int nth); + +/* TLV list handling functions */ +faim_internal aim_tlvlist_t *aim_tlvlist_read(aim_bstream_t *bs); +faim_internal aim_tlvlist_t *aim_tlvlist_readnum(aim_bstream_t *bs, guint16 num); +faim_internal aim_tlvlist_t *aim_tlvlist_readlen(aim_bstream_t *bs, guint16 len); +faim_internal aim_tlvlist_t *aim_tlvlist_copy(aim_tlvlist_t *orig); + +faim_internal int aim_tlvlist_count(aim_tlvlist_t **list); +faim_internal int aim_tlvlist_size(aim_tlvlist_t **list); +faim_internal int aim_tlvlist_cmp(aim_tlvlist_t *one, aim_tlvlist_t *two); +faim_internal int aim_tlvlist_write(aim_bstream_t *bs, aim_tlvlist_t **list); +faim_internal void aim_tlvlist_free(aim_tlvlist_t **list); + +faim_internal int aim_tlvlist_add_raw(aim_tlvlist_t **list, const guint16 type, const guint16 length, const guint8 *value); +faim_internal int aim_tlvlist_add_noval(aim_tlvlist_t **list, const guint16 type); +faim_internal int aim_tlvlist_add_8(aim_tlvlist_t **list, const guint16 type, const guint8 value); +faim_internal int aim_tlvlist_add_16(aim_tlvlist_t **list, const guint16 type, const guint16 value); +faim_internal int aim_tlvlist_add_32(aim_tlvlist_t **list, const guint16 type, const guint32 value); +faim_internal int aim_tlvlist_add_str(aim_tlvlist_t **list, const guint16 type, const char *value); +faim_internal int aim_tlvlist_add_caps(aim_tlvlist_t **list, const guint16 type, const guint32 caps); +faim_internal int aim_tlvlist_add_userinfo(aim_tlvlist_t **list, guint16 type, aim_userinfo_t *userinfo); +faim_internal int aim_tlvlist_add_chatroom(aim_tlvlist_t **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance); +faim_internal int aim_tlvlist_add_frozentlvlist(aim_tlvlist_t **list, guint16 type, aim_tlvlist_t **tl); + +faim_internal int aim_tlvlist_replace_raw(aim_tlvlist_t **list, const guint16 type, const guint16 lenth, const guint8 *value); +faim_internal int aim_tlvlist_replace_str(aim_tlvlist_t **list, const guint16 type, const char *str); +faim_internal int aim_tlvlist_replace_noval(aim_tlvlist_t **list, const guint16 type); +faim_internal int aim_tlvlist_replace_8(aim_tlvlist_t **list, const guint16 type, const guint8 value); +faim_internal int aim_tlvlist_replace_16(aim_tlvlist_t **list, const guint16 type, const guint16 value); +faim_internal int aim_tlvlist_replace_32(aim_tlvlist_t **list, const guint16 type, const guint32 value); + +faim_internal void aim_tlvlist_remove(aim_tlvlist_t **list, const guint16 type); + + + +/* util.c */ +/* + * These are really ugly. You'd think this was LISP. I wish it was. + * + * XXX With the advent of bstream's, these should be removed to enforce + * their use. + * + */ +#define aimutil_put8(buf, data) ((*(buf) = (guint8)(data)&0xff),1) +#define aimutil_get8(buf) ((*(buf))&0xff) +#define aimutil_put16(buf, data) ( \ + (*(buf) = (guint8)((data)>>8)&0xff), \ + (*((buf)+1) = (guint8)(data)&0xff), \ + 2) +#define aimutil_get16(buf) ((((*(buf))<<8)&0xff00) + ((*((buf)+1)) & 0xff)) +#define aimutil_put32(buf, data) ( \ + (*((buf)) = (guint8)((data)>>24)&0xff), \ + (*((buf)+1) = (guint8)((data)>>16)&0xff), \ + (*((buf)+2) = (guint8)((data)>>8)&0xff), \ + (*((buf)+3) = (guint8)(data)&0xff), \ + 4) +#define aimutil_get32(buf) ((((*(buf))<<24)&0xff000000) + \ + (((*((buf)+1))<<16)&0x00ff0000) + \ + (((*((buf)+2))<< 8)&0x0000ff00) + \ + (((*((buf)+3) )&0x000000ff))) + +/* Little-endian versions (damn ICQ) */ +#define aimutil_putle8(buf, data) ( \ + (*(buf) = (guint8)(data) & 0xff), \ + 1) +#define aimutil_getle8(buf) ( \ + (*(buf)) & 0xff \ + ) +#define aimutil_putle16(buf, data) ( \ + (*((buf)+0) = (guint8)((data) >> 0) & 0xff), \ + (*((buf)+1) = (guint8)((data) >> 8) & 0xff), \ + 2) +#define aimutil_getle16(buf) ( \ + (((*((buf)+0)) << 0) & 0x00ff) + \ + (((*((buf)+1)) << 8) & 0xff00) \ + ) +#define aimutil_putle32(buf, data) ( \ + (*((buf)+0) = (guint8)((data) >> 0) & 0xff), \ + (*((buf)+1) = (guint8)((data) >> 8) & 0xff), \ + (*((buf)+2) = (guint8)((data) >> 16) & 0xff), \ + (*((buf)+3) = (guint8)((data) >> 24) & 0xff), \ + 4) +#define aimutil_getle32(buf) ( \ + (((*((buf)+0)) << 0) & 0x000000ff) + \ + (((*((buf)+1)) << 8) & 0x0000ff00) + \ + (((*((buf)+2)) << 16) & 0x00ff0000) + \ + (((*((buf)+3)) << 24) & 0xff000000)) + + +faim_export int aimutil_putstr(char *, const char *, int); +faim_export guint16 aimutil_iconsum(const guint8 *buf, int buflen); +faim_export int aimutil_tokslen(char *toSearch, int theindex, char dl); +faim_export int aimutil_itemcnt(char *toSearch, char dl); +faim_export char *aimutil_itemindex(char *toSearch, int theindex, char dl); + +faim_export int aim_snvalid(const char *sn); +faim_export int aim_sn_is_icq(const char *sn); +faim_export int aim_sn_is_sms(const char *sn); +faim_export int aim_snlen(const char *sn); +faim_export int aim_sncmp(const char *sn1, const char *sn2); + +#include "oscar_internal.h" + +#ifdef __cplusplus +} +#endif + +#endif /* _OSCAR_H_ */ diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/oscar_internal.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/oscar/oscar_internal.h Sat Feb 11 21:45:18 2006 +0000 @@ -0,0 +1,231 @@ +/* + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * oscar_internal.h -- prototypes/structs for the guts of libfaim + * + */ + +#ifndef _OSCAR_INTERNAL_H_ +#define _OSCAR_INTERNAL_H_ + +typedef struct { + guint16 family; + guint16 subtype; + guint16 flags; + guint32 id; +} aim_modsnac_t; + +#define AIM_MODULENAME_MAXLEN 16 +#define AIM_MODFLAG_MULTIFAMILY 0x0001 +typedef struct aim_module_s { + guint16 family; + guint16 version; + guint16 toolid; + guint16 toolversion; + guint16 flags; + char name[AIM_MODULENAME_MAXLEN+1]; + int (*snachandler)(aim_session_t *sess, struct aim_module_s *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs); + + void (*shutdown)(aim_session_t *sess, struct aim_module_s *mod); + void *priv; + struct aim_module_s *next; +} aim_module_t; + +faim_internal int aim__registermodule(aim_session_t *sess, int (*modfirst)(aim_session_t *, aim_module_t *)); +faim_internal void aim__shutdownmodules(aim_session_t *sess); +faim_internal aim_module_t *aim__findmodulebygroup(aim_session_t *sess, guint16 group); +faim_internal aim_module_t *aim__findmodule(aim_session_t *sess, const char *name); + +faim_internal int admin_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int buddylist_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int bos_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int search_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int stats_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int auth_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int msg_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int misc_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int chatnav_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int chat_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int locate_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int service_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int invite_modfirst(aim_session_t *sess, aim_module_t *mod); +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 odir_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int bart_modfirst(aim_session_t *sess, aim_module_t *mod); +faim_internal int ssi_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 email_modfirst(aim_session_t *sess, aim_module_t *mod); + +faim_internal int aim_genericreq_n(aim_session_t *, aim_conn_t *conn, guint16 family, guint16 subtype); +faim_internal int aim_genericreq_n_snacid(aim_session_t *, aim_conn_t *conn, guint16 family, guint16 subtype); +faim_internal int aim_genericreq_l(aim_session_t *, aim_conn_t *conn, guint16 family, guint16 subtype, guint32 *); +faim_internal int aim_genericreq_s(aim_session_t *, aim_conn_t *conn, guint16 family, guint16 subtype, guint16 *); + +#define AIMBS_CURPOSPAIR(x) ((x)->data + (x)->offset), ((x)->len - (x)->offset) + +/* bstream.c */ +faim_internal int aim_bstream_init(aim_bstream_t *bs, guint8 *data, int len); +faim_internal int aim_bstream_empty(aim_bstream_t *bs); +faim_internal int aim_bstream_curpos(aim_bstream_t *bs); +faim_internal int aim_bstream_setpos(aim_bstream_t *bs, unsigned int off); +faim_internal void aim_bstream_rewind(aim_bstream_t *bs); +faim_internal int aim_bstream_advance(aim_bstream_t *bs, int n); +faim_internal guint8 aimbs_get8(aim_bstream_t *bs); +faim_internal guint16 aimbs_get16(aim_bstream_t *bs); +faim_internal guint32 aimbs_get32(aim_bstream_t *bs); +faim_internal guint8 aimbs_getle8(aim_bstream_t *bs); +faim_internal guint16 aimbs_getle16(aim_bstream_t *bs); +faim_internal guint32 aimbs_getle32(aim_bstream_t *bs); +faim_internal int aimbs_getrawbuf(aim_bstream_t *bs, guint8 *buf, int len); +faim_internal guint8 *aimbs_getraw(aim_bstream_t *bs, int len); +faim_internal char *aimbs_getstr(aim_bstream_t *bs, int len); +faim_internal int aimbs_put8(aim_bstream_t *bs, guint8 v); +faim_internal int aimbs_put16(aim_bstream_t *bs, guint16 v); +faim_internal int aimbs_put32(aim_bstream_t *bs, guint32 v); +faim_internal int aimbs_putle8(aim_bstream_t *bs, guint8 v); +faim_internal int aimbs_putle16(aim_bstream_t *bs, guint16 v); +faim_internal int aimbs_putle32(aim_bstream_t *bs, guint32 v); +faim_internal int aimbs_putraw(aim_bstream_t *bs, const guint8 *v, int len); +faim_internal int aimbs_putstr(aim_bstream_t *bs, const char *str); +faim_internal int aimbs_putbs(aim_bstream_t *bs, aim_bstream_t *srcbs, int len); +faim_internal int aimbs_putcaps(aim_bstream_t *bs, guint32 caps); + +/* conn.c */ +faim_internal aim_conn_t *aim_cloneconn(aim_session_t *sess, aim_conn_t *src); + +/* rxhandlers.c */ +faim_internal aim_rxcallback_t aim_callhandler(aim_session_t *sess, aim_conn_t *conn, guint16 family, guint16 type); +faim_internal int aim_callhandler_noparam(aim_session_t *sess, aim_conn_t *conn, guint16 family, guint16 type, aim_frame_t *ptr); +faim_internal int aim_parse_unknown(aim_session_t *, aim_frame_t *, ...); +faim_internal void aim_clonehandlers(aim_session_t *sess, aim_conn_t *dest, aim_conn_t *src); + +/* rxqueue.c */ +faim_internal int aim_recv(int fd, void *buf, size_t count); +faim_internal int aim_bstream_recv(aim_bstream_t *bs, int fd, size_t count); +faim_internal void aim_rxqueue_cleanbyconn(aim_session_t *sess, aim_conn_t *conn); +faim_internal void aim_frame_destroy(aim_frame_t *); + +/* txqueue.c */ +faim_internal aim_frame_t *aim_tx_new(aim_session_t *sess, aim_conn_t *conn, guint8 framing, guint16 chan, int datalen); +faim_internal int aim_tx_enqueue(aim_session_t *, aim_frame_t *); +faim_internal int aim_bstream_send(aim_bstream_t *bs, aim_conn_t *conn, size_t count); +faim_internal int aim_tx_sendframe(aim_session_t *sess, aim_frame_t *cur); +faim_internal void aim_tx_cleanqueue(aim_session_t *, aim_conn_t *); + +/* + * Generic SNAC structure. Rarely if ever used. + */ +typedef struct aim_snac_s { + aim_snacid_t id; + guint16 family; + guint16 type; + guint16 flags; + void *data; + time_t issuetime; + struct aim_snac_s *next; +} aim_snac_t; + +/* snac.c */ +faim_internal void aim_initsnachash(aim_session_t *sess); +faim_internal aim_snacid_t aim_newsnac(aim_session_t *, aim_snac_t *newsnac); +faim_internal aim_snacid_t aim_cachesnac(aim_session_t *sess, const guint16 family, const guint16 type, const guint16 flags, const void *data, const int datalen); +faim_internal aim_snac_t *aim_remsnac(aim_session_t *, aim_snacid_t id); +faim_internal int aim_putsnac(aim_bstream_t *, guint16 family, guint16 type, guint16 flags, aim_snacid_t id); + +/* Stored in ->priv of the service request SNAC for chats. */ +struct chatsnacinfo { + guint16 exchange; + char name[128]; + guint16 instance; +}; + +/* + * In SNACland, the terms 'family' and 'group' are synonymous -- the former + * is my term, the latter is AOL's. + */ +struct snacgroup { + guint16 group; + struct snacgroup *next; +}; + +struct snacpair { + guint16 group; + guint16 subtype; + struct snacpair *next; +}; + +struct rateclass { + guint16 classid; + guint32 windowsize; + guint32 clear; + guint32 alert; + guint32 limit; + guint32 disconnect; + guint32 current; + guint32 max; + guint8 unknown[5]; /* only present in versions >= 3 */ + struct snacpair *members; + struct rateclass *next; +}; + +/* + * This is inside every connection. But it is a void * to anything + * outside of libfaim. It should remain that way. It's called data + * abstraction. Maybe you've heard of it. (Probably not if you're a + * libfaim user.) + * + */ +typedef struct aim_conn_inside_s { + struct snacgroup *groups; + struct rateclass *rates; +} aim_conn_inside_t; + +faim_internal void aim_conn_addgroup(aim_conn_t *conn, guint16 group); + +faim_internal int aim_cachecookie(aim_session_t *sess, aim_msgcookie_t *cookie); +faim_internal aim_msgcookie_t *aim_uncachecookie(aim_session_t *sess, guint8 *cookie, int type); +faim_internal aim_msgcookie_t *aim_mkcookie(guint8 *, int, void *); +faim_internal aim_msgcookie_t *aim_checkcookie(aim_session_t *, const unsigned char *, const int); +faim_internal int aim_freecookie(aim_session_t *sess, aim_msgcookie_t *cookie); +faim_internal int aim_msgcookie_gettype(int reqclass); +faim_internal int aim_cookie_free(aim_session_t *sess, aim_msgcookie_t *cookie); + +/* 0x0002 - locate.c */ +faim_internal void aim_locate_requestuserinfo(aim_session_t *sess, const char *sn); +faim_internal guint32 aim_locate_getcaps(aim_session_t *sess, aim_bstream_t *bs, int len); +faim_internal guint32 aim_locate_getcaps_short(aim_session_t *sess, aim_bstream_t *bs, int len); +faim_internal void aim_info_free(aim_userinfo_t *); +faim_internal int aim_info_extract(aim_session_t *sess, aim_bstream_t *bs, aim_userinfo_t *); +faim_internal int aim_putuserinfo(aim_bstream_t *bs, aim_userinfo_t *info); + +faim_internal int aim_chat_readroominfo(aim_bstream_t *bs, struct aim_chat_roominfo *outinfo); + +faim_internal void aim_conn_kill_chat(aim_session_t *sess, aim_conn_t *conn); + +/* 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_rates_addparam(aim_session_t *, aim_conn_t *); +faim_internal int aim_rates_delparam(aim_session_t *, aim_conn_t *); + +#endif /* _OSCAR_INTERNAL_H_ */ diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/peer.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/oscar/peer.c Sat Feb 11 21:45:18 2006 +0000 @@ -0,0 +1,1228 @@ +/* + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * Oscar File transfer (OFT) and Oscar Direct Connect (ODC). + * (ODC is also referred to as DirectIM and IM Image.) + * + * There are a few static helper functions at the top, then + * ODC stuff, then ft stuff. + * + * I feel like this is a good place to explain OFT, so I'm going to + * do just that. Each OFT packet has a header type. I guess this + * is pretty similar to the subtype of a SNAC packet. The type + * basically tells the other client the meaning of the OFT packet. + * There are two distinct types of file transfer, which I usually + * call "sendfile" and "getfile." Sendfile is when you send a file + * to another AIM user. Getfile is when you share a group of files, + * and other users request that you send them the files. + * + * A typical sendfile file transfer goes like this: + * 1) Sender sends a channel 2 ICBM telling the other user that + * we want to send them a file. At the same time, we open a + * listener socket (this should be done before sending the + * ICBM) on some port, and wait for them to connect to us. + * The ICBM we sent should contain our IP address and the port + * number that we're listening on. + * 2) The receiver connects to the sender on the given IP address + * and port. After the connection is established, the receiver + * sends an ICBM signifying that we are ready and waiting. + * 3) The sender sends an OFT PROMPT message over the OFT + * connection. + * 4) The receiver of the file sends back an exact copy of this + * OFT packet, except the cookie is filled in with the cookie + * from the ICBM. I think this might be an attempt to verify + * that the user that is connected is actually the guy that + * we sent the ICBM to. Oh, I've been calling this the ACK. + * 5) The sender starts sending raw data across the connection + * until the entire file has been sent. + * 6) The receiver knows the file is finished because the sender + * sent the file size in an earlier OFT packet. So then the + * receiver sends the DONE thingy (after filling in the + * "received" checksum and size) and closes the connection. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "oscar.h" +#include "peer.h" + +#ifndef _WIN32 +#include +#include +#include +#include +#include /* for aim_odc_initiate */ +#include /* for inet_ntoa */ +#include /* for UINT_MAX */ +#define G_DIR_SEPARATOR '/' +#endif + +#ifdef _WIN32 +#include "win32dep.h" +#endif + +/* + * I really want to switch all our networking code to using IPv6 only, + * but that really isn't a good idea at all. Evan S. of Adium says + * OS X sets all connections as "AF_INET6/PF_INET6," even if there is + * nothing inherently IPv6 about them. And I feel like Linux kernel + * 2.6.5 is doing the same thing. So we REALLY should accept + * connections if they're showing up as IPv6. Old OSes (Solaris?) + * that might not have full IPv6 support yet will fail if we try + * to use PF_INET6 but it isn't defined. --Mark Doliner + */ +#ifndef PF_INET6 +#define PF_INET6 PF_INET +#endif + +struct aim_odc_intdata { + guint8 cookie[8]; + char sn[MAXSNLEN+1]; + char ip[22]; +}; + +/** + * Convert the directory separator from / (0x2f) to ^A (0x01) + * + * @param name The filename to convert. + */ +static void aim_oft_dirconvert_tostupid(char *name) +{ + while (name[0]) { + if (name[0] == 0x01) + name[0] = G_DIR_SEPARATOR; + name++; + } +} + +/** + * Convert the directory separator from ^A (0x01) to / (0x2f) + * + * @param name The filename to convert. + */ +static void aim_oft_dirconvert_fromstupid(char *name) +{ + while (name[0]) { + if (name[0] == G_DIR_SEPARATOR) + name[0] = 0x01; + name++; + } +} + +/** + * Calculate oft checksum of buffer + * + * Prevcheck should be 0xFFFF0000 when starting a checksum of a file. The + * checksum is kind of a rolling checksum thing, so each time you get bytes + * of a file you just call this puppy and it updates the checksum. You can + * calculate the checksum of an entire file by calling this in a while or a + * for loop, or something. + * + * Thanks to Graham Booker for providing this improved checksum routine, + * which is simpler and should be more accurate than Josh Myer's original + * code. -- wtm + * + * This algorithm works every time I have tried it. The other fails + * sometimes. So, AOL who thought this up? It has got to be the weirdest + * checksum I have ever seen. + * + * @param buffer Buffer of data to checksum. Man I'd like to buff her... + * @param bufsize Size of buffer. + * @param prevcheck Previous checksum. + */ +faim_export guint32 aim_oft_checksum_chunk(const guint8 *buffer, int bufferlen, guint32 prevcheck) +{ + guint32 check = (prevcheck >> 16) & 0xffff, oldcheck; + int i; + unsigned short val; + + for (i=0; i oldcheck) + check--; + } + check = ((check & 0x0000ffff) + (check >> 16)); + check = ((check & 0x0000ffff) + (check >> 16)); + return check << 16; +} + +faim_export guint32 aim_oft_checksum_file(char *filename) { + FILE *fd; + guint32 checksum = 0xffff0000; + + if ((fd = fopen(filename, "rb"))) { + int bytes; + guint8 buffer[1024]; + + while ((bytes = fread(buffer, 1, 1024, fd))) + checksum = aim_oft_checksum_chunk(buffer, bytes, checksum); + fclose(fd); + } + + return checksum; +} + +/** + * After establishing a listening socket, this is called to accept a connection. It + * clones the conn used by the listener, and passes both of these to a signal handler. + * The signal handler should close the listener conn and keep track of the new conn, + * since this is what is used for file transfers and what not. + * + * @param sess The session. + * @param cur The conn the incoming connection is on. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_handlerendconnect(aim_session_t *sess, aim_conn_t *cur) +{ + int acceptfd = 0; + struct sockaddr addr; + socklen_t addrlen = sizeof(addr); + int ret = 0; + aim_conn_t *newconn; + char ip[20]; + unsigned short port; + + if ((acceptfd = accept(cur->fd, &addr, &addrlen)) == -1) + return 0; /* not an error */ + + if ((addr.sa_family != PF_INET) && (addr.sa_family != PF_INET6)) { + close(acceptfd); + aim_conn_close(cur); + return -1; + } + + strncpy(ip, inet_ntoa(((struct sockaddr_in *)&addr)->sin_addr), sizeof(ip)); + port = ntohs(((struct sockaddr_in *)&addr)->sin_port); + + if (!(newconn = aim_cloneconn(sess, cur))) { + close(acceptfd); + aim_conn_close(cur); + return -ENOMEM; + } + + newconn->type = AIM_CONN_TYPE_RENDEZVOUS; + newconn->fd = acceptfd; + + if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) { + aim_rxcallback_t userfunc; + struct aim_odc_intdata *priv; + + priv = (struct aim_odc_intdata *)(newconn->internal = cur->internal); + cur->internal = NULL; + snprintf(priv->ip, sizeof(priv->ip), "%s:%hu", ip, port); + + if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, OFT_TYPE_DIRECTIM_ESTABLISHED))) + ret = userfunc(sess, NULL, newconn, cur); + + } else if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE) { + } else if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_SENDFILE) { + aim_rxcallback_t userfunc; + + if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, OFT_TYPE_ESTABLISHED))) + ret = userfunc(sess, NULL, newconn, cur); + + } else { + gaim_debug_warning("oscar", "Got a connection on a listener that's not rendezvous. Closing connection.\n"); + aim_conn_close(newconn); + ret = -1; + } + + return ret; +} + +/** + * Send client-to-client typing notification over an established direct connection. + * + * @param sess The session. + * @param conn The already-connected ODC connection. + * @param typing If 0x0002, sends a "typing" message, 0x0001 sends "typed," and + * 0x0000 sends "stopped." + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_odc_send_typing(aim_session_t *sess, aim_conn_t *conn, int typing) +{ + struct aim_odc_intdata *intdata = (struct aim_odc_intdata *)conn->internal; + aim_frame_t *fr; + aim_bstream_t *hdrbs; + guint8 *hdr; + int hdrlen = 0x44; + + if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS)) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0001, 0))) + return -ENOMEM; + memcpy(fr->hdr.rend.magic, "ODC2", 4); + fr->hdr.rend.hdrlen = hdrlen + 8; + + if (!(hdr = calloc(1, hdrlen))) { + aim_frame_destroy(fr); + return -ENOMEM; + } + + hdrbs = &(fr->data); + aim_bstream_init(hdrbs, hdr, hdrlen); + + aimbs_put16(hdrbs, 0x0006); + aimbs_put16(hdrbs, 0x0000); + aimbs_putraw(hdrbs, intdata->cookie, 8); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put32(hdrbs, 0x00000000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + + if (typing == 0x0002) + aimbs_put16(hdrbs, 0x0002 | 0x0008); + else if (typing == 0x0001) + aimbs_put16(hdrbs, 0x0002 | 0x0004); + else + aimbs_put16(hdrbs, 0x0002); + + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_putstr(hdrbs, sess->sn); + + aim_bstream_setpos(hdrbs, 52); /* bleeehh */ + + aimbs_put8(hdrbs, 0x00); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put8(hdrbs, 0x00); + + /* end of hdr */ + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/** + * Send client-to-client IM over an established direct connection. + * Call this just like you would aim_send_im, to send a directim. + * + * @param sess The session. + * @param conn The already-connected ODC connection. + * @param msg Null-terminated string to send. + * @param len The length of the message to send, including binary data. + * @param encoding See the AIM_CHARSET_* defines in oscar.h + * @param isawaymsg 0 if this is not an auto-response, 1 if it is. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_odc_send_im(aim_session_t *sess, aim_conn_t *conn, const char *msg, int len, int encoding, int isawaymsg) +{ + aim_frame_t *fr; + aim_bstream_t *hdrbs; + struct aim_odc_intdata *intdata = (struct aim_odc_intdata *)conn->internal; + int hdrlen = 0x44; + guint8 *hdr; + + if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS) || !msg) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x01, 0))) + return -ENOMEM; + + memcpy(fr->hdr.rend.magic, "ODC2", 4); + fr->hdr.rend.hdrlen = hdrlen + 8; + + if (!(hdr = calloc(1, hdrlen + len))) { + aim_frame_destroy(fr); + return -ENOMEM; + } + + hdrbs = &(fr->data); + aim_bstream_init(hdrbs, hdr, hdrlen + len); + + aimbs_put16(hdrbs, 0x0006); + aimbs_put16(hdrbs, 0x0000); + aimbs_putraw(hdrbs, intdata->cookie, 8); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put32(hdrbs, len); + aimbs_put16(hdrbs, encoding); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + + /* flags - used for typing notification and to mark if this is an away message */ + aimbs_put16(hdrbs, 0x0000 | isawaymsg); + + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_putstr(hdrbs, sess->sn); + + aim_bstream_setpos(hdrbs, 52); /* bleeehh */ + + aimbs_put8(hdrbs, 0x00); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x0000); + aimbs_put8(hdrbs, 0x00); + + /* end of hdr2 */ + +#if 0 /* XXX - this is how you send buddy icon info... */ + aimbs_put16(hdrbs, 0x0008); + aimbs_put16(hdrbs, 0x000c); + aimbs_put16(hdrbs, 0x0000); + aimbs_put16(hdrbs, 0x1466); + aimbs_put16(hdrbs, 0x0001); + aimbs_put16(hdrbs, 0x2e0f); + aimbs_put16(hdrbs, 0x393e); + aimbs_put16(hdrbs, 0xcac8); +#endif + aimbs_putraw(hdrbs, (guchar *)msg, len); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/** + * Get the screen name of the peer of a direct connection. + * + * @param conn The ODC connection. + * @return The screen name of the dude, or NULL if there was an anomaly. + */ +faim_export const char *aim_odc_getsn(aim_conn_t *conn) +{ + struct aim_odc_intdata *intdata; + + if (!conn || !conn->internal) + return NULL; + + if ((conn->type != AIM_CONN_TYPE_RENDEZVOUS) || + (conn->subtype != AIM_CONN_SUBTYPE_OFT_DIRECTIM)) + return NULL; + + intdata = (struct aim_odc_intdata *)conn->internal; + + return intdata->sn; +} + +/** + * Get the cookie of a direct connection. + * + * @param conn The ODC connection. + * @return The cookie, an 8 byte unterminated string, or NULL if there was an anomaly. + */ +faim_export const guchar *aim_odc_getcookie(aim_conn_t *conn) +{ + struct aim_odc_intdata *intdata; + + if (!conn || !conn->internal) + return NULL; + + intdata = (struct aim_odc_intdata *)conn->internal; + + return intdata->cookie; +} + +/** + * Find the conn of a direct connection with the given buddy. + * + * @param sess The session. + * @param sn The screen name of the buddy whose direct connection you want to find. + * @return The conn for the direct connection with the given buddy, or NULL if no + * connection was found. + */ +faim_export aim_conn_t *aim_odc_getconn(aim_session_t *sess, const char *sn) +{ + aim_conn_t *cur; + struct aim_odc_intdata *intdata; + + if (!sess || !sn || !strlen(sn)) + return NULL; + + for (cur = sess->connlist; cur; cur = cur->next) { + if ((cur->type == AIM_CONN_TYPE_RENDEZVOUS) && (cur->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM)) { + intdata = cur->internal; + if (!aim_sncmp(intdata->sn, sn)) + return cur; + } + } + + return NULL; +} + +/** + * For those times when we want to open up the direct connection channel ourselves. + * + * You'll want to set up some kind of watcher on this socket. + * When the state changes, call aim_handlerendconnection with + * the connection returned by this. aim_handlerendconnection + * will accept the pending connection and stop listening. + * + * @param sess The session + * @param sn The screen name to connect to. + * @return The new connection. + */ +faim_export aim_conn_t *aim_odc_initiate(aim_session_t *sess, const char *sn, int listenfd, + const guint8 *localip, guint16 port, const guint8 *mycookie) +{ + aim_conn_t *newconn; + aim_msgcookie_t *cookie; + struct aim_odc_intdata *priv; + guint8 ck[8]; + + if (!localip) + return NULL; + + if (mycookie) { + memcpy(ck, mycookie, 8); + aim_im_sendch2_odcrequest(sess, ck, TRUE, sn, localip, port); + } else + aim_im_sendch2_odcrequest(sess, ck, FALSE, sn, localip, port); + + cookie = (aim_msgcookie_t *)calloc(1, sizeof(aim_msgcookie_t)); + memcpy(cookie->cookie, ck, 8); + cookie->type = AIM_COOKIETYPE_OFTIM; + + /* this one is for the cookie */ + priv = (struct aim_odc_intdata *)calloc(1, sizeof(struct aim_odc_intdata)); + + memcpy(priv->cookie, ck, 8); + strncpy(priv->sn, sn, sizeof(priv->sn)); + cookie->data = priv; + aim_cachecookie(sess, cookie); + + /* XXX - switch to aim_cloneconn()? */ + if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_LISTENER))) { + close(listenfd); + return NULL; + } + + /* this one is for the conn */ + priv = (struct aim_odc_intdata *)calloc(1, sizeof(struct aim_odc_intdata)); + + memcpy(priv->cookie, ck, 8); + strncpy(priv->sn, sn, sizeof(priv->sn)); + + newconn->fd = listenfd; + newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM; + newconn->internal = priv; + newconn->lastactivity = time(NULL); + + return newconn; +} + +/** + * Connect directly to the given buddy for directim. + * + * This is a wrapper for aim_newconn. + * + * If addr is NULL, the socket is not created, but the connection is + * allocated and setup to connect. + * + * @param sess The Godly session. + * @param sn The screen name we're connecting to. I hope it's a girl... + * @param addr Address to connect to. + * @return The new connection. + */ +faim_export aim_conn_t *aim_odc_connect(aim_session_t *sess, const char *sn, const char *addr, const guint8 *cookie) +{ + aim_conn_t *newconn; + struct aim_odc_intdata *intdata; + + if (!sess || !sn) + return NULL; + + if (!(intdata = calloc(1, sizeof(struct aim_odc_intdata)))) + return NULL; + memcpy(intdata->cookie, cookie, 8); + strncpy(intdata->sn, sn, sizeof(intdata->sn)); + if (addr) + strncpy(intdata->ip, addr, sizeof(intdata->ip)); + + /* XXX - verify that non-blocking connects actually work */ + if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS))) { + free(intdata); + return NULL; + } + + newconn->internal = intdata; + newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM; + + return newconn; +} + +/** + * Sometimes you just don't know with these kinds of people. + * + * @param sess The session. + * @param conn The ODC connection of the incoming data. + * @param frr The frame allocated for the incoming data. + * @param bs It stands for "bologna sandwich." + * @return Return 0 if no errors, otherwise return the error number. + */ +static int handlehdr_odc(aim_session_t *sess, aim_conn_t *conn, aim_frame_t *frr, aim_bstream_t *bs) +{ + aim_frame_t fr; + int ret = 0; + aim_rxcallback_t userfunc; + guint32 payloadlength; + guint16 flags, encoding; + char *snptr = NULL; + + fr.conn = conn; + + /* AAA - ugly */ + aim_bstream_setpos(bs, 20); + payloadlength = aimbs_get32(bs); + + aim_bstream_setpos(bs, 24); + encoding = aimbs_get16(bs); + + aim_bstream_setpos(bs, 30); + flags = aimbs_get16(bs); + + aim_bstream_setpos(bs, 36); + /* XXX - create an aimbs_getnullstr function? */ + snptr = aimbs_getstr(bs, 32); /* Next 32 bytes contain the sn, padded with null chars */ + + gaim_debug_misc("oscar", "faim: OFT frame: handlehdr_odc: %04x / %04x / %s\n", payloadlength, flags, snptr); + + if (flags & 0x0008) { + if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, OFT_TYPE_DIRECTIMTYPING))) + ret = userfunc(sess, &fr, snptr, 2); + } else if (flags & 0x0004) { + if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, OFT_TYPE_DIRECTIMTYPING))) + ret = userfunc(sess, &fr, snptr, 1); + } else { + if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, OFT_TYPE_DIRECTIMTYPING))) + ret = userfunc(sess, &fr, snptr, 0); + } + + if ((payloadlength != 0) && (payloadlength != UINT_MAX)) { + char *msg; + int recvd = 0; + int i, isawaymsg; + + isawaymsg = flags & 0x0001; + + if (!(msg = calloc(1, payloadlength+1))) { + free(snptr); + return -ENOMEM; + } + + while (payloadlength - recvd) { + if (payloadlength - recvd >= 1024) + i = aim_recv(conn->fd, &msg[recvd], 1024); + else + i = aim_recv(conn->fd, &msg[recvd], payloadlength - recvd); + if (i <= 0) { + free(msg); + free(snptr); + return -1; + } + recvd = recvd + i; + if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER))) + ret = userfunc(sess, &fr, snptr, (double)recvd / payloadlength); + } + + if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, OFT_TYPE_DIRECTIMINCOMING))) + ret = userfunc(sess, &fr, snptr, msg, payloadlength, encoding, isawaymsg); + + free(msg); + } + + free(snptr); + + return ret; +} + +faim_export struct aim_oft_info *aim_oft_createinfo(aim_session_t *sess, const guint8 *cookie, const char *sn, const char *ip, guint16 port, guint32 size, guint32 modtime, char *filename, int send_or_recv, int method, int stage) +{ + struct aim_oft_info *new; + + if (!sess) + return NULL; + + if (!(new = (struct aim_oft_info *)calloc(1, sizeof(struct aim_oft_info)))) + return NULL; + + new->sess = sess; + if (cookie) + memcpy(new->cookie, cookie, 8); + else + aim_icbm_makecookie(new->cookie); + if (ip) + new->clientip = strdup(ip); + else + new->clientip = NULL; + if (sn) + new->sn = strdup(sn); + else + new->sn = NULL; + new->method = method; + new->send_or_recv = send_or_recv; + new->stage = stage; + new->port = port; + new->xfer_reffed = FALSE; + new->success = FALSE; + new->fh.totfiles = 1; + new->fh.filesleft = 1; + new->fh.totparts = 1; + new->fh.partsleft = 1; + new->fh.totsize = size; + new->fh.size = size; + new->fh.modtime = modtime; + new->fh.checksum = 0xffff0000; + new->fh.rfrcsum = 0xffff0000; + new->fh.rfcsum = 0xffff0000; + new->fh.recvcsum = 0xffff0000; + strncpy(new->fh.idstring, "OFT_Windows ICBMFT V1.1 32", 31); + if (filename) { + strncpy(new->fh.name, filename, 63); + new->fh.name[63] = '\0'; + } + + new->next = sess->oft_info; + sess->oft_info = new; + + return new; +} + +faim_export struct aim_rv_proxy_info *aim_rv_proxy_createinfo(aim_session_t *sess, const guint8 *cookie, + guint16 port) +{ + struct aim_rv_proxy_info *proxy_info; + + if (!(proxy_info = (struct aim_rv_proxy_info*)calloc(1, sizeof(struct aim_rv_proxy_info)))) + return NULL; + + proxy_info->sess = sess; + proxy_info->port = port; + proxy_info->packet_ver = AIM_RV_PROXY_PACKETVER_DFLT; + proxy_info->unknownA = AIM_RV_PROXY_UNKNOWNA_DFLT; + + if (cookie) + memcpy(proxy_info->cookie, cookie, 8); + + return proxy_info; +} + +/** + * Remove the given oft_info struct from the oft_info linked list, and + * then free its memory. + * + * @param sess The session. + * @param oft_info The aim_oft_info struct that we're destroying. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_oft_destroyinfo(struct aim_oft_info *oft_info) +{ + aim_session_t *sess; + + if (!oft_info || !(sess = oft_info->sess)) + return -EINVAL; + + if (sess->oft_info && (sess->oft_info == oft_info)) { + sess->oft_info = sess->oft_info->next; + } else { + struct aim_oft_info *cur; + for (cur=sess->oft_info; (cur->next && (cur->next!=oft_info)); cur=cur->next); + if (cur->next) + cur->next = cur->next->next; + } + + free(oft_info->sn); + free(oft_info->proxyip); + free(oft_info->clientip); + free(oft_info->verifiedip); + free(oft_info); + + return 0; +} + +/** + * Creates a listener socket so the other dude can connect to us. + * + * You'll want to set up some kind of watcher on this socket. + * When the state changes, call aim_handlerendconnection with + * the connection returned by this. aim_handlerendconnection + * will accept the pending connection and stop listening. + * + * @param sess The session. + * @param oft_info File transfer information associated with this + * connection. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_sendfile_listen(aim_session_t *sess, struct aim_oft_info *oft_info, int listenfd) +{ + if (!oft_info) + return -EINVAL; + + if (!(oft_info->conn = aim_newconn(sess, AIM_CONN_TYPE_LISTENER))) { + close(listenfd); + return -ENOMEM; + } + + oft_info->conn->fd = listenfd; + oft_info->conn->subtype = AIM_CONN_SUBTYPE_OFT_SENDFILE; + oft_info->conn->lastactivity = time(NULL); + + return 0; +} + +/** + * Extract an &aim_fileheader_t from the given buffer. + * + * @param bs The should be from an incoming rendezvous packet. + * @return A pointer to new struct on success, or NULL on error. + */ +static struct aim_fileheader_t *aim_oft_getheader(aim_bstream_t *bs) +{ + struct aim_fileheader_t *fh; + + if (!(fh = calloc(1, sizeof(struct aim_fileheader_t)))) + return NULL; + + /* The bstream should be positioned after the hdrtype. */ + aimbs_getrawbuf(bs, fh->bcookie, 8); + fh->encrypt = aimbs_get16(bs); + fh->compress = aimbs_get16(bs); + fh->totfiles = aimbs_get16(bs); + fh->filesleft = aimbs_get16(bs); + fh->totparts = aimbs_get16(bs); + fh->partsleft = aimbs_get16(bs); + fh->totsize = aimbs_get32(bs); + fh->size = aimbs_get32(bs); + fh->modtime = aimbs_get32(bs); + fh->checksum = aimbs_get32(bs); + fh->rfrcsum = aimbs_get32(bs); + fh->rfsize = aimbs_get32(bs); + fh->cretime = aimbs_get32(bs); + fh->rfcsum = aimbs_get32(bs); + fh->nrecvd = aimbs_get32(bs); + fh->recvcsum = aimbs_get32(bs); + aimbs_getrawbuf(bs, (guchar *)fh->idstring, 32); + fh->flags = aimbs_get8(bs); + fh->lnameoffset = aimbs_get8(bs); + fh->lsizeoffset = aimbs_get8(bs); + aimbs_getrawbuf(bs, (guchar *)fh->dummy, 69); + aimbs_getrawbuf(bs, (guchar *)fh->macfileinfo, 16); + fh->nencode = aimbs_get16(bs); + fh->nlanguage = aimbs_get16(bs); + aimbs_getrawbuf(bs, (guchar *)fh->name, 64); /* XXX - filenames longer than 64B */ + fh->name[63] = '\0'; + + return fh; +} + +/** + * Fills a buffer with network-order fh data + * + * @param bs A bstream to fill -- automatically initialized + * @param fh A struct aim_fileheader_t to get data from. + * @return Return non-zero on error. + */ +static int aim_oft_buildheader(aim_bstream_t *bs, struct aim_fileheader_t *fh) +{ + guint8 *hdr; + + if (!bs || !fh) + return -EINVAL; + + if (!(hdr = (unsigned char *)calloc(1, 0x100 - 8))) + return -ENOMEM; + + aim_bstream_init(bs, hdr, 0x100 - 8); + aimbs_putraw(bs, fh->bcookie, 8); + aimbs_put16(bs, fh->encrypt); + aimbs_put16(bs, fh->compress); + aimbs_put16(bs, fh->totfiles); + aimbs_put16(bs, fh->filesleft); + aimbs_put16(bs, fh->totparts); + aimbs_put16(bs, fh->partsleft); + aimbs_put32(bs, fh->totsize); + aimbs_put32(bs, fh->size); + aimbs_put32(bs, fh->modtime); + aimbs_put32(bs, fh->checksum); + aimbs_put32(bs, fh->rfrcsum); + aimbs_put32(bs, fh->rfsize); + aimbs_put32(bs, fh->cretime); + aimbs_put32(bs, fh->rfcsum); + aimbs_put32(bs, fh->nrecvd); + aimbs_put32(bs, fh->recvcsum); + aimbs_putraw(bs, (guchar *)fh->idstring, 32); + aimbs_put8(bs, fh->flags); + aimbs_put8(bs, fh->lnameoffset); + aimbs_put8(bs, fh->lsizeoffset); + aimbs_putraw(bs, (guchar *)fh->dummy, 69); + aimbs_putraw(bs, (guchar *)fh->macfileinfo, 16); + aimbs_put16(bs, fh->nencode); + aimbs_put16(bs, fh->nlanguage); + aimbs_putraw(bs, (guchar *)fh->name, 64); /* XXX - filenames longer than 64B */ + + return 0; +} + +/** + * Create an OFT packet based on the given information, and send it on its merry way. + * + * @param sess The session. + * @param type The subtype of the OFT packet we're sending. + * @param oft_info The aim_oft_info struct with the connection and OFT + * info we're sending. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_oft_sendheader(aim_session_t *sess, guint16 type, struct aim_oft_info *oft_info) +{ + aim_frame_t *fr; + + if (!sess || !oft_info || !oft_info->conn || (oft_info->conn->type != AIM_CONN_TYPE_RENDEZVOUS)) + return -EINVAL; + +#if 0 + /* + * If you are receiving a file, the cookie should be null, if you are sending a + * file, the cookie should be the same as the one used in the ICBM negotiation + * SNACs. + */ + fh->lnameoffset = 0x1a; + fh->lsizeoffset = 0x10; + + /* These should be the same as charset and charsubset in ICBMs */ + fh->nencode = 0x0000; + fh->nlanguage = 0x0000; +#endif + + aim_oft_dirconvert_tostupid(oft_info->fh.name); + + if (!(fr = aim_tx_new(sess, oft_info->conn, AIM_FRAMETYPE_OFT, type, 0))) + return -ENOMEM; + + if (aim_oft_buildheader(&fr->data, &oft_info->fh) == -1) { + aim_frame_destroy(fr); + return -ENOMEM; + } + + memcpy(fr->hdr.rend.magic, "OFT2", 4); + fr->hdr.rend.hdrlen = aim_bstream_curpos(&fr->data) + 8; + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/** + * Create a rendezvous "init recv" packet and send it on its merry way. + * This is the first packet sent to the proxy server by the second client + * involved in this rendezvous proxy session. + * + * @param sess The session. + * @param proxy_info Changable pieces of data for this packet + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_rv_proxy_init_recv(struct aim_rv_proxy_info *proxy_info) +{ +#if 0 + aim_tlvlist_t *tlvlist_sendfile; +#endif + aim_bstream_t bs; + guint8 *bs_raw; + guint16 packet_len; + guint8 sn_len; + int err; + + err = 0; + + if (!proxy_info) + return -EINVAL; + + sn_len = strlen(proxy_info->sess->sn); + packet_len = 2 + 2 /* packet_len, packet_ver */ + + 2 + 4 /* cmd_type, unknownA */ + + 2 /* flags */ + + 1 + sn_len /* Length/value pair for screenname */ + + 8 /* ICBM Cookie */ + + 2 /* port */ + + 2 + 2 + 16; /* TLV for Filesend capability block */ + + if (!(bs_raw = malloc(packet_len))) + return -ENOMEM; + + aim_bstream_init(&bs, bs_raw, packet_len); + aimbs_put16(&bs, packet_len - 2); /* Length includes only packets after length marker */ + aimbs_put16(&bs, proxy_info->packet_ver); + aimbs_put16(&bs, AIM_RV_PROXY_INIT_RECV); + aimbs_put32(&bs, proxy_info->unknownA); + aimbs_put16(&bs, proxy_info->flags); + aimbs_put8(&bs, sn_len); + aimbs_putraw(&bs, (const guchar *)proxy_info->sess->sn, sn_len); + aimbs_put16(&bs, proxy_info->port); + aimbs_putraw(&bs, proxy_info->cookie, 8); + + aimbs_put16(&bs, 0x0001); /* Type */ + aimbs_put16(&bs, 16); /* Length */ + aimbs_putcaps(&bs, AIM_CAPS_SENDFILE); /* Value */ + + +#if 0 + /* TODO: Use built-in TLV */ + aim_tlvlist_add_caps(&tlvlist_sendfile, 0x0001, AIM_CAPS_SENDFILE); + aim_tlvlist_write(&bs, &tlvlist_sendfile); +#endif + + aim_bstream_rewind(&bs); + if (aim_bstream_send(&bs, proxy_info->conn, packet_len) != packet_len) + err = errno; + proxy_info->conn->lastactivity = time(NULL); + +#if 0 + aim_tlvlist_free(tlvlist_sendfile); +#endif + free(bs_raw); + + return err; +} + + +/** + * Create a rendezvous "init send" packet and send it on its merry way. + * This is the first packet sent to the proxy server by the client + * first indicating that this will be a proxied connection + * + * @param sess The session. + * @param proxy_info Changable pieces of data for this packet + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_rv_proxy_init_send(struct aim_rv_proxy_info *proxy_info) +{ +#if 0 + aim_tlvlist_t *tlvlist_sendfile; +#endif + aim_bstream_t bs; + guint8 *bs_raw; + guint16 packet_len; + guint8 sn_len; + int err; + + err = 0; + + if (!proxy_info) + return -EINVAL; + + sn_len = strlen(proxy_info->sess->sn); + packet_len = 2 + 2 /* packet_len, packet_ver */ + + 2 + 4 /* cmd_type, unknownA */ + + 2 /* flags */ + + 1 + sn_len /* Length/value pair for screenname */ + + 8 /* ICBM Cookie */ + + 2 + 2 + 16; /* TLV for Filesend capability block */ + + if (!(bs_raw = malloc(packet_len))) + return -ENOMEM; + + aim_bstream_init(&bs, bs_raw, packet_len); + aimbs_put16(&bs, packet_len - 2); /* Length includes only packets after length marker */ + aimbs_put16(&bs, proxy_info->packet_ver); + aimbs_put16(&bs, AIM_RV_PROXY_INIT_SEND); + aimbs_put32(&bs, proxy_info->unknownA); + aimbs_put16(&bs, proxy_info->flags); + aimbs_put8(&bs, sn_len); + aimbs_putraw(&bs, (const guchar *)proxy_info->sess->sn, sn_len); + aimbs_putraw(&bs, proxy_info->cookie, 8); + + aimbs_put16(&bs, 0x0001); /* Type */ + aimbs_put16(&bs, 16); /* Length */ + aimbs_putcaps(&bs, AIM_CAPS_SENDFILE); /* Value */ + + /* TODO: Use built-in TLV */ +#if 0 + aim_tlvlist_add_caps(&tlvlist_sendfile, 0x0001, AIM_CAPS_SENDFILE); + aim_tlvlist_write(&bs, &tlvlist_sendfile); +#endif + + aim_bstream_rewind(&bs); + if (aim_bstream_send(&bs, proxy_info->conn, packet_len) != packet_len) + err = errno; + proxy_info->conn->lastactivity = time(NULL); + +#if 0 + aim_tlvlist_free(tlvlist_sendfile); +#endif + free(bs_raw); + + return err; +} + +/** + * Handle incoming data on a rendezvous connection. This is analogous to the + * consumesnac function in rxhandlers.c, and I really think this should probably + * be in rxhandlers.c as well, but I haven't finished cleaning everything up yet. + * + * @param sess The session. + * @param fr The frame allocated for the incoming data. + * @return Return 0 if the packet was handled correctly, otherwise return the + * error number. + */ +faim_internal int aim_rxdispatch_rendezvous(aim_session_t *sess, aim_frame_t *fr) +{ + aim_conn_t *conn = fr->conn; + int ret = 1; + + if (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) { + if (fr->hdr.rend.type == 0x0001) + ret = handlehdr_odc(sess, conn, fr, &fr->data); + else + gaim_debug_info("oscar", "ODC directim frame unknown, type is %04x\n", fr->hdr.rend.type); + + } else { + aim_rxcallback_t userfunc; + struct aim_fileheader_t *header = aim_oft_getheader(&fr->data); + aim_oft_dirconvert_fromstupid(header->name); /* XXX - This should be client-side */ + + if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, fr->hdr.rend.type))) + ret = userfunc(sess, fr, conn, header->bcookie, header); + + free(header); + } + + if (ret == -1) + aim_conn_close(conn); + + return ret; +} + +/** + * Handle incoming data on a rendezvous proxy connection. This is similar to + * aim_rxdispatch_rendezvous above and should probably be kept with that function. + * + * @param sess The session. + * @param fr The frame allocated for the incoming data. + * @return Return 0 if the packet was handled correctly, otherwise return the + * error number. + */ +faim_internal struct aim_rv_proxy_info *aim_rv_proxy_read(aim_session_t *sess, aim_conn_t *conn) +{ + aim_bstream_t bs_hdr; + guint8 hdr_buf[AIM_RV_PROXY_HDR_LEN]; + aim_bstream_t bs_body; /* The body (everything but the header) of the packet */ + guint8 *body_buf = NULL; + guint8 body_len; + + char str_ip[30] = {""}; + guint8 ip_temp[4]; + + guint16 len; + struct aim_rv_proxy_info *proxy_info; + + if(!(proxy_info = malloc(sizeof(struct aim_rv_proxy_info)))) + return NULL; + + aim_bstream_init(&bs_hdr, hdr_buf, AIM_RV_PROXY_HDR_LEN); + if (aim_bstream_recv(&bs_hdr, conn->fd, AIM_RV_PROXY_HDR_LEN) == AIM_RV_PROXY_HDR_LEN) { + aim_bstream_rewind(&bs_hdr); + len = aimbs_get16(&bs_hdr); + proxy_info->packet_ver = aimbs_get16(&bs_hdr); + proxy_info->cmd_type = aimbs_get16(&bs_hdr); + proxy_info->unknownA = aimbs_get32(&bs_hdr); + proxy_info->flags = aimbs_get16(&bs_hdr); + if(proxy_info->cmd_type == AIM_RV_PROXY_READY) { + /* Do a little victory dance + * A ready packet contains no additional information */ + } else if(proxy_info->cmd_type == AIM_RV_PROXY_ERROR) { + if(len == AIM_RV_PROXY_ERROR_LEN - 2) { + body_len = AIM_RV_PROXY_ERROR_LEN - AIM_RV_PROXY_HDR_LEN; + body_buf = malloc(body_len); + aim_bstream_init(&bs_body, body_buf, body_len); + if (aim_bstream_recv(&bs_body, conn->fd, body_len) == body_len) { + aim_bstream_rewind(&bs_body); + proxy_info->err_code = aimbs_get16(&bs_body); + } else { + gaim_debug_warning("oscar","error reading rv proxy error packet\n"); + aim_conn_close(conn); + free(proxy_info); + proxy_info = NULL; + } + } else { + gaim_debug_warning("oscar","invalid length for proxy error packet\n"); + free(proxy_info); + proxy_info = NULL; + } + } else if(proxy_info->cmd_type == AIM_RV_PROXY_ACK) { + if(len == AIM_RV_PROXY_ACK_LEN - 2) { + body_len = AIM_RV_PROXY_ACK_LEN - AIM_RV_PROXY_HDR_LEN; + body_buf = malloc(body_len); + aim_bstream_init(&bs_body, body_buf, body_len); + if (aim_bstream_recv(&bs_body, conn->fd, body_len) == body_len) { + int i; + aim_bstream_rewind(&bs_body); + proxy_info->port = aimbs_get16(&bs_body); + for(i=0; i<4; i++) + ip_temp[i] = aimbs_get8(&bs_body); + snprintf(str_ip, sizeof(str_ip), "%hhu.%hhu.%hhu.%hhu", + ip_temp[0], ip_temp[1], + ip_temp[2], ip_temp[3]); + proxy_info->ip = strdup(str_ip); + } else { + gaim_debug_warning("oscar","error reading rv proxy error packet\n"); + aim_conn_close(conn); + free(proxy_info); + proxy_info = NULL; + } + } else { + gaim_debug_warning("oscar","invalid length for proxy error packet\n"); + free(proxy_info); + proxy_info = NULL; + } + } else { + gaim_debug_warning("oscar","unknown type for aim rendezvous proxy packet\n"); + } + } else { + gaim_debug_warning("oscar","error reading header of rv proxy packet\n"); + aim_conn_close(conn); + free(proxy_info); + proxy_info = NULL; + } + if(body_buf) { + free(body_buf); + body_buf = NULL; + } + return proxy_info; +} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/peer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/oscar/peer.h Sat Feb 11 21:45:18 2006 +0000 @@ -0,0 +1,161 @@ +/* + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * OFT Services + * + * For all of the above #defines, the number is the subtype + * of the SNAC. For OFT #defines, the number is the + * "hdrtype" which comes after the magic string and OFT + * packet length. + * + * I'm pretty sure the ODC ones are arbitrary right now, + * that should be changed. + */ + +#ifndef _PEER_H_ +#define _PEER_H_ + +#define AIM_CB_FAM_OFT 0xfffe /* OFT/Rvous */ + +#define OFT_TYPE_DIRECTIMCONNECTREQ 0x0001 /* connect request -- actually an OSCAR CAP */ +#define OFT_TYPE_DIRECTIMINCOMING 0x0002 +#define OFT_TYPE_DIRECTIMDISCONNECT 0x0003 +#define OFT_TYPE_DIRECTIMTYPING 0x0004 +#define OFT_TYPE_DIRECTIM_ESTABLISHED 0x0005 + +#define OFT_TYPE_PROMPT 0x0101 /* "I am going to send you this file, is that ok?" */ +#define OFT_TYPE_RESUMESOMETHING 0x0106 /* I really don't know */ +#define OFT_TYPE_ACK 0x0202 /* "Yes, it is ok for you to send me that file" */ +#define OFT_TYPE_DONE 0x0204 /* "I received that file with no problems, thanks a bunch" */ +#define OFT_TYPE_RESUME 0x0205 /* Resume transferring, sent by whoever paused? */ +#define OFT_TYPE_RESUMEACK 0x0207 /* Not really sure */ + +#define OFT_TYPE_GETFILE_REQUESTLISTING 0x1108 /* "I have a listing.txt file, do you want it?" */ +#define OFT_TYPE_GETFILE_RECEIVELISTING 0x1209 /* "Yes, please send me your listing.txt file" */ +#define OFT_TYPE_GETFILE_RECEIVEDLISTING 0x120a /* received corrupt listing.txt file? I'm just guessing about this one... */ +#define OFT_TYPE_GETFILE_ACKLISTING 0x120b /* "I received the listing.txt file successfully" */ +#define OFT_TYPE_GETFILE_REQUESTFILE 0x120c /* "Please send me this file" */ + +#define OFT_TYPE_ESTABLISHED 0xFFFF /* connection to buddy initiated */ + + +struct aim_fileheader_t { +#if 0 + char magic[4]; /* 0 */ + guint16 hdrlen; /* 4 */ + guint16 hdrtype; /* 6 */ +#endif + guchar bcookie[8]; /* 8 */ + guint16 encrypt; /* 16 */ + guint16 compress; /* 18 */ + guint16 totfiles; /* 20 */ + guint16 filesleft; /* 22 */ + guint16 totparts; /* 24 */ + guint16 partsleft; /* 26 */ + guint32 totsize; /* 28 */ + guint32 size; /* 32 */ + guint32 modtime; /* 36 */ + guint32 checksum; /* 40 */ + guint32 rfrcsum; /* 44 */ + guint32 rfsize; /* 48 */ + guint32 cretime; /* 52 */ + guint32 rfcsum; /* 56 */ + guint32 nrecvd; /* 60 */ + guint32 recvcsum; /* 64 */ + char idstring[32]; /* 68 */ + guint8 flags; /* 100 */ + guint8 lnameoffset; /* 101 */ + guint8 lsizeoffset; /* 102 */ + char dummy[69]; /* 103 */ + char macfileinfo[16]; /* 172 */ + guint16 nencode; /* 188 */ + guint16 nlanguage; /* 190 */ + char name[64]; /* 192 */ + /* 256 */ +}; + +struct aim_rv_proxy_info { + guint16 packet_ver; + guint16 cmd_type; + guint16 flags; + char* ip; /* IP address sent along with this packet */ + guint16 port; /* This is NOT the port we should use to connect. Always connect to 5190 */ + guchar cookie[8]; + guint32 unknownA; + guint16 err_code; /* Valid only for cmd_type of AIM_RV_PROXY_ERROR */ + aim_conn_t *conn; + aim_session_t *sess; +}; + +struct aim_oft_info { + guchar cookie[8]; + char *sn; + char *proxyip; + char *clientip; + char *verifiedip; + guint16 port; + + int send_or_recv; /* Send or receive */ + int method; /* What method is being used to transfer this file? DIRECT, REDIR, or PROXY */ + int stage; /* At what stage was a proxy requested? NONE, STG1, STG2*/ + int xfer_reffed; /* There are many timers, but we should only ref the xfer once */ + int redir_attempted; /* Have we previously attempted to redirect the connection? */ + guint32 res_bytes; /* The bytes already received for resuming a transfer */ + + aim_conn_t *conn; + aim_session_t *sess; + int success; /* Was the connection successful? Used for timing out the transfer. */ + struct aim_fileheader_t fh; + struct aim_oft_info *next; + struct aim_rv_proxy_info *proxy_info; +}; + +faim_export guint32 aim_oft_checksum_chunk(const guint8 *buffer, int bufferlen, guint32 prevcheck); +faim_export guint32 aim_oft_checksum_file(char *filename); +faim_export int aim_handlerendconnect(aim_session_t *sess, aim_conn_t *cur); +faim_export int aim_odc_send_typing(aim_session_t *sess, aim_conn_t *conn, int typing); +faim_export int aim_odc_send_im(aim_session_t *sess, aim_conn_t *conn, const char *msg, int len, int encoding, int isawaymsg); +faim_export const char *aim_odc_getsn(aim_conn_t *conn); +faim_export const guchar *aim_odc_getcookie(aim_conn_t *conn); +faim_export aim_conn_t *aim_odc_getconn(aim_session_t *sess, const char *sn); +faim_export aim_conn_t *aim_odc_initiate(aim_session_t *sess, const char *sn, int listenfd, + const guint8 *localip, guint16 port, const guchar *mycookie); +faim_export aim_conn_t *aim_odc_connect(aim_session_t *sess, const char *sn, const char *addr, const guchar *cookie); + +faim_export struct aim_oft_info *aim_oft_createinfo(aim_session_t *sess, const guchar *cookie, const char *sn, + const char *ip, guint16 port, guint32 size, guint32 modtime, char *filename, int send_or_recv, + int method, int stage); +faim_export int aim_oft_destroyinfo(struct aim_oft_info *oft_info); +faim_export struct aim_rv_proxy_info *aim_rv_proxy_createinfo(aim_session_t *sess, const guchar *cookie, guint16 port); +faim_export int aim_sendfile_listen(aim_session_t *sess, struct aim_oft_info *oft_info, int listenfd); +faim_export int aim_oft_sendheader(aim_session_t *sess, guint16 type, struct aim_oft_info *oft_info); + +faim_export int aim_rv_proxy_init_recv(struct aim_rv_proxy_info *proxy_info); +faim_export int aim_rv_proxy_init_send(struct aim_rv_proxy_info *proxy_info); + +faim_export int aim_sendfile_listen(aim_session_t *sess, struct aim_oft_info *oft_info, int listenfd); +faim_export int aim_oft_sendheader(aim_session_t *sess, guint16 type, struct aim_oft_info *oft_info); +faim_internal struct aim_rv_proxy_info *aim_rv_proxy_read(aim_session_t *sess, aim_conn_t *conn); + + +faim_internal int aim_rxdispatch_rendezvous(aim_session_t *sess, aim_frame_t *fr); + +#endif /* _PEER_H_ */ diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/popups.c --- a/src/protocols/oscar/popups.c Sat Feb 11 19:16:38 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,64 +0,0 @@ -/* - * Family 0x0008 - Popups. - * - * Popups are just what it sounds like. They're a way for the server to - * open up an informative box on the client's screen. - */ - -#define FAIM_INTERNAL -#include - -/* - * This is all there is to it. - * - * The message is probably HTML. - * - */ -static int parsepopup(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_tlvlist_t *tl; - int ret = 0; - char *msg, *url; - fu16_t width, height, delay; - - tl = aim_tlvlist_read(bs); - - msg = aim_tlv_getstr(tl, 0x0001, 1); - url = aim_tlv_getstr(tl, 0x0002, 1); - width = aim_tlv_get16(tl, 0x0003, 1); - height = aim_tlv_get16(tl, 0x0004, 1); - delay = aim_tlv_get16(tl, 0x0005, 1); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, msg, url, width, height, delay); - - aim_tlvlist_free(&tl); - free(msg); - free(url); - - 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 == 0x0002) - return parsepopup(sess, mod, rx, snac, bs); - - return 0; -} - -faim_internal int popups_modfirst(aim_session_t *sess, aim_module_t *mod) -{ - - mod->family = 0x0008; - mod->version = 0x0001; - mod->toolid = 0x0104; - mod->toolversion = 0x0001; - mod->flags = 0; - strncpy(mod->name, "popups", sizeof(mod->name)); - mod->snachandler = snachandler; - - return 0; -} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/rxhandlers.c --- a/src/protocols/oscar/rxhandlers.c Sat Feb 11 19:16:38 2006 +0000 +++ b/src/protocols/oscar/rxhandlers.c Sat Feb 11 21:45:18 2006 +0000 @@ -1,3 +1,23 @@ +/* + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + /* * rxhandlers.c * @@ -7,18 +27,18 @@ * */ -#define FAIM_INTERNAL -#include +#include "oscar.h" +#include "peer.h" struct aim_rxcblist_s { - fu16_t family; - fu16_t type; + guint16 family; + guint16 type; aim_rxcallback_t handler; - fu16_t flags; + guint16 flags; struct aim_rxcblist_s *next; }; -faim_internal aim_module_t *aim__findmodulebygroup(aim_session_t *sess, fu16_t group) +faim_internal aim_module_t *aim__findmodulebygroup(aim_session_t *sess, guint16 group) { aim_module_t *cur; @@ -143,7 +163,7 @@ return 0; } -static int consumenonsnac(aim_session_t *sess, aim_frame_t *rx, fu16_t family, fu16_t subtype) +static int consumenonsnac(aim_session_t *sess, aim_frame_t *rx, guint16 family, guint16 subtype) { aim_module_t *cur; aim_modsnac_t snac; @@ -170,7 +190,7 @@ { aim_tlvlist_t *tlvlist; char *msg = NULL; - fu16_t code = 0; + guint16 code = 0; aim_rxcallback_t userfunc; int ret = 1; @@ -208,8 +228,8 @@ */ static int bleck(aim_session_t *sess, aim_frame_t *frame, ...) { - fu16_t family, subtype; - fu16_t maxf, maxs; + guint16 family, subtype; + guint16 maxf, maxs; static const char *channels[6] = { "Invalid (0)", @@ -391,7 +411,7 @@ return 1; } -faim_export int aim_conn_addhandler(aim_session_t *sess, aim_conn_t *conn, fu16_t family, fu16_t type, aim_rxcallback_t newhandler, fu16_t flags) +faim_export int aim_conn_addhandler(aim_session_t *sess, aim_conn_t *conn, guint16 family, guint16 type, aim_rxcallback_t newhandler, guint16 flags) { struct aim_rxcblist_s *newcb; @@ -441,7 +461,7 @@ return 0; } -faim_internal aim_rxcallback_t aim_callhandler(aim_session_t *sess, aim_conn_t *conn, fu16_t family, fu16_t type) +faim_internal aim_rxcallback_t aim_callhandler(aim_session_t *sess, aim_conn_t *conn, guint16 family, guint16 type) { struct aim_rxcblist_s *cur; @@ -477,7 +497,7 @@ return; } -faim_internal int aim_callhandler_noparam(aim_session_t *sess, aim_conn_t *conn,fu16_t family, fu16_t type, aim_frame_t *ptr) +faim_internal int aim_callhandler_noparam(aim_session_t *sess, aim_conn_t *conn,guint16 family, guint16 type, aim_frame_t *ptr) { aim_rxcallback_t userfunc; diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/rxqueue.c --- a/src/protocols/oscar/rxqueue.c Sat Feb 11 19:16:38 2006 +0000 +++ b/src/protocols/oscar/rxqueue.c Sat Feb 11 21:45:18 2006 +0000 @@ -1,11 +1,30 @@ +/* + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + /* * This file contains the management routines for the receive * (incoming packet) queue. The actual packet handlers are in * rxhandlers.c. */ -#define FAIM_INTERNAL -#include +#include "oscar.h" #ifndef _WIN32 #include @@ -84,7 +103,7 @@ */ static int aim_get_command_flap(aim_session_t *sess, aim_conn_t *conn, aim_frame_t *fr) { - fu8_t hdr_raw[6]; + guint8 hdr_raw[6]; aim_bstream_t hdr; fr->hdrtype = AIM_FRAMETYPE_FLAP; @@ -132,7 +151,7 @@ */ static int aim_get_command_rendezvous(aim_session_t *sess, aim_conn_t *conn, aim_frame_t *fr) { - fu8_t hdr_raw[8]; + guint8 hdr_raw[8]; aim_bstream_t hdr; fr->hdrtype = AIM_FRAMETYPE_OFT; @@ -204,9 +223,9 @@ } if (payloadlen > 0) { - fu8_t *payload = NULL; + guint8 *payload = NULL; - if (!(payload = (fu8_t *) malloc(payloadlen))) { + if (!(payload = (guint8 *) malloc(payloadlen))) { aim_frame_destroy(fr); return -1; } diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/search.c --- a/src/protocols/oscar/search.c Sat Feb 11 19:16:38 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,134 +0,0 @@ -/* - * Family 0x000a - User Search. - * - * TODO: Add aim_usersearch_name() - * - */ - -#define FAIM_INTERNAL -#include - -/* - * Subtype 0x0001 - * - * XXX can this be integrated with the rest of the error handling? - */ -static int error(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; - aim_snac_t *snac2; - - /* XXX the modules interface should have already retrieved this for us */ - if (!(snac2 = aim_remsnac(sess, snac->id))) { - gaim_debug_misc("oscar", "search error: couldn't get a snac for 0x%08lx\n", snac->id); - return 0; - } - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, snac2->data /* address */); - - /* XXX freesnac()? */ - if (snac2) - free(snac2->data); - free(snac2); - - return ret; -} - -/* - * Subtype 0x0002 - * - */ -faim_export int aim_search_address(aim_session_t *sess, aim_conn_t *conn, const char *address) -{ - aim_frame_t *fr; - aim_snacid_t snacid; - - if (!sess || !conn || !address) - return -EINVAL; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+strlen(address)))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x000a, 0x0002, 0x0000, strdup(address), strlen(address)+1); - aim_putsnac(&fr->data, 0x000a, 0x0002, 0x0000, snacid); - - aimbs_putstr(&fr->data, address); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * Subtype 0x0003 - * - */ -static int reply(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) -{ - int j = 0, m, ret = 0; - aim_tlvlist_t *tlvlist; - char *cur = NULL, *buf = NULL; - aim_rxcallback_t userfunc; - aim_snac_t *snac2; - char *searchaddr = NULL; - - if ((snac2 = aim_remsnac(sess, snac->id))) - searchaddr = (char *)snac2->data; - - tlvlist = aim_tlvlist_read(bs); - m = aim_tlvlist_count(&tlvlist); - - /* XXX uhm. - * This is the only place that uses something other than 1 for the 3rd - * parameter to aim_tlv_gettlv_whatever(). - */ - while ((cur = aim_tlv_getstr(tlvlist, 0x0001, j+1)) && j < m) { - buf = realloc(buf, (j+1) * (MAXSNLEN+1)); - - strncpy(&buf[j * (MAXSNLEN+1)], cur, MAXSNLEN); - free(cur); - - j++; - } - - aim_tlvlist_free(&tlvlist); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, searchaddr, j, buf); - - /* XXX freesnac()? */ - if (snac2) - free(snac2->data); - free(snac2); - - free(buf); - - 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 == 0x0001) - return error(sess, mod, rx, snac, bs); - else if (snac->subtype == 0x0003) - return reply(sess, mod, rx, snac, bs); - - return 0; -} - -faim_internal int search_modfirst(aim_session_t *sess, aim_module_t *mod) -{ - - mod->family = 0x000a; - mod->version = 0x0001; - mod->toolid = 0x0110; - mod->toolversion = 0x0629; - mod->flags = 0; - strncpy(mod->name, "search", sizeof(mod->name)); - mod->snachandler = snachandler; - - return 0; -} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/service.c --- a/src/protocols/oscar/service.c Sat Feb 11 19:16:38 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1126 +0,0 @@ -/* - * Family 0x0001 - This is a very special group. All connections support - * this group, as it does some particularly good things (like rate limiting). - */ - -#define FAIM_INTERNAL -#define FAIM_NEED_CONN_INTERNAL -#include "aim.h" - -#include "cipher.h" - -/* Subtype 0x0002 - Client Online */ -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 - gaim_debug_misc("oscar", "aim_clientready: server supports group 0x%04x but we don't!\n", sg->group); - } - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * Subtype 0x0003 - Host Online - * - * 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; -} - -/* Subtype 0x0004 - Service request */ -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); -} - -/* - * Join a room of name roomname. This is the first step to joining an - * already created room. It's basically a Service Request for - * family 0x000e, with a little added on to specify the exchange and room - * name. - */ -faim_export int aim_chat_join(aim_session_t *sess, aim_conn_t *conn, fu16_t exchange, const char *roomname, fu16_t instance) -{ - aim_frame_t *fr; - aim_snacid_t snacid; - aim_tlvlist_t *tl = NULL; - struct chatsnacinfo csi; - - if (!sess || !conn || !roomname || !strlen(roomname)) - return -EINVAL; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 512))) - return -ENOMEM; - - memset(&csi, 0, sizeof(csi)); - csi.exchange = exchange; - strncpy(csi.name, roomname, sizeof(csi.name)); - csi.instance = instance; - - snacid = aim_cachesnac(sess, 0x0001, 0x0004, 0x0000, &csi, sizeof(csi)); - aim_putsnac(&fr->data, 0x0001, 0x0004, 0x0000, snacid); - - /* - * Requesting service chat (0x000e) - */ - aimbs_put16(&fr->data, 0x000e); - - aim_tlvlist_add_chatroom(&tl, 0x0001, exchange, roomname, instance); - aim_tlvlist_write(&fr->data, &tl); - aim_tlvlist_free(&tl); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* Subtype 0x0005 - Redirect */ -static int redirect(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) -{ - struct aim_redirect_data redir; - aim_rxcallback_t userfunc; - aim_tlvlist_t *tlvlist; - aim_snac_t *origsnac = NULL; - int ret = 0; - - memset(&redir, 0, sizeof(redir)); - - tlvlist = aim_tlvlist_read(bs); - - if (!aim_tlv_gettlv(tlvlist, 0x000d, 1) || - !aim_tlv_gettlv(tlvlist, 0x0005, 1) || - !aim_tlv_gettlv(tlvlist, 0x0006, 1)) { - aim_tlvlist_free(&tlvlist); - return 0; - } - - redir.group = aim_tlv_get16(tlvlist, 0x000d, 1); - redir.ip = aim_tlv_getstr(tlvlist, 0x0005, 1); - redir.cookielen = aim_tlv_gettlv(tlvlist, 0x0006, 1)->length; - redir.cookie = (guchar *)aim_tlv_getstr(tlvlist, 0x0006, 1); - - /* Fetch original SNAC so we can get csi if needed */ - origsnac = aim_remsnac(sess, snac->id); - - if ((redir.group == AIM_CONN_TYPE_CHAT) && origsnac) { - struct chatsnacinfo *csi = (struct chatsnacinfo *)origsnac->data; - - redir.chat.exchange = csi->exchange; - redir.chat.room = csi->name; - redir.chat.instance = csi->instance; - } - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, &redir); - - free((void *)redir.ip); - free((void *)redir.cookie); - - if (origsnac) - free(origsnac->data); - free(origsnac); - - aim_tlvlist_free(&tlvlist); - - return ret; -} - -/* Subtype 0x0006 - Request Rate Information. */ -faim_internal int aim_reqrates(aim_session_t *sess, aim_conn_t *conn) -{ - return aim_genericreq_n_snacid(sess, conn, 0x0001, 0x0006); -} - -/* - * OSCAR defines several 'rate classes'. Each class has separate - * 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; -} - -/* Subtype 0x0007 - Rate Parameters */ -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)); - - gaim_debug_misc("oscar", "--- Adding rate class %d to connection type %d: window size = %ld, clear = %ld, alert = %ld, limit = %ld, disconnect = %ld, current = %ld, max = %ld\n", rx->conn->type, rc.classid, rc.windowsize, rc.clear, rc.alert, rc.limit, rc.disconnect, rc.current, rc.max); - - 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; -} - -/* Subtype 0x0008 - Add Rate Parameter */ -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; -} - -/* Subtype 0x0009 - Delete Rate Parameter */ -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; -} - -/* Subtype 0x000a - Rate Change */ -static int ratechange(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; - 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))) - ret = userfunc(sess, rx, code, rateclass, windowsize, clear, alert, limit, disconnect, currentavg, maxavg); - - return ret; -} - -/* - * 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. - * - */ - -/* Subtype 0x000b - Service Pause */ -static int serverpause(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; -} - -/* - * Subtype 0x000c - Service Pause Acknowledgement - * - * 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; -} - -/* Subtype 0x000d - Service Resume */ -static int serverresume(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; -} - -/* Subtype 0x000e - Request self-info */ -faim_export int aim_reqpersonalinfo(aim_session_t *sess, aim_conn_t *conn) -{ - return aim_genericreq_n_snacid(sess, conn, 0x0001, 0x000e); -} - -/* Subtype 0x000f - Self User Info */ -static int selfinfo(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; - aim_userinfo_t userinfo; - - aim_info_extract(sess, bs, &userinfo); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, &userinfo); - - aim_info_free(&userinfo); - - return ret; -} - -/* Subtype 0x0010 - Evil Notification */ -static int evilnotify(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; - fu16_t newevil; - aim_userinfo_t userinfo; - - memset(&userinfo, 0, sizeof(aim_userinfo_t)); - - newevil = aimbs_get16(bs); - - if (aim_bstream_empty(bs)) - aim_info_extract(sess, bs, &userinfo); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, newevil, &userinfo); - - aim_info_free(&userinfo); - - return ret; -} - -/* - * Subtype 0x0011 - Idle Notification - * - * 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_srv_setidle(aim_session_t *sess, fu32_t idletime) -{ - aim_conn_t *conn; - - if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_BOS))) - return -EINVAL; - - return aim_genericreq_l(sess, conn, 0x0001, 0x0011, &idletime); -} - -/* - * Subtype 0x0012 - Service Migrate - * - * 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); - - gaim_debug_misc("oscar", "bifurcated migration unsupported -- group 0x%04x\n", group); - } - - tl = aim_tlvlist_read(bs); - - if (aim_tlv_gettlv(tl, 0x0005, 1)) - ip = aim_tlv_getstr(tl, 0x0005, 1); - - cktlv = aim_tlv_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_tlvlist_free(&tl); - free(ip); - - return ret; -} - -/* Subtype 0x0013 - Message of the Day */ -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_tlvlist_read(bs); - - msg = aim_tlv_getstr(tlvlist, 0x000b, 1); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, id, msg); - - free(msg); - - aim_tlvlist_free(&tlvlist); - - return ret; -} - -/* - * Subtype 0x0014 - Set 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); -} - -/* - * Subtype 0x0016 - No-op - * - * WinAIM sends these every 4min or so to keep the connection alive. Its not - * really necessary. - * - * Wha? No? Since when? I think WinAIM sends an empty channel 3 - * SNAC as a no-op... - */ -faim_export int aim_nop(aim_session_t *sess, aim_conn_t *conn) -{ - return aim_genericreq_n(sess, conn, 0x0001, 0x0016); -} - -/* - * Subtype 0x0017 - Set client versions - * - * 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 - gaim_debug_misc("oscar", "aim_setversions: server supports group 0x%04x but we don't!\n", sg->group); - } - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* Subtype 0x0018 - Host versions */ -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; -} - -/* - * Subtype 0x001e - Set various account settings (mostly ICQ related). - * - * These settings are transient, not server-stored (i.e. they only - * apply to this session, and must be re-set the next time you sign - * on). - * - * You can set your ICQ status (available, away, do not disturb, - * etc.), or whether your IP address should be hidden or not, or - * if your status is visible on ICQ web sites, and you can set - * your IP address info and what not. - * - * These are the same TLVs seen in user info. You can - * also set 0x0008 and 0x000c. - */ -faim_export int aim_setextstatus(aim_session_t *sess, fu32_t status) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - aim_tlvlist_t *tl = NULL; - fu32_t data; - - if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_MSG))) - return -EINVAL; - - data = AIM_ICQ_STATE_HIDEIP | AIM_ICQ_STATE_DIRECTREQUIREAUTH | status; - - 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_tlvlist_add_32(&tl, 0x0006, data); -#if 0 - aim_tlvlist_add_raw(&tl, 0x000c, 0x0025, chunk_of_x25_bytes_with_ip_address_etc); - aim_tlvlist_add_raw(&tl, 0x0011, 0x0005, unknown 0x01 61 10 f6 41); - aim_tlvlist_add_16(&tl, 0x0012, unknown 0x00 00); -#endif - aim_tlvlist_write(&fr->data, &tl); - aim_tlvlist_free(&tl); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * Subtype 0x001e - Extended Status. - * - * Sets your "available" message. This is currently only supported by iChat - * and Gaim. - * - * These are the same TLVs seen in user info. You can - * also set 0x0008 and 0x000c. - */ -faim_export int aim_srv_setstatusmsg(aim_session_t *sess, const char *msg) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - - if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) - return -EINVAL; - - if ((msg != NULL) && *msg != '\0') { - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + strlen(msg) + 8))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0001, 0x001e, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0001, 0x001e, 0x0000, snacid); - - aimbs_put16(&fr->data, 0x001d); /* userinfo TLV type */ - aimbs_put16(&fr->data, strlen(msg)+8); /* total length of userinfo TLV data */ - aimbs_put16(&fr->data, 0x0002); - aimbs_put8(&fr->data, 0x04); - aimbs_put8(&fr->data, strlen(msg)+4); - aimbs_put16(&fr->data, strlen(msg)); - aimbs_putstr(&fr->data, msg); - aimbs_put16(&fr->data, 0x0000); - } else { - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + 8))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, 0x0001, 0x001e, 0x0000, NULL, 0); - aim_putsnac(&fr->data, 0x0001, 0x001e, 0x0000, snacid); - - aimbs_put16(&fr->data, 0x001d); - aimbs_put16(&fr->data, 0x0008); - aimbs_put16(&fr->data, 0x0002); - aimbs_put16(&fr->data, 0x0404); - aimbs_put16(&fr->data, 0x0000); - aimbs_put16(&fr->data, 0x0000); - } - - 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 receives this request.) - * - * When the client receives 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, receiving 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. - * - */ -/* Subtype 0x001f - Client verification */ -static int memrequest(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; - fu32_t offset, len; - aim_tlvlist_t *list; - char *modname; - - offset = aimbs_get32(bs); - len = aimbs_get32(bs); - list = aim_tlvlist_read(bs); - - modname = aim_tlv_getstr(list, 0x0001, 1); - - gaim_debug_info("oscar", "Got memory request for data at 0x%08lx (%d bytes) of requested %s\n", offset, len, modname ? modname : "aim.exe"); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, offset, len, modname); - - free(modname); - aim_tlvlist_free(&list); - - return ret; -} - -/* Subtype 0x0020 - Client verification reply */ -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 */ - GaimCipher *cipher; - GaimCipherContext *context; - guchar digest[16]; - - cipher = gaim_ciphers_find_cipher("md5"); - - context = gaim_cipher_context_new(cipher, NULL); - gaim_cipher_context_append(context, buf, len); - gaim_cipher_context_digest(context, 16, digest, NULL); - gaim_cipher_context_destroy(context); - - aimbs_putraw(&fr->data, digest, 0x10); - - } else if (len == 0) { /* no length, just hash NULL (buf is optional) */ - GaimCipher *cipher; - GaimCipherContext *context; - guchar digest[16]; - fu8_t nil = '\0'; - - /* - * I'm not sure if we really need the empty append with the - * new MD5 functions, so I'll leave it in, just in case. - */ - cipher = gaim_ciphers_find_cipher("md5"); - - context = gaim_cipher_context_new(cipher, NULL); - gaim_cipher_context_append(context, &nil, 0); - gaim_cipher_context_digest(context, 16, digest, NULL); - gaim_cipher_context_destroy(context); - - aimbs_putraw(&fr->data, 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 - gaim_debug_warning("oscar", "sendmemblock: unknown hash request\n"); - - } - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * Subtype 0x0021 - Receive our extended status - * - * This is used for iChat's "available" messages, and maybe ICQ extended - * status messages? It's also used to tell the client whether or not it - * needs to upload an SSI buddy icon... who engineers this stuff, anyway? - */ -static int aim_parse_extstatus(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; - fu16_t type; - fu8_t flags, length; - - type = aimbs_get16(bs); - flags = aimbs_get8(bs); - length = aimbs_get8(bs); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) { - switch (type) { - case 0x0000: - case 0x0001: { /* buddy icon checksum */ - /* not sure what the difference between 1 and 0 is */ - fu8_t *md5 = aimbs_getraw(bs, length); - ret = userfunc(sess, rx, type, flags, length, md5); - free(md5); - } break; - case 0x0002: { /* available message */ - /* there is a second length that is just for the message */ - char *msg = aimbs_getstr(bs, aimbs_get16(bs)); - ret = userfunc(sess, rx, msg); - free(msg); - } break; - } - } - - 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 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); - else if (snac->subtype == 0x0021) - return aim_parse_extstatus(sess, mod, rx, snac, bs); - - return 0; -} - -faim_internal int service_modfirst(aim_session_t *sess, aim_module_t *mod) -{ - - mod->family = 0x0001; - mod->version = 0x0003; - mod->toolid = 0x0110; - mod->toolversion = 0x0629; - mod->flags = 0; - strncpy(mod->name, "service", sizeof(mod->name)); - mod->snachandler = snachandler; - - return 0; -} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/snac.c --- a/src/protocols/oscar/snac.c Sat Feb 11 19:16:38 2006 +0000 +++ b/src/protocols/oscar/snac.c Sat Feb 11 21:45:18 2006 +0000 @@ -1,3 +1,23 @@ +/* + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + /* * * Various SNAC-related dodads... @@ -12,8 +32,7 @@ * */ -#define FAIM_INTERNAL -#include +#include "oscar.h" /* * Called from aim_session_init() to initialize the hash. @@ -28,7 +47,7 @@ return; } -faim_internal aim_snacid_t aim_cachesnac(aim_session_t *sess, const fu16_t family, const fu16_t type, const fu16_t flags, const void *data, const int datalen) +faim_internal aim_snacid_t aim_cachesnac(aim_session_t *sess, const guint16 family, const guint16 type, const guint16 flags, const void *data, const int datalen) { aim_snac_t snac; @@ -136,7 +155,7 @@ return; } -faim_internal int aim_putsnac(aim_bstream_t *bs, fu16_t family, fu16_t subtype, fu16_t flags, aim_snacid_t snacid) +faim_internal int aim_putsnac(aim_bstream_t *bs, guint16 family, guint16 subtype, guint16 flags, aim_snacid_t snacid) { aimbs_put16(bs, family); diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/snactypes.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/oscar/snactypes.h Sat Feb 11 21:45:18 2006 +0000 @@ -0,0 +1,302 @@ +/* + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * AIM Callback Types + * + */ +#ifndef _SNACTYPES_H_ +#define _SNACTYPES_H_ + +/* + * SNAC Families. + */ +#define AIM_CB_FAM_ACK 0x0000 +#define OSCAR_FAMILY_OSERVICE 0x0001 +#define OSCAR_FAMILY_LOCATE 0x0002 +#define OSCAR_FAMILY_BUDDY 0x0003 +#define OSCAR_FAMILY_ICBM 0x0004 +#define OSCAR_FAMILY_ADVERT 0x0005 +#define OSCAR_FAMILY_INVITE 0x0006 +#define OSCAR_FAMILY_ADMIN 0x0007 +#define OSCAR_FAMILY_POPUP 0x0008 +#define OSCAR_FAMILY_BOS 0x0009 +#define OSCAR_FAMILY_USERLOOKUP 0x000a +#define OSCAR_FAMILY_STATS 0x000b +#define OSCAR_FAMILY_TRANSLATE 0x000c +#define OSCAR_FAMILY_CHATNAV 0x000d +#define OSCAR_FAMILY_CHAT 0x000e +#define OSCAR_FAMILY_ODIR 0x000f +#define OSCAR_FAMILY_BART 0x0010 +#define OSCAR_FAMILY_FEEDBAG 0x0013 +#define OSCAR_FAMILY_ICQ 0x0015 +#define OSCAR_FAMILY_AUTH 0x0017 +#define OSCAR_FAMILY_ALERT 0x0018 + +#define AIM_CB_FAM_SPECIAL 0xffff /* Internal libfaim use */ + +/* + * SNAC Family: Ack. + * + * Not really a family, but treating it as one really + * helps it fit into the libfaim callback structure better. + * + */ +#define AIM_CB_ACK_ACK 0x0001 + +/* + * SNAC Family: General. + */ +#define OSCAR_SUBTYPE_OSERVICE_ERROR 0x0001 +#define OSCAR_SUBTYPE_OSERVICE_CLIENTREADY 0x0002 +#define OSCAR_SUBTYPE_OSERVICE_SERVERREADY 0x0003 +#define OSCAR_SUBTYPE_OSERVICE_SERVICEREQ 0x0004 +#define OSCAR_SUBTYPE_OSERVICE_REDIRECT 0x0005 +#define OSCAR_SUBTYPE_OSERVICE_RATEINFOREQ 0x0006 +#define OSCAR_SUBTYPE_OSERVICE_RATEINFO 0x0007 +#define OSCAR_SUBTYPE_OSERVICE_RATEINFOACK 0x0008 +#define OSCAR_SUBTYPE_OSERVICE_RATECHANGE 0x000a +#define OSCAR_SUBTYPE_OSERVICE_SERVERPAUSE 0x000b +#define OSCAR_SUBTYPE_OSERVICE_SERVERRESUME 0x000d +#define OSCAR_SUBTYPE_OSERVICE_REQSELFINFO 0x000e +#define OSCAR_SUBTYPE_OSERVICE_SELFINFO 0x000f +#define OSCAR_SUBTYPE_OSERVICE_EVIL 0x0010 +#define OSCAR_SUBTYPE_OSERVICE_SETIDLE 0x0011 +#define OSCAR_SUBTYPE_OSERVICE_MIGRATIONREQ 0x0012 +#define OSCAR_SUBTYPE_OSERVICE_MOTD 0x0013 +#define OSCAR_SUBTYPE_OSERVICE_SETPRIVFLAGS 0x0014 +#define OSCAR_SUBTYPE_OSERVICE_WELLKNOWNURL 0x0015 +#define OSCAR_SUBTYPE_OSERVICE_NOP 0x0016 +#define OSCAR_SUBTYPE_OSERVICE_DEFAULT 0xffff + +/* + * SNAC Family: Location Services. + */ +#define OSCAR_SUBTYPE_LOCATE_ERROR 0x0001 +#define OSCAR_SUBTYPE_LOCATE_REQRIGHTS 0x0002 +#define OSCAR_SUBTYPE_LOCATE_RIGHTSINFO 0x0003 +#define OSCAR_SUBTYPE_LOCATE_SETUSERINFO 0x0004 +#define OSCAR_SUBTYPE_LOCATE_REQUSERINFO 0x0005 +#define OSCAR_SUBTYPE_LOCATE_USERINFO 0x0006 +#define OSCAR_SUBTYPE_LOCATE_WATCHERSUBREQ 0x0007 +#define OSCAR_SUBTYPE_LOCATE_WATCHERNOT 0x0008 +#define OSCAR_SUBTYPE_LOCATE_GOTINFOBLOCK 0xfffd +#define OSCAR_SUBTYPE_LOCATE_REQUESTINFOTIMEOUT 0xfffe +#define OSCAR_SUBTYPE_LOCATE_DEFAULT 0xffff + +/* + * SNAC Family: Buddy List Management Services. + */ +#define OSCAR_SUBTYPE_BUDDY_ERROR 0x0001 +#define OSCAR_SUBTYPE_BUDDY_REQRIGHTS 0x0002 +#define OSCAR_SUBTYPE_BUDDY_RIGHTSINFO 0x0003 +#define OSCAR_SUBTYPE_BUDDY_ADDBUDDY 0x0004 +#define OSCAR_SUBTYPE_BUDDY_REMBUDDY 0x0005 +#define OSCAR_SUBTYPE_BUDDY_REJECT 0x000a +#define OSCAR_SUBTYPE_BUDDY_ONCOMING 0x000b +#define OSCAR_SUBTYPE_BUDDY_OFFGOING 0x000c +#define OSCAR_SUBTYPE_BUDDY_DEFAULT 0xffff + +/* + * SNAC Family: Messaging Services. + */ +#define OSCAR_SUBTYPE_ICBM_ERROR 0x0001 +#define OSCAR_SUBTYPE_ICBM_PARAMINFO 0x0005 +#define OSCAR_SUBTYPE_ICBM_INCOMING 0x0007 +#define OSCAR_SUBTYPE_ICBM_EVIL 0x0009 +#define OSCAR_SUBTYPE_ICBM_MISSEDCALL 0x000a +#define OSCAR_SUBTYPE_ICBM_CLIENTAUTORESP 0x000b +#define OSCAR_SUBTYPE_ICBM_ACK 0x000c +#define OSCAR_SUBTYPE_ICBM_MTN 0x0014 +#define OSCAR_SUBTYPE_ICBM_DEFAULT 0xffff + +/* + * SNAC Family: Advertisement Services + */ +#define OSCAR_SUBTYPE_ADVERT_ERROR 0x0001 +#define OSCAR_SUBTYPE_ADVERT_DEFAULT 0xffff + +/* + * SNAC Family: Invitation Services. + */ +#define OSCAR_SUBTYPE_INVITE_ERROR 0x0001 +#define OSCAR_SUBTYPE_INVITE_DEFAULT 0xffff + +/* + * SNAC Family: Administrative Services. + */ +#define OSCAR_SUBTYPE_ADMIN_ERROR 0x0001 +#define OSCAR_SUBTYPE_ADMIN_INFOCHANGE_REPLY 0x0005 +#define OSCAR_SUBTYPE_ADMIN_DEFAULT 0xffff + +/* + * SNAC Family: Popup Messages + */ +#define OSCAR_SUBTYPE_POPUP_ERROR 0x0001 +#define OSCAR_SUBTYPE_POPUP_DEFAULT 0xffff + +/* + * SNAC Family: Misc BOS Services. + */ +#define OSCAR_SUBTYPE_BOS_ERROR 0x0001 +#define OSCAR_SUBTYPE_BOS_RIGHTSQUERY 0x0002 +#define OSCAR_SUBTYPE_BOS_RIGHTS 0x0003 +#define OSCAR_SUBTYPE_BOS_DEFAULT 0xffff + +/* + * SNAC Family: User Lookup Services + */ +#define OSCAR_SUBTYPE_USERLOOKUP_ERROR 0x0001 +#define OSCAR_SUBTYPE_USERLOOKUP_DEFAULT 0xffff + +/* + * SNAC Family: User Status Services + */ +#define OSCAR_SUBTYPE_STATS_ERROR 0x0001 +#define OSCAR_SUBTYPE_STATS_SETREPORTINTERVAL 0x0002 +#define OSCAR_SUBTYPE_STATS_REPORTACK 0x0004 +#define OSCAR_SUBTYPE_STATS_DEFAULT 0xffff + +/* + * SNAC Family: Translation Services + */ +#define OSCAR_SUBTYPE_TRANSLATE_ERROR 0x0001 +#define OSCAR_SUBTYPE_TRANSLATE_DEFAULT 0xffff + +/* + * SNAC Family: Chat Navigation Services + */ +#define OSCAR_SUBTYPE_CHATNAV_ERROR 0x0001 +#define OSCAR_SUBTYPE_CHATNAV_CREATE 0x0008 +#define OSCAR_SUBTYPE_CHATNAV_INFO 0x0009 +#define OSCAR_SUBTYPE_CHATNAV_DEFAULT 0xffff + +/* + * SNAC Family: Chat Services + */ +#define OSCAR_SUBTYPE_CHAT_ERROR 0x0001 +#define OSCAR_SUBTYPE_CHAT_ROOMINFOUPDATE 0x0002 +#define OSCAR_SUBTYPE_CHAT_USERJOIN 0x0003 +#define OSCAR_SUBTYPE_CHAT_USERLEAVE 0x0004 +#define OSCAR_SUBTYPE_CHAT_OUTGOINGMSG 0x0005 +#define OSCAR_SUBTYPE_CHAT_INCOMINGMSG 0x0006 +#define OSCAR_SUBTYPE_CHAT_DEFAULT 0xffff + +/* + * SNAC Family: "New" Search + */ +#define OSCAR_SUBTYPE_ODIR_ERROR 0x0001 +#define OSCAR_SUBTYPE_ODIR_SEARCH 0x0002 +#define OSCAR_SUBTYPE_ODIR_RESULTS 0x0003 + +/* + * SNAC Family: Buddy icons + */ +#define OSCAR_SUBTYPE_BART_ERROR 0x0001 +#define OSCAR_SUBTYPE_BART_REQUEST 0x0004 +#define OSCAR_SUBTYPE_BART_RESPONSE 0x0005 + +/* + * SNAC Family: Server-Stored Buddy Lists + */ +#define OSCAR_SUBTYPE_FEEDBAG_ERROR 0x0001 +#define OSCAR_SUBTYPE_FEEDBAG_REQRIGHTS 0x0002 +#define OSCAR_SUBTYPE_FEEDBAG_RIGHTSINFO 0x0003 +#define OSCAR_SUBTYPE_FEEDBAG_REQDATA 0x0004 +#define OSCAR_SUBTYPE_FEEDBAG_REQIFCHANGED 0x0005 +#define OSCAR_SUBTYPE_FEEDBAG_LIST 0x0006 +#define OSCAR_SUBTYPE_FEEDBAG_ACTIVATE 0x0007 +#define OSCAR_SUBTYPE_FEEDBAG_ADD 0x0008 +#define OSCAR_SUBTYPE_FEEDBAG_MOD 0x0009 +#define OSCAR_SUBTYPE_FEEDBAG_DEL 0x000A +#define OSCAR_SUBTYPE_FEEDBAG_SRVACK 0x000E +#define OSCAR_SUBTYPE_FEEDBAG_NOLIST 0x000F +#define OSCAR_SUBTYPE_FEEDBAG_EDITSTART 0x0011 +#define OSCAR_SUBTYPE_FEEDBAG_EDITSTOP 0x0012 +#define OSCAR_SUBTYPE_FEEDBAG_SENDAUTH 0x0014 +#define OSCAR_SUBTYPE_FEEDBAG_RECVAUTH 0x0015 +#define OSCAR_SUBTYPE_FEEDBAG_SENDAUTHREQ 0x0018 +#define OSCAR_SUBTYPE_FEEDBAG_RECVAUTHREQ 0x0019 +#define OSCAR_SUBTYPE_FEEDBAG_SENDAUTHREP 0x001a +#define OSCAR_SUBTYPE_FEEDBAG_RECVAUTHREP 0x001b +#define OSCAR_SUBTYPE_FEEDBAG_ADDED 0x001c + +/* + * SNAC Family: ICQ + * + * Most of these are actually special. + */ +#define OSCAR_SUBTYPE_ICQ_ERROR 0x0001 +#define OSCAR_SUBTYPE_ICQ_OFFLINEMSG 0x00f0 +#define OSCAR_SUBTYPE_ICQ_OFFLINEMSGCOMPLETE 0x00f1 +#define OSCAR_SUBTYPE_ICQ_INFO 0x00f2 +#define OSCAR_SUBTYPE_ICQ_ALIAS 0x00f3 +#define OSCAR_SUBTYPE_ICQ_DEFAULT 0xffff + +/* + * SNAC Family: Authorizer + * + * Used only in protocol versions three and above. + * + */ +#define OSCAR_SUBTYPE_AUTH_ERROR 0x0001 +#define OSCAR_SUBTYPE_AUTH_LOGINREQEST 0x0002 +#define OSCAR_SUBTYPE_AUTH_LOGINRESPONSE 0x0003 +#define OSCAR_SUBTYPE_AUTH_AUTHREQ 0x0006 +#define OSCAR_SUBTYPE_AUTH_AUTHRESPONSE 0x0007 +#define OSCAR_SUBTYPE_AUTH_SECURID_REQUEST 0x000a +#define OSCAR_SUBTYPE_AUTH_SECURID_RESPONSE 0x000b + +/* + * SNAC Family: Email + * + * Used for getting information on the email address + * associated with your screen name. + * + */ +#define OSCAR_SUBTYPE_ALERT_ERROR 0x0001 +#define OSCAR_SUBTYPE_ALERT_SENDCOOKIES 0x0006 +#define OSCAR_SUBTYPE_ALERT_MAILSTATUS 0x0007 +#define OSCAR_SUBTYPE_ALERT_INIT 0x0016 + +/* + * SNAC Family: Internal Messages + * + * This isn't truly a SNAC family either, but using + * these, we can integrated non-SNAC services into + * the SNAC-centered libfaim callback structure. + * + */ +#define AIM_CB_SPECIAL_AUTHSUCCESS 0x0001 +#define AIM_CB_SPECIAL_AUTHOTHER 0x0002 +#define AIM_CB_SPECIAL_CONNERR 0x0003 +#define AIM_CB_SPECIAL_CONNCOMPLETE 0x0004 +#define AIM_CB_SPECIAL_FLAPVER 0x0005 +#define AIM_CB_SPECIAL_CONNINITDONE 0x0006 +#define AIM_CB_SPECIAL_IMAGETRANSFER 0x0007 +#define AIM_CB_SPECIAL_MSGTIMEOUT 0x0008 +#define AIM_CB_SPECIAL_CONNDEAD 0x0009 +#define AIM_CB_SPECIAL_UNKNOWN 0xffff +#define AIM_CB_SPECIAL_DEFAULT AIM_CB_SPECIAL_UNKNOWN + +/* SNAC flags */ +#define AIM_SNACFLAGS_DESTRUCTOR 0x0001 + +#endif /* _SNACTYPES_H_ */ diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/ssi.c --- a/src/protocols/oscar/ssi.c Sat Feb 11 19:16:38 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1988 +0,0 @@ -/* - * Family 0x0013 - Server-Side/Stored Information. - * - * Relatively new facility that allows certain types of information, such as - * a user's buddy list, permit/deny list, and permit/deny preferences, to be - * stored on the server, so that they can be accessed from any client. - * - * We keep 2 copies of SSI data: - * 1) An exact copy of what is stored on the AIM servers. - * 2) A local copy that we make changes to, and then send diffs - * between this and the exact copy to keep them in sync. - * - * All the "aim_ssi_itemlist_bleh" functions near the top just modify the list - * that is given to them (i.e. they don't send SNACs). - * - * The SNAC sending and receiving functions are lower down in the file, and - * they're simpler. They are in the order of the subtypes they deal with, - * starting with the request rights function (subtype 0x0002), then parse - * rights (subtype 0x0003), then--well, you get the idea. - * - * This is entirely too complicated. - * You don't know the half of it. - * - */ - -#define FAIM_INTERNAL -#include - -/** - * Locally rebuild the 0x00c8 TLV in the additional data of the given group. - * - * @param list A pointer to a pointer to the current list of items. - * @param name A null terminated string containing the group name, or NULL - * if you want to modify the master group. - * @return Return a pointer to the modified item. - */ -static struct aim_ssi_item *aim_ssi_itemlist_rebuildgroup(struct aim_ssi_item *list, const char *name) -{ - int newlen; - struct aim_ssi_item *cur, *group; - - if (!list) - return NULL; - - /* Find the group */ - if (!(group = aim_ssi_itemlist_finditem(list, name, NULL, AIM_SSI_TYPE_GROUP))) - return NULL; - - /* Find the length for the new additional data */ - newlen = 0; - if (group->gid == 0x0000) { - for (cur=list; cur; cur=cur->next) - if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid != 0x0000)) - newlen += 2; - } else { - for (cur=list; cur; cur=cur->next) - if ((cur->gid == group->gid) && (cur->type == AIM_SSI_TYPE_BUDDY)) - newlen += 2; - } - - /* Build the new TLV list */ - if (newlen > 0) { - fu8_t *newdata; - - if (!(newdata = (fu8_t *)malloc((newlen)*sizeof(fu8_t)))) - return NULL; - newlen = 0; - if (group->gid == 0x0000) { - for (cur=list; cur; cur=cur->next) - if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid != 0x0000)) - newlen += aimutil_put16(newdata+newlen, cur->gid); - } else { - for (cur=list; cur; cur=cur->next) - if ((cur->gid == group->gid) && (cur->type == AIM_SSI_TYPE_BUDDY)) - newlen += aimutil_put16(newdata+newlen, cur->bid); - } - aim_tlvlist_replace_raw(&group->data, 0x00c8, newlen, newdata); - - free(newdata); - } - - return group; -} - -/** - * Locally add a new item to the given item list. - * - * @param list A pointer to a pointer to the current list of items. - * @param name A null terminated string of the name of the new item, or NULL if the - * item should have no name. - * @param gid The group ID# you want the new item to have, or 0xFFFF if we should pick something. - * @param bid The buddy ID# you want the new item to have, or 0xFFFF if we should pick something. - * @param type The type of the item, 0x0000 for a contact, 0x0001 for a group, etc. - * @param data The additional data for the new item. - * @return A pointer to the newly created item. - */ -static struct aim_ssi_item *aim_ssi_itemlist_add(struct aim_ssi_item **list, const char *name, fu16_t gid, fu16_t bid, fu16_t type, aim_tlvlist_t *data) -{ - int i; - struct aim_ssi_item *cur, *new; - - if (!list) - return NULL; - - if (!(new = (struct aim_ssi_item *)malloc(sizeof(struct aim_ssi_item)))) - return NULL; - - /* Set the name */ - if (name) { - new->name = (char *)malloc((strlen(name)+1)*sizeof(char)); - strcpy(new->name, name); - } else - new->name = NULL; - - /* Set the group ID# and buddy ID# */ - new->gid = gid; - new->bid = bid; - if (type == AIM_SSI_TYPE_GROUP) { - if ((new->gid == 0xFFFF) && name) { - do { - new->gid += 0x0001; - for (cur=*list, i=0; ((cur) && (!i)); cur=cur->next) - if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid == new->gid)) - i=1; - } while (i); - } - } else { - if (new->bid == 0xFFFF) { - do { - new->bid += 0x0001; - for (cur=*list, i=0; ((cur) && (!i)); cur=cur->next) - if ((cur->bid == new->bid) && (cur->gid == new->gid)) - i=1; - } while (i); - } - } - - /* Set the type */ - new->type = type; - - /* Set the TLV list */ - new->data = aim_tlvlist_copy(data); - - /* Add the item to the list in the correct numerical position. Fancy, eh? */ - if (*list) { - if ((new->gid < (*list)->gid) || ((new->gid == (*list)->gid) && (new->bid < (*list)->bid))) { - new->next = *list; - *list = new; - } else { - struct aim_ssi_item *prev; - for ((prev=*list, cur=(*list)->next); (cur && ((new->gid > cur->gid) || ((new->gid == cur->gid) && (new->bid > cur->bid)))); prev=cur, cur=cur->next); - new->next = prev->next; - prev->next = new; - } - } else { - new->next = *list; - *list = new; - } - - return new; -} - -/** - * Locally delete an item from the given item list. - * - * @param list A pointer to a pointer to the current list of items. - * @param del A pointer to the item you want to remove from the list. - * @return Return 0 if no errors, otherwise return the error number. - */ -static int aim_ssi_itemlist_del(struct aim_ssi_item **list, struct aim_ssi_item *del) -{ - if (!list || !(*list) || !del) - return -EINVAL; - - /* Remove the item from the list */ - if (*list == del) { - *list = (*list)->next; - } else { - struct aim_ssi_item *cur; - for (cur=*list; (cur->next && (cur->next!=del)); cur=cur->next); - if (cur->next) - cur->next = del->next; - } - - /* Free the removed item */ - free(del->name); - aim_tlvlist_free(&del->data); - free(del); - - return 0; -} - -/** - * Compare two items to see if they have the same data. - * - * @param cur1 A pointer to a pointer to the first item. - * @param cur2 A pointer to a pointer to the second item. - * @return Return 0 if no differences, or a number if there are differences. - */ -static int aim_ssi_itemlist_cmp(struct aim_ssi_item *cur1, struct aim_ssi_item *cur2) -{ - if (!cur1 || !cur2) - return 1; - - if (cur1->data && !cur2->data) - return 2; - - if (!cur1->data && cur2->data) - return 3; - - if ((cur1->data && cur2->data) && (aim_tlvlist_cmp(cur1->data, cur2->data))) - return 4; - - if (cur1->name && !cur2->name) - return 5; - - if (!cur1->name && cur2->name) - return 6; - - if (cur1->name && cur2->name && aim_sncmp(cur1->name, cur2->name)) - return 7; - - if (cur1->gid != cur2->gid) - return 8; - - if (cur1->bid != cur2->bid) - return 9; - - if (cur1->type != cur2->type) - return 10; - - return 0; -} - -static int aim_ssi_itemlist_valid(struct aim_ssi_item *list, struct aim_ssi_item *item) -{ - struct aim_ssi_item *cur; - for (cur=list; cur; cur=cur->next) - if (cur == item) - return 1; - return 0; -} - -/** - * Locally find an item given a group ID# and a buddy ID#. - * - * @param list A pointer to the current list of items. - * @param gid The group ID# of the desired item. - * @param bid The buddy ID# of the desired item. - * @return Return a pointer to the item if found, else return NULL; - */ -faim_export struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_item *list, fu16_t gid, fu16_t bid) -{ - struct aim_ssi_item *cur; - for (cur=list; cur; cur=cur->next) - if ((cur->gid == gid) && (cur->bid == bid)) - return cur; - return NULL; -} - -/** - * Locally find an item given a group name, screen name, and type. If group name - * and screen name are null, then just return the first item of the given type. - * - * @param list A pointer to the current list of items. - * @param gn The group name of the desired item. - * @param bn The buddy name of the desired item. - * @param type The type of the desired item. - * @return Return a pointer to the item if found, else return NULL. - */ -faim_export struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_item *list, const char *gn, const char *sn, fu16_t type) -{ - struct aim_ssi_item *cur; - if (!list) - return NULL; - - if (gn && sn) { /* For finding buddies in groups */ - for (cur=list; cur; cur=cur->next) - if ((cur->type == type) && (cur->name) && !(aim_sncmp(cur->name, sn))) { - struct aim_ssi_item *curg; - for (curg=list; curg; curg=curg->next) - if ((curg->type == AIM_SSI_TYPE_GROUP) && (curg->gid == cur->gid) && (curg->name) && !(aim_sncmp(curg->name, gn))) - return cur; - } - - } else if (gn) { /* For finding groups */ - for (cur=list; cur; cur=cur->next) { - if ((cur->type == type) && (cur->bid == 0x0000) && (cur->name) && !(aim_sncmp(cur->name, gn))) { - return cur; - } - } - - } else if (sn) { /* For finding permits, denies, and ignores */ - for (cur=list; cur; cur=cur->next) { - if ((cur->type == type) && (cur->name) && !(aim_sncmp(cur->name, sn))) { - return cur; - } - } - - /* For stuff without names--permit deny setting, visibility mask, etc. */ - } else for (cur=list; cur; cur=cur->next) { - if ((cur->type == type) && (!cur->name)) - return cur; - } - - return NULL; -} - -/** - * Check if the given buddy exists in any group in the buddy list. - * - * @param list A pointer to the current list of items. - * @param sn The group name of the desired item. - * @return Return a pointer to the name of the item if found, else return NULL; - */ -faim_export struct aim_ssi_item *aim_ssi_itemlist_exists(struct aim_ssi_item *list, const char *sn) -{ - struct aim_ssi_item *cur; - if (!list || !sn) - return NULL; - for (cur=list; cur; cur=cur->next) - if ((cur->type == AIM_SSI_TYPE_BUDDY) && (cur->name) && (!aim_sncmp(cur->name, sn))) - return cur; - return NULL; -} - -/** - * Locally find the parent item of the given buddy name. - * - * @param list A pointer to the current list of items. - * @param bn The buddy name of the desired item. - * @return Return a pointer to the name of the item if found, else return NULL; - */ -faim_export char *aim_ssi_itemlist_findparentname(struct aim_ssi_item *list, const char *sn) -{ - struct aim_ssi_item *cur, *curg; - if (!list || !sn) - return NULL; - if (!(cur = aim_ssi_itemlist_exists(list, sn))) - return NULL; - if (!(curg = aim_ssi_itemlist_find(list, cur->gid, 0x0000))) - return NULL; - return curg->name; -} - -/** - * Locally find the permit/deny setting item, and return the setting. - * - * @param list A pointer to the current list of items. - * @return Return the current SSI permit deny setting, or 0 if no setting was found. - */ -faim_export int aim_ssi_getpermdeny(struct aim_ssi_item *list) -{ - struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PDINFO); - if (cur) { - aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x00ca, 1); - if (tlv && tlv->value) - return aimutil_get8(tlv->value); - } - return 0; -} - -/** - * Locally find the presence flag item, and return the setting. The returned setting is a - * bitmask of the user flags that you are visible to. See the AIM_FLAG_* #defines - * in aim.h - * - * @param list A pointer to the current list of items. - * @return Return the current visibility mask. - */ -faim_export fu32_t aim_ssi_getpresence(struct aim_ssi_item *list) -{ - struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS); - if (cur) { - aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x00c9, 1); - if (tlv && tlv->length) - return aimutil_get32(tlv->value); - } - return 0xFFFFFFFF; -} - -/** - * Locally find the alias of the given buddy. - * - * @param list A pointer to the current list of items. - * @param gn The group of the buddy. - * @param sn The name of the buddy. - * @return A pointer to a NULL terminated string that is the buddy's - * alias, or NULL if the buddy has no alias. You should free - * this returned value! - */ -faim_export char *aim_ssi_getalias(struct aim_ssi_item *list, const char *gn, const char *sn) -{ - struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, sn, AIM_SSI_TYPE_BUDDY); - if (cur) { - aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x0131, 1); - if (tlv && tlv->length) { - char *alias = (char *)malloc((tlv->length+1)*sizeof(char)); - strncpy(alias, (char *)tlv->value, tlv->length); - alias[tlv->length] = 0; - return alias; - } - } - return NULL; -} - -/** - * Locally find the comment of the given buddy. - * - * @param list A pointer to the current list of items. - * @param gn The group of the buddy. - * @param sn The name of the buddy. - * @return A pointer to a NULL terminated string that is the buddy's - * comment, or NULL if the buddy has no comment. You should free - * this returned value! - */ -faim_export char *aim_ssi_getcomment(struct aim_ssi_item *list, const char *gn, const char *sn) -{ - struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, sn, AIM_SSI_TYPE_BUDDY); - if (cur) { - aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x013c, 1); - if (tlv && tlv->length) { - char *alias = (char *)malloc((tlv->length+1)*sizeof(char)); - strncpy(alias, (char *)tlv->value, tlv->length); - alias[tlv->length] = 0; - return alias; - } - } - return NULL; -} - -/** - * Locally find if you are waiting for authorization for a buddy. - * - * @param list A pointer to the current list of items. - * @param gn The group of the buddy. - * @param sn The name of the buddy. - * @return 1 if you are waiting for authorization; 0 if you are not - */ -faim_export int aim_ssi_waitingforauth(struct aim_ssi_item *list, const char *gn, const char *sn) -{ - struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, sn, AIM_SSI_TYPE_BUDDY); - if (cur) { - if (aim_tlv_gettlv(cur->data, 0x0066, 1)) - return 1; - } - return 0; -} - -/** - * If there are changes, then create temporary items and - * call addmoddel. - * - * @param sess The oscar session. - * @return Return 0 if no errors, otherwise return the error number. - */ -static int aim_ssi_sync(aim_session_t *sess) -{ - struct aim_ssi_item *cur1, *cur2; - struct aim_ssi_tmp *cur, *new; - - if (!sess) - return -EINVAL; - - /* If we're waiting for an ack, we shouldn't do anything else */ - if (sess->ssi.waiting_for_ack) - return 0; - - /* - * Compare the 2 lists and create an aim_ssi_tmp for each difference. - * We should only send either additions, modifications, or deletions - * before waiting for an acknowledgement. So first do deletions, then - * additions, then modifications. Also, both the official and the local - * list should be in ascending numerical order for the group ID#s and the - * buddy ID#s, which makes things more efficient. I think. - */ - - /* Additions */ - if (!sess->ssi.pending) { - for (cur1=sess->ssi.local; cur1; cur1=cur1->next) { - if (!aim_ssi_itemlist_find(sess->ssi.official, cur1->gid, cur1->bid)) { - new = (struct aim_ssi_tmp *)malloc(sizeof(struct aim_ssi_tmp)); - new->action = AIM_CB_SSI_ADD; - new->ack = 0xffff; - new->name = NULL; - new->item = cur1; - new->next = NULL; - if (sess->ssi.pending) { - for (cur=sess->ssi.pending; cur->next; cur=cur->next); - cur->next = new; - } else - sess->ssi.pending = new; - } - } - } - - /* Deletions */ - if (!sess->ssi.pending) { - for (cur1=sess->ssi.official; cur1; cur1=cur1->next) { - if (!aim_ssi_itemlist_find(sess->ssi.local, cur1->gid, cur1->bid)) { - new = (struct aim_ssi_tmp *)malloc(sizeof(struct aim_ssi_tmp)); - new->action = AIM_CB_SSI_DEL; - new->ack = 0xffff; - new->name = NULL; - new->item = cur1; - new->next = NULL; - if (sess->ssi.pending) { - for (cur=sess->ssi.pending; cur->next; cur=cur->next); - cur->next = new; - } else - sess->ssi.pending = new; - } - } - } - - /* Modifications */ - if (!sess->ssi.pending) { - for (cur1=sess->ssi.local; cur1; cur1=cur1->next) { - cur2 = aim_ssi_itemlist_find(sess->ssi.official, cur1->gid, cur1->bid); - if (cur2 && (aim_ssi_itemlist_cmp(cur1, cur2))) { - new = (struct aim_ssi_tmp *)malloc(sizeof(struct aim_ssi_tmp)); - new->action = AIM_CB_SSI_MOD; - new->ack = 0xffff; - new->name = NULL; - new->item = cur1; - new->next = NULL; - if (sess->ssi.pending) { - for (cur=sess->ssi.pending; cur->next; cur=cur->next); - cur->next = new; - } else - sess->ssi.pending = new; - } - } - } - - /* We're out of stuff to do, so tell the AIM servers we're done and exit */ - if (!sess->ssi.pending) { - aim_ssi_modend(sess); - return 0; - } - - /* Make sure we don't send anything else between now - * and when we receive the ack for the following operation */ - sess->ssi.waiting_for_ack = 1; - - /* Now go mail off our data and wait 4 to 6 weeks */ - aim_ssi_addmoddel(sess); - - return 0; -} - -/** - * Free all SSI data. - * - * This doesn't remove it from the server, that's different. - * - * @param sess The oscar session. - * @return Return 0 if no errors, otherwise return the error number. - */ -static int aim_ssi_freelist(aim_session_t *sess) -{ - struct aim_ssi_item *cur, *del; - struct aim_ssi_tmp *curtmp, *deltmp; - - cur = sess->ssi.official; - while (cur) { - del = cur; - cur = cur->next; - free(del->name); - aim_tlvlist_free(&del->data); - free(del); - } - - cur = sess->ssi.local; - while (cur) { - del = cur; - cur = cur->next; - free(del->name); - aim_tlvlist_free(&del->data); - free(del); - } - - curtmp = sess->ssi.pending; - while (curtmp) { - deltmp = curtmp; - curtmp = curtmp->next; - free(deltmp); - } - - sess->ssi.numitems = 0; - sess->ssi.official = NULL; - sess->ssi.local = NULL; - sess->ssi.pending = NULL; - sess->ssi.timestamp = (time_t)0; - - return 0; -} - -/** - * Delete all SSI data. - * - * @param sess The oscar session. - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_ssi_deletelist(aim_session_t *sess) -{ - struct aim_ssi_item *cur, *del; - - if (!sess) - return -EINVAL; - - /* Free the local list */ - cur = sess->ssi.local; - while (cur) { - del = cur; - cur = cur->next; - free(del->name); - aim_tlvlist_free(&del->data); - free(del); - } - sess->ssi.local = NULL; - - /* Sync our local list with the server list */ - aim_ssi_sync(sess); - - return 0; -} - -/** - * This "cleans" the ssi list. It does the following: - * 1) Makes sure all buddies, permits, and denies have names. - * 2) Makes sure that all buddies are in a group that exist. - * 3) Deletes any empty groups - * - * @param sess The oscar session. - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_ssi_cleanlist(aim_session_t *sess) -{ - struct aim_ssi_item *cur, *next; - - if (!sess) - return -EINVAL; - - /* Delete any buddies, permits, or denies with empty names. */ - /* If there are any buddies directly in the master group, add them to a real group. */ - /* DESTROY any buddies that are directly in the master group. */ - /* Do the same for buddies that are in a non-existant group. */ - /* This will kind of mess up if you hit the item limit, but this function isn't too critical */ - cur = sess->ssi.local; - while (cur) { - next = cur->next; - if (!cur->name) { - if (cur->type == AIM_SSI_TYPE_BUDDY) - aim_ssi_delbuddy(sess, NULL, NULL); - else if (cur->type == AIM_SSI_TYPE_PERMIT) - aim_ssi_delpermit(sess, NULL); - else if (cur->type == AIM_SSI_TYPE_DENY) - aim_ssi_deldeny(sess, NULL); - } else if ((cur->type == AIM_SSI_TYPE_BUDDY) && ((cur->gid == 0x0000) || (!aim_ssi_itemlist_find(sess->ssi.local, cur->gid, 0x0000)))) { - char *alias = aim_ssi_getalias(sess->ssi.local, NULL, cur->name); - aim_ssi_addbuddy(sess, cur->name, "orphans", alias, NULL, NULL, 0); - aim_ssi_delbuddy(sess, cur->name, NULL); - free(alias); - } - cur = next; - } - - /* Make sure there aren't any duplicate buddies in a group, or duplicate permits or denies */ - cur = sess->ssi.local; - while (cur) { - if ((cur->type == AIM_SSI_TYPE_BUDDY) || (cur->type == AIM_SSI_TYPE_PERMIT) || (cur->type == AIM_SSI_TYPE_DENY)) - { - struct aim_ssi_item *cur2, *next2; - cur2 = cur->next; - while (cur2) { - next2 = cur2->next; - if ((cur->type == cur2->type) && (cur->gid == cur2->gid) && (cur->name != NULL) && (cur2->name != NULL) && (!strcmp(cur->name, cur2->name))) { - aim_ssi_itemlist_del(&sess->ssi.local, cur2); - } - cur2 = next2; - } - } - cur = cur->next; - } - - /* Check if there are empty groups and delete them */ - cur = sess->ssi.local; - while (cur) { - next = cur->next; - if (cur->type == AIM_SSI_TYPE_GROUP) { - aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x00c8, 1); - if (!tlv || !tlv->length) - aim_ssi_itemlist_del(&sess->ssi.local, cur); - } - cur = next; - } - - /* Check if the master group is empty */ - if ((cur = aim_ssi_itemlist_find(sess->ssi.local, 0x0000, 0x0000)) && (!cur->data)) - aim_ssi_itemlist_del(&sess->ssi.local, cur); - - /* If we've made any changes then sync our list with the server's */ - aim_ssi_sync(sess); - - return 0; -} - -/** - * Add a buddy to the list. - * - * @param sess The oscar session. - * @param name The name of the item. - * @param group The group of the item. - * @param alias The alias/nickname of the item, or NULL. - * @param comment The buddy comment for the item, or NULL. - * @param smsnum The locally assigned SMS number, or NULL. - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_ssi_addbuddy(aim_session_t *sess, const char *name, const char *group, const char *alias, const char *comment, const char *smsnum, int needauth) -{ - struct aim_ssi_item *parent; - aim_tlvlist_t *data = NULL; - - if (!sess || !name || !group) - return -EINVAL; - - /* Find the parent */ - if (!(parent = aim_ssi_itemlist_finditem(sess->ssi.local, group, NULL, AIM_SSI_TYPE_GROUP))) { - /* Find the parent's parent (the master group) */ - if (!(parent = aim_ssi_itemlist_find(sess->ssi.local, 0x0000, 0x0000))) - if (!(parent = aim_ssi_itemlist_add(&sess->ssi.local, NULL, 0x0000, 0x0000, AIM_SSI_TYPE_GROUP, NULL))) - return -ENOMEM; - /* Add the parent */ - if (!(parent = aim_ssi_itemlist_add(&sess->ssi.local, group, 0xFFFF, 0x0000, AIM_SSI_TYPE_GROUP, NULL))) - return -ENOMEM; - - /* Modify the parent's parent (the master group) */ - aim_ssi_itemlist_rebuildgroup(sess->ssi.local, NULL); - } - - /* Create a TLV list for the new buddy */ - if (needauth) - aim_tlvlist_add_noval(&data, 0x0066); - if (alias) - aim_tlvlist_add_str(&data, 0x0131, alias); - if (smsnum) - aim_tlvlist_add_str(&data, 0x013a, smsnum); - if (comment) - aim_tlvlist_add_str(&data, 0x013c, comment); - - /* Add that bad boy */ - aim_ssi_itemlist_add(&sess->ssi.local, name, parent->gid, 0xFFFF, AIM_SSI_TYPE_BUDDY, data); - aim_tlvlist_free(&data); - - /* Modify the parent group */ - aim_ssi_itemlist_rebuildgroup(sess->ssi.local, group); - - /* Sync our local list with the server list */ - aim_ssi_sync(sess); - - return 0; -} - -/** - * Add a permit buddy to the list. - * - * @param sess The oscar session. - * @param name The name of the item.. - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_ssi_addpermit(aim_session_t *sess, const char *name) -{ - - if (!sess || !name) - return -EINVAL; - - /* Add that bad boy */ - aim_ssi_itemlist_add(&sess->ssi.local, name, 0x0000, 0xFFFF, AIM_SSI_TYPE_PERMIT, NULL); - - /* Sync our local list with the server list */ - aim_ssi_sync(sess); - - return 0; -} - -/** - * Add a deny buddy to the list. - * - * @param sess The oscar session. - * @param name The name of the item.. - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_ssi_adddeny(aim_session_t *sess, const char *name) -{ - - if (!sess || !name) - return -EINVAL; - - /* Add that bad boy */ - aim_ssi_itemlist_add(&sess->ssi.local, name, 0x0000, 0xFFFF, AIM_SSI_TYPE_DENY, NULL); - - /* Sync our local list with the server list */ - aim_ssi_sync(sess); - - return 0; -} - -/** - * Deletes a buddy from the list. - * - * @param sess The oscar session. - * @param name The name of the item, or NULL. - * @param group The group of the item, or NULL. - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_ssi_delbuddy(aim_session_t *sess, const char *name, const char *group) -{ - struct aim_ssi_item *del; - - if (!sess) - return -EINVAL; - - /* Find the buddy */ - if (!(del = aim_ssi_itemlist_finditem(sess->ssi.local, group, name, AIM_SSI_TYPE_BUDDY))) - return -EINVAL; - - /* Remove the item from the list */ - aim_ssi_itemlist_del(&sess->ssi.local, del); - - /* Modify the parent group */ - aim_ssi_itemlist_rebuildgroup(sess->ssi.local, group); - - /* Check if we should delete the parent group */ - if ((del = aim_ssi_itemlist_finditem(sess->ssi.local, group, NULL, AIM_SSI_TYPE_GROUP)) && (!del->data)) { - aim_ssi_itemlist_del(&sess->ssi.local, del); - - /* Modify the parent group */ - aim_ssi_itemlist_rebuildgroup(sess->ssi.local, NULL); - - /* Check if we should delete the parent's parent (the master group) */ - if ((del = aim_ssi_itemlist_find(sess->ssi.local, 0x0000, 0x0000)) && (!del->data)) { - aim_ssi_itemlist_del(&sess->ssi.local, del); - } - } - - /* Sync our local list with the server list */ - aim_ssi_sync(sess); - - return 0; -} - -/** - * Deletes a permit buddy from the list. - * - * @param sess The oscar session. - * @param name The name of the item, or NULL. - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_ssi_delpermit(aim_session_t *sess, const char *name) -{ - struct aim_ssi_item *del; - - if (!sess) - return -EINVAL; - - /* Find the item */ - if (!(del = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, name, AIM_SSI_TYPE_PERMIT))) - return -EINVAL; - - /* Remove the item from the list */ - aim_ssi_itemlist_del(&sess->ssi.local, del); - - /* Sync our local list with the server list */ - aim_ssi_sync(sess); - - return 0; -} - -/** - * Deletes a deny buddy from the list. - * - * @param sess The oscar session. - * @param name The name of the item, or NULL. - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_ssi_deldeny(aim_session_t *sess, const char *name) -{ - struct aim_ssi_item *del; - - if (!sess) - return -EINVAL; - - /* Find the item */ - if (!(del = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, name, AIM_SSI_TYPE_DENY))) - return -EINVAL; - - /* Remove the item from the list */ - aim_ssi_itemlist_del(&sess->ssi.local, del); - - /* Sync our local list with the server list */ - aim_ssi_sync(sess); - - return 0; -} - -/** - * Move a buddy from one group to another group. This basically just deletes the - * buddy and re-adds it. - * - * @param sess The oscar session. - * @param oldgn The group that the buddy is currently in. - * @param newgn The group that the buddy should be moved in to. - * @param sn The name of the buddy to be moved. - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_ssi_movebuddy(aim_session_t *sess, const char *oldgn, const char *newgn, const char *sn) -{ - char *alias = aim_ssi_getalias(sess->ssi.local, oldgn, sn); - aim_ssi_addbuddy(sess, sn, newgn, alias, NULL, NULL, aim_ssi_waitingforauth(sess->ssi.local, oldgn, sn)); - aim_ssi_delbuddy(sess, sn, oldgn); - free(alias); - return 0; -} - -/** - * Change the alias stored on the server for a given buddy. - * - * @param sess The oscar session. - * @param gn The group that the buddy is currently in. - * @param sn The screen name of the buddy. - * @param alias The new alias for the buddy, or NULL if you want to remove - * a buddy's comment. - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_ssi_aliasbuddy(aim_session_t *sess, const char *gn, const char *sn, const char *alias) -{ - struct aim_ssi_item *tmp; - - if (!sess || !gn || !sn) - return -EINVAL; - - if (!(tmp = aim_ssi_itemlist_finditem(sess->ssi.local, gn, sn, AIM_SSI_TYPE_BUDDY))) - return -EINVAL; - - /* Either add or remove the 0x0131 TLV from the TLV chain */ - if ((alias != NULL) && (strlen(alias) > 0)) - aim_tlvlist_replace_str(&tmp->data, 0x0131, alias); - else - aim_tlvlist_remove(&tmp->data, 0x0131); - - /* Sync our local list with the server list */ - aim_ssi_sync(sess); - - return 0; -} - -/** - * Change the comment stored on the server for a given buddy. - * - * @param sess The oscar session. - * @param gn The group that the buddy is currently in. - * @param sn The screen name of the buddy. - * @param alias The new comment for the buddy, or NULL if you want to remove - * a buddy's comment. - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_ssi_editcomment(aim_session_t *sess, const char *gn, const char *sn, const char *comment) -{ - struct aim_ssi_item *tmp; - - if (!sess || !gn || !sn) - return -EINVAL; - - if (!(tmp = aim_ssi_itemlist_finditem(sess->ssi.local, gn, sn, AIM_SSI_TYPE_BUDDY))) - return -EINVAL; - - /* Either add or remove the 0x0131 TLV from the TLV chain */ - if ((comment != NULL) && (strlen(comment) > 0)) - aim_tlvlist_replace_str(&tmp->data, 0x013c, comment); - else - aim_tlvlist_remove(&tmp->data, 0x013c); - - /* Sync our local list with the server list */ - aim_ssi_sync(sess); - - return 0; -} - -/** - * Rename a group. - * - * @param sess The oscar session. - * @param oldgn The old group name. - * @param newgn The new group name. - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_ssi_rename_group(aim_session_t *sess, const char *oldgn, const char *newgn) -{ - struct aim_ssi_item *group; - - if (!sess || !oldgn || !newgn) - return -EINVAL; - - if (!(group = aim_ssi_itemlist_finditem(sess->ssi.local, oldgn, NULL, AIM_SSI_TYPE_GROUP))) - return -EINVAL; - - free(group->name); - group->name = (char *)malloc((strlen(newgn)+1)*sizeof(char)); - strcpy(group->name, newgn); - - /* Sync our local list with the server list */ - aim_ssi_sync(sess); - - return 0; -} - -/** - * Stores your permit/deny setting on the server, and starts using it. - * - * @param sess The oscar session. - * @param permdeny Your permit/deny setting. Can be one of the following: - * 1 - Allow all users - * 2 - Block all users - * 3 - Allow only the users below - * 4 - Block only the users below - * 5 - Allow only users on my buddy list - * @param vismask A bitmask of the class of users to whom you want to be - * visible. See the AIM_FLAG_BLEH #defines in aim.h - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_ssi_setpermdeny(aim_session_t *sess, fu8_t permdeny, fu32_t vismask) -{ - struct aim_ssi_item *tmp; - - if (!sess) - return -EINVAL; - - /* Find the PDINFO item, or add it if it does not exist */ - if (!(tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, NULL, AIM_SSI_TYPE_PDINFO))) - tmp = aim_ssi_itemlist_add(&sess->ssi.local, NULL, 0x0000, 0xFFFF, AIM_SSI_TYPE_PDINFO, NULL); - - /* Need to add the 0x00ca TLV to the TLV chain */ - aim_tlvlist_replace_8(&tmp->data, 0x00ca, permdeny); - - /* Need to add the 0x00cb TLV to the TLV chain */ - aim_tlvlist_replace_32(&tmp->data, 0x00cb, vismask); - - /* Sync our local list with the server list */ - aim_ssi_sync(sess); - - return 0; -} - -/** - * Set buddy icon information - * - * @param sess The oscar session. - * @param iconcsum The MD5 checksum of the icon you are using. - * @param iconcsumlen Length of the MD5 checksum given above. Should be 0x10 bytes. - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_ssi_seticon(aim_session_t *sess, fu8_t *iconsum, fu16_t iconsumlen) -{ - struct aim_ssi_item *tmp; - fu8_t *csumdata; - - if (!sess || !iconsum || !iconsumlen) - return -EINVAL; - - /* Find the ICONINFO item, or add it if it does not exist */ - if (!(tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, "1", AIM_SSI_TYPE_ICONINFO))) { - tmp = aim_ssi_itemlist_add(&sess->ssi.local, "1", 0x0000, 0x51F4, AIM_SSI_TYPE_ICONINFO, NULL); - } - - /* Need to add the 0x00d5 TLV to the TLV chain */ - if (!(csumdata = (fu8_t *)malloc((iconsumlen+2)*sizeof(fu8_t)))) - return -ENOMEM; - csumdata[0] = 0x00; - csumdata[1] = 0x10; - memcpy(&csumdata[2], iconsum, iconsumlen); - aim_tlvlist_replace_raw(&tmp->data, 0x00d5, (iconsumlen+2) * sizeof(fu8_t), csumdata); - free(csumdata); - - /* Need to add the 0x0131 TLV to the TLV chain, used to cache the icon */ - aim_tlvlist_replace_noval(&tmp->data, 0x0131); - - /* Sync our local list with the server list */ - aim_ssi_sync(sess); - return 0; -} - -/** - * Remove a reference to a server stored buddy icon. This will make your - * icon stop showing up to other people. - * - * @param sess The oscar session. - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_ssi_delicon(aim_session_t *sess) -{ - struct aim_ssi_item *tmp; - - if (!sess) - return -EINVAL; - - /* Find the ICONINFO item and delete it if it exists*/ - if ((tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, "1", AIM_SSI_TYPE_ICONINFO))) - aim_ssi_itemlist_del(&sess->ssi.local, tmp); - - /* Sync our local list with the server list */ - aim_ssi_sync(sess); - return 0; -} - -/** - * Stores your setting for various SSI settings. Whether you - * should show up as idle or not, etc. - * - * @param sess The oscar session. - * @param presence I think it's a bitmask, but I only know what one of the bits is: - * 0x00000002 - Hide wireless? - * 0x00000400 - Allow others to see your idle time - * @return Return 0 if no errors, otherwise return the error number. - */ -faim_export int aim_ssi_setpresence(aim_session_t *sess, fu32_t presence) { - struct aim_ssi_item *tmp; - - if (!sess) - return -EINVAL; - - /* Find the PRESENCEPREFS item, or add it if it does not exist */ - if (!(tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS))) - tmp = aim_ssi_itemlist_add(&sess->ssi.local, NULL, 0x0000, 0xFFFF, AIM_SSI_TYPE_PRESENCEPREFS, NULL); - - /* Need to add the x00c9 TLV to the TLV chain */ - aim_tlvlist_replace_32(&tmp->data, 0x00c9, presence); - - /* Sync our local list with the server list */ - aim_ssi_sync(sess); - - return 0; -} - -/* - * Subtype 0x0002 - Request SSI Rights. - */ -faim_export int aim_ssi_reqrights(aim_session_t *sess) -{ - aim_conn_t *conn; - - if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI))) - return -EINVAL; - - return aim_genericreq_n_snacid(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_REQRIGHTS); -} - -/* - * Subtype 0x0003 - SSI Rights Information. - */ -static int parserights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) -{ - int ret = 0, i; - aim_rxcallback_t userfunc; - aim_tlvlist_t *tlvlist; - aim_tlv_t *tlv; - aim_bstream_t bstream; - fu16_t *maxitems; - - /* This SNAC is made up of a bunch of TLVs */ - tlvlist = aim_tlvlist_read(bs); - - /* TLV 0x0004 contains the maximum number of each item */ - if (!(tlv = aim_tlv_gettlv(tlvlist, 0x0004, 1))) { - aim_tlvlist_free(&tlvlist); - return 0; - } - - aim_bstream_init(&bstream, tlv->value, tlv->length); - - if (!(maxitems = (fu16_t *)malloc((tlv->length/2)*sizeof(fu16_t)))) { - aim_tlvlist_free(&tlvlist); - return 0; - } - - for (i=0; i<(tlv->length/2); i++) - maxitems[i] = aimbs_get16(&bstream); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, tlv->length/2, maxitems); - - aim_tlvlist_free(&tlvlist); - free(maxitems); - - return ret; -} - -/* - * Subtype 0x0004 - Request SSI Data when you don't have a timestamp and - * revision number. - * - */ -faim_export int aim_ssi_reqdata(aim_session_t *sess) -{ - aim_conn_t *conn; - - if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI))) - return -EINVAL; - - /* Free any current data, just in case */ - aim_ssi_freelist(sess); - - return aim_genericreq_n_snacid(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_REQDATA); -} - -/* - * Subtype 0x0005 - Request SSI Data when you have a timestamp and revision - * number. - * - * The data will only be sent if it is newer than the posted local - * timestamp and revision. - * - * Note that the client should never increment the revision, only the server. - * - */ -faim_export int aim_ssi_reqifchanged(aim_session_t *sess, time_t timestamp, fu16_t numitems) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - - if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI))) - return -EINVAL; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4+2))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_REQIFCHANGED, 0x0000, NULL, 0); - - aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_REQIFCHANGED, 0x0000, snacid); - aimbs_put32(&fr->data, timestamp); - aimbs_put16(&fr->data, numitems); - - aim_tx_enqueue(sess, fr); - - /* Free any current data, just in case */ - aim_ssi_freelist(sess); - - return 0; -} - -/* - * Subtype 0x0006 - SSI Data. - */ -static int parsedata(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; - fu8_t fmtver; /* guess */ - fu16_t namelen, gid, bid, type; - char *name; - aim_tlvlist_t *data; - - fmtver = aimbs_get8(bs); /* Version of ssi data. Should be 0x00 */ - sess->ssi.numitems += aimbs_get16(bs); /* # of items in this SSI SNAC */ - - /* Read in the list */ - while (aim_bstream_empty(bs) > 4) { /* last four bytes are timestamp */ - if ((namelen = aimbs_get16(bs))) - name = aimbs_getstr(bs, namelen); - else - name = NULL; - gid = aimbs_get16(bs); - bid = aimbs_get16(bs); - type = aimbs_get16(bs); - data = aim_tlvlist_readlen(bs, aimbs_get16(bs)); - aim_ssi_itemlist_add(&sess->ssi.official, name, gid, bid, type, data); - free(name); - aim_tlvlist_free(&data); - } - - /* Read in the timestamp */ - sess->ssi.timestamp = aimbs_get32(bs); - - if (!(snac->flags & 0x0001)) { - /* Make a copy of the list */ - struct aim_ssi_item *cur; - for (cur=sess->ssi.official; cur; cur=cur->next) - aim_ssi_itemlist_add(&sess->ssi.local, cur->name, cur->gid, cur->bid, cur->type, cur->data); - - sess->ssi.received_data = 1; - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, fmtver, sess->ssi.numitems, sess->ssi.official, sess->ssi.timestamp); - } - - return ret; -} - -/* - * Subtype 0x0007 - SSI Activate Data. - * - * Should be sent after receiving 13/6 or 13/f to tell the server you - * are ready to begin using the list. It will promptly give you the - * presence information for everyone in your list and put your permit/deny - * settings into effect. - * - */ -faim_export int aim_ssi_enable(aim_session_t *sess) -{ - aim_conn_t *conn; - - if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI))) - return -EINVAL; - - return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, 0x0007); -} - -/* - * Subtype 0x0008/0x0009/0x000a - SSI Add/Mod/Del Item(s). - * - * Sends the SNAC to add, modify, or delete an item from the server-stored - * information. These 3 SNACs all have an identical structure. The only - * difference is the subtype that is set for the SNAC. - * - */ -faim_export int aim_ssi_addmoddel(aim_session_t *sess) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - int snaclen; - struct aim_ssi_tmp *cur; - - if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)) || !sess->ssi.pending || !sess->ssi.pending->item) - return -EINVAL; - - /* Calculate total SNAC size */ - snaclen = 10; /* For family, subtype, flags, and SNAC ID */ - for (cur=sess->ssi.pending; cur; cur=cur->next) { - snaclen += 10; /* For length, GID, BID, type, and length */ - if (cur->item->name) - snaclen += strlen(cur->item->name); - if (cur->item->data) - snaclen += aim_tlvlist_size(&cur->item->data); - } - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, snaclen))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, sess->ssi.pending->action, 0x0000, NULL, 0); - aim_putsnac(&fr->data, AIM_CB_FAM_SSI, sess->ssi.pending->action, 0x0000, snacid); - - for (cur=sess->ssi.pending; cur; cur=cur->next) { - aimbs_put16(&fr->data, cur->item->name ? strlen(cur->item->name) : 0); - if (cur->item->name) - aimbs_putstr(&fr->data, cur->item->name); - aimbs_put16(&fr->data, cur->item->gid); - aimbs_put16(&fr->data, cur->item->bid); - aimbs_put16(&fr->data, cur->item->type); - aimbs_put16(&fr->data, cur->item->data ? aim_tlvlist_size(&cur->item->data) : 0); - if (cur->item->data) - aim_tlvlist_write(&fr->data, &cur->item->data); - } - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * Subtype 0x0008 - Incoming SSI add. - * - * Sent by the server, for example, when someone is added to - * your "Recent Buddies" group. - */ -static int parseadd(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; - char *name; - fu16_t len, gid, bid, type; - aim_tlvlist_t *data; - - while (aim_bstream_empty(bs)) { - if ((len = aimbs_get16(bs))) - name = aimbs_getstr(bs, len); - else - name = NULL; - gid = aimbs_get16(bs); - bid = aimbs_get16(bs); - type = aimbs_get16(bs); - if ((len = aimbs_get16(bs))) - data = aim_tlvlist_readlen(bs, len); - else - data = NULL; - - aim_ssi_itemlist_add(&sess->ssi.local, name, gid, bid, type, data); - aim_ssi_itemlist_add(&sess->ssi.official, name, gid, bid, type, data); - aim_tlvlist_free(&data); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, type, name); - - free(name); - } - - return ret; -} - -/* - * Subtype 0x0009 - Incoming SSI mod. - * - * XXX - It would probably be good for the client to actually do something when it gets this. - */ -static int parsemod(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; - char *name; - fu16_t len, gid, bid, type; - aim_tlvlist_t *data; - struct aim_ssi_item *item; - - while (aim_bstream_empty(bs)) { - if ((len = aimbs_get16(bs))) - name = aimbs_getstr(bs, len); - else - name = NULL; - gid = aimbs_get16(bs); - bid = aimbs_get16(bs); - type = aimbs_get16(bs); - if ((len = aimbs_get16(bs))) - data = aim_tlvlist_readlen(bs, len); - else - data = NULL; - - /* Replace the 2 local items with the given one */ - if ((item = aim_ssi_itemlist_find(sess->ssi.local, gid, bid))) { - item->type = type; - free(item->name); - if (name) { - item->name = (char *)malloc((strlen(name)+1)*sizeof(char)); - strcpy(item->name, name); - } else - item->name = NULL; - aim_tlvlist_free(&item->data); - item->data = aim_tlvlist_copy(data); - } - - if ((item = aim_ssi_itemlist_find(sess->ssi.official, gid, bid))) { - item->type = type; - free(item->name); - if (name) { - item->name = (char *)malloc((strlen(name)+1)*sizeof(char)); - strcpy(item->name, name); - } else - item->name = NULL; - aim_tlvlist_free(&item->data); - item->data = aim_tlvlist_copy(data); - } - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx); - - free(name); - aim_tlvlist_free(&data); - } - - return ret; -} - -/* - * Subtype 0x000a - Incoming SSI del. - * - * XXX - It would probably be good for the client to actually do something when it gets this. - */ -static int parsedel(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; - fu16_t gid, bid; - struct aim_ssi_item *del; - - while (aim_bstream_empty(bs)) { - aim_bstream_advance(bs, aimbs_get16(bs)); - gid = aimbs_get16(bs); - bid = aimbs_get16(bs); - aimbs_get16(bs); - aim_bstream_advance(bs, aimbs_get16(bs)); - - if ((del = aim_ssi_itemlist_find(sess->ssi.local, gid, bid))) - aim_ssi_itemlist_del(&sess->ssi.local, del); - if ((del = aim_ssi_itemlist_find(sess->ssi.official, gid, bid))) - aim_ssi_itemlist_del(&sess->ssi.official, del); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx); - } - - return ret; -} - -/* - * Subtype 0x000e - SSI Add/Mod/Del Ack. - * - * Response to add, modify, or delete SNAC (sent with aim_ssi_addmoddel). - * - */ -static int parseack(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; - struct aim_ssi_tmp *cur, *del; - - /* Read in the success/failure flags from the ack SNAC */ - cur = sess->ssi.pending; - while (cur && (aim_bstream_empty(bs)>0)) { - cur->ack = aimbs_get16(bs); - cur = cur->next; - } - - /* - * If outcome is 0, then add the item to the item list, or replace the other item, - * or remove the old item. If outcome is non-zero, then remove the item from the - * local list, or unmodify it, or add it. - */ - for (cur=sess->ssi.pending; (cur && (cur->ack != 0xffff)); cur=cur->next) { - if (cur->item) { - if (cur->ack) { - /* Our action was unsuccessful, so change the local list back to how it was */ - if (cur->action == AIM_CB_SSI_ADD) { - /* Remove the item from the local list */ - /* Make sure cur->item is still valid memory */ - if (aim_ssi_itemlist_valid(sess->ssi.local, cur->item)) { - if (cur->item->name) { - cur->name = (char *)malloc((strlen(cur->item->name)+1)*sizeof(char)); - strcpy(cur->name, cur->item->name); - } - aim_ssi_itemlist_del(&sess->ssi.local, cur->item); - } - cur->item = NULL; - - } else if (cur->action == AIM_CB_SSI_MOD) { - /* Replace the local item with the item from the official list */ - if (aim_ssi_itemlist_valid(sess->ssi.local, cur->item)) { - struct aim_ssi_item *cur1; - if ((cur1 = aim_ssi_itemlist_find(sess->ssi.official, cur->item->gid, cur->item->bid))) { - free(cur->item->name); - if (cur1->name) { - cur->item->name = (char *)malloc((strlen(cur1->name)+1)*sizeof(char)); - strcpy(cur->item->name, cur1->name); - } else - cur->item->name = NULL; - aim_tlvlist_free(&cur->item->data); - cur->item->data = aim_tlvlist_copy(cur1->data); - } - } else - cur->item = NULL; - - } else if (cur->action == AIM_CB_SSI_DEL) { - /* Add the item back into the local list */ - if (aim_ssi_itemlist_valid(sess->ssi.official, cur->item)) { - aim_ssi_itemlist_add(&sess->ssi.local, cur->item->name, cur->item->gid, cur->item->bid, cur->item->type, cur->item->data); - } else - cur->item = NULL; - } - - } else { - /* Do the exact opposite */ - if (cur->action == AIM_CB_SSI_ADD) { - /* Add the local item to the official list */ - if (aim_ssi_itemlist_valid(sess->ssi.local, cur->item)) { - aim_ssi_itemlist_add(&sess->ssi.official, cur->item->name, cur->item->gid, cur->item->bid, cur->item->type, cur->item->data); - } else - cur->item = NULL; - - } else if (cur->action == AIM_CB_SSI_MOD) { - /* Replace the official item with the item from the local list */ - if (aim_ssi_itemlist_valid(sess->ssi.local, cur->item)) { - struct aim_ssi_item *cur1; - if ((cur1 = aim_ssi_itemlist_find(sess->ssi.official, cur->item->gid, cur->item->bid))) { - free(cur1->name); - if (cur->item->name) { - cur1->name = (char *)malloc((strlen(cur->item->name)+1)*sizeof(char)); - strcpy(cur1->name, cur->item->name); - } else - cur1->name = NULL; - aim_tlvlist_free(&cur1->data); - cur1->data = aim_tlvlist_copy(cur->item->data); - } - } else - cur->item = NULL; - - } else if (cur->action == AIM_CB_SSI_DEL) { - /* Remove the item from the official list */ - if (aim_ssi_itemlist_valid(sess->ssi.official, cur->item)) - aim_ssi_itemlist_del(&sess->ssi.official, cur->item); - cur->item = NULL; - } - - } - } /* End if (cur->item) */ - } /* End for loop */ - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, sess->ssi.pending); - - /* Free all aim_ssi_tmp's with an outcome */ - cur = sess->ssi.pending; - while (cur && (cur->ack != 0xffff)) { - del = cur; - cur = cur->next; - free(del->name); - free(del); - } - sess->ssi.pending = cur; - - /* If we're not waiting for any more acks, then send more SNACs */ - if (!sess->ssi.pending) { - sess->ssi.pending = NULL; - sess->ssi.waiting_for_ack = 0; - aim_ssi_sync(sess); - } - - return ret; -} - -/* - * Subtype 0x000f - SSI Data Unchanged. - * - * Response to aim_ssi_reqifchanged() if the server-side data is not newer than - * posted local stamp/revision. - * - */ -static int parsedataunchanged(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; - - sess->ssi.received_data = 1; - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx); - - return ret; -} - -/* - * Subtype 0x0011 - SSI Begin Data Modification. - * - * Tells the server you're going to start modifying data. - * - */ -faim_export int aim_ssi_modbegin(aim_session_t *sess) -{ - aim_conn_t *conn; - - if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI))) - return -EINVAL; - - return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_EDITSTART); -} - -/* - * Subtype 0x0012 - SSI End Data Modification. - * - * Tells the server you're finished modifying data. - * - */ -faim_export int aim_ssi_modend(aim_session_t *sess) -{ - aim_conn_t *conn; - - if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI))) - return -EINVAL; - - return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_EDITSTOP); -} - -/* - * Subtype 0x0014 - Grant authorization - * - * Authorizes a contact so they can add you to their contact list. - * - */ -faim_export int aim_ssi_sendauth(aim_session_t *sess, char *sn, char *msg) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - - if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)) || !sn) - return -EINVAL; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)+2+(msg ? strlen(msg)+1 : 0)+2))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTH, 0x0000, NULL, 0); - aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTH, 0x0000, snacid); - - /* Screen name */ - aimbs_put8(&fr->data, strlen(sn)); - aimbs_putstr(&fr->data, sn); - - /* Message (null terminated) */ - aimbs_put16(&fr->data, msg ? strlen(msg) : 0); - if (msg) { - aimbs_putstr(&fr->data, msg); - aimbs_put8(&fr->data, 0x00); - } - - /* Unknown */ - aimbs_put16(&fr->data, 0x0000); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * Subtype 0x0015 - Receive an authorization grant - */ -static int receiveauthgrant(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; - fu16_t tmp; - char *sn, *msg; - - /* Read screen name */ - if ((tmp = aimbs_get8(bs))) - sn = aimbs_getstr(bs, tmp); - else - sn = NULL; - - /* Read message (null terminated) */ - if ((tmp = aimbs_get16(bs))) - msg = aimbs_getstr(bs, tmp); - else - msg = NULL; - - /* Unknown */ - tmp = aimbs_get16(bs); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, sn, msg); - - free(sn); - free(msg); - - return ret; -} - -/* - * Subtype 0x0018 - Send authorization request - * - * Sends a request for authorization to the given contact. The request will either be - * granted, denied, or dropped. - * - */ -faim_export int aim_ssi_sendauthrequest(aim_session_t *sess, char *sn, const char *msg) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - - if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)) || !sn) - return -EINVAL; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)+2+(msg ? strlen(msg)+1 : 0)+2))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREQ, 0x0000, NULL, 0); - aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREQ, 0x0000, snacid); - - /* Screen name */ - aimbs_put8(&fr->data, strlen(sn)); - aimbs_putstr(&fr->data, sn); - - /* Message (null terminated) */ - aimbs_put16(&fr->data, msg ? strlen(msg) : 0); - if (msg) { - aimbs_putstr(&fr->data, msg); - aimbs_put8(&fr->data, 0x00); - } - - /* Unknown */ - aimbs_put16(&fr->data, 0x0000); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * Subtype 0x0019 - Receive an authorization request - */ -static int receiveauthrequest(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; - fu16_t tmp; - char *sn, *msg; - - /* Read screen name */ - if ((tmp = aimbs_get8(bs))) - sn = aimbs_getstr(bs, tmp); - else - sn = NULL; - - /* Read message (null terminated) */ - if ((tmp = aimbs_get16(bs))) - msg = aimbs_getstr(bs, tmp); - else - msg = NULL; - - /* Unknown */ - tmp = aimbs_get16(bs); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, sn, msg); - - free(sn); - free(msg); - - return ret; -} - -/* - * Subtype 0x001a - Send authorization reply - * - * Sends a reply to a request for authorization. The reply can either - * grant authorization or deny authorization. - * - * if reply=0x00 then deny - * if reply=0x01 then grant - * - */ -faim_export int aim_ssi_sendauthreply(aim_session_t *sess, char *sn, fu8_t reply, const char *msg) -{ - aim_conn_t *conn; - aim_frame_t *fr; - aim_snacid_t snacid; - - if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)) || !sn) - return -EINVAL; - - if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 1+strlen(sn) + 1 + 2+(msg ? strlen(msg)+1 : 0) + 2))) - return -ENOMEM; - - snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREP, 0x0000, NULL, 0); - aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREP, 0x0000, snacid); - - /* Screen name */ - aimbs_put8(&fr->data, strlen(sn)); - aimbs_putstr(&fr->data, sn); - - /* Grant or deny */ - aimbs_put8(&fr->data, reply); - - /* Message (null terminated) */ - aimbs_put16(&fr->data, msg ? (strlen(msg)+1) : 0); - if (msg) { - aimbs_putstr(&fr->data, msg); - aimbs_put8(&fr->data, 0x00); - } - - /* Unknown */ - aimbs_put16(&fr->data, 0x0000); - - aim_tx_enqueue(sess, fr); - - return 0; -} - -/* - * Subtype 0x001b - Receive an authorization reply - * You get this bad boy when other people respond to the authorization - * request that you have previously sent them. - */ -static int receiveauthreply(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; - fu16_t tmp; - fu8_t reply; - char *sn, *msg; - - /* Read screen name */ - if ((tmp = aimbs_get8(bs))) - sn = aimbs_getstr(bs, tmp); - else - sn = NULL; - - /* Read reply */ - reply = aimbs_get8(bs); - - /* Read message (null terminated) */ - if ((tmp = aimbs_get16(bs))) - msg = aimbs_getstr(bs, tmp); - else - msg = NULL; - - /* Unknown */ - tmp = aimbs_get16(bs); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, sn, reply, msg); - - free(sn); - free(msg); - - return ret; -} - -/* - * Subtype 0x001c - Receive a message telling you someone added you to their list. - */ -static int receiveadded(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; - fu16_t tmp; - char *sn; - - /* Read screen name */ - if ((tmp = aimbs_get8(bs))) - sn = aimbs_getstr(bs, tmp); - else - sn = NULL; - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, sn); - - free(sn); - - 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 == AIM_CB_SSI_RIGHTSINFO) - return parserights(sess, mod, rx, snac, bs); - else if (snac->subtype == AIM_CB_SSI_LIST) - return parsedata(sess, mod, rx, snac, bs); - else if (snac->subtype == AIM_CB_SSI_ADD) - return parseadd(sess, mod, rx, snac, bs); - else if (snac->subtype == AIM_CB_SSI_MOD) - return parsemod(sess, mod, rx, snac, bs); - else if (snac->subtype == AIM_CB_SSI_DEL) - return parsedel(sess, mod, rx, snac, bs); - else if (snac->subtype == AIM_CB_SSI_SRVACK) - return parseack(sess, mod, rx, snac, bs); - else if (snac->subtype == AIM_CB_SSI_NOLIST) - return parsedataunchanged(sess, mod, rx, snac, bs); - else if (snac->subtype == AIM_CB_SSI_RECVAUTH) - return receiveauthgrant(sess, mod, rx, snac, bs); - else if (snac->subtype == AIM_CB_SSI_RECVAUTHREQ) - return receiveauthrequest(sess, mod, rx, snac, bs); - else if (snac->subtype == AIM_CB_SSI_RECVAUTHREP) - return receiveauthreply(sess, mod, rx, snac, bs); - else if (snac->subtype == AIM_CB_SSI_ADDED) - return receiveadded(sess, mod, rx, snac, bs); - - return 0; -} - -static void ssi_shutdown(aim_session_t *sess, aim_module_t *mod) -{ - aim_ssi_freelist(sess); -} - -faim_internal int ssi_modfirst(aim_session_t *sess, aim_module_t *mod) -{ - - mod->family = AIM_CB_FAM_SSI; - mod->version = 0x0004; - mod->toolid = 0x0110; - mod->toolversion = 0x0629; - mod->flags = 0; - strncpy(mod->name, "ssi", sizeof(mod->name)); - mod->snachandler = snachandler; - mod->shutdown = ssi_shutdown; - - return 0; -} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/stats.c --- a/src/protocols/oscar/stats.c Sat Feb 11 19:16:38 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,44 +0,0 @@ -/* - * Family 0x000b - Statistics. - * - */ - -#define FAIM_INTERNAL -#include - -static int reportinterval(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; - fu16_t interval; - - interval = aimbs_get16(bs); - - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, interval); - - 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 == 0x0002) - return reportinterval(sess, mod, rx, snac, bs); - - return 0; -} - -faim_internal int stats_modfirst(aim_session_t *sess, aim_module_t *mod) -{ - - mod->family = 0x000b; - mod->version = 0x0001; - mod->toolid = 0x0104; - mod->toolversion = 0x0001; - mod->flags = 0; - strncpy(mod->name, "stats", sizeof(mod->name)); - mod->snachandler = snachandler; - - return 0; -} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/tlv.c --- a/src/protocols/oscar/tlv.c Sat Feb 11 19:16:38 2006 +0000 +++ b/src/protocols/oscar/tlv.c Sat Feb 11 21:45:18 2006 +0000 @@ -1,8 +1,27 @@ +/* + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ -#define FAIM_INTERNAL -#include -static aim_tlv_t *createtlv(fu16_t type, fu16_t length, fu8_t *value) +#include "oscar.h" + +static aim_tlv_t *createtlv(guint16 type, guint16 length, guint8 *value) { aim_tlv_t *ret; @@ -50,7 +69,7 @@ aim_tlvlist_t *list = NULL, *cur; while (aim_bstream_empty(bs) > 0) { - fu16_t type, length; + guint16 type, length; type = aimbs_get16(bs); length = aimbs_get16(bs); @@ -130,12 +149,12 @@ * preceded by the number of TLVs. So you can limit that with this. * @return Return the TLV chain read */ -faim_internal aim_tlvlist_t *aim_tlvlist_readnum(aim_bstream_t *bs, fu16_t num) +faim_internal aim_tlvlist_t *aim_tlvlist_readnum(aim_bstream_t *bs, guint16 num) { aim_tlvlist_t *list = NULL, *cur; while ((aim_bstream_empty(bs) > 0) && (num != 0)) { - fu16_t type, length; + guint16 type, length; type = aimbs_get16(bs); length = aimbs_get16(bs); @@ -199,12 +218,12 @@ * preceded by the length of the TLVs. So you can limit that with this. * @return Return the TLV chain read */ -faim_internal aim_tlvlist_t *aim_tlvlist_readlen(aim_bstream_t *bs, fu16_t len) +faim_internal aim_tlvlist_t *aim_tlvlist_readlen(aim_bstream_t *bs, guint16 len) { aim_tlvlist_t *list = NULL, *cur; while ((aim_bstream_empty(bs) > 0) && (len > 0)) { - fu16_t type, length; + guint16 type, length; type = aimbs_get16(bs); length = aimbs_get16(bs); @@ -280,8 +299,8 @@ if (aim_tlvlist_size(&one) != aim_tlvlist_size(&two)) return 1; - aim_bstream_init(&bs1, ((fu8_t *)malloc(aim_tlvlist_size(&one)*sizeof(fu8_t))), aim_tlvlist_size(&one)); - aim_bstream_init(&bs2, ((fu8_t *)malloc(aim_tlvlist_size(&two)*sizeof(fu8_t))), aim_tlvlist_size(&two)); + aim_bstream_init(&bs1, ((guint8 *)malloc(aim_tlvlist_size(&one)*sizeof(guint8))), aim_tlvlist_size(&one)); + aim_bstream_init(&bs2, ((guint8 *)malloc(aim_tlvlist_size(&two)*sizeof(guint8))), aim_tlvlist_size(&two)); aim_tlvlist_write(&bs1, &one); aim_tlvlist_write(&bs2, &two); @@ -380,7 +399,7 @@ * @param value String to add. * @return The size of the value added. */ -faim_internal int aim_tlvlist_add_raw(aim_tlvlist_t **list, const fu16_t type, const fu16_t length, const fu8_t *value) +faim_internal int aim_tlvlist_add_raw(aim_tlvlist_t **list, const guint16 type, const guint16 length, const guint8 *value) { aim_tlvlist_t *newtlv, *cur; @@ -396,7 +415,7 @@ return 0; } if (newtlv->tlv->length > 0) { - newtlv->tlv->value = (fu8_t *)malloc(newtlv->tlv->length); + newtlv->tlv->value = (guint8 *)malloc(newtlv->tlv->length); memcpy(newtlv->tlv->value, value, newtlv->tlv->length); } @@ -419,9 +438,9 @@ * @param value Value to add. * @return The size of the value added. */ -faim_internal int aim_tlvlist_add_8(aim_tlvlist_t **list, const fu16_t type, const fu8_t value) +faim_internal int aim_tlvlist_add_8(aim_tlvlist_t **list, const guint16 type, const guint8 value) { - fu8_t v8[1]; + guint8 v8[1]; aimutil_put8(v8, value); @@ -436,9 +455,9 @@ * @param value Value to add. * @return The size of the value added. */ -faim_internal int aim_tlvlist_add_16(aim_tlvlist_t **list, const fu16_t type, const fu16_t value) +faim_internal int aim_tlvlist_add_16(aim_tlvlist_t **list, const guint16 type, const guint16 value) { - fu8_t v16[2]; + guint8 v16[2]; aimutil_put16(v16, value); @@ -453,9 +472,9 @@ * @param value Value to add. * @return The size of the value added. */ -faim_internal int aim_tlvlist_add_32(aim_tlvlist_t **list, const fu16_t type, const fu32_t value) +faim_internal int aim_tlvlist_add_32(aim_tlvlist_t **list, const guint16 type, const guint32 value) { - fu8_t v32[4]; + guint8 v32[4]; aimutil_put32(v32, value); @@ -470,9 +489,9 @@ * @param value Value to add. * @return The size of the value added. */ -faim_internal int aim_tlvlist_add_str(aim_tlvlist_t **list, const fu16_t type, const char *value) +faim_internal int aim_tlvlist_add_str(aim_tlvlist_t **list, const guint16 type, const char *value) { - return aim_tlvlist_add_raw(list, type, strlen(value), (fu8_t *)value); + return aim_tlvlist_add_raw(list, type, strlen(value), (guint8 *)value); } /** @@ -491,9 +510,9 @@ * @param caps Bitfield of capability flags to send * @return The size of the value added. */ -faim_internal int aim_tlvlist_add_caps(aim_tlvlist_t **list, const fu16_t type, const fu32_t caps) +faim_internal int aim_tlvlist_add_caps(aim_tlvlist_t **list, const guint16 type, const guint32 caps) { - fu8_t buf[16*16]; /* XXX icky fixed length buffer */ + guint8 buf[16*16]; /* XXX icky fixed length buffer */ aim_bstream_t bs; if (!caps) @@ -513,9 +532,9 @@ * @param type TLV type to add. * @return The size of the value added. */ -faim_internal int aim_tlvlist_add_userinfo(aim_tlvlist_t **list, fu16_t type, aim_userinfo_t *userinfo) +faim_internal int aim_tlvlist_add_userinfo(aim_tlvlist_t **list, guint16 type, aim_userinfo_t *userinfo) { - fu8_t buf[1024]; /* bleh */ + guint8 buf[1024]; /* bleh */ aim_bstream_t bs; aim_bstream_init(&bs, buf, sizeof(buf)); @@ -534,9 +553,9 @@ * @param instance The instance. * @return The size of the value added. */ -faim_internal int aim_tlvlist_add_chatroom(aim_tlvlist_t **list, fu16_t type, fu16_t exchange, const char *roomname, fu16_t instance) +faim_internal int aim_tlvlist_add_chatroom(aim_tlvlist_t **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance) { - fu8_t *buf; + guint8 *buf; int len; aim_bstream_t bs; @@ -566,7 +585,7 @@ * @param type TLV type to add. * @return The size of the value added. */ -faim_internal int aim_tlvlist_add_noval(aim_tlvlist_t **list, const fu16_t type) +faim_internal int aim_tlvlist_add_noval(aim_tlvlist_t **list, const guint16 type) { return aim_tlvlist_add_raw(list, type, 0, NULL); } @@ -587,9 +606,9 @@ * 0 is returned if there was an error or if the destination * TLV chain has length 0. */ -faim_internal int aim_tlvlist_add_frozentlvlist(aim_tlvlist_t **list, fu16_t type, aim_tlvlist_t **tl) +faim_internal int aim_tlvlist_add_frozentlvlist(aim_tlvlist_t **list, guint16 type, aim_tlvlist_t **tl) { - fu8_t *buf; + guint8 *buf; int buflen; aim_bstream_t bs; @@ -623,7 +642,7 @@ * @param value String to add. * @return The length of the TLV. */ -faim_internal int aim_tlvlist_replace_raw(aim_tlvlist_t **list, const fu16_t type, const fu16_t length, const fu8_t *value) +faim_internal int aim_tlvlist_replace_raw(aim_tlvlist_t **list, const guint16 type, const guint16 length, const guint8 *value) { aim_tlvlist_t *cur; @@ -637,7 +656,7 @@ free(cur->tlv->value); cur->tlv->length = length; if (cur->tlv->length > 0) { - cur->tlv->value = (fu8_t *)malloc(cur->tlv->length); + cur->tlv->value = (guint8 *)malloc(cur->tlv->length); memcpy(cur->tlv->value, value, cur->tlv->length); } else cur->tlv->value = NULL; @@ -655,7 +674,7 @@ * @param str String to add. * @return The length of the TLV. */ -faim_internal int aim_tlvlist_replace_str(aim_tlvlist_t **list, const fu16_t type, const char *str) +faim_internal int aim_tlvlist_replace_str(aim_tlvlist_t **list, const guint16 type, const char *str) { return aim_tlvlist_replace_raw(list, type, strlen(str), (const guchar *)str); } @@ -669,7 +688,7 @@ * @param type TLV type. * @return The length of the TLV. */ -faim_internal int aim_tlvlist_replace_noval(aim_tlvlist_t **list, const fu16_t type) +faim_internal int aim_tlvlist_replace_noval(aim_tlvlist_t **list, const guint16 type) { return aim_tlvlist_replace_raw(list, type, 0, NULL); } @@ -684,9 +703,9 @@ * @param value 8 bit value to add. * @return The length of the TLV. */ -faim_internal int aim_tlvlist_replace_8(aim_tlvlist_t **list, const fu16_t type, const fu8_t value) +faim_internal int aim_tlvlist_replace_8(aim_tlvlist_t **list, const guint16 type, const guint8 value) { - fu8_t v8[1]; + guint8 v8[1]; aimutil_put8(v8, value); @@ -703,9 +722,9 @@ * @param value 32 bit value to add. * @return The length of the TLV. */ -faim_internal int aim_tlvlist_replace_32(aim_tlvlist_t **list, const fu16_t type, const fu32_t value) +faim_internal int aim_tlvlist_replace_32(aim_tlvlist_t **list, const guint16 type, const guint32 value) { - fu8_t v32[4]; + guint8 v32[4]; aimutil_put32(v32, value); @@ -719,7 +738,7 @@ * @param list Desination chain (%NULL pointer if empty). * @param type TLV type. */ -faim_internal void aim_tlvlist_remove(aim_tlvlist_t **list, const fu16_t type) +faim_internal void aim_tlvlist_remove(aim_tlvlist_t **list, const guint16 type) { aim_tlvlist_t *del; @@ -795,7 +814,7 @@ * @param nth Index of TLV of type to get. * @return The TLV you were looking for, or NULL if one could not be found. */ -faim_internal aim_tlv_t *aim_tlv_gettlv(aim_tlvlist_t *list, const fu16_t type, const int nth) +faim_internal aim_tlv_t *aim_tlv_gettlv(aim_tlvlist_t *list, const guint16 type, const int nth) { aim_tlvlist_t *cur; int i; @@ -821,7 +840,7 @@ * @return The length of the data in this TLV, or -1 if the TLV could not be * found. Unless -1 is returned, this value will be 2 bytes. */ -faim_internal int aim_tlv_getlength(aim_tlvlist_t *list, const fu16_t type, const int nth) +faim_internal int aim_tlv_getlength(aim_tlvlist_t *list, const guint16 type, const int nth) { aim_tlvlist_t *cur; int i; @@ -848,7 +867,7 @@ * not be found. This is a dynamic buffer and must be freed by the * caller. */ -faim_internal char *aim_tlv_getstr(aim_tlvlist_t *list, const fu16_t type, const int nth) +faim_internal char *aim_tlv_getstr(aim_tlvlist_t *list, const guint16 type, const int nth) { aim_tlv_t *tlv; char *newstr; @@ -873,7 +892,7 @@ * @return The value the TLV you were looking for, or 0 if one could * not be found. */ -faim_internal fu8_t aim_tlv_get8(aim_tlvlist_t *list, const fu16_t type, const int nth) +faim_internal guint8 aim_tlv_get8(aim_tlvlist_t *list, const guint16 type, const int nth) { aim_tlv_t *tlv; @@ -892,7 +911,7 @@ * @return The value the TLV you were looking for, or 0 if one could * not be found. */ -faim_internal fu16_t aim_tlv_get16(aim_tlvlist_t *list, const fu16_t type, const int nth) +faim_internal guint16 aim_tlv_get16(aim_tlvlist_t *list, const guint16 type, const int nth) { aim_tlv_t *tlv; @@ -911,7 +930,7 @@ * @return The value the TLV you were looking for, or 0 if one could * not be found. */ -faim_internal fu32_t aim_tlv_get32(aim_tlvlist_t *list, const fu16_t type, const int nth) +faim_internal guint32 aim_tlv_get32(aim_tlvlist_t *list, const guint16 type, const int nth) { aim_tlv_t *tlv; diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/translate.c --- a/src/protocols/oscar/translate.c Sat Feb 11 19:16:38 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,27 +0,0 @@ -/* - * Family 0x000c - Translation. - * - * I have no idea why this group was issued. I have never seen anything - * that uses it. From what I remember, the last time I tried to poke at - * the server with this group, it whined about not supporting it. - * - * But we advertise it anyway, because its fun. - * - */ - -#define FAIM_INTERNAL -#include - -faim_internal int translate_modfirst(aim_session_t *sess, aim_module_t *mod) -{ - - mod->family = 0x000c; - mod->version = 0x0001; - mod->toolid = 0x0104; - mod->toolversion = 0x0001; - mod->flags = 0; - strncpy(mod->name, "translate", sizeof(mod->name)); - mod->snachandler = NULL; - - return 0; -} diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/txqueue.c --- a/src/protocols/oscar/txqueue.c Sat Feb 11 19:16:38 2006 +0000 +++ b/src/protocols/oscar/txqueue.c Sat Feb 11 21:45:18 2006 +0000 @@ -1,3 +1,23 @@ +/* + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + /* * txqueue.c * @@ -5,8 +25,8 @@ * */ -#define FAIM_INTERNAL -#include +#include "oscar.h" +#include "peer.h" #ifndef _WIN32 #include @@ -26,7 +46,7 @@ * chan = channel for FLAP, hdrtype for OFT * */ -faim_internal aim_frame_t *aim_tx_new(aim_session_t *sess, aim_conn_t *conn, fu8_t framing, fu16_t chan, int datalen) +faim_internal aim_frame_t *aim_tx_new(aim_session_t *sess, aim_conn_t *conn, guint8 framing, guint16 chan, int datalen) { aim_frame_t *fr; @@ -61,7 +81,7 @@ gaim_debug_misc("oscar", "tx_new: unknown framing\n"); if (datalen > 0) { - fu8_t *data; + guint8 *data; if (!(data = (unsigned char *)malloc(datalen))) { aim_frame_destroy(fr); @@ -262,7 +282,7 @@ static int sendframe_flap(aim_session_t *sess, aim_frame_t *fr) { aim_bstream_t bs; - fu8_t *bs_raw; + guint8 *bs_raw; int payloadlen, err = 0, bslen; payloadlen = aim_bstream_curpos(&fr->data); @@ -298,7 +318,7 @@ static int sendframe_rendezvous(aim_session_t *sess, aim_frame_t *fr) { aim_bstream_t bs; - fu8_t *bs_raw; + guint8 *bs_raw; int payloadlen, err = 0, bslen; payloadlen = aim_bstream_curpos(&fr->data); diff -r f09c6e8df82c -r f2431a7e33aa src/protocols/oscar/util.c --- a/src/protocols/oscar/util.c Sat Feb 11 19:16:38 2006 +0000 +++ b/src/protocols/oscar/util.c Sat Feb 11 21:45:18 2006 +0000 @@ -1,3 +1,23 @@ +/* + * Gaim's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + /* * A little bit of this * A little bit of that @@ -5,8 +25,7 @@ * Now we're up to bat */ -#define FAIM_INTERNAL -#include +#include "oscar.h" #include #ifdef _WIN32 @@ -109,9 +128,9 @@ * Calculate the checksum of a given icon. * */ -faim_export fu16_t aimutil_iconsum(const fu8_t *buf, int buflen) +faim_export guint16 aimutil_iconsum(const guint8 *buf, int buflen) { - fu32_t sum; + guint32 sum; int i; for (i=0, sum=0; i+1