source: protocols/oscar/ssi.c @ 0eb971a

Last change on this file since 0eb971a was 0eb971a, checked in by Wilmer van der Gaast <wilmer@…>, at 2012-10-19T22:39:10Z

Removing some fully dead code.

  • Property mode set to 100644
File size: 42.4 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 setting for whether you should show up as idle or not.
1003 *
1004 * @param sess The oscar session.
1005 * @param conn The bos connection for this session.
1006 * @param presence I think it's a bitmask, but I only know what one of the bits is:
1007 *        0x00000400 - Allow others to see your idle time
1008 * @return Return 0 if no errors, otherwise return the error number.
1009 */
1010int aim_ssi_setpresence(aim_session_t *sess, aim_conn_t *conn, guint32 presence) {
1011        struct aim_ssi_item *cur; //, *tmp;
1012//      guint16 j;
1013        aim_tlv_t *tlv;
1014
1015        if (!sess || !conn)
1016                return -EINVAL;
1017
1018        /* Look up the item */
1019        cur = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS);
1020
1021        if (cur) {
1022                /* The item exists */
1023                if (cur->data && (tlv = aim_gettlv(cur->data, 0x00c9, 1))) {
1024                        /* Just change the value of the x00c9 TLV */
1025                        if (tlv->length != 4) {
1026                                tlv->length = 4;
1027                                g_free(tlv->value);
1028                                tlv->value = (guint8 *)g_malloc(4*sizeof(guint8));
1029                        }
1030                        aimutil_put32(tlv->value, presence);
1031                } else {
1032                        /* Need to add the x00c9 TLV to the TLV chain */
1033                        aim_addtlvtochain32((aim_tlvlist_t**)&cur->data, 0x00c9, presence);
1034                }
1035
1036                /* Send the mod item SNAC */
1037                aim_ssi_addmoddel(sess, conn, &cur, 1, AIM_CB_SSI_MOD);
1038        } else {
1039                /* Need to add the item */
1040                if (!(cur = aim_ssi_itemlist_add(&sess->ssi.items, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS)))
1041                        return -ENOMEM;
1042                aim_addtlvtochain32((aim_tlvlist_t**)&cur->data, 0x00c9, presence);
1043                aim_ssi_addmoddel(sess, conn, &cur, 1, AIM_CB_SSI_ADD);
1044        }
1045
1046        /* Begin sending SSI SNACs */
1047        aim_ssi_dispatch(sess, conn);
1048
1049        return 0;
1050}
1051
1052/*
1053 * Request SSI Rights.
1054 */
1055int aim_ssi_reqrights(aim_session_t *sess, aim_conn_t *conn)
1056{
1057        return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_REQRIGHTS);
1058}
1059
1060/*
1061 * SSI Rights Information.
1062 */
1063static int parserights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
1064{
1065        int ret = 0;
1066        aim_rxcallback_t userfunc;
1067
1068        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
1069                ret = userfunc(sess, rx);
1070
1071        return ret;
1072}
1073
1074/*
1075 * Request SSI Data.
1076 *
1077 * The data will only be sent if it is newer than the posted local
1078 * timestamp and revision.
1079 *
1080 * Note that the client should never increment the revision, only the server.
1081 *
1082 */
1083int aim_ssi_reqdata(aim_session_t *sess, aim_conn_t *conn, time_t localstamp, guint16 localrev)
1084{
1085        aim_frame_t *fr;
1086        aim_snacid_t snacid;
1087
1088        if (!sess || !conn)
1089                return -EINVAL;
1090
1091        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4+2)))
1092                return -ENOMEM;
1093
1094        snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_REQLIST, 0x0000, NULL, 0);
1095
1096        aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_REQLIST, 0x0000, snacid);
1097        aimbs_put32(&fr->data, localstamp);
1098        aimbs_put16(&fr->data, localrev);
1099
1100        aim_tx_enqueue(sess, fr);
1101
1102        return 0;
1103}
1104
1105int aim_ssi_reqalldata(aim_session_t *sess, aim_conn_t *conn)
1106{
1107        aim_frame_t *fr;
1108        aim_snacid_t snacid;
1109
1110        if (!sess || !conn)
1111                return -EINVAL;
1112
1113        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10)))
1114                return -ENOMEM;
1115
1116        snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_REQFULLLIST, 0x0000, NULL, 0);
1117
1118        aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_REQFULLLIST, 0x0000, snacid);
1119
1120        aim_tx_enqueue(sess, fr);
1121
1122        return 0;
1123}
1124
1125/*
1126 * SSI Data.
1127 */
1128static int parsedata(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
1129{
1130        int ret = 0;
1131        aim_rxcallback_t userfunc;
1132        struct aim_ssi_item *cur = NULL;
1133        guint8 fmtver; /* guess */
1134        guint16 revision;
1135        guint32 timestamp;
1136
1137        /* When you set the version for the SSI family to 2-4, the beginning of this changes.
1138         * Instead of the version and then the revision, there is "0x0006" and then a type
1139         * 0x0001 TLV containing the 2 byte SSI family version that you sent earlier.  Also,
1140         * the SNAC flags go from 0x0000 to 0x8000.  I guess the 0x0006 is the length of the
1141         * TLV(s) that follow.  The rights SNAC does the same thing, with the differing flag
1142         * and everything.
1143         */
1144
1145        fmtver = aimbs_get8(bs); /* Version of ssi data.  Should be 0x00 */
1146        revision = aimbs_get16(bs); /* # of times ssi data has been modified */
1147        if (revision != 0)
1148                sess->ssi.revision = revision;
1149
1150        for (cur = sess->ssi.items; cur && cur->next; cur=cur->next) ;
1151
1152        while (aim_bstream_empty(bs) > 4) { /* last four bytes are stamp */
1153                guint16 namelen, tbslen;
1154
1155                if (!sess->ssi.items) {
1156                        if (!(sess->ssi.items = g_new0(struct aim_ssi_item, 1)))
1157                                return -ENOMEM;
1158                        cur = sess->ssi.items;
1159                } else {
1160                        if (!(cur->next = g_new0(struct aim_ssi_item, 1)))
1161                                return -ENOMEM;
1162                        cur = cur->next;
1163                }
1164
1165                if ((namelen = aimbs_get16(bs)))
1166                        cur->name = aimbs_getstr(bs, namelen);
1167                cur->gid = aimbs_get16(bs);
1168                cur->bid = aimbs_get16(bs);
1169                cur->type = aimbs_get16(bs);
1170
1171                if ((tbslen = aimbs_get16(bs))) {
1172                        aim_bstream_t tbs;
1173
1174                        aim_bstream_init(&tbs, bs->data + bs->offset /* XXX */, tbslen);
1175                        cur->data = (void *)aim_readtlvchain(&tbs);
1176                        aim_bstream_advance(bs, tbslen);
1177                }
1178        }
1179
1180        timestamp = aimbs_get32(bs);
1181        if (timestamp != 0)
1182                sess->ssi.timestamp = timestamp;
1183        sess->ssi.received_data = 1;
1184
1185        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
1186                ret = userfunc(sess, rx, fmtver, sess->ssi.revision, sess->ssi.timestamp, sess->ssi.items);
1187
1188        return ret;
1189}
1190
1191/*
1192 * SSI Data Enable Presence.
1193 *
1194 * Should be sent after receiving 13/6 or 13/f to tell the server you
1195 * are ready to begin using the list.  It will promptly give you the
1196 * presence information for everyone in your list and put your permit/deny
1197 * settings into effect.
1198 *
1199 */
1200int aim_ssi_enable(aim_session_t *sess, aim_conn_t *conn)
1201{
1202        return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, 0x0007);
1203}
1204
1205/*
1206 * Stuff for SSI authorizations. The code used to work with the old im_ch4
1207 * messages, but those are supposed to be obsolete. This is probably
1208 * ICQ-specific.
1209 */
1210
1211/**
1212 * Request authorization to add someone to the server-side buddy list.
1213 *
1214 * @param sess The oscar session.
1215 * @param conn The bos connection for this session.
1216 * @param uin The contact's ICQ UIN.
1217 * @param reason The reason string to send with the request.
1218 * @return Return 0 if no errors, otherwise return the error number.
1219 */
1220int aim_ssi_auth_request( aim_session_t *sess, aim_conn_t *conn, char *uin, char *reason )
1221{
1222        aim_frame_t *fr;
1223        aim_snacid_t snacid;
1224        int snaclen;
1225       
1226        snaclen = 10 + 1 + strlen( uin ) + 2 + strlen( reason ) + 2;
1227       
1228        if( !( fr = aim_tx_new( sess, conn, AIM_FRAMETYPE_FLAP, 0x02, snaclen ) ) )
1229                return -ENOMEM;
1230
1231        snacid = aim_cachesnac( sess, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREQ, 0x0000, NULL, 0 );
1232        aim_putsnac( &fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREQ, 0x0000, snacid );
1233       
1234        aimbs_put8( &fr->data, strlen( uin ) );
1235        aimbs_putraw( &fr->data, (guint8 *)uin, strlen( uin ) );
1236        aimbs_put16( &fr->data, strlen( reason ) );
1237        aimbs_putraw( &fr->data, (guint8 *)reason, strlen( reason ) );
1238        aimbs_put16( &fr->data, 0 );
1239       
1240        aim_tx_enqueue( sess, fr );
1241       
1242        return( 0 );
1243}
1244
1245/**
1246 * Reply to an authorization request to add someone to the server-side buddy list.
1247 *
1248 * @param sess The oscar session.
1249 * @param conn The bos connection for this session.
1250 * @param uin The contact's ICQ UIN.
1251 * @param yesno 1 == Permit, 0 == Deny
1252 * @param reason The reason string to send with the request.
1253 * @return Return 0 if no errors, otherwise return the error number.
1254 */
1255int aim_ssi_auth_reply( aim_session_t *sess, aim_conn_t *conn, char *uin, int yesno, char *reason )
1256{
1257        aim_frame_t *fr;
1258        aim_snacid_t snacid;
1259        int snaclen;
1260       
1261        snaclen = 10 + 1 + strlen( uin ) + 3 + strlen( reason );
1262       
1263        if( !( fr = aim_tx_new( sess, conn, AIM_FRAMETYPE_FLAP, 0x02, snaclen ) ) )
1264                return -ENOMEM;
1265       
1266        snacid = aim_cachesnac( sess, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREP, 0x0000, NULL, 0 );
1267        aim_putsnac( &fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREP, 0x0000, snacid );
1268       
1269        aimbs_put8( &fr->data, strlen( uin ) );
1270        aimbs_putraw( &fr->data, (guint8 *)uin, strlen( uin ) );
1271        aimbs_put8( &fr->data, yesno );
1272        aimbs_put16( &fr->data, strlen( reason ) );
1273        aimbs_putraw( &fr->data, (guint8 *)reason, strlen( reason ) );
1274       
1275        aim_tx_enqueue( sess, fr );
1276       
1277        return( 0 );
1278}
1279
1280
1281/*
1282 * SSI Add/Mod/Del Item(s).
1283 *
1284 * Sends the SNAC to add, modify, or delete an item from the server-stored
1285 * information.  These 3 SNACs all have an identical structure.  The only
1286 * difference is the subtype that is set for the SNAC.
1287 *
1288 */
1289int aim_ssi_addmoddel(aim_session_t *sess, aim_conn_t *conn, struct aim_ssi_item **items, unsigned int num, guint16 subtype)
1290{
1291        aim_frame_t *fr;
1292        aim_snacid_t snacid;
1293        int i, snaclen, listlen;
1294        char *list = NULL;
1295
1296        if (!sess || !conn || !items || !num)
1297                return -EINVAL;
1298
1299        snaclen = 10; /* For family, subtype, flags, and SNAC ID */
1300        listlen = 0;
1301        for (i=0; i<num; i++) {
1302                snaclen += 10; /* For length, GID, BID, type, and length */
1303                if (items[i]->name) {
1304                        snaclen += strlen(items[i]->name);
1305                       
1306                        if (subtype == AIM_CB_SSI_ADD) {
1307                                list = g_realloc(list, listlen + strlen(items[i]->name) + 1);
1308                                strcpy(list + listlen, items[i]->name);
1309                                listlen += strlen(items[i]->name) + 1;
1310                        }
1311                } else {
1312                        if (subtype == AIM_CB_SSI_ADD) {
1313                                list = g_realloc(list, listlen + 1);
1314                                list[listlen] = '\0';
1315                                listlen ++;
1316                        }
1317                }
1318                if (items[i]->data)
1319                        snaclen += aim_sizetlvchain((aim_tlvlist_t **)&items[i]->data);
1320        }
1321       
1322        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, snaclen)))
1323                return -ENOMEM;
1324
1325        snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, subtype, 0x0000, list, list ? listlen : 0);
1326        aim_putsnac(&fr->data, AIM_CB_FAM_SSI, subtype, 0x0000, snacid);
1327       
1328        g_free(list);
1329
1330        for (i=0; i<num; i++) {
1331                aimbs_put16(&fr->data, items[i]->name ? strlen(items[i]->name) : 0);
1332                if (items[i]->name)
1333                        aimbs_putraw(&fr->data, (guint8 *)items[i]->name, strlen(items[i]->name));
1334                aimbs_put16(&fr->data, items[i]->gid);
1335                aimbs_put16(&fr->data, items[i]->bid);
1336                aimbs_put16(&fr->data, items[i]->type);
1337                aimbs_put16(&fr->data, items[i]->data ? aim_sizetlvchain((aim_tlvlist_t **)&items[i]->data) : 0);
1338                if (items[i]->data)
1339                        aim_writetlvchain(&fr->data, (aim_tlvlist_t **)&items[i]->data);
1340        }
1341
1342        aim_ssi_enqueue(sess, conn, fr);
1343
1344        return 0;
1345}
1346
1347/*
1348 * SSI Add/Mod/Del Ack.
1349 *
1350 * Response to add, modify, or delete SNAC (sent with aim_ssi_addmoddel).
1351 *
1352 */
1353static int parseack(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
1354{
1355        int ret = 0;
1356        aim_rxcallback_t userfunc;
1357        aim_snac_t *origsnac;
1358
1359        sess->ssi.waiting_for_ack = 0;
1360        aim_ssi_dispatch(sess, rx->conn);
1361       
1362        origsnac = aim_remsnac(sess, snac->id);
1363       
1364        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
1365                ret = userfunc(sess, rx, origsnac);
1366       
1367        if (origsnac) {
1368                g_free(origsnac->data);
1369                g_free(origsnac);
1370        }
1371       
1372        return ret;
1373}
1374
1375/*
1376 * SSI Begin Data Modification.
1377 *
1378 * Tells the server you're going to start modifying data.
1379 *
1380 */
1381int aim_ssi_modbegin(aim_session_t *sess, aim_conn_t *conn)
1382{
1383        return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_EDITSTART);
1384}
1385
1386/*
1387 * SSI End Data Modification.
1388 *
1389 * Tells the server you're done modifying data.
1390 *
1391 */
1392int aim_ssi_modend(aim_session_t *sess, aim_conn_t *conn)
1393{
1394        return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_EDITSTOP);
1395}
1396
1397/*
1398 * SSI Data Unchanged.
1399 *
1400 * Response to aim_ssi_reqdata() if the server-side data is not newer than
1401 * posted local stamp/revision.
1402 *
1403 */
1404static int parsedataunchanged(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
1405{
1406        int ret = 0;
1407        aim_rxcallback_t userfunc;
1408
1409        sess->ssi.received_data = 1;
1410
1411        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
1412                ret = userfunc(sess, rx);
1413
1414        return ret;
1415}
1416
1417static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
1418{
1419
1420        if (snac->subtype == AIM_CB_SSI_RIGHTSINFO)
1421                return parserights(sess, mod, rx, snac, bs);
1422        else if (snac->subtype == AIM_CB_SSI_LIST)
1423                return parsedata(sess, mod, rx, snac, bs);
1424        else if (snac->subtype == AIM_CB_SSI_SRVACK)
1425                return parseack(sess, mod, rx, snac, bs);
1426        else if (snac->subtype == AIM_CB_SSI_NOLIST)
1427                return parsedataunchanged(sess, mod, rx, snac, bs);
1428
1429        return 0;
1430}
1431
1432static void ssi_shutdown(aim_session_t *sess, aim_module_t *mod)
1433{
1434        aim_ssi_freelist(sess);
1435
1436        return;
1437}
1438
1439int ssi_modfirst(aim_session_t *sess, aim_module_t *mod)
1440{
1441
1442        mod->family = AIM_CB_FAM_SSI;
1443        mod->version = 0x0003;
1444        mod->toolid = 0x0110;
1445        mod->toolversion = 0x0629;
1446        mod->flags = 0;
1447        strncpy(mod->name, "ssi", sizeof(mod->name));
1448        mod->snachandler = snachandler;
1449        mod->shutdown = ssi_shutdown;
1450
1451        return 0;
1452}
Note: See TracBrowser for help on using the repository browser.