/*
 * ----------------------------------------------------------------
 * Night Light IRC Proxy - Channel Mode
 * ----------------------------------------------------------------
 * 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 CHAN_MODE_C

#define NEED_SYS_TYPES_H 1		/* Extra types */
#define NEED_SYS_PARAM_H 1		/* Some systems need this */
#define NEED_LIMITS_H 1			/* 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.h"
#include "conn_sendq.h"

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

/* VARIABLES - JONAS (05.07.2000) */

extern struct Conn_Struct *Conn_Head;

/* CHAN_ADDMODE FUNCTION - JONAS (31.07.2001) */

struct ChanMode_Struct *chan_addmode(struct Conn_Struct *ConnS, struct Chan_Struct *ChanS, const char *const ModePT, const unsigned short int Priority, const unsigned short int Delay) {

  struct ChanMode_Struct *ChanMode = NULL;
  struct ChanMode_Struct *ChanMode_NEW = NULL;

  assert(ConnS != NULL);
  assert(ChanS != NULL);
  assert(ModePT != NULL);

  assert(ChanUser_IsOP(ChanS->Me));

  FAKELOOP {

    unsigned short int ModeLen = strlen(ModePT);
    char Mode1[ModeLen+1];
    char Mode2[ModeLen+1];

    strcpy(Mode1, ModePT);
    strcpy(Mode2, ModePT);

    if (Mode1[0] == '+') { Mode2[0] = '-'; }
    else if (Mode1[0] == '-') { Mode2[0] = '+'; }
    else {
      aerrno = AEINVALID;
      return(NULL);
    }

    ChanMode = chan_getmode(ConnS, ChanS, Mode1);
    if (ChanMode != NULL) {
      aerrno = AEEXISTS;
      return(ChanMode);
    }

    ChanMode = chan_getmode(ConnS, ChanS, Mode2);
    if (ChanMode != NULL) {
      chan_remmode(ConnS, ChanS, ChanMode);
      aerrno = AESUCCESS;
      return(NULL);
    }

    ChanMode_NEW = malloc(sizeof(struct ChanMode_Struct));
    if (ChanMode_NEW == NULL) {
      aerrno = AEMALLOC;
      return(NULL);
    }

    memset(ChanMode_NEW, 0, sizeof(struct ChanMode_Struct));
    chan_initmode(ConnS, ChanS, ChanMode_NEW);

    ChanMode_NEW->Mode = strdup(Mode1);
    if (ChanMode_NEW->Mode == NULL) {
      free(ChanMode_NEW);
      aerrno = AEMALLOC;
      return(NULL);
    }
    ChanMode_NEW->Priority = Priority;
    ChanMode_NEW->Delay = Delay;
    ChanMode_NEW->Time = NOW;

  }

  if (ChanS->Mode_Head == NULL) {
    ChanS->Mode_Head = ChanMode_NEW;
    ChanS->Mode_Tail = ChanMode_NEW;
  }
  else {
    for (ChanMode = ChanS->Mode_Head ; ChanMode != NULL ; ChanMode = ChanMode->Next) {
      if (ChanMode->Priority > Priority) {
        if (ChanMode->Prev == NULL) {

          /* (NULL | ChanMode_NEW | ChanMode_CUR | ...) */

          ChanS->Mode_Head = ChanMode_NEW;
          ChanMode_NEW->Next = ChanMode;
          ChanMode->Prev = ChanMode_NEW;

        }
        else {

          /* (... | ChanMode_PRV | ChanMode_NEW | ChanMode_CUR | ...) */

          ChanMode->Prev->Next = ChanMode_NEW;
          ChanMode_NEW->Prev = ChanMode->Prev;
          ChanMode_NEW->Next = ChanMode;
          ChanMode->Prev = ChanMode_NEW;

        }
        break;
      }
    }
    if (ChanMode == NULL) {

      /* (... | ChanMode_NEW (TAIL) | NULL) */

      ChanMode = ChanS->Mode_Tail;
      ChanMode->Next = ChanMode_NEW;
      ChanMode_NEW->Prev = ChanMode;
      ChanS->Mode_Tail = ChanMode_NEW;
    }
  }

  ChanS->Modes++;

  aerrno = AESUCCESS;
  return(ChanMode_NEW);

}

/* CHAN_REMMODE FUNCTION - JONAS (31.07.2001) */

void chan_remmode(struct Conn_Struct *ConnS, struct Chan_Struct *ChanS, struct ChanMode_Struct *ChanMode) {

  assert(ConnS != NULL);
  assert(ChanS != NULL);
  assert(ChanMode != NULL);

  if (ChanMode->Prev == NULL) { ChanS->Mode_Head = ChanMode->Next; }
  else { ChanMode->Prev->Next = ChanMode->Next; }

  if (ChanMode->Next == NULL) { ChanS->Mode_Tail = ChanMode->Prev; }
  else { ChanMode->Next->Prev = ChanMode->Prev; }

  chan_initmode(ConnS, ChanS, ChanMode);

  free(ChanMode);

  ChanS->Modes--;

}

