]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/inspircd_io.cpp
Fixed a bad extern (bad extern, BAD)
[user/henk/code/inspircd.git] / src / inspircd_io.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  Inspire is copyright (C) 2002-2004 ChatSpike-Dev.
6  *                       E-mail:
7  *                <brain@chatspike.net>
8  *                <Craig@chatspike.net>
9  *     
10  * Written by Craig Edwards, Craig McLure, and others.
11  * This program is free but copyrighted software; see
12  *            the file COPYING for details.
13  *
14  * ---------------------------------------------------
15  */
16
17 using namespace std;
18
19 #include "inspircd_config.h"
20 #include <sys/time.h>
21 #include <sys/resource.h>
22 #include <sys/types.h>
23 #include <string>
24 #include <unistd.h>
25 #include <sstream>
26 #include <iostream>
27 #include <fstream>
28 #include "inspircd.h"
29 #include "inspircd_io.h"
30 #include "inspircd_util.h"
31 #include "inspstring.h"
32 #include "helperfuncs.h"
33 #include "xline.h"
34
35 extern ServerConfig *Config;
36 extern int 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 (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(), 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 (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(), 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 WriteOpers(char* text, ...);
332
333 void Exit (int status)
334 {
335         if (Config->log_file)
336                 fclose(Config->log_file);
337         send_error("Server shutdown.");
338         exit (status);
339 }
340
341 void Killed(int status)
342 {
343         if (Config->log_file)
344                 fclose(Config->log_file);
345         send_error("Server terminated.");
346         exit(status);
347 }
348
349 void Rehash(int status)
350 {
351         WriteOpers("Rehashing config file %s due to SIGHUP",CONFIG_FILE);
352         Config->Read(false,NULL);
353 }
354
355
356
357 void Start (void)
358 {
359         printf("\033[1;32mInspire Internet Relay Chat Server, compiled %s at %s\n",__DATE__,__TIME__);
360         printf("(C) ChatSpike Development team.\033[0m\n\n");
361         printf("Developers:\033[1;32m     Brain, FrostyCoolSlug\033[0m\n");
362         printf("Documentation:\033[1;32m  FrostyCoolSlug, w00t\033[0m\n");
363         printf("Testers:\033[1;32m        typobox43, piggles, Lord_Zathras, CC\033[0m\n");
364         printf("Name concept:\033[1;32m   Lord_Zathras\033[0m\n\n");
365 }
366
367 void WritePID(std::string filename)
368 {
369         ofstream outfile(filename.c_str());
370         if (outfile.is_open())
371         {
372                 outfile << getpid();
373                 outfile.close();
374         }
375         else
376         {
377                 printf("Failed to write PID-file '%s', exiting.\n",filename.c_str());
378                 log(DEFAULT,"Failed to write PID-file '%s', exiting.",filename.c_str());
379                 Exit(0);
380         }
381 }
382
383
384 int DaemonSeed (void)
385 {
386         int childpid;
387         signal (SIGALRM, SIG_IGN);
388         signal (SIGHUP, Rehash);
389         signal (SIGPIPE, SIG_IGN);
390         signal (SIGTERM, Exit);
391         signal (SIGSEGV, Error);
392         if ((childpid = fork ()) < 0)
393                 return (ERROR);
394         else if (childpid > 0)
395                 exit (0);
396         setsid ();
397         umask (007);
398         printf("InspIRCd Process ID: \033[1;32m%lu\033[0m\n",(unsigned long)getpid());
399
400         setpriority(PRIO_PROCESS,(int)getpid(),15);
401
402         if (Config->unlimitcore)
403         {
404                 rlimit rl;
405                 if (getrlimit(RLIMIT_CORE, &rl) == -1)
406                 {
407                         log(DEFAULT,"Failed to getrlimit()!");
408                         return(FALSE);
409                 }
410                 else
411                 {
412                         rl.rlim_cur = rl.rlim_max;
413                         if (setrlimit(RLIMIT_CORE, &rl) == -1)
414                                 log(DEFAULT,"setrlimit() failed, cannot increase coredump size.");
415                 }
416         }
417   
418         return (TRUE);
419 }
420
421
422 /* Make Sure Modules Are Avaliable!
423  * (BugFix By Craig.. See? I do work! :p)
424  * Modified by brain, requires const char*
425  * to work with other API functions
426  */
427
428 bool FileExists (const char* file)
429 {
430         FILE *input;
431         if ((input = fopen (file, "r")) == NULL)
432         {
433                 return(false);
434         }
435         else
436         {
437                 fclose (input);
438                 return(true);
439         }
440 }
441
442 /* ConfProcess does the following things to a config line in the following order:
443  *
444  * Processes the line for syntax errors as shown below
445  *      (1) Line void of quotes or equals (a malformed, illegal tag format)
446  *      (2) Odd number of quotes on the line indicating a missing quote
447  *      (3) number of equals signs not equal to number of quotes / 2 (missing an equals sign)
448  *      (4) Spaces between the opening bracket (<) and the keyword
449  *      (5) Spaces between a keyword and an equals sign
450  *      (6) Spaces between an equals sign and a quote
451  * Removes trailing spaces
452  * Removes leading spaces
453  * Converts tabs to spaces
454  * Turns multiple spaces that are outside of quotes into single spaces
455  */
456
457 std::string ServerConfig::ConfProcess(char* buffer, long linenumber, std::stringstream* errorstream, bool &error, std::string filename)
458 {
459         long number_of_quotes = 0;
460         long number_of_equals = 0;
461         bool has_open_bracket = false;
462         bool in_quotes = false;
463         error = false;
464         if (!buffer)
465         {
466                 return "";
467         }
468         // firstly clean up the line by stripping spaces from the start and end and converting tabs to spaces
469         for (unsigned int d = 0; d < strlen(buffer); d++)
470                 if ((buffer[d]) == 9)
471                         buffer[d] = ' ';
472         while ((buffer[0] == ' ') && (strlen(buffer)>0)) buffer++;
473         while ((buffer[strlen(buffer)-1] == ' ') && (strlen(buffer)>0)) buffer[strlen(buffer)-1] = '\0';
474
475         // empty lines are syntactically valid, as are comments
476         if (!(*buffer) || buffer[0] == '#')
477                 return "";
478
479         for (unsigned int c = 0; c < strlen(buffer); c++)
480         {
481                 // convert all spaces that are OUTSIDE quotes into hardspace (0xA0) as this will make them easier to
482                 // search and replace later :)
483                 if ((!in_quotes) && (buffer[c] == ' '))
484                         buffer[c] = '\xA0';
485                 if ((buffer[c] == '<') && (!in_quotes))
486                 {
487                         has_open_bracket = true;
488                         if (strlen(buffer) == 1)
489                         {
490                                 *errorstream << "Tag without identifier at " << filename << ":" << linenumber << endl;
491                                 error = true;
492                                 return "";
493                         }
494                         else if ((tolower(buffer[c+1]) < 'a') || (tolower(buffer[c+1]) > 'z'))
495                         {
496                                 *errorstream << "Invalid characters in identifier at " << filename << ":" << linenumber << endl;
497                                 error = true;
498                                 return "";
499                         }
500                 }
501                 if (buffer[c] == '"')
502                 {
503                         number_of_quotes++;
504                         in_quotes = (!in_quotes);
505                 }
506                 if ((buffer[c] == '=') && (!in_quotes))
507                 {
508                         number_of_equals++;
509                         if (strlen(buffer) == c)
510                         {
511                                 *errorstream << "Variable without a value at " << filename << ":" << linenumber << endl;
512                                 error = true;
513                                 return "";
514                         }
515                         else if (buffer[c+1] != '"')
516                         {
517                                 *errorstream << "Variable name not followed immediately by its value at " << filename << ":" << linenumber << endl;
518                                 error = true;
519                                 return "";
520                         }
521                         else if (!c)
522                         {
523                                 *errorstream << "Value without a variable (line starts with '=') at " << filename << ":" << linenumber << endl;
524                                 error = true;
525                                 return "";
526                         }
527                         else if (buffer[c-1] == '\xA0')
528                         {
529                                 *errorstream << "Variable name not followed immediately by its value at " << filename << ":" << linenumber << endl;
530                                 error = true;
531                                 return "";
532                         }
533                 }
534         }
535         // no quotes, and no equals. something freaky.
536         if ((!number_of_quotes) || (!number_of_equals) && (strlen(buffer)>2) && (buffer[0]=='<'))
537         {
538                 *errorstream << "Malformed tag at " << filename << ":" << linenumber << endl;
539                 error = true;
540                 return "";
541         }
542         // odd number of quotes. thats just wrong.
543         if ((number_of_quotes % 2) != 0)
544         {
545                 *errorstream << "Missing \" at " << filename << ":" << linenumber << endl;
546                 error = true;
547                 return "";
548         }
549         if (number_of_equals < (number_of_quotes/2))
550         {
551                 *errorstream << "Missing '=' at " << filename << ":" << linenumber << endl;
552         }
553         if (number_of_equals > (number_of_quotes/2))
554         {
555                 *errorstream << "Too many '=' at " << filename << ":" << linenumber << endl;
556         }
557
558         std::string parsedata = buffer;
559         // turn multispace into single space
560         while (parsedata.find("\xA0\xA0") != std::string::npos)
561         {
562                 parsedata.erase(parsedata.find("\xA0\xA0"),1);
563         }
564
565         // turn our hardspace back into softspace
566         for (unsigned int d = 0; d < parsedata.length(); d++)
567         {
568                 if (parsedata[d] == '\xA0')
569                         parsedata[d] = ' ';
570         }
571
572         // and we're done, the line is fine!
573         return parsedata;
574 }
575
576 int ServerConfig::fgets_safe(char* buffer, size_t maxsize, FILE* &file)
577 {
578         char c_read = '\0';
579         unsigned int bufptr = 0;
580         while ((!feof(file)) && (c_read != '\n') && (c_read != '\r') && (bufptr < maxsize))
581         {
582                 c_read = fgetc(file);
583                 if ((c_read != '\n') && (c_read != '\r'))
584                         buffer[bufptr++] = c_read;
585         }
586         buffer[bufptr] = '\0';
587         return bufptr;
588 }
589
590 bool ServerConfig::LoadConf(const char* filename, std::stringstream *target, std::stringstream* errorstream)
591 {
592         target->str("");
593         errorstream->str("");
594         long linenumber = 1;
595         // first, check that the file exists before we try to do anything with it
596         if (!FileExists(filename))
597         {
598                 *errorstream << "File " << filename << " not found." << endl;
599                 return false;
600         }
601         // Fix the chmod of the file to restrict it to the current user and group
602         chmod(filename,0600);
603         for (unsigned int t = 0; t < include_stack.size(); t++)
604         {
605                 if (std::string(filename) == include_stack[t])
606                 {
607                         *errorstream << "File " << filename << " is included recursively (looped inclusion)." << endl;
608                         return false;
609                 }
610         }
611         include_stack.push_back(filename);
612         // now open it
613         FILE* conf = fopen(filename,"r");
614         char buffer[MAXBUF];
615         if (conf)
616         {
617                 while (!feof(conf))
618                 {
619                         if (fgets_safe(buffer, MAXBUF, conf))
620                         {
621                                 if ((!feof(conf)) && (buffer) && (strlen(buffer)))
622                                 {
623                                         if ((buffer[0] != '#') && (buffer[0] != '\r')  && (buffer[0] != '\n'))
624                                         {
625                                                 if (!strncmp(buffer,"<include file=\"",15))
626                                                 {
627                                                         char* buf = buffer;
628                                                         char confpath[10240],newconf[10240];
629                                                         // include file directive
630                                                         buf += 15;      // advance to filename
631                                                         for (unsigned int j = 0; j < strlen(buf); j++)
632                                                         {
633                                                                 if (buf[j] == '\\')
634                                                                         buf[j] = '/';
635                                                                 if (buf[j] == '"')
636                                                                 {
637                                                                         buf[j] = '\0';
638                                                                         break;
639                                                                 }
640                                                         }
641                                                         log(DEFAULT,"Opening included file '%s'",buf);
642                                                         if (*buf != '/')
643                                                         {
644                                                                 strlcpy(confpath,CONFIG_FILE,10240);
645                                                                 if (strstr(confpath,"/inspircd.conf"))
646                                                                 {
647                                                                         // leaves us with just the path
648                                                                         *(strstr(confpath,"/inspircd.conf")) = '\0';
649                                                                 }
650                                                                 snprintf(newconf,10240,"%s/%s",confpath,buf);
651                                                         }
652                                                         else snprintf(newconf,10240,"%s",buf);
653                                                         std::stringstream merge(stringstream::in | stringstream::out);
654                                                         // recursively call LoadConf and get the new data, use the same errorstream
655                                                         if (LoadConf(newconf, &merge, errorstream))
656                                                         {
657                                                                 // append to the end of the file
658                                                                 std::string newstuff = merge.str();
659                                                                 *target << newstuff;
660                                                         }
661                                                         else
662                                                         {
663                                                                 // the error propogates up to its parent recursively
664                                                                 // causing the config reader to bail at the top level.
665                                                                 fclose(conf);
666                                                                 return false;
667                                                         }
668                                                 }
669                                                 else
670                                                 {
671                                                         bool error = false;
672                                                         std::string data = this->ConfProcess(buffer,linenumber++,errorstream,error,filename);
673                                                         if (error)
674                                                         {
675                                                                 return false;
676                                                         }
677                                                         *target << data;
678                                                 }
679                                         }
680                                         else linenumber++;
681                                 }
682                         }
683                 }
684                 fclose(conf);
685         }
686         target->seekg(0);
687         return true;
688 }
689
690 /* Counts the number of tags of a certain type within the config file, e.g. to enumerate opers */
691
692 int ServerConfig::EnumConf(std::stringstream *config, const char* tag)
693 {
694         int ptr = 0;
695         char buffer[MAXBUF], c_tag[MAXBUF], c, lastc;
696         int in_token, in_quotes, tptr, idx = 0;
697
698         const char* buf = config->str().c_str();
699         long bptr = 0;
700         long len = strlen(buf);
701         
702         ptr = 0;
703         in_token = 0;
704         in_quotes = 0;
705         lastc = '\0';
706         while (bptr<len)
707         {
708                 lastc = c;
709                 c = buf[bptr++];
710                 if ((c == '#') && (lastc == '\n'))
711                 {
712                         while ((c != '\n') && (bptr<len))
713                         {
714                                 lastc = c;
715                                 c = buf[bptr++];
716                         }
717                 }
718                 if ((c == '<') && (!in_quotes))
719                 {
720                         tptr = 0;
721                         in_token = 1;
722                         do {
723                                 c = buf[bptr++];
724                                 if (c != ' ')
725                                 {
726                                         c_tag[tptr++] = c;
727                                         c_tag[tptr] = '\0';
728                                 }
729                         } while (c != ' ');
730                 }
731                 if (c == '"')
732                 {
733                         in_quotes = (!in_quotes);
734                 }
735                 if ((c == '>') && (!in_quotes))
736                 {
737                         in_token = 0;
738                         if (!strcmp(c_tag,tag))
739                         {
740                                 /* correct tag, but wrong index */
741                                 idx++;
742                         }
743                         c_tag[0] = '\0';
744                         buffer[0] = '\0';
745                         ptr = 0;
746                         tptr = 0;
747                 }
748                 if (c != '>')
749                 {
750                         if ((in_token) && (c != '\n') && (c != '\r'))
751                         {
752                                 buffer[ptr++] = c;
753                                 buffer[ptr] = '\0';
754                         }
755                 }
756         }
757         return idx;
758 }
759
760 /* Counts the number of values within a certain tag */
761
762 int ServerConfig::EnumValues(std::stringstream *config, const char* tag, int index)
763 {
764         int ptr = 0;
765         char buffer[MAXBUF], c_tag[MAXBUF], c, lastc;
766         int in_token, in_quotes, tptr, idx = 0;
767         
768         bool correct_tag = false;
769         int num_items = 0;
770
771         const char* buf = config->str().c_str();
772         long bptr = 0;
773         long len = strlen(buf);
774         
775         ptr = 0;
776         in_token = 0;
777         in_quotes = 0;
778         lastc = '\0';
779         while (bptr<len)
780         {
781                 lastc = c;
782                 c = buf[bptr++];
783                 if ((c == '#') && (lastc == '\n'))
784                 {
785                         while ((c != '\n') && (bptr<len))
786                         {
787                                 lastc = c;
788                                 c = buf[bptr++];
789                         }
790                 }
791                 if ((c == '<') && (!in_quotes))
792                 {
793                         tptr = 0;
794                         in_token = 1;
795                         do {
796                                 c = buf[bptr++];
797                                 if (c != ' ')
798                                 {
799                                         c_tag[tptr++] = c;
800                                         c_tag[tptr] = '\0';
801                                         
802                                         if ((!strcmp(c_tag,tag)) && (idx == index))
803                                         {
804                                                 correct_tag = true;
805                                         }
806                                 }
807                         } while (c != ' ');
808                 }
809                 if (c == '"')
810                 {
811                         in_quotes = (!in_quotes);
812                 }
813                 
814                 if ( (correct_tag) && (!in_quotes) && ( (c == ' ') || (c == '\n') || (c == '\r') ) )
815                 {
816                         num_items++;
817                 }
818                 if ((c == '>') && (!in_quotes))
819                 {
820                         in_token = 0;
821                         if (correct_tag)
822                                 correct_tag = false;
823                         if (!strcmp(c_tag,tag))
824                         {
825                                 /* correct tag, but wrong index */
826                                 idx++;
827                         }
828                         c_tag[0] = '\0';
829                         buffer[0] = '\0';
830                         ptr = 0;
831                         tptr = 0;
832                 }
833                 if (c != '>')
834                 {
835                         if ((in_token) && (c != '\n') && (c != '\r'))
836                         {
837                                 buffer[ptr++] = c;
838                                 buffer[ptr] = '\0';
839                         }
840                 }
841         }
842         return num_items+1;
843 }
844
845
846
847 int ServerConfig::ConfValueEnum(char* tag, std::stringstream* config)
848 {
849         return EnumConf(config,tag);
850 }
851
852
853
854 /* Retrieves a value from the config file. If there is more than one value of the specified
855  * key and section (e.g. for opers etc) then the index value specifies which to retreive, e.g.
856  *
857  * ConfValue("oper","name",2,result);
858  */
859
860 int ServerConfig::ReadConf(std::stringstream *config, const char* tag, const char* var, int index, char *result)
861 {
862         int ptr = 0;
863         char buffer[65535], c_tag[MAXBUF], c, lastc;
864         int in_token, in_quotes, tptr, idx = 0;
865         char* key;
866
867         const char* buf = config->str().c_str();
868         long bptr = 0;
869         long len = config->str().length();
870         
871         ptr = 0;
872         in_token = 0;
873         in_quotes = 0;
874         lastc = '\0';
875         c_tag[0] = '\0';
876         buffer[0] = '\0';
877         while (bptr<len)
878         {
879                 lastc = c;
880                 c = buf[bptr++];
881                 // FIX: Treat tabs as spaces
882                 if (c == 9)
883                         c = 32;
884                 if ((c == '<') && (!in_quotes))
885                 {
886                         tptr = 0;
887                         in_token = 1;
888                         do {
889                                 c = buf[bptr++];
890                                 if (c != ' ')
891                                 {
892                                         c_tag[tptr++] = c;
893                                         c_tag[tptr] = '\0';
894                                 }
895                         // FIX: Tab can follow a tagname as well as space.
896                         } while ((c != ' ') && (c != 9));
897                 }
898                 if (c == '"')
899                 {
900                         in_quotes = (!in_quotes);
901                 }
902                 if ((c == '>') && (!in_quotes))
903                 {
904                         in_token = 0;
905                         if (idx == index)
906                         {
907                                 if (!strcmp(c_tag,tag))
908                                 {
909                                         if ((buffer) && (c_tag) && (var))
910                                         {
911                                                 key = strstr(buffer,var);
912                                                 if (!key)
913                                                 {
914                                                         /* value not found in tag */
915                                                         strcpy(result,"");
916                                                         return 0;
917                                                 }
918                                                 else
919                                                 {
920                                                         key+=strlen(var);
921                                                         while (key[0] !='"')
922                                                         {
923                                                                 if (!strlen(key))
924                                                                 {
925                                                                         /* missing quote */
926                                                                         strcpy(result,"");
927                                                                         return 0;
928                                                                 }
929                                                                 key++;
930                                                         }
931                                                         key++;
932                                                         for (unsigned j = 0; j < strlen(key); j++)
933                                                         {
934                                                                 if (key[j] == '"')
935                                                                 {
936                                                                         key[j] = '\0';
937                                                                 }
938                                                         }
939                                                         strlcpy(result,key,MAXBUF);
940                                                         return 1;
941                                                 }
942                                         }
943                                 }
944                         }
945                         if (!strcmp(c_tag,tag))
946                         {
947                                 /* correct tag, but wrong index */
948                                 idx++;
949                         }
950                         c_tag[0] = '\0';
951                         buffer[0] = '\0';
952                         ptr = 0;
953                         tptr = 0;
954                 }
955                 if (c != '>')
956                 {
957                         if ((in_token) && (c != '\n') && (c != '\r'))
958                         {
959                                 buffer[ptr++] = c;
960                                 buffer[ptr] = '\0';
961                         }
962                 }
963         }
964         strcpy(result,""); // value or its tag not found at all
965         return 0;
966 }
967
968
969
970 int ServerConfig::ConfValue(char* tag, char* var, int index, char *result,std::stringstream *config)
971 {
972         ReadConf(config, tag, var, index, result);
973         return 0;
974 }
975
976
977
978 // This will bind a socket to a port. It works for UDP/TCP
979 int BindSocket (int sockfd, struct sockaddr_in client, struct sockaddr_in server, int port, char* addr)
980 {
981         memset((char *)&server,0,sizeof(server));
982         struct in_addr addy;
983         inet_aton(addr,&addy);
984         server.sin_family = AF_INET;
985         if (!strcmp(addr,""))
986         {
987                 server.sin_addr.s_addr = htonl(INADDR_ANY);
988         }
989         else
990         {
991                 server.sin_addr = addy;
992         }
993         server.sin_port = htons(port);
994         if (bind(sockfd,(struct sockaddr*)&server,sizeof(server))<0)
995         {
996                 return(ERROR);
997         }
998         else
999         {
1000                 listen(sockfd, Config->MaxConn);
1001                 return(TRUE);
1002         }
1003 }
1004
1005
1006 // Open a TCP Socket
1007 int OpenTCPSocket (void)
1008 {
1009         int sockfd;
1010         int on = 1;
1011         struct linger linger = { 0 };
1012   
1013         if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
1014                 return (ERROR);
1015         else
1016         {
1017                 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
1018                 /* This is BSD compatible, setting l_onoff to 0 is *NOT* http://web.irc.org/mla/ircd-dev/msg02259.html */
1019                 linger.l_onoff = 1;
1020                 linger.l_linger = 1;
1021                 setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (const char*)&linger,sizeof(linger));
1022                 return (sockfd);
1023         }
1024 }
1025
1026 int BindPorts()
1027 {
1028         char configToken[MAXBUF], Addr[MAXBUF], Type[MAXBUF];
1029         sockaddr_in client,server;
1030         int clientportcount = 0;
1031         int BoundPortCount;
1032         for (int count = 0; count < Config->ConfValueEnum("bind",&Config->config_f); count++)
1033         {
1034                 Config->ConfValue("bind","port",count,configToken,&Config->config_f);
1035                 Config->ConfValue("bind","address",count,Addr,&Config->config_f);
1036                 Config->ConfValue("bind","type",count,Type,&Config->config_f);
1037                 if (strcmp(Type,"servers"))
1038                 {
1039                         // modules handle server bind types now,
1040                         // its not a typo in the strcmp.
1041                         Config->ports[clientportcount] = atoi(configToken);
1042                         strlcpy(Config->addrs[clientportcount],Addr,256);
1043                         clientportcount++;
1044                         log(DEBUG,"InspIRCd: startup: read binding %s:%s [%s] from config",Addr,configToken, Type);
1045                 }
1046         }
1047         int PortCount = clientportcount;
1048
1049         for (int count = 0; count < PortCount; count++)
1050         {
1051                 if ((openSockfd[BoundPortCount] = OpenTCPSocket()) == ERROR)
1052                 {
1053                         log(DEBUG,"InspIRCd: startup: bad fd %lu",(unsigned long)openSockfd[BoundPortCount]);
1054                         return(ERROR);
1055                 }
1056                 if (BindSocket(openSockfd[BoundPortCount],client,server,Config->ports[count],Config->addrs[count]) == ERROR)
1057                 {
1058                         log(DEFAULT,"InspIRCd: startup: failed to bind port %lu",(unsigned long)Config->ports[count]);
1059                 }
1060                 else    /* well we at least bound to one socket so we'll continue */
1061                 {
1062                         BoundPortCount++;
1063                 }
1064         }
1065
1066         /* if we didn't bind to anything then abort */
1067         if (!BoundPortCount)
1068         {
1069                 log(DEFAULT,"InspIRCd: startup: no ports bound, bailing!");
1070                 printf("\nERROR: Was not able to bind any of %lu ports! Please check your configuration.\n\n", (unsigned long)PortCount);
1071                 return (ERROR);
1072         }
1073
1074         return BoundPortCount;
1075 }
1076