/*
 * ----------------------------------------------------------------
 * Night Light IRC Proxy - Connection 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 (24.08.2007)
 *
 */

#define CONN_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 0			/* Functions needed for ares */
#define NEED_SSL 1			/* Needed for SSL support */

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

#include "conn_conf.h"

#include "client.h"
#include "client_connection.h"
#include "client_notice.h"

#include "conn.h"
#include "conn_connection.h"
#include "conn_parser.h"
#include "conn_sendq.h"
#include "conn_log.h"
#include "conn_ignore.h"

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

/* VARIABLES - JONAS (31.07.2001) */

extern char *IRCNICK;
extern struct Client_Struct *Client_Head;

/* CONN_PARSER FUNCTION - JONAS (18.07.2001) */

void conn_parser(struct Conn_Struct *ConnS) {

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

  assert(ConnS != NULL);

  BufferPT = ConnS->RecvBuffer;

  for (Count = 1 ; BufferPT != NULL ; Count++) {
    DumbPT = strchr(BufferPT, '\n');
    if (DumbPT == NULL) {
      if (Count == 1) { return; }
      Len = strlen(BufferPT) + 1;
      memmove(ConnS->RecvBuffer, BufferPT, Len);
      TempPT = realloc(ConnS->RecvBuffer, Len);
      if (TempPT == NULL) {
        if (Conn_IsWelcome(ConnS)) { conn_quit(ConnS, "Memory allocation failure: [%d] %s", errno, strerror(errno)); }
        else { conn_disconnect(ConnS, "Connection %s: Memory allocation failure: [%d] %s", ConnS->Name, errno, strerror(errno)); }
        return;
      }
      ConnS->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'; }
    DEBUGPRINT(BITMASK_DEBUG_CONN, "Connection %s: Server: %s: Receive: %s", ConnS->Name, ConnS->ServerHostName, LinePT);
    conn_parse_message(ConnS, LinePT);
    if (ConnS->RecvBuffer == NULL) { return; }
  }
  FREE(ConnS->RecvBuffer);

}

/* CONN_PARSE_MESSAGE FUNCTION - JONAS (17.07.2001) */

void conn_parse_message(struct Conn_Struct *ConnS, char *MessagePT) {

  char *PrefixPT = NULL;
  char *CommandPT = NULL;
  char *LinePT = NULL;
  char **ParamsPT = NULL;
  char **TempPT = NULL;
  unsigned short int Index = 0;
  unsigned short int Params = 0;
  unsigned short int Numeric = 0;
  struct Client_Struct *ClientS = NULL;

  assert(ConnS != NULL);
  assert(MessagePT != NULL);

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

  LinePT = strdup(MessagePT);
  if (LinePT == NULL) {
    if (Conn_IsWelcome(ConnS)) { conn_quit(ConnS, "Memory allocation failure: [%d] %s", errno, strerror(errno)); }
    else { conn_disconnect(ConnS, "Connection %s: Memory allocation failure: [%d] %s", ConnS->Name, errno, strerror(errno)); }
    return;
  }

  if (MessagePT[0] == ':') {
    MessagePT++;
    PrefixPT = MessagePT;
    MessagePT = strchr(MessagePT, ' ');
    if (MessagePT == NULL) {
      free(LinePT);
      return;
    }
    MessagePT[0] = '\0';
    MessagePT++;
    while (MessagePT[0] == ' ') { MessagePT++; }
    if (MessagePT[0] == '\0') {
      free(LinePT);
      return;
    }
  }
  else {
    if (ConnS->ServerName == NULL) {
      if (ConnS->ServerHostName == NULL) { PrefixPT = ConnS->ServerHostIPS; }
      else { PrefixPT = ConnS->ServerHostName; }
    }
    else { PrefixPT = ConnS->ServerName; }
  }

  CommandPT = MessagePT;
  MessagePT = strchr(MessagePT, ' ');
  if (MessagePT == NULL) {
    free(LinePT);
    return;
  }
  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) {
        if (Conn_IsWelcome(ConnS)) { conn_quit(ConnS, "Memory allocation failure: [%d] %s", errno, strerror(errno)); }
        else { conn_disconnect(ConnS, "Connection %s: Memory allocation failure: [%d] %s", ConnS->Name, errno, strerror(errno)); }
        free(LinePT);
        free(ParamsPT);
        return;
      }
      ParamsPT = TempPT;
      ParamsPT[Index] = MessagePT;
      break;
    }
    else {
      ++Params;
      TempPT = realloc(ParamsPT, (sizeof(char *) * Params));
      if (TempPT == NULL) {
        if (Conn_IsWelcome(ConnS)) { conn_quit(ConnS, "Memory allocation failure: [%d] %s", errno, strerror(errno)); }
        else { conn_disconnect(ConnS, "Connection %s: Memory allocation failure: [%d] %s", ConnS->Name, errno, strerror(errno)); }
        free(LinePT);
        free(ParamsPT);
        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; }
    }

  }

  if (strdigit(CommandPT) == TRUE) {
    Numeric = atoi(CommandPT);
    if (conn_parse_numeric(ConnS, PrefixPT, LinePT, Numeric, ParamsPT, Params) == FALSE) {
      free(LinePT);
      free(ParamsPT);
      return;
    }
  }
  else {
    if (conn_parse_event(ConnS, PrefixPT, CommandPT, ParamsPT, Params) == FALSE) {
      free(LinePT);
      free(ParamsPT);
      return;
    }
  }

  for (ClientS = Client_Head ; ClientS != NULL ; ClientS = ClientS->Next) {
    if (ClientS->ConnS == ConnS) { client_addsend(ClientS, "%s", LinePT); }
  }

  free(LinePT);
  free(ParamsPT);

}

/* CONN_PARSE_NUMERIC FUNCTION - JONAS (17.07.2001) */

