source: protocols/oscar/info.c @ 2e8523b

Last change on this file since 2e8523b was 5ebff60, checked in by dequis <dx@…>, at 2015-02-20T22:50:54Z

Reindent everything to K&R style with tabs

Used uncrustify, with the configuration file in ./doc/uncrustify.cfg

Commit author set to "Indent <please@…>" so that it's easier to
skip while doing git blame.

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