Mercurial > pidgin
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 } |