/* CHAN_GETMODE FUNCTION - JONAS (05.07.2000) */

struct ChanMode_Struct *chan_getmode(struct Conn_Struct *ConnS, struct Chan_Struct *ChanS, const char *const ModePT) {

  struct ChanMode_Struct *ChanMode = NULL;

  assert(ConnS != NULL);
  assert(ChanS != NULL);
  assert(ModePT != NULL);

  for (ChanMode = ChanS->Mode_Head ; ChanMode != NULL ; ChanMode = ChanMode->Next) {
    if (strcasecmp(ChanMode->Mode, ModePT) == FALSE) {
      aerrno = AESUCCESS;
      return(ChanMode);
    }
  }
  aerrno = AENOMATCH;
  return(NULL);

}

/* CHAN_INITMODE FUNCTION - JONAS (05.07.2000) */

void chan_initmode(struct Conn_Struct *ConnS, struct Chan_Struct *ChanS, struct ChanMode_Struct *ChanMode) {

  assert(ConnS != NULL);
  assert(ChanS != NULL);
  assert(ChanMode != NULL);

  FREE(ChanMode->Mode);
  ChanMode->Time = 0;
  ChanMode->Priority = 0;
  ChanMode->Delay = 0;

}

/* CHAN_DESTROYMODES FUNCTION - JONAS (31.07.2001) */

void chan_destroymodes(struct Conn_Struct *ConnS, struct Chan_Struct *ChanS) {

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

  while (ChanS->Mode_Head != NULL) { chan_remmode(ConnS, ChanS, ChanS->Mode_Head); }
  assert(ChanS->Modes == 0);

}

/* CHAN_ADDSENTMODE FUNCTION - JONAS (31.07.2001) */

struct ChanSentMode_Struct *chan_addsentmode(struct Conn_Struct *ConnS, struct Chan_Struct *ChanS, const char *const ModePT) {

  struct ChanSentMode_Struct *ChanSentMode = NULL;
  struct ChanSentMode_Struct *ChanSentMode_NEW = NULL;

  assert(ConnS != NULL);
  assert(ChanS != NULL);
  assert(ModePT != NULL);

  ChanSentMode = chan_getsentmode(ConnS, ChanS, ModePT);
  if (ChanSentMode != NULL) {
    aerrno = AEEXISTS;
    return(ChanSentMode);
  }

  ChanSentMode_NEW = malloc(sizeof(struct ChanSentMode_Struct));
  if (ChanSentMode_NEW == NULL) {
    aerrno = AEMALLOC;
    return(NULL);
  }

  memset(ChanSentMode_NEW, 0, sizeof(struct ChanSentMode_Struct));
  chan_initsentmode(ConnS, ChanS, ChanSentMode_NEW);

  ChanSentMode_NEW->Mode = strdup(ModePT);
  if (ChanSentMode_NEW->Mode == NULL) {
    free(ChanSentMode_NEW->Mode);
    aerrno = AEMALLOC;
    return(NULL);
  }
  ChanSentMode_NEW->Time = NOW;

  if (ChanS->SentMode_Head == NULL) {
    ChanS->SentMode_Head = ChanSentMode_NEW;
    ChanS->SentMode_Tail = ChanSentMode_NEW;
  }
  else {
    ChanSentMode = ChanS->SentMode_Tail;
    ChanSentMode->Next = ChanSentMode_NEW;
    ChanSentMode_NEW->Prev = ChanSentMode;
    ChanS->SentMode_Tail = ChanSentMode_NEW;
  }

  ChanS->SentModes++;

  aerrno = AESUCCESS;
  return(ChanSentMode_NEW);

}

/* CHAN_REMSENTMODE FUNCTION - JONAS (31.07.2001) */

void chan_remsentmode(struct Conn_Struct *ConnS, struct Chan_Struct *ChanS, struct ChanSentMode_Struct *ChanSentMode) {

  assert(ConnS != NULL);
  assert(ChanS != NULL);
  assert(ChanSentMode != NULL);

  if (ChanSentMode->Prev == NULL) { ChanS->SentMode_Head = ChanSentMode->Next; }
  else { ChanSentMode->Prev->Next = ChanSentMode->Next; }

  if (ChanSentMode->Next == NULL) { ChanS->SentMode_Tail = ChanSentMode->Prev; }
  else { ChanSentMode->Next->Prev = ChanSentMode->Prev; }

  chan_initsentmode(ConnS, ChanS, ChanSentMode);

  free(ChanSentMode);

  ChanS->SentModes--;

}

