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