]> andersk Git - libfaim.git/blob - src/info.c
a36a7f52525f522d457cd9a8a1cbf56d784836b8
[libfaim.git] / src / info.c
1 /*
2  * aim_info.c
3  *
4  * The functions here are responsible for requesting and parsing information-
5  * gathering SNACs.  
6  *
7  */
8
9 #define FAIM_INTERNAL
10 #include <aim.h>
11
12 struct aim_priv_inforeq {
13   char sn[MAXSNLEN+1];
14   unsigned short infotype;
15 };
16
17 faim_export int aim_getinfo(struct aim_session_t *sess,
18                             struct aim_conn_t *conn, 
19                             const char *sn,
20                             unsigned short infotype)
21 {
22   struct command_tx_struct *newpacket;
23   struct aim_priv_inforeq privdata;
24   int i = 0;
25
26   if (!sess || !conn || !sn)
27     return -1;
28
29   if ((infotype != AIM_GETINFO_GENERALINFO) &&
30       (infotype != AIM_GETINFO_AWAYMESSAGE))
31     return -1;
32
33   if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 12+1+strlen(sn))))
34     return -1;
35
36   newpacket->lock = 1;
37
38   i = aim_putsnac(newpacket->data, 0x0002, 0x0005, 0x0000, sess->snac_nextid);
39
40   i += aimutil_put16(newpacket->data+i, infotype);
41   i += aimutil_put8(newpacket->data+i, strlen(sn));
42   i += aimutil_putstr(newpacket->data+i, sn, strlen(sn));
43
44   newpacket->lock = 0;
45   aim_tx_enqueue(sess, newpacket);
46
47   strncpy(privdata.sn, sn, sizeof(privdata.sn));
48   privdata.infotype = infotype;
49   aim_cachesnac(sess, 0x0002, 0x0005, 0x0000, &privdata, sizeof(struct aim_priv_inforeq));
50
51   return 0;
52 }
53
54 /*
55  * Capability blocks.  
56  */
57 static const struct {
58   unsigned short flag;
59   unsigned char data[16];
60 } aim_caps[] = {
61   
62   {AIM_CAPS_BUDDYICON,
63    {0x09, 0x46, 0x13, 0x46, 0x4c, 0x7f, 0x11, 0xd1, 
64     0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
65   
66   {AIM_CAPS_VOICE,
67    {0x09, 0x46, 0x13, 0x41, 0x4c, 0x7f, 0x11, 0xd1, 
68     0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
69
70   {AIM_CAPS_IMIMAGE,
71    {0x09, 0x46, 0x13, 0x45, 0x4c, 0x7f, 0x11, 0xd1, 
72     0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
73   
74   {AIM_CAPS_CHAT,
75    {0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, 
76     0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
77   
78   {AIM_CAPS_GETFILE,
79    {0x09, 0x46, 0x13, 0x48, 0x4c, 0x7f, 0x11, 0xd1,
80     0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
81
82   {AIM_CAPS_SENDFILE,
83    {0x09, 0x46, 0x13, 0x43, 0x4c, 0x7f, 0x11, 0xd1, 
84     0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
85
86   {AIM_CAPS_SAVESTOCKS,
87    {0x09, 0x46, 0x13, 0x47, 0x4c, 0x7f, 0x11, 0xd1,
88     0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
89
90   /*
91    * Indeed, there are two of these.  The former appears
92    * to be correct, but in some versions of winaim, the
93    * second one is set.  Either they forgot to fix endianness,
94    * or they made a typo. It really doesn't matter which.
95    */
96   {AIM_CAPS_GAMES,
97    {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1,
98     0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
99   {AIM_CAPS_GAMES2,
100    {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1,
101     0x22, 0x82, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
102
103   {AIM_CAPS_SENDBUDDYLIST,
104    {0x09, 0x46, 0x13, 0x4b, 0x4c, 0x7f, 0x11, 0xd1,
105     0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
106
107   {AIM_CAPS_LAST}
108 };
109
110 faim_internal unsigned short aim_getcap(struct aim_session_t *sess, unsigned char *capblock, int buflen)
111 {
112   unsigned short flags;
113   int i;
114   int offset = 0;
115   int identified;
116
117   for (offset = 0, flags = 0; offset < buflen; offset += 0x0010) {
118
119     for (i = 0, identified = 0; !(aim_caps[i].flag & AIM_CAPS_LAST); i++) {
120
121       if (memcmp(&aim_caps[i].data, capblock+offset, 0x10) == 0) {
122         flags |= aim_caps[i].flag;
123         identified++;
124         break; /* should only match once... */
125       }
126
127     }
128
129     if (!identified)
130       faimdprintf(sess, 0, "unknown capability!\n");
131
132   }
133
134   return flags;
135 }
136
137 faim_internal int aim_putcap(unsigned char *capblock, int buflen, unsigned short caps)
138 {
139   int offset, i;
140
141   if (!capblock)
142     return 0;
143
144   for (i = 0, offset = 0; 
145        !(aim_caps[i].flag & AIM_CAPS_LAST) && (offset < buflen); i++) {
146
147     if (caps & aim_caps[i].flag) {
148       memcpy(capblock+offset, aim_caps[i].data, 16);
149       offset += 16;
150     }
151
152   }
153
154   return offset;
155 }
156
157 /*
158  * AIM is fairly regular about providing user info.  This
159  * is a generic routine to extract it in its standard form.
160  */
161 faim_internal int aim_extractuserinfo(struct aim_session_t *sess, unsigned char *buf, struct aim_userinfo_s *outinfo)
162 {
163   int i = 0;
164   int tlvcnt = 0;
165   int curtlv = 0;
166   int tlv1 = 0;
167   u_short curtype;
168   int lastvalid;
169
170
171   if (!buf || !outinfo)
172     return -1;
173
174   /* Clear out old data first */
175   memset(outinfo, 0x00, sizeof(struct aim_userinfo_s));
176
177   /*
178    * Screen name.    Stored as an unterminated string prepended
179    *                 with an unsigned byte containing its length.
180    */
181   if (buf[i] < MAXSNLEN) {
182     memcpy(outinfo->sn, &(buf[i+1]), buf[i]);
183     outinfo->sn[(int)buf[i]] = '\0';
184   } else {
185     memcpy(outinfo->sn, &(buf[i+1]), MAXSNLEN-1);
186     outinfo->sn[MAXSNLEN] = '\0';
187   }
188   i = 1 + (int)buf[i];
189
190   /*
191    * Warning Level.  Stored as an unsigned short.
192    */
193   outinfo->warnlevel = aimutil_get16(&buf[i]);
194   i += 2;
195
196   /*
197    * TLV Count.      Unsigned short representing the number of 
198    *                 Type-Length-Value triples that follow.
199    */
200   tlvcnt = aimutil_get16(&buf[i]);
201   i += 2;
202
203   /* 
204    * Parse out the Type-Length-Value triples as they're found.
205    */
206   while (curtlv < tlvcnt) {
207     lastvalid = 1;
208     curtype = aimutil_get16(&buf[i]);
209     switch (curtype) {
210       /*
211        * Type = 0x0000: Invalid
212        *
213        * AOL has been trying to throw these in just to break us.
214        * They're real nice guys over there at AOL.  
215        *
216        * Just skip the two zero bytes and continue on. (This doesn't
217        * count towards tlvcnt!)
218        */
219     case 0x0000:
220       lastvalid = 0;
221       i += 2;
222       break;
223
224       /*
225        * Type = 0x0001: User flags
226        * 
227        * Specified as any of the following bitwise ORed together:
228        *      0x0001  Trial (user less than 60days)
229        *      0x0002  Unknown bit 2
230        *      0x0004  AOL Main Service user
231        *      0x0008  Unknown bit 4
232        *      0x0010  Free (AIM) user 
233        *      0x0020  Away
234        *
235        * In some odd cases, we can end up with more
236        * than one of these.  We only want the first,
237        * as the others may not be something we want.
238        *
239        */
240     case 0x0001:
241       if (tlv1) /* use only the first */
242         break;
243       outinfo->flags = aimutil_get16(&buf[i+4]);
244       tlv1++;
245       break;
246       
247       /*
248        * Type = 0x0002: Member-Since date. 
249        *
250        * The time/date that the user originally
251        * registered for the service, stored in 
252        * time_t format
253        */
254     case 0x0002: 
255       outinfo->membersince = aimutil_get32(&buf[i+4]);
256       break;
257       
258       /*
259        * Type = 0x0003: On-Since date.
260        *
261        * The time/date that the user started 
262        * their current session, stored in time_t
263        * format.
264        */
265     case 0x0003:
266       outinfo->onlinesince = aimutil_get32(&buf[i+4]);
267       break;
268       
269       /*
270        * Type = 0x0004: Idle time.
271        *
272        * Number of seconds since the user
273        * actively used the service.
274        */
275     case 0x0004:
276       outinfo->idletime = aimutil_get16(&buf[i+4]);
277       break;
278       
279       /*
280        * Type = 0x0006: ICQ Online Status
281        *
282        * ICQ's Away/DND/etc "enriched" status
283        * Some decoding of values done by Scott <darkagl@pcnet.com>
284        */
285     case 0x0006:
286       outinfo->icqinfo.status = aimutil_get16(buf+i+2+2+2);
287       break;
288
289
290       /*
291        * Type = 0x000a
292        *
293        * ICQ User IP Address.
294        * Ahh, the joy of ICQ security.
295        */
296     case 0x000a:
297       outinfo->icqinfo.ipaddr = aimutil_get32(&buf[i+4]);
298       break;
299
300       /* Type = 0x000c
301        *
302        * random crap containing the IP address,
303        * apparently a port number, and some Other Stuff.
304        *
305        */
306     case 0x000c:
307       memcpy(outinfo->icqinfo.crap, &buf[i+4], 0x25);
308       break;
309
310       /*
311        * Type = 0x000d
312        *
313        * Capability information.  Not real sure of
314        * actual decoding.  See comment on aim_bos_setprofile()
315        * in aim_misc.c about the capability block, its the same.
316        *
317        */
318     case 0x000d:
319       {
320         int len;
321         len = aimutil_get16(buf+i+2);
322         if (!len)
323           break;
324         
325         outinfo->capabilities = aim_getcap(sess, buf+i+4, len);
326       }
327       break;
328       
329       /*
330        * Type = 0x000e
331        *
332        * Unknown.  Always of zero length, and always only
333        * on AOL users.
334        *
335        * Ignore.
336        *
337        */
338     case 0x000e:
339       break;
340       
341       /*
342        * Type = 0x000f: Session Length. (AIM)
343        * Type = 0x0010: Session Length. (AOL)
344        *
345        * The duration, in seconds, of the user's
346        * current session.
347        *
348        * Which TLV type this comes in depends
349        * on the service the user is using (AIM or AOL).
350        *
351        */
352     case 0x000f:
353     case 0x0010:
354       outinfo->sessionlen = aimutil_get32(&buf[i+4]);
355       break;
356       
357       /*
358        * Reaching here indicates that either AOL has
359        * added yet another TLV for us to deal with, 
360        * or the parsing has gone Terribly Wrong.
361        *
362        * Either way, inform the owner and attempt
363        * recovery.
364        *
365        */
366     default:
367       {
368         int len,z = 0, y = 0, x = 0;
369         char tmpstr[160];
370
371         faimdprintf(sess, 0, "userinfo: **warning: unexpected TLV:\n");
372         faimdprintf(sess, 0, "userinfo:   sn    =%s\n", outinfo->sn);
373         faimdprintf(sess, 0, "userinfo:   curtlv=0x%04x\n", curtlv);
374         faimdprintf(sess, 0, "userinfo:   type  =0x%04x\n",aimutil_get16(&buf[i]));
375         faimdprintf(sess, 0, "userinfo:   length=0x%04x\n", len = aimutil_get16(&buf[i+2]));
376         faimdprintf(sess, 0, "userinfo:   data: \n");
377         while (z<len)
378           {
379             x = snprintf(tmpstr, sizeof(tmpstr), "userinfo:      ");
380             for (y = 0; y < 8; y++)
381               {
382                 if (z<len)
383                   {
384                     snprintf(tmpstr+x, sizeof(tmpstr)-x, "%02x ", buf[i+4+z]);
385                     z++;
386                     x += 3;
387                   }
388                 else
389                   break;
390               }
391             faimdprintf(sess, 0, "%s\n", tmpstr);
392           }
393       }
394       break;
395     }  
396     /*
397      * No matter what, TLV triplets should always look like this:
398      *
399      *   u_short type;
400      *   u_short length;
401      *   u_char  data[length];
402      *
403      */
404     if (lastvalid) {
405       i += (2 + 2 + aimutil_get16(&buf[i+2]));     
406       curtlv++;
407     }
408   }
409   
410   return i;
411 }
412
413 /*
414  * Inverse of aim_extractuserinfo()
415  */
416 faim_internal int aim_putuserinfo(u_char *buf, int buflen, struct aim_userinfo_s *info)
417 {
418   int i = 0, numtlv = 0;
419   struct aim_tlvlist_t *tlvlist = NULL;
420
421   if (!buf || !info)
422     return 0;
423
424   i += aimutil_put8(buf+i, strlen(info->sn));
425   i += aimutil_putstr(buf+i, info->sn, strlen(info->sn));
426
427   i += aimutil_put16(buf+i, info->warnlevel);
428
429
430   aim_addtlvtochain16(&tlvlist, 0x0001, info->flags);
431   numtlv++;
432
433   aim_addtlvtochain32(&tlvlist, 0x0002, info->membersince);
434   numtlv++;
435
436   aim_addtlvtochain32(&tlvlist, 0x0003, info->onlinesince);
437   numtlv++;
438
439   aim_addtlvtochain16(&tlvlist, 0x0004, info->idletime);
440   numtlv++;
441
442 #if ICQ_OSCAR_SUPPORT
443   if(atoi(info->sn) != 0) {
444     aim_addtlvtochain16(&tlvlist, 0x0006, info->icqinfo.status);
445     aim_addtlvtochain32(&tlvlist, 0x000a, info->icqinfo.ipaddr);
446   }
447 #endif
448
449   aim_addtlvtochain_caps(&tlvlist, 0x000d, info->capabilities);
450   numtlv++;
451
452   aim_addtlvtochain32(&tlvlist, (unsigned short)((info->flags)&AIM_FLAG_AOL?0x0010:0x000f), info->sessionlen);
453   numtlv++;
454
455   i += aimutil_put16(buf+i, numtlv); /* tlvcount */
456   i += aim_writetlvchain(buf+i, buflen-i, &tlvlist); /* tlvs */
457   aim_freetlvchain(&tlvlist);
458
459   return i;
460 }
461
462 faim_export int aim_sendbuddyoncoming(struct aim_session_t *sess, struct aim_conn_t *conn, struct aim_userinfo_s *info)
463 {
464   struct command_tx_struct *tx;
465   int i = 0;
466
467   if (!sess || !conn || !info)
468     return 0;
469
470   if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 1152)))
471     return -1;
472
473   tx->lock = 1;
474
475   i += aimutil_put16(tx->data+i, 0x0003);
476   i += aimutil_put16(tx->data+i, 0x000b);
477   i += aimutil_put16(tx->data+i, 0x0000);
478   i += aimutil_put16(tx->data+i, 0x0000);
479   i += aimutil_put16(tx->data+i, 0x0000);
480
481   i += aim_putuserinfo(tx->data+i, tx->commandlen-i, info);
482
483   tx->commandlen = i;
484   tx->lock = 0;
485   aim_tx_enqueue(sess, tx);
486
487   return 0;
488 }
489
490 faim_export int aim_sendbuddyoffgoing(struct aim_session_t *sess, struct aim_conn_t *conn, char *sn)
491 {
492   struct command_tx_struct *tx;
493   int i = 0;
494
495   if (!sess || !conn || !sn)
496     return 0;
497
498   if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+1+strlen(sn))))
499     return -1;
500
501   tx->lock = 1;
502
503   i += aimutil_put16(tx->data+i, 0x0003);
504   i += aimutil_put16(tx->data+i, 0x000c);
505   i += aimutil_put16(tx->data+i, 0x0000);
506   i += aimutil_put16(tx->data+i, 0x0000);
507   i += aimutil_put16(tx->data+i, 0x0000);
508
509   i += aimutil_put8(tx->data+i, strlen(sn));
510   i += aimutil_putstr(tx->data+i, sn, strlen(sn));
511   
512   tx->lock = 0;
513   aim_tx_enqueue(sess, tx);
514
515   return 0;
516 }
517
518 faim_export int aim_0002_000b(struct aim_session_t *sess, struct aim_conn_t *conn, const char *sn)
519 {
520   struct command_tx_struct *tx;
521   int i = 0;
522
523   if (!sess || !conn || !sn)
524     return 0;
525
526   if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+1+strlen(sn))))
527     return -1;
528
529   tx->lock = 1;
530
531   i = aim_putsnac(tx->data, 0x0002, 0x000b, 0x0000, sess->snac_nextid);
532   i += aimutil_put8(tx->data+i, strlen(sn));
533   i += aimutil_putstr(tx->data+i, sn, strlen(sn));
534   
535   tx->commandlen = i;
536   tx->lock = 0;
537   aim_tx_enqueue(sess, tx);
538
539   return 0;
540 }
541
542 /*
543  * Normally contains:
544  *   t(0001)  - short containing max profile length (value = 1024)
545  *   t(0002)  - short - unknown (value = 16) [max MIME type length?]
546  *   t(0003)  - short - unknown (value = 7)
547  */
548 static int rights(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
549 {
550         struct aim_tlvlist_t *tlvlist;
551         aim_rxcallback_t userfunc;
552         int ret = 0;
553         unsigned short maxsiglen = 0;
554
555         tlvlist = aim_readtlvchain(data, datalen);
556
557         if (aim_gettlv(tlvlist, 0x0001, 1))
558                 maxsiglen = aim_gettlv16(tlvlist, 0x0001, 1);
559
560         if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
561                 ret = userfunc(sess, rx, maxsiglen);
562
563         aim_freetlvchain(&tlvlist);
564
565         return ret;
566 }
567
568 static int userinfo(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
569 {
570   struct aim_userinfo_s userinfo;
571   char *text_encoding = NULL;
572   char *text = NULL;
573   int i = 0;
574   aim_rxcallback_t userfunc;
575   struct aim_tlvlist_t *tlvlist;
576   struct aim_snac_t *origsnac = NULL;
577   struct aim_priv_inforeq *inforeq;
578   int ret = 0;
579
580   origsnac = aim_remsnac(sess, snac->id);
581
582   if (!origsnac || !origsnac->data) {
583     faimdprintf(sess, 0, "parse_userinfo_middle: major problem: no snac stored!\n");
584     return 0;
585   }
586
587   inforeq = (struct aim_priv_inforeq *)origsnac->data;
588
589   if ((inforeq->infotype != AIM_GETINFO_GENERALINFO) &&
590       (inforeq->infotype != AIM_GETINFO_AWAYMESSAGE)) {
591     faimdprintf(sess, 0, "parse_userinfo_middle: unknown infotype in request! (0x%04x)\n", inforeq->infotype);
592     return 0;
593   }
594
595   i = aim_extractuserinfo(sess, data, &userinfo);
596   
597   tlvlist = aim_readtlvchain(data+i, datalen-i);
598
599   /* 
600    * Depending on what informational text was requested, different
601    * TLVs will appear here.
602    *
603    * Profile will be 1 and 2, away message will be 3 and 4.
604    */
605   if (aim_gettlv(tlvlist, 0x0001, 1)) {
606     text_encoding = aim_gettlv_str(tlvlist, 0x0001, 1);
607     text = aim_gettlv_str(tlvlist, 0x0002, 1);
608   } else if (aim_gettlv(tlvlist, 0x0003, 1)) {
609     text_encoding = aim_gettlv_str(tlvlist, 0x0003, 1);
610     text = aim_gettlv_str(tlvlist, 0x0004, 1);
611   }
612
613   if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
614     ret = userfunc(sess, rx, &userinfo, text_encoding, text, inforeq->infotype);
615
616   free(text_encoding);
617   free(text);
618
619   aim_freetlvchain(&tlvlist);
620
621   if (origsnac) {
622     if (origsnac->data)
623       free(origsnac->data);
624     free(origsnac);
625   }
626
627   return ret;
628 }
629
630 static int snachandler(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
631 {
632
633   if (snac->subtype == 0x0003)
634     return rights(sess, mod, rx, snac, data, datalen);
635   else if (snac->subtype == 0x0006)
636     return userinfo(sess, mod, rx, snac, data, datalen);
637
638   return 0;
639 }
640
641 faim_internal int locate_modfirst(struct aim_session_t *sess, aim_module_t *mod)
642 {
643
644   mod->family = 0x0002;
645   mod->version = 0x0000;
646   mod->flags = 0;
647   strncpy(mod->name, "locate", sizeof(mod->name));
648   mod->snachandler = snachandler;
649
650   return 0;
651 }
This page took 0.519431 seconds and 3 git commands to generate.