/*
 * ----------------------------------------------------------------
 * Night Light IRC Proxy - Connection SendQ Functions
 * ----------------------------------------------------------------
 * Copyright (C) 1997-2003 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 (07.07.2003)
 *
 */

#define CONN_SENDQ_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 "conf.h"

#include "access_conf.h"

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

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

/* VARIABLES - JONAS (31.07.2001) */

extern struct Conf_Struct ConfS;
extern struct Client_Struct *Client_Head;

/* CONN_ADDSENDQ FUNCTION - JONAS (01.07.2000) */

void conn_addsendq(struct Conn_Struct *ConnS, const char *const LinePT, ...) {

  char Line[IRCMSGLEN+1] = "";
  va_list Args = { 0 };
  struct SendQ_Struct *SendQ = NULL;
  struct SendQ_Struct *SendQ_NEW = NULL;
  struct SendQ_Struct *SendQ_PRV = NULL;
  struct Client_Struct *ClientS = NULL;

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

  va_start(Args, LinePT);
  vsnprintf(Line, IRCMSGLEN+1, LinePT, Args);
  va_end(Args);

  if (!Conn_IsSocket(ConnS)) {
    sysprint(BITMASK_ERROR, "conn_addsendq() called for connection %s without socket: %s.", ConnS->Name, Line);
    return;
  }

#if DEBUG
  client_noticealluser(ConnS->User, "Connection %s: Adding line \"%s\" to sendQ.", ConnS->Name, Line);
  client_noticealluser(ConnS->User, "Connection %s: Allowed buffer to be sent: %ld - Allowed number of lines to be sent: %ld.", ConnS->Name, ConnS->SendQMaxFlushBuffer, ConnS->SendQMaxFlushLines);
#endif

  SendQ_NEW = malloc(sizeof(struct SendQ_Struct));
  if (SendQ_NEW == NULL) { return; }

  memset(SendQ_NEW, 0, sizeof(struct SendQ_Struct));

  SendQ_NEW->Line = strdup(Line);
  if (SendQ_NEW->Line == NULL) {
    free(SendQ_NEW);
    return;
  }

  SendQ_NEW->Time = NOW;

  if (strncasecmp(Line, "PASS ", 5) == FALSE) { SendQ_NEW->Priority = 1; }
  else if (strncasecmp(Line, "NICK ", 5) == FALSE) { SendQ_NEW->Priority = 1; }
  else if (strncasecmp(Line, "USER ", 5) == FALSE) { SendQ_NEW->Priority = 1; }
  else if (strncasecmp(Line, "PONG ", 5) == FALSE) { SendQ_NEW->Priority = 1; }
  else if (strncasecmp(Line, "QUIT ", 5) == FALSE) { SendQ_NEW->Priority = 1; }

  else if (strncasecmp(Line, "JOIN ", 5) == FALSE) { SendQ_NEW->Priority = 2; }
  else if (strncasecmp(Line, "PART ", 5) == FALSE) { SendQ_NEW->Priority = 2; }
  else if (strncasecmp(Line, "ISON ", 5) == FALSE) { SendQ_NEW->Priority = 2; }
  else if (strncasecmp(Line, "MODE ", 5) == FALSE) { SendQ_NEW->Priority = 2; }
  else if (strncasecmp(Line, "OPER ", 5) == FALSE) { SendQ_NEW->Priority = 2; }
  else if (strncasecmp(Line, "KILL ", 5) == FALSE) { SendQ_NEW->Priority = 2; }
  else if (strncasecmp(Line, "NAMES ", 6) == FALSE) { SendQ_NEW->Priority = 2; }
  else if (strncasecmp(Line, "LINKS ", 6) == FALSE) { SendQ_NEW->Priority = 2; }
  else if (strncasecmp(Line, "WHO ", 4) == FALSE) { SendQ_NEW->Priority = 2; }
  else if (strncasecmp(Line, "WHOIS ", 6) == FALSE) { SendQ_NEW->Priority = 2; }
  else if (strncasecmp(Line, "AWAY ", 5) == FALSE) { SendQ_NEW->Priority = 2; }
  else if (strncasecmp(Line, "PING ", 5) == FALSE) { SendQ_NEW->Priority = 2; }
  else if (strncasecmp(Line, "USERHOST ", 9) == FALSE) { SendQ_NEW->Priority = 2; }

  else if (strncasecmp(Line, "KICK ", 5) == FALSE) { SendQ_NEW->Priority = 3; }

  else if (strncasecmp(Line, "PRIVMSG ", 8) == FALSE) { SendQ_NEW->Priority = 4; }
  else if (strncasecmp(Line, "NOTICE ", 7) == FALSE) { SendQ_NEW->Priority = 4; }
  else if (strncasecmp(Line, "INVITE ", 7) == FALSE) { SendQ_NEW->Priority = 4; }
  else if (strncasecmp(Line, "TOPIC ", 6) == FALSE) { SendQ_NEW->Priority = 4; }

  else { SendQ_NEW->Priority = 4; }

  if (ConnS->SendQ_Head == NULL) { ConnS->SendQ_Head = SendQ_NEW; }
  else {
    for (SendQ = ConnS->SendQ_Head ; SendQ != NULL ;) {
      if (SendQ->Priority > SendQ_NEW->Priority) {
        if (SendQ_PRV == NULL) {
          ConnS->SendQ_Head = SendQ_NEW;
          SendQ_NEW->Next = SendQ;
        }
        else {
          SendQ_PRV->Next = SendQ_NEW;
          SendQ_NEW->Next = SendQ;
        }
        break;
      }
      SendQ_PRV = SendQ;
      SendQ = SendQ->Next;
    }
    if (SendQ == NULL) { SendQ_PRV->Next = SendQ_NEW; }
  }

  ++ConnS->NumSendQs;

  if (ConnS->NumSendQs >= ConfS.ClientMaxSendQ) {
#if 0
    conn_quit(ConnS, ACCESS_CONF_SENDQFLOOD);
#endif
    for (ClientS = Client_Head ; ClientS != NULL ; ClientS = ClientS->Next) {
      if (!Client_IsVerified(ClientS)) { continue; }
      if (ClientS->ConnS != ConnS) { continue; }
      client_close(ClientS, ACCESS_CONF_SENDQFLOOD);
      access_conf_adddeny(ClientS->HostName, ACCESS_CONF_SENDQFLOOD);
    }
  }

}

