source: libfaim/ssi.c @ e374dee

barnowl_perlaimdebianowlrelease-1.10release-1.4release-1.5release-1.6release-1.7release-1.8release-1.9
Last change on this file since e374dee was e374dee, checked in by James M. Kretchmar <kretch@mit.edu>, 21 years ago
*** empty log message ***
  • Property mode set to 100644
File size: 52.6 KB
Line 
1/*
2 * Family 0x0013 - Server-Side/Stored Information.
3 *
4 * Relatively new facility that allows certain types of information, such as
5 * a user's buddy list, permit/deny list, and permit/deny preferences, to be
6 * stored on the server, so that they can be accessed from any client.
7 *
8 * We keep 2 copies of SSI data:
9 * 1) An exact copy of what is stored on the AIM servers.
10 * 2) A local copy that we make changes to, and then send diffs
11 *    between this and the exact copy to keep them in sync.
12 *
13 * All the "aim_ssi_itemlist_bleh" functions near the top just modify the list
14 * that is given to them (i.e. they don't send SNACs).
15 *
16 * The SNAC sending and receiving functions are lower down in the file, and
17 * they're simpler.  They are in the order of the subtypes they deal with,
18 * starting with the request rights function (subtype 0x0002), then parse
19 * rights (subtype 0x0003), then--well, you get the idea.
20 *
21 * This is entirely too complicated.
22 * You don't know the half of it.
23 *
24 * XXX - Preserve unknown data in TLV lists
25 *
26 */
27
28#define FAIM_INTERNAL
29#include <aim.h>
30
31/**
32 * Locally rebuild the 0x00c8 TLV in the additional data of the given group.
33 *
34 * @param list A pointer to a pointer to the current list of items.
35 * @param name A null terminated string containing the group name, or NULL
36 *        if you want to modify the master group.
37 * @return Return a pointer to the modified item.
38 */
39static struct aim_ssi_item *aim_ssi_itemlist_rebuildgroup(struct aim_ssi_item *list, const char *name)
40{
41        int newlen;
42        struct aim_ssi_item *cur, *group;
43
44        if (!list)
45                return NULL;
46
47        /* Find the group */
48        if (!(group = aim_ssi_itemlist_finditem(list, name, NULL, AIM_SSI_TYPE_GROUP)))
49                return NULL;
50
51        /* Free the old data */
52        aim_freetlvchain(&group->data);
53        group->data = NULL;
54
55        /* Find the length for the new additional data */
56        newlen = 0;
57        if (group->gid == 0x0000) {
58                for (cur=list; cur; cur=cur->next)
59                        if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid != 0x0000))
60                                newlen += 2;
61        } else {
62                for (cur=list; cur; cur=cur->next)
63                        if ((cur->gid == group->gid) && (cur->type == AIM_SSI_TYPE_BUDDY))
64                                newlen += 2;
65        }
66
67        /* Build the new TLV list */
68        if (newlen > 0) {
69                fu8_t *newdata;
70
71                if (!(newdata = (fu8_t *)malloc((newlen)*sizeof(fu8_t))))
72                        return NULL;
73                newlen = 0;
74                if (group->gid == 0x0000) {
75                        for (cur=list; cur; cur=cur->next)
76                                if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid != 0x0000))
77                                                newlen += aimutil_put16(newdata+newlen, cur->gid);
78                } else {
79                        for (cur=list; cur; cur=cur->next)
80                                if ((cur->gid == group->gid) && (cur->type == AIM_SSI_TYPE_BUDDY))
81                                                newlen += aimutil_put16(newdata+newlen, cur->bid);
82                }
83                aim_addtlvtochain_raw(&group->data, 0x00c8, newlen, newdata);
84
85                free(newdata);
86        }
87
88        return group;
89}
90
91/**
92 * Locally add a new item to the given item list.
93 *
94 * @param list A pointer to a pointer to the current list of items.
95 * @param name A null terminated string of the name of the new item, or NULL if the
96 *        item should have no name.
97 * @param gid The group ID# you want the new item to have, or 0xFFFF if we should pick something.
98 * @param bid The buddy ID# you want the new item to have, or 0xFFFF if we should pick something.
99 * @param type The type of the item, 0x0000 for a contact, 0x0001 for a group, etc.
100 * @param data The additional data for the new item.
101 * @return A pointer to the newly created item.
102 */
103static struct aim_ssi_item *aim_ssi_itemlist_add(struct aim_ssi_item **list, const char *name, fu16_t gid, fu16_t bid, fu16_t type, aim_tlvlist_t *data)
104{
105        int i;
106        struct aim_ssi_item *cur, *new;
107
108        if (!list)
109                return NULL;
110
111        if (!(new = (struct aim_ssi_item *)malloc(sizeof(struct aim_ssi_item))))
112                return NULL;
113
114        /* Set the name */
115        if (name) {
116                new->name = (char *)malloc((strlen(name)+1)*sizeof(char));
117                strcpy(new->name, name);
118        } else
119                new->name = NULL;
120
121        /* Set the group ID# and buddy ID# */
122        new->gid = gid;
123        new->bid = bid;
124        if (type == AIM_SSI_TYPE_GROUP) {
125                if ((new->gid == 0xFFFF) && name) {
126                        do {
127                                new->gid += 0x0001;
128                                for (cur=*list, i=0; ((cur) && (!i)); cur=cur->next)
129                                        if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid == new->gid))
130                                                i=1;
131                        } while (i);
132                }
133        } else {
134                if (new->bid == 0xFFFF) {
135                        do {
136                                new->bid += 0x0001;
137                                for (cur=*list, i=0; ((cur) && (!i)); cur=cur->next)
138                                        if ((cur->bid == new->bid) && (cur->gid == new->gid))
139                                                i=1;
140                        } while (i);
141                }
142        }
143
144        /* Set the type */
145        new->type = type;
146
147        /* Set the TLV list */
148        new->data = aim_tlvlist_copy(data);
149
150        /* Add the item to the list in the correct numerical position.  Fancy, eh? */
151        if (*list) {
152                if ((new->gid < (*list)->gid) || ((new->gid == (*list)->gid) && (new->bid < (*list)->bid))) {
153                        new->next = *list;
154                        *list = new;
155                } else {
156                        struct aim_ssi_item *prev;
157                        for ((prev=*list, cur=(*list)->next); (cur && ((new->gid > cur->gid) || ((new->gid == cur->gid) && (new->bid > cur->bid)))); prev=cur, cur=cur->next);
158                        new->next = prev->next;
159                        prev->next = new;
160                }
161        } else {
162                new->next = *list;
163                *list = new;
164        }
165
166        return new;
167}
168
169/**
170 * Locally delete an item from the given item list.
171 *
172 * @param list A pointer to a pointer to the current list of items.
173 * @param del A pointer to the item you want to remove from the list.
174 * @return Return 0 if no errors, otherwise return the error number.
175 */
176static int aim_ssi_itemlist_del(struct aim_ssi_item **list, struct aim_ssi_item *del)
177{
178        if (!list || !(*list) || !del)
179                return -EINVAL;
180
181        /* Remove the item from the list */
182        if (*list == del) {
183                *list = (*list)->next;
184        } else {
185                struct aim_ssi_item *cur;
186                for (cur=*list; (cur->next && (cur->next!=del)); cur=cur->next);
187                if (cur->next)
188                        cur->next = cur->next->next;
189        }
190
191        /* Free the deleted item */
192        free(del->name);
193        aim_freetlvchain(&del->data);
194        free(del);
195
196        return 0;
197}
198
199/**
200 * Compare two items to see if they have the same data.
201 *
202 * @param cur1 A pointer to a pointer to the first item.
203 * @param cur2 A pointer to a pointer to the second item.
204 * @return Return 0 if no differences, or a number if there are differences.
205 */
206static int aim_ssi_itemlist_cmp(struct aim_ssi_item *cur1, struct aim_ssi_item *cur2)
207{
208        if (!cur1 || !cur2)
209                return 1;
210
211        if (cur1->data && !cur2->data)
212                return 2;
213
214        if (!cur1->data && cur2->data)
215                return 3;
216
217        if ((cur1->data && cur2->data) && (aim_tlvlist_cmp(cur1->data, cur2->data)))
218                        return 4;
219
220        if (cur1->name && !cur2->name)
221                return 5;
222
223        if (!cur1->name && cur2->name)
224                return 6;
225
226        if (cur1->name && cur2->name && aim_sncmp(cur1->name, cur2->name))
227                return 7;
228
229        if (cur1->gid != cur2->gid)
230                return 8;
231
232        if (cur1->bid != cur2->bid)
233                return 9;
234
235        if (cur1->type != cur2->type)
236                return 10;
237
238        return 0;
239}
240
241faim_export int aim_ssi_itemlist_valid(struct aim_ssi_item *list, struct aim_ssi_item *item)
242{
243        struct aim_ssi_item *cur;
244        for (cur=list; cur; cur=cur->next)
245                if (cur == item)
246                        return 1;
247        return 0;
248}
249
250/**
251 * Locally find an item given a group ID# and a buddy ID#.
252 *
253 * @param list A pointer to the current list of items.
254 * @param gid The group ID# of the desired item.
255 * @param bid The buddy ID# of the desired item.
256 * @return Return a pointer to the item if found, else return NULL;
257 */
258faim_export struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_item *list, fu16_t gid, fu16_t bid)
259{
260        struct aim_ssi_item *cur;
261        for (cur=list; cur; cur=cur->next)
262                if ((cur->gid == gid) && (cur->bid == bid))
263                        return cur;
264        return NULL;
265}
266
267/**
268 * Locally find an item given a group name, screen name, and type.  If group name
269 * and screen name are null, then just return the first item of the given type.
270 *
271 * @param list A pointer to the current list of items.
272 * @param gn The group name of the desired item.
273 * @param bn The buddy name of the desired item.
274 * @param type The type of the desired item.
275 * @return Return a pointer to the item if found, else return NULL;
276 */
277faim_export struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_item *list, const char *gn, const char *sn, fu16_t type)
278{
279        struct aim_ssi_item *cur;
280        if (!list)
281                return NULL;
282
283        if (gn && sn) { /* For finding buddies in groups */
284                for (cur=list; cur; cur=cur->next)
285                        if ((cur->type == type) && (cur->name) && !(aim_sncmp(cur->name, sn))) {
286                                struct aim_ssi_item *curg;
287                                for (curg=list; curg; curg=curg->next)
288                                        if ((curg->type == AIM_SSI_TYPE_GROUP) && (curg->gid == cur->gid) && (curg->name) && !(aim_sncmp(curg->name, gn)))
289                                                return cur;
290                        }
291
292        } else if (gn) { /* For finding groups */
293                for (cur=list; cur; cur=cur->next) {
294                        if ((cur->type == type) && (cur->bid == 0x0000) && (cur->name) && !(aim_sncmp(cur->name, gn))) {
295                                return cur;
296                        }
297                }
298
299        } else if (sn) { /* For finding permits, denies, and ignores */
300                for (cur=list; cur; cur=cur->next) {
301                        if ((cur->type == type) && (cur->name) && !(aim_sncmp(cur->name, sn))) {
302                                return cur;
303                        }
304                }
305
306        /* For stuff without names--permit deny setting, visibility mask, etc. */
307        } else for (cur=list; cur; cur=cur->next) {
308                if ((cur->type == type) && (!cur->name))
309                        return cur;
310        }
311
312        return NULL;
313}
314
315/**
316 * Check if the given buddy exists in any group in the buddy list.
317 *
318 * @param list A pointer to the current list of items.
319 * @param sn The group name of the desired item.
320 * @return Return a pointer to the name of the item if found, else return NULL;
321 */
322faim_export struct aim_ssi_item *aim_ssi_itemlist_exists(struct aim_ssi_item *list, const char *sn)
323{
324        struct aim_ssi_item *cur;
325        if (!list || !sn)
326                return NULL;
327        for (cur=list; cur; cur=cur->next)
328                if ((cur->type == AIM_SSI_TYPE_BUDDY) && (cur->name) && (!aim_sncmp(cur->name, sn)))
329                        return cur;
330        return NULL;
331}
332
333/**
334 * Locally find the parent item of the given buddy name.
335 *
336 * @param list A pointer to the current list of items.
337 * @param bn The buddy name of the desired item.
338 * @return Return a pointer to the name of the item if found, else return NULL;
339 */
340faim_export char *aim_ssi_itemlist_findparentname(struct aim_ssi_item *list, const char *sn)
341{
342        struct aim_ssi_item *cur, *curg;
343        if (!list || !sn)
344                return NULL;
345        if (!(cur = aim_ssi_itemlist_exists(list, sn)))
346                return NULL;
347        if (!(curg = aim_ssi_itemlist_find(list, cur->gid, 0x0000)))
348                return NULL;
349        return curg->name;
350}
351
352/**
353 * Locally find the permit/deny setting item, and return the setting.
354 *
355 * @param list A pointer to the current list of items.
356 * @return Return the current SSI permit deny setting, or 0 if no setting was found.
357 */
358faim_export int aim_ssi_getpermdeny(struct aim_ssi_item *list)
359{
360        struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PDINFO);
361        if (cur) {
362                aim_tlvlist_t *tlvlist = cur->data;
363                if (tlvlist) {
364                        aim_tlv_t *tlv = aim_gettlv(tlvlist, 0x00ca, 1);
365                        if (tlv && tlv->value)
366                                return aimutil_get8(tlv->value);
367                }
368        }
369        return 0;
370}
371
372/**
373 * Locally find the presence flag item, and return the setting.  The returned setting is a
374 * bitmask of the user flags that you are visible to.  See the AIM_FLAG_* #defines
375 * in aim.h
376 *
377 * @param list A pointer to the current list of items.
378 * @return Return the current visibility mask.
379 */
380faim_export fu32_t aim_ssi_getpresence(struct aim_ssi_item *list)
381{
382        struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS);
383        if (cur) {
384                aim_tlvlist_t *tlvlist = cur->data;
385                if (tlvlist) {
386                        aim_tlv_t *tlv = aim_gettlv(tlvlist, 0x00c9, 1);
387                        if (tlv && tlv->length)
388                                return aimutil_get32(tlv->value);
389                }
390        }
391        return 0xFFFFFFFF;
392}
393
394/**
395 * Locally find the alias of the given buddy.
396 *
397 * @param list A pointer to the current list of items.
398 * @param gn The group of the buddy.
399 * @param sn The name of the buddy.
400 * @return A pointer to a NULL terminated string that is the buddies
401 *         alias, or NULL if the buddy has no alias.  You should free
402 *         this returned value!
403 */
404faim_export char *aim_ssi_getalias(struct aim_ssi_item *list, const char *gn, const char *sn)
405{
406        struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, sn, AIM_SSI_TYPE_BUDDY);
407        if (cur) {
408                aim_tlvlist_t *tlvlist = cur->data;
409                if (tlvlist) {
410                        aim_tlv_t *tlv = aim_gettlv(tlvlist, 0x0131, 1);
411                        if (tlv && tlv->length) {
412                                char *alias = (char *)malloc((tlv->length+1)*sizeof(char));
413                                strncpy(alias, tlv->value, tlv->length);
414                                alias[tlv->length] = 0;
415                                return alias;
416                        }
417                }
418        }
419        return NULL;
420}
421
422/**
423 * Locally find if you are waiting for authorization for a buddy.
424 *
425 * @param list A pointer to the current list of items.
426 * @param gn The group of the buddy.
427 * @param sn The name of the buddy.
428 * @return A pointer to a NULL terminated string that is the buddies
429 *         alias, or NULL if the buddy has no alias.  You should free
430 *         this returned value!
431 */
432faim_export int aim_ssi_waitingforauth(struct aim_ssi_item *list, const char *gn, const char *sn)
433{
434        struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, sn, AIM_SSI_TYPE_BUDDY);
435        if (cur) {
436                aim_tlvlist_t *tlvlist = cur->data;
437                if (tlvlist)
438                        if (aim_gettlv(tlvlist, 0x0066, 1))
439                                return 1;
440        }
441        return 0;
442}
443
444/**
445 * If there are changes, then create temporary items and
446 * call addmoddel.
447 *
448 * @param sess The oscar session.
449 * @return Return 0 if no errors, otherwise return the error number.
450 */
451static int aim_ssi_sync(aim_session_t *sess)
452{
453        struct aim_ssi_item *cur1, *cur2;
454        struct aim_ssi_tmp *cur, *new;
455
456        if (!sess)
457                return -EINVAL;
458
459        /* If we're waiting for an ack, we shouldn't do anything else */
460        if (sess->ssi.waiting_for_ack)
461                return 0;
462
463        /*
464         * Compare the 2 lists and create an aim_ssi_tmp for each difference. 
465         * We should only send either additions, modifications, or deletions
466         * before waiting for an acknowledgement.  So first do deletions, then
467         * additions, then modifications.  Also, both the official and the local
468         * list should be in ascending numerical order for the group ID#s and the
469         * buddy ID#s, which makes things more efficient.  I think.
470         */
471
472        /* Additions */
473        if (!sess->ssi.pending) {
474                for (cur1=sess->ssi.local; cur1; cur1=cur1->next) {
475                        if (!aim_ssi_itemlist_find(sess->ssi.official, cur1->gid, cur1->bid)) {
476                                new = (struct aim_ssi_tmp *)malloc(sizeof(struct aim_ssi_tmp));
477                                new->action = AIM_CB_SSI_ADD;
478                                new->ack = 0xffff;
479                                new->name = NULL;
480                                new->item = cur1;
481                                new->next = NULL;
482                                if (sess->ssi.pending) {
483                                        for (cur=sess->ssi.pending; cur->next; cur=cur->next);
484                                        cur->next = new;
485                                } else
486                                        sess->ssi.pending = new;
487                        }
488                }
489        }
490
491        /* Deletions */
492        if (!sess->ssi.pending) {
493                for (cur1=sess->ssi.official; cur1; cur1=cur1->next) {
494                        if (!aim_ssi_itemlist_find(sess->ssi.local, cur1->gid, cur1->bid)) {
495                                new = (struct aim_ssi_tmp *)malloc(sizeof(struct aim_ssi_tmp));
496                                new->action = AIM_CB_SSI_DEL;
497                                new->ack = 0xffff;
498                                new->name = NULL;
499                                new->item = cur1;
500                                new->next = NULL;
501                                if (sess->ssi.pending) {
502                                        for (cur=sess->ssi.pending; cur->next; cur=cur->next);
503                                        cur->next = new;
504                                } else
505                                        sess->ssi.pending = new;
506                        }
507                }
508        }
509
510        /* Modifications */
511        if (!sess->ssi.pending) {
512                for (cur1=sess->ssi.local; cur1; cur1=cur1->next) {
513                        cur2 = aim_ssi_itemlist_find(sess->ssi.official, cur1->gid, cur1->bid);
514                        if (cur2 && (aim_ssi_itemlist_cmp(cur1, cur2))) {
515                                new = (struct aim_ssi_tmp *)malloc(sizeof(struct aim_ssi_tmp));
516                                new->action = AIM_CB_SSI_MOD;
517                                new->ack = 0xffff;
518                                new->name = NULL;
519                                new->item = cur1;
520                                new->next = NULL;
521                                if (sess->ssi.pending) {
522                                        for (cur=sess->ssi.pending; cur->next; cur=cur->next);
523                                        cur->next = new;
524                                } else
525                                        sess->ssi.pending = new;
526                        }
527                }
528        }
529
530        /* We're out of stuff to do, so tell the AIM servers we're done and exit */
531        if (!sess->ssi.pending) {
532                aim_ssi_modend(sess);
533                return 0;
534        }
535
536        /* Make sure we don't send anything else between now
537         * and when we receive the ack for the following operation */
538        sess->ssi.waiting_for_ack = 1;
539
540        /* Now go mail off our data and wait 4 to 6 weeks */
541        aim_ssi_addmoddel(sess);
542
543        return 0;
544}
545
546/**
547 * Free all SSI data.
548 *
549 * This doesn't remove it from the server, that's different.
550 *
551 * @param sess The oscar session.
552 * @return Return 0 if no errors, otherwise return the error number.
553 */
554static int aim_ssi_freelist(aim_session_t *sess)
555{
556        struct aim_ssi_item *cur, *del;
557        struct aim_ssi_tmp *curtmp, *deltmp;
558
559        cur = sess->ssi.official;
560        while (cur) {
561                del = cur;
562                cur = cur->next;
563                free(del->name);
564                aim_freetlvchain(&del->data);
565                free(del);
566        }
567
568        cur = sess->ssi.local;
569        while (cur) {
570                del = cur;
571                cur = cur->next;
572                free(del->name);
573                aim_freetlvchain(&del->data);
574                free(del);
575        }
576
577        curtmp = sess->ssi.pending;
578        while (curtmp) {
579                deltmp = curtmp;
580                curtmp = curtmp->next;
581                free(deltmp);
582        }
583
584        sess->ssi.numitems = 0;
585        sess->ssi.official = NULL;
586        sess->ssi.local = NULL;
587        sess->ssi.pending = NULL;
588        sess->ssi.timestamp = (time_t)0;
589
590        return 0;
591}
592
593/**
594 * Delete all SSI data.
595 *
596 * @param sess The oscar session.
597 * @return Return 0 if no errors, otherwise return the error number.
598 */
599faim_export int aim_ssi_deletelist(aim_session_t *sess)
600{
601        struct aim_ssi_item *cur, *del;
602
603        if (!sess)
604                return -EINVAL;
605
606        /* Free the local list */
607        cur = sess->ssi.local;
608        while (cur) {
609                del = cur;
610                cur = cur->next;
611                free(del->name);
612                aim_freetlvchain(&del->data);
613                free(del);
614        }
615        sess->ssi.local = NULL;
616
617        /* Sync our local list with the server list */
618        aim_ssi_sync(sess);
619
620        return 0;
621}
622
623/**
624 * This "cleans" the ssi list.  It does the following:
625 * 1) Makes sure all buddies, permits, and denies have names.
626 * 2) Makes sure that all buddies are in a group that exist.
627 * 3) Deletes any empty groups
628 *
629 * @param sess The oscar session.
630 * @return Return 0 if no errors, otherwise return the error number.
631 */
632faim_export int aim_ssi_cleanlist(aim_session_t *sess)
633{
634        struct aim_ssi_item *cur, *next;
635
636        if (!sess)
637                return -EINVAL;
638
639        /* Delete any buddies, permits, or denies with empty names. */
640        /* If there are any buddies directly in the master group, add them to a real group. */
641        /* DESTROY any buddies that are directly in the master group. */
642        /* Do the same for buddies that are in a non-existant group. */
643        /* This will kind of mess up if you hit the item limit, but this function isn't too critical */
644        cur = sess->ssi.local;
645        while (cur) {
646                next = cur->next;
647                if (!cur->name) {
648                        if (cur->type == AIM_SSI_TYPE_BUDDY)
649                                aim_ssi_delbuddy(sess, NULL, NULL);
650                        else if (cur->type == AIM_SSI_TYPE_PERMIT)
651                                aim_ssi_delpermit(sess, NULL);
652                        else if (cur->type == AIM_SSI_TYPE_DENY)
653                                aim_ssi_deldeny(sess, NULL);
654                } else if ((cur->type == AIM_SSI_TYPE_BUDDY) && ((cur->gid == 0x0000) || (!aim_ssi_itemlist_find(sess->ssi.local, cur->gid, 0x0000)))) {
655                        aim_ssi_addbuddy(sess, cur->name, "orphans", NULL, NULL, NULL, 0);
656                        aim_ssi_delbuddy(sess, cur->name, NULL);
657                }
658                cur = next;
659        }
660
661        /* Check if there are empty groups and delete them */
662        cur = sess->ssi.local;
663        while (cur) {
664                next = cur->next;
665                if (cur->type == AIM_SSI_TYPE_GROUP) {
666                        aim_tlv_t *tlv = aim_gettlv(cur->data, 0x00c8, 1);
667                        if (!tlv || !tlv->length)
668                                aim_ssi_itemlist_del(&sess->ssi.local, cur);
669                }
670                cur = next;
671        }
672
673        /* Check if the master group is empty */
674        if ((cur = aim_ssi_itemlist_find(sess->ssi.local, 0x0000, 0x0000)) && (!cur->data))
675                aim_ssi_itemlist_del(&sess->ssi.local, cur);
676
677        return 0;
678}
679
680/**
681 * Add a buddy to the list.
682 *
683 * @param sess The oscar session.
684 * @param name The name of the item.
685 * @param group The group of the item.
686 * @param alias The alias/nickname of the item, or NULL.
687 * @param comment The buddy comment for the item, or NULL.
688 * @param smsnum The locally assigned SMS number, or NULL.
689 * @return Return 0 if no errors, otherwise return the error number.
690 */
691faim_export int aim_ssi_addbuddy(aim_session_t *sess, const char *name, const char *group, const char *alias, const char *comment, const char *smsnum, int needauth)
692{
693        struct aim_ssi_item *parent;
694        aim_tlvlist_t *data = NULL;
695
696        if (!sess || !name || !group)
697                return -EINVAL;
698
699        /* Find the parent */
700        if (!(parent = aim_ssi_itemlist_finditem(sess->ssi.local, group, NULL, AIM_SSI_TYPE_GROUP))) {
701                /* Find the parent's parent (the master group) */
702                if (!(parent = aim_ssi_itemlist_find(sess->ssi.local, 0x0000, 0x0000)))
703                        if (!(parent = aim_ssi_itemlist_add(&sess->ssi.local, NULL, 0x0000, 0x0000, AIM_SSI_TYPE_GROUP, NULL)))
704                                return -ENOMEM;
705                /* Add the parent */
706                if (!(parent = aim_ssi_itemlist_add(&sess->ssi.local, group, 0xFFFF, 0x0000, AIM_SSI_TYPE_GROUP, NULL)))
707                        return -ENOMEM;
708
709                /* Modify the parent's parent (the master group) */
710                aim_ssi_itemlist_rebuildgroup(sess->ssi.local, NULL);
711        }
712
713        /* Create a TLV list for the new buddy */
714        if (needauth)
715                aim_addtlvtochain_noval(&data, 0x0066);
716        if (alias)
717                aim_addtlvtochain_raw(&data, 0x0131, strlen(alias), alias);
718        if (smsnum)
719                aim_addtlvtochain_raw(&data, 0x013a, strlen(smsnum), smsnum);
720        if (comment)
721                aim_addtlvtochain_raw(&data, 0x013c, strlen(comment), comment);
722
723        /* Add that bad boy */
724        aim_ssi_itemlist_add(&sess->ssi.local, name, parent->gid, 0xFFFF, AIM_SSI_TYPE_BUDDY, data);
725        aim_freetlvchain(&data);
726
727        /* Modify the parent group */
728        aim_ssi_itemlist_rebuildgroup(sess->ssi.local, group);
729
730        /* Sync our local list with the server list */
731        aim_ssi_sync(sess);
732
733        return 0;
734}
735
736/**
737 * Add a permit buddy to the list.
738 *
739 * @param sess The oscar session.
740 * @param name The name of the item..
741 * @return Return 0 if no errors, otherwise return the error number.
742 */
743faim_export int aim_ssi_addpermit(aim_session_t *sess, const char *name)
744{
745
746        if (!sess || !name)
747                return -EINVAL;
748
749        /* Add that bad boy */
750        aim_ssi_itemlist_add(&sess->ssi.local, name, 0x0000, 0xFFFF, AIM_SSI_TYPE_PERMIT, NULL);
751
752        /* Sync our local list with the server list */
753        aim_ssi_sync(sess);
754
755        return 0;
756}
757
758/**
759 * Add a deny buddy to the list.
760 *
761 * @param sess The oscar session.
762 * @param name The name of the item..
763 * @return Return 0 if no errors, otherwise return the error number.
764 */
765faim_export int aim_ssi_adddeny(aim_session_t *sess, const char *name)
766{
767
768        if (!sess || !name)
769                return -EINVAL;
770
771        /* Add that bad boy */
772        aim_ssi_itemlist_add(&sess->ssi.local, name, 0x0000, 0xFFFF, AIM_SSI_TYPE_DENY, NULL);
773
774        /* Sync our local list with the server list */
775        aim_ssi_sync(sess);
776
777        return 0;
778}
779
780/**
781 * Deletes a buddy from the list.
782 *
783 * @param sess The oscar session.
784 * @param name The name of the item, or NULL.
785 * @param group The group of the item, or NULL.
786 * @return Return 0 if no errors, otherwise return the error number.
787 */
788faim_export int aim_ssi_delbuddy(aim_session_t *sess, const char *name, const char *group)
789{
790        struct aim_ssi_item *del;
791
792        if (!sess)
793                return -EINVAL;
794
795        /* Find the buddy */
796        if (!(del = aim_ssi_itemlist_finditem(sess->ssi.local, group, name, AIM_SSI_TYPE_BUDDY)))
797                return -EINVAL;
798
799        /* Remove the item from the list */
800        aim_ssi_itemlist_del(&sess->ssi.local, del);
801
802        /* Modify the parent group */
803        aim_ssi_itemlist_rebuildgroup(sess->ssi.local, group);
804
805        /* Check if we should delete the parent group */
806        if ((del = aim_ssi_itemlist_finditem(sess->ssi.local, group, NULL, AIM_SSI_TYPE_GROUP)) && (!del->data)) {
807                aim_ssi_itemlist_del(&sess->ssi.local, del);
808
809                /* Modify the parent group */
810                aim_ssi_itemlist_rebuildgroup(sess->ssi.local, NULL);
811
812                /* Check if we should delete the parent's parent (the master group) */
813                if ((del = aim_ssi_itemlist_find(sess->ssi.local, 0x0000, 0x0000)) && (!del->data)) {
814                        aim_ssi_itemlist_del(&sess->ssi.local, del);
815                }
816        }
817
818        /* Sync our local list with the server list */
819        aim_ssi_sync(sess);
820
821        return 0;
822}
823
824/**
825 * Deletes a permit buddy from the list.
826 *
827 * @param sess The oscar session.
828 * @param name The name of the item, or NULL.
829 * @return Return 0 if no errors, otherwise return the error number.
830 */
831faim_export int aim_ssi_delpermit(aim_session_t *sess, const char *name)
832{
833        struct aim_ssi_item *del;
834
835        if (!sess)
836                return -EINVAL;
837
838        /* Find the item */
839        if (!(del = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, name, AIM_SSI_TYPE_PERMIT)))
840                return -EINVAL;
841
842        /* Remove the item from the list */
843        aim_ssi_itemlist_del(&sess->ssi.local, del);
844
845        /* Sync our local list with the server list */
846        aim_ssi_sync(sess);
847
848        return 0;
849}
850
851/**
852 * Deletes a deny buddy from the list.
853 *
854 * @param sess The oscar session.
855 * @param name The name of the item, or NULL.
856 * @return Return 0 if no errors, otherwise return the error number.
857 */
858faim_export int aim_ssi_deldeny(aim_session_t *sess, const char *name)
859{
860        struct aim_ssi_item *del;
861
862        if (!sess)
863                return -EINVAL;
864
865        /* Find the item */
866        if (!(del = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, name, AIM_SSI_TYPE_DENY)))
867                return -EINVAL;
868
869        /* Remove the item from the list */
870        aim_ssi_itemlist_del(&sess->ssi.local, del);
871
872        /* Sync our local list with the server list */
873        aim_ssi_sync(sess);
874
875        return 0;
876}
877
878/**
879 * Move a buddy from one group to another group.  This basically just deletes the
880 * buddy and re-adds it.
881 *
882 * @param sess The oscar session.
883 * @param oldgn The group that the buddy is currently in.
884 * @param newgn The group that the buddy should be moved in to.
885 * @param sn The name of the buddy to be moved.
886 * @return Return 0 if no errors, otherwise return the error number.
887 */
888faim_export int aim_ssi_movebuddy(aim_session_t *sess, const char *oldgn, const char *newgn, const char *sn)
889{
890        aim_ssi_addbuddy(sess, sn, newgn, aim_ssi_getalias(sess->ssi.local, oldgn, sn), NULL, NULL, aim_ssi_waitingforauth(sess->ssi.local, oldgn, sn));
891        aim_ssi_delbuddy(sess, sn, oldgn);
892        return 0;
893}
894
895/**
896 * Change the alias stored on the server for a given buddy.
897 *
898 * @param sess The oscar session.
899 * @param gn The group that the buddy is currently in.
900 * @param sn The screen name of the buddy.
901 * @param alias The new alias for the buddy.
902 * @return Return 0 if no errors, otherwise return the error number.
903 */
904faim_export int aim_ssi_aliasbuddy(aim_session_t *sess, const char *gn, const char *sn, const char *alias)
905{
906        struct aim_ssi_item *tmp;
907        aim_tlvlist_t *data = NULL;
908
909        if (!sess || !gn || !sn)
910                return -EINVAL;
911
912        if (!(tmp = aim_ssi_itemlist_finditem(sess->ssi.local, gn, sn, AIM_SSI_TYPE_BUDDY)))
913                return -EINVAL;
914
915        if (alias && !strlen(alias))
916                alias = NULL;
917
918        /* Need to add the x0131 TLV to the TLV chain */
919        if (alias)
920                aim_addtlvtochain_raw(&data, 0x0131, strlen(alias), alias);
921
922        aim_freetlvchain(&tmp->data);
923        tmp->data = data;
924
925        /* Sync our local list with the server list */
926        aim_ssi_sync(sess);
927
928        return 0;
929}
930
931/**
932 * Rename a group.
933 *
934 * @param sess The oscar session.
935 * @param oldgn The old group name.
936 * @param newgn The new group name.
937 * @return Return 0 if no errors, otherwise return the error number.
938 */
939faim_export int aim_ssi_rename_group(aim_session_t *sess, const char *oldgn, const char *newgn)
940{
941        struct aim_ssi_item *group;
942
943        if (!sess || !oldgn || !newgn)
944                return -EINVAL;
945
946        if (!(group = aim_ssi_itemlist_finditem(sess->ssi.local, oldgn, NULL, AIM_SSI_TYPE_GROUP)))
947                return -EINVAL;
948
949        free(group->name);
950        group->name = (char *)malloc((strlen(newgn)+1)*sizeof(char));
951        strcpy(group->name, newgn);
952
953        /* Sync our local list with the server list */
954        aim_ssi_sync(sess);
955
956        return 0;
957}
958
959/**
960 * Stores your permit/deny setting on the server, and starts using it.
961 *
962 * @param sess The oscar session.
963 * @param permdeny Your permit/deny setting.  Can be one of the following:
964 *        1 - Allow all users
965 *        2 - Block all users
966 *        3 - Allow only the users below
967 *        4 - Block only the users below
968 *        5 - Allow only users on my buddy list
969 * @param vismask A bitmask of the class of users to whom you want to be
970 *        visible.  See the AIM_FLAG_BLEH #defines in aim.h
971 * @return Return 0 if no errors, otherwise return the error number.
972 */
973faim_export int aim_ssi_setpermdeny(aim_session_t *sess, fu8_t permdeny, fu32_t vismask)
974{
975        struct aim_ssi_item *tmp;
976        aim_tlvlist_t *data = NULL;
977
978        if (!sess)
979                return -EINVAL;
980
981        /* Need to add the x00ca TLV to the TLV chain */
982        aim_addtlvtochain8(&data, 0x00ca, permdeny);
983
984        /* Need to add the x00cb TLV to the TLV chain */
985        aim_addtlvtochain32(&data, 0x00cb, vismask);
986
987        if ((tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, NULL, AIM_SSI_TYPE_PDINFO))) {
988                aim_freetlvchain(&tmp->data);
989                tmp->data = data;
990        } else {
991                tmp = aim_ssi_itemlist_add(&sess->ssi.local, NULL, 0x0000, 0xFFFF, AIM_SSI_TYPE_PDINFO, data);
992                aim_freetlvchain(&data);
993        }
994
995        /* Sync our local list with the server list */
996        aim_ssi_sync(sess);
997
998        return 0;
999}
1000
1001/**
1002 * Set buddy icon information
1003 *
1004 * @param sess The oscar session.
1005 * @param iconcsum The MD5 checksum of the icon you are using.
1006 * @param iconcsumlen Length of the MD5 checksum given above.  Should be 0x10 bytes.
1007 * @return Return 0 if no errors, otherwise return the error number.
1008 */
1009faim_export int aim_ssi_seticon(aim_session_t *sess, fu8_t *iconsum, fu16_t iconsumlen)
1010{
1011        struct aim_ssi_item *tmp;
1012        aim_tlvlist_t *data = NULL;
1013        fu8_t *csumdata;
1014
1015        if (!sess || !iconsum || !iconsumlen)
1016                return -EINVAL;
1017
1018        if (!(csumdata = (fu8_t *)malloc((iconsumlen+2)*sizeof(fu8_t))))
1019                return -ENOMEM;
1020        csumdata[0] = 0x00;
1021        csumdata[1] = 0x10;
1022        memcpy(&csumdata[2], iconsum, iconsumlen);
1023
1024        /* Need to add the x00d5 TLV to the TLV chain */
1025        aim_addtlvtochain_raw(&data, 0x00d5, (iconsumlen+2) * sizeof(fu8_t), csumdata);
1026
1027        /* This TLV is added to cache the icon. */
1028        aim_addtlvtochain_noval(&data, 0x0131);
1029
1030        if ((tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, "1", AIM_SSI_TYPE_ICONINFO))) {
1031                /* If the new tlvchain and oldtlvchain are the same, then do nothing */
1032                if (!aim_tlvlist_cmp(tmp->data, data)) {
1033                        /* The new tlvlist is the identical to the old one */
1034                        aim_freetlvchain(&data);
1035                        free(csumdata);
1036                        return 0;
1037                }
1038                aim_freetlvchain(&tmp->data);
1039                tmp->data = data;
1040        } else {
1041                tmp = aim_ssi_itemlist_add(&sess->ssi.local, "1", 0x0000, 0x51F4, AIM_SSI_TYPE_ICONINFO, data);
1042                aim_freetlvchain(&data);
1043        }
1044
1045        /* Sync our local list with the server list */
1046        aim_ssi_sync(sess);
1047        free(csumdata);
1048        return 0;
1049}
1050
1051/**
1052 * Stores your setting for whether you should show up as idle or not.
1053 *
1054 * @param sess The oscar session.
1055 * @param presence I think it's a bitmask, but I only know what one of the bits is:
1056 *        0x00000400 - Allow others to see your idle time
1057 * @return Return 0 if no errors, otherwise return the error number.
1058 */
1059faim_export int aim_ssi_setpresence(aim_session_t *sess, fu32_t presence) {
1060        struct aim_ssi_item *tmp;
1061        aim_tlvlist_t *data = NULL;
1062
1063        if (!sess)
1064                return -EINVAL;
1065
1066        /* Need to add the x00c9 TLV to the TLV chain */
1067        aim_addtlvtochain32(&data, 0x00c9, presence);
1068
1069        if ((tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS))) {
1070                aim_freetlvchain(&tmp->data);
1071                tmp->data = data;
1072        } else {
1073                tmp = aim_ssi_itemlist_add(&sess->ssi.local, NULL, 0x0000, 0xFFFF, AIM_SSI_TYPE_PRESENCEPREFS, data);
1074                aim_freetlvchain(&data);
1075        }
1076
1077        /* Sync our local list with the server list */
1078        aim_ssi_sync(sess);
1079
1080        return 0;
1081}
1082
1083/*
1084 * Subtype 0x0002 - Request SSI Rights.
1085 */
1086faim_export int aim_ssi_reqrights(aim_session_t *sess)
1087{
1088        aim_conn_t *conn;
1089
1090        if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)))
1091                return -EINVAL;
1092
1093        return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_REQRIGHTS);
1094}
1095
1096/*
1097 * Subtype 0x0003 - SSI Rights Information.
1098 */
1099static int parserights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
1100{
1101        int ret = 0, i;
1102        aim_rxcallback_t userfunc;
1103        aim_tlvlist_t *tlvlist;
1104        aim_tlv_t *tlv;
1105        aim_bstream_t bstream;
1106        fu16_t *maxitems;
1107
1108        /* This SNAC is made up of a bunch of TLVs */
1109        tlvlist = aim_readtlvchain(bs);
1110
1111        /* TLV 0x0004 contains the maximum number of each item */
1112        if (!(tlv = aim_gettlv(tlvlist, 0x0004, 1))) {
1113                aim_freetlvchain(&tlvlist);
1114                return 0;
1115        }
1116
1117        aim_bstream_init(&bstream, tlv->value, tlv->length);
1118
1119        if (!(maxitems = (fu16_t *)malloc((tlv->length/2)*sizeof(fu16_t)))) {
1120                aim_freetlvchain(&tlvlist);
1121                return 0;
1122        }
1123
1124        for (i=0; i<(tlv->length/2); i++)
1125                maxitems[i] = aimbs_get16(&bstream);
1126
1127        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
1128                ret = userfunc(sess, rx, tlv->length/2, maxitems);
1129
1130        aim_freetlvchain(&tlvlist);
1131        free(maxitems);
1132
1133        return ret;
1134}
1135
1136/*
1137 * Subtype 0x0004 - Request SSI Data when you don't have a timestamp and
1138 * revision number.
1139 *
1140 */
1141faim_export int aim_ssi_reqdata(aim_session_t *sess)
1142{
1143        aim_conn_t *conn;
1144
1145        if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)))
1146                return -EINVAL;
1147
1148        /* Free any current data, just in case */
1149        aim_ssi_freelist(sess);
1150
1151        return aim_genericreq_n_snacid(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_REQDATA);
1152}
1153
1154/*
1155 * Subtype 0x0005 - Request SSI Data when you have a timestamp and revision
1156 * number.
1157 *
1158 * The data will only be sent if it is newer than the posted local
1159 * timestamp and revision.
1160 *
1161 * Note that the client should never increment the revision, only the server.
1162 *
1163 */
1164faim_export int aim_ssi_reqifchanged(aim_session_t *sess, time_t timestamp, fu16_t numitems)
1165{
1166        aim_conn_t *conn;
1167        aim_frame_t *fr;
1168        aim_snacid_t snacid;
1169
1170        if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)))
1171                return -EINVAL;
1172
1173        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4+2)))
1174                return -ENOMEM;
1175
1176        snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_REQIFCHANGED, 0x0000, NULL, 0);
1177
1178        aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_REQIFCHANGED, 0x0000, snacid);
1179        aimbs_put32(&fr->data, timestamp);
1180        aimbs_put16(&fr->data, numitems);
1181
1182        aim_tx_enqueue(sess, fr);
1183
1184        /* Free any current data, just in case */
1185        aim_ssi_freelist(sess);
1186
1187        return 0;
1188}
1189
1190/*
1191 * Subtype 0x0006 - SSI Data.
1192 */
1193static int parsedata(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
1194{
1195        int ret = 0;
1196        aim_rxcallback_t userfunc;
1197        fu8_t fmtver; /* guess */
1198        fu16_t namelen, gid, bid, type;
1199        char *name;
1200        aim_tlvlist_t *data;
1201
1202        fmtver = aimbs_get8(bs); /* Version of ssi data.  Should be 0x00 */
1203        sess->ssi.numitems += aimbs_get16(bs); /* # of items in this SSI SNAC */
1204
1205        /* Read in the list */
1206        while (aim_bstream_empty(bs) > 4) { /* last four bytes are timestamp */
1207                if ((namelen = aimbs_get16(bs)))
1208                        name = aimbs_getstr(bs, namelen);
1209                else
1210                        name = NULL;
1211                gid = aimbs_get16(bs);
1212                bid = aimbs_get16(bs);
1213                type = aimbs_get16(bs);
1214                data = aim_readtlvchain_len(bs, aimbs_get16(bs));
1215                aim_ssi_itemlist_add(&sess->ssi.official, name, gid, bid, type, data);
1216                free(name);
1217                aim_freetlvchain(&data);
1218        }
1219
1220        /* Read in the timestamp */
1221        sess->ssi.timestamp = aimbs_get32(bs);
1222
1223        if (!(snac->flags & 0x0001)) {
1224                /* Make a copy of the list */
1225                struct aim_ssi_item *cur;
1226                for (cur=sess->ssi.official; cur; cur=cur->next)
1227                        aim_ssi_itemlist_add(&sess->ssi.local, cur->name, cur->gid, cur->bid, cur->type, cur->data);
1228
1229                sess->ssi.received_data = 1;
1230
1231                if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
1232                        ret = userfunc(sess, rx, fmtver, sess->ssi.numitems, sess->ssi.official, sess->ssi.timestamp);
1233        }
1234
1235        return ret;
1236}
1237
1238/*
1239 * Subtype 0x0007 - SSI Activate Data.
1240 *
1241 * Should be sent after receiving 13/6 or 13/f to tell the server you
1242 * are ready to begin using the list.  It will promptly give you the
1243 * presence information for everyone in your list and put your permit/deny
1244 * settings into effect.
1245 *
1246 */
1247faim_export int aim_ssi_enable(aim_session_t *sess)
1248{
1249        aim_conn_t *conn;
1250
1251        if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)))
1252                return -EINVAL;
1253
1254        return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, 0x0007);
1255}
1256
1257/*
1258 * Subtype 0x0008/0x0009/0x000a - SSI Add/Mod/Del Item(s).
1259 *
1260 * Sends the SNAC to add, modify, or delete an item from the server-stored
1261 * information.  These 3 SNACs all have an identical structure.  The only
1262 * difference is the subtype that is set for the SNAC.
1263 *
1264 */
1265faim_export int aim_ssi_addmoddel(aim_session_t *sess)
1266{
1267        aim_conn_t *conn;
1268        aim_frame_t *fr;
1269        aim_snacid_t snacid;
1270        int snaclen;
1271        struct aim_ssi_tmp *cur;
1272
1273        if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)) || !sess->ssi.pending || !sess->ssi.pending->item)
1274                return -EINVAL;
1275
1276        /* Calculate total SNAC size */
1277        snaclen = 10; /* For family, subtype, flags, and SNAC ID */
1278        for (cur=sess->ssi.pending; cur; cur=cur->next) {
1279                snaclen += 10; /* For length, GID, BID, type, and length */
1280                if (cur->item->name)
1281                        snaclen += strlen(cur->item->name);
1282                if (cur->item->data)
1283                        snaclen += aim_sizetlvchain(&cur->item->data);
1284        }
1285
1286        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, snaclen)))
1287                return -ENOMEM;
1288
1289        snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, sess->ssi.pending->action, 0x0000, NULL, 0);
1290        aim_putsnac(&fr->data, AIM_CB_FAM_SSI, sess->ssi.pending->action, 0x0000, snacid);
1291
1292        for (cur=sess->ssi.pending; cur; cur=cur->next) {
1293                aimbs_put16(&fr->data, cur->item->name ? strlen(cur->item->name) : 0);
1294                if (cur->item->name)
1295                        aimbs_putraw(&fr->data, cur->item->name, strlen(cur->item->name));
1296                aimbs_put16(&fr->data, cur->item->gid);
1297                aimbs_put16(&fr->data, cur->item->bid);
1298                aimbs_put16(&fr->data, cur->item->type);
1299                aimbs_put16(&fr->data, cur->item->data ? aim_sizetlvchain(&cur->item->data) : 0);
1300                if (cur->item->data)
1301                        aim_writetlvchain(&fr->data, &cur->item->data);
1302        }
1303
1304        aim_tx_enqueue(sess, fr);
1305
1306        return 0;
1307}
1308
1309/*
1310 * Subtype 0x0008 - Incoming SSI add.
1311 *
1312 * XXX - It would probably be good for the client to actually do something when it gets this.
1313 */
1314static int parseadd(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
1315{
1316        int ret = 0;
1317        aim_rxcallback_t userfunc;
1318        char *name;
1319        fu16_t len, gid, bid, type;
1320        aim_tlvlist_t *data;
1321
1322        while (aim_bstream_empty(bs)) {
1323                if ((len = aimbs_get16(bs)))
1324                        name = aimbs_getstr(bs, len);
1325                else
1326                        name = NULL;
1327                gid = aimbs_get16(bs);
1328                bid = aimbs_get16(bs);
1329                type = aimbs_get16(bs);
1330                if ((len = aimbs_get16(bs)))
1331                        data = aim_readtlvchain_len(bs, len);
1332                else
1333                        data = NULL;
1334
1335                aim_ssi_itemlist_add(&sess->ssi.local, name, gid, bid, type, data);
1336                aim_ssi_itemlist_add(&sess->ssi.official, name, gid, bid, type, data);
1337                free(name);
1338                aim_freetlvchain(&data);
1339
1340                if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
1341                        ret = userfunc(sess, rx);
1342
1343                free(name);
1344        }
1345
1346        return ret;
1347}
1348
1349/*
1350 * Subtype 0x0009 - Incoming SSI mod.
1351 *
1352 * XXX - It would probably be good for the client to actually do something when it gets this.
1353 */
1354static int parsemod(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
1355{
1356        int ret = 0;
1357        aim_rxcallback_t userfunc;
1358        char *name;
1359        fu16_t len, gid, bid, type;
1360        aim_tlvlist_t *data;
1361        struct aim_ssi_item *item;
1362
1363        while (aim_bstream_empty(bs)) {
1364                if ((len = aimbs_get16(bs)))
1365                        name = aimbs_getstr(bs, len);
1366                else
1367                        name = NULL;
1368                gid = aimbs_get16(bs);
1369                bid = aimbs_get16(bs);
1370                type = aimbs_get16(bs);
1371                if ((len = aimbs_get16(bs)))
1372                        data = aim_readtlvchain_len(bs, len);
1373                else
1374                        data = NULL;
1375
1376                /* Replace the 2 local items with the given one */
1377                if ((item = aim_ssi_itemlist_find(sess->ssi.local, gid, bid))) {
1378                        item->type = type;
1379                        free(item->name);
1380                        if (name) {
1381                                item->name = (char *)malloc((strlen(name)+1)*sizeof(char));
1382                                strcpy(item->name, name);
1383                        } else
1384                                item->name = NULL;
1385                        aim_freetlvchain(&item->data);
1386                        item->data = aim_tlvlist_copy(data);
1387                }
1388
1389                if ((item = aim_ssi_itemlist_find(sess->ssi.official, gid, bid))) {
1390                        item->type = type;
1391                        free(item->name);
1392                        if (name) {
1393                                item->name = (char *)malloc((strlen(name)+1)*sizeof(char));
1394                                strcpy(item->name, name);
1395                        } else
1396                                item->name = NULL;
1397                        aim_freetlvchain(&item->data);
1398                        item->data = aim_tlvlist_copy(data);
1399                }
1400
1401                if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
1402                        ret = userfunc(sess, rx);
1403
1404                free(name);
1405                aim_freetlvchain(&data);
1406        }
1407
1408        return ret;
1409}
1410
1411/*
1412 * Subtype 0x000a - Incoming SSI del.
1413 *
1414 * XXX - It would probably be good for the client to actually do something when it gets this.
1415 */
1416static int parsedel(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
1417{
1418        int ret = 0;
1419        aim_rxcallback_t userfunc;
1420        fu16_t gid, bid;
1421        struct aim_ssi_item *del;
1422
1423        while (aim_bstream_empty(bs)) {
1424                aim_bstream_advance(bs, aimbs_get16(bs));
1425                gid = aimbs_get16(bs);
1426                bid = aimbs_get16(bs);
1427                aimbs_get16(bs);
1428                aim_bstream_advance(bs, aimbs_get16(bs));
1429
1430                if ((del = aim_ssi_itemlist_find(sess->ssi.local, gid, bid)))
1431                        aim_ssi_itemlist_del(&sess->ssi.local, del);
1432                if ((del = aim_ssi_itemlist_find(sess->ssi.official, gid, bid)))
1433                        aim_ssi_itemlist_del(&sess->ssi.official, del);
1434
1435                if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
1436                        ret = userfunc(sess, rx);
1437        }
1438
1439        return ret;
1440}
1441
1442/*
1443 * Subtype 0x000e - SSI Add/Mod/Del Ack.
1444 *
1445 * Response to add, modify, or delete SNAC (sent with aim_ssi_addmoddel).
1446 *
1447 */
1448static int parseack(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
1449{
1450        int ret = 0;
1451        aim_rxcallback_t userfunc;
1452        struct aim_ssi_tmp *cur, *del;
1453
1454        /* Read in the success/failure flags from the ack SNAC */
1455        cur = sess->ssi.pending;
1456        while (cur && (aim_bstream_empty(bs)>0)) {
1457                cur->ack = aimbs_get16(bs);
1458                cur = cur->next;
1459        }
1460
1461        /*
1462         * If outcome is 0, then add the item to the item list, or replace the other item,
1463         * or remove the old item.  If outcome is non-zero, then remove the item from the
1464         * local list, or unmodify it, or add it.
1465         */
1466        for (cur=sess->ssi.pending; (cur && (cur->ack != 0xffff)); cur=cur->next) {
1467        if (cur->item) {
1468                if (cur->ack) {
1469                        /* Our action was unsuccessful, so change the local list back to how it was */
1470                        if (cur->action == AIM_CB_SSI_ADD) {
1471                                /* Remove the item from the local list */
1472                                /* Make sure cur->item is still valid memory */
1473                                if (aim_ssi_itemlist_valid(sess->ssi.local, cur->item)) {
1474                                        if (cur->item->name) {
1475                                                cur->name = (char *)malloc((strlen(cur->item->name)+1)*sizeof(char));
1476                                                strcpy(cur->name, cur->item->name);
1477                                        }
1478                                        aim_ssi_itemlist_del(&sess->ssi.local, cur->item);
1479                                }
1480                                cur->item = NULL;
1481
1482                        } else if (cur->action == AIM_CB_SSI_MOD) {
1483                                /* Replace the local item with the item from the official list */
1484                                if (aim_ssi_itemlist_valid(sess->ssi.local, cur->item)) {
1485                                        struct aim_ssi_item *cur1;
1486                                        if ((cur1 = aim_ssi_itemlist_find(sess->ssi.official, cur->item->gid, cur->item->bid))) {
1487                                                free(cur->item->name);
1488                                                if (cur1->name) {
1489                                                        cur->item->name = (char *)malloc((strlen(cur1->name)+1)*sizeof(char));
1490                                                        strcpy(cur->item->name, cur1->name);
1491                                                } else
1492                                                        cur->item->name = NULL;
1493                                                aim_freetlvchain(&cur->item->data);
1494                                                cur->item->data = aim_tlvlist_copy(cur1->data);
1495                                        }
1496                                } else
1497                                        cur->item = NULL;
1498
1499                        } else if (cur->action == AIM_CB_SSI_DEL) {
1500                                /* Add the item back into the local list */
1501                                if (aim_ssi_itemlist_valid(sess->ssi.official, cur->item)) {
1502                                        aim_ssi_itemlist_add(&sess->ssi.local, cur->item->name, cur->item->gid, cur->item->bid, cur->item->type, cur->item->data);
1503                                } else
1504                                        cur->item = NULL;
1505                        }
1506
1507                } else {
1508                        /* Do the exact opposite */
1509                        if (cur->action == AIM_CB_SSI_ADD) {
1510                        /* Add the local item to the official list */
1511                                if (aim_ssi_itemlist_valid(sess->ssi.local, cur->item)) {
1512                                        aim_ssi_itemlist_add(&sess->ssi.official, cur->item->name, cur->item->gid, cur->item->bid, cur->item->type, cur->item->data);
1513                                } else
1514                                        cur->item = NULL;
1515
1516                        } else if (cur->action == AIM_CB_SSI_MOD) {
1517                                /* Replace the official item with the item from the local list */
1518                                if (aim_ssi_itemlist_valid(sess->ssi.local, cur->item)) {
1519                                        struct aim_ssi_item *cur1;
1520                                        if ((cur1 = aim_ssi_itemlist_find(sess->ssi.official, cur->item->gid, cur->item->bid))) {
1521                                                free(cur1->name);
1522                                                if (cur->item->name) {
1523                                                        cur1->name = (char *)malloc((strlen(cur->item->name)+1)*sizeof(char));
1524                                                        strcpy(cur1->name, cur->item->name);
1525                                                } else
1526                                                        cur1->name = NULL;
1527                                                aim_freetlvchain(&cur1->data);
1528                                                cur1->data = aim_tlvlist_copy(cur->item->data);
1529                                        }
1530                                } else
1531                                        cur->item = NULL;
1532
1533                        } else if (cur->action == AIM_CB_SSI_DEL) {
1534                                /* Remove the item from the official list */
1535                                if (aim_ssi_itemlist_valid(sess->ssi.official, cur->item))
1536                                        aim_ssi_itemlist_del(&sess->ssi.official, cur->item);
1537                                cur->item = NULL;
1538                        }
1539
1540                }
1541        } /* End if (cur->item) */
1542        } /* End for loop */
1543
1544        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
1545                ret = userfunc(sess, rx, sess->ssi.pending);
1546
1547        /* Free all aim_ssi_tmp's with an outcome */
1548        cur = sess->ssi.pending;
1549        while (cur && (cur->ack != 0xffff)) {
1550                del = cur;
1551                cur = cur->next;
1552                free(del->name);
1553                free(del);
1554        }
1555        sess->ssi.pending = cur;
1556
1557        /* If we're not waiting for any more acks, then send more SNACs */
1558        if (!sess->ssi.pending) {
1559                sess->ssi.pending = NULL;
1560                sess->ssi.waiting_for_ack = 0;
1561                aim_ssi_sync(sess);
1562        }
1563
1564        return ret;
1565}
1566
1567/*
1568 * Subtype 0x000f - SSI Data Unchanged.
1569 *
1570 * Response to aim_ssi_reqifchanged() if the server-side data is not newer than
1571 * posted local stamp/revision.
1572 *
1573 */
1574static int parsedataunchanged(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
1575{
1576        int ret = 0;
1577        aim_rxcallback_t userfunc;
1578
1579        sess->ssi.received_data = 1;
1580
1581        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
1582                ret = userfunc(sess, rx);
1583
1584        return ret;
1585}
1586
1587/*
1588 * Subtype 0x0011 - SSI Begin Data Modification.
1589 *
1590 * Tells the server you're going to start modifying data.
1591 *
1592 */
1593faim_export int aim_ssi_modbegin(aim_session_t *sess)
1594{
1595        aim_conn_t *conn;
1596
1597        if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)))
1598                return -EINVAL;
1599
1600        return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_EDITSTART);
1601}
1602
1603/*
1604 * Subtype 0x0012 - SSI End Data Modification.
1605 *
1606 * Tells the server you're finished modifying data.
1607 *
1608 */
1609faim_export int aim_ssi_modend(aim_session_t *sess)
1610{
1611        aim_conn_t *conn;
1612
1613        if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)))
1614                return -EINVAL;
1615
1616        return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_EDITSTOP);
1617}
1618
1619/*
1620 * Subtype 0x0014 - Grant authorization
1621 *
1622 * Authorizes a contact so they can add you to their contact list.
1623 *
1624 */
1625faim_export int aim_ssi_sendauth(aim_session_t *sess, char *sn, char *msg)
1626{
1627        aim_conn_t *conn;
1628        aim_frame_t *fr;
1629        aim_snacid_t snacid;
1630
1631        if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)) || !sn)
1632                return -EINVAL;
1633
1634        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)+2+(msg ? strlen(msg)+1 : 0)+2)))
1635                return -ENOMEM;
1636
1637        snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTH, 0x0000, NULL, 0);
1638        aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTH, 0x0000, snacid);
1639
1640        /* Screen name */
1641        aimbs_put8(&fr->data, strlen(sn));
1642        aimbs_putraw(&fr->data, sn, strlen(sn));
1643
1644        /* Message (null terminated) */
1645        aimbs_put16(&fr->data, msg ? strlen(msg) : 0);
1646        if (msg) {
1647                aimbs_putraw(&fr->data, msg, strlen(msg));
1648                aimbs_put8(&fr->data, 0x00);
1649        }
1650
1651        /* Unknown */
1652        aimbs_put16(&fr->data, 0x0000);
1653
1654        aim_tx_enqueue(sess, fr);
1655
1656        return 0;
1657}
1658
1659/*
1660 * Subtype 0x0015 - Receive an authorization grant
1661 */
1662static int receiveauthgrant(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
1663{
1664        int ret = 0;
1665        aim_rxcallback_t userfunc;
1666        fu16_t tmp;
1667        char *sn, *msg;
1668
1669        /* Read screen name */
1670        if ((tmp = aimbs_get8(bs)))
1671                sn = aimbs_getstr(bs, tmp);
1672        else
1673                sn = NULL;
1674
1675        /* Read message (null terminated) */
1676        if ((tmp = aimbs_get16(bs)))
1677                msg = aimbs_getstr(bs, tmp);
1678        else
1679                msg = NULL;
1680
1681        /* Unknown */
1682        tmp = aimbs_get16(bs);
1683
1684        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
1685                ret = userfunc(sess, rx, sn, msg);
1686
1687        free(sn);
1688        free(msg);
1689
1690        return ret;
1691}
1692
1693/*
1694 * Subtype 0x0018 - Send authorization request
1695 *
1696 * Sends a request for authorization to the given contact.  The request will either be
1697 * granted, denied, or dropped.
1698 *
1699 */
1700faim_export int aim_ssi_sendauthrequest(aim_session_t *sess, char *sn, char *msg)
1701{
1702        aim_conn_t *conn;
1703        aim_frame_t *fr;
1704        aim_snacid_t snacid;
1705
1706        if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)) || !sn)
1707                return -EINVAL;
1708
1709        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)+2+(msg ? strlen(msg)+1 : 0)+2)))
1710                return -ENOMEM;
1711
1712        snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREQ, 0x0000, NULL, 0);
1713        aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREQ, 0x0000, snacid);
1714
1715        /* Screen name */
1716        aimbs_put8(&fr->data, strlen(sn));
1717        aimbs_putraw(&fr->data, sn, strlen(sn));
1718
1719        /* Message (null terminated) */
1720        aimbs_put16(&fr->data, msg ? strlen(msg) : 0);
1721        if (msg) {
1722                aimbs_putraw(&fr->data, msg, strlen(msg));
1723                aimbs_put8(&fr->data, 0x00);
1724        }
1725
1726        /* Unknown */
1727        aimbs_put16(&fr->data, 0x0000);
1728
1729        aim_tx_enqueue(sess, fr);
1730
1731        return 0;
1732}
1733
1734/*
1735 * Subtype 0x0019 - Receive an authorization request
1736 */
1737static int receiveauthrequest(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
1738{
1739        int ret = 0;
1740        aim_rxcallback_t userfunc;
1741        fu16_t tmp;
1742        char *sn, *msg;
1743
1744        /* Read screen name */
1745        if ((tmp = aimbs_get8(bs)))
1746                sn = aimbs_getstr(bs, tmp);
1747        else
1748                sn = NULL;
1749
1750        /* Read message (null terminated) */
1751        if ((tmp = aimbs_get16(bs)))
1752                msg = aimbs_getstr(bs, tmp);
1753        else
1754                msg = NULL;
1755
1756        /* Unknown */
1757        tmp = aimbs_get16(bs);
1758
1759        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
1760                ret = userfunc(sess, rx, sn, msg);
1761
1762        free(sn);
1763        free(msg);
1764
1765        return ret;
1766}
1767
1768/*
1769 * Subtype 0x001a - Send authorization reply
1770 *
1771 * Sends a reply to a request for authorization.  The reply can either
1772 * grant authorization or deny authorization.
1773 *
1774 * if reply=0x00 then deny
1775 * if reply=0x01 then grant
1776 *
1777 */
1778faim_export int aim_ssi_sendauthreply(aim_session_t *sess, char *sn, fu8_t reply, char *msg)
1779{
1780        aim_conn_t *conn;
1781        aim_frame_t *fr;
1782        aim_snacid_t snacid;
1783
1784        if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)) || !sn)
1785                return -EINVAL;
1786
1787        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 1+strlen(sn) + 1 + 2+(msg ? strlen(msg)+1 : 0) + 2)))
1788                return -ENOMEM;
1789
1790        snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREP, 0x0000, NULL, 0);
1791        aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREP, 0x0000, snacid);
1792
1793        /* Screen name */
1794        aimbs_put8(&fr->data, strlen(sn));
1795        aimbs_putraw(&fr->data, sn, strlen(sn));
1796
1797        /* Grant or deny */
1798        aimbs_put8(&fr->data, reply);
1799
1800        /* Message (null terminated) */
1801        aimbs_put16(&fr->data, msg ? (strlen(msg)+1) : 0);
1802        if (msg) {
1803                aimbs_putraw(&fr->data, msg, strlen(msg));
1804                aimbs_put8(&fr->data, 0x00);
1805        }
1806
1807        /* Unknown */
1808        aimbs_put16(&fr->data, 0x0000);
1809
1810        aim_tx_enqueue(sess, fr);
1811
1812        return 0;
1813}
1814
1815/*
1816 * Subtype 0x001b - Receive an authorization reply
1817 * You get this bad boy when other people respond to the authorization
1818 * request that you have previously sent them.
1819 */
1820static int receiveauthreply(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
1821{
1822        int ret = 0;
1823        aim_rxcallback_t userfunc;
1824        fu16_t tmp;
1825        fu8_t reply;
1826        char *sn, *msg;
1827
1828        /* Read screen name */
1829        if ((tmp = aimbs_get8(bs)))
1830                sn = aimbs_getstr(bs, tmp);
1831        else
1832                sn = NULL;
1833
1834        /* Read reply */
1835        reply = aimbs_get8(bs);
1836
1837        /* Read message (null terminated) */
1838        if ((tmp = aimbs_get16(bs)))
1839                msg = aimbs_getstr(bs, tmp);
1840        else
1841                msg = NULL;
1842
1843        /* Unknown */
1844        tmp = aimbs_get16(bs);
1845
1846        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
1847                ret = userfunc(sess, rx, sn, reply, msg);
1848
1849        free(sn);
1850        free(msg);
1851
1852        return ret;
1853}
1854
1855/*
1856 * Subtype 0x001c - Receive a message telling you someone added you to their list.
1857 */
1858static int receiveadded(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
1859{
1860        int ret = 0;
1861        aim_rxcallback_t userfunc;
1862        fu16_t tmp;
1863        char *sn;
1864
1865        /* Read screen name */
1866        if ((tmp = aimbs_get8(bs)))
1867                sn = aimbs_getstr(bs, tmp);
1868        else
1869                sn = NULL;
1870
1871        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
1872                ret = userfunc(sess, rx, sn);
1873
1874        free(sn);
1875
1876        return ret;
1877}
1878
1879static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
1880{
1881
1882        if (snac->subtype == AIM_CB_SSI_RIGHTSINFO)
1883                return parserights(sess, mod, rx, snac, bs);
1884        else if (snac->subtype == AIM_CB_SSI_LIST)
1885                return parsedata(sess, mod, rx, snac, bs);
1886        else if (snac->subtype == AIM_CB_SSI_ADD)
1887                return parseadd(sess, mod, rx, snac, bs);
1888        else if (snac->subtype == AIM_CB_SSI_MOD)
1889                return parsemod(sess, mod, rx, snac, bs);
1890        else if (snac->subtype == AIM_CB_SSI_DEL)
1891                return parsedel(sess, mod, rx, snac, bs);
1892        else if (snac->subtype == AIM_CB_SSI_SRVACK)
1893                return parseack(sess, mod, rx, snac, bs);
1894        else if (snac->subtype == AIM_CB_SSI_NOLIST)
1895                return parsedataunchanged(sess, mod, rx, snac, bs);
1896        else if (snac->subtype == AIM_CB_SSI_RECVAUTH)
1897                return receiveauthgrant(sess, mod, rx, snac, bs);
1898        else if (snac->subtype == AIM_CB_SSI_RECVAUTHREQ)
1899                return receiveauthrequest(sess, mod, rx, snac, bs);
1900        else if (snac->subtype == AIM_CB_SSI_RECVAUTHREP)
1901                return receiveauthreply(sess, mod, rx, snac, bs);
1902        else if (snac->subtype == AIM_CB_SSI_ADDED)
1903                return receiveadded(sess, mod, rx, snac, bs);
1904
1905        return 0;
1906}
1907
1908static void ssi_shutdown(aim_session_t *sess, aim_module_t *mod)
1909{
1910        aim_ssi_freelist(sess);
1911
1912        return;
1913}
1914
1915faim_internal int ssi_modfirst(aim_session_t *sess, aim_module_t *mod)
1916{
1917
1918        mod->family = AIM_CB_FAM_SSI;
1919        mod->version = 0x0004;
1920        mod->toolid = 0x0110;
1921        mod->toolversion = 0x0629;
1922        mod->flags = 0;
1923        strncpy(mod->name, "ssi", sizeof(mod->name));
1924        mod->snachandler = snachandler;
1925        mod->shutdown = ssi_shutdown;
1926
1927        return 0;
1928}
Note: See TracBrowser for help on using the repository browser.