Changeset e374dee for libfaim/ssi.c


Ignore:
Timestamp:
Oct 10, 2003, 5:12:30 PM (18 years ago)
Author:
James M. Kretchmar <kretch@mit.edu>
Branches:
master, barnowl_perlaim, debian, owl, release-1.4, release-1.5, release-1.6, release-1.7, release-1.8, release-1.9
Children:
fe6f1d3
Parents:
f4d0975
Message:
*** empty log message ***
File:
1 edited

Legend:

Unmodified
Added
Removed
  • libfaim/ssi.c

    r862371b re374dee  
    11/*
    2  * Server-Side/Stored Information.
    3  *
    4  * Relatively new facility that allows storing of certain types of information,
    5  * such as a users buddy list, permit/deny list, and permit/deny preferences,
    6  * to be stored on the server, so that they can be accessed from any client.
    7  *
    8  * We keep a copy of the ssi data in sess->ssi, because the data needs to be
    9  * accessed for various reasons.  So all the "aim_ssi_itemlist_bleh" functions
    10  * near the top just manage the local data.
     2 * Family 0x0013 - Server-Side/Stored Information.
     3 *
     4 * Relatively new facility that allows certain types of information, such as
     5 * a user's buddy list, permit/deny list, and permit/deny preferences, to be
     6 * stored on the server, so that they can be accessed from any client.
     7 *
     8 * We keep 2 copies of SSI data:
     9 * 1) An exact copy of what is stored on the AIM servers.
     10 * 2) A local copy that we make changes to, and then send diffs
     11 *    between this and the exact copy to keep them in sync.
     12 *
     13 * All the "aim_ssi_itemlist_bleh" functions near the top just modify the list
     14 * that is given to them (i.e. they don't send SNACs).
    1115 *
    1216 * The SNAC sending and receiving functions are lower down in the file, and
     
    1822 * You don't know the half of it.
    1923 *
    20  * XXX - Test for memory leaks
    21  * XXX - Better parsing of rights, and use the rights info to limit adds
     24 * XXX - Preserve unknown data in TLV lists
    2225 *
    2326 */
     
    2730
    2831/**
     32 * Locally rebuild the 0x00c8 TLV in the additional data of the given group.
     33 *
     34 * @param list A pointer to a pointer to the current list of items.
     35 * @param name A null terminated string containing the group name, or NULL
     36 *        if you want to modify the master group.
     37 * @return Return a pointer to the modified item.
     38 */
     39static struct aim_ssi_item *aim_ssi_itemlist_rebuildgroup(struct aim_ssi_item *list, const char *name)
     40{
     41        int newlen;
     42        struct aim_ssi_item *cur, *group;
     43
     44        if (!list)
     45                return NULL;
     46
     47        /* Find the group */
     48        if (!(group = aim_ssi_itemlist_finditem(list, name, NULL, AIM_SSI_TYPE_GROUP)))
     49                return NULL;
     50
     51        /* Free the old data */
     52        aim_freetlvchain(&group->data);
     53        group->data = NULL;
     54
     55        /* Find the length for the new additional data */
     56        newlen = 0;
     57        if (group->gid == 0x0000) {
     58                for (cur=list; cur; cur=cur->next)
     59                        if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid != 0x0000))
     60                                newlen += 2;
     61        } else {
     62                for (cur=list; cur; cur=cur->next)
     63                        if ((cur->gid == group->gid) && (cur->type == AIM_SSI_TYPE_BUDDY))
     64                                newlen += 2;
     65        }
     66
     67        /* Build the new TLV list */
     68        if (newlen > 0) {
     69                fu8_t *newdata;
     70
     71                if (!(newdata = (fu8_t *)malloc((newlen)*sizeof(fu8_t))))
     72                        return NULL;
     73                newlen = 0;
     74                if (group->gid == 0x0000) {
     75                        for (cur=list; cur; cur=cur->next)
     76                                if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid != 0x0000))
     77                                                newlen += aimutil_put16(newdata+newlen, cur->gid);
     78                } else {
     79                        for (cur=list; cur; cur=cur->next)
     80                                if ((cur->gid == group->gid) && (cur->type == AIM_SSI_TYPE_BUDDY))
     81                                                newlen += aimutil_put16(newdata+newlen, cur->bid);
     82                }
     83                aim_addtlvtochain_raw(&group->data, 0x00c8, newlen, newdata);
     84
     85                free(newdata);
     86        }
     87
     88        return group;
     89}
     90
     91/**
    2992 * Locally add a new item to the given item list.
    3093 *
    3194 * @param list A pointer to a pointer to the current list of items.
    32  * @param parent A pointer to the parent group, or NULL if the item should have no
    33  *        parent group (ie. the group ID# should be 0).
    3495 * @param name A null terminated string of the name of the new item, or NULL if the
    3596 *        item should have no name.
    36  * @param type The type of the item, 0x0001 for a contact, 0x0002 for a group, etc.
    37  * @return The newly created item.
    38  */
    39 static struct aim_ssi_item *aim_ssi_itemlist_add(struct aim_ssi_item **list, struct aim_ssi_item *parent, const char *name, fu16_t type)
     97 * @param gid The group ID# you want the new item to have, or 0xFFFF if we should pick something.
     98 * @param bid The buddy ID# you want the new item to have, or 0xFFFF if we should pick something.
     99 * @param type The type of the item, 0x0000 for a contact, 0x0001 for a group, etc.
     100 * @param data The additional data for the new item.
     101 * @return A pointer to the newly created item.
     102 */
     103static struct aim_ssi_item *aim_ssi_itemlist_add(struct aim_ssi_item **list, const char *name, fu16_t gid, fu16_t bid, fu16_t type, aim_tlvlist_t *data)
    40104{
    41105        int i;
    42         struct aim_ssi_item *cur, *newitem;
    43 
    44         if (!(newitem = (struct aim_ssi_item *)malloc(sizeof(struct aim_ssi_item))))
     106        struct aim_ssi_item *cur, *new;
     107
     108        if (!list)
     109                return NULL;
     110
     111        if (!(new = (struct aim_ssi_item *)malloc(sizeof(struct aim_ssi_item))))
    45112                return NULL;
    46113
    47114        /* Set the name */
    48115        if (name) {
    49                 if (!(newitem->name = (char *)malloc((strlen(name)+1)*sizeof(char)))) {
    50                         free(newitem);
    51                         return NULL;
    52                 }
    53                 strcpy(newitem->name, name);
     116                new->name = (char *)malloc((strlen(name)+1)*sizeof(char));
     117                strcpy(new->name, name);
    54118        } else
    55                 newitem->name = NULL;
    56 
    57         /* Set the group ID# and the buddy ID# */
    58         newitem->gid = 0x0000;
    59         newitem->bid = 0x0000;
     119                new->name = NULL;
     120
     121        /* Set the group ID# and buddy ID# */
     122        new->gid = gid;
     123        new->bid = bid;
    60124        if (type == AIM_SSI_TYPE_GROUP) {
    61                 if (name)
     125                if ((new->gid == 0xFFFF) && name) {
    62126                        do {
    63                                 newitem->gid += 0x0001;
     127                                new->gid += 0x0001;
    64128                                for (cur=*list, i=0; ((cur) && (!i)); cur=cur->next)
    65                                         if ((cur->gid == newitem->gid) && (cur->gid == newitem->gid))
     129                                        if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid == new->gid))
    66130                                                i=1;
    67131                        } while (i);
     132                }
    68133        } else {
    69                 if (parent)
    70                         newitem->gid = parent->gid;
    71                 do {
    72                         newitem->bid += 0x0001;
    73                         for (cur=*list, i=0; ((cur) && (!i)); cur=cur->next)
    74                                 if ((cur->bid == newitem->bid) && (cur->gid == newitem->gid))
    75                                         i=1;
    76                 } while (i);
    77         }
    78 
    79         /* Set the rest */
    80         newitem->type = type;
    81         newitem->data = NULL;
    82         newitem->next = *list;
    83         *list = newitem;
    84 
    85         return newitem;
    86 }
    87 
    88 /**
    89  * Locally rebuild the 0x00c8 TLV in the additional data of the given group.
     134                if (new->bid == 0xFFFF) {
     135                        do {
     136                                new->bid += 0x0001;
     137                                for (cur=*list, i=0; ((cur) && (!i)); cur=cur->next)
     138                                        if ((cur->bid == new->bid) && (cur->gid == new->gid))
     139                                                i=1;
     140                        } while (i);
     141                }
     142        }
     143
     144        /* Set the type */
     145        new->type = type;
     146
     147        /* Set the TLV list */
     148        new->data = aim_tlvlist_copy(data);
     149
     150        /* Add the item to the list in the correct numerical position.  Fancy, eh? */
     151        if (*list) {
     152                if ((new->gid < (*list)->gid) || ((new->gid == (*list)->gid) && (new->bid < (*list)->bid))) {
     153                        new->next = *list;
     154                        *list = new;
     155                } else {
     156                        struct aim_ssi_item *prev;
     157                        for ((prev=*list, cur=(*list)->next); (cur && ((new->gid > cur->gid) || ((new->gid == cur->gid) && (new->bid > cur->bid)))); prev=cur, cur=cur->next);
     158                        new->next = prev->next;
     159                        prev->next = new;
     160                }
     161        } else {
     162                new->next = *list;
     163                *list = new;
     164        }
     165
     166        return new;
     167}
     168
     169/**
     170 * Locally delete an item from the given item list.
    90171 *
    91172 * @param list A pointer to a pointer to the current list of items.
    92  * @param parentgroup A pointer to the group who's additional data you want to rebuild.
     173 * @param del A pointer to the item you want to remove from the list.
    93174 * @return Return 0 if no errors, otherwise return the error number.
    94175 */
    95 static int aim_ssi_itemlist_rebuildgroup(struct aim_ssi_item **list, struct aim_ssi_item *parentgroup)
    96 {
    97         int newlen;
     176static int aim_ssi_itemlist_del(struct aim_ssi_item **list, struct aim_ssi_item *del)
     177{
     178        if (!list || !(*list) || !del)
     179                return -EINVAL;
     180
     181        /* Remove the item from the list */
     182        if (*list == del) {
     183                *list = (*list)->next;
     184        } else {
     185                struct aim_ssi_item *cur;
     186                for (cur=*list; (cur->next && (cur->next!=del)); cur=cur->next);
     187                if (cur->next)
     188                        cur->next = cur->next->next;
     189        }
     190
     191        /* Free the deleted item */
     192        free(del->name);
     193        aim_freetlvchain(&del->data);
     194        free(del);
     195
     196        return 0;
     197}
     198
     199/**
     200 * Compare two items to see if they have the same data.
     201 *
     202 * @param cur1 A pointer to a pointer to the first item.
     203 * @param cur2 A pointer to a pointer to the second item.
     204 * @return Return 0 if no differences, or a number if there are differences.
     205 */
     206static int aim_ssi_itemlist_cmp(struct aim_ssi_item *cur1, struct aim_ssi_item *cur2)
     207{
     208        if (!cur1 || !cur2)
     209                return 1;
     210
     211        if (cur1->data && !cur2->data)
     212                return 2;
     213
     214        if (!cur1->data && cur2->data)
     215                return 3;
     216
     217        if ((cur1->data && cur2->data) && (aim_tlvlist_cmp(cur1->data, cur2->data)))
     218                        return 4;
     219
     220        if (cur1->name && !cur2->name)
     221                return 5;
     222
     223        if (!cur1->name && cur2->name)
     224                return 6;
     225
     226        if (cur1->name && cur2->name && aim_sncmp(cur1->name, cur2->name))
     227                return 7;
     228
     229        if (cur1->gid != cur2->gid)
     230                return 8;
     231
     232        if (cur1->bid != cur2->bid)
     233                return 9;
     234
     235        if (cur1->type != cur2->type)
     236                return 10;
     237
     238        return 0;
     239}
     240
     241faim_export int aim_ssi_itemlist_valid(struct aim_ssi_item *list, struct aim_ssi_item *item)
     242{
    98243        struct aim_ssi_item *cur;
    99 
    100         /* Free the old additional data */
    101         if (parentgroup->data) {
    102                 aim_freetlvchain((aim_tlvlist_t **)&parentgroup->data);
    103                 parentgroup->data = NULL;
    104         }
    105 
    106         /* Find the length for the new additional data */
    107         newlen = 0;
    108         if (parentgroup->gid == 0x0000) {
    109                 for (cur=*list; cur; cur=cur->next)
    110                         if ((cur->gid != 0x0000) && (cur->type == AIM_SSI_TYPE_GROUP))
    111                                 newlen += 2;
    112         } else {
    113                 for (cur=*list; cur; cur=cur->next)
    114                         if ((cur->gid == parentgroup->gid) && (cur->type == AIM_SSI_TYPE_BUDDY))
    115                                 newlen += 2;
    116         }
    117 
    118         /* Rebuild the additional data */
    119         if (newlen>0) {
    120                 fu8_t *newdata;
    121 
    122                 if (!(newdata = (fu8_t *)malloc((newlen)*sizeof(fu8_t))))
    123                         return -ENOMEM;
    124                 newlen = 0;
    125                 if (parentgroup->gid == 0x0000) {
    126                         for (cur=*list; cur; cur=cur->next)
    127                                 if ((cur->gid != 0x0000) && (cur->type == AIM_SSI_TYPE_GROUP))
    128                                                 newlen += aimutil_put16(newdata+newlen, cur->gid);
    129                 } else {
    130                         for (cur=*list; cur; cur=cur->next)
    131                                 if ((cur->gid == parentgroup->gid) && (cur->type == AIM_SSI_TYPE_BUDDY))
    132                                                 newlen += aimutil_put16(newdata+newlen, cur->bid);
    133                 }
    134                 aim_addtlvtochain_raw((aim_tlvlist_t **)&(parentgroup->data), 0x00c8, newlen, newdata);
    135 
    136                 free(newdata);
    137         }
    138 
    139         return 0;
    140 }
    141 
    142 /**
    143  * Locally free all of the stored buddy list information.
    144  *
    145  * @param sess The oscar session.
    146  * @return Return 0 if no errors, otherwise return the error number.
    147  */
    148 static int aim_ssi_freelist(aim_session_t *sess)
    149 {
    150         struct aim_ssi_item *cur, *delitem;
    151 
    152         cur = sess->ssi.items;
    153         while (cur) {
    154                 if (cur->name)  free(cur->name);
    155                 if (cur->data)  aim_freetlvchain((aim_tlvlist_t **)&cur->data);
    156                 delitem = cur;
    157                 cur = cur->next;
    158                 free(delitem);
    159         }
    160 
    161         sess->ssi.items = NULL;
    162         sess->ssi.revision = 0;
    163         sess->ssi.timestamp = (time_t)0;
    164 
     244        for (cur=list; cur; cur=cur->next)
     245                if (cur == item)
     246                        return 1;
    165247        return 0;
    166248}
     
    208290                        }
    209291
    210         } else if (sn) { /* For finding groups, permits, denies, and ignores */
    211                 for (cur=list; cur; cur=cur->next)
    212                         if ((cur->type == type) && (cur->name) && !(aim_sncmp(cur->name, sn)))
     292        } else if (gn) { /* For finding groups */
     293                for (cur=list; cur; cur=cur->next) {
     294                        if ((cur->type == type) && (cur->bid == 0x0000) && (cur->name) && !(aim_sncmp(cur->name, gn))) {
    213295                                return cur;
     296                        }
     297                }
     298
     299        } else if (sn) { /* For finding permits, denies, and ignores */
     300                for (cur=list; cur; cur=cur->next) {
     301                        if ((cur->type == type) && (cur->name) && !(aim_sncmp(cur->name, sn))) {
     302                                return cur;
     303                        }
     304                }
    214305
    215306        /* For stuff without names--permit deny setting, visibility mask, etc. */
    216307        } else for (cur=list; cur; cur=cur->next) {
    217                 if (cur->type == type)
     308                if ((cur->type == type) && (!cur->name))
    218309                        return cur;
    219310        }
    220311
     312        return NULL;
     313}
     314
     315/**
     316 * Check if the given buddy exists in any group in the buddy list.
     317 *
     318 * @param list A pointer to the current list of items.
     319 * @param sn The group name of the desired item.
     320 * @return Return a pointer to the name of the item if found, else return NULL;
     321 */
     322faim_export struct aim_ssi_item *aim_ssi_itemlist_exists(struct aim_ssi_item *list, const char *sn)
     323{
     324        struct aim_ssi_item *cur;
     325        if (!list || !sn)
     326                return NULL;
     327        for (cur=list; cur; cur=cur->next)
     328                if ((cur->type == AIM_SSI_TYPE_BUDDY) && (cur->name) && (!aim_sncmp(cur->name, sn)))
     329                        return cur;
    221330        return NULL;
    222331}
     
    227336 * @param list A pointer to the current list of items.
    228337 * @param bn The buddy name of the desired item.
    229  * @return Return a pointer to the item if found, else return NULL;
    230  */
    231 faim_export struct aim_ssi_item *aim_ssi_itemlist_findparent(struct aim_ssi_item *list, char *sn)
     338 * @return Return a pointer to the name of the item if found, else return NULL;
     339 */
     340faim_export char *aim_ssi_itemlist_findparentname(struct aim_ssi_item *list, const char *sn)
    232341{
    233342        struct aim_ssi_item *cur, *curg;
    234343        if (!list || !sn)
    235344                return NULL;
    236         if (!(cur = aim_ssi_itemlist_finditem(list, NULL, sn, AIM_SSI_TYPE_BUDDY)))
     345        if (!(cur = aim_ssi_itemlist_exists(list, sn)))
    237346                return NULL;
    238         for (curg=list; curg; curg=curg->next)
    239                 if ((curg->type == AIM_SSI_TYPE_GROUP) && (curg->gid == cur->gid))
    240                         return curg;
    241         return NULL;
     347        if (!(curg = aim_ssi_itemlist_find(list, cur->gid, 0x0000)))
     348                return NULL;
     349        return curg->name;
    242350}
    243351
     
    285393
    286394/**
    287  * Add the given packet to the holding queue.  We totally need to send SSI SNACs one at
    288  * a time, so we have a local queue where packets get put before they are sent, and
    289  * then we send stuff one at a time, nice and orderly-like.
     395 * Locally find the alias of the given buddy.
     396 *
     397 * @param list A pointer to the current list of items.
     398 * @param gn The group of the buddy.
     399 * @param sn The name of the buddy.
     400 * @return A pointer to a NULL terminated string that is the buddies
     401 *         alias, or NULL if the buddy has no alias.  You should free
     402 *         this returned value!
     403 */
     404faim_export char *aim_ssi_getalias(struct aim_ssi_item *list, const char *gn, const char *sn)
     405{
     406        struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, sn, AIM_SSI_TYPE_BUDDY);
     407        if (cur) {
     408                aim_tlvlist_t *tlvlist = cur->data;
     409                if (tlvlist) {
     410                        aim_tlv_t *tlv = aim_gettlv(tlvlist, 0x0131, 1);
     411                        if (tlv && tlv->length) {
     412                                char *alias = (char *)malloc((tlv->length+1)*sizeof(char));
     413                                strncpy(alias, tlv->value, tlv->length);
     414                                alias[tlv->length] = 0;
     415                                return alias;
     416                        }
     417                }
     418        }
     419        return NULL;
     420}
     421
     422/**
     423 * Locally find if you are waiting for authorization for a buddy.
     424 *
     425 * @param list A pointer to the current list of items.
     426 * @param gn The group of the buddy.
     427 * @param sn The name of the buddy.
     428 * @return A pointer to a NULL terminated string that is the buddies
     429 *         alias, or NULL if the buddy has no alias.  You should free
     430 *         this returned value!
     431 */
     432faim_export int aim_ssi_waitingforauth(struct aim_ssi_item *list, const char *gn, const char *sn)
     433{
     434        struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, sn, AIM_SSI_TYPE_BUDDY);
     435        if (cur) {
     436                aim_tlvlist_t *tlvlist = cur->data;
     437                if (tlvlist)
     438                        if (aim_gettlv(tlvlist, 0x0066, 1))
     439                                return 1;
     440        }
     441        return 0;
     442}
     443
     444/**
     445 * If there are changes, then create temporary items and
     446 * call addmoddel.
    290447 *
    291448 * @param sess The oscar session.
    292  * @param conn The bos connection for this session.
    293  * @param fr The newly created SNAC that you want to send.
    294449 * @return Return 0 if no errors, otherwise return the error number.
    295450 */
    296 static int aim_ssi_enqueue(aim_session_t *sess, aim_conn_t *conn, aim_frame_t *fr)
    297 {
    298         aim_frame_t *cur;
    299 
    300         if (!sess || !conn || !fr)
    301                 return -EINVAL;
    302 
    303         fr->next = NULL;
    304         if (sess->ssi.holding_queue == NULL) {
    305                 sess->ssi.holding_queue = fr;
    306                 if (!sess->ssi.waiting_for_ack)
    307                         aim_ssi_modbegin(sess, conn);
    308         } else {
    309                 for (cur = sess->ssi.holding_queue; cur->next; cur = cur->next) ;
    310                 cur->next = fr;
    311         }
    312 
    313         return 0;
    314 }
    315 
    316 /**
    317  * Send the next SNAC from the holding queue.  This is called
    318  * automatically when an ack from an add, mod, or del is received. 
    319  * If the queue is empty, it sends the modend SNAC.
     451static int aim_ssi_sync(aim_session_t *sess)
     452{
     453        struct aim_ssi_item *cur1, *cur2;
     454        struct aim_ssi_tmp *cur, *new;
     455
     456        if (!sess)
     457                return -EINVAL;
     458
     459        /* If we're waiting for an ack, we shouldn't do anything else */
     460        if (sess->ssi.waiting_for_ack)
     461                return 0;
     462
     463        /*
     464         * Compare the 2 lists and create an aim_ssi_tmp for each difference. 
     465         * We should only send either additions, modifications, or deletions
     466         * before waiting for an acknowledgement.  So first do deletions, then
     467         * additions, then modifications.  Also, both the official and the local
     468         * list should be in ascending numerical order for the group ID#s and the
     469         * buddy ID#s, which makes things more efficient.  I think.
     470         */
     471
     472        /* Additions */
     473        if (!sess->ssi.pending) {
     474                for (cur1=sess->ssi.local; cur1; cur1=cur1->next) {
     475                        if (!aim_ssi_itemlist_find(sess->ssi.official, cur1->gid, cur1->bid)) {
     476                                new = (struct aim_ssi_tmp *)malloc(sizeof(struct aim_ssi_tmp));
     477                                new->action = AIM_CB_SSI_ADD;
     478                                new->ack = 0xffff;
     479                                new->name = NULL;
     480                                new->item = cur1;
     481                                new->next = NULL;
     482                                if (sess->ssi.pending) {
     483                                        for (cur=sess->ssi.pending; cur->next; cur=cur->next);
     484                                        cur->next = new;
     485                                } else
     486                                        sess->ssi.pending = new;
     487                        }
     488                }
     489        }
     490
     491        /* Deletions */
     492        if (!sess->ssi.pending) {
     493                for (cur1=sess->ssi.official; cur1; cur1=cur1->next) {
     494                        if (!aim_ssi_itemlist_find(sess->ssi.local, cur1->gid, cur1->bid)) {
     495                                new = (struct aim_ssi_tmp *)malloc(sizeof(struct aim_ssi_tmp));
     496                                new->action = AIM_CB_SSI_DEL;
     497                                new->ack = 0xffff;
     498                                new->name = NULL;
     499                                new->item = cur1;
     500                                new->next = NULL;
     501                                if (sess->ssi.pending) {
     502                                        for (cur=sess->ssi.pending; cur->next; cur=cur->next);
     503                                        cur->next = new;
     504                                } else
     505                                        sess->ssi.pending = new;
     506                        }
     507                }
     508        }
     509
     510        /* Modifications */
     511        if (!sess->ssi.pending) {
     512                for (cur1=sess->ssi.local; cur1; cur1=cur1->next) {
     513                        cur2 = aim_ssi_itemlist_find(sess->ssi.official, cur1->gid, cur1->bid);
     514                        if (cur2 && (aim_ssi_itemlist_cmp(cur1, cur2))) {
     515                                new = (struct aim_ssi_tmp *)malloc(sizeof(struct aim_ssi_tmp));
     516                                new->action = AIM_CB_SSI_MOD;
     517                                new->ack = 0xffff;
     518                                new->name = NULL;
     519                                new->item = cur1;
     520                                new->next = NULL;
     521                                if (sess->ssi.pending) {
     522                                        for (cur=sess->ssi.pending; cur->next; cur=cur->next);
     523                                        cur->next = new;
     524                                } else
     525                                        sess->ssi.pending = new;
     526                        }
     527                }
     528        }
     529
     530        /* We're out of stuff to do, so tell the AIM servers we're done and exit */
     531        if (!sess->ssi.pending) {
     532                aim_ssi_modend(sess);
     533                return 0;
     534        }
     535
     536        /* Make sure we don't send anything else between now
     537         * and when we receive the ack for the following operation */
     538        sess->ssi.waiting_for_ack = 1;
     539
     540        /* Now go mail off our data and wait 4 to 6 weeks */
     541        aim_ssi_addmoddel(sess);
     542
     543        return 0;
     544}
     545
     546/**
     547 * Free all SSI data.
     548 *
     549 * This doesn't remove it from the server, that's different.
    320550 *
    321551 * @param sess The oscar session.
    322  * @param conn The bos connection for this session.
    323552 * @return Return 0 if no errors, otherwise return the error number.
    324553 */
    325 static int aim_ssi_dispatch(aim_session_t *sess, aim_conn_t *conn)
    326 {
    327         aim_frame_t *cur;
    328 
    329         if (!sess || !conn)
    330                 return -EINVAL;
    331 
    332         if (!sess->ssi.waiting_for_ack) {
    333                 if (sess->ssi.holding_queue) {
    334                         sess->ssi.waiting_for_ack = 1;
    335                         cur = sess->ssi.holding_queue->next;
    336                         sess->ssi.holding_queue->next = NULL;
    337                         aim_tx_enqueue(sess, sess->ssi.holding_queue);
    338                         sess->ssi.holding_queue = cur;
    339                 } else
    340                         aim_ssi_modend(sess, conn);
    341         }
    342 
    343         return 0;
    344 }
    345 
    346 /**
    347  * Send SNACs necessary to remove all SSI data from the server list,
    348  * and then free the local copy as well.
     554static int aim_ssi_freelist(aim_session_t *sess)
     555{
     556        struct aim_ssi_item *cur, *del;
     557        struct aim_ssi_tmp *curtmp, *deltmp;
     558
     559        cur = sess->ssi.official;
     560        while (cur) {
     561                del = cur;
     562                cur = cur->next;
     563                free(del->name);
     564                aim_freetlvchain(&del->data);
     565                free(del);
     566        }
     567
     568        cur = sess->ssi.local;
     569        while (cur) {
     570                del = cur;
     571                cur = cur->next;
     572                free(del->name);
     573                aim_freetlvchain(&del->data);
     574                free(del);
     575        }
     576
     577        curtmp = sess->ssi.pending;
     578        while (curtmp) {
     579                deltmp = curtmp;
     580                curtmp = curtmp->next;
     581                free(deltmp);
     582        }
     583
     584        sess->ssi.numitems = 0;
     585        sess->ssi.official = NULL;
     586        sess->ssi.local = NULL;
     587        sess->ssi.pending = NULL;
     588        sess->ssi.timestamp = (time_t)0;
     589
     590        return 0;
     591}
     592
     593/**
     594 * Delete all SSI data.
    349595 *
    350596 * @param sess The oscar session.
    351  * @param conn The bos connection for this session.
    352597 * @return Return 0 if no errors, otherwise return the error number.
    353598 */
    354 faim_export int aim_ssi_deletelist(aim_session_t *sess, aim_conn_t *conn)
    355 {
    356         int num;
    357         struct aim_ssi_item *cur, **items;
    358 
    359         for (cur=sess->ssi.items, num=0; cur; cur=cur->next)
    360                 num++;
    361 
    362         if (!(items = (struct aim_ssi_item **)malloc(num*sizeof(struct aim_ssi_item *))))
    363                 return -ENOMEM;
    364         memset(items, 0, num*sizeof(struct aim_ssi_item *));
    365         for (cur=sess->ssi.items, num=0; cur; cur=cur->next) {
    366                 items[num] = cur;
    367                 num++;
    368         }
    369 
    370         aim_ssi_addmoddel(sess, conn, items, num, AIM_CB_SSI_DEL);
    371         free(items);
    372         aim_ssi_dispatch(sess, conn);
    373         aim_ssi_freelist(sess);
    374 
    375         return 0;
    376 }
    377 
    378 /**
    379  * This "cleans" the ssi list.  It does a few things, with the intent of making
    380  * sure there ain't nothin' wrong with your SSI.
    381  *   -Make sure all buddies are in a group, and all groups have the correct
    382  *     additional data.
    383  *   -Make sure there are no empty groups in the list.  While there is nothing
    384  *     wrong empty groups in the SSI, it's wiser to not have them.
     599faim_export int aim_ssi_deletelist(aim_session_t *sess)
     600{
     601        struct aim_ssi_item *cur, *del;
     602
     603        if (!sess)
     604                return -EINVAL;
     605
     606        /* Free the local list */
     607        cur = sess->ssi.local;
     608        while (cur) {
     609                del = cur;
     610                cur = cur->next;
     611                free(del->name);
     612                aim_freetlvchain(&del->data);
     613                free(del);
     614        }
     615        sess->ssi.local = NULL;
     616
     617        /* Sync our local list with the server list */
     618        aim_ssi_sync(sess);
     619
     620        return 0;
     621}
     622
     623/**
     624 * This "cleans" the ssi list.  It does the following:
     625 * 1) Makes sure all buddies, permits, and denies have names.
     626 * 2) Makes sure that all buddies are in a group that exist.
     627 * 3) Deletes any empty groups
    385628 *
    386629 * @param sess The oscar session.
    387  * @param conn The bos connection for this session.
    388630 * @return Return 0 if no errors, otherwise return the error number.
    389631 */
    390 faim_export int aim_ssi_cleanlist(aim_session_t *sess, aim_conn_t *conn)
    391 {
    392         unsigned int i;
    393         struct aim_ssi_item *cur, *parentgroup;
    394 
    395         /* Make sure we actually need to clean out the list */
    396         for (cur=sess->ssi.items, i=0; cur && !i; cur=cur->next)
    397                 /* Any buddies directly in the master group */
    398                 if ((cur->type == AIM_SSI_TYPE_BUDDY) && (cur->gid == 0x0000))
    399                         i++;
    400         if (!i)
    401                 return 0;
    402 
    403         /* Remove all the additional data from all groups */
    404         for (cur=sess->ssi.items; cur; cur=cur->next)
    405                 if ((cur->data) && (cur->type == AIM_SSI_TYPE_GROUP)) {
    406                         aim_freetlvchain((aim_tlvlist_t **)&cur->data);
    407                         cur->data = NULL;
     632faim_export int aim_ssi_cleanlist(aim_session_t *sess)
     633{
     634        struct aim_ssi_item *cur, *next;
     635
     636        if (!sess)
     637                return -EINVAL;
     638
     639        /* Delete any buddies, permits, or denies with empty names. */
     640        /* If there are any buddies directly in the master group, add them to a real group. */
     641        /* DESTROY any buddies that are directly in the master group. */
     642        /* Do the same for buddies that are in a non-existant group. */
     643        /* This will kind of mess up if you hit the item limit, but this function isn't too critical */
     644        cur = sess->ssi.local;
     645        while (cur) {
     646                next = cur->next;
     647                if (!cur->name) {
     648                        if (cur->type == AIM_SSI_TYPE_BUDDY)
     649                                aim_ssi_delbuddy(sess, NULL, NULL);
     650                        else if (cur->type == AIM_SSI_TYPE_PERMIT)
     651                                aim_ssi_delpermit(sess, NULL);
     652                        else if (cur->type == AIM_SSI_TYPE_DENY)
     653                                aim_ssi_deldeny(sess, NULL);
     654                } else if ((cur->type == AIM_SSI_TYPE_BUDDY) && ((cur->gid == 0x0000) || (!aim_ssi_itemlist_find(sess->ssi.local, cur->gid, 0x0000)))) {
     655                        aim_ssi_addbuddy(sess, cur->name, "orphans", NULL, NULL, NULL, 0);
     656                        aim_ssi_delbuddy(sess, cur->name, NULL);
    408657                }
    409 
    410         /* If there are buddies directly in the master group, make sure  */
    411         /* there is a group to put them in.  Any group, any group at all. */
    412         for (cur=sess->ssi.items; ((cur) && ((cur->type != AIM_SSI_TYPE_BUDDY) || (cur->gid != 0x0000))); cur=cur->next);
    413         if (!cur) {
    414                 for (parentgroup=sess->ssi.items; ((parentgroup) && (parentgroup->type!=AIM_SSI_TYPE_GROUP) && (parentgroup->gid==0x0000)); parentgroup=parentgroup->next);
    415                 if (!parentgroup) {
    416                         char *newgroup;
    417                         newgroup = (char*)malloc(strlen("Unknown")*sizeof(char));
    418                         strcpy(newgroup, "Unknown");
    419                         aim_ssi_addgroups(sess, conn, (const char**)&newgroup, 1);
     658                cur = next;
     659        }
     660
     661        /* Check if there are empty groups and delete them */
     662        cur = sess->ssi.local;
     663        while (cur) {
     664                next = cur->next;
     665                if (cur->type == AIM_SSI_TYPE_GROUP) {
     666                        aim_tlv_t *tlv = aim_gettlv(cur->data, 0x00c8, 1);
     667                        if (!tlv || !tlv->length)
     668                                aim_ssi_itemlist_del(&sess->ssi.local, cur);
    420669                }
    421         }
    422 
    423         /* Set parentgroup equal to any arbitray group */
    424         for (parentgroup=sess->ssi.items; parentgroup->gid==0x0000 || parentgroup->type!=AIM_SSI_TYPE_GROUP; parentgroup=parentgroup->next);
    425 
    426         /* If there are any buddies directly in the master group, put them in a real group */
    427         for (cur=sess->ssi.items; cur; cur=cur->next)
    428                 if ((cur->type == AIM_SSI_TYPE_BUDDY) && (cur->gid == 0x0000)) {
    429                         aim_ssi_addmoddel(sess, conn, &cur, 1, AIM_CB_SSI_DEL);
    430                         cur->gid = parentgroup->gid;
    431                         aim_ssi_addmoddel(sess, conn, &cur, 1, AIM_CB_SSI_ADD);
     670                cur = next;
     671        }
     672
     673        /* Check if the master group is empty */
     674        if ((cur = aim_ssi_itemlist_find(sess->ssi.local, 0x0000, 0x0000)) && (!cur->data))
     675                aim_ssi_itemlist_del(&sess->ssi.local, cur);
     676
     677        return 0;
     678}
     679
     680/**
     681 * Add a buddy to the list.
     682 *
     683 * @param sess The oscar session.
     684 * @param name The name of the item.
     685 * @param group The group of the item.
     686 * @param alias The alias/nickname of the item, or NULL.
     687 * @param comment The buddy comment for the item, or NULL.
     688 * @param smsnum The locally assigned SMS number, or NULL.
     689 * @return Return 0 if no errors, otherwise return the error number.
     690 */
     691faim_export int aim_ssi_addbuddy(aim_session_t *sess, const char *name, const char *group, const char *alias, const char *comment, const char *smsnum, int needauth)
     692{
     693        struct aim_ssi_item *parent;
     694        aim_tlvlist_t *data = NULL;
     695
     696        if (!sess || !name || !group)
     697                return -EINVAL;
     698
     699        /* Find the parent */
     700        if (!(parent = aim_ssi_itemlist_finditem(sess->ssi.local, group, NULL, AIM_SSI_TYPE_GROUP))) {
     701                /* Find the parent's parent (the master group) */
     702                if (!(parent = aim_ssi_itemlist_find(sess->ssi.local, 0x0000, 0x0000)))
     703                        if (!(parent = aim_ssi_itemlist_add(&sess->ssi.local, NULL, 0x0000, 0x0000, AIM_SSI_TYPE_GROUP, NULL)))
     704                                return -ENOMEM;
     705                /* Add the parent */
     706                if (!(parent = aim_ssi_itemlist_add(&sess->ssi.local, group, 0xFFFF, 0x0000, AIM_SSI_TYPE_GROUP, NULL)))
     707                        return -ENOMEM;
     708
     709                /* Modify the parent's parent (the master group) */
     710                aim_ssi_itemlist_rebuildgroup(sess->ssi.local, NULL);
     711        }
     712
     713        /* Create a TLV list for the new buddy */
     714        if (needauth)
     715                aim_addtlvtochain_noval(&data, 0x0066);
     716        if (alias)
     717                aim_addtlvtochain_raw(&data, 0x0131, strlen(alias), alias);
     718        if (smsnum)
     719                aim_addtlvtochain_raw(&data, 0x013a, strlen(smsnum), smsnum);
     720        if (comment)
     721                aim_addtlvtochain_raw(&data, 0x013c, strlen(comment), comment);
     722
     723        /* Add that bad boy */
     724        aim_ssi_itemlist_add(&sess->ssi.local, name, parent->gid, 0xFFFF, AIM_SSI_TYPE_BUDDY, data);
     725        aim_freetlvchain(&data);
     726
     727        /* Modify the parent group */
     728        aim_ssi_itemlist_rebuildgroup(sess->ssi.local, group);
     729
     730        /* Sync our local list with the server list */
     731        aim_ssi_sync(sess);
     732
     733        return 0;
     734}
     735
     736/**
     737 * Add a permit buddy to the list.
     738 *
     739 * @param sess The oscar session.
     740 * @param name The name of the item..
     741 * @return Return 0 if no errors, otherwise return the error number.
     742 */
     743faim_export int aim_ssi_addpermit(aim_session_t *sess, const char *name)
     744{
     745
     746        if (!sess || !name)
     747                return -EINVAL;
     748
     749        /* Add that bad boy */
     750        aim_ssi_itemlist_add(&sess->ssi.local, name, 0x0000, 0xFFFF, AIM_SSI_TYPE_PERMIT, NULL);
     751
     752        /* Sync our local list with the server list */
     753        aim_ssi_sync(sess);
     754
     755        return 0;
     756}
     757
     758/**
     759 * Add a deny buddy to the list.
     760 *
     761 * @param sess The oscar session.
     762 * @param name The name of the item..
     763 * @return Return 0 if no errors, otherwise return the error number.
     764 */
     765faim_export int aim_ssi_adddeny(aim_session_t *sess, const char *name)
     766{
     767
     768        if (!sess || !name)
     769                return -EINVAL;
     770
     771        /* Add that bad boy */
     772        aim_ssi_itemlist_add(&sess->ssi.local, name, 0x0000, 0xFFFF, AIM_SSI_TYPE_DENY, NULL);
     773
     774        /* Sync our local list with the server list */
     775        aim_ssi_sync(sess);
     776
     777        return 0;
     778}
     779
     780/**
     781 * Deletes a buddy from the list.
     782 *
     783 * @param sess The oscar session.
     784 * @param name The name of the item, or NULL.
     785 * @param group The group of the item, or NULL.
     786 * @return Return 0 if no errors, otherwise return the error number.
     787 */
     788faim_export int aim_ssi_delbuddy(aim_session_t *sess, const char *name, const char *group)
     789{
     790        struct aim_ssi_item *del;
     791
     792        if (!sess)
     793                return -EINVAL;
     794
     795        /* Find the buddy */
     796        if (!(del = aim_ssi_itemlist_finditem(sess->ssi.local, group, name, AIM_SSI_TYPE_BUDDY)))
     797                return -EINVAL;
     798
     799        /* Remove the item from the list */
     800        aim_ssi_itemlist_del(&sess->ssi.local, del);
     801
     802        /* Modify the parent group */
     803        aim_ssi_itemlist_rebuildgroup(sess->ssi.local, group);
     804
     805        /* Check if we should delete the parent group */
     806        if ((del = aim_ssi_itemlist_finditem(sess->ssi.local, group, NULL, AIM_SSI_TYPE_GROUP)) && (!del->data)) {
     807                aim_ssi_itemlist_del(&sess->ssi.local, del);
     808
     809                /* Modify the parent group */
     810                aim_ssi_itemlist_rebuildgroup(sess->ssi.local, NULL);
     811
     812                /* Check if we should delete the parent's parent (the master group) */
     813                if ((del = aim_ssi_itemlist_find(sess->ssi.local, 0x0000, 0x0000)) && (!del->data)) {
     814                        aim_ssi_itemlist_del(&sess->ssi.local, del);
    432815                }
    433 
    434         /* Rebuild additional data for all groups */
    435         for (parentgroup=sess->ssi.items; parentgroup; parentgroup=parentgroup->next)
    436                 if (parentgroup->type == AIM_SSI_TYPE_GROUP)
    437                         aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, parentgroup);
    438 
    439         /* Send a mod snac for all groups */
    440         i = 0;
    441         for (cur=sess->ssi.items; cur; cur=cur->next)
    442                 if (cur->type == AIM_SSI_TYPE_GROUP)
    443                         i++;
    444         if (i > 0) {
    445                 /* Allocate an array of pointers to each of the groups */
    446                 struct aim_ssi_item **groups;
    447                 if (!(groups = (struct aim_ssi_item **)malloc(i*sizeof(struct aim_ssi_item *))))
    448                         return -ENOMEM;
    449 
    450                 for (cur=sess->ssi.items, i=0; cur; cur=cur->next)
    451                         if (cur->type == AIM_SSI_TYPE_GROUP)
    452                                 groups[i] = cur;
    453 
    454                 aim_ssi_addmoddel(sess, conn, groups, i, AIM_CB_SSI_MOD);
    455                 free(groups);
    456         }
    457 
    458         /* Send a del snac for any empty groups */
    459         i = 0;
    460         for (cur=sess->ssi.items; cur; cur=cur->next)
    461                 if ((cur->type == AIM_SSI_TYPE_GROUP) && !(cur->data))
    462                         i++;
    463         if (i > 0) {
    464                 /* Allocate an array of pointers to each of the groups */
    465                 struct aim_ssi_item **groups;
    466                 if (!(groups = (struct aim_ssi_item **)malloc(i*sizeof(struct aim_ssi_item *))))
    467                         return -ENOMEM;
    468 
    469                 for (cur=sess->ssi.items, i=0; cur; cur=cur->next)
    470                         if ((cur->type == AIM_SSI_TYPE_GROUP) && !(cur->data))
    471                                 groups[i] = cur;
    472 
    473                 aim_ssi_addmoddel(sess, conn, groups, i, AIM_CB_SSI_DEL);
    474                 free(groups);
    475         }
    476 
    477         /* Begin sending SSI SNACs */
    478         aim_ssi_dispatch(sess, conn);
    479 
    480         return 0;
    481 }
    482 
    483 /**
    484  * Add an array of screen names to the given group.
     816        }
     817
     818        /* Sync our local list with the server list */
     819        aim_ssi_sync(sess);
     820
     821        return 0;
     822}
     823
     824/**
     825 * Deletes a permit buddy from the list.
    485826 *
    486827 * @param sess The oscar session.
    487  * @param conn The bos connection for this session.
    488  * @param gn The name of the group to which you want to add these names.
    489  * @param sn An array of null terminated strings of the names you want to add.
    490  * @param num The number of screen names you are adding (size of the sn array).
     828 * @param name The name of the item, or NULL.
    491829 * @return Return 0 if no errors, otherwise return the error number.
    492830 */
    493 faim_export int aim_ssi_addbuddies(aim_session_t *sess, aim_conn_t *conn, const char *gn, const char **sn, unsigned int num)
    494 {
    495         struct aim_ssi_item *parentgroup, **newitems;
    496         fu16_t i;
    497 
    498         if (!sess || !conn || !gn || !sn || !num)
    499                 return -EINVAL;
    500 
    501         /* Look up the parent group */
    502         if (!(parentgroup = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, gn, AIM_SSI_TYPE_GROUP))) {
    503                 aim_ssi_addgroups(sess, conn, (const char **)&gn, 1);
    504                 if (!(parentgroup = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, gn, AIM_SSI_TYPE_GROUP)))
    505                         return -ENOMEM;
    506         }
    507 
    508         /* Allocate an array of pointers to each of the new items */
    509         if (!(newitems = (struct aim_ssi_item **)malloc(num*sizeof(struct aim_ssi_item *))))
    510                 return -ENOMEM;
    511 
    512         /* Add items to the local list, and index them in the array */
    513         for (i=0; i<num; i++)
    514                 if (!(newitems[i] = aim_ssi_itemlist_add(&sess->ssi.items, parentgroup, sn[i], AIM_SSI_TYPE_BUDDY))) {
    515                         free(newitems);
    516                         return -ENOMEM;
    517                 }
    518 
    519         /* Send the add item SNAC */
    520         if ((i = aim_ssi_addmoddel(sess, conn, newitems, num, AIM_CB_SSI_ADD))) {
    521                 free(newitems);
    522                 return -i;
    523         }
    524 
    525         /* Free the array of pointers to each of the new items */
    526         free(newitems);
    527 
    528         /* Rebuild the additional data in the parent group */
    529         if ((i = aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, parentgroup)))
    530                 return i;
    531 
    532         /* Send the mod item SNAC */
    533         if ((i = aim_ssi_addmoddel(sess, conn, &parentgroup, 1, AIM_CB_SSI_MOD)))
    534                 return i;
    535 
    536         /* Begin sending SSI SNACs */
    537         if (!(i = aim_ssi_dispatch(sess, conn)))
    538                 return i;
    539 
    540         return 0;
    541 }
    542 
    543 /**
    544  * Add the master group (the group containing all groups).  This is called by
    545  * aim_ssi_addgroups, if necessary.
     831faim_export int aim_ssi_delpermit(aim_session_t *sess, const char *name)
     832{
     833        struct aim_ssi_item *del;
     834
     835        if (!sess)
     836                return -EINVAL;
     837
     838        /* Find the item */
     839        if (!(del = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, name, AIM_SSI_TYPE_PERMIT)))
     840                return -EINVAL;
     841
     842        /* Remove the item from the list */
     843        aim_ssi_itemlist_del(&sess->ssi.local, del);
     844
     845        /* Sync our local list with the server list */
     846        aim_ssi_sync(sess);
     847
     848        return 0;
     849}
     850
     851/**
     852 * Deletes a deny buddy from the list.
    546853 *
    547854 * @param sess The oscar session.
    548  * @param conn The bos connection for this session.
     855 * @param name The name of the item, or NULL.
    549856 * @return Return 0 if no errors, otherwise return the error number.
    550857 */
    551 faim_export int aim_ssi_addmastergroup(aim_session_t *sess, aim_conn_t *conn)
    552 {
    553         struct aim_ssi_item *newitem;
    554 
    555         if (!sess || !conn)
    556                 return -EINVAL;
    557 
    558         /* Add the item to the local list, and keep a pointer to it */
    559         if (!(newitem = aim_ssi_itemlist_add(&sess->ssi.items, NULL, NULL, AIM_SSI_TYPE_GROUP)))
    560                 return -ENOMEM;
    561 
    562         /* If there are any existing groups (technically there shouldn't be, but */
    563         /* just in case) then add their group ID#'s to the additional data */
    564         aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, newitem);
    565 
    566         /* Send the add item SNAC */
    567         aim_ssi_addmoddel(sess, conn, &newitem, 1, AIM_CB_SSI_ADD);
    568 
    569         /* Begin sending SSI SNACs */
    570         aim_ssi_dispatch(sess, conn);
    571 
    572         return 0;
    573 }
    574 
    575 /**
    576  * Add an array of groups to the list.
    577  *
    578  * @param sess The oscar session.
    579  * @param conn The bos connection for this session.
    580  * @param gn An array of null terminated strings of the names you want to add.
    581  * @param num The number of groups names you are adding (size of the sn array).
    582  * @return Return 0 if no errors, otherwise return the error number.
    583  */
    584 faim_export int aim_ssi_addgroups(aim_session_t *sess, aim_conn_t *conn, const char **gn, unsigned int num)
    585 {
    586         struct aim_ssi_item *parentgroup, **newitems;
    587         fu16_t i;
    588 
    589         if (!sess || !conn || !gn || !num)
    590                 return -EINVAL;
    591 
    592         /* Look up the parent group */
    593         if (!(parentgroup = aim_ssi_itemlist_find(sess->ssi.items, 0, 0))) {
    594                 aim_ssi_addmastergroup(sess, conn);
    595                 if (!(parentgroup = aim_ssi_itemlist_find(sess->ssi.items, 0, 0)))
    596                         return -ENOMEM;
    597         }
    598 
    599         /* Allocate an array of pointers to each of the new items */
    600         if (!(newitems = (struct aim_ssi_item **)malloc(num*sizeof(struct aim_ssi_item *))))
    601                 return -ENOMEM;
    602 
    603         /* Add items to the local list, and index them in the array */
    604         for (i=0; i<num; i++)
    605                 if (!(newitems[i] = aim_ssi_itemlist_add(&sess->ssi.items, parentgroup, gn[i], AIM_SSI_TYPE_GROUP))) {
    606                         free(newitems);
    607                         return -ENOMEM;
    608                 }
    609 
    610         /* Send the add item SNAC */
    611         if ((i = aim_ssi_addmoddel(sess, conn, newitems, num, AIM_CB_SSI_ADD))) {
    612                 free(newitems);
    613                 return -i;
    614         }
    615 
    616         /* Free the array of pointers to each of the new items */
    617         free(newitems);
    618 
    619         /* Rebuild the additional data in the parent group */
    620         if ((i = aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, parentgroup)))
    621                 return i;
    622 
    623         /* Send the mod item SNAC */
    624         if ((i = aim_ssi_addmoddel(sess, conn, &parentgroup, 1, AIM_CB_SSI_MOD)))
    625                 return i;
    626 
    627         /* Begin sending SSI SNACs */
    628         if (!(i = aim_ssi_dispatch(sess, conn)))
    629                 return i;
    630 
    631         return 0;
    632 }
    633 
    634 /**
    635  * Add an array of a certain type of item to the list.  This can be used for
    636  * permit buddies, deny buddies, ICQ's ignore buddies, and probably other
    637  * types, also.
    638  *
    639  * @param sess The oscar session.
    640  * @param conn The bos connection for this session.
    641  * @param sn An array of null terminated strings of the names you want to add.
    642  * @param num The number of groups names you are adding (size of the sn array).
    643  * @param type The type of item you want to add.  See the AIM_SSI_TYPE_BLEH
    644  *        #defines in aim.h.
    645  * @return Return 0 if no errors, otherwise return the error number.
    646  */
    647 faim_export int aim_ssi_addpord(aim_session_t *sess, aim_conn_t *conn, const char **sn, unsigned int num, fu16_t type)
    648 {
    649         struct aim_ssi_item **newitems;
    650         fu16_t i;
    651 
    652         if (!sess || !conn || !sn || !num)
    653                 return -EINVAL;
    654 
    655         /* Allocate an array of pointers to each of the new items */
    656         if (!(newitems = (struct aim_ssi_item **)malloc(num*sizeof(struct aim_ssi_item *))))
    657                 return -ENOMEM;
    658 
    659         /* Add items to the local list, and index them in the array */
    660         for (i=0; i<num; i++)
    661                 if (!(newitems[i] = aim_ssi_itemlist_add(&sess->ssi.items, NULL, sn[i], type))) {
    662                         free(newitems);
    663                         return -ENOMEM;
    664                 }
    665 
    666         /* Send the add item SNAC */
    667         if ((i = aim_ssi_addmoddel(sess, conn, newitems, num, AIM_CB_SSI_ADD))) {
    668                 free(newitems);
    669                 return -i;
    670         }
    671 
    672         /* Free the array of pointers to each of the new items */
    673         free(newitems);
    674 
    675         /* Begin sending SSI SNACs */
    676         if (!(i = aim_ssi_dispatch(sess, conn)))
    677                 return i;
     858faim_export int aim_ssi_deldeny(aim_session_t *sess, const char *name)
     859{
     860        struct aim_ssi_item *del;
     861
     862        if (!sess)
     863                return -EINVAL;
     864
     865        /* Find the item */
     866        if (!(del = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, name, AIM_SSI_TYPE_DENY)))
     867                return -EINVAL;
     868
     869        /* Remove the item from the list */
     870        aim_ssi_itemlist_del(&sess->ssi.local, del);
     871
     872        /* Sync our local list with the server list */
     873        aim_ssi_sync(sess);
    678874
    679875        return 0;
     
    685881 *
    686882 * @param sess The oscar session.
    687  * @param conn The bos connection for this session.
    688883 * @param oldgn The group that the buddy is currently in.
    689884 * @param newgn The group that the buddy should be moved in to.
     
    691886 * @return Return 0 if no errors, otherwise return the error number.
    692887 */
    693 faim_export int aim_ssi_movebuddy(aim_session_t *sess, aim_conn_t *conn, const char *oldgn, const char *newgn, const char *sn)
    694 {
    695         struct aim_ssi_item **groups, *buddy, *cur;
    696         fu16_t i;
    697 
    698         if (!sess || !conn || !oldgn || !newgn || !sn)
    699                 return -EINVAL;
    700 
    701         /* Look up the buddy */
    702         if (!(buddy = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, sn, AIM_SSI_TYPE_BUDDY)))
    703                 return -ENOMEM;
    704 
    705         /* Allocate an array of pointers to the two groups */
    706         if (!(groups = (struct aim_ssi_item **)malloc(2*sizeof(struct aim_ssi_item *))))
    707                 return -ENOMEM;
    708 
    709         /* Look up the old parent group */
    710         if (!(groups[0] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, oldgn, AIM_SSI_TYPE_GROUP))) {
    711                 free(groups);
    712                 return -ENOMEM;
    713         }
    714 
    715         /* Look up the new parent group */
    716         if (!(groups[1] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, newgn, AIM_SSI_TYPE_GROUP))) {
    717                 aim_ssi_addgroups(sess, conn, (const char**)&newgn, 1);
    718                 if (!(groups[1] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, newgn, AIM_SSI_TYPE_GROUP))) {
    719                         free(groups);
    720                         return -ENOMEM;
    721                 }
    722         }
    723 
    724         /* Send the delete item SNAC */
    725         aim_ssi_addmoddel(sess, conn, &buddy, 1, AIM_CB_SSI_DEL);
    726 
    727         /* Put the buddy in the new group */
    728         buddy->gid = groups[1]->gid;
    729 
    730         /* Assign a new buddy ID#, because the new group might already have a buddy with this ID# */
    731         buddy->bid = 0;
    732         do {
    733                 buddy->bid += 0x0001;
    734                 for (cur=sess->ssi.items, i=0; ((cur) && (!i)); cur=cur->next)
    735                         if ((cur->bid == buddy->bid) && (cur->gid == buddy->gid) && (cur->type == AIM_SSI_TYPE_BUDDY) && (cur->name) && aim_sncmp(cur->name, buddy->name))
    736                                 i=1;
    737         } while (i);
    738 
    739         /* Rebuild the additional data in the two parent groups */
    740         aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, groups[0]);
    741         aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, groups[1]);
    742 
    743         /* Send the add item SNAC */
    744         aim_ssi_addmoddel(sess, conn, &buddy, 1, AIM_CB_SSI_ADD);
    745 
    746         /* Send the mod item SNAC */
    747         aim_ssi_addmoddel(sess, conn, groups, 2, AIM_CB_SSI_MOD);
    748 
    749         /* Free the temporary array */
    750         free(groups);
    751 
    752         /* Begin sending SSI SNACs */
    753         aim_ssi_dispatch(sess, conn);
    754 
    755         return 0;
    756 }
    757 
    758 /**
    759  * Rename a group.  I really like how this is done.  It turns me on.
    760  *
    761  * Did I say that out loud?...
     888faim_export int aim_ssi_movebuddy(aim_session_t *sess, const char *oldgn, const char *newgn, const char *sn)
     889{
     890        aim_ssi_addbuddy(sess, sn, newgn, aim_ssi_getalias(sess->ssi.local, oldgn, sn), NULL, NULL, aim_ssi_waitingforauth(sess->ssi.local, oldgn, sn));
     891        aim_ssi_delbuddy(sess, sn, oldgn);
     892        return 0;
     893}
     894
     895/**
     896 * Change the alias stored on the server for a given buddy.
    762897 *
    763898 * @param sess The oscar session.
    764  * @param conn The bos connection for this session.
     899 * @param gn The group that the buddy is currently in.
     900 * @param sn The screen name of the buddy.
     901 * @param alias The new alias for the buddy.
     902 * @return Return 0 if no errors, otherwise return the error number.
     903 */
     904faim_export int aim_ssi_aliasbuddy(aim_session_t *sess, const char *gn, const char *sn, const char *alias)
     905{
     906        struct aim_ssi_item *tmp;
     907        aim_tlvlist_t *data = NULL;
     908
     909        if (!sess || !gn || !sn)
     910                return -EINVAL;
     911
     912        if (!(tmp = aim_ssi_itemlist_finditem(sess->ssi.local, gn, sn, AIM_SSI_TYPE_BUDDY)))
     913                return -EINVAL;
     914
     915        if (alias && !strlen(alias))
     916                alias = NULL;
     917
     918        /* Need to add the x0131 TLV to the TLV chain */
     919        if (alias)
     920                aim_addtlvtochain_raw(&data, 0x0131, strlen(alias), alias);
     921
     922        aim_freetlvchain(&tmp->data);
     923        tmp->data = data;
     924
     925        /* Sync our local list with the server list */
     926        aim_ssi_sync(sess);
     927
     928        return 0;
     929}
     930
     931/**
     932 * Rename a group.
     933 *
     934 * @param sess The oscar session.
    765935 * @param oldgn The old group name.
    766936 * @param newgn The new group name.
    767937 * @return Return 0 if no errors, otherwise return the error number.
    768938 */
    769 faim_export int aim_ssi_rename_group(aim_session_t *sess, aim_conn_t *conn, const char *oldgn, const char *newgn)
     939faim_export int aim_ssi_rename_group(aim_session_t *sess, const char *oldgn, const char *newgn)
    770940{
    771941        struct aim_ssi_item *group;
    772942
    773         if (!sess || !conn || !oldgn || !newgn)
    774                 return -EINVAL;
    775 
    776         /* Look up the group */
    777         if (!(group = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, oldgn, AIM_SSI_TYPE_GROUP)))
    778                 return -ENOMEM;
    779 
    780         /* Free the old group name and copy the new one in its place. */
    781         if (group->name)
    782                 free(group->name);
    783         if (!(group->name = (char *)malloc((strlen(newgn)+1)*sizeof(char)))) {
    784                 group->name = NULL;
    785                 return -ENOMEM;
    786         }
     943        if (!sess || !oldgn || !newgn)
     944                return -EINVAL;
     945
     946        if (!(group = aim_ssi_itemlist_finditem(sess->ssi.local, oldgn, NULL, AIM_SSI_TYPE_GROUP)))
     947                return -EINVAL;
     948
     949        free(group->name);
     950        group->name = (char *)malloc((strlen(newgn)+1)*sizeof(char));
    787951        strcpy(group->name, newgn);
    788952
    789         /* Send the mod item SNAC */
    790         aim_ssi_addmoddel(sess, conn, &group, 1, AIM_CB_SSI_MOD);
    791 
    792         /* Begin sending SSI SNACs */
    793         aim_ssi_dispatch(sess, conn);
    794 
    795         return 0;
    796 }
    797 
    798 /**
    799  * Delete an array of screen names from the given group.
     953        /* Sync our local list with the server list */
     954        aim_ssi_sync(sess);
     955
     956        return 0;
     957}
     958
     959/**
     960 * Stores your permit/deny setting on the server, and starts using it.
    800961 *
    801962 * @param sess The oscar session.
    802  * @param conn The bos connection for this session.
    803  * @param gn The name of the group from which you want to delete these names.
    804  * @param sn An array of null terminated strings of the names you want to delete.
    805  * @param num The number of screen names you are deleting (size of the sn array).
    806  * @return Return 0 if no errors, otherwise return the error number.
    807  */
    808 faim_export int aim_ssi_delbuddies(aim_session_t *sess, aim_conn_t *conn, const char *gn, char **sn, unsigned int num)
    809 {
    810         struct aim_ssi_item *cur, *parentgroup, **delitems;
    811         int i;
    812 
    813         if (!sess || !conn || !gn || !sn || !num)
    814                 return -EINVAL;
    815 
    816         /* Look up the parent group */
    817         if (!(parentgroup = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, gn, AIM_SSI_TYPE_GROUP)))
    818                 return -EINVAL;
    819 
    820         /* Allocate an array of pointers to each of the items to be deleted */
    821         delitems = (struct aim_ssi_item **)malloc(num*sizeof(struct aim_ssi_item *));
    822         memset(delitems, 0, num*sizeof(struct aim_ssi_item *));
    823 
    824         /* Make the delitems array a pointer to the aim_ssi_item structs to be deleted */
    825         for (i=0; i<num; i++) {
    826                 if (!(delitems[i] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, sn[i], AIM_SSI_TYPE_BUDDY))) {
    827                         free(delitems);
    828                         return -EINVAL;
    829                 }
    830 
    831                 /* Remove the delitems from the item list */
    832                 if (sess->ssi.items == delitems[i]) {
    833                         sess->ssi.items = sess->ssi.items->next;
    834                 } else {
    835                         for (cur=sess->ssi.items; (cur->next && (cur->next!=delitems[i])); cur=cur->next);
    836                         if (cur->next)
    837                                 cur->next = cur->next->next;
    838                 }
    839         }
    840 
    841         /* Send the del item SNAC */
    842         aim_ssi_addmoddel(sess, conn, delitems, num, AIM_CB_SSI_DEL);
    843 
    844         /* Free the items */
    845         for (i=0; i<num; i++) {
    846                 if (delitems[i]->name)
    847                         free(delitems[i]->name);
    848                 if (delitems[i]->data)
    849                         aim_freetlvchain((aim_tlvlist_t **)&delitems[i]->data);
    850                 free(delitems[i]);
    851         }
    852         free(delitems);
    853 
    854         /* Rebuild the additional data in the parent group */
    855         aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, parentgroup);
    856 
    857         /* Send the mod item SNAC */
    858         aim_ssi_addmoddel(sess, conn, &parentgroup, 1, AIM_CB_SSI_MOD);
    859 
    860         /* Delete the group, but only if it's empty */
    861         if (!parentgroup->data)
    862                 aim_ssi_delgroups(sess, conn, &parentgroup->name, 1);
    863 
    864         /* Begin sending SSI SNACs */
    865         aim_ssi_dispatch(sess, conn);
    866 
    867         return 0;
    868 }
    869 
    870 /**
    871  * Delete the master group from the item list.  There can be only one.
    872  * Er, so just find the one master group and delete it.
    873  *
    874  * @param sess The oscar session.
    875  * @param conn The bos connection for this session.
    876  * @return Return 0 if no errors, otherwise return the error number.
    877  */
    878 faim_export int aim_ssi_delmastergroup(aim_session_t *sess, aim_conn_t *conn)
    879 {
    880         struct aim_ssi_item *cur, *delitem;
    881 
    882         if (!sess || !conn)
    883                 return -EINVAL;
    884 
    885         /* Make delitem a pointer to the aim_ssi_item to be deleted */
    886         if (!(delitem = aim_ssi_itemlist_find(sess->ssi.items, 0, 0)))
    887                 return -EINVAL;
    888 
    889         /* Remove delitem from the item list */
    890         if (sess->ssi.items == delitem) {
    891                 sess->ssi.items = sess->ssi.items->next;
    892         } else {
    893                 for (cur=sess->ssi.items; (cur->next && (cur->next!=delitem)); cur=cur->next);
    894                 if (cur->next)
    895                         cur->next = cur->next->next;
    896         }
    897 
    898         /* Send the del item SNAC */
    899         aim_ssi_addmoddel(sess, conn, &delitem, 1, AIM_CB_SSI_DEL);
    900 
    901         /* Free the item */
    902         if (delitem->name)
    903                 free(delitem->name);
    904         if (delitem->data)
    905                 aim_freetlvchain((aim_tlvlist_t **)&delitem->data);
    906         free(delitem);
    907 
    908         /* Begin sending SSI SNACs */
    909         aim_ssi_dispatch(sess, conn);
    910 
    911         return 0;
    912 }
    913 
    914 /**
    915  * Delete an array of groups.
    916  *
    917  * @param sess The oscar session.
    918  * @param conn The bos connection for this session.
    919  * @param gn An array of null terminated strings of the groups you want to delete.
    920  * @param num The number of groups you are deleting (size of the gn array).
    921  * @return Return 0 if no errors, otherwise return the error number.
    922  */
    923 faim_export int aim_ssi_delgroups(aim_session_t *sess, aim_conn_t *conn, char **gn, unsigned int num) {
    924         struct aim_ssi_item *cur, *parentgroup, **delitems;
    925         int i;
    926 
    927         if (!sess || !conn || !gn || !num)
    928                 return -EINVAL;
    929 
    930         /* Look up the parent group */
    931         if (!(parentgroup = aim_ssi_itemlist_find(sess->ssi.items, 0, 0)))
    932                 return -EINVAL;
    933 
    934         /* Allocate an array of pointers to each of the items to be deleted */
    935         delitems = (struct aim_ssi_item **)malloc(num*sizeof(struct aim_ssi_item *));
    936         memset(delitems, 0, num*sizeof(struct aim_ssi_item *));
    937 
    938         /* Make the delitems array a pointer to the aim_ssi_item structs to be deleted */
    939         for (i=0; i<num; i++) {
    940                 if (!(delitems[i] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, gn[i], AIM_SSI_TYPE_GROUP))) {
    941                         free(delitems);
    942                         return -EINVAL;
    943                 }
    944 
    945                 /* Remove the delitems from the item list */
    946                 if (sess->ssi.items == delitems[i]) {
    947                         sess->ssi.items = sess->ssi.items->next;
    948                 } else {
    949                         for (cur=sess->ssi.items; (cur->next && (cur->next!=delitems[i])); cur=cur->next);
    950                         if (cur->next)
    951                                 cur->next = cur->next->next;
    952                 }
    953         }
    954 
    955         /* Send the del item SNAC */
    956         aim_ssi_addmoddel(sess, conn, delitems, num, AIM_CB_SSI_DEL);
    957 
    958         /* Free the items */
    959         for (i=0; i<num; i++) {
    960                 if (delitems[i]->name)
    961                         free(delitems[i]->name);
    962                 if (delitems[i]->data)
    963                         aim_freetlvchain((aim_tlvlist_t **)&delitems[i]->data);
    964                 free(delitems[i]);
    965         }
    966         free(delitems);
    967 
    968         /* Rebuild the additional data in the parent group */
    969         aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, parentgroup);
    970 
    971         /* Send the mod item SNAC */
    972         aim_ssi_addmoddel(sess, conn, &parentgroup, 1, AIM_CB_SSI_MOD);
    973 
    974         /* Delete the group, but only if it's empty */
    975         if (!parentgroup->data)
    976                 aim_ssi_delmastergroup(sess, conn);
    977 
    978         /* Begin sending SSI SNACs */
    979         aim_ssi_dispatch(sess, conn);
    980 
    981         return 0;
    982 }
    983 
    984 /**
    985  * Delete an array of a certain type of item from the list.  This can be
    986  * used for permit buddies, deny buddies, ICQ's ignore buddies, and
    987  * probably other types, also.
    988  *
    989  * @param sess The oscar session.
    990  * @param conn The bos connection for this session.
    991  * @param sn An array of null terminated strings of the items you want to delete.
    992  * @param num The number of items you are deleting (size of the sn array).
    993  * @return Return 0 if no errors, otherwise return the error number.
    994  */
    995 faim_export int aim_ssi_delpord(aim_session_t *sess, aim_conn_t *conn, const char **sn, unsigned int num, fu16_t type) {
    996         struct aim_ssi_item *cur, **delitems;
    997         int i;
    998 
    999         if (!sess || !conn || !sn || !num || (type!=AIM_SSI_TYPE_PERMIT && type!=AIM_SSI_TYPE_DENY))
    1000                 return -EINVAL;
    1001 
    1002         /* Allocate an array of pointers to each of the items to be deleted */
    1003         delitems = (struct aim_ssi_item **)malloc(num*sizeof(struct aim_ssi_item *));
    1004         memset(delitems, 0, num*sizeof(struct aim_ssi_item *));
    1005 
    1006         /* Make the delitems array a pointer to the aim_ssi_item structs to be deleted */
    1007         for (i=0; i<num; i++) {
    1008                 if (!(delitems[i] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, sn[i], type))) {
    1009                         free(delitems);
    1010                         return -EINVAL;
    1011                 }
    1012 
    1013                 /* Remove the delitems from the item list */
    1014                 if (sess->ssi.items == delitems[i]) {
    1015                         sess->ssi.items = sess->ssi.items->next;
    1016                 } else {
    1017                         for (cur=sess->ssi.items; (cur->next && (cur->next!=delitems[i])); cur=cur->next);
    1018                         if (cur->next)
    1019                                 cur->next = cur->next->next;
    1020                 }
    1021         }
    1022 
    1023         /* Send the del item SNAC */
    1024         aim_ssi_addmoddel(sess, conn, delitems, num, AIM_CB_SSI_DEL);
    1025 
    1026         /* Free the items */
    1027         for (i=0; i<num; i++) {
    1028                 if (delitems[i]->name)
    1029                         free(delitems[i]->name);
    1030                 if (delitems[i]->data)
    1031                         aim_freetlvchain((aim_tlvlist_t **)&delitems[i]->data);
    1032                 free(delitems[i]);
    1033         }
    1034         free(delitems);
    1035 
    1036         /* Begin sending SSI SNACs */
    1037         aim_ssi_dispatch(sess, conn);
    1038 
    1039         return 0;
    1040 }
    1041 
    1042 /**
    1043  * Stores your permit/deny setting on the server, and starts using it.
    1044  *
    1045  * @param sess The oscar session.
    1046  * @param conn The bos connection for this session.
    1047963 * @param permdeny Your permit/deny setting.  Can be one of the following:
    1048964 *        1 - Allow all users
     
    1055971 * @return Return 0 if no errors, otherwise return the error number.
    1056972 */
    1057 faim_export int aim_ssi_setpermdeny(aim_session_t *sess, aim_conn_t *conn, fu8_t permdeny, fu32_t vismask) {
    1058         struct aim_ssi_item *cur;
    1059         aim_tlv_t *tlv;
    1060 
    1061         if (!sess || !conn)
    1062                 return -EINVAL;
    1063 
    1064         /* Look up the permit/deny settings item */
    1065         cur = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, NULL, AIM_SSI_TYPE_PDINFO);
    1066 
    1067         if (cur) {
    1068                 /* The permit/deny item exists */
    1069                 if (cur->data && (tlv = aim_gettlv(cur->data, 0x00ca, 1))) {
    1070                         /* Just change the value of the x00ca TLV */
    1071                         if (tlv->length != 1) {
    1072                                 tlv->length = 1;
    1073                                 free(tlv->value);
    1074                                 tlv->value = (fu8_t *)malloc(sizeof(fu8_t));
    1075                         }
    1076                         tlv->value[0] = permdeny;
    1077                 } else {
    1078                         /* Need to add the x00ca TLV to the TLV chain */
    1079                         aim_addtlvtochain8((aim_tlvlist_t**)&cur->data, 0x00ca, permdeny);
     973faim_export int aim_ssi_setpermdeny(aim_session_t *sess, fu8_t permdeny, fu32_t vismask)
     974{
     975        struct aim_ssi_item *tmp;
     976        aim_tlvlist_t *data = NULL;
     977
     978        if (!sess)
     979                return -EINVAL;
     980
     981        /* Need to add the x00ca TLV to the TLV chain */
     982        aim_addtlvtochain8(&data, 0x00ca, permdeny);
     983
     984        /* Need to add the x00cb TLV to the TLV chain */
     985        aim_addtlvtochain32(&data, 0x00cb, vismask);
     986
     987        if ((tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, NULL, AIM_SSI_TYPE_PDINFO))) {
     988                aim_freetlvchain(&tmp->data);
     989                tmp->data = data;
     990        } else {
     991                tmp = aim_ssi_itemlist_add(&sess->ssi.local, NULL, 0x0000, 0xFFFF, AIM_SSI_TYPE_PDINFO, data);
     992                aim_freetlvchain(&data);
     993        }
     994
     995        /* Sync our local list with the server list */
     996        aim_ssi_sync(sess);
     997
     998        return 0;
     999}
     1000
     1001/**
     1002 * Set buddy icon information
     1003 *
     1004 * @param sess The oscar session.
     1005 * @param iconcsum The MD5 checksum of the icon you are using.
     1006 * @param iconcsumlen Length of the MD5 checksum given above.  Should be 0x10 bytes.
     1007 * @return Return 0 if no errors, otherwise return the error number.
     1008 */
     1009faim_export int aim_ssi_seticon(aim_session_t *sess, fu8_t *iconsum, fu16_t iconsumlen)
     1010{
     1011        struct aim_ssi_item *tmp;
     1012        aim_tlvlist_t *data = NULL;
     1013        fu8_t *csumdata;
     1014
     1015        if (!sess || !iconsum || !iconsumlen)
     1016                return -EINVAL;
     1017
     1018        if (!(csumdata = (fu8_t *)malloc((iconsumlen+2)*sizeof(fu8_t))))
     1019                return -ENOMEM;
     1020        csumdata[0] = 0x00;
     1021        csumdata[1] = 0x10;
     1022        memcpy(&csumdata[2], iconsum, iconsumlen);
     1023
     1024        /* Need to add the x00d5 TLV to the TLV chain */
     1025        aim_addtlvtochain_raw(&data, 0x00d5, (iconsumlen+2) * sizeof(fu8_t), csumdata);
     1026
     1027        /* This TLV is added to cache the icon. */
     1028        aim_addtlvtochain_noval(&data, 0x0131);
     1029
     1030        if ((tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, "1", AIM_SSI_TYPE_ICONINFO))) {
     1031                /* If the new tlvchain and oldtlvchain are the same, then do nothing */
     1032                if (!aim_tlvlist_cmp(tmp->data, data)) {
     1033                        /* The new tlvlist is the identical to the old one */
     1034                        aim_freetlvchain(&data);
     1035                        free(csumdata);
     1036                        return 0;
    10801037                }
    1081 
    1082                 if (cur->data && (tlv = aim_gettlv(cur->data, 0x00cb, 1))) {
    1083                         /* Just change the value of the x00cb TLV */
    1084                         if (tlv->length != 4) {
    1085                                 tlv->length = 4;
    1086                                 free(tlv->value);
    1087                                 tlv->value = (fu8_t *)malloc(4*sizeof(fu8_t));
    1088                         }
    1089                         aimutil_put32(tlv->value, vismask);
    1090                 } else {
    1091                         /* Need to add the x00cb TLV to the TLV chain */
    1092                         aim_addtlvtochain32((aim_tlvlist_t**)&cur->data, 0x00cb, vismask);
    1093                 }
    1094 
    1095                 /* Send the mod item SNAC */
    1096                 aim_ssi_addmoddel(sess, conn, &cur, 1, AIM_CB_SSI_MOD);
     1038                aim_freetlvchain(&tmp->data);
     1039                tmp->data = data;
    10971040        } else {
    1098                 /* Need to add the permit/deny item */
    1099                 if (!(cur = aim_ssi_itemlist_add(&sess->ssi.items, NULL, NULL, AIM_SSI_TYPE_PDINFO)))
    1100                         return -ENOMEM;
    1101                 aim_addtlvtochain8((aim_tlvlist_t**)&cur->data, 0x00ca, permdeny);
    1102                 aim_addtlvtochain32((aim_tlvlist_t**)&cur->data, 0x00cb, vismask);
    1103                 aim_ssi_addmoddel(sess, conn, &cur, 1, AIM_CB_SSI_ADD);
    1104         }
    1105 
    1106         /* Begin sending SSI SNACs */
    1107         aim_ssi_dispatch(sess, conn);
    1108 
     1041                tmp = aim_ssi_itemlist_add(&sess->ssi.local, "1", 0x0000, 0x51F4, AIM_SSI_TYPE_ICONINFO, data);
     1042                aim_freetlvchain(&data);
     1043        }
     1044
     1045        /* Sync our local list with the server list */
     1046        aim_ssi_sync(sess);
     1047        free(csumdata);
    11091048        return 0;
    11101049}
     
    11141053 *
    11151054 * @param sess The oscar session.
    1116  * @param conn The bos connection for this session.
    11171055 * @param presence I think it's a bitmask, but I only know what one of the bits is:
    11181056 *        0x00000400 - Allow others to see your idle time
    11191057 * @return Return 0 if no errors, otherwise return the error number.
    11201058 */
    1121 faim_export int aim_ssi_setpresence(aim_session_t *sess, aim_conn_t *conn, fu32_t presence) {
    1122         struct aim_ssi_item *cur;
     1059faim_export int aim_ssi_setpresence(aim_session_t *sess, fu32_t presence) {
     1060        struct aim_ssi_item *tmp;
     1061        aim_tlvlist_t *data = NULL;
     1062
     1063        if (!sess)
     1064                return -EINVAL;
     1065
     1066        /* Need to add the x00c9 TLV to the TLV chain */
     1067        aim_addtlvtochain32(&data, 0x00c9, presence);
     1068
     1069        if ((tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS))) {
     1070                aim_freetlvchain(&tmp->data);
     1071                tmp->data = data;
     1072        } else {
     1073                tmp = aim_ssi_itemlist_add(&sess->ssi.local, NULL, 0x0000, 0xFFFF, AIM_SSI_TYPE_PRESENCEPREFS, data);
     1074                aim_freetlvchain(&data);
     1075        }
     1076
     1077        /* Sync our local list with the server list */
     1078        aim_ssi_sync(sess);
     1079
     1080        return 0;
     1081}
     1082
     1083/*
     1084 * Subtype 0x0002 - Request SSI Rights.
     1085 */
     1086faim_export int aim_ssi_reqrights(aim_session_t *sess)
     1087{
     1088        aim_conn_t *conn;
     1089
     1090        if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)))
     1091                return -EINVAL;
     1092
     1093        return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_REQRIGHTS);
     1094}
     1095
     1096/*
     1097 * Subtype 0x0003 - SSI Rights Information.
     1098 */
     1099static int parserights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
     1100{
     1101        int ret = 0, i;
     1102        aim_rxcallback_t userfunc;
     1103        aim_tlvlist_t *tlvlist;
    11231104        aim_tlv_t *tlv;
    1124 
    1125         if (!sess || !conn)
    1126                 return -EINVAL;
    1127 
    1128         /* Look up the item */
    1129         cur = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS);
    1130 
    1131         if (cur) {
    1132                 /* The item exists */
    1133                 if (cur->data && (tlv = aim_gettlv(cur->data, 0x00c9, 1))) {
    1134                         /* Just change the value of the x00c9 TLV */
    1135                         if (tlv->length != 4) {
    1136                                 tlv->length = 4;
    1137                                 free(tlv->value);
    1138                                 tlv->value = (fu8_t *)malloc(4*sizeof(fu8_t));
    1139                         }
    1140                         aimutil_put32(tlv->value, presence);
    1141                 } else {
    1142                         /* Need to add the x00c9 TLV to the TLV chain */
    1143                         aim_addtlvtochain32((aim_tlvlist_t**)&cur->data, 0x00c9, presence);
    1144                 }
    1145 
    1146                 /* Send the mod item SNAC */
    1147                 aim_ssi_addmoddel(sess, conn, &cur, 1, AIM_CB_SSI_MOD);
    1148         } else {
    1149                 /* Need to add the item */
    1150                 if (!(cur = aim_ssi_itemlist_add(&sess->ssi.items, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS)))
    1151                         return -ENOMEM;
    1152                 aim_addtlvtochain32((aim_tlvlist_t**)&cur->data, 0x00c9, presence);
    1153                 aim_ssi_addmoddel(sess, conn, &cur, 1, AIM_CB_SSI_ADD);
    1154         }
    1155 
    1156         /* Begin sending SSI SNACs */
    1157         aim_ssi_dispatch(sess, conn);
    1158 
    1159         return 0;
    1160 }
    1161 
    1162 /*
    1163  * Request SSI Rights.
    1164  */
    1165 faim_export int aim_ssi_reqrights(aim_session_t *sess, aim_conn_t *conn)
    1166 {
    1167         return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_REQRIGHTS);
    1168 }
    1169 
    1170 /*
    1171  * SSI Rights Information.
    1172  */
    1173 static int parserights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
    1174 {
    1175         int ret = 0;
    1176         aim_rxcallback_t userfunc;
     1105        aim_bstream_t bstream;
     1106        fu16_t *maxitems;
     1107
     1108        /* This SNAC is made up of a bunch of TLVs */
     1109        tlvlist = aim_readtlvchain(bs);
     1110
     1111        /* TLV 0x0004 contains the maximum number of each item */
     1112        if (!(tlv = aim_gettlv(tlvlist, 0x0004, 1))) {
     1113                aim_freetlvchain(&tlvlist);
     1114                return 0;
     1115        }
     1116
     1117        aim_bstream_init(&bstream, tlv->value, tlv->length);
     1118
     1119        if (!(maxitems = (fu16_t *)malloc((tlv->length/2)*sizeof(fu16_t)))) {
     1120                aim_freetlvchain(&tlvlist);
     1121                return 0;
     1122        }
     1123
     1124        for (i=0; i<(tlv->length/2); i++)
     1125                maxitems[i] = aimbs_get16(&bstream);
    11771126
    11781127        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
    1179                 ret = userfunc(sess, rx);
     1128                ret = userfunc(sess, rx, tlv->length/2, maxitems);
     1129
     1130        aim_freetlvchain(&tlvlist);
     1131        free(maxitems);
    11801132
    11811133        return ret;
     
    11831135
    11841136/*
    1185  * Request SSI Data.
     1137 * Subtype 0x0004 - Request SSI Data when you don't have a timestamp and
     1138 * revision number.
     1139 *
     1140 */
     1141faim_export int aim_ssi_reqdata(aim_session_t *sess)
     1142{
     1143        aim_conn_t *conn;
     1144
     1145        if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)))
     1146                return -EINVAL;
     1147
     1148        /* Free any current data, just in case */
     1149        aim_ssi_freelist(sess);
     1150
     1151        return aim_genericreq_n_snacid(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_REQDATA);
     1152}
     1153
     1154/*
     1155 * Subtype 0x0005 - Request SSI Data when you have a timestamp and revision
     1156 * number.
    11861157 *
    11871158 * The data will only be sent if it is newer than the posted local
     
    11911162 *
    11921163 */
    1193 faim_export int aim_ssi_reqdata(aim_session_t *sess, aim_conn_t *conn, time_t localstamp, fu16_t localrev)
    1194 {
     1164faim_export int aim_ssi_reqifchanged(aim_session_t *sess, time_t timestamp, fu16_t numitems)
     1165{
     1166        aim_conn_t *conn;
    11951167        aim_frame_t *fr;
    11961168        aim_snacid_t snacid;
    11971169
    1198         if (!sess || !conn)
     1170        if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)))
    11991171                return -EINVAL;
    12001172
     
    12021174                return -ENOMEM;
    12031175
    1204         snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_REQLIST, 0x0000, NULL, 0);
    1205 
    1206         aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_REQLIST, 0x0000, snacid);
    1207         aimbs_put32(&fr->data, localstamp);
    1208         aimbs_put16(&fr->data, localrev);
     1176        snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_REQIFCHANGED, 0x0000, NULL, 0);
     1177
     1178        aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_REQIFCHANGED, 0x0000, snacid);
     1179        aimbs_put32(&fr->data, timestamp);
     1180        aimbs_put16(&fr->data, numitems);
    12091181
    12101182        aim_tx_enqueue(sess, fr);
    12111183
    1212         return 0;
    1213 }
    1214 
    1215 /*
    1216  * SSI Data.
     1184        /* Free any current data, just in case */
     1185        aim_ssi_freelist(sess);
     1186
     1187        return 0;
     1188}
     1189
     1190/*
     1191 * Subtype 0x0006 - SSI Data.
    12171192 */
    12181193static int parsedata(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
     
    12201195        int ret = 0;
    12211196        aim_rxcallback_t userfunc;
    1222         struct aim_ssi_item *cur = NULL;
    12231197        fu8_t fmtver; /* guess */
    1224         fu16_t revision;
    1225         fu32_t timestamp;
    1226 
    1227         /* When you set the version for the SSI family to 2-4, the beginning of this changes.
    1228          * Instead of the version and then the revision, there is "0x0006" and then a type
    1229          * 0x0001 TLV containing the 2 byte SSI family version that you sent earlier.  Also,
    1230          * the SNAC flags go from 0x0000 to 0x8000.  I guess the 0x0006 is the length of the
    1231          * TLV(s) that follow.  The rights SNAC does the same thing, with the differing flag
    1232          * and everything.
    1233          */
     1198        fu16_t namelen, gid, bid, type;
     1199        char *name;
     1200        aim_tlvlist_t *data;
    12341201
    12351202        fmtver = aimbs_get8(bs); /* Version of ssi data.  Should be 0x00 */
    1236         revision = aimbs_get16(bs); /* # of times ssi data has been modified */
    1237         if (revision != 0)
    1238                 sess->ssi.revision = revision;
    1239 
    1240         for (cur = sess->ssi.items; cur && cur->next; cur=cur->next) ;
    1241 
    1242         while (aim_bstream_empty(bs) > 4) { /* last four bytes are stamp */
    1243                 fu16_t namelen, tbslen;
    1244 
    1245                 if (!sess->ssi.items) {
    1246                         if (!(sess->ssi.items = malloc(sizeof(struct aim_ssi_item))))
    1247                                 return -ENOMEM;
    1248                         cur = sess->ssi.items;
    1249                 } else {
    1250                         if (!(cur->next = malloc(sizeof(struct aim_ssi_item))))
    1251                                 return -ENOMEM;
    1252                         cur = cur->next;
    1253                 }
    1254                 memset(cur, 0, sizeof(struct aim_ssi_item));
    1255 
     1203        sess->ssi.numitems += aimbs_get16(bs); /* # of items in this SSI SNAC */
     1204
     1205        /* Read in the list */
     1206        while (aim_bstream_empty(bs) > 4) { /* last four bytes are timestamp */
    12561207                if ((namelen = aimbs_get16(bs)))
    1257                         cur->name = aimbs_getstr(bs, namelen);
    1258                 cur->gid = aimbs_get16(bs);
    1259                 cur->bid = aimbs_get16(bs);
    1260                 cur->type = aimbs_get16(bs);
    1261 
    1262                 if ((tbslen = aimbs_get16(bs))) {
    1263                         aim_bstream_t tbs;
    1264 
    1265                         aim_bstream_init(&tbs, bs->data + bs->offset /* XXX */, tbslen);
    1266                         cur->data = (void *)aim_readtlvchain(&tbs);
    1267                         aim_bstream_advance(bs, tbslen);
    1268                 }
    1269         }
    1270 
    1271         timestamp = aimbs_get32(bs);
    1272         if (timestamp != 0)
    1273                 sess->ssi.timestamp = timestamp;
    1274         sess->ssi.received_data = 1;
    1275 
    1276         if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
    1277                 ret = userfunc(sess, rx, fmtver, sess->ssi.revision, sess->ssi.timestamp, sess->ssi.items);
     1208                        name = aimbs_getstr(bs, namelen);
     1209                else
     1210                        name = NULL;
     1211                gid = aimbs_get16(bs);
     1212                bid = aimbs_get16(bs);
     1213                type = aimbs_get16(bs);
     1214                data = aim_readtlvchain_len(bs, aimbs_get16(bs));
     1215                aim_ssi_itemlist_add(&sess->ssi.official, name, gid, bid, type, data);
     1216                free(name);
     1217                aim_freetlvchain(&data);
     1218        }
     1219
     1220        /* Read in the timestamp */
     1221        sess->ssi.timestamp = aimbs_get32(bs);
     1222
     1223        if (!(snac->flags & 0x0001)) {
     1224                /* Make a copy of the list */
     1225                struct aim_ssi_item *cur;
     1226                for (cur=sess->ssi.official; cur; cur=cur->next)
     1227                        aim_ssi_itemlist_add(&sess->ssi.local, cur->name, cur->gid, cur->bid, cur->type, cur->data);
     1228
     1229                sess->ssi.received_data = 1;
     1230
     1231                if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
     1232                        ret = userfunc(sess, rx, fmtver, sess->ssi.numitems, sess->ssi.official, sess->ssi.timestamp);
     1233        }
    12781234
    12791235        return ret;
     
    12811237
    12821238/*
    1283  * SSI Data Enable Presence.
     1239 * Subtype 0x0007 - SSI Activate Data.
    12841240 *
    12851241 * Should be sent after receiving 13/6 or 13/f to tell the server you
     
    12891245 *
    12901246 */
    1291 faim_export int aim_ssi_enable(aim_session_t *sess, aim_conn_t *conn)
    1292 {
     1247faim_export int aim_ssi_enable(aim_session_t *sess)
     1248{
     1249        aim_conn_t *conn;
     1250
     1251        if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)))
     1252                return -EINVAL;
     1253
    12931254        return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, 0x0007);
    12941255}
    12951256
    12961257/*
    1297  * SSI Add/Mod/Del Item(s).
     1258 * Subtype 0x0008/0x0009/0x000a - SSI Add/Mod/Del Item(s).
    12981259 *
    12991260 * Sends the SNAC to add, modify, or delete an item from the server-stored
     
    13021263 *
    13031264 */
    1304 faim_export int aim_ssi_addmoddel(aim_session_t *sess, aim_conn_t *conn, struct aim_ssi_item **items, unsigned int num, fu16_t subtype)
    1305 {
     1265faim_export int aim_ssi_addmoddel(aim_session_t *sess)
     1266{
     1267        aim_conn_t *conn;
    13061268        aim_frame_t *fr;
    13071269        aim_snacid_t snacid;
    1308         int i, snaclen;
    1309 
    1310         if (!sess || !conn || !items || !num)
    1311                 return -EINVAL;
    1312 
     1270        int snaclen;
     1271        struct aim_ssi_tmp *cur;
     1272
     1273        if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)) || !sess->ssi.pending || !sess->ssi.pending->item)
     1274                return -EINVAL;
     1275
     1276        /* Calculate total SNAC size */
    13131277        snaclen = 10; /* For family, subtype, flags, and SNAC ID */
    1314         for (i=0; i<num; i++) {
     1278        for (cur=sess->ssi.pending; cur; cur=cur->next) {
    13151279                snaclen += 10; /* For length, GID, BID, type, and length */
    1316                 if (items[i]->name)
    1317                         snaclen += strlen(items[i]->name);
    1318                 if (items[i]->data)
    1319                         snaclen += aim_sizetlvchain((aim_tlvlist_t **)&items[i]->data);
     1280                if (cur->item->name)
     1281                        snaclen += strlen(cur->item->name);
     1282                if (cur->item->data)
     1283                        snaclen += aim_sizetlvchain(&cur->item->data);
    13201284        }
    13211285
     
    13231287                return -ENOMEM;
    13241288
    1325         snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, subtype, 0x0000, NULL, 0);
    1326         aim_putsnac(&fr->data, AIM_CB_FAM_SSI, subtype, 0x0000, snacid);
    1327 
    1328         for (i=0; i<num; i++) {
    1329                 aimbs_put16(&fr->data, items[i]->name ? strlen(items[i]->name) : 0);
    1330                 if (items[i]->name)
    1331                         aimbs_putraw(&fr->data, items[i]->name, strlen(items[i]->name));
    1332                 aimbs_put16(&fr->data, items[i]->gid);
    1333                 aimbs_put16(&fr->data, items[i]->bid);
    1334                 aimbs_put16(&fr->data, items[i]->type);
    1335                 aimbs_put16(&fr->data, items[i]->data ? aim_sizetlvchain((aim_tlvlist_t **)&items[i]->data) : 0);
    1336                 if (items[i]->data)
    1337                         aim_writetlvchain(&fr->data, (aim_tlvlist_t **)&items[i]->data);
    1338         }
    1339 
    1340         aim_ssi_enqueue(sess, conn, fr);
    1341 
    1342         return 0;
    1343 }
    1344 
    1345 /*
    1346  * SSI Add/Mod/Del Ack.
    1347  *
    1348  * Response to add, modify, or delete SNAC (sent with aim_ssi_addmoddel).
    1349  *
    1350  */
    1351 static int parseack(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
     1289        snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, sess->ssi.pending->action, 0x0000, NULL, 0);
     1290        aim_putsnac(&fr->data, AIM_CB_FAM_SSI, sess->ssi.pending->action, 0x0000, snacid);
     1291
     1292        for (cur=sess->ssi.pending; cur; cur=cur->next) {
     1293                aimbs_put16(&fr->data, cur->item->name ? strlen(cur->item->name) : 0);
     1294                if (cur->item->name)
     1295                        aimbs_putraw(&fr->data, cur->item->name, strlen(cur->item->name));
     1296                aimbs_put16(&fr->data, cur->item->gid);
     1297                aimbs_put16(&fr->data, cur->item->bid);
     1298                aimbs_put16(&fr->data, cur->item->type);
     1299                aimbs_put16(&fr->data, cur->item->data ? aim_sizetlvchain(&cur->item->data) : 0);
     1300                if (cur->item->data)
     1301                        aim_writetlvchain(&fr->data, &cur->item->data);
     1302        }
     1303
     1304        aim_tx_enqueue(sess, fr);
     1305
     1306        return 0;
     1307}
     1308
     1309/*
     1310 * Subtype 0x0008 - Incoming SSI add.
     1311 *
     1312 * XXX - It would probably be good for the client to actually do something when it gets this.
     1313 */
     1314static int parseadd(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
    13521315{
    13531316        int ret = 0;
    13541317        aim_rxcallback_t userfunc;
    1355 
    1356         sess->ssi.waiting_for_ack = 0;
    1357         aim_ssi_dispatch(sess, rx->conn);
     1318        char *name;
     1319        fu16_t len, gid, bid, type;
     1320        aim_tlvlist_t *data;
     1321
     1322        while (aim_bstream_empty(bs)) {
     1323                if ((len = aimbs_get16(bs)))
     1324                        name = aimbs_getstr(bs, len);
     1325                else
     1326                        name = NULL;
     1327                gid = aimbs_get16(bs);
     1328                bid = aimbs_get16(bs);
     1329                type = aimbs_get16(bs);
     1330                if ((len = aimbs_get16(bs)))
     1331                        data = aim_readtlvchain_len(bs, len);
     1332                else
     1333                        data = NULL;
     1334
     1335                aim_ssi_itemlist_add(&sess->ssi.local, name, gid, bid, type, data);
     1336                aim_ssi_itemlist_add(&sess->ssi.official, name, gid, bid, type, data);
     1337                free(name);
     1338                aim_freetlvchain(&data);
     1339
     1340                if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
     1341                        ret = userfunc(sess, rx);
     1342
     1343                free(name);
     1344        }
     1345
     1346        return ret;
     1347}
     1348
     1349/*
     1350 * Subtype 0x0009 - Incoming SSI mod.
     1351 *
     1352 * XXX - It would probably be good for the client to actually do something when it gets this.
     1353 */
     1354static int parsemod(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
     1355{
     1356        int ret = 0;
     1357        aim_rxcallback_t userfunc;
     1358        char *name;
     1359        fu16_t len, gid, bid, type;
     1360        aim_tlvlist_t *data;
     1361        struct aim_ssi_item *item;
     1362
     1363        while (aim_bstream_empty(bs)) {
     1364                if ((len = aimbs_get16(bs)))
     1365                        name = aimbs_getstr(bs, len);
     1366                else
     1367                        name = NULL;
     1368                gid = aimbs_get16(bs);
     1369                bid = aimbs_get16(bs);
     1370                type = aimbs_get16(bs);
     1371                if ((len = aimbs_get16(bs)))
     1372                        data = aim_readtlvchain_len(bs, len);
     1373                else
     1374                        data = NULL;
     1375
     1376                /* Replace the 2 local items with the given one */
     1377                if ((item = aim_ssi_itemlist_find(sess->ssi.local, gid, bid))) {
     1378                        item->type = type;
     1379                        free(item->name);
     1380                        if (name) {
     1381                                item->name = (char *)malloc((strlen(name)+1)*sizeof(char));
     1382                                strcpy(item->name, name);
     1383                        } else
     1384                                item->name = NULL;
     1385                        aim_freetlvchain(&item->data);
     1386                        item->data = aim_tlvlist_copy(data);
     1387                }
     1388
     1389                if ((item = aim_ssi_itemlist_find(sess->ssi.official, gid, bid))) {
     1390                        item->type = type;
     1391                        free(item->name);
     1392                        if (name) {
     1393                                item->name = (char *)malloc((strlen(name)+1)*sizeof(char));
     1394                                strcpy(item->name, name);
     1395                        } else
     1396                                item->name = NULL;
     1397                        aim_freetlvchain(&item->data);
     1398                        item->data = aim_tlvlist_copy(data);
     1399                }
     1400
     1401                if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
     1402                        ret = userfunc(sess, rx);
     1403
     1404                free(name);
     1405                aim_freetlvchain(&data);
     1406        }
     1407
     1408        return ret;
     1409}
     1410
     1411/*
     1412 * Subtype 0x000a - Incoming SSI del.
     1413 *
     1414 * XXX - It would probably be good for the client to actually do something when it gets this.
     1415 */
     1416static int parsedel(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
     1417{
     1418        int ret = 0;
     1419        aim_rxcallback_t userfunc;
     1420        fu16_t gid, bid;
     1421        struct aim_ssi_item *del;
     1422
     1423        while (aim_bstream_empty(bs)) {
     1424                aim_bstream_advance(bs, aimbs_get16(bs));
     1425                gid = aimbs_get16(bs);
     1426                bid = aimbs_get16(bs);
     1427                aimbs_get16(bs);
     1428                aim_bstream_advance(bs, aimbs_get16(bs));
     1429
     1430                if ((del = aim_ssi_itemlist_find(sess->ssi.local, gid, bid)))
     1431                        aim_ssi_itemlist_del(&sess->ssi.local, del);
     1432                if ((del = aim_ssi_itemlist_find(sess->ssi.official, gid, bid)))
     1433                        aim_ssi_itemlist_del(&sess->ssi.official, del);
     1434
     1435                if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
     1436                        ret = userfunc(sess, rx);
     1437        }
     1438
     1439        return ret;
     1440}
     1441
     1442/*
     1443 * Subtype 0x000e - SSI Add/Mod/Del Ack.
     1444 *
     1445 * Response to add, modify, or delete SNAC (sent with aim_ssi_addmoddel).
     1446 *
     1447 */
     1448static int parseack(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
     1449{
     1450        int ret = 0;
     1451        aim_rxcallback_t userfunc;
     1452        struct aim_ssi_tmp *cur, *del;
     1453
     1454        /* Read in the success/failure flags from the ack SNAC */
     1455        cur = sess->ssi.pending;
     1456        while (cur && (aim_bstream_empty(bs)>0)) {
     1457                cur->ack = aimbs_get16(bs);
     1458                cur = cur->next;
     1459        }
     1460
     1461        /*
     1462         * If outcome is 0, then add the item to the item list, or replace the other item,
     1463         * or remove the old item.  If outcome is non-zero, then remove the item from the
     1464         * local list, or unmodify it, or add it.
     1465         */
     1466        for (cur=sess->ssi.pending; (cur && (cur->ack != 0xffff)); cur=cur->next) {
     1467        if (cur->item) {
     1468                if (cur->ack) {
     1469                        /* Our action was unsuccessful, so change the local list back to how it was */
     1470                        if (cur->action == AIM_CB_SSI_ADD) {
     1471                                /* Remove the item from the local list */
     1472                                /* Make sure cur->item is still valid memory */
     1473                                if (aim_ssi_itemlist_valid(sess->ssi.local, cur->item)) {
     1474                                        if (cur->item->name) {
     1475                                                cur->name = (char *)malloc((strlen(cur->item->name)+1)*sizeof(char));
     1476                                                strcpy(cur->name, cur->item->name);
     1477                                        }
     1478                                        aim_ssi_itemlist_del(&sess->ssi.local, cur->item);
     1479                                }
     1480                                cur->item = NULL;
     1481
     1482                        } else if (cur->action == AIM_CB_SSI_MOD) {
     1483                                /* Replace the local item with the item from the official list */
     1484                                if (aim_ssi_itemlist_valid(sess->ssi.local, cur->item)) {
     1485                                        struct aim_ssi_item *cur1;
     1486                                        if ((cur1 = aim_ssi_itemlist_find(sess->ssi.official, cur->item->gid, cur->item->bid))) {
     1487                                                free(cur->item->name);
     1488                                                if (cur1->name) {
     1489                                                        cur->item->name = (char *)malloc((strlen(cur1->name)+1)*sizeof(char));
     1490                                                        strcpy(cur->item->name, cur1->name);
     1491                                                } else
     1492                                                        cur->item->name = NULL;
     1493                                                aim_freetlvchain(&cur->item->data);
     1494                                                cur->item->data = aim_tlvlist_copy(cur1->data);
     1495                                        }
     1496                                } else
     1497                                        cur->item = NULL;
     1498
     1499                        } else if (cur->action == AIM_CB_SSI_DEL) {
     1500                                /* Add the item back into the local list */
     1501                                if (aim_ssi_itemlist_valid(sess->ssi.official, cur->item)) {
     1502                                        aim_ssi_itemlist_add(&sess->ssi.local, cur->item->name, cur->item->gid, cur->item->bid, cur->item->type, cur->item->data);
     1503                                } else
     1504                                        cur->item = NULL;
     1505                        }
     1506
     1507                } else {
     1508                        /* Do the exact opposite */
     1509                        if (cur->action == AIM_CB_SSI_ADD) {
     1510                        /* Add the local item to the official list */
     1511                                if (aim_ssi_itemlist_valid(sess->ssi.local, cur->item)) {
     1512                                        aim_ssi_itemlist_add(&sess->ssi.official, cur->item->name, cur->item->gid, cur->item->bid, cur->item->type, cur->item->data);
     1513                                } else
     1514                                        cur->item = NULL;
     1515
     1516                        } else if (cur->action == AIM_CB_SSI_MOD) {
     1517                                /* Replace the official item with the item from the local list */
     1518                                if (aim_ssi_itemlist_valid(sess->ssi.local, cur->item)) {
     1519                                        struct aim_ssi_item *cur1;
     1520                                        if ((cur1 = aim_ssi_itemlist_find(sess->ssi.official, cur->item->gid, cur->item->bid))) {
     1521                                                free(cur1->name);
     1522                                                if (cur->item->name) {
     1523                                                        cur1->name = (char *)malloc((strlen(cur->item->name)+1)*sizeof(char));
     1524                                                        strcpy(cur1->name, cur->item->name);
     1525                                                } else
     1526                                                        cur1->name = NULL;
     1527                                                aim_freetlvchain(&cur1->data);
     1528                                                cur1->data = aim_tlvlist_copy(cur->item->data);
     1529                                        }
     1530                                } else
     1531                                        cur->item = NULL;
     1532
     1533                        } else if (cur->action == AIM_CB_SSI_DEL) {
     1534                                /* Remove the item from the official list */
     1535                                if (aim_ssi_itemlist_valid(sess->ssi.official, cur->item))
     1536                                        aim_ssi_itemlist_del(&sess->ssi.official, cur->item);
     1537                                cur->item = NULL;
     1538                        }
     1539
     1540                }
     1541        } /* End if (cur->item) */
     1542        } /* End for loop */
     1543
     1544        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
     1545                ret = userfunc(sess, rx, sess->ssi.pending);
     1546
     1547        /* Free all aim_ssi_tmp's with an outcome */
     1548        cur = sess->ssi.pending;
     1549        while (cur && (cur->ack != 0xffff)) {
     1550                del = cur;
     1551                cur = cur->next;
     1552                free(del->name);
     1553                free(del);
     1554        }
     1555        sess->ssi.pending = cur;
     1556
     1557        /* If we're not waiting for any more acks, then send more SNACs */
     1558        if (!sess->ssi.pending) {
     1559                sess->ssi.pending = NULL;
     1560                sess->ssi.waiting_for_ack = 0;
     1561                aim_ssi_sync(sess);
     1562        }
     1563
     1564        return ret;
     1565}
     1566
     1567/*
     1568 * Subtype 0x000f - SSI Data Unchanged.
     1569 *
     1570 * Response to aim_ssi_reqifchanged() if the server-side data is not newer than
     1571 * posted local stamp/revision.
     1572 *
     1573 */
     1574static int parsedataunchanged(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
     1575{
     1576        int ret = 0;
     1577        aim_rxcallback_t userfunc;
     1578
     1579        sess->ssi.received_data = 1;
    13581580
    13591581        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
     
    13641586
    13651587/*
    1366  * SSI Begin Data Modification.
     1588 * Subtype 0x0011 - SSI Begin Data Modification.
    13671589 *
    13681590 * Tells the server you're going to start modifying data.
    13691591 *
    13701592 */
    1371 faim_export int aim_ssi_modbegin(aim_session_t *sess, aim_conn_t *conn)
    1372 {
     1593faim_export int aim_ssi_modbegin(aim_session_t *sess)
     1594{
     1595        aim_conn_t *conn;
     1596
     1597        if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)))
     1598                return -EINVAL;
     1599
    13731600        return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_EDITSTART);
    13741601}
    13751602
    13761603/*
    1377  * SSI End Data Modification.
    1378  *
    1379  * Tells the server you're done modifying data.
    1380  *
    1381  */
    1382 faim_export int aim_ssi_modend(aim_session_t *sess, aim_conn_t *conn)
    1383 {
     1604 * Subtype 0x0012 - SSI End Data Modification.
     1605 *
     1606 * Tells the server you're finished modifying data.
     1607 *
     1608 */
     1609faim_export int aim_ssi_modend(aim_session_t *sess)
     1610{
     1611        aim_conn_t *conn;
     1612
     1613        if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)))
     1614                return -EINVAL;
     1615
    13841616        return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_EDITSTOP);
    13851617}
    13861618
    13871619/*
    1388  * SSI Data Unchanged.
    1389  *
    1390  * Response to aim_ssi_reqdata() if the server-side data is not newer than
    1391  * posted local stamp/revision.
    1392  *
    1393  */
    1394 static int parsedataunchanged(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
     1620 * Subtype 0x0014 - Grant authorization
     1621 *
     1622 * Authorizes a contact so they can add you to their contact list.
     1623 *
     1624 */
     1625faim_export int aim_ssi_sendauth(aim_session_t *sess, char *sn, char *msg)
     1626{
     1627        aim_conn_t *conn;
     1628        aim_frame_t *fr;
     1629        aim_snacid_t snacid;
     1630
     1631        if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)) || !sn)
     1632                return -EINVAL;
     1633
     1634        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)+2+(msg ? strlen(msg)+1 : 0)+2)))
     1635                return -ENOMEM;
     1636
     1637        snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTH, 0x0000, NULL, 0);
     1638        aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTH, 0x0000, snacid);
     1639
     1640        /* Screen name */
     1641        aimbs_put8(&fr->data, strlen(sn));
     1642        aimbs_putraw(&fr->data, sn, strlen(sn));
     1643
     1644        /* Message (null terminated) */
     1645        aimbs_put16(&fr->data, msg ? strlen(msg) : 0);
     1646        if (msg) {
     1647                aimbs_putraw(&fr->data, msg, strlen(msg));
     1648                aimbs_put8(&fr->data, 0x00);
     1649        }
     1650
     1651        /* Unknown */
     1652        aimbs_put16(&fr->data, 0x0000);
     1653
     1654        aim_tx_enqueue(sess, fr);
     1655
     1656        return 0;
     1657}
     1658
     1659/*
     1660 * Subtype 0x0015 - Receive an authorization grant
     1661 */
     1662static int receiveauthgrant(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
    13951663{
    13961664        int ret = 0;
    13971665        aim_rxcallback_t userfunc;
    1398 
    1399         sess->ssi.received_data = 1;
     1666        fu16_t tmp;
     1667        char *sn, *msg;
     1668
     1669        /* Read screen name */
     1670        if ((tmp = aimbs_get8(bs)))
     1671                sn = aimbs_getstr(bs, tmp);
     1672        else
     1673                sn = NULL;
     1674
     1675        /* Read message (null terminated) */
     1676        if ((tmp = aimbs_get16(bs)))
     1677                msg = aimbs_getstr(bs, tmp);
     1678        else
     1679                msg = NULL;
     1680
     1681        /* Unknown */
     1682        tmp = aimbs_get16(bs);
    14001683
    14011684        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
    1402                 ret = userfunc(sess, rx);
     1685                ret = userfunc(sess, rx, sn, msg);
     1686
     1687        free(sn);
     1688        free(msg);
     1689
     1690        return ret;
     1691}
     1692
     1693/*
     1694 * Subtype 0x0018 - Send authorization request
     1695 *
     1696 * Sends a request for authorization to the given contact.  The request will either be
     1697 * granted, denied, or dropped.
     1698 *
     1699 */
     1700faim_export int aim_ssi_sendauthrequest(aim_session_t *sess, char *sn, char *msg)
     1701{
     1702        aim_conn_t *conn;
     1703        aim_frame_t *fr;
     1704        aim_snacid_t snacid;
     1705
     1706        if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)) || !sn)
     1707                return -EINVAL;
     1708
     1709        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)+2+(msg ? strlen(msg)+1 : 0)+2)))
     1710                return -ENOMEM;
     1711
     1712        snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREQ, 0x0000, NULL, 0);
     1713        aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREQ, 0x0000, snacid);
     1714
     1715        /* Screen name */
     1716        aimbs_put8(&fr->data, strlen(sn));
     1717        aimbs_putraw(&fr->data, sn, strlen(sn));
     1718
     1719        /* Message (null terminated) */
     1720        aimbs_put16(&fr->data, msg ? strlen(msg) : 0);
     1721        if (msg) {
     1722                aimbs_putraw(&fr->data, msg, strlen(msg));
     1723                aimbs_put8(&fr->data, 0x00);
     1724        }
     1725
     1726        /* Unknown */
     1727        aimbs_put16(&fr->data, 0x0000);
     1728
     1729        aim_tx_enqueue(sess, fr);
     1730
     1731        return 0;
     1732}
     1733
     1734/*
     1735 * Subtype 0x0019 - Receive an authorization request
     1736 */
     1737static int receiveauthrequest(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
     1738{
     1739        int ret = 0;
     1740        aim_rxcallback_t userfunc;
     1741        fu16_t tmp;
     1742        char *sn, *msg;
     1743
     1744        /* Read screen name */
     1745        if ((tmp = aimbs_get8(bs)))
     1746                sn = aimbs_getstr(bs, tmp);
     1747        else
     1748                sn = NULL;
     1749
     1750        /* Read message (null terminated) */
     1751        if ((tmp = aimbs_get16(bs)))
     1752                msg = aimbs_getstr(bs, tmp);
     1753        else
     1754                msg = NULL;
     1755
     1756        /* Unknown */
     1757        tmp = aimbs_get16(bs);
     1758
     1759        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
     1760                ret = userfunc(sess, rx, sn, msg);
     1761
     1762        free(sn);
     1763        free(msg);
     1764
     1765        return ret;
     1766}
     1767
     1768/*
     1769 * Subtype 0x001a - Send authorization reply
     1770 *
     1771 * Sends a reply to a request for authorization.  The reply can either
     1772 * grant authorization or deny authorization.
     1773 *
     1774 * if reply=0x00 then deny
     1775 * if reply=0x01 then grant
     1776 *
     1777 */
     1778faim_export int aim_ssi_sendauthreply(aim_session_t *sess, char *sn, fu8_t reply, char *msg)
     1779{
     1780        aim_conn_t *conn;
     1781        aim_frame_t *fr;
     1782        aim_snacid_t snacid;
     1783
     1784        if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)) || !sn)
     1785                return -EINVAL;
     1786
     1787        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 1+strlen(sn) + 1 + 2+(msg ? strlen(msg)+1 : 0) + 2)))
     1788                return -ENOMEM;
     1789
     1790        snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREP, 0x0000, NULL, 0);
     1791        aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREP, 0x0000, snacid);
     1792
     1793        /* Screen name */
     1794        aimbs_put8(&fr->data, strlen(sn));
     1795        aimbs_putraw(&fr->data, sn, strlen(sn));
     1796
     1797        /* Grant or deny */
     1798        aimbs_put8(&fr->data, reply);
     1799
     1800        /* Message (null terminated) */
     1801        aimbs_put16(&fr->data, msg ? (strlen(msg)+1) : 0);
     1802        if (msg) {
     1803                aimbs_putraw(&fr->data, msg, strlen(msg));
     1804                aimbs_put8(&fr->data, 0x00);
     1805        }
     1806
     1807        /* Unknown */
     1808        aimbs_put16(&fr->data, 0x0000);
     1809
     1810        aim_tx_enqueue(sess, fr);
     1811
     1812        return 0;
     1813}
     1814
     1815/*
     1816 * Subtype 0x001b - Receive an authorization reply
     1817 * You get this bad boy when other people respond to the authorization
     1818 * request that you have previously sent them.
     1819 */
     1820static int receiveauthreply(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
     1821{
     1822        int ret = 0;
     1823        aim_rxcallback_t userfunc;
     1824        fu16_t tmp;
     1825        fu8_t reply;
     1826        char *sn, *msg;
     1827
     1828        /* Read screen name */
     1829        if ((tmp = aimbs_get8(bs)))
     1830                sn = aimbs_getstr(bs, tmp);
     1831        else
     1832                sn = NULL;
     1833
     1834        /* Read reply */
     1835        reply = aimbs_get8(bs);
     1836
     1837        /* Read message (null terminated) */
     1838        if ((tmp = aimbs_get16(bs)))
     1839                msg = aimbs_getstr(bs, tmp);
     1840        else
     1841                msg = NULL;
     1842
     1843        /* Unknown */
     1844        tmp = aimbs_get16(bs);
     1845
     1846        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
     1847                ret = userfunc(sess, rx, sn, reply, msg);
     1848
     1849        free(sn);
     1850        free(msg);
     1851
     1852        return ret;
     1853}
     1854
     1855/*
     1856 * Subtype 0x001c - Receive a message telling you someone added you to their list.
     1857 */
     1858static int receiveadded(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
     1859{
     1860        int ret = 0;
     1861        aim_rxcallback_t userfunc;
     1862        fu16_t tmp;
     1863        char *sn;
     1864
     1865        /* Read screen name */
     1866        if ((tmp = aimbs_get8(bs)))
     1867                sn = aimbs_getstr(bs, tmp);
     1868        else
     1869                sn = NULL;
     1870
     1871        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
     1872                ret = userfunc(sess, rx, sn);
     1873
     1874        free(sn);
    14031875
    14041876        return ret;
     
    14121884        else if (snac->subtype == AIM_CB_SSI_LIST)
    14131885                return parsedata(sess, mod, rx, snac, bs);
     1886        else if (snac->subtype == AIM_CB_SSI_ADD)
     1887                return parseadd(sess, mod, rx, snac, bs);
     1888        else if (snac->subtype == AIM_CB_SSI_MOD)
     1889                return parsemod(sess, mod, rx, snac, bs);
     1890        else if (snac->subtype == AIM_CB_SSI_DEL)
     1891                return parsedel(sess, mod, rx, snac, bs);
    14141892        else if (snac->subtype == AIM_CB_SSI_SRVACK)
    14151893                return parseack(sess, mod, rx, snac, bs);
    14161894        else if (snac->subtype == AIM_CB_SSI_NOLIST)
    14171895                return parsedataunchanged(sess, mod, rx, snac, bs);
     1896        else if (snac->subtype == AIM_CB_SSI_RECVAUTH)
     1897                return receiveauthgrant(sess, mod, rx, snac, bs);
     1898        else if (snac->subtype == AIM_CB_SSI_RECVAUTHREQ)
     1899                return receiveauthrequest(sess, mod, rx, snac, bs);
     1900        else if (snac->subtype == AIM_CB_SSI_RECVAUTHREP)
     1901                return receiveauthreply(sess, mod, rx, snac, bs);
     1902        else if (snac->subtype == AIM_CB_SSI_ADDED)
     1903                return receiveadded(sess, mod, rx, snac, bs);
    14181904
    14191905        return 0;
     
    14311917
    14321918        mod->family = AIM_CB_FAM_SSI;
    1433         mod->version = 0x0001;
     1919        mod->version = 0x0004;
    14341920        mod->toolid = 0x0110;
    1435         mod->toolversion = 0x047b;
     1921        mod->toolversion = 0x0629;
    14361922        mod->flags = 0;
    14371923        strncpy(mod->name, "ssi", sizeof(mod->name));
Note: See TracChangeset for help on using the changeset viewer.