view src/protocols/icq/icqlib.c @ 6160:7416a43dc786

[gaim-migrate @ 6635] Hold it.. Nobody said anything about _three_ books. Oh that stinkin' wise man. He was so busy filling me full of his secret little workds and his phrases and his BALONEY that he never said anything about this... committer: Tailor Script <tailor@pidgin.im>
author Christian Hammond <chipx86@chipx86.com>
date Wed, 16 Jul 2003 22:43:27 +0000
parents 424a40f12a6c
children
line wrap: on
line source

/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */

/*
 * $Id: icqlib.c 2096 2001-07-31 01:00:39Z warmenhoven $
 *
 * Copyright (C) 1998-2001, Denis V. Dmitrienko <denis@null.net> and
 *                          Bill Soudan <soudan@kde.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include "icqlib.h"

#include <stdlib.h>

#ifdef _WIN32
#include <winsock.h>
#else
#include <netdb.h>
#include <sys/socket.h>
#endif

#include <sys/stat.h>

#include "util.h"
#include "icq.h"
#include "udp.h"
#include "tcp.h"
#include "queue.h"
#include "socketmanager.h"
#include "contacts.h"

int icq_Russian = FALSE;
BYTE icq_LogLevel = 0;

DWORD icq_SendMessage(icq_Link *icqlink, DWORD uin, const char *text, 
  BYTE thruSrv)
{
  if(thruSrv==ICQ_SEND_THRUSERVER)
    return icq_UDPSendMessage(icqlink, uin, text);
  else if(thruSrv==ICQ_SEND_DIRECT)
    return icq_TCPSendMessage(icqlink, uin, text);
  else if(thruSrv==ICQ_SEND_BESTWAY)
  {
    icq_ContactItem *pcontact=icq_ContactFind(icqlink, uin);
    if(pcontact)
    {
      if(pcontact->tcp_flag == 0x04)
        return icq_TCPSendMessage(icqlink, uin, text);
      else
        return icq_UDPSendMessage(icqlink, uin, text);
    }
    else
    {
      return icq_UDPSendMessage(icqlink, uin, text);
    }
  }
  return 0;
}

DWORD icq_SendURL(icq_Link *icqlink, DWORD uin, const char *url, 
  const char *descr, BYTE thruSrv)
{
  if(thruSrv==ICQ_SEND_THRUSERVER)
    return icq_UDPSendURL(icqlink, uin, url, descr);
  else if(thruSrv==ICQ_SEND_DIRECT)
    return icq_TCPSendURL(icqlink, uin, descr, url);
  else if(thruSrv==ICQ_SEND_BESTWAY)
  {
    icq_ContactItem *pcontact=icq_ContactFind(icqlink, uin);
    if(pcontact)
    {
      if(pcontact->tcp_flag == 0x04)
        return icq_TCPSendURL(icqlink, uin, descr, url);
      else
        return icq_UDPSendURL(icqlink, uin, url, descr);
    }
    else
    {
      return icq_UDPSendURL(icqlink, uin, url, descr);
    }
  }
  return 0;
}

static int icqlib_initialized = 0;

void icq_LibInit()
{
  srand(time(0L));

  /* initialize internal lists, if necessary */
  if (!icq_SocketList)
    icq_SocketList = icq_ListNew();

  if (!icq_TimeoutList)
  {
    icq_TimeoutList = icq_ListNew();
    icq_TimeoutList->compare_function =
      (icq_ListCompareFunc)icq_TimeoutCompare;
  }

  icqlib_initialized = 1;
}

icq_Link *icq_LinkNew(DWORD uin, const char *password, const char *nick,
                        unsigned char useTCP)
{
  icq_Link *icqlink = (icq_Link *)malloc(sizeof(icq_Link));

  icq_LinkInit(icqlink, uin, password, nick, useTCP);

  return icqlink;
}

