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