2 * InspIRCd -- Internet Relay Chat Daemon
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>
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.
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
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/>.
39 /* Find a user record by nickname and return a pointer to it */
40 User* InspIRCd::FindNick(const std::string &nick)
42 if (!nick.empty() && isdigit(*nick.begin()))
43 return FindUUID(nick);
44 return FindNickOnly(nick);
47 User* InspIRCd::FindNickOnly(const std::string &nick)
49 user_hash::iterator iter = this->Users->clientlist.find(nick);
51 if (iter == this->Users->clientlist.end())
57 User *InspIRCd::FindUUID(const std::string &uid)
59 user_hash::iterator finduuid = this->Users->uuidlist.find(uid);
61 if (finduuid == this->Users->uuidlist.end())
64 return finduuid->second;
66 /* find a channel record by channel name and return a pointer to it */
68 Channel* InspIRCd::FindChan(const std::string &chan)
70 chan_hash::iterator iter = chanlist.find(chan);
72 if (iter == chanlist.end())
73 /* Couldn't find it */
79 bool InspIRCd::IsValidMask(const std::string &mask)
81 const char* dest = mask.c_str();
85 for (const char* i = dest; *i; i++)
87 /* out of range character, bad mask */
88 if (*i < 32 || *i > 126)
104 /* valid masks only have 1 ! and @ */
105 if (exclamation != 1 || atsign != 1)
108 if (mask.length() > ServerInstance->Config->Limits.GetMaxMask())
114 void InspIRCd::StripColor(std::string &sentence)
116 /* refactor this completely due to SQUIT bug since the old code would strip last char and replace with \0 --peavey */
119 for (std::string::iterator i = sentence.begin(); i != sentence.end();)
123 else if (seq && (( ((*i >= '0') && (*i <= '9')) || (*i == ',') ) ))
126 if ( (seq <= 4) && (*i == ',') )
134 // Strip all control codes too except \001 for CTCP
135 if (seq || ((*i >= 0) && (*i < 32) && (*i != 1)))
136 i = sentence.erase(i);
142 void InspIRCd::ProcessColors(file_cache& input)
145 * Replace all color codes from the special[] array to actual
146 * color code chars using C++ style escape sequences. You
147 * can append other chars to replace if you like -- Justasic
149 static struct special_chars
151 std::string character;
153 special_chars(const std::string& c, const std::string& r)
159 special_chars("\\b", "\x02"), // Bold
160 special_chars("\\c", "\x03"), // Color
161 special_chars("\\i", "\x1D"), // Italic
162 special_chars("\\m", "\x11"), // Monospace
163 special_chars("\\r", "\x16"), // Reverse
164 special_chars("\\s", "\x1E"), // Strikethrough
165 special_chars("\\u", "\x1F"), // Underline
166 special_chars("\\x", "\x0F"), // Reset
167 special_chars("", "")
170 for(file_cache::iterator it = input.begin(), it_end = input.end(); it != it_end; it++)
172 std::string ret = *it;
173 for(int i = 0; special[i].character.empty() == false; ++i)
175 std::string::size_type pos = ret.find(special[i].character);
176 if(pos == std::string::npos) // Couldn't find the character, skip this line
179 if((pos > 0) && (ret[pos-1] == '\\') && (ret[pos] == '\\'))
180 continue; // Skip double slashes.
182 // Replace all our characters in the array
183 while(pos != std::string::npos)
185 ret = ret.substr(0, pos) + special[i].replace + ret.substr(pos + special[i].character.size());
186 pos = ret.find(special[i].character, pos + special[i].replace.size());
190 // Replace double slashes with a single slash before we return
191 std::string::size_type pos = ret.find("\\\\");
192 while(pos != std::string::npos)
194 ret = ret.substr(0, pos) + "\\" + ret.substr(pos + 2);
195 pos = ret.find("\\\\", pos + 1);
201 /* true for valid channel name, false else */
202 bool InspIRCd::DefaultIsChannel(const std::string& chname)
204 if (chname.empty() || chname.length() > ServerInstance->Config->Limits.ChanMax)
207 if (chname[0] != '#')
210 for (std::string::const_iterator i = chname.begin()+1; i != chname.end(); ++i)
224 /* true for valid nickname, false else */
225 bool InspIRCd::DefaultIsNick(const std::string& n)
227 if (n.empty() || n.length() > ServerInstance->Config->Limits.NickMax)
230 for (std::string::const_iterator i = n.begin(); i != n.end(); ++i)
232 if ((*i >= 'A') && (*i <= '}'))
234 /* "A"-"}" can occur anywhere in a nickname */
238 if ((((*i >= '0') && (*i <= '9')) || (*i == '-')) && (i != n.begin()))
240 /* "0"-"9", "-" can occur anywhere BUT the first char of a nickname */
244 /* invalid character! abort */
251 /* return true for good ident, false else */
252 bool InspIRCd::DefaultIsIdent(const std::string& n)
257 for (std::string::const_iterator i = n.begin(); i != n.end(); ++i)
259 if ((*i >= 'A') && (*i <= '}'))
264 if (((*i >= '0') && (*i <= '9')) || (*i == '-') || (*i == '.'))
275 bool InspIRCd::IsHost(const std::string& host)
277 // Hostnames must be non-empty and shorter than the maximum hostname length.
278 if (host.empty() || host.length() > ServerInstance->Config->Limits.MaxHost)
281 unsigned int numdashes = 0;
282 unsigned int numdots = 0;
283 bool seendot = false;
284 const std::string::const_iterator hostend = host.end() - 1;
285 for (std::string::const_iterator iter = host.begin(); iter != host.end(); ++iter)
287 unsigned char chr = static_cast<unsigned char>(*iter);
289 // If the current character is a label separator.
294 // Consecutive separators are not allowed and dashes can not exist at the start or end
295 // of labels and separators must only exist between labels.
296 if (seendot || numdashes || iter == host.begin() || iter == hostend)
303 // If this point is reached then the character is not a dot.
306 // If the current character is a dash.
309 // Consecutive separators are not allowed and dashes can not exist at the start or end
310 // of labels and separators must only exist between labels.
311 if (seendot || numdashes >= 2 || iter == host.begin() || iter == hostend)
318 // If this point is reached then the character is not a dash.
321 // Alphanumeric characters are allowed at any position.
322 if ((chr >= '0' && chr <= '9') || (chr >= 'A' && chr <= 'Z') || (chr >= 'a' && chr <= 'z'))
328 // Whilst simple hostnames (e.g. localhost) are valid we do not allow the server to use
329 // them to prevent issues with clients that differentiate between short client and server
330 // prefixes by checking whether the nickname contains a dot.
334 bool InspIRCd::IsSID(const std::string &str)
336 /* Returns true if the string given is exactly 3 characters long,
337 * starts with a digit, and the other two characters are A-Z or digits
339 return ((str.length() == 3) && isdigit(str[0]) &&
340 ((str[1] >= 'A' && str[1] <= 'Z') || isdigit(str[1])) &&
341 ((str[2] >= 'A' && str[2] <= 'Z') || isdigit(str[2])));
344 /** A lookup table of values for multiplier characters used by
345 * InspIRCd::Duration(). In this lookup table, the indexes for
346 * the ascii values 'm' and 'M' have the value '60', the indexes
347 * for the ascii values 'D' and 'd' have a value of '86400', etc.
349 static const unsigned int duration_multi[] =
351 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
352 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
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, 86400, 0, 0, 0, 3600, 0, 0, 0, 0, 60, 0, 0,
356 0, 0, 0, 1, 0, 0, 0, 604800, 0, 31557600, 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, 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 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,
369 bool InspIRCd::Duration(const std::string& str, unsigned long& duration)
371 unsigned long total = 0;
372 unsigned long subtotal = 0;
374 /* Iterate each item in the string, looking for number or multiplier */
375 for (std::string::const_iterator i = str.begin(); i != str.end(); ++i)
377 /* Found a number, queue it onto the current number */
378 if ((*i >= '0') && (*i <= '9'))
380 subtotal = (subtotal * 10) + (*i - '0');
384 /* Found something that's not a number, find out how much
385 * it multiplies the built up number by, multiply the total
386 * and reset the built up number.
388 unsigned int multiplier = duration_multi[static_cast<unsigned char>(*i)];
392 total += subtotal * multiplier;
394 /* Next subtotal please */
398 /* Any trailing values built up are treated as raw seconds */
399 duration = total + subtotal;
403 unsigned long InspIRCd::Duration(const std::string& str)
405 unsigned long out = 0;
406 InspIRCd::Duration(str, out);
410 bool InspIRCd::IsValidDuration(const std::string& duration)
412 for (std::string::const_iterator i = duration.begin(); i != duration.end(); ++i)
414 unsigned char c = *i;
415 if (((c >= '0') && (c <= '9')))
418 if (!duration_multi[c])
424 std::string InspIRCd::DurationString(time_t duration)
429 time_t years = duration / 31449600;
430 time_t weeks = (duration / 604800) % 52;
431 time_t days = (duration / 86400) % 7;
432 time_t hours = (duration / 3600) % 24;
433 time_t minutes = (duration / 60) % 60;
434 time_t seconds = duration % 60;
439 ret = ConvToStr(years) + "y";
441 ret += ConvToStr(weeks) + "w";
443 ret += ConvToStr(days) + "d";
445 ret += ConvToStr(hours) + "h";
447 ret += ConvToStr(minutes) + "m";
449 ret += ConvToStr(seconds) + "s";
454 std::string InspIRCd::Format(va_list& vaList, const char* formatString)
456 static std::vector<char> formatBuffer(1024);
461 va_copy(dst, vaList);
463 int vsnret = vsnprintf(&formatBuffer[0], formatBuffer.size(), formatString, dst);
466 if (vsnret > 0 && static_cast<unsigned>(vsnret) < formatBuffer.size())
471 formatBuffer.resize(formatBuffer.size() * 2);
474 return std::string(&formatBuffer[0]);
477 std::string InspIRCd::Format(const char* formatString, ...)
480 VAFORMAT(ret, formatString, formatString);
484 std::string InspIRCd::TimeString(time_t curtime, const char* format, bool utc)
491 struct tm* timeinfo = utc ? gmtime(&curtime) : localtime(&curtime);
495 timeinfo = localtime(&curtime);
498 // If the calculated year exceeds four digits or is less than the year 1000,
499 // the behavior of asctime() is undefined
500 if (timeinfo->tm_year + 1900 > 9999)
501 timeinfo->tm_year = 9999 - 1900;
502 else if (timeinfo->tm_year + 1900 < 1000)
503 timeinfo->tm_year = 0;
505 // This is the default format used by asctime without the terminating new line.
507 format = "%a %b %d %Y %H:%M:%S";
510 if (!strftime(buffer, sizeof(buffer), format, timeinfo))
516 std::string InspIRCd::GenRandomStr(unsigned int length, bool printable)
518 std::vector<char> str(length);
519 GenRandom(&str[0], length);
521 for (size_t i = 0; i < length; i++)
522 str[i] = 0x3F + (str[i] & 0x3F);
523 return std::string(&str[0], str.size());
526 // NOTE: this has a slight bias for lower values if max is not a power of 2.
527 // Don't use it if that matters.
528 unsigned long InspIRCd::GenRandomInt(unsigned long max)
531 GenRandom((char*)&rv, sizeof(rv));
535 // This is overridden by a higher-quality algorithm when TLS (SSL) support is loaded
536 void InspIRCd::DefaultGenRandom(char* output, size_t max)
538 #if defined HAS_ARC4RANDOM_BUF
539 arc4random_buf(output, max);
541 for (unsigned int i = 0; i < max; ++i)
545 if(rand_s(&uTemp) != 0)
551 output[i] = random();