Annotation of ChivanetAimPidgin/oscarprpl/src/c/family_chatnav.c, revision 1.1.1.1

1.1       snw         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.
                      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., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
                     19: */
                     20: 
                     21: /*
                     22:  * Family 0x000d - Handle ChatNav.
                     23:  *
                     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.
                     27:  *
                     28:  */
                     29: 
                     30: #include "oscar.h"
                     31: 
                     32: static int
                     33: error(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
                     34: {
                     35:        int ret = 0;
                     36:        aim_snac_t *snac2;
                     37:        guint16 error, chatnav_error;
                     38:        GSList *tlvlist;
                     39: 
                     40:        snac2 = aim_remsnac(od, snac->id);
                     41:        if (!snac2) {
                     42:                purple_debug_warning("oscar", "chatnav error: received response to unknown request (%08x)\n", snac->id);
                     43:                return 0;
                     44:        }
                     45: 
                     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);
                     48:                g_free(snac2->data);
                     49:                g_free(snac2);
                     50:                return 0;
                     51:        }
                     52: 
                     53:        /*
                     54:         * We now know what the original SNAC subtype was.
                     55:         */
                     56:        if (snac2->type == 0x0008) /* create room */
                     57:        {
                     58:                error = byte_stream_get16(bs);
                     59:                tlvlist = aim_tlvlist_read(bs);
                     60:                chatnav_error = aim_tlv_get16(tlvlist, 0x0008, 1);
                     61: 
                     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") : _("Unknown error"));
                     67: 
                     68:                ret = 1;
                     69:        }
                     70: 
                     71:        g_free(snac2->data);
                     72:        g_free(snac2);
                     73: 
                     74:        return ret;
                     75: }
                     76: 
                     77: /*
                     78:  * Subtype 0x0002
                     79:  *
                     80:  * conn must be a chatnav connection!
                     81:  *
                     82:  */
                     83: void aim_chatnav_reqrights(OscarData *od, FlapConnection *conn)
                     84: {
                     85:        aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_CHATNAV, 0x0002);
                     86: }
                     87: 
                     88: /*
                     89:  * Subtype 0x0008
                     90:  */
                     91: int aim_chatnav_createroom(OscarData *od, FlapConnection *conn, const char *name, guint16 exchange)
                     92: {
                     93:        static const char ck[] = {"create"};
                     94:        static const char lang[] = {"en"};
                     95:        static const char charset[] = {"us-ascii"};
                     96:        ByteStream bs;
                     97:        aim_snacid_t snacid;
                     98:        GSList *tlvlist = NULL;
                     99: 
                    100:        byte_stream_new(&bs, 1142);
                    101: 
                    102:        snacid = aim_cachesnac(od, SNAC_FAMILY_CHATNAV, 0x0008, 0x0000, NULL, 0);
                    103: 
                    104:        /* exchange */
                    105:        byte_stream_put16(&bs, exchange);
                    106: 
                    107:        /*
                    108:         * This looks to be a big hack.  You'll note that this entire
                    109:         * SNAC is just a room info structure, but the hard room name,
                    110:         * here, is set to "create".
                    111:         *
                    112:         * Either this goes on the "list of questions concerning
                    113:         * why-the-hell-did-you-do-that", or this value is completely
                    114:         * ignored.  Without experimental evidence, but a good knowledge of
                    115:         * AOL style, I'm going to guess that it is the latter, and that
                    116:         * the value of the room name in create requests is ignored.
                    117:         */
                    118:        byte_stream_put8(&bs, strlen(ck));
                    119:        byte_stream_putstr(&bs, ck);
                    120: 
                    121:        /*
                    122:         * instance
                    123:         *
                    124:         * Setting this to 0xffff apparently assigns the last instance.
                    125:         *
                    126:         */
                    127:        byte_stream_put16(&bs, 0xffff);
                    128: 
                    129:        /* detail level */
                    130:        byte_stream_put8(&bs, 0x01);
                    131: 
                    132:        aim_tlvlist_add_str(&tlvlist, 0x00d3, name);
                    133:        aim_tlvlist_add_str(&tlvlist, 0x00d6, charset);
                    134:        aim_tlvlist_add_str(&tlvlist, 0x00d7, lang);
                    135: 
                    136:        /* tlvcount */
                    137:        byte_stream_put16(&bs, aim_tlvlist_count(tlvlist));
                    138:        aim_tlvlist_write(&bs, &tlvlist);
                    139: 
                    140:        aim_tlvlist_free(tlvlist);
                    141: 
                    142:        flap_connection_send_snac(od, conn, SNAC_FAMILY_CHATNAV, 0x0008, snacid, &bs);
                    143: 
                    144:        byte_stream_destroy(&bs);
                    145: 
                    146:        return 0;
                    147: }
                    148: 
                    149: static int
                    150: parseinfo_perms(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs, aim_snac_t *snac2)
                    151: {
                    152:        aim_rxcallback_t userfunc;
                    153:        int ret = 0;
                    154:        struct aim_chat_exchangeinfo *exchanges = NULL;
                    155:        int curexchange;
                    156:        aim_tlv_t *exchangetlv;
                    157:        guint8 maxrooms = 0;
                    158:        GSList *tlvlist, *innerlist;
                    159: 
                    160:        tlvlist = aim_tlvlist_read(bs);
                    161: 
                    162:        /*
                    163:         * Type 0x0002: Maximum concurrent rooms.
                    164:         */
                    165:        if (aim_tlv_gettlv(tlvlist, 0x0002, 1))
                    166:                maxrooms = aim_tlv_get8(tlvlist, 0x0002, 1);
                    167: 
                    168:        /*
                    169:         * Type 0x0003: Exchange information
                    170:         *
                    171:         * There can be any number of these, each one
                    172:         * representing another exchange.
                    173:         *
                    174:         */
                    175:        for (curexchange = 0; ((exchangetlv = aim_tlv_gettlv(tlvlist, 0x0003, curexchange+1))); ) {
                    176:                ByteStream tbs;
                    177: 
                    178:                byte_stream_init(&tbs, exchangetlv->value, exchangetlv->length);
                    179: 
                    180:                curexchange++;
                    181: 
                    182:                exchanges = g_realloc(exchanges, curexchange * sizeof(struct aim_chat_exchangeinfo));
                    183: 
                    184:                /* exchange number */
                    185:                exchanges[curexchange-1].number = byte_stream_get16(&tbs);
                    186:                innerlist = aim_tlvlist_read(&tbs);
                    187: 
                    188:                /*
                    189:                 * Type 0x0002: Unknown
                    190:                 */
                    191:                if (aim_tlv_gettlv(innerlist, 0x0002, 1)) {
                    192:                        guint16 classperms;
                    193: 
                    194:                        classperms = aim_tlv_get16(innerlist, 0x0002, 1);
                    195: 
                    196:                        purple_debug_misc("oscar", "faim: class permissions %x\n", classperms);
                    197:                }
                    198: 
                    199:                /*
                    200:                 * Type 0x00c9: Flags
                    201:                 *
                    202:                 * 1 Evilable
                    203:                 * 2 Nav Only
                    204:                 * 4 Instancing Allowed
                    205:                 * 8 Occupant Peek Allowed
                    206:                 *
                    207:                 */
                    208:                if (aim_tlv_gettlv(innerlist, 0x00c9, 1))
                    209:                        exchanges[curexchange-1].flags = aim_tlv_get16(innerlist, 0x00c9, 1);
                    210: 
                    211:                /*
                    212:                 * Type 0x00d3: Exchange Description
                    213:                 */
                    214:                if (aim_tlv_gettlv(innerlist, 0x00d3, 1))
                    215:                        exchanges[curexchange-1].name = aim_tlv_getstr(innerlist, 0x00d3, 1);
                    216:                else
                    217:                        exchanges[curexchange-1].name = NULL;
                    218: 
                    219:                /*
                    220:                 * Type 0x00d5: Creation Permissions
                    221:                 *
                    222:                 * 0  Creation not allowed
                    223:                 * 1  Room creation allowed
                    224:                 * 2  Exchange creation allowed
                    225:                 *
                    226:                 */
                    227:                if (aim_tlv_gettlv(innerlist, 0x00d5, 1))
                    228:                        aim_tlv_get8(innerlist, 0x00d5, 1); /* createperms */
                    229: 
                    230:                /*
                    231:                 * Type 0x00d6: Character Set (First Time)
                    232:                 */
                    233:                if (aim_tlv_gettlv(innerlist, 0x00d6, 1))
                    234:                        exchanges[curexchange-1].charset1 = aim_tlv_getstr(innerlist, 0x00d6, 1);
                    235:                else
                    236:                        exchanges[curexchange-1].charset1 = NULL;
                    237: 
                    238:                /*
                    239:                 * Type 0x00d7: Language (First Time)
                    240:                 */
                    241:                if (aim_tlv_gettlv(innerlist, 0x00d7, 1))
                    242:                        exchanges[curexchange-1].lang1 = aim_tlv_getstr(innerlist, 0x00d7, 1);
                    243:                else
                    244:                        exchanges[curexchange-1].lang1 = NULL;
                    245: 
                    246:                /*
                    247:                 * Type 0x00d8: Character Set (Second Time)
                    248:                 */
                    249:                if (aim_tlv_gettlv(innerlist, 0x00d8, 1))
                    250:                        exchanges[curexchange-1].charset2 = aim_tlv_getstr(innerlist, 0x00d8, 1);
                    251:                else
                    252:                        exchanges[curexchange-1].charset2 = NULL;
                    253: 
                    254:                /*
                    255:                 * Type 0x00d9: Language (Second Time)
                    256:                 */
                    257:                if (aim_tlv_gettlv(innerlist, 0x00d9, 1))
                    258:                        exchanges[curexchange-1].lang2 = aim_tlv_getstr(innerlist, 0x00d9, 1);
                    259:                else
                    260:                        exchanges[curexchange-1].lang2 = NULL;
                    261: 
                    262:                aim_tlvlist_free(innerlist);
                    263:        }
                    264: 
                    265:        /*
                    266:         * Call client.
                    267:         */
                    268:        if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
                    269:                ret = userfunc(od, conn, frame, snac2->type, maxrooms, curexchange, exchanges);
                    270: 
                    271:        for (curexchange--; curexchange >= 0; curexchange--) {
                    272:                g_free(exchanges[curexchange].name);
                    273:                g_free(exchanges[curexchange].charset1);
                    274:                g_free(exchanges[curexchange].lang1);
                    275:                g_free(exchanges[curexchange].charset2);
                    276:                g_free(exchanges[curexchange].lang2);
                    277:        }
                    278:        g_free(exchanges);
                    279:        aim_tlvlist_free(tlvlist);
                    280: 
                    281:        return ret;
                    282: }
                    283: 
                    284: static int
                    285: parseinfo_create(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs, aim_snac_t *snac2)
                    286: {
                    287:        aim_rxcallback_t userfunc;
                    288:        GSList *tlvlist, *innerlist;
                    289:        char *ck = NULL, *fqcn = NULL, *name = NULL;
                    290:        guint16 exchange = 0, instance = 0, unknown = 0, flags = 0, maxmsglen = 0, maxoccupancy = 0;
                    291:        guint32 createtime = 0;
                    292:        guint8 createperms = 0, detaillevel;
                    293:        int cklen;
                    294:        aim_tlv_t *bigblock;
                    295:        int ret = 0;
                    296:        ByteStream bbbs;
                    297: 
                    298:        tlvlist = aim_tlvlist_read(bs);
                    299: 
                    300:        if (!(bigblock = aim_tlv_gettlv(tlvlist, 0x0004, 1))) {
                    301:                purple_debug_misc("oscar", "no bigblock in top tlv in create room response\n");
                    302:                aim_tlvlist_free(tlvlist);
                    303:                return 0;
                    304:        }
                    305: 
                    306:        byte_stream_init(&bbbs, bigblock->value, bigblock->length);
                    307: 
                    308:        exchange = byte_stream_get16(&bbbs);
                    309:        cklen = byte_stream_get8(&bbbs);
                    310:        ck = byte_stream_getstr(&bbbs, cklen);
                    311:        instance = byte_stream_get16(&bbbs);
                    312:        detaillevel = byte_stream_get8(&bbbs);
                    313: 
                    314:        if (detaillevel != 0x02) {
                    315:                purple_debug_misc("oscar", "unknown detaillevel in create room response (0x%02x)\n", detaillevel);
                    316:                aim_tlvlist_free(tlvlist);
                    317:                g_free(ck);
                    318:                return 0;
                    319:        }
                    320: 
                    321:        unknown = byte_stream_get16(&bbbs);
                    322: 
                    323:        innerlist = aim_tlvlist_read(&bbbs);
                    324: 
                    325:        if (aim_tlv_gettlv(innerlist, 0x006a, 1))
                    326:                fqcn = aim_tlv_getstr(innerlist, 0x006a, 1);
                    327: 
                    328:        if (aim_tlv_gettlv(innerlist, 0x00c9, 1))
                    329:                flags = aim_tlv_get16(innerlist, 0x00c9, 1);
                    330: 
                    331:        if (aim_tlv_gettlv(innerlist, 0x00ca, 1))
                    332:                createtime = aim_tlv_get32(innerlist, 0x00ca, 1);
                    333: 
                    334:        if (aim_tlv_gettlv(innerlist, 0x00d1, 1))
                    335:                maxmsglen = aim_tlv_get16(innerlist, 0x00d1, 1);
                    336: 
                    337:        if (aim_tlv_gettlv(innerlist, 0x00d2, 1))
                    338:                maxoccupancy = aim_tlv_get16(innerlist, 0x00d2, 1);
                    339: 
                    340:        if (aim_tlv_gettlv(innerlist, 0x00d3, 1))
                    341:                name = aim_tlv_getstr(innerlist, 0x00d3, 1);
                    342: 
                    343:        if (aim_tlv_gettlv(innerlist, 0x00d5, 1))
                    344:                createperms = aim_tlv_get8(innerlist, 0x00d5, 1);
                    345: 
                    346:        if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) {
                    347:                ret = userfunc(od, conn, frame, snac2->type, fqcn, instance, exchange, flags, createtime, maxmsglen, maxoccupancy, createperms, unknown, name, ck);
                    348:        }
                    349: 
                    350:        g_free(ck);
                    351:        g_free(name);
                    352:        g_free(fqcn);
                    353:        aim_tlvlist_free(innerlist);
                    354:        aim_tlvlist_free(tlvlist);
                    355: 
                    356:        return ret;
                    357: }
                    358: 
                    359: /*
                    360:  * Subtype 0x0009
                    361:  *
                    362:  * Since multiple things can trigger this callback, we must lookup the
                    363:  * snacid to determine the original snac subtype that was called.
                    364:  *
                    365:  * XXX This isn't really how this works.  But this is:  Every d/9 response
                    366:  * has a 16bit value at the beginning. That matches to:
                    367:  *    Short Desc = 1
                    368:  *    Full Desc = 2
                    369:  *    Instance Info = 4
                    370:  *    Nav Short Desc = 8
                    371:  *    Nav Instance Info = 16
                    372:  * And then everything is really asynchronous.  There is no specific
                    373:  * attachment of a response to a create room request, for example.  Creating
                    374:  * the room yields no different a response than requesting the room's info.
                    375:  *
                    376:  */
                    377: static int
                    378: parseinfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
                    379: {
                    380:        aim_snac_t *snac2;
                    381:        int ret = 0;
                    382: 
                    383:        if (!(snac2 = aim_remsnac(od, snac->id))) {
                    384:                purple_debug_misc("oscar", "faim: chatnav_parse_info: received response to unknown request! (%08x)\n", snac->id);
                    385:                return 0;
                    386:        }
                    387: 
                    388:        if (snac2->family != SNAC_FAMILY_CHATNAV) {
                    389:                purple_debug_misc("oscar", "faim: chatnav_parse_info: received response that maps to corrupt request! (fam=%04x)\n", snac2->family);
                    390:                g_free(snac2->data);
                    391:                g_free(snac2);
                    392:                return 0;
                    393:        }
                    394: 
                    395:        /*
                    396:         * We now know what the original SNAC subtype was.
                    397:         */
                    398:        if (snac2->type == 0x0002) /* request chat rights */
                    399:                ret = parseinfo_perms(od, conn, mod, frame, snac, bs, snac2);
                    400:        else if (snac2->type == 0x0003) /* request exchange info */
                    401:                purple_debug_misc("oscar", "chatnav_parse_info: response to exchange info\n");
                    402:        else if (snac2->type == 0x0004) /* request room info */
                    403:                purple_debug_misc("oscar", "chatnav_parse_info: response to room info\n");
                    404:        else if (snac2->type == 0x0005) /* request more room info */
                    405:                purple_debug_misc("oscar", "chatnav_parse_info: response to more room info\n");
                    406:        else if (snac2->type == 0x0006) /* request occupant list */
                    407:                purple_debug_misc("oscar", "chatnav_parse_info: response to occupant info\n");
                    408:        else if (snac2->type == 0x0007) /* search for a room */
                    409:                purple_debug_misc("oscar", "chatnav_parse_info: search results\n");
                    410:        else if (snac2->type == 0x0008) /* create room */
                    411:                ret = parseinfo_create(od, conn, mod, frame, snac, bs, snac2);
                    412:        else
                    413:                purple_debug_misc("oscar", "chatnav_parse_info: unknown request subtype (%04x)\n", snac2->type);
                    414: 
                    415:        if (snac2)
                    416:                g_free(snac2->data);
                    417:        g_free(snac2);
                    418: 
                    419:        return ret;
                    420: }
                    421: 
                    422: static int
                    423: snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
                    424: {
                    425:        if (snac->subtype == 0x0001)
                    426:                return error(od, conn, mod, frame, snac, bs);
                    427:        else if (snac->subtype == 0x0009)
                    428:                return parseinfo(od, conn, mod, frame, snac, bs);
                    429: 
                    430:        return 0;
                    431: }
                    432: 
                    433: int
                    434: chatnav_modfirst(OscarData *od, aim_module_t *mod)
                    435: {
                    436:        mod->family = SNAC_FAMILY_CHATNAV;
                    437:        mod->version = 0x0001;
                    438:        mod->toolid = 0x0010;
                    439:        mod->toolversion = 0x0629;
                    440:        mod->flags = 0;
                    441:        strncpy(mod->name, "chatnav", sizeof(mod->name));
                    442:        mod->snachandler = snachandler;
                    443: 
                    444:        return 0;
                    445: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>