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

1.1       snw         1: /*
                      2:  * Purple's oscar protocol plugin
                      3:  * This file is the legal property of its developers.
                      4:  * Please see the AUTHORS file distributed alongside this file.
                      5:  *
                      6:  * This library is free software; you can redistribute it and/or
                      7:  * modify it under the terms of the GNU Lesser General Public
                      8:  * License as published by the Free Software Foundation; either
                      9:  * version 2 of the License, or (at your option) any later version.
                     10:  *
                     11:  * This library is distributed in the hope that it will be useful,
                     12:  * but WITHOUT ANY WARRANTY; without even the implied warranty of
                     13:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
                     14:  * Lesser General Public License for more details.
                     15:  *
                     16:  * You should have received a copy of the GNU Lesser General Public
                     17:  * License along with this library; if not, write to the Free Software
                     18:  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
                     19: */
                     20: 
                     21: /*
                     22:  * Family 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>