source: protocols/oscar/chatnav.c @ 0b1ae59

Last change on this file since 0b1ae59 was 5ebff60, checked in by dequis <dx@…>, at 2015-02-20T22:50:54Z

Reindent everything to K&R style with tabs

Used uncrustify, with the configuration file in ./doc/uncrustify.cfg

Commit author set to "Indent <please@…>" so that it's easier to
skip while doing git blame.

  • Property mode set to 100644
File size: 10.9 KB
RevLine 
[b7d3cc34]1/*
2 * Handle ChatNav.
3 *
4 * [The ChatNav(igation) service does various things to keep chat
[5ebff60]5 *  alive.  It provides room information, room searching and creating,
[b7d3cc34]6 *  as well as giving users the right ("permission") to use chat.]
7 *
8 */
9
10#include <aim.h>
11#include "chatnav.h"
12
13/*
14 * conn must be a chatnav connection!
15 */
16int aim_chatnav_reqrights(aim_session_t *sess, aim_conn_t *conn)
17{
18        return aim_genericreq_n_snacid(sess, conn, 0x000d, 0x0002);
19}
20
21int aim_chatnav_createroom(aim_session_t *sess, aim_conn_t *conn, const char *name, guint16 exchange)
22{
[5ebff60]23        static const char ck[] = { "create" };
24        static const char lang[] = { "en" };
25        static const char charset[] = { "us-ascii" };
[b7d3cc34]26        aim_frame_t *fr;
27        aim_snacid_t snacid;
28        aim_tlvlist_t *tl = NULL;
29
[5ebff60]30        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152))) {
[b7d3cc34]31                return -ENOMEM;
[5ebff60]32        }
[b7d3cc34]33
34        snacid = aim_cachesnac(sess, 0x000d, 0x0008, 0x0000, NULL, 0);
35        aim_putsnac(&fr->data, 0x000d, 0x0008, 0x0000, snacid);
36
37        /* exchange */
38        aimbs_put16(&fr->data, exchange);
39
40        /*
41         * This looks to be a big hack.  You'll note that this entire
42         * SNAC is just a room info structure, but the hard room name,
[5ebff60]43         * here, is set to "create".
[b7d3cc34]44         *
45         * Either this goes on the "list of questions concerning
46         * why-the-hell-did-you-do-that", or this value is completly
47         * ignored.  Without experimental evidence, but a good knowledge of
48         * AOL style, I'm going to guess that it is the latter, and that
49         * the value of the room name in create requests is ignored.
50         */
51        aimbs_put8(&fr->data, strlen(ck));
[5ebff60]52        aimbs_putraw(&fr->data, (guint8 *) ck, strlen(ck));
[b7d3cc34]53
[5ebff60]54        /*
[b7d3cc34]55         * instance
[5ebff60]56         *
[b7d3cc34]57         * Setting this to 0xffff apparently assigns the last instance.
58         *
59         */
60        aimbs_put16(&fr->data, 0xffff);
61
62        /* detail level */
63        aimbs_put8(&fr->data, 0x01);
64
[5ebff60]65        aim_addtlvtochain_raw(&tl, 0x00d3, strlen(name), (guint8 *) name);
66        aim_addtlvtochain_raw(&tl, 0x00d6, strlen(charset), (guint8 *) charset);
67        aim_addtlvtochain_raw(&tl, 0x00d7, strlen(lang), (guint8 *) lang);
[b7d3cc34]68
69        /* tlvcount */
70        aimbs_put16(&fr->data, aim_counttlvchain(&tl));
71        aim_writetlvchain(&fr->data, &tl);
72
73        aim_freetlvchain(&tl);
74
75        aim_tx_enqueue(sess, fr);
76
77        return 0;
78}
79
[5ebff60]80static int parseinfo_perms(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac,
81                           aim_bstream_t *bs, aim_snac_t *snac2)
[b7d3cc34]82{
83        aim_rxcallback_t userfunc;
84        int ret = 0;
85        struct aim_chat_exchangeinfo *exchanges = NULL;
86        int curexchange;
87        aim_tlv_t *exchangetlv;
88        guint8 maxrooms = 0;
89        aim_tlvlist_t *tlvlist, *innerlist;
90
91        tlvlist = aim_readtlvchain(bs);
92
[5ebff60]93        /*
[b7d3cc34]94         * Type 0x0002: Maximum concurrent rooms.
[5ebff60]95         */
96        if (aim_gettlv(tlvlist, 0x0002, 1)) {
[b7d3cc34]97                maxrooms = aim_gettlv8(tlvlist, 0x0002, 1);
[5ebff60]98        }
[b7d3cc34]99
[5ebff60]100        /*
[b7d3cc34]101         * Type 0x0003: Exchange information
102         *
103         * There can be any number of these, each one
[5ebff60]104         * representing another exchange.
105         *
[b7d3cc34]106         */
[5ebff60]107        for (curexchange = 0; ((exchangetlv = aim_gettlv(tlvlist, 0x0003, curexchange + 1))); ) {
[b7d3cc34]108                aim_bstream_t tbs;
109
110                aim_bstream_init(&tbs, exchangetlv->value, exchangetlv->length);
111
112                curexchange++;
113
114                exchanges = g_realloc(exchanges, curexchange * sizeof(struct aim_chat_exchangeinfo));
115
116                /* exchange number */
[5ebff60]117                exchanges[curexchange - 1].number = aimbs_get16(&tbs);
[b7d3cc34]118                innerlist = aim_readtlvchain(&tbs);
119
[5ebff60]120                /*
[b7d3cc34]121                 * Type 0x000a: Unknown.
122                 *
123                 * Usually three bytes: 0x0114 (exchange 1) or 0x010f (others).
124                 *
125                 */
[5ebff60]126                if (aim_gettlv(innerlist, 0x000a, 1)) {
[b7d3cc34]127                        ;
[5ebff60]128                }
[b7d3cc34]129
[5ebff60]130                /*
[b7d3cc34]131                 * Type 0x000d: Unknown.
132                 */
[5ebff60]133                if (aim_gettlv(innerlist, 0x000d, 1)) {
[b7d3cc34]134                        ;
[5ebff60]135                }
[b7d3cc34]136
[5ebff60]137                /*
[b7d3cc34]138                 * Type 0x0004: Unknown
139                 */
[5ebff60]140                if (aim_gettlv(innerlist, 0x0004, 1)) {
[b7d3cc34]141                        ;
[5ebff60]142                }
[b7d3cc34]143
[5ebff60]144                /*
[b7d3cc34]145                 * Type 0x0002: Unknown
146                 */
[5ebff60]147                if (aim_gettlv(innerlist, 0x0002, 1)) {
[d18db32f]148                        ;
[5ebff60]149                }
[b7d3cc34]150
151                /*
152                 * Type 0x00c9: Flags
153                 *
154                 * 1 Evilable
155                 * 2 Nav Only
156                 * 4 Instancing Allowed
157                 * 8 Occupant Peek Allowed
158                 *
[5ebff60]159                 */
160                if (aim_gettlv(innerlist, 0x00c9, 1)) {
161                        exchanges[curexchange - 1].flags = aim_gettlv16(innerlist, 0x00c9, 1);
162                }
163
[b7d3cc34]164                /*
[5ebff60]165                 * Type 0x00ca: Creation Date
[b7d3cc34]166                 */
[5ebff60]167                if (aim_gettlv(innerlist, 0x00ca, 1)) {
[b7d3cc34]168                        ;
[5ebff60]169                }
170
[b7d3cc34]171                /*
172                 * Type 0x00d0: Mandatory Channels?
173                 */
[5ebff60]174                if (aim_gettlv(innerlist, 0x00d0, 1)) {
[b7d3cc34]175                        ;
[5ebff60]176                }
[b7d3cc34]177
178                /*
179                 * Type 0x00d1: Maximum Message length
180                 */
[5ebff60]181                if (aim_gettlv(innerlist, 0x00d1, 1)) {
[b7d3cc34]182                        ;
[5ebff60]183                }
[b7d3cc34]184
185                /*
186                 * Type 0x00d2: Maximum Occupancy?
187                 */
[5ebff60]188                if (aim_gettlv(innerlist, 0x00d2, 1)) {
[b7d3cc34]189                        ;
[5ebff60]190                }
[b7d3cc34]191
192                /*
193                 * Type 0x00d3: Exchange Description
194                 */
[5ebff60]195                if (aim_gettlv(innerlist, 0x00d3, 1)) {
196                        exchanges[curexchange - 1].name = aim_gettlv_str(innerlist, 0x00d3, 1);
197                } else {
198                        exchanges[curexchange - 1].name = NULL;
199                }
[b7d3cc34]200
201                /*
202                 * Type 0x00d4: Exchange Description URL
203                 */
[5ebff60]204                if (aim_gettlv(innerlist, 0x00d4, 1)) {
[b7d3cc34]205                        ;
[5ebff60]206                }
[b7d3cc34]207
208                /*
209                 * Type 0x00d5: Creation Permissions
210                 *
211                 * 0  Creation not allowed
212                 * 1  Room creation allowed
213                 * 2  Exchange creation allowed
[5ebff60]214                 *
[b7d3cc34]215                 */
216                if (aim_gettlv(innerlist, 0x00d5, 1)) {
[b041b52]217                        aim_gettlv8(innerlist, 0x00d5, 1); /* createperms */
[b7d3cc34]218                }
219
220                /*
221                 * Type 0x00d6: Character Set (First Time)
[5ebff60]222                 */
223                if (aim_gettlv(innerlist, 0x00d6, 1)) {
224                        exchanges[curexchange - 1].charset1 = aim_gettlv_str(innerlist, 0x00d6, 1);
225                } else {
226                        exchanges[curexchange - 1].charset1 = NULL;
227                }
228
[b7d3cc34]229                /*
230                 * Type 0x00d7: Language (First Time)
[5ebff60]231                 */
232                if (aim_gettlv(innerlist, 0x00d7, 1)) {
233                        exchanges[curexchange - 1].lang1 = aim_gettlv_str(innerlist, 0x00d7, 1);
234                } else {
235                        exchanges[curexchange - 1].lang1 = NULL;
236                }
[b7d3cc34]237
238                /*
239                 * Type 0x00d8: Character Set (Second Time)
[5ebff60]240                 */
241                if (aim_gettlv(innerlist, 0x00d8, 1)) {
242                        exchanges[curexchange - 1].charset2 = aim_gettlv_str(innerlist, 0x00d8, 1);
243                } else {
244                        exchanges[curexchange - 1].charset2 = NULL;
245                }
[b7d3cc34]246
247                /*
248                 * Type 0x00d9: Language (Second Time)
[5ebff60]249                 */
250                if (aim_gettlv(innerlist, 0x00d9, 1)) {
251                        exchanges[curexchange - 1].lang2 = aim_gettlv_str(innerlist, 0x00d9, 1);
252                } else {
253                        exchanges[curexchange - 1].lang2 = NULL;
254                }
255
[b7d3cc34]256                /*
257                 * Type 0x00da: Unknown
258                 */
[5ebff60]259                if (aim_gettlv(innerlist, 0x00da, 1)) {
[b7d3cc34]260                        ;
[5ebff60]261                }
[b7d3cc34]262
263                aim_freetlvchain(&innerlist);
264        }
265
266        /*
267         * Call client.
268         */
[5ebff60]269        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) {
[b7d3cc34]270                ret = userfunc(sess, rx, snac2->type, maxrooms, curexchange, exchanges);
[5ebff60]271        }
[b7d3cc34]272
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);
279        }
280        g_free(exchanges);
281        aim_freetlvchain(&tlvlist);
282
283        return ret;
284}
285
[5ebff60]286static int parseinfo_create(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac,
287                            aim_bstream_t *bs, aim_snac_t *snac2)
[b7d3cc34]288{
289        aim_rxcallback_t userfunc;
290        aim_tlvlist_t *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;
295        int cklen;
296        aim_tlv_t *bigblock;
297        int ret = 0;
298        aim_bstream_t bbbs;
299
300        tlvlist = aim_readtlvchain(bs);
301
302        if (!(bigblock = aim_gettlv(tlvlist, 0x0004, 1))) {
[84b045d]303                imcb_error(sess->aux_data, "no bigblock in top tlv in create room response");
[5ebff60]304
[b7d3cc34]305                aim_freetlvchain(&tlvlist);
306                return 0;
307        }
308
309        aim_bstream_init(&bbbs, bigblock->value, bigblock->length);
310
311        exchange = aimbs_get16(&bbbs);
312        cklen = aimbs_get8(&bbbs);
313        ck = aimbs_getstr(&bbbs, cklen);
314        instance = aimbs_get16(&bbbs);
315        detaillevel = aimbs_get8(&bbbs);
316
317        if (detaillevel != 0x02) {
[84b045d]318                imcb_error(sess->aux_data, "unknown detaillevel in create room response");
[b7d3cc34]319                aim_freetlvchain(&tlvlist);
320                g_free(ck);
321                return 0;
322        }
323
324        unknown = aimbs_get16(&bbbs);
325
326        innerlist = aim_readtlvchain(&bbbs);
327
[5ebff60]328        if (aim_gettlv(innerlist, 0x006a, 1)) {
[b7d3cc34]329                fqcn = aim_gettlv_str(innerlist, 0x006a, 1);
[5ebff60]330        }
[b7d3cc34]331
[5ebff60]332        if (aim_gettlv(innerlist, 0x00c9, 1)) {
[b7d3cc34]333                flags = aim_gettlv16(innerlist, 0x00c9, 1);
[5ebff60]334        }
[b7d3cc34]335
[5ebff60]336        if (aim_gettlv(innerlist, 0x00ca, 1)) {
[b7d3cc34]337                createtime = aim_gettlv32(innerlist, 0x00ca, 1);
[5ebff60]338        }
[b7d3cc34]339
[5ebff60]340        if (aim_gettlv(innerlist, 0x00d1, 1)) {
[b7d3cc34]341                maxmsglen = aim_gettlv16(innerlist, 0x00d1, 1);
[5ebff60]342        }
[b7d3cc34]343
[5ebff60]344        if (aim_gettlv(innerlist, 0x00d2, 1)) {
[b7d3cc34]345                maxoccupancy = aim_gettlv16(innerlist, 0x00d2, 1);
[5ebff60]346        }
[b7d3cc34]347
[5ebff60]348        if (aim_gettlv(innerlist, 0x00d3, 1)) {
[b7d3cc34]349                name = aim_gettlv_str(innerlist, 0x00d3, 1);
[5ebff60]350        }
[b7d3cc34]351
[5ebff60]352        if (aim_gettlv(innerlist, 0x00d5, 1)) {
[b7d3cc34]353                createperms = aim_gettlv8(innerlist, 0x00d5, 1);
[5ebff60]354        }
[b7d3cc34]355
356        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) {
[5ebff60]357                ret = userfunc(sess, rx, snac2->type, fqcn, instance, exchange, flags, createtime, maxmsglen,
358                               maxoccupancy, createperms, unknown, name, ck);
[b7d3cc34]359        }
360
361        g_free(ck);
362        g_free(name);
363        g_free(fqcn);
364        aim_freetlvchain(&innerlist);
365        aim_freetlvchain(&tlvlist);
366
367        return ret;
368}
369
370/*
[5ebff60]371 * Since multiple things can trigger this callback, we must lookup the
[b7d3cc34]372 * snacid to determine the original snac subtype that was called.
373 *
374 * XXX This isn't really how this works.  But this is:  Every d/9 response
375 * has a 16bit value at the beginning. That matches to:
376 *    Short Desc = 1
377 *    Full Desc = 2
378 *    Instance Info = 4
379 *    Nav Short Desc = 8
380 *    Nav Instance Info = 16
[5ebff60]381 * And then everything is really asynchronous.  There is no specific
[b7d3cc34]382 * attachment of a response to a create room request, for example.  Creating
383 * the room yields no different a response than requesting the room's info.
384 *
385 */
386static int parseinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
387{
388        aim_snac_t *snac2;
389        int ret = 0;
390
391        if (!(snac2 = aim_remsnac(sess, snac->id))) {
[84b045d]392                imcb_error(sess->aux_data, "received response to unknown request!");
[b7d3cc34]393                return 0;
394        }
395
396        if (snac2->family != 0x000d) {
[7064d28]397                imcb_error(sess->aux_data, "received response that maps to corrupt request!");
[b7d3cc34]398                return 0;
399        }
400
401        /*
402         * We now know what the original SNAC subtype was.
403         */
[5ebff60]404        if (snac2->type == 0x0002) { /* request chat rights */
[b7d3cc34]405                ret = parseinfo_perms(sess, mod, rx, snac, bs, snac2);
[5ebff60]406        } else if (snac2->type == 0x0003) {
407        }                                  /* request exchange info */
408        else if (snac2->type == 0x0004) {
409        }                                  /* request room info */
410        else if (snac2->type == 0x0005) {
411        }                                  /* request more room info */
412        else if (snac2->type == 0x0006) {
413        }                                  /* request occupant list */
414        else if (snac2->type == 0x0007) {
415        }                                  /* search for a room */
416        else if (snac2->type == 0x0008) { /* create room */
[b7d3cc34]417                ret = parseinfo_create(sess, mod, rx, snac, bs, snac2);
[5ebff60]418        } else {
[84b045d]419                imcb_error(sess->aux_data, "unknown request subtype");
[5ebff60]420        }
[b7d3cc34]421
[5ebff60]422        if (snac2) {
[b7d3cc34]423                g_free(snac2->data);
[5ebff60]424        }
[b7d3cc34]425        g_free(snac2);
426
427        return ret;
428}
429
430static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
431{
432
[5ebff60]433        if (snac->subtype == 0x0009) {
[b7d3cc34]434                return parseinfo(sess, mod, rx, snac, bs);
[5ebff60]435        }
[b7d3cc34]436
437        return 0;
438}
439
440int chatnav_modfirst(aim_session_t *sess, aim_module_t *mod)
441{
442
443        mod->family = 0x000d;
444        mod->version = 0x0003;
445        mod->toolid = 0x0010;
446        mod->toolversion = 0x0629;
447        mod->flags = 0;
448        strncpy(mod->name, "chatnav", sizeof(mod->name));
449        mod->snachandler = snachandler;
450
451        return 0;
452}
Note: See TracBrowser for help on using the repository browser.