source: libfaim/ssi.c @ 1e34e40

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