2672
|
1 /*
|
|
2 * Server-Side/Stored Information.
|
|
3 *
|
|
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,
|
|
6 * to be stored on the server, so that they can be accessed from any client.
|
|
7 *
|
3210
|
8 * We keep a copy of the ssi data in sess->ssi, because the data needs to be
|
|
9 * accessed for various reasons. So all the "aim_ssi_itemlist_bleh" functions
|
|
10 * near the top just manage the local data.
|
|
11 *
|
|
12 * 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,
|
|
14 * starting with the request rights function (subtype 0x0002), then parse
|
|
15 * rights (subtype 0x0003), then--well, you get the idea.
|
|
16 *
|
2672
|
17 * This is entirely too complicated.
|
2991
|
18 * You don't know the half of it.
|
|
19 *
|
|
20 * XXX - Test for memory leaks
|
|
21 * XXX - Better parsing of rights, and use the rights info to limit adds
|
2672
|
22 *
|
|
23 */
|
|
24
|
|
25 #define FAIM_INTERNAL
|
|
26 #include <aim.h>
|
|
27
|
3210
|
28 /**
|
|
29 * Locally add a new item to the given item list.
|
|
30 *
|
|
31 * @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
|
|
35 * item should have no name.
|
|
36 * @param type The type of the item, 0x0001 for a contact, 0x0002 for a group, etc.
|
|
37 * @return The newly created item.
|
2991
|
38 */
|
3466
|
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)
|
2991
|
40 {
|
3210
|
41 int i;
|
|
42 struct aim_ssi_item *cur, *newitem;
|
|
43
|
|
44 if (!(newitem = (struct aim_ssi_item *)malloc(sizeof(struct aim_ssi_item))))
|
|
45 return NULL;
|
|
46
|
|
47 /* Set the name */
|
|
48 if (name) {
|
|
49 if (!(newitem->name = (char *)malloc((strlen(name)+1)*sizeof(char)))) {
|
|
50 free(newitem);
|
|
51 return NULL;
|
|
52 }
|
|
53 strcpy(newitem->name, name);
|
|
54 } else
|
|
55 newitem->name = NULL;
|
|
56
|
|
57 /* Set the group ID# and the buddy ID# */
|
|
58 newitem->gid = 0x0000;
|
|
59 newitem->bid = 0x0000;
|
|
60 if (type == AIM_SSI_TYPE_GROUP) {
|
|
61 if (name)
|
|
62 do {
|
|
63 newitem->gid += 0x0001;
|
|
64 for (cur=*list, i=0; ((cur) && (!i)); cur=cur->next)
|
|
65 if ((cur->gid == newitem->gid) && (cur->gid == newitem->gid))
|
|
66 i=1;
|
|
67 } while (i);
|
|
68 } else {
|
|
69 if (parent)
|
|
70 newitem->gid = parent->gid;
|
|
71 do {
|
|
72 newitem->bid += 0x0001;
|
|
73 for (cur=*list, i=0; ((cur) && (!i)); cur=cur->next)
|
|
74 if ((cur->bid == newitem->bid) && (cur->gid == newitem->gid))
|
|
75 i=1;
|
|
76 } while (i);
|
|
77 }
|
|
78
|
|
79 /* Set the rest */
|
|
80 newitem->type = type;
|
|
81 newitem->data = NULL;
|
|
82 newitem->next = *list;
|
|
83 *list = newitem;
|
|
84
|
|
85 return newitem;
|
|
86 }
|
|
87
|
|
88 /**
|
|
89 * Locally rebuild the 0x00c8 TLV in the additional data of the given group.
|
|
90 *
|
|
91 * @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.
|
|
93 * @return Return 0 if no errors, otherwise return the error number.
|
|
94 */
|
|
95 static int aim_ssi_itemlist_rebuildgroup(struct aim_ssi_item **list, struct aim_ssi_item *parentgroup)
|
|
96 {
|
3348
|
97 int newlen;
|
2991
|
98 struct aim_ssi_item *cur;
|
3210
|
99
|
|
100 /* Free the old additional data */
|
|
101 if (parentgroup->data) {
|
|
102 aim_freetlvchain((aim_tlvlist_t **)&parentgroup->data);
|
|
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 {
|
|
113 for (cur=*list; cur; cur=cur->next)
|
|
114 if ((cur->gid == parentgroup->gid) && (cur->type == AIM_SSI_TYPE_BUDDY))
|
|
115 newlen += 2;
|
|
116 }
|
|
117
|
|
118 /* Rebuild the additional data */
|
|
119 if (newlen>0) {
|
|
120 fu8_t *newdata;
|
|
121
|
|
122 if (!(newdata = (fu8_t *)malloc((newlen)*sizeof(fu8_t))))
|
|
123 return -ENOMEM;
|
|
124 newlen = 0;
|
|
125 if (parentgroup->gid == 0x0000) {
|
|
126 for (cur=*list; cur; cur=cur->next)
|
|
127 if ((cur->gid != 0x0000) && (cur->type == AIM_SSI_TYPE_GROUP))
|
3332
|
128 newlen += aimutil_put16(newdata+newlen, cur->gid);
|
3210
|
129 } else {
|
|
130 for (cur=*list; cur; cur=cur->next)
|
|
131 if ((cur->gid == parentgroup->gid) && (cur->type == AIM_SSI_TYPE_BUDDY))
|
3332
|
132 newlen += aimutil_put16(newdata+newlen, cur->bid);
|
3210
|
133 }
|
|
134 aim_addtlvtochain_raw((aim_tlvlist_t **)&(parentgroup->data), 0x00c8, newlen, newdata);
|
|
135
|
|
136 free(newdata);
|
|
137 }
|
|
138
|
2991
|
139 return 0;
|
|
140 }
|
|
141
|
3210
|
142 /**
|
|
143 * Locally free all of the stored buddy list information.
|
|
144 *
|
|
145 * @param sess The oscar session.
|
|
146 * @return Return 0 if no errors, otherwise return the error number.
|
3017
|
147 */
|
3210
|
148 static int aim_ssi_freelist(aim_session_t *sess)
|
3017
|
149 {
|
3210
|
150 struct aim_ssi_item *cur, *delitem;
|
|
151
|
|
152 cur = sess->ssi.items;
|
|
153 while (cur) {
|
|
154 if (cur->name) free(cur->name);
|
|
155 if (cur->data) aim_freetlvchain((aim_tlvlist_t **)&cur->data);
|
|
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
|
3017
|
165 return 0;
|
|
166 }
|
|
167
|
3210
|
168 /**
|
|
169 * Locally find an item given a group ID# and a buddy ID#.
|
|
170 *
|
|
171 * @param list A pointer to the current list of items.
|
|
172 * @param gid The group ID# of the desired item.
|
|
173 * @param bid The buddy ID# of the desired item.
|
|
174 * @return Return a pointer to the item if found, else return NULL;
|
2991
|
175 */
|
3210
|
176 faim_export struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_item *list, fu16_t gid, fu16_t bid)
|
2991
|
177 {
|
|
178 struct aim_ssi_item *cur;
|
3210
|
179 for (cur=list; cur; cur=cur->next)
|
|
180 if ((cur->gid == gid) && (cur->bid == bid))
|
|
181 return cur;
|
2991
|
182 return NULL;
|
|
183 }
|
|
184
|
3210
|
185 /**
|
|
186 * Locally find an item given a group name, screen name, and type. If group name
|
|
187 * and screen name are null, then just return the first item of the given type.
|
|
188 *
|
|
189 * @param list A pointer to the current list of items.
|
|
190 * @param gn The group name of the desired item.
|
|
191 * @param bn The buddy name of the desired item.
|
|
192 * @param type The type of the desired item.
|
|
193 * @return Return a pointer to the item if found, else return NULL;
|
3109
|
194 */
|
3466
|
195 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
|
196 {
|
3210
|
197 struct aim_ssi_item *cur;
|
|
198 if (!list)
|
|
199 return NULL;
|
|
200
|
|
201 if (gn && sn) { /* For finding buddies in groups */
|
|
202 for (cur=list; cur; cur=cur->next)
|
|
203 if ((cur->type == type) && (cur->name) && !(aim_sncmp(cur->name, sn))) {
|
|
204 struct aim_ssi_item *curg;
|
|
205 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)))
|
|
207 return cur;
|
|
208 }
|
|
209
|
|
210 } else if (sn) { /* For finding groups, permits, denies, and ignores */
|
|
211 for (cur=list; cur; cur=cur->next)
|
|
212 if ((cur->type == type) && (cur->name) && !(aim_sncmp(cur->name, sn)))
|
|
213 return cur;
|
|
214
|
|
215 /* For stuff without names--permit deny setting, visibility mask, etc. */
|
|
216 } else for (cur=list; cur; cur=cur->next) {
|
|
217 if (cur->type == type)
|
|
218 return cur;
|
|
219 }
|
|
220
|
|
221 return NULL;
|
|
222 }
|
|
223
|
|
224 /**
|
|
225 * Locally find the parent item of the given buddy name.
|
|
226 *
|
|
227 * @param list A pointer to the current list of items.
|
|
228 * @param bn The buddy name of the desired item.
|
|
229 * @return Return a pointer to the item if found, else return NULL;
|
|
230 */
|
|
231 faim_export struct aim_ssi_item *aim_ssi_itemlist_findparent(struct aim_ssi_item *list, char *sn)
|
|
232 {
|
|
233 struct aim_ssi_item *cur, *curg;
|
|
234 if (!list || !sn)
|
|
235 return NULL;
|
|
236 if (!(cur = aim_ssi_itemlist_finditem(list, NULL, sn, AIM_SSI_TYPE_BUDDY)))
|
|
237 return NULL;
|
|
238 for (curg=list; curg; curg=curg->next)
|
|
239 if ((curg->type == AIM_SSI_TYPE_GROUP) && (curg->gid == cur->gid))
|
|
240 return curg;
|
|
241 return NULL;
|
|
242 }
|
|
243
|
|
244 /**
|
|
245 * Locally find the permit/deny setting item, and return the setting.
|
|
246 *
|
|
247 * @param list A pointer to the current list of items.
|
|
248 * @return Return the current SSI permit deny setting, or 0 if no setting was found.
|
|
249 */
|
|
250 faim_export int aim_ssi_getpermdeny(struct aim_ssi_item *list)
|
|
251 {
|
|
252 struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PDINFO);
|
3109
|
253 if (cur) {
|
|
254 aim_tlvlist_t *tlvlist = cur->data;
|
|
255 if (tlvlist) {
|
|
256 aim_tlv_t *tlv = aim_gettlv(tlvlist, 0x00ca, 1);
|
|
257 if (tlv && tlv->value)
|
|
258 return aimutil_get8(tlv->value);
|
|
259 }
|
|
260 }
|
|
261 return 0;
|
|
262 }
|
|
263
|
3210
|
264 /**
|
|
265 * Locally find the presence flag item, and return the setting. The returned setting is a
|
|
266 * bitmask of the user flags that you are visible to. See the AIM_FLAG_* #defines
|
|
267 * in aim.h
|
|
268 *
|
|
269 * @param list A pointer to the current list of items.
|
|
270 * @return Return the current visibility mask.
|
3109
|
271 */
|
3210
|
272 faim_export fu32_t aim_ssi_getpresence(struct aim_ssi_item *list)
|
3109
|
273 {
|
3210
|
274 struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS);
|
3109
|
275 if (cur) {
|
|
276 aim_tlvlist_t *tlvlist = cur->data;
|
|
277 if (tlvlist) {
|
|
278 aim_tlv_t *tlv = aim_gettlv(tlvlist, 0x00c9, 1);
|
|
279 if (tlv && tlv->length)
|
|
280 return aimutil_get32(tlv->value);
|
|
281 }
|
|
282 }
|
|
283 return 0xFFFFFFFF;
|
|
284 }
|
|
285
|
3210
|
286 /**
|
|
287 * Add the given packet to the holding queue. We totally need to send SSI SNACs one at
|
|
288 * a time, so we have a local queue where packets get put before they are sent, and
|
|
289 * then we send stuff one at a time, nice and orderly-like.
|
|
290 *
|
|
291 * @param sess The oscar session.
|
|
292 * @param conn The bos connection for this session.
|
|
293 * @param fr The newly created SNAC that you want to send.
|
|
294 * @return Return 0 if no errors, otherwise return the error number.
|
2991
|
295 */
|
|
296 static int aim_ssi_enqueue(aim_session_t *sess, aim_conn_t *conn, aim_frame_t *fr)
|
|
297 {
|
|
298 aim_frame_t *cur;
|
|
299
|
|
300 if (!sess || !conn || !fr)
|
|
301 return -EINVAL;
|
|
302
|
|
303 fr->next = NULL;
|
|
304 if (sess->ssi.holding_queue == NULL) {
|
|
305 sess->ssi.holding_queue = fr;
|
|
306 if (!sess->ssi.waiting_for_ack)
|
|
307 aim_ssi_modbegin(sess, conn);
|
|
308 } else {
|
|
309 for (cur = sess->ssi.holding_queue; cur->next; cur = cur->next) ;
|
|
310 cur->next = fr;
|
|
311 }
|
|
312
|
|
313 return 0;
|
|
314 }
|
|
315
|
3210
|
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 *
|
|
321 * @param sess The oscar session.
|
|
322 * @param conn The bos connection for this session.
|
|
323 * @return Return 0 if no errors, otherwise return the error number.
|
2991
|
324 */
|
|
325 static int aim_ssi_dispatch(aim_session_t *sess, aim_conn_t *conn)
|
|
326 {
|
|
327 aim_frame_t *cur;
|
|
328
|
|
329 if (!sess || !conn)
|
|
330 return -EINVAL;
|
|
331
|
|
332 if (!sess->ssi.waiting_for_ack) {
|
|
333 if (sess->ssi.holding_queue) {
|
|
334 sess->ssi.waiting_for_ack = 1;
|
|
335 cur = sess->ssi.holding_queue->next;
|
|
336 sess->ssi.holding_queue->next = NULL;
|
|
337 aim_tx_enqueue(sess, sess->ssi.holding_queue);
|
|
338 sess->ssi.holding_queue = cur;
|
|
339 } else
|
|
340 aim_ssi_modend(sess, conn);
|
|
341 }
|
|
342
|
|
343 return 0;
|
|
344 }
|
|
345
|
3210
|
346 /**
|
|
347 * Send SNACs necessary to remove all SSI data from the server list,
|
|
348 * and then free the local copy as well.
|
|
349 *
|
|
350 * @param sess The oscar session.
|
|
351 * @param conn The bos connection for this session.
|
|
352 * @return Return 0 if no errors, otherwise return the error number.
|
2991
|
353 */
|
|
354 faim_export int aim_ssi_deletelist(aim_session_t *sess, aim_conn_t *conn)
|
|
355 {
|
3000
|
356 int num;
|
2991
|
357 struct aim_ssi_item *cur, **items;
|
|
358
|
|
359 for (cur=sess->ssi.items, num=0; cur; cur=cur->next)
|
|
360 num++;
|
|
361
|
|
362 if (!(items = (struct aim_ssi_item **)malloc(num*sizeof(struct aim_ssi_item *))))
|
|
363 return -ENOMEM;
|
3017
|
364 memset(items, 0, num*sizeof(struct aim_ssi_item *));
|
2991
|
365 for (cur=sess->ssi.items, num=0; cur; cur=cur->next) {
|
|
366 items[num] = cur;
|
|
367 num++;
|
|
368 }
|
|
369
|
3017
|
370 aim_ssi_addmoddel(sess, conn, items, num, AIM_CB_SSI_DEL);
|
2995
|
371 free(items);
|
2991
|
372 aim_ssi_dispatch(sess, conn);
|
|
373 aim_ssi_freelist(sess);
|
|
374
|
|
375 return 0;
|
|
376 }
|
|
377
|
3210
|
378 /**
|
|
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 *
|
|
386 * @param sess The oscar session.
|
|
387 * @param conn The bos connection for this session.
|
|
388 * @return Return 0 if no errors, otherwise return the error number.
|
2991
|
389 */
|
|
390 faim_export int aim_ssi_cleanlist(aim_session_t *sess, aim_conn_t *conn)
|
|
391 {
|
3210
|
392 unsigned int i;
|
2991
|
393 struct aim_ssi_item *cur, *parentgroup;
|
|
394
|
|
395 /* Make sure we actually need to clean out the list */
|
3210
|
396 for (cur=sess->ssi.items, i=0; cur && !i; cur=cur->next)
|
2991
|
397 /* Any buddies directly in the master group */
|
3017
|
398 if ((cur->type == AIM_SSI_TYPE_BUDDY) && (cur->gid == 0x0000))
|
3210
|
399 i++;
|
|
400 if (!i)
|
2991
|
401 return 0;
|
|
402
|
3210
|
403 /* Remove all the additional data from all groups */
|
2991
|
404 for (cur=sess->ssi.items; cur; cur=cur->next)
|
3210
|
405 if ((cur->data) && (cur->type == AIM_SSI_TYPE_GROUP)) {
|
2991
|
406 aim_freetlvchain((aim_tlvlist_t **)&cur->data);
|
|
407 cur->data = NULL;
|
|
408 }
|
|
409
|
|
410 /* If there are buddies directly in the master group, make sure */
|
|
411 /* there is a group to put them in. Any group, any group at all. */
|
3017
|
412 for (cur=sess->ssi.items; ((cur) && ((cur->type != AIM_SSI_TYPE_BUDDY) || (cur->gid != 0x0000))); cur=cur->next);
|
2991
|
413 if (!cur) {
|
3210
|
414 for (parentgroup=sess->ssi.items; ((parentgroup) && (parentgroup->type!=AIM_SSI_TYPE_GROUP) && (parentgroup->gid==0x0000)); parentgroup=parentgroup->next);
|
2991
|
415 if (!parentgroup) {
|
|
416 char *newgroup;
|
|
417 newgroup = (char*)malloc(strlen("Unknown")*sizeof(char));
|
|
418 strcpy(newgroup, "Unknown");
|
3595
|
419 aim_ssi_addgroups(sess, conn, (const char**)&newgroup, 1);
|
2991
|
420 }
|
|
421 }
|
|
422
|
|
423 /* Set parentgroup equal to any arbitray group */
|
3017
|
424 for (parentgroup=sess->ssi.items; parentgroup->gid==0x0000 || parentgroup->type!=AIM_SSI_TYPE_GROUP; parentgroup=parentgroup->next);
|
2991
|
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)
|
3017
|
428 if ((cur->type == AIM_SSI_TYPE_BUDDY) && (cur->gid == 0x0000)) {
|
|
429 aim_ssi_addmoddel(sess, conn, &cur, 1, AIM_CB_SSI_DEL);
|
2991
|
430 cur->gid = parentgroup->gid;
|
3017
|
431 aim_ssi_addmoddel(sess, conn, &cur, 1, AIM_CB_SSI_ADD);
|
2991
|
432 }
|
|
433
|
|
434 /* Rebuild additional data for all groups */
|
|
435 for (parentgroup=sess->ssi.items; parentgroup; parentgroup=parentgroup->next)
|
3017
|
436 if (parentgroup->type == AIM_SSI_TYPE_GROUP)
|
3210
|
437 aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, parentgroup);
|
2991
|
438
|
|
439 /* Send a mod snac for all groups */
|
3210
|
440 i = 0;
|
2991
|
441 for (cur=sess->ssi.items; cur; cur=cur->next)
|
3017
|
442 if (cur->type == AIM_SSI_TYPE_GROUP)
|
3210
|
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 }
|
2991
|
476
|
|
477 /* Begin sending SSI SNACs */
|
|
478 aim_ssi_dispatch(sess, conn);
|
|
479
|
|
480 return 0;
|
|
481 }
|
|
482
|
3210
|
483 /**
|
|
484 * Add an array of screen names to the given group.
|
2991
|
485 *
|
3210
|
486 * @param sess The oscar session.
|
|
487 * @param conn The bos connection for this session.
|
|
488 * @param gn The name of the group to which you want to add these names.
|
|
489 * @param sn An array of null terminated strings of the names you want to add.
|
|
490 * @param num The number of screen names you are adding (size of the sn array).
|
|
491 * @return Return 0 if no errors, otherwise return the error number.
|
2991
|
492 */
|
3466
|
493 faim_export int aim_ssi_addbuddies(aim_session_t *sess, aim_conn_t *conn, const char *gn, const char **sn, unsigned int num)
|
2991
|
494 {
|
3210
|
495 struct aim_ssi_item *parentgroup, **newitems;
|
|
496 fu16_t i;
|
2991
|
497
|
|
498 if (!sess || !conn || !gn || !sn || !num)
|
|
499 return -EINVAL;
|
|
500
|
|
501 /* Look up the parent group */
|
3210
|
502 if (!(parentgroup = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, gn, AIM_SSI_TYPE_GROUP))) {
|
3630
|
503 aim_ssi_addgroups(sess, conn, (const char **)&gn, 1);
|
3210
|
504 if (!(parentgroup = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, gn, AIM_SSI_TYPE_GROUP)))
|
2991
|
505 return -ENOMEM;
|
|
506 }
|
|
507
|
|
508 /* Allocate an array of pointers to each of the new items */
|
|
509 if (!(newitems = (struct aim_ssi_item **)malloc(num*sizeof(struct aim_ssi_item *))))
|
|
510 return -ENOMEM;
|
|
511
|
3210
|
512 /* Add items to the local list, and index them in the array */
|
|
513 for (i=0; i<num; i++)
|
|
514 if (!(newitems[i] = aim_ssi_itemlist_add(&sess->ssi.items, parentgroup, sn[i], AIM_SSI_TYPE_BUDDY))) {
|
2991
|
515 free(newitems);
|
|
516 return -ENOMEM;
|
|
517 }
|
|
518
|
|
519 /* Send the add item SNAC */
|
3348
|
520 if ((i = aim_ssi_addmoddel(sess, conn, newitems, num, AIM_CB_SSI_ADD))) {
|
3210
|
521 free(newitems);
|
|
522 return -i;
|
|
523 }
|
2991
|
524
|
|
525 /* Free the array of pointers to each of the new items */
|
|
526 free(newitems);
|
|
527
|
|
528 /* Rebuild the additional data in the parent group */
|
3348
|
529 if ((i = aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, parentgroup)))
|
3210
|
530 return i;
|
2991
|
531
|
|
532 /* Send the mod item SNAC */
|
3348
|
533 if ((i = aim_ssi_addmoddel(sess, conn, &parentgroup, 1, AIM_CB_SSI_MOD)))
|
3210
|
534 return i;
|
3017
|
535
|
|
536 /* Begin sending SSI SNACs */
|
3210
|
537 if (!(i = aim_ssi_dispatch(sess, conn)))
|
|
538 return i;
|
3017
|
539
|
|
540 return 0;
|
|
541 }
|
|
542
|
3210
|
543 /**
|
|
544 * Add the master group (the group containing all groups). This is called by
|
|
545 * aim_ssi_addgroups, if necessary.
|
|
546 *
|
|
547 * @param sess The oscar session.
|
|
548 * @param conn The bos connection for this session.
|
|
549 * @return Return 0 if no errors, otherwise return the error number.
|
3017
|
550 */
|
3210
|
551 faim_export int aim_ssi_addmastergroup(aim_session_t *sess, aim_conn_t *conn)
|
|
552 {
|
3017
|
553 struct aim_ssi_item *newitem;
|
|
554
|
|
555 if (!sess || !conn)
|
|
556 return -EINVAL;
|
|
557
|
3210
|
558 /* Add the item to the local list, and keep a pointer to it */
|
|
559 if (!(newitem = aim_ssi_itemlist_add(&sess->ssi.items, NULL, NULL, AIM_SSI_TYPE_GROUP)))
|
3017
|
560 return -ENOMEM;
|
|
561
|
|
562 /* If there are any existing groups (technically there shouldn't be, but */
|
|
563 /* just in case) then add their group ID#'s to the additional data */
|
3210
|
564 aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, newitem);
|
3017
|
565
|
|
566 /* Send the add item SNAC */
|
|
567 aim_ssi_addmoddel(sess, conn, &newitem, 1, AIM_CB_SSI_ADD);
|
2991
|
568
|
|
569 /* Begin sending SSI SNACs */
|
|
570 aim_ssi_dispatch(sess, conn);
|
|
571
|
|
572 return 0;
|
|
573 }
|
|
574
|
3210
|
575 /**
|
|
576 * Add an array of groups to the list.
|
|
577 *
|
|
578 * @param sess The oscar session.
|
|
579 * @param conn The bos connection for this session.
|
|
580 * @param gn An array of null terminated strings of the names you want to add.
|
|
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.
|
|
583 */
|
3595
|
584 faim_export int aim_ssi_addgroups(aim_session_t *sess, aim_conn_t *conn, const char **gn, unsigned int num)
|
2991
|
585 {
|
3210
|
586 struct aim_ssi_item *parentgroup, **newitems;
|
|
587 fu16_t i;
|
2991
|
588
|
|
589 if (!sess || !conn || !gn || !num)
|
|
590 return -EINVAL;
|
|
591
|
|
592 /* Look up the parent group */
|
3239
|
593 if (!(parentgroup = aim_ssi_itemlist_find(sess->ssi.items, 0, 0))) {
|
2991
|
594 aim_ssi_addmastergroup(sess, conn);
|
3239
|
595 if (!(parentgroup = aim_ssi_itemlist_find(sess->ssi.items, 0, 0)))
|
2991
|
596 return -ENOMEM;
|
|
597 }
|
|
598
|
|
599 /* Allocate an array of pointers to each of the new items */
|
|
600 if (!(newitems = (struct aim_ssi_item **)malloc(num*sizeof(struct aim_ssi_item *))))
|
|
601 return -ENOMEM;
|
|
602
|
3210
|
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))) {
|
2991
|
606 free(newitems);
|
|
607 return -ENOMEM;
|
|
608 }
|
|
609
|
|
610 /* Send the add item SNAC */
|
3348
|
611 if ((i = aim_ssi_addmoddel(sess, conn, newitems, num, AIM_CB_SSI_ADD))) {
|
3210
|
612 free(newitems);
|
|
613 return -i;
|
|
614 }
|
2991
|
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 */
|
3348
|
620 if ((i = aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, parentgroup)))
|
3210
|
621 return i;
|
2991
|
622
|
|
623 /* Send the mod item SNAC */
|
3348
|
624 if ((i = aim_ssi_addmoddel(sess, conn, &parentgroup, 1, AIM_CB_SSI_MOD)))
|
3210
|
625 return i;
|
2991
|
626
|
|
627 /* Begin sending SSI SNACs */
|
3210
|
628 if (!(i = aim_ssi_dispatch(sess, conn)))
|
|
629 return i;
|
2991
|
630
|
|
631 return 0;
|
|
632 }
|
|
633
|
3210
|
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 *
|
|
639 * @param sess The oscar session.
|
|
640 * @param conn The bos connection for this session.
|
|
641 * @param sn An array of null terminated strings of the names you want to add.
|
|
642 * @param num The number of groups names you are adding (size of the sn array).
|
|
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.
|
3017
|
646 */
|
3466
|
647 faim_export int aim_ssi_addpord(aim_session_t *sess, aim_conn_t *conn, const char **sn, unsigned int num, fu16_t type)
|
2991
|
648 {
|
3210
|
649 struct aim_ssi_item **newitems;
|
|
650 fu16_t i;
|
2991
|
651
|
3210
|
652 if (!sess || !conn || !sn || !num)
|
2991
|
653 return -EINVAL;
|
|
654
|
|
655 /* Allocate an array of pointers to each of the new items */
|
|
656 if (!(newitems = (struct aim_ssi_item **)malloc(num*sizeof(struct aim_ssi_item *))))
|
|
657 return -ENOMEM;
|
|
658
|
3210
|
659 /* Add items to the local list, and index them in the array */
|
|
660 for (i=0; i<num; i++)
|
|
661 if (!(newitems[i] = aim_ssi_itemlist_add(&sess->ssi.items, NULL, sn[i], type))) {
|
2991
|
662 free(newitems);
|
|
663 return -ENOMEM;
|
|
664 }
|
|
665
|
|
666 /* Send the add item SNAC */
|
3348
|
667 if ((i = aim_ssi_addmoddel(sess, conn, newitems, num, AIM_CB_SSI_ADD))) {
|
3210
|
668 free(newitems);
|
|
669 return -i;
|
|
670 }
|
2991
|
671
|
|
672 /* Free the array of pointers to each of the new items */
|
|
673 free(newitems);
|
|
674
|
|
675 /* Begin sending SSI SNACs */
|
3210
|
676 if (!(i = aim_ssi_dispatch(sess, conn)))
|
|
677 return i;
|
2991
|
678
|
|
679 return 0;
|
|
680 }
|
|
681
|
3210
|
682 /**
|
|
683 * Move a buddy from one group to another group. This basically just deletes the
|
|
684 * buddy and re-adds it.
|
|
685 *
|
|
686 * @param sess The oscar session.
|
|
687 * @param conn The bos connection for this session.
|
|
688 * @param oldgn The group that the buddy is currently in.
|
|
689 * @param newgn The group that the buddy should be moved in to.
|
|
690 * @param sn The name of the buddy to be moved.
|
|
691 * @return Return 0 if no errors, otherwise return the error number.
|
|
692 */
|
3867
|
693 faim_export int aim_ssi_movebuddy(aim_session_t *sess, aim_conn_t *conn, const char *oldgn, const char *newgn, const char *sn)
|
3140
|
694 {
|
|
695 struct aim_ssi_item **groups, *buddy, *cur;
|
|
696 fu16_t i;
|
|
697
|
|
698 if (!sess || !conn || !oldgn || !newgn || !sn)
|
|
699 return -EINVAL;
|
|
700
|
|
701 /* Look up the buddy */
|
3210
|
702 if (!(buddy = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, sn, AIM_SSI_TYPE_BUDDY)))
|
3140
|
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 */
|
3210
|
710 if (!(groups[0] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, oldgn, AIM_SSI_TYPE_GROUP))) {
|
3140
|
711 free(groups);
|
|
712 return -ENOMEM;
|
|
713 }
|
|
714
|
|
715 /* Look up the new parent group */
|
3210
|
716 if (!(groups[1] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, newgn, AIM_SSI_TYPE_GROUP))) {
|
3595
|
717 aim_ssi_addgroups(sess, conn, (const char**)&newgn, 1);
|
3348
|
718 if (!(groups[1] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, newgn, AIM_SSI_TYPE_GROUP))) {
|
|
719 free(groups);
|
|
720 return -ENOMEM;
|
|
721 }
|
3140
|
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 */
|
3210
|
740 aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, groups[0]);
|
|
741 aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, groups[1]);
|
3140
|
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
|
3210
|
758 /**
|
3348
|
759 * Rename a group. I really like how this is done. It turns me on.
|
|
760 *
|
|
761 * Did I say that out loud?...
|
|
762 *
|
|
763 * @param sess The oscar session.
|
|
764 * @param conn The bos connection for this session.
|
|
765 * @param oldgn The old group name.
|
|
766 * @param newgn The new group name.
|
|
767 * @return Return 0 if no errors, otherwise return the error number.
|
|
768 */
|
3867
|
769 faim_export int aim_ssi_rename_group(aim_session_t *sess, aim_conn_t *conn, const char *oldgn, const char *newgn)
|
3348
|
770 {
|
|
771 struct aim_ssi_item *group;
|
|
772
|
|
773 if (!sess || !conn || !oldgn || !newgn)
|
|
774 return -EINVAL;
|
|
775
|
|
776 /* Look up the group */
|
|
777 if (!(group = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, oldgn, AIM_SSI_TYPE_GROUP)))
|
|
778 return -ENOMEM;
|
|
779
|
|
780 /* Free the old group name and copy the new one in its place. */
|
|
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);
|
|
788
|
|
789 /* Send the mod item SNAC */
|
|
790 aim_ssi_addmoddel(sess, conn, &group, 1, AIM_CB_SSI_MOD);
|
|
791
|
|
792 /* Begin sending SSI SNACs */
|
|
793 aim_ssi_dispatch(sess, conn);
|
|
794
|
|
795 return 0;
|
|
796 }
|
|
797
|
|
798 /**
|
3210
|
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 */
|
3867
|
808 faim_export int aim_ssi_delbuddies(aim_session_t *sess, aim_conn_t *conn, const char *gn, char **sn, unsigned int num)
|
2991
|
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 */
|
3210
|
817 if (!(parentgroup = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, gn, AIM_SSI_TYPE_GROUP)))
|
2991
|
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 *));
|
3017
|
822 memset(delitems, 0, num*sizeof(struct aim_ssi_item *));
|
2991
|
823
|
|
824 /* Make the delitems array a pointer to the aim_ssi_item structs to be deleted */
|
|
825 for (i=0; i<num; i++) {
|
3210
|
826 if (!(delitems[i] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, sn[i], AIM_SSI_TYPE_BUDDY))) {
|
2991
|
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 */
|
3017
|
842 aim_ssi_addmoddel(sess, conn, delitems, num, AIM_CB_SSI_DEL);
|
2991
|
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 */
|
3210
|
855 aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, parentgroup);
|
2991
|
856
|
|
857 /* Send the mod item SNAC */
|
3017
|
858 aim_ssi_addmoddel(sess, conn, &parentgroup, 1, AIM_CB_SSI_MOD);
|
2991
|
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
|
3210
|
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 {
|
3017
|
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 */
|
3239
|
886 if (!(delitem = aim_ssi_itemlist_find(sess->ssi.items, 0, 0)))
|
3017
|
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
|
3210
|
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 */
|
3017
|
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;
|
2991
|
925 int i;
|
|
926
|
3017
|
927 if (!sess || !conn || !gn || !num)
|
|
928 return -EINVAL;
|
|
929
|
|
930 /* Look up the parent group */
|
3239
|
931 if (!(parentgroup = aim_ssi_itemlist_find(sess->ssi.items, 0, 0)))
|
2991
|
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 *));
|
3017
|
936 memset(delitems, 0, num*sizeof(struct aim_ssi_item *));
|
2991
|
937
|
|
938 /* Make the delitems array a pointer to the aim_ssi_item structs to be deleted */
|
|
939 for (i=0; i<num; i++) {
|
3210
|
940 if (!(delitems[i] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, gn[i], AIM_SSI_TYPE_GROUP))) {
|
2991
|
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 */
|
3017
|
956 aim_ssi_addmoddel(sess, conn, delitems, num, AIM_CB_SSI_DEL);
|
2991
|
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
|
3017
|
968 /* Rebuild the additional data in the parent group */
|
3210
|
969 aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, parentgroup);
|
3017
|
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
|
2991
|
978 /* Begin sending SSI SNACs */
|
|
979 aim_ssi_dispatch(sess, conn);
|
|
980
|
|
981 return 0;
|
|
982 }
|
|
983
|
3210
|
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 */
|
3466
|
995 faim_export int aim_ssi_delpord(aim_session_t *sess, aim_conn_t *conn, const char **sn, unsigned int num, fu16_t type) {
|
2991
|
996 struct aim_ssi_item *cur, **delitems;
|
|
997 int i;
|
|
998
|
3017
|
999 if (!sess || !conn || !sn || !num || (type!=AIM_SSI_TYPE_PERMIT && type!=AIM_SSI_TYPE_DENY))
|
2991
|
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 *));
|
3017
|
1004 memset(delitems, 0, num*sizeof(struct aim_ssi_item *));
|
2991
|
1005
|
|
1006 /* Make the delitems array a pointer to the aim_ssi_item structs to be deleted */
|
|
1007 for (i=0; i<num; i++) {
|
3210
|
1008 if (!(delitems[i] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, sn[i], type))) {
|
2991
|
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 */
|
3017
|
1024 aim_ssi_addmoddel(sess, conn, delitems, num, AIM_CB_SSI_DEL);
|
2991
|
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
|
|
1039 return 0;
|
|
1040 }
|
|
1041
|
3210
|
1042 /**
|
2991
|
1043 * Stores your permit/deny setting on the server, and starts using it.
|
3210
|
1044 *
|
|
1045 * @param sess The oscar session.
|
|
1046 * @param conn The bos connection for this session.
|
|
1047 * @param permdeny Your permit/deny setting. Can be one of the following:
|
|
1048 * 1 - Allow all users
|
|
1049 * 2 - Block all users
|
|
1050 * 3 - Allow only the users below
|
|
1051 * 4 - Block only the users below
|
|
1052 * 5 - Allow only users on my buddy list
|
|
1053 * @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
|
|
1055 * @return Return 0 if no errors, otherwise return the error number.
|
2991
|
1056 */
|
3210
|
1057 faim_export int aim_ssi_setpermdeny(aim_session_t *sess, aim_conn_t *conn, fu8_t permdeny, fu32_t vismask) {
|
3348
|
1058 struct aim_ssi_item *cur;
|
2991
|
1059 aim_tlv_t *tlv;
|
|
1060
|
|
1061 if (!sess || !conn)
|
|
1062 return -EINVAL;
|
|
1063
|
|
1064 /* Look up the permit/deny settings item */
|
3210
|
1065 cur = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, NULL, AIM_SSI_TYPE_PDINFO);
|
2991
|
1066
|
|
1067 if (cur) {
|
|
1068 /* The permit/deny item exists */
|
|
1069 if (cur->data && (tlv = aim_gettlv(cur->data, 0x00ca, 1))) {
|
|
1070 /* Just change the value of the x00ca TLV */
|
|
1071 if (tlv->length != 1) {
|
|
1072 tlv->length = 1;
|
|
1073 free(tlv->value);
|
|
1074 tlv->value = (fu8_t *)malloc(sizeof(fu8_t));
|
|
1075 }
|
3090
|
1076 tlv->value[0] = permdeny;
|
2991
|
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
|
3210
|
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
|
2991
|
1095 /* Send the mod item SNAC */
|
3017
|
1096 aim_ssi_addmoddel(sess, conn, &cur, 1, AIM_CB_SSI_MOD);
|
2991
|
1097 } else {
|
|
1098 /* Need to add the permit/deny item */
|
3210
|
1099 if (!(cur = aim_ssi_itemlist_add(&sess->ssi.items, NULL, NULL, AIM_SSI_TYPE_PDINFO)))
|
2991
|
1100 return -ENOMEM;
|
|
1101 aim_addtlvtochain8((aim_tlvlist_t**)&cur->data, 0x00ca, permdeny);
|
3210
|
1102 aim_addtlvtochain32((aim_tlvlist_t**)&cur->data, 0x00cb, vismask);
|
3017
|
1103 aim_ssi_addmoddel(sess, conn, &cur, 1, AIM_CB_SSI_ADD);
|
2991
|
1104 }
|
|
1105
|
|
1106 /* Begin sending SSI SNACs */
|
|
1107 aim_ssi_dispatch(sess, conn);
|
|
1108
|
|
1109 return 0;
|
|
1110 }
|
|
1111
|
3210
|
1112 /**
|
3109
|
1113 * Stores your setting for whether you should show up as idle or not.
|
3210
|
1114 *
|
|
1115 * @param sess The oscar session.
|
|
1116 * @param conn The bos connection for this session.
|
|
1117 * @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
|
|
1119 * @return Return 0 if no errors, otherwise return the error number.
|
3109
|
1120 */
|
|
1121 faim_export int aim_ssi_setpresence(aim_session_t *sess, aim_conn_t *conn, fu32_t presence) {
|
3348
|
1122 struct aim_ssi_item *cur;
|
3109
|
1123 aim_tlv_t *tlv;
|
|
1124
|
|
1125 if (!sess || !conn)
|
|
1126 return -EINVAL;
|
|
1127
|
|
1128 /* Look up the item */
|
3210
|
1129 cur = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS);
|
3109
|
1130
|
|
1131 if (cur) {
|
|
1132 /* The item exists */
|
|
1133 if (cur->data && (tlv = aim_gettlv(cur->data, 0x00c9, 1))) {
|
|
1134 /* Just change the value of the x00c9 TLV */
|
|
1135 if (tlv->length != 4) {
|
|
1136 tlv->length = 4;
|
|
1137 free(tlv->value);
|
|
1138 tlv->value = (fu8_t *)malloc(4*sizeof(fu8_t));
|
|
1139 }
|
|
1140 aimutil_put32(tlv->value, presence);
|
|
1141 } else {
|
|
1142 /* Need to add the x00c9 TLV to the TLV chain */
|
|
1143 aim_addtlvtochain32((aim_tlvlist_t**)&cur->data, 0x00c9, presence);
|
|
1144 }
|
|
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 */
|
3210
|
1150 if (!(cur = aim_ssi_itemlist_add(&sess->ssi.items, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS)))
|
3109
|
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 /*
|
2672
|
1163 * Request SSI Rights.
|
|
1164 */
|
|
1165 faim_export int aim_ssi_reqrights(aim_session_t *sess, aim_conn_t *conn)
|
|
1166 {
|
3017
|
1167 return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_REQRIGHTS);
|
2672
|
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
|
|
1178 if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
|
|
1179 ret = userfunc(sess, rx);
|
|
1180
|
|
1181 return ret;
|
|
1182 }
|
|
1183
|
|
1184 /*
|
|
1185 * Request SSI Data.
|
|
1186 *
|
|
1187 * The data will only be sent if it is newer than the posted local
|
|
1188 * timestamp and revision.
|
|
1189 *
|
|
1190 * Note that the client should never increment the revision, only the server.
|
|
1191 *
|
|
1192 */
|
|
1193 faim_export int aim_ssi_reqdata(aim_session_t *sess, aim_conn_t *conn, time_t localstamp, fu16_t localrev)
|
|
1194 {
|
|
1195 aim_frame_t *fr;
|
|
1196 aim_snacid_t snacid;
|
|
1197
|
|
1198 if (!sess || !conn)
|
|
1199 return -EINVAL;
|
|
1200
|
|
1201 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4+2)))
|
|
1202 return -ENOMEM;
|
|
1203
|
3017
|
1204 snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_REQLIST, 0x0000, NULL, 0);
|
2672
|
1205
|
3017
|
1206 aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_REQLIST, 0x0000, snacid);
|
2672
|
1207 aimbs_put32(&fr->data, localstamp);
|
|
1208 aimbs_put16(&fr->data, localrev);
|
|
1209
|
|
1210 aim_tx_enqueue(sess, fr);
|
|
1211
|
|
1212 return 0;
|
|
1213 }
|
|
1214
|
|
1215 /*
|
|
1216 * SSI Data.
|
|
1217 */
|
|
1218 static int parsedata(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
|
|
1219 {
|
|
1220 int ret = 0;
|
|
1221 aim_rxcallback_t userfunc;
|
2991
|
1222 struct aim_ssi_item *cur = NULL;
|
2672
|
1223 fu8_t fmtver; /* guess */
|
2991
|
1224 fu16_t revision;
|
|
1225 fu32_t timestamp;
|
2672
|
1226
|
3210
|
1227 /* When you set the version for the SSI family to 2-4, the beginning of this changes.
|
|
1228 * Instead of the version and then the revision, there is "0x0006" and then a type
|
|
1229 * 0x0001 TLV containing the 2 byte SSI family version that you sent earlier. Also,
|
|
1230 * the SNAC flags go from 0x0000 to 0x8000. I guess the 0x0006 is the length of the
|
|
1231 * TLV(s) that follow. The rights SNAC does the same thing, with the differing flag
|
|
1232 * and everything.
|
|
1233 */
|
|
1234
|
2991
|
1235 fmtver = aimbs_get8(bs); /* Version of ssi data. Should be 0x00 */
|
|
1236 revision = aimbs_get16(bs); /* # of times ssi data has been modified */
|
|
1237 if (revision != 0)
|
|
1238 sess->ssi.revision = revision;
|
|
1239
|
|
1240 for (cur = sess->ssi.items; cur && cur->next; cur=cur->next) ;
|
2672
|
1241
|
|
1242 while (aim_bstream_empty(bs) > 4) { /* last four bytes are stamp */
|
|
1243 fu16_t namelen, tbslen;
|
|
1244
|
2991
|
1245 if (!sess->ssi.items) {
|
|
1246 if (!(sess->ssi.items = malloc(sizeof(struct aim_ssi_item))))
|
|
1247 return -ENOMEM;
|
|
1248 cur = sess->ssi.items;
|
|
1249 } else {
|
|
1250 if (!(cur->next = malloc(sizeof(struct aim_ssi_item))))
|
|
1251 return -ENOMEM;
|
|
1252 cur = cur->next;
|
|
1253 }
|
|
1254 memset(cur, 0, sizeof(struct aim_ssi_item));
|
2672
|
1255
|
|
1256 if ((namelen = aimbs_get16(bs)))
|
2991
|
1257 cur->name = aimbs_getstr(bs, namelen);
|
|
1258 cur->gid = aimbs_get16(bs);
|
|
1259 cur->bid = aimbs_get16(bs);
|
|
1260 cur->type = aimbs_get16(bs);
|
2672
|
1261
|
|
1262 if ((tbslen = aimbs_get16(bs))) {
|
|
1263 aim_bstream_t tbs;
|
|
1264
|
|
1265 aim_bstream_init(&tbs, bs->data + bs->offset /* XXX */, tbslen);
|
2991
|
1266 cur->data = (void *)aim_readtlvchain(&tbs);
|
2672
|
1267 aim_bstream_advance(bs, tbslen);
|
|
1268 }
|
|
1269 }
|
|
1270
|
2991
|
1271 timestamp = aimbs_get32(bs);
|
|
1272 if (timestamp != 0)
|
|
1273 sess->ssi.timestamp = timestamp;
|
|
1274 sess->ssi.received_data = 1;
|
2672
|
1275
|
|
1276 if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
|
2991
|
1277 ret = userfunc(sess, rx, fmtver, sess->ssi.revision, sess->ssi.timestamp, sess->ssi.items);
|
2672
|
1278
|
|
1279 return ret;
|
|
1280 }
|
|
1281
|
|
1282 /*
|
|
1283 * SSI Data Enable Presence.
|
|
1284 *
|
|
1285 * Should be sent after receiving 13/6 or 13/f to tell the server you
|
|
1286 * are ready to begin using the list. It will promptly give you the
|
|
1287 * presence information for everyone in your list and put your permit/deny
|
|
1288 * settings into effect.
|
|
1289 *
|
|
1290 */
|
|
1291 faim_export int aim_ssi_enable(aim_session_t *sess, aim_conn_t *conn)
|
|
1292 {
|
3017
|
1293 return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, 0x0007);
|
2672
|
1294 }
|
|
1295
|
|
1296 /*
|
3017
|
1297 * SSI Add/Mod/Del Item(s).
|
2991
|
1298 *
|
3017
|
1299 * Sends the SNAC to add, modify, or delete an item from the server-stored
|
|
1300 * information. These 3 SNACs all have an identical structure. The only
|
|
1301 * difference is the subtype that is set for the SNAC.
|
2991
|
1302 *
|
|
1303 */
|
3017
|
1304 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)
|
2991
|
1305 {
|
|
1306 aim_frame_t *fr;
|
|
1307 aim_snacid_t snacid;
|
|
1308 int i, snaclen;
|
|
1309
|
|
1310 if (!sess || !conn || !items || !num)
|
|
1311 return -EINVAL;
|
|
1312
|
|
1313 snaclen = 10; /* For family, subtype, flags, and SNAC ID */
|
|
1314 for (i=0; i<num; i++) {
|
|
1315 snaclen += 10; /* For length, GID, BID, type, and length */
|
|
1316 if (items[i]->name)
|
|
1317 snaclen += strlen(items[i]->name);
|
|
1318 if (items[i]->data)
|
|
1319 snaclen += aim_sizetlvchain((aim_tlvlist_t **)&items[i]->data);
|
|
1320 }
|
|
1321
|
|
1322 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, snaclen)))
|
|
1323 return -ENOMEM;
|
|
1324
|
3017
|
1325 snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, subtype, 0x0000, NULL, 0);
|
|
1326 aim_putsnac(&fr->data, AIM_CB_FAM_SSI, subtype, 0x0000, snacid);
|
2991
|
1327
|
|
1328 for (i=0; i<num; i++) {
|
|
1329 aimbs_put16(&fr->data, items[i]->name ? strlen(items[i]->name) : 0);
|
|
1330 if (items[i]->name)
|
|
1331 aimbs_putraw(&fr->data, items[i]->name, strlen(items[i]->name));
|
|
1332 aimbs_put16(&fr->data, items[i]->gid);
|
|
1333 aimbs_put16(&fr->data, items[i]->bid);
|
|
1334 aimbs_put16(&fr->data, items[i]->type);
|
|
1335 aimbs_put16(&fr->data, items[i]->data ? aim_sizetlvchain((aim_tlvlist_t **)&items[i]->data) : 0);
|
|
1336 if (items[i]->data)
|
|
1337 aim_writetlvchain(&fr->data, (aim_tlvlist_t **)&items[i]->data);
|
|
1338 }
|
|
1339
|
|
1340 aim_ssi_enqueue(sess, conn, fr);
|
|
1341
|
|
1342 return 0;
|
|
1343 }
|
|
1344
|
|
1345 /*
|
|
1346 * SSI Add/Mod/Del Ack.
|
|
1347 *
|
3017
|
1348 * Response to add, modify, or delete SNAC (sent with aim_ssi_addmoddel).
|
2991
|
1349 *
|
|
1350 */
|
|
1351 static int parseack(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
|
|
1352 {
|
|
1353 int ret = 0;
|
|
1354 aim_rxcallback_t userfunc;
|
|
1355
|
|
1356 sess->ssi.waiting_for_ack = 0;
|
|
1357 aim_ssi_dispatch(sess, rx->conn);
|
|
1358
|
|
1359 if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
|
|
1360 ret = userfunc(sess, rx);
|
|
1361
|
|
1362 return ret;
|
|
1363 }
|
|
1364
|
|
1365 /*
|
2672
|
1366 * SSI Begin Data Modification.
|
|
1367 *
|
|
1368 * Tells the server you're going to start modifying data.
|
|
1369 *
|
|
1370 */
|
|
1371 faim_export int aim_ssi_modbegin(aim_session_t *sess, aim_conn_t *conn)
|
|
1372 {
|
3017
|
1373 return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_EDITSTART);
|
2672
|
1374 }
|
|
1375
|
|
1376 /*
|
|
1377 * SSI End Data Modification.
|
|
1378 *
|
|
1379 * Tells the server you're done modifying data.
|
|
1380 *
|
|
1381 */
|
|
1382 faim_export int aim_ssi_modend(aim_session_t *sess, aim_conn_t *conn)
|
|
1383 {
|
3017
|
1384 return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_EDITSTOP);
|
2672
|
1385 }
|
|
1386
|
|
1387 /*
|
|
1388 * SSI Data Unchanged.
|
|
1389 *
|
|
1390 * Response to aim_ssi_reqdata() if the server-side data is not newer than
|
|
1391 * posted local stamp/revision.
|
|
1392 *
|
|
1393 */
|
|
1394 static int parsedataunchanged(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
|
|
1395 {
|
|
1396 int ret = 0;
|
|
1397 aim_rxcallback_t userfunc;
|
|
1398
|
2991
|
1399 sess->ssi.received_data = 1;
|
|
1400
|
2672
|
1401 if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
|
|
1402 ret = userfunc(sess, rx);
|
|
1403
|
|
1404 return ret;
|
|
1405 }
|
|
1406
|
|
1407 static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
|
|
1408 {
|
|
1409
|
3017
|
1410 if (snac->subtype == AIM_CB_SSI_RIGHTSINFO)
|
2672
|
1411 return parserights(sess, mod, rx, snac, bs);
|
3017
|
1412 else if (snac->subtype == AIM_CB_SSI_LIST)
|
2672
|
1413 return parsedata(sess, mod, rx, snac, bs);
|
2991
|
1414 else if (snac->subtype == AIM_CB_SSI_SRVACK)
|
|
1415 return parseack(sess, mod, rx, snac, bs);
|
3017
|
1416 else if (snac->subtype == AIM_CB_SSI_NOLIST)
|
2672
|
1417 return parsedataunchanged(sess, mod, rx, snac, bs);
|
|
1418
|
|
1419 return 0;
|
|
1420 }
|
|
1421
|
2991
|
1422 static void ssi_shutdown(aim_session_t *sess, aim_module_t *mod)
|
|
1423 {
|
|
1424 aim_ssi_freelist(sess);
|
|
1425
|
|
1426 return;
|
|
1427 }
|
|
1428
|
2672
|
1429 faim_internal int ssi_modfirst(aim_session_t *sess, aim_module_t *mod)
|
|
1430 {
|
|
1431
|
3017
|
1432 mod->family = AIM_CB_FAM_SSI;
|
2672
|
1433 mod->version = 0x0001;
|
|
1434 mod->toolid = 0x0110;
|
|
1435 mod->toolversion = 0x047b;
|
|
1436 mod->flags = 0;
|
|
1437 strncpy(mod->name, "ssi", sizeof(mod->name));
|
|
1438 mod->snachandler = snachandler;
|
2991
|
1439 mod->shutdown = ssi_shutdown;
|
2672
|
1440
|
|
1441 return 0;
|
|
1442 }
|