source: protocols/oscar/ssi.c @ 4c225f0

Last change on this file since 4c225f0 was fef7813, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-09-05T11:31:22Z

Fix compiler warnings. Also fixing irc_send_motd(), which so far got away
with a horrible practice of reading the MOTD file one by one.

  • Property mode set to 100644
File size: 44.7 KB
Line 
1/*
2 * Server-Side/Stored Information.
3 *
4 * Relatively new facility that allows storing of certain types of information,
5 * such as a users buddy list, permit/deny list, and permit/deny preferences,
6 * to be stored on the server, so that they can be accessed from any client.
7 *
8 * We keep a copy of the ssi data in sess->ssi, because the data needs to be
9 * accessed for various reasons.  So all the "aim_ssi_itemlist_bleh" functions
10 * near the top just manage the local data.
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 *
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.
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, char *name, guint16 type)
40{
41        int i;
42        struct aim_ssi_item *cur, *newitem;
43
44        if (!(newitem = g_new0(struct aim_ssi_item, 1)))
45                return NULL;
46
47        /* Set the name */
48        if (name) {
49                if (!(newitem->name = (char *)g_malloc((strlen(name)+1)*sizeof(char)))) {
50                        g_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; //, i;
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                guint8 *newdata;
121
122                if (!(newdata = (guint8 *)g_malloc((newlen)*sizeof(guint8))))
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                g_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)  g_free(cur->name);
155                if (cur->data)  aim_freetlvchain((aim_tlvlist_t **)&cur->data);
156                delitem = cur;
157                cur = cur->next;
158                g_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 */
176struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_item *list, guint16 gid, guint16 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 */
195struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_item *list, char *gn, char *sn, guint16 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 */
231struct 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 */
250int 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 */
272guint32 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 */
354int 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 = g_new0(struct aim_ssi_item *, num)))
363                return -ENOMEM;
364
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        g_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 */
390int 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*)g_malloc(strlen("Unknown")+1);
418                        strcpy(newgroup, "Unknown");
419                        aim_ssi_addgroups(sess, conn, &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 = g_new0(struct aim_ssi_item *, i)))
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                g_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 = g_new0(struct aim_ssi_item *, i)))
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                g_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 * @param flags 1 - Add with TLV(0x66)
492 * @return Return 0 if no errors, otherwise return the error number.
493 */
494int aim_ssi_addbuddies(aim_session_t *sess, aim_conn_t *conn, char *gn, char **sn, unsigned int num, unsigned int flags)
495{
496        struct aim_ssi_item *parentgroup, **newitems;
497        guint16 i;
498
499        if (!sess || !conn || !gn || !sn || !num)
500                return -EINVAL;
501
502        /* Look up the parent group */
503        if (!(parentgroup = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, gn, AIM_SSI_TYPE_GROUP))) {
504                aim_ssi_addgroups(sess, conn, &gn, 1);
505                if (!(parentgroup = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, gn, AIM_SSI_TYPE_GROUP)))
506                        return -ENOMEM;
507        }
508
509        /* Allocate an array of pointers to each of the new items */
510        if (!(newitems = g_new0(struct aim_ssi_item *, num)))
511                return -ENOMEM;
512
513        /* Add items to the local list, and index them in the array */
514        for (i=0; i<num; i++)
515                if (!(newitems[i] = aim_ssi_itemlist_add(&sess->ssi.items, parentgroup, sn[i], AIM_SSI_TYPE_BUDDY))) {
516                        g_free(newitems);
517                        return -ENOMEM;
518                } else if (flags & 1) {
519                        aim_tlvlist_t *tl = NULL;
520                        aim_addtlvtochain_noval(&tl, 0x66);
521                        newitems[i]->data = tl;
522                }
523
524        /* Send the add item SNAC */
525        if ((i = aim_ssi_addmoddel(sess, conn, newitems, num, AIM_CB_SSI_ADD))) {
526                g_free(newitems);
527                return -i;
528        }
529
530        /* Free the array of pointers to each of the new items */
531        g_free(newitems);
532
533        /* Rebuild the additional data in the parent group */
534        if ((i = aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, parentgroup)))
535                return i;
536
537        /* Send the mod item SNAC */
538        if ((i = aim_ssi_addmoddel(sess, conn, &parentgroup, 1, AIM_CB_SSI_MOD )))
539                return i;
540
541        /* Begin sending SSI SNACs */
542        if (!(i = aim_ssi_dispatch(sess, conn)))
543                return i;
544
545        return 0;
546}
547
548/**
549 * Add the master group (the group containing all groups).  This is called by
550 * aim_ssi_addgroups, if necessary.
551 *
552 * @param sess The oscar session.
553 * @param conn The bos connection for this session.
554 * @return Return 0 if no errors, otherwise return the error number.
555 */
556int aim_ssi_addmastergroup(aim_session_t *sess, aim_conn_t *conn)
557{
558        struct aim_ssi_item *newitem;
559
560        if (!sess || !conn)
561                return -EINVAL;
562
563        /* Add the item to the local list, and keep a pointer to it */
564        if (!(newitem = aim_ssi_itemlist_add(&sess->ssi.items, NULL, NULL, AIM_SSI_TYPE_GROUP)))
565                return -ENOMEM;
566
567        /* If there are any existing groups (technically there shouldn't be, but */
568        /* just in case) then add their group ID#'s to the additional data */
569        aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, newitem);
570
571        /* Send the add item SNAC */
572        aim_ssi_addmoddel(sess, conn, &newitem, 1, AIM_CB_SSI_ADD);
573
574        /* Begin sending SSI SNACs */
575        aim_ssi_dispatch(sess, conn);
576
577        return 0;
578}
579
580/**
581 * Add an array of groups to the list.
582 *
583 * @param sess The oscar session.
584 * @param conn The bos connection for this session.
585 * @param gn An array of null terminated strings of the names you want to add.
586 * @param num The number of groups names you are adding (size of the sn array).
587 * @return Return 0 if no errors, otherwise return the error number.
588 */
589int aim_ssi_addgroups(aim_session_t *sess, aim_conn_t *conn, char **gn, unsigned int num)
590{
591        struct aim_ssi_item *parentgroup, **newitems;
592        guint16 i;
593
594        if (!sess || !conn || !gn || !num)
595                return -EINVAL;
596
597        /* Look up the parent group */
598        if (!(parentgroup = aim_ssi_itemlist_find(sess->ssi.items, 0, 0))) {
599                aim_ssi_addmastergroup(sess, conn);
600                if (!(parentgroup = aim_ssi_itemlist_find(sess->ssi.items, 0, 0)))
601                        return -ENOMEM;
602        }
603
604        /* Allocate an array of pointers to each of the new items */
605        if (!(newitems = g_new0(struct aim_ssi_item *, num)))
606                return -ENOMEM;
607
608        /* Add items to the local list, and index them in the array */
609        for (i=0; i<num; i++)
610                if (!(newitems[i] = aim_ssi_itemlist_add(&sess->ssi.items, parentgroup, gn[i], AIM_SSI_TYPE_GROUP))) {
611                        g_free(newitems);
612                        return -ENOMEM;
613                }
614
615        /* Send the add item SNAC */
616        if ((i = aim_ssi_addmoddel(sess, conn, newitems, num, AIM_CB_SSI_ADD))) {
617                g_free(newitems);
618                return -i;
619        }
620
621        /* Free the array of pointers to each of the new items */
622        g_free(newitems);
623
624        /* Rebuild the additional data in the parent group */
625        if ((i = aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, parentgroup)))
626                return i;
627
628        /* Send the mod item SNAC */
629        if ((i = aim_ssi_addmoddel(sess, conn, &parentgroup, 1, AIM_CB_SSI_MOD)))
630                return i;
631
632        /* Begin sending SSI SNACs */
633        if (!(i = aim_ssi_dispatch(sess, conn)))
634                return i;
635
636        return 0;
637}
638
639/**
640 * Add an array of a certain type of item to the list.  This can be used for
641 * permit buddies, deny buddies, ICQ's ignore buddies, and probably other
642 * types, also.
643 *
644 * @param sess The oscar session.
645 * @param conn The bos connection for this session.
646 * @param sn An array of null terminated strings of the names you want to add.
647 * @param num The number of groups names you are adding (size of the sn array).
648 * @param type The type of item you want to add.  See the AIM_SSI_TYPE_BLEH
649 *        #defines in aim.h.
650 * @return Return 0 if no errors, otherwise return the error number.
651 */
652int aim_ssi_addpord(aim_session_t *sess, aim_conn_t *conn, char **sn, unsigned int num, guint16 type)
653{
654        struct aim_ssi_item **newitems;
655        guint16 i;
656
657        if (!sess || !conn || !sn || !num)
658                return -EINVAL;
659
660        /* Allocate an array of pointers to each of the new items */
661        if (!(newitems = g_new0(struct aim_ssi_item *, num)))
662                return -ENOMEM;
663
664        /* Add items to the local list, and index them in the array */
665        for (i=0; i<num; i++)
666                if (!(newitems[i] = aim_ssi_itemlist_add(&sess->ssi.items, NULL, sn[i], type))) {
667                        g_free(newitems);
668                        return -ENOMEM;
669                }
670
671        /* Send the add item SNAC */
672        if ((i = aim_ssi_addmoddel(sess, conn, newitems, num, AIM_CB_SSI_ADD))) {
673                g_free(newitems);
674                return -i;
675        }
676
677        /* Free the array of pointers to each of the new items */
678        g_free(newitems);
679
680        /* Begin sending SSI SNACs */
681        if (!(i = aim_ssi_dispatch(sess, conn)))
682                return i;
683
684        return 0;
685}
686
687/**
688 * Move a buddy from one group to another group.  This basically just deletes the
689 * buddy and re-adds it.
690 *
691 * @param sess The oscar session.
692 * @param conn The bos connection for this session.
693 * @param oldgn The group that the buddy is currently in.
694 * @param newgn The group that the buddy should be moved in to.
695 * @param sn The name of the buddy to be moved.
696 * @return Return 0 if no errors, otherwise return the error number.
697 */
698int aim_ssi_movebuddy(aim_session_t *sess, aim_conn_t *conn, char *oldgn, char *newgn, char *sn)
699{
700        struct aim_ssi_item **groups, *buddy, *cur;
701        guint16 i;
702
703        if (!sess || !conn || !oldgn || !newgn || !sn)
704                return -EINVAL;
705
706        /* Look up the buddy */
707        if (!(buddy = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, sn, AIM_SSI_TYPE_BUDDY)))
708                return -ENOMEM;
709
710        /* Allocate an array of pointers to the two groups */
711        if (!(groups = g_new0(struct aim_ssi_item *, 2)))
712                return -ENOMEM;
713
714        /* Look up the old parent group */
715        if (!(groups[0] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, oldgn, AIM_SSI_TYPE_GROUP))) {
716                g_free(groups);
717                return -ENOMEM;
718        }
719
720        /* Look up the new parent group */
721        if (!(groups[1] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, newgn, AIM_SSI_TYPE_GROUP))) {
722                g_free(groups);
723                return -ENOMEM;
724        }
725
726        /* Send the delete item SNAC */
727        aim_ssi_addmoddel(sess, conn, &buddy, 1, AIM_CB_SSI_DEL);
728
729        /* Put the buddy in the new group */
730        buddy->gid = groups[1]->gid;
731
732        /* Assign a new buddy ID#, because the new group might already have a buddy with this ID# */
733        buddy->bid = 0;
734        do {
735                buddy->bid += 0x0001;
736                for (cur=sess->ssi.items, i=0; ((cur) && (!i)); cur=cur->next)
737                        if ((cur->bid == buddy->bid) && (cur->gid == buddy->gid) && (cur->type == AIM_SSI_TYPE_BUDDY) && (cur->name) && aim_sncmp(cur->name, buddy->name))
738                                i=1;
739        } while (i);
740
741        /* Rebuild the additional data in the two parent groups */
742        aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, groups[0]);
743        aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, groups[1]);
744
745        /* Send the add item SNAC */
746        aim_ssi_addmoddel(sess, conn, &buddy, 1, AIM_CB_SSI_ADD);
747
748        /* Send the mod item SNAC */
749        aim_ssi_addmoddel(sess, conn, groups, 2, AIM_CB_SSI_MOD);
750
751        /* Free the temporary array */
752        g_free(groups);
753
754        /* Begin sending SSI SNACs */
755        aim_ssi_dispatch(sess, conn);
756
757        return 0;
758}
759
760/**
761 * Delete an array of screen names from the given group.
762 *
763 * @param sess The oscar session.
764 * @param conn The bos connection for this session.
765 * @param gn The name of the group from which you want to delete these names.
766 * @param sn An array of null terminated strings of the names you want to delete.
767 * @param num The number of screen names you are deleting (size of the sn array).
768 * @return Return 0 if no errors, otherwise return the error number.
769 */
770int aim_ssi_delbuddies(aim_session_t *sess, aim_conn_t *conn, char *gn, char **sn, unsigned int num)
771{
772        struct aim_ssi_item *cur, *parentgroup, **delitems;
773        int i;
774
775        if (!sess || !conn || !gn || !sn || !num)
776                return -EINVAL;
777
778        /* Look up the parent group */
779        if (!(parentgroup = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, gn, AIM_SSI_TYPE_GROUP)))
780                return -EINVAL;
781
782        /* Allocate an array of pointers to each of the items to be deleted */
783        delitems = g_new0(struct aim_ssi_item *, num);
784
785        /* Make the delitems array a pointer to the aim_ssi_item structs to be deleted */
786        for (i=0; i<num; i++) {
787                if (!(delitems[i] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, sn[i], AIM_SSI_TYPE_BUDDY))) {
788                        g_free(delitems);
789                        return -EINVAL;
790                }
791
792                /* Remove the delitems from the item list */
793                if (sess->ssi.items == delitems[i]) {
794                        sess->ssi.items = sess->ssi.items->next;
795                } else {
796                        for (cur=sess->ssi.items; (cur->next && (cur->next!=delitems[i])); cur=cur->next);
797                        if (cur->next)
798                                cur->next = cur->next->next;
799                }
800        }
801
802        /* Send the del item SNAC */
803        aim_ssi_addmoddel(sess, conn, delitems, num, AIM_CB_SSI_DEL);
804
805        /* Free the items */
806        for (i=0; i<num; i++) {
807                if (delitems[i]->name)
808                        g_free(delitems[i]->name);
809                if (delitems[i]->data)
810                        aim_freetlvchain((aim_tlvlist_t **)&delitems[i]->data);
811                g_free(delitems[i]);
812        }
813        g_free(delitems);
814
815        /* Rebuild the additional data in the parent group */
816        aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, parentgroup);
817
818        /* Send the mod item SNAC */
819        aim_ssi_addmoddel(sess, conn, &parentgroup, 1, AIM_CB_SSI_MOD);
820
821        /* Delete the group, but only if it's empty */
822        if (!parentgroup->data)
823                aim_ssi_delgroups(sess, conn, &parentgroup->name, 1);
824
825        /* Begin sending SSI SNACs */
826        aim_ssi_dispatch(sess, conn);
827
828        return 0;
829}
830
831/**
832 * Delete the master group from the item list.  There can be only one.
833 * Er, so just find the one master group and delete it.
834 *
835 * @param sess The oscar session.
836 * @param conn The bos connection for this session.
837 * @return Return 0 if no errors, otherwise return the error number.
838 */
839int aim_ssi_delmastergroup(aim_session_t *sess, aim_conn_t *conn)
840{
841        struct aim_ssi_item *cur, *delitem;
842
843        if (!sess || !conn)
844                return -EINVAL;
845
846        /* Make delitem a pointer to the aim_ssi_item to be deleted */
847        if (!(delitem = aim_ssi_itemlist_find(sess->ssi.items, 0, 0)))
848                return -EINVAL;
849
850        /* Remove delitem from the item list */
851        if (sess->ssi.items == delitem) {
852                sess->ssi.items = sess->ssi.items->next;
853        } else {
854                for (cur=sess->ssi.items; (cur->next && (cur->next!=delitem)); cur=cur->next);
855                if (cur->next)
856                        cur->next = cur->next->next;
857        }
858
859        /* Send the del item SNAC */
860        aim_ssi_addmoddel(sess, conn, &delitem, 1, AIM_CB_SSI_DEL);
861
862        /* Free the item */
863        if (delitem->name)
864                g_free(delitem->name);
865        if (delitem->data)
866                aim_freetlvchain((aim_tlvlist_t **)&delitem->data);
867        g_free(delitem);
868
869        /* Begin sending SSI SNACs */
870        aim_ssi_dispatch(sess, conn);
871
872        return 0;
873}
874
875/**
876 * Delete an array of groups.
877 *
878 * @param sess The oscar session.
879 * @param conn The bos connection for this session.
880 * @param gn An array of null terminated strings of the groups you want to delete.
881 * @param num The number of groups you are deleting (size of the gn array).
882 * @return Return 0 if no errors, otherwise return the error number.
883 */
884int aim_ssi_delgroups(aim_session_t *sess, aim_conn_t *conn, char **gn, unsigned int num) {
885        struct aim_ssi_item *cur, *parentgroup, **delitems;
886        int i;
887
888        if (!sess || !conn || !gn || !num)
889                return -EINVAL;
890
891        /* Look up the parent group */
892        if (!(parentgroup = aim_ssi_itemlist_find(sess->ssi.items, 0, 0)))
893                return -EINVAL;
894
895        /* Allocate an array of pointers to each of the items to be deleted */
896        delitems = g_new0(struct aim_ssi_item *, num);
897
898        /* Make the delitems array a pointer to the aim_ssi_item structs to be deleted */
899        for (i=0; i<num; i++) {
900                if (!(delitems[i] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, gn[i], AIM_SSI_TYPE_GROUP))) {
901                        g_free(delitems);
902                        return -EINVAL;
903                }
904
905                /* Remove the delitems from the item list */
906                if (sess->ssi.items == delitems[i]) {
907                        sess->ssi.items = sess->ssi.items->next;
908                } else {
909                        for (cur=sess->ssi.items; (cur->next && (cur->next!=delitems[i])); cur=cur->next);
910                        if (cur->next)
911                                cur->next = cur->next->next;
912                }
913        }
914
915        /* Send the del item SNAC */
916        aim_ssi_addmoddel(sess, conn, delitems, num, AIM_CB_SSI_DEL);
917
918        /* Free the items */
919        for (i=0; i<num; i++) {
920                if (delitems[i]->name)
921                        g_free(delitems[i]->name);
922                if (delitems[i]->data)
923                        aim_freetlvchain((aim_tlvlist_t **)&delitems[i]->data);
924                g_free(delitems[i]);
925        }
926        g_free(delitems);
927
928        /* Rebuild the additional data in the parent group */
929        aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, parentgroup);
930
931        /* Send the mod item SNAC */
932        aim_ssi_addmoddel(sess, conn, &parentgroup, 1, AIM_CB_SSI_MOD);
933
934        /* Delete the group, but only if it's empty */
935        if (!parentgroup->data)
936                aim_ssi_delmastergroup(sess, conn);
937
938        /* Begin sending SSI SNACs */
939        aim_ssi_dispatch(sess, conn);
940
941        return 0;
942}
943
944/**
945 * Delete an array of a certain type of item from the list.  This can be
946 * used for permit buddies, deny buddies, ICQ's ignore buddies, and
947 * probably other types, also.
948 *
949 * @param sess The oscar session.
950 * @param conn The bos connection for this session.
951 * @param sn An array of null terminated strings of the items you want to delete.
952 * @param num The number of items you are deleting (size of the sn array).
953 * @return Return 0 if no errors, otherwise return the error number.
954 */
955int aim_ssi_delpord(aim_session_t *sess, aim_conn_t *conn, char **sn, unsigned int num, guint16 type) {
956        struct aim_ssi_item *cur, **delitems;
957        int i;
958
959        if (!sess || !conn || !sn || !num || (type!=AIM_SSI_TYPE_PERMIT && type!=AIM_SSI_TYPE_DENY))
960                return -EINVAL;
961
962        /* Allocate an array of pointers to each of the items to be deleted */
963        delitems = g_new0(struct aim_ssi_item *, num);
964
965        /* Make the delitems array a pointer to the aim_ssi_item structs to be deleted */
966        for (i=0; i<num; i++) {
967                if (!(delitems[i] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, sn[i], type))) {
968                        g_free(delitems);
969                        return -EINVAL;
970                }
971
972                /* Remove the delitems from the item list */
973                if (sess->ssi.items == delitems[i]) {
974                        sess->ssi.items = sess->ssi.items->next;
975                } else {
976                        for (cur=sess->ssi.items; (cur->next && (cur->next!=delitems[i])); cur=cur->next);
977                        if (cur->next)
978                                cur->next = cur->next->next;
979                }
980        }
981
982        /* Send the del item SNAC */
983        aim_ssi_addmoddel(sess, conn, delitems, num, AIM_CB_SSI_DEL);
984
985        /* Free the items */
986        for (i=0; i<num; i++) {
987                if (delitems[i]->name)
988                        g_free(delitems[i]->name);
989                if (delitems[i]->data)
990                        aim_freetlvchain((aim_tlvlist_t **)&delitems[i]->data);
991                g_free(delitems[i]);
992        }
993        g_free(delitems);
994
995        /* Begin sending SSI SNACs */
996        aim_ssi_dispatch(sess, conn);
997
998        return 0;
999}
1000
1001/**
1002 * Stores your permit/deny setting on the server, and starts using it.
1003 *
1004 * @param sess The oscar session.
1005 * @param conn The bos connection for this session.
1006 * @param permdeny Your permit/deny setting.  Can be one of the following:
1007 *        1 - Allow all users
1008 *        2 - Block all users
1009 *        3 - Allow only the users below
1010 *        4 - Block only the users below
1011 *        5 - Allow only users on my buddy list
1012 * @param vismask A bitmask of the class of users to whom you want to be
1013 *        visible.  See the AIM_FLAG_BLEH #defines in aim.h
1014 * @return Return 0 if no errors, otherwise return the error number.
1015 */
1016int aim_ssi_setpermdeny(aim_session_t *sess, aim_conn_t *conn, guint8 permdeny, guint32 vismask) {
1017        struct aim_ssi_item *cur; //, *tmp;
1018//      guint16 j;
1019        aim_tlv_t *tlv;
1020
1021        if (!sess || !conn)
1022                return -EINVAL;
1023
1024        /* Look up the permit/deny settings item */
1025        cur = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, NULL, AIM_SSI_TYPE_PDINFO);
1026
1027        if (cur) {
1028                /* The permit/deny item exists */
1029                if (cur->data && (tlv = aim_gettlv(cur->data, 0x00ca, 1))) {
1030                        /* Just change the value of the x00ca TLV */
1031                        if (tlv->length != 1) {
1032                                tlv->length = 1;
1033                                g_free(tlv->value);
1034                                tlv->value = (guint8 *)g_malloc(sizeof(guint8));
1035                        }
1036                        tlv->value[0] = permdeny;
1037                } else {
1038                        /* Need to add the x00ca TLV to the TLV chain */
1039                        aim_addtlvtochain8((aim_tlvlist_t**)&cur->data, 0x00ca, permdeny);
1040                }
1041
1042                if (cur->data && (tlv = aim_gettlv(cur->data, 0x00cb, 1))) {
1043                        /* Just change the value of the x00cb TLV */
1044                        if (tlv->length != 4) {
1045                                tlv->length = 4;
1046                                g_free(tlv->value);
1047                                tlv->value = (guint8 *)g_malloc(4*sizeof(guint8));
1048                        }
1049                        aimutil_put32(tlv->value, vismask);
1050                } else {
1051                        /* Need to add the x00cb TLV to the TLV chain */
1052                        aim_addtlvtochain32((aim_tlvlist_t**)&cur->data, 0x00cb, vismask);
1053                }
1054
1055                /* Send the mod item SNAC */
1056                aim_ssi_addmoddel(sess, conn, &cur, 1, AIM_CB_SSI_MOD);
1057        } else {
1058                /* Need to add the permit/deny item */
1059                if (!(cur = aim_ssi_itemlist_add(&sess->ssi.items, NULL, NULL, AIM_SSI_TYPE_PDINFO)))
1060                        return -ENOMEM;
1061                aim_addtlvtochain8((aim_tlvlist_t**)&cur->data, 0x00ca, permdeny);
1062                aim_addtlvtochain32((aim_tlvlist_t**)&cur->data, 0x00cb, vismask);
1063                aim_ssi_addmoddel(sess, conn, &cur, 1, AIM_CB_SSI_ADD);
1064        }
1065
1066        /* Begin sending SSI SNACs */
1067        aim_ssi_dispatch(sess, conn);
1068
1069        return 0;
1070}
1071
1072/**
1073 * Stores your setting for whether you should show up as idle or not.
1074 *
1075 * @param sess The oscar session.
1076 * @param conn The bos connection for this session.
1077 * @param presence I think it's a bitmask, but I only know what one of the bits is:
1078 *        0x00000400 - Allow others to see your idle time
1079 * @return Return 0 if no errors, otherwise return the error number.
1080 */
1081int aim_ssi_setpresence(aim_session_t *sess, aim_conn_t *conn, guint32 presence) {
1082        struct aim_ssi_item *cur; //, *tmp;
1083//      guint16 j;
1084        aim_tlv_t *tlv;
1085
1086        if (!sess || !conn)
1087                return -EINVAL;
1088
1089        /* Look up the item */
1090        cur = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS);
1091
1092        if (cur) {
1093                /* The item exists */
1094                if (cur->data && (tlv = aim_gettlv(cur->data, 0x00c9, 1))) {
1095                        /* Just change the value of the x00c9 TLV */
1096                        if (tlv->length != 4) {
1097                                tlv->length = 4;
1098                                g_free(tlv->value);
1099                                tlv->value = (guint8 *)g_malloc(4*sizeof(guint8));
1100                        }
1101                        aimutil_put32(tlv->value, presence);
1102                } else {
1103                        /* Need to add the x00c9 TLV to the TLV chain */
1104                        aim_addtlvtochain32((aim_tlvlist_t**)&cur->data, 0x00c9, presence);
1105                }
1106
1107                /* Send the mod item SNAC */
1108                aim_ssi_addmoddel(sess, conn, &cur, 1, AIM_CB_SSI_MOD);
1109        } else {
1110                /* Need to add the item */
1111                if (!(cur = aim_ssi_itemlist_add(&sess->ssi.items, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS)))
1112                        return -ENOMEM;
1113                aim_addtlvtochain32((aim_tlvlist_t**)&cur->data, 0x00c9, presence);
1114                aim_ssi_addmoddel(sess, conn, &cur, 1, AIM_CB_SSI_ADD);
1115        }
1116
1117        /* Begin sending SSI SNACs */
1118        aim_ssi_dispatch(sess, conn);
1119
1120        return 0;
1121}
1122
1123/*
1124 * Request SSI Rights.
1125 */
1126int aim_ssi_reqrights(aim_session_t *sess, aim_conn_t *conn)
1127{
1128        return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_REQRIGHTS);
1129}
1130
1131/*
1132 * SSI Rights Information.
1133 */
1134static int parserights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
1135{
1136        int ret = 0;
1137        aim_rxcallback_t userfunc;
1138
1139        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
1140                ret = userfunc(sess, rx);
1141
1142        return ret;
1143}
1144
1145/*
1146 * Request SSI Data.
1147 *
1148 * The data will only be sent if it is newer than the posted local
1149 * timestamp and revision.
1150 *
1151 * Note that the client should never increment the revision, only the server.
1152 *
1153 */
1154int aim_ssi_reqdata(aim_session_t *sess, aim_conn_t *conn, time_t localstamp, guint16 localrev)
1155{
1156        aim_frame_t *fr;
1157        aim_snacid_t snacid;
1158
1159        if (!sess || !conn)
1160                return -EINVAL;
1161
1162        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4+2)))
1163                return -ENOMEM;
1164
1165        snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_REQLIST, 0x0000, NULL, 0);
1166
1167        aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_REQLIST, 0x0000, snacid);
1168        aimbs_put32(&fr->data, localstamp);
1169        aimbs_put16(&fr->data, localrev);
1170
1171        aim_tx_enqueue(sess, fr);
1172
1173        return 0;
1174}
1175
1176int aim_ssi_reqalldata(aim_session_t *sess, aim_conn_t *conn)
1177{
1178        aim_frame_t *fr;
1179        aim_snacid_t snacid;
1180
1181        if (!sess || !conn)
1182                return -EINVAL;
1183
1184        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10)))
1185                return -ENOMEM;
1186
1187        snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_REQFULLLIST, 0x0000, NULL, 0);
1188
1189        aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_REQFULLLIST, 0x0000, snacid);
1190
1191        aim_tx_enqueue(sess, fr);
1192
1193        return 0;
1194}
1195
1196/*
1197 * SSI Data.
1198 */
1199static int parsedata(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
1200{
1201        int ret = 0;
1202        aim_rxcallback_t userfunc;
1203        struct aim_ssi_item *cur = NULL;
1204        guint8 fmtver; /* guess */
1205        guint16 revision;
1206        guint32 timestamp;
1207
1208        /* When you set the version for the SSI family to 2-4, the beginning of this changes.
1209         * Instead of the version and then the revision, there is "0x0006" and then a type
1210         * 0x0001 TLV containing the 2 byte SSI family version that you sent earlier.  Also,
1211         * the SNAC flags go from 0x0000 to 0x8000.  I guess the 0x0006 is the length of the
1212         * TLV(s) that follow.  The rights SNAC does the same thing, with the differing flag
1213         * and everything.
1214         */
1215
1216        fmtver = aimbs_get8(bs); /* Version of ssi data.  Should be 0x00 */
1217        revision = aimbs_get16(bs); /* # of times ssi data has been modified */
1218        if (revision != 0)
1219                sess->ssi.revision = revision;
1220
1221        for (cur = sess->ssi.items; cur && cur->next; cur=cur->next) ;
1222
1223        while (aim_bstream_empty(bs) > 4) { /* last four bytes are stamp */
1224                guint16 namelen, tbslen;
1225
1226                if (!sess->ssi.items) {
1227                        if (!(sess->ssi.items = g_new0(struct aim_ssi_item, 1)))
1228                                return -ENOMEM;
1229                        cur = sess->ssi.items;
1230                } else {
1231                        if (!(cur->next = g_new0(struct aim_ssi_item, 1)))
1232                                return -ENOMEM;
1233                        cur = cur->next;
1234                }
1235
1236                if ((namelen = aimbs_get16(bs)))
1237                        cur->name = aimbs_getstr(bs, namelen);
1238                cur->gid = aimbs_get16(bs);
1239                cur->bid = aimbs_get16(bs);
1240                cur->type = aimbs_get16(bs);
1241
1242                if ((tbslen = aimbs_get16(bs))) {
1243                        aim_bstream_t tbs;
1244
1245                        aim_bstream_init(&tbs, bs->data + bs->offset /* XXX */, tbslen);
1246                        cur->data = (void *)aim_readtlvchain(&tbs);
1247                        aim_bstream_advance(bs, tbslen);
1248                }
1249        }
1250
1251        timestamp = aimbs_get32(bs);
1252        if (timestamp != 0)
1253                sess->ssi.timestamp = timestamp;
1254        sess->ssi.received_data = 1;
1255
1256        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
1257                ret = userfunc(sess, rx, fmtver, sess->ssi.revision, sess->ssi.timestamp, sess->ssi.items);
1258
1259        return ret;
1260}
1261
1262/*
1263 * SSI Data Enable Presence.
1264 *
1265 * Should be sent after receiving 13/6 or 13/f to tell the server you
1266 * are ready to begin using the list.  It will promptly give you the
1267 * presence information for everyone in your list and put your permit/deny
1268 * settings into effect.
1269 *
1270 */
1271int aim_ssi_enable(aim_session_t *sess, aim_conn_t *conn)
1272{
1273        return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, 0x0007);
1274}
1275
1276/*
1277 * Stuff for SSI authorizations. The code used to work with the old im_ch4
1278 * messages, but those are supposed to be obsolete. This is probably
1279 * ICQ-specific.
1280 */
1281
1282/**
1283 * Request authorization to add someone to the server-side buddy list.
1284 *
1285 * @param sess The oscar session.
1286 * @param conn The bos connection for this session.
1287 * @param uin The contact's ICQ UIN.
1288 * @param reason The reason string to send with the request.
1289 * @return Return 0 if no errors, otherwise return the error number.
1290 */
1291int aim_ssi_auth_request( aim_session_t *sess, aim_conn_t *conn, char *uin, char *reason )
1292{
1293        aim_frame_t *fr;
1294        aim_snacid_t snacid;
1295        int snaclen;
1296       
1297        snaclen = 10 + 1 + strlen( uin ) + 2 + strlen( reason ) + 2;
1298       
1299        if( !( fr = aim_tx_new( sess, conn, AIM_FRAMETYPE_FLAP, 0x02, snaclen ) ) )
1300                return -ENOMEM;
1301
1302        snacid = aim_cachesnac( sess, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREQ, 0x0000, NULL, 0 );
1303        aim_putsnac( &fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREQ, 0x0000, snacid );
1304       
1305        aimbs_put8( &fr->data, strlen( uin ) );
1306        aimbs_putraw( &fr->data, (guint8 *)uin, strlen( uin ) );
1307        aimbs_put16( &fr->data, strlen( reason ) );
1308        aimbs_putraw( &fr->data, (guint8 *)reason, strlen( reason ) );
1309        aimbs_put16( &fr->data, 0 );
1310       
1311        aim_tx_enqueue( sess, fr );
1312       
1313        return( 0 );
1314}
1315
1316/**
1317 * Reply to an authorization request to add someone to the server-side buddy list.
1318 *
1319 * @param sess The oscar session.
1320 * @param conn The bos connection for this session.
1321 * @param uin The contact's ICQ UIN.
1322 * @param yesno 1 == Permit, 0 == Deny
1323 * @param reason The reason string to send with the request.
1324 * @return Return 0 if no errors, otherwise return the error number.
1325 */
1326int aim_ssi_auth_reply( aim_session_t *sess, aim_conn_t *conn, char *uin, int yesno, char *reason )
1327{
1328        aim_frame_t *fr;
1329        aim_snacid_t snacid;
1330        int snaclen;
1331       
1332        snaclen = 10 + 1 + strlen( uin ) + 3 + strlen( reason );
1333       
1334        if( !( fr = aim_tx_new( sess, conn, AIM_FRAMETYPE_FLAP, 0x02, snaclen ) ) )
1335                return -ENOMEM;
1336       
1337        snacid = aim_cachesnac( sess, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREP, 0x0000, NULL, 0 );
1338        aim_putsnac( &fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREP, 0x0000, snacid );
1339       
1340        aimbs_put8( &fr->data, strlen( uin ) );
1341        aimbs_putraw( &fr->data, (guint8 *)uin, strlen( uin ) );
1342        aimbs_put8( &fr->data, yesno );
1343        aimbs_put16( &fr->data, strlen( reason ) );
1344        aimbs_putraw( &fr->data, (guint8 *)reason, strlen( reason ) );
1345       
1346        aim_tx_enqueue( sess, fr );
1347       
1348        return( 0 );
1349}
1350
1351
1352/*
1353 * SSI Add/Mod/Del Item(s).
1354 *
1355 * Sends the SNAC to add, modify, or delete an item from the server-stored
1356 * information.  These 3 SNACs all have an identical structure.  The only
1357 * difference is the subtype that is set for the SNAC.
1358 *
1359 */
1360int aim_ssi_addmoddel(aim_session_t *sess, aim_conn_t *conn, struct aim_ssi_item **items, unsigned int num, guint16 subtype)
1361{
1362        aim_frame_t *fr;
1363        aim_snacid_t snacid;
1364        int i, snaclen, listlen;
1365        char *list = NULL;
1366
1367        if (!sess || !conn || !items || !num)
1368                return -EINVAL;
1369
1370        snaclen = 10; /* For family, subtype, flags, and SNAC ID */
1371        listlen = 0;
1372        for (i=0; i<num; i++) {
1373                snaclen += 10; /* For length, GID, BID, type, and length */
1374                if (items[i]->name) {
1375                        snaclen += strlen(items[i]->name);
1376                       
1377                        if (subtype == AIM_CB_SSI_ADD) {
1378                                list = g_realloc(list, listlen + strlen(items[i]->name) + 1);
1379                                strcpy(list + listlen, items[i]->name);
1380                                listlen += strlen(items[i]->name) + 1;
1381                        }
1382                } else {
1383                        if (subtype == AIM_CB_SSI_ADD) {
1384                                list = g_realloc(list, listlen + 1);
1385                                list[listlen] = '\0';
1386                                listlen ++;
1387                        }
1388                }
1389                if (items[i]->data)
1390                        snaclen += aim_sizetlvchain((aim_tlvlist_t **)&items[i]->data);
1391        }
1392       
1393        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, snaclen)))
1394                return -ENOMEM;
1395
1396        snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, subtype, 0x0000, list, list ? listlen : 0);
1397        aim_putsnac(&fr->data, AIM_CB_FAM_SSI, subtype, 0x0000, snacid);
1398       
1399        g_free(list);
1400
1401        for (i=0; i<num; i++) {
1402                aimbs_put16(&fr->data, items[i]->name ? strlen(items[i]->name) : 0);
1403                if (items[i]->name)
1404                        aimbs_putraw(&fr->data, (guint8 *)items[i]->name, strlen(items[i]->name));
1405                aimbs_put16(&fr->data, items[i]->gid);
1406                aimbs_put16(&fr->data, items[i]->bid);
1407                aimbs_put16(&fr->data, items[i]->type);
1408                aimbs_put16(&fr->data, items[i]->data ? aim_sizetlvchain((aim_tlvlist_t **)&items[i]->data) : 0);
1409                if (items[i]->data)
1410                        aim_writetlvchain(&fr->data, (aim_tlvlist_t **)&items[i]->data);
1411        }
1412
1413        aim_ssi_enqueue(sess, conn, fr);
1414
1415        return 0;
1416}
1417
1418/*
1419 * SSI Add/Mod/Del Ack.
1420 *
1421 * Response to add, modify, or delete SNAC (sent with aim_ssi_addmoddel).
1422 *
1423 */
1424static int parseack(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
1425{
1426        int ret = 0;
1427        aim_rxcallback_t userfunc;
1428        aim_snac_t *origsnac;
1429
1430        sess->ssi.waiting_for_ack = 0;
1431        aim_ssi_dispatch(sess, rx->conn);
1432       
1433        origsnac = aim_remsnac(sess, snac->id);
1434       
1435        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
1436                ret = userfunc(sess, rx, origsnac);
1437       
1438        if (origsnac) {
1439                g_free(origsnac->data);
1440                g_free(origsnac);
1441        }
1442       
1443        return ret;
1444}
1445
1446/*
1447 * SSI Begin Data Modification.
1448 *
1449 * Tells the server you're going to start modifying data.
1450 *
1451 */
1452int aim_ssi_modbegin(aim_session_t *sess, aim_conn_t *conn)
1453{
1454        return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_EDITSTART);
1455}
1456
1457/*
1458 * SSI End Data Modification.
1459 *
1460 * Tells the server you're done modifying data.
1461 *
1462 */
1463int aim_ssi_modend(aim_session_t *sess, aim_conn_t *conn)
1464{
1465        return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_EDITSTOP);
1466}
1467
1468/*
1469 * SSI Data Unchanged.
1470 *
1471 * Response to aim_ssi_reqdata() if the server-side data is not newer than
1472 * posted local stamp/revision.
1473 *
1474 */
1475static int parsedataunchanged(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
1476{
1477        int ret = 0;
1478        aim_rxcallback_t userfunc;
1479
1480        sess->ssi.received_data = 1;
1481
1482        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
1483                ret = userfunc(sess, rx);
1484
1485        return ret;
1486}
1487
1488static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
1489{
1490
1491        if (snac->subtype == AIM_CB_SSI_RIGHTSINFO)
1492                return parserights(sess, mod, rx, snac, bs);
1493        else if (snac->subtype == AIM_CB_SSI_LIST)
1494                return parsedata(sess, mod, rx, snac, bs);
1495        else if (snac->subtype == AIM_CB_SSI_SRVACK)
1496                return parseack(sess, mod, rx, snac, bs);
1497        else if (snac->subtype == AIM_CB_SSI_NOLIST)
1498                return parsedataunchanged(sess, mod, rx, snac, bs);
1499
1500        return 0;
1501}
1502
1503static void ssi_shutdown(aim_session_t *sess, aim_module_t *mod)
1504{
1505        aim_ssi_freelist(sess);
1506
1507        return;
1508}
1509
1510int ssi_modfirst(aim_session_t *sess, aim_module_t *mod)
1511{
1512
1513        mod->family = AIM_CB_FAM_SSI;
1514        mod->version = 0x0003;
1515        mod->toolid = 0x0110;
1516        mod->toolversion = 0x0629;
1517        mod->flags = 0;
1518        strncpy(mod->name, "ssi", sizeof(mod->name));
1519        mod->snachandler = snachandler;
1520        mod->shutdown = ssi_shutdown;
1521
1522        return 0;
1523}
Note: See TracBrowser for help on using the repository browser.