]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/inspircd_io.cpp
Added a lot of config error checking
[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 #include <sys/time.h>
18 #include <sys/resource.h>
19 #include <sys/types.h>
20 #include <string>
21 #include <unistd.h>
22 #include <sstream>
23 #include <iostream>
24 #include <fstream>
25 #include "inspircd.h"
26 #include "inspircd_io.h"
27 #include "inspircd_util.h"
28
29 using namespace std;
30
31 extern FILE *log_file;
32
33 void WriteOpers(char* text, ...);
34
35 void Exit (int status)
36 {
37   if (log_file)
38         fclose(log_file);
39   send_error("Server shutdown.");
40   exit (status);
41 }
42
43 void Killed(int status)
44 {
45   if (log_file)
46         fclose(log_file);
47   send_error("Server terminated.");
48   exit(status);
49 }
50
51 void Rehash(int status)
52 {
53   WriteOpers("Rehashing config file %s due to SIGHUP",CONFIG_FILE);
54   ReadConfig(false,NULL);
55 }
56
57
58
59 void Start (void)
60 {
61   printf("\033[1;37mInspire Internet Relay Chat Server, compiled " __DATE__ " at " __TIME__ "\n");
62   printf("(C) ChatSpike Development team.\033[0;37m\n\n");
63   printf("\033[1;37mDevelopers:\033[0;37m     Brain, FrostyCoolSlug\n");
64   printf("\033[1;37mDocumentation:\033[0;37m  FrostyCoolSlug, w00t\n");
65   printf("\033[1;37mTesters:\033[0;37m        typobox43, piggles, Lord_Zathras, CC\n");
66   printf("\033[1;37mName concept:\033[0;37m   Lord_Zathras\n\n");
67 }
68
69
70 void DeadPipe(int status)
71 {
72   signal (SIGPIPE, DeadPipe);
73 }
74
75 int DaemonSeed (void)
76 {
77   int childpid;
78   signal (SIGALRM, SIG_IGN);
79   signal (SIGHUP, Rehash);
80   signal (SIGPIPE, DeadPipe);
81   signal (SIGTERM, Exit);
82   signal (SIGABRT, Exit);
83   signal (SIGSEGV, Error);
84   signal (SIGURG, Exit);
85   signal (SIGKILL, Exit);
86   if ((childpid = fork ()) < 0)
87     return (ERROR);
88   else if (childpid > 0)
89     exit (0);
90   setsid ();
91   umask (007);
92   /* close stdout, stdin, stderr */
93   close(0);
94   close(1);
95   close(2);
96
97   setpriority(PRIO_PROCESS,(int)getpid(),15); /* ircd sets to low process priority so it doesnt hog the box */
98   
99   return (TRUE);
100 }
101
102
103 /* Make Sure Modules Are Avaliable!
104  * (BugFix By Craig.. See? I do work! :p)
105  * Modified by brain, requires const char*
106  * to work with other API functions
107  */
108
109 bool FileExists (const char* file)
110 {
111   FILE *input;
112   
113   if ((input = fopen (file, "r")) == NULL) { return(false); }
114   else { fclose (input); return(true); }
115 }
116
117 /* ConfProcess does the following things to a config line in the following order:
118  *
119  * Processes the line for syntax errors as shown below
120  *      (1) Line void of quotes or equals (a malformed, illegal tag format)
121  *      (2) Odd number of quotes on the line indicating a missing quote
122  *      (3) number of equals signs not equal to number of quotes / 2 (missing an equals sign)
123  *      (4) Spaces between the opening bracket (<) and the keyword
124  *      (5) Spaces between a keyword and an equals sign
125  *      (6) Spaces between an equals sign and a quote
126  * Removes trailing spaces
127  * Removes leading spaces
128  * Converts tabs to spaces
129  * Turns multiple spaces that are outside of quotes into single spaces
130  */
131
132 std::string ConfProcess(char* buffer, long linenumber, std::stringstream* errorstream, bool &error, std::string filename)
133 {
134         long number_of_quotes = 0;
135         long number_of_equals = 0;
136         bool has_open_bracket = false;
137         bool in_quotes = false;
138         error = false;
139         if (!buffer)
140         {
141                 return "";
142         }
143         // firstly clean up the line by stripping spaces from the start and end
144         while ((buffer[0] == ' ') && (strlen(buffer)>0)) buffer++;
145         while ((buffer[strlen(buffer)-1] == ' ') && (strlen(buffer)>0)) buffer[strlen(buffer)-1] = '\0';
146         // empty lines are syntactically valid
147         if (!strcmp(buffer,""))
148                 return "";
149         else if (buffer[0] == '#')
150                 return "";
151         for (int c = 0; c < strlen(buffer); c++)
152         {
153                 if (buffer[c] == 9)
154                         buffer[c] = ' ';
155                 // convert all spaces that are OUTSIDE quotes into hardspace (0xA0) as this will make them easier to
156                 // search and replace later :)
157                 if ((!in_quotes) && (buffer[c] == ' '))
158                         buffer[c] = '\xA0';
159                 if ((buffer[c] == '<') && (!in_quotes))
160                 {
161                         has_open_bracket = true;
162                         if (strlen(buffer) == 1)
163                         {
164                                 *errorstream << "Tag without identifier at " << filename << ":" << linenumber << endl;
165                                 error = true;
166                                 return "";
167                         }
168                         else if ((tolower(buffer[c+1]) < 'a') || (tolower(buffer[c+1]) > 'z'))
169                         {
170                                 *errorstream << "Invalid characters in identifier at " << filename << ":" << linenumber << endl;
171                                 error = true;
172                                 return "";
173                         }
174                 }
175                 if (buffer[c] == '"')
176                 {
177                         number_of_quotes++;
178                         in_quotes = (!in_quotes);
179                 }
180                 if ((buffer[c] == '=') && (!in_quotes))
181                 {
182                         number_of_equals++;
183                         if (strlen(buffer) == c)
184                         {
185                                 *errorstream << "Variable without a value at " << filename << ":" << linenumber << endl;
186                                 error = true;
187                                 return "";
188                         }
189                         else if (buffer[c+1] != '"')
190                         {
191                                 *errorstream << "Variable name not followed immediately by its value at " << filename << ":" << linenumber << endl;
192                                 error = true;
193                                 return "";
194                         }
195                         else if (!c)
196                         {
197                                 *errorstream << "Value without a variable (line starts with '=') at " << filename << ":" << linenumber << endl;
198                                 error = true;
199                                 return "";
200                         }
201                         else if (buffer[c-1] == '\xA0')
202                         {
203                                 *errorstream << "Variable name not followed immediately by its value at " << filename << ":" << linenumber << endl;
204                                 error = true;
205                                 return "";
206                         }
207                 }
208         }
209         // no quotes, and no equals. something freaky.
210         if ((!number_of_quotes) || (!number_of_equals))
211         {
212                 *errorstream << "Malformed tag at " << filename << ":" << linenumber << endl;
213                 error = true;
214                 return "";
215         }
216         // odd number of quotes. thats just wrong.
217         if ((number_of_quotes % 2) != 0)
218         {
219                 *errorstream << "Missing \" at " << filename << ":" << linenumber << endl;
220                 error = true;
221                 return "";
222         }
223         if (number_of_equals < (number_of_quotes/2))
224         {
225                 *errorstream << "Missing '=' at " << filename << ":" << linenumber << endl;
226         }
227         if (number_of_equals > (number_of_quotes/2))
228         {
229                 *errorstream << "Too many '=' at " << filename << ":" << linenumber << endl;
230         }
231
232         std::string parsedata = buffer;
233         // turn multispace into single space
234         while (parsedata.find("\xA0\xA0") != std::string::npos)
235         {
236                 parsedata.erase(parsedata.find("\xA0\xA0"),1);
237         }
238
239         // turn our hardspace back into softspace
240         for (int d = 0; d < parsedata.length(); d++)
241         {
242                 if (parsedata[d] == '\xA0')
243                         parsedata[d] = ' ';
244         }
245
246         // and we're done, the line is fine!
247         return parsedata;
248 }
249
250 bool LoadConf(const char* filename, std::stringstream *target, std::stringstream* errorstream)
251 {
252         target->str("");
253         errorstream->str("");
254         long linenumber = 1;
255         FILE* conf = fopen(filename,"r");
256         if (!FileExists(filename))
257         {
258                 *errorstream << "File " << filename << " not found." << endl;
259                 return false;
260         }
261         char buffer[MAXBUF];
262         if (conf)
263         {
264                 while (!feof(conf))
265                 {
266                         if (fgets(buffer, MAXBUF, conf))
267                         {
268                                 if ((!feof(conf)) && (buffer) && (strlen(buffer)))
269                                 {
270                                         if ((buffer[0] != '#') && (buffer[0] != '\r')  && (buffer[0] != '\n'))
271                                         {
272                                                 bool error = false;
273                                                 std::string data = ConfProcess(buffer,linenumber++,errorstream,error,filename);
274                                                 if (error)
275                                                 {
276                                                         return false;
277                                                 }
278                                                 *target << data;
279                                         }
280                                         else linenumber++;
281                                 }
282                         }
283                 }
284                 fclose(conf);
285         }
286         target->seekg(0);
287         return true;
288 }
289
290 /* Counts the number of tags of a certain type within the config file, e.g. to enumerate opers */
291
292 int EnumConf(std::stringstream *config, const char* tag)
293 {
294         int ptr = 0;
295         char buffer[MAXBUF], c_tag[MAXBUF], c, lastc;
296         int in_token, in_quotes, tptr, j, idx = 0;
297         char* key;
298
299         const char* buf = config->str().c_str();
300         long bptr = 0;
301         long len = strlen(buf);
302         
303         ptr = 0;
304         in_token = 0;
305         in_quotes = 0;
306         lastc = '\0';
307         while (bptr<len)
308         {
309                 lastc = c;
310                 c = buf[bptr++];
311                 if ((c == '#') && (lastc == '\n'))
312                 {
313                         while ((c != '\n') && (bptr<len))
314                         {
315                                 lastc = c;
316                                 c = buf[bptr++];
317                         }
318                 }
319                 if ((c == '<') && (!in_quotes))
320                 {
321                         tptr = 0;
322                         in_token = 1;
323                         do {
324                                 c = buf[bptr++];
325                                 if (c != ' ')
326                                 {
327                                         c_tag[tptr++] = c;
328                                         c_tag[tptr] = '\0';
329                                 }
330                         } while (c != ' ');
331                 }
332                 if (c == '"')
333                 {
334                         in_quotes = (!in_quotes);
335                 }
336                 if ((c == '>') && (!in_quotes))
337                 {
338                         in_token = 0;
339                         if (!strcmp(c_tag,tag))
340                         {
341                                 /* correct tag, but wrong index */
342                                 idx++;
343                         }
344                         c_tag[0] = '\0';
345                         buffer[0] = '\0';
346                         ptr = 0;
347                         tptr = 0;
348                 }
349                 if (c != '>')
350                 {
351                         if ((in_token) && (c != '\n') && (c != '\r'))
352                         {
353                                 buffer[ptr++] = c;
354                                 buffer[ptr] = '\0';
355                         }
356                 }
357         }
358         return idx;
359 }
360
361 /* Counts the number of values within a certain tag */
362
363 int EnumValues(std::stringstream *config, const char* tag, int index)
364 {
365         int ptr = 0;
366         char buffer[MAXBUF], c_tag[MAXBUF], c, lastc;
367         int in_token, in_quotes, tptr, j, idx = 0;
368         char* key;
369         
370         bool correct_tag = false;
371         int num_items = 0;
372
373         const char* buf = config->str().c_str();
374         long bptr = 0;
375         long len = strlen(buf);
376         
377         ptr = 0;
378         in_token = 0;
379         in_quotes = 0;
380         lastc = '\0';
381         while (bptr<len)
382         {
383                 lastc = c;
384                 c = buf[bptr++];
385                 if ((c == '#') && (lastc == '\n'))
386                 {
387                         while ((c != '\n') && (bptr<len))
388                         {
389                                 lastc = c;
390                                 c = buf[bptr++];
391                         }
392                 }
393                 if ((c == '<') && (!in_quotes))
394                 {
395                         tptr = 0;
396                         in_token = 1;
397                         do {
398                                 c = buf[bptr++];
399                                 if (c != ' ')
400                                 {
401                                         c_tag[tptr++] = c;
402                                         c_tag[tptr] = '\0';
403                                         
404                                         if ((!strcmp(c_tag,tag)) && (idx == index))
405                                         {
406                                                 correct_tag = true;
407                                         }
408                                 }
409                         } while (c != ' ');
410                 }
411                 if (c == '"')
412                 {
413                         in_quotes = (!in_quotes);
414                 }
415                 
416                 if ( (correct_tag) && (!in_quotes) && ( (c == ' ') || (c == '\n') || (c == '\r') ) )
417                 {
418                         num_items++;
419                 }
420                 if ((c == '>') && (!in_quotes))
421                 {
422                         in_token = 0;
423                         if (correct_tag)
424                                 correct_tag = false;
425                         if (!strcmp(c_tag,tag))
426                         {
427                                 /* correct tag, but wrong index */
428                                 idx++;
429                         }
430                         c_tag[0] = '\0';
431                         buffer[0] = '\0';
432                         ptr = 0;
433                         tptr = 0;
434                 }
435                 if (c != '>')
436                 {
437                         if ((in_token) && (c != '\n') && (c != '\r'))
438                         {
439                                 buffer[ptr++] = c;
440                                 buffer[ptr] = '\0';
441                         }
442                 }
443         }
444         return num_items+1;
445 }
446
447
448
449 int ConfValueEnum(char* tag, std::stringstream* config)
450 {
451         return EnumConf(config,tag);
452 }
453
454
455
456 /* Retrieves a value from the config file. If there is more than one value of the specified
457  * key and section (e.g. for opers etc) then the index value specifies which to retreive, e.g.
458  *
459  * ConfValue("oper","name",2,result);
460  */
461
462 int ReadConf(std::stringstream *config, const char* tag, const char* var, int index, char *result)
463 {
464         int ptr = 0;
465         char buffer[65535], c_tag[MAXBUF], c, lastc;
466         int in_token, in_quotes, tptr, j, idx = 0;
467         char* key;
468
469         const char* buf = config->str().c_str();
470         long bptr = 0;
471         long len = strlen(buf);
472         
473         ptr = 0;
474         in_token = 0;
475         in_quotes = 0;
476         lastc = '\0';
477         c_tag[0] = '\0';
478         buffer[0] = '\0';
479         while (bptr<len)
480         {
481                 lastc = c;
482                 c = buf[bptr++];
483                 // FIX: Treat tabs as spaces
484                 if (c == 9)
485                         c = 32;
486                 if ((c == '<') && (!in_quotes))
487                 {
488                         tptr = 0;
489                         in_token = 1;
490                         do {
491                                 c = buf[bptr++];
492                                 if (c != ' ')
493                                 {
494                                         c_tag[tptr++] = c;
495                                         c_tag[tptr] = '\0';
496                                 }
497                         // FIX: Tab can follow a tagname as well as space.
498                         } while ((c != ' ') && (c != 9));
499                 }
500                 if (c == '"')
501                 {
502                         in_quotes = (!in_quotes);
503                 }
504                 if ((c == '>') && (!in_quotes))
505                 {
506                         in_token = 0;
507                         if (idx == index)
508                         {
509                                 if (!strcmp(c_tag,tag))
510                                 {
511                                         if ((buffer) && (c_tag) && (var))
512                                         {
513                                                 key = strstr(buffer,var);
514                                                 if (!key)
515                                                 {
516                                                         /* value not found in tag */
517                                                         strcpy(result,"");
518                                                         return 0;
519                                                 }
520                                                 else
521                                                 {
522                                                         key+=strlen(var);
523                                                         while (key[0] !='"')
524                                                         {
525                                                                 if (!strlen(key))
526                                                                 {
527                                                                         /* missing quote */
528                                                                         strcpy(result,"");
529                                                                         return 0;
530                                                                 }
531                                                                 key++;
532                                                         }
533                                                         key++;
534                                                         for (j = 0; j < strlen(key); j++)
535                                                         {
536                                                                 if (key[j] == '"')
537                                                                 {
538                                                                         key[j] = '\0';
539                                                                 }
540                                                         }
541                                                         strlcpy(result,key,MAXBUF);
542                                                         return 1;
543                                                 }
544                                         }
545                                 }
546                         }
547                         if (!strcmp(c_tag,tag))
548                         {
549                                 /* correct tag, but wrong index */
550                                 idx++;
551                         }
552                         c_tag[0] = '\0';
553                         buffer[0] = '\0';
554                         ptr = 0;
555                         tptr = 0;
556                 }
557                 if (c != '>')
558                 {
559                         if ((in_token) && (c != '\n') && (c != '\r'))
560                         {
561                                 buffer[ptr++] = c;
562                                 buffer[ptr] = '\0';
563                         }
564                 }
565         }
566         strcpy(result,""); // value or its tag not found at all
567         return 0;
568 }
569
570
571
572 int ConfValue(char* tag, char* var, int index, char *result,std::stringstream *config)
573 {
574         ReadConf(config, tag, var, index, result);
575         return 0;
576 }
577
578
579
580 /* This will bind a socket to a port. It works for UDP/TCP */
581 int BindSocket (int sockfd, struct sockaddr_in client, struct sockaddr_in server, int port, char* addr)
582 {
583   bzero((char *)&server,sizeof(server));
584   struct in_addr addy;
585   inet_aton(addr,&addy);
586
587   server.sin_family = AF_INET;
588   if (!strcmp(addr,""))
589   {
590           server.sin_addr.s_addr = htonl(INADDR_ANY);
591   }
592   else
593   {
594           server.sin_addr = addy;
595   }
596
597   server.sin_port = htons(port);
598
599   if (bind(sockfd,(struct sockaddr*)&server,sizeof(server))<0)
600   {
601     return(ERROR);
602   }
603   else
604   {
605     listen(sockfd,5);
606     return(TRUE);
607   }
608 }
609
610
611 /* Open a TCP Socket */
612 int OpenTCPSocket (void)
613 {
614   int sockfd;
615   int on = 0;
616   struct linger linger = { 0 };
617   
618   if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
619     return (ERROR);
620   else
621   {
622     setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
623     /* This is BSD compatible, setting l_onoff to 0 is *NOT* http://web.irc.org/mla/ircd-dev/msg02259.html */
624     linger.l_onoff = 1;
625     linger.l_linger = 0;
626     setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (const char*)&linger,sizeof(linger));
627     return (sockfd);
628   }
629 }
630