DELTA 130 486 2026
SVN  ¥a¬!m•o€ƒ7 o™5€„ }R€…7 T‰z€\ o¡/¿ /¢]‚ ¤€ ¿ ;‘\‚ z“€ qœy¿ 0ž)‚ Ÿ[€ ‚S–
€E ˆ5€8 \‰u« ƒYŠ{£ r&/*
 * Family 0x0007 - Account Administration.
 *
 * Used for stuff like changing the formating of your screen name, changing your 
 * email address, requesting an account confirmation email, getting account info, 
 *
 */

#define FAIM_INTERNAL
#include <aim.h>

/*
 * Subtype 0x0002 - Request a bit of account info.
 *
 * Info should be one of the following:
 * 0x0001 - Screen name formatting
 * 0x0011 - Email address
 * 0x0013 - Unknownfr;
	aim_snacid_t snacid;

	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 14)))
		return -ENOMEM;

	snacid = aim_cachesnac(sess, 0x0007, 0x0002, 0x0000, NULL, 0);
	aim_putsnac(&fr->data, 0x0007, 0x0002, 0x0000, snacid);

	aimbs_put16(&fr->data, info);
	aimbs_put16(&fr->data, 0x0000);

	aim_tx_enqueue(sess, fr);

	return 0;
}

/*
 * Subtypes 0x0003 and 0x0005 - Parse account info.
 *
 * Called in reply to both an information request (subtype 0x0002) and 
 * an information change (subtype 0x0004).
 *
	aim_rxcallback_t userfunc;
	char *url=NULL, *sn=NULL, *email=NULL;
	fu16_t perms, tlvcount, err=0;

	perms = aimbs_get16(bs);
	tlvcount = aimbs_get16(bs);

	while (tlvcount && aim_bstream_empty(bs)) {
		fu16_t type, length;

		type = aimbs_get16(bs);
		length = aimbs_get16(bs);

		switch (type) {
			case 0x0001: {
				sn = aimbs_getstr(bs, length);
			} break;

			case 0x0004: {
				url = aimbs_getstr(bs, length);
			} break;

			case 0x0008: {
				err = aimbs_get16(bs);
			} break;

			case 0x0011: {
				if (length == 0) {
					email = (char*)malloc(13*sizeof(char));
					strcpy(email, "*suppressed*");
				} else
					email = aimbs_getstr(bs, length);
			} break;
		}

		tlvcount--;
	}userfunc(sess, rx, (snac->subtype == 0x0005) ? 1 : 0, perms, err, url, sn, email);

	if (sn) free(sn);
	if (url) free(url);
	if (email) free(email);

	return 1;
}

/*
 * Subtype 0x0004 - Set screenname formatting.
 *
 */fr;
	aim_snacid_t snacid;
	aim_tlvlist_t *tl = NULL;

	if (!(frfrfr->data, &tl);
	aim_freetlvchain(&tl);
	
	aim_tx_enqueue(sess, fr);


	return 0;
}

/*
 * Subtype 0x0004 - Change password.
 *
 */fr;
	aim_tlvlist_t *tl = NULL;
	aim_snacid_t snacid;

	if (!(frfrfr->data, &tl);
	aim_freetlvchain(&tl);

	aim_tx_enqueue(sess, fr);

	return 0;
}

/*
 * Subtype 0x0004 - Change email address.
 *
 */fr;
	aim_snacid_t snacid;
	aim_tlvlist_t *tl = NULL;

	if (!(frfrfr->data, &tl);
	aim_freetlvchain(&tl);
	
	aim_tx_enqueue(sess, fr);

	return 0;
}

/*
 * Subtype 0x0006 - Request account confirmation.Subtype 0x0007 - Account confirmation request acknowledgement.
 *
 */int ret = 0;
	aim_rxcallback_t userfunc;
	fu16_t status;
	aim_tlvlist_t *tl;

	status = aimbs_get16(bs);
	/* This is 0x0013 if unable to confirm at this time */

	tl = aim_readtlvchain = userfunc(sess, rx, status);

	return ret0x0010;
	mod->toolversion = 0x0629;ENDREP
DELTA 205 0 1131
SVN  £w£v	  žr  …žsENDREP
DELTA 130 2953 15506
SVN  ‚Ÿ-‚ìƒEžf \  ] V‚j€; …O€F {ˆ| ƒoŠf€J \ŽL‚ †w*€R ‚-—|€—x z¥$€2 k§X€n ˆ ª; T²j€K 6µ3 s¶k… j·k nÅ ˆD¸UŽ ‚Â2 Á€ƒ qÆq€„< …Uì5 Qò	° 3óe „Hõ€  ƒHú'¾ ƒtþ0¬ ƒg‚† ƒ…{€] ƒ4‰ ƒ=4€ ˆ+‘ Sš#¥ ‚œ(€‚3 „gès€‚Y „¨Š ¬O¼ •j­M€‚
 ‚dÅH€‡> jË€›" ƒWÖS  „Î ‚ÊG %ÌQ€v „FÒ€ƒ q×€‚3 |Ù² †<Ûd€
 Lõz€i dç2€‚ ŠY÷~€¯A ‚Š0€‚Y Qñ8œ ƒSò€ƒ( …cÛ€‚" ‹Kàl… _‚Œ… m‚j± ‚
€ ‚‚‘0… ‚“Z¶ D‚•(€ ‚‚˜$€E j‚š}œ {‚›~€O1ndef _WIN32
#include <sys/time.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#else
#include <winsock.h>
#endif

#ifdef __cplusplus
extern "C" {int fu32_t;
typedef fu32_t aim_snacid_t;
typedef fu16_t flap_seqnum_t;#ifndef FALSE
#define FALSE (0)
#endif

#ifndef TRUE
#define TRUE (!FALSE)97*
 * Maximum length for the password of an ICQ account
 */
#define MAXICQPASSLEN 8fu16_t major;
	fu16_t minor;
	fu16_t point;
	fu16_t build;
	fu32_t distrib;
	const char *country; /* two-letter abbrev */
	const char *lang; /* two-letter abbrev */
};

/* Needs to be checked */
#define CLIENTINFO_AIM_3_5_1670 { \
	"AOL Instant Messenger (SM), version 3.5.1670/WIN32", \
	0x0004, \
	0x0003, 0x0005, \
	0x0000, 0x0686, \
	0x0000002a, \
	"us", "en", \
}

/* Needs to be checked */
/* Latest winaim without ssi */
#define CLIENTINFO_AIM_4_1_2010 { \
	"AOL Instant Messenger (SM), version 4.1.2010/WIN32", \
	0x0004, \
	0x0004, 0x0001, \
	0x0000, 0x07da, \
	0x0000004b, \
	"us", "en", \
}

/* Needs to be checked */
#define CLIENTINFO_AIM_4_3_2188 { \
	"AOL Instant Messenger (SM), version 4.3.2188/WIN32", \
	0x0109, \
	0x0400, 0x0003, \
	0x0000, 0x088c, \
	0x00000086, \
	"us", "en", \
}

/* Needs to be checked */
#define CLIENTINFO_AIM_4_8_2540 { \
	"AOL Instant Messenger (SM), version 4.8.2540/WIN32", \
	0x0109, \
	0x0004, 0x0008, \
	0x0000, 0x09ec, \
	0x000000af, \
	"us", "en", \
}

/* Needs to be checked */
#define CLIENTINFO_AIM_5_0_2938 { \
	"AOL Instant Messenger, version 5.0.2938/WIN32", \
	0x0109, \
	0x0005, 0x0000, \
	0x0000, 0x0b7a, \
	0x00000000, \
	"us", "en", \
}

#define CLIENTINFO_AIM_5_1_3036 { \
	"AOL Instant Messenger, version 5.1.3036/WIN32", \
	0x0109, \
	0x0005, 0x0001, \
	0x0000, 0x0bdc, \
	0x000000d2, \
	"us", "en", \
}

#define CLIENTINFO_ICHAT_1_0 { \
	"Apple iChat", \
	0x311a, \
	0x0001, 0x0000, \
	0x0000, 0x003c, \
	0x000000c6, \
	"us", "en", \
}

/* Needs to be checked */
#define CLIENTINFO_ICQ_4_65_3281 { \
	"ICQ Inc. - Product of ICQ (TM) 2000b.4.65.1.3281.85", \
	0x010a, \
	0x0004, 0x0041, \
	0x0001, 0x0cd1, \
	0x00000055, \
	"us", "en", \
}

/* Needs to be checked */
#define CLIENTINFO_ICQ_5_34_3728 { \
	"ICQ Inc. - Product of ICQ (TM).2002a.5.34.1.3728.85", \
	0x010a, \
	0x0005, 0x0022, \
	0x0001, 0x0e8f, \
	0x00000055, \
	"us", "en", \
}

#define CLIENTINFO_ICQ_5_45_3777 { \
	"ICQ Inc. - Product of ICQ (TM).2003a.5.45.1.3777.85", \
	0x010a, \
	0x0005, 0x002d, \
	0x0001, 0x0ec1, \
	0x00000055, \
	"us", "en", \
}

#define CLIENTINFO_ICQBasic_14_3_1068 { \
	"ICQBasic", \
	0x010a, \
	0x0014, 0x0003, \
	0x0000, 0x042c, \
	0x0000043d, \
	"us", "en", \
}

#define CLIENTINFO_Netscape_7_0_1 { \
	"Netscape 2000 an approved user of AOL Instant Messenger (SM)", \
	0x1d0d, \
	0x0007, 0x0000, \
	0x0001, 0x0000, \
	0x00000058, \
	"us", "en", \
}

#define CLIENTINFO_AIM_KNOWNGOOD CLIENTINFO_AIM_5_1_3036
#define CLIENTINFO_ICQ_KNOWNGOOD CLIENTINFO_ICQ_5_45_3777

/* 
 * These could be arbitrary, but its easier to use the actual AIM values 
 */
#define AIM_CONN_TYPE_BOS		0x0002
#define AIM_CONN_TYPE_ADS		0x0005
#define AIM_CONN_TYPE_AUTH		0x0007
#define AIM_CONN_TYPE_CHATNAV		0x000d
#define AIM_CONN_TYPE_CHAT		0x000e
#define AIM_CONN_TYPE_SEARCH		0x000f
#define AIM_CONN_TYPE_ICON		0x0010
#define AIM_CONN_TYPE_EMAIL		0x0018

/* they start getting arbitrary for rendezvous stuff =) */
#define AIM_CONN_TYPE_RENDEZVOUS	0xfffe /* these do not speak FLAP! */
#define AIM_CONN_TYPE_LISTENER		0xffff	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			0x0001
#define AIM_CONN_STATUS_INTERNALERR	0x0002
#define AIM_CONN_STATUS_RESOLVERR	0x0040
#define AIM_CONN_STATUS_CONNERR		0x0080
#define AIM_CONN_STATUS_INPROGRESS	0x0100

#define AIM_FRAMETYPE_FLAP		0x0000
#define AIM_FRAMETYPE_OFT		32_t len;
	fu328_t magic[4]; /* ODC2 or OFT2 */
			fu16_t hdrlen;
			fu16_t type;
		} rend	fu8_tvoid *modlistvaim_msgcookie_t *msgcookies;
	struct aim_icq_info *icq_info;
	struct aim_oft_info *oft_info;
	struct aim_authresp_info *authinfo;
	struct aim_emailinfo *emailinfo;

	/* Server-stored information (ssi) */
	struct {
		int received_data;
		fu16_t numitems;
		struct aim_ssi_item *official;
		struct aim_ssi_item *local;
		struct aim_ssi_tmp *pending;
		time_t timestamp;
		int waiting_for_ack;
	} ssi;
} aim_session_t;		0x00000000
#define AIM_ICQ_STATE_AWAY		0x00000001
#define AIM_ICQ_STATE_DND		0x00000002
#define AIM_ICQ_STATE_OUT		0x00000004
#define AIM_ICQ_STATE_BUSY		0x00000010
#define AIM_ICQ_STATE_CHAT		0x00000020
#define AIM_ICQ_STATE_INVISIBLE		0x00000100
#define AIM_ICQ_STATE_WEBAWARE		0x00010000
#define AIM_ICQ_STATE_HIDEIP		0x00020000
#define AIM_ICQ_STATE_BIRTHDAY		0x00080000
#define AIM_ICQ_STATE_DIRECTDISABLED	0x00100000
#define AIM_ICQ_STATE_ICQHOMEPAGE	0x00200000
#define AIM_ICQ_STATE_DIRECTREQUIREAUTH	0x10000000
#define AIM_ICQ_STATE_DIRECTCONTACTLIST	0x20000000

/* auth.c */16_t cookielen;
	fu8_t *cookie;
	char *chpassurlfu16_t cookielenfu8_t *digest);
faim_export void aim_purge_rxqueue(aim_session_t *);
faim_export void aim_cleansnacs(aim_session_t *, int maxagefu16_t family, fu16_t type, aim_rxcallback_t newhandler, fu16_n_sess(aim_session_t *sess, aim_conn_t *connfu32_t

/* service.c */
faim_export int aim_srv_setavailmsg(aim_session_t *sess, char *msg);



/* _encoding, const char *profile, const int profile_len, const char *awaymsg_encoding, const char *awaymsg, const int awaymsg_lenfu32_t statusfu16_t aim_im_fingerprint(const fu8_t

/* im.c */
#define AIM_OFT_SUBTYPE_SEND_FILE	0x0001
#define AIM_OFT_SUBTYPE_SEND_DIR	0x0002
#define AIM_OFT_SUBTYPE_GET_FILE	0x0011
#define AIM_OPT_SUBTYPE_GET_LIST	0x0012

#define AIM_TRANSFER_DENY_NOTSUPPORTED	0x0000
#define AIM_TRANSFER_DENY_DECLINE	0x0001
#define AIM_TRANSFER_DENY_NOTACCEPTING	0x0002struct aim_icbmparameters {
	fu16_t maxchan;
	fu32_t flags; /* AIM_IMPARAM_FLAG_ */
	fu16_t maxmsglen; /* message size that you will accept */
	fu16_t maxsenderwarn; /* this and below are *10 (999=99.9%) */
	fu16_t maxrecverwarn;
	fu32_t minmsginterval; /* in milliseconds? */
};

struct aim_chat_roominfo {
	fu16_t exchange;
	char *name;
	fu16_X			0x0200#define AIM_IMFLAGS_TYPINGNOT		0x1000 /* typing notification	0x0000
#define AIM_RENDEZVOUS_CANCEL	0x0001
#define AIM_RENDEZVOUS_ACCEPT	0x0002

struct aim_incomingim_ch2_args {
	fu16_t status;
	fu8_t cookie[8];
	int reqclass;
	const char *proxyip;
	const char *clientip;
	const char *verifiedip;
	fu16_t port;
	fu16_t errorcode	struct {
			fu16_t subtype;
			fu16_t totfiles;
			fu32_t totsize;
			char *filename;
		} sendfile;
	} info;
	void *destructor; /* used internally only */
};

/* Valid values for channel 4 args->type */
#define AIM_ICQMSG_AUTHREQUEST	0x0006
#define AIM_ICQMSG_AUTHDENIED	0x0007
#define AIM_ICQMSG_AUTHGRANTED	0x0008

struct aim_incomingim_ch4_args {
	fu32_t uin; /* Of the sender of the ICBM */
	fu8_t type;
	fu8_t flags;
	char *msg; /* Reason for auth request, deny, or accept */
	int msglen;
};

/* SNAC sending functions */
/* 0x0002 */ faim_export int aim_im_setparams(aim_session_t *sess, struct aim_icbmparameters *params);
/* 0x0004 */ faim_export int aim_im_reqparams(aim_session_t *sess);
/* 0x0006 */ faim_export int aim_im_sendch1_ext(aim_session_t *sess, struct aim_sendimext_args *args);
/* 0x0006 */ faim_export int aim_im_sendch1(aim_session_t *, const char *destsn, fu16_t flags, const char *msg);
/* 0x0006 */ faim_export int aim_im_sendch2/* 0x0006 */ faim_export int aim_im_sendch2_rtfmsg(aim_session_t *sess, struct aim_sendrtfmsg_args *args);
/* 0x0006 */ faim_export int aim_im_sendch2_odcrequest(aim_session_t *sess, fu8_t *cookie, const char *sn, const fu8_t *ip, fu16_t port);
/* 0x0006 */ faim_export int aim_im_sendch2_sendfile_ask(aim_session_t *sess, struct aim_oft_info *oft_info);
/* 0x0006 */ faim_export int aim_im_sendch2_sendfile_accept(aim_session_t *sess, struct aim_oft_info *info);
/* 0x0006 */ faim_export int aim_im_sendch2_sendfile_cancel(aim_session_t *sess, struct aim_oft_info *oft_info);
/* 0x0006 */ faim_export int aim_im_sendch2_geticqaway(aim_session_t *sess, const char *sn, int type);
/* 0x0006 */ faim_export int aim_im_sendch4(aim_session_t *sess, char *sn, fu16_t type, fu8_t *message);
/* 0x0008 */ faim_export int aim_im_warn(aim_session_t *sess, aim_conn_t *conn, const char *destsn, fu32_t flags);
/* 0x000b */ faim_export int aim_im_denytransfer(aim_session_t *sess, const char *sender, const char *cookie, fu16_t code);
/* 0x0014 */ faim_export int aim_im_sendmtn(aim_session_t *sess, fu16_t type1, const char *sn, fu16_t type2);



/* ft.c */
struct aim_fileheader_t {
#if 0
	char magic[4];		/* 0 */
	fu16_t hdrlen;		/* 4 */
	fu16_t hdrtype;		/* 6 */
#endif
	char bcookie[8];	/* 8 */
	fu16_t encrypt;		/* 16 */
	fu16_t compress;	/* 18 */
	fu16_t totfiles;	/* 20 */
	fu16_t filesleft;	/* 22 */
	fu16_t totparts;	/* 24 */
	fu16_t partsleft;	/* 26 */
	fu32_t totsize;		/* 28 */
	fu32_t size;		/* 32 */
	fu32_t modtime;		/* 36 */
	fu32_t checksum;	/* 40 */
	fu32_t rfrcsum;		/* 44 */
	fu32_t rfsize;		/* 48 */
	fu32_t cretime;		/* 52 */
	fu32_t rfcsum;		/* 56 */
	fu32_t nrecvd;		/* 60 */
	fu32_t recvcsum;	/* 64 */
	fu8_t idstring[32];	/* 68 */
	fu8_t flags;		/* 100 */
	fu8_t lnameoffset;	/* 101 */
	fu8_t lsizeoffset;	/* 102 */
	char dummy[69];		/* 103 */
	char macfileinfo[16];	/* 172 */
	fu16_t nencode;		/* 188 */
	fu16_t nlanguage;	/* 190 */
	char name[64];		/* 192 */
				/* 256 */
};

struct aim_oft_info {
	char cookie[8];
	char *sn;
	char *proxyip;
	char *clientip;
	char *verifiedip;
	fu16_t port;
	aim_conn_t *conn;
	aim_session_t *sess;
	struct aim_fileheader_t fh;
	struct aim_oft_info *next;
};

faim_export fu32_t aim_oft_checksum_chunk(const fu8_t *buffer, int bufferlen, fu32_t prevcheck);
faim_export fu32_t aim_oft_checksum_file(char *filename);
faim_export int aim_handlerendconnect(aim_session_t *sess, aim_conn_t *cur);
faim_export int aim_odc_send_typing(aim_session_t *sess, aim_conn_t *conn, int typing);
faim_export int aim_odc_send_im(aim_session_t *sess, aim_conn_t *conn, const char *msg, int len, int encoding, int isawaymsg);
faim_export const char *aim_odc_getsn(aim_conn_t *conn);
faim_export aim_conn_t *aim_odc_getconn(aim_session_t *sess, const char *sn);
faim_export aim_conn_t *aim_odc_initiate(aim_session_t *sess, const char *sn);
faim_export aim_conn_t *aim_odc_connect(aim_session_t *sess, const char *sn, const char *addr, const fu8_t *cookie);

faim_export struct aim_oft_info *aim_oft_createinfo(aim_session_t *sess, const fu8_t *cookie, const char *sn, const char *ip, fu16_t port, fu32_t size, fu32_t modtime, char *filename);
faim_export int aim_oft_destroyinfo(struct aim_oft_info *oft_info);
faim_export int aim_sendfile_listen(aim_session_t *sess, struct aim_oft_info *oft_info);
faim_export int aim_oft_sendheader(aim_session_t *sess, fu16_t type, struct aim_oft_info *oft_info);



/* info.c */
/*
 * AIM User Info, Standard Form.
 */#define AIM_FLAG_ALLUSERS	0x001f	fu16_t iconcsumlen;
	fu8_t *iconcsum;
	char *availmsg_encoding;
	char *availmsg;
	int availmsg_len;
} aim_userinfo_t;CAPS_BUDDYICON	0x00000001
#define AIM_CAPS_VOICE		0x00000002
#define AIM_CAPS_DIRECTIM	0x00000004
#define AIM_CAPS_CHAT		0x00000008
#define AIM_CAPS_GETFILE	0x00000010
#define AIM_CAPS_SENDFILE	0x00000020
#define AIM_CAPS_GAMES		0x00000040
#define AIM_CAPS_SAVESTOCKS	0x00000080
#define AIM_CAPS_SENDBUDDYLIST	0x00000100
#define AIM_CAPS_GAMES2		0x00000200
#define AIM_CAPS_ICQ		0x00000400
#define AIM_CAPS_APINFO			0x00004000
#define AIM_CAPS_ICQUTF8OLD	0x00008000
#define AIM_CAPS_TRILLIANCRYPT	0x00010000
#define AIM_CAPS_ICQUTF8	0x00020000
#define AIM_CAPS_INTEROPERATE	0x00040000
#define AIM_CAPS_ICHAT		0x00080000
#define AIM_CAPS_HIPTOP		0x00100000
#define AIM_CAPS_SECUREIM	0x00200000
#define AIM_CAPS_LAST		0x0040fu32_t offset, fu32_t len, const fu8_t *buf, fu8_t/* 0x0005 */ faim_export int aim_getinfo(aim_session_t *, aim_conn_t *, const char *, fu16_t);



/* 0x0003 - buddylist.c */
/* 0x0004 */ /* 0x0005 */ faim_export int aim_remove_buddy(aim_session_t *, aim_conn_t *, const char *);
/* 0x000b */ /* 0x000c */ faim_export int aim_sendbuddyoffgoing(aim_session_t *sess, aim_conn_t *conn, const char *sn);



/* 0x000a - search.c */
faim_export int aim_search_address(aim_session_t *, aim_conn_t *, const char *);



/* 0x000d - chatnav.c */
/* 0x000e - chat.c */
/* 0x000f - odir.c */
struct aim_odir {
	char *first;
	char *last;
	char *middle;
	char *maiden;
	char *email;
	char *country;
	char *state;
	char *city;
	char *sn;
	char *interest;
	char *nick;
	char *zip;
	char *region;
	char *address;
	struct aim_odir *next;
};

faim_export int aim_odir_email(aim_session_t *, const char *, const char *);
faim_export int aim_odir_name(aim_session_t *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *);
faim_export int aim_odir_interest(aim_session_t *, const char *, const char *);



/* 0x0010 - icon.c */
faim_export int aim_bart_upload(aim_session_t *sess, const fu8_t *icon, fu16_t iconlen);
faim_export int aim_bart_request(aim_session_t *sess, const char *sn, const fu8_t *iconstr, fu16_t iconstrlen);



/* 0x0013 - ssi.c */
#define AIM_SSI_TYPE_BUDDY		0x0000
#define AIM_SSI_TYPE_GROUP		0x0001
#define AIM_SSI_TYPE_PERMIT		0x0002
#define AIM_SSI_TYPE_DENY		0x0003
#define AIM_SSI_TYPE_PDINFO		0x0004
#define AIM_SSI_TYPE_PRESENCEPREFS	0x0005
#define AIM_SSI_TYPE_ICONINFO		0x0014

#define AIM_SSI_ACK_SUCCESS		0x0000
#define AIM_SSI_ACK_ITEMNOTFOUND	0x0002
#define AIM_SSI_ACK_IDNUMINUSE		0x000a
#define AIM_SSI_ACK_ATMAX		0x000c
#define AIM_SSI_ACK_INVALIDNAME		0x000d
#define AIM_SSI_ACK_AUTHREQUIRED	0x000e

struct aim_ssi_item {
	char *name;
	fu16_t gid;
	fu16_t bid;
	fu16_t type;
	struct aim_tlvlist_s *data;
	struct aim_ssi_item *next;
};

struct aim_ssi_tmp {
	fu16_t action;
	fu16_t ack;
	char *name;
	struct aim_ssi_item *item;
	struct aim_ssi_tmp *next;
};

/* These build the actual SNACs and queue them to be sent */
/* 0x0002 */ faim_export int aim_ssi_reqrights(aim_session_t *sess);
/* 0x0004 */ faim_export int aim_ssi_reqdata(aim_session_t *sess);
/* 0x0005 */ faim_export int aim_ssi_reqifchanged(aim_session_t *sess, time_t localstamp, fu16_t localrev);
/* 0x0007 */ faim_export int aim_ssi_enable(aim_session_t *sess);
/* 0x0008 */ faim_export int aim_ssi_addmoddel(aim_session_t *sess);
/* 0x0011 */ faim_export int aim_ssi_modbegin(aim_session_t *sess);
/* 0x0012 */ faim_export int aim_ssi_modend(aim_session_t *sess);
/* 0x0014 */ faim_export int aim_ssi_sendauth(aim_session_t *sess, char *sn, char *msg);
/* 0x0018 */ faim_export int aim_ssi_sendauthrequest(aim_session_t *sess, char *sn, char *msg);
/* 0x001a */ faim_export int aim_ssi_sendauthreply(aim_session_t *sess, char *sn, fu8_t reply, char *msg);

/* Client functions for retrieving SSI data */
faim_export struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_item *list, fu16_t gid, fu16_t bid);
faim_export struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_item *list, const char *gn, const char *sn, fu16_t type);
faim_export struct aim_ssi_item *aim_ssi_itemlist_exists(struct aim_ssi_item *list, const char *sn);
faim_export char *aim_ssi_itemlist_findparentname(struct aim_ssi_item *list, const char *sn);
faim_export int aim_ssi_getpermdeny(struct aim_ssi_item *list);
faim_export fu32_t aim_ssi_getpresence(struct aim_ssi_item *list);
faim_export char *aim_ssi_getalias(struct aim_ssi_item *list, const char *gn, const char *sn);
faim_export int aim_ssi_waitingforauth(struct aim_ssi_item *list, const char *gn, const char *sn);

/* Client functions for changing SSI data */
faim_export int aim_ssi_addbuddy(aim_session_t *sess, const char *name, const char *group, const char *alias, const char *comment, const char *smsnum, int needauth);
faim_export int aim_ssi_addpermit(aim_session_t *sess, const char *name);
faim_export int aim_ssi_adddeny(aim_session_t *sess, const char *name);
faim_export int aim_ssi_delbuddy(aim_session_t *sess, const char *name, const char *group);
faim_export int aim_ssi_delpermit(aim_session_t *sess, const char *name);
faim_export int aim_ssi_deldeny(aim_session_t *sess, const char *name);
faim_export int aim_ssi_movebuddy(aim_session_t *sess, const char *oldgn, const char *newgn, const char *sn);
faim_export int aim_ssi_aliasbuddy(aim_session_t *sess, const char *gn, const char *sn, const char *alias);
faim_export int aim_ssi_rename_group(aim_session_t *sess, const char *oldgn, const char *newgn);
faim_export int aim_ssi_cleanlist(aim_session_t *sess);
faim_export int aim_ssi_deletelist(aim_session_t *sess);
faim_export int aim_ssi_setpermdeny(aim_session_t *sess, fu8_t permdeny, fu32_t vismask);
faim_export int aim_ssi_setpresence(aim_session_t *sess, fu32_t presence);
faim_export int aim_ssi_seticon(aim_session_t *sess, fu8_t *iconsum, fu16_t iconsumlen);



/* 0x0015 - icq.c */
#define AIM_ICQ_INFO_SIMPLE	0x001
#define AIM_ICQ_INFO_SUMMARY	0x002
#define AIM_ICQ_INFO_EMAIL	0x004
#define AIM_ICQ_INFO_PERSONAL	0x008
#define AIM_ICQ_INFO_ADDITIONAL	0x010
#define AIM_ICQ_INFO_WORK	0x020
#define AIM_ICQ_INFO_INTERESTS	0x040
#define AIM_ICQ_INFO_ORGS	0x080
#define AIM_ICQ_INFO_UNKNOWN	0x100
#define AIM_ICQ_INFO_HAVEALL	0x1ff

struct aim_icq_offlinemsg {
	fu32_t sender;
	fu16_t year;
	fu8_t month, day, hour, minute;
	fu8_t type;
	fu8_t flags;
	char *msg;
	int msglen;
};

struct aim_icq_info {
	fu16_t reqid;

	/* simple */
	fu32_t uin;

	/* general and "home" information (0x00c8) */
	char *nick;
	char *first;
	char *last;
	char *email;
	char *homecity;
	char *homestate;
	char *homephone;
	char *homefax;
	char *homeaddr;
	char *mobile;
	char *homezip;
	fu16_t homecountry;
/*	fu8_t timezone;
	fu8_t hideemail; */

	/* personal (0x00dc) */
	fu8_t age;
	fu8_t unknown;
	fu8_t gender;
	char *personalwebpage;
	fu16_t birthyear;
	fu8_t birthmonth;
	fu8_t birthday;
	fu8_t language1;
	fu8_t language2;
	fu8_t language3;

	/* work (0x00d2) */
	char *workcity;
	char *workstate;
	char *workphone;
	char *workfax;
	char *workaddr;
	char *workzip;
	fu16_t workcountry;
	char *workcompany;
	char *workdivision;
	char *workposition;
	char *workwebpage;

	/* additional personal information (0x00e6) */
	char *info;

	/* email (0x00eb) */
	fu16_t numaddresses;
	char **email2;