void icq_LinkInit(icq_Link *icqlink, DWORD uin, const char *password, 
  const char *nick, unsigned char useTCP)
{
  icqlink->d = (icq_LinkPrivate *)malloc(sizeof(icq_LinkPrivate));

  if (!icqlib_initialized)
    icq_LibInit();

  /* Initialize all callbacks */
  icqlink->icq_Logged = 0L;
  icqlink->icq_Disconnected = 0L;
  icqlink->icq_RecvMessage = 0L;
  icqlink->icq_RecvURL = 0L;
  icqlink->icq_RecvContactList = 0L;
  icqlink->icq_RecvWebPager = 0L;
  icqlink->icq_RecvMailExpress = 0L;
  icqlink->icq_RecvChatReq = 0L;
  icqlink->icq_RecvFileReq = 0L;
  icqlink->icq_RecvAdded = 0L;
  icqlink->icq_RecvAuthReq = 0L;
  icqlink->icq_UserFound = 0L;
  icqlink->icq_SearchDone = 0L;
  icqlink->icq_UpdateSuccess = 0L;
  icqlink->icq_UpdateFailure = 0L;
  icqlink->icq_UserOnline = 0L;
  icqlink->icq_UserOffline = 0L;
  icqlink->icq_UserStatusUpdate = 0L;
  icqlink->icq_InfoReply = 0L;
  icqlink->icq_ExtInfoReply = 0L;
  icqlink->icq_WrongPassword = 0L;
  icqlink->icq_InvalidUIN = 0L;
  icqlink->icq_Log = 0L;
  icqlink->icq_SrvAck = 0L;
  icqlink->icq_RequestNotify = 0L;
  icqlink->icq_NewUIN = 0L;
  icqlink->icq_MetaUserFound = 0L;
  icqlink->icq_MetaUserInfo = 0L;
  icqlink->icq_MetaUserWork = 0L;
  icqlink->icq_MetaUserMore = 0L;
  icqlink->icq_MetaUserAbout = 0L;
  icqlink->icq_MetaUserInterests = 0L;
  icqlink->icq_MetaUserAffiliations = 0L;
  icqlink->icq_MetaUserHomePageCategory = 0L;

  /* General stuff */
  icqlink->icq_Uin = uin;
  icqlink->icq_Password = strdup(password);
  icqlink->icq_Nick = strdup(nick);
  icqlink->icq_OurIP = -1;
  icqlink->icq_OurPort = 0;
  icqlink->d->icq_ContactList = icq_ListNew();
  icqlink->icq_Status = -1;
  icqlink->icq_UserData = 0L;

  /* UDP stuff */
  icqlink->icq_UDPSok = -1;
  memset(icqlink->d->icq_UDPServMess, FALSE, 
    sizeof(icqlink->d->icq_UDPServMess));
  icqlink->d->icq_UDPSeqNum1 = 0;
  icqlink->d->icq_UDPSeqNum2 = 0;
  icqlink->d->icq_UDPSession = 0;
  icq_UDPQueueNew(icqlink);

  /* TCP stuff */
  icqlink->icq_UseTCP = useTCP;
  if (useTCP)
    icq_TCPInit(icqlink);

  /* Proxy stuff */
  icqlink->icq_UseProxy = 0;
  icqlink->icq_ProxyHost = 0L;
  icqlink->icq_ProxyIP = -1;
  icqlink->icq_ProxyPort = 0;
  icqlink->icq_ProxyAuth = 0;
  icqlink->icq_ProxyName = 0L;
  icqlink->icq_ProxyPass = 0L;
  icqlink->icq_ProxySok = -1;
  icqlink->icq_ProxyOurPort = 0;
  icqlink->icq_ProxyDestIP = -1;
  icqlink->icq_ProxyDestPort = 0;
}

void icq_LinkDestroy(icq_Link *icqlink)
{
  if(icqlink->icq_UseTCP)
    icq_TCPDone(icqlink);
  if(icqlink->icq_Password)
    free(icqlink->icq_Password);
  if(icqlink->icq_Nick)
    free(icqlink->icq_Nick);
  if(icqlink->d->icq_ContactList)
    icq_ListDelete(icqlink->d->icq_ContactList, icq_ContactDelete);
  icq_UDPQueueDelete(icqlink);
  free(icqlink->d);
}

void icq_LinkDelete(icq_Link *icqlink)
{
  icq_LinkDestroy(icqlink);
  free(icqlink);
}

/******************************
Main function connects gets icq_Uin
and icq_Password and logins in and sits
in a loop waiting for server responses.
*******************************/
void icq_Main()
{
  icq_SocketPoll();
}

