]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/helperfuncs.cpp
Pedantic safe
[user/henk/code/inspircd.git] / src / helperfuncs.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2007 InspIRCd Development Team
6  * See: http://www.inspircd.org/wiki/index.php/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 /* $Core: libIRCDhelper */
15
16 #include "inspircd.h"
17 #include <stdarg.h>
18 #include "wildcard.h"
19 #include "xline.h"
20 #include "exitcodes.h"
21
22 static char TIMESTR[26];
23 static time_t LAST = 0;
24
25 /** Log()
26  *  Write a line of text `text' to the logfile (and stdout, if in nofork) if the level `level'
27  *  is greater than the configured loglevel.
28  */
29 void InspIRCd::Log(int level, const char* text, ...)
30 {
31         /* sanity check, just in case */
32         if (!this->Config || !this->Logger)
33                 return;
34
35         /* Do this check again here so that we save pointless vsnprintf calls */
36         if ((level < Config->LogLevel) && !Config->forcedebug)
37                 return;
38
39         va_list argsPtr;
40         char textbuffer[65536];
41
42         va_start(argsPtr, text);
43         vsnprintf(textbuffer, 65536, text, argsPtr);
44         va_end(argsPtr);
45
46         this->Log(level, std::string(textbuffer));
47 }
48
49 void InspIRCd::Log(int level, const std::string &text)
50 {
51         /* sanity check, just in case */
52         if (!this->Config || !this->Logger)
53                 return;
54
55         /* If we were given -debug we output all messages, regardless of configured loglevel */
56         if ((level < Config->LogLevel) && !Config->forcedebug)
57                 return;
58
59         if (Time() != LAST)
60         {
61                 time_t local = Time();
62                 struct tm *timeinfo = localtime(&local);
63
64                 strlcpy(TIMESTR,asctime(timeinfo),26);
65                 TIMESTR[24] = ':';
66                 LAST = Time();
67         }
68
69         if (Config->log_file && Config->writelog)
70         {
71                 std::string out = std::string(TIMESTR) + " " + text.c_str() + "\n";
72                 this->Logger->WriteLogLine(out);
73         }
74
75         if (Config->nofork)
76         {
77                 printf("%s %s\n", TIMESTR, text.c_str());
78         }
79 }
80
81 std::string InspIRCd::GetServerDescription(const char* servername)
82 {
83         std::string description;
84
85         FOREACH_MOD_I(this,I_OnGetServerDescription,OnGetServerDescription(servername,description));
86
87         if (!description.empty())
88         {
89                 return description;
90         }
91         else
92         {
93                 // not a remote server that can be found, it must be me.
94                 return Config->ServerDesc;
95         }
96 }
97
98 /* XXX - We don't use WriteMode for this because WriteMode is very slow and
99  * this isnt. Basically WriteMode has to iterate ALL the users 'n' times for
100  * the number of modes provided, e.g. if you send WriteMode 'og' to write to
101  * opers with globops, and you have 2000 users, thats 4000 iterations. WriteOpers
102  * uses the oper list, which means if you have 2000 users but only 5 opers,
103  * it iterates 5 times.
104  */
105 void InspIRCd::WriteOpers(const char* text, ...)
106 {
107         char textbuffer[MAXBUF];
108         va_list argsPtr;
109
110         va_start(argsPtr, text);
111         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
112         va_end(argsPtr);
113
114         this->WriteOpers(std::string(textbuffer));
115 }
116
117 void InspIRCd::WriteOpers(const std::string &text)
118 {
119         for (std::list<User*>::iterator i = this->all_opers.begin(); i != this->all_opers.end(); i++)
120         {
121                 User* a = *i;
122                 if (IS_LOCAL(a) && a->IsModeSet('s'))
123                 {
124                         // send server notices to all with +s
125                         a->WriteServ("NOTICE %s :%s",a->nick,text.c_str());
126                 }
127         }
128 }
129
130 void InspIRCd::ServerNoticeAll(char* text, ...)
131 {
132         if (!text)
133                 return;
134
135         char textbuffer[MAXBUF];
136         char formatbuffer[MAXBUF];
137         va_list argsPtr;
138         va_start (argsPtr, text);
139         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
140         va_end(argsPtr);
141
142         snprintf(formatbuffer,MAXBUF,"NOTICE $%s :%s",Config->ServerName,textbuffer);
143
144         for (std::vector<User*>::const_iterator i = local_users.begin(); i != local_users.end(); i++)
145         {
146                 User* t = *i;
147                 t->WriteServ(std::string(formatbuffer));
148         }
149 }
150
151 void InspIRCd::ServerPrivmsgAll(char* text, ...)
152 {
153         if (!text)
154                 return;
155
156         char textbuffer[MAXBUF];
157         char formatbuffer[MAXBUF];
158         va_list argsPtr;
159         va_start (argsPtr, text);
160         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
161         va_end(argsPtr);
162
163         snprintf(formatbuffer,MAXBUF,"PRIVMSG $%s :%s",Config->ServerName,textbuffer);
164
165         for (std::vector<User*>::const_iterator i = local_users.begin(); i != local_users.end(); i++)
166         {
167                 User* t = *i;
168                 t->WriteServ(std::string(formatbuffer));
169         }
170 }
171
172 void InspIRCd::WriteMode(const char* modes, int flags, const char* text, ...)
173 {
174         char textbuffer[MAXBUF];
175         int modelen;
176         va_list argsPtr;
177
178         if (!text || !modes || !flags)
179         {
180                 this->Log(DEFAULT,"*** BUG *** WriteMode was given an invalid parameter");
181                 return;
182         }
183
184         va_start(argsPtr, text);
185         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
186         va_end(argsPtr);
187         modelen = strlen(modes);
188
189         if (flags == WM_AND)
190         {
191                 for (std::vector<User*>::const_iterator i = local_users.begin(); i != local_users.end(); i++)
192                 {
193                         User* t = *i;
194                         bool send_to_user = true;
195
196                         for (int n = 0; n < modelen; n++)
197                         {
198                                 if (!t->IsModeSet(modes[n]))
199                                 {
200                                         send_to_user = false;
201                                         break;
202                                 }
203                         }
204                         if (send_to_user)
205                         {
206                                 t->WriteServ("NOTICE %s :%s", t->nick, textbuffer);
207                         }
208                 }
209         }
210         else if (flags == WM_OR)
211         {
212                 for (std::vector<User*>::const_iterator i = local_users.begin(); i != local_users.end(); i++)
213                 {
214                         User* t = *i;
215                         bool send_to_user = false;
216
217                         for (int n = 0; n < modelen; n++)
218                         {
219                                 if (t->IsModeSet(modes[n]))
220                                 {
221                                         send_to_user = true;
222                                         break;
223                                 }
224                         }
225
226                         if (send_to_user)
227                         {
228                                 t->WriteServ("NOTICE %s :%s", t->nick, textbuffer);
229                         }
230                 }
231         }
232 }
233
234 /* Find a user record by nickname and return a pointer to it */
235 User* InspIRCd::FindNick(const std::string &nick)
236 {
237         if (!nick.empty() && isdigit(*nick.begin()))
238                 return FindUUID(nick);
239
240         user_hash::iterator iter = clientlist->find(nick);
241
242         if (iter == clientlist->end())
243                 /* Couldn't find it */
244                 return NULL;
245
246         return iter->second;
247 }
248
249 User* InspIRCd::FindNick(const char* nick)
250 {
251         if (isdigit(*nick))
252                 return FindUUID(nick);
253
254         user_hash::iterator iter = clientlist->find(nick);
255         
256         if (iter == clientlist->end())
257                 return NULL;
258
259         return iter->second;
260 }
261
262 User* InspIRCd::FindNickOnly(const std::string &nick)
263 {
264         user_hash::iterator iter = clientlist->find(nick);
265
266         if (iter == clientlist->end())
267                 return NULL;
268
269         return iter->second;
270 }
271
272 User* InspIRCd::FindNickOnly(const char* nick)
273 {
274         user_hash::iterator iter = clientlist->find(nick);
275
276         if (iter == clientlist->end())
277                 return NULL;
278
279         return iter->second;
280 }
281
282 User *InspIRCd::FindUUID(const std::string &uid)
283 {
284         return FindUUID(uid.c_str());
285 }
286
287 User *InspIRCd::FindUUID(const char *uid)
288 {
289         user_hash::iterator finduuid = uuidlist->find(uid);
290
291         if (finduuid == uuidlist->end())
292                 return NULL;
293
294         return finduuid->second;
295 }
296
297 /* find a channel record by channel name and return a pointer to it */
298 Channel* InspIRCd::FindChan(const char* chan)
299 {
300         chan_hash::iterator iter = chanlist->find(chan);
301
302         if (iter == chanlist->end())
303                 /* Couldn't find it */
304                 return NULL;
305
306         return iter->second;
307 }
308
309 Channel* InspIRCd::FindChan(const std::string &chan)
310 {
311         chan_hash::iterator iter = chanlist->find(chan);
312
313         if (iter == chanlist->end())
314                 /* Couldn't find it */
315                 return NULL;
316
317         return iter->second;
318 }
319
320 /* Send an error notice to all users, registered or not */
321 void InspIRCd::SendError(const std::string &s)
322 {
323         for (std::vector<User*>::const_iterator i = this->local_users.begin(); i != this->local_users.end(); i++)
324         {
325                 if ((*i)->registered == REG_ALL)
326                 {
327                         (*i)->WriteServ("NOTICE %s :%s",(*i)->nick,s.c_str());
328                 }
329                 else
330                 {
331                         /* Unregistered connections receive ERROR, not a NOTICE */
332                         (*i)->Write("ERROR :" + s);
333                 }
334                 /* This might generate a whole load of EAGAIN, but we dont really
335                  * care about this, as if we call SendError something catastrophic
336                  * has occured anyway, and we wont receive the events for these.
337                  */
338                 (*i)->FlushWriteBuf();
339         }
340 }
341
342 /* this function counts all users connected, wether they are registered or NOT. */
343 int InspIRCd::UserCount()
344 {
345         return clientlist->size();
346 }
347
348 /* this counts only registered users, so that the percentages in /MAP don't mess up when users are sitting in an unregistered state */
349 int InspIRCd::RegisteredUserCount()
350 {
351         return clientlist->size() - this->UnregisteredUserCount();
352 }
353
354 /* return how many users have a given mode e.g. 'a' */
355 int InspIRCd::ModeCount(const char mode)
356 {
357         ModeHandler* mh = this->Modes->FindMode(mode, MODETYPE_USER);
358
359         if (mh)
360                 return mh->GetCount();
361         else
362                 return 0;
363 }
364
365 /* return how many users are opered */
366 int InspIRCd::OperCount()
367 {
368         return this->all_opers.size();
369 }
370
371 /* return how many users are unregistered */
372 int InspIRCd::UnregisteredUserCount()
373 {
374         return this->unregistered_count;
375 }
376
377 /* return channel count */
378 long InspIRCd::ChannelCount()
379 {
380         return chanlist->size();
381 }
382
383 /* return how many local registered users there are */
384 long InspIRCd::LocalUserCount()
385 {
386         /* Doesnt count unregistered clients */
387         return (local_users.size() - this->UnregisteredUserCount());
388 }
389
390 bool InspIRCd::IsValidMask(const std::string &mask)
391 {
392         char* dest = (char*)mask.c_str();
393         int exclamation = 0;
394         int atsign = 0;
395
396         for (char* i = dest; *i; i++)
397         {
398                 /* out of range character, bad mask */
399                 if (*i < 32 || *i > 126)
400                 {
401                         return false;
402                 }
403
404                 switch (*i)
405                 {
406                         case '!':
407                                 exclamation++;
408                                 break;
409                         case '@':
410                                 atsign++;
411                                 break;
412                 }
413         }
414
415         /* valid masks only have 1 ! and @ */
416         if (exclamation != 1 || atsign != 1)
417                 return false;
418
419         return true;
420 }
421
422 /* true for valid channel name, false else */
423 bool InspIRCd::IsChannel(const char *chname)
424 {
425         char *c;
426
427         /* check for no name - don't check for !*chname, as if it is empty, it won't be '#'! */
428         if (!chname || *chname != '#')
429         {
430                 return false;
431         }
432
433         c = (char *)chname + 1;
434         while (*c)
435         {
436                 switch (*c)
437                 {
438                         case ' ':
439                         case ',':
440                         case 7:
441                                 return false;
442                 }
443
444                 c++;
445         }
446                 
447         /* too long a name - note funky pointer arithmetic here. */
448         if ((c - chname) > CHANMAX)
449         {
450                         return false;
451         }
452
453         return true;
454 }
455
456 /* true for valid nickname, false else */
457 bool IsNickHandler::Call(const char* n)
458 {
459         if (!n || !*n)
460                 return false;
461  
462         int p = 0;
463         for (char* i = (char*)n; *i; i++, p++)
464         {
465                 if ((*i >= 'A') && (*i <= '}'))
466                 {
467                         /* "A"-"}" can occur anywhere in a nickname */
468                         continue;
469                 }
470
471                 if ((((*i >= '0') && (*i <= '9')) || (*i == '-')) && (i > n))
472                 {
473                         /* "0"-"9", "-" can occur anywhere BUT the first char of a nickname */
474                         continue;
475                 }
476
477                 /* invalid character! abort */
478                 return false;
479         }
480
481         /* too long? or not -- pointer arithmetic rocks */
482         return (p < NICKMAX - 1);
483 }
484
485 /* return true for good ident, false else */
486 bool IsIdentHandler::Call(const char* n)
487 {
488         if (!n || !*n)
489                 return false;
490
491         for (char* i = (char*)n; *i; i++)
492         {
493                 if ((*i >= 'A') && (*i <= '}'))
494                 {
495                         continue;
496                 }
497
498                 if (((*i >= '0') && (*i <= '9')) || (*i == '-') || (*i == '.'))
499                 {
500                         continue;
501                 }
502
503                 return false;
504         }
505
506         return true;
507 }
508
509 /* open the proper logfile */
510 bool InspIRCd::OpenLog(char**, int)
511 {
512         Config->MyDir = Config->GetFullProgDir();
513
514         if (!*this->LogFileName)
515         {
516                 if (Config->logpath.empty())
517                 {
518                         Config->logpath = Config->MyDir + "/ircd.log";
519                 }
520
521                 Config->log_file = fopen(Config->logpath.c_str(),"a+");
522         }
523         else
524         {
525                 Config->log_file = fopen(this->LogFileName,"a+");
526         }
527
528         if (!Config->log_file)
529         {
530                 this->Logger = NULL;
531                 return false;
532         }
533
534         this->Logger = new FileLogger(this, Config->log_file);
535         return true;
536 }
537
538 void InspIRCd::CheckRoot()
539 {
540         if (geteuid() == 0)
541         {
542                 printf("WARNING!!! You are running an irc server as ROOT!!! DO NOT DO THIS!!!\n\n");
543                 this->Log(DEFAULT,"Cant start as root");
544                 Exit(EXIT_STATUS_ROOT);
545         }
546 }
547
548 void InspIRCd::CheckDie()
549 {
550         if (*Config->DieValue)
551         {
552                 printf("WARNING: %s\n\n",Config->DieValue);
553                 this->Log(DEFAULT,"Died because of <die> tag: %s",Config->DieValue);
554                 Exit(EXIT_STATUS_DIETAG);
555         }
556 }
557
558 void InspIRCd::SendWhoisLine(User* user, User* dest, int numeric, const std::string &text)
559 {
560         std::string copy_text = text;
561
562         int MOD_RESULT = 0;
563         FOREACH_RESULT_I(this, I_OnWhoisLine, OnWhoisLine(user, dest, numeric, copy_text));
564
565         if (!MOD_RESULT)
566                 user->WriteServ("%d %s", numeric, copy_text.c_str());
567 }
568
569 void InspIRCd::SendWhoisLine(User* user, User* dest, int numeric, const char* format, ...)
570 {
571         char textbuffer[MAXBUF];
572         va_list argsPtr;
573         va_start (argsPtr, format);
574         vsnprintf(textbuffer, MAXBUF, format, argsPtr);
575         va_end(argsPtr);
576
577         this->SendWhoisLine(user, dest, numeric, std::string(textbuffer));
578 }
579
580 /** Refactored by Brain, Jun 2007. Much faster with some clever O(1) array
581  * lookups and pointer maths.
582  */
583 long InspIRCd::Duration(const std::string &str)
584 {
585         unsigned char multiplier = 0;
586         long total = 0;
587         long times = 1;
588         long subtotal = 0;
589
590         /* Iterate each item in the string, looking for number or multiplier */
591         for (std::string::const_reverse_iterator i = str.rbegin(); i != str.rend(); ++i)
592         {
593                 /* Found a number, queue it onto the current number */
594                 if ((*i >= '0') && (*i <= '9'))
595                 {
596                         subtotal = subtotal + ((*i - '0') * times);
597                         times = times * 10;
598                 }
599                 else
600                 {
601                         /* Found something thats not a number, find out how much
602                          * it multiplies the built up number by, multiply the total
603                          * and reset the built up number.
604                          */
605                         if (subtotal)
606                                 total += subtotal * duration_multi[multiplier];
607
608                         /* Next subtotal please */
609                         subtotal = 0;
610                         multiplier = *i;
611                         times = 1;
612                 }
613         }
614         if (multiplier)
615         {
616                 total += subtotal * duration_multi[multiplier];
617                 subtotal = 0;
618         }
619         /* Any trailing values built up are treated as raw seconds */
620         return total + subtotal;
621 }
622
623 bool InspIRCd::ULine(const char* server)
624 {
625         if (!server)
626                 return false;
627         if (!*server)
628                 return true;
629
630         return (Config->ulines.find(server) != Config->ulines.end());
631 }
632
633 bool InspIRCd::SilentULine(const char* server)
634 {
635         std::map<irc::string,bool>::iterator n = Config->ulines.find(server);
636         if (n != Config->ulines.end())
637                 return n->second;
638         else return false;
639 }
640
641 std::string InspIRCd::TimeString(time_t curtime)
642 {
643         return std::string(ctime(&curtime),24);
644 }
645