unsigned short int conn_parse_numeric(struct Conn_Struct *ConnS, const char *const PrefixPT, const char *const LinePT, const unsigned short int Numeric, char **ParamsPT, const unsigned short int Params) {

  unsigned short int Result = TRUE;

  switch(Numeric) {
    case 1:
    case 2:
    case 3:
    case 4:
      CONN_PARSE_NUMERIC_CHECKPARAMS(1);
      Result = conn_parse_numeric_welcome(ConnS, PrefixPT, LinePT, Numeric, ParamsPT, Params);
      break;
    case IRC_GENERIC_RPL_ISUPPORT:
      if (!Conn_IsWelcome(ConnS)) { break; }
      CONN_PARSE_NUMERIC_CHECKPARAMS(1);
      Result = conn_parse_numeric_isupport(ConnS, PrefixPT, LinePT, Numeric, ParamsPT, Params);
      break;
    case IRC_RFC1459_RPL_NOWAWAY:
      if (!Conn_IsWelcome(ConnS)) { break; }
      Result = conn_parse_numeric_nowaway(ConnS, PrefixPT, LinePT, Numeric, ParamsPT, Params);
      break;
    case IRC_RFC1459_RPL_UNAWAY:
      if (!Conn_IsWelcome(ConnS)) { break; }
      Result = conn_parse_numeric_unaway(ConnS, PrefixPT, LinePT, Numeric, ParamsPT, Params);
      break;
    case IRC_RFC1459_ERR_NONICKNAMEGIVEN:
    case IRC_UNDERNET_RPL_NICKTOOFAST:
      if (!Conn_IsWelcome(ConnS)) { break; }
      Conn_ClearSentNick(ConnS);
      Conn_ClearSentAwayNick(ConnS);
      break;
    case IRC_RFC1459_ERR_ERRONEUSNICKNAME:
    case IRC_RFC1459_ERR_NICKCOLLISION:
    case IRC_RFC1459_ERR_NICKNAMEINUSE:
      CONN_PARSE_NUMERIC_CHECKPARAMS(3);
      Result = conn_parse_numeric_nextnick(ConnS, PrefixPT, LinePT, Numeric, ParamsPT, Params);
      break;
    case IRC_RFC1459_RPL_ISON:
      if (!Conn_IsWelcome(ConnS)) { break; }
      CONN_PARSE_NUMERIC_CHECKPARAMS(2);
      Result = conn_parse_numeric_ison(ConnS, PrefixPT, LinePT, Numeric, ParamsPT, Params);
      break;
    case IRC_RFC1459_RPL_NAMEREPLY:
      if (!Conn_IsWelcome(ConnS)) { break; }
      CONN_PARSE_NUMERIC_CHECKPARAMS(4);
      Result = conn_parse_numeric_namereply(ConnS, PrefixPT, LinePT, Numeric, ParamsPT, Params);
      break;
    case IRC_RFC1459_RPL_ENDOFNAMES:
      if (!Conn_IsWelcome(ConnS)) { break; }
      CONN_PARSE_NUMERIC_CHECKPARAMS(2);
      Result = conn_parse_numeric_endofnames(ConnS, PrefixPT, LinePT, Numeric, ParamsPT, Params);
      break;
    case IRC_RFC1459_RPL_WHOREPLY:
      if (!Conn_IsWelcome(ConnS)) { break; }
      CONN_PARSE_NUMERIC_CHECKPARAMS(8);
      Result = conn_parse_numeric_whoreply(ConnS, PrefixPT, LinePT, Numeric, ParamsPT, Params);
      break;
    case IRC_RFC1459_RPL_ENDOFWHO:
      if (!Conn_IsWelcome(ConnS)) { break; }
      CONN_PARSE_NUMERIC_CHECKPARAMS(2);
      Result = conn_parse_numeric_endofwho(ConnS, PrefixPT, LinePT, Numeric, ParamsPT, Params);
      break;
  }

  return(Result);

}

/* CONN_PARSE_NUMERIC_WELCOME FUNCTION - JONAS (18.07.2001) */

CONN_PARSE_NUMERIC(conn_parse_numeric_welcome) {

  char *TextPT = NULL;
  char *DumbPT = NULL;
  unsigned short int TextIndex = 0;
  unsigned short int TextLen = 1;
  unsigned short int Len = 0;
  unsigned short int Count = 0;

  if (Numeric == 1) {

    unsigned short int Index = 0;

    Conn_SetWelcome(ConnS);
    ConnS->ServerName = CONN_PARSE_STRREALLOC(ConnS->ServerName, PrefixPT);
    ConnS->IRCNick = CONN_PARSE_STRREALLOC(ConnS->IRCNick, ParamsPT[0]);
    for (Index = 0 ; Index <= 4 ; Index++) { FREE(ConnS->Welcome[Index]); }
    if (Conn_IsSentAwayNick(ConnS)) { Conn_SetAwayNick(ConnS); }
    else { Conn_ClearAwayNick(ConnS); }
    Conn_ClearSentNick(ConnS);
    Conn_ClearSentAwayNick(ConnS);
    conn_addsendq(ConnS, "MODE %s %s", ConnS->IRCNick, ConnS->Mode);
    conn_addsendq(ConnS, "JOIN %s", ConnS->Chans);
    client_noticealluser(ConnS->User, "Connection %s: Successfully connected on %s(%s):%ld \"%s\" as %s.", ConnS->Name, ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH, ConnS->ServerName, ConnS->IRCNick);

    if ((ConnS->NumClients <= 0) && (ConnConf_IsAutoAway(ConnS)) && (!Conn_IsAway(ConnS))) { conn_addsendq(ConnS, "AWAY :%s", ConnS->AwayMsg); }

  }

  for (Count = 1 ; Count < Params ; Count++) {
    Len = strlen(ParamsPT[Count]);
    if (Count > 1) { Len++; }
    if ((Count == (Params - 1)) && (strchr(ParamsPT[Count], ' ') != NULL)) { ++Len; }
    TextLen += Len;
    DumbPT = realloc(TextPT, TextLen);
    if (DumbPT == NULL) {
      free(TextPT);
      if (Conn_IsWelcome(ConnS)) { conn_quit(ConnS, "Memory allocation failure: [%d] %s", errno, strerror(errno)); }
      else { conn_disconnect(ConnS, "Connection %s: Memory allocation failure: [%d] %s", ConnS->Name, errno, strerror(errno)); }
      return(FALSE);
    }
    TextPT = DumbPT;
    DumbPT += TextIndex;
    if (Count > 1) { strcpy(DumbPT, " "); DumbPT++; }
    if ((Count == (Params - 1)) && (strchr(ParamsPT[Count], ' ') != NULL)) { strcpy(DumbPT, ":"); DumbPT++; }
    strcpy(DumbPT, ParamsPT[Count]);
    TextIndex += Len;
  }

  ConnS->Welcome[Numeric] = CONN_PARSE_STRREALLOC(ConnS->Welcome[Numeric], TextPT);
  FREE(TextPT);

  return(TRUE);

}

