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