source: protocols/oscar/ssi.c @ eb73d05

Last change on this file since eb73d05 was 81a15da, checked in by dequis <dx@…>, at 2015-05-07T23:12:06Z

oscar/ssi.c: fix "same on both sides" in condition

Fixes trac ticket 1196

  • Property mode set to 100644
File size: 35.9 KB
RevLine 
[b7d3cc34]1/*
2 * Server-Side/Stored Information.
3 *
4 * Relatively new facility that allows storing of certain types of information,
[5ebff60]5 * such as a users buddy list, permit/deny list, and permit/deny preferences,
[b7d3cc34]6 * to be stored on the server, so that they can be accessed from any client.
7 *
[5ebff60]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
[b7d3cc34]10 * near the top just manage the local data.
11 *
[5ebff60]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
[b7d3cc34]15 * rights (subtype 0x0003), then--well, you get the idea.
16 *
17 * 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
22 *
23 */
24
25#include <aim.h>
26#include "ssi.h"
27
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.
[5ebff60]32 * @param parent A pointer to the parent group, or NULL if the item should have no
[b7d3cc34]33 *        parent group (ie. the group ID# should be 0).
[5ebff60]34 * @param name A null terminated string of the name of the new item, or NULL if the
[b7d3cc34]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 */
[5ebff60]39static struct aim_ssi_item *aim_ssi_itemlist_add(struct aim_ssi_item **list, struct aim_ssi_item *parent, char *name,
40                                                 guint16 type)
[b7d3cc34]41{
42        int i;
43        struct aim_ssi_item *cur, *newitem;
44
[5ebff60]45        if (!(newitem = g_new0(struct aim_ssi_item, 1))) {
[b7d3cc34]46                return NULL;
[5ebff60]47        }
[b7d3cc34]48
49        /* Set the name */
50        if (name) {
[5ebff60]51                if (!(newitem->name = (char *) g_malloc((strlen(name) + 1) * sizeof(char)))) {
[b7d3cc34]52                        g_free(newitem);
53                        return NULL;
54                }
55                strcpy(newitem->name, name);
[5ebff60]56        } else {
[b7d3cc34]57                newitem->name = NULL;
[5ebff60]58        }
[b7d3cc34]59
60        /* Set the group ID# and the buddy ID# */
61        newitem->gid = 0x0000;
62        newitem->bid = 0x0000;
63        if (type == AIM_SSI_TYPE_GROUP) {
[5ebff60]64                if (name) {
[b7d3cc34]65                        do {
66                                newitem->gid += 0x0001;
[5ebff60]67                                for (cur = *list, i = 0; ((cur) && (!i)); cur = cur->next) {
[81a15da]68                                        if ((cur->bid == newitem->bid) && (cur->gid == newitem->gid)) {
[5ebff60]69                                                i = 1;
70                                        }
71                                }
[b7d3cc34]72                        } while (i);
[5ebff60]73                }
[b7d3cc34]74        } else {
[5ebff60]75                if (parent) {
[b7d3cc34]76                        newitem->gid = parent->gid;
[5ebff60]77                }
[b7d3cc34]78                do {
79                        newitem->bid += 0x0001;
[5ebff60]80                        for (cur = *list, i = 0; ((cur) && (!i)); cur = cur->next) {
81                                if ((cur->bid == newitem->bid) && (cur->gid == newitem->gid)) {
82                                        i = 1;
83                                }
84                        }
[b7d3cc34]85                } while (i);
86        }
87
88        /* Set the rest */
89        newitem->type = type;
90        newitem->data = NULL;
91        newitem->next = *list;
92        *list = newitem;
93
94        return newitem;
95}
96
97/**
98 * Locally rebuild the 0x00c8 TLV in the additional data of the given group.
99 *
100 * @param list A pointer to a pointer to the current list of items.
101 * @param parentgroup A pointer to the group who's additional data you want to rebuild.
102 * @return Return 0 if no errors, otherwise return the error number.
103 */
104static int aim_ssi_itemlist_rebuildgroup(struct aim_ssi_item **list, struct aim_ssi_item *parentgroup)
105{
106        int newlen; //, i;
107        struct aim_ssi_item *cur;
108
109        /* Free the old additional data */
110        if (parentgroup->data) {
[5ebff60]111                aim_freetlvchain((aim_tlvlist_t **) &parentgroup->data);
[b7d3cc34]112                parentgroup->data = NULL;
113        }
114
115        /* Find the length for the new additional data */
116        newlen = 0;
117        if (parentgroup->gid == 0x0000) {
[5ebff60]118                for (cur = *list; cur; cur = cur->next) {
119                        if ((cur->gid != 0x0000) && (cur->type == AIM_SSI_TYPE_GROUP)) {
[b7d3cc34]120                                newlen += 2;
[5ebff60]121                        }
122                }
[b7d3cc34]123        } else {
[5ebff60]124                for (cur = *list; cur; cur = cur->next) {
125                        if ((cur->gid == parentgroup->gid) && (cur->type == AIM_SSI_TYPE_BUDDY)) {
[b7d3cc34]126                                newlen += 2;
[5ebff60]127                        }
128                }
[b7d3cc34]129        }
130
131        /* Rebuild the additional data */
[5ebff60]132        if (newlen > 0) {
[b7d3cc34]133                guint8 *newdata;
134
[5ebff60]135                if (!(newdata = (guint8 *) g_malloc((newlen) * sizeof(guint8)))) {
[b7d3cc34]136                        return -ENOMEM;
[5ebff60]137                }
[b7d3cc34]138                newlen = 0;
139                if (parentgroup->gid == 0x0000) {
[5ebff60]140                        for (cur = *list; cur; cur = cur->next) {
141                                if ((cur->gid != 0x0000) && (cur->type == AIM_SSI_TYPE_GROUP)) {
142                                        newlen += aimutil_put16(newdata + newlen, cur->gid);
143                                }
144                        }
[b7d3cc34]145                } else {
[5ebff60]146                        for (cur = *list; cur; cur = cur->next) {
147                                if ((cur->gid == parentgroup->gid) && (cur->type == AIM_SSI_TYPE_BUDDY)) {
148                                        newlen += aimutil_put16(newdata + newlen, cur->bid);
149                                }
150                        }
[b7d3cc34]151                }
[5ebff60]152                aim_addtlvtochain_raw((aim_tlvlist_t **) &(parentgroup->data), 0x00c8, newlen, newdata);
[b7d3cc34]153
154                g_free(newdata);
155        }
156
157        return 0;
158}
159
160/**
161 * Locally free all of the stored buddy list information.
162 *
163 * @param sess The oscar session.
164 * @return Return 0 if no errors, otherwise return the error number.
165 */
166static int aim_ssi_freelist(aim_session_t *sess)
167{
168        struct aim_ssi_item *cur, *delitem;
169
170        cur = sess->ssi.items;
171        while (cur) {
[5ebff60]172                if (cur->name) {
173                        g_free(cur->name);
174                }
175                if (cur->data) {
176                        aim_freetlvchain((aim_tlvlist_t **) &cur->data);
177                }
[b7d3cc34]178                delitem = cur;
179                cur = cur->next;
180                g_free(delitem);
181        }
182
183        sess->ssi.items = NULL;
184        sess->ssi.revision = 0;
[5ebff60]185        sess->ssi.timestamp = (time_t) 0;
[b7d3cc34]186
187        return 0;
188}
189
190/**
191 * Locally find an item given a group ID# and a buddy ID#.
192 *
193 * @param list A pointer to the current list of items.
194 * @param gid The group ID# of the desired item.
195 * @param bid The buddy ID# of the desired item.
196 * @return Return a pointer to the item if found, else return NULL;
197 */
198struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_item *list, guint16 gid, guint16 bid)
199{
200        struct aim_ssi_item *cur;
[5ebff60]201
202        for (cur = list; cur; cur = cur->next) {
203                if ((cur->gid == gid) && (cur->bid == bid)) {
[b7d3cc34]204                        return cur;
[5ebff60]205                }
206        }
[b7d3cc34]207        return NULL;
208}
209
210/**
[5ebff60]211 * Locally find an item given a group name, screen name, and type.  If group name
[b7d3cc34]212 * and screen name are null, then just return the first item of the given type.
213 *
214 * @param list A pointer to the current list of items.
215 * @param gn The group name of the desired item.
216 * @param bn The buddy name of the desired item.
217 * @param type The type of the desired item.
218 * @return Return a pointer to the item if found, else return NULL;
219 */
220struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_item *list, char *gn, char *sn, guint16 type)
221{
222        struct aim_ssi_item *cur;
[5ebff60]223
224        if (!list) {
[b7d3cc34]225                return NULL;
[5ebff60]226        }
[b7d3cc34]227
228        if (gn && sn) { /* For finding buddies in groups */
[5ebff60]229                for (cur = list; cur; cur = cur->next) {
[b7d3cc34]230                        if ((cur->type == type) && (cur->name) && !(aim_sncmp(cur->name, sn))) {
231                                struct aim_ssi_item *curg;
[5ebff60]232                                for (curg = list; curg; curg = curg->next) {
233                                        if ((curg->type == AIM_SSI_TYPE_GROUP) && (curg->gid == cur->gid) &&
234                                            (curg->name) && !(aim_sncmp(curg->name, gn))) {
[b7d3cc34]235                                                return cur;
[5ebff60]236                                        }
237                                }
[b7d3cc34]238                        }
[5ebff60]239                }
[b7d3cc34]240
241        } else if (sn) { /* For finding groups, permits, denies, and ignores */
[5ebff60]242                for (cur = list; cur; cur = cur->next) {
243                        if ((cur->type == type) && (cur->name) && !(aim_sncmp(cur->name, sn))) {
[b7d3cc34]244                                return cur;
[5ebff60]245                        }
246                }
[b7d3cc34]247
[5ebff60]248                /* For stuff without names--permit deny setting, visibility mask, etc. */
249        } else { for (cur = list; cur; cur = cur->next) {
250                         if (cur->type == type) {
251                                 return cur;
252                         }
253                 }
[b7d3cc34]254        }
255
256        return NULL;
257}
258
259/**
260 * Locally find the parent item of the given buddy name.
261 *
262 * @param list A pointer to the current list of items.
263 * @param bn The buddy name of the desired item.
264 * @return Return a pointer to the item if found, else return NULL;
265 */
266struct aim_ssi_item *aim_ssi_itemlist_findparent(struct aim_ssi_item *list, char *sn)
267{
268        struct aim_ssi_item *cur, *curg;
[5ebff60]269
270        if (!list || !sn) {
[b7d3cc34]271                return NULL;
[5ebff60]272        }
273        if (!(cur = aim_ssi_itemlist_finditem(list, NULL, sn, AIM_SSI_TYPE_BUDDY))) {
[b7d3cc34]274                return NULL;
[5ebff60]275        }
276        for (curg = list; curg; curg = curg->next) {
277                if ((curg->type == AIM_SSI_TYPE_GROUP) && (curg->gid == cur->gid)) {
[b7d3cc34]278                        return curg;
[5ebff60]279                }
280        }
[b7d3cc34]281        return NULL;
282}
283
284/**
285 * Locally find the permit/deny setting item, and return the setting.
286 *
287 * @param list A pointer to the current list of items.
288 * @return Return the current SSI permit deny setting, or 0 if no setting was found.
289 */
290int aim_ssi_getpermdeny(struct aim_ssi_item *list)
291{
292        struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PDINFO);
[5ebff60]293
[b7d3cc34]294        if (cur) {
295                aim_tlvlist_t *tlvlist = cur->data;
296                if (tlvlist) {
297                        aim_tlv_t *tlv = aim_gettlv(tlvlist, 0x00ca, 1);
[5ebff60]298                        if (tlv && tlv->value) {
[b7d3cc34]299                                return aimutil_get8(tlv->value);
[5ebff60]300                        }
[b7d3cc34]301                }
302        }
303        return 0;
304}
305
306/**
[5ebff60]307 * Add the given packet to the holding queue.  We totally need to send SSI SNACs one at
308 * a time, so we have a local queue where packets get put before they are sent, and
[b7d3cc34]309 * then we send stuff one at a time, nice and orderly-like.
310 *
311 * @param sess The oscar session.
312 * @param conn The bos connection for this session.
313 * @param fr The newly created SNAC that you want to send.
314 * @return Return 0 if no errors, otherwise return the error number.
315 */
316static int aim_ssi_enqueue(aim_session_t *sess, aim_conn_t *conn, aim_frame_t *fr)
317{
318        aim_frame_t *cur;
319
[5ebff60]320        if (!sess || !conn || !fr) {
[b7d3cc34]321                return -EINVAL;
[5ebff60]322        }
[b7d3cc34]323
324        fr->next = NULL;
325        if (sess->ssi.holding_queue == NULL) {
326                sess->ssi.holding_queue = fr;
[5ebff60]327                if (!sess->ssi.waiting_for_ack) {
[b7d3cc34]328                        aim_ssi_modbegin(sess, conn);
[5ebff60]329                }
[b7d3cc34]330        } else {
[5ebff60]331                for (cur = sess->ssi.holding_queue; cur->next; cur = cur->next) {
332                        ;
333                }
[b7d3cc34]334                cur->next = fr;
335        }
336
337        return 0;
338}
339
340/**
[5ebff60]341 * Send the next SNAC from the holding queue.  This is called
342 * automatically when an ack from an add, mod, or del is received.
[b7d3cc34]343 * If the queue is empty, it sends the modend SNAC.
344 *
345 * @param sess The oscar session.
346 * @param conn The bos connection for this session.
347 * @return Return 0 if no errors, otherwise return the error number.
348 */
349static int aim_ssi_dispatch(aim_session_t *sess, aim_conn_t *conn)
350{
351        aim_frame_t *cur;
352
[5ebff60]353        if (!sess || !conn) {
[b7d3cc34]354                return -EINVAL;
[5ebff60]355        }
[b7d3cc34]356
357        if (!sess->ssi.waiting_for_ack) {
358                if (sess->ssi.holding_queue) {
359                        sess->ssi.waiting_for_ack = 1;
360                        cur = sess->ssi.holding_queue->next;
361                        sess->ssi.holding_queue->next = NULL;
362                        aim_tx_enqueue(sess, sess->ssi.holding_queue);
363                        sess->ssi.holding_queue = cur;
[5ebff60]364                } else {
[b7d3cc34]365                        aim_ssi_modend(sess, conn);
[5ebff60]366                }
[b7d3cc34]367        }
368
369        return 0;
370}
371
372/**
373 * Add an array of screen names to the given group.
374 *
375 * @param sess The oscar session.
376 * @param conn The bos connection for this session.
377 * @param gn The name of the group to which you want to add these names.
378 * @param sn An array of null terminated strings of the names you want to add.
379 * @param num The number of screen names you are adding (size of the sn array).
380 * @param flags 1 - Add with TLV(0x66)
381 * @return Return 0 if no errors, otherwise return the error number.
382 */
383int aim_ssi_addbuddies(aim_session_t *sess, aim_conn_t *conn, char *gn, char **sn, unsigned int num, unsigned int flags)
384{
385        struct aim_ssi_item *parentgroup, **newitems;
386        guint16 i;
387
[5ebff60]388        if (!sess || !conn || !gn || !sn || !num) {
[b7d3cc34]389                return -EINVAL;
[5ebff60]390        }
[b7d3cc34]391
392        /* Look up the parent group */
393        if (!(parentgroup = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, gn, AIM_SSI_TYPE_GROUP))) {
394                aim_ssi_addgroups(sess, conn, &gn, 1);
[5ebff60]395                if (!(parentgroup = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, gn, AIM_SSI_TYPE_GROUP))) {
[b7d3cc34]396                        return -ENOMEM;
[5ebff60]397                }
[b7d3cc34]398        }
399
400        /* Allocate an array of pointers to each of the new items */
[5ebff60]401        if (!(newitems = g_new0(struct aim_ssi_item *, num))) {
[b7d3cc34]402                return -ENOMEM;
[5ebff60]403        }
[b7d3cc34]404
405        /* Add items to the local list, and index them in the array */
[5ebff60]406        for (i = 0; i < num; i++) {
[b7d3cc34]407                if (!(newitems[i] = aim_ssi_itemlist_add(&sess->ssi.items, parentgroup, sn[i], AIM_SSI_TYPE_BUDDY))) {
408                        g_free(newitems);
409                        return -ENOMEM;
410                } else if (flags & 1) {
411                        aim_tlvlist_t *tl = NULL;
412                        aim_addtlvtochain_noval(&tl, 0x66);
413                        newitems[i]->data = tl;
414                }
[5ebff60]415        }
[b7d3cc34]416
417        /* Send the add item SNAC */
418        if ((i = aim_ssi_addmoddel(sess, conn, newitems, num, AIM_CB_SSI_ADD))) {
419                g_free(newitems);
420                return -i;
421        }
422
423        /* Free the array of pointers to each of the new items */
424        g_free(newitems);
425
426        /* Rebuild the additional data in the parent group */
[5ebff60]427        if ((i = aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, parentgroup))) {
[b7d3cc34]428                return i;
[5ebff60]429        }
[b7d3cc34]430
431        /* Send the mod item SNAC */
[5ebff60]432        if ((i = aim_ssi_addmoddel(sess, conn, &parentgroup, 1, AIM_CB_SSI_MOD))) {
[b7d3cc34]433                return i;
[5ebff60]434        }
[b7d3cc34]435
436        /* Begin sending SSI SNACs */
[5ebff60]437        if (!(i = aim_ssi_dispatch(sess, conn))) {
[b7d3cc34]438                return i;
[5ebff60]439        }
[b7d3cc34]440
441        return 0;
442}
443
444/**
[5ebff60]445 * Add the master group (the group containing all groups).  This is called by
[b7d3cc34]446 * aim_ssi_addgroups, if necessary.
447 *
448 * @param sess The oscar session.
449 * @param conn The bos connection for this session.
450 * @return Return 0 if no errors, otherwise return the error number.
451 */
452int aim_ssi_addmastergroup(aim_session_t *sess, aim_conn_t *conn)
453{
454        struct aim_ssi_item *newitem;
455
[5ebff60]456        if (!sess || !conn) {
[b7d3cc34]457                return -EINVAL;
[5ebff60]458        }
[b7d3cc34]459
460        /* Add the item to the local list, and keep a pointer to it */
[5ebff60]461        if (!(newitem = aim_ssi_itemlist_add(&sess->ssi.items, NULL, NULL, AIM_SSI_TYPE_GROUP))) {
[b7d3cc34]462                return -ENOMEM;
[5ebff60]463        }
[b7d3cc34]464
465        /* If there are any existing groups (technically there shouldn't be, but */
466        /* just in case) then add their group ID#'s to the additional data */
467        aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, newitem);
468
469        /* Send the add item SNAC */
470        aim_ssi_addmoddel(sess, conn, &newitem, 1, AIM_CB_SSI_ADD);
471
472        /* Begin sending SSI SNACs */
473        aim_ssi_dispatch(sess, conn);
474
475        return 0;
476}
477
478/**
479 * Add an array of groups to the list.
480 *
481 * @param sess The oscar session.
482 * @param conn The bos connection for this session.
483 * @param gn An array of null terminated strings of the names you want to add.
484 * @param num The number of groups names you are adding (size of the sn array).
485 * @return Return 0 if no errors, otherwise return the error number.
486 */
487int aim_ssi_addgroups(aim_session_t *sess, aim_conn_t *conn, char **gn, unsigned int num)
488{
489        struct aim_ssi_item *parentgroup, **newitems;
490        guint16 i;
491
[5ebff60]492        if (!sess || !conn || !gn || !num) {
[b7d3cc34]493                return -EINVAL;
[5ebff60]494        }
[b7d3cc34]495
496        /* Look up the parent group */
497        if (!(parentgroup = aim_ssi_itemlist_find(sess->ssi.items, 0, 0))) {
498                aim_ssi_addmastergroup(sess, conn);
[5ebff60]499                if (!(parentgroup = aim_ssi_itemlist_find(sess->ssi.items, 0, 0))) {
[b7d3cc34]500                        return -ENOMEM;
[5ebff60]501                }
[b7d3cc34]502        }
503
504        /* Allocate an array of pointers to each of the new items */
[5ebff60]505        if (!(newitems = g_new0(struct aim_ssi_item *, num))) {
[b7d3cc34]506                return -ENOMEM;
[5ebff60]507        }
[b7d3cc34]508
509        /* Add items to the local list, and index them in the array */
[5ebff60]510        for (i = 0; i < num; i++) {
[b7d3cc34]511                if (!(newitems[i] = aim_ssi_itemlist_add(&sess->ssi.items, parentgroup, gn[i], AIM_SSI_TYPE_GROUP))) {
512                        g_free(newitems);
513                        return -ENOMEM;
514                }
[5ebff60]515        }
[b7d3cc34]516
517        /* Send the add item SNAC */
518        if ((i = aim_ssi_addmoddel(sess, conn, newitems, num, AIM_CB_SSI_ADD))) {
519                g_free(newitems);
520                return -i;
521        }
522
523        /* Free the array of pointers to each of the new items */
524        g_free(newitems);
525
526        /* Rebuild the additional data in the parent group */
[5ebff60]527        if ((i = aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, parentgroup))) {
[b7d3cc34]528                return i;
[5ebff60]529        }
[b7d3cc34]530
531        /* Send the mod item SNAC */
[5ebff60]532        if ((i = aim_ssi_addmoddel(sess, conn, &parentgroup, 1, AIM_CB_SSI_MOD))) {
[b7d3cc34]533                return i;
[5ebff60]534        }
[b7d3cc34]535
536        /* Begin sending SSI SNACs */
[5ebff60]537        if (!(i = aim_ssi_dispatch(sess, conn))) {
[b7d3cc34]538                return i;
[5ebff60]539        }
[b7d3cc34]540
541        return 0;
542}
543
544/**
[5ebff60]545 * Add an array of a certain type of item to the list.  This can be used for
546 * permit buddies, deny buddies, ICQ's ignore buddies, and probably other
[b7d3cc34]547 * types, also.
548 *
549 * @param sess The oscar session.
550 * @param conn The bos connection for this session.
551 * @param sn An array of null terminated strings of the names you want to add.
552 * @param num The number of groups names you are adding (size of the sn array).
[5ebff60]553 * @param type The type of item you want to add.  See the AIM_SSI_TYPE_BLEH
[b7d3cc34]554 *        #defines in aim.h.
555 * @return Return 0 if no errors, otherwise return the error number.
556 */
557int aim_ssi_addpord(aim_session_t *sess, aim_conn_t *conn, char **sn, unsigned int num, guint16 type)
558{
559        struct aim_ssi_item **newitems;
560        guint16 i;
561
[5ebff60]562        if (!sess || !conn || !sn || !num) {
[b7d3cc34]563                return -EINVAL;
[5ebff60]564        }
[b7d3cc34]565
566        /* Allocate an array of pointers to each of the new items */
[5ebff60]567        if (!(newitems = g_new0(struct aim_ssi_item *, num))) {
[b7d3cc34]568                return -ENOMEM;
[5ebff60]569        }
[b7d3cc34]570
571        /* Add items to the local list, and index them in the array */
[5ebff60]572        for (i = 0; i < num; i++) {
[b7d3cc34]573                if (!(newitems[i] = aim_ssi_itemlist_add(&sess->ssi.items, NULL, sn[i], type))) {
574                        g_free(newitems);
575                        return -ENOMEM;
576                }
[5ebff60]577        }
[b7d3cc34]578
579        /* Send the add item SNAC */
580        if ((i = aim_ssi_addmoddel(sess, conn, newitems, num, AIM_CB_SSI_ADD))) {
581                g_free(newitems);
582                return -i;
583        }
584
585        /* Free the array of pointers to each of the new items */
586        g_free(newitems);
587
588        /* Begin sending SSI SNACs */
[5ebff60]589        if (!(i = aim_ssi_dispatch(sess, conn))) {
[b7d3cc34]590                return i;
[5ebff60]591        }
[b7d3cc34]592
593        return 0;
594}
595
596/**
[5ebff60]597 * Move a buddy from one group to another group.  This basically just deletes the
[b7d3cc34]598 * buddy and re-adds it.
599 *
600 * @param sess The oscar session.
601 * @param conn The bos connection for this session.
602 * @param oldgn The group that the buddy is currently in.
603 * @param newgn The group that the buddy should be moved in to.
604 * @param sn The name of the buddy to be moved.
605 * @return Return 0 if no errors, otherwise return the error number.
606 */
607int aim_ssi_movebuddy(aim_session_t *sess, aim_conn_t *conn, char *oldgn, char *newgn, char *sn)
608{
609        struct aim_ssi_item **groups, *buddy, *cur;
610        guint16 i;
611
[5ebff60]612        if (!sess || !conn || !oldgn || !newgn || !sn) {
[b7d3cc34]613                return -EINVAL;
[5ebff60]614        }
[b7d3cc34]615
616        /* Look up the buddy */
[5ebff60]617        if (!(buddy = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, sn, AIM_SSI_TYPE_BUDDY))) {
[b7d3cc34]618                return -ENOMEM;
[5ebff60]619        }
[b7d3cc34]620
621        /* Allocate an array of pointers to the two groups */
[5ebff60]622        if (!(groups = g_new0(struct aim_ssi_item *, 2))) {
[b7d3cc34]623                return -ENOMEM;
[5ebff60]624        }
[b7d3cc34]625
626        /* Look up the old parent group */
627        if (!(groups[0] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, oldgn, AIM_SSI_TYPE_GROUP))) {
628                g_free(groups);
629                return -ENOMEM;
630        }
631
632        /* Look up the new parent group */
633        if (!(groups[1] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, newgn, AIM_SSI_TYPE_GROUP))) {
634                g_free(groups);
635                return -ENOMEM;
636        }
637
638        /* Send the delete item SNAC */
639        aim_ssi_addmoddel(sess, conn, &buddy, 1, AIM_CB_SSI_DEL);
640
641        /* Put the buddy in the new group */
642        buddy->gid = groups[1]->gid;
643
644        /* Assign a new buddy ID#, because the new group might already have a buddy with this ID# */
645        buddy->bid = 0;
646        do {
647                buddy->bid += 0x0001;
[5ebff60]648                for (cur = sess->ssi.items, i = 0; ((cur) && (!i)); cur = cur->next) {
649                        if ((cur->bid == buddy->bid) && (cur->gid == buddy->gid) && (cur->type == AIM_SSI_TYPE_BUDDY) &&
650                            (cur->name) && aim_sncmp(cur->name, buddy->name)) {
651                                i = 1;
652                        }
653                }
[b7d3cc34]654        } while (i);
655
656        /* Rebuild the additional data in the two parent groups */
657        aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, groups[0]);
658        aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, groups[1]);
659
660        /* Send the add item SNAC */
661        aim_ssi_addmoddel(sess, conn, &buddy, 1, AIM_CB_SSI_ADD);
662
663        /* Send the mod item SNAC */
664        aim_ssi_addmoddel(sess, conn, groups, 2, AIM_CB_SSI_MOD);
665
666        /* Free the temporary array */
667        g_free(groups);
668
669        /* Begin sending SSI SNACs */
670        aim_ssi_dispatch(sess, conn);
671
672        return 0;
673}
674
675/**
676 * Delete an array of screen names from the given group.
677 *
678 * @param sess The oscar session.
679 * @param conn The bos connection for this session.
680 * @param gn The name of the group from which you want to delete these names.
681 * @param sn An array of null terminated strings of the names you want to delete.
682 * @param num The number of screen names you are deleting (size of the sn array).
683 * @return Return 0 if no errors, otherwise return the error number.
684 */
685int aim_ssi_delbuddies(aim_session_t *sess, aim_conn_t *conn, char *gn, char **sn, unsigned int num)
686{
687        struct aim_ssi_item *cur, *parentgroup, **delitems;
688        int i;
689
[5ebff60]690        if (!sess || !conn || !gn || !sn || !num) {
[b7d3cc34]691                return -EINVAL;
[5ebff60]692        }
[b7d3cc34]693
694        /* Look up the parent group */
[5ebff60]695        if (!(parentgroup = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, gn, AIM_SSI_TYPE_GROUP))) {
[b7d3cc34]696                return -EINVAL;
[5ebff60]697        }
[b7d3cc34]698
699        /* Allocate an array of pointers to each of the items to be deleted */
700        delitems = g_new0(struct aim_ssi_item *, num);
701
702        /* Make the delitems array a pointer to the aim_ssi_item structs to be deleted */
[5ebff60]703        for (i = 0; i < num; i++) {
[b7d3cc34]704                if (!(delitems[i] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, sn[i], AIM_SSI_TYPE_BUDDY))) {
705                        g_free(delitems);
706                        return -EINVAL;
707                }
708
709                /* Remove the delitems from the item list */
710                if (sess->ssi.items == delitems[i]) {
711                        sess->ssi.items = sess->ssi.items->next;
712                } else {
[5ebff60]713                        for (cur = sess->ssi.items; (cur->next && (cur->next != delitems[i])); cur = cur->next) {
714                                ;
715                        }
716                        if (cur->next) {
[b7d3cc34]717                                cur->next = cur->next->next;
[5ebff60]718                        }
[b7d3cc34]719                }
720        }
721
722        /* Send the del item SNAC */
723        aim_ssi_addmoddel(sess, conn, delitems, num, AIM_CB_SSI_DEL);
724
725        /* Free the items */
[5ebff60]726        for (i = 0; i < num; i++) {
727                if (delitems[i]->name) {
[b7d3cc34]728                        g_free(delitems[i]->name);
[5ebff60]729                }
730                if (delitems[i]->data) {
731                        aim_freetlvchain((aim_tlvlist_t **) &delitems[i]->data);
732                }
[b7d3cc34]733                g_free(delitems[i]);
734        }
735        g_free(delitems);
736
737        /* Rebuild the additional data in the parent group */
738        aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, parentgroup);
739
740        /* Send the mod item SNAC */
741        aim_ssi_addmoddel(sess, conn, &parentgroup, 1, AIM_CB_SSI_MOD);
742
743        /* Delete the group, but only if it's empty */
[5ebff60]744        if (!parentgroup->data) {
[b7d3cc34]745                aim_ssi_delgroups(sess, conn, &parentgroup->name, 1);
[5ebff60]746        }
[b7d3cc34]747
748        /* Begin sending SSI SNACs */
749        aim_ssi_dispatch(sess, conn);
750
751        return 0;
752}
753
754/**
755 * Delete the master group from the item list.  There can be only one.
756 * Er, so just find the one master group and delete it.
757 *
758 * @param sess The oscar session.
759 * @param conn The bos connection for this session.
760 * @return Return 0 if no errors, otherwise return the error number.
761 */
762int aim_ssi_delmastergroup(aim_session_t *sess, aim_conn_t *conn)
763{
764        struct aim_ssi_item *cur, *delitem;
765
[5ebff60]766        if (!sess || !conn) {
[b7d3cc34]767                return -EINVAL;
[5ebff60]768        }
[b7d3cc34]769
770        /* Make delitem a pointer to the aim_ssi_item to be deleted */
[5ebff60]771        if (!(delitem = aim_ssi_itemlist_find(sess->ssi.items, 0, 0))) {
[b7d3cc34]772                return -EINVAL;
[5ebff60]773        }
[b7d3cc34]774
775        /* Remove delitem from the item list */
776        if (sess->ssi.items == delitem) {
777                sess->ssi.items = sess->ssi.items->next;
778        } else {
[5ebff60]779                for (cur = sess->ssi.items; (cur->next && (cur->next != delitem)); cur = cur->next) {
780                        ;
781                }
782                if (cur->next) {
[b7d3cc34]783                        cur->next = cur->next->next;
[5ebff60]784                }
[b7d3cc34]785        }
786
787        /* Send the del item SNAC */
788        aim_ssi_addmoddel(sess, conn, &delitem, 1, AIM_CB_SSI_DEL);
789
790        /* Free the item */
[5ebff60]791        if (delitem->name) {
[b7d3cc34]792                g_free(delitem->name);
[5ebff60]793        }
794        if (delitem->data) {
795                aim_freetlvchain((aim_tlvlist_t **) &delitem->data);
796        }
[b7d3cc34]797        g_free(delitem);
798
799        /* Begin sending SSI SNACs */
800        aim_ssi_dispatch(sess, conn);
801
802        return 0;
803}
804
805/**
806 * Delete an array of groups.
807 *
808 * @param sess The oscar session.
809 * @param conn The bos connection for this session.
810 * @param gn An array of null terminated strings of the groups you want to delete.
811 * @param num The number of groups you are deleting (size of the gn array).
812 * @return Return 0 if no errors, otherwise return the error number.
813 */
[5ebff60]814int aim_ssi_delgroups(aim_session_t *sess, aim_conn_t *conn, char **gn, unsigned int num)
815{
[b7d3cc34]816        struct aim_ssi_item *cur, *parentgroup, **delitems;
817        int i;
818
[5ebff60]819        if (!sess || !conn || !gn || !num) {
[b7d3cc34]820                return -EINVAL;
[5ebff60]821        }
[b7d3cc34]822
823        /* Look up the parent group */
[5ebff60]824        if (!(parentgroup = aim_ssi_itemlist_find(sess->ssi.items, 0, 0))) {
[b7d3cc34]825                return -EINVAL;
[5ebff60]826        }
[b7d3cc34]827
828        /* Allocate an array of pointers to each of the items to be deleted */
829        delitems = g_new0(struct aim_ssi_item *, num);
830
831        /* Make the delitems array a pointer to the aim_ssi_item structs to be deleted */
[5ebff60]832        for (i = 0; i < num; i++) {
[b7d3cc34]833                if (!(delitems[i] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, gn[i], AIM_SSI_TYPE_GROUP))) {
834                        g_free(delitems);
835                        return -EINVAL;
836                }
837
838                /* Remove the delitems from the item list */
839                if (sess->ssi.items == delitems[i]) {
840                        sess->ssi.items = sess->ssi.items->next;
841                } else {
[5ebff60]842                        for (cur = sess->ssi.items; (cur->next && (cur->next != delitems[i])); cur = cur->next) {
843                                ;
844                        }
845                        if (cur->next) {
[b7d3cc34]846                                cur->next = cur->next->next;
[5ebff60]847                        }
[b7d3cc34]848                }
849        }
850
851        /* Send the del item SNAC */
852        aim_ssi_addmoddel(sess, conn, delitems, num, AIM_CB_SSI_DEL);
853
854        /* Free the items */
[5ebff60]855        for (i = 0; i < num; i++) {
856                if (delitems[i]->name) {
[b7d3cc34]857                        g_free(delitems[i]->name);
[5ebff60]858                }
859                if (delitems[i]->data) {
860                        aim_freetlvchain((aim_tlvlist_t **) &delitems[i]->data);
861                }
[b7d3cc34]862                g_free(delitems[i]);
863        }
864        g_free(delitems);
865
866        /* Rebuild the additional data in the parent group */
867        aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, parentgroup);
868
869        /* Send the mod item SNAC */
870        aim_ssi_addmoddel(sess, conn, &parentgroup, 1, AIM_CB_SSI_MOD);
871
872        /* Delete the group, but only if it's empty */
[5ebff60]873        if (!parentgroup->data) {
[b7d3cc34]874                aim_ssi_delmastergroup(sess, conn);
[5ebff60]875        }
[b7d3cc34]876
877        /* Begin sending SSI SNACs */
878        aim_ssi_dispatch(sess, conn);
879
880        return 0;
881}
882
883/**
[5ebff60]884 * Delete an array of a certain type of item from the list.  This can be
885 * used for permit buddies, deny buddies, ICQ's ignore buddies, and
[b7d3cc34]886 * probably other types, also.
887 *
888 * @param sess The oscar session.
889 * @param conn The bos connection for this session.
890 * @param sn An array of null terminated strings of the items you want to delete.
891 * @param num The number of items you are deleting (size of the sn array).
892 * @return Return 0 if no errors, otherwise return the error number.
893 */
[5ebff60]894int aim_ssi_delpord(aim_session_t *sess, aim_conn_t *conn, char **sn, unsigned int num, guint16 type)
895{
[b7d3cc34]896        struct aim_ssi_item *cur, **delitems;
897        int i;
898
[5ebff60]899        if (!sess || !conn || !sn || !num || (type != AIM_SSI_TYPE_PERMIT && type != AIM_SSI_TYPE_DENY)) {
[b7d3cc34]900                return -EINVAL;
[5ebff60]901        }
[b7d3cc34]902
903        /* Allocate an array of pointers to each of the items to be deleted */
904        delitems = g_new0(struct aim_ssi_item *, num);
905
906        /* Make the delitems array a pointer to the aim_ssi_item structs to be deleted */
[5ebff60]907        for (i = 0; i < num; i++) {
[b7d3cc34]908                if (!(delitems[i] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, sn[i], type))) {
909                        g_free(delitems);
910                        return -EINVAL;
911                }
912
913                /* Remove the delitems from the item list */
914                if (sess->ssi.items == delitems[i]) {
915                        sess->ssi.items = sess->ssi.items->next;
916                } else {
[5ebff60]917                        for (cur = sess->ssi.items; (cur->next && (cur->next != delitems[i])); cur = cur->next) {
918                                ;
919                        }
920                        if (cur->next) {
[b7d3cc34]921                                cur->next = cur->next->next;
[5ebff60]922                        }
[b7d3cc34]923                }
924        }
925
926        /* Send the del item SNAC */
927        aim_ssi_addmoddel(sess, conn, delitems, num, AIM_CB_SSI_DEL);
928
929        /* Free the items */
[5ebff60]930        for (i = 0; i < num; i++) {
931                if (delitems[i]->name) {
[b7d3cc34]932                        g_free(delitems[i]->name);
[5ebff60]933                }
934                if (delitems[i]->data) {
935                        aim_freetlvchain((aim_tlvlist_t **) &delitems[i]->data);
936                }
[b7d3cc34]937                g_free(delitems[i]);
938        }
939        g_free(delitems);
940
941        /* Begin sending SSI SNACs */
942        aim_ssi_dispatch(sess, conn);
943
944        return 0;
945}
946
947/*
948 * Request SSI Rights.
949 */
950int aim_ssi_reqrights(aim_session_t *sess, aim_conn_t *conn)
951{
952        return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_REQRIGHTS);
953}
954
955/*
956 * SSI Rights Information.
957 */
958static int parserights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
959{
960        int ret = 0;
961        aim_rxcallback_t userfunc;
962
[5ebff60]963        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) {
[b7d3cc34]964                ret = userfunc(sess, rx);
[5ebff60]965        }
[b7d3cc34]966
967        return ret;
968}
969
970int aim_ssi_reqalldata(aim_session_t *sess, aim_conn_t *conn)
971{
972        aim_frame_t *fr;
973        aim_snacid_t snacid;
974
[5ebff60]975        if (!sess || !conn) {
[b7d3cc34]976                return -EINVAL;
[5ebff60]977        }
[b7d3cc34]978
[5ebff60]979        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10))) {
[b7d3cc34]980                return -ENOMEM;
[5ebff60]981        }
[b7d3cc34]982
983        snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_REQFULLLIST, 0x0000, NULL, 0);
984
985        aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_REQFULLLIST, 0x0000, snacid);
986
987        aim_tx_enqueue(sess, fr);
988
989        return 0;
990}
991
992/*
993 * SSI Data.
994 */
995static int parsedata(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
996{
997        int ret = 0;
998        aim_rxcallback_t userfunc;
999        struct aim_ssi_item *cur = NULL;
1000        guint8 fmtver; /* guess */
1001        guint16 revision;
1002        guint32 timestamp;
1003
1004        /* When you set the version for the SSI family to 2-4, the beginning of this changes.
[5ebff60]1005         * Instead of the version and then the revision, there is "0x0006" and then a type
1006         * 0x0001 TLV containing the 2 byte SSI family version that you sent earlier.  Also,
1007         * the SNAC flags go from 0x0000 to 0x8000.  I guess the 0x0006 is the length of the
1008         * TLV(s) that follow.  The rights SNAC does the same thing, with the differing flag
[b7d3cc34]1009         * and everything.
1010         */
1011
1012        fmtver = aimbs_get8(bs); /* Version of ssi data.  Should be 0x00 */
1013        revision = aimbs_get16(bs); /* # of times ssi data has been modified */
[5ebff60]1014        if (revision != 0) {
[b7d3cc34]1015                sess->ssi.revision = revision;
[5ebff60]1016        }
[b7d3cc34]1017
[5ebff60]1018        for (cur = sess->ssi.items; cur && cur->next; cur = cur->next) {
1019                ;
1020        }
[b7d3cc34]1021
1022        while (aim_bstream_empty(bs) > 4) { /* last four bytes are stamp */
1023                guint16 namelen, tbslen;
1024
1025                if (!sess->ssi.items) {
[5ebff60]1026                        if (!(sess->ssi.items = g_new0(struct aim_ssi_item, 1))) {
[b7d3cc34]1027                                return -ENOMEM;
[5ebff60]1028                        }
[b7d3cc34]1029                        cur = sess->ssi.items;
1030                } else {
[5ebff60]1031                        if (!(cur->next = g_new0(struct aim_ssi_item, 1))) {
[b7d3cc34]1032                                return -ENOMEM;
[5ebff60]1033                        }
[b7d3cc34]1034                        cur = cur->next;
1035                }
1036
[5ebff60]1037                if ((namelen = aimbs_get16(bs))) {
[b7d3cc34]1038                        cur->name = aimbs_getstr(bs, namelen);
[5ebff60]1039                }
[b7d3cc34]1040                cur->gid = aimbs_get16(bs);
1041                cur->bid = aimbs_get16(bs);
1042                cur->type = aimbs_get16(bs);
1043
1044                if ((tbslen = aimbs_get16(bs))) {
1045                        aim_bstream_t tbs;
1046
1047                        aim_bstream_init(&tbs, bs->data + bs->offset /* XXX */, tbslen);
[5ebff60]1048                        cur->data = (void *) aim_readtlvchain(&tbs);
[b7d3cc34]1049                        aim_bstream_advance(bs, tbslen);
1050                }
1051        }
1052
1053        timestamp = aimbs_get32(bs);
[5ebff60]1054        if (timestamp != 0) {
[b7d3cc34]1055                sess->ssi.timestamp = timestamp;
[5ebff60]1056        }
[b7d3cc34]1057        sess->ssi.received_data = 1;
1058
[5ebff60]1059        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) {
[b7d3cc34]1060                ret = userfunc(sess, rx, fmtver, sess->ssi.revision, sess->ssi.timestamp, sess->ssi.items);
[5ebff60]1061        }
[b7d3cc34]1062
1063        return ret;
1064}
1065
1066/*
1067 * SSI Data Enable Presence.
1068 *
1069 * Should be sent after receiving 13/6 or 13/f to tell the server you
1070 * are ready to begin using the list.  It will promptly give you the
1071 * presence information for everyone in your list and put your permit/deny
1072 * settings into effect.
[5ebff60]1073 *
[b7d3cc34]1074 */
1075int aim_ssi_enable(aim_session_t *sess, aim_conn_t *conn)
1076{
1077        return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, 0x0007);
1078}
1079
1080/*
1081 * Stuff for SSI authorizations. The code used to work with the old im_ch4
1082 * messages, but those are supposed to be obsolete. This is probably
1083 * ICQ-specific.
1084 */
1085
1086/**
1087 * Request authorization to add someone to the server-side buddy list.
1088 *
1089 * @param sess The oscar session.
1090 * @param conn The bos connection for this session.
1091 * @param uin The contact's ICQ UIN.
1092 * @param reason The reason string to send with the request.
1093 * @return Return 0 if no errors, otherwise return the error number.
1094 */
[5ebff60]1095int aim_ssi_auth_request(aim_session_t *sess, aim_conn_t *conn, char *uin, char *reason)
[b7d3cc34]1096{
1097        aim_frame_t *fr;
1098        aim_snacid_t snacid;
1099        int snaclen;
[5ebff60]1100
1101        snaclen = 10 + 1 + strlen(uin) + 2 + strlen(reason) + 2;
1102
1103        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, snaclen))) {
[b7d3cc34]1104                return -ENOMEM;
[5ebff60]1105        }
1106
1107        snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREQ, 0x0000, NULL, 0);
1108        aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREQ, 0x0000, snacid);
[b7d3cc34]1109
[5ebff60]1110        aimbs_put8(&fr->data, strlen(uin));
1111        aimbs_putraw(&fr->data, (guint8 *) uin, strlen(uin));
1112        aimbs_put16(&fr->data, strlen(reason));
1113        aimbs_putraw(&fr->data, (guint8 *) reason, strlen(reason));
1114        aimbs_put16(&fr->data, 0);
1115
1116        aim_tx_enqueue(sess, fr);
1117
1118        return(0);
[b7d3cc34]1119}
1120
1121/**
1122 * Reply to an authorization request to add someone to the server-side buddy list.
1123 *
1124 * @param sess The oscar session.
1125 * @param conn The bos connection for this session.
1126 * @param uin The contact's ICQ UIN.
1127 * @param yesno 1 == Permit, 0 == Deny
1128 * @param reason The reason string to send with the request.
1129 * @return Return 0 if no errors, otherwise return the error number.
1130 */
[5ebff60]1131int aim_ssi_auth_reply(aim_session_t *sess, aim_conn_t *conn, char *uin, int yesno, char *reason)
[b7d3cc34]1132{
1133        aim_frame_t *fr;
1134        aim_snacid_t snacid;
1135        int snaclen;
[5ebff60]1136
1137        snaclen = 10 + 1 + strlen(uin) + 3 + strlen(reason);
1138
1139        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, snaclen))) {
[b7d3cc34]1140                return -ENOMEM;
[5ebff60]1141        }
1142
1143        snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREP, 0x0000, NULL, 0);
1144        aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREP, 0x0000, snacid);
1145
1146        aimbs_put8(&fr->data, strlen(uin));
1147        aimbs_putraw(&fr->data, (guint8 *) uin, strlen(uin));
1148        aimbs_put8(&fr->data, yesno);
1149        aimbs_put16(&fr->data, strlen(reason));
1150        aimbs_putraw(&fr->data, (guint8 *) reason, strlen(reason));
1151
1152        aim_tx_enqueue(sess, fr);
1153
1154        return(0);
[b7d3cc34]1155}
1156
1157
1158/*
1159 * SSI Add/Mod/Del Item(s).
1160 *
1161 * Sends the SNAC to add, modify, or delete an item from the server-stored
1162 * information.  These 3 SNACs all have an identical structure.  The only
1163 * difference is the subtype that is set for the SNAC.
[5ebff60]1164 *
[b7d3cc34]1165 */
[5ebff60]1166int aim_ssi_addmoddel(aim_session_t *sess, aim_conn_t *conn, struct aim_ssi_item **items, unsigned int num,
1167                      guint16 subtype)
[b7d3cc34]1168{
1169        aim_frame_t *fr;
1170        aim_snacid_t snacid;
1171        int i, snaclen, listlen;
1172        char *list = NULL;
1173
[5ebff60]1174        if (!sess || !conn || !items || !num) {
[b7d3cc34]1175                return -EINVAL;
[5ebff60]1176        }
[b7d3cc34]1177
1178        snaclen = 10; /* For family, subtype, flags, and SNAC ID */
1179        listlen = 0;
[5ebff60]1180        for (i = 0; i < num; i++) {
[b7d3cc34]1181                snaclen += 10; /* For length, GID, BID, type, and length */
1182                if (items[i]->name) {
1183                        snaclen += strlen(items[i]->name);
[5ebff60]1184
[b7d3cc34]1185                        if (subtype == AIM_CB_SSI_ADD) {
1186                                list = g_realloc(list, listlen + strlen(items[i]->name) + 1);
1187                                strcpy(list + listlen, items[i]->name);
1188                                listlen += strlen(items[i]->name) + 1;
1189                        }
1190                } else {
1191                        if (subtype == AIM_CB_SSI_ADD) {
1192                                list = g_realloc(list, listlen + 1);
1193                                list[listlen] = '\0';
[5ebff60]1194                                listlen++;
[b7d3cc34]1195                        }
1196                }
[5ebff60]1197                if (items[i]->data) {
1198                        snaclen += aim_sizetlvchain((aim_tlvlist_t **) &items[i]->data);
1199                }
[b7d3cc34]1200        }
[5ebff60]1201
1202        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, snaclen))) {
[b7d3cc34]1203                return -ENOMEM;
[5ebff60]1204        }
[b7d3cc34]1205
1206        snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, subtype, 0x0000, list, list ? listlen : 0);
1207        aim_putsnac(&fr->data, AIM_CB_FAM_SSI, subtype, 0x0000, snacid);
[5ebff60]1208
[b7d3cc34]1209        g_free(list);
1210
[5ebff60]1211        for (i = 0; i < num; i++) {
[b7d3cc34]1212                aimbs_put16(&fr->data, items[i]->name ? strlen(items[i]->name) : 0);
[5ebff60]1213                if (items[i]->name) {
1214                        aimbs_putraw(&fr->data, (guint8 *) items[i]->name, strlen(items[i]->name));
1215                }
[b7d3cc34]1216                aimbs_put16(&fr->data, items[i]->gid);
1217                aimbs_put16(&fr->data, items[i]->bid);
1218                aimbs_put16(&fr->data, items[i]->type);
[5ebff60]1219                aimbs_put16(&fr->data, items[i]->data ? aim_sizetlvchain((aim_tlvlist_t **) &items[i]->data) : 0);
1220                if (items[i]->data) {
1221                        aim_writetlvchain(&fr->data, (aim_tlvlist_t **) &items[i]->data);
1222                }
[b7d3cc34]1223        }
1224
1225        aim_ssi_enqueue(sess, conn, fr);
1226
1227        return 0;
1228}
1229
1230/*
1231 * SSI Add/Mod/Del Ack.
1232 *
1233 * Response to add, modify, or delete SNAC (sent with aim_ssi_addmoddel).
1234 *
1235 */
1236static int parseack(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
1237{
1238        int ret = 0;
1239        aim_rxcallback_t userfunc;
1240        aim_snac_t *origsnac;
1241
1242        sess->ssi.waiting_for_ack = 0;
1243        aim_ssi_dispatch(sess, rx->conn);
[5ebff60]1244
[b7d3cc34]1245        origsnac = aim_remsnac(sess, snac->id);
[5ebff60]1246
1247        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) {
[b7d3cc34]1248                ret = userfunc(sess, rx, origsnac);
[5ebff60]1249        }
1250
[b7d3cc34]1251        if (origsnac) {
1252                g_free(origsnac->data);
1253                g_free(origsnac);
1254        }
[5ebff60]1255
[b7d3cc34]1256        return ret;
1257}
1258
1259/*
1260 * SSI Begin Data Modification.
1261 *
1262 * Tells the server you're going to start modifying data.
[5ebff60]1263 *
[b7d3cc34]1264 */
1265int aim_ssi_modbegin(aim_session_t *sess, aim_conn_t *conn)
1266{
1267        return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_EDITSTART);
1268}
1269
1270/*
1271 * SSI End Data Modification.
1272 *
1273 * Tells the server you're done modifying data.
1274 *
1275 */
1276int aim_ssi_modend(aim_session_t *sess, aim_conn_t *conn)
1277{
1278        return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_EDITSTOP);
1279}
1280
1281/*
1282 * SSI Data Unchanged.
1283 *
1284 * Response to aim_ssi_reqdata() if the server-side data is not newer than
1285 * posted local stamp/revision.
1286 *
1287 */
[5ebff60]1288static int parsedataunchanged(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac,
1289                              aim_bstream_t *bs)
[b7d3cc34]1290{
1291        int ret = 0;
1292        aim_rxcallback_t userfunc;
1293
1294        sess->ssi.received_data = 1;
1295
[5ebff60]1296        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) {
[b7d3cc34]1297                ret = userfunc(sess, rx);
[5ebff60]1298        }
[b7d3cc34]1299
1300        return ret;
1301}
1302
1303static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
1304{
1305
[5ebff60]1306        if (snac->subtype == AIM_CB_SSI_RIGHTSINFO) {
[b7d3cc34]1307                return parserights(sess, mod, rx, snac, bs);
[5ebff60]1308        } else if (snac->subtype == AIM_CB_SSI_LIST) {
[b7d3cc34]1309                return parsedata(sess, mod, rx, snac, bs);
[5ebff60]1310        } else if (snac->subtype == AIM_CB_SSI_SRVACK) {
[b7d3cc34]1311                return parseack(sess, mod, rx, snac, bs);
[5ebff60]1312        } else if (snac->subtype == AIM_CB_SSI_NOLIST) {
[b7d3cc34]1313                return parsedataunchanged(sess, mod, rx, snac, bs);
[5ebff60]1314        }
[b7d3cc34]1315
1316        return 0;
1317}
1318
1319static void ssi_shutdown(aim_session_t *sess, aim_module_t *mod)
1320{
1321        aim_ssi_freelist(sess);
1322
1323        return;
1324}
1325
1326int ssi_modfirst(aim_session_t *sess, aim_module_t *mod)
1327{
1328
1329        mod->family = AIM_CB_FAM_SSI;
1330        mod->version = 0x0003;
1331        mod->toolid = 0x0110;
1332        mod->toolversion = 0x0629;
1333        mod->flags = 0;
1334        strncpy(mod->name, "ssi", sizeof(mod->name));
1335        mod->snachandler = snachandler;
1336        mod->shutdown = ssi_shutdown;
1337
1338        return 0;
1339}
Note: See TracBrowser for help on using the repository browser.