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