/*
 * ----------------------------------------------------------------
 * Night Light IRC Proxy - Client Parser 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 (25.11.2007)
 *
 */

#define CLIENT_PARSER_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 1			/* isdigit(), etc */
#define NEED_NETINET_IN_H 1		/* in_addr, sockaddr_in, etc */
#define NEED_ARPA_INET_H 0		/* 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 0		/* Socket functions */
#define NEED_NETDB_H 0			/* 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 "conf.h"

#if MEMDEBUG
  #include "memlist.h"
#endif

#include "access_conf.h"
#include "conn_conf.h"

#include "listen.h"

#include "client.h"
#include "client_connection.h"
#include "client_parser.h"
#include "client_auth.h"
#include "client_handle.h"
#include "client_notice.h"

#include "conn.h"
#include "conn_connection.h"
#include "conn_sendq.h"

#include "chan.h"
#include "chan_user.h"

/* VARIABLES - JONAS (31.07.2001) */

#if MEMDEBUG
  extern unsigned long int Leaks;
  extern unsigned long int LeaksSize;
#endif

#if ARES
  extern ares_channel Ares_Channel;
#endif

extern struct Conf_Struct ConfS;
extern struct Conn_Struct *Conn_Head;

/* CLIENT_PARSE FUNCTION - JONAS (18.07.2001) */

void client_parse(struct Client_Struct *ClientS) {

  char *BufferPT = NULL;
  char *LinePT = NULL;
  unsigned long int Count = 0;
  unsigned long int Len = 0;
  char *TempPT = NULL;

  BufferPT = ClientS->RecvBuffer;

  for (Count = 1 ; BufferPT != NULL ; Count++) {
    TempPT = strchr(BufferPT, '\n');
    if (TempPT == NULL) {
      if (Count == 1) { return; }
      Len = strlen(BufferPT) + 1;
      memmove(ClientS->RecvBuffer, BufferPT, Len);
      TempPT = realloc(ClientS->RecvBuffer, Len);
      if (TempPT == NULL) {
        client_close(ClientS, "Memory allocation failure: [%d] %s", errno, strerror(errno));
        return;
      }
      ClientS->RecvBuffer = TempPT;
      return;
    }
    LinePT = strtok(BufferPT, "\n");
    BufferPT = strtok(NULL, "\0");
    if (LinePT == NULL) { continue; }
    Len = strlen(LinePT);
    if (LinePT[Len-1] == '\r') { LinePT[Len-1] = '\0'; }
    client_parse_message(ClientS, LinePT);
    if (ClientS->RecvBuffer == NULL) { return; }
  }
  FREE(ClientS->RecvBuffer);

}

/* CLIENT_PARSE_MESSAGE FUNCTION - JONAS (17.07.2001) */

