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