	/* we keep track of these in a linked list because we're 1337 */
	struct aim_icq_info *nexthideip(aim_session_t *sess);
faim_export int aim_icq_changepasswd(aim_session_t *sess, const char *passwd);
faim_export int aim_icq_getsimpleinfo(aim_session_t *sess, const char *uin);
faim_export int aim_icq_getalias(aim_session_t *sess, const char *uin);
faim_export int aim_icq_getallinfo(aim_session_t *sess, const char *uin);



/* 0x0017 -16_t length, const fu8_t *);

/* 0x0018 - email.c */
struct aim_emailinfo {
	fu8_t *cookie16;
	fu8_t *cookie8;
	char *url;
	fu16_t nummsgs;
	fu8_t unread;
	char *domain;
	fu16_t flag;
	struct aim_emailinfo *next;
};

faim_export int aim_email_sendcookies(aim_session_t *sess, aim_conn_t *conn);
faim_export int aim_email_activate(aim_session_t *sess, aim_conn_t *conn);



/* tlv.c - TLV handling */
#if defined(FAIM_INTERNAL) || defined(FAIM_NEED_TLV)aim_tlvlist_t *aim_readtlvchain_num(aim_bstream_t *bs, fu16_t num);
faim_internal aim_tlvlist_t *aim_readtlvchain_len(aim_bstream_t *bs, fu16_t len);
faim_internal aim_tlvlist_t *aim_tlvlist_copy(aim_tlvlist_t *orig);
faim_internal int aim_tlvlist_cmp(aim_tlvlist_t *one, aim_tlvlist_t *two

/* fu8_tfu8_t)((data)>>8)&0xff), \
		(*((buf)+1) = (fu8_tfu8_t)((data)>>24)&0xff), \
		(*((buf)+1) = (fu8_t)((data)>>16)&0xff), \
		(*((buf)+2) = (fu8_t)((data)>>8)&0xff), \
		(*((buf)+3) = (fu8_tfu8_tfu8_t)((data) >> 0) & 0xff),  \
		(*((buf)+1) = (fu8_tfu8_t)((data) >>  0) & 0xff), \
		(*((buf)+1) = (fu8_t)((data) >>  8) & 0xff), \
		(*((buf)+2) = (fu8_t)((data) >> 16) & 0xff), \
		(*((buf)+3) = (fu8_tchar *, const char *, int);
faim_export fu16_t aimutil_iconsum(const fu8_t *buf, int buflen);
faim_export int aim_util_getlocalip(fu8_t *ip);
faim_export int aimutil_tokslen(char *toSearch, int thendex(char *toSearch, int the#include <aim_internal.h>

#ifdef __cplusplus
}
#endif

#endif /* __AIM_H__ */
ENDREP
DELTA 130 18472 2487
SVN  ³rÅ;š „\ €x 
…€K ŒP•8€~ M¡s€†T ‚£s€… §€ˆ< ƒV¯8€d ^²v€WSCH 0x000f /* "New" search */
#define AIM_CB_FAM_ICO 0x0010 /* Used for uploading buddy icons */
#define AIM_CB_FAM_SSI 0x0013 /* Server stored information */
#define AIM_CB_FAM_ICQ 0x0015
#define AIM_CB_FAM_ATH 0x0017
#define AIM_CB_FAM_EML 0x0018AUTORESP 0x000b
#define AIM_CB_MSG_ACK 0x000c
#define AIM_CB_MSG_MTN 0x0014"New" Search
 */ 
#define AIM_CB_SCH_ERROR 0x0001
#define AIM_CB_SCH_SEARCH 0x0002
#define AIM_CB_SCH_RESULTS 0x0003

/*
 * SNAC Family: Buddy icons
 */ 
#define AIM_CB_ICO_ERROR 0x0001
#define AIM_CB_ICO_REQUEST 0x0004
#define AIM_CB_ICO_RESPONSE 0x0005INFO 0x00f2
#define AIM_CB_ICQ_ALIAS 0x00f3
#define AIM_CB_ICQ_DEFAULT 0xffff

/*
 * SNAC Family: Server-Stored Buddy Lists
 */
#define AIM_CB_SSI_ERROR 0x0001
#define AIM_CB_SSI_REQRIGHTS 0x0002
#define AIM_CB_SSI_RIGHTSINFO 0x0003
#define AIM_CB_SSI_REQDATA 0x0004
#define AIM_CB_SSI_REQIFCHANGED 0x0005
#define AIM_CB_SSI_LIST 0x0006
#define AIM_CB_SSI_ACTIVATE 0x0007
#define AIM_CB_SSI_ADD 0x0008
#define AIM_CB_SSI_MOD 0x0009
#define AIM_CB_SSI_DEL 0x000A
#define AIM_CB_SSI_SRVACK 0x000E
#define AIM_CB_SSI_NOLIST 0x000F
#define AIM_CB_SSI_EDITSTART 0x0011
#define AIM_CB_SSI_EDITSTOP 0x0012
#define AIM_CB_SSI_SENDAUTH 0x0014
#define AIM_CB_SSI_RECVAUTH 0x0015
#define AIM_CB_SSI_SENDAUTHREQ 0x0018
#define AIM_CB_SSI_RECVAUTHREQ 0x0019
#define AIM_CB_SSI_SENDAUTHREP 0x001a
#define AIM_CB_SSI_RECVAUTHREP 0x001b
#define AIM_CB_SSI_ADDED 0x001cSNAC Family: Email
 *
 * Used for getting information on the email address
 * associated with your screen name.
 *
 */
#define AIM_CB_EML_ERROR 0x0001
#define AIM_CB_EML_SENDCOOKIES 0x0006
#define AIM_CB_EML_MAILSTATUS 0x0007
#define AIM_CB_EML_INIT 0x0016

/*
 * OFT Services
 *
 * For all of the above #defines, the number is the subtype 
 * of the SNAC.  For OFT #defines, the number is the 
 * "hdrtype" which comes after the magic string and OFT 
 * packet length.
 *
 * I'm pretty sure the ODC ones are arbitrary right now, 
 * that should be changed.
 */
#define AIM_CB_OFT_DIRECTIMCONNECTREQ 0x0001	/* connect request -- actually an OSCAR CAP _ESTABLISHED 0x0005

#define AIM_CB_OFT_PROMPT 0x0101		/* "I am going to send you this file, is that ok?" */
#define AIM_CB_OFT_RESUMESOMETHING 0x0106	/* I really don't know */
#define AIM_CB_OFT_ACK 0x0202			/* "Yes, it is ok for you to send me that file" */
#define AIM_CB_OFT_DONE 0x0204			/* "I received that file with no problems, thanks a bunch" */
#define AIM_CB_OFT_RESUME 0x0205		/* Resume transferring, sent by whoever paused? */
#define AIM_CB_OFT_RESUMEACK 0x0207		/* Not really sure */

#define AIM_CB_OFT_GETFILE_REQUESTLISTING 0x1108 /* "I have a listing.txt file, do you want it?" */
#define AIM_CB_OFT_GETFILE_RECEIVELISTING 0x1209 /* "Yes, please send me your listing.txt file" */
#define AIM_CB_OFT_GETFILE_RECEIVEDLISTING 0x120a /* received corrupt listing.txt file? */ /* I'm just guessing about this one... */
#define AIM_CB_OFT_GETFILE_ACKLISTING 0x120b	/* "I received the listing.txt file successfully" */
#define AIM_CB_OFT_GETFILE_REQUESTFILE 0x120c	/* "Please send me this file" */

#define AIM_CB_OFT_ESTABLISHED 0xFFFF		/* connection to buddy initiated */IMAGETRANSFER 0x0007
#define AIM_CB_SPECIAL_MSGTIMEOUT 0x0008
#define AIM_CB_SPECIAL_CONNDEAD 0x0009/* SNAC flags */
#define AIM_SNACFLAGS_DESTRUCTOR 0x0001

#endif/*__AIM_CBTYPES_H__ */
ENDREP
DELTA 130 20972 3337
SVN  ÎxÊR‡ „U  ƒg„T… E‹| Nˆ;ƒ E‹|† E‹|… E‹|„ F‹| ƒ<‹z ‚0~„ E‹|„ E‹|ƒ E‹|ƒ E‹|… „9’1 ‹˜F€‚9 	©	€8 —< S–x· _¥3€X R¤p K¤6€	 ^ªŒ ƒ «n ^¯. ²X =¶L€H ‚RÄ  „GÊ1adminbossearchstatsauthodirbartssiicqemail/* bstream.c *//* conn.c */
faim_internal aim_conn_t *aim_cloneconn(aim_session_t *sess, aim_conn_t *src);

/* ft.c */
faim_internal int aim_rxdispatch_rendezvous(aim_session_t *sess, aim_frame_t *fr);

/* rxhandlers.c */
faim_internal aim_rxcallback_t aim_callhandler(aim_session_t *sess, aim_conn_t *conn, fu16_t family, fu16_faim_internal int aim_parse_unknown(aim_session_t *, aim_frame_t *, ...);
faim_internal void aim_clonehandlers(aim_session_t *sess, aim_conn_t *dest, aim_conn_t *src);

/* rxqueue.c */void aim_frame_destroy(aim_frame_t *);

/* txqueue.c */16_t chan, int datalen);
faim_internal int aim_tx_enqueue(aim_session_t *, aim_frame_t *void aim_tx_cleanqueue(aim_session_t *, aim_conn_t *);

/* XXX - What is this?   faim_internal int aim_tx_printqueue(aim_session_t *); *//* snac.c */void aim_info_free(aim_userinfo_t *);
faim_internal int aim_info_extractENDREP
DELTA 130 24322 6773
SVN  ÷ùVQ˜_€1 I … ‚BV‹ „† `…*‘ pž6 ˜J‡ hŸl€‚ Q£€ ƒÁH£ F© €S ƒ>ª	 ‰µW„ ‚q¾\€G ƒÁJ£ ÄC€ EÆ[€ ‚×N€E Ú(€] |Ü‚ ?Ý¦ kÞ[€T 
ß‹ já€@ `ã{€d aå=‚ &æ€g cè*€h ~és€‚ êp ƒ{î €Y ñx€„$ dÕm ƒô
®/*
 * Family 0x0017 - Authentication.
 *
 * Deals with the authorizer for SNAC-based login, and also old-style 
 * non-SNAC login.
 *
 */

#define FAIM_INTERNAL
#include <aim.h>fu8_t16_t lengthlengthlength, chipsahoy, struct client_info_s *ci)
{
	aim_frame_t *fr;
	aim_tlvlist_t *tl = NULL;
	int passwdlen;
	fu8_t *password_encoded;

	passwdlen = strlen(password);
	if (!(password_encoded = (char *)malloc(passwdlen+1)))
		return -ENOMEM;
	if (passwdlen > MAXICQPASSLEN)
		passwdlen = MAXICQPASSLEN /* FLAP Version */
	aim_addtlvtochain_raw(&tl, 0x0001, strlen(sn), sn);
	aim_addtlvtochain_raw(&tl, 0x0002, passwdlen, password_encoded32(&tl, 0x0014, (fu32_t)ci->distribci->lang), ci->lang);
	aim_addtlvtochain_raw(&tl, 0x000e, strlen(ci->country), ci->, ci/*
	 * Newer versions of winaim have an empty type x004c TLV here.
	 */32(&tl, 0x0014, (fu32_t)ci->distrib#ifndef NOSSI
	/*
	 * If set, old-fashioned buddy lists will not work. You will need
	 * to use SSI.
	 */
	aim_addtlvtochain8(&tl, 0x004a, 0x01);
#endif*info;
	int ret = 0;

	info = (struct aim_authresp_info *)malloc(sizeof(struct aim_authresp_info));
	memset(info, 0, sizeof(struct aim_authresp_->sn = aim_gettlv_str(tlvlist, 0x0001, 1);
		strncpy(sess->sn, info->->errorcode = aim_gettlv16(tlvlist, 0x0008, 1);
	if (aim_gettlv(tlvlist, 0x0004, 1))
		info->->->cookielen = tmptlv->length;
		info->or @mac.com logins.
	 *   If you receive this TLV, then you are allowed to use 
	 *   family 0x0018 to check the status of your email.
	 * XXX - Not really true!
	 */
	if (aim_gettlv(tlvlist, 0x0011, 1))
		info->or @mac.com Means you can use the admin family? (0x0007)
	 *
	 */
	if (aim_gettlv(tlvlist, 0x0013, 1))
		info->regstatus = aim_gettlv16(tlvlist, 0x0013, 1);

	if (aim_gettlv(tlvlist, 0x0040, 1))
		info->->latestbeta.url = aim_gettlv_str(tlvlist, 0x0041, 1);
	if (aim_gettlv(tlvlist, 0x0042, 1))
		info->->->latestrelease.build = aim_gettlv32(tlvlist, 0x0044, 1);
	if (aim_gettlv(tlvlist, 0x0045, 1))
		info->->latestrelease.info = aim_gettlv_str(tlvlist, 0x0046, 1);
	if (aim_gettlv(tlvlist, 0x0047, 1))
		info->	/*
	 * URL to change password.
	 */
	if (aim_gettlv(tlvlist, 0x0054, 1))
		info->chpassurl = aim_gettlv_str(tlvlist, 0x0054, 1);

	/*
	 * Unknown.  Seen on an @mac.com screen name with value of 0x003f
	 */
	if (aim_gettlv(tlvlist, 0x0055, 1))
		;

	sess->authinfo = info;/* XXX - When GiantGrayPanda signed on AIM I got a thing asking me to register 
	 * for the netscape network.  This SNAC had a type 0x0058 TLV with length 10.  
	 * Data is 0x0007 0004 3e19 ae1e 0006 0004 0000 0005 */void auth_shutdown(aim_session_t *sess, aim_module_t *mod)
{
	if (sess->authinfo) {
		free(sess->authinfo->sn);
		free(sess->authinfo->bosip);
		free(sess->authinfo->errorurl);
		free(sess->authinfo->email);
		free(sess->authinfo->chpassurl);
		free(sess->authinfo->latestrelease.name);
		free(sess->authinfo->latestrelease.url);
		free(sess->authinfo->latestrelease.info);
		free(sess->authinfo->latestbeta.name);
		free(sess->authinfo->latestbeta.url);
		free(sess->authinfo->latestbeta.info);
		free(sess->authinfo);
	}
}

static int snachandler	mod->shutdown = auth_shutdown;

	return 0;
}
ENDREP
DELTA 130 31108 2384
SVN  ¢$£9%‚h€U Pœ †w© ƒˆ9€H u‹}ƒ „;œsƒ q¡1/*
 * Family 0x0009 - Basic Oscar Service.
 *
 * The functionality of this family has been replaced by SSI.
 */

#define FAIM_INTERNAL
#include <aim.h>

#include <string.h>

/* Subtype 0x0002 - Request BOS rights.Subtype 0x0003 - BOS Rights.ubtype 0x0004 - Set group permisson mask.Stubtypes 0x0005, 0x0006, 0x0007, and 0x0008 - Modify permit/deny lists.nde629ENDREP
DELTA 130 33505 1757
SVN  ›b²pW•m€ƒ …X…*· 8Š€l „J‘}€Š … –B€i S˜n€‚G j—t“ #˜n€O zšh» \*² u:’ ‚>€i oŠ}µ ƒtŒ3ƒ w*/*
 * Family 0x0003 - Old-style Buddylist Management (non-SSI).
 *
 */

#define FAIM_INTERNAL
#include <aim.h>

#include <string.h>

/*
 * Subtype 0x0002 - Request rights.
 *
 * Request Buddy List rights.
 *
 */
faim_export int aim_bos_reqbuddyrights(aim_session_t *sess, aim_conn_t *conn)
{
	return aim_genericreq_n(sess, conn, 0x0003, 0x0002);
}

/*
 * Subtype 0x0003 - Rights.
 *
 *//*
	 * TLV type 0x0003: Unknown.
	 *
	 * ICQ only?
	 *//*
 * Subtype 0x0004 - Add buddy to list.
 *
 * Adds a single buddy to your buddy list after login.
 * XXX TSubtype 0x0004 - Add multiple buddies to your buddy list.
 *
 * This just builds the "set buddy list" command then queues it.
 *
 * buddy_list = "Screen Name One&ScreenNameTwo&";
 *
 * XXX Clean this up.  
 *
 */
faim_export int aim_bos_setbuddylist(aim_session_t *sess, aim_conn_t *conn, const char *buddy_list)
{
	aim_frame_t *fr;
	aim_snacid_t snacid;
	int len = 0;
	char *localcpy = NULL;
	char *tmpptr = NULL;

	if (!buddy_list || !(localcpy = strdup(buddy_list))) 
		return -EINVAL;

	for (tmpptr = strtok(localcpy, "&"); tmpptr; ) {
		faimdprintf(sess, 2, "---adding: %s (%d)\n", tmpptr, strlen(tmpptr));
		len += 1 + strlen(tmpptr);
		tmpptr = strtok(NULL, "&");
	}

	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+len)))
		return -ENOMEM;

	snacid = aim_cachesnac(sess, 0x0003, 0x0004, 0x0000, NULL, 0);
	aim_putsnac(&fr->data, 0x0003, 0x0004, 0x0000, snacid);

	strncpy(localcpy, buddy_list, strlen(buddy_list) + 1);

	for (tmpptr = strtok(localcpy, "&"); tmpptr; ) {

		faimdprintf(sess, 2, "---adding: %s (%d)\n", tmpptr, strlen(tmpptr));

		aimbs_put8(&fr->data, strlen(tmpptr));
		aimbs_putraw(&fr->data, tmpptr, strlen(tmpptr));
		tmpptr = strtok(NULL, "&");
	}

	aim_tx_enqueue(sess, fr);

	free(localcpy);

	return 0;
}

/*
 * Subtype 0x0005 - Remove buddy from list.
 /* 
 * Subtype 0x000b
 *
 * XXX Why would we send this?
 *
 */
faim_export int aim_sendbuddyoncoming(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *info)
{
	aim_frame_t *fr;
	aim_snacid_t snacid;

	if (!sess || !conn || !info152)))
		return -ENOMEM;

	snacid = aim_cachesnac(sess, 0x0003, 0x000b, 0x0000, NULL, 0);
	
	aim_putsnac(&fr->data, 0x0003, 0x000b, 0x0000, snacid);
	aim_putuserinfo(&fr->data, info);

	aim_tx_enqueue(sess, fr);

	return 0;
}

/* 
 * Subtype 0x000c
 *
 * XXX Why would we send this?
 *
 */
faim_export int aim_sendbuddyoffgoingess || !conn || !snc, 0x0000, NULL, 0);
	
	aim_putsnac(&fr->data, 0x0003, 0x000c, 0x0000, snacid);/*
 * Subtypes 0x000b and 0x000c - Change in buddy status
 's close enough to run
 * through aim_info_extractaim_info_extract()int ret = 0;
	aim_userinfo_t userinfo;
	aim_rxcallback_t userfunc;

	aim_info_extract(sess, bs, &userinfo&userinfo);

	aim_info_free(&userinfo);

	return ret;629ENDREP
DELTA 130 35275 6796
SVN  ÿüˆQ€y |e ŠI„E — j€F ‚w¸e€g ‡a½B  Å"– (áy ‚<È ‡{ËŒ Œ!Óº ßt€A bà  ‚Qá`Œ )ä@€Q ‡H€0 ˜E€q ƒšf† ‚_ž– ŒSæŒ …jòm™ „oøUŸ qþ/*
 * Family 0x000e - Routines for the Chat service.
 *
 */

#define FAIM_INTERNAL
#include <aim.h> 

#include <string.h>	/*
	 * Cookie
	 */
	for (i = 0; i < 8; i++)
		ckstr[i] = (fu8_t)rand(/* ICBM Header */
	aimbs_putraw(&fr->data, ckstr, 8); /* Cookie */
	aimbs_put16(&fr->data, 0x0002); /* Channel */
	aimbs_put8(&fr->data, strlen(sn)); /* Screename length */
	aimbs_putraw(&fr->data, sn, strlen(sn)); /* Screenname */Subtype 0x0002 -/
static int infoupdatinfo_extract
				&roominfo,
				roomname,
				usercount,
				userinfo,
	while (usercount > 0)
		aim_info_free(&userinfo[--usercount]);
/* Subtypes 0x0003 and 0x0004 */info_extractaim_info_free(userinfo);
	free(userinfo);

	return ret;
}

/*
 * Subtype 0x0005 -	/*
	 * Cookie
	 *
	 * XXX mkcookie should generate the cookie and cache it in one
	 * operation to preserve uniqueness.
	 */
	for (i = 0; i < 8; i++)
		ckstr[i] = (fu8_t)rand(/* ICBM Header */
	aimbs_putraw(&fr->data, ckstr, 8); /* Cookie */
	aimbs_put16(&fr->data, 0x0003); /* Channel */msglen/*
 * Subtype 0x0006
 info_extractaim_info_free(&userinfo);10;
	mod->toolversion = 0x0629;ENDREP
DELTA 130 42084 4696
SVN  ÙÙUƒ€‚a ‚>˜ ÀƒQ ”9ÃSƒ uØ/*
 * Family 0x000d - Handle ChatNav.
 *
 * The ChatNav(igation) service does various things to keep chat
 * alive.  It provides room information, room searching and creating,
 * as well as giving users the right ("permission") to use chat.
 *
 */

#define FAIM_INTERNAL
#include <aim.h>

/*
 * Subtype 0x0002
 *
 * conn must be a chatnav connection!
 */*
 * Subtype 0x0008
 */ubtype 0x0009
 629ENDREP
DELTA 205 17578 2934
SVN  ­@­p_ ;  „n8Œ ’$€S \Ÿdint ret = 0; = userfunc(sess, rx, new, havenewmail);

	aim_freetlvchain(&tlvlist);

	return retENDREP
DELTA 130 48771 12123
SVN  ÎlÌo\‡g z£ ¥Zr •m¨,€‚+ —MÀ€U Š2Ø% È,âV  ªr€q ˆQ«{€	 r´Y€ Šµ ÀQœ „cÇ< lÌ  \Í#ifdef _WIN32
#include "win32dep.h"	aim_rxcallback_t userfunc;

	if (deadconn->fd >= 3)
		close(deadconn->fd);

	deadconn->fd = -1;

	if ((userfunc = aim_callhandler(deadconn->sessv, deadconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNDEAD)))
		userfunc(deadconn->sessv, NULL, deadconn);

	if (deadconn->handlerlist)
		aim_clearhandlers(		if (write(fd, buf, i) < i) {
			*statusret = errno;
			close(fd);
			return -1;
		}icq_info = NULL;
	sess->oft_infosess->ssi.received_data = 0;
	sess->ssi.numitems = 0;
	sess->ssi.official = NULL;
	sess->ssi.local = NULL;
	sess->ssi.pending = NULL;
	sess->ssi.timestamp = (time_t)0;
	sess->ssi.waiting_for_ack = 0;

	sess->authinfo = NULL;
	sess->emailinfo	aim__registermodule(sess, odir_modfirst); */ /* kretch */
	/*	aim__registermodule(sess, bart_modfirst); */ /* kretch */
	/* missing 0x11 /* XXX - Make sure this isn't sent for AIM */
	/* missing 0x16 */
	aim__registermodule(sess, auth_modfirst);
	aim__registermodule(sess, emailfcntl(conn->fd, F_SETFL, 0);ENDREP
DELTA 130 60907 14048
SVN  ‚‰ ‚‰ ÃP ƒ Å0ÃP -gENDREP
DELTA 130 74968 195
SVN  BE  ƒ+ -gENDREP
DELTA 130 76236 16864
SVN  ƒ•~Úo<¿€© ‚r«ˆ )® (¯B _°m• ²< ~³V¦ Wµ€‡^ Šm€ d‹p† ŒU€W HŽ;€„_ J–>€± [¾8‰ o¿± B³&€B P¶6ˆ fÄ=€k &¸s€K Òv€Q VÔ7¦ HÕ9º Öx  Ø€ˆL AÚ?’ KÞ>€V KÞ>€B KÞ>€…C UÞ>€ÀY/*
 * Oscar File transfer (OFT) and Oscar Direct Connect (ODC).
 * (ODC is also referred to as DirectIM and IM Image.)
 *
 * There are a few static helper functions at the top, then 
 * ODC stuff, then ft stuff.
 *
 * I feel like this is a good place to explain OFT, so I'm going to 
 * do just that.  Each OFT packet has a header type.  I guess this 
 * is pretty similar to the subtype of a SNAC packet.  The type 
 * basically tells the other client the meaning of the OFT packet.  
 * There are two distinct types of file transfer, which I usually 
 * call "sendfile" and "getfile."  Sendfile is when you send a file 
 * to another AIM user.  Getfile is when you share a group of files, 
 * and other users request that you send them the files.
 *
 * A typical sendfile file transfer goes like this:
 *   1) Sender sends a channel 2 ICBM telling the other user that 
 *      we want to send them a file.  At the same time, we open a 
 *      listener socket (this should be done before sending the 
 *      ICBM) on some port, and wait for them to connect to us.  
 *      The ICBM we sent should contain our IP address and the port 
 *      number that we're listening on.
 *   2) The receiver connects to the sender on the given IP address 
 *      and port.  After the connection is established, the receiver 
 *      sends an ICBM signifying that we are ready and waiting.
 *   3) The sender sends an OFT PROMPT message over the OFT 
 *      connection.
 *   4) The receiver of the file sends back an exact copy of this 
 *      OFT packet, except the cookie is filled in with the cookie 
 *      from the ICBM.  I think this might be an attempt to verify 
 *      that the user that is connected is actually the guy that 
 *      we sent the ICBM to.  Oh, I've been calling this the ACK.
 *   5) The sender starts sending raw data across the connection 
 *      until the entire file has been sent.
 *   6) The receiver knows the file is finished because the sender 
 *      sent the file size in an earlier OFT packet.  So then the 
 *      receiver sends the DONE thingy (after filling in the 
 *      "received" checksum and size) and closes the connection.
 */

#define FAIM_INTERNAL
#ifdef HAVE_CONFIG_H
#include  <config.h>
#endif

#include <aim.h>

#ifndef _WIN32
#include <stdio.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/utsname.h> /* for aim_odc_initiate */
#include <arpa/inet.h> /* for inet_ntoa */
#define G_DIR_SEPARATOR '/'
#endif

#ifdef _WIN32
#include "win32dep.h"
#endif

struct aim_odc_intdata {
	fu8_t cookie[8];
	char sn[MAXSNLEN+1];
	char ip[22];
};

/**
 * Convert the directory separator from / (0x2f) to ^A (0x01)
 *
 * @param name The filename to convert.
 */
static void aim_oft_dirconvert_tostupid(char *name)
{
	while (name[0]) {
		if (name[0] == 0x01)
			name[0] = G_DIR_SEPARATOR;
		name++;
	}
}

/**
 * Convert the directory separator from ^A (0x01) to / (0x2f)
 *
 * @param name The filename to convert.
 */
static void aim_oft_dirconvert_fromstupid(char *name)
{
	while (name[0]) {
		if (name[0] == G_DIR_SEPARATOR)
			name[0] = 0x01;
		name++;
	}
}

/**
 * Calculate oft checksum of buffer
 *
 * Prevcheck should be 0xFFFF0000 when starting a checksum of a file.  The 
 * checksum is kind of a rolling checksum thing, so each time you get bytes 
 * of a file you just call this puppy and it updates the checksum.  You can 
 * calculate the checksum of an entire file by calling this in a while or a 
 * for loop, or something.
 *
 * Thanks to Graham Booker for providing this improved checksum routine, 
 * which is simpler and should be more accurate than Josh Myer's original 
 * code. -- wtm
 *
 * This algorithim works every time I have tried it.  The other fails 
 * sometimes.  So, AOL who thought this up?  It has got to be the weirdest 
 * checksum I have ever seen.
 *
 * @param buffer Buffer of data to checksum.  Man I'd like to buff her...
 * @param bufsize Size of buffer.
 * @param prevcheck Previous checksum.
 */
faim_export fu32_t aim_oft_checksum_chunk(const fu8_t *buffer, int bufferlen, fu32_t prevcheck)
{
	fu32_t check = (prevcheck >> 16) & 0xffff, oldcheck;
	int i;
	unsigned short val;

	for (i=0; i<bufferlen; i++) {
		oldcheck = check;
		if (i&1)
			val = buffer[i];
		else
			val = buffer[i] << 8;
		check -= val;
		/*
		 * The following appears to be necessary.... It happens 
		 * every once in a while and the checksum doesn't fail.
		 */
		if (check > oldcheck)
			check--;
	}
	check = ((check & 0x0000ffff) + (check >> 16));
	check = ((check & 0x0000ffff) + (check >> 16));
	return check << 16;
}

faim_export fu32_t aim_oft_checksum_file(char *filename) {
	FILE *fd;
	fu32_t checksum = 0xffff0000;

	if ((fd = fopen(filename, "rb"))) {
		int bytes;
		fu8_t buffer[1024];

		while ((bytes = fread(buffer, 1, 1024, fd)))
			checksum = aim_oft_checksum_chunk(buffer, bytes, checksum);
		fclose(fd);
	}

	return checksum;
}

/**
 * Create a listening socket on a given port.
 *
 * 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
 *
 * @param portnum The port number to bind to.
 * @return The file descriptor of the listening socket. any IP  freeaddrinfo(ressave)");
		close(listenfd);
		return -1;
	}");
		close(listenfd);
		return -1;
	}
#endif

	if (listen(listenfd, 4) != 0) {
		perror("listen");
		close(listenfd);
		return -1;
	}
	fcntl(listenfd, F_SETFL, O_NONBLOCK);

	return listenfd;
}

/**
 * After establishing a listening socket, this is called to accept a connection.  It
 * clones the conn used by the listener, and passes both of these to a signal handler.
 * The signal handler should close the listener conn and keep track of the new conn,
 * since this is what is used for file transfers and what not.
 *
 * @param sess The session.
 * @param cur The conn the incoming connection is on.
 * @return Return 0 if no errors, otherwise return the error number.
 */
faim_export int aim_handlerendconnect(aim_session_t *sess, aim_conn_t *cur)
{
	int acceptfd = 0;
	struct sockaddr addr;
	socklen_t addrlen = sizeof(addr);
	int ret = 0;
	aim_conn_t *newconn;
	char ip[20];
	int port;

	if ((acceptfd = accept(cur->fd, &addr, &addrlen)) == -1)
		return 0; /* not an error */

	if (

	strncpy(ip, inet_ntoa(((struct sockaddr_in *)&addr)->sin_addr), sizeof(ip));
	port = ntohs(((struct sockaddr_in *)&addr)->sin_port);ENOMEM
		aim_rxcallback_t userfunc;
		struct aim_odc_intdata *priv;

		priv = (struct aim_odc		snprintf(priv->ip, sizeof(priv->ip), "%s:%u", ip, port);

		if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIM_ESTABLISHED)))
			ret = userfunc(sess, NULL, newconn, cur);

	} else if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE) {
	} else if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_SENDFILE) {
		aim_rxcallback_t userfunc;

		if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_ESTABLISHED)))
			ret = userfunc(sess, NULL, newconn, cur);

	} else {
		faimdprintf(sess, 1,"Got a connection on a listener that's not rendezvous.  Closing connectioSend client-to-client typing notification over an established direct connection.
 *
 * @param sess The session.
 * @param conn The already-connected ODC connection.
 * @param typing If 0x0002, sends a "typing" message, 0x0001 sends "typed," and 
 *        0x0000 sends "stopped."
 * @return Return 0 if no errors, otherwise return the error number.
 */
faim_export int aim_odc_send_typing(aim_session_t *sess, aim_conn_t *conn, int typing)
{
	struct aim_odc_intdata *intdata = (struct aim_odc_intdata *)conn->internal;
	aim_frame_t *fr;
	aim_bstream_t *hdrbs;
	fu8_t *hdr;
	int hdrlen = 0x44;

	if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS))
		return -EINVAL;

	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0001, 0)))
		return -ENOMEM;
	memcpy(fr->hdr.rend.magic, "ODC2", 4);
	fr->hdr.rend.hdrlen = hdrlen;

	if (!(hdr = calloc(1, hdrlen))) {
		aim_frame_destroy(fr);
		return -ENOMEM;
	}

	hdrbs = &(fr->data);
	aim_bstream_init(hdrbs, hdr, hdrlen);

	aimbs_put16(hdrbs, 0x0006);
	aimbs_put16(hdrbs, 0x0000);
	aimbs_putraw(hdrbs, intdata->cookie, 8);
	aimbs_put16(hdrbs, 0x0000);
	aimbs_put16(hdrbs, 0x0000);
	aimbs_put16(hdrbs, 0x0000);
	aimbs_put16(hdrbs, 0x0000);
	aimbs_put32(hdrbs, 0x00000000);
	aimbs_put16(hdrbs, 0x0000);
	aimbs_put16(hdrbs, 0x0000);
	aimbs_put16(hdrbs, 0x0000);

	if (typing == 0x0002)
		aimbs_put16(hdrbs, 0x0002 | 0x0008);
	else if (typing == 0x0001)
		aimbs_put16(hdrbs, 0x0002 | 0x0004);
	else
		aimbs_put16(hdrbs, 0x0002);

	aimbs_put16(hdrbs, 0x0000);
	aimbs_put16(hdrbs, 0x0000);
	aimbs_putraw(hdrbs, sess->sn, strlen(sess->sn));

	aim_bstream_setpos(hdrbs, 52); /* bleeehh */

	aimbs_put8(hdrbs, 0x00);
	aimbs_put16(hdrbs, 0x0000);
	aimbs_put16(hdrbs, 0x0000);
	aimbs_put16(hdrbs, 0x0000);
	aimbs_put16(hdrbs, 0x0000);
	aimbs_put16(hdrbs, 0x0000);
	aimbs_put16(hdrbs, 0x0000);
	aimbs_put16(hdrbs, 0x0000);
	aimbs_put8(hdrbs, 0x00);

	/* end of hdr */

	aim_tx_enqueue(sess, fr);

	return 0;
}