/* CONN_PARSE_NUMERIC_ISUPPORT FUNCTION - JONAS (18.07.2001) */

CONN_PARSE_NUMERIC(conn_parse_numeric_isupport) {

  char *TextPT = NULL;
  char *TempPT = NULL;
  char *ISupportPT = NULL;
  unsigned short int TextIndex = 0;
  unsigned short int TextLen = 1;
  unsigned short int Len = 0;
  unsigned short int Index = 0;
  char **ISupportTempPT = NULL;
  unsigned short int ISupportIndex = 0;

  for (Index = 1 ; Index < Params ; Index++) {

    /* Gather ISupport info that can be used internally */

    ISupportPT = strdup(ParamsPT[Index]);
    TempPT = ISupportPT;
    while (TempPT[0] == ' ') { ++TempPT; }
    if (TempPT[0] != '\0') {

      char *OptionPT = NULL;
      char *ValuePT = NULL;

      OptionPT = TempPT;
      TempPT = strchr(TempPT, '=');
      if (TempPT == NULL) { ValuePT = ""; }
      else {
        TempPT[0] = '\0';
        ++TempPT;
        while (TempPT[0] == ' ') { ++TempPT; }
        if (TempPT == '\0') { ValuePT = ""; }
        else { ValuePT = TempPT; }
      }
      if (strcasecmp(OptionPT, "NETWORK") == FALSE) {
        if (strcasecmp(ValuePT, "IRCNET") == FALSE) { ConnS->ISupport.Networks |= IRC_NETWORK_IRCNET; }
        else if (strcasecmp(ValuePT, "EFNET") == FALSE) { ConnS->ISupport.Networks |= IRC_NETWORK_EFNET; }
        else if (strcasecmp(ValuePT, "UNDERNET") == FALSE) { ConnS->ISupport.Networks |= IRC_NETWORK_UNDERNET; }
        else if (strcasecmp(ValuePT, "DALNET") == FALSE) { ConnS->ISupport.Networks |= IRC_NETWORK_DALNET; }
        else if (strcasecmp(ValuePT, "FREENODE") == FALSE) { ConnS->ISupport.Networks |= IRC_NETWORK_FREENODE; }
        TempPT = strrealloc(ConnS->ISupport.Network, ValuePT);
        if (TempPT != NULL) { ConnS->ISupport.Network = TempPT; }
      }
      else if (strcasecmp(OptionPT, "MODES") == FALSE) { ConnS->ISupport.MaxModes = atoi(ValuePT); }
      else if (strcasecmp(OptionPT, "MAXCHANNELS") == FALSE) { ConnS->ISupport.MaxChans = atoi(ValuePT); }
      else if (strcasecmp(OptionPT, "MAXBANS") == FALSE) { ConnS->ISupport.MaxBans = atoi(ValuePT); }
      else if (strcasecmp(OptionPT, "NICKLEN") == FALSE) { ConnS->ISupport.NickLen = atoi(ValuePT); }
      else if (strcasecmp(OptionPT, "AWAYLEN") == FALSE) { ConnS->ISupport.AwayMsgLen = atoi(ValuePT); }
      else if (strcasecmp(OptionPT, "TOPICLEN") == FALSE) { ConnS->ISupport.TopicLen = atoi(ValuePT); }
      else if (strcasecmp(OptionPT, "KICKLEN") == FALSE) { ConnS->ISupport.KickMsgLen = atoi(ValuePT); }
      else if (strcasecmp(OptionPT, "CHANMODES") == FALSE) {
        for (; *ValuePT != '\0' ; ValuePT++) {
          switch (*ValuePT) {
            case 'a': /* ANONYMOUS */
              ConnISupport_SetAnonymous(ConnS);
              break;
            case 'i': /* INVITE-ONLY */
              ConnISupport_SetInviteOnly(ConnS);
              break;
            case 'm': /* MODERATED */
              ConnISupport_SetModerated(ConnS);
              break;
            case 'n': /* NO OUTSIDE USER MESSAGES */
              ConnISupport_SetNoOutSide(ConnS);
              break;
            case 'q': /* QUIET */
              ConnISupport_SetQuiet(ConnS);
              break;
            case 'p': /* PRIVATE */
              ConnISupport_SetPrivate(ConnS);
              break;
            case 's': /* SECRET */
              ConnISupport_SetSecret(ConnS);
              break;
            case 'r': /* REOP */
              ConnISupport_SetReOP(ConnS);
              break;
            case 't': /* ONLY OP TOPIC */
              ConnISupport_SetOnlyOPTopic(ConnS);
              break;
            case 'k': /* KEY */
              ConnISupport_SetKey(ConnS);
              break;
            case 'l': /* LIMIT */
              ConnISupport_SetLimit(ConnS);
              break;
            case 'b': /* BAN */
              ConnISupport_SetBan(ConnS);
              break;
            case 'e': /* EXCEPTION */
              ConnISupport_SetException(ConnS);
              break;
            case 'I': /* INVITATION */
              ConnISupport_SetInvitation(ConnS);
              break;
            case 'O': /* CREATOR */
              ConnISupport_SetCreator(ConnS);
              break;
            case 'v': /* VOICE */
              ConnISupport_SetVoice(ConnS);
              break;
            case 'o': /* OPERATOR */
              ConnISupport_SetOperator(ConnS);
              break;
            default:
              break;
          }
        }
      }
    }
    free(ISupportPT);

    /* Create "fake" ISupport lines that can be sent to the IRC client */

    Len = strlen(ParamsPT[Index]);
    if (Index > 1) { Len++; }
    if ((Index == Params-1) && (strchr(ParamsPT[Index], ' ') != NULL)) { Len++; }
    TextLen += Len;
    TempPT = realloc(TextPT, TextLen);
    if (TempPT == NULL) {
      free(TextPT);
      conn_quit(ConnS, "Memory allocation failure: [%d] %s", errno, strerror(errno));
      return(FALSE);
    }
    TextPT = TempPT;
    TempPT += TextIndex;
    if (Index > 1) { strcpy(TempPT, " "); TempPT++; }
    if ((Index == Params-1) && (strchr(ParamsPT[Index], ' ') != NULL)) { strcpy(TempPT, ":"); TempPT++; }
    strcpy(TempPT, ParamsPT[Index]);
    TextIndex += Len;
  }

  ++ConnS->ISupportLines;
  ISupportTempPT = realloc(ConnS->ISupport_String, (sizeof(char *) * ConnS->ISupportLines));
  if (ISupportTempPT == NULL) {
    free(TextPT);
    conn_quit(ConnS, "Memory allocation failure: [%d] %s", errno, strerror(errno));
    return(FALSE);
  }
  ConnS->ISupport_String = ISupportTempPT;
  ISupportIndex = ConnS->ISupportLines - 1;
  ConnS->ISupport_String[ISupportIndex] = TextPT;

  DEBUGPRINT(BITMASK_DEBUG_CONN, "Connection: %s ISUPPORT Network: %s MaxModes: %d MaxChans: %d MaxBans: %d, NickLen: %d ChanTypes: %s Prefix: %s ChanModes: %s\n", ConnS->Name, ConnS->ISupport.Network, ConnS->ISupport.MaxModes, ConnS->ISupport.MaxChans, ConnS->ISupport.MaxBans, ConnS->ISupport.NickLen, ConnS->ISupport.ChanTypes, ConnS->ISupport.Prefix, ConnS->ISupport.ChanModes);

  return(TRUE);

}