/* CHAN_GETSENTMODESTRUCT FUNCTION - JONAS (05.07.2000) */

struct ChanSentMode_Struct *chan_getsentmode(struct Conn_Struct *ConnS, struct Chan_Struct *ChanS, const char *const ModePT) {

  struct ChanSentMode_Struct *ChanSentMode = NULL;

  assert(ConnS != NULL);
  assert(ChanS != NULL);
  assert(ModePT != NULL);

  for (ChanSentMode = ChanS->SentMode_Head ; ChanSentMode != NULL ; ChanSentMode = ChanSentMode->Next) {
    if (strcasecmp(ChanSentMode->Mode, ModePT) == FALSE) {
      aerrno = AESUCCESS;
      return(ChanSentMode);
    }
  }
  aerrno = AENOMATCH;
  return(NULL);

}

/* CHAN_INITSENTMODE FUNCTION - JONAS (05.07.2000) */

void chan_initsentmode(struct Conn_Struct *ConnS, struct Chan_Struct *ChanS, struct ChanSentMode_Struct *ChanSentMode) {

  assert(ConnS != NULL);
  assert(ChanS != NULL);
  assert(ChanSentMode != NULL);

  FREE(ChanSentMode->Mode);
  ChanSentMode->Time = 0;

}

/* CHAN_DESTROYSENTMODES FUNCTION - JONAS (31.07.2001) */

void chan_destroysentmodes(struct Conn_Struct *ConnS, struct Chan_Struct *ChanS) {

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

  while (ChanS->SentMode_Head != NULL) { chan_remsentmode(ConnS, ChanS, ChanS->SentMode_Head); }
  assert(ChanS->SentModes == 0);

}

/* CHAN_EXPIRESENTMODES FUNCTION - JONAS (31.07.2001) */

void chan_expiresentmodes(struct Conn_Struct *ConnS, struct Chan_Struct *ChanS) {

  struct ChanSentMode_Struct *ChanSentMode = NULL;
  struct ChanSentMode_Struct *ChanSentMode_DEL = NULL;
  time_t Duration = 0;
  char *ModePT = NULL;

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

  assert(ChanUser_IsOP(ChanS->Me));

  ChanS->CheckSentModesTime = NOW;

  for (ChanSentMode = ChanS->SentMode_Head ; ChanSentMode != NULL ;) {
    Duration = (NOW - ChanSentMode->Time);
    if (Duration >= CHANMODESENTMODETTL) {
#if 0
      sysprint(BITMASK_BOTCHANMODE, "No response in %s for mode %s on %s: Attempting to resend.", strduration(Duration), ChanSentMode->Mode, ChanS->Chan);
#endif
      ChanSentMode_DEL = ChanSentMode;
      ChanSentMode = ChanSentMode->Next;
      ModePT = strdup(ChanSentMode_DEL->Mode);
      chan_remsentmode(ConnS, ChanS, ChanSentMode_DEL);
      chan_mode(ConnS, ChanS, ModePT, CHANMODE_PRIOHIGH, 0);
      free(ModePT);
      continue;
    }
    ChanSentMode = ChanSentMode->Next;
  }

}

/* CHAN_MODE FUNCTION - JONAS (05.07.2000) */

signed short int chan_mode(struct Conn_Struct *ConnS, struct Chan_Struct *ChanS, const char *const ModePT, const unsigned short int Priority, const unsigned short int Delay) {

  struct Conn_Struct *P_ConnS = NULL;
  struct Chan_Struct *P_ChanS = NULL;
  struct ChanMode_Struct *ChanMode = NULL;
  struct ChanSentMode_Struct *ChanSentMode = NULL;

  assert(ConnS != NULL);
  assert(ChanS != NULL);
  assert(ModePT != NULL);

  assert(ChanUser_IsOP(ChanS->Me));

  for (P_ConnS = Conn_Head ; P_ConnS != NULL ; P_ConnS = P_ConnS->Next) {
    if (!Conn_IsWelcome(P_ConnS)) { continue; }
    P_ChanS = chan_get(P_ConnS, ChanS->Chan);
    if (P_ChanS == NULL) { continue; }
    if (!ChanUser_IsOP(P_ChanS->Me)) { continue; }
    ChanSentMode = chan_getsentmode(P_ConnS, P_ChanS, ModePT);
    if (ChanSentMode != NULL) { return(AEEXISTS); }
  }

  ChanSentMode = chan_addsentmode(ConnS, ChanS, ModePT);
  if (aerrno != AESUCCESS) { return(aerrno); }
  ChanMode = chan_addmode(ConnS, ChanS, ModePT, Priority, 0);
  if (aerrno != AESUCCESS) { return(aerrno); }

  return(SUCCESS);

}

/* CHAN_FLUSHMODES FUNCTION - JONAS (05.07.2000) */

