]> andersk Git - libfaim.git/blob - src/info.c
- Fri Mar 23 05:42:11 UTC 2001
[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 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(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 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     faimdprintf(sess, 0, "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(sess, 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    0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
130 };
131
132 faim_internal unsigned short aim_getcap(struct aim_session_t *sess, 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       faimdprintf(sess, 0, "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(struct aim_session_t *sess, unsigned 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(sess, 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[160];
421
422         faimdprintf(sess, 0, "userinfo: **warning: unexpected TLV:\n");
423         faimdprintf(sess, 0, "userinfo:   sn    =%s\n", outinfo->sn);
424         faimdprintf(sess, 0, "userinfo:   curtlv=0x%04x\n", curtlv);
425         faimdprintf(sess, 0, "userinfo:   type  =0x%04x\n",aimutil_get16(&buf[i]));
426         faimdprintf(sess, 0, "userinfo:   length=0x%04x\n", len = aimutil_get16(&buf[i+2]));
427         faimdprintf(sess, 0, "userinfo:   data: \n");
428         while (z<len)
429           {
430             x = snprintf(tmpstr, sizeof(tmpstr), "userinfo:      ");
431             for (y = 0; y < 8; y++)
432               {
433                 if (z<len)
434                   {
435                     snprintf(tmpstr+x, sizeof(tmpstr)-x, "%02x ", buf[i+4+z]);
436                     z++;
437                     x += 3;
438                   }
439                 else
440                   break;
441               }
442             faimdprintf(sess, 0, "%s\n", tmpstr);
443           }
444       }
445       break;
446     }  
447     /*
448      * No matter what, TLV triplets should always look like this:
449      *
450      *   u_short type;
451      *   u_short length;
452      *   u_char  data[length];
453      *
454      */
455     if (lastvalid) {
456       i += (2 + 2 + aimutil_get16(&buf[i+2]));     
457       curtlv++;
458     }
459   }
460   
461   return i;
462 }
463
464 /*
465  * This parses the user info stuff out all nice and pretty then calls 
466  * the higher-level callback (in the user app).
467  *
468  */
469 faim_internal int aim_parse_userinfo_middle(struct aim_session_t *sess,
470                                             struct command_rx_struct *command)
471 {
472   struct aim_userinfo_s userinfo;
473   char *text_encoding = NULL;
474   char *text = NULL;
475   u_int i = 0;
476   rxcallback_t userfunc=NULL;
477   struct aim_tlvlist_t *tlvlist;
478   struct aim_snac_t *origsnac = NULL;
479   u_long snacid;
480   struct aim_priv_inforeq *inforeq;
481   
482   snacid = aimutil_get32(&command->data[6]);
483   origsnac = aim_remsnac(sess, snacid);
484
485   if (!origsnac || !origsnac->data) {
486     faimdprintf(sess, 0, "parse_userinfo_middle: major problem: no snac stored!\n");
487     return 1;
488   }
489
490   inforeq = (struct aim_priv_inforeq *)origsnac->data;
491
492   switch (inforeq->infotype) {
493   case AIM_GETINFO_GENERALINFO:
494   case AIM_GETINFO_AWAYMESSAGE:
495     i = 10;
496
497     /*
498      * extractuserinfo will give us the basic metaTLV information
499      */
500     i += aim_extractuserinfo(sess, command->data+i, &userinfo);
501   
502     /*
503      * However, in this command, there's usually more TLVs following...
504      */ 
505     tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i);
506
507     /* 
508      * Depending on what informational text was requested, different
509      * TLVs will appear here.
510      *
511      * Profile will be 1 and 2, away message will be 3 and 4.
512      */
513     if (aim_gettlv(tlvlist, 0x0001, 1)) {
514       text_encoding = aim_gettlv_str(tlvlist, 0x0001, 1);
515       text = aim_gettlv_str(tlvlist, 0x0002, 1);
516     } else if (aim_gettlv(tlvlist, 0x0003, 1)) {
517       text_encoding = aim_gettlv_str(tlvlist, 0x0003, 1);
518       text = aim_gettlv_str(tlvlist, 0x0004, 1);
519     }
520
521     userfunc = aim_callhandler(sess, command->conn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO);
522     if (userfunc) {
523       i = userfunc(sess,
524                    command, 
525                    &userinfo, 
526                    text_encoding, 
527                    text,
528                    inforeq->infotype); 
529     }
530   
531     free(text_encoding);
532     free(text);
533     aim_freetlvchain(&tlvlist);
534     break;
535   default:
536     faimdprintf(sess, 0, "parse_userinfo_middle: unknown infotype in request! (0x%04x)\n", inforeq->infotype);
537     break;
538   }
539
540   if (origsnac) {
541     if (origsnac->data)
542       free(origsnac->data);
543     free(origsnac);
544   }
545
546   return 1;
547 }
548
549 /*
550  * Inverse of aim_extractuserinfo()
551  */
552 faim_internal int aim_putuserinfo(u_char *buf, int buflen, struct aim_userinfo_s *info)
553 {
554   int i = 0, numtlv = 0;
555   struct aim_tlvlist_t *tlvlist = NULL;
556
557   if (!buf || !info)
558     return 0;
559
560   i += aimutil_put8(buf+i, strlen(info->sn));
561   i += aimutil_putstr(buf+i, info->sn, strlen(info->sn));
562
563   i += aimutil_put16(buf+i, info->warnlevel);
564
565
566   aim_addtlvtochain16(&tlvlist, 0x0001, info->flags);
567   numtlv++;
568
569   aim_addtlvtochain32(&tlvlist, 0x0002, info->membersince);
570   numtlv++;
571
572   aim_addtlvtochain32(&tlvlist, 0x0003, info->onlinesince);
573   numtlv++;
574
575   aim_addtlvtochain16(&tlvlist, 0x0004, info->idletime);
576   numtlv++;
577
578 #if ICQ_OSCAR_SUPPORT
579   if(atoi(info->sn) != 0) {
580     aim_addtlvtochain16(&tlvlist, 0x0006, info->icqinfo.status);
581     aim_addtlvtochain32(&tlvlist, 0x000a, info->icqinfo.ipaddr);
582   }
583 #endif
584
585   aim_addtlvtochain_caps(&tlvlist, 0x000d, info->capabilities);
586   numtlv++;
587
588   aim_addtlvtochain32(&tlvlist, (unsigned short)((info->flags)&AIM_FLAG_AOL?0x0010:0x000f), info->sessionlen);
589   numtlv++;
590
591   i += aimutil_put16(buf+i, numtlv); /* tlvcount */
592   i += aim_writetlvchain(buf+i, buflen-i, &tlvlist); /* tlvs */
593   aim_freetlvchain(&tlvlist);
594
595   return i;
596 }
597
598 faim_export int aim_sendbuddyoncoming(struct aim_session_t *sess, struct aim_conn_t *conn, struct aim_userinfo_s *info)
599 {
600   struct command_tx_struct *tx;
601   int i = 0;
602
603   if (!sess || !conn || !info)
604     return 0;
605
606   if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 1152)))
607     return -1;
608
609   tx->lock = 1;
610
611   i += aimutil_put16(tx->data+i, 0x0003);
612   i += aimutil_put16(tx->data+i, 0x000b);
613   i += aimutil_put16(tx->data+i, 0x0000);
614   i += aimutil_put16(tx->data+i, 0x0000);
615   i += aimutil_put16(tx->data+i, 0x0000);
616
617   i += aim_putuserinfo(tx->data+i, tx->commandlen-i, info);
618
619   tx->commandlen = i;
620   tx->lock = 0;
621   aim_tx_enqueue(sess, tx);
622
623   return 0;
624 }
625
626 faim_export int aim_sendbuddyoffgoing(struct aim_session_t *sess, struct aim_conn_t *conn, char *sn)
627 {
628   struct command_tx_struct *tx;
629   int i = 0;
630
631   if (!sess || !conn || !sn)
632     return 0;
633
634   if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+1+strlen(sn))))
635     return -1;
636
637   tx->lock = 1;
638
639   i += aimutil_put16(tx->data+i, 0x0003);
640   i += aimutil_put16(tx->data+i, 0x000c);
641   i += aimutil_put16(tx->data+i, 0x0000);
642   i += aimutil_put16(tx->data+i, 0x0000);
643   i += aimutil_put16(tx->data+i, 0x0000);
644
645   i += aimutil_put8(tx->data+i, strlen(sn));
646   i += aimutil_putstr(tx->data+i, sn, strlen(sn));
647   
648   tx->lock = 0;
649   aim_tx_enqueue(sess, tx);
650
651   return 0;
652 }
653
This page took 1.083161 seconds and 5 git commands to generate.