/* CONN_PARSE_NUMERIC_NOWAWAY FUNCTION - JONAS (18.07.2001) */

CONN_PARSE_NUMERIC(conn_parse_numeric_nowaway) {

  Conn_SetAway(ConnS);

  return(TRUE);

}

/* CONN_PARSE_NUMERIC_UNAWAY FUNCTION - JONAS (18.07.2001) */

CONN_PARSE_NUMERIC(conn_parse_numeric_unaway) {

  Conn_ClearAway(ConnS);

  return(TRUE);

}

/* CONN_PARSE_NUMERIC_NEXTNICK FUNCTION - JONAS (18.07.2001) */

CONN_PARSE_NUMERIC(conn_parse_numeric_nextnick) {

  assert(ConnS->Nick != NULL);

  FREE(IRCNICK);
  if ((Conn_IsSentNick(ConnS)) || (Conn_IsSentAwayNick(ConnS))) {
    if (Conn_IsSentNick(ConnS)) {
      ConnS->NicksIndex = irc_nextnick(ConnS->Nick, ConnS->NicksIndex, ConnS->ISupport.NickLen);
      ++ConnS->NicksIndex;
    }
    else if (Conn_IsSentAwayNick(ConnS)) {
      ConnS->NicksIndex = irc_nextnick(ConnS->AwayNick, ConnS->NicksIndex, ConnS->ISupport.NickLen);
      ++ConnS->NicksIndex;
    }
    DEBUGPRINT(BITMASK_DEBUG_CONN, "Connection %s: %s: Server said: %s: Trying new nickname \"%s\".", ConnS->Name, ParamsPT[1], ParamsPT[2], IRCNICK);
    conn_addsendq(ConnS, "NICK %s", IRCNICK);
  }

  return(TRUE);

}

/* CONN_PARSE_NUMERIC_ISON FUNCTION - JONAS (18.07.2001) */

CONN_PARSE_NUMERIC(conn_parse_numeric_ison) {

  if (Conn_IsSentISON(ConnS)) {

    char *P_NicksPT = ParamsPT[1];
    unsigned short int Found = FALSE;

    Conn_ClearSentISON(ConnS);

    while (P_NicksPT != NULL) {
      char *P_NickPT = strtok(P_NicksPT, " ");
      if (P_NickPT == NULL) { break; }
      P_NicksPT = strtok(NULL, "\0");
      if ((Conn_IsAwayNick(ConnS)) && (strcmp(P_NickPT, ConnS->AwayNick) == FALSE)) { Found = TRUE; }
      else if (strcmp(P_NickPT, ConnS->Nick) == FALSE) { Found = TRUE; }
    }

    if (Found == FALSE) {
      if (Conn_IsAwayNick(ConnS)) {
        Conn_SetSentAwayNick(ConnS);
        ConnS->NicksIndex = 0;
        conn_addsendq(ConnS, "NICK %s", ConnS->AwayNick);
      }
      else {
        Conn_SetSentNick(ConnS);
        ConnS->NicksIndex = 0;
        conn_addsendq(ConnS, "NICK %s", ConnS->Nick);
      }
    }
    return(FALSE);
  }
  return(TRUE);

}

/* CONN_PARSE_NUMERIC_NAMEREPLY FUNCTION - JONAS (18.07.2001) */

CONN_PARSE_NUMERIC(conn_parse_numeric_namereply) {

  char *P_ChanPT = ParamsPT[2];
  char *P_NicksPT = ParamsPT[3];
  char *P_NickPT = NULL;
  struct Chan_Struct *ChanS = NULL;
  struct ChanUser_Struct *ChanUser = NULL;
  char Mode = 0;

  ChanS = chan_get(ConnS, P_ChanPT);
  if (ChanS == NULL) { return(TRUE); }

  while (P_NicksPT != NULL) {
    P_NickPT = strtok(P_NicksPT, " ");
    if (P_NickPT == NULL) { break; }
    P_NicksPT = strtok(NULL, "\0");
    Mode = '\0';
    if (P_NickPT[0] == '+') { Mode = 'v'; P_NickPT++; }
    else if (P_NickPT[0] == '@') { Mode = 'o'; P_NickPT++; }
    FAKELOOP {
      unsigned short int NUHLen = strlen(P_NickPT) + 16;
      char NUH[NUHLen+1];
      snprintf(NUH, NUHLen+1, "%s!unknown@unknown", P_NickPT);
      ChanUser = chan_adduser(ConnS, ChanS, P_NickPT, "unknown", "unknown", NUH);
      if (ChanUser == NULL) {
        CONN_PARSE_NUMERIC_ERROR("chan_adduser");
        return(FALSE);
      }
    }
    if (Mode == 'v') { ChanUser_SetVoice(ChanUser); }
    if (Mode == 'o') { ChanUser_SetOP(ChanUser); }
  }

  return(TRUE);

}

/* CONN_PARSE_NUMERIC_ENDOFNAMES FUNCTION - JONAS (18.07.2001) */

