]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/inspircd_io.cpp
More config tidyups
[user/henk/code/inspircd.git] / src / inspircd_io.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  Inspire is copyright (C) 2002-2004 ChatSpike-Dev.
6  *                       E-mail:
7  *                <brain@chatspike.net>
8  *                <Craig@chatspike.net>
9  *     
10  * Written by Craig Edwards, Craig McLure, and others.
11  * This program is free but copyrighted software; see
12  *            the file COPYING for details.
13  *
14  * ---------------------------------------------------
15  */
16
17 using namespace std;
18
19 #include "inspircd_config.h"
20 #include <sys/time.h>
21 #include <sys/resource.h>
22 #include <sys/types.h>
23 #include <string>
24 #include <unistd.h>
25 #include <sstream>
26 #include <iostream>
27 #include <fstream>
28 #include "inspircd.h"
29 #include "inspircd_io.h"
30 #include "inspircd_util.h"
31 #include "inspstring.h"
32 #include "helperfuncs.h"
33 #include "xline.h"
34
35 extern ServerConfig *Config;
36 extern int boundPortCount;
37 extern int openSockfd[MAXSOCKS];
38 extern time_t TIME;
39
40
41 ServerConfig::ServerConfig()
42 {
43         this->ClearStack();
44         *ServerName = *Network = *ServerDesc = *AdminName = '\0';
45         *AdminEmail = *AdminNick = *diepass = *restartpass = '\0';
46         *motd = *rules = *PrefixQuit = *DieValue = *DNSServer = '\0';
47         *ModPath = *MyExecutable = *DisabledCommands = *PID = '\0';
48         log_file = NULL;
49         nofork = false;
50         unlimitcore = false;
51         AllowHalfop = true;
52         dns_timeout = 5;
53         NetBufferSize = 10240;
54         SoftLimit = MAXCLIENTS;
55         MaxConn = SOMAXCONN;
56         MaxWhoResults = 100;
57         debugging = 0;
58         LogLevel = DEFAULT;
59         DieDelay = 5;
60 }
61
62 void ServerConfig::ClearStack()
63 {
64         include_stack.clear();
65 }
66
67
68 void ServerConfig::Read(bool bail, userrec* user)
69 {
70         char dbg[MAXBUF],pauseval[MAXBUF],Value[MAXBUF],timeout[MAXBUF],NB[MAXBUF],flood[MAXBUF],MW[MAXBUF],MCON[MAXBUF];
71         char AH[MAXBUF],AP[MAXBUF],AF[MAXBUF],DNT[MAXBUF],pfreq[MAXBUF],thold[MAXBUF],sqmax[MAXBUF],rqmax[MAXBUF],SLIMT[MAXBUF];
72         ConnectClass c;
73         std::stringstream errstr;
74         include_stack.clear();
75
76         if (!LoadConf(CONFIG_FILE,&Config->config_f,&errstr))
77         {
78                 errstr.seekg(0);
79                 log(DEFAULT,"There were errors in your configuration:\n%s",errstr.str().c_str());
80                 if (bail)
81                 {
82                         printf("There were errors in your configuration:\n%s",errstr.str().c_str());
83                         Exit(0);
84                 }
85                 else
86                 {
87                         char dataline[1024];
88                         if (user)
89                         {
90                                 WriteServ(user->fd,"NOTICE %s :There were errors in the configuration file:",user->nick);
91                                 while (!errstr.eof())
92                                 {
93                                         errstr.getline(dataline,1024);
94                                         WriteServ(user->fd,"NOTICE %s :%s",user->nick,dataline);
95                                 }
96                         }
97                         else
98                         {
99                                 WriteOpers("There were errors in the configuration file:");
100                                 while (!errstr.eof())
101                                 {
102                                         errstr.getline(dataline,1024);
103                                         WriteOpers(dataline);
104                                 }
105                         }
106                         return;
107                 }
108         }
109
110         ConfValue("server","name",0,Config->ServerName,&Config->config_f);
111         ConfValue("server","description",0,Config->ServerDesc,&Config->config_f);
112         ConfValue("server","network",0,Config->Network,&Config->config_f);
113         ConfValue("admin","name",0,Config->AdminName,&Config->config_f);
114         ConfValue("admin","email",0,Config->AdminEmail,&Config->config_f);
115         ConfValue("admin","nick",0,Config->AdminNick,&Config->config_f);
116         ConfValue("files","motd",0,Config->motd,&Config->config_f);
117         ConfValue("files","rules",0,Config->rules,&Config->config_f);
118         ConfValue("power","diepass",0,Config->diepass,&Config->config_f);
119         ConfValue("power","pause",0,pauseval,&Config->config_f);
120         ConfValue("power","restartpass",0,Config->restartpass,&Config->config_f);
121         ConfValue("options","prefixquit",0,Config->PrefixQuit,&Config->config_f);
122         ConfValue("die","value",0,Config->DieValue,&Config->config_f);
123         ConfValue("options","loglevel",0,dbg,&Config->config_f);
124         ConfValue("options","netbuffersize",0,NB,&Config->config_f);
125         ConfValue("options","maxwho",0,MW,&Config->config_f);
126         ConfValue("options","allowhalfop",0,AH,&Config->config_f);
127         ConfValue("options","allowprotect",0,AP,&Config->config_f);
128         ConfValue("options","allowfounder",0,AF,&Config->config_f);
129         ConfValue("dns","server",0,Config->DNSServer,&Config->config_f);
130         ConfValue("dns","timeout",0,DNT,&Config->config_f);
131         ConfValue("options","moduledir",0,Config->ModPath,&Config->config_f);
132         ConfValue("disabled","commands",0,Config->DisabledCommands,&Config->config_f);
133         ConfValue("options","somaxconn",0,MCON,&Config->config_f);
134         ConfValue("options","softlimit",0,SLIMT,&Config->config_f);
135
136         Config->SoftLimit = atoi(SLIMT);
137         if ((Config->SoftLimit < 1) || (Config->SoftLimit > MAXCLIENTS))
138         {
139                 log(DEFAULT,"WARNING: <options:softlimit> value is greater than %d or less than 0, set to %d.",MAXCLIENTS,MAXCLIENTS);
140                 Config->SoftLimit = MAXCLIENTS;
141         }
142         Config->MaxConn = atoi(MCON);
143         if (Config->MaxConn > SOMAXCONN)
144                 log(DEFAULT,"WARNING: <options:somaxconn> value may be higher than the system-defined SOMAXCONN value!");
145         Config->NetBufferSize = atoi(NB);
146         Config->MaxWhoResults = atoi(MW);
147         Config->dns_timeout = atoi(DNT);
148         if (!Config->dns_timeout)
149                 Config->dns_timeout = 5;
150         if (!Config->MaxConn)
151                 Config->MaxConn = SOMAXCONN;
152         if (!*Config->DNSServer)
153                 strlcpy(Config->DNSServer,"127.0.0.1",MAXBUF);
154         if (!*Config->ModPath)
155                 strlcpy(Config->ModPath,MOD_PATH,MAXBUF);
156         Config->AllowHalfop = ((!strcasecmp(AH,"true")) || (!strcasecmp(AH,"1")) || (!strcasecmp(AH,"yes")));
157         if ((!Config->NetBufferSize) || (Config->NetBufferSize > 65535) || (Config->NetBufferSize < 1024))
158         {
159                 log(DEFAULT,"No NetBufferSize specified or size out of range, setting to default of 10240.");
160                 Config->NetBufferSize = 10240;
161         }
162         if ((!Config->MaxWhoResults) || (Config->MaxWhoResults > 65535) || (Config->MaxWhoResults < 1))
163         {
164                 log(DEFAULT,"No MaxWhoResults specified or size out of range, setting to default of 128.");
165                 Config->MaxWhoResults = 128;
166         }
167         Config->LogLevel = DEFAULT;
168         if (!strcmp(dbg,"debug"))
169         {
170                 Config->LogLevel = DEBUG;
171                 Config->debugging = 1;
172         }
173         if (!strcmp(dbg,"verbose"))
174                 Config->LogLevel = VERBOSE;
175         if (!strcmp(dbg,"default"))
176                 Config->LogLevel = DEFAULT;
177         if (!strcmp(dbg,"sparse"))
178                 Config->LogLevel = SPARSE;
179         if (!strcmp(dbg,"none"))
180                 Config->LogLevel = NONE;
181
182         readfile(Config->MOTD,Config->motd);
183         log(DEFAULT,"Reading message of the day...");
184         readfile(Config->RULES,Config->rules);
185         log(DEFAULT,"Reading connect classes...");
186         Classes.clear();
187         for (int i = 0; i < ConfValueEnum("connect",&Config->config_f); i++)
188         {
189                 strcpy(Value,"");
190                 ConfValue("connect","allow",i,Value,&Config->config_f);
191                 ConfValue("connect","timeout",i,timeout,&Config->config_f);
192                 ConfValue("connect","flood",i,flood,&Config->config_f);
193                 ConfValue("connect","pingfreq",i,pfreq,&Config->config_f);
194                 ConfValue("connect","threshold",i,thold,&Config->config_f);
195                 ConfValue("connect","sendq",i,sqmax,&Config->config_f);
196                 ConfValue("connect","recvq",i,rqmax,&Config->config_f);
197                 if (*Value)
198                 {
199                         strlcpy(c.host,Value,MAXBUF);
200                         c.type = CC_ALLOW;
201                         strlcpy(Value,"",MAXBUF);
202                         ConfValue("connect","password",i,Value,&Config->config_f);
203                         strlcpy(c.pass,Value,MAXBUF);
204                         c.registration_timeout = 90; // default is 2 minutes
205                         c.pingtime = 120;
206                         c.flood = atoi(flood);
207                         c.threshold = 5;
208                         c.sendqmax = 262144; // 256k
209                         c.recvqmax = 4096;   // 4k
210                         if (atoi(thold)>0)
211                         {
212                                 c.threshold = atoi(thold);
213                         }
214                         if (atoi(sqmax)>0)
215                         {
216                                 c.sendqmax = atoi(sqmax);
217                         }
218                         if (atoi(rqmax)>0)
219                         {
220                                 c.recvqmax = atoi(rqmax);
221                         }
222                         if (atoi(timeout)>0)
223                         {
224                                 c.registration_timeout = atoi(timeout);
225                         }
226                         if (atoi(pfreq)>0)
227                         {
228                                 c.pingtime = atoi(pfreq);
229                         }
230                         Classes.push_back(c);
231                 }
232                 else
233                 {
234                         ConfValue("connect","deny",i,Value,&Config->config_f);
235                         strlcpy(c.host,Value,MAXBUF);
236                         c.type = CC_DENY;
237                         Classes.push_back(c);
238                         log(DEBUG,"Read connect class type DENY, host=%s",c.host);
239                 }
240
241         }
242         log(DEFAULT,"Reading K lines,Q lines and Z lines from config...");
243         read_xline_defaults();
244         log(DEFAULT,"Applying K lines, Q lines and Z lines...");
245         apply_lines(APPLY_ALL);
246
247         ConfValue("pid","file",0,Config->PID,&Config->config_f);
248         // write once here, to try it out and make sure its ok
249         WritePID(Config->PID);
250
251         log(DEFAULT,"Done reading configuration file, InspIRCd is now starting.");
252         if (!bail)
253         {
254                 log(DEFAULT,"Adding and removing modules due to rehash...");
255
256                 std::vector<std::string> old_module_names, new_module_names, added_modules, removed_modules;
257
258                 // store the old module names
259                 for (std::vector<std::string>::iterator t = module_names.begin(); t != module_names.end(); t++)
260                 {
261                         old_module_names.push_back(*t);
262                 }
263
264                 // get the new module names
265                 for (int count2 = 0; count2 < ConfValueEnum("module",&Config->config_f); count2++)
266                 {
267                         ConfValue("module","name",count2,Value,&Config->config_f);
268                         new_module_names.push_back(Value);
269                 }
270
271                 // now create a list of new modules that are due to be loaded
272                 // and a seperate list of modules which are due to be unloaded
273                 for (std::vector<std::string>::iterator _new = new_module_names.begin(); _new != new_module_names.end(); _new++)
274                 {
275                         bool added = true;
276                         for (std::vector<std::string>::iterator old = old_module_names.begin(); old != old_module_names.end(); old++)
277                         {
278                                 if (*old == *_new)
279                                         added = false;
280                         }
281                         if (added)
282                                 added_modules.push_back(*_new);
283                 }
284                 for (std::vector<std::string>::iterator oldm = old_module_names.begin(); oldm != old_module_names.end(); oldm++)
285                 {
286                         bool removed = true;
287                         for (std::vector<std::string>::iterator newm = new_module_names.begin(); newm != new_module_names.end(); newm++)
288                         {
289                                 if (*newm == *oldm)
290                                         removed = false;
291                         }
292                         if (removed)
293                                 removed_modules.push_back(*oldm);
294                 }
295                 // now we have added_modules, a vector of modules to be loaded, and removed_modules, a vector of modules
296                 // to be removed.
297                 int rem = 0, add = 0;
298                 if (!removed_modules.empty())
299                 for (std::vector<std::string>::iterator removing = removed_modules.begin(); removing != removed_modules.end(); removing++)
300                 {
301                         if (UnloadModule(removing->c_str()))
302                         {
303                                 WriteOpers("*** REHASH UNLOADED MODULE: %s",removing->c_str());
304                                 WriteServ(user->fd,"973 %s %s :Module %s successfully unloaded.",user->nick, removing->c_str(), removing->c_str());
305                                 rem++;
306                         }
307                         else
308                         {
309                                 WriteServ(user->fd,"972 %s %s :Failed to unload module %s: %s",user->nick, removing->c_str(), removing->c_str(), ModuleError());
310                         }
311                 }
312                 if (!added_modules.empty())
313                 for (std::vector<std::string>::iterator adding = added_modules.begin(); adding != added_modules.end(); adding++)
314                 {
315                         if (LoadModule(adding->c_str()))
316                         {
317                                 WriteOpers("*** REHASH LOADED MODULE: %s",adding->c_str());
318                                 WriteServ(user->fd,"975 %s %s :Module %s successfully loaded.",user->nick, adding->c_str(), adding->c_str());
319                                 add++;
320                         }
321                         else
322                         {
323                                 WriteServ(user->fd,"974 %s %s :Failed to load module %s: %s",user->nick, adding->c_str(), adding->c_str(), ModuleError());
324                         }
325                 }
326                 log(DEFAULT,"Successfully unloaded %lu of %lu modules and loaded %lu of %lu modules.",(unsigned long)rem,(unsigned long)removed_modules.size(),
327                                                                                                         (unsigned long)add,(unsigned long)added_modules.size());
328         }
329 }
330
331
332 void WriteOpers(char* text, ...);
333
334 void Exit (int status)
335 {
336         if (Config->log_file)
337                 fclose(Config->log_file);
338         send_error("Server shutdown.");
339         exit (status);
340 }
341
342 void Killed(int status)
343 {
344         if (Config->log_file)
345                 fclose(Config->log_file);
346         send_error("Server terminated.");
347         exit(status);
348 }
349
350 void Rehash(int status)
351 {
352         WriteOpers("Rehashing config file %s due to SIGHUP",CONFIG_FILE);
353         Config->Read(false,NULL);
354 }
355
356
357
358 void Start (void)
359 {
360         printf("\033[1;32mInspire Internet Relay Chat Server, compiled %s at %s\n",__DATE__,__TIME__);
361         printf("(C) ChatSpike Development team.\033[0m\n\n");
362         printf("Developers:\033[1;32m     Brain, FrostyCoolSlug\033[0m\n");
363         printf("Documentation:\033[1;32m  FrostyCoolSlug, w00t\033[0m\n");
364         printf("Testers:\033[1;32m        typobox43, piggles, Lord_Zathras, CC\033[0m\n");
365         printf("Name concept:\033[1;32m   Lord_Zathras\033[0m\n\n");
366 }
367
368 void WritePID(std::string filename)
369 {
370         ofstream outfile(filename.c_str());
371         if (outfile.is_open())
372         {
373                 outfile << getpid();
374                 outfile.close();
375         }
376         else
377         {
378                 printf("Failed to write PID-file '%s', exiting.\n",filename.c_str());
379                 log(DEFAULT,"Failed to write PID-file '%s', exiting.",filename.c_str());
380                 Exit(0);
381         }
382 }
383
384
385 int DaemonSeed (void)
386 {
387         int childpid;
388         signal (SIGALRM, SIG_IGN);
389         signal (SIGHUP, Rehash);
390         signal (SIGPIPE, SIG_IGN);
391         signal (SIGTERM, Exit);
392         signal (SIGSEGV, Error);
393         if ((childpid = fork ()) < 0)
394                 return (ERROR);
395         else if (childpid > 0)
396                 exit (0);
397         setsid ();
398         umask (007);
399         printf("InspIRCd Process ID: \033[1;32m%lu\033[0m\n",(unsigned long)getpid());
400
401         setpriority(PRIO_PROCESS,(int)getpid(),15);
402
403         if (Config->unlimitcore)
404         {
405                 rlimit rl;
406                 if (getrlimit(RLIMIT_CORE, &rl) == -1)
407                 {
408                         log(DEFAULT,"Failed to getrlimit()!");
409                         return(FALSE);
410                 }
411                 else
412                 {
413                         rl.rlim_cur = rl.rlim_max;
414                         if (setrlimit(RLIMIT_CORE, &rl) == -1)
415                                 log(DEFAULT,"setrlimit() failed, cannot increase coredump size.");
416                 }
417         }
418   
419         return (TRUE);
420 }
421
422
423 /* Make Sure Modules Are Avaliable!
424  * (BugFix By Craig.. See? I do work! :p)
425  * Modified by brain, requires const char*
426  * to work with other API functions
427  */
428
429 bool FileExists (const char* file)
430 {
431         FILE *input;
432         if ((input = fopen (file, "r")) == NULL)
433         {
434                 return(false);
435         }
436         else
437         {
438                 fclose (input);
439                 return(true);
440         }
441 }
442
443 /* ConfProcess does the following things to a config line in the following order:
444  *
445  * Processes the line for syntax errors as shown below
446  *      (1) Line void of quotes or equals (a malformed, illegal tag format)
447  *      (2) Odd number of quotes on the line indicating a missing quote
448  *      (3) number of equals signs not equal to number of quotes / 2 (missing an equals sign)
449  *      (4) Spaces between the opening bracket (<) and the keyword
450  *      (5) Spaces between a keyword and an equals sign
451  *      (6) Spaces between an equals sign and a quote
452  * Removes trailing spaces
453  * Removes leading spaces
454  * Converts tabs to spaces
455  * Turns multiple spaces that are outside of quotes into single spaces
456  */
457
458 std::string ServerConfig::ConfProcess(char* buffer, long linenumber, std::stringstream* errorstream, bool &error, std::string filename)
459 {
460         long number_of_quotes = 0;
461         long number_of_equals = 0;
462         bool has_open_bracket = false;
463         bool in_quotes = false;
464         error = false;
465         if (!buffer)
466         {
467                 return "";
468         }
469         // firstly clean up the line by stripping spaces from the start and end and converting tabs to spaces
470         for (unsigned int d = 0; d < strlen(buffer); d++)
471                 if ((buffer[d]) == 9)
472                         buffer[d] = ' ';
473         while ((buffer[0] == ' ') && (strlen(buffer)>0)) buffer++;
474         while ((buffer[strlen(buffer)-1] == ' ') && (strlen(buffer)>0)) buffer[strlen(buffer)-1] = '\0';
475
476         // empty lines are syntactically valid, as are comments
477         if (!(*buffer) || buffer[0] == '#')
478                 return "";
479
480         for (unsigned int c = 0; c < strlen(buffer); c++)
481         {
482                 // convert all spaces that are OUTSIDE quotes into hardspace (0xA0) as this will make them easier to
483                 // search and replace later :)
484                 if ((!in_quotes) && (buffer[c] == ' '))
485                         buffer[c] = '\xA0';
486                 if ((buffer[c] == '<') && (!in_quotes))
487                 {
488                         has_open_bracket = true;
489                         if (strlen(buffer) == 1)
490                         {
491                                 *errorstream << "Tag without identifier at " << filename << ":" << linenumber << endl;
492                                 error = true;
493                                 return "";
494                         }
495                         else if ((tolower(buffer[c+1]) < 'a') || (tolower(buffer[c+1]) > 'z'))
496                         {
497                                 *errorstream << "Invalid characters in identifier at " << filename << ":" << linenumber << endl;
498                                 error = true;
499                                 return "";
500                         }
501                 }
502                 if (buffer[c] == '"')
503                 {
504                         number_of_quotes++;
505                         in_quotes = (!in_quotes);
506                 }
507                 if ((buffer[c] == '=') && (!in_quotes))
508                 {
509                         number_of_equals++;
510                         if (strlen(buffer) == c)
511                         {
512                                 *errorstream << "Variable without a value at " << filename << ":" << linenumber << endl;
513                                 error = true;
514                                 return "";
515                         }
516                         else if (buffer[c+1] != '"')
517                         {
518                                 *errorstream << "Variable name not followed immediately by its value at " << filename << ":" << linenumber << endl;
519                                 error = true;
520                                 return "";
521                         }
522                         else if (!c)
523                         {
524                                 *errorstream << "Value without a variable (line starts with '=') at " << filename << ":" << linenumber << endl;
525                                 error = true;
526                                 return "";
527                         }
528                         else if (buffer[c-1] == '\xA0')
529                         {
530                                 *errorstream << "Variable name not followed immediately by its value at " << filename << ":" << linenumber << endl;
531                                 error = true;
532                                 return "";
533                         }
534                 }
535         }
536         // no quotes, and no equals. something freaky.
537         if ((!number_of_quotes) || (!number_of_equals) && (strlen(buffer)>2) && (buffer[0]=='<'))
538         {
539                 *errorstream << "Malformed tag at " << filename << ":" << linenumber << endl;
540                 error = true;
541                 return "";
542         }
543         // odd number of quotes. thats just wrong.
544         if ((number_of_quotes % 2) != 0)
545         {
546                 *errorstream << "Missing \" at " << filename << ":" << linenumber << endl;
547                 error = true;
548                 return "";
549         }
550         if (number_of_equals < (number_of_quotes/2))
551         {
552                 *errorstream << "Missing '=' at " << filename << ":" << linenumber << endl;
553         }
554         if (number_of_equals > (number_of_quotes/2))
555         {
556                 *errorstream << "Too many '=' at " << filename << ":" << linenumber << endl;
557         }
558
559         std::string parsedata = buffer;
560         // turn multispace into single space
561         while (parsedata.find("\xA0\xA0") != std::string::npos)
562         {
563                 parsedata.erase(parsedata.find("\xA0\xA0"),1);
564         }
565
566         // turn our hardspace back into softspace
567         for (unsigned int d = 0; d < parsedata.length(); d++)
568         {
569                 if (parsedata[d] == '\xA0')
570                         parsedata[d] = ' ';
571         }
572
573         // and we're done, the line is fine!
574         return parsedata;
575 }
576
577 int ServerConfig::fgets_safe(char* buffer, size_t maxsize, FILE* &file)
578 {
579         char c_read = '\0';
580         unsigned int bufptr = 0;
581         while ((!feof(file)) && (c_read != '\n') && (c_read != '\r') && (bufptr < maxsize))
582         {
583                 c_read = fgetc(file);
584                 if ((c_read != '\n') && (c_read != '\r'))
585                         buffer[bufptr++] = c_read;
586         }
587         buffer[bufptr] = '\0';
588         return bufptr;
589 }
590
591 bool ServerConfig::LoadConf(const char* filename, std::stringstream *target, std::stringstream* errorstream)
592 {
593         target->str("");
594         errorstream->str("");
595         long linenumber = 1;
596         // first, check that the file exists before we try to do anything with it
597         if (!FileExists(filename))
598         {
599                 *errorstream << "File " << filename << " not found." << endl;
600                 return false;
601         }
602         // Fix the chmod of the file to restrict it to the current user and group
603         chmod(filename,0600);
604         for (unsigned int t = 0; t < include_stack.size(); t++)
605         {
606                 if (std::string(filename) == include_stack[t])
607                 {
608                         *errorstream << "File " << filename << " is included recursively (looped inclusion)." << endl;
609                         return false;
610                 }
611         }
612         include_stack.push_back(filename);
613         // now open it
614         FILE* conf = fopen(filename,"r");
615         char buffer[MAXBUF];
616         if (conf)
617         {
618                 while (!feof(conf))
619                 {
620                         if (fgets_safe(buffer, MAXBUF, conf))
621                         {
622                                 if ((!feof(conf)) && (buffer) && (strlen(buffer)))
623                                 {
624                                         if ((buffer[0] != '#') && (buffer[0] != '\r')  && (buffer[0] != '\n'))
625                                         {
626                                                 if (!strncmp(buffer,"<include file=\"",15))
627                                                 {
628                                                         char* buf = buffer;
629                                                         char confpath[10240],newconf[10240];
630                                                         // include file directive
631                                                         buf += 15;      // advance to filename
632                                                         for (unsigned int j = 0; j < strlen(buf); j++)
633                                                         {
634                                                                 if (buf[j] == '\\')
635                                                                         buf[j] = '/';
636                                                                 if (buf[j] == '"')
637                                                                 {
638                                                                         buf[j] = '\0';
639                                                                         break;
640                                                                 }
641                                                         }
642                                                         log(DEFAULT,"Opening included file '%s'",buf);
643                                                         if (*buf != '/')
644                                                         {
645                                                                 strlcpy(confpath,CONFIG_FILE,10240);
646                                                                 if (strstr(confpath,"/inspircd.conf"))
647                                                                 {
648                                                                         // leaves us with just the path
649                                                                         *(strstr(confpath,"/inspircd.conf")) = '\0';
650                                                                 }
651                                                                 snprintf(newconf,10240,"%s/%s",confpath,buf);
652                                                         }
653                                                         else snprintf(newconf,10240,"%s",buf);
654                                                         std::stringstream merge(stringstream::in | stringstream::out);
655                                                         // recursively call LoadConf and get the new data, use the same errorstream
656                                                         if (LoadConf(newconf, &merge, errorstream))
657                                                         {
658                                                                 // append to the end of the file
659                                                                 std::string newstuff = merge.str();
660                                                                 *target << newstuff;
661                                                         }
662                                                         else
663                                                         {
664                                                                 // the error propogates up to its parent recursively
665                                                                 // causing the config reader to bail at the top level.
666                                                                 fclose(conf);
667                                                                 return false;
668                                                         }
669                                                 }
670                                                 else
671                                                 {
672                                                         bool error = false;
673                                                         std::string data = this->ConfProcess(buffer,linenumber++,errorstream,error,filename);
674                                                         if (error)
675                                                         {
676                                                                 return false;
677                                                         }
678                                                         *target << data;
679                                                 }
680                                         }
681                                         else linenumber++;
682                                 }
683                         }
684                 }
685                 fclose(conf);
686         }
687         target->seekg(0);
688         return true;
689 }
690
691 /* Counts the number of tags of a certain type within the config file, e.g. to enumerate opers */
692
693 int ServerConfig::EnumConf(std::stringstream *config, const char* tag)
694 {
695         int ptr = 0;
696         char buffer[MAXBUF], c_tag[MAXBUF], c, lastc;
697         int in_token, in_quotes, tptr, idx = 0;
698
699         const char* buf = config->str().c_str();
700         long bptr = 0;
701         long len = strlen(buf);
702         
703         ptr = 0;
704         in_token = 0;
705         in_quotes = 0;
706         lastc = '\0';
707         while (bptr<len)
708         {
709                 lastc = c;
710                 c = buf[bptr++];
711                 if ((c == '#') && (lastc == '\n'))
712                 {
713                         while ((c != '\n') && (bptr<len))
714                         {
715                                 lastc = c;
716                                 c = buf[bptr++];
717                         }
718                 }
719                 if ((c == '<') && (!in_quotes))
720                 {
721                         tptr = 0;
722                         in_token = 1;
723                         do {
724                                 c = buf[bptr++];
725                                 if (c != ' ')
726                                 {
727                                         c_tag[tptr++] = c;
728                                         c_tag[tptr] = '\0';
729                                 }
730                         } while (c != ' ');
731                 }
732                 if (c == '"')
733                 {
734                         in_quotes = (!in_quotes);
735                 }
736                 if ((c == '>') && (!in_quotes))
737                 {
738                         in_token = 0;
739                         if (!strcmp(c_tag,tag))
740                         {
741                                 /* correct tag, but wrong index */
742                                 idx++;
743                         }
744                         c_tag[0] = '\0';
745                         buffer[0] = '\0';
746                         ptr = 0;
747                         tptr = 0;
748                 }
749                 if (c != '>')
750                 {
751                         if ((in_token) && (c != '\n') && (c != '\r'))
752                         {
753                                 buffer[ptr++] = c;
754                                 buffer[ptr] = '\0';
755                         }
756                 }
757         }
758         return idx;
759 }
760
761 /* Counts the number of values within a certain tag */
762
763 int ServerConfig::EnumValues(std::stringstream *config, const char* tag, int index)
764 {
765         int ptr = 0;
766         char buffer[MAXBUF], c_tag[MAXBUF], c, lastc;
767         int in_token, in_quotes, tptr, idx = 0;
768         
769         bool correct_tag = false;
770         int num_items = 0;
771
772         const char* buf = config->str().c_str();
773         long bptr = 0;
774         long len = strlen(buf);
775         
776         ptr = 0;
777         in_token = 0;
778         in_quotes = 0;
779         lastc = '\0';
780         while (bptr<len)
781         {
782                 lastc = c;
783                 c = buf[bptr++];
784                 if ((c == '#') && (lastc == '\n'))
785                 {
786                         while ((c != '\n') && (bptr<len))
787                         {
788                                 lastc = c;
789                                 c = buf[bptr++];
790                         }
791                 }
792                 if ((c == '<') && (!in_quotes))
793                 {
794                         tptr = 0;
795                         in_token = 1;
796                         do {
797                                 c = buf[bptr++];
798                                 if (c != ' ')
799                                 {
800                                         c_tag[tptr++] = c;
801                                         c_tag[tptr] = '\0';
802                                         
803                                         if ((!strcmp(c_tag,tag)) && (idx == index))
804                                         {
805                                                 correct_tag = true;
806                                         }
807                                 }
808                         } while (c != ' ');
809                 }
810                 if (c == '"')
811                 {
812                         in_quotes = (!in_quotes);
813                 }
814                 
815                 if ( (correct_tag) && (!in_quotes) && ( (c == ' ') || (c == '\n') || (c == '\r') ) )
816                 {
817                         num_items++;
818                 }
819                 if ((c == '>') && (!in_quotes))
820                 {
821                         in_token = 0;
822                         if (correct_tag)
823                                 correct_tag = false;
824                         if (!strcmp(c_tag,tag))
825                         {
826                                 /* correct tag, but wrong index */
827                                 idx++;
828                         }
829                         c_tag[0] = '\0';
830                         buffer[0] = '\0';
831                         ptr = 0;
832                         tptr = 0;
833                 }
834                 if (c != '>')
835                 {
836                         if ((in_token) && (c != '\n') && (c != '\r'))
837                         {
838                                 buffer[ptr++] = c;
839                                 buffer[ptr] = '\0';
840                         }
841                 }
842         }
843         return num_items+1;
844 }
845
846
847
848 int ServerConfig::ConfValueEnum(char* tag, std::stringstream* config)
849 {
850         return EnumConf(config,tag);
851 }
852
853
854
855 /* Retrieves a value from the config file. If there is more than one value of the specified
856  * key and section (e.g. for opers etc) then the index value specifies which to retreive, e.g.
857  *
858  * ConfValue("oper","name",2,result);
859  */
860
861 int ServerConfig::ReadConf(std::stringstream *config, const char* tag, const char* var, int index, char *result)
862 {
863         int ptr = 0;
864         char buffer[65535], c_tag[MAXBUF], c, lastc;
865         int in_token, in_quotes, tptr, idx = 0;
866         char* key;
867
868         const char* buf = config->str().c_str();
869         long bptr = 0;
870         long len = config->str().length();
871         
872         ptr = 0;
873         in_token = 0;
874         in_quotes = 0;
875         lastc = '\0';
876         c_tag[0] = '\0';
877         buffer[0] = '\0';
878         while (bptr<len)
879         {
880                 lastc = c;
881                 c = buf[bptr++];
882                 // FIX: Treat tabs as spaces
883                 if (c == 9)
884                         c = 32;
885                 if ((c == '<') && (!in_quotes))
886                 {
887                         tptr = 0;
888                         in_token = 1;
889                         do {
890                                 c = buf[bptr++];
891                                 if (c != ' ')
892                                 {
893                                         c_tag[tptr++] = c;
894                                         c_tag[tptr] = '\0';
895                                 }
896                         // FIX: Tab can follow a tagname as well as space.
897                         } while ((c != ' ') && (c != 9));
898                 }
899                 if (c == '"')
900                 {
901                         in_quotes = (!in_quotes);
902                 }
903                 if ((c == '>') && (!in_quotes))
904                 {
905                         in_token = 0;
906                         if (idx == index)
907                         {
908                                 if (!strcmp(c_tag,tag))
909                                 {
910                                         if ((buffer) && (c_tag) && (var))
911                                         {
912                                                 key = strstr(buffer,var);
913                                                 if (!key)
914                                                 {
915                                                         /* value not found in tag */
916                                                         strcpy(result,"");
917                                                         return 0;
918                                                 }
919                                                 else
920                                                 {
921                                                         key+=strlen(var);
922                                                         while (key[0] !='"')
923                                                         {
924                                                                 if (!strlen(key))
925                                                                 {
926                                                                         /* missing quote */
927                                                                         strcpy(result,"");
928                                                                         return 0;
929                                                                 }
930                                                                 key++;
931                                                         }
932                                                         key++;
933                                                         for (unsigned j = 0; j < strlen(key); j++)
934                                                         {
935                                                                 if (key[j] == '"')
936                                                                 {
937                                                                         key[j] = '\0';
938                                                                 }
939                                                         }
940                                                         strlcpy(result,key,MAXBUF);
941                                                         return 1;
942                                                 }
943                                         }
944                                 }
945                         }
946                         if (!strcmp(c_tag,tag))
947                         {
948                                 /* correct tag, but wrong index */
949                                 idx++;
950                         }
951                         c_tag[0] = '\0';
952                         buffer[0] = '\0';
953                         ptr = 0;
954                         tptr = 0;
955                 }
956                 if (c != '>')
957                 {
958                         if ((in_token) && (c != '\n') && (c != '\r'))
959                         {
960                                 buffer[ptr++] = c;
961                                 buffer[ptr] = '\0';
962                         }
963                 }
964         }
965         strcpy(result,""); // value or its tag not found at all
966         return 0;
967 }
968
969
970
971 int ServerConfig::ConfValue(char* tag, char* var, int index, char *result,std::stringstream *config)
972 {
973         ReadConf(config, tag, var, index, result);
974         return 0;
975 }
976
977
978
979 // This will bind a socket to a port. It works for UDP/TCP
980 int BindSocket (int sockfd, struct sockaddr_in client, struct sockaddr_in server, int port, char* addr)
981 {
982         memset((char *)&server,0,sizeof(server));
983         struct in_addr addy;
984         inet_aton(addr,&addy);
985         server.sin_family = AF_INET;
986         if (!strcmp(addr,""))
987         {
988                 server.sin_addr.s_addr = htonl(INADDR_ANY);
989         }
990         else
991         {
992                 server.sin_addr = addy;
993         }
994         server.sin_port = htons(port);
995         if (bind(sockfd,(struct sockaddr*)&server,sizeof(server))<0)
996         {
997                 return(ERROR);
998         }
999         else
1000         {
1001                 listen(sockfd, Config->MaxConn);
1002                 return(TRUE);
1003         }
1004 }
1005
1006
1007 // Open a TCP Socket
1008 int OpenTCPSocket (void)
1009 {
1010         int sockfd;
1011         int on = 1;
1012         struct linger linger = { 0 };
1013   
1014         if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
1015                 return (ERROR);
1016         else
1017         {
1018                 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
1019                 /* This is BSD compatible, setting l_onoff to 0 is *NOT* http://web.irc.org/mla/ircd-dev/msg02259.html */
1020                 linger.l_onoff = 1;
1021                 linger.l_linger = 1;
1022                 setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (const char*)&linger,sizeof(linger));
1023                 return (sockfd);
1024         }
1025 }