]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/inspircd_io.cpp
7630eddceebaceee5db24f8417c4b159694181a9
[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                                 if (user)
421                                         WriteServ(user->fd,"973 %s %s :Module %s successfully unloaded.",user->nick, removing->c_str(), removing->c_str());
422                                 rem++;
423                         }
424                         else
425                         {
426                                 if (user)
427                                         WriteServ(user->fd,"972 %s %s :Failed to unload module %s: %s",user->nick, removing->c_str(), removing->c_str(), ServerInstance->ModuleError());
428                         }
429                 }
430                 if (!added_modules.empty())
431                 for (std::vector<std::string>::iterator adding = added_modules.begin(); adding != added_modules.end(); adding++)
432                 {
433                         if (ServerInstance->LoadModule(adding->c_str()))
434                         {
435                                 WriteOpers("*** REHASH LOADED MODULE: %s",adding->c_str());
436                                 if (user)
437                                         WriteServ(user->fd,"975 %s %s :Module %s successfully loaded.",user->nick, adding->c_str(), adding->c_str());
438                                 add++;
439                         }
440                         else
441                         {
442                                 if (user)
443                                         WriteServ(user->fd,"974 %s %s :Failed to load module %s: %s",user->nick, adding->c_str(), adding->c_str(), ServerInstance->ModuleError());
444                         }
445                 }
446                 log(DEFAULT,"Successfully unloaded %lu of %lu modules and loaded %lu of %lu modules.",(unsigned long)rem,(unsigned long)removed_modules.size(),
447                                                                                                         (unsigned long)add,(unsigned long)added_modules.size());
448         }
449 }
450
451
452 void Exit (int status)
453 {
454         if (Config->log_file)
455                 fclose(Config->log_file);
456         send_error("Server shutdown.");
457         exit (status);
458 }
459
460 void Killed(int status)
461 {
462         if (Config->log_file)
463                 fclose(Config->log_file);
464         send_error("Server terminated.");
465         exit(status);
466 }
467
468 char* CleanFilename(char* name)
469 {
470         char* p = name + strlen(name);
471         while ((p != name) && (*p != '/')) p--;
472         return (p != name ? ++p : p);
473 }
474
475
476 void Rehash(int status)
477 {
478         WriteOpers("Rehashing config file %s due to SIGHUP",CleanFilename(CONFIG_FILE));
479         fclose(Config->log_file);
480         OpenLog(NULL,0);
481         Config->Read(false,NULL);
482         FOREACH_MOD(I_OnRehash,OnRehash(""));
483 }
484
485
486
487 void Start (void)
488 {
489         printf("\033[1;32mInspire Internet Relay Chat Server, compiled %s at %s\n",__DATE__,__TIME__);
490         printf("(C) ChatSpike Development team.\033[0m\n\n");
491         printf("Developers:\033[1;32m     Brain, FrostyCoolSlug\033[0m\n");
492         printf("Documentation:\033[1;32m  FrostyCoolSlug, w00t\033[0m\n");
493         printf("Testers:\033[1;32m        typobox43, piggles, Lord_Zathras, CC\033[0m\n");
494         printf("Name concept:\033[1;32m   Lord_Zathras\033[0m\n\n");
495 }
496
497 void WritePID(std::string filename)
498 {
499         ofstream outfile(filename.c_str());
500         if (outfile.is_open())
501         {
502                 outfile << getpid();
503                 outfile.close();
504         }
505         else
506         {
507                 printf("Failed to write PID-file '%s', exiting.\n",filename.c_str());
508                 log(DEFAULT,"Failed to write PID-file '%s', exiting.",filename.c_str());
509                 Exit(0);
510         }
511 }
512
513 void SetSignals()
514 {
515         signal (SIGALRM, SIG_IGN);
516         signal (SIGHUP, Rehash);
517         signal (SIGPIPE, SIG_IGN);
518         signal (SIGTERM, Exit);
519         signal (SIGSEGV, Error);
520 }
521
522
523 int DaemonSeed (void)
524 {
525         int childpid;
526         if ((childpid = fork ()) < 0)
527                 return (ERROR);
528         else if (childpid > 0)
529                 exit (0);
530         setsid ();
531         umask (007);
532         printf("InspIRCd Process ID: \033[1;32m%lu\033[0m\n",(unsigned long)getpid());
533
534         setpriority(PRIO_PROCESS,(int)getpid(),15);
535
536         if (Config->unlimitcore)
537         {
538                 rlimit rl;
539                 if (getrlimit(RLIMIT_CORE, &rl) == -1)
540                 {
541                         log(DEFAULT,"Failed to getrlimit()!");
542                         return(FALSE);
543                 }
544                 else
545                 {
546                         rl.rlim_cur = rl.rlim_max;
547                         if (setrlimit(RLIMIT_CORE, &rl) == -1)
548                                 log(DEFAULT,"setrlimit() failed, cannot increase coredump size.");
549                 }
550         }
551   
552         return (TRUE);
553 }
554
555
556 /* Make Sure Modules Are Avaliable!
557  * (BugFix By Craig.. See? I do work! :p)
558  * Modified by brain, requires const char*
559  * to work with other API functions
560  */
561
562 bool FileExists (const char* file)
563 {
564         FILE *input;
565         if ((input = fopen (file, "r")) == NULL)
566         {
567                 return(false);
568         }
569         else
570         {
571                 fclose (input);
572                 return(true);
573         }
574 }
575
576 /* ConfProcess does the following things to a config line in the following order:
577  *
578  * Processes the line for syntax errors as shown below
579  *      (1) Line void of quotes or equals (a malformed, illegal tag format)
580  *      (2) Odd number of quotes on the line indicating a missing quote
581  *      (3) number of equals signs not equal to number of quotes / 2 (missing an equals sign)
582  *      (4) Spaces between the opening bracket (<) and the keyword
583  *      (5) Spaces between a keyword and an equals sign
584  *      (6) Spaces between an equals sign and a quote
585  * Removes trailing spaces
586  * Removes leading spaces
587  * Converts tabs to spaces
588  * Turns multiple spaces that are outside of quotes into single spaces
589  */
590
591 std::string ServerConfig::ConfProcess(char* buffer, long linenumber, std::stringstream* errorstream, bool &error, std::string filename)
592 {
593         long number_of_quotes = 0;
594         long number_of_equals = 0;
595         bool has_open_bracket = false;
596         bool in_quotes = false;
597         error = false;
598         if (!buffer)
599         {
600                 return "";
601         }
602         // firstly clean up the line by stripping spaces from the start and end and converting tabs to spaces
603         for (unsigned int d = 0; d < strlen(buffer); d++)
604                 if ((buffer[d]) == 9)
605                         buffer[d] = ' ';
606         while ((buffer[0] == ' ') && (strlen(buffer)>0)) buffer++;
607         while ((buffer[strlen(buffer)-1] == ' ') && (strlen(buffer)>0)) buffer[strlen(buffer)-1] = '\0';
608
609         // empty lines are syntactically valid, as are comments
610         if (!(*buffer) || buffer[0] == '#')
611                 return "";
612
613         for (unsigned int c = 0; c < strlen(buffer); c++)
614         {
615                 // convert all spaces that are OUTSIDE quotes into hardspace (0xA0) as this will make them easier to
616                 // search and replace later :)
617                 if ((!in_quotes) && (buffer[c] == ' '))
618                         buffer[c] = '\xA0';
619                 if ((buffer[c] == '<') && (!in_quotes))
620                 {
621                         has_open_bracket = true;
622                         if (strlen(buffer) == 1)
623                         {
624                                 *errorstream << "Tag without identifier at " << filename << ":" << linenumber << endl;
625                                 error = true;
626                                 return "";
627                         }
628                         else if ((tolower(buffer[c+1]) < 'a') || (tolower(buffer[c+1]) > 'z'))
629                         {
630                                 *errorstream << "Invalid characters in identifier at " << filename << ":" << linenumber << endl;
631                                 error = true;
632                                 return "";
633                         }
634                 }
635                 if (buffer[c] == '"')
636                 {
637                         number_of_quotes++;
638                         in_quotes = (!in_quotes);
639                 }
640                 if ((buffer[c] == '=') && (!in_quotes))
641                 {
642                         number_of_equals++;
643                         if (strlen(buffer) == c)
644                         {
645                                 *errorstream << "Variable without a value at " << filename << ":" << linenumber << endl;
646                                 error = true;
647                                 return "";
648                         }
649                         else if (buffer[c+1] != '"')
650                         {
651                                 *errorstream << "Variable name not followed immediately by its value at " << filename << ":" << linenumber << endl;
652                                 error = true;
653                                 return "";
654                         }
655                         else if (!c)
656                         {
657                                 *errorstream << "Value without a variable (line starts with '=') at " << filename << ":" << linenumber << endl;
658                                 error = true;
659                                 return "";
660                         }
661                         else if (buffer[c-1] == '\xA0')
662                         {
663                                 *errorstream << "Variable name not followed immediately by its value at " << filename << ":" << linenumber << endl;
664                                 error = true;
665                                 return "";
666                         }
667                 }
668         }
669         // no quotes, and no equals. something freaky.
670         if ((!number_of_quotes) || (!number_of_equals) && (strlen(buffer)>2) && (buffer[0]=='<'))
671         {
672                 *errorstream << "Malformed tag at " << filename << ":" << linenumber << endl;
673                 error = true;
674                 return "";
675         }
676         // odd number of quotes. thats just wrong.
677         if ((number_of_quotes % 2) != 0)
678         {
679                 *errorstream << "Missing \" at " << filename << ":" << linenumber << endl;
680                 error = true;
681                 return "";
682         }
683         if (number_of_equals < (number_of_quotes/2))
684         {
685                 *errorstream << "Missing '=' at " << filename << ":" << linenumber << endl;
686         }
687         if (number_of_equals > (number_of_quotes/2))
688         {
689                 *errorstream << "Too many '=' at " << filename << ":" << linenumber << endl;
690         }
691
692         std::string parsedata = buffer;
693         // turn multispace into single space
694         while (parsedata.find("\xA0\xA0") != std::string::npos)
695         {
696                 parsedata.erase(parsedata.find("\xA0\xA0"),1);
697         }
698
699         // turn our hardspace back into softspace
700         for (unsigned int d = 0; d < parsedata.length(); d++)
701         {
702                 if (parsedata[d] == '\xA0')
703                         parsedata[d] = ' ';
704         }
705
706         // and we're done, the line is fine!
707         return parsedata;
708 }
709
710 int ServerConfig::fgets_safe(char* buffer, size_t maxsize, FILE* &file)
711 {
712         char c_read = '\0';
713         unsigned int bufptr = 0;
714         while ((!feof(file)) && (c_read != '\n') && (c_read != '\r') && (bufptr < maxsize))
715         {
716                 c_read = fgetc(file);
717                 if ((c_read != '\n') && (c_read != '\r'))
718                         buffer[bufptr++] = c_read;
719         }
720         buffer[bufptr] = '\0';
721         return bufptr;
722 }
723
724 bool ServerConfig::LoadConf(const char* filename, std::stringstream *target, std::stringstream* errorstream)
725 {
726         target->str("");
727         errorstream->str("");
728         long linenumber = 1;
729         // first, check that the file exists before we try to do anything with it
730         if (!FileExists(filename))
731         {
732                 *errorstream << "File " << filename << " not found." << endl;
733                 return false;
734         }
735         // Fix the chmod of the file to restrict it to the current user and group
736         chmod(filename,0600);
737         for (unsigned int t = 0; t < include_stack.size(); t++)
738         {
739                 if (std::string(filename) == include_stack[t])
740                 {
741                         *errorstream << "File " << filename << " is included recursively (looped inclusion)." << endl;
742                         return false;
743                 }
744         }
745         include_stack.push_back(filename);
746         // now open it
747         FILE* conf = fopen(filename,"r");
748         char buffer[MAXBUF];
749         if (conf)
750         {
751                 while (!feof(conf))
752                 {
753                         if (fgets_safe(buffer, MAXBUF, conf))
754                         {
755                                 if ((!feof(conf)) && (buffer) && (strlen(buffer)))
756                                 {
757                                         if ((buffer[0] != '#') && (buffer[0] != '\r')  && (buffer[0] != '\n'))
758                                         {
759                                                 if (!strncmp(buffer,"<include file=\"",15))
760                                                 {
761                                                         char* buf = buffer;
762                                                         char confpath[10240],newconf[10240];
763                                                         // include file directive
764                                                         buf += 15;      // advance to filename
765                                                         for (unsigned int j = 0; j < strlen(buf); j++)
766                                                         {
767                                                                 if (buf[j] == '\\')
768                                                                         buf[j] = '/';
769                                                                 if (buf[j] == '"')
770                                                                 {
771                                                                         buf[j] = '\0';
772                                                                         break;
773                                                                 }
774                                                         }
775                                                         log(DEFAULT,"Opening included file '%s'",buf);
776                                                         if (*buf != '/')
777                                                         {
778                                                                 strlcpy(confpath,CONFIG_FILE,10240);
779                                                                 if (strstr(confpath,"/inspircd.conf"))
780                                                                 {
781                                                                         // leaves us with just the path
782                                                                         *(strstr(confpath,"/inspircd.conf")) = '\0';
783                                                                 }
784                                                                 snprintf(newconf,10240,"%s/%s",confpath,buf);
785                                                         }
786                                                         else strlcpy(newconf,buf,10240);
787                                                         std::stringstream merge(stringstream::in | stringstream::out);
788                                                         // recursively call LoadConf and get the new data, use the same errorstream
789                                                         if (LoadConf(newconf, &merge, errorstream))
790                                                         {
791                                                                 // append to the end of the file
792                                                                 std::string newstuff = merge.str();
793                                                                 *target << newstuff;
794                                                         }
795                                                         else
796                                                         {
797                                                                 // the error propogates up to its parent recursively
798                                                                 // causing the config reader to bail at the top level.
799                                                                 fclose(conf);
800                                                                 return false;
801                                                         }
802                                                 }
803                                                 else
804                                                 {
805                                                         bool error = false;
806                                                         std::string data = this->ConfProcess(buffer,linenumber++,errorstream,error,filename);
807                                                         if (error)
808                                                         {
809                                                                 return false;
810                                                         }
811                                                         *target << data;
812                                                 }
813                                         }
814                                         else linenumber++;
815                                 }
816                         }
817                 }
818                 fclose(conf);
819         }
820         target->seekg(0);
821         return true;
822 }
823
824 /* Counts the number of tags of a certain type within the config file, e.g. to enumerate opers */
825
826 int ServerConfig::EnumConf(std::stringstream *config, const char* tag)
827 {
828         int ptr = 0;
829         char buffer[MAXBUF], c_tag[MAXBUF], c, lastc;
830         int in_token, in_quotes, tptr, idx = 0;
831
832         const char* buf = config->str().c_str();
833         long bptr = 0;
834         long len = strlen(buf);
835         
836         ptr = 0;
837         in_token = 0;
838         in_quotes = 0;
839         lastc = '\0';
840         while (bptr<len)
841         {
842                 lastc = c;
843                 c = buf[bptr++];
844                 if ((c == '#') && (lastc == '\n'))
845                 {
846                         while ((c != '\n') && (bptr<len))
847                         {
848                                 lastc = c;
849                                 c = buf[bptr++];
850                         }
851                 }
852                 if ((c == '<') && (!in_quotes))
853                 {
854                         tptr = 0;
855                         in_token = 1;
856                         do {
857                                 c = buf[bptr++];
858                                 if (c != ' ')
859                                 {
860                                         c_tag[tptr++] = c;
861                                         c_tag[tptr] = '\0';
862                                 }
863                         } while (c != ' ');
864                 }
865                 if (c == '"')
866                 {
867                         in_quotes = (!in_quotes);
868                 }
869                 if ((c == '>') && (!in_quotes))
870                 {
871                         in_token = 0;
872                         if (!strcmp(c_tag,tag))
873                         {
874                                 /* correct tag, but wrong index */
875                                 idx++;
876                         }
877                         c_tag[0] = '\0';
878                         buffer[0] = '\0';
879                         ptr = 0;
880                         tptr = 0;
881                 }
882                 if (c != '>')
883                 {
884                         if ((in_token) && (c != '\n') && (c != '\r'))
885                         {
886                                 buffer[ptr++] = c;
887                                 buffer[ptr] = '\0';
888                         }
889                 }
890         }
891         return idx;
892 }
893
894 /* Counts the number of values within a certain tag */
895
896 int ServerConfig::EnumValues(std::stringstream *config, const char* tag, int index)
897 {
898         int ptr = 0;
899         char buffer[MAXBUF], c_tag[MAXBUF], c, lastc;
900         int in_token, in_quotes, tptr, idx = 0;
901         
902         bool correct_tag = false;
903         int num_items = 0;
904
905         const char* buf = config->str().c_str();
906         long bptr = 0;
907         long len = strlen(buf);
908         
909         ptr = 0;
910         in_token = 0;
911         in_quotes = 0;
912         lastc = '\0';
913         while (bptr<len)
914         {
915                 lastc = c;
916                 c = buf[bptr++];
917                 if ((c == '#') && (lastc == '\n'))
918                 {
919                         while ((c != '\n') && (bptr<len))
920                         {
921                                 lastc = c;
922                                 c = buf[bptr++];
923                         }
924                 }
925                 if ((c == '<') && (!in_quotes))
926                 {
927                         tptr = 0;
928                         in_token = 1;
929                         do {
930                                 c = buf[bptr++];
931                                 if (c != ' ')
932                                 {
933                                         c_tag[tptr++] = c;
934                                         c_tag[tptr] = '\0';
935                                         
936                                         if ((!strcmp(c_tag,tag)) && (idx == index))
937                                         {
938                                                 correct_tag = true;
939                                         }
940                                 }
941                         } while (c != ' ');
942                 }
943                 if (c == '"')
944                 {
945                         in_quotes = (!in_quotes);
946                 }
947                 
948                 if ( (correct_tag) && (!in_quotes) && ( (c == ' ') || (c == '\n') || (c == '\r') ) )
949                 {
950                         num_items++;
951                 }
952                 if ((c == '>') && (!in_quotes))
953                 {
954                         in_token = 0;
955                         if (correct_tag)
956                                 correct_tag = false;
957                         if (!strcmp(c_tag,tag))
958                         {
959                                 /* correct tag, but wrong index */
960                                 idx++;
961                         }
962                         c_tag[0] = '\0';
963                         buffer[0] = '\0';
964                         ptr = 0;
965                         tptr = 0;
966                 }
967                 if (c != '>')
968                 {
969                         if ((in_token) && (c != '\n') && (c != '\r'))
970                         {
971                                 buffer[ptr++] = c;
972                                 buffer[ptr] = '\0';
973                         }
974                 }
975         }
976         return num_items+1;
977 }
978
979
980
981 int ServerConfig::ConfValueEnum(char* tag, std::stringstream* config)
982 {
983         return EnumConf(config,tag);
984 }
985
986
987
988 /* Retrieves a value from the config file. If there is more than one value of the specified
989  * key and section (e.g. for opers etc) then the index value specifies which to retreive, e.g.
990  *
991  * ConfValue("oper","name",2,result);
992  */
993
994 int ServerConfig::ReadConf(std::stringstream *config, const char* tag, const char* var, int index, char *result)
995 {
996         int ptr = 0;
997         char buffer[65535], c_tag[MAXBUF], c, lastc;
998         int in_token, in_quotes, tptr, idx = 0;
999         char* key;
1000
1001         const char* buf = config->str().c_str();
1002         long bptr = 0;
1003         long len = config->str().length();
1004         
1005         ptr = 0;
1006         in_token = 0;
1007         in_quotes = 0;
1008         lastc = '\0';
1009         c_tag[0] = '\0';
1010         buffer[0] = '\0';
1011         while (bptr<len)
1012         {
1013                 lastc = c;
1014                 c = buf[bptr++];
1015                 // FIX: Treat tabs as spaces
1016                 if (c == 9)
1017                         c = 32;
1018                 if ((c == '<') && (!in_quotes))
1019                 {
1020                         tptr = 0;
1021                         in_token = 1;
1022                         do {
1023                                 c = buf[bptr++];
1024                                 if (c != ' ')
1025                                 {
1026                                         c_tag[tptr++] = c;
1027                                         c_tag[tptr] = '\0';
1028                                 }
1029                         // FIX: Tab can follow a tagname as well as space.
1030                         } while ((c != ' ') && (c != 9));
1031                 }
1032                 if (c == '"')
1033                 {
1034                         in_quotes = (!in_quotes);
1035                 }
1036                 if ((c == '>') && (!in_quotes))
1037                 {
1038                         in_token = 0;
1039                         if (idx == index)
1040                         {
1041                                 if (!strcmp(c_tag,tag))
1042                                 {
1043                                         if ((buffer) && (c_tag) && (var))
1044                                         {
1045                                                 key = strstr(buffer,var);
1046                                                 if (!key)
1047                                                 {
1048                                                         /* value not found in tag */
1049                                                         *result = 0;
1050                                                         return 0;
1051                                                 }
1052                                                 else
1053                                                 {
1054                                                         key+=strlen(var);
1055                                                         while (*key !='"')
1056                                                         {
1057                                                                 if (!*key)
1058                                                                 {
1059                                                                         /* missing quote */
1060                                                                         *result = 0;
1061                                                                         return 0;
1062                                                                 }
1063                                                                 key++;
1064                                                         }
1065                                                         key++;
1066                                                         for (unsigned j = 0; j < strlen(key); j++)
1067                                                         {
1068                                                                 if (key[j] == '"')
1069                                                                 {
1070                                                                         key[j] = '\0';
1071                                                                 }
1072                                                         }
1073                                                         strlcpy(result,key,MAXBUF);
1074                                                         return 1;
1075                                                 }
1076                                         }
1077                                 }
1078                         }
1079                         if (!strcmp(c_tag,tag))
1080                         {
1081                                 /* correct tag, but wrong index */
1082                                 idx++;
1083                         }
1084                         c_tag[0] = '\0';
1085                         buffer[0] = '\0';
1086                         ptr = 0;
1087                         tptr = 0;
1088                 }
1089                 if (c != '>')
1090                 {
1091                         if ((in_token) && (c != '\n') && (c != '\r'))
1092                         {
1093                                 buffer[ptr++] = c;
1094                                 buffer[ptr] = '\0';
1095                         }
1096                 }
1097         }
1098         *result = 0; // value or its tag not found at all
1099         return 0;
1100 }
1101
1102
1103
1104 int ServerConfig::ConfValue(char* tag, char* var, int index, char *result,std::stringstream *config)
1105 {
1106         ReadConf(config, tag, var, index, result);
1107         return 0;
1108 }
1109
1110
1111
1112 // This will bind a socket to a port. It works for UDP/TCP
1113 int BindSocket (int sockfd, struct sockaddr_in client, struct sockaddr_in server, int port, char* addr)
1114 {
1115         memset((char *)&server,0,sizeof(server));
1116         struct in_addr addy;
1117         inet_aton(addr,&addy);
1118         server.sin_family = AF_INET;
1119         if (!strcmp(addr,""))
1120         {
1121                 server.sin_addr.s_addr = htonl(INADDR_ANY);
1122         }
1123         else
1124         {
1125                 server.sin_addr = addy;
1126         }
1127         server.sin_port = htons(port);
1128         if (bind(sockfd,(struct sockaddr*)&server,sizeof(server))<0)
1129         {
1130                 return(ERROR);
1131         }
1132         else
1133         {
1134                 listen(sockfd, Config->MaxConn);
1135                 return(TRUE);
1136         }
1137 }
1138
1139
1140 // Open a TCP Socket
1141 int OpenTCPSocket (void)
1142 {
1143         int sockfd;
1144         int on = 1;
1145         struct linger linger = { 0 };
1146   
1147         if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
1148                 return (ERROR);
1149         else
1150         {
1151                 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
1152                 /* This is BSD compatible, setting l_onoff to 0 is *NOT* http://web.irc.org/mla/ircd-dev/msg02259.html */
1153                 linger.l_onoff = 1;
1154                 linger.l_linger = 1;
1155                 setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (const char*)&linger,sizeof(linger));
1156                 return (sockfd);
1157         }
1158 }
1159
1160 int BindPorts()
1161 {
1162         char configToken[MAXBUF], Addr[MAXBUF], Type[MAXBUF];
1163         sockaddr_in client,server;
1164         int clientportcount = 0;
1165         int BoundPortCount = 0;
1166         for (int count = 0; count < Config->ConfValueEnum("bind",&Config->config_f); count++)
1167         {
1168                 Config->ConfValue("bind","port",count,configToken,&Config->config_f);
1169                 Config->ConfValue("bind","address",count,Addr,&Config->config_f);
1170                 Config->ConfValue("bind","type",count,Type,&Config->config_f);
1171                 if ((!*Type) || (!strcmp(Type,"clients")))
1172                 {
1173                         // modules handle server bind types now,
1174                         // its not a typo in the strcmp.
1175                         Config->ports[clientportcount] = atoi(configToken);
1176                         strlcpy(Config->addrs[clientportcount],Addr,256);
1177                         clientportcount++;
1178                         log(DEBUG,"InspIRCd: startup: read binding %s:%s [%s] from config",Addr,configToken, Type);
1179                 }
1180         }
1181         int PortCount = clientportcount;
1182
1183         for (int count = 0; count < PortCount; count++)
1184         {
1185                 if ((openSockfd[BoundPortCount] = OpenTCPSocket()) == ERROR)
1186                 {
1187                         log(DEBUG,"InspIRCd: startup: bad fd %lu",(unsigned long)openSockfd[BoundPortCount]);
1188                         return(ERROR);
1189                 }
1190                 if (BindSocket(openSockfd[BoundPortCount],client,server,Config->ports[count],Config->addrs[count]) == ERROR)
1191                 {
1192                         log(DEFAULT,"InspIRCd: startup: failed to bind port %lu",(unsigned long)Config->ports[count]);
1193                 }
1194                 else    /* well we at least bound to one socket so we'll continue */
1195                 {
1196                         BoundPortCount++;
1197                 }
1198         }
1199
1200         /* if we didn't bind to anything then abort */
1201         if (!BoundPortCount)
1202         {
1203                 log(DEFAULT,"InspIRCd: startup: no ports bound, bailing!");
1204                 printf("\nERROR: Was not able to bind any of %lu ports! Please check your configuration.\n\n", (unsigned long)PortCount);
1205                 return (ERROR);
1206         }
1207
1208         return BoundPortCount;
1209 }
1210