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