source: libfaim/ssi.c @ fe6f1d3

barnowl_perlaimdebianowlrelease-1.10release-1.4release-1.5release-1.6release-1.7release-1.8release-1.9
Last change on this file since fe6f1d3 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
RevLine 
[5e53c4a]1/*
[e374dee]2 * Family 0x0013 - Server-Side/Stored Information.
[5e53c4a]3 *
[e374dee]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.
[5e53c4a]7 *
[e374dee]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).
[862371b]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 *
[5e53c4a]21 * This is entirely too complicated.
[862371b]22 * You don't know the half of it.
23 *
[e374dee]24 * XXX - Preserve unknown data in TLV lists
[5e53c4a]25 *
26 */
27
28#define FAIM_INTERNAL
29#include <aim.h>
30
[862371b]31/**
[e374dee]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/**
[862371b]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.
[e374dee]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.
[862371b]102 */
[e374dee]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)
[862371b]104{
105        int i;
[e374dee]106        struct aim_ssi_item *cur, *new;
107
108        if (!list)
109                return NULL;
[862371b]110
[e374dee]111        if (!(new = (struct aim_ssi_item *)malloc(sizeof(struct aim_ssi_item))))
[862371b]112                return NULL;
113
114        /* Set the name */
115        if (name) {
[e374dee]116                new->name = (char *)malloc((strlen(name)+1)*sizeof(char));
117                strcpy(new->name, name);
[862371b]118        } else
[e374dee]119                new->name = NULL;
[862371b]120
[e374dee]121        /* Set the group ID# and buddy ID# */
122        new->gid = gid;
123        new->bid = bid;
[862371b]124        if (type == AIM_SSI_TYPE_GROUP) {
[e374dee]125                if ((new->gid == 0xFFFF) && name) {
[862371b]126                        do {
[e374dee]127                                new->gid += 0x0001;
[862371b]128                                for (cur=*list, i=0; ((cur) && (!i)); cur=cur->next)
[e374dee]129                                        if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid == new->gid))
[862371b]130                                                i=1;
131                        } while (i);
[e374dee]132                }
[862371b]133        } else {
[e374dee]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                }
[862371b]142        }
143
[e374dee]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        }
[862371b]165
[e374dee]166        return new;
[862371b]167}
168
169/**
[e374dee]170 * Locally delete an item from the given item list.
[862371b]171 *
172 * @param list A pointer to a pointer to the current list of items.
[e374dee]173 * @param del A pointer to the item you want to remove from the list.
[862371b]174 * @return Return 0 if no errors, otherwise return the error number.
175 */
[e374dee]176static int aim_ssi_itemlist_del(struct aim_ssi_item **list, struct aim_ssi_item *del)
[862371b]177{
[e374dee]178        if (!list || !(*list) || !del)
179                return -EINVAL;
[862371b]180
[e374dee]181        /* Remove the item from the list */
182        if (*list == del) {
183                *list = (*list)->next;
[862371b]184        } else {
[e374dee]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;
[862371b]189        }
190
[e374dee]191        /* Free the deleted item */
192        free(del->name);
193        aim_freetlvchain(&del->data);
194        free(del);
[862371b]195
196        return 0;
197}
198
199/**
[e374dee]200 * Compare two items to see if they have the same data.
[862371b]201 *
[e374dee]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.
[862371b]205 */
[e374dee]206static int aim_ssi_itemlist_cmp(struct aim_ssi_item *cur1, struct aim_ssi_item *cur2)
[862371b]207{
[e374dee]208        if (!cur1 || !cur2)
209                return 1;
[862371b]210
[e374dee]211        if (cur1->data && !cur2->data)
212                return 2;
[862371b]213
[e374dee]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}
[862371b]240
[e374dee]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;
[862371b]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
[e374dee]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))) {
[862371b]302                                return cur;
[e374dee]303                        }
304                }
[862371b]305
306        /* For stuff without names--permit deny setting, visibility mask, etc. */
307        } else for (cur=list; cur; cur=cur->next) {
[e374dee]308                if ((cur->type == type) && (!cur->name))
[862371b]309                        return cur;
310        }
311
312        return NULL;
313}
314
315/**
[e374dee]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/**
[862371b]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.
[e374dee]338 * @return Return a pointer to the name of the item if found, else return NULL;
[862371b]339 */
[e374dee]340faim_export char *aim_ssi_itemlist_findparentname(struct aim_ssi_item *list, const char *sn)
[862371b]341{
342        struct aim_ssi_item *cur, *curg;
343        if (!list || !sn)
344                return NULL;
[e374dee]345        if (!(cur = aim_ssi_itemlist_exists(list, sn)))
[862371b]346                return NULL;
[e374dee]347        if (!(curg = aim_ssi_itemlist_find(list, cur->gid, 0x0000)))
348                return NULL;
349        return curg->name;
[862371b]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/**
[e374dee]395 * Locally find the alias of the given buddy.
[862371b]396 *
[e374dee]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!
[862371b]403 */
[e374dee]404faim_export char *aim_ssi_getalias(struct aim_ssi_item *list, const char *gn, const char *sn)
[862371b]405{
[e374dee]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                }
[862371b]418        }
[e374dee]419        return NULL;
[862371b]420}
421
422/**
[e374dee]423 * Locally find if you are waiting for authorization for a buddy.
[862371b]424 *
[e374dee]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!
[862371b]431 */
[e374dee]432faim_export int aim_ssi_waitingforauth(struct aim_ssi_item *list, const char *gn, const char *sn)
[862371b]433{
[e374dee]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;
[862371b]440        }
441        return 0;
442}
443
444/**
[e374dee]445 * If there are changes, then create temporary items and
446 * call addmoddel.
[862371b]447 *
448 * @param sess The oscar session.
449 * @return Return 0 if no errors, otherwise return the error number.
450 */
[e374dee]451static int aim_ssi_sync(aim_session_t *sess)
[862371b]452{
[e374dee]453        struct aim_ssi_item *cur1, *cur2;
454        struct aim_ssi_tmp *cur, *new;
[862371b]455
[e374dee]456        if (!sess)
457                return -EINVAL;
[862371b]458
[e374dee]459        /* If we're waiting for an ack, we shouldn't do anything else */
460        if (sess->ssi.waiting_for_ack)
[862371b]461                return 0;
462
[e374dee]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         */
[862371b]471
[e374dee]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                        }
[862371b]488                }
489        }
490
[e374dee]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                        }
[862371b]507                }
508        }
509
[e374dee]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        }
[862371b]529
[e374dee]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;
[862371b]534        }
535
[e374dee]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);
[862371b]542
543        return 0;
544}
545
546/**
[e374dee]547 * Free all SSI data.
548 *
549 * This doesn't remove it from the server, that's different.
[862371b]550 *
551 * @param sess The oscar session.
552 * @return Return 0 if no errors, otherwise return the error number.
553 */
[e374dee]554static int aim_ssi_freelist(aim_session_t *sess)
[862371b]555{
[e374dee]556        struct aim_ssi_item *cur, *del;
557        struct aim_ssi_tmp *curtmp, *deltmp;
[862371b]558
[e374dee]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);
[862371b]566        }
567
[e374dee]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);
[862371b]575        }
576
[e374dee]577        curtmp = sess->ssi.pending;
578        while (curtmp) {
579                deltmp = curtmp;
580                curtmp = curtmp->next;
581                free(deltmp);
582        }
[862371b]583
[e374dee]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;
[862371b]589
590        return 0;
591}
592
593/**
[e374dee]594 * Delete all SSI data.
[862371b]595 *
596 * @param sess The oscar session.
597 * @return Return 0 if no errors, otherwise return the error number.
598 */
[e374dee]599faim_export int aim_ssi_deletelist(aim_session_t *sess)
[862371b]600{
[e374dee]601        struct aim_ssi_item *cur, *del;
[862371b]602
[e374dee]603        if (!sess)
[862371b]604                return -EINVAL;
605
[e374dee]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;
[862371b]616
[e374dee]617        /* Sync our local list with the server list */
618        aim_ssi_sync(sess);
[862371b]619
620        return 0;
621}
622
623/**
[e374dee]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
[862371b]628 *
629 * @param sess The oscar session.
630 * @return Return 0 if no errors, otherwise return the error number.
631 */
[e374dee]632faim_export int aim_ssi_cleanlist(aim_session_t *sess)
[862371b]633{
[e374dee]634        struct aim_ssi_item *cur, *next;
[862371b]635
[e374dee]636        if (!sess)
[862371b]637                return -EINVAL;
638
[e374dee]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;
[862371b]659        }
660
[e374dee]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);
[862371b]669                }
[e374dee]670                cur = next;
[862371b]671        }
672
[e374dee]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);
[862371b]676
677        return 0;
678}
679
680/**
[e374dee]681 * Add a buddy to the list.
[862371b]682 *
683 * @param sess The oscar session.
[e374dee]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.
[862371b]689 * @return Return 0 if no errors, otherwise return the error number.
690 */
[e374dee]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)
[862371b]692{
[e374dee]693        struct aim_ssi_item *parent;
694        aim_tlvlist_t *data = NULL;
[862371b]695
[e374dee]696        if (!sess || !name || !group)
[862371b]697                return -EINVAL;
698
[e374dee]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)))
[862371b]707                        return -ENOMEM;
708
[e374dee]709                /* Modify the parent's parent (the master group) */
710                aim_ssi_itemlist_rebuildgroup(sess->ssi.local, NULL);
[862371b]711        }
712
[e374dee]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);
[862371b]729
[e374dee]730        /* Sync our local list with the server list */
731        aim_ssi_sync(sess);
[862371b]732
733        return 0;
734}
735
736/**
[e374dee]737 * Add a permit buddy to the list.
[862371b]738 *
739 * @param sess The oscar session.
[e374dee]740 * @param name The name of the item..
[862371b]741 * @return Return 0 if no errors, otherwise return the error number.
742 */
[e374dee]743faim_export int aim_ssi_addpermit(aim_session_t *sess, const char *name)
[862371b]744{
745
[e374dee]746        if (!sess || !name)
[862371b]747                return -EINVAL;
748
[e374dee]749        /* Add that bad boy */
750        aim_ssi_itemlist_add(&sess->ssi.local, name, 0x0000, 0xFFFF, AIM_SSI_TYPE_PERMIT, NULL);
[862371b]751
[e374dee]752        /* Sync our local list with the server list */
753        aim_ssi_sync(sess);
[862371b]754
755        return 0;
756}
757
758/**
[e374dee]759 * Add a deny buddy to the list.
[862371b]760 *
761 * @param sess The oscar session.
[e374dee]762 * @param name The name of the item..
[862371b]763 * @return Return 0 if no errors, otherwise return the error number.
764 */
[e374dee]765faim_export int aim_ssi_adddeny(aim_session_t *sess, const char *name)
[862371b]766{
767
[e374dee]768        if (!sess || !name)
[862371b]769                return -EINVAL;
770
[e374dee]771        /* Add that bad boy */
772        aim_ssi_itemlist_add(&sess->ssi.local, name, 0x0000, 0xFFFF, AIM_SSI_TYPE_DENY, NULL);
[862371b]773
[e374dee]774        /* Sync our local list with the server list */
775        aim_ssi_sync(sess);
[862371b]776
777        return 0;
778}
779
780/**
[e374dee]781 * Deletes a buddy from the list.
[862371b]782 *
783 * @param sess The oscar session.
[e374dee]784 * @param name The name of the item, or NULL.
785 * @param group The group of the item, or NULL.
[862371b]786 * @return Return 0 if no errors, otherwise return the error number.
787 */
[e374dee]788faim_export int aim_ssi_delbuddy(aim_session_t *sess, const char *name, const char *group)
[862371b]789{
[e374dee]790        struct aim_ssi_item *del;
[862371b]791
[e374dee]792        if (!sess)
[862371b]793                return -EINVAL;
794
[e374dee]795        /* Find the buddy */
796        if (!(del = aim_ssi_itemlist_finditem(sess->ssi.local, group, name, AIM_SSI_TYPE_BUDDY)))
[862371b]797                return -EINVAL;
798
[e374dee]799        /* Remove the item from the list */
800        aim_ssi_itemlist_del(&sess->ssi.local, del);
[862371b]801
[e374dee]802        /* Modify the parent group */
803        aim_ssi_itemlist_rebuildgroup(sess->ssi.local, group);
[862371b]804
[e374dee]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);
[862371b]808
[e374dee]809                /* Modify the parent group */
810                aim_ssi_itemlist_rebuildgroup(sess->ssi.local, NULL);
[862371b]811
[e374dee]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                }
[862371b]816        }
817
[e374dee]818        /* Sync our local list with the server list */
819        aim_ssi_sync(sess);
[862371b]820
821        return 0;
822}
823
824/**
[e374dee]825 * Deletes a permit buddy from the list.
[862371b]826 *
827 * @param sess The oscar session.
[e374dee]828 * @param name The name of the item, or NULL.
[862371b]829 * @return Return 0 if no errors, otherwise return the error number.
830 */
[e374dee]831faim_export int aim_ssi_delpermit(aim_session_t *sess, const char *name)
[862371b]832{
[e374dee]833        struct aim_ssi_item *del;
[862371b]834
[e374dee]835        if (!sess)
[862371b]836                return -EINVAL;
837
[e374dee]838        /* Find the item */
839        if (!(del = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, name, AIM_SSI_TYPE_PERMIT)))
[862371b]840                return -EINVAL;
841
[e374dee]842        /* Remove the item from the list */
843        aim_ssi_itemlist_del(&sess->ssi.local, del);
[862371b]844
[e374dee]845        /* Sync our local list with the server list */
846        aim_ssi_sync(sess);
[862371b]847
848        return 0;
849}
850
851/**
[e374dee]852 * Deletes a deny buddy from the list.
[862371b]853 *
854 * @param sess The oscar session.
[e374dee]855 * @param name The name of the item, or NULL.
[862371b]856 * @return Return 0 if no errors, otherwise return the error number.
857 */
[e374dee]858faim_export int aim_ssi_deldeny(aim_session_t *sess, const char *name)
859{
860        struct aim_ssi_item *del;
[862371b]861
[e374dee]862        if (!sess)
[862371b]863                return -EINVAL;
864
[e374dee]865        /* Find the item */
866        if (!(del = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, name, AIM_SSI_TYPE_DENY)))
[862371b]867                return -EINVAL;
868
[e374dee]869        /* Remove the item from the list */
870        aim_ssi_itemlist_del(&sess->ssi.local, del);
[862371b]871
[e374dee]872        /* Sync our local list with the server list */
873        aim_ssi_sync(sess);
[862371b]874
[e374dee]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;
[862371b]908
[e374dee]909        if (!sess || !gn || !sn)
910                return -EINVAL;
[862371b]911
[e374dee]912        if (!(tmp = aim_ssi_itemlist_finditem(sess->ssi.local, gn, sn, AIM_SSI_TYPE_BUDDY)))
913                return -EINVAL;
[862371b]914
[e374dee]915        if (alias && !strlen(alias))
916                alias = NULL;
[862371b]917
[e374dee]918        /* Need to add the x0131 TLV to the TLV chain */
919        if (alias)
920                aim_addtlvtochain_raw(&data, 0x0131, strlen(alias), alias);
[862371b]921
[e374dee]922        aim_freetlvchain(&tmp->data);
923        tmp->data = data;
[862371b]924
[e374dee]925        /* Sync our local list with the server list */
926        aim_ssi_sync(sess);
[862371b]927
928        return 0;
929}
930
931/**
[e374dee]932 * Rename a group.
[862371b]933 *
934 * @param sess The oscar session.
[e374dee]935 * @param oldgn The old group name.
936 * @param newgn The new group name.
[862371b]937 * @return Return 0 if no errors, otherwise return the error number.
938 */
[e374dee]939faim_export int aim_ssi_rename_group(aim_session_t *sess, const char *oldgn, const char *newgn)
940{
941        struct aim_ssi_item *group;
[862371b]942
[e374dee]943        if (!sess || !oldgn || !newgn)
[862371b]944                return -EINVAL;
945
[e374dee]946        if (!(group = aim_ssi_itemlist_finditem(sess->ssi.local, oldgn, NULL, AIM_SSI_TYPE_GROUP)))
947                return -EINVAL;
[862371b]948
[e374dee]949        free(group->name);
950        group->name = (char *)malloc((strlen(newgn)+1)*sizeof(char));
951        strcpy(group->name, newgn);
[862371b]952
[e374dee]953        /* Sync our local list with the server list */
954        aim_ssi_sync(sess);
[862371b]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 */
[e374dee]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;
[862371b]977
[e374dee]978        if (!sess)
[862371b]979                return -EINVAL;
980
[e374dee]981        /* Need to add the x00ca TLV to the TLV chain */
982        aim_addtlvtochain8(&data, 0x00ca, permdeny);
[862371b]983
[e374dee]984        /* Need to add the x00cb TLV to the TLV chain */
985        aim_addtlvtochain32(&data, 0x00cb, vismask);
[862371b]986
[e374dee]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;
[862371b]990        } else {
[e374dee]991                tmp = aim_ssi_itemlist_add(&sess->ssi.local, NULL, 0x0000, 0xFFFF, AIM_SSI_TYPE_PDINFO, data);
992                aim_freetlvchain(&data);
[862371b]993        }
994
[e374dee]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        }
[862371b]1044
[e374dee]1045        /* Sync our local list with the server list */
1046        aim_ssi_sync(sess);
1047        free(csumdata);
[862371b]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 */
[e374dee]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;
[862371b]1062
[e374dee]1063        if (!sess)
[862371b]1064                return -EINVAL;
1065
[e374dee]1066        /* Need to add the x00c9 TLV to the TLV chain */
1067        aim_addtlvtochain32(&data, 0x00c9, presence);
[862371b]1068
[e374dee]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;
[862371b]1072        } else {
[e374dee]1073                tmp = aim_ssi_itemlist_add(&sess->ssi.local, NULL, 0x0000, 0xFFFF, AIM_SSI_TYPE_PRESENCEPREFS, data);
1074                aim_freetlvchain(&data);
[862371b]1075        }
1076
[e374dee]1077        /* Sync our local list with the server list */
1078        aim_ssi_sync(sess);
[862371b]1079
1080        return 0;
1081}
1082
[5e53c4a]1083/*
[e374dee]1084 * Subtype 0x0002 - Request SSI Rights.
[5e53c4a]1085 */
[e374dee]1086faim_export int aim_ssi_reqrights(aim_session_t *sess)
[5e53c4a]1087{
[e374dee]1088        aim_conn_t *conn;
1089
1090        if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)))
1091                return -EINVAL;
1092
[862371b]1093        return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_REQRIGHTS);
[5e53c4a]1094}
1095
1096/*
[e374dee]1097 * Subtype 0x0003 - SSI Rights Information.
[5e53c4a]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{
[e374dee]1101        int ret = 0, i;
[5e53c4a]1102        aim_rxcallback_t userfunc;
[e374dee]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);
[5e53c4a]1126
1127        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
[e374dee]1128                ret = userfunc(sess, rx, tlv->length/2, maxitems);
1129
1130        aim_freetlvchain(&tlvlist);
1131        free(maxitems);
[5e53c4a]1132
1133        return ret;
1134}
1135
1136/*
[e374dee]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.
[5e53c4a]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 */
[e374dee]1164faim_export int aim_ssi_reqifchanged(aim_session_t *sess, time_t timestamp, fu16_t numitems)
[5e53c4a]1165{
[e374dee]1166        aim_conn_t *conn;
[5e53c4a]1167        aim_frame_t *fr;
1168        aim_snacid_t snacid;
1169
[e374dee]1170        if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)))
[5e53c4a]1171                return -EINVAL;
1172
1173        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4+2)))
1174                return -ENOMEM;
1175
[e374dee]1176        snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_REQIFCHANGED, 0x0000, NULL, 0);
[5e53c4a]1177
[e374dee]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);
[5e53c4a]1181
1182        aim_tx_enqueue(sess, fr);
1183
[e374dee]1184        /* Free any current data, just in case */
1185        aim_ssi_freelist(sess);
1186
[5e53c4a]1187        return 0;
1188}
1189
1190/*
[e374dee]1191 * Subtype 0x0006 - SSI Data.
[5e53c4a]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 */
[e374dee]1198        fu16_t namelen, gid, bid, type;
1199        char *name;
1200        aim_tlvlist_t *data;
[862371b]1201
1202        fmtver = aimbs_get8(bs); /* Version of ssi data.  Should be 0x00 */
[e374dee]1203        sess->ssi.numitems += aimbs_get16(bs); /* # of items in this SSI SNAC */
[5e53c4a]1204
[e374dee]1205        /* Read in the list */
1206        while (aim_bstream_empty(bs) > 4) { /* last four bytes are timestamp */
[5e53c4a]1207                if ((namelen = aimbs_get16(bs)))
[e374dee]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        }
[5e53c4a]1219
[e374dee]1220        /* Read in the timestamp */
1221        sess->ssi.timestamp = aimbs_get32(bs);
[5e53c4a]1222
[e374dee]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);
[5e53c4a]1228
[e374dee]1229                sess->ssi.received_data = 1;
[5e53c4a]1230
[e374dee]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        }
[5e53c4a]1234
1235        return ret;
1236}
1237
1238/*
[e374dee]1239 * Subtype 0x0007 - SSI Activate Data.
[5e53c4a]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 */
[e374dee]1247faim_export int aim_ssi_enable(aim_session_t *sess)
[5e53c4a]1248{
[e374dee]1249        aim_conn_t *conn;
1250
1251        if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)))
1252                return -EINVAL;
1253
[862371b]1254        return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, 0x0007);
1255}
1256
1257/*
[e374dee]1258 * Subtype 0x0008/0x0009/0x000a - SSI Add/Mod/Del Item(s).
[862371b]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 */
[e374dee]1265faim_export int aim_ssi_addmoddel(aim_session_t *sess)
[862371b]1266{
[e374dee]1267        aim_conn_t *conn;
[862371b]1268        aim_frame_t *fr;
1269        aim_snacid_t snacid;
[e374dee]1270        int snaclen;
1271        struct aim_ssi_tmp *cur;
[862371b]1272
[e374dee]1273        if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)) || !sess->ssi.pending || !sess->ssi.pending->item)
[862371b]1274                return -EINVAL;
1275
[e374dee]1276        /* Calculate total SNAC size */
[862371b]1277        snaclen = 10; /* For family, subtype, flags, and SNAC ID */
[e374dee]1278        for (cur=sess->ssi.pending; cur; cur=cur->next) {
[862371b]1279                snaclen += 10; /* For length, GID, BID, type, and length */
[e374dee]1280                if (cur->item->name)
1281                        snaclen += strlen(cur->item->name);
1282                if (cur->item->data)
1283                        snaclen += aim_sizetlvchain(&cur->item->data);
[862371b]1284        }
1285
1286        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, snaclen)))
1287                return -ENOMEM;
1288
[e374dee]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);
[862371b]1302        }
1303
[e374dee]1304        aim_tx_enqueue(sess, fr);
[862371b]1305
1306        return 0;
1307}
1308
1309/*
[e374dee]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.
[862371b]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;
[e374dee]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;
[862371b]1578
[e374dee]1579        sess->ssi.received_data = 1;
[862371b]1580
1581        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
1582                ret = userfunc(sess, rx);
1583
1584        return ret;
[5e53c4a]1585}
1586
1587/*
[e374dee]1588 * Subtype 0x0011 - SSI Begin Data Modification.
[5e53c4a]1589 *
1590 * Tells the server you're going to start modifying data.
1591 *
1592 */
[e374dee]1593faim_export int aim_ssi_modbegin(aim_session_t *sess)
[5e53c4a]1594{
[e374dee]1595        aim_conn_t *conn;
1596
1597        if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)))
1598                return -EINVAL;
1599
[862371b]1600        return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_EDITSTART);
[5e53c4a]1601}
1602
1603/*
[e374dee]1604 * Subtype 0x0012 - SSI End Data Modification.
[5e53c4a]1605 *
[e374dee]1606 * Tells the server you're finished modifying data.
[5e53c4a]1607 *
1608 */
[e374dee]1609faim_export int aim_ssi_modend(aim_session_t *sess)
[5e53c4a]1610{
[e374dee]1611        aim_conn_t *conn;
1612
1613        if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)))
1614                return -EINVAL;
1615
[862371b]1616        return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_EDITSTOP);
[5e53c4a]1617}
1618
1619/*
[e374dee]1620 * Subtype 0x0014 - Grant authorization
[5e53c4a]1621 *
[e374dee]1622 * Authorizes a contact so they can add you to their contact list.
[5e53c4a]1623 *
1624 */
[e374dee]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)
[5e53c4a]1663{
1664        int ret = 0;
1665        aim_rxcallback_t userfunc;
[e374dee]1666        fu16_t tmp;
1667        char *sn, *msg;
[5e53c4a]1668
[e374dee]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);
[862371b]1683
[5e53c4a]1684        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
[e374dee]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);
[5e53c4a]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
[862371b]1882        if (snac->subtype == AIM_CB_SSI_RIGHTSINFO)
[5e53c4a]1883                return parserights(sess, mod, rx, snac, bs);
[862371b]1884        else if (snac->subtype == AIM_CB_SSI_LIST)
[5e53c4a]1885                return parsedata(sess, mod, rx, snac, bs);
[e374dee]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);
[862371b]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)
[5e53c4a]1895                return parsedataunchanged(sess, mod, rx, snac, bs);
[e374dee]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);
[5e53c4a]1904
1905        return 0;
1906}
1907
[862371b]1908static void ssi_shutdown(aim_session_t *sess, aim_module_t *mod)
1909{
1910        aim_ssi_freelist(sess);
1911
1912        return;
1913}
1914
[5e53c4a]1915faim_internal int ssi_modfirst(aim_session_t *sess, aim_module_t *mod)
1916{
1917
[862371b]1918        mod->family = AIM_CB_FAM_SSI;
[e374dee]1919        mod->version = 0x0004;
[5e53c4a]1920        mod->toolid = 0x0110;
[e374dee]1921        mod->toolversion = 0x0629;
[5e53c4a]1922        mod->flags = 0;
1923        strncpy(mod->name, "ssi", sizeof(mod->name));
1924        mod->snachandler = snachandler;
[862371b]1925        mod->shutdown = ssi_shutdown;
[5e53c4a]1926
1927        return 0;
1928}
Note: See TracBrowser for help on using the repository browser.