Changeset 862371b for libfaim/ssi.c


Ignore:
Timestamp:
Jun 29, 2003, 1:47:04 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:
e016fc2
Parents:
03ad7b2
Message:
*** empty log message ***
File:
1 edited

Legend:

Unmodified
Added
Removed
  • libfaim/ssi.c

    r5e53c4a r862371b  
    66 * to be stored on the server, so that they can be accessed from any client.
    77 *
     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.
     11 *
     12 * The SNAC sending and receiving functions are lower down in the file, and
     13 * they're simpler.  They are in the order of the subtypes they deal with,
     14 * starting with the request rights function (subtype 0x0002), then parse
     15 * rights (subtype 0x0003), then--well, you get the idea.
     16 *
    817 * This is entirely too complicated.
     18 * You don't know the half of it.
     19 *
     20 * XXX - Test for memory leaks
     21 * XXX - Better parsing of rights, and use the rights info to limit adds
    922 *
    1023 */
     
    1326#include <aim.h>
    1427
     28/**
     29 * Locally add a new item to the given item list.
     30 *
     31 * @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).
     34 * @param name A null terminated string of the name of the new item, or NULL if the
     35 *        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 */
     39static struct aim_ssi_item *aim_ssi_itemlist_add(struct aim_ssi_item **list, struct aim_ssi_item *parent, const char *name, fu16_t type)
     40{
     41        int i;
     42        struct aim_ssi_item *cur, *newitem;
     43
     44        if (!(newitem = (struct aim_ssi_item *)malloc(sizeof(struct aim_ssi_item))))
     45                return NULL;
     46
     47        /* Set the name */
     48        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);
     54        } else
     55                newitem->name = NULL;
     56
     57        /* Set the group ID# and the buddy ID# */
     58        newitem->gid = 0x0000;
     59        newitem->bid = 0x0000;
     60        if (type == AIM_SSI_TYPE_GROUP) {
     61                if (name)
     62                        do {
     63                                newitem->gid += 0x0001;
     64                                for (cur=*list, i=0; ((cur) && (!i)); cur=cur->next)
     65                                        if ((cur->gid == newitem->gid) && (cur->gid == newitem->gid))
     66                                                i=1;
     67                        } while (i);
     68        } 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.
     90 *
     91 * @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.
     93 * @return Return 0 if no errors, otherwise return the error number.
     94 */
     95static int aim_ssi_itemlist_rebuildgroup(struct aim_ssi_item **list, struct aim_ssi_item *parentgroup)
     96{
     97        int newlen;
     98        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 */
     148static 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
     165        return 0;
     166}
     167
     168/**
     169 * Locally find an item given a group ID# and a buddy ID#.
     170 *
     171 * @param list A pointer to the current list of items.
     172 * @param gid The group ID# of the desired item.
     173 * @param bid The buddy ID# of the desired item.
     174 * @return Return a pointer to the item if found, else return NULL;
     175 */
     176faim_export struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_item *list, fu16_t gid, fu16_t bid)
     177{
     178        struct aim_ssi_item *cur;
     179        for (cur=list; cur; cur=cur->next)
     180                if ((cur->gid == gid) && (cur->bid == bid))
     181                        return cur;
     182        return NULL;
     183}
     184
     185/**
     186 * Locally find an item given a group name, screen name, and type.  If group name
     187 * and screen name are null, then just return the first item of the given type.
     188 *
     189 * @param list A pointer to the current list of items.
     190 * @param gn The group name of the desired item.
     191 * @param bn The buddy name of the desired item.
     192 * @param type The type of the desired item.
     193 * @return Return a pointer to the item if found, else return NULL;
     194 */
     195faim_export struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_item *list, const char *gn, const char *sn, fu16_t type)
     196{
     197        struct aim_ssi_item *cur;
     198        if (!list)
     199                return NULL;
     200
     201        if (gn && sn) { /* For finding buddies in groups */
     202                for (cur=list; cur; cur=cur->next)
     203                        if ((cur->type == type) && (cur->name) && !(aim_sncmp(cur->name, sn))) {
     204                                struct aim_ssi_item *curg;
     205                                for (curg=list; curg; curg=curg->next)
     206                                        if ((curg->type == AIM_SSI_TYPE_GROUP) && (curg->gid == cur->gid) && (curg->name) && !(aim_sncmp(curg->name, gn)))
     207                                                return cur;
     208                        }
     209
     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)))
     213                                return cur;
     214
     215        /* For stuff without names--permit deny setting, visibility mask, etc. */
     216        } else for (cur=list; cur; cur=cur->next) {
     217                if (cur->type == type)
     218                        return cur;
     219        }
     220
     221        return NULL;
     222}
     223
     224/**
     225 * Locally find the parent item of the given buddy name.
     226 *
     227 * @param list A pointer to the current list of items.
     228 * @param bn The buddy name of the desired item.
     229 * @return Return a pointer to the item if found, else return NULL;
     230 */
     231faim_export struct aim_ssi_item *aim_ssi_itemlist_findparent(struct aim_ssi_item *list, char *sn)
     232{
     233        struct aim_ssi_item *cur, *curg;
     234        if (!list || !sn)
     235                return NULL;
     236        if (!(cur = aim_ssi_itemlist_finditem(list, NULL, sn, AIM_SSI_TYPE_BUDDY)))
     237                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;
     242}
     243
     244/**
     245 * Locally find the permit/deny setting item, and return the setting.
     246 *
     247 * @param list A pointer to the current list of items.
     248 * @return Return the current SSI permit deny setting, or 0 if no setting was found.
     249 */
     250faim_export int aim_ssi_getpermdeny(struct aim_ssi_item *list)
     251{
     252        struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PDINFO);
     253        if (cur) {
     254                aim_tlvlist_t *tlvlist = cur->data;
     255                if (tlvlist) {
     256                        aim_tlv_t *tlv = aim_gettlv(tlvlist, 0x00ca, 1);
     257                        if (tlv && tlv->value)
     258                                return aimutil_get8(tlv->value);
     259                }
     260        }
     261        return 0;
     262}
     263
     264/**
     265 * Locally find the presence flag item, and return the setting.  The returned setting is a
     266 * bitmask of the user flags that you are visible to.  See the AIM_FLAG_* #defines
     267 * in aim.h
     268 *
     269 * @param list A pointer to the current list of items.
     270 * @return Return the current visibility mask.
     271 */
     272faim_export fu32_t aim_ssi_getpresence(struct aim_ssi_item *list)
     273{
     274        struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS);
     275        if (cur) {
     276                aim_tlvlist_t *tlvlist = cur->data;
     277                if (tlvlist) {
     278                        aim_tlv_t *tlv = aim_gettlv(tlvlist, 0x00c9, 1);
     279                        if (tlv && tlv->length)
     280                                return aimutil_get32(tlv->value);
     281                }
     282        }
     283        return 0xFFFFFFFF;
     284}
     285
     286/**
     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.
     290 *
     291 * @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.
     294 * @return Return 0 if no errors, otherwise return the error number.
     295 */
     296static 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.
     320 *
     321 * @param sess The oscar session.
     322 * @param conn The bos connection for this session.
     323 * @return Return 0 if no errors, otherwise return the error number.
     324 */
     325static 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.
     349 *
     350 * @param sess The oscar session.
     351 * @param conn The bos connection for this session.
     352 * @return Return 0 if no errors, otherwise return the error number.
     353 */
     354faim_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.
     385 *
     386 * @param sess The oscar session.
     387 * @param conn The bos connection for this session.
     388 * @return Return 0 if no errors, otherwise return the error number.
     389 */
     390faim_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;
     408                }
     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);
     420                }
     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);
     432                }
     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.
     485 *
     486 * @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).
     491 * @return Return 0 if no errors, otherwise return the error number.
     492 */
     493faim_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.
     546 *
     547 * @param sess The oscar session.
     548 * @param conn The bos connection for this session.
     549 * @return Return 0 if no errors, otherwise return the error number.
     550 */
     551faim_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 */
     584faim_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 */
     647faim_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;
     678
     679        return 0;
     680}
     681
     682/**
     683 * Move a buddy from one group to another group.  This basically just deletes the
     684 * buddy and re-adds it.
     685 *
     686 * @param sess The oscar session.
     687 * @param conn The bos connection for this session.
     688 * @param oldgn The group that the buddy is currently in.
     689 * @param newgn The group that the buddy should be moved in to.
     690 * @param sn The name of the buddy to be moved.
     691 * @return Return 0 if no errors, otherwise return the error number.
     692 */
     693faim_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?...
     762 *
     763 * @param sess The oscar session.
     764 * @param conn The bos connection for this session.
     765 * @param oldgn The old group name.
     766 * @param newgn The new group name.
     767 * @return Return 0 if no errors, otherwise return the error number.
     768 */
     769faim_export int aim_ssi_rename_group(aim_session_t *sess, aim_conn_t *conn, const char *oldgn, const char *newgn)
     770{
     771        struct aim_ssi_item *group;
     772
     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        }
     787        strcpy(group->name, newgn);
     788
     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.
     800 *
     801 * @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 */
     808faim_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 */
     878faim_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 */
     923faim_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 */
     995faim_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.
     1047 * @param permdeny Your permit/deny setting.  Can be one of the following:
     1048 *        1 - Allow all users
     1049 *        2 - Block all users
     1050 *        3 - Allow only the users below
     1051 *        4 - Block only the users below
     1052 *        5 - Allow only users on my buddy list
     1053 * @param vismask A bitmask of the class of users to whom you want to be
     1054 *        visible.  See the AIM_FLAG_BLEH #defines in aim.h
     1055 * @return Return 0 if no errors, otherwise return the error number.
     1056 */
     1057faim_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);
     1080                }
     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);
     1097        } 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
     1109        return 0;
     1110}
     1111
     1112/**
     1113 * Stores your setting for whether you should show up as idle or not.
     1114 *
     1115 * @param sess The oscar session.
     1116 * @param conn The bos connection for this session.
     1117 * @param presence I think it's a bitmask, but I only know what one of the bits is:
     1118 *        0x00000400 - Allow others to see your idle time
     1119 * @return Return 0 if no errors, otherwise return the error number.
     1120 */
     1121faim_export int aim_ssi_setpresence(aim_session_t *sess, aim_conn_t *conn, fu32_t presence) {
     1122        struct aim_ssi_item *cur;
     1123        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
    151162/*
    161163 * Request SSI Rights.
     
    181165faim_export int aim_ssi_reqrights(aim_session_t *sess, aim_conn_t *conn)
    191166{
    20         return aim_genericreq_n(sess, conn, 0x0013, 0x0002);
     1167        return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_REQRIGHTS);
    211168}
    221169
     
    551202                return -ENOMEM;
    561203
    57         snacid = aim_cachesnac(sess, 0x0013, 0x0005, 0x0000, NULL, 0);
    58 
    59         aim_putsnac(&fr->data, 0x0013, 0x0005, 0x0000, snacid);
     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);
    601207        aimbs_put32(&fr->data, localstamp);
    611208        aimbs_put16(&fr->data, localrev);
     
    731220        int ret = 0;
    741221        aim_rxcallback_t userfunc;
    75         struct aim_ssi_item *list = NULL;
     1222        struct aim_ssi_item *cur = NULL;
    761223        fu8_t fmtver; /* guess */
    77         fu16_t itemcount;
    78         fu32_t stamp;
    79 
    80         fmtver = aimbs_get8(bs);
    81         itemcount = aimbs_get16(bs);
     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         */
     1234
     1235        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) ;
    821241
    831242        while (aim_bstream_empty(bs) > 4) { /* last four bytes are stamp */
    841243                fu16_t namelen, tbslen;
    85                 struct aim_ssi_item *nl, *el;
    86 
    87                 if (!(nl = malloc(sizeof(struct aim_ssi_item))))
    88                         break;
    89                 memset(nl, 0, sizeof(struct aim_ssi_item));
     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));
    901255
    911256                if ((namelen = aimbs_get16(bs)))
    92                         nl->name = aimbs_getstr(bs, namelen);
    93                 nl->gid = aimbs_get16(bs);
    94                 nl->bid = aimbs_get16(bs);
    95                 nl->type = 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);
    961261
    971262                if ((tbslen = aimbs_get16(bs))) {
     
    991264
    1001265                        aim_bstream_init(&tbs, bs->data + bs->offset /* XXX */, tbslen);
    101                         nl->data = (void *)aim_readtlvchain(&tbs);
     1266                        cur->data = (void *)aim_readtlvchain(&tbs);
    1021267                        aim_bstream_advance(bs, tbslen);
    1031268                }
    104 
    105                 for (el = list; el && el->next; el = el->next)
    106                         ;
    107                 if (el)
    108                         el->next = nl;
    109                 else
    110                         list = nl;
    111         }
    112 
    113         stamp = aimbs_get32(bs);
     1269        }
     1270
     1271        timestamp = aimbs_get32(bs);
     1272        if (timestamp != 0)
     1273                sess->ssi.timestamp = timestamp;
     1274        sess->ssi.received_data = 1;
    1141275
    1151276        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
    116                 ret = userfunc(sess, rx, fmtver, itemcount, stamp, list);
    117 
    118         while (list) {
    119                 struct aim_ssi_item *tmp;
    120 
    121                 tmp = list->next;
    122                 aim_freetlvchain((aim_tlvlist_t **)&list->data);
    123                 free(list);
    124                 list = tmp;
    125         }
     1277                ret = userfunc(sess, rx, fmtver, sess->ssi.revision, sess->ssi.timestamp, sess->ssi.items);
    1261278
    1271279        return ret;
     
    1391291faim_export int aim_ssi_enable(aim_session_t *sess, aim_conn_t *conn)
    1401292{
    141         return aim_genericreq_n(sess, conn, 0x0013, 0x0007);
     1293        return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, 0x0007);
     1294}
     1295
     1296/*
     1297 * SSI Add/Mod/Del Item(s).
     1298 *
     1299 * Sends the SNAC to add, modify, or delete an item from the server-stored
     1300 * information.  These 3 SNACs all have an identical structure.  The only
     1301 * difference is the subtype that is set for the SNAC.
     1302 *
     1303 */
     1304faim_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{
     1306        aim_frame_t *fr;
     1307        aim_snacid_t snacid;
     1308        int i, snaclen;
     1309
     1310        if (!sess || !conn || !items || !num)
     1311                return -EINVAL;
     1312
     1313        snaclen = 10; /* For family, subtype, flags, and SNAC ID */
     1314        for (i=0; i<num; i++) {
     1315                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);
     1320        }
     1321
     1322        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, snaclen)))
     1323                return -ENOMEM;
     1324
     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 */
     1351static int parseack(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
     1352{
     1353        int ret = 0;
     1354        aim_rxcallback_t userfunc;
     1355
     1356        sess->ssi.waiting_for_ack = 0;
     1357        aim_ssi_dispatch(sess, rx->conn);
     1358
     1359        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
     1360                ret = userfunc(sess, rx);
     1361
     1362        return ret;
    1421363}
    1431364
     
    1501371faim_export int aim_ssi_modbegin(aim_session_t *sess, aim_conn_t *conn)
    1511372{
    152         return aim_genericreq_n(sess, conn, 0x0013, 0x0011);
     1373        return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_EDITSTART);
    1531374}
    1541375
     
    1611382faim_export int aim_ssi_modend(aim_session_t *sess, aim_conn_t *conn)
    1621383{
    163         return aim_genericreq_n(sess, conn, 0x0013, 0x0012);
     1384        return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_EDITSTOP);
    1641385}
    1651386
     
    1761397        aim_rxcallback_t userfunc;
    1771398
     1399        sess->ssi.received_data = 1;
     1400
    1781401        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
    1791402                ret = userfunc(sess, rx);
     
    1851408{
    1861409
    187         if (snac->subtype == 0x0003)
     1410        if (snac->subtype == AIM_CB_SSI_RIGHTSINFO)
    1881411                return parserights(sess, mod, rx, snac, bs);
    189         else if (snac->subtype == 0x006)
     1412        else if (snac->subtype == AIM_CB_SSI_LIST)
    1901413                return parsedata(sess, mod, rx, snac, bs);
    191         else if (snac->subtype == 0x00f)
     1414        else if (snac->subtype == AIM_CB_SSI_SRVACK)
     1415                return parseack(sess, mod, rx, snac, bs);
     1416        else if (snac->subtype == AIM_CB_SSI_NOLIST)
    1921417                return parsedataunchanged(sess, mod, rx, snac, bs);
    1931418
     
    1951420}
    1961421
     1422static void ssi_shutdown(aim_session_t *sess, aim_module_t *mod)
     1423{
     1424        aim_ssi_freelist(sess);
     1425
     1426        return;
     1427}
     1428
    1971429faim_internal int ssi_modfirst(aim_session_t *sess, aim_module_t *mod)
    1981430{
    1991431
    200         mod->family = 0x0013;
     1432        mod->family = AIM_CB_FAM_SSI;
    2011433        mod->version = 0x0001;
    2021434        mod->toolid = 0x0110;
     
    2051437        strncpy(mod->name, "ssi", sizeof(mod->name));
    2061438        mod->snachandler = snachandler;
    207 
    208         return 0;
    209 }
    210 
    211 
     1439        mod->shutdown = ssi_shutdown;
     1440
     1441        return 0;
     1442}
Note: See TracChangeset for help on using the changeset viewer.