source: libfaim/ssi.c @ 07ab1cb

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