]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/helperfuncs.cpp
Much faster hash<string> for case-insensitive hashing, combined copy and lowercase...
[user/henk/code/inspircd.git] / src / helperfuncs.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd is copyright (C) 2002-2006 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 <stdarg.h>
18 #include "inspircd_config.h"
19 #include "configreader.h"
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <sys/errno.h>
23 #include <signal.h>
24 #include <time.h>
25 #include <string>
26 #include <sstream>
27 #ifdef HAS_EXECINFO
28 #include <execinfo.h>
29 #endif
30 #include "connection.h"
31 #include "users.h"
32 #include "ctables.h"
33 #include "globals.h"
34 #include "modules.h"
35 #include "dynamic.h"
36 #include "wildcard.h"
37 #include "mode.h"
38 #include "xline.h"
39 #include "commands.h"
40 #include "inspstring.h"
41 #include "helperfuncs.h"
42 #include "hashcomp.h"
43 #include "typedefs.h"
44 #include "inspircd.h"
45
46 extern int MODCOUNT;
47 extern ModuleList modules;
48 extern InspIRCd* ServerInstance;
49 extern time_t TIME;
50 extern char lowermap[255];
51 extern std::vector<userrec*> all_opers;
52
53 char LOG_FILE[MAXBUF];
54
55 static char TIMESTR[26];
56 static time_t LAST = 0;
57
58 /** Log()
59  *  Write a line of text `text' to the logfile (and stdout, if in nofork) if the level `level'
60  *  is greater than the configured loglevel.
61  */
62 void InspIRCd::Log(int level, const char* text, ...)
63 {
64         va_list argsPtr;
65         char textbuffer[MAXBUF];
66
67         va_start(argsPtr, text);
68         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
69         va_end(argsPtr);
70
71         InspIRCd::Log(level, std::string(textbuffer));
72 }
73
74 void InspIRCd::Log(int level, const std::string &text)
75 {
76         if (!ServerInstance || !ServerInstance->Config)
77                 return;
78
79         /* If we were given -debug we output all messages, regardless of configured loglevel */
80         if ((level < ServerInstance->Config->LogLevel) && !ServerInstance->Config->forcedebug)
81                 return;
82
83         if (TIME != LAST)
84         {
85                 struct tm *timeinfo = localtime(&TIME);
86
87                 strlcpy(TIMESTR,asctime(timeinfo),26);
88                 TIMESTR[24] = ':';
89                 LAST = TIME;
90         }
91
92         if (ServerInstance->Config->log_file && ServerInstance->Config->writelog)
93         {
94                 fprintf(ServerInstance->Config->log_file,"%s %s\n",TIMESTR,text.c_str());
95                 fflush(ServerInstance->Config->log_file);
96         }
97
98         if (ServerInstance->Config->nofork)
99         {
100                 printf("%s %s\n", TIMESTR, text.c_str());
101         }
102 }
103
104 std::string InspIRCd::GetServerDescription(const char* servername)
105 {
106         std::string description = "";
107
108         FOREACH_MOD(I_OnGetServerDescription,OnGetServerDescription(servername,description));
109
110         if (description != "")
111         {
112                 return description;
113         }
114         else
115         {
116                 // not a remote server that can be found, it must be me.
117                 return Config->ServerDesc;
118         }
119 }
120
121 /* XXX - We don't use WriteMode for this because WriteMode is very slow and
122  * this isnt. Basically WriteMode has to iterate ALL the users 'n' times for
123  * the number of modes provided, e.g. if you send WriteMode 'og' to write to
124  * opers with globops, and you have 2000 users, thats 4000 iterations. WriteOpers
125  * uses the oper list, which means if you have 2000 users but only 5 opers,
126  * it iterates 5 times.
127  */
128 void InspIRCd::WriteOpers(const char* text, ...)
129 {
130         char textbuffer[MAXBUF];
131         va_list argsPtr;
132
133         va_start(argsPtr, text);
134         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
135         va_end(argsPtr);
136
137         this->WriteOpers(std::string(textbuffer));
138 }
139
140 void InspIRCd::WriteOpers(const std::string &text)
141 {
142         for (std::vector<userrec*>::iterator i = all_opers.begin(); i != all_opers.end(); i++)
143         {
144                 userrec* a = *i;
145                 if (IS_LOCAL(a) && a->modes[UM_SERVERNOTICE])
146                 {
147                         // send server notices to all with +s
148                         a->WriteServ("NOTICE %s :%s",a->nick,text.c_str());
149                 }
150         }
151 }
152
153 void InspIRCd::ServerNoticeAll(char* text, ...)
154 {
155         if (!text)
156                 return;
157
158         char textbuffer[MAXBUF];
159         char formatbuffer[MAXBUF];
160         va_list argsPtr;
161         va_start (argsPtr, text);
162         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
163         va_end(argsPtr);
164
165         snprintf(formatbuffer,MAXBUF,"NOTICE $%s :%s",ServerInstance->Config->ServerName,textbuffer);
166
167         for (std::vector<userrec*>::const_iterator i = local_users.begin(); i != local_users.end(); i++)
168         {
169                 userrec* t = *i;
170                 t->WriteServ(std::string(formatbuffer));
171         }
172 }
173
174 void InspIRCd::ServerPrivmsgAll(char* text, ...)
175 {
176         if (!text)
177                 return;
178
179         char textbuffer[MAXBUF];
180         char formatbuffer[MAXBUF];
181         va_list argsPtr;
182         va_start (argsPtr, text);
183         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
184         va_end(argsPtr);
185
186         snprintf(formatbuffer,MAXBUF,"PRIVMSG $%s :%s",ServerInstance->Config->ServerName,textbuffer);
187
188         for (std::vector<userrec*>::const_iterator i = local_users.begin(); i != local_users.end(); i++)
189         {
190                 userrec* t = *i;
191                 t->WriteServ(std::string(formatbuffer));
192         }
193 }
194
195 void InspIRCd::WriteMode(const char* modes, int flags, const char* text, ...)
196 {
197         char textbuffer[MAXBUF];
198         int modelen;
199         va_list argsPtr;
200
201         if ((!text) || (!modes) || (!flags))
202         {
203                 log(DEFAULT,"*** BUG *** WriteMode was given an invalid parameter");
204                 return;
205         }
206
207         va_start(argsPtr, text);
208         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
209         va_end(argsPtr);
210         modelen = strlen(modes);
211
212         for (std::vector<userrec*>::const_iterator i = local_users.begin(); i != local_users.end(); i++)
213         {
214                 userrec* t = (userrec*)(*i);
215                 bool send_to_user = false;
216
217                 if (flags == WM_AND)
218                 {
219                         send_to_user = true;
220
221                         for (int n = 0; n < modelen; n++)
222                         {
223                                 if (!t->modes[modes[n]-65])
224                                 {
225                                         send_to_user = false;
226                                         break;
227                                 }
228                         }
229                 }
230                 else if (flags == WM_OR)
231                 {
232                         send_to_user = false;
233
234                         for (int n = 0; n < modelen; n++)
235                         {
236                                 if (t->modes[modes[n]-65])
237                                 {
238                                         send_to_user = true;
239                                         break;
240                                 }
241                         }
242                 }
243
244                 if (send_to_user)
245                 {
246                         t->WriteServ("NOTICE %s :%s",t->nick,textbuffer);
247                 }
248         }
249 }
250
251 /* Find a user record by nickname and return a pointer to it */
252
253 userrec* InspIRCd::FindNick(const std::string &nick)
254 {
255         user_hash::iterator iter = clientlist.find(nick);
256
257         if (iter == clientlist.end())
258                 /* Couldn't find it */
259                 return NULL;
260
261         return iter->second;
262 }
263
264 userrec* InspIRCd::FindNick(const char* nick)
265 {
266         user_hash::iterator iter;
267
268         if (!nick)
269                 return NULL;
270
271         iter = clientlist.find(nick);
272         
273         if (iter == clientlist.end())
274                 return NULL;
275
276         return iter->second;
277 }
278
279 /* find a channel record by channel name and return a pointer to it */
280
281 chanrec* InspIRCd::FindChan(const char* chan)
282 {
283         chan_hash::iterator iter;
284
285         if (!chan)
286                 return NULL;
287
288         iter = chanlist.find(chan);
289
290         if (iter == chanlist.end())
291                 /* Couldn't find it */
292                 return NULL;
293
294         return iter->second;
295 }
296
297 chanrec* InspIRCd::FindChan(const std::string &chan)
298 {
299         chan_hash::iterator iter = chanlist.find(chan);
300
301         if (iter == chanlist.end())
302                 /* Couldn't find it */
303                 return NULL;
304
305         return iter->second;
306 }
307
308
309 /*
310  * sends out an error notice to all connected clients (not to be used
311  * lightly!)
312  */
313 void InspIRCd::SendError(const char *s)
314 {
315         for (std::vector<userrec*>::const_iterator i = this->local_users.begin(); i != this->local_users.end(); i++)
316         {
317                 userrec* t = (userrec*)(*i);
318                 if (t->registered == REG_ALL)
319                 {
320                         t->WriteServ("NOTICE %s :%s",t->nick,s);
321                 }
322                 else
323                 {
324                         // fix - unregistered connections receive ERROR, not NOTICE
325                         t->Write("ERROR :%s",s);
326                 }
327         }
328 }
329
330 void Error(int status)
331 {
332         void *array[300];
333         size_t size;
334         char **strings;
335
336         signal(SIGALRM, SIG_IGN);
337         signal(SIGPIPE, SIG_IGN);
338         signal(SIGTERM, SIG_IGN);
339         signal(SIGABRT, SIG_IGN);
340         signal(SIGSEGV, SIG_IGN);
341         signal(SIGURG, SIG_IGN);
342         signal(SIGKILL, SIG_IGN);
343         log(DEFAULT,"*** fell down a pothole in the road to perfection ***");
344 #ifdef HAS_EXECINFO
345         log(DEFAULT,"Please report the backtrace lines shown below with any bugreport to the bugtracker at http://www.inspircd.org/bugtrack/");
346         size = backtrace(array, 30);
347         strings = backtrace_symbols(array, size);
348         for (size_t i = 0; i < size; i++) {
349                 log(DEFAULT,"[%d] %s", i, strings[i]);
350         }
351         free(strings);
352         ServerInstance->WriteOpers("*** SIGSEGV: Please see the ircd.log for backtrace and report the error to http://www.inspircd.org/bugtrack/");
353 #else
354         log(DEFAULT,"You do not have execinfo.h so i could not backtrace -- on FreeBSD, please install the libexecinfo port.");
355 #endif
356         ServerInstance->SendError("Somebody screwed up... Whoops. IRC Server terminating.");
357         signal(SIGSEGV, SIG_DFL);
358         if (raise(SIGSEGV) == -1)
359         {
360                 log(DEFAULT,"What the hell, i couldnt re-raise SIGSEGV! Error: %s",strerror(errno));
361         }
362         Exit(status);
363 }
364
365 // this function counts all users connected, wether they are registered or NOT.
366 int InspIRCd::usercnt()
367 {
368         return clientlist.size();
369 }
370
371 // this counts only registered users, so that the percentages in /MAP don't mess up when users are sitting in an unregistered state
372 int InspIRCd::registered_usercount()
373 {
374         int c = 0;
375
376         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
377         {
378                 c += (i->second->registered == REG_ALL);
379         }
380
381         return c;
382 }
383
384 int InspIRCd::usercount_invisible()
385 {
386         int c = 0;
387
388         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
389         {
390                 c += ((i->second->registered == REG_ALL) && (i->second->modes[UM_INVISIBLE]));
391         }
392
393         return c;
394 }
395
396 int InspIRCd::usercount_opers()
397 {
398         int c = 0;
399
400         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
401         {
402                 if (*(i->second->oper))
403                         c++;
404         }
405         return c;
406 }
407
408 int InspIRCd::usercount_unknown()
409 {
410         int c = 0;
411
412         for (std::vector<userrec*>::const_iterator i = local_users.begin(); i != local_users.end(); i++)
413         {
414                 userrec* t = (userrec*)(*i);
415                 if (t->registered != REG_ALL)
416                         c++;
417         }
418
419         return c;
420 }
421
422 long InspIRCd::chancount()
423 {
424         return chanlist.size();
425 }
426
427 long InspIRCd::local_count()
428 {
429         int c = 0;
430
431         for (std::vector<userrec*>::const_iterator i = local_users.begin(); i != local_users.end(); i++)
432         {
433                 userrec* t = (userrec*)(*i);
434                 if (t->registered == REG_ALL)
435                         c++;
436         }
437
438         return c;
439 }
440
441 void ShowMOTD(userrec *user)
442 {
443         if (!ServerInstance->Config->MOTD.size())
444         {
445                 user->WriteServ("422 %s :Message of the day file is missing.",user->nick);
446                 return;
447         }
448         user->WriteServ("375 %s :%s message of the day", user->nick, ServerInstance->Config->ServerName);
449
450         for (unsigned int i = 0; i < ServerInstance->Config->MOTD.size(); i++)
451                 user->WriteServ("372 %s :- %s",user->nick,ServerInstance->Config->MOTD[i].c_str());
452
453         user->WriteServ("376 %s :End of message of the day.", user->nick);
454 }
455
456 void ShowRULES(userrec *user)
457 {
458         if (!ServerInstance->Config->RULES.size())
459         {
460                 user->WriteServ("NOTICE %s :Rules file is missing.",user->nick);
461                 return;
462         }
463         user->WriteServ("NOTICE %s :%s rules",user->nick,ServerInstance->Config->ServerName);
464
465         for (unsigned int i = 0; i < ServerInstance->Config->RULES.size(); i++)
466                 user->WriteServ("NOTICE %s :%s",user->nick,ServerInstance->Config->RULES[i].c_str());
467
468         user->WriteServ("NOTICE %s :End of %s rules.",user->nick,ServerInstance->Config->ServerName);
469 }
470
471 bool IsValidChannelName(const char *chname)
472 {
473         char *c;
474
475         /* check for no name - don't check for !*chname, as if it is empty, it won't be '#'! */
476         if (!chname || *chname != '#')
477         {
478                 return false;
479         }
480
481         c = (char *)chname + 1;
482         while (*c)
483         {
484                 switch (*c)
485                 {
486                         case ' ':
487                         case ',':
488                         case 7:
489                                 return false;
490                 }
491
492                 c++;
493         }
494                 
495         /* too long a name - note funky pointer arithmetic here. */
496         if ((c - chname) > CHANMAX)
497         {
498                         return false;
499         }
500
501         return true;
502 }
503
504 void OpenLog(char** argv, int argc)
505 {
506         if (!*LOG_FILE)
507         {
508                 if (ServerInstance->Config->logpath == "")
509                 {
510                         ServerInstance->Config->logpath = ServerConfig::GetFullProgDir(argv,argc) + "/ircd.log";
511                 }
512         }
513         else
514         {
515                 ServerInstance->Config->log_file = fopen(LOG_FILE,"a+");
516
517                 if (!ServerInstance->Config->log_file)
518                 {
519                         printf("ERROR: Could not write to logfile %s, bailing!\n\n",ServerInstance->Config->logpath.c_str());
520                         Exit(ERROR);
521                 }
522                 
523                 return;
524         }
525
526         ServerInstance->Config->log_file = fopen(ServerInstance->Config->logpath.c_str(),"a+");
527
528         if (!ServerInstance->Config->log_file)
529         {
530                 printf("ERROR: Could not write to logfile %s, bailing!\n\n",ServerInstance->Config->logpath.c_str());
531                 Exit(ERROR);
532         }
533 }
534
535 void CheckRoot()
536 {
537         if (geteuid() == 0)
538         {
539                 printf("WARNING!!! You are running an irc server as ROOT!!! DO NOT DO THIS!!!\n\n");
540                 log(DEFAULT,"InspIRCd: startup: not starting with UID 0!");
541                 Exit(ERROR);
542         }
543 }
544
545 void CheckDie()
546 {
547         if (*ServerInstance->Config->DieValue)
548         {
549                 printf("WARNING: %s\n\n",ServerInstance->Config->DieValue);
550                 log(DEFAULT,"Uh-Oh, somebody didn't read their config file: '%s'",ServerInstance->Config->DieValue);
551                 Exit(ERROR);
552         }
553 }
554
555 /* We must load the modules AFTER initializing the socket engine, now */
556 void LoadAllModules(InspIRCd* ServerInstance)
557 {
558         char configToken[MAXBUF];
559         ServerInstance->Config->module_names.clear();
560         MODCOUNT = -1;
561
562         for (int count = 0; count < ServerInstance->Config->ConfValueEnum(ServerInstance->Config->config_data, "module"); count++)
563         {
564                 ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "module","name",count,configToken,MAXBUF);
565                 printf("[\033[1;32m*\033[0m] Loading module:\t\033[1;32m%s\033[0m\n",configToken);
566                 
567                 if (!ServerInstance->LoadModule(configToken))           
568                 {
569                         log(DEFAULT,"Exiting due to a module loader error.");
570                         printf("\nThere was an error loading a module: %s\n\n",ServerInstance->ModuleError());
571                         Exit(0);
572                 }
573         }
574         
575         log(DEFAULT,"Total loaded modules: %lu",(unsigned long)MODCOUNT+1);
576 }