CONN_PARSE_NUMERIC(conn_parse_numeric_endofnames) {

  char *ChanPT = ParamsPT[1];

  struct Chan_Struct *ChanS = NULL;

  ChanS = chan_get(ConnS, ChanPT);
  if (ChanS == NULL) { return(TRUE); }
  Chan_SetNames(ChanS);

  return(TRUE);

}

/* CONN_PARSE_NUMERIC_WHOREPLY FUNCTION - JONAS (18.07.2001) */

CONN_PARSE_NUMERIC(conn_parse_numeric_whoreply) {

  char *ChanPT = ParamsPT[1];
  char *P_UserPT = ParamsPT[2];
  char *P_HostPT = ParamsPT[3];
  char *P_NickPT = ParamsPT[5];
  struct Chan_Struct *ChanS = NULL;
  struct ChanUser_Struct *ChanUser = NULL;
 
  ChanS = chan_get(ConnS, ChanPT);
  if (ChanS == NULL) { return(TRUE); }

  ChanUser = chan_getuser(ConnS, ChanS, P_NickPT);
  if (ChanUser != NULL) {
    ChanUser->User = CONN_PARSE_STRREALLOC(ChanUser->User, P_UserPT);
    ChanUser->Host = CONN_PARSE_STRREALLOC(ChanUser->Host, P_HostPT);
    FAKELOOP {
     char NUH[strlen(ChanUser->Nick)+1+strlen(ChanUser->User)+1+strlen(ChanUser->Host)+1];
     sprintf(NUH, "%s!%s@%s", ChanUser->Nick, ChanUser->User, ChanUser->Host);
     ChanUser->NUH = CONN_PARSE_STRREALLOC(ChanUser->NUH, NUH);
    }
    if (!ChanUser_IsWho(ChanUser)) {
      ChanUser_SetWho(ChanUser);
      ChanS->UserWhos++;
    }
  }

  if (ChanS->Who_Head != NULL) {
    if (ChanS->Who_Head->ClientS != NULL) { client_addsend(ChanS->Who_Head->ClientS, "%s", LinePT); }
    return(FALSE);
  }

  return(TRUE);

}

/* CONN_PARSE_NUMERIC_ENDOFWHO FUNCTION - JONAS (18.07.2001) */

CONN_PARSE_NUMERIC(conn_parse_numeric_endofwho) {

  char *ChanPT = ParamsPT[1];
  struct Chan_Struct *ChanS = NULL;

  ChanS = chan_get(ConnS, ChanPT);
  if (ChanS == NULL) { return(TRUE); }
  Chan_ClearSentWho(ChanS);
  Chan_SetWho(ChanS);

  if (ChanS->Who_Head) {
    struct Who_Struct *WhoS = ChanS->Who_Head;
    ChanS->Who_Head = WhoS->NextForChan;
    if (WhoS->PrevForClient != NULL) { WhoS->PrevForClient->NextForClient = WhoS->NextForClient; }
    if (WhoS->NextForClient != NULL) { WhoS->NextForClient->PrevForClient = WhoS->PrevForClient; }
    if (WhoS->ClientS != NULL) {
      if (WhoS->ClientS->Who_Head == WhoS) { WhoS->ClientS->Who_Head = WhoS->NextForClient; }
      client_addsend(WhoS->ClientS, "%s", LinePT);
    }
    free(WhoS);
    return(FALSE);
  }

  return(TRUE);

}

/* CONN_PARSE_EVENT FUNCTION - JONAS (17.07.2001) */

unsigned short int conn_parse_event(struct Conn_Struct *ConnS, const char *PrefixPT, const char *const CommandPT, char **ParamsPT, const unsigned short int Params) {

  const char *NUHPT = PrefixPT;
  unsigned short int NUHLen = strlen(NUHPT);
  char P_NUH[NUHLen+1];
  char *P_NUHPT = P_NUH;
  char *NickPT = NULL;
  char *UserPT = NULL;
  char *HostPT = NULL;
  unsigned short int Result = TRUE;

  strcpy(P_NUHPT, NUHPT);

  NickPT = P_NUHPT;

  if (P_NUHPT != NULL) {
    P_NUHPT = strchr(P_NUHPT, '!');
    if (P_NUHPT != NULL) {
      *P_NUHPT = '\0';
      P_NUHPT++;
      UserPT = P_NUHPT;
      if (UserPT == NULL) { UserPT = NickPT; }
    }
    else { UserPT = NickPT; }
  }
  else { UserPT = NickPT; }

  if (P_NUHPT != NULL) {
    P_NUHPT = strchr(P_NUHPT, '@');
    if (P_NUHPT != NULL) {
      *P_NUHPT = '\0';
      P_NUHPT++;
      HostPT = P_NUHPT;
      if (HostPT == NULL) { HostPT = NickPT; }
    }
    else { HostPT = NickPT; }
  }
  else { HostPT = NickPT; }

  if (strcasecmp(CommandPT, "PING") == FALSE) {
    CONN_PARSE_EVENT_CHECKPARAMS(1);
    Result = conn_parse_event_ping(ConnS, NickPT, UserPT, HostPT, NUHPT, CommandPT, ParamsPT, Params);
    return(Result);
  }
  else if (strcasecmp(CommandPT, "PONG") == FALSE) {
    CONN_PARSE_EVENT_CHECKPARAMS(1);
    Result = conn_parse_event_pong(ConnS, NickPT, UserPT, HostPT, NUHPT, CommandPT, ParamsPT, Params);
    return(Result);
  }
  else if (strcasecmp(CommandPT, "ERROR") == FALSE) {
    CONN_PARSE_EVENT_CHECKPARAMS(1);
    Result = conn_parse_event_error(ConnS, NickPT, UserPT, HostPT, NUHPT, CommandPT, ParamsPT, Params);
    return(Result);
  }
  else if (strcasecmp(CommandPT, "NICK") == FALSE) {
    if (!Conn_IsWelcome(ConnS)) { return(Result); }
    CONN_PARSE_EVENT_CHECKPARAMS(1);
    Result = conn_parse_event_nick(ConnS, NickPT, UserPT, HostPT, NUHPT, CommandPT, ParamsPT, Params);
    return(Result);
  }
  else if (strcasecmp(CommandPT, "JOIN") == FALSE) {
    if (!Conn_IsWelcome(ConnS)) { return(Result); }
    CONN_PARSE_EVENT_CHECKPARAMS(1);
    Result = conn_parse_event_join(ConnS, NickPT, UserPT, HostPT, NUHPT, CommandPT, ParamsPT, Params);
    return(Result);
  }
  else if (strcasecmp(CommandPT, "PART") == FALSE) {
    if (!Conn_IsWelcome(ConnS)) { return(Result); }
    CONN_PARSE_EVENT_CHECKPARAMS(1);
    Result = conn_parse_event_part(ConnS, NickPT, UserPT, HostPT, NUHPT, CommandPT, ParamsPT, Params);
    return(Result);
  }
  else if (strcasecmp(CommandPT, "QUIT") == FALSE) {
    if (!Conn_IsWelcome(ConnS)) { return(Result); }
    CONN_PARSE_EVENT_CHECKPARAMS(1);
    Result = conn_parse_event_quit(ConnS, NickPT, UserPT, HostPT, NUHPT, CommandPT, ParamsPT, Params);
    return(Result);
  }
  else if (strcasecmp(CommandPT, "KICK") == FALSE) {
    if (!Conn_IsWelcome(ConnS)) { return(Result); }
    CONN_PARSE_EVENT_CHECKPARAMS(2);
    Result = conn_parse_event_kick(ConnS, NickPT, UserPT, HostPT, NUHPT, CommandPT, ParamsPT, Params);
    return(Result);
  }
  else if (strcasecmp(CommandPT, "MODE") == FALSE) {
    if (!Conn_IsWelcome(ConnS)) { return(Result); }
    CONN_PARSE_EVENT_CHECKPARAMS(2);
    Result = conn_parse_event_mode(ConnS, NickPT, UserPT, HostPT, NUHPT, CommandPT, ParamsPT, Params);
    return(Result);
  }
  else if ((strcasecmp(CommandPT, "PRIVMSG") == FALSE) || (strcasecmp(CommandPT, "NOTICE") == FALSE) || (strcasecmp(CommandPT, "TOPIC") == FALSE)) {
    if (!Conn_IsWelcome(ConnS)) { return(Result); }
    CONN_PARSE_EVENT_CHECKPARAMS(2);
    Result = conn_parse_event_privmsg_notice_topic(ConnS, NickPT, UserPT, HostPT, NUHPT, CommandPT, ParamsPT, Params);
    return(Result);
  }

  return(TRUE);

}

