source: libfaim/ssi.c @ 7c9c847

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