/*
 * ----------------------------------------------------------------
 * Night Light IRC Proxy - Client I/O Functions
 * ----------------------------------------------------------------
 * Copyright (C) 1997-2007 Jonas Kvinge <jonas@night-light.net>
 * All rights reserved.
 *
 * 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.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Last modified by:
 * Jonas Kvinge (24.11.2007)
 *
 */

#define CLIENT_IO_C

#define NEED_SYS_TYPES_H 1		/* Extra types */
#define NEED_SYS_PARAM_H 1		/* Some systems need this */
#define NEED_LIMITS_H 0			/* Kernel limits */
#define NEED_STDARG_H 1			/* va_list, etc */
#define NEED_ERRNO_H 1			/* errno */
#define NEED_CTYPE_H 0			/* isdigit(), etc */
#define NEED_NETINET_IN_H 1		/* in_addr, sockaddr_in, etc */
#define NEED_ARPA_INET_H 1		/* inet_ntoa(), inet_aton(), etc */
#define NEED_STDIO_H 1			/* Standard C UNIX functions */
#define NEED_STDLIB_H 1			/* malloc(), exit(), atoi(), etc */
#define NEED_TIME_H 1			/* time(), etc */
#define NEED_SYSCTL_H 0			/* sysctl(), etc */
#define NEED_SYS_STAT_H 0		/* chmod(), mkdir(), etc */
#define NEED_SYS_UIO_H 0		/* iovec, etc */
#define NEED_FCNTL_H 1			/* open(), creat(), fcntl(), etc */
#define NEED_SYS_IOCTL_H 0		/* ioctl(), etc */
#define NEED_SYS_FILIO_H 0		/* Solaris need this for ioctl(), etc */
#define NEED_UNISTD_H 1			/* Unix standard functions */
#define NEED_STRING_H 1			/* C string functions */
#define NEED_SIGNAL_H 0			/* Signal functions */
#define NEED_SYS_SOCKET_H 1		/* Socket functions */
#define NEED_NETDB_H 1			/* Network database functions */
#define NEED_ARPA_NAMESER_H 0		/* Nameserver definitions */
#define NEED_GETUSERPW_HEADERS 0 	/* Functions to retrive system passwords */
#define NEED_ARES 1			/* Functions needed for ares */
#define NEED_SSL 1			/* Needed for SSL support */

#include "includes.h"
#include "irc.h"

#include "client.h"
#include "client_io.h"

#if SSL_SUPPORT
#include "client_io_ssl.h"
#endif /* SSL_SUPPORT */

#include "client_connection.h"
#include "client_parser.h"
#include "client_auth.h"

/* VARIABLES - JONAS (31.07.2001) */

extern struct Client_Struct *Client_Head;

/* CLIENT_FDS FUNCTION - JONAS (22.07.2001) */

void client_fds(fd_set *ReadFDS, fd_set *WriteFDS, unsigned long int *FDS) {

  struct Client_Struct *ClientS = NULL;
  struct Client_Struct *ClientS_DEL = NULL;
  time_t Duration = 0;

  for (ClientS = Client_Head ; ClientS != NULL ;) {

    if ((Host_IsResolved(ClientS->ResolveFlags)) && (Client_IsSocket(ClientS))) {
      if (Client_IsVerified(ClientS)) {
        if (Client_IsSentPing(ClientS)) {
          Duration = (NOW - ClientS->SentPingTime);
          if (Duration >= CLIENT_PINGTIMEOUT) { client_close(ClientS, "No response from client in %s, closing connection to client.", strduration(Duration)); }
        }
        else {
          Duration = (NOW - ClientS->LastRecvTime);
          if (Duration >= CLIENT_IDLETIMEBEFOREPING) {
            ClientS->SentPingTime = NOW;
            Client_SetSentPing(ClientS);
            client_addsend(ClientS, "PING :ircproxy");
          }
        }
      }
      else {
        Duration = (NOW - ClientS->Time);
        if (Duration >= CLIENT_VERIFYTIMEOUT) { client_close(ClientS, "No response from client in %s while waiting for PASS/NICK/USER, closing connection to client.", strduration(Duration)); }
      }
    }

    if (Client_IsSentError(ClientS)) {
      Duration = (NOW - ClientS->SentErrorTime);
      if (Duration >= CLIENT_ERRORTIMEOUT) { client_cleanup(ClientS, "Disconnecting client %s (%s): Timeout while closing connection.", ClientS->HostName, ClientS->HostIPS); }
    }

    if ((Client_IsSocket(ClientS)) && (Host_IsResolved(ClientS->ResolveFlags))) {
      FD_SET(ClientS->FD, ReadFDS);
      if (ClientS->SendBuffer != NULL) { FD_SET(ClientS->FD, WriteFDS); }
    }

    if ((!Client_IsSocket(ClientS)) && (!Host_IsResolving(ClientS->ResolveFlags))) {
      ClientS_DEL = ClientS;
      ClientS = ClientS->Next;
      client_rem(ClientS_DEL);
      continue;
    }
    ClientS = ClientS->Next;

  }

}

/* CLIENT_IO FUNCTION - JONAS (01.07.2000) */

