source: protocols/oscar/info.c @ fcb2c2e

Last change on this file since fcb2c2e was 6042a54, checked in by Wilmer van der Gaast <wilmer@…>, at 2012-10-19T23:38:33Z

Massive cleanup in OSCAR.

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