rename accountopt.[ch] to purpleaccountoption.[ch]
[pidgin-git.git] / libpurple / protocols / oscar / family_chat.c
blob9a16b1b210fc2a03ea7eb5c1f7ae40edf13839b3
1 /*
2 * Purple's oscar protocol plugin
3 * This file is the legal property of its developers.
4 * Please see the AUTHORS file distributed alongside this file.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
22 * Family 0x000e - Routines for the Chat service.
26 #include "oscar.h"
28 #include <string.h>
30 /* Stored in the ->internal of chat connections */
31 struct chatconnpriv
33 guint16 exchange;
34 char *name;
35 guint16 instance;
38 void
39 flap_connection_destroy_chat(OscarData *od, FlapConnection *conn)
41 struct chatconnpriv *ccp = (struct chatconnpriv *)conn->internal;
43 if (ccp)
44 g_free(ccp->name);
45 g_free(ccp);
47 return;
50 int
51 aim_chat_readroominfo(ByteStream *bs, struct aim_chat_roominfo *outinfo)
53 if (!outinfo)
54 return 0;
56 outinfo->exchange = byte_stream_get16(bs);
57 outinfo->namelen = byte_stream_get8(bs);
58 outinfo->name = (char *)byte_stream_getraw(bs, outinfo->namelen);
59 outinfo->instance = byte_stream_get16(bs);
61 return 0;
65 * Subtype 0x0002 - General room information. Lots of stuff.
67 * Values I know are in here but I haven't attached
68 * them to any of the 'Unknown's:
69 * - Language (English)
72 static int
73 infoupdate(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
75 aim_rxcallback_t userfunc;
76 int ret = 0;
77 guint8 detaillevel = 0;
78 struct aim_chat_roominfo roominfo;
79 GSList *tlvlist;
80 guint16 maxmsglen, maxvisiblemsglen;
82 aim_chat_readroominfo(bs, &roominfo);
84 detaillevel = byte_stream_get8(bs);
86 if (detaillevel != 0x02) {
87 purple_debug_misc("oscar", "faim: chat_roomupdateinfo: detail level %d not supported\n", detaillevel);
88 return 1;
91 byte_stream_get16(bs); /* skip the TLV count */
94 * Everything else are TLVs.
96 tlvlist = aim_tlvlist_read(bs);
99 * Type 0x00d1: Maximum Message Length
101 maxmsglen = aim_tlv_get16(tlvlist, 0x00d1, 1);
104 * Type 0x00da: Maximum visible message length
106 maxvisiblemsglen = aim_tlv_get16(tlvlist, 0x00da, 1);
108 if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) {
109 ret = userfunc(od, conn, frame, maxmsglen, maxvisiblemsglen);
112 g_free(roominfo.name);
114 aim_tlvlist_free(tlvlist);
116 return ret;
119 /* Subtypes 0x0003 and 0x0004 */
120 static int
121 userlistchange(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
123 aim_userinfo_t *userinfo = NULL;
124 aim_rxcallback_t userfunc;
125 int curcount = 0, ret = 0;
127 while (byte_stream_bytes_left(bs)) {
128 curcount++;
129 userinfo = g_realloc(userinfo, curcount * sizeof(aim_userinfo_t));
130 aim_info_extract(od, bs, &userinfo[curcount-1]);
133 if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
134 ret = userfunc(od, conn, frame, curcount, userinfo);
136 aim_info_free(userinfo);
137 g_free(userinfo);
139 return ret;
143 * Subtype 0x0005 - Send a Chat Message.
145 * Possible flags:
146 * AIM_CHATFLAGS_NOREFLECT -- Unset the flag that requests messages
147 * should be sent to their sender.
148 * AIM_CHATFLAGS_AWAY -- Mark the message as an autoresponse
149 * (Note that WinAIM does not honor this,
150 * and displays the message as normal.)
152 * XXX convert this to use tlvchains
155 aim_chat_send_im(OscarData *od, FlapConnection *conn, guint16 flags, const gchar *msg, int msglen, const char *encoding, const char *language)
157 int i;
158 ByteStream bs;
159 IcbmCookie *cookie;
160 aim_snacid_t snacid;
161 guint8 ckstr[8];
162 GSList *tlvlist = NULL, *inner_tlvlist = NULL;
164 if (!od || !conn || !msg || (msglen <= 0))
165 return 0;
167 byte_stream_new(&bs, 1142);
169 snacid = aim_cachesnac(od, SNAC_FAMILY_CHAT, 0x0005, 0x0000, NULL, 0);
172 * Cookie
174 * XXX mkcookie should generate the cookie and cache it in one
175 * operation to preserve uniqueness.
177 for (i = 0; i < 8; i += 4) {
178 gint32 rnd = g_random_int();
179 ckstr[i] = (guint8)((rnd & 0xFF000000) >> 24);
180 ckstr[i+1] = (guint8)((rnd & 0xFF0000) >> 16);
181 ckstr[i+2] = (guint8)((rnd & 0xFF00) >> 8);
182 ckstr[i+3] = (guint8)(rnd & 0xFF);
185 cookie = aim_mkcookie(ckstr, AIM_COOKIETYPE_CHAT, NULL);
186 cookie->data = NULL; /* XXX store something useful here */
188 aim_cachecookie(od, cookie);
190 /* ICBM Header */
191 byte_stream_putraw(&bs, ckstr, 8); /* Cookie */
192 byte_stream_put16(&bs, 0x0003); /* Channel */
195 * Type 1: Flag meaning this message is destined to the room.
197 aim_tlvlist_add_noval(&tlvlist, 0x0001);
200 * Type 6: Reflect
202 if (!(flags & AIM_CHATFLAGS_NOREFLECT))
203 aim_tlvlist_add_noval(&tlvlist, 0x0006);
206 * Type 7: Autoresponse
208 if (flags & AIM_CHATFLAGS_AWAY)
209 aim_tlvlist_add_noval(&tlvlist, 0x0007);
212 * SubTLV: Type 1: Message
214 aim_tlvlist_add_raw(&inner_tlvlist, 0x0001, msglen, (guchar *)msg);
217 * SubTLV: Type 2: Encoding
219 if (encoding != NULL)
220 aim_tlvlist_add_str(&inner_tlvlist, 0x0002, encoding);
223 * SubTLV: Type 3: Language
225 if (language != NULL)
226 aim_tlvlist_add_str(&inner_tlvlist, 0x0003, language);
229 * Type 5: Message block. Contains more TLVs.
231 * This could include other information... We just
232 * put in a message TLV however.
235 aim_tlvlist_add_frozentlvlist(&tlvlist, 0x0005, &inner_tlvlist);
237 aim_tlvlist_write(&bs, &tlvlist);
239 aim_tlvlist_free(inner_tlvlist);
240 aim_tlvlist_free(tlvlist);
242 flap_connection_send_snac(od, conn, SNAC_FAMILY_CHAT, 0x0005, snacid, &bs);
244 byte_stream_destroy(&bs);
246 return 0;
250 * Subtype 0x0006
252 * We could probably include this in the normal ICBM parsing
253 * code as channel 0x0003, however, since only the start
254 * would be the same, we might as well do it here.
256 * General outline of this SNAC:
257 * snac
258 * cookie
259 * channel id
260 * tlvlist
261 * unknown
262 * source user info
263 * name
264 * evility
265 * userinfo tlvs
266 * online time
267 * etc
268 * message metatlv
269 * message tlv
270 * message string
271 * possibly others
274 static int
275 incomingim_ch3(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
277 int ret = 0, i;
278 aim_rxcallback_t userfunc;
279 aim_userinfo_t userinfo;
280 guint8 cookie[8];
281 guint16 channel;
282 GSList *tlvlist;
283 char *msg = NULL;
284 int len = 0;
285 char *encoding = NULL, *language = NULL;
286 IcbmCookie *ck;
287 aim_tlv_t *tlv;
288 ByteStream tbs;
290 memset(&userinfo, 0, sizeof(aim_userinfo_t));
293 * Read ICBM Cookie.
295 for (i = 0; i < 8; i++)
296 cookie[i] = byte_stream_get8(bs);
298 if ((ck = aim_uncachecookie(od, cookie, AIM_COOKIETYPE_CHAT))) {
299 g_free(ck->data);
300 g_free(ck);
304 * Channel ID
306 * Channel 0x0003 is used for chat messages.
309 channel = byte_stream_get16(bs);
311 if (channel != 0x0003) {
312 purple_debug_misc("oscar", "faim: chat_incoming: unknown channel! (0x%04x)\n", channel);
313 return 0;
317 * Start parsing TLVs right away.
319 tlvlist = aim_tlvlist_read(bs);
322 * Type 0x0003: Source User Information
324 tlv = aim_tlv_gettlv(tlvlist, 0x0003, 1);
325 if (tlv != NULL)
327 byte_stream_init(&tbs, tlv->value, tlv->length);
328 aim_info_extract(od, &tbs, &userinfo);
332 * Type 0x0005: Message Block. Conains more TLVs.
334 tlv = aim_tlv_gettlv(tlvlist, 0x0005, 1);
335 if (tlv != NULL)
337 GSList *inner_tlvlist;
338 aim_tlv_t *inner_tlv;
340 byte_stream_init(&tbs, tlv->value, tlv->length);
341 inner_tlvlist = aim_tlvlist_read(&tbs);
344 * Type 0x0001: Message.
346 inner_tlv = aim_tlv_gettlv(inner_tlvlist, 0x0001, 1);
347 if (inner_tlv != NULL)
349 len = inner_tlv->length;
350 msg = aim_tlv_getvalue_as_string(inner_tlv);
354 * Type 0x0002: Encoding.
356 encoding = aim_tlv_getstr(inner_tlvlist, 0x0002, 1);
359 * Type 0x0003: Language.
361 language = aim_tlv_getstr(inner_tlvlist, 0x0003, 1);
363 aim_tlvlist_free(inner_tlvlist);
366 if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
367 ret = userfunc(od, conn, frame, &userinfo, len, msg, encoding, language);
369 aim_info_free(&userinfo);
370 g_free(msg);
371 g_free(encoding);
372 g_free(language);
373 aim_tlvlist_free(tlvlist);
375 return ret;
378 static int
379 snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
381 if (snac->subtype == 0x0002)
382 return infoupdate(od, conn, mod, frame, snac, bs);
383 else if ((snac->subtype == 0x0003) || (snac->subtype == 0x0004))
384 return userlistchange(od, conn, mod, frame, snac, bs);
385 else if (snac->subtype == 0x0006)
386 return incomingim_ch3(od, conn, mod, frame, snac, bs);
388 return 0;
392 chat_modfirst(OscarData *od, aim_module_t *mod)
394 mod->family = SNAC_FAMILY_CHAT;
395 mod->version = 0x0001;
396 mod->toolid = 0x0010;
397 mod->toolversion = 0x0629;
398 mod->flags = 0;
399 strncpy(mod->name, "chat", sizeof(mod->name));
400 mod->snachandler = snachandler;
402 return 0;