/**
 * Send client-to-client IM over an established direct connection.
 * Call this just like you would aim_send_im, to send a directim.
 * 
 * @param sess The session.
 * @param conn The already-connected ODC connection.
 * @param msg Null-terminated string to send.
 * @param len The length of the message to send, including binary data.
 * @param encoding 0 for ascii, 2 for Unicode, 3 for ISO 8859-1.
 * @param isawaymsg 0 if this is not an auto-response, 1 if it is.
 * @return Return 0 if no errors, otherwise return the error number.
 */
faim_export int aim_odc_send_im(aim_session_t *sess, aim_conn_t *conn, const char *msg, int len, int encoding, int isawaymsg)
{
	aim_frame_t *fr;
	aim_bstream_t *hdrbs;
	struct aim_odc_intdata *intdata = (struct aim_odc_intdata *)conn->internal;
	int hdrlen = 0x44;
	fu8_t *hdr;

	if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS) || !msg)
		return -EINVAL;

	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x01, 0)))
		return -ENOMEM;

	memcpy(fr->hdr.rend.magic, "ODC2", 4);
	fr->hdr.rend.hdrlen = hdrlen;

	if (!(hdr = calloc(1, hdrlen + len))) {
		aim_frame_destroy(fr);
		return -ENOMEM;
	}

	hdrbs = &(fr->data);
	aim_bstream_init(hdrbs, hdr, hdrlen + len);

	aimbs_put16(hdrbs, 0x0006);
	aimbs_put16(hdrbs, 0x0000);
	aimbs_putraw(hdrbs, intdata->cookie, 8);
	aimbs_put16(hdrbs, 0x0000);
	aimbs_put16(hdrbs, 0x0000);
	aimbs_put16(hdrbs, 0x0000);
	aimbs_put16(hdrbs, 0x0000);
	aimbs_put32(hdrbs, len);
	aimbs_put16(hdrbs, encoding);
	aimbs_put16(hdrbs, 0x0000);
	aimbs_put16(hdrbs, 0x0000);

	/* flags - used for typing notification and to mark if this is an away message */
	aimbs_put16(hdrbs, 0x0000 | isawaymsg);

	aimbs_put16(hdrbs, 0x0000);
	aimbs_put16(hdrbs, 0x0000);
	aimbs_putraw(hdrbs, sess->sn, strlen(sess->sn));

	aim_bstream_setpos(hdrbs, 52); /* bleeehh */

	aimbs_put8(hdrbs, 0x00);
	aimbs_put16(hdrbs, 0x0000);
	aimbs_put16(hdrbs, 0x0000);
	aimbs_put16(hdrbs, 0x0000);
	aimbs_put16(hdrbs, 0x0000);
	aimbs_put16(hdrbs, 0x0000);
	aimbs_put16(hdrbs, 0x0000);
	aimbs_put16(hdrbs, 0x0000);
	aimbs_put8(hdrbs, 0x00);

	/* end of hdr2 */

#if 0 /* XXX - this is how you send buddy icon info... */	
	aimbs_put16(hdrbs, 0x0008);
	aimbs_put16(hdrbs, 0x000c);
	aimbs_put16(hdrbs, 0x0000);
	aimbs_put16(hdrbs, 0x1466);
	aimbs_put16(hdrbs, 0x0001);
	aimbs_put16(hdrbs, 0x2e0f);
	aimbs_put16(hdrbs, 0x393e);
	aimbs_put16(hdrbs, 0xcac8);
#endif
	aimbs_putraw(hdrbs, msg, len);

	aim_tx_enqueue(sess, fr);

	return 0;
}

/**
 * Get the screen name of the peer of a direct connection.
 * 
 * @param conn The ODC connection.
 * @return The screen name of the dude, or NULL if there was an anomaly.
 */
faim_export const char *aim_odc_getsn(aim_conn_t *conn)
{
	struct aim_odc_intdata *intdata;

	if (!conn || !conn->internal)
		return NULL;

	if ((conn->type != AIM_CONN_TYPE_RENDEZVOUS) || 
			(conn->subtype != AIM_CONN_SUBTYPE_OFT_DIRECTIM))
		return NULL;

	intdata = (struct aim_odc_intdata *)conn->internal;

	return intdata->sn;
}

/**
 * Find the conn of a direct connection with the given buddy.
 *
 * @param sess The session.
 * @param sn The screen name of the buddy whose direct connection you want to find.
 * @return The conn for the direct connection with the given buddy, or NULL if no 
 *         connection was found.
 */
faim_export aim_conn_t *aim_odc_getconn(aim_session_t *sess, const char *sn)
{
	aim_conn_t *cur;
	struct aim_odc_intdata *intdata;

	if (!sess || !sn || !strlen(sn))
		return NULL;

	for (cur = sess->connlist; cur; cur = cur->next) {
		if ((cur->type == AIM_CONN_TYPE_RENDEZVOUS) && (cur->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM)) {
			intdata = cur->internal;
			if (!aim_sncmp(intdata->sn, sn))
				return cur;
		}
	}

	return NULL;
}

/**
 * For those times when we want to open up the direct connection channel ourselves.
 *
 * You'll want to set up some kind of watcher on this socket.  
 * When the state changes, call aim_handlerendconnection with 
 * the connection returned by this.  aim_handlerendconnection 
 * will accept the pending connection and stop listening.
 *
 * @param sess The session
 * @param sn The screen name to connect to.
 * @return The new connection.
 */
faim_export aim_conn_t *aim_odc_initiate(aim_session_t *sess, const char *sn)
{
	aim_conn_t *newconn;
	aim_msgcookie_t *cookie;
	struct aim_odcaim_util_im_sendch2_odcrequest(sess, ck, sn, localip, portodc_intdata *)calloc(1, sizeof(struct aim_odc_intdata));

	memcpy(priv->cookie, ck, 8);
	strncpy(priv->sn, sn, sizeof(priv->sn));
	cookie->data = priv;
	aim_cachecookie(sess, cookie);

	/* XXX -LISTENERodc_intdata *)calloc(1, sizeof(struct aim_odc_intdata));

	memcpy(priv->cookie, ck, 8);
	strncpy(priv->sn, return newconn;
}

/**
 * Connect directly to the given buddy for directim. @param sess The Godly session.
 * @param sn The screen name we're connecting to.  I hope it's a girl...
 * @param addr Address to connect to.
 * @return The new connection.
 */
faim_export aim_conn_t *aim_odc
	aim_conn_t *newconn;
	struct aim_odccalloc(1, sizeof(struct aim_odc_intdata))))
		return NULL;-newconn->internal = intdata;
	newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM;

	return newconn;
}

/**
 * Sometimes you just don't know with these kinds of people.
 *
 * @param sess The session.
 * @param conn The ODC connection of the incoming data.
 * @param frr The frame allocated for the incoming data.
 * @param bs It stands for "bologna sandwich."
 * @return Return 0 if no errors, otherwise return the error number.
 */
static int handlehdr_odc(aim_session_t *sess, aim_conn_t *conn, aim_frame_t *frr, aim_bstream_t *bs)
{
	aim_frame_t fr;
	int ret = 0;
	aim_rxcallback_t userfunc;
	fu32_t payloadlength;
	fu16_t flags, encoding;
	char *snptr = NULL;

	fr.conn = conn;

	/* AAA - ugly */
	aim_bstream_setpos(bs, 20);
	payloadlength = aimbs_get32(bs);

	aim_bstream_setpos(bs, 24);
	encoding = aimbs_get16(bs);

	aim_bstream_setpos(bs, 30);
	flags = aimbs_get16(bs);

	aim_bstream_setpos(bs, 36);
	/* XXX - create an aimbs_getnullstr function? */
	snptr = aimbs_getstr(bs, 32); /* Next 32 bytes contain the sn, padded with null chars */

	faimdprintf(sess, 2, "faim: OFT frame: handlehdr_odc& 0x0008) {
		if (TYPING)))
			ret = userfunc(sess, &fr, snptr, 2);
	} else if (flags & 0x0004) {
		if (TYPING)))
			ret = userfunc(sess, &fr, snptr, 1);
	} else {
		if (TYPING)))
			ret = userfunc(sess, &fr, snptr, 0);
	}

	if (payloadlength) {
		char *msg;
		int recvd = 0;
		int i, isawaymsg;

		isawaymsg = flags & 0x0001;

		if (!(msg = calloc(1, payloadlength+1))) {
			free(snptr);
			return -ENOMEM;
		}

		while (payloadlength - recvd) {
			if (payloadlength - recvd >= 1024)
				i = aim_recv(conn->fd, &msg[recvd], 1024);
			else 
				i = aim_recv(conn->fd, &msg[recvd], payloadlength - recvd);
			if (i <= 0) {
				free(msg);
				free(snptr);
				return -1;
			}
			recvd = recvd + i;
			if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER)))
				ret = userfunc(sess, &fr, snptr, (double)recvd / payloadlength);
		}
		
		if ()
			ret = userfunc(sess, &fr, snptr, msg, payloadlength, encoding, isawaymsg);

		free(msg);
	}

	free(snptr);

	return ret;
}

faim_export struct aim_oft_info *aim_oft_createinfo(aim_session_t *sess, const fu8_t *cookie, const char *sn, const char *ip, fu16_t port, fu32_t size, fu32_t modtime, char *filename)
{
	struct aim_oft_info *new;

	if (!sess)
		return NULL;

	if (!(new = (struct aim_oft_info *)calloc(1, sizeof(struct aim_oft_info))))
		return NULL;

	new->sess = sess;
	if (cookie)
		memcpy(new->cookie, cookie, 8);
	if (ip)
		new->clientip = strdup(ip);
	if (sn)
		new->sn = strdup(sn);
	new->port = port;
	new->fh.totfiles = 1;
	new->fh.filesleft = 1;
	new->fh.totparts = 1;
	new->fh.partsleft = 1;
	new->fh.totsize = size;
	new->fh.size = size;
	new->fh.modtime = modtime;
	new->fh.checksum = 0xffff0000;
	new->fh.rfrcsum = 0xffff0000;
	new->fh.rfcsum = 0xffff0000;
	new->fh.recvcsum = 0xffff0000;
	strncpy(new->fh.idstring, "OFT_Windows ICBMFT V1.1 32", 31);
	if (filename)
		strncpy(new->fh.name, filename, 63);

	new->next = sess->oft_info;
	sess->oft_info = new;

	return new;
}

/**
 * Remove the given oft_info struct from the oft_info linked list, and 
 * then free its memory.
 *
 * @param sess The session.
 * @param oft_info The aim_oft_info struct that we're destroying.
 * @return Return 0 if no errors, otherwise return the error number.
 */
faim_export int aim_oft_destroyinfo(struct aim_oft_info *oft_info)
{
	aim_session_t *sess;

	if (!oft_info || !(sess = oft_info->sess))
		return -EINVAL;

	if (sess->oft_info && (sess->oft_info == oft_info)) {
		sess->oft_info = sess->oft_info->next;
	} else {
		struct aim_oft_info *cur;
		for (cur=sess->oft_info; (cur->next && (cur->next!=oft_info)); cur=cur->next);
		if (cur->next)
			cur->next = cur->next->next;
	}

	free(oft_info->sn);
	free(oft_info->proxyip);
	free(oft_info->clientip);
	free(oft_info->verifiedip);
	free(oft_info);

	return 0;
}

/**
 * Creates a listener socket so the other dude can connect to us.
 *
 * You'll want to set up some kind of watcher on this socket.  
 * When the state changes, call aim_handlerendconnection with 
 * the connection returned by this.  aim_handlerendconnection 
 * will accept the pending connection and stop listening.
 *
 * @param sess The session.
 * @param oft_info File transfer information associated with this 
 *        connection.
 * @return Return 0 if no errors, otherwise return the error number.
 */
faim_export int aim_sendfile_listen(aim_session_t *sess, struct aim_oft_info *oft_info)
{
	int listenfd;

	if (!oft_info)
		return -EINVAL;

	if ((listenfd = listenestablish(oft_info->port)) == -1)
		return 1;

	if (!(oft_info->conn = aim_newconn(sess, AIM_CONN_TYPE_LISTENER, NULL))) {
		close(listenfd);
		return -ENOMEM;
	}

	oft_info->conn->fd = listenfd;
	oft_info->conn->subtype = AIM_CONN_SUBTYPE_OFT_SENDFILE;
	oft_info->conn->lastactivity = time(NULL);

	return 0;
}

/**
 * Extract an &aim_fileheader_t from the given buffer.
 *
 * @param bs The should be from an incoming rendezvous packet.
 * @return A pointer to new struct on success, or NULL on error.
 */
static struct aim_fileheader_t *aim_oft_getheader(aim_bstream_t *bs)
{
	struct aim_fileheader_t *fh;

	if (!(fh = calloc(1, sizeof(struct aim_fileheader_t))))
		return NULL;

	/* The bstream should be positioned after the hdrtype. */
	aimbs_getrawbuf(bs, fh->bcookie, 8);
	fh->encrypt = aimbs_get16(bs);
	fh->compress = aimbs_get16(bs);
	fh->totfiles = aimbs_get16(bs);
	fh->filesleft = aimbs_get16(bs);
	fh->totparts = aimbs_get16(bs);
	fh->partsleft = aimbs_get16(bs);
	fh->totsize = aimbs_get32(bs);
	fh->size = aimbs_get32(bs);
	fh->modtime = aimbs_get32(bs);
	fh->checksum = aimbs_get32(bs);
	fh->rfrcsum = aimbs_get32(bs);
	fh->rfsize = aimbs_get32(bs);
	fh->cretime = aimbs_get32(bs);
	fh->rfcsum = aimbs_get32(bs);
	fh->nrecvd = aimbs_get32(bs);
	fh->recvcsum = aimbs_get32(bs);
	aimbs_getrawbuf(bs, fh->idstring, 32);
	fh->flags = aimbs_get8(bs);
	fh->lnameoffset = aimbs_get8(bs);
	fh->lsizeoffset = aimbs_get8(bs);
	aimbs_getrawbuf(bs, fh->dummy, 69);
	aimbs_getrawbuf(bs, fh->macfileinfo, 16);
	fh->nencode = aimbs_get16(bs);
	fh->nlanguage = aimbs_get16(bs);
	aimbs_getrawbuf(bs, fh->name, 64); /* XXX - filenames longer than 64B */

	return fh;
} 

/**
 * Fills a buffer with network-order fh data
 *
 * @param bs A bstream to fill -- automatically initialized
 * @param fh A struct aim_fileheader_t to get data from.
 * @return Return non-zero on error.
 */
static int aim_oft_buildheader(aim_bstream_t *bs, struct aim_fileheader_t *fh)
{ 
	fu8_t *hdr;

	if (!bs || !fh)
		return -EINVAL;

	if (!(hdr = (unsigned char *)calloc(1, 0x100 - 8)))
		return -ENOMEM;

	aim_bstream_init(bs, hdr, 0x100 - 8);
	aimbs_putraw(bs, fh->bcookie, 8);
	aimbs_put16(bs, fh->encrypt);
	aimbs_put16(bs, fh->compress);
	aimbs_put16(bs, fh->totfiles);
	aimbs_put16(bs, fh->filesleft);
	aimbs_put16(bs, fh->totparts);
	aimbs_put16(bs, fh->partsleft);
	aimbs_put32(bs, fh->totsize);
	aimbs_put32(bs, fh->size);
	aimbs_put32(bs, fh->modtime);
	aimbs_put32(bs, fh->checksum);
	aimbs_put32(bs, fh->rfrcsum);
	aimbs_put32(bs, fh->rfsize);
	aimbs_put32(bs, fh->cretime);
	aimbs_put32(bs, fh->rfcsum);
	aimbs_put32(bs, fh->nrecvd);
	aimbs_put32(bs, fh->recvcsum);
	aimbs_putraw(bs, fh->idstring, 32);
	aimbs_put8(bs, fh->flags);
	aimbs_put8(bs, fh->lnameoffset);
	aimbs_put8(bs, fh->lsizeoffset);
	aimbs_putraw(bs, fh->dummy, 69);
	aimbs_putraw(bs, fh->macfileinfo, 16);
	aimbs_put16(bs, fh->nencode);
	aimbs_put16(bs, fh->nlanguage);
	aimbs_putraw(bs, fh->name, 64);	/* XXX - filenames longer than 64B */

	return 0;
}

/**
 * Create an OFT packet based on the given information, and send it on its merry way.
 *
 * @param sess The session.
 * @param type The subtype of the OFT packet we're sending.
 * @param oft_info The aim_oft_info struct with the connection and OFT 
 *        info we're sending.
 * @return Return 0 if no errors, otherwise return the error number.
 */
faim_export int aim_oft_sendheader(aim_session_t *sess, fu16_t type, struct aim_oft_info *oft_info)
{
	aim_frame_t *fr;

	if (!sess || !oft_info || !oft_info->conn || (oft_info->conn->type != AIM_CONN_TYPE_RENDEZVOUS))
		return -EINVAL;

#if 0
	/*
	 * If you are receiving a file, the cookie should be null, if you are sending a 
	 * file, the cookie should be the same as the one used in the ICBM negotiation 
	 * SNACs.
	 */
	fh->lnameoffset = 0x1a;
	fh->lsizeoffset = 0x10;

	/* apparently 0 is ASCII, 2 is UCS-2 */
	/* it is likely that 3 is ISO 8859-1 */
	/* I think "nlanguage" might be the same thing as "subenc" in im.c */
	fh->nencode = 0x0000;
	fh->nlanguage = 0x0000;
#endif

	aim_oft_dirconvert_tostupid(oft_info->fh.name);

	if (!(fr = aim_tx_new(sess, oft_info->conn, AIM_FRAMETYPE_OFT, type, 0)))
		return -ENOMEM;

	if (aim_oft_buildheader(&fr->data, &oft_info->fh) == -1) {
		aim_frame_destroy(fr);
		return -ENOMEM;
	}

	memcpy(fr->hdr.rend.magic, "OFT2", 4);
	fr->hdr.rend.hdrlen = aim_bstream_curpos(&fr->data);

	aim_tx_enqueue(sess, fr);

	return 0;
}

/**
 * Handle incoming data on a rendezvous connection.  This is analogous to the 
 * consumesnac function in rxhandlers.c, and I really think this should probably 
 * be in rxhandlers.c as well, but I haven't finished cleaning everything up yet.
 *
 * @param sess The session.
 * @param fr The frame allocated for the incoming data.
 * @return Return 0 if the packet was handled correctly, otherwise return the 
 *         error number.
 */
faim_internal int aim_rxdispatch_rendezvous(aim_session_t *sess, aim_frame_t *fr)
{
	aim_conn_t *conn = fr->conn;
	int ret = 1;

	if (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) {
		if (fr->hdr.rend.type == 0x0001)
			ret = handlehdr_odc(sess, conn, fr, &fr->data);
		else
			faimdprintf(sess, 0, "faim: ODC directim frame unknown, type is %04x\n", fr->hdr.rend.type);

	} else {
		aim_rxcallback_t userfunc;
		struct aim_fileheader_t *header = aim_oft_getheader(&fr->data);
		aim_oft_dirconvert_fromstupid(header->name); /* XXX - This should be client-side */

		if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, fr->hdr.rend.type)))
			ret = userfunc(sess, fr, conn, header->bcookie, header);

		free(header);
	}

	if (ret == -1)
		aim_conn_close(conn);

	return ret;
}
ENDREP
DELTA 130 93113 2169
SVN  ³[€6º• † 9nŠ ƒC‚3€… eŽ8€x „*@€ D†>† t•%š …W–ƒ T›q€‚ p•)š …W–ƒ T›q€m ‡_”s ‡x€†. …Wœy– ‚E¢a€r ‚V¥o€¡B M¥y€P M¥y€‚B ‚¯9€^ .±9 c²h­/*
 * Family 0x0015 -hideip+4+2+2+2+47d0); /* I command thee. */
	aimbs_putle16(&fr->data, snacid); /* eh. */
	aimbs_putle16(&fr->data, 0x0424); /* shrug. */
	aimbs_putle16(&fr->data, 0x0001);
	aimbs_putle16(&fr->data, 0x0001);

	aim_tx_enqueue(sess, fr);

	return 0;
}

/**
 * Change your ICQ password.
 *
 * @param sess The oscar session
 * @param passwd The new password.  If this is longer than 8 characters it 
 *        will be truncated.
 * @return Return 0 if no errors, otherwise return the error number.
 */
faim_export int aim_icq_changepasswd(aim_session_t *sess, const char *passwd)
{
	aim_conn_t *conn;
	aim_frame_t *fr;
	aim_snacid_t snacid;
	int bslen, passwdlen;

	if (!passwdpasswdlen = strlen(passwd);
	if (passwdlen > MAXICQPASSLEN)
		passwdlen = MAXICQPASSLEN;
	bslen = 2+4+2+2+2+2+passwdlen+42e); /* shrug. */
	aimbs_putle16(&fr->data, passwdlen+1);
	aimbs_putraw(&fr->data, passwd, passwdlen);
	aimbs_putle8(&fr->data, '\0');getall	struct aim_icq_info *info4b2/* Keep track of this request and the ICQ number and request ID */
	info = (struct aim_icq_info *)calloc(1, sizeof(struct aim_icq_info));
	info->reqid = snacid;
	info->uin = atoi(uin);
	info->next = sess->icq_info;
	sess->icq_info = info;

	return 0;
}

faim_export int aim_icq_getalias	struct aim_icq_info *info4ba/* Keep track of this request and the ICQ number and request ID */
	info = (struct aim_icq_info *)calloc(1, sizeof(struct aim_icq_info));
	info->reqid = snacid;
	info->uin = atoi(uin);
	info->next = sess->icq_info;
	sess->icq_info = infostatic void aim_icq_freeinfo(struct aim_icq_info *info) {
	int i;

	if (!info)
		return;
	free(info->nick);
	free(info->first);
	free(info->last);
	free(info->email);
	free(info->homecity);
	free(info->homestate);
	free(info->homephone);
	free(info->homefax);
	free(info->homeaddr);
	free(info->mobile);
	free(info->homezip);
	free(info->personalwebpage);
	if (info->email2)
		for (i = 0; i < info->numaddresses; i++)
			free(info->email2[i]);
	free(info->email2);
	free(info->workcity);
	free(info->workstate);
	free(info->workphone);
	free(info->workfax);
	free(info->workaddr);
	free(info->workzip);
	free(info->workcompany);
	free(info->workdivision);
	free(info->workposition);
	free(info->workwebpage);
	free(info->info);
	free(info);
}

