view libfaim/aim_chat.c @ 312:3069be4c291e

[gaim-migrate @ 322] I don't know why I did this. I have homework due in 15 hours that I haven't started yet, and it's in a language I don't know and it's a project I don't understand. If my teacher knew about this, he would be pissed. He looks pissed all the time, even when he's not. When he smiles he looks devilish. Maybe I only think that because literally half the class flunked the midterm. I am not joking about that. More people got F's than A, B, and C combined. It's 2 am and the homework's due at 5 tomorrow so what do I do? Get chat to work. Wow. That's going to look good on my resume. "Why did you flunk this class?" "Because I was getting chat in Instant Messenger to work." Not that that's not something to be proud of, but I wonder which is more important to employers. The big battle, experience versus education. Just because you got good grades in college doesn't mean you're smarter than someone who flunked, it just means you put in the effort necessary to get a better grade and the other person didn't. Maybe the person who flunked was working on real honest-to-god actually *used* software, as opposed to some stupid tree that only gets used for a fringe branch of computer science that doesn't offer much more than a normal heap or binary search tree offers. Maybe the person was out there reverse-engineering protocols and allowing cross- platform communication to occur, creating interoperability and causing a greater demand not only for the product, but for the platform it runs on! Given the choices, who would you pick? Someone who was told how to code a tree and managed to get it to work, or someone who increases your userbase and marketability? Enough of my rant for a while. I've had waaaaay too much sugar (gummy candy is deadly). committer: Tailor Script <tailor@pidgin.im>
author Eric Warmenhoven <eric@warmenhoven.org>
date Fri, 02 Jun 2000 09:11:48 +0000
parents 0f14e6d8a51b
children 8cdc4ab449ec
line wrap: on
line source

/*
 * aim_chat.c
 *
 * Routines for the Chat service.  Nothing works (yet).
 *
 */

#include <faim/aim.h> 

char *aim_chat_getname(struct aim_conn_t *conn)
{
  if (!conn)
    return NULL;
  if (conn->type != AIM_CONN_TYPE_CHAT)
    return NULL;

  return (char *)conn->priv; /* yuck ! */
}

struct aim_conn_t *aim_chat_getconn(struct aim_session_t *sess, char *name)
{
  int i;

  for (i=0;i<AIM_CONN_MAX;i++)
    {
      if (sess->conns[i].type == AIM_CONN_TYPE_CHAT)
	{
	  if (sess->conns[i].priv)
	    if (!strcmp((char *)sess->conns[i].priv, name))
	      {
		return &sess->conns[i];
	      }
	}
    }
  return NULL;
}

int aim_chat_attachname(struct aim_conn_t *conn, char *roomname)
{
  if (!conn || !roomname)
    return -1;

  conn->priv = malloc(strlen(roomname)+1);
  strcpy(conn->priv, roomname);

  return 0;
}

u_long aim_chat_send_im(struct aim_session_t *sess,
			struct aim_conn_t *conn, 
			char *msg)
{   

  int curbyte,i;
  struct command_tx_struct *newpacket;

  if (!sess || !conn || !msg)
    return 0;
  
  if (!(newpacket = aim_tx_new(0x0002, conn, 1152)))
    return -1;

  newpacket->lock = 1; /* lock struct */

  curbyte  = 0;
  curbyte += aim_putsnac(newpacket->data+curbyte, 
			 0x000e, 0x0005, 0x0000, sess->snac_nextid);

  /* 
   * Generate a random message cookie 
   */
  for (i=0;i<8;i++)
    curbyte += aimutil_put8(newpacket->data+curbyte, (u_char) random());

  /*
   * metaTLV start.  -- i assume this is a metaTLV.  it could be the
   *                    channel ID though.
   */
  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003);

  /*
   * Type 1: Unknown.  Blank.
   */
  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
  
  /*
   * Type 6: Unknown.  Blank.
   */
  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0006);
  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);

  /*
   * Type 5: Message block.  Contains more TLVs.
   *
   * This could include other information... We just
   * put in a message TLV however.  
   * 
   */
  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
  curbyte += aimutil_put16(newpacket->data+curbyte, strlen(msg)+4);

  /*
   * SubTLV: Type 1: Message
   */
  curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x0001, strlen(msg), msg);
  
  newpacket->commandlen = curbyte;

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

  return (sess->snac_nextid++);
}

