aboutsummaryrefslogtreecommitdiffstats
path: root/protocols/oscar/im.h
blob: 42a8a6b173df3645869b720cf8b496289fd62848 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
#ifndef __OSCAR_IM_H__
#define __OSCAR_IM_H__

#define AIM_CB_FAM_MSG 0x0004

/*
 * SNAC Family: Messaging Services.
 */ 
#define AIM_CB_MSG_ERROR 0x0001
#define AIM_CB_MSG_PARAMINFO 0x0005
#define AIM_CB_MSG_INCOMING 0x0007
#define AIM_CB_MSG_EVIL 0x0009
#define AIM_CB_MSG_MISSEDCALL 0x000a
#define AIM_CB_MSG_CLIENTAUTORESP 0x000b
#define AIM_CB_MSG_ACK 0x000c
#define AIM_CB_MSG_MTN 0x0014
#define AIM_CB_MSG_DEFAULT 0xffff

#define AIM_IMFLAGS_AWAY		0x0001 /* mark as an autoreply */
#define AIM_IMFLAGS_ACK			0x0002 /* request a receipt notice */
#define AIM_IMFLAGS_UNICODE		0x0004
#define AIM_IMFLAGS_ISO_8859_1		0x0008
#define AIM_IMFLAGS_BUDDYREQ		0x0010 /* buddy icon requested */
#define AIM_IMFLAGS_HASICON		0x0020 /* already has icon */
#define AIM_IMFLAGS_SUBENC_MACINTOSH	0x0040 /* damn that Steve Jobs! */
#define AIM_IMFLAGS_CUSTOMFEATURES 	0x0080 /* features field present */
#define AIM_IMFLAGS_EXTDATA		0x0100
#define AIM_IMFLAGS_CUSTOMCHARSET	0x0200 /* charset fields set */
#define AIM_IMFLAGS_MULTIPART		0x0400 /* ->mpmsg section valid */
#define AIM_IMFLAGS_OFFLINE		0x0800 /* send to offline user */

/*
 * Multipart message structures.
 */
typedef struct aim_mpmsg_section_s {
	guint16 charset;
	guint16 charsubset;
	guint8 *data;
	guint16 datalen;
	struct aim_mpmsg_section_s *next;
} aim_mpmsg_section_t;

typedef struct aim_mpmsg_s {
	int numparts;
	aim_mpmsg_section_t *parts;
} aim_mpmsg_t;

int aim_mpmsg_init(aim_session_t *sess, aim_mpmsg_t *mpm);
int aim_mpmsg_addraw(aim_session_t *sess, aim_mpmsg_t *mpm, guint16 charset, guint16 charsubset, const guint8 *data, guint16 datalen);
int aim_mpmsg_addascii(aim_session_t *sess, aim_mpmsg_t *mpm, const char *ascii);
int aim_mpmsg_addunicode(aim_session_t *sess, aim_mpmsg_t *mpm, const guint16 *unicode, guint16 unicodelen);
void aim_mpmsg_free(aim_session_t *sess, aim_mpmsg_t *mpm);

/*
 * Arguments to aim_send_im_ext().
 *
 * This is really complicated.  But immensely versatile.
 *
 */
struct aim_sendimext_args {

	/* These are _required_ */
	const char *destsn;
	guint32 flags; /* often 0 */

	/* Only required if not using multipart messages */
	const char *msg;
	int msglen;

	/* Required if ->msg is not provided */
	aim_mpmsg_t *mpmsg;

	/* Only used if AIM_IMFLAGS_HASICON is set */
	guint32 iconlen;
	time_t iconstamp;
	guint32 iconsum;

	/* Only used if AIM_IMFLAGS_CUSTOMFEATURES is set */
	guint8 *features;
	guint8 featureslen;

	/* Only used if AIM_IMFLAGS_CUSTOMCHARSET is set and mpmsg not used */
	guint16 charset;
	guint16 charsubset;
};

/*
 * Arguments to aim_send_rtfmsg().
 */