/**
 * Subtype 0x0003 - Response to 0x0015/0x002, contains an ICQesque /* offline message */8(&qbs);
		msg.flags = aimbs_getle8(&qbs);
		msg.msglen = aimbs_getle16(&qbs);
		msg.msg = aimbs_getstr(&qbs, msg.
	} else if (cmd == 0x07da) { /* information */
		fu16_t subtype;
		struct aim_icq_info *info;
		aim_rxcallback_t userfunc;

		subtype = aimbs_getle16(&qbs);
		aim_bstream_advance(&qbs, 1); /* 0x0a */

		/* find other data from the same request */
		for (info = sess->icq_info; info && (info->reqid != reqid); info = info->next);
		if (!info) {
			info = (struct aim_icq_info *)calloc(1, sizeof(struct aim_icq_info));
			info->reqid = reqid;
			info->next = sess->icq_info;
			sess->icq_info = info;
		}

		switch (subtype) {
		case 0x00a0: { /* hide ip status */
			/* nothing */
		} break;

		case 0x00aa: { /* password change status */
			/* nothing */
		} break;

		case 0x00c8: { /* general and "home" information */
			info->nick = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
			info->first = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
			info->last = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
			info->email = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
			info->homecity = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
			info->homestate = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
			info->homephone = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
			info->homefax = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
			info->homeaddr = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
			info->mobile = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
			info->homezip = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
			info->homecountry = aimbs_getle16(&qbs);
			/* 0x0a 00 02 00 */
			/* 1 byte timezone? */
			/* 1 byte hide email flag? */
		} break;

		case 0x00dc: { /* personal information */
			info->age = aimbs_getle8(&qbs);
			info->unknown = aimbs_getle8(&qbs);
			info->gender = aimbs_getle8(&qbs);
			info->personalwebpage = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
			info->birthyear = aimbs_getle16(&qbs);
			info->birthmonth = aimbs_getle8(&qbs);
			info->birthday = aimbs_getle8(&qbs);
			info->language1 = aimbs_getle8(&qbs);
			info->language2 = aimbs_getle8(&qbs);
			info->language3 = aimbs_getle8(&qbs);
			/* 0x00 00 01 00 00 01 00 00 00 00 00 */
		} break;

		case 0x00d2: { /* work information */
			info->workcity = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
			info->workstate = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
			info->workphone = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
			info->workfax = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
			info->workaddr = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
			info->workzip = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
			info->workcountry = aimbs_getle16(&qbs);
			info->workcompany = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
			info->workdivision = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
			info->workposition = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
			aim_bstream_advance(&qbs, 2); /* 0x01 00 */
			info->workwebpage = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
		} break;

		case 0x00e6: { /* additional personal information */
			info->info = aimbs_getstr(&qbs, aimbs_getle16(&qbs)-1);
		} break;

		case 0x00eb: { /* email address(es) */
			int i;
			info->numaddresses = aimbs_getle16(&qbs);
			info->email2 = (char **)calloc(info->numaddresses, sizeof(char *));
			for (i = 0; i < info->numaddresses; i++) {
				info->email2[i] = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
				if (i+1 != info->numaddresses)
					aim_bstream_advance(&qbs, 1); /* 0x00 */
			}
		} break;

		case 0x00f0: { /* personal interests */
		} break;

		case 0x00fa: { /* past background and current organizations */
		} break;

		case 0x0104: { /* alias info */
			info->nick = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
			info->first = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
			info->last = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
			aim_bstream_advance(&qbs, aimbs_getle16(&qbs)); /* email address? */
			/* Then 0x00 02 00 */
		} break;

		case 0x010e: { /* unknown */
			/* 0x00 00 */
		} break;

		case 0x019a: { /* simple info */
			aim_bstream_advance(&qbs, 2);
			info->uin = aimbs_getle32(&qbs);
			info->nick = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
			info->first = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
			info->last = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
			info->email = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
			/* Then 0x00 02 00 00 00 00 00 */
		} break;
		} /* End switch statement */

		if (!(snac->flags & 0x0001)) {
			if (subtype != 0x0104)
		INFO)))
					ret = userfunc(sess, rx, info);

			if (info->uin && info->nick)
		ALIAS)))
					ret = userfunc(sess, rx, info);

			if (sess->icq_info == info) {
				sess->icq_info = info->next;
			} else {
				struct aim_icq_info *cur;
				for (cur=sess->icq_info; (cur->next && (cur->next!=info)); cur=cur->next);
				if (cur->next)
					cur->next = cur->next->next;
			}
			aim_icq_freeinfo(info);
		}static void icq_shutdown(aim_session_t *sess, aim_module_t *mod)
{
	struct aim_icq_info *del;

	while (sess->icq_info) {
		del = sess->icq_info;
		sess->icq_info = sess->icq_info->next;
		aim_icq_freeinfo(del);
	}

	returnc	mod->shutdown = icq_shutdown;

	return 0;
}
ENDREP
DELTA 130 95295 19545
SVN  ‚üPƒùk…X¶G€Žo ‚t†O€ƒ Œ1• †YR« s‚Üt© †Y‚Þ3€] <‚Û8€W S‚ €N ‚o‚æA— ’—€‚i ‡«r i²u X³] iµ6€‚ ‚bºgµ ‚&½F€ƒ qÆ6€s OÇ €X ‚FÈJŸ Ë½ &Ì €p Î0€/ FÐ~€ƒ+ Óx» X«r€| ‚)×¹ ðª fñ< …AÝ¬ …âX€ uè3† ‚$é|” gìA€v dðS— mÝ7€n ‡ô;€‚ uýƒ Ø( €! #0€] _„@ˆ ƒ(†&€, Q‚Ô|€“q j‚Ùg€r z‚Þj TŒ/¥ Ø(€ƒx z‚Þj TŒ/¥ Ø(€ˆp Ø(€‘! áU€ˆ, XŒ.£ Ø(€…k ¢|™q “¼l ˆ2Ï~ „$Øa Žeà †îz€^ ”ôc ‚ˆ{ 
‚–€1 *‚ €ˆk ƒx‚—¢ ‚X‚›† …'‚€ƒ ‚¦€# P‚¨^€' 	‚«o€+ ƒa‚¬m€ƒ` ‡‚³%€y R‚ì; ‚‚»	™ O‚ €„9 R‚ì;€m †=‚½¤ ‚ÄZ€ ‚c‚Åv€l O‚ÉjŒ …K‚ËH€…U RŒ/€k QÚ>½ ‚)‚é/Œ i‚ëg€G ‚‚Ò& †1‚Ô$€" S‚  ‚‚îK€s U‚ì9€‰_ T‚ì:€j T‚ì:€‚\ ‚ñr€ R‚ì;€‚l w‚Þj  UŒ/€ƒ5 qÚ&€‚a S‚ €x g‚ð4µ 6‚õ6‡ ‚I‚ölˆ h‚ù8· Q‚úƒ w‚ûY/*
 * Family 0x0004 - Routines for sending/receiving Instant Messages.
 *
 * Note the term ICBM (Inter-Client Basic Message) which blankets
 * all types of genericly routed through-server messages.  Within
 * the ICBM types (family 4), a channel is defined.  Each channel
 * represents a different type of message.  Channel 1 is used for
 * what would commonly be called an "instant message".  Channel 2
 * is used for negotiating "rendezvous".  These transactions end in
 * something more complex happening, such as a chat invitation, or
 * a file transfer.  Channel 3 is used for chat messages (not in 
 * the same family as these channels).  Channel 4 is used for 
 * various ICQ messages.  Examples are normal messages, URLs, and 
 * old-style authorization.
 *
 * In addition to the channel, every ICBM contains a cookie.  For
 * standard IMs, these are only used for error messages.  However,
 * the more complex rendezvous messages make suitably more complex
 * use of this field.
 *
 * TODO: Split this up into an im.c file an an icbm.c file.  It 
 *       will be beautiful, you'll see.
 *
 *       Need to rename all the mpmsg messages to aim_im_bleh.
 *
 *       Make sure aim_conn_findbygroup is used by all functions.
 */

#define FAIM_INTERNAL
#include <aim.h>

#ifdef _WIN32
#include "win32dep.h"
#endif

/**
 * Add a standard ICBM header to the given bstream with the given 
 * information.
 *
 * @param bs The bstream to write the ICBM header to.
 * @param c c is for cookie, and cookie is for me.
 * @param ch The ICBM channel (1 through 4).
 * @param sn Null-terminated scrizeen nizame.
 * @return The number of bytes written.  It's really not useful.
 */
static int aim_im_puticbm(aim_bstream_t *bs, const fu8_t *c, fu16_t ch, const char *sn)
{
	aimbs_putraw(bs, c, 8);
	aimbs_put16(bs, ch);
	aimbs_put8(bs, strlen(sn));
	aimbs_putraw(bs, sn, strlen(sn));
	return 8+2+1+strlen(sn);
}		AOL Mobile Communicator, WinAIM 1.0.414
 *  0501 0003 0101 0201 01		WinAIM 2.0.847, 2.1.1187, 3.0.1464, 
 *					4.3.2229, 4.4.2286
 *  0501 0004 0101 0102 0101		WinAIM 4.1.2010, libfaim (right here)
 *  0501 0003 0101 02			WinAIM 5
 *  0501 0001 01			iChat x.x, mobile buddies
 *  0501 0001 0101 01			AOL v6.0, CompuServe 2000 v6.0, any TOC client
 *  0501 0002 0106			WinICQ 5.45.1.3777.85im_fingerprint(const * 
 * Subtype 0x0002 - Set ICBM parameters.
 * 
 */
faim_export int aim_im_setparams/**
 * Subtype 0x0004 - Request ICBM parameter information.
 *
 */
faim_export int aim_im_req*
 * Subtype 0x0005 - Receive parameter information.
 *
 */
static int aim_im_paraminfobstream_t *bs)
{
	aim_rxcallback_t userfunc;
	struct aim_icbmparameters params/**
 * Subtype 0x0006 -- more precise verification that we never send SNACs larger than 8192
 * XXX - check SNAC size for multipart
 *
 */
faim_export int aim_im_sendch1_ext(aim_session_t *sess, struct aim_sendimext_args *args)
{
	aim_conn_t *conn;
	aim_frame_t *fr;
	aim_snacid_t snacid;
	fu8_t ck[8];
	int i, msgtlvlen;
	static const fu8_t deffeatures[] = { 0x01, 0x01, 0x01, 0x02 }-ck[i] = (fu8_t)rand();

	/* ICBM header */
	aim_im_puticbm(&fr->data, ck, 0x0001, args->destsn);

	/* Message TLV (type 0x0002) */
	aimbs_put16(&fr->data, 0x0002);
	aimbs_put16(&fr->data, msgtlvlen);

	/* Features TLV (type 0x0501) */
	aimbs_put16(&fr->data, 0x0501);/* Insert each message part in a TLV (type 0x0101) *//* Insert message text in a TLV (type 0x0101) */
		aimbs_put16(&fr->data, 0x0101);

		/* Message block length */
		aimbs_put16(&fr->data, args->msglen + 0x04);

		/* Character set */
		aimbs_put16(&fr->data, args->charset);
		aimbs_put16(&fr->data, args->charsubset);

		/* Message.  Not terminated */
		aimbs_putraw(&fr->data, args->msg, args->msglen);
	}

	/* Set the Autoresponse flag else if (args->flags & AIM_IMFLAGS_ACK) {
		/* Set the Request Acknowledge flag */
		aimbs_put16(&fr->data, 0x0003 XXX - This should really only be sent on initial 
	 * IMs and when you change your icon XXX - Everytime?  Surely not../* Move this to receive aim_flap_nop and send aim_flap_nop */im_sendch1_ext() 
 *
 * You cannot use aim_send_im if you need the HASICON flag.  You must
 * use aim_im_sendch1im_sendch1_ext().
 *
 */
faim_export int aim_im_sendch1(aim_session_t *sess, const char *sn, fu16_t flags, const char *msg)
{
	struct aim_sendimext_args args;

	args.destsn = 	args.charset = 0x0000;
	args.charsubset = 0x0000;

	/* Make these don't get set by accident -- they need aim_im_sendch1_ext */
	args.flags &= ~(AIM_IMFLAGS_CUSTOMFEATURES | AIM_IMFLAGS_HASICON | AIM_IMFLAGS_MULTIPART);

	return aim_im_sendch1_ext(sess, &args);
}

/**
 * Subtype 0x0006 - Send your icon to a given user.
 *
 * This is also performance sensitive. (If you can believe it...)
 *
 */
faim_export int aim_im_sendch2aim_frame_t *fr;
	aim_snacid_t snacid;
	fu8_t ck[8];
	int isn || !icon || (iconlen <= 0) || (iconlen >= MAXICONLEN))
		return -EINVAL;

	for (i = 0; i < 8; i++)
		ck[i] = (fu8_t)rand( ICBM header */
	aim_im_puticbm(&fr->data, ck, 0x0002, sn+6+4+4+iconlen+4+4+4+strlen(AIM_ICONIDENT)Subtype 0x0006 - Send a rich text message.
 im_sendch2_rtfmsg(aim_session_t *sess, struct aim_sendrtfmsg_args *args)
{
	aim_conn_t *conn;
	aim_frame_t *fr;
	aim_snacid_t snacid;
	fu8_t ck[8];int i,ck[i] = (fu8_t)rand( ICBM header */
	aim_im_puticbm(&fr->data, ck, 0x0002, args->destsn);

	/* TLV t(0005) - Encompasses everything below. t(000a) l(0002) v(0001t(000f) l(0000) v() */
	aimbs_put16(&fr->data, 0x000f);
	aimbs_put16(&fr->data, 0x0000);

	/* Service Data TLV/**
 * Subtype 0x0006 - Send an "I want to directly connect to you" message
 *
 */
faim_export int aim_im_sendch2_odcrequest(aim_session_t *sess, fu8_t *cookie, const char *sn, const fu8_t *ip, fu16_t port)
{
	aim_conn_t *conn;
	aim_frame_t *fr;
	aim_snacid_t snacid;
	fu8_t ck[8]sn)-ookie)
		memcpy(cookie, ck, 8);

	/* ICBM header */
	aim_im_puticbm(&fr->data, ck, 0x0002, snDIRECTIM/**
 * Subtype 0x0006 - Send an "I want to send you this file" message
 *
 */
faim_export int aim_im_sendch2_sendfile_ask(aim_session_t *sess, struct aim_oft_info *oft_info=NULL, *subtl=NULL;
	int i;

	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !oft_info)
		return -EINVAL;

	/* XXX - Should be like "21CBF95" and null terminated */
	for (i = 0; i < 7; i++)
		oft_info->cookie[i] = 0x30 + ((fu8_t)rand() % 10);
	oft_info->cookie[7] = '\0';

	{ /* Create the subTLV chain */
		fu8_t *buf;
		int buflen;
		aim_bstream_t bs;

		aim_addtlvtochain16(&subtl, 0x000a, 0x0001);
		aim_addtlvtochain_noval(&subtl, 0x000f);
/*		aim_addtlvtochain_raw(&subtl, 0x000e, 2, "en");
		aim_addtlvtochain_raw(&subtl, 0x000d, 8, "us-ascii");
		aim_addtlvtochain_raw(&subtl, 0x000c, 24, "Please accept this file."); */
		if (oft_info->clientip) {
			fu8_t ip[4];
			char *nexttoken;
			int i = 0;
			nexttoken = strtok(oft_info->clientip, ".");
			while (nexttoken && i<4) {
				ip[i] = atoi(nexttoken);
				nexttoken = strtok(NULL, ".");
				i++;
			}
			aim_addtlvtochain_raw(&subtl, 0x0003, 4, ip);
		}
		aim_addtlvtochain16(&subtl, 0x0005, oft_info->port);

		/* TLV t(2711) */
		buflen = 2+2+4+strlen(oft_info->fh.name)+1;
		buf = malloc(buflen);
		aim_bstream_init(&bs, buf, buflen);
		aimbs_put16(&bs, (oft_info->fh.totfiles > 1) ? 0x0002 : 0x0001);
		aimbs_put16(&bs, oft_info->fh.totfiles);
		aimbs_put32(&bs, oft_info->fh.totsize);

		/* Filename - NULL terminated, for some odd reason */
		aimbs_putraw(&bs, oft_info->fh.name, strlen(oft_info->fh.name));
		aimbs_put8(&bs, 0x00);

		aim_addtlvtochain_raw(&subtl, 0x2711, bs.len, bs.data);
		free(buf);
	}

	{ /* Create the main TLV chain */
		fu8_t *buf;
		int buflen;
		aim_bstream_t bs;

		/* TLV t(0005) - Encompasses everything from above. Gee. */
		buflen = 2+8+16+aim_sizetlvchain(&subtl);
		buf = malloc(buflen);
		aim_bstream_init(&bs, buf, buflen);
		aimbs_put16(&bs, AIM_RENDEZVOUS_PROPOSE);
		aimbs_putraw(&bs, oft_info->cookie, 8);
		aim_putcap(&bs, AIM_CAPS_SENDFILE);
		aim_writetlvchain(&bs, &subtl);
		aim_freetlvchain(&subtl);
		aim_addtlvtochain_raw(&tl, 0x0005, bs.len, bs.data);
		free(buf);

		/* TLV t(0003) - Request an ack */
		aim_addtlvtochain_noval(&tl, 0x0003);
	}

	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 11+strlen(oft_info->sn) + aim_sizetlvchain(&tl))))
		return -ENOMEM;

	snacid = aim_cachesnac(sess, 0x0004, 0x0006, AIM_SNACFLAGS_DESTRUCTOR, oft_info->cookie, sizeof(oft_info->cookie));
	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);

	/* ICBM header */
	aim_im_puticbm(&fr->data, oft_info->cookie, 0x0002, oft_info->sn);

	/* All that crap from above (the 0x0005 TLV and the 0x0003 TLV) */*
 * Subtype 0x0006 - Send an "I will accept this file" message?
 *
 * @param rendid Capability type (AIM_CAPS_GETFILE or AIM_CAPS_SENDFILE)
 */
faim_export int aim_im_sendch2_sendfile_accept(aim_session_t *sess, struct aim_oft_info *oft_info || !oft_info + 11+strlen(oft_info->sn) + 4+2+8+16 ICBM header */
	aim_im_puticbm(&fr->data, oft_info->cookie, 0x0002, oft_info->sn);

	aimbs_put16(&fr->data, 0x0005);
	aimbs_put16(&fr->data, 0x001a);
	aimbs_put16(&fr->data, AIM_RENDEZVOUS_ACCEPT);
	aimbs_putraw(&fr->data, oft_info->cookie, 8);
	aim_putcap(&fr->data, AIM_CAPS_SENDFILE);

	aim_tx_enqueue(sess, fr);

	return 0;
}

/**
 * Subtype 0x0006 - Send a "cancel this file transfer" message?
 *
 */
faim_export int aim_im_sendch2_sendfile_cancel(aim_session_t *sess, struct aim_oft_info *oft_info || !oft_info + 11+strlen(oft_info->sn) + 4+2+8+16 ICBM header */
	aim_im_puticbm(&fr->data, oft_info->cookie, 0x0002, oft_info->sn);

	aimbs_put16(&fr->data, 0x0005);
	aimbs_put16(&fr->data, 0x001a);
	aimbs_put16(&fr->data, AIM_RENDEZVOUS_CANCEL);
	aimbs_putraw(&fr->data, oft_info->cookie, 8);
	aim_putcap(&fr->data, AIM_CAPS_SENDFILE);

	aim_tx_enqueue(sess, fr);

	return 0;
}

/**
 * Subtype 0x0006 - Request the status message of the given ICQ user.
 *
 * @param sess The oscar session.
 * @param sn The UIN of the user of whom you wish to request info.
 * @param type The type of info you wish to request.  This should be the current 
 *        state of the user, as one of the AIM_ICQ_STATE_* defines.
 * @return Return 0 if no errors, otherwise return the error number.
 */
faim_export int aim_im_sendch2_geticqaway(aim_session_t *sess, const char *sn, int type)
{
	aim_conn_t *conn;
	aim_frame_t *fr;
	aim_snacid_t snacid;
	int i;
	fu8_t ck[8];

	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !sn)
		return -EINVAL;

	for (i = 0; i < 8; i++)
		ck[i] = (fu8_t)rand();

	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sn) + 4+0x5e + 4 ICBM header */
	aim_im_puticbm(&fr->data, ck, 0x0002, sn);

	/* TLV t(0005) - Encompasses almost everything below. */
	aimbs_put16(&fr->data, 0x0005); /* T */
	aimbs_put16(&fr->data, 0x005e); /* L */
	{ /* V */
		aimbs_put16(&fr->data, 0x0000);

		/* Cookie */
		aimbs_putraw(&fr->data, ck, 8);

		/* Put the 16 byte server relay capability */
		aim_putcap(&fr->data, AIM_CAPS_ICQSERVERRELAY);

		/* TLV t(000a) */
		aimbs_put16(&fr->data, 0x000a);
		aimbs_put16(&fr->data, 0x0002);
		aimbs_put16(&fr->data, 0x0001);

		/* TLV t(000f) */
		aimbs_put16(&fr->data, 0x000f);
		aimbs_put16(&fr->data, 0x0000);

		/* TLV t(2711) */
		aimbs_put16(&fr->data, 0x2711);
		aimbs_put16(&fr->data, 0x0036);
		{ /* V */
			aimbs_putle16(&fr->data, 0x001b); /* L */
			aimbs_putle16(&fr->data, 0x0008); /* XXX - Protocol version */
			aim_putcap(&fr->data, AIM_CAPS_EMPTY);
			aimbs_putle16(&fr->data, 0x0000); /* Unknown */
			aimbs_putle16(&fr->data, 0x0003); /* Client features? */
			aimbs_putle16(&fr->data, 0x0000); /* Unknown */
			aimbs_putle8(&fr->data, 0x00); /* Unkizown */
			aimbs_putle16(&fr->data, 0xffff); /* Sequence number?  XXX - This should decrement by 1 with each request */

			aimbs_putle16(&fr->data, 0x000e); /* L */
			aimbs_putle16(&fr->data, 0xffff); /* Sequence number?  XXX - This should decrement by 1 with each request */
			aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
			aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
			aimbs_putle32(&fr->data, 0x00000000); /* Unknown */

			/* The type of status message being requested */
			if (type & AIM_ICQ_STATE_CHAT)
				aimbs_putle16(&fr->data, 0x03ec);
			else if(type & AIM_ICQ_STATE_DND)
				aimbs_putle16(&fr->data, 0x03eb);
			else if(type & AIM_ICQ_STATE_OUT)
				aimbs_putle16(&fr->data, 0x03ea);
			else if(type & AIM_ICQ_STATE_BUSY)
				aimbs_putle16(&fr->data, 0x03e9);
			else if(type & AIM_ICQ_STATE_AWAY)
				aimbs_putle16(&fr->data, 0x03e8);

			aimbs_putle16(&fr->data, 0x0000); /* Status? */
			aimbs_putle16(&fr->data, 0x0001); /* Priority of this message? */
			aimbs_putle16(&fr->data, 0x0001); /* L */
			aimbs_putle8(&fr->data, 0x00); /* String of length L */
		} /* End TLV t(2711) */
	} /* End TLV t(0005) */*
 * Subtype 0x0006 - Send an ICQ-esque ICBM.
 *
 * This can be used to send an ICQ authorization reply (deny or grant).  It is the "old way."  
 * The new way is to use SSI.  I like the new way a lot better.  This seems like such a hack, 
 * mostly because it's in network byte order.  Figuring this stuff out sometimes takes a while, 
 * but thats ok, because it gives me time to try to figure out what kind of drugs the AOL people 
 * were taking when they merged the two protocols.
 *
 * @param sn The destination screen name.
 * @param type The type of message.  0x0007 for authorization denied.  0x0008 for authorization granted.
 * @param message The message you want to send, it should be null terminated.
 * @return Return 0 if no errors, otherwise return the error number.
 */
faim_export int aim_im_sendch4(aim_session_t *sess, char *sn, fu16_t type, fu8_t *message)
{
	aim_conn_t *conn;
	aim_frame_t *fr;
	aim_snacid_t snacid;
	int i;
	char ck[8];

	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0002)))
		return -EINVAL;

	if (!sn || !type || !messag3+strlen(sn)+12+strlen(message)+1+4 Cookie */
	for (i=0; i<8; i++)
		ck[i] = (fu8_t)rand();

	/* ICBM header */
	aim_im_puticbm(&fr->data, ck, 0x0004, sn);

	/*
	 * TLV t(0005)
	 *
	 * ICQ data (the UIN and the message).
	 */
	aimbs_put16(&fr->data, 0x0005);
	aimbs_put16(&fr->data, 4 + 2+2+strlen(message)+1);

	/*
	 * Your UIN
	 */
	aimbs_putle32(&fr->data, atoi(sess->sn));

	/*
	 * TLV t(type) l(strlen(message)+1) v(message+NULL)
	 */
	aimbs_putle16(&fr->data, type);
	aimbs_putle16(&fr->data, strlen(message)+1);
	aimbs_putraw(&fr->data, message, strlen(message)+1);

	/*
	 * TLV t(0006) l(0000) v()
	 */
	aimbs_put16(&fr->data, 0x0006);
	aimbs_put16(&fr->data, 0x0000);

	aim_tx_enqueue(sess, fr);

	return 0;
}

/*
 * XXX - I don't see when this would ever get called...
 */---0b) { /* Non-direct connect typing notification */

			args.icbmflags |= AIM_IMFLAGS_TYPINGNOT--static void incomingim_ch2_sendfile_free(aim_session_t *sess, struct aim_incomingim_ch2_args *args)
{
	free(args->info.sendfile.filename);
}

static void incomingim_ch2_sendfile
	args->destructor = (void *)incomingim_ch2_sendfile_free;

	/* Maybe there is a better way to tell what kind of sendfile 
	 * this is?  Maybe TLV t(000a)? */
	if (servdata) { /* Someone is sending us a file */
		int flen;

		/* subtype is one of AIM_OFT_SUBTYPE_* */
		args->info.sendfile.subtype = aimbs_get16(servdata);
		args->info.sendfile.totfiles = aimbs_get16(servdata);
		args->info.sendfile.totsize = aimbs_get32(servdata);

		/*
		 * I hope to God I'm right when I guess that there is a 
		 * 32 char max filename length for single files.  I think 
		 * OFT tends to do that.  Gotta love inconsistency.  I saw 
		 * a 26 byte filename?
		 */
		/* AAA - create an aimbs_getnullstr function (don't anymore)(maybe) */
		/* Use an inelegant way of getting the null-terminated filename, 
		 * since there's no easy bstream routine. */
		for (flen = 0; aimbs_get8(servdata); flen++);
		aim_bstream_advance(servdata, -flen -1);
		args->info.sendfile.filename = aimbs_getstr(servdata, flen);

		/* There is sometimes more after the null-terminated filename, 
		 * but I'm unsure of its format. */
		/* I don't believe him. */
	}proxyip[30] = {""};
	char clientipcancelto proxy the file transfer through.
	 *
	 * XXX - I don't like this.  Maybe just read in an int?  Or inet_ntoa...
	 */
	if (aim_gettlv(list2, 0x0002, 1)) {
		aim_tlv_t *iptlv;

		iptlv = aim_gettlv(list2, 0x0002, 1);
		if (iptlv->length == 4)
			snprintf(proxyip, sizeof(proxyip), "%hhd.%hhd.%hhd.%hhd",
				iptlv->value[0], iptlv->value[1],
				iptlv->value[2], iptlv->value[3]);
	}

	/*
	 *		if (iptlv->length == 4)
			snprintf(clientip, sizeof(clientip), "%hhd.%hhd.%hhd.%hhd",
				iptlv->value[0], iptlv->value[1],
				iptlv->value[2], iptlv->value[3]		if (iptlv->length == 4)
			snprintf(verifiedip, sizeof(verifiedip), "%hhd.%hhd.%hhd.%hhd",
				iptlv->value[0], iptlv->value[1],
				iptlv->value[2], iptlv->value[3]Something to do with ft -- two bytes
	 * 0x0001 - "I want to send you this file"
	 * 0x0002 - "I will accept this file from you"
	 */
	if (aim_gettlv(list2, 0x000a, 1))
		
	 * Unknown -- no value
	 *
	 * Maybe means we should connect directly to transfer the file?
	 */
	if (aim_gettlv(list2, 0x000f, 1))
		;

	/*
	 * Unknown -- no value
	 *
	 * Maybe means we should proxy the file transfer through an AIM server?
	 */
	if (aim_gettlv(list2, 0x0010, 1))
		;

	if (strlen(proxyip))
		args.proxyip = (char *)proxyip;
	if (strlen(clientip))
		args.clientip = (char *)clientip;
	if (strlen(verifiedip))
		args.verifiedip = (char *)verifiedip;

	/*
	 * Th	else if (args.reqclass & AIM_CAPS_SENDFILE)
		incomingim_ch2_sendfile(sess, mod, rx, snac, userinfo, &args, sdbsptr);


static int incomingim_ch4fu16_t channel, aim_userinfo_t *userinfo, aim_tlvlist_t *tlvlist, fu8_t *cookie)
{
	aim_bstream_t meat;
	aim_rxcallback_t userfunc;
	aim_tlv_t *block;
	struct aim_incomingim_ch4_args args;
	int ret = 0;

	/*
	 * Make a bstream for the meaty part.  Yum.  Meat.
	 */
	if (!(block = aim_gettlv(tlvlist, 0x0005, 1)))
		return -1;
	aim_bstream_init(&meat, block->value, block->length);

	args.uin = aimbs_getle32(&meat);
	args.type = aimbs_getle8(&meat);
	args.flags = aimbs_getle8(&meat);
	args.msglen = aimbs_getle16(&meat);
	args.msg = aimbs_getraw(&meat, args.msglen);

ret = userfunc(sess, rx, channel, userinfo, &args);

	free(args.msg);

	return ret;
}

/*
 * Subtype 0x0007
 It is 
	 * used to send basic ICBMs.
	 * Channel 0x0003 is used for chat messages.
	 *
	 * Channel 0x0004 is used for ICQ authorization, or 
	 * possibly any system notice.
	 *You can start reading the message TLVs after aim_info_extract() 
	 * parses out the standard userinfo block.info_extractif (channel == 4) {
		aim_tlvlist_t *tlvlist;

		tlvlist = aim_readtlvchain(bs);
		ret = incomingim_ch4(sess, mod, rx, snac, channel, &userinfo, tlvlist, cookie);
		aim_freetlvchain(&tlvlist);

	} else {
		faimdprintf(sess, 0, "icbm: ICBM received on an unsupported channel.  Ignoring.  (chan = %04x)\n", channel);
	}

	aim_info_free(&userinfo);

	return ret;
}

/*
 * Subtype 0x0008 - Send a warning to sn.
 * 
 * Flags:
 *  AIM_WARN_ANON  Send as an anonymous (doesn't count as much)
 *
 * returns -1 on error (couldn't alloc packet), 0 on success. 
 *
 */
faim_export int aim_im_warn(aim_session_t *sess, aim_conn_t *conn, const char *sn, fu32_t flags)
{
	aim_frame_t *fr;
	aim_snacid_t snacid;

	if (!sess || !conn || !snstrlen(sn)+13)))
		return -ENOMEM;

	snacid = aim_cachesnac(sess, 0x0004, 0x0008, 0x0000, sn, strlen(sn)+1);
	aim_putsnac(&fr->data, 0x0004, 0x0008, 0x0000, snacid);

	aimbs_put16(&fr->data, (flags & AIM_WARN_ANON) ? 0x0001 : 0x0000); aim_tx_enqueue(sess, fr);

	return 0;
}

/* Subtype 0x000a */info_extract
		aim_info_free(&userinfo);
	}

	return ret;
}

/*
 * Subtype 0x000b
 Subtype 0x000b - Receive the response from an ICQ status message request.
 *
 * This contains the ICQ status message.  Go figure.
 *
 */
static int clientautorespchannel == 0x0002) { /* File transfer declined */
		aimbs_get16(bs); /* Unknown */
		aimbs_get16(bs); /* Unknown */ret = userfunc(sess, rx, channel, sn, reason, ck);
	} else if (channel == 0x0004) { /* ICQ message */
		switch (reason) {
			case 0x0003: { /* ICQ status message.  Maybe other stuff too, you never know with these people. */
				fu8_t statusmsgtype, *msg;
				fu16_t len;
				fu32_t state;

				len = aimbs_getle16(bs); /* Should be 0x001b */
				aim_bstream_advance(bs, len); /* Unknown */

				len = aimbs_getle16(bs); /* Should be 0x000e */
				aim_bstream_advance(bs, len); /* Unknown */

				statusmsgtype = aimbs_getle8(bs);
				switch (statusmsgtype) {
					case 0xe8:
						state = AIM_ICQ_STATE_AWAY;
						break;
					case 0xe9:
						state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY;
						break;
					case 0xea:
						state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_OUT;
						break;
					case 0xeb:
						state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY;
						break;
					case 0xec:
						state = AIM_ICQ_STATE_CHAT;
						break;
					default:
						state = 0;
						break;
				}

				aimbs_getle8(bs); /* Unknown - 0x03 Maybe this means this is an auto-reply */
				aimbs_getle16(bs); /* Unknown - 0x0000 */
				aimbs_getle16(bs); /* Unknown - 0x0000 */

				len = aimbs_getle16(bs);
				msg = aimbs_getraw(bs, len);

				ret = userfunc(sess, rx, channel, sn, reason, state, msg);

				free(msg);
			} break;

			default: {
				ret = userfunc(sess, rx, channel, sn, reason);
			} break;
		} /* end switch */
	}

	free(ck);
	free(sn);

	return ret;
}

/*
 * Subtype 0x000c - Receive an ack after sending an ICBM.
 *
 * You have to have send the message with the AIM_IMFLAGS_ACK flag set 
 * (TLV t(0003)).  The ack contains the ICBM header of the message you 
 * sent.
 *
 */ch;
	fu8_t *ck;
	char *sn;
	int ret = 0;

	ck = aimbs_getraw(bs, 8);
	ch = aimbs_get16(bs);
	sn = aimbs_getstr(bs, aimbs_get8(bs));

ret = userfunc(sess, rx, ch, sn);

	free(sn);
	free(ck);

	return ret;
}

/*
 * Subtype 0x0014 - Send a mini typing notification (mtn) packet.
 *
 * This is supported by winaim5 and newer, MacAIM bleh and newer, iChat bleh and newer, 
 * and Gaim 0.60 and newer.
 *
 */
faim_export int aim_im_sendmtn(aim_session_t *sess, fu16_t type1, const char *sn, fu16_t type22)))
		return -EINVAL;

	if (!sn11+strlen(sn)+2)))
		return -ENOMEM;

	snacid = aim_cachesnac(sess, 0x0004, 0x0014, 0x0000, NULL, 0);
	aim_putsnac(&fr->data, 0x0004, 0x0014, 0x0000, snacid);

	/*
	 * 8 days of light
	 * Er, that is to say, 8 bytes of 0's
	 */
	aimbs_put16(&fr->data, 0x0000);
	aimbs_put16(&fr->data, 0x0000);
	aimbs_put16(&fr->data, 0x0000);
	aimbs_put16(&fr->data, 0x0000);

	/*
	 * Type 1 (should be 0x0001 for mtn)
	 */
	aimbs_put16(&fr->data, type1ype 2 (should be 0x0000, 0x0001, or 0x0002 for mtn)
	 */
	aimbs_put16(&fr->data, type2);

	aim_tx_enqueue(sess, fr);

	return 0;
}

/*
 * Subtype 0x0014 - Receive a mini typing notification (mtn) packet.
 *
 * This is supported by winaim5 and newer, MacAIM bleh and newer, iChat bleh and newer, 
 * and Gaim 0.60 and newer.
 *
 */
static int mtn_receivebstream_t *bs)
{
	int ret = 0;
	aim_rxcallback_t userfunc;
	char *sn;
	fu8_t snlen;
	fu16_t type1, type2;

	aim_bstream_advance(bs, 8); /* Unknown - All 0's */
	type1 = aimbs_get16(bs);
	snlen = aimbs_get8(bs);
	sn = aimbs_getstr(bs, snlen);
	type2ret = userfunc(sess, rx, type1, sn, type2);

	free(snaim_im_autoresp	else if (snac->subtype == 0x0014)
		return mtn_receive629ENDREP
DELTA 130 126095 2467
SVN  ¦Nƒ¤GYƒ…3€ò) N‚r€T #„€„U l 3€„Y X†i€B ‰O€‚[ 2Œ0€‡| R 6€ ‚.—7€ Ÿ&€„ R 6€^ Ÿ&€ˆP R 6€z Ÿ'€ƒU R 6€B Ÿ'€š+ k 4€ƒ{ ‚ž6›   3– š9€Œ` Ÿ'€‚# l 3€‰D Ÿ'€‚# l 3€‹< Ÿ'€‚] l 3€+ Ÿ%€} l 3 %¡€ˆf ^¤,€i/*
 * Family 0x0013 - Server-Side/Stored Information.
 *
 * Relatively new facility that allows certain types of information, such as 
 * a user's buddy list, permit/deny list, and permit/deny preferences, to be 
 * stored on the server, so that they can be accessed from any client.
 *
 * We keep 2 copies of SSI data:
 * 1) An exact copy of what is stored on the AIM servers.
 * 2) A local copy that we make changes to, and then send diffs 
 *    between this and the exact copy to keep them in sync.
 *
 * All the "aim_ssi_itemlist_bleh" functions near the top just modify the list 
 * that is given to them (i.e. they don't send SNACs).
 *
 * The SNAC sending and receiving functions are lower down in the file, and 
 * they're simpler.  They are in the order of the subtypes they deal with, 
 * starting with the request rights function (subtype 0x0002), then parse 
 * rights (subtype 0x0003), then--well, you get the idea.
 *
 * This is entirely too complicated.
 * You don't know the half of it.
 *
 * XXX - Preserve unknown data in TLV lists
 *
 */

#define FAIM_INTERNAL
#include <aim.h>

/**
 * Locally rebuild the 0x00c8 TLV in the additional data of the given group.
 *
 * @param list A pointer to a pointer to the current list of items.
 * @param name A null terminated string containing the group name, or NULL 
 *        if you want to modify the master group.
 * @return Return a pointer to the modified item.
 */
static struct aim_ssi_item *aim_ssi_itemlist_rebuildgroup(struct aim_ssi_item *list, const char *name)
{
	int newlen;
	struct aim_ssi_item *cur, *group;

	if (!list)
		return NULL;

	/* Find the group */
	if (!(group = aim_ssi_itemlist_finditem(list, name, NULL, AIM_SSI_TYPE_GROUP)))
		return NULL;

	/* Free the old data */
	aim_freetlvchain(&group->data);
	group->data = NULL;

	/* Find the length for the new additional data */
	newlen = 0;
	if (group->gid == 0x0000) {
		for (cur=list; cur; cur=cur->next)
			if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid != 0x0000))
				newlen += 2;
	} else {
		for (cur=list; cur; cur=cur->next)
			if ((cur->gid == group->gid) && (cur->type == AIM_SSI_TYPE_BUDDY))
				newlen += 2;
	}

	/* Build the new TLV list */
	if (newlen > 0) {
		fu8_t *newdata;

		if (!(newdata = (fu8_t *)malloc((newlen)*sizeof(fu8_t))))
			return NULL;
		newlen = 0;
		if (group->gid == 0x0000) {
			for (cur=list; cur; cur=cur->next)
				if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid != 0x0000))
						newlen += aimutil_put16(newdata+newlen, cur->gid);
		} else {
			for (cur=list; cur; cur=cur->next)
				if ((cur->gid == group->gid) && (cur->type == AIM_SSI_TYPE_BUDDY))
						newlen += aimutil_put16(newdata+newlen, cur->bid);
		}
		aim_addtlvtochain_raw(&group->data, 0x00c8, newlen, newdata);

		free(newdata);
	}

	return group;
}

