diff plugins/icq/tcp.c @ 1152:201ec77f3a60

[gaim-migrate @ 1162] icq. whoop de doo committer: Tailor Script <tailor@pidgin.im>
author Eric Warmenhoven <eric@warmenhoven.org>
date Tue, 28 Nov 2000 02:22:42 +0000
parents
children 0a766047b4fd
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/icq/tcp.c	Tue Nov 28 02:22:42 2000 +0000
@@ -0,0 +1,639 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+$Id: tcp.c 1162 2000-11-28 02:22:42Z warmenhoven $
+$Log$
+Revision 1.1  2000/11/28 02:22:42  warmenhoven
+icq. whoop de doo
+
+Revision 1.36  2000/07/09 22:19:35  bills
+added new *Close functions, use *Close functions instead of *Delete
+where correct, and misc cleanup
+
+Revision 1.35  2000/06/15 01:52:16  bills
+added Cancel and Refuse functions for chat and file reqs, changed packet
+sending code to use new icq_TCPLinkSendSeq function to elimitane duplicate
+code, removed *Seq functions, renamed chat req functions
+
+Revision 1.34  2000/05/04 15:57:20  bills
+Reworked file transfer notification, small bugfixes, and cleanups.
+
+Revision 1.33  2000/04/10 18:11:45  denis
+ANSI cleanups.
+
+Revision 1.32  2000/04/10 16:36:04  denis
+Some more Win32 compatibility from Guillaume Rosanis <grs@mail.com>
+
+Revision 1.31  2000/04/06 16:38:04  denis
+icq_*Send*Seq() functions with specified sequence number were added.
+
+Revision 1.30  2000/04/05 14:37:02  denis
+Applied patch from "Guillaume R." <grs@mail.com> for basic Win32
+compatibility.
+
+Revision 1.29  2000/02/15 04:02:41  bills
+warning cleanup
+
+Revision 1.28  2000/02/15 03:58:20  bills
+use new icq_ChatRusConv_n function in icq_TCPSendChatData,
+new icq_TCPSendChatData_n function
+
+Revision 1.27  2000/02/07 02:40:23  bills
+new code for SOCKS connections, more cyrillic translations
+
+Revision 1.26  2000/01/20 19:59:15  bills
+first implementation of sending file requests
+
+Revision 1.25  2000/01/16 21:28:24  bills
+renamed icq_TCPAcceptFileReq to icq_AcceptFileRequest, moved file request
+functions to new file session code
+
+Revision 1.24  2000/01/16 03:59:10  bills
+reworked list code so list_nodes don't need to be inside item structures,
+removed strlist code and replaced with generic list calls
+
+Revision 1.23  1999/12/27 16:10:04  bills
+fixed buy in icq_TCPAcceptFileReq, added icq_TCPFileSetSpeed
+
+Revision 1.22  1999/12/21 00:29:59  bills
+moved _process_packet logic into tcplink::icq_TCPLinkProcessReceived,
+removed unnecessary icq_TCPSendFile??Packet functions
+
+Revision 1.21  1999/12/14 03:31:48  bills
+fixed double delete bug in _handle_ready_sockets, added code to implement
+connect timeout
+
+Revision 1.20  1999/11/30 09:44:31  bills
+added file session logic
+
+Revision 1.19  1999/09/29 20:07:12  bills
+cleanups, moved connect logic from _handle_ready_sockets to
+icq_TCPLinkOnConnect, tcp_link->icq_TCPLink
+
+Revision 1.18  1999/09/29 17:08:48  denis
+Cleanups.
+
+Revision 1.17  1999/07/18 20:19:56  bills
+added better log messages
+
+Revision 1.16  1999/07/16 15:45:56  denis
+Cleaned up.
+
+Revision 1.15  1999/07/16 12:14:13  denis
+tcp_packet* functions renamed to icq_Packet*
+Cleaned up.
+
+Revision 1.14  1999/07/12 15:13:34  cproch
+- added definition of ICQLINK to hold session-specific global variabled
+  applications which have more than one connection are now possible
+- changed nearly every function defintion to support ICQLINK parameter
+
+Revision 1.13  1999/07/03 06:33:49  lord
+. byte order conversion macros added
+. some compilation warnings removed
+
+Revision 1.12  1999/06/30 13:52:22  bills
+implemented non-blocking connects
+
+Revision 1.11  1999/05/03 21:41:26  bills
+initial file xfer support added- untested
+
+Revision 1.10  1999/04/29 09:35:41  denis
+Cleanups, warning removed
+
+Revision 1.9  1999/04/17 19:30:50  bills
+_major_ restructuring.  all tcp sockets (including listening sockets) are
+kept in global linked list, icq_TCPLinks. accept and listen functions
+moved to tcplink.c.  changed return values of Send* functions to DWORD.
+
+Revision 1.8  1999/04/14 14:57:05  denis
+Cleanups for "strict" compiling (-ansi -pedantic)
+Parameter port added to function icq_TCPCreateListeningSocket()
+
+*/
+
+/*
+   Peer-to-peer ICQ protocol implementation
+
+   Uses version 2 of the ICQ protocol
+
+   Thanks to Douglas F. McLaughlin and many others for
+   packet details (see tcp02.txt)
+
+*/
+
+#include <stdlib.h>
+
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+
+#include <fcntl.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include <sys/types.h>
+
+#ifdef _WIN32
+#include <winsock.h>
+#else
+#include <sys/socket.h>
+#endif
+
+#include <sys/stat.h>
+
+#ifndef _WIN32
+#include <sys/time.h>
+#endif
+
+#include "icqtypes.h"
+#include "icqlib.h"
+
+#include "tcp.h"
+#include "stdpackets.h"
+#include "list.h"
+#include "tcplink.h"
+#include "chatsession.h"
+#include "filesession.h"
+
+/**
+ Initializes structures necessary for TCP use.  Not required by user
+ programs.
+
+ \return true on error
+*/
+ 
+int icq_TCPInit(ICQLINK *link)
+{
+  icq_TCPLink *plink;
+
+  /* allocate lists */
+  link->icq_TCPLinks=list_new();
+  link->icq_ChatSessions=list_new();
+  link->icq_FileSessions=list_new();
+
+  /* only the main listening socket gets created upon initialization -
+   * the other two are created when necessary */
+  plink=icq_TCPLinkNew( link );
+  icq_TCPLinkListen(plink);
+  link->icq_TCPSrvPort=ntohs(plink->socket_address.sin_port);
+
+  /* reset tcp sequence number */
+  link->icq_TCPSequence=0xfffffffe;
+
+  return 0;
+}
+
+void icq_TCPDone(ICQLINK *link)
+{
+  /* close and deallocate all tcp links, this will also close any attached 
+   * file or chat sessions */
+  list_delete(link->icq_TCPLinks, icq_TCPLinkDelete);
+  list_delete(link->icq_ChatSessions, icq_ChatSessionDelete);
+  list_delete(link->icq_FileSessions, icq_FileSessionDelete);
+}
+
+/* helper function for icq_TCPMain */
+int _generate_fds(void *p, va_list data)
+{
+  icq_TCPLink *plink=(icq_TCPLink *)p;
+  ICQLINK *icqlink = plink->icqlink;
+
+  (void)data;
+
+  if(plink->socket>-1)
+  {
+    int socket=plink->socket;
+
+    FD_SET(socket, &icqlink->TCP_readfds);
+
+    /* we only care about writing if socket is trying to connect */
+    if(plink->mode & TCP_LINK_MODE_CONNECTING)
+    {
+      if(plink->mode & (TCP_LINK_SOCKS_AUTHORIZATION | TCP_LINK_SOCKS_NOAUTHSTATUS | TCP_LINK_SOCKS_AUTHSTATUS | TCP_LINK_SOCKS_CONNSTATUS))
+        FD_SET(socket, &icqlink->TCP_readfds);
+      else
+        FD_SET(socket, &icqlink->TCP_writefds);
+    }
+
+    if(socket+1>icqlink->TCP_maxfd)
+      icqlink->TCP_maxfd=socket+1;
+  }
+
+  return 0; /* traverse the entire list */
+}
+
+/* helper function for icq_TCPMain */
+int _handle_ready_sockets(void *p, va_list data)
+{
+  icq_TCPLink *plink=(icq_TCPLink *)p;
+  ICQLINK *icqlink = plink->icqlink;
+  int socket=plink->socket;
+
+  (void)data;
+
+  /* handle connecting sockets */
+  if (plink->mode & TCP_LINK_MODE_CONNECTING)
+  {
+    if(socket>-1 && (FD_ISSET(socket, &icqlink->TCP_writefds) || FD_ISSET(socket, &icqlink->TCP_readfds)))
+    {
+      icq_TCPLinkOnConnect(plink);
+      return 0; 
+    }
+
+    if((time(0L) - plink->connect_time) > TCP_LINK_CONNECT_TIMEOUT)
+    {
+      icq_TCPLinkClose(plink);
+      return 0;
+    }
+  }
+
+  /* handle ready for read sockets- either a connection is waiting on *
+   * the listen sockets or data is ready to be read */
+  if(socket>-1 && FD_ISSET(socket, &icqlink->TCP_readfds))
+  {
+    if(plink->mode & TCP_LINK_MODE_LISTEN)
+      (void)icq_TCPLinkAccept(plink);
+    else {
+
+      int result=icq_TCPLinkOnDataReceived(plink);
+
+      /* close the link if there was a receive error or if *
+       * the remote end has closed the connection */
+      if (result < 1) 
+        icq_TCPLinkClose(plink);
+
+    }
+  }
+
+  return 0; /* traverse the entire list */
+}
+
+/* helper function for icq_TCPMain */
+int _process_links(void *p, va_list data)
+{
+  icq_TCPLink *plink=(icq_TCPLink *)p;
+
+  (void)data;
+
+  /* receive any packets watiting on the link */
+  icq_TCPLinkProcessReceived(plink);
+
+  /* if this a currently sending file link, send data! */
+  if(plink->type==TCP_LINK_FILE) {
+    icq_FileSession *psession=plink->session;
+    if(psession && psession->status==FILE_STATUS_SENDING)
+      icq_FileSessionSendData(psession);
+  }
+
+  return 0; /* traverse entire list */
+}
+
+void icq_TCPMain(ICQLINK *link)
+{
+  struct timeval tv;
+
+  tv.tv_sec = 0;
+  tv.tv_usec = 0;
+
+  link->TCP_maxfd = 0;
+  FD_ZERO(&link->TCP_readfds);
+  FD_ZERO(&link->TCP_writefds);
+
+  /* generate the fd sets for all open tcp links */
+  (void)list_traverse(link->icq_TCPLinks, _generate_fds);
+
+  /* determine which sockets require maintenance */
+  select(link->TCP_maxfd, &link->TCP_readfds, &link->TCP_writefds, 0, &tv);
+
+  /* call icq_TCPLinkOnDataReceived for any sockets with ready data,
+   * send all packets on send queue if socket has connected, and
+   * accept() from any listening sockets with pending connections */ 
+  (void)list_traverse(link->icq_TCPLinks, _handle_ready_sockets, 0, 0);
+
+  /* process all packets waiting for each TCPLink */
+  (void)list_traverse(link->icq_TCPLinks, _process_links, 0, 0);
+}
+
+icq_TCPLink *icq_TCPCheckLink(ICQLINK *link, DWORD uin, int type)
+{
+  icq_TCPLink *plink=icq_FindTCPLink(link, uin, type);
+
+  if(!plink)
+  {
+    plink=icq_TCPLinkNew( link );
+    if(type==TCP_LINK_MESSAGE)
+      icq_TCPLinkConnect(plink, uin, 0);
+  }
+
+  return plink;
+
+}
+
+DWORD icq_TCPSendMessage(ICQLINK *link, DWORD uin, const char *message)
+{
+  icq_TCPLink *plink;
+  icq_Packet *p;
+  DWORD sequence;
+  char data[512] ;
+  
+  strncpy(data,message,512) ;
+  icq_RusConv("kw", data) ;
+
+  plink=icq_TCPCheckLink(link, uin, TCP_LINK_MESSAGE);
+
+  /* create and send the message packet */
+  p=icq_TCPCreateMessagePacket(plink, (unsigned char *)data);
+  sequence=icq_TCPLinkSendSeq(plink, p, 0);
+
+#ifdef TCP_PACKET_TRACE
+  printf("message packet sent to uin %lu { sequence=%lx }\n", uin, p->id);
+#endif
+
+  return sequence;
+}
+
+DWORD icq_TCPSendURL(ICQLINK *link, DWORD uin, const char *message, const char *url)
+{
+  icq_TCPLink *plink;
+  icq_Packet *p;
+  DWORD sequence;
+  char data[512];
+
+  plink=icq_TCPCheckLink(link, uin, TCP_LINK_MESSAGE);
+  
+  strncpy(data, message, 512);
+  data[511] = '\0';
+  icq_RusConv("kw", data);
+
+  /* create and send the url packet */
+  p=icq_TCPCreateURLPacket(plink, data, url);
+  sequence=icq_TCPLinkSendSeq(plink, p, 0);
+
+#ifdef TCP_PACKET_TRACE
+  printf("url packet queued for uin %lu { sequence=%lx }\n", uin, p->id);
+#endif
+
+  return sequence;
+}
+
+DWORD icq_SendChatRequest(ICQLINK *link, DWORD uin, const char *message)
+{
+  icq_TCPLink *plink;
+  icq_Packet *p;
+  DWORD sequence;
+  char data[512];
+
+  plink=icq_TCPCheckLink(link, uin, TCP_LINK_MESSAGE);
+  
+  strncpy(data, message, 512);
+  data[511] = '\0';
+  icq_RusConv("kw", data);
+
+  /* create and send the url packet */
+  p=icq_TCPCreateChatReqPacket(plink, (unsigned char *)data);
+  sequence=icq_TCPLinkSendSeq(plink, p, 0);
+
+#ifdef TCP_PACKET_TRACE
+  printf("chat req packet sent to uin %lu { sequence=%lx }\n", uin, p->id);
+#endif
+
+  return sequence;
+}
+
+unsigned long icq_SendFileRequest(ICQLINK *link, unsigned long uin,
+  const char *message, char **files)
+{
+  icq_TCPLink *plink;
+  icq_FileSession *pfile;
+  icq_Packet *p;
+  unsigned long sequence;
+  char filename[64];
+  char data[512];
+
+  plink=icq_TCPCheckLink(link, uin, TCP_LINK_MESSAGE);
+
+  /* create the file session, this will be linked to the incoming icq_TCPLink
+   * in icq_HandleFileAck */ 
+  pfile=icq_FileSessionNew(link);
+  pfile->remote_uin=uin;
+  pfile->files=files;
+  pfile->direction=FILE_STATUS_SENDING;
+
+  /* count the number and size of the files */
+  pfile->total_files=0;
+  while(*files) {
+    struct stat file_status;
+
+    if(stat(*files, &file_status)==0) {
+      pfile->total_files++;
+      pfile->total_bytes+=file_status.st_size;
+    }
+    files++;
+  }
+
+  strncpy(filename, *(pfile->files), 64);
+
+  strncpy(data, message, 512);
+  data[511] = '\0';
+  icq_RusConv("kw", data);  
+
+  /* create and send the file req packet */
+  p=icq_TCPCreateFileReqPacket(plink, (char *)data, filename, 
+    pfile->total_bytes);
+  sequence=icq_TCPLinkSendSeq(plink, p, 0);
+  pfile->id=sequence;
+
+#ifdef TCP_PACKET_TRACE
+  printf("file req packet sent to uin %lu { sequence=%lx }\n", uin, p->id);
+#endif
+
+  return sequence;
+}            
+
+void icq_AcceptChatRequest(ICQLINK *link, DWORD uin, unsigned long sequence)
+{
+  icq_TCPLink *pmessage, *plisten;
+  icq_ChatSession *pchat;
+  icq_Packet *p;
+
+  pmessage=icq_TCPCheckLink(link, uin, TCP_LINK_MESSAGE);
+
+  /* create the chat listening socket if necessary */
+  if(!(plisten=icq_FindTCPLink(link, 0, TCP_LINK_CHAT)))
+  {
+    plisten=icq_TCPLinkNew( link );
+    plisten->type=TCP_LINK_CHAT;
+    icq_TCPLinkListen(plisten);
+  }
+
+  /* create the chat session, this will be linked to the incoming icq_TCPLink
+   * in TCPProcessHello */ 
+  pchat=icq_ChatSessionNew(link);
+  pchat->id=sequence;
+  pchat->remote_uin=uin;
+
+  /* create and send the ack packet */
+  p=icq_TCPCreateChatReqAck(pmessage,
+    ntohs(plisten->socket_address.sin_port));
+  (void)icq_TCPLinkSendSeq(pmessage, p, sequence);
+
+#ifdef TCP_PACKET_TRACE
+  printf("chat req ack sent to uin %lu { sequence=%lx }\n", uin, sequence);
+#endif
+}
+
+void icq_TCPSendChatData(ICQLINK *link, DWORD uin, const char *data)
+{
+  icq_TCPLink *plink=icq_FindTCPLink(link, uin, TCP_LINK_CHAT);
+  char data1[512];
+  int data1_len;
+
+  if(!plink)
+    return;  
+
+  strncpy(data1,data,512) ;
+  data1[511] = '\0';
+  data1_len = strlen(data);
+  icq_ChatRusConv_n("kw", data1, data1_len);
+
+  send(plink->socket, data1, data1_len, 0);
+
+}
+
+void icq_TCPSendChatData_n(ICQLINK *link, DWORD uin, const char *data, int len)
+{
+  icq_TCPLink *plink=icq_FindTCPLink(link, uin, TCP_LINK_CHAT);
+  char *data1;
+
+  if(!plink)
+    return;
+
+  data1 = (char *)malloc(len);
+  memcpy(data1, data, len);
+  icq_ChatRusConv_n("kw", data1, len);  
+
+  send(plink->socket, data1, len, 0);
+
+}
+
+void icq_TCPCloseChat(ICQLINK *link, unsigned long uin)
+{
+  icq_TCPLink *plink=icq_FindTCPLink(link, uin, TCP_LINK_CHAT);
+
+  if(plink)
+    icq_TCPLinkClose(plink);
+
+}
+
+icq_FileSession *icq_AcceptFileRequest(ICQLINK *link, DWORD uin,
+  unsigned long sequence)
+{
+  icq_TCPLink *pmessage, *plisten;
+  icq_FileSession *pfile;
+  icq_Packet *p;
+
+  pmessage=icq_TCPCheckLink(link, uin, TCP_LINK_MESSAGE);
+
+  /* create the file listening socket if necessary */
+  if(!(plisten=icq_FindTCPLink(link, 0, TCP_LINK_FILE)))
+  {
+    plisten=icq_TCPLinkNew( link );
+    plisten->type=TCP_LINK_FILE;
+    icq_TCPLinkListen(plisten);
+  }
+
+  /* create the file session, this will be linked to the incoming icq_TCPLink
+   * in TCPProcessHello */ 
+  pfile=icq_FileSessionNew(link);
+  pfile->id=sequence;
+  pfile->remote_uin=uin;
+  pfile->direction=FILE_STATUS_RECEIVING;
+  icq_FileSessionSetStatus(pfile, FILE_STATUS_LISTENING);
+
+  /* create and send the ack packet */
+  p=icq_TCPCreateFileReqAck(pmessage, 
+    ntohs(plisten->socket_address.sin_port));
+  (void)icq_TCPLinkSendSeq(pmessage, p, sequence);
+
+#ifdef TCP_PACKET_TRACE
+  printf("file req ack sent to uin %lu { sequence=%lx }\n", uin, sequence);
+#endif
+
+  return pfile;
+
+}
+
+void icq_RefuseFileRequest(ICQLINK *link, DWORD uin, 
+  unsigned long sequence, const char *reason)
+{
+  icq_TCPLink *pmessage=icq_TCPCheckLink(link, uin, TCP_LINK_MESSAGE);
+  icq_Packet *p;
+
+  /* create and send the refuse packet */
+  p=icq_TCPCreateFileReqRefuse(pmessage,
+    ntohs(pmessage->socket_address.sin_port), reason);
+  (void)icq_TCPLinkSendSeq(pmessage, p, sequence);
+
+#ifdef TCP_PACKET_TRACE
+  printf("file req refuse sent to uin %lu { sequence=%lx, reason=\"%s\" }\n",
+    uin, sequence, reason);
+#endif
+
+}
+
+void icq_CancelFileRequest(ICQLINK *link, DWORD uin, unsigned long sequence)
+{
+  icq_TCPLink *pmessage=icq_TCPCheckLink(link, uin, TCP_LINK_MESSAGE);
+  icq_FileSession *psession=icq_FindFileSession(link, uin, sequence);
+  icq_Packet *p;
+
+  if (psession)
+    icq_FileSessionClose(psession);
+
+  /* create and send the cancel packet */
+  p=icq_TCPCreateFileReqCancel(pmessage,
+    ntohs(pmessage->socket_address.sin_port));
+  (void)icq_TCPLinkSendSeq(pmessage, p, sequence);
+#ifdef TCP_PACKET_TRACE
+  printf("file req cancel sent to uin %lu { sequence=%lx }\n", uin, sequence);
+#endif
+
+}
+
+void icq_RefuseChatRequest(ICQLINK *link, DWORD uin, 
+  unsigned long sequence, const char *reason)
+{
+  icq_TCPLink *pmessage=icq_TCPCheckLink(link, uin, TCP_LINK_MESSAGE);
+  icq_Packet *p;
+
+  /* create and send the refuse packet */
+  p=icq_TCPCreateChatReqRefuse(pmessage,
+    ntohs(pmessage->socket_address.sin_port), reason);
+  (void)icq_TCPLinkSendSeq(pmessage, p, sequence);
+
+#ifdef TCP_PACKET_TRACE
+  printf("chat req refuse sent to uin %lu { sequence=%lx, reason=\"%s\" }\n",
+    uin, sequence, reason);
+#endif
+
+}
+
+void icq_CancelChatRequest(ICQLINK *link, DWORD uin, unsigned long sequence)
+{
+  icq_TCPLink *pmessage=icq_TCPCheckLink(link, uin, TCP_LINK_MESSAGE);
+  icq_FileSession *psession=icq_FindFileSession(link, uin, sequence);
+  icq_Packet *p;
+
+  if (psession)
+    icq_FileSessionClose(psession);
+
+  /* create and send the cancel packet */
+  p=icq_TCPCreateChatReqCancel(pmessage,
+    ntohs(pmessage->socket_address.sin_port));
+  (void)icq_TCPLinkSendSeq(pmessage, p, sequence);
+
+#ifdef TCP_PACKET_TRACE
+  printf("chat req cancel sent to uin %lu { sequence=%lx }\n", uin, sequence);
+#endif
+
+}