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 0x000d - Handle ChatNav.
24 * The ChatNav(igation) service does various things to keep chat
25 * alive. It provides room information, room searching and creating,
26 * as well as giving users the right ("permission") to use chat.
33 error(OscarData
*od
, FlapConnection
*conn
, aim_module_t
*mod
, FlapFrame
*frame
, aim_modsnac_t
*snac
, ByteStream
*bs
)
37 guint16 error
, chatnav_error
;
40 snac2
= aim_remsnac(od
, snac
->id
);
42 purple_debug_warning("oscar", "chatnav error: received response to unknown request (%08x)\n", snac
->id
);
46 if (snac2
->family
!= SNAC_FAMILY_CHATNAV
) {
47 purple_debug_warning("oscar", "chatnav error: received response that maps to corrupt request (fam=%04x)\n", snac2
->family
);
54 * We now know what the original SNAC subtype was.
56 if (snac2
->type
== 0x0008) /* create room */
58 error
= byte_stream_get16(bs
);
59 tlvlist
= aim_tlvlist_read(bs
);
60 chatnav_error
= aim_tlv_get16(tlvlist
, 0x0008, 1);
62 purple_debug_warning("oscar",
63 "Could not join room, error=0x%04hx, chatnav_error=0x%04hx\n",
64 error
, chatnav_error
);
65 purple_notify_error(od
->gc
, NULL
, _("Could not join chat room"),
66 chatnav_error
== 0x0033 ? _("Invalid chat room name") :
68 purple_request_cpar_from_connection(od
->gc
));
82 * conn must be a chatnav connection!
85 void aim_chatnav_reqrights(OscarData
*od
, FlapConnection
*conn
)
87 aim_genericreq_n_snacid(od
, conn
, SNAC_FAMILY_CHATNAV
, 0x0002);
93 int aim_chatnav_createroom(OscarData
*od
, FlapConnection
*conn
, const char *name
, guint16 exchange
)
95 static const char ck
[] = {"create"};
96 static const char lang
[] = {"en"};
97 static const char charset
[] = {"us-ascii"};
100 GSList
*tlvlist
= NULL
;
102 byte_stream_new(&bs
, 1142);
104 snacid
= aim_cachesnac(od
, SNAC_FAMILY_CHATNAV
, 0x0008, 0x0000, NULL
, 0);
107 byte_stream_put16(&bs
, exchange
);
110 * This looks to be a big hack. You'll note that this entire
111 * SNAC is just a room info structure, but the hard room name,
112 * here, is set to "create".
114 * Either this goes on the "list of questions concerning
115 * why-the-hell-did-you-do-that", or this value is completely
116 * ignored. Without experimental evidence, but a good knowledge of
117 * AOL style, I'm going to guess that it is the latter, and that
118 * the value of the room name in create requests is ignored.
120 byte_stream_put8(&bs
, strlen(ck
));
121 byte_stream_putstr(&bs
, ck
);
126 * Setting this to 0xffff apparently assigns the last instance.
129 byte_stream_put16(&bs
, 0xffff);
132 byte_stream_put8(&bs
, 0x01);
134 aim_tlvlist_add_str(&tlvlist
, 0x00d3, name
);
135 aim_tlvlist_add_str(&tlvlist
, 0x00d6, charset
);
136 aim_tlvlist_add_str(&tlvlist
, 0x00d7, lang
);
139 byte_stream_put16(&bs
, aim_tlvlist_count(tlvlist
));
140 aim_tlvlist_write(&bs
, &tlvlist
);
142 aim_tlvlist_free(tlvlist
);
144 flap_connection_send_snac(od
, conn
, SNAC_FAMILY_CHATNAV
, 0x0008, snacid
, &bs
);
146 byte_stream_destroy(&bs
);
152 parseinfo_perms(OscarData
*od
, FlapConnection
*conn
, aim_module_t
*mod
, FlapFrame
*frame
, aim_modsnac_t
*snac
, ByteStream
*bs
, aim_snac_t
*snac2
)
154 aim_rxcallback_t userfunc
;
156 struct aim_chat_exchangeinfo
*exchanges
= NULL
;
158 aim_tlv_t
*exchangetlv
;
160 GSList
*tlvlist
, *innerlist
;
162 tlvlist
= aim_tlvlist_read(bs
);
165 * Type 0x0002: Maximum concurrent rooms.
167 if (aim_tlv_gettlv(tlvlist
, 0x0002, 1))
168 maxrooms
= aim_tlv_get8(tlvlist
, 0x0002, 1);
171 * Type 0x0003: Exchange information
173 * There can be any number of these, each one
174 * representing another exchange.
177 for (curexchange
= 0; ((exchangetlv
= aim_tlv_gettlv(tlvlist
, 0x0003, curexchange
+1))); ) {
180 byte_stream_init(&tbs
, exchangetlv
->value
, exchangetlv
->length
);
184 exchanges
= g_realloc(exchanges
, curexchange
* sizeof(struct aim_chat_exchangeinfo
));
186 /* exchange number */
187 exchanges
[curexchange
-1].number
= byte_stream_get16(&tbs
);
188 innerlist
= aim_tlvlist_read(&tbs
);
191 * Type 0x0002: Unknown
193 if (aim_tlv_gettlv(innerlist
, 0x0002, 1)) {
196 classperms
= aim_tlv_get16(innerlist
, 0x0002, 1);
198 purple_debug_misc("oscar", "faim: class permissions %x\n", classperms
);
206 * 4 Instancing Allowed
207 * 8 Occupant Peek Allowed
210 if (aim_tlv_gettlv(innerlist
, 0x00c9, 1))
211 exchanges
[curexchange
-1].flags
= aim_tlv_get16(innerlist
, 0x00c9, 1);
214 * Type 0x00d3: Exchange Description
216 if (aim_tlv_gettlv(innerlist
, 0x00d3, 1))
217 exchanges
[curexchange
-1].name
= aim_tlv_getstr(innerlist
, 0x00d3, 1);
219 exchanges
[curexchange
-1].name
= NULL
;
222 * Type 0x00d5: Creation Permissions
224 * 0 Creation not allowed
225 * 1 Room creation allowed
226 * 2 Exchange creation allowed
229 if (aim_tlv_gettlv(innerlist
, 0x00d5, 1))
230 aim_tlv_get8(innerlist
, 0x00d5, 1); /* createperms */
233 * Type 0x00d6: Character Set (First Time)
235 if (aim_tlv_gettlv(innerlist
, 0x00d6, 1))
236 exchanges
[curexchange
-1].charset1
= aim_tlv_getstr(innerlist
, 0x00d6, 1);
238 exchanges
[curexchange
-1].charset1
= NULL
;
241 * Type 0x00d7: Language (First Time)
243 if (aim_tlv_gettlv(innerlist
, 0x00d7, 1))
244 exchanges
[curexchange
-1].lang1
= aim_tlv_getstr(innerlist
, 0x00d7, 1);
246 exchanges
[curexchange
-1].lang1
= NULL
;
249 * Type 0x00d8: Character Set (Second Time)
251 if (aim_tlv_gettlv(innerlist
, 0x00d8, 1))
252 exchanges
[curexchange
-1].charset2
= aim_tlv_getstr(innerlist
, 0x00d8, 1);
254 exchanges
[curexchange
-1].charset2
= NULL
;
257 * Type 0x00d9: Language (Second Time)
259 if (aim_tlv_gettlv(innerlist
, 0x00d9, 1))
260 exchanges
[curexchange
-1].lang2
= aim_tlv_getstr(innerlist
, 0x00d9, 1);
262 exchanges
[curexchange
-1].lang2
= NULL
;
264 aim_tlvlist_free(innerlist
);
270 if ((userfunc
= aim_callhandler(od
, snac
->family
, snac
->subtype
)))
271 ret
= userfunc(od
, conn
, frame
, snac2
->type
, maxrooms
, curexchange
, exchanges
);
273 for (curexchange
--; curexchange
>= 0; curexchange
--) {
274 g_free(exchanges
[curexchange
].name
);
275 g_free(exchanges
[curexchange
].charset1
);
276 g_free(exchanges
[curexchange
].lang1
);
277 g_free(exchanges
[curexchange
].charset2
);
278 g_free(exchanges
[curexchange
].lang2
);
281 aim_tlvlist_free(tlvlist
);
287 parseinfo_create(OscarData
*od
, FlapConnection
*conn
, aim_module_t
*mod
, FlapFrame
*frame
, aim_modsnac_t
*snac
, ByteStream
*bs
, aim_snac_t
*snac2
)
289 aim_rxcallback_t userfunc
;
290 GSList
*tlvlist
, *innerlist
;
291 char *ck
= NULL
, *fqcn
= NULL
, *name
= NULL
;
292 guint16 exchange
= 0, instance
= 0, unknown
= 0, flags
= 0, maxmsglen
= 0, maxoccupancy
= 0;
293 guint32 createtime
= 0;
294 guint8 createperms
= 0, detaillevel
;
300 tlvlist
= aim_tlvlist_read(bs
);
302 if (!(bigblock
= aim_tlv_gettlv(tlvlist
, 0x0004, 1))) {
303 purple_debug_misc("oscar", "no bigblock in top tlv in create room response\n");
304 aim_tlvlist_free(tlvlist
);
308 byte_stream_init(&bbbs
, bigblock
->value
, bigblock
->length
);
310 exchange
= byte_stream_get16(&bbbs
);
311 cklen
= byte_stream_get8(&bbbs
);
312 ck
= byte_stream_getstr(&bbbs
, cklen
);
313 instance
= byte_stream_get16(&bbbs
);
314 detaillevel
= byte_stream_get8(&bbbs
);
316 if (detaillevel
!= 0x02) {
317 purple_debug_misc("oscar", "unknown detaillevel in create room response (0x%02x)\n", detaillevel
);
318 aim_tlvlist_free(tlvlist
);
323 unknown
= byte_stream_get16(&bbbs
);
325 innerlist
= aim_tlvlist_read(&bbbs
);
327 if (aim_tlv_gettlv(innerlist
, 0x006a, 1))
328 fqcn
= aim_tlv_getstr(innerlist
, 0x006a, 1);
330 if (aim_tlv_gettlv(innerlist
, 0x00c9, 1))
331 flags
= aim_tlv_get16(innerlist
, 0x00c9, 1);
333 if (aim_tlv_gettlv(innerlist
, 0x00ca, 1))
334 createtime
= aim_tlv_get32(innerlist
, 0x00ca, 1);
336 if (aim_tlv_gettlv(innerlist
, 0x00d1, 1))
337 maxmsglen
= aim_tlv_get16(innerlist
, 0x00d1, 1);
339 if (aim_tlv_gettlv(innerlist
, 0x00d2, 1))
340 maxoccupancy
= aim_tlv_get16(innerlist
, 0x00d2, 1);
342 if (aim_tlv_gettlv(innerlist
, 0x00d3, 1))
343 name
= aim_tlv_getstr(innerlist
, 0x00d3, 1);
345 if (aim_tlv_gettlv(innerlist
, 0x00d5, 1))
346 createperms
= aim_tlv_get8(innerlist
, 0x00d5, 1);
348 if ((userfunc
= aim_callhandler(od
, snac
->family
, snac
->subtype
))) {
349 ret
= userfunc(od
, conn
, frame
, snac2
->type
, fqcn
, instance
, exchange
, flags
, createtime
, maxmsglen
, maxoccupancy
, createperms
, unknown
, name
, ck
);
355 aim_tlvlist_free(innerlist
);
356 aim_tlvlist_free(tlvlist
);
364 * Since multiple things can trigger this callback, we must lookup the
365 * snacid to determine the original snac subtype that was called.
367 * XXX This isn't really how this works. But this is: Every d/9 response
368 * has a 16bit value at the beginning. That matches to:
373 * Nav Instance Info = 16
374 * And then everything is really asynchronous. There is no specific
375 * attachment of a response to a create room request, for example. Creating
376 * the room yields no different a response than requesting the room's info.
380 parseinfo(OscarData
*od
, FlapConnection
*conn
, aim_module_t
*mod
, FlapFrame
*frame
, aim_modsnac_t
*snac
, ByteStream
*bs
)
385 if (!(snac2
= aim_remsnac(od
, snac
->id
))) {
386 purple_debug_misc("oscar", "faim: chatnav_parse_info: received response to unknown request! (%08x)\n", snac
->id
);
390 if (snac2
->family
!= SNAC_FAMILY_CHATNAV
) {
391 purple_debug_misc("oscar", "faim: chatnav_parse_info: received response that maps to corrupt request! (fam=%04x)\n", snac2
->family
);
398 * We now know what the original SNAC subtype was.
400 if (snac2
->type
== 0x0002) /* request chat rights */
401 ret
= parseinfo_perms(od
, conn
, mod
, frame
, snac
, bs
, snac2
);
402 else if (snac2
->type
== 0x0003) /* request exchange info */
403 purple_debug_misc("oscar", "chatnav_parse_info: response to exchange info\n");
404 else if (snac2
->type
== 0x0004) /* request room info */
405 purple_debug_misc("oscar", "chatnav_parse_info: response to room info\n");
406 else if (snac2
->type
== 0x0005) /* request more room info */
407 purple_debug_misc("oscar", "chatnav_parse_info: response to more room info\n");
408 else if (snac2
->type
== 0x0006) /* request occupant list */
409 purple_debug_misc("oscar", "chatnav_parse_info: response to occupant info\n");
410 else if (snac2
->type
== 0x0007) /* search for a room */
411 purple_debug_misc("oscar", "chatnav_parse_info: search results\n");
412 else if (snac2
->type
== 0x0008) /* create room */
413 ret
= parseinfo_create(od
, conn
, mod
, frame
, snac
, bs
, snac2
);
415 purple_debug_misc("oscar", "chatnav_parse_info: unknown request subtype (%04x)\n", snac2
->type
);
425 snachandler(OscarData
*od
, FlapConnection
*conn
, aim_module_t
*mod
, FlapFrame
*frame
, aim_modsnac_t
*snac
, ByteStream
*bs
)
427 if (snac
->subtype
== 0x0001)
428 return error(od
, conn
, mod
, frame
, snac
, bs
);
429 else if (snac
->subtype
== 0x0009)
430 return parseinfo(od
, conn
, mod
, frame
, snac
, bs
);
436 chatnav_modfirst(OscarData
*od
, aim_module_t
*mod
)
438 mod
->family
= SNAC_FAMILY_CHATNAV
;
439 mod
->version
= 0x0001;
440 mod
->toolid
= 0x0010;
441 mod
->toolversion
= 0x0629;
443 strncpy(mod
->name
, "chatnav", sizeof(mod
->name
));
444 mod
->snachandler
= snachandler
;