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