/*
* ----------------------------------------------------------------
* 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