void chan_flushmodes(struct Conn_Struct *ConnS, struct Chan_Struct *ChanS) {

  struct ChanMode_Struct *ChanMode = NULL;
  struct ChanMode_Struct *ChanMode_DEL = NULL;

  unsigned short int Count = 0;
  time_t Duration;

  char *ModeCharPT = NULL;
  char ModeCHR = 0;
  char ModeCNG = 0;
  char *ModeParamPT = NULL;
  char ModePChars[IRCMODECHARSLEN+1] = "";
  char ModeMChars[IRCMODECHARSLEN+1] = "";
  char ModePParams[IRCMODEPARAMSLEN+1] = "";
  char ModeMParams[IRCMODEPARAMSLEN+1] = "";
  unsigned short int ModePIndex = 0;
  unsigned short int ModeMIndex = 0;
  char Mode[IRCMODELINELEN+1] = "";

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

  assert(ChanUser_IsOP(ChanS->Me));

  ChanS->FlushModesTime = NOW;


  /* Grab the modes for one line but dont delete them from the mode queue until we know they can be flushed by SendQ --Jonas */

  for (ChanMode = ChanS->Mode_Head, Count = 0 ; ChanMode != NULL ; ChanMode = ChanMode->Next) {

    assert(ChanMode->Flushed == FALSE);

    Duration = (NOW - ChanMode->Time);
    if (Duration < ChanMode->Delay) { continue; }

    Count++;

    ModeCharPT = strtok(ChanMode->Mode, " ");
    ModeParamPT = strtok(NULL, " ");
    ModeCNG = ModeCharPT[0];
    ModeCHR = ModeCharPT[1];
    if (ModeCNG == '+') {
      ModePChars[ModePIndex] = ModeCHR;
      ModePIndex++;
      if (ModeParamPT != NULL) {
        if (strcmp(ModePParams, "") != FALSE) { strncat(ModePParams, " ", IRCMODEPARAMSLEN - strlen(ModePParams)); }
        strncat(ModePParams, ModeParamPT, IRCMODEPARAMSLEN - strlen(ModePParams));
      }
    }
    else if (ModeCNG == '-') {
      ModeMChars[ModeMIndex] = ModeCHR;
      ModeMIndex++;
      if (ModeParamPT != NULL) {
        if (strcmp(ModeMParams, "") != FALSE) { strncat(ModeMParams, " ", IRCMODEPARAMSLEN - strlen(ModeMParams)); }
        strncat(ModeMParams, ModeParamPT, IRCMODEPARAMSLEN - strlen(ModeMParams));
      }
    }

    ChanMode->Flushed = TRUE;
    if (Count >= ConnS->ISupport.MaxModes) { break; }
  }

  /* Compile a MODE-line from the minus and plus modes */

  snprintf(Mode, IRCMODELINELEN+1, "MODE %s ", ChanS->Chan);

  if (strlen(ModeMChars) > 0) {
    strncat(Mode, "-", IRCMODELINELEN - strlen(Mode));
    strncat(Mode, ModeMChars, IRCMODELINELEN - strlen(Mode));
  }
  if (strlen(ModePChars) > 0) {
    strncat(Mode, "+", IRCMODELINELEN - strlen(Mode));
    strncat(Mode, ModePChars, IRCMODELINELEN - strlen(Mode));
  }
  if (strlen(ModeMParams) > 0) {
    strncat(Mode, " ", IRCMODELINELEN - strlen(Mode));
    strncat(Mode, ModeMParams, IRCMODELINELEN - strlen(Mode));
  }
  if (strlen(ModePParams) > 0) {
    strncat(Mode, " ", IRCMODELINELEN - strlen(Mode));
    strncat(Mode, ModePParams, IRCMODELINELEN - strlen(Mode));
  }


  if ((strlen(Mode) > ConnS->SendQMaxFlushBuffer) || (ConnS->SendQMaxFlushLines < 1)) { /* There is no point in flushing ModeQ if SendQ can't flush the data immediately. --Jonas */
    for (ChanMode = ChanS->Mode_Head ; ChanMode != NULL ; ChanMode = ChanMode->Next) { ChanMode->Flushed = FALSE; }
    return;
  }

  /* Delete the modes from ModeQ we put in the "Mode" line --Jonas */

  for (ChanMode = ChanS->Mode_Head, Count = 1 ; ChanMode != NULL ; Count++) {
    if (ChanMode->Flushed != TRUE) { continue; }
    ChanMode_DEL = ChanMode;
    ChanMode = ChanMode->Next;
    chan_remmode(ConnS, ChanS, ChanMode_DEL);
    if (Count >= ConnS->ISupport.MaxModes) { break; }
  }

  /* Pass the "Mode" line to SendQ --Jonas */

  conn_addsendq(ConnS, "%s", Mode);

}



syntax highlighted by Code2HTML, v. 0.9.1