source: libfaim/ssi.c @ 601733d

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