view libfaim/aim_info.c @ 283:0f14e6d8a51b

[gaim-migrate @ 293] - Good fun things here. Sometimes I think Adam does things just to make it difficult for me, I swear (j/k :) ). - I moved all the .h files into the faim/ directory because I'm tired of changing faim/aim.h to just aim.h every time Adam makes an update. - libfaim can now send messages much longer than WinAIM or TOC can. But, the length checking code is still in conversation.c, and I haven't changed it yet, mostly because I'm lazy. committer: Tailor Script <tailor@pidgin.im>
author Eric Warmenhoven <eric@warmenhoven.org>
date Wed, 31 May 2000 03:20:11 +0000
parents 810c595258c8
children e4c34ca88d9b
line wrap: on
line source

/*
 * aim_info.c
 *
 * The functions here are responsible for requesting and parsing information-
 * gathering SNACs.  
 *
 */


#include <faim/aim.h>

struct aim_priv_inforeq {
  char sn[MAXSNLEN];
  unsigned short infotype;
};

u_long aim_getinfo(struct aim_session_t *sess,
		   struct aim_conn_t *conn, 
		   const char *sn,
		   unsigned short infotype)
{
  struct command_tx_struct *newpacket;
  int i = 0;

  if (!sess || !conn || !sn)
    return 0;

  if (!(newpacket = aim_tx_new(0x0002, conn, 12+1+strlen(sn))))
    return -1;

  newpacket->lock = 1;

  i = aim_putsnac(newpacket->data, 0x0002, 0x0005, 0x0000, sess->snac_nextid);

  i += aimutil_put16(newpacket->data+i, infotype);
  i += aimutil_put8(newpacket->data+i, strlen(sn));
  i += aimutil_putstr(newpacket->data+i, sn, strlen(sn));

  newpacket->lock = 0;
  aim_tx_enqueue(sess, newpacket);

  {
    struct aim_snac_t snac;
    
    snac.id = sess->snac_nextid;
    snac.family = 0x0002;
    snac.type = 0x0005;
    snac.flags = 0x0000;

    snac.data = malloc(sizeof(struct aim_priv_inforeq));
    strcpy(((struct aim_priv_inforeq *)snac.data)->sn, sn);
    ((struct aim_priv_inforeq *)snac.data)->infotype = infotype;

    aim_newsnac(sess, &snac);
  }

  return (sess->snac_nextid++);
}


/*
 * Capability blocks.  
 */