/*
 * Join a room of name roomname.  This is the first
 * step to joining an already created room.  It's 
 * basically a Service Request for family 0x000e, 
 * with a little added on to specify the exchange
 * and room name.
 *
 */
u_long aim_chat_join(struct aim_session_t *sess,
		     struct aim_conn_t *conn, 
		     u_short exchange,
		     const char *roomname)
{
  struct command_tx_struct *newpacket;
  int i;

  if (!sess || !conn || !roomname)
    return 0;
  
  if (!(newpacket = aim_tx_new(0x0002, conn, 10+9+strlen(roomname)+2)))
    return -1;

  newpacket->lock = 1;
  
  i = aim_putsnac(newpacket->data, 0x0001, 0x0004, 0x0000, sess->snac_nextid);

  i+= aimutil_put16(newpacket->data+i, 0x000e);

  /* 
   * this is techinally a TLV, but we can't use normal functions
   * because we need the extraneous nulls and other weird things.
   */
  i+= aimutil_put16(newpacket->data+i, 0x0001);
  i+= aimutil_put16(newpacket->data+i, 2+1+strlen(roomname)+2);
  i+= aimutil_put16(newpacket->data+i, exchange);
  i+= aimutil_put8(newpacket->data+i, strlen(roomname));
  memcpy(newpacket->data+i, roomname, strlen(roomname));
  i+= strlen(roomname);
  //i+= aimutil_putstr(newpacket->data+i, roomname, strlen(roomname));
  i+= aimutil_put16(newpacket->data+i, 0x0000);

  /*
   * Chat hack.
   *
   * XXX: A problem occurs here if we request a channel
   *      join but it fails....pendingjoin will be nonnull
   *      even though the channel is never going to get a
   *      redirect!
   *
   */
  sess->pendingjoin = (char *)malloc(strlen(roomname)+1);
  strcpy(sess->pendingjoin, roomname);

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

#if 0
  {
    struct aim_snac_t snac;
    
    snac.id = sess->snac_nextid;
    snac.family = 0x0001;
    snac.type = 0x0004;
    snac.flags = 0x0000;

    snac.data = malloc(strlen(roomname)+1);
    strcpy(snac.data, roomname);

    aim_newsnac(sess, &snac);
  }

#endif
  return (sess->snac_nextid++);
}

int aim_chat_readroominfo(u_char *buf, struct aim_chat_roominfo *outinfo)
{
  int namelen = 0;
  int i = 0;

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

  outinfo->exchange = aimutil_get16(buf+i);
  i += 2;

  namelen = aimutil_get8(buf+i);
  i += 1;

  outinfo->name = (char *)malloc(namelen+1);
  memcpy(outinfo->name, buf+i, namelen);
  outinfo->name[namelen] = '\0';
  i += namelen;

  outinfo->instance = aimutil_get16(buf+i);
  i += 2;
  
  return i;
};


/*
 * General room information.  Lots of stuff.
 *
 * Values I know are in here but I havent attached
 * them to any of the 'Unknown's:
 *	- Language (English)
 *
 */
int aim_chat_parse_infoupdate(struct aim_session_t *sess,
			      struct command_rx_struct *command)
{
  struct aim_userinfo_s *userinfo = NULL;
  rxcallback_t userfunc=NULL;	
  int ret = 1, i = 0;
  int usercount = 0;
  u_char detaillevel = 0;
  char *roomname = NULL;
  struct aim_chat_roominfo roominfo;
  u_short tlvcount = 0;
  struct aim_tlvlist_t *tlvlist;
  char *roomdesc = NULL;

