misc.cpp

Go to the documentation of this file.
00001 /* Miscellaneous routines.
00002  *
00003  * (C) 2003-2013 Anope Team
00004  * Contact us at team@anope.org
00005  *
00006  * Please read COPYING and README for further details.
00007  *
00008  * Based on the original code of Epona by Lara.
00009  * Based on the original code of Services by Andy Church.
00010  *
00011  */
00012 
00013 #include "services.h"
00014 #include "version.h"
00015 #include "modules.h"
00016 #include "lists.h"
00017 #include "config.h"
00018 #include "bots.h"
00019 #include "language.h"
00020 #include "regexpr.h"
00021 #include "sockets.h"
00022 
00023 #include <errno.h>
00024 #include <sys/types.h>
00025 #include <sys/stat.h>
00026 #ifndef _WIN32
00027 #include <sys/socket.h>
00028 #include <netdb.h>
00029 #endif
00030 
00031 NumberList::NumberList(const Anope::string &list, bool descending) : is_valid(true), desc(descending)
00032 {
00033         Anope::string error;
00034         commasepstream sep(list);
00035         Anope::string token;
00036 
00037         sep.GetToken(token);
00038         if (token.empty())
00039                 token = list;
00040         do
00041         {
00042                 size_t t = token.find('-');
00043 
00044                 if (t == Anope::string::npos)
00045                 {
00046                         unsigned num = convertTo<unsigned>(token, error, false);
00047                         if (error.empty())
00048                                 numbers.insert(num);
00049                         else
00050                         {
00051                                 if (!this->InvalidRange(list))
00052                                 {
00053                                         is_valid = false;
00054                                         return;
00055                                 }
00056                         }
00057                 }
00058                 else
00059                 {
00060                         Anope::string error2;
00061                         unsigned num1 = convertTo<unsigned>(token.substr(0, t), error, false);
00062                         unsigned num2 = convertTo<unsigned>(token.substr(t + 1), error2, false);
00063                         if (error.empty() && error2.empty())
00064                         {
00065                                 for (unsigned i = num1; i <= num2; ++i)
00066                                         numbers.insert(i);
00067                         }
00068                         else
00069                         {
00070                                 if (!this->InvalidRange(list))
00071                                 {
00072                                         is_valid = false;
00073                                         return;
00074                                 }
00075                         }
00076                 }
00077         } while (sep.GetToken(token));
00078 }
00079 
00080 NumberList::~NumberList()
00081 {
00082 }
00083 
00084 void NumberList::Process()
00085 {
00086         if (!is_valid)
00087                 return;
00088 
00089         if (this->desc)
00090         {
00091                 for (std::set<unsigned>::reverse_iterator it = numbers.rbegin(), it_end = numbers.rend(); it != it_end; ++it)
00092                         this->HandleNumber(*it);
00093         }
00094         else
00095         {
00096                 for (std::set<unsigned>::iterator it = numbers.begin(), it_end = numbers.end(); it != it_end; ++it)
00097                         this->HandleNumber(*it);
00098         }
00099 }
00100 
00101 void NumberList::HandleNumber(unsigned)
00102 {
00103 }
00104 
00105 bool NumberList::InvalidRange(const Anope::string &)
00106 {
00107         return true;
00108 }
00109 
00110 ListFormatter &ListFormatter::AddColumn(const Anope::string &name)
00111 {
00112         this->columns.push_back(name);
00113         return *this;
00114 }
00115 
00116 void ListFormatter::AddEntry(const ListEntry &entry)
00117 {
00118         this->entries.push_back(entry);
00119 }
00120 
00121 bool ListFormatter::IsEmpty() const
00122 {
00123         return this->entries.empty();
00124 }
00125 
00126 void ListFormatter::Process(std::vector<Anope::string> &buffer)
00127 {
00128         std::map<Anope::string, size_t> lenghts;
00129         std::set<Anope::string> breaks;
00130         for (unsigned i = 0; i < this->columns.size(); ++i)
00131                 lenghts[this->columns[i]] = this->columns[i].length();
00132         for (unsigned i = 0; i < this->entries.size(); ++i)
00133         {
00134                 ListEntry &e = this->entries[i];
00135                 for (unsigned j = 0; j < this->columns.size(); ++j)
00136                         if (e[this->columns[j]].length() > lenghts[this->columns[j]])
00137                                 lenghts[this->columns[j]] = e[this->columns[j]].length();
00138         }
00139         unsigned length = 0;
00140         for (std::map<Anope::string, size_t>::iterator it = lenghts.begin(), it_end = lenghts.end(); it != it_end; ++it)
00141         {
00142                 /* Break lines at 80 chars */
00143                 if (length > 80)
00144                 {
00145                         breaks.insert(it->first);
00146                         length = 0;
00147                 }
00148                 else
00149                         length += it->second;
00150         }
00151 
00152         /* Only put a list header if more than 1 column */
00153         if (this->columns.size() > 1)
00154         {
00155                 Anope::string s;
00156                 for (unsigned i = 0; i < this->columns.size(); ++i)
00157                 {
00158                         if (breaks.count(this->columns[i]))
00159                         {
00160                                 buffer.push_back(s);
00161                                 s = "  ";
00162                         }
00163                         else if (!s.empty())
00164                                 s += "  ";
00165                         s += this->columns[i];
00166                         if (i + 1  != this->columns.size())
00167                                 for (unsigned j = this->columns[i].length(); j < lenghts[this->columns[i]]; ++j)
00168                                         s += " ";
00169                 }
00170                 buffer.push_back(s);
00171         }
00172 
00173         for (unsigned i = 0; i < this->entries.size(); ++i)
00174         {
00175                 ListEntry &e = this->entries[i];
00176 
00177                 Anope::string s;
00178                 for (unsigned j = 0; j < this->columns.size(); ++j)
00179                 {
00180                         if (breaks.count(this->columns[j]))
00181                         {
00182                                 buffer.push_back(s);
00183                                 s = "  ";
00184                         }
00185                         else if (!s.empty())
00186                                 s += "  ";
00187                         s += e[this->columns[j]];
00188                         if (j + 1 != this->columns.size())
00189                                 for (unsigned k = e[this->columns[j]].length(); k < lenghts[this->columns[j]]; ++k)
00190                                         s += " ";
00191                 }
00192                 buffer.push_back(s);
00193         }
00194 }
00195 
00196 InfoFormatter::InfoFormatter(NickCore *acc) : nc(acc), longest(0)
00197 {
00198 }
00199 
00200 void InfoFormatter::Process(std::vector<Anope::string> &buffer)
00201 {
00202         buffer.clear();
00203 
00204         for (std::vector<std::pair<Anope::string, Anope::string> >::iterator it = this->replies.begin(), it_end = this->replies.end(); it != it_end; ++it)
00205         {
00206                 Anope::string s;
00207                 for (unsigned i = it->first.length(); i < this->longest; ++i)
00208                         s += " ";
00209                 s += Anope::string(Language::Translate(this->nc, it->first.c_str())) + ": " + it->second;
00210 
00211                 buffer.push_back(s);
00212         }
00213 }
00214 
00215 Anope::string& InfoFormatter::operator[](const Anope::string &key)
00216 {
00217         if (key.length() > this->longest)
00218                 this->longest = key.length();
00219         this->replies.push_back(std::make_pair(key, ""));
00220         return this->replies.back().second;
00221 }
00222 
00223 bool Anope::IsFile(const Anope::string &filename)
00224 {
00225         struct stat fileinfo;
00226         if (!stat(filename.c_str(), &fileinfo))
00227                 return true;
00228 
00229         return false;
00230 }
00231 
00232 time_t Anope::DoTime(const Anope::string &s)
00233 {
00234         if (s.empty())
00235                 return -1;
00236 
00237         int amount = 0;
00238         Anope::string end;
00239 
00240         try
00241         {
00242                 amount = convertTo<int>(s, end, false);
00243                 if (!end.empty())
00244                 {
00245                         switch (end[0])
00246                         {
00247                                 case 's':
00248                                         return amount;
00249                                 case 'm':
00250                                         return amount * 60;
00251                                 case 'h':
00252                                         return amount * 3600;
00253                                 case 'd':
00254                                         return amount * 86400;
00255                                 case 'w':
00256                                         return amount * 86400 * 7;
00257                                 case 'y':
00258                                         return amount * 86400 * 365;
00259                                 default:
00260                                         return -1;
00261                         }
00262                 }
00263         }
00264         catch (const ConvertException &) { }
00265 
00266         return amount;
00267 }
00268 
00269 Anope::string Anope::Duration(time_t t, const NickCore *nc)
00270 {
00271         /* We first calculate everything */
00272         time_t years = t / 31536000;
00273         time_t days = (t / 86400) % 365;
00274         time_t hours = (t / 3600) % 24;
00275         time_t minutes = (t / 60) % 60;
00276         time_t seconds = (t) % 60;
00277 
00278         if (!days && !hours && !minutes)
00279                 return stringify(seconds) + " " + (seconds != 1 ? Language::Translate(nc, _("seconds")) : Language::Translate(nc, _("second")));
00280         else
00281         {
00282                 bool need_comma = false;
00283                 Anope::string buffer;
00284                 if (years)
00285                 {
00286                         buffer = stringify(years) + " " + (years != 1 ? Language::Translate(nc, _("years")) : Language::Translate(nc, _("year")));
00287                         need_comma = true;
00288                 }
00289                 if (days)
00290                 {
00291                         buffer += need_comma ? ", " : "";
00292                         buffer += stringify(days) + " " + (days != 1 ? Language::Translate(nc, _("days")) : Language::Translate(nc, _("day")));
00293                         need_comma = true;
00294                 }
00295                 if (hours)
00296                 {
00297                         buffer += need_comma ? ", " : "";
00298                         buffer += stringify(hours) + " " + (hours != 1 ? Language::Translate(nc, _("hours")) : Language::Translate(nc, _("hour")));
00299                         need_comma = true;
00300                 }
00301                 if (minutes)
00302                 {
00303                         buffer += need_comma ? ", " : "";
00304                         buffer += stringify(minutes) + " " + (minutes != 1 ? Language::Translate(nc, _("minutes")) : Language::Translate(nc, _("minute")));
00305                 }
00306                 return buffer;
00307         }
00308 }
00309 
00310 Anope::string Anope::strftime(time_t t, const NickCore *nc, bool short_output)
00311 {
00312         tm tm = *localtime(&t);
00313         char buf[BUFSIZE];
00314         strftime(buf, sizeof(buf), Language::Translate(nc, _("%b %d %H:%M:%S %Y %Z")), &tm);
00315         if (short_output)
00316                 return buf;
00317         if (t < Anope::CurTime)
00318                 return Anope::string(buf) + " " + Anope::printf(Language::Translate(nc, _("(%s ago)")), Duration(Anope::CurTime - t).c_str(), nc);
00319         else
00320                 return Anope::string(buf) + " " + Anope::printf(Language::Translate(nc, _("(%s from now)")), Duration(t - Anope::CurTime).c_str(), nc);
00321 }
00322 
00323 Anope::string Anope::Expires(time_t expires, const NickCore *nc)
00324 {
00325         if (!expires)
00326                 return Language::Translate(nc, NO_EXPIRE);
00327         else if (expires <= Anope::CurTime)
00328                 return Language::Translate(nc, _("expires momentarily"));
00329         else
00330         {
00331                 char buf[256];
00332                 time_t diff = expires - Anope::CurTime + 59;
00333 
00334                 if (diff >= 86400)
00335                 {
00336                         int days = diff / 86400;
00337                         snprintf(buf, sizeof(buf), Language::Translate(nc, days == 1 ? _("expires in %d day") : _("expires in %d days")), days);
00338                 }
00339                 else
00340                 {
00341                         if (diff <= 3600)
00342                         {
00343                                 int minutes = diff / 60;
00344                                 snprintf(buf, sizeof(buf), Language::Translate(nc, minutes == 1 ? _("expires in %d minute") : _("expires in %d minutes")), minutes);
00345                         }
00346                         else
00347                         {
00348                                 int hours = diff / 3600, minutes;
00349                                 diff -= hours * 3600;
00350                                 minutes = diff / 60;
00351                                 snprintf(buf, sizeof(buf), Language::Translate(nc, hours == 1 && minutes == 1 ? _("expires in %d hour, %d minute") : (hours == 1 && minutes != 1 ? _("expires in %d hour, %d minutes") : (hours != 1 && minutes == 1 ? _("expires in %d hours, %d minute") : _("expires in %d hours, %d minutes")))), hours, minutes);
00352                         }
00353                 }
00354 
00355                 return buf;
00356         }
00357 }
00358 
00359 bool Anope::Match(const Anope::string &str, const Anope::string &mask, bool case_sensitive, bool use_regex)
00360 {
00361         size_t s = 0, m = 0, str_len = str.length(), mask_len = mask.length();
00362 
00363         if (use_regex && mask_len >= 2 && mask[0] == '/' && mask[mask.length() - 1] == '/')
00364         {
00365                 Anope::string stripped_mask = mask.substr(1, mask_len - 2);
00366                 // This is often called with the same mask multiple times in a row, so cache it
00367                 static Regex *r = NULL;
00368 
00369                 if (r == NULL || r->GetExpression() != stripped_mask)
00370                 {
00371                         ServiceReference<RegexProvider> provider("Regex", Config->RegexEngine);
00372                         if (provider)
00373                         {
00374                                 try
00375                                 {
00376                                         delete r;
00377                                         r = NULL;
00378                                         // This may throw
00379                                         r = provider->Compile(stripped_mask);
00380                                 }
00381                                 catch (const RegexException &ex)
00382                                 {
00383                                         Log(LOG_DEBUG) << ex.GetReason();
00384                                 }
00385                         }
00386                         else
00387                         {
00388                                 delete r;
00389                                 r = NULL;
00390                         }
00391                 }
00392 
00393                 if (r != NULL && r->Matches(str))
00394                         return true;
00395 
00396                 // Fall through to non regex match
00397         }
00398 
00399         while (s < str_len && m < mask_len && mask[m] != '*')
00400         {
00401                 char string = str[s], wild = mask[m];
00402                 if (case_sensitive)
00403                 {
00404                         if (wild != string && wild != '?')
00405                                 return false;
00406                 }
00407                 else
00408                 {
00409                         if (tolower(wild) != tolower(string) && wild != '?')
00410                                 return false;
00411                 }
00412 
00413                 ++m;
00414                 ++s;
00415         }
00416 
00417         size_t sp = Anope::string::npos, mp = Anope::string::npos;
00418         while (s < str_len)
00419         {
00420                 char string = str[s], wild = mask[m];
00421                 if (wild == '*')
00422                 {
00423                         if (++m == mask_len)
00424                                 return 1;
00425 
00426                         mp = m;
00427                         sp = s + 1;
00428                 }
00429                 else if (case_sensitive)
00430                 {
00431                         if (wild == string || wild == '?')
00432                         {
00433                                 ++m;
00434                                 ++s;
00435                         }
00436                         else
00437                         {
00438                                 m = mp;
00439                                 s = sp++;
00440                         }
00441                 }
00442                 else
00443                 {
00444                         if (tolower(wild) == tolower(string) || wild == '?')
00445                         {
00446                                 ++m;
00447                                 ++s;
00448                         }
00449                         else
00450                         {
00451                                 m = mp;
00452                                 s = sp++;
00453                         }
00454                 }
00455         }
00456 
00457         if (m < mask_len && mask[m] == '*')
00458                 ++m;
00459 
00460         return m == mask_len;
00461 }
00462 
00463 void Anope::Encrypt(const Anope::string &src, Anope::string &dest)
00464 {
00465         EventReturn MOD_RESULT;
00466         FOREACH_RESULT(I_OnEncrypt, OnEncrypt(src, dest));
00467 }
00468 
00469 bool Anope::Decrypt(const Anope::string &src, Anope::string &dest)
00470 {
00471         size_t pos = src.find(':');
00472         if (pos == Anope::string::npos)
00473         {
00474                 Log() << "Error: Anope::Decrypt() called with invalid password string (" << src << ")";
00475                 return false;
00476         }
00477         Anope::string hashm(src.begin(), src.begin() + pos);
00478 
00479         EventReturn MOD_RESULT;
00480         FOREACH_RESULT(I_OnDecrypt, OnDecrypt(hashm, src, dest));
00481         if (MOD_RESULT == EVENT_ALLOW)
00482                 return true;
00483 
00484         return false;
00485 }
00486 
00487 Anope::string Anope::printf(const char *fmt, ...)
00488 {
00489         va_list args;
00490         char buf[1024];
00491         va_start(args, fmt);
00492         vsnprintf(buf, sizeof(buf), fmt, args);
00493         va_end(args);
00494         return buf;
00495 }
00496 
00497 Anope::string Anope::Hex(const Anope::string &data)
00498 {
00499         const char hextable[] = "0123456789abcdef";
00500 
00501         size_t l = data.length();
00502         Anope::string rv;
00503         for (size_t i = 0; i < l; ++i)
00504         {
00505                 unsigned char c = data[i];
00506                 rv += hextable[c >> 4];
00507                 rv += hextable[c & 0xF];
00508         }
00509         return rv;
00510 }
00511 
00512 Anope::string Anope::Hex(const char *data, unsigned len)
00513 {
00514         const char hextable[] = "0123456789abcdef";
00515 
00516         Anope::string rv;
00517         for (size_t i = 0; i < len; ++i)
00518         {
00519                 unsigned char c = data[i];
00520                 rv += hextable[c >> 4];
00521                 rv += hextable[c & 0xF];
00522         }
00523         return rv;
00524 }
00525 
00526 void Anope::Unhex(const Anope::string &src, Anope::string &dest)
00527 {
00528         size_t len = src.length();
00529         Anope::string rv;
00530         for (size_t i = 0; i + 1 < len; i += 2)
00531         {
00532                 char h = std::tolower(src[i], Anope::casemap), l = std::tolower(src[i + 1], Anope::casemap);
00533                 unsigned char byte = (h >= 'a' ? h - 'a' + 10 : h - '0') << 4;
00534                 byte += (l >= 'a' ? l - 'a' + 10 : l - '0');
00535                 rv += byte;
00536         }
00537         dest = rv;
00538 }
00539 
00540 void Anope::Unhex(const Anope::string &src, char *dest, size_t sz)
00541 {
00542         Anope::string d;
00543         Anope::Unhex(src, d);
00544 
00545         memcpy(dest, d.c_str(), std::min(d.length() + 1, sz));
00546 }
00547 
00548 int Anope::LastErrorCode()
00549 {
00550 #ifndef _WIN32
00551         return errno;
00552 #else
00553         return GetLastError();
00554 #endif
00555 }
00556 
00557 const Anope::string Anope::LastError()
00558 {
00559 #ifndef _WIN32
00560         return strerror(errno);
00561 #else
00562         char errbuf[513];
00563         DWORD err = GetLastError();
00564         if (!err)
00565                 return "";
00566         FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, 0, errbuf, 512, NULL);
00567         return errbuf;
00568 #endif
00569 }
00570 
00571 ModuleVersion Module::GetVersion() const
00572 {
00573         return ModuleVersion(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
00574 }
00575 
00576 Anope::string Anope::Version()
00577 {
00578 #ifdef VERSION_GIT
00579         return stringify(VERSION_MAJOR) + "." + stringify(VERSION_MINOR) + "." + stringify(VERSION_PATCH) + VERSION_EXTRA + " (" + VERSION_GIT + ")";
00580 #else
00581         return stringify(VERSION_MAJOR) + "." + stringify(VERSION_MINOR) + "." + stringify(VERSION_PATCH) + VERSION_EXTRA;
00582 #endif
00583 }
00584 
00585 Anope::string Anope::VersionShort()
00586 {
00587         return stringify(VERSION_MAJOR) + "." + stringify(VERSION_MINOR) + "." + stringify(VERSION_PATCH);
00588 }
00589 
00590 Anope::string Anope::VersionBuildString()
00591 {
00592         Anope::string s = "build #" + stringify(BUILD) + ", compiled " + Anope::compiled;
00593         Anope::string flags;
00594 
00595 #ifdef DEBUG_BUILD
00596         flags += "D";
00597 #endif
00598 #ifdef VERSION_GIT
00599         flags += "G";
00600 #endif
00601 #ifdef _WIN32
00602         flags += "W";
00603 #endif
00604 
00605         if (!flags.empty())
00606                 s += ", flags " + flags;
00607         
00608         return s;
00609 }
00610 
00611 int Anope::VersionMajor() { return VERSION_MAJOR; }
00612 int Anope::VersionMinor() { return VERSION_MINOR; }
00613 int Anope::VersionPatch() { return VERSION_PATCH; }
00614 
00615 Anope::string Anope::NormalizeBuffer(const Anope::string &buf)
00616 {
00617         Anope::string newbuf;
00618 
00619         for (unsigned i = 0, end = buf.length(); i < end; ++i)
00620         {
00621                 switch (buf[i])
00622                 {
00623                         /* ctrl char */
00624                         case 1:
00625                         /* Bold ctrl char */
00626                         case 2:
00627                                 break;
00628                         /* Color ctrl char */
00629                         case 3:
00630                                 /* If the next character is a digit, its also removed */
00631                                 if (isdigit(buf[i + 1]))
00632                                 {
00633                                         ++i;
00634 
00635                                         /* not the best way to remove colors
00636                                          * which are two digit but no worse then
00637                                          * how the Unreal does with +S - TSL
00638                                          */
00639                                         if (isdigit(buf[i + 1]))
00640                                                 ++i;
00641 
00642                                         /* Check for background color code
00643                                          * and remove it as well
00644                                          */
00645                                         if (buf[i + 1] == ',')
00646                                         {
00647                                                 ++i;
00648 
00649                                                 if (isdigit(buf[i + 1]))
00650                                                         ++i;
00651                                                 /* not the best way to remove colors
00652                                                  * which are two digit but no worse then
00653                                                  * how the Unreal does with +S - TSL
00654                                                  */
00655                                                 if (isdigit(buf[i + 1]))
00656                                                         ++i;
00657                                         }
00658                                 }
00659 
00660                                 break;
00661                         /* line feed char */
00662                         case 10:
00663                         /* carriage returns char */
00664                         case 13:
00665                         /* Reverse ctrl char */
00666                         case 22:
00667                         /* Italic ctrl char */
00668                         case 29:
00669                         /* Underline ctrl char */
00670                         case 31:
00671                                 break;
00672                         /* A valid char gets copied into the new buffer */
00673                         default:
00674                                 newbuf += buf[i];
00675                 }
00676         }
00677 
00678         return newbuf;
00679 }
00680 
00681 Anope::string Anope::Resolve(const Anope::string &host, int type)
00682 {
00683         Anope::string result = host;
00684 
00685         addrinfo hints;
00686         memset(&hints, 0, sizeof(hints));
00687         hints.ai_family = type;
00688 
00689         Log(LOG_DEBUG_2) << "Resolver: BlockingQuery: Looking up " << host;
00690 
00691         addrinfo *addrresult = NULL;
00692         if (getaddrinfo(host.c_str(), NULL, &hints, &addrresult) == 0)
00693         {
00694                 sockaddrs addr;
00695                 memcpy(&addr, addrresult->ai_addr, addrresult->ai_addrlen);
00696                 try
00697                 {
00698                         result = addr.addr();
00699                         Log(LOG_DEBUG_2) << "Resolver: " << host << " -> " << result;
00700                 }
00701                 catch (const SocketException &) { }
00702 
00703                 freeaddrinfo(addrresult);
00704         }
00705 
00706         return result;
00707 }
00708