]> andersk Git - libfaim.git/blame - src/info.c
- Wed Sep 19 18:50:34 PDT 2001
[libfaim.git] / src / info.c
CommitLineData
9de3ca7e 1/*
2 * aim_info.c
3 *
4 * The functions here are responsible for requesting and parsing information-
d410cf58 5 * gathering SNACs. Or something like that.
9de3ca7e 6 *
7 */
8
37ee990e 9#define FAIM_INTERNAL
dd60ff8b 10#include <aim.h>
9de3ca7e 11
262272cc 12struct aim_priv_inforeq {
d410cf58 13 char sn[MAXSNLEN+1];
14 fu16_t infotype;
262272cc 15};
16
d410cf58 17faim_export int aim_getinfo(aim_session_t *sess, aim_conn_t *conn, const char *sn, fu16_t infotype)
9de3ca7e 18{
d410cf58 19 struct aim_priv_inforeq privdata;
20 aim_frame_t *fr;
21 aim_snacid_t snacid;
276495a3 22
d410cf58 23 if (!sess || !conn || !sn)
24 return -EINVAL;
00ef5271 25
d410cf58 26 if ((infotype != AIM_GETINFO_GENERALINFO) && (infotype != AIM_GETINFO_AWAYMESSAGE))
27 return -EINVAL;
9de3ca7e 28
d410cf58 29 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 12+1+strlen(sn))))
30 return -ENOMEM;
9de3ca7e 31
d410cf58 32 strncpy(privdata.sn, sn, sizeof(privdata.sn));
33 privdata.infotype = infotype;
34 snacid = aim_cachesnac(sess, 0x0002, 0x0005, 0x0000, &privdata, sizeof(struct aim_priv_inforeq));
35
36 aim_putsnac(&fr->data, 0x0002, 0x0005, 0x0000, snacid);
37 aimbs_put16(&fr->data, infotype);
38 aimbs_put8(&fr->data, strlen(sn));
39 aimbs_putraw(&fr->data, sn, strlen(sn));
9de3ca7e 40
d410cf58 41 aim_tx_enqueue(sess, fr);
9de3ca7e 42
d410cf58 43 return 0;
96f8b1ed 44}
5b79dc93 45
46/*
47 * Capability blocks.
48 */
f7bb124a 49static const struct {
d410cf58 50 unsigned short flag;
51 unsigned char data[16];
f7bb124a 52} aim_caps[] = {
d410cf58 53
54 {AIM_CAPS_BUDDYICON,
55 {0x09, 0x46, 0x13, 0x46, 0x4c, 0x7f, 0x11, 0xd1,
56 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
57
58 {AIM_CAPS_VOICE,
59 {0x09, 0x46, 0x13, 0x41, 0x4c, 0x7f, 0x11, 0xd1,
60 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
61
62 {AIM_CAPS_IMIMAGE,
63 {0x09, 0x46, 0x13, 0x45, 0x4c, 0x7f, 0x11, 0xd1,
64 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
65
66 {AIM_CAPS_CHAT,
67 {0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1,
68 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
69
70 {AIM_CAPS_GETFILE,
71 {0x09, 0x46, 0x13, 0x48, 0x4c, 0x7f, 0x11, 0xd1,
72 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
73
74 {AIM_CAPS_SENDFILE,
75 {0x09, 0x46, 0x13, 0x43, 0x4c, 0x7f, 0x11, 0xd1,
76 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
77
78 {AIM_CAPS_SAVESTOCKS,
79 {0x09, 0x46, 0x13, 0x47, 0x4c, 0x7f, 0x11, 0xd1,
80 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
81
82 /*
83 * Indeed, there are two of these. The former appears to be correct,
84 * but in some versions of winaim, the second one is set. Either they
85 * forgot to fix endianness, or they made a typo. It really doesn't
86 * matter which.
87 */
88 {AIM_CAPS_GAMES,
89 {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1,
90 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
91 {AIM_CAPS_GAMES2,
92 {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1,
93 0x22, 0x82, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
94
95 {AIM_CAPS_SENDBUDDYLIST,
96 {0x09, 0x46, 0x13, 0x4b, 0x4c, 0x7f, 0x11, 0xd1,
97 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
98
99 {AIM_CAPS_LAST}
5b79dc93 100};
101
d410cf58 102/*
103 * This still takes a length parameter even with a bstream because capabilities
104 * are not naturally bounded.
105 *
106 */
107faim_internal fu16_t aim_getcap(aim_session_t *sess, aim_bstream_t *bs, int len)
b69540e3 108{
d410cf58 109 fu16_t flags = 0;
110 int offset;
111
112 for (offset = 0; aim_bstream_empty(bs) && (offset < len); offset += 0x10) {
113 fu8_t *cap;
114 int i, identified;
b69540e3 115
d410cf58 116 cap = aimbs_getraw(bs, 0x10);
f7bb124a 117
d410cf58 118 for (i = 0, identified = 0; !(aim_caps[i].flag & AIM_CAPS_LAST); i++) {
f7bb124a 119
d410cf58 120 if (memcmp(&aim_caps[i].data, cap, 0x10) == 0) {
121 flags |= aim_caps[i].flag;
122 identified++;
123 break; /* should only match once... */
f7bb124a 124
d410cf58 125 }
126 }
f7bb124a 127
d410cf58 128 if (!identified)
129 faimdprintf(sess, 0, "unknown capability!\n");
0589dc54 130
d410cf58 131 free(cap);
132 }
f7bb124a 133
d410cf58 134 return flags;
b69540e3 135}
136
d410cf58 137faim_internal int aim_putcap(aim_bstream_t *bs, fu16_t caps)
b69540e3 138{
d410cf58 139 int i;
b69540e3 140
d410cf58 141 if (!bs)
142 return -EINVAL;
f7bb124a 143
d410cf58 144 for (i = 0; aim_bstream_empty(bs); i++) {
f7bb124a 145
d410cf58 146 if (aim_caps[i].flag == AIM_CAPS_LAST)
147 break;
b69540e3 148
d410cf58 149 if (caps & aim_caps[i].flag)
150 aimbs_putraw(bs, aim_caps[i].data, 0x10);
b69540e3 151
d410cf58 152 }
153
154 return 0;
b69540e3 155}
156
9de3ca7e 157/*
d410cf58 158 * AIM is fairly regular about providing user info. This is a generic
159 * routine to extract it in its standard form.
9de3ca7e 160 */
d410cf58 161faim_internal int aim_extractuserinfo(aim_session_t *sess, aim_bstream_t *bs, struct aim_userinfo_s *outinfo)
9de3ca7e 162{
d410cf58 163 int curtlv, tlvcnt;
164 fu8_t snlen;
165
166 if (!bs || !outinfo)
167 return -EINVAL;
168
169 /* Clear out old data first */
170 memset(outinfo, 0x00, sizeof(struct aim_userinfo_s));
171
172 /*
173 * Screen name. Stored as an unterminated string prepended with a
174 * byte containing its length.
175 */
176 snlen = aimbs_get8(bs);
177 aimbs_getrawbuf(bs, outinfo->sn, snlen);
178
179 /*
180 * Warning Level. Stored as an unsigned short.
181 */
182 outinfo->warnlevel = aimbs_get16(bs);
183
184 /*
185 * TLV Count. Unsigned short representing the number of
186 * Type-Length-Value triples that follow.
187 */
188 tlvcnt = aimbs_get16(bs);
189
190 /*
191 * Parse out the Type-Length-Value triples as they're found.
192 */
193 for (curtlv = 0; curtlv < tlvcnt; curtlv++) {
194 int endpos;
195 fu16_t type, length;
196
197 type = aimbs_get16(bs);
198 length = aimbs_get16(bs);
199
200 endpos = aim_bstream_curpos(bs) + length;
201
202 if (type == 0x0001) {
203 /*
204 * Type = 0x0001: User flags
205 *
206 * Specified as any of the following ORed together:
207 * 0x0001 Trial (user less than 60days)
208 * 0x0002 Unknown bit 2
209 * 0x0004 AOL Main Service user
210 * 0x0008 Unknown bit 4
211 * 0x0010 Free (AIM) user
212 * 0x0020 Away
25aaf30e 213 * 0x0400 ActiveBuddy
d410cf58 214 *
215 */
216 outinfo->flags = aimbs_get16(bs);
217
218 } else if (type == 0x0002) {
219 /*
220 * Type = 0x0002: Member-Since date.
221 *
222 * The time/date that the user originally registered for
223 * the service, stored in time_t format.
224 */
225 outinfo->membersince = aimbs_get32(bs);
226
227 } else if (type == 0x0003) {
228 /*
229 * Type = 0x0003: On-Since date.
230 *
231 * The time/date that the user started their current
232 * session, stored in time_t format.
233 */
234 outinfo->onlinesince = aimbs_get32(bs);
235
236 } else if (type == 0x0004) {
237 /*
238 * Type = 0x0004: Idle time.
239 *
240 * Number of seconds since the user actively used the
241 * service.
242 *
243 * Note that the client tells the server when to start
244 * counting idle times, so this may or may not be
245 * related to reality.
246 */
247 outinfo->idletime = aimbs_get16(bs);
248
249 } else if (type == 0x0006) {
250 /*
251 * Type = 0x0006: ICQ Online Status
252 *
253 * ICQ's Away/DND/etc "enriched" status. Some decoding
254 * of values done by Scott <darkagl@pcnet.com>
255 */
256 aimbs_get16(bs);
257 outinfo->icqinfo.status = aimbs_get16(bs);
258
259 } else if (type == 0x000a) {
260 /*
261 * Type = 0x000a
262 *
263 * ICQ User IP Address.
264 * Ahh, the joy of ICQ security.
265 */
266 outinfo->icqinfo.ipaddr = aimbs_get32(bs);
267
268 } else if (type == 0x000c) {
269 /*
270 * Type = 0x000c
271 *
272 * random crap containing the IP address,
273 * apparently a port number, and some Other Stuff.
274 *
275 */
276 aimbs_getrawbuf(bs, outinfo->icqinfo.crap, 0x25);
277
278 } else if (type == 0x000d) {
279 /*
280 * Type = 0x000d
281 *
282 * Capability information.
283 *
284 */
285 outinfo->capabilities = aim_getcap(sess, bs, length);
286
287 } else if (type == 0x000e) {
288 /*
289 * Type = 0x000e
290 *
291 * Unknown. Always of zero length, and always only
292 * on AOL users.
293 *
294 * Ignore.
295 *
296 */
297
298 } else if ((type == 0x000f) || (type == 0x0010)) {
299 /*
300 * Type = 0x000f: Session Length. (AIM)
301 * Type = 0x0010: Session Length. (AOL)
302 *
303 * The duration, in seconds, of the user's current
304 * session.
305 *
306 * Which TLV type this comes in depends on the
307 * service the user is using (AIM or AOL).
308 *
309 */
310 outinfo->sessionlen = aimbs_get32(bs);
311
312 } else {
313
314 /*
315 * Reaching here indicates that either AOL has
316 * added yet another TLV for us to deal with,
317 * or the parsing has gone Terribly Wrong.
318 *
319 * Either way, inform the owner and attempt
320 * recovery.
321 *
322 */
323 faimdprintf(sess, 0, "userinfo: **warning: unexpected TLV:\n");
324 faimdprintf(sess, 0, "userinfo: sn =%s\n", outinfo->sn);
325 faimdprintf(sess, 0, "userinfo: type =0x%04x\n",type);
326 faimdprintf(sess, 0, "userinfo: length=0x%04x\n", length);
327
328 }
329
330 /* Save ourselves. */
331 aim_bstream_setpos(bs, endpos);
332 }
333
334 return 0;
9de3ca7e 335}
336
50443ea0 337/*
338 * Inverse of aim_extractuserinfo()
339 */
d410cf58 340faim_internal int aim_putuserinfo(aim_bstream_t *bs, struct aim_userinfo_s *info)
50443ea0 341{
d410cf58 342 aim_tlvlist_t *tlvlist = NULL;
50443ea0 343
d410cf58 344 if (!bs || !info)
345 return -EINVAL;
50443ea0 346
d410cf58 347 aimbs_put8(bs, strlen(info->sn));
348 aimbs_putraw(bs, info->sn, strlen(info->sn));
50443ea0 349
d410cf58 350 aimbs_put16(bs, info->warnlevel);
50443ea0 351
9c38f1a7 352
d410cf58 353 aim_addtlvtochain16(&tlvlist, 0x0001, info->flags);
354 aim_addtlvtochain32(&tlvlist, 0x0002, info->membersince);
355 aim_addtlvtochain32(&tlvlist, 0x0003, info->onlinesince);
356 aim_addtlvtochain16(&tlvlist, 0x0004, info->idletime);
3b101546 357
358#if ICQ_OSCAR_SUPPORT
d410cf58 359 if (atoi(info->sn) != 0) {
360 aim_addtlvtochain16(&tlvlist, 0x0006, info->icqinfo.status);
361 aim_addtlvtochain32(&tlvlist, 0x000a, info->icqinfo.ipaddr);
362 }
3b101546 363#endif
364
d410cf58 365 aim_addtlvtochain_caps(&tlvlist, 0x000d, info->capabilities);
3b101546 366
d410cf58 367 aim_addtlvtochain32(&tlvlist, (fu16_t)((info->flags & AIM_FLAG_AOL) ? 0x0010 : 0x000f), info->sessionlen);
3b101546 368
d410cf58 369 aimbs_put16(bs, aim_counttlvchain(&tlvlist));
370 aim_writetlvchain(bs, &tlvlist);
371 aim_freetlvchain(&tlvlist);
3b101546 372
d410cf58 373 return 0;
50443ea0 374}
375
d410cf58 376faim_export int aim_sendbuddyoncoming(aim_session_t *sess, aim_conn_t *conn, struct aim_userinfo_s *info)
50443ea0 377{
d410cf58 378 aim_frame_t *fr;
379 aim_snacid_t snacid;
50443ea0 380
d410cf58 381 if (!sess || !conn || !info)
382 return -EINVAL;
50443ea0 383
d410cf58 384 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
385 return -ENOMEM;
50443ea0 386
d410cf58 387 snacid = aim_cachesnac(sess, 0x0003, 0x000b, 0x0000, NULL, 0);
388
389 aim_putsnac(&fr->data, 0x0003, 0x000b, 0x0000, snacid);
390 aim_putuserinfo(&fr->data, info);
50443ea0 391
d410cf58 392 aim_tx_enqueue(sess, fr);
50443ea0 393
d410cf58 394 return 0;
50443ea0 395}
396
d410cf58 397faim_export int aim_sendbuddyoffgoing(aim_session_t *sess, aim_conn_t *conn, const char *sn)
50443ea0 398{
d410cf58 399 aim_frame_t *fr;
400 aim_snacid_t snacid;
50443ea0 401
d410cf58 402 if (!sess || !conn || !sn)
403 return -EINVAL;
50443ea0 404
d410cf58 405 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn))))
406 return -ENOMEM;
50443ea0 407
d410cf58 408 snacid = aim_cachesnac(sess, 0x0003, 0x000c, 0x0000, NULL, 0);
409
410 aim_putsnac(&fr->data, 0x0003, 0x000c, 0x0000, snacid);
411 aimbs_put8(&fr->data, strlen(sn));
412 aimbs_putraw(&fr->data, sn, strlen(sn));
50443ea0 413
d410cf58 414 aim_tx_enqueue(sess, fr);
50443ea0 415
d410cf58 416 return 0;
50443ea0 417}
262272cc 418
d410cf58 419/*
420 * Huh? What is this?
421 */
422faim_export int aim_0002_000b(aim_session_t *sess, aim_conn_t *conn, const char *sn)
e80a0fa9 423{
d410cf58 424 aim_frame_t *fr;
425 aim_snacid_t snacid;
e80a0fa9 426
d410cf58 427 if (!sess || !conn || !sn)
428 return -EINVAL;
e80a0fa9 429
d410cf58 430 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn))))
431 return -ENOMEM;
e80a0fa9 432
d410cf58 433 snacid = aim_cachesnac(sess, 0x0002, 0x000b, 0x0000, NULL, 0);
434
435 aim_putsnac(&fr->data, 0x0002, 0x000b, 0x0000, snacid);
436 aimbs_put8(&fr->data, strlen(sn));
437 aimbs_putraw(&fr->data, sn, strlen(sn));
e80a0fa9 438
d410cf58 439 aim_tx_enqueue(sess, fr);
e80a0fa9 440
d410cf58 441 return 0;
e80a0fa9 442}
443
b39e0a91 444/*
445 * Normally contains:
446 * t(0001) - short containing max profile length (value = 1024)
447 * t(0002) - short - unknown (value = 16) [max MIME type length?]
25aaf30e 448 * t(0003) - short - unknown (value = 10)
b39e0a91 449 */
d410cf58 450static int rights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
00ef5271 451{
d410cf58 452 aim_tlvlist_t *tlvlist;
0867959e 453 aim_rxcallback_t userfunc;
454 int ret = 0;
d410cf58 455 fu16_t maxsiglen = 0;
b39e0a91 456
d410cf58 457 tlvlist = aim_readtlvchain(bs);
00ef5271 458
0867959e 459 if (aim_gettlv(tlvlist, 0x0001, 1))
460 maxsiglen = aim_gettlv16(tlvlist, 0x0001, 1);
00ef5271 461
0867959e 462 if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
463 ret = userfunc(sess, rx, maxsiglen);
b39e0a91 464
0867959e 465 aim_freetlvchain(&tlvlist);
00ef5271 466
0867959e 467 return ret;
00ef5271 468}
469
d410cf58 470static int userinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
00ef5271 471{
d410cf58 472 struct aim_userinfo_s userinfo;
473 char *text_encoding = NULL, *text = NULL;
474 aim_rxcallback_t userfunc;
475 aim_tlvlist_t *tlvlist;
476 aim_snac_t *origsnac = NULL;
477 struct aim_priv_inforeq *inforeq;
478 int ret = 0;
479
480 origsnac = aim_remsnac(sess, snac->id);
481
482 if (!origsnac || !origsnac->data) {
483 faimdprintf(sess, 0, "parse_userinfo_middle: major problem: no snac stored!\n");
484 return 0;
485 }
486
487 inforeq = (struct aim_priv_inforeq *)origsnac->data;
488
489 if ((inforeq->infotype != AIM_GETINFO_GENERALINFO) &&
490 (inforeq->infotype != AIM_GETINFO_AWAYMESSAGE)) {
491 faimdprintf(sess, 0, "parse_userinfo_middle: unknown infotype in request! (0x%04x)\n", inforeq->infotype);
492 return 0;
493 }
494
495 aim_extractuserinfo(sess, bs, &userinfo);
496
497 tlvlist = aim_readtlvchain(bs);
498
499 /*
500 * Depending on what informational text was requested, different
501 * TLVs will appear here.
502 *
503 * Profile will be 1 and 2, away message will be 3 and 4.
504 */
505 if (aim_gettlv(tlvlist, 0x0001, 1)) {
506 text_encoding = aim_gettlv_str(tlvlist, 0x0001, 1);
507 text = aim_gettlv_str(tlvlist, 0x0002, 1);
508 } else if (aim_gettlv(tlvlist, 0x0003, 1)) {
509 text_encoding = aim_gettlv_str(tlvlist, 0x0003, 1);
510 text = aim_gettlv_str(tlvlist, 0x0004, 1);
511 }
512
513 if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
514 ret = userfunc(sess, rx, &userinfo, text_encoding, text, inforeq->infotype);
515
516 free(text_encoding);
517 free(text);
518
519 aim_freetlvchain(&tlvlist);
520
521 if (origsnac)
522 free(origsnac->data);
523 free(origsnac);
524
525 return ret;
00ef5271 526}
527
d410cf58 528static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
00ef5271 529{
530
d410cf58 531 if (snac->subtype == 0x0003)
532 return rights(sess, mod, rx, snac, bs);
533 else if (snac->subtype == 0x0006)
534 return userinfo(sess, mod, rx, snac, bs);
00ef5271 535
d410cf58 536 return 0;
00ef5271 537}
538
d410cf58 539faim_internal int locate_modfirst(aim_session_t *sess, aim_module_t *mod)
00ef5271 540{
541
d410cf58 542 mod->family = 0x0002;
543 mod->version = 0x0000;
544 mod->flags = 0;
545 strncpy(mod->name, "locate", sizeof(mod->name));
546 mod->snachandler = snachandler;
00ef5271 547
d410cf58 548 return 0;
00ef5271 549}
This page took 0.209873 seconds and 5 git commands to generate.