Mercurial > pidgin
annotate src/protocols/oscar/locate.c @ 10563:3e2cd3fe8897
[gaim-migrate @ 11944]
Fix a problem Justin Wood noticed and supplied sf patch 920581 to fix
My fix is slightly difference.
The problem was that, if an iChat user had an available message up,
then they changed their buddy icon, Gaim would stop showing the
available message for the person.
committer: Tailor Script <tailor@pidgin.im>
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Tue, 01 Feb 2005 05:19:27 +0000 |
parents | 08c8a18fb557 |
children | ecc0f22db510 |
rev | line source |
---|---|
7011 | 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 /* | |
16 * Capability blocks. | |
17 * | |
18 * These are CLSIDs. They should actually be of the form: | |
19 * | |
20 * {0x0946134b, 0x4c7f, 0x11d1, | |
21 * {0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}}, | |
22 * | |
23 * But, eh. | |
24 */ | |
25 static const struct { | |
26 fu32_t flag; | |
27 fu8_t data[16]; | |
28 } aim_caps[] = { | |
29 | |
30 /* | |
31 * These are in ascending numerical order. | |
32 */ | |
7253 | 33 |
34 /* | |
35 * Perhaps better called AIM_CAPS_SHORTCAPS | |
36 */ | |
7011 | 37 {AIM_CAPS_ICHAT, |
10436 | 38 {0x09, 0x46, 0x00, 0x00, 0x4c, 0x7f, 0x11, 0xd1, |
7011 | 39 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, |
40 | |
41 {AIM_CAPS_SECUREIM, | |
10436 | 42 {0x09, 0x46, 0x00, 0x01, 0x4c, 0x7f, 0x11, 0xd1, |
7011 | 43 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, |
44 | |
7582 | 45 {AIM_CAPS_VIDEO, |
10436 | 46 {0x09, 0x46, 0x01, 0x00, 0x4c, 0x7f, 0x11, 0xd1, |
7582 | 47 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, |
48 | |
8092 | 49 /* "Live Video" support in Windows AIM 5.5.3501 and newer */ |
50 {AIM_CAPS_LIVEVIDEO, | |
10436 | 51 {0x09, 0x46, 0x01, 0x01, 0x4c, 0x7f, 0x11, 0xd1, |
8092 | 52 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, |
53 | |
54 /* "Camera" support in Windows AIM 5.5.3501 and newer */ | |
55 {AIM_CAPS_CAMERA, | |
10436 | 56 {0x09, 0x46, 0x01, 0x02, 0x4c, 0x7f, 0x11, 0xd1, |
8092 | 57 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, |
58 | |
59 /* In Windows AIM 5.5.3501 and newer */ | |
60 {AIM_CAPS_GENERICUNKNOWN, | |
10436 | 61 {0x09, 0x46, 0x01, 0x03, 0x4c, 0x7f, 0x11, 0xd1, |
8092 | 62 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, |
63 | |
64 /* In iChatAV (version numbers...?) */ | |
7945 | 65 {AIM_CAPS_ICHATAV, |
10436 | 66 {0x09, 0x46, 0x01, 0x05, 0x4c, 0x7f, 0x11, 0xd1, |
7945 | 67 0x82, 0x22, 0x44, 0x45, 0x45, 0x53, 0x54, 0x00}}, |
68 | |
7025 | 69 /* |
70 * Not really sure about this one. In an email from | |
71 * 26 Sep 2003, Matthew Sachs suggested that, "this | |
72 * is probably the capability for the SMS features." | |
73 */ | |
74 {AIM_CAPS_SMS, | |
10436 | 75 {0x09, 0x46, 0x01, 0xff, 0x4c, 0x7f, 0x11, 0xd1, |
7025 | 76 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, |
77 | |
7091 | 78 {AIM_CAPS_GENERICUNKNOWN, |
10436 | 79 {0x09, 0x46, 0xf0, 0x03, 0x4c, 0x7f, 0x11, 0xd1, |
7025 | 80 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, |
81 | |
7091 | 82 {AIM_CAPS_GENERICUNKNOWN, |
10436 | 83 {0x09, 0x46, 0xf0, 0x04, 0x4c, 0x7f, 0x11, 0xd1, |
7025 | 84 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, |
85 | |
7091 | 86 {AIM_CAPS_GENERICUNKNOWN, |
10436 | 87 {0x09, 0x46, 0xf0, 0x05, 0x4c, 0x7f, 0x11, 0xd1, |
7025 | 88 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, |
89 | |
7011 | 90 {AIM_CAPS_HIPTOP, |
10436 | 91 {0x09, 0x46, 0x13, 0x23, 0x4c, 0x7f, 0x11, 0xd1, |
7011 | 92 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, |
93 | |
8092 | 94 {AIM_CAPS_TALK, |
10436 | 95 {0x09, 0x46, 0x13, 0x41, 0x4c, 0x7f, 0x11, 0xd1, |
7011 | 96 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, |
97 | |
98 {AIM_CAPS_SENDFILE, | |
10436 | 99 {0x09, 0x46, 0x13, 0x43, 0x4c, 0x7f, 0x11, 0xd1, |
7011 | 100 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, |
101 | |
7141 | 102 {AIM_CAPS_ICQ_DIRECT, |
10436 | 103 {0x09, 0x46, 0x13, 0x44, 0x4c, 0x7f, 0x11, 0xd1, |
7011 | 104 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, |
105 | |
106 {AIM_CAPS_DIRECTIM, | |
10436 | 107 {0x09, 0x46, 0x13, 0x45, 0x4c, 0x7f, 0x11, 0xd1, |
7011 | 108 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, |
109 | |
110 {AIM_CAPS_BUDDYICON, | |
10436 | 111 {0x09, 0x46, 0x13, 0x46, 0x4c, 0x7f, 0x11, 0xd1, |
7011 | 112 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, |
113 | |
8092 | 114 {AIM_CAPS_ADDINS, |
7011 | 115 {0x09, 0x46, 0x13, 0x47, 0x4c, 0x7f, 0x11, 0xd1, |
116 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, | |
117 | |
118 {AIM_CAPS_GETFILE, | |
119 {0x09, 0x46, 0x13, 0x48, 0x4c, 0x7f, 0x11, 0xd1, | |
120 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, | |
121 | |
122 {AIM_CAPS_ICQSERVERRELAY, | |
123 {0x09, 0x46, 0x13, 0x49, 0x4c, 0x7f, 0x11, 0xd1, | |
124 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, | |
125 | |
126 /* | |
127 * Indeed, there are two of these. The former appears to be correct, | |
128 * but in some versions of winaim, the second one is set. Either they | |
129 * forgot to fix endianness, or they made a typo. It really doesn't | |
130 * matter which. | |
131 */ | |
132 {AIM_CAPS_GAMES, | |
133 {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1, | |
134 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, | |
135 {AIM_CAPS_GAMES2, | |
136 {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1, | |
137 0x22, 0x82, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, | |
138 | |
139 {AIM_CAPS_SENDBUDDYLIST, | |
140 {0x09, 0x46, 0x13, 0x4b, 0x4c, 0x7f, 0x11, 0xd1, | |
141 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, | |
142 | |
143 /* | |
144 * Setting this lets AIM users receive messages from ICQ users, and ICQ | |
145 * users receive messages from AIM users. It also lets ICQ users show | |
146 * up in buddy lists for AIM users, and AIM users show up in buddy lists | |
147 * for ICQ users. And ICQ privacy/invisibility acts like AIM privacy, | |
148 * in that if you add a user to your deny list, you will not be able to | |
149 * see them as online (previous you could still see them, but they | |
150 * couldn't see you. | |
151 */ | |
152 {AIM_CAPS_INTEROPERATE, | |
153 {0x09, 0x46, 0x13, 0x4d, 0x4c, 0x7f, 0x11, 0xd1, | |
154 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, | |
155 | |
156 {AIM_CAPS_ICQUTF8, | |
157 {0x09, 0x46, 0x13, 0x4e, 0x4c, 0x7f, 0x11, 0xd1, | |
158 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, | |
159 | |
160 {AIM_CAPS_ICQUTF8OLD, | |
161 {0x2e, 0x7a, 0x64, 0x75, 0xfa, 0xdf, 0x4d, 0xc8, | |
162 0x88, 0x6f, 0xea, 0x35, 0x95, 0xfd, 0xb6, 0xdf}}, | |
163 | |
164 /* | |
165 * Chat is oddball. | |
166 */ | |
167 {AIM_CAPS_CHAT, | |
10436 | 168 {0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, |
7011 | 169 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, |
170 | |
171 /* | |
172 {AIM_CAPS_ICQ2GO, | |
173 {0x56, 0x3f, 0xc8, 0x09, 0x0b, 0x6f, 0x41, 0xbd, | |
174 0x9f, 0x79, 0x42, 0x26, 0x09, 0xdf, 0xa2, 0xf3}}, | |
175 */ | |
176 | |
177 {AIM_CAPS_ICQRTF, | |
178 {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34, | |
179 0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x92}}, | |
180 | |
7091 | 181 /* This is added by the servers and it only shows up for ourselves... */ |
182 {AIM_CAPS_GENERICUNKNOWN, | |
10436 | 183 {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34, |
7091 | 184 0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x09}}, |
7011 | 185 |
186 {AIM_CAPS_APINFO, | |
187 {0xaa, 0x4a, 0x32, 0xb5, 0xf8, 0x84, 0x48, 0xc6, | |
188 0xa3, 0xd7, 0x8c, 0x50, 0x97, 0x19, 0xfd, 0x5b}}, | |
189 | |
190 {AIM_CAPS_TRILLIANCRYPT, | |
191 {0xf2, 0xe7, 0xc7, 0xf4, 0xfe, 0xad, 0x4d, 0xfb, | |
192 0xb2, 0x35, 0x36, 0x79, 0x8b, 0xdf, 0x00, 0x00}}, | |
193 | |
194 {AIM_CAPS_EMPTY, | |
195 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
196 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, | |
197 | |
198 {AIM_CAPS_LAST} | |
199 }; | |
200 | |
201 /* | |
202 * Add the userinfo to our linked list. If we already have userinfo | |
203 * for this buddy, then just overwrite parts of the old data. | |
10436 | 204 * |
7011 | 205 * @param userinfo Contains the new information for the buddy. |
206 */ | |
7045 | 207 static void aim_locate_adduserinfo(aim_session_t *sess, aim_userinfo_t *userinfo) { |
7011 | 208 aim_userinfo_t *cur; |
10436 | 209 aim_conn_t *conn; |
210 aim_rxcallback_t userfunc; | |
7011 | 211 |
7045 | 212 cur = aim_locate_finduserinfo(sess, userinfo->sn); |
7011 | 213 |
214 if (cur == NULL) { | |
215 cur = (aim_userinfo_t *)calloc(1, sizeof(aim_userinfo_t)); | |
216 cur->sn = strdup(userinfo->sn); | |
7045 | 217 cur->next = sess->locate.userinfo; |
218 sess->locate.userinfo = cur; | |
7011 | 219 } |
220 | |
221 cur->warnlevel = userinfo->warnlevel; | |
222 cur->idletime = userinfo->idletime; | |
223 if (userinfo->flags != 0) | |
224 cur->flags = userinfo->flags; | |
225 if (userinfo->createtime != 0) | |
226 cur->createtime = userinfo->createtime; | |
227 if (userinfo->membersince != 0) | |
228 cur->membersince = userinfo->membersince; | |
229 if (userinfo->onlinesince != 0) | |
230 cur->onlinesince = userinfo->onlinesince; | |
231 if (userinfo->sessionlen != 0) | |
232 cur->sessionlen = userinfo->sessionlen; | |
233 if (userinfo->capabilities != 0) | |
234 cur->capabilities = userinfo->capabilities; | |
235 cur->present |= userinfo->present; | |
236 | |
7046 | 237 if (userinfo->iconcsumlen > 0) { |
238 free(cur->iconcsum); | |
239 cur->iconcsum = (fu8_t *)malloc(userinfo->iconcsumlen); | |
240 memcpy(cur->iconcsum, userinfo->iconcsum, userinfo->iconcsumlen); | |
241 cur->iconcsumlen = userinfo->iconcsumlen; | |
7011 | 242 } |
243 | |
7301 | 244 if (userinfo->info != NULL) { |
7011 | 245 free(cur->info); |
246 free(cur->info_encoding); | |
8393 | 247 if (userinfo->info_len > 0) { |
248 cur->info = (char *)malloc(userinfo->info_len); | |
249 memcpy(cur->info, userinfo->info, userinfo->info_len); | |
8394 | 250 } else |
251 cur->info = NULL; | |
7235 | 252 cur->info_encoding = strdup(userinfo->info_encoding); |
7011 | 253 cur->info_len = userinfo->info_len; |
254 } | |
7046 | 255 |
7301 | 256 if (userinfo->away != NULL) { |
7046 | 257 free(cur->away); |
258 free(cur->away_encoding); | |
8393 | 259 if (userinfo->away_len > 0) { |
260 cur->away = (char *)malloc(userinfo->away_len); | |
261 memcpy(cur->away, userinfo->away, userinfo->away_len); | |
8394 | 262 } else |
263 cur->away = NULL; | |
7235 | 264 cur->away_encoding = strdup(userinfo->away_encoding); |
7046 | 265 cur->away_len = userinfo->away_len; |
266 } | |
10436 | 267 |
268 /* | |
269 * This callback can be used by a client if they want to know whenever | |
270 * info for a buddy is updated. For example, if a client shows away | |
271 * messages in its buddy list, then it would need to know if a user's | |
272 * away message changes. | |
273 */ | |
274 conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC); | |
275 if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_LOC, AIM_CB_LOC_GOTINFOBLOCK))) | |
276 userfunc(sess, NULL, cur); | |
7011 | 277 } |
278 | |
8341 | 279 faim_export void aim_locate_dorequest(aim_session_t *sess) { |
7466 | 280 struct userinfo_node *cur = sess->locate.torequest; |
7011 | 281 |
282 if (cur == NULL) | |
283 return; | |
284 | |
7045 | 285 if (sess->locate.waiting_for_response == TRUE) |
7011 | 286 return; |
287 | |
7045 | 288 sess->locate.waiting_for_response = TRUE; |
7166 | 289 aim_locate_getinfoshort(sess, cur->sn, 0x00000003); |
7466 | 290 |
291 /* Move this node to the "requested" queue */ | |
292 sess->locate.torequest = cur->next; | |
293 cur->next = sess->locate.requested; | |
294 sess->locate.requested = cur; | |
7011 | 295 } |
296 | |
7447 | 297 /** |
8735
92cbf9713795
[gaim-migrate @ 9490]
Christian Hammond <chipx86@chipx86.com>
parents:
8394
diff
changeset
|
298 * Remove this screen name from our queue. If this info was requested |
7447 | 299 * by our info request queue, then pop the next element off of the queue. |
7259 | 300 * |
301 * @param sess The aim session. | |
302 * @param sn Screen name of the info we just received. | |
303 * @return True if the request was explicit (client requested the info), | |
304 * false if the request was implicit (libfaim request the info). | |
305 */ | |
306 static int aim_locate_gotuserinfo(aim_session_t *sess, const char *sn) { | |
307 struct userinfo_node *cur, *del; | |
308 int was_explicit = TRUE; | |
309 | |
7466 | 310 while ((sess->locate.requested != NULL) && (aim_sncmp(sn, sess->locate.requested->sn) == 0)) { |
311 del = sess->locate.requested; | |
312 sess->locate.requested = del->next; | |
7259 | 313 was_explicit = FALSE; |
314 free(del->sn); | |
315 free(del); | |
316 } | |
317 | |
7466 | 318 cur = sess->locate.requested; |
7259 | 319 while ((cur != NULL) && (cur->next != NULL)) { |
320 if (aim_sncmp(sn, cur->next->sn) == 0) { | |
321 del = cur->next; | |
322 cur->next = del->next; | |
323 was_explicit = FALSE; | |
324 free(del->sn); | |
325 free(del); | |
326 } else | |
327 cur = cur->next; | |
328 } | |
329 | |
330 if (!was_explicit) { | |
8341 | 331 aim_conn_t *conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC); |
332 aim_rxcallback_t userfunc; | |
333 | |
7259 | 334 sess->locate.waiting_for_response = FALSE; |
8341 | 335 |
336 if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_LOC, AIM_CB_LOC_REQUESTINFOTIMEOUT))) | |
337 userfunc(sess, NULL); | |
8342 | 338 else |
339 aim_locate_dorequest(sess); | |
7259 | 340 } |
341 | |
342 return was_explicit; | |
343 } | |
344 | |
7011 | 345 faim_internal void aim_locate_requestuserinfo(aim_session_t *sess, const char *sn) { |
7045 | 346 struct userinfo_node *cur; |
7011 | 347 |
7266 | 348 /* Make sure we aren't already requesting info for this buddy */ |
7466 | 349 cur = sess->locate.torequest; |
7266 | 350 while (cur != NULL) { |
351 if (aim_sncmp(sn, cur->sn) == 0) | |
352 return; | |
353 cur = cur->next; | |
354 } | |
355 | |
356 /* Add a new node to our request queue */ | |
7045 | 357 cur = (struct userinfo_node *)malloc(sizeof(struct userinfo_node)); |
7011 | 358 cur->sn = strdup(sn); |
7466 | 359 cur->next = sess->locate.torequest; |
360 sess->locate.torequest = cur; | |
7011 | 361 |
7266 | 362 /* Actually request some info up in this piece */ |
7011 | 363 aim_locate_dorequest(sess); |
364 } | |
365 | |
7045 | 366 faim_export aim_userinfo_t *aim_locate_finduserinfo(aim_session_t *sess, const char *sn) { |
10116 | 367 aim_userinfo_t *cur = NULL; |
368 | |
369 if (sn == NULL) | |
370 return NULL; | |
371 | |
372 cur = sess->locate.userinfo; | |
7011 | 373 |
374 while (cur != NULL) { | |
375 if (aim_sncmp(cur->sn, sn) == 0) | |
376 return cur; | |
377 cur = cur->next; | |
378 } | |
379 | |
380 return NULL; | |
381 } | |
382 | |
7334 | 383 faim_internal fu32_t aim_locate_getcaps(aim_session_t *sess, aim_bstream_t *bs, int len) |
7011 | 384 { |
385 fu32_t flags = 0; | |
386 int offset; | |
387 | |
388 for (offset = 0; aim_bstream_empty(bs) && (offset < len); offset += 0x10) { | |
389 fu8_t *cap; | |
390 int i, identified; | |
391 | |
392 cap = aimbs_getraw(bs, 0x10); | |
393 | |
394 for (i = 0, identified = 0; !(aim_caps[i].flag & AIM_CAPS_LAST); i++) { | |
395 if (memcmp(&aim_caps[i].data, cap, 0x10) == 0) { | |
396 flags |= aim_caps[i].flag; | |
397 identified++; | |
398 break; /* should only match once... */ | |
399 } | |
400 } | |
401 | |
7334 | 402 if (!identified) |
7011 | 403 faimdprintf(sess, 0, "unknown capability: {%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n", |
404 cap[0], cap[1], cap[2], cap[3], | |
405 cap[4], cap[5], | |
406 cap[6], cap[7], | |
407 cap[8], cap[9], | |
408 cap[10], cap[11], cap[12], cap[13], | |
409 cap[14], cap[15]); | |
7334 | 410 |
411 free(cap); | |
412 } | |
413 | |
414 return flags; | |
415 } | |
416 | |
417 faim_internal fu32_t aim_locate_getcaps_short(aim_session_t *sess, aim_bstream_t *bs, int len) | |
418 { | |
419 fu32_t flags = 0; | |
420 int offset; | |
421 | |
422 for (offset = 0; aim_bstream_empty(bs) && (offset < len); offset += 0x02) { | |
423 fu8_t *cap; | |
424 int i, identified; | |
425 | |
426 cap = aimbs_getraw(bs, 0x02); | |
427 | |
428 for (i = 0, identified = 0; !(aim_caps[i].flag & AIM_CAPS_LAST); i++) { | |
429 if (memcmp(&aim_caps[i].data[2], cap, 0x02) == 0) { | |
430 flags |= aim_caps[i].flag; | |
431 identified++; | |
432 break; /* should only match once... */ | |
433 } | |
7011 | 434 } |
435 | |
7334 | 436 if (!identified) |
437 faimdprintf(sess, 0, "unknown short capability: {%02x%02x}\n", cap[0], cap[1]); | |
438 | |
7011 | 439 free(cap); |
440 } | |
441 | |
442 return flags; | |
443 } | |
444 | |
445 faim_internal int aim_putcap(aim_bstream_t *bs, fu32_t caps) | |
446 { | |
447 int i; | |
448 | |
449 if (!bs) | |
450 return -EINVAL; | |
451 | |
452 for (i = 0; aim_bstream_empty(bs); i++) { | |
453 | |
454 if (aim_caps[i].flag == AIM_CAPS_LAST) | |
455 break; | |
456 | |
457 if (caps & aim_caps[i].flag) | |
458 aimbs_putraw(bs, aim_caps[i].data, 0x10); | |
459 | |
460 } | |
461 | |
462 return 0; | |
463 } | |
464 | |
465 static void dumptlv(aim_session_t *sess, fu16_t type, aim_bstream_t *bs, fu8_t len) | |
466 { | |
467 int i; | |
468 | |
469 if (!sess || !bs || !len) | |
470 return; | |
471 | |
472 faimdprintf(sess, 0, "userinfo: type =0x%04x\n", type); | |
473 faimdprintf(sess, 0, "userinfo: length=0x%04x\n", len); | |
474 faimdprintf(sess, 0, "userinfo: value:\n"); | |
475 | |
476 for (i = 0; i < len; i++) { | |
477 if ((i % 8) == 0) | |
478 faimdprintf(sess, 0, "\nuserinfo: "); | |
479 faimdprintf(sess, 0, "0x%2x ", aimbs_get8(bs)); | |
480 } | |
481 | |
482 faimdprintf(sess, 0, "\n"); | |
483 | |
484 return; | |
485 } | |
486 | |
487 faim_internal void aim_info_free(aim_userinfo_t *info) | |
488 { | |
489 free(info->sn); | |
490 free(info->iconcsum); | |
491 free(info->info); | |
492 free(info->info_encoding); | |
493 free(info->avail); | |
494 free(info->avail_encoding); | |
495 free(info->away); | |
496 free(info->away_encoding); | |
497 } | |
498 | |
499 /* | |
500 * AIM is fairly regular about providing user info. This is a generic | |
501 * routine to extract it in its standard form. | |
502 */ | |
503 faim_internal int aim_info_extract(aim_session_t *sess, aim_bstream_t *bs, aim_userinfo_t *outinfo) | |
504 { | |
505 int curtlv, tlvcnt; | |
506 fu8_t snlen; | |
507 | |
508 if (!bs || !outinfo) | |
509 return -EINVAL; | |
510 | |
511 /* Clear out old data first */ | |
512 memset(outinfo, 0x00, sizeof(aim_userinfo_t)); | |
513 | |
514 /* | |
515 * Screen name. Stored as an unterminated string prepended with a | |
516 * byte containing its length. | |
517 */ | |
518 snlen = aimbs_get8(bs); | |
519 outinfo->sn = aimbs_getstr(bs, snlen); | |
520 | |
521 /* | |
522 * Warning Level. Stored as an unsigned short. | |
523 */ | |
524 outinfo->warnlevel = aimbs_get16(bs); | |
525 | |
526 /* | |
527 * TLV Count. Unsigned short representing the number of | |
528 * Type-Length-Value triples that follow. | |
529 */ | |
530 tlvcnt = aimbs_get16(bs); | |
531 | |
532 /* | |
533 * Parse out the Type-Length-Value triples as they're found. | |
534 */ | |
535 for (curtlv = 0; curtlv < tlvcnt; curtlv++) { | |
536 int endpos; | |
537 fu16_t type, length; | |
538 | |
539 type = aimbs_get16(bs); | |
540 length = aimbs_get16(bs); | |
541 | |
542 endpos = aim_bstream_curpos(bs) + length; | |
543 | |
544 if (type == 0x0001) { | |
545 /* | |
546 * Type = 0x0001: User flags | |
547 * | |
548 * Specified as any of the following ORed together: | |
549 * 0x0001 Trial (user less than 60days) | |
550 * 0x0002 Unknown bit 2 | |
551 * 0x0004 AOL Main Service user | |
552 * 0x0008 Unknown bit 4 | |
553 * 0x0010 Free (AIM) user | |
554 * 0x0020 Away | |
555 * 0x0400 ActiveBuddy | |
556 * | |
557 */ | |
558 outinfo->flags = aimbs_get16(bs); | |
559 outinfo->present |= AIM_USERINFO_PRESENT_FLAGS; | |
560 | |
561 } else if (type == 0x0002) { | |
562 /* | |
563 * Type = 0x0002: Account creation time. | |
564 * | |
565 * The time/date that the user originally registered for | |
566 * the service, stored in time_t format. | |
567 * | |
568 * I'm not sure how this differs from type 5 ("member | |
569 * since"). | |
570 * | |
571 * Note: This is the field formerly known as "member | |
572 * since". All these years and I finally found out | |
573 * that I got the name wrong. | |
574 */ | |
575 outinfo->createtime = aimbs_get32(bs); | |
576 outinfo->present |= AIM_USERINFO_PRESENT_CREATETIME; | |
577 | |
578 } else if (type == 0x0003) { | |
579 /* | |
580 * Type = 0x0003: On-Since date. | |
581 * | |
582 * The time/date that the user started their current | |
583 * session, stored in time_t format. | |
584 */ | |
585 outinfo->onlinesince = aimbs_get32(bs); | |
586 outinfo->present |= AIM_USERINFO_PRESENT_ONLINESINCE; | |
587 | |
588 } else if (type == 0x0004) { | |
589 /* | |
590 * Type = 0x0004: Idle time. | |
591 * | |
592 * Number of minutes since the user actively used the | |
593 * service. | |
594 * | |
595 * Note that the client tells the server when to start | |
596 * counting idle times, so this may or may not be | |
597 * related to reality. | |
598 */ | |
599 outinfo->idletime = aimbs_get16(bs); | |
600 outinfo->present |= AIM_USERINFO_PRESENT_IDLE; | |
601 | |
602 } else if (type == 0x0005) { | |
603 /* | |
604 * Type = 0x0005: Member since date. | |
605 * | |
606 * The time/date that the user originally registered for | |
607 * the service, stored in time_t format. | |
608 * | |
609 * This is sometimes sent instead of type 2 ("account | |
610 * creation time"), particularly in the self-info. | |
611 * And particularly for ICQ? | |
612 */ | |
613 outinfo->membersince = aimbs_get32(bs); | |
614 outinfo->present |= AIM_USERINFO_PRESENT_MEMBERSINCE; | |
615 | |
616 } else if (type == 0x0006) { | |
617 /* | |
618 * Type = 0x0006: ICQ Online Status | |
619 * | |
620 * ICQ's Away/DND/etc "enriched" status. Some decoding | |
621 * of values done by Scott <darkagl@pcnet.com> | |
622 */ | |
623 aimbs_get16(bs); | |
624 outinfo->icqinfo.status = aimbs_get16(bs); | |
625 outinfo->present |= AIM_USERINFO_PRESENT_ICQEXTSTATUS; | |
626 | |
7141 | 627 } else if (type == 0x0008) { |
628 /* | |
629 * Type = 0x0008 | |
630 * | |
631 * Client type, or some such. | |
632 */ | |
633 | |
7011 | 634 } else if (type == 0x000a) { |
635 /* | |
636 * Type = 0x000a | |
637 * | |
638 * ICQ User IP Address. | |
639 * Ahh, the joy of ICQ security. | |
640 */ | |
641 outinfo->icqinfo.ipaddr = aimbs_get32(bs); | |
642 outinfo->present |= AIM_USERINFO_PRESENT_ICQIPADDR; | |
643 | |
644 } else if (type == 0x000c) { | |
645 /* | |
646 * Type = 0x000c | |
647 * | |
648 * random crap containing the IP address, | |
649 * apparently a port number, and some Other Stuff. | |
650 * | |
651 * Format is: | |
652 * 4 bytes - Our IP address, 0xc0 a8 01 2b for 192.168.1.43 | |
653 * | |
654 * | |
655 */ | |
656 aimbs_getrawbuf(bs, outinfo->icqinfo.crap, 0x25); | |
657 outinfo->present |= AIM_USERINFO_PRESENT_ICQDATA; | |
658 | |
659 } else if (type == 0x000d) { | |
660 /* | |
661 * Type = 0x000d | |
662 * | |
7141 | 663 * OSCAR Capability information. |
7011 | 664 * |
665 */ | |
7334 | 666 outinfo->capabilities |= aim_locate_getcaps(sess, bs, length); |
7011 | 667 outinfo->present |= AIM_USERINFO_PRESENT_CAPABILITIES; |
668 | |
669 } else if (type == 0x000e) { | |
670 /* | |
671 * Type = 0x000e | |
672 * | |
7141 | 673 * AOL capability information. |
7011 | 674 * |
675 */ | |
676 | |
677 } else if ((type == 0x000f) || (type == 0x0010)) { | |
678 /* | |
679 * Type = 0x000f: Session Length. (AIM) | |
680 * Type = 0x0010: Session Length. (AOL) | |
681 * | |
682 * The duration, in seconds, of the user's current | |
683 * session. | |
684 * | |
685 * Which TLV type this comes in depends on the | |
686 * service the user is using (AIM or AOL). | |
687 * | |
688 */ | |
689 outinfo->sessionlen = aimbs_get32(bs); | |
690 outinfo->present |= AIM_USERINFO_PRESENT_SESSIONLEN; | |
691 | |
692 } else if (type == 0x0019) { | |
7141 | 693 /* |
694 * Type = 0x0019 | |
695 * | |
696 * OSCAR short capability information. A shortened | |
697 * form of the normal capabilities. | |
698 */ | |
7334 | 699 outinfo->capabilities |= aim_locate_getcaps_short(sess, bs, length); |
700 outinfo->present |= AIM_USERINFO_PRESENT_CAPABILITIES; | |
7011 | 701 |
702 } else if (type == 0x001b) { | |
7141 | 703 /* |
704 * Type = 0x001a | |
705 * | |
706 * AOL short capability information. A shortened | |
707 * form of the normal capabilities. | |
708 */ | |
709 | |
710 } else if (type == 0x001b) { | |
711 /* | |
712 * Type = 0x0019 | |
713 * | |
714 * Encryption certification MD5 checksum. | |
715 */ | |
7011 | 716 |
717 } else if (type == 0x001d) { | |
718 /* | |
719 * Type = 0x001d | |
720 * | |
721 * Buddy icon information and available messages. | |
722 * | |
723 * This almost seems like the AIM protocol guys gave | |
724 * the iChat guys a Type, and the iChat guys tried to | |
725 * cram as much cool shit into it as possible. Then | |
726 * the Windows AIM guys were like, "hey, that's | |
727 * pretty neat, let's copy those prawns." | |
728 * | |
729 * In that spirit, this can contain a custom message, | |
730 * kind of like an away message, but you're not away | |
731 * (it's called an "available" message). Or it can | |
732 * contain information about the buddy icon the user | |
733 * has stored on the server. | |
734 */ | |
735 int type2, number, length2; | |
736 | |
737 while (aim_bstream_curpos(bs) < endpos) { | |
738 type2 = aimbs_get16(bs); | |
739 number = aimbs_get8(bs); | |
740 length2 = aimbs_get8(bs); | |
741 | |
742 switch (type2) { | |
743 case 0x0000: { /* This is an official buddy icon? */ | |
744 /* This is always 5 bytes of "0x02 01 d2 04 72"? */ | |
745 aim_bstream_advance(bs, length2); | |
746 } break; | |
747 | |
748 case 0x0001: { /* A buddy icon checksum */ | |
749 if ((length2 > 0) && (number == 0x01)) { | |
750 free(outinfo->iconcsum); | |
751 outinfo->iconcsum = aimbs_getraw(bs, length2); | |
752 outinfo->iconcsumlen = length2; | |
753 } else | |
754 aim_bstream_advance(bs, length2); | |
755 } break; | |
756 | |
757 case 0x0002: { /* An available message */ | |
10563 | 758 if (length2 >= 4) { |
7011 | 759 free(outinfo->avail); |
760 outinfo->avail_len = aimbs_get16(bs); | |
761 outinfo->avail = aimbs_getstr(bs, outinfo->avail_len); | |
762 if (aimbs_get16(bs) == 0x0001) { /* We have an encoding */ | |
763 aimbs_get16(bs); | |
764 outinfo->avail_encoding = aimbs_getstr(bs, aimbs_get16(bs)); | |
765 } else { | |
766 /* No explicit encoding, client should use UTF-8 */ | |
767 outinfo->avail_encoding = NULL; | |
768 } | |
769 } else | |
770 aim_bstream_advance(bs, length2); | |
771 } break; | |
772 | |
773 default: { | |
774 aim_bstream_advance(bs, length2); | |
775 } break; | |
776 } | |
777 } | |
778 | |
779 } else if (type == 0x001e) { | |
780 /* | |
781 * Type 30: Unknown. | |
782 * | |
783 * Always four bytes, but it doesn't look like an int. | |
784 */ | |
7025 | 785 |
786 } else if (type == 0x001f) { | |
787 /* | |
788 * Type 31: Unknown. | |
789 * | |
790 * Seen on a buddy using DeadAIM. Data was 4 bytes: | |
791 * 0x00 00 00 10 | |
792 */ | |
793 | |
7011 | 794 } else { |
795 | |
796 /* | |
797 * Reaching here indicates that either AOL has | |
798 * added yet another TLV for us to deal with, | |
799 * or the parsing has gone Terribly Wrong. | |
800 * | |
801 * Either way, inform the owner and attempt | |
802 * recovery. | |
803 * | |
804 */ | |
805 faimdprintf(sess, 0, "userinfo: **warning: unexpected TLV:\n"); | |
806 faimdprintf(sess, 0, "userinfo: sn =%s\n", outinfo->sn); | |
807 dumptlv(sess, type, bs, length); | |
808 } | |
809 | |
810 /* Save ourselves. */ | |
811 aim_bstream_setpos(bs, endpos); | |
812 } | |
813 | |
7045 | 814 aim_locate_adduserinfo(sess, outinfo); |
7011 | 815 |
816 return 0; | |
817 } | |
818 | |
819 /* | |
820 * Inverse of aim_info_extract() | |
821 */ | |
822 faim_internal int aim_putuserinfo(aim_bstream_t *bs, aim_userinfo_t *info) | |
823 { | |
824 aim_tlvlist_t *tlvlist = NULL; | |
825 | |
826 if (!bs || !info) | |
827 return -EINVAL; | |
828 | |
829 aimbs_put8(bs, strlen(info->sn)); | |
830 aimbs_putraw(bs, info->sn, strlen(info->sn)); | |
831 | |
832 aimbs_put16(bs, info->warnlevel); | |
833 | |
834 if (info->present & AIM_USERINFO_PRESENT_FLAGS) | |
7167 | 835 aim_tlvlist_add_16(&tlvlist, 0x0001, info->flags); |
7011 | 836 if (info->present & AIM_USERINFO_PRESENT_MEMBERSINCE) |
7167 | 837 aim_tlvlist_add_32(&tlvlist, 0x0002, info->membersince); |
7011 | 838 if (info->present & AIM_USERINFO_PRESENT_ONLINESINCE) |
7167 | 839 aim_tlvlist_add_32(&tlvlist, 0x0003, info->onlinesince); |
7011 | 840 if (info->present & AIM_USERINFO_PRESENT_IDLE) |
7167 | 841 aim_tlvlist_add_16(&tlvlist, 0x0004, info->idletime); |
7011 | 842 |
843 /* XXX - So, ICQ_OSCAR_SUPPORT is never defined anywhere... */ | |
844 #if ICQ_OSCAR_SUPPORT | |
845 if (atoi(info->sn) != 0) { | |
846 if (info->present & AIM_USERINFO_PRESENT_ICQEXTSTATUS) | |
7167 | 847 aim_tlvlist_add_16(&tlvlist, 0x0006, info->icqinfo.status); |
7011 | 848 if (info->present & AIM_USERINFO_PRESENT_ICQIPADDR) |
7167 | 849 aim_tlvlist_add_32(&tlvlist, 0x000a, info->icqinfo.ipaddr); |
7011 | 850 } |
851 #endif | |
852 | |
853 if (info->present & AIM_USERINFO_PRESENT_CAPABILITIES) | |
7167 | 854 aim_tlvlist_add_caps(&tlvlist, 0x000d, info->capabilities); |
10436 | 855 |
7011 | 856 if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN) |
7167 | 857 aim_tlvlist_add_32(&tlvlist, (fu16_t)((info->flags & AIM_FLAG_AOL) ? 0x0010 : 0x000f), info->sessionlen); |
7011 | 858 |
7167 | 859 aimbs_put16(bs, aim_tlvlist_count(&tlvlist)); |
860 aim_tlvlist_write(bs, &tlvlist); | |
861 aim_tlvlist_free(&tlvlist); | |
7011 | 862 |
863 return 0; | |
864 } | |
865 | |
866 /* | |
7259 | 867 * Subtype 0x0001 |
868 */ | |
869 static int error(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) | |
870 { | |
871 int ret = 0; | |
872 aim_rxcallback_t userfunc; | |
873 aim_snac_t *snac2; | |
874 fu16_t reason; | |
875 char *sn; | |
876 int was_explicit; | |
877 | |
878 if (!(snac2 = aim_remsnac(sess, snac->id))) { | |
879 faimdprintf(sess, 0, "faim: locate.c, error(): received response from unknown request!\n"); | |
880 return 0; | |
881 } | |
882 | |
883 if ((snac2->family != 0x0002) && (snac2->type != 0x0015)) { | |
884 faimdprintf(sess, 0, "faim: locate.c, error(): received response from invalid request! %d\n", snac2->family); | |
885 return 0; | |
886 } | |
887 | |
888 if (!(sn = snac2->data)) { | |
889 faimdprintf(sess, 0, "faim: locate.c, error(): received response from request without a screen name!\n"); | |
890 return 0; | |
891 } | |
892 | |
893 reason = aimbs_get16(bs); | |
894 | |
895 /* | |
896 * Remove this screen name from our queue. If the client requested | |
897 * this buddy's info explicitly, then notify them that we do not have | |
898 * info for this buddy. | |
899 */ | |
900 was_explicit = aim_locate_gotuserinfo(sess, sn); | |
901 if (was_explicit == TRUE) | |
902 if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) | |
903 ret = userfunc(sess, rx, reason, sn); | |
904 | |
905 if (snac2) | |
906 free(snac2->data); | |
907 free(snac2); | |
908 | |
909 return ret; | |
910 } | |
911 | |
912 /* | |
7011 | 913 * Subtype 0x0002 |
914 * | |
915 * Request Location services rights. | |
916 * | |
917 */ | |
918 faim_export int aim_locate_reqrights(aim_session_t *sess) | |
919 { | |
920 aim_conn_t *conn; | |
921 | |
922 if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC))) | |
923 return -EINVAL; | |
924 | |
7282 | 925 return aim_genericreq_n_snacid(sess, conn, AIM_CB_FAM_LOC, AIM_CB_LOC_REQRIGHTS); |
7011 | 926 } |
927 | |
928 /* | |
929 * Subtype 0x0003 | |
930 * | |
931 * Normally contains: | |
932 * t(0001) - short containing max profile length (value = 1024) | |
933 * t(0002) - short - unknown (value = 16) [max MIME type length?] | |
934 * t(0003) - short - unknown (value = 10) | |
935 * t(0004) - short - unknown (value = 2048) [ICQ only?] | |
936 */ | |
937 static int rights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) | |
938 { | |
939 aim_tlvlist_t *tlvlist; | |
940 aim_rxcallback_t userfunc; | |
941 int ret = 0; | |
942 fu16_t maxsiglen = 0; | |
943 | |
7167 | 944 tlvlist = aim_tlvlist_read(bs); |
7011 | 945 |
7167 | 946 if (aim_tlv_gettlv(tlvlist, 0x0001, 1)) |
947 maxsiglen = aim_tlv_get16(tlvlist, 0x0001, 1); | |
7011 | 948 |
949 if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) | |
950 ret = userfunc(sess, rx, maxsiglen); | |
951 | |
7167 | 952 aim_tlvlist_free(&tlvlist); |
7011 | 953 |
954 return ret; | |
955 } | |
956 | |
957 /* | |
958 * Subtype 0x0004 | |
959 * | |
960 * Gives BOS your profile. | |
961 * | |
962 * profile_encoding and awaymsg_encoding MUST be set if profile or | |
963 * away are set, respectively, and their value may or may not be | |
964 * restricted to a few choices. I am currently aware of: | |
965 * | |
966 * us-ascii Just that | |
967 * unicode-2-0 UCS2-BE | |
968 * | |
969 * profile_len and awaymsg_len MUST be set similarly, and they MUST | |
970 * be the length of their respective strings in bytes. | |
971 * | |
972 * To get the previous behavior of awaymsg == "" un-setting the away | |
973 * message, set awaymsg non-NULL and awaymsg_len to 0 (this is the | |
974 * obvious equivalent). | |
975 * | |
976 */ | |
977 faim_export int aim_locate_setprofile(aim_session_t *sess, | |
978 const char *profile_encoding, const char *profile, const int profile_len, | |
7334 | 979 const char *awaymsg_encoding, const char *awaymsg, const int awaymsg_len) |
7011 | 980 { |
981 aim_conn_t *conn; | |
982 aim_frame_t *fr; | |
983 aim_snacid_t snacid; | |
984 aim_tlvlist_t *tl = NULL; | |
985 char *encoding; | |
986 static const char defencoding[] = {"text/aolrtf; charset=\"%s\""}; | |
987 | |
988 if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC))) | |
989 return -EINVAL; | |
990 | |
7510 | 991 if (!profile && !awaymsg) |
992 return -EINVAL; | |
993 | |
7011 | 994 if ((profile && profile_encoding == NULL) || (awaymsg && awaymsg_len && awaymsg_encoding == NULL)) { |
995 return -EINVAL; | |
996 } | |
997 | |
998 /* Build the packet first to get real length */ | |
999 if (profile) { | |
1000 /* no + 1 here because of %s */ | |
1001 encoding = malloc(strlen(defencoding) + strlen(profile_encoding)); | |
1002 if (encoding == NULL) { | |
1003 return -ENOMEM; | |
1004 } | |
1005 snprintf(encoding, strlen(defencoding) + strlen(profile_encoding), defencoding, profile_encoding); | |
7167 | 1006 aim_tlvlist_add_raw(&tl, 0x0001, strlen(encoding), encoding); |
1007 aim_tlvlist_add_raw(&tl, 0x0002, profile_len, profile); | |
7011 | 1008 free(encoding); |
1009 } | |
1010 | |
1011 /* | |
1012 * So here's how this works: | |
1013 * - You are away when you have a non-zero-length type 4 TLV stored. | |
1014 * - You become unaway when you clear the TLV with a zero-length | |
1015 * type 4 TLV. | |
1016 * - If you do not send the type 4 TLV, your status does not change | |
1017 * (that is, if you were away, you'll remain away). | |
1018 */ | |
1019 if (awaymsg) { | |
1020 if (awaymsg_len) { | |
1021 encoding = malloc(strlen(defencoding) + strlen(awaymsg_encoding)); | |
1022 if (encoding == NULL) { | |
1023 return -ENOMEM; | |
1024 } | |
1025 snprintf(encoding, strlen(defencoding) + strlen(awaymsg_encoding), defencoding, awaymsg_encoding); | |
7167 | 1026 aim_tlvlist_add_raw(&tl, 0x0003, strlen(encoding), encoding); |
1027 aim_tlvlist_add_raw(&tl, 0x0004, awaymsg_len, awaymsg); | |
7011 | 1028 free(encoding); |
1029 } else | |
7167 | 1030 aim_tlvlist_add_noval(&tl, 0x0004); |
7011 | 1031 } |
1032 | |
7334 | 1033 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + aim_tlvlist_size(&tl)))) |
1034 return -ENOMEM; | |
1035 | |
1036 snacid = aim_cachesnac(sess, 0x0002, 0x0004, 0x0000, NULL, 0); | |
1037 aim_putsnac(&fr->data, 0x0002, 0x004, 0x0000, snacid); | |
1038 | |
1039 aim_tlvlist_write(&fr->data, &tl); | |
1040 aim_tlvlist_free(&tl); | |
1041 | |
1042 aim_tx_enqueue(sess, fr); | |
1043 | |
1044 return 0; | |
1045 } | |
1046 | |
1047 /* | |
1048 * Subtype 0x0004 - Set your client's capabilities. | |
1049 */ | |
1050 faim_export int aim_locate_setcaps(aim_session_t *sess, fu32_t caps) | |
1051 { | |
1052 aim_conn_t *conn; | |
1053 aim_frame_t *fr; | |
1054 aim_snacid_t snacid; | |
1055 aim_tlvlist_t *tl = NULL; | |
1056 | |
1057 if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC))) | |
1058 return -EINVAL; | |
1059 | |
1060 aim_tlvlist_add_caps(&tl, 0x0005, caps); | |
7011 | 1061 |
7167 | 1062 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + aim_tlvlist_size(&tl)))) |
7011 | 1063 return -ENOMEM; |
1064 | |
1065 snacid = aim_cachesnac(sess, 0x0002, 0x0004, 0x0000, NULL, 0); | |
1066 aim_putsnac(&fr->data, 0x0002, 0x004, 0x0000, snacid); | |
1067 | |
7167 | 1068 aim_tlvlist_write(&fr->data, &tl); |
1069 aim_tlvlist_free(&tl); | |
7011 | 1070 |
1071 aim_tx_enqueue(sess, fr); | |
1072 | |
1073 return 0; | |
1074 } | |
1075 | |
1076 /* | |
1077 * Subtype 0x0005 - Request info of another AIM user. | |
1078 * | |
1079 * @param sn The screenname whose info you wish to request. | |
1080 * @param infotype The type of info you wish to request. | |
1081 * 0x0001 - Info/profile | |
1082 * 0x0003 - Away message | |
1083 * 0x0004 - Capabilities | |
1084 */ | |
1085 faim_export int aim_locate_getinfo(aim_session_t *sess, const char *sn, fu16_t infotype) | |
1086 { | |
1087 aim_conn_t *conn; | |
1088 aim_frame_t *fr; | |
1089 aim_snacid_t snacid; | |
1090 | |
1091 if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC)) || !sn) | |
1092 return -EINVAL; | |
1093 | |
1094 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 12+1+strlen(sn)))) | |
1095 return -ENOMEM; | |
1096 | |
1097 snacid = aim_cachesnac(sess, 0x0002, 0x0005, 0x0000, NULL, 0); | |
10436 | 1098 |
7011 | 1099 aim_putsnac(&fr->data, 0x0002, 0x0005, 0x0000, snacid); |
1100 aimbs_put16(&fr->data, infotype); | |
1101 aimbs_put8(&fr->data, strlen(sn)); | |
1102 aimbs_putraw(&fr->data, sn, strlen(sn)); | |
1103 | |
1104 aim_tx_enqueue(sess, fr); | |
1105 | |
1106 return 0; | |
1107 } | |
1108 | |
1109 /* Subtype 0x0006 */ | |
1110 static int userinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) | |
1111 { | |
1112 int ret = 0; | |
1113 aim_rxcallback_t userfunc; | |
1114 aim_userinfo_t *userinfo, *userinfo2; | |
1115 aim_tlvlist_t *tlvlist; | |
1116 aim_tlv_t *tlv = NULL; | |
1117 int was_explicit; | |
1118 | |
1119 userinfo = (aim_userinfo_t *)malloc(sizeof(aim_userinfo_t)); | |
1120 aim_info_extract(sess, bs, userinfo); | |
7167 | 1121 tlvlist = aim_tlvlist_read(bs); |
7011 | 1122 |
1123 /* Profile will be 1 and 2 */ | |
7167 | 1124 userinfo->info_encoding = aim_tlv_getstr(tlvlist, 0x0001, 1); |
1125 if ((tlv = aim_tlv_gettlv(tlvlist, 0x0002, 1))) { | |
7011 | 1126 userinfo->info = (char *)malloc(tlv->length); |
1127 memcpy(userinfo->info, tlv->value, tlv->length); | |
1128 userinfo->info_len = tlv->length; | |
1129 } | |
1130 | |
1131 /* Away message will be 3 and 4 */ | |
7167 | 1132 userinfo->away_encoding = aim_tlv_getstr(tlvlist, 0x0003, 1); |
1133 if ((tlv = aim_tlv_gettlv(tlvlist, 0x0004, 1))) { | |
7011 | 1134 userinfo->away = (char *)malloc(tlv->length); |
1135 memcpy(userinfo->away, tlv->value, tlv->length); | |
1136 userinfo->away_len = tlv->length; | |
1137 } | |
1138 | |
1139 /* Caps will be 5 */ | |
7167 | 1140 if ((tlv = aim_tlv_gettlv(tlvlist, 0x0005, 1))) { |
7011 | 1141 aim_bstream_t cbs; |
1142 aim_bstream_init(&cbs, tlv->value, tlv->length); | |
7334 | 1143 userinfo->capabilities = aim_locate_getcaps(sess, &cbs, tlv->length); |
7011 | 1144 userinfo->present = AIM_USERINFO_PRESENT_CAPABILITIES; |
1145 } | |
7167 | 1146 aim_tlvlist_free(&tlvlist); |
7011 | 1147 |
7045 | 1148 aim_locate_adduserinfo(sess, userinfo); |
1149 userinfo2 = aim_locate_finduserinfo(sess, userinfo->sn); | |
7011 | 1150 aim_info_free(userinfo); |
1151 free(userinfo); | |
1152 | |
1153 /* | |
1154 * Remove this screen name from our queue. If the client requested | |
1155 * this buddy's info explicitly, then notify them that we have info | |
1156 * for this buddy. | |
1157 */ | |
7259 | 1158 was_explicit = aim_locate_gotuserinfo(sess, userinfo2->sn); |
1159 if (was_explicit == TRUE) | |
7011 | 1160 if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) |
1161 ret = userfunc(sess, rx, userinfo2); | |
1162 | |
1163 return ret; | |
1164 } | |
1165 | |
1166 /* | |
1167 * Subtype 0x0009 - Set directory profile data. | |
1168 * | |
1169 * This is not the same as aim_location_setprofile! | |
1170 * privacy: 1 to allow searching, 0 to disallow. | |
1171 * | |
1172 */ | |
1173 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) | |
1174 { | |
1175 aim_conn_t *conn; | |
1176 aim_frame_t *fr; | |
1177 aim_snacid_t snacid; | |
1178 aim_tlvlist_t *tl = NULL; | |
1179 | |
1180 if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC))) | |
1181 return -EINVAL; | |
1182 | |
7167 | 1183 aim_tlvlist_add_16(&tl, 0x000a, privacy); |
7011 | 1184 |
1185 if (first) | |
7167 | 1186 aim_tlvlist_add_raw(&tl, 0x0001, strlen(first), first); |
7011 | 1187 if (last) |
7167 | 1188 aim_tlvlist_add_raw(&tl, 0x0002, strlen(last), last); |
7011 | 1189 if (middle) |
7167 | 1190 aim_tlvlist_add_raw(&tl, 0x0003, strlen(middle), middle); |
7011 | 1191 if (maiden) |
7167 | 1192 aim_tlvlist_add_raw(&tl, 0x0004, strlen(maiden), maiden); |
7011 | 1193 |
1194 if (state) | |
7167 | 1195 aim_tlvlist_add_raw(&tl, 0x0007, strlen(state), state); |
7011 | 1196 if (city) |
7167 | 1197 aim_tlvlist_add_raw(&tl, 0x0008, strlen(city), city); |
7011 | 1198 |
1199 if (nickname) | |
7167 | 1200 aim_tlvlist_add_raw(&tl, 0x000c, strlen(nickname), nickname); |
7011 | 1201 if (zip) |
7167 | 1202 aim_tlvlist_add_raw(&tl, 0x000d, strlen(zip), zip); |
7011 | 1203 |
1204 if (street) | |
7167 | 1205 aim_tlvlist_add_raw(&tl, 0x0021, strlen(street), street); |
7011 | 1206 |
7167 | 1207 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_tlvlist_size(&tl)))) |
7011 | 1208 return -ENOMEM; |
1209 | |
1210 snacid = aim_cachesnac(sess, 0x0002, 0x0009, 0x0000, NULL, 0); | |
1211 | |
1212 aim_putsnac(&fr->data, 0x0002, 0x0009, 0x0000, snacid); | |
7167 | 1213 aim_tlvlist_write(&fr->data, &tl); |
1214 aim_tlvlist_free(&tl); | |
7011 | 1215 |
1216 aim_tx_enqueue(sess, fr); | |
1217 | |
1218 return 0; | |
1219 } | |
1220 | |
1221 /* | |
1222 * Subtype 0x000b - Huh? What is this? | |
1223 */ | |
1224 faim_export int aim_locate_000b(aim_session_t *sess, const char *sn) | |
1225 { | |
1226 aim_conn_t *conn; | |
1227 aim_frame_t *fr; | |
1228 aim_snacid_t snacid; | |
1229 | |
1230 return -EINVAL; | |
1231 | |
1232 if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC)) || !sn) | |
1233 return -EINVAL; | |
1234 | |
1235 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)))) | |
1236 return -ENOMEM; | |
1237 | |
1238 snacid = aim_cachesnac(sess, 0x0002, 0x000b, 0x0000, NULL, 0); | |
1239 | |
1240 aim_putsnac(&fr->data, 0x0002, 0x000b, 0x0000, snacid); | |
1241 aimbs_put8(&fr->data, strlen(sn)); | |
1242 aimbs_putraw(&fr->data, sn, strlen(sn)); | |
1243 | |
1244 aim_tx_enqueue(sess, fr); | |
1245 | |
1246 return 0; | |
1247 } | |
1248 | |
1249 /* | |
1250 * Subtype 0x000f | |
1251 * | |
1252 * XXX pass these in better | |
1253 * | |
1254 */ | |
1255 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) | |
1256 { | |
1257 aim_conn_t *conn; | |
1258 aim_frame_t *fr; | |
1259 aim_snacid_t snacid; | |
1260 aim_tlvlist_t *tl = NULL; | |
1261 | |
1262 if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC))) | |
1263 return -EINVAL; | |
1264 | |
1265 /* ?? privacy ?? */ | |
7167 | 1266 aim_tlvlist_add_16(&tl, 0x000a, privacy); |
7011 | 1267 |
1268 if (interest1) | |
7167 | 1269 aim_tlvlist_add_raw(&tl, 0x0000b, strlen(interest1), interest1); |
7011 | 1270 if (interest2) |
7167 | 1271 aim_tlvlist_add_raw(&tl, 0x0000b, strlen(interest2), interest2); |
7011 | 1272 if (interest3) |
7167 | 1273 aim_tlvlist_add_raw(&tl, 0x0000b, strlen(interest3), interest3); |
7011 | 1274 if (interest4) |
7167 | 1275 aim_tlvlist_add_raw(&tl, 0x0000b, strlen(interest4), interest4); |
7011 | 1276 if (interest5) |
7167 | 1277 aim_tlvlist_add_raw(&tl, 0x0000b, strlen(interest5), interest5); |
7011 | 1278 |
7167 | 1279 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_tlvlist_size(&tl)))) |
7011 | 1280 return -ENOMEM; |
1281 | |
1282 snacid = aim_cachesnac(sess, 0x0002, 0x000f, 0x0000, NULL, 0); | |
1283 | |
1284 aim_putsnac(&fr->data, 0x0002, 0x000f, 0x0000, 0); | |
7167 | 1285 aim_tlvlist_write(&fr->data, &tl); |
1286 aim_tlvlist_free(&tl); | |
7011 | 1287 |
1288 aim_tx_enqueue(sess, fr); | |
1289 | |
1290 return 0; | |
1291 } | |
1292 | |
1293 /* | |
1294 * Subtype 0x0015 - Request the info a user using the short method. This is | |
1295 * what iChat uses. It normally is VERY leniently rate limited. | |
1296 * | |
1297 * @param sn The screen name whose info you wish to request. | |
1298 * @param flags The bitmask which specifies the type of info you wish to request. | |
1299 * 0x00000001 - Info/profile. | |
1300 * 0x00000002 - Away message. | |
1301 * 0x00000004 - Capabilities. | |
1302 * 0x00000008 - Certification. | |
1303 * @return Return 0 if no errors, otherwise return the error number. | |
1304 */ | |
1305 faim_export int aim_locate_getinfoshort(aim_session_t *sess, const char *sn, fu32_t flags) | |
1306 { | |
1307 aim_conn_t *conn; | |
1308 aim_frame_t *fr; | |
1309 aim_snacid_t snacid; | |
1310 | |
1311 if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC)) || !sn) | |
1312 return -EINVAL; | |
1313 | |
1314 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4+1+strlen(sn)))) | |
1315 return -ENOMEM; | |
1316 | |
7259 | 1317 snacid = aim_cachesnac(sess, 0x0002, 0x0015, 0x0000, sn, strlen(sn)+1); |
7011 | 1318 |
7259 | 1319 aim_putsnac(&fr->data, 0x0002, 0x0015, 0x0000, snacid); |
7011 | 1320 aimbs_put32(&fr->data, flags); |
1321 aimbs_put8(&fr->data, strlen(sn)); | |
1322 aimbs_putraw(&fr->data, sn, strlen(sn)); | |
1323 | |
1324 aim_tx_enqueue(sess, fr); | |
1325 | |
1326 return 0; | |
1327 } | |
1328 | |
1329 static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) | |
1330 { | |
1331 | |
7259 | 1332 if (snac->subtype == 0x0001) |
1333 return error(sess, mod, rx, snac, bs); | |
1334 else if (snac->subtype == 0x0003) | |
7011 | 1335 return rights(sess, mod, rx, snac, bs); |
1336 else if (snac->subtype == 0x0006) | |
1337 return userinfo(sess, mod, rx, snac, bs); | |
1338 | |
1339 return 0; | |
1340 } | |
1341 | |
1342 static void locate_shutdown(aim_session_t *sess, aim_module_t *mod) | |
1343 { | |
1344 aim_userinfo_t *del; | |
1345 | |
7045 | 1346 while (sess->locate.userinfo) { |
1347 del = sess->locate.userinfo; | |
1348 sess->locate.userinfo = sess->locate.userinfo->next; | |
7046 | 1349 aim_info_free(del); |
7011 | 1350 free(del); |
1351 } | |
1352 } | |
1353 | |
1354 faim_internal int locate_modfirst(aim_session_t *sess, aim_module_t *mod) | |
1355 { | |
1356 | |
1357 mod->family = AIM_CB_FAM_LOC; | |
1358 mod->version = 0x0001; | |
1359 mod->toolid = 0x0110; | |
1360 mod->toolversion = 0x0629; | |
1361 mod->flags = 0; | |
1362 strncpy(mod->name, "locate", sizeof(mod->name)); | |
1363 mod->snachandler = snachandler; | |
1364 mod->shutdown = locate_shutdown; | |
1365 | |
1366 return 0; | |
1367 } |