  i = 10;
  i += aim_chat_readroominfo(command->data+i, &roominfo);
  
  detaillevel = aimutil_get8(command->data+i);
  i++;
  
  tlvcount = aimutil_get16(command->data+i);
  i += 2;

  /*
   * Everything else are TLVs.
   */ 
  tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i);
  
  /*
   * TLV type 0x006a is the room name in Human Readable Form.
   */
  if (aim_gettlv(tlvlist, 0x006a, 1))
      roomname = aim_gettlv_str(tlvlist, 0x006a, 1);

  /*
   * Type 0x006f: Number of occupants.
   */
  if (aim_gettlv(tlvlist, 0x006f, 1))
    {
      struct aim_tlv_t *tmptlv;
      tmptlv = aim_gettlv(tlvlist, 0x006f, 1);

      usercount = aimutil_get16(tmptlv->value);
    }

  /*
   * Type 0x0073:  Occupant list.
   */
  if (aim_gettlv(tlvlist, 0x0073, 1))
    {	
      int curoccupant = 0;
      struct aim_tlv_t *tmptlv;

      tmptlv = aim_gettlv(tlvlist, 0x0073, 1);

      /* Allocate enough userinfo structs for all occupants */
      userinfo = calloc(usercount, sizeof(struct aim_userinfo_s));

      i = 0;
      while (curoccupant < usercount)
	i += aim_extractuserinfo(tmptlv->value+i, &userinfo[curoccupant++]);
    }
  
  /* 
   * Type 0x00c9: Unknown.
   */
  if (aim_gettlv(tlvlist, 0x00c9, 1))
    ;
  
  /* 
   * Type 0x00ca: Creation date
   */
  if (aim_gettlv(tlvlist, 0x00ca, 1))
    ;

  /* 
   * Type 0x00d1: Maximum Message Length
   */
  if (aim_gettlv(tlvlist, 0x00d1, 1))
    ;

  /* 
   * Type 0x00d2: Unknown.
   */
  if (aim_gettlv(tlvlist, 0x00d2, 1))
    ;

  /* 
   * Type 0x00d3: Room Description
   */
  if (aim_gettlv(tlvlist, 0x00d3, 1))
    roomdesc = aim_gettlv_str(tlvlist, 0x00d3, 1);

  /* 
   * Type 0x00d5: Unknown.
   */
  if (aim_gettlv(tlvlist, 0x00d5, 1))
    ;


  userfunc = aim_callhandler(command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_ROOMINFOUPDATE);
  if (userfunc)
    {
      ret = userfunc(sess,
		     command, 
		     &roominfo,
		     roomname,
		     usercount,
		     userinfo,	
		     roomdesc);
    }
  free(roominfo.name);
  free(userinfo);
  free(roomname);
  free(roomdesc);
  aim_freetlvchain(&tlvlist);
 
  return ret;
}

int aim_chat_parse_joined(struct aim_session_t *sess,
			      struct command_rx_struct *command)
{
  struct aim_userinfo_s *userinfo = NULL;
  rxcallback_t userfunc=NULL;	
  int i = 10, curcount = 0, ret = 1;

  while (i < command->commandlen)
    {
      curcount++;
      userinfo = realloc(userinfo, curcount * sizeof(struct aim_userinfo_s));
      i += aim_extractuserinfo(command->data+i, &userinfo[curcount-1]);
    }

  userfunc = aim_callhandler(command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERJOIN);
  if (userfunc)
    {
      ret = userfunc(sess,
		     command, 
		     curcount,
		     userinfo);
    }

  free(userinfo);

  return ret;
}	      

int aim_chat_parse_leave(struct aim_session_t *sess,
			      struct command_rx_struct *command)
{

  struct aim_userinfo_s *userinfo = NULL;
  rxcallback_t userfunc=NULL;	
  int i = 10, curcount = 0, ret = 1;

