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 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, 2010 Craig Edwards <brain@inspircd.org>
14 * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
15 * Copyright (C) 2006-2008 Robin Burchell <robin+git@viroteck.net>
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/>.
38 #include "exitcodes.h"
41 /* Find a user record by nickname and return a pointer to it */
42 User* InspIRCd::FindNick(const std::string &nick)
44 if (!nick.empty() && isdigit(*nick.begin()))
45 return FindUUID(nick);
46 return FindNickOnly(nick);
49 User* InspIRCd::FindNickOnly(const std::string &nick)
51 user_hash::iterator iter = this->Users->clientlist.find(nick);
53 if (iter == this->Users->clientlist.end())
59 User *InspIRCd::FindUUID(const std::string &uid)
61 user_hash::iterator finduuid = this->Users->uuidlist.find(uid);
63 if (finduuid == this->Users->uuidlist.end())
66 return finduuid->second;
68 /* find a channel record by channel name and return a pointer to it */
70 Channel* InspIRCd::FindChan(const std::string &chan)
72 chan_hash::iterator iter = chanlist.find(chan);
74 if (iter == chanlist.end())
75 /* Couldn't find it */
81 bool InspIRCd::IsValidMask(const std::string &mask)
83 const char* dest = mask.c_str();
87 for (const char* i = dest; *i; i++)
89 /* out of range character, bad mask */
90 if (*i < 32 || *i > 126)
106 /* valid masks only have 1 ! and @ */
107 if (exclamation != 1 || atsign != 1)
110 if (mask.length() > ServerInstance->Config->Limits.GetMaxMask())
116 void InspIRCd::StripColor(std::string &sentence)
118 /* refactor this completely due to SQUIT bug since the old code would strip last char and replace with \0 --peavey */
121 for (std::string::iterator i = sentence.begin(); i != sentence.end();)
125 else if (seq && (( ((*i >= '0') && (*i <= '9')) || (*i == ',') ) ))
128 if ( (seq <= 4) && (*i == ',') )
136 // Strip all control codes too except \001 for CTCP
137 if (seq || ((*i >= 0) && (*i < 32) && (*i != 1)))
138 i = sentence.erase(i);
144 void InspIRCd::ProcessColors(file_cache& input)
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
151 static struct special_chars
153 std::string character;
155 special_chars(const std::string& c, const std::string& r)
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("", "")
172 for(file_cache::iterator it = input.begin(), it_end = input.end(); it != it_end; it++)
174 std::string ret = *it;
175 for(int i = 0; special[i].character.empty() == false; ++i)
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
181 if((pos > 0) && (ret[pos-1] == '\\') && (ret[pos] == '\\'))
182 continue; // Skip double slashes.
184 // Replace all our characters in the array
185 while(pos != std::string::npos)
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());
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)
196 ret = ret.substr(0, pos) + "\\" + ret.substr(pos + 2);
197 pos = ret.find("\\\\", pos + 1);
203 /* true for valid channel name, false else */
204 bool InspIRCd::DefaultIsChannel(const std::string& chname)
206 if (chname.empty() || chname.length() > ServerInstance->Config->Limits.ChanMax)
209 if (chname[0] != '#')
212 for (std::string::const_iterator i = chname.begin()+1; i != chname.end(); ++i)
226 /* true for valid nickname, false else */
227 bool InspIRCd::DefaultIsNick(const std::string& n)
229 if (n.empty() || n.length() > ServerInstance->Config->Limits.NickMax)
232 for (std::string::const_iterator i = n.begin(); i != n.end(); ++i)
234 if ((*i >= 'A') && (*i <= '}'))
236 /* "A"-"}" can occur anywhere in a nickname */
240 if ((((*i >= '0') && (*i <= '9')) || (*i == '-')) && (i != n.begin()))
242 /* "0"-"9", "-" can occur anywhere BUT the first char of a nickname */
246 /* invalid character! abort */
253 /* return true for good ident, false else */
254 bool InspIRCd::DefaultIsIdent(const std::string& n)
259 for (std::string::const_iterator i = n.begin(); i != n.end(); ++i)
261 if ((*i >= 'A') && (*i <= '}'))
266 if (((*i >= '0') && (*i <= '9')) || (*i == '-') || (*i == '.'))
277 bool InspIRCd::IsHost(const std::string& host)
279 // Hostnames must be non-empty and shorter than the maximum hostname length.
280 if (host.empty() || host.length() > ServerInstance->Config->Limits.MaxHost)
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)
289 unsigned char chr = static_cast<unsigned char>(*iter);
291 // If the current character is a label separator.
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)
305 // If this point is reached then the character is not a dot.
308 // If the current character is a dash.
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)
320 // If this point is reached then the character is not a dash.
323 // Alphanumeric characters are allowed at any position.
324 if ((chr >= '0' && chr <= '9') || (chr >= 'A' && chr <= 'Z') || (chr >= 'a' && chr <= 'z'))
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.
336 bool InspIRCd::IsSID(const std::string &str)
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
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])));
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.
351 static const unsigned int duration_multi[] =
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,
371 bool InspIRCd::Duration(const std::string& str, unsigned long& duration)
373 unsigned long total = 0;
374 unsigned long subtotal = 0;
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)
379 /* Found a number, queue it onto the current number */
380 if ((*i >= '0') && (*i <= '9'))
382 subtotal = (subtotal * 10) + (*i - '0');
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.
390 unsigned int multiplier = duration_multi[static_cast<unsigned char>(*i)];
394 total += subtotal * multiplier;
396 /* Next subtotal please */
400 /* Any trailing values built up are treated as raw seconds */
401 duration = total + subtotal;
405 unsigned long InspIRCd::Duration(const std::string& str)
407 unsigned long out = 0;
408 InspIRCd::Duration(str, out);
412 bool InspIRCd::IsValidDuration(const std::string& duration)
414 for (std::string::const_iterator i = duration.begin(); i != duration.end(); ++i)
416 unsigned char c = *i;
417 if (((c >= '0') && (c <= '9')))
420 if (!duration_multi[c])
426 std::string InspIRCd::DurationString(time_t duration)
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;
441 ret = ConvToStr(years) + "y";
443 ret += ConvToStr(weeks) + "w";
445 ret += ConvToStr(days) + "d";
447 ret += ConvToStr(hours) + "h";
449 ret += ConvToStr(minutes) + "m";
451 ret += ConvToStr(seconds) + "s";
456 std::string InspIRCd::Format(va_list& vaList, const char* formatString)
458 static std::vector<char> formatBuffer(1024);
463 va_copy(dst, vaList);
465 int vsnret = vsnprintf(&formatBuffer[0], formatBuffer.size(), formatString, dst);
468 if (vsnret > 0 && static_cast<unsigned>(vsnret) < formatBuffer.size())
473 formatBuffer.resize(formatBuffer.size() * 2);
476 return std::string(&formatBuffer[0]);
479 std::string InspIRCd::Format(const char* formatString, ...)
482 VAFORMAT(ret, formatString, formatString);
486 std::string InspIRCd::TimeString(time_t curtime, const char* format, bool utc)
493 struct tm* timeinfo = utc ? gmtime(&curtime) : localtime(&curtime);
497 timeinfo = localtime(&curtime);
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;
507 // This is the default format used by asctime without the terminating new line.
509 format = "%a %b %d %Y %H:%M:%S";
512 if (!strftime(buffer, sizeof(buffer), format, timeinfo))
518 std::string InspIRCd::GenRandomStr(unsigned int length, bool printable)
520 std::vector<char> str(length);
521 GenRandom(&str[0], length);
523 for (size_t i = 0; i < length; i++)
524 str[i] = 0x3F + (str[i] & 0x3F);
525 return std::string(&str[0], str.size());
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)
533 GenRandom((char*)&rv, sizeof(rv));
537 // This is overridden by a higher-quality algorithm when TLS (SSL) support is loaded
538 void InspIRCd::DefaultGenRandom(char* output, size_t max)
540 #if defined HAS_ARC4RANDOM_BUF
541 arc4random_buf(output, max);
543 for (unsigned int i = 0; i < max; ++i)
547 if(rand_s(&uTemp) != 0)
553 output[i] = random();