]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/helperfuncs.cpp
Merge extras/m_privdeaf into m_deaf and update documentation.
[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         time_t years = duration / 31536000;
436         time_t weeks = (duration / 604800) % 52;
437         time_t days = (duration / 86400) % 7;
438         time_t hours = (duration / 3600) % 24;
439         time_t minutes = (duration / 60) % 60;
440         time_t seconds = duration % 60;
441
442         std::string ret;
443
444         if (years)
445                 ret = ConvToStr(years) + "y";
446         if (weeks)
447                 ret += ConvToStr(weeks) + "w";
448         if (days)
449                 ret += ConvToStr(days) + "d";
450         if (hours)
451                 ret += ConvToStr(hours) + "h";
452         if (minutes)
453                 ret += ConvToStr(minutes) + "m";
454         if (seconds)
455                 ret += ConvToStr(seconds) + "s";
456
457         return ret;
458 }
459
460 std::string InspIRCd::Format(va_list& vaList, const char* formatString)
461 {
462         static std::vector<char> formatBuffer(1024);
463
464         while (true)
465         {
466                 va_list dst;
467                 va_copy(dst, vaList);
468
469                 int vsnret = vsnprintf(&formatBuffer[0], formatBuffer.size(), formatString, dst);
470                 va_end(dst);
471
472                 if (vsnret > 0 && static_cast<unsigned>(vsnret) < formatBuffer.size())
473                 {
474                         break;
475                 }
476
477                 formatBuffer.resize(formatBuffer.size() * 2);
478         }
479
480         return std::string(&formatBuffer[0]);
481 }
482
483 std::string InspIRCd::Format(const char* formatString, ...)
484 {
485         std::string ret;
486         VAFORMAT(ret, formatString, formatString);
487         return ret;
488 }
489
490 std::string InspIRCd::TimeString(time_t curtime, const char* format, bool utc)
491 {
492 #ifdef _WIN32
493         if (curtime < 0)
494                 curtime = 0;
495 #endif
496
497         struct tm* timeinfo = utc ? gmtime(&curtime) : localtime(&curtime);
498         if (!timeinfo)
499         {
500                 curtime = 0;
501                 timeinfo = localtime(&curtime);
502         }
503
504         // If the calculated year exceeds four digits or is less than the year 1000,
505         // the behavior of asctime() is undefined
506         if (timeinfo->tm_year + 1900 > 9999)
507                 timeinfo->tm_year = 9999 - 1900;
508         else if (timeinfo->tm_year + 1900 < 1000)
509                 timeinfo->tm_year = 0;
510
511         // This is the default format used by asctime without the terminating new line.
512         if (!format)
513                 format = "%a %b %d %Y %H:%M:%S";
514
515         char buffer[512];
516         if (!strftime(buffer, sizeof(buffer), format, timeinfo))
517                 buffer[0] = '\0';
518
519         return buffer;
520 }
521
522 std::string InspIRCd::GenRandomStr(unsigned int length, bool printable)
523 {
524         char* buf = new char[length];
525         GenRandom(buf, length);
526         std::string rv;
527         rv.resize(length);
528         for(size_t i = 0; i < length; i++)
529                 rv[i] = printable ? 0x3F + (buf[i] & 0x3F) : buf[i];
530         delete[] buf;
531         return rv;
532 }
533
534 // NOTE: this has a slight bias for lower values if max is not a power of 2.
535 // Don't use it if that matters.
536 unsigned long InspIRCd::GenRandomInt(unsigned long max)
537 {
538         unsigned long rv;
539         GenRandom((char*)&rv, sizeof(rv));
540         return rv % max;
541 }
542
543 // This is overridden by a higher-quality algorithm when SSL support is loaded
544 void InspIRCd::DefaultGenRandom(char* output, size_t max)
545 {
546 #if defined HAS_ARC4RANDOM_BUF
547         arc4random_buf(output, max);
548 #else
549         for (unsigned int i = 0; i < max; ++i)
550 # ifdef _WIN32
551         {
552                 unsigned int uTemp;
553                 if(rand_s(&uTemp) != 0)
554                         output[i] = rand();
555                 else
556                         output[i] = uTemp;
557         }
558 # else
559                 output[i] = random();
560 # endif
561 #endif
562 }