]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/helperfuncs.cpp
Move strlower into nspace namespace where hash<std::string> lives
[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 // this returns 1 when all modules are satisfied that the user should be allowed onto the irc server
472 // (until this returns true, a user will block in the waiting state, waiting to connect up to the
473 // registration timeout maximum seconds)
474 bool AllModulesReportReady(userrec* user)
475 {
476         if (!ServerInstance->Config->global_implementation[I_OnCheckReady])
477                 return true;
478
479         for (int i = 0; i <= MODCOUNT; i++)
480         {
481                 if (ServerInstance->Config->implement_lists[i][I_OnCheckReady])
482                 {
483                         int res = modules[i]->OnCheckReady(user);
484                         if (!res)
485                                 return false;
486                 }
487         }
488
489         return true;
490 }
491
492 bool IsValidChannelName(const char *chname)
493 {
494         char *c;
495
496         /* check for no name - don't check for !*chname, as if it is empty, it won't be '#'! */
497         if (!chname || *chname != '#')
498         {
499                 return false;
500         }
501
502         c = (char *)chname + 1;
503         while (*c)
504         {
505                 switch (*c)
506                 {
507                         case ' ':
508                         case ',':
509                         case 7:
510                                 return false;
511                 }
512
513                 c++;
514         }
515                 
516         /* too long a name - note funky pointer arithmetic here. */
517         if ((c - chname) > CHANMAX)
518         {
519                         return false;
520         }
521
522         return true;
523 }
524
525 void OpenLog(char** argv, int argc)
526 {
527         if (!*LOG_FILE)
528         {
529                 if (ServerInstance->Config->logpath == "")
530                 {
531                         ServerInstance->Config->logpath = ServerConfig::GetFullProgDir(argv,argc) + "/ircd.log";
532                 }
533         }
534         else
535         {
536                 ServerInstance->Config->log_file = fopen(LOG_FILE,"a+");
537
538                 if (!ServerInstance->Config->log_file)
539                 {
540                         printf("ERROR: Could not write to logfile %s, bailing!\n\n",ServerInstance->Config->logpath.c_str());
541                         Exit(ERROR);
542                 }
543                 
544                 return;
545         }
546
547         ServerInstance->Config->log_file = fopen(ServerInstance->Config->logpath.c_str(),"a+");
548
549         if (!ServerInstance->Config->log_file)
550         {
551                 printf("ERROR: Could not write to logfile %s, bailing!\n\n",ServerInstance->Config->logpath.c_str());
552                 Exit(ERROR);
553         }
554 }
555
556 void CheckRoot()
557 {
558         if (geteuid() == 0)
559         {
560                 printf("WARNING!!! You are running an irc server as ROOT!!! DO NOT DO THIS!!!\n\n");
561                 log(DEFAULT,"InspIRCd: startup: not starting with UID 0!");
562                 Exit(ERROR);
563         }
564 }
565
566 void CheckDie()
567 {
568         if (*ServerInstance->Config->DieValue)
569         {
570                 printf("WARNING: %s\n\n",ServerInstance->Config->DieValue);
571                 log(DEFAULT,"Uh-Oh, somebody didn't read their config file: '%s'",ServerInstance->Config->DieValue);
572                 Exit(ERROR);
573         }
574 }
575
576 /* We must load the modules AFTER initializing the socket engine, now */
577 void LoadAllModules(InspIRCd* ServerInstance)
578 {
579         char configToken[MAXBUF];
580         ServerInstance->Config->module_names.clear();
581         MODCOUNT = -1;
582
583         for (int count = 0; count < ServerInstance->Config->ConfValueEnum(ServerInstance->Config->config_data, "module"); count++)
584         {
585                 ServerInstance->Config->ConfValue(ServerInstance->Config->config_data, "module","name",count,configToken,MAXBUF);
586                 printf("[\033[1;32m*\033[0m] Loading module:\t\033[1;32m%s\033[0m\n",configToken);
587                 
588                 if (!ServerInstance->LoadModule(configToken))           
589                 {
590                         log(DEFAULT,"Exiting due to a module loader error.");
591                         printf("\nThere was an error loading a module: %s\n\n",ServerInstance->ModuleError());
592                         Exit(0);
593                 }
594         }
595         
596         log(DEFAULT,"Total loaded modules: %lu",(unsigned long)MODCOUNT+1);
597 }