X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fmodules%2Fextra%2Fm_mysql.cpp;h=39b0c369d797e0baa9982a3cefdc5676f62f0d97;hb=f471083cd0519d47c7c7a09029813ede41994f7b;hp=ae219df70755b68561d90135d4474af4f01a9bdb;hpb=a5dc76ec96a07211e4b7345e98c919aaca53ca32;p=user%2Fhenk%2Fcode%2Finspircd.git diff --git a/src/modules/extra/m_mysql.cpp b/src/modules/extra/m_mysql.cpp index ae219df70..39b0c369d 100644 --- a/src/modules/extra/m_mysql.cpp +++ b/src/modules/extra/m_mysql.cpp @@ -1,34 +1,50 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ +/* + * InspIRCd -- Internet Relay Chat Daemon * - * InspIRCd: (C) 2002-2010 InspIRCd Development Team - * See: http://wiki.inspircd.org/Credits + * Copyright (C) 2009-2010 Daniel De Graaf + * Copyright (C) 2006-2007, 2009 Dennis Friis + * Copyright (C) 2006-2009 Craig Edwards + * Copyright (C) 2008 Robin Burchell * - * This program is free but copyrighted software; see - * the file COPYING for details. + * This file is part of InspIRCd. InspIRCd is free software: you can + * redistribute it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, version 2. * - * --------------------------------------------------- + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ -/* Stop mysql wanting to use long long */ -#define NO_CLIENT_LONG_LONG +/// $CompilerFlags: execute("mysql_config --include" "MYSQL_CXXFLAGS") +/// $LinkerFlags: execute("mysql_config --libs_r" "MYSQL_LDFLAGS" "-lmysqlclient") + +/// $PackageInfo: require_system("centos" "6.0" "6.99") mysql-devel +/// $PackageInfo: require_system("centos" "7.0") mariadb-devel +/// $PackageInfo: require_system("darwin") mysql-connector-c +/// $PackageInfo: require_system("ubuntu") libmysqlclient-dev + + +// Fix warnings about the use of `long long` on C++03. +#if defined __clang__ +# pragma clang diagnostic ignored "-Wc++11-long-long" +#elif defined __GNUC__ +# pragma GCC diagnostic ignored "-Wlong-long" +#endif #include "inspircd.h" #include -#include "sql.h" +#include "modules/sql.h" -#ifdef WINDOWS -#pragma comment(lib, "mysqlclient.lib") +#ifdef _WIN32 +# pragma comment(lib, "libmysql.lib") #endif /* VERSION 3 API: With nonblocking (threaded) requests */ -/* $ModDesc: SQL Service Provider module for all other m_sql* modules */ -/* $CompileFlags: exec("mysql_config --include") */ -/* $LinkerFlags: exec("mysql_config --libs_r") rpath("mysql_config --libs_r") */ -/* $ModDep: m_sqlv2.h */ - /* THE NONBLOCKING MYSQL API! * * MySQL provides no nonblocking (asyncronous) API of its own, and its developers recommend @@ -70,8 +86,9 @@ class DispatcherThread; struct QQueueItem { SQLQuery* q; + std::string query; SQLConnection* c; - QQueueItem(SQLQuery* Q, SQLConnection* C) : q(Q), c(C) {} + QQueueItem(SQLQuery* Q, const std::string& S, SQLConnection* C) : q(Q), query(S), c(C) {} }; struct RQueueItem @@ -81,7 +98,7 @@ struct RQueueItem RQueueItem(SQLQuery* Q, MySQLresult* R) : q(Q), r(R) {} }; -typedef std::map ConnMap; +typedef insp::flat_map ConnMap; typedef std::deque QueryQueue; typedef std::deque ResultQueue; @@ -96,11 +113,11 @@ class ModuleSQL : public Module ConnMap connections; // main thread only ModuleSQL(); - void init(); + void init() CXX11_OVERRIDE; ~ModuleSQL(); - void OnRehash(User* user); - void OnUnloadModule(Module* mod); - Version GetVersion(); + void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE; + void OnUnloadModule(Module* mod) CXX11_OVERRIDE; + Version GetVersion() CXX11_OVERRIDE; }; class DispatcherThread : public SocketThread @@ -110,8 +127,8 @@ class DispatcherThread : public SocketThread public: DispatcherThread(ModuleSQL* CreatorModule) : Parent(CreatorModule) { } ~DispatcherThread() { } - virtual void Run(); - virtual void OnNotify(); + void Run(); + void OnNotify(); }; #if !defined(MYSQL_VERSION_ID) || MYSQL_VERSION_ID<32224 @@ -169,7 +186,6 @@ class MySQLresult : public SQLResult rows++; } mysql_free_result(res); - res = NULL; } } @@ -178,21 +194,17 @@ class MySQLresult : public SQLResult } - ~MySQLresult() - { - } - - virtual int Rows() + int Rows() { return rows; } - virtual void GetCols(std::vector& result) + void GetCols(std::vector& result) { result.assign(colnames.begin(), colnames.end()); } - virtual SQLEntry GetValue(int row, int column) + SQLEntry GetValue(int row, int column) { if ((row >= 0) && (row < rows) && (column >= 0) && (column < (int)fieldlists[row].size())) { @@ -201,7 +213,7 @@ class MySQLresult : public SQLResult return SQLEntry(); } - virtual bool GetRow(SQLEntries& result) + bool GetRow(SQLEntries& result) { if (currentrow < rows) { @@ -228,7 +240,7 @@ class SQLConnection : public SQLProvider // This constructor creates an SQLConnection object with the given credentials, but does not connect yet. SQLConnection(Module* p, ConfigTag* tag) : SQLProvider(p, "SQL/" + tag->getString("id")), - config(tag) + config(tag), connection(NULL) { } @@ -252,6 +264,12 @@ class SQLConnection : public SQLProvider bool rv = mysql_real_connect(connection, host.c_str(), user.c_str(), pass.c_str(), dbname.c_str(), port, NULL, 0); if (!rv) return rv; + + // Enable character set settings + std::string charset = config->getString("charset"); + if ((!charset.empty()) && (mysql_set_character_set(connection, charset.c_str()))) + ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: Could not set character set to \"%s\"", charset.c_str()); + std::string initquery; if (config->readString("initialquery", initquery)) { @@ -260,68 +278,16 @@ class SQLConnection : public SQLProvider return true; } - virtual std::string FormatQuery(const std::string& q, const ParamL& p) - { - std::string res; - unsigned int param = 0; - for(std::string::size_type i = 0; i < q.length(); i++) - { - if (q[i] != '?') - res.push_back(q[i]); - else - { - // TODO numbered parameter support ('?1') - if (param < p.size()) - { - std::string parm = p[param++]; - char buffer[MAXBUF]; - mysql_escape_string(buffer, parm.c_str(), parm.length()); -// mysql_real_escape_string(connection, queryend, paramscopy[paramnum].c_str(), paramscopy[paramnum].length()); - res.append(buffer); - } - } - } - return res; - } - - std::string FormatQuery(const std::string& q, const ParamM& p) - { - std::string res; - for(std::string::size_type i = 0; i < q.length(); i++) - { - if (q[i] != '$') - res.push_back(q[i]); - else - { - std::string field; - i++; - while (i < q.length() && isalpha(q[i])) - field.push_back(q[i++]); - i--; - - ParamM::const_iterator it = p.find(field); - if (it != p.end()) - { - std::string parm = it->second; - char buffer[MAXBUF]; - mysql_escape_string(buffer, parm.c_str(), parm.length()); - res.append(buffer); - } - } - } - return res; - } - ModuleSQL* Parent() { return (ModuleSQL*)(Module*)creator; } - MySQLresult* DoBlockingQuery(SQLQuery* req) + MySQLresult* DoBlockingQuery(const std::string& query) { /* Parse the command string and dispatch it to mysql */ - if (CheckConnection() && !mysql_real_query(connection, req->query.data(), req->query.length())) + if (CheckConnection() && !mysql_real_query(connection, query.data(), query.length())) { /* Successfull query */ MYSQL_RES* res = mysql_use_result(connection); @@ -332,18 +298,16 @@ class SQLConnection : public SQLProvider { /* XXX: See /usr/include/mysql/mysqld_error.h for a list of * possible error numbers and error messages */ - SQLerror e(SQL_QREPLY_FAIL, ConvToStr(mysql_errno(connection)) + std::string(": ") + mysql_error(connection)); + SQLerror e(SQL_QREPLY_FAIL, ConvToStr(mysql_errno(connection)) + ": " + mysql_error(connection)); return new MySQLresult(e); } } bool CheckConnection() { - if (mysql_ping(connection) != 0) - { + if (!connection || mysql_ping(connection) != 0) return Connect(); - } - else return true; + return true; } std::string GetError() @@ -356,12 +320,69 @@ class SQLConnection : public SQLProvider mysql_close(connection); } - void submit(SQLQuery* q) + void submit(SQLQuery* q, const std::string& qs) { Parent()->Dispatcher->LockQueue(); - Parent()->qq.push_back(QQueueItem(q, this)); + Parent()->qq.push_back(QQueueItem(q, qs, this)); Parent()->Dispatcher->UnlockQueueWakeup(); } + + void submit(SQLQuery* call, const std::string& q, const ParamL& p) + { + std::string res; + unsigned int param = 0; + for(std::string::size_type i = 0; i < q.length(); i++) + { + if (q[i] != '?') + res.push_back(q[i]); + else + { + if (param < p.size()) + { + std::string parm = p[param++]; + // In the worst case, each character may need to be encoded as using two bytes, + // and one byte is the terminating null + std::vector buffer(parm.length() * 2 + 1); + + // The return value of mysql_escape_string() is the length of the encoded string, + // not including the terminating null + unsigned long escapedsize = mysql_escape_string(&buffer[0], parm.c_str(), parm.length()); +// mysql_real_escape_string(connection, queryend, paramscopy[paramnum].c_str(), paramscopy[paramnum].length()); + res.append(&buffer[0], escapedsize); + } + } + } + submit(call, res); + } + + void submit(SQLQuery* call, const std::string& q, const ParamM& p) + { + std::string res; + for(std::string::size_type i = 0; i < q.length(); i++) + { + if (q[i] != '$') + res.push_back(q[i]); + else + { + std::string field; + i++; + while (i < q.length() && isalnum(q[i])) + field.push_back(q[i++]); + i--; + + ParamM::const_iterator it = p.find(field); + if (it != p.end()) + { + std::string parm = it->second; + // NOTE: See above + std::vector buffer(parm.length() * 2 + 1); + unsigned long escapedsize = mysql_escape_string(&buffer[0], parm.c_str(), parm.length()); + res.append(&buffer[0], escapedsize); + } + } + } + submit(call, res); + } }; ModuleSQL::ModuleSQL() @@ -372,10 +393,7 @@ ModuleSQL::ModuleSQL() void ModuleSQL::init() { Dispatcher = new DispatcherThread(this); - ServerInstance->Threads->Start(Dispatcher); - - Implementation eventlist[] = { I_OnRehash, I_OnUnloadModule }; - ServerInstance->Modules->Attach(eventlist, this, 2); + ServerInstance->Threads.Start(Dispatcher); } ModuleSQL::~ModuleSQL() @@ -392,7 +410,7 @@ ModuleSQL::~ModuleSQL() } } -void ModuleSQL::OnRehash(User* user) +void ModuleSQL::ReadConfig(ConfigStatus& status) { ConnMap conns; ConfigTagList tags = ServerInstance->Config->ConfTags("database"); @@ -425,13 +443,14 @@ void ModuleSQL::OnRehash(User* user) i->second->lock.Lock(); i->second->lock.Unlock(); // now remove all active queries to this DB - for(unsigned int j = qq.size() - 1; j >= 0; j--) + for (size_t j = qq.size(); j > 0; j--) { - if (qq[j].c == i->second) + size_t k = j - 1; + if (qq[k].c == i->second) { - qq[j].q->OnError(err); - delete qq[j].q; - qq.erase(qq.begin() + j); + qq[k].q->OnError(err); + delete qq[k].q; + qq.erase(qq.begin() + k); } } // finally, nuke the connection @@ -445,8 +464,10 @@ void ModuleSQL::OnUnloadModule(Module* mod) { SQLerror err(SQL_BAD_DBID); Dispatcher->LockQueue(); - for(unsigned int i = qq.size() - 1; i >= 0; i--) + unsigned int i = qq.size(); + while (i > 0) { + i--; if (qq[i].q->creator == mod) { if (i == 0) @@ -481,7 +502,7 @@ void DispatcherThread::Run() QQueueItem i = Parent->qq.front(); i.c->lock.Lock(); this->UnlockQueue(); - MySQLresult* res = i.c->DoBlockingQuery(i.q); + MySQLresult* res = i.c->DoBlockingQuery(i.query); i.c->lock.Unlock(); /* @@ -491,7 +512,7 @@ void DispatcherThread::Run() */ this->LockQueue(); - if (Parent->qq.front().q == i.q) + if (!Parent->qq.empty() && Parent->qq.front().q == i.q) { Parent->qq.pop_front(); Parent->rq.push_back(RQueueItem(i.q, res));