Mercurial > pidgin
changeset 1535:1e2cc8c8bf3c
[gaim-migrate @ 1545]
libfaim updates.
committer: Tailor Script <tailor@pidgin.im>
author | Eric Warmenhoven <eric@warmenhoven.org> |
---|---|
date | Mon, 05 Mar 2001 03:59:32 +0000 |
parents | d98b92e3d9ff |
children | 3a9f11c9278b |
files | ChangeLog TODO libfaim/CHANGES libfaim/Makefile.am libfaim/adverts.c libfaim/aim.h libfaim/aim_ads.c libfaim/aim_auth.c libfaim/aim_buddylist.c libfaim/aim_cbtypes.h libfaim/aim_chat.c libfaim/aim_chatnav.c libfaim/aim_conn.c libfaim/aim_ft.c libfaim/aim_im.c libfaim/aim_info.c libfaim/aim_login.c libfaim/aim_logoff.c libfaim/aim_meta.c libfaim/aim_misc.c libfaim/aim_msgcookie.c libfaim/aim_rxhandlers.c libfaim/aim_rxqueue.c libfaim/aim_search.c libfaim/aim_snac.c libfaim/aim_tlv.c libfaim/aim_txqueue.c libfaim/aim_util.c libfaim/auth.c libfaim/buddylist.c libfaim/chat.c libfaim/chatnav.c libfaim/conn.c libfaim/faim/aim.h libfaim/faim/aim_cbtypes.h libfaim/faim/faimconfig.h libfaim/faimconfig.h libfaim/ft.c libfaim/im.c libfaim/info.c libfaim/libfaim_config.h libfaim/login.c libfaim/meta.c libfaim/misc.c libfaim/msgcookie.c libfaim/rxhandlers.c libfaim/rxqueue.c libfaim/search.c libfaim/snac.c libfaim/tlv.c libfaim/txqueue.c libfaim/util.c src/dialogs.c src/gaim.h src/oscar.c |
diffstat | 55 files changed, 12271 insertions(+), 11410 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog Sun Mar 04 22:37:18 2001 +0000 +++ b/ChangeLog Mon Mar 05 03:59:32 2001 +0000 @@ -5,6 +5,7 @@ * Napster plugin is included again (Thanks for pointing out that it was missing, (Yan V. Bulgak) + * Oscar can: search by email, request confirmation, change password version 0.11.0-pre5 (02/26/2001): * Minor GUI changes
--- a/TODO Sun Mar 04 22:37:18 2001 +0000 +++ b/TODO Mon Mar 05 03:59:32 2001 +0000 @@ -15,12 +15,6 @@ Sounds on buddy pounce Implement system logging - I have an updated copy of libfaim. It adds a few new features - (change password, confirm, req. email, change email, - search by email). Unfortunately it breaks direct im. - Once direct im is fixed (which should also cause get - file to work) I'll add all these things to gaim. - ICQ through Oscar plugin (requires hacking libfaim :-/) Syd is cool and gave all of these ideas:
--- a/libfaim/CHANGES Sun Mar 04 22:37:18 2001 +0000 +++ b/libfaim/CHANGES Mon Mar 05 03:59:32 2001 +0000 @@ -1,6 +1,61 @@ No release numbers ------------------ + - Mon Mar 5 01:19:48 UTC 2001 + - Fix typo in last CHANGES entry. + - Add fix from Eric to keep efence from complaining about malloc(0) + - Add .cvsignore files (also from Eric) + - Throw in josh's latest OFT/ODC kludges + - Completly remove all calls to printf and (unbounded) sprintf + - Add aim_setdebuggingcb() + - Remove preprocessor-based debug semantics (faimconfig.h) + - faimdprintf() now calls a user-specified callback to do printing + - Rearrange aim_tx_new() parameters to be more sensical, add sess + - Do some sanity checking in aim_tx_new() + - Add #defines to keep printf from compiling inside libfaim + - Who knows what else. (in total, this patch is nearly 5k lines) + + - Fri Mar 2 02:17:22 UTC 2001 + - Add aim_fingerprintclient(). (AB will trust this, but you shouldn't.) + - Add some EXTRA_DIST to Makefile.am + + - Mon Feb 26 01:46:34 UTC 2001 + - Fix swapped bytes in aim_caps[]. Thanks to + Bruce Miller <bmiller@ics.uci.edu> for pointing this out. + + - Sun Feb 11 01:07:36 UTC 2001 + - Make secondary connections to the authorizer work again + - Make aim_auth_changepasswd() work again. + - Add aim_auth_setversions(), aim_auth_getinfo(), aim_auth_setemail(), + aim_auth_reqconfirm() + - Add middle handlers for responses to all of above. + - Add examples of above to faimtest (start with 'reqauth') + + - Fri Feb 9 22:31:22 UTC 2001 + - Add aim_tx_setenqueue(). + + - Thu Feb 8 20:12:39 UTC 2001 + - Let TLV functions out of aim.h if FAIM_NEED_TLV is defined. + + - Thu Feb 8 02:31:25 UTC 2001 + - Remove aim_purgecookies() -- never used anyway + - Clean up a few bits of aim_msgcookie.c + - Remove AIM_CONN_MAX constant (hasn't been relevent in ages) + - Change commentary on synchro in aim.h + - Add NOPLOCKS and make them default + - Make faim/aim.h more of a mess; try to keep interfaces hiddenish. + - Add #define FAIM_INTERNAL to top of every file + - Remove aim_countconn (no one uses it anyway) + - Add aim_cloneconn() (cringe.) + - Throw in josh's getfile patch (and then munge it enough to break it) + - Add getopt() to faimtest. + - Remove aim_buildcode.h/mkbuildinfo.sh crap. + - Clean up aim_cleansnacs() + - There may have been other stuff too. As always, the diff is the + most accurate changelog there is. + - *** FT is currently more broken than it was before. Also, this patch + introduces even more memory leaks. You've been warned. + - Sun Dec 17 07:19:04 UTC 2000 - Update the capability block list
--- a/libfaim/Makefile.am Sun Mar 04 22:37:18 2001 +0000 +++ b/libfaim/Makefile.am Mon Mar 05 03:59:32 2001 +0000 @@ -1,30 +1,29 @@ noinst_LIBRARIES = libfaim.a -EXTRA_DIST = faim/aim.h faim/aim_cbtypes.h faim/faimconfig.h md5.h README \ +EXTRA_DIST = aim.h aim_cbtypes.h faimconfig.h md5.h README \ README.gaim CHANGES CHANGES.gaim COPYING BUGS AUTHORS -libfaim_a_SOURCES = aim_ads.c \ - aim_auth.c \ - aim_buddylist.c \ - aim_chat.c \ - aim_chatnav.c \ - aim_conn.c \ - aim_ft.c \ - aim_im.c \ - aim_info.c \ - aim_login.c \ - aim_logoff.c \ - aim_meta.c \ - aim_misc.c \ - aim_msgcookie.c \ - aim_rxhandlers.c \ - aim_rxqueue.c \ - aim_search.c \ - aim_snac.c \ - aim_tlv.c \ - aim_txqueue.c \ - aim_util.c \ - md5.c +libfaim_a_SOURCES = adverts.c \ + auth.c \ + buddylist.c \ + chat.c \ + chatnav.c \ + conn.c \ + ft.c \ + im.c \ + info.c \ + login.c \ + md5.c \ + meta.c \ + misc.c \ + msgcookie.c \ + rxhandlers.c \ + rxqueue.c \ + search.c \ + snac.c \ + tlv.c \ + txqueue.c \ + util.c CFLAGS += -DAIM_BUILDDATE=\"`date +%Y%m%d`\" -DAIM_BUILDTIME=\"`date +%H%M%S`\"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfaim/adverts.c Mon Mar 05 03:59:32 2001 +0000 @@ -0,0 +1,43 @@ +/* + * + * + */ + +#define FAIM_INTERNAL +#include <aim.h> + +faim_export unsigned long aim_ads_clientready(struct aim_session_t *sess, + struct aim_conn_t *conn) +{ + struct command_tx_struct *newpacket; + int i; + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 0x1a))) + return -1; + + newpacket->lock = 1; + + i = aim_putsnac(newpacket->data, 0x0001, 0x0002, 0x0000, sess->snac_nextid); + + i+= aimutil_put16(newpacket->data+i, 0x0001); + i+= aimutil_put16(newpacket->data+i, 0x0002); + + i+= aimutil_put16(newpacket->data+i, 0x0001); + i+= aimutil_put16(newpacket->data+i, 0x0013); + + i+= aimutil_put16(newpacket->data+i, 0x0005); + i+= aimutil_put16(newpacket->data+i, 0x0001); + i+= aimutil_put16(newpacket->data+i, 0x0001); + i+= aimutil_put16(newpacket->data+i, 0x0001); + + newpacket->lock = 0; + aim_tx_enqueue(sess, newpacket); + + return (sess->snac_nextid++); +} + +faim_export unsigned long aim_ads_requestads(struct aim_session_t *sess, + struct aim_conn_t *conn) +{ + return aim_genericreq_n(sess, conn, 0x0005, 0x0002); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfaim/aim.h Mon Mar 05 03:59:32 2001 +0000 @@ -0,0 +1,918 @@ +/* + * 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 Meyer + * + */ + +#ifndef __AIM_H__ +#define __AIM_H__ + +#define FAIM_VERSION_MAJOR 0 +#define FAIM_VERSION_MINOR 99 +#define FAIM_VERSION_MINORMINOR 1 + +#include <faimconfig.h> +#include <aim_cbtypes.h> + +#if !defined(FAIM_USEPTHREADS) && !defined(FAIM_USEFAKELOCKS) && !defined(FAIM_USENOPLOCKS) +#error pthreads, fakelocks, or noplocks are currently required. +#endif + +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <sys/types.h> +#include <stdlib.h> +#include <stdarg.h> +#include <errno.h> +#include <time.h> + +#ifdef _WIN32 +#include <windows.h> +#include <io.h> +#else +#include <sys/time.h> +#include <unistd.h> +#include <netinet/in.h> +#include <sys/socket.h> +#endif + +#ifdef FAIM_USEPTHREADS +#include <pthread.h> +#define faim_mutex_t pthread_mutex_t +#define faim_mutex_init(x) pthread_mutex_init(x, NULL) +#define faim_mutex_lock(x) pthread_mutex_lock(x) +#define faim_mutex_unlock(x) pthread_mutex_unlock(x) +#define faim_mutex_destroy(x) pthread_mutex_destroy(x) +#elif defined(FAIM_USEFAKELOCKS) +/* + * For platforms without pthreads, we also assume + * we're not linking against a threaded app. Which + * means we don't have to do real locking. The + * macros below do nothing really. They're a joke. + * But they get it to compile. + */ +#define faim_mutex_t char +#define faim_mutex_init(x) *x = 0 +#define faim_mutex_lock(x) while(*x != 0) {/* spin */}; *x = 1; +#define faim_mutex_unlock(x) while(*x != 0) {/* spin spin spin */}; *x = 0; +#define faim_mutex_destroy(x) while(*x != 0) {/* spiiiinnn */}; *x = 0; +#elif defined(FAIM_USENOPLOCKS) +#define faim_mutex_t char +#define faim_mutex_init(x) +#define faim_mutex_lock(x) +#define faim_mutex_unlock(x) +#define faim_mutex_destroy(x) +#endif + +/* Portability stuff (DMP) */ + +#ifdef _WIN32 +#define sleep Sleep +#define socklen_t int /* this must be a POSIXy thing */ +#define snprintf _snprintf /* I'm not sure whats wrong with Microsoft here */ +#define close(x) closesocket(x) /* no comment */ +#endif + +#if defined(mach) && defined(__APPLE__) +#define gethostbyname(x) gethostbyname2(x, AF_INET) +#endif + +#if defined(_WIN32) || defined(STRICT_ANSI) +#define faim_shortfunc +#else +#define faim_shortfunc inline +#endif + +#if defined(_WIN32) && !defined(WIN32_STATIC) +/* + * For a win32 DLL, we define WIN32_INDLL if this file + * is included while compiling the DLL. If its not + * defined (its 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 + +/* + * Current Maximum Length for Screen Names (not including NULL) + * + * Currently only names up to 16 characters can be registered + * however it is aparently legal for them to be larger. + */ +#define MAXSNLEN 32 + +/* + * 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 + +/* + * 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 + +/* + * Standard size of an AIM authorization cookie + */ +#define AIM_COOKIELEN 0x100 + +#define AIM_MD5_STRING "AOL Instant Messenger (SM)" + +/* + * Client info. Filled in by the client and passed + * in to aim_login(). The information ends up + * getting passed to OSCAR through the initial + * login command. + * + * XXX: Should this be per-session? -mid + * + */ +struct client_info_s { + char clientstring[100]; /* arbitrary size */ + int major; + int minor; + int build; + char country[3]; + char lang[3]; + int major2; + int minor2; + long unknown; +}; + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +/* + * These could be arbitrary, but its easier to use the actual AIM values + */ +#define AIM_CONN_TYPE_AUTH 0x0007 +#define AIM_CONN_TYPE_ADS 0x0005 +#define AIM_CONN_TYPE_BOS 0x0002 +#define AIM_CONN_TYPE_CHAT 0x000e +#define AIM_CONN_TYPE_CHATNAV 0x000d + +/* they start getting arbitrary in rendezvous stuff =) */ +#define AIM_CONN_TYPE_RENDEZVOUS 0x0101 /* these do not speak OSCAR! */ +#define AIM_CONN_TYPE_RENDEZVOUS_OUT 0x0102 /* socket waiting for accept() */ + +/* + * 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_OSCAR 0x0000 +#define AIM_FRAMETYPE_OFT 0x0001 + +struct aim_conn_t { + int fd; + unsigned short type; + unsigned short subtype; + int seqnum; + int status; + void *priv; /* misc data the client may want to store */ + time_t lastactivity; /* time of last transmit */ + int forcedlatency; + struct aim_rxcblist_t *handlerlist; + faim_mutex_t active; /* lock around read/writes */ + faim_mutex_t seqnum_lock; /* lock around ->seqnum changes */ + struct aim_conn_t *next; +}; + +/* struct for incoming commands */ +struct command_rx_struct { + unsigned char hdrtype; /* defines which piece of the union to use */ + union { + struct { + char type; + unsigned short seqnum; + } oscar; + struct { + unsigned short type; + unsigned char magic[4]; /* ODC2 OFT2 */ + unsigned short hdr2len; + unsigned char *hdr2; /* rest of bloated header */ + } oft; + } hdr; + unsigned short commandlen; /* total payload length */ + unsigned char *data; /* packet data (from 7 byte on) */ + unsigned char lock; /* 0 = open, !0 = locked */ + unsigned char handled; /* 0 = new, !0 = been handled */ + unsigned char nofree; /* 0 = free data on purge, 1 = only unlink */ + struct aim_conn_t *conn; /* the connection it came in on... */ + struct command_rx_struct *next; /* ptr to next struct in list */ +}; + +/* struct for outgoing commands */ +struct command_tx_struct { + unsigned char hdrtype; /* defines which piece of the union to use */ + union { + struct { + unsigned char type; + unsigned short seqnum; + } oscar; + struct { + unsigned short type; + unsigned char magic[4]; /* ODC2 OFT2 */ + unsigned short hdr2len; + unsigned char *hdr2; + } oft; + } hdr; + u_int commandlen; + u_char *data; + u_int lock; /* 0 = open, !0 = locked */ + u_int sent; /* 0 = pending, !0 = has been sent */ + struct aim_conn_t *conn; + struct command_tx_struct *next; /* ptr to next struct in list */ +}; + +/* + * AIM Session: The main client-data interface. + * + */ +struct aim_session_t { + + /* ---- 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 + */ + struct aim_conn_t *connlist; + faim_mutex_t connlistlock; + + /* + * TX/RX queues + */ + struct command_tx_struct *queue_outgoing; + struct command_rx_struct *queue_incoming; + + /* + * Tx Enqueuing function + */ + int (*tx_enqueue)(struct aim_session_t *, struct command_tx_struct *); + + /* + * This is a dreadful solution to the what-room-are-we-joining + * problem. (There's no connection between the service + * request and the resulting redirect.) + */ + char *pendingjoin; + unsigned short pendingjoinexchange; + + /* + * Outstanding snac handling + * + * XXX: Should these be per-connection? -mid + */ + struct aim_snac_t *snac_hash[FAIM_SNAC_HASH_SIZE]; + faim_mutex_t snac_hash_locks[FAIM_SNAC_HASH_SIZE]; + unsigned long snac_nextid; + + struct { + char server[128]; + char username[128]; + char password[128]; + } socksproxy; + + unsigned long flags; + + int debug; + void (*debugcb)(struct aim_session_t *sess, int level, const char *format, va_list va); /* same as faim_debugging_callback_t */ + + struct aim_msgcookie_t *msgcookies; +}; + +/* Values for sess->flags */ +#define AIM_SESS_FLAGS_SNACLOGIN 0x00000001 +#define AIM_SESS_FLAGS_XORLOGIN 0x00000002 +#define AIM_SESS_FLAGS_NONBLOCKCONNECT 0x00000004 + +/* + * AIM User Info, Standard Form. + */ +struct aim_userinfo_s { + char sn[MAXSNLEN+1]; + u_short warnlevel; + u_short idletime; + u_short flags; + u_long membersince; + u_long onlinesince; + u_long sessionlen; + u_short capabilities; + struct { + unsigned short status; + unsigned int ipaddr; + char crap[0x25]; /* until we figure it out... */ + } icqinfo; +}; + +#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_UNKNOWN40 0x0040 +#define AIM_FLAG_UNKNOWN80 0x0080 + +#define AIM_FLAG_ALLUSERS 0x001f + + +#if defined(FAIM_INTERNAL) || defined(FAIM_NEED_TLV) +/* + * TLV handling + */ + +/* Generic TLV structure. */ +struct aim_tlv_t { + u_short type; + u_short length; + u_char *value; +}; + +/* List of above. */ +struct aim_tlvlist_t { + struct aim_tlv_t *tlv; + struct aim_tlvlist_t *next; +}; + +/* TLV-handling functions */ +faim_internal struct aim_tlvlist_t *aim_readtlvchain(u_char *buf, int maxlen); +faim_internal void aim_freetlvchain(struct aim_tlvlist_t **list); +faim_internal struct aim_tlv_t *aim_grabtlv(u_char *src); +faim_internal struct aim_tlv_t *aim_grabtlvstr(u_char *src); +faim_internal struct aim_tlv_t *aim_gettlv(struct aim_tlvlist_t *, u_short, int); +faim_internal char *aim_gettlv_str(struct aim_tlvlist_t *, u_short, int); +faim_internal unsigned char aim_gettlv8(struct aim_tlvlist_t *list, unsigned short type, int num); +faim_internal unsigned short aim_gettlv16(struct aim_tlvlist_t *list, unsigned short type, int num); +faim_internal unsigned long aim_gettlv32(struct aim_tlvlist_t *list, unsigned short type, int num); +faim_internal int aim_puttlv (u_char *dest, struct aim_tlv_t *newtlv); +faim_internal struct aim_tlv_t *aim_createtlv(void); +faim_internal int aim_freetlv(struct aim_tlv_t **oldtlv); +faim_internal int aim_puttlv_8(unsigned char *buf, unsigned short t, unsigned char v); +faim_internal int aim_puttlv_16(u_char *, u_short, u_short); +faim_internal int aim_puttlv_32(u_char *, u_short, u_long); +faim_internal int aim_puttlv_str(u_char *buf, u_short t, int l, char *v); +faim_internal int aim_writetlvchain(u_char *buf, int buflen, struct aim_tlvlist_t **list); +faim_internal int aim_addtlvtochain16(struct aim_tlvlist_t **list, unsigned short type, unsigned short val); +faim_internal int aim_addtlvtochain32(struct aim_tlvlist_t **list, unsigned short type, unsigned long val); +faim_internal int aim_addtlvtochain_str(struct aim_tlvlist_t **list, unsigned short type, char *str, int len); +faim_internal int aim_addtlvtochain_caps(struct aim_tlvlist_t **list, unsigned short type, unsigned short caps); +faim_internal int aim_addtlvtochain_noval(struct aim_tlvlist_t **list, unsigned short type); +faim_internal int aim_counttlvchain(struct aim_tlvlist_t **list); +#endif /* FAIM_INTERNAL */ + +/* + * Get command from connections / Dispatch commands + * already in queue. + */ +faim_export int aim_get_command(struct aim_session_t *, struct aim_conn_t *); +int aim_rxdispatch(struct aim_session_t *); + +faim_export unsigned long aim_debugconn_sendconnect(struct aim_session_t *sess, struct aim_conn_t *conn); + +faim_export int aim_logoff(struct aim_session_t *); + +#ifndef FAIM_INTERNAL +/* the library should never call aim_conn_kill */ +faim_export void aim_conn_kill(struct aim_session_t *sess, struct aim_conn_t **deadconn); +#endif /* ndef FAIM_INTERNAL */ + +typedef int (*rxcallback_t)(struct aim_session_t *, struct command_rx_struct *, ...); + +#ifdef FAIM_INTERNAL +faim_internal unsigned long aim_genericreq_n(struct aim_session_t *, struct aim_conn_t *conn, u_short family, u_short subtype); +faim_internal unsigned long aim_genericreq_l(struct aim_session_t *, struct aim_conn_t *conn, u_short family, u_short subtype, u_long *); +faim_internal unsigned long aim_genericreq_s(struct aim_session_t *, struct aim_conn_t *conn, u_short family, u_short subtype, u_short *); +#endif + +/* aim_login.c */ +faim_export int aim_sendconnack(struct aim_session_t *sess, struct aim_conn_t *conn); +faim_export int aim_request_login (struct aim_session_t *sess, struct aim_conn_t *conn, char *sn); +faim_export int aim_send_login (struct aim_session_t *, struct aim_conn_t *, char *, char *, struct client_info_s *, char *key); +faim_export unsigned long aim_sendauthresp(struct aim_session_t *sess, struct aim_conn_t *conn, char *sn, int errorcode, char *errorurl, char *bosip, char *cookie, char *email, int regstatus); +faim_export int aim_gencookie(unsigned char *buf); +faim_export int aim_sendserverready(struct aim_session_t *sess, struct aim_conn_t *conn); +faim_export unsigned long aim_sendredirect(struct aim_session_t *sess, struct aim_conn_t *conn, unsigned short servid, char *ip, char *cookie); +faim_export void aim_purge_rxqueue(struct aim_session_t *); + +#ifdef FAIM_INTERNAL +faim_internal int aim_authkeyparse(struct aim_session_t *sess, struct command_rx_struct *command); +faim_internal void aim_rxqueue_cleanbyconn(struct aim_session_t *sess, struct aim_conn_t *conn); +faim_internal int aim_recv(int fd, void *buf, size_t count); + +faim_internal int aim_parse_unknown(struct aim_session_t *, struct command_rx_struct *command, ...); +faim_internal int aim_get_command_rendezvous(struct aim_session_t *sess, struct aim_conn_t *conn); + +faim_internal int aim_tx_sendframe(struct aim_session_t *sess, struct command_tx_struct *cur); +faim_internal unsigned int aim_get_next_txseqnum(struct aim_conn_t *); +faim_internal struct command_tx_struct *aim_tx_new(struct aim_session_t *sess, struct aim_conn_t *conn, unsigned char framing, int chan, int datalen); +faim_internal int aim_tx_enqueue(struct aim_session_t *, struct command_tx_struct *); +faim_internal int aim_tx_printqueue(struct aim_session_t *); +faim_internal int aim_parse_hostonline(struct aim_session_t *sess, struct command_rx_struct *command, ...); +faim_internal int aim_parse_hostversions(struct aim_session_t *sess, struct command_rx_struct *command, ...); +faim_internal int aim_parse_accountconfirm(struct aim_session_t *sess, struct command_rx_struct *command); +faim_internal int aim_parse_infochange(struct aim_session_t *sess, struct command_rx_struct *command); +#endif /* FAIM_INTERNAL */ + +#define AIM_TX_QUEUED 0 /* default */ +#define AIM_TX_IMMEDIATE 1 +#define AIM_TX_USER 2 +faim_export int aim_tx_setenqueue(struct aim_session_t *sess, int what, int (*func)(struct aim_session_t *, struct command_tx_struct *)); + +faim_export int aim_tx_flushqueue(struct aim_session_t *); +faim_export void aim_tx_purgequeue(struct aim_session_t *); + +struct aim_rxcblist_t { + u_short family; + u_short type; + rxcallback_t handler; + u_short flags; + struct aim_rxcblist_t *next; +}; + +faim_export int aim_conn_setlatency(struct aim_conn_t *conn, int newval); + +faim_export int aim_conn_addhandler(struct aim_session_t *, struct aim_conn_t *conn, u_short family, u_short type, rxcallback_t newhandler, u_short flags); +faim_export int aim_clearhandlers(struct aim_conn_t *conn); + +#ifdef FAIM_INTERNAL +faim_internal rxcallback_t aim_callhandler(struct aim_session_t *sess, struct aim_conn_t *conn, u_short family, u_short type); +#endif + +#ifdef FAIM_INTERNAL +/* + * Generic SNAC structure. Rarely if ever used. + */ +struct aim_snac_t { + u_long id; + u_short family; + u_short type; + u_short flags; + void *data; + time_t issuetime; + struct aim_snac_t *next; +}; +faim_internal void aim_initsnachash(struct aim_session_t *sess); +faim_internal unsigned long aim_newsnac(struct aim_session_t *, struct aim_snac_t *newsnac); +faim_internal unsigned long aim_cachesnac(struct aim_session_t *sess, const unsigned short family, const unsigned short type, const unsigned short flags, const void *data, const int datalen); +faim_internal struct aim_snac_t *aim_remsnac(struct aim_session_t *, u_long id); +faim_internal int aim_cleansnacs(struct aim_session_t *, int maxage); +faim_internal int aim_putsnac(u_char *, int, int, int, u_long); +#endif /* FAIM_INTERNAL */ + +#ifdef FAIM_INTERNAL +faim_internal void aim_connrst(struct aim_session_t *); +faim_internal struct aim_conn_t *aim_conn_getnext(struct aim_session_t *); +faim_internal struct aim_conn_t *aim_cloneconn(struct aim_session_t *sess, struct aim_conn_t *src); +#endif /* FAIM_INTERNAL */ + +faim_export void aim_conn_close(struct aim_conn_t *deadconn); +faim_export struct aim_conn_t *aim_newconn(struct aim_session_t *, int type, char *dest); +faim_export int aim_conngetmaxfd(struct aim_session_t *); +faim_export struct aim_conn_t *aim_select(struct aim_session_t *, struct timeval *, int *); +faim_export int aim_conn_isready(struct aim_conn_t *); +faim_export int aim_conn_setstatus(struct aim_conn_t *, int); +faim_export int aim_conn_completeconnect(struct aim_session_t *sess, struct aim_conn_t *conn); +faim_export int aim_conn_isconnecting(struct aim_conn_t *conn); + +typedef void (*faim_debugging_callback_t)(struct aim_session_t *sess, int level, const char *format, va_list va); +faim_export int aim_setdebuggingcb(struct aim_session_t *sess, faim_debugging_callback_t); +faim_export void aim_session_init(struct aim_session_t *, unsigned long flags, int debuglevel); +faim_export void aim_setupproxy(struct aim_session_t *sess, char *server, char *username, char *password); +faim_export struct aim_conn_t *aim_getconn_type(struct aim_session_t *, int type); + +/* aim_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_send_warning(struct aim_session_t *sess, struct aim_conn_t *conn, char *destsn, int anon); +faim_export unsigned long aim_bos_nop(struct aim_session_t *, struct aim_conn_t *); +faim_export unsigned long aim_flap_nop(struct aim_session_t *sess, struct aim_conn_t *conn); +faim_export unsigned long aim_bos_setidle(struct aim_session_t *, struct aim_conn_t *, u_long); +faim_export unsigned long aim_bos_changevisibility(struct aim_session_t *, struct aim_conn_t *, int, char *); +faim_export unsigned long aim_bos_setbuddylist(struct aim_session_t *, struct aim_conn_t *, char *); +faim_export unsigned long aim_bos_setprofile(struct aim_session_t *, struct aim_conn_t *, char *, char *, unsigned short); +faim_export unsigned long aim_bos_setgroupperm(struct aim_session_t *, struct aim_conn_t *, u_long); +faim_export unsigned long aim_bos_clientready(struct aim_session_t *, struct aim_conn_t *); +faim_export unsigned long aim_bos_reqrate(struct aim_session_t *, struct aim_conn_t *); +faim_export unsigned long aim_bos_ackrateresp(struct aim_session_t *, struct aim_conn_t *); +faim_export unsigned long aim_bos_setprivacyflags(struct aim_session_t *, struct aim_conn_t *, u_long); +faim_export unsigned long aim_bos_reqpersonalinfo(struct aim_session_t *, struct aim_conn_t *); +faim_export unsigned long aim_bos_reqservice(struct aim_session_t *, struct aim_conn_t *, u_short); +faim_export unsigned long aim_bos_reqrights(struct aim_session_t *, struct aim_conn_t *); +faim_export unsigned long aim_bos_reqbuddyrights(struct aim_session_t *, struct aim_conn_t *); +faim_export unsigned long aim_bos_reqlocaterights(struct aim_session_t *, struct aim_conn_t *); +faim_export unsigned long aim_bos_reqicbmparaminfo(struct aim_session_t *, struct aim_conn_t *); +faim_export unsigned long aim_addicbmparam(struct aim_session_t *sess,struct aim_conn_t *conn); +faim_export unsigned long aim_setversions(struct aim_session_t *sess, struct aim_conn_t *conn); +faim_export unsigned long aim_auth_setversions(struct aim_session_t *sess, struct aim_conn_t *conn); +faim_export unsigned long aim_auth_reqconfirm(struct aim_session_t *sess, struct aim_conn_t *conn); +faim_export unsigned long aim_auth_getinfo(struct aim_session_t *sess, struct aim_conn_t *conn, unsigned short info); +faim_export unsigned long aim_auth_setemail(struct aim_session_t *sess, struct aim_conn_t *conn, char *newemail); +faim_export unsigned long aim_setdirectoryinfo(struct aim_session_t *sess, struct aim_conn_t *conn, char *first, char *middle, char *last, char *maiden, char *nickname, char *street, char *city, char *state, char *zip, int country, unsigned short privacy); +faim_export unsigned long aim_setuserinterests(struct aim_session_t *sess, struct aim_conn_t *conn, char *interest1, char *interest2, char *interest3, char *interest4, char *interest5, unsigned short privacy); +faim_export unsigned long aim_icq_setstatus(struct aim_session_t *sess, struct aim_conn_t *conn, unsigned long status); + +faim_export struct aim_fileheader_t *aim_getlisting(struct aim_session_t *sess, FILE *); + +#ifdef FAIM_INTERNAL +faim_internal int aim_oft_buildheader(unsigned char *,struct aim_fileheader_t *); +faim_internal int aim_listenestablish(u_short); +faim_internal int aim_tx_destroy(struct command_tx_struct *); +#endif /* FAIM_INTERNAL */ + +/* aim_rxhandlers.c */ +faim_export int aim_rxdispatch(struct aim_session_t *); + +#ifdef FAIM_INTERNAL +faim_internal int aim_authparse(struct aim_session_t *, struct command_rx_struct *); +faim_internal int aim_handleredirect_middle(struct aim_session_t *, struct command_rx_struct *, ...); +faim_internal int aim_parse_unknown(struct aim_session_t *, struct command_rx_struct *, ...); +faim_internal int aim_parse_generalerrs(struct aim_session_t *, struct command_rx_struct *command, ...); +faim_internal int aim_parsemotd_middle(struct aim_session_t *sess, struct command_rx_struct *command, ...); + +/* these are used by aim_*_clientready */ +#define AIM_TOOL_JAVA 0x0001 +#define AIM_TOOL_MAC 0x0002 +#define AIM_TOOL_WIN16 0x0003 +#define AIM_TOOL_WIN32 0x0004 +#define AIM_TOOL_MAC68K 0x0005 +#define AIM_TOOL_MACPPC 0x0006 +#define AIM_TOOL_NEWWIN 0x0010 +struct aim_tool_version { + unsigned short group; + unsigned short version; + unsigned short tool; + unsigned short toolversion; +}; + +#endif /* FAIM_INTERNAL */ + +#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 unsigned short aim_fingerprintclient(unsigned char *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 unsigned long aim_ads_clientready(struct aim_session_t *sess, struct aim_conn_t *conn); +faim_export unsigned long aim_ads_requestads(struct aim_session_t *sess, struct aim_conn_t *conn); + +#ifdef FAIM_INTERNAL +faim_internal int aim_parse_ratechange_middle(struct aim_session_t *sess, struct command_rx_struct *command); + +faim_internal int aim_parse_evilnotify_middle(struct aim_session_t *sess, struct command_rx_struct *command); +faim_internal int aim_parse_msgack_middle(struct aim_session_t *sess, struct command_rx_struct *command); +#endif /* FAIM_INTERNAL */ + +/* aim_im.c */ +struct aim_directim_priv { + unsigned char cookie[8]; + char sn[MAXSNLEN+1]; + char ip[30]; +}; + +struct aim_fileheader_t { +#if 0 + char magic[4]; /* 0 */ + short hdrlen; /* 4 */ + short hdrtype; /* 6 */ +#endif + char bcookie[8]; /* 8 */ + short encrypt; /* 16 */ + short compress; /* 18 */ + short totfiles; /* 20 */ + short filesleft; /* 22 */ + short totparts; /* 24 */ + short partsleft; /* 26 */ + long totsize; /* 28 */ + long size; /* 32 */ + long modtime; /* 36 */ + long checksum; /* 40 */ + long rfrcsum; /* 44 */ + long rfsize; /* 48 */ + long cretime; /* 52 */ + long rfcsum; /* 56 */ + long nrecvd; /* 60 */ + long recvcsum; /* 64 */ + char idstring[32]; /* 68 */ + char flags; /* 100 */ + char lnameoffset; /* 101 */ + char lsizeoffset; /* 102 */ + char dummy[69]; /* 103 */ + char macfileinfo[16]; /* 172 */ + short nencode; /* 188 */ + short nlanguage; /* 190 */ + char name[64]; /* 192 */ + /* 256 */ +}; + +struct aim_filetransfer_priv { + char sn[MAXSNLEN]; + char cookie[8]; + char ip[30]; + int state; + struct aim_fileheader_t fh; +}; + + +#define AIM_IMFLAGS_AWAY 0x01 /* mark as an autoreply */ +#define AIM_IMFLAGS_ACK 0x02 /* request a receipt notice */ + +faim_export unsigned long aim_send_im(struct aim_session_t *, struct aim_conn_t *, char *, u_int, char *); +faim_export int aim_send_im_direct(struct aim_session_t *, struct aim_conn_t *, char *); +faim_export struct aim_conn_t * aim_directim_initiate(struct aim_session_t *, struct aim_conn_t *, struct aim_directim_priv *, char *destsn); +faim_export struct aim_conn_t *aim_directim_connect(struct aim_session_t *, struct aim_conn_t *, struct aim_directim_priv *); +faim_export unsigned long aim_seticbmparam(struct aim_session_t *, struct aim_conn_t *conn); + +#ifdef FAIM_INTERNAL +faim_internal int aim_parse_incoming_im_middle(struct aim_session_t *, struct command_rx_struct *); +faim_internal int aim_parse_outgoing_im_middle(struct aim_session_t *, struct command_rx_struct *); +faim_internal int aim_parse_msgerror_middle(struct aim_session_t *, struct command_rx_struct *); +faim_internal int aim_negchan_middle(struct aim_session_t *sess, struct command_rx_struct *command); +faim_internal int aim_parse_bosrights(struct aim_session_t *sess, struct command_rx_struct *command, ...); +faim_internal int aim_parse_missedcall(struct aim_session_t *sess, struct command_rx_struct *command); +#endif /* FAIM_INTERNAL */ + +faim_export struct aim_conn_t *aim_getfile_initiate(struct aim_session_t *sess, struct aim_conn_t *conn, char *destsn); +faim_export int aim_oft_getfile_request(struct aim_session_t *sess, struct aim_conn_t *conn, const unsigned char *name, const int size); +faim_export int aim_oft_getfile_ack(struct aim_session_t *sess, struct aim_conn_t *conn); +faim_export int aim_oft_getfile_end(struct aim_session_t *sess, struct aim_conn_t *conn); + +/* aim_info.c */ +#define AIM_CAPS_BUDDYICON 0x01 +#define AIM_CAPS_VOICE 0x02 +#define AIM_CAPS_IMIMAGE 0x04 +#define AIM_CAPS_CHAT 0x08 +#define AIM_CAPS_GETFILE 0x10 +#define AIM_CAPS_SENDFILE 0x20 +#define AIM_CAPS_GAMES 0x40 +#define AIM_CAPS_SAVESTOCKS 0x80 + +#ifdef FAIM_INTERNAL +extern u_char aim_caps[8][16]; +faim_internal unsigned short aim_getcap(struct aim_session_t *sess, unsigned char *capblock, int buflen); +faim_internal int aim_putcap(unsigned char *capblock, int buflen, u_short caps); +#endif /* FAIM_INTERNAL */ + + +#define AIM_GETINFO_GENERALINFO 0x00001 +#define AIM_GETINFO_AWAYMESSAGE 0x00003 + +struct aim_msgcookie_t { + unsigned char cookie[8]; + int type; + void *data; + time_t addtime; + struct aim_msgcookie_t *next; +}; + +struct aim_invite_priv { + char *sn; + char *roomname; + int exchange; + int 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 + +#ifdef FAIM_INTERNAL +faim_internal int aim_cachecookie(struct aim_session_t *sess, struct aim_msgcookie_t *cookie); +faim_internal struct aim_msgcookie_t *aim_uncachecookie(struct aim_session_t *sess, unsigned char *cookie, int type); +faim_internal struct aim_msgcookie_t *aim_mkcookie(unsigned char *, int, void *); +faim_internal struct aim_msgcookie_t *aim_checkcookie(struct aim_session_t *, const unsigned char *, const int); +faim_internal int aim_freecookie(struct aim_session_t *sess, struct aim_msgcookie_t *cookie); +faim_internal int aim_msgcookie_gettype(int reqclass); +faim_internal int aim_cookie_free(struct aim_session_t *sess, struct aim_msgcookie_t *cookie); +#endif /* FAIM_INTERNAL */ + +faim_export int aim_handlerendconnect(struct aim_session_t *sess, struct aim_conn_t *cur); + +#define AIM_TRANSFER_DENY_NOTSUPPORTED 0x0000 +#define AIM_TRANSFER_DENY_DECLINE 0x0001 +#define AIM_TRANSFER_DENY_NOTACCEPTING 0x0002 +faim_export unsigned long aim_denytransfer(struct aim_session_t *sess, struct aim_conn_t *conn, char *sender, char *cookie, unsigned short code); +faim_export struct aim_conn_t *aim_accepttransfer(struct aim_session_t *sess, struct aim_conn_t *conn, char *sn, char *cookie, char *ip, unsigned short listingfiles, unsigned short listingtotsize, unsigned short listingsize, unsigned int listingchecksum, unsigned short rendid); + +#ifdef FAIM_INTERNAL +faim_internal int aim_extractuserinfo(struct aim_session_t *sess, unsigned char *, struct aim_userinfo_s *); +faim_internal int aim_parse_userinfo_middle(struct aim_session_t *, struct command_rx_struct *); +faim_internal int aim_parse_oncoming_middle(struct aim_session_t *, struct command_rx_struct *); +faim_internal int aim_parse_offgoing_middle(struct aim_session_t *, struct command_rx_struct *); +faim_internal int aim_putuserinfo(u_char *buf, int buflen, struct aim_userinfo_s *info); +faim_internal int aim_parse_locateerr(struct aim_session_t *sess, struct command_rx_struct *command); +#endif /* FAIM_INTERNAL */ + +faim_export unsigned long aim_getinfo(struct aim_session_t *, struct aim_conn_t *, const char *, unsigned short); +faim_export int aim_sendbuddyoncoming(struct aim_session_t *sess, struct aim_conn_t *conn, struct aim_userinfo_s *info); +faim_export int aim_sendbuddyoffgoing(struct aim_session_t *sess, struct aim_conn_t *conn, char *sn); + + +/* aim_auth.c */ +faim_export int aim_auth_sendcookie(struct aim_session_t *, struct aim_conn_t *, u_char *); +faim_export u_long aim_auth_clientready(struct aim_session_t *, struct aim_conn_t *); +faim_export unsigned long aim_auth_changepasswd(struct aim_session_t *, struct aim_conn_t *, char *, char *); + +/* aim_buddylist.c */ +faim_export unsigned long aim_add_buddy(struct aim_session_t *, struct aim_conn_t *, char *); +faim_export unsigned long aim_remove_buddy(struct aim_session_t *, struct aim_conn_t *, char *); + +#ifdef FAIM_INTERNAL +faim_internal int aim_parse_buddyrights(struct aim_session_t *sess, struct command_rx_struct *command, ...); +#endif /* FAIM_INTERNAL */ + +/* aim_search.c */ +faim_export u_long aim_usersearch_address(struct aim_session_t *, struct aim_conn_t *, char *); + +#ifdef FAIM_INTERNAL +faim_internal unsigned long aim_parse_searcherror(struct aim_session_t *, struct command_rx_struct *); +faim_internal unsigned long aim_parse_searchreply(struct aim_session_t *, struct command_rx_struct *); +#endif + +struct aim_chat_roominfo { + u_short exchange; + char *name; + u_short instance; +}; + +struct aim_chat_exchangeinfo { + u_short number; + char *name; + char *charset1; + char *lang1; + char *charset2; + char *lang2; +}; + +faim_export unsigned long aim_chat_send_im(struct aim_session_t *sess, struct aim_conn_t *conn, char *msg); +faim_export unsigned long aim_chat_join(struct aim_session_t *sess, struct aim_conn_t *conn, u_short exchange, const char *roomname); +faim_export unsigned long aim_chat_clientready(struct aim_session_t *sess, struct aim_conn_t *conn); +faim_export int aim_chat_attachname(struct aim_conn_t *conn, char *roomname); +faim_export char *aim_chat_getname(struct aim_conn_t *conn); +faim_export struct aim_conn_t *aim_chat_getconn(struct aim_session_t *, char *name); + +faim_export unsigned long aim_chatnav_reqrights(struct aim_session_t *sess, struct aim_conn_t *conn); +faim_export unsigned long aim_chatnav_clientready(struct aim_session_t *sess, struct aim_conn_t *conn); + +faim_export unsigned long aim_chat_invite(struct aim_session_t *sess, struct aim_conn_t *conn, char *sn, char *msg, u_short exchange, char *roomname, u_short instance); + +faim_export u_long aim_chatnav_createroom(struct aim_session_t *sess, struct aim_conn_t *conn, char *name, u_short exchange); +faim_export int aim_chat_leaveroom(struct aim_session_t *sess, char *name); + +#ifdef FAIM_INTERNAL +faim_internal int aim_chat_readroominfo(u_char *buf, struct aim_chat_roominfo *outinfo); +faim_internal int aim_chat_parse_infoupdate(struct aim_session_t *sess, struct command_rx_struct *command); +faim_internal int aim_chat_parse_joined(struct aim_session_t *sess, struct command_rx_struct *command); +faim_internal int aim_chat_parse_leave(struct aim_session_t *sess, struct command_rx_struct *command); +faim_internal int aim_chat_parse_incoming(struct aim_session_t *sess, struct command_rx_struct *command); +faim_internal int aim_chatnav_parse_info(struct aim_session_t *sess, struct command_rx_struct *command); +#endif /* FAIM_INTERNAL */ + +/* aim_util.c */ +#ifdef AIMUTIL_USEMACROS +/* + * These are really ugly. You'd think this was LISP. I wish it was. + */ +#define aimutil_put8(buf, data) ((*(buf) = (u_char)(data)&0xff),1) +#define aimutil_get8(buf) ((*(buf))&0xff) +#define aimutil_put16(buf, data) ( \ + (*(buf) = (u_char)((data)>>8)&0xff), \ + (*((buf)+1) = (u_char)(data)&0xff), \ + 2) +#define aimutil_get16(buf) ((((*(buf))<<8)&0xff00) + ((*((buf)+1)) & 0xff)) +#define aimutil_put32(buf, data) ( \ + (*((buf)) = (u_char)((data)>>24)&0xff), \ + (*((buf)+1) = (u_char)((data)>>16)&0xff), \ + (*((buf)+2) = (u_char)((data)>>8)&0xff), \ + (*((buf)+3) = (u_char)(data)&0xff), \ + 4) +#define aimutil_get32(buf) ((((*(buf))<<24)&0xff000000) + \ + (((*((buf)+1))<<16)&0x00ff0000) + \ + (((*((buf)+2))<< 8)&0x0000ff00) + \ + (((*((buf)+3) )&0x000000ff))) +#else +#warning Not using aimutil macros. May have performance problems. +int aimutil_put8(u_char *, u_char); +u_char aimutil_get8(u_char *buf); +int aimutil_put16(u_char *, u_short); +u_short aimutil_get16(u_char *); +int aimutil_put32(u_char *, u_long); +u_long aimutil_get32(u_char *); +#endif + +faim_export int aimutil_putstr(u_char *, const char *, int); +faim_export int aimutil_tokslen(char *toSearch, int index, char dl); +faim_export int aimutil_itemcnt(char *toSearch, char dl); +faim_export char *aimutil_itemidx(char *toSearch, int index, char dl); + +faim_export int aim_snlen(const char *sn); +faim_export int aim_sncmp(const char *sn1, const char *sn2); + +/* for libc's that dont have it */ +faim_export char *aim_strsep(char **pp, const char *delim); + +/* aim_meta.c */ +faim_export char *aim_getbuilddate(void); +faim_export char *aim_getbuildtime(void); +faim_export char *aim_getbuildstring(void); + +#ifdef FAIM_INTERNAL +faim_internal void faimdprintf(struct aim_session_t *sess, int dlevel, const char *format, ...); + +/* why the hell wont cpp let you use #error inside #define's? */ +#define printf() printf called inside libfaim +#define sprintf() unbounded sprintf used inside libfaim + +#endif /* FAIM_INTERNAL */ + +#endif /* __AIM_H__ */ +
--- a/libfaim/aim_ads.c Sun Mar 04 22:37:18 2001 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,42 +0,0 @@ -/* - * - * - */ - -#include <faim/aim.h> - -faim_export unsigned long aim_ads_clientready(struct aim_session_t *sess, - struct aim_conn_t *conn) -{ - struct command_tx_struct *newpacket; - int i; - - if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 0x1a))) - return -1; - - newpacket->lock = 1; - - i = aim_putsnac(newpacket->data, 0x0001, 0x0002, 0x0000, sess->snac_nextid); - - i+= aimutil_put16(newpacket->data+i, 0x0001); - i+= aimutil_put16(newpacket->data+i, 0x0002); - - i+= aimutil_put16(newpacket->data+i, 0x0001); - i+= aimutil_put16(newpacket->data+i, 0x0013); - - i+= aimutil_put16(newpacket->data+i, 0x0005); - i+= aimutil_put16(newpacket->data+i, 0x0001); - i+= aimutil_put16(newpacket->data+i, 0x0001); - i+= aimutil_put16(newpacket->data+i, 0x0001); - - newpacket->lock = 0; - aim_tx_enqueue(sess, newpacket); - - return (sess->snac_nextid++); -} - -faim_export unsigned long aim_ads_requestads(struct aim_session_t *sess, - struct aim_conn_t *conn) -{ - return aim_genericreq_n(sess, conn, 0x0005, 0x0002); -}
--- a/libfaim/aim_auth.c Sun Mar 04 22:37:18 2001 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,85 +0,0 @@ -/* - aim_auth.c - - Deals with the authorizer. - - */ - -#include <faim/aim.h> - -/* this just pushes the passed cookie onto the passed connection -- NO SNAC! */ -faim_export int aim_auth_sendcookie(struct aim_session_t *sess, - struct aim_conn_t *conn, - unsigned char *chipsahoy) -{ - struct command_tx_struct *newpacket; - int curbyte=0; - - if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0001, conn, 4+2+2+AIM_COOKIELEN))) - return -1; - - newpacket->lock = 1; - - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0006); - curbyte += aimutil_put16(newpacket->data+curbyte, AIM_COOKIELEN); - memcpy(newpacket->data+curbyte, chipsahoy, AIM_COOKIELEN); - - return aim_tx_enqueue(sess, newpacket); -} - -faim_export unsigned long aim_auth_clientready(struct aim_session_t *sess, - struct aim_conn_t *conn) -{ - struct command_tx_struct *newpacket; - int curbyte = 0; - - if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 26))) - return -1; - - newpacket->lock = 1; - - curbyte += aim_putsnac(newpacket->data+curbyte, 0x0001, 0x0002, 0x0000, sess->snac_nextid); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0013); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0007); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001); - - aim_tx_enqueue(sess, newpacket); - - aim_cachesnac(sess, 0x0001, 0x0004, 0x0000, NULL, 0); - - return sess->snac_nextid; -} - -faim_export unsigned long aim_auth_changepasswd(struct aim_session_t *sess, - struct aim_conn_t *conn, - char *new, char *current) -{ - struct command_tx_struct *newpacket; - int i; - - if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+4+strlen(current)+4+strlen(new)))) - return -1; - - newpacket->lock = 1; - - i = aim_putsnac(newpacket->data, 0x0007, 0x0004, 0x0000, sess->snac_nextid); - - /* current password TLV t(0002) */ - i += aim_puttlv_str(newpacket->data+i, 0x0002, strlen(current), current); - - /* new password TLV t(0012) */ - i += aim_puttlv_str(newpacket->data+i, 0x0012, strlen(new), new); - - aim_tx_enqueue(sess, newpacket); - - aim_cachesnac(sess, 0x0001, 0x0004, 0x0000, NULL, 0); - - return sess->snac_nextid; -}
--- a/libfaim/aim_buddylist.c Sun Mar 04 22:37:18 2001 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,98 +0,0 @@ - -#include <faim/aim.h> - -/* - * aim_add_buddy() - * - * Adds a single buddy to your buddy list after login. - * - */ -faim_export unsigned long aim_add_buddy(struct aim_session_t *sess, - struct aim_conn_t *conn, - char *sn ) -{ - struct command_tx_struct *newpacket; - int i; - - if(!sn) - return -1; - - if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+1+strlen(sn)))) - return -1; - - newpacket->lock = 1; - - i = aim_putsnac(newpacket->data, 0x0003, 0x0004, 0x0000, sess->snac_nextid); - i += aimutil_put8(newpacket->data+i, strlen(sn)); - i += aimutil_putstr(newpacket->data+i, sn, strlen(sn)); - - aim_tx_enqueue(sess, newpacket ); - - aim_cachesnac(sess, 0x0003, 0x0004, 0x0000, sn, strlen(sn)+1); - - return sess->snac_nextid; -} - -faim_export unsigned long aim_remove_buddy(struct aim_session_t *sess, - struct aim_conn_t *conn, - char *sn ) -{ - struct command_tx_struct *newpacket; - int i; - - if(!sn) - return -1; - - if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+1+strlen(sn)))) - return -1; - - newpacket->lock = 1; - - i = aim_putsnac(newpacket->data, 0x0003, 0x0005, 0x0000, sess->snac_nextid); - - i += aimutil_put8(newpacket->data+i, strlen(sn)); - i += aimutil_putstr(newpacket->data+i, sn, strlen(sn)); - - aim_tx_enqueue(sess, newpacket); - - aim_cachesnac(sess, 0x0003, 0x0005, 0x0000, sn, strlen(sn)+1); - - return sess->snac_nextid; -} - -faim_internal int aim_parse_buddyrights(struct aim_session_t *sess, - struct command_rx_struct *command, ...) -{ - rxcallback_t userfunc = NULL; - int ret=1; - struct aim_tlvlist_t *tlvlist; - unsigned short maxbuddies = 0, maxwatchers = 0; - - /* - * TLVs follow - */ - if (!(tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10))) - return ret; - - /* - * TLV type 0x0001: Maximum number of buddies. - */ - if (aim_gettlv(tlvlist, 0x0001, 1)) - maxbuddies = aim_gettlv16(tlvlist, 0x0001, 1); - - /* - * TLV type 0x0002: Maximum number of watchers. - * - * XXX: what the hell is a watcher? - * - */ - if (aim_gettlv(tlvlist, 0x0002, 1)) - maxwatchers = aim_gettlv16(tlvlist, 0x0002, 1); - - if ((userfunc = aim_callhandler(command->conn, 0x0003, 0x0003))) - ret = userfunc(sess, command, maxbuddies, maxwatchers); - - aim_freetlvchain(&tlvlist); - - return ret; -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfaim/aim_cbtypes.h Mon Mar 05 03:59:32 2001 +0000 @@ -0,0 +1,231 @@ +/* + * 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_ATH 0x0017 +#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_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: Messeging 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_CLIENTERROR 0x000b +#define AIM_CB_MSG_ACK 0x000c +#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: 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 + +/* + * OFT Services + * + * See non-SNAC note below. + */ +#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_DIRECTIMINITIATE 0x0005 + +#define AIM_CB_OFT_GETFILECONNECTREQ 0x0006 /* connect request -- actually an OSCAR CAP*/ +#define AIM_CB_OFT_GETFILELISTINGREQ 0x0007 /* OFT listing.txt request */ +#define AIM_CB_OFT_GETFILEFILEREQ 0x0008 /* received file request */ +#define AIM_CB_OFT_GETFILEFILESEND 0x0009 /* received file request confirm -- send data */ +#define AIM_CB_OFT_GETFILECOMPLETE 0x000a /* received file send complete*/ +#define AIM_CB_OFT_GETFILEINITIATE 0x000b /* request for file get acknowledge */ +#define AIM_CB_OFT_GETFILEDISCONNECT 0x000c /* OFT connection disconnected.*/ +#define AIM_CB_OFT_GETFILELISTING 0x000d /* OFT listing.txt received.*/ +#define AIM_CB_OFT_GETFILERECEIVE 0x000e /* OFT file incoming.*/ +#define AIM_CB_OFT_GETFILELISTINGRXCONFIRM 0x000f +#define AIM_CB_OFT_GETFILESTATE4 0x0010 + +#define AIM_CB_OFT_SENDFILEDISCONNECT 0x0020 /* OFT connection disconnected.*/ + + + +/* + * SNAC Family: Internal Messages + * + * This isn't truely 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_DEBUGCONN_CONNECT 0xe001 +#define AIM_CB_SPECIAL_UNKNOWN 0xffff +#define AIM_CB_SPECIAL_DEFAULT AIM_CB_SPECIAL_UNKNOWN + + +#endif/*__AIM_CBTYPES_H__ */
--- a/libfaim/aim_chat.c Sun Mar 04 22:37:18 2001 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,647 +0,0 @@ -/* - * aim_chat.c - * - * Routines for the Chat service. - * - */ - -#include <faim/aim.h> - -faim_export char *aim_chat_getname(struct aim_conn_t *conn) -{ - if (!conn) - return NULL; - if (conn->type != AIM_CONN_TYPE_CHAT) - return NULL; - - return (char *)conn->priv; /* yuck ! */ -} - -faim_export struct aim_conn_t *aim_chat_getconn(struct aim_session_t *sess, char *name) -{ - struct aim_conn_t *cur; - - faim_mutex_lock(&sess->connlistlock); - for (cur = sess->connlist; cur; cur = cur->next) { - if (cur->type != AIM_CONN_TYPE_CHAT) - continue; - if (!cur->priv) { - printf("faim: chat: chat connection with no name! (fd = %d)\n", cur->fd); - continue; - } - if (strcmp((char *)cur->priv, name) == 0) - break; - } - faim_mutex_unlock(&sess->connlistlock); - - return cur; -} - -faim_export int aim_chat_attachname(struct aim_conn_t *conn, char *roomname) -{ - if (!conn || !roomname) - return -1; - - if (conn->priv) - free(conn->priv); - - conn->priv = strdup(roomname); - - return 0; -} - -faim_export unsigned long aim_chat_send_im(struct aim_session_t *sess, - struct aim_conn_t *conn, - char *msg) -{ - - int curbyte,i; - struct command_tx_struct *newpacket; - - if (!sess || !conn || !msg) - return 0; - - if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 1152))) - return -1; - - newpacket->lock = 1; /* lock struct */ - - curbyte = 0; - curbyte += aim_putsnac(newpacket->data+curbyte, - 0x000e, 0x0005, 0x0000, sess->snac_nextid); - - /* - * Generate a random message cookie - */ - for (i=0;i<8;i++) - curbyte += aimutil_put8(newpacket->data+curbyte, (u_char) rand()); - - aim_cachecookie(sess, aim_mkcookie(newpacket->data+curbyte-8, AIM_COOKIETYPE_CHAT, NULL)); - - /* - * metaTLV start. -- i assume this is a metaTLV. it could be the - * channel ID though. - */ - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003); - - /* - * Type 1: Unknown. Blank. - */ - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); - - /* - * Type 6: Unknown. Blank. - */ - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0006); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); - - /* - * Type 5: Message block. Contains more TLVs. - * - * This could include other information... We just - * put in a message TLV however. - * - */ - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005); - curbyte += aimutil_put16(newpacket->data+curbyte, strlen(msg)+4); - - /* - * SubTLV: Type 1: Message - */ - curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x0001, strlen(msg), msg); - - newpacket->commandlen = curbyte; - - newpacket->lock = 0; - aim_tx_enqueue(sess, newpacket); - - return (sess->snac_nextid++); -} - -/* - * 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 unsigned long aim_chat_join(struct aim_session_t *sess, - struct aim_conn_t *conn, - u_short exchange, - const char *roomname) -{ - struct command_tx_struct *newpacket; - int i; - - if (!sess || !conn || !roomname) - return 0; - - if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+9+strlen(roomname)+2))) - return -1; - - newpacket->lock = 1; - - i = aim_putsnac(newpacket->data, 0x0001, 0x0004, 0x0000, sess->snac_nextid); - - i+= aimutil_put16(newpacket->data+i, 0x000e); - - /* - * this is techinally a TLV, but we can't use normal functions - * because we need the extraneous nulls and other weird things. - */ - i+= aimutil_put16(newpacket->data+i, 0x0001); - i+= aimutil_put16(newpacket->data+i, 2+1+strlen(roomname)+2); - i+= aimutil_put16(newpacket->data+i, exchange); - i+= aimutil_put8(newpacket->data+i, strlen(roomname)); - i+= aimutil_putstr(newpacket->data+i, roomname, strlen(roomname)); - i+= aimutil_put16(newpacket->data+i, 0x0000); /* instance? */ - - /* - * Chat hack. - * - * XXX: A problem occurs here if we request a channel - * join but it fails....pendingjoin will be nonnull - * even though the channel is never going to get a - * redirect! - * - */ - sess->pendingjoin = strdup(roomname); - sess->pendingjoinexchange = exchange; - - newpacket->lock = 0; - aim_tx_enqueue(sess, newpacket); - - aim_cachesnac(sess, 0x0001, 0x0004, 0x0000, roomname, strlen(roomname)+1); - - return sess->snac_nextid; -} - -faim_internal int aim_chat_readroominfo(u_char *buf, struct aim_chat_roominfo *outinfo) -{ - int namelen = 0; - int i = 0; - - if (!buf || !outinfo) - return 0; - - outinfo->exchange = aimutil_get16(buf+i); - i += 2; - - namelen = aimutil_get8(buf+i); - i += 1; - - outinfo->name = (char *)malloc(namelen+1); - memcpy(outinfo->name, buf+i, namelen); - outinfo->name[namelen] = '\0'; - i += namelen; - - outinfo->instance = aimutil_get16(buf+i); - i += 2; - - return i; -} - - -/* - * General room information. Lots of stuff. - * - * Values I know are in here but I havent attached - * them to any of the 'Unknown's: - * - Language (English) - * - * SNAC 000e/0002 - */ -faim_internal int aim_chat_parse_infoupdate(struct aim_session_t *sess, - struct command_rx_struct *command) -{ - struct aim_userinfo_s *userinfo = NULL; - rxcallback_t userfunc=NULL; - int ret = 1, i = 0; - int usercount = 0; - u_char detaillevel = 0; - char *roomname = NULL; - struct aim_chat_roominfo roominfo; - u_short tlvcount = 0; - struct aim_tlvlist_t *tlvlist; - char *roomdesc = NULL; - unsigned short unknown_c9 = 0; - unsigned long creationtime = 0; - unsigned short maxmsglen = 0; - unsigned short unknown_d2 = 0, unknown_d5 = 0; - - i = 10; - i += aim_chat_readroominfo(command->data+i, &roominfo); - - detaillevel = aimutil_get8(command->data+i); - i++; - - if (detaillevel != 0x02) { - if (detaillevel == 0x01) - printf("faim: chat_roomupdateinfo: detail level 1 not supported\n"); - else - printf("faim: chat_roomupdateinfo: unknown detail level %d\n", detaillevel); - return 1; - } - - tlvcount = aimutil_get16(command->data+i); - i += 2; - - /* - * Everything else are TLVs. - */ - tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i); - - /* - * TLV type 0x006a is the room name in Human Readable Form. - */ - if (aim_gettlv(tlvlist, 0x006a, 1)) - roomname = aim_gettlv_str(tlvlist, 0x006a, 1); - - /* - * Type 0x006f: Number of occupants. - */ - if (aim_gettlv(tlvlist, 0x006f, 1)) - usercount = aim_gettlv16(tlvlist, 0x006f, 1); - - /* - * Type 0x0073: Occupant list. - */ - if (aim_gettlv(tlvlist, 0x0073, 1)) { - int curoccupant = 0; - struct aim_tlv_t *tmptlv; - - tmptlv = aim_gettlv(tlvlist, 0x0073, 1); - - /* Allocate enough userinfo structs for all occupants */ - userinfo = calloc(usercount, sizeof(struct aim_userinfo_s)); - - i = 0; - while (curoccupant < usercount) - i += aim_extractuserinfo(tmptlv->value+i, &userinfo[curoccupant++]); - } - - /* - * Type 0x00c9: Unknown. (2 bytes) - */ - if (aim_gettlv(tlvlist, 0x00c9, 1)) - unknown_c9 = aim_gettlv16(tlvlist, 0x00c9, 1); - - /* - * Type 0x00ca: Creation time (4 bytes) - */ - if (aim_gettlv(tlvlist, 0x00ca, 1)) - creationtime = aim_gettlv32(tlvlist, 0x00ca, 1); - - /* - * Type 0x00d1: Maximum Message Length - */ - if (aim_gettlv(tlvlist, 0x00d1, 1)) - maxmsglen = aim_gettlv16(tlvlist, 0x00d1, 1); - - /* - * Type 0x00d2: Unknown. (2 bytes) - */ - if (aim_gettlv(tlvlist, 0x00d2, 1)) - unknown_d2 = aim_gettlv16(tlvlist, 0x00d2, 1); - - /* - * Type 0x00d3: Room Description - */ - if (aim_gettlv(tlvlist, 0x00d3, 1)) - roomdesc = aim_gettlv_str(tlvlist, 0x00d3, 1); - - /* - * Type 0x00d5: Unknown. (1 byte) - */ - if (aim_gettlv(tlvlist, 0x00d5, 1)) - unknown_d5 = aim_gettlv8(tlvlist, 0x00d5, 1); - - - if ((userfunc = aim_callhandler(command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_ROOMINFOUPDATE))) { - ret = userfunc(sess, - command, - &roominfo, - roomname, - usercount, - userinfo, - roomdesc, - unknown_c9, - creationtime, - maxmsglen, - unknown_d2, - unknown_d5); - } - free(roominfo.name); - free(userinfo); - free(roomname); - free(roomdesc); - aim_freetlvchain(&tlvlist); - - return ret; -} - -faim_internal int aim_chat_parse_joined(struct aim_session_t *sess, - struct command_rx_struct *command) -{ - struct aim_userinfo_s *userinfo = NULL; - rxcallback_t userfunc=NULL; - int i = 10, curcount = 0, ret = 1; - - while (i < command->commandlen) { - curcount++; - userinfo = realloc(userinfo, curcount * sizeof(struct aim_userinfo_s)); - i += aim_extractuserinfo(command->data+i, &userinfo[curcount-1]); - } - - if ((userfunc = aim_callhandler(command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERJOIN))) { - ret = userfunc(sess, - command, - curcount, - userinfo); - } - - free(userinfo); - - return ret; -} - -faim_internal int aim_chat_parse_leave(struct aim_session_t *sess, - struct command_rx_struct *command) -{ - - struct aim_userinfo_s *userinfo = NULL; - rxcallback_t userfunc=NULL; - int i = 10, curcount = 0, ret = 1; - - while (i < command->commandlen) { - curcount++; - userinfo = realloc(userinfo, curcount * sizeof(struct aim_userinfo_s)); - i += aim_extractuserinfo(command->data+i, &userinfo[curcount-1]); - } - - if ((userfunc = aim_callhandler(command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERLEAVE))) { - ret = userfunc(sess, - command, - curcount, - userinfo); - } - - free(userinfo); - - return ret; -} - -/* - * 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. - */ -faim_internal int aim_chat_parse_incoming(struct aim_session_t *sess, - struct command_rx_struct *command) -{ - struct aim_userinfo_s userinfo; - rxcallback_t userfunc=NULL; - int ret = 1, i = 0, z = 0; - unsigned char cookie[8]; - int channel; - struct aim_tlvlist_t *outerlist; - char *msg = NULL; - struct aim_msgcookie_t *ck; - - memset(&userinfo, 0x00, sizeof(struct aim_userinfo_s)); - - i = 10; /* skip snac */ - - /* - * ICBM Cookie. Cache it. - */ - for (z=0; z<8; z++,i++) - cookie[z] = command->data[i]; - - if ((ck = aim_uncachecookie(sess, cookie, AIM_COOKIETYPE_CHAT))) { - if (ck->data) - free(ck->data); - free(ck); - } - - /* - * Channel ID - * - * Channels 1 and 2 are implemented in the normal ICBM - * parser. - * - * We only do channel 3 here. - * - */ - channel = aimutil_get16(command->data+i); - i += 2; - - if (channel != 0x0003) { - printf("faim: chat_incoming: unknown channel! (0x%04x)\n", channel); - return 1; - } - - /* - * Start parsing TLVs right away. - */ - outerlist = aim_readtlvchain(command->data+i, command->commandlen-i); - - /* - * Type 0x0003: Source User Information - */ - if (aim_gettlv(outerlist, 0x0003, 1)) { - struct aim_tlv_t *userinfotlv; - - userinfotlv = aim_gettlv(outerlist, 0x0003, 1); - aim_extractuserinfo(userinfotlv->value, &userinfo); - } - - /* - * Type 0x0001: Unknown. - */ - if (aim_gettlv(outerlist, 0x0001, 1)) - ; - - /* - * Type 0x0005: Message Block. Conains more TLVs. - */ - if (aim_gettlv(outerlist, 0x0005, 1)) - { - struct aim_tlvlist_t *innerlist; - struct aim_tlv_t *msgblock; - - msgblock = aim_gettlv(outerlist, 0x0005, 1); - innerlist = aim_readtlvchain(msgblock->value, msgblock->length); - - /* - * Type 0x0001: Message. - */ - if (aim_gettlv(innerlist, 0x0001, 1)) - msg = aim_gettlv_str(innerlist, 0x0001, 1); - - aim_freetlvchain(&innerlist); - } - - userfunc = aim_callhandler(command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_INCOMINGMSG); - if (userfunc) - { - ret = userfunc(sess, - command, - &userinfo, - msg); - } - free(msg); - aim_freetlvchain(&outerlist); - - return ret; -} - -faim_export unsigned long aim_chat_clientready(struct aim_session_t *sess, - struct aim_conn_t *conn) -{ - struct command_tx_struct *newpacket; - int i; - - if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 0x20))) - return -1; - - newpacket->lock = 1; - - i = aim_putsnac(newpacket->data, 0x0001, 0x0002, 0x0000, sess->snac_nextid); - - i+= aimutil_put16(newpacket->data+i, 0x000e); - i+= aimutil_put16(newpacket->data+i, 0x0001); - - i+= aimutil_put16(newpacket->data+i, 0x0004); - i+= aimutil_put16(newpacket->data+i, 0x0001); - - i+= aimutil_put16(newpacket->data+i, 0x0001); - i+= aimutil_put16(newpacket->data+i, 0x0003); - - i+= aimutil_put16(newpacket->data+i, 0x0004); - i+= aimutil_put16(newpacket->data+i, 0x0686); - - newpacket->lock = 0; - aim_tx_enqueue(sess, newpacket); - - return (sess->snac_nextid++); -} - -faim_export int aim_chat_leaveroom(struct aim_session_t *sess, char *name) -{ - struct aim_conn_t *conn; - - if ((conn = aim_chat_getconn(sess, name))) - aim_conn_close(conn); - - if (!conn) - return -1; - return 0; -} - -/* - * conn must be a BOS connection! - */ -faim_export unsigned long aim_chat_invite(struct aim_session_t *sess, - struct aim_conn_t *conn, - char *sn, - char *msg, - u_short exchange, - char *roomname, - u_short instance) -{ - struct command_tx_struct *newpacket; - int i,curbyte=0; - - if (!sess || !conn || !sn || !msg || !roomname) - return -1; - - if (conn->type != AIM_CONN_TYPE_BOS) - return -1; - - if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 1152+strlen(sn)+strlen(roomname)+strlen(msg)))) - return -1; - - newpacket->lock = 1; - - curbyte = aim_putsnac(newpacket->data, 0x0004, 0x0006, 0x0000, sess->snac_nextid); - - /* - * Cookie - */ - for (i=0;i<8;i++) - curbyte += aimutil_put8(newpacket->data+curbyte, (u_char)rand()); - - /* XXX this should get uncached by the unwritten 'invite accept' handler */ - aim_cachecookie(sess, aim_mkcookie(newpacket->data+curbyte-8, AIM_COOKIETYPE_CHAT, NULL)); - - /* - * Channel (2) - */ - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002); - - /* - * Dest sn - */ - curbyte += aimutil_put8(newpacket->data+curbyte, strlen(sn)); - curbyte += aimutil_putstr(newpacket->data+curbyte, sn, strlen(sn)); - - /* - * TLV t(0005) - */ - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x28+strlen(msg)+0x04+0x03+strlen(roomname)+0x02); - - /* - * Unknown info - */ - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x3131); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x3538); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x3446); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x4100); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x748f); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x2420); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x6287); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x11d1); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x8222); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x4445); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x5354); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); - - /* - * TLV t(000a) -- Unknown - */ - curbyte += aimutil_put16(newpacket->data+curbyte, 0x000a); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001); - - /* - * TLV t(000f) -- Unknown - */ - curbyte += aimutil_put16(newpacket->data+curbyte, 0x000f); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); - - /* - * TLV t(000c) -- Invitation message - */ - curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x000c, strlen(msg), msg); - - /* - * TLV t(2711) -- Container for room information - */ - curbyte += aimutil_put16(newpacket->data+curbyte, 0x2711); - curbyte += aimutil_put16(newpacket->data+curbyte, 3+strlen(roomname)+2); - curbyte += aimutil_put16(newpacket->data+curbyte, exchange); - curbyte += aimutil_put8(newpacket->data+curbyte, strlen(roomname)); - curbyte += aimutil_putstr(newpacket->data+curbyte, roomname, strlen(roomname)); - curbyte += aimutil_put16(newpacket->data+curbyte, instance); - - newpacket->commandlen = curbyte; - newpacket->lock = 0; - aim_tx_enqueue(sess, newpacket); - - return (sess->snac_nextid++); -}
--- a/libfaim/aim_chatnav.c Sun Mar 04 22:37:18 2001 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,434 +0,0 @@ -/* - * - * - * - * - */ - -#include <faim/aim.h> - - -/* - * conn must be a chatnav connection! - */ -faim_export unsigned long aim_chatnav_reqrights(struct aim_session_t *sess, - struct aim_conn_t *conn) -{ - aim_genericreq_n(sess, conn, 0x000d, 0x0002); - - return sess->snac_nextid; -} - -faim_export unsigned long aim_chatnav_clientready(struct aim_session_t *sess, - struct aim_conn_t *conn) -{ - struct command_tx_struct *newpacket; - int i; - - if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 0x20))) - return -1; - - newpacket->lock = 1; - - i = aim_putsnac(newpacket->data, 0x0001, 0x0002, 0x0000, sess->snac_nextid); - - i+= aimutil_put16(newpacket->data+i, 0x000d); - i+= aimutil_put16(newpacket->data+i, 0x0001); - - i+= aimutil_put16(newpacket->data+i, 0x0004); - i+= aimutil_put16(newpacket->data+i, 0x0001); - - i+= aimutil_put16(newpacket->data+i, 0x0001); - i+= aimutil_put16(newpacket->data+i, 0x0003); - - i+= aimutil_put16(newpacket->data+i, 0x0004); - i+= aimutil_put16(newpacket->data+i, 0x0686); - - aim_tx_enqueue(sess, newpacket); - - return (sess->snac_nextid++); -} - -/* - * Since multiple things can trigger this callback, - * we must lookup the snacid to determine the original - * snac subtype that was called. - */ -faim_internal int aim_chatnav_parse_info(struct aim_session_t *sess, struct command_rx_struct *command) -{ - struct aim_snac_t *snac; - u_long snacid; - rxcallback_t userfunc; - int ret=1; - - snacid = aimutil_get32(command->data+6); - snac = aim_remsnac(sess, snacid); - - if (!snac) - { - printf("faim: chatnav_parse_info: received response to unknown request! (%08lx)\n", snacid); - return 1; - } - - if (snac->family != 0x000d) - { - printf("faim: chatnav_parse_info: recieved response that maps to corrupt request! (fam=%04x)\n", snac->family); - return 1; - } - - /* - * We now know what the original SNAC subtype was. - */ - switch(snac->type) - { - case 0x0002: /* request chat rights */ - { - struct aim_tlvlist_t *tlvlist; - struct aim_chat_exchangeinfo *exchanges = NULL; - int curexchange = 0; - struct aim_tlv_t *exchangetlv; - u_char maxrooms = 0; - struct aim_tlvlist_t *innerlist; - - tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10); - - /* - * Type 0x0002: Maximum concurrent rooms. - */ - if (aim_gettlv(tlvlist, 0x0002, 1)) - maxrooms = aim_gettlv8(tlvlist, 0x0002, 1); - - /* - * Type 0x0003: Exchange information - * - * There can be any number of these, each one - * representing another exchange. - * - */ - curexchange = 0; - while ((exchangetlv = aim_gettlv(tlvlist, 0x0003, curexchange+1))) - { - curexchange++; - exchanges = realloc(exchanges, curexchange * sizeof(struct aim_chat_exchangeinfo)); - - - /* exchange number */ - exchanges[curexchange-1].number = aimutil_get16(exchangetlv->value); - innerlist = aim_readtlvchain(exchangetlv->value+2, exchangetlv->length-2); - - /* - * Type 0x000d: Unknown. - */ - if (aim_gettlv(innerlist, 0x000d, 1)) - ; - - /* - * Type 0x0004: Unknown - */ - if (aim_gettlv(innerlist, 0x0004, 1)) - ; - - /* - * Type 0x0002: Unknown - */ - if (aim_gettlv(innerlist, 0x0002, 1)) { - unsigned short classperms; - - classperms = aim_gettlv16(innerlist, 0x0002, 1); - - printf("faim: class permissions %x\n", classperms); - } - - /* - * Type 0x00c9: Unknown - */ - if (aim_gettlv(innerlist, 0x00c9, 1)) - ; - - /* - * Type 0x00ca: Creation Date - */ - if (aim_gettlv(innerlist, 0x00ca, 1)) - ; - - /* - * Type 0x00d0: Mandatory Channels? - */ - if (aim_gettlv(innerlist, 0x00d0, 1)) - ; - - /* - * Type 0x00d1: Maximum Message length - */ - if (aim_gettlv(innerlist, 0x00d1, 1)) - ; - - /* - * Type 0x00d2: Maximum Occupancy? - */ - if (aim_gettlv(innerlist, 0x00d2, 1)) - ; - - /* - * Type 0x00d3: Exchange Name - */ - if (aim_gettlv(innerlist, 0x00d3, 1)) - exchanges[curexchange-1].name = aim_gettlv_str(innerlist, 0x00d3, 1); - else - exchanges[curexchange-1].name = NULL; - - /* - * Type 0x00d5: Creation Permissions - * - * 0 Creation not allowed - * 1 Room creation allowed - * 2 Exchange creation allowed - * - */ - if (aim_gettlv(innerlist, 0x00d5, 1)) { - unsigned char createperms; - - createperms = aim_gettlv8(innerlist, 0x00d5, 1); - } - - /* - * Type 0x00d6: Character Set (First Time) - */ - if (aim_gettlv(innerlist, 0x00d6, 1)) - exchanges[curexchange-1].charset1 = aim_gettlv_str(innerlist, 0x00d6, 1); - else - exchanges[curexchange-1].charset1 = NULL; - - /* - * Type 0x00d7: Language (First Time) - */ - if (aim_gettlv(innerlist, 0x00d7, 1)) - exchanges[curexchange-1].lang1 = aim_gettlv_str(innerlist, 0x00d7, 1); - else - exchanges[curexchange-1].lang1 = NULL; - - /* - * Type 0x00d8: Character Set (Second Time) - */ - if (aim_gettlv(innerlist, 0x00d8, 1)) - exchanges[curexchange-1].charset2 = aim_gettlv_str(innerlist, 0x00d8, 1); - else - exchanges[curexchange-1].charset2 = NULL; - - /* - * Type 0x00d9: Language (Second Time) - */ - if (aim_gettlv(innerlist, 0x00d9, 1)) - exchanges[curexchange-1].lang2 = aim_gettlv_str(innerlist, 0x00d9, 1); - else - exchanges[curexchange-1].lang2 = NULL; - - aim_freetlvchain(&innerlist); - } - - /* - * Call client. - */ - userfunc = aim_callhandler(command->conn, 0x000d, 0x0009); - if (userfunc) - ret = userfunc(sess, - command, - snac->type, - maxrooms, - curexchange, - exchanges); - curexchange--; - while(curexchange >= 0) - { - if (exchanges[curexchange].name) - free(exchanges[curexchange].name); - if (exchanges[curexchange].charset1) - free(exchanges[curexchange].charset1); - if (exchanges[curexchange].lang1) - free(exchanges[curexchange].lang1); - if (exchanges[curexchange].charset2) - free(exchanges[curexchange].charset2); - if (exchanges[curexchange].lang2) - free(exchanges[curexchange].lang2); - curexchange--; - } - free(exchanges); - aim_freetlvchain(&tlvlist); - - break; - } - case 0x0003: /* request exchange info */ - printf("faim: chatnav_parse_info: resposne to exchange info\n"); - break; - case 0x0004: /* request room info */ - printf("faim: chatnav_parse_info: response to room info\n"); - break; - case 0x0005: /* request more room info */ - printf("faim: chatnav_parse_info: response to more room info\n"); - break; - case 0x0006: /* request occupant list */ - printf("faim: chatnav_parse_info: response to occupant info\n"); - break; - case 0x0007: /* search for a room */ - printf("faim: chatnav_parse_info: search results\n"); - break; - case 0x0008: { /* create room */ - /* - 000d 0009 0000 0000 0010 - - 0004 0053 - 0004 -- exchange - 0c 7a 6f6f 6f6d 7a6f 6f6f 6d34 32 cookie/name - 0000 -- instance - 02 -- detail level - 0007 -- unknown! - 006a 000c 7a 6f 6f6f 6d7a 6f6f 6f6d 3432 -- fully qualified name - 00c9 0002 0011 -- flags - 00ca 0004 39c0 0883 -- create time - 00d1 0002 0200 -- max msg len - 00d2 0002 0018 -- max occupants - 00d3 000c -- name - 7a6f 6f6f 6d7a 6f6f 6f6d 3432 - 00d5 0001 02 -- creation permission - */ - struct aim_tlvlist_t *tlvlist, *innerlist; - char *ck = NULL, *fqcn = NULL, *name = NULL; - unsigned short exchange, instance, unknown, flags, maxmsglen, maxoccupancy; - unsigned long createtime = 0; - unsigned char createperms; - int i, cklen; - struct aim_tlv_t *bigblock; - - i = 10; - if (!(tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i))) { - printf("faim: unable to read top tlv in create room response\n"); - break; - } - - if (!(bigblock = aim_gettlv(tlvlist, 0x0004, 1))) { - printf("faim: no bigblock in top tlv in create room response\n"); - aim_freetlvchain(&tlvlist); - break; - } - i = 0; - - exchange = aimutil_get16(bigblock->value+i); - i += 2; - - cklen = aimutil_get8(bigblock->value+i); - i++; - - ck = malloc(cklen+1); - memcpy(ck, bigblock->value+i, cklen); - ck[cklen] = '\0'; - i += cklen; - - instance = aimutil_get16(bigblock->value+i); - i += 2; - - if (aimutil_get8(bigblock->value+i) != 0x02) { - printf("faim: unknown detaillevel in create room response (0x%02x)\n", aimutil_get8(bigblock->value+i)); - aim_freetlvchain(&tlvlist); - free(ck); - break; - } - i += 1; - - unknown = aimutil_get16(bigblock->value+i); - i += 2; - - if (!(innerlist = aim_readtlvchain(bigblock->value+i, bigblock->length-i))) { - printf("faim: unable to read inner tlv chain in create room response\n"); - aim_freetlvchain(&tlvlist); - free(ck); - break; - } - - if (aim_gettlv(innerlist, 0x006a, 1)) - fqcn = aim_gettlv_str(innerlist, 0x006a, 1); - - if (aim_gettlv(innerlist, 0x00c9, 1)) - flags = aim_gettlv16(innerlist, 0x00c9, 1); - - if (aim_gettlv(innerlist, 0x00ca, 1)) - createtime = aim_gettlv32(innerlist, 0x00ca, 1); - - if (aim_gettlv(innerlist, 0x00d1, 1)) - maxmsglen = aim_gettlv16(innerlist, 0x00d1, 1); - - if (aim_gettlv(innerlist, 0x00d2, 1)) - maxoccupancy = aim_gettlv16(innerlist, 0x00d2, 1); - - if (aim_gettlv(innerlist, 0x00d3, 1)) - name = aim_gettlv_str(innerlist, 0x00d3, 1); - - if (aim_gettlv(innerlist, 0x00d5, 1)) - createperms = aim_gettlv8(innerlist, 0x00d5, 1); - - if ((userfunc = aim_callhandler(command->conn, 0x000d, 0x0009))) { - ret = userfunc(sess, command, snac->type, fqcn, instance, exchange, flags, createtime, maxmsglen, maxoccupancy, createperms, unknown, name, ck); - } - - if (ck) - free(ck); - if (name) - free(name); - if (fqcn) - free(fqcn); - aim_freetlvchain(&innerlist); - aim_freetlvchain(&tlvlist); - - break; - } - default: /* unknown */ - printf("faim: chatnav_parse_info: unknown request subtype (%04x)\n", snac->type); - } - - if (snac && snac->data) - free(snac->data); - if (snac) - free(snac); - - return ret; -} - -faim_export unsigned long aim_chatnav_createroom(struct aim_session_t *sess, - struct aim_conn_t *conn, - char *name, - u_short exchange) -{ - struct command_tx_struct *newpacket; - int i; - - if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+12+strlen("invite")+strlen(name)))) - return -1; - - newpacket->lock = 1; - - i = aim_putsnac(newpacket->data, 0x000d, 0x0008, 0x0000, sess->snac_nextid); - - /* exchange */ - i+= aimutil_put16(newpacket->data+i, exchange); - - /* room cookie */ - i+= aimutil_put8(newpacket->data+i, strlen("invite")); - i+= aimutil_putstr(newpacket->data+i, "invite", strlen("invite")); - - /* instance */ - i+= aimutil_put16(newpacket->data+i, 0xffff); - - /* detail level */ - i+= aimutil_put8(newpacket->data+i, 0x01); - - /* tlvcount */ - i+= aimutil_put16(newpacket->data+i, 0x0001); - - /* room name */ - i+= aim_puttlv_str(newpacket->data+i, 0x00d3, strlen(name), name); - - aim_cachesnac(sess, 0x000d, 0x0008, 0x0000, NULL, 0); - - aim_tx_enqueue(sess, newpacket); - - return sess->snac_nextid; -}
--- a/libfaim/aim_conn.c Sun Mar 04 22:37:18 2001 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,805 +0,0 @@ - -/* - * aim_conn.c - * - * Does all this gloriously nifty connection handling stuff... - * - */ - -#include <faim/aim.h> - -#ifndef _WIN32 -#include <netdb.h> -#include <sys/socket.h> -#include <netinet/in.h> -#endif - -/** - * aim_connrst - Clears out connection list, killing remaining connections. - * @sess: Session to be cleared - * - * Clears out the connection list and kills any connections left. - * - */ -faim_internal void aim_connrst(struct aim_session_t *sess) -{ - faim_mutex_init(&sess->connlistlock); - if (sess->connlist) { - struct aim_conn_t *cur = sess->connlist, *tmp; - - while(cur) { - tmp = cur->next; - aim_conn_close(cur); - free(cur); - cur = tmp; - } - } - sess->connlist = NULL; - return; -} - -/** - * aim_conn_init - Reset a connection to default values. - * @deadconn: Connection to be reset - * - * Initializes and/or resets a connection structure. - * - */ -static void aim_conn_init(struct aim_conn_t *deadconn) -{ - if (!deadconn) - return; - - deadconn->fd = -1; - deadconn->subtype = -1; - deadconn->type = -1; - deadconn->seqnum = 0; - deadconn->lastactivity = 0; - deadconn->forcedlatency = 0; - deadconn->handlerlist = NULL; - deadconn->priv = NULL; - faim_mutex_init(&deadconn->active); - faim_mutex_init(&deadconn->seqnum_lock); - - return; -} - -/** - * aim_conn_getnext - Gets a new connection structure. - * @sess: Session - * - * Allocate a new empty connection structure. - * - */ -faim_internal struct aim_conn_t *aim_conn_getnext(struct aim_session_t *sess) -{ - struct aim_conn_t *newconn, *cur; - - if (!(newconn = malloc(sizeof(struct aim_conn_t)))) - return NULL; - - memset(newconn, 0, sizeof(struct aim_conn_t)); - aim_conn_init(newconn); - newconn->next = NULL; - - faim_mutex_lock(&sess->connlistlock); - if (sess->connlist == NULL) - sess->connlist = newconn; - else { - for (cur = sess->connlist; cur->next; cur = cur->next) - ; - cur->next = newconn; - } - faim_mutex_unlock(&sess->connlistlock); - - return newconn; -} - -/** - * aim_conn_kill - Close and free a connection. - * @sess: Session for the connection - * @deadconn: Connection to be freed - * - * Close, clear, and free a connection structure. - * - */ -faim_export void aim_conn_kill(struct aim_session_t *sess, struct aim_conn_t **deadconn) -{ - struct aim_conn_t *cur; - - if (!deadconn || !*deadconn) - return; - - faim_mutex_lock(&sess->connlistlock); - if (sess->connlist == NULL) - ; - else if (sess->connlist->next == NULL) { - if (sess->connlist == *deadconn) - sess->connlist = NULL; - } else { - cur = sess->connlist; - while (cur->next) { - if (cur->next == *deadconn) { - cur->next = cur->next->next; - break; - } - cur = cur->next; - } - } - faim_mutex_unlock(&sess->connlistlock); - - /* XXX: do we need this for txqueue too? */ - aim_rxqueue_cleanbyconn(sess, *deadconn); - - if ((*deadconn)->fd != -1) - aim_conn_close(*deadconn); - if ((*deadconn)->priv) - free((*deadconn)->priv); - free(*deadconn); - deadconn = NULL; - - return; -} - -/** - * aim_conn_close - Close a connection - * @deadconn: Connection to close - * - * Close (but not free) a connection. - * - * This leaves everything untouched except for clearing the - * handler list and setting the fd to -1 (used to recognize - * dead connections). - * - */ -faim_export void aim_conn_close(struct aim_conn_t *deadconn) -{ - - faim_mutex_destroy(&deadconn->active); - faim_mutex_destroy(&deadconn->seqnum_lock); - if (deadconn->fd >= 3) - close(deadconn->fd); - deadconn->fd = -1; - if (deadconn->handlerlist) - aim_clearhandlers(deadconn); - - return; -} - -/** - * aim_getconn_type - Find a connection of a specific type - * @sess: Session to search - * @type: Type of connection to look for - * - * Searches for a connection of the specified type in the - * specified session. Returns the first connection of that - * type found. - * - */ -faim_internal struct aim_conn_t *aim_getconn_type(struct aim_session_t *sess, - int type) -{ - struct aim_conn_t *cur; - - faim_mutex_lock(&sess->connlistlock); - for (cur = sess->connlist; cur; cur = cur->next) { - if ((cur->type == type) && !(cur->status & AIM_CONN_STATUS_INPROGRESS)) - break; - } - faim_mutex_unlock(&sess->connlistlock); - return cur; -} - -/** - * aim_proxyconnect - An extrememly quick and dirty SOCKS5 interface. - * @sess: Session to connect - * @host: Host to connect to - * @port: Port to connect to - * @statusret: Return value of the connection - * - * Attempts to connect to the specified host via the configured - * proxy settings, if present. If no proxy is configured for - * this session, the connection is done directly. - * - */ -static int aim_proxyconnect(struct aim_session_t *sess, - char *host, unsigned short port, - int *statusret) -{ - int fd = -1; - - if (strlen(sess->socksproxy.server)) { /* connecting via proxy */ - int i; - unsigned char buf[512]; - struct sockaddr_in sa; - struct hostent *hp; - char *proxy; - unsigned short proxyport = 1080; - - for(i=0;i<(int)strlen(sess->socksproxy.server);i++) { - if (sess->socksproxy.server[i] == ':') { - proxyport = atoi(&(sess->socksproxy.server[i+1])); - break; - } - } - proxy = (char *)malloc(i+1); - strncpy(proxy, sess->socksproxy.server, i); - proxy[i] = '\0'; - - if (!(hp = gethostbyname(proxy))) { - printf("proxyconnect: unable to resolve proxy name\n"); - *statusret = (h_errno | AIM_CONN_STATUS_RESOLVERR); - return -1; - } - free(proxy); - - memset(&sa.sin_zero, 0, 8); - sa.sin_port = htons(proxyport); - memcpy(&sa.sin_addr, hp->h_addr, hp->h_length); - sa.sin_family = hp->h_addrtype; - - fd = socket(hp->h_addrtype, SOCK_STREAM, 0); - if (connect(fd, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0) { - printf("proxyconnect: unable to connect to proxy\n"); - close(fd); - return -1; - } - - i = 0; - buf[0] = 0x05; /* SOCKS version 5 */ - if (strlen(sess->socksproxy.username)) { - buf[1] = 0x02; /* two methods */ - buf[2] = 0x00; /* no authentication */ - buf[3] = 0x02; /* username/password authentication */ - i = 4; - } else { - buf[1] = 0x01; - buf[2] = 0x00; - i = 3; - } - - if (write(fd, buf, i) < i) { - *statusret = errno; - close(fd); - return -1; - } - - if (read(fd, buf, 2) < 2) { - *statusret = errno; - close(fd); - return -1; - } - - if ((buf[0] != 0x05) || (buf[1] == 0xff)) { - *statusret = EINVAL; - close(fd); - return -1; - } - - /* check if we're doing username authentication */ - if (buf[1] == 0x02) { - i = aimutil_put8(buf, 0x01); /* version 1 */ - i += aimutil_put8(buf+i, strlen(sess->socksproxy.username)); - i += aimutil_putstr(buf+i, sess->socksproxy.username, strlen(sess->socksproxy.username)); - i += aimutil_put8(buf+i, strlen(sess->socksproxy.password)); - i += aimutil_putstr(buf+i, sess->socksproxy.password, strlen(sess->socksproxy.password)); - if (write(fd, buf, i) < i) { - *statusret = errno; - close(fd); - return -1; - } - if (read(fd, buf, 2) < 2) { - *statusret = errno; - close(fd); - return -1; - } - if ((buf[0] != 0x01) || (buf[1] != 0x00)) { - *statusret = EINVAL; - close(fd); - return -1; - } - } - - i = aimutil_put8(buf, 0x05); - i += aimutil_put8(buf+i, 0x01); /* CONNECT */ - i += aimutil_put8(buf+i, 0x00); /* reserved */ - i += aimutil_put8(buf+i, 0x03); /* address type: host name */ - i += aimutil_put8(buf+i, strlen(host)); - i += aimutil_putstr(buf+i, host, strlen(host)); - i += aimutil_put16(buf+i, port); - - if (write(fd, buf, i) < i) { - *statusret = errno; - close(fd); - return -1; - } - if (read(fd, buf, 10) < 10) { - *statusret = errno; - close(fd); - return -1; - } - if ((buf[0] != 0x05) || (buf[1] != 0x00)) { - *statusret = EINVAL; - close(fd); - return -1; - } - - } else { /* connecting directly */ - struct sockaddr_in sa; - struct hostent *hp; - - if (!(hp = gethostbyname(host))) { - *statusret = (h_errno | AIM_CONN_STATUS_RESOLVERR); - return -1; - } - - memset(&sa, 0, sizeof(struct sockaddr_in)); - sa.sin_port = htons(port); - memcpy(&sa.sin_addr, hp->h_addr, hp->h_length); - sa.sin_family = hp->h_addrtype; - - fd = socket(hp->h_addrtype, SOCK_STREAM, 0); - - if (sess->flags & AIM_SESS_FLAGS_NONBLOCKCONNECT) - fcntl(fd, F_SETFL, O_NONBLOCK); /* XXX save flags */ - - if (connect(fd, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0) { - if (sess->flags & AIM_SESS_FLAGS_NONBLOCKCONNECT) { - if ((errno == EINPROGRESS) || (errno == EINTR)) { - if (statusret) - *statusret |= AIM_CONN_STATUS_INPROGRESS; - return fd; - } - } - close(fd); - fd = -1; - } - } - return fd; -} - -/** - * aim_newconn - Open a new connection - * @sess: Session to create connection in - * @type: Type of connection to create - * @dest: Host to connect to (in "host:port" syntax) - * - * Opens a new connection to the specified dest host of specified - * type, using the proxy settings if available. If @host is %NULL, - * the connection is allocated and returned, but no connection - * is made. - * - * FIXME: Return errors in a more sane way. - * - */ -faim_export struct aim_conn_t *aim_newconn(struct aim_session_t *sess, - int type, char *dest) -{ - struct aim_conn_t *connstruct; - int ret; - u_short port = FAIM_LOGIN_PORT; - char *host = NULL; - int i=0; - - if ((connstruct=aim_conn_getnext(sess))==NULL) - return NULL; - - faim_mutex_lock(&connstruct->active); - - connstruct->type = type; - - if (!dest) { /* just allocate a struct */ - connstruct->fd = -1; - connstruct->status = 0; - faim_mutex_unlock(&connstruct->active); - return connstruct; - } - - /* - * As of 23 Jul 1999, AOL now sends the port number, preceded by a - * colon, in the BOS redirect. This fatally breaks all previous - * libfaims. Bad, bad AOL. - * - * We put this here to catch every case. - * - */ - - for(i=0;i<(int)strlen(dest);i++) { - if (dest[i] == ':') { - port = atoi(&(dest[i+1])); - break; - } - } - host = (char *)malloc(i+1); - strncpy(host, dest, i); - host[i] = '\0'; - - if ((ret = aim_proxyconnect(sess, host, port, &connstruct->status)) < 0) { - connstruct->fd = -1; - connstruct->status = (errno | AIM_CONN_STATUS_CONNERR); - free(host); - faim_mutex_unlock(&connstruct->active); - return connstruct; - } else - connstruct->fd = ret; - - faim_mutex_unlock(&connstruct->active); - - free(host); - - return connstruct; -} - -/** - * aim_conngetmaxfd - Return the highest valued file discriptor in session - * @sess: Session to search - * - * Returns the highest valued filed descriptor of all open - * connections in @sess. - * - */ -faim_export int aim_conngetmaxfd(struct aim_session_t *sess) -{ - int j = 0; - struct aim_conn_t *cur; - - faim_mutex_lock(&sess->connlistlock); - for (cur = sess->connlist; cur; cur = cur->next) { - if (cur->fd > j) - j = cur->fd; - } - faim_mutex_unlock(&sess->connlistlock); - - return j; -} - -/** - * aim_countconn - Return the number of open connections in the session - * @sess: Session to look at - * - * Returns the number of number connections in @sess. - * - */ -static int aim_countconn(struct aim_session_t *sess) -{ - int cnt = 0; - struct aim_conn_t *cur; - - faim_mutex_lock(&sess->connlistlock); - for (cur = sess->connlist; cur; cur = cur->next) - cnt++; - faim_mutex_unlock(&sess->connlistlock); - - return cnt; -} - -/** - * aim_conn_in_sess - Predicate to test the precense of a connection in a sess - * @sess: Session to look in - * @conn: Connection to look for - * - * Searches @sess for the passed connection. Returns 1 if its present, - * zero otherwise. - * - */ -faim_export int aim_conn_in_sess(struct aim_session_t *sess, struct aim_conn_t *conn) -{ - struct aim_conn_t *cur; - - faim_mutex_lock(&sess->connlistlock); - for(cur = sess->connlist; cur; cur = cur->next) - if(cur == conn) { - faim_mutex_unlock(&sess->connlistlock); - return 1; - } - faim_mutex_unlock(&sess->connlistlock); - return 0; -} - -/** - * aim_select - Wait for a socket with data or timeout - * @sess: Session to wait on - * @timeout: How long to wait - * @status: Return status - * - * Waits for a socket with data or for timeout, whichever comes first. - * See select(). - * - * Return codes in *status: - * -1 error in select() (%NULL returned) - * 0 no events pending (%NULL returned) - * 1 outgoing data pending (%NULL returned) - * 2 incoming data pending (connection with pending data returned) - * - * XXX: we could probably stand to do a little courser locking here. - * - */ -faim_export struct aim_conn_t *aim_select(struct aim_session_t *sess, - struct timeval *timeout, int *status) -{ - struct aim_conn_t *cur; - fd_set fds, wfds; - int maxfd = 0; - int i, haveconnecting = 0; - - faim_mutex_lock(&sess->connlistlock); - if (sess->connlist == NULL) { - faim_mutex_unlock(&sess->connlistlock); - *status = -1; - return NULL; - } - faim_mutex_unlock(&sess->connlistlock); - - FD_ZERO(&fds); - FD_ZERO(&wfds); - maxfd = 0; - - faim_mutex_lock(&sess->connlistlock); - for (cur = sess->connlist; cur; cur = cur->next) { - if (cur->fd == -1) { - /* don't let invalid/dead connections sit around */ - *status = 2; - faim_mutex_unlock(&sess->connlistlock); - return cur; - } else if (cur->status & AIM_CONN_STATUS_INPROGRESS) { - FD_SET(cur->fd, &wfds); - haveconnecting++; - } - FD_SET(cur->fd, &fds); - if (cur->fd > maxfd) - maxfd = cur->fd; - } - faim_mutex_unlock(&sess->connlistlock); - - /* - * If we have data waiting to be sent, return - * - * We have to not do this if theres at least one - * connection thats still connecting, since that connection - * may have queued data and this return would prevent - * the connection from ever completing! This is a major - * inadequacy of the libfaim way of doing things. It means - * that nothing can transmit as long as there's connecting - * sockets. Evil. - * - * But its still better than having blocking connects. - * - */ - if (!haveconnecting && (sess->queue_outgoing != NULL)) { - *status = 1; - return NULL; - } - - if ((i = select(maxfd+1, &fds, &wfds, NULL, timeout))>=1) { - faim_mutex_lock(&sess->connlistlock); - for (cur = sess->connlist; cur; cur = cur->next) { - if ((FD_ISSET(cur->fd, &fds)) || - ((cur->status & AIM_CONN_STATUS_INPROGRESS) && - FD_ISSET(cur->fd, &wfds))) { - *status = 2; - faim_mutex_unlock(&sess->connlistlock); - return cur; /* XXX race condition here -- shouldnt unlock connlist */ - } - } - *status = 0; /* shouldn't happen */ - } else if ((i == -1) && (errno == EINTR)) /* treat interrupts as a timeout */ - *status = 0; - else - *status = i; /* can be 0 or -1 */ - - faim_mutex_unlock(&sess->connlistlock); - return NULL; /* no waiting or error, return */ -} - -/** - * aim_conn_isready - Test if a connection is marked ready - * @conn: Connection to test - * - * Returns true if the connection is ready, false otherwise. - * Returns -1 if the connection is invalid. - * - * XXX: This is deprecated. - * - */ -faim_export int aim_conn_isready(struct aim_conn_t *conn) -{ - if (conn) - return (conn->status & 0x0001); - return -1; -} - -/** - * aim_conn_setstatus - Set the status of a connection - * @conn: Connection - * @status: New status - * - * @newstatus is %XOR'd with the previous value of the connection - * status and returned. Returns -1 if the connection is invalid. - * - * This isn't real useful. - * - */ -faim_export int aim_conn_setstatus(struct aim_conn_t *conn, int status) -{ - int val; - - if (!conn) - return -1; - - faim_mutex_lock(&conn->active); - val = conn->status ^= status; - faim_mutex_unlock(&conn->active); - return val; -} - -/** - * aim_conn_setlatency - Set a forced latency value for connection - * @conn: Conn to set latency for - * @newval: Number of seconds to force between transmits - * - * Causes @newval seconds to be spent between transmits on a connection. - * - * This is my lame attempt at overcoming not understanding the rate - * limiting. - * - * XXX: This should really be replaced with something that scales and - * backs off like the real rate limiting does. - * - */ -faim_export int aim_conn_setlatency(struct aim_conn_t *conn, int newval) -{ - if (!conn) - return -1; - - faim_mutex_lock(&conn->active); - conn->forcedlatency = newval; - conn->lastactivity = 0; /* reset this just to make sure */ - faim_mutex_unlock(&conn->active); - - return 0; -} - -/** - * aim_setupproxy - Configure a proxy for this session - * @sess: Session to set proxy for - * @server: SOCKS server - * @username: SOCKS username - * @password: SOCKS password - * - * Call this with your SOCKS5 proxy server parameters before - * the first call to aim_newconn(). If called with all %NULL - * args, it will clear out a previously set proxy. - * - * Set username and password to %NULL if not applicable. - * - */ -faim_export void aim_setupproxy(struct aim_session_t *sess, char *server, char *username, char *password) -{ - /* clear out the proxy info */ - if (!server || !strlen(server)) { - memset(sess->socksproxy.server, 0, sizeof(sess->socksproxy.server)); - memset(sess->socksproxy.username, 0, sizeof(sess->socksproxy.username)); - memset(sess->socksproxy.password, 0, sizeof(sess->socksproxy.password)); - return; - } - - strncpy(sess->socksproxy.server, server, sizeof(sess->socksproxy.server)); - if (username && strlen(username)) - strncpy(sess->socksproxy.username, username, sizeof(sess->socksproxy.username)); - if (password && strlen(password)) - strncpy(sess->socksproxy.password, password, sizeof(sess->socksproxy.password)); - return; -} - -/** - * aim_session_init - Initializes a session structure - * @sess: Session to initialize - * @flags: Flags to use. Any of %AIM_SESS_FLAGS %OR'd together. - * - * Sets up the initial values for a session. - * - */ -faim_export void aim_session_init(struct aim_session_t *sess, unsigned long flags) -{ - if (!sess) - return; - - memset(sess, 0, sizeof(struct aim_session_t)); - aim_connrst(sess); - sess->queue_outgoing = NULL; - sess->queue_incoming = NULL; - sess->pendingjoin = NULL; - sess->pendingjoinexchange = 0; - aim_initsnachash(sess); - sess->msgcookies = NULL; - sess->snac_nextid = 0x00000001; - - sess->flags = 0; - - /* - * Default to SNAC login unless XORLOGIN is explicitly set. - */ - if (!(flags & AIM_SESS_FLAGS_XORLOGIN)) - sess->flags |= AIM_SESS_FLAGS_SNACLOGIN; - sess->flags |= flags; - - /* - * This must always be set. Default to the queue-based - * version for back-compatibility. - */ - sess->tx_enqueue = &aim_tx_enqueue__queuebased; - - return; -} - -/** - * aim_conn_isconnecting - Determine if a connection is connecting - * @conn: Connection to examine - * - * Returns nonzero if the connection is in the process of - * connecting (or if it just completed and aim_conn_completeconnect() - * has yet to be called on it). - * - */ -faim_export int aim_conn_isconnecting(struct aim_conn_t *conn) -{ - if (!conn) - return 0; - return (conn->status & AIM_CONN_STATUS_INPROGRESS)?1:0; -} - -faim_export int aim_conn_completeconnect(struct aim_session_t *sess, struct aim_conn_t *conn) -{ - fd_set fds, wfds; - struct timeval tv; - int res, error = ETIMEDOUT; - rxcallback_t userfunc; - - if (!conn || (conn->fd == -1)) - return -1; - - if (!(conn->status & AIM_CONN_STATUS_INPROGRESS)) - return -1; - - FD_ZERO(&fds); - FD_SET(conn->fd, &fds); - FD_ZERO(&wfds); - FD_SET(conn->fd, &wfds); - tv.tv_sec = 0; - tv.tv_usec = 0; - - if ((res = select(conn->fd+1, &fds, &wfds, NULL, &tv)) == -1) { - error = errno; - aim_conn_close(conn); - errno = error; - return -1; - } else if (res == 0) { - printf("faim: aim_conn_completeconnect: false alarm on %d\n", conn->fd); - return 0; /* hasn't really completed yet... */ - } - - if (FD_ISSET(conn->fd, &fds) || FD_ISSET(conn->fd, &wfds)) { - int len = sizeof(error); - - if (getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) - error = errno; - } - - if (error) { - aim_conn_close(conn); - errno = error; - return -1; - } - - fcntl(conn->fd, F_SETFL, 0); /* XXX should restore original flags */ - - conn->status &= ~AIM_CONN_STATUS_INPROGRESS; - - if ((userfunc = aim_callhandler(conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE))) - userfunc(sess, NULL, conn); - - /* Flush out the queues if there was something waiting for this conn */ - aim_tx_flushqueue(sess); - - return 0; -}
--- a/libfaim/aim_ft.c Sun Mar 04 22:37:18 2001 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1847 +0,0 @@ -#include <faim/aim.h> - -#ifndef _WIN32 -#include <netdb.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <sys/utsname.h> /* for aim_directim_initiate */ -#include <arpa/inet.h> /* for inet_ntoa */ -#endif - -/* aim_msgcookies.c is mostly new. just look at the diff and replace yours, easiest. */ - -/* - function name where i had it - aim_send_im_direct aim_im.c - aim_directim_initiate aim_im.c - aim_filetransfer_accept aim_im.c - aim_getlisting aim_misc.c (?!) -- prototype function. can be ignored. - establish aim_misc.c - aim_get_command_rendezvous aim_r - oft_getfh aim_rxqueue.c -*/ - -faim_export int aim_handlerendconnect(struct aim_session_t *sess, struct aim_conn_t *cur) -{ - int acceptfd = 0; - rxcallback_t userfunc; - struct sockaddr cliaddr; - size_t clilen = sizeof(cliaddr); - int ret = 0; - - /* - * Listener sockets only have incoming connections. No data. - */ - if( (acceptfd = accept(cur->fd, &cliaddr, &clilen)) == -1) - return -1; - - if (cliaddr.sa_family != AF_INET) /* just in case IPv6 really is happening */ - return -1; - - switch(cur->subtype) { - case AIM_CONN_SUBTYPE_OFT_DIRECTIM: { - struct aim_directim_priv *priv; - - priv = (struct aim_directim_priv *)calloc(1, sizeof(struct aim_directim_priv)); - - snprintf(priv->ip, sizeof(priv->ip), "%s:%u", inet_ntoa(((struct sockaddr_in *)&cliaddr)->sin_addr), ntohs(((struct sockaddr_in *)&cliaddr)->sin_port)); - - if(!cur->priv) - cur->priv = priv; /* what happens if there is one?! -- mid */ - - cur->type = AIM_CONN_TYPE_RENDEZVOUS; - close(cur->fd); /* should we really do this? seems like the client should decide. maybe clone the connection and keep the listener open. -- mid */ - cur->fd = acceptfd; - - if ( (userfunc = aim_callhandler(cur, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINITIATE))) - ret = userfunc(sess, NULL, cur); - - break; - } - case AIM_CONN_SUBTYPE_OFT_GETFILE: { - struct aim_filetransfer_priv *priv; - - priv = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv)); - - snprintf(priv->ip, sizeof(priv->ip), "%s:%u", inet_ntoa(((struct sockaddr_in *)&cliaddr)->sin_addr), ntohs(((struct sockaddr_in *)&cliaddr)->sin_port)); - - if(!cur->priv) - cur->priv = priv; - - if ( (userfunc = aim_callhandler(cur, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEINITIATE))) - ret = userfunc(sess, NULL, cur); - break; - } - default: { - /* XXX */ - } - } - return ret; -} - - -/* - * aim_send_im_direct: - * sess - session - * conn - directim connection - * msg - null-terminated string to send - */ - -faim_export int aim_send_im_direct(struct aim_session_t *sess, - struct aim_conn_t *conn, - char *msg) -{ - struct command_tx_struct *newpacket , *newpacket2; - - /* newpacket contains a real header with data, newpacket2 is just a - null packet, with a cookie and a lot of 0x00s. newpacket is the - "i'm sending", newpacket2 is the "i'm typing".*/ - - /* uhm. the client should send those as two seperate things -- mid */ - - struct aim_directim_priv *priv = NULL; - int i; - - if (!sess || !conn || !(conn->type) || (conn->type != AIM_CONN_TYPE_RENDEZVOUS) || !conn->priv || !msg) { - printf("faim: directim: invalid arguments\n"); - return -1; - }; - - if (strlen(msg) >= MAXMSGLEN) - return -1; - - priv = (struct aim_directim_priv *)conn->priv; - - /* NULLish Header */ - - if (!(newpacket2 = aim_tx_new(AIM_FRAMETYPE_OFT, 0x0001, conn, 0))) { - printf("faim: directim: tx_new2 failed\n"); - return -1; - } - - newpacket2->lock = 1; /* lock struct */ - - memcpy(newpacket2->hdr.oft.magic, "ODC2", 4); - newpacket2->hdr.oft.hdr2len = 0x44; - - if (!(newpacket2->hdr.oft.hdr2 = calloc(1,newpacket2->hdr.oft.hdr2len))) { - free(newpacket2); - return -1; - } - - i = 0; - i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0006); - i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000); - - i += aimutil_putstr(newpacket2->hdr.oft.hdr2+i, (char *)priv->cookie, 8); - - i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000); - i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000); - i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000); - i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000); - - i += aimutil_put32(newpacket2->hdr.oft.hdr2+i, 0x00000000); - - i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000); - i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000); - i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000); - - i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x000e); - - i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000); - i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000); - - i += aimutil_putstr(newpacket2->hdr.oft.hdr2+i, sess->sn, strlen(sess->sn)); - - i = 52; /* 0x34 */ - i += aimutil_put8(newpacket2->hdr.oft.hdr2+i, 0x00); /* 53 */ - i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000); /* 55 */ - i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000); - i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000); - i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);/* 61 */ - i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000); - i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);/* 65 */ - i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);/* end of hdr2 */ - - newpacket2->lock = 0; - newpacket2->data = NULL; - - aim_tx_enqueue(sess, newpacket2); - - /* Header packet */ - - if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OFT, 0x0001, conn, strlen(msg)))) { - printf("faim: directim: tx_new failed\n"); - return -1; - } - - newpacket->lock = 1; /* lock struct */ - - memcpy(newpacket->hdr.oft.magic, "ODC2", 4); - newpacket->hdr.oft.hdr2len = 0x54; - - if (!(newpacket->hdr.oft.hdr2 = calloc(1,newpacket->hdr.oft.hdr2len))) { - free(newpacket); - return -1; - } - - i = 0; - i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0006); - i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); - - i += aimutil_putstr(newpacket->hdr.oft.hdr2+i, (char *)priv->cookie, 8); - - i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); - i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); - i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); - i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); - - i += aimutil_put32(newpacket->hdr.oft.hdr2+i, strlen(msg)); - - i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); - i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); - i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); - i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); - i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); - i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); - - i += aimutil_putstr(newpacket->hdr.oft.hdr2+i, sess->sn, strlen(sess->sn)); - - i = 52; /* 0x34 */ - i += aimutil_put8(newpacket->hdr.oft.hdr2+i, 0x00); /* 53 */ - i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); /* 55 */ - i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); - i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); - i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);/* 61 */ - i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); - i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);/* 65 */ - i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);/* end of hdr2 */ - - /* values grabbed from a dump */ - i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0008); /* 69 */ - i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x000c); - i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);/* 71 */ - i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x1466);/* 73 */ - i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0001);/* 73 */ - i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x2e0f); - i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x393e); - i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0xcac8); - - memcpy(newpacket->data, msg, strlen(msg)); - - newpacket->lock = 0; - - aim_tx_enqueue(sess, newpacket); - - return 0; -} - -/* - * aim_directim_intitiate: - * For those times when we want to open up the directim channel ourselves. - * sess is your session, - * conn is the BOS conn, - * priv is a dummy priv value (we'll let it get filled in later) (if - * you pass a NULL, we alloc one) - * destsn is the SN to connect to. - */ - - -faim_export struct aim_conn_t *aim_directim_initiate(struct aim_session_t *sess, - struct aim_conn_t *conn, - struct aim_directim_priv *priv, - char *destsn) -{ - struct command_tx_struct *newpacket; - struct aim_conn_t *newconn; - - struct aim_msgcookie_t *cookie; - - int curbyte, i, listenfd; - short port = 4443; - - struct hostent *hptr; - char localhost[129]; - - unsigned char cap[16]; - char d[4]; /* XXX: IPv6. *cough* */ - - /* - * Open our socket - */ - - if( (listenfd = aim_listenestablish(port)) == -1) - return NULL; - - /* - * get our local IP - */ - - if(gethostname(localhost, 128) < 0) - return NULL; - - if( (hptr = gethostbyname(localhost)) == NULL) - return NULL; - - memcpy(&d, hptr->h_addr_list[0], 4); /* XXX: this probably isn't quite kosher, but it works */ - - aim_putcap(cap, 16, AIM_CAPS_IMIMAGE); - - /* - * create the OSCAR packet - */ - - if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+8+2+1+strlen(destsn)+4+4+0x32))) - return NULL; - - newpacket->lock = 1; /* lock struct */ - - curbyte = 0; - curbyte += aim_putsnac(newpacket->data+curbyte, - 0x0004, 0x0006, 0x0000, sess->snac_nextid); - - /* - * Generate a random message cookie - * This cookie needs to be alphanumeric and NULL-terminated to be TOC-compatible. - */ - for (i=0;i<7;i++) - curbyte += aimutil_put8(newpacket->data+curbyte, 0x30 + ((u_char) rand() % 20)); - curbyte += aimutil_put8(newpacket->data+curbyte, 0x00); - - /* - * grab all the data for cookie caching. - */ - cookie = (struct aim_msgcookie_t *)calloc(1, sizeof(struct aim_msgcookie_t)); - - memcpy(cookie->cookie, newpacket->data+curbyte-8, 8); - cookie->type = AIM_COOKIETYPE_OFTIM; - - if(!priv) - priv = (struct aim_directim_priv *)calloc(1, sizeof(struct aim_directim_priv)); - - memcpy(priv->cookie, cookie, 8); - memcpy(priv->sn, destsn, sizeof(priv->sn)); - - cookie->data = priv; - - aim_cachecookie(sess, cookie); /* cache da cookie */ - - /* - * Channel ID - */ - curbyte += aimutil_put16(newpacket->data+curbyte,0x0002); - - /* - * Destination SN (prepended with byte length) - */ - curbyte += aimutil_put8(newpacket->data+curbyte,strlen(destsn)); - curbyte += aimutil_putstr(newpacket->data+curbyte, destsn, strlen(destsn)); - - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); - - /* - * enTLV start - */ - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0032); - - /* - * Flag data / ICBM Parameters? - */ - curbyte += aimutil_put8(newpacket->data+curbyte, 0x00); - curbyte += aimutil_put8(newpacket->data+curbyte, 0x00); - - /* - * Cookie - */ - curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cookie, 8); - - /* - * Capability String - */ - curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cap, 0x10); - - /* - * 000a/0002 : 0001 - */ - curbyte += aimutil_put16(newpacket->data+curbyte, 0x000a); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001); - - /* - * 0003/0004: IP address - */ - - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0004); - - for(i = 0; i < 4; i++) - curbyte += aimutil_put8(newpacket->data+curbyte, d[i]); /* already in network byte order */ - - /* - * 0005/0002: Port - */ - - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002); - curbyte += aimutil_put16(newpacket->data+curbyte, port); - - /* - * 000f/0000: umm.. dunno. Zigamorph[1]? - * [1]: see esr's TNHD. - */ - - curbyte += aimutil_put16(newpacket->data+curbyte, 0x000f); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); - - printf("curbyte: 0x%x\n",curbyte); - - newpacket->commandlen = curbyte; - newpacket->lock = 0; - - aim_tx_enqueue(sess, newpacket); - - /* - * allocate and set up our connection - */ - - newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS_OUT, NULL); - if (!newconn) { - perror("aim_newconn"); - aim_conn_kill(sess, &newconn); - return NULL; - } - - newconn->fd = listenfd; - newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM; - newconn->priv = priv; - printf("faim: listening (fd = %d, unconnected)\n", newconn->fd); - - /* - * XXX We need some way of closing the listener socket after - * n seconds of no connection. -- mid - */ - - return (newconn); -} - -/* - * struct aim_conn_t *aim_directim_connect(struct aim_session_t *sess, struct aim_conn_t *conn, struct aim_directim_priv *priv) - * sess is the session to append the conn to, - * conn is the BOS connection, - * priv is the filled-in priv data structure for the connection - * returns conn if connected, NULL on error - */ - - -faim_export struct aim_conn_t *aim_directim_connect(struct aim_session_t *sess, - struct aim_conn_t *conn, - struct aim_directim_priv *priv ) -{ - struct aim_conn_t *newconn = NULL;; - - newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS, priv->ip); - if (!newconn || (newconn->fd == -1)) { - printf("could not connect to %s\n", priv->ip); - perror("aim_newconn"); - aim_conn_kill(sess, &newconn); - return NULL; - } else { - newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM; - newconn->priv = priv; - printf("faim: connected to peer (fd = %d)\n", newconn->fd); - return newconn; - } - return newconn; -} - -/* - * struct aim_conn_t *aim_directim_getconn(struct aim_session_t *sess, const char *name) - * sess is your session, - * name is the name to get, - * returns conn for directim with name, NULL if none found. - */ - -faim_export struct aim_conn_t *aim_directim_getconn(struct aim_session_t *sess, const char *name) -{ - struct aim_conn_t *cur; - struct aim_directim_priv *priv; - - faim_mutex_lock(&sess->connlistlock); - for (cur = sess->connlist; cur; cur = cur->next) { - if (cur->type != AIM_CONN_TYPE_RENDEZVOUS || cur->subtype != AIM_CONN_SUBTYPE_OFT_DIRECTIM) - continue; - - priv = cur->priv; - if (aim_sncmp(priv->sn, name) == 0) - break; - } - faim_mutex_unlock(&sess->connlistlock); - - return cur; -} - -/* - * aim_accepttransfer: - * accept a file transfer request. - * sess is the session, - * conn is the BOS conn for the CAP reply - * sn is the screenname to send it to, - * cookie is the cookie used - * ip is the ip to connect to - * file is the listing file to use - * rendid is CAP type - * - * returns connection - */ - -faim_export struct aim_conn_t *aim_accepttransfer(struct aim_session_t *sess, - struct aim_conn_t *conn, - char *sn, - char *cookie, - char *ip, - FILE *file, - unsigned short rendid) -{ - struct command_tx_struct *newpacket, *newoft; - - struct aim_conn_t *newconn; - struct aim_fileheader_t *listingfh; - struct aim_filetransfer_priv *priv; - struct aim_msgcookie_t *cachedcook; - - int curbyte, i; - - if(rendid == AIM_CAPS_GETFILE) { - - newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS, ip); - - newconn->subtype = AIM_CONN_SUBTYPE_OFT_GETFILE; - - if (!newconn || (newconn->fd == -1)) { - perror("aim_newconn"); - faimdprintf(2, "could not connect to %s (fd: %i)\n", ip, newconn?newconn->fd:0); - aim_conn_kill(sess, &newconn); - return NULL; - } else { - priv = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv)); - memcpy(priv->cookie, cookie, 8); - strncpy(priv->sn, sn, MAXSNLEN); - strncpy(priv->ip, ip, sizeof(priv->ip)); - newconn->priv = (void *)priv; - faimdprintf(2, "faim: connected to peer (fd = %d)\n", newconn->fd); - } - - /* cache da cookie. COOOOOKIES! */ - - if(!(cachedcook = (struct aim_msgcookie_t *)calloc(1, sizeof(struct aim_msgcookie_t)))) { - faimdprintf(1, "faim: accepttransfer: couldn't calloc cachedcook. yeep!\n"); - /* XXX die here, or go on? search for cachedcook for future references */ - } - - if(cachedcook) - memcpy(cachedcook->cookie, cookie, 8); - - /* strncpy(ft->fh.name, miscinfo->value+8, sizeof(ft->fh.name)); */ - - if(cachedcook) { /* see above calloc of cachedcook */ - cachedcook->type = AIM_COOKIETYPE_OFTGET; - cachedcook->data = (void *)priv; - - if (aim_cachecookie(sess, cachedcook) != 0) - faimdprintf(1, "faim: ERROR caching message cookie\n"); - } - - if(rendid == AIM_CAPS_GETFILE) { - faimdprintf(2, "faim: getfile request accept\n"); - if(!(newoft = aim_tx_new(AIM_FRAMETYPE_OFT, 0x1108, newconn, 0))) { - faimdprintf(2, "faim: aim_accepttransfer: tx_new OFT failed\n"); - /* XXX: what else do we need to clean up here? */ - return NULL; - } - - newoft->lock = 1; - - memcpy(newoft->hdr.oft.magic, "OFT2", 4); - newoft->hdr.oft.hdr2len = 0xf8; /* 0x100 - 8 */ - - if(!(listingfh = aim_getlisting(file))) { - return NULL; - } - - if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) { - free(newoft); - return NULL; - } - - memcpy(listingfh->bcookie, cookie, 8); - - aim_oft_buildheader((void *)newoft->hdr.oft.hdr2, listingfh); - - free(listingfh); - - newoft->lock = 0; - aim_tx_enqueue(sess, newoft); - printf("faim: getfile: OFT listing enqueued.\n"); - - } - - /* OSCAR CAP accept packet */ - - if(!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+8+2+1+strlen(sn)+4+2+8+16))) - return NULL; - - newpacket->lock = 1; - - curbyte = aim_putsnac(newpacket->data, 0x0004, 0x0006, 0x0000, sess->snac_nextid); - for (i = 0; i < 8; i++) - curbyte += aimutil_put8(newpacket->data+curbyte, cookie[i]); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002); - curbyte += aimutil_put8(newpacket->data+curbyte, strlen(sn)); - curbyte += aimutil_putstr(newpacket->data+curbyte, sn, strlen(sn)); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x001a); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002 /* accept */); - for (i = 0; i < 8; i++) - curbyte += aimutil_put8(newpacket->data+curbyte, cookie[i]); - curbyte += aim_putcap(newpacket->data+curbyte, 0x10, rendid); - - newpacket->lock = 0; - aim_tx_enqueue(sess, newpacket); - - return newconn; - } - return NULL; -} - -/* - * aim_getlisting(FILE *file) - * - * file is an opened listing file - * returns a pointer to the filled-in fileheader_t - * - * currently omits checksum. we'll fix this when AOL breaks us, i - * guess. - * - */ - -faim_internal struct aim_fileheader_t *aim_getlisting(FILE *file) -{ - struct aim_fileheader_t *fh; - u_long totsize = 0, size = 0, checksum = 0xffff0000; - short totfiles = 0; - char *linebuf, sizebuf[9]; - - int linelength = 1024; - - /* XXX: if we have a line longer than 1024chars, God help us. */ - if( (linebuf = (char *)calloc(1, linelength)) == NULL ) { - faimdprintf(2, "linebuf calloc failed\n"); - return NULL; - } - - if(fseek(file, 0, SEEK_END) == -1) { /* use this for sanity check */ - perror("getlisting END1 fseek:"); - faimdprintf(2, "getlising fseek END1 error\n"); - } - - if(fgetpos(file, &size) == -1) { - perror("getlisting END1 getpos:"); - faimdprintf(2, "getlising getpos END1 error\n"); - } - - if(fseek(file, 0, SEEK_SET) != 0) { - perror("getlesting fseek(SET):"); - faimdprintf(2, "faim: getlisting: couldn't seek to beginning of listing file\n"); - } - - bzero(linebuf, linelength); - - size = 0; - - while(fgets(linebuf, linelength, file)) { - totfiles++; - bzero(sizebuf, 9); - - size += strlen(linebuf); - - if(strlen(linebuf) < 23) { - faimdprintf(2, "line \"%s\" too short. skipping\n", linebuf); - continue; - } - if(linebuf[strlen(linebuf)-1] != '\n') { - faimdprintf(2, "faim: OFT: getlisting -- hit EOF or line too long!\n"); - } - - memcpy(sizebuf, linebuf+17, 8); - - totsize += strtol(sizebuf, NULL, 10); - bzero(linebuf, linelength); - } - - /* if(size != 0) { - faimdprintf(2, "faim: getlisting: size != 0 after while.. %i\n", size); - }*/ - - if(fseek(file, 0, SEEK_SET) == -1) { - perror("getlisting END2 fseek:"); - faimdprintf(2, "getlising fseek END2 error\n"); - } - - free(linebuf); - - /* we're going to ignore checksumming the data for now -- that - * requires walking the whole listing.txt. it should probably be - * done at register time and cached, but, eh. */ - - if(!(fh = (struct aim_fileheader_t*)calloc(1, sizeof(struct aim_fileheader_t)))) - return NULL; - - printf( "faim: OFT: getlisting: totfiles: %u, totsize: %lu, size: %lu\n", totfiles, totsize, size); - - fh->encrypt = 0x0000; - fh->compress = 0x0000; - fh->totfiles = totfiles; - fh->filesleft = totfiles; /* is this right ?*/ - fh->totparts = 0x0001; - fh->partsleft = 0x0001; - fh->totsize = totsize; - fh->size = size; /* ls -l listing.txt */ - fh->modtime = (int)time(NULL); /* we'll go with current time for now */ - fh->checksum = checksum; /* XXX: checksum ! */ - fh->rfcsum = 0x00000000; - fh->rfsize = 0x00000000; - fh->cretime = 0x00000000; - fh->rfcsum = 0x00000000; - fh->nrecvd = 0x00000000; - fh->recvcsum = 0x00000000; - - /* memset(fh->idstring, 0, sizeof(fh->idstring)); */ - memcpy(fh->idstring, "OFT_Windows ICBMFT V1.1 32", sizeof(fh->idstring)); - memset(fh->idstring+strlen(fh->idstring), 0, sizeof(fh->idstring)-strlen(fh->idstring)); - - fh->flags = 0x02; - fh->lnameoffset = 0x1a; - fh->lsizeoffset = 0x10; - - /* memset(fh->dummy, 0, sizeof(fh->dummy)); */ - memset(fh->macfileinfo, 0, sizeof(fh->macfileinfo)); - - fh->nencode = 0x0000; /* we need to figure out these encodings for filenames */ - fh->nlanguage = 0x0000; - - /* memset(fh->name, 0, sizeof(fh->name)); */ - memcpy(fh->name, "listing.txt", sizeof(fh->name)); - memset(fh->name+strlen(fh->name), 0, 64-strlen(fh->name)); - - faimdprintf(2, "faim: OFT: listing fh name %s / %s\n", fh->name, (fh->name+(strlen(fh->name)))); - return fh; -} - -/* - * establish: create a listening socket on a port. you need to call - * accept() when it's connected. - * portnum is the port number to bind to. - * returns your fd - */ - -faim_internal int aim_listenestablish(u_short portnum) -{ -#if defined(__linux__) /* XXX what other OS's support getaddrinfo? */ - int listenfd; - const int on = 1; - struct addrinfo hints, *res, *ressave; - char serv[5]; - sprintf(serv, "%d", portnum); - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_flags = AI_PASSIVE; - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - if (getaddrinfo(NULL/*any IP*/, serv, &hints, &res) != 0) { - perror("getaddrinfo"); - return -1; - } - ressave = res; - do { - listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); - if (listenfd < 0) - continue; - setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - if (bind(listenfd, res->ai_addr, res->ai_addrlen) == 0) - break; /* success */ - close(listenfd); - } while ( (res = res->ai_next) ); - if (!res) - return -1; - if (listen(listenfd, 1024)!=0) { - perror("listen"); - return -1; - } - freeaddrinfo(ressave); - return listenfd; -#else - int listenfd; - const int on = 1; - struct sockaddr_in sockin; - - if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - perror("socket(listenfd)"); - return -1; - } - if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on) != 0)) { - perror("setsockopt(listenfd)"); - close(listenfd); - return -1; - } - memset(&sockin, 0, sizeof(struct sockaddr_in)); - sockin.sin_family = AF_INET; - sockin.sin_port = htons(portnum); - if (bind(listenfd, (struct sockaddr *)&sockin, sizeof(struct sockaddr_in)) != 0) { - perror("bind(listenfd)"); - close(listenfd); - return -1; - } - if (listen(listenfd, 4) != 0) { - perror("listen(listenfd)"); - close(listenfd); - return -1; - } - - return listenfd; -#endif -} - -faim_internal int aim_get_command_rendezvous(struct aim_session_t *sess, struct aim_conn_t *conn) -{ - unsigned char hdrbuf1[6]; - unsigned char *hdr = NULL; - int hdrlen, hdrtype; - int flags = 0; - rxcallback_t userfunc = NULL; - - - memset(hdrbuf1, 0, sizeof(hdrbuf1)); - - faim_mutex_lock(&conn->active); /* gets locked down for the entirety */ - - if ( (hdrlen = aim_recv(conn->fd, hdrbuf1, 6)) < 6) { - - faimdprintf(2, "faim: rend: read error (fd: %i) %02x%02x%02x%02x%02x%02x (%i)\n", conn->fd, hdrbuf1[0],hdrbuf1[1],hdrbuf1[0],hdrbuf1[0],hdrbuf1[0],hdrbuf1[0],hdrlen); - faim_mutex_unlock(&conn->active); - - if(hdrlen < 0) - perror("read"); - else { /* disconnected */ - switch(conn->subtype) { - case AIM_CONN_SUBTYPE_OFT_DIRECTIM: { /* XXX: clean up cookies here ? */ - struct aim_directim_priv *priv = NULL; - if(!(priv = (struct aim_directim_priv *)conn->priv) ) - return -1; /* not much we can do */ - aim_uncachecookie(sess, priv->cookie, AIM_COOKIETYPE_OFTIM); - - - if ( (userfunc = aim_callhandler(conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMDISCONNECT)) ) { - aim_conn_close(conn); - return userfunc(sess, NULL, conn, priv->sn); - } - - break; - } - - case AIM_CONN_SUBTYPE_OFT_GETFILE: { - struct aim_filetransfer_priv *priv; - if(!(priv = (struct aim_filetransfer_priv *)conn->priv)) - return -1; - - aim_uncachecookie(sess, priv->cookie, AIM_COOKIETYPE_OFTGET); - - if ( (userfunc = aim_callhandler(conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEDISCONNECT)) ) { - aim_conn_close(conn); - return userfunc(sess, NULL, conn, priv->sn); - } - - break; - } - - case AIM_CONN_SUBTYPE_OFT_SENDFILE: { - struct aim_filetransfer_priv *priv; - if(!(priv = (struct aim_filetransfer_priv *)conn->priv)) - return -1; - - aim_uncachecookie(sess, priv->cookie, AIM_COOKIETYPE_OFTSEND); - - if ( (userfunc = aim_callhandler(conn, AIM_CB_FAM_OFT, AIM_CB_OFT_SENDFILEDISCONNECT)) ) { - aim_conn_close(conn); - return userfunc(sess, NULL, conn, priv->sn); - } - - break; - } - } - - aim_conn_close(conn); - aim_conn_kill(sess, &conn); - - return -1; - } - } - - hdrlen = aimutil_get16(hdrbuf1+4); - - hdrlen -= 6; - if (!(hdr = malloc(hdrlen))) { - faim_mutex_unlock(&conn->active); - return -1; - } - - if (aim_recv(conn->fd, hdr, hdrlen) < hdrlen) { - perror("read"); - faimdprintf(2,"faim: rend: read2 error\n"); - free(hdr); - faim_mutex_unlock(&conn->active); - aim_conn_close(conn); - return -1; - } - - hdrtype = aimutil_get16(hdr); - - switch (hdrtype) { - case 0x0001: { /* directim */ - int payloadlength = 0; - char *snptr = NULL; - struct aim_directim_priv *priv; - int i; - - priv = (struct aim_directim_priv *)calloc(1, sizeof(struct aim_directim_priv)); - - payloadlength = aimutil_get32(hdr+22); - flags = aimutil_get16(hdr+32); - snptr = (char *)hdr+38; - - strncpy(priv->sn, snptr, MAXSNLEN); - - faimdprintf(2, "faim: OFT frame: %04x / %04x / %04x / %s\n", hdrtype, payloadlength, flags, snptr); - - if (flags == 0x000e) { - faim_mutex_unlock(&conn->active); - if ( (userfunc = aim_callhandler(conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING)) ) - return userfunc(sess, NULL, snptr); - } else if ((flags == 0x0000) && payloadlength) { - unsigned char *msg; - - if(! (msg = calloc(1, payloadlength+1)) ) { - faim_mutex_unlock(&conn->active); - return -1; - } - - if (aim_recv(conn->fd, msg, payloadlength) < payloadlength) { - perror("read"); - printf("faim: rend: read3 error\n"); - free(msg); - faim_mutex_unlock(&conn->active); - aim_conn_close(conn); - return -1; - } - faim_mutex_unlock(&conn->active); - msg[payloadlength] = '\0'; - faimdprintf(2, "faim: directim: %s/%04x/%04x/%s\n", snptr, payloadlength, flags, msg); - - - if ( (userfunc = aim_callhandler(conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING)) ) - i = userfunc(sess, NULL, conn, snptr, msg); - - free(msg); - return i; - } - break; - } -#if 0 - /* currently experimental to a non-compiling degree */ - case 0x1108: { /* getfile listing.txt incoming tx->rx */ - struct aim_filetransfer_priv *ft; - struct aim_fileheader_t *fh; - struct aim_msgcookie_t *cook; - struct aim_conn_type *newoft; - - int commandlen; - char *data; - - faimdprintf(2,"faim: rend: fileget 0x1108\n"); - - if(!(ft = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv)))) { - faimdprintf(2, "faim: couldn't malloc ft. um. bad. bad bad. file transfer will likely fail, sorry.\n"); - faim_mutex_unlock(&conn->active); - return -1; - } - - ft->state = 1; /* we're waaaaiiiting.. */ - - fh = aim_oft_getfh(hdr); - - memcpy(&(ft->fh), fh, sizeof(struct aim_fileheader_t)); - - if(!(cook = aim_checkcookie(sess, ft->fh.bcookie, AIM_COOKIETYPE_OFTGET))) { - faim_mutex_unlock(&conn->active); - return -1; - } - - if(cook->data) - free(cook->data); - - cook->data = ft; - - aim_cachecookie(sess, cook); - - if(!(newoft = aim_tx_new(AIM_FRAMETYPE_OFT, 0x1209, conn, 0))) { - /* XXX: what else do we need to clean up here? */ - faim_mutex_unlock(&conn->active); - return -1; - } - - - aim_oft_buildheader((void *)newoft->hdr.oft.hdr2, ft->fh); /* no change */ - newoft->lock = 0; - aim_tx_enqueue(sess, newoft); - break; - } -#endif - case 0x1209: { /* get file listing ack rx->tx */ - struct aim_filetransfer_priv *ft; - struct aim_fileheader_t *fh; - struct aim_msgcookie_t *cook; - - int commandlen; - char *data; - - faimdprintf(2,"faim: rend: fileget 0x1209\n"); - - if(!(ft = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv)))) { - faimdprintf(2, "faim: couldn't malloc ft. um. bad. bad bad. file transfer will likely fail, sorry.\n"); - faim_mutex_unlock(&conn->active); - return -1; - } - - fh = aim_oft_getfh(hdr); - - memcpy(&(ft->fh), fh, sizeof(struct aim_fileheader_t)); - - cook = aim_checkcookie(sess, ft->fh.bcookie, AIM_COOKIETYPE_OFTGET); - - /* we currently trust the other client to be giving us Valid - * Enough input, else this gets to be a messy function (and we - * won't break like winaim does when it gets bad input =) */ - - if(cook->data) - free(cook->data); /* XXX */ - - cook->data = ft; - - aim_cachecookie(sess, cook); - - /* XXX: have this send chunks of the file instead of the whole - * file. requires rethinking some code. */ - - if(fseek(sess->oft.listing, 0, SEEK_SET) != 0) { - perror("get_command_rendezvous 1209 fseek(SET):"); - faimdprintf(2, "faim: getlisting: couldn't seek to beginning of listing file\n"); - } - commandlen = ft->fh.size; - - if((data = (char *)calloc(1, commandlen)) == NULL) { - faimdprintf(2, "faim: get_command_rendezvous 1209: couldn't malloc data.\n"); - faim_mutex_unlock(&conn->active); - return -1; - - } - - for(commandlen = 0; commandlen < ft->fh.size; commandlen++) - if( (data[commandlen] = (unsigned char)fgetc(sess->oft.listing)) == EOF) - faimdprintf(2, "faim: get_command_rendezvous 1209: got early EOF (error?)\n"); - - commandlen = ft->fh.size; - - if (write(conn->fd, data, commandlen) != commandlen) { - perror("listing write error"); - } - faim_mutex_unlock(&conn->active); - - faimdprintf(2, "faim: get_command_rendezvous: hit end of 1209\n"); - - free(data); - - break; - } - case 0x120b: { /* get file second */ - struct aim_filetransfer_priv *ft; - struct aim_msgcookie_t *cook; - - struct aim_fileheader_t *fh; - - faimdprintf(2, "faim: rend: fileget 120b\n"); - - if(!(ft = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv)))) { - faimdprintf(2, "faim: couldn't malloc ft. um. bad. bad bad. file transfer will likely fail, sorry.\n"); - faim_mutex_unlock(&conn->active); - return -1; - } - - fh = aim_oft_getfh(hdr); - - memcpy(&(ft->fh), fh, sizeof(struct aim_fileheader_t)); - - if(!(cook = aim_checkcookie(sess, ft->fh.bcookie, AIM_COOKIETYPE_OFTGET))) { - faim_mutex_unlock(&conn->active); - return -1; - } - - if(cook->data) - free(cook->data); /* XXX: integrate cookie caching */ - - cook->data = ft; - - aim_cachecookie(sess, cook); - - faim_mutex_unlock(&conn->active); - - /* call listing.txt rx confirm */ - - break; - } - case 0x120c: { /* yet more get file */ - struct aim_filetransfer_priv *ft; - struct aim_msgcookie_t *cook; - struct aim_fileheader_t *listingfh; - struct command_tx_struct *newoft; - - int i; - - rxcallback_t userfunc; - - printf("faim: rend: fileget 120c\n"); - - if(!(ft = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv)))) { - printf("faim: couldn't malloc ft. um. bad. bad bad. file transfer will likely fail, sorry.\n"); - faim_mutex_unlock(&conn->active); - return -1; - } - - if(hdrlen != 0x100) - printf("faim: fileget_command(120c): um. hdrlen != 0x100..\n"); - - listingfh = aim_oft_getfh((char *)hdr); - - memcpy(&(ft->fh), listingfh, sizeof(struct aim_fileheader_t)); - - if(!(cook = aim_checkcookie(sess, ft->fh.bcookie, AIM_COOKIETYPE_OFTGET))) { - faim_mutex_unlock(&conn->active); - return -1; - } - - if(cook->data) - free(cook->data); /* XXX */ - - cook->data = ft; - - aim_cachecookie(sess, cook); - - faim_mutex_unlock(&conn->active); - - faimdprintf(2, "faim: fileget: %s seems to want %s\n", ft->sn, ft->fh.name); - - if( (userfunc = aim_callhandler(conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILEREQ)) ) - i = userfunc(sess, NULL, conn, &ft->fh, cook->cookie); - - if(i < 0) - return -1; - - if(!(newoft = aim_tx_new(AIM_FRAMETYPE_OFT, 0x0101, conn, 0))) { - faimdprintf(2, "faim: send_final_transfer: tx_new OFT failed\n"); - return -1; - } - - newoft->lock = 1; - - memcpy(newoft->hdr.oft.magic, "OFT2", 4); - newoft->hdr.oft.hdr2len = 0xf8; /* 0x100 - 8 */ - - if (!(newoft->hdr.oft.hdr2 = calloc(1,newoft->hdr.oft.hdr2len))) { - newoft->lock = 0; - aim_tx_destroy(newoft); - return -1; - } - - /* memcpy(listingfh->bcookie, ft->fh.bcookie, 8); */ - - listingfh->nrecvd = 0; /* these need reset for protocol-correctness */ - listingfh->recvcsum = 0; - - aim_oft_buildheader((void *)newoft->hdr.oft.hdr2, listingfh); - - newoft->lock = 0; - aim_tx_enqueue(sess, newoft); - faimdprintf(2, "faim: OFT: OFT file enqueued.\n"); - - if( (userfunc = aim_callhandler(conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILEREQ)) == NULL) - return 1; - - i = userfunc(sess, NULL, conn, listingfh, listingfh->bcookie); - - free(listingfh); - - return i; - - break; - } - case 0x0202: { /* get file: ready to recieve data */ - struct aim_fileheader_t *fh; - fh = aim_oft_getfh((char *)hdr); - - faimdprintf(2, "faim: get_rend: looks like we're ready to send data.(oft 0x0202)\n"); - - faim_mutex_unlock(&conn->active); - - if ( (userfunc = aim_callhandler(conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILESEND)) == NULL) - return 1; - - return userfunc(sess, NULL, conn, fh); - - free(fh); - - break; - } - case 0x0204: { /* get file: finished. close it up */ - int i; - - struct aim_fileheader_t *fh; - fh = aim_oft_getfh((char *)hdr); - - faimdprintf(2, "faim: get_rend: looks like we're done with a transfer (oft 0x0204)\n"); - - faim_mutex_unlock(&conn->active); - - if ( (userfunc = aim_callhandler(conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILECOMPLETE)) ) - i = userfunc(sess, NULL, conn, fh); - else - i = 1; - - /* - free(fh); */ - /* not sure where to do this yet, as we need to keep it to allow multiple file sends... bleh */ - - return i; - break; - } - default: { - printf("OFT frame: type %04x\n", hdrtype); - /* data connection may be unreliable here */ - faim_mutex_unlock(&conn->active); - break; - } - } /* switch */ - - free(hdr); - - return 0; -} - -/* - * aim_oft_registerlisting() - * sess: aim session - * file: opened FILE * - * listingdir: the path to listing.txt - * returns -1 on error, 0 on success. - * - * it's not my problem if the listing fd is already set. - */ - -faim_export int aim_oft_registerlisting(struct aim_session_t *sess, FILE *file, char* listingdir) -{ - if(!sess) - return -1; - - /* XXX: checksum each file in the listing */ - -#if 0 - if(sess->oft.listing) { - faimdprintf(1, "We already have a file pointer. Closing and overwriting.\n"); - fclose(sess->oft.listing); - } -#endif - sess->oft.listing = file; -#if 0 - if(sess->oft.listingdir) { - faimdprintf(1, "We already have a directory string. Freeing and overwriting\n"); - free(sess->oft.listingdir); - } -#endif - - if( (sess->oft.listingdir = (char *)calloc(1, strlen(listingdir)+1)) ) - memcpy(sess->oft.listingdir, listingdir, strlen(listingdir)); - else - return -1; - return 0; -} - -faim_internal struct aim_fileheader_t *aim_oft_getfh(unsigned char *hdr) -{ - struct aim_fileheader_t *fh; - int i, j; - - if(!(fh = calloc(1, sizeof(struct aim_fileheader_t)))) - return NULL; - - /* [0] and [1] are the type. we can ignore those here. */ - - i = 2; - - for(j = 0; j < 8; j++, i++) - fh->bcookie[j] = hdr[i]; - fh->encrypt = aimutil_get16(hdr+i); - i += 2; - fh->compress = aimutil_get16(hdr+i); - i += 2; - fh->totfiles = aimutil_get16(hdr+i); - i += 2; - fh->filesleft = aimutil_get16(hdr+i); - i += 2; - fh->totparts = aimutil_get16(hdr+i); - i += 2; - fh->partsleft = aimutil_get16(hdr+i); - i += 2; - fh->totsize = aimutil_get32(hdr+i); - i += 4; - fh->size = aimutil_get32(hdr+i); - i += 4; - fh->modtime = aimutil_get32(hdr+i); - i += 4; - fh->checksum = aimutil_get32(hdr+i); - i += 4; - fh->rfrcsum = aimutil_get32(hdr+i); - i += 4; - fh->rfsize = aimutil_get32(hdr+i); - i += 4; - fh->cretime = aimutil_get32(hdr+i); - i += 4; - fh->rfcsum = aimutil_get32(hdr+i); - i += 4; - fh->nrecvd = aimutil_get32(hdr+i); - i += 4; - fh->recvcsum = aimutil_get32(hdr+i); - i += 4; - - memcpy(fh->idstring, hdr+i, 32); - i += 32; - - fh->flags = aimutil_get8(hdr+i); - i += 1; - fh->lnameoffset = aimutil_get8(hdr+i); - i += 1; - fh->lsizeoffset = aimutil_get8(hdr+i); - i += 1; - - memcpy(fh->dummy, hdr+i, 69); - i += 69; - - memcpy(fh->macfileinfo, hdr+i, 16); - i += 16; - - fh->nencode = aimutil_get16(hdr+i); - i += 2; - fh->nlanguage = aimutil_get16(hdr+i); - i += 2; - - memcpy(fh->name, hdr+i, 64); - i += 64; - - return fh; -} - -faim_export int aim_oft_checksum(char *buffer, int bufsize, int *checksum) -{ - short check0, check1; - int i; - - check0 = ((*checksum & 0xFF000000) >> 16); - check1 = ((*checksum & 0x00ff0000) >> 16); - - for(i = 0; i < bufsize; i++) { - - if(i % 2) { /* use check1 -- second byte */ - if ( (short)buffer[i] > check1 ) { /* wrapping */ - - check1 += 0x100; /* this is a cheap way to wrap */ - - /* if we're wrapping, decrement the other one */ - if(check0 == 0) /* XXX: check this corner case */ - check0 = 0x00ff; - else - check0--; - } - - check1 -= buffer[i]; - } else { /* use check0 -- first byte */ - if ( (short)buffer[i] > check0 ) { /* wrapping */ - - check0 += 0x100; /* this is a cheap way to wrap */ - - /* if we're wrapping, decrement the other one */ - if(check1 == 0) /* XXX: check this corner case */ - check1 = 0x00ff; - else - check1--; - } - - check0 -= buffer[i]; - } - } - - if(check0 > 0xff || check1 > 0xff) { /* they shouldn't be able to do this. error! */ - faimdprintf(2, "check0 or check1 is too high: 0x%04x, 0x%04x\n", check0, check1); - return -1; - } - - check0 &= 0xff; /* grab just the lowest byte */ - check1 &= 0xff; /* this should be clean, but just in case */ - - *checksum = ((check0 * 0x1000000) + (check1 * 0x10000)); - - return *checksum; -} - - -/* - * aim_oft_buildheader: - * fills a buffer with network-order fh data. - * returns length written; -1 on error. - * dest: buffer to fill -- pre-alloced - * listingfh: fh to get data from - * - * DOES NOT DO BOUNDS CHECKING! - */ - - -faim_internal int aim_oft_buildheader(char *dest,struct aim_fileheader_t *listingfh) -{ - int i, curbyte; - - if(!dest || !listingfh) - return -1; - - curbyte = 0; - for(i = 0; i < 8; i++) - curbyte += aimutil_put8(dest+curbyte, listingfh->bcookie[i]); - curbyte += aimutil_put16(dest+curbyte, listingfh->encrypt); - curbyte += aimutil_put16(dest+curbyte, listingfh->compress); - curbyte += aimutil_put16(dest+curbyte, listingfh->totfiles); - curbyte += aimutil_put16(dest+curbyte, listingfh->filesleft); - curbyte += aimutil_put16(dest+curbyte, listingfh->totparts); - curbyte += aimutil_put16(dest+curbyte, listingfh->partsleft); - curbyte += aimutil_put32(dest+curbyte, listingfh->totsize); - curbyte += aimutil_put32(dest+curbyte, listingfh->size); - curbyte += aimutil_put32(dest+curbyte, listingfh->modtime); - curbyte += aimutil_put32(dest+curbyte, listingfh->checksum); - curbyte += aimutil_put32(dest+curbyte, listingfh->rfrcsum); - curbyte += aimutil_put32(dest+curbyte, listingfh->rfsize); - curbyte += aimutil_put32(dest+curbyte, listingfh->cretime); - curbyte += aimutil_put32(dest+curbyte, listingfh->rfcsum); - curbyte += aimutil_put32(dest+curbyte, listingfh->nrecvd); - curbyte += aimutil_put32(dest+curbyte, listingfh->recvcsum); - - memcpy(dest+curbyte, listingfh->idstring, 32); - curbyte += 32; - - curbyte += aimutil_put8(dest+curbyte, listingfh->flags); - curbyte += aimutil_put8(dest+curbyte, listingfh->lnameoffset); - curbyte += aimutil_put8(dest+curbyte, listingfh->lsizeoffset); - - memcpy(dest+curbyte, listingfh->dummy, 69); - curbyte += 69; - - memcpy(dest+curbyte, listingfh->macfileinfo, 16); - curbyte += 16; - - curbyte += aimutil_put16(dest+curbyte, listingfh->nencode); - curbyte += aimutil_put16(dest+curbyte, listingfh->nlanguage); - - memcpy(dest+curbyte, listingfh->name, 64); /* XXX: Filenames longer than 64B */ - curbyte += 64; - - return curbyte; -} - -/* - * int aim_getfile_send(struct aim_conn_t *conn, FILE *tosend, struct aim_fileheader_t *fh) - * conn is the OFT connection to shove the data down, - * tosend is the FILE * for the file to send - * fh is the filled-in fh value - * returns -1 on error, 1 on success. - */ - -faim_export int aim_getfile_send(struct aim_conn_t *conn, FILE *tosend, struct aim_fileheader_t *fh) -{ - int pos, bufpos, i; - const int bufsize = 4096; - char *buf; - - /* sanity checks */ - if(conn->type != AIM_CONN_TYPE_RENDEZVOUS || conn->subtype != AIM_CONN_SUBTYPE_OFT_GETFILE) { - faimdprintf(1, "getfile_send: conn->type(0x%04x) != RENDEZVOUS or conn->subtype(0x%04x) != GETFILE\n", conn->type, conn->subtype); - return -1; - } - - if(!tosend) { - faimdprintf(1, "getfile_send: file pointer isn't valid\n"); - return -1; - } - - if(!fh) { - faimdprintf(1, "getfile_send: fh isn't valid\n"); - return -1; - } - - /* real code */ - - if(!(buf = (char *)calloc(1, bufsize))) { - perror("faim: getfile_send: calloc:"); - faimdprintf(2, "getfile_send calloc error\n"); - return -1; - } - - pos = 0; - - if( fseek(tosend, 0, SEEK_SET) == -1) { - perror("faim: getfile_send: fseek:"); - faimdprintf(2, "getfile_send fseek error\n"); - } - - faimdprintf(2, "faim: getfile_send: using %i byte blocks\n", bufsize); - - for(pos = 0; pos < fh->size; pos++) { - bufpos = pos % bufsize; - - if(bufpos == 0 && pos > 0) { /* filled our buffer. spit it across the wire */ - if ( (i = write(conn->fd, buf, bufsize)) != bufsize ) { - perror("faim: getfile_send: write1: "); - faimdprintf(1, "faim: getfile_send: whoopsy, didn't write it all...\n"); - free(buf); - return -1; - } - } - if( (buf[bufpos] = fgetc(tosend)) == EOF) { - if(pos != fh->size) { - printf("faim: getfile_send: hrm... apparent early EOF at pos 0x%x of 0x%lx\n", pos, fh->size); - faimdprintf(1, "faim: getfile_send: hrm... apparent early EOF at pos 0x%lx of 0x%lx\n", pos, fh->size); - free(buf); - return -1; - } - } - - - } - - if( (i = write(conn->fd, buf, bufpos+1)) != (bufpos+1)) { - perror("faim: getfile_send: write2: "); - faimdprintf(1, "faim: getfile_send cleanup: whoopsy, didn't write it all...\n"); - free(buf); - return -1; - } - - free(buf); - - fclose(tosend); - return 1; -} - -/* - * int aim_getfile_send_chunk(struct aim_conn_t *conn, FILE *tosend, struct aim_fileheader_t *fh, int pos, int bufsize) - * conn is the OFT connection to shove the data down, - * tosend is the FILE * for the file to send - * fh is the filled-in fh value - * pos is the position to start at (at beginning should be 0, after 5 - * bytes are sent should be 5); -1 for "don't seek" - * bufsize is the size of the chunk to send - * - * returns -1 on error, new pos on success. - * - * - * Notes on usage: - * You don't really have to keep track of pos if you don't want - * to. just always call with -1 for pos, and it'll use the one - * contained within the FILE *. - * - * if (pos + chunksize > fh->size), we only send as much data as we - * can get (ie: up to fh->size. - */ -faim_export int aim_getfile_send_chunk(struct aim_conn_t *conn, FILE *tosend, struct aim_fileheader_t *fh, int pos, int bufsize) -{ - int bufpos; - char *buf; - - /* sanity checks */ - if(conn->type != AIM_CONN_TYPE_RENDEZVOUS || conn->type != AIM_CONN_SUBTYPE_OFT_GETFILE) { - faimdprintf(1, "faim: getfile_send_chunk: conn->type(0x%04x) != RENDEZVOUS or conn->subtype(0x%04x) != GETFILE\n", conn->type, conn->subtype); - return -1; - } - - if(!tosend) { - faimdprintf(1, "faim: getfile_send_chunk: file pointer isn't valid\n"); - return -1; - } - - if(!fh) { - faimdprintf(1, "faim: getfile_send_chunk: fh isn't valid\n"); - return -1; - } - - /* real code */ - - if(!(buf = (char *)calloc(1, bufsize))) { - perror("faim: getfile_send_chunk: calloc:"); - faimdprintf(2, "faim: getfile_send_chunk calloc error\n"); - return -1; - } - - if(pos != -1) { - if( fseek(tosend, pos, SEEK_SET) == -1) { - perror("faim: getfile_send_chunk: fseek:"); - faimdprintf(2, "faim: getfile_send_chunk fseek error\n"); - } - } - - faimdprintf(2, "faim: getfile_send_chunk: using %i byte blocks\n", bufsize); - - for(bufpos = 0; pos < fh->size; bufpos++, pos++) { - if( (buf[bufpos] = fgetc(tosend)) == EOF) { - if(pos != fh->size) { - faimdprintf(1, "faim: getfile_send_chunk: hrm... apparent early EOF at pos 0x%x of 0x%x\n", pos, fh->size); - free(buf); - return -1; - } - } - } - - if( write(conn->fd, buf, bufpos+1) != (bufpos+1)) { - faimdprintf(1, "faim: getfile_send_chunk cleanup: whoopsy, didn't write it all...\n"); - free(buf); - return -1; - } - - free(buf); - - return (pos + bufpos); -} - -/* - * aim_tx_destroy: - * free's tx_command_t's. if command is locked, doesn't free. - * returns -1 on error (locked struct); 0 on success. - * command is the command to free - */ - -faim_internal int aim_tx_destroy(struct command_tx_struct *command) -{ - if(command->lock) - return -1; - if(command->data) - free(command->data); - free(command); - - return 0; -} - -#if 0 -/* - * aim_getfile_intitiate: - * For those times when we want to open up the directim channel ourselves. - * sess is your session, - * conn is the BOS conn, - * priv is a dummy priv value (we'll let it get filled in later) (if - * you pass a NULL, we alloc one) - * destsn is the SN to connect to. - */ - - -faim_export struct aim_conn_t *aim_getfile_initiate(struct aim_session_t *sess, - struct aim_conn_t *conn, - struct aim_getfile_priv *priv, - char *destsn) -{ - struct command_tx_struct *newpacket; - struct aim_conn_t *newconn; - - struct aim_msgcookie_t *cookie; - - int curbyte, i, listenfd; - short port = 4443; - - struct hostent *hptr; - struct utsname myname; - - char cap[16]; - char d[4]; /* XXX: IPv6. *cough* */ - - /* - * Open our socket - */ - - if( (listenfd = aim_listenestablish(port)) == -1) - return NULL; - - /* - * get our local IP - */ - - if(uname(&myname) < 0) - return NULL; - - if( (hptr = gethostbyname(myname.nodename)) == NULL) - return NULL; - - memcpy(&d, hptr->h_addr_list[0], 4); /* XXX: this probably isn't quite kosher, but it works */ - - aim_putcap(cap, 16, AIM_CAPS_IMIMAGE); - - /* - * create the OSCAR packet - */ - - if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+8+2+1+strlen(destsn)+4+4+0x32))) - return NULL; - - newpacket->lock = 1; /* lock struct */ - - curbyte = 0; - curbyte += aim_putsnac(newpacket->data+curbyte, - 0x0004, 0x0006, 0x0000, sess->snac_nextid); - - /* - * Generate a random message cookie - * This cookie needs to be alphanumeric and NULL-terminated to be TOC-compatible. - */ - for (i=0;i<7;i++) - curbyte += aimutil_put8(newpacket->data+curbyte, 0x30 + ((u_char) random() % 20)); - curbyte += aimutil_put8(newpacket->data+curbyte, 0x00); - - /* - * grab all the data for cookie caching. - */ - cookie = (struct aim_msgcookie_t *)calloc(1, sizeof(struct aim_msgcookie_t)); - - memcpy(cookie->cookie, newpacket->data+curbyte-8, 8); - cookie->type = AIM_COOKIETYPE_OFTIM; - - if(!priv) - priv = (struct aim_directim_priv *)calloc(1, sizeof(struct aim_directim_priv)); - - memcpy(priv->cookie, cookie, 8); - memcpy(priv->sn, destsn, sizeof(priv->sn)); - - cookie->data = priv; - - aim_cachecookie(sess, cookie); /* cache da cookie */ - - /* - * Channel ID - */ - curbyte += aimutil_put16(newpacket->data+curbyte,0x0002); - - /* - * Destination SN (prepended with byte length) - */ - curbyte += aimutil_put8(newpacket->data+curbyte,strlen(destsn)); - curbyte += aimutil_putstr(newpacket->data+curbyte, destsn, strlen(destsn)); - - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); - - /* - * enTLV start - */ - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0032); - - /* - * Flag data / ICBM Parameters? - */ - curbyte += aimutil_put8(newpacket->data+curbyte, 0x00); - curbyte += aimutil_put8(newpacket->data+curbyte, 0x00); - - /* - * Cookie - */ - curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cookie, 8); - - /* - * Capability String - */ - curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cap, 0x10); - - /* - * 000a/0002 : 0001 - */ - curbyte += aimutil_put16(newpacket->data+curbyte, 0x000a); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001); - - /* - * 0003/0004: IP address - */ - - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0004); - - for(i = 0; i < 4; i++) - curbyte += aimutil_put8(newpacket->data+curbyte, d[i]); /* already in network byte order */ - - /* - * 0005/0002: Port - */ - - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002); - curbyte += aimutil_put16(newpacket->data+curbyte, port); - - /* - * 000f/0000: umm.. dunno. Zigamorph[1]? - * [1]: see esr's TNHD. - */ - - curbyte += aimutil_put16(newpacket->data+curbyte, 0x000f); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); - - printf("curbyte: 0x%x\n",curbyte); - - newpacket->commandlen = curbyte; - newpacket->lock = 0; - - aim_tx_enqueue(sess, newpacket); - - /* - * allocate and set up our connection - */ - - i = fcntl(listenfd, F_GETFL, 0); - fcntl(listenfd, F_SETFL, i | O_NONBLOCK); - - newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS_OUT, NULL); - if (!newconn) { - perror("aim_newconn"); - aim_conn_kill(sess, &newconn); - return NULL; - } - - newconn->fd = listenfd; - newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM; - newconn->priv = priv; - printf("faim: listening (fd = %d, unconnected)\n", newconn->fd); - - /* - * XXX We need some way of closing the listener socket after - * n seconds of no connection. -- mid - */ - - return newconn; -} - -#endif
--- a/libfaim/aim_im.c Sun Mar 04 22:37:18 2001 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,896 +0,0 @@ -/* - * aim_im.c - * - * The routines for sending/receiving Instant Messages. - * - */ - -#include <faim/aim.h> - -/* - * 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) - * - */ -faim_export unsigned long aim_send_im(struct aim_session_t *sess, - struct aim_conn_t *conn, - char *destsn, u_int flags, char *msg) -{ - - int curbyte,i; - struct command_tx_struct *newpacket; - - if (strlen(msg) >= MAXMSGLEN) - return -1; - - if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, strlen(msg)+256))) - return -1; - - newpacket->lock = 1; /* lock struct */ - - curbyte = 0; - curbyte += aim_putsnac(newpacket->data+curbyte, - 0x0004, 0x0006, 0x0000, sess->snac_nextid); - - /* - * Generate a random message cookie - * - * We could cache these like we do SNAC IDs. (In fact, it - * might be a good idea.) In the message error functions, - * the 8byte message cookie is returned as well as the - * SNAC ID. - * - */ - for (i=0;i<8;i++) - curbyte += aimutil_put8(newpacket->data+curbyte, (u_char) rand()); - - /* - * Channel ID - */ - curbyte += aimutil_put16(newpacket->data+curbyte,0x0001); - - /* - * Destination SN (prepended with byte length) - */ - curbyte += aimutil_put8(newpacket->data+curbyte,strlen(destsn)); - curbyte += aimutil_putstr(newpacket->data+curbyte, destsn, strlen(destsn)); - - /* - * metaTLV start. - */ - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002); - curbyte += aimutil_put16(newpacket->data+curbyte, strlen(msg) + 0x10); - - /* - * Flag data / ICBM Parameters? - */ - curbyte += aimutil_put8(newpacket->data+curbyte, 0x05); - curbyte += aimutil_put8(newpacket->data+curbyte, 0x01); - - /* number of bytes to follow */ - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0004); - curbyte += aimutil_put8(newpacket->data+curbyte, 0x01); - curbyte += aimutil_put8(newpacket->data+curbyte, 0x01); - curbyte += aimutil_put8(newpacket->data+curbyte, 0x01); - curbyte += aimutil_put8(newpacket->data+curbyte, 0x02); - - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0101); - - /* - * Message block length. - */ - curbyte += aimutil_put16(newpacket->data+curbyte, strlen(msg) + 0x04); - - /* - * Character set data? - */ - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); - - /* - * Message. Not terminated. - */ - curbyte += aimutil_putstr(newpacket->data+curbyte,msg, strlen(msg)); - - /* - * Set the Request Acknowledge flag. - */ - if (flags & AIM_IMFLAGS_ACK) { - curbyte += aimutil_put16(newpacket->data+curbyte,0x0003); - curbyte += aimutil_put16(newpacket->data+curbyte,0x0000); - } - - /* - * Set the Autoresponse flag. - */ - if (flags & AIM_IMFLAGS_AWAY) { - curbyte += aimutil_put16(newpacket->data+curbyte,0x0004); - curbyte += aimutil_put16(newpacket->data+curbyte,0x0000); - } - - newpacket->commandlen = curbyte; - newpacket->lock = 0; - - aim_tx_enqueue(sess, newpacket); - - aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, destsn, strlen(destsn)+1); - aim_cleansnacs(sess, 60); /* clean out all SNACs over 60sec old */ - - return sess->snac_nextid; -} - -faim_internal int aim_parse_outgoing_im_middle(struct aim_session_t *sess, - struct command_rx_struct *command) -{ - unsigned int i = 0, z; - rxcallback_t userfunc = NULL; - unsigned char cookie[8]; - int channel; - struct aim_tlvlist_t *tlvlist; - char sn[MAXSNLEN]; - unsigned short icbmflags = 0; - unsigned char flag1 = 0, flag2 = 0; - unsigned char *msgblock = NULL, *msg = NULL; - - i = 10; - - /* ICBM Cookie. */ - for (z=0; z<8; z++,i++) - cookie[z] = command->data[i]; - - /* Channel ID */ - channel = aimutil_get16(command->data+i); - i += 2; - - if (channel != 0x01) { - printf("faim: icbm: ICBM recieved on unsupported channel. Ignoring. (chan = %04x)\n", channel); - return 1; - } - - strncpy(sn, (char *) command->data+i+1, (int) *(command->data+i)); - i += 1 + (int) *(command->data+i); - - tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i); - - if (aim_gettlv(tlvlist, 0x0003, 1)) - icbmflags |= AIM_IMFLAGS_ACK; - if (aim_gettlv(tlvlist, 0x0004, 1)) - icbmflags |= AIM_IMFLAGS_AWAY; - - if (aim_gettlv(tlvlist, 0x0002, 1)) { - int j = 0; - - msgblock = (unsigned char *)aim_gettlv_str(tlvlist, 0x0002, 1); - - /* no, this really is correct. I'm not high or anything either. */ - j += 2; - j += 2 + aimutil_get16(msgblock+j); - j += 2; - - j += 2; /* final block length */ - - flag1 = aimutil_get16(msgblock); - j += 2; - flag2 = aimutil_get16(msgblock); - j += 2; - - msg = msgblock+j; - } - - if ((userfunc = aim_callhandler(command->conn, 0x0004, 0x0006)) || (i = 0)) - i = userfunc(sess, command, channel, sn, msg, icbmflags, flag1, flag2); - - if (msgblock) - free(msgblock); - aim_freetlvchain(&tlvlist); - - return 0; -} - -/* - * 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. - * - * We should also support at least minimal parsing of - * Channel 2, so that we can at least know the name of the - * room we're invited to, but obviously can't attend... - * - */ -faim_internal int aim_parse_incoming_im_middle(struct aim_session_t *sess, - struct command_rx_struct *command) -{ - u_int i = 0,z; - rxcallback_t userfunc = NULL; - u_char cookie[8]; - int channel; - struct aim_tlvlist_t *tlvlist; - struct aim_userinfo_s userinfo; - u_short wastebits; - - memset(&userinfo, 0x00, sizeof(struct aim_userinfo_s)); - - i = 10; /* Skip SNAC header */ - - /* - * Read ICBM Cookie. And throw away. - */ - for (z=0; z<8; z++,i++) - cookie[z] = command->data[i]; - - /* - * Channel ID. - * - * Channel 0x0001 is the message channel. There are - * other channels for things called "rendevous" - * which represent chat and some of the other new - * features of AIM2/3/3.5. - * - * Channel 0x0002 is the Rendevous channel, which - * is where Chat Invitiations and various client-client - * connection negotiations come from. - * - */ - channel = aimutil_get16(command->data+i); - i += 2; - - /* - * - */ - if ((channel != 0x01) && (channel != 0x02)) - { - printf("faim: icbm: ICBM received on an unsupported channel. Ignoring.\n (chan = %04x)", channel); - return 1; - } - - /* - * 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 seperation. - * aim_extractuserinfo() returns the number of bytes used by the - * userinfo tlvs, so you can start reading the rest of them right - * afterward. - * - * 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. - * - */ - i += aim_extractuserinfo(command->data+i, &userinfo); - - /* - * Read block of TLVs (not including the userinfo data). All - * further data is derived from what is parsed here. - */ - tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i); - - /* - * From here on, its depends on what channel we're on. - */ - if (channel == 1) - { - u_int j = 0, y = 0, z = 0; - char *msg = NULL; - u_int icbmflags = 0; - struct aim_tlv_t *msgblocktlv; - u_char *msgblock; - u_short flag1,flag2; - - /* - * Check Autoresponse status. If it is an autoresponse, - * it will contain a type 0x0004 TLV, with zero length. - */ - if (aim_gettlv(tlvlist, 0x0004, 1)) - icbmflags |= AIM_IMFLAGS_AWAY; - - /* - * Check Ack Request status. - */ - if (aim_gettlv(tlvlist, 0x0003, 1)) - icbmflags |= AIM_IMFLAGS_ACK; - - /* - * Message block. - */ - msgblocktlv = aim_gettlv(tlvlist, 0x0002, 1); - if (!msgblocktlv || !msgblocktlv->value) { - printf("faim: icbm: major error! no message block TLV found!\n"); - aim_freetlvchain(&tlvlist); - return 1; - } - - /* - * Extracting the message from the unknown cruft. - * - * This is a bit messy, and I'm not really qualified, - * even as the author, to comment on it. At least - * its not as bad as a while loop shooting into infinity. - * - * "Do you believe in magic?" - * - */ - msgblock = msgblocktlv->value; - j = 0; - - wastebits = aimutil_get8(msgblock+j++); - wastebits = aimutil_get8(msgblock+j++); - - y = aimutil_get16(msgblock+j); - j += 2; - for (z = 0; z < y; z++) - wastebits = aimutil_get8(msgblock+j++); - wastebits = aimutil_get8(msgblock+j++); - wastebits = aimutil_get8(msgblock+j++); - - /* - * Message string length, including flag words. - */ - i = aimutil_get16(msgblock+j); - j += 2; - - /* - * Flag words. - * - * Its rumored that these can kick in some funky - * 16bit-wide char stuff that used to really kill - * libfaim. Hopefully the latter is no longer true. - * - * Though someone should investiagte the former. - * - */ - flag1 = aimutil_get16(msgblock+j); - j += 2; - flag2 = aimutil_get16(msgblock+j); - j += 2; - - if (flag1 || flag2) - printf("faim: icbm: **warning: encoding flags are being used! {%04x, %04x}\n", flag1, flag2); - - /* - * Message string. - */ - i -= 4; - msg = (char *)malloc(i+1); - memcpy(msg, msgblock+j, i); - msg[i] = '\0'; - - /* - * Call client. - */ - userfunc = aim_callhandler(command->conn, 0x0004, 0x0007); - if (userfunc) - i = userfunc(sess, command, channel, &userinfo, msg, icbmflags, flag1, flag2); - else - i = 0; - - free(msg); - } - else if (channel == 0x0002) - { - struct aim_tlv_t *block1; - struct aim_tlvlist_t *list2; - unsigned short reqclass = 0; - unsigned short status = 0; - - /* - * There's another block of TLVs embedded in the type 5 here. - */ - block1 = aim_gettlv(tlvlist, 0x0005, 1); - if (!block1) { - printf("faim: no tlv 0x0005 in rendezvous transaction!\n"); - aim_freetlvchain(&tlvlist); - return 1; /* major problem */ - } - - /* - * First two bytes represent the status of the connection. - * - * 0 is a request, 2 is an accept - */ - status = aimutil_get16(block1->value+0); - - /* - * Next comes the cookie. Should match the ICBM cookie. - */ - if (memcmp(block1->value+2, cookie, 8) != 0) - printf("faim: rend: warning cookies don't match!\n"); - - /* - * The next 16bytes are a capability block so we can - * identify what type of rendezvous this is. - * - * Thanks to Eric Warmenhoven <warmenhoven@linux.com> (of GAIM) - * for pointing some of this out to me. In fact, a lot of - * the client-to-client info comes from the work of the GAIM - * developers. Thanks! - * - * Read off one capability string and we should have it ID'd. - * - */ - reqclass = aim_getcap(block1->value+2+8, 0x10); - if (reqclass == 0x0000) { - printf("faim: rend: no ID block\n"); - aim_freetlvchain(&tlvlist); - return 1; - } - - /* - * 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_readtlvchain(block1->value+2+8+16, block1->length-2-8-16); - - if (!list2 || ((reqclass != AIM_CAPS_IMIMAGE) && !(aim_gettlv(list2, 0x2711, 1)))) { - struct aim_msgcookie_t *cook; - int type; - - type = aim_msgcookie_gettype(reqclass); /* XXX: fix this shitty code */ - - if ((cook = aim_uncachecookie(sess, cookie, type)) == NULL) { - printf("faim: non-data rendezvous thats not in cache!\n"); - aim_freetlvchain(&list2); - aim_freetlvchain(&tlvlist); - return 1; - } - - if (cook->type == AIM_CAPS_SENDFILE) { - struct aim_filetransfer_priv *ft; - - if (cook->data) { - int errorcode = -1; /* XXX shouldnt this be 0? */ - - ft = (struct aim_filetransfer_priv *)cook->data; - - if (aim_gettlv(list2, 0x000b, 1)) - errorcode = aim_gettlv16(list2, 0x000b, 1); - - if (errorcode) { - printf("faim: transfer from %s (%s) for %s cancelled (error code %d)\n", ft->sn, ft->ip, ft->fh.name, errorcode); - } else if (status == 0x0002) { /* connection accepted */ - printf("faim: transfer from %s (%s) for %s accepted\n", ft->sn, ft->ip, ft->fh.name); - } - free(cook->data); - } else { - printf("faim: not data attached to file transfer\n"); - } - } else if (cook->type == AIM_CAPS_VOICE) { - printf("faim: voice request cancelled\n"); - } else { - printf("faim: unknown cookie cache type %d\n", cook->type); - } - - free(cook); - if (list2) - aim_freetlvchain(&list2); - aim_freetlvchain(&tlvlist); - return 1; - } - - /* - * The rest of the handling depends on what type it is. - */ - if (reqclass & AIM_CAPS_BUDDYICON) { - - /* - * Call client. - */ -#if 0 - userfunc = aim_callhandler(command->conn, 0x0004, 0x0007); - if (userfunc || (i = 0)) - i = userfunc(sess, - command, - channel, - reqclass, - &userinfo, - ip, - cookie); -#endif - - } else if (reqclass & AIM_CAPS_VOICE) { - struct aim_msgcookie_t *cachedcook; - - printf("faim: rend: voice!\n"); - - if(!(cachedcook = (struct aim_msgcookie_t*)calloc(1, sizeof(struct aim_msgcookie_t)))) - return 1; - - memcpy(cachedcook->cookie, cookie, 8); - cachedcook->type = AIM_COOKIETYPE_OFTVOICE; - cachedcook->data = NULL; - - if (aim_cachecookie(sess, cachedcook) != 0) - printf("faim: ERROR caching message cookie\n"); - - /* XXX: implement all this */ - - /* - * Call client. - */ - userfunc = aim_callhandler(command->conn, 0x0004, 0x0007); - if (userfunc || (i = 0)) { - i = userfunc(sess, command, channel, reqclass, &userinfo); - } - } else if ((reqclass & AIM_CAPS_IMIMAGE) || (reqclass & AIM_CAPS_BUDDYICON)) { - char ip[30]; - struct aim_directim_priv *priv; - - memset(ip, 0, 30); - - if (aim_gettlv(list2, 0x0003, 1) && aim_gettlv(list2, 0x0005, 1)) { - struct aim_tlv_t *iptlv, *porttlv; - - iptlv = aim_gettlv(list2, 0x0003, 1); - porttlv = aim_gettlv(list2, 0x0005, 1); - - snprintf(ip, 30, "%d.%d.%d.%d:%d", - aimutil_get8(iptlv->value+0), - aimutil_get8(iptlv->value+1), - aimutil_get8(iptlv->value+2), - aimutil_get8(iptlv->value+3), - 4443 /*aimutil_get16(porttlv->value)*/); - } - - printf("faim: rend: directIM request from %s (%s)\n", - userinfo.sn, - ip); - - /* XXX: there are a couple of different request packets for - * different things */ - - priv = (struct aim_directim_priv *)calloc(1, sizeof(struct aim_directim_priv)); - memcpy(priv->ip, ip, sizeof(priv->ip)); - memcpy(priv->sn, userinfo.sn, sizeof(priv->sn)); - memcpy(priv->cookie, cookie, sizeof(priv->cookie)); - - /* - * Call client. - */ - userfunc = aim_callhandler(command->conn, 0x0004, 0x0007); - if (userfunc || (i = 0)) - i = userfunc(sess, - command, - channel, - reqclass, - &userinfo, priv); - - } else if (reqclass & AIM_CAPS_CHAT) { - struct aim_tlv_t *miscinfo; - struct aim_chat_roominfo roominfo; - char *msg=NULL,*encoding=NULL,*lang=NULL; - - miscinfo = aim_gettlv(list2, 0x2711, 1); - aim_chat_readroominfo(miscinfo->value, &roominfo); - - if (aim_gettlv(list2, 0x000c, 1)) - msg = aim_gettlv_str(list2, 0x000c, 1); - - if (aim_gettlv(list2, 0x000d, 1)) - encoding = aim_gettlv_str(list2, 0x000d, 1); - - if (aim_gettlv(list2, 0x000e, 1)) - lang = aim_gettlv_str(list2, 0x000e, 1); - - /* - * Call client. - */ - userfunc = aim_callhandler(command->conn, 0x0004, 0x0007); - if (userfunc || (i = 0)) - i = userfunc(sess, - command, - channel, - reqclass, - &userinfo, - &roominfo, - msg, - encoding?encoding+1:NULL, - lang?lang+1:NULL); - free(roominfo.name); - free(msg); - free(encoding); - free(lang); - } else if (reqclass & AIM_CAPS_GETFILE) { - char ip[30]; - struct aim_msgcookie_t *cachedcook; - struct aim_tlv_t *miscinfo; - - if (!(cachedcook = calloc(1, sizeof(struct aim_msgcookie_t)))) - return 0; - - memset(ip, 0, 30); - - if (!(miscinfo = aim_gettlv(list2, 0x2711, 1))) { - free(cachedcook); - return 0; - } - - if (aim_gettlv(list2, 0x0003, 1) && aim_gettlv(list2, 0x0005, 1)) { - struct aim_tlv_t *iptlv, *porttlv; - - if (!(iptlv = aim_gettlv(list2, 0x0003, 1)) || !(porttlv = aim_gettlv(list2, 0x0005, 1))) { - free(cachedcook); - return 0; - } - - snprintf(ip, 30, "%d.%d.%d.%d:%d", - aimutil_get8(iptlv->value+0), - aimutil_get8(iptlv->value+1), - aimutil_get8(iptlv->value+2), - aimutil_get8(iptlv->value+3), - aimutil_get16(porttlv->value)); - } - - printf("faim: rend: file get request from %s (%s)\n", userinfo.sn, ip); - - /* - * Call client. - */ - userfunc = aim_callhandler(command->conn, 0x0004, 0x0007); - if (userfunc || (i = 0)) - i = userfunc(sess, - command, - channel, - reqclass, - &userinfo, - ip, - cookie); - - } else if (reqclass & AIM_CAPS_SENDFILE) { -#if 0 - char ip[30]; - char *desc = NULL; - struct aim_msgcookie_t *cachedcook; - struct aim_filetransfer_priv *ft; - struct aim_tlv_t *miscinfo; - - memset(ip, 0, sizeof(ip)); - - if (!(miscinfo = aim_gettlv(list2, 0x2711, 1))) - return 0; - - if (aim_gettlv(list2, 0x0003, 1) && aim_gettlv(list2, 0x0003, 1)) { - struct aim_tlv_t *iptlv, *porttlv; - - iptlv = aim_gettlv(list2, 0x0003, 1); - porttlv = aim_gettlv(list2, 0x0005, 1); - - snprintf(ip, sizeof(ip)-1, "%d.%d.%d.%d:%d", - aimutil_get8(iptlv->value+0), - aimutil_get8(iptlv->value+1), - aimutil_get8(iptlv->value+2), - aimutil_get8(iptlv->value+3), - aimutil_get16(porttlv->value)); - } - - if (aim_gettlv(list2, 0x000c, 1)) { - desc = aim_gettlv_str(list2, 0x000c, 1); - } - - printf("faim: rend: file transfer request from %s for %s: %s (%s)\n", - userinfo.sn, - miscinfo->value+8, - desc, - ip); - - memcpy(cachedcook->cookie, cookie, 8); - - ft = malloc(sizeof(struct aim_filetransfer_priv)); - strncpy(ft->sn, userinfo.sn, sizeof(ft->sn)); - strncpy(ft->ip, ip, sizeof(ft->ip)); - strncpy(ft->fh.name, miscinfo->value+8, sizeof(ft->fh.name)); - cachedcook->type = AIM_COOKIETYPE_OFTSEND; - cachedcook->data = ft; - - if (aim_cachecookie(sess, cachedcook) != 0) - printf("faim: ERROR caching message cookie\n"); - - - aim_accepttransfer(sess, command->conn, ft->sn, cookie, AIM_CAPS_SENDFILE); - - if (desc) - free(desc); -#endif - /* - * Call client. - */ - userfunc = aim_callhandler(command->conn, 0x0004, 0x0007); - if (userfunc || (i = 0)) - i = userfunc(sess, - command, - channel, - reqclass, - &userinfo); - } else { - printf("faim: rend: unknown rendezvous 0x%04x\n", reqclass); - } - - aim_freetlvchain(&list2); - } - - /* - * Free up the TLV chain. - */ - aim_freetlvchain(&tlvlist); - - - return i; -} - -/* - * 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 unsigned long aim_denytransfer(struct aim_session_t *sess, - struct aim_conn_t *conn, - char *sender, - char *cookie, - unsigned short code) -{ - struct command_tx_struct *newpacket; - int curbyte, i; - - if(!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+8+2+1+strlen(sender)+6))) - return -1; - - newpacket->lock = 1; - - curbyte = aim_putsnac(newpacket->data, 0x0004, 0x000b, 0x0000, sess->snac_nextid); - for (i = 0; i < 8; i++) - curbyte += aimutil_put8(newpacket->data+curbyte, cookie[i]); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002); - curbyte += aimutil_put8(newpacket->data+curbyte, strlen(sender)); - curbyte += aimutil_putstr(newpacket->data+curbyte, sender, strlen(sender)); - curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0003, code); - - newpacket->lock = 0; - aim_tx_enqueue(sess, newpacket); - - return (sess->snac_nextid++); -} - -/* - * Not real sure what this does, nor does anyone I've talk to. - * - * Didn't use to send it. But now I think it might be a good - * idea. - * - */ -faim_export unsigned long aim_seticbmparam(struct aim_session_t *sess, - struct aim_conn_t *conn) -{ - struct command_tx_struct *newpacket; - int curbyte; - - if(!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+16))) - return -1; - - newpacket->lock = 1; - - curbyte = aim_putsnac(newpacket->data, 0x0004, 0x0002, 0x0000, sess->snac_nextid); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); - curbyte += aimutil_put32(newpacket->data+curbyte, 0x00000003); - curbyte += aimutil_put8(newpacket->data+curbyte, 0x1f); - curbyte += aimutil_put8(newpacket->data+curbyte, 0x40); - curbyte += aimutil_put8(newpacket->data+curbyte, 0x03); - curbyte += aimutil_put8(newpacket->data+curbyte, 0xe7); - curbyte += aimutil_put8(newpacket->data+curbyte, 0x03); - curbyte += aimutil_put8(newpacket->data+curbyte, 0xe7); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); - - newpacket->lock = 0; - aim_tx_enqueue(sess, newpacket); - - return (sess->snac_nextid++); -} - -faim_internal int aim_parse_msgerror_middle(struct aim_session_t *sess, - struct command_rx_struct *command) -{ - u_long snacid = 0x000000000; - struct aim_snac_t *snac = NULL; - int ret = 0; - rxcallback_t userfunc = NULL; - char *dest; - unsigned short reason = 0; - - /* - * Get SNAC from packet and look it up - * the list of unrepliedto/outstanding - * SNACs. - * - * After its looked up, the SN that the - * message should've gone to will be - * in the ->data element of the snac struct. - * - */ - snacid = aimutil_get32(command->data+6); - snac = aim_remsnac(sess, snacid); - - if (!snac) { - printf("faim: msgerr: got an ICBM-failed error on an unknown SNAC ID! (%08lx)\n", snacid); - dest = NULL; - } else - dest = snac->data; - - reason = aimutil_get16(command->data+10); - - /* - * Call client. - */ - userfunc = aim_callhandler(command->conn, 0x0004, 0x0001); - if (userfunc) - ret = userfunc(sess, command, dest, reason); - else - ret = 0; - - if (snac) { - free(snac->data); - free(snac); - } - - return ret; -} - - -faim_internal int aim_parse_missedcall(struct aim_session_t *sess, - struct command_rx_struct *command) -{ - int i, ret = 1; - rxcallback_t userfunc = NULL; - unsigned short channel, nummissed, reason; - struct aim_userinfo_s userinfo; - - i = 10; /* Skip SNAC header */ - - - /* - * XXX: supposedly, this entire packet can repeat as many times - * as necessary. Should implement that. - */ - - /* - * Channel ID. - */ - channel = aimutil_get16(command->data+i); - i += 2; - - /* - * Extract the standard user info block. - */ - i += aim_extractuserinfo(command->data+i, &userinfo); - - nummissed = aimutil_get16(command->data+i); - i += 2; - - reason = aimutil_get16(command->data+i); - i += 2; - - /* - * Call client. - */ - userfunc = aim_callhandler(command->conn, 0x0004, 0x000a); - if (userfunc) - ret = userfunc(sess, command, channel, &userinfo, nummissed, reason); - else - ret = 0; - - return ret; -}
--- a/libfaim/aim_info.c Sun Mar 04 22:37:18 2001 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,702 +0,0 @@ -/* - * aim_info.c - * - * The functions here are responsible for requesting and parsing information- - * gathering SNACs. - * - */ - - -#include <faim/aim.h> - -struct aim_priv_inforeq { - char sn[MAXSNLEN+1]; - unsigned short infotype; -}; - -faim_export unsigned long aim_getinfo(struct aim_session_t *sess, - struct aim_conn_t *conn, - const char *sn, - unsigned short infotype) -{ - struct command_tx_struct *newpacket; - struct aim_priv_inforeq privdata; - int i = 0; - - if (!sess || !conn || !sn) - return 0; - - if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 12+1+strlen(sn)))) - return -1; - - newpacket->lock = 1; - - i = aim_putsnac(newpacket->data, 0x0002, 0x0005, 0x0000, sess->snac_nextid); - - i += aimutil_put16(newpacket->data+i, infotype); - i += aimutil_put8(newpacket->data+i, strlen(sn)); - i += aimutil_putstr(newpacket->data+i, sn, strlen(sn)); - - newpacket->lock = 0; - aim_tx_enqueue(sess, newpacket); - - strncpy(privdata.sn, sn, sizeof(privdata.sn)); - privdata.infotype = infotype; - aim_cachesnac(sess, 0x0002, 0x0005, 0x0000, &privdata, sizeof(struct aim_priv_inforeq)); - - return sess->snac_nextid; -} - -faim_internal int aim_parse_locateerr(struct aim_session_t *sess, - struct command_rx_struct *command) -{ - u_long snacid = 0x000000000; - struct aim_snac_t *snac = NULL; - int ret = 0; - rxcallback_t userfunc = NULL; - char *dest; - unsigned short reason = 0; - - /* - * Get SNAC from packet and look it up - * the list of unrepliedto/outstanding - * SNACs. - * - */ - snacid = aimutil_get32(command->data+6); - snac = aim_remsnac(sess, snacid); - - if (!snac) { - printf("faim: locerr: got an locate-failed error on an unknown SNAC ID! (%08lx)\n", snacid); - dest = NULL; - } else - dest = snac->data; - - reason = aimutil_get16(command->data+10); - - /* - * Call client. - */ - userfunc = aim_callhandler(command->conn, 0x0002, 0x0001); - if (userfunc) - ret = userfunc(sess, command, dest, reason); - else - ret = 0; - - if (snac) { - free(snac->data); - free(snac); - } - - return ret; -} - -/* - * Capability blocks. - */ -u_char aim_caps[8][16] = { - - /* Buddy icon */ - {0x09, 0x46, 0x13, 0x46, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, - - /* Voice */ - {0x09, 0x46, 0x13, 0x41, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, - - /* IM image */ - {0x09, 0x46, 0x13, 0x45, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, - - /* Chat */ - {0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, - - /* Get file */ - {0x09, 0x46, 0x13, 0x48, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, - - /* Send file */ - {0x09, 0x46, 0x13, 0x43, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, - - /* Saves stock portfolios */ - {0x09, 0x46, 0x13, 0x47, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, - - /* Games */ - {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, -}; - -faim_internal unsigned short aim_getcap(unsigned char *capblock, int buflen) -{ - u_short ret = 0; - int y; - int offset = 0; - int identified; - - while (offset < buflen) { - identified = 0; - for(y=0; y < (sizeof(aim_caps)/0x10); y++) { - if (memcmp(&aim_caps[y], capblock+offset, 0x10) == 0) { - switch(y) { - case 0: ret |= AIM_CAPS_BUDDYICON; identified++; break; - case 1: ret |= AIM_CAPS_VOICE; identified++; break; - case 2: ret |= AIM_CAPS_IMIMAGE; identified++; break; - case 3: ret |= AIM_CAPS_CHAT; identified++; break; - case 4: ret |= AIM_CAPS_GETFILE; identified++; break; - case 5: ret |= AIM_CAPS_SENDFILE; identified++; break; - case 6: ret |= AIM_CAPS_GAMES; identified++; break; - case 7: ret |= AIM_CAPS_SAVESTOCKS; identified++; break; - } - } - } - if (!identified) { - faimdprintf(1, "faim: unknown capability "); - for (y = 0; y < 0x10; y++) - faimdprintf(2, "%02x ", capblock[offset+y]); - faimdprintf(1, "\n"); - ret |= 0xff00; - } - - offset += 0x10; - } - return ret; -} - -faim_internal int aim_putcap(unsigned char *capblock, int buflen, u_short caps) -{ - int offset = 0; - - if (!capblock) - return -1; - - if ((caps & AIM_CAPS_BUDDYICON) && (offset < buflen)) { - memcpy(capblock+offset, aim_caps[0], sizeof(aim_caps[0])); - offset += sizeof(aim_caps[1]); - } - if ((caps & AIM_CAPS_VOICE) && (offset < buflen)) { - memcpy(capblock+offset, aim_caps[1], sizeof(aim_caps[1])); - offset += sizeof(aim_caps[1]); - } - if ((caps & AIM_CAPS_IMIMAGE) && (offset < buflen)) { - memcpy(capblock+offset, aim_caps[2], sizeof(aim_caps[2])); - offset += sizeof(aim_caps[2]); - } - if ((caps & AIM_CAPS_CHAT) && (offset < buflen)) { - memcpy(capblock+offset, aim_caps[3], sizeof(aim_caps[3])); - offset += sizeof(aim_caps[3]); - } - if ((caps & AIM_CAPS_GETFILE) && (offset < buflen)) { - memcpy(capblock+offset, aim_caps[4], sizeof(aim_caps[4])); - offset += sizeof(aim_caps[4]); - } - if ((caps & AIM_CAPS_SENDFILE) && (offset < buflen)) { - memcpy(capblock+offset, aim_caps[5], sizeof(aim_caps[5])); - offset += sizeof(aim_caps[5]); - } - if ((caps & AIM_CAPS_GAMES) && (offset < buflen)) { - memcpy(capblock+offset, aim_caps[6], sizeof(aim_caps[6])); - offset += sizeof(aim_caps[6]); - } - if ((caps & AIM_CAPS_SAVESTOCKS) && (offset < buflen)) { - memcpy(capblock+offset, aim_caps[7], sizeof(aim_caps[7])); - offset += sizeof(aim_caps[7]); - } - - return offset; -} - -/* - * AIM is fairly regular about providing user info. This - * is a generic routine to extract it in its standard form. - */ -faim_internal int aim_extractuserinfo(u_char *buf, struct aim_userinfo_s *outinfo) -{ - int i = 0; - int tlvcnt = 0; - int curtlv = 0; - int tlv1 = 0; - u_short curtype; - int lastvalid; - - - if (!buf || !outinfo) - return -1; - - /* Clear out old data first */ - memset(outinfo, 0x00, sizeof(struct aim_userinfo_s)); - - /* - * Screen name. Stored as an unterminated string prepended - * with an unsigned byte containing its length. - */ - if (buf[i] < MAXSNLEN) { - memcpy(outinfo->sn, &(buf[i+1]), buf[i]); - outinfo->sn[(int)buf[i]] = '\0'; - } else { - memcpy(outinfo->sn, &(buf[i+1]), MAXSNLEN-1); - outinfo->sn[MAXSNLEN] = '\0'; - } - i = 1 + (int)buf[i]; - - /* - * Warning Level. Stored as an unsigned short. - */ - outinfo->warnlevel = aimutil_get16(&buf[i]); - i += 2; - - /* - * TLV Count. Unsigned short representing the number of - * Type-Length-Value triples that follow. - */ - tlvcnt = aimutil_get16(&buf[i]); - i += 2; - - /* - * Parse out the Type-Length-Value triples as they're found. - */ - while (curtlv < tlvcnt) { - lastvalid = 1; - curtype = aimutil_get16(&buf[i]); - switch (curtype) { - /* - * Type = 0x0000: Invalid - * - * AOL has been trying to throw these in just to break us. - * They're real nice guys over there at AOL. - * - * Just skip the two zero bytes and continue on. (This doesn't - * count towards tlvcnt!) - */ - case 0x0000: - lastvalid = 0; - i += 2; - break; - - /* - * Type = 0x0001: User flags - * - * Specified as any of the following bitwise 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 - * - * In some odd cases, we can end up with more - * than one of these. We only want the first, - * as the others may not be something we want. - * - */ - case 0x0001: - if (tlv1) /* use only the first */ - break; - outinfo->flags = aimutil_get16(&buf[i+4]); - tlv1++; - break; - - /* - * Type = 0x0002: Member-Since date. - * - * The time/date that the user originally - * registered for the service, stored in - * time_t format - */ - case 0x0002: - outinfo->membersince = aimutil_get32(&buf[i+4]); - break; - - /* - * Type = 0x0003: On-Since date. - * - * The time/date that the user started - * their current session, stored in time_t - * format. - */ - case 0x0003: - outinfo->onlinesince = aimutil_get32(&buf[i+4]); - break; - - /* - * Type = 0x0004: Idle time. - * - * Number of seconds since the user - * actively used the service. - */ - case 0x0004: - outinfo->idletime = aimutil_get16(&buf[i+4]); - break; - - /* - * Type = 0x0006: ICQ Online Status - * - * ICQ's Away/DND/etc "enriched" status - * Some decoding of values done by Scott <darkagl@pcnet.com> - */ - case 0x0006: - outinfo->icqinfo.status = aimutil_get16(buf+i+2+2+2); - break; - - - /* - * Type = 0x000a - * - * ICQ User IP Address. - * Ahh, the joy of ICQ security. - */ - case 0x000a: - outinfo->icqinfo.ipaddr = aimutil_get32(&buf[i+4]); - break; - - /* Type = 0x000c - * - * random crap containing the IP address, - * apparently a port number, and some Other Stuff. - * - */ - case 0x000c: - memcpy(outinfo->icqinfo.crap, &buf[i+4], 0x25); - break; - - /* - * Type = 0x000d - * - * Capability information. Not real sure of - * actual decoding. See comment on aim_bos_setprofile() - * in aim_misc.c about the capability block, its the same. - * - */ - case 0x000d: - { - int len; - len = aimutil_get16(buf+i+2); - if (!len) - break; - - outinfo->capabilities = aim_getcap(buf+i+4, len); - if (outinfo->capabilities & 0xff00) - faimdprintf(2, "%s\n", outinfo->sn); - } - break; - - /* - * Type = 0x000e - * - * Unknown. Always of zero length, and always only - * on AOL users. - * - * Ignore. - * - */ - case 0x000e: - break; - - /* - * 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). - * - */ - case 0x000f: - case 0x0010: - outinfo->sessionlen = aimutil_get32(&buf[i+4]); - break; - - /* - * 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. - * - */ - default: - { - int len,z = 0, y = 0, x = 0; - char tmpstr[80]; - printf("faim: userinfo: **warning: unexpected TLV:\n"); - printf("faim: userinfo: sn =%s\n", outinfo->sn); - printf("faim: userinfo: curtlv=0x%04x\n", curtlv); - printf("faim: userinfo: type =0x%04x\n",aimutil_get16(&buf[i])); - printf("faim: userinfo: length=0x%04x\n", len = aimutil_get16(&buf[i+2])); - printf("faim: userinfo: data: \n"); - while (z<len) - { - x = sprintf(tmpstr, "faim: userinfo: "); - for (y = 0; y < 8; y++) - { - if (z<len) - { - sprintf(tmpstr+x, "%02x ", buf[i+4+z]); - z++; - x += 3; - } - else - break; - } - printf("%s\n", tmpstr); - } - } - break; - } - /* - * No matter what, TLV triplets should always look like this: - * - * u_short type; - * u_short length; - * u_char data[length]; - * - */ - if (lastvalid) { - i += (2 + 2 + aimutil_get16(&buf[i+2])); - curtlv++; - } - } - - return i; -} - -/* - * Oncoming Buddy notifications contain a subset of the - * user information structure. Its close enough to run - * through aim_extractuserinfo() however. - * - */ -faim_internal int aim_parse_oncoming_middle(struct aim_session_t *sess, - struct command_rx_struct *command) -{ - struct aim_userinfo_s userinfo; - u_int i = 0; - rxcallback_t userfunc=NULL; - - i = 10; - i += aim_extractuserinfo(command->data+i, &userinfo); - - userfunc = aim_callhandler(command->conn, AIM_CB_FAM_BUD, AIM_CB_BUD_ONCOMING); - if (userfunc) - i = userfunc(sess, command, &userinfo); - - return 1; -} - -/* - * Offgoing Buddy notifications contain no useful - * information other than the name it applies to. - * - */ -faim_internal int aim_parse_offgoing_middle(struct aim_session_t *sess, - struct command_rx_struct *command) -{ - char sn[MAXSNLEN+1]; - u_int i = 0; - rxcallback_t userfunc=NULL; - - strncpy(sn, (char *)command->data+11, (int)command->data[10]); - sn[(int)command->data[10]] = '\0'; - - userfunc = aim_callhandler(command->conn, AIM_CB_FAM_BUD, AIM_CB_BUD_OFFGOING); - if (userfunc) - i = userfunc(sess, command, sn); - - return 1; -} - -/* - * This parses the user info stuff out all nice and pretty then calls - * the higher-level callback (in the user app). - * - */ -faim_internal int aim_parse_userinfo_middle(struct aim_session_t *sess, - struct command_rx_struct *command) -{ - struct aim_userinfo_s userinfo; - char *text_encoding = NULL; - char *text = NULL; - u_int i = 0; - rxcallback_t userfunc=NULL; - struct aim_tlvlist_t *tlvlist; - struct aim_snac_t *origsnac = NULL; - u_long snacid; - struct aim_priv_inforeq *inforeq; - - snacid = aimutil_get32(&command->data[6]); - origsnac = aim_remsnac(sess, snacid); - - if (!origsnac || !origsnac->data) { - printf("faim: parse_userinfo_middle: major problem: no snac stored!\n"); - return 1; - } - - inforeq = (struct aim_priv_inforeq *)origsnac->data; - - switch (inforeq->infotype) { - case AIM_GETINFO_GENERALINFO: - case AIM_GETINFO_AWAYMESSAGE: - i = 10; - - /* - * extractuserinfo will give us the basic metaTLV information - */ - i += aim_extractuserinfo(command->data+i, &userinfo); - - /* - * However, in this command, there's usually more TLVs following... - */ - tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i); - - /* - * Depending on what informational text was requested, different - * TLVs will appear here. - * - * Profile will be 1 and 2, away message will be 3 and 4. - */ - if (aim_gettlv(tlvlist, 0x0001, 1)) { - text_encoding = aim_gettlv_str(tlvlist, 0x0001, 1); - text = aim_gettlv_str(tlvlist, 0x0002, 1); - } else if (aim_gettlv(tlvlist, 0x0003, 1)) { - text_encoding = aim_gettlv_str(tlvlist, 0x0003, 1); - text = aim_gettlv_str(tlvlist, 0x0004, 1); - } - - userfunc = aim_callhandler(command->conn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO); - if (userfunc) { - i = userfunc(sess, - command, - &userinfo, - text_encoding, - text, - inforeq->infotype); - } - - free(text_encoding); - free(text); - aim_freetlvchain(&tlvlist); - break; - default: - printf("faim: parse_userinfo_middle: unknown infotype in request! (0x%04x)\n", inforeq->infotype); - break; - } - - if (origsnac) { - if (origsnac->data) - free(origsnac->data); - free(origsnac); - } - - return 1; -} - -/* - * Inverse of aim_extractuserinfo() - */ -faim_internal int aim_putuserinfo(u_char *buf, int buflen, struct aim_userinfo_s *info) -{ - int i = 0, numtlv = 0; - struct aim_tlvlist_t *tlvlist = NULL; - - if (!buf || !info) - return 0; - - i += aimutil_put8(buf+i, strlen(info->sn)); - i += aimutil_putstr(buf+i, info->sn, strlen(info->sn)); - - i += aimutil_put16(buf+i, info->warnlevel); - - - aim_addtlvtochain16(&tlvlist, 0x0001, info->flags); - numtlv++; - - aim_addtlvtochain32(&tlvlist, 0x0002, info->membersince); - numtlv++; - - aim_addtlvtochain32(&tlvlist, 0x0003, info->onlinesince); - numtlv++; - - aim_addtlvtochain16(&tlvlist, 0x0004, info->idletime); - numtlv++; - -#if ICQ_OSCAR_SUPPORT - if(atoi(info->sn) != 0) { - aim_addtlvtochain16(&tlvlist, 0x0006, info->icqinfo.status); - aim_addtlvtochain32(&tlvlist, 0x000a, info->icqinfo.ipaddr); - } -#endif - - aim_addtlvtochain_caps(&tlvlist, 0x000d, info->capabilities); - numtlv++; - - aim_addtlvtochain32(&tlvlist, (unsigned short)((info->flags)&AIM_FLAG_AOL?0x0010:0x000f), info->sessionlen); - numtlv++; - - i += aimutil_put16(buf+i, numtlv); /* tlvcount */ - i += aim_writetlvchain(buf+i, buflen-i, &tlvlist); /* tlvs */ - aim_freetlvchain(&tlvlist); - - return i; -} - -faim_export int aim_sendbuddyoncoming(struct aim_session_t *sess, struct aim_conn_t *conn, struct aim_userinfo_s *info) -{ - struct command_tx_struct *tx; - int i = 0; - - if (!sess || !conn || !info) - return 0; - - if (!(tx = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 1152))) - return -1; - - tx->lock = 1; - - i += aimutil_put16(tx->data+i, 0x0003); - i += aimutil_put16(tx->data+i, 0x000b); - i += aimutil_put16(tx->data+i, 0x0000); - i += aimutil_put16(tx->data+i, 0x0000); - i += aimutil_put16(tx->data+i, 0x0000); - - i += aim_putuserinfo(tx->data+i, tx->commandlen-i, info); - - tx->commandlen = i; - tx->lock = 0; - aim_tx_enqueue(sess, tx); - - return 0; -} - -faim_export int aim_sendbuddyoffgoing(struct aim_session_t *sess, struct aim_conn_t *conn, char *sn) -{ - struct command_tx_struct *tx; - int i = 0; - - if (!sess || !conn || !sn) - return 0; - - if (!(tx = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+1+strlen(sn)))) - return -1; - - tx->lock = 1; - - i += aimutil_put16(tx->data+i, 0x0003); - i += aimutil_put16(tx->data+i, 0x000c); - i += aimutil_put16(tx->data+i, 0x0000); - i += aimutil_put16(tx->data+i, 0x0000); - i += aimutil_put16(tx->data+i, 0x0000); - - i += aimutil_put8(tx->data+i, strlen(sn)); - i += aimutil_putstr(tx->data+i, sn, strlen(sn)); - - tx->lock = 0; - aim_tx_enqueue(sess, tx); - - return 0; -} -
--- a/libfaim/aim_login.c Sun Mar 04 22:37:18 2001 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,579 +0,0 @@ -/* - * aim_login.c - * - * This contains all the functions needed to actually login. - * - */ - -#include <faim/aim.h> - -#include "md5.h" - -static int aim_encode_password_md5(const char *password, const char *key, md5_byte_t *digest); -static int aim_encode_password(const char *password, unsigned char *encoded); - -/* - * FIXME: Reimplement the TIS stuff. - */ -#ifdef TIS_TELNET_PROXY -#include "tis_telnet_proxy.h" -#endif - -faim_export int aim_sendconnack(struct aim_session_t *sess, - struct aim_conn_t *conn) -{ - int curbyte=0; - - struct command_tx_struct *newpacket; - - if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0001, conn, 4))) - return -1; - - newpacket->lock = 1; - - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001); - - newpacket->lock = 0; - return aim_tx_enqueue(sess, newpacket); -} - -/* - * 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(struct aim_session_t *sess, - struct aim_conn_t *conn, - char *sn) -{ - int curbyte; - struct command_tx_struct *newpacket; - - if (!sess || !conn || !sn) - return -1; - - /* - * For ICQ, we enable the ancient horrible login and stuff - * a key packet into the queue to make it look like we got - * a reply back. This is so the client doesn't know we're - * really not doing MD5 login. - * - * This may sound stupid, but I'm not in the best of moods and - * I don't plan to keep support for this crap around much longer. - * Its all AOL's fault anyway, really. I hate AOL. Really. They - * always seem to be able to piss me off by doing the dumbest little - * things. Like disabling MD5 logins for ICQ UINs, or adding purposefully - * wrong TLV lengths, or adding superfluous information to host strings, - * or... I'll stop. - * - */ - if ((sn[0] >= '0') && (sn[0] <= '9')) { - struct command_rx_struct *newrx; - int i; - - if (!(newrx = (struct command_rx_struct *)malloc(sizeof(struct command_rx_struct)))) - return -1; - memset(newrx, 0x00, sizeof(struct command_rx_struct)); - newrx->lock = 1; - newrx->hdrtype = AIM_FRAMETYPE_OSCAR; - newrx->hdr.oscar.type = 0x02; - newrx->hdr.oscar.seqnum = 0; - newrx->commandlen = 10+2+1; - newrx->nofree = 0; - if (!(newrx->data = malloc(newrx->commandlen))) { - free(newrx); - return -1; - } - - i = aim_putsnac(newrx->data, 0x0017, 0x0007, 0x0000, 0x0000); - i += aimutil_put16(newrx->data+i, 0x01); - i += aimutil_putstr(newrx->data+i, "0", 1); - - newrx->conn = conn; - - newrx->next = sess->queue_incoming; - sess->queue_incoming = newrx; - - newrx->lock = 0; - - sess->flags &= ~AIM_SESS_FLAGS_SNACLOGIN; - - return 0; - } - - sess->flags |= AIM_SESS_FLAGS_SNACLOGIN; - - aim_sendconnack(sess, conn); - - if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+2+2+strlen(sn)))) - return -1; - - newpacket->lock = 1; - - curbyte = aim_putsnac(newpacket->data, 0x0017, 0x0006, 0x0000, 0x00010000); - curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x0001, strlen(sn), sn); - - newpacket->commandlen = curbyte; - newpacket->lock = 0; - - return aim_tx_enqueue(sess, newpacket); -} - -/* - * send_login(int socket, char *sn, char *password) - * - * This is the initial login request packet. - * - * The password is encoded before transmition, as per - * encode_password(). See that function for their - * stupid method of doing it. - * - * Latest WinAIM: - * clientstring = "AOL Instant Messenger (SM), version 4.3.2188/WIN32" - * major2 = 0x0109 - * major = 0x0400 - * minor = 0x0003 - * minor2 = 0x0000 - * build = 0x088c - * unknown = 0x00000086 - * lang = "en" - * country = "us" - * unknown4a = 0x01 - */ -faim_export int aim_send_login (struct aim_session_t *sess, - struct aim_conn_t *conn, - char *sn, char *password, - struct client_info_s *clientinfo, - char *key) -{ - int curbyte=0; - struct command_tx_struct *newpacket; - - if (!clientinfo || !sn || !password) - return -1; - - if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 1152))) - return -1; - - newpacket->lock = 1; - - newpacket->hdr.oscar.type = (sess->flags & AIM_SESS_FLAGS_SNACLOGIN)?0x02:0x01; - - if (sess->flags & AIM_SESS_FLAGS_SNACLOGIN) - curbyte = aim_putsnac(newpacket->data, 0x0017, 0x0002, 0x0000, 0x00010000); - else { - curbyte = aimutil_put16(newpacket->data, 0x0000); - curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001); - } - - curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x0001, strlen(sn), sn); - - if (sess->flags & AIM_SESS_FLAGS_SNACLOGIN) { - md5_byte_t digest[16]; - - aim_encode_password_md5(password, key, digest); - curbyte+= aim_puttlv_str(newpacket->data+curbyte, 0x0025, 16, (char *)digest); - } else { - char *password_encoded; - - password_encoded = (char *) malloc(strlen(password)); - aim_encode_password(password, password_encoded); - curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x0002, strlen(password), password_encoded); - free(password_encoded); - } - - /* XXX is clientstring required by oscar? */ - if (strlen(clientinfo->clientstring)) - curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x0003, strlen(clientinfo->clientstring), clientinfo->clientstring); - - if (sess->flags & AIM_SESS_FLAGS_SNACLOGIN) { - curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0016, (unsigned short)clientinfo->major2); - curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0017, (unsigned short)clientinfo->major); - curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0018, (unsigned short)clientinfo->minor); - curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0019, (unsigned short)clientinfo->minor2); - curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x001a, (unsigned short)clientinfo->build); - - curbyte += aim_puttlv_32(newpacket->data+curbyte, 0x0014, clientinfo->unknown); - curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0009, 0x0015); - curbyte += aim_puttlv_8(newpacket->data+curbyte, 0x004a, 0x00); - } else { - /* Use very specific version numbers, to further indicate the hack. */ - curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0016, 0x010a); - curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0017, 0x0004); - curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0018, 0x003c); - curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0019, 0x0001); - curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x001a, 0x0cce); - curbyte += aim_puttlv_32(newpacket->data+curbyte, 0x0014, 0x00000055); - } - - if (strlen(clientinfo->country)) - curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x000e, strlen(clientinfo->country), clientinfo->country); - else - curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x000e, 2, "us"); - - if (strlen(clientinfo->lang)) - curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x000f, strlen(clientinfo->lang), clientinfo->lang); - else - curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x000f, 2, "en"); - - newpacket->commandlen = curbyte; - - newpacket->lock = 0; - return aim_tx_enqueue(sess, newpacket); -} - -static int aim_encode_password_md5(const char *password, const char *key, md5_byte_t *digest) -{ - md5_state_t state; - - md5_init(&state); - md5_append(&state, (const md5_byte_t *)key, strlen(key)); - md5_append(&state, (const md5_byte_t *)password, strlen(password)); - md5_append(&state, (const md5_byte_t *)AIM_MD5_STRING, strlen(AIM_MD5_STRING)); - md5_finish(&state, (md5_byte_t *)digest); - - return 0; -} - -/** - * aim_encode_password - Encode a password using old XOR method - * @password: incoming password - * @encoded: buffer to put encoded password - * - * This takes a const pointer to a (null terminated) string - * containing the unencoded password. It also gets passed - * an already allocated buffer to store the encoded password. - * This buffer should be the exact length of the password without - * the null. The encoded password buffer /is not %NULL terminated/. - * - * The encoding_table seems to be a fixed set of values. We'll - * hope it doesn't change over time! - * - * This is only used for the XOR method, not the better MD5 method. - * - */ -static int aim_encode_password(const char *password, unsigned char *encoded) -{ - u_char encoding_table[] = { -#if 0 /* old v1 table */ - 0xf3, 0xb3, 0x6c, 0x99, - 0x95, 0x3f, 0xac, 0xb6, - 0xc5, 0xfa, 0x6b, 0x63, - 0x69, 0x6c, 0xc3, 0x9f -#else /* v2.1 table, also works for ICQ */ - 0xf3, 0x26, 0x81, 0xc4, - 0x39, 0x86, 0xdb, 0x92, - 0x71, 0xa3, 0xb9, 0xe6, - 0x53, 0x7a, 0x95, 0x7c -#endif - }; - - int i; - - for (i = 0; i < strlen(password); i++) - encoded[i] = (password[i] ^ encoding_table[i]); - - return 0; -} - -/* - * This is sent back as a general response to the login command. - * It can be either an error or a success, depending on the - * precense of certain TLVs. - * - * The client should check the value passed as errorcode. If - * its nonzero, there was an error. - * - */ -faim_internal int aim_authparse(struct aim_session_t *sess, - struct command_rx_struct *command) -{ - struct aim_tlvlist_t *tlvlist; - int ret = 1; - rxcallback_t userfunc = NULL; - char *sn = NULL, *bosip = NULL, *errurl = NULL, *email = NULL; - unsigned char *cookie = NULL; - int errorcode = 0, regstatus = 0; - int latestbuild = 0, latestbetabuild = 0; - char *latestrelease = NULL, *latestbeta = NULL; - char *latestreleaseurl = NULL, *latestbetaurl = NULL; - char *latestreleaseinfo = NULL, *latestbetainfo = NULL; - - /* - * Read block of TLVs. All further data is derived - * from what is parsed here. - * - * For SNAC login, there's a 17/3 SNAC header in front. - * - */ - if (sess->flags & AIM_SESS_FLAGS_SNACLOGIN) - tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10); - else - tlvlist = aim_readtlvchain(command->data, command->commandlen); - - /* - * No matter what, we should have a screen name. - */ - memset(sess->sn, 0, sizeof(sess->sn)); - if (aim_gettlv(tlvlist, 0x0001, 1)) { - sn = aim_gettlv_str(tlvlist, 0x0001, 1); - strncpy(sess->sn, sn, sizeof(sess->sn)); - } - - /* - * Check for an error code. If so, we should also - * have an error url. - */ - if (aim_gettlv(tlvlist, 0x0008, 1)) - errorcode = aim_gettlv16(tlvlist, 0x0008, 1); - if (aim_gettlv(tlvlist, 0x0004, 1)) - errurl = aim_gettlv_str(tlvlist, 0x0004, 1); - - /* - * BOS server address. - */ - if (aim_gettlv(tlvlist, 0x0005, 1)) - bosip = aim_gettlv_str(tlvlist, 0x0005, 1); - - /* - * Authorization cookie. - */ - if (aim_gettlv(tlvlist, 0x0006, 1)) { - struct aim_tlv_t *tmptlv; - - tmptlv = aim_gettlv(tlvlist, 0x0006, 1); - - if ((cookie = malloc(tmptlv->length))) - memcpy(cookie, tmptlv->value, tmptlv->length); - } - - /* - * The email address attached to this account - * Not available for ICQ logins. - */ - if (aim_gettlv(tlvlist, 0x0011, 1)) - email = aim_gettlv_str(tlvlist, 0x0011, 1); - - /* - * The registration status. (Not real sure what it means.) - * Not available for ICQ 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. - * - */ - if (aim_gettlv(tlvlist, 0x0013, 1)) - regstatus = aim_gettlv16(tlvlist, 0x0013, 1); - - if (aim_gettlv(tlvlist, 0x0040, 1)) - latestbetabuild = aim_gettlv32(tlvlist, 0x0040, 1); - if (aim_gettlv(tlvlist, 0x0041, 1)) - latestbetaurl = aim_gettlv_str(tlvlist, 0x0041, 1); - if (aim_gettlv(tlvlist, 0x0042, 1)) - latestbetainfo = aim_gettlv_str(tlvlist, 0x0042, 1); - if (aim_gettlv(tlvlist, 0x0043, 1)) - latestbeta = aim_gettlv_str(tlvlist, 0x0043, 1); - if (aim_gettlv(tlvlist, 0x0048, 1)) - ; /* no idea what this is */ - - if (aim_gettlv(tlvlist, 0x0044, 1)) - latestbuild = aim_gettlv32(tlvlist, 0x0044, 1); - if (aim_gettlv(tlvlist, 0x0045, 1)) - latestreleaseurl = aim_gettlv_str(tlvlist, 0x0045, 1); - if (aim_gettlv(tlvlist, 0x0046, 1)) - latestreleaseinfo = aim_gettlv_str(tlvlist, 0x0046, 1); - if (aim_gettlv(tlvlist, 0x0047, 1)) - latestrelease = aim_gettlv_str(tlvlist, 0x0047, 1); - if (aim_gettlv(tlvlist, 0x0049, 1)) - ; /* no idea what this is */ - - - if ((userfunc = aim_callhandler(command->conn, 0x0017, 0x0003))) - ret = userfunc(sess, command, sn, errorcode, errurl, regstatus, email, bosip, cookie, latestrelease, latestbuild, latestreleaseurl, latestreleaseinfo, latestbeta, latestbetabuild, latestbetaurl, latestbetainfo); - - - if (sn) - free(sn); - if (bosip) - free(bosip); - if (errurl) - free(errurl); - if (email) - free(email); - if (cookie) - free(cookie); - if (latestrelease) - free(latestrelease); - if (latestreleaseurl) - free(latestreleaseurl); - if (latestbeta) - free(latestbeta); - if (latestbetaurl) - free(latestbetaurl); - if (latestreleaseinfo) - free(latestreleaseinfo); - if (latestbetainfo) - free(latestbetainfo); - - aim_freetlvchain(&tlvlist); - - return ret; -} - -/* - * 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. - * - */ -faim_internal int aim_authkeyparse(struct aim_session_t *sess, struct command_rx_struct *command) -{ - unsigned char *key; - int keylen; - int ret = 1; - rxcallback_t userfunc; - - keylen = aimutil_get16(command->data+10); - if (!(key = malloc(keylen+1))) - return ret; - memcpy(key, command->data+12, keylen); - key[keylen] = '\0'; - - if ((userfunc = aim_callhandler(command->conn, 0x0017, 0x0007))) - ret = userfunc(sess, command, (char *)key); - - free(key); - - return ret; -} - -/* - * Generate an authorization response. - * - * You probably don't want this unless you're writing an AIM server. - * - */ -faim_export unsigned long aim_sendauthresp(struct aim_session_t *sess, - struct aim_conn_t *conn, - char *sn, int errorcode, - char *errorurl, char *bosip, - char *cookie, char *email, - int regstatus) -{ - struct command_tx_struct *tx; - struct aim_tlvlist_t *tlvlist = NULL; - - if (!(tx = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0004, conn, 1152))) - return -1; - - tx->lock = 1; - - if (sn) - aim_addtlvtochain_str(&tlvlist, 0x0001, sn, strlen(sn)); - else - aim_addtlvtochain_str(&tlvlist, 0x0001, sess->sn, strlen(sess->sn)); - - if (errorcode) { - aim_addtlvtochain16(&tlvlist, 0x0008, errorcode); - aim_addtlvtochain_str(&tlvlist, 0x0004, errorurl, strlen(errorurl)); - } else { - aim_addtlvtochain_str(&tlvlist, 0x0005, bosip, strlen(bosip)); - aim_addtlvtochain_str(&tlvlist, 0x0006, cookie, AIM_COOKIELEN); - aim_addtlvtochain_str(&tlvlist, 0x0011, email, strlen(email)); - aim_addtlvtochain16(&tlvlist, 0x0013, (unsigned short)regstatus); - } - - tx->commandlen = aim_writetlvchain(tx->data, tx->commandlen, &tlvlist); - tx->lock = 0; - - return aim_tx_enqueue(sess, tx); -} - -/* - * Generate a random cookie. (Non-client use only) - */ -faim_export int aim_gencookie(unsigned char *buf) -{ - int i; - - srand(time(NULL)); - - for (i=0; i < AIM_COOKIELEN; i++) - buf[i] = 1+(int) (256.0*rand()/(RAND_MAX+0.0)); - - return i; -} - -/* - * Send Server Ready. (Non-client) - */ -faim_export int aim_sendserverready(struct aim_session_t *sess, struct aim_conn_t *conn) -{ - struct command_tx_struct *tx; - int i = 0; - - if (!(tx = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+0x22))) - return -1; - - tx->lock = 1; - - i += aim_putsnac(tx->data, 0x0001, 0x0003, 0x0000, sess->snac_nextid++); - - i += aimutil_put16(tx->data+i, 0x0001); - i += aimutil_put16(tx->data+i, 0x0002); - i += aimutil_put16(tx->data+i, 0x0003); - i += aimutil_put16(tx->data+i, 0x0004); - i += aimutil_put16(tx->data+i, 0x0006); - i += aimutil_put16(tx->data+i, 0x0008); - i += aimutil_put16(tx->data+i, 0x0009); - i += aimutil_put16(tx->data+i, 0x000a); - i += aimutil_put16(tx->data+i, 0x000b); - i += aimutil_put16(tx->data+i, 0x000c); - i += aimutil_put16(tx->data+i, 0x0013); - i += aimutil_put16(tx->data+i, 0x0015); - - tx->commandlen = i; - tx->lock = 0; - return aim_tx_enqueue(sess, tx); -} - - -/* - * Send service redirect. (Non-Client) - */ -faim_export unsigned long aim_sendredirect(struct aim_session_t *sess, - struct aim_conn_t *conn, - unsigned short servid, - char *ip, - char *cookie) -{ - struct command_tx_struct *tx; - struct aim_tlvlist_t *tlvlist = NULL; - int i = 0; - - if (!(tx = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 1152))) - return -1; - - tx->lock = 1; - - i += aim_putsnac(tx->data+i, 0x0001, 0x0005, 0x0000, 0x00000000); - - aim_addtlvtochain16(&tlvlist, 0x000d, servid); - aim_addtlvtochain_str(&tlvlist, 0x0005, ip, strlen(ip)); - aim_addtlvtochain_str(&tlvlist, 0x0006, cookie, AIM_COOKIELEN); - - tx->commandlen = aim_writetlvchain(tx->data+i, tx->commandlen-i, &tlvlist)+i; - aim_freetlvchain(&tlvlist); - - tx->lock = 0; - return aim_tx_enqueue(sess, tx); -}
--- a/libfaim/aim_logoff.c Sun Mar 04 22:37:18 2001 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,21 +0,0 @@ -/* - * aim_logoff.c - * - * - */ - -#include <faim/aim.h> - -/* - * aim_logoff() - * - * Closes -ALL- open connections. - * - */ -int aim_logoff(struct aim_session_t *sess) -{ - aim_connrst(sess); /* in case we want to connect again */ - - return 0; - -}
--- a/libfaim/aim_meta.c Sun Mar 04 22:37:18 2001 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +0,0 @@ -/* - * Administrative things for libfaim. - * - * Changes by EWarmenhoven Wed May 31 00:31:52 UTC 2000 - * - I don't wanna use aim_buildcode! :) I'll put the things that get made - * by the .sh file into Makefile.am and hopefully that'll work better. - * - */ - -#include <faim/aim.h> -/* #include <aim_buildcode.h> generated by mkbuildinfo.sh */ - -faim_export char *aim_getbuilddate(void) -{ - return AIM_BUILDDATE; -} - -faim_export char *aim_getbuildtime(void) -{ - return AIM_BUILDTIME; -} - -faim_export char *aim_getbuildstring(void) -{ - static char string[100]; - - snprintf(string, 99, "%d.%d.%d-%s%s", - FAIM_VERSION_MAJOR, - FAIM_VERSION_MINOR, - FAIM_VERSION_MINORMINOR, - aim_getbuilddate(), - aim_getbuildtime()); - return string; -} - -#if debug > 0 -faim_internal void faimdprintf(int dlevel, const char *format, ...) -{ - if (dlevel >= debug) { - va_list ap; - - va_start(ap, format); - vfprintf(stderr, format, ap); - va_end(ap); - } - return; -} -#else -faim_internal void faimdprintf(int dlevel, const char *format, ...) -{ - return; -} -#endif
--- a/libfaim/aim_misc.c Sun Mar 04 22:37:18 2001 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,888 +0,0 @@ - -/* - * aim_misc.c - * - * TODO: Seperate a lot of this into an aim_bos.c. - * - * Other things... - * - * - Idle setting - * - * - */ - -#include <faim/aim.h> - -/* - * aim_bos_setidle() - * - * Should set your current idle time in seconds. Idealy, OSCAR should - * do this for us. But, it doesn't. The client must call this to set idle - * time. - * - */ -faim_export unsigned long aim_bos_setidle(struct aim_session_t *sess, - struct aim_conn_t *conn, - u_long idletime) -{ - return aim_genericreq_l(sess, conn, 0x0001, 0x0011, &idletime); -} - - -/* - * aim_bos_changevisibility(conn, changtype, namelist) - * - * Changes your visibility depending on changetype: - * - * AIM_VISIBILITYCHANGE_PERMITADD: Lets provided list of names see you - * AIM_VISIBILITYCHANGE_PERMIDREMOVE: Removes listed names from permit list - * AIM_VISIBILITYCHANGE_DENYADD: Hides you from provided list of names - * AIM_VISIBILITYCHANGE_DENYREMOVE: Lets list see you again - * - * list should be a list of - * screen names in the form "Screen Name One&ScreenNameTwo&" etc. - * - * Equivelents to options in WinAIM: - * - Allow all users to contact me: Send an AIM_VISIBILITYCHANGE_DENYADD - * with only your name on it. - * - Allow only users on my Buddy List: Send an - * AIM_VISIBILITYCHANGE_PERMITADD with the list the same as your - * buddy list - * - Allow only the uesrs below: Send an AIM_VISIBILITYCHANGE_PERMITADD - * with everyone listed that you want to see you. - * - Block all users: Send an AIM_VISIBILITYCHANGE_PERMITADD with only - * yourself in the list - * - Block the users below: Send an AIM_VISIBILITYCHANGE_DENYADD with - * the list of users to be blocked - * - * - */ -faim_export unsigned long aim_bos_changevisibility(struct aim_session_t *sess, - struct aim_conn_t *conn, - int changetype, - char *denylist) -{ - struct command_tx_struct *newpacket; - int packlen = 0; - u_short subtype; - - char *localcpy = NULL; - char *tmpptr = NULL; - int i,j; - int listcount; - - if (!denylist) - return 0; - - localcpy = (char *) malloc(strlen(denylist)+1); - memcpy(localcpy, denylist, strlen(denylist)+1); - - listcount = aimutil_itemcnt(localcpy, '&'); - packlen = aimutil_tokslen(localcpy, 99, '&') + listcount + 9; - - if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, packlen))) - return -1; - - newpacket->lock = 1; - - switch(changetype) - { - case AIM_VISIBILITYCHANGE_PERMITADD: subtype = 0x05; break; - case AIM_VISIBILITYCHANGE_PERMITREMOVE: subtype = 0x06; break; - case AIM_VISIBILITYCHANGE_DENYADD: subtype = 0x07; break; - case AIM_VISIBILITYCHANGE_DENYREMOVE: subtype = 0x08; break; - default: - free(newpacket->data); - free(newpacket); - return 0; - } - - /* We actually DO NOT send a SNAC ID with this one! */ - aim_putsnac(newpacket->data, 0x0009, subtype, 0x00, 0); - - j = 10; /* the next byte */ - - for (i=0; (i < (listcount - 1)) && (i < 99); i++) - { - tmpptr = aimutil_itemidx(localcpy, i, '&'); - - newpacket->data[j] = strlen(tmpptr); - memcpy(&(newpacket->data[j+1]), tmpptr, strlen(tmpptr)); - j += strlen(tmpptr)+1; - free(tmpptr); - } - free(localcpy); - - newpacket->lock = 0; - - aim_tx_enqueue(sess, newpacket); - - return (sess->snac_nextid); /* dont increment */ - -} - - - -/* - * aim_bos_setbuddylist(buddylist) - * - * This just builds the "set buddy list" command then queues it. - * - * buddy_list = "Screen Name One&ScreenNameTwo&"; - * - * TODO: Clean this up. - * - * XXX: I can't stress the TODO enough. - * - */ -faim_export unsigned long aim_bos_setbuddylist(struct aim_session_t *sess, - struct aim_conn_t *conn, - char *buddy_list) -{ - int i, j; - - struct command_tx_struct *newpacket; - - int len = 0; - - char *localcpy = NULL; - char *tmpptr = NULL; - - len = 10; /* 10B SNAC headers */ - - if (!buddy_list || !(localcpy = (char *) malloc(strlen(buddy_list)+1))) - return -1; - strncpy(localcpy, buddy_list, strlen(buddy_list)+1); - - i = 0; - tmpptr = strtok(localcpy, "&"); - while ((tmpptr != NULL) && (i < 150)) { -#if debug > 0 - printf("---adding %d: %s (%d)\n", i, tmpptr, strlen(tmpptr)); -#endif - len += 1+strlen(tmpptr); - i++; - tmpptr = strtok(NULL, "&"); - } -#if debug > 0 - printf("*** send buddy list len: %d (%x)\n", len, len); -#endif - - if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, len))) - return -1; - - newpacket->lock = 1; - - aim_putsnac(newpacket->data, 0x0003, 0x0004, 0x0000, 0); - - j = 10; /* the next byte */ - - strncpy(localcpy, buddy_list, strlen(buddy_list)+1); - i = 0; - tmpptr = strtok(localcpy, "&"); - while ((tmpptr != NULL) & (i < 150)) { -#if debug > 0 - printf("---adding %d: %s (%d)\n", i, tmpptr, strlen(tmpptr)); -#endif - newpacket->data[j] = strlen(tmpptr); - memcpy(&(newpacket->data[j+1]), tmpptr, strlen(tmpptr)); - j += 1+strlen(tmpptr); - i++; - tmpptr = strtok(NULL, "&"); - } - - newpacket->lock = 0; - - aim_tx_enqueue(sess, newpacket); - - free(localcpy); - - return (sess->snac_nextid); -} - -/* - * aim_bos_setprofile(profile) - * - * Gives BOS your profile. - * - * - */ -faim_export unsigned long aim_bos_setprofile(struct aim_session_t *sess, - struct aim_conn_t *conn, - char *profile, - char *awaymsg, - unsigned short caps) -{ - struct command_tx_struct *newpacket; - int i = 0, tmp, caplen; - - if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 1152+strlen(profile)+1+(awaymsg?strlen(awaymsg):0)))) - return -1; - - i += aim_putsnac(newpacket->data, 0x0002, 0x004, 0x0000, sess->snac_nextid); - i += aim_puttlv_str(newpacket->data+i, 0x0001, strlen("text/x-aolrtf; charset=\"us-ascii\""), "text/x-aolrtf; charset=\"us-ascii\""); - i += aim_puttlv_str(newpacket->data+i, 0x0002, strlen(profile), profile); - /* why do we send this twice? */ - i += aim_puttlv_str(newpacket->data+i, 0x0003, strlen("text/x-aolrtf; charset=\"us-ascii\""), "text/x-aolrtf; charset=\"us-ascii\""); - - /* Away message -- we send this no matter what, even if its blank */ - if (awaymsg) - i += aim_puttlv_str(newpacket->data+i, 0x0004, strlen(awaymsg), awaymsg); - else - i += aim_puttlv_str(newpacket->data+i, 0x0004, 0x0000, NULL); - - /* Capability information. */ - - tmp = (i += aimutil_put16(newpacket->data+i, 0x0005)); - i += aimutil_put16(newpacket->data+i, 0x0000); /* rewritten later */ - i += (caplen = aim_putcap(newpacket->data+i, 512, caps)); - aimutil_put16(newpacket->data+tmp, caplen); /* rewrite TLV size */ - - newpacket->commandlen = i; - aim_tx_enqueue(sess, newpacket); - - return (sess->snac_nextid++); -} - -/* - * aim_bos_setgroupperm(mask) - * - * Set group permisson mask. Normally 0x1f (all classes). - * - * The group permission mask allows you to keep users of a certain - * class or classes from talking to you. The mask should be - * a bitwise OR of all the user classes you want to see you. - * - */ -faim_export unsigned long aim_bos_setgroupperm(struct aim_session_t *sess, - struct aim_conn_t *conn, - u_long mask) -{ - return aim_genericreq_l(sess, conn, 0x0009, 0x0004, &mask); -} - -faim_internal int aim_parse_bosrights(struct aim_session_t *sess, - struct command_rx_struct *command, ...) -{ - rxcallback_t userfunc = NULL; - int ret=1; - struct aim_tlvlist_t *tlvlist; - unsigned short maxpermits = 0, maxdenies = 0; - - /* - * TLVs follow - */ - if (!(tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10))) - return ret; - - /* - * TLV type 0x0001: Maximum number of buddies on permit list. - */ - if (aim_gettlv(tlvlist, 0x0001, 1)) - maxpermits = aim_gettlv16(tlvlist, 0x0001, 1); - - /* - * TLV type 0x0002: Maximum number of buddies on deny list. - * - */ - if (aim_gettlv(tlvlist, 0x0002, 1)) - maxdenies = aim_gettlv16(tlvlist, 0x0002, 1); - - if ((userfunc = aim_callhandler(command->conn, 0x0009, 0x0003))) - ret = userfunc(sess, command, maxpermits, maxdenies); - - aim_freetlvchain(&tlvlist); - - return ret; -} - -/* - * aim_bos_clientready() - * - * Send Client Ready. - * - */ -faim_export unsigned long aim_bos_clientready(struct aim_session_t *sess, - struct aim_conn_t *conn) -{ -#define AIM_TOOL_JAVA 0x0001 -#define AIM_TOOL_MAC 0x0002 -#define AIM_TOOL_WIN16 0x0003 -#define AIM_TOOL_WIN32 0x0004 -#define AIM_TOOL_MAC68K 0x0005 -#define AIM_TOOL_MACPPC 0x0006 - struct aim_tool_version { - unsigned short group; - unsigned short version; - unsigned short tool; - unsigned short toolversion; - } tools[] = { - {0x0001, 0x0003, AIM_TOOL_WIN32, 0x0686}, - {0x0002, 0x0001, AIM_TOOL_WIN32, 0x0001}, - {0x0003, 0x0001, AIM_TOOL_WIN32, 0x0001}, - {0x0004, 0x0001, AIM_TOOL_WIN32, 0x0001}, - {0x0006, 0x0001, AIM_TOOL_WIN32, 0x0001}, - {0x0008, 0x0001, AIM_TOOL_WIN32, 0x0001}, - {0x0009, 0x0001, AIM_TOOL_WIN32, 0x0001}, - {0x000a, 0x0001, AIM_TOOL_WIN32, 0x0001}, - {0x000b, 0x0001, AIM_TOOL_WIN32, 0x0001} - }; - int i,j; - struct command_tx_struct *newpacket; - int toolcount = sizeof(tools)/sizeof(struct aim_tool_version); - - if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 1152))) - return -1; - - newpacket->lock = 1; - - i = aim_putsnac(newpacket->data, 0x0001, 0x0002, 0x0000, sess->snac_nextid); - aim_cachesnac(sess, 0x0001, 0x0002, 0x0000, NULL, 0); - - for (j = 0; j < toolcount; j++) { - i += aimutil_put16(newpacket->data+i, tools[j].group); - i += aimutil_put16(newpacket->data+i, tools[j].version); - i += aimutil_put16(newpacket->data+i, tools[j].tool); - i += aimutil_put16(newpacket->data+i, tools[j].toolversion); - } - - newpacket->commandlen = i; - newpacket->lock = 0; - - aim_tx_enqueue(sess, newpacket); - - return sess->snac_nextid; -} - -/* - * Request Rate Information. - * - */ -faim_export unsigned long aim_bos_reqrate(struct aim_session_t *sess, - struct aim_conn_t *conn) -{ - return aim_genericreq_n(sess, conn, 0x0001, 0x0006); -} - -/* - * Rate Information Response Acknowledge. - * - */ -faim_export unsigned long aim_bos_ackrateresp(struct aim_session_t *sess, - struct aim_conn_t *conn) -{ - struct command_tx_struct *newpacket; - int packlen = 20, i=0; - - if(!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, packlen))) - return (sess->snac_nextid); - - newpacket->lock = 1; - - i = aim_putsnac(newpacket->data, 0x0001, 0x0008, 0x0000, 0); - i += aimutil_put16(newpacket->data+i, 0x0001); - i += aimutil_put16(newpacket->data+i, 0x0002); - i += aimutil_put16(newpacket->data+i, 0x0003); - i += aimutil_put16(newpacket->data+i, 0x0004); - i += aimutil_put16(newpacket->data+i, 0x0005); - - newpacket->commandlen = i; - newpacket->lock = 0; - - aim_tx_enqueue(sess, newpacket); - - return (sess->snac_nextid); -} - -/* - * aim_bos_setprivacyflags() - * - * Sets privacy flags. Normally 0x03. - * - * Bit 1: Allows other AIM users to see how long you've been idle. - * Bit 2: Allows other AIM users to see how long you've been a member. - * - */ -faim_export unsigned long aim_bos_setprivacyflags(struct aim_session_t *sess, - struct aim_conn_t *conn, - u_long flags) -{ - return aim_genericreq_l(sess, conn, 0x0001, 0x0014, &flags); -} - -/* - * aim_bos_reqpersonalinfo() - * - * Requests the current user's information. Can't go generic on this one - * because aparently it uses SNAC flags. - * - */ -faim_export unsigned long aim_bos_reqpersonalinfo(struct aim_session_t *sess, - struct aim_conn_t *conn) -{ - return aim_genericreq_n(sess, conn, 0x0001, 0x000e); -} - -faim_export unsigned long aim_setversions(struct aim_session_t *sess, - struct aim_conn_t *conn) -{ - struct command_tx_struct *newpacket; - int i; - - if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10 + (4*12)))) - return -1; - - newpacket->lock = 1; - - i = aim_putsnac(newpacket->data, 0x0001, 0x0017, 0x0000, sess->snac_nextid); - aim_cachesnac(sess, 0x0001, 0x0017, 0x0000, NULL, 0); - - i += aimutil_put16(newpacket->data+i, 0x0001); - i += aimutil_put16(newpacket->data+i, 0x0003); - - i += aimutil_put16(newpacket->data+i, 0x0013); - i += aimutil_put16(newpacket->data+i, 0x0001); - - i += aimutil_put16(newpacket->data+i, 0x0002); - i += aimutil_put16(newpacket->data+i, 0x0001); - - i += aimutil_put16(newpacket->data+i, 0x0003); - i += aimutil_put16(newpacket->data+i, 0x0001); - - i += aimutil_put16(newpacket->data+i, 0x0004); - i += aimutil_put16(newpacket->data+i, 0x0001); - - i += aimutil_put16(newpacket->data+i, 0x0006); - i += aimutil_put16(newpacket->data+i, 0x0001); - - i += aimutil_put16(newpacket->data+i, 0x0008); - i += aimutil_put16(newpacket->data+i, 0x0001); - - i += aimutil_put16(newpacket->data+i, 0x0009); - i += aimutil_put16(newpacket->data+i, 0x0001); - - i += aimutil_put16(newpacket->data+i, 0x000a); - i += aimutil_put16(newpacket->data+i, 0x0001); - - i += aimutil_put16(newpacket->data+i, 0x000b); - i += aimutil_put16(newpacket->data+i, 0x0001); - - i += aimutil_put16(newpacket->data+i, 0x000c); - i += aimutil_put16(newpacket->data+i, 0x0001); - - newpacket->commandlen = i; - newpacket->lock = 0; - aim_tx_enqueue(sess, newpacket); - - return sess->snac_nextid; -} - - -/* - * aim_bos_reqservice(serviceid) - * - * Service request. - * - */ -faim_export unsigned long aim_bos_reqservice(struct aim_session_t *sess, - struct aim_conn_t *conn, - u_short serviceid) -{ - return aim_genericreq_s(sess, conn, 0x0001, 0x0004, &serviceid); -} - -/* - * aim_bos_nop() - * - * No-op. WinAIM sends these every 4min or so to keep - * the connection alive. Its not real necessary. - * - */ -faim_export unsigned long aim_bos_nop(struct aim_session_t *sess, - struct aim_conn_t *conn) -{ - return aim_genericreq_n(sess, conn, 0x0001, 0x0016); -} - -/* - * aim_flap_nop() - * - * No-op. WinAIM 4.x sends these _every minute_ to keep - * the connection alive. - */ -faim_export unsigned long aim_flap_nop(struct aim_session_t *sess, - struct aim_conn_t *conn) -{ - struct command_tx_struct *newpacket; - - if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0005, conn, 0))) - return sess->snac_nextid; - - newpacket->lock = 1; - newpacket->commandlen = 0; - newpacket->lock = 0; - - aim_tx_enqueue(sess, newpacket); - - return (sess->snac_nextid); -} - -/* - * aim_bos_reqrights() - * - * Request BOS rights. - * - */ -faim_export unsigned long aim_bos_reqrights(struct aim_session_t *sess, - struct aim_conn_t *conn) -{ - return aim_genericreq_n(sess, conn, 0x0009, 0x0002); -} - -/* - * aim_bos_reqbuddyrights() - * - * Request Buddy List rights. - * - */ -faim_export unsigned long aim_bos_reqbuddyrights(struct aim_session_t *sess, - struct aim_conn_t *conn) -{ - return aim_genericreq_n(sess, conn, 0x0003, 0x0002); -} - -/* - * aim_send_warning(struct aim_session_t *sess, - * struct aim_conn_t *conn, char *destsn, int anon) - * send a warning to destsn. - * anon is anonymous or not; - * AIM_WARN_ANON anonymous - * - * returns -1 on error (couldn't alloc packet), next snacid on success. - * - */ -faim_export int aim_send_warning(struct aim_session_t *sess, struct aim_conn_t *conn, char *destsn, int anon) -{ - struct command_tx_struct *newpacket; - int curbyte; - - if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, strlen(destsn)+13))) - return -1; - - newpacket->lock = 1; - - curbyte = 0; - curbyte += aim_putsnac(newpacket->data+curbyte, - 0x0004, 0x0008, 0x0000, sess->snac_nextid); - - curbyte += aimutil_put16(newpacket->data+curbyte, (anon & AIM_WARN_ANON)?1:0); - - curbyte += aimutil_put8(newpacket->data+curbyte, strlen(destsn)); - - curbyte += aimutil_putstr(newpacket->data+curbyte, destsn, strlen(destsn)); - - newpacket->commandlen = curbyte; - newpacket->lock = 0; - - aim_tx_enqueue(sess, newpacket); - - return (sess->snac_nextid++); -} - -/* - * aim_debugconn_sendconnect() - * - * For aimdebugd. If you don't know what it is, you don't want to. - */ -faim_export unsigned long aim_debugconn_sendconnect(struct aim_session_t *sess, - struct aim_conn_t *conn) -{ - return aim_genericreq_n(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_DEBUGCONN_CONNECT); -} - -/* - * Generic routine for sending commands. - * - * - * I know I can do this in a smarter way...but I'm not thinking straight - * right now... - * - * I had one big function that handled all three cases, but then it broke - * and I split it up into three. But then I fixed it. I just never went - * back to the single. I don't see any advantage to doing it either way. - * - */ -faim_internal unsigned long aim_genericreq_n(struct aim_session_t *sess, - struct aim_conn_t *conn, - u_short family, u_short subtype) -{ - struct command_tx_struct *newpacket; - - if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10))) - return 0; - - newpacket->lock = 1; - - aim_putsnac(newpacket->data, family, subtype, 0x0000, sess->snac_nextid); - - aim_cachesnac(sess, family, subtype, 0x0000, NULL, 0); - - aim_tx_enqueue(sess, newpacket); - return sess->snac_nextid; -} - -/* - * - * - */ -faim_internal unsigned long aim_genericreq_l(struct aim_session_t *sess, - struct aim_conn_t *conn, - u_short family, u_short subtype, - u_long *longdata) -{ - struct command_tx_struct *newpacket; - u_long newlong; - - /* If we don't have data, there's no reason to use this function */ - if (!longdata) - return aim_genericreq_n(sess, conn, family, subtype); - - if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+sizeof(u_long)))) - return -1; - - newpacket->lock = 1; - - aim_putsnac(newpacket->data, family, subtype, 0x0000, sess->snac_nextid); - aim_cachesnac(sess, family, subtype, 0x0000, NULL, 0); - - /* copy in data */ - newlong = htonl(*longdata); - memcpy(&(newpacket->data[10]), &newlong, sizeof(u_long)); - - aim_tx_enqueue(sess, newpacket); - return sess->snac_nextid; -} - -faim_internal unsigned long aim_genericreq_s(struct aim_session_t *sess, - struct aim_conn_t *conn, - u_short family, u_short subtype, - u_short *shortdata) -{ - struct command_tx_struct *newpacket; - u_short newshort; - - /* If we don't have data, there's no reason to use this function */ - if (!shortdata) - return aim_genericreq_n(sess, conn, family, subtype); - - if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+sizeof(u_short)))) - return -1; - - newpacket->lock = 1; - - aim_putsnac(newpacket->data, family, subtype, 0x0000, sess->snac_nextid); - aim_cachesnac(sess, family, subtype, 0x0000, NULL, 0); - - /* copy in data */ - newshort = htons(*shortdata); - memcpy(&(newpacket->data[10]), &newshort, sizeof(u_short)); - - aim_tx_enqueue(sess, newpacket); - return sess->snac_nextid; -} - -/* - * aim_bos_reqlocaterights() - * - * Request Location services rights. - * - */ -faim_export unsigned long aim_bos_reqlocaterights(struct aim_session_t *sess, - struct aim_conn_t *conn) -{ - return aim_genericreq_n(sess, conn, 0x0002, 0x0002); -} - -/* -* aim_bos_reqicbmparaminfo() - * - * Request ICBM parameter information. - * - */ -faim_export unsigned long aim_bos_reqicbmparaminfo(struct aim_session_t *sess, - struct aim_conn_t *conn) -{ - return aim_genericreq_n(sess, conn, 0x0004, 0x0004); -} - -/* - * Add ICBM parameter? Huh? - */ -faim_export unsigned long aim_addicbmparam(struct aim_session_t *sess, - struct aim_conn_t *conn) -{ - struct command_tx_struct *newpacket; - int packlen = 10+16, i=0; - - if(!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, packlen))) - return (sess->snac_nextid); - - newpacket->lock = 1; - - i = aim_putsnac(newpacket->data, 0x0004, 0x0002, 0x0000, sess->snac_nextid); - aim_cachesnac(sess, 0x0004, 0x0002, 0x0000, NULL, 0); - - i += aimutil_put16(newpacket->data+i, 0x0000); - i += aimutil_put16(newpacket->data+i, 0x0000); - i += aimutil_put16(newpacket->data+i, 0x0003); - i += aimutil_put16(newpacket->data+i, 0x1f40); - i += aimutil_put16(newpacket->data+i, 0x03e7); - i += aimutil_put16(newpacket->data+i, 0x03e7); - i += aimutil_put16(newpacket->data+i, 0x0000); - i += aimutil_put16(newpacket->data+i, 0x0000); - - aim_tx_enqueue(sess, newpacket); - - return sess->snac_nextid; -} - -/* - * Set directory profile data (not the same as aim_bos_setprofile!) - */ -faim_export unsigned long aim_setdirectoryinfo(struct aim_session_t *sess, struct aim_conn_t *conn, char *first, char *middle, char *last, char *maiden, char *nickname, char *street, char *city, char *state, char *zip, int country, unsigned short privacy) -{ - struct command_tx_struct *newpacket; - int packlen = 0, i = 0; - - packlen += 2+2+2; - - if(first) /* TLV 0001 */ - packlen += (strlen(first) + 4); - if(middle) - packlen += (strlen(middle) + 4); - if(last) - packlen += (strlen(last) + 4); - if(maiden) - packlen += (strlen(maiden) + 4); - if(nickname) - packlen += (strlen(nickname) + 4); - if(street) - packlen += (strlen(street) + 4); - if(state) - packlen += (strlen(state) + 4); - if(city) - packlen += (strlen(city) + 4); - if(zip) - packlen += (strlen(zip) + 4); - - if(!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, packlen+10))) - return -1; - - newpacket->lock = 1; - - i = aim_putsnac(newpacket->data, 0x0002, 0x0009, 0x0000, 0); - - /* 000a/0002: privacy: 1 to allow search/disp, 0 to disallow */ - i += aim_puttlv_16(newpacket->data+i, 0x000a, privacy); - - - if (first) - i += aim_puttlv_str(newpacket->data+i, 0x0001, strlen(first), first); - if (middle) - i += aim_puttlv_str(newpacket->data+i, 0x0003, strlen(middle), middle); - if (last) - i += aim_puttlv_str(newpacket->data+i, 0x0002, strlen(last), last); - if (maiden) - i += aim_puttlv_str(newpacket->data+i, 0x0004, strlen(maiden), maiden); - if (nickname) - i += aim_puttlv_str(newpacket->data+i, 0x000c, strlen(nickname), nickname); - if (street) - i += aim_puttlv_str(newpacket->data+i, 0x0021, strlen(street), street); - if (city) - i += aim_puttlv_str(newpacket->data+i, 0x0008, strlen(city), city); - if (state) - i += aim_puttlv_str(newpacket->data+i, 0x0007, strlen(state), state); - if (zip) - i += aim_puttlv_str(newpacket->data+i, 0x000d, strlen(zip), zip); - - newpacket->commandlen = i; - newpacket->lock = 0; - - aim_tx_enqueue(sess, newpacket); - - return(sess->snac_nextid); -} - -faim_export unsigned long aim_setuserinterests(struct aim_session_t *sess, struct aim_conn_t *conn, char *interest1, char *interest2, char *interest3, char *interest4, char *interest5, unsigned short privacy) -{ - struct command_tx_struct *newpacket; - int packlen = 0, i = 0; - - packlen += 2+2+2; - - if(interest1) - packlen += (strlen(interest1) + 4); - if(interest2) - packlen += (strlen(interest2) + 4); - if(interest3) - packlen += (strlen(interest3) + 4); - if(interest4) - packlen += (strlen(interest4) + 4); - if(interest5) - packlen += (strlen(interest5) + 4) ; - - - if(!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, packlen+10))) - return -1; - - newpacket->lock = 1; - - i = aim_putsnac(newpacket->data, 0x0002, 0x000f, 0x0000, 0); - - /* 000a/0002: 0000 ?? ?privacy? */ - i += aim_puttlv_16(newpacket->data+i, 0x000a, privacy); - - if(interest1) - i += aim_puttlv_str(newpacket->data+i, 0x000b, strlen(interest1), interest1); - if(interest2) - i += aim_puttlv_str(newpacket->data+i, 0x000b, strlen(interest2), interest2); - if(interest3) - i += aim_puttlv_str(newpacket->data+i, 0x000b, strlen(interest3), interest3); - if(interest4) - i += aim_puttlv_str(newpacket->data+i, 0x000b, strlen(interest4), interest4); - if(interest5) - i += aim_puttlv_str(newpacket->data+i, 0x000b, strlen(interest1), interest5); - - newpacket->commandlen = i; - newpacket->lock = 0; - - aim_tx_enqueue(sess, newpacket); - - return(sess->snac_nextid); -} - -faim_export unsigned long aim_icq_setstatus(struct aim_session_t *sess, - struct aim_conn_t *conn, - unsigned long status) -{ - struct command_tx_struct *newpacket; - int i; - unsigned long data; - - data = 0x00030000 | status; /* yay for error checking ;^) */ - - if(!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10 + 4))) - return -1; - - newpacket->lock = 1; - - i = aim_putsnac(newpacket->data, 0x0001, 0x001e, 0x0000, 0x0000001e); - i += aim_puttlv_32(newpacket->data+i, 0x0006, data); - - newpacket->commandlen = i; - newpacket->lock = 0; - - aim_tx_enqueue(sess, newpacket); - - return(sess->snac_nextid); -}
--- a/libfaim/aim_msgcookie.c Sun Mar 04 22:37:18 2001 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,203 +0,0 @@ -/* - * Cookie Caching stuff. Adam wrote this, apparently just some - * derivatives of n's SNAC work. I cleaned it up, added comments. - * - * I'm going to rewrite this stuff eventually, honest. -jbm - * - */ - -/* - * I'm assuming that cookies are type-specific. that is, we can have - * "1234578" for type 1 and type 2 concurrently. if i'm wrong, then we - * lose some error checking. if we assume cookies are not type-specific and are - * wrong, we get quirky behavior when cookies step on each others' toes. - */ - -#include <faim/aim.h> - -/* - * aim_cachecookie: - * appends a cookie to the cookie list for sess. - * - if cookie->cookie for type cookie->type is found, addtime is updated. - * - copies cookie struct; you need to free() it afterwards; - * - cookie->data is not copied, but passed along. don't free it. - * - newcook->addtime is updated accordingly; - * - cookie->type is just passed across. - * - * returns -1 on error, 0 on success. - */ -faim_internal int aim_cachecookie(struct aim_session_t *sess, - struct aim_msgcookie_t *cookie) -{ - struct aim_msgcookie_t *newcook; - - if (!cookie) - return -1; - - if( (newcook = aim_checkcookie(sess, cookie->cookie, cookie->type)) ) { - newcook->addtime = time(NULL); - if(cookie->data != newcook->data) { - - printf("faim: cachecookie: matching cookie/type pair " - "%x%x%x%x%x%x%x%x/%x has different *data. free()ing cookie copy..\n", - cookie->cookie[0], cookie->cookie[1], cookie->cookie[2], - cookie->cookie[3], cookie->cookie[4], cookie->cookie[5], - cookie->cookie[6], cookie->cookie[7], cookie->type); - - free(cookie->data); - } - - return 0; - } - - if (!(newcook = malloc(sizeof(struct aim_msgcookie_t)))) - return -1; - memcpy(newcook, cookie, sizeof(struct aim_msgcookie_t)); - newcook->addtime = time(NULL); - - newcook->next = sess->msgcookies; - sess->msgcookies = newcook; - - return 0; -} - -/* - * aim_uncachecookie: - * takes a cookie string and grabs the cookie struct associated with - * it. removes struct from chain. returns the struct if found, or - * NULL on not found. - */ -faim_internal struct aim_msgcookie_t *aim_uncachecookie(struct aim_session_t *sess, unsigned char *cookie, int type) -{ - struct aim_msgcookie_t *cur; - - if (!cookie || !sess->msgcookies) - return NULL; - - if ((sess->msgcookies->type == type) && - (memcmp(sess->msgcookies->cookie, cookie, 8) == 0)) { - struct aim_msgcookie_t *tmp; - - tmp = sess->msgcookies; - sess->msgcookies = sess->msgcookies->next; - - return tmp; - } - - for (cur = sess->msgcookies; cur->next; cur = cur->next) { - if ((cur->next->type == type) && - (memcmp(cur->next->cookie, cookie, 8) == 0)) { - struct aim_msgcookie_t *tmp; - - tmp = cur->next; - cur->next = cur->next->next; - - return tmp; - } - } - - return NULL; -} - -/* - * aim_purgecookies: - * purge out old cookies - * - * finds old cookies, calls uncache on them. - * - * this is highly inefficient, but It Works. and i don't feel like - * totally rewriting this. it might have some concurrency issues as - * well, if i rewrite it. - * - * i'll avoid the puns. - */ - -faim_export int aim_purgecookies(struct aim_session_t *sess, int maxage) -{ - struct aim_msgcookie_t *cur; - time_t curtime; - - curtime = time(NULL); - - for (cur = sess->msgcookies; cur; cur = cur->next) { - if (cur->addtime > (time(NULL) - maxage)) { - struct aim_msgcookie_t *remed = NULL; - -#if 1 - printf("aimmsgcookie: WARNING purged obsolete message cookie %x%x%x%x %x%x%x%x\n", - cur->cookie[0], cur->cookie[1], cur->cookie[2], cur->cookie[3], - cur->cookie[4], cur->cookie[5], cur->cookie[6], cur->cookie[7]); -#endif - - remed = aim_uncachecookie(sess, cur->cookie, cur->type); - if (remed) { - if (remed->data) - free(remed->data); - free(remed); - } - } - } - - return 0; -} - -faim_internal struct aim_msgcookie_t *aim_mkcookie(unsigned char *c, int type, void *data) -{ - struct aim_msgcookie_t *cookie; - - if (!c) - return NULL; - - if (!(cookie = calloc(1, sizeof(struct aim_msgcookie_t)))) - return NULL; - - cookie->data = data; - cookie->type = type; - memcpy(cookie->cookie, c, 8); - - return cookie; -} - -faim_internal struct aim_msgcookie_t *aim_checkcookie(struct aim_session_t *sess, unsigned char *cookie, int type) -{ - struct aim_msgcookie_t *cur; - - for (cur = sess->msgcookies; cur; cur = cur->next) { - if ((cur->type == type) && - (memcmp(cur->cookie, cookie, 8) == 0)) - return cur; - } - - return NULL; -} - -static int aim_freecookie(struct aim_msgcookie_t *cookie) { - return 0; -} - -faim_internal int aim_msgcookie_gettype(int reqclass) { - /* XXX: hokey-assed. needs fixed. */ - switch(reqclass) { - case AIM_CAPS_BUDDYICON: - return AIM_COOKIETYPE_OFTICON; - break; - case AIM_CAPS_VOICE: - return AIM_COOKIETYPE_OFTVOICE; - break; - case AIM_CAPS_IMIMAGE: - return AIM_COOKIETYPE_OFTIMAGE; - break; - case AIM_CAPS_CHAT: - return AIM_COOKIETYPE_CHAT; - break; - case AIM_CAPS_GETFILE: - return AIM_COOKIETYPE_OFTGET; - break; - case AIM_CAPS_SENDFILE: - return AIM_COOKIETYPE_OFTSEND; - break; - default: - return AIM_COOKIETYPE_UNKNOWN; - break; - } -}
--- a/libfaim/aim_rxhandlers.c Sun Mar 04 22:37:18 2001 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,978 +0,0 @@ -/* - * aim_rxhandlers.c - * - * This file contains most all of the incoming packet handlers, along - * with aim_rxdispatch(), the Rx dispatcher. Queue/list management is - * actually done in aim_rxqueue.c. - * - */ - -#include <faim/aim.h> - -/* - * Bleck functions get called when there's no non-bleck functions - * around to cleanup the mess... - */ -faim_internal int bleck(struct aim_session_t *sess,struct command_rx_struct *workingPtr, ...) -{ - u_short family; - u_short subtype; - - u_short maxf; - u_short maxs; - - /* XXX: this is ugly. and big just for debugging. */ - char *literals[14][25] = { - {"Invalid", - NULL - }, - {"General", - "Invalid", - "Error", - "Client Ready", - "Server Ready", - "Service Request", - "Redirect", - "Rate Information Request", - "Rate Information", - "Rate Information Ack", - NULL, - "Rate Information Change", - "Server Pause", - NULL, - "Server Resume", - "Request Personal User Information", - "Personal User Information", - "Evil Notification", - NULL, - "Migration notice", - "Message of the Day", - "Set Privacy Flags", - "Well Known URL", - "NOP" - }, - {"Location", - "Invalid", - "Error", - "Request Rights", - "Rights Information", - "Set user information", - "Request User Information", - "User Information", - "Watcher Sub Request", - "Watcher Notification" - }, - {"Buddy List Management", - "Invalid", - "Error", - "Request Rights", - "Rights Information", - "Add Buddy", - "Remove Buddy", - "Watcher List Query", - "Watcher List Response", - "Watcher SubRequest", - "Watcher Notification", - "Reject Notification", - "Oncoming Buddy", - "Offgoing Buddy" - }, - {"Messeging", - "Invalid", - "Error", - "Add ICBM Parameter", - "Remove ICBM Parameter", - "Request Parameter Information", - "Parameter Information", - "Outgoing Message", - "Incoming Message", - "Evil Request", - "Evil Reply", - "Missed Calls", - "Message Error", - "Host Ack" - }, - {"Advertisements", - "Invalid", - "Error", - "Request Ad", - "Ad Data (GIFs)" - }, - {"Invitation / Client-to-Client", - "Invalid", - "Error", - "Invite a Friend", - "Invitation Ack" - }, - {"Administrative", - "Invalid", - "Error", - "Information Request", - "Information Reply", - "Information Change Request", - "Information Chat Reply", - "Account Confirm Request", - "Account Confirm Reply", - "Account Delete Request", - "Account Delete Reply" - }, - {"Popups", - "Invalid", - "Error", - "Display Popup" - }, - {"BOS", - "Invalid", - "Error", - "Request Rights", - "Rights Response", - "Set group permission mask", - "Add permission list entries", - "Delete permission list entries", - "Add deny list entries", - "Delete deny list entries", - "Server Error" - }, - {"User Lookup", - "Invalid", - "Error", - "Search Request", - "Search Response" - }, - {"Stats", - "Invalid", - "Error", - "Set minimum report interval", - "Report Events" - }, - {"Translate", - "Invalid", - "Error", - "Translate Request", - "Translate Reply", - }, - {"Chat Navigation", - "Invalid", - "Error", - "Request rights", - "Request Exchange Information", - "Request Room Information", - "Request Occupant List", - "Search for Room", - "Outgoing Message", - "Incoming Message", - "Evil Request", - "Evil Reply", - "Chat Error", - } - }; - - maxf = sizeof(literals) / sizeof(literals[0]); - maxs = sizeof(literals[0]) / sizeof(literals[0][0]); - - family = aimutil_get16(workingPtr->data+0); - subtype= aimutil_get16(workingPtr->data+2); - - if((family < maxf) && (subtype+1 < maxs) && (literals[family][subtype] != NULL)) - printf("bleck: null handler for %04x/%04x (%s)\n", family, subtype, literals[family][subtype+1]); - else - printf("bleck: null handler for %04x/%04x (no literal)\n",family,subtype); - - return 1; -} - -faim_export int aim_conn_addhandler(struct aim_session_t *sess, - struct aim_conn_t *conn, - u_short family, - u_short type, - rxcallback_t newhandler, - u_short flags) -{ - struct aim_rxcblist_t *newcb; - - if (!conn) - return -1; - - faimdprintf(1, "aim_conn_addhandler: adding for %04x/%04x\n", family, type); - - if (!(newcb = (struct aim_rxcblist_t *)calloc(1, sizeof(struct aim_rxcblist_t)))) - return -1; - newcb->family = family; - newcb->type = type; - newcb->flags = flags; - if (!newhandler) - newcb->handler = &bleck; - else - newcb->handler = newhandler; - newcb->next = NULL; - - if (!conn->handlerlist) - conn->handlerlist = newcb; - else { - struct aim_rxcblist_t *cur; - - cur = conn->handlerlist; - - while (cur->next) - cur = cur->next; - cur->next = newcb; - } - - return 0; -} - -faim_export int aim_clearhandlers(struct aim_conn_t *conn) -{ - struct aim_rxcblist_t *cur; - - if (!conn) - return -1; - - for (cur = conn->handlerlist; cur; ) { - struct aim_rxcblist_t *tmp; - - tmp = cur->next; - free(cur); - cur = tmp; - } - conn->handlerlist = NULL; - - return 0; -} - -faim_internal rxcallback_t aim_callhandler(struct aim_conn_t *conn, - u_short family, - u_short type) -{ - struct aim_rxcblist_t *cur; - - if (!conn) - return NULL; - - faimdprintf(1, "aim_callhandler: calling for %04x/%04x\n", family, type); - - cur = conn->handlerlist; - while(cur) - { - if ( (cur->family == family) && (cur->type == type) ) - return cur->handler; - cur = cur->next; - } - - if (type==0xffff) - return NULL; - - return aim_callhandler(conn, family, 0xffff); -} - -faim_internal int aim_callhandler_noparam(struct aim_session_t *sess, - struct aim_conn_t *conn, - u_short family, - u_short type, - struct command_rx_struct *ptr) -{ - rxcallback_t userfunc = NULL; - userfunc = aim_callhandler(conn, family, type); - if (userfunc) - return userfunc(sess, ptr); - return 1; /* XXX */ -} - -/* - aim_rxdispatch() - - Basically, heres what this should do: - 1) Determine correct packet handler for this packet - 2) Mark the packet handled (so it can be dequeued in purge_queue()) - 3) Send the packet to the packet handler - 4) Go to next packet in the queue and start over - 5) When done, run purge_queue() to purge handled commands - - Note that any unhandlable packets should probably be left in the - queue. This is the best way to prevent data loss. This means - that a single packet may get looked at by this function multiple - times. This is more good than bad! This behavior may change. - - Aren't queue's fun? - - TODO: Get rid of all the ugly if's. - TODO: Clean up. - TODO: More support for mid-level handlers. - TODO: Allow for NULL handlers. - - */ -faim_export int aim_rxdispatch(struct aim_session_t *sess) -{ - int i = 0; - struct command_rx_struct *workingPtr = NULL; - - if (sess->queue_incoming == NULL) { - faimdprintf(1, "parse_generic: incoming packet queue empty.\n"); - return 0; - } else { - workingPtr = sess->queue_incoming; - for (i = 0; workingPtr != NULL; workingPtr = workingPtr->next, i++) { - /* - * XXX: This is still fairly ugly. - */ - if (workingPtr->handled) - continue; - - /* - * This is a debugging/sanity check only and probably could/should be removed - * for stable code. - */ - if (((workingPtr->hdrtype == AIM_FRAMETYPE_OFT) && - (workingPtr->conn->type != AIM_CONN_TYPE_RENDEZVOUS)) || - ((workingPtr->hdrtype == AIM_FRAMETYPE_OSCAR) && - (workingPtr->conn->type == AIM_CONN_TYPE_RENDEZVOUS))) { - printf("faim: rxhandlers: incompatible frame type %d on connection type 0x%04x\n", workingPtr->hdrtype, workingPtr->conn->type); - workingPtr->handled = 1; - continue; - } - - switch(workingPtr->conn->type) { - case -1: - /* - * This can happen if we have a queued command - * that was recieved after a connection has - * been terminated. In which case, the handler - * list has been cleared, and there's nothing we - * can do for it. We can only cancel it. - */ - workingPtr->handled = 1; - break; - case AIM_CONN_TYPE_AUTH: { - unsigned long head; - - head = aimutil_get32(workingPtr->data); - if ((head == 0x00000001) && (workingPtr->commandlen == 4)) { - faimdprintf(1, "got connection ack on auth line\n"); - workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr); - } else if (workingPtr->hdr.oscar.type == 0x04) { - /* Used only by the older login protocol */ - workingPtr->handled = aim_authparse(sess, workingPtr); - } else { - unsigned short family,subtype; - - family = aimutil_get16(workingPtr->data); - subtype = aimutil_get16(workingPtr->data+2); - - switch (family) { - /* New login protocol */ - case 0x0017: - if (subtype == 0x0001) - workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0x0001, workingPtr); - else if (subtype == 0x0003) - workingPtr->handled = aim_authparse(sess, workingPtr); - else if (subtype == 0x0007) - workingPtr->handled = aim_authkeyparse(sess, workingPtr); - else - workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0xffff, workingPtr); - break; - case 0x0001: - if (subtype == 0x0003) - workingPtr->handled = aim_parse_hostonline(sess, workingPtr); - else - workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0xffff, workingPtr); - break; - case 0x0007: - if (subtype == 0x0005) - workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_ADM, AIM_CB_ADM_INFOCHANGE_REPLY, workingPtr); - break; - case AIM_CB_FAM_SPECIAL: - if (subtype == AIM_CB_SPECIAL_DEBUGCONN_CONNECT) { - workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr); - break; - } else - workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0xffff, workingPtr); - break; - default: - break; - } - } - break; - } - case AIM_CONN_TYPE_BOS: { - u_short family; - u_short subtype; - - if (workingPtr->hdr.oscar.type == 0x04) { - workingPtr->handled = aim_negchan_middle(sess, workingPtr); - break; - } - - family = aimutil_get16(workingPtr->data); - subtype = aimutil_get16(workingPtr->data+2); - - switch (family) { - case 0x0000: /* not really a family, but it works */ - if (subtype == 0x0001) - workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr); - else - workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr); - break; - case 0x0001: /* Family: General */ - switch (subtype) { - case 0x0001: - workingPtr->handled = aim_parse_generalerrs(sess, workingPtr); - break; - case 0x0003: - workingPtr->handled = aim_parse_hostonline(sess, workingPtr); - break; - case 0x0005: - workingPtr->handled = aim_handleredirect_middle(sess, workingPtr); - break; - case 0x0007: - workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr); - break; - case 0x000a: - workingPtr->handled = aim_parse_ratechange_middle(sess, workingPtr); - break; - case 0x000f: - workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x000f, workingPtr); - break; - case 0x0010: - workingPtr->handled = aim_parse_evilnotify_middle(sess, workingPtr); - break; - case 0x0013: - workingPtr->handled = aim_parsemotd_middle(sess, workingPtr); - break; - case 0x0018: - workingPtr->handled = aim_parse_hostversions(sess, workingPtr); - break; - default: - workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_GEN, AIM_CB_GEN_DEFAULT, workingPtr); - break; - } - break; - case 0x0002: /* Family: Location */ - switch (subtype) { - case 0x0001: - workingPtr->handled = aim_parse_locateerr(sess, workingPtr); - break; - case 0x0003: - workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0002, 0x0003, workingPtr); - break; - case 0x0006: - workingPtr->handled = aim_parse_userinfo_middle(sess, workingPtr); - break; - default: - workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOC, AIM_CB_LOC_DEFAULT, workingPtr); - break; - } - break; - case 0x0003: /* Family: Buddy List */ - switch (subtype) { - case 0x0001: - workingPtr->handled = aim_parse_generalerrs(sess, workingPtr); - break; - case 0x0003: - workingPtr->handled = aim_parse_buddyrights(sess, workingPtr); - break; - case 0x000b: /* oncoming buddy */ - workingPtr->handled = aim_parse_oncoming_middle(sess, workingPtr); - break; - case 0x000c: /* offgoing buddy */ - workingPtr->handled = aim_parse_offgoing_middle(sess, workingPtr); - break; - default: - workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BUD, AIM_CB_BUD_DEFAULT, workingPtr); - } - break; - case 0x0004: /* Family: Messaging */ - switch (subtype) { - case 0x0001: - workingPtr->handled = aim_parse_msgerror_middle(sess, workingPtr); - break; - case 0x0005: - workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0004, 0x0005, workingPtr); - break; - case 0x0006: - workingPtr->handled = aim_parse_outgoing_im_middle(sess, workingPtr); - break; - case 0x0007: - workingPtr->handled = aim_parse_incoming_im_middle(sess, workingPtr); - break; - case 0x000a: - workingPtr->handled = aim_parse_missedcall(sess, workingPtr); - break; - case 0x000c: - workingPtr->handled = aim_parse_msgack_middle(sess, workingPtr); - break; - default: - workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_MSG, AIM_CB_MSG_DEFAULT, workingPtr); - } - break; - case 0x0009: - if (subtype == 0x0001) - workingPtr->handled = aim_parse_generalerrs(sess, workingPtr); - else if (subtype == 0x0003) - workingPtr->handled = aim_parse_bosrights(sess, workingPtr); - else - workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BOS, AIM_CB_BOS_DEFAULT, workingPtr); - break; - case 0x000a: /* Family: User lookup */ - switch (subtype) { - case 0x0001: - workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000a, 0x0001, workingPtr); - break; - case 0x0003: - workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000a, 0x0003, workingPtr); - break; - default: - workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOK, AIM_CB_LOK_DEFAULT, workingPtr); - } - break; - case 0x000b: { - if (subtype == 0x0001) - workingPtr->handled = aim_parse_generalerrs(sess, workingPtr); - else if (subtype == 0x0002) - workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000b, 0x0002, workingPtr); - else - workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_STS, AIM_CB_STS_DEFAULT, workingPtr); - break; - } - case 0x0013: { - printf("lalala: 0x%04x/0x%04x\n", family, subtype); - break; - } - case AIM_CB_FAM_SPECIAL: - workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr); - break; - default: - workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr); - break; - } /* switch(family) */ - break; - } /* AIM_CONN_TYPE_BOS */ - case AIM_CONN_TYPE_CHATNAV: { - u_short family; - u_short subtype; - family = aimutil_get16(workingPtr->data); - subtype= aimutil_get16(workingPtr->data+2); - - if ((family == 0x0000) && (subtype == 0x00001)) { - workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr); - } else if ((family == 0x0001) && (subtype == 0x0003)) { - workingPtr->handled = aim_parse_hostonline(sess, workingPtr); - } else if ((family == 0x000d) && (subtype == 0x0009)) { - workingPtr->handled = aim_chatnav_parse_info(sess, workingPtr); - } else { - workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr); - } - break; - } - case AIM_CONN_TYPE_CHAT: { - u_short family, subtype; - - family = aimutil_get16(workingPtr->data); - subtype= aimutil_get16(workingPtr->data+2); - - if ((family == 0x0000) && (subtype == 0x00001)) { - workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr); - } else if (family == 0x0001) { - if (subtype == 0x0001) - workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0001, workingPtr); - else if (subtype == 0x0003) - workingPtr->handled = aim_parse_hostonline(sess, workingPtr); - else if (subtype == 0x0007) - workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr); - else if (subtype == 0x000a) - workingPtr->handled = aim_parse_ratechange_middle(sess, workingPtr); - else - workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr); - } else if (family == 0x000e) { - if (subtype == 0x0002) - workingPtr->handled = aim_chat_parse_infoupdate(sess, workingPtr); - else if (subtype == 0x0003) - workingPtr->handled = aim_chat_parse_joined(sess, workingPtr); - else if (subtype == 0x0004) - workingPtr->handled = aim_chat_parse_leave(sess, workingPtr); - else if (subtype == 0x0006) - workingPtr->handled = aim_chat_parse_incoming(sess, workingPtr); - else - printf("Chat: unknown snac %04x/%04x\n", family, subtype); - } else { - printf("Chat: unknown snac %04x/%04x\n", family, subtype); - workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_DEFAULT, workingPtr); - } - break; - } - case AIM_CONN_TYPE_RENDEZVOUS: { - /* make sure that we only get OFT frames on these connections */ - if (workingPtr->hdrtype != AIM_FRAMETYPE_OFT) { - printf("faim: internal error: non-OFT frames on OFT connection\n"); - workingPtr->handled = 1; /* get rid of it */ - break; - } - - /* XXX: implement this */ - printf("faim: OFT frame!\n"); - - break; - } - case AIM_CONN_TYPE_RENDEZVOUS_OUT: { - /* not possible */ - break; - } - default: - printf("\ninternal error: unknown connection type (very bad.) (type = %d, fd = %d, commandlen = %02x)\n\n", workingPtr->conn->type, workingPtr->conn->fd, workingPtr->commandlen); - workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr); - break; - } - } - } - - /* - * This doesn't have to be called here. It could easily be done - * by a seperate thread or something. It's an administrative operation, - * and can take a while. Though the less you call it the less memory - * you'll have :) - */ - aim_purge_rxqueue(sess); - - return 0; -} - -faim_internal int aim_parse_msgack_middle(struct aim_session_t *sess, struct command_rx_struct *command) -{ - rxcallback_t userfunc = NULL; - char sn[MAXSNLEN]; - unsigned short type; - int i = 10+8; /* skip SNAC and cookie */ - int ret = 1; - unsigned char snlen; - - type = aimutil_get16(command->data+i); - i += 2; - - snlen = aimutil_get8(command->data+i); - i++; - - memset(sn, 0, sizeof(sn)); - strncpy(sn, (char *)command->data+i, snlen); - - if ((userfunc = aim_callhandler(command->conn, 0x0004, 0x000c))) - ret = userfunc(sess, command, type, sn); - - return ret; -} - -/* - * The Rate Limiting System, An Abridged Guide to Nonsense. - * - * OSCAR defines several 'rate classes'. Each class has seperate - * rate limiting properties (limit level, alert level, disconnect - * level, etc), and a set of SNAC family/type pairs associated with - * it. The rate classes, their limiting properties, and the definitions - * of which SNACs are belong to which class, are defined in the - * Rate Response packet at login to each host. - * - * Logically, all rate offenses within one class count against further - * offenses for other SNACs in the same class (ie, sending messages - * too fast will limit the number of user info requests you can send, - * since those two SNACs are in the same rate class). - * - * Since the rate classes are defined dynamically at login, the values - * below may change. But they seem to be fairly constant. - * - * Currently, BOS defines five rate classes, with the commonly used - * members as follows... - * - * Rate class 0x0001: - * - Everything thats not in any of the other classes - * - * Rate class 0x0002: - * - Buddy list add/remove - * - Permit list add/remove - * - Deny list add/remove - * - * Rate class 0x0003: - * - User information requests - * - Outgoing ICBMs - * - * Rate class 0x0004: - * - A few unknowns: 2/9, 2/b, and f/2 - * - * Rate class 0x0005: - * - Chat room create - * - Outgoing chat ICBMs - * - * The only other thing of note is that class 5 (chat) has slightly looser - * limiting properties than class 3 (normal messages). But thats just a - * small bit of trivia for you. - * - * The last thing that needs to be learned about the rate limiting - * system is how the actual numbers relate to the passing of time. This - * seems to be a big mystery. - * - */ -faim_internal int aim_parse_ratechange_middle(struct aim_session_t *sess, struct command_rx_struct *command) -{ - rxcallback_t userfunc = NULL; - int ret = 1; - int i; - int code; - unsigned long rateclass, windowsize, clear, alert, limit, disconnect; - unsigned long currentavg, maxavg; - - i = 10; - - code = aimutil_get16(command->data+i); - i += 2; - - rateclass = aimutil_get16(command->data+i); - i += 2; - - windowsize = aimutil_get32(command->data+i); - i += 4; - clear = aimutil_get32(command->data+i); - i += 4; - alert = aimutil_get32(command->data+i); - i += 4; - limit = aimutil_get32(command->data+i); - i += 4; - disconnect = aimutil_get32(command->data+i); - i += 4; - currentavg = aimutil_get32(command->data+i); - i += 4; - maxavg = aimutil_get32(command->data+i); - i += 4; - - if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x000a))) - ret = userfunc(sess, command, code, rateclass, windowsize, clear, alert, limit, disconnect, currentavg, maxavg); - - return ret; -} - -faim_internal int aim_parse_evilnotify_middle(struct aim_session_t *sess, struct command_rx_struct *command) -{ - rxcallback_t userfunc = NULL; - int ret = 1; - int i; - unsigned short newevil; - struct aim_userinfo_s userinfo; - - i = 10; - newevil = aimutil_get16(command->data+10); - i += 2; - - memset(&userinfo, 0, sizeof(struct aim_userinfo_s)); - if (command->commandlen-i) - i += aim_extractuserinfo(command->data+i, &userinfo); - - if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0010))) - ret = userfunc(sess, command, newevil, &userinfo); - - return ret; -} - -faim_internal int aim_parsemotd_middle(struct aim_session_t *sess, - struct command_rx_struct *command, ...) -{ - rxcallback_t userfunc = NULL; - char *msg; - int ret=1; - struct aim_tlvlist_t *tlvlist; - u_short id; - - /* - * Code. - * - * Valid values: - * 1 Mandatory upgrade - * 2 Advisory upgrade - * 3 System bulletin - * 4 Nothing's wrong ("top o the world" -- normal) - * - */ - id = aimutil_get16(command->data+10); - - /* - * TLVs follow - */ - if (!(tlvlist = aim_readtlvchain(command->data+12, command->commandlen-12))) - return ret; - - if (!(msg = aim_gettlv_str(tlvlist, 0x000b, 1))) { - aim_freetlvchain(&tlvlist); - return ret; - } - - userfunc = aim_callhandler(command->conn, 0x0001, 0x0013); - if (userfunc) - ret = userfunc(sess, command, id, msg); - - aim_freetlvchain(&tlvlist); - free(msg); - - return ret; -} - -faim_internal int aim_parse_hostonline(struct aim_session_t *sess, - struct command_rx_struct *command, ...) -{ - rxcallback_t userfunc = NULL; - int ret = 1; - unsigned short *families = NULL; - int famcount = 0, i; - - famcount = (command->commandlen-10)/2; - if (!(families = malloc(command->commandlen-10))) - return ret; - - for (i = 0; i < famcount; i++) - families[i] = aimutil_get16(command->data+((i*2)+10)); - - if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0003))) - ret = userfunc(sess, command, famcount, families); - - free(families); - - return ret; -} - -faim_internal int aim_parse_hostversions(struct aim_session_t *sess, - struct command_rx_struct *command, ...) -{ - rxcallback_t userfunc = NULL; - int ret = 1; - int vercount; - - vercount = (command->commandlen-10)/4; - - if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0018))) - ret = userfunc(sess, command, vercount, command->data+10); - - return ret; -} - -faim_internal int aim_handleredirect_middle(struct aim_session_t *sess, - struct command_rx_struct *command, ...) -{ - int serviceid = 0; - unsigned char *cookie = NULL; - char *ip = NULL; - rxcallback_t userfunc = NULL; - struct aim_tlvlist_t *tlvlist; - int ret = 1; - - tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10); - - if (aim_gettlv(tlvlist, 0x000d, 1)) - serviceid = aim_gettlv16(tlvlist, 0x000d, 1); - if (aim_gettlv(tlvlist, 0x0005, 1)) - ip = aim_gettlv_str(tlvlist, 0x0005, 1); - if (aim_gettlv(tlvlist, 0x0006, 1)) - cookie = aim_gettlv_str(tlvlist, 0x0006, 1); - - if ((serviceid == AIM_CONN_TYPE_CHAT) && sess->pendingjoin) { - - /* - * Chat hack. - * - */ - if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0005))) - ret = userfunc(sess, command, serviceid, ip, cookie, sess->pendingjoin, (int)sess->pendingjoinexchange); - free(sess->pendingjoin); - sess->pendingjoin = NULL; - sess->pendingjoinexchange = 0; - } else if (!serviceid || !ip || !cookie) { /* yeep! */ - ret = 1; - } else { - if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0005))) - ret = userfunc(sess, command, serviceid, ip, cookie); - } - - if (ip) - free(ip); - if (cookie) - free(cookie); - - aim_freetlvchain(&tlvlist); - - return ret; -} - -faim_internal int aim_parse_unknown(struct aim_session_t *sess, - struct command_rx_struct *command, ...) -{ - u_int i = 0; - - if (!sess || !command) - return 1; - - faimdprintf(1, "\nRecieved unknown packet:"); - - for (i = 0; i < command->commandlen; i++) - { - if ((i % 8) == 0) - faimdprintf(1, "\n\t"); - - faimdprintf(1, "0x%2x ", command->data[i]); - } - - faimdprintf(1, "\n\n"); - - return 1; -} - - -faim_internal int aim_negchan_middle(struct aim_session_t *sess, - struct command_rx_struct *command) -{ - struct aim_tlvlist_t *tlvlist; - char *msg = NULL; - unsigned short code = 0; - rxcallback_t userfunc = NULL; - int ret = 1; - - tlvlist = aim_readtlvchain(command->data, command->commandlen); - - if (aim_gettlv(tlvlist, 0x0009, 1)) - code = aim_gettlv16(tlvlist, 0x0009, 1); - - if (aim_gettlv(tlvlist, 0x000b, 1)) - msg = aim_gettlv_str(tlvlist, 0x000b, 1); - - if ((userfunc = aim_callhandler(command->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR))) - ret = userfunc(sess, command, code, msg); - - aim_freetlvchain(&tlvlist); - - if (msg) - free(msg); - - return ret; -} - -/* - * aim_parse_generalerrs() - * - * Middle handler for 0x0001 snac of each family. - * - */ -faim_internal int aim_parse_generalerrs(struct aim_session_t *sess, - struct command_rx_struct *command, ...) -{ - unsigned short family; - unsigned short subtype; - int ret = 1; - int error = 0; - rxcallback_t userfunc = NULL; - - family = aimutil_get16(command->data+0); - subtype= aimutil_get16(command->data+2); - - if (command->commandlen > 10) - error = aimutil_get16(command->data+10); - - if ((userfunc = aim_callhandler(command->conn, family, subtype))) - ret = userfunc(sess, command, error); - - return ret; -} - - -
--- a/libfaim/aim_rxqueue.c Sun Mar 04 22:37:18 2001 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,249 +0,0 @@ -/* - * aim_rxqueue.c - * - * This file contains the management routines for the receive - * (incoming packet) queue. The actual packet handlers are in - * aim_rxhandlers.c. - */ - -#include <faim/aim.h> - -#ifndef _WIN32 -#include <sys/socket.h> -#endif - -/* - * Since not all implementations support MSG_WAITALL, define - * an alternate guarenteed read function... - * - * We keep recv() for systems that can do it because it means - * a single system call for the entire packet, where read may - * take more for a badly fragmented packet. - * - */ -faim_internal int aim_recv(int fd, void *buf, size_t count) -{ -#ifdef MSG_WAITALL - return recv(fd, buf, count, MSG_WAITALL); -#else - int left, ret, cur = 0; - - left = count; - - while (left) { - ret = recv(fd, ((unsigned char *)buf)+cur, left, 0); - if (ret == -1) - return -1; - if (ret == 0) - return cur; - - cur += ret; - left -= ret; - } - - return cur; -#endif -} - -/* - * Grab a single command sequence off the socket, and enqueue - * it in the incoming event queue in a seperate struct. - */ -faim_export int aim_get_command(struct aim_session_t *sess, struct aim_conn_t *conn) -{ - unsigned char generic[6]; - struct command_rx_struct *newrx = NULL; - - if (!sess || !conn) - return 0; - - if (conn->fd == -1) - return -1; /* its a aim_conn_close()'d connection */ - - if (conn->fd < 3) /* can happen when people abuse the interface */ - return 0; - - if (conn->status & AIM_CONN_STATUS_INPROGRESS) - return aim_conn_completeconnect(sess, conn); - - /* - * Rendezvous (client-client) connections do not speak - * FLAP, so this function will break on them. - */ - if (conn->type == AIM_CONN_TYPE_RENDEZVOUS) - return aim_get_command_rendezvous(sess, conn); - if (conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) - return 0; - - /* - * Read FLAP header. Six bytes: - * - * 0 char -- Always 0x2a - * 1 char -- Channel ID. Usually 2 -- 1 and 4 are used during login. - * 2 short -- Sequence number - * 4 short -- Number of data bytes that follow. - */ - faim_mutex_lock(&conn->active); - if (aim_recv(conn->fd, generic, 6) < 6){ - aim_conn_close(conn); - faim_mutex_unlock(&conn->active); - return -1; - } - - /* - * This shouldn't happen unless the socket breaks, the server breaks, - * or we break. We must handle it just in case. - */ - if (generic[0] != 0x2a) { - faimdprintf(1, "Bad incoming data!"); - aim_conn_close(conn); - faim_mutex_unlock(&conn->active); - return -1; - } - - /* allocate a new struct */ - if (!(newrx = (struct command_rx_struct *)malloc(sizeof(struct command_rx_struct)))) { - faim_mutex_unlock(&conn->active); - return -1; - } - memset(newrx, 0x00, sizeof(struct command_rx_struct)); - - newrx->lock = 1; /* lock the struct */ - - /* we're doing OSCAR if we're here */ - newrx->hdrtype = AIM_FRAMETYPE_OSCAR; - - /* store channel -- byte 2 */ - newrx->hdr.oscar.type = (char) generic[1]; - - /* store seqnum -- bytes 3 and 4 */ - newrx->hdr.oscar.seqnum = aimutil_get16(generic+2); - - /* store commandlen -- bytes 5 and 6 */ - newrx->commandlen = aimutil_get16(generic+4); - - newrx->nofree = 0; /* free by default */ - - /* malloc for data portion */ - if (!(newrx->data = (u_char *) malloc(newrx->commandlen))) { - free(newrx); - faim_mutex_unlock(&conn->active); - return -1; - } - - /* read the data portion of the packet */ - if (aim_recv(conn->fd, newrx->data, newrx->commandlen) < newrx->commandlen){ - free(newrx->data); - free(newrx); - aim_conn_close(conn); - faim_mutex_unlock(&conn->active); - return -1; - } - faim_mutex_unlock(&conn->active); - - newrx->conn = conn; - - newrx->next = NULL; /* this will always be at the bottom */ - newrx->lock = 0; /* unlock */ - - /* enqueue this packet */ - if (sess->queue_incoming == NULL) { - sess->queue_incoming = newrx; - } else { - struct command_rx_struct *cur; - - /* - * This append operation takes a while. It might be faster - * if we maintain a pointer to the last entry in the queue - * and just update that. Need to determine if the overhead - * to maintain that is lower than the overhead for this loop. - */ - for (cur = sess->queue_incoming; cur->next; cur = cur->next) - ; - cur->next = newrx; - } - - newrx->conn->lastactivity = time(NULL); - - return 0; -} - -/* - * Purge recieve queue of all handled commands (->handled==1). Also - * allows for selective freeing using ->nofree so that the client can - * keep the data for various purposes. - * - * If ->nofree is nonzero, the frame will be delinked from the global list, - * but will not be free'ed. The client _must_ keep a pointer to the - * data -- libfaim will not! If the client marks ->nofree but - * does not keep a pointer, it's lost forever. - * - */ -faim_export void aim_purge_rxqueue(struct aim_session_t *sess) -{ - struct command_rx_struct *cur = NULL; - struct command_rx_struct *tmp; - - if (sess->queue_incoming == NULL) - return; - - if (sess->queue_incoming->next == NULL) { - if (sess->queue_incoming->handled) { - tmp = sess->queue_incoming; - sess->queue_incoming = NULL; - - if (!tmp->nofree) { - if (tmp->hdrtype == AIM_FRAMETYPE_OFT) - free(tmp->hdr.oft.hdr2); - free(tmp->data); - free(tmp); - } else - tmp->next = NULL; - } - return; - } - - for(cur = sess->queue_incoming; cur->next != NULL; ) { - if (cur->next->handled) { - tmp = cur->next; - cur->next = tmp->next; - if (!tmp->nofree) { - if (tmp->hdrtype == AIM_FRAMETYPE_OFT) - free(tmp->hdr.oft.hdr2); - free(tmp->data); - free(tmp); - } else - tmp->next = NULL; - } - cur = cur->next; - - /* - * Be careful here. Because of the way we just - * manipulated the pointer, cur may be NULL and - * the for() will segfault doing the check unless - * we find this case first. - */ - if (cur == NULL) - break; - } - - return; -} - -/* - * Since aim_get_command will aim_conn_kill dead connections, we need - * to clean up the rxqueue of unprocessed connections on that socket. - * - * XXX: this is something that was handled better in the old connection - * handling method, but eh. - */ -faim_internal void aim_rxqueue_cleanbyconn(struct aim_session_t *sess, struct aim_conn_t *conn) -{ - struct command_rx_struct *currx; - - for (currx = sess->queue_incoming; currx; currx = currx->next) { - if ((!currx->handled) && (currx->conn == conn)) - currx->handled = 1; - } - return; -}
--- a/libfaim/aim_search.c Sun Mar 04 22:37:18 2001 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,35 +0,0 @@ - -/* - * aim_search.c - * - * TODO: Add aim_usersearch_name() - * - */ - -#include <faim/aim.h> - -faim_export unsigned long aim_usersearch_address(struct aim_session_t *sess, - struct aim_conn_t *conn, - char *address) -{ - struct command_tx_struct *newpacket; - - if (!address) - return -1; - - if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+strlen(address)))) - return -1; - - newpacket->lock = 1; - - aim_putsnac(newpacket->data, 0x000a, 0x0002, 0x0000, sess->snac_nextid); - - aimutil_putstr(newpacket->data+10, address, strlen(address)); - - aim_tx_enqueue(sess, newpacket); - - aim_cachesnac(sess, 0x000a, 0x0002, 0x0000, address, strlen(address)+1); - - return sess->snac_nextid; -} -
--- a/libfaim/aim_snac.c Sun Mar 04 22:37:18 2001 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,179 +0,0 @@ - -/* - * - * Various SNAC-related dodads... - * - * outstanding_snacs is a list of aim_snac_t structs. A SNAC should be added - * whenever a new SNAC is sent and it should remain in the list until the - * response for it has been receieved. - * - * cleansnacs() should be called periodically by the client in order - * to facilitate the aging out of unreplied-to SNACs. This can and does - * happen, so it should be handled. - * - */ - -#include <faim/aim.h> - -/* - * Called from aim_session_init() to initialize the hash. - */ -faim_internal void aim_initsnachash(struct aim_session_t *sess) -{ - int i; - - for (i = 0; i < FAIM_SNAC_HASH_SIZE; i++) { - sess->snac_hash[i] = NULL; - faim_mutex_init(&sess->snac_hash_locks[i]); - } - - return; -} - -faim_internal unsigned long aim_cachesnac(struct aim_session_t *sess, - const unsigned short family, - const unsigned short type, - const unsigned short flags, - const void *data, const int datalen) -{ - struct aim_snac_t snac; - - snac.id = sess->snac_nextid++; - snac.family = family; - snac.type = type; - snac.flags = flags; - - snac.data = malloc(datalen); - memcpy(snac.data, data, datalen); - - return aim_newsnac(sess, &snac); -} - -/* - * Clones the passed snac structure and caches it in the - * list/hash. - */ -faim_internal unsigned long aim_newsnac(struct aim_session_t *sess, - struct aim_snac_t *newsnac) -{ - struct aim_snac_t *snac = NULL; - int index; - - if (!newsnac) - return 0; - - if (!(snac = calloc(1, sizeof(struct aim_snac_t)))) - return 0; - memcpy(snac, newsnac, sizeof(struct aim_snac_t)); - snac->issuetime = time(&snac->issuetime); - snac->next = NULL; - - index = snac->id % FAIM_SNAC_HASH_SIZE; - - faim_mutex_lock(&sess->snac_hash_locks[index]); - snac->next = sess->snac_hash[index]; - sess->snac_hash[index] = snac; - faim_mutex_unlock(&sess->snac_hash_locks[index]); - - return(snac->id); -} - -/* - * Finds a snac structure with the passed SNAC ID, - * removes it from the list/hash, and returns a pointer to it. - * - * The returned structure must be freed by the caller. - * - */ -faim_internal struct aim_snac_t *aim_remsnac(struct aim_session_t *sess, - u_long id) -{ - struct aim_snac_t *cur = NULL; - int index; - - index = id % FAIM_SNAC_HASH_SIZE; - - faim_mutex_lock(&sess->snac_hash_locks[index]); - if (!sess->snac_hash[index]) - ; - else if (sess->snac_hash[index]->id == id) { - cur = sess->snac_hash[index]; - sess->snac_hash[index] = cur->next; - } else { - cur = sess->snac_hash[index]; - while (cur->next) { - if (cur->next->id == id) { - struct aim_snac_t *tmp; - - tmp = cur->next; - cur->next = cur->next->next; - cur = tmp; - break; - } - cur = cur->next; - } - } - faim_mutex_unlock(&sess->snac_hash_locks[index]); - - return cur; -} - -/* - * This is for cleaning up old SNACs that either don't get replies or - * a reply was never received for. Garabage collection. Plain and simple. - * - * maxage is the _minimum_ age in seconds to keep SNACs. - * - */ -faim_internal int aim_cleansnacs(struct aim_session_t *sess, - int maxage) -{ - int i; - - for (i = 0; i < FAIM_SNAC_HASH_SIZE; i++) { - struct aim_snac_t *cur = NULL, *next = NULL, *prev = NULL; - time_t curtime; - - faim_mutex_lock(&sess->snac_hash_locks[i]); - if (!sess->snac_hash[i]) { - faim_mutex_unlock(&sess->snac_hash_locks[i]); - continue; - } - - curtime = time(NULL); /* done here in case we waited for the lock */ - - cur = sess->snac_hash[i]; - while (cur) { - next = cur->next; - if ((curtime - cur->issuetime) > maxage) { - if (sess->snac_hash[i] == cur) - prev = sess->snac_hash[i] = next; - else - prev->next = next; - - /* XXX should we have destructors here? */ - if (cur->data) - free(cur->data); - free(cur); - - } else { - prev = cur; - } - cur = next; - } - - faim_mutex_unlock(&sess->snac_hash_locks[i]); - } - - return 0; -} - -faim_internal int aim_putsnac(u_char *buf, int family, int subtype, int flags, u_long snacid) -{ - int curbyte = 0; - curbyte += aimutil_put16(buf+curbyte, (u_short)(family&0xffff)); - curbyte += aimutil_put16(buf+curbyte, (u_short)(subtype&0xffff)); - curbyte += aimutil_put16(buf+curbyte, (u_short)(flags&0xffff)); - curbyte += aimutil_put32(buf+curbyte, snacid); - return curbyte; -}
--- a/libfaim/aim_tlv.c Sun Mar 04 22:37:18 2001 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,723 +0,0 @@ -#include <faim/aim.h> - -/** - * aim_readtlvchain - Read a TLV chain from a buffer. - * @buf: Input buffer - * @maxlen: Length of input buffer - * - * Reads and parses a series of TLV patterns from a data buffer; the - * returned structure is manipulatable with the rest of the TLV - * routines. When done with a TLV chain, aim_freetlvchain() should - * be called to free the dynamic substructures. - * - */ -faim_export struct aim_tlvlist_t *aim_readtlvchain(u_char *buf, int maxlen) -{ - int pos; - struct aim_tlvlist_t *list; - struct aim_tlvlist_t *cur; - - u_short type; - u_short length; - - if (!buf) - return NULL; - - list = NULL; - - pos = 0; - - while (pos < maxlen) - { - type = aimutil_get16(buf+pos); - pos += 2; - - if (pos < maxlen) - { - length = aimutil_get16(buf+pos); - pos += 2; - - if ((pos+length) <= maxlen) - { - /* - * Okay, so now AOL has decided that any TLV of - * type 0x0013 can only be two bytes, despite - * what the actual given length is. So here - * we dump any invalid TLVs of that sort. Hopefully - * theres no special cases to this special case. - * - mid (30jun2000) - */ - if ((type == 0x0013) && (length != 0x0002)) { - printf("faim: skipping TLV t(0013) with invalid length (0x%04x)\n", length); - length = 0x0002; - } else { - cur = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t)); - memset(cur, 0x00, sizeof(struct aim_tlvlist_t)); - - cur->tlv = aim_createtlv(); - cur->tlv->type = type; - cur->tlv->length = length; - if (length) { - cur->tlv->value = (unsigned char *)malloc(length); - memcpy(cur->tlv->value, buf+pos, length); - } - - cur->next = list; - list = cur; - } - pos += length; - } - } - } - - return list; -} - -/** - * aim_freetlvchain - Free a TLV chain structure - * @list: Chain to be freed - * - * Walks the list of TLVs in the passed TLV chain and - * frees each one. Note that any references to this data - * should be removed before calling this. - * - */ -faim_export void aim_freetlvchain(struct aim_tlvlist_t **list) -{ - struct aim_tlvlist_t *cur, *cur2; - - if (!list || !(*list)) - return; - - cur = *list; - while (cur) - { - aim_freetlv(&cur->tlv); - cur2 = cur->next; - free(cur); - cur = cur2; - } - list = NULL; - return; -} - -/** - * aim_counttlvchain - Count the number of TLVs in a chain - * @list: Chain to be counted - * - * Returns the number of TLVs stored in the passed chain. - * - */ -faim_export int aim_counttlvchain(struct aim_tlvlist_t **list) -{ - struct aim_tlvlist_t *cur; - int count = 0; - - if (!list || !(*list)) - return 0; - - for (cur = *list; cur; cur = cur->next) - count++; - - return count; -} - -/** - * aim_sizetlvchain - Count the number of bytes in a TLV chain - * @list: Chain to be sized - * - * Returns the number of bytes that would be needed to - * write the passed TLV chain to a data buffer. - * - */ -faim_export int aim_sizetlvchain(struct aim_tlvlist_t **list) -{ - struct aim_tlvlist_t *cur; - int size = 0; - - if (!list || !(*list)) - return 0; - - for (cur = *list; cur; cur = cur->next) - size += (4 + cur->tlv->length); - - return size; -} - -/** - * aim_addtlvtochain_str - Add a string to a TLV chain - * @list: Desination chain (%NULL pointer if empty) - * @type: TLV type - * @str: String to add - * @len: Length of string to add (not including %NULL) - * - * Adds the passed string as a TLV element of the passed type - * to the TLV chain. - * - */ -faim_export int aim_addtlvtochain_str(struct aim_tlvlist_t **list, unsigned short type, char *str, int len) -{ - struct aim_tlvlist_t *newtlv; - struct aim_tlvlist_t *cur; - - if (!list) - return 0; - - newtlv = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t)); - memset(newtlv, 0x00, sizeof(struct aim_tlvlist_t)); - - newtlv->tlv = aim_createtlv(); - newtlv->tlv->type = type; - newtlv->tlv->length = len; - newtlv->tlv->value = (unsigned char *)malloc(newtlv->tlv->length*sizeof(unsigned char)); - memcpy(newtlv->tlv->value, str, newtlv->tlv->length); - - newtlv->next = NULL; - - if (*list == NULL) { - *list = newtlv; - } else if ((*list)->next == NULL) { - (*list)->next = newtlv; - } else { - for(cur = *list; cur->next; cur = cur->next) - ; - cur->next = newtlv; - } - return newtlv->tlv->length; -} - -/** - * aim_addtlvtochain16 - Add a 16bit integer to a TLV chain - * @list: Destination chain - * @type: TLV type to add - * @val: Value to add - * - * Adds a two-byte unsigned integer to a TLV chain. - * - */ -faim_export int aim_addtlvtochain16(struct aim_tlvlist_t **list, unsigned short type, unsigned short val) -{ - struct aim_tlvlist_t *newtl; - struct aim_tlvlist_t *cur; - - if (!list) - return 0; - - newtl = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t)); - memset(newtl, 0x00, sizeof(struct aim_tlvlist_t)); - - newtl->tlv = aim_createtlv(); - newtl->tlv->type = type; - newtl->tlv->length = 2; - newtl->tlv->value = (unsigned char *)malloc(newtl->tlv->length*sizeof(unsigned char)); - aimutil_put16(newtl->tlv->value, val); - - newtl->next = NULL; - - if (*list == NULL) { - *list = newtl; - } else if ((*list)->next == NULL) { - (*list)->next = newtl; - } else { - for(cur = *list; cur->next; cur = cur->next) - ; - cur->next = newtl; - } - return 2; -} - -/** - * aim_addtlvtochain32 - Add a 32bit integer to a TLV chain - * @list: Destination chain - * @type: TLV type to add - * @val: Value to add - * - * Adds a four-byte unsigned integer to a TLV chain. - * - */ -faim_export int aim_addtlvtochain32(struct aim_tlvlist_t **list, unsigned short type, unsigned long val) -{ - struct aim_tlvlist_t *newtl; - struct aim_tlvlist_t *cur; - - if (!list) - return 0; - - newtl = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t)); - memset(newtl, 0x00, sizeof(struct aim_tlvlist_t)); - - newtl->tlv = aim_createtlv(); - newtl->tlv->type = type; - newtl->tlv->length = 4; - newtl->tlv->value = (unsigned char *)malloc(newtl->tlv->length*sizeof(unsigned char)); - aimutil_put32(newtl->tlv->value, val); - - newtl->next = NULL; - - if (*list == NULL) { - *list = newtl; - } else if ((*list)->next == NULL) { - (*list)->next = newtl; - } else { - for(cur = *list; cur->next; cur = cur->next) - ; - cur->next = newtl; - } - return 4; -} - -/** - * aim_addtlvtochain_caps - Add a capability block to a TLV chain - * @list: Destination chain - * @type: TLV type to add - * @caps: Bitfield of capability flags to send - * - * Adds a block of capability blocks to a TLV chain. The bitfield - * passed in should be a bitwise %OR of any of the %AIM_CAPS constants: - * - * %AIM_CAPS_BUDDYICON Supports Buddy Icons - * - * %AIM_CAPS_VOICE Supports Voice Chat - * - * %AIM_CAPS_IMIMAGE Supports DirectIM/IMImage - * - * %AIM_CAPS_CHAT Supports Chat - * - * %AIM_CAPS_GETFILE Supports Get File functions - * - * %AIM_CAPS_SENDFILE Supports Send File functions - * - */ -faim_export int aim_addtlvtochain_caps(struct aim_tlvlist_t **list, unsigned short type, unsigned short caps) -{ - unsigned char buf[128]; /* icky fixed length buffer */ - struct aim_tlvlist_t *newtl; - struct aim_tlvlist_t *cur; - - if(!list) - return 0; - - newtl = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t)); - memset(newtl, 0x00, sizeof(struct aim_tlvlist_t)); - - newtl->tlv = aim_createtlv(); - newtl->tlv->type = type; - - newtl->tlv->length = aim_putcap(buf, sizeof(buf), caps); - newtl->tlv->value = (unsigned char *)calloc(1, newtl->tlv->length); - memcpy(newtl->tlv->value, buf, newtl->tlv->length); - - newtl->next = NULL; - - if (*list == NULL) { - *list = newtl; - } else if ((*list)->next == NULL) { - (*list)->next = newtl; - } else { - for(cur = *list; cur->next; cur = cur->next) - ; - cur->next = newtl; - } - return newtl->tlv->length; -} - -/** - * aim_addtlvtochain_noval - Add a blank TLV to a TLV chain - * @list: Destination chain - * @type: TLV type to add - * - * Adds a TLV with a zero length to a TLV chain. - * - */ -faim_internal int aim_addtlvtochain_noval(struct aim_tlvlist_t **list, unsigned short type) -{ - struct aim_tlvlist_t *newtlv; - struct aim_tlvlist_t *cur; - - newtlv = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t)); - memset(newtlv, 0x00, sizeof(struct aim_tlvlist_t)); - - newtlv->tlv = aim_createtlv(); - newtlv->tlv->type = type; - newtlv->tlv->length = 0; - newtlv->tlv->value = NULL; - - newtlv->next = NULL; - - if (*list == NULL) { - *list = newtlv; - } else if ((*list)->next == NULL) { - (*list)->next = newtlv; - } else { - for(cur = *list; cur->next; cur = cur->next) - ; - cur->next = newtlv; - } - return newtlv->tlv->length; -} - -/** - * aim_writetlvchain - Write a TLV chain into a data buffer. - * @buf: Destination buffer - * @buflen: Maximum number of bytes that will be written to buffer - * @list: Source TLV chain - * - * Copies a TLV chain into a raw data buffer, writing only the number - * of bytes specified. This operation does not free the chain; - * aim_freetlvchain() must still be called to free up the memory used - * by the chain structures. - * - */ -faim_export int aim_writetlvchain(u_char *buf, int buflen, struct aim_tlvlist_t **list) -{ - int goodbuflen = 0; - int i = 0; - struct aim_tlvlist_t *cur; - - if (!list || !buf || !buflen) - return 0; - - /* do an initial run to test total length */ - for (cur = *list; cur; cur = cur->next) { - goodbuflen += 2 + 2; /* type + len */ - goodbuflen += cur->tlv->length; - } - - if (goodbuflen > buflen) - return 0; /* not enough buffer */ - - /* do the real write-out */ - for (cur = *list; cur; cur = cur->next) { - i += aimutil_put16(buf+i, cur->tlv->type); - i += aimutil_put16(buf+i, cur->tlv->length); - memcpy(buf+i, cur->tlv->value, cur->tlv->length); - i += cur->tlv->length; - } - - return i; -} - - -/** - * aim_gettlv - Grab the Nth TLV of type type in the TLV list list. - * @list: Source chain - * @type: Requested TLV type - * @nth: Index of TLV of type to get - * - * Returns a pointer to an aim_tlv_t of the specified type; - * %NULL on error. The @nth parameter is specified starting at %1. - * In most cases, there will be no more than one TLV of any type - * in a chain. - * - */ -faim_export struct aim_tlv_t *aim_gettlv(struct aim_tlvlist_t *list, u_short type, int nth) -{ - int i; - struct aim_tlvlist_t *cur; - - i = 0; - for (cur = list; cur != NULL; cur = cur->next) - { - if (cur && cur->tlv) - { - if (cur->tlv->type == type) - i++; - if (i >= nth) - return cur->tlv; - } - } - return NULL; -} - -/** - * aim_gettlv_str - Retrieve the Nth TLV in chain as a string. - * @list: Source TLV chain - * @type: TLV type to search for - * @nth: Index of TLV to return - * - * Same as aim_gettlv(), except that the return value is a %NULL- - * terminated string instead of an aim_tlv_t. This is a - * dynamic buffer and must be freed by the caller. - * - */ -faim_export char *aim_gettlv_str(struct aim_tlvlist_t *list, u_short type, int nth) -{ - struct aim_tlv_t *tlv; - char *newstr; - - if (!(tlv = aim_gettlv(list, type, nth))) - return NULL; - - newstr = (char *) malloc(tlv->length + 1); - memcpy(newstr, tlv->value, tlv->length); - *(newstr + tlv->length) = '\0'; - - return newstr; -} - -/** - * aim_gettlv8 - Retrieve the Nth TLV in chain as a 8bit integer. - * @list: Source TLV chain - * @type: TLV type to search for - * @nth: Index of TLV to return - * - * Same as aim_gettlv(), except that the return value is a - * 8bit integer instead of an aim_tlv_t. - * - */ -faim_internal unsigned char aim_gettlv8(struct aim_tlvlist_t *list, unsigned short type, int num) -{ - struct aim_tlv_t *tlv; - - if (!(tlv = aim_gettlv(list, type, num)) || !tlv->value) - return 0; /* erm */ - return aimutil_get8(tlv->value); -} - -/** - * aim_gettlv16 - Retrieve the Nth TLV in chain as a 16bit integer. - * @list: Source TLV chain - * @type: TLV type to search for - * @nth: Index of TLV to return - * - * Same as aim_gettlv(), except that the return value is a - * 16bit integer instead of an aim_tlv_t. - * - */ -faim_internal unsigned short aim_gettlv16(struct aim_tlvlist_t *list, unsigned short type, int num) -{ - struct aim_tlv_t *tlv; - - if (!(tlv = aim_gettlv(list, type, num)) || !tlv->value) - return 0; /* erm */ - return aimutil_get16(tlv->value); -} - -/** - * aim_gettlv32 - Retrieve the Nth TLV in chain as a 32bit integer. - * @list: Source TLV chain - * @type: TLV type to search for - * @nth: Index of TLV to return - * - * Same as aim_gettlv(), except that the return value is a - * 32bit integer instead of an aim_tlv_t. - * - */ -faim_internal unsigned long aim_gettlv32(struct aim_tlvlist_t *list, unsigned short type, int num) -{ - struct aim_tlv_t *tlv; - - if (!(tlv = aim_gettlv(list, type, num)) || !tlv->value) - return 0; /* erm */ - return aimutil_get32(tlv->value); -} - -/** - * aim_grabtlv - Grab a single TLV from a data buffer - * @src: Source data buffer (must be at least 4 bytes long) - * - * Creates a TLV structure aim_tlv_t and returns it - * filled with values from a buffer, possibly including a - * dynamically allocated buffer for the value portion. - * - * Both the aim_tlv_t and the tlv->value pointer - * must be freed by the caller if non-%NULL. - * - */ -faim_export struct aim_tlv_t *aim_grabtlv(u_char *src) -{ - struct aim_tlv_t *dest = NULL; - - dest = aim_createtlv(); - - dest->type = src[0] << 8; - dest->type += src[1]; - - dest->length = src[2] << 8; - dest->length += src[3]; - - dest->value = (u_char *) malloc(dest->length*sizeof(u_char)); - memset(dest->value, 0, dest->length*sizeof(u_char)); - - memcpy(dest->value, &(src[4]), dest->length*sizeof(u_char)); - - return dest; -} - -/** - * aim_grabtlvstr - Grab a single TLV from a data buffer as string - * @src: Source data buffer (must be at least 4 bytes long) - * - * Creates a TLV structure aim_tlv_t and returns it - * filled with values from a buffer, possibly including a - * dynamically allocated buffer for the value portion, which - * is %NULL-terminated as a string. - * - * Both the aim_tlv_t and the tlv->value pointer - * must be freed by the caller if non-%NULL. - * - */ -faim_export struct aim_tlv_t *aim_grabtlvstr(u_char *src) -{ - struct aim_tlv_t *dest = NULL; - - dest = aim_createtlv(); - - dest->type = src[0] << 8; - dest->type += src[1]; - - dest->length = src[2] << 8; - dest->length += src[3]; - - dest->value = (u_char *) malloc((dest->length+1)*sizeof(u_char)); - memset(dest->value, 0, (dest->length+1)*sizeof(u_char)); - - memcpy(dest->value, &(src[4]), dest->length*sizeof(u_char)); - dest->value[dest->length] = '\0'; - - return dest; -} - -/** - * aim_puttlv - Write a aim_tlv_t into a data buffer - * @dest: Destination data buffer - * @newtlv: Source TLV structure - * - * Writes out the passed TLV structure into the buffer. No bounds - * checking is done on the output buffer. - * - * The passed aim_tlv_t is not freed. aim_freetlv() should - * still be called by the caller to free the structure. - * - */ -faim_export int aim_puttlv(u_char *dest, struct aim_tlv_t *newtlv) -{ - int i=0; - - dest[i++] = newtlv->type >> 8; - dest[i++] = newtlv->type & 0x00FF; - dest[i++] = newtlv->length >> 8; - dest[i++] = newtlv->length & 0x00FF; - memcpy(&(dest[i]), newtlv->value, newtlv->length); - i+=newtlv->length; - return i; -} - -/** - * aim_createtlv - Generate an aim_tlv_t structure. - * - * Allocates an empty TLV structure and returns a pointer - * to it; %NULL on error. - * - */ -faim_export struct aim_tlv_t *aim_createtlv(void) -{ - struct aim_tlv_t *newtlv; - - if (!(newtlv = (struct aim_tlv_t *)malloc(sizeof(struct aim_tlv_t)))) - return NULL; - memset(newtlv, 0, sizeof(struct aim_tlv_t)); - return newtlv; -} - -/** - * aim_freetlv - Free a aim_tlv_t structure - * @oldtlv: TLV to be destroyed - * - * Frees both the TLV structure and the value portion. - * - */ -faim_export int aim_freetlv(struct aim_tlv_t **oldtlv) -{ - if (!oldtlv) - return -1; - if (!*oldtlv) - return -1; - if ((*oldtlv)->value) - free((*oldtlv)->value); - free(*(oldtlv)); - (*oldtlv) = NULL; - - return 0; -} - -/** - * aim_puttlv_8 - Write a one-byte TLV. - * @buf: Destination buffer - * @t: TLV type - * @v: Value - * - * Writes a TLV with a one-byte integer value portion. - * - */ -faim_export int aim_puttlv_8(unsigned char *buf, unsigned short t, unsigned char v) -{ - int curbyte=0; - - curbyte += aimutil_put16(buf+curbyte, (unsigned short)(t&0xffff)); - curbyte += aimutil_put16(buf+curbyte, (unsigned short)0x0001); - curbyte += aimutil_put8(buf+curbyte, (unsigned char)(v&0xff)); - - return curbyte; -} - -/** - * aim_puttlv_16 - Write a two-byte TLV. - * @buf: Destination buffer - * @t: TLV type - * @v: Value - * - * Writes a TLV with a two-byte integer value portion. - * - */ -faim_export int aim_puttlv_16(u_char *buf, u_short t, u_short v) -{ - int curbyte=0; - curbyte += aimutil_put16(buf+curbyte, (u_short)(t&0xffff)); - curbyte += aimutil_put16(buf+curbyte, (u_short)0x0002); - curbyte += aimutil_put16(buf+curbyte, (u_short)(v&0xffff)); - return curbyte; -} - -/** - * aim_puttlv_32 - Write a four-byte TLV. - * @buf: Destination buffer - * @t: TLV type - * @v: Value - * - * Writes a TLV with a four-byte integer value portion. - * - */ -faim_export int aim_puttlv_32(u_char *buf, u_short t, u_long v) -{ - int curbyte=0; - curbyte += aimutil_put16(buf+curbyte, (u_short)(t&0xffff)); - curbyte += aimutil_put16(buf+curbyte, (u_short)0x0004); - curbyte += aimutil_put32(buf+curbyte, (u_long)(v&0xffffffff)); - return curbyte; -} - -/** - * aim_puttlv_str - Write a string TLV. - * @buf: Destination buffer - * @t: TLV type - * @l: Length of string - * @v: String to write - * - * Writes a TLV with a string value portion. (Only the first @l - * bytes of the passed string will be written, which should not - * include the terminating NULL.) - * - */ -faim_export int aim_puttlv_str(u_char *buf, u_short t, int l, char *v) -{ - int curbyte; - - curbyte = 0; - curbyte += aimutil_put16(buf+curbyte, (u_short)(t&0xffff)); - curbyte += aimutil_put16(buf+curbyte, (u_short)(l&0xffff)); - if (v) - memcpy(buf+curbyte, (unsigned char *)v, l); - curbyte += l; - return curbyte; -}
--- a/libfaim/aim_txqueue.c Sun Mar 04 22:37:18 2001 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,435 +0,0 @@ -/* - * aim_txqueue.c - * - * Herein lies all the mangement routines for the transmit (Tx) queue. - * - */ - -#include <faim/aim.h> - -#ifndef _WIN32 -#include <sys/socket.h> -#endif - -/* - * Allocate a new tx frame. - * - * This is more for looks than anything else. - * - * Right now, that is. If/when we implement a pool of transmit - * frames, this will become the request-an-unused-frame part. - * - * framing = AIM_FRAMETYPE_OFT/OSCAR - * chan = channel for OSCAR, hdrtype for OFT - * - */ -faim_internal struct command_tx_struct *aim_tx_new(unsigned char framing, int chan, struct aim_conn_t *conn, int datalen) -{ - struct command_tx_struct *newtx; - - if (!conn) { - printf("aim_tx_new: ERROR: no connection specified\n"); - return NULL; - } - - newtx = (struct command_tx_struct *)malloc(sizeof(struct command_tx_struct)); - if (!newtx) - return NULL; - memset(newtx, 0, sizeof(struct command_tx_struct)); - - newtx->conn = conn; - - if(datalen) { - newtx->data = (unsigned char *)malloc(datalen); - newtx->commandlen = datalen; - } else - newtx->data = NULL; - - newtx->hdrtype = framing; - if (newtx->hdrtype == AIM_FRAMETYPE_OSCAR) { - newtx->hdr.oscar.type = chan; - } else if (newtx->hdrtype == AIM_FRAMETYPE_OFT) { - newtx->hdr.oft.type = chan; - newtx->hdr.oft.hdr2len = 0; /* this will get setup by caller */ - } else { - printf("tx_new: unknown framing\n"); - } - - return newtx; -} - -/* - * aim_tx_enqeue__queuebased() - * - * The overall purpose here is to enqueue the passed in command struct - * into the outgoing (tx) queue. Basically... - * 1) Make a scope-irrelevent copy of the struct - * 2) Lock the struct - * 3) Mark as not-sent-yet - * 4) Enqueue the struct into the list - * 5) Unlock the struct once it's linked in - * 6) Return - * - * Note that this is only used when doing queue-based transmitting; - * that is, when sess->tx_enqueue is set to &aim_tx_enqueue__queuebased. - * - */ -faim_internal int aim_tx_enqueue__queuebased(struct aim_session_t *sess, - struct command_tx_struct *newpacket) -{ - struct command_tx_struct *cur; - - if (newpacket->conn == NULL) { - faimdprintf(1, "aim_tx_enqueue: WARNING: enqueueing packet with no connecetion\n"); - newpacket->conn = aim_getconn_type(sess, AIM_CONN_TYPE_BOS); - } - - if (newpacket->hdrtype == AIM_FRAMETYPE_OSCAR) { - /* assign seqnum */ - newpacket->hdr.oscar.seqnum = aim_get_next_txseqnum(newpacket->conn); - } - /* set some more fields */ - newpacket->lock = 1; /* lock */ - newpacket->sent = 0; /* not sent yet */ - newpacket->next = NULL; /* always last */ - - /* see overhead note in aim_rxqueue counterpart */ - if (sess->queue_outgoing == NULL) { - sess->queue_outgoing = newpacket; - } else { - for (cur = sess->queue_outgoing; - cur->next; - cur = cur->next) - ; - cur->next = newpacket; - } - - newpacket->lock = 0; /* unlock so it can be sent */ - -#if debug == 2 - faimdprintf(2, "calling aim_tx_printqueue()\n"); - aim_tx_printqueue(sess); - faimdprintf(2, "back from aim_tx_printqueue()\n"); -#endif - - return 0; -} - -/* - * aim_tx_enqueue__immediate() - * - * Parallel to aim_tx_enqueue__queuebased, however, this bypasses - * the whole queue mess when you want immediate writes to happen. - * - * Basically the same as its __queuebased couterpart, however - * instead of doing a list append, it just calls aim_tx_sendframe() - * right here. - * - */ -faim_internal int aim_tx_enqueue__immediate(struct aim_session_t *sess, struct command_tx_struct *newpacket) -{ - if (newpacket->conn == NULL) { - faimdprintf(1, "aim_tx_enqueue: ERROR: packet has no connection\n"); - if (newpacket->data) - free(newpacket->data); - free(newpacket); - return -1; - } - - if (newpacket->hdrtype == AIM_FRAMETYPE_OSCAR) - newpacket->hdr.oscar.seqnum = aim_get_next_txseqnum(newpacket->conn); - - newpacket->lock = 1; /* lock */ - newpacket->sent = 0; /* not sent yet */ - - aim_tx_sendframe(sess, newpacket); - - if (newpacket->data) - free(newpacket->data); - free(newpacket); - - return 0; -} - -faim_internal int aim_tx_enqueue(struct aim_session_t *sess, struct command_tx_struct *command) -{ - /* - * If we want to send a connection thats inprogress, we have to force - * them to use the queue based version. Otherwise, use whatever they - * want. - */ - if (command && command->conn && (command->conn->status & AIM_CONN_STATUS_INPROGRESS)) { - return aim_tx_enqueue__queuebased(sess, command); - } - return (*sess->tx_enqueue)(sess, command); -} - -/* - * aim_get_next_txseqnum() - * - * This increments the tx command count, and returns the seqnum - * that should be stamped on the next FLAP packet sent. This is - * normally called during the final step of packet preparation - * before enqueuement (in aim_tx_enqueue()). - * - */ -faim_internal unsigned int aim_get_next_txseqnum(struct aim_conn_t *conn) -{ - u_int ret; - - faim_mutex_lock(&conn->seqnum_lock); - ret = ++conn->seqnum; - faim_mutex_unlock(&conn->seqnum_lock); - return ret; -} - -/* - * aim_tx_printqueue() - * - * This is basically for debuging purposes only. It dumps all the - * records in the tx queue and their current status. Very helpful - * if the queue isn't working quite right. - * - */ -#if debug == 2 -faim_internal int aim_tx_printqueue(struct aim_session_t *sess) -{ - struct command_tx_struct *cur; - - faimdprintf(2, "\ncurrent aim_queue_outgoing...\n"); - faimdprintf(2, "\ttype seqnum len lock sent\n"); - - if (sess->queue_outgoing == NULL) - faimdprintf(2, "aim_tx_flushqueue(): queue empty"); - else { - for (cur = sess->queue_outgoing; cur; cur = cur->next) { - faimdprintf(2, "\t %2x %2x %4x %4x %1d %1d\n", - cur->hdrtype, - (cur->hdrtype==AIM_FRAMETYPE_OFT)?cur->hdr.oft.type:cur->hdr.oscar.type, - (cur->hdrtype==AIM_FRAMETYPE_OSCAR)?cur->seqnum:0, - cur->commandlen, cur->lock, - cur->sent); - } - } - - faimdprintf(2, "\n(done printing queue)\n"); - - return 0; -} -#endif - -/* - * aim_tx_flushqueue() - * - * This the function is responsable for putting the queued commands - * onto the wire. This function is critical to the operation of - * the queue and therefore is the most prone to brokenness. It - * seems to be working quite well at this point. - * - * Procedure: - * 1) Traverse the list, only operate on commands that are unlocked - * and haven't been sent yet. - * 2) Lock the struct - * 3) Allocate a temporary buffer to store the finished, fully - * processed packet in. - * 4) Build the packet from the command_tx_struct data. - * 5) Write the packet to the socket. - * 6) If success, mark the packet sent, if fail report failure, do NOT - * mark the packet sent (so it will not get purged and therefore - * be attempted again on next call). - * 7) Unlock the struct. - * 8) Free the temp buffer - * 9) Step to next struct in list and go back to 1. - * - */ -faim_internal int aim_tx_sendframe(struct aim_session_t *sess, struct command_tx_struct *cur) -{ - int buflen = 0; - unsigned char *curPacket; - - if (!cur) - return -1; /* fatal */ - - cur->lock = 1; /* lock the struct */ - - if (cur->hdrtype == AIM_FRAMETYPE_OSCAR) - buflen = cur->commandlen + 6; - else if (cur->hdrtype == AIM_FRAMETYPE_OFT) - buflen = cur->hdr.oft.hdr2len + 8; - else { - cur->lock = 0; - return -1; - } - - /* allocate full-packet buffer */ - if (!(curPacket = (unsigned char *) malloc(buflen))) { - cur->lock = 0; - return -1; - } - - if (cur->hdrtype == AIM_FRAMETYPE_OSCAR) { - /* command byte */ - curPacket[0] = 0x2a; - - /* type/family byte */ - curPacket[1] = cur->hdr.oscar.type; - - /* bytes 3+4: word: FLAP sequence number */ - aimutil_put16(curPacket+2, cur->hdr.oscar.seqnum); - - /* bytes 5+6: word: SNAC len */ - aimutil_put16(curPacket+4, cur->commandlen); - - /* bytes 7 and on: raw: SNAC data */ /* XXX: ye gods! get rid of this! */ - memcpy(&(curPacket[6]), cur->data, cur->commandlen); - - } else if (cur->hdrtype == AIM_FRAMETYPE_OFT) { - int z = 0; - - z += aimutil_put8(curPacket+z, cur->hdr.oft.magic[0]); - z += aimutil_put8(curPacket+z, cur->hdr.oft.magic[1]); - z += aimutil_put8(curPacket+z, cur->hdr.oft.magic[2]); - z += aimutil_put8(curPacket+z, cur->hdr.oft.magic[3]); - - z += aimutil_put16(curPacket+z, cur->hdr.oft.hdr2len + 8); - z += aimutil_put16(curPacket+z, cur->hdr.oft.type); - - memcpy(curPacket+z, cur->hdr.oft.hdr2, cur->hdr.oft.hdr2len); - } - - /* - * For OSCAR, a full image of the raw packet data now in curPacket. - * For OFT, an image of just the bloated header is in curPacket, - * since OFT allows us to do the data in a different write (yay!). - */ - faim_mutex_lock(&cur->conn->active); - if (send(cur->conn->fd, curPacket, buflen, 0) != buflen) { - faim_mutex_unlock(&cur->conn->active); - cur->sent = 1; - aim_conn_close(cur->conn); - return 0; /* bail out */ - } - - if ((cur->hdrtype == AIM_FRAMETYPE_OFT) && cur->commandlen) { - if (send(cur->conn->fd, cur->data, cur->commandlen, 0) != (int)cur->commandlen) { - /* - * Theres nothing we can do about this since we've already sent the - * header! The connection is unstable. - */ - } - } - - cur->sent = 1; /* mark the struct as sent */ - cur->conn->lastactivity = time(NULL); - - faim_mutex_unlock(&cur->conn->active); - -#if debug > 2 - faimdprintf(2, "\nPacket:"); - for (i = 0; i < (cur->commandlen + 6); i++) { - if ((i % 8) == 0) { - faimdprintf(2, "\n\t"); - } - if (curPacket[i] >= ' ' && curPacket[i]<127) { - faimdprintf(2, "%c=%02x ", curPacket[i], curPacket[i]); - } else { - faimdprintf(2, "0x%2x ", curPacket[i]); - } - } - faimdprintf(2, "\n"); -#endif - cur->lock = 0; /* unlock the struct */ - free(curPacket); /* free up full-packet buffer */ - - return 1; /* success */ -} - -faim_export int aim_tx_flushqueue(struct aim_session_t *sess) -{ - struct command_tx_struct *cur; - -#if debug > 1 - int i = 0; -#endif - - if (sess->queue_outgoing == NULL) - return 0; - - faimdprintf(2, "beginning txflush...\n"); - for (cur = sess->queue_outgoing; cur; cur = cur->next) { - /* only process if its unlocked and unsent */ - if (!cur->lock && !cur->sent) { - - if (cur->conn && (cur->conn->status & AIM_CONN_STATUS_INPROGRESS)) - continue; - - /* - * And now for the meager attempt to force transmit - * latency and avoid missed messages. - */ - if ((cur->conn->lastactivity + cur->conn->forcedlatency) >= time(NULL)) { - /* FIXME FIXME -- should be a break! we dont want to block the upper layers */ - sleep((cur->conn->lastactivity + cur->conn->forcedlatency) - time(NULL)); - } - - if (aim_tx_sendframe(sess, cur) == -1) - break; - } - } - - /* purge sent commands from queue */ - aim_tx_purgequeue(sess); - - return 0; -} - -/* - * aim_tx_purgequeue() - * - * This is responsable for removing sent commands from the transmit - * queue. This is not a required operation, but it of course helps - * reduce memory footprint at run time! - * - */ -faim_export void aim_tx_purgequeue(struct aim_session_t *sess) -{ - struct command_tx_struct *cur = NULL; - struct command_tx_struct *tmp; - - if (sess->queue_outgoing == NULL) - return; - - if (sess->queue_outgoing->next == NULL) { - if (!sess->queue_outgoing->lock && sess->queue_outgoing->sent) { - tmp = sess->queue_outgoing; - sess->queue_outgoing = NULL; - if (tmp->hdrtype == AIM_FRAMETYPE_OFT) - free(tmp->hdr.oft.hdr2); - free(tmp->data); - free(tmp); - } - return; - } - - for(cur = sess->queue_outgoing; cur->next != NULL; ) { - if (!cur->next->lock && cur->next->sent) { - tmp = cur->next; - cur->next = tmp->next; - if (tmp->hdrtype == AIM_FRAMETYPE_OFT) - free(tmp->hdr.oft.hdr2); - free(tmp->data); - free(tmp); - } - cur = cur->next; - - /* - * Be careful here. Because of the way we just - * manipulated the pointer, cur may be NULL and - * the for() will segfault doing the check unless - * we find this case first. - */ - if (cur == NULL) - break; - } - return; -}
--- a/libfaim/aim_util.c Sun Mar 04 22:37:18 2001 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,265 +0,0 @@ -/* - * - * - * - */ - -#include <faim/aim.h> -#include <ctype.h> - -#ifdef AIMUTIL_USEMACROS -/* macros in faim/aim.h */ -#else -faim_shortfunc int aimutil_put8(u_char *buf, u_char data) -{ - buf[0] = (u_char)data&0xff; - return 1; -} - -faim_shortfunc u_char aimutil_get8(u_char *buf) -{ - return buf[0]; -} - -/* - * Endian-ness issues here? - */ -faim_shortfunc int aimutil_put16(u_char *buf, u_short data) -{ - buf[0] = (u_char)(data>>8)&0xff; - buf[1] = (u_char)(data)&0xff; - return 2; -} - -faim_shortfunc u_short aimutil_get16(u_char *buf) -{ - u_short val; - val = (buf[0] << 8) & 0xff00; - val+= (buf[1]) & 0xff; - return val; -} - -faim_shortfunc int aimutil_put32(u_char *buf, u_long data) -{ - buf[0] = (u_char)(data>>24)&0xff; - buf[1] = (u_char)(data>>16)&0xff; - buf[2] = (u_char)(data>>8)&0xff; - buf[3] = (u_char)(data)&0xff; - return 4; -} - -faim_shortfunc u_long aimutil_get32(u_char *buf) -{ - u_long val; - val = (buf[0] << 24) & 0xff000000; - val+= (buf[1] << 16) & 0x00ff0000; - val+= (buf[2] << 8) & 0x0000ff00; - val+= (buf[3] ) & 0x000000ff; - return val; -} -#endif /* AIMUTIL_USEMACROS */ - -faim_export faim_shortfunc int aimutil_putstr(u_char *dest, const char *src, int len) -{ - memcpy(dest, src, len); - return len; -} - -/* - * Tokenizing functions. Used to portably replace strtok/sep. - * -- DMP. - * - */ -faim_export int aimutil_tokslen(char *toSearch, int index, char dl) -{ - int curCount = 1; - char *next; - char *last; - int toReturn; - - last = toSearch; - next = strchr(toSearch, dl); - - while(curCount < index && next != NULL) - { - curCount++; - last = next + 1; - next = strchr(last, dl); - } - - if ((curCount < index) || (next == NULL)) - toReturn = strlen(toSearch) - (curCount - 1); - else - toReturn = next - toSearch - (curCount - 1); - - return toReturn; -} - -faim_export int aimutil_itemcnt(char *toSearch, char dl) -{ - int curCount; - char *next; - - curCount = 1; - - next = strchr(toSearch, dl); - - while(next != NULL) - { - curCount++; - next = strchr(next + 1, dl); - } - - return curCount; -} - -faim_export char *aimutil_itemidx(char *toSearch, int index, char dl) -{ - int curCount; - char *next; - char *last; - char *toReturn; - - curCount = 0; - - last = toSearch; - next = strchr(toSearch, dl); - - while(curCount < index && next != NULL) - { - curCount++; - last = next + 1; - next = strchr(last, dl); - } - - if (curCount < index) - { - toReturn = malloc(sizeof(char)); - *toReturn = '\0'; - } - next = strchr(last, dl); - - if (curCount < index) - { - toReturn = malloc(sizeof(char)); - *toReturn = '\0'; - } - else - { - if (next == NULL) - { - toReturn = malloc((strlen(last) + 1) * sizeof(char)); - strcpy(toReturn, last); - } - else - { - toReturn = malloc((next - last + 1) * sizeof(char)); - memcpy(toReturn, last, (next - last)); - toReturn[next - last] = '\0'; - } - } - return toReturn; -} - -/* - * int snlen(const char *) - * - * This takes a screen name and returns its length without - * spaces. If there are no spaces in the SN, then the - * return is equal to that of strlen(). - * - */ -faim_export int aim_snlen(const char *sn) -{ - int i = 0; - const char *curPtr = NULL; - - if (!sn) - return 0; - - curPtr = sn; - while ( (*curPtr) != (char) NULL) { - if ((*curPtr) != ' ') - i++; - curPtr++; - } - - return i; -} - -/* - * int sncmp(const char *, const char *) - * - * This takes two screen names and compares them using the rules - * on screen names for AIM/AOL. Mainly, this means case and space - * insensitivity (all case differences and spacing differences are - * ignored). - * - * Return: 0 if equal - * non-0 if different - * - */ - -faim_export int aim_sncmp(const char *sn1, const char *sn2) -{ - const char *curPtr1 = NULL, *curPtr2 = NULL; - - if (aim_snlen(sn1) != aim_snlen(sn2)) - return 1; - - curPtr1 = sn1; - curPtr2 = sn2; - while ( (*curPtr1 != (char) NULL) && (*curPtr2 != (char) NULL) ) { - if ( (*curPtr1 == ' ') || (*curPtr2 == ' ') ) { - if (*curPtr1 == ' ') - curPtr1++; - if (*curPtr2 == ' ') - curPtr2++; - } else { - if ( toupper(*curPtr1) != toupper(*curPtr2)) - return 1; - curPtr1++; - curPtr2++; - } - } - - return 0; -} - -/* strsep Copyright (C) 1992, 1993 Free Software Foundation, Inc. - strsep is part of the GNU C Library. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - The GNU C 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with the GNU C Library; see the file COPYING.LIB. If - not, write to the Free Software Foundation, Inc., 675 Mass Ave, - Cambridge, MA 02139, USA. */ - -/* Minor changes by and1000 on 15/1/97 to make it go under Nemesis */ - -faim_export char *aim_strsep(char **pp, const char *delim) -{ - char *p, *q; - - if (!(p = *pp)) - return 0; - - if ((q = strpbrk (p, delim))) - { - *pp = q + 1; - *q = '\0'; - } - else - *pp = 0; - - return p; -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfaim/auth.c Mon Mar 05 03:59:32 2001 +0000 @@ -0,0 +1,185 @@ +/* + aim_auth.c + + Deals with the authorizer. + + */ + +#define FAIM_INTERNAL +#include <aim.h> + +/* this just pushes the passed cookie onto the passed connection -- NO SNAC! */ +faim_export int aim_auth_sendcookie(struct aim_session_t *sess, + struct aim_conn_t *conn, + unsigned char *chipsahoy) +{ + struct command_tx_struct *newpacket; + int curbyte=0; + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0001, 4+2+2+AIM_COOKIELEN))) + return -1; + + newpacket->lock = 1; + + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0006); + curbyte += aimutil_put16(newpacket->data+curbyte, AIM_COOKIELEN); + memcpy(newpacket->data+curbyte, chipsahoy, AIM_COOKIELEN); + + return aim_tx_enqueue(sess, newpacket); +} + +faim_export unsigned long aim_auth_clientready(struct aim_session_t *sess, + struct aim_conn_t *conn) +{ + struct aim_tool_version tools[] = { + {0x0001, 0x0003, AIM_TOOL_NEWWIN, 0x0361}, + {0x0007, 0x0001, AIM_TOOL_NEWWIN, 0x0361}, + }; + int i,j; + struct command_tx_struct *newpacket; + int toolcount = sizeof(tools)/sizeof(struct aim_tool_version); + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 1152))) + return -1; + + newpacket->lock = 1; + + i = aim_putsnac(newpacket->data, 0x0001, 0x0002, 0x0000, sess->snac_nextid); + aim_cachesnac(sess, 0x0001, 0x0002, 0x0000, NULL, 0); + + for (j = 0; j < toolcount; j++) { + i += aimutil_put16(newpacket->data+i, tools[j].group); + i += aimutil_put16(newpacket->data+i, tools[j].version); + i += aimutil_put16(newpacket->data+i, tools[j].tool); + i += aimutil_put16(newpacket->data+i, tools[j].toolversion); + } + + newpacket->commandlen = i; + newpacket->lock = 0; + + aim_tx_enqueue(sess, newpacket); + + return sess->snac_nextid; +} + +faim_export unsigned long aim_auth_changepasswd(struct aim_session_t *sess, + struct aim_conn_t *conn, + char *new, char *current) +{ + struct command_tx_struct *newpacket; + int i; + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+4+strlen(current)+4+strlen(new)))) + return -1; + + newpacket->lock = 1; + + i = aim_putsnac(newpacket->data, 0x0007, 0x0004, 0x0000, sess->snac_nextid); + aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0); + + /* new password TLV t(0002) */ + i += aim_puttlv_str(newpacket->data+i, 0x0002, strlen(new), new); + + /* current password TLV t(0012) */ + i += aim_puttlv_str(newpacket->data+i, 0x0012, strlen(current), current); + + aim_tx_enqueue(sess, newpacket); + + return sess->snac_nextid; +} + +faim_export unsigned long aim_auth_setversions(struct aim_session_t *sess, + struct aim_conn_t *conn) +{ + struct command_tx_struct *newpacket; + int i; + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10 + (4*2)))) + return -1; + + newpacket->lock = 1; + + i = aim_putsnac(newpacket->data, 0x0001, 0x0017, 0x0000, sess->snac_nextid); + aim_cachesnac(sess, 0x0001, 0x0017, 0x0000, NULL, 0); + + i += aimutil_put16(newpacket->data+i, 0x0001); + i += aimutil_put16(newpacket->data+i, 0x0003); + + i += aimutil_put16(newpacket->data+i, 0x0007); + i += aimutil_put16(newpacket->data+i, 0x0001); + + newpacket->commandlen = i; + newpacket->lock = 0; + aim_tx_enqueue(sess, newpacket); + + return sess->snac_nextid; +} + +/* + * 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 unsigned long aim_auth_reqconfirm(struct aim_session_t *sess, + struct aim_conn_t *conn) +{ + return aim_genericreq_n(sess, conn, 0x0007, 0x0006); +} + +/* + * Request a bit of account info. + * + * The only known valid tag is 0x0011 (email address). + * + */ +faim_export unsigned long aim_auth_getinfo(struct aim_session_t *sess, + struct aim_conn_t *conn, + unsigned short info) +{ + struct command_tx_struct *newpacket; + int i; + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10 + 4))) + return -1; + + newpacket->lock = 1; + + i = aim_putsnac(newpacket->data, 0x0007, 0x0002, 0x0000, sess->snac_nextid); + aim_cachesnac(sess, 0x0002, 0x0002, 0x0000, NULL, 0); + + i += aimutil_put16(newpacket->data+i, info); + i += aimutil_put16(newpacket->data+i, 0x0000); + + newpacket->commandlen = i; + newpacket->lock = 0; + aim_tx_enqueue(sess, newpacket); + + return sess->snac_nextid; +} + +faim_export unsigned long aim_auth_setemail(struct aim_session_t *sess, + struct aim_conn_t *conn, + char *newemail) +{ + struct command_tx_struct *newpacket; + int i; + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+2+2+strlen(newemail)))) + return -1; + + newpacket->lock = 1; + + i = aim_putsnac(newpacket->data, 0x0007, 0x0004, 0x0000, sess->snac_nextid); + aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0); + + i += aim_puttlv_str(newpacket->data+i, 0x0011, strlen(newemail), newemail); + + aim_tx_enqueue(sess, newpacket); + + return sess->snac_nextid; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfaim/buddylist.c Mon Mar 05 03:59:32 2001 +0000 @@ -0,0 +1,106 @@ + +#define FAIM_INTERNAL +#include <aim.h> + +/* + * aim_add_buddy() + * + * Adds a single buddy to your buddy list after login. + * + * XXX this should just be an extension of setbuddylist() + * + */ +faim_export unsigned long aim_add_buddy(struct aim_session_t *sess, + struct aim_conn_t *conn, + char *sn ) +{ + struct command_tx_struct *newpacket; + int i; + + if(!sn) + return -1; + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+1+strlen(sn)))) + return -1; + + newpacket->lock = 1; + + i = aim_putsnac(newpacket->data, 0x0003, 0x0004, 0x0000, sess->snac_nextid); + i += aimutil_put8(newpacket->data+i, strlen(sn)); + i += aimutil_putstr(newpacket->data+i, sn, strlen(sn)); + + aim_tx_enqueue(sess, newpacket ); + + aim_cachesnac(sess, 0x0003, 0x0004, 0x0000, sn, strlen(sn)+1); + + return sess->snac_nextid; +} + +/* + * XXX generalise to support removing multiple buddies (basically, its + * the same as setbuddylist() but with a different snac subtype). + * + */ +faim_export unsigned long aim_remove_buddy(struct aim_session_t *sess, + struct aim_conn_t *conn, + char *sn ) +{ + struct command_tx_struct *newpacket; + int i; + + if(!sn) + return -1; + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+1+strlen(sn)))) + return -1; + + newpacket->lock = 1; + + i = aim_putsnac(newpacket->data, 0x0003, 0x0005, 0x0000, sess->snac_nextid); + + i += aimutil_put8(newpacket->data+i, strlen(sn)); + i += aimutil_putstr(newpacket->data+i, sn, strlen(sn)); + + aim_tx_enqueue(sess, newpacket); + + aim_cachesnac(sess, 0x0003, 0x0005, 0x0000, sn, strlen(sn)+1); + + return sess->snac_nextid; +} + +faim_internal int aim_parse_buddyrights(struct aim_session_t *sess, + struct command_rx_struct *command, ...) +{ + rxcallback_t userfunc = NULL; + int ret=1; + struct aim_tlvlist_t *tlvlist; + unsigned short maxbuddies = 0, maxwatchers = 0; + + /* + * TLVs follow + */ + if (!(tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10))) + return ret; + + /* + * TLV type 0x0001: Maximum number of buddies. + */ + if (aim_gettlv(tlvlist, 0x0001, 1)) + maxbuddies = aim_gettlv16(tlvlist, 0x0001, 1); + + /* + * TLV type 0x0002: Maximum number of watchers. + * + * XXX: what the hell is a watcher? + * + */ + if (aim_gettlv(tlvlist, 0x0002, 1)) + maxwatchers = aim_gettlv16(tlvlist, 0x0002, 1); + + if ((userfunc = aim_callhandler(sess, command->conn, 0x0003, 0x0003))) + ret = userfunc(sess, command, maxbuddies, maxwatchers); + + aim_freetlvchain(&tlvlist); + + return ret; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfaim/chat.c Mon Mar 05 03:59:32 2001 +0000 @@ -0,0 +1,681 @@ +/* + * aim_chat.c + * + * Routines for the Chat service. + * + */ + +#define FAIM_INTERNAL +#include <aim.h> + +faim_export char *aim_chat_getname(struct aim_conn_t *conn) +{ + if (!conn) + return NULL; + if (conn->type != AIM_CONN_TYPE_CHAT) + return NULL; + + return (char *)conn->priv; /* yuck ! */ +} + +faim_export struct aim_conn_t *aim_chat_getconn(struct aim_session_t *sess, char *name) +{ + struct aim_conn_t *cur; + + faim_mutex_lock(&sess->connlistlock); + for (cur = sess->connlist; cur; cur = cur->next) { + if (cur->type != AIM_CONN_TYPE_CHAT) + continue; + if (!cur->priv) { + faimdprintf(sess, 0, "faim: chat: chat connection with no name! (fd = %d)\n", cur->fd); + continue; + } + if (strcmp((char *)cur->priv, name) == 0) + break; + } + faim_mutex_unlock(&sess->connlistlock); + + return cur; +} + +faim_export int aim_chat_attachname(struct aim_conn_t *conn, char *roomname) +{ + if (!conn || !roomname) + return -1; + + if (conn->priv) + free(conn->priv); + + conn->priv = strdup(roomname); + + return 0; +} + +/* XXX convert this to use tlvchains */ +faim_export unsigned long aim_chat_send_im(struct aim_session_t *sess, + struct aim_conn_t *conn, + char *msg) +{ + + int curbyte,i; + struct command_tx_struct *newpacket; + struct aim_msgcookie_t *cookie; + + if (!sess || !conn || !msg) + return 0; + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 1152))) + return -1; + + newpacket->lock = 1; /* lock struct */ + + curbyte = 0; + curbyte += aim_putsnac(newpacket->data+curbyte, + 0x000e, 0x0005, 0x0000, sess->snac_nextid); + + /* + * Generate a random message cookie + */ + for (i=0;i<8;i++) + curbyte += aimutil_put8(newpacket->data+curbyte, (u_char) rand()); + + cookie = aim_mkcookie(newpacket->data+curbyte-8, AIM_COOKIETYPE_CHAT, NULL); + cookie->data = strdup(conn->priv); /* chat hack dependent */ + + aim_cachecookie(sess, cookie); + + /* + * Channel ID. + */ + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003); + + /* + * Type 1: Unknown. Blank. + */ + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); + + /* + * Type 6: Unknown. Blank. + */ + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0006); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); + + /* + * Type 5: Message block. Contains more TLVs. + * + * This could include other information... We just + * put in a message TLV however. + * + */ + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005); + curbyte += aimutil_put16(newpacket->data+curbyte, strlen(msg)+4); + + /* + * SubTLV: Type 1: Message + */ + curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x0001, strlen(msg), msg); + + newpacket->commandlen = curbyte; + + newpacket->lock = 0; + aim_tx_enqueue(sess, newpacket); + + return (sess->snac_nextid++); +} + +/* + * 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 unsigned long aim_chat_join(struct aim_session_t *sess, + struct aim_conn_t *conn, + u_short exchange, + const char *roomname) +{ + struct command_tx_struct *newpacket; + int i; + + if (!sess || !conn || !roomname) + return 0; + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+9+strlen(roomname)+2))) + return -1; + + newpacket->lock = 1; + + i = aim_putsnac(newpacket->data, 0x0001, 0x0004, 0x0000, sess->snac_nextid); + + i+= aimutil_put16(newpacket->data+i, 0x000e); + + /* + * this is techinally a TLV, but we can't use normal functions + * because we need the extraneous nulls and other weird things. + */ + i+= aimutil_put16(newpacket->data+i, 0x0001); + i+= aimutil_put16(newpacket->data+i, 2+1+strlen(roomname)+2); + i+= aimutil_put16(newpacket->data+i, exchange); + i+= aimutil_put8(newpacket->data+i, strlen(roomname)); + i+= aimutil_putstr(newpacket->data+i, roomname, strlen(roomname)); + i+= aimutil_put16(newpacket->data+i, 0x0000); /* instance? */ + + /* + * Chat hack. + * + * XXX: A problem occurs here if we request a channel + * join but it fails....pendingjoin will be nonnull + * even though the channel is never going to get a + * redirect! + * + */ + sess->pendingjoin = strdup(roomname); + sess->pendingjoinexchange = exchange; + + newpacket->lock = 0; + aim_tx_enqueue(sess, newpacket); + + aim_cachesnac(sess, 0x0001, 0x0004, 0x0000, roomname, strlen(roomname)+1); + + return sess->snac_nextid; +} + +faim_internal int aim_chat_readroominfo(u_char *buf, struct aim_chat_roominfo *outinfo) +{ + int namelen = 0; + int i = 0; + + if (!buf || !outinfo) + return 0; + + outinfo->exchange = aimutil_get16(buf+i); + i += 2; + + namelen = aimutil_get8(buf+i); + i += 1; + + outinfo->name = (char *)malloc(namelen+1); + memcpy(outinfo->name, buf+i, namelen); + outinfo->name[namelen] = '\0'; + i += namelen; + + outinfo->instance = aimutil_get16(buf+i); + i += 2; + + return i; +} + + +/* + * General room information. Lots of stuff. + * + * Values I know are in here but I havent attached + * them to any of the 'Unknown's: + * - Language (English) + * + * SNAC 000e/0002 + */ +faim_internal int aim_chat_parse_infoupdate(struct aim_session_t *sess, + struct command_rx_struct *command) +{ + struct aim_userinfo_s *userinfo = NULL; + rxcallback_t userfunc=NULL; + int ret = 1, i = 0; + int usercount = 0; + u_char detaillevel = 0; + char *roomname = NULL; + struct aim_chat_roominfo roominfo; + u_short tlvcount = 0; + struct aim_tlvlist_t *tlvlist; + char *roomdesc = NULL; + unsigned short unknown_c9 = 0; + unsigned long creationtime = 0; + unsigned short maxmsglen = 0; + unsigned short unknown_d2 = 0, unknown_d5 = 0; + + i = 10; + i += aim_chat_readroominfo(command->data+i, &roominfo); + + detaillevel = aimutil_get8(command->data+i); + i++; + + if (detaillevel != 0x02) { + if (detaillevel == 0x01) + faimdprintf(sess, 0, "faim: chat_roomupdateinfo: detail level 1 not supported\n"); + else + faimdprintf(sess, 0, "faim: chat_roomupdateinfo: unknown detail level %d\n", detaillevel); + return 1; + } + + tlvcount = aimutil_get16(command->data+i); + i += 2; + + /* + * Everything else are TLVs. + */ + tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i); + + /* + * TLV type 0x006a is the room name in Human Readable Form. + */ + if (aim_gettlv(tlvlist, 0x006a, 1)) + roomname = aim_gettlv_str(tlvlist, 0x006a, 1); + + /* + * Type 0x006f: Number of occupants. + */ + if (aim_gettlv(tlvlist, 0x006f, 1)) + usercount = aim_gettlv16(tlvlist, 0x006f, 1); + + /* + * Type 0x0073: Occupant list. + */ + if (aim_gettlv(tlvlist, 0x0073, 1)) { + int curoccupant = 0; + struct aim_tlv_t *tmptlv; + + tmptlv = aim_gettlv(tlvlist, 0x0073, 1); + + /* Allocate enough userinfo structs for all occupants */ + userinfo = calloc(usercount, sizeof(struct aim_userinfo_s)); + + i = 0; + while (curoccupant < usercount) + i += aim_extractuserinfo(sess, tmptlv->value+i, &userinfo[curoccupant++]); + } + + /* + * Type 0x00c9: Unknown. (2 bytes) + */ + if (aim_gettlv(tlvlist, 0x00c9, 1)) + unknown_c9 = aim_gettlv16(tlvlist, 0x00c9, 1); + + /* + * Type 0x00ca: Creation time (4 bytes) + */ + if (aim_gettlv(tlvlist, 0x00ca, 1)) + creationtime = aim_gettlv32(tlvlist, 0x00ca, 1); + + /* + * Type 0x00d1: Maximum Message Length + */ + if (aim_gettlv(tlvlist, 0x00d1, 1)) + maxmsglen = aim_gettlv16(tlvlist, 0x00d1, 1); + + /* + * Type 0x00d2: Unknown. (2 bytes) + */ + if (aim_gettlv(tlvlist, 0x00d2, 1)) + unknown_d2 = aim_gettlv16(tlvlist, 0x00d2, 1); + + /* + * Type 0x00d3: Room Description + */ + if (aim_gettlv(tlvlist, 0x00d3, 1)) + roomdesc = aim_gettlv_str(tlvlist, 0x00d3, 1); + + /* + * Type 0x00d5: Unknown. (1 byte) + */ + if (aim_gettlv(tlvlist, 0x00d5, 1)) + unknown_d5 = aim_gettlv8(tlvlist, 0x00d5, 1); + + + if ((userfunc = aim_callhandler(sess, command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_ROOMINFOUPDATE))) { + ret = userfunc(sess, + command, + &roominfo, + roomname, + usercount, + userinfo, + roomdesc, + unknown_c9, + creationtime, + maxmsglen, + unknown_d2, + unknown_d5); + } + free(roominfo.name); + free(userinfo); + free(roomname); + free(roomdesc); + aim_freetlvchain(&tlvlist); + + return ret; +} + +faim_internal int aim_chat_parse_joined(struct aim_session_t *sess, + struct command_rx_struct *command) +{ + struct aim_userinfo_s *userinfo = NULL; + rxcallback_t userfunc=NULL; + int i = 10, curcount = 0, ret = 1; + + while (i < command->commandlen) { + curcount++; + userinfo = realloc(userinfo, curcount * sizeof(struct aim_userinfo_s)); + i += aim_extractuserinfo(sess, command->data+i, &userinfo[curcount-1]); + } + + if ((userfunc = aim_callhandler(sess, command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERJOIN))) { + ret = userfunc(sess, + command, + curcount, + userinfo); + } + + free(userinfo); + + return ret; +} + +faim_internal int aim_chat_parse_leave(struct aim_session_t *sess, + struct command_rx_struct *command) +{ + + struct aim_userinfo_s *userinfo = NULL; + rxcallback_t userfunc=NULL; + int i = 10, curcount = 0, ret = 1; + + while (i < command->commandlen) { + curcount++; + userinfo = realloc(userinfo, curcount * sizeof(struct aim_userinfo_s)); + i += aim_extractuserinfo(sess, command->data+i, &userinfo[curcount-1]); + } + + if ((userfunc = aim_callhandler(sess, command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERLEAVE))) { + ret = userfunc(sess, + command, + curcount, + userinfo); + } + + free(userinfo); + + return ret; +} + +/* + * 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 + * + */ +faim_internal int aim_chat_parse_incoming(struct aim_session_t *sess, + struct command_rx_struct *command) +{ + struct aim_userinfo_s userinfo; + rxcallback_t userfunc=NULL; + int ret = 1, i = 0, z = 0; + unsigned char cookie[8]; + int channel; + struct aim_tlvlist_t *outerlist; + char *msg = NULL; + struct aim_msgcookie_t *ck; + + memset(&userinfo, 0x00, sizeof(struct aim_userinfo_s)); + + i = 10; /* skip snac */ + + /* + * ICBM Cookie. Cache it. + */ + for (z=0; z<8; z++,i++) + cookie[z] = command->data[i]; + + if ((ck = aim_uncachecookie(sess, cookie, AIM_COOKIETYPE_CHAT))) { + if (ck->data) + free(ck->data); + free(ck); + } + + /* + * Channel ID + * + * Channels 1 and 2 are implemented in the normal ICBM + * parser. + * + * We only do channel 3 here. + * + */ + channel = aimutil_get16(command->data+i); + i += 2; + + if (channel != 0x0003) { + faimdprintf(sess, 0, "faim: chat_incoming: unknown channel! (0x%04x)\n", channel); + return 1; + } + + /* + * Start parsing TLVs right away. + */ + outerlist = aim_readtlvchain(command->data+i, command->commandlen-i); + + /* + * Type 0x0003: Source User Information + */ + if (aim_gettlv(outerlist, 0x0003, 1)) { + struct aim_tlv_t *userinfotlv; + + userinfotlv = aim_gettlv(outerlist, 0x0003, 1); + aim_extractuserinfo(sess, userinfotlv->value, &userinfo); + } + + /* + * Type 0x0001: Unknown. + */ + if (aim_gettlv(outerlist, 0x0001, 1)) + ; + + /* + * Type 0x0005: Message Block. Conains more TLVs. + */ + if (aim_gettlv(outerlist, 0x0005, 1)) + { + struct aim_tlvlist_t *innerlist; + struct aim_tlv_t *msgblock; + + msgblock = aim_gettlv(outerlist, 0x0005, 1); + innerlist = aim_readtlvchain(msgblock->value, msgblock->length); + + /* + * Type 0x0001: Message. + */ + if (aim_gettlv(innerlist, 0x0001, 1)) + msg = aim_gettlv_str(innerlist, 0x0001, 1); + + aim_freetlvchain(&innerlist); + } + + userfunc = aim_callhandler(sess, command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_INCOMINGMSG); + if (userfunc) + { + ret = userfunc(sess, + command, + &userinfo, + msg); + } + free(msg); + aim_freetlvchain(&outerlist); + + return ret; +} + +faim_export unsigned long aim_chat_clientready(struct aim_session_t *sess, + struct aim_conn_t *conn) +{ + struct command_tx_struct *newpacket; + int i; + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 0x20))) + return -1; + + newpacket->lock = 1; + + i = aim_putsnac(newpacket->data, 0x0001, 0x0002, 0x0000, sess->snac_nextid); + + i+= aimutil_put16(newpacket->data+i, 0x000e); + i+= aimutil_put16(newpacket->data+i, 0x0001); + + i+= aimutil_put16(newpacket->data+i, 0x0004); + i+= aimutil_put16(newpacket->data+i, 0x0001); + + i+= aimutil_put16(newpacket->data+i, 0x0001); + i+= aimutil_put16(newpacket->data+i, 0x0003); + + i+= aimutil_put16(newpacket->data+i, 0x0004); + i+= aimutil_put16(newpacket->data+i, 0x0686); + + newpacket->lock = 0; + aim_tx_enqueue(sess, newpacket); + + return (sess->snac_nextid++); +} + +faim_export int aim_chat_leaveroom(struct aim_session_t *sess, char *name) +{ + struct aim_conn_t *conn; + + if ((conn = aim_chat_getconn(sess, name))) + aim_conn_close(conn); + + if (!conn) + return -1; + return 0; +} + +/* + * conn must be a BOS connection! + */ +faim_export unsigned long aim_chat_invite(struct aim_session_t *sess, + struct aim_conn_t *conn, + char *sn, + char *msg, + u_short exchange, + char *roomname, + u_short instance) +{ + struct command_tx_struct *newpacket; + int i,curbyte=0; + struct aim_msgcookie_t *cookie; + struct aim_invite_priv *priv; + + if (!sess || !conn || !sn || !msg || !roomname) + return -1; + + if (conn->type != AIM_CONN_TYPE_BOS) + return -1; + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 1152+strlen(sn)+strlen(roomname)+strlen(msg)))) + return -1; + + newpacket->lock = 1; + + curbyte = aim_putsnac(newpacket->data, 0x0004, 0x0006, 0x0000, sess->snac_nextid); + + /* + * Cookie + */ + for (i=0;i<8;i++) + curbyte += aimutil_put8(newpacket->data+curbyte, (u_char)rand()); + + /* XXX this should get uncached by the unwritten 'invite accept' handler */ + if(!(priv = calloc(sizeof(struct aim_invite_priv), 1))) + return -1; + priv->sn = strdup(sn); + priv->roomname = strdup(roomname); + priv->exchange = exchange; + priv->instance = instance; + + if(!(cookie = aim_mkcookie(newpacket->data+curbyte-8, AIM_COOKIETYPE_INVITE, priv))) + return -1; + aim_cachecookie(sess, cookie); + + /* + * Channel (2) + */ + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002); + + /* + * Dest sn + */ + curbyte += aimutil_put8(newpacket->data+curbyte, strlen(sn)); + curbyte += aimutil_putstr(newpacket->data+curbyte, sn, strlen(sn)); + + /* + * TLV t(0005) + */ + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x28+strlen(msg)+0x04+0x03+strlen(roomname)+0x02); + + /* + * Unknown info + */ + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x3131); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x3538); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x3446); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x4100); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x748f); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x2420); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x6287); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x11d1); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x8222); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x4445); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x5354); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); + + /* + * TLV t(000a) -- Unknown + */ + curbyte += aimutil_put16(newpacket->data+curbyte, 0x000a); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001); + + /* + * TLV t(000f) -- Unknown + */ + curbyte += aimutil_put16(newpacket->data+curbyte, 0x000f); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); + + /* + * TLV t(000c) -- Invitation message + */ + curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x000c, strlen(msg), msg); + + /* + * TLV t(2711) -- Container for room information + */ + curbyte += aimutil_put16(newpacket->data+curbyte, 0x2711); + curbyte += aimutil_put16(newpacket->data+curbyte, 3+strlen(roomname)+2); + curbyte += aimutil_put16(newpacket->data+curbyte, exchange); + curbyte += aimutil_put8(newpacket->data+curbyte, strlen(roomname)); + curbyte += aimutil_putstr(newpacket->data+curbyte, roomname, strlen(roomname)); + curbyte += aimutil_put16(newpacket->data+curbyte, instance); + + newpacket->commandlen = curbyte; + newpacket->lock = 0; + aim_tx_enqueue(sess, newpacket); + + return (sess->snac_nextid++); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfaim/chatnav.c Mon Mar 05 03:59:32 2001 +0000 @@ -0,0 +1,428 @@ +/* + * 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 <aim.h> + +/* + * conn must be a chatnav connection! + */ +faim_export unsigned long aim_chatnav_reqrights(struct aim_session_t *sess, + struct aim_conn_t *conn) +{ + aim_genericreq_n(sess, conn, 0x000d, 0x0002); + + return sess->snac_nextid; +} + +faim_export unsigned long aim_chatnav_clientready(struct aim_session_t *sess, + struct aim_conn_t *conn) +{ + struct command_tx_struct *newpacket; + int i; + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 0x20))) + return -1; + + newpacket->lock = 1; + + i = aim_putsnac(newpacket->data, 0x0001, 0x0002, 0x0000, sess->snac_nextid); + + i+= aimutil_put16(newpacket->data+i, 0x000d); + i+= aimutil_put16(newpacket->data+i, 0x0001); + + i+= aimutil_put16(newpacket->data+i, 0x0004); + i+= aimutil_put16(newpacket->data+i, 0x0001); + + i+= aimutil_put16(newpacket->data+i, 0x0001); + i+= aimutil_put16(newpacket->data+i, 0x0003); + + i+= aimutil_put16(newpacket->data+i, 0x0004); + i+= aimutil_put16(newpacket->data+i, 0x0686); + + aim_tx_enqueue(sess, newpacket); + + return (sess->snac_nextid++); +} + +/* + * Since multiple things can trigger this callback, + * we must lookup the snacid to determine the original + * snac subtype that was called. + */ +faim_internal int aim_chatnav_parse_info(struct aim_session_t *sess, struct command_rx_struct *command) +{ + struct aim_snac_t *snac; + u_long snacid; + rxcallback_t userfunc; + int ret=1; + + snacid = aimutil_get32(command->data+6); + snac = aim_remsnac(sess, snacid); + + if (!snac) { + faimdprintf(sess, 0, "faim: chatnav_parse_info: received response to unknown request! (%08lx)\n", snacid); + return 1; + } + + if (snac->family != 0x000d) { + faimdprintf(sess, 0, "faim: chatnav_parse_info: recieved response that maps to corrupt request! (fam=%04x)\n", snac->family); + return 1; + } + + /* + * We now know what the original SNAC subtype was. + */ + switch(snac->type) + { + case 0x0002: /* request chat rights */ + { + struct aim_tlvlist_t *tlvlist; + struct aim_chat_exchangeinfo *exchanges = NULL; + int curexchange = 0; + struct aim_tlv_t *exchangetlv; + u_char maxrooms = 0; + struct aim_tlvlist_t *innerlist; + + tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10); + + /* + * Type 0x0002: Maximum concurrent rooms. + */ + if (aim_gettlv(tlvlist, 0x0002, 1)) + maxrooms = aim_gettlv8(tlvlist, 0x0002, 1); + + /* + * Type 0x0003: Exchange information + * + * There can be any number of these, each one + * representing another exchange. + * + */ + curexchange = 0; + while ((exchangetlv = aim_gettlv(tlvlist, 0x0003, curexchange+1))) + { + curexchange++; + exchanges = realloc(exchanges, curexchange * sizeof(struct aim_chat_exchangeinfo)); + + + /* exchange number */ + exchanges[curexchange-1].number = aimutil_get16(exchangetlv->value); + innerlist = aim_readtlvchain(exchangetlv->value+2, exchangetlv->length-2); + + /* + * Type 0x000d: Unknown. + */ + if (aim_gettlv(innerlist, 0x000d, 1)) + ; + + /* + * Type 0x0004: Unknown + */ + if (aim_gettlv(innerlist, 0x0004, 1)) + ; + + /* + * Type 0x0002: Unknown + */ + if (aim_gettlv(innerlist, 0x0002, 1)) { + unsigned short classperms; + + classperms = aim_gettlv16(innerlist, 0x0002, 1); + + faimdprintf(sess, 1, "faim: class permissions %x\n", classperms); + } + + /* + * Type 0x00c9: Unknown + */ + if (aim_gettlv(innerlist, 0x00c9, 1)) + ; + + /* + * Type 0x00ca: Creation Date + */ + if (aim_gettlv(innerlist, 0x00ca, 1)) + ; + + /* + * Type 0x00d0: Mandatory Channels? + */ + if (aim_gettlv(innerlist, 0x00d0, 1)) + ; + + /* + * Type 0x00d1: Maximum Message length + */ + if (aim_gettlv(innerlist, 0x00d1, 1)) + ; + + /* + * Type 0x00d2: Maximum Occupancy? + */ + if (aim_gettlv(innerlist, 0x00d2, 1)) + ; + + /* + * Type 0x00d3: Exchange Name + */ + if (aim_gettlv(innerlist, 0x00d3, 1)) + exchanges[curexchange-1].name = aim_gettlv_str(innerlist, 0x00d3, 1); + else + exchanges[curexchange-1].name = NULL; + + /* + * Type 0x00d5: Creation Permissions + * + * 0 Creation not allowed + * 1 Room creation allowed + * 2 Exchange creation allowed + * + */ + if (aim_gettlv(innerlist, 0x00d5, 1)) { + unsigned char createperms; + + createperms = aim_gettlv8(innerlist, 0x00d5, 1); + } + + /* + * Type 0x00d6: Character Set (First Time) + */ + if (aim_gettlv(innerlist, 0x00d6, 1)) + exchanges[curexchange-1].charset1 = aim_gettlv_str(innerlist, 0x00d6, 1); + else + exchanges[curexchange-1].charset1 = NULL; + + /* + * Type 0x00d7: Language (First Time) + */ + if (aim_gettlv(innerlist, 0x00d7, 1)) + exchanges[curexchange-1].lang1 = aim_gettlv_str(innerlist, 0x00d7, 1); + else + exchanges[curexchange-1].lang1 = NULL; + + /* + * Type 0x00d8: Character Set (Second Time) + */ + if (aim_gettlv(innerlist, 0x00d8, 1)) + exchanges[curexchange-1].charset2 = aim_gettlv_str(innerlist, 0x00d8, 1); + else + exchanges[curexchange-1].charset2 = NULL; + + /* + * Type 0x00d9: Language (Second Time) + */ + if (aim_gettlv(innerlist, 0x00d9, 1)) + exchanges[curexchange-1].lang2 = aim_gettlv_str(innerlist, 0x00d9, 1); + else + exchanges[curexchange-1].lang2 = NULL; + + aim_freetlvchain(&innerlist); + } + + /* + * Call client. + */ + if ((userfunc = aim_callhandler(sess, command->conn, 0x000d, 0x0009))) + ret = userfunc(sess, command, snac->type, maxrooms, curexchange, exchanges); + curexchange--; + while(curexchange >= 0) + { + if (exchanges[curexchange].name) + free(exchanges[curexchange].name); + if (exchanges[curexchange].charset1) + free(exchanges[curexchange].charset1); + if (exchanges[curexchange].lang1) + free(exchanges[curexchange].lang1); + if (exchanges[curexchange].charset2) + free(exchanges[curexchange].charset2); + if (exchanges[curexchange].lang2) + free(exchanges[curexchange].lang2); + curexchange--; + } + free(exchanges); + aim_freetlvchain(&tlvlist); + + break; + } + case 0x0003: /* request exchange info */ + faimdprintf(sess, 0, "chatnav_parse_info: resposne to exchange info\n"); + break; + case 0x0004: /* request room info */ + faimdprintf(sess, 0, "chatnav_parse_info: response to room info\n"); + break; + case 0x0005: /* request more room info */ + faimdprintf(sess, 0, "chatnav_parse_info: response to more room info\n"); + break; + case 0x0006: /* request occupant list */ + faimdprintf(sess, 0, "chatnav_parse_info: response to occupant info\n"); + break; + case 0x0007: /* search for a room */ + faimdprintf(sess, 0, "chatnav_parse_info: search results\n"); + break; + case 0x0008: { /* create room */ + /* + 000d 0009 0000 0000 0010 + + 0004 0053 + 0004 -- exchange + 0c 7a 6f6f 6f6d 7a6f 6f6f 6d34 32 cookie/name + 0000 -- instance + 02 -- detail level + 0007 -- unknown! + 006a 000c 7a 6f 6f6f 6d7a 6f6f 6f6d 3432 -- fully qualified name + 00c9 0002 0011 -- flags + 00ca 0004 39c0 0883 -- create time + 00d1 0002 0200 -- max msg len + 00d2 0002 0018 -- max occupants + 00d3 000c -- name + 7a6f 6f6f 6d7a 6f6f 6f6d 3432 + 00d5 0001 02 -- creation permission + */ + struct aim_tlvlist_t *tlvlist, *innerlist; + char *ck = NULL, *fqcn = NULL, *name = NULL; + unsigned short exchange = 0, instance = 0, unknown = 0, flags = 0, maxmsglen = 0, maxoccupancy = 0; + unsigned long createtime = 0; + unsigned char createperms = 0; + int i, cklen; + struct aim_tlv_t *bigblock; + + i = 10; + if (!(tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i))) { + faimdprintf(sess, 0, "unable to read top tlv in create room response\n"); + break; + } + + if (!(bigblock = aim_gettlv(tlvlist, 0x0004, 1))) { + faimdprintf(sess, 0, "no bigblock in top tlv in create room response\n"); + aim_freetlvchain(&tlvlist); + break; + } + i = 0; + + exchange = aimutil_get16(bigblock->value+i); + i += 2; + + cklen = aimutil_get8(bigblock->value+i); + i++; + + ck = malloc(cklen+1); + memcpy(ck, bigblock->value+i, cklen); + ck[cklen] = '\0'; + i += cklen; + + instance = aimutil_get16(bigblock->value+i); + i += 2; + + if (aimutil_get8(bigblock->value+i) != 0x02) { + faimdprintf(sess, 0, "unknown detaillevel in create room response (0x%02x)\n", aimutil_get8(bigblock->value+i)); + aim_freetlvchain(&tlvlist); + free(ck); + break; + } + i += 1; + + unknown = aimutil_get16(bigblock->value+i); + i += 2; + + if (!(innerlist = aim_readtlvchain(bigblock->value+i, bigblock->length-i))) { + faimdprintf(sess, 0, "unable to read inner tlv chain in create room response\n"); + aim_freetlvchain(&tlvlist); + free(ck); + break; + } + + if (aim_gettlv(innerlist, 0x006a, 1)) + fqcn = aim_gettlv_str(innerlist, 0x006a, 1); + + if (aim_gettlv(innerlist, 0x00c9, 1)) + flags = aim_gettlv16(innerlist, 0x00c9, 1); + + if (aim_gettlv(innerlist, 0x00ca, 1)) + createtime = aim_gettlv32(innerlist, 0x00ca, 1); + + if (aim_gettlv(innerlist, 0x00d1, 1)) + maxmsglen = aim_gettlv16(innerlist, 0x00d1, 1); + + if (aim_gettlv(innerlist, 0x00d2, 1)) + maxoccupancy = aim_gettlv16(innerlist, 0x00d2, 1); + + if (aim_gettlv(innerlist, 0x00d3, 1)) + name = aim_gettlv_str(innerlist, 0x00d3, 1); + + if (aim_gettlv(innerlist, 0x00d5, 1)) + createperms = aim_gettlv8(innerlist, 0x00d5, 1); + + if ((userfunc = aim_callhandler(sess, command->conn, 0x000d, 0x0009))) { + ret = userfunc(sess, command, snac->type, fqcn, instance, exchange, flags, createtime, maxmsglen, maxoccupancy, createperms, unknown, name, ck); + } + + if (ck) + free(ck); + if (name) + free(name); + if (fqcn) + free(fqcn); + aim_freetlvchain(&innerlist); + aim_freetlvchain(&tlvlist); + + break; + } + default: /* unknown */ + faimdprintf(sess, 0, "chatnav_parse_info: unknown request subtype (%04x)\n", snac->type); + } + + if (snac && snac->data) + free(snac->data); + if (snac) + free(snac); + + return ret; +} + +faim_export unsigned long aim_chatnav_createroom(struct aim_session_t *sess, + struct aim_conn_t *conn, + char *name, + u_short exchange) +{ + struct command_tx_struct *newpacket; + int i; + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+12+strlen("invite")+strlen(name)))) + return -1; + + newpacket->lock = 1; + + i = aim_putsnac(newpacket->data, 0x000d, 0x0008, 0x0000, sess->snac_nextid); + + /* exchange */ + i+= aimutil_put16(newpacket->data+i, exchange); + + /* room cookie */ + i+= aimutil_put8(newpacket->data+i, strlen("invite")); + i+= aimutil_putstr(newpacket->data+i, "invite", strlen("invite")); + + /* instance */ + i+= aimutil_put16(newpacket->data+i, 0xffff); + + /* detail level */ + i+= aimutil_put8(newpacket->data+i, 0x01); + + /* tlvcount */ + i+= aimutil_put16(newpacket->data+i, 0x0001); + + /* room name */ + i+= aim_puttlv_str(newpacket->data+i, 0x00d3, strlen(name), name); + + aim_cachesnac(sess, 0x000d, 0x0008, 0x0000, NULL, 0); + + aim_tx_enqueue(sess, newpacket); + + return sess->snac_nextid; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfaim/conn.c Mon Mar 05 03:59:32 2001 +0000 @@ -0,0 +1,872 @@ + +/* + * aim_conn.c + * + * Does all this gloriously nifty connection handling stuff... + * + */ + +#define FAIM_INTERNAL +#include <aim.h> + +#ifndef _WIN32 +#include <netdb.h> +#include <sys/socket.h> +#include <netinet/in.h> +#endif + +/** + * aim_connrst - Clears out connection list, killing remaining connections. + * @sess: Session to be cleared + * + * Clears out the connection list and kills any connections left. + * + */ +faim_internal void aim_connrst(struct aim_session_t *sess) +{ + faim_mutex_init(&sess->connlistlock); + if (sess->connlist) { + struct aim_conn_t *cur = sess->connlist, *tmp; + + while(cur) { + tmp = cur->next; + aim_conn_close(cur); + free(cur); + cur = tmp; + } + } + sess->connlist = NULL; + return; +} + +/** + * aim_conn_init - Reset a connection to default values. + * @deadconn: Connection to be reset + * + * Initializes and/or resets a connection structure. + * + */ +static void aim_conn_init(struct aim_conn_t *deadconn) +{ + if (!deadconn) + return; + + deadconn->fd = -1; + deadconn->subtype = -1; + deadconn->type = -1; + deadconn->seqnum = 0; + deadconn->lastactivity = 0; + deadconn->forcedlatency = 0; + deadconn->handlerlist = NULL; + deadconn->priv = NULL; + faim_mutex_init(&deadconn->active); + faim_mutex_init(&deadconn->seqnum_lock); + + return; +} + +/** + * aim_conn_getnext - Gets a new connection structure. + * @sess: Session + * + * Allocate a new empty connection structure. + * + */ +faim_internal struct aim_conn_t *aim_conn_getnext(struct aim_session_t *sess) +{ + struct aim_conn_t *newconn, *cur; + + if (!(newconn = malloc(sizeof(struct aim_conn_t)))) + return NULL; + + memset(newconn, 0, sizeof(struct aim_conn_t)); + aim_conn_init(newconn); + newconn->next = NULL; + + faim_mutex_lock(&sess->connlistlock); + if (sess->connlist == NULL) + sess->connlist = newconn; + else { + for (cur = sess->connlist; cur->next; cur = cur->next) + ; + cur->next = newconn; + } + faim_mutex_unlock(&sess->connlistlock); + + return newconn; +} + +/** + * aim_conn_kill - Close and free a connection. + * @sess: Session for the connection + * @deadconn: Connection to be freed + * + * Close, clear, and free a connection structure. Should never be + * called from within libfaim. + * + */ +faim_export void aim_conn_kill(struct aim_session_t *sess, struct aim_conn_t **deadconn) +{ + struct aim_conn_t *cur; + + if (!deadconn || !*deadconn) + return; + + faim_mutex_lock(&sess->connlistlock); + if (sess->connlist == NULL) + ; + else if (sess->connlist->next == NULL) { + if (sess->connlist == *deadconn) + sess->connlist = NULL; + } else { + cur = sess->connlist; + while (cur->next) { + if (cur->next == *deadconn) { + cur->next = cur->next->next; + break; + } + cur = cur->next; + } + } + faim_mutex_unlock(&sess->connlistlock); + + /* XXX: do we need this for txqueue too? */ + aim_rxqueue_cleanbyconn(sess, *deadconn); + + if ((*deadconn)->fd != -1) + aim_conn_close(*deadconn); + if ((*deadconn)->priv) + free((*deadconn)->priv); + free(*deadconn); + deadconn = NULL; + + return; +} + +/** + * aim_conn_close - Close a connection + * @deadconn: Connection to close + * + * Close (but not free) a connection. + * + * This leaves everything untouched except for clearing the + * handler list and setting the fd to -1 (used to recognize + * dead connections). + * + */ +faim_export void aim_conn_close(struct aim_conn_t *deadconn) +{ + + faim_mutex_destroy(&deadconn->active); + faim_mutex_destroy(&deadconn->seqnum_lock); + if (deadconn->fd >= 3) + close(deadconn->fd); + deadconn->fd = -1; + if (deadconn->handlerlist) + aim_clearhandlers(deadconn); + + return; +} + +/** + * aim_getconn_type - Find a connection of a specific type + * @sess: Session to search + * @type: Type of connection to look for + * + * Searches for a connection of the specified type in the + * specified session. Returns the first connection of that + * type found. + * + */ +faim_export struct aim_conn_t *aim_getconn_type(struct aim_session_t *sess, + int type) +{ + struct aim_conn_t *cur; + + faim_mutex_lock(&sess->connlistlock); + for (cur = sess->connlist; cur; cur = cur->next) { + if ((cur->type == type) && !(cur->status & AIM_CONN_STATUS_INPROGRESS)) + break; + } + faim_mutex_unlock(&sess->connlistlock); + return cur; +} + +/** + * aim_proxyconnect - An extrememly quick and dirty SOCKS5 interface. + * @sess: Session to connect + * @host: Host to connect to + * @port: Port to connect to + * @statusret: Return value of the connection + * + * Attempts to connect to the specified host via the configured + * proxy settings, if present. If no proxy is configured for + * this session, the connection is done directly. + * + */ +static int aim_proxyconnect(struct aim_session_t *sess, + char *host, unsigned short port, + int *statusret) +{ + int fd = -1; + + if (strlen(sess->socksproxy.server)) { /* connecting via proxy */ + int i; + unsigned char buf[512]; + struct sockaddr_in sa; + struct hostent *hp; + char *proxy; + unsigned short proxyport = 1080; + + for(i=0;i<(int)strlen(sess->socksproxy.server);i++) { + if (sess->socksproxy.server[i] == ':') { + proxyport = atoi(&(sess->socksproxy.server[i+1])); + break; + } + } + proxy = (char *)malloc(i+1); + strncpy(proxy, sess->socksproxy.server, i); + proxy[i] = '\0'; + + if (!(hp = gethostbyname(proxy))) { + faimdprintf(sess, 0, "proxyconnect: unable to resolve proxy name\n"); + *statusret = (h_errno | AIM_CONN_STATUS_RESOLVERR); + return -1; + } + free(proxy); + + memset(&sa.sin_zero, 0, 8); + sa.sin_port = htons(proxyport); + memcpy(&sa.sin_addr, hp->h_addr, hp->h_length); + sa.sin_family = hp->h_addrtype; + + fd = socket(hp->h_addrtype, SOCK_STREAM, 0); + if (connect(fd, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0) { + faimdprintf(sess, 0, "proxyconnect: unable to connect to proxy\n"); + close(fd); + return -1; + } + + i = 0; + buf[0] = 0x05; /* SOCKS version 5 */ + if (strlen(sess->socksproxy.username)) { + buf[1] = 0x02; /* two methods */ + buf[2] = 0x00; /* no authentication */ + buf[3] = 0x02; /* username/password authentication */ + i = 4; + } else { + buf[1] = 0x01; + buf[2] = 0x00; + i = 3; + } + + if (write(fd, buf, i) < i) { + *statusret = errno; + close(fd); + return -1; + } + + if (read(fd, buf, 2) < 2) { + *statusret = errno; + close(fd); + return -1; + } + + if ((buf[0] != 0x05) || (buf[1] == 0xff)) { + *statusret = EINVAL; + close(fd); + return -1; + } + + /* check if we're doing username authentication */ + if (buf[1] == 0x02) { + i = aimutil_put8(buf, 0x01); /* version 1 */ + i += aimutil_put8(buf+i, strlen(sess->socksproxy.username)); + i += aimutil_putstr(buf+i, sess->socksproxy.username, strlen(sess->socksproxy.username)); + i += aimutil_put8(buf+i, strlen(sess->socksproxy.password)); + i += aimutil_putstr(buf+i, sess->socksproxy.password, strlen(sess->socksproxy.password)); + if (write(fd, buf, i) < i) { + *statusret = errno; + close(fd); + return -1; + } + if (read(fd, buf, 2) < 2) { + *statusret = errno; + close(fd); + return -1; + } + if ((buf[0] != 0x01) || (buf[1] != 0x00)) { + *statusret = EINVAL; + close(fd); + return -1; + } + } + + i = aimutil_put8(buf, 0x05); + i += aimutil_put8(buf+i, 0x01); /* CONNECT */ + i += aimutil_put8(buf+i, 0x00); /* reserved */ + i += aimutil_put8(buf+i, 0x03); /* address type: host name */ + i += aimutil_put8(buf+i, strlen(host)); + i += aimutil_putstr(buf+i, host, strlen(host)); + i += aimutil_put16(buf+i, port); + + if (write(fd, buf, i) < i) { + *statusret = errno; + close(fd); + return -1; + } + if (read(fd, buf, 10) < 10) { + *statusret = errno; + close(fd); + return -1; + } + if ((buf[0] != 0x05) || (buf[1] != 0x00)) { + *statusret = EINVAL; + close(fd); + return -1; + } + + } else { /* connecting directly */ + struct sockaddr_in sa; + struct hostent *hp; + + if (!(hp = gethostbyname(host))) { + *statusret = (h_errno | AIM_CONN_STATUS_RESOLVERR); + return -1; + } + + memset(&sa, 0, sizeof(struct sockaddr_in)); + sa.sin_port = htons(port); + memcpy(&sa.sin_addr, hp->h_addr, hp->h_length); + sa.sin_family = hp->h_addrtype; + + fd = socket(hp->h_addrtype, SOCK_STREAM, 0); + + if (sess->flags & AIM_SESS_FLAGS_NONBLOCKCONNECT) + fcntl(fd, F_SETFL, O_NONBLOCK); /* XXX save flags */ + + if (connect(fd, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0) { + if (sess->flags & AIM_SESS_FLAGS_NONBLOCKCONNECT) { + if ((errno == EINPROGRESS) || (errno == EINTR)) { + if (statusret) + *statusret |= AIM_CONN_STATUS_INPROGRESS; + return fd; + } + } + close(fd); + fd = -1; + } + } + return fd; +} + +/** + * aim_cloneconn - clone an aim_conn_t + * @sess: session containing parent + * @src: connection to clone + * + * A new connection is allocated, and the values are filled in + * appropriately. Note that this function sets the new connnection's + * ->priv pointer to be equal to that of its parent: only the pointer + * is copied, not the data it points to. + * + * This function returns a pointer to the new aim_conn_t, or %NULL on + * error + */ +faim_internal struct aim_conn_t *aim_cloneconn(struct aim_session_t *sess, + struct aim_conn_t *src) +{ + struct aim_conn_t *conn; + struct aim_rxcblist_t *cur; + + if (!(conn = aim_conn_getnext(sess))) + return NULL; + + faim_mutex_lock(&conn->active); + + conn->fd = src->fd; + conn->type = src->type; + conn->subtype = src->subtype; + conn->seqnum = src->seqnum; + conn->priv = src->priv; + conn->lastactivity = src->lastactivity; + conn->forcedlatency = src->forcedlatency; + + /* clone handler list */ + for (cur = src->handlerlist; cur; cur = cur->next) { + aim_conn_addhandler(sess, conn, cur->family, cur->type, + cur->handler, cur->flags); + } + + faim_mutex_unlock(&conn->active); + + return conn; +} + +/** + * aim_newconn - Open a new connection + * @sess: Session to create connection in + * @type: Type of connection to create + * @dest: Host to connect to (in "host:port" syntax) + * + * Opens a new connection to the specified dest host of specified + * type, using the proxy settings if available. If @host is %NULL, + * the connection is allocated and returned, but no connection + * is made. + * + * FIXME: Return errors in a more sane way. + * + */ +faim_export struct aim_conn_t *aim_newconn(struct aim_session_t *sess, + int type, char *dest) +{ + struct aim_conn_t *connstruct; + int ret; + u_short port = FAIM_LOGIN_PORT; + char *host = NULL; + int i=0; + + if ((connstruct=aim_conn_getnext(sess))==NULL) + return NULL; + + faim_mutex_lock(&connstruct->active); + + connstruct->type = type; + + if (!dest) { /* just allocate a struct */ + connstruct->fd = -1; + connstruct->status = 0; + faim_mutex_unlock(&connstruct->active); + return connstruct; + } + + /* + * As of 23 Jul 1999, AOL now sends the port number, preceded by a + * colon, in the BOS redirect. This fatally breaks all previous + * libfaims. Bad, bad AOL. + * + * We put this here to catch every case. + * + */ + + for(i=0;i<(int)strlen(dest);i++) { + if (dest[i] == ':') { + port = atoi(&(dest[i+1])); + break; + } + } + host = (char *)malloc(i+1); + strncpy(host, dest, i); + host[i] = '\0'; + + if ((ret = aim_proxyconnect(sess, host, port, &connstruct->status)) < 0) { + connstruct->fd = -1; + connstruct->status = (errno | AIM_CONN_STATUS_CONNERR); + free(host); + faim_mutex_unlock(&connstruct->active); + return connstruct; + } else + connstruct->fd = ret; + + faim_mutex_unlock(&connstruct->active); + + free(host); + + return connstruct; +} + +/** + * aim_conngetmaxfd - Return the highest valued file discriptor in session + * @sess: Session to search + * + * Returns the highest valued filed descriptor of all open + * connections in @sess. + * + */ +faim_export int aim_conngetmaxfd(struct aim_session_t *sess) +{ + int j = 0; + struct aim_conn_t *cur; + + faim_mutex_lock(&sess->connlistlock); + for (cur = sess->connlist; cur; cur = cur->next) { + if (cur->fd > j) + j = cur->fd; + } + faim_mutex_unlock(&sess->connlistlock); + + return j; +} + +/** + * aim_conn_in_sess - Predicate to test the precense of a connection in a sess + * @sess: Session to look in + * @conn: Connection to look for + * + * Searches @sess for the passed connection. Returns 1 if its present, + * zero otherwise. + * + */ +faim_export int aim_conn_in_sess(struct aim_session_t *sess, struct aim_conn_t *conn) +{ + struct aim_conn_t *cur; + + faim_mutex_lock(&sess->connlistlock); + for(cur = sess->connlist; cur; cur = cur->next) + if(cur == conn) { + faim_mutex_unlock(&sess->connlistlock); + return 1; + } + faim_mutex_unlock(&sess->connlistlock); + return 0; +} + +/** + * aim_select - Wait for a socket with data or timeout + * @sess: Session to wait on + * @timeout: How long to wait + * @status: Return status + * + * Waits for a socket with data or for timeout, whichever comes first. + * See select(2). + * + * Return codes in *status: + * -1 error in select() (%NULL returned) + * 0 no events pending (%NULL returned) + * 1 outgoing data pending (%NULL returned) + * 2 incoming data pending (connection with pending data returned) + * + * XXX: we could probably stand to do a little courser locking here. + * + */ +faim_export struct aim_conn_t *aim_select(struct aim_session_t *sess, + struct timeval *timeout, int *status) +{ + struct aim_conn_t *cur; + fd_set fds, wfds; + int maxfd = 0; + int i, haveconnecting = 0; + + faim_mutex_lock(&sess->connlistlock); + if (sess->connlist == NULL) { + faim_mutex_unlock(&sess->connlistlock); + *status = -1; + return NULL; + } + faim_mutex_unlock(&sess->connlistlock); + + FD_ZERO(&fds); + FD_ZERO(&wfds); + maxfd = 0; + + faim_mutex_lock(&sess->connlistlock); + for (cur = sess->connlist; cur; cur = cur->next) { + if (cur->fd == -1) { + /* don't let invalid/dead connections sit around */ + *status = 2; + faim_mutex_unlock(&sess->connlistlock); + return cur; + } else if (cur->status & AIM_CONN_STATUS_INPROGRESS) { + FD_SET(cur->fd, &wfds); + haveconnecting++; + } + FD_SET(cur->fd, &fds); + if (cur->fd > maxfd) + maxfd = cur->fd; + } + faim_mutex_unlock(&sess->connlistlock); + + /* + * If we have data waiting to be sent, return + * + * We have to not do this if theres at least one + * connection thats still connecting, since that connection + * may have queued data and this return would prevent + * the connection from ever completing! This is a major + * inadequacy of the libfaim way of doing things. It means + * that nothing can transmit as long as there's connecting + * sockets. Evil. + * + * But its still better than having blocking connects. + * + */ + if (!haveconnecting && (sess->queue_outgoing != NULL)) { + *status = 1; + return NULL; + } + + if ((i = select(maxfd+1, &fds, &wfds, NULL, timeout))>=1) { + faim_mutex_lock(&sess->connlistlock); + for (cur = sess->connlist; cur; cur = cur->next) { + if ((FD_ISSET(cur->fd, &fds)) || + ((cur->status & AIM_CONN_STATUS_INPROGRESS) && + FD_ISSET(cur->fd, &wfds))) { + *status = 2; + faim_mutex_unlock(&sess->connlistlock); + return cur; /* XXX race condition here -- shouldnt unlock connlist */ + } + } + *status = 0; /* shouldn't happen */ + } else if ((i == -1) && (errno == EINTR)) /* treat interrupts as a timeout */ + *status = 0; + else + *status = i; /* can be 0 or -1 */ + + faim_mutex_unlock(&sess->connlistlock); + + return NULL; /* no waiting or error, return */ +} + +/** + * aim_conn_isready - Test if a connection is marked ready + * @conn: Connection to test + * + * Returns true if the connection is ready, false otherwise. + * Returns -1 if the connection is invalid. + * + * XXX: This is deprecated. + * + */ +faim_export int aim_conn_isready(struct aim_conn_t *conn) +{ + if (conn) + return (conn->status & 0x0001); + return -1; +} + +/** + * aim_conn_setstatus - Set the status of a connection + * @conn: Connection + * @status: New status + * + * @newstatus is %XOR'd with the previous value of the connection + * status and returned. Returns -1 if the connection is invalid. + * + * This isn't real useful. + * + */ +faim_export int aim_conn_setstatus(struct aim_conn_t *conn, int status) +{ + int val; + + if (!conn) + return -1; + + faim_mutex_lock(&conn->active); + val = conn->status ^= status; + faim_mutex_unlock(&conn->active); + + return val; +} + +/** + * aim_conn_setlatency - Set a forced latency value for connection + * @conn: Conn to set latency for + * @newval: Number of seconds to force between transmits + * + * Causes @newval seconds to be spent between transmits on a connection. + * + * This is my lame attempt at overcoming not understanding the rate + * limiting. + * + * XXX: This should really be replaced with something that scales and + * backs off like the real rate limiting does. + * + */ +faim_export int aim_conn_setlatency(struct aim_conn_t *conn, int newval) +{ + if (!conn) + return -1; + + faim_mutex_lock(&conn->active); + conn->forcedlatency = newval; + conn->lastactivity = 0; /* reset this just to make sure */ + faim_mutex_unlock(&conn->active); + + return 0; +} + +/** + * aim_setupproxy - Configure a proxy for this session + * @sess: Session to set proxy for + * @server: SOCKS server + * @username: SOCKS username + * @password: SOCKS password + * + * Call this with your SOCKS5 proxy server parameters before + * the first call to aim_newconn(). If called with all %NULL + * args, it will clear out a previously set proxy. + * + * Set username and password to %NULL if not applicable. + * + */ +faim_export void aim_setupproxy(struct aim_session_t *sess, char *server, char *username, char *password) +{ + /* clear out the proxy info */ + if (!server || !strlen(server)) { + memset(sess->socksproxy.server, 0, sizeof(sess->socksproxy.server)); + memset(sess->socksproxy.username, 0, sizeof(sess->socksproxy.username)); + memset(sess->socksproxy.password, 0, sizeof(sess->socksproxy.password)); + return; + } + + strncpy(sess->socksproxy.server, server, sizeof(sess->socksproxy.server)); + if (username && strlen(username)) + strncpy(sess->socksproxy.username, username, sizeof(sess->socksproxy.username)); + if (password && strlen(password)) + strncpy(sess->socksproxy.password, password, sizeof(sess->socksproxy.password)); + return; +} + +/** + * aim_session_init - Initializes a session structure + * @sess: Session to initialize + * @flags: Flags to use. Any of %AIM_SESS_FLAGS %OR'd together. + * @debuglevel: Level of debugging output (zero is least) + * + * Sets up the initial values for a session. + * + */ +faim_export void aim_session_init(struct aim_session_t *sess, unsigned long flags, int debuglevel) +{ + if (!sess) + return; + + memset(sess, 0, sizeof(struct aim_session_t)); + aim_connrst(sess); + sess->queue_outgoing = NULL; + sess->queue_incoming = NULL; + sess->pendingjoin = NULL; + sess->pendingjoinexchange = 0; + aim_initsnachash(sess); + sess->msgcookies = NULL; + sess->snac_nextid = 0x00000001; + + sess->flags = 0; + sess->debug = 0; + sess->debugcb = NULL; + + /* + * Default to SNAC login unless XORLOGIN is explicitly set. + */ + if (!(flags & AIM_SESS_FLAGS_XORLOGIN)) + sess->flags |= AIM_SESS_FLAGS_SNACLOGIN; + sess->flags |= flags; + + /* + * This must always be set. Default to the queue-based + * version for back-compatibility. + */ + aim_tx_setenqueue(sess, AIM_TX_QUEUED, NULL); + + return; +} + +/** + * aim_setdebuggingcb - Set the function to call when outputting debugging info + * @sess: Session to change + * @cb: Function to call + * + * The function specified is called whenever faimdprintf() is used within + * libfaim, and the session's debugging level is greater tha nor equal to + * the value faimdprintf was called with. + * + */ +faim_export int aim_setdebuggingcb(struct aim_session_t *sess, faim_debugging_callback_t cb) +{ + + if (!sess) + return -1; + + sess->debugcb = cb; + + return 0; +} + +/** + * aim_conn_isconnecting - Determine if a connection is connecting + * @conn: Connection to examine + * + * Returns nonzero if the connection is in the process of + * connecting (or if it just completed and aim_conn_completeconnect() + * has yet to be called on it). + * + */ +faim_export int aim_conn_isconnecting(struct aim_conn_t *conn) +{ + if (!conn) + return 0; + return (conn->status & AIM_CONN_STATUS_INPROGRESS)?1:0; +} + +faim_export int aim_conn_completeconnect(struct aim_session_t *sess, struct aim_conn_t *conn) +{ + fd_set fds, wfds; + struct timeval tv; + int res, error = ETIMEDOUT; + rxcallback_t userfunc; + + if (!conn || (conn->fd == -1)) + return -1; + + if (!(conn->status & AIM_CONN_STATUS_INPROGRESS)) + return -1; + + FD_ZERO(&fds); + FD_SET(conn->fd, &fds); + FD_ZERO(&wfds); + FD_SET(conn->fd, &wfds); + tv.tv_sec = 0; + tv.tv_usec = 0; + + if ((res = select(conn->fd+1, &fds, &wfds, NULL, &tv)) == -1) { + error = errno; + aim_conn_close(conn); + errno = error; + return -1; + } else if (res == 0) { + faimdprintf(sess, 0, "aim_conn_completeconnect: false alarm on %d\n", conn->fd); + return 0; /* hasn't really completed yet... */ + } + + if (FD_ISSET(conn->fd, &fds) || FD_ISSET(conn->fd, &wfds)) { + int len = sizeof(error); + + if (getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) + error = errno; + } + + if (error) { + aim_conn_close(conn); + errno = error; + return -1; + } + + fcntl(conn->fd, F_SETFL, 0); /* XXX should restore original flags */ + + conn->status &= ~AIM_CONN_STATUS_INPROGRESS; + + if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE))) + userfunc(sess, NULL, conn); + + /* Flush out the queues if there was something waiting for this conn */ + aim_tx_flushqueue(sess); + + return 0; +} + +/* + * aim_logoff() + * + * Closes -ALL- open connections. + * + */ +faim_export int aim_logoff(struct aim_session_t *sess) +{ + + aim_connrst(sess); /* in case we want to connect again */ + + return 0; + +} +
--- a/libfaim/faim/aim.h Sun Mar 04 22:37:18 2001 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,824 +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 Meyer - * - */ - -#ifndef __AIM_H__ -#define __AIM_H__ - -#define FAIM_VERSION_MAJOR 0 -#define FAIM_VERSION_MINOR 99 -#define FAIM_VERSION_MINORMINOR 0 - -#include <faim/faimconfig.h> -#include <faim/aim_cbtypes.h> - -#if !defined(FAIM_USEPTHREADS) && !defined(FAIM_USEFAKELOCKS) -#error pthreads or fakelocks are currently required. -#endif - -#include <stdio.h> -#include <string.h> -#include <fcntl.h> -#include <sys/types.h> -#include <stdlib.h> -#include <stdarg.h> -#include <errno.h> - -#ifdef _WIN32 -#include <windows.h> -#include <time.h> -#include <io.h> -#else -#include <sys/time.h> -#include <unistd.h> -#include <netinet/in.h> -#endif - -#ifdef FAIM_USEPTHREADS -#include <pthread.h> -#define faim_mutex_t pthread_mutex_t -#define faim_mutex_init(x) pthread_mutex_init(x, NULL) -#define faim_mutex_lock(x) pthread_mutex_lock(x) -#define faim_mutex_unlock(x) pthread_mutex_unlock(x) -#define faim_mutex_destroy(x) pthread_mutex_destroy(x) -#elif defined(FAIM_USEFAKELOCKS) -/* - * For platforms without pthreads, we also assume - * we're not linking against a threaded app. Which - * means we don't have to do real locking. The - * macros below do nothing really. They're a joke. - * But they get it to compile. - */ -#define faim_mutex_t char -#define faim_mutex_init(x) *x = 0 -#define faim_mutex_lock(x) *x = 1; -#define faim_mutex_unlock(x) *x = 0; -#define faim_mutex_destroy(x) *x = 0; -#endif - -/* Portability stuff (DMP) */ - -#ifdef _WIN32 -#define sleep Sleep -#define socklen_t int /* this must be a POSIXy thing */ -#define snprintf _snprintf /* I'm not sure whats wrong with Microsoft here */ -#define close(x) closesocket(x) /* no comment */ -#endif - -#if defined(mach) && defined(__APPLE__) -#define gethostbyname(x) gethostbyname2(x, AF_INET) -#endif - -#if defined(_WIN32) || defined(STRICT_ANSI) -#define faim_shortfunc -#else -#define faim_shortfunc inline -#endif - -#if defined(_WIN32) && !defined(WIN32_STATIC) -/* - * For a win32 DLL, we define WIN32_INDLL if this file - * is included while compiling the DLL. If its not - * defined (its 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 - -/* - * Current Maximum Length for Screen Names (not including NULL) - * - * Currently only names up to 16 characters can be registered - * however it is aparently legal for them to be larger. - */ -#define MAXSNLEN 32 - -/* - * 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 - -/* - * 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 - -/* - * Standard size of an AIM authorization cookie - */ -#define AIM_COOKIELEN 0x100 - -#define AIM_MD5_STRING "AOL Instant Messenger (SM)" - -/* - * Client info. Filled in by the client and passed - * in to aim_login(). The information ends up - * getting passed to OSCAR through the initial - * login command. - * - * XXX: Should this be per-session? -mid - * - */ -struct client_info_s { - char clientstring[100]; /* arbitrary size */ - int major; - int minor; - int build; - char country[3]; - char lang[3]; - int major2; - int minor2; - long unknown; -}; - -#ifndef TRUE -#define TRUE 1 -#define FALSE 0 -#endif - -/* - * These could be arbitrary, but its easier to use the actual AIM values - */ -#define AIM_CONN_TYPE_AUTH 0x0007 -#define AIM_CONN_TYPE_ADS 0x0005 -#define AIM_CONN_TYPE_BOS 0x0002 -#define AIM_CONN_TYPE_CHAT 0x000e -#define AIM_CONN_TYPE_CHATNAV 0x000d - -/* they start getting arbitrary in rendezvous stuff =) */ -#define AIM_CONN_TYPE_RENDEZVOUS 0x0101 /* these do not speak OSCAR! */ -#define AIM_CONN_TYPE_RENDEZVOUS_OUT 0x0102 /* socket waiting for accept() */ - -/* - * 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_OSCAR 0x0000 -#define AIM_FRAMETYPE_OFT 0x0001 - -struct aim_conn_t { - int fd; - unsigned short type; - unsigned short subtype; - int seqnum; - int status; - void *priv; /* misc data the client may want to store */ - time_t lastactivity; /* time of last transmit */ - int forcedlatency; - struct aim_rxcblist_t *handlerlist; - faim_mutex_t active; /* lock around read/writes */ - faim_mutex_t seqnum_lock; /* lock around ->seqnum changes */ - struct aim_conn_t *next; -}; - -/* struct for incoming commands */ -struct command_rx_struct { - unsigned char hdrtype; /* defines which piece of the union to use */ - union { - struct { - char type; - unsigned short seqnum; - } oscar; - struct { - unsigned short type; - unsigned char magic[4]; /* ODC2 OFT2 */ - unsigned short hdr2len; - unsigned char *hdr2; /* rest of bloated header */ - } oft; - } hdr; - unsigned short commandlen; /* total payload length */ - unsigned char *data; /* packet data (from 7 byte on) */ - unsigned char lock; /* 0 = open, !0 = locked */ - unsigned char handled; /* 0 = new, !0 = been handled */ - unsigned char nofree; /* 0 = free data on purge, 1 = only unlink */ - struct aim_conn_t *conn; /* the connection it came in on... */ - struct command_rx_struct *next; /* ptr to next struct in list */ -}; - -/* struct for outgoing commands */ -struct command_tx_struct { - unsigned char hdrtype; /* defines which piece of the union to use */ - union { - struct { - unsigned char type; - unsigned short seqnum; - } oscar; - struct { - unsigned short type; - unsigned char magic[4]; /* ODC2 OFT2 */ - unsigned short hdr2len; - unsigned char *hdr2; - } oft; - } hdr; - u_int commandlen; - u_char *data; - u_int lock; /* 0 = open, !0 = locked */ - u_int sent; /* 0 = pending, !0 = has been sent */ - struct aim_conn_t *conn; - struct command_tx_struct *next; /* ptr to next struct in list */ -}; - -/* - * OFT session: random oft cruft, per-session. - * - */ -struct aim_oft_session_t { - FILE *listing; - char *listingdir; -}; - -/* - * AIM Session: The main client-data interface. - * - */ -struct aim_session_t { - - /* ---- 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; - - /* - * OFT Data - */ - struct aim_oft_session_t oft; - - /* ---- Internal Use Only ------------------------ */ - /* - * Connection information - */ - struct aim_conn_t *connlist; - faim_mutex_t connlistlock; - - /* - * TX/RX queues - */ - struct command_tx_struct *queue_outgoing; - struct command_rx_struct *queue_incoming; - - /* - * Tx Enqueuing function - */ - int (*tx_enqueue)(struct aim_session_t *, struct command_tx_struct *); - - /* - * This is a dreadful solution to the what-room-are-we-joining - * problem. (There's no connection between the service - * request and the resulting redirect.) - */ - char *pendingjoin; - unsigned short pendingjoinexchange; - - /* - * Outstanding snac handling - * - * XXX: Should these be per-connection? -mid - */ - struct aim_snac_t *snac_hash[FAIM_SNAC_HASH_SIZE]; - faim_mutex_t snac_hash_locks[FAIM_SNAC_HASH_SIZE]; - u_long snac_nextid; - - struct { - char server[128]; - char username[128]; - char password[128]; - } socksproxy; - - unsigned long flags; - - struct aim_msgcookie_t *msgcookies; -}; - -/* Values for sess->flags */ -#define AIM_SESS_FLAGS_SNACLOGIN 0x00000001 -#define AIM_SESS_FLAGS_XORLOGIN 0x00000002 -#define AIM_SESS_FLAGS_NONBLOCKCONNECT 0x00000004 - -/* - * AIM User Info, Standard Form. - */ -struct aim_userinfo_s { - char sn[MAXSNLEN+1]; - u_short warnlevel; - u_short idletime; - u_short flags; - u_long membersince; - u_long onlinesince; - u_long sessionlen; - u_short capabilities; - struct { - unsigned short status; - unsigned int ipaddr; - char crap[0x25]; /* until we figure it out... */ - } icqinfo; -}; - -#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_UNKNOWN40 0x0040 -#define AIM_FLAG_UNKNOWN80 0x0080 - -#define AIM_FLAG_ALLUSERS 0x001f - -/* - * TLV handling - */ - -/* Generic TLV structure. */ -struct aim_tlv_t { - u_short type; - u_short length; - u_char *value; -}; - -/* List of above. */ -struct aim_tlvlist_t { - struct aim_tlv_t *tlv; - struct aim_tlvlist_t *next; -}; - -/* TLV-handling functions */ -faim_internal struct aim_tlvlist_t *aim_readtlvchain(u_char *buf, int maxlen); -faim_internal void aim_freetlvchain(struct aim_tlvlist_t **list); -faim_internal struct aim_tlv_t *aim_grabtlv(u_char *src); -faim_internal struct aim_tlv_t *aim_grabtlvstr(u_char *src); -faim_internal struct aim_tlv_t *aim_gettlv(struct aim_tlvlist_t *, u_short, int); -faim_internal char *aim_gettlv_str(struct aim_tlvlist_t *, u_short, int); -faim_internal unsigned char aim_gettlv8(struct aim_tlvlist_t *list, unsigned short type, int num); -faim_internal unsigned short aim_gettlv16(struct aim_tlvlist_t *list, unsigned short type, int num); -faim_internal unsigned long aim_gettlv32(struct aim_tlvlist_t *list, unsigned short type, int num); -faim_internal int aim_puttlv (u_char *dest, struct aim_tlv_t *newtlv); -faim_internal struct aim_tlv_t *aim_createtlv(void); -faim_internal int aim_freetlv(struct aim_tlv_t **oldtlv); -faim_export int aim_puttlv_8(unsigned char *buf, unsigned short t, unsigned char v); -faim_internal int aim_puttlv_16(u_char *, u_short, u_short); -faim_internal int aim_puttlv_32(u_char *, u_short, u_long); -faim_internal int aim_puttlv_str(u_char *buf, u_short t, int l, char *v); -faim_internal int aim_writetlvchain(u_char *buf, int buflen, struct aim_tlvlist_t **list); -faim_internal int aim_addtlvtochain16(struct aim_tlvlist_t **list, unsigned short type, unsigned short val); -faim_internal int aim_addtlvtochain32(struct aim_tlvlist_t **list, unsigned short type, unsigned long val); -faim_internal int aim_addtlvtochain_str(struct aim_tlvlist_t **list, unsigned short type, char *str, int len); -faim_internal int aim_addtlvtochain_caps(struct aim_tlvlist_t **list, unsigned short type, unsigned short caps); -faim_internal int aim_addtlvtochain_noval(struct aim_tlvlist_t **list, unsigned short type); -faim_internal int aim_counttlvchain(struct aim_tlvlist_t **list); - -/* - * Get command from connections / Dispatch commands - * already in queue. - */ -faim_export int aim_get_command(struct aim_session_t *, struct aim_conn_t *); -int aim_rxdispatch(struct aim_session_t *); - -faim_export unsigned long aim_debugconn_sendconnect(struct aim_session_t *sess, struct aim_conn_t *conn); - -int aim_logoff(struct aim_session_t *); - -faim_export void aim_conn_kill(struct aim_session_t *sess, struct aim_conn_t **deadconn); - -typedef int (*rxcallback_t)(struct aim_session_t *, struct command_rx_struct *, ...); -int aim_register_callbacks(rxcallback_t *); - -faim_internal unsigned long aim_genericreq_n(struct aim_session_t *, struct aim_conn_t *conn, u_short family, u_short subtype); -faim_internal unsigned long aim_genericreq_l(struct aim_session_t *, struct aim_conn_t *conn, u_short family, u_short subtype, u_long *); -faim_internal unsigned long aim_genericreq_s(struct aim_session_t *, struct aim_conn_t *conn, u_short family, u_short subtype, u_short *); - -faim_internal struct aim_fileheader_t *aim_oft_getfh(unsigned char *hdr); -faim_export int aim_oft_registerlisting(struct aim_session_t *sess, FILE *file, char* listingdir); -faim_export int aim_getfile_send(struct aim_conn_t *conn, FILE *tosend, struct aim_fileheader_t *fh); -faim_export int aim_getfile_send_chunk(struct aim_conn_t *conn, FILE *tosend, struct aim_fileheader_t *fh, int pos, int bufsize); - -/* aim_login.c */ -faim_export int aim_sendconnack(struct aim_session_t *sess, struct aim_conn_t *conn); -faim_export int aim_request_login (struct aim_session_t *sess, struct aim_conn_t *conn, char *sn); -faim_export int aim_send_login (struct aim_session_t *, struct aim_conn_t *, char *, char *, struct client_info_s *, char *key); -faim_export unsigned long aim_sendauthresp(struct aim_session_t *sess, struct aim_conn_t *conn, char *sn, int errorcode, char *errorurl, char *bosip, char *cookie, char *email, int regstatus); -faim_export int aim_gencookie(unsigned char *buf); -faim_export int aim_sendserverready(struct aim_session_t *sess, struct aim_conn_t *conn); -faim_internal int aim_authkeyparse(struct aim_session_t *sess, struct command_rx_struct *command); -faim_export unsigned long aim_sendredirect(struct aim_session_t *sess, struct aim_conn_t *conn, unsigned short servid, char *ip, char *cookie); -faim_export void aim_purge_rxqueue(struct aim_session_t *); -faim_internal void aim_rxqueue_cleanbyconn(struct aim_session_t *sess, struct aim_conn_t *conn); -faim_internal int aim_recv(int fd, void *buf, size_t count); - -int aim_parse_unknown(struct aim_session_t *, struct command_rx_struct *command, ...); -int aim_parse_missed_im(struct aim_session_t *, struct command_rx_struct *, ...); -int aim_parse_last_bad(struct aim_session_t *, struct command_rx_struct *, ...); -faim_internal int aim_get_command_rendezvous(struct aim_session_t *sess, struct aim_conn_t *conn); - -faim_internal struct command_tx_struct *aim_tx_new(unsigned char framing, int chan, struct aim_conn_t *conn, int datalen); -faim_internal int aim_tx_enqueue__queuebased(struct aim_session_t *, struct command_tx_struct *); -faim_internal int aim_tx_enqueue__immediate(struct aim_session_t *, struct command_tx_struct *); -faim_internal int aim_tx_enqueue(struct aim_session_t *, struct command_tx_struct *); -faim_internal int aim_tx_sendframe(struct aim_session_t *sess, struct command_tx_struct *cur); -faim_internal unsigned int aim_get_next_txseqnum(struct aim_conn_t *); -faim_export int aim_tx_flushqueue(struct aim_session_t *); -faim_internal int aim_tx_printqueue(struct aim_session_t *); -faim_export void aim_tx_purgequeue(struct aim_session_t *); -faim_internal int aim_parse_hostonline(struct aim_session_t *sess, struct command_rx_struct *command, ...); -faim_internal int aim_parse_hostversions(struct aim_session_t *sess, struct command_rx_struct *command, ...); - - -struct aim_rxcblist_t { - u_short family; - u_short type; - rxcallback_t handler; - u_short flags; - struct aim_rxcblist_t *next; -}; - -faim_export int aim_conn_setlatency(struct aim_conn_t *conn, int newval); - -faim_export int aim_conn_addhandler(struct aim_session_t *, struct aim_conn_t *conn, u_short family, u_short type, rxcallback_t newhandler, u_short flags); -faim_internal rxcallback_t aim_callhandler(struct aim_conn_t *conn, u_short family, u_short type); -faim_export int aim_clearhandlers(struct aim_conn_t *conn); - -/* - * Generic SNAC structure. Rarely if ever used. - */ -struct aim_snac_t { - u_long id; - u_short family; - u_short type; - u_short flags; - void *data; - time_t issuetime; - struct aim_snac_t *next; -}; -faim_internal void aim_initsnachash(struct aim_session_t *sess); -faim_internal unsigned long aim_newsnac(struct aim_session_t *, struct aim_snac_t *newsnac); -faim_internal unsigned long aim_cachesnac(struct aim_session_t *sess, const unsigned short family, const unsigned short type, const unsigned short flags, const void *data, const int datalen); -faim_internal struct aim_snac_t *aim_remsnac(struct aim_session_t *, u_long id); -faim_internal int aim_cleansnacs(struct aim_session_t *, int maxage); -faim_internal int aim_putsnac(u_char *, int, int, int, u_long); - - -faim_internal void aim_connrst(struct aim_session_t *); -faim_internal struct aim_conn_t *aim_conn_getnext(struct aim_session_t *); -faim_export void aim_conn_close(struct aim_conn_t *deadconn); -faim_internal struct aim_conn_t *aim_getconn_type(struct aim_session_t *, int type); -faim_export struct aim_conn_t *aim_newconn(struct aim_session_t *, int type, char *dest); -faim_export int aim_conngetmaxfd(struct aim_session_t *); -faim_export struct aim_conn_t *aim_select(struct aim_session_t *, struct timeval *, int *); -faim_export int aim_conn_isready(struct aim_conn_t *); -faim_export int aim_conn_setstatus(struct aim_conn_t *, int); -faim_export int aim_conn_completeconnect(struct aim_session_t *sess, struct aim_conn_t *conn); -faim_export int aim_conn_isconnecting(struct aim_conn_t *conn); -faim_export void aim_session_init(struct aim_session_t *, unsigned long flags); -faim_export void aim_setupproxy(struct aim_session_t *sess, char *server, char *username, char *password); - -/* aim_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_send_warning(struct aim_session_t *sess, struct aim_conn_t *conn, char *destsn, int anon); -faim_export unsigned long aim_bos_nop(struct aim_session_t *, struct aim_conn_t *); -faim_export unsigned long aim_flap_nop(struct aim_session_t *sess, struct aim_conn_t *conn); -faim_export unsigned long aim_bos_setidle(struct aim_session_t *, struct aim_conn_t *, u_long); -faim_export unsigned long aim_bos_changevisibility(struct aim_session_t *, struct aim_conn_t *, int, char *); -faim_export unsigned long aim_bos_setbuddylist(struct aim_session_t *, struct aim_conn_t *, char *); -faim_export unsigned long aim_bos_setprofile(struct aim_session_t *, struct aim_conn_t *, char *, char *, unsigned short); -faim_export unsigned long aim_bos_setgroupperm(struct aim_session_t *, struct aim_conn_t *, u_long); -faim_export unsigned long aim_bos_clientready(struct aim_session_t *, struct aim_conn_t *); -faim_export unsigned long aim_bos_reqrate(struct aim_session_t *, struct aim_conn_t *); -faim_export unsigned long aim_bos_ackrateresp(struct aim_session_t *, struct aim_conn_t *); -faim_export unsigned long aim_bos_setprivacyflags(struct aim_session_t *, struct aim_conn_t *, u_long); -faim_export unsigned long aim_bos_reqpersonalinfo(struct aim_session_t *, struct aim_conn_t *); -faim_export unsigned long aim_bos_reqservice(struct aim_session_t *, struct aim_conn_t *, u_short); -faim_export unsigned long aim_bos_reqrights(struct aim_session_t *, struct aim_conn_t *); -faim_export unsigned long aim_bos_reqbuddyrights(struct aim_session_t *, struct aim_conn_t *); -faim_export unsigned long aim_bos_reqlocaterights(struct aim_session_t *, struct aim_conn_t *); -faim_export unsigned long aim_bos_reqicbmparaminfo(struct aim_session_t *, struct aim_conn_t *); -faim_export unsigned long aim_addicbmparam(struct aim_session_t *sess,struct aim_conn_t *conn); -faim_export unsigned long aim_setversions(struct aim_session_t *sess, struct aim_conn_t *conn); -faim_export unsigned long aim_setdirectoryinfo(struct aim_session_t *sess, struct aim_conn_t *conn, char *first, char *middle, char *last, char *maiden, char *nickname, char *street, char *city, char *state, char *zip, int country, unsigned short privacy); -faim_export unsigned long aim_setuserinterests(struct aim_session_t *sess, struct aim_conn_t *conn, char *interest1, char *interest2, char *interest3, char *interest4, char *interest5, unsigned short privacy); -faim_export unsigned long aim_icq_setstatus(struct aim_session_t *sess, struct aim_conn_t *conn, unsigned long status); - -faim_internal struct aim_fileheader_t *aim_getlisting(FILE *); -faim_internal int aim_oft_buildheader(char *,struct aim_fileheader_t *); -faim_internal int aim_listenestablish(u_short); -faim_internal int aim_tx_destroy(struct command_tx_struct *); - -/* aim_rxhandlers.c */ -faim_export int aim_rxdispatch(struct aim_session_t *); -faim_internal int aim_authparse(struct aim_session_t *, struct command_rx_struct *); -faim_internal int aim_handleredirect_middle(struct aim_session_t *, struct command_rx_struct *, ...); -faim_internal int aim_parse_unknown(struct aim_session_t *, struct command_rx_struct *, ...); -int aim_parse_last_bad(struct aim_session_t *, struct command_rx_struct *, ...); -faim_internal int aim_parse_generalerrs(struct aim_session_t *, struct command_rx_struct *command, ...); -faim_internal int aim_parsemotd_middle(struct aim_session_t *sess, struct command_rx_struct *command, ...); - -#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_internal int aim_parse_ratechange_middle(struct aim_session_t *sess, struct command_rx_struct *command); - -faim_internal int aim_parse_evilnotify_middle(struct aim_session_t *sess, struct command_rx_struct *command); -faim_internal int aim_parse_msgack_middle(struct aim_session_t *sess, struct command_rx_struct *command); - -faim_export unsigned long aim_ads_clientready(struct aim_session_t *sess, struct aim_conn_t *conn); -faim_export unsigned long aim_ads_requestads(struct aim_session_t *sess, struct aim_conn_t *conn); - -/* aim_im.c */ -struct aim_directim_priv { - unsigned char cookie[8]; - char sn[MAXSNLEN+1]; - char ip[30]; -}; - -#define AIM_IMFLAGS_AWAY 0x01 /* mark as an autoreply */ -#define AIM_IMFLAGS_ACK 0x02 /* request a receipt notice */ - -faim_export unsigned long aim_send_im(struct aim_session_t *, struct aim_conn_t *, char *, u_int, char *); -faim_internal int aim_parse_incoming_im_middle(struct aim_session_t *, struct command_rx_struct *); -faim_internal int aim_parse_outgoing_im_middle(struct aim_session_t *, struct command_rx_struct *); -faim_export unsigned long aim_seticbmparam(struct aim_session_t *, struct aim_conn_t *conn); -faim_internal int aim_parse_msgerror_middle(struct aim_session_t *, struct command_rx_struct *); -faim_internal int aim_negchan_middle(struct aim_session_t *sess, struct command_rx_struct *command); -faim_internal int aim_parse_bosrights(struct aim_session_t *sess, struct command_rx_struct *command, ...); -faim_internal int aim_parse_missedcall(struct aim_session_t *sess, struct command_rx_struct *command); - -faim_export struct aim_conn_t * aim_directim_initiate(struct aim_session_t *, struct aim_conn_t *, struct aim_directim_priv *, char *); -faim_export int aim_send_im_direct(struct aim_session_t *, struct aim_conn_t *, char *); -faim_export struct aim_conn_t *aim_directim_connect(struct aim_session_t *, struct aim_conn_t *, struct aim_directim_priv *); - -/* aim_info.c */ -#define AIM_CAPS_BUDDYICON 0x01 -#define AIM_CAPS_VOICE 0x02 -#define AIM_CAPS_IMIMAGE 0x04 -#define AIM_CAPS_CHAT 0x08 -#define AIM_CAPS_GETFILE 0x10 -#define AIM_CAPS_SENDFILE 0x20 -#define AIM_CAPS_GAMES 0x40 -#define AIM_CAPS_SAVESTOCKS 0x80 - -extern u_char aim_caps[8][16]; -faim_internal unsigned short aim_getcap(unsigned char *capblock, int buflen); -faim_internal int aim_putcap(unsigned char *capblock, int buflen, u_short caps); - -#define AIM_GETINFO_GENERALINFO 0x00001 -#define AIM_GETINFO_AWAYMESSAGE 0x00003 - -struct aim_msgcookie_t { - unsigned char cookie[8]; - int type; - void *data; - time_t addtime; - struct aim_msgcookie_t *next; -}; - -struct aim_fileheader_t { -#if 0 - char magic[4]; /* 0 */ - short hdrlen; /* 4 */ - short hdrtype; /* 6 */ -#endif - char bcookie[8]; /* 8 */ - short encrypt; /* 16 */ - short compress; /* 18 */ - short totfiles; /* 20 */ - short filesleft; /* 22 */ - short totparts; /* 24 */ - short partsleft; /* 26 */ - long totsize; /* 28 */ - long size; /* 32 */ - long modtime; /* 36 */ - long checksum; /* 40 */ - long rfrcsum; /* 44 */ - long rfsize; /* 48 */ - long cretime; /* 52 */ - long rfcsum; /* 56 */ - long nrecvd; /* 60 */ - long recvcsum; /* 64 */ - char idstring[32]; /* 68 */ - char flags; /* 100 */ - char lnameoffset; /* 101 */ - char lsizeoffset; /* 102 */ - char dummy[69]; /* 103 */ - char macfileinfo[16]; /* 172 */ - short nencode; /* 188 */ - short nlanguage; /* 190 */ - char name[64]; /* 192 */ - /* 256 */ -}; - -struct aim_filetransfer_priv { - char sn[MAXSNLEN]; - char cookie[8]; - char ip[30]; - int state; - struct aim_fileheader_t fh; -}; - -#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 -/* 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_internal int aim_cachecookie(struct aim_session_t *sess, struct aim_msgcookie_t *cookie); -faim_export int aim_purgecookies(struct aim_session_t *sess, int maxage); -faim_internal struct aim_msgcookie_t *aim_uncachecookie(struct aim_session_t *sess, unsigned char *cookie, int type); -faim_internal struct aim_msgcookie_t *aim_mkcookie(unsigned char *, int, void *); -faim_internal struct aim_msgcookie_t *aim_checkcookie(struct aim_session_t *, unsigned char *, int); -faim_internal int aim_msgcookie_gettype(int reqclass); - -faim_export int aim_handlerendconnect(struct aim_session_t *sess, struct aim_conn_t *cur); - -#define AIM_TRANSFER_DENY_NOTSUPPORTED 0x0000 -#define AIM_TRANSFER_DENY_DECLINE 0x0001 -#define AIM_TRANSFER_DENY_NOTACCEPTING 0x0002 -faim_export unsigned long aim_denytransfer(struct aim_session_t *sess, struct aim_conn_t *conn, char *sender, char *cookie, unsigned short code); -faim_export struct aim_conn_t *aim_accepttransfer(struct aim_session_t *sess, struct aim_conn_t *conn, char *sn,char *cookie,char *ip, FILE *file, unsigned short rendid); - - -faim_export unsigned long aim_getinfo(struct aim_session_t *, struct aim_conn_t *, const char *, unsigned short); -faim_internal int aim_extractuserinfo(u_char *, struct aim_userinfo_s *); -faim_internal int aim_parse_userinfo_middle(struct aim_session_t *, struct command_rx_struct *); -faim_internal int aim_parse_oncoming_middle(struct aim_session_t *, struct command_rx_struct *); -faim_internal int aim_parse_offgoing_middle(struct aim_session_t *, struct command_rx_struct *); -faim_internal int aim_putuserinfo(u_char *buf, int buflen, struct aim_userinfo_s *info); -faim_export int aim_sendbuddyoncoming(struct aim_session_t *sess, struct aim_conn_t *conn, struct aim_userinfo_s *info); -faim_export int aim_sendbuddyoffgoing(struct aim_session_t *sess, struct aim_conn_t *conn, char *sn); -faim_internal int aim_parse_locateerr(struct aim_session_t *sess, struct command_rx_struct *command); - -/* aim_auth.c */ -faim_export int aim_auth_sendcookie(struct aim_session_t *, struct aim_conn_t *, u_char *); -faim_export u_long aim_auth_clientready(struct aim_session_t *, struct aim_conn_t *); -faim_export unsigned long aim_auth_changepasswd(struct aim_session_t *, struct aim_conn_t *, char *, char *); - -/* aim_buddylist.c */ -faim_export unsigned long aim_add_buddy(struct aim_session_t *, struct aim_conn_t *, char *); -faim_export unsigned long aim_remove_buddy(struct aim_session_t *, struct aim_conn_t *, char *); -faim_internal int aim_parse_buddyrights(struct aim_session_t *sess, struct command_rx_struct *command, ...); - -/* aim_search.c */ -faim_export u_long aim_usersearch_address(struct aim_session_t *, struct aim_conn_t *, char *); - - -struct aim_chat_roominfo { - u_short exchange; - char *name; - u_short instance; -}; -faim_internal int aim_chat_readroominfo(u_char *buf, struct aim_chat_roominfo *outinfo); -faim_internal int aim_chat_parse_infoupdate(struct aim_session_t *sess, struct command_rx_struct *command); -faim_internal int aim_chat_parse_joined(struct aim_session_t *sess, struct command_rx_struct *command); -faim_internal int aim_chat_parse_leave(struct aim_session_t *sess, struct command_rx_struct *command); -faim_internal int aim_chat_parse_incoming(struct aim_session_t *sess, struct command_rx_struct *command); -faim_export unsigned long aim_chat_send_im(struct aim_session_t *sess, struct aim_conn_t *conn, char *msg); -faim_export unsigned long aim_chat_join(struct aim_session_t *sess, struct aim_conn_t *conn, u_short exchange, const char *roomname); -faim_export unsigned long aim_chat_clientready(struct aim_session_t *sess, struct aim_conn_t *conn); -faim_export int aim_chat_attachname(struct aim_conn_t *conn, char *roomname); -faim_export char *aim_chat_getname(struct aim_conn_t *conn); -faim_export struct aim_conn_t *aim_chat_getconn(struct aim_session_t *, char *name); - -faim_export unsigned long aim_chatnav_reqrights(struct aim_session_t *sess, struct aim_conn_t *conn); -faim_export unsigned long aim_chatnav_clientready(struct aim_session_t *sess, struct aim_conn_t *conn); - -faim_export unsigned long aim_chat_invite(struct aim_session_t *sess, struct aim_conn_t *conn, char *sn, char *msg, u_short exchange, char *roomname, u_short instance); - -struct aim_chat_exchangeinfo { - u_short number; - char *name; - char *charset1; - char *lang1; - char *charset2; - char *lang2; -}; -faim_internal int aim_chatnav_parse_info(struct aim_session_t *sess, struct command_rx_struct *command); -faim_export u_long aim_chatnav_createroom(struct aim_session_t *sess, struct aim_conn_t *conn, char *name, u_short exchange); -faim_export int aim_chat_leaveroom(struct aim_session_t *sess, char *name); - -/* aim_util.c */ -#ifdef AIMUTIL_USEMACROS -/* - * These are really ugly. You'd think this was LISP. I wish it was. - */ -#define aimutil_put8(buf, data) ((*(buf) = (u_char)(data)&0xff),1) -#define aimutil_get8(buf) ((*(buf))&0xff) -#define aimutil_put16(buf, data) ( \ - (*(buf) = (u_char)((data)>>8)&0xff), \ - (*((buf)+1) = (u_char)(data)&0xff), \ - 2) -#define aimutil_get16(buf) ((((*(buf))<<8)&0xff00) + ((*((buf)+1)) & 0xff)) -#define aimutil_put32(buf, data) ( \ - (*((buf)) = (u_char)((data)>>24)&0xff), \ - (*((buf)+1) = (u_char)((data)>>16)&0xff), \ - (*((buf)+2) = (u_char)((data)>>8)&0xff), \ - (*((buf)+3) = (u_char)(data)&0xff), \ - 4) -#define aimutil_get32(buf) ((((*(buf))<<24)&0xff000000) + \ - (((*((buf)+1))<<16)&0x00ff0000) + \ - (((*((buf)+2))<< 8)&0x0000ff00) + \ - (((*((buf)+3) )&0x000000ff))) -#else -#warning Not using aimutil macros. May have performance problems. -int aimutil_put8(u_char *, u_char); -u_char aimutil_get8(u_char *buf); -int aimutil_put16(u_char *, u_short); -u_short aimutil_get16(u_char *); -int aimutil_put32(u_char *, u_long); -u_long aimutil_get32(u_char *); -#endif - -faim_export int aimutil_putstr(u_char *, const char *, int); -faim_export int aimutil_tokslen(char *toSearch, int index, char dl); -faim_export int aimutil_itemcnt(char *toSearch, char dl); -faim_export char *aimutil_itemidx(char *toSearch, int index, char dl); - -faim_export int aim_snlen(const char *sn); -faim_export int aim_sncmp(const char *sn1, const char *sn2); - -/* for libc's that dont have it */ -faim_export char *aim_strsep(char **pp, const char *delim); - -/* aim_meta.c */ -faim_export char *aim_getbuilddate(void); -faim_export char *aim_getbuildtime(void); -faim_export char *aim_getbuildstring(void); -faim_internal void faimdprintf(int dlevel, const char *format, ...); - -#endif /* __AIM_H__ */ -
--- a/libfaim/faim/aim_cbtypes.h Sun Mar 04 22:37:18 2001 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,226 +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_ATH 0x0017 -#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_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: Messeging 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_CLIENTERROR 0x000b -#define AIM_CB_MSG_ACK 0x000c -#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: 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 - -/* - * OFT Services - * - * See non-SNAC note below. - */ -#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_DIRECTIMINITIATE 0x0005 - -#define AIM_CB_OFT_GETFILECONNECTREQ 0x0006 /* connect request -- actually an OSCAR CAP*/ -#define AIM_CB_OFT_GETFILEFILEREQ 0x0007 /* recieved file request */ -#define AIM_CB_OFT_GETFILEFILESEND 0x0008 /* recieved file request confirm -- send data */ -#define AIM_CB_OFT_GETFILECOMPLETE 0x0009 /* recieved file send complete*/ -#define AIM_CB_OFT_GETFILEINITIATE 0x000a /* request for file get acknowledge */ -#define AIM_CB_OFT_GETFILEDISCONNECT 0x000b /* OFT connection disconnected.*/ - -#define AIM_CB_OFT_SENDFILEDISCONNECT 0x000c /* OFT connection disconnected.*/ - - - -/* - * SNAC Family: Internal Messages - * - * This isn't truely 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_DEBUGCONN_CONNECT 0xe001 -#define AIM_CB_SPECIAL_UNKNOWN 0xffff -#define AIM_CB_SPECIAL_DEFAULT AIM_CB_SPECIAL_UNKNOWN - - -#endif/*__AIM_CBTYPES_H__ */
--- a/libfaim/faim/faimconfig.h Sun Mar 04 22:37:18 2001 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,115 +0,0 @@ -/* - * faimconfig.h - * - * Contains various compile-time options that apply _only_ to libfaim. - * Note that setting any of these options in a frontend header does not imply - * that they'll get set here. Notably, the 'debug' of this file is _not_ - * the same as the frontend 'debug'. They can be different values. - * - */ - -#ifndef __FAIMCONFIG_H__ -#define __FAIMCONFIG_H__ - -/* - * set debug to be > 0 if you want debugging information spewing - * on the attached tty. set to 0 for daily use. this value - * is _not_ inherited by the frontend, only this backend. - * - * Default: 0 -*/ -#define debug 0 - -/* - * Maximum number of connections the library can simultaneously - * handle per session structure. Five is fairly arbitrary. - * - * Normally, only one connection gets used at a time. However, if - * chat is used, its easily possible for several connections to be - * open simultaneously. - * - * Normal connection list looks like this: - * 1 -- used for authentication at login (closed after login) - * 1 -- used for BOS (general messaging) (stays open for entire session) - * 1 -- used for chatnav (room creation, etc) (opened at random) - * 1n -- used for n connected chat rooms (AOL limits to three) - * - * Default: 7 - * - */ -#define AIM_CONN_MAX 7 - -/* - * USE_SNAC_FOR_IMS is an old feature that allowed better - * tracking of error messages by caching SNAC IDs of outgoing - * ICBMs and comparing them to incoming errors. However, - * its a helluvalot of overhead for something that should - * rarely happen. - * - * Default: defined. This is now defined by default - * because it should be stable and its not too bad. - * And Josh wanted it. - * - */ -#define USE_SNAC_FOR_IMS - -/* - * Default Authorizer server name and TCP port for the OSCAR farm. - * - * You shouldn't need to change this unless you're writing - * your own server. - * - * Note that only one server is needed to start the whole - * AIM process. The later server addresses come from - * the authorizer service. - * - * This is only here for convenience. Its still up to - * the client to connect to it. - * - */ -#define FAIM_LOGIN_SERVER "login.oscar.aol.com" -#define FAIM_LOGIN_PORT 5190 - -/* - * The integer extraction/copying functions in aim_util.c have - * both a function version and a macro version. The macro - * version is suggested. Since the function version is more - * readable, I leave both around for reference. - * - * Default: defined. - */ -#define AIMUTIL_USEMACROS - -/* - * Select whether or not to use POSIX thread functionality. - * - * We don't actually use threads, but we do use the POSIX mutex - * in order to maintain thread safety. You can use the fake locking - * if you really don't like pthreads or you don't have it. - * - * Default: defined on Linux, otherwise use fake locks. - */ -#ifdef __linux__ -#define FAIM_USEPTHREADS -#else -#define FAIM_USEFAKELOCKS -#endif - -/* - * Size of the SNAC caching hash. - * - * Default: 16 - * - */ -#define FAIM_SNAC_HASH_SIZE 16 - -/* - * If building on Win32,define WIN32_STATIC if you don't want - * to compile libfaim as a DLL (and instead link it right into - * your app). - */ -#define WIN32_STATIC - -#endif /* __FAIMCONFIG_H__ */ - -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfaim/faimconfig.h Mon Mar 05 03:59:32 2001 +0000 @@ -0,0 +1,94 @@ +/* + * faimconfig.h + * + * Contains various compile-time options that apply _only_ to libfaim. + * + */ + +#ifndef __FAIMCONFIG_H__ +#define __FAIMCONFIG_H__ + +/* + * USE_SNAC_FOR_IMS is an old feature that allowed better + * tracking of error messages by caching SNAC IDs of outgoing + * ICBMs and comparing them to incoming errors. However, + * its a helluvalot of overhead for something that should + * rarely happen. + * + * Default: defined. This is now defined by default + * because it should be stable and its not too bad. + * And Josh wanted it. + * + */ +#define USE_SNAC_FOR_IMS + +/* + * Default Authorizer server name and TCP port for the OSCAR farm. + * + * You shouldn't need to change this unless you're writing + * your own server. + * + * Note that only one server is needed to start the whole + * AIM process. The later server addresses come from + * the authorizer service. + * + * This is only here for convenience. Its still up to + * the client to connect to it. + * + */ +#define FAIM_LOGIN_SERVER "login.oscar.aol.com" +#define FAIM_LOGIN_PORT 5190 + +/* + * The integer extraction/copying functions in aim_util.c have + * both a function version and a macro version. The macro + * version is suggested. Since the function version is more + * readable, I leave both around for reference. + * + * Default: defined. + */ +#define AIMUTIL_USEMACROS + +/* + * What type of synchronisation to use. + * + * We don't actually use threads, but can use the POSIX mutex + * in order to maintain thread safety. You can use the fake locking + * if you really don't like pthreads (which I don't) or if you don't + * have it. + * + * USEPTHREADS - Use POSIX mutecies + * USEFAKELOCKS - Use little stub spinners to help find locking bugs + * USENOPLOCKS - No-op out all synchro calls at compile time + * + * Default: use noplocks by default. + * + * !!!NOTE: Even with USEPTHREADS turned on, libfaim is not fully thread + * safe. It will still take some effort to add locking calls to + * the places that need them. In fact, this feature in general + * is in danger of being officially deprecated and removed from + * the code. + * + */ +#undef FAIM_USEPTHREADS +#undef FAIM_USEFAKELOCKS +#define FAIM_USENOPLOCKS + +/* + * Size of the SNAC caching hash. + * + * Default: 16 + * + */ +#define FAIM_SNAC_HASH_SIZE 16 + +/* + * If building on Win32,define WIN32_STATIC if you don't want + * to compile libfaim as a DLL (and instead link it right into + * your app). + */ +#define WIN32_STATIC + +#endif /* __FAIMCONFIG_H__ */ + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfaim/ft.c Mon Mar 05 03:59:32 2001 +0000 @@ -0,0 +1,1898 @@ +/* + * File transfer (OFT) and DirectIM (ODC). + * (OSCAR File Transfer, Oscar Direct Connect(ion?) + */ + +#define FAIM_INTERNAL +#include <aim.h> + + +#ifndef _WIN32 +#include <netdb.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <sys/utsname.h> /* for aim_directim_initiate */ + +#include <arpa/inet.h> /* for inet_ntoa */ + +#endif + +/* TODO: + o look for memory leaks.. there's going to be shitloads, i'm sure. +*/ + +static struct aim_fileheader_t *aim_oft_getfh(unsigned char *hdr); + +/** + * aim_handlerendconnect - call this to accept OFT connections and set up the requisite structures + * @sess: the session + * @cur: the conn the incoming connection is on + * + * call this when you get an outstanding read on a conn with subtype + * AIM_CONN_SUBTYPE_RENDEZVOUS_OUT, it will clone the current + * &aim_conn_t and tweak things as appropriate. the new conn and the + * listener conn are both returned to the client in the + * %AIM_CB_FAM_OFT, %AIM_CB_OFT_<CLASS>INITIATE callback. + */ +faim_export int aim_handlerendconnect(struct aim_session_t *sess, struct aim_conn_t *cur) +{ + int acceptfd = 0; + rxcallback_t userfunc; + struct sockaddr cliaddr; + socklen_t clilen = sizeof(cliaddr); + int ret = 0; + struct aim_conn_t *newconn; + + if ( (acceptfd = accept(cur->fd, &cliaddr, &clilen)) == -1) + return -1; + if (cliaddr.sa_family != AF_INET) { /* just in case IPv6 really is happening */ + close(acceptfd); + aim_conn_close(cur); + return -1; + } + + /* safe? maybe cur->priv should be NULLed after this. --mid */ + + /* That would be bad. very bad. we want cur->priv->sn to make it up + to the client-level for conn management and such. even though + that is abusing the interface --jbm */ + + if (!(newconn = aim_cloneconn(sess, cur))) { + close(acceptfd); + aim_conn_close(cur); + return -1; + } + + newconn->type = AIM_CONN_TYPE_RENDEZVOUS; + newconn->fd = acceptfd; + + switch(newconn->subtype) { + case AIM_CONN_SUBTYPE_OFT_DIRECTIM: { + struct aim_directim_priv *priv; + + priv = cur->priv; + + newconn->priv = cur->priv; + + cur->priv = NULL; + + snprintf(priv->ip, sizeof(priv->ip), "%s:%u", + inet_ntoa(((struct sockaddr_in *)&cliaddr)->sin_addr), + ntohs(((struct sockaddr_in *)&cliaddr)->sin_port)); + + if ( (userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINITIATE))) + ret = userfunc(sess, NULL, newconn, cur); + + break; + } + case AIM_CONN_SUBTYPE_OFT_GETFILE: { + struct aim_filetransfer_priv *priv; + + + newconn->priv = cur->priv; + cur->priv = NULL; + priv = (struct aim_filetransfer_priv *)newconn->priv; + + snprintf(priv->ip, sizeof(priv->ip), "%s:%u", inet_ntoa(((struct sockaddr_in *)&cliaddr)->sin_addr), ntohs(((struct sockaddr_in *)&cliaddr)->sin_port)); + + if ( (userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEINITIATE))) + ret = userfunc(sess, NULL, newconn, cur); + + break; + } + default: { + faimdprintf(sess, 1,"Got a Connection on a listener that's not Rendezvous(??!) Closing conn.\n"); + aim_conn_close(newconn); + break; + } + } + + return ret; +} + +/** + * aim_send_im_direct - send IM client-to-client over established connection + * @sess: session to conn + * @conn: directim connection + * @msg: null-terminated string to send; if this is NULL, it will send a "typing" notice. + * + * Call this just like you would aim_send_im, to send a directim. You + * _must_ have previously established the directim connection. + */ +faim_export int aim_send_im_direct(struct aim_session_t *sess, struct aim_conn_t *conn, char *msg) +{ + struct command_tx_struct *newpacket; + struct aim_directim_priv *priv = NULL; + int i; + + if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS) || !conn->priv) { + faimdprintf(sess, 2,"faim: directim: invalid arguments\n"); + return -1; + } + + priv = (struct aim_directim_priv *)conn->priv; + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0001, strlen(msg)))) { + faimdprintf(sess, 2,"faim: directim: tx_new failed\n"); + return -1; + } + + newpacket->lock = 1; + + /* if msg is non-null, we'resending an IM, else a "typing" notice */ + if (msg) { + if (strlen(msg) >= MAXMSGLEN) + return -1; + newpacket->hdr.oft.hdr2len = 0x54; + if (!(newpacket->hdr.oft.hdr2 = calloc(1,newpacket->hdr.oft.hdr2len))) { + newpacket->lock = 0; + aim_tx_destroy(newpacket); + return -1; + } + } else { + newpacket->hdr.oft.hdr2len = 0x44; + if (!(newpacket->hdr.oft.hdr2 = calloc(1,newpacket->hdr.oft.hdr2len))) { + newpacket->lock = 0; + aim_tx_destroy(newpacket); + return -1; + } + } + + memcpy(newpacket->hdr.oft.magic, "ODC2", 4); + newpacket->data = NULL; + + i = 0; + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0006); + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); + i += aimutil_putstr(newpacket->hdr.oft.hdr2+i, (char *)priv->cookie, 8); + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); + i += aimutil_put32(newpacket->hdr.oft.hdr2+i, strlen(msg)); + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); + + /* flags -- 0x000e for "typing", 0x0000 for message */ + if (msg) + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); + else + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x000e); + + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); + i += aimutil_putstr(newpacket->hdr.oft.hdr2+i, sess->sn, strlen(sess->sn)); + i = 52; + + i += aimutil_put8(newpacket->hdr.oft.hdr2+i, 0x00); + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); + + /* end of hdr2 */ + + if (msg) { + /* values grabbed from a dump */ + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0008); + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x000c); + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x1466); + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0001); + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x2e0f); + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x393e); + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0xcac8); + if(!(newpacket->data = strdup(msg))) + return -1; + } + newpacket->lock = 0; + aim_tx_enqueue(sess, newpacket); + return 0; +} + +/* XXX: give the client author the responsibility of setting up a + * listener, then we no longer have a libfaim problem with broken + * solaris *innocent smile* -jbm */ + +/** + * aim_directim_intitiate - For those times when we want to open up the directim channel ourselves. + * @sess: your session, + * @conn: the BOS conn, + * @priv: a dummy priv value (we'll let it get filled in later) (if you pass a %NULL, we alloc one) + * @destsn: the SN to connect to. + * + */ +faim_export struct aim_conn_t *aim_directim_initiate(struct aim_session_t *sess, + struct aim_conn_t *conn, + struct aim_directim_priv *priv, + char *destsn) +{ + + struct command_tx_struct *newpacket; + struct aim_conn_t *newconn; + struct aim_msgcookie_t *cookie; + int curbyte, i, listenfd; + short port = 4443; + struct hostent *hptr; + char localhost[129]; + unsigned char cap[16]; + char d[4]; /* IPv6 is a bit bigger... */ + + /* XXX: TLVlist-ize this */ + + /* Open our socket */ + + if ( (listenfd = aim_listenestablish(port)) == -1) + return NULL; + + /* get our local IP */ + /* XXX if available, use getaddrinfo() */ + /* XXX allow client to specify which IP to use for multihomed boxes */ + if (gethostname(localhost, 128) < 0) + return NULL; + if ( (hptr = gethostbyname(localhost)) == NULL) + return NULL; + memcpy(&d, hptr->h_addr_list[0], 4); + + aim_putcap(cap, 16, AIM_CAPS_IMIMAGE); + + /* create the OSCAR packet */ + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+8+2+1+strlen(destsn)+4+4+0x32))) + return NULL; + newpacket->lock = 1; + + curbyte = 0; + curbyte += aim_putsnac(newpacket->data+curbyte, 0x0004, 0x0006, 0x0000, sess->snac_nextid); + + /* Generate a random message cookie */ + /* This cookie needs to be alphanumeric and NULL-terminated to be TOC-compatible. */ + for (i=0; i<7; i++) + curbyte += aimutil_put8(newpacket->data+curbyte, 0x30 + ((u_char) rand() % 20)); + + curbyte += aimutil_put8(newpacket->data+curbyte, 0x00); + + /* grab all the data for cookie caching */ + cookie = (struct aim_msgcookie_t *)calloc(1, sizeof(struct aim_msgcookie_t)); + memcpy(cookie->cookie, newpacket->data+curbyte-8, 8); + cookie->type = AIM_COOKIETYPE_OFTIM; + priv = cookie->data; + + if (!priv) + priv = (struct aim_directim_priv *)calloc(1, sizeof(struct aim_directim_priv)); + + memcpy(priv->cookie, cookie, 8); + memcpy(priv->sn, destsn, sizeof(priv->sn)); + cookie->data = priv; + aim_cachecookie(sess, cookie); + + /* Channel ID */ + curbyte += aimutil_put16(newpacket->data+curbyte,0x0002); + + /* Destination SN (prepended with byte length)*/ + curbyte += aimutil_put8(newpacket->data+curbyte,strlen(destsn)); + curbyte += aimutil_putstr(newpacket->data+curbyte, destsn, strlen(destsn)); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); + + /* enTLV start */ + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0032); + + /* Flag data / ICBM Parameters */ + curbyte += aimutil_put8(newpacket->data+curbyte, 0x00); + curbyte += aimutil_put8(newpacket->data+curbyte, 0x00); + + /* Cookie */ + curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cookie, 8); + + /*Capability String */ + curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cap, 0x10); + + /* 000a/0002 : 0001 */ + curbyte += aimutil_put16(newpacket->data+curbyte, 0x000a); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001); + + /* 0003/0004: IP address */ + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0004); + for(i = 0;i < 4; i++) + curbyte += aimutil_put8(newpacket->data+curbyte, d[i]); + + /* 0005/0002: Port */ + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002); + curbyte += aimutil_put16(newpacket->data+curbyte, port); + + /* 000f/0000: ?? */ + curbyte += aimutil_put16(newpacket->data+curbyte, 0x000f); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); + newpacket->commandlen = curbyte; + newpacket->lock = 0; + aim_tx_enqueue(sess, newpacket); + + + /* XXX switch to aim_cloneconn()? */ + if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS_OUT, NULL))) + return NULL; + + newconn->fd = listenfd; + newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM; + newconn->priv = priv; + newconn->lastactivity = time(NULL); + + faimdprintf(sess, 2,"faim: listening (fd = %d, unconnected)\n", newconn->fd); + + return newconn; +} + +/** + * unsigned int aim_oft_listener_clean - close up old listeners + * @sess: session to clean up in + * @age: maximum age in seconds + * + * returns number closed, -1 on error. + */ +faim_export unsigned int aim_oft_listener_clean(struct aim_session_t *sess, time_t age) +{ + struct aim_conn_t *cur; + time_t now; + unsigned int hit = 0; + + if (!sess) + return -1; + now = time(NULL); + faim_mutex_lock(&sess->connlistlock); + for(cur = sess->connlist;cur; cur = cur->next) + if (cur->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) { + faim_mutex_lock(&cur->active); + if (cur->lastactivity < (now - age) ) { + faim_mutex_unlock(&cur->active); + aim_conn_close(cur); + hit++; + } else + faim_mutex_unlock(&cur->active); + } + faim_mutex_unlock(&sess->connlistlock); + return hit; +} + +/** + * aim_directim_connect - connect to buddy for directim + * @sess: the session to append the conn to, + * @conn: the BOS connection, + * @priv: the filled-in priv data structure for the connection + * + * returns conn if connected, %NULL on error + */ +faim_export struct aim_conn_t *aim_directim_connect(struct aim_session_t *sess, struct aim_conn_t *conn, struct aim_directim_priv *priv) +{ + struct aim_conn_t *newconn = NULL; + + if (!sess || !conn || !priv) + return NULL; + + /* XXX verify that non-blocking connects actually work */ + newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS, priv->ip); + if (!newconn || (newconn->fd == -1)) { + faimdprintf(sess, 2, "could not connect to %s\n", priv->ip); + perror("aim_newconn"); + return newconn; + } + + newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM; + newconn->priv = priv; + faimdprintf(sess, 2, "faim: connected to peer (fd = %d)\n", newconn->fd); + + return newconn; +} + +/** + * aim_directim_getconn - find a directim conn for buddy name + * @sess: your session, + * @name: the name to get, + * + * returns conn for directim with name, %NULL if none found. + * + */ +faim_export struct aim_conn_t *aim_directim_getconn(struct aim_session_t *sess, const char *name) +{ + struct aim_conn_t *cur; + struct aim_directim_priv *priv; + + if (!sess || !name) + return NULL; + + faim_mutex_lock(&sess->connlistlock); + + for (cur = sess->connlist; cur; cur = cur->next) { + if (cur->type != AIM_CONN_TYPE_RENDEZVOUS || cur->subtype != AIM_CONN_SUBTYPE_OFT_DIRECTIM) + continue; + priv = cur->priv; + if (aim_sncmp(priv->sn, name) == 0) + break; + } faim_mutex_unlock(&sess->connlistlock); + return cur; +} + +/** + * aim_accepttransfer - accept a file transfer request + * @sess: the session, + * @conn: the BOS conn for the CAP reply + * @sn: the screenname to send it to, + * @cookie: the cookie used + * @ip: the ip to connect to + * @listingfiles: number of files to share + * @listingtotsize: total size of shared files + * @listingsize: length of the listing file(buffer) + * @listingchecksum: checksum of the listing + * @rendid: capability type (%AIM_CAPS_GETFILE or %AIM_CAPS_SENDFILE) + * + * Returns new connection or %NULL on error. + */ +faim_export struct aim_conn_t *aim_accepttransfer(struct aim_session_t *sess, + struct aim_conn_t *conn, + char *sn, char *cookie, + char *ip, + unsigned short listingfiles, + unsigned short listingtotsize, + unsigned short listingsize, + unsigned int listingchecksum, + unsigned short rendid) +{ + struct command_tx_struct *newpacket, *newoft; + struct aim_conn_t *newconn; + struct aim_fileheader_t *fh; + struct aim_filetransfer_priv *priv; + struct aim_msgcookie_t *cachedcook; + int curbyte, i; + + if (!sess || !conn || !sn || !cookie || !ip) { + return NULL; + } + + newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS, ip); + + if (!newconn || (newconn->fd == -1)) { + perror("aim_newconn"); + faimdprintf(sess, 2, "could not connect to %s (fd: %i)\n", ip, newconn?newconn->fd:0); + return newconn; + } else { + priv = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv)); + + memcpy(priv->cookie, cookie, 8); + priv->state = 0; + strncpy(priv->sn, sn, MAXSNLEN); + strncpy(priv->ip, ip, sizeof(priv->ip)); + newconn->priv = (void *)priv; + + faimdprintf(sess, 2, "faim: connected to peer (fd = %d)\n", newconn->fd); + } + + if (rendid == AIM_CAPS_GETFILE) { + newconn->subtype = AIM_CONN_SUBTYPE_OFT_GETFILE; + + faimdprintf(sess, 2, "faim: getfile request accept\n"); + + if (!(newoft = aim_tx_new(sess, newconn, AIM_FRAMETYPE_OFT, 0x1108, 0))) { + faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n"); + /* XXX: conn leak here */ + return NULL; + } + + newoft->lock = 1; + memcpy(newoft->hdr.oft.magic, "OFT2", 4); + newoft->hdr.oft.hdr2len = 0x100 - 8; + + if (!(fh = (struct aim_fileheader_t*)calloc(1, sizeof(struct aim_fileheader_t)))) { + /* XXX: conn leak here */ + perror("calloc"); + return NULL; + } + + fh->encrypt = 0x0000; + fh->compress = 0x0000; + fh->totfiles = listingfiles; + fh->filesleft = listingfiles; /* is this right -- total parts and parts left?*/ + fh->totparts = 0x0001; + fh->partsleft = 0x0001; + fh->totsize = listingtotsize; + fh->size = listingsize; /* ls -l listing.txt */ + fh->modtime = (int)time(NULL); /* we'll go with current time for now */ + fh->checksum = listingchecksum; + fh->rfcsum = 0x00000000; + fh->rfsize = 0x00000000; + fh->cretime = 0x00000000; + fh->rfcsum = 0x00000000; + fh->nrecvd = 0x00000000; + fh->recvcsum = 0x00000000; + memset(fh->idstring, 0, sizeof(fh->idstring)); + memcpy(fh->idstring, "OFT_Windows ICBMFT V1.1 32", sizeof(fh->idstring)); + fh->flags = 0x02; + fh->lnameoffset = 0x1a; + fh->lsizeoffset = 0x10; + memset(fh->dummy, 0, sizeof(fh->dummy)); + memset(fh->macfileinfo, 0, sizeof(fh->macfileinfo)); + + /* we need to figure out these encodings for filenames */ + fh->nencode = 0x0000; + fh->nlanguage = 0x0000; + memset(fh->name, 0, sizeof(fh->name)); + memcpy(fh->name, "listing.txt", sizeof(fh->name)); + + if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) { + newoft->lock = 0; + aim_tx_destroy(newoft); + /* XXX: conn leak */ + perror("calloc (1)"); + return NULL; + } + + memcpy(fh->bcookie, cookie, 8); + + if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, fh))) + faimdprintf(sess, 1, "eek, bh fail!\n"); + + newoft->lock = 0; + aim_tx_enqueue(sess, newoft); + + if (!(cachedcook = (struct aim_msgcookie_t *)calloc(1, sizeof(struct aim_msgcookie_t)))) { + faimdprintf(sess, 1, "faim: accepttransfer: couldn't calloc cachedcook. yeep!\n"); + /* XXX: more cleanup, conn leak */ + perror("calloc (2)"); + return NULL; + } + + memcpy(&(priv->fh), fh, sizeof(struct aim_fileheader_t)); + memcpy(cachedcook->cookie, cookie, 8); + + cachedcook->type = AIM_COOKIETYPE_OFTGET; + cachedcook->data = (void *)priv; + + if (aim_cachecookie(sess, cachedcook) == -1) + faimdprintf(sess, 1, "faim: ERROR caching message cookie\n"); + + free(fh); + + /* OSCAR CAP accept packet */ + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+8+2+1+strlen(sn)+4+2+8+16))) { + return NULL; + } + } else { + return NULL; + } + + newpacket->lock = 1; + curbyte = aim_putsnac(newpacket->data, 0x0004, 0x0006, 0x0000, sess->snac_nextid); + + for (i = 0; i < 8; i++) + curbyte += aimutil_put8(newpacket->data+curbyte, cookie[i]); + + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002); + curbyte += aimutil_put8(newpacket->data+curbyte, strlen(sn)); + curbyte += aimutil_putstr(newpacket->data+curbyte, sn, strlen(sn)); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x001a); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002 /* accept*/); + + for (i = 0;i < 8; i++) + curbyte += aimutil_put8(newpacket->data+curbyte, cookie[i]); + + curbyte += aim_putcap(newpacket->data+curbyte, 0x10, rendid); + newpacket->lock = 0; + aim_tx_enqueue(sess, newpacket); + + return newconn; +} + +/** + * aim_getlisting(FILE *file) -- get an aim_fileheader_t for a given FILE* + * @file is an opened listing file + * + * returns a pointer to the filled-in fileheader_t + * + * Currently omits checksum. we'll fix this when AOL breaks us, i + * guess. + * + */ + +faim_export struct aim_fileheader_t *aim_getlisting(struct aim_session_t *sess, FILE *file) +{ + struct aim_fileheader_t *fh; + u_long totsize = 0, size = 0, checksum = 0xffff0000; + short totfiles = 0; + char *linebuf, sizebuf[9]; + + int linelength = 1024; + + /* XXX: if we have a line longer than 1024chars, God help us. */ + if ( (linebuf = (char *)calloc(1, linelength)) == NULL ) { + faimdprintf(sess, 2, "linebuf calloc failed\n"); + return NULL; + } + + if (fseek(file, 0, SEEK_END) == -1) { /* use this for sanity check */ + perror("getlisting END1 fseek:"); + faimdprintf(sess, 2, "getlising fseek END1 error\n"); + } + + if ((size = ftell(file)) == -1) { + perror("getlisting END1 getpos:"); + faimdprintf(sess, 2, "getlising getpos END1 error\n"); + } + + if (fseek(file, 0, SEEK_SET) != 0) { + perror("getlesting fseek(SET):"); + faimdprintf(sess, 2, "faim: getlisting: couldn't seek to beginning of listing file\n"); + } + + memset(linebuf, 0, linelength); + + size = 0; + + while(fgets(linebuf, linelength, file)) { + totfiles++; + memset(sizebuf, 0, 9); + + size += strlen(linebuf); + + if (strlen(linebuf) < 23) { + faimdprintf(sess, 2, "line \"%s\" too short. skipping\n", linebuf); + continue; + } + if (linebuf[strlen(linebuf)-1] != '\n') { + faimdprintf(sess, 2, "faim: OFT: getlisting -- hit EOF or line too long!\n"); + } + + memcpy(sizebuf, linebuf+17, 8); + + totsize += strtol(sizebuf, NULL, 10); + memset(linebuf, 0, linelength); + } + + if (fseek(file, 0, SEEK_SET) == -1) { + perror("getlisting END2 fseek:"); + faimdprintf(sess, 2, "getlising fseek END2 error\n"); + } + + free(linebuf); + + /* we're going to ignore checksumming the data for now -- that + * requires walking the whole listing.txt. it should probably be + * done at register time and cached, but, eh. */ + + if (!(fh = (struct aim_fileheader_t*)calloc(1, sizeof(struct aim_fileheader_t)))) + return NULL; + + fh->encrypt = 0x0000; + fh->compress = 0x0000; + fh->totfiles = totfiles; + fh->filesleft = totfiles; /* is this right ?*/ + fh->totparts = 0x0001; + fh->partsleft = 0x0001; + fh->totsize = totsize; + fh->size = size; /* ls -l listing.txt */ + fh->modtime = (int)time(NULL); /* we'll go with current time for now */ + fh->checksum = checksum; /* XXX: checksum ! */ + fh->rfcsum = 0x00000000; + fh->rfsize = 0x00000000; + fh->cretime = 0x00000000; + fh->rfcsum = 0x00000000; + fh->nrecvd = 0x00000000; + fh->recvcsum = 0x00000000; + + /* memset(fh->idstring, 0, sizeof(fh->idstring)); */ + memcpy(fh->idstring, "OFT_Windows ICBMFT V1.1 32", sizeof(fh->idstring)); + memset(fh->idstring+strlen(fh->idstring), 0, sizeof(fh->idstring)-strlen(fh->idstring)); + + fh->flags = 0x02; + fh->lnameoffset = 0x1a; + fh->lsizeoffset = 0x10; + + /* memset(fh->dummy, 0, sizeof(fh->dummy)); */ + memset(fh->macfileinfo, 0, sizeof(fh->macfileinfo)); + + fh->nencode = 0x0000; /* we need to figure out these encodings for filenames */ + fh->nlanguage = 0x0000; + + /* memset(fh->name, 0, sizeof(fh->name)); */ + memcpy(fh->name, "listing.txt", sizeof(fh->name)); + memset(fh->name+strlen(fh->name), 0, 64-strlen(fh->name)); + + faimdprintf(sess, 2, "faim: OFT: listing fh name %s / %s\n", fh->name, (fh->name+(strlen(fh->name)))); + return fh; +} + +/** + * aim_listenestablish - create a listening socket on a port. + * @portnum: the port number to bind to. + * + * you need to call accept() when it's connected. returns your fd + * + */ +faim_export int aim_listenestablish(u_short portnum) +{ +#if defined(__linux__) + /* XXX what other OS's support getaddrinfo? */ + int listenfd; + const int on = 1; + struct addrinfo hints, *res, *ressave; + char serv[5]; + + snprintf(serv, sizeof(serv), "%d", portnum); + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + if (getaddrinfo(NULL /*any IP*/, serv, &hints, &res) != 0) { + perror("getaddrinfo"); + return -1; + } + ressave = res; + do { + listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (listenfd < 0) + continue; + setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + if (bind(listenfd, res->ai_addr, res->ai_addrlen) == 0) + break; + /* success */ + close(listenfd); + } while ( (res = res->ai_next) ); + + if (!res) + return -1; + + if (listen(listenfd, 1024)!=0) { + perror("listen"); + return -1; + } + + freeaddrinfo(ressave); + return listenfd; +#else + int listenfd; + const int on = 1; + struct sockaddr_in sockin; + + if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket(listenfd)"); + return -1; + } + + if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on) != 0)) { + perror("setsockopt(listenfd)"); + close(listenfd); + return -1; + } + + memset(&sockin, 0, sizeof(struct sockaddr_in)); + sockin.sin_family = AF_INET; + sockin.sin_port = htons(portnum); + + if (bind(listenfd, (struct sockaddr *)&sockin, sizeof(struct sockaddr_in)) != 0) { + perror("bind(listenfd)"); + close(listenfd); + return -1; + } + if (listen(listenfd, 4) != 0) { + perror("listen(listenfd)"); + close(listenfd); + return -1; + } + return listenfd; +#endif +} + +/** + * aim_get_command_rendezvous - OFT equivalent of aim_get_command + * @sess: session to work on + * @conn: conn to pull data from + * + * this reads and handles data from conn->fd. currently a little rough + * around the edges + */ +faim_internal int aim_get_command_rendezvous(struct aim_session_t *sess, struct aim_conn_t *conn) +{ + unsigned char hdrbuf1[6]; + unsigned char *hdr = NULL; + int hdrlen, hdrtype; + int flags = 0; + rxcallback_t userfunc = NULL; + + if (!sess || !conn || !conn->priv) + return -1; + + memset(hdrbuf1, 0, sizeof(hdrbuf1)); + faim_mutex_lock(&conn->active); + + /* gets locked down for the entirety */ + + if (conn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE ) { + struct aim_filetransfer_priv *ft; + ft = conn->priv; + if (ft->state == 2) { + /* waiting on listing data */ + int ret = 0; + char *listing; + struct command_tx_struct *newoft; + if (!(listing = malloc(ft->fh.size))) { + faim_mutex_unlock(&conn->active); + return -1; + } + + ft->state = 0; + if (aim_recv(conn->fd, listing, ft->fh.size) != ft->fh.size) + faimdprintf(sess, 2, "OFT get: file %s was short. (0x%lx)\n", ft->fh.name, ft->fh.size); + + if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x120b, 0))) { + faimdprintf(sess, 2, "faim: aim_get_command_rendezvous: getfile listing: tx_new OFT failed\n"); + faim_mutex_unlock(&conn->active); + free(listing); + aim_conn_close(conn); + return -1; + } + + newoft->lock = 1; + + memcpy(newoft->hdr.oft.magic, "OFT2", 4); + newoft->hdr.oft.hdr2len = 0x100 - 8; + + /* Protocol BS - set nrecvd to size of listing, recvcsum to + listing checksum, flags to 0 */ + + ft->fh.nrecvd = ft->fh.size; + ft->fh.recvcsum = ft->fh.checksum; + ft->fh.flags = 0; + + if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) { + newoft->lock = 0; + aim_tx_destroy(newoft); + free(listing); + faim_mutex_unlock(&conn->active); + return -1; + } + + if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)))) + faimdprintf(sess, 2, "eek! bh fail listing\n"); + + /* send the 120b */ + newoft->lock = 0; + aim_tx_enqueue(sess, newoft); + if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTING)) ) + ret = userfunc(sess, NULL, conn, ft, listing); + + faim_mutex_unlock(&conn->active); + free(listing); + return ret; + } + if (ft->state == 3) { + /* waiting on file data */ + if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILERECEIVE)) ) { + faim_mutex_unlock(&conn->active); + return userfunc(sess, NULL, conn, ft); + } + faim_mutex_unlock(&conn->active); + return 0; + } + if(ft->state == 4) { + if( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILESTATE4)) ) { + faim_mutex_unlock(&conn->active); + return userfunc(sess, NULL, conn); + } + faim_mutex_unlock(&conn->active); + aim_conn_close(conn); + return 0; + } + } + + if ( (hdrlen = aim_recv(conn->fd, hdrbuf1, 6)) < 6) { + faimdprintf(sess, 2, "faim: rend: read error (fd: %i) %02x%02x%02x%02x%02x%02x (%i)\n", + conn->fd, hdrbuf1[0],hdrbuf1[1],hdrbuf1[2],hdrbuf1[3],hdrbuf1[4],hdrbuf1[5],hdrlen); + faim_mutex_unlock(&conn->active); + if (hdrlen < 0) + perror("read"); + else { /* disconnected */ + char *screenname = NULL; + int ret; + struct aim_msgcookie_t *cook; + + switch(conn->subtype) { + case AIM_CONN_SUBTYPE_OFT_DIRECTIM: { + struct aim_directim_priv *priv = NULL; + if (!(priv = (struct aim_directim_priv *)conn->priv) ) + return -1; + + screenname = strdup(priv->sn); + + cook = aim_uncachecookie(sess, priv->cookie, AIM_COOKIETYPE_OFTIM); + aim_cookie_free(sess, cook); + if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMDISCONNECT)) ) { + aim_conn_close(conn); + ret = userfunc(sess, NULL, conn, screenname); + free(screenname); + return ret; + } + break; + } + case AIM_CONN_SUBTYPE_OFT_GETFILE: { + struct aim_filetransfer_priv *priv; + if (!(priv = (struct aim_filetransfer_priv *)conn->priv)) + return -1; + screenname = strdup(priv->sn); + + cook = aim_uncachecookie(sess, priv->cookie, AIM_COOKIETYPE_OFTGET); + + aim_cookie_free(sess, cook); + + if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEDISCONNECT)) ) { + aim_conn_close(conn); + ret = userfunc(sess, NULL, conn, screenname); + free(screenname); + return ret; + } + break; + } + case AIM_CONN_SUBTYPE_OFT_SENDFILE: { + struct aim_filetransfer_priv *priv; + if (!(priv = (struct aim_filetransfer_priv *)conn->priv)) + return -1; + + screenname = strdup(priv->sn); + + cook = aim_uncachecookie(sess, priv->cookie, AIM_COOKIETYPE_OFTSEND); + aim_cookie_free(sess, cook); + if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_SENDFILEDISCONNECT)) ) { + aim_conn_close(conn); + ret = userfunc(sess, NULL, conn, screenname); + free(screenname); + return ret; + } + break; + } + } + + aim_conn_close(conn); + return -1; + } + } + + hdrlen = aimutil_get16(hdrbuf1+4); + hdrlen -= 6; + + if (!(hdr = malloc(hdrlen))) { + faim_mutex_unlock(&conn->active); + return -1; + } + + if (aim_recv(conn->fd, hdr, hdrlen) < hdrlen) { + perror("read"); + faimdprintf(sess, 2,"faim: rend: read2 error on %d (%d)\n", conn->fd, hdrlen); + free(hdr); + faim_mutex_unlock(&conn->active); + aim_conn_close(conn); + return -1; + } + hdrtype = aimutil_get16(hdr); + + switch (hdrtype) { + case 0x0001: { /* directim */ + int payloadlength = 0; + char *snptr = NULL; + struct aim_directim_priv *priv; + int i; + + if (!(priv = (struct aim_directim_priv *)calloc(1, sizeof(struct aim_directim_priv)))) { + faim_mutex_unlock(&conn->active); + free(hdr); + return -1; + } + + payloadlength = aimutil_get32(hdr+22); + flags = aimutil_get16(hdr+32); + snptr = (char *)hdr+38; + strncpy(priv->sn, snptr, MAXSNLEN); + + faimdprintf(sess, 2, "faim: OFT frame: %04x / %04x / %04x / %s\n", hdrtype, payloadlength, flags, snptr); + + free(hdr); + hdr = NULL; + + if (flags == 0x000e) { + faim_mutex_unlock(&conn->active); + if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING)) ) + return userfunc(sess, NULL, snptr); + } else { + + if ((flags == 0x0000) && payloadlength) { + unsigned char *msg; + + if (!(msg = calloc(1, payloadlength+1))) { + faim_mutex_unlock(&conn->active); + return -1; + } + + if (aim_recv(conn->fd, msg, payloadlength) < payloadlength) { + perror("read"); + faimdprintf(sess, 2,"faim: rend: read3 error\n"); + free(msg); + faim_mutex_unlock(&conn->active); + aim_conn_close(conn); + return -1; + } + + faim_mutex_unlock(&conn->active); + msg[payloadlength] = 0x00; + faimdprintf(sess, 2, "faim: directim: %s/%04x/%04x/%s\n", snptr, payloadlength, flags, msg); + + if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING)) ) + i = userfunc(sess, NULL, conn, snptr, msg); + else { + faimdprintf(sess, 0, "directim: %s/%04x/%04x/%s\n", snptr, payloadlength, flags, msg); + i = 1; + } + + free(msg); + + return i; + } + } + break; + } + case 0x1108: { /* getfile listing.txt incoming tx->rx */ + struct aim_filetransfer_priv *ft; + struct aim_fileheader_t *fh; + struct aim_msgcookie_t *cook; + struct command_tx_struct *newoft; + + faimdprintf(sess, 2,"faim: rend: fileget 0x1108\n"); + fh = aim_oft_getfh(hdr); + + free(hdr); + hdr = NULL; + + faim_mutex_unlock(&conn->active); + + if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) { + faim_mutex_unlock(&conn->active); + free(fh); + return -1; + } + + ft = cook->data; + + /* we're waaaaiiiting.. for listing.txt */ + ft->state = 2; + + memcpy(&(ft->fh), fh, sizeof(struct aim_fileheader_t)); + free(fh); + + if(aim_cachecookie(sess, cook) == -1) { + faimdprintf(sess, 1, "error caching cookie\n"); + return -1; + } + + if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x1209, 0))) { + aim_conn_close(conn); + return -1; + } + + memcpy(newoft->hdr.oft.magic, "OFT2", 4); + newoft->hdr.oft.hdr2len = 0x100 - 8; + + if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) { + newoft->lock = 0; + aim_tx_destroy(newoft); + return -1; + } + + if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)))) { + newoft->lock = 0; + aim_tx_destroy(newoft); + return -1; + } + + newoft->lock = 0; + aim_tx_enqueue(sess, newoft); + break; + + } + case 0x1209: { /* get file listing ack rx->tx */ + struct aim_filetransfer_priv *ft; + struct aim_fileheader_t *fh; + struct aim_msgcookie_t *cook; + int ret = 0; + + if(!(fh = aim_oft_getfh(hdr))) { + perror("getfh"); + free(hdr); + return -1; + } + + free(hdr); + hdr = NULL; + + faim_mutex_unlock(&conn->active); + + if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) + faimdprintf(sess, 2, "shit, no cookie in 0x1209. (%i/%s)going to crash..\n", + AIM_COOKIETYPE_OFTGET, fh->bcookie); + + ft = cook->data; + + if (ft->fh.size != fh->size) + faimdprintf(sess, 2, "hrm. ft->fh.size (%ld) != fh->size (%ld). um. using ft->fh.size\n", + ft->fh.size, fh->size); + + if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTINGREQ))) + ret = userfunc(sess, NULL, conn, fh); + + faimdprintf(sess, 2, "faim: get_command_rendezvous: hit end of 1209\n"); + + free(fh); + + return ret; + + break; + } + case 0x120b: { /* getfile listing.txt rx confirm */ + struct aim_filetransfer_priv *ft; + struct aim_msgcookie_t *cook; + struct aim_fileheader_t *fh; + + fh = aim_oft_getfh(hdr); + + free(hdr); + hdr = NULL; + + faim_mutex_unlock(&conn->active); + + if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) { + free(fh); + return -1; + } + + free(fh); + + ft = cook->data; + + if (aim_cachecookie(sess, cook) == -1) { + return -1; + } + + if((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTINGRXCONFIRM))) + return userfunc(sess, NULL, conn); + + break; + } + case 0x120c: { /* getfile file request */ + struct aim_filetransfer_priv *ft; + struct aim_msgcookie_t *cook; + struct aim_fileheader_t *fh; + struct command_tx_struct *newoft; + int i = 0; + + fh = aim_oft_getfh(hdr); + + free(hdr); + hdr = NULL; + + faim_mutex_unlock(&conn->active); + + if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) { + faimdprintf(sess, 2, "no cookie in 120c\n"); + return -1; + } + + ft = cook->data; + memcpy(&(ft->fh), fh, sizeof(struct aim_fileheader_t)); + free(fh); + + aim_cachecookie(sess, cook); + + faimdprintf(sess, 2, "faim: fileget: %s seems to want %s\n", ft->sn, ft->fh.name); + + if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILEREQ)) ) + i = userfunc(sess, NULL, conn, &(ft->fh), cook->cookie); + + if (i < 0) + return i; + + if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0101, 0))) { + faimdprintf(sess, 2, "faim: send_final_transfer: tx_new OFT failed\n"); + return -1; + } + + newoft->lock = 1; + memcpy(newoft->hdr.oft.magic, "OFT2", 4); + newoft->hdr.oft.hdr2len = 0x100 - 8; + + if (!(newoft->hdr.oft.hdr2 = calloc(1,newoft->hdr.oft.hdr2len))) { + newoft->lock = 0; + aim_tx_destroy(newoft); + return -1; + } + + /* protocol BS: nrecvd, recvcsum to 0, flags to 0x20. */ + ft->fh.nrecvd = 0; + ft->fh.recvcsum = 0; + ft->fh.flags = 0x20; + + aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)); + + newoft->lock = 0; + aim_tx_enqueue(sess, newoft); + + faimdprintf(sess, 2, "faim: OFT: OFT file header enqueued.\n"); + + return i; + + break; + } + case 0x0101: { /* getfile: sending data */ + struct aim_fileheader_t *fh; + struct aim_filetransfer_priv *ft; + struct aim_msgcookie_t *cook; + struct command_tx_struct *newoft; + + fh = aim_oft_getfh(hdr); + + free(hdr); + hdr = NULL; + + faim_mutex_unlock(&conn->active); + + if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) { + free(fh); + return -1; + } + free(fh); + + ft = cook->data; + + ft->state = 3; + + if (aim_cachecookie(sess, cook) == -1) { + perror("aim_cachecookie"); + return -1; + } + + faimdprintf(sess, 2, "faim: fileget: %s seems to want to send %s\n", ft->sn, ft->fh.name); + + if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0202, 0))) { + aim_conn_close(conn); + faimdprintf(sess, 2, "faim: send_final_transfer: tx_new OFT failed\n"); + return -1; + } + + newoft->lock = 1; + memcpy(newoft->hdr.oft.magic, "OFT2", 4); + + newoft->hdr.oft.hdr2len = 0x100 - 8; + + if (!(newoft->hdr.oft.hdr2 = calloc(1,newoft->hdr.oft.hdr2len))) { + newoft->lock = 0; + aim_tx_destroy(newoft); + return -1; + } + + aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)); + + newoft->lock = 0; + aim_tx_enqueue(sess, newoft); + + faimdprintf(sess, 2, "faim: OFT: OFT 0x0202 enqueued.\n"); + + if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILEREQ)) == NULL) + return 1; + + break; + } + case 0x0202: { /* get file: ready to receive data */ + struct aim_fileheader_t *fh; + struct aim_filetransfer_priv *ft; + struct aim_msgcookie_t *cook; + int ret = 1; + + fh = aim_oft_getfh(hdr); + + free(hdr); + hdr = NULL; + + faim_mutex_unlock(&conn->active); + + if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) { + free(fh); + return -1; + } + + ft = cook->data; + + faimdprintf(sess, 2, "faim: get_rend: looks like we're ready to send data.(oft 0x0202)\n"); + + if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILESEND)) ) + ret = userfunc(sess, NULL, conn, fh); + + free(fh); + + return ret; + break; + } + case 0x0204: { /* get file: finished. close it up */ + int i; + struct aim_fileheader_t *fh; + + fh = aim_oft_getfh(hdr); + + free(hdr); + hdr = NULL; + + faim_mutex_unlock(&conn->active); + + faimdprintf(sess, 2, "faim: get_rend: looks like we're done with a transfer (oft 0x0204)\n"); + + if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILECOMPLETE)) ) + i = userfunc(sess, NULL, conn, fh); + else + i = 1; + + if (conn) + aim_conn_close(conn); + + free(fh); + + return i; + break; + } + default: { + free(hdr); + hdr = NULL; + faimdprintf(sess, 2,"faim: OFT frame: uknown type %04x\n", hdrtype); + faim_mutex_unlock(&conn->active); + break; + } + } /* switch */ + if (hdr) { + faimdprintf(sess, 0, "hdr wasn't freed by a rendezvous switch case (hdrtype: %0x04x)!\n", hdrtype); + free(hdr); + hdr = NULL; + } + return 0; +} + +/** + * aim_oft_getfh - extracts an &aim_fileheader_t from buffer hdr. + * @hdr: buffer to extract header from + * + * returns pointer to new struct on success; %NULL on error. + * + */ +static struct aim_fileheader_t *aim_oft_getfh(unsigned char *hdr) +{ + struct aim_fileheader_t *fh; + int i, j; + if (!(fh = calloc(1, sizeof(struct aim_fileheader_t)))) + return NULL; + + /* [0] and [1] are the type. we can ignore those here. */ + i = 2; + for(j = 0; j < 8; j++, i++) + fh->bcookie[j] = hdr[i]; + fh->encrypt = aimutil_get16(hdr+i); + i += 2; + fh->compress = aimutil_get16(hdr+i); + i += 2; + fh->totfiles = aimutil_get16(hdr+i); + i += 2; + fh->filesleft = aimutil_get16(hdr+i); + i += 2; + fh->totparts = aimutil_get16(hdr+i); + i += 2; + fh->partsleft = aimutil_get16(hdr+i); + i += 2; + fh->totsize = aimutil_get32(hdr+i); + i += 4; + fh->size = aimutil_get32(hdr+i); + i += 4; + fh->modtime = aimutil_get32(hdr+i); + i += 4; + fh->checksum = aimutil_get32(hdr+i); + i += 4; + fh->rfrcsum = aimutil_get32(hdr+i); + i += 4; + fh->rfsize = aimutil_get32(hdr+i); + i += 4; + fh->cretime = aimutil_get32(hdr+i); + i += 4; + fh->rfcsum = aimutil_get32(hdr+i); + i += 4; + fh->nrecvd = aimutil_get32(hdr+i); + i += 4; + fh->recvcsum = aimutil_get32(hdr+i); + i += 4; + memcpy(fh->idstring, hdr+i, 32); + i += 32; + fh->flags = aimutil_get8(hdr+i); + i += 1; + fh->lnameoffset = aimutil_get8(hdr+i); + i += 1; + fh->lsizeoffset = aimutil_get8(hdr+i); + i += 1; + memcpy(fh->dummy, hdr+i, 69); + i += 69; + memcpy(fh->macfileinfo, hdr+i, 16); + i += 16; + fh->nencode = aimutil_get16(hdr+i); + i += 2; + fh->nlanguage = aimutil_get16(hdr+i); + i += 2; + memcpy(fh->name, hdr+i, 64); + i += 64; + return fh; +} + +/** + * aim_oft_checksum - calculate oft checksum of buffer + * @buffer: buffer of data to checksum + * @bufsize: size of buffer + * @checksum: pointer to integer to place result in (pointer!) + * + * + * Note that checksum is a pointer. Checksum should be filled with + * 0xFFFF0000 for each new file; you can have this checksum chunks of + * files in series if you just call it repeatedly in a for(; ; ) loop + * and don't reset the checksum between each call. And you thought we + * didn't care about you and your pathetic client's meomry footprint + * ;^) + * + * + * Also, it's been said that this is incorrect as currently + * written. You were warned. + */ +faim_export int aim_oft_checksum(struct aim_session_t *sess, char *buffer, int bufsize, int *checksum) +{ + short check0, check1; + int i; + check0 = ((*checksum & 0xFF000000) >> 16); + check1 = ((*checksum & 0x00ff0000) >> 16); + for(i = 0; i < bufsize; i++) { + if (i % 2) { /* use check1 -- second byte */ + if ( (short)buffer[i] > check1 ) { /* wrapping */ + check1 += 0x100; /* this is a cheap way to wrap */ + + /* if we're wrapping, decrement the other one */ + /* XXX: check this corner case */ + if (check0 == 0) + check0 = 0x00ff; + else + check0--; + } + check1 -= buffer[i]; + } else { /* use check0 -- first byte */ + if ( (short)buffer[i] > check0 ) { /* wrapping */ + check0 += 0x100; /* this is a cheap way to wrap */ + + /* if we're wrapping, decrement the other one */ + /* XXX: check this corner case */ + if (check1 == 0) + check1 = 0x00ff; + else + check1--; + } + check0 -= buffer[i]; + } + } + + if (check0 > 0xff || check1 > 0xff) { + /* they shouldn't be able to do this. error! */ + faimdprintf(sess, 2, "check0 or check1 is too high: 0x%04x, 0x%04x\n", check0, check1); + return -1; + } + + /* grab just the lowest byte; this should be clean, but just in + case */ + check0 &= 0xff; + check1 &= 0xff; + + *checksum = ((check0 * 0x1000000) + (check1 * 0x10000)); + return *checksum; +} + +/** + * aim_oft_buildheader - fills a buffer with network-order fh data + * @dest: buffer to fill -- pre-alloced + * @fh: fh to get data from + * + * returns length written; -1 on error. + * DOES NOT DO BOUNDS CHECKING! + * + */ +faim_internal int aim_oft_buildheader(unsigned char *dest,struct aim_fileheader_t *fh) +{ + int i, curbyte; + if (!dest || !fh) + return -1; + curbyte = 0; + for(i = 0; i < 8; i++) + curbyte += aimutil_put8(dest+curbyte, fh->bcookie[i]); + curbyte += aimutil_put16(dest+curbyte, fh->encrypt); + curbyte += aimutil_put16(dest+curbyte, fh->compress); + curbyte += aimutil_put16(dest+curbyte, fh->totfiles); + curbyte += aimutil_put16(dest+curbyte, fh->filesleft); + curbyte += aimutil_put16(dest+curbyte, fh->totparts); + curbyte += aimutil_put16(dest+curbyte, fh->partsleft); + curbyte += aimutil_put32(dest+curbyte, fh->totsize); + curbyte += aimutil_put32(dest+curbyte, fh->size); + curbyte += aimutil_put32(dest+curbyte, fh->modtime); + curbyte += aimutil_put32(dest+curbyte, fh->checksum); + curbyte += aimutil_put32(dest+curbyte, fh->rfrcsum); + curbyte += aimutil_put32(dest+curbyte, fh->rfsize); + curbyte += aimutil_put32(dest+curbyte, fh->cretime); + curbyte += aimutil_put32(dest+curbyte, fh->rfcsum); + curbyte += aimutil_put32(dest+curbyte, fh->nrecvd); + curbyte += aimutil_put32(dest+curbyte, fh->recvcsum); + memcpy(dest+curbyte, fh->idstring, 32); + curbyte += 32; + curbyte += aimutil_put8(dest+curbyte, fh->flags); + curbyte += aimutil_put8(dest+curbyte, fh->lnameoffset); + curbyte += aimutil_put8(dest+curbyte, fh->lsizeoffset); + memcpy(dest+curbyte, fh->dummy, 69); + curbyte += 69; + memcpy(dest+curbyte, fh->macfileinfo, 16); + curbyte += 16; + curbyte += aimutil_put16(dest+curbyte, fh->nencode); + curbyte += aimutil_put16(dest+curbyte, fh->nlanguage); + memset(dest+curbyte, 0x00, 64); + memcpy(dest+curbyte, fh->name, 64); + + /* XXX: Filenames longer than 64B */ + curbyte += 64; + return curbyte; +} + + +/** + * aim_tx_destroy - free's tx_command_t's + * @command: the command to free + * + * if command is locked, doesn't free. + * returns -1 on error (locked struct); 0 on success. + * + */ +faim_internal int aim_tx_destroy(struct command_tx_struct *command){ + if (command->lock) + return -1; + if (command->data) + free(command->data); + if (command->hdrtype == AIM_FRAMETYPE_OFT && command->hdr.oft.hdr2) + free(command->hdr.oft.hdr2); + free(command); + return 0; +} + +/** + * aim_getfile_intitiate - Request an OFT getfile session + * @sess: your session, + * @conn: the BOS conn, + * @destsn is the SN to connect to. + * + * returns a new &aim_conn_t on success, %NULL on error + */ +faim_export struct aim_conn_t *aim_getfile_initiate(struct aim_session_t *sess, struct aim_conn_t *conn, char *destsn) +{ + struct command_tx_struct *newpacket; + struct aim_conn_t *newconn; + struct aim_filetransfer_priv *priv; + struct aim_msgcookie_t *cookie; + int curbyte, i, listenfd; + short port = 4443; + struct hostent *hptr; + struct utsname myname; + char cap[16]; + char d[4]; + + /* Open our socket */ + + if ( (listenfd = aim_listenestablish(port)) == -1) + return NULL; + + /* get our local IP */ + + if (uname(&myname) < 0) + return NULL; + if ( (hptr = gethostbyname(myname.nodename)) == NULL) + return NULL; + memcpy(&d, hptr->h_addr_list[0], 4); + + aim_putcap(cap, 16, AIM_CAPS_GETFILE); + + /* create the OSCAR packet */ + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+8+2+1+strlen(destsn)+4+4+0x42))) + return NULL; + newpacket->lock = 1; + + /* lock struct */ + curbyte = 0; + curbyte += aim_putsnac(newpacket->data+curbyte, 0x0004, 0x0006, 0x0000, sess->snac_nextid); + + /* XXX: check the cookie before commiting to using it */ + + /* Generate a random message cookie + * This cookie needs to be alphanumeric and NULL-terminated to be TOC-compatible. */ + for (i=0; i<7; i++) + curbyte += aimutil_put8(newpacket->data+curbyte, 0x30 + ((u_char) random() % 10)); + + curbyte += aimutil_put8(newpacket->data+curbyte, 0x00); + + /* grab all the data for cookie caching. */ + + if (!(cookie = (struct aim_msgcookie_t *)calloc(1, sizeof(struct aim_msgcookie_t)))) + return NULL; + memcpy(cookie->cookie, newpacket->data+curbyte-8, 8); + cookie->type = AIM_COOKIETYPE_OFTGET; + + if (!(priv = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv)))) + return NULL; + memcpy(priv->cookie, cookie, 8); + memcpy(priv->sn, destsn, sizeof(priv->sn)); + memcpy(priv->fh.name, "listing.txt", strlen("listing.txt")); + priv->state = 1; + + cookie->data = priv; + + aim_cachecookie(sess, cookie); + + /* Channel ID */ + curbyte += aimutil_put16(newpacket->data+curbyte,0x0002); + + /* Destination SN (prepended with byte length) */ + curbyte += aimutil_put8(newpacket->data+curbyte,strlen(destsn)); + curbyte += aimutil_putstr(newpacket->data+curbyte, destsn, strlen(destsn)); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); + + /* enTLV start */ + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0042); + + /* Flag data / ICBM Parameters? */ + curbyte += aimutil_put8(newpacket->data+curbyte, 0x00); + curbyte += aimutil_put8(newpacket->data+curbyte, 0x00); + + /* Cookie */ + curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cookie, 8); + + /* Capability String */ + curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cap, 0x10); + + /* 000a/0002 : 0001 */ + curbyte += aimutil_put16(newpacket->data+curbyte, 0x000a); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001); + + /* 0003/0004: IP address */ + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0004); + for(i = 0; i < 4; i++) + curbyte += aimutil_put8(newpacket->data+curbyte, d[i]); + + /* already in network byte order */ + + /* 0005/0002: Port */ + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002); + curbyte += aimutil_put16(newpacket->data+curbyte, port); + + /* 000f/0000: ?? */ + curbyte += aimutil_put16(newpacket->data+curbyte, 0x000f); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); + + /* 2711/000c: ?? */ + curbyte += aimutil_put16(newpacket->data+curbyte, 0x2711); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x000c); + curbyte += aimutil_put32(newpacket->data+curbyte, 0x00120001); + + for(i = 0; i < 0x000c - 4; i++) + curbyte += aimutil_put8(newpacket->data+curbyte, 0x00); + + newpacket->commandlen = curbyte; + newpacket->lock = 0; + aim_tx_enqueue(sess, newpacket); + + /* allocate and set up our connection */ + + i = fcntl(listenfd, F_GETFL, 0); + fcntl(listenfd, F_SETFL, i | O_NONBLOCK); + newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS_OUT, NULL); + + if (!newconn){ + perror("aim_newconn"); + return NULL; + } + + newconn->fd = listenfd; + newconn->subtype = AIM_CONN_SUBTYPE_OFT_GETFILE; + newconn->priv = priv; + faimdprintf(sess, 2,"faim: listening (fd = %d, unconnected)\n", newconn->fd); + + return newconn; +} + +/** + * aim_oft_getfile_request - request a particular file over an established getfile connection + * @sess: your session + * @conn: the established OFT getfile connection + * @name: filename to request + * @size: size of the file + * + * + * returns -1 on error, 0 on successful enqueuing + */ +faim_export int aim_oft_getfile_request(struct aim_session_t *sess, struct aim_conn_t *conn, const unsigned char *name, const int size) +{ + struct command_tx_struct *newoft; + struct aim_filetransfer_priv *ft; + if (!sess || !conn || !conn->priv || !name) + return -1; + + if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x120c, 0))) { + faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n"); + return -1; + } + + newoft->lock = 1; + + memcpy(newoft->hdr.oft.magic, "OFT2", 4); + newoft->hdr.oft.hdr2len = 0x100 - 8; + + ft = (struct aim_filetransfer_priv *)conn->priv; + ft->fh.filesleft = 1; + ft->fh.totfiles = 1; + ft->fh.totparts = 1; + ft->fh.partsleft = 1; + ft->fh.totsize = size; + ft->fh.size = size; + ft->fh.checksum = 0; + memcpy(ft->fh.name, name, strlen(name)); + memset(ft->fh.name+strlen(name), 0, 1); + + if (!(newoft->hdr.oft.hdr2 = (unsigned char *)calloc(1,newoft->hdr.oft.hdr2len))) { + newoft->lock = 0; + aim_tx_destroy(newoft); + return -1; + } + + if (!(aim_oft_buildheader(newoft->hdr.oft.hdr2, &(ft->fh)))) { + newoft->lock = 0; + aim_tx_destroy(newoft); + return -1; + } + + newoft->lock = 0; + + aim_tx_enqueue(sess, newoft); + return 0; +} + +/** + * aim_oft_getfile_ack - acknowledge a getfile download as complete + * @sess: your session + * @conn: the getfile conn to send the ack over + * + * Call this function after you have read all the data in a particular + * filetransfer. Returns -1 on error, 0 on apparent success + * + */ +faim_export int aim_oft_getfile_ack(struct aim_session_t *sess, struct aim_conn_t *conn) +{ + struct command_tx_struct *newoft; + struct aim_filetransfer_priv *ft; + + if (!sess || !conn || !conn->priv) + return -1; + + if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0202, 0))) { + faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n"); + return -1; + } + + newoft->lock = 1; + + memcpy(newoft->hdr.oft.magic, "OFT2", 4); + newoft->hdr.oft.hdr2len = 0x100-8; + + if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) { + newoft->lock = 0; + aim_tx_destroy(newoft); + return -1; + } + + ft = (struct aim_filetransfer_priv *)conn->priv; + + if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)))) { + newoft->lock = 0; + aim_tx_destroy(newoft); + return -1; + } + + newoft->lock = 0; + aim_tx_enqueue(sess, newoft); + return 0; +} + +/** + * aim_oft_getfile_end - end a getfile. + * @sess: your session + * @conn: the getfile connection + * + * call this before you close the getfile connection if you're on the + * receiving/requesting end. + */ +faim_export int aim_oft_getfile_end(struct aim_session_t *sess, struct aim_conn_t *conn) +{ + struct command_tx_struct *newoft; + struct aim_filetransfer_priv *ft; + + if (!sess || !conn || !conn->priv) + return -1; + + if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0204, 0))) { + faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n"); + return -1; + } + + newoft->lock = 1; + + memcpy(newoft->hdr.oft.magic, "OFT2", 4); + newoft->hdr.oft.hdr2len = 0x100 - 8; + + if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) { + newoft->lock = 0; + aim_tx_destroy(newoft); + return -1; + } + + ft = (struct aim_filetransfer_priv *)conn->priv; + ft->state = 4; /* no longer wanting data */ + ft->fh.nrecvd = ft->fh.size; + ft->fh.recvcsum = ft->fh.checksum; + ft->fh.flags = 0x21; + + if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)))) { + newoft->lock = 0; + aim_tx_destroy(newoft); + return -1; + } + + newoft->lock = 0; + aim_tx_enqueue(sess, newoft); + + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfaim/im.c Mon Mar 05 03:59:32 2001 +0000 @@ -0,0 +1,958 @@ +/* + * aim_im.c + * + * The routines for sending/receiving Instant Messages. + * + */ + +#define FAIM_INTERNAL +#include <aim.h> + +/* + * 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 0001 0101 01 AOL v6.0, CompuServe 2000 v6.0, any + * TOC client + */ +faim_export unsigned short aim_fingerprintclient(unsigned char *msghdr, int len) +{ + static const struct { + unsigned short clientid; + int len; + unsigned char data[10]; + } fingerprints[] = { + /* AOL Mobile Communicator, WinAIM 1.0.414 */ + { AIM_CLIENTTYPE_MC, + 9, {0x05, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01}}, + + /* WinAIM 2.0.847, 2.1.1187, 3.0.1464, 4.3.2229, 4.4.2286 */ + { AIM_CLIENTTYPE_WINAIM, + 9, {0x05, 0x01, 0x00, 0x03, 0x01, 0x01, 0x02, 0x01, 0x01}}, + + /* WinAIM 4.1.2010, libfaim */ + { AIM_CLIENTTYPE_WINAIM41, + 10, {0x05, 0x01, 0x00, 0x04, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01}}, + + /* AOL v6.0, CompuServe 2000 v6.0, any TOC client */ + { AIM_CLIENTTYPE_AOL_TOC, + 7, {0x05, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01}}, + + { 0, 0} + }; + int i; + + if (!msghdr || (len <= 0)) + return 0; + + 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; +} + +/* + * 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) + * + */ +faim_export unsigned long aim_send_im(struct aim_session_t *sess, + struct aim_conn_t *conn, + char *destsn, u_int flags, char *msg) +{ + + int curbyte,i; + struct command_tx_struct *newpacket; + + if (strlen(msg) >= MAXMSGLEN) + return -1; + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, strlen(msg)+256))) + return -1; + + newpacket->lock = 1; /* lock struct */ + + curbyte = 0; + curbyte += aim_putsnac(newpacket->data+curbyte, + 0x0004, 0x0006, 0x0000, sess->snac_nextid); + + /* + * Generate a random message cookie + * + * We could cache these like we do SNAC IDs. (In fact, it + * might be a good idea.) In the message error functions, + * the 8byte message cookie is returned as well as the + * SNAC ID. + * + */ + for (i=0;i<8;i++) + curbyte += aimutil_put8(newpacket->data+curbyte, (u_char) rand()); + + /* + * Channel ID + */ + curbyte += aimutil_put16(newpacket->data+curbyte,0x0001); + + /* + * Destination SN (prepended with byte length) + */ + curbyte += aimutil_put8(newpacket->data+curbyte,strlen(destsn)); + curbyte += aimutil_putstr(newpacket->data+curbyte, destsn, strlen(destsn)); + + /* + * metaTLV start. + */ + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002); + curbyte += aimutil_put16(newpacket->data+curbyte, strlen(msg) + 0x10); + + /* + * Flag data / ICBM Parameters? + * + * I don't know what these are... + * + */ + curbyte += aimutil_put8(newpacket->data+curbyte, 0x05); + curbyte += aimutil_put8(newpacket->data+curbyte, 0x01); + + /* number of bytes to follow */ + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0004); + curbyte += aimutil_put8(newpacket->data+curbyte, 0x01); + curbyte += aimutil_put8(newpacket->data+curbyte, 0x01); + curbyte += aimutil_put8(newpacket->data+curbyte, 0x01); + curbyte += aimutil_put8(newpacket->data+curbyte, 0x02); + + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0101); + + /* + * Message block length. + */ + curbyte += aimutil_put16(newpacket->data+curbyte, strlen(msg) + 0x04); + + /* + * Character set data? + */ + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); + + /* + * Message. Not terminated. + */ + curbyte += aimutil_putstr(newpacket->data+curbyte,msg, strlen(msg)); + + /* + * Set the Request Acknowledge flag. + */ + if (flags & AIM_IMFLAGS_ACK) { + curbyte += aimutil_put16(newpacket->data+curbyte,0x0003); + curbyte += aimutil_put16(newpacket->data+curbyte,0x0000); + } + + /* + * Set the Autoresponse flag. + */ + if (flags & AIM_IMFLAGS_AWAY) { + curbyte += aimutil_put16(newpacket->data+curbyte,0x0004); + curbyte += aimutil_put16(newpacket->data+curbyte,0x0000); + } + + newpacket->commandlen = curbyte; + newpacket->lock = 0; + + aim_tx_enqueue(sess, newpacket); + + aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, destsn, strlen(destsn)+1); + aim_cleansnacs(sess, 60); /* clean out all SNACs over 60sec old */ + + return sess->snac_nextid; +} + +faim_internal int aim_parse_outgoing_im_middle(struct aim_session_t *sess, + struct command_rx_struct *command) +{ + unsigned int i = 0, z; + rxcallback_t userfunc = NULL; + unsigned char cookie[8]; + int channel; + struct aim_tlvlist_t *tlvlist; + char sn[MAXSNLEN]; + unsigned short icbmflags = 0; + unsigned char flag1 = 0, flag2 = 0; + unsigned char *msgblock = NULL, *msg = NULL; + + i = 10; + + /* ICBM Cookie. */ + for (z=0; z<8; z++,i++) + cookie[z] = command->data[i]; + + /* Channel ID */ + channel = aimutil_get16(command->data+i); + i += 2; + + if (channel != 0x01) { + faimdprintf(sess, 0, "icbm: ICBM recieved on unsupported channel. Ignoring. (chan = %04x)\n", channel); + return 1; + } + + strncpy(sn, (char *) command->data+i+1, (int) *(command->data+i)); + i += 1 + (int) *(command->data+i); + + tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i); + + if (aim_gettlv(tlvlist, 0x0003, 1)) + icbmflags |= AIM_IMFLAGS_ACK; + if (aim_gettlv(tlvlist, 0x0004, 1)) + icbmflags |= AIM_IMFLAGS_AWAY; + + if (aim_gettlv(tlvlist, 0x0002, 1)) { + int j = 0; + + msgblock = (unsigned char *)aim_gettlv_str(tlvlist, 0x0002, 1); + + /* no, this really is correct. I'm not high or anything either. */ + j += 2; + j += 2 + aimutil_get16(msgblock+j); + j += 2; + + j += 2; /* final block length */ + + flag1 = aimutil_get16(msgblock); + j += 2; + flag2 = aimutil_get16(msgblock); + j += 2; + + msg = msgblock+j; + } + + if ((userfunc = aim_callhandler(sess, command->conn, 0x0004, 0x0006)) || (i = 0)) + i = userfunc(sess, command, channel, sn, msg, icbmflags, flag1, flag2); + + if (msgblock) + free(msgblock); + aim_freetlvchain(&tlvlist); + + return 0; +} + +/* + * 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. + * + * We should also support at least minimal parsing of + * Channel 2, so that we can at least know the name of the + * room we're invited to, but obviously can't attend... + * + */ +faim_internal int aim_parse_incoming_im_middle(struct aim_session_t *sess, + struct command_rx_struct *command) +{ + u_int i = 0,z; + rxcallback_t userfunc = NULL; + u_char cookie[8]; + int channel; + struct aim_tlvlist_t *tlvlist; + struct aim_userinfo_s userinfo; + + memset(&userinfo, 0x00, sizeof(struct aim_userinfo_s)); + + i = 10; /* Skip SNAC header */ + + /* + * Read ICBM Cookie. And throw away. + */ + for (z=0; z<8; z++,i++) + cookie[z] = command->data[i]; + + /* + * Channel ID. + * + * Channel 0x0001 is the message channel. There are + * other channels for things called "rendevous" + * which represent chat and some of the other new + * features of AIM2/3/3.5. + * + * Channel 0x0002 is the Rendevous channel, which + * is where Chat Invitiations and various client-client + * connection negotiations come from. + * + */ + channel = aimutil_get16(command->data+i); + i += 2; + + /* + * + */ + if ((channel != 0x01) && (channel != 0x02)) { + faimdprintf(sess, 0, "icbm: ICBM received on an unsupported channel. Ignoring.\n (chan = %04x)", channel); + return 1; + } + + /* + * 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 seperation. + * aim_extractuserinfo() returns the number of bytes used by the + * userinfo tlvs, so you can start reading the rest of them right + * afterward. + * + * 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. + * + */ + i += aim_extractuserinfo(sess, command->data+i, &userinfo); + + /* + * Read block of TLVs (not including the userinfo data). All + * further data is derived from what is parsed here. + */ + tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i); + + /* + * From here on, its depends on what channel we're on. + */ + if (channel == 1) + { + u_int j = 0, y = 0, z = 0; + char *msg = NULL; + u_int icbmflags = 0; + struct aim_tlv_t *msgblocktlv; + u_char *msgblock; + u_short flag1,flag2; + int finlen = 0; + unsigned char fingerprint[10]; + u_short wastebits; + + /* + * Check Autoresponse status. If it is an autoresponse, + * it will contain a type 0x0004 TLV, with zero length. + */ + if (aim_gettlv(tlvlist, 0x0004, 1)) + icbmflags |= AIM_IMFLAGS_AWAY; + + /* + * Check Ack Request status. + */ + if (aim_gettlv(tlvlist, 0x0003, 1)) + icbmflags |= AIM_IMFLAGS_ACK; + + /* + * Message block. + */ + msgblocktlv = aim_gettlv(tlvlist, 0x0002, 1); + if (!msgblocktlv || !msgblocktlv->value) { + faimdprintf(sess, 0, "icbm: major error! no message block TLV found!\n"); + aim_freetlvchain(&tlvlist); + return 1; + } + + /* + * Extracting the message from the unknown cruft. + * + * This is a bit messy, and I'm not really qualified, + * even as the author, to comment on it. At least + * its not as bad as a while loop shooting into infinity. + * + * "Do you believe in magic?" + * + */ + msgblock = msgblocktlv->value; + j = 0; + + wastebits = aimutil_get8(msgblock+j++); + wastebits = aimutil_get8(msgblock+j++); + + y = aimutil_get16(msgblock+j); + j += 2; + for (z = 0; z < y; z++) + wastebits = aimutil_get8(msgblock+j++); + wastebits = aimutil_get8(msgblock+j++); + wastebits = aimutil_get8(msgblock+j++); + + finlen = j; + if (finlen > sizeof(fingerprint)) + finlen = sizeof(fingerprint); + memcpy(fingerprint, msgblocktlv->value, finlen); + + /* + * Message string length, including flag words. + */ + i = aimutil_get16(msgblock+j); + j += 2; + + /* + * Flag words. + * + * Its rumored that these can kick in some funky + * 16bit-wide char stuff that used to really kill + * libfaim. Hopefully the latter is no longer true. + * + * Though someone should investiagte the former. + * + */ + flag1 = aimutil_get16(msgblock+j); + j += 2; + flag2 = aimutil_get16(msgblock+j); + j += 2; + + if (flag1 || flag2) + faimdprintf(sess, 0, "icbm: **warning: encoding flags are being used! {%04x, %04x}\n", flag1, flag2); + + /* + * Message string. + */ + i -= 4; + msg = (char *)malloc(i+1); + memcpy(msg, msgblock+j, i); + msg[i] = '\0'; + + /* + * Call client. + */ + userfunc = aim_callhandler(sess, command->conn, 0x0004, 0x0007); + if (userfunc) + i = userfunc(sess, command, channel, &userinfo, msg, icbmflags, flag1, flag2, finlen, fingerprint); + else + i = 0; + + free(msg); + } + else if (channel == 0x0002) + { + struct aim_tlv_t *block1; + struct aim_tlvlist_t *list2; + unsigned short reqclass = 0; + unsigned short status = 0; + + /* + * There's another block of TLVs embedded in the type 5 here. + */ + block1 = aim_gettlv(tlvlist, 0x0005, 1); + if (!block1) { + faimdprintf(sess, 0, "no tlv 0x0005 in rendezvous transaction!\n"); + aim_freetlvchain(&tlvlist); + return 1; /* major problem */ + } + + /* + * First two bytes represent the status of the connection. + * + * 0 is a request, 2 is an accept + */ + status = aimutil_get16(block1->value+0); + + /* + * Next comes the cookie. Should match the ICBM cookie. + */ + if (memcmp(block1->value+2, cookie, 8) != 0) + faimdprintf(sess, 0, "rend: warning cookies don't match!\n"); + + /* + * The next 16bytes are a capability block so we can + * identify what type of rendezvous this is. + * + * Thanks to Eric Warmenhoven <warmenhoven@linux.com> (of GAIM) + * for pointing some of this out to me. In fact, a lot of + * the client-to-client info comes from the work of the GAIM + * developers. Thanks! + * + * Read off one capability string and we should have it ID'd. + * + */ + reqclass = aim_getcap(sess, block1->value+2+8, 0x10); + if (reqclass == 0x0000) { + faimdprintf(sess, 0, "rend: no ID block\n"); + aim_freetlvchain(&tlvlist); + return 1; + } + + /* + * 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_readtlvchain(block1->value+2+8+16, block1->length-2-8-16); + + if (!list2 || ((reqclass != AIM_CAPS_IMIMAGE) && !(aim_gettlv(list2, 0x2711, 1)))) { + struct aim_msgcookie_t *cook; + int type; + + type = aim_msgcookie_gettype(reqclass); /* XXX: fix this shitty code */ + + if ((cook = aim_checkcookie(sess, cookie, type)) == NULL) { + faimdprintf(sess, 0, "non-data rendezvous thats not in cache %d/%s!\n", type, cookie); + aim_freetlvchain(&list2); + aim_freetlvchain(&tlvlist); + return 1; + } + + if (cook->type == AIM_COOKIETYPE_OFTGET) { + struct aim_filetransfer_priv *ft; + + if (cook->data) { + int errorcode = -1; /* XXX shouldnt this be 0? */ + + ft = (struct aim_filetransfer_priv *)cook->data; + + if(status != 0x0002) { + if (aim_gettlv(list2, 0x000b, 1)) + errorcode = aim_gettlv16(list2, 0x000b, 1); + + if (errorcode) + faimdprintf(sess, 0, "transfer from %s (%s) for %s cancelled (error code %d)\n", ft->sn, ft->ip, ft->fh.name, errorcode); + } + } else { + faimdprintf(sess, 0, "no data attached to file transfer\n"); + } + } else if (cook->type == AIM_CAPS_VOICE) { + faimdprintf(sess, 0, "voice request cancelled\n"); + } else { + faimdprintf(sess, 0, "unknown cookie cache type %d\n", cook->type); + } + + if (list2) + aim_freetlvchain(&list2); + aim_freetlvchain(&tlvlist); + return 1; + } + + /* + * The rest of the handling depends on what type it is. + */ + if (reqclass & AIM_CAPS_BUDDYICON) { + + /* + * Call client. + */ +#if 0 + userfunc = aim_callhandler(sess, command->conn, 0x0004, 0x0007); + if (userfunc || (i = 0)) + i = userfunc(sess, + command, + channel, + reqclass, + &userinfo, + ip, + cookie); +#endif + + } else if (reqclass & AIM_CAPS_VOICE) { + struct aim_msgcookie_t *cachedcook; + + faimdprintf(sess, 0, "rend: voice!\n"); + + if(!(cachedcook = (struct aim_msgcookie_t*)calloc(1, sizeof(struct aim_msgcookie_t)))) + return 1; + + memcpy(cachedcook->cookie, cookie, 8); + cachedcook->type = AIM_COOKIETYPE_OFTVOICE; + cachedcook->data = NULL; + + if (aim_cachecookie(sess, cachedcook) == -1) + faimdprintf(sess, 0, "ERROR caching message cookie\n"); + + /* XXX: implement all this */ + + /* + * Call client. + */ + userfunc = aim_callhandler(sess, command->conn, 0x0004, 0x0007); + if (userfunc || (i = 0)) { + i = userfunc(sess, command, channel, reqclass, &userinfo); + } + } else if ((reqclass & AIM_CAPS_IMIMAGE) || (reqclass & AIM_CAPS_BUDDYICON)) { + char ip[30]; + struct aim_directim_priv *priv; + + memset(ip, 0, 30); + + if (aim_gettlv(list2, 0x0003, 1) && aim_gettlv(list2, 0x0005, 1)) { + struct aim_tlv_t *iptlv, *porttlv; + + iptlv = aim_gettlv(list2, 0x0003, 1); + porttlv = aim_gettlv(list2, 0x0005, 1); + + snprintf(ip, 30, "%d.%d.%d.%d:%d", + aimutil_get8(iptlv->value+0), + aimutil_get8(iptlv->value+1), + aimutil_get8(iptlv->value+2), + aimutil_get8(iptlv->value+3), + 4443 /*aimutil_get16(porttlv->value)*/); + } + + faimdprintf(sess, 0, "rend: directIM request from %s (%s)\n", + userinfo.sn, ip); + + /* XXX: there are a couple of different request packets for + * different things */ + + priv = (struct aim_directim_priv *)calloc(1, sizeof(struct aim_directim_priv)); + memcpy(priv->ip, ip, sizeof(priv->ip)); + memcpy(priv->sn, userinfo.sn, sizeof(priv->sn)); + memcpy(priv->cookie, cookie, sizeof(priv->cookie)); + + /* + * Call client. + */ + userfunc = aim_callhandler(sess, command->conn, 0x0004, 0x0007); + if (userfunc || (i = 0)) + i = userfunc(sess, + command, + channel, + reqclass, + &userinfo, priv); + + } else if (reqclass & AIM_CAPS_CHAT) { + struct aim_tlv_t *miscinfo; + struct aim_chat_roominfo roominfo; + char *msg=NULL,*encoding=NULL,*lang=NULL; + + miscinfo = aim_gettlv(list2, 0x2711, 1); + aim_chat_readroominfo(miscinfo->value, &roominfo); + + if (aim_gettlv(list2, 0x000c, 1)) + msg = aim_gettlv_str(list2, 0x000c, 1); + + if (aim_gettlv(list2, 0x000d, 1)) + encoding = aim_gettlv_str(list2, 0x000d, 1); + + if (aim_gettlv(list2, 0x000e, 1)) + lang = aim_gettlv_str(list2, 0x000e, 1); + + /* + * Call client. + */ + userfunc = aim_callhandler(sess, command->conn, 0x0004, 0x0007); + if (userfunc || (i = 0)) + i = userfunc(sess, + command, + channel, + reqclass, + &userinfo, + &roominfo, + msg, + encoding?encoding+1:NULL, + lang?lang+1:NULL); + free(roominfo.name); + free(msg); + free(encoding); + free(lang); + } else if (reqclass & AIM_CAPS_GETFILE) { + char ip[30]; + struct aim_msgcookie_t *cachedcook; + struct aim_tlv_t *miscinfo; + + if (!(cachedcook = calloc(1, sizeof(struct aim_msgcookie_t)))) + return 0; + + memset(ip, 0, 30); + + if (!(miscinfo = aim_gettlv(list2, 0x2711, 1))) { + aim_cookie_free(sess, cachedcook); + return 0; + } + + if (aim_gettlv(list2, 0x0003, 1) && aim_gettlv(list2, 0x0005, 1)) { + struct aim_tlv_t *iptlv, *porttlv; + + if (!(iptlv = aim_gettlv(list2, 0x0003, 1)) || !(porttlv = aim_gettlv(list2, 0x0005, 1))) { + aim_cookie_free(sess, cachedcook); + return 0; + } + + snprintf(ip, 30, "%d.%d.%d.%d:%d", + aimutil_get8(iptlv->value+0), + aimutil_get8(iptlv->value+1), + aimutil_get8(iptlv->value+2), + aimutil_get8(iptlv->value+3), + aimutil_get16(porttlv->value)); + } + + faimdprintf(sess, 0, "rend: file get request from %s (%s)\n", userinfo.sn, ip); + + /* + * Call client. + */ + userfunc = aim_callhandler(sess, command->conn, 0x0004, 0x0007); + if (userfunc || (i = 0)) + i = userfunc(sess, + command, + channel, + reqclass, + &userinfo, + ip, + cookie); + + } else if (reqclass & AIM_CAPS_SENDFILE) { +#if 0 + char ip[30]; + char *desc = NULL; + struct aim_msgcookie_t *cachedcook; + struct aim_filetransfer_priv *ft; + struct aim_tlv_t *miscinfo; + + memset(ip, 0, sizeof(ip)); + + if (!(miscinfo = aim_gettlv(list2, 0x2711, 1))) + return 0; + + if (aim_gettlv(list2, 0x0003, 1) && aim_gettlv(list2, 0x0003, 1)) { + struct aim_tlv_t *iptlv, *porttlv; + + iptlv = aim_gettlv(list2, 0x0003, 1); + porttlv = aim_gettlv(list2, 0x0005, 1); + + snprintf(ip, sizeof(ip)-1, "%d.%d.%d.%d:%d", + aimutil_get8(iptlv->value+0), + aimutil_get8(iptlv->value+1), + aimutil_get8(iptlv->value+2), + aimutil_get8(iptlv->value+3), + aimutil_get16(porttlv->value)); + } + + if (aim_gettlv(list2, 0x000c, 1)) { + desc = aim_gettlv_str(list2, 0x000c, 1); + } + + faimdprintf(sess, 0, "rend: file transfer request from %s for %s: %s (%s)\n", + userinfo.sn, + miscinfo->value+8, + desc, + ip); + + memcpy(cachedcook->cookie, cookie, 8); + + ft = malloc(sizeof(struct aim_filetransfer_priv)); + strncpy(ft->sn, userinfo.sn, sizeof(ft->sn)); + strncpy(ft->ip, ip, sizeof(ft->ip)); + strncpy(ft->fh.name, miscinfo->value+8, sizeof(ft->fh.name)); + cachedcook->type = AIM_COOKIETYPE_OFTSEND; + cachedcook->data = ft; + + if (aim_cachecookie(sess, cachedcook) == -1) + faimdprintf(sess, 0, "ERROR caching message cookie\n"); + + + aim_accepttransfer(sess, command->conn, ft->sn, cookie, AIM_CAPS_SENDFILE); + + if (desc) + free(desc); +#endif + /* + * Call client. + */ + userfunc = aim_callhandler(sess, command->conn, 0x0004, 0x0007); + if (userfunc || (i = 0)) + i = userfunc(sess, + command, + channel, + reqclass, + &userinfo); + } else + faimdprintf(sess, 0, "rend: unknown rendezvous 0x%04x\n", reqclass); + + aim_freetlvchain(&list2); + } + + /* + * Free up the TLV chain. + */ + aim_freetlvchain(&tlvlist); + + + return i; +} + +/* + * 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 unsigned long aim_denytransfer(struct aim_session_t *sess, + struct aim_conn_t *conn, + char *sender, + char *cookie, + unsigned short code) +{ + struct command_tx_struct *newpacket; + int curbyte, i; + + if(!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+8+2+1+strlen(sender)+6))) + return -1; + + newpacket->lock = 1; + + curbyte = aim_putsnac(newpacket->data, 0x0004, 0x000b, 0x0000, sess->snac_nextid); + for (i = 0; i < 8; i++) + curbyte += aimutil_put8(newpacket->data+curbyte, cookie[i]); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002); + curbyte += aimutil_put8(newpacket->data+curbyte, strlen(sender)); + curbyte += aimutil_putstr(newpacket->data+curbyte, sender, strlen(sender)); + curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0003, code); + + newpacket->lock = 0; + aim_tx_enqueue(sess, newpacket); + + return (sess->snac_nextid++); +} + +/* + * Not real sure what this does, nor does anyone I've talk to. + * + * Didn't use to send it. But now I think it might be a good + * idea. + * + */ +faim_export unsigned long aim_seticbmparam(struct aim_session_t *sess, + struct aim_conn_t *conn) +{ + struct command_tx_struct *newpacket; + int curbyte; + + if(!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+16))) + return -1; + + newpacket->lock = 1; + + curbyte = aim_putsnac(newpacket->data, 0x0004, 0x0002, 0x0000, sess->snac_nextid); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); + curbyte += aimutil_put32(newpacket->data+curbyte, 0x00000003); + curbyte += aimutil_put8(newpacket->data+curbyte, 0x1f); + curbyte += aimutil_put8(newpacket->data+curbyte, 0x40); + curbyte += aimutil_put8(newpacket->data+curbyte, 0x03); + curbyte += aimutil_put8(newpacket->data+curbyte, 0xe7); + curbyte += aimutil_put8(newpacket->data+curbyte, 0x03); + curbyte += aimutil_put8(newpacket->data+curbyte, 0xe7); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); + + newpacket->lock = 0; + aim_tx_enqueue(sess, newpacket); + + return (sess->snac_nextid++); +} + +faim_internal int aim_parse_msgerror_middle(struct aim_session_t *sess, + struct command_rx_struct *command) +{ + u_long snacid = 0x000000000; + struct aim_snac_t *snac = NULL; + int ret = 0; + rxcallback_t userfunc = NULL; + char *dest; + unsigned short reason = 0; + + /* + * Get SNAC from packet and look it up + * the list of unrepliedto/outstanding + * SNACs. + * + * After its looked up, the SN that the + * message should've gone to will be + * in the ->data element of the snac struct. + * + */ + snacid = aimutil_get32(command->data+6); + snac = aim_remsnac(sess, snacid); + + if (!snac) { + faimdprintf(sess, 0, "msgerr: got an ICBM-failed error on an unknown SNAC ID! (%08lx)\n", snacid); + dest = NULL; + } else + dest = snac->data; + + reason = aimutil_get16(command->data+10); + + /* + * Call client. + */ + userfunc = aim_callhandler(sess, command->conn, 0x0004, 0x0001); + if (userfunc) + ret = userfunc(sess, command, dest, reason); + else + ret = 0; + + if (snac) { + free(snac->data); + free(snac); + } + + return ret; +} + + +faim_internal int aim_parse_missedcall(struct aim_session_t *sess, + struct command_rx_struct *command) +{ + int i, ret = 1; + rxcallback_t userfunc = NULL; + unsigned short channel, nummissed, reason; + struct aim_userinfo_s userinfo; + + i = 10; /* Skip SNAC header */ + + + /* + * XXX: supposedly, this entire packet can repeat as many times + * as necessary. Should implement that. + */ + + /* + * Channel ID. + */ + channel = aimutil_get16(command->data+i); + i += 2; + + /* + * Extract the standard user info block. + */ + i += aim_extractuserinfo(sess, command->data+i, &userinfo); + + nummissed = aimutil_get16(command->data+i); + i += 2; + + reason = aimutil_get16(command->data+i); + i += 2; + + /* + * Call client. + */ + userfunc = aim_callhandler(sess, command->conn, 0x0004, 0x000a); + if (userfunc) + ret = userfunc(sess, command, channel, &userinfo, nummissed, reason); + else + ret = 0; + + return ret; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfaim/info.c Mon Mar 05 03:59:32 2001 +0000 @@ -0,0 +1,698 @@ +/* + * aim_info.c + * + * The functions here are responsible for requesting and parsing information- + * gathering SNACs. + * + */ + +#define FAIM_INTERNAL +#include <aim.h> + +struct aim_priv_inforeq { + char sn[MAXSNLEN+1]; + unsigned short infotype; +}; + +faim_export unsigned long aim_getinfo(struct aim_session_t *sess, + struct aim_conn_t *conn, + const char *sn, + unsigned short infotype) +{ + struct command_tx_struct *newpacket; + struct aim_priv_inforeq privdata; + int i = 0; + + if (!sess || !conn || !sn) + return 0; + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 12+1+strlen(sn)))) + return -1; + + newpacket->lock = 1; + + i = aim_putsnac(newpacket->data, 0x0002, 0x0005, 0x0000, sess->snac_nextid); + + i += aimutil_put16(newpacket->data+i, infotype); + i += aimutil_put8(newpacket->data+i, strlen(sn)); + i += aimutil_putstr(newpacket->data+i, sn, strlen(sn)); + + newpacket->lock = 0; + aim_tx_enqueue(sess, newpacket); + + strncpy(privdata.sn, sn, sizeof(privdata.sn)); + privdata.infotype = infotype; + aim_cachesnac(sess, 0x0002, 0x0005, 0x0000, &privdata, sizeof(struct aim_priv_inforeq)); + + return sess->snac_nextid; +} + +faim_internal int aim_parse_locateerr(struct aim_session_t *sess, + struct command_rx_struct *command) +{ + u_long snacid = 0x000000000; + struct aim_snac_t *snac = NULL; + int ret = 0; + rxcallback_t userfunc = NULL; + char *dest; + unsigned short reason = 0; + + /* + * Get SNAC from packet and look it up + * the list of unrepliedto/outstanding + * SNACs. + * + */ + snacid = aimutil_get32(command->data+6); + snac = aim_remsnac(sess, snacid); + + if (!snac) { + faimdprintf(sess, 0, "locerr: got an locate-failed error on an unknown SNAC ID! (%08lx)\n", snacid); + dest = NULL; + } else + dest = snac->data; + + reason = aimutil_get16(command->data+10); + + /* + * Call client. + */ + userfunc = aim_callhandler(sess, command->conn, 0x0002, 0x0001); + if (userfunc) + ret = userfunc(sess, command, dest, reason); + else + ret = 0; + + if (snac) { + free(snac->data); + free(snac); + } + + return ret; +} + +/* + * Capability blocks. + */ +u_char aim_caps[8][16] = { + + /* Buddy icon */ + {0x09, 0x46, 0x13, 0x46, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + /* Voice */ + {0x09, 0x46, 0x13, 0x41, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + /* IM image */ + {0x09, 0x46, 0x13, 0x45, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + /* Chat */ + {0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + /* Get file */ + {0x09, 0x46, 0x13, 0x48, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + /* Send file */ + {0x09, 0x46, 0x13, 0x43, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + /* Saves stock portfolios */ + {0x09, 0x46, 0x13, 0x47, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + /* Games */ + {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, +}; + +faim_internal unsigned short aim_getcap(struct aim_session_t *sess, unsigned char *capblock, int buflen) +{ + u_short ret = 0; + int y; + int offset = 0; + int identified; + + while (offset < buflen) { + identified = 0; + for(y=0; y < (sizeof(aim_caps)/0x10); y++) { + if (memcmp(&aim_caps[y], capblock+offset, 0x10) == 0) { + switch(y) { + case 0: ret |= AIM_CAPS_BUDDYICON; identified++; break; + case 1: ret |= AIM_CAPS_VOICE; identified++; break; + case 2: ret |= AIM_CAPS_IMIMAGE; identified++; break; + case 3: ret |= AIM_CAPS_CHAT; identified++; break; + case 4: ret |= AIM_CAPS_GETFILE; identified++; break; + case 5: ret |= AIM_CAPS_SENDFILE; identified++; break; + case 6: ret |= AIM_CAPS_GAMES; identified++; break; + case 7: ret |= AIM_CAPS_SAVESTOCKS; identified++; break; + } + } + } + if (!identified) { + faimdprintf(sess, 0, "unknown capability!\n"); + ret |= 0xff00; + } + + offset += 0x10; + } + return ret; +} + +faim_internal int aim_putcap(unsigned char *capblock, int buflen, u_short caps) +{ + int offset = 0; + + if (!capblock) + return -1; + + if ((caps & AIM_CAPS_BUDDYICON) && (offset < buflen)) { + memcpy(capblock+offset, aim_caps[0], sizeof(aim_caps[0])); + offset += sizeof(aim_caps[1]); + } + if ((caps & AIM_CAPS_VOICE) && (offset < buflen)) { + memcpy(capblock+offset, aim_caps[1], sizeof(aim_caps[1])); + offset += sizeof(aim_caps[1]); + } + if ((caps & AIM_CAPS_IMIMAGE) && (offset < buflen)) { + memcpy(capblock+offset, aim_caps[2], sizeof(aim_caps[2])); + offset += sizeof(aim_caps[2]); + } + if ((caps & AIM_CAPS_CHAT) && (offset < buflen)) { + memcpy(capblock+offset, aim_caps[3], sizeof(aim_caps[3])); + offset += sizeof(aim_caps[3]); + } + if ((caps & AIM_CAPS_GETFILE) && (offset < buflen)) { + memcpy(capblock+offset, aim_caps[4], sizeof(aim_caps[4])); + offset += sizeof(aim_caps[4]); + } + if ((caps & AIM_CAPS_SENDFILE) && (offset < buflen)) { + memcpy(capblock+offset, aim_caps[5], sizeof(aim_caps[5])); + offset += sizeof(aim_caps[5]); + } + if ((caps & AIM_CAPS_GAMES) && (offset < buflen)) { + memcpy(capblock+offset, aim_caps[6], sizeof(aim_caps[6])); + offset += sizeof(aim_caps[6]); + } + if ((caps & AIM_CAPS_SAVESTOCKS) && (offset < buflen)) { + memcpy(capblock+offset, aim_caps[7], sizeof(aim_caps[7])); + offset += sizeof(aim_caps[7]); + } + + return offset; +} + +/* + * AIM is fairly regular about providing user info. This + * is a generic routine to extract it in its standard form. + */ +faim_internal int aim_extractuserinfo(struct aim_session_t *sess, unsigned char *buf, struct aim_userinfo_s *outinfo) +{ + int i = 0; + int tlvcnt = 0; + int curtlv = 0; + int tlv1 = 0; + u_short curtype; + int lastvalid; + + + if (!buf || !outinfo) + return -1; + + /* Clear out old data first */ + memset(outinfo, 0x00, sizeof(struct aim_userinfo_s)); + + /* + * Screen name. Stored as an unterminated string prepended + * with an unsigned byte containing its length. + */ + if (buf[i] < MAXSNLEN) { + memcpy(outinfo->sn, &(buf[i+1]), buf[i]); + outinfo->sn[(int)buf[i]] = '\0'; + } else { + memcpy(outinfo->sn, &(buf[i+1]), MAXSNLEN-1); + outinfo->sn[MAXSNLEN] = '\0'; + } + i = 1 + (int)buf[i]; + + /* + * Warning Level. Stored as an unsigned short. + */ + outinfo->warnlevel = aimutil_get16(&buf[i]); + i += 2; + + /* + * TLV Count. Unsigned short representing the number of + * Type-Length-Value triples that follow. + */ + tlvcnt = aimutil_get16(&buf[i]); + i += 2; + + /* + * Parse out the Type-Length-Value triples as they're found. + */ + while (curtlv < tlvcnt) { + lastvalid = 1; + curtype = aimutil_get16(&buf[i]); + switch (curtype) { + /* + * Type = 0x0000: Invalid + * + * AOL has been trying to throw these in just to break us. + * They're real nice guys over there at AOL. + * + * Just skip the two zero bytes and continue on. (This doesn't + * count towards tlvcnt!) + */ + case 0x0000: + lastvalid = 0; + i += 2; + break; + + /* + * Type = 0x0001: User flags + * + * Specified as any of the following bitwise 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 + * + * In some odd cases, we can end up with more + * than one of these. We only want the first, + * as the others may not be something we want. + * + */ + case 0x0001: + if (tlv1) /* use only the first */ + break; + outinfo->flags = aimutil_get16(&buf[i+4]); + tlv1++; + break; + + /* + * Type = 0x0002: Member-Since date. + * + * The time/date that the user originally + * registered for the service, stored in + * time_t format + */ + case 0x0002: + outinfo->membersince = aimutil_get32(&buf[i+4]); + break; + + /* + * Type = 0x0003: On-Since date. + * + * The time/date that the user started + * their current session, stored in time_t + * format. + */ + case 0x0003: + outinfo->onlinesince = aimutil_get32(&buf[i+4]); + break; + + /* + * Type = 0x0004: Idle time. + * + * Number of seconds since the user + * actively used the service. + */ + case 0x0004: + outinfo->idletime = aimutil_get16(&buf[i+4]); + break; + + /* + * Type = 0x0006: ICQ Online Status + * + * ICQ's Away/DND/etc "enriched" status + * Some decoding of values done by Scott <darkagl@pcnet.com> + */ + case 0x0006: + outinfo->icqinfo.status = aimutil_get16(buf+i+2+2+2); + break; + + + /* + * Type = 0x000a + * + * ICQ User IP Address. + * Ahh, the joy of ICQ security. + */ + case 0x000a: + outinfo->icqinfo.ipaddr = aimutil_get32(&buf[i+4]); + break; + + /* Type = 0x000c + * + * random crap containing the IP address, + * apparently a port number, and some Other Stuff. + * + */ + case 0x000c: + memcpy(outinfo->icqinfo.crap, &buf[i+4], 0x25); + break; + + /* + * Type = 0x000d + * + * Capability information. Not real sure of + * actual decoding. See comment on aim_bos_setprofile() + * in aim_misc.c about the capability block, its the same. + * + */ + case 0x000d: + { + int len; + len = aimutil_get16(buf+i+2); + if (!len) + break; + + outinfo->capabilities = aim_getcap(sess, buf+i+4, len); + } + break; + + /* + * Type = 0x000e + * + * Unknown. Always of zero length, and always only + * on AOL users. + * + * Ignore. + * + */ + case 0x000e: + break; + + /* + * 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). + * + */ + case 0x000f: + case 0x0010: + outinfo->sessionlen = aimutil_get32(&buf[i+4]); + break; + + /* + * 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. + * + */ + default: + { + int len,z = 0, y = 0, x = 0; + char tmpstr[160]; + + faimdprintf(sess, 0, "userinfo: **warning: unexpected TLV:\n"); + faimdprintf(sess, 0, "userinfo: sn =%s\n", outinfo->sn); + faimdprintf(sess, 0, "userinfo: curtlv=0x%04x\n", curtlv); + faimdprintf(sess, 0, "userinfo: type =0x%04x\n",aimutil_get16(&buf[i])); + faimdprintf(sess, 0, "userinfo: length=0x%04x\n", len = aimutil_get16(&buf[i+2])); + faimdprintf(sess, 0, "userinfo: data: \n"); + while (z<len) + { + x = snprintf(tmpstr, sizeof(tmpstr), "userinfo: "); + for (y = 0; y < 8; y++) + { + if (z<len) + { + snprintf(tmpstr+x, sizeof(tmpstr)-x, "%02x ", buf[i+4+z]); + z++; + x += 3; + } + else + break; + } + faimdprintf(sess, 0, "%s\n", tmpstr); + } + } + break; + } + /* + * No matter what, TLV triplets should always look like this: + * + * u_short type; + * u_short length; + * u_char data[length]; + * + */ + if (lastvalid) { + i += (2 + 2 + aimutil_get16(&buf[i+2])); + curtlv++; + } + } + + return i; +} + +/* + * Oncoming Buddy notifications contain a subset of the + * user information structure. Its close enough to run + * through aim_extractuserinfo() however. + * + */ +faim_internal int aim_parse_oncoming_middle(struct aim_session_t *sess, + struct command_rx_struct *command) +{ + struct aim_userinfo_s userinfo; + u_int i = 0; + rxcallback_t userfunc=NULL; + + i = 10; + i += aim_extractuserinfo(sess, command->data+i, &userinfo); + + userfunc = aim_callhandler(sess, command->conn, AIM_CB_FAM_BUD, AIM_CB_BUD_ONCOMING); + if (userfunc) + i = userfunc(sess, command, &userinfo); + + return 1; +} + +/* + * Offgoing Buddy notifications contain no useful + * information other than the name it applies to. + * + */ +faim_internal int aim_parse_offgoing_middle(struct aim_session_t *sess, + struct command_rx_struct *command) +{ + char sn[MAXSNLEN+1]; + u_int i = 0; + rxcallback_t userfunc=NULL; + + strncpy(sn, (char *)command->data+11, (int)command->data[10]); + sn[(int)command->data[10]] = '\0'; + + userfunc = aim_callhandler(sess, command->conn, AIM_CB_FAM_BUD, AIM_CB_BUD_OFFGOING); + if (userfunc) + i = userfunc(sess, command, sn); + + return 1; +} + +/* + * This parses the user info stuff out all nice and pretty then calls + * the higher-level callback (in the user app). + * + */ +faim_internal int aim_parse_userinfo_middle(struct aim_session_t *sess, + struct command_rx_struct *command) +{ + struct aim_userinfo_s userinfo; + char *text_encoding = NULL; + char *text = NULL; + u_int i = 0; + rxcallback_t userfunc=NULL; + struct aim_tlvlist_t *tlvlist; + struct aim_snac_t *origsnac = NULL; + u_long snacid; + struct aim_priv_inforeq *inforeq; + + snacid = aimutil_get32(&command->data[6]); + origsnac = aim_remsnac(sess, snacid); + + if (!origsnac || !origsnac->data) { + faimdprintf(sess, 0, "parse_userinfo_middle: major problem: no snac stored!\n"); + return 1; + } + + inforeq = (struct aim_priv_inforeq *)origsnac->data; + + switch (inforeq->infotype) { + case AIM_GETINFO_GENERALINFO: + case AIM_GETINFO_AWAYMESSAGE: + i = 10; + + /* + * extractuserinfo will give us the basic metaTLV information + */ + i += aim_extractuserinfo(sess, command->data+i, &userinfo); + + /* + * However, in this command, there's usually more TLVs following... + */ + tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i); + + /* + * Depending on what informational text was requested, different + * TLVs will appear here. + * + * Profile will be 1 and 2, away message will be 3 and 4. + */ + if (aim_gettlv(tlvlist, 0x0001, 1)) { + text_encoding = aim_gettlv_str(tlvlist, 0x0001, 1); + text = aim_gettlv_str(tlvlist, 0x0002, 1); + } else if (aim_gettlv(tlvlist, 0x0003, 1)) { + text_encoding = aim_gettlv_str(tlvlist, 0x0003, 1); + text = aim_gettlv_str(tlvlist, 0x0004, 1); + } + + userfunc = aim_callhandler(sess, command->conn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO); + if (userfunc) { + i = userfunc(sess, + command, + &userinfo, + text_encoding, + text, + inforeq->infotype); + } + + free(text_encoding); + free(text); + aim_freetlvchain(&tlvlist); + break; + default: + faimdprintf(sess, 0, "parse_userinfo_middle: unknown infotype in request! (0x%04x)\n", inforeq->infotype); + break; + } + + if (origsnac) { + if (origsnac->data) + free(origsnac->data); + free(origsnac); + } + + return 1; +} + +/* + * Inverse of aim_extractuserinfo() + */ +faim_internal int aim_putuserinfo(u_char *buf, int buflen, struct aim_userinfo_s *info) +{ + int i = 0, numtlv = 0; + struct aim_tlvlist_t *tlvlist = NULL; + + if (!buf || !info) + return 0; + + i += aimutil_put8(buf+i, strlen(info->sn)); + i += aimutil_putstr(buf+i, info->sn, strlen(info->sn)); + + i += aimutil_put16(buf+i, info->warnlevel); + + + aim_addtlvtochain16(&tlvlist, 0x0001, info->flags); + numtlv++; + + aim_addtlvtochain32(&tlvlist, 0x0002, info->membersince); + numtlv++; + + aim_addtlvtochain32(&tlvlist, 0x0003, info->onlinesince); + numtlv++; + + aim_addtlvtochain16(&tlvlist, 0x0004, info->idletime); + numtlv++; + +#if ICQ_OSCAR_SUPPORT + if(atoi(info->sn) != 0) { + aim_addtlvtochain16(&tlvlist, 0x0006, info->icqinfo.status); + aim_addtlvtochain32(&tlvlist, 0x000a, info->icqinfo.ipaddr); + } +#endif + + aim_addtlvtochain_caps(&tlvlist, 0x000d, info->capabilities); + numtlv++; + + aim_addtlvtochain32(&tlvlist, (unsigned short)((info->flags)&AIM_FLAG_AOL?0x0010:0x000f), info->sessionlen); + numtlv++; + + i += aimutil_put16(buf+i, numtlv); /* tlvcount */ + i += aim_writetlvchain(buf+i, buflen-i, &tlvlist); /* tlvs */ + aim_freetlvchain(&tlvlist); + + return i; +} + +faim_export int aim_sendbuddyoncoming(struct aim_session_t *sess, struct aim_conn_t *conn, struct aim_userinfo_s *info) +{ + struct command_tx_struct *tx; + int i = 0; + + if (!sess || !conn || !info) + return 0; + + if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 1152))) + return -1; + + tx->lock = 1; + + i += aimutil_put16(tx->data+i, 0x0003); + i += aimutil_put16(tx->data+i, 0x000b); + i += aimutil_put16(tx->data+i, 0x0000); + i += aimutil_put16(tx->data+i, 0x0000); + i += aimutil_put16(tx->data+i, 0x0000); + + i += aim_putuserinfo(tx->data+i, tx->commandlen-i, info); + + tx->commandlen = i; + tx->lock = 0; + aim_tx_enqueue(sess, tx); + + return 0; +} + +faim_export int aim_sendbuddyoffgoing(struct aim_session_t *sess, struct aim_conn_t *conn, char *sn) +{ + struct command_tx_struct *tx; + int i = 0; + + if (!sess || !conn || !sn) + return 0; + + if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+1+strlen(sn)))) + return -1; + + tx->lock = 1; + + i += aimutil_put16(tx->data+i, 0x0003); + i += aimutil_put16(tx->data+i, 0x000c); + i += aimutil_put16(tx->data+i, 0x0000); + i += aimutil_put16(tx->data+i, 0x0000); + i += aimutil_put16(tx->data+i, 0x0000); + + i += aimutil_put8(tx->data+i, strlen(sn)); + i += aimutil_putstr(tx->data+i, sn, strlen(sn)); + + tx->lock = 0; + aim_tx_enqueue(sess, tx); + + return 0; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfaim/libfaim_config.h Mon Mar 05 03:59:32 2001 +0000 @@ -0,0 +1,81 @@ +/* include/libfaim_config.h. Generated automatically by configure. */ +/* include/libfaim_config.h.in. Generated automatically from configure.in by autoheader. */ + +/* Define to empty if the keyword does not work. */ +/* #undef const */ + +/* Define if you don't have vprintf but do have _doprnt. */ +/* #undef HAVE_DOPRNT */ + +/* Define if you have the strftime function. */ +#define HAVE_STRFTIME 1 + +/* Define if you have <sys/wait.h> that is POSIX.1 compatible. */ +#define HAVE_SYS_WAIT_H 1 + +/* Define if you have the vprintf function. */ +#define HAVE_VPRINTF 1 + +/* Define as __inline if that's what the C compiler calls it. */ +/* #undef inline */ + +/* Define to `int' if <sys/types.h> doesn't define. */ +/* #undef pid_t */ + +/* Define as the return type of signal handlers (int or void). */ +#define RETSIGTYPE void + +/* Define to `unsigned' if <sys/types.h> doesn't define. */ +/* #undef size_t */ + +/* Define if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define if you can safely include both <sys/time.h> and <time.h>. */ +#define TIME_WITH_SYS_TIME 1 + +/* Define if your <sys/time.h> declares struct tm. */ +/* #undef TM_IN_SYS_TIME */ + +/* Define if you have the gethostname function. */ +#define HAVE_GETHOSTNAME 1 + +/* Define if you have the gettimeofday function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define if you have the select function. */ +#define HAVE_SELECT 1 + +/* Define if you have the socket function. */ +#define HAVE_SOCKET 1 + +/* Define if you have the strdup function. */ +#define HAVE_STRDUP 1 + +/* Define if you have the strstr function. */ +#define HAVE_STRSTR 1 + +/* Define if you have the strtol function. */ +#define HAVE_STRTOL 1 + +/* Define if you have the uname function. */ +#define HAVE_UNAME 1 + +/* Define if you have the <fcntl.h> header file. */ +#define HAVE_FCNTL_H 1 + +/* Define if you have the <sys/time.h> header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Define if you have the faim library (-lfaim). */ +/* #undef HAVE_LIBFAIM */ + +/* Name of package */ +#define PACKAGE "libfaim" + +/* Version number of package */ +#define VERSION "0.99.1" +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfaim/login.c Mon Mar 05 03:59:32 2001 +0000 @@ -0,0 +1,573 @@ +/* + * aim_login.c + * + * This contains all the functions needed to actually login. + * + */ + +#define FAIM_INTERNAL +#include <aim.h> + +#include "md5.h" + +static int aim_encode_password_md5(const char *password, const char *key, md5_byte_t *digest); +static int aim_encode_password(const char *password, unsigned char *encoded); + +faim_export int aim_sendconnack(struct aim_session_t *sess, + struct aim_conn_t *conn) +{ + int curbyte=0; + + struct command_tx_struct *newpacket; + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0001, 4))) + return -1; + + newpacket->lock = 1; + + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001); + + newpacket->lock = 0; + return aim_tx_enqueue(sess, newpacket); +} + +/* + * 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(struct aim_session_t *sess, + struct aim_conn_t *conn, + char *sn) +{ + int curbyte; + struct command_tx_struct *newpacket; + + if (!sess || !conn || !sn) + return -1; + + /* + * For ICQ, we enable the ancient horrible login and stuff + * a key packet into the queue to make it look like we got + * a reply back. This is so the client doesn't know we're + * really not doing MD5 login. + * + * This may sound stupid, but I'm not in the best of moods and + * I don't plan to keep support for this crap around much longer. + * Its all AOL's fault anyway, really. I hate AOL. Really. They + * always seem to be able to piss me off by doing the dumbest little + * things. Like disabling MD5 logins for ICQ UINs, or adding purposefully + * wrong TLV lengths, or adding superfluous information to host strings, + * or... I'll stop. + * + */ + if ((sn[0] >= '0') && (sn[0] <= '9')) { + struct command_rx_struct *newrx; + int i; + + if (!(newrx = (struct command_rx_struct *)malloc(sizeof(struct command_rx_struct)))) + return -1; + memset(newrx, 0x00, sizeof(struct command_rx_struct)); + newrx->lock = 1; + newrx->hdrtype = AIM_FRAMETYPE_OSCAR; + newrx->hdr.oscar.type = 0x02; + newrx->hdr.oscar.seqnum = 0; + newrx->commandlen = 10+2+1; + newrx->nofree = 0; + if (!(newrx->data = malloc(newrx->commandlen))) { + free(newrx); + return -1; + } + + i = aim_putsnac(newrx->data, 0x0017, 0x0007, 0x0000, 0x0000); + i += aimutil_put16(newrx->data+i, 0x01); + i += aimutil_putstr(newrx->data+i, "0", 1); + + newrx->conn = conn; + + newrx->next = sess->queue_incoming; + sess->queue_incoming = newrx; + + newrx->lock = 0; + + sess->flags &= ~AIM_SESS_FLAGS_SNACLOGIN; + + return 0; + } + + sess->flags |= AIM_SESS_FLAGS_SNACLOGIN; + + aim_sendconnack(sess, conn); + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+2+2+strlen(sn)))) + return -1; + + newpacket->lock = 1; + + curbyte = aim_putsnac(newpacket->data, 0x0017, 0x0006, 0x0000, 0x00010000); + curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x0001, strlen(sn), sn); + + newpacket->commandlen = curbyte; + newpacket->lock = 0; + + return aim_tx_enqueue(sess, newpacket); +} + +/* + * send_login(int socket, char *sn, char *password) + * + * This is the initial login request packet. + * + * The password is encoded before transmition, as per + * encode_password(). See that function for their + * stupid method of doing it. + * + * Latest WinAIM: + * clientstring = "AOL Instant Messenger (SM), version 4.3.2188/WIN32" + * major2 = 0x0109 + * major = 0x0400 + * minor = 0x0003 + * minor2 = 0x0000 + * build = 0x088c + * unknown = 0x00000086 + * lang = "en" + * country = "us" + * unknown4a = 0x01 + */ +faim_export int aim_send_login (struct aim_session_t *sess, + struct aim_conn_t *conn, + char *sn, char *password, + struct client_info_s *clientinfo, + char *key) +{ + int curbyte=0; + struct command_tx_struct *newpacket; + + if (!clientinfo || !sn || !password) + return -1; + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 1152))) + return -1; + + newpacket->lock = 1; + + newpacket->hdr.oscar.type = (sess->flags & AIM_SESS_FLAGS_SNACLOGIN)?0x02:0x01; + + if (sess->flags & AIM_SESS_FLAGS_SNACLOGIN) + curbyte = aim_putsnac(newpacket->data, 0x0017, 0x0002, 0x0000, 0x00010000); + else { + curbyte = aimutil_put16(newpacket->data, 0x0000); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001); + } + + curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x0001, strlen(sn), sn); + + if (sess->flags & AIM_SESS_FLAGS_SNACLOGIN) { + md5_byte_t digest[16]; + + aim_encode_password_md5(password, key, digest); + curbyte+= aim_puttlv_str(newpacket->data+curbyte, 0x0025, 16, (char *)digest); + } else { + char *password_encoded; + + password_encoded = (char *) malloc(strlen(password)); + aim_encode_password(password, password_encoded); + curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x0002, strlen(password), password_encoded); + free(password_encoded); + } + + /* XXX is clientstring required by oscar? */ + if (strlen(clientinfo->clientstring)) + curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x0003, strlen(clientinfo->clientstring), clientinfo->clientstring); + + if (sess->flags & AIM_SESS_FLAGS_SNACLOGIN) { + curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0016, (unsigned short)clientinfo->major2); + curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0017, (unsigned short)clientinfo->major); + curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0018, (unsigned short)clientinfo->minor); + curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0019, (unsigned short)clientinfo->minor2); + curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x001a, (unsigned short)clientinfo->build); + + curbyte += aim_puttlv_32(newpacket->data+curbyte, 0x0014, clientinfo->unknown); + curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0009, 0x0015); + curbyte += aim_puttlv_8(newpacket->data+curbyte, 0x004a, 0x00); + } else { + /* Use very specific version numbers, to further indicate the hack. */ + curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0016, 0x010a); + curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0017, 0x0004); + curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0018, 0x003c); + curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0019, 0x0001); + curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x001a, 0x0cce); + curbyte += aim_puttlv_32(newpacket->data+curbyte, 0x0014, 0x00000055); + } + + if (strlen(clientinfo->country)) + curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x000e, strlen(clientinfo->country), clientinfo->country); + else + curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x000e, 2, "us"); + + if (strlen(clientinfo->lang)) + curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x000f, strlen(clientinfo->lang), clientinfo->lang); + else + curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x000f, 2, "en"); + + newpacket->commandlen = curbyte; + + newpacket->lock = 0; + return aim_tx_enqueue(sess, newpacket); +} + +static int aim_encode_password_md5(const char *password, const char *key, md5_byte_t *digest) +{ + md5_state_t state; + + md5_init(&state); + md5_append(&state, (const md5_byte_t *)key, strlen(key)); + md5_append(&state, (const md5_byte_t *)password, strlen(password)); + md5_append(&state, (const md5_byte_t *)AIM_MD5_STRING, strlen(AIM_MD5_STRING)); + md5_finish(&state, (md5_byte_t *)digest); + + return 0; +} + +/** + * aim_encode_password - Encode a password using old XOR method + * @password: incoming password + * @encoded: buffer to put encoded password + * + * This takes a const pointer to a (null terminated) string + * containing the unencoded password. It also gets passed + * an already allocated buffer to store the encoded password. + * This buffer should be the exact length of the password without + * the null. The encoded password buffer /is not %NULL terminated/. + * + * The encoding_table seems to be a fixed set of values. We'll + * hope it doesn't change over time! + * + * This is only used for the XOR method, not the better MD5 method. + * + */ +static int aim_encode_password(const char *password, unsigned char *encoded) +{ + u_char encoding_table[] = { +#if 0 /* old v1 table */ + 0xf3, 0xb3, 0x6c, 0x99, + 0x95, 0x3f, 0xac, 0xb6, + 0xc5, 0xfa, 0x6b, 0x63, + 0x69, 0x6c, 0xc3, 0x9f +#else /* v2.1 table, also works for ICQ */ + 0xf3, 0x26, 0x81, 0xc4, + 0x39, 0x86, 0xdb, 0x92, + 0x71, 0xa3, 0xb9, 0xe6, + 0x53, 0x7a, 0x95, 0x7c +#endif + }; + + int i; + + for (i = 0; i < strlen(password); i++) + encoded[i] = (password[i] ^ encoding_table[i]); + + return 0; +} + +/* + * This is sent back as a general response to the login command. + * It can be either an error or a success, depending on the + * precense of certain TLVs. + * + * The client should check the value passed as errorcode. If + * its nonzero, there was an error. + * + */ +faim_internal int aim_authparse(struct aim_session_t *sess, + struct command_rx_struct *command) +{ + struct aim_tlvlist_t *tlvlist; + int ret = 1; + rxcallback_t userfunc = NULL; + char *sn = NULL, *bosip = NULL, *errurl = NULL, *email = NULL; + unsigned char *cookie = NULL; + int errorcode = 0, regstatus = 0; + int latestbuild = 0, latestbetabuild = 0; + char *latestrelease = NULL, *latestbeta = NULL; + char *latestreleaseurl = NULL, *latestbetaurl = NULL; + char *latestreleaseinfo = NULL, *latestbetainfo = NULL; + + /* + * Read block of TLVs. All further data is derived + * from what is parsed here. + * + * For SNAC login, there's a 17/3 SNAC header in front. + * + */ + if (sess->flags & AIM_SESS_FLAGS_SNACLOGIN) + tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10); + else + tlvlist = aim_readtlvchain(command->data, command->commandlen); + + /* + * No matter what, we should have a screen name. + */ + memset(sess->sn, 0, sizeof(sess->sn)); + if (aim_gettlv(tlvlist, 0x0001, 1)) { + sn = aim_gettlv_str(tlvlist, 0x0001, 1); + strncpy(sess->sn, sn, sizeof(sess->sn)); + } + + /* + * Check for an error code. If so, we should also + * have an error url. + */ + if (aim_gettlv(tlvlist, 0x0008, 1)) + errorcode = aim_gettlv16(tlvlist, 0x0008, 1); + if (aim_gettlv(tlvlist, 0x0004, 1)) + errurl = aim_gettlv_str(tlvlist, 0x0004, 1); + + /* + * BOS server address. + */ + if (aim_gettlv(tlvlist, 0x0005, 1)) + bosip = aim_gettlv_str(tlvlist, 0x0005, 1); + + /* + * Authorization cookie. + */ + if (aim_gettlv(tlvlist, 0x0006, 1)) { + struct aim_tlv_t *tmptlv; + + tmptlv = aim_gettlv(tlvlist, 0x0006, 1); + + if ((cookie = malloc(tmptlv->length))) + memcpy(cookie, tmptlv->value, tmptlv->length); + } + + /* + * The email address attached to this account + * Not available for ICQ logins. + */ + if (aim_gettlv(tlvlist, 0x0011, 1)) + email = aim_gettlv_str(tlvlist, 0x0011, 1); + + /* + * The registration status. (Not real sure what it means.) + * Not available for ICQ 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. + * + */ + if (aim_gettlv(tlvlist, 0x0013, 1)) + regstatus = aim_gettlv16(tlvlist, 0x0013, 1); + + if (aim_gettlv(tlvlist, 0x0040, 1)) + latestbetabuild = aim_gettlv32(tlvlist, 0x0040, 1); + if (aim_gettlv(tlvlist, 0x0041, 1)) + latestbetaurl = aim_gettlv_str(tlvlist, 0x0041, 1); + if (aim_gettlv(tlvlist, 0x0042, 1)) + latestbetainfo = aim_gettlv_str(tlvlist, 0x0042, 1); + if (aim_gettlv(tlvlist, 0x0043, 1)) + latestbeta = aim_gettlv_str(tlvlist, 0x0043, 1); + if (aim_gettlv(tlvlist, 0x0048, 1)) + ; /* no idea what this is */ + + if (aim_gettlv(tlvlist, 0x0044, 1)) + latestbuild = aim_gettlv32(tlvlist, 0x0044, 1); + if (aim_gettlv(tlvlist, 0x0045, 1)) + latestreleaseurl = aim_gettlv_str(tlvlist, 0x0045, 1); + if (aim_gettlv(tlvlist, 0x0046, 1)) + latestreleaseinfo = aim_gettlv_str(tlvlist, 0x0046, 1); + if (aim_gettlv(tlvlist, 0x0047, 1)) + latestrelease = aim_gettlv_str(tlvlist, 0x0047, 1); + if (aim_gettlv(tlvlist, 0x0049, 1)) + ; /* no idea what this is */ + + + if ((userfunc = aim_callhandler(sess, command->conn, 0x0017, 0x0003))) + ret = userfunc(sess, command, sn, errorcode, errurl, regstatus, email, bosip, cookie, latestrelease, latestbuild, latestreleaseurl, latestreleaseinfo, latestbeta, latestbetabuild, latestbetaurl, latestbetainfo); + + + if (sn) + free(sn); + if (bosip) + free(bosip); + if (errurl) + free(errurl); + if (email) + free(email); + if (cookie) + free(cookie); + if (latestrelease) + free(latestrelease); + if (latestreleaseurl) + free(latestreleaseurl); + if (latestbeta) + free(latestbeta); + if (latestbetaurl) + free(latestbetaurl); + if (latestreleaseinfo) + free(latestreleaseinfo); + if (latestbetainfo) + free(latestbetainfo); + + aim_freetlvchain(&tlvlist); + + return ret; +} + +/* + * 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. + * + */ +faim_internal int aim_authkeyparse(struct aim_session_t *sess, struct command_rx_struct *command) +{ + unsigned char *key; + int keylen; + int ret = 1; + rxcallback_t userfunc; + + keylen = aimutil_get16(command->data+10); + if (!(key = malloc(keylen+1))) + return ret; + memcpy(key, command->data+12, keylen); + key[keylen] = '\0'; + + if ((userfunc = aim_callhandler(sess, command->conn, 0x0017, 0x0007))) + ret = userfunc(sess, command, (char *)key); + + free(key); + + return ret; +} + +/* + * Generate an authorization response. + * + * You probably don't want this unless you're writing an AIM server. + * + */ +faim_export unsigned long aim_sendauthresp(struct aim_session_t *sess, + struct aim_conn_t *conn, + char *sn, int errorcode, + char *errorurl, char *bosip, + char *cookie, char *email, + int regstatus) +{ + struct command_tx_struct *tx; + struct aim_tlvlist_t *tlvlist = NULL; + + if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0004, 1152))) + return -1; + + tx->lock = 1; + + if (sn) + aim_addtlvtochain_str(&tlvlist, 0x0001, sn, strlen(sn)); + else + aim_addtlvtochain_str(&tlvlist, 0x0001, sess->sn, strlen(sess->sn)); + + if (errorcode) { + aim_addtlvtochain16(&tlvlist, 0x0008, errorcode); + aim_addtlvtochain_str(&tlvlist, 0x0004, errorurl, strlen(errorurl)); + } else { + aim_addtlvtochain_str(&tlvlist, 0x0005, bosip, strlen(bosip)); + aim_addtlvtochain_str(&tlvlist, 0x0006, cookie, AIM_COOKIELEN); + aim_addtlvtochain_str(&tlvlist, 0x0011, email, strlen(email)); + aim_addtlvtochain16(&tlvlist, 0x0013, (unsigned short)regstatus); + } + + tx->commandlen = aim_writetlvchain(tx->data, tx->commandlen, &tlvlist); + tx->lock = 0; + + return aim_tx_enqueue(sess, tx); +} + +/* + * Generate a random cookie. (Non-client use only) + */ +faim_export int aim_gencookie(unsigned char *buf) +{ + int i; + + srand(time(NULL)); + + for (i=0; i < AIM_COOKIELEN; i++) + buf[i] = 1+(int) (256.0*rand()/(RAND_MAX+0.0)); + + return i; +} + +/* + * Send Server Ready. (Non-client) + */ +faim_export int aim_sendserverready(struct aim_session_t *sess, struct aim_conn_t *conn) +{ + struct command_tx_struct *tx; + int i = 0; + + if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+0x22))) + return -1; + + tx->lock = 1; + + i += aim_putsnac(tx->data, 0x0001, 0x0003, 0x0000, sess->snac_nextid++); + + i += aimutil_put16(tx->data+i, 0x0001); + i += aimutil_put16(tx->data+i, 0x0002); + i += aimutil_put16(tx->data+i, 0x0003); + i += aimutil_put16(tx->data+i, 0x0004); + i += aimutil_put16(tx->data+i, 0x0006); + i += aimutil_put16(tx->data+i, 0x0008); + i += aimutil_put16(tx->data+i, 0x0009); + i += aimutil_put16(tx->data+i, 0x000a); + i += aimutil_put16(tx->data+i, 0x000b); + i += aimutil_put16(tx->data+i, 0x000c); + i += aimutil_put16(tx->data+i, 0x0013); + i += aimutil_put16(tx->data+i, 0x0015); + + tx->commandlen = i; + tx->lock = 0; + return aim_tx_enqueue(sess, tx); +} + + +/* + * Send service redirect. (Non-Client) + */ +faim_export unsigned long aim_sendredirect(struct aim_session_t *sess, + struct aim_conn_t *conn, + unsigned short servid, + char *ip, + char *cookie) +{ + struct command_tx_struct *tx; + struct aim_tlvlist_t *tlvlist = NULL; + int i = 0; + + if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 1152))) + return -1; + + tx->lock = 1; + + i += aim_putsnac(tx->data+i, 0x0001, 0x0005, 0x0000, 0x00000000); + + aim_addtlvtochain16(&tlvlist, 0x000d, servid); + aim_addtlvtochain_str(&tlvlist, 0x0005, ip, strlen(ip)); + aim_addtlvtochain_str(&tlvlist, 0x0006, cookie, AIM_COOKIELEN); + + tx->commandlen = aim_writetlvchain(tx->data+i, tx->commandlen-i, &tlvlist)+i; + aim_freetlvchain(&tlvlist); + + tx->lock = 0; + return aim_tx_enqueue(sess, tx); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfaim/meta.c Mon Mar 05 03:59:32 2001 +0000 @@ -0,0 +1,48 @@ +/* + * Administrative things for libfaim. + * + * + */ + +#include <aim.h> + +faim_export char *aim_getbuilddate(void) +{ + return AIM_BUILDDATE; +} + +faim_export char *aim_getbuildtime(void) +{ + return AIM_BUILDTIME; +} + +faim_export char *aim_getbuildstring(void) +{ + static char string[100]; + + snprintf(string, 99, "%d.%d.%d-%s%s", + FAIM_VERSION_MAJOR, + FAIM_VERSION_MINOR, + FAIM_VERSION_MINORMINOR, + aim_getbuilddate(), + aim_getbuildtime()); + return string; +} + +faim_internal void faimdprintf(struct aim_session_t *sess, int dlevel, const char *format, ...) +{ + if (!sess) { + fprintf(stderr, "faimdprintf: no session! boo! (%d, %s)\n", dlevel, format); + return; + } + + if ((dlevel <= sess->debug) && sess->debugcb) { + va_list ap; + + va_start(ap, format); + sess->debugcb(sess, dlevel, format, ap); + va_end(ap); + } + + return; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfaim/misc.c Mon Mar 05 03:59:32 2001 +0000 @@ -0,0 +1,872 @@ + +/* + * aim_misc.c + * + * TODO: Seperate a lot of this into an aim_bos.c. + * + * Other things... + * + * - Idle setting + * + * + */ + +#define FAIM_INTERNAL +#include <aim.h> + +/* + * aim_bos_setidle() + * + * Should set your current idle time in seconds. Idealy, OSCAR should + * do this for us. But, it doesn't. The client must call this to set idle + * time. + * + */ +faim_export unsigned long aim_bos_setidle(struct aim_session_t *sess, + struct aim_conn_t *conn, + u_long idletime) +{ + return aim_genericreq_l(sess, conn, 0x0001, 0x0011, &idletime); +} + + +/* + * aim_bos_changevisibility(conn, changtype, namelist) + * + * Changes your visibility depending on changetype: + * + * AIM_VISIBILITYCHANGE_PERMITADD: Lets provided list of names see you + * AIM_VISIBILITYCHANGE_PERMIDREMOVE: Removes listed names from permit list + * AIM_VISIBILITYCHANGE_DENYADD: Hides you from provided list of names + * AIM_VISIBILITYCHANGE_DENYREMOVE: Lets list see you again + * + * list should be a list of + * screen names in the form "Screen Name One&ScreenNameTwo&" etc. + * + * Equivelents to options in WinAIM: + * - Allow all users to contact me: Send an AIM_VISIBILITYCHANGE_DENYADD + * with only your name on it. + * - Allow only users on my Buddy List: Send an + * AIM_VISIBILITYCHANGE_PERMITADD with the list the same as your + * buddy list + * - Allow only the uesrs below: Send an AIM_VISIBILITYCHANGE_PERMITADD + * with everyone listed that you want to see you. + * - Block all users: Send an AIM_VISIBILITYCHANGE_PERMITADD with only + * yourself in the list + * - Block the users below: Send an AIM_VISIBILITYCHANGE_DENYADD with + * the list of users to be blocked + * + * + */ +faim_export unsigned long aim_bos_changevisibility(struct aim_session_t *sess, + struct aim_conn_t *conn, + int changetype, + char *denylist) +{ + struct command_tx_struct *newpacket; + int packlen = 0; + u_short subtype; + + char *localcpy = NULL; + char *tmpptr = NULL; + int i,j; + int listcount; + + if (!denylist) + return 0; + + localcpy = (char *) malloc(strlen(denylist)+1); + memcpy(localcpy, denylist, strlen(denylist)+1); + + listcount = aimutil_itemcnt(localcpy, '&'); + packlen = aimutil_tokslen(localcpy, 99, '&') + listcount + 9; + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, packlen))) + return -1; + + newpacket->lock = 1; + + switch(changetype) + { + case AIM_VISIBILITYCHANGE_PERMITADD: subtype = 0x05; break; + case AIM_VISIBILITYCHANGE_PERMITREMOVE: subtype = 0x06; break; + case AIM_VISIBILITYCHANGE_DENYADD: subtype = 0x07; break; + case AIM_VISIBILITYCHANGE_DENYREMOVE: subtype = 0x08; break; + default: + free(newpacket->data); + free(newpacket); + return 0; + } + + /* We actually DO NOT send a SNAC ID with this one! */ + aim_putsnac(newpacket->data, 0x0009, subtype, 0x00, 0); + + j = 10; /* the next byte */ + + for (i=0; (i < (listcount - 1)) && (i < 99); i++) + { + tmpptr = aimutil_itemidx(localcpy, i, '&'); + + newpacket->data[j] = strlen(tmpptr); + memcpy(&(newpacket->data[j+1]), tmpptr, strlen(tmpptr)); + j += strlen(tmpptr)+1; + free(tmpptr); + } + free(localcpy); + + newpacket->lock = 0; + + aim_tx_enqueue(sess, newpacket); + + return (sess->snac_nextid); /* dont increment */ + +} + + + +/* + * aim_bos_setbuddylist(buddylist) + * + * This just builds the "set buddy list" command then queues it. + * + * buddy_list = "Screen Name One&ScreenNameTwo&"; + * + * TODO: Clean this up. + * + * XXX: I can't stress the TODO enough. + * + */ +faim_export unsigned long aim_bos_setbuddylist(struct aim_session_t *sess, + struct aim_conn_t *conn, + char *buddy_list) +{ + int i, j; + + struct command_tx_struct *newpacket; + + int len = 0; + + char *localcpy = NULL; + char *tmpptr = NULL; + + len = 10; /* 10B SNAC headers */ + + if (!buddy_list || !(localcpy = (char *) malloc(strlen(buddy_list)+1))) + return -1; + strncpy(localcpy, buddy_list, strlen(buddy_list)+1); + + i = 0; + tmpptr = strtok(localcpy, "&"); + while ((tmpptr != NULL) && (i < 150)) { + faimdprintf(sess, 2, "---adding %d: %s (%d)\n", i, tmpptr, strlen(tmpptr)); + len += 1+strlen(tmpptr); + i++; + tmpptr = strtok(NULL, "&"); + } + faimdprintf(sess, 2, "*** send buddy list len: %d (%x)\n", len, len); + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, len))) + return -1; + + newpacket->lock = 1; + + aim_putsnac(newpacket->data, 0x0003, 0x0004, 0x0000, 0); + + j = 10; /* the next byte */ + + strncpy(localcpy, buddy_list, strlen(buddy_list)+1); + i = 0; + tmpptr = strtok(localcpy, "&"); + while ((tmpptr != NULL) & (i < 150)) { + faimdprintf(sess, 2, "---adding %d: %s (%d)\n", i, tmpptr, strlen(tmpptr)); + newpacket->data[j] = strlen(tmpptr); + memcpy(&(newpacket->data[j+1]), tmpptr, strlen(tmpptr)); + j += 1+strlen(tmpptr); + i++; + tmpptr = strtok(NULL, "&"); + } + + newpacket->lock = 0; + + aim_tx_enqueue(sess, newpacket); + + free(localcpy); + + return (sess->snac_nextid); +} + +/* + * aim_bos_setprofile(profile) + * + * Gives BOS your profile. + * + * + */ +faim_export unsigned long aim_bos_setprofile(struct aim_session_t *sess, + struct aim_conn_t *conn, + char *profile, + char *awaymsg, + unsigned short caps) +{ + struct command_tx_struct *newpacket; + int i = 0, tmp, caplen; + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 1152+strlen(profile)+1+(awaymsg?strlen(awaymsg):0)))) + return -1; + + i += aim_putsnac(newpacket->data, 0x0002, 0x004, 0x0000, sess->snac_nextid); + i += aim_puttlv_str(newpacket->data+i, 0x0001, strlen("text/x-aolrtf; charset=\"us-ascii\""), "text/x-aolrtf; charset=\"us-ascii\""); + i += aim_puttlv_str(newpacket->data+i, 0x0002, strlen(profile), profile); + /* why do we send this twice? */ + i += aim_puttlv_str(newpacket->data+i, 0x0003, strlen("text/x-aolrtf; charset=\"us-ascii\""), "text/x-aolrtf; charset=\"us-ascii\""); + + /* Away message -- we send this no matter what, even if its blank */ + if (awaymsg) + i += aim_puttlv_str(newpacket->data+i, 0x0004, strlen(awaymsg), awaymsg); + else + i += aim_puttlv_str(newpacket->data+i, 0x0004, 0x0000, NULL); + + /* Capability information. */ + + tmp = (i += aimutil_put16(newpacket->data+i, 0x0005)); + i += aimutil_put16(newpacket->data+i, 0x0000); /* rewritten later */ + i += (caplen = aim_putcap(newpacket->data+i, 512, caps)); + aimutil_put16(newpacket->data+tmp, caplen); /* rewrite TLV size */ + + newpacket->commandlen = i; + aim_tx_enqueue(sess, newpacket); + + return (sess->snac_nextid++); +} + +/* + * aim_bos_setgroupperm(mask) + * + * Set group permisson mask. Normally 0x1f (all classes). + * + * The group permission mask allows you to keep users of a certain + * class or classes from talking to you. The mask should be + * a bitwise OR of all the user classes you want to see you. + * + */ +faim_export unsigned long aim_bos_setgroupperm(struct aim_session_t *sess, + struct aim_conn_t *conn, + u_long mask) +{ + return aim_genericreq_l(sess, conn, 0x0009, 0x0004, &mask); +} + +faim_internal int aim_parse_bosrights(struct aim_session_t *sess, + struct command_rx_struct *command, ...) +{ + rxcallback_t userfunc = NULL; + int ret=1; + struct aim_tlvlist_t *tlvlist; + unsigned short maxpermits = 0, maxdenies = 0; + + /* + * TLVs follow + */ + if (!(tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10))) + return ret; + + /* + * TLV type 0x0001: Maximum number of buddies on permit list. + */ + if (aim_gettlv(tlvlist, 0x0001, 1)) + maxpermits = aim_gettlv16(tlvlist, 0x0001, 1); + + /* + * TLV type 0x0002: Maximum number of buddies on deny list. + * + */ + if (aim_gettlv(tlvlist, 0x0002, 1)) + maxdenies = aim_gettlv16(tlvlist, 0x0002, 1); + + if ((userfunc = aim_callhandler(sess, command->conn, 0x0009, 0x0003))) + ret = userfunc(sess, command, maxpermits, maxdenies); + + aim_freetlvchain(&tlvlist); + + return ret; +} + +/* + * aim_bos_clientready() + * + * Send Client Ready. + * + */ +faim_export unsigned long aim_bos_clientready(struct aim_session_t *sess, + struct aim_conn_t *conn) +{ + struct aim_tool_version tools[] = { + {0x0001, 0x0003, AIM_TOOL_WIN32, 0x0686}, + {0x0002, 0x0001, AIM_TOOL_WIN32, 0x0001}, + {0x0003, 0x0001, AIM_TOOL_WIN32, 0x0001}, + {0x0004, 0x0001, AIM_TOOL_WIN32, 0x0001}, + {0x0006, 0x0001, AIM_TOOL_WIN32, 0x0001}, + {0x0008, 0x0001, AIM_TOOL_WIN32, 0x0001}, + {0x0009, 0x0001, AIM_TOOL_WIN32, 0x0001}, + {0x000a, 0x0001, AIM_TOOL_WIN32, 0x0001}, + {0x000b, 0x0001, AIM_TOOL_WIN32, 0x0001} + }; + int i,j; + struct command_tx_struct *newpacket; + int toolcount = sizeof(tools)/sizeof(struct aim_tool_version); + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 1152))) + return -1; + + newpacket->lock = 1; + + i = aim_putsnac(newpacket->data, 0x0001, 0x0002, 0x0000, sess->snac_nextid); + aim_cachesnac(sess, 0x0001, 0x0002, 0x0000, NULL, 0); + + for (j = 0; j < toolcount; j++) { + i += aimutil_put16(newpacket->data+i, tools[j].group); + i += aimutil_put16(newpacket->data+i, tools[j].version); + i += aimutil_put16(newpacket->data+i, tools[j].tool); + i += aimutil_put16(newpacket->data+i, tools[j].toolversion); + } + + newpacket->commandlen = i; + newpacket->lock = 0; + + aim_tx_enqueue(sess, newpacket); + + return sess->snac_nextid; +} + +/* + * Request Rate Information. + * + */ +faim_export unsigned long aim_bos_reqrate(struct aim_session_t *sess, + struct aim_conn_t *conn) +{ + return aim_genericreq_n(sess, conn, 0x0001, 0x0006); +} + +/* + * Rate Information Response Acknowledge. + * + */ +faim_export unsigned long aim_bos_ackrateresp(struct aim_session_t *sess, + struct aim_conn_t *conn) +{ + struct command_tx_struct *newpacket; + int packlen = 20, i=0; + + if(!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, packlen))) + return (sess->snac_nextid); + + newpacket->lock = 1; + + i = aim_putsnac(newpacket->data, 0x0001, 0x0008, 0x0000, 0); + i += aimutil_put16(newpacket->data+i, 0x0001); + i += aimutil_put16(newpacket->data+i, 0x0002); + i += aimutil_put16(newpacket->data+i, 0x0003); + i += aimutil_put16(newpacket->data+i, 0x0004); + i += aimutil_put16(newpacket->data+i, 0x0005); + + newpacket->commandlen = i; + newpacket->lock = 0; + + aim_tx_enqueue(sess, newpacket); + + return (sess->snac_nextid); +} + +/* + * aim_bos_setprivacyflags() + * + * Sets privacy flags. Normally 0x03. + * + * Bit 1: Allows other AIM users to see how long you've been idle. + * Bit 2: Allows other AIM users to see how long you've been a member. + * + */ +faim_export unsigned long aim_bos_setprivacyflags(struct aim_session_t *sess, + struct aim_conn_t *conn, + u_long flags) +{ + return aim_genericreq_l(sess, conn, 0x0001, 0x0014, &flags); +} + +/* + * aim_bos_reqpersonalinfo() + * + * Requests the current user's information. Can't go generic on this one + * because aparently it uses SNAC flags. + * + */ +faim_export unsigned long aim_bos_reqpersonalinfo(struct aim_session_t *sess, + struct aim_conn_t *conn) +{ + return aim_genericreq_n(sess, conn, 0x0001, 0x000e); +} + +faim_export unsigned long aim_setversions(struct aim_session_t *sess, + struct aim_conn_t *conn) +{ + struct command_tx_struct *newpacket; + int i; + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10 + (4*12)))) + return -1; + + newpacket->lock = 1; + + i = aim_putsnac(newpacket->data, 0x0001, 0x0017, 0x0000, sess->snac_nextid); + aim_cachesnac(sess, 0x0001, 0x0017, 0x0000, NULL, 0); + + i += aimutil_put16(newpacket->data+i, 0x0001); + i += aimutil_put16(newpacket->data+i, 0x0003); + + i += aimutil_put16(newpacket->data+i, 0x0013); + i += aimutil_put16(newpacket->data+i, 0x0001); + + i += aimutil_put16(newpacket->data+i, 0x0002); + i += aimutil_put16(newpacket->data+i, 0x0001); + + i += aimutil_put16(newpacket->data+i, 0x0003); + i += aimutil_put16(newpacket->data+i, 0x0001); + + i += aimutil_put16(newpacket->data+i, 0x0004); + i += aimutil_put16(newpacket->data+i, 0x0001); + + i += aimutil_put16(newpacket->data+i, 0x0006); + i += aimutil_put16(newpacket->data+i, 0x0001); + + i += aimutil_put16(newpacket->data+i, 0x0008); + i += aimutil_put16(newpacket->data+i, 0x0001); + + i += aimutil_put16(newpacket->data+i, 0x0009); + i += aimutil_put16(newpacket->data+i, 0x0001); + + i += aimutil_put16(newpacket->data+i, 0x000a); + i += aimutil_put16(newpacket->data+i, 0x0001); + + i += aimutil_put16(newpacket->data+i, 0x000b); + i += aimutil_put16(newpacket->data+i, 0x0001); + + i += aimutil_put16(newpacket->data+i, 0x000c); + i += aimutil_put16(newpacket->data+i, 0x0001); + + newpacket->commandlen = i; + newpacket->lock = 0; + aim_tx_enqueue(sess, newpacket); + + return sess->snac_nextid; +} + + +/* + * aim_bos_reqservice(serviceid) + * + * Service request. + * + */ +faim_export unsigned long aim_bos_reqservice(struct aim_session_t *sess, + struct aim_conn_t *conn, + u_short serviceid) +{ + return aim_genericreq_s(sess, conn, 0x0001, 0x0004, &serviceid); +} + +/* + * aim_bos_nop() + * + * No-op. WinAIM sends these every 4min or so to keep + * the connection alive. Its not real necessary. + * + */ +faim_export unsigned long aim_bos_nop(struct aim_session_t *sess, + struct aim_conn_t *conn) +{ + return aim_genericreq_n(sess, conn, 0x0001, 0x0016); +} + +/* + * aim_flap_nop() + * + * No-op. WinAIM 4.x sends these _every minute_ to keep + * the connection alive. + */ +faim_export unsigned long aim_flap_nop(struct aim_session_t *sess, + struct aim_conn_t *conn) +{ + struct command_tx_struct *newpacket; + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0005, 0))) + return sess->snac_nextid; + + newpacket->lock = 1; + newpacket->commandlen = 0; + newpacket->lock = 0; + + aim_tx_enqueue(sess, newpacket); + + return (sess->snac_nextid); +} + +/* + * aim_bos_reqrights() + * + * Request BOS rights. + * + */ +faim_export unsigned long aim_bos_reqrights(struct aim_session_t *sess, + struct aim_conn_t *conn) +{ + return aim_genericreq_n(sess, conn, 0x0009, 0x0002); +} + +/* + * aim_bos_reqbuddyrights() + * + * Request Buddy List rights. + * + */ +faim_export unsigned long aim_bos_reqbuddyrights(struct aim_session_t *sess, + struct aim_conn_t *conn) +{ + return aim_genericreq_n(sess, conn, 0x0003, 0x0002); +} + +/* + * aim_send_warning(struct aim_session_t *sess, + * struct aim_conn_t *conn, char *destsn, int anon) + * send a warning to destsn. + * anon is anonymous or not; + * AIM_WARN_ANON anonymous + * + * returns -1 on error (couldn't alloc packet), next snacid on success. + * + */ +faim_export int aim_send_warning(struct aim_session_t *sess, struct aim_conn_t *conn, char *destsn, int anon) +{ + struct command_tx_struct *newpacket; + int curbyte; + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, strlen(destsn)+13))) + return -1; + + newpacket->lock = 1; + + curbyte = 0; + curbyte += aim_putsnac(newpacket->data+curbyte, + 0x0004, 0x0008, 0x0000, sess->snac_nextid); + + curbyte += aimutil_put16(newpacket->data+curbyte, (anon & AIM_WARN_ANON)?1:0); + + curbyte += aimutil_put8(newpacket->data+curbyte, strlen(destsn)); + + curbyte += aimutil_putstr(newpacket->data+curbyte, destsn, strlen(destsn)); + + newpacket->commandlen = curbyte; + newpacket->lock = 0; + + aim_tx_enqueue(sess, newpacket); + + return (sess->snac_nextid++); +} + +/* + * aim_debugconn_sendconnect() + * + * For aimdebugd. If you don't know what it is, you don't want to. + */ +faim_export unsigned long aim_debugconn_sendconnect(struct aim_session_t *sess, + struct aim_conn_t *conn) +{ + return aim_genericreq_n(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_DEBUGCONN_CONNECT); +} + +/* + * Generic routine for sending commands. + * + * + * I know I can do this in a smarter way...but I'm not thinking straight + * right now... + * + * I had one big function that handled all three cases, but then it broke + * and I split it up into three. But then I fixed it. I just never went + * back to the single. I don't see any advantage to doing it either way. + * + */ +faim_internal unsigned long aim_genericreq_n(struct aim_session_t *sess, + struct aim_conn_t *conn, + u_short family, u_short subtype) +{ + struct command_tx_struct *newpacket; + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10))) + return 0; + + newpacket->lock = 1; + + aim_putsnac(newpacket->data, family, subtype, 0x0000, sess->snac_nextid); + + aim_cachesnac(sess, family, subtype, 0x0000, NULL, 0); + + aim_tx_enqueue(sess, newpacket); + return sess->snac_nextid; +} + +/* + * + * + */ +faim_internal unsigned long aim_genericreq_l(struct aim_session_t *sess, + struct aim_conn_t *conn, + u_short family, u_short subtype, + u_long *longdata) +{ + struct command_tx_struct *newpacket; + u_long newlong; + + /* If we don't have data, there's no reason to use this function */ + if (!longdata) + return aim_genericreq_n(sess, conn, family, subtype); + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+sizeof(u_long)))) + return -1; + + newpacket->lock = 1; + + aim_putsnac(newpacket->data, family, subtype, 0x0000, sess->snac_nextid); + aim_cachesnac(sess, family, subtype, 0x0000, NULL, 0); + + /* copy in data */ + newlong = htonl(*longdata); + memcpy(&(newpacket->data[10]), &newlong, sizeof(u_long)); + + aim_tx_enqueue(sess, newpacket); + return sess->snac_nextid; +} + +faim_internal unsigned long aim_genericreq_s(struct aim_session_t *sess, + struct aim_conn_t *conn, + u_short family, u_short subtype, + u_short *shortdata) +{ + struct command_tx_struct *newpacket; + u_short newshort; + + /* If we don't have data, there's no reason to use this function */ + if (!shortdata) + return aim_genericreq_n(sess, conn, family, subtype); + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+sizeof(u_short)))) + return -1; + + newpacket->lock = 1; + + aim_putsnac(newpacket->data, family, subtype, 0x0000, sess->snac_nextid); + aim_cachesnac(sess, family, subtype, 0x0000, NULL, 0); + + /* copy in data */ + newshort = htons(*shortdata); + memcpy(&(newpacket->data[10]), &newshort, sizeof(u_short)); + + aim_tx_enqueue(sess, newpacket); + return sess->snac_nextid; +} + +/* + * aim_bos_reqlocaterights() + * + * Request Location services rights. + * + */ +faim_export unsigned long aim_bos_reqlocaterights(struct aim_session_t *sess, + struct aim_conn_t *conn) +{ + return aim_genericreq_n(sess, conn, 0x0002, 0x0002); +} + +/* +* aim_bos_reqicbmparaminfo() + * + * Request ICBM parameter information. + * + */ +faim_export unsigned long aim_bos_reqicbmparaminfo(struct aim_session_t *sess, + struct aim_conn_t *conn) +{ + return aim_genericreq_n(sess, conn, 0x0004, 0x0004); +} + +/* + * Add ICBM parameter? Huh? + */ +faim_export unsigned long aim_addicbmparam(struct aim_session_t *sess, + struct aim_conn_t *conn) +{ + struct command_tx_struct *newpacket; + int packlen = 10+16, i=0; + + if(!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, packlen))) + return (sess->snac_nextid); + + newpacket->lock = 1; + + i = aim_putsnac(newpacket->data, 0x0004, 0x0002, 0x0000, sess->snac_nextid); + aim_cachesnac(sess, 0x0004, 0x0002, 0x0000, NULL, 0); + + i += aimutil_put16(newpacket->data+i, 0x0000); + i += aimutil_put16(newpacket->data+i, 0x0000); + i += aimutil_put16(newpacket->data+i, 0x0003); + i += aimutil_put16(newpacket->data+i, 0x1f40); + i += aimutil_put16(newpacket->data+i, 0x03e7); + i += aimutil_put16(newpacket->data+i, 0x03e7); + i += aimutil_put16(newpacket->data+i, 0x0000); + i += aimutil_put16(newpacket->data+i, 0x0000); + + aim_tx_enqueue(sess, newpacket); + + return sess->snac_nextid; +} + +/* + * Set directory profile data (not the same as aim_bos_setprofile!) + */ +faim_export unsigned long aim_setdirectoryinfo(struct aim_session_t *sess, struct aim_conn_t *conn, char *first, char *middle, char *last, char *maiden, char *nickname, char *street, char *city, char *state, char *zip, int country, unsigned short privacy) +{ + struct command_tx_struct *newpacket; + int packlen = 0, i = 0; + + packlen += 2+2+2; + + if(first) /* TLV 0001 */ + packlen += (strlen(first) + 4); + if(middle) + packlen += (strlen(middle) + 4); + if(last) + packlen += (strlen(last) + 4); + if(maiden) + packlen += (strlen(maiden) + 4); + if(nickname) + packlen += (strlen(nickname) + 4); + if(street) + packlen += (strlen(street) + 4); + if(state) + packlen += (strlen(state) + 4); + if(city) + packlen += (strlen(city) + 4); + if(zip) + packlen += (strlen(zip) + 4); + + if(!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, packlen+10))) + return -1; + + newpacket->lock = 1; + + i = aim_putsnac(newpacket->data, 0x0002, 0x0009, 0x0000, 0); + + /* 000a/0002: privacy: 1 to allow search/disp, 0 to disallow */ + i += aim_puttlv_16(newpacket->data+i, 0x000a, privacy); + + + if (first) + i += aim_puttlv_str(newpacket->data+i, 0x0001, strlen(first), first); + if (middle) + i += aim_puttlv_str(newpacket->data+i, 0x0003, strlen(middle), middle); + if (last) + i += aim_puttlv_str(newpacket->data+i, 0x0002, strlen(last), last); + if (maiden) + i += aim_puttlv_str(newpacket->data+i, 0x0004, strlen(maiden), maiden); + if (nickname) + i += aim_puttlv_str(newpacket->data+i, 0x000c, strlen(nickname), nickname); + if (street) + i += aim_puttlv_str(newpacket->data+i, 0x0021, strlen(street), street); + if (city) + i += aim_puttlv_str(newpacket->data+i, 0x0008, strlen(city), city); + if (state) + i += aim_puttlv_str(newpacket->data+i, 0x0007, strlen(state), state); + if (zip) + i += aim_puttlv_str(newpacket->data+i, 0x000d, strlen(zip), zip); + + newpacket->commandlen = i; + newpacket->lock = 0; + + aim_tx_enqueue(sess, newpacket); + + return(sess->snac_nextid); +} + +faim_export unsigned long aim_setuserinterests(struct aim_session_t *sess, struct aim_conn_t *conn, char *interest1, char *interest2, char *interest3, char *interest4, char *interest5, unsigned short privacy) +{ + struct command_tx_struct *newpacket; + int packlen = 0, i = 0; + + packlen += 2+2+2; + + if(interest1) + packlen += (strlen(interest1) + 4); + if(interest2) + packlen += (strlen(interest2) + 4); + if(interest3) + packlen += (strlen(interest3) + 4); + if(interest4) + packlen += (strlen(interest4) + 4); + if(interest5) + packlen += (strlen(interest5) + 4) ; + + + if(!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, packlen+10))) + return -1; + + newpacket->lock = 1; + + i = aim_putsnac(newpacket->data, 0x0002, 0x000f, 0x0000, 0); + + /* 000a/0002: 0000 ?? ?privacy? */ + i += aim_puttlv_16(newpacket->data+i, 0x000a, privacy); + + if(interest1) + i += aim_puttlv_str(newpacket->data+i, 0x000b, strlen(interest1), interest1); + if(interest2) + i += aim_puttlv_str(newpacket->data+i, 0x000b, strlen(interest2), interest2); + if(interest3) + i += aim_puttlv_str(newpacket->data+i, 0x000b, strlen(interest3), interest3); + if(interest4) + i += aim_puttlv_str(newpacket->data+i, 0x000b, strlen(interest4), interest4); + if(interest5) + i += aim_puttlv_str(newpacket->data+i, 0x000b, strlen(interest1), interest5); + + newpacket->commandlen = i; + newpacket->lock = 0; + + aim_tx_enqueue(sess, newpacket); + + return(sess->snac_nextid); +} + +faim_export unsigned long aim_icq_setstatus(struct aim_session_t *sess, + struct aim_conn_t *conn, + unsigned long status) +{ + struct command_tx_struct *newpacket; + int i; + unsigned long data; + + data = 0x00030000 | status; /* yay for error checking ;^) */ + + if(!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10 + 4))) + return -1; + + newpacket->lock = 1; + + i = aim_putsnac(newpacket->data, 0x0001, 0x001e, 0x0000, 0x0000001e); + i += aim_puttlv_32(newpacket->data+i, 0x0006, data); + + newpacket->commandlen = i; + newpacket->lock = 0; + + aim_tx_enqueue(sess, newpacket); + + return(sess->snac_nextid); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfaim/msgcookie.c Mon Mar 05 03:59:32 2001 +0000 @@ -0,0 +1,212 @@ +/* + * Cookie Caching stuff. Adam wrote this, apparently just some + * derivatives of n's SNAC work. I cleaned it up, added comments. + * + */ + +/* + * I'm assuming that cookies are type-specific. that is, we can have + * "1234578" for type 1 and type 2 concurrently. if i'm wrong, then we + * lose some error checking. if we assume cookies are not type-specific and are + * wrong, we get quirky behavior when cookies step on each others' toes. + */ + +#define FAIM_INTERNAL +#include <aim.h> + +/** + * aim_cachecookie - appends a cookie to the cookie list + * @sess: session to add to + * @cookie: pointer to struct to append + * + * if cookie->cookie for type cookie->type is found, updates the + * ->addtime of the found structure; otherwise adds the given cookie + * to the cache + * + * returns -1 on error, 0 on append, 1 on update. the cookie you pass + * in may be free'd, so don't count on its value after calling this! + * + */ +faim_internal int aim_cachecookie(struct aim_session_t *sess, + struct aim_msgcookie_t *cookie) +{ + struct aim_msgcookie_t *newcook; + + if (!sess || !cookie) + return -1; + + if( (newcook = aim_checkcookie(sess, cookie->cookie, cookie->type)) ) { + if(newcook != cookie) { + aim_cookie_free(sess, newcook); + } else { + newcook->addtime = time(NULL); + return 1; + } + } + + cookie->addtime = time(NULL); + + cookie->next = sess->msgcookies; + sess->msgcookies = cookie; + + return 0; +} + +/** + * aim_uncachecookie - grabs a cookie from the cookie cache (removes it from the list) + * @sess: session to grab cookie from + * @cookie: cookie string to look for + * @type: cookie type to look for + * + * takes a cookie string and a cookie type and finds the cookie struct associated with that duple, removing it from the cookie list ikn the process. + * + * if found, returns the struct; if none found (or on error), returns NULL: + */ +faim_internal struct aim_msgcookie_t *aim_uncachecookie(struct aim_session_t *sess, unsigned char *cookie, int type) +{ + struct aim_msgcookie_t *cur, **prev; + + if (!cookie || !sess->msgcookies) + return NULL; + + for (prev = &sess->msgcookies; (cur = *prev); ) { + if ((cur->type == type) && + (memcmp(cur->cookie, cookie, 8) == 0)) { + *prev = cur->next; + return cur; + } + prev = &cur->next; + } + + return NULL; +} + +/** + * aim_mkcookie - generate an aim_msgcookie_t *struct from a cookie string, a type, and a data pointer. + * @c: pointer to the cookie string array + * @type: cookie type to use + * @data: data to be cached with the cookie + * + * returns NULL on error, a pointer to the newly-allocated cookie on + * success. + * + */ +faim_internal struct aim_msgcookie_t *aim_mkcookie(unsigned char *c, int type, void *data) +{ + struct aim_msgcookie_t *cookie; + + if (!c) + return NULL; + + if (!(cookie = calloc(1, sizeof(struct aim_msgcookie_t)))) + return NULL; + + cookie->data = data; + cookie->type = type; + memcpy(cookie->cookie, c, 8); + + return cookie; +} + +/** + * aim_checkcookie - check to see if a cookietuple has been cached + * @sess: session to check for the cookie in + * @cookie: pointer to the cookie string array + * @type: type of the cookie to look for + * + * this returns a pointer to the cookie struct (still in the list) on + * success; returns NULL on error/not found + * + */ + +faim_internal struct aim_msgcookie_t *aim_checkcookie(struct aim_session_t *sess, + const unsigned char *cookie, + const int type) +{ + struct aim_msgcookie_t *cur; + + for (cur = sess->msgcookies; cur; cur = cur->next) { + if ((cur->type == type) && + (memcmp(cur->cookie, cookie, 8) == 0)) + return cur; + } + + return NULL; +} + +#if 0 /* debugging feature */ +faim_internal int aim_dumpcookie(struct aim_msgcookie_t *cookie) +{ + if(!cookie) + return -1; + printf("\tCookie at %p: %d/%s with %p, next %p\n", cookie, cookie->type, cookie->cookie, cookie->data, cookie->next); + return 0; +} +#endif + +/** + * aim_cookie_free - free an aim_msgcookie_t struct + * @sess: session to remove the cookie from + * @cookiep: the address of a pointer to the cookie struct to remove + * + * this function removes the cookie *cookie from teh list of cookies + * in sess, and then frees all memory associated with it. including + * its data! if you want to use the private data after calling this, + * make sure you copy it first. + * + * returns -1 on error, 0 on success. + * + */ + +faim_internal int aim_cookie_free(struct aim_session_t *sess, + struct aim_msgcookie_t *cookie) +{ + struct aim_msgcookie_t *cur, **prev; + + if (!sess || !cookie) + return -1; + + if(!cookie) + return 0; + + for (prev = &sess->msgcookies; (cur = *prev); ) { + if (cur == cookie) { + *prev = cur->next; + } else + prev = &cur->next; + } + + if(cookie->data) + free(cookie->data); + + free(cookie); + + return 0; +} + +faim_internal int aim_msgcookie_gettype(int reqclass) { + /* XXX: hokey-assed. needs fixed. */ + switch(reqclass) { + case AIM_CAPS_BUDDYICON: + return AIM_COOKIETYPE_OFTICON; + break; + case AIM_CAPS_VOICE: + return AIM_COOKIETYPE_OFTVOICE; + break; + case AIM_CAPS_IMIMAGE: + return AIM_COOKIETYPE_OFTIMAGE; + break; + case AIM_CAPS_CHAT: + return AIM_COOKIETYPE_CHAT; + break; + case AIM_CAPS_GETFILE: + return AIM_COOKIETYPE_OFTGET; + break; + case AIM_CAPS_SENDFILE: + return AIM_COOKIETYPE_OFTSEND; + break; + default: + return AIM_COOKIETYPE_UNKNOWN; + break; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfaim/rxhandlers.c Mon Mar 05 03:59:32 2001 +0000 @@ -0,0 +1,1081 @@ +/* + * aim_rxhandlers.c + * + * This file contains most all of the incoming packet handlers, along + * with aim_rxdispatch(), the Rx dispatcher. Queue/list management is + * actually done in aim_rxqueue.c. + * + */ + +#define FAIM_INTERNAL +#include <aim.h> + +/* + * Bleck functions get called when there's no non-bleck functions + * around to cleanup the mess... + */ +faim_internal int bleck(struct aim_session_t *sess,struct command_rx_struct *workingPtr, ...) +{ + u_short family; + u_short subtype; + + u_short maxf; + u_short maxs; + + /* XXX: this is ugly. and big just for debugging. */ + char *literals[14][25] = { + {"Invalid", + NULL + }, + {"General", + "Invalid", + "Error", + "Client Ready", + "Server Ready", + "Service Request", + "Redirect", + "Rate Information Request", + "Rate Information", + "Rate Information Ack", + NULL, + "Rate Information Change", + "Server Pause", + NULL, + "Server Resume", + "Request Personal User Information", + "Personal User Information", + "Evil Notification", + NULL, + "Migration notice", + "Message of the Day", + "Set Privacy Flags", + "Well Known URL", + "NOP" + }, + {"Location", + "Invalid", + "Error", + "Request Rights", + "Rights Information", + "Set user information", + "Request User Information", + "User Information", + "Watcher Sub Request", + "Watcher Notification" + }, + {"Buddy List Management", + "Invalid", + "Error", + "Request Rights", + "Rights Information", + "Add Buddy", + "Remove Buddy", + "Watcher List Query", + "Watcher List Response", + "Watcher SubRequest", + "Watcher Notification", + "Reject Notification", + "Oncoming Buddy", + "Offgoing Buddy" + }, + {"Messeging", + "Invalid", + "Error", + "Add ICBM Parameter", + "Remove ICBM Parameter", + "Request Parameter Information", + "Parameter Information", + "Outgoing Message", + "Incoming Message", + "Evil Request", + "Evil Reply", + "Missed Calls", + "Message Error", + "Host Ack" + }, + {"Advertisements", + "Invalid", + "Error", + "Request Ad", + "Ad Data (GIFs)" + }, + {"Invitation / Client-to-Client", + "Invalid", + "Error", + "Invite a Friend", + "Invitation Ack" + }, + {"Administrative", + "Invalid", + "Error", + "Information Request", + "Information Reply", + "Information Change Request", + "Information Chat Reply", + "Account Confirm Request", + "Account Confirm Reply", + "Account Delete Request", + "Account Delete Reply" + }, + {"Popups", + "Invalid", + "Error", + "Display Popup" + }, + {"BOS", + "Invalid", + "Error", + "Request Rights", + "Rights Response", + "Set group permission mask", + "Add permission list entries", + "Delete permission list entries", + "Add deny list entries", + "Delete deny list entries", + "Server Error" + }, + {"User Lookup", + "Invalid", + "Error", + "Search Request", + "Search Response" + }, + {"Stats", + "Invalid", + "Error", + "Set minimum report interval", + "Report Events" + }, + {"Translate", + "Invalid", + "Error", + "Translate Request", + "Translate Reply", + }, + {"Chat Navigation", + "Invalid", + "Error", + "Request rights", + "Request Exchange Information", + "Request Room Information", + "Request Occupant List", + "Search for Room", + "Outgoing Message", + "Incoming Message", + "Evil Request", + "Evil Reply", + "Chat Error", + } + }; + + maxf = sizeof(literals) / sizeof(literals[0]); + maxs = sizeof(literals[0]) / sizeof(literals[0][0]); + + family = aimutil_get16(workingPtr->data+0); + subtype= aimutil_get16(workingPtr->data+2); + + if((family < maxf) && (subtype+1 < maxs) && (literals[family][subtype] != NULL)) + faimdprintf(sess, 0, "bleck: null handler for %04x/%04x (%s)\n", family, subtype, literals[family][subtype+1]); + else + faimdprintf(sess, 0, "bleck: null handler for %04x/%04x (no literal)\n",family,subtype); + + return 1; +} + +faim_export int aim_conn_addhandler(struct aim_session_t *sess, + struct aim_conn_t *conn, + u_short family, + u_short type, + rxcallback_t newhandler, + u_short flags) +{ + struct aim_rxcblist_t *newcb; + + if (!conn) + return -1; + + faimdprintf(sess, 1, "aim_conn_addhandler: adding for %04x/%04x\n", family, type); + + if (!(newcb = (struct aim_rxcblist_t *)calloc(1, sizeof(struct aim_rxcblist_t)))) + return -1; + newcb->family = family; + newcb->type = type; + newcb->flags = flags; + if (!newhandler) + newcb->handler = &bleck; + else + newcb->handler = newhandler; + newcb->next = NULL; + + if (!conn->handlerlist) + conn->handlerlist = newcb; + else { + struct aim_rxcblist_t *cur; + + cur = conn->handlerlist; + + while (cur->next) + cur = cur->next; + cur->next = newcb; + } + + return 0; +} + +faim_export int aim_clearhandlers(struct aim_conn_t *conn) +{ + struct aim_rxcblist_t *cur; + + if (!conn) + return -1; + + for (cur = conn->handlerlist; cur; ) { + struct aim_rxcblist_t *tmp; + + tmp = cur->next; + free(cur); + cur = tmp; + } + conn->handlerlist = NULL; + + return 0; +} + +faim_internal rxcallback_t aim_callhandler(struct aim_session_t *sess, + struct aim_conn_t *conn, + unsigned short family, + unsigned short type) +{ + struct aim_rxcblist_t *cur; + + if (!conn) + return NULL; + + faimdprintf(sess, 1, "aim_callhandler: calling for %04x/%04x\n", family, type); + + for (cur = conn->handlerlist; cur; cur = cur->next) { + if ((cur->family == family) && (cur->type == type)) + return cur->handler; + } + + if (type == AIM_CB_SPECIAL_DEFAULT) { + faimdprintf(sess, 1, "aim_callhandler: no default handler for family 0x%04x\n", family); + return NULL; /* prevent infinite recursion */ + } + + faimdprintf(sess, 1, "aim_callhandler: no handler for 0x%04x/0x%04x\n", family, type); + + return aim_callhandler(sess, conn, family, AIM_CB_SPECIAL_DEFAULT); +} + +faim_internal int aim_callhandler_noparam(struct aim_session_t *sess, + struct aim_conn_t *conn, + u_short family, + u_short type, + struct command_rx_struct *ptr) +{ + rxcallback_t userfunc = NULL; + userfunc = aim_callhandler(sess, conn, family, type); + if (userfunc) + return userfunc(sess, ptr); + return 1; /* XXX */ +} + +/* + aim_rxdispatch() + + Basically, heres what this should do: + 1) Determine correct packet handler for this packet + 2) Mark the packet handled (so it can be dequeued in purge_queue()) + 3) Send the packet to the packet handler + 4) Go to next packet in the queue and start over + 5) When done, run purge_queue() to purge handled commands + + Note that any unhandlable packets should probably be left in the + queue. This is the best way to prevent data loss. This means + that a single packet may get looked at by this function multiple + times. This is more good than bad! This behavior may change. + + Aren't queue's fun? + + TODO: Get rid of all the ugly if's. + TODO: Clean up. + TODO: More support for mid-level handlers. + TODO: Allow for NULL handlers. + + */ +faim_export int aim_rxdispatch(struct aim_session_t *sess) +{ + int i = 0; + struct command_rx_struct *workingPtr = NULL; + + if (sess->queue_incoming == NULL) { + faimdprintf(sess, 1, "parse_generic: incoming packet queue empty.\n"); + return 0; + } else { + workingPtr = sess->queue_incoming; + for (i = 0; workingPtr != NULL; workingPtr = workingPtr->next, i++) { + /* + * XXX: This is still fairly ugly. + */ + if (workingPtr->handled) + continue; + + /* + * This is a debugging/sanity check only and probably could/should be removed + * for stable code. + */ + if (((workingPtr->hdrtype == AIM_FRAMETYPE_OFT) && + (workingPtr->conn->type != AIM_CONN_TYPE_RENDEZVOUS)) || + ((workingPtr->hdrtype == AIM_FRAMETYPE_OSCAR) && + (workingPtr->conn->type == AIM_CONN_TYPE_RENDEZVOUS))) { + faimdprintf(sess, 0, "rxhandlers: incompatible frame type %d on connection type 0x%04x\n", workingPtr->hdrtype, workingPtr->conn->type); + workingPtr->handled = 1; + continue; + } + + switch(workingPtr->conn->type) { + case -1: + /* + * This can happen if we have a queued command + * that was recieved after a connection has + * been terminated. In which case, the handler + * list has been cleared, and there's nothing we + * can do for it. We can only cancel it. + */ + workingPtr->handled = 1; + break; + case AIM_CONN_TYPE_AUTH: { + unsigned long head; + + head = aimutil_get32(workingPtr->data); + if ((head == 0x00000001) && (workingPtr->commandlen == 4)) { + faimdprintf(sess, 1, "got connection ack on auth line\n"); + workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr); + } else if (workingPtr->hdr.oscar.type == 0x04) { + /* Used only by the older login protocol */ + workingPtr->handled = aim_authparse(sess, workingPtr); + } else { + unsigned short family,subtype; + + family = aimutil_get16(workingPtr->data); + subtype = aimutil_get16(workingPtr->data+2); + + switch (family) { + /* New login protocol */ + case 0x0017: + if (subtype == 0x0001) + workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0x0001, workingPtr); + else if (subtype == 0x0003) + workingPtr->handled = aim_authparse(sess, workingPtr); + else if (subtype == 0x0007) + workingPtr->handled = aim_authkeyparse(sess, workingPtr); + else + workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0xffff, workingPtr); + break; + + case 0x0001: + if (subtype == 0x0003) + workingPtr->handled = aim_parse_hostonline(sess, workingPtr); + else if (subtype == 0x0007) + workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr); + else if (subtype == 0x0018) + workingPtr->handled = aim_parse_hostversions(sess, workingPtr); + else + workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0xffff, workingPtr); + break; + + case 0x0007: + if (subtype == 0x0003) + workingPtr->handled = aim_parse_infochange(sess, workingPtr); + else if (subtype == 0x0005) + workingPtr->handled = aim_parse_infochange(sess, workingPtr); + else if (subtype == 0x0007) + workingPtr->handled = aim_parse_accountconfirm(sess, workingPtr); + break; + + case AIM_CB_FAM_SPECIAL: + if (subtype == AIM_CB_SPECIAL_DEBUGCONN_CONNECT) { + workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr); + break; + } else + workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0xffff, workingPtr); + break; + + default: + break; + } + } + break; + } + case AIM_CONN_TYPE_BOS: { + u_short family; + u_short subtype; + + if (workingPtr->hdr.oscar.type == 0x04) { + workingPtr->handled = aim_negchan_middle(sess, workingPtr); + break; + } + + family = aimutil_get16(workingPtr->data); + subtype = aimutil_get16(workingPtr->data+2); + + switch (family) { + case 0x0000: /* not really a family, but it works */ + if (subtype == 0x0001) + workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr); + else + workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr); + break; + case 0x0001: /* Family: General */ + switch (subtype) { + case 0x0001: + workingPtr->handled = aim_parse_generalerrs(sess, workingPtr); + break; + case 0x0003: + workingPtr->handled = aim_parse_hostonline(sess, workingPtr); + break; + case 0x0005: + workingPtr->handled = aim_handleredirect_middle(sess, workingPtr); + break; + case 0x0007: + workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr); + break; + case 0x000a: + workingPtr->handled = aim_parse_ratechange_middle(sess, workingPtr); + break; + case 0x000f: + workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x000f, workingPtr); + break; + case 0x0010: + workingPtr->handled = aim_parse_evilnotify_middle(sess, workingPtr); + break; + case 0x0013: + workingPtr->handled = aim_parsemotd_middle(sess, workingPtr); + break; + case 0x0018: + workingPtr->handled = aim_parse_hostversions(sess, workingPtr); + break; + default: + workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_GEN, AIM_CB_GEN_DEFAULT, workingPtr); + break; + } + break; + case 0x0002: /* Family: Location */ + switch (subtype) { + case 0x0001: + workingPtr->handled = aim_parse_locateerr(sess, workingPtr); + break; + case 0x0003: + workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0002, 0x0003, workingPtr); + break; + case 0x0006: + workingPtr->handled = aim_parse_userinfo_middle(sess, workingPtr); + break; + default: + workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOC, AIM_CB_LOC_DEFAULT, workingPtr); + break; + } + break; + case 0x0003: /* Family: Buddy List */ + switch (subtype) { + case 0x0001: + workingPtr->handled = aim_parse_generalerrs(sess, workingPtr); + break; + case 0x0003: + workingPtr->handled = aim_parse_buddyrights(sess, workingPtr); + break; + case 0x000b: /* oncoming buddy */ + workingPtr->handled = aim_parse_oncoming_middle(sess, workingPtr); + break; + case 0x000c: /* offgoing buddy */ + workingPtr->handled = aim_parse_offgoing_middle(sess, workingPtr); + break; + default: + workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BUD, AIM_CB_BUD_DEFAULT, workingPtr); + } + break; + case 0x0004: /* Family: Messaging */ + switch (subtype) { + case 0x0001: + workingPtr->handled = aim_parse_msgerror_middle(sess, workingPtr); + break; + case 0x0005: + workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0004, 0x0005, workingPtr); + break; + case 0x0006: + workingPtr->handled = aim_parse_outgoing_im_middle(sess, workingPtr); + break; + case 0x0007: + workingPtr->handled = aim_parse_incoming_im_middle(sess, workingPtr); + break; + case 0x000a: + workingPtr->handled = aim_parse_missedcall(sess, workingPtr); + break; + case 0x000c: + workingPtr->handled = aim_parse_msgack_middle(sess, workingPtr); + break; + default: + workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_MSG, AIM_CB_MSG_DEFAULT, workingPtr); + } + break; + case 0x0009: + if (subtype == 0x0001) + workingPtr->handled = aim_parse_generalerrs(sess, workingPtr); + else if (subtype == 0x0003) + workingPtr->handled = aim_parse_bosrights(sess, workingPtr); + else + workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BOS, AIM_CB_BOS_DEFAULT, workingPtr); + break; + case 0x000a: /* Family: User lookup */ + switch (subtype) { + case 0x0001: + workingPtr->handled = aim_parse_searcherror(sess, workingPtr); + break; + case 0x0003: + workingPtr->handled = aim_parse_searchreply(sess, workingPtr); + break; + default: + workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOK, AIM_CB_LOK_DEFAULT, workingPtr); + } + break; + case 0x000b: { + if (subtype == 0x0001) + workingPtr->handled = aim_parse_generalerrs(sess, workingPtr); + else if (subtype == 0x0002) + workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000b, 0x0002, workingPtr); + else + workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_STS, AIM_CB_STS_DEFAULT, workingPtr); + break; + } + case 0x0013: { + faimdprintf(sess, 0, "lalala: 0x%04x/0x%04x\n", family, subtype); + break; + } + case AIM_CB_FAM_SPECIAL: + workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr); + break; + default: + workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr); + break; + } /* switch(family) */ + break; + } /* AIM_CONN_TYPE_BOS */ + case AIM_CONN_TYPE_ADS: { + unsigned short family; + unsigned short subtype; + + family = aimutil_get16(workingPtr->data); + subtype= aimutil_get16(workingPtr->data+2); + + if ((family == 0x0000) && (subtype == 0x00001)) { + workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr); + } else if ((family == 0x0001) && (subtype == 0x0003)) { + workingPtr->handled = aim_parse_hostonline(sess, workingPtr); + } else { + workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr); + } + break; + } + case AIM_CONN_TYPE_CHATNAV: { + u_short family; + u_short subtype; + family = aimutil_get16(workingPtr->data); + subtype= aimutil_get16(workingPtr->data+2); + + if ((family == 0x0000) && (subtype == 0x00001)) { + workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr); + } else if ((family == 0x0001) && (subtype == 0x0003)) { + workingPtr->handled = aim_parse_hostonline(sess, workingPtr); + } else if ((family == 0x000d) && (subtype == 0x0009)) { + workingPtr->handled = aim_chatnav_parse_info(sess, workingPtr); + } else { + workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr); + } + break; + } + case AIM_CONN_TYPE_CHAT: { + u_short family, subtype; + + family = aimutil_get16(workingPtr->data); + subtype= aimutil_get16(workingPtr->data+2); + + if ((family == 0x0000) && (subtype == 0x00001)) { + workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr); + } else if (family == 0x0001) { + if (subtype == 0x0001) + workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0001, workingPtr); + else if (subtype == 0x0003) + workingPtr->handled = aim_parse_hostonline(sess, workingPtr); + else if (subtype == 0x0007) + workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr); + else if (subtype == 0x000a) + workingPtr->handled = aim_parse_ratechange_middle(sess, workingPtr); + else + workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr); + } else if (family == 0x000e) { + if (subtype == 0x0002) + workingPtr->handled = aim_chat_parse_infoupdate(sess, workingPtr); + else if (subtype == 0x0003) + workingPtr->handled = aim_chat_parse_joined(sess, workingPtr); + else if (subtype == 0x0004) + workingPtr->handled = aim_chat_parse_leave(sess, workingPtr); + else if (subtype == 0x0006) + workingPtr->handled = aim_chat_parse_incoming(sess, workingPtr); + else + faimdprintf(sess, 0, "Chat: unknown snac %04x/%04x\n", family, subtype); + } else { + faimdprintf(sess, 0, "Chat: unknown snac %04x/%04x\n", family, subtype); + workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_DEFAULT, workingPtr); + } + break; + } + case AIM_CONN_TYPE_RENDEZVOUS: { + /* make sure that we only get OFT frames on these connections */ + if (workingPtr->hdrtype != AIM_FRAMETYPE_OFT) { + faimdprintf(sess, 0, "internal error: non-OFT frames on OFT connection\n"); + workingPtr->handled = 1; /* get rid of it */ + break; + } + + /* XXX: implement this */ + faimdprintf(sess, 0, "faim: OFT frame!\n"); + + break; + } + case AIM_CONN_TYPE_RENDEZVOUS_OUT: { + /* not possible */ + break; + } + default: + faimdprintf(sess, 0, "internal error: unknown connection type (very bad.) (type = %d, fd = %d, commandlen = %02x)\n\n", workingPtr->conn->type, workingPtr->conn->fd, workingPtr->commandlen); + workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr); + break; + } + } + } + + /* + * This doesn't have to be called here. It could easily be done + * by a seperate thread or something. It's an administrative operation, + * and can take a while. Though the less you call it the less memory + * you'll have :) + */ + aim_purge_rxqueue(sess); + + return 0; +} + +faim_internal int aim_parse_msgack_middle(struct aim_session_t *sess, struct command_rx_struct *command) +{ + rxcallback_t userfunc = NULL; + char sn[MAXSNLEN]; + unsigned short type; + int i = 10+8; /* skip SNAC and cookie */ + int ret = 1; + unsigned char snlen; + + type = aimutil_get16(command->data+i); + i += 2; + + snlen = aimutil_get8(command->data+i); + i++; + + memset(sn, 0, sizeof(sn)); + strncpy(sn, (char *)command->data+i, snlen); + + if ((userfunc = aim_callhandler(sess, command->conn, 0x0004, 0x000c))) + ret = userfunc(sess, command, type, sn); + + return ret; +} + +/* + * The Rate Limiting System, An Abridged Guide to Nonsense. + * + * OSCAR defines several 'rate classes'. Each class has seperate + * rate limiting properties (limit level, alert level, disconnect + * level, etc), and a set of SNAC family/type pairs associated with + * it. The rate classes, their limiting properties, and the definitions + * of which SNACs are belong to which class, are defined in the + * Rate Response packet at login to each host. + * + * Logically, all rate offenses within one class count against further + * offenses for other SNACs in the same class (ie, sending messages + * too fast will limit the number of user info requests you can send, + * since those two SNACs are in the same rate class). + * + * Since the rate classes are defined dynamically at login, the values + * below may change. But they seem to be fairly constant. + * + * Currently, BOS defines five rate classes, with the commonly used + * members as follows... + * + * Rate class 0x0001: + * - Everything thats not in any of the other classes + * + * Rate class 0x0002: + * - Buddy list add/remove + * - Permit list add/remove + * - Deny list add/remove + * + * Rate class 0x0003: + * - User information requests + * - Outgoing ICBMs + * + * Rate class 0x0004: + * - A few unknowns: 2/9, 2/b, and f/2 + * + * Rate class 0x0005: + * - Chat room create + * - Outgoing chat ICBMs + * + * The only other thing of note is that class 5 (chat) has slightly looser + * limiting properties than class 3 (normal messages). But thats just a + * small bit of trivia for you. + * + * The last thing that needs to be learned about the rate limiting + * system is how the actual numbers relate to the passing of time. This + * seems to be a big mystery. + * + */ +faim_internal int aim_parse_ratechange_middle(struct aim_session_t *sess, struct command_rx_struct *command) +{ + rxcallback_t userfunc = NULL; + int ret = 1; + int i; + int code; + unsigned long rateclass, windowsize, clear, alert, limit, disconnect; + unsigned long currentavg, maxavg; + + i = 10; + + code = aimutil_get16(command->data+i); + i += 2; + + rateclass = aimutil_get16(command->data+i); + i += 2; + + windowsize = aimutil_get32(command->data+i); + i += 4; + clear = aimutil_get32(command->data+i); + i += 4; + alert = aimutil_get32(command->data+i); + i += 4; + limit = aimutil_get32(command->data+i); + i += 4; + disconnect = aimutil_get32(command->data+i); + i += 4; + currentavg = aimutil_get32(command->data+i); + i += 4; + maxavg = aimutil_get32(command->data+i); + i += 4; + + if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x000a))) + ret = userfunc(sess, command, code, rateclass, windowsize, clear, alert, limit, disconnect, currentavg, maxavg); + + return ret; +} + +faim_internal int aim_parse_evilnotify_middle(struct aim_session_t *sess, struct command_rx_struct *command) +{ + rxcallback_t userfunc = NULL; + int ret = 1; + int i; + unsigned short newevil; + struct aim_userinfo_s userinfo; + + i = 10; + newevil = aimutil_get16(command->data+10); + i += 2; + + memset(&userinfo, 0, sizeof(struct aim_userinfo_s)); + if (command->commandlen-i) + i += aim_extractuserinfo(sess, command->data+i, &userinfo); + + if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0010))) + ret = userfunc(sess, command, newevil, &userinfo); + + return ret; +} + +faim_internal int aim_parsemotd_middle(struct aim_session_t *sess, + struct command_rx_struct *command, ...) +{ + rxcallback_t userfunc = NULL; + char *msg; + int ret=1; + struct aim_tlvlist_t *tlvlist; + u_short id; + + /* + * Code. + * + * Valid values: + * 1 Mandatory upgrade + * 2 Advisory upgrade + * 3 System bulletin + * 4 Nothing's wrong ("top o the world" -- normal) + * + */ + id = aimutil_get16(command->data+10); + + /* + * TLVs follow + */ + if (!(tlvlist = aim_readtlvchain(command->data+12, command->commandlen-12))) + return ret; + + if (!(msg = aim_gettlv_str(tlvlist, 0x000b, 1))) { + aim_freetlvchain(&tlvlist); + return ret; + } + + userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0013); + if (userfunc) + ret = userfunc(sess, command, id, msg); + + aim_freetlvchain(&tlvlist); + free(msg); + + return ret; +} + +faim_internal int aim_parse_hostonline(struct aim_session_t *sess, + struct command_rx_struct *command, ...) +{ + rxcallback_t userfunc = NULL; + int ret = 1; + unsigned short *families = NULL; + int famcount = 0, i; + + famcount = (command->commandlen-10)/2; + if (!(families = malloc(command->commandlen-10))) + return ret; + + for (i = 0; i < famcount; i++) + families[i] = aimutil_get16(command->data+((i*2)+10)); + + if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0003))) + ret = userfunc(sess, command, famcount, families); + + free(families); + + return ret; +} + +faim_internal int aim_parse_accountconfirm(struct aim_session_t *sess, + struct command_rx_struct *command) +{ + rxcallback_t userfunc = NULL; + int ret = 1; + int status = -1; + + status = aimutil_get16(command->data+10); + + if ((userfunc = aim_callhandler(sess, command->conn, 0x0007, 0x0007))) + ret = userfunc(sess, command, status); + + return ret; +} + +faim_internal int aim_parse_infochange(struct aim_session_t *sess, + struct command_rx_struct *command) +{ + unsigned short subtype; /* called for both reply and change-reply */ + int i; + + subtype = aimutil_get16(command->data+2); + + /* + * struct { + * unsigned short perms; + * unsigned short tlvcount; + * aim_tlv_t tlvs[tlvcount]; + * } admin_info[n]; + */ + for (i = 10; i < command->commandlen; ) { + int perms, tlvcount; + + perms = aimutil_get16(command->data+i); + i += 2; + + tlvcount = aimutil_get16(command->data+i); + i += 2; + + while (tlvcount) { + rxcallback_t userfunc; + struct aim_tlv_t *tlv; + int str = 0; + + if ((aimutil_get16(command->data+i) == 0x0011) || + (aimutil_get16(command->data+i) == 0x0004)) + str = 1; + + if (str) + tlv = aim_grabtlvstr(command->data+i); + else + tlv = aim_grabtlv(command->data+i); + + /* XXX fix so its only called once for the entire packet */ + if ((userfunc = aim_callhandler(sess, command->conn, 0x0007, subtype))) + userfunc(sess, command, perms, tlv->type, tlv->length, tlv->value, str); + + if (tlv) + i += 2+2+tlv->length; + + if (tlv && tlv->value) + free(tlv->value); + if (tlv) + free(tlv); + + tlvcount--; + } + } + + return 1; +} + +faim_internal int aim_parse_hostversions(struct aim_session_t *sess, + struct command_rx_struct *command, ...) +{ + rxcallback_t userfunc = NULL; + int ret = 1; + int vercount; + + vercount = (command->commandlen-10)/4; + + if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0018))) + ret = userfunc(sess, command, vercount, command->data+10); + + return ret; +} + +faim_internal int aim_handleredirect_middle(struct aim_session_t *sess, + struct command_rx_struct *command, ...) +{ + int serviceid = 0; + unsigned char *cookie = NULL; + char *ip = NULL; + rxcallback_t userfunc = NULL; + struct aim_tlvlist_t *tlvlist; + int ret = 1; + + tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10); + + if (aim_gettlv(tlvlist, 0x000d, 1)) + serviceid = aim_gettlv16(tlvlist, 0x000d, 1); + if (aim_gettlv(tlvlist, 0x0005, 1)) + ip = aim_gettlv_str(tlvlist, 0x0005, 1); + if (aim_gettlv(tlvlist, 0x0006, 1)) + cookie = aim_gettlv_str(tlvlist, 0x0006, 1); + + if ((serviceid == AIM_CONN_TYPE_CHAT) && sess->pendingjoin) { + + /* + * Chat hack. + * + */ + if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0005))) + ret = userfunc(sess, command, serviceid, ip, cookie, sess->pendingjoin, (int)sess->pendingjoinexchange); + free(sess->pendingjoin); + sess->pendingjoin = NULL; + sess->pendingjoinexchange = 0; + } else if (!serviceid || !ip || !cookie) { /* yeep! */ + ret = 1; + } else { + if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0005))) + ret = userfunc(sess, command, serviceid, ip, cookie); + } + + if (ip) + free(ip); + if (cookie) + free(cookie); + + aim_freetlvchain(&tlvlist); + + return ret; +} + +faim_internal int aim_parse_unknown(struct aim_session_t *sess, + struct command_rx_struct *command, ...) +{ + u_int i = 0; + + if (!sess || !command) + return 1; + + faimdprintf(sess, 1, "\nRecieved unknown packet:"); + + for (i = 0; i < command->commandlen; i++) + { + if ((i % 8) == 0) + faimdprintf(sess, 1, "\n\t"); + + faimdprintf(sess, 1, "0x%2x ", command->data[i]); + } + + faimdprintf(sess, 1, "\n\n"); + + return 1; +} + + +faim_internal int aim_negchan_middle(struct aim_session_t *sess, + struct command_rx_struct *command) +{ + struct aim_tlvlist_t *tlvlist; + char *msg = NULL; + unsigned short code = 0; + rxcallback_t userfunc = NULL; + int ret = 1; + + tlvlist = aim_readtlvchain(command->data, command->commandlen); + + if (aim_gettlv(tlvlist, 0x0009, 1)) + code = aim_gettlv16(tlvlist, 0x0009, 1); + + if (aim_gettlv(tlvlist, 0x000b, 1)) + msg = aim_gettlv_str(tlvlist, 0x000b, 1); + + if ((userfunc = aim_callhandler(sess, command->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR))) + ret = userfunc(sess, command, code, msg); + + aim_freetlvchain(&tlvlist); + + if (msg) + free(msg); + + return ret; +} + +/* + * aim_parse_generalerrs() + * + * Middle handler for 0x0001 snac of each family. + * + */ +faim_internal int aim_parse_generalerrs(struct aim_session_t *sess, + struct command_rx_struct *command, ...) +{ + unsigned short family; + unsigned short subtype; + int ret = 1; + int error = 0; + rxcallback_t userfunc = NULL; + + family = aimutil_get16(command->data+0); + subtype= aimutil_get16(command->data+2); + + if (command->commandlen > 10) + error = aimutil_get16(command->data+10); + + if ((userfunc = aim_callhandler(sess, command->conn, family, subtype))) + ret = userfunc(sess, command, error); + + return ret; +} + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfaim/rxqueue.c Mon Mar 05 03:59:32 2001 +0000 @@ -0,0 +1,252 @@ +/* + * aim_rxqueue.c + * + * This file contains the management routines for the receive + * (incoming packet) queue. The actual packet handlers are in + * aim_rxhandlers.c. + */ + +#define FAIM_INTERNAL +#include <aim.h> + +#ifndef _WIN32 +#include <sys/socket.h> +#endif + +/* + * Since not all implementations support MSG_WAITALL, define + * an alternate guarenteed read function... + * + * We keep recv() for systems that can do it because it means + * a single system call for the entire packet, where read may + * take more for a badly fragmented packet. + * + */ +faim_internal int aim_recv(int fd, void *buf, size_t count) +{ +#ifdef MSG_WAITALL + return recv(fd, buf, count, MSG_WAITALL); +#else + int left, ret, cur = 0; + + left = count; + + while (left) { + ret = recv(fd, ((unsigned char *)buf)+cur, left, 0); + if (ret == -1) + return -1; + if (ret == 0) + return cur; + + cur += ret; + left -= ret; + } + + return cur; +#endif +} + +/* + * Grab a single command sequence off the socket, and enqueue + * it in the incoming event queue in a seperate struct. + */ +faim_export int aim_get_command(struct aim_session_t *sess, struct aim_conn_t *conn) +{ + unsigned char generic[6]; + struct command_rx_struct *newrx = NULL; + + if (!sess || !conn) + return 0; + + if (conn->fd == -1) + return -1; /* its a aim_conn_close()'d connection */ + + if (conn->fd < 3) /* can happen when people abuse the interface */ + return 0; + + if (conn->status & AIM_CONN_STATUS_INPROGRESS) + return aim_conn_completeconnect(sess, conn); + + /* + * Rendezvous (client-client) connections do not speak + * FLAP, so this function will break on them. + */ + if (conn->type == AIM_CONN_TYPE_RENDEZVOUS) + return aim_get_command_rendezvous(sess, conn); + if (conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) { + faimdprintf(sess, 0, "out on fd %d\n", conn->fd); + return 0; + } + + /* + * Read FLAP header. Six bytes: + * + * 0 char -- Always 0x2a + * 1 char -- Channel ID. Usually 2 -- 1 and 4 are used during login. + * 2 short -- Sequence number + * 4 short -- Number of data bytes that follow. + */ + faim_mutex_lock(&conn->active); + if (aim_recv(conn->fd, generic, 6) < 6){ + aim_conn_close(conn); + faim_mutex_unlock(&conn->active); + return -1; + } + + /* + * This shouldn't happen unless the socket breaks, the server breaks, + * or we break. We must handle it just in case. + */ + if (generic[0] != 0x2a) { + faimdprintf(sess, 1, "Bad incoming data!"); + aim_conn_close(conn); + faim_mutex_unlock(&conn->active); + return -1; + } + + /* allocate a new struct */ + if (!(newrx = (struct command_rx_struct *)malloc(sizeof(struct command_rx_struct)))) { + faim_mutex_unlock(&conn->active); + return -1; + } + memset(newrx, 0x00, sizeof(struct command_rx_struct)); + + newrx->lock = 1; /* lock the struct */ + + /* we're doing OSCAR if we're here */ + newrx->hdrtype = AIM_FRAMETYPE_OSCAR; + + /* store channel -- byte 2 */ + newrx->hdr.oscar.type = (char) generic[1]; + + /* store seqnum -- bytes 3 and 4 */ + newrx->hdr.oscar.seqnum = aimutil_get16(generic+2); + + /* store commandlen -- bytes 5 and 6 */ + newrx->commandlen = aimutil_get16(generic+4); + + newrx->nofree = 0; /* free by default */ + + /* malloc for data portion */ + if (!(newrx->data = (u_char *) malloc(newrx->commandlen))) { + free(newrx); + faim_mutex_unlock(&conn->active); + return -1; + } + + /* read the data portion of the packet */ + if (aim_recv(conn->fd, newrx->data, newrx->commandlen) < newrx->commandlen){ + free(newrx->data); + free(newrx); + aim_conn_close(conn); + faim_mutex_unlock(&conn->active); + return -1; + } + faim_mutex_unlock(&conn->active); + + newrx->conn = conn; + + newrx->next = NULL; /* this will always be at the bottom */ + newrx->lock = 0; /* unlock */ + + /* enqueue this packet */ + if (sess->queue_incoming == NULL) { + sess->queue_incoming = newrx; + } else { + struct command_rx_struct *cur; + + /* + * This append operation takes a while. It might be faster + * if we maintain a pointer to the last entry in the queue + * and just update that. Need to determine if the overhead + * to maintain that is lower than the overhead for this loop. + */ + for (cur = sess->queue_incoming; cur->next; cur = cur->next) + ; + cur->next = newrx; + } + + newrx->conn->lastactivity = time(NULL); + + return 0; +} + +/* + * Purge recieve queue of all handled commands (->handled==1). Also + * allows for selective freeing using ->nofree so that the client can + * keep the data for various purposes. + * + * If ->nofree is nonzero, the frame will be delinked from the global list, + * but will not be free'ed. The client _must_ keep a pointer to the + * data -- libfaim will not! If the client marks ->nofree but + * does not keep a pointer, it's lost forever. + * + */ +faim_export void aim_purge_rxqueue(struct aim_session_t *sess) +{ + struct command_rx_struct *cur = NULL; + struct command_rx_struct *tmp; + + if (sess->queue_incoming == NULL) + return; + + if (sess->queue_incoming->next == NULL) { + if (sess->queue_incoming->handled) { + tmp = sess->queue_incoming; + sess->queue_incoming = NULL; + + if (!tmp->nofree) { + if (tmp->hdrtype == AIM_FRAMETYPE_OFT) + free(tmp->hdr.oft.hdr2); + free(tmp->data); + free(tmp); + } else + tmp->next = NULL; + } + return; + } + + for(cur = sess->queue_incoming; cur->next != NULL; ) { + if (cur->next->handled) { + tmp = cur->next; + cur->next = tmp->next; + if (!tmp->nofree) { + if (tmp->hdrtype == AIM_FRAMETYPE_OFT) + free(tmp->hdr.oft.hdr2); + free(tmp->data); + free(tmp); + } else + tmp->next = NULL; + } + cur = cur->next; + + /* + * Be careful here. Because of the way we just + * manipulated the pointer, cur may be NULL and + * the for() will segfault doing the check unless + * we find this case first. + */ + if (cur == NULL) + break; + } + + return; +} + +/* + * Since aim_get_command will aim_conn_kill dead connections, we need + * to clean up the rxqueue of unprocessed connections on that socket. + * + * XXX: this is something that was handled better in the old connection + * handling method, but eh. + */ +faim_internal void aim_rxqueue_cleanbyconn(struct aim_session_t *sess, struct aim_conn_t *conn) +{ + struct command_rx_struct *currx; + + for (currx = sess->queue_incoming; currx; currx = currx->next) { + if ((!currx->handled) && (currx->conn == conn)) + currx->handled = 1; + } + return; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfaim/search.c Mon Mar 05 03:59:32 2001 +0000 @@ -0,0 +1,122 @@ + +/* + * aim_search.c + * + * TODO: Add aim_usersearch_name() + * + */ + +#define FAIM_INTERNAL +#include <aim.h> + +faim_export unsigned long aim_usersearch_address(struct aim_session_t *sess, + struct aim_conn_t *conn, + char *address) +{ + struct command_tx_struct *newpacket; + + if (!address) + return -1; + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+strlen(address)))) + return -1; + + newpacket->lock = 1; + + aim_putsnac(newpacket->data, 0x000a, 0x0002, 0x0000, sess->snac_nextid); + + aimutil_putstr(newpacket->data+10, address, strlen(address)); + + aim_tx_enqueue(sess, newpacket); + + aim_cachesnac(sess, 0x000a, 0x0002, 0x0000, address, strlen(address)+1); + + return sess->snac_nextid; +} + + +faim_internal unsigned long aim_parse_searcherror(struct aim_session_t *sess, struct command_rx_struct *command) +{ + u_int i, ret; + int snacid; + rxcallback_t userfunc; + struct aim_snac_t *snac; + + i = 6; + + snacid = aimutil_get32(command->data+i); + i += 4; + + if(!(snac = aim_remsnac(sess, snacid))) { + faimdprintf(sess, 2, "faim: couldn't get a snac for %d, probably should crash.\n", snacid); + return 0; + } + + if((userfunc = aim_callhandler(sess, command->conn, 0x000a, 0x0001))) + ret = userfunc(sess, command, snac->data /* address */); + else + ret = 0; + + if(snac) { + if(snac->data) + free(snac->data); + free(snac); + } + + return ret; +} + + +faim_internal unsigned long aim_parse_searchreply(struct aim_session_t *sess, struct command_rx_struct *command) +{ + u_int i, j, m, ret; + int snacid; + struct aim_tlvlist_t *tlvlist; + char *cur = NULL, *buf = NULL; + rxcallback_t userfunc; + struct aim_snac_t *snac; + + i = 6; + + snacid = aimutil_get32(command->data+i); + i += 4; + + if(!(snac = aim_remsnac(sess, snacid))) { + faimdprintf(sess, 2, "faim: couldn't get a snac for %d, probably should crash.\n", snacid); + return 0; + } + + tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i); + + j = 0; + + m = aim_counttlvchain(&tlvlist); + + while((cur = aim_gettlv_str(tlvlist, 0x0001, j+1)) && j < m) { + if(!(buf = realloc(buf, (j+1) * (MAXSNLEN+1)))) + faimdprintf(sess, 2, "faim: couldn't realloc buf. oh well.\n"); + + strncpy(&buf[j * (MAXSNLEN+1)], cur, MAXSNLEN); + free(cur); + + j++; + } + + aim_freetlvchain(&tlvlist); + + if((userfunc = aim_callhandler(sess, command->conn, 0x000a, 0x0003))) + ret = userfunc(sess, command, snac->data /* address */, j, buf); + else + ret = 0; + + if(snac) { + if(snac->data) + free(snac->data); + free(snac); + } + + if(buf) + free(buf); + + return ret; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfaim/snac.c Mon Mar 05 03:59:32 2001 +0000 @@ -0,0 +1,177 @@ +/* + * + * Various SNAC-related dodads... + * + * outstanding_snacs is a list of aim_snac_t structs. A SNAC should be added + * whenever a new SNAC is sent and it should remain in the list until the + * response for it has been receieved. + * + * cleansnacs() should be called periodically by the client in order + * to facilitate the aging out of unreplied-to SNACs. This can and does + * happen, so it should be handled. + * + */ + +#define FAIM_INTERNAL +#include <aim.h> + +/* + * Called from aim_session_init() to initialize the hash. + */ +faim_internal void aim_initsnachash(struct aim_session_t *sess) +{ + int i; + + for (i = 0; i < FAIM_SNAC_HASH_SIZE; i++) { + sess->snac_hash[i] = NULL; + faim_mutex_init(&sess->snac_hash_locks[i]); + } + + return; +} + +faim_internal unsigned long aim_cachesnac(struct aim_session_t *sess, + const unsigned short family, + const unsigned short type, + const unsigned short flags, + const void *data, const int datalen) +{ + struct aim_snac_t snac; + + snac.id = sess->snac_nextid++; + snac.family = family; + snac.type = type; + snac.flags = flags; + + if (datalen) { + if (!(snac.data = malloc(datalen))) + return 0; /* er... */ + memcpy(snac.data, data, datalen); + } else + snac.data = NULL; + + return aim_newsnac(sess, &snac); +} + +/* + * Clones the passed snac structure and caches it in the + * list/hash. + */ +faim_internal unsigned long aim_newsnac(struct aim_session_t *sess, + struct aim_snac_t *newsnac) +{ + struct aim_snac_t *snac = NULL; + int index; + + if (!newsnac) + return 0; + + if (!(snac = calloc(1, sizeof(struct aim_snac_t)))) + return 0; + memcpy(snac, newsnac, sizeof(struct aim_snac_t)); + snac->issuetime = time(&snac->issuetime); + snac->next = NULL; + + index = snac->id % FAIM_SNAC_HASH_SIZE; + + faim_mutex_lock(&sess->snac_hash_locks[index]); + snac->next = sess->snac_hash[index]; + sess->snac_hash[index] = snac; + faim_mutex_unlock(&sess->snac_hash_locks[index]); + + return(snac->id); +} + +/* + * Finds a snac structure with the passed SNAC ID, + * removes it from the list/hash, and returns a pointer to it. + * + * The returned structure must be freed by the caller. + * + */ +faim_internal struct aim_snac_t *aim_remsnac(struct aim_session_t *sess, + u_long id) +{ + struct aim_snac_t *cur = NULL; + int index; + + index = id % FAIM_SNAC_HASH_SIZE; + + faim_mutex_lock(&sess->snac_hash_locks[index]); + if (!sess->snac_hash[index]) + ; + else if (sess->snac_hash[index]->id == id) { + cur = sess->snac_hash[index]; + sess->snac_hash[index] = cur->next; + } else { + cur = sess->snac_hash[index]; + while (cur->next) { + if (cur->next->id == id) { + struct aim_snac_t *tmp; + + tmp = cur->next; + cur->next = cur->next->next; + cur = tmp; + break; + } + cur = cur->next; + } + } + faim_mutex_unlock(&sess->snac_hash_locks[index]); + + return cur; +} + +/* + * This is for cleaning up old SNACs that either don't get replies or + * a reply was never received for. Garabage collection. Plain and simple. + * + * maxage is the _minimum_ age in seconds to keep SNACs. + * + */ +faim_internal int aim_cleansnacs(struct aim_session_t *sess, + int maxage) +{ + int i; + + for (i = 0; i < FAIM_SNAC_HASH_SIZE; i++) { + struct aim_snac_t *cur, **prev; + time_t curtime; + + faim_mutex_lock(&sess->snac_hash_locks[i]); + if (!sess->snac_hash[i]) { + faim_mutex_unlock(&sess->snac_hash_locks[i]); + continue; + } + + curtime = time(NULL); /* done here in case we waited for the lock */ + + for (prev = &sess->snac_hash[i]; (cur = *prev); ) { + if ((curtime - cur->issuetime) > maxage) { + + *prev = cur->next; + + /* XXX should we have destructors here? */ + if (cur->data) + free(cur->data); + free(cur); + + } else + prev = &cur->next; + } + + faim_mutex_unlock(&sess->snac_hash_locks[i]); + } + + return 0; +} + +faim_internal int aim_putsnac(u_char *buf, int family, int subtype, int flags, u_long snacid) +{ + int curbyte = 0; + curbyte += aimutil_put16(buf+curbyte, (u_short)(family&0xffff)); + curbyte += aimutil_put16(buf+curbyte, (u_short)(subtype&0xffff)); + curbyte += aimutil_put16(buf+curbyte, (u_short)(flags&0xffff)); + curbyte += aimutil_put32(buf+curbyte, snacid); + return curbyte; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfaim/tlv.c Mon Mar 05 03:59:32 2001 +0000 @@ -0,0 +1,724 @@ + +#define FAIM_INTERNAL +#include <aim.h> + +/** + * aim_readtlvchain - Read a TLV chain from a buffer. + * @buf: Input buffer + * @maxlen: Length of input buffer + * + * Reads and parses a series of TLV patterns from a data buffer; the + * returned structure is manipulatable with the rest of the TLV + * routines. When done with a TLV chain, aim_freetlvchain() should + * be called to free the dynamic substructures. + * + */ +faim_export struct aim_tlvlist_t *aim_readtlvchain(u_char *buf, int maxlen) +{ + int pos; + struct aim_tlvlist_t *list; + struct aim_tlvlist_t *cur; + + u_short type; + u_short length; + + if (!buf) + return NULL; + + list = NULL; + + pos = 0; + + while (pos < maxlen) + { + type = aimutil_get16(buf+pos); + pos += 2; + + if (pos < maxlen) + { + length = aimutil_get16(buf+pos); + pos += 2; + + if ((pos+length) <= maxlen) + { + /* + * Okay, so now AOL has decided that any TLV of + * type 0x0013 can only be two bytes, despite + * what the actual given length is. So here + * we dump any invalid TLVs of that sort. Hopefully + * theres no special cases to this special case. + * - mid (30jun2000) + */ + if ((type == 0x0013) && (length != 0x0002)) + length = 0x0002; + else { + cur = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t)); + memset(cur, 0x00, sizeof(struct aim_tlvlist_t)); + + cur->tlv = aim_createtlv(); + cur->tlv->type = type; + cur->tlv->length = length; + if (length) { + cur->tlv->value = (unsigned char *)malloc(length); + memcpy(cur->tlv->value, buf+pos, length); + } + + cur->next = list; + list = cur; + } + pos += length; + } + } + } + + return list; +} + +/** + * aim_freetlvchain - Free a TLV chain structure + * @list: Chain to be freed + * + * Walks the list of TLVs in the passed TLV chain and + * frees each one. Note that any references to this data + * should be removed before calling this. + * + */ +faim_export void aim_freetlvchain(struct aim_tlvlist_t **list) +{ + struct aim_tlvlist_t *cur, *cur2; + + if (!list || !(*list)) + return; + + cur = *list; + while (cur) + { + aim_freetlv(&cur->tlv); + cur2 = cur->next; + free(cur); + cur = cur2; + } + list = NULL; + return; +} + +/** + * aim_counttlvchain - Count the number of TLVs in a chain + * @list: Chain to be counted + * + * Returns the number of TLVs stored in the passed chain. + * + */ +faim_export int aim_counttlvchain(struct aim_tlvlist_t **list) +{ + struct aim_tlvlist_t *cur; + int count = 0; + + if (!list || !(*list)) + return 0; + + for (cur = *list; cur; cur = cur->next) + count++; + + return count; +} + +/** + * aim_sizetlvchain - Count the number of bytes in a TLV chain + * @list: Chain to be sized + * + * Returns the number of bytes that would be needed to + * write the passed TLV chain to a data buffer. + * + */ +faim_export int aim_sizetlvchain(struct aim_tlvlist_t **list) +{ + struct aim_tlvlist_t *cur; + int size = 0; + + if (!list || !(*list)) + return 0; + + for (cur = *list; cur; cur = cur->next) + size += (4 + cur->tlv->length); + + return size; +} + +/** + * aim_addtlvtochain_str - Add a string to a TLV chain + * @list: Desination chain (%NULL pointer if empty) + * @type: TLV type + * @str: String to add + * @len: Length of string to add (not including %NULL) + * + * Adds the passed string as a TLV element of the passed type + * to the TLV chain. + * + */ +faim_export int aim_addtlvtochain_str(struct aim_tlvlist_t **list, unsigned short type, char *str, int len) +{ + struct aim_tlvlist_t *newtlv; + struct aim_tlvlist_t *cur; + + if (!list) + return 0; + + newtlv = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t)); + memset(newtlv, 0x00, sizeof(struct aim_tlvlist_t)); + + newtlv->tlv = aim_createtlv(); + newtlv->tlv->type = type; + newtlv->tlv->length = len; + newtlv->tlv->value = (unsigned char *)malloc(newtlv->tlv->length*sizeof(unsigned char)); + memcpy(newtlv->tlv->value, str, newtlv->tlv->length); + + newtlv->next = NULL; + + if (*list == NULL) { + *list = newtlv; + } else if ((*list)->next == NULL) { + (*list)->next = newtlv; + } else { + for(cur = *list; cur->next; cur = cur->next) + ; + cur->next = newtlv; + } + return newtlv->tlv->length; +} + +/** + * aim_addtlvtochain16 - Add a 16bit integer to a TLV chain + * @list: Destination chain + * @type: TLV type to add + * @val: Value to add + * + * Adds a two-byte unsigned integer to a TLV chain. + * + */ +faim_export int aim_addtlvtochain16(struct aim_tlvlist_t **list, unsigned short type, unsigned short val) +{ + struct aim_tlvlist_t *newtl; + struct aim_tlvlist_t *cur; + + if (!list) + return 0; + + newtl = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t)); + memset(newtl, 0x00, sizeof(struct aim_tlvlist_t)); + + newtl->tlv = aim_createtlv(); + newtl->tlv->type = type; + newtl->tlv->length = 2; + newtl->tlv->value = (unsigned char *)malloc(newtl->tlv->length*sizeof(unsigned char)); + aimutil_put16(newtl->tlv->value, val); + + newtl->next = NULL; + + if (*list == NULL) { + *list = newtl; + } else if ((*list)->next == NULL) { + (*list)->next = newtl; + } else { + for(cur = *list; cur->next; cur = cur->next) + ; + cur->next = newtl; + } + return 2; +} + +/** + * aim_addtlvtochain32 - Add a 32bit integer to a TLV chain + * @list: Destination chain + * @type: TLV type to add + * @val: Value to add + * + * Adds a four-byte unsigned integer to a TLV chain. + * + */ +faim_export int aim_addtlvtochain32(struct aim_tlvlist_t **list, unsigned short type, unsigned long val) +{ + struct aim_tlvlist_t *newtl; + struct aim_tlvlist_t *cur; + + if (!list) + return 0; + + newtl = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t)); + memset(newtl, 0x00, sizeof(struct aim_tlvlist_t)); + + newtl->tlv = aim_createtlv(); + newtl->tlv->type = type; + newtl->tlv->length = 4; + newtl->tlv->value = (unsigned char *)malloc(newtl->tlv->length*sizeof(unsigned char)); + aimutil_put32(newtl->tlv->value, val); + + newtl->next = NULL; + + if (*list == NULL) { + *list = newtl; + } else if ((*list)->next == NULL) { + (*list)->next = newtl; + } else { + for(cur = *list; cur->next; cur = cur->next) + ; + cur->next = newtl; + } + return 4; +} + +/** + * aim_addtlvtochain_caps - Add a capability block to a TLV chain + * @list: Destination chain + * @type: TLV type to add + * @caps: Bitfield of capability flags to send + * + * Adds a block of capability blocks to a TLV chain. The bitfield + * passed in should be a bitwise %OR of any of the %AIM_CAPS constants: + * + * %AIM_CAPS_BUDDYICON Supports Buddy Icons + * + * %AIM_CAPS_VOICE Supports Voice Chat + * + * %AIM_CAPS_IMIMAGE Supports DirectIM/IMImage + * + * %AIM_CAPS_CHAT Supports Chat + * + * %AIM_CAPS_GETFILE Supports Get File functions + * + * %AIM_CAPS_SENDFILE Supports Send File functions + * + */ +faim_export int aim_addtlvtochain_caps(struct aim_tlvlist_t **list, unsigned short type, unsigned short caps) +{ + unsigned char buf[128]; /* icky fixed length buffer */ + struct aim_tlvlist_t *newtl; + struct aim_tlvlist_t *cur; + + if(!list) + return 0; + + newtl = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t)); + memset(newtl, 0x00, sizeof(struct aim_tlvlist_t)); + + newtl->tlv = aim_createtlv(); + newtl->tlv->type = type; + + newtl->tlv->length = aim_putcap(buf, sizeof(buf), caps); + newtl->tlv->value = (unsigned char *)calloc(1, newtl->tlv->length); + memcpy(newtl->tlv->value, buf, newtl->tlv->length); + + newtl->next = NULL; + + if (*list == NULL) { + *list = newtl; + } else if ((*list)->next == NULL) { + (*list)->next = newtl; + } else { + for(cur = *list; cur->next; cur = cur->next) + ; + cur->next = newtl; + } + return newtl->tlv->length; +} + +/** + * aim_addtlvtochain_noval - Add a blank TLV to a TLV chain + * @list: Destination chain + * @type: TLV type to add + * + * Adds a TLV with a zero length to a TLV chain. + * + */ +faim_internal int aim_addtlvtochain_noval(struct aim_tlvlist_t **list, unsigned short type) +{ + struct aim_tlvlist_t *newtlv; + struct aim_tlvlist_t *cur; + + newtlv = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t)); + memset(newtlv, 0x00, sizeof(struct aim_tlvlist_t)); + + newtlv->tlv = aim_createtlv(); + newtlv->tlv->type = type; + newtlv->tlv->length = 0; + newtlv->tlv->value = NULL; + + newtlv->next = NULL; + + if (*list == NULL) { + *list = newtlv; + } else if ((*list)->next == NULL) { + (*list)->next = newtlv; + } else { + for(cur = *list; cur->next; cur = cur->next) + ; + cur->next = newtlv; + } + return newtlv->tlv->length; +} + +/** + * aim_writetlvchain - Write a TLV chain into a data buffer. + * @buf: Destination buffer + * @buflen: Maximum number of bytes that will be written to buffer + * @list: Source TLV chain + * + * Copies a TLV chain into a raw data buffer, writing only the number + * of bytes specified. This operation does not free the chain; + * aim_freetlvchain() must still be called to free up the memory used + * by the chain structures. + * + */ +faim_export int aim_writetlvchain(u_char *buf, int buflen, struct aim_tlvlist_t **list) +{ + int goodbuflen = 0; + int i = 0; + struct aim_tlvlist_t *cur; + + if (!list || !buf || !buflen) + return 0; + + /* do an initial run to test total length */ + for (cur = *list; cur; cur = cur->next) { + goodbuflen += 2 + 2; /* type + len */ + goodbuflen += cur->tlv->length; + } + + if (goodbuflen > buflen) + return 0; /* not enough buffer */ + + /* do the real write-out */ + for (cur = *list; cur; cur = cur->next) { + i += aimutil_put16(buf+i, cur->tlv->type); + i += aimutil_put16(buf+i, cur->tlv->length); + memcpy(buf+i, cur->tlv->value, cur->tlv->length); + i += cur->tlv->length; + } + + return i; +} + + +/** + * aim_gettlv - Grab the Nth TLV of type type in the TLV list list. + * @list: Source chain + * @type: Requested TLV type + * @nth: Index of TLV of type to get + * + * Returns a pointer to an aim_tlv_t of the specified type; + * %NULL on error. The @nth parameter is specified starting at %1. + * In most cases, there will be no more than one TLV of any type + * in a chain. + * + */ +faim_export struct aim_tlv_t *aim_gettlv(struct aim_tlvlist_t *list, u_short type, int nth) +{ + int i; + struct aim_tlvlist_t *cur; + + i = 0; + for (cur = list; cur != NULL; cur = cur->next) + { + if (cur && cur->tlv) + { + if (cur->tlv->type == type) + i++; + if (i >= nth) + return cur->tlv; + } + } + return NULL; +} + +/** + * aim_gettlv_str - Retrieve the Nth TLV in chain as a string. + * @list: Source TLV chain + * @type: TLV type to search for + * @nth: Index of TLV to return + * + * Same as aim_gettlv(), except that the return value is a %NULL- + * terminated string instead of an aim_tlv_t. This is a + * dynamic buffer and must be freed by the caller. + * + */ +faim_export char *aim_gettlv_str(struct aim_tlvlist_t *list, u_short type, int nth) +{ + struct aim_tlv_t *tlv; + char *newstr; + + if (!(tlv = aim_gettlv(list, type, nth))) + return NULL; + + newstr = (char *) malloc(tlv->length + 1); + memcpy(newstr, tlv->value, tlv->length); + *(newstr + tlv->length) = '\0'; + + return newstr; +} + +/** + * aim_gettlv8 - Retrieve the Nth TLV in chain as a 8bit integer. + * @list: Source TLV chain + * @type: TLV type to search for + * @nth: Index of TLV to return + * + * Same as aim_gettlv(), except that the return value is a + * 8bit integer instead of an aim_tlv_t. + * + */ +faim_internal unsigned char aim_gettlv8(struct aim_tlvlist_t *list, unsigned short type, int num) +{ + struct aim_tlv_t *tlv; + + if (!(tlv = aim_gettlv(list, type, num)) || !tlv->value) + return 0; /* erm */ + return aimutil_get8(tlv->value); +} + +/** + * aim_gettlv16 - Retrieve the Nth TLV in chain as a 16bit integer. + * @list: Source TLV chain + * @type: TLV type to search for + * @nth: Index of TLV to return + * + * Same as aim_gettlv(), except that the return value is a + * 16bit integer instead of an aim_tlv_t. + * + */ +faim_internal unsigned short aim_gettlv16(struct aim_tlvlist_t *list, unsigned short type, int num) +{ + struct aim_tlv_t *tlv; + + if (!(tlv = aim_gettlv(list, type, num)) || !tlv->value) + return 0; /* erm */ + return aimutil_get16(tlv->value); +} + +/** + * aim_gettlv32 - Retrieve the Nth TLV in chain as a 32bit integer. + * @list: Source TLV chain + * @type: TLV type to search for + * @nth: Index of TLV to return + * + * Same as aim_gettlv(), except that the return value is a + * 32bit integer instead of an aim_tlv_t. + * + */ +faim_internal unsigned long aim_gettlv32(struct aim_tlvlist_t *list, unsigned short type, int num) +{ + struct aim_tlv_t *tlv; + + if (!(tlv = aim_gettlv(list, type, num)) || !tlv->value) + return 0; /* erm */ + return aimutil_get32(tlv->value); +} + +/** + * aim_grabtlv - Grab a single TLV from a data buffer + * @src: Source data buffer (must be at least 4 bytes long) + * + * Creates a TLV structure aim_tlv_t and returns it + * filled with values from a buffer, possibly including a + * dynamically allocated buffer for the value portion. + * + * Both the aim_tlv_t and the tlv->value pointer + * must be freed by the caller if non-%NULL. + * + */ +faim_export struct aim_tlv_t *aim_grabtlv(u_char *src) +{ + struct aim_tlv_t *dest = NULL; + + dest = aim_createtlv(); + + dest->type = src[0] << 8; + dest->type += src[1]; + + dest->length = src[2] << 8; + dest->length += src[3]; + + dest->value = (u_char *) malloc(dest->length*sizeof(u_char)); + memset(dest->value, 0, dest->length*sizeof(u_char)); + + memcpy(dest->value, &(src[4]), dest->length*sizeof(u_char)); + + return dest; +} + +/** + * aim_grabtlvstr - Grab a single TLV from a data buffer as string + * @src: Source data buffer (must be at least 4 bytes long) + * + * Creates a TLV structure aim_tlv_t and returns it + * filled with values from a buffer, possibly including a + * dynamically allocated buffer for the value portion, which + * is %NULL-terminated as a string. + * + * Both the aim_tlv_t and the tlv->value pointer + * must be freed by the caller if non-%NULL. + * + */ +faim_export struct aim_tlv_t *aim_grabtlvstr(u_char *src) +{ + struct aim_tlv_t *dest = NULL; + + dest = aim_createtlv(); + + dest->type = src[0] << 8; + dest->type += src[1]; + + dest->length = src[2] << 8; + dest->length += src[3]; + + dest->value = (u_char *) malloc((dest->length+1)*sizeof(u_char)); + memset(dest->value, 0, (dest->length+1)*sizeof(u_char)); + + memcpy(dest->value, &(src[4]), dest->length*sizeof(u_char)); + dest->value[dest->length] = '\0'; + + return dest; +} + +/** + * aim_puttlv - Write a aim_tlv_t into a data buffer + * @dest: Destination data buffer + * @newtlv: Source TLV structure + * + * Writes out the passed TLV structure into the buffer. No bounds + * checking is done on the output buffer. + * + * The passed aim_tlv_t is not freed. aim_freetlv() should + * still be called by the caller to free the structure. + * + */ +faim_export int aim_puttlv(u_char *dest, struct aim_tlv_t *newtlv) +{ + int i=0; + + dest[i++] = newtlv->type >> 8; + dest[i++] = newtlv->type & 0x00FF; + dest[i++] = newtlv->length >> 8; + dest[i++] = newtlv->length & 0x00FF; + memcpy(&(dest[i]), newtlv->value, newtlv->length); + i+=newtlv->length; + return i; +} + +/** + * aim_createtlv - Generate an aim_tlv_t structure. + * + * Allocates an empty TLV structure and returns a pointer + * to it; %NULL on error. + * + */ +faim_export struct aim_tlv_t *aim_createtlv(void) +{ + struct aim_tlv_t *newtlv; + + if (!(newtlv = (struct aim_tlv_t *)malloc(sizeof(struct aim_tlv_t)))) + return NULL; + memset(newtlv, 0, sizeof(struct aim_tlv_t)); + return newtlv; +} + +/** + * aim_freetlv - Free a aim_tlv_t structure + * @oldtlv: TLV to be destroyed + * + * Frees both the TLV structure and the value portion. + * + */ +faim_export int aim_freetlv(struct aim_tlv_t **oldtlv) +{ + if (!oldtlv) + return -1; + if (!*oldtlv) + return -1; + if ((*oldtlv)->value) + free((*oldtlv)->value); + free(*(oldtlv)); + (*oldtlv) = NULL; + + return 0; +} + +/** + * aim_puttlv_8 - Write a one-byte TLV. + * @buf: Destination buffer + * @t: TLV type + * @v: Value + * + * Writes a TLV with a one-byte integer value portion. + * + */ +faim_export int aim_puttlv_8(unsigned char *buf, unsigned short t, unsigned char v) +{ + int curbyte=0; + + curbyte += aimutil_put16(buf+curbyte, (unsigned short)(t&0xffff)); + curbyte += aimutil_put16(buf+curbyte, (unsigned short)0x0001); + curbyte += aimutil_put8(buf+curbyte, (unsigned char)(v&0xff)); + + return curbyte; +} + +/** + * aim_puttlv_16 - Write a two-byte TLV. + * @buf: Destination buffer + * @t: TLV type + * @v: Value + * + * Writes a TLV with a two-byte integer value portion. + * + */ +faim_export int aim_puttlv_16(u_char *buf, u_short t, u_short v) +{ + int curbyte=0; + curbyte += aimutil_put16(buf+curbyte, (u_short)(t&0xffff)); + curbyte += aimutil_put16(buf+curbyte, (u_short)0x0002); + curbyte += aimutil_put16(buf+curbyte, (u_short)(v&0xffff)); + return curbyte; +} + +/** + * aim_puttlv_32 - Write a four-byte TLV. + * @buf: Destination buffer + * @t: TLV type + * @v: Value + * + * Writes a TLV with a four-byte integer value portion. + * + */ +faim_export int aim_puttlv_32(u_char *buf, u_short t, u_long v) +{ + int curbyte=0; + curbyte += aimutil_put16(buf+curbyte, (u_short)(t&0xffff)); + curbyte += aimutil_put16(buf+curbyte, (u_short)0x0004); + curbyte += aimutil_put32(buf+curbyte, (u_long)(v&0xffffffff)); + return curbyte; +} + +/** + * aim_puttlv_str - Write a string TLV. + * @buf: Destination buffer + * @t: TLV type + * @l: Length of string + * @v: String to write + * + * Writes a TLV with a string value portion. (Only the first @l + * bytes of the passed string will be written, which should not + * include the terminating NULL.) + * + */ +faim_export int aim_puttlv_str(u_char *buf, u_short t, int l, char *v) +{ + int curbyte; + + curbyte = 0; + curbyte += aimutil_put16(buf+curbyte, (u_short)(t&0xffff)); + curbyte += aimutil_put16(buf+curbyte, (u_short)(l&0xffff)); + if (v) + memcpy(buf+curbyte, (unsigned char *)v, l); + curbyte += l; + return curbyte; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfaim/txqueue.c Mon Mar 05 03:59:32 2001 +0000 @@ -0,0 +1,431 @@ +/* + * aim_txqueue.c + * + * Herein lies all the mangement routines for the transmit (Tx) queue. + * + */ + +#define FAIM_INTERNAL +#include <aim.h> + +#ifndef _WIN32 +#include <sys/socket.h> +#endif + +/* + * Allocate a new tx frame. + * + * This is more for looks than anything else. + * + * Right now, that is. If/when we implement a pool of transmit + * frames, this will become the request-an-unused-frame part. + * + * framing = AIM_FRAMETYPE_OFT/OSCAR + * chan = channel for OSCAR, hdrtype for OFT + * + */ +faim_internal struct command_tx_struct *aim_tx_new(struct aim_session_t *sess, struct aim_conn_t *conn, unsigned char framing, int chan, int datalen) +{ + struct command_tx_struct *newtx; + + if (!conn) { + faimdprintf(sess, 0, "aim_tx_new: ERROR: no connection specified\n"); + return NULL; + } + + /* For sanity... */ + if ((conn->type == AIM_CONN_TYPE_RENDEZVOUS) || (conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT)) { + if (framing != AIM_FRAMETYPE_OFT) { + faimdprintf(sess, 0, "aim_tx_new: attempted to allocate inappropriate frame type for rendezvous connection\n"); + return NULL; + } + } else { + if (framing != AIM_FRAMETYPE_OSCAR) { + faimdprintf(sess, 0, "aim_tx_new: attempted to allocate inappropriate frame type for FLAP connection\n"); + return NULL; + } + } + + newtx = (struct command_tx_struct *)malloc(sizeof(struct command_tx_struct)); + if (!newtx) + return NULL; + memset(newtx, 0, sizeof(struct command_tx_struct)); + + newtx->conn = conn; + + if(datalen) { + newtx->data = (unsigned char *)malloc(datalen); + newtx->commandlen = datalen; + } else + newtx->data = NULL; + + newtx->hdrtype = framing; + if (newtx->hdrtype == AIM_FRAMETYPE_OSCAR) { + newtx->hdr.oscar.type = chan; + } else if (newtx->hdrtype == AIM_FRAMETYPE_OFT) { + newtx->hdr.oft.type = chan; + newtx->hdr.oft.hdr2len = 0; /* this will get setup by caller */ + } else { + faimdprintf(sess, 0, "tx_new: unknown framing\n"); + } + + return newtx; +} + +/* + * aim_tx_enqeue__queuebased() + * + * The overall purpose here is to enqueue the passed in command struct + * into the outgoing (tx) queue. Basically... + * 1) Make a scope-irrelevent copy of the struct + * 2) Lock the struct + * 3) Mark as not-sent-yet + * 4) Enqueue the struct into the list + * 5) Unlock the struct once it's linked in + * 6) Return + * + * Note that this is only used when doing queue-based transmitting; + * that is, when sess->tx_enqueue is set to &aim_tx_enqueue__queuebased. + * + */ +static int aim_tx_enqueue__queuebased(struct aim_session_t *sess, struct command_tx_struct *newpacket) +{ + struct command_tx_struct *cur; + + if (newpacket->conn == NULL) { + faimdprintf(sess, 1, "aim_tx_enqueue: WARNING: enqueueing packet with no connecetion\n"); + newpacket->conn = aim_getconn_type(sess, AIM_CONN_TYPE_BOS); + } + + if (newpacket->hdrtype == AIM_FRAMETYPE_OSCAR) { + /* assign seqnum */ + newpacket->hdr.oscar.seqnum = aim_get_next_txseqnum(newpacket->conn); + } + /* set some more fields */ + newpacket->lock = 1; /* lock */ + newpacket->sent = 0; /* not sent yet */ + newpacket->next = NULL; /* always last */ + + /* see overhead note in aim_rxqueue counterpart */ + if (sess->queue_outgoing == NULL) { + sess->queue_outgoing = newpacket; + } else { + for (cur = sess->queue_outgoing; + cur->next; + cur = cur->next) + ; + cur->next = newpacket; + } + + newpacket->lock = 0; /* unlock so it can be sent */ + + return 0; +} + +/* + * aim_tx_enqueue__immediate() + * + * Parallel to aim_tx_enqueue__queuebased, however, this bypasses + * the whole queue mess when you want immediate writes to happen. + * + * Basically the same as its __queuebased couterpart, however + * instead of doing a list append, it just calls aim_tx_sendframe() + * right here. + * + */ +static int aim_tx_enqueue__immediate(struct aim_session_t *sess, struct command_tx_struct *newpacket) +{ + if (newpacket->conn == NULL) { + faimdprintf(sess, 1, "aim_tx_enqueue: ERROR: packet has no connection\n"); + if (newpacket->data) + free(newpacket->data); + free(newpacket); + return -1; + } + + if (newpacket->hdrtype == AIM_FRAMETYPE_OSCAR) + newpacket->hdr.oscar.seqnum = aim_get_next_txseqnum(newpacket->conn); + + newpacket->lock = 1; /* lock */ + newpacket->sent = 0; /* not sent yet */ + + aim_tx_sendframe(sess, newpacket); + + if (newpacket->data) + free(newpacket->data); + free(newpacket); + + return 0; +} + +faim_export int aim_tx_setenqueue(struct aim_session_t *sess, int what, int (*func)(struct aim_session_t *, struct command_tx_struct *)) +{ + if (!sess) + return -1; + + if (what == AIM_TX_QUEUED) + sess->tx_enqueue = &aim_tx_enqueue__queuebased; + else if (what == AIM_TX_IMMEDIATE) + sess->tx_enqueue = &aim_tx_enqueue__immediate; + else if (what == AIM_TX_USER) { + if (!func) + return -1; + sess->tx_enqueue = func; + } else + return -1; /* unknown action */ + + return 0; +} + +faim_internal int aim_tx_enqueue(struct aim_session_t *sess, struct command_tx_struct *command) +{ + /* + * If we want to send a connection thats inprogress, we have to force + * them to use the queue based version. Otherwise, use whatever they + * want. + */ + if (command && command->conn && (command->conn->status & AIM_CONN_STATUS_INPROGRESS)) { + return aim_tx_enqueue__queuebased(sess, command); + } + return (*sess->tx_enqueue)(sess, command); +} + +/* + * aim_get_next_txseqnum() + * + * This increments the tx command count, and returns the seqnum + * that should be stamped on the next FLAP packet sent. This is + * normally called during the final step of packet preparation + * before enqueuement (in aim_tx_enqueue()). + * + */ +faim_internal unsigned int aim_get_next_txseqnum(struct aim_conn_t *conn) +{ + u_int ret; + + faim_mutex_lock(&conn->seqnum_lock); + ret = ++conn->seqnum; + faim_mutex_unlock(&conn->seqnum_lock); + return ret; +} + +/* + * aim_tx_flushqueue() + * + * This the function is responsable for putting the queued commands + * onto the wire. This function is critical to the operation of + * the queue and therefore is the most prone to brokenness. It + * seems to be working quite well at this point. + * + * Procedure: + * 1) Traverse the list, only operate on commands that are unlocked + * and haven't been sent yet. + * 2) Lock the struct + * 3) Allocate a temporary buffer to store the finished, fully + * processed packet in. + * 4) Build the packet from the command_tx_struct data. + * 5) Write the packet to the socket. + * 6) If success, mark the packet sent, if fail report failure, do NOT + * mark the packet sent (so it will not get purged and therefore + * be attempted again on next call). + * 7) Unlock the struct. + * 8) Free the temp buffer + * 9) Step to next struct in list and go back to 1. + * + */ +faim_internal int aim_tx_sendframe(struct aim_session_t *sess, struct command_tx_struct *cur) +{ + int buflen = 0; + unsigned char *curPacket; + + if (!cur) + return -1; /* fatal */ + + cur->lock = 1; /* lock the struct */ + + if (cur->hdrtype == AIM_FRAMETYPE_OSCAR) + buflen = cur->commandlen + 6; + else if (cur->hdrtype == AIM_FRAMETYPE_OFT) + buflen = cur->hdr.oft.hdr2len + 8; + else { + cur->lock = 0; + return -1; + } + + /* allocate full-packet buffer */ + if (!(curPacket = (unsigned char *) malloc(buflen))) { + cur->lock = 0; + return -1; + } + + if (cur->hdrtype == AIM_FRAMETYPE_OSCAR) { + /* command byte */ + curPacket[0] = 0x2a; + + /* type/family byte */ + curPacket[1] = cur->hdr.oscar.type; + + /* bytes 3+4: word: FLAP sequence number */ + aimutil_put16(curPacket+2, cur->hdr.oscar.seqnum); + + /* bytes 5+6: word: SNAC len */ + aimutil_put16(curPacket+4, cur->commandlen); + + /* bytes 7 and on: raw: SNAC data */ /* XXX: ye gods! get rid of this! */ + memcpy(&(curPacket[6]), cur->data, cur->commandlen); + + } else if (cur->hdrtype == AIM_FRAMETYPE_OFT) { + int z = 0; + + z += aimutil_put8(curPacket+z, cur->hdr.oft.magic[0]); + z += aimutil_put8(curPacket+z, cur->hdr.oft.magic[1]); + z += aimutil_put8(curPacket+z, cur->hdr.oft.magic[2]); + z += aimutil_put8(curPacket+z, cur->hdr.oft.magic[3]); + + z += aimutil_put16(curPacket+z, cur->hdr.oft.hdr2len + 8); + z += aimutil_put16(curPacket+z, cur->hdr.oft.type); + + memcpy(curPacket+z, cur->hdr.oft.hdr2, cur->hdr.oft.hdr2len); + } + + /* + * For OSCAR, a full image of the raw packet data now in curPacket. + * For OFT, an image of just the bloated header is in curPacket, + * since OFT allows us to do the data in a different write (yay!). + */ + faim_mutex_lock(&cur->conn->active); + if (send(cur->conn->fd, curPacket, buflen, 0) != buflen) { + faim_mutex_unlock(&cur->conn->active); + cur->sent = 1; + aim_conn_close(cur->conn); + return 0; /* bail out */ + } + + if ((cur->hdrtype == AIM_FRAMETYPE_OFT) && cur->commandlen) { + int curposi; + for(curposi = 0; curposi < cur->commandlen; curposi++) + faimdprintf(sess, 0, "%02x ", cur->data[curposi]); + + if (send(cur->conn->fd, cur->data, cur->commandlen, 0) != (int)cur->commandlen) { + /* + * Theres nothing we can do about this since we've already sent the + * header! The connection is unstable. + */ + faim_mutex_unlock(&cur->conn->active); + cur->sent = 1; + aim_conn_close(cur->conn); + return 0; /* bail out */ + } + + } + + cur->sent = 1; /* mark the struct as sent */ + cur->conn->lastactivity = time(NULL); + + faim_mutex_unlock(&cur->conn->active); + + if (sess->debug >= 2) { + int i; + + faimdprintf(sess, 2, "\nOutgoing packet: (only valid for OSCAR)"); + for (i = 0; i < buflen; i++) { + if (!(i % 8)) + faimdprintf(sess, 2, "\n\t"); + faimdprintf(sess, 2, "0x%02x ", curPacket[i]); + } + faimdprintf(sess, 2, "\n"); + } + + cur->lock = 0; /* unlock the struct */ + + free(curPacket); /* free up full-packet buffer */ + + return 1; /* success */ +} + +faim_export int aim_tx_flushqueue(struct aim_session_t *sess) +{ + struct command_tx_struct *cur; + + if (sess->queue_outgoing == NULL) + return 0; + + faimdprintf(sess, 2, "beginning txflush...\n"); + for (cur = sess->queue_outgoing; cur; cur = cur->next) { + /* only process if its unlocked and unsent */ + if (!cur->lock && !cur->sent) { + + if (cur->conn && (cur->conn->status & AIM_CONN_STATUS_INPROGRESS)) + continue; + + /* + * And now for the meager attempt to force transmit + * latency and avoid missed messages. + */ + if ((cur->conn->lastactivity + cur->conn->forcedlatency) >= time(NULL)) { + /* FIXME FIXME -- should be a break! we dont want to block the upper layers */ + sleep((cur->conn->lastactivity + cur->conn->forcedlatency) - time(NULL)); + } + + /* XXX XXX XXX this should call the custom "queuing" function!! */ + if (aim_tx_sendframe(sess, cur) == -1) + break; + } + } + + /* purge sent commands from queue */ + aim_tx_purgequeue(sess); + + return 0; +} + +/* + * aim_tx_purgequeue() + * + * This is responsable for removing sent commands from the transmit + * queue. This is not a required operation, but it of course helps + * reduce memory footprint at run time! + * + */ +faim_export void aim_tx_purgequeue(struct aim_session_t *sess) +{ + struct command_tx_struct *cur = NULL; + struct command_tx_struct *tmp; + + if (sess->queue_outgoing == NULL) + return; + + if (sess->queue_outgoing->next == NULL) { + if (!sess->queue_outgoing->lock && sess->queue_outgoing->sent) { + tmp = sess->queue_outgoing; + sess->queue_outgoing = NULL; + if (tmp->hdrtype == AIM_FRAMETYPE_OFT) + free(tmp->hdr.oft.hdr2); + free(tmp->data); + free(tmp); + } + return; + } + + for(cur = sess->queue_outgoing; cur->next != NULL; ) { + if (!cur->next->lock && cur->next->sent) { + tmp = cur->next; + cur->next = tmp->next; + if (tmp->hdrtype == AIM_FRAMETYPE_OFT) + free(tmp->hdr.oft.hdr2); + free(tmp->data); + free(tmp); + } + cur = cur->next; + + /* + * Be careful here. Because of the way we just + * manipulated the pointer, cur may be NULL and + * the for() will segfault doing the check unless + * we find this case first. + */ + if (cur == NULL) + break; + } + return; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfaim/util.c Mon Mar 05 03:59:32 2001 +0000 @@ -0,0 +1,265 @@ +/* + * + * + * + */ + +#include <aim.h> +#include <ctype.h> + +#ifdef AIMUTIL_USEMACROS +/* macros in faim/aim.h */ +#else +faim_shortfunc int aimutil_put8(u_char *buf, u_char data) +{ + buf[0] = (u_char)data&0xff; + return 1; +} + +faim_shortfunc u_char aimutil_get8(u_char *buf) +{ + return buf[0]; +} + +/* + * Endian-ness issues here? + */ +faim_shortfunc int aimutil_put16(u_char *buf, u_short data) +{ + buf[0] = (u_char)(data>>8)&0xff; + buf[1] = (u_char)(data)&0xff; + return 2; +} + +faim_shortfunc u_short aimutil_get16(u_char *buf) +{ + u_short val; + val = (buf[0] << 8) & 0xff00; + val+= (buf[1]) & 0xff; + return val; +} + +faim_shortfunc int aimutil_put32(u_char *buf, u_long data) +{ + buf[0] = (u_char)(data>>24)&0xff; + buf[1] = (u_char)(data>>16)&0xff; + buf[2] = (u_char)(data>>8)&0xff; + buf[3] = (u_char)(data)&0xff; + return 4; +} + +faim_shortfunc u_long aimutil_get32(u_char *buf) +{ + u_long val; + val = (buf[0] << 24) & 0xff000000; + val+= (buf[1] << 16) & 0x00ff0000; + val+= (buf[2] << 8) & 0x0000ff00; + val+= (buf[3] ) & 0x000000ff; + return val; +} +#endif /* AIMUTIL_USEMACROS */ + +faim_export faim_shortfunc int aimutil_putstr(u_char *dest, const char *src, int len) +{ + memcpy(dest, src, len); + return len; +} + +/* + * Tokenizing functions. Used to portably replace strtok/sep. + * -- DMP. + * + */ +faim_export int aimutil_tokslen(char *toSearch, int index, char dl) +{ + int curCount = 1; + char *next; + char *last; + int toReturn; + + last = toSearch; + next = strchr(toSearch, dl); + + while(curCount < index && next != NULL) + { + curCount++; + last = next + 1; + next = strchr(last, dl); + } + + if ((curCount < index) || (next == NULL)) + toReturn = strlen(toSearch) - (curCount - 1); + else + toReturn = next - toSearch - (curCount - 1); + + return toReturn; +} + +faim_export int aimutil_itemcnt(char *toSearch, char dl) +{ + int curCount; + char *next; + + curCount = 1; + + next = strchr(toSearch, dl); + + while(next != NULL) + { + curCount++; + next = strchr(next + 1, dl); + } + + return curCount; +} + +faim_export char *aimutil_itemidx(char *toSearch, int index, char dl) +{ + int curCount; + char *next; + char *last; + char *toReturn; + + curCount = 0; + + last = toSearch; + next = strchr(toSearch, dl); + + while(curCount < index && next != NULL) + { + curCount++; + last = next + 1; + next = strchr(last, dl); + } + + if (curCount < index) + { + toReturn = malloc(sizeof(char)); + *toReturn = '\0'; + } + next = strchr(last, dl); + + if (curCount < index) + { + toReturn = malloc(sizeof(char)); + *toReturn = '\0'; + } + else + { + if (next == NULL) + { + toReturn = malloc((strlen(last) + 1) * sizeof(char)); + strcpy(toReturn, last); + } + else + { + toReturn = malloc((next - last + 1) * sizeof(char)); + memcpy(toReturn, last, (next - last)); + toReturn[next - last] = '\0'; + } + } + return toReturn; +} + +/* + * int snlen(const char *) + * + * This takes a screen name and returns its length without + * spaces. If there are no spaces in the SN, then the + * return is equal to that of strlen(). + * + */ +faim_export int aim_snlen(const char *sn) +{ + int i = 0; + const char *curPtr = NULL; + + if (!sn) + return 0; + + curPtr = sn; + while ( (*curPtr) != (char) NULL) { + if ((*curPtr) != ' ') + i++; + curPtr++; + } + + return i; +} + +/* + * int sncmp(const char *, const char *) + * + * This takes two screen names and compares them using the rules + * on screen names for AIM/AOL. Mainly, this means case and space + * insensitivity (all case differences and spacing differences are + * ignored). + * + * Return: 0 if equal + * non-0 if different + * + */ + +faim_export int aim_sncmp(const char *sn1, const char *sn2) +{ + const char *curPtr1 = NULL, *curPtr2 = NULL; + + if (aim_snlen(sn1) != aim_snlen(sn2)) + return 1; + + curPtr1 = sn1; + curPtr2 = sn2; + while ( (*curPtr1 != (char) NULL) && (*curPtr2 != (char) NULL) ) { + if ( (*curPtr1 == ' ') || (*curPtr2 == ' ') ) { + if (*curPtr1 == ' ') + curPtr1++; + if (*curPtr2 == ' ') + curPtr2++; + } else { + if ( toupper(*curPtr1) != toupper(*curPtr2)) + return 1; + curPtr1++; + curPtr2++; + } + } + + return 0; +} + +/* strsep Copyright (C) 1992, 1993 Free Software Foundation, Inc. + strsep is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, write to the Free Software Foundation, Inc., 675 Mass Ave, + Cambridge, MA 02139, USA. */ + +/* Minor changes by and1000 on 15/1/97 to make it go under Nemesis */ + +faim_export char *aim_strsep(char **pp, const char *delim) +{ + char *p, *q; + + if (!(p = *pp)) + return 0; + + if ((q = strpbrk (p, delim))) + { + *pp = q + 1; + *q = '\0'; + } + else + *pp = 0; + + return p; +}
--- a/src/dialogs.c Sun Mar 04 22:37:18 2001 +0000 +++ b/src/dialogs.c Mon Mar 05 03:59:32 2001 +0000 @@ -141,6 +141,7 @@ struct findbyemail { GtkWidget *window; GtkWidget *emailentry; + struct gaim_connection *gc; }; struct findbyinfo { @@ -2008,8 +2009,9 @@ email = gtk_entry_get_text(GTK_ENTRY(b->emailentry)); - /* FIXME : email search. not sure if even works; not important */ - if (connections) + if (b->gc) + serv_dir_search(b->gc, "","","","","","","", email); + else serv_dir_search(connections->data, "","","","","","","", email); destroy_dialog(NULL, b->window); @@ -2175,7 +2177,7 @@ gtk_widget_show(b->window); } -void show_find_email() +void show_find_email(struct gaim_connection *gc) { GtkWidget *label; GtkWidget *bbox; @@ -2185,6 +2187,8 @@ GtkWidget *button; struct findbyemail *b = g_new0(struct findbyemail, 1); + if (g_slist_find(connections, gc)) + b->gc = gc; b->window = gtk_window_new(GTK_WINDOW_DIALOG); gtk_window_set_policy(GTK_WINDOW(b->window), FALSE, TRUE, TRUE); gtk_window_set_wmclass(GTK_WINDOW(b->window), "find_email", "Gaim");
--- a/src/gaim.h Sun Mar 04 22:37:18 2001 +0000 +++ b/src/gaim.h Mon Mar 05 03:59:32 2001 +0000 @@ -771,7 +771,7 @@ extern void show_import_dialog(); extern void show_new_bp(); extern void show_log_dialog(struct conversation *); -extern void show_find_email(); +extern void show_find_email(struct gaim_connection *gc); extern void show_find_info(); extern void g_show_info (struct aim_user *, char *); extern void g_show_info_text (char *);
--- a/src/oscar.c Sun Mar 04 22:37:18 2001 +0000 +++ b/src/oscar.c Mon Mar 05 03:59:32 2001 +0000 @@ -40,7 +40,7 @@ #include "multi.h" #include "prpl.h" #include "gaim.h" -#include "faim/aim.h" +#include "aim.h" #include "pixmaps/cancel.xpm" #include "pixmaps/admin_icon.xpm" @@ -61,12 +61,18 @@ struct aim_session_t *sess; struct aim_conn_t *conn; - int cnpa; - int paspa; + guint cnpa; + guint paspa; int create_exchange; char *create_name; + gboolean conf; + gboolean reqemail; + gboolean chpass; + char *oldp; + char *newp; + GSList *oscar_chats; GSList *direct_ims; GSList *getfiles; @@ -183,21 +189,6 @@ return c; } -static struct gaim_connection *find_gaim_conn_by_aim_sess(struct aim_session_t *sess) { - GSList *g = connections; - struct gaim_connection *gc = NULL; - - while (g) { - gc = (struct gaim_connection *)g->data; - if (sess == ((struct oscar_data *)gc->proto_data)->sess) - break; - g = g->next; - gc = NULL; - } - - return gc; -} - static struct gaim_connection *find_gaim_conn_by_oscar_conn(struct aim_conn_t *conn) { GSList *g = connections; struct gaim_connection *c = NULL; @@ -227,6 +218,8 @@ static int gaim_parse_login (struct aim_session_t *, struct command_rx_struct *, ...); static int gaim_server_ready (struct aim_session_t *, struct command_rx_struct *, ...); static int gaim_handle_redirect (struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_info_change (struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_account_confirm (struct aim_session_t *, struct command_rx_struct *, ...); static int gaim_parse_oncoming (struct aim_session_t *, struct command_rx_struct *, ...); static int gaim_parse_offgoing (struct aim_session_t *, struct command_rx_struct *, ...); static int gaim_parse_incoming_im(struct aim_session_t *, struct command_rx_struct *, ...); @@ -241,6 +234,8 @@ static int gaim_parse_msgack (struct aim_session_t *, struct command_rx_struct *, ...); static int gaim_parse_ratechange (struct aim_session_t *, struct command_rx_struct *, ...); static int gaim_parse_evilnotify (struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_parse_searcherror(struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_parse_searchreply(struct aim_session_t *, struct command_rx_struct *, ...); static int gaim_bosrights (struct aim_session_t *, struct command_rx_struct *, ...); static int gaim_rateresp (struct aim_session_t *, struct command_rx_struct *, ...); static int gaim_reportinterval (struct aim_session_t *, struct command_rx_struct *, ...); @@ -320,19 +315,25 @@ char buf[BUF_LONG]; debug_printf("disconnected from chat room %s\n", c->name); c->conn = NULL; - if (c->inpa > -1) + if (c->inpa > 0) gdk_input_remove(c->inpa); - c->inpa = -1; + c->inpa = 0; c->fd = -1; aim_conn_kill(odata->sess, &conn); sprintf(buf, _("You have been disconnected from chat room %s."), c->name); do_error_dialog(buf, _("Chat Error!")); } else if (conn->type == AIM_CONN_TYPE_CHATNAV) { - if (odata->cnpa > -1) + if (odata->cnpa > 0) gdk_input_remove(odata->cnpa); - odata->cnpa = -1; + odata->cnpa = 0; debug_printf("removing chatnav input watcher\n"); aim_conn_kill(odata->sess, &conn); + } else if (conn->type == AIM_CONN_TYPE_AUTH) { + if (odata->paspa > 0) + gdk_input_remove(odata->paspa); + odata->paspa = 0; + debug_printf("removing authconn input watcher\n"); + aim_conn_kill(odata->sess, &conn); } else if (conn->type == AIM_CONN_TYPE_RENDEZVOUS) { debug_printf("No handler for rendezvous disconnect (%d).\n", source); @@ -347,6 +348,21 @@ } } +static void oscar_debug(struct aim_session_t *sess, int level, const char *format, va_list va) { + char *s = g_strdup_vprintf(format, va); + char buf[256]; + char *t; + struct gaim_connection *gc = sess->aux_data; + + g_snprintf(buf, sizeof(buf), "%s %d: ", gc->username, level); + t = g_strconcat(buf, s, NULL); + debug_printf(t); + if (t[strlen(t)-1] != '\n') + debug_printf("\n"); + g_free(t); + g_free(s); +} + void oscar_login(struct aim_user *user) { struct aim_session_t *sess; struct aim_conn_t *conn; @@ -360,7 +376,8 @@ sess = g_new0(struct aim_session_t, 1); - aim_session_init(sess, AIM_SESS_FLAGS_NONBLOCKCONNECT); + aim_session_init(sess, AIM_SESS_FLAGS_NONBLOCKCONNECT, 0); + aim_setdebuggingcb(sess, oscar_debug); if (user->proto_opt[USEROPT_SOCKSHOST][0]) { char *finalproxy; @@ -383,8 +400,9 @@ /* we need an immediate queue because we don't use a while-loop to * see if things need to be sent. */ - sess->tx_enqueue = &aim_tx_enqueue__immediate; + aim_tx_setenqueue(sess, AIM_TX_IMMEDIATE, NULL); odata->sess = sess; + sess->aux_data = gc; conn = aim_newconn(sess, AIM_CONN_TYPE_AUTH, finalauth ? finalauth : FAIM_LOGIN_SERVER); @@ -459,7 +477,7 @@ char *latestreleaseurl = NULL, *latestbetaurl = NULL; char *latestreleaseinfo = NULL, *latestbetainfo = NULL; - struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess); + struct gaim_connection *gc = sess->aux_data; va_start(ap, command); sn = va_arg(ap, char *); @@ -555,11 +573,11 @@ 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_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_LOC, AIM_CB_LOC_USERINFO, gaim_parse_user_info, 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_CTN, AIM_CB_CTN_DEFAULT, aim_parse_unknown, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_DEFAULT, aim_parse_unknown, 0); aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_MOTD, gaim_parse_motd, 0); aim_conn_addhandler(sess, bosconn, 0x0001, 0x0001, gaim_parse_genericerr, 0); aim_conn_addhandler(sess, bosconn, 0x0003, 0x0001, gaim_parse_genericerr, 0); @@ -578,7 +596,7 @@ struct client_info_s info = {"AOL Instant Messenger (SM), version 4.1.2010/WIN32", 4, 30, 3141, "us", "en", 0x0004, 0x0001, 0x055}; char *key; va_list ap; - struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess); + struct gaim_connection *gc = sess->aux_data; va_start(ap, command); key = va_arg(ap, char *); @@ -591,9 +609,32 @@ int gaim_server_ready(struct aim_session_t *sess, struct command_rx_struct *command, ...) { static int id = 1; - struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess); + struct gaim_connection *gc = sess->aux_data; + struct oscar_data *od = gc->proto_data; struct chat_connection *chatcon; switch (command->conn->type) { + case AIM_CONN_TYPE_AUTH: + aim_auth_setversions(sess, command->conn); + aim_bos_reqrate(sess, command->conn); + debug_printf("done with AUTH ServerReady\n"); + if (od->chpass) { + debug_printf("changing password\n"); + aim_auth_changepasswd(sess, command->conn, od->newp, od->oldp); + g_free(od->oldp); + g_free(od->newp); + od->chpass = FALSE; + } + if (od->conf) { + debug_printf("confirming account\n"); + aim_auth_reqconfirm(sess, command->conn); + od->conf = FALSE; + } + if (od->reqemail) { + debug_printf("requesting email\n"); + aim_auth_getinfo(sess, command->conn, 0x0011); + od->reqemail = FALSE; + } + break; case AIM_CONN_TYPE_BOS: aim_setversions(sess, command->conn); aim_bos_reqrate(sess, command->conn); /* request rate info */ @@ -635,7 +676,7 @@ int serviceid; char *ip; unsigned char *cookie; - struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess); + struct gaim_connection *gc = sess->aux_data; struct oscar_data *odata = (struct oscar_data *)gc->proto_data; va_start(ap, command); @@ -651,6 +692,11 @@ if (tstconn == NULL || tstconn->status & AIM_CONN_STATUS_RESOLVERR) debug_printf("unable to reconnect with authorizer\n"); else { + aim_conn_addhandler(sess, tstconn, 0x0001, 0x0003, gaim_server_ready, 0); + aim_conn_addhandler(sess, tstconn, 0x0001, 0x0007, gaim_rateresp, 0); + aim_conn_addhandler(sess, tstconn, 0x0007, 0x0003, gaim_info_change, 0); + aim_conn_addhandler(sess, tstconn, 0x0007, 0x0005, gaim_info_change, 0); + aim_conn_addhandler(sess, tstconn, 0x0007, 0x0007, gaim_account_confirm, 0); odata->paspa = gdk_input_add(tstconn->fd, GDK_INPUT_READ | GDK_INPUT_EXCEPTION, oscar_callback, tstconn); @@ -716,7 +762,7 @@ struct aim_userinfo_s *info; time_t time_idle; int type = 0; - struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess); + struct gaim_connection *gc = sess->aux_data; va_list ap; va_start(ap, command); @@ -750,7 +796,7 @@ struct command_rx_struct *command, ...) { char *sn; va_list ap; - struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess); + struct gaim_connection *gc = sess->aux_data; va_start(ap, command); sn = va_arg(ap, char *); @@ -834,6 +880,7 @@ return TRUE; } +/* static void cancel_getfile(gpointer w, struct ask_getfile *g) { g_free(g->ip); g_free(g->cookie); @@ -869,7 +916,7 @@ } static int gaim_getfile_filereq(struct aim_session_t *sess, struct command_rx_struct *command, ...) { - struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess); + struct gaim_connection *gc = sess->aux_data; struct oscar_data *od = (struct oscar_data *)gc->proto_data; struct getfile_transfer *gt; char buf[2048]; @@ -935,7 +982,7 @@ } static int gaim_getfile_filesend(struct aim_session_t *sess, struct command_rx_struct *command, ...) { - struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess); + struct gaim_connection *gc = sess->aux_data; struct oscar_data *od = (struct oscar_data *)gc->proto_data; struct getfile_transfer *gt; @@ -971,7 +1018,7 @@ } static int gaim_getfile_complete(struct aim_session_t *sess, struct command_rx_struct *command, ...) { - struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess); + struct gaim_connection *gc = sess->aux_data; struct oscar_data *od = (struct oscar_data *)gc->proto_data; struct getfile_transfer *gt; @@ -994,7 +1041,7 @@ } static int gaim_getfile_disconnect(struct aim_session_t *sess, struct command_rx_struct *command, ...) { - struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess); + struct gaim_connection *gc = sess->aux_data; struct oscar_data *od = (struct oscar_data *)gc->proto_data; struct getfile_transfer *gt; @@ -1117,12 +1164,13 @@ return TRUE; } +*/ int gaim_parse_incoming_im(struct aim_session_t *sess, struct command_rx_struct *command, ...) { int channel; va_list ap; - struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess); + struct gaim_connection *gc = sess->aux_data; va_start(ap, command); channel = va_arg(ap, int); @@ -1166,6 +1214,7 @@ msg); } else if (rendtype & AIM_CAPS_SENDFILE) { } else if (rendtype & AIM_CAPS_GETFILE) { + /* char *ip, *cookie; struct ask_getfile *g = g_new0(struct ask_getfile, 1); char buf[256]; @@ -1185,6 +1234,7 @@ g_snprintf(buf, sizeof buf, "%s has just asked to get a file from %s.", userinfo->sn, gc->username); do_ask_dialog(buf, g, accept_getfile, cancel_getfile); + */ } else if (rendtype & AIM_CAPS_VOICE) { } else if (rendtype & AIM_CAPS_BUDDYICON) { } else if (rendtype & AIM_CAPS_IMIMAGE) { @@ -1302,7 +1352,7 @@ char *prof_enc = NULL, *prof = NULL; u_short infotype; char buf[BUF_LONG]; - struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess); + struct gaim_connection *gc = sess->aux_data; va_list ap; va_start(ap, command); @@ -1362,7 +1412,7 @@ struct command_rx_struct *command, ...) { va_list ap; u_short type; - struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess); + struct gaim_connection *gc = sess->aux_data; struct oscar_data *odata = (struct oscar_data *)gc->proto_data; va_start(ap, command); @@ -1435,7 +1485,7 @@ va_list ap; int count, i = 0; struct aim_userinfo_s *info; - struct gaim_connection *g = find_gaim_conn_by_aim_sess(sess); + struct gaim_connection *g = sess->aux_data; GSList *bcs = g->buddy_chats; struct conversation *b = NULL; @@ -1466,7 +1516,7 @@ va_list ap; int count, i = 0; struct aim_userinfo_s *info; - struct gaim_connection *g = find_gaim_conn_by_aim_sess(sess); + struct gaim_connection *g = sess->aux_data; GSList *bcs = g->buddy_chats; struct conversation *b = NULL; @@ -1503,7 +1553,7 @@ va_list ap; struct aim_userinfo_s *info; char *msg; - struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess); + struct gaim_connection *gc = sess->aux_data; GSList *bcs = gc->buddy_chats; struct conversation *b = NULL; @@ -1595,7 +1645,7 @@ va_list ap; int newevil; struct aim_userinfo_s *userinfo; - struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess); + struct gaim_connection *gc = sess->aux_data; va_start(ap, command); newevil = va_arg(ap, int); @@ -1608,7 +1658,7 @@ } int gaim_rateresp(struct aim_session_t *sess, struct command_rx_struct *command, ...) { - struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess); + struct gaim_connection *gc = sess->aux_data; switch (command->conn->type) { case AIM_CONN_TYPE_BOS: aim_bos_ackrateresp(sess, command->conn); @@ -1634,6 +1684,11 @@ AIM_PRIVFLAGS_ALLOWMEMBERSINCE); break; + case AIM_CONN_TYPE_AUTH: + aim_bos_ackrateresp(sess, command->conn); + aim_auth_clientready(sess, command->conn); + debug_printf("connected to auth (admin)\n"); + break; default: debug_printf("got rate response for unhandled connection type %04x\n", command->conn->type); @@ -1683,6 +1738,96 @@ return 1; } +int gaim_parse_searchreply(struct aim_session_t *sess, struct command_rx_struct *command, ...) { + va_list ap; + char *address, *SNs; + int i, num; + char *buf; + int at = 0, len; + + va_start(ap, command); + address = va_arg(ap, char *); + num = va_arg(ap, int); + SNs = va_arg(ap, char *); + va_end(ap); + + len = num * (MAXSNLEN + 1) + 1024; + buf = g_malloc(len); + at += g_snprintf(buf + at, len - at, "<B>%s has the following screen names:</B><BR>", address); + for (i = 0; i < num; i++) + at += g_snprintf(buf + at, len - at, "%s<BR>", &SNs[i * (MAXSNLEN + 1)]); + g_show_info_text(buf); + g_free(buf); + + return 1; +} + +int gaim_parse_searcherror(struct aim_session_t *sess, struct command_rx_struct *command, ...) { + va_list ap; + char *address; + char buf[BUF_LONG]; + + va_start(ap, command); + address = va_arg(ap, char *); + va_end(ap); + + g_snprintf(buf, sizeof(buf), "No results found for email address %s", address); + do_error_dialog(buf, _("Error")); + + return 1; +} + +int gaim_account_confirm(struct aim_session_t *sess, struct command_rx_struct *command, ...) { + int status; + va_list ap; + char msg[256]; + struct gaim_connection *gc = sess->aux_data; + + va_start(ap, command); + status = va_arg(ap, int); /* status code of confirmation request */ + va_end(ap); + + debug_printf("account confirmation returned status 0x%04x (%s)\n", status, + status ? "email sent" : "unknown"); + if (status) { + g_snprintf(msg, sizeof(msg), "You should receive an email asking to confirm %s.", + gc->username); + do_error_dialog(msg, "Confirm"); + } + + return 1; +} + +int gaim_info_change(struct aim_session_t *sess, struct command_rx_struct *command, ...) { + unsigned short change = 0; + int perms, type, length, str; + char *val; + va_list ap; + char buf[BUF_LONG]; + struct gaim_connection *gc = sess->aux_data; + + va_start(ap, command); + perms = va_arg(ap, int); + type = va_arg(ap, int); + length = va_arg(ap, int); + val = va_arg(ap, char *); + str = va_arg(ap, int); + va_end(ap); + + if (aimutil_get16(command->data+2) == 0x0005) + change = 1; + + debug_printf("info%s: perms = %d, type = %x, length = %d, val = %s\n", + change ? " change" : "", perms, type, length, str ? val : "(not string)"); + + if ((type == 0x0011) && str) { + g_snprintf(buf, sizeof(buf), "The email address for %s is %s", gc->username, val); + do_error_dialog(buf, "Email"); + } + + return 1; +} + static void oscar_keepalive(struct gaim_connection *gc) { struct oscar_data *odata = (struct oscar_data *)gc->proto_data; aim_flap_nop(odata->sess, odata->conn); @@ -1903,7 +2048,7 @@ static int gaim_directim_initiate(struct aim_session_t *sess, struct command_rx_struct *command, ...) { va_list ap; - struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess); + struct gaim_connection *gc = sess->aux_data; struct oscar_data *od = (struct oscar_data *)gc->proto_data; struct aim_directim_priv *priv; struct aim_conn_t *newconn; @@ -1944,7 +2089,7 @@ va_list ap; char *sn = NULL, *msg = NULL; struct aim_conn_t *conn; - struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess); + struct gaim_connection *gc = sess->aux_data; va_start(ap, command); conn = va_arg(ap, struct aim_conn_t *); @@ -1963,7 +2108,7 @@ va_list ap; struct aim_conn_t *conn; char *sn; - struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess); + struct gaim_connection *gc = sess->aux_data; struct oscar_data *od = (struct oscar_data *)gc->proto_data; struct direct_im *dim; char buf[256]; @@ -2281,8 +2426,27 @@ static void oscar_do_action(struct gaim_connection *gc, char *act) { + struct oscar_data *od = gc->proto_data; + if (!strcmp(act, "Set User Info")) { show_set_info(gc); + } else if (!strcmp(act, "Change Password")) { + show_change_passwd(gc); + } else if (!strcmp(act, "Confirm Account")) { + if (od->paspa == 0) { + od->conf = TRUE; + aim_bos_reqservice(od->sess, od->conn, AIM_CONN_TYPE_AUTH); + } else + aim_auth_reqconfirm(od->sess, aim_getconn_type(od->sess, AIM_CONN_TYPE_AUTH)); + } else if (!strcmp(act, "Change Email")) { + } else if (!strcmp(act, "Display Current Registered Address")) { + if (od->paspa == 0) { + od->reqemail = TRUE; + aim_bos_reqservice(od->sess, od->conn, AIM_CONN_TYPE_AUTH); + } else + aim_auth_getinfo(od->sess, aim_getconn_type(od->sess, AIM_CONN_TYPE_AUTH), 0x11); + } else if (!strcmp(act, "Search for Buddy by Email")) { + show_find_email(gc); } } @@ -2291,10 +2455,33 @@ GList *m = NULL; m = g_list_append(m, "Set User Info"); + m = g_list_append(m, NULL); + m = g_list_append(m, "Change Password"); + m = g_list_append(m, "Confirm Account"); + /* + m = g_list_append(m, "Change Email"); + */ + m = g_list_append(m, "Display Current Registered Address"); + m = g_list_append(m, NULL); + m = g_list_append(m, "Search for Buddy by Email"); return m; } +static void oscar_change_passwd(struct gaim_connection *gc, char *old, char *new) +{ + struct oscar_data *od = gc->proto_data; + if (od->paspa == 0) { + od->chpass = TRUE; + od->oldp = g_strdup(old); + od->newp = g_strdup(new); + aim_bos_reqservice(od->sess, od->conn, AIM_CONN_TYPE_AUTH); + } else { + aim_auth_changepasswd(od->sess, aim_getconn_type(od->sess, AIM_CONN_TYPE_AUTH), + new, old); + } +} + void oscar_init(struct prpl *ret) { ret->protocol = PROTO_OSCAR; ret->options = OPT_PROTO_HTML | OPT_PROTO_CORRECT_TIME; @@ -2318,7 +2505,7 @@ ret->get_dir = NULL; /* Oscar really doesn't have this */ ret->dir_search = oscar_dir_search; ret->set_idle = oscar_set_idle; - ret->change_passwd = NULL; /* Oscar doesn't have this either */ + ret->change_passwd = oscar_change_passwd; ret->add_buddy = oscar_add_buddy; ret->add_buddies = oscar_add_buddies; ret->remove_buddy = oscar_remove_buddy;