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