void client_parse_message(struct Client_Struct *ClientS, char *MessagePT) {

  char *PrefixPT = NULL;
  char *LinePT = NULL;
  char *CommandPT = NULL;
  char **ParamsPT = NULL;
  char **TempPT = NULL;
  unsigned short int Index = 0;
  unsigned short int Params = 0;
  time_t Duration = 0;

  Duration = (NOW - ClientS->LineTime);
  if (Duration >= ConfS.ClientLineFloodTTL) { ClientS->Lines = 0; }

  ++ClientS->Lines;
  if (ClientS->Lines >= ConfS.ClientMaxLines) {
    client_close(ClientS, ACCESS_CONF_USERFLOOD);
    access_conf_adddeny(ClientS->HostName, ACCESS_CONF_USERFLOOD);
    return;
  }
  ClientS->LineTime = NOW;

  while (MessagePT[0] == ' ') { MessagePT++; }
  if (MessagePT[0] == '\0') { return; }

  if (MessagePT[0] == ':') {
    MessagePT++;
    PrefixPT = MessagePT;
    MessagePT = strchr(MessagePT, ' ');
    if (MessagePT == NULL) { return; }
    MessagePT[0] = '\0';
    MessagePT++;
    while (MessagePT[0] == ' ') { MessagePT++; }
    if (MessagePT[0] == '\0') { return; }
  }
  else {
    if (ClientS->User == NULL) { PrefixPT = "*"; }
    else { PrefixPT = ClientS->User; }
  }

  LinePT = strdup(MessagePT);
  if (LinePT == NULL) {
    client_close(ClientS, "Memory allocation failure: [%d] %s", errno, strerror(errno));
    return;
  }

  CommandPT = MessagePT;
  MessagePT = strchr(MessagePT, ' ');
  if (MessagePT != NULL) {
    MessagePT[0] = '\0';
    MessagePT++;
    while (MessagePT[0] == ' ') { MessagePT++; }
    if (MessagePT[0] == '\0') {
      free(LinePT);
      return;
    }

    FOREVERLOOP {
      if (MessagePT[0] == ':') {
        MessagePT++;
        ++Params;
        TempPT = realloc(ParamsPT, (sizeof(char *) * Params));
        if (TempPT == NULL) {
          client_close(ClientS, "Memory allocation failure: [%d] %s", errno, strerror(errno));
          free(ParamsPT);
          free(LinePT);
          return;
        }
        ParamsPT = TempPT;
        ParamsPT[Index] = MessagePT;
        break;
      }
      else {
        ++Params;
        TempPT = realloc(ParamsPT, (sizeof(char *) * Params));
        if (TempPT == NULL) {
          client_close(ClientS, "Memory allocation failure: [%d] %s", errno, strerror(errno));
          free(ParamsPT);
          free(LinePT);
          return;
        }
        ParamsPT = TempPT;
        ParamsPT[Index] = MessagePT;
        ++Index;
        MessagePT = strchr(MessagePT, ' ');
        if (MessagePT == NULL) { break; }
        MessagePT[0] = '\0';
        MessagePT++;
        while (MessagePT[0] == ' ') { MessagePT++; }
        if (MessagePT[0] == '\0') { break; }
      }
    }
  }

  strupper(CommandPT);

  client_parse_command(ClientS, LinePT, CommandPT, ParamsPT, Params);

  free(LinePT);
  free(ParamsPT);

}

/* CLIENT_PARSE_COMMAND FUNCTION - JONAS (17.07.2001) */