  while (i < command->commandlen)
    {
      curcount++;
      userinfo = realloc(userinfo, curcount * sizeof(struct aim_userinfo_s));
      i += aim_extractuserinfo(command->data+i, &userinfo[curcount-1]);
    }

  userfunc = aim_callhandler(command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERLEAVE);
  if (userfunc)
    {
      ret = userfunc(sess,
		     command, 
		     curcount,
		     userinfo);
    }

  free(userinfo);

  return ret;
}	   

/*
 * We could probably include this in the normal ICBM parsing 
 * code as channel 0x0003, however, since only the start
 * would be the same, we might as well do it here.
 */
int aim_chat_parse_incoming(struct aim_session_t *sess,
			      struct command_rx_struct *command)
{
  struct aim_userinfo_s userinfo;
  rxcallback_t userfunc=NULL;	
  int ret = 1, i = 0, z = 0;
  u_char cookie[8];
  int channel;
  struct aim_tlvlist_t *outerlist;
  char *msg = NULL;

  memset(&userinfo, 0x00, sizeof(struct aim_userinfo_s));

  i = 10; /* skip snac */

  /*
   * ICBM Cookie.  Ignore it.
   */ 
  for (z=0; z<8; z++,i++)
    cookie[z] = command->data[i];

  /*
   * Channel ID
   *
   * Channels 1 and 2 are implemented in the normal ICBM
   * parser.
   *
   * We only do channel 3 here.
   *
   */
  channel = aimutil_get16(command->data+i);
  i += 2;

  if (channel != 0x0003)
    {
      printf("faim: chat_incoming: unknown channel! (0x%04x)\n", channel);
      return 1;
    }

  /*
   * Start parsing TLVs right away. 
   */
  outerlist = aim_readtlvchain(command->data+i, command->commandlen-i);
  
  /*
   * Type 0x0003: Source User Information
   */
  if (aim_gettlv(outerlist, 0x0003, 1))
    {
      struct aim_tlv_t *userinfotlv;
      
      userinfotlv = aim_gettlv(outerlist, 0x0003, 1);
      aim_extractuserinfo(userinfotlv->value, &userinfo);
    }

  /*
   * Type 0x0001: Unknown.
   */
  if (aim_gettlv(outerlist, 0x0001, 1))
    ;

  /*
   * Type 0x0005: Message Block.  Conains more TLVs.
   */
  if (aim_gettlv(outerlist, 0x0005, 1))
    {
      struct aim_tlvlist_t *innerlist;
      struct aim_tlv_t *msgblock;

      msgblock = aim_gettlv(outerlist, 0x0005, 1);
      innerlist = aim_readtlvchain(msgblock->value, msgblock->length);
      
      /* 
       * Type 0x0001: Message.
       */	
      if (aim_gettlv(innerlist, 0x0001, 1))
	  msg = aim_gettlv_str(innerlist, 0x0001, 1);

      aim_freetlvchain(&innerlist); 
    }

  userfunc = aim_callhandler(command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_INCOMINGMSG);
  if (userfunc)
    {
      ret = userfunc(sess,
		     command, 
		     &userinfo,
		     msg);
    }
  free(msg);
  aim_freetlvchain(&outerlist);

  return ret;
}	      

u_long aim_chat_clientready(struct aim_session_t *sess,
			    struct aim_conn_t *conn)
{
  struct command_tx_struct *newpacket;
  int i;

  if (!(newpacket = aim_tx_new(0x0002, conn, 0x20)))
    return -1;

  newpacket->lock = 1;

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

  i+= aimutil_put16(newpacket->data+i, 0x000e);
  i+= aimutil_put16(newpacket->data+i, 0x0001);

  i+= aimutil_put16(newpacket->data+i, 0x0004);
  i+= aimutil_put16(newpacket->data+i, 0x0001);

  i+= aimutil_put16(newpacket->data+i, 0x0001);
  i+= aimutil_put16(newpacket->data+i, 0x0003);

