]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/helperfuncs.cpp
Make chanhistory skip CTCPs when storing messages.
[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 /** A lookup table of values for multiplier characters used by
342  * InspIRCd::Duration(). In this lookup table, the indexes for
343  * the ascii values 'm' and 'M' have the value '60', the indexes
344  * for the ascii values 'D' and 'd' have a value of '86400', etc.
345  */
346 static const unsigned int duration_multi[] =
347 {
348         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
349         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
350         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
351         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
352         0, 0, 0, 0, 86400, 0, 0, 0, 3600, 0, 0, 0, 0, 60, 0, 0,
353         0, 0, 0, 1, 0, 0, 0, 604800, 0, 31557600, 0, 0, 0, 0, 0, 0,
354         0, 0, 0, 0, 86400, 0, 0, 0, 3600, 0, 0, 0, 0, 60, 0, 0,
355         0, 0, 0, 1, 0, 0, 0, 604800, 0, 31557600, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
358         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
359         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
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 };
365
366 bool InspIRCd::Duration(const std::string& str, unsigned long& duration)
367 {
368         unsigned long total = 0;
369         unsigned long subtotal = 0;
370
371         /* Iterate each item in the string, looking for number or multiplier */
372         for (std::string::const_iterator i = str.begin(); i != str.end(); ++i)
373         {
374                 /* Found a number, queue it onto the current number */
375                 if ((*i >= '0') && (*i <= '9'))
376                 {
377                         subtotal = (subtotal * 10) + (*i - '0');
378                 }
379                 else
380                 {
381                         /* Found something thats not a number, find out how much
382                          * it multiplies the built up number by, multiply the total
383                          * and reset the built up number.
384                          */
385                         unsigned int multiplier = duration_multi[static_cast<unsigned char>(*i)];
386                         if (multiplier == 0)
387                                 return false;
388
389                         total += subtotal * multiplier;
390
391                         /* Next subtotal please */
392                         subtotal = 0;
393                 }
394         }
395         /* Any trailing values built up are treated as raw seconds */
396         duration = total + subtotal;
397         return true;
398 }
399
400 unsigned long InspIRCd::Duration(const std::string& str)
401 {
402         unsigned long out = 0;
403         InspIRCd::Duration(str, out);
404         return out;
405 }
406
407 bool InspIRCd::IsValidDuration(const std::string& duration)
408 {
409         for (std::string::const_iterator i = duration.begin(); i != duration.end(); ++i)
410         {
411                 unsigned char c = *i;
412                 if (((c >= '0') && (c <= '9')))
413                         continue;
414
415                 if (!duration_multi[c])
416                         return false;
417         }
418         return true;
419 }
420
421 std::string InspIRCd::DurationString(time_t duration)
422 {
423         if (duration == 0)
424                 return "0s";
425
426         time_t years = duration / 31449600;
427         time_t weeks = (duration / 604800) % 52;
428         time_t days = (duration / 86400) % 7;
429         time_t hours = (duration / 3600) % 24;
430         time_t minutes = (duration / 60) % 60;
431         time_t seconds = duration % 60;
432
433         std::string ret;
434
435         if (years)
436                 ret = ConvToStr(years) + "y";
437         if (weeks)
438                 ret += ConvToStr(weeks) + "w";
439         if (days)
440                 ret += ConvToStr(days) + "d";
441         if (hours)
442                 ret += ConvToStr(hours) + "h";
443         if (minutes)
444                 ret += ConvToStr(minutes) + "m";
445         if (seconds)
446                 ret += ConvToStr(seconds) + "s";
447
448         return ret;
449 }
450
451 std::string InspIRCd::Format(va_list& vaList, const char* formatString)
452 {
453         static std::vector<char> formatBuffer(1024);
454
455         while (true)
456         {
457                 va_list dst;
458                 va_copy(dst, vaList);
459
460                 int vsnret = vsnprintf(&formatBuffer[0], formatBuffer.size(), formatString, dst);
461                 va_end(dst);
462
463                 if (vsnret > 0 && static_cast<unsigned>(vsnret) < formatBuffer.size())
464                 {
465                         break;
466                 }
467
468                 formatBuffer.resize(formatBuffer.size() * 2);
469         }
470
471         return std::string(&formatBuffer[0]);
472 }
473
474 std::string InspIRCd::Format(const char* formatString, ...)
475 {
476         std::string ret;
477         VAFORMAT(ret, formatString, formatString);
478         return ret;
479 }
480
481 std::string InspIRCd::TimeString(time_t curtime, const char* format, bool utc)
482 {
483 #ifdef _WIN32
484         if (curtime < 0)
485                 curtime = 0;
486 #endif
487
488         struct tm* timeinfo = utc ? gmtime(&curtime) : localtime(&curtime);
489         if (!timeinfo)
490         {
491                 curtime = 0;
492                 timeinfo = localtime(&curtime);
493         }
494
495         // If the calculated year exceeds four digits or is less than the year 1000,
496         // the behavior of asctime() is undefined
497         if (timeinfo->tm_year + 1900 > 9999)
498                 timeinfo->tm_year = 9999 - 1900;
499         else if (timeinfo->tm_year + 1900 < 1000)
500                 timeinfo->tm_year = 0;
501
502         // This is the default format used by asctime without the terminating new line.
503         if (!format)
504                 format = "%a %b %d %Y %H:%M:%S";
505
506         char buffer[512];
507         if (!strftime(buffer, sizeof(buffer), format, timeinfo))
508                 buffer[0] = '\0';
509
510         return buffer;
511 }
512
513 std::string InspIRCd::GenRandomStr(unsigned int length, bool printable)
514 {
515         char* buf = new char[length];
516         GenRandom(buf, length);
517         std::string rv;
518         rv.resize(length);
519         for(size_t i = 0; i < length; i++)
520                 rv[i] = printable ? 0x3F + (buf[i] & 0x3F) : buf[i];
521         delete[] buf;
522         return rv;
523 }
524
525 // NOTE: this has a slight bias for lower values if max is not a power of 2.
526 // Don't use it if that matters.
527 unsigned long InspIRCd::GenRandomInt(unsigned long max)
528 {
529         unsigned long rv;
530         GenRandom((char*)&rv, sizeof(rv));
531         return rv % max;
532 }
533
534 // This is overridden by a higher-quality algorithm when SSL support is loaded
535 void InspIRCd::DefaultGenRandom(char* output, size_t max)
536 {
537 #if defined HAS_ARC4RANDOM_BUF
538         arc4random_buf(output, max);
539 #else
540         for (unsigned int i = 0; i < max; ++i)
541 # ifdef _WIN32
542         {
543                 unsigned int uTemp;
544                 if(rand_s(&uTemp) != 0)
545                         output[i] = rand();
546                 else
547                         output[i] = uTemp;
548         }
549 # else
550                 output[i] = random();
551 # endif
552 #endif
553 }