u_char aim_caps[6][16] = {
  
  /* Buddy icon */
  {0x09, 0x46, 0x13, 0x46, 0x4c, 0x7f, 0x11, 0xd1, 
   0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
  
  /* Voice */
  {0x09, 0x46, 0x13, 0x41, 0x4c, 0x7f, 0x11, 0xd1, 
   0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
  
  /* IM image */
  {0x09, 0x46, 0x13, 0x45, 0x4c, 0x7f, 0x11, 0xd1, 
   0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
  
  /* Chat */
  {0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, 
   0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
  
  /* Get file */
  {0x09, 0x46, 0x13, 0x48, 0x4c, 0x7f, 0x11, 0xd1,
   0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
  
  /* Send file */
  {0x09, 0x46, 0x13, 0x43, 0x4c, 0x7f, 0x11, 0xd1, 
   0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}
};

/*
 * AIM is fairly regular about providing user info.  This
 * is a generic routine to extract it in its standard form.
 */
int aim_extractuserinfo(u_char *buf, struct aim_userinfo_s *outinfo)
{
  int i = 0;
  int tlvcnt = 0;
  int curtlv = 0;
  int tlv1 = 0;
  u_short curtype;


  if (!buf || !outinfo)
    return -1;

  /* Clear out old data first */
  memset(outinfo, 0x00, sizeof(struct aim_userinfo_s));

  /*
   * Screen name.    Stored as an unterminated string prepended
   *                 with an unsigned byte containing its length.
   */
  memcpy(outinfo->sn, &(buf[i+1]), buf[i]);
  outinfo->sn[(int)buf[i]] = '\0';
  i = 1 + (int)buf[i];

  /*
   * Warning Level.  Stored as an unsigned short.
   */
  outinfo->warnlevel = aimutil_get16(&buf[i]);
  i += 2;

  /*
   * TLV Count.      Unsigned short representing the number of 
   *                 Type-Length-Value triples that follow.
   */
  tlvcnt = aimutil_get16(&buf[i]);
  i += 2;

  /* 
   * Parse out the Type-Length-Value triples as they're found.
   */
  while (curtlv < tlvcnt)
    {
      curtype = aimutil_get16(&buf[i]);
      switch (curtype)
	{
	  /*
	   * Type = 0x0001: Member Class.   
	   * 
	   * Specified as any of the following bitwise ORed together:
	   *      0x0001  Trial (user less than 60days)
	   *      0x0002  Unknown bit 2
	   *      0x0004  AOL Main Service user
	   *      0x0008  Unknown bit 4
	   *      0x0010  Free (AIM) user 
	   *      0x0020  Away
	   *
	   * In some odd cases, we can end up with more
	   * than one of these.  We only want the first,
	   * as the others may not be something we want.
	   *
	   */
	case 0x0001:
	  if (tlv1) /* use only the first */
	    break;
	  outinfo->class = aimutil_get16(&buf[i+4]);
	  tlv1++;
	  break;
	  
	  /*
	   * Type = 0x0002: Member-Since date. 
	   *
	   * The time/date that the user originally
	   * registered for the service, stored in 
	   * time_t format
	   */
	case 0x0002: 
	  outinfo->membersince = aimutil_get32(&buf[i+4]);
	  break;
	  
	  /*
	   * Type = 0x0003: On-Since date.
	   *
	   * The time/date that the user started 
	   * their current session, stored in time_t
	   * format.
	   */
	case 0x0003:
	  outinfo->onlinesince = aimutil_get32(&buf[i+4]);
	  break;

	  /*
	   * Type = 0x0004: Idle time.
	   *
	   * Number of seconds since the user
	   * actively used the service.
	   */
	case 0x0004:
	  outinfo->idletime = aimutil_get16(&buf[i+4]);
	  break;

	  /*
	   * Type = 0x000d
	   *
	   * Capability information.  Not real sure of
	   * actual decoding.  See comment on aim_bos_setprofile()
	   * in aim_misc.c about the capability block, its the same.
	   *
	   * Ignore.
	   *
	   */
	case 0x000d:
	  {
	    int z,y; 
	    int len;
	    len = aimutil_get16(buf+i+2);
	    if (!len)
	      break;

	    for (z = 0; z < len; z+=0x10) {
	      for(y=0; y < 6; y++) {
		if (memcmp(&aim_caps[y], buf+i+4+z, 0x10) == 0) {
		  switch(y) {
		  case 0: outinfo->capabilities |= AIM_CAPS_BUDDYICON; break;
		  case 1: outinfo->capabilities |= AIM_CAPS_VOICE; break;
		  case 2: outinfo->capabilities |= AIM_CAPS_IMIMAGE; break;
		  case 3: outinfo->capabilities |= AIM_CAPS_CHAT; break;
		  case 4: outinfo->capabilities |= AIM_CAPS_GETFILE; break;
		  case 5: outinfo->capabilities |= AIM_CAPS_SENDFILE; break;
		  default: outinfo->capabilities |= 0xff00; break;
		  }
		}
	      }
	    }
	  }
	  break;

	  /*
	   * Type = 0x000e
	   *
	   * Unknown.  Always of zero length, and always only
	   * on AOL users.
	   *
	   * Ignore.
	   *
	   */
	case 0x000e:
	  break;
	  
	  /*
	   * Type = 0x000f: Session Length. (AIM)
	   * Type = 0x0010: Session Length. (AOL)
	   *
	   * The duration, in seconds, of the user's
	   * current session.
	   *
	   * Which TLV type this comes in depends
	   * on the service the user is using (AIM or AOL).
	   *
	   */
	case 0x000f:
	case 0x0010:
	  outinfo->sessionlen = aimutil_get32(&buf[i+4]);
	  break;

	  /*
	   * Reaching here indicates that either AOL has
	   * added yet another TLV for us to deal with, 
	   * or the parsing has gone Terribly Wrong.
	   *
	   * Either way, inform the owner and attempt
	   * recovery.
	   *
	   */
	default:
	  {
	    int len,z = 0, y = 0, x = 0;
	    char tmpstr[80];
	    printf("faim: userinfo: **warning: unexpected TLV:\n");
	    printf("faim: userinfo:   sn    =%s\n", outinfo->sn);
	    printf("faim: userinfo:   curtlv=0x%04x\n", curtlv);
	    printf("faim: userinfo:   type  =0x%04x\n",aimutil_get16(&buf[i]));
	    printf("faim: userinfo:   length=0x%04x\n", len = aimutil_get16(&buf[i+2]));
	    printf("faim: userinfo:   data: \n");
	    while (z<len)
	      {
		x = sprintf(tmpstr, "faim: userinfo:      ");
		for (y = 0; y < 8; y++)
		  {
		    if (z<len)
		      {
			sprintf(tmpstr+x, "%02x ", buf[i+4+z]);
			z++;
			x += 3;
		      }
		    else
		      break;
		  }
		printf("%s\n", tmpstr);
	      }
	  }
	  break;
	}  
      /*
       * No matter what, TLV triplets should always look like this:
       *
       *   u_short type;
       *   u_short length;
       *   u_char  data[length];
       *
       */
      i += (2 + 2 + aimutil_get16(&buf[i+2]));
      
      curtlv++;
    }
  
  return i;
}

/*
 * Oncoming Buddy notifications contain a subset of the
 * user information structure.  Its close enough to run
 * through aim_extractuserinfo() however.
 *
 */
int aim_parse_oncoming_middle(struct aim_session_t *sess,
			      struct command_rx_struct *command)
{
  struct aim_userinfo_s userinfo;
  u_int i = 0;
  rxcallback_t userfunc=NULL;

  i = 10;
  i += aim_extractuserinfo(command->data+i, &userinfo);

  userfunc = aim_callhandler(command->conn, AIM_CB_FAM_BUD, AIM_CB_BUD_ONCOMING);
  if (userfunc)
    i = userfunc(sess, command, &userinfo);

  return 1;
}

/*
 * Offgoing Buddy notifications contain no useful
 * information other than the name it applies to.
 *
 */
int aim_parse_offgoing_middle(struct aim_session_t *sess,
			      struct command_rx_struct *command)
{
  char sn[MAXSNLEN+1];
  u_int i = 0;
  rxcallback_t userfunc=NULL;

  strncpy(sn, command->data+11, (int)command->data[10]);
  sn[(int)command->data[10]] = '\0';

  userfunc = aim_callhandler(command->conn, AIM_CB_FAM_BUD, AIM_CB_BUD_OFFGOING);
  if (userfunc)
    i = userfunc(sess, command, sn);

  return 1;
}

/*
 * This parses the user info stuff out all nice and pretty then calls 
 * the higher-level callback (in the user app).
 *
 */
int aim_parse_userinfo_middle(struct aim_session_t *sess,
			      struct command_rx_struct *command)
{
  struct aim_userinfo_s userinfo;
  char *text_encoding = NULL;
  char *text = NULL;
  u_int i = 0;
  rxcallback_t userfunc=NULL;
  struct aim_tlvlist_t *tlvlist;
  struct aim_snac_t *origsnac = NULL;
  u_long snacid;
  struct aim_priv_inforeq *inforeq;
  
  snacid = aimutil_get32(&command->data[6]);
  origsnac = aim_remsnac(sess, snacid);

  if (!origsnac || !origsnac->data) {
    printf("faim: parse_userinfo_middle: major problem: no snac stored!\n");
    return 1;
  }

  inforeq = (struct aim_priv_inforeq *)origsnac->data;

  switch (inforeq->infotype) {
  case AIM_GETINFO_GENERALINFO:
  case AIM_GETINFO_AWAYMESSAGE:
    i = 10;

    /*
     * extractuserinfo will give us the basic metaTLV information
     */
    i += aim_extractuserinfo(command->data+i, &userinfo);
  
    /*
     * However, in this command, there's usually more TLVs following...
     */ 
    tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i);

    /* 
     * Depending on what informational text was requested, different
     * TLVs will appear here.
     *
     * Profile will be 1 and 2, away message will be 3 and 4.
     */
    if (aim_gettlv(tlvlist, 0x0001, 1)) {
      text_encoding = aim_gettlv_str(tlvlist, 0x0001, 1);
      text = aim_gettlv_str(tlvlist, 0x0002, 1);
    } else if (aim_gettlv(tlvlist, 0x0003, 1)) {
      text_encoding = aim_gettlv_str(tlvlist, 0x0003, 1);
      text = aim_gettlv_str(tlvlist, 0x0004, 1);
    }

    userfunc = aim_callhandler(command->conn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO);
    if (userfunc) {
      i = userfunc(sess,
		   command, 
		   &userinfo, 
		   text_encoding, 
		   text,
		   inforeq->infotype); 
    }
  
    free(text_encoding);
    free(text);
    aim_freetlvchain(&tlvlist);
    break;
  default:
    printf("faim: parse_userinfo_middle: unknown infotype in request! (0x%04x)\n", inforeq->infotype);
    break;
  }

  if (origsnac) {
    if (origsnac->data)
      free(origsnac->data);
    free(origsnac);
  }

  return 1;
}

/*
 * Inverse of aim_extractuserinfo()
 */
int aim_putuserinfo(u_char *buf, int buflen, struct aim_userinfo_s *info)
{
  int i = 0;
  struct aim_tlvlist_t *tlvlist = NULL;

  if (!buf || !info)
    return 0;

  i += aimutil_put8(buf+i, strlen(info->sn));
  i += aimutil_putstr(buf+i, info->sn, strlen(info->sn));

  i += aimutil_put16(buf+i, info->warnlevel);

  /* XXX: we only put down five */
  i += aimutil_put16(buf+i, 5);
  aim_addtlvtochain16(&tlvlist, 0x0001, info->class);
  aim_addtlvtochain32(&tlvlist, 0x0002, info->membersince);
  aim_addtlvtochain32(&tlvlist, 0x0003, info->onlinesince);
  aim_addtlvtochain16(&tlvlist, 0x0004, info->idletime);
  /* XXX: should put caps here */
  aim_addtlvtochain32(&tlvlist, (info->class)&AIM_CLASS_AOL?0x0010:0x000f, info->sessionlen);
  
  i += aim_writetlvchain(buf+i, buflen-i, &tlvlist);
  aim_freetlvchain(&tlvlist);
  
  return i;
}

int aim_sendbuddyoncoming(struct aim_session_t *sess, struct aim_conn_t *conn, struct aim_userinfo_s *info)
{
  struct command_tx_struct *tx;
  int i = 0;

  if (!sess || !conn || !info)
    return 0;

  if (!(tx = aim_tx_new(0x0002, conn, 1152)))
    return -1;

  tx->lock = 1;

  i += aimutil_put16(tx->data+i, 0x0003);
  i += aimutil_put16(tx->data+i, 0x000b);
  i += aimutil_put16(tx->data+i, 0x0000);
  i += aimutil_put16(tx->data+i, 0x0000);
  i += aimutil_put16(tx->data+i, 0x0000);

  i += aim_putuserinfo(tx->data+i, tx->commandlen-i, info);

  tx->commandlen = i;
  tx->lock = 0;
  aim_tx_enqueue(sess, tx);

  return 0;
}

int aim_sendbuddyoffgoing(struct aim_session_t *sess, struct aim_conn_t *conn, char *sn)
{
  struct command_tx_struct *tx;
  int i = 0;

  if (!sess || !conn || !sn)
    return 0;

  if (!(tx = aim_tx_new(0x0002, conn, 10+1+strlen(sn))))
    return -1;

  tx->lock = 1;

  i += aimutil_put16(tx->data+i, 0x0003);
  i += aimutil_put16(tx->data+i, 0x000c);
  i += aimutil_put16(tx->data+i, 0x0000);
  i += aimutil_put16(tx->data+i, 0x0000);
  i += aimutil_put16(tx->data+i, 0x0000);

  i += aimutil_put8(tx->data+i, strlen(sn));
  i += aimutil_putstr(tx->data+i, sn, strlen(sn));
  
  tx->lock = 0;
  aim_tx_enqueue(sess, tx);

  return 0;
}