struct aim_sendrtfmsg_args {
	const char *destsn;
	guint32 fgcolor;
	guint32 bgcolor;
	const char *rtfmsg; /* must be in RTF */
};

/*
 * This information is provided in the Incoming ICBM callback for
 * Channel 1 ICBM's.  
 *
 * Note that although CUSTOMFEATURES and CUSTOMCHARSET say they
 * are optional, both are always set by the current libfaim code.
 * That may or may not change in the future.  It is mainly for
 * consistency with aim_sendimext_args.
 *
 * Multipart messages require some explanation. If you want to use them,
 * I suggest you read all the comments in im.c.
 *
 */
struct aim_incomingim_ch1_args {

	/* Always provided */
	aim_mpmsg_t mpmsg;
	guint32 icbmflags; /* some flags apply only to ->msg, not all mpmsg */
	
	/* Only provided if message has a human-readable section */
	char *msg;
	int msglen;

	/* Only provided if AIM_IMFLAGS_HASICON is set */
	time_t iconstamp;
	guint32 iconlen;
	guint16 iconsum;

	/* Only provided if AIM_IMFLAGS_CUSTOMFEATURES is set */
	guint8 *features;
	guint8 featureslen;

	/* Only provided if AIM_IMFLAGS_EXTDATA is set */
	guint8 extdatalen;
	guint8 *extdata;

	/* Only used if AIM_IMFLAGS_CUSTOMCHARSET is set */
	guint16 charset;
	guint16 charsubset;
};

/* Valid values for channel 2 args->status */
#define AIM_RENDEZVOUS_PROPOSE 0x0000
#define AIM_RENDEZVOUS_CANCEL  0x0001
#define AIM_RENDEZVOUS_ACCEPT  0x0002

struct aim_incomingim_ch2_args {
	guint8 cookie[8];
	guint16 reqclass;
	guint16 status;
	guint16 errorcode;
	const char *clientip;
	const char *clientip2;
	const char *verifiedip;
	guint16 port;
	const char *msg; /* invite message or file description */
	const char *encoding;
	const char *language;
	union {
		struct {
			guint32 checksum;
			guint32 length;
			time_t timestamp;
			guint8 *icon;
		} icon;
		struct {
			struct aim_chat_roominfo roominfo;
		} chat;
		struct {
			guint32 fgcolor;
			guint32 bgcolor;
			const char *rtfmsg;
		} rtfmsg;
	} info;
	void *destructor; /* used internally only */
};

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

struct aim_incomingim_ch4_args {
	guint32 uin; /* Of the sender of the ICBM */
	guint16 type;
	char *msg; /* Reason for auth request, deny, or accept */
};

int aim_send_rtfmsg(aim_session_t *sess, struct aim_sendrtfmsg_args *args);
int aim_send_im_ext(aim_session_t *sess, struct aim_sendimext_args *args);
int aim_send_im(aim_session_t *, const char *destsn, unsigned short flags, const char *msg);
int aim_send_icon(aim_session_t *sess, const char *sn, const guint8 *icon, int iconlen, time_t stamp, guint16 iconsum);
guint16 aim_iconsum(const guint8 *buf, int buflen);
int aim_send_typing(aim_session_t *sess, aim_conn_t *conn, int typing);
int aim_send_im_direct(aim_session_t *, aim_conn_t *, const char *msg, int len);
const char *aim_directim_getsn(aim_conn_t *conn);
aim_conn_t *aim_directim_initiate(aim_session_t *, const char *destsn);
aim_conn_t *aim_directim_connect(aim_session_t *, const char *sn, const char *addr, const guint8 *cookie);

int aim_send_im_ch2_geticqmessage(aim_session_t *sess, const char *sn, int type);
int aim_im_sendmtn(aim_session_t *sess, guint16 type1, const char *sn, guint16 type2);
int aim_send_im_ch2_statusmessage(aim_session_t *sess, const char *sender, const guint8 *cookie, const char *message, const guint8 state, const guint16 dc);

