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