/**
 * Locally add a new item to the given item list.
 *
 * @param list A pointer to a pointer to the current list of items.
 * @param name A null terminated string of the name of the new item, or NULL if the 
 *        item should have no name.
 * @param gid The group ID# you want the new item to have, or 0xFFFF if we should pick something.
 * @param bid The buddy ID# you want the new item to have, or 0xFFFF if we should pick something.
 * @param type The type of the item, 0x0000 for a contact, 0x0001 for a group, etc.
 * @param data The additional data for the new item.
 * @return A pointer to the newly created item.
 */
static struct aim_ssi_item *aim_ssi_itemlist_add(struct aim_ssi_item **list, const char *name, fu16_t gid, fu16_t bid, fu16_t type, aim_tlvlist_t *data)
{
	int i;
	struct aim_ssi_item *cur, *new;

	if (!list)
		return NULL;

	if (!(new = (struct aim_ssi_item *)malloc(sizeof(struct aim_ssi_item))))
		return NULL;

	/* Set the name */
	if (name) {
		new->name = (char *)malloc((strlen(name)+1)*sizeof(char));
		strcpy(new->name, name);
	} else
		new->name = NULL;

	/* Set the group ID# and buddy ID# */
	new->gid = gid;
	new->bid = bid;
	if (type == AIM_SSI_TYPE_GROUP) {
		if ((new->gid == 0xFFFF) && name) {
			do {
				new->gid += 0x0001;
				for (cur=*list, i=0; ((cur) && (!i)); cur=cur->next)
					if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid == new->gid))
						i=1;
			} while (i);
		}
	} else {
		if (new->bid == 0xFFFF) {
			do {
				new->bid += 0x0001;
				for (cur=*list, i=0; ((cur) && (!i)); cur=cur->next)
					if ((cur->bid == new->bid) && (cur->gid == new->gid))
						i=1;
			} while (i);
		}
	}

	/* Set the type */
	new->type = type;

	/* Set the TLV list */
	new->data = aim_tlvlist_copy(data);

	/* Add the item to the list in the correct numerical position.  Fancy, eh? */
	if (*list) {
		if ((new->gid < (*list)->gid) || ((new->gid == (*list)->gid) && (new->bid < (*list)->bid))) {
			new->next = *list;
			*list = new;
		} else {
			struct aim_ssi_item *prev;
			for ((prev=*list, cur=(*list)->next); (cur && ((new->gid > cur->gid) || ((new->gid == cur->gid) && (new->bid > cur->bid)))); prev=cur, cur=cur->next);
			new->next = prev->next;
			prev->next = new;
		}
	} else {
		new->next = *list;
		*list = new;
	}

	return new;
}

/**
 * Locally delete an item from the given item list.
 *
 * @param list A pointer to a pointer to the current list of items.
 * @param del A pointer to the item you want to remove from the list.
 * @return Return 0 if no errors, otherwise return the error number.
 */
static int aim_ssi_itemlist_del(struct aim_ssi_item **list, struct aim_ssi_item *del)
{
	if (!list || !(*list) || !del)
		return -EINVAL;

	/* Remove the item from the list */
	if (*list == del) {
		*list = (*list)->next;
	} else {
		struct aim_ssi_item *cur;
		for (cur=*list; (cur->next && (cur->next!=del)); cur=cur->next);
		if (cur->next)
			cur->next = cur->next->next;
	}

	/* Free the deleted item */
	free(del->name);
	aim_freetlvchain(&del->data);
	free(del);

	return 0;
}

/**
 * Compare two items to see if they have the same data.
 *
 * @param cur1 A pointer to a pointer to the first item.
 * @param cur2 A pointer to a pointer to the second item.
 * @return Return 0 if no differences, or a number if there are differences.
 */
static int aim_ssi_itemlist_cmp(struct aim_ssi_item *cur1, struct aim_ssi_item *cur2)
{
	if (!cur1 || !cur2)
		return 1;

	if (cur1->data && !cur2->data)
		return 2;

	if (!cur1->data && cur2->data)
		return 3;

	if ((cur1->data && cur2->data) && (aim_tlvlist_cmp(cur1->data, cur2->data)))
			return 4;

	if (cur1->name && !cur2->name)
		return 5;

	if (!cur1->name && cur2->name)
		return 6;

	if (cur1->name && cur2->name && aim_sncmp(cur1->name, cur2->name))
		return 7;

	if (cur1->gid != cur2->gid)
		return 8;

	if (cur1->bid != cur2->bid)
		return 9;

	if (cur1->type != cur2->type)
		return 10;

	return 0;
}

faim_export int aim_ssi_itemlist_valid(struct aim_ssi_item *list, struct aim_ssi_item *item)
{
	struct aim_ssi_item *cur;
	for (cur=list; cur; cur=cur->next)
		if (cur == item)
			return 1;
	return 0;
}

/**
 * Locally find an item given a group ID# and a buddy ID#.
 *
 * @param list A pointer to the current list of items.
 * @param gid The group ID# of the desired item.
 * @param bid The buddy ID# of the desired item.
 * @return Return a pointer to the item if found, else return NULL;
 */
faim_export struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_item *list, fu16_t gid, fu16_t bid)
{
	struct aim_ssi_item *cur;
	for (cur=list; cur; cur=cur->next)
		if ((cur->gid == gid) && (cur->bid == bid))
			return cur;
	return NULL;
}

/**
 * Locally find an item given a group name, screen name, and type.  If group name 
 * and screen name are null, then just return the first item of the given type.
 *
 * @param list A pointer to the current list of items.
 * @param gn The group name of the desired item.
 * @param bn The buddy name of the desired item.
 * @param type The type of the desired item.
 * @return Return a pointer to the item if found, else return NULL;
 */
faim_export struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_item *list, const char *gn, const char *sn, fu16_t type)
{
	struct aim_ssi_item *cur;
	if (!list)
		return NULL;

	if (gn && sn) { /* For finding buddies in groups */
		for (cur=list; cur; cur=cur->next)
			if ((cur->type == type) && (cur->name) && !(aim_sncmp(cur->name, sn))) {
				struct aim_ssi_item *curg;
				for (curg=list; curg; curg=curg->next)
					if ((curg->type == AIM_SSI_TYPE_GROUP) && (curg->gid == cur->gid) && (curg->name) && !(aim_sncmp(curg->name, gn)))
						return cur;
			}

	} else if (gn) { /* For finding groups */
		for (cur=list; cur; cur=cur->next) {
			if ((cur->type == type) && (cur->bid == 0x0000) && (cur->name) && !(aim_sncmp(cur->name, gn))) {
				return cur;
			}
		}

	} else if (sn) { /* For finding permits, denies, and ignores */
		for (cur=list; cur; cur=cur->next) {
			if ((cur->type == type) && (cur->name) && !(aim_sncmp(cur->name, sn))) {
				return cur;
			}
		}

	/* For stuff without names--permit deny setting, visibility mask, etc. */
	} else for (cur=list; cur; cur=cur->next) {
		if ((cur->type == type) && (!cur->name))
			return cur;
	}

	return NULL;
}

/**
 * Check if the given buddy exists in any group in the buddy list.
 *
 * @param list A pointer to the current list of items.
 * @param sn The group name of the desired item.
 * @return Return a pointer to the name of the item if found, else return NULL;
 */
faim_export struct aim_ssi_item *aim_ssi_itemlist_exists(struct aim_ssi_item *list, const char *sn)
{
	struct aim_ssi_item *cur;
	if (!list || !sn)
		return NULL;
	for (cur=list; cur; cur=cur->next)
		if ((cur->type == AIM_SSI_TYPE_BUDDY) && (cur->name) && (!aim_sncmp(cur->name, sn)))
			return cur;
	return NULL;
}

/**
 * Locally find the parent item of the given buddy name.
 *
 * @param list A pointer to the current list of items.
 * @param bn The buddy name of the desired item.
 * @return Return a pointer to the name of the item if found, else return NULL;
 */
faim_export char *aim_ssi_itemlist_findparentname(struct aim_ssi_item *list, const char *sn)
{
	struct aim_ssi_item *cur, *curg;
	if (!list || !sn)
		return NULL;
	if (!(cur = aim_ssi_itemlist_exists(list, sn)))
		return NULL;
	if (!(curg = aim_ssi_itemlist_find(list, cur->gid, 0x0000)))
		return NULL;
	return curg->name;
}

/**
 * Locally find the permit/deny setting item, and return the setting.
 *
 * @param list A pointer to the current list of items.
 * @return Return the current SSI permit deny setting, or 0 if no setting was found.
 */
faim_export int aim_ssi_getpermdeny(struct aim_ssi_item *list)
{
	struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PDINFO);
	if (cur) {
		aim_tlvlist_t *tlvlist = cur->data;
		if (tlvlist) {
			aim_tlv_t *tlv = aim_gettlv(tlvlist, 0x00ca, 1);
			if (tlv && tlv->value)
				return aimutil_get8(tlv->value);
		}
	}
	return 0;
}

/**
 * Locally find the presence flag item, and return the setting.  The returned setting is a 
 * bitmask of the user flags that you are visible to.  See the AIM_FLAG_* #defines 
 * in aim.h
 *
 * @param list A pointer to the current list of items.
 * @return Return the current visibility mask.
 */
faim_export fu32_t aim_ssi_getpresence(struct aim_ssi_item *list)
{
	struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS);
	if (cur) {
		aim_tlvlist_t *tlvlist = cur->data;
		if (tlvlist) {
			aim_tlv_t *tlv = aim_gettlv(tlvlist, 0x00c9, 1);
			if (tlv && tlv->length)
				return aimutil_get32(tlv->value);
		}
	}
	return 0xFFFFFFFF;
}

/**
 * Locally find the alias of the given buddy.
 *
 * @param list A pointer to the current list of items.
 * @param gn The group of the buddy.
 * @param sn The name of the buddy.
 * @return A pointer to a NULL terminated string that is the buddies 
 *         alias, or NULL if the buddy has no alias.  You should free
 *         this returned value!
 */
faim_export char *aim_ssi_getalias(struct aim_ssi_item *list, const char *gn, const char *sn)
{
	struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, sn, AIM_SSI_TYPE_BUDDY);
	if (cur) {
		aim_tlvlist_t *tlvlist = cur->data;
		if (tlvlist) {
			aim_tlv_t *tlv = aim_gettlv(tlvlist, 0x0131, 1);
			if (tlv && tlv->length) {
				char *alias = (char *)malloc((tlv->length+1)*sizeof(char));
				strncpy(alias, tlv->value, tlv->length);
				alias[tlv->length] = 0;
				return alias;
			}
		}
	}
	return NULL;
}

/**
 * Locally find if you are waiting for authorization for a buddy.
 *
 * @param list A pointer to the current list of items.
 * @param gn The group of the buddy.
 * @param sn The name of the buddy.
 * @return A pointer to a NULL terminated string that is the buddies 
 *         alias, or NULL if the buddy has no alias.  You should free
 *         this returned value!
 */
faim_export int aim_ssi_waitingforauth(struct aim_ssi_item *list, const char *gn, const char *sn)
{
	struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, sn, AIM_SSI_TYPE_BUDDY);
	if (cur) {
		aim_tlvlist_t *tlvlist = cur->data;
		if (tlvlist)
			if (aim_gettlv(tlvlist, 0x0066, 1))
				return 1;
	}
	return 0;
}

/**
 * If there are changes, then create temporary items and 
 * call addmoddel.
 *
 * @param sess The oscar session.
 * @return Return 0 if no errors, otherwise return the error number.
 */
static int aim_ssi_sync(aim_session_t *sess)
{
	struct aim_ssi_item *cur1, *cur2;
	struct aim_ssi_tmp *cur, *new;

	if (!sess)
		return -EINVAL;

	/* If we're waiting for an ack, we shouldn't do anything else */
	if (sess->ssi.waiting_for_ack)
		return 0;

	/*
	 * Compare the 2 lists and create an aim_ssi_tmp for each difference.  
	 * We should only send either additions, modifications, or deletions 
	 * before waiting for an acknowledgement.  So first do deletions, then 
	 * additions, then modifications.  Also, both the official and the local 
	 * list should be in ascending numerical order for the group ID#s and the 
	 * buddy ID#s, which makes things more efficient.  I think.
	 */

	/* Additions */
	if (!sess->ssi.pending) {
		for (cur1=sess->ssi.local; cur1; cur1=cur1->next) {
			if (!aim_ssi_itemlist_find(sess->ssi.official, cur1->gid, cur1->bid)) {
				new = (struct aim_ssi_tmp *)malloc(sizeof(struct aim_ssi_tmp));
				new->action = AIM_CB_SSI_ADD;
				new->ack = 0xffff;
				new->name = NULL;
				new->item = cur1;
				new->next = NULL;
				if (sess->ssi.pending) {
					for (cur=sess->ssi.pending; cur->next; cur=cur->next);
					cur->next = new;
				} else
					sess->ssi.pending = new;
			}
		}
	}

	/* Deletions */
	if (!sess->ssi.pending) {
		for (cur1=sess->ssi.official; cur1; cur1=cur1->next) {
			if (!aim_ssi_itemlist_find(sess->ssi.local, cur1->gid, cur1->bid)) {
				new = (struct aim_ssi_tmp *)malloc(sizeof(struct aim_ssi_tmp));
				new->action = AIM_CB_SSI_DEL;
				new->ack = 0xffff;
				new->name = NULL;
				new->item = cur1;
				new->next = NULL;
				if (sess->ssi.pending) {
					for (cur=sess->ssi.pending; cur->next; cur=cur->next);
					cur->next = new;
				} else
					sess->ssi.pending = new;
			}
		}
	}

	/* Modifications */
	if (!sess->ssi.pending) {
		for (cur1=sess->ssi.local; cur1; cur1=cur1->next) {
			cur2 = aim_ssi_itemlist_find(sess->ssi.official, cur1->gid, cur1->bid);
			if (cur2 && (aim_ssi_itemlist_cmp(cur1, cur2))) {
				new = (struct aim_ssi_tmp *)malloc(sizeof(struct aim_ssi_tmp));
				new->action = AIM_CB_SSI_MOD;
				new->ack = 0xffff;
				new->name = NULL;
				new->item = cur1;
				new->next = NULL;
				if (sess->ssi.pending) {
					for (cur=sess->ssi.pending; cur->next; cur=cur->next);
					cur->next = new;
				} else
					sess->ssi.pending = new;
			}
		}
	}

	/* We're out of stuff to do, so tell the AIM servers we're done and exit */
	if (!sess->ssi.pending) {
		aim_ssi_modend(sess);
		return 0;
	}

	/* Make sure we don't send anything else between now 
	 * and when we receive the ack for the following operation */
	sess->ssi.waiting_for_ack = 1;

	/* Now go mail off our data and wait 4 to 6 weeks */
	aim_ssi_addmoddel(sess);

	return 0;
}

/**
 * Free all SSI data.
 *
 * This doesn't remove it from the server, that's different.
 *
 * @param sess The oscar session.
 * @return Return 0 if no errors, otherwise return the error number.
 */
static int aim_ssi_freelist(aim_session_t *sess)
{
	struct aim_ssi_item *cur, *del;
	struct aim_ssi_tmp *curtmp, *deltmp;

	cur = sess->ssi.official;
	while (cur) {
		del = cur;
		cur = cur->next;
		free(del->name);
		aim_freetlvchain(&del->data);
		free(del);
	}

	cur = sess->ssi.local;
	while (cur) {
		del = cur;
		cur = cur->next;
		free(del->name);
		aim_freetlvchain(&del->data);
		free(del);
	}

	curtmp = sess->ssi.pending;
	while (curtmp) {
		deltmp = curtmp;
		curtmp = curtmp->next;
		free(deltmp);
	}

	sess->ssi.numitems = 0;
	sess->ssi.official = NULL;
	sess->ssi.local = NULL;
	sess->ssi.pending = NULL;
	sess->ssi.timestamp = (time_t)0;

	return 0;
}

/**
 * Delete all SSI data.
 *
 * @param sess The oscar session.
 * @return Return 0 if no errors, otherwise return the error number.
 */
faim_export int aim_ssi_deletelist(aim_session_t *sess)
{
	struct aim_ssi_item *cur, *del;

	if (!sess)
		return -EINVAL;

	/* Free the local list */
	cur = sess->ssi.local;
	while (cur) {
		del = cur;
		cur = cur->next;
		free(del->name);
		aim_freetlvchain(&del->data);
		free(del);
	}
	sess->ssi.local = NULL;

	/* Sync our local list with the server list */
	aim_ssi_sync(sess);

	return 0;
}

/**
 * This "cleans" the ssi list.  It does the following:
 * 1) Makes sure all buddies, permits, and denies have names.
 * 2) Makes sure that all buddies are in a group that exist.
 * 3) Deletes any empty groups
 *
 * @param sess The oscar session.
 * @return Return 0 if no errors, otherwise return the error number.
 */
faim_export int aim_ssi_cleanlist(aim_session_t *sess)
{
	struct aim_ssi_item *cur, *next;

	if (!sess)
		return -EINVAL;

	/* Delete any buddies, permits, or denies with empty names. */
	/* If there are any buddies directly in the master group, add them to a real group. */
	/* DESTROY any buddies that are directly in the master group. */
	/* Do the same for buddies that are in a non-existant group. */
	/* This will kind of mess up if you hit the item limit, but this function isn't too critical */
	cur = sess->ssi.local;
	while (cur) {
		next = cur->next;
		if (!cur->name) {
			if (cur->type == AIM_SSI_TYPE_BUDDY)
				aim_ssi_delbuddy(sess, NULL, NULL);
			else if (cur->type == AIM_SSI_TYPE_PERMIT)
				aim_ssi_delpermit(sess, NULL);
			else if (cur->type == AIM_SSI_TYPE_DENY)
				aim_ssi_deldeny(sess, NULL);
		} else if ((cur->type == AIM_SSI_TYPE_BUDDY) && ((cur->gid == 0x0000) || (!aim_ssi_itemlist_find(sess->ssi.local, cur->gid, 0x0000)))) {
			aim_ssi_addbuddy(sess, cur->name, "orphans", NULL, NULL, NULL, 0);
			aim_ssi_delbuddy(sess, cur->name, NULL);
		}
		cur = next;
	}

	/* Check if there are empty groups and delete them */
	cur = sess->ssi.local;
	while (cur) {
		next = cur->next;
		if (cur->type == AIM_SSI_TYPE_GROUP) {
			aim_tlv_t *tlv = aim_gettlv(cur->data, 0x00c8, 1);
			if (!tlv || !tlv->length)
				aim_ssi_itemlist_del(&sess->ssi.local, cur);
		}
		cur = next;
	}

	/* Check if the master group is empty */
	if ((cur = aim_ssi_itemlist_find(sess->ssi.local, 0x0000, 0x0000)) && (!cur->data))
		aim_ssi_itemlist_del(&sess->ssi.local, cur);

	return 0;
}

/**
 * Add a buddy to the list.
 *
 * @param sess The oscar session.
 * @param name The name of the item.
 * @param group The group of the item.
 * @param alias The alias/nickname of the item, or NULL.
 * @param comment The buddy comment for the item, or NULL.
 * @param smsnum The locally assigned SMS number, or NULL.
 * @return Return 0 if no errors, otherwise return the error number.
 */
faim_export int aim_ssi_addbuddy(aim_session_t *sess, const char *name, const char *group, const char *alias, const char *comment, const char *smsnum, int needauth)
{
	struct aim_ssi_item *parent;
	aim_tlvlist_t *data = NULL;

	if (!sess || !name || !group)
		return -EINVAL;

	/* Find the parent */
	if (!(parent = aim_ssi_itemlist_finditem(sess->ssi.local, group, NULL, AIM_SSI_TYPE_GROUP))) {
		/* Find the parent's parent (the master group) */
		if (!(parent = aim_ssi_itemlist_find(sess->ssi.local, 0x0000, 0x0000)))
			if (!(parent = aim_ssi_itemlist_add(&sess->ssi.local, NULL, 0x0000, 0x0000, AIM_SSI_TYPE_GROUP, NULL)))
				return -ENOMEM;
		/* Add the parent */
		if (!(parent = aim_ssi_itemlist_add(&sess->ssi.local, group, 0xFFFF, 0x0000, AIM_SSI_TYPE_GROUP, NULL)))
			return -ENOMEM;

		/* Modify the parent's parent (the master group) */
		aim_ssi_itemlist_rebuildgroup(sess->ssi.local, NULL);
	}

	/* Create a TLV list for the new buddy */
	if (needauth)
		aim_addtlvtochain_noval(&data, 0x0066);
	if (alias)
		aim_addtlvtochain_raw(&data, 0x0131, strlen(alias), alias);
	if (smsnum)
		aim_addtlvtochain_raw(&data, 0x013a, strlen(smsnum), smsnum);
	if (comment)
		aim_addtlvtochain_raw(&data, 0x013c, strlen(comment), comment);

	/* Add that bad boy */
	aim_ssi_itemlist_add(&sess->ssi.local, name, parent->gid, 0xFFFF, AIM_SSI_TYPE_BUDDY, data);
	aim_freetlvchain(&data);

	/* Modify the parent group */
	aim_ssi_itemlist_rebuildgroup(sess->ssi.local, group);

	/* Sync our local list with the server list */
	aim_ssi_sync(sess);

	return 0;
}

/**
 * Add a permit buddy to the list.
 *
 * @param sess The oscar session.
 * @param name The name of the item..
 * @return Return 0 if no errors, otherwise return the error number.
 */
faim_export int aim_ssi_addpermit(aim_session_t *sess, const char *name)
{

	if (!sess || !name)
		return -EINVAL;

	/* Add that bad boy */
	aim_ssi_itemlist_add(&sess->ssi.local, name, 0x0000, 0xFFFF, AIM_SSI_TYPE_PERMIT, NULL);

	/* Sync our local list with the server list */
	aim_ssi_sync(sess);

	return 0;
}

/**
 * Add a deny buddy to the list.
 *
 * @param sess The oscar session.
 * @param name The name of the item..
 * @return Return 0 if no errors, otherwise return the error number.
 */
faim_export int aim_ssi_adddeny(aim_session_t *sess, const char *name)
{

	if (!sess || !name)
		return -EINVAL;

	/* Add that bad boy */
	aim_ssi_itemlist_add(&sess->ssi.local, name, 0x0000, 0xFFFF, AIM_SSI_TYPE_DENY, NULL);

	/* Sync our local list with the server list */
	aim_ssi_sync(sess);

	return 0;
}

/**
 * Deletes a buddy from the list.
 *
 * @param sess The oscar session.
 * @param name The name of the item, or NULL.
 * @param group The group of the item, or NULL.
 * @return Return 0 if no errors, otherwise return the error number.
 */
faim_export int aim_ssi_delbuddy(aim_session_t *sess, const char *name, const char *group)
{
	struct aim_ssi_item *del;

	if (!sess)
		return -EINVAL;

	/* Find the buddy */
	if (!(del = aim_ssi_itemlist_finditem(sess->ssi.local, group, name, AIM_SSI_TYPE_BUDDY)))
		return -EINVAL;

	/* Remove the item from the list */
	aim_ssi_itemlist_del(&sess->ssi.local, del);

	/* Modify the parent group */
	aim_ssi_itemlist_rebuildgroup(sess->ssi.local, group);

	/* Check if we should delete the parent group */
	if ((del = aim_ssi_itemlist_finditem(sess->ssi.local, group, NULL, AIM_SSI_TYPE_GROUP)) && (!del->data)) {
		aim_ssi_itemlist_del(&sess->ssi.local, del);

		/* Modify the parent group */
		aim_ssi_itemlist_rebuildgroup(sess->ssi.local, NULL);

		/* Check if we should delete the parent's parent (the master group) */
		if ((del = aim_ssi_itemlist_find(sess->ssi.local, 0x0000, 0x0000)) && (!del->data)) {
			aim_ssi_itemlist_del(&sess->ssi.local, del);
		}
	}

	/* Sync our local list with the server list */
	aim_ssi_sync(sess);

	return 0;
}

/**
 * Deletes a permit buddy from the list.
 *
 * @param sess The oscar session.
 * @param name The name of the item, or NULL.
 * @return Return 0 if no errors, otherwise return the error number.
 */
faim_export int aim_ssi_delpermit(aim_session_t *sess, const char *name)
{
	struct aim_ssi_item *del;

	if (!sess)
		return -EINVAL;

	/* Find the item */
	if (!(del = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, name, AIM_SSI_TYPE_PERMIT)))
		return -EINVAL;

	/* Remove the item from the list */
	aim_ssi_itemlist_del(&sess->ssi.local, del);

	/* Sync our local list with the server list */
	aim_ssi_sync(sess);

	return 0;
}

/**
 * Deletes a deny buddy from the list.
 *
 * @param sess The oscar session.
 * @param name The name of the item, or NULL.
 * @return Return 0 if no errors, otherwise return the error number.
 */
faim_export int aim_ssi_deldeny(aim_session_t *sess, const char *name)
{
	struct aim_ssi_item *del;

	if (!sess)
		return -EINVAL;

	/* Find the item */
	if (!(del = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, name, AIM_SSI_TYPE_DENY)))
		return -EINVAL;

	/* Remove the item from the list */
	aim_ssi_itemlist_del(&sess->ssi.local, del);

	/* Sync our local list with the server list */
	aim_ssi_sync(sess);

	return 0;
}

/**
 * Move a buddy from one group to another group.  This basically just deletes the 
 * buddy and re-adds it.
 *
 * @param sess The oscar session.
 * @param oldgn The group that the buddy is currently in.
 * @param newgn The group that the buddy should be moved in to.
 * @param sn The name of the buddy to be moved.
 * @return Return 0 if no errors, otherwise return the error number.
 */
faim_export int aim_ssi_movebuddy(aim_session_t *sess, const char *oldgn, const char *newgn, const char *sn)
{
	aim_ssi_addbuddy(sess, sn, newgn, aim_ssi_getalias(sess->ssi.local, oldgn, sn), NULL, NULL, aim_ssi_waitingforauth(sess->ssi.local, oldgn, sn));
	aim_ssi_delbuddy(sess, sn, oldgn);
	return 0;
}

/**
 * Change the alias stored on the server for a given buddy.
 *
 * @param sess The oscar session.
 * @param gn The group that the buddy is currently in.
 * @param sn The screen name of the buddy.
 * @param alias The new alias for the buddy.
 * @return Return 0 if no errors, otherwise return the error number.
 */
faim_export int aim_ssi_aliasbuddy(aim_session_t *sess, const char *gn, const char *sn, const char *alias)
{
	struct aim_ssi_item *tmp;
	aim_tlvlist_t *data = NULL;

	if (!sess || !gn || !sn)
		return -EINVAL;

	if (!(tmp = aim_ssi_itemlist_finditem(sess->ssi.local, gn, sn, AIM_SSI_TYPE_BUDDY)))
		return -EINVAL;

	if (alias && !strlen(alias))
		alias = NULL;

	/* Need to add the x0131 TLV to the TLV chain */
	if (alias)
		aim_addtlvtochain_raw(&data, 0x0131, strlen(alias), alias);

	aim_freetlvchain(&tmp->data);
	tmp->data = data;

	/* Sync our local list with the server list */
	aim_ssi_sync(sess);

	return 0;
}

