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