comparison libpurple/protocols/oscar/family_chat.c @ 15373:5fe8042783c1

Rename gtk/ and libgaim/ to pidgin/ and libpurple/
author Sean Egan <seanegan@gmail.com>
date Sat, 20 Jan 2007 02:32:10 +0000
parents
children 32c366eeeb99
comparison
equal deleted inserted replaced
15372:f79e0f4df793 15373:5fe8042783c1
1 /*
2 * Gaim's oscar protocol plugin
3 * This file is the legal property of its developers.
4 * Please see the AUTHORS file distributed alongside this file.
5 *
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.
10 *
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.
15 *
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 /*
22 * Family 0x000e - Routines for the Chat service.
23 *
24 */
25
26 #include "oscar.h"
27
28 #include <string.h>
29
30 /* Stored in the ->internal of chat connections */
31 struct chatconnpriv
32 {
33 guint16 exchange;
34 char *name;
35 guint16 instance;
36 };
37
38 void
39 flap_connection_destroy_chat(OscarData *od, FlapConnection *conn)
40 {
41 struct chatconnpriv *ccp = (struct chatconnpriv *)conn->internal;
42
43 if (ccp)
44 free(ccp->name);
45 free(ccp);
46
47 return;
48 }
49
50 char *
51 aim_chat_getname(FlapConnection *conn)
52 {
53 struct chatconnpriv *ccp;
54
55 if (!conn)
56 return NULL;
57
58 if (conn->type != SNAC_FAMILY_CHAT)
59 return NULL;
60
61 ccp = (struct chatconnpriv *)conn->internal;
62
63 return ccp->name;
64 }
65
66 /* XXX get this into conn.c -- evil!! */
67 FlapConnection *
68 aim_chat_getconn(OscarData *od, const char *name)
69 {
70 GSList *cur;
71
72 for (cur = od->oscar_connections; cur; cur = cur->next)
73 {
74 FlapConnection *conn;
75 struct chatconnpriv *ccp;
76
77 conn = cur->data;
78 ccp = (struct chatconnpriv *)conn->internal;
79
80 if (conn->type != SNAC_FAMILY_CHAT)
81 continue;
82 if (!conn->internal) {
83 gaim_debug_misc("oscar", "faim: chat: chat connection with no name! (fd = %d)\n", conn->fd);
84 continue;
85 }
86
87 if (strcmp(ccp->name, name) == 0)
88 return conn;;
89 }
90
91 return NULL;
92 }
93
94 int
95 aim_chat_attachname(FlapConnection *conn, guint16 exchange, const char *roomname, guint16 instance)
96 {
97 struct chatconnpriv *ccp;
98
99 if (!conn || !roomname)
100 return -EINVAL;
101
102 if (conn->internal)
103 free(conn->internal);
104
105 ccp = g_new(struct chatconnpriv, 1);
106
107 ccp->exchange = exchange;
108 ccp->name = strdup(roomname);
109 ccp->instance = instance;
110
111 conn->internal = (void *)ccp;
112
113 return 0;
114 }
115
116 int
117 aim_chat_readroominfo(ByteStream *bs, struct aim_chat_roominfo *outinfo)
118 {
119 if (!bs || !outinfo)
120 return 0;
121
122 outinfo->exchange = byte_stream_get16(bs);
123 outinfo->namelen = byte_stream_get8(bs);
124 outinfo->name = (char *)byte_stream_getraw(bs, outinfo->namelen);
125 outinfo->instance = byte_stream_get16(bs);
126
127 return 0;
128 }
129
130 int
131 aim_chat_leaveroom(OscarData *od, const char *name)
132 {
133 FlapConnection *conn;
134
135 if (!(conn = aim_chat_getconn(od, name)))
136 return -ENOENT;
137
138 flap_connection_close(od, conn);
139
140 return 0;
141 }
142
143 /*
144 * Subtype 0x0002 - General room information. Lots of stuff.
145 *
146 * Values I know are in here but I haven't attached
147 * them to any of the 'Unknown's:
148 * - Language (English)
149 *
150 */
151 static int
152 infoupdate(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
153 {
154 aim_userinfo_t *userinfo = NULL;
155 aim_rxcallback_t userfunc;
156 int ret = 0;
157 int usercount;
158 guint8 detaillevel = 0;
159 char *roomname;
160 struct aim_chat_roominfo roominfo;
161 guint16 tlvcount = 0;
162 aim_tlvlist_t *tlvlist;
163 aim_tlv_t *tlv;
164 char *roomdesc;
165 guint16 flags;
166 guint32 creationtime;
167 guint16 maxmsglen, maxvisiblemsglen;
168 guint16 unknown_d2, unknown_d5;
169
170 aim_chat_readroominfo(bs, &roominfo);
171
172 detaillevel = byte_stream_get8(bs);
173
174 if (detaillevel != 0x02) {
175 gaim_debug_misc("oscar", "faim: chat_roomupdateinfo: detail level %d not supported\n", detaillevel);
176 return 1;
177 }
178
179 tlvcount = byte_stream_get16(bs);
180
181 /*
182 * Everything else are TLVs.
183 */
184 tlvlist = aim_tlvlist_read(bs);
185
186 /*
187 * TLV type 0x006a is the room name in Human Readable Form.
188 */
189 roomname = aim_tlv_getstr(tlvlist, 0x006a, 1);
190
191 /*
192 * Type 0x006f: Number of occupants.
193 */
194 usercount = aim_tlv_get16(tlvlist, 0x006f, 1);
195
196 /*
197 * Type 0x0073: Occupant list.
198 */
199 tlv = aim_tlv_gettlv(tlvlist, 0x0073, 1);
200 if (tlv != NULL)
201 {
202 int curoccupant = 0;
203 ByteStream occbs;
204
205 /* Allocate enough userinfo structs for all occupants */
206 userinfo = calloc(usercount, sizeof(aim_userinfo_t));
207
208 byte_stream_init(&occbs, tlv->value, tlv->length);
209
210 while (curoccupant < usercount)
211 aim_info_extract(od, &occbs, &userinfo[curoccupant++]);
212 }
213
214 /*
215 * Type 0x00c9: Flags. (AIM_CHATROOM_FLAG)
216 */
217 flags = aim_tlv_get16(tlvlist, 0x00c9, 1);
218
219 /*
220 * Type 0x00ca: Creation time (4 bytes)
221 */
222 creationtime = aim_tlv_get32(tlvlist, 0x00ca, 1);
223
224 /*
225 * Type 0x00d1: Maximum Message Length
226 */
227 maxmsglen = aim_tlv_get16(tlvlist, 0x00d1, 1);
228
229 /*
230 * Type 0x00d2: Unknown. (2 bytes)
231 */
232 unknown_d2 = aim_tlv_get16(tlvlist, 0x00d2, 1);
233
234 /*
235 * Type 0x00d3: Room Description
236 */
237 roomdesc = aim_tlv_getstr(tlvlist, 0x00d3, 1);
238
239 #if 0
240 /*
241 * Type 0x000d4: Unknown (flag only)
242 */
243 if (aim_tlv_gettlv(tlvlist, 0x000d4, 1)) {
244 /* Unhandled */
245 }
246 #endif
247
248 /*
249 * Type 0x00d5: Unknown. (1 byte)
250 */
251 unknown_d5 = aim_tlv_get8(tlvlist, 0x00d5, 1);
252
253 #if 0
254 /*
255 * Type 0x00d6: Encoding 1 ("us-ascii")
256 */
257 if (aim_tlv_gettlv(tlvlist, 0x000d6, 1)) {
258 /* Unhandled */
259 }
260
261 /*
262 * Type 0x00d7: Language 1 ("en")
263 */
264 if (aim_tlv_gettlv(tlvlist, 0x000d7, 1)) {
265 /* Unhandled */
266 }
267
268 /*
269 * Type 0x00d8: Encoding 2 ("us-ascii")
270 */
271 if (aim_tlv_gettlv(tlvlist, 0x000d8, 1)) {
272 /* Unhandled */
273 }
274
275 /*
276 * Type 0x00d9: Language 2 ("en")
277 */
278 if (aim_tlv_gettlv(tlvlist, 0x000d9, 1)) {
279 /* Unhandled */
280 }
281 #endif
282
283 /*
284 * Type 0x00da: Maximum visible message length
285 */
286 maxvisiblemsglen = aim_tlv_get16(tlvlist, 0x00da, 1);
287
288 if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) {
289 ret = userfunc(od, conn,
290 frame,
291 &roominfo,
292 roomname,
293 usercount,
294 userinfo,
295 roomdesc,
296 flags,
297 creationtime,
298 maxmsglen,
299 unknown_d2,
300 unknown_d5,
301 maxvisiblemsglen);
302 }
303
304 free(roominfo.name);
305
306 while (usercount > 0)
307 aim_info_free(&userinfo[--usercount]);
308
309 free(userinfo);
310 free(roomname);
311 free(roomdesc);
312 aim_tlvlist_free(&tlvlist);
313
314 return ret;
315 }
316
317 /* Subtypes 0x0003 and 0x0004 */
318 static int
319 userlistchange(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
320 {
321 aim_userinfo_t *userinfo = NULL;
322 aim_rxcallback_t userfunc;
323 int curcount = 0, ret = 0;
324
325 while (byte_stream_empty(bs)) {
326 curcount++;
327 userinfo = realloc(userinfo, curcount * sizeof(aim_userinfo_t));
328 aim_info_extract(od, bs, &userinfo[curcount-1]);
329 }
330
331 if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
332 ret = userfunc(od, conn, frame, curcount, userinfo);
333
334 aim_info_free(userinfo);
335 free(userinfo);
336
337 return ret;
338 }
339
340 /*
341 * Subtype 0x0005 - Send a Chat Message.
342 *
343 * Possible flags:
344 * AIM_CHATFLAGS_NOREFLECT -- Unset the flag that requests messages
345 * should be sent to their sender.
346 * AIM_CHATFLAGS_AWAY -- Mark the message as an autoresponse
347 * (Note that WinAIM does not honor this,
348 * and displays the message as normal.)
349 *
350 * XXX convert this to use tlvchains
351 */
352 int
353 aim_chat_send_im(OscarData *od, FlapConnection *conn, guint16 flags, const gchar *msg, int msglen, const char *encoding, const char *language)
354 {
355 int i;
356 FlapFrame *frame;
357 IcbmCookie *cookie;
358 aim_snacid_t snacid;
359 guint8 ckstr[8];
360 aim_tlvlist_t *tlvlist = NULL, *inner_tlvlist = NULL;
361
362 if (!od || !conn || !msg || (msglen <= 0))
363 return 0;
364
365 frame = flap_frame_new(od, 0x02, 1152);
366
367 snacid = aim_cachesnac(od, 0x000e, 0x0005, 0x0000, NULL, 0);
368 aim_putsnac(&frame->data, 0x000e, 0x0005, 0x0000, snacid);
369
370 /*
371 * Cookie
372 *
373 * XXX mkcookie should generate the cookie and cache it in one
374 * operation to preserve uniqueness.
375 */
376 for (i = 0; i < 8; i++)
377 ckstr[i] = (guint8)rand();
378
379 cookie = aim_mkcookie(ckstr, AIM_COOKIETYPE_CHAT, NULL);
380 cookie->data = NULL; /* XXX store something useful here */
381
382 aim_cachecookie(od, cookie);
383
384 /* ICBM Header */
385 byte_stream_putraw(&frame->data, ckstr, 8); /* Cookie */
386 byte_stream_put16(&frame->data, 0x0003); /* Channel */
387
388 /*
389 * Type 1: Flag meaning this message is destined to the room.
390 */
391 aim_tlvlist_add_noval(&tlvlist, 0x0001);
392
393 /*
394 * Type 6: Reflect
395 */
396 if (!(flags & AIM_CHATFLAGS_NOREFLECT))
397 aim_tlvlist_add_noval(&tlvlist, 0x0006);
398
399 /*
400 * Type 7: Autoresponse
401 */
402 if (flags & AIM_CHATFLAGS_AWAY)
403 aim_tlvlist_add_noval(&tlvlist, 0x0007);
404
405 /*
406 * SubTLV: Type 1: Message
407 */
408 aim_tlvlist_add_raw(&inner_tlvlist, 0x0001, msglen, (guchar *)msg);
409
410 /*
411 * SubTLV: Type 2: Encoding
412 */
413 if (encoding != NULL)
414 aim_tlvlist_add_str(&inner_tlvlist, 0x0002, encoding);
415
416 /*
417 * SubTLV: Type 3: Language
418 */
419 if (language != NULL)
420 aim_tlvlist_add_str(&inner_tlvlist, 0x0003, language);
421
422 /*
423 * Type 5: Message block. Contains more TLVs.
424 *
425 * This could include other information... We just
426 * put in a message TLV however.
427 *
428 */
429 aim_tlvlist_add_frozentlvlist(&tlvlist, 0x0005, &inner_tlvlist);
430
431 aim_tlvlist_write(&frame->data, &tlvlist);
432
433 aim_tlvlist_free(&inner_tlvlist);
434 aim_tlvlist_free(&tlvlist);
435
436 flap_connection_send(conn, frame);
437
438 return 0;
439 }
440
441 /*
442 * Subtype 0x0006
443 *
444 * We could probably include this in the normal ICBM parsing
445 * code as channel 0x0003, however, since only the start
446 * would be the same, we might as well do it here.
447 *
448 * General outline of this SNAC:
449 * snac
450 * cookie
451 * channel id
452 * tlvlist
453 * unknown
454 * source user info
455 * name
456 * evility
457 * userinfo tlvs
458 * online time
459 * etc
460 * message metatlv
461 * message tlv
462 * message string
463 * possibly others
464 *
465 */
466 static int
467 incomingim_ch3(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
468 {
469 int ret = 0, i;
470 aim_rxcallback_t userfunc;
471 aim_userinfo_t userinfo;
472 guint8 cookie[8];
473 guint16 channel;
474 aim_tlvlist_t *tlvlist;
475 char *msg = NULL;
476 int len = 0;
477 char *encoding = NULL, *language = NULL;
478 IcbmCookie *ck;
479 aim_tlv_t *tlv;
480 ByteStream tbs;
481
482 memset(&userinfo, 0, sizeof(aim_userinfo_t));
483
484 /*
485 * Read ICBM Cookie.
486 */
487 for (i = 0; i < 8; i++)
488 cookie[i] = byte_stream_get8(bs);
489
490 if ((ck = aim_uncachecookie(od, cookie, AIM_COOKIETYPE_CHAT))) {
491 free(ck->data);
492 free(ck);
493 }
494
495 /*
496 * Channel ID
497 *
498 * Channel 0x0003 is used for chat messages.
499 *
500 */
501 channel = byte_stream_get16(bs);
502
503 if (channel != 0x0003) {
504 gaim_debug_misc("oscar", "faim: chat_incoming: unknown channel! (0x%04x)\n", channel);
505 return 0;
506 }
507
508 /*
509 * Start parsing TLVs right away.
510 */
511 tlvlist = aim_tlvlist_read(bs);
512
513 /*
514 * Type 0x0003: Source User Information
515 */
516 tlv = aim_tlv_gettlv(tlvlist, 0x0003, 1);
517 if (tlv != NULL)
518 {
519 byte_stream_init(&tbs, tlv->value, tlv->length);
520 aim_info_extract(od, &tbs, &userinfo);
521 }
522
523 #if 0
524 /*
525 * Type 0x0001: If present, it means it was a message to the
526 * room (as opposed to a whisper).
527 */
528 if (aim_tlv_gettlv(tlvlist, 0x0001, 1)) {
529 /* Unhandled */
530 }
531 #endif
532
533 /*
534 * Type 0x0005: Message Block. Conains more TLVs.
535 */
536 tlv = aim_tlv_gettlv(tlvlist, 0x0005, 1);
537 if (tlv != NULL)
538 {
539 aim_tlvlist_t *inner_tlvlist;
540 aim_tlv_t *inner_tlv;
541
542 byte_stream_init(&tbs, tlv->value, tlv->length);
543 inner_tlvlist = aim_tlvlist_read(&tbs);
544
545 /*
546 * Type 0x0001: Message.
547 */
548 inner_tlv = aim_tlv_gettlv(inner_tlvlist, 0x0001, 1);
549 if (inner_tlv != NULL)
550 {
551 len = inner_tlv->length;
552 msg = aim_tlv_getvalue_as_string(inner_tlv);
553 }
554
555 /*
556 * Type 0x0002: Encoding.
557 */
558 encoding = aim_tlv_getstr(inner_tlvlist, 0x0002, 1);
559
560 /*
561 * Type 0x0003: Language.
562 */
563 language = aim_tlv_getstr(inner_tlvlist, 0x0003, 1);
564
565 aim_tlvlist_free(&inner_tlvlist);
566 }
567
568 if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
569 ret = userfunc(od, conn, frame, &userinfo, len, msg, encoding, language);
570
571 aim_info_free(&userinfo);
572 free(msg);
573 free(encoding);
574 free(language);
575 aim_tlvlist_free(&tlvlist);
576
577 return ret;
578 }
579
580 static int
581 snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
582 {
583 if (snac->subtype == 0x0002)
584 return infoupdate(od, conn, mod, frame, snac, bs);
585 else if ((snac->subtype == 0x0003) || (snac->subtype == 0x0004))
586 return userlistchange(od, conn, mod, frame, snac, bs);
587 else if (snac->subtype == 0x0006)
588 return incomingim_ch3(od, conn, mod, frame, snac, bs);
589
590 return 0;
591 }
592
593 int
594 chat_modfirst(OscarData *od, aim_module_t *mod)
595 {
596 mod->family = 0x000e;
597 mod->version = 0x0001;
598 mod->toolid = 0x0010;
599 mod->toolversion = 0x0629;
600 mod->flags = 0;
601 strncpy(mod->name, "chat", sizeof(mod->name));
602 mod->snachandler = snachandler;
603
604 return 0;
605 }