/**
 * Rename a group.
 *
 * @param sess The oscar session.
 * @param oldgn The old group name.
 * @param newgn The new group name.
 * @return Return 0 if no errors, otherwise return the error number.
 */
faim_export int aim_ssi_rename_group(aim_session_t *sess, const char *oldgn, const char *newgn)
{
	struct aim_ssi_item *group;

	if (!sess || !oldgn || !newgn)
		return -EINVAL;

	if (!(group = aim_ssi_itemlist_finditem(sess->ssi.local, oldgn, NULL, AIM_SSI_TYPE_GROUP)))
		return -EINVAL;

	free(group->name);
	group->name = (char *)malloc((strlen(newgn)+1)*sizeof(char));
	strcpy(group->name, newgn);

	/* Sync our local list with the server list */
	aim_ssi_sync(sess);

	return 0;
}

/**
 * Stores your permit/deny setting on the server, and starts using it.
 *
 * @param sess The oscar session.
 * @param permdeny Your permit/deny setting.  Can be one of the following:
 *        1 - Allow all users
 *        2 - Block all users
 *        3 - Allow only the users below
 *        4 - Block only the users below
 *        5 - Allow only users on my buddy list
 * @param vismask A bitmask of the class of users to whom you want to be 
 *        visible.  See the AIM_FLAG_BLEH #defines in aim.h
 * @return Return 0 if no errors, otherwise return the error number.
 */
faim_export int aim_ssi_setpermdeny(aim_session_t *sess, fu8_t permdeny, fu32_t vismask)
{
	struct aim_ssi_item *tmp;
	aim_tlvlist_t *data = NULL;

	if (!sess)
		return -EINVAL;

	/* Need to add the x00ca TLV to the TLV chain */
	aim_addtlvtochain8(&data, 0x00ca, permdeny);

	/* Need to add the x00cb TLV to the TLV chain */
	aim_addtlvtochain32(&data, 0x00cb, vismask);

	if ((tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, NULL, AIM_SSI_TYPE_PDINFO))) {
		aim_freetlvchain(&tmp->data);
		tmp->data = data;
	} else {
		tmp = aim_ssi_itemlist_add(&sess->ssi.local, NULL, 0x0000, 0xFFFF, AIM_SSI_TYPE_PDINFO, data);
		aim_freetlvchain(&data);
	}

	/* Sync our local list with the server list */
	aim_ssi_sync(sess);

	return 0;
}

/**
 * Set buddy icon information
 *
 * @param sess The oscar session.
 * @param iconcsum The MD5 checksum of the icon you are using.
 * @param iconcsumlen Length of the MD5 checksum given above.  Should be 0x10 bytes.
 * @return Return 0 if no errors, otherwise return the error number.
 */
faim_export int aim_ssi_seticon(aim_session_t *sess, fu8_t *iconsum, fu16_t iconsumlen)
{
	struct aim_ssi_item *tmp;
	aim_tlvlist_t *data = NULL;
	fu8_t *csumdata;

	if (!sess || !iconsum || !iconsumlen)
		return -EINVAL;

	if (!(csumdata = (fu8_t *)malloc((iconsumlen+2)*sizeof(fu8_t))))
		return -ENOMEM;
	csumdata[0] = 0x00;
	csumdata[1] = 0x10;
	memcpy(&csumdata[2], iconsum, iconsumlen);

	/* Need to add the x00d5 TLV to the TLV chain */
	aim_addtlvtochain_raw(&data, 0x00d5, (iconsumlen+2) * sizeof(fu8_t), csumdata);

	/* This TLV is added to cache the icon. */
	aim_addtlvtochain_noval(&data, 0x0131);

	if ((tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, "1", AIM_SSI_TYPE_ICONINFO))) {
		/* If the new tlvchain and oldtlvchain are the same, then do nothing */
		if (!aim_tlvlist_cmp(tmp->data, data)) {
			/* The new tlvlist is the identical to the old one */
			aim_freetlvchain(&data);
			free(csumdata);
			return 0;
		}
		aim_freetlvchain(&tmp->data);
		tmp->data = data;
	} else {
		tmp = aim_ssi_itemlist_add(&sess->ssi.local, "1", 0x0000, 0x51F4, AIM_SSI_TYPE_ICONINFO, data);
		aim_freetlvchain(&data);
	}

	/* Sync our local list with the server list */
	aim_ssi_sync(sess);
	free(csumdata);
	return 0;
}

/**
 * Stores your setting for whether you should show up as idle or not.
 *
 * @param sess The oscar session.
 * @param presence I think it's a bitmask, but I only know what one of the bits is:
 *        0x00000400 - Allow others to see your idle time
 * @return Return 0 if no errors, otherwise return the error number.
 */
faim_export int aim_ssi_setpresence(aim_session_t *sess, fu32_t presence) {
	struct aim_ssi_item *tmp;
	aim_tlvlist_t *data = NULL;

	if (!sess)
		return -EINVAL;

	/* Need to add the x00c9 TLV to the TLV chain */
	aim_addtlvtochain32(&data, 0x00c9, presence);

	if ((tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS))) {
		aim_freetlvchain(&tmp->data);
		tmp->data = data;
	} else {
		tmp = aim_ssi_itemlist_add(&sess->ssi.local, NULL, 0x0000, 0xFFFF, AIM_SSI_TYPE_PRESENCEPREFS, data);
		aim_freetlvchain(&data);
	}

	/* Sync our local list with the server list */
	aim_ssi_sync(sess);

	return 0;
}

/*
 * Subtype 0x0002 -)
{
	aim_conn_t *conn;

	if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)))
		return -EINVAL;

	return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_REQRIGHTS);
}

/*
 * Subtype 0x0003 -, i;
	aim_rxcallback_t userfunc;
	aim_tlvlist_t *tlvlist;
	aim_tlv_t *tlv;
	aim_bstream_t bstream;
	fu16_t *maxitems;

	/* This SNAC is made up of a bunch of TLVs */
	tlvlist = aim_readtlvchain(bs);

	/* TLV 0x0004 contains the maximum number of each item */
	if (!(tlv = aim_gettlv(tlvlist, 0x0004, 1))) {
		aim_freetlvchain(&tlvlist);
		return 0;
	}

	aim_bstream_init(&bstream, tlv->value, tlv->length);

	if (!(maxitems = (fu16_t *)malloc((tlv->length/2)*sizeof(fu16_t)))) {
		aim_freetlvchain(&tlvlist);
		return 0;
	}

	for (i=0; i<(tlv->length/2); i++)
		maxitems[i] = aimbs_get16(&bstream), tlv->length/2, maxitems);

	aim_freetlvchain(&tlvlist);
	free(maxitems);

	return ret;
}

/*
 * Subtype 0x0004 - Request SSI Data when you don't have a timestamp and 
 * revision number.
 * 
 */
faim_export int aim_ssi_reqdata(aim_session_t *sess)
{
	aim_conn_t *conn;

	if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)))
		return -EINVAL;

	/* Free any current data, just in case */
	aim_ssi_freelist(sess);

	return aim_genericreq_n_snacid(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_REQDATA);
}

/*
 * Subtype 0x0005 - Request SSI Data when you have a timestamp and revision 
 * numberifchanged(aim_session_t *sess, time_t timestamp, fu16_t numitems)
{
	aim_conn_t *conn;
	aim_frame_t *fr;
	aim_snacid_t snacid;

	if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI))AIM_CB_FAM_SSI, AIM_CB_SSI_REQIFCHANGED, 0x0000, NULL, 0);

	aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_REQIFCHANGED, 0x0000, snacid);
	aimbs_put32(&fr->data, timestamp);
	aimbs_put16(&fr->data, numitems);

	aim_tx_enqueue(sess, fr);

	/* Free any current data, just in case */
	aim_ssi_freelist(sess);

	return 0;
}

/*
 * Subtype 0x0006 -fu8_t fmtver; /* guess */
	fu16_t namelen, gid, bid, type;
	char *name;
	aim_tlvlist_t *data;

	fmtver = aimbs_get8(bs); /* Version of ssi data.  Should be 0x00 */
	sess->ssi.numitems += aimbs_get16(bs); /* # of items in this SSI SNAC */

	/* Read in the list */
	while (aim_bstream_empty(bs) > 4) { /* last four bytes are timestamp */
		if ((namelen = aimbs_get16(bs)))
			name = aimbs_getstr(bs, namelen);
		else
			name = NULL;
		gid = aimbs_get16(bs);
		bid = aimbs_get16(bs);
		type = aimbs_get16(bs);
		data = aim_readtlvchain_len(bs, aimbs_get16(bs));
		aim_ssi_itemlist_add(&sess->ssi.official, name, gid, bid, type, data);
		free(name);
		aim_freetlvchain(&data);
	}

	/* Read in the timestamp */
	sess->ssi.timestamp = aimbs_get32(bs);

	if (!(snac->flags & 0x0001)) {
		/* Make a copy of the list */
		struct aim_ssi_item *cur;
		for (cur=sess->ssi.official; cur; cur=cur->next)
			aim_ssi_itemlist_add(&sess->ssi.local, cur->name, cur->gid, cur->bid, cur->type, cur->data);

		sess->ssi.received_data = 1;

		ret = userfunc(sess, rx, fmtver, sess->ssi.numitems, sess->ssi.official, sess->ssi.timestamp);
	}

	return ret;
}

/*
 * Subtype 0x0007 - SSI Activate Data)
{
	aim_conn_t *conn;

	if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)))
		return -EINVAL;

	return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, 0x0007);
}

/*
 * Subtype 0x0008/0x0009/0x000a - SSI Add/Mod/Del Item(s).
 *
 * Sends the SNAC to add, modify, or delete an item from the server-stored
 * information.  These 3 SNACs all have an identical structure.  The only
 * difference is the subtype that is set for the SNAC.
 * 
 */
faim_export int aim_ssi_addmoddel(aim_session_t *sess)
{
	aim_conn_t *conn;
	aim_frame_t *fr;
	aim_snacid_t snacid;
	int snaclen;
	struct aim_ssi_tmp *cur;

	if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)) || !sess->ssi.pending || !sess->ssi.pending->item)
		return -EINVAL;

	/* Calculate total SNAC size */
	snaclen = 10; /* For family, subtype, flags, and SNAC ID */
	for (cur=sess->ssi.pending; cur; cur=cur->next) {
		snaclen += 10; /* For length, GID, BID, type, and length */
		if (cur->item->name)
			snaclen += strlen(cur->item->name);
		if (cur->item->data)
			snaclen += aim_sizetlvchain(&cur->item->data);
	}

	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, snaclen)))
		return -ENOMEM;

	snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, sess->ssi.pending->action, 0x0000, NULL, 0);
	aim_putsnac(&fr->data, AIM_CB_FAM_SSI, sess->ssi.pending->action, 0x0000, snacid);

	for (cur=sess->ssi.pending; cur; cur=cur->next) {
		aimbs_put16(&fr->data, cur->item->name ? strlen(cur->item->name) : 0);
		if (cur->item->name)
			aimbs_putraw(&fr->data, cur->item->name, strlen(cur->item->name));
		aimbs_put16(&fr->data, cur->item->gid);
		aimbs_put16(&fr->data, cur->item->bid);
		aimbs_put16(&fr->data, cur->item->type);
		aimbs_put16(&fr->data, cur->item->data ? aim_sizetlvchain(&cur->item->data) : 0);
		if (cur->item->data)
			aim_writetlvchain(&fr->data, &cur->item->data);
	}

	aim_tx_enqueue(sess, fr);

	return 0;
}

/*
 * Subtype 0x0008 - Incoming SSI add.
 *
 * XXX - It would probably be good for the client to actually do something when it gets this.
 */
static int parsead	char *name;
	fu16_t len, gid, bid, type;
	aim_tlvlist_t *data;

	while (aim_bstream_empty(bs)) {
		if ((len = aimbs_get16(bs)))
			name = aimbs_getstr(bs, len);
		else
			name = NULL;
		gid = aimbs_get16(bs);
		bid = aimbs_get16(bs);
		type = aimbs_get16(bs);
		if ((len = aimbs_get16(bs)))
			data = aim_readtlvchain_len(bs, len);
		else
			data = NULL;

		aim_ssi_itemlist_add(&sess->ssi.local, name, gid, bid, type, data);
		aim_ssi_itemlist_add(&sess->ssi.official, name, gid, bid, type, data);
		free(name);
		aim_freetlvchain(&data);

		ret = userfunc(sess, rx);

		free(name);
	}

	return ret;
}

/*
 * Subtype 0x0009 - Incoming SSI mod.
 *
 * XXX - It would probably be good for the client to actually do something when it gets this.
 */
static int parsemo	char *name;
	fu16_t len, gid, bid, type;
	aim_tlvlist_t *data;
	struct aim_ssi_item *item;

	while (aim_bstream_empty(bs)) {
		if ((len = aimbs_get16(bs)))
			name = aimbs_getstr(bs, len);
		else
			name = NULL;
		gid = aimbs_get16(bs);
		bid = aimbs_get16(bs);
		type = aimbs_get16(bs);
		if ((len = aimbs_get16(bs)))
			data = aim_readtlvchain_len(bs, len);
		else
			data = NULL;

		/* Replace the 2 local items with the given one */
		if ((item = aim_ssi_itemlist_find(sess->ssi.local, gid, bid))) {
			item->type = type;
			free(item->name);
			if (name) {
				item->name = (char *)malloc((strlen(name)+1)*sizeof(char));
				strcpy(item->name, name);
			} else
				item->name = NULL;
			aim_freetlvchain(&item->data);
			item->data = aim_tlvlist_copy(data);
		}

		if ((item = aim_ssi_itemlist_find(sess->ssi.official, gid, bid))) {
			item->type = type;
			free(item->name);
			if (name) {
				item->name = (char *)malloc((strlen(name)+1)*sizeof(char));
				strcpy(item->name, name);
			} else
				item->name = NULL;
			aim_freetlvchain(&item->data);
			item->data = aim_tlvlist_copy(data);
		}

		ret = userfunc(sess, rx);

		free(name);
		aim_freetlvchain(&data);
	}

	return ret;
}

/*
 * Subtype 0x000a - Incoming SSI del.
 *
 * XXX - It would probably be good for the client to actually do something when it gets this.
 */
static int parsedel	fu16_t gid, bid;
	struct aim_ssi_item *del;

	while (aim_bstream_empty(bs)) {
		aim_bstream_advance(bs, aimbs_get16(bs));
		gid = aimbs_get16(bs);
		bid = aimbs_get16(bs);
		aimbs_get16(bs);
		aim_bstream_advance(bs, aimbs_get16(bs));

		if ((del = aim_ssi_itemlist_find(sess->ssi.local, gid, bid)))
			aim_ssi_itemlist_del(&sess->ssi.local, del);
		if ((del = aim_ssi_itemlist_find(sess->ssi.official, gid, bid)))
			aim_ssi_itemlist_del(&sess->ssi.official, del);

		ret = userfunc(sess, rx);
	}

	return ret;
}

/*
 * Subtype 0x000e - SSI Add/Mod/Del Ack.
 *
 * Response to add, modify, or delete SNAC (sent with aim_ssi_addmoddel).
 *
 */
static int parseack	struct aim_ssi_tmp *cur, *del;

	/* Read in the success/failure flags from the ack SNAC */
	cur = sess->ssi.pending;
	while (cur && (aim_bstream_empty(bs)>0)) {
		cur->ack = aimbs_get16(bs);
		cur = cur->next;
	}

	/*
	 * If outcome is 0, then add the item to the item list, or replace the other item, 
	 * or remove the old item.  If outcome is non-zero, then remove the item from the 
	 * local list, or unmodify it, or add it.
	 */
	for (cur=sess->ssi.pending; (cur && (cur->ack != 0xffff)); cur=cur->next) {
	if (cur->item) {
		if (cur->ack) {
			/* Our action was unsuccessful, so change the local list back to how it was */
			if (cur->action == AIM_CB_SSI_ADD) {
				/* Remove the item from the local list */
				/* Make sure cur->item is still valid memory */
				if (aim_ssi_itemlist_valid(sess->ssi.local, cur->item)) {
					if (cur->item->name) {
						cur->name = (char *)malloc((strlen(cur->item->name)+1)*sizeof(char));
						strcpy(cur->name, cur->item->name);
					}
					aim_ssi_itemlist_del(&sess->ssi.local, cur->item);
				}
				cur->item = NULL;

			} else if (cur->action == AIM_CB_SSI_MOD) {
				/* Replace the local item with the item from the official list */
				if (aim_ssi_itemlist_valid(sess->ssi.local, cur->item)) {
					struct aim_ssi_item *cur1;
					if ((cur1 = aim_ssi_itemlist_find(sess->ssi.official, cur->item->gid, cur->item->bid))) {
						free(cur->item->name);
						if (cur1->name) {
							cur->item->name = (char *)malloc((strlen(cur1->name)+1)*sizeof(char));
							strcpy(cur->item->name, cur1->name);
						} else
							cur->item->name = NULL;
						aim_freetlvchain(&cur->item->data);
						cur->item->data = aim_tlvlist_copy(cur1->data);
					}
				} else
					cur->item = NULL;

			} else if (cur->action == AIM_CB_SSI_DEL) {
				/* Add the item back into the local list */
				if (aim_ssi_itemlist_valid(sess->ssi.official, cur->item)) {
					aim_ssi_itemlist_add(&sess->ssi.local, cur->item->name, cur->item->gid, cur->item->bid, cur->item->type, cur->item->data);
				} else
					cur->item = NULL;
			}

		} else {
			/* Do the exact opposite */
			if (cur->action == AIM_CB_SSI_ADD) {
			/* Add the local item to the official list */
				if (aim_ssi_itemlist_valid(sess->ssi.local, cur->item)) {
					aim_ssi_itemlist_add(&sess->ssi.official, cur->item->name, cur->item->gid, cur->item->bid, cur->item->type, cur->item->data);
				} else
					cur->item = NULL;

			} else if (cur->action == AIM_CB_SSI_MOD) {
				/* Replace the official item with the item from the local list */
				if (aim_ssi_itemlist_valid(sess->ssi.local, cur->item)) {
					struct aim_ssi_item *cur1;
					if ((cur1 = aim_ssi_itemlist_find(sess->ssi.official, cur->item->gid, cur->item->bid))) {
						free(cur1->name);
						if (cur->item->name) {
							cur1->name = (char *)malloc((strlen(cur->item->name)+1)*sizeof(char));
							strcpy(cur1->name, cur->item->name);
						} else
							cur1->name = NULL;
						aim_freetlvchain(&cur1->data);
						cur1->data = aim_tlvlist_copy(cur->item->data);
					}
				} else
					cur->item = NULL;

			} else if (cur->action == AIM_CB_SSI_DEL) {
				/* Remove the item from the official list */
				if (aim_ssi_itemlist_valid(sess->ssi.official, cur->item))
					aim_ssi_itemlist_del(&sess->ssi.official, cur->item);
				cur->item = NULL;
			}

		}
	} /* End if (cur->item) */
	} /* End for loop */, sess->ssi.pending);

	/* Free all aim_ssi_tmp's with an outcome */
	cur = sess->ssi.pending;
	while (cur && (cur->ack != 0xffff)) {
		del = cur;
		cur = cur->next;
		free(del->name);
		free(del);
	}
	sess->ssi.pending = cur;

	/* If we're not waiting for any more acks, then send more SNACs */
	if (!sess->ssi.pending) {
		sess->ssi.pending = NULL;
		sess->ssi.waiting_for_ack = 0;
		aim_ssi_sync(sess);
	}

	return ret;
}

/*
 * Subtype 0x000f - SSI Data Unchanged.
 *
 * Response to aim_ssi_reqifchangedsess->ssi.received_data = 1/*
 * Subtype 0x0011 -)
{
	aim_conn_t *conn;

	if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)))
		return -EINVAL;

	return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_EDITSTART);
}

/*
 * Subtype 0x0012 - SSI End Data Modification.
 *
 * Tells the server you're finished modifying data.
 *
 */
faim_export int aim_ssi_modend(aim_session_t *sess)
{
	aim_conn_t *conn;

	if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)))
		return -EINVAL;

	return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_EDITSTOP);
}

/*
 * Subtype 0x0014 - Grant authorization
 *
 * Authorizes a contact so they can add you to their contact list.
 *
 */
faim_export int aim_ssi_sendauth(aim_session_t *sess, char *sn, char *msg)
{
	aim_conn_t *conn;
	aim_frame_t *fr;
	aim_snacid_t snacid;

	if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)) || !sn)
		return -EINVAL;

	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)+2+(msg ? strlen(msg)+1 : 0)+2)))
		return -ENOMEM;

	snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTH, 0x0000, NULL, 0);
	aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTH, 0x0000, snacid);

	/* Screen name */
	aimbs_put8(&fr->data, strlen(sn));
	aimbs_putraw(&fr->data, sn, strlen(sn));

	/* Message (null terminated) */
	aimbs_put16(&fr->data, msg ? strlen(msg) : 0);
	if (msg) {
		aimbs_putraw(&fr->data, msg, strlen(msg));
		aimbs_put8(&fr->data, 0x00);
	}

	/* Unknown */
	aimbs_put16(&fr->data, 0x0000);

	aim_tx_enqueue(sess, fr);

	return 0;
}

/*
 * Subtype 0x0015 - Receive an authorization grant
 */
static int receiveauthgrant	fu16_t tmp;
	char *sn, *msg;

	/* Read screen name */
	if ((tmp = aimbs_get8(bs)))
		sn = aimbs_getstr(bs, tmp);
	else
		sn = NULL;

	/* Read message (null terminated) */
	if ((tmp = aimbs_get16(bs)))
		msg = aimbs_getstr(bs, tmp);
	else
		msg = NULL;

	/* Unknown */
	tmp = aimbs_get16(bs), sn, msg);

	free(sn);
	free(msg);

	return ret;
}

/*
 * Subtype 0x0018 - Send authorization request
 *
 * Sends a request for authorization to the given contact.  The request will either be
 * granted, denied, or dropped.
 *
 */
faim_export int aim_ssi_sendauthrequest(aim_session_t *sess, char *sn, char *msg)
{
	aim_conn_t *conn;
	aim_frame_t *fr;
	aim_snacid_t snacid;

	if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)) || !sn)
		return -EINVAL;

	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)+2+(msg ? strlen(msg)+1 : 0)+2)))
		return -ENOMEM;

	snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREQ, 0x0000, NULL, 0);
	aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREQ, 0x0000, snacid);

	/* Screen name */
	aimbs_put8(&fr->data, strlen(sn));
	aimbs_putraw(&fr->data, sn, strlen(sn));

	/* Message (null terminated) */
	aimbs_put16(&fr->data, msg ? strlen(msg) : 0);
	if (msg) {
		aimbs_putraw(&fr->data, msg, strlen(msg));
		aimbs_put8(&fr->data, 0x00);
	}

	/* Unknown */
	aimbs_put16(&fr->data, 0x0000);

	aim_tx_enqueue(sess, fr);

	return 0;
}

/*
 * Subtype 0x0019 - Receive an authorization request
 */
static int receiveauthrequest	fu16_t tmp;
	char *sn, *msg;

	/* Read screen name */
	if ((tmp = aimbs_get8(bs)))
		sn = aimbs_getstr(bs, tmp);
	else
		sn = NULL;

	/* Read message (null terminated) */
	if ((tmp = aimbs_get16(bs)))
		msg = aimbs_getstr(bs, tmp);
	else
		msg = NULL;

	/* Unknown */
	tmp = aimbs_get16(bs), sn, msg);

	free(sn);
	free(msg);

	return ret;
}

/*
 * Subtype 0x001a - Send authorization reply
 *
 * Sends a reply to a request for authorization.  The reply can either 
 * grant authorization or deny authorization.
 *
 * if reply=0x00 then deny
 * if reply=0x01 then grant
 *
 */
faim_export int aim_ssi_sendauthreply(aim_session_t *sess, char *sn, fu8_t reply, char *msg)
{
	aim_conn_t *conn;
	aim_frame_t *fr;
	aim_snacid_t snacid;

	if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)) || !sn)
		return -EINVAL;

	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 1+strlen(sn) + 1 + 2+(msg ? strlen(msg)+1 : 0) + 2)))
		return -ENOMEM;

	snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREP, 0x0000, NULL, 0);
	aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREP, 0x0000, snacid);

	/* Screen name */
	aimbs_put8(&fr->data, strlen(sn));
	aimbs_putraw(&fr->data, sn, strlen(sn));

	/* Grant or deny */
	aimbs_put8(&fr->data, reply);

	/* Message (null terminated) */
	aimbs_put16(&fr->data, msg ? (strlen(msg)+1) : 0);
	if (msg) {
		aimbs_putraw(&fr->data, msg, strlen(msg));
		aimbs_put8(&fr->data, 0x00);
	}

	/* Unknown */
	aimbs_put16(&fr->data, 0x0000);

	aim_tx_enqueue(sess, fr);

	return 0;
}

/*
 * Subtype 0x001b - Receive an authorization reply
 * You get this bad boy when other people respond to the authorization 
 * request that you have previously sent them.
 */
static int receiveauthreply	fu16_t tmp;
	fu8_t reply;
	char *sn, *msg;

	/* Read screen name */
	if ((tmp = aimbs_get8(bs)))
		sn = aimbs_getstr(bs, tmp);
	else
		sn = NULL;

	/* Read reply */
	reply = aimbs_get8(bs);

	/* Read message (null terminated) */
	if ((tmp = aimbs_get16(bs)))
		msg = aimbs_getstr(bs, tmp);
	else
		msg = NULL;

	/* Unknown */
	tmp = aimbs_get16(bs), sn, reply, msg);

	free(sn);
	free(msg);

	return ret;
}

/*
 * Subtype 0x001c - Receive a message telling you someone added you to their list.
 */
static int receiveadd	fu16_t tmp;
	char *sn;

	/* Read screen name */
	if ((tmp = aimbs_get8(bs)))
		sn = aimbs_getstr(bs, tmp);
	else
		sn = NULL, sn);

	free(snAIM_CB_SSI_RIGHTSINFO)
		return parserights(sess, mod, rx, snac, bs);
	else if (snac->subtype == AIM_CB_SSI_LIST)
		return parsedata(sess, mod, rx, snac, bs);
	else if (snac->subtype == AIM_CB_SSI_ADD)
		return parseadd(sess, mod, rx, snac, bs);
	else if (snac->subtype == AIM_CB_SSI_MOD)
		return parsemod(sess, mod, rx, snac, bs);
	else if (snac->subtype == AIM_CB_SSI_DEL)
		return parsedel(sess, mod, rx, snac, bs);
	else if (snac->subtype == AIM_CB_SSI_SRVACK)
		return parseack(sess, mod, rx, snac, bs);
	else if (snac->subtype == AIM_CB_SSI_NOLIST)
		return parsedataunchanged(sess, mod, rx, snac, bs);
	else if (snac->subtype == AIM_CB_SSI_RECVAUTH)
		return receiveauthgrant(sess, mod, rx, snac, bs);
	else if (snac->subtype == AIM_CB_SSI_RECVAUTHREQ)
		return receiveauthrequest(sess, mod, rx, snac, bs);
	else if (snac->subtype == AIM_CB_SSI_RECVAUTHREP)
		return receiveauthreply(sess, mod, rx, snac, bs);
	else if (snac->subtype == AIM_CB_SSI_ADDED)
		return receiveadded(sess, mod, rx, snac, bs);

	return 0;
}

static void ssi_shutdown(aim_session_t *sess, aim_module_t *mod)
{
	aim_ssi_freelist(sess);

	returnAIM_CB_FAM_SSI;
	mod->version = 0x0004;
	mod->toolid = 0x0110;
	mod->toolversion = 0x0629;
	mod->flags = 0;
	strncpy(mod->name, "ssi", sizeof(mod->name));
	mod->snachandler = snachandler;
	mod->shutdown = ssi_shutdown;

	return 0;
}
ENDREP
DELTA 130 114853 903
SVN  ‡~ˆ• ‡€p/*
 * Family 0x0006 -629;
	mod->flags = 0;
	strncpy(mod->name, "invite", sizeof(mod->name));
	mod->snachandler = NULL;

	return 0;
}
ENDREP
DELTA 130 140455 2565
SVN  §§7C ›$ €; ˆVœ@ˆ z¥session_t *sess, aim_msgcookie_t *cookie) 
{

	if (!cookie)
		return -EINVAL;

	faimdprintf(sess, 0, "\tCookie at %p: %d/%s with %p, next %p\n", cookie, 
			cookie->type, cookie->cookie, DIRECTIMENDREP
DELTA 130 143993 5727
SVN  í!é6Œ† ‚B
… ˆ-‚R 8‹ „rŒ=€† ¬W‘, ‚(Â •ÅE€P ä€… …vç(/*
 * fu16_/* SNAC flags are apparently uniform across all SNACs, so we handle them here */
	if (snac.flags & 0x0001) {
		/*
		 * This means the SNAC will be followed by another SNAC with 
		 * related information.  We don't need to do anything about 
		 * this here.
		 */
	}
	if (snac.flags & 0x8000) {
		/*
		 * This packet contains the version of the family that this SNAC is 
		 * in.  You get this when your SSI module is version 2 or higher.  
		 * For now we have no need for this, but you could always save 
		 * it as a part of aim_modnsac_t, or something.  The format is...
		 * 2 byte length of total mini-header (which is 6 bytes), then TLV 
		 * of  type 0x0001, length 0x0002, value is the 2 byte version 
		 * number
		 */
		aim_bstream_advance(&rx->data, aimbs_get16(&rx->data));
	}if (cur->hdrtype == AIM_FRAMETYPE_FLAP) {
			if (cur->hdr.flap.type == 0x01) {
		continue;

			} else if (cur->hdr.flap.type == 0x02) {
				if ((cur->handled = consumesnac(sess, cur)))
					continue;

			} else if (cur->hdr.flap.type == 0x04) {
				cur->handled = negchan_middle(sess, cur);
				continue;

			} else if (cur->hdr.flap.type == 0x05) {

			}

		} else if (cur->hdrtype == AIM_FRAMETYPE_OFT) {
			if (cur->conn->type == AIM_CONN_TYPE_RENDEZVOUS) {
				aim_rxdispatch_rendezvous(sess, cur);
				cur->handled = 1;
				continue;

			} else if (cur->conn->type == AIM_CONN_TYPE_LISTENER) {
				/* not possible */
				faimdprintf(sess, 0, "rxdispatch called on LISTENER connection!\n");
				cur->handled = 1;
				continue;
			}
		}
