]> andersk Git - libfaim.git/blame - src/info.c
this gets faimtest to compile on my work box. stupid gcc 2.96
[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
213 *
214 */
215 outinfo->flags = aimbs_get16(bs);
216
217 } else if (type == 0x0002) {
218 /*
219 * Type = 0x0002: Member-Since date.
220 *
221 * The time/date that the user originally registered for
222 * the service, stored in time_t format.
223 */
224 outinfo->membersince = aimbs_get32(bs);
225
226 } else if (type == 0x0003) {
227 /*
228 * Type = 0x0003: On-Since date.
229 *
230 * The time/date that the user started their current
231 * session, stored in time_t format.
232 */
233 outinfo->onlinesince = aimbs_get32(bs);
234
235 } else if (type == 0x0004) {
236 /*
237 * Type = 0x0004: Idle time.
238 *
239 * Number of seconds since the user actively used the
240 * service.
241 *
242 * Note that the client tells the server when to start
243 * counting idle times, so this may or may not be
244 * related to reality.
245 */
246 outinfo->idletime = aimbs_get16(bs);
247
248 } else if (type == 0x0006) {
249 /*
250 * Type = 0x0006: ICQ Online Status
251 *
252 * ICQ's Away/DND/etc "enriched" status. Some decoding
253 * of values done by Scott <darkagl@pcnet.com>
254 */
255 aimbs_get16(bs);
256 outinfo->icqinfo.status = aimbs_get16(bs);
257
258 } else if (type == 0x000a) {
259 /*
260 * Type = 0x000a
261 *
262 * ICQ User IP Address.
263 * Ahh, the joy of ICQ security.
264 */
265 outinfo->icqinfo.ipaddr = aimbs_get32(bs);
266
267 } else if (type == 0x000c) {
268 /*
269 * Type = 0x000c
270 *
271 * random crap containing the IP address,
272 * apparently a port number, and some Other Stuff.
273 *
274 */
275 aimbs_getrawbuf(bs, outinfo->icqinfo.crap, 0x25);
276
277 } else if (type == 0x000d) {
278 /*
279 * Type = 0x000d
280 *
281 * Capability information.
282 *
283 */
284 outinfo->capabilities = aim_getcap(sess, bs, length);
285
286 } else if (type == 0x000e) {
287 /*
288 * Type = 0x000e
289 *
290 * Unknown. Always of zero length, and always only
291 * on AOL users.
292 *
293 * Ignore.
294 *
295 */
296
297 } else if ((type == 0x000f) || (type == 0x0010)) {
298 /*
299 * Type = 0x000f: Session Length. (AIM)
300 * Type = 0x0010: Session Length. (AOL)
301 *
302 * The duration, in seconds, of the user's current
303 * session.
304 *
305 * Which TLV type this comes in depends on the
306 * service the user is using (AIM or AOL).
307 *
308 */
309 outinfo->sessionlen = aimbs_get32(bs);
310
311 } else {
312
313 /*
314 * Reaching here indicates that either AOL has
315 * added yet another TLV for us to deal with,
316 * or the parsing has gone Terribly Wrong.
317 *
318 * Either way, inform the owner and attempt
319 * recovery.
320 *
321 */
322 faimdprintf(sess, 0, "userinfo: **warning: unexpected TLV:\n");
323 faimdprintf(sess, 0, "userinfo: sn =%s\n", outinfo->sn);
324 faimdprintf(sess, 0, "userinfo: type =0x%04x\n",type);
325 faimdprintf(sess, 0, "userinfo: length=0x%04x\n", length);
326
327 }
328
329 /* Save ourselves. */
330 aim_bstream_setpos(bs, endpos);
331 }
332
333 return 0;
9de3ca7e 334}
335
50443ea0 336/*
337 * Inverse of aim_extractuserinfo()
338 */
d410cf58 339faim_internal int aim_putuserinfo(aim_bstream_t *bs, struct aim_userinfo_s *info)
50443ea0 340{
d410cf58 341 aim_tlvlist_t *tlvlist = NULL;
50443ea0 342
d410cf58 343 if (!bs || !info)
344 return -EINVAL;
50443ea0 345
d410cf58 346 aimbs_put8(bs, strlen(info->sn));
347 aimbs_putraw(bs, info->sn, strlen(info->sn));
50443ea0 348
d410cf58 349 aimbs_put16(bs, info->warnlevel);
50443ea0 350
9c38f1a7 351
d410cf58 352 aim_addtlvtochain16(&tlvlist, 0x0001, info->flags);
353 aim_addtlvtochain32(&tlvlist, 0x0002, info->membersince);
354 aim_addtlvtochain32(&tlvlist, 0x0003, info->onlinesince);
355 aim_addtlvtochain16(&tlvlist, 0x0004, info->idletime);
3b101546 356
357#if ICQ_OSCAR_SUPPORT
d410cf58 358 if (atoi(info->sn) != 0) {
359 aim_addtlvtochain16(&tlvlist, 0x0006, info->icqinfo.status);
360 aim_addtlvtochain32(&tlvlist, 0x000a, info->icqinfo.ipaddr);
361 }
3b101546 362#endif
363
d410cf58 364 aim_addtlvtochain_caps(&tlvlist, 0x000d, info->capabilities);
3b101546 365
d410cf58 366 aim_addtlvtochain32(&tlvlist, (fu16_t)((info->flags & AIM_FLAG_AOL) ? 0x0010 : 0x000f), info->sessionlen);
3b101546 367
d410cf58 368 aimbs_put16(bs, aim_counttlvchain(&tlvlist));
369 aim_writetlvchain(bs, &tlvlist);
370 aim_freetlvchain(&tlvlist);
3b101546 371
d410cf58 372 return 0;
50443ea0 373}
374
d410cf58 375faim_export int aim_sendbuddyoncoming(aim_session_t *sess, aim_conn_t *conn, struct aim_userinfo_s *info)
50443ea0 376{
d410cf58 377 aim_frame_t *fr;
378 aim_snacid_t snacid;
50443ea0 379
d410cf58 380 if (!sess || !conn || !info)
381 return -EINVAL;
50443ea0 382
d410cf58 383 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
384 return -ENOMEM;
50443ea0 385
d410cf58 386 snacid = aim_cachesnac(sess, 0x0003, 0x000b, 0x0000, NULL, 0);
387
388 aim_putsnac(&fr->data, 0x0003, 0x000b, 0x0000, snacid);
389 aim_putuserinfo(&fr->data, info);
50443ea0 390
d410cf58 391 aim_tx_enqueue(sess, fr);
50443ea0 392
d410cf58 393 return 0;
50443ea0 394}
395
d410cf58 396faim_export int aim_sendbuddyoffgoing(aim_session_t *sess, aim_conn_t *conn, const char *sn)
50443ea0 397{
d410cf58 398 aim_frame_t *fr;
399 aim_snacid_t snacid;
50443ea0 400
d410cf58 401 if (!sess || !conn || !sn)
402 return -EINVAL;
50443ea0 403
d410cf58 404 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn))))
405 return -ENOMEM;
50443ea0 406
d410cf58 407 snacid = aim_cachesnac(sess, 0x0003, 0x000c, 0x0000, NULL, 0);
408
409 aim_putsnac(&fr->data, 0x0003, 0x000c, 0x0000, snacid);
410 aimbs_put8(&fr->data, strlen(sn));
411 aimbs_putraw(&fr->data, sn, strlen(sn));
50443ea0 412
d410cf58 413 aim_tx_enqueue(sess, fr);
50443ea0 414
d410cf58 415 return 0;
50443ea0 416}
262272cc 417
d410cf58 418/*
419 * Huh? What is this?
420 */
421faim_export int aim_0002_000b(aim_session_t *sess, aim_conn_t *conn, const char *sn)
e80a0fa9 422{
d410cf58 423 aim_frame_t *fr;
424 aim_snacid_t snacid;
e80a0fa9 425
d410cf58 426 if (!sess || !conn || !sn)
427 return -EINVAL;
e80a0fa9 428
d410cf58 429 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn))))
430 return -ENOMEM;
e80a0fa9 431
d410cf58 432 snacid = aim_cachesnac(sess, 0x0002, 0x000b, 0x0000, NULL, 0);
433
434 aim_putsnac(&fr->data, 0x0002, 0x000b, 0x0000, snacid);
435 aimbs_put8(&fr->data, strlen(sn));
436 aimbs_putraw(&fr->data, sn, strlen(sn));
e80a0fa9 437
d410cf58 438 aim_tx_enqueue(sess, fr);
e80a0fa9 439
d410cf58 440 return 0;
e80a0fa9 441}
442
b39e0a91 443/*
444 * Normally contains:
445 * t(0001) - short containing max profile length (value = 1024)
446 * t(0002) - short - unknown (value = 16) [max MIME type length?]
447 * t(0003) - short - unknown (value = 7)
448 */
d410cf58 449static int rights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
00ef5271 450{
d410cf58 451 aim_tlvlist_t *tlvlist;
0867959e 452 aim_rxcallback_t userfunc;
453 int ret = 0;
d410cf58 454 fu16_t maxsiglen = 0;
b39e0a91 455
d410cf58 456 tlvlist = aim_readtlvchain(bs);
00ef5271 457
0867959e 458 if (aim_gettlv(tlvlist, 0x0001, 1))
459 maxsiglen = aim_gettlv16(tlvlist, 0x0001, 1);
00ef5271 460
0867959e 461 if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
462 ret = userfunc(sess, rx, maxsiglen);
b39e0a91 463
0867959e 464 aim_freetlvchain(&tlvlist);
00ef5271 465
0867959e 466 return ret;
00ef5271 467}
468
d410cf58 469static int userinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
00ef5271 470{
d410cf58 471 struct aim_userinfo_s userinfo;
472 char *text_encoding = NULL, *text = NULL;
473 aim_rxcallback_t userfunc;
474 aim_tlvlist_t *tlvlist;
475 aim_snac_t *origsnac = NULL;
476 struct aim_priv_inforeq *inforeq;
477 int ret = 0;
478
479 origsnac = aim_remsnac(sess, snac->id);
480
481 if (!origsnac || !origsnac->data) {
482 faimdprintf(sess, 0, "parse_userinfo_middle: major problem: no snac stored!\n");
483 return 0;
484 }
485
486 inforeq = (struct aim_priv_inforeq *)origsnac->data;
487
488 if ((inforeq->infotype != AIM_GETINFO_GENERALINFO) &&
489 (inforeq->infotype != AIM_GETINFO_AWAYMESSAGE)) {
490 faimdprintf(sess, 0, "parse_userinfo_middle: unknown infotype in request! (0x%04x)\n", inforeq->infotype);
491 return 0;
492 }
493
494 aim_extractuserinfo(sess, bs, &userinfo);
495
496 tlvlist = aim_readtlvchain(bs);
497
498 /*
499 * Depending on what informational text was requested, different
500 * TLVs will appear here.
501 *
502 * Profile will be 1 and 2, away message will be 3 and 4.
503 */
504 if (aim_gettlv(tlvlist, 0x0001, 1)) {
505 text_encoding = aim_gettlv_str(tlvlist, 0x0001, 1);
506 text = aim_gettlv_str(tlvlist, 0x0002, 1);
507 } else if (aim_gettlv(tlvlist, 0x0003, 1)) {
508 text_encoding = aim_gettlv_str(tlvlist, 0x0003, 1);
509 text = aim_gettlv_str(tlvlist, 0x0004, 1);
510 }
511
512 if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
513 ret = userfunc(sess, rx, &userinfo, text_encoding, text, inforeq->infotype);
514
515 free(text_encoding);
516 free(text);
517
518 aim_freetlvchain(&tlvlist);
519
520 if (origsnac)
521 free(origsnac->data);
522 free(origsnac);
523
524 return ret;
00ef5271 525}
526
d410cf58 527static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
00ef5271 528{
529
d410cf58 530 if (snac->subtype == 0x0003)
531 return rights(sess, mod, rx, snac, bs);
532 else if (snac->subtype == 0x0006)
533 return userinfo(sess, mod, rx, snac, bs);
00ef5271 534
d410cf58 535 return 0;
00ef5271 536}
537
d410cf58 538faim_internal int locate_modfirst(aim_session_t *sess, aim_module_t *mod)
00ef5271 539{
540
d410cf58 541 mod->family = 0x0002;
542 mod->version = 0x0000;
543 mod->flags = 0;
544 strncpy(mod->name, "locate", sizeof(mod->name));
545 mod->snachandler = snachandler;
00ef5271 546
d410cf58 547 return 0;
00ef5271 548}
This page took 0.162936 seconds and 5 git commands to generate.