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