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