Annotation of ChivanetAimPidgin/oscarprpl/src/c/family_oservice.c, revision 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 0x0001 - This is a very special group.  All connections support
        !            23:  * this group, as it does some particularly good things (like rate limiting).
        !            24:  */
        !            25: 
        !            26: #include "oscar.h"
        !            27: 
        !            28: #include "cipher.h"
        !            29: 
        !            30: /*
        !            31:  * Each time we make a FLAP connection to an oscar server the server gives
        !            32:  * us a list of rate classes.  Each rate class has different properties for
        !            33:  * how frequently we can send SNACs in that rate class before we become
        !            34:  * throttled or disconnected.
        !            35:  *
        !            36:  * The server also gives us a list of every available SNAC and tells us which
        !            37:  * rate class it's in.  There are a lot of different SNACs, so this list can be
        !            38:  * fairly large.  One important characteristic of these rate classes is that
        !            39:  * currently (and since at least 2004) most SNACs are in the same rate class.
        !            40:  *
        !            41:  * One optimization we can do to save memory is to only keep track of SNACs
        !            42:  * that are in classes other than this default rate class.  So if we try to
        !            43:  * look up a SNAC and it's not in our hash table then we can assume that it's
        !            44:  * in the default rate class.
        !            45:  */
        !            46: #define OSCAR_DEFAULT_RATECLASS 1
        !            47: 
        !            48: /* Subtype 0x0002 - Client Online */
        !            49: void
        !            50: aim_srv_clientready(OscarData *od, FlapConnection *conn)
        !            51: {
        !            52:        ByteStream bs;
        !            53:        aim_snacid_t snacid;
        !            54:        GSList *cur;
        !            55: 
        !            56:        byte_stream_new(&bs, 1142);
        !            57: 
        !            58:        /*
        !            59:         * Send only the tool versions that the server cares about (that it
        !            60:         * marked as supporting in the server ready SNAC).
        !            61:         */
        !            62:        for (cur = conn->groups; cur != NULL; cur = cur->next)
        !            63:        {
        !            64:                aim_module_t *mod;
        !            65: 
        !            66:                if ((mod = aim__findmodulebygroup(od, GPOINTER_TO_UINT(cur->data))))
        !            67:                {
        !            68:                        byte_stream_put16(&bs, mod->family);
        !            69:                        byte_stream_put16(&bs, mod->version);
        !            70:                        byte_stream_put16(&bs, mod->toolid);
        !            71:                        byte_stream_put16(&bs, mod->toolversion);
        !            72:                }
        !            73:        }
        !            74: 
        !            75:        snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0002, 0x0000, NULL, 0);
        !            76:        flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0002, snacid, &bs);
        !            77: 
        !            78:        byte_stream_destroy(&bs);
        !            79: }
        !            80: 
        !            81: /*
        !            82:  * Subtype 0x0003 - Host Online
        !            83:  *
        !            84:  * See comments in conn.c about how the group associations are supposed
        !            85:  * to work, and how they really work.
        !            86:  *
        !            87:  * This info probably doesn't even need to make it to the client.
        !            88:  *
        !            89:  * We don't actually call the client here.  This starts off the connection
        !            90:  * initialization routine required by all AIM connections.  The next time
        !            91:  * the client is called is the CONNINITDONE callback, which should be
        !            92:  * shortly after the rate information is acknowledged.
        !            93:  *
        !            94:  */
        !            95: static int
        !            96: hostonline(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
        !            97: {
        !            98:        int group;
        !            99: 
        !           100:        while (byte_stream_bytes_left(bs))
        !           101:        {
        !           102:                group = byte_stream_get16(bs);
        !           103:                conn->groups = g_slist_prepend(conn->groups, GUINT_TO_POINTER(group));
        !           104:        }
        !           105: 
        !           106:        /*
        !           107:         * Next step is in the Host Versions handler.
        !           108:         *
        !           109:         * Note that we must send this before we request rates, since
        !           110:         * the format of the rate information depends on the versions we
        !           111:         * give it.
        !           112:         *
        !           113:         */
        !           114:        aim_srv_setversions(od, conn);
        !           115: 
        !           116:        return 1;
        !           117: }
        !           118: 
        !           119: /* Subtype 0x0004 - Service request */
        !           120: void
        !           121: aim_srv_requestnew(OscarData *od, guint16 serviceid)
        !           122: {
        !           123:        FlapConnection *conn;
        !           124:        ByteStream bs;
        !           125:        aim_snacid_t snacid;
        !           126:        GSList *tlvlist = NULL;
        !           127: 
        !           128:        conn = flap_connection_findbygroup(od, SNAC_FAMILY_BOS);
        !           129:        if(!conn)
        !           130:                return;
        !           131: 
        !           132:        byte_stream_new(&bs, 6);
        !           133: 
        !           134:        byte_stream_put16(&bs, serviceid);
        !           135: 
        !           136:        if (od->use_ssl)
        !           137:                /* Request SSL Connection */
        !           138:                aim_tlvlist_add_noval(&tlvlist, 0x008c);
        !           139: 
        !           140:        aim_tlvlist_write(&bs, &tlvlist);
        !           141:        aim_tlvlist_free(tlvlist);
        !           142: 
        !           143:        snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0004, 0x0000, NULL, 0);
        !           144:        flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0004, snacid, &bs);
        !           145: 
        !           146:        byte_stream_destroy(&bs);
        !           147: }
        !           148: 
        !           149: /*
        !           150:  * Join a room of name roomname.  This is the first step to joining an
        !           151:  * already created room.  It's basically a Service Request for
        !           152:  * family 0x000e, with a little added on to specify the exchange and room
        !           153:  * name.
        !           154:  */
        !           155: int
        !           156: aim_chat_join(OscarData *od, guint16 exchange, const char *roomname, guint16 instance)
        !           157: {
        !           158:        FlapConnection *conn;
        !           159:        ByteStream bs;
        !           160:        aim_snacid_t snacid;
        !           161:        GSList *tlvlist = NULL;
        !           162:        struct chatsnacinfo csi;
        !           163: 
        !           164:        conn = flap_connection_findbygroup(od, SNAC_FAMILY_BOS);
        !           165:        if (!conn || !roomname || roomname[0] == '\0')
        !           166:                return -EINVAL;
        !           167: 
        !           168:        byte_stream_new(&bs, 506);
        !           169: 
        !           170:        memset(&csi, 0, sizeof(csi));
        !           171:        csi.exchange = exchange;
        !           172:        g_strlcpy(csi.name, roomname, sizeof(csi.name));
        !           173:        csi.instance = instance;
        !           174: 
        !           175:        /*
        !           176:         * Requesting service chat (0x000e)
        !           177:         */
        !           178:        byte_stream_put16(&bs, 0x000e);
        !           179: 
        !           180:        aim_tlvlist_add_chatroom(&tlvlist, 0x0001, exchange, roomname, instance);
        !           181: 
        !           182:        if (od->use_ssl)
        !           183:                /* Request SSL Connection */
        !           184:                aim_tlvlist_add_noval(&tlvlist, 0x008c);
        !           185: 
        !           186:        aim_tlvlist_write(&bs, &tlvlist);
        !           187:        aim_tlvlist_free(tlvlist);
        !           188: 
        !           189:        snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0004, 0x0000, &csi, sizeof(csi));
        !           190:        flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0004, snacid, &bs);
        !           191: 
        !           192:        byte_stream_destroy(&bs);
        !           193: 
        !           194:        return 0;
        !           195: }
        !           196: 
        !           197: /* Subtype 0x0005 - Redirect */
        !           198: static int
        !           199: redirect(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
        !           200: {
        !           201:        struct aim_redirect_data redir;
        !           202:        aim_rxcallback_t userfunc;
        !           203:        GSList *tlvlist;
        !           204:        aim_snac_t *origsnac = NULL;
        !           205:        int ret = 0;
        !           206: 
        !           207:        memset(&redir, 0, sizeof(redir));
        !           208: 
        !           209:        tlvlist = aim_tlvlist_read(bs);
        !           210: 
        !           211:        if (!aim_tlv_gettlv(tlvlist, 0x000d, 1) ||
        !           212:                        !aim_tlv_gettlv(tlvlist, 0x0005, 1) ||
        !           213:                        !aim_tlv_gettlv(tlvlist, 0x0006, 1)) {
        !           214:                aim_tlvlist_free(tlvlist);
        !           215:                return 0;
        !           216:        }
        !           217: 
        !           218:        redir.group = aim_tlv_get16(tlvlist, 0x000d, 1);
        !           219:        redir.ip = aim_tlv_getstr(tlvlist, 0x0005, 1);
        !           220:        redir.cookielen = aim_tlv_gettlv(tlvlist, 0x0006, 1)->length;
        !           221:        redir.cookie = (guchar *)aim_tlv_getstr(tlvlist, 0x0006, 1);
        !           222:        redir.ssl_cert_cn = aim_tlv_getstr(tlvlist, 0x008d, 1);
        !           223:        redir.use_ssl = aim_tlv_get8(tlvlist, 0x008e, 1);
        !           224: 
        !           225:        /* Fetch original SNAC so we can get csi if needed */
        !           226:        origsnac = aim_remsnac(od, snac->id);
        !           227: 
        !           228:        if ((redir.group == SNAC_FAMILY_CHAT) && origsnac) {
        !           229:                struct chatsnacinfo *csi = (struct chatsnacinfo *)origsnac->data;
        !           230: 
        !           231:                redir.chat.exchange = csi->exchange;
        !           232:                redir.chat.room = csi->name;
        !           233:                redir.chat.instance = csi->instance;
        !           234:        }
        !           235: 
        !           236:        if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
        !           237:                ret = userfunc(od, conn, frame, &redir);
        !           238: 
        !           239:        g_free((void *)redir.ip);
        !           240:        g_free((void *)redir.cookie);
        !           241:        g_free((void *)redir.ssl_cert_cn);
        !           242: 
        !           243:        if (origsnac)
        !           244:                g_free(origsnac->data);
        !           245:        g_free(origsnac);
        !           246: 
        !           247:        aim_tlvlist_free(tlvlist);
        !           248: 
        !           249:        return ret;
        !           250: }
        !           251: 
        !           252: /* Subtype 0x0006 - Request Rate Information. */
        !           253: void
        !           254: aim_srv_reqrates(OscarData *od, FlapConnection *conn)
        !           255: {
        !           256:        aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_OSERVICE, 0x0006);
        !           257: }
        !           258: 
        !           259: /*
        !           260:  * OSCAR defines several 'rate classes'.  Each class has separate
        !           261:  * rate limiting properties (limit level, alert level, disconnect
        !           262:  * level, etc), and a set of SNAC family/type pairs associated with
        !           263:  * it.  The rate classes, their limiting properties, and the definitions
        !           264:  * of which SNACs belong to which class are defined in the
        !           265:  * Rate Response packet at login to each host.
        !           266:  *
        !           267:  * Logically, all rate offenses within one class count against further
        !           268:  * offenses for other SNACs in the same class (ie, sending messages
        !           269:  * too fast will limit the number of user info requests you can send,
        !           270:  * since those two SNACs are in the same rate class).
        !           271:  *
        !           272:  * Since the rate classes are defined dynamically at login, the values
        !           273:  * below may change. But they seem to be fairly constant.
        !           274:  *
        !           275:  * Currently, BOS defines five rate classes, with the commonly used
        !           276:  * members as follows...
        !           277:  *
        !           278:  *  Rate class 0x0001:
        !           279:  *     - Everything thats not in any of the other classes
        !           280:  *
        !           281:  *  Rate class 0x0002:
        !           282:  *     - Buddy list add/remove
        !           283:  *     - Permit list add/remove
        !           284:  *     - Deny list add/remove
        !           285:  *
        !           286:  *  Rate class 0x0003:
        !           287:  *     - User information requests
        !           288:  *     - Outgoing ICBMs
        !           289:  *
        !           290:  *  Rate class 0x0004:
        !           291:  *     - A few unknowns: 2/9, 2/b, and f/2
        !           292:  *
        !           293:  *  Rate class 0x0005:
        !           294:  *     - Chat room create
        !           295:  *     - Outgoing chat ICBMs
        !           296:  *
        !           297:  * The only other thing of note is that class 5 (chat) has slightly looser
        !           298:  * limiting properties than class 3 (normal messages).  But thats just a
        !           299:  * small bit of trivia for you.
        !           300:  *
        !           301:  * The last thing that needs to be learned about the rate limiting
        !           302:  * system is how the actual numbers relate to the passing of time.  This
        !           303:  * seems to be a big mystery.
        !           304:  *
        !           305:  * See joscar's javadoc for the RateClassInfo class for a great
        !           306:  * explanation.  You might be able to find it at
        !           307:  * http://dscoder.com/RateClassInfo.html
        !           308:  */
        !           309: 
        !           310: static struct rateclass *
        !           311: rateclass_find(GSList *rateclasses, guint16 id)
        !           312: {
        !           313:        GSList *tmp;
        !           314: 
        !           315:        for (tmp = rateclasses; tmp != NULL; tmp = tmp->next)
        !           316:        {
        !           317:                struct rateclass *rateclass;
        !           318:                rateclass = tmp->data;
        !           319:                if (rateclass->classid == id)
        !           320:                        return rateclass;
        !           321:        }
        !           322: 
        !           323:        return NULL;
        !           324: }
        !           325: 
        !           326: /* Subtype 0x0007 - Rate Parameters */
        !           327: static int
        !           328: rateresp(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
        !           329: {
        !           330:        guint16 numclasses, i;
        !           331:        aim_rxcallback_t userfunc;
        !           332: 
        !           333:        /*
        !           334:         * First are the parameters for each rate class.
        !           335:         */
        !           336:        numclasses = byte_stream_get16(bs);
        !           337:        for (i = 0; i < numclasses; i++)
        !           338:        {
        !           339:                struct rateclass *rateclass;
        !           340:                guint32 delta;
        !           341:                struct timeval now;
        !           342: 
        !           343:                gettimeofday(&now, NULL);
        !           344:                rateclass = g_new(struct rateclass, 1);
        !           345: 
        !           346:                rateclass->classid = byte_stream_get16(bs);
        !           347:                rateclass->windowsize = byte_stream_get32(bs);
        !           348:                rateclass->clear = byte_stream_get32(bs);
        !           349:                rateclass->alert = byte_stream_get32(bs);
        !           350:                rateclass->limit = byte_stream_get32(bs);
        !           351:                rateclass->disconnect = byte_stream_get32(bs);
        !           352:                rateclass->current = byte_stream_get32(bs);
        !           353:                rateclass->max = byte_stream_get32(bs);
        !           354:                if (mod->version >= 3) {
        !           355:                        delta = byte_stream_get32(bs);
        !           356:                        rateclass->dropping_snacs = byte_stream_get8(bs);
        !           357:                } else {
        !           358:                        delta = 0;
        !           359:                        rateclass->dropping_snacs = 0;
        !           360:                }
        !           361: 
        !           362:                rateclass->last.tv_sec = now.tv_sec - delta / 1000;
        !           363:                rateclass->last.tv_usec = now.tv_usec - (delta % 1000) * 1000;
        !           364: 
        !           365:                conn->rateclasses = g_slist_prepend(conn->rateclasses, rateclass);
        !           366: 
        !           367:                if (rateclass->classid == OSCAR_DEFAULT_RATECLASS)
        !           368:                        conn->default_rateclass = rateclass;
        !           369:        }
        !           370:        conn->rateclasses = g_slist_reverse(conn->rateclasses);
        !           371: 
        !           372:        /*
        !           373:         * Then the members of each class.
        !           374:         */
        !           375:        for (i = 0; i < numclasses; i++)
        !           376:        {
        !           377:                guint16 classid, count;
        !           378:                struct rateclass *rateclass;
        !           379:                int j;
        !           380: 
        !           381:                classid = byte_stream_get16(bs);
        !           382:                count = byte_stream_get16(bs);
        !           383: 
        !           384:                if (classid == OSCAR_DEFAULT_RATECLASS) {
        !           385:                        /*
        !           386:                         * Don't bother adding these SNACs to the hash table.  See the
        !           387:                         * comment for OSCAR_DEFAULT_RATECLASS at the top of this file.
        !           388:                         */
        !           389:                        byte_stream_advance(bs, 4 * count);
        !           390:                        continue;
        !           391:                }
        !           392: 
        !           393:                rateclass = rateclass_find(conn->rateclasses, classid);
        !           394: 
        !           395:                for (j = 0; j < count; j++)
        !           396:                {
        !           397:                        guint16 group, subtype;
        !           398: 
        !           399:                        group = byte_stream_get16(bs);
        !           400:                        subtype = byte_stream_get16(bs);
        !           401: 
        !           402:                        if (rateclass != NULL)
        !           403:                                g_hash_table_insert(conn->rateclass_members,
        !           404:                                                GUINT_TO_POINTER((group << 16) + subtype),
        !           405:                                                rateclass);
        !           406:                }
        !           407:        }
        !           408: 
        !           409:        /*
        !           410:         * We don't pass the rate information up to the client, as it really
        !           411:         * doesn't care.  The information is stored in the connection, however
        !           412:         * so that we can do rate limiting management when sending SNACs.
        !           413:         */
        !           414: 
        !           415:        /*
        !           416:         * Subscribe to rate change information for all rate classes.
        !           417:         */
        !           418:        aim_srv_rates_addparam(od, conn);
        !           419: 
        !           420:        /*
        !           421:         * Finally, tell the client it's ready to go...
        !           422:         */
        !           423:        if ((userfunc = aim_callhandler(od, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE)))
        !           424:                userfunc(od, conn, frame);
        !           425: 
        !           426:        return 1;
        !           427: }
        !           428: 
        !           429: /* Subtype 0x0008 - Add Rate Parameter */
        !           430: void
        !           431: aim_srv_rates_addparam(OscarData *od, FlapConnection *conn)
        !           432: {
        !           433:        ByteStream bs;
        !           434:        aim_snacid_t snacid;
        !           435:        GSList *tmp;
        !           436: 
        !           437:        byte_stream_new(&bs, 502);
        !           438: 
        !           439:        for (tmp = conn->rateclasses; tmp != NULL; tmp = tmp->next)
        !           440:        {
        !           441:                struct rateclass *rateclass;
        !           442:                rateclass = tmp->data;
        !           443:                byte_stream_put16(&bs, rateclass->classid);
        !           444:        }
        !           445: 
        !           446:        snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0008, 0x0000, NULL, 0);
        !           447:        flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0008, snacid, &bs);
        !           448: 
        !           449:        byte_stream_destroy(&bs);
        !           450: }
        !           451: 
        !           452: /* Subtype 0x000a - Rate Change */
        !           453: static int
        !           454: ratechange(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
        !           455: {
        !           456:        guint16 code, classid;
        !           457:        struct rateclass *rateclass;
        !           458:        guint32 delta;
        !           459:        struct timeval now;
        !           460:        static const char *codes[5] = {
        !           461:                "invalid",
        !           462:                "change",
        !           463:                "warning",
        !           464:                "limit",
        !           465:                "limit cleared",
        !           466:        };
        !           467: 
        !           468:        gettimeofday(&now, NULL);
        !           469:        code = byte_stream_get16(bs);
        !           470:        classid = byte_stream_get16(bs);
        !           471: 
        !           472:        rateclass = rateclass_find(conn->rateclasses, classid);
        !           473:        if (rateclass == NULL)
        !           474:                /* This should never really happen */
        !           475:                return 0;
        !           476: 
        !           477:        rateclass->windowsize = byte_stream_get32(bs);
        !           478:        rateclass->clear = byte_stream_get32(bs);
        !           479:        rateclass->alert = byte_stream_get32(bs);
        !           480:        rateclass->limit = byte_stream_get32(bs);
        !           481:        rateclass->disconnect = byte_stream_get32(bs);
        !           482:        rateclass->current = byte_stream_get32(bs);
        !           483:        rateclass->max = byte_stream_get32(bs);
        !           484:        if (mod->version >= 3) {
        !           485:                delta = byte_stream_get32(bs);
        !           486:                rateclass->dropping_snacs = byte_stream_get8(bs);
        !           487:        } else {
        !           488:                delta = 0;
        !           489:                rateclass->dropping_snacs = 0;
        !           490:        }
        !           491: 
        !           492:        rateclass->last.tv_sec = now.tv_sec - delta / 1000;
        !           493:        rateclass->last.tv_usec = now.tv_usec - (delta % 1000) * 1000;
        !           494: 
        !           495:        purple_debug_misc("oscar", "rate %s (param ID 0x%04hx): curavg = %u, "
        !           496:                        "maxavg = %u, alert at %u, clear warning at %u, limit at %u, "
        !           497:                        "disconnect at %u, delta is %u, dropping is %u (window size = %u)\n",
        !           498:                        (code < 5) ? codes[code] : codes[0], rateclass->classid,
        !           499:                        rateclass->current, rateclass->max, rateclass->alert,
        !           500:                        rateclass->clear, rateclass->limit, rateclass->disconnect,
        !           501:                        delta, rateclass->dropping_snacs, rateclass->windowsize);
        !           502: 
        !           503:        if (code == AIM_RATE_CODE_LIMIT) {
        !           504:                purple_debug_warning("oscar",  "The last action you attempted "
        !           505:                                "could not be performed because you are over the rate "
        !           506:                                "limit. Please wait 10 seconds and try again.\n");
        !           507:        }
        !           508: 
        !           509:        return 1;
        !           510: }
        !           511: 
        !           512: /*
        !           513:  * How Migrations work.
        !           514:  *
        !           515:  * The server sends a Server Pause message, which the client should respond to
        !           516:  * with a Server Pause Ack, which contains the families it needs on this
        !           517:  * connection. The server will send a Migration Notice with an IP address, and
        !           518:  * then disconnect. Next the client should open the connection and send the
        !           519:  * cookie.  Repeat the normal login process and pretend this never happened.
        !           520:  *
        !           521:  * The Server Pause contains no data.
        !           522:  *
        !           523:  */
        !           524: 
        !           525: /* Subtype 0x000b - Service Pause */
        !           526: static int
        !           527: serverpause(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
        !           528: {
        !           529:        int ret = 0;
        !           530:        aim_rxcallback_t userfunc;
        !           531: 
        !           532:        if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
        !           533:                ret = userfunc(od, conn, frame);
        !           534: 
        !           535:        return ret;
        !           536: }
        !           537: 
        !           538: /* Subtype 0x000d - Service Resume */
        !           539: static int
        !           540: serverresume(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
        !           541: {
        !           542:        int ret = 0;
        !           543:        aim_rxcallback_t userfunc;
        !           544: 
        !           545:        if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
        !           546:                ret = userfunc(od, conn, frame);
        !           547: 
        !           548:        return ret;
        !           549: }
        !           550: 
        !           551: /* Subtype 0x000e - Request self-info */
        !           552: void
        !           553: aim_srv_reqpersonalinfo(OscarData *od, FlapConnection *conn)
        !           554: {
        !           555:        aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_OSERVICE, 0x000e);
        !           556: }
        !           557: 
        !           558: /* Subtype 0x000f - Self User Info */
        !           559: static int
        !           560: selfinfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
        !           561: {
        !           562:        int ret = 0;
        !           563:        aim_rxcallback_t userfunc;
        !           564:        aim_userinfo_t userinfo;
        !           565: 
        !           566:        aim_info_extract(od, bs, &userinfo);
        !           567: 
        !           568:        if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
        !           569:                ret = userfunc(od, conn, frame, &userinfo);
        !           570: 
        !           571:        aim_info_free(&userinfo);
        !           572: 
        !           573:        return ret;
        !           574: }
        !           575: 
        !           576: /* Subtype 0x0010 - Evil Notification */
        !           577: static int
        !           578: evilnotify(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
        !           579: {
        !           580:        int ret = 0;
        !           581:        aim_rxcallback_t userfunc;
        !           582:        guint16 newevil;
        !           583:        aim_userinfo_t userinfo;
        !           584: 
        !           585:        memset(&userinfo, 0, sizeof(aim_userinfo_t));
        !           586: 
        !           587:        newevil = byte_stream_get16(bs);
        !           588: 
        !           589:        if (byte_stream_bytes_left(bs))
        !           590:                aim_info_extract(od, bs, &userinfo);
        !           591: 
        !           592:        if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
        !           593:                ret = userfunc(od, conn, frame, newevil, &userinfo);
        !           594: 
        !           595:        aim_info_free(&userinfo);
        !           596: 
        !           597:        return ret;
        !           598: }
        !           599: 
        !           600: /*
        !           601:  * Subtype 0x0011 - Idle Notification
        !           602:  *
        !           603:  * Should set your current idle time in seconds.  Note that this should
        !           604:  * never be called consecutively with a non-zero idle time.  That makes
        !           605:  * OSCAR do funny things.  Instead, just set it once you go idle, and then
        !           606:  * call it again with zero when you're back.
        !           607:  *
        !           608:  */
        !           609: void
        !           610: aim_srv_setidle(OscarData *od, guint32 idletime)
        !           611: {
        !           612:        FlapConnection *conn;
        !           613: 
        !           614:        conn = flap_connection_findbygroup(od, SNAC_FAMILY_BOS);
        !           615:        if(!conn)
        !           616:                return;
        !           617: 
        !           618:        aim_genericreq_l(od, conn, SNAC_FAMILY_OSERVICE, 0x0011, &idletime);
        !           619: }
        !           620: 
        !           621: /*
        !           622:  * Subtype 0x0012 - Service Migrate
        !           623:  *
        !           624:  * This is the final SNAC sent on the original connection during a migration.
        !           625:  * It contains the IP and cookie used to connect to the new server, and
        !           626:  * optionally a list of the SNAC groups being migrated.
        !           627:  *
        !           628:  */
        !           629: static int
        !           630: migrate(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
        !           631: {
        !           632:        aim_rxcallback_t userfunc;
        !           633:        int ret = 0;
        !           634:        guint16 groupcount, i;
        !           635:        GSList *tlvlist;
        !           636:        char *ip = NULL;
        !           637:        aim_tlv_t *cktlv;
        !           638: 
        !           639:        /*
        !           640:         * Apparently there's some fun stuff that can happen right here. The
        !           641:         * migration can actually be quite selective about what groups it
        !           642:         * moves to the new server.  When not all the groups for a connection
        !           643:         * are migrated, or they are all migrated but some groups are moved
        !           644:         * to a different server than others, it is called a bifurcated
        !           645:         * migration.
        !           646:         *
        !           647:         * Let's play dumb and not support that.
        !           648:         *
        !           649:         */
        !           650:        groupcount = byte_stream_get16(bs);
        !           651:        for (i = 0; i < groupcount; i++) {
        !           652:                guint16 group;
        !           653: 
        !           654:                group = byte_stream_get16(bs);
        !           655: 
        !           656:                purple_debug_misc("oscar", "bifurcated migration unsupported -- group 0x%04x\n", group);
        !           657:        }
        !           658: 
        !           659:        tlvlist = aim_tlvlist_read(bs);
        !           660: 
        !           661:        if (aim_tlv_gettlv(tlvlist, 0x0005, 1))
        !           662:                ip = aim_tlv_getstr(tlvlist, 0x0005, 1);
        !           663: 
        !           664:        cktlv = aim_tlv_gettlv(tlvlist, 0x0006, 1);
        !           665: 
        !           666:        if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
        !           667:                ret = userfunc(od, conn, frame, ip, cktlv ? cktlv->value : NULL);
        !           668: 
        !           669:        aim_tlvlist_free(tlvlist);
        !           670:        g_free(ip);
        !           671: 
        !           672:        return ret;
        !           673: }
        !           674: 
        !           675: /* Subtype 0x0013 - Message of the Day */
        !           676: static int
        !           677: motd(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
        !           678: {
        !           679:        aim_rxcallback_t userfunc;
        !           680:        char *msg = NULL;
        !           681:        int ret = 0;
        !           682:        GSList *tlvlist;
        !           683:        guint16 id;
        !           684: 
        !           685:        /*
        !           686:         * Code.
        !           687:         *
        !           688:         * Valid values:
        !           689:         *   1 Mandatory upgrade
        !           690:         *   2 Advisory upgrade
        !           691:         *   3 System bulletin
        !           692:         *   4 Nothing's wrong ("top o the world" -- normal)
        !           693:         *   5 Lets-break-something.
        !           694:         *
        !           695:         */
        !           696:        id = byte_stream_get16(bs);
        !           697: 
        !           698:        /*
        !           699:         * TLVs follow
        !           700:         */
        !           701:        tlvlist = aim_tlvlist_read(bs);
        !           702: 
        !           703:        msg = aim_tlv_getstr(tlvlist, 0x000b, 1);
        !           704: 
        !           705:        if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
        !           706:                ret = userfunc(od, conn, frame, id, msg);
        !           707: 
        !           708:        g_free(msg);
        !           709: 
        !           710:        aim_tlvlist_free(tlvlist);
        !           711: 
        !           712:        return ret;
        !           713: }
        !           714: 
        !           715: /*
        !           716:  * Subtype 0x0017 - Set client versions
        !           717:  *
        !           718:  * If you've seen the clientonline/clientready SNAC you're probably
        !           719:  * wondering what the point of this one is.  And that point seems to be
        !           720:  * that the versions in the client online SNAC are sent too late for the
        !           721:  * server to be able to use them to change the protocol for the earlier
        !           722:  * login packets (client versions are sent right after Host Online is
        !           723:  * received, but client online versions aren't sent until quite a bit later).
        !           724:  * We can see them already making use of this by changing the format of
        !           725:  * the rate information based on what version of group 1 we advertise here.
        !           726:  *
        !           727:  */
        !           728: void
        !           729: aim_srv_setversions(OscarData *od, FlapConnection *conn)
        !           730: {
        !           731:        ByteStream bs;
        !           732:        aim_snacid_t snacid;
        !           733:        GSList *cur;
        !           734: 
        !           735:        byte_stream_new(&bs, 1142);
        !           736: 
        !           737:        /*
        !           738:         * Send only the versions that the server cares about (that it
        !           739:         * marked as supporting in the server ready SNAC).
        !           740:         */
        !           741:        for (cur = conn->groups; cur != NULL; cur = cur->next)
        !           742:        {
        !           743:                aim_module_t *mod;
        !           744: 
        !           745:                if ((mod = aim__findmodulebygroup(od, GPOINTER_TO_UINT(cur->data))))
        !           746:                {
        !           747:                        byte_stream_put16(&bs, mod->family);
        !           748:                        byte_stream_put16(&bs, mod->version);
        !           749:                }
        !           750:        }
        !           751: 
        !           752:        snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0017, 0x0000, NULL, 0);
        !           753:        flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0017, snacid, &bs);
        !           754: 
        !           755:        byte_stream_destroy(&bs);
        !           756: }
        !           757: 
        !           758: /* Subtype 0x0018 - Host versions */
        !           759: static int
        !           760: hostversions(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
        !           761: {
        !           762:        int vercount;
        !           763:        guint8 *versions;
        !           764: 
        !           765:        /* This is frivolous. (Thank you SmarterChild.) */
        !           766:        vercount = byte_stream_bytes_left(bs)/4;
        !           767: 
        !           768:        /* XXX: vercount probably should be used for reading versions. */
        !           769:        (void)vercount;
        !           770:        versions = byte_stream_getraw(bs, byte_stream_bytes_left(bs));
        !           771:        g_free(versions);
        !           772: 
        !           773:        /*
        !           774:         * Now request rates.
        !           775:         */
        !           776:        aim_srv_reqrates(od, conn);
        !           777: 
        !           778:        return 1;
        !           779: }
        !           780: 
        !           781: /**
        !           782:  * Subtype 0x001e - Extended Status/Extra Info.
        !           783:  *
        !           784:  * These settings are transient, not server-stored (i.e. they only
        !           785:  * apply to this session, and must be re-set the next time you sign
        !           786:  * on).
        !           787:  *
        !           788:  * You can set your ICQ status (available, away, do not disturb,
        !           789:  * etc.), or whether your IP address should be hidden or not, or
        !           790:  * if your status is visible on ICQ web sites, and you can set
        !           791:  * your IP address info and what not.
        !           792:  *
        !           793:  * You can also set your "available" message.  This is currently
        !           794:  * only supported by iChat, Purple and other 3rd party clients.
        !           795:  *
        !           796:  * These are the same TLVs seen in user info.  You can
        !           797:  * also set 0x0008 and 0x000c.
        !           798:  */
        !           799: int
        !           800: aim_srv_setextrainfo(OscarData *od,
        !           801:                gboolean seticqstatus, guint32 icqstatus,
        !           802:                gboolean setstatusmsg, const char *statusmsg, const char *itmsurl)
        !           803: {
        !           804:        FlapConnection *conn;
        !           805:        ByteStream bs;
        !           806:        aim_snacid_t snacid;
        !           807:        GSList *tlvlist = NULL;
        !           808: 
        !           809:        if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
        !           810:                return -EINVAL;
        !           811: 
        !           812:        if (seticqstatus)
        !           813:        {
        !           814:                aim_tlvlist_add_32(&tlvlist, 0x0006, icqstatus |
        !           815:                                AIM_ICQ_STATE_HIDEIP | AIM_ICQ_STATE_DIRECTREQUIREAUTH);
        !           816:        }
        !           817: 
        !           818:        if (setstatusmsg)
        !           819:        {
        !           820:                size_t statusmsglen, itmsurllen;
        !           821:                ByteStream tmpbs;
        !           822: 
        !           823:                statusmsglen = (statusmsg != NULL) ? strlen(statusmsg) : 0;
        !           824:                itmsurllen = (itmsurl != NULL) ? strlen(itmsurl) : 0;
        !           825: 
        !           826:                byte_stream_new(&tmpbs, statusmsglen + 8 + itmsurllen + 8);
        !           827:                byte_stream_put_bart_asset_str(&tmpbs, 0x0002, statusmsg);
        !           828:                byte_stream_put_bart_asset_str(&tmpbs, 0x0009, itmsurl);
        !           829: 
        !           830:                aim_tlvlist_add_raw(&tlvlist, 0x001d,
        !           831:                                byte_stream_curpos(&tmpbs), tmpbs.data);
        !           832:                byte_stream_destroy(&tmpbs);
        !           833:        }
        !           834: 
        !           835:        byte_stream_new(&bs, aim_tlvlist_size(tlvlist));
        !           836: 
        !           837:        aim_tlvlist_write(&bs, &tlvlist);
        !           838:        aim_tlvlist_free(tlvlist);
        !           839: 
        !           840:        snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x001e, 0x0000, NULL, 0);
        !           841:        flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x001e, snacid, &bs);
        !           842: 
        !           843:        byte_stream_destroy(&bs);
        !           844: 
        !           845:        return 0;
        !           846: }
        !           847: 
        !           848: /* Send dummy DC (direct connect) information to the server.
        !           849:  * Direct connect is ICQ's counterpart for AIM's DirectIM,
        !           850:  * as far as I can tell. Anyway, we don't support it;
        !           851:  * the reason to send this packet is that some clients
        !           852:  * (Miranda, QIP) won't send us channel 2 ICBM messages
        !           853:  * unless we specify DC version >= 8.
        !           854:  *
        !           855:  * See #12044 for more information.
        !           856:  */
        !           857: void
        !           858: aim_srv_set_dc_info(OscarData *od)
        !           859: {
        !           860:        FlapConnection *conn;
        !           861: 
        !           862:        ByteStream bs, tlv0c;
        !           863:        aim_snacid_t snacid;
        !           864:        GSList *tlvlist = NULL;
        !           865: 
        !           866:        /* http://iserverd.khstu.ru/oscar/snac_01_1e.html has a nice analysis of what goes in 0xc tlv.
        !           867:         * Kopete sends a dummy DC info, too, so I just copied the values from them.
        !           868:         */
        !           869:        byte_stream_new(&tlv0c, 4*2 + 1 + 2 + 4*6 + 2);
        !           870:        byte_stream_put32(&tlv0c, 0x0);
        !           871:        byte_stream_put32(&tlv0c, 0x0);
        !           872:        byte_stream_put8(&tlv0c, 0x0); /* We don't support DC */
        !           873:        byte_stream_put16(&tlv0c, 8); /* DC version */
        !           874:        byte_stream_put32(&tlv0c, 0x0);
        !           875:        byte_stream_put32(&tlv0c, 0x50);
        !           876:        byte_stream_put32(&tlv0c, 0x3);
        !           877:        byte_stream_put32(&tlv0c, 0x0);
        !           878:        byte_stream_put32(&tlv0c, 0x0);
        !           879:        byte_stream_put32(&tlv0c, 0x0);
        !           880:        byte_stream_put16(&tlv0c, 0x0);
        !           881:        aim_tlvlist_add_raw(&tlvlist, 0x000c, byte_stream_curpos(&tlv0c), tlv0c.data);
        !           882:        byte_stream_destroy(&tlv0c);
        !           883: 
        !           884:        byte_stream_new(&bs, aim_tlvlist_size(tlvlist));
        !           885:        aim_tlvlist_write(&bs, &tlvlist);
        !           886:        aim_tlvlist_free(tlvlist);
        !           887: 
        !           888:        snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x001e, 0x0000, NULL, 0);
        !           889:        conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
        !           890:        g_warn_if_fail(conn != NULL);
        !           891:        if (conn) {
        !           892:                flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE,
        !           893:                        0x001e, snacid, &bs);
        !           894:        }
        !           895: 
        !           896:        byte_stream_destroy(&bs);
        !           897: }
        !           898: 
        !           899: /**
        !           900:  * Starting this past week (26 Mar 2001, say), AOL has started sending
        !           901:  * this nice little extra SNAC.  AFAIK, it has never been used until now.
        !           902:  *
        !           903:  * The request contains eight bytes.  The first four are an offset, the
        !           904:  * second four are a length.
        !           905:  *
        !           906:  * The offset is an offset into aim.exe when it is mapped during execution
        !           907:  * on Win32.  So far, AOL has only been requesting bytes in static regions
        !           908:  * of memory.  (I won't put it past them to start requesting data in
        !           909:  * less static regions -- regions that are initialized at run time, but still
        !           910:  * before the client receives this request.)
        !           911:  *
        !           912:  * When the client receives the request, it adds it to the current ds
        !           913:  * (0x00400000) and dereferences it, copying the data into a buffer which
        !           914:  * it then runs directly through the MD5 hasher.  The 16 byte output of
        !           915:  * the hash is then sent back to the server.
        !           916:  *
        !           917:  * If the client does not send any data back, or the data does not match
        !           918:  * the data that the specific client should have, the client will get the
        !           919:  * following message from "AOL Instant Messenger":
        !           920:  *    "You have been disconnected from the AOL Instant Message Service (SM)
        !           921:  *     for accessing the AOL network using unauthorized software.  You can
        !           922:  *     download a FREE, fully featured, and authorized client, here
        !           923:  *     http://www.aol.com/aim/download2.html"
        !           924:  * The connection is then closed, receiving disconnect code 1, URL
        !           925:  * http://www.aim.aol.com/errors/USER_LOGGED_OFF_NEW_LOGIN.html.
        !           926:  *
        !           927:  * Note, however, that numerous inconsistencies can cause the above error,
        !           928:  * not just sending back a bad hash.  Do not immediatly suspect this code
        !           929:  * if you get disconnected.  AOL and the open/free software community have
        !           930:  * played this game for a couple years now, generating the above message
        !           931:  * on numerous ocassions.
        !           932:  *
        !           933:  * Anyway, neener.  We win again.
        !           934:  *
        !           935:  */
        !           936: /* Subtype 0x001f - Client verification */
        !           937: static int
        !           938: memrequest(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
        !           939: {
        !           940:        int ret = 0;
        !           941:        aim_rxcallback_t userfunc;
        !           942:        guint32 offset, len;
        !           943:        GSList *tlvlist;
        !           944:        char *modname;
        !           945: 
        !           946:        offset = byte_stream_get32(bs);
        !           947:        len = byte_stream_get32(bs);
        !           948:        tlvlist = aim_tlvlist_read(bs);
        !           949: 
        !           950:        modname = aim_tlv_getstr(tlvlist, 0x0001, 1);
        !           951: 
        !           952:        purple_debug_info("oscar", "Got memory request for data at 0x%08x (%u bytes) of requested %s\n", offset, len, modname ? modname : "aim.exe");
        !           953: 
        !           954:        if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
        !           955:                ret = userfunc(od, conn, frame, offset, len, modname);
        !           956: 
        !           957:        g_free(modname);
        !           958:        aim_tlvlist_free(tlvlist);
        !           959: 
        !           960:        return ret;
        !           961: }
        !           962: 
        !           963: /* Subtype 0x0020 - Client verification reply */
        !           964: int
        !           965: aim_sendmemblock(OscarData *od, FlapConnection *conn, guint32 offset, guint32 len, const guint8 *buf, guint8 flag)
        !           966: {
        !           967:        ByteStream bs;
        !           968:        aim_snacid_t snacid;
        !           969: 
        !           970:        if (!od || !conn)
        !           971:                return -EINVAL;
        !           972: 
        !           973:        byte_stream_new(&bs, 2+16);
        !           974: 
        !           975:        byte_stream_put16(&bs, 0x0010); /* md5 is always 16 bytes */
        !           976: 
        !           977:        if ((flag == AIM_SENDMEMBLOCK_FLAG_ISHASH) && buf && (len == 0x10)) { /* we're getting a hash */
        !           978: 
        !           979:                byte_stream_putraw(&bs, buf, 0x10);
        !           980: 
        !           981:        } else if (buf && (len > 0)) { /* use input buffer */
        !           982:                PurpleCipherContext *context;
        !           983:                guchar digest[16];
        !           984: 
        !           985:                context = purple_cipher_context_new_by_name("md5", NULL);
        !           986:                purple_cipher_context_append(context, buf, len);
        !           987:                purple_cipher_context_digest(context, 16, digest, NULL);
        !           988:                purple_cipher_context_destroy(context);
        !           989: 
        !           990:                byte_stream_putraw(&bs, digest, 0x10);
        !           991: 
        !           992:        } else if (len == 0) { /* no length, just hash NULL (buf is optional) */
        !           993:                PurpleCipherContext *context;
        !           994:                guchar digest[16];
        !           995:                guint8 nil = '\0';
        !           996: 
        !           997:                /*
        !           998:                 * I'm not sure if we really need the empty append with the
        !           999:                 * new MD5 functions, so I'll leave it in, just in case.
        !          1000:                 */
        !          1001:                context = purple_cipher_context_new_by_name("md5", NULL);
        !          1002:                purple_cipher_context_append(context, &nil, 0);
        !          1003:                purple_cipher_context_digest(context, 16, digest, NULL);
        !          1004:                purple_cipher_context_destroy(context);
        !          1005: 
        !          1006:                byte_stream_putraw(&bs, digest, 0x10);
        !          1007: 
        !          1008:        } else {
        !          1009: 
        !          1010:                /*
        !          1011:                 * This data is correct for AIM 3.5.1670.
        !          1012:                 *
        !          1013:                 * Using these blocks is as close to "legal" as you can get
        !          1014:                 * without using an AIM binary.
        !          1015:                 *
        !          1016:                 */
        !          1017:                if ((offset == 0x03ffffff) && (len == 0x03ffffff)) {
        !          1018: 
        !          1019: #if 1 /* with "AnrbnrAqhfzcd" */
        !          1020:                        byte_stream_put32(&bs, 0x44a95d26);
        !          1021:                        byte_stream_put32(&bs, 0xd2490423);
        !          1022:                        byte_stream_put32(&bs, 0x93b8821f);
        !          1023:                        byte_stream_put32(&bs, 0x51c54b01);
        !          1024: #else /* no filename */
        !          1025:                        byte_stream_put32(&bs, 0x1df8cbae);
        !          1026:                        byte_stream_put32(&bs, 0x5523b839);
        !          1027:                        byte_stream_put32(&bs, 0xa0e10db3);
        !          1028:                        byte_stream_put32(&bs, 0xa46d3b39);
        !          1029: #endif
        !          1030: 
        !          1031:                } else
        !          1032:                        purple_debug_warning("oscar", "sendmemblock: unknown hash request\n");
        !          1033: 
        !          1034:        }
        !          1035: 
        !          1036:        snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0020, 0x0000, NULL, 0);
        !          1037:        flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0020, snacid, &bs);
        !          1038: 
        !          1039:        byte_stream_destroy(&bs);
        !          1040: 
        !          1041:        return 0;
        !          1042: }
        !          1043: 
        !          1044: /*
        !          1045:  * Subtype 0x0021 - Receive our extended status
        !          1046:  *
        !          1047:  * This is used for iChat's "available" messages, and maybe ICQ extended
        !          1048:  * status messages?  It's also used to tell the client whether or not it
        !          1049:  * needs to upload an SSI buddy icon... who engineers this stuff, anyway?
        !          1050:  */
        !          1051: static int
        !          1052: aim_parse_extstatus(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
        !          1053: {
        !          1054:        guint16 type = byte_stream_get16(bs);
        !          1055:        if (type == 0x0000 || type == 0x0001) {
        !          1056:                /* buddy icon checksum */
        !          1057:                /* not sure what the difference between 1 and 0 is */
        !          1058:                guint8 flags = byte_stream_get8(bs);
        !          1059:                guint8 length = byte_stream_get8(bs);
        !          1060:                guint8 *md5 = byte_stream_getraw(bs, length);
        !          1061: 
        !          1062:                if ((flags == 0x00) || (flags == 0x41)) {
        !          1063:                        if (!flap_connection_getbytype(od, SNAC_FAMILY_BART) && !od->iconconnecting) {
        !          1064:                                od->iconconnecting = TRUE;
        !          1065:                                od->set_icon = TRUE;
        !          1066:                                aim_srv_requestnew(od, SNAC_FAMILY_BART);
        !          1067:                        } else {
        !          1068:                                PurpleAccount *account = purple_connection_get_account(od->gc);
        !          1069:                                PurpleStoredImage *img = purple_buddy_icons_find_account_icon(account);
        !          1070:                                if (img == NULL) {
        !          1071:                                        aim_ssi_delicon(od);
        !          1072:                                } else {
        !          1073: 
        !          1074:                                        purple_debug_info("oscar",
        !          1075:                                                                        "Uploading icon to icon server\n");
        !          1076:                                        aim_bart_upload(od, purple_imgstore_get_data(img),
        !          1077:                                                        purple_imgstore_get_size(img));
        !          1078:                                        purple_imgstore_unref(img);
        !          1079:                                }
        !          1080:                        }
        !          1081:                } else if (flags == 0x81) {
        !          1082:                        PurpleAccount *account = purple_connection_get_account(od->gc);
        !          1083:                        PurpleStoredImage *img = purple_buddy_icons_find_account_icon(account);
        !          1084:                        if (img == NULL)
        !          1085:                                aim_ssi_delicon(od);
        !          1086:                        else {
        !          1087:                                aim_ssi_seticon(od, md5, length);
        !          1088:                                purple_imgstore_unref(img);
        !          1089:                        }
        !          1090:                }
        !          1091: 
        !          1092:                g_free(md5);
        !          1093:        }
        !          1094: 
        !          1095:        return 0;
        !          1096: }
        !          1097: 
        !          1098: static int
        !          1099: snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
        !          1100: {
        !          1101:        if (snac->subtype == 0x0003)
        !          1102:                return hostonline(od, conn, mod, frame, snac, bs);
        !          1103:        else if (snac->subtype == 0x0005)
        !          1104:                return redirect(od, conn, mod, frame, snac, bs);
        !          1105:        else if (snac->subtype == 0x0007)
        !          1106:                return rateresp(od, conn, mod, frame, snac, bs);
        !          1107:        else if (snac->subtype == 0x000a)
        !          1108:                return ratechange(od, conn, mod, frame, snac, bs);
        !          1109:        else if (snac->subtype == 0x000b)
        !          1110:                return serverpause(od, conn, mod, frame, snac, bs);
        !          1111:        else if (snac->subtype == 0x000d)
        !          1112:                return serverresume(od, conn, mod, frame, snac, bs);
        !          1113:        else if (snac->subtype == 0x000f)
        !          1114:                return selfinfo(od, conn, mod, frame, snac, bs);
        !          1115:        else if (snac->subtype == 0x0010)
        !          1116:                return evilnotify(od, conn, mod, frame, snac, bs);
        !          1117:        else if (snac->subtype == 0x0012)
        !          1118:                return migrate(od, conn, mod, frame, snac, bs);
        !          1119:        else if (snac->subtype == 0x0013)
        !          1120:                return motd(od, conn, mod, frame, snac, bs);
        !          1121:        else if (snac->subtype == 0x0018)
        !          1122:                return hostversions(od, conn, mod, frame, snac, bs);
        !          1123:        else if (snac->subtype == 0x001f)
        !          1124:                return memrequest(od, conn, mod, frame, snac, bs);
        !          1125:        else if (snac->subtype == 0x0021)
        !          1126:                return aim_parse_extstatus(od, conn, mod, frame, snac, bs);
        !          1127: 
        !          1128:        return 0;
        !          1129: }
        !          1130: 
        !          1131: int service_modfirst(OscarData *od, aim_module_t *mod)
        !          1132: {
        !          1133:        mod->family = SNAC_FAMILY_OSERVICE;
        !          1134:        mod->version = 0x0003;
        !          1135:        mod->toolid = 0x0110;
        !          1136:        mod->toolversion = 0x0629;
        !          1137:        mod->flags = 0;
        !          1138:        strncpy(mod->name, "oservice", sizeof(mod->name));
        !          1139:        mod->snachandler = snachandler;
        !          1140: 
        !          1141:        return 0;
        !          1142: }

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