Mercurial > pidgin
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 } |