Annotation of ChivanetAimPidgin/oscarprpl/src/c/family_feedbag.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 0x0013 - Server-Side/Stored Information.
        !            23:  *
        !            24:  * Relatively new facility that allows certain types of information, such as
        !            25:  * a user's buddy list, permit/deny list, and permit/deny preferences, to be
        !            26:  * stored on the server, so that they can be accessed from any client.
        !            27:  *
        !            28:  * We keep 2 copies of SSI data:
        !            29:  * 1) An exact copy of what is stored on the AIM servers.
        !            30:  * 2) A local copy that we make changes to, and then send diffs
        !            31:  *    between this and the exact copy to keep them in sync.
        !            32:  *
        !            33:  * All the "aim_ssi_itemlist_bleh" functions near the top just modify the list
        !            34:  * that is given to them (i.e. they don't send SNACs).
        !            35:  *
        !            36:  * The SNAC sending and receiving functions are lower down in the file, and
        !            37:  * they're simpler.  They are in the order of the subtypes they deal with,
        !            38:  * starting with the request rights function (subtype 0x0002), then parse
        !            39:  * rights (subtype 0x0003), then--well, you get the idea.
        !            40:  *
        !            41:  * This is entirely too complicated.
        !            42:  * You don't know the half of it.
        !            43:  *
        !            44:  */
        !            45: 
        !            46: #include "oscar.h"
        !            47: #include "debug.h"
        !            48: 
        !            49: static int aim_ssi_addmoddel(OscarData *od);
        !            50: 
        !            51: /**
        !            52:  * List types based on http://dev.aol.com/aim/oscar/#FEEDBAG (archive.org)
        !            53:  * and http://iserverd.khstu.ru/oscar/ssi_item.html
        !            54:  *
        !            55:  * @param type The type of a list item as integer number, as provided by an aim_ssi_item struct.
        !            56:  * @return Returns the name of the item type as a character string.
        !            57:  */
        !            58: static const gchar*
        !            59: aim_ssi_type_to_string(guint16 type)
        !            60: {
        !            61:        struct TypeStringPair
        !            62:        {
        !            63:                guint16 type;
        !            64:                const gchar *string;
        !            65:        };
        !            66:        static const struct TypeStringPair type_strings[] = {
        !            67:                { 0x0000, "Buddy" },
        !            68:                { 0x0001, "Group" },
        !            69:                { 0x0002, "Permit/Visible" },
        !            70:                { 0x0003, "Deny/Invisible" },
        !            71:                { 0x0004, "PDInfo" },
        !            72:                { 0x0005, "PresencePrefs" }, 
        !            73:                { 0x0006, "Non-Buddy Info" },
        !            74:                { 0x0009, "ClientPrefs" },
        !            75:                { 0x000e, "ICQDeny/Ignore" },
        !            76:                { 0x0014, "Buddy Icon" }, 
        !            77:                { 0x0015, "Recent Buddies" },
        !            78:                { 0x0019, "Non-Buddy" },
        !            79:                { 0x001d, "Vanity Info" },
        !            80:                { 0x0020, "ICQ-MDir" },
        !            81:                { 0x0029, "Facebook" },
        !            82:        };
        !            83:        size_t i;
        !            84:        for (i = 0; i < G_N_ELEMENTS(type_strings); i++) {
        !            85:                if (type_strings[i].type == type) {
        !            86:                        return type_strings[i].string;
        !            87:                }
        !            88:        }
        !            89:        return "unknown";
        !            90: }
        !            91: 
        !            92: /** For debug log output: Appends a line containing information about a given list item to a string.
        !            93:  *
        !            94:  * @param str String to which the line will be appended.
        !            95:  * @param prefix A string which will be prepended to the line.
        !            96:  * @param item List item from which information is extracted.
        !            97:  */
        !            98: static void
        !            99: aim_ssi_item_debug_append(GString *str, char *prefix, struct aim_ssi_item *item)
        !           100: {
        !           101:        g_string_append_printf(str, 
        !           102:                "%s gid=0x%04hx, bid=0x%04hx, list_type=0x%04hx [%s], name=%s.\n",
        !           103:                prefix, item->gid, item->bid, item->type, aim_ssi_type_to_string(item->type),
        !           104:                item->name ? item->name : "(null)");
        !           105: }
        !           106: 
        !           107: /**
        !           108:  * Locally rebuild the 0x00c8 TLV in the additional data of the given group.
        !           109:  *
        !           110:  * @param list A pointer to a pointer to the current list of items.
        !           111:  * @param name A null terminated string containing the group name, or NULL
        !           112:  *        if you want to modify the master group.
        !           113:  * @return Return a pointer to the modified item.
        !           114:  */
        !           115: static void
        !           116: aim_ssi_itemlist_rebuildgroup(struct aim_ssi_item *list, const char *name)
        !           117: {
        !           118:        int newlen;
        !           119:        struct aim_ssi_item *cur, *group;
        !           120: 
        !           121:        /* Find the group */
        !           122:        if (!(group = aim_ssi_itemlist_finditem(list, name, NULL, AIM_SSI_TYPE_GROUP)))
        !           123:                return;
        !           124: 
        !           125:        /* Find the length for the new additional data */
        !           126:        newlen = 0;
        !           127:        if (group->gid == 0x0000) {
        !           128:                for (cur=list; cur; cur=cur->next)
        !           129:                        if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid != 0x0000))
        !           130:                                newlen += 2;
        !           131:        } else {
        !           132:                for (cur=list; cur; cur=cur->next)
        !           133:                        if ((cur->gid == group->gid) && (cur->type == AIM_SSI_TYPE_BUDDY))
        !           134:                                newlen += 2;
        !           135:        }
        !           136: 
        !           137:        /* Build the new TLV list */
        !           138:        if (newlen > 0) {
        !           139:                guint8 *newdata;
        !           140: 
        !           141:                newdata = (guint8 *)g_malloc((newlen)*sizeof(guint8));
        !           142:                newlen = 0;
        !           143:                if (group->gid == 0x0000) {
        !           144:                        for (cur=list; cur; cur=cur->next)
        !           145:                                if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid != 0x0000))
        !           146:                                                newlen += aimutil_put16(newdata+newlen, cur->gid);
        !           147:                } else {
        !           148:                        for (cur=list; cur; cur=cur->next)
        !           149:                                if ((cur->gid == group->gid) && (cur->type == AIM_SSI_TYPE_BUDDY))
        !           150:                                                newlen += aimutil_put16(newdata+newlen, cur->bid);
        !           151:                }
        !           152:                aim_tlvlist_replace_raw(&group->data, 0x00c8, newlen, newdata);
        !           153: 
        !           154:                g_free(newdata);
        !           155:        }
        !           156: }
        !           157: 
        !           158: /**
        !           159:  * Locally add a new item to the given item list.
        !           160:  *
        !           161:  * @param list A pointer to a pointer to the current list of items.
        !           162:  * @param name A null terminated string of the name of the new item, or NULL if the
        !           163:  *        item should have no name.
        !           164:  * @param gid The group ID# you want the new item to have, or 0xFFFF if we should pick something.
        !           165:  * @param bid The buddy ID# you want the new item to have, or 0xFFFF if we should pick something.
        !           166:  * @param type The type of the item, 0x0000 for a contact, 0x0001 for a group, etc.
        !           167:  * @param data The additional data for the new item.
        !           168:  * @return A pointer to the newly created item.
        !           169:  */
        !           170: static struct aim_ssi_item *aim_ssi_itemlist_add(struct aim_ssi_item **list, const char *name, guint16 gid, guint16 bid, guint16 type, GSList *data)
        !           171: {
        !           172:        gboolean exists;
        !           173:        struct aim_ssi_item *cur, *new;
        !           174: 
        !           175:        new = g_new(struct aim_ssi_item, 1);
        !           176: 
        !           177:        /* Set the name */
        !           178:        new->name = g_strdup(name);
        !           179: 
        !           180:        /* Set the group ID# and buddy ID# */
        !           181:        new->gid = gid;
        !           182:        new->bid = bid;
        !           183:        if (type == AIM_SSI_TYPE_GROUP) {
        !           184:                if ((new->gid == 0xFFFF) && name) {
        !           185:                        do {
        !           186:                                new->gid += 0x0001;
        !           187:                                exists = FALSE;
        !           188:                                for (cur = *list; cur != NULL; cur = cur->next)
        !           189:                                        if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid == new->gid)) {
        !           190:                                                exists = TRUE;
        !           191:                                                break;
        !           192:                                        }
        !           193:                        } while (exists);
        !           194:                }
        !           195:        } else if (new->gid == 0x0000) {
        !           196:                /*
        !           197:                 * This is weird, but apparently items in the root group can't
        !           198:                 * have a buddy ID equal to any group ID.  You'll get error
        !           199:                 * 0x0003 when trying to add, which is "item already exists"
        !           200:                 */
        !           201:                if (new->bid == 0xFFFF) {
        !           202:                        do {
        !           203:                                new->bid += 0x0001;
        !           204:                                exists = FALSE;
        !           205:                                for (cur = *list; cur != NULL; cur = cur->next)
        !           206:                                        if (cur->bid == new->bid || cur->gid == new->bid) {
        !           207:                                                exists = TRUE;
        !           208:                                                break;
        !           209:                                        }
        !           210:                        } while (exists);
        !           211:                }
        !           212:        } else {
        !           213:                if (new->bid == 0xFFFF) {
        !           214:                        do {
        !           215:                                new->bid += 0x0001;
        !           216:                                exists = FALSE;
        !           217:                                for (cur = *list; cur != NULL; cur = cur->next)
        !           218:                                        if (cur->bid == new->bid && cur->gid == new->gid) {
        !           219:                                                exists = TRUE;
        !           220:                                                break;
        !           221:                                        }
        !           222:                        } while (exists);
        !           223:                }
        !           224:        }
        !           225: 
        !           226:        /* Set the type */
        !           227:        new->type = type;
        !           228: 
        !           229:        /* Set the TLV list */
        !           230:        new->data = aim_tlvlist_copy(data);
        !           231: 
        !           232:        /* Add the item to the list in the correct numerical position.  Fancy, eh? */
        !           233:        if (*list) {
        !           234:                if ((new->gid < (*list)->gid) || ((new->gid == (*list)->gid) && (new->bid < (*list)->bid))) {
        !           235:                        new->next = *list;
        !           236:                        *list = new;
        !           237:                } else {
        !           238:                        struct aim_ssi_item *prev;
        !           239:                        for ((prev=*list, cur=(*list)->next); (cur && ((new->gid > cur->gid) || ((new->gid == cur->gid) && (new->bid > cur->bid)))); prev=cur, cur=cur->next);
        !           240:                        new->next = prev->next;
        !           241:                        prev->next = new;
        !           242:                }
        !           243:        } else {
        !           244:                new->next = *list;
        !           245:                *list = new;
        !           246:        }
        !           247: 
        !           248:        return new;
        !           249: }
        !           250: 
        !           251: /**
        !           252:  * Locally delete an item from the given item list.
        !           253:  *
        !           254:  * @param list A pointer to a pointer to the current list of items.
        !           255:  * @param del A pointer to the item you want to remove from the list.
        !           256:  * @return Return 0 if no errors, otherwise return the error number.
        !           257:  */
        !           258: static int aim_ssi_itemlist_del(struct aim_ssi_item **list, struct aim_ssi_item *del)
        !           259: {
        !           260:        if (!(*list) || !del)
        !           261:                return -EINVAL;
        !           262: 
        !           263:        /* Remove the item from the list */
        !           264:        if (*list == del) {
        !           265:                *list = (*list)->next;
        !           266:        } else {
        !           267:                struct aim_ssi_item *cur;
        !           268:                for (cur=*list; (cur->next && (cur->next!=del)); cur=cur->next);
        !           269:                if (cur->next)
        !           270:                        cur->next = del->next;
        !           271:        }
        !           272: 
        !           273:        /* Free the removed item */
        !           274:        g_free(del->name);
        !           275:        aim_tlvlist_free(del->data);
        !           276:        g_free(del);
        !           277: 
        !           278:        return 0;
        !           279: }
        !           280: 
        !           281: /**
        !           282:  * Compare two items to see if they have the same data.
        !           283:  *
        !           284:  * @param cur1 A pointer to a pointer to the first item.
        !           285:  * @param cur2 A pointer to a pointer to the second item.
        !           286:  * @return Return 0 if no differences, or a number if there are differences.
        !           287:  */
        !           288: static int aim_ssi_itemlist_cmp(struct aim_ssi_item *cur1, struct aim_ssi_item *cur2)
        !           289: {
        !           290:        if (!cur1 || !cur2)
        !           291:                return 1;
        !           292: 
        !           293:        if (cur1->data && !cur2->data)
        !           294:                return 2;
        !           295: 
        !           296:        if (!cur1->data && cur2->data)
        !           297:                return 3;
        !           298: 
        !           299:        if ((cur1->data && cur2->data) && (aim_tlvlist_cmp(cur1->data, cur2->data)))
        !           300:                return 4;
        !           301: 
        !           302:        if (cur1->name && !cur2->name)
        !           303:                return 5;
        !           304: 
        !           305:        if (!cur1->name && cur2->name)
        !           306:                return 6;
        !           307: 
        !           308:        if (cur1->name && cur2->name && oscar_util_name_compare(cur1->name, cur2->name))
        !           309:                return 7;
        !           310: 
        !           311:        if (cur1->gid != cur2->gid)
        !           312:                return 8;
        !           313: 
        !           314:        if (cur1->bid != cur2->bid)
        !           315:                return 9;
        !           316: 
        !           317:        if (cur1->type != cur2->type)
        !           318:                return 10;
        !           319: 
        !           320:        return 0;
        !           321: }
        !           322: 
        !           323: static gboolean aim_ssi_itemlist_valid(struct aim_ssi_item *list, struct aim_ssi_item *item)
        !           324: {
        !           325:        struct aim_ssi_item *cur;
        !           326:        for (cur=list; cur; cur=cur->next)
        !           327:                if (cur == item)
        !           328:                        return TRUE;
        !           329:        return FALSE;
        !           330: }
        !           331: 
        !           332: /**
        !           333:  * Locally find an item given a group ID# and a buddy ID#.
        !           334:  *
        !           335:  * @param list A pointer to the current list of items.
        !           336:  * @param gid The group ID# of the desired item.
        !           337:  * @param bid The buddy ID# of the desired item.
        !           338:  * @return Return a pointer to the item if found, else return NULL;
        !           339:  */
        !           340: struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_item *list, guint16 gid, guint16 bid)
        !           341: {
        !           342:        struct aim_ssi_item *cur;
        !           343:        for (cur=list; cur; cur=cur->next)
        !           344:                if ((cur->gid == gid) && (cur->bid == bid))
        !           345:                        return cur;
        !           346:        return NULL;
        !           347: }
        !           348: 
        !           349: /**
        !           350:  * Locally find an item given a group name, buddy name, and type.  If group name
        !           351:  * and buddy name are null, then just return the first item of the given type.
        !           352:  *
        !           353:  * @param list A pointer to the current list of items.
        !           354:  * @param gn The group name of the desired item.
        !           355:  * @param bn The buddy name of the desired item.
        !           356:  * @param type The type of the desired item.
        !           357:  * @return Return a pointer to the item if found, else return NULL.
        !           358:  */
        !           359: struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_item *list, const char *gn, const char *bn, guint16 type)
        !           360: {
        !           361:        struct aim_ssi_item *cur;
        !           362:        if (!list)
        !           363:                return NULL;
        !           364: 
        !           365:        if (gn && bn) { /* For finding buddies in groups */
        !           366:                for (cur=list; cur; cur=cur->next)
        !           367:                        if ((cur->type == type) && (cur->name) && !(oscar_util_name_compare(cur->name, bn))) {
        !           368:                                struct aim_ssi_item *curg;
        !           369:                                for (curg=list; curg; curg=curg->next)
        !           370:                                        if ((curg->type == AIM_SSI_TYPE_GROUP) && (curg->gid == cur->gid) && (curg->name) && !(oscar_util_name_compare(curg->name, gn)))
        !           371:                                                return cur;
        !           372:                        }
        !           373: 
        !           374:        } else if (gn) { /* For finding groups */
        !           375:                for (cur=list; cur; cur=cur->next) {
        !           376:                        if ((cur->type == type) && (cur->bid == 0x0000) && (cur->name) && !(oscar_util_name_compare(cur->name, gn))) {
        !           377:                                return cur;
        !           378:                        }
        !           379:                }
        !           380: 
        !           381:        } else if (bn) { /* For finding permits, denies, and ignores */
        !           382:                for (cur=list; cur; cur=cur->next) {
        !           383:                        if ((cur->type == type) && (cur->name) && !(oscar_util_name_compare(cur->name, bn))) {
        !           384:                                return cur;
        !           385:                        }
        !           386:                }
        !           387: 
        !           388:        /* For stuff without names--permit deny setting, visibility mask, etc. */
        !           389:        } else for (cur=list; cur; cur=cur->next) {
        !           390:                if ((cur->type == type) && (!cur->name))
        !           391:                        return cur;
        !           392:        }
        !           393: 
        !           394:        return NULL;
        !           395: }
        !           396: 
        !           397: /**
        !           398:  * Check if the given buddy exists in any group in the buddy list.
        !           399:  *
        !           400:  * @param list A pointer to the current list of items.
        !           401:  * @param bn The group name of the desired item.
        !           402:  * @return Return a pointer to the name of the item if found, else return NULL;
        !           403:  */
        !           404: struct aim_ssi_item *aim_ssi_itemlist_exists(struct aim_ssi_item *list, const char *bn)
        !           405: {
        !           406:        if (!bn)
        !           407:                return NULL;
        !           408:        return aim_ssi_itemlist_finditem(list, NULL, bn, AIM_SSI_TYPE_BUDDY);
        !           409: }
        !           410: 
        !           411: /**
        !           412:  * Locally find the parent item of the given buddy name.
        !           413:  *
        !           414:  * @param list A pointer to the current list of items.
        !           415:  * @param bn The buddy name of the desired item.
        !           416:  * @return Return a pointer to the name of the item if found, else return NULL;
        !           417:  */
        !           418: char *aim_ssi_itemlist_findparentname(struct aim_ssi_item *list, const char *bn)
        !           419: {
        !           420:        struct aim_ssi_item *cur, *curg;
        !           421:        if (!list || !bn)
        !           422:                return NULL;
        !           423:        if (!(cur = aim_ssi_itemlist_exists(list, bn)))
        !           424:                return NULL;
        !           425:        if (!(curg = aim_ssi_itemlist_find(list, cur->gid, 0x0000)))
        !           426:                return NULL;
        !           427:        return curg->name;
        !           428: }
        !           429: 
        !           430: /**
        !           431:  * Locally find the permit/deny setting item, and return the setting.
        !           432:  *
        !           433:  * @param list A pointer to the current list of items.
        !           434:  * @return Return the current SSI permit deny setting, or 0 if no setting was found.
        !           435:  */
        !           436: int aim_ssi_getpermdeny(struct aim_ssi_item *list)
        !           437: {
        !           438:        struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PDINFO);
        !           439:        if (cur) {
        !           440:                aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x00ca, 1);
        !           441:                if (tlv && tlv->value)
        !           442:                        return aimutil_get8(tlv->value);
        !           443:        }
        !           444:        return 0;
        !           445: }
        !           446: 
        !           447: /**
        !           448:  * Locally find the presence flag item, and return the setting.  The returned setting is a
        !           449:  * bitmask of the preferences.  See the AIM_SSI_PRESENCE_FLAG_* #defines in oscar.h.
        !           450:  *
        !           451:  * @param list A pointer to the current list of items.
        !           452:  * @return Return the current set of preferences.
        !           453:  */
        !           454: guint32 aim_ssi_getpresence(struct aim_ssi_item *list)
        !           455: {
        !           456:        struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS);
        !           457:        if (cur) {
        !           458:                aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x00c9, 1);
        !           459:                if (tlv && tlv->length)
        !           460:                        return aimutil_get32(tlv->value);
        !           461:        }
        !           462:        return 0xFFFFFFFF;
        !           463: }
        !           464: 
        !           465: /**
        !           466:  * Locally find the alias of the given buddy.
        !           467:  *
        !           468:  * @param list A pointer to the current list of items.
        !           469:  * @param gn The group of the buddy.
        !           470:  * @param bn The name of the buddy.
        !           471:  * @return A pointer to a NULL terminated string that is the buddy's
        !           472:  *         alias, or NULL if the buddy has no alias.  You should free
        !           473:  *         this returned value!
        !           474:  */
        !           475: char *aim_ssi_getalias(struct aim_ssi_item *list, const char *gn, const char *bn)
        !           476: {
        !           477:        struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, bn, AIM_SSI_TYPE_BUDDY);
        !           478:        if (cur) {
        !           479:                aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x0131, 1);
        !           480:                if (tlv && tlv->length)
        !           481:                        return g_strndup((const gchar *)tlv->value, tlv->length);
        !           482:        }
        !           483:        return NULL;
        !           484: }
        !           485: 
        !           486: /**
        !           487:  * Locally find the comment of the given buddy.
        !           488:  *
        !           489:  * @param list A pointer to the current list of items.
        !           490:  * @param gn The group of the buddy.
        !           491:  * @param bn The name of the buddy.
        !           492:  * @return A pointer to a NULL terminated string that is the buddy's
        !           493:  *         comment, or NULL if the buddy has no comment.  You should free
        !           494:  *         this returned value!
        !           495:  */
        !           496: char *aim_ssi_getcomment(struct aim_ssi_item *list, const char *gn, const char *bn)
        !           497: {
        !           498:        struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, bn, AIM_SSI_TYPE_BUDDY);
        !           499:        if (cur) {
        !           500:                aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x013c, 1);
        !           501:                if (tlv && tlv->length) {
        !           502:                        return g_strndup((const gchar *)tlv->value, tlv->length);
        !           503:                }
        !           504:        }
        !           505:        return NULL;
        !           506: }
        !           507: 
        !           508: /**
        !           509:  * Locally find if you are waiting for authorization for a buddy.
        !           510:  *
        !           511:  * @param list A pointer to the current list of items.
        !           512:  * @param gn The group of the buddy.
        !           513:  * @param bn The name of the buddy.
        !           514:  * @return 1 if you are waiting for authorization; 0 if you are not
        !           515:  */
        !           516: gboolean aim_ssi_waitingforauth(struct aim_ssi_item *list, const char *gn, const char *bn)
        !           517: {
        !           518:        struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, bn, AIM_SSI_TYPE_BUDDY);
        !           519:        if (cur) {
        !           520:                if (aim_tlv_gettlv(cur->data, 0x0066, 1))
        !           521:                        return TRUE;
        !           522:        }
        !           523:        return FALSE;
        !           524: }
        !           525: 
        !           526: /**
        !           527:  * If there are changes, then create temporary items and
        !           528:  * call addmoddel.
        !           529:  *
        !           530:  * @param od The oscar session.
        !           531:  * @return Return 0 if no errors, otherwise return the error number.
        !           532:  */
        !           533: static int aim_ssi_sync(OscarData *od)
        !           534: {
        !           535:        struct aim_ssi_item *cur1, *cur2;
        !           536:        struct aim_ssi_tmp *cur, *new;
        !           537:        int n = 0;
        !           538:        GString *debugstr = g_string_new("");
        !           539: 
        !           540:        /*
        !           541:         * The variable "n" is used to limit the number of addmoddel's that
        !           542:         * are performed in a single SNAC.  It will hopefully keep the size
        !           543:         * of the SNAC below the maximum SNAC size.
        !           544:         */
        !           545: 
        !           546:        if (!od)
        !           547:                return -EINVAL;
        !           548: 
        !           549:        /* If we're waiting for an ack, we shouldn't do anything else */
        !           550:        if (od->ssi.waiting_for_ack)
        !           551:                return 0;
        !           552: 
        !           553:        /*
        !           554:         * Compare the 2 lists and create an aim_ssi_tmp for each difference.
        !           555:         * We should only send either additions, modifications, or deletions
        !           556:         * before waiting for an acknowledgement.  So first do deletions, then
        !           557:         * additions, then modifications.  Also, both the official and the local
        !           558:         * list should be in ascending numerical order for the group ID#s and the
        !           559:         * buddy ID#s, which makes things more efficient.  I think.
        !           560:         */
        !           561: 
        !           562:        /* Deletions */
        !           563:        if (!od->ssi.pending) {
        !           564:                for (cur1=od->ssi.official; cur1 && (n < 15); cur1=cur1->next) {
        !           565:                        if (!aim_ssi_itemlist_find(od->ssi.local, cur1->gid, cur1->bid)) {
        !           566:                                n++;
        !           567:                                new = g_new(struct aim_ssi_tmp, 1);
        !           568:                                new->action = SNAC_SUBTYPE_FEEDBAG_DEL;
        !           569:                                new->ack = 0xffff;
        !           570:                                new->name = NULL;
        !           571:                                new->item = cur1;
        !           572:                                new->next = NULL;
        !           573:                                if (od->ssi.pending) {
        !           574:                                        for (cur=od->ssi.pending; cur->next; cur=cur->next);
        !           575:                                        cur->next = new;
        !           576:                                } else
        !           577:                                        od->ssi.pending = new;
        !           578:                                aim_ssi_item_debug_append(debugstr, "Deleting item ", cur1);
        !           579:                        }
        !           580:                }
        !           581:        }
        !           582: 
        !           583:        /* Additions */
        !           584:        if (!od->ssi.pending) {
        !           585:                for (cur1=od->ssi.local; cur1 && (n < 15); cur1=cur1->next) {
        !           586:                        if (!aim_ssi_itemlist_find(od->ssi.official, cur1->gid, cur1->bid)) {
        !           587:                                n++;
        !           588:                                new = g_new(struct aim_ssi_tmp, 1);
        !           589:                                new->action = SNAC_SUBTYPE_FEEDBAG_ADD;
        !           590:                                new->ack = 0xffff;
        !           591:                                new->name = NULL;
        !           592:                                new->item = cur1;
        !           593:                                new->next = NULL;
        !           594:                                if (od->ssi.pending) {
        !           595:                                        for (cur=od->ssi.pending; cur->next; cur=cur->next);
        !           596:                                        cur->next = new;
        !           597:                                } else
        !           598:                                        od->ssi.pending = new;
        !           599:                                aim_ssi_item_debug_append(debugstr, "Adding item ", cur1);
        !           600:                        }
        !           601:                }
        !           602:        }
        !           603: 
        !           604:        /* Modifications */
        !           605:        if (!od->ssi.pending) {
        !           606:                for (cur1=od->ssi.local; cur1 && (n < 15); cur1=cur1->next) {
        !           607:                        cur2 = aim_ssi_itemlist_find(od->ssi.official, cur1->gid, cur1->bid);
        !           608:                        if (cur2 && (aim_ssi_itemlist_cmp(cur1, cur2))) {
        !           609:                                n++;
        !           610:                                new = g_new(struct aim_ssi_tmp, 1);
        !           611:                                new->action = SNAC_SUBTYPE_FEEDBAG_MOD;
        !           612:                                new->ack = 0xffff;
        !           613:                                new->name = NULL;
        !           614:                                new->item = cur1;
        !           615:                                new->next = NULL;
        !           616:                                if (od->ssi.pending) {
        !           617:                                        for (cur=od->ssi.pending; cur->next; cur=cur->next);
        !           618:                                        cur->next = new;
        !           619:                                } else
        !           620:                                        od->ssi.pending = new;
        !           621:                                aim_ssi_item_debug_append(debugstr, "Modifying item ", cur1);
        !           622:                        }
        !           623:                }
        !           624:        }
        !           625:        if (debugstr->len > 0) {
        !           626:                purple_debug_info("oscar", "%s", debugstr->str);
        !           627:                if (purple_debug_is_verbose()) {
        !           628:                        g_string_truncate(debugstr, 0);
        !           629:                        for (cur1 = od->ssi.local; cur1; cur1 = cur1->next) 
        !           630:                                aim_ssi_item_debug_append(debugstr, "\t", cur1);
        !           631:                        purple_debug_misc("oscar", "Dumping item list of account %s:\n%s",
        !           632:                                purple_connection_get_account(od->gc)->username, debugstr->str);
        !           633:                }
        !           634:        }
        !           635:        g_string_free(debugstr, TRUE);
        !           636: 
        !           637:        /* We're out of stuff to do, so tell the AIM servers we're done and exit */
        !           638:        if (!od->ssi.pending) {
        !           639:                if (od->ssi.in_transaction) {
        !           640:                        aim_ssi_modend(od);
        !           641:                        od->ssi.in_transaction = FALSE;
        !           642:                }
        !           643:                return 0;
        !           644:        }
        !           645: 
        !           646:        /* If this is the first in a series of add/mod/del
        !           647:         * requests then send the "begin transaction" message. */
        !           648:        if (!od->ssi.in_transaction)
        !           649:        {
        !           650:                aim_ssi_modbegin(od);
        !           651:                od->ssi.in_transaction = TRUE;
        !           652:        }
        !           653: 
        !           654:        /* Make sure we don't send anything else between now
        !           655:         * and when we receive the ack for the following operation */
        !           656:        od->ssi.waiting_for_ack = TRUE;
        !           657: 
        !           658:        /* Now go mail off our data and wait 4 to 6 weeks */
        !           659:        return aim_ssi_addmoddel(od);;
        !           660: }
        !           661: 
        !           662: /**
        !           663:  * Free all SSI data.
        !           664:  *
        !           665:  * This doesn't remove it from the server, that's different.
        !           666:  *
        !           667:  * @param od The oscar odion.
        !           668:  * @return Return 0 if no errors, otherwise return the error number.
        !           669:  */
        !           670: static void
        !           671: aim_ssi_freelist(OscarData *od)
        !           672: {
        !           673:        struct aim_ssi_item *cur, *del;
        !           674:        struct aim_ssi_tmp *curtmp, *deltmp;
        !           675: 
        !           676:        cur = od->ssi.official;
        !           677:        while (cur) {
        !           678:                del = cur;
        !           679:                cur = cur->next;
        !           680:                g_free(del->name);
        !           681:                aim_tlvlist_free(del->data);
        !           682:                g_free(del);
        !           683:        }
        !           684: 
        !           685:        cur = od->ssi.local;
        !           686:        while (cur) {
        !           687:                del = cur;
        !           688:                cur = cur->next;
        !           689:                g_free(del->name);
        !           690:                aim_tlvlist_free(del->data);
        !           691:                g_free(del);
        !           692:        }
        !           693: 
        !           694:        curtmp = od->ssi.pending;
        !           695:        while (curtmp) {
        !           696:                deltmp = curtmp;
        !           697:                curtmp = curtmp->next;
        !           698:                g_free(deltmp);
        !           699:        }
        !           700: 
        !           701:        od->ssi.numitems = 0;
        !           702:        od->ssi.official = NULL;
        !           703:        od->ssi.local = NULL;
        !           704:        od->ssi.pending = NULL;
        !           705:        od->ssi.timestamp = (time_t)0;
        !           706: }
        !           707: 
        !           708: /**
        !           709:  * This "cleans" the ssi list.  It does the following:
        !           710:  * 1) Makes sure all buddies, permits, and denies have names.
        !           711:  * 2) Makes sure that all buddies are in a group that exist.
        !           712:  * 3) Deletes any empty groups
        !           713:  *
        !           714:  * @param od The oscar odion.
        !           715:  * @return Return 0 if no errors, otherwise return the error number.
        !           716:  */
        !           717: int aim_ssi_cleanlist(OscarData *od)
        !           718: {
        !           719:        struct aim_ssi_item *cur, *next;
        !           720: 
        !           721:        if (!od)
        !           722:                return -EINVAL;
        !           723: 
        !           724:        /* Delete any buddies, permits, or denies with empty names. */
        !           725:        /* If there are any buddies directly in the master group, add them to a real group. */
        !           726:        /* DESTROY any buddies that are directly in the master group. */
        !           727:        /* Do the same for buddies that are in a non-existant group. */
        !           728:        /* This will kind of mess up if you hit the item limit, but this function isn't too critical */
        !           729:        cur = od->ssi.local;
        !           730:        while (cur) {
        !           731:                next = cur->next;
        !           732:                if (!cur->name) {
        !           733:                        if (cur->type == AIM_SSI_TYPE_BUDDY)
        !           734:                                aim_ssi_delbuddy(od, NULL, NULL);
        !           735:                        else if (cur->type == AIM_SSI_TYPE_PERMIT || cur->type == AIM_SSI_TYPE_DENY || cur->type == AIM_SSI_TYPE_ICQDENY)
        !           736:                                aim_ssi_del_from_private_list(od, NULL, cur->type);
        !           737:                } else if ((cur->type == AIM_SSI_TYPE_BUDDY) && ((cur->gid == 0x0000) || (!aim_ssi_itemlist_find(od->ssi.local, cur->gid, 0x0000)))) {
        !           738:                        char *alias = aim_ssi_getalias(od->ssi.local, NULL, cur->name);
        !           739:                        aim_ssi_addbuddy(od, cur->name, "orphans", NULL, alias, NULL, NULL, FALSE);
        !           740:                        aim_ssi_delbuddy(od, cur->name, NULL);
        !           741:                        g_free(alias);
        !           742:                }
        !           743:                cur = next;
        !           744:        }
        !           745: 
        !           746:        /* Make sure there aren't any duplicate buddies in a group, or duplicate permits or denies */
        !           747:        cur = od->ssi.local;
        !           748:        while (cur) {
        !           749:                if ((cur->type == AIM_SSI_TYPE_BUDDY) || (cur->type == AIM_SSI_TYPE_PERMIT) || (cur->type == AIM_SSI_TYPE_DENY))
        !           750:                {
        !           751:                        struct aim_ssi_item *cur2, *next2;
        !           752:                        cur2 = cur->next;
        !           753:                        while (cur2) {
        !           754:                                next2 = cur2->next;
        !           755:                                if ((cur->type == cur2->type) && (cur->gid == cur2->gid) && (cur->name != NULL) && (cur2->name != NULL) && (!oscar_util_name_compare(cur->name, cur2->name))) {
        !           756:                                        aim_ssi_itemlist_del(&od->ssi.local, cur2);
        !           757:                                }
        !           758:                                cur2 = next2;
        !           759:                        }
        !           760:                }
        !           761:                cur = cur->next;
        !           762:        }
        !           763: 
        !           764:        /* If we've made any changes then sync our list with the server's */
        !           765:        return aim_ssi_sync(od);
        !           766: }
        !           767: 
        !           768: /**
        !           769:  * Add a buddy to the list.
        !           770:  *
        !           771:  * @param od The oscar odion.
        !           772:  * @param name The name of the item.
        !           773:  * @param group The group of the item.
        !           774:  * @param data A TLV list to use as the additional data for this item.
        !           775:  * @param alias The alias/nickname of the item, or NULL.
        !           776:  * @param comment The buddy comment for the item, or NULL.
        !           777:  * @param smsnum The locally assigned SMS number, or NULL.
        !           778:  * @return Return 0 if no errors, otherwise return the error number.
        !           779:  */
        !           780: int aim_ssi_addbuddy(OscarData *od, const char *name, const char *group, GSList *data, const char *alias, const char *comment, const char *smsnum, gboolean needauth)
        !           781: {
        !           782:        struct aim_ssi_item *parent;
        !           783: 
        !           784:        if (!od || !name || !group)
        !           785:                return -EINVAL;
        !           786: 
        !           787:        /* Find the parent */
        !           788:        if (!(parent = aim_ssi_itemlist_finditem(od->ssi.local, group, NULL, AIM_SSI_TYPE_GROUP))) {
        !           789:                /* Find the parent's parent (the master group) */
        !           790:                if (aim_ssi_itemlist_find(od->ssi.local, 0x0000, 0x0000) == NULL)
        !           791:                        aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0x0000, AIM_SSI_TYPE_GROUP, NULL);
        !           792: 
        !           793:                /* Add the parent */
        !           794:                parent = aim_ssi_itemlist_add(&od->ssi.local, group, 0xFFFF, 0x0000, AIM_SSI_TYPE_GROUP, NULL);
        !           795: 
        !           796:                /* Modify the parent's parent (the master group) */
        !           797:                aim_ssi_itemlist_rebuildgroup(od->ssi.local, NULL);
        !           798:        }
        !           799: 
        !           800:        /* Create a TLV list for the new buddy */
        !           801:        if (needauth)
        !           802:                aim_tlvlist_add_noval(&data, 0x0066);
        !           803:        if (alias != NULL)
        !           804:                aim_tlvlist_add_str(&data, 0x0131, alias);
        !           805:        if (smsnum != NULL)
        !           806:                aim_tlvlist_add_str(&data, 0x013a, smsnum);
        !           807:        if (comment != NULL)
        !           808:                aim_tlvlist_add_str(&data, 0x013c, comment);
        !           809: 
        !           810:        /* Add that bad boy */
        !           811:        aim_ssi_itemlist_add(&od->ssi.local, name, parent->gid, 0xFFFF, AIM_SSI_TYPE_BUDDY, data);
        !           812:        aim_tlvlist_free(data);
        !           813: 
        !           814:        /* Modify the parent group */
        !           815:        aim_ssi_itemlist_rebuildgroup(od->ssi.local, group);
        !           816: 
        !           817:        /* Sync our local list with the server list */
        !           818:        return aim_ssi_sync(od);
        !           819: }
        !           820: 
        !           821: int
        !           822: aim_ssi_add_to_private_list(OscarData *od, const char* name, guint16 list_type)
        !           823: {
        !           824:        if (!od || !name || !od->ssi.received_data)
        !           825:                return -EINVAL;
        !           826: 
        !           827:        if (aim_ssi_itemlist_find(od->ssi.local, 0x0000, 0x0000) == NULL)
        !           828:                aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0x0000, AIM_SSI_TYPE_GROUP, NULL);
        !           829: 
        !           830:        aim_ssi_itemlist_add(&od->ssi.local, name, 0x0000, 0xFFFF, list_type, NULL);
        !           831:        return aim_ssi_sync(od);
        !           832: }
        !           833: 
        !           834: int
        !           835: aim_ssi_del_from_private_list(OscarData* od, const char* name, guint16 list_type)
        !           836: {
        !           837:        struct aim_ssi_item *del;
        !           838: 
        !           839:        if (!od)
        !           840:                return -EINVAL;
        !           841: 
        !           842:        if (!(del = aim_ssi_itemlist_finditem(od->ssi.local, NULL, name, list_type)))
        !           843:                return -EINVAL;
        !           844: 
        !           845:        aim_ssi_itemlist_del(&od->ssi.local, del);
        !           846:        return aim_ssi_sync(od);
        !           847: }
        !           848: 
        !           849: /**
        !           850:  * Deletes a buddy from the list.
        !           851:  *
        !           852:  * @param od The oscar odion.
        !           853:  * @param name The name of the item, or NULL.
        !           854:  * @param group The group of the item, or NULL.
        !           855:  * @return Return 0 if no errors, otherwise return the error number.
        !           856:  */
        !           857: int aim_ssi_delbuddy(OscarData *od, const char *name, const char *group)
        !           858: {
        !           859:        struct aim_ssi_item *del;
        !           860: 
        !           861:        if (!od)
        !           862:                return -EINVAL;
        !           863: 
        !           864:        /* Find the buddy */
        !           865:        if (!(del = aim_ssi_itemlist_finditem(od->ssi.local, group, name, AIM_SSI_TYPE_BUDDY)))
        !           866:                return -EINVAL;
        !           867: 
        !           868:        /* Remove the item from the list */
        !           869:        aim_ssi_itemlist_del(&od->ssi.local, del);
        !           870: 
        !           871:        /* Modify the parent group */
        !           872:        aim_ssi_itemlist_rebuildgroup(od->ssi.local, group);
        !           873: 
        !           874:        /* Sync our local list with the server list */
        !           875:        return aim_ssi_sync(od);
        !           876: }
        !           877: 
        !           878: /**
        !           879:  * Deletes a group from the list.
        !           880:  *
        !           881:  * @param od The oscar odion.
        !           882:  * @param group The name of the group.
        !           883:  * @return Return 0 if no errors, otherwise return the error number.
        !           884:  */
        !           885: int aim_ssi_delgroup(OscarData *od, const char *group)
        !           886: {
        !           887:        struct aim_ssi_item *del;
        !           888:        aim_tlv_t *tlv;
        !           889: 
        !           890:        if (!od)
        !           891:                return -EINVAL;
        !           892: 
        !           893:        /* Find the group */
        !           894:        if (!(del = aim_ssi_itemlist_finditem(od->ssi.local, group, NULL, AIM_SSI_TYPE_GROUP)))
        !           895:                return -EINVAL;
        !           896: 
        !           897:        /* Don't delete the group if it's not empty */
        !           898:        tlv = aim_tlv_gettlv(del->data, 0x00c8, 1);
        !           899:        if (tlv && tlv->length > 0)
        !           900:                return -EINVAL;
        !           901: 
        !           902:        /* Remove the item from the list */
        !           903:        aim_ssi_itemlist_del(&od->ssi.local, del);
        !           904: 
        !           905:        /* Modify the parent group */
        !           906:        aim_ssi_itemlist_rebuildgroup(od->ssi.local, group);
        !           907: 
        !           908:        /* Sync our local list with the server list */
        !           909:        return aim_ssi_sync(od);
        !           910: }
        !           911: 
        !           912: /**
        !           913:  * Move a buddy from one group to another group.  This basically just deletes the
        !           914:  * buddy and re-adds it.
        !           915:  *
        !           916:  * @param od The oscar odion.
        !           917:  * @param oldgn The group that the buddy is currently in.
        !           918:  * @param newgn The group that the buddy should be moved in to.
        !           919:  * @param bn The name of the buddy to be moved.
        !           920:  * @return Return 0 if no errors, otherwise return the error number.
        !           921:  */
        !           922: int aim_ssi_movebuddy(OscarData *od, const char *oldgn, const char *newgn, const char *bn)
        !           923: {
        !           924:        struct aim_ssi_item *buddy;
        !           925:        GSList *data;
        !           926: 
        !           927:        /* Find the buddy */
        !           928:        buddy = aim_ssi_itemlist_finditem(od->ssi.local, oldgn, bn, AIM_SSI_TYPE_BUDDY);
        !           929:        if (buddy == NULL)
        !           930:                return -EINVAL;
        !           931: 
        !           932:        /* Make a copy of the buddy's TLV list */
        !           933:        data = aim_tlvlist_copy(buddy->data);
        !           934: 
        !           935:        /* Delete the old item */
        !           936:        aim_ssi_delbuddy(od, bn, oldgn);
        !           937: 
        !           938:        /* Add the new item using the EXACT SAME TLV list */
        !           939:        aim_ssi_addbuddy(od, bn, newgn, data, NULL, NULL, NULL, FALSE);
        !           940: 
        !           941:        return 0;
        !           942: }
        !           943: 
        !           944: /**
        !           945:  * Change the alias stored on the server for a given buddy.
        !           946:  *
        !           947:  * @param od The oscar odion.
        !           948:  * @param gn The group that the buddy is currently in.
        !           949:  * @param bn The name of the buddy.
        !           950:  * @param alias The new alias for the buddy, or NULL if you want to remove
        !           951:  *        a buddy's comment.
        !           952:  * @return Return 0 if no errors, otherwise return the error number.
        !           953:  */
        !           954: int aim_ssi_aliasbuddy(OscarData *od, const char *gn, const char *bn, const char *alias)
        !           955: {
        !           956:        struct aim_ssi_item *tmp;
        !           957: 
        !           958:        if (!od || !gn || !bn)
        !           959:                return -EINVAL;
        !           960: 
        !           961:        if (!(tmp = aim_ssi_itemlist_finditem(od->ssi.local, gn, bn, AIM_SSI_TYPE_BUDDY)))
        !           962:                return -EINVAL;
        !           963: 
        !           964:        /* Either add or remove the 0x0131 TLV from the TLV chain */
        !           965:        if ((alias != NULL) && (strlen(alias) > 0))
        !           966:                aim_tlvlist_replace_str(&tmp->data, 0x0131, alias);
        !           967:        else
        !           968:                aim_tlvlist_remove(&tmp->data, 0x0131);
        !           969: 
        !           970:        /* Sync our local list with the server list */
        !           971:        return aim_ssi_sync(od);
        !           972: }
        !           973: 
        !           974: /**
        !           975:  * Change the comment stored on the server for a given buddy.
        !           976:  *
        !           977:  * @param od The oscar odion.
        !           978:  * @param gn The group that the buddy is currently in.
        !           979:  * @param bn The name of the buddy.
        !           980:  * @param alias The new comment for the buddy, or NULL if you want to remove
        !           981:  *        a buddy's comment.
        !           982:  * @return Return 0 if no errors, otherwise return the error number.
        !           983:  */
        !           984: int aim_ssi_editcomment(OscarData *od, const char *gn, const char *bn, const char *comment)
        !           985: {
        !           986:        struct aim_ssi_item *tmp;
        !           987: 
        !           988:        if (!od || !gn || !bn)
        !           989:                return -EINVAL;
        !           990: 
        !           991:        if (!(tmp = aim_ssi_itemlist_finditem(od->ssi.local, gn, bn, AIM_SSI_TYPE_BUDDY)))
        !           992:                return -EINVAL;
        !           993: 
        !           994:        /* Either add or remove the 0x0131 TLV from the TLV chain */
        !           995:        if ((comment != NULL) && (strlen(comment) > 0))
        !           996:                aim_tlvlist_replace_str(&tmp->data, 0x013c, comment);
        !           997:        else
        !           998:                aim_tlvlist_remove(&tmp->data, 0x013c);
        !           999: 
        !          1000:        /* Sync our local list with the server list */
        !          1001:        return aim_ssi_sync(od);
        !          1002: }
        !          1003: 
        !          1004: /**
        !          1005:  * Rename a group.
        !          1006:  *
        !          1007:  * @param od The oscar odion.
        !          1008:  * @param oldgn The old group name.
        !          1009:  * @param newgn The new group name.
        !          1010:  * @return Return 0 if no errors, otherwise return the error number.
        !          1011:  */
        !          1012: int aim_ssi_rename_group(OscarData *od, const char *oldgn, const char *newgn)
        !          1013: {
        !          1014:        struct aim_ssi_item *group;
        !          1015: 
        !          1016:        if (!od || !oldgn || !newgn)
        !          1017:                return -EINVAL;
        !          1018: 
        !          1019:        if (!(group = aim_ssi_itemlist_finditem(od->ssi.local, oldgn, NULL, AIM_SSI_TYPE_GROUP)))
        !          1020:                return -EINVAL;
        !          1021: 
        !          1022:        g_free(group->name);
        !          1023:        group->name = g_strdup(newgn);
        !          1024: 
        !          1025:        /* Sync our local list with the server list */
        !          1026:        return aim_ssi_sync(od);
        !          1027: }
        !          1028: 
        !          1029: /**
        !          1030:  * Stores your permit/deny setting on the server, and starts using it.
        !          1031:  *
        !          1032:  * @param od The oscar odion.
        !          1033:  * @param permdeny Your permit/deny setting. For ICQ accounts, it actually affects your visibility
        !          1034:  *        and has nothing to do with blocking. Can be one of the following:
        !          1035:  *        1 - Allow all users
        !          1036:  *        2 - Block all users
        !          1037:  *        3 - Allow only the users below
        !          1038:  *        4 - Block only the users below
        !          1039:  *        5 - Allow only users on my buddy list
        !          1040:  * @return Return 0 if no errors, otherwise return the error number.
        !          1041:  */
        !          1042: int aim_ssi_setpermdeny(OscarData *od, guint8 permdeny)
        !          1043: {
        !          1044:        struct aim_ssi_item *tmp;
        !          1045: 
        !          1046:        if (!od || !od->ssi.received_data)
        !          1047:                return -EINVAL;
        !          1048: 
        !          1049:        /* Find the PDINFO item, or add it if it does not exist */
        !          1050:        if (!(tmp = aim_ssi_itemlist_finditem(od->ssi.local, NULL, NULL, AIM_SSI_TYPE_PDINFO))) {
        !          1051:                /* Make sure the master group exists */
        !          1052:                if (aim_ssi_itemlist_find(od->ssi.local, 0x0000, 0x0000) == NULL)
        !          1053:                        aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0x0000, AIM_SSI_TYPE_GROUP, NULL);
        !          1054: 
        !          1055:                tmp = aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0xFFFF, AIM_SSI_TYPE_PDINFO, NULL);
        !          1056:        }
        !          1057: 
        !          1058:        /* Need to add the 0x00ca TLV to the TLV chain */
        !          1059:        aim_tlvlist_replace_8(&tmp->data, 0x00ca, permdeny);
        !          1060: 
        !          1061:        /* Sync our local list with the server list */
        !          1062:        return aim_ssi_sync(od);
        !          1063: }
        !          1064: 
        !          1065: /**
        !          1066:  * Set buddy icon information
        !          1067:  *
        !          1068:  * @param od The oscar odion.
        !          1069:  * @param iconcsum The MD5 checksum of the icon you are using.
        !          1070:  * @param iconcsumlen Length of the MD5 checksum given above.  Should be 0x10 bytes.
        !          1071:  * @return Return 0 if no errors, otherwise return the error number.
        !          1072:  */
        !          1073: int aim_ssi_seticon(OscarData *od, const guint8 *iconsum, guint8 iconsumlen)
        !          1074: {
        !          1075:        struct aim_ssi_item *tmp;
        !          1076:        guint8 *csumdata;
        !          1077: 
        !          1078:        if (!od || !iconsum || !iconsumlen || !od->ssi.received_data)
        !          1079:                return -EINVAL;
        !          1080: 
        !          1081:        /* Find the ICONINFO item, or add it if it does not exist */
        !          1082:        if (!(tmp = aim_ssi_itemlist_finditem(od->ssi.local, NULL, "1", AIM_SSI_TYPE_ICONINFO))) {
        !          1083:                /* Make sure the master group exists */
        !          1084:                if (aim_ssi_itemlist_find(od->ssi.local, 0x0000, 0x0000) == NULL)
        !          1085:                        aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0x0000, AIM_SSI_TYPE_GROUP, NULL);
        !          1086: 
        !          1087:                tmp = aim_ssi_itemlist_add(&od->ssi.local, "1", 0x0000, 0xFFFF, AIM_SSI_TYPE_ICONINFO, NULL);
        !          1088:        }
        !          1089: 
        !          1090:        /* Need to add the 0x00d5 TLV to the TLV chain */
        !          1091:        csumdata = (guint8 *)g_malloc((iconsumlen+2)*sizeof(guint8));
        !          1092:        (void)aimutil_put8(&csumdata[0], 0x00);
        !          1093:        (void)aimutil_put8(&csumdata[1], iconsumlen);
        !          1094:        memcpy(&csumdata[2], iconsum, iconsumlen);
        !          1095:        aim_tlvlist_replace_raw(&tmp->data, 0x00d5, (iconsumlen+2) * sizeof(guint8), csumdata);
        !          1096:        g_free(csumdata);
        !          1097: 
        !          1098:        /* Need to add the 0x0131 TLV to the TLV chain, used to cache the icon */
        !          1099:        aim_tlvlist_replace_noval(&tmp->data, 0x0131);
        !          1100: 
        !          1101:        /* Sync our local list with the server list */
        !          1102:        aim_ssi_sync(od);
        !          1103:        return 0;
        !          1104: }
        !          1105: 
        !          1106: /**
        !          1107:  * Remove a reference to a server stored buddy icon.  This will make your
        !          1108:  * icon stop showing up to other people.
        !          1109:  *
        !          1110:  * Really this function just sets the icon to a dummy value.  It's weird...
        !          1111:  * but I think the dummy value basically means "I don't have an icon!"
        !          1112:  *
        !          1113:  * @param od The oscar session.
        !          1114:  * @return Return 0 if no errors, otherwise return the error number.
        !          1115:  */
        !          1116: int aim_ssi_delicon(OscarData *od)
        !          1117: {
        !          1118:        const guint8 csumdata[] = {0x02, 0x01, 0xd2, 0x04, 0x72};
        !          1119: 
        !          1120:        return aim_ssi_seticon(od, csumdata, 5);
        !          1121: }
        !          1122: 
        !          1123: /**
        !          1124:  * Stores your setting for various SSI settings.  Whether you
        !          1125:  * should show up as idle or not, etc.
        !          1126:  *
        !          1127:  * @param od The oscar odion.
        !          1128:  * @param presence A bitmask of the first 32 entries [0-31] from
        !          1129:  *        http://dev.aol.com/aim/oscar/#FEEDBAG__BUDDY_PREFS
        !          1130:  *        0x00000002 - Hide "eBuddy group" (whatever that is)
        !          1131:  *        0x00000400 - Allow others to see your idle time
        !          1132:  *        0x00020000 - Don't show Recent Buddies
        !          1133:  * @return Return 0 if no errors, otherwise return the error number.
        !          1134:  */
        !          1135: int aim_ssi_setpresence(OscarData *od, guint32 presence) {
        !          1136:        struct aim_ssi_item *tmp;
        !          1137: 
        !          1138:        if (!od || !od->ssi.received_data)
        !          1139:                return -EINVAL;
        !          1140: 
        !          1141:        /* Find the PRESENCEPREFS item, or add it if it does not exist */
        !          1142:        if (!(tmp = aim_ssi_itemlist_finditem(od->ssi.local, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS))) {
        !          1143:                /* Make sure the master group exists */
        !          1144:                if (aim_ssi_itemlist_find(od->ssi.local, 0x0000, 0x0000) == NULL)
        !          1145:                        aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0x0000, AIM_SSI_TYPE_GROUP, NULL);
        !          1146: 
        !          1147:                tmp = aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0xFFFF, AIM_SSI_TYPE_PRESENCEPREFS, NULL);
        !          1148:        }
        !          1149: 
        !          1150:        /* Need to add the x00c9 TLV to the TLV chain */
        !          1151:        aim_tlvlist_replace_32(&tmp->data, 0x00c9, presence);
        !          1152: 
        !          1153:        /* Sync our local list with the server list */
        !          1154:        return aim_ssi_sync(od);
        !          1155: }
        !          1156: 
        !          1157: /*
        !          1158:  * Subtype 0x0002 - Request SSI Rights.
        !          1159:  */
        !          1160: int aim_ssi_reqrights(OscarData *od)
        !          1161: {
        !          1162:        FlapConnection *conn;
        !          1163: 
        !          1164:        if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)))
        !          1165:                return -EINVAL;
        !          1166: 
        !          1167:        aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_REQRIGHTS);
        !          1168: 
        !          1169:        return 0;
        !          1170: }
        !          1171: 
        !          1172: /*
        !          1173:  * Subtype 0x0003 - SSI Rights Information.
        !          1174:  */
        !          1175: static int parserights(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
        !          1176: {
        !          1177:        int ret = 0, i;
        !          1178:        aim_rxcallback_t userfunc;
        !          1179:        GSList *tlvlist;
        !          1180:        aim_tlv_t *tlv;
        !          1181:        ByteStream bstream;
        !          1182:        guint16 *maxitems;
        !          1183: 
        !          1184:        /* This SNAC is made up of a bunch of TLVs */
        !          1185:        tlvlist = aim_tlvlist_read(bs);
        !          1186: 
        !          1187:        /* TLV 0x0004 contains the maximum number of each item */
        !          1188:        if (!(tlv = aim_tlv_gettlv(tlvlist, 0x0004, 1))) {
        !          1189:                aim_tlvlist_free(tlvlist);
        !          1190:                return 0;
        !          1191:        }
        !          1192: 
        !          1193:        byte_stream_init(&bstream, tlv->value, tlv->length);
        !          1194: 
        !          1195:        maxitems = (guint16 *)g_malloc((tlv->length/2)*sizeof(guint16));
        !          1196: 
        !          1197:        for (i=0; i<(tlv->length/2); i++)
        !          1198:                maxitems[i] = byte_stream_get16(&bstream);
        !          1199: 
        !          1200:        if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
        !          1201:                ret = userfunc(od, conn, frame, tlv->length/2, maxitems);
        !          1202: 
        !          1203:        aim_tlvlist_free(tlvlist);
        !          1204:        g_free(maxitems);
        !          1205: 
        !          1206:        return ret;
        !          1207: }
        !          1208: 
        !          1209: /*
        !          1210:  * Subtype 0x0004 - Request SSI Data when you don't have a timestamp and
        !          1211:  * revision number.
        !          1212:  *
        !          1213:  */
        !          1214: int aim_ssi_reqdata(OscarData *od)
        !          1215: {
        !          1216:        FlapConnection *conn;
        !          1217: 
        !          1218:        if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)))
        !          1219:                return -EINVAL;
        !          1220: 
        !          1221:        /* Free any current data, just in case */
        !          1222:        aim_ssi_freelist(od);
        !          1223: 
        !          1224:        aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_REQDATA);
        !          1225: 
        !          1226:        return 0;
        !          1227: }
        !          1228: 
        !          1229: /*
        !          1230:  * Subtype 0x0006 - SSI Data.
        !          1231:  */
        !          1232: static int parsedata(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
        !          1233: {
        !          1234:        int ret = 0;
        !          1235:        aim_rxcallback_t userfunc;
        !          1236:        guint8 fmtver; /* guess */
        !          1237:        guint16 namelen, gid, bid, type;
        !          1238:        char *name;
        !          1239:        GSList *data;
        !          1240:        GString *debugstr = g_string_new("");
        !          1241: 
        !          1242:        fmtver = byte_stream_get8(bs); /* Version of ssi data.  Should be 0x00 */
        !          1243:        od->ssi.numitems += byte_stream_get16(bs); /* # of items in this SSI SNAC */
        !          1244: 
        !          1245:        /* Read in the list */
        !          1246:        while (byte_stream_bytes_left(bs) > 4) { /* last four bytes are timestamp */
        !          1247:                if ((namelen = byte_stream_get16(bs)))
        !          1248:                        name = byte_stream_getstr(bs, namelen);
        !          1249:                else
        !          1250:                        name = NULL;
        !          1251:                gid = byte_stream_get16(bs);
        !          1252:                bid = byte_stream_get16(bs);
        !          1253:                type = byte_stream_get16(bs);
        !          1254:                data = aim_tlvlist_readlen(bs, byte_stream_get16(bs));
        !          1255:                aim_ssi_item_debug_append(debugstr, "\t", aim_ssi_itemlist_add(&od->ssi.official, name, gid, bid, type, data));
        !          1256:                g_free(name);
        !          1257:                aim_tlvlist_free(data);
        !          1258:        }
        !          1259:        purple_debug_misc("oscar", "Reading items from tlvlist for account %s:\n%s",
        !          1260:                purple_connection_get_account(od->gc)->username, debugstr->str);
        !          1261:        g_string_free(debugstr, TRUE);
        !          1262: 
        !          1263:        /* Read in the timestamp */
        !          1264:        od->ssi.timestamp = byte_stream_get32(bs);
        !          1265: 
        !          1266:        if (!(snac->flags & 0x0001)) {
        !          1267:                /* Make a copy of the list */
        !          1268:                struct aim_ssi_item *cur;
        !          1269:                for (cur=od->ssi.official; cur; cur=cur->next)
        !          1270:                        aim_ssi_itemlist_add(&od->ssi.local, cur->name, cur->gid, cur->bid, cur->type, cur->data);
        !          1271: 
        !          1272:                od->ssi.received_data = TRUE;
        !          1273: 
        !          1274:                if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
        !          1275:                        ret = userfunc(od, conn, frame, fmtver, od->ssi.numitems, od->ssi.timestamp);
        !          1276:        }
        !          1277: 
        !          1278:        return ret;
        !          1279: }
        !          1280: 
        !          1281: /*
        !          1282:  * Subtype 0x0007 - SSI Activate Data.
        !          1283:  *
        !          1284:  * Should be sent after receiving 13/6 or 13/f to tell the server you
        !          1285:  * are ready to begin using the list.  It will promptly give you the
        !          1286:  * presence information for everyone in your list and put your permit/deny
        !          1287:  * settings into effect.
        !          1288:  *
        !          1289:  */
        !          1290: int aim_ssi_enable(OscarData *od)
        !          1291: {
        !          1292:        FlapConnection *conn;
        !          1293: 
        !          1294:        if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)))
        !          1295:                return -EINVAL;
        !          1296: 
        !          1297:        aim_genericreq_n(od, conn, SNAC_FAMILY_FEEDBAG, 0x0007);
        !          1298: 
        !          1299:        return 0;
        !          1300: }
        !          1301: 
        !          1302: /*
        !          1303:  * Subtype 0x0008/0x0009/0x000a - SSI Add/Mod/Del Item(s).
        !          1304:  *
        !          1305:  * Sends the SNAC to add, modify, or delete items from the server-stored
        !          1306:  * information.  These 3 SNACs all have an identical structure.  The only
        !          1307:  * difference is the subtype that is set for the SNAC.
        !          1308:  *
        !          1309:  */
        !          1310: static int aim_ssi_addmoddel(OscarData *od)
        !          1311: {
        !          1312:        FlapConnection *conn;
        !          1313:        ByteStream bs;
        !          1314:        aim_snacid_t snacid;
        !          1315:        int bslen;
        !          1316:        struct aim_ssi_tmp *cur;
        !          1317: 
        !          1318:        if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)) || !od->ssi.pending || !od->ssi.pending->item)
        !          1319:                return -EINVAL;
        !          1320: 
        !          1321:        /* Calculate total SNAC size */
        !          1322:        bslen = 0;
        !          1323:        for (cur=od->ssi.pending; cur; cur=cur->next) {
        !          1324:                bslen += 10; /* For length, GID, BID, type, and length */
        !          1325:                if (cur->item->name)
        !          1326:                        bslen += strlen(cur->item->name);
        !          1327:                if (cur->item->data)
        !          1328:                        bslen += aim_tlvlist_size(cur->item->data);
        !          1329:        }
        !          1330: 
        !          1331:        byte_stream_new(&bs, bslen);
        !          1332: 
        !          1333:        for (cur=od->ssi.pending; cur; cur=cur->next) {
        !          1334:                byte_stream_put16(&bs, cur->item->name ? strlen(cur->item->name) : 0);
        !          1335:                if (cur->item->name)
        !          1336:                        byte_stream_putstr(&bs, cur->item->name);
        !          1337:                byte_stream_put16(&bs, cur->item->gid);
        !          1338:                byte_stream_put16(&bs, cur->item->bid);
        !          1339:                byte_stream_put16(&bs, cur->item->type);
        !          1340:                byte_stream_put16(&bs, cur->item->data ? aim_tlvlist_size(cur->item->data) : 0);
        !          1341:                if (cur->item->data)
        !          1342:                        aim_tlvlist_write(&bs, &cur->item->data);
        !          1343:        }
        !          1344: 
        !          1345:        snacid = aim_cachesnac(od, SNAC_FAMILY_FEEDBAG, od->ssi.pending->action, 0x0000, NULL, 0);
        !          1346:        flap_connection_send_snac(od, conn, SNAC_FAMILY_FEEDBAG, od->ssi.pending->action, snacid, &bs);
        !          1347: 
        !          1348:        byte_stream_destroy(&bs);
        !          1349: 
        !          1350:        return 0;
        !          1351: }
        !          1352: 
        !          1353: /*
        !          1354:  * Subtype 0x0008 - Incoming SSI add.
        !          1355:  *
        !          1356:  * Sent by the server, for example, when someone is added to
        !          1357:  * your "Recent Buddies" group.
        !          1358:  */
        !          1359: static int parseadd(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
        !          1360: {
        !          1361:        int ret = 0;
        !          1362:        aim_rxcallback_t userfunc;
        !          1363:        char *name;
        !          1364:        guint16 len, gid, bid, type;
        !          1365:        GSList *data;
        !          1366: 
        !          1367:        while (byte_stream_bytes_left(bs)) {
        !          1368:                if ((len = byte_stream_get16(bs)))
        !          1369:                        name = byte_stream_getstr(bs, len);
        !          1370:                else
        !          1371:                        name = NULL;
        !          1372:                gid = byte_stream_get16(bs);
        !          1373:                bid = byte_stream_get16(bs);
        !          1374:                type = byte_stream_get16(bs);
        !          1375:                if ((len = byte_stream_get16(bs)))
        !          1376:                        data = aim_tlvlist_readlen(bs, len);
        !          1377:                else
        !          1378:                        data = NULL;
        !          1379: 
        !          1380:                aim_ssi_itemlist_add(&od->ssi.local, name, gid, bid, type, data);
        !          1381:                aim_ssi_itemlist_add(&od->ssi.official, name, gid, bid, type, data);
        !          1382:                aim_tlvlist_free(data);
        !          1383: 
        !          1384:                if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
        !          1385:                        ret = userfunc(od, conn, frame, snac->subtype, type, name);
        !          1386: 
        !          1387:                g_free(name);
        !          1388:        }
        !          1389: 
        !          1390:        return ret;
        !          1391: }
        !          1392: 
        !          1393: /*
        !          1394:  * Subtype 0x0009 - Incoming SSI mod.
        !          1395:  */
        !          1396: static int parsemod(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
        !          1397: {
        !          1398:        int ret = 0;
        !          1399:        aim_rxcallback_t userfunc;
        !          1400:        char *name;
        !          1401:        guint16 len, gid, bid, type;
        !          1402:        GSList *data;
        !          1403:        struct aim_ssi_item *item;
        !          1404: 
        !          1405:        while (byte_stream_bytes_left(bs)) {
        !          1406:                if ((len = byte_stream_get16(bs)))
        !          1407:                        name = byte_stream_getstr(bs, len);
        !          1408:                else
        !          1409:                        name = NULL;
        !          1410:                gid = byte_stream_get16(bs);
        !          1411:                bid = byte_stream_get16(bs);
        !          1412:                type = byte_stream_get16(bs);
        !          1413:                if ((len = byte_stream_get16(bs)))
        !          1414:                        data = aim_tlvlist_readlen(bs, len);
        !          1415:                else
        !          1416:                        data = NULL;
        !          1417: 
        !          1418:                /* Replace the 2 local items with the given one */
        !          1419:                if ((item = aim_ssi_itemlist_find(od->ssi.local, gid, bid))) {
        !          1420:                        item->type = type;
        !          1421:                        g_free(item->name);
        !          1422:                        item->name = g_strdup(name);
        !          1423:                        aim_tlvlist_free(item->data);
        !          1424:                        item->data = aim_tlvlist_copy(data);
        !          1425:                }
        !          1426: 
        !          1427:                if ((item = aim_ssi_itemlist_find(od->ssi.official, gid, bid))) {
        !          1428:                        item->type = type;
        !          1429:                        g_free(item->name);
        !          1430:                        item->name = g_strdup(name);
        !          1431:                        aim_tlvlist_free(item->data);
        !          1432:                        item->data = aim_tlvlist_copy(data);
        !          1433:                }
        !          1434: 
        !          1435:                if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
        !          1436:                        ret = userfunc(od, conn, frame, snac->subtype, type, name);
        !          1437: 
        !          1438:                g_free(name);
        !          1439:                aim_tlvlist_free(data);
        !          1440:        }
        !          1441: 
        !          1442:        return ret;
        !          1443: }
        !          1444: 
        !          1445: /*
        !          1446:  * Subtype 0x000a - Incoming SSI del.
        !          1447:  *
        !          1448:  * XXX - It would probably be good for the client to actually do something when it gets this.
        !          1449:  */
        !          1450: static int parsedel(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
        !          1451: {
        !          1452:        int ret = 0;
        !          1453:        aim_rxcallback_t userfunc;
        !          1454:        guint16 gid, bid;
        !          1455:        struct aim_ssi_item *del;
        !          1456: 
        !          1457:        while (byte_stream_bytes_left(bs)) {
        !          1458:                byte_stream_advance(bs, byte_stream_get16(bs));
        !          1459:                gid = byte_stream_get16(bs);
        !          1460:                bid = byte_stream_get16(bs);
        !          1461:                byte_stream_get16(bs);
        !          1462:                byte_stream_advance(bs, byte_stream_get16(bs));
        !          1463: 
        !          1464:                if ((del = aim_ssi_itemlist_find(od->ssi.local, gid, bid)))
        !          1465:                        aim_ssi_itemlist_del(&od->ssi.local, del);
        !          1466:                if ((del = aim_ssi_itemlist_find(od->ssi.official, gid, bid)))
        !          1467:                        aim_ssi_itemlist_del(&od->ssi.official, del);
        !          1468: 
        !          1469:                if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
        !          1470:                        ret = userfunc(od, conn, frame);
        !          1471:        }
        !          1472: 
        !          1473:        return ret;
        !          1474: }
        !          1475: 
        !          1476: /*
        !          1477:  * Subtype 0x000e - SSI Add/Mod/Del Ack.
        !          1478:  *
        !          1479:  * Response to add, modify, or delete SNAC (sent with aim_ssi_addmoddel).
        !          1480:  *
        !          1481:  */
        !          1482: static int parseack(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
        !          1483: {
        !          1484:        int ret = 0;
        !          1485:        aim_rxcallback_t userfunc;
        !          1486:        struct aim_ssi_tmp *cur, *del;
        !          1487: 
        !          1488:        /* Read in the success/failure flags from the ack SNAC */
        !          1489:        cur = od->ssi.pending;
        !          1490:        while (cur && (byte_stream_bytes_left(bs)>0)) {
        !          1491:                cur->ack = byte_stream_get16(bs);
        !          1492:                cur = cur->next;
        !          1493:        }
        !          1494: 
        !          1495:        /*
        !          1496:         * If outcome is 0, then add the item to the item list, or replace the other item,
        !          1497:         * or remove the old item.  If outcome is non-zero, then remove the item from the
        !          1498:         * local list, or unmodify it, or add it.
        !          1499:         */
        !          1500:        for (cur=od->ssi.pending; (cur && (cur->ack != 0xffff)); cur=cur->next) {
        !          1501:        if (cur->item) {
        !          1502:                if (cur->ack) {
        !          1503:                        /* Our action was unsuccessful, so change the local list back to how it was */
        !          1504:                        if (cur->action == SNAC_SUBTYPE_FEEDBAG_ADD) {
        !          1505:                                /* Remove the item from the local list */
        !          1506:                                /* Make sure cur->item is still valid memory */
        !          1507:                                if (aim_ssi_itemlist_valid(od->ssi.local, cur->item)) {
        !          1508:                                        cur->name = g_strdup(cur->item->name);
        !          1509:                                        aim_ssi_itemlist_del(&od->ssi.local, cur->item);
        !          1510:                                }
        !          1511:                                cur->item = NULL;
        !          1512: 
        !          1513:                        } else if (cur->action == SNAC_SUBTYPE_FEEDBAG_MOD) {
        !          1514:                                /* Replace the local item with the item from the official list */
        !          1515:                                if (aim_ssi_itemlist_valid(od->ssi.local, cur->item)) {
        !          1516:                                        struct aim_ssi_item *cur1;
        !          1517:                                        if ((cur1 = aim_ssi_itemlist_find(od->ssi.official, cur->item->gid, cur->item->bid))) {
        !          1518:                                                g_free(cur->item->name);
        !          1519:                                                cur->item->name = g_strdup(cur1->name);
        !          1520:                                                aim_tlvlist_free(cur->item->data);
        !          1521:                                                cur->item->data = aim_tlvlist_copy(cur1->data);
        !          1522:                                        }
        !          1523:                                } else
        !          1524:                                        cur->item = NULL;
        !          1525: 
        !          1526:                        } else if (cur->action == SNAC_SUBTYPE_FEEDBAG_DEL) {
        !          1527:                                /* Add the item back into the local list */
        !          1528:                                if (aim_ssi_itemlist_valid(od->ssi.official, cur->item)) {
        !          1529:                                        aim_ssi_itemlist_add(&od->ssi.local, cur->item->name, cur->item->gid, cur->item->bid, cur->item->type, cur->item->data);
        !          1530:                                } else
        !          1531:                                        cur->item = NULL;
        !          1532:                        }
        !          1533: 
        !          1534:                } else {
        !          1535:                        /* Do the exact opposite */
        !          1536:                        if (cur->action == SNAC_SUBTYPE_FEEDBAG_ADD) {
        !          1537:                        /* Add the local item to the official list */
        !          1538:                                if (aim_ssi_itemlist_valid(od->ssi.local, cur->item)) {
        !          1539:                                        aim_ssi_itemlist_add(&od->ssi.official, cur->item->name, cur->item->gid, cur->item->bid, cur->item->type, cur->item->data);
        !          1540:                                } else
        !          1541:                                        cur->item = NULL;
        !          1542: 
        !          1543:                        } else if (cur->action == SNAC_SUBTYPE_FEEDBAG_MOD) {
        !          1544:                                /* Replace the official item with the item from the local list */
        !          1545:                                if (aim_ssi_itemlist_valid(od->ssi.local, cur->item)) {
        !          1546:                                        struct aim_ssi_item *cur1;
        !          1547:                                        if ((cur1 = aim_ssi_itemlist_find(od->ssi.official, cur->item->gid, cur->item->bid))) {
        !          1548:                                                g_free(cur1->name);
        !          1549:                                                cur1->name = g_strdup(cur->item->name);
        !          1550:                                                aim_tlvlist_free(cur1->data);
        !          1551:                                                cur1->data = aim_tlvlist_copy(cur->item->data);
        !          1552:                                        }
        !          1553:                                } else
        !          1554:                                        cur->item = NULL;
        !          1555: 
        !          1556:                        } else if (cur->action == SNAC_SUBTYPE_FEEDBAG_DEL) {
        !          1557:                                /* Remove the item from the official list */
        !          1558:                                if (aim_ssi_itemlist_valid(od->ssi.official, cur->item))
        !          1559:                                        aim_ssi_itemlist_del(&od->ssi.official, cur->item);
        !          1560:                                cur->item = NULL;
        !          1561:                        }
        !          1562: 
        !          1563:                }
        !          1564:        } /* End if (cur->item) */
        !          1565:        } /* End for loop */
        !          1566: 
        !          1567:        if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
        !          1568:                ret = userfunc(od, conn, frame, od->ssi.pending);
        !          1569: 
        !          1570:        /* Free all aim_ssi_tmp's with an outcome */
        !          1571:        cur = od->ssi.pending;
        !          1572:        while (cur && (cur->ack != 0xffff)) {
        !          1573:                del = cur;
        !          1574:                cur = cur->next;
        !          1575:                g_free(del->name);
        !          1576:                g_free(del);
        !          1577:        }
        !          1578:        od->ssi.pending = cur;
        !          1579: 
        !          1580:        /* If we're not waiting for any more acks, then send more SNACs */
        !          1581:        if (!od->ssi.pending) {
        !          1582:                od->ssi.waiting_for_ack = FALSE;
        !          1583:                aim_ssi_sync(od);
        !          1584:        }
        !          1585: 
        !          1586:        return ret;
        !          1587: }
        !          1588: 
        !          1589: /*
        !          1590:  * Subtype 0x000f - SSI Data Unchanged.
        !          1591:  *
        !          1592:  * Response to aim_ssi_reqifchanged() if the server-side data is not newer than
        !          1593:  * posted local stamp/revision.
        !          1594:  *
        !          1595:  */
        !          1596: static int parsedataunchanged(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
        !          1597: {
        !          1598:        int ret = 0;
        !          1599:        aim_rxcallback_t userfunc;
        !          1600: 
        !          1601:        od->ssi.received_data = TRUE;
        !          1602: 
        !          1603:        if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
        !          1604:                ret = userfunc(od, conn, frame);
        !          1605: 
        !          1606:        return ret;
        !          1607: }
        !          1608: 
        !          1609: /*
        !          1610:  * Subtype 0x0011 - SSI Begin Data Modification.
        !          1611:  *
        !          1612:  * Tell the server you're going to start modifying data.  This marks
        !          1613:  * the beginning of a transaction.
        !          1614:  */
        !          1615: int aim_ssi_modbegin(OscarData *od)
        !          1616: {
        !          1617:        FlapConnection *conn;
        !          1618: 
        !          1619:        if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)))
        !          1620:                return -EINVAL;
        !          1621: 
        !          1622:        aim_genericreq_n(od, conn, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_EDITSTART);
        !          1623: 
        !          1624:        return 0;
        !          1625: }
        !          1626: 
        !          1627: /*
        !          1628:  * Subtype 0x0012 - SSI End Data Modification.
        !          1629:  *
        !          1630:  * Tell the server you're finished modifying data.  The marks the end
        !          1631:  * of a transaction.
        !          1632:  */
        !          1633: int aim_ssi_modend(OscarData *od)
        !          1634: {
        !          1635:        FlapConnection *conn;
        !          1636: 
        !          1637:        if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)))
        !          1638:                return -EINVAL;
        !          1639: 
        !          1640:        aim_genericreq_n(od, conn, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_EDITSTOP);
        !          1641: 
        !          1642:        return 0;
        !          1643: }
        !          1644: 
        !          1645: /*
        !          1646:  * Subtype 0x0015 - Receive an authorization grant
        !          1647:  */
        !          1648: static int receiveauthgrant(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
        !          1649: {
        !          1650:        int ret = 0;
        !          1651:        aim_rxcallback_t userfunc;
        !          1652:        guint16 tmp;
        !          1653:        char *bn, *msg, *tmpstr;
        !          1654: 
        !          1655:        /* Read buddy name */
        !          1656:        tmp = byte_stream_get8(bs);
        !          1657:        if (!tmp) {
        !          1658:                purple_debug_warning("oscar", "Dropping auth grant SNAC "
        !          1659:                                "because username was empty\n");
        !          1660:                return 0;
        !          1661:        }
        !          1662:        bn = byte_stream_getstr(bs, tmp);
        !          1663:        if (!g_utf8_validate(bn, -1, NULL)) {
        !          1664:                purple_debug_warning("oscar", "Dropping auth grant SNAC "
        !          1665:                                "because the username was not valid UTF-8\n");
        !          1666:                g_free(bn);
        !          1667:        }
        !          1668: 
        !          1669:        /* Read message */
        !          1670:        tmp = byte_stream_get16(bs);
        !          1671:        if (tmp) {
        !          1672:                msg = byte_stream_getstr(bs, tmp);
        !          1673:                if (!g_utf8_validate(msg, -1, NULL)) {
        !          1674:                        /* Ugh, msg isn't UTF8.  Let's salvage. */
        !          1675:                        purple_debug_warning("oscar", "Got non-UTF8 message in auth "
        !          1676:                                        "grant from %s\n", bn);
        !          1677:                        tmpstr = purple_utf8_salvage(msg);
        !          1678:                        g_free(msg);
        !          1679:                        msg = tmpstr;
        !          1680:                }
        !          1681:        } else
        !          1682:                msg = NULL;
        !          1683: 
        !          1684:        /* Unknown */
        !          1685:        tmp = byte_stream_get16(bs);
        !          1686: 
        !          1687:        if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
        !          1688:                ret = userfunc(od, conn, frame, bn, msg);
        !          1689: 
        !          1690:        g_free(bn);
        !          1691:        g_free(msg);
        !          1692: 
        !          1693:        return ret;
        !          1694: }
        !          1695: 
        !          1696: /*
        !          1697:  * Subtype 0x0018 - Send authorization request
        !          1698:  *
        !          1699:  * Sends a request for authorization to the given contact.  The request will either be
        !          1700:  * granted, denied, or dropped.
        !          1701:  *
        !          1702:  */
        !          1703: int aim_ssi_sendauthrequest(OscarData *od, const char *bn, const char *msg)
        !          1704: {
        !          1705:        FlapConnection *conn;
        !          1706:        ByteStream bs;
        !          1707:        aim_snacid_t snacid;
        !          1708: 
        !          1709:        if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)) || !bn)
        !          1710:                return -EINVAL;
        !          1711: 
        !          1712:        byte_stream_new(&bs, 1+strlen(bn) + 2+(msg ? strlen(msg)+1 : 0) + 2);
        !          1713: 
        !          1714:        /* Username */
        !          1715:        byte_stream_put8(&bs, strlen(bn));
        !          1716:        byte_stream_putstr(&bs, bn);
        !          1717: 
        !          1718:        /* Message (null terminated) */
        !          1719:        byte_stream_put16(&bs, msg ? strlen(msg) : 0);
        !          1720:        if (msg) {
        !          1721:                byte_stream_putstr(&bs, msg);
        !          1722:                byte_stream_put8(&bs, 0x00);
        !          1723:        }
        !          1724: 
        !          1725:        /* Unknown */
        !          1726:        byte_stream_put16(&bs, 0x0000);
        !          1727: 
        !          1728:        snacid = aim_cachesnac(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_SENDAUTHREQ, 0x0000, NULL, 0);
        !          1729:        flap_connection_send_snac(od, conn, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_SENDAUTHREQ, snacid, &bs);
        !          1730: 
        !          1731:        byte_stream_destroy(&bs);
        !          1732: 
        !          1733:        return 0;
        !          1734: }
        !          1735: 
        !          1736: /*
        !          1737:  * Subtype 0x0019 - Receive an authorization request
        !          1738:  */
        !          1739: static int receiveauthrequest(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
        !          1740: {
        !          1741:        int ret = 0;
        !          1742:        aim_rxcallback_t userfunc;
        !          1743:        guint16 tmp;
        !          1744:        char *bn, *msg, *tmpstr;
        !          1745: 
        !          1746:        /* Read buddy name */
        !          1747:        tmp = byte_stream_get8(bs);
        !          1748:        if (!tmp) {
        !          1749:                purple_debug_warning("oscar", "Dropping auth request SNAC "
        !          1750:                                "because username was empty\n");
        !          1751:                return 0;
        !          1752:        }
        !          1753:        bn = byte_stream_getstr(bs, tmp);
        !          1754:        if (!g_utf8_validate(bn, -1, NULL)) {
        !          1755:                purple_debug_warning("oscar", "Dropping auth request SNAC "
        !          1756:                                "because the username was not valid UTF-8\n");
        !          1757:                g_free(bn);
        !          1758:        }
        !          1759: 
        !          1760:        /* Read message */
        !          1761:        tmp = byte_stream_get16(bs);
        !          1762:        if (tmp) {
        !          1763:                msg = byte_stream_getstr(bs, tmp);
        !          1764:                if (!g_utf8_validate(msg, -1, NULL)) {
        !          1765:                        /* Ugh, msg isn't UTF8.  Let's salvage. */
        !          1766:                        purple_debug_warning("oscar", "Got non-UTF8 message in auth "
        !          1767:                                        "request from %s\n", bn);
        !          1768:                        tmpstr = purple_utf8_salvage(msg);
        !          1769:                        g_free(msg);
        !          1770:                        msg = tmpstr;
        !          1771:                }
        !          1772:        } else
        !          1773:                msg = NULL;
        !          1774: 
        !          1775:        /* Unknown */
        !          1776:        tmp = byte_stream_get16(bs);
        !          1777: 
        !          1778:        if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
        !          1779:                ret = userfunc(od, conn, frame, bn, msg);
        !          1780: 
        !          1781:        g_free(bn);
        !          1782:        g_free(msg);
        !          1783: 
        !          1784:        return ret;
        !          1785: }
        !          1786: 
        !          1787: /*
        !          1788:  * Subtype 0x001a - Send authorization reply
        !          1789:  *
        !          1790:  * Sends a reply to a request for authorization.  The reply can either
        !          1791:  * grant authorization or deny authorization.
        !          1792:  *
        !          1793:  * if reply=0x00 then deny
        !          1794:  * if reply=0x01 then grant
        !          1795:  *
        !          1796:  */
        !          1797: int aim_ssi_sendauthreply(OscarData *od, const char *bn, guint8 reply, const char *msg)
        !          1798: {
        !          1799:        FlapConnection *conn;
        !          1800:        ByteStream bs;
        !          1801:        aim_snacid_t snacid;
        !          1802: 
        !          1803:        if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)) || !bn)
        !          1804:                return -EINVAL;
        !          1805: 
        !          1806:        byte_stream_new(&bs, 1+strlen(bn) + 1 + 2+(msg ? (strlen(msg)+1) : 0) + 2);
        !          1807: 
        !          1808:        /* Username */
        !          1809:        byte_stream_put8(&bs, strlen(bn));
        !          1810:        byte_stream_putstr(&bs, bn);
        !          1811: 
        !          1812:        /* Grant or deny */
        !          1813:        byte_stream_put8(&bs, reply);
        !          1814: 
        !          1815:        /* Message (null terminated) */
        !          1816:        byte_stream_put16(&bs, msg ? (strlen(msg)+1) : 0);
        !          1817:        if (msg) {
        !          1818:                byte_stream_putstr(&bs, msg);
        !          1819:                byte_stream_put8(&bs, 0x00);
        !          1820:        }
        !          1821: 
        !          1822:        /* Unknown */
        !          1823:        byte_stream_put16(&bs, 0x0000);
        !          1824: 
        !          1825:        snacid = aim_cachesnac(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_SENDAUTHREP, 0x0000, NULL, 0);
        !          1826:        flap_connection_send_snac(od, conn, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_SENDAUTHREP, snacid, &bs);
        !          1827: 
        !          1828:        byte_stream_destroy(&bs);
        !          1829: 
        !          1830:        return 0;
        !          1831: }
        !          1832: 
        !          1833: /*
        !          1834:  * Subtype 0x001b - Receive an authorization reply
        !          1835:  *
        !          1836:  * You get this bad boy when other people respond to the authorization
        !          1837:  * request that you have previously sent them.
        !          1838:  */
        !          1839: static int receiveauthreply(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
        !          1840: {
        !          1841:        int ret = 0;
        !          1842:        aim_rxcallback_t userfunc;
        !          1843:        guint16 tmp;
        !          1844:        guint8 reply;
        !          1845:        char *bn, *msg, *tmpstr;
        !          1846: 
        !          1847:        /* Read buddy name */
        !          1848:        tmp = byte_stream_get8(bs);
        !          1849:        if (!tmp) {
        !          1850:                purple_debug_warning("oscar", "Dropping auth reply SNAC "
        !          1851:                                "because username was empty\n");
        !          1852:                return 0;
        !          1853:        }
        !          1854:        bn = byte_stream_getstr(bs, tmp);
        !          1855:        if (!g_utf8_validate(bn, -1, NULL)) {
        !          1856:                purple_debug_warning("oscar", "Dropping auth reply SNAC "
        !          1857:                                "because the username was not valid UTF-8\n");
        !          1858:                g_free(bn);
        !          1859:        }
        !          1860: 
        !          1861:        /* Read reply */
        !          1862:        reply = byte_stream_get8(bs);
        !          1863: 
        !          1864:        /* Read message */
        !          1865:        tmp = byte_stream_get16(bs);
        !          1866:        if (tmp) {
        !          1867:                msg = byte_stream_getstr(bs, tmp);
        !          1868:                if (!g_utf8_validate(msg, -1, NULL)) {
        !          1869:                        /* Ugh, msg isn't UTF8.  Let's salvage. */
        !          1870:                        purple_debug_warning("oscar", "Got non-UTF8 message in auth "
        !          1871:                                        "reply from %s\n", bn);
        !          1872:                        tmpstr = purple_utf8_salvage(msg);
        !          1873:                        g_free(msg);
        !          1874:                        msg = tmpstr;
        !          1875:                }
        !          1876:        } else
        !          1877:                msg = NULL;
        !          1878: 
        !          1879:        /* Unknown */
        !          1880:        tmp = byte_stream_get16(bs);
        !          1881: 
        !          1882:        if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
        !          1883:                ret = userfunc(od, conn, frame, bn, reply, msg);
        !          1884: 
        !          1885:        g_free(bn);
        !          1886:        g_free(msg);
        !          1887: 
        !          1888:        return ret;
        !          1889: }
        !          1890: 
        !          1891: /*
        !          1892:  * Subtype 0x001c - Receive a message telling you someone added you to their list.
        !          1893:  */
        !          1894: static int receiveadded(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
        !          1895: {
        !          1896:        int ret = 0;
        !          1897:        aim_rxcallback_t userfunc;
        !          1898:        guint16 tmp;
        !          1899:        char *bn;
        !          1900: 
        !          1901:        /* Read buddy name */
        !          1902:        tmp = byte_stream_get8(bs);
        !          1903:        if (!tmp) {
        !          1904:                purple_debug_warning("oscar", "Dropping 'you were added' SNAC "
        !          1905:                                "because username was empty\n");
        !          1906:                return 0;
        !          1907:        }
        !          1908:        bn = byte_stream_getstr(bs, tmp);
        !          1909:        if (!g_utf8_validate(bn, -1, NULL)) {
        !          1910:                purple_debug_warning("oscar", "Dropping 'you were added' SNAC "
        !          1911:                                "because the username was not valid UTF-8\n");
        !          1912:                g_free(bn);
        !          1913:        }
        !          1914: 
        !          1915:        if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
        !          1916:                ret = userfunc(od, conn, frame, bn);
        !          1917: 
        !          1918:        g_free(bn);
        !          1919: 
        !          1920:        return ret;
        !          1921: }
        !          1922: 
        !          1923: /*
        !          1924:  * If we're on ICQ, then AIM_SSI_TYPE_DENY is used for the "permanently invisible" list.
        !          1925:  * AIM_SSI_TYPE_ICQDENY is used for blocking users instead.
        !          1926:  */
        !          1927: guint16
        !          1928: aim_ssi_getdenyentrytype(OscarData* od)
        !          1929: {
        !          1930:        return od->icq ? AIM_SSI_TYPE_ICQDENY : AIM_SSI_TYPE_DENY;
        !          1931: }
        !          1932: 
        !          1933: static int
        !          1934: snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
        !          1935: {
        !          1936:        if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_RIGHTSINFO)
        !          1937:                return parserights(od, conn, mod, frame, snac, bs);
        !          1938:        else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_LIST)
        !          1939:                return parsedata(od, conn, mod, frame, snac, bs);
        !          1940:        else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_ADD)
        !          1941:                return parseadd(od, conn, mod, frame, snac, bs);
        !          1942:        else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_MOD)
        !          1943:                return parsemod(od, conn, mod, frame, snac, bs);
        !          1944:        else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_DEL)
        !          1945:                return parsedel(od, conn, mod, frame, snac, bs);
        !          1946:        else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_SRVACK)
        !          1947:                return parseack(od, conn, mod, frame, snac, bs);
        !          1948:        else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_NOLIST)
        !          1949:                return parsedataunchanged(od, conn, mod, frame, snac, bs);
        !          1950:        else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_RECVAUTH)
        !          1951:                return receiveauthgrant(od, conn, mod, frame, snac, bs);
        !          1952:        else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_RECVAUTHREQ)
        !          1953:                return receiveauthrequest(od, conn, mod, frame, snac, bs);
        !          1954:        else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_RECVAUTHREP)
        !          1955:                return receiveauthreply(od, conn, mod, frame, snac, bs);
        !          1956:        else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_ADDED)
        !          1957:                return receiveadded(od, conn, mod, frame, snac, bs);
        !          1958: 
        !          1959:        return 0;
        !          1960: }
        !          1961: 
        !          1962: static void
        !          1963: ssi_shutdown(OscarData *od, aim_module_t *mod)
        !          1964: {
        !          1965:        aim_ssi_freelist(od);
        !          1966: }
        !          1967: 
        !          1968: int
        !          1969: ssi_modfirst(OscarData *od, aim_module_t *mod)
        !          1970: {
        !          1971:        mod->family = SNAC_FAMILY_FEEDBAG;
        !          1972:        mod->version = 0x0004;
        !          1973:        mod->toolid = 0x0110;
        !          1974:        mod->toolversion = 0x0629;
        !          1975:        mod->flags = 0;
        !          1976:        strncpy(mod->name, "feedbag", sizeof(mod->name));
        !          1977:        mod->snachandler = snachandler;
        !          1978:        mod->shutdown = ssi_shutdown;
        !          1979: 
        !          1980:        return 0;
        !          1981: }

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