ENDREP
DELTA 130 155467 4240
SVN  Ï³)2‘O† ˆd€q x¬`€‚ mµc ¶X „·Z€ˆ [¯*€o d²€„L 4¾Q/*
 * /**
 * aim_frame_destroy - free aim_frame_t
 * @frame: the frame to free
 *
 * returns -1 on error; 0 on success.	free(frame);

	return;
}

/*
 * Read a FLAP header from conn into fr, and return the number of bytes in the payload.
 */
static int aim_get_command_flap(aim_session_t *sess, aim_conn_t *conn, aim_frame_t *fr)
{
	fu8_t flaphdr_raw[6];
	aim_bstream_t flaphdr;
	fu16_t payloadlen;
	we're doing FLAP if we're here */
	fr->hdrtype = AIM_FRAMETYPE_FLAP;

	fr->hdr.flap.type = aimbs_get8(&flaphdr);
	fr->hdr.flap.seqnum = aimbs_get16(&flaphdr);
	payloadlen = aimbs_get16(&flaphdr); /* length of payload */

	return payloadlen;
}

/*
 * Read a rendezvous header from conn into fr, and return the number of bytes in the payload.
 */
static int aim_get_command_rendezvous(aim_session_t *sess, aim_conn_t *conn, aim_frame_t *fr)
{
	fu8_t rendhdr_raw[8];
	aim_bstream_t rendhdr;

	aim_bstream_init(&rendhdr, rendhdr_raw, sizeof(rendhdr_raw));

	if (aim_bstream_recv(&rendhdr, conn->fd, 8) < 8) {
		aim_conn_close(conn);
		return -1;
	}

	aim_bstream_rewind(&rendhdr);

	fr->hdrtype = AIM_FRAMETYPE_OFT; /* a misnomer--rendezvous */

	aimbs_getrawbuf(&rendhdr, fr->hdr.rend.magic, 4);
	fr->hdr.rend.hdrlen = aimbs_get16(&rendhdr) - 8;
	fr->hdr.rend.type = aimbs_get16(&rendhdr);

	return fr->hdr.rend.hdrlen;
}

/*
 * Grab a single command sequence off the socket, and enqueue it in the incoming event queue 
 * in a sepaaim_frame_t *newrx;
	fu16_t payloadlen;

	if (!sess || !conn)
		return -EINVAL;

	if (conn->fd == -1)
		return -1; /* it's an aim_conn_close()'d connection */

	if (conn->fd < 3) /* can happen when people abuse the interface */
		return -1if (!(newrx = (aim_frame_t *)calloc(sizeof(aim_frame_t), 1)))
		return -ENOMEM;

	/*
	 * Rendezvous (client to client) connections do not speak FLAP, so this 
	 * function will break on them.
	 */
	if (conn->type == AIM_CONN_TYPE_RENDEZVOUS) {
		int ret = aim_get_command_rendezvous(sess, conn, newrx);

		if (ret < 0) {
			free(newrx);
			return -1;
		}

		payloadlen = ret;
	} else if (conn->type == AIM_CONN_TYPE_LISTENER) {
		faimdprintf(sess, 0, "AIM_CONN_TYPE_LISTENER on fd %d\n", conn->fd);
		free(newrx);
		return -1;
	} else
		payloadlen = aim_get_command_flap(sess, conn, newrxENDREP
DELTA 130 159720 1524
SVN  •q–K‚ €T „u…J° „™ Š:Š>ƒ t”{/*
 * Family 0x000a - User Search.
 *
 * TODO: Add aim_usersearch_name()
 *
 */

#define FAIM_INTERNAL
#include <aim.h>

/*
 * Subtype 0x0001
 *
 * XXX can this be integrated with the rest of the error handling?
/*
 * Subtype 0x0002
 *
 */
faim_export int aim_
 * Subtype 0x0003
 *
 */629ENDREP
DELTA 130 161257 11281
SVN  Òeê:‚.¤
• hž ˆ‚œ ˆaŠ8Ÿ &“9™ „T”y¬ …™>€L \çk ”- · i­x ‘;µQ£ „!Ç0¦ „!Ëxœ ~Ð9Œ ƒUÑ5€ ƒ=Ö Ù}Œ sÚz€\ ˆÜL¹ e­xŒ sÚz€M çDž |èt€U mê:€ e­xŒ 9ì~Œ mê:€  ƒ8ðŸ ‹óu¶ ­x „M€3¡ ‚T…%€r ˆm£ ‹uŠ-¹ e­x ‚—G€ƒq ƒ›t€‹	 ŽXž\¤ ~­_Œ ƒ®[‚ _±| ‚o²gª ‘s¶€‚6 e­x€ VÔ.€ƒl ˆ=Çu¿ UÐƒ uÑo/*
 * Family 0x0001 -Subtype 0x0002 - Client OnlineSubtype 0x0003 - Host Onlineubtype 0x0004 - Service requestSubtype 0x0005 - Redirectlen = aim_gettlv(tlvlist, 0x0006, 1)->lengthSubtype 0x0006 - Request Rate Information. */
faim_internal int aim_reqratesSubtype 0x0007 - Rate Parameters */
static int raterespSubtype 0x0008 - Add Rate ParameterSubtype 0x0009 - Delete Rate ParameterSubtype 0x000a - Rate Changeint ret = 0; = userfunc(sess, rx, code, rateclass, windowsize, clear, alert, limit, disconnect, currentavg, maxavg);

	return ret;
}

/*
 * How Migrations work.ubtype 0x000b - Service Pauseint ret = 0; = userfunc(sess, rx);

	return ret;
}

/*
 * Subtype 0x000c - Service Pause Acknowledgementubtype 0x000d - Service Resume */
static int serverresumeint ret = 0; = userfunc(sess, rx);

	return ret;
}

/* Subtype 0x000e - Request self-infoubtype 0x000f - Self User Infoint ret = 0;
	aim_rxcallback_t userfunc;
	aim_userinfo_t userinfo;

	aim_info_extract = userfunc(sess, rx, &userinfo);

	aim_info_free(&userinfo);

	return ret;
}

/* Subtype 0x0010 - Evil Notification */
static int evilnotifyint ret = 0;info_extract = userfunc(sess, rx, newevil, &userinfo);

	aim_info_free(&userinfo);

	return ret;
}

/*
 * Subtype 0x0011 - Idle Notificationubtype 0x0012 - Service MigrateSubtype 0x0013 - Message of the Day */
static int motdubtype 0x0014 - Set privacy flagsSubtype 0x0016 - No-op
 *
 * WinAIM sends these every 4min or so to keep the connection alive.  Its not 
 * reallyubtype 0x0017 - Set client versionsSubtype 0x0018 - Host versions */
static int hostversionsubtype 0x001e - Extended Status
 *
 * Sets your ICQ status (available, away, do not disturb, etc.)
 *
 * These are the same TLVs seen in user info.  You can 
 * also set 0x0008 and 0x000c.
 */
faim_export int aim_setextstatus(aim_session_t *sess, fu32_t status)
{
	aim_conn_t *conn;
	aim_frame_t *fr;
	aim_snacid_t snacid;
	aim_tlvlist_t *tl = NULL;
	fu32_t data;

	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
		return -EINVAL;

	data = AIM_ICQ_STATE_HIDEIP | AIM_ICQ_STATE_WEBAWARE 
 * Subtype 0x001e - Extended Status.
 *
 * Sets your "available" message.  This is currently only supported by iChat 
 * and Gaim.
 *
 * These are the same TLVs seen in user info.  You can 
 * also set 0x0008 and 0x000c.
 */
faim_export int aim_srv_setavailmsg(aim_session_t *sess, char *msg)
{
	aim_conn_t *conn;
	aim_frame_t *fr;
	aim_snacid_t snacid;

	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0001)))
		return -EINVAL;

	if (msg) {
		if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + strlen(msg) + 8)))
			return -ENOMEM;

		snacid = aim_cachesnac(sess, 0x0001, 0x001e, 0x0000, NULL, 0);
		aim_putsnac(&fr->data, 0x0001, 0x001e, 0x0000, snacid);

		aimbs_put16(&fr->data, 0x001d);
		aimbs_put16(&fr->data, strlen(msg)+8);
		aimbs_put16(&fr->data, 0x0002);
		aimbs_put8(&fr->data, 0x04);
		aimbs_put8(&fr->data, strlen(msg)+4);
		aimbs_put16(&fr->data, strlen(msg));
		aimbs_putraw(&fr->data, msg, strlen(msg));
		aimbs_put16(&fr->data, 0x0000);
	} else {
		if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + 8)))
			return -ENOMEM;

		snacid = aim_cachesnac(sess, 0x0001, 0x001e, 0x0000, NULL, 0);
		aim_putsnac(&fr->data, 0x0001, 0x001e, 0x0000, snacid);

		aimbs_put16(&fr->data, 0x001d);
		aimbs_put16(&fr->data, 0x0008);
		aimbs_put16(&fr->data, 0x0002);
		aimbs_put16(&fr->data, 0x0404);
		aimbs_put16(&fr->data, 0x0000);
		aimbs_put16(&fr->data, 0x0000);
	}
Subtype 0x001f - Client verificationint ret = 0; =ret;
}

#if 0Subtype 0x0020 - Client verification reply/*
 * Subtype 0x0021 - Receive our extended status
 *
 * This is used for iChat's "available" messages, and maybe ICQ extended 
 * status messages?  It's also used to tell the client whether or not it 
 * needs to upload an SSI buddy icon... who engineers this stuff, anyway?
 */
static int aim_parse_extstatusint ret = 0;
	aim_rxcallback_t userfunc;
	fu16_t type;
	fu8_t flags, length;

	type = aimbs_get16(bs); 
	flags = aimbs_get8(bs);
	length = aimbs_get8 {
		switch (type) {
		case 0x0000:
		case 0x0001: { /* buddy icon checksum */
			/* not sure what the difference between 1 and 0 is */
			fu8_t *md5 = aimbs_getraw(bs, length);
			ret = userfunc(sess, rx, type, flags, length, md5);
			free(md5);
			} break;
		case 0x0002: { /* available message */
			/* there is a second length that is just for the message */
			char *msg = aimbs_getstr(bs, aimbs_get16(bs));
			ret = userfunc(sess, rx, msg);
			free(msg);
			} break;
		}
	}

	return ret	else if (snac->subtype == 0x0021)
		return aim_parse_extstatus629ENDREP
DELTA 130 172551 1891
SVN  ˜k™ 9 €\ ‚5† ƒ’\Ÿ ‚#–Hif (cur->flags & AIM_SNACFLAGS_DESTRUCTOR) {
				free(cur->data);
				cur->data = NULL;
			}exportfree(cur->data);
				free(cur);ENDREP
DELTA 130 175088 523
SVN  †r‡*© ' ¹ vR­ ƒ~‚t/*
 * Family 0x000b - Statistics.
 *
 */
int ret = 0;
	aim_rxcallback_t userfunc;
	fu16_t interval = userfunc(sess, rx, interval);

	return retENDREP
DELTA 130 175624 5254
SVN  évlD£1 ƒ; – …ƒo „q‰€‚H `\€„
 „_ƒo€‰} „_ƒo€a ŠH’D€/ ‰ižB— œ¨G¤ ¤Ådparam bs Input bstream	 {
				aim_freetlvchain(&list);
				return NULL;
			}

			cur = (aim_tlvlist_t *)malloc(sizeof(aim_tlvlist_t));
			if (!cur) {
				aim_freetlvchain(&list);
				return NULL;
			}

			memset(cur, 0, sizeof(aim_tlvlist_t));

			cur->tlv = createtlv();
			if (!cur->tlv) {
				free(cur);
				aim_freetlvchain(&list);
				return NULLaim_freetlvchain(&list);
				       return NULL;
			       }
			}

			cur->next = list;
			list = cur;
		}
	}

	return list;
}

/**
 * aim_readtlvchain_num - Read a TLV chain from a buffer.
 * @param bs Input bstream
 * @param num The max number of TLVs that will be read, or -1 if unlimited.  
 *        There are a number of places where you want to read in a tlvchain, 
 *        but the chain is not at the end of the SNAC, and the chain is 
 *        preceeded by the number of TLVs.  So you can limit that with this._num(aim_bstream_t *bs, fu16_t num)
{
	aim_tlvlist_t *list = NULL, *cur;

	while ((aim_bstream_empty(bs) > 0) && (num != 0)) {
		fu16_t type, length;

		type = aimbs_get16(bs);
		length = aimbs_get16(bs);

		if (length > aim_bstream_empty(bs)) {
			aim_freetlvchain(&list);
			return NULL;
		}

		cur = (aim_tlvlist_t *)malloc(sizeof(aim_tlvlist_t));
		if (!cur) {
			aim_freetlvchain(&list);
			return NULL;
		}

		memset(cur, 0, sizeof(aim_tlvlist_t));

		cur->tlv = createtlv();
		if (!cur->tlv) {
			free(cur);
			aim_freetlvchain(&list);
			return NULL;
		}
		cur->tlv->type = type;
		if ((cur->tlv->length = length)) {
		       cur->tlv->value = aimbs_getraw(bs, length);
		       if (!cur->tlv->value) {
			       freetlv(&cur->tlv);
			       free(cur);
			       aim_freetlvchain(&list);
			       return NULL;
		       }
		}

		num--;
		cur->next = list;
		list = cur;
	}

	return list;
}

/**
 * aim_readtlvchain_len - Read a TLV chain from a buffer.
 * @param bs Input bstream
 * @param len The max length in bytes that will be read.
 *        There are a number of places where you want to read in a tlvchain, 
 *        but the chain is not at the end of the SNAC, and the chain is 
 *        preceeded by the length of the TLVs.  So you can limit that with this._len(aim_bstream_t *bs, fu16_t len)
{
	aim_tlvlist_t *list = NULL, *cur;

	while ((aim_bstream_empty(bs) > 0) && (len > 0)) {
		fu16_t type, length;

		type = aimbs_get16(bs);
		length = aimbs_get16(bs);

		if (length > aim_bstream_empty(bs)) {
			aim_freetlvchain(&list);
			return NULL;
		}

		cur = (aim_tlvlist_t *)malloc(sizeof(aim_tlvlist_t));
		if (!cur) {
			aim_freetlvchain(&list);
			return NULL;
		}

		memset(cur, 0, sizeof(aim_tlvlist_t));

		cur->tlv = createtlv();
		if (!cur->tlv) {
			free(cur);
			aim_freetlvchain(&list);
			return NULL;
		}
		cur->tlv->type = type;
		if ((cur->tlv->length = length)) {
		       cur->tlv->value = aimbs_getraw(bs, length);
		       if (!cur->tlv->value) {
			       freetlv(&cur->tlv);
			       free(cur);
			       aim_freetlvchain(&list);
			       return NULL;
		       }
		}

		len -= aim_sizetlvchain(&cur);
		cur->next = list;
		list = cur;
	}

	return list;
}

/**
 * aim_tlvlist_copy - Duplicate a TLV chain.
 * @param orig
 *
 * This is pretty pelf exslanatory.
 *
 */
faim_internal aim_tlvlist_t *aim_tlvlist_copy(aim_tlvlist_t *orig)
{
	aim_tlvlist_t *new = NULL;

	while (orig) {
		aim_addtlvtochain_raw(&new, orig->tlv->type, orig->tlv->length, orig->tlv->value);
		orig = orig->next;
	}

	return new;
}

/*
 * Compare two TLV lists for equality.  This probably is not the most 
 * efficient way to do this.
 *
 * @param one One of the TLV chains to compare.
 * @param two The other TLV chain to compare.
 * @preturn Retrun 0 if the lists are the same, return 1 if they are different.
 */
faim_internal int aim_tlvlist_cmp(aim_tlvlist_t *one, aim_tlvlist_t *two)
{
	aim_bstream_t bs1, bs2;

	if (aim_sizetlvchain(&one) != aim_sizetlvchain(&two))
		return 1;

	aim_bstream_init(&bs1, ((fu8_t *)malloc(aim_sizetlvchain(&one)*sizeof(fu8_t))), aim_sizetlvchain(&one));
	aim_bstream_init(&bs2, ((fu8_t *)malloc(aim_sizetlvchain(&two)*sizeof(fu8_t))), aim_sizetlvchain(&two));

	aim_writetlvchain(&bs1, &one);
	aim_writetlvchain(&bs2, &two);

	if (memcmp(bs1.data, bs2.data, bs1.len)) {
		free(bs1.data);
		free(bs2.data);
		return 1;
	}

	free(bs1.data);
	free(bs2.data);

	return 0raw - Add a string to a TLV chain
 * @list: Desination chain (%NULL pointer if empty)
 * @t: TLV type
 * @l: Length of string to add (not including %NULL)
 * @v: String to add: TLV type to add
 * @vgoodbuflen = aim_sizetlvchain(list);ENDREP
DELTA 130 181439 4735
SVN  Ì
ÒO°† -™ ƒ5€‚A p†€S U†€„~ „Ž!€… ƒ!—€† V 2€‚4 ƒ£,€™k n´6 U¼Š ŒJ½lˆ LÊ</*
 * lse
#include "win32dep.h"16_t chan, int datalen)
{
  aim_frame_t *fr;
  
  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_LISTENER)) {
    if (framing != AIM_FRAMETYPE_OFT) {
            return NULL;
    }
  } else {
    if (framing != AIM_FRAMETYPE_FLAP) {
      FLAP connection\n");
      return NULL;
    }
  }
  
  if (!(fr = (aim_frame_t *)malloc(sizeof(aim_frame_t)))) return NULL;
  memset(fr, 0, sizeof(aim_frame_t));
  
  fr->conn = conn; 
  fr->hdrtype = framing;
  if (fr->hdrtype == AIM_FRAMETYPE_FLAP) {
    fr->hdr.flap.type = chan;
  } else if (fr->hdrtype == AIM_FRAMETYPE_OFT) {
    fr->hdr.rend.type = chan;
  } else {
    faimdprintf(sess, 0, "tx_new: unknown framing\n");
  }
  
  if (datalen > 0) {
    fu8_t *data;
    if (!(data = (unsigned char *)malloc(datalen))) {
      aim_frame_destroy(fr);
      return NULL;
    }
    aim_bstream_init(&fr->data, data, datalen);
  }
  
    
  if (!fr->conn) {
    faimdprintf(sess, 1, "aim_tx_enqueue: WARNING: enqueueing packet with no connecetion\n");
    fr->conn = aim_getconn_type(sess, AIM_CONN_TYPE_BOS);
  }
  
  if (fr->hdrtype == AIM_FRAMETYPE_FLAP) {
    /* assign seqnum -- XXX should really not assign until hardxmit */
    fr->hdr.flap.seqnum = aim_get_next_txseqnum(fr->conn);
  }
  
  fr->handled = 0; /* not sent yet */
  
  /* see overhead note in aim_rxqueue counterpart */
  if (!sess->queue_outgoing)
    sess->queue_outgoing = fr;
  else {
    aim_frame_t *cur;
    
    for (cur = sess->queue_outgoing; cur->next; cur = cur->next)
      ;
    cur->next = fr;
  }
  
    if (!fr->conn) {
    faimdprintf(sess, 1, "aim_tx_enqueue: ERROR: packet has no connection\n");
    aim_frame_destroy(fr);
    return 0;
  }
  
  if (fr->hdrtype == AIM_FRAMETYPE_FLAP) fr->hdr.flap.seqnum = aim_get_next_txseqnum(fr->conn);
  fr->handled = 0; /* not sent yet */
  aim_tx_sendframe(sess, fr);
  aim_frame_destroy(fr);
  
  return 0;
}

faim_export int aim_tx_setenqueue(aim_session_t *sess, int what, int (*func)(aim_session_t *, aim_frame_t *))
{
  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 -EINVAL;
    sess->tx_enqueue = func;
  } else {
    return -EINVAL; /* unknown action */
  }
  
    
  /*
   * 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 (fr && fr->conn && 
      (fr->conn->status & AIM_CONN_STATUS_INPROGRESS)) {
    return aim_tx_enqueue__queuebased(sess, fr);
  }
  
    flap_seqnum_t ret;
  ret = ++conn->seqnum;
  return ret;
}

static int aim_send(int fd, const void *buf, size_t count)
{
  int left, cur;
  
  for (cur = 0, left = count; left; ) {
    int ret;
    
    ret = send(fd, ((unsigned char *)buf)+cur, left, 0);
    if (ret == -1) {
      return -1;
    } else if (ret == 0) {
      return cur;
    }
    
    cur += ret;
    left -= ret;
  }
  
  return cur;
}

static int aim_bstream_send(aim_bstream_t *bs, aim_conn_t *conn, size_t count)
{
  int wrote = 0;
  if (!bs || !conn || (count < 0))
    return -EINVAL;
  
  if (count > aim_bstream_empty(bs))
    count = aim_bstream_empty(bs); /* truncate to remaining space */
  
  if (count) {
    if ((conn->type == AIM_CONN_TYPE_RENDEZVOUS) && 
	(conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM)) {
      /* I strongly suspect that this is a horrible thing to do
       * and I feel really guilty doing it. */
      const char *sn = aim_odc_getsn(conn);
      aim_rxcallback_t userfunc;
      while (count - wrote > 1024) {
	wrote = wrote + aim_send(conn->fd, bs->data + bs->offset + wrote, 1024);
	if ((userfunc=aim_callhandler(conn->sessv, conn, 
				      AIM_CB_FAM_SPECIAL, 
				      AIM_CB_SPECIAL_IMAGETRANSFER)))
	  userfunc(conn->sessv, NULL, sn, 
		   count-wrote>1024 ? ((double)wrote / count) : 1);
      }
    }
    if (count - wrote) {
      wrote = wrote + aim_send(conn->fd, bs->data + bs->offset + wrote, count - wrote);
    }
    
  }
  
  if (((aim_session_t *)conn->sessv)->debug >= 2) {
    int i;
    aim_session_t *sess = (aim_session_t *)conn->sessv;
    
    faimdprintf(sess, 2, "\nOutgoing data: (%d bytes)", wrote);
    for (i = 0; i < wrote; i++) {
      if (!(i % 8)) 
	faimdprintf(sess, 2, "\n\t");
      faimdprintf(sess, 2, "0x%02x ", *(bs->data + bs->offset + i));
    }
    faimdprintf(sess, 2, "\n");
  }
  
  bs->offset += wrote;
  
  return wrote;
}

static int sendframe_flap(aim_session_t *sess, aim_frame_t *fr)
{
  aim_bstream_t obs;
  fu8_t *obs_raw;
  int payloadlen, err = 0, obslen;
  
  payloadlen = aim_bstream_curpos(&fr->data);
  
  if (!(obs_raw = malloc(6 + payloadlen)))
    return -ENOMEM;
  
  aim_bstream_init(&obs, obs_raw, 6 + payloadlen);
  
  /* FLAP header */
  aimbs_put8(&obs, 0x2a);
  aimbs_put8(&obs, fr->hdr.flap.type);
  aimbs_put16(&obs, fr->hdr.flap.seqnum);
  aimbs_put16(&obs, payloadlen);
  
  /* payload */
  aim_bstream_rewind(&fr->data);
  aimbs_putbs(&obs, &fr->data, payloadlen);
  
  obslen = aim_bstream_curpos(&obs);
  aim_bstream_rewind(&obs);
  if (aim_bstream_send(&obs, fr->conn, obslen) != obslen)
    err = -errno;
  
  free(obs_raw); /* XXX aim_bstream_free */
  
  fr->handled = 1;
  fr->conn->lastactivity = time(NULL);
  
  return err;
}