#endif /* __OSCAR_IM_H__ */
an class="p">(innerlist, 0x0002, 1); } /* * Type 0x00c9: Flags * * 1 Evilable * 2 Nav Only * 4 Instancing Allowed * 8 Occupant Peek Allowed * */ if (aim_gettlv(innerlist, 0x00c9, 1)) exchanges[curexchange-1].flags = aim_gettlv16(innerlist, 0x00c9, 1); /* * Type 0x00ca: Creation Date */ if (aim_gettlv(innerlist, 0x00ca, 1)) ; /* * Type 0x00d0: Mandatory Channels? */ if (aim_gettlv(innerlist, 0x00d0, 1)) ; /* * Type 0x00d1: Maximum Message length */ if (aim_gettlv(innerlist, 0x00d1, 1)) ; /* * Type 0x00d2: Maximum Occupancy? */ if (aim_gettlv(innerlist, 0x00d2, 1)) ; /* * Type 0x00d3: Exchange Description */ if (aim_gettlv(innerlist, 0x00d3, 1)) exchanges[curexchange-1].name = aim_gettlv_str(innerlist, 0x00d3, 1); else exchanges[curexchange-1].name = NULL; /* * Type 0x00d4: Exchange Description URL */ if (aim_gettlv(innerlist, 0x00d4, 1)) ; /* * Type 0x00d5: Creation Permissions * * 0 Creation not allowed * 1 Room creation allowed * 2 Exchange creation allowed * */ if (aim_gettlv(innerlist, 0x00d5, 1)) { guint8 createperms; createperms = aim_gettlv8(innerlist, 0x00d5, 1); } /* * Type 0x00d6: Character Set (First Time) */ if (aim_gettlv(innerlist, 0x00d6, 1)) exchanges[curexchange-1].charset1 = aim_gettlv_str(innerlist, 0x00d6, 1); else exchanges[curexchange-1].charset1 = NULL; /* * Type 0x00d7: Language (First Time) */ if (aim_gettlv(innerlist, 0x00d7, 1)) exchanges[curexchange-1].lang1 = aim_gettlv_str(innerlist, 0x00d7, 1); else exchanges[curexchange-1].lang1 = NULL; /* * Type 0x00d8: Character Set (Second Time) */ if (aim_gettlv(innerlist, 0x00d8, 1)) exchanges[curexchange-1].charset2 = aim_gettlv_str(innerlist, 0x00d8, 1); else exchanges[curexchange-1].charset2 = NULL; /* * Type 0x00d9: Language (Second Time) */ if (aim_gettlv(innerlist, 0x00d9, 1)) exchanges[curexchange-1].lang2 = aim_gettlv_str(innerlist, 0x00d9, 1); else exchanges[curexchange-1].lang2 = NULL; /* * Type 0x00da: Unknown */ if (aim_gettlv(innerlist, 0x00da, 1)) ; aim_freetlvchain(&innerlist); } /* * Call client. */ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) ret = userfunc(sess, rx, snac2->type, maxrooms, curexchange, exchanges); for (curexchange--; curexchange >= 0; curexchange--) { g_free(exchanges[curexchange].name); g_free(exchanges[curexchange].charset1); g_free(exchanges[curexchange].lang1); g_free(exchanges[curexchange].charset2); g_free(exchanges[curexchange].lang2); } g_free(exchanges); aim_freetlvchain(&tlvlist); return ret; } static int parseinfo_create(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs, aim_snac_t *snac2) { aim_rxcallback_t userfunc; aim_tlvlist_t *tlvlist, *innerlist; char *ck = NULL, *fqcn = NULL, *name = NULL; guint16 exchange = 0, instance = 0, unknown = 0, flags = 0, maxmsglen = 0, maxoccupancy = 0; guint32 createtime = 0; guint8 createperms = 0, detaillevel; int cklen; aim_tlv_t *bigblock; int ret = 0; aim_bstream_t bbbs; tlvlist = aim_readtlvchain(bs); if (!(bigblock = aim_gettlv(tlvlist, 0x0004, 1))) { imcb_error(sess->aux_data, "no bigblock in top tlv in create room response"); aim_freetlvchain(&tlvlist); return 0; } aim_bstream_init(&bbbs, bigblock->value, bigblock->length); exchange = aimbs_get16(&bbbs); cklen = aimbs_get8(&bbbs); ck = aimbs_getstr(&bbbs, cklen); instance = aimbs_get16(&bbbs); detaillevel = aimbs_get8(&bbbs); if (detaillevel != 0x02) { imcb_error(sess->aux_data, "unknown detaillevel in create room response"); aim_freetlvchain(&tlvlist); g_free(ck); return 0; } unknown = aimbs_get16(&bbbs); innerlist = aim_readtlvchain(&bbbs); if (aim_gettlv(innerlist, 0x006a, 1)) fqcn = aim_gettlv_str(innerlist, 0x006a, 1); if (aim_gettlv(innerlist, 0x00c9, 1)) flags = aim_gettlv16(innerlist, 0x00c9, 1); if (aim_gettlv(innerlist, 0x00ca, 1)) createtime = aim_gettlv32(innerlist, 0x00ca, 1); if (aim_gettlv(innerlist, 0x00d1, 1)) maxmsglen = aim_gettlv16(innerlist, 0x00d1, 1); if (aim_gettlv(innerlist, 0x00d2, 1)) maxoccupancy = aim_gettlv16(innerlist, 0x00d2, 1); if (aim_gettlv(innerlist, 0x00d3, 1)) name = aim_gettlv_str(innerlist, 0x00d3, 1); if (aim_gettlv(innerlist, 0x00d5, 1)) createperms = aim_gettlv8(innerlist, 0x00d5, 1); if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) { ret = userfunc(sess, rx, snac2->type, fqcn, instance, exchange, flags, createtime, maxmsglen, maxoccupancy, createperms, unknown, name, ck); } g_free(ck); g_free(name); g_free(fqcn); aim_freetlvchain(&innerlist); aim_freetlvchain(&tlvlist); return ret; } /* * Since multiple things can trigger this callback, we must lookup the * snacid to determine the original snac subtype that was called. * * XXX This isn't really how this works. But this is: Every d/9 response * has a 16bit value at the beginning. That matches to: * Short Desc = 1 * Full Desc = 2 * Instance Info = 4 * Nav Short Desc = 8 * Nav Instance Info = 16 * And then everything is really asynchronous. There is no specific * attachment of a response to a create room request, for example. Creating * the room yields no different a response than requesting the room's info. * */ static int parseinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) { aim_snac_t *snac2; int ret = 0; if (!(snac2 = aim_remsnac(sess, snac->id))) { imcb_error(sess->aux_data, "received response to unknown request!"); return 0; } if (snac2->family != 0x000d) { imcb_error(sess->aux_data, "recieved response that maps to corrupt request!"); return 0; } /* * We now know what the original SNAC subtype was. */ if (snac2->type == 0x0002) /* request chat rights */ ret = parseinfo_perms(sess, mod, rx, snac, bs, snac2); else if (snac2->type == 0x0003) {} /* request exchange info */ else if (snac2->type == 0x0004) {} /* request room info */ else if (snac2->type == 0x0005) {} /* request more room info */ else if (snac2->type == 0x0006) {} /* request occupant list */ else if (snac2->type == 0x0007) {} /* search for a room */ else if (snac2->type == 0x0008) /* create room */ ret = parseinfo_create(sess, mod, rx, snac, bs, snac2); else imcb_error(sess->aux_data, "unknown request subtype"); if (snac2) g_free(snac2->data); g_free(snac2); return ret; } static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) { if (snac->subtype == 0x0009) return parseinfo(sess, mod, rx, snac, bs); return 0; } int chatnav_modfirst(aim_session_t *sess, aim_module_t *mod) { mod->family = 0x000d; mod->version = 0x0003; mod->toolid = 0x0010; mod->toolversion = 0x0629; mod->flags = 0; strncpy(mod->name, "chatnav", sizeof(mod->name)); mod->snachandler = snachandler; return 0; }