comparison src/protocols/oscar/locate.c @ 7011:4375bf2d9020

[gaim-migrate @ 7574] The user-visible changes? Gaim now shows peep's away messages in their tooltip for AIM over oscar. Things to do: -Make sure this doesn't screw up with huge buddy lists -Replace %n with your screen name, etc. in the tooltip -Leave in b, i, u tags in the tooltip -Fix the 2 8 byte memleaks in locate.c Client authors that aren't me will want to read the following pretty closely... I made some internal libfaim changes. I desire to make libfaim cleaner. I don't know if this really helps or not. Here's what I've done: Changed all the SNAC-sending functions in info.c to NOT take a conn argument. The connection is looked up from the session. I'm trying to make oscar.c less-aware of connections. Added aim_locate_finduserinfo() - Use for getting the aim_userinfo_t for the given screenname. Added aim_locate_getinfoshort() - Use this to request that the servers send you profile info, away message, caps, or a combination of the above. It's like aim_locate_getinfo() but less rate limited. Renamed aim_bos_reqlocaterights() to aim_locate_reqrights() Renamed aim_getinfo() to aim_locate_getinfo() Renamed aim_setdirectoryinfo() to aim_locate_setdirinfo() Renamed aim_0002_000b() to aim_locate_000b() Renamed aim_setuserinterests() to aim_locate_setinterests() Removed aim_userinfo_sn() Removed aim_userinfo_flags() Removed aim_userinfo_idle() Removed aim_userinfo_warnlevel() Removed aim_userinfo_createtime() Removed aim_userinfo_membersince() Removed aim_userinfo_onlinesince() Removed aim_userinfo_sessionlen() Removed aim_userinfo_hascap() Renamed info.c to locate.c Made locate.c keep a static, linked list of nodes of sn, away message, available message, user info. This list is maintained by libfaim automatically. Now clients don't have to keep track of all this stuff themselves, and users won't have to wait for away message/info retrieval if the person is in your buddy list. libfaim uses the iChat way of retrieving stuff, which is not nearly as rate limited as the old way. I actually have a feeling WinAIM uses the same SNAC now (although I didn't check), but I like pluggin iChat because it's innovative. Moved sess->emailinfo to a static variable inside email.c. Removed evilhack from oscar.c I think that's about everything... committer: Tailor Script <tailor@pidgin.im>
author Mark Doliner <mark@kingant.net>
date Mon, 29 Sep 2003 12:30:03 +0000
parents
children db041f206848
comparison
equal deleted inserted replaced
7010:eb3ed649252e 7011:4375bf2d9020
1 /*
2 * Family 0x0002 - Locate.
3 *
4 * The functions here are responsible for requesting and parsing information-
5 * gathering SNACs. Or something like that. This family contains the SNACs
6 * for getting and setting info, away messages, directory profile thingy, etc.
7 */
8
9 #define FAIM_INTERNAL
10 #include <aim.h>
11 #ifdef _WIN32
12 #include "win32dep.h"
13 #endif
14
15 struct node {
16 char *sn;
17 struct node *next;
18 };
19
20 /**
21 * Keep an aim_userinfo_t for each user we are aware of.
22 */
23 static aim_userinfo_t *infos = NULL;
24 static struct node *request_queue = NULL;
25 static int waiting_for_response = FALSE;
26
27 /*
28 * Capability blocks.
29 *
30 * These are CLSIDs. They should actually be of the form:
31 *
32 * {0x0946134b, 0x4c7f, 0x11d1,
33 * {0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}},
34 *
35 * But, eh.
36 */
37 static const struct {
38 fu32_t flag;
39 fu8_t data[16];
40 } aim_caps[] = {
41
42 /*
43 * These are in ascending numerical order.
44 */
45 {AIM_CAPS_ICHAT,
46 {0x09, 0x46, 0x00, 0x00, 0x4c, 0x7f, 0x11, 0xd1,
47 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
48
49 {AIM_CAPS_SECUREIM,
50 {0x09, 0x46, 0x00, 0x01, 0x4c, 0x7f, 0x11, 0xd1,
51 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
52
53 {AIM_CAPS_HIPTOP,
54 {0x09, 0x46, 0x13, 0x23, 0x4c, 0x7f, 0x11, 0xd1,
55 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
56
57 {AIM_CAPS_VOICE,
58 {0x09, 0x46, 0x13, 0x41, 0x4c, 0x7f, 0x11, 0xd1,
59 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
60
61 {AIM_CAPS_SENDFILE,
62 {0x09, 0x46, 0x13, 0x43, 0x4c, 0x7f, 0x11, 0xd1,
63 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
64
65 /*
66 * Advertised by the EveryBuddy client.
67 */
68 {AIM_CAPS_ICQ,
69 {0x09, 0x46, 0x13, 0x44, 0x4c, 0x7f, 0x11, 0xd1,
70 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
71
72 {AIM_CAPS_DIRECTIM,
73 {0x09, 0x46, 0x13, 0x45, 0x4c, 0x7f, 0x11, 0xd1,
74 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
75
76 {AIM_CAPS_BUDDYICON,
77 {0x09, 0x46, 0x13, 0x46, 0x4c, 0x7f, 0x11, 0xd1,
78 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
79
80 /*
81 * Windows AIM calls this "Add-ins," which is probably more accurate
82 */
83 {AIM_CAPS_SAVESTOCKS,
84 {0x09, 0x46, 0x13, 0x47, 0x4c, 0x7f, 0x11, 0xd1,
85 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
86
87 {AIM_CAPS_GETFILE,
88 {0x09, 0x46, 0x13, 0x48, 0x4c, 0x7f, 0x11, 0xd1,
89 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
90
91 {AIM_CAPS_ICQSERVERRELAY,
92 {0x09, 0x46, 0x13, 0x49, 0x4c, 0x7f, 0x11, 0xd1,
93 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
94
95 /*
96 * Indeed, there are two of these. The former appears to be correct,
97 * but in some versions of winaim, the second one is set. Either they
98 * forgot to fix endianness, or they made a typo. It really doesn't
99 * matter which.
100 */
101 {AIM_CAPS_GAMES,
102 {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1,
103 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
104 {AIM_CAPS_GAMES2,
105 {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1,
106 0x22, 0x82, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
107
108 {AIM_CAPS_SENDBUDDYLIST,
109 {0x09, 0x46, 0x13, 0x4b, 0x4c, 0x7f, 0x11, 0xd1,
110 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
111
112 /*
113 * Setting this lets AIM users receive messages from ICQ users, and ICQ
114 * users receive messages from AIM users. It also lets ICQ users show
115 * up in buddy lists for AIM users, and AIM users show up in buddy lists
116 * for ICQ users. And ICQ privacy/invisibility acts like AIM privacy,
117 * in that if you add a user to your deny list, you will not be able to
118 * see them as online (previous you could still see them, but they
119 * couldn't see you.
120 */
121 {AIM_CAPS_INTEROPERATE,
122 {0x09, 0x46, 0x13, 0x4d, 0x4c, 0x7f, 0x11, 0xd1,
123 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
124
125 {AIM_CAPS_ICQUTF8,
126 {0x09, 0x46, 0x13, 0x4e, 0x4c, 0x7f, 0x11, 0xd1,
127 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
128
129 {AIM_CAPS_ICQUTF8OLD,
130 {0x2e, 0x7a, 0x64, 0x75, 0xfa, 0xdf, 0x4d, 0xc8,
131 0x88, 0x6f, 0xea, 0x35, 0x95, 0xfd, 0xb6, 0xdf}},
132
133 /*
134 * Chat is oddball.
135 */
136 {AIM_CAPS_CHAT,
137 {0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1,
138 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
139
140 /*
141 {AIM_CAPS_ICQ2GO,
142 {0x56, 0x3f, 0xc8, 0x09, 0x0b, 0x6f, 0x41, 0xbd,
143 0x9f, 0x79, 0x42, 0x26, 0x09, 0xdf, 0xa2, 0xf3}},
144 */
145
146 {AIM_CAPS_ICQRTF,
147 {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34,
148 0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x92}},
149
150 /* supposed to be ICQRTF?
151 {AIM_CAPS_TRILLUNKNOWN,
152 {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34,
153 0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x09}}, */
154
155 {AIM_CAPS_APINFO,
156 {0xaa, 0x4a, 0x32, 0xb5, 0xf8, 0x84, 0x48, 0xc6,
157 0xa3, 0xd7, 0x8c, 0x50, 0x97, 0x19, 0xfd, 0x5b}},
158
159 {AIM_CAPS_TRILLIANCRYPT,
160 {0xf2, 0xe7, 0xc7, 0xf4, 0xfe, 0xad, 0x4d, 0xfb,
161 0xb2, 0x35, 0x36, 0x79, 0x8b, 0xdf, 0x00, 0x00}},
162
163 {AIM_CAPS_EMPTY,
164 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
165 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
166
167 {AIM_CAPS_LAST}
168 };
169
170 /*
171 * Add the userinfo to our linked list. If we already have userinfo
172 * for this buddy, then just overwrite parts of the old data.
173 * @param userinfo Contains the new information for the buddy.
174 */
175 static void aim_locate_adduserinfo(aim_userinfo_t *userinfo) {
176 aim_userinfo_t *cur;
177
178 cur = aim_locate_finduserinfo(userinfo->sn);
179
180 if (cur == NULL) {
181 cur = (aim_userinfo_t *)calloc(1, sizeof(aim_userinfo_t));
182 cur->sn = strdup(userinfo->sn);
183 cur->next = infos;
184 infos = cur;
185 }
186
187 cur->warnlevel = userinfo->warnlevel;
188 cur->idletime = userinfo->idletime;
189 if (userinfo->flags != 0)
190 cur->flags = userinfo->flags;
191 if (userinfo->createtime != 0)
192 cur->createtime = userinfo->createtime;
193 if (userinfo->membersince != 0)
194 cur->membersince = userinfo->membersince;
195 if (userinfo->onlinesince != 0)
196 cur->onlinesince = userinfo->onlinesince;
197 if (userinfo->sessionlen != 0)
198 cur->sessionlen = userinfo->sessionlen;
199 if (userinfo->capabilities != 0)
200 cur->capabilities = userinfo->capabilities;
201 cur->present |= userinfo->present;
202
203 if ((userinfo->away != NULL) && (userinfo->away_len > 0)) {
204 free(cur->away);
205 free(cur->away_encoding);
206 cur->away = (char *)malloc(userinfo->away_len);
207 memcpy(cur->away, userinfo->away, userinfo->away_len);
208 cur->away_encoding = strdup(userinfo->away_encoding); /* XXX - This seems to leak occasionally */
209 cur->away_len = userinfo->away_len;
210 }
211
212 if ((userinfo->info != NULL) && (userinfo->info_len > 0)) {
213 free(cur->info);
214 free(cur->info_encoding);
215 cur->info = (char *)malloc(userinfo->info_len);
216 memcpy(cur->info, userinfo->info, userinfo->info_len);
217 cur->info_encoding = strdup(userinfo->info_encoding); /* XXX - This seems to leak occasionally */
218 cur->info_len = userinfo->info_len;
219 }
220 }
221
222 static void aim_locate_dorequest(aim_session_t *sess) {
223 struct node *cur = request_queue;
224
225 if (cur == NULL)
226 return;
227
228 if (waiting_for_response == TRUE)
229 return;
230
231 waiting_for_response = TRUE;
232 aim_locate_getinfoshort(sess, cur->sn, 0x00000007);
233 }
234
235 faim_internal void aim_locate_requestuserinfo(aim_session_t *sess, const char *sn) {
236 struct node *cur;
237
238 cur = (struct node *)malloc(sizeof(struct node));
239 cur->sn = strdup(sn);
240 cur->next = request_queue;
241 request_queue = cur;
242
243 aim_locate_dorequest(sess);
244 }
245
246 faim_export aim_userinfo_t *aim_locate_finduserinfo(const char *sn) {
247 aim_userinfo_t *cur = infos;
248
249 while (cur != NULL) {
250 if (aim_sncmp(cur->sn, sn) == 0)
251 return cur;
252 cur = cur->next;
253 }
254
255 return NULL;
256 }
257
258 /*
259 * This still takes a length parameter even with a bstream because capabilities
260 * are not naturally bounded.
261 */
262 faim_internal fu32_t aim_getcap(aim_session_t *sess, aim_bstream_t *bs, int len)
263 {
264 fu32_t flags = 0;
265 int offset;
266
267 for (offset = 0; aim_bstream_empty(bs) && (offset < len); offset += 0x10) {
268 fu8_t *cap;
269 int i, identified;
270
271 cap = aimbs_getraw(bs, 0x10);
272
273 for (i = 0, identified = 0; !(aim_caps[i].flag & AIM_CAPS_LAST); i++) {
274
275 if (memcmp(&aim_caps[i].data, cap, 0x10) == 0) {
276 flags |= aim_caps[i].flag;
277 identified++;
278 break; /* should only match once... */
279
280 }
281 }
282
283 if (!identified) {
284 faimdprintf(sess, 0, "unknown capability: {%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n",
285 cap[0], cap[1], cap[2], cap[3],
286 cap[4], cap[5],
287 cap[6], cap[7],
288 cap[8], cap[9],
289 cap[10], cap[11], cap[12], cap[13],
290 cap[14], cap[15]);
291 }
292
293 free(cap);
294 }
295
296 return flags;
297 }
298
299 faim_internal int aim_putcap(aim_bstream_t *bs, fu32_t caps)
300 {
301 int i;
302
303 if (!bs)
304 return -EINVAL;
305
306 for (i = 0; aim_bstream_empty(bs); i++) {
307
308 if (aim_caps[i].flag == AIM_CAPS_LAST)
309 break;
310
311 if (caps & aim_caps[i].flag)
312 aimbs_putraw(bs, aim_caps[i].data, 0x10);
313
314 }
315
316 return 0;
317 }
318
319 static void dumptlv(aim_session_t *sess, fu16_t type, aim_bstream_t *bs, fu8_t len)
320 {
321 int i;
322
323 if (!sess || !bs || !len)
324 return;
325
326 faimdprintf(sess, 0, "userinfo: type =0x%04x\n", type);
327 faimdprintf(sess, 0, "userinfo: length=0x%04x\n", len);
328 faimdprintf(sess, 0, "userinfo: value:\n");
329
330 for (i = 0; i < len; i++) {
331 if ((i % 8) == 0)
332 faimdprintf(sess, 0, "\nuserinfo: ");
333 faimdprintf(sess, 0, "0x%2x ", aimbs_get8(bs));
334 }
335
336 faimdprintf(sess, 0, "\n");
337
338 return;
339 }
340
341 faim_internal void aim_info_free(aim_userinfo_t *info)
342 {
343 free(info->sn);
344 free(info->iconcsum);
345 free(info->info);
346 free(info->info_encoding);
347 free(info->avail);
348 free(info->avail_encoding);
349 free(info->away);
350 free(info->away_encoding);
351 }
352
353 /*
354 * AIM is fairly regular about providing user info. This is a generic
355 * routine to extract it in its standard form.
356 */
357 faim_internal int aim_info_extract(aim_session_t *sess, aim_bstream_t *bs, aim_userinfo_t *outinfo)
358 {
359 int curtlv, tlvcnt;
360 fu8_t snlen;
361
362 if (!bs || !outinfo)
363 return -EINVAL;
364
365 /* Clear out old data first */
366 memset(outinfo, 0x00, sizeof(aim_userinfo_t));
367
368 /*
369 * Screen name. Stored as an unterminated string prepended with a
370 * byte containing its length.
371 */
372 snlen = aimbs_get8(bs);
373 outinfo->sn = aimbs_getstr(bs, snlen);
374
375 /*
376 * Warning Level. Stored as an unsigned short.
377 */
378 outinfo->warnlevel = aimbs_get16(bs);
379
380 /*
381 * TLV Count. Unsigned short representing the number of
382 * Type-Length-Value triples that follow.
383 */
384 tlvcnt = aimbs_get16(bs);
385
386 /*
387 * Parse out the Type-Length-Value triples as they're found.
388 */
389 for (curtlv = 0; curtlv < tlvcnt; curtlv++) {
390 int endpos;
391 fu16_t type, length;
392
393 type = aimbs_get16(bs);
394 length = aimbs_get16(bs);
395
396 endpos = aim_bstream_curpos(bs) + length;
397
398 if (type == 0x0001) {
399 /*
400 * Type = 0x0001: User flags
401 *
402 * Specified as any of the following ORed together:
403 * 0x0001 Trial (user less than 60days)
404 * 0x0002 Unknown bit 2
405 * 0x0004 AOL Main Service user
406 * 0x0008 Unknown bit 4
407 * 0x0010 Free (AIM) user
408 * 0x0020 Away
409 * 0x0400 ActiveBuddy
410 *
411 */
412 outinfo->flags = aimbs_get16(bs);
413 outinfo->present |= AIM_USERINFO_PRESENT_FLAGS;
414
415 } else if (type == 0x0002) {
416 /*
417 * Type = 0x0002: Account creation time.
418 *
419 * The time/date that the user originally registered for
420 * the service, stored in time_t format.
421 *
422 * I'm not sure how this differs from type 5 ("member
423 * since").
424 *
425 * Note: This is the field formerly known as "member
426 * since". All these years and I finally found out
427 * that I got the name wrong.
428 */
429 outinfo->createtime = aimbs_get32(bs);
430 outinfo->present |= AIM_USERINFO_PRESENT_CREATETIME;
431
432 } else if (type == 0x0003) {
433 /*
434 * Type = 0x0003: On-Since date.
435 *
436 * The time/date that the user started their current
437 * session, stored in time_t format.
438 */
439 outinfo->onlinesince = aimbs_get32(bs);
440 outinfo->present |= AIM_USERINFO_PRESENT_ONLINESINCE;
441
442 } else if (type == 0x0004) {
443 /*
444 * Type = 0x0004: Idle time.
445 *
446 * Number of minutes since the user actively used the
447 * service.
448 *
449 * Note that the client tells the server when to start
450 * counting idle times, so this may or may not be
451 * related to reality.
452 */
453 outinfo->idletime = aimbs_get16(bs);
454 outinfo->present |= AIM_USERINFO_PRESENT_IDLE;
455
456 } else if (type == 0x0005) {
457 /*
458 * Type = 0x0005: Member since date.
459 *
460 * The time/date that the user originally registered for
461 * the service, stored in time_t format.
462 *
463 * This is sometimes sent instead of type 2 ("account
464 * creation time"), particularly in the self-info.
465 * And particularly for ICQ?
466 */
467 outinfo->membersince = aimbs_get32(bs);
468 outinfo->present |= AIM_USERINFO_PRESENT_MEMBERSINCE;
469
470 } else if (type == 0x0006) {
471 /*
472 * Type = 0x0006: ICQ Online Status
473 *
474 * ICQ's Away/DND/etc "enriched" status. Some decoding
475 * of values done by Scott <darkagl@pcnet.com>
476 */
477 aimbs_get16(bs);
478 outinfo->icqinfo.status = aimbs_get16(bs);
479 outinfo->present |= AIM_USERINFO_PRESENT_ICQEXTSTATUS;
480
481 } else if (type == 0x000a) {
482 /*
483 * Type = 0x000a
484 *
485 * ICQ User IP Address.
486 * Ahh, the joy of ICQ security.
487 */
488 outinfo->icqinfo.ipaddr = aimbs_get32(bs);
489 outinfo->present |= AIM_USERINFO_PRESENT_ICQIPADDR;
490
491 } else if (type == 0x000c) {
492 /*
493 * Type = 0x000c
494 *
495 * random crap containing the IP address,
496 * apparently a port number, and some Other Stuff.
497 *
498 * Format is:
499 * 4 bytes - Our IP address, 0xc0 a8 01 2b for 192.168.1.43
500 *
501 *
502 */
503 aimbs_getrawbuf(bs, outinfo->icqinfo.crap, 0x25);
504 outinfo->present |= AIM_USERINFO_PRESENT_ICQDATA;
505
506 } else if (type == 0x000d) {
507 /*
508 * Type = 0x000d
509 *
510 * Capability information.
511 *
512 */
513 outinfo->capabilities = aim_getcap(sess, bs, length);
514 outinfo->present |= AIM_USERINFO_PRESENT_CAPABILITIES;
515
516 } else if (type == 0x000e) {
517 /*
518 * Type = 0x000e
519 *
520 * Unknown. Always of zero length, and always only
521 * on AOL users.
522 *
523 * Ignore.
524 *
525 */
526
527 } else if ((type == 0x000f) || (type == 0x0010)) {
528 /*
529 * Type = 0x000f: Session Length. (AIM)
530 * Type = 0x0010: Session Length. (AOL)
531 *
532 * The duration, in seconds, of the user's current
533 * session.
534 *
535 * Which TLV type this comes in depends on the
536 * service the user is using (AIM or AOL).
537 *
538 */
539 outinfo->sessionlen = aimbs_get32(bs);
540 outinfo->present |= AIM_USERINFO_PRESENT_SESSIONLEN;
541
542 } else if (type == 0x0019) {
543 /* faimdprintf(sess, 0, "userinfo: **warning: unexpected TLV type 0x0019: from %s\n", outinfo->sn); */
544
545 } else if (type == 0x001b) {
546 /* faimdprintf(sess, 0, "userinfo: **warning: unexpected TLV type 0x001b: from %s\n", outinfo->sn); */
547
548 } else if (type == 0x001d) {
549 /*
550 * Type = 0x001d
551 *
552 * Buddy icon information and available messages.
553 *
554 * This almost seems like the AIM protocol guys gave
555 * the iChat guys a Type, and the iChat guys tried to
556 * cram as much cool shit into it as possible. Then
557 * the Windows AIM guys were like, "hey, that's
558 * pretty neat, let's copy those prawns."
559 *
560 * In that spirit, this can contain a custom message,
561 * kind of like an away message, but you're not away
562 * (it's called an "available" message). Or it can
563 * contain information about the buddy icon the user
564 * has stored on the server.
565 */
566 int type2, number, length2;
567
568 while (aim_bstream_curpos(bs) < endpos) {
569 type2 = aimbs_get16(bs);
570 number = aimbs_get8(bs);
571 length2 = aimbs_get8(bs);
572
573 switch (type2) {
574 case 0x0000: { /* This is an official buddy icon? */
575 /* This is always 5 bytes of "0x02 01 d2 04 72"? */
576 aim_bstream_advance(bs, length2);
577 } break;
578
579 case 0x0001: { /* A buddy icon checksum */
580 if ((length2 > 0) && (number == 0x01)) {
581 free(outinfo->iconcsum);
582 outinfo->iconcsum = aimbs_getraw(bs, length2);
583 outinfo->iconcsumlen = length2;
584 } else
585 aim_bstream_advance(bs, length2);
586 } break;
587
588 case 0x0002: { /* An available message */
589 if (length2 > 4) {
590 free(outinfo->avail);
591 outinfo->avail_len = aimbs_get16(bs);
592 outinfo->avail = aimbs_getstr(bs, outinfo->avail_len);
593 if (aimbs_get16(bs) == 0x0001) { /* We have an encoding */
594 aimbs_get16(bs);
595 outinfo->avail_encoding = aimbs_getstr(bs, aimbs_get16(bs));
596 } else {
597 /* No explicit encoding, client should use UTF-8 */
598 outinfo->avail_encoding = NULL;
599 }
600 } else
601 aim_bstream_advance(bs, length2);
602 } break;
603
604 default: {
605 aim_bstream_advance(bs, length2);
606 } break;
607 }
608 }
609
610 } else if (type == 0x001e) {
611 /*
612 * Type 30: Unknown.
613 *
614 * Always four bytes, but it doesn't look like an int.
615 */
616 } else {
617
618 /*
619 * Reaching here indicates that either AOL has
620 * added yet another TLV for us to deal with,
621 * or the parsing has gone Terribly Wrong.
622 *
623 * Either way, inform the owner and attempt
624 * recovery.
625 *
626 */
627 faimdprintf(sess, 0, "userinfo: **warning: unexpected TLV:\n");
628 faimdprintf(sess, 0, "userinfo: sn =%s\n", outinfo->sn);
629 dumptlv(sess, type, bs, length);
630 }
631
632 /* Save ourselves. */
633 aim_bstream_setpos(bs, endpos);
634 }
635
636 aim_locate_adduserinfo(outinfo);
637
638 return 0;
639 }
640
641 /*
642 * Inverse of aim_info_extract()
643 */
644 faim_internal int aim_putuserinfo(aim_bstream_t *bs, aim_userinfo_t *info)
645 {
646 aim_tlvlist_t *tlvlist = NULL;
647
648 if (!bs || !info)
649 return -EINVAL;
650
651 aimbs_put8(bs, strlen(info->sn));
652 aimbs_putraw(bs, info->sn, strlen(info->sn));
653
654 aimbs_put16(bs, info->warnlevel);
655
656 if (info->present & AIM_USERINFO_PRESENT_FLAGS)
657 aim_addtlvtochain16(&tlvlist, 0x0001, info->flags);
658 if (info->present & AIM_USERINFO_PRESENT_MEMBERSINCE)
659 aim_addtlvtochain32(&tlvlist, 0x0002, info->membersince);
660 if (info->present & AIM_USERINFO_PRESENT_ONLINESINCE)
661 aim_addtlvtochain32(&tlvlist, 0x0003, info->onlinesince);
662 if (info->present & AIM_USERINFO_PRESENT_IDLE)
663 aim_addtlvtochain16(&tlvlist, 0x0004, info->idletime);
664
665 /* XXX - So, ICQ_OSCAR_SUPPORT is never defined anywhere... */
666 #if ICQ_OSCAR_SUPPORT
667 if (atoi(info->sn) != 0) {
668 if (info->present & AIM_USERINFO_PRESENT_ICQEXTSTATUS)
669 aim_addtlvtochain16(&tlvlist, 0x0006, info->icqinfo.status);
670 if (info->present & AIM_USERINFO_PRESENT_ICQIPADDR)
671 aim_addtlvtochain32(&tlvlist, 0x000a, info->icqinfo.ipaddr);
672 }
673 #endif
674
675 if (info->present & AIM_USERINFO_PRESENT_CAPABILITIES)
676 aim_addtlvtochain_caps(&tlvlist, 0x000d, info->capabilities);
677
678 if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN)
679 aim_addtlvtochain32(&tlvlist, (fu16_t)((info->flags & AIM_FLAG_AOL) ? 0x0010 : 0x000f), info->sessionlen);
680
681 aimbs_put16(bs, aim_counttlvchain(&tlvlist));
682 aim_writetlvchain(bs, &tlvlist);
683 aim_freetlvchain(&tlvlist);
684
685 return 0;
686 }
687
688 /*
689 * Subtype 0x0002
690 *
691 * Request Location services rights.
692 *
693 */
694 faim_export int aim_locate_reqrights(aim_session_t *sess)
695 {
696 aim_conn_t *conn;
697
698 if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC)))
699 return -EINVAL;
700
701 return aim_genericreq_n(sess, conn, AIM_CB_FAM_LOC, AIM_CB_LOC_REQRIGHTS);
702 }
703
704 /*
705 * Subtype 0x0003
706 *
707 * Normally contains:
708 * t(0001) - short containing max profile length (value = 1024)
709 * t(0002) - short - unknown (value = 16) [max MIME type length?]
710 * t(0003) - short - unknown (value = 10)
711 * t(0004) - short - unknown (value = 2048) [ICQ only?]
712 */
713 static int rights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
714 {
715 aim_tlvlist_t *tlvlist;
716 aim_rxcallback_t userfunc;
717 int ret = 0;
718 fu16_t maxsiglen = 0;
719
720 tlvlist = aim_readtlvchain(bs);
721
722 if (aim_gettlv(tlvlist, 0x0001, 1))
723 maxsiglen = aim_gettlv16(tlvlist, 0x0001, 1);
724
725 if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
726 ret = userfunc(sess, rx, maxsiglen);
727
728 aim_freetlvchain(&tlvlist);
729
730 return ret;
731 }
732
733 /*
734 * Subtype 0x0004
735 *
736 * Gives BOS your profile.
737 *
738 * profile_encoding and awaymsg_encoding MUST be set if profile or
739 * away are set, respectively, and their value may or may not be
740 * restricted to a few choices. I am currently aware of:
741 *
742 * us-ascii Just that
743 * unicode-2-0 UCS2-BE
744 *
745 * profile_len and awaymsg_len MUST be set similarly, and they MUST
746 * be the length of their respective strings in bytes.
747 *
748 * To get the previous behavior of awaymsg == "" un-setting the away
749 * message, set awaymsg non-NULL and awaymsg_len to 0 (this is the
750 * obvious equivalent).
751 *
752 */
753 faim_export int aim_locate_setprofile(aim_session_t *sess,
754 const char *profile_encoding, const char *profile, const int profile_len,
755 const char *awaymsg_encoding, const char *awaymsg, const int awaymsg_len,
756 fu32_t caps)
757 {
758 aim_conn_t *conn;
759 aim_frame_t *fr;
760 aim_snacid_t snacid;
761 aim_tlvlist_t *tl = NULL;
762 char *encoding;
763 static const char defencoding[] = {"text/aolrtf; charset=\"%s\""};
764
765 if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC)))
766 return -EINVAL;
767
768 if ((profile && profile_encoding == NULL) || (awaymsg && awaymsg_len && awaymsg_encoding == NULL)) {
769 return -EINVAL;
770 }
771
772 /* Build the packet first to get real length */
773 if (profile) {
774 /* no + 1 here because of %s */
775 encoding = malloc(strlen(defencoding) + strlen(profile_encoding));
776 if (encoding == NULL) {
777 return -ENOMEM;
778 }
779 snprintf(encoding, strlen(defencoding) + strlen(profile_encoding), defencoding, profile_encoding);
780 aim_addtlvtochain_raw(&tl, 0x0001, strlen(encoding), encoding);
781 aim_addtlvtochain_raw(&tl, 0x0002, profile_len, profile);
782 free(encoding);
783 }
784
785 /*
786 * So here's how this works:
787 * - You are away when you have a non-zero-length type 4 TLV stored.
788 * - You become unaway when you clear the TLV with a zero-length
789 * type 4 TLV.
790 * - If you do not send the type 4 TLV, your status does not change
791 * (that is, if you were away, you'll remain away).
792 */
793 if (awaymsg) {
794 if (awaymsg_len) {
795 encoding = malloc(strlen(defencoding) + strlen(awaymsg_encoding));
796 if (encoding == NULL) {
797 return -ENOMEM;
798 }
799 snprintf(encoding, strlen(defencoding) + strlen(awaymsg_encoding), defencoding, awaymsg_encoding);
800 aim_addtlvtochain_raw(&tl, 0x0003, strlen(encoding), encoding);
801 aim_addtlvtochain_raw(&tl, 0x0004, awaymsg_len, awaymsg);
802 free(encoding);
803 } else
804 aim_addtlvtochain_noval(&tl, 0x0004);
805 }
806
807 aim_addtlvtochain_caps(&tl, 0x0005, caps);
808
809 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + aim_sizetlvchain(&tl))))
810 return -ENOMEM;
811
812 snacid = aim_cachesnac(sess, 0x0002, 0x0004, 0x0000, NULL, 0);
813 aim_putsnac(&fr->data, 0x0002, 0x004, 0x0000, snacid);
814
815 aim_writetlvchain(&fr->data, &tl);
816 aim_freetlvchain(&tl);
817
818 aim_tx_enqueue(sess, fr);
819
820 return 0;
821 }
822
823 /*
824 * Subtype 0x0005 - Request info of another AIM user.
825 *
826 * @param sn The screenname whose info you wish to request.
827 * @param infotype The type of info you wish to request.
828 * 0x0001 - Info/profile
829 * 0x0003 - Away message
830 * 0x0004 - Capabilities
831 */
832 faim_export int aim_locate_getinfo(aim_session_t *sess, const char *sn, fu16_t infotype)
833 {
834 aim_conn_t *conn;
835 aim_frame_t *fr;
836 aim_snacid_t snacid;
837
838 if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC)) || !sn)
839 return -EINVAL;
840
841 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 12+1+strlen(sn))))
842 return -ENOMEM;
843
844 snacid = aim_cachesnac(sess, 0x0002, 0x0005, 0x0000, NULL, 0);
845
846 aim_putsnac(&fr->data, 0x0002, 0x0005, 0x0000, snacid);
847 aimbs_put16(&fr->data, infotype);
848 aimbs_put8(&fr->data, strlen(sn));
849 aimbs_putraw(&fr->data, sn, strlen(sn));
850
851 aim_tx_enqueue(sess, fr);
852
853 return 0;
854 }
855
856 /* Subtype 0x0006 */
857 static int userinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
858 {
859 int ret = 0;
860 aim_rxcallback_t userfunc;
861 aim_userinfo_t *userinfo, *userinfo2;
862 aim_tlvlist_t *tlvlist;
863 aim_tlv_t *tlv = NULL;
864 int was_explicit;
865 struct node *cur, *del;
866
867 userinfo = (aim_userinfo_t *)malloc(sizeof(aim_userinfo_t));
868 aim_info_extract(sess, bs, userinfo);
869 tlvlist = aim_readtlvchain(bs);
870
871 /* Profile will be 1 and 2 */
872 userinfo->info_encoding = aim_gettlv_str(tlvlist, 0x0001, 1);
873 if ((tlv = aim_gettlv(tlvlist, 0x0002, 1))) {
874 userinfo->info = (char *)malloc(tlv->length);
875 memcpy(userinfo->info, tlv->value, tlv->length);
876 userinfo->info_len = tlv->length;
877 }
878
879 /* Away message will be 3 and 4 */
880 userinfo->away_encoding = aim_gettlv_str(tlvlist, 0x0003, 1);
881 if ((tlv = aim_gettlv(tlvlist, 0x0004, 1))) {
882 userinfo->away = (char *)malloc(tlv->length);
883 memcpy(userinfo->away, tlv->value, tlv->length);
884 userinfo->away_len = tlv->length;
885 }
886
887 /* Caps will be 5 */
888 if ((tlv = aim_gettlv(tlvlist, 0x0005, 1))) {
889 aim_bstream_t cbs;
890 aim_bstream_init(&cbs, tlv->value, tlv->length);
891 userinfo->capabilities = aim_getcap(sess, &cbs, tlv->length);
892 userinfo->present = AIM_USERINFO_PRESENT_CAPABILITIES;
893 }
894 aim_freetlvchain(&tlvlist);
895
896 aim_locate_adduserinfo(userinfo);
897 userinfo2 = aim_locate_finduserinfo(userinfo->sn);
898 aim_info_free(userinfo);
899 free(userinfo);
900
901 /*
902 * Remove this screen name from our queue. If the client requested
903 * this buddy's info explicitly, then notify them that we have info
904 * for this buddy.
905 */
906 was_explicit = TRUE;
907 while ((request_queue != NULL) && (aim_sncmp(userinfo2->sn, request_queue->sn) == 0)) {
908 del = request_queue;
909 request_queue = del->next;
910 was_explicit = FALSE;
911 free(del->sn);
912 free(del);
913 }
914 cur = request_queue;
915 while ((cur != NULL) && (cur->next != NULL)) {
916 if (aim_sncmp(userinfo2->sn, cur->next->sn) == 0) {
917 del = cur->next;
918 cur->next = del->next;
919 was_explicit = FALSE;
920 free(del->sn);
921 free(del);
922 } else
923 cur = cur->next;
924 }
925
926 if (was_explicit == TRUE) {
927 if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
928 ret = userfunc(sess, rx, userinfo2);
929 } else {
930 waiting_for_response = FALSE;
931 aim_locate_dorequest(sess);
932 }
933
934 return ret;
935 }
936
937 /*
938 * Subtype 0x0009 - Set directory profile data.
939 *
940 * This is not the same as aim_location_setprofile!
941 * privacy: 1 to allow searching, 0 to disallow.
942 *
943 */
944 faim_export int aim_locate_setdirinfo(aim_session_t *sess, const char *first, const char *middle, const char *last, const char *maiden, const char *nickname, const char *street, const char *city, const char *state, const char *zip, int country, fu16_t privacy)
945 {
946 aim_conn_t *conn;
947 aim_frame_t *fr;
948 aim_snacid_t snacid;
949 aim_tlvlist_t *tl = NULL;
950
951 if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC)))
952 return -EINVAL;
953
954 aim_addtlvtochain16(&tl, 0x000a, privacy);
955
956 if (first)
957 aim_addtlvtochain_raw(&tl, 0x0001, strlen(first), first);
958 if (last)
959 aim_addtlvtochain_raw(&tl, 0x0002, strlen(last), last);
960 if (middle)
961 aim_addtlvtochain_raw(&tl, 0x0003, strlen(middle), middle);
962 if (maiden)
963 aim_addtlvtochain_raw(&tl, 0x0004, strlen(maiden), maiden);
964
965 if (state)
966 aim_addtlvtochain_raw(&tl, 0x0007, strlen(state), state);
967 if (city)
968 aim_addtlvtochain_raw(&tl, 0x0008, strlen(city), city);
969
970 if (nickname)
971 aim_addtlvtochain_raw(&tl, 0x000c, strlen(nickname), nickname);
972 if (zip)
973 aim_addtlvtochain_raw(&tl, 0x000d, strlen(zip), zip);
974
975 if (street)
976 aim_addtlvtochain_raw(&tl, 0x0021, strlen(street), street);
977
978 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_sizetlvchain(&tl))))
979 return -ENOMEM;
980
981 snacid = aim_cachesnac(sess, 0x0002, 0x0009, 0x0000, NULL, 0);
982
983 aim_putsnac(&fr->data, 0x0002, 0x0009, 0x0000, snacid);
984 aim_writetlvchain(&fr->data, &tl);
985 aim_freetlvchain(&tl);
986
987 aim_tx_enqueue(sess, fr);
988
989 return 0;
990 }
991
992 /*
993 * Subtype 0x000b - Huh? What is this?
994 */
995 faim_export int aim_locate_000b(aim_session_t *sess, const char *sn)
996 {
997 aim_conn_t *conn;
998 aim_frame_t *fr;
999 aim_snacid_t snacid;
1000
1001 return -EINVAL;
1002
1003 if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC)) || !sn)
1004 return -EINVAL;
1005
1006 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn))))
1007 return -ENOMEM;
1008
1009 snacid = aim_cachesnac(sess, 0x0002, 0x000b, 0x0000, NULL, 0);
1010
1011 aim_putsnac(&fr->data, 0x0002, 0x000b, 0x0000, snacid);
1012 aimbs_put8(&fr->data, strlen(sn));
1013 aimbs_putraw(&fr->data, sn, strlen(sn));
1014
1015 aim_tx_enqueue(sess, fr);
1016
1017 return 0;
1018 }
1019
1020 /*
1021 * Subtype 0x000f
1022 *
1023 * XXX pass these in better
1024 *
1025 */
1026 faim_export int aim_locate_setinterests(aim_session_t *sess, const char *interest1, const char *interest2, const char *interest3, const char *interest4, const char *interest5, fu16_t privacy)
1027 {
1028 aim_conn_t *conn;
1029 aim_frame_t *fr;
1030 aim_snacid_t snacid;
1031 aim_tlvlist_t *tl = NULL;
1032
1033 if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC)))
1034 return -EINVAL;
1035
1036 /* ?? privacy ?? */
1037 aim_addtlvtochain16(&tl, 0x000a, privacy);
1038
1039 if (interest1)
1040 aim_addtlvtochain_raw(&tl, 0x0000b, strlen(interest1), interest1);
1041 if (interest2)
1042 aim_addtlvtochain_raw(&tl, 0x0000b, strlen(interest2), interest2);
1043 if (interest3)
1044 aim_addtlvtochain_raw(&tl, 0x0000b, strlen(interest3), interest3);
1045 if (interest4)
1046 aim_addtlvtochain_raw(&tl, 0x0000b, strlen(interest4), interest4);
1047 if (interest5)
1048 aim_addtlvtochain_raw(&tl, 0x0000b, strlen(interest5), interest5);
1049
1050 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_sizetlvchain(&tl))))
1051 return -ENOMEM;
1052
1053 snacid = aim_cachesnac(sess, 0x0002, 0x000f, 0x0000, NULL, 0);
1054
1055 aim_putsnac(&fr->data, 0x0002, 0x000f, 0x0000, 0);
1056 aim_writetlvchain(&fr->data, &tl);
1057 aim_freetlvchain(&tl);
1058
1059 aim_tx_enqueue(sess, fr);
1060
1061 return 0;
1062 }
1063
1064 /*
1065 * Subtype 0x0015 - Request the info a user using the short method. This is
1066 * what iChat uses. It normally is VERY leniently rate limited.
1067 *
1068 * @param sn The screen name whose info you wish to request.
1069 * @param flags The bitmask which specifies the type of info you wish to request.
1070 * 0x00000001 - Info/profile.
1071 * 0x00000002 - Away message.
1072 * 0x00000004 - Capabilities.
1073 * 0x00000008 - Certification.
1074 * @return Return 0 if no errors, otherwise return the error number.
1075 */
1076 faim_export int aim_locate_getinfoshort(aim_session_t *sess, const char *sn, fu32_t flags)
1077 {
1078 aim_conn_t *conn;
1079 aim_frame_t *fr;
1080 aim_snacid_t snacid;
1081
1082 if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC)) || !sn)
1083 return -EINVAL;
1084
1085 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4+1+strlen(sn))))
1086 return -ENOMEM;
1087
1088 snacid = aim_cachesnac(sess, 0x0002, 0x0015, 0x0000, NULL, 0);
1089
1090 aim_putsnac(&fr->data, 0x0002, 0x0015, 0x0000, 0);
1091 aimbs_put32(&fr->data, flags);
1092 aimbs_put8(&fr->data, strlen(sn));
1093 aimbs_putraw(&fr->data, sn, strlen(sn));
1094
1095 aim_tx_enqueue(sess, fr);
1096
1097 return 0;
1098 }
1099
1100 static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
1101 {
1102
1103 if (snac->subtype == 0x0003)
1104 return rights(sess, mod, rx, snac, bs);
1105 else if (snac->subtype == 0x0006)
1106 return userinfo(sess, mod, rx, snac, bs);
1107
1108 return 0;
1109 }
1110
1111 static void locate_shutdown(aim_session_t *sess, aim_module_t *mod)
1112 {
1113 aim_userinfo_t *del;
1114
1115 while (infos) {
1116 del = infos;
1117 infos = infos->next;
1118 free(del->sn);
1119 free(del->info);
1120 free(del->avail);
1121 free(del->away);
1122 free(del);
1123 }
1124 }
1125
1126 faim_internal int locate_modfirst(aim_session_t *sess, aim_module_t *mod)
1127 {
1128
1129 mod->family = AIM_CB_FAM_LOC;
1130 mod->version = 0x0001;
1131 mod->toolid = 0x0110;
1132 mod->toolversion = 0x0629;
1133 mod->flags = 0;
1134 strncpy(mod->name, "locate", sizeof(mod->name));
1135 mod->snachandler = snachandler;
1136 mod->shutdown = locate_shutdown;
1137
1138 return 0;
1139 }