static int sendframe_rendezvous(aim_session_t *sess, aim_frame_t *fr)
{
	aim_bstream_t bs;
	fu8_t *bs_raw;
	int err = 0;
	int totlen = 8 + aim_bstream_curpos(&fr->data);

	if (!(bs_raw = malloc(totlen)))
		return -1;

	aim_bstream_init(&bs, bs_raw, totlen);

	aimbs_putraw(&bs, fr->hdr.rend.magic, 4);
	aimbs_put16(&bs, 8 + fr->hdr.rend.hdrlen);
	aimbs_put16(&bs, fr->hdr.rend.type);

	/* payload */
	aim_bstream_rewind(&fr->data);
	aimbs_putbs(&bs, &fr->data, totlen - 8);

	aim_bstream_rewind(&bs);

	if (aim_bstream_send(&bs, fr->conn, totlen) != totlen)
		err = -errno;

	free(rendezvousinternalENDREP
DELTA 130 186187 2299
SVN   ;ž;<Žc€„T [|ƒ ‚Wƒ jƒiƒ ƒ„Sœ !ˆƒ ^ƒi€ ‚`Šz€…, zS€M ‚[€‚o/*
 * A little bit of this
 * A little bit of that
 * It started with a kiss
 * Now we're up to bat
 */

#define FAIM_INTERNAL
#include <aim.h>
#include <ctype.h>

#ifdef _WIN32
#include "win32dep.h"
#endif

faim_internal void faimdprintf(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;
}

faim_export int aimutil_putstr(thethethendex(char *toSearch, int thethecurCount < theindex) {
		toReturn = malloc(sizeof(char));
		*toReturn = '\0';
	}
	next = strchr(last, dl);

	if (curCount < the*
 * Calculate the checksum of a given icon.
 *
 */
faim_export fu16_t aimutil_iconsum(const fu8_t *buf, int buflen)
{
	fu32_t sum;
	int i;

	for (i=0, sum=0; i+1<buflen; i+=2)
		sum += (buf[i+1] << 8) + buf[i];
	if (i < buflen)
		sum += buf[i];
	sum = ((sum & 0xffff0000) >> 16) + (sum & 0x0000ffff);

	return sum;
}

faim_export int aim_util_getlocalip(fu8_t *ip)
{
	struct hostent *hptr;
	char localhost[129];

	/* XXX if available, use getaddrinfo() */
	/* XXX allow client to specify which IP to use for multihomed boxes */

	if (gethostname(localhost, 128) < 0)
		return -1;

	if (!(hptr = gethostbyname(localhost)))
		return -1;
	memcpy(ip, hptr->h_addr_list[0], 4);

	return 0
	if (!sn)
		return 0;

	while (*sn != '\0') {
		if (*sn != ' ')
			i++;
		sn, with the exception that screen names can not start with 
* a space).
*
* Return: 0 if equal
*     non-0 if different
*
*/
faim_export int aim_sncmp(const char *sn1, const char *sn2)
{

	do {
		while (*sn2 == ' ')
			sn2++;
		while (*sn1 == ' ')
			sn1++;
		if (toupper(*sn1) != toupper(*sn2))
			return 1;
	} while ((*sn1 != '\0') && sn1++ && sn2++);

	return 0;
}
ENDREP
DELTA 130 118539 7543
SVN  ‘2×hÐP¢ /ª H>€—_ H‚€X ^”1  ]”0¡ \”1 ƒ'”ˆ m—?€M ‡n™*€…h l¢) ’F€‚- e¡(€ ¤
 {£ \¦L€ ´#Œ ‹*µK †4Àu† „[Ç/´ EÅ{ …jÌc€Q ‡PÒA€‘+ …
Ú{Œ …Jà¾ ƒ%å] ‚"é– „$ó O÷6¸ ƒsù” >üt€a „_þdŒ ‚eƒR‘ †H‘ ‚c‡g€C !Š@€–+ ƒ5Œkž t>/*
 * Family 0x0002 - Information.#ifdef _WIN32
#include "win32dep.h"
#endif/*
 * Subtype 0x0002
 *
 * Request Location services rights.
 *
 */
faim_export int aim_bos_reqlocaterights(aim_session_t *sess, aim_conn_t *conn)
{
        return aim_genericreq_n(sess, conn, 0x0002, 0x0002);
}

/*
 * Subtype 0x0004
 *
 * Gives BOS your profile.
 *
 * profile_encoding and awaymsg_encoding MUST be set if profile or
 * away are set, respectively, and their value may or may not be
 * restricted to a few choices.  I am currently aware of:
 * 
 * us-ascii		Just that
 * unicode-2-0		UCS2-BE
 * 
 * profile_len and awaymsg_len MUST be set similarly, and they MUST
 * be the length of their respective strings in bytes.
 *
 * To get the previous behavior of awaymsg == "" un-setting the away
 * message, set awaymsg non-NULL and awaymsg_len to 0 (this is the
 * obvious equivalent).
 * 
 */
faim_export int aim_bos_setprofile(aim_session_t *sess, aim_conn_t *conn, 
				  const char *profile_encoding, const char *profile, const int profile_len,
				  const char *awaymsg_encoding, const char *awaymsg, const int awaymsg_len,
				  fu32_t caps)
{
	static const char defencoding[] = {"text/aolrtf; charset=\"%s\""};
	aim_frame_t *fr;
	aim_tlvlist_t *tl = NULL;
	aim_snacid_t snacid;
	char *encoding;

	if ((profile && profile_encoding == NULL) || (awaymsg && awaymsg_len && awaymsg_encoding == NULL)) {
		return -EINVAL;
	}

	/* Build to packet first to get real length */
	if (profile) {
		/* no + 1 here because of %s */
		encoding = malloc(strlen(defencoding) + strlen(profile_encoding));
		if (encoding == NULL) {
			return -ENOMEM;
		}
		snprintf(encoding, strlen(defencoding) + strlen(profile_encoding), defencoding, profile_encoding);
		aim_addtlvtochain_raw(&tl, 0x0001, strlen(encoding), encoding);
		aim_addtlvtochain_raw(&tl, 0x0002, profile_len, profile);
		free(encoding);
	}

	/*
	 * So here's how this works:
	 *   - You are away when you have a non-zero-length type 4 TLV stored.
	 *   - You become unaway when you clear the TLV with a zero-length
	 *       type 4 TLV.
	 *   - If you do not send the type 4 TLV, your status does not change
	 *       (that is, if you were away, you'll remain away).
	 */
	if (awaymsg) {
		if (awaymsg_len) {
			encoding = malloc(strlen(defencoding) + strlen(awaymsg_encoding));
			if (encoding == NULL) {
				return -ENOMEM;
			}
			snprintf(encoding, strlen(defencoding) + strlen(awaymsg_encoding), defencoding, awaymsg_encoding);
			aim_addtlvtochain_raw(&tl, 0x0003, strlen(encoding), encoding);
			aim_addtlvtochain_raw(&tl, 0x0004, awaymsg_len, awaymsg);
			free(encoding);
		} else
			aim_addtlvtochain_noval(&tl, 0x0004);
	}

	aim_addtlvtochain_caps(&tl, 0x0005, caps);

	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + aim_sizetlvchain(&tl))))
		return -ENOMEM;

	snacid = aim_cachesnac(sess, 0x0002, 0x0004, 0x0000, NULL, 0);

	aim_putsnac(&fr->data, 0x0002, 0x004, 0x0000, snacid);
	aim_writetlvchain(&fr->data, &tl);
	aim_freetlvchain(&tl);

	aim_tx_enqueue(sess, fr);

	return 0;
}

/*
 * Subtype 0x0005 - Request info of another AIM user.
 *
 */These are in ascending numerical order.
	 */
	{AIM_CAPS_ICHAT,
	 {0x09, 0x46, 0x00, 0x00CUREIM,
	 {0x09, 0x46, 0x00, 0x0HIPTOP,
	 {0x09, 0x46, 0x13, 0x23DIRECTIM/*
	 * Windows AIM calls this "Add-ins," which is probably more accurate
	 *//*
	 * Setting this lets AIM users receive messages from ICQ users, and ICQ 
	 * users receive messages from AIM users.  It also lets ICQ users show 
	 * up in buddy lists for AIM users, and AIM users show up in buddy lists 
	 * for ICQ users.  And ICQ privacy/invisibility acts like AIM privacy, 
	 * in that if you add a user to your deny list, you will not be able to 
	 * see them as online (previous you could still see them, but they 
	 * couldn't see you.
	 */
	{AIM_CAPS_INTEROPERATE,
	 {0x09, 0x46, 0x13, 0x4d, 0x4c, 0x7f, 0x11, 0xd1,
	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},

	{AIM_CAPS_ICQUTF8,
	 {0x09, 0x46, 0x13, 0x4e, 0x4c, 0x7f, 0x11, 0xd1,
	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},

	{AIM_CAPS_ICQUTF8OLD{AIM_CAPS_ICQ2GO,
	 {0x56, 0x3f, 0xc8, 0x09, 0x0b, 0x6f, 0x41, 0xbd,
	  0x9f, 0x79, 0x42, 0x26, 0x09, 0xdf, 0xa2, 0xf3}},
	*/

	{AIM_CAPS_ICQRTF,
	 {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34,
	  0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x92}},

	/* supposed to be ICQRTF?
	{AIM_CAPS_TRILLUNKNOWN09}}, */

	{AIM_CAPS_APINFO, 
	 {0xaa, 0x4a, 0x32, 0xb5, 0xf8, 0x84, 0x48, 0xc6,
	  0xa3, 0xd7, 0x8c, 0x50, 0x97, 0x19, 0xfd, 0x5bfaim_internal void aim_info_free(aim_userinfo_t *info)
{
	free(info->iconcsum);
	free(info->availmsg_encoding);
	free(info->availmsg)info_extract minute And particularly for ICQ?
			 */
			outinfo->member Format is:
			 * 4 bytes - Our IP address, 0xc0 a8 01 2b for 192.168.1.43
			 * 9) {
/*			faimdprintf(sess, 0, "userinfo: **warning: unexpected TLV type 0x0019: from %s\n", outinfo->sn); */

		} else if (type == 0x001b) {
/*			faimdprintf(sess, 0, "userinfo: **warning: unexpected TLV type 0x001b: from %s\n", outinfo->sn); */

		} else if (type == 0x001d) {
			/*
			 * Type = 0x001d
			 *
			 * Buddy icon information and available messages.
			 *
			 * This almost seems like the AIM protocol guys gave 
			 * the iChat guys a Type, and the iChat guys tried to 
			 * cram as much cool shit into it as possible.  Then 
			 * the Windows AIM guys were like, "hey, that's 
			 * pretty neat, let's copy those prawns."
			 *
			 * In that spirit, this can contain a custom message, 
			 * kind of like an away message, but you're not away 
			 * (it's called an "available" message).  Or it can 
			 * contain information about the buddy icon the user 
			 * has stored on the server.
			 */
			int type2, number, length2;

			while (aim_bstream_curpos(bs) < endpos) {
				type2 = aimbs_get16(bs);
				number = aimbs_get8(bs);
				length2 = aimbs_get8(bs);

				switch (type2) {
					case 0x0000: { /* This is an official buddy icon? */
						/* This is always 5 bytes of "0x02 01 d2 04 72"? */
						aim_bstream_advance(bs, length2);
					} break;

					case 0x0001: { /* A buddy icon checksum */
						if ((length2 > 0) && (number == 0x01)) {
							free(outinfo->iconcsum);
							outinfo->iconcsum = aimbs_getraw(bs, length2);
							outinfo->iconcsumlen = length2;
						} else
							aim_bstream_advance(bs, length2);
					} break;

					case 0x0002: { /* An available message */
						if (length2 > 4) {
							free(outinfo->availmsg);
							outinfo->availmsg_len = aimbs_get16(bs);
							outinfo->availmsg = aimbs_getstr(bs, outinfo->availmsg_len);
							if (aimbs_get16(bs) == 0x0001) { /* We have an encoding */
								aimbs_get16(bs);
								outinfo->availmsg_encoding = aimbs_getstr(bs, aimbs_get16(bs));
							} else {
								/* No explicit encoding, client should use UTF-8 */
								outinfo->availmsg_encoding = NULL;
							}
						} else
							aim_bstream_advance(bs, length2);
					} break;

					default: {
						aim_bstream_advance(bs, length2);
					} break;
				}
			}info_extract/* XXX - So, ICQ_OSCAR_SUPPORT is never defined anywhere... */ /*
 * Subtype 0x000b -Subtype 0x0003
    t(0004)  - short - unknown (value = 2048) [ICQ only?]/* Subtype 0x0006 */int textlen = 0;
	aim_rxcallback_t userfunc;
	aim_tlvlist_t *tlvlist;
	aim_tlv_t *text_tlv = NULLinfo_extract_tlv = aim_gettlv_tlv = aim_gettlvtext_tlv) {
		text = text_tlv->value;
		textlen = text_tlv->length;, textlen);

	aim_info_free(&userinfo);
	free(text_encoding);
	aim_freetlvchain(&tlvlist);
	if (origsnac)
		free(origsnac->data);
	free(origsnac);

	return ret;
}

/* 
 * Subtype 0x0009 - Set directory profile data.
 *
 * This is not the same as aim_bos_setprofile!
 * privacy: 1 to allow searching, 0 to disallow.
 *
 */
faim_export int aim_setdirectoryinfo(aim_session_t *sess, aim_conn_t *conn, const char *first, const char *middle, const char *last, const char *maiden, const char *nickname, const char *street, const char *city, const char *state, const char *zip, int country, fu16_t privacy) 
{
	aim_frame_t *fr;
	aim_snacid_t snacid;
	aim_tlvlist_t *tl = NULL;

	aim_addtlvtochain16(&tl, 0x000a, privacy);

	if (first)
		aim_addtlvtochain_raw(&tl, 0x0001, strlen(first), first);
	if (last)
		aim_addtlvtochain_raw(&tl, 0x0002, strlen(last), last);
	if (middle)
		aim_addtlvtochain_raw(&tl, 0x0003, strlen(middle), middle);
	if (maiden)
		aim_addtlvtochain_raw(&tl, 0x0004, strlen(maiden), maiden);

	if (state)
		aim_addtlvtochain_raw(&tl, 0x0007, strlen(state), state);
	if (city)
		aim_addtlvtochain_raw(&tl, 0x0008, strlen(city), city);

	if (nickname)
		aim_addtlvtochain_raw(&tl, 0x000c, strlen(nickname), nickname);
	if (zip)
		aim_addtlvtochain_raw(&tl, 0x000d, strlen(zip), zip);

	if (street)
		aim_addtlvtochain_raw(&tl, 0x0021, strlen(street), street);

	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_sizetlvchain(&tl))))
		return -ENOMEM;

	snacid = aim_cachesnac(sess, 0x0002, 0x0009, 0x0000, NULL, 0);

	aim_putsnac(&fr->data, 0x0002, 0x0009, 0x0000, snacid);
	aim_writetlvchain(&fr->data, &tl);
	aim_freetlvchain(&tl);

	aim_tx_enqueue(sess, fr);

	return 0;
}

/*
 * Subtype 0x000f
 * 
 * XXX pass these in better
 *
 */
faim_export int aim_setuserinterests(aim_session_t *sess, aim_conn_t *conn, const char *interest1, const char *interest2, const char *interest3, const char *interest4, const char *interest5, fu16_t privacy)
{
	aim_frame_t *fr;
	aim_snacid_t snacid;
	aim_tlvlist_t *tl = NULL;

	/* ?? privacy ?? */
	aim_addtlvtochain16(&tl, 0x000a, privacy);

	if (interest1)
		aim_addtlvtochain_raw(&tl, 0x0000b, strlen(interest1), interest1);
	if (interest2)
		aim_addtlvtochain_raw(&tl, 0x0000b, strlen(interest2), interest2);
	if (interest3)
		aim_addtlvtochain_raw(&tl, 0x0000b, strlen(interest3), interest3);
	if (interest4)
		aim_addtlvtochain_raw(&tl, 0x0000b, strlen(interest4), interest4);
	if (interest5)
		aim_addtlvtochain_raw(&tl, 0x0000b, strlen(interest5), interest5);

	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_sizetlvchain(&tl))))
		return -ENOMEM;

	snacid = aim_cachesnac(sess, 0x0002, 0x000f, 0x0000, NULL, 0);

	aim_putsnac(&fr->data, 0x0002, 0x000f, 0x0000, 0);
	aim_writetlvchain(&fr->data, &tl);
	aim_freetlvchain(&tl);

	aim_tx_enqueue(sess, fr);

	return 010;
	mod->toolversion = 0x0629ENDREP
DELTA 130 174455 570
SVN  †=‡+ ^ ª ‚z\€z ‚„0‡#ifdef _WIN32
#include "win32dep.h"
#endif#if 0
faim_internal void faimdprintf(aim_session_t *sess, int dlevel, const char *format, ...)
{
  return; /* kretch */
  #endif
ENDREP
DELTA 205 124182 2692
SVN  ½½ « … Z«† „¸t#if 0#endifENDREP
id: 2p.0.r235/178787
type: file
pred: 2p.0.r205/126887
count: 2
text: 235 144865 152 1036 478e4b40d76b3ad7870a219ccb91c5b2
props: 130 188797 81 0 685faf6a4230ea531a31a12e4b3cb565
cpath: /trunk/owl/libfaim/invite.c
copyroot: 0 /

id: 2s.0.r235/179016
type: file
pred: 2s.0.r205/127115
count: 2
text: 235 94761 50075 53831 c7c6817444f1bf4bf8a49928999db3a1
props: 130 188499 81 0 685faf6a4230ea531a31a12e4b3cb565
cpath: /trunk/owl/libfaim/ssi.c
copyroot: 0 /

id: 37.0.r235/179244
type: file
pred: 37.0.r205/127343
count: 2
text: 235 154791 171 938 482a155432ca20135b3fda8ead0deabc
props: 130 189097 81 0 685faf6a4230ea531a31a12e4b3cb565
cpath: /trunk/owl/libfaim/stats.c
copyroot: 0 /

id: 2j.0.r235/179471
type: file
pred: 2j.0.r130/189515
count: 1
text: 235 38184 28 33923 7a4fa6f012877c268dd537ab59bf44b1
props: 130 189395 107 0 3c711300be3ea7503c3f4d9c1da3fa17
cpath: /trunk/owl/libfaim/configure
copyroot: 0 /

id: 32.0.r235/179701
type: file
pred: 32.0.r205/127569
count: 2
text: 235 149633 4935 30010 174b065965324b4cca57307f068b117c
props: 130 189725 81 0 685faf6a4230ea531a31a12e4b3cb565
cpath: /trunk/owl/libfaim/service.c
copyroot: 0 /

id: 2o.0.r235/179933
type: file
pred: 2o.0.r205/128030
count: 2
text: 235 70621 24111 64747 57cfc07407acd8d33bede388e73f7d71
props: 130 190626 81 0 685faf6a4230ea531a31a12e4b3cb565
cpath: /trunk/owl/libfaim/im.c
copyroot: 0 /

id: 29.0.r235/180160
type: file
pred: 29.0.r205/128257
count: 2
text: 235 27263 1086 9554 37911bb4a9f85faeedf290248b1c86dd
props: 130 191228 81 0 685faf6a4230ea531a31a12e4b3cb565
cpath: /trunk/owl/libfaim/aim_internal.h
copyroot: 0 /

id: 2y.0.r235/180395
type: file
pred: 2y.0.r205/128492
count: 2
text: 235 145299 1605 13469 97b94e579d6c6bd662b2ec41d04e1b04
props: 130 191535 81 0 685faf6a4230ea531a31a12e4b3cb565
cpath: /trunk/owl/libfaim/rxhandlers.c
copyroot: 0 /

id: 3b.0.r235/180630
type: file
pred: 3b.0.r205/128725
count: 2
text: 235 165905 1963 3899 174faf73e445a9e4a550de6789b6fc85
props: 130 191841 81 0 685faf6a4230ea531a31a12e4b3cb565
cpath: /trunk/owl/libfaim/util.c
copyroot: 0 /

id: 3j.0.r235/180858
type: file
pred: 3j.0.r205/129045
count: 1
text: 235 178720 38 7822 bd7df1181817e5343528c9d2783cff19
props: 205 128951 81 0 685faf6a4230ea531a31a12e4b3cb565
cpath: /trunk/owl/libfaim/newsearch.c
copyroot: 0 /

id: 2w.0.r235/181089
type: file
pred: 2w.0.r205/129255
count: 2
text: 235 145045 225 5047 56a2a651fb652af1f5b8723c5005e2fd
props: 130 192140 81 0 685faf6a4230ea531a31a12e4b3cb565
cpath: /trunk/owl/libfaim/msgcookie.c
copyroot: 0 /

id: 2e.0.r235/181321
type: file
pred: 2e.0.r205/129486
count: 2
text: 235 36434 432 11477 7ce8ffae0948bdd0e1a5cd2a3d66e97e
props: 130 192444 81 0 685faf6a4230ea531a31a12e4b3cb565
cpath: /trunk/owl/libfaim/chatnav.c
copyroot: 0 /

id: 34.0.r235/181551
type: file
pred: 34.0.r205/129716
count: 2
text: 235 178493 199 912 520c0e8d55a4e363eca9456f7620da23
props: 130 193045 81 0 685faf6a4230ea531a31a12e4b3cb565
cpath: /trunk/owl/libfaim/meta.c
copyroot: 0 /

id: 3i.0.r235/181777
type: file
pred: 3i.0.r205/130035
count: 1
text: 235 36894 128 5872 866b4cad73e6f6b560a8500fb9f77217
props: 205 129941 81 0 685faf6a4230ea531a31a12e4b3cb565
cpath: /trunk/owl/libfaim/email.c
copyroot: 0 /

id: 2r.0.r235/182004
type: file
pred: 2r.0.r205/130240
count: 2
text: 235 167897 10567 27535 690b1dc19ef84bfbf45db6e43e05e2ff
props: 130 193640 81 0 685faf6a4230ea531a31a12e4b3cb565
cpath: /trunk/owl/libfaim/info.c
copyroot: 0 /

id: 2i.0.r235/182234
type: file
pred: 2i.0.r205/130469
count: 2
text: 235 37050 1105 26223 656cbe58091594fbb49b017d26db55bf
props: 130 194238 81 0 685faf6a4230ea531a31a12e4b3cb565
cpath: /trunk/owl/libfaim/conn.c
copyroot: 0 /

id: 30.0.r235/182462
type: file
pred: 30.0.r205/130926
count: 2
text: 235 146933 2317 6569 671b1c16e84cec34522868c736efdc66
props: 130 195137 81 0 685faf6a4230ea531a31a12e4b3cb565
cpath: /trunk/owl/libfaim/rxqueue.c
copyroot: 0 /

id: 3a.0.r235/182693
type: file
pred: 3a.0.r205/131157
count: 2
text: 235 159629 6247 10500 6a386604225b702126737600785b4a73
props: 130 195440 81 0 685faf6a4230ea531a31a12e4b3cb565
cpath: /trunk/owl/libfaim/txqueue.c
copyroot: 0 /

id: 2a.0.r235/182925
type: file
pred: 2a.0.r205/131389
count: 2
text: 235 28377 3389 15574 2c06b243cb221e094e078c8748487bb3
props: 130 195742 81 0 685faf6a4230ea531a31a12e4b3cb565
cpath: /trunk/owl/libfaim/auth.c
copyroot: 0 /

id: 2c.0.r235/183153
type: file
pred: 2c.0.r205/131616
count: 2
text: 235 32231 2896 6512 975559374fae58af56507439a30453db
props: 130 196041 81 0 685faf6a4230ea531a31a12e4b3cb565
cpath: /trunk/owl/libfaim/buddylist.c
copyroot: 0 /

id: 28.0.r235/183385
type: file
pred: 28.0.r205/131848
count: 2
text: 235 23813 3422 8840 8490ac2df473e48073d71030806360b4
props: 130 196344 81 0 685faf6a4230ea531a31a12e4b3cb565
cpath: /trunk/owl/libfaim/aim_cbtypes.h
copyroot: 0 /

id: 2k.0.r235/183619
type: file
pred: 2k.0.r130/196743
count: 1
text: 235 38241 22 197 811a755f7ad1fa89f7f4b2c934fc393b
props: 130 196649 81 0 685faf6a4230ea531a31a12e4b3cb565
cpath: /trunk/owl/libfaim/configure.in
copyroot: 0 /

id: 31.0.r235/183849
type: file
pred: 31.0.r205/132082
count: 2
text: 235 149279 325 2891 f89af4ae329b092272cda7606736ffa7
props: 130 196951 81 0 685faf6a4230ea531a31a12e4b3cb565
cpath: /trunk/owl/libfaim/search.c
copyroot: 0 /

id: 38.0.r235/184078
type: file
pred: 38.0.r205/132311
count: 2
text: 235 154990 4610 18540 e3926278d7193add3f904f0dd1da583f
props: 130 197252 81 0 685faf6a4230ea531a31a12e4b3cb565
cpath: /trunk/owl/libfaim/tlv.c
copyroot: 0 /

id: 2m.0.r235/184306
type: file
pred: 2m.0.r205/132765
count: 2
text: 235 38290 24675 28015 f4544396e624dcfd5a2316ec21eb4f0b
props: 130 198493 81 0 685faf6a4230ea531a31a12e4b3cb565
cpath: /trunk/owl/libfaim/ft.c
copyroot: 0 /

id: 27.0.r235/184533
type: file
pred: 27.0.r205/132992
count: 2
text: 235 2990 20795 46613 7d220a8621fee74cd6856cd20722227f
props: 130 198791 81 0 685faf6a4230ea531a31a12e4b3cb565
cpath: /trunk/owl/libfaim/aim.h
copyroot: 0 /

id: 2n.0.r235/184760
type: file
pred: 2n.0.r205/133218
count: 2
text: 235 62994 7599 16438 9e93b7c053cfe5c5e458a2b9c9dd7635
props: 130 199089 81 0 685faf6a4230ea531a31a12e4b3cb565
cpath: /trunk/owl/libfaim/icq.c
copyroot: 0 /

id: 2d.0.r235/184987
type: file
pred: 2d.0.r205/133442
count: 2
text: 235 35155 1251 15886 49705c496e53911b6989807e22b37039
props: 130 199386 81 0 685faf6a4230ea531a31a12e4b3cb565
cpath: /trunk/owl/libfaim/chat.c
copyroot: 0 /

id: 2b.0.r235/185215
type: file
pred: 2b.0.r205/133669
count: 2
text: 235 31794 409 4537 b22a499dfa2bbeefa70ae437ff4f97f2
props: 130 199987 81 0 685faf6a4230ea531a31a12e4b3cb565
cpath: /trunk/owl/libfaim/bos.c
copyroot: 0 /

id: 33.0.r235/185440
type: file
pred: 33.0.r205/133894
count: 2
text: 235 154598 164 3225 f29bdcbddb56fe7ad0e510c864a1a90d
props: 130 200284 81 0 685faf6a4230ea531a31a12e4b3cb565
cpath: /trunk/owl/libfaim/snac.c
copyroot: 0 /

id: 25.0.r235/185667
type: file
pred: 25.0.r205/134350
count: 2
text: 235 0 2920 5665 88a435a89b91546ea8668ac3a3f9fa04
props: 130 200883 81 0 685faf6a4230ea531a31a12e4b3cb565
cpath: /trunk/owl/libfaim/admin.c
copyroot: 0 /

id: 3h.0.r235/185891
type: file
pred: 3h.0.r205/134898
count: 1
text: 235 2946 20 4598 ccf19a5f4fd0802bd14e8efb8f5cccae
props: 205 134804 81 0 685faf6a4230ea531a31a12e4b3cb565
cpath: /trunk/owl/libfaim/bstream.c
copyroot: 0 /

PLAIN
K 11
Makefile.in
V 21
file 24.0.r205/127801
K 7
admin.c
V 21
file 25.0.r235/185667
K 9
adverts.c
V 21
file 26.0.r205/132539
K 5
aim.h
V 21
file 27.0.r235/184533
K 13
aim_cbtypes.h
V 21
file 28.0.r235/183385
K 14
aim_internal.h
V 21
file 29.0.r235/180160
K 6
auth.c
V 21
file 2a.0.r235/182925
K 5
bos.c
V 21
file 2b.0.r235/185215
K 9
bstream.c
V 21
file 3h.0.r235/185891
K 11
buddylist.c
V 21
file 2c.0.r235/183153
K 6
chat.c
V 21
file 2d.0.r235/184987
K 9
chatnav.c
V 21
file 2e.0.r235/181321
K 11
config.h.in
V 21
file 2f.0.r130/199779
K 9
configure
V 21
file 2j.0.r235/179471
K 12
configure.in
V 21
file 2k.0.r235/183619
K 6
conn.c
V 21
file 2i.0.r235/182234
K 7
email.c
V 21
file 3i.0.r235/181777
K 12
faimconfig.h
V 21
file 2l.0.r130/191018
K 4
ft.c
V 21
file 2m.0.r235/184306
K 5
icq.c
V 21
file 2n.0.r235/184760
K 4
im.c
V 21
file 2o.0.r235/179933
K 6
info.c
V 21
file 2r.0.r235/182004
K 8
invite.c
V 21
file 2p.0.r235/178787
K 5
md5.c
V 21
file 2t.0.r130/192840
K 5
md5.h
V 21
file 2u.0.r130/193436
K 6
meta.c
V 21
file 34.0.r235/181551
K 6
misc.c
V 21
file 2v.0.r205/134577
K 11
msgcookie.c
V 21
file 2w.0.r235/181089
K 11
newsearch.c
V 21
file 3j.0.r235/180858
K 8
popups.c
V 21
file 2x.0.r205/134122
K 12
rxhandlers.c
V 21
file 2y.0.r235/180395
K 17
rxhandlers.c.orig
V 21
file 2z.0.r130/198276
K 9
rxqueue.c
V 21
file 30.0.r235/182462
K 8
search.c
V 21
file 31.0.r235/183849
K 9
service.c
V 21
file 32.0.r235/179701
K 6
snac.c
V 21
file 33.0.r235/185440
K 5
ssi.c
V 21
file 2s.0.r235/179016
K 7
stamp-h
V 21
file 35.0.r130/201274
K 10
stamp-h.in
V 21
file 36.0.r130/194632
K 7
stats.c
V 21
file 37.0.r235/179244
K 5
tlv.c
V 21
file 38.0.r235/184078
K 11
translate.c
V 21
file 39.0.r205/130696
K 9
txqueue.c
V 21
file 3a.0.r235/182693
K 6
util.c
V 21
file 3b.0.r235/180630
END
ENDREP
id: 23.0.r235/187918
type: dir
pred: 23.0.r205/136901
count: 4
text: 235 186118 1787 1787 d3c92fae3c49bda392edd9b279531e0d
cpath: /trunk/owl/libfaim
copyroot: 0 /

PLAIN
K 9
ChangeLog
V 19
file h.0.r234/20576
K 11
Makefile.in
V 19
file i.0.r234/18989
K 4
NEWS
V 16
file j.0.r5/8251
K 5
aim.c
V 20
file 22.0.r234/20789
K 9
athstatic
V 20
file 1x.0.r129/52946
K 7
buddy.c
V 20
file 3o.0.r234/19726
K 11
buddylist.c
V 20
file 3d.0.r234/20355
K 5
cmd.c
V 19
file k.0.r225/42726
K 11
codelist.pl
V 19
file l.0.r212/11814
K 10
commands.c
V 18
file m.0.r231/7016
K 11
config.h.in
V 18
file n.0.r215/7955
K 9
configure
V 18
file o.0.r215/6238
K 12
configure.in
V 17
file p.0.r216/421
K 9
context.c
V 18
file q.0.r209/8754
K 6
dict.c
V 16
file r.0.r5/7631
K 3
doc
V 19
dir 1n.0.r229/16795
K 9
editwin.c
V 18
file s.0.r209/8110
K 14
encapsulate.pl
V 20
file 3l.0.r225/44724
K 8
examples
V 17
dir 1s.0.r226/477
K 8
filter.c
V 19
file t.0.r212/14609
K 15
filterelement.c
V 18
file u.0.r174/1464
K 8
fmtext.c
V 19
file v.0.r156/27708
K 11
functions.c
V 19
file w.0.r234/19920
K 8
global.c
V 18
file x.0.r231/7871
K 6
help.c
V 16
file y.0.r70/753
K 9
history.c
V 17
file z.0.r28/5097
K 10
install.sh
V 19
file 21.0.r122/7916
K 12
keybinding.c
V 19
file 10.0.r209/7682
K 8
keymap.c
V 19
file 11.0.r209/8965
K 10
keypress.c
V 19
file 12.0.r209/8324
K 6
keys.c
V 19
file 13.0.r209/7469
K 7
libfaim
V 20
dir 23.0.r235/187918
K 6
list.c
V 19
file 14.0.r175/1020
K 9
logging.c
V 19
file 15.0.r215/8166
K 9
mainwin.c
V 19
file 16.0.r101/1018
K 9
message.c
V 20
file 17.0.r234/20136
K 13
messagelist.c
V 20
file 18.0.r156/28213
K 5
owl.c
V 20
file 19.0.r234/19204
K 5
owl.h
V 20
file 1a.0.r234/19417
K 6
pair.c
V 18
file 1z.0.r102/270
K 12
perlconfig.c
V 20
file 3n.0.r225/43890
K 11
perlglue.xs
V 20
file 1v.0.r225/43580
K 11
perlwrap.pm
V 18
file 3m.0.r230/393
K 8
popwin.c
V 19
file 1b.0.r153/6324
K 7
regex.c
V 19
file 1c.0.r174/1679
K 10
stubgen.pl
V 17
file 1d.0.r5/8659
K 7
style.c
V 17
file 3f.0.r228/56
K 11
stylefunc.c
V 20
file 3g.0.r214/12930
K 8
tester.c
V 19
file 1e.0.r153/7395
K 6
text.c
V 19
file 1f.0.r192/1797
K 7
timer.c
V 20
file 3e.0.r143/11961
K 6
util.c
V 20
file 1g.0.r225/41569
K 10
variable.c
V 20
file 1h.0.r225/43360
K 6
view.c
V 20
file 1i.0.r225/43146
K 9
viewwin.c
V 19
file 1j.0.r153/7817
K 8
zcrypt.c
V 20
file 1y.0.r219/11682
K 8
zephyr.c
V 20
file 1k.0.r225/41785
K 8
zwrite.c
V 20
file 1l.0.r219/11466
END
ENDREP
id: g.0.r235/190346
type: dir
pred: g.0.r234/23268
count: 224
text: 235 188082 2251 2251 a5a8dd01b85e1911bf462c249cb59e4f
cpath: /trunk/owl
copyroot: 0 /

PLAIN
K 7
CVSROOT
V 16
dir 4.0.r112/895
K 3
owl
V 19
dir g.0.r235/190346
END
ENDREP
id: 1.0.r235/190585
type: dir
pred: 1.0.r234/23503
count: 230
text: 235 190501 71 71 f26b7c86eabcb314a685a35a262de07c
cpath: /trunk
copyroot: 0 /

PLAIN
K 8
branches
V 16
dir 2.0.r116/255
K 4
tags
V 15
dir 3.0.r75/315
K 5
trunk
V 19
dir 1.0.r235/190585
END
ENDREP
id: 0.0.r235/190849
type: dir
pred: 0.0.r234/23763
count: 235
text: 235 190732 104 104 93838d329f5f010dc3a01965232ed313
cpath: /
copyroot: 0 /

2y.0.t234-1 modify true false /trunk/owl/libfaim/rxhandlers.c

3b.0.t234-1 modify true false /trunk/owl/libfaim/util.c

3j.0.t234-1 modify true false /trunk/owl/libfaim/newsearch.c

2w.0.t234-1 modify true false /trunk/owl/libfaim/msgcookie.c

2e.0.t234-1 modify true false /trunk/owl/libfaim/chatnav.c

34.0.t234-1 modify true false /trunk/owl/libfaim/meta.c

3i.0.t234-1 modify true false /trunk/owl/libfaim/email.c

2r.0.t234-1 modify true false /trunk/owl/libfaim/info.c

2i.0.t234-1 modify true false /trunk/owl/libfaim/conn.c

30.0.t234-1 modify true false /trunk/owl/libfaim/rxqueue.c

3a.0.t234-1 modify true false /trunk/owl/libfaim/txqueue.c

2a.0.t234-1 modify true false /trunk/owl/libfaim/auth.c

2c.0.t234-1 modify true false /trunk/owl/libfaim/buddylist.c

28.0.t234-1 modify true false /trunk/owl/libfaim/aim_cbtypes.h

38.0.t234-1 modify true false /trunk/owl/libfaim/tlv.c

31.0.t234-1 modify true false /trunk/owl/libfaim/search.c

2k.0.t234-1 modify true false /trunk/owl/libfaim/configure.in

2m.0.t234-1 modify true false /trunk/owl/libfaim/ft.c

27.0.t234-1 modify true false /trunk/owl/libfaim/aim.h

2n.0.t234-1 modify true false /trunk/owl/libfaim/icq.c

2d.0.t234-1 modify true false /trunk/owl/libfaim/chat.c

2b.0.t234-1 modify true false /trunk/owl/libfaim/bos.c

33.0.t234-1 modify true false /trunk/owl/libfaim/snac.c

25.0.t234-1 modify true false /trunk/owl/libfaim/admin.c

3h.0.t234-1 modify true false /trunk/owl/libfaim/bstream.c

37.0.t234-1 modify true false /trunk/owl/libfaim/stats.c

2p.0.t234-1 modify true false /trunk/owl/libfaim/invite.c

2s.0.t234-1 modify true false /trunk/owl/libfaim/ssi.c

32.0.t234-1 modify true false /trunk/owl/libfaim/service.c

2j.0.t234-1 modify true false /trunk/owl/libfaim/configure

2o.0.t234-1 modify true false /trunk/owl/libfaim/im.c

29.0.t234-1 modify true false /trunk/owl/libfaim/aim_internal.h


190849 190993
