1 #ifdef RCS
2 static char rcsid[]="$Id: setup.c,v 1.2 2001/03/13 05:04:47 holsta Exp $";
3 #endif
4 /******************************************************************************
5  *                    Internetting Cooperating Programmers
6  * ----------------------------------------------------------------------------
7  *
8  *  ____    PROJECT
9  * |  _ \  __ _ _ __   ___ ___ _ __
10  * | | | |/ _` | '_ \ / __/ _ \ '__|
11  * | |_| | (_| | | | | (_|  __/ |
12  * |____/ \__,_|_| |_|\___\___|_|   the IRC bot
13  *
14  * All files in this archive are subject to the GNU General Public License.
15  *
16  * $Source: /cvsroot/dancer/dancer/src/setup.c,v $
17  * $Revision: 1.2 $
18  * $Date: 2001/03/13 05:04:47 $
19  * $Author: holsta $
20  * $State: Exp $
21  * $Locker:  $
22  *
23  * ---------------------------------------------------------------------------
24  *****************************************************************************/
25 
26 #include <stdlib.h>
27 #include <stddef.h> /* does this work in all unixes? the offsetof() */
28 
29 #include "dancer.h"
30 #include "trio.h"
31 #include "strio.h"
32 #include "list.h"
33 #include "function.h"
34 #include "setup.h"
35 #include "user.h"
36 #include "command.h"
37 #include "files.h"
38 
39 extern int floodrate, floodtime, floodrepeatrate, floodrepeattime;
40 extern int floodbeeps, floodjoins;
41 extern int logdays;
42 extern ulong activelog;
43 extern char *errfrom;  /* this is like stderr, only for nicks */
44 extern itemident *current;
45 
46 int numretry;
47 int opactionlen;
48 int pubbantime;
49 int oplevel;
50 int netburp;  /* Period after netsplit where FLOOD is off */
51 int defbantime; /* Default value if time is omitted in BAN */
52 int seenmonths; /* Max number of months to keep entries in seen file */
53 int tellmonths; /* Max number of months to keep entries in tell file */
54 int warnmonths; /* Max number of months to keep entries in tell file */
55 long climit; /* the +l limit, 0 disables */
56 long newsexpire;
57 ulong linkvalue; /* "Public" key for linkbot */
58 bool lockmode;      /* lock channel mode */
59 bool lockkey;       /* lock channel key  */
60 bool locklimit;     /* lock channel limit  */
61 bool avalance;      /* kick ctcp bombers */
62 bool floodmode;     /* prevent user floods */
63 bool welcome;       /* welcome new users */
64 bool netsplitmode;  /* deop and unban server ops/bans */
65 bool deopprotect;   /* bankick massive deoppers */
66 bool uppercheck;    /* check users for uppercase violations! */
67 bool nickflood;     /* kick on nick floods */
68 bool talkative;     /* start in talkative mode */
69 bool xdccmode;      /* kick xdcc senders */
70 bool saymode;       /* allow say/me commands */
71 bool warnmode;      /* check people with the warnlist */
72 bool repeatmode;    /* kick repeaters */
73 bool multimode;     /* kick multiple users */
74 bool ctcpmode;      /* CTCP flood checks */
75 bool reportban;     /* report SET and BANs in public */
76 bool invitable;     /* Respond to /invite or not */
77 bool autojoin;      /* Rejoin on kick */
78 bool autoop;        /* Auto-Op feature */
79 bool dispcomment;   /* Display Comments */
80 bool masterflood;
81 bool kickbans;      /* kick banned users if they still join */
82 bool beepcheck;     /* kick/warn beepers */
83 bool colourcheck;   /* kick/warn colourcode violators */
84 bool helpsyntax;    /* automatically show syntax info after help */
85 bool execprotect;   /* Enable/disable execution of shell scripts */
86 bool identprotect;  /* Enable/disable the IDENT command */
87 bool autounban;     /* automatically unban the weakest ban when 18 bans are
88                        set in the channel */
89 bool strictopmode;  /* Deop unrecognized users if they get opped by someone
90                        less than LEVEL_BOT.
91                        [FASCISM ALERT] Use with care! */
92 bool banuserkicks;  /* Enable/disable non-bot users' kicks from causing the
93                        bot to ban for multiple kicks. If DISABLED, the bot
94                        will only ban if its own kicks are too frequent to
95                        be acceptable. If ENABLED, all users' kicks are counted
96                        and taken into consideration! */
97 bool fakenamemode;  /* Kick people joining with a faked host/domain name.  The
98                        bot won't ban them since it may not be able to select a
99                        ban pattern on user with '* in their patterns.  */
100 bool banprotect;    /* Enable/disable banprotection (main switch) */
101 bool mute;          /* Disable most other noise not controlled by say,
102                        talkative, welcome and reportban settings */
103 bool opprotect;     /* Op chanop-users if they get deoped by someone
104                        less than LEVEL_CHANOP. Will first deop the deoper.
105                        [FASCISM ALERT] Use with care! */
106 bool loopservers;   /* Loop the serverlist forever */
107 
108 char nameserver[256] = "";
109 char userfile[256] = "";
110 char logfile[256] = "";
111 char setfile[256] = "";
112 char expfile[256] = "";
113 char seenfile[256] = "";
114 char tellfile[256] = "";
115 char banfile[256] = "";
116 char warnfile[256] = "";
117 char funcfile[256] = "";
118 char servfile[256] = "";
119 char fplconf[256] = "";
120 char newsfile[256] = "";
121 char language[128] = "";
122 
123 /* Use StrDuplicateMax() instead of static (fixed) arrays ? */
124 char channel[200] = "";
125 char realname[256] = "";
126 char nickstring[128] = "";
127 char nickname[NICKLEN+1] = "";
128 char staticnick[NICKLEN+1] = "";
129 char cmodes[16] = "";
130 char chanmodes[16] = "";
131 char ckey[32] = "";
132 char chankey[32] = "";
133 
134 char ctcpversion[256] = "";
135 char ctcpuserinfo[256] = "";
136 char rulesmsg[512] = "";
137 char findfilename[256] = "";
138 char cmdvrfy[256] = "";
139 char cmdhost[256] = "";
140 char cmdfinger[256] = "";
141 char cmdspell[256] = "";
142 char askforops[256] = "";
143 char opaction[256] = "";
144 char exchangecmd[256] = "";
145 char defaultpasswd[256] = "";
146 #ifdef NICKSERV
147 char nickpasswd[256] = "";
148 #endif
149 #ifdef UNDERNET
150 char unetpasswd[256] = "";
151 char unetnick[256] = "";
152 char unetserv[256] = "";
153 #endif
154 char dancer_myhost[256] = "";
155 
156 /* New LEVEL stuff - Easy-to-change array */
157 long levels[] = {0, 10, 20, 50, 100, 200, 242, 999999};
158 
159 itemlist *serverHead = NULL;      /* List of servers for the bot */
160 itemlist *hostispHead = NULL;     /* A list of ISP-patterns that should use the
161                                      host-style bans */
162 itemlist *nonhostispHead = NULL;  /* A list of ISP-patterns that DOES not use
163                                      the host-style bans (even if it matched
164                                      a hostisp entry in the first run) */
165 itemlist *dontsitebanHead = NULL; /* A list of ISP-patterns that shouldn't
166                                      get sitebanned by the bot */
167 
168 /* --- Config structure ------------------------------------------- */
169 
170 struct Config cfg[] =
171 {
172   {"server",      &serverHead, "bot.server.org",   CFG_LIST,   FALSE,
173    "# A comma seperated list of servers, of the format\n"
174    "#   server[:port[:password]]\n"},
175 
176   {"channel",     channel,     "#bot",             CFG_STRING, FALSE,
177    "# Which channel to join at startup.\n"},
178 
179   {"nick",        nickstring,  "bot",              CFG_STRING, FALSE,
180    "# Nickmask, see dancerdoc.html for further info.\n"},
181 
182 #ifdef NICKSERV
183   {"nickpass",    nickpasswd,  "",                 CFG_STRING, FALSE,
184    "# Password for identifying with NickServ (ie. DALnet services).\n"},
185 #endif
186 
187 #ifdef UNDERNET
188   {"unetpass",        unetpasswd,   "",                 CFG_STRING, FALSE,
189     "# Password for authenticating with Undernet's X.\n"},
190 
191   {"unetnick",        unetnick,   "",                 CFG_STRING, FALSE,
192     "# Nick the bot is registered with, for authenticating with Undernet's\n# channel service.\n"},
193 
194   {"unetserv",  unetserv,   "x@channels.undernet.org",   CFG_STRING, FALSE,
195     "# Name of Undernet's channel service.\n"},
196 #endif
197   {"staticnick",  staticnick,  "danc",             CFG_STRING, FALSE,
198    "# A static 4-letter-nick that the bot will always recognize as\n"
199    "# itself regardless of its current nickname.\n"},
200 
201   {"name",        realname,    "An IRC bot",       CFG_STRING, FALSE,
202    "# Realname. Shows up on i.e /whois output.\n"},
203 
204   {"ctcpversion", ctcpversion, CTCPVERSION,        CFG_STRING, FALSE,
205    "# Ctcpversion enables you to choose a different reply to ctcp\n"
206    "# version requests. Per default it will reply with the name and\n"
207    "# version of the bot.\n"},
208 
209   {"ctcpuserinfo", ctcpuserinfo, "No info",        CFG_STRING, FALSE,
210    "# Ctcpuserinfo determines the reply of the ctcp userinfo request.\n"},
211 
212   {"rulesmsg",    rulesmsg,    "No rules",         CFG_STRING, FALSE,
213    "# Rulesmsg is the text that the RULES command prints.\n"},
214 
215   {"defaultpasswd", defaultpasswd, DEFPASSWDTXT,   CFG_STRING, FALSE,
216    "# Default password set to all new users.\n"},
217 
218   {"dontsiteban", &dontsitebanHead, "",            CFG_LIST,   FALSE,
219    "# Specify a comma separated list of ISP patterns that should never\n"
220    "# ever get sitebanned by the bot.\n"
221    "# (see also 'hostisp' / 'nonhostisp')\n"},
222 
223   {"hostisp",     &hostispHead, "*.demon.co.uk",   CFG_LIST,   FALSE,
224    "# Specify a comma separated list of ISP patterns that should only\n"
225    "# use host-patterns and never allow sitebans.\n"
226    "# (see also 'nonhostisp')\n"},
227 
228   {"nonhostisp",  &nonhostispHead, "*dismayl.demon.co.uk", CFG_LIST, FALSE,
229    "# A comma separated list of patterns that should *not* be treated\n"
230    "# as a 'hostisp' even if it matched one of them.\n"},
231 
232   {"exchangecmd", exchangecmd, "",                 CFG_STRING, FALSE,
233    "# Specify the full path to the program that should be used for\n"
234    "# currency translations plus arguments.\n"
235    "# If you're using the supplied 'curr.sh' script for currency\n"
236    "# translation, the setup below should be used. If you write your\n"
237    "# own or use a third party script, set it up accordingly.\n"
238    "#exchangecmd = /my/program/for/currency /full/path/to/rates/file\n"},
239 
240   {"spellcmd",    cmdspell, "./script/speller.sh", CFG_STRING, FALSE,
241    "# Full path to the spell command/script.\n"},
242 
243   {"cmdvrfy",     cmdvrfy,     "",                 CFG_STRING, FALSE,
244    "# Cmdvrfy is the name (preferably including the full path) of the\n"
245    "# 'vrfy' command.\n"},
246 
247   {"cmdfinger",   cmdfinger,   "/usr/bin/finger",  CFG_STRING, FALSE,
248    "# Cmdfinger is the name (preferably including the full path) of the\n"
249    "# 'finger' command.\n"},
250 
251   {"cmdhost",     cmdhost,     "/usr/bin/host",    CFG_STRING, FALSE,
252    "# Cmdhost is the name (preferably including the full path) of the\n"
253    "# 'host' command.\n"},
254 
255   {"nameserver",  nameserver,  "",                 CFG_STRING, FALSE,
256    "# Nameserver is the name of your local nameserver. This is\n"
257    "# needed for the HOST command. If none is supplied, it will\n"
258    "# try to get the yp master, and if that fails it will try\n"
259    "# to do without.\n"},
260 
261   {"findfilename", findfilename, "",               CFG_STRING, FALSE,
262    "# Findfilename is the name of the index file which is used by\n"
263    "# the FIND command.\n"},
264 
265   {"numretry",    &numretry,   (void *)8,          CFG_INTEGER, FALSE,
266    "# Number of retries to connect to the same server before giving up.\n"},
267 
268   {"storedays",   &logdays,    (void *)7,          CFG_INTEGER, FALSE,
269    "# The number of logfiles to keep, and therefore which\n"
270    "# logfiles to remove automatically!\n"},
271 
272   {"seenmonths",  &seenmonths, (void *)6,          CFG_INTEGER, FALSE,
273    "# Maximum number of months to keep old entries in the seenfile.\n"},
274 
275   {"tellmonths",  &tellmonths, (void *)2,          CFG_INTEGER, FALSE,
276    "# Maximum number of months to keep old entries in tellfile.\n"},
277 
278   {"warnmonths",  &warnmonths, (void *)3,          CFG_INTEGER, FALSE,
279    "# Maximum number of months to keep old entries in warnlist.\n"},
280 
281   {"newsexpire",  &newsexpire, (void *)90,         CFG_INTEGER, FALSE,
282    "# Default number of days of a news item to live.\n"},
283 
284   {"netburp",     &netburp,   (void *)NETBURPTIME, CFG_INTEGER, FALSE,
285    "# Netburp is the period (in seconds) after a netjoin where FLOOD\n"
286    "# is off, to prevent kicking people because of an sudden exchange\n"
287    "# of data between the servers (which usually is called a netburp).\n"},
288 
289   {"myhost",      dancer_myhost, "",               CFG_STRING, FALSE,
290    "# The IP number of the host that runs this bot. It should be a valid\n"
291    "# IP from one of your machine's network interfaces or this is likely\n"
292    "# to severly confuse this program. DON'T set this if you don't know\n"
293    "# exactly what you are doing and why.\n"},
294 
295   {"askforops",   askforops,    "",                CFG_STRING, FALSE,
296    "# Askforops is an action that the bot does when entering the\n"
297    "# channel, to make people aware that it would like to get opped.\n"},
298 
299   {"opaction",    opaction,     "",                CFG_STRING, FALSE,
300    "# Opaction is a kind of passive auto-op. It ops a user/bot if\n"
301    "# the level is exactly LEVELBOT and it makes an action starting\n"
302    "# with the content of opaction. If no opaction is supplied this\n"
303    "# passive auto-op is disabled.\n"},
304 
305   {NULL, NULL, NULL, CFG_COMMENT, FALSE,
306   "#################################################################\n"
307   "# Normally you aren't required to touch anything below.\n"
308   "# Use LOG, CHGCMDLEV and SET commands to configure the\n"
309   "# appropriate items online.\n"},
310 
311 /* --- Filenames --- */
312   {"banfile",     banfile,      BANFILE,           CFG_STRING, FALSE,
313    "# File name to store ban information in.\n"},
314 
315   {"explainfile", expfile,      EXPFILE,           CFG_STRING, FALSE,
316    "# Explainfile contains all standard explainations. Explainations that\n"
317    "# are added force this file to get re-written.\n"},
318 
319   {"fplfile",     fplconf,      FPLFILE,           CFG_STRING, FALSE,
320    "# fplfile is the name of the fpl-config file.\n"},
321 
322   {"funcfile",    funcfile,     FUNCFILE,          CFG_STRING, FALSE,
323    "# Funcfile is the name of the file which is read for the 'funcs'/\n"
324    "# aliases Dancer supports.\n"},
325 
326   {"logfile",     logfile,      LOGFILE,           CFG_STRING, FALSE,
327    "# This is the prefix to use for the log files.\n"},
328 
329   {"newsfile",    newsfile,     NEWSFILE,          CFG_STRING, FALSE,
330    "# File name to store news items in (for the NEWS* commands).\n"},
331 
332   {"seenfile",    seenfile,     SEENFILE,          CFG_STRING, FALSE,
333    "# Seenfile contains all the info on who has visited the channel.\n"},
334 
335   {"servfile",    servfile,     SERVFILE,          CFG_STRING, FALSE,
336    "# Servfile is the name of the file used for the server list.\n"},
337 
338   {"setfile",     setfile,      SETFILE,           CFG_STRING, FALSE,
339    "# Setfile contains all saved settings. It overrides the config stored\n"
340    "# in the .config file.\n"},
341 
342   {"tellfile",    tellfile,     TELLFILE,          CFG_STRING, FALSE,
343    "# Tellfile is the file where all TELL messages are stored.\n"},
344 
345   {"userfile",    userfile,     USERFILE,          CFG_STRING, FALSE,
346    "# This file contains all registered users.\n"},
347 
348   {"warnfile",    warnfile,     WARNFILE,          CFG_STRING, FALSE,
349    "# File name to store warning information in.\n"},
350 
351 /* --- User / command levels --- */
352   {"level.anybody",    &levels[0],  (void *)  0, CFG_INTEGER, FALSE,
353    "# Which numerical value to use for ANYBODY.\n"},
354 
355   {"level.recognized", &levels[1],  (void *) 10, CFG_INTEGER, FALSE,
356    "# Which numerical value to use for RECOGNIZED.\n"},
357 
358   {"level.chanop",     &levels[2],  (void *) 20, CFG_INTEGER, FALSE,
359    "# Which numerical value to use for CHANOP.\n"},
360 
361   {"level.trusted",    &levels[3],  (void *) 50, CFG_INTEGER, FALSE,
362    "# Which numerical value to use for TRUSTED.\n"},
363 
364   {"level.expert",     &levels[4],  (void *)100, CFG_INTEGER, FALSE,
365   "# Which numerical value to use for EXPERT.\n"},
366 
367   {"level.maintainer", &levels[5],  (void *)200, CFG_INTEGER, FALSE,
368    "# Which numerical value to use for MAINTAINER.\n"},
369 
370   {"level.owner",      &levels[6],  (void *)242, CFG_INTEGER, FALSE,
371    "# Which numerical value to use for OWNER.\n"},
372 
373   {"linkvalue", &linkvalue, (void *)0,   CFG_INTEGER, FALSE, NULL},
374 
375   {NULL, NULL, NULL, CFG_COMMENT, FALSE,
376   "#################################################################\n"
377   "# Everything below this line can be configured online using\n"
378   "# LOG, CHGCMDLEV and SET commands.\n"},
379 
380   {"activelog",        &activelog,    (void *)-1, CFG_INTEGER, TRUE,
381    "# Numerical representation of what information that should be\n"
382    "# included in the logfile.\n"},
383 
384   /* This *is* saved but differently (thus the FALSE): */
385   {"chgcmdlev",      ChgCmdLev,       NULL,      CFG_FUNCTION, FALSE,
386    "# Change the level requirement for single command by entering\n"
387    "# them here. Use the format <command> <level>. One entry per\n"
388    "# line and chgcmdlev:-label.\n"},
389 
390 /* --- Ban settings --- */
391   {"ban.autounban",    &autounban,    BFALSE,    CFG_SWITCH, TRUE,
392    "# Automatically unbans the least important ban when the channel\n"
393    "# reaches 18 bans in the list.\n"},
394 
395   {"ban.banprotect",   &banprotect,   BTRUE,     CFG_SWITCH, TRUE,
396    "# Enable banprotection.\n"},
397 
398   {"ban.banuserkicks", &banuserkicks, BTRUE,     CFG_SWITCH, TRUE,
399    "# Count user kicks when deciding whether to ban because of too\n"
400    "# many kicks within a certain time.\n"},
401 
402   {"ban.bantime",      &pubbantime,   (void *)0, CFG_INTEGER, TRUE,
403    "# Default ban time (in seconds) for public bans in the channel.\n"},
404 
405   {"ban.botbantime",   &defbantime,   (void *)0, CFG_INTEGER, TRUE,
406    "# Default time (in seconds) of a bot ban without time specified.\n"},
407 
408   {"ban.kickban",      &kickbans,     BTRUE,     CFG_SWITCH, TRUE,
409    "# Kick banned users if they join (split or lagged servers).\n"},
410 
411   {"ban.reportban",    &reportban,    BFALSE,    CFG_SWITCH, TRUE,
412    "# Report about bans in public.\n"},
413 
414 /* --- Flood settings --- */
415   {"flood.beep",       &beepcheck,    BTRUE,     CFG_SWITCH, TRUE,
416    "# Kick beepers.\n"},
417 
418   {"flood.bomb",       &avalance,     BTRUE,     CFG_SWITCH, TRUE,
419    "# Kick CTCP bombers.\n"},
420 
421   {"flood.colour",     &colourcheck,  BTRUE,     CFG_SWITCH, TRUE,
422    "# Kick colourcode (ab)users.\n"},
423 
424   {"flood.ctcp",       &ctcpmode,     BTRUE,     CFG_SWITCH, TRUE,
425    "# Prevent CTCP floods.\n"},
426 
427   {"flood.fakename",   &fakenamemode, BTRUE,     CFG_SWITCH, TRUE,
428    "# Kick guests joining with faked host/domain names.\n"},
429 
430   {"flood.flood",      &floodmode,    BTRUE,     CFG_SWITCH, TRUE,
431    "# Prevent public floods.\n"},
432 
433   {"flood.multi",      &multimode,    BTRUE,     CFG_SWITCH, TRUE,
434    "# Prevent user to join from the same account multiple times.\n"},
435 
436   {"flood.nick",       &nickflood,    BTRUE,     CFG_SWITCH, TRUE,
437    "# Kick nick flooders.\n"},
438 
439   {"flood.repeat",     &repeatmode,   BTRUE,     CFG_SWITCH, TRUE,
440    "# Prevent repeaters.\n"},
441 
442   {"flood.upper",      &uppercheck,   BFALSE,    CFG_SWITCH, TRUE,
443    "# Check for uppercase violations.\n"},
444 
445   {"flood.xdcc",       &xdccmode,     BFALSE,    CFG_SWITCH, TRUE,
446    "# Kick xdcc announcers.\n"},
447 
448 /* --- Misc settings --- */
449   {"misc.autojoin",    &autojoin,     BTRUE,     CFG_SWITCH, TRUE,
450    "# Join channel automatically if being kicked.\n"},
451 
452   {"misc.autoop",      &autoop,       BFALSE,    CFG_SWITCH, TRUE,
453    "# Enable auto-ops.\n"},
454 
455   {"misc.ckey",        ckey,          "",        CFG_STRING, TRUE,
456    "# Ckey is the channel key.\n"},
457 
458   {"misc.cmode",       cmodes,        "",        CFG_STRING, TRUE,
459    "# Cmode determines which channel modes the bot should try\n"
460    "# to keep if lockmode is on.\n"},
461 
462   {"misc.climit",      &climit,       (void *)0, CFG_INTEGER, TRUE,
463    "# Climit is the channel +l limit, 0 disables.\n"},
464 
465   {"misc.deop",        &deopprotect,  BTRUE,     CFG_SWITCH, TRUE,
466    "# Check for massive deoppers.\n"},
467 
468   {"misc.helpsyntax",  &helpsyntax,   BTRUE,     CFG_SWITCH, TRUE,
469    "# Make the syntax information get displayed when HELP is used.\n"},
470 
471   {"misc.invitable",   &invitable,    BTRUE,     CFG_SWITCH, TRUE,
472    "# Join a channel if being /invite'd (by a high-level user).\n"},
473 
474   {"misc.key",         &lockkey,      BTRUE,     CFG_SWITCH, TRUE,
475    "# Lock the channel key.\n"},
476 
477   {"misc.limit",       &locklimit,    BTRUE,     CFG_SWITCH, TRUE,
478    "# Lock the channel limit.\n"},
479 
480   {"misc.mode",        &lockmode,     BTRUE,     CFG_SWITCH, TRUE,
481    "# Lock the channel mode.\n"},
482 
483   {"misc.netsplit",    &netsplitmode, BTRUE,     CFG_SWITCH, TRUE,
484    "# Deop and unban server ops/bans.\n"},
485 
486   {"misc.opprotect",   &opprotect,    BFALSE,   CFG_SWITCH, TRUE,
487    "# Protect channel operators. With this enabled, non-chanop users will\n"
488    "# not be allowed to deop or kick chan-op users. The bot will deop the\n"
489    "# deoper and op the chan-op users (if they were deoped).\n"},
490 
491   {"misc.strictop",    &strictopmode, BFALSE,   CFG_SWITCH, TRUE,
492    "# With this enabled, non-recognized users will not be allowed to get\n"
493    "# chanop status by people without very high status. LEVELOP can\n"
494    "# be used to define the level of the allowed ops.\n"},
495 
496 /* --- Threshold settings --- */
497   {"thres.floodbeeps", &floodbeeps,      (void *)2,  CFG_INTEGER, TRUE,
498    "# Maximum number of public beeps allowed.\n"},
499 
500   {"thres.floodjoins", &floodjoins,      (void *)3,  CFG_INTEGER, TRUE,
501    "# Number of joins accepted from the same hostpattern.\n"},
502 
503   {"thres.floodrate",  &floodrate,       (void *)3,  CFG_INTEGER, TRUE,
504    "# Prevent more than FLOODRATE messages in FLOODTIME seconds.\n"},
505 
506   {"thres.repeatrate", &floodrepeatrate, (void *)3,  CFG_INTEGER, TRUE,
507    "# Prevent more than FLOODRATE messages in FLOODTIME seconds.\n"},
508 
509   {"thres.repeattime", &floodrepeattime, (void *)20, CFG_INTEGER, TRUE,
510    "# See FLOODREPEATRATE.\n"},
511 
512   {"thres.floodtime",  &floodtime,       (void *)1,  CFG_INTEGER, TRUE,
513    "# See FLOODRATE.\n"},
514 
515 /* --- Verbose settings --- */
516   {"verbose.comment",  &dispcomment,  BFALSE,    CFG_SWITCH, TRUE,
517    "# Enable user-comments on join.\n"},
518 
519   {"verbose.mute",     &mute,         BFALSE,    CFG_SWITCH, TRUE,
520    "# Enable/disable mute mode.\n"},
521 
522   {"verbose.say",      &saymode,      BTRUE,     CFG_SWITCH, TRUE,
523    "# Enable/disable the SAY and ME commands.\n"},
524 
525   {"verbose.talk",     &talkative,    BFALSE,    CFG_SWITCH, TRUE,
526    "# Talkative mode.\n"},
527 
528   {"verbose.warn",     &warnmode,     BTRUE,     CFG_SWITCH, TRUE,
529    "# Report about people on the warnlist.\n"},
530 
531   {"verbose.welcome",  &welcome,      BFALSE,    CFG_SWITCH, TRUE,
532    "# Welcome new users.\n"},
533 
534 /* --- Owner settings --- */
535   {"owner.execprotect", &execprotect, BTRUE,     CFG_SWITCH, TRUE,
536    "# Prevents shell executions by the bot.\n"},
537 
538   {"owner.identprotect", &identprotect, BTRUE,   CFG_SWITCH, TRUE,
539    "# Prevent users from being able to use the IDENT command.\n"},
540 
541   {"owner.language",   language,      "English", CFG_STRING, TRUE,
542    "# Set primary language for the bot to use.\n"
543    "# ('dansk', 'deutsch', 'english', 'espa�ol', 'fran�ais', 'nederlands',\n"
544    "# 'norsk', 'svenska' and 'suomi' are supported languages).\n"},
545 
546   {"owner.loopservers", &loopservers, BFALSE,    CFG_SWITCH, TRUE,
547    "# Makes the bot loop the serverlist forever.\n"},
548 
549   {"owner.oplevel",    &oplevel,      (void *)0, CFG_INTEGER, TRUE,
550    "# Users with level lower than OPLEVEL are not allowed to get OPs in\n"
551    "# the channel. Setting oplevel to 0 sets it off. See also opprotect\n"
552    "# and strictop.\n"},
553 };
554 
555 
556 /* --- MakeConfig ------------------------------------------------- */
557 
MakeConfig(void)558 void MakeConfig(void)
559 {
560   int i;
561   itemlist *l;
562 
563   snapshot;
564   fprintf(stdout,
565           "#################################################################\n"
566           "# This is an autogenerated config file done by " VERSIONMSG "\n"
567           "#\n"
568           "# You _must_ set the three entries server, channel, and nick.\n"
569           "# (remove preceeding '#' letters to make the labels \"take effect\")\n"
570           );
571   for (i=0; i < sizeof(cfg)/sizeof(cfg[0]); i++) {
572     if (cfg[i].verbose) {
573       /* Print the help string */
574       fprintf(stdout, "\n%s", cfg[i].verbose);
575 
576       /* Print out the item and its value (commented out and with default
577        * value if unchanged) */
578       switch (cfg[i].flags) {
579 
580         case CFG_SWITCH:
581           if (cfg[i].changed)
582             fprintf(stdout, "%s = %s\n", cfg[i].label, *(bool *)cfg[i].value ? "On" : "Off");
583           else
584             fprintf(stdout, "#%s = %s\n", cfg[i].label, cfg[i].def ? "On" : "Off");
585           break;
586 
587         case CFG_INTEGER:
588           if (cfg[i].changed)
589             fprintf(stdout, "%s = %d\n", cfg[i].label, *(int *)cfg[i].value);
590           else
591             fprintf(stdout, "#%s = %d\n", cfg[i].label, (int)cfg[i].def);
592           break;
593 
594         case CFG_STRING:
595           if (cfg[i].changed)
596             fprintf(stdout, "%s = %s\n", cfg[i].label, (char *)cfg[i].value);
597           else
598             fprintf(stdout, "#%s = %s\n", cfg[i].label, (char *)cfg[i].def);
599           break;
600 
601         case CFG_LIST:
602           if (cfg[i].changed) {
603             int n=0;
604 
605             fprintf(stdout, "%s = ", cfg[i].label);
606             for (l = First(*(itemlist **)cfg[i].value); l; l = Next(l)) {
607               fprintf(stdout, "%s%s", n ? "," : "", l->pointer);
608               n++;
609             }
610             fprintf(stdout, "\n");
611           }
612           else
613             fprintf(stdout, "#%s = %s\n", cfg[i].label, (char *)cfg[i].def);
614           break;
615 
616         default:
617           break;
618 
619       }
620     }
621   }
622 }
623 
624 /* --- BuildList -------------------------------------------------- */
625 
BuildList(itemlist ** appendHead,char * string)626 itemlist *BuildList(itemlist **appendHead, char *string)
627 {
628   char buffer[BIGBUFFER];
629   int i=0;
630   itemlist *head, *l;
631 
632   snapshot;
633   if ((NULL == appendHead) || (NULL == *appendHead)) {
634     head = NewList(itemlist);
635     if (appendHead)
636       *appendHead = head;
637   }
638   else
639     head = *appendHead;
640 
641   if (head) {
642 #ifdef HAVE_N_IN_SCANF
643     int index;
644 
645    /*
646     * Execution of a %n directive does not increment the
647     * assignment  count  returned  at  the completion of
648     * execution of the function.
649     */
650     while (1 == StrScan(&string[i], "%"BIGBUFFERTXT"[^,\n] %n", buffer, &index)) {
651       i += index;
652 #else
653     while (1 == StrScan(&string[i], "%"BIGBUFFERTXT"[^,\n]", buffer)) {
654       i += StrLength(buffer);
655 #endif
656       if (',' == string[i])
657         i++;
658 
659       l = NewEntry(itemlist);
660       if (l) {
661         InsertLast(head, l);
662         l->pointer = (void *)StrDuplicate(buffer);
663       }
664       else {
665         break;
666       }
667     }
668   }
669   return head;
670 }
671 
672 /* --- PreConfig -------------------------------------------------- */
673 
674 static bool inited = FALSE;
675 
676 void PreConfig(void)
677 {
678   int i;
679 
680   snapshot;
681   for (i=0; i < sizeof(cfg)/sizeof(cfg[0]); i++) {
682     switch (cfg[i].flags) {
683 
684       case CFG_SWITCH:
685         *(bool *)cfg[i].value = (bool)((int)cfg[i].def);
686         break;
687 
688       case CFG_INTEGER:
689         *(int *)cfg[i].value = (int)cfg[i].def;
690         break;
691 
692       case CFG_STRING:
693 /* Need limit check!!! */
694         StrCopy((char *)cfg[i].value, (char *)cfg[i].def);
695         break;
696 
697       default:
698         break;
699 
700     }
701     cfg[i].changed = FALSE;
702   }
703 
704   inited = TRUE;
705 }
706 
707 /* --- PostConfig ------------------------------------------------- */
708 
709 void PostConfig(void)
710 {
711   int i;
712 
713   snapshot;
714   for (i=0; i < sizeof(cfg)/sizeof(cfg[0]); i++) {
715     if (CFG_LIST == cfg[i].flags) {
716       if (NULL == First(*(itemlist **)cfg[i].value))
717         BuildList((itemlist **)cfg[i].value, cfg[i].def);
718     }
719   }
720   defaultlang = LanguageNum(language);
721 }
722 
723 /* --- ConfigAddItem ---------------------------------------------- */
724 
725 void ConfigAddItem(char *line)
726 {
727   char keyword[MINIBUFFER];
728   char valuebuffer[BIGBUFFER];
729   int i;
730 
731   snapshot;
732   if (2 <= StrScan(line, "%"MINIBUFFERTXT"[a-zA-Z0-9.] %*[=: ] %"BIGBUFFERTXT"[^\n]",
733                    keyword, valuebuffer)) {
734     for (i=0; i < sizeof(cfg)/sizeof(cfg[0]); i++) {
735       if (StrEqual(keyword, cfg[i].label)) { /* Match */
736         switch (cfg[i].flags) {
737 
738           case CFG_SWITCH:
739             if (StrEqual(valuebuffer, "ON") ||
740                 StrEqual(valuebuffer, "YES") ||
741                 atoi(valuebuffer))
742               *(bool *)cfg[i].value = TRUE;
743             else
744               *(bool *)cfg[i].value = FALSE;
745             break;
746 
747           case CFG_INTEGER:
748             *(int *)cfg[i].value = atoi(valuebuffer);
749             break;
750 
751           case CFG_STRING:
752 /* Need limit check!!! */
753             StrCopy((char *)cfg[i].value, valuebuffer);
754             break;
755 
756           case CFG_LIST:
757             BuildList((itemlist **)cfg[i].value, valuebuffer);
758             break;
759 
760           case CFG_FUNCTION:
761             (*(void (*)(char *, char *))cfg[i].value)(cfg[i].def, valuebuffer);
762             break;
763 
764           default:
765             break;
766 
767         }
768         cfg[i].changed = TRUE;
769         break;
770       }
771     }
772   }
773 }
774 
775 /* --- ConfigInit ------------------------------------------------- */
776 
777 bool ConfigInit(void)
778 {
779   char line[MAXLINE];
780   FILE *f;
781 
782   snapshot;
783   PreConfig();
784 
785   f = fopen(CONFIGFILE, "r");
786   if (f) {
787     while (fgets(line, sizeof(line), f)) {
788       switch (line[0]) {
789 
790         case '#':
791         case '\n':
792           break;
793 
794         default:
795           ConfigAddItem(line);
796           break;
797 
798       }
799     }
800     fclose(f);
801 
802     f = fopen(setfile, "r");
803     if (f) {
804       while (fgets(line, sizeof(line), f)) {
805         switch (line[0]) {
806 
807           case '#':
808           case '\n':
809             break;
810 
811           default:
812             ConfigAddItem(line);
813             break;
814 
815         }
816       }
817       fclose(f);
818     }
819 
820     PostConfig();
821 
822     StrCopy(chanmodes, cmodes);
823     StrCopy(chankey, ckey);
824     opactionlen = StrLength(opaction);
825 
826     InitSets();
827 
828     return TRUE;
829   }
830   return FALSE;
831 }
832 
833 /* --- SaveSettings ----------------------------------------------- */
834 
835 void SaveSettings(void)
836 {
837   extern struct Command cmds[];
838   char tempfile[MIDBUFFER];
839   bool ok = TRUE;
840   int numchanged = 0;
841   int i;
842   FILE *f;
843 
844   snapshot;
845   if (NIL == setfile[0])
846     return;
847 
848   StrFormatMax(tempfile, sizeof(tempfile), "%s~", setfile);
849 
850   f = fopen(tempfile, "w");
851   if (f) {
852     for (i=0; ok && (i < sizeof(cfg)/sizeof(cfg[0])); i++) {
853       if (cfg[i].setting) {
854         if (cfg[i].verbose) {
855           /* Write comment */
856           if (0 > fprintf(f, "\n%s", cfg[i].verbose)) {
857             ok = FALSE;
858             break;
859           }
860         }
861         switch (cfg[i].flags) {
862 
863           case CFG_SWITCH:
864             if (0 > fprintf(f, "%s = %s\n", cfg[i].label, OnOff(*(bool *)cfg[i].value)))
865               ok = FALSE;
866             break;
867 
868           case CFG_INTEGER:
869             if (0 > fprintf(f, "%s = %d\n", cfg[i].label, *(int *)cfg[i].value))
870               ok = FALSE;
871             break;
872 
873           case CFG_STRING:
874             if (0 > fprintf(f, "%s = %s\n", cfg[i].label, (char *)cfg[i].value))
875               ok = FALSE;
876             break;
877 
878           default:
879             break;
880 
881         }
882       }
883     }
884 
885     /* Save new command levels */
886     for (i=0; ok && cmds[i].name; i++) {
887       if (cmds[i].flags & CMD_CHANGED) {
888         if (1 == ++numchanged) {
889           if (0 > fprintf(f, "\n# Here follows a list of commands with their new levels:\n")) {
890             ok = FALSE;
891             break;
892           }
893         }
894         /* Save the new level */
895         if (0 > fprintf(f, "chgcmdlev: %s %d\n", cmds[i].name, cmds[i].level)) {
896           ok = FALSE;
897           break;
898         }
899       }
900     }
901     fclose(f);
902 
903     if (ok)
904       rename(tempfile, setfile);
905   }
906 }
907 
908 /* --- ConfigCleanup ---------------------------------------------- */
909 
910 void ConfigCleanup(void)
911 {
912   snapshot;
913   if (!inited)
914     return;
915 
916   SaveSettings();
917 
918   /* Flush server list */
919   DeleteList(serverHead, FreeList);
920 }
921 
922 
923 /*********************************************************************
924  * The following piece of source is the new SET functions.
925  ********************************************************************/
926 
927 #define SPECIAL_CMODE    1
928 #define SPECIAL_CKEY     2
929 #define SPECIAL_LANGUAGE 3
930 
931 static struct SetItem ban[] = {
932   {"AUTOUNBAN",    LEVEL_ANYBODY, SET_ONOFF, &autounban, 0, msg_helpset_autounban},
933   {"BANPROTECT",   LEVEL_ANYBODY, SET_ONOFF, &banprotect, 0, msg_helpset_banprotect},
934   {"BANUSERKICKS", LEVEL_ANYBODY, SET_ONOFF, &banuserkicks, 0, msg_helpset_banuserkicks},
935   {"BANTIME",      LEVEL_BOT,     SET_TIME,  &pubbantime, 0, msg_helpset_bantime},
936   {"BOTBANTIME",   LEVEL_BOT,     SET_TIME,  &defbantime, 0, msg_helpset_botbantime},
937   {"KICKBAN",      LEVEL_ANYBODY, SET_ONOFF, &kickbans, 0, msg_helpset_kickban},
938   {"REPORTBAN",    LEVEL_ANYBODY, SET_ONOFF, &reportban, 0, msg_helpset_reportban},
939   {NULL, 0, 0, NULL, 0, NULL} /* End series with a NULL-entry */
940 };
941 
942 static struct SetItem flood[] = {
943   {"BEEP",     LEVEL_ANYBODY, SET_ONOFF, &beepcheck, 0, msg_helpset_beep},
944   {"BOMB",     LEVEL_ANYBODY, SET_ONOFF, &avalance, 0, msg_helpset_bomb},
945   {"COLOUR",   LEVEL_ANYBODY, SET_ONOFF, &colourcheck, 0, msg_helpset_colour},
946   {"CTCP",     LEVEL_ANYBODY, SET_ONOFF, &ctcpmode, 0, msg_helpset_ctcp},
947   {"FAKENAME", LEVEL_ANYBODY, SET_ONOFF, &fakenamemode, 0, msg_helpset_fakename},
948   {"FLOOD",    LEVEL_ANYBODY, SET_ONOFF, &floodmode, 0, msg_helpset_flood},
949   {"MULTI",    LEVEL_ANYBODY, SET_ONOFF, &multimode, 0, msg_helpset_multi},
950   {"NICK",     LEVEL_ANYBODY, SET_ONOFF, &nickflood, 0, msg_helpset_nick},
951   {"REPEAT",   LEVEL_ANYBODY, SET_ONOFF, &repeatmode, 0, msg_helpset_repeat},
952   {"UPPER",    LEVEL_ANYBODY, SET_ONOFF, &uppercheck, 0, msg_helpset_upper},
953   {"XDCC",     LEVEL_ANYBODY, SET_ONOFF, &xdccmode, 0, msg_helpset_xdcc},
954   {NULL, 0, 0, NULL, 0, NULL} /* End series with a NULL-entry */
955 };
956 
957 static struct SetItem misc[] = {
958   {"AUTOJOIN",  LEVEL_ANYBODY, SET_ONOFF, &autojoin, 0, msg_helpset_autojoin},
959   {"AUTOOP",    LEVEL_ANYBODY, SET_ONOFF, &autoop, 0, msg_helpset_autoop},
960   {"CKEY",      LEVEL_ANYBODY, SET_SPEC,  NULL, SPECIAL_CKEY,  msg_helpset_ckey},
961   {"CMODE",     LEVEL_ANYBODY, SET_SPEC,  NULL, SPECIAL_CMODE, msg_helpset_cmode},
962   {"CLIMIT",    LEVEL_ANYBODY, SET_NUM,   &climit, 0, msg_helpset_climit},
963   {"DEOP",      LEVEL_ANYBODY, SET_ONOFF, &deopprotect, 0, msg_helpset_deop},
964   {"HELPSYNTAX",LEVEL_ANYBODY, SET_ONOFF, &helpsyntax, 0, msg_helpset_helpsyntax},
965   {"INVITE",    LEVEL_ANYBODY, SET_ONOFF, &invitable, 0, msg_helpset_invite},
966   {"KEY",       LEVEL_ANYBODY, SET_ONOFF, &lockkey, 0, msg_helpset_key},
967   {"LIMIT",     LEVEL_ANYBODY, SET_ONOFF, &locklimit, 0, msg_helpset_limit},
968   {"MODE",      LEVEL_ANYBODY, SET_ONOFF, &lockmode, 0, msg_helpset_mode},
969   {"NETSPLIT",  LEVEL_ANYBODY, SET_ONOFF, &netsplitmode, 0, msg_helpset_netsplit},
970   {"OPPROTECT", LEVEL_BOT,     SET_ONOFF, &opprotect, 0, msg_helpset_opprotect},
971   {"STRICTOP",  LEVEL_BOT,     SET_ONOFF, &strictopmode, 0, msg_helpset_strictop},
972   {NULL, 0, 0, NULL, 0, NULL} /* End series with a NULL-entry */
973 };
974 
975 static struct SetItem thres[] = {
976   {"FLOODBEEPS",      LEVEL_ANYBODY, SET_NUM, &floodbeeps, 0, msg_helpset_floodbeeps},
977   {"FLOODJOINS",      LEVEL_ANYBODY, SET_NUM, &floodjoins, 0, msg_helpset_floodjoins},
978   {"FLOODRATE",       LEVEL_ANYBODY, SET_NUM, &floodrate, 0, msg_helpset_floodrate},
979   {"FLOODREPEATRATE", LEVEL_ANYBODY, SET_NUM, &floodrepeatrate, 0, msg_helpset_floodrepeatrate},
980   {"FLOODREPEATTIME", LEVEL_ANYBODY, SET_NUM, &floodrepeattime, 0, msg_helpset_floodrepeattime},
981   {"FLOODTIME",       LEVEL_ANYBODY, SET_NUM, &floodtime, 0, msg_helpset_floodtime},
982   {NULL, 0, 0, NULL, 0, NULL} /* End series with a NULL-entry */
983 };
984 
985 static struct SetItem verbose[] = {
986   {"COMMENT", LEVEL_ANYBODY, SET_ONOFF, &dispcomment, 0, msg_helpset_comment},
987   {"MUTE",    LEVEL_ANYBODY, SET_ONOFF, &mute, 0, msg_helpset_mute},
988   {"SAY",     LEVEL_ANYBODY, SET_ONOFF, &saymode, 0, msg_helpset_say},
989   {"TALK",    LEVEL_ANYBODY, SET_ONOFF, &talkative, 0, msg_helpset_talk},
990   {"WARN",    LEVEL_ANYBODY, SET_ONOFF, &warnmode, 0, msg_helpset_warn},
991   {"WELCOME", LEVEL_ANYBODY, SET_ONOFF, &welcome, 0, msg_helpset_welcome},
992   {NULL, 0, 0, NULL, 0, NULL} /* End series with a NULL-entry */
993 };
994 
995 static struct SetItem owner[] = {
996   {"EXECPROTECT",  LEVEL_ANYBODY, SET_ONOFF, &execprotect, 0, msg_helpset_execprotect},
997   {"IDENTPROTECT", LEVEL_ANYBODY, SET_ONOFF, &identprotect, 0, msg_helpset_identprotect},
998   {"LANGUAGE",     LEVEL_ANYBODY, SET_SPEC,  NULL, SPECIAL_LANGUAGE, msg_helpset_language},
999   {"LOOPSERVERS",  LEVEL_ANYBODY, SET_ONOFF, &loopservers, 0, msg_helpset_loopservers},
1000   {"OPLEVEL",      LEVEL_ANYBODY, SET_NUM,   &oplevel, 0, msg_helpset_oplevel},
1001   {NULL, 0, 0, NULL, 0, NULL} /* End series with a NULL-entry */
1002 };
1003 
1004 static struct SetGroup sets[] = {
1005   {"BAN",        LEVEL_ANYBODY, ban,     msg_setgroup_ban},
1006   {"FLOOD",      LEVEL_ANYBODY, flood,   msg_setgroup_flood},
1007   {"MISC",       LEVEL_ANYBODY, misc,    msg_setgroup_misc},
1008   {"THRESHOLDS", LEVEL_ANYBODY, thres,   msg_setgroup_thres},
1009   {"VERBOSE",    LEVEL_ANYBODY, verbose, msg_setgroup_verbose},
1010   {"OWNER",      LEVEL_OWNER,   owner,   msg_setgroup_owner},
1011 };
1012 
1013 
1014 /* --- ShowSetItem ------------------------------------------------ */
1015 
1016 /*
1017  *  Fills in a buffer, showing the contents of the specified item.
1018  *  Returns buffer.
1019  */
1020 
1021 static char *ShowSetItem(char *buffer, int buffer_size, struct SetItem *item)
1022 {
1023   char level[32] = "";
1024 
1025   snapshot;
1026   if (item->level) {
1027     StrFormatMax(level, sizeof(level), " [%d]", item->level);
1028   }
1029 
1030   StrFormatMax(buffer, buffer_size, "%s%s = ", item->name, level);
1031 
1032   switch (item->flags) {
1033 
1034     case SET_ONOFF:
1035       StrAppendMax(buffer, buffer_size, OnOff(*(bool *)item->value));
1036       break;
1037 
1038     case SET_NUM:
1039       StrFormatAppendMax(buffer, buffer_size, "%d", *(int *)item->value);
1040       break;
1041 
1042     case SET_TIME:
1043       StrAppendMax(buffer, buffer_size, SecsToString(*(int *)item->value));
1044       break;
1045 
1046     case SET_SPEC:
1047       switch (item->ID) {
1048 
1049         case SPECIAL_CMODE:
1050           StrAppendMax(buffer, buffer_size, cmodes);
1051           break;
1052 
1053         case SPECIAL_CKEY:
1054           StrAppendMax(buffer, buffer_size, ckey);
1055           break;
1056 
1057         case SPECIAL_LANGUAGE:
1058           StrAppendMax(buffer, buffer_size, language);
1059           break;
1060 
1061       }
1062       break;
1063 
1064   }
1065 
1066   return buffer;
1067 }
1068 
1069 /* --- ShowSetGroup ----------------------------------------------- */
1070 
1071 /* Shows the SETs of the specified group. All items in the group
1072  * will get displayed (even if some might have higher access levels)
1073  * if the person has the access.
1074  *
1075  * Output format:
1076  * "Group OWNER [242]: EXECPROTECT = on, OTHERFLAG = off"
1077  */
1078 
1079 static void ShowSetGroup(char *from, struct SetGroup *grp)
1080 {
1081   char buffer[MIDBUFFER];
1082   char set[MINIBUFFER];
1083   char level[32] = "";
1084   int count = 0;
1085   int i;
1086   struct SetItem *item = grp->group;
1087 
1088   snapshot;
1089   if (grp->level > current->level) {
1090     return;
1091   }
1092 
1093   if (grp->level) {
1094     StrFormatMax(level, sizeof(level), " [%d]", grp->level);
1095   }
1096 
1097   StrFormatMax(buffer, sizeof(buffer), "<%s%s>: ", grp->name, level);
1098 
1099   for (i=0; item[i].name; i++) {
1100 
1101     /* Don't show non-accessible items */
1102     if (item[i].level > current->level)
1103       continue;
1104 
1105     ShowSetItem(set, sizeof(set), &item[i]);
1106 
1107     if ((StrLength(buffer) + StrLength(set) + 1) >= sizeof(buffer)) {
1108       Send(from, buffer);
1109       buffer[0] = (char)0;
1110       count = 0;
1111     }
1112 
1113     StrFormatAppendMax(buffer, sizeof(buffer), "%s%s", count ? ", " : "", set);
1114     count++;
1115   }
1116 
1117   Send(from, buffer);
1118 }
1119 
1120 /* --- FindItemInGroup -------------------------------------------- */
1121 
1122 int static FindItemInGroup(struct SetGroup *grp, char *label,
1123                            struct SetItem **item)
1124 {
1125   int i;
1126   int amount = 0;
1127   struct SetItem *group = grp->group;
1128   struct SetItem *hit = NULL;
1129 
1130   snapshot;
1131   for (i=0; group[i].name; i++) {
1132 #if 0
1133     if (group[i].level > current->level)
1134       /*
1135        * Don't even bother to try labels we aren't allowed
1136        * to access.
1137        */
1138       continue;
1139 #endif
1140 
1141     if (StrEqualMax(group[i].name, StrLength(label), label)) {
1142       *item = &group[i];
1143       amount++;
1144     }
1145   }
1146   return amount;
1147 }
1148 
1149 /* --- GimmeGroup ------------------------------------------------- */
1150 
1151 /* Returns the group number which matches the 'group' parameter.
1152  * Set 'error' to TRUE to get error reports, FALSE will make it
1153  * return -1 silently.
1154  */
1155 
1156 static struct SetGroup *GimmeGroup(char *from, char *group, bool error)
1157 {
1158   int grpnum = -1;
1159   int amount = 0;
1160   int i;
1161 
1162   snapshot;
1163   for (i=0; i < sizeof(sets)/sizeof(sets[0]); i++) {
1164 #if 0
1165     if (sets[i].level > current->level)
1166       /*
1167        * Don't even bother to search for a group that we aren't allowed
1168        * to access.
1169        */
1170       continue;
1171 #endif
1172     if (StrEqualMax(sets[i].name, StrLength(group), group)) {
1173       amount++;
1174       grpnum = i;
1175     }
1176   }
1177 
1178   if (0 == amount) {
1179     if (error)
1180       Sendf(errfrom, GetText(msg_group_doesnt_exist), group);
1181     return NULL;
1182   }
1183   else if (2 <= amount) {
1184     if (error)
1185       Sendf(errfrom, GetText(msg_matches_several_groups), group);
1186     return NULL;
1187   }
1188   return &sets[grpnum];
1189 }
1190 
1191 /* --- GimmeItem -------------------------------------------------- */
1192 
1193 /* Returns the label which matches the 'label' parameter.
1194  * Set 'error' to TRUE to get error reports, FALSE will make it
1195  * return -1 silently.
1196  * The 'retgrp' will get a pointer to the group in which the label
1197  * was found.
1198  */
1199 
1200 struct SetItem *GimmeItem(char *from, char *group, char *label, bool error,
1201                           struct SetGroup **retgrp)
1202 
1203 {
1204   struct SetItem *hit;
1205   struct SetGroup *grp;
1206   int amount = 0;
1207   int i;
1208 
1209   snapshot;
1210   if (group[0]) {
1211     grp = GimmeGroup(from, group, TRUE);
1212     if (NULL == grp)
1213       return NULL;
1214     amount = FindItemInGroup(grp, label, &hit);
1215   }
1216   else {
1217     int num;
1218 
1219     for (i=0; i<sizeof(sets)/sizeof(sets[0]);i++) {
1220       num = FindItemInGroup(&sets[i], label, &hit);
1221       amount += num;
1222       if (num)
1223         grp = &sets[i];
1224     }
1225   }
1226   if (0 == amount) {
1227     if (error)
1228       Sendf(errfrom, GetText(msg_item_doesnt_exist),
1229             label, group[0] ? GetText(msg_in_that_group) : "");
1230     return NULL;
1231   }
1232   else if (2 <= amount) {
1233     if (error)
1234       Sendf(errfrom, GetText(msg_matches_several_items), label);
1235     return NULL;
1236   }
1237 
1238   *retgrp = grp;
1239   return hit;
1240 }
1241 
1242 /* --- HelpSet ---------------------------------------------------- */
1243 
1244 /* Display help text and information about the specified token.
1245  * (group or item)
1246  */
1247 
1248 int HelpSet(char *from, char *token)
1249 {
1250   char set[MINIBUFFER];
1251   struct SetItem *hit;
1252   struct SetGroup *grp;
1253 
1254   snapshot;
1255   /* First, try an item with this name */
1256   hit = GimmeItem(from, "", token, FALSE, &grp);
1257   if (hit) {
1258     SendMulti(from, "%s %s: %s (%s)", GetText(msg_set),
1259               hit->name, GetText(hit->help),
1260               (hit->level > current->level) ? GetText(msg_no_access) :
1261               ShowSetItem(set, sizeof(set), hit));
1262   }
1263   else {
1264     /* now, try a group names like this */
1265     grp = GimmeGroup(from, token, FALSE);
1266     if (grp) {
1267       SendMulti(from, "%s %s: %s.", GetText(msg_group), grp->name, GetText(grp->help));
1268     }
1269     else {
1270       Send(from, GetText(msg_cant_find_set_keyword));
1271       return TRUE;
1272     }
1273   }
1274   return FALSE;
1275 }
1276 
1277 /* --- ShowSetAll ------------------------------------------------- */
1278 
1279 /* Show all items in all groups */
1280 
1281 void ShowSetAll(char *from)
1282 {
1283   int i;
1284 
1285   snapshot;
1286   for (i=0; i < sizeof(sets)/sizeof(sets[0]); i++) {
1287     ShowSetGroup(from, &sets[i]);
1288   }
1289 }
1290 
1291 /* --- SetSet ----------------------------------------------------- */
1292 
1293 /* Called straight from command.c and the CmdSet(). Parse the input
1294  * and perform every action in here.
1295  */
1296 
1297 int SetSet(char *from, char *line)
1298 {
1299   char group[32], dot[32], label[32], value[32];
1300   int amount = 0;
1301   int i;
1302   struct SetItem *hit;
1303   struct SetGroup *grp;
1304 
1305   snapshot;
1306   i = StrScan(line, "%31[^ .\n]%31[. ]%31s %31s", group, dot, label, value);
1307 
1308   switch (i) {
1309 
1310     case -1:
1311     /*
1312      * This is what they call EOF, returned "If the input ends before
1313      * the first matching failure or conversion"
1314      */
1315     case 0:
1316       ShowSetAll(from);
1317       return FALSE;
1318 
1319     case 1:
1320       /* One parameter, show the SETs for the specified group */
1321       grp = GimmeGroup(from, group, FALSE);
1322       if (NULL == grp) {
1323         /* We'll try to show the group that item belongs to then */
1324         hit = GimmeItem(from, "", group, FALSE, &grp);
1325         if (NULL == hit) {
1326           Send(errfrom, GetText(msg_i_dont_understand));
1327           return TRUE;
1328         }
1329       }
1330       ShowSetGroup(from, grp);
1331       return FALSE;
1332 
1333     case 2:
1334     case 3:
1335       if ((2 == i) || StrIndex(dot, '.')) {
1336         /*
1337          * This just be an attempt to specify a group name, but we don't
1338          * have a value.
1339          */
1340         grp = GimmeGroup(from, group, TRUE);
1341         if (grp)
1342           ShowSetGroup(from, grp);
1343         else
1344           return TRUE;
1345         return FALSE;
1346       }
1347 /* Need limit check!!! */
1348       StrCopy(value, label);
1349       StrCopy(label, group);
1350       group[0] = (char)0; /* No group specified */
1351       break;
1352 
1353   }
1354 
1355   hit = GimmeItem(from, group, label, TRUE, &grp);
1356   if (NULL == hit)
1357     return TRUE;
1358 
1359   /*
1360    * Access control could be done at lower layers. Using it this way,
1361    * everything but actually changing the SET will be available to
1362    * anyone (i.e help and display current status).
1363    */
1364   if (grp->level > current->level ||
1365       hit->level > current->level) {
1366     Send(errfrom, GetText(msg_dont_touch_this));
1367     return TRUE;
1368   }
1369 
1370   switch (hit->flags) {
1371 
1372     case SET_ONOFF:
1373       *(bool *)hit->value = IsOn(value);
1374       break;
1375 
1376     case SET_NUM:
1377       *(int *)hit->value = atoi(value);
1378       if (StrEqualCase(hit->name, "OPLEVEL")) {
1379         if (oplevel > current->level)
1380           oplevel = current->level;
1381       }
1382       break;
1383 
1384     case SET_TIME:
1385       *(int *)hit->value = ToSeconds(value);
1386       break;
1387 
1388     case SET_SPEC:
1389       switch (hit->ID) {
1390 
1391         case SPECIAL_CMODE:
1392           CmdCmode(from, value);
1393           break;
1394 
1395         case SPECIAL_CKEY:
1396           CmdCkey(from, value);
1397           break;
1398 
1399         case SPECIAL_LANGUAGE:
1400           defaultlang = LanguageNum(value);
1401           StrCopy(language, LanguageLocal(defaultlang));
1402           break;
1403 
1404       }
1405       break;
1406 
1407   }
1408 
1409   SaveSettings();
1410   ShowSetGroup(from, grp);
1411   return FALSE;
1412 }
1413 
1414 /* --- InitSets --------------------------------------------------- */
1415 
1416 void InitSets(void)
1417 {
1418   int i;
1419   struct SetItem *item;
1420 
1421   snapshot;
1422   for (i=0; i < sizeof(sets)/sizeof(sets[0]); i++) {
1423     sets[i].level = levels[ sets[i].level ];
1424     for (item = sets[i].group; item->name; item++)
1425       item->level = levels[ item->level ];
1426   }
1427 }
1428 
1429 /* --- ChgCmdLev -------------------------------------------------- */
1430 
1431 void ChgCmdLev(char *def, char *value)
1432 {
1433   char cmd[MINIBUFFER];
1434   int level;
1435   struct Command *command;
1436 
1437   if (2 == StrScan(value, "%"MINIBUFFERTXT"s %d", cmd, &level)) {
1438     if ((level >= LEVELANYBODY) && (level <= LEVELOWNER)) {
1439       command = FindCommand(cmd);
1440       if (command) {
1441         command->level = level;
1442         command->flags |= CMD_CHANGED;
1443       }
1444     }
1445   }
1446 }
1447 
1448 #if 0
1449 /******************************************************************************
1450 New stuff 970618 to better deal with the users' own info, flags and status.
1451 
1452 This is 4.7+ stuff
1453 
1454  __  __       _        __
1455 |  \/  |_   _(_)_ __  / _| ___
1456 | |\/| | | | | | '_ \| |_ / _ \
1457 | |  | | |_| | | | | |  _| (_) |
1458 |_|  |_|\__, |_|_| |_|_|  \___/
1459         |___/
1460 *****************************************************************************/
1461 
1462 
1463 #define MYINFO_USER  0x80
1464 #define MYINFO_GUEST 0x40
1465 
1466 typedef enum {
1467   MYINFO_LANGUAGE=1,
1468   MYINFO_REPORT,
1469   MYINFO_SPY,
1470   MYINFO_DEBUG,
1471 } myspecial;
1472 
1473 /* debug-defines to make it compile */
1474 #define msg_myinfo_comment NULL
1475 #define msg_myinfo_email NULL
1476 #define msg_myinfo_language NULL
1477 #define msg_myinfo_report NULL
1478 #define msg_myinfo_spy NULL
1479 #define msg_myinfo_debug NULL
1480 #define msg_myinfo_info NULL
1481 #define msg_myinfo_flags NULL
1482 
1483 static struct SetItem myinfoinfo[]={
1484   {"EMAIL",
1485    0,
1486    SET_STRING,
1487    (void *)offsetof(itemuser, realname),
1488    MYINFO_USER,
1489    msg_myinfo_email},
1490   /* replaces SETEMAIL */
1491 
1492   {"COMMENT", 0, SET_STRING, (void *)offsetof(itemuser, comment), MYINFO_USER,
1493    msg_myinfo_comment},
1494   /* replaces COMMENT */
1495 
1496   {"LANGUAGE", 0, SET_STRING,(void *)offsetof(itemuser, language) ,
1497    MYINFO_USER | MYINFO_LANGUAGE, msg_myinfo_language},
1498   /* replaces LANGUAGE */
1499 };
1500 static struct SetItem myinfoflags[]={
1501   {"REPORT", LEVEL_CHANOP, SET_ONOFF, NULL, MYINFO_REPORT, msg_myinfo_report},
1502   /* replaces REPORTADD and REPORTDEL */
1503 
1504   {"SPY", LEVEL_BOT, SET_ONOFF, NULL, MYINFO_SPY, msg_myinfo_spy},
1505   /* replaces SPYADD and SPYDEL */
1506 
1507   {"DEBUG", LEVEL_BOT, SET_ONOFF, NULL, MYINFO_DEBUG, msg_myinfo_debug},
1508   /* replaces DEBUGADD and DEBUGDEL */
1509 };
1510 
1511 static struct SetGroup myinfo[]={
1512   {"INFO",       LEVEL_RECOG, myinfoinfo,  msg_myinfo_info},
1513   {"FLAGS",      0, myinfoflags, msg_myinfo_flags},
1514 };
1515 
1516 #endif
1517