void client_io(fd_set *ReadFDS, fd_set *WriteFDS, unsigned long int *FDS) {

  struct Client_Struct *ClientS = NULL;

  for (ClientS = Client_Head ; ClientS != NULL ; ClientS = ClientS->Next) {

    assert(*FDS >= 0);
    if (*FDS <= 0) { return; }

    if (Client_IsSocket(ClientS)) {
      if (FD_ISSET(ClientS->FD, ReadFDS)) { *FDS = *FDS - 1; }
      if (FD_ISSET(ClientS->FD, WriteFDS)) { *FDS = *FDS - 1; }
    }


    if (Client_IsSocket(ClientS)) {
      if (FD_ISSET(ClientS->FD, ReadFDS)) {
#if SSL_SUPPORT
        if (Client_IsSSL(ClientS)) { client_recv_ssl(ClientS); }
        else {
#endif /* SSL_SUPPORT */
          client_recv(ClientS);
#if SSL_SUPPORT
        }
#endif /* SSL_SUPPORT */
      }
    }
    if (Client_IsSocket(ClientS)) {
      if (FD_ISSET(ClientS->FD, WriteFDS)) {
#if SSL_SUPPORT
        if (Client_IsSSL(ClientS)) { client_send_ssl(ClientS); }
        else {
#endif /* SSL_SUPPORT */
          client_send(ClientS);
#if SSL_SUPPORT
        }
#endif /* SSL_SUPPORT */
      }
    }
  }

}

/* CLIENT_RECV FUNCTION - JONAS (01.07.2000) */

void client_recv(struct Client_Struct *ClientS) {

  signed long int Result = 0;
  unsigned long int OldLen = 0;
  unsigned long int NewLen = 0;
  char RecvBuffer[RECVBUFFERLEN+1] = "";
  char *RecvBufferPT = NULL;

  assert(ClientS != NULL);

  ClientS->LastRecvTime = NOW;

  do {
    memset(&RecvBuffer, 0, sizeof(RecvBuffer));
    Result = recv(ClientS->FD, RecvBuffer, RECVBUFFERLEN, MSG_NOSIGNAL);
    if (Result <= ERROR) {
      if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR)) { break; }
      client_cleanup(ClientS, "Read error to client %s (%s): [%d] %s", ClientS->HostName, ClientS->HostIPS, errno, strerror(errno));
      return;
    }
    if (Result == 0) {
      if (ClientS->RecvBuffer != NULL) { client_parse(ClientS); }
      client_cleanup(ClientS, "EOF to client %s (%s).", ClientS->HostName, ClientS->HostIPS);
      return;
    }
    if (ClientS->RecvBuffer == NULL) { OldLen = 0; }
    else { OldLen = strlen(ClientS->RecvBuffer); }
    NewLen = OldLen + Result + 1;
    RecvBufferPT = realloc(ClientS->RecvBuffer, NewLen);
    if (RecvBufferPT == NULL) {
      client_close(ClientS, "Memory allocation failure: [%d] %s", errno, strerror(errno));
      return;
    }
    ClientS->RecvBuffer = RecvBufferPT;
    RecvBufferPT += OldLen;
    strcpy(RecvBufferPT, RecvBuffer);
  }
  while (Result >= RECVBUFFERLEN);

  if (Client_IsSentError(ClientS)) { return; }

  client_parse(ClientS);

}

/* CLIENT_SEND FUNCTION - JONAS (01.07.2000) */

void client_send(struct Client_Struct *ClientS) {

  char *SendBufferPT = NULL;
  char SendBuffer[SENDBUFFERLEN+1] = "";
  unsigned long int SendLen = 0;
  unsigned long int SentLen = 0;
  signed long int Result = 0;

  assert(ClientS != NULL);

  for (SendBufferPT = ClientS->SendBuffer ; *SendBufferPT != '\0' ; SendBufferPT += SentLen) {
    SendLen = strlen(SendBufferPT);
    if (SendLen > SENDBUFFERLEN) { SendLen = SENDBUFFERLEN; }
    memset(&SendBuffer, 0, sizeof(SendBuffer));
    strncpy(SendBuffer, SendBufferPT, SendLen);
    Result = send(ClientS->FD, SendBufferPT, SendLen, MSG_NOSIGNAL);
    if (Result <= ERROR) {
      if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR) || (errno == ENOMEM) || (errno == ENOBUFS)) {
        unsigned long int Len = 0;
        sysprint(BITMASK_MAIN, "Write error to client %s (%s): [%d] %s", ClientS->HostName, ClientS->HostIPS, errno, strerror(errno));
        /*
         * EAGAIN/EWOULDBLOCK - THE SOCKET IMPLEMENTATION CAN'T HANDLE MORE DATA.
         * EINTR - INTERRUPTED BY A SIGNAL.
         * ENOMEM - NO MEMORY LEFT.
         * ENOBUFS - NO BUFFER SPACE AVAILABLE.
         *
         * COPY WHATS LEFT TO THE THE START OF THE SENDBUFFER, REALLOCATE THE MEMORY AND BAIL OUT - JONAS (01.12.1999)
         *
         */
        Len = strlen(SendBufferPT) + 1;
        memmove(ClientS->SendBuffer, SendBufferPT, Len);
        SendBufferPT = realloc(ClientS->SendBuffer, Len);
        assert(SendBufferPT != NULL);
        ClientS->SendBuffer = SendBufferPT;
        return;
      }
      client_cleanup(ClientS, "Write error to client %s (%s): [%d] %s", ClientS->HostName, ClientS->HostIPS, errno, strerror(errno));
      return;
    }
    SentLen = Result;
    assert(SentLen == SendLen);
  }

  FREE(ClientS->SendBuffer);

  if (Client_IsSentError(ClientS)) { client_cleanup(ClientS, "Successfully closed connection to client %s (%s).", ClientS->HostName, ClientS->HostIPS); }

}


syntax highlighted by Code2HTML, v. 0.9.1