  i+= aimutil_put16(newpacket->data+i, 0x0004);
  i+= aimutil_put16(newpacket->data+i, 0x0686);

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

  return (sess->snac_nextid++);
}

int aim_chat_leaveroom(struct aim_session_t *sess, char *name)
{
  int i;

  for (i=0;i<AIM_CONN_MAX;i++)
    {
      if (sess->conns[i].type == AIM_CONN_TYPE_CHAT)
	{
	  if (sess->conns[i].priv)
	    if (!strcmp((char *)sess->conns[i].priv, name))
	      {
		aim_conn_close(&sess->conns[i]);
		return 0;
	      }
	}
    }
  return -1;
}

/*
 * conn must be a BOS connection!
 */
u_long aim_chat_invite(struct aim_session_t *sess,
		       struct aim_conn_t *conn,
		       char *sn,
		       char *msg,
		       u_short exchange,
		       char *roomname,
		       u_short instance)
{
  struct command_tx_struct *newpacket;
  int i,curbyte=0;

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

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

  newpacket->lock = 1;

  curbyte = aim_putsnac(newpacket->data, 0x0004, 0x0006, 0x0000, sess->snac_nextid);

  /*
   * Cookie
   */
  for (i=0;i<8;i++)
    curbyte += aimutil_put8(newpacket->data+curbyte, (u_char)rand());

  /*
   * Channel (2)
   */
  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);

  /*
   * Dest sn
   */
  curbyte += aimutil_put8(newpacket->data+curbyte, strlen(sn));
  curbyte += aimutil_putstr(newpacket->data+curbyte, sn, strlen(sn));

  /*
   * TLV t(0005)
   */
  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
  curbyte += aimutil_put16(newpacket->data+curbyte, 0x28+strlen(msg)+0x04+0x03+strlen(roomname)+0x02);
  
  /* 
   * Unknown info
   */
  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
  curbyte += aimutil_put16(newpacket->data+curbyte, 0x3131);
  curbyte += aimutil_put16(newpacket->data+curbyte, 0x3538);
  curbyte += aimutil_put16(newpacket->data+curbyte, 0x3446);
  curbyte += aimutil_put16(newpacket->data+curbyte, 0x4100);
  curbyte += aimutil_put16(newpacket->data+curbyte, 0x748f);
  curbyte += aimutil_put16(newpacket->data+curbyte, 0x2420);
  curbyte += aimutil_put16(newpacket->data+curbyte, 0x6287);
  curbyte += aimutil_put16(newpacket->data+curbyte, 0x11d1);
  curbyte += aimutil_put16(newpacket->data+curbyte, 0x8222);
  curbyte += aimutil_put16(newpacket->data+curbyte, 0x4445);
  curbyte += aimutil_put16(newpacket->data+curbyte, 0x5354);
  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
  
  /*
   * TLV t(000a) -- Unknown
   */
  curbyte += aimutil_put16(newpacket->data+curbyte, 0x000a);
  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
  
  /*
   * TLV t(000f) -- Unknown
   */
  curbyte += aimutil_put16(newpacket->data+curbyte, 0x000f);
  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
  
  /*
   * TLV t(000c) -- Invitation message
   */
  curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x000c, strlen(msg), msg);

  /*
   * TLV t(2711) -- Container for room information 
   */
  curbyte += aimutil_put16(newpacket->data+curbyte, 0x2711);
  curbyte += aimutil_put16(newpacket->data+curbyte, 3+strlen(roomname)+2);
  curbyte += aimutil_put16(newpacket->data+curbyte, exchange);
  curbyte += aimutil_put8(newpacket->data+curbyte, strlen(roomname));
  curbyte += aimutil_putstr(newpacket->data+curbyte, roomname, strlen(roomname));
  curbyte += aimutil_put16(newpacket->data+curbyte, instance);

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

  return (sess->snac_nextid++);
}