comparison src/protocols/oscar/ssi.c @ 4230:9f729d6d88a6

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