]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/helperfuncs.cpp
Deprecate Send{Channel,User}Notice; use SendMessage instead.
[user/henk/code/inspircd.git] / src / helperfuncs.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
5  *   Copyright (C) 2006-2008 Robin Burchell <robin+git@viroteck.net>
6  *   Copyright (C) 2005-2008 Craig Edwards <craigedwards@brainbox.cc>
7  *   Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
8  *   Copyright (C) 2006-2007 Oliver Lupton <oliverlupton@gmail.com>
9  *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
10  *   Copyright (C) 2003-2019 Anope Team <team@anope.org>
11  *
12  * This file is part of InspIRCd.  InspIRCd is free software: you can
13  * redistribute it and/or modify it under the terms of the GNU General Public
14  * License as published by the Free Software Foundation, version 2.
15  *
16  * This program is distributed in the hope that it will be useful, but WITHOUT
17  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
19  * details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23  */
24
25
26 #ifdef _WIN32
27 #define _CRT_RAND_S
28 #include <stdlib.h>
29 #endif
30
31 #include "inspircd.h"
32 #include "xline.h"
33 #include "exitcodes.h"
34 #include <iostream>
35
36 /* Find a user record by nickname and return a pointer to it */
37 User* InspIRCd::FindNick(const std::string &nick)
38 {
39         if (!nick.empty() && isdigit(*nick.begin()))
40                 return FindUUID(nick);
41         return FindNickOnly(nick);
42 }
43
44 User* InspIRCd::FindNickOnly(const std::string &nick)
45 {
46         user_hash::iterator iter = this->Users->clientlist.find(nick);
47
48         if (iter == this->Users->clientlist.end())
49                 return NULL;
50
51         return iter->second;
52 }
53
54 User *InspIRCd::FindUUID(const std::string &uid)
55 {
56         user_hash::iterator finduuid = this->Users->uuidlist.find(uid);
57
58         if (finduuid == this->Users->uuidlist.end())
59                 return NULL;
60
61         return finduuid->second;
62 }
63 /* find a channel record by channel name and return a pointer to it */
64
65 Channel* InspIRCd::FindChan(const std::string &chan)
66 {
67         chan_hash::iterator iter = chanlist.find(chan);
68
69         if (iter == chanlist.end())
70                 /* Couldn't find it */
71                 return NULL;
72
73         return iter->second;
74 }
75
76 bool InspIRCd::IsValidMask(const std::string &mask)
77 {
78         const char* dest = mask.c_str();
79         int exclamation = 0;
80         int atsign = 0;
81
82         for (const char* i = dest; *i; i++)
83         {
84                 /* out of range character, bad mask */
85                 if (*i < 32 || *i > 126)
86                 {
87                         return false;
88                 }
89
90                 switch (*i)
91                 {
92                         case '!':
93                                 exclamation++;
94                                 break;
95                         case '@':
96                                 atsign++;
97                                 break;
98                 }
99         }
100
101         /* valid masks only have 1 ! and @ */
102         if (exclamation != 1 || atsign != 1)
103                 return false;
104
105         if (mask.length() > ServerInstance->Config->Limits.GetMaxMask())
106                 return false;
107
108         return true;
109 }
110
111 void InspIRCd::StripColor(std::string &sentence)
112 {
113         /* refactor this completely due to SQUIT bug since the old code would strip last char and replace with \0 --peavey */
114         int seq = 0;
115
116         for (std::string::iterator i = sentence.begin(); i != sentence.end();)
117         {
118                 if (*i == 3)
119                         seq = 1;
120                 else if (seq && (( ((*i >= '0') && (*i <= '9')) || (*i == ',') ) ))
121                 {
122                         seq++;
123                         if ( (seq <= 4) && (*i == ',') )
124                                 seq = 1;
125                         else if (seq > 3)
126                                 seq = 0;
127                 }
128                 else
129                         seq = 0;
130
131                 // Strip all control codes too except \001 for CTCP
132                 if (seq || ((*i >= 0) && (*i < 32) && (*i != 1)))
133                         i = sentence.erase(i);
134                 else
135                         ++i;
136         }
137 }
138
139 void InspIRCd::ProcessColors(file_cache& input)
140 {
141         /*
142          * Replace all color codes from the special[] array to actual
143          * color code chars using C++ style escape sequences. You
144          * can append other chars to replace if you like -- Justasic
145          */
146         static struct special_chars
147         {
148                 std::string character;
149                 std::string replace;
150                 special_chars(const std::string& c, const std::string& r)
151                         : character(c)
152                         , replace(r)
153                 {
154                 }
155         } special[] = {
156                 special_chars("\\b", "\x02"), // Bold
157                 special_chars("\\c", "\x03"), // Color
158                 special_chars("\\i", "\x1D"), // Italic
159                 special_chars("\\m", "\x11"), // Monospace
160                 special_chars("\\r", "\x16"), // Reverse
161                 special_chars("\\s", "\x1E"), // Strikethrough
162                 special_chars("\\u", "\x1F"), // Underline
163                 special_chars("\\x", "\x0F"), // Reset
164                 special_chars("", "")
165         };
166
167         for(file_cache::iterator it = input.begin(), it_end = input.end(); it != it_end; it++)
168         {
169                 std::string ret = *it;
170                 for(int i = 0; special[i].character.empty() == false; ++i)
171                 {
172                         std::string::size_type pos = ret.find(special[i].character);
173                         if(pos == std::string::npos) // Couldn't find the character, skip this line
174                                 continue;
175
176                         if((pos > 0) && (ret[pos-1] == '\\') && (ret[pos] == '\\'))
177                                 continue; // Skip double slashes.
178
179                         // Replace all our characters in the array
180                         while(pos != std::string::npos)
181                         {
182                                 ret = ret.substr(0, pos) + special[i].replace + ret.substr(pos + special[i].character.size());
183                                 pos = ret.find(special[i].character, pos + special[i].replace.size());
184                         }
185                 }
186
187                 // Replace double slashes with a single slash before we return
188                 std::string::size_type pos = ret.find("\\\\");
189                 while(pos != std::string::npos)
190                 {
191                         ret = ret.substr(0, pos) + "\\" + ret.substr(pos + 2);
192                         pos = ret.find("\\\\", pos + 1);
193                 }
194                 *it = ret;
195         }
196 }
197
198 /* true for valid channel name, false else */
199 bool InspIRCd::DefaultIsChannel(const std::string& chname)
200 {
201         if (chname.empty() || chname.length() > ServerInstance->Config->Limits.ChanMax)
202                 return false;
203
204         if (chname[0] != '#')
205                 return false;
206
207         for (std::string::const_iterator i = chname.begin()+1; i != chname.end(); ++i)
208         {
209                 switch (*i)
210                 {
211                         case ' ':
212                         case ',':
213                         case 7:
214                                 return false;
215                 }
216         }
217
218         return true;
219 }
220
221 /* true for valid nickname, false else */
222 bool InspIRCd::DefaultIsNick(const std::string& n)
223 {
224         if (n.empty() || n.length() > ServerInstance->Config->Limits.NickMax)
225                 return false;
226
227         for (std::string::const_iterator i = n.begin(); i != n.end(); ++i)
228         {
229                 if ((*i >= 'A') && (*i <= '}'))
230                 {
231                         /* "A"-"}" can occur anywhere in a nickname */
232                         continue;
233                 }
234
235                 if ((((*i >= '0') && (*i <= '9')) || (*i == '-')) && (i != n.begin()))
236                 {
237                         /* "0"-"9", "-" can occur anywhere BUT the first char of a nickname */
238                         continue;
239                 }
240
241                 /* invalid character! abort */
242                 return false;
243         }
244
245         return true;
246 }
247
248 /* return true for good ident, false else */
249 bool InspIRCd::DefaultIsIdent(const std::string& n)
250 {
251         if (n.empty())
252                 return false;
253
254         for (std::string::const_iterator i = n.begin(); i != n.end(); ++i)
255         {
256                 if ((*i >= 'A') && (*i <= '}'))
257                 {
258                         continue;
259                 }
260
261                 if (((*i >= '0') && (*i <= '9')) || (*i == '-') || (*i == '.'))
262                 {
263                         continue;
264                 }
265
266                 return false;
267         }
268
269         return true;
270 }
271
272 bool InspIRCd::IsHost(const std::string& host)
273 {
274         // Hostnames must be non-empty and shorter than the maximum hostname length.
275         if (host.empty() || host.length() > ServerInstance->Config->Limits.MaxHost)
276                 return false;
277
278         unsigned int numdashes = 0;
279         unsigned int numdots = 0;
280         bool seendot = false;
281         const std::string::const_iterator hostend = host.end() - 1;
282         for (std::string::const_iterator iter = host.begin(); iter != host.end(); ++iter)
283         {
284                 unsigned char chr = static_cast<unsigned char>(*iter);
285
286                 // If the current character is a label separator.
287                 if (chr == '.')
288                 {
289                         numdots++;
290
291                         // Consecutive separators are not allowed and dashes can not exist at the start or end
292                         // of labels and separators must only exist between labels.
293                         if (seendot || numdashes || iter == host.begin() || iter == hostend)
294                                 return false;
295
296                         seendot = true;
297                         continue;
298                 }
299
300                 // If this point is reached then the character is not a dot.
301                 seendot = false;
302
303                 // If the current character is a dash.
304                 if (chr == '-')
305                 {
306                         // Consecutive separators are not allowed and dashes can not exist at the start or end
307                         // of labels and separators must only exist between labels.
308                         if (seendot || numdashes >= 2 || iter == host.begin() || iter == hostend)
309                                 return false;
310
311                         numdashes += 1;
312                         continue;
313                 }
314
315                 // If this point is reached then the character is not a dash.
316                 numdashes = 0;
317
318                 // Alphanumeric characters are allowed at any position.
319                 if ((chr >= '0' && chr <= '9') || (chr >= 'A' && chr <= 'Z') || (chr >= 'a' && chr <= 'z'))
320                         continue;
321
322                 return false;
323         }
324
325         // Whilst simple hostnames (e.g. localhost) are valid we do not allow the server to use
326         // them to prevent issues with clients that differentiate between short client and server
327         // prefixes by checking whether the nickname contains a dot.
328         return numdots;
329 }
330
331 bool InspIRCd::IsSID(const std::string &str)
332 {
333         /* Returns true if the string given is exactly 3 characters long,
334          * starts with a digit, and the other two characters are A-Z or digits
335          */
336         return ((str.length() == 3) && isdigit(str[0]) &&
337                         ((str[1] >= 'A' && str[1] <= 'Z') || isdigit(str[1])) &&
338                          ((str[2] >= 'A' && str[2] <= 'Z') || isdigit(str[2])));
339 }
340
341 void InspIRCd::CheckRoot()
342 {
343 #ifndef _WIN32
344         if (geteuid() == 0)
345         {
346                 std::cout << "ERROR: You are running an irc server as root! DO NOT DO THIS!" << std::endl << std::endl;
347                 this->Logs->Log("STARTUP", LOG_DEFAULT, "Can't start as root");
348                 Exit(EXIT_STATUS_ROOT);
349         }
350 #endif
351 }
352
353 /** A lookup table of values for multiplier characters used by
354  * InspIRCd::Duration(). In this lookup table, the indexes for
355  * the ascii values 'm' and 'M' have the value '60', the indexes
356  * for the ascii values 'D' and 'd' have a value of '86400', etc.
357  */
358 static const unsigned int duration_multi[] =
359 {
360         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
361         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
362         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
363         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
364         0, 0, 0, 0, 86400, 0, 0, 0, 3600, 0, 0, 0, 0, 60, 0, 0,
365         0, 0, 0, 1, 0, 0, 0, 604800, 0, 31557600, 0, 0, 0, 0, 0, 0,
366         0, 0, 0, 0, 86400, 0, 0, 0, 3600, 0, 0, 0, 0, 60, 0, 0,
367         0, 0, 0, 1, 0, 0, 0, 604800, 0, 31557600, 0, 0, 0, 0, 0, 0,
368         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
369         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
370         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
371         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
372         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
373         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
374         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
375         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
376 };
377
378 bool InspIRCd::Duration(const std::string& str, unsigned long& duration)
379 {
380         unsigned long total = 0;
381         unsigned long subtotal = 0;
382
383         /* Iterate each item in the string, looking for number or multiplier */
384         for (std::string::const_iterator i = str.begin(); i != str.end(); ++i)
385         {
386                 /* Found a number, queue it onto the current number */
387                 if ((*i >= '0') && (*i <= '9'))
388                 {
389                         subtotal = (subtotal * 10) + (*i - '0');
390                 }
391                 else
392                 {
393                         /* Found something thats not a number, find out how much
394                          * it multiplies the built up number by, multiply the total
395                          * and reset the built up number.
396                          */
397                         unsigned int multiplier = duration_multi[static_cast<unsigned char>(*i)];
398                         if (multiplier == 0)
399                                 return false;
400
401                         total += subtotal * multiplier;
402
403                         /* Next subtotal please */
404                         subtotal = 0;
405                 }
406         }
407         /* Any trailing values built up are treated as raw seconds */
408         duration = total + subtotal;
409         return true;
410 }
411
412 unsigned long InspIRCd::Duration(const std::string& str)
413 {
414         unsigned long out = 0;
415         InspIRCd::Duration(str, out);
416         return out;
417 }
418
419 bool InspIRCd::IsValidDuration(const std::string& duration)
420 {
421         for (std::string::const_iterator i = duration.begin(); i != duration.end(); ++i)
422         {
423                 unsigned char c = *i;
424                 if (((c >= '0') && (c <= '9')))
425                         continue;
426
427                 if (!duration_multi[c])
428                         return false;
429         }
430         return true;
431 }
432
433 std::string InspIRCd::DurationString(time_t duration)
434 {
435         if (duration == 0)
436                 return "0s";
437
438         time_t years = duration / 31449600;
439         time_t weeks = (duration / 604800) % 52;
440         time_t days = (duration / 86400) % 7;
441         time_t hours = (duration / 3600) % 24;
442         time_t minutes = (duration / 60) % 60;
443         time_t seconds = duration % 60;
444
445         std::string ret;
446
447         if (years)
448                 ret = ConvToStr(years) + "y";
449         if (weeks)
450                 ret += ConvToStr(weeks) + "w";
451         if (days)
452                 ret += ConvToStr(days) + "d";
453         if (hours)
454                 ret += ConvToStr(hours) + "h";
455         if (minutes)
456                 ret += ConvToStr(minutes) + "m";
457         if (seconds)
458                 ret += ConvToStr(seconds) + "s";
459
460         return ret;
461 }
462
463 std::string InspIRCd::Format(va_list& vaList, const char* formatString)
464 {
465         static std::vector<char> formatBuffer(1024);
466
467         while (true)
468         {
469                 va_list dst;
470                 va_copy(dst, vaList);
471
472                 int vsnret = vsnprintf(&formatBuffer[0], formatBuffer.size(), formatString, dst);
473                 va_end(dst);
474
475                 if (vsnret > 0 && static_cast<unsigned>(vsnret) < formatBuffer.size())
476                 {
477                         break;
478                 }
479
480                 formatBuffer.resize(formatBuffer.size() * 2);
481         }
482
483         return std::string(&formatBuffer[0]);
484 }
485
486 std::string InspIRCd::Format(const char* formatString, ...)
487 {
488         std::string ret;
489         VAFORMAT(ret, formatString, formatString);
490         return ret;
491 }
492
493 std::string InspIRCd::TimeString(time_t curtime, const char* format, bool utc)
494 {
495 #ifdef _WIN32
496         if (curtime < 0)
497                 curtime = 0;
498 #endif
499
500         struct tm* timeinfo = utc ? gmtime(&curtime) : localtime(&curtime);
501         if (!timeinfo)
502         {
503                 curtime = 0;
504                 timeinfo = localtime(&curtime);
505         }
506
507         // If the calculated year exceeds four digits or is less than the year 1000,
508         // the behavior of asctime() is undefined
509         if (timeinfo->tm_year + 1900 > 9999)
510                 timeinfo->tm_year = 9999 - 1900;
511         else if (timeinfo->tm_year + 1900 < 1000)
512                 timeinfo->tm_year = 0;
513
514         // This is the default format used by asctime without the terminating new line.
515         if (!format)
516                 format = "%a %b %d %Y %H:%M:%S";
517
518         char buffer[512];
519         if (!strftime(buffer, sizeof(buffer), format, timeinfo))
520                 buffer[0] = '\0';
521
522         return buffer;
523 }
524
525 std::string InspIRCd::GenRandomStr(unsigned int length, bool printable)
526 {
527         char* buf = new char[length];
528         GenRandom(buf, length);
529         std::string rv;
530         rv.resize(length);
531         for(size_t i = 0; i < length; i++)
532                 rv[i] = printable ? 0x3F + (buf[i] & 0x3F) : buf[i];
533         delete[] buf;
534         return rv;
535 }
536
537 // NOTE: this has a slight bias for lower values if max is not a power of 2.
538 // Don't use it if that matters.
539 unsigned long InspIRCd::GenRandomInt(unsigned long max)
540 {
541         unsigned long rv;
542         GenRandom((char*)&rv, sizeof(rv));
543         return rv % max;
544 }
545
546 // This is overridden by a higher-quality algorithm when SSL support is loaded
547 void InspIRCd::DefaultGenRandom(char* output, size_t max)
548 {
549 #if defined HAS_ARC4RANDOM_BUF
550         arc4random_buf(output, max);
551 #else
552         for (unsigned int i = 0; i < max; ++i)
553 # ifdef _WIN32
554         {
555                 unsigned int uTemp;
556                 if(rand_s(&uTemp) != 0)
557                         output[i] = rand();
558                 else
559                         output[i] = uTemp;
560         }
561 # else
562                 output[i] = random();
563 # endif
564 #endif
565 }