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