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