/**********************************
Connects to hostname on port port
hostname can be DNS or nnn.nnn.nnn.nnn
write out messages to the FD aux
***********************************/
int icq_Connect(icq_Link *icqlink, const char *hostname, int port)
{
  char buf[1024]; /*, un = 1;*/
/*  char tmpbuf[256], our_host[256]*/
  int conct, res;
  unsigned int length;
  struct sockaddr_in saddr, prsin;  /* used to store inet addr stuff */
  struct hostent *host_struct; /* used in DNS llokup */

  /* create the unconnected socket*/
  icqlink->icq_UDPSok = icq_SocketNew(AF_INET, SOCK_DGRAM, 0);

  if(icqlink->icq_UDPSok == -1)
  {
    icq_FmtLog(icqlink, ICQ_LOG_FATAL, "Socket creation failed\n");
    return -1;
  }
  icq_FmtLog(icqlink, ICQ_LOG_MESSAGE, "Socket created attempting to connect\n");
  saddr.sin_addr.s_addr = INADDR_ANY;
  saddr.sin_family = AF_INET; /* we're using the inet not appletalk*/
  saddr.sin_port = 0;
  if(bind(icqlink->icq_UDPSok, (struct sockaddr*)&saddr, sizeof(struct sockaddr))<0)
  {
    icq_FmtLog(icqlink, ICQ_LOG_FATAL, "Can't bind socket to free port\n");
    icq_SocketDelete(icqlink->icq_UDPSok);
    icqlink->icq_UDPSok = -1;
    return -1;
  }
  length = sizeof(saddr);
  getsockname(icqlink->icq_UDPSok, (struct sockaddr*)&saddr, &length);
  icqlink->icq_ProxyOurPort = ntohs(saddr.sin_port);
  if(icqlink->icq_UseProxy)
  {
    int hasName = icqlink->icq_ProxyName && strlen(icqlink->icq_ProxyName);
    int hasPass = icqlink->icq_ProxyPass && strlen(icqlink->icq_ProxyPass);
    int authEnabled = icqlink->icq_ProxyAuth && hasName && hasPass;

    icq_FmtLog(icqlink, ICQ_LOG_MESSAGE, "[SOCKS] Trying to use SOCKS5 proxy\n");
    prsin.sin_addr.s_addr = inet_addr(icqlink->icq_ProxyHost);
    if(prsin.sin_addr.s_addr  == (unsigned long)-1) /* name isn't n.n.n.n so must be DNS */
    {
      host_struct = gethostbyname(icqlink->icq_ProxyHost);
      if(host_struct == 0L)
      {
        icq_FmtLog(icqlink, ICQ_LOG_FATAL, "[SOCKS] Can't find hostname: %s\n", icqlink->icq_ProxyHost);
        return -1;
      }
      prsin.sin_addr = *((struct in_addr*)host_struct->h_addr);
    }
    icqlink->icq_ProxyIP = ntohl(prsin.sin_addr.s_addr);
    prsin.sin_family = AF_INET; /* we're using the inet not appletalk*/
    prsin.sin_port = htons(icqlink->icq_ProxyPort); /* port */

    /* create the unconnected socket*/
    icqlink->icq_ProxySok = icq_SocketNew(AF_INET, SOCK_STREAM, 0);

    if(icqlink->icq_ProxySok == -1)
    {
      icq_FmtLog(icqlink, ICQ_LOG_FATAL, "[SOCKS] Socket creation failed\n");
      return -1;
    }
    icq_FmtLog(icqlink, ICQ_LOG_MESSAGE, "[SOCKS] Socket created attempting to connect\n");
    conct = connect(icqlink->icq_ProxySok, (struct sockaddr *) &prsin, sizeof(prsin));
    if(conct == -1) /* did we connect ?*/
    {
      icq_FmtLog(icqlink, ICQ_LOG_FATAL, "[SOCKS] Connection refused\n");
      return -1;
    }
    buf[0] = 5; /* protocol version */
    buf[1] = 1; /* number of methods */
    buf[2] = authEnabled ? 2 : 0; /* authentication method */

#ifdef _WIN32
    send(icqlink->icq_ProxySok, buf, 3, 0);
    res = recv(icqlink->icq_ProxySok, buf, 2, 0);
#else
    write(icqlink->icq_ProxySok, buf, 3);
    res = read(icqlink->icq_ProxySok, buf, 2);
#endif

    if(authEnabled)
    {
      if(res != 2 || buf[0] != 5 || buf[1] != 2) /* username/password authentication*/
      {
        icq_FmtLog(icqlink, ICQ_LOG_FATAL, "[SOCKS] Authentication method incorrect\n");
        icq_SocketDelete(icqlink->icq_ProxySok);
        return -1;
      }
      buf[0] = 1; /* version of subnegotiation */
      buf[1] = strlen(icqlink->icq_ProxyName);
      memcpy(&buf[2], icqlink->icq_ProxyName, buf[1]);
      buf[2+buf[1]] = strlen(icqlink->icq_ProxyPass);
      memcpy(&buf[3+buf[1]], icqlink->icq_ProxyPass, buf[2+buf[1]]);
#ifdef _WIN32
      send(icqlink->icq_ProxySok, buf, buf[1]+buf[2+buf[1]]+3, 0);
      res = recv(icqlink->icq_ProxySok, buf, 2, 0);
#else
      write(icqlink->icq_ProxySok, buf, buf[1]+buf[2+buf[1]]+3);
      res = read(icqlink->icq_ProxySok, buf, 2);
#endif
      if(res != 2 || buf[0] != 1 || buf[1] != 0)
      {
        icq_FmtLog(icqlink, ICQ_LOG_FATAL, "[SOCKS] Authorization failure\n");
        icq_SocketDelete(icqlink->icq_ProxySok);
        return -1;
      }
    }
    else
    {
      if(res != 2 || buf[0] != 5 || buf[1] != 0) /* no authentication required */
      {
        icq_FmtLog(icqlink, ICQ_LOG_FATAL, "[SOCKS] Authentication method incorrect\n");
        icq_SocketDelete(icqlink->icq_ProxySok);
        return -1;
      }
    }
    buf[0] = 5; /* protocol version */
    buf[1] = 3; /* command UDP associate */
    buf[2] = 0; /* reserved */
    buf[3] = 1; /* address type IP v4 */
    buf[4] = (char)0;
    buf[5] = (char)0;
    buf[6] = (char)0;
    buf[7] = (char)0;
    *(unsigned short*)&buf[8] = htons(icqlink->icq_ProxyOurPort);
/*     memcpy(&buf[8], &icqlink->icq_ProxyOurPort, 2); */
#ifdef _WIN32
    send(icqlink->icq_ProxySok, buf, 10, 0);
    res = recv(icqlink->icq_ProxySok, buf, 10, 0);
#else
    write(icqlink->icq_ProxySok, buf, 10);
    res = read(icqlink->icq_ProxySok, buf, 10);
#endif
    if(res != 10 || buf[0] != 5 || buf[1] != 0)
    {
      switch(buf[1])
      {
        case 1:
          icq_FmtLog(icqlink, ICQ_LOG_FATAL, "[SOCKS] General SOCKS server failure\n");
          break;
        case 2:
          icq_FmtLog(icqlink, ICQ_LOG_FATAL, "[SOCKS] Connection not allowed by ruleset\n");
          break;
        case 3:
          icq_FmtLog(icqlink, ICQ_LOG_FATAL, "[SOCKS] Network unreachable\n");
          break;
        case 4:
          icq_FmtLog(icqlink, ICQ_LOG_FATAL, "[SOCKS] Host unreachable\n");
          break;
        case 5:
          icq_FmtLog(icqlink, ICQ_LOG_FATAL, "[SOCKS] Connection refused\n");
          break;
        case 6:
          icq_FmtLog(icqlink, ICQ_LOG_FATAL, "[SOCKS] TTL expired\n");
          break;
        case 7:
          icq_FmtLog(icqlink, ICQ_LOG_FATAL, "[SOCKS] Command not supported\n");
          break;
        case 8:
          icq_FmtLog(icqlink, ICQ_LOG_FATAL, "[SOCKS] Address type not supported\n");
          break;
        default:
          icq_FmtLog(icqlink, ICQ_LOG_FATAL, "[SOCKS] Unknown SOCKS server failure\n");
          break;
      }
      icq_SocketDelete(icqlink->icq_ProxySok);
      icqlink->icq_ProxySok = -1;
      return -1;
    }
  }
  saddr.sin_addr.s_addr = inet_addr(hostname); /* checks for n.n.n.n notation */
  if(saddr.sin_addr.s_addr == (unsigned long)-1) /* name isn't n.n.n.n so must be DNS */
  {
    host_struct = gethostbyname(hostname);
    if(host_struct == 0L)
    {
      icq_FmtLog(icqlink, ICQ_LOG_FATAL, "Can't find hostname: %s\n", hostname);
      if(icqlink->icq_UseProxy)
      {
        icq_SocketDelete(icqlink->icq_ProxySok);
      }
      return -1;
    }
    saddr.sin_addr = *((struct in_addr *)host_struct->h_addr);
  }
  if(icqlink->icq_UseProxy)
  {
    icqlink->icq_ProxyDestIP = ntohl(saddr.sin_addr.s_addr);
    memcpy(&saddr.sin_addr.s_addr, &buf[4], 4);
  }
  saddr.sin_family = AF_INET; /* we're using the inet not appletalk*/
  saddr.sin_port = htons(port); /* port */
  if(icqlink->icq_UseProxy)
  {
    icqlink->icq_ProxyDestPort = port;
    memcpy(&saddr.sin_port, &buf[8], 2);
  }
  conct = connect(icqlink->icq_UDPSok, (struct sockaddr*)&saddr, sizeof(saddr));
  if(conct == -1) /* did we connect ?*/
  {
    icq_FmtLog(icqlink, ICQ_LOG_FATAL, "Connection refused\n");
    if(icqlink->icq_UseProxy)
    {
      icq_SocketDelete(icqlink->icq_ProxySok);
    }
    return -1;
  }
  length = sizeof(saddr) ;
  getsockname(icqlink->icq_UDPSok, (struct sockaddr*)&saddr, &length);
  icqlink->icq_OurIP = ntohl(saddr.sin_addr.s_addr);
  icqlink->icq_OurPort = ntohs(saddr.sin_port);

  /* sockets are ready to receive data - install handlers */
  icq_SocketSetHandler(icqlink->icq_UDPSok, ICQ_SOCKET_READ,
    (icq_SocketHandler)icq_HandleServerResponse, icqlink);
  if (icqlink->icq_UseProxy)
    icq_SocketSetHandler(icqlink->icq_ProxySok, ICQ_SOCKET_READ,
      (icq_SocketHandler)icq_HandleProxyResponse, icqlink);
  return icqlink->icq_UDPSok;
}