void client_parse_command(struct Client_Struct *ClientS, char *LinePT, char *CommandPT, char **ParamsPT, const unsigned short int Params) {

  if (!Client_IsVerified(ClientS)) {
    if (strcasecmp(CommandPT, "PASS") == FALSE) {
      if (Params < 1) { client_notice(ClientS, "%s :Not enough parameters.", CommandPT); return; }
      ClientS->Pass = strrealloc(ClientS->Pass, ParamsPT[0]);
#if 0
      client_notice(ClientS, "Got password.");
#endif
    }
    else if (strcasecmp(CommandPT, "NICK") == FALSE) {
      if (Params < 1) { client_notice(ClientS, "%s :Not enough parameters.", CommandPT); return; }
      ClientS->Nick = strrealloc(ClientS->Nick, ParamsPT[0]);
#if 0
      client_notice(ClientS, "Got nickname.");
#endif
      if (ClientS->Pass == NULL) { client_notice(ClientS, "You need to send the PASS command!"); }
    }
    else if (strcasecmp(CommandPT, "USER") == FALSE) {
      if (Params < 4) { client_notice(ClientS, "%s :Not enough parameters.", CommandPT); return; }
      ClientS->User = strrealloc(ClientS->User, ParamsPT[0]);
#if 0
      client_notice(ClientS, "Got username.");
#endif
      if (ClientS->Pass == NULL) { client_notice(ClientS, "You need to send the PASS command!"); }
    }
    else { client_addsend(ClientS, ":%s 451 * :You have not registered.", IRCP_USER_HOST(ClientS)); }

    if ((ClientS->User != NULL) && (ClientS->Pass != NULL)) {

      struct Conn_Struct *ConnS = NULL;

      assert(geteuid() != 0);
      client_authcheck(ClientS->User, ClientS->Pass);
      assert(geteuid() != 0);
      if (aerrno != AESUCCESS) {
        client_close(ClientS, "USER / PASS is incorrect.");
        return;
      }

      sysprint(BITMASK_MAIN, "Client %s (%s) sucessfully authenticated as \"%s\".", ClientS->HostName, ClientS->HostIPS, ClientS->User);
      client_noticeallbutone(ClientS, "Client %s (%s) authenticated as \"%s\".", ClientS->HostName, ClientS->HostIPS, ClientS->User);

      Client_SetVerified(ClientS);
      client_welcome(ClientS);

      for (ConnS = Conn_Head ; ConnS != NULL ; ConnS = ConnS->Next) {
        if ((strcmp(ClientS->User, ConnS->User) == FALSE) && (Conn_IsAttached(ConnS))) { client_attach(ClientS, ConnS); break; }
      }
      return;
    }
    return;
  }

  if (strcasecmp(CommandPT, "PCONNLIST") == FALSE) {

    struct Conn_Struct *ConnS = NULL;
    struct ConnServer_Struct *ConnServer = NULL;
    unsigned short int Count = 0;

    for (ConnS = Conn_Head ; ConnS != NULL ; ConnS = ConnS->Next) {

      if (strcmp(ConnS->User, ClientS->User) != FALSE) { continue; }
      ++Count;

      client_notice(ClientS, "\2Connection: %s Host: %s\2", ConnS->Name, ConnS->Host);

      if (Host_IsResolving(ConnS->ResolveFlags)) { client_notice(ClientS, "--- Status: Resolving local hostname %s to IP-address.", ConnS->HostName); }
      else if (Host_IsResolving(ConnS->ServerResolveFlags)) { client_notice(ClientS, "--- Status: Resolving server nostname %s to IP-address.", ConnS->ServerHostName); }
      else if (Conn_IsConnecting(ConnS)) { client_notice(ClientS, "--- Status: Connecting to %s(%s):%ld.", ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH); }
      else if (Conn_IsWelcome(ConnS)) { client_notice(ClientS, "--- Status: Connected to %s(%s):%ld \"%s\".", ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH, ConnS->ServerName); }
      else if (Conn_IsConnected(ConnS)) { client_notice(ClientS, "--- Status: Socket connected to %s(%s):%ld.", ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH); }
      else { client_notice(ClientS, "--- Status: Disconnected."); }

      if (ConnS->Server_Head == NULL) { client_notice(ClientS, "--- No servers!"); }
      else {
        for (ConnServer = ConnS->Server_Head ; ConnServer != NULL ; ConnServer = ConnServer->Next) {
          client_notice(ClientS, "--- Server: %s Port: %ld", ConnServer->Host, ConnServer->Port);
        }
      }

    }

    if (Count == 0) {
      client_notice(ClientS, "You have no connections.");
    }

    return;

  }
  else if (strcasecmp(CommandPT, "PCONNECT") == FALSE) {

    struct Conn_Struct *ConnS = NULL;

    if (Params < 1) {
      if (ClientS->ConnS == NULL) { client_notice(ClientS, "Usage: PCONNECT <Connection name> [Connection server]."); return; }
      ConnS = ClientS->ConnS;
    }
    else { ConnS = conn_get(ParamsPT[0]); }

    if (ConnS == NULL) { client_notice(ClientS, "No such connection, use /PCONNLIST to list your connections."); return; }

    if (strcmp(ConnS->User, ClientS->User) != FALSE) { client_notice(ClientS, "Permission denied, You're not the owner of connection %s.", ConnS->Name); return; }

    if (Params >= 2) {
      for (ConnS->ConnServerTry = ConnS->Server_Head ; ConnS->ConnServerTry != NULL ; ConnS->ConnServerTry = ConnS->ConnServerTry->Next) {
        if (strwm(ParamsPT[1], ConnS->ConnServerTry->Host) == TRUE) { break; }
      }
      if (ConnS->ConnServerTry == NULL) { client_notice(ClientS, "No such server for that connection."); return; }
    }
    if (Host_IsResolving(ConnS->ResolveFlags)) {
      if (ConnS->ConnServerTry == NULL) { client_notice(ClientS, "Connection %s: Resolving local hostname %s to IP-address.", ConnS->Name, ConnS->HostName); return; }
#if HAVE_ARES_CANCELQUERY
      ares_cancelquery(Ares_Channel, ConnS);
#endif
      return;
    }
    else if (Host_IsResolving(ConnS->ServerResolveFlags)) {
      if (ConnS->ConnServerTry == NULL) { client_notice(ClientS, "Connection %s: Resolving server hostname %s to IP-address.", ConnS->Name, ConnS->ServerHostName); return; }
#if HAVE_ARES_CANCELQUERY
      ares_cancelquery(Ares_Channel, ConnS);
#endif
      return;
    }
    else if (Conn_IsConnecting(ConnS)) {
      if (ConnS->ConnServerTry == NULL) { client_notice(ClientS, "%s: Connecting to %s(%s):%ld.", ConnS->Name, ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH); return; }
      conn_disconnect(ConnS, "Connection %s: Connection attempt to %s(%s):%ld cancelled by user %s(%s).", ConnS->Name, ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH, ClientS->HostName, ClientS->HostIPS);
      return;
    }
    else if (Conn_IsWelcome(ConnS)) {
      if ((ConnS->ConnServerTry == NULL) || (strcasecmp(ConnS->ServerHostName, ConnS->ConnServerTry->Host) == FALSE)) { client_notice(ClientS, "Connection %s: Already connected to %s, use PQUIT to quit.", ConnS->Name, ConnS->ServerHostName); return; }
      conn_quit(ConnS, "Changing server to %s.", ConnS->ConnServerTry->Host);
      return;
    }
    else if (Conn_IsConnected(ConnS)) {
      if (ConnS->ConnServerTry == NULL) { client_notice(ClientS, "Connection %s: Socket connected to %s(%s):%ld.", ConnS->Name, ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH); return; }
      conn_disconnect(ConnS, "Connection %s attempt to %s(%s):%ld cancelled by user %s(%s).", ConnS->Name, ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH, ClientS->HostName, ClientS->HostIPS);
      return;
    }

#if SSL_SUPPORT
    conn_connect(ConnS);
#else
    if (!ConnConf_IsSSL(ConnS)) { conn_connect(ConnS); }
    else { client_notice(ClientS, "Connection %s: SSL is enabled for this connection, but %s is compiled without SSL support.", ConnS->Name, PACKAGE); }
#endif
    return;

  }
  else if (strcasecmp(CommandPT, "PQUIT") == FALSE) {

    struct Conn_Struct *ConnS = NULL;

    if (Params >= 1) {
      ConnS = conn_get(ParamsPT[0]);
      if (ConnS == NULL) { client_notice(ClientS, "No such connection, eventually use /PCONNLIST to list your connections."); return; }
    }
    else {
      ConnS = ClientS->ConnS;
      if (ConnS == NULL) { client_notice(ClientS, "Usage: PQUIT <Connection name>"); return; }
    }

    if (strcmp(ConnS->User, ClientS->User) != FALSE) { client_notice(ClientS, "Permission denied, You're not the owner of connection %s.", ConnS->Name); return; }

    if (Conn_IsConnect(ConnS)) {
      if (Conn_IsWelcome(ConnS)) {
        if (Conn_IsSentQuit(ConnS)) { conn_disconnect(ConnS, "Connection %s to server %s aborted by user %s (%s[%s]).", ConnS->Name, ConnS->ServerHostName, ClientS->User, ClientS->HostName, ClientS->HostIPS); return; }
        else { conn_quit(ConnS, "%s (%s[%s]) requested QUIT.", ClientS->User, ClientS->HostName, ClientS->HostIPS); }
      }
      else { conn_disconnect(ConnS, "Connection %s to server %s aborted by user %s (%s[%s]).", ConnS->Name, ConnS->ServerHostName, ClientS->User, ClientS->HostName, ClientS->HostIPS); return; }
    }
    else { client_notice(ClientS, "Connection %s is not connected.", ConnS->Name); return; }

    return;

  }
  else if ((strcasecmp(CommandPT, "PATTACH") == FALSE) || (strcasecmp(CommandPT, "PRESUME") == FALSE)) {

    struct Conn_Struct *ConnS = NULL;

    if (ClientS->ConnS != NULL) { client_notice(ClientS, "You are already attached on connection %s.", ClientS->ConnS->Name); return; }
    if (Params < 1) { client_notice(ClientS, "Usage: PATTACH <Connection name>"); return; }
    ConnS = conn_get(ParamsPT[0]);
    if (ConnS == NULL) { client_notice(ClientS, "No such connection."); return; }
    if (strcmp(ConnS->User, ClientS->User) != FALSE) { client_notice(ClientS, "Permission denied, You're not the owner of connection %s.", ConnS->Name); return; }
    client_attach(ClientS, ConnS);

  }
  else if (strcasecmp(CommandPT, "PDETACH") == FALSE) {
    if (ClientS->ConnS == NULL) { client_notice(ClientS, "You have no attached connection."); return; }
    Conn_ClearAttached(ClientS->ConnS);
    client_detach(ClientS);
  }
  else if (strcasecmp(CommandPT, "QUIT") == FALSE) {
    client_close(ClientS, "Client sent QUIT.");
  }
  else if (strcasecmp(CommandPT, "PING") == FALSE) {
    if (Params < 1) { client_notice(ClientS, "%s :Not enough parameters.", CommandPT); return; }
    client_addsend(ClientS, ":%s PONG %s :%s", HOSTNAME, IRCP_USER_HOST(ClientS), ParamsPT[0]);
    /* client_addsend(ClientS, "PONG :%s", ParamsPT[0]); */
  }
  else if (strcasecmp(CommandPT, "PONG") == FALSE) {
    Client_ClearSentPing(ClientS);
  }
  else if (strcasecmp(CommandPT, "QUOTE") == FALSE) {

    char *DumbPT = NULL;

    if (ClientS->ConnS == NULL) { client_notice(ClientS, "QUOTE: No attached connection."); return; }
    if (!Conn_IsConnected(ClientS->ConnS)) { client_notice(ClientS, "Connection %s not established.", ClientS->ConnS->Name); return; }

    DumbPT = strchr(LinePT, ' ');
    DumbPT++;
    conn_addsendq(ClientS->ConnS, "%s", DumbPT);
  }
  else if (strcasecmp(CommandPT, "READLOG") == FALSE) {

    FILE *FilePT = NULL;
    struct Conn_Struct *ConnS = NULL;
    unsigned long int Count = 0;

    if (Params >= 1) {
      ConnS = conn_get(ParamsPT[0]);
      if (ConnS == NULL) { client_notice(ClientS, "No such connection, eventually use /PCONNLIST to list your connections."); return; }
    }
    else {
      ConnS = ClientS->ConnS;
      if (ConnS == NULL) { client_notice(ClientS, "Usage: READLOG <Connection name>"); return; }
    }

    if (strcmp(ConnS->User, ClientS->User) != FALSE) { client_notice(ClientS, "Permission denied, You're not the owner of connection %s.", ConnS->Name); return; }
    if (ConnS->FileName == NULL) { client_notice(ClientS, "Logfile missing for connection %s.", ConnS->Name); return; }
#if !WIN32
    if (Conn_IsLogHomeDir(ConnS)) { sysseteuidbyuser(ConnS->User); }
#endif

    FilePT = fopen(ConnS->FileName, "r");
    if (FilePT == NULL) {
      sysprint(BITMASK_ERROR, "Unable to open %s for reading: [%d] %s", ConnS->FileName, errno, strerror(errno));
      client_notice(ClientS, "Unable to open %s for reading: [%d] %s", ConnS->FileName, errno, strerror(errno));
#if !WIN32
      if (Conn_IsLogHomeDir(ConnS)) { sysseteuidnormal(); }
#endif
      return;
    }

    FOREVERLOOP {

      char Line[LINELEN+1] = "";
      char *DumbPT = NULL;

      memset(&Line, 0, LINELEN+1);
      DumbPT = fgets(Line, LINELEN, FilePT);
      if (DumbPT == NULL) { break; }

      Count++;

      client_addsend(ClientS, ":%s PRIVMSG %s :%s", IRCP_USER_HOST(ClientS), IRCP_USER_NICK(ClientS), Line);

    }
    if (Count == 0) { client_notice(ClientS, "Logfile for %s is empty.", ConnS->Name); }
    fclose(FilePT);

#if !WIN32
    if (Conn_IsLogHomeDir(ConnS)) { sysseteuidnormal(); }
#endif

  }
  else if (strcasecmp(CommandPT, "ERASELOG") == FALSE) {

    FILE *FilePT = NULL;
    struct Conn_Struct *ConnS = NULL;
    unsigned long int Count = 0;

    if (Params >= 1) {
      ConnS = conn_get(ParamsPT[0]);
      if (ConnS == NULL) { client_notice(ClientS, "No such connection, eventually use /PCONNLIST to list your connections."); return; }
    }
    else {
      ConnS = ClientS->ConnS;
      if (ConnS == NULL) { client_notice(ClientS, "Usage: ERASELOG <Connection name>"); return; }
    }

    if (strcmp(ConnS->User, ClientS->User) != FALSE) { client_notice(ClientS, "Permission denied, You're not the owner of connection %s.", ConnS->Name); return; }
    if (ConnS->FileName == NULL) { client_notice(ClientS, "Logfile missing for connection %s.", ConnS->Name); return; }
#if !WIN32
    if (Conn_IsLogHomeDir(ConnS)) { sysseteuidbyuser(ConnS->User); }
#endif

    FilePT = fopen(ConnS->FileName, "r");
    if (FilePT == NULL) {
      sysprint(BITMASK_ERROR, "Unable to open %s for reading: [%d] %s", ConnS->FileName, errno, strerror(errno));
      client_notice(ClientS, "Unable to open %s for reading: [%d] %s", ConnS->FileName, errno, strerror(errno));
#if !WIN32
      if (Conn_IsLogHomeDir(ConnS)) { sysseteuidnormal(); }
#endif
      return;
    }
    FOREVERLOOP {
      char Line[LINELEN+1] = "";
      char *DumbPT = NULL;
      memset(&Line, 0, LINELEN+1);
      DumbPT = fgets(Line, LINELEN, FilePT);
      if (DumbPT == NULL) { break; }
      Count++;
    }
    fclose(FilePT);
    if (Count == 0) {
      client_notice(ClientS, "Logfile for %s is empty.", ConnS->Name);
#if !WIN32
      if (Conn_IsLogHomeDir(ConnS)) { sysseteuidnormal(); }
#endif
      return;
    }

    FilePT = fopen(ConnS->FileName, "w+");
    if (FilePT == NULL) {
      sysprint(BITMASK_ERROR, "Unable to open %s for writing: [%d] %s", ConnS->FileName, errno, strerror(errno));
      client_notice(ClientS, "Unable to open %s for writing: [%d] %s", ConnS->FileName, errno, strerror(errno));
#if !WIN32
      if (Conn_IsLogHomeDir(ConnS)) { sysseteuidnormal(); }
#endif
      return;
    }
    fclose(FilePT);
#if !WIN32
    if (Conn_IsLogHomeDir(ConnS)) { sysseteuidnormal(); }
#endif

    client_notice(ClientS, "Logfile for %s erased.", ConnS->Name);

  }
#if MEMDEBUG
  else if (strcasecmp(CommandPT, "MEMLEAKS") == FALSE) {
    client_notice(ClientS, "Calculating memory leaks...");
    mem_leaks();
    if (Leaks > 1 ) {
      client_notice(ClientS, "Memory leaks: %ld size: %ld", Leaks, LeaksSize);
      client_notice(ClientS, "!!! MEMORY LEAKS FOUND --- LOOK IN THE ERROR LOG !!!");
    }
    else { client_notice(ClientS, "No memory leaks found! IGNORE 1 FAKE MEMLEAK IN client_io.c!"); }
  }
#endif
  else {
    if (ClientS->ConnS == NULL) { client_notice(ClientS, "No attached connection, use PATTACH to attach first."); }
    else {
      if (Conn_IsConnect(ClientS->ConnS)) {
        if (Conn_IsWelcome(ClientS->ConnS)) {
          if ((!strcasecmp(CommandPT, "WHO")) && (Params >= 1)) {
            struct Chan_Struct *ChanS;
            struct Who_Struct *WhoS;
            ChanS = chan_get(ClientS->ConnS, ParamsPT[0]);
            if (ChanS != NULL) {
              WhoS = malloc(sizeof(struct Who_Struct));
              if (WhoS != NULL) {
                memset(WhoS, 0, sizeof(struct Who_Struct));
                WhoS->PrevForClient = NULL;
                WhoS->NextForClient = ClientS->Who_Head;
                if (WhoS->NextForClient != NULL) { WhoS->NextForClient->PrevForClient = WhoS; }
                ClientS->Who_Head = WhoS;
                WhoS->NextForChan = ChanS->Who_Head;
                ChanS->Who_Head = WhoS;
                WhoS->ClientS = ClientS;
              }
            }
          }
        }
        conn_addsendq(ClientS->ConnS, "%s", LinePT);
      }
      else { client_notice(ClientS, "Attached connection not established."); }
    }
  }
}


syntax highlighted by Code2HTML, v. 0.9.1