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