/*

oer - IRC bot

See ftp://nic.funet.fi/pub/unix/irc/docs/FAQ.gz section 11 for the
definition of the word bot.

Copyright (C) 2000-2001 EQU <equ@equnet.org>

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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

#ifdef HAVE_STRINGS_H
  #include <strings.h>
#endif

#include <string.h>

#ifdef HAVE_CRYPT_H
  #include <crypt.h>
#endif

#ifdef HAVE_CTYPE_H 
  #include <ctype.h> 
#endif

#ifdef HAVE_LOCALE_H
  #include <locale.h>
#endif

#include <errno.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <pwd.h>
#include <signal.h>

#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>

#ifdef TIME_WITH_SYS_TIME
  #include <sys/time.h>
  #include <time.h>
#else
  #ifdef HAVE_SYS_TIME_H
    #include <sys/time.h>
  #else
    #include <time.h>
  #endif
#endif

#include <sys/wait.h>

enum NTHMODE_RETURN_VALUES {
	OER_NTHMODE_OP,
	OER_NTHMODE_DOP,
	OER_NTHMODE_VOICE,
	OER_NTHMODE_DVOICE,
	OER_NTHMODE_BAN,
	OER_NTHMODE_UNBAN,
	OER_NTHMODE_UNKNOWN
};

enum PUBMSG_FLOOD_RETURN_VALUES {
	OER_PUBMSG_FLOOD_NORMAL,
	OER_PUBMSG_FLOOD_REPEAT,
	OER_PUBMSG_FLOOD_NONE
};

enum PARSEIRC_RETURN_VALUES {
	OER_PARSEIRC_ERR_WRITE,
	OER_PARSEIRC_ERR_PARSE,
	OER_PARSEIRC_EVERYTHING_OK
};

enum RECONNECT_TYPES {
	OER_RECONNECT_NONE,
	OER_RECONNECT_ADMIN,
	OER_RECONNECT_STONED,
	OER_RECONNECT_ERROR
};

enum WHICHCTCP_RETURN_VALUES {
	OER_WHICHCTCP_ACTION = 1,
	OER_WHICHCTCP_FINGER,
	OER_WHICHCTCP_PING,
	OER_WHICHCTCP_USERINFO,
	OER_WHICHCTCP_VERSION,
	OER_WHICHCTCP_INVALID
};

enum WHICHCOMMAND_RETURN_VALUES {
	OER_WHICHCOMMAND_TOPIC_SET,
	OER_WHICHCOMMAND_TOPIC_ADD,
	OER_WHICHCOMMAND_TOPIC_DEL,
	OER_WHICHCOMMAND_TOPIC_EDIT,
	OER_WHICHCOMMAND_TOPIC_INS,
	OER_WHICHCOMMAND_TOPIC_LIST,
	OER_WHICHCOMMAND_TOPIC_GET,
	OER_WHICHCOMMAND_TOPIC_REFRESH,
	OER_WHICHCOMMAND_TOPIC_SWAP,
	OER_WHICHCOMMAND_LOCK,
	OER_WHICHCOMMAND_LOCKU,
	OER_WHICHCOMMAND_UNLOCK,
	OER_WHICHCOMMAND_RANDOM_KICK,
	OER_WHICHCOMMAND_RANDOM_BANKICK,
	OER_WHICHCOMMAND_SYNC,
	OER_WHICHCOMMAND_SYNCALL,
	OER_WHICHCOMMAND_JUMP,
	OER_WHICHCOMMAND_QUIT,
	OER_WHICHCOMMAND_OP,
	OER_WHICHCOMMAND_DOP,
	OER_WHICHCOMMAND_VOICE,
	OER_WHICHCOMMAND_BAN,
	OER_WHICHCOMMAND_UNBAN,
	OER_WHICHCOMMAND_BANKICK,
	OER_WHICHCOMMAND_KICK,
	OER_WHICHCOMMAND_INVITE,
	OER_WHICHCOMMAND_MODE,
	OER_WHICHCOMMAND_MASSMESSAGE,
	OER_WHICHCOMMAND_HELP,
	OER_WHICHCOMMAND_SAVE,
	OER_WHICHCOMMAND_LIST,
	OER_WHICHCOMMAND_INFO,
	OER_WHICHCOMMAND_RAW,
	OER_WHICHCOMMAND_UPTIME,
	OER_WHICHCOMMAND_ADD,
	OER_WHICHCOMMAND_DEL,
	OER_WHICHCOMMAND_EDIT,
	OER_WHICHCOMMAND_SAY,
	OER_WHICHCOMMAND_LAST,
	OER_WHICHCOMMAND_QUOTE,
	OER_WHICHCOMMAND_FLUSH,
	OER_WHICHCOMMAND_SEEN,
	OER_WHICHCOMMAND_NSTATS,
	OER_WHICHCOMMAND_ACTION,
	OER_WHICHCOMMAND_LOGON,
	OER_WHICHCOMMAND_LOGOFF,
	OER_WHICHCOMMAND_WALL,
	OER_WHICHCOMMAND_INVALID
};

enum TIMED_TYPES {
	OER_TIMED_IRC_COMMAND,
	OER_TIMED_INTERNAL_COMMAND
};

enum TIMED_FUNCTION {
	OER_TIMED_IRC_COMMAND_KICK,
	OER_TIMED_IRC_COMMAND_DUMMY,
	OER_TIMED_INTERNAL_COMMAND_DUMMY
};

enum PROCESSMMODES_RETURN_VALUES {
	OER_PROCESSMMODES_ERR_WRITE,
	OER_PROCESSMMODES_EVERYTHING_OK
};

enum PROCESSTIMEDS_RETURN_VALUES {
	OER_PROCESSTIMEDS_ERR_WRITE,
	OER_PROCESSTIMEDS_EVERYTHING_OK
};

enum ESTABLISHCONNECTION_RETURN_VALUES {
	OER_ESTABLISHCONNECTION_EVERYTHING_OK
};

enum SERVERCONNECTION_RETURN_VALUES {
	OER_SERVERCONNECTION_ERR_SOCKET,
	OER_SERVERCONNECTION_ERR_GETHOSTNAME,
	OER_SERVERCONNECTION_ERR_GETHOSTBYNAME,
	OER_SERVERCONNECTION_ERR_GETHOSTBYNAME_RETRY,
	OER_SERVERCONNECTION_ERR_GETSERVER_RETRY,
	OER_SERVERCONNECTION_ERR_BIND,
	OER_SERVERCONNECTION_ERR_CONNECT,
	OER_SERVERCONNECTION_ERR_CONNECT_RETRY,
	OER_SERVERCONNECTION_ALREADY_CONNECTED,
	OER_SERVERCONNECTION_EVERYTHING_OK
};

enum REGISTERCONNECTION_RETURN_VALUES {
	OER_REGISTERCONNECTION_ERR_GETPWUID,
	OER_REGISTERCONNECTION_ERR_WRITE,
	OER_REGISTERCONNECTION_ERR_HANDLESERVERDATA,
	OER_REGISTERCONNECTION_ALREADY_REGISTERED,
	OER_REGISTERCONNECTION_EVERYTHING_OK
};

enum REGISTERCONNECTION_STATUS {
	OER_REGISTERCONNECTION_STATUS_BEGIN,
	OER_REGISTERCONNECTION_STATUS_NICK,
	OER_REGISTERCONNECTION_STATUS_USER,
	OER_REGISTERCONNECTION_STATUS_DONE,
	OER_REGISTERCONNECTION_STATUS_ERROR
};

enum HANDLESERVERDATA_RETURN_VALUES {
	OER_HANDLESERVERDATA_ERR_READ,
	OER_HANDLESERVERDATA_ERR_NOTHING_READ,
	OER_HANDLESERVERDATA_ERR_WRITE,
	OER_HANDLESERVERDATA_EVERYTHING_OK
};

enum OER_DEBUG_LEVELS {
	OER_DEBUG_NONE = 0,
	OER_DEBUG_FATAL = 1,
	OER_DEBUG_ERROR = 2,
	OER_DEBUG_WARNING = 3,
	OER_DEBUG_INFO = 4,
	OER_DEBUG_FLOOD = 9
};

enum OER_ADVERT_TYPES {
	OER_ADVERT_TYPE_LAMERS,
	OER_ADVERT_TYPE_BOTUSERS_OPS,
	OER_ADVERT_TYPE_BOTUSERS_ADMINS,
	OER_ADVERT_TYPE_BOTUSERS_OTHER,
	OER_ADVERT_TYPE_INVALID
};

#define numofparams(x) (wordcount(x) - 1)

/* from glib.h */
#define ABS(a)         (((a) < 0) ? -(a) : (a))

#define OER_MAINLOOP_TIMEOUT_SECS 1
#define OER_MAINLOOP_TIMEOUT_USECS 0
/* only change this if you are 100% sure what you are doing */
#define OER_ACTIONS_PER_MAINLOOP 1
#define OER_NICKS_PER_USERHOST 5
/* multiple kicks aren't supported for backwards compatibility */
#define OER_NICKS_PER_KICK 1
#define OER_MAXTARGETS 20
#define OER_TARGET_DELAY 120
#define OER_WALL_DELAY 5
#define OER_DEFAULT_DELIM "::"
#define OER_DEFAULT_DELIM_LEN 2
#define OER_QUOTE_MIN_LENGTH 10
#define OER_QUOTE_MAX_LENGTH 200
#define OER_WAIT_BETWEEN_CONNECTION_ATTEMPTS 15
#define OER_JOINS 20
#define OER_PARTS 20
#define OER_PART_EXPIRE 1800
#define OER_BURST_JOINS OER_JOINS / 3
#define OER_BURST_JOINS_LIMIT OER_JOINS / 2
#define OER_DELAY_BETWEEN_REJOINS 10
#define OER_AUTO_REJOIN_TIME 3
#define OER_QUOTE_INTERVAL 300
#define OER_ALLOWED_PARTS OER_PARTS / 5
#define OER_RECONNECT_DELAY 30
#define OER_PUBMSGS 20
#define OER_TOPICS 10
#define OER_SERVER_IS_STONED_FOR 300
#define OER_MASSMODE_LIMIT 3
#define OER_LASTS 3
#define OER_LAST_MAX_LENGTH 120
#define OER_SEEN 3
#define OER_PERMBANS 25
#define OER_LOGON_TIMEOUT 1800
#define OER_BANVARS_AUTO_REJOIN 30
#define OER_BANVARS_PART_REJOIN 600
#define OER_BANVARS_PUBLIC_FLOOD 300
#define OER_BANVARS_PUBLIC_FLOOD_REPEAT 600
#define OER_BANVARS_BAD_WORD 3600
#define OER_BANVARS_BAD_NICK 3600
#define OER_BANVARS_NORMAL_BAN 3600
#define OER_SAVE_INTERVAL 3600
#define OER_ALLOWED_QUOTE_TIME_USEC 600000
#define Q_NICK "Q"
/* Define if you want to show server idle percentage */
/* #undef OER_UPTIME_SHOW_IDLE */
#define PUBMSG_FLOOD_LINES OER_PUBMSGS / 2
#define PUBMSG_FLOOD_CHARS OER_PUBMSGS * 80
#define PUBMSG_FLOOD_INTERVAL 60
#define PUBMSG_FLOOD_REPEAT 3
#define PUBMSG_FLOOD_REPEAT_EXPIRE 1800
#define TXT_BUFFER 1024
/* this is the ircd buffer size */
#define IRCD_BUFFER_LENGTH 512
#define WRITE_BUFFER_LENGTH IRCD_BUFFER_LENGTH
/* this could be larger, i don't see any reason why though */
#define READ_BUFFER_LENGTH IRCD_BUFFER_LENGTH
/* we leave 80 bytes for header strings */
#define WRITE_BUFFER_HEADER_LENGTH 80
#define MICROSTRINGLEN 16
#define TINYSTRINGLEN 32
#define MIDSTRINGLEN 64
#define STRINGLEN 128
#define BIGSTRINGLEN 256
#define HUGESTRINGLEN (WRITE_BUFFER_LENGTH - WRITE_BUFFER_HEADER_LENGTH)
#define NICKLEN 32
#define USERLEN 32
#define CHANLEN 64
#define FLAGLEN 64
#define HOSTLEN 256
#define TOPICLEN 160
#define OER_MAX_LIST_LINES 5
#define OER_HELP "For a complete command reference go to http://oer.equnet.org/help.html"
#define DEFAULT_NICKBK_MESSAGE "prohibited nick"
#define OER_REALNAME "http://oer.equnet.org/help.html"
#define OER_COPYRIGHT1 "oer version 1.0-38, Copyright (C) 2000-2001 EQU <equ@equnet.org>"
#define OER_COPYRIGHT2 "oer comes with ABSOLUTELY NO WARRANTY; for details see the\n\
GNU General Public License (COPYING) provided with this distribution.\n\
This is free software, and you are welcome to redistribute it under\n\
certain conditions; again see COPYING for details.\n"

struct floodvars {
	int repeat_expire;
	int repeat_limit;
	int interval;
	int lines;
	int chars;
};

struct banvars {
	int auto_rejoin;
	int part_rejoin;
	int public_flood;
	int public_flood_repeat;
	int bad_word;
	int bad_nick;
	int normal_ban;
};

struct locked {
	int locked;
	int unlocked;
	int auto_unlock;
	char reason[STRINGLEN + 1];
	char nick[NICKLEN + 1];
	char host[HOSTLEN + 1];
};

struct chanuser {
	char *nick;
	char *host;
	int hostquery;
	int ircop;
	int chanop;
	int voice;
	int tobek;
	struct chanuser *prev;
	struct chanuser *next;
};

struct botuser {
	char *handle;
	char *options;
	char password[TINYSTRINGLEN + 1];
	struct maskstruct *firstmask;
	struct botuser *prev;
	struct botuser *next;
};

struct authed {
	time_t at;
	struct botuser *user;
	char host[HOSTLEN + 1];
	struct authed *prev;
	struct authed *next;
};

struct topic {
	time_t at;
	int list;
	char *setby;
	char *topic;
	struct topic *prev;
	struct topic *next;
};

struct modeline {
	int done;
	time_t at;
	char *mode;
	char *target;
	struct modeline *prev;
	struct modeline *next;
};

struct timed {
	time_t at;
	int type;
	int function;
	char *irc_command;
	char *optstring1;
	char *optstring2;
	char *optstring3;
	struct timed *prev;
	struct timed *next;
};

struct ls_wrapstruct {
	time_t at;
	char *str1;
	char *str2;
	char *str3;
	struct ls_wrapstruct *prev;
	struct ls_wrapstruct *next;
};

struct pubmsg {
	time_t at;
	char *nick;
	char *host;
	char *message;
	struct pubmsg *prev;
	struct pubmsg *next;
};

struct join {
	time_t at;
	char *nick;
	char *host;
	struct join *prev;
	struct join *next;
};

struct part {
	time_t at;
	int valid;
	char *nick;
	char *host;
	struct part *prev;
	struct part *next;
};

struct maskstruct {
	char *mask;
	char *optstring;
	struct maskstruct *prev;
	struct maskstruct *next;
};

struct advert {
	char type[TINYSTRINGLEN + 1];
	char *message;
	struct advert *prev;
	struct advert *next;
};

struct server {
	char serverhost[HOSTLEN + 1];
	int serverport;
	int servermodes;
	int pingfrequency;
	int protected_ircops;
	int used;
	int connected;
	int registered;
	int stoned;
	int linenoise;
        char password[TINYSTRINGLEN + 1];
	time_t lastping;
	struct server *prev;
	struct server *next;
};

struct channel {
	char name[CHANLEN + 1];
	char mode[CHANLEN + 1];
	char key[CHANLEN + 1];
	char chanflags[FLAGLEN + 1];
	int haskey;
	int nickcount;
	int setchanmode;
	int joined;
	int joining;
	int i_am_op;
	int topic_change;
	int topic_list;
	int synced;
	time_t rejoin_at;
	time_t last_quote;
	FILE *seen;
	FILE *last;
	struct join *joins;
	struct part *parts;
	struct pubmsg *pubmsgs;
	struct modeline *modelines;
	struct locked locked;
        struct floodvars floodvars;
        struct banvars banvars;
	struct chanuser *nicks;
	struct maskstruct *permbans;
	struct maskstruct *nickbks;
	struct maskstruct *wordbks;
	struct botuser *users;
	struct authed *autheds;
	struct topic *topics;
	struct advert *adverts;
	struct channel *prev;
	struct channel *next;
};

struct state {
	char progname[STRINGLEN + 1];
	char nick[NICKLEN + 1];
	char altnick[NICKLEN + 1];
        char user[USERLEN + 1];
        char host[HOSTLEN + 1];
	char mode[CHANLEN + 1];
	char signoff[STRINGLEN + 1];
	char preferredserver[HOSTLEN + 1];
	char realname[STRINGLEN + 1];
	char flags[FLAGLEN + 1];
	char prefix[MICROSTRINGLEN + 1];
	int customuser;
	int vhost;
	int use_alt_nick;
	int sockfd;
	int loopforever;
	int newnick;
	int newmode;
	int netjoining;
	int bailout;
	int quitting;
	int reconnect;
	int kickrs;
	time_t now;
	time_t last_actions;
	time_t startup;
	time_t postnj_checks_at;
	time_t last_saved;
	FILE *logfp;
	FILE *logto;
	struct botuser *admins;
	struct channel *channels;
	struct maskstruct *trusted;
	struct maskstruct *kickreasons;
	struct server *servers;
	struct server *current_server;
	struct timed *timeds;
};