void icq_Disconnect(icq_Link *icqlink)
{
  icq_SocketDelete(icqlink->icq_UDPSok);
  if(icqlink->icq_UseProxy)
    icq_SocketDelete(icqlink->icq_ProxySok);
  icq_UDPQueueFree(icqlink);
}

/*
void icq_InitNewUser(const char *hostname, DWORD port)
{
  srv_net_icq_pak pak;
  int s;
  struct timeval tv;
  fd_set readfds;

  icq_Connect(hostname, port);
  if((icq_UDPSok == -1) || (icq_UDPSok == 0))
  {
    printf("Couldn't establish connection\n");
    exit(1);
  }
  icq_RegNewUser(icq_Password);
  for(;;)
  {
    tv.tv_sec = 2;
    tv.tv_usec = 500000;

    FD_ZERO(&readfds);
    FD_SET(icq_UDPSok, &readfds);

    select(icq_UDPSok+1, &readfds, 0L, 0L, &tv);

    if(FD_ISSET(icq_UDPSok, &readfds))
    {
      s = icq_UDPSockRead(icq_UDPSok, &pak.head, sizeof(pak));
      if(icqtohs(pak.head.cmd) == SRV_NEW_UIN)
      {
        icq_Uin = icqtohl(&pak.data[2]);
        return;
      }
    }
  }
}
*/

/************************
icq_UDPServMess functions
*************************/
BOOL icq_GetServMess(icq_Link *icqlink, WORD num)
{
  return ((icqlink->d->icq_UDPServMess[num/8] & (1 << (num%8))) >> (num%8));
}

void icq_SetServMess(icq_Link *icqlink, WORD num)
{
  icqlink->d->icq_UDPServMess[num/8] |= (1 << (num%8));
}

int icq_GetSok(icq_Link *icqlink)
{
  return icqlink->icq_UDPSok;
}