/*
 * ----------------------------------------------------------------
 * Night Light IRC Proxy - Channel User
 * ----------------------------------------------------------------
 * 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 (03.12.2007)
 *
 */

#define CHAN_USER_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 "chan.h"
#include "chan_user.h"
#include "chan_mode.h"

#include "autoop_conf.h"

/* VARIABLES - JONAS (30.07.2000) */

static CHANUSER_HASHMEMS ChanUser_Hash_Map[CHANUSER_HASHMAPSIZE];
static CHANUSER_HASHMEMS ChanUser_Hash_Weight_Table[CHAR_MAX - CHAR_MIN + 1];

extern struct AutoOPConf_Struct *AutoOPConf_Head;

extern struct Conn_Struct *Conn_Head;

/* CHAN_ADDUSER FUNCTION - JONAS (26.07.2001) */

struct ChanUser_Struct *chan_adduser(struct Conn_Struct *ConnS, struct Chan_Struct *ChanS, const char *const NickPT, const char *const UserPT, const char *const HostPT, const char *const NUHPT) {

  struct ChanUser_Struct *ChanUser = NULL;
  struct ChanUser_Struct *ChanUser_NEW = NULL;
  CHANUSER_HASHREGS Hash = 0;

  assert(ConnS != NULL);
  assert(ChanS != NULL);
  assert(NickPT != NULL);
  assert(UserPT != NULL);
  assert(HostPT != NULL);
  assert(NUHPT != NULL);

  ChanUser = chan_getuser(ConnS, ChanS, NickPT);
  if (ChanUser != NULL) {
    aerrno = AEEXISTS;
    return(ChanUser);
  }

  ChanUser_NEW = malloc(sizeof(struct ChanUser_Struct));
  if (ChanUser_NEW == NULL) {
    aerrno = AEMALLOC;
    return(NULL);
  }

  memset(ChanUser_NEW, 0, sizeof(struct ChanUser_Struct));
  chan_inituser(ConnS, ChanS, ChanUser_NEW);

  ChanUser_NEW->Nick = strdup(NickPT);
  ChanUser_NEW->User = strdup(UserPT);
  ChanUser_NEW->Host = strdup(HostPT);
  ChanUser_NEW->NUH = strdup(NUHPT);
  ChanUser_NEW->Time = NOW;

  if ((ChanUser_NEW->Nick == NULL) || (ChanUser_NEW->User == NULL) || (ChanUser_NEW->Host == NULL) || (ChanUser_NEW->NUH == NULL)) {
    free(ChanUser_NEW->Nick);
    free(ChanUser_NEW->User);
    free(ChanUser_NEW->Host);
    free(ChanUser_NEW);
    aerrno = AEMALLOC;
    return(NULL);
  }

  if (ChanS->User_Head == NULL) {
    ChanS->User_Head = ChanUser_NEW;
    ChanS->User_Tail = ChanUser_NEW;
  }
  else {
    ChanUser = ChanS->User_Tail;
    ChanUser->Next = ChanUser_NEW;
    ChanUser_NEW->Prev = ChanUser;
    ChanS->User_Tail = ChanUser_NEW;
  }

  Hash = chanuser_strhash(ChanUser_NEW->Nick);

  ChanUser = ChanS->User_Table[Hash];
  if (ChanUser != NULL) { ChanUser->PrevH = ChanUser_NEW; }

  ChanUser_NEW->NextH = ChanUser;
  ChanS->User_Table[Hash] = ChanUser_NEW;

  ChanS->Users++;

  aerrno = AESUCCESS;
  return(ChanUser_NEW);

}

/* CHAN_CHANGEUSER FUNCTION - JONAS (26.07.2001) */

signed short int chan_changeuser(struct Conn_Struct *ConnS, struct Chan_Struct *ChanS, struct ChanUser_Struct *ChanUser, const char *const NickPT) {

  struct ChanUser_Struct *ChanUserH = NULL;
  CHANUSER_HASHREGS Hash = 0;
  CHANUSER_HASHREGS NewHash = 0;

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

  Hash = chanuser_strhash(ChanUser->Nick);
  NewHash = chanuser_strhash(NickPT);

  for (ChanUserH = ChanS->User_Table[Hash] ; ((ChanUserH != NULL) && (ChanUserH != ChanUser)) ; ChanUserH = ChanUserH->NextH);
  assert(ChanUserH == ChanUser);

  if (ChanUserH->PrevH != NULL) { ChanUserH->PrevH->NextH = ChanUserH->NextH; }
  if (ChanUserH->NextH != NULL) { ChanUserH->NextH->PrevH = ChanUserH->PrevH; }
  if (ChanS->User_Table[Hash] == ChanUserH) { ChanS->User_Table[Hash] = ChanUserH->NextH; }

  ChanUserH = ChanS->User_Table[NewHash];
  if (ChanUserH != NULL) { ChanUserH->PrevH = ChanUser; }
  ChanUser->NextH = ChanUserH;
  ChanS->User_Table[NewHash] = ChanUser;

  ChanUser->Nick = strrealloc(ChanUser->Nick, NickPT);
  if (aerrno != AESUCCESS) {
    return(ERROR);
  }
  FAKELOOP {
    unsigned long int NUHLen = strlen(NickPT) + strlen(ChanUser->User) + strlen(ChanUser->Host) + 2;
    char NUH[NUHLen+1];
    snprintf(NUH, NUHLen+1, "%s!%s@%s", NickPT, ChanUser->User, ChanUser->Host);
    ChanUser->NUH = strrealloc(ChanUser->NUH, NUH);
    if (aerrno != AESUCCESS) {
      return(ERROR);
    }
  }

  aerrno = AESUCCESS;
  return(SUCCESS);

}

/* CHAN_REMUSER FUNCTION - JONAS (26.07.2001) */

void chan_remuser(struct Conn_Struct *ConnS, struct Chan_Struct *ChanS, struct ChanUser_Struct *ChanUser) {

  CHANUSER_HASHREGS Hash = 0;
  struct ChanUser_Struct *ChanUserH = NULL;

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

  if (ChanUser == ChanS->Me) ChanS->Me = NULL;

  if (ChanUser->Prev == NULL) { ChanS->User_Head = ChanUser->Next; }
  else { ChanUser->Prev->Next = ChanUser->Next; }

  if (ChanUser->Next == NULL) { ChanS->User_Tail = ChanUser->Prev; }
  else { ChanUser->Next->Prev = ChanUser->Prev; }

  Hash = chanuser_strhash(ChanUser->Nick);
  for (ChanUserH = ChanS->User_Table[Hash] ; ((ChanUserH != NULL) && (ChanUserH != ChanUser)) ; ChanUserH = ChanUserH->NextH);
  assert(ChanUserH == ChanUser);

  if (ChanUserH->PrevH != NULL) { ChanUserH->PrevH->NextH = ChanUserH->NextH; }
  if (ChanUserH->NextH != NULL) { ChanUserH->NextH->PrevH = ChanUserH->PrevH; }

  if (ChanS->User_Table[Hash] == ChanUserH) { ChanS->User_Table[Hash] = ChanUserH->NextH; }

  chan_inituser(ConnS, ChanS, ChanUser);

  free(ChanUser);

  ChanS->Users--;

}

/* CHAN_GETUSER FUNCTION - JONAS (26.07.2001) */

struct ChanUser_Struct *chan_getuser(struct Conn_Struct *ConnS, struct Chan_Struct *ChanS, const char *const NickPT) {

  CHANUSER_HASHREGS Hash = 0;
  struct ChanUser_Struct *ChanUser = NULL;
  struct ChanUser_Struct *ChanUserH = NULL;

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

  Hash = chanuser_strhash(NickPT);
  for (ChanUser = ChanS->User_Table[Hash] ; ((ChanUser != NULL) && (strcasecmp(NickPT, ChanUser->Nick) != FALSE)) ; ChanUser = ChanUser->NextH);

  if (ChanUser == NULL) {
    aerrno = AENOMATCH;
    return(NULL);
  }

  if (ChanUser != ChanS->User_Table[Hash]) {
    if (ChanUser->PrevH != NULL) { ChanUser->PrevH->NextH = ChanUser->NextH; }
    if (ChanUser->NextH != NULL) { ChanUser->NextH->PrevH = ChanUser->PrevH; }
    ChanUserH = ChanS->User_Table[Hash];
    ChanUserH->PrevH = ChanUser;
    ChanUser->PrevH = NULL;
    ChanUser->NextH = ChanUserH;
    ChanS->User_Table[Hash] = ChanUser;
  }

  aerrno = AESUCCESS;
  return(ChanUser);

}

/* CHANUSER_HASH_INIT FUNCTION - JONAS (26.07.2001) */

void chanuser_hash_init(void) {

  signed short int Index1 = 0;
  signed short int Index2 = 0;

  unsigned long Count1 = 0;
  unsigned long Count2 = 0;

  for (Index1 = CHAR_MIN ; Index1 <= CHAR_MAX ; Index1++) {
    ChanUser_Hash_Weight(Index1) = (CHANUSER_HASHMEMS) (CHANUSER_HASHSTEP * ((char) Index1));
  }

  for (Index1 = CHAR_MIN ; Index1 <= CHAR_MAX ; Index1++) {
    for (Index2 = CHAR_MIN ; Index2 < Index1 ; Index2++) {
      if (HASHEQ(Index1, Index2)) {
        ChanUser_Hash_Weight(Index1) = ChanUser_Hash_Weight(Index2);
      }
    }
  }

  for (Count1 = 0 ; Count1 < (unsigned long) CHANUSER_HASHMAPSIZE ; Count1++) {

    Count2 = Count1;
    Count2 *= (unsigned long) CHANUSER_HASHSHIFT;
    Count2 %= (unsigned long) CHANUSER_HASHSIZE;
    ChanUser_Hash_Map[Count1] = (CHANUSER_HASHMEMS) Count2;

  }

}

/* CHANUSER_STRHASH FUNCTION - JONAS (26.07.2001) */

static CHANUSER_HASHREGS chanuser_strhash(const char *NickPT) {

  CHANUSER_HASHREGS Hash = ChanUser_Hash_Weight(*NickPT);

  NickPT++;

  while (*NickPT != '\0') {
    Hash = ChanUser_Hash_Map[Hash] + ChanUser_Hash_Weight(*NickPT);
    NickPT++;
  }

  return(ChanUser_Hash_Map[Hash]);

}

/* CHAN_INITUSER FUNCTION - JONAS (05.07.2000) */

void chan_inituser(struct Conn_Struct *ConnS, struct Chan_Struct *ChanS, struct ChanUser_Struct *ChanUser) {

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

  FREE(ChanUser->Nick);
  FREE(ChanUser->User);
  FREE(ChanUser->Host);
  FREE(ChanUser->NUH);

}

/* CHAN_DESTROYUSERS FUNCTION - JONAS (30.07.2001) */

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

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

  while (ChanS->User_Head != NULL) { chan_remuser(ConnS, ChanS, ChanS->User_Head); }
  assert(ChanS->Users == 0);

}

/* CHANUSER_AUTOOPCHECK FUNCTION - JONAS (27.12.2007) */

void chanuser_autoopcheck(struct Conn_Struct *ConnS, struct Chan_Struct *ChanS, struct ChanUser_Struct *ChanUser) {

  struct AutoOPConf_Struct *AutoOPConf = NULL;
  struct AutoOPConfChan_Struct *AutoOPConfChan = NULL;

  for (AutoOPConf = AutoOPConf_Head ; AutoOPConf != NULL ; AutoOPConf = AutoOPConf->Next) {
    if (strwm(AutoOPConf->NUH, ChanUser->NUH) != TRUE) { continue; }
    if (strwm(AutoOPConf->Connection, ConnS->Name) != TRUE) { continue; }
    for (AutoOPConfChan = AutoOPConf->Chan_Head ; AutoOPConfChan != NULL ; AutoOPConfChan = AutoOPConfChan->Next) {
      if (strwm(AutoOPConfChan->Chan, ChanS->Chan) != TRUE) { continue; }
      chan_op(ConnS, ChanS, ChanUser);
      ChanUser_SetAutoOPCheck(ChanUser);
      return;
    }
  }

  ChanUser_SetAutoOPCheck(ChanUser);

}

/* CHAN_OP FUNCTION - JONAS (05.07.2000) */

void chan_op(struct Conn_Struct *ConnS, struct Chan_Struct *ChanS, struct ChanUser_Struct *ChanUser) {

  struct Conn_Struct *P_ConnS = NULL;
  struct Chan_Struct *P_ChanS = NULL;
  struct ChanUser_Struct *P_ChanUser = NULL;
  char Mode[IRCMODELINELEN+1] = "";

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

  assert(ChanUser_IsOP(ChanS->Me));
  assert(!ChanUser_IsOP(ChanUser));
  assert(!ChanUser_IsSentOP(ChanUser));

  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; }
    P_ChanUser = chan_getuser(P_ConnS, P_ChanS, ChanUser->Nick);
    if (P_ChanUser == NULL) { continue; }
    if ((strcasecmp(ChanUser->User, P_ChanUser->User) != FALSE) || (strcasecmp(ChanUser->Host, P_ChanUser->Host) != FALSE)) { continue; }
    if (ChanUser_IsSentOP(P_ChanUser)) { return; }
  }

  DEBUGPRINT(BITMASK_DEBUG_CONN, "Connection %s is requesting OP for %s on %s.", ConnS->Name, ChanUser->Nick, ChanS->Chan);

  ChanUser_SetSentOP(ChanUser);
  ChanUser->SentOPTime = NOW;
  snprintf(Mode, IRCMODELINELEN+1, "+o %s", ChanUser->Nick);
  chan_addmode(ConnS, ChanS, Mode, CHANMODEPRIOOP, 0);

}


syntax highlighted by Code2HTML, v. 0.9.1