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