]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/helperfuncs.cpp
Merge pull request #1351 from SaberUK/master+webirc
[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  *
11  * This file is part of InspIRCd.  InspIRCd is free software: you can
12  * redistribute it and/or modify it under the terms of the GNU General Public
13  * License as published by the Free Software Foundation, version 2.
14  *
15  * This program is distributed in the hope that it will be useful, but WITHOUT
16  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
22  */
23
24
25 #ifdef _WIN32
26 #define _CRT_RAND_S
27 #include <stdlib.h>
28 #endif
29
30 #include "inspircd.h"
31 #include "xline.h"
32 #include "exitcodes.h"
33 #include <iostream>
34
35 /* Find a user record by nickname and return a pointer to it */
36 User* InspIRCd::FindNick(const std::string &nick)
37 {
38         if (!nick.empty() && isdigit(*nick.begin()))
39                 return FindUUID(nick);
40         return FindNickOnly(nick);
41 }
42
43 User* InspIRCd::FindNickOnly(const std::string &nick)
44 {
45         user_hash::iterator iter = this->Users->clientlist.find(nick);
46
47         if (iter == this->Users->clientlist.end())
48                 return NULL;
49
50         return iter->second;
51 }
52
53 User *InspIRCd::FindUUID(const std::string &uid)
54 {
55         user_hash::iterator finduuid = this->Users->uuidlist.find(uid);
56
57         if (finduuid == this->Users->uuidlist.end())
58                 return NULL;
59
60         return finduuid->second;
61 }
62 /* find a channel record by channel name and return a pointer to it */
63
64 Channel* InspIRCd::FindChan(const std::string &chan)
65 {
66         chan_hash::iterator iter = chanlist.find(chan);
67
68         if (iter == chanlist.end())
69                 /* Couldn't find it */
70                 return NULL;
71
72         return iter->second;
73 }
74
75 bool InspIRCd::IsValidMask(const std::string &mask)
76 {
77         const char* dest = mask.c_str();
78         int exclamation = 0;
79         int atsign = 0;
80
81         for (const char* i = dest; *i; i++)
82         {
83                 /* out of range character, bad mask */
84                 if (*i < 32 || *i > 126)
85                 {
86                         return false;
87                 }
88
89                 switch (*i)
90                 {
91                         case '!':
92                                 exclamation++;
93                                 break;
94                         case '@':
95                                 atsign++;
96                                 break;
97                 }
98         }
99
100         /* valid masks only have 1 ! and @ */
101         if (exclamation != 1 || atsign != 1)
102                 return false;
103
104         if (mask.length() > 250)
105                 return false;
106
107         return true;
108 }
109
110 void InspIRCd::StripColor(std::string &sentence)
111 {
112         /* refactor this completely due to SQUIT bug since the old code would strip last char and replace with \0 --peavey */
113         int seq = 0;
114
115         for (std::string::iterator i = sentence.begin(); i != sentence.end();)
116         {
117                 if (*i == 3)
118                         seq = 1;
119                 else if (seq && (( ((*i >= '0') && (*i <= '9')) || (*i == ',') ) ))
120                 {
121                         seq++;
122                         if ( (seq <= 4) && (*i == ',') )
123                                 seq = 1;
124                         else if (seq > 3)
125                                 seq = 0;
126                 }
127                 else
128                         seq = 0;
129
130                 // Strip all control codes too except \001 for CTCP
131                 if (seq || ((*i >= 0) && (*i < 32) && (*i != 1)))
132                         i = sentence.erase(i);
133                 else
134                         ++i;
135         }
136 }
137
138 void InspIRCd::ProcessColors(file_cache& input)
139 {
140         /*
141          * Replace all color codes from the special[] array to actual
142          * color code chars using C++ style escape sequences. You
143          * can append other chars to replace if you like -- Justasic
144          */
145         static struct special_chars
146         {
147                 std::string character;
148                 std::string replace;
149                 special_chars(const std::string &c, const std::string &r) : character(c), replace(r) { }
150         }
151
152         special[] = {
153                 special_chars("\\002", "\002"),  // Bold
154                 special_chars("\\037", "\037"),  // underline
155                 special_chars("\\003", "\003"),  // Color
156                 special_chars("\\017", "\017"), // Stop colors
157                 special_chars("\\u", "\037"),    // Alias for underline
158                 special_chars("\\b", "\002"),    // Alias for Bold
159                 special_chars("\\x", "\017"),    // Alias for stop
160                 special_chars("\\c", "\003"),    // Alias for color
161                 special_chars("", "")
162         };
163
164         for(file_cache::iterator it = input.begin(), it_end = input.end(); it != it_end; it++)
165         {
166                 std::string ret = *it;
167                 for(int i = 0; special[i].character.empty() == false; ++i)
168                 {
169                         std::string::size_type pos = ret.find(special[i].character);
170                         if(pos == std::string::npos) // Couldn't find the character, skip this line
171                                 continue;
172
173                         if((pos > 0) && (ret[pos-1] == '\\') && (ret[pos] == '\\'))
174                                 continue; // Skip double slashes.
175
176                         // Replace all our characters in the array
177                         while(pos != std::string::npos)
178                         {
179                                 ret = ret.substr(0, pos) + special[i].replace + ret.substr(pos + special[i].character.size());
180                                 pos = ret.find(special[i].character, pos + special[i].replace.size());
181                         }
182                 }
183
184                 // Replace double slashes with a single slash before we return
185                 std::string::size_type pos = ret.find("\\\\");
186                 while(pos != std::string::npos)
187                 {
188                         ret = ret.substr(0, pos) + "\\" + ret.substr(pos + 2);
189                         pos = ret.find("\\\\", pos + 1);
190                 }
191                 *it = ret;
192         }
193 }
194
195 /* true for valid channel name, false else */
196 bool IsChannelHandler::Call(const std::string& chname)
197 {
198         if (chname.empty() || chname.length() > ServerInstance->Config->Limits.ChanMax)
199                 return false;
200
201         if (chname[0] != '#')
202                 return false;
203
204         for (std::string::const_iterator i = chname.begin()+1; i != chname.end(); ++i)
205         {
206                 switch (*i)
207                 {
208                         case ' ':
209                         case ',':
210                         case 7:
211                                 return false;
212                 }
213         }
214
215         return true;
216 }
217
218 /* true for valid nickname, false else */
219 bool IsNickHandler::Call(const std::string& n)
220 {
221         if (n.empty() || n.length() > ServerInstance->Config->Limits.NickMax)
222                 return false;
223
224         for (std::string::const_iterator i = n.begin(); i != n.end(); ++i)
225         {
226                 if ((*i >= 'A') && (*i <= '}'))
227                 {
228                         /* "A"-"}" can occur anywhere in a nickname */
229                         continue;
230                 }
231
232                 if ((((*i >= '0') && (*i <= '9')) || (*i == '-')) && (i != n.begin()))
233                 {
234                         /* "0"-"9", "-" can occur anywhere BUT the first char of a nickname */
235                         continue;
236                 }
237
238                 /* invalid character! abort */
239                 return false;
240         }
241
242         return true;
243 }
244
245 /* return true for good ident, false else */
246 bool IsIdentHandler::Call(const std::string& n)
247 {
248         if (n.empty())
249                 return false;
250
251         for (std::string::const_iterator i = n.begin(); i != n.end(); ++i)
252         {
253                 if ((*i >= 'A') && (*i <= '}'))
254                 {
255                         continue;
256                 }
257
258                 if (((*i >= '0') && (*i <= '9')) || (*i == '-') || (*i == '.'))
259                 {
260                         continue;
261                 }
262
263                 return false;
264         }
265
266         return true;
267 }
268
269 bool InspIRCd::IsSID(const std::string &str)
270 {
271         /* Returns true if the string given is exactly 3 characters long,
272          * starts with a digit, and the other two characters are A-Z or digits
273          */
274         return ((str.length() == 3) && isdigit(str[0]) &&
275                         ((str[1] >= 'A' && str[1] <= 'Z') || isdigit(str[1])) &&
276                          ((str[2] >= 'A' && str[2] <= 'Z') || isdigit(str[2])));
277 }
278
279 void InspIRCd::CheckRoot()
280 {
281 #ifndef _WIN32
282         if (geteuid() == 0)
283         {
284                 std::cout << "ERROR: You are running an irc server as root! DO NOT DO THIS!" << std::endl << std::endl;
285                 this->Logs->Log("STARTUP", LOG_DEFAULT, "Can't start as root");
286                 Exit(EXIT_STATUS_ROOT);
287         }
288 #endif
289 }
290
291 /** Refactored by Brain, Jun 2009. Much faster with some clever O(1) array
292  * lookups and pointer maths.
293  */
294 unsigned long InspIRCd::Duration(const std::string &str)
295 {
296         unsigned char multiplier = 0;
297         long total = 0;
298         long times = 1;
299         long subtotal = 0;
300
301         /* Iterate each item in the string, looking for number or multiplier */
302         for (std::string::const_reverse_iterator i = str.rbegin(); i != str.rend(); ++i)
303         {
304                 /* Found a number, queue it onto the current number */
305                 if ((*i >= '0') && (*i <= '9'))
306                 {
307                         subtotal = subtotal + ((*i - '0') * times);
308                         times = times * 10;
309                 }
310                 else
311                 {
312                         /* Found something thats not a number, find out how much
313                          * it multiplies the built up number by, multiply the total
314                          * and reset the built up number.
315                          */
316                         if (subtotal)
317                                 total += subtotal * duration_multi[multiplier];
318
319                         /* Next subtotal please */
320                         subtotal = 0;
321                         multiplier = *i;
322                         times = 1;
323                 }
324         }
325         if (multiplier)
326         {
327                 total += subtotal * duration_multi[multiplier];
328                 subtotal = 0;
329         }
330         /* Any trailing values built up are treated as raw seconds */
331         return total + subtotal;
332 }
333
334 const char* InspIRCd::Format(va_list &vaList, const char* formatString)
335 {
336         static std::vector<char> formatBuffer(1024);
337
338         while (true)
339         {
340                 va_list dst;
341                 va_copy(dst, vaList);
342
343                 int vsnret = vsnprintf(&formatBuffer[0], formatBuffer.size(), formatString, dst);
344                 va_end(dst);
345
346                 if (vsnret > 0 && static_cast<unsigned>(vsnret) < formatBuffer.size())
347                 {
348                         break;
349                 }
350
351                 formatBuffer.resize(formatBuffer.size() * 2);
352         }
353
354         return &formatBuffer[0];
355 }
356
357 const char* InspIRCd::Format(const char* formatString, ...)
358 {
359         const char* ret;
360         VAFORMAT(ret, formatString, formatString);
361         return ret;
362 }
363
364 std::string InspIRCd::TimeString(time_t curtime, const char* format, bool utc)
365 {
366 #ifdef _WIN32
367         if (curtime < 0)
368                 curtime = 0;
369 #endif
370
371         struct tm* timeinfo = utc ? gmtime(&curtime) : localtime(&curtime);
372         if (!timeinfo)
373         {
374                 curtime = 0;
375                 timeinfo = localtime(&curtime);
376         }
377
378         // If the calculated year exceeds four digits or is less than the year 1000,
379         // the behavior of asctime() is undefined
380         if (timeinfo->tm_year + 1900 > 9999)
381                 timeinfo->tm_year = 9999 - 1900;
382         else if (timeinfo->tm_year + 1900 < 1000)
383                 timeinfo->tm_year = 0;
384
385         // This is the default format used by asctime without the terminating new line.
386         if (!format)
387                 format = "%a %b %d %H:%M:%S %Y";
388
389         char buffer[512];
390         if (!strftime(buffer, sizeof(buffer), format, timeinfo))
391                 buffer[0] = '\0';
392
393         return buffer;
394 }
395
396 std::string InspIRCd::GenRandomStr(int length, bool printable)
397 {
398         char* buf = new char[length];
399         GenRandom(buf, length);
400         std::string rv;
401         rv.resize(length);
402         for(int i=0; i < length; i++)
403                 rv[i] = printable ? 0x3F + (buf[i] & 0x3F) : buf[i];
404         delete[] buf;
405         return rv;
406 }
407
408 // NOTE: this has a slight bias for lower values if max is not a power of 2.
409 // Don't use it if that matters.
410 unsigned long InspIRCd::GenRandomInt(unsigned long max)
411 {
412         unsigned long rv;
413         GenRandom((char*)&rv, sizeof(rv));
414         return rv % max;
415 }
416
417 // This is overridden by a higher-quality algorithm when SSL support is loaded
418 void GenRandomHandler::Call(char *output, size_t max)
419 {
420         for(unsigned int i=0; i < max; i++)
421 #ifdef _WIN32
422         {
423                 unsigned int uTemp;
424                 if(rand_s(&uTemp) != 0)
425                         output[i] = rand();
426                 else
427                         output[i] = uTemp;
428         }
429 #else
430                 output[i] = random();
431 #endif
432 }