#ifdef RCS static char rcsid[]="$Id: setup.c,v 1.2 2001/03/13 05:04:47 holsta Exp $"; #endif /****************************************************************************** * Internetting Cooperating Programmers * ---------------------------------------------------------------------------- * * ____ PROJECT * | _ \ __ _ _ __ ___ ___ _ __ * | | | |/ _` | '_ \ / __/ _ \ '__| * | |_| | (_| | | | | (_| __/ | * |____/ \__,_|_| |_|\___\___|_| the IRC bot * * All files in this archive are subject to the GNU General Public License. * * $Source: /cvsroot/dancer/dancer/src/setup.c,v $ * $Revision: 1.2 $ * $Date: 2001/03/13 05:04:47 $ * $Author: holsta $ * $State: Exp $ * $Locker: $ * * --------------------------------------------------------------------------- *****************************************************************************/ #include #include /* does this work in all unixes? the offsetof() */ #include "dancer.h" #include "trio.h" #include "strio.h" #include "list.h" #include "function.h" #include "setup.h" #include "user.h" #include "command.h" #include "files.h" extern int floodrate, floodtime, floodrepeatrate, floodrepeattime; extern int floodbeeps, floodjoins; extern int logdays; extern ulong activelog; extern char *errfrom; /* this is like stderr, only for nicks */ extern itemident *current; int numretry; int opactionlen; int pubbantime; int oplevel; int netburp; /* Period after netsplit where FLOOD is off */ int defbantime; /* Default value if time is omitted in BAN */ int seenmonths; /* Max number of months to keep entries in seen file */ int tellmonths; /* Max number of months to keep entries in tell file */ int warnmonths; /* Max number of months to keep entries in tell file */ long climit; /* the +l limit, 0 disables */ long newsexpire; ulong linkvalue; /* "Public" key for linkbot */ bool lockmode; /* lock channel mode */ bool lockkey; /* lock channel key */ bool locklimit; /* lock channel limit */ bool avalance; /* kick ctcp bombers */ bool floodmode; /* prevent user floods */ bool welcome; /* welcome new users */ bool netsplitmode; /* deop and unban server ops/bans */ bool deopprotect; /* bankick massive deoppers */ bool uppercheck; /* check users for uppercase violations! */ bool nickflood; /* kick on nick floods */ bool talkative; /* start in talkative mode */ bool xdccmode; /* kick xdcc senders */ bool saymode; /* allow say/me commands */ bool warnmode; /* check people with the warnlist */ bool repeatmode; /* kick repeaters */ bool multimode; /* kick multiple users */ bool ctcpmode; /* CTCP flood checks */ bool reportban; /* report SET and BANs in public */ bool invitable; /* Respond to /invite or not */ bool autojoin; /* Rejoin on kick */ bool autoop; /* Auto-Op feature */ bool dispcomment; /* Display Comments */ bool masterflood; bool kickbans; /* kick banned users if they still join */ bool beepcheck; /* kick/warn beepers */ bool colourcheck; /* kick/warn colourcode violators */ bool helpsyntax; /* automatically show syntax info after help */ bool execprotect; /* Enable/disable execution of shell scripts */ bool identprotect; /* Enable/disable the IDENT command */ bool autounban; /* automatically unban the weakest ban when 18 bans are set in the channel */ bool strictopmode; /* Deop unrecognized users if they get opped by someone less than LEVEL_BOT. [FASCISM ALERT] Use with care! */ bool banuserkicks; /* Enable/disable non-bot users' kicks from causing the bot to ban for multiple kicks. If DISABLED, the bot will only ban if its own kicks are too frequent to be acceptable. If ENABLED, all users' kicks are counted and taken into consideration! */ bool fakenamemode; /* Kick people joining with a faked host/domain name. The bot won't ban them since it may not be able to select a ban pattern on user with '* in their patterns. */ bool banprotect; /* Enable/disable banprotection (main switch) */ bool mute; /* Disable most other noise not controlled by say, talkative, welcome and reportban settings */ bool opprotect; /* Op chanop-users if they get deoped by someone less than LEVEL_CHANOP. Will first deop the deoper. [FASCISM ALERT] Use with care! */ bool loopservers; /* Loop the serverlist forever */ char nameserver[256] = ""; char userfile[256] = ""; char logfile[256] = ""; char setfile[256] = ""; char expfile[256] = ""; char seenfile[256] = ""; char tellfile[256] = ""; char banfile[256] = ""; char warnfile[256] = ""; char funcfile[256] = ""; char servfile[256] = ""; char fplconf[256] = ""; char newsfile[256] = ""; char language[128] = ""; /* Use StrDuplicateMax() instead of static (fixed) arrays ? */ char channel[200] = ""; char realname[256] = ""; char nickstring[128] = ""; char nickname[NICKLEN+1] = ""; char staticnick[NICKLEN+1] = ""; char cmodes[16] = ""; char chanmodes[16] = ""; char ckey[32] = ""; char chankey[32] = ""; char ctcpversion[256] = ""; char ctcpuserinfo[256] = ""; char rulesmsg[512] = ""; char findfilename[256] = ""; char cmdvrfy[256] = ""; char cmdhost[256] = ""; char cmdfinger[256] = ""; char cmdspell[256] = ""; char askforops[256] = ""; char opaction[256] = ""; char exchangecmd[256] = ""; char defaultpasswd[256] = ""; #ifdef NICKSERV char nickpasswd[256] = ""; #endif #ifdef UNDERNET char unetpasswd[256] = ""; char unetnick[256] = ""; char unetserv[256] = ""; #endif char dancer_myhost[256] = ""; /* New LEVEL stuff - Easy-to-change array */ long levels[] = {0, 10, 20, 50, 100, 200, 242, 999999}; itemlist *serverHead = NULL; /* List of servers for the bot */ itemlist *hostispHead = NULL; /* A list of ISP-patterns that should use the host-style bans */ itemlist *nonhostispHead = NULL; /* A list of ISP-patterns that DOES not use the host-style bans (even if it matched a hostisp entry in the first run) */ itemlist *dontsitebanHead = NULL; /* A list of ISP-patterns that shouldn't get sitebanned by the bot */ /* --- Config structure ------------------------------------------- */ struct Config cfg[] = { {"server", &serverHead, "bot.server.org", CFG_LIST, FALSE, "# A comma seperated list of servers, of the format\n" "# server[:port[:password]]\n"}, {"channel", channel, "#bot", CFG_STRING, FALSE, "# Which channel to join at startup.\n"}, {"nick", nickstring, "bot", CFG_STRING, FALSE, "# Nickmask, see dancerdoc.html for further info.\n"}, #ifdef NICKSERV {"nickpass", nickpasswd, "", CFG_STRING, FALSE, "# Password for identifying with NickServ (ie. DALnet services).\n"}, #endif #ifdef UNDERNET {"unetpass", unetpasswd, "", CFG_STRING, FALSE, "# Password for authenticating with Undernet's X.\n"}, {"unetnick", unetnick, "", CFG_STRING, FALSE, "# Nick the bot is registered with, for authenticating with Undernet's\n# channel service.\n"}, {"unetserv", unetserv, "x@channels.undernet.org", CFG_STRING, FALSE, "# Name of Undernet's channel service.\n"}, #endif {"staticnick", staticnick, "danc", CFG_STRING, FALSE, "# A static 4-letter-nick that the bot will always recognize as\n" "# itself regardless of its current nickname.\n"}, {"name", realname, "An IRC bot", CFG_STRING, FALSE, "# Realname. Shows up on i.e /whois output.\n"}, {"ctcpversion", ctcpversion, CTCPVERSION, CFG_STRING, FALSE, "# Ctcpversion enables you to choose a different reply to ctcp\n" "# version requests. Per default it will reply with the name and\n" "# version of the bot.\n"}, {"ctcpuserinfo", ctcpuserinfo, "No info", CFG_STRING, FALSE, "# Ctcpuserinfo determines the reply of the ctcp userinfo request.\n"}, {"rulesmsg", rulesmsg, "No rules", CFG_STRING, FALSE, "# Rulesmsg is the text that the RULES command prints.\n"}, {"defaultpasswd", defaultpasswd, DEFPASSWDTXT, CFG_STRING, FALSE, "# Default password set to all new users.\n"}, {"dontsiteban", &dontsitebanHead, "", CFG_LIST, FALSE, "# Specify a comma separated list of ISP patterns that should never\n" "# ever get sitebanned by the bot.\n" "# (see also 'hostisp' / 'nonhostisp')\n"}, {"hostisp", &hostispHead, "*.demon.co.uk", CFG_LIST, FALSE, "# Specify a comma separated list of ISP patterns that should only\n" "# use host-patterns and never allow sitebans.\n" "# (see also 'nonhostisp')\n"}, {"nonhostisp", &nonhostispHead, "*dismayl.demon.co.uk", CFG_LIST, FALSE, "# A comma separated list of patterns that should *not* be treated\n" "# as a 'hostisp' even if it matched one of them.\n"}, {"exchangecmd", exchangecmd, "", CFG_STRING, FALSE, "# Specify the full path to the program that should be used for\n" "# currency translations plus arguments.\n" "# If you're using the supplied 'curr.sh' script for currency\n" "# translation, the setup below should be used. If you write your\n" "# own or use a third party script, set it up accordingly.\n" "#exchangecmd = /my/program/for/currency /full/path/to/rates/file\n"}, {"spellcmd", cmdspell, "./script/speller.sh", CFG_STRING, FALSE, "# Full path to the spell command/script.\n"}, {"cmdvrfy", cmdvrfy, "", CFG_STRING, FALSE, "# Cmdvrfy is the name (preferably including the full path) of the\n" "# 'vrfy' command.\n"}, {"cmdfinger", cmdfinger, "/usr/bin/finger", CFG_STRING, FALSE, "# Cmdfinger is the name (preferably including the full path) of the\n" "# 'finger' command.\n"}, {"cmdhost", cmdhost, "/usr/bin/host", CFG_STRING, FALSE, "# Cmdhost is the name (preferably including the full path) of the\n" "# 'host' command.\n"}, {"nameserver", nameserver, "", CFG_STRING, FALSE, "# Nameserver is the name of your local nameserver. This is\n" "# needed for the HOST command. If none is supplied, it will\n" "# try to get the yp master, and if that fails it will try\n" "# to do without.\n"}, {"findfilename", findfilename, "", CFG_STRING, FALSE, "# Findfilename is the name of the index file which is used by\n" "# the FIND command.\n"}, {"numretry", &numretry, (void *)8, CFG_INTEGER, FALSE, "# Number of retries to connect to the same server before giving up.\n"}, {"storedays", &logdays, (void *)7, CFG_INTEGER, FALSE, "# The number of logfiles to keep, and therefore which\n" "# logfiles to remove automatically!\n"}, {"seenmonths", &seenmonths, (void *)6, CFG_INTEGER, FALSE, "# Maximum number of months to keep old entries in the seenfile.\n"}, {"tellmonths", &tellmonths, (void *)2, CFG_INTEGER, FALSE, "# Maximum number of months to keep old entries in tellfile.\n"}, {"warnmonths", &warnmonths, (void *)3, CFG_INTEGER, FALSE, "# Maximum number of months to keep old entries in warnlist.\n"}, {"newsexpire", &newsexpire, (void *)90, CFG_INTEGER, FALSE, "# Default number of days of a news item to live.\n"}, {"netburp", &netburp, (void *)NETBURPTIME, CFG_INTEGER, FALSE, "# Netburp is the period (in seconds) after a netjoin where FLOOD\n" "# is off, to prevent kicking people because of an sudden exchange\n" "# of data between the servers (which usually is called a netburp).\n"}, {"myhost", dancer_myhost, "", CFG_STRING, FALSE, "# The IP number of the host that runs this bot. It should be a valid\n" "# IP from one of your machine's network interfaces or this is likely\n" "# to severly confuse this program. DON'T set this if you don't know\n" "# exactly what you are doing and why.\n"}, {"askforops", askforops, "", CFG_STRING, FALSE, "# Askforops is an action that the bot does when entering the\n" "# channel, to make people aware that it would like to get opped.\n"}, {"opaction", opaction, "", CFG_STRING, FALSE, "# Opaction is a kind of passive auto-op. It ops a user/bot if\n" "# the level is exactly LEVELBOT and it makes an action starting\n" "# with the content of opaction. If no opaction is supplied this\n" "# passive auto-op is disabled.\n"}, {NULL, NULL, NULL, CFG_COMMENT, FALSE, "#################################################################\n" "# Normally you aren't required to touch anything below.\n" "# Use LOG, CHGCMDLEV and SET commands to configure the\n" "# appropriate items online.\n"}, /* --- Filenames --- */ {"banfile", banfile, BANFILE, CFG_STRING, FALSE, "# File name to store ban information in.\n"}, {"explainfile", expfile, EXPFILE, CFG_STRING, FALSE, "# Explainfile contains all standard explainations. Explainations that\n" "# are added force this file to get re-written.\n"}, {"fplfile", fplconf, FPLFILE, CFG_STRING, FALSE, "# fplfile is the name of the fpl-config file.\n"}, {"funcfile", funcfile, FUNCFILE, CFG_STRING, FALSE, "# Funcfile is the name of the file which is read for the 'funcs'/\n" "# aliases Dancer supports.\n"}, {"logfile", logfile, LOGFILE, CFG_STRING, FALSE, "# This is the prefix to use for the log files.\n"}, {"newsfile", newsfile, NEWSFILE, CFG_STRING, FALSE, "# File name to store news items in (for the NEWS* commands).\n"}, {"seenfile", seenfile, SEENFILE, CFG_STRING, FALSE, "# Seenfile contains all the info on who has visited the channel.\n"}, {"servfile", servfile, SERVFILE, CFG_STRING, FALSE, "# Servfile is the name of the file used for the server list.\n"}, {"setfile", setfile, SETFILE, CFG_STRING, FALSE, "# Setfile contains all saved settings. It overrides the config stored\n" "# in the .config file.\n"}, {"tellfile", tellfile, TELLFILE, CFG_STRING, FALSE, "# Tellfile is the file where all TELL messages are stored.\n"}, {"userfile", userfile, USERFILE, CFG_STRING, FALSE, "# This file contains all registered users.\n"}, {"warnfile", warnfile, WARNFILE, CFG_STRING, FALSE, "# File name to store warning information in.\n"}, /* --- User / command levels --- */ {"level.anybody", &levels[0], (void *) 0, CFG_INTEGER, FALSE, "# Which numerical value to use for ANYBODY.\n"}, {"level.recognized", &levels[1], (void *) 10, CFG_INTEGER, FALSE, "# Which numerical value to use for RECOGNIZED.\n"}, {"level.chanop", &levels[2], (void *) 20, CFG_INTEGER, FALSE, "# Which numerical value to use for CHANOP.\n"}, {"level.trusted", &levels[3], (void *) 50, CFG_INTEGER, FALSE, "# Which numerical value to use for TRUSTED.\n"}, {"level.expert", &levels[4], (void *)100, CFG_INTEGER, FALSE, "# Which numerical value to use for EXPERT.\n"}, {"level.maintainer", &levels[5], (void *)200, CFG_INTEGER, FALSE, "# Which numerical value to use for MAINTAINER.\n"}, {"level.owner", &levels[6], (void *)242, CFG_INTEGER, FALSE, "# Which numerical value to use for OWNER.\n"}, {"linkvalue", &linkvalue, (void *)0, CFG_INTEGER, FALSE, NULL}, {NULL, NULL, NULL, CFG_COMMENT, FALSE, "#################################################################\n" "# Everything below this line can be configured online using\n" "# LOG, CHGCMDLEV and SET commands.\n"}, {"activelog", &activelog, (void *)-1, CFG_INTEGER, TRUE, "# Numerical representation of what information that should be\n" "# included in the logfile.\n"}, /* This *is* saved but differently (thus the FALSE): */ {"chgcmdlev", ChgCmdLev, NULL, CFG_FUNCTION, FALSE, "# Change the level requirement for single command by entering\n" "# them here. Use the format . One entry per\n" "# line and chgcmdlev:-label.\n"}, /* --- Ban settings --- */ {"ban.autounban", &autounban, BFALSE, CFG_SWITCH, TRUE, "# Automatically unbans the least important ban when the channel\n" "# reaches 18 bans in the list.\n"}, {"ban.banprotect", &banprotect, BTRUE, CFG_SWITCH, TRUE, "# Enable banprotection.\n"}, {"ban.banuserkicks", &banuserkicks, BTRUE, CFG_SWITCH, TRUE, "# Count user kicks when deciding whether to ban because of too\n" "# many kicks within a certain time.\n"}, {"ban.bantime", &pubbantime, (void *)0, CFG_INTEGER, TRUE, "# Default ban time (in seconds) for public bans in the channel.\n"}, {"ban.botbantime", &defbantime, (void *)0, CFG_INTEGER, TRUE, "# Default time (in seconds) of a bot ban without time specified.\n"}, {"ban.kickban", &kickbans, BTRUE, CFG_SWITCH, TRUE, "# Kick banned users if they join (split or lagged servers).\n"}, {"ban.reportban", &reportban, BFALSE, CFG_SWITCH, TRUE, "# Report about bans in public.\n"}, /* --- Flood settings --- */ {"flood.beep", &beepcheck, BTRUE, CFG_SWITCH, TRUE, "# Kick beepers.\n"}, {"flood.bomb", &avalance, BTRUE, CFG_SWITCH, TRUE, "# Kick CTCP bombers.\n"}, {"flood.colour", &colourcheck, BTRUE, CFG_SWITCH, TRUE, "# Kick colourcode (ab)users.\n"}, {"flood.ctcp", &ctcpmode, BTRUE, CFG_SWITCH, TRUE, "# Prevent CTCP floods.\n"}, {"flood.fakename", &fakenamemode, BTRUE, CFG_SWITCH, TRUE, "# Kick guests joining with faked host/domain names.\n"}, {"flood.flood", &floodmode, BTRUE, CFG_SWITCH, TRUE, "# Prevent public floods.\n"}, {"flood.multi", &multimode, BTRUE, CFG_SWITCH, TRUE, "# Prevent user to join from the same account multiple times.\n"}, {"flood.nick", &nickflood, BTRUE, CFG_SWITCH, TRUE, "# Kick nick flooders.\n"}, {"flood.repeat", &repeatmode, BTRUE, CFG_SWITCH, TRUE, "# Prevent repeaters.\n"}, {"flood.upper", &uppercheck, BFALSE, CFG_SWITCH, TRUE, "# Check for uppercase violations.\n"}, {"flood.xdcc", &xdccmode, BFALSE, CFG_SWITCH, TRUE, "# Kick xdcc announcers.\n"}, /* --- Misc settings --- */ {"misc.autojoin", &autojoin, BTRUE, CFG_SWITCH, TRUE, "# Join channel automatically if being kicked.\n"}, {"misc.autoop", &autoop, BFALSE, CFG_SWITCH, TRUE, "# Enable auto-ops.\n"}, {"misc.ckey", ckey, "", CFG_STRING, TRUE, "# Ckey is the channel key.\n"}, {"misc.cmode", cmodes, "", CFG_STRING, TRUE, "# Cmode determines which channel modes the bot should try\n" "# to keep if lockmode is on.\n"}, {"misc.climit", &climit, (void *)0, CFG_INTEGER, TRUE, "# Climit is the channel +l limit, 0 disables.\n"}, {"misc.deop", &deopprotect, BTRUE, CFG_SWITCH, TRUE, "# Check for massive deoppers.\n"}, {"misc.helpsyntax", &helpsyntax, BTRUE, CFG_SWITCH, TRUE, "# Make the syntax information get displayed when HELP is used.\n"}, {"misc.invitable", &invitable, BTRUE, CFG_SWITCH, TRUE, "# Join a channel if being /invite'd (by a high-level user).\n"}, {"misc.key", &lockkey, BTRUE, CFG_SWITCH, TRUE, "# Lock the channel key.\n"}, {"misc.limit", &locklimit, BTRUE, CFG_SWITCH, TRUE, "# Lock the channel limit.\n"}, {"misc.mode", &lockmode, BTRUE, CFG_SWITCH, TRUE, "# Lock the channel mode.\n"}, {"misc.netsplit", &netsplitmode, BTRUE, CFG_SWITCH, TRUE, "# Deop and unban server ops/bans.\n"}, {"misc.opprotect", &opprotect, BFALSE, CFG_SWITCH, TRUE, "# Protect channel operators. With this enabled, non-chanop users will\n" "# not be allowed to deop or kick chan-op users. The bot will deop the\n" "# deoper and op the chan-op users (if they were deoped).\n"}, {"misc.strictop", &strictopmode, BFALSE, CFG_SWITCH, TRUE, "# With this enabled, non-recognized users will not be allowed to get\n" "# chanop status by people without very high status. LEVELOP can\n" "# be used to define the level of the allowed ops.\n"}, /* --- Threshold settings --- */ {"thres.floodbeeps", &floodbeeps, (void *)2, CFG_INTEGER, TRUE, "# Maximum number of public beeps allowed.\n"}, {"thres.floodjoins", &floodjoins, (void *)3, CFG_INTEGER, TRUE, "# Number of joins accepted from the same hostpattern.\n"}, {"thres.floodrate", &floodrate, (void *)3, CFG_INTEGER, TRUE, "# Prevent more than FLOODRATE messages in FLOODTIME seconds.\n"}, {"thres.repeatrate", &floodrepeatrate, (void *)3, CFG_INTEGER, TRUE, "# Prevent more than FLOODRATE messages in FLOODTIME seconds.\n"}, {"thres.repeattime", &floodrepeattime, (void *)20, CFG_INTEGER, TRUE, "# See FLOODREPEATRATE.\n"}, {"thres.floodtime", &floodtime, (void *)1, CFG_INTEGER, TRUE, "# See FLOODRATE.\n"}, /* --- Verbose settings --- */ {"verbose.comment", &dispcomment, BFALSE, CFG_SWITCH, TRUE, "# Enable user-comments on join.\n"}, {"verbose.mute", &mute, BFALSE, CFG_SWITCH, TRUE, "# Enable/disable mute mode.\n"}, {"verbose.say", &saymode, BTRUE, CFG_SWITCH, TRUE, "# Enable/disable the SAY and ME commands.\n"}, {"verbose.talk", &talkative, BFALSE, CFG_SWITCH, TRUE, "# Talkative mode.\n"}, {"verbose.warn", &warnmode, BTRUE, CFG_SWITCH, TRUE, "# Report about people on the warnlist.\n"}, {"verbose.welcome", &welcome, BFALSE, CFG_SWITCH, TRUE, "# Welcome new users.\n"}, /* --- Owner settings --- */ {"owner.execprotect", &execprotect, BTRUE, CFG_SWITCH, TRUE, "# Prevents shell executions by the bot.\n"}, {"owner.identprotect", &identprotect, BTRUE, CFG_SWITCH, TRUE, "# Prevent users from being able to use the IDENT command.\n"}, {"owner.language", language, "English", CFG_STRING, TRUE, "# Set primary language for the bot to use.\n" "# ('dansk', 'deutsch', 'english', 'español', 'français', 'nederlands',\n" "# 'norsk', 'svenska' and 'suomi' are supported languages).\n"}, {"owner.loopservers", &loopservers, BFALSE, CFG_SWITCH, TRUE, "# Makes the bot loop the serverlist forever.\n"}, {"owner.oplevel", &oplevel, (void *)0, CFG_INTEGER, TRUE, "# Users with level lower than OPLEVEL are not allowed to get OPs in\n" "# the channel. Setting oplevel to 0 sets it off. See also opprotect\n" "# and strictop.\n"}, }; /* --- MakeConfig ------------------------------------------------- */ void MakeConfig(void) { int i; itemlist *l; snapshot; fprintf(stdout, "#################################################################\n" "# This is an autogenerated config file done by " VERSIONMSG "\n" "#\n" "# You _must_ set the three entries server, channel, and nick.\n" "# (remove preceeding '#' letters to make the labels \"take effect\")\n" ); for (i=0; i < sizeof(cfg)/sizeof(cfg[0]); i++) { if (cfg[i].verbose) { /* Print the help string */ fprintf(stdout, "\n%s", cfg[i].verbose); /* Print out the item and its value (commented out and with default * value if unchanged) */ switch (cfg[i].flags) { case CFG_SWITCH: if (cfg[i].changed) fprintf(stdout, "%s = %s\n", cfg[i].label, *(bool *)cfg[i].value ? "On" : "Off"); else fprintf(stdout, "#%s = %s\n", cfg[i].label, cfg[i].def ? "On" : "Off"); break; case CFG_INTEGER: if (cfg[i].changed) fprintf(stdout, "%s = %d\n", cfg[i].label, *(int *)cfg[i].value); else fprintf(stdout, "#%s = %d\n", cfg[i].label, (int)cfg[i].def); break; case CFG_STRING: if (cfg[i].changed) fprintf(stdout, "%s = %s\n", cfg[i].label, (char *)cfg[i].value); else fprintf(stdout, "#%s = %s\n", cfg[i].label, (char *)cfg[i].def); break; case CFG_LIST: if (cfg[i].changed) { int n=0; fprintf(stdout, "%s = ", cfg[i].label); for (l = First(*(itemlist **)cfg[i].value); l; l = Next(l)) { fprintf(stdout, "%s%s", n ? "," : "", l->pointer); n++; } fprintf(stdout, "\n"); } else fprintf(stdout, "#%s = %s\n", cfg[i].label, (char *)cfg[i].def); break; default: break; } } } } /* --- BuildList -------------------------------------------------- */ itemlist *BuildList(itemlist **appendHead, char *string) { char buffer[BIGBUFFER]; int i=0; itemlist *head, *l; snapshot; if ((NULL == appendHead) || (NULL == *appendHead)) { head = NewList(itemlist); if (appendHead) *appendHead = head; } else head = *appendHead; if (head) { #ifdef HAVE_N_IN_SCANF int index; /* * Execution of a %n directive does not increment the * assignment count returned at the completion of * execution of the function. */ while (1 == StrScan(&string[i], "%"BIGBUFFERTXT"[^,\n] %n", buffer, &index)) { i += index; #else while (1 == StrScan(&string[i], "%"BIGBUFFERTXT"[^,\n]", buffer)) { i += StrLength(buffer); #endif if (',' == string[i]) i++; l = NewEntry(itemlist); if (l) { InsertLast(head, l); l->pointer = (void *)StrDuplicate(buffer); } else { break; } } } return head; } /* --- PreConfig -------------------------------------------------- */ static bool inited = FALSE; void PreConfig(void) { int i; snapshot; for (i=0; i < sizeof(cfg)/sizeof(cfg[0]); i++) { switch (cfg[i].flags) { case CFG_SWITCH: *(bool *)cfg[i].value = (bool)((int)cfg[i].def); break; case CFG_INTEGER: *(int *)cfg[i].value = (int)cfg[i].def; break; case CFG_STRING: /* Need limit check!!! */ StrCopy((char *)cfg[i].value, (char *)cfg[i].def); break; default: break; } cfg[i].changed = FALSE; } inited = TRUE; } /* --- PostConfig ------------------------------------------------- */ void PostConfig(void) { int i; snapshot; for (i=0; i < sizeof(cfg)/sizeof(cfg[0]); i++) { if (CFG_LIST == cfg[i].flags) { if (NULL == First(*(itemlist **)cfg[i].value)) BuildList((itemlist **)cfg[i].value, cfg[i].def); } } defaultlang = LanguageNum(language); } /* --- ConfigAddItem ---------------------------------------------- */ void ConfigAddItem(char *line) { char keyword[MINIBUFFER]; char valuebuffer[BIGBUFFER]; int i; snapshot; if (2 <= StrScan(line, "%"MINIBUFFERTXT"[a-zA-Z0-9.] %*[=: ] %"BIGBUFFERTXT"[^\n]", keyword, valuebuffer)) { for (i=0; i < sizeof(cfg)/sizeof(cfg[0]); i++) { if (StrEqual(keyword, cfg[i].label)) { /* Match */ switch (cfg[i].flags) { case CFG_SWITCH: if (StrEqual(valuebuffer, "ON") || StrEqual(valuebuffer, "YES") || atoi(valuebuffer)) *(bool *)cfg[i].value = TRUE; else *(bool *)cfg[i].value = FALSE; break; case CFG_INTEGER: *(int *)cfg[i].value = atoi(valuebuffer); break; case CFG_STRING: /* Need limit check!!! */ StrCopy((char *)cfg[i].value, valuebuffer); break; case CFG_LIST: BuildList((itemlist **)cfg[i].value, valuebuffer); break; case CFG_FUNCTION: (*(void (*)(char *, char *))cfg[i].value)(cfg[i].def, valuebuffer); break; default: break; } cfg[i].changed = TRUE; break; } } } } /* --- ConfigInit ------------------------------------------------- */ bool ConfigInit(void) { char line[MAXLINE]; FILE *f; snapshot; PreConfig(); f = fopen(CONFIGFILE, "r"); if (f) { while (fgets(line, sizeof(line), f)) { switch (line[0]) { case '#': case '\n': break; default: ConfigAddItem(line); break; } } fclose(f); f = fopen(setfile, "r"); if (f) { while (fgets(line, sizeof(line), f)) { switch (line[0]) { case '#': case '\n': break; default: ConfigAddItem(line); break; } } fclose(f); } PostConfig(); StrCopy(chanmodes, cmodes); StrCopy(chankey, ckey); opactionlen = StrLength(opaction); InitSets(); return TRUE; } return FALSE; } /* --- SaveSettings ----------------------------------------------- */ void SaveSettings(void) { extern struct Command cmds[]; char tempfile[MIDBUFFER]; bool ok = TRUE; int numchanged = 0; int i; FILE *f; snapshot; if (NIL == setfile[0]) return; StrFormatMax(tempfile, sizeof(tempfile), "%s~", setfile); f = fopen(tempfile, "w"); if (f) { for (i=0; ok && (i < sizeof(cfg)/sizeof(cfg[0])); i++) { if (cfg[i].setting) { if (cfg[i].verbose) { /* Write comment */ if (0 > fprintf(f, "\n%s", cfg[i].verbose)) { ok = FALSE; break; } } switch (cfg[i].flags) { case CFG_SWITCH: if (0 > fprintf(f, "%s = %s\n", cfg[i].label, OnOff(*(bool *)cfg[i].value))) ok = FALSE; break; case CFG_INTEGER: if (0 > fprintf(f, "%s = %d\n", cfg[i].label, *(int *)cfg[i].value)) ok = FALSE; break; case CFG_STRING: if (0 > fprintf(f, "%s = %s\n", cfg[i].label, (char *)cfg[i].value)) ok = FALSE; break; default: break; } } } /* Save new command levels */ for (i=0; ok && cmds[i].name; i++) { if (cmds[i].flags & CMD_CHANGED) { if (1 == ++numchanged) { if (0 > fprintf(f, "\n# Here follows a list of commands with their new levels:\n")) { ok = FALSE; break; } } /* Save the new level */ if (0 > fprintf(f, "chgcmdlev: %s %d\n", cmds[i].name, cmds[i].level)) { ok = FALSE; break; } } } fclose(f); if (ok) rename(tempfile, setfile); } } /* --- ConfigCleanup ---------------------------------------------- */ void ConfigCleanup(void) { snapshot; if (!inited) return; SaveSettings(); /* Flush server list */ DeleteList(serverHead, FreeList); } /********************************************************************* * The following piece of source is the new SET functions. ********************************************************************/ #define SPECIAL_CMODE 1 #define SPECIAL_CKEY 2 #define SPECIAL_LANGUAGE 3 static struct SetItem ban[] = { {"AUTOUNBAN", LEVEL_ANYBODY, SET_ONOFF, &autounban, 0, msg_helpset_autounban}, {"BANPROTECT", LEVEL_ANYBODY, SET_ONOFF, &banprotect, 0, msg_helpset_banprotect}, {"BANUSERKICKS", LEVEL_ANYBODY, SET_ONOFF, &banuserkicks, 0, msg_helpset_banuserkicks}, {"BANTIME", LEVEL_BOT, SET_TIME, &pubbantime, 0, msg_helpset_bantime}, {"BOTBANTIME", LEVEL_BOT, SET_TIME, &defbantime, 0, msg_helpset_botbantime}, {"KICKBAN", LEVEL_ANYBODY, SET_ONOFF, &kickbans, 0, msg_helpset_kickban}, {"REPORTBAN", LEVEL_ANYBODY, SET_ONOFF, &reportban, 0, msg_helpset_reportban}, {NULL, 0, 0, NULL, 0, NULL} /* End series with a NULL-entry */ }; static struct SetItem flood[] = { {"BEEP", LEVEL_ANYBODY, SET_ONOFF, &beepcheck, 0, msg_helpset_beep}, {"BOMB", LEVEL_ANYBODY, SET_ONOFF, &avalance, 0, msg_helpset_bomb}, {"COLOUR", LEVEL_ANYBODY, SET_ONOFF, &colourcheck, 0, msg_helpset_colour}, {"CTCP", LEVEL_ANYBODY, SET_ONOFF, &ctcpmode, 0, msg_helpset_ctcp}, {"FAKENAME", LEVEL_ANYBODY, SET_ONOFF, &fakenamemode, 0, msg_helpset_fakename}, {"FLOOD", LEVEL_ANYBODY, SET_ONOFF, &floodmode, 0, msg_helpset_flood}, {"MULTI", LEVEL_ANYBODY, SET_ONOFF, &multimode, 0, msg_helpset_multi}, {"NICK", LEVEL_ANYBODY, SET_ONOFF, &nickflood, 0, msg_helpset_nick}, {"REPEAT", LEVEL_ANYBODY, SET_ONOFF, &repeatmode, 0, msg_helpset_repeat}, {"UPPER", LEVEL_ANYBODY, SET_ONOFF, &uppercheck, 0, msg_helpset_upper}, {"XDCC", LEVEL_ANYBODY, SET_ONOFF, &xdccmode, 0, msg_helpset_xdcc}, {NULL, 0, 0, NULL, 0, NULL} /* End series with a NULL-entry */ }; static struct SetItem misc[] = { {"AUTOJOIN", LEVEL_ANYBODY, SET_ONOFF, &autojoin, 0, msg_helpset_autojoin}, {"AUTOOP", LEVEL_ANYBODY, SET_ONOFF, &autoop, 0, msg_helpset_autoop}, {"CKEY", LEVEL_ANYBODY, SET_SPEC, NULL, SPECIAL_CKEY, msg_helpset_ckey}, {"CMODE", LEVEL_ANYBODY, SET_SPEC, NULL, SPECIAL_CMODE, msg_helpset_cmode}, {"CLIMIT", LEVEL_ANYBODY, SET_NUM, &climit, 0, msg_helpset_climit}, {"DEOP", LEVEL_ANYBODY, SET_ONOFF, &deopprotect, 0, msg_helpset_deop}, {"HELPSYNTAX",LEVEL_ANYBODY, SET_ONOFF, &helpsyntax, 0, msg_helpset_helpsyntax}, {"INVITE", LEVEL_ANYBODY, SET_ONOFF, &invitable, 0, msg_helpset_invite}, {"KEY", LEVEL_ANYBODY, SET_ONOFF, &lockkey, 0, msg_helpset_key}, {"LIMIT", LEVEL_ANYBODY, SET_ONOFF, &locklimit, 0, msg_helpset_limit}, {"MODE", LEVEL_ANYBODY, SET_ONOFF, &lockmode, 0, msg_helpset_mode}, {"NETSPLIT", LEVEL_ANYBODY, SET_ONOFF, &netsplitmode, 0, msg_helpset_netsplit}, {"OPPROTECT", LEVEL_BOT, SET_ONOFF, &opprotect, 0, msg_helpset_opprotect}, {"STRICTOP", LEVEL_BOT, SET_ONOFF, &strictopmode, 0, msg_helpset_strictop}, {NULL, 0, 0, NULL, 0, NULL} /* End series with a NULL-entry */ }; static struct SetItem thres[] = { {"FLOODBEEPS", LEVEL_ANYBODY, SET_NUM, &floodbeeps, 0, msg_helpset_floodbeeps}, {"FLOODJOINS", LEVEL_ANYBODY, SET_NUM, &floodjoins, 0, msg_helpset_floodjoins}, {"FLOODRATE", LEVEL_ANYBODY, SET_NUM, &floodrate, 0, msg_helpset_floodrate}, {"FLOODREPEATRATE", LEVEL_ANYBODY, SET_NUM, &floodrepeatrate, 0, msg_helpset_floodrepeatrate}, {"FLOODREPEATTIME", LEVEL_ANYBODY, SET_NUM, &floodrepeattime, 0, msg_helpset_floodrepeattime}, {"FLOODTIME", LEVEL_ANYBODY, SET_NUM, &floodtime, 0, msg_helpset_floodtime}, {NULL, 0, 0, NULL, 0, NULL} /* End series with a NULL-entry */ }; static struct SetItem verbose[] = { {"COMMENT", LEVEL_ANYBODY, SET_ONOFF, &dispcomment, 0, msg_helpset_comment}, {"MUTE", LEVEL_ANYBODY, SET_ONOFF, &mute, 0, msg_helpset_mute}, {"SAY", LEVEL_ANYBODY, SET_ONOFF, &saymode, 0, msg_helpset_say}, {"TALK", LEVEL_ANYBODY, SET_ONOFF, &talkative, 0, msg_helpset_talk}, {"WARN", LEVEL_ANYBODY, SET_ONOFF, &warnmode, 0, msg_helpset_warn}, {"WELCOME", LEVEL_ANYBODY, SET_ONOFF, &welcome, 0, msg_helpset_welcome}, {NULL, 0, 0, NULL, 0, NULL} /* End series with a NULL-entry */ }; static struct SetItem owner[] = { {"EXECPROTECT", LEVEL_ANYBODY, SET_ONOFF, &execprotect, 0, msg_helpset_execprotect}, {"IDENTPROTECT", LEVEL_ANYBODY, SET_ONOFF, &identprotect, 0, msg_helpset_identprotect}, {"LANGUAGE", LEVEL_ANYBODY, SET_SPEC, NULL, SPECIAL_LANGUAGE, msg_helpset_language}, {"LOOPSERVERS", LEVEL_ANYBODY, SET_ONOFF, &loopservers, 0, msg_helpset_loopservers}, {"OPLEVEL", LEVEL_ANYBODY, SET_NUM, &oplevel, 0, msg_helpset_oplevel}, {NULL, 0, 0, NULL, 0, NULL} /* End series with a NULL-entry */ }; static struct SetGroup sets[] = { {"BAN", LEVEL_ANYBODY, ban, msg_setgroup_ban}, {"FLOOD", LEVEL_ANYBODY, flood, msg_setgroup_flood}, {"MISC", LEVEL_ANYBODY, misc, msg_setgroup_misc}, {"THRESHOLDS", LEVEL_ANYBODY, thres, msg_setgroup_thres}, {"VERBOSE", LEVEL_ANYBODY, verbose, msg_setgroup_verbose}, {"OWNER", LEVEL_OWNER, owner, msg_setgroup_owner}, }; /* --- ShowSetItem ------------------------------------------------ */ /* * Fills in a buffer, showing the contents of the specified item. * Returns buffer. */ static char *ShowSetItem(char *buffer, int buffer_size, struct SetItem *item) { char level[32] = ""; snapshot; if (item->level) { StrFormatMax(level, sizeof(level), " [%d]", item->level); } StrFormatMax(buffer, buffer_size, "%s%s = ", item->name, level); switch (item->flags) { case SET_ONOFF: StrAppendMax(buffer, buffer_size, OnOff(*(bool *)item->value)); break; case SET_NUM: StrFormatAppendMax(buffer, buffer_size, "%d", *(int *)item->value); break; case SET_TIME: StrAppendMax(buffer, buffer_size, SecsToString(*(int *)item->value)); break; case SET_SPEC: switch (item->ID) { case SPECIAL_CMODE: StrAppendMax(buffer, buffer_size, cmodes); break; case SPECIAL_CKEY: StrAppendMax(buffer, buffer_size, ckey); break; case SPECIAL_LANGUAGE: StrAppendMax(buffer, buffer_size, language); break; } break; } return buffer; } /* --- ShowSetGroup ----------------------------------------------- */ /* Shows the SETs of the specified group. All items in the group * will get displayed (even if some might have higher access levels) * if the person has the access. * * Output format: * "Group OWNER [242]: EXECPROTECT = on, OTHERFLAG = off" */ static void ShowSetGroup(char *from, struct SetGroup *grp) { char buffer[MIDBUFFER]; char set[MINIBUFFER]; char level[32] = ""; int count = 0; int i; struct SetItem *item = grp->group; snapshot; if (grp->level > current->level) { return; } if (grp->level) { StrFormatMax(level, sizeof(level), " [%d]", grp->level); } StrFormatMax(buffer, sizeof(buffer), "<%s%s>: ", grp->name, level); for (i=0; item[i].name; i++) { /* Don't show non-accessible items */ if (item[i].level > current->level) continue; ShowSetItem(set, sizeof(set), &item[i]); if ((StrLength(buffer) + StrLength(set) + 1) >= sizeof(buffer)) { Send(from, buffer); buffer[0] = (char)0; count = 0; } StrFormatAppendMax(buffer, sizeof(buffer), "%s%s", count ? ", " : "", set); count++; } Send(from, buffer); } /* --- FindItemInGroup -------------------------------------------- */ int static FindItemInGroup(struct SetGroup *grp, char *label, struct SetItem **item) { int i; int amount = 0; struct SetItem *group = grp->group; struct SetItem *hit = NULL; snapshot; for (i=0; group[i].name; i++) { #if 0 if (group[i].level > current->level) /* * Don't even bother to try labels we aren't allowed * to access. */ continue; #endif if (StrEqualMax(group[i].name, StrLength(label), label)) { *item = &group[i]; amount++; } } return amount; } /* --- GimmeGroup ------------------------------------------------- */ /* Returns the group number which matches the 'group' parameter. * Set 'error' to TRUE to get error reports, FALSE will make it * return -1 silently. */ static struct SetGroup *GimmeGroup(char *from, char *group, bool error) { int grpnum = -1; int amount = 0; int i; snapshot; for (i=0; i < sizeof(sets)/sizeof(sets[0]); i++) { #if 0 if (sets[i].level > current->level) /* * Don't even bother to search for a group that we aren't allowed * to access. */ continue; #endif if (StrEqualMax(sets[i].name, StrLength(group), group)) { amount++; grpnum = i; } } if (0 == amount) { if (error) Sendf(errfrom, GetText(msg_group_doesnt_exist), group); return NULL; } else if (2 <= amount) { if (error) Sendf(errfrom, GetText(msg_matches_several_groups), group); return NULL; } return &sets[grpnum]; } /* --- GimmeItem -------------------------------------------------- */ /* Returns the label which matches the 'label' parameter. * Set 'error' to TRUE to get error reports, FALSE will make it * return -1 silently. * The 'retgrp' will get a pointer to the group in which the label * was found. */ struct SetItem *GimmeItem(char *from, char *group, char *label, bool error, struct SetGroup **retgrp) { struct SetItem *hit; struct SetGroup *grp; int amount = 0; int i; snapshot; if (group[0]) { grp = GimmeGroup(from, group, TRUE); if (NULL == grp) return NULL; amount = FindItemInGroup(grp, label, &hit); } else { int num; for (i=0; iname, GetText(hit->help), (hit->level > current->level) ? GetText(msg_no_access) : ShowSetItem(set, sizeof(set), hit)); } else { /* now, try a group names like this */ grp = GimmeGroup(from, token, FALSE); if (grp) { SendMulti(from, "%s %s: %s.", GetText(msg_group), grp->name, GetText(grp->help)); } else { Send(from, GetText(msg_cant_find_set_keyword)); return TRUE; } } return FALSE; } /* --- ShowSetAll ------------------------------------------------- */ /* Show all items in all groups */ void ShowSetAll(char *from) { int i; snapshot; for (i=0; i < sizeof(sets)/sizeof(sets[0]); i++) { ShowSetGroup(from, &sets[i]); } } /* --- SetSet ----------------------------------------------------- */ /* Called straight from command.c and the CmdSet(). Parse the input * and perform every action in here. */ int SetSet(char *from, char *line) { char group[32], dot[32], label[32], value[32]; int amount = 0; int i; struct SetItem *hit; struct SetGroup *grp; snapshot; i = StrScan(line, "%31[^ .\n]%31[. ]%31s %31s", group, dot, label, value); switch (i) { case -1: /* * This is what they call EOF, returned "If the input ends before * the first matching failure or conversion" */ case 0: ShowSetAll(from); return FALSE; case 1: /* One parameter, show the SETs for the specified group */ grp = GimmeGroup(from, group, FALSE); if (NULL == grp) { /* We'll try to show the group that item belongs to then */ hit = GimmeItem(from, "", group, FALSE, &grp); if (NULL == hit) { Send(errfrom, GetText(msg_i_dont_understand)); return TRUE; } } ShowSetGroup(from, grp); return FALSE; case 2: case 3: if ((2 == i) || StrIndex(dot, '.')) { /* * This just be an attempt to specify a group name, but we don't * have a value. */ grp = GimmeGroup(from, group, TRUE); if (grp) ShowSetGroup(from, grp); else return TRUE; return FALSE; } /* Need limit check!!! */ StrCopy(value, label); StrCopy(label, group); group[0] = (char)0; /* No group specified */ break; } hit = GimmeItem(from, group, label, TRUE, &grp); if (NULL == hit) return TRUE; /* * Access control could be done at lower layers. Using it this way, * everything but actually changing the SET will be available to * anyone (i.e help and display current status). */ if (grp->level > current->level || hit->level > current->level) { Send(errfrom, GetText(msg_dont_touch_this)); return TRUE; } switch (hit->flags) { case SET_ONOFF: *(bool *)hit->value = IsOn(value); break; case SET_NUM: *(int *)hit->value = atoi(value); if (StrEqualCase(hit->name, "OPLEVEL")) { if (oplevel > current->level) oplevel = current->level; } break; case SET_TIME: *(int *)hit->value = ToSeconds(value); break; case SET_SPEC: switch (hit->ID) { case SPECIAL_CMODE: CmdCmode(from, value); break; case SPECIAL_CKEY: CmdCkey(from, value); break; case SPECIAL_LANGUAGE: defaultlang = LanguageNum(value); StrCopy(language, LanguageLocal(defaultlang)); break; } break; } SaveSettings(); ShowSetGroup(from, grp); return FALSE; } /* --- InitSets --------------------------------------------------- */ void InitSets(void) { int i; struct SetItem *item; snapshot; for (i=0; i < sizeof(sets)/sizeof(sets[0]); i++) { sets[i].level = levels[ sets[i].level ]; for (item = sets[i].group; item->name; item++) item->level = levels[ item->level ]; } } /* --- ChgCmdLev -------------------------------------------------- */ void ChgCmdLev(char *def, char *value) { char cmd[MINIBUFFER]; int level; struct Command *command; if (2 == StrScan(value, "%"MINIBUFFERTXT"s %d", cmd, &level)) { if ((level >= LEVELANYBODY) && (level <= LEVELOWNER)) { command = FindCommand(cmd); if (command) { command->level = level; command->flags |= CMD_CHANGED; } } } } #if 0 /****************************************************************************** New stuff 970618 to better deal with the users' own info, flags and status. This is 4.7+ stuff __ __ _ __ | \/ |_ _(_)_ __ / _| ___ | |\/| | | | | | '_ \| |_ / _ \ | | | | |_| | | | | | _| (_) | |_| |_|\__, |_|_| |_|_| \___/ |___/ *****************************************************************************/ #define MYINFO_USER 0x80 #define MYINFO_GUEST 0x40 typedef enum { MYINFO_LANGUAGE=1, MYINFO_REPORT, MYINFO_SPY, MYINFO_DEBUG, } myspecial; /* debug-defines to make it compile */ #define msg_myinfo_comment NULL #define msg_myinfo_email NULL #define msg_myinfo_language NULL #define msg_myinfo_report NULL #define msg_myinfo_spy NULL #define msg_myinfo_debug NULL #define msg_myinfo_info NULL #define msg_myinfo_flags NULL static struct SetItem myinfoinfo[]={ {"EMAIL", 0, SET_STRING, (void *)offsetof(itemuser, realname), MYINFO_USER, msg_myinfo_email}, /* replaces SETEMAIL */ {"COMMENT", 0, SET_STRING, (void *)offsetof(itemuser, comment), MYINFO_USER, msg_myinfo_comment}, /* replaces COMMENT */ {"LANGUAGE", 0, SET_STRING,(void *)offsetof(itemuser, language) , MYINFO_USER | MYINFO_LANGUAGE, msg_myinfo_language}, /* replaces LANGUAGE */ }; static struct SetItem myinfoflags[]={ {"REPORT", LEVEL_CHANOP, SET_ONOFF, NULL, MYINFO_REPORT, msg_myinfo_report}, /* replaces REPORTADD and REPORTDEL */ {"SPY", LEVEL_BOT, SET_ONOFF, NULL, MYINFO_SPY, msg_myinfo_spy}, /* replaces SPYADD and SPYDEL */ {"DEBUG", LEVEL_BOT, SET_ONOFF, NULL, MYINFO_DEBUG, msg_myinfo_debug}, /* replaces DEBUGADD and DEBUGDEL */ }; static struct SetGroup myinfo[]={ {"INFO", LEVEL_RECOG, myinfoinfo, msg_myinfo_info}, {"FLAGS", 0, myinfoflags, msg_myinfo_flags}, }; #endif