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