]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - include/configreader.h
61010d321e7d3265468e77eabb0a589772bc47e9
[user/henk/code/inspircd.git] / include / configreader.h
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2009 InspIRCd Development Team
6  * See: http://wiki.inspircd.org/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 #ifndef INSPIRCD_CONFIGREADER
15 #define INSPIRCD_CONFIGREADER
16
17 #include <sstream>
18 #include <string>
19 #include <vector>
20 #include <map>
21 #include "inspircd.h"
22 #include "modules.h"
23 #include "socketengine.h"
24 #include "socket.h"
25
26 /* Required forward definitions */
27 class ServerConfig;
28 class ServerLimits;
29 class InspIRCd;
30 class BufferedSocket;
31
32 /** A set of oper types
33  */
34 typedef std::map<irc::string,std::string> opertype_t;
35
36 /** Holds an oper class.
37  */
38 struct operclass_data : public classbase
39 {
40         /** Command list for the class
41          */
42         std::string commandlist;
43
44         /** Channel mode list for the class
45          */
46         std::string cmodelist;
47
48         /** User mode list for the class
49          */
50         std::string umodelist;
51
52         /** Priviledges given by this class
53          */
54         std::string privs;
55 };
56
57 /** A Set of oper classes
58  */
59 typedef std::map<irc::string, operclass_data> operclass_t;
60
61 /** Defines the server's length limits on various length-limited
62  * items such as topics, nicknames, channel names etc.
63  */
64 class ServerLimits
65 {
66  public:
67         /** Maximum nickname length */
68         size_t NickMax;
69         /** Maximum channel length */
70         size_t ChanMax;
71         /** Maximum number of modes per line */
72         size_t MaxModes;
73         /** Maximum length of ident, not including ~ etc */
74         size_t IdentMax;
75         /** Maximum length of a quit message */
76         size_t MaxQuit;
77         /** Maximum topic length */
78         size_t MaxTopic;
79         /** Maximum kick message length */
80         size_t MaxKick;
81         /** Maximum GECOS (real name) length */
82         size_t MaxGecos;
83         /** Maximum away message length */
84         size_t MaxAway;
85
86         /** Creating the class initialises it to the defaults
87          * as in 1.1's ./configure script. Reading other values
88          * from the config will change these values.
89          */
90         ServerLimits() : NickMax(31), ChanMax(64), MaxModes(20), IdentMax(12), MaxQuit(255), MaxTopic(307), MaxKick(255), MaxGecos(128), MaxAway(200)
91         {
92         }
93
94         /** Finalises the settings by adding one. This allows for them to be used as-is
95          * without a 'value+1' when using the std::string assignment methods etc.
96          */
97         void Finalise()
98         {
99                 NickMax++;
100                 ChanMax++;
101                 IdentMax++;
102                 MaxQuit++;
103                 MaxTopic++;
104                 MaxKick++;
105                 MaxGecos++;
106                 MaxAway++;
107         }
108 };
109
110 /** This class holds the bulk of the runtime configuration for the ircd.
111  * It allows for reading new config values, accessing configuration files,
112  * and storage of the configuration data needed to run the ircd, such as
113  * the servername, connect classes, /ADMIN data, MOTDs and filenames etc.
114  */
115 class CoreExport ServerConfig : public classbase
116 {
117   private:
118         /** This variable holds the names of all
119          * files included from the main one. This
120          * is used to make sure that no files are
121          * recursively included.
122          */
123         std::vector<std::string> include_stack;
124
125         /* classes removed by this rehash */
126         std::vector<ConnectClass*> removed_classes;
127
128         /** This private method processes one line of
129          * configutation, appending errors to errorstream
130          * and setting error if an error has occured.
131          */
132         bool ParseLine(const std::string &filename, std::string &line, long &linenumber, bool allowexeinc);
133
134         /** Check that there is only one of each configuration item
135          */
136         bool CheckOnce(const char* tag);
137
138         void CrossCheckOperClassType();
139         void CrossCheckConnectBlocks(ServerConfig* current);
140
141  public:
142         /** Process an include executable directive
143          */
144         bool DoPipe(const std::string &file);
145
146         /** Process an include file directive
147          */
148         bool DoInclude(const std::string &file, bool allowexeinc);
149
150         /** Error stream, contains error output from any failed configuration parsing.
151          */
152         std::stringstream errstr;
153
154         /** True if this configuration is valid enough to run with */
155         bool valid;
156
157         /** Set of included files. Do we use this any more?
158          */
159         std::map<std::string, std::istream*> IncludedFiles;
160
161         /** Used to indicate who we announce invites to on a channel */
162         enum InviteAnnounceState { INVITE_ANNOUNCE_NONE, INVITE_ANNOUNCE_ALL, INVITE_ANNOUNCE_OPS, INVITE_ANNOUNCE_DYNAMIC };
163
164         /** Not used any more as it is named, can probably be removed or renamed.
165          */
166         int DoDownloads();
167
168         /** This holds all the information in the config file,
169          * it's indexed by tag name to a vector of key/values.
170          */
171         ConfigDataHash config_data;
172
173         /** Length limits, see definition of ServerLimits class
174          */
175         ServerLimits Limits;
176
177         /** Clones CIDR range for ipv4 (0-32)
178          * Defaults to 32 (checks clones on all IPs seperately)
179          */
180         int c_ipv4_range;
181
182         /** Clones CIDR range for ipv6 (0-128)
183          * Defaults to 128 (checks on all IPs seperately)
184          */
185         int c_ipv6_range;
186
187         /** Max number of WhoWas entries per user.
188          */
189         int WhoWasGroupSize;
190
191         /** Max number of cumulative user-entries in WhoWas.
192          *  When max reached and added to, push out oldest entry FIFO style.
193          */
194         int WhoWasMaxGroups;
195
196         /** Max seconds a user is kept in WhoWas before being pruned.
197          */
198         int WhoWasMaxKeep;
199
200         /** Both for set(g|u)id.
201          */
202         std::string SetUser;
203         std::string SetGroup;
204
205         /** Holds the server name of the local server
206          * as defined by the administrator.
207          */
208         std::string ServerName;
209
210         /** Notice to give to users when they are Xlined
211          */
212         std::string MoronBanner;
213
214         /* Holds the network name the local server
215          * belongs to. This is an arbitary field defined
216          * by the administrator.
217          */
218         std::string Network;
219
220         /** Holds the description of the local server
221          * as defined by the administrator.
222          */
223         std::string ServerDesc;
224
225         /** Holds the admin's name, for output in
226          * the /ADMIN command.
227          */
228         std::string AdminName;
229
230         /** Holds the email address of the admin,
231          * for output in the /ADMIN command.
232          */
233         std::string AdminEmail;
234
235         /** Holds the admin's nickname, for output
236          * in the /ADMIN command
237          */
238         std::string AdminNick;
239
240         /** The admin-configured /DIE password
241          */
242         std::string diepass;
243
244         /** The admin-configured /RESTART password
245          */
246         std::string restartpass;
247
248         /** The hash method for *BOTH* the die and restart passwords.
249          */
250         std::string powerhash;
251
252         /** The pathname and filename of the message of the
253          * day file, as defined by the administrator.
254          */
255         std::string motd;
256
257         /** The pathname and filename of the rules file,
258          * as defined by the administrator.
259          */
260         std::string rules;
261
262         /** The quit prefix in use, or an empty string
263          */
264         std::string PrefixQuit;
265
266         /** The quit suffix in use, or an empty string
267          */
268         std::string SuffixQuit;
269
270         /** The fixed quit message in use, or an empty string
271          */
272         std::string FixedQuit;
273
274         /** The part prefix in use, or an empty string
275          */
276         std::string PrefixPart;
277
278         /** The part suffix in use, or an empty string
279          */
280         std::string SuffixPart;
281
282         /** The fixed part message in use, or an empty string
283          */
284         std::string FixedPart;
285
286         /** The last string found within a <die> tag, or
287          * an empty string.
288          */
289         std::string DieValue;
290
291         /** The DNS server to use for DNS queries
292          */
293         std::string DNSServer;
294
295         /** Pretend disabled commands don't exist.
296          */
297         bool DisabledDontExist;
298
299         /** This variable contains a space-seperated list
300          * of commands which are disabled by the
301          * administrator of the server for non-opers.
302          */
303         std::string DisabledCommands;
304
305         /** This variable identifies which usermodes have been diabled.
306          */
307
308         char DisabledUModes[64];
309
310         /** This variable identifies which chanmodes have been disabled.
311          */
312         char DisabledCModes[64];
313
314         /** The full path to the modules directory.
315          * This is either set at compile time, or
316          * overridden in the configuration file via
317          * the <options> tag.
318          */
319         std::string ModPath;
320
321         /** The file handle of the logfile. If this
322          * value is NULL, the log file is not open,
323          * probably due to a permissions error on
324          * startup (this should not happen in normal
325          * operation!).
326          */
327         FILE *log_file;
328
329         /** If this value is true, the owner of the
330          * server specified -nofork on the command
331          * line, causing the daemon to stay in the
332          * foreground.
333          */
334         bool nofork;
335
336         /** If this value if true then all log
337          * messages will be output, regardless of
338          * the level given in the config file.
339          * This is set with the -debug commandline
340          * option.
341          */
342         bool forcedebug;
343
344         /** If this is true then log output will be
345          * written to the logfile. This is the default.
346          * If you put -nolog on the commandline then
347          * the logfile will not be written.
348          * This is meant to be used in conjunction with
349          * -debug for debugging without filling up the
350          * hard disk.
351          */
352         bool writelog;
353
354         /** If set to true, then all opers on this server are
355          * shown with a generic 'is an IRC operator' line rather
356          * than the oper type. Oper types are still used internally.
357          */
358         bool GenericOper;
359
360         /** If this value is true, banned users (+b, not extbans) will not be able to change nick
361          * if banned on any channel, nor to message them.
362          */
363         bool RestrictBannedUsers;
364
365         /** If this value is true, halfops have been
366          * enabled in the configuration file.
367          */
368         bool AllowHalfop;
369
370         /** If this is set to true, then mode lists (e.g
371          * MODE #chan b) are hidden from unprivileged
372          * users.
373          */
374         bool HideModeLists[256];
375
376         /** The number of seconds the DNS subsystem
377          * will wait before timing out any request.
378          */
379         int dns_timeout;
380
381         /** The size of the read() buffer in the user
382          * handling code, used to read data into a user's
383          * recvQ.
384          */
385         int NetBufferSize;
386
387         /** The value to be used for listen() backlogs
388          * as default.
389          */
390         int MaxConn;
391
392         /** The soft limit value assigned to the irc server.
393          * The IRC server will not allow more than this
394          * number of local users.
395          */
396         unsigned int SoftLimit;
397
398         /** Maximum number of targets for a multi target command
399          * such as PRIVMSG or KICK
400          */
401         unsigned int MaxTargets;
402
403         /** The maximum number of /WHO results allowed
404          * in any single /WHO command.
405          */
406         int MaxWhoResults;
407
408         /** True if the DEBUG loglevel is selected.
409          */
410         int debugging;
411
412         /** How many seconds to wait before exiting
413          * the program when /DIE is correctly issued.
414          */
415         int DieDelay;
416
417         /** True if we're going to hide netsplits as *.net *.split for non-opers
418          */
419         bool HideSplits;
420
421         /** True if we're going to hide ban reasons for non-opers (e.g. G-Lines,
422          * K-Lines, Z-Lines)
423          */
424         bool HideBans;
425
426         /** Announce invites to the channel with a server notice
427          */
428         InviteAnnounceState AnnounceInvites;
429
430         /** If this is enabled then operators will
431          * see invisible (+i) channels in /whois.
432          */
433         bool OperSpyWhois;
434
435         /** Set to a non-empty string to obfuscate the server name of users in WHOIS
436          */
437         std::string HideWhoisServer;
438
439         /** Set to a non empty string to obfuscate nicknames prepended to a KILL.
440          */
441         std::string HideKillsServer;
442
443         /** The MOTD file, cached in a file_cache type.
444          */
445         file_cache MOTD;
446
447         /** The RULES file, cached in a file_cache type.
448          */
449         file_cache RULES;
450
451         /** The full pathname and filename of the PID
452          * file as defined in the configuration.
453          */
454         std::string PID;
455
456         /** The connect classes in use by the IRC server.
457          */
458         ClassVector Classes;
459
460         /** The 005 tokens of this server (ISUPPORT)
461          * populated/repopulated upon loading or unloading
462          * modules.
463          */
464         std::string data005;
465
466         /** isupport strings
467          */
468         std::vector<std::string> isupport;
469
470         /** STATS characters in this list are available
471          * only to operators.
472          */
473         std::string UserStats;
474
475         /** The path and filename of the ircd.log file
476          */
477         std::string logpath;
478
479         /** Default channel modes
480          */
481         std::string DefaultModes;
482
483         /** Custom version string, which if defined can replace the system info in VERSION.
484          */
485         std::string CustomVersion;
486
487         /** List of u-lined servers
488          */
489         std::map<irc::string, bool> ulines;
490
491         /** Max banlist sizes for channels (the std::string is a glob)
492          */
493         std::map<std::string, int> maxbans;
494
495         /** Directory where the inspircd binary resides
496          */
497         std::string MyDir;
498
499         /** If set to true, no user DNS lookups are to be performed
500          */
501         bool NoUserDns;
502
503         /** If set to true, provide syntax hints for unknown commands
504          */
505         bool SyntaxHints;
506
507         /** If set to true, users appear to quit then rejoin when their hosts change.
508          * This keeps clients synchronized properly.
509          */
510         bool CycleHosts;
511
512         /** If set to true, prefixed channel NOTICEs and PRIVMSGs will have the prefix
513          *  added to the outgoing text for undernet style msg prefixing.
514          */
515         bool UndernetMsgPrefix;
516
517         /** If set to true, the full nick!user@host will be shown in the TOPIC command
518          * for who set the topic last. If false, only the nick is shown.
519          */
520         bool FullHostInTopic;
521
522         /** All oper type definitions from the config file
523          */
524         opertype_t opertypes;
525
526         /** All oper class definitions from the config file
527          */
528         operclass_t operclass;
529
530         /** Saved argv from startup
531          */
532         char** argv;
533
534         /** Saved argc from startup
535          */
536         int argc;
537
538         /** Max channels per user
539          */
540         unsigned int MaxChans;
541
542         /** Oper max channels per user
543          */
544         unsigned int OperMaxChans;
545
546         /** TS6-like server ID.
547          * NOTE: 000...999 are usable for InspIRCd servers. This
548          * makes code simpler. 0AA, 1BB etc with letters are reserved
549          * for services use.
550          */
551         std::string sid;
552
553         /** True if we have been told to run the testsuite from the commandline,
554          * rather than entering the mainloop.
555          */
556         bool TestSuite;
557
558         /** Construct a new ServerConfig
559          */
560         ServerConfig();
561
562         /** Get server ID as string with required leading zeroes
563          */
564         std::string GetSID();
565
566         /** Update the 005 vector
567          */
568         void Update005();
569
570         /** Send the 005 numerics (ISUPPORT) to a user
571          */
572         void Send005(User* user);
573
574         /** Read the entire configuration into memory
575          * and initialize this class. All other methods
576          * should be used only by the core.
577          */
578         void Read();
579
580         /** Apply configuration changes from the old configuration.
581          */
582         void Apply(ServerConfig* old, const std::string &useruid);
583         void ApplyModules(User* user);
584
585         /** Read a file into a file_cache object
586          */
587         bool ReadFile(file_cache &F, const char* fname);
588
589         /* Returns true if the given string starts with a windows drive letter
590          */
591         bool StartsWithWindowsDriveLetter(const std::string &path);
592
593         /** Load 'filename' into 'target', with the new config parser everything is parsed into
594          * tag/key/value at load-time rather than at read-value time.
595          */
596         bool LoadConf(FILE* &conf, const char* filename, bool allowexeinc);
597
598         /** Load 'filename' into 'target', with the new config parser everything is parsed into
599          * tag/key/value at load-time rather than at read-value time.
600          */
601         bool LoadConf(FILE* &conf, const std::string &filename, bool allowexeinc);
602
603         /** Writes 'length' chars into 'result' as a string
604          */
605         bool ConfValue(const char* tag, const char* var, int index, char* result, int length, bool allow_linefeeds = false);
606
607         /** Writes 'length' chars into 'result' as a string
608          */
609         bool ConfValue(const char* tag, const char* var, const char* default_value, int index, char* result, int length, bool allow_linefeeds = false);
610
611         /** Writes 'length' chars into 'result' as a string
612          */
613         bool ConfValue(const std::string &tag, const std::string &var, int index, std::string &result, bool allow_linefeeds = false);
614
615         /** Writes 'length' chars into 'result' as a string
616          */
617         bool ConfValue(const std::string &tag, const std::string &var, const std::string &default_value, int index, std::string &result, bool allow_linefeeds = false);
618
619         /** Tries to convert the value to an integer and write it to 'result'
620          */
621         bool ConfValueInteger(const char* tag, const char* var, int index, int &result);
622
623         /** Tries to convert the value to an integer and write it to 'result'
624          */
625         bool ConfValueInteger(const char* tag, const char* var, const char* default_value, int index, int &result);
626
627         /** Tries to convert the value to an integer and write it to 'result'
628          */
629         bool ConfValueInteger(const std::string &tag, const std::string &var, int index, int &result);
630
631         /** Tries to convert the value to an integer and write it to 'result'
632          */
633         bool ConfValueInteger(const std::string &tag, const std::string &var, const std::string &default_value, int index, int &result);
634
635         /** Returns true if the value exists and has a true value, false otherwise
636          */
637         bool ConfValueBool(const char* tag, const char* var, int index);
638
639         /** Returns true if the value exists and has a true value, false otherwise
640          */
641         bool ConfValueBool(const char* tag, const char* var, const char* default_value, int index);
642
643         /** Returns true if the value exists and has a true value, false otherwise
644          */
645         bool ConfValueBool(const std::string &tag, const std::string &var, int index);
646
647         /** Returns true if the value exists and has a true value, false otherwise
648          */
649         bool ConfValueBool(const std::string &tag, const std::string &var, const std::string &default_value, int index);
650
651         /** Returns the number of occurences of tag in the config file
652          */
653         int ConfValueEnum(const char* tag);
654         /** Returns the number of occurences of tag in the config file
655          */
656         int ConfValueEnum(const std::string &tag);
657
658         /** Returns the numbers of vars inside the index'th 'tag in the config file
659          */
660         int ConfVarEnum(const char* tag, int index);
661         /** Returns the numbers of vars inside the index'th 'tag in the config file
662          */
663         int ConfVarEnum(const std::string &tag, int index);
664
665         bool ApplyDisabledCommands(const std::string& data);
666
667         /** Clean a filename, stripping the directories (and drives) from string.
668          * @param name Directory to tidy
669          * @return The cleaned filename
670          */
671         static const char* CleanFilename(const char* name);
672
673         /** Check if a file exists.
674          * @param file The full path to a file
675          * @return True if the file exists and is readable.
676          */
677         static bool FileExists(const char* file);
678
679         /** If this value is true, invites will bypass more than just +i
680          */
681         bool InvBypassModes;
682
683 };
684
685
686 /** Types of data in the core config
687  */
688 enum ConfigDataType
689 {
690         DT_NOTHING       = 0,           /* No data */
691         DT_INTEGER       = 1,           /* Integer */
692         DT_CHARPTR       = 2,           /* Char pointer */
693         DT_BOOLEAN       = 3,           /* Boolean */
694         DT_HOSTNAME      = 4,           /* Hostname syntax */
695         DT_NOSPACES      = 5,           /* No spaces */
696         DT_IPADDRESS     = 6,           /* IP address (v4, v6) */
697         DT_CHANNEL       = 7,           /* Channel name */
698         DT_LIMIT     = 8,       /* size_t */
699         DT_ALLOW_WILD    = 64,          /* Allow wildcards/CIDR in DT_IPADDRESS */
700         DT_ALLOW_NEWLINE = 128          /* New line characters allowed in DT_CHARPTR */
701 };
702
703 /** The maximum number of values in a core configuration tag. Can be increased if needed.
704  */
705 #define MAX_VALUES_PER_TAG 18
706
707 /** Holds a config value, either string, integer or boolean.
708  * Callback functions receive one or more of these, either on
709  * their own as a reference, or in a reference to a deque of them.
710  * The callback function can then alter the values of the ValueItem
711  * classes to validate the settings.
712  */
713 class ValueItem
714 {
715         /** Actual data */
716         std::string v;
717  public:
718         /** Initialize with an int */
719         ValueItem(int value);
720         /** Initialize with a bool */
721         ValueItem(bool value);
722         /** Initialize with a string */
723         ValueItem(const char* value) : v(value) { }
724         /** Change value to a string */
725         void Set(const std::string &val);
726         /** Change value to an int */
727         void Set(int value);
728         /** Get value as an int */
729         int GetInteger();
730         /** Get value as a string */
731         const char* GetString() const;
732         /** Get value as a string */
733         inline const std::string& GetValue() const { return v; }
734         /** Get value as a bool */
735         bool GetBool();
736 };
737
738 /** The base class of the container 'ValueContainer'
739  * used internally by the core to hold core values.
740  */
741 class ValueContainerBase
742 {
743  public:
744         /** Constructor */
745         ValueContainerBase() { }
746         /** Destructor */
747         virtual ~ValueContainerBase() { }
748 };
749
750 /** ValueContainer is used to contain pointers to different
751  * core values such as the server name, maximum number of
752  * clients etc.
753  * It is specialized to hold a data type, then pointed at
754  * a value in the ServerConfig class. When the value has been
755  * read and validated, the Set method is called to write the
756  * value safely in a type-safe manner.
757  */
758 template<typename T> class ValueContainer : public ValueContainerBase
759 {
760         T ServerConfig::* const vptr;
761  public:
762         /** Initialize with a value of type T */
763         ValueContainer(T ServerConfig::* const offset) : vptr(offset)
764         {
765         }
766
767         /** Change value to type T of size s */
768         void Set(ServerConfig* conf, const T& value)
769         {
770                 conf->*vptr = value;
771         }
772
773         void Set(ServerConfig* conf, const ValueItem& item);
774 };
775
776 class ValueContainerLimit : public ValueContainerBase
777 {
778         size_t ServerLimits::* const vptr;
779  public:
780         /** Initialize with a value of type T */
781         ValueContainerLimit(size_t ServerLimits::* const offset) : vptr(offset)
782         {
783         }
784
785         /** Change value to type T of size s */
786         void Set(ServerConfig* conf, const size_t& value)
787         {
788                 conf->Limits.*vptr = value;
789         }
790 };
791
792 /** A specialization of ValueContainer to hold a pointer to a bool
793  */
794 typedef ValueContainer<bool> ValueContainerBool;
795
796 /** A specialization of ValueContainer to hold a pointer to
797  * an unsigned int
798  */
799 typedef ValueContainer<unsigned int> ValueContainerUInt;
800
801 /** A specialization of ValueContainer to hold a pointer to
802  * a char array.
803  */
804 typedef ValueContainer<std::string> ValueContainerString;
805
806 /** A specialization of ValueContainer to hold a pointer to
807  * an int
808  */
809 typedef ValueContainer<int> ValueContainerInt;
810
811 /** A set of ValueItems used by multi-value validator functions
812  */
813 typedef std::deque<ValueItem> ValueList;
814
815 /** A callback for validating a single value
816  */
817 typedef bool (*Validator)(ServerConfig* conf, const char*, const char*, ValueItem&);
818 /** A callback for validating multiple value entries
819  */
820 typedef bool (*MultiValidator)(ServerConfig* conf, const char*, const char**, ValueList&, int*);
821 /** A callback indicating the end of a group of entries
822  */
823 typedef bool (*MultiNotify)(ServerConfig* conf, const char*);
824
825 /** Holds a core configuration item and its callbacks
826  */
827 struct InitialConfig
828 {
829         /** Tag name */
830         const char* tag;
831         /** Value name */
832         const char* value;
833         /** Default, if not defined */
834         const char* default_value;
835         /** Value containers */
836         ValueContainerBase* val;
837         /** Data types */
838         int datatype;
839         /** Validation function */
840         Validator validation_function;
841         ~InitialConfig();
842 };
843
844 /** Represents a deprecated configuration tag.
845  */
846 struct Deprecated
847 {
848         /** Tag name
849          */
850         const char* tag;
851         /** Tag value
852          */
853         const char* value;
854         /** Reason for deprecation
855          */
856         const char* reason;
857 };
858
859 /** Holds a core configuration item and its callbacks
860  * where there may be more than one item
861  */
862 struct MultiConfig
863 {
864         /** Tag name */
865         const char*     tag;
866         /** One or more items within tag */
867         const char*     items[MAX_VALUES_PER_TAG];
868         /** One or more defaults for items within tags */
869         const char* items_default[MAX_VALUES_PER_TAG];
870         /** One or more data types */
871         int             datatype[MAX_VALUES_PER_TAG];
872         /** Validation function */
873         MultiValidator  validation_function;
874 };
875
876 #endif