/* CONN_PARSE_EVENT_PING FUNCTION - JONAS (18.07.2001) */

CONN_PARSE_EVENT(conn_parse_event_ping) {

  conn_addsendq(ConnS, "PONG :%s", ParamsPT[0]);
  return(FALSE);

}

/* CONN_PARSE_EVENT_PONG FUNCTION - JONAS (18.07.2001) */

CONN_PARSE_EVENT(conn_parse_event_pong) {

  Conn_ClearSentPing(ConnS);
  ConnS->SentPingTime = 0;

  return(FALSE);

}

/* CONN_PARSE_EVENT_ERROR FUNCTION - JONAS (18.07.2001) */

CONN_PARSE_EVENT(conn_parse_event_error) {

  client_noticealluser(ConnS->User, "Connection %s: Received ERROR: \"%s\" from server %s(%s):%ld \"%s\"", ConnS->Name, ParamsPT[0], ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH, ConnS->ServerName);

  return(FALSE);

}

/* CONN_PARSE_EVENT_NICK FUNCTION - JONAS (18.07.2001) */

CONN_PARSE_EVENT(conn_parse_event_nick) {

  struct Chan_Struct *ChanS = NULL;
  struct ChanUser_Struct *ChanUser = NULL;

  if (ConnS->IRCNick == NULL) { conn_quit(ConnS, "%s: Internal error: ConnS->IRCNick == NULL.", CommandPT); return(FALSE); }
  if (strcasecmp(ConnS->IRCNick, NickPT) == FALSE) {
    if (Conn_IsSentAwayNick(ConnS)) { Conn_SetAwayNick(ConnS); }
    else { Conn_ClearAwayNick(ConnS); }
    Conn_ClearSentNick(ConnS);
    Conn_ClearSentAwayNick(ConnS);
    ConnS->IRCNick = CONN_PARSE_STRREALLOC(ConnS->IRCNick, ParamsPT[0]);
  }

  for (ChanS = ConnS->Chan_Head ; ChanS != NULL ; ChanS = ChanS->Next) {
    ChanUser = chan_getuser(ConnS, ChanS, NickPT);
    if (ChanUser == NULL) { continue; }
    chan_changeuser(ConnS, ChanS, ChanUser, ParamsPT[0]);
    if (aerrno != SUCCESS) { CONN_PARSE_EVENT_ERROR("chan_changeuser"); }
    ChanUser->User = CONN_PARSE_STRREALLOC(ChanUser->User, UserPT);
    ChanUser->Host = CONN_PARSE_STRREALLOC(ChanUser->Host, HostPT);
    if (!ChanUser_IsWho(ChanUser)) {
      ChanUser_SetWho(ChanUser);
      ChanS->UserWhos++;
    }
    if ((ConnS->NumClients <= 0) && (!ChanUser_IsOP(ChanUser)) && (!ChanUser_IsSentOP(ChanUser)) && (!ChanUser_IsAutoOPCheck(ChanUser))) { chanuser_autoopcheck(ConnS, ChanS, ChanUser); }
  }

  return(TRUE);

}

/* CONN_PARSE_EVENT_JOIN FUNCTION - JONAS (18.07.2001) */

CONN_PARSE_EVENT(conn_parse_event_join) {

  struct Chan_Struct *ChanS = NULL;
  struct ChanUser_Struct *ChanUser = NULL;

  if (ConnS->IRCNick == NULL) { conn_quit(ConnS, "%s: Internal error", CommandPT); return(FALSE); }
  if (strcasecmp(ConnS->IRCNick, NickPT) == FALSE) {
    ChanS = chan_add(ConnS, ParamsPT[0]);
    if (ChanS == NULL) { CONN_PARSE_EVENT_ERROR("chan_add"); }
    ChanUser = chan_adduser(ConnS, ChanS, NickPT, UserPT, HostPT, NUHPT);
    if (ChanUser == NULL) { CONN_PARSE_EVENT_ERROR("chan_adduser"); }
    if (!ChanUser_IsWho(ChanUser)) {
      ChanUser_SetWho(ChanUser);
      ChanS->UserWhos++;
    }
    ChanS->Me = ChanUser;
    return(TRUE);
  }
  ChanS = chan_get(ConnS, ParamsPT[0]);
  if (ChanS == NULL) { CONN_PARSE_EVENT_ERROR("chan_get"); }
  ChanUser = chan_adduser(ConnS, ChanS, NickPT, UserPT, HostPT, NUHPT);
  if (ChanUser == NULL) { CONN_PARSE_EVENT_ERROR("chan_adduser"); }
  if (!ChanUser_IsWho(ChanUser)) {
    ChanUser_SetWho(ChanUser);
    ChanS->UserWhos++;
  }
  if ((ConnS->NumClients <= 0) && (!ChanUser_IsOP(ChanUser)) && (!ChanUser_IsSentOP(ChanUser)) && (!ChanUser_IsAutoOPCheck(ChanUser))) { chanuser_autoopcheck(ConnS, ChanS, ChanUser); }

  return(TRUE);

}

/* CONN_PARSE_EVENT_PART FUNCTION - JONAS (18.07.2001) */

CONN_PARSE_EVENT(conn_parse_event_part) {

  struct Chan_Struct *ChanS = NULL;
  struct ChanUser_Struct *ChanUser = NULL;

  ChanS = chan_get(ConnS, ParamsPT[0]);
  if (ChanS == NULL) { return(TRUE); }
  ChanUser = chan_getuser(ConnS, ChanS, NickPT);
  if (ChanUser == NULL) { return(TRUE); }
  if (ChanUser_IsWho(ChanUser)) { ChanS->UserWhos--; }
  if (ChanUser == ChanS->Me) { chan_rem(ConnS, ChanS); return(TRUE); }
  chan_remuser(ConnS, ChanS, ChanUser);

  if ((ConnConf_IsChanCycle(ConnS)) && (ChanS->Users <= 1) && (ConnS->NumClients <= 0) && (ChanS->Me != NULL) && (!ChanUser_IsOP(ChanS->Me))) {
    conn_addsendq(ConnS, "PART %s", ChanS->Chan);
    conn_addsendq(ConnS, "JOIN %s", ChanS->Chan);
  }

  return(TRUE);

}

/* CONN_PARSE_EVENT_QUIT FUNCTION - JONAS (18.07.2001) */

CONN_PARSE_EVENT(conn_parse_event_quit) {

  struct Chan_Struct *ChanS = NULL;
  struct ChanUser_Struct *ChanUser = NULL;

  for (ChanS = ConnS->Chan_Head ; ChanS != NULL ; ChanS = ChanS->Next) {
    ChanUser = chan_getuser(ConnS, ChanS, NickPT);
    if (ChanUser == NULL) { continue; }
    if (ChanUser_IsWho(ChanUser)) { ChanS->UserWhos--; }
    chan_remuser(ConnS, ChanS, ChanUser);

    if ((ConnConf_IsChanCycle(ConnS)) && (ChanS->Users <= 1) && (ConnS->NumClients <= 0) && (ChanS->Me != NULL) && (!ChanUser_IsOP(ChanS->Me))) {
      conn_addsendq(ConnS, "PART %s", ChanS->Chan);
      conn_addsendq(ConnS, "JOIN %s", ChanS->Chan);
    }

  }

  return(TRUE);

}

/* CONN_PARSE_EVENT_KICK FUNCTION - JONAS (18.07.2001) */

CONN_PARSE_EVENT(conn_parse_event_kick) {

  struct Chan_Struct *ChanS = NULL;
  struct ChanUser_Struct *ChanUser = NULL;

  ChanS = chan_get(ConnS, ParamsPT[0]);
  if (ChanS == NULL) { return(TRUE); }
  ChanUser = chan_getuser(ConnS, ChanS, NickPT);
  if (ChanUser != NULL) {
    ChanUser->User = CONN_PARSE_STRREALLOC(ChanUser->User, UserPT);
    ChanUser->Host = CONN_PARSE_STRREALLOC(ChanUser->Host, HostPT);
    ChanUser->NUH = CONN_PARSE_STRREALLOC(ChanUser->NUH, NUHPT);
    if (!ChanUser_IsWho(ChanUser)) {
      ChanUser_SetWho(ChanUser);
      ChanS->UserWhos++;
    }
  }
  ChanUser = chan_getuser(ConnS, ChanS, ParamsPT[1]);
  if (ChanUser == NULL) { return(TRUE); }
  if (ChanUser_IsWho(ChanUser)) { ChanS->UserWhos--; }
  if (ChanUser == ChanS->Me) { chan_rem(ConnS, ChanS); return(TRUE); }
  chan_remuser(ConnS, ChanS, ChanUser);

  /* This is valid if the only OP on the channel kicks himself */
  if ((ConnConf_IsChanCycle(ConnS)) && (ChanS->Users <= 1) && (ConnS->NumClients <= 0) && (ChanS->Me != NULL) && (!ChanUser_IsOP(ChanS->Me))) {
    conn_addsendq(ConnS, "PART %s", ChanS->Chan);
    conn_addsendq(ConnS, "JOIN %s", ChanS->Chan);
  }

  return(TRUE);

}

/* CONN_PARSE_EVENT_MODE FUNCTION - JONAS (18.07.2001) */

CONN_PARSE_EVENT(conn_parse_event_mode) {

  char *TargetPT = ParamsPT[0];

  char *ModeCharsPT = ParamsPT[1];
  char **ModeParamsPT = (ParamsPT + 2);

  char ModeChar = 0;
  char *ModeParamPT = NULL;

  unsigned short int ModeCharNumber = strlen(ModeCharsPT);
  unsigned short int ModeCharCount = 0;
  unsigned short int ModeCharIndex = 0;

  unsigned short int ModeParamNumber = (Params - 2);
  unsigned short int ModeParamCount = 0;
  unsigned short int ModeParamIndex = 0;

  char ModeChange = 0;

  struct Chan_Struct *ChanS = NULL;
  struct ChanUser_Struct *ChanUser = NULL;

  ChanS = chan_get(ConnS, TargetPT);
  if (ChanS == NULL) { return(TRUE); }

  ChanUser = chan_getuser(ConnS, ChanS, NickPT);
  if (ChanUser != NULL) {
    ChanUser->User = CONN_PARSE_STRREALLOC(ChanUser->User, UserPT);
    ChanUser->Host = CONN_PARSE_STRREALLOC(ChanUser->Host, HostPT);
    ChanUser->NUH = CONN_PARSE_STRREALLOC(ChanUser->NUH, NUHPT);
    ChanUser_SetWho(ChanUser); 
  }

  for (ModeCharCount = 1, ModeCharIndex = 0 ; ModeCharCount <= ModeCharNumber ; ModeCharCount++) {

    struct ChanUser_Struct *P_ChanUser = NULL;

    ModeChar = ModeCharsPT[ModeCharIndex];
    ModeCharIndex++;
    if ((ModeChar == '+') || (ModeChar == '-')) { ModeChange = ModeChar; continue; }
    switch (ModeChar) {
      case 'v':
      case 'o':
      case 'O':
        ModeParamCount++;
        if (ModeParamCount > ModeParamNumber) { conn_quit(ConnS, "Too few params for %s", CommandPT); return(FALSE); }
        ModeParamPT = ModeParamsPT[ModeParamIndex];
        ModeParamIndex++;
        P_ChanUser = chan_getuser(ConnS, ChanS, ModeParamPT);
        if (P_ChanUser == NULL) { break; }
        if (ModeChange == '+') {
          switch (ModeChar) {
            case 'v':
              ChanUser_SetVoice(P_ChanUser);
              break;
            case 'o':
              ChanUser_SetOP(P_ChanUser);
              break;
            case 'O':
               ChanUser_SetCreator(P_ChanUser);
               break;
            default:
              break;
          }
        }
        else if (ModeChange == '-') {
          switch (ModeChar) {
            case 'v':
              ChanUser_ClearVoice(P_ChanUser);
              break;
            case 'o':
              ChanUser_ClearOP(P_ChanUser);
              break;
            case 'O':
              ChanUser_ClearCreator(P_ChanUser);
              break;
            default:
              break;
          }
        }
        break;
      case 'I':
      case 'e':
      case 'b':
        ModeParamCount++;
        ModeParamIndex++;
        break;
      default:
        break;
    }
  }
  return(TRUE);

}

/* CONN_PARSE_EVENT_PRIVMSG_NOTICE_TOPIC FUNCTION - JONAS (18.07.2001) */

CONN_PARSE_EVENT(conn_parse_event_privmsg_notice_topic) {

  if (irc_isvalidchan(ParamsPT[0]) == FALSE) {
    if (*ParamsPT[0] == '@') { return(TRUE); }
    if (ConnS->NumClients > 0) { return(TRUE); }
    else {

      char *MaskPT = NULL;
      struct Ignore_Struct *IgnoreS = NULL;
      char *CTCPCmdPT = NULL;
      char *MessagePT = NULL;
      signed long int Result = 0;

      if (ConnConf_IsLogging(ConnS)) {
        if (Params >= 2) { MessagePT = ParamsPT[1]; }
        else { MessagePT = ""; }
        Result = conn_log(ConnS, NUHPT, CommandPT, MessagePT);
      }

      MaskPT = irc_nuhmask(NickPT, UserPT, HostPT, CONN_IGNORENICKMASK, CONN_IGNOREUSERMASK, CONN_IGNOREHOSTMASK);
      assert(MaskPT != NULL);

      IgnoreS = conn_addignore(ConnS, MaskPT);
      if (IgnoreS == NULL) { return(TRUE); }
      IgnoreS->ExpireTime = NOW + CONN_IGNORETTL;
      if (aerrno == AEEXISTS) { return(TRUE); }

      if ((ConnS->NickServNUH != NULL) && (strwm(ConnS->NickServNUH, NUHPT) == TRUE)) {
        conn_addsendq(ConnS, "PRIVMSG %s :IDENTIFY %s", NickPT, ConnS->NickServPass);
        return(FALSE);
      }

      if ((strcmp(CommandPT, "PRIVMSG") == FALSE) && (MessagePT[0] == 1) && (MessagePT[strlen(MessagePT)-1] == 1)) {
        ++MessagePT;
        MessagePT[strlen(MessagePT)-1] = '\0';
        CTCPCmdPT = strtok(MessagePT, " ");
        if (CTCPCmdPT == NULL) { return(FALSE); }
        MessagePT = strtok(NULL, "\0");
        if (strcmp(CTCPCmdPT, "VERSION") == FALSE) { conn_addsendq(ConnS, "NOTICE %s :\1VERSION %s v%s - %s %s %s - %s\1", NickPT, SHORTNAME, VERSION, OSNAME, OSRELEASE, PLATFORM, URL); }
        else if (strcmp(CTCPCmdPT, "PING") == FALSE) {
          if (MessagePT == NULL) { conn_addsendq(ConnS, "NOTICE %s :\1PING\1", NickPT); }
          else { conn_addsendq(ConnS, "NOTICE %s :\1PING %s\1", NickPT, MessagePT); }
        }
        return(FALSE);
      }

      if ((ConnConf_IsLogging(ConnS)) && (Result == SUCCESS)) { conn_addsendq(ConnS, "%s %s :I'm currently not here, your message has been logged.", CommandPT, NickPT); }
      else { conn_addsendq(ConnS, "%s %s :I'm currently not here, messages are not logged.", CommandPT, NickPT); }
    }
  }
  else {

    struct Chan_Struct *ChanS = NULL;
    struct ChanUser_Struct *ChanUser = NULL;

    ChanS = chan_get(ConnS, ParamsPT[0]);
    if (ChanS == NULL) { return(TRUE); }
    ChanUser = chan_getuser(ConnS, ChanS, NickPT);
    if (ChanUser == NULL) { return(TRUE); }
    ChanUser->User = CONN_PARSE_STRREALLOC(ChanUser->User, UserPT);
    ChanUser->Host = CONN_PARSE_STRREALLOC(ChanUser->Host, HostPT);
    ChanUser->NUH = CONN_PARSE_STRREALLOC(ChanUser->NUH, NUHPT);
    if (!ChanUser_IsWho(ChanUser)) {
      ChanUser_SetWho(ChanUser);
      ChanS->UserWhos++;
    }

    if ((ConnS->NumClients <= 0) && (!ChanUser_IsOP(ChanUser)) && (!ChanUser_IsSentOP(ChanUser)) && (!ChanUser_IsAutoOPCheck(ChanUser))) { chanuser_autoopcheck(ConnS, ChanS, ChanUser); }
  }
  return(TRUE);

}


syntax highlighted by Code2HTML, v. 0.9.1