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