/* CONN_FLUSHSENDQ FUNCTION - JONAS (24.06.2001) */

void conn_flushsendq(struct Conn_Struct *ConnS) {

  struct SendQ_Struct *SendQ = NULL;
  struct FlushL_Struct *FlushL = NULL;
  struct FlushB_Struct *FlushB = NULL;
  unsigned long int Duration = 0;
  unsigned short int Count = 0;
  unsigned short int Len = 0;
  unsigned short int FlushLen = 0;

  assert(ConnS != NULL);
  assert(ConnS->SendQ_Head != NULL);

  while (ConnS->FlushL_Head != NULL) {
    FlushL = ConnS->FlushL_Head;
    Duration = (NOW - FlushL->Time);
    if (Duration < ConnS->SendLineTime) { break; }
    ConnS->SendQMaxFlushLines += FlushL->Lines;

    ConnS->FlushL_Head = FlushL->Next;
    if (FlushL->Next == NULL) { ConnS->FlushL_Tail = NULL; }
    else { FlushL->Next->Prev = NULL; }

    free(FlushL);
    ConnS->NumFlushLs--;
  }

  while (ConnS->FlushB_Head != NULL) {
    FlushB = ConnS->FlushB_Head;
    Duration = (NOW - FlushB->Time);
    if (Duration < ConnS->SendBufferTime) { break; }
    ConnS->SendQMaxFlushBuffer += FlushB->Len;

    ConnS->FlushB_Head = FlushB->Next;
    if (FlushB->Next == NULL) { ConnS->FlushB_Tail = NULL; }
    else { FlushB->Next->Prev = NULL; }

    free(FlushB);
    ConnS->NumFlushBs--;
  }

  while ((ConnS->SendQ_Head != NULL) && (ConnS->SendQMaxFlushLines > 0) && (ConnS->SendQMaxFlushBuffer > 0)) {
    SendQ = ConnS->SendQ_Head;
    Len = strlen(SendQ->Line) + 2;
    if (Len > ConnS->SendQMaxFlushBuffer) { break; }
    ConnS->NumSendQs--;
    ConnS->SendQ_Head = ConnS->SendQ_Head->Next;
    conn_addsend(ConnS, SendQ->Line);
    ConnS->SendQMaxFlushLines--;
    ConnS->SendQMaxFlushBuffer -= Len;
    FlushLen += Len;
    free(SendQ->Line);
    free(SendQ);
    Count++;
  }

  if (Count > 0) {
    conn_addflushl(ConnS, Count);
    conn_addflushb(ConnS, FlushLen);
  }

}

/* CONN_INITSENDQ FUNCTION - JONAS (01.07.2000) */

void conn_initsendq(struct Conn_Struct *ConnS) {

  struct SendQ_Struct *SendQ = NULL;

  assert(ConnS != NULL);

  conn_initflushl(ConnS);
  conn_initflushb(ConnS);

  while (ConnS->SendQ_Head != NULL) {
    SendQ = ConnS->SendQ_Head;
    ConnS->SendQ_Head = ConnS->SendQ_Head->Next;
    free(SendQ->Line);
    free(SendQ);
    ConnS->NumSendQs--;
  }
  assert(ConnS->NumSendQs == 0);

  ConnS->SendQMaxFlushLines = ConnS->SendMaxLines;
  ConnS->SendQMaxFlushBuffer = ConnS->SendMaxBuffer;

}

/* CONN_ADDFLUSHL FUNCTION - JONAS (24.06.2001) */

void conn_addflushl(struct Conn_Struct *ConnS, const unsigned short int Lines) {

  struct FlushL_Struct *FlushL = NULL;
  struct FlushL_Struct *FlushL_NEW = NULL;

  assert(ConnS != NULL);

  FlushL_NEW = malloc(sizeof(struct FlushL_Struct));
  if (FlushL_NEW == NULL) { return; }

  memset(FlushL_NEW, 0, sizeof(struct FlushL_Struct));

  FlushL_NEW->Lines = Lines;
  FlushL_NEW->Time = NOW;

  if (ConnS->FlushL_Head == NULL) {
    ConnS->FlushL_Head = FlushL_NEW;
    ConnS->FlushL_Tail = FlushL_NEW;
  }
  else {
    FlushL = ConnS->FlushL_Tail;
    FlushL->Next = FlushL_NEW;
    FlushL_NEW->Prev = FlushL;
    ConnS->FlushL_Tail = FlushL_NEW;
  }

  ConnS->NumFlushLs++;

}

/* CONN_INITFLUSHL FUNCTION - JONAS (24.06.2001) */

void conn_initflushl(struct Conn_Struct *ConnS) {

  struct FlushL_Struct *FlushL = NULL;

  assert(ConnS != NULL);

  while (ConnS->FlushL_Head != NULL) {
    FlushL = ConnS->FlushL_Head;
    ConnS->FlushL_Head = ConnS->FlushL_Head->Next;
    free(FlushL);
    ConnS->NumFlushLs--;
  }
  ConnS->FlushL_Tail = NULL;
  assert(ConnS->NumFlushLs == 0);

  ConnS->SendQMaxFlushLines = ConnS->SendMaxLines;

}

/* CONN_ADDFLUSHB FUNCTION - JONAS (24.06.2001) */

void conn_addflushb(struct Conn_Struct *ConnS, const unsigned short int Len) {

  struct FlushB_Struct *FlushB = NULL;
  struct FlushB_Struct *FlushB_NEW = NULL;

  assert(ConnS != NULL);

  FlushB_NEW = malloc(sizeof(struct FlushB_Struct));
  if (FlushB_NEW == NULL) { return; }

  memset(FlushB_NEW, 0, sizeof(struct FlushB_Struct));

  FlushB_NEW->Len = Len;
  FlushB_NEW->Time = NOW;

  if (ConnS->FlushB_Head == NULL) {
    ConnS->FlushB_Head = FlushB_NEW;
    ConnS->FlushB_Tail = FlushB_NEW;
  }
  else {
    FlushB = ConnS->FlushB_Tail;
    FlushB->Next = FlushB_NEW;
    FlushB_NEW->Prev = FlushB;
    ConnS->FlushB_Tail = FlushB_NEW;
  }

  ConnS->NumFlushBs++;

}

/* CONN_INITFLUSHB FUNCTION - JONAS (24.06.2001) */

void conn_initflushb(struct Conn_Struct *ConnS) {

  struct FlushB_Struct *FlushB = NULL;

  assert(ConnS != NULL);

  while (ConnS->FlushB_Head != NULL) {
    FlushB = ConnS->FlushB_Head;
    ConnS->FlushB_Head = ConnS->FlushB_Head->Next;
    free(FlushB);
    ConnS->NumFlushBs--;
  }
  ConnS->FlushB_Tail = NULL;
  assert(ConnS->NumFlushBs == 0);

  ConnS->SendQMaxFlushBuffer = ConnS->SendMaxBuffer;

}


syntax highlighted by Code2HTML, v. 0.9.1