/* * UserBackendMysql.cpp * * Copyright (C) 2008 Matthias Schiffer * * This program 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, either version 3 of the License, or * (at your option) any later version. * * 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 . */ #include #include "UserBackendMysql.h" #include #include #include #include #include #include #include #include namespace Mad { namespace Modules { namespace UserBackendMysql { bool UserBackendMysql::handleConfigEntry(const Core::ConfigEntry &entry, bool handled) { if(handled) return false; if(entry[0].getKey().matches("UserBackendMysql")) { if(entry[1].getKey().matches("Host")) { if(entry[2].empty()) host = entry[1][0]; } else if(entry[1].getKey().matches("Username")) { if(entry[2].empty()) username = entry[1][0]; } else if(entry[1].getKey().matches("Password")) { if(entry[2].empty()) passwd = entry[1][0]; } else if(entry[1].getKey().matches("Database")) { if(entry[2].empty()) db = entry[1][0]; } else if(entry[1].getKey().matches("Port")) { if(entry[2].empty()) { char *endptr; long val; val = strtol(entry[1][0].c_str(), &endptr, 10); if(endptr != 0 || val < 0 || val > 65535) application->log(Core::LoggerBase::WARNING, "UserBackendMysql: Invalid port"); else port = val; } } else if(entry[1].getKey().matches("UnixSocket")) { if(entry[2].empty()) unixSocket = entry[1][0]; } else if(entry[1].getKey().matches("Queries")) { if(entry[2].getKey().matches("ListUsers")) { if(entry[3].empty()) queryListUsers = entry[2][0]; } else if(entry[2].getKey().matches("ListGroups")) { if(entry[3].empty()) queryListGroups = entry[2][0]; } else if(entry[2].getKey().matches("ListUserGroups")) { if(entry[3].empty()) queryListUserGroups = entry[2][0]; } else if(entry[2].getKey().matches("ListGroupUsers")) { if(entry[3].empty()) queryListGroupUsers = entry[2][0]; } else if(entry[2].getKey().matches("UserById")) { if(entry[3].empty()) queryUserById = entry[2][0]; } else if(entry[2].getKey().matches("UserByName")) { if(entry[3].empty()) queryUserByName = entry[2][0]; } else if(entry[2].getKey().matches("GroupById")) { if(entry[3].empty()) queryGroupById = entry[2][0]; } else if(entry[2].getKey().matches("GroupByName")) { if(entry[3].empty()) queryGroupByName = entry[2][0]; } else if(entry[2].getKey().matches("UserGroupTable")) { if(entry[3].empty()) queryUserGroupTable = entry[2][0]; } else if(entry[2].getKey().matches("AddUser")) { if(entry[3].empty()) queryAddUser = entry[2][0]; } else if(!entry[2].empty()) return false; } else if(!entry[1].empty()) return false; return true; } return false; } void UserBackendMysql::configFinished() { if(db.empty()) { application->log(Core::LoggerBase::ERROR, "UserBackendMysql: No database name given"); return; } boost::lock_guard lock(mutex); mysql = mysql_init(0); mysql_real_connect(mysql, host.c_str(), username.c_str(), passwd.c_str(), db.c_str(), port, unixSocket.empty() ? 0 : unixSocket.c_str(), 0); } boost::shared_ptr > UserBackendMysql::getUserList(boost::posix_time::ptime *timestamp) throw(Core::Exception) { application->getThreadManager()->detach(); boost::unique_lock lock(mutex); if(timestamp) { if(timestamp->is_not_a_date_time() || *timestamp < lastUpdate) *timestamp = lastUpdate; else return boost::shared_ptr >(); } if(!mysql || mysql_ping(mysql)) throw Core::Exception(Core::Exception::NOT_AVAILABLE); mysql_real_query(mysql, queryListUsers.c_str(), queryListUsers.length()); Result result(mysql); lock.unlock(); if(!result || result.getErrno() || result.getFieldNumber() < 4) throw Core::Exception(Core::Exception::NOT_AVAILABLE); boost::shared_ptr > users(new std::map()); while(MYSQL_ROW row = result.getNextRow()) { Common::UserInfo user(strtoul(row[0], 0, 10), row[2]); user.setGid(strtoul(row[1], 0, 10)); user.setFullName(row[3]); users->insert(std::make_pair(user.getUid(), user)); } return users; } boost::shared_ptr UserBackendMysql::getUserInfo(unsigned long uid, boost::posix_time::ptime *timestamp) throw(Core::Exception) { application->getThreadManager()->detach(); boost::unique_lock lock(mutex); if(timestamp) { if(timestamp->is_not_a_date_time() || *timestamp < lastUpdate) *timestamp = lastUpdate; else return boost::shared_ptr(); } if(!mysql || mysql_ping(mysql)) throw Core::Exception(Core::Exception::NOT_AVAILABLE); std::string query = queryUserById; std::ostringstream tmp; tmp << '"'; tmp << uid; tmp << '"'; query = boost::regex_replace(query, boost::regex("\\{UID\\}"), tmp.str(), boost::match_default); mysql_real_query(mysql, query.c_str(), query.length()); Result result(mysql); lock.unlock(); if(!result || result.getErrno() || result.getFieldNumber() < 4) throw Core::Exception(Core::Exception::NOT_AVAILABLE); MYSQL_ROW row = result.getNextRow(); if(row) { boost::shared_ptr user(new Common::UserInfo(strtoul(row[0], 0, 10), row[2])); user->setGid(strtoul(row[1], 0, 10)); user->setFullName(row[3]); return user; } throw Core::Exception(Core::Exception::NOT_FOUND); } boost::shared_ptr UserBackendMysql::getUserInfoByName(const std::string &name, boost::posix_time::ptime *timestamp) throw(Core::Exception) { application->getThreadManager()->detach(); boost::unique_lock lock(mutex); if(timestamp) { if(timestamp->is_not_a_date_time() || *timestamp < lastUpdate) *timestamp = lastUpdate; else return boost::shared_ptr(); } if(!mysql || mysql_ping(mysql)) throw Core::Exception(Core::Exception::NOT_AVAILABLE); boost::scoped_array escapedName(new char[name.length()*2+1]); mysql_real_escape_string(mysql, escapedName.get(), name.c_str(), name.length()); std::string query = boost::regex_replace(queryUserByName, boost::regex("\\{USER\\}"), "\""+std::string(escapedName.get())+"\"", boost::match_default); mysql_real_query(mysql, query.c_str(), query.length()); Result result(mysql); lock.unlock(); if(!result || result.getErrno() || result.getFieldNumber() < 4) throw Core::Exception(Core::Exception::NOT_AVAILABLE); MYSQL_ROW row = result.getNextRow(); if(row) { boost::shared_ptr user(new Common::UserInfo(strtoul(row[0], 0, 10), row[2])); user->setGid(strtoul(row[1], 0, 10)); user->setFullName(row[3]); return user; } throw Core::Exception(Core::Exception::NOT_FOUND); } boost::shared_ptr > UserBackendMysql::getUserGroupList(unsigned long uid, boost::posix_time::ptime *timestamp) throw(Core::Exception) { application->getThreadManager()->detach(); boost::unique_lock lock(mutex); if(timestamp) { if(timestamp->is_not_a_date_time() || *timestamp < lastUpdate) *timestamp = lastUpdate; else return boost::shared_ptr >(); } if(!mysql || mysql_ping(mysql)) throw Core::Exception(Core::Exception::NOT_AVAILABLE); std::ostringstream tmp; tmp << '"'; tmp << uid; tmp << '"'; std::string query = boost::regex_replace(queryListUserGroups, boost::regex("\\{UID\\}"), tmp.str(), boost::match_default); mysql_real_query(mysql, query.c_str(), query.length()); Result result(mysql); lock.unlock(); if(!result || result.getErrno() || result.getFieldNumber() < 1) throw Core::Exception(Core::Exception::NOT_AVAILABLE); boost::shared_ptr > groups(new std::set); while(MYSQL_ROW row = result.getNextRow()) groups->insert(strtoul(row[0], 0, 10)); return groups; } boost::shared_ptr > UserBackendMysql::getGroupList(boost::posix_time::ptime *timestamp) throw(Core::Exception) { application->getThreadManager()->detach(); boost::unique_lock lock(mutex); if(timestamp) { if(timestamp->is_not_a_date_time() || *timestamp < lastUpdate) *timestamp = lastUpdate; else return boost::shared_ptr >(); } if(!mysql || mysql_ping(mysql)) throw Core::Exception(Core::Exception::NOT_AVAILABLE); mysql_real_query(mysql, queryListGroups.c_str(), queryListGroups.length()); Result result(mysql); lock.unlock(); if(!result || result.getErrno() || result.getFieldNumber() < 2) throw Core::Exception(Core::Exception::NOT_AVAILABLE); boost::shared_ptr > groups(new std::map()); while(MYSQL_ROW row = result.getNextRow()) { Common::GroupInfo group(strtoul(row[0], 0, 10), row[1]); groups->insert(std::make_pair(group.getGid(), group)); } return groups; } boost::shared_ptr UserBackendMysql::getGroupInfo(unsigned long gid, boost::posix_time::ptime *timestamp) throw(Core::Exception) { application->getThreadManager()->detach(); boost::unique_lock lock(mutex); if(timestamp) { if(timestamp->is_not_a_date_time() || *timestamp < lastUpdate) *timestamp = lastUpdate; else return boost::shared_ptr(); } if(!mysql || mysql_ping(mysql)) throw Core::Exception(Core::Exception::NOT_AVAILABLE); std::string query = queryGroupById; std::ostringstream tmp; tmp << '"'; tmp << gid; tmp << '"'; query = boost::regex_replace(query, boost::regex("\\{GID\\}"), tmp.str(), boost::match_default); mysql_real_query(mysql, query.c_str(), query.length()); Result result(mysql); lock.unlock(); if(!result || result.getErrno() || result.getFieldNumber() < 2) throw Core::Exception(Core::Exception::NOT_AVAILABLE); MYSQL_ROW row = result.getNextRow(); if(row) return boost::shared_ptr(new Common::GroupInfo(strtoul(row[0], 0, 10), row[1])); throw Core::Exception(Core::Exception::NOT_FOUND); } boost::shared_ptr UserBackendMysql::getGroupInfoByName(const std::string &name, boost::posix_time::ptime *timestamp) throw(Core::Exception) { application->getThreadManager()->detach(); boost::unique_lock lock(mutex); if(timestamp) { if(timestamp->is_not_a_date_time() || *timestamp < lastUpdate) *timestamp = lastUpdate; else return boost::shared_ptr(); } if(!mysql || mysql_ping(mysql)) throw Core::Exception(Core::Exception::NOT_AVAILABLE); boost::scoped_array escapedName(new char[name.length()*2+1]); mysql_real_escape_string(mysql, escapedName.get(), name.c_str(), name.length()); std::string query = boost::regex_replace(queryGroupByName, boost::regex("\\{GROUP\\}"), "\""+std::string(escapedName.get())+"\"", boost::match_default); mysql_real_query(mysql, query.c_str(), query.length()); Result result(mysql); lock.unlock(); if(!result || result.getErrno() || result.getFieldNumber() < 2) throw Core::Exception(Core::Exception::NOT_AVAILABLE); MYSQL_ROW row = result.getNextRow(); if(row) return boost::shared_ptr(new Common::GroupInfo(strtoul(row[0], 0, 10), row[1])); throw Core::Exception(Core::Exception::NOT_FOUND); } boost::shared_ptr > UserBackendMysql::getGroupUserList(unsigned long gid, boost::posix_time::ptime *timestamp) throw(Core::Exception) { application->getThreadManager()->detach(); boost::unique_lock lock(mutex); if(timestamp) { if(timestamp->is_not_a_date_time() || *timestamp < lastUpdate) *timestamp = lastUpdate; else return boost::shared_ptr >(); } if(!mysql || mysql_ping(mysql)) throw Core::Exception(Core::Exception::NOT_AVAILABLE); std::ostringstream tmp; tmp << '"'; tmp << gid; tmp << '"'; std::string query = boost::regex_replace(queryListGroupUsers, boost::regex("\\{GID\\}"), tmp.str(), boost::match_default); mysql_real_query(mysql, query.c_str(), query.length()); Result result(mysql); lock.unlock(); if(!result || result.getErrno() || result.getFieldNumber() < 1) throw Core::Exception(Core::Exception::NOT_AVAILABLE); boost::shared_ptr > users(new std::set); while(MYSQL_ROW row = result.getNextRow()) users->insert(strtoul(row[0], 0, 10)); return users; } boost::shared_ptr > UserBackendMysql::getFullUserGroupList(boost::posix_time::ptime *timestamp) throw(Core::Exception) { application->getThreadManager()->detach(); boost::unique_lock lock(mutex); if(timestamp) { if(timestamp->is_not_a_date_time() || *timestamp < lastUpdate) *timestamp = lastUpdate; else return boost::shared_ptr >(); } if(!mysql || mysql_ping(mysql)) throw Core::Exception(Core::Exception::NOT_AVAILABLE); mysql_real_query(mysql, queryUserGroupTable.c_str(), queryUserGroupTable.length()); Result result(mysql); lock.unlock(); if(!result || result.getErrno() || result.getFieldNumber() < 2) throw Core::Exception(Core::Exception::NOT_AVAILABLE); boost::shared_ptr > usergroups(new std::multimap); while(MYSQL_ROW row = result.getNextRow()) usergroups->insert(std::make_pair(strtoul(row[0], 0, 10), strtoul(row[1], 0, 10))); return usergroups; } void UserBackendMysql::addUser(const Common::UserInfo &userInfo) throw(Core::Exception) { application->getThreadManager()->detach(); boost::lock_guard lock(mutex); if(!mysql || mysql_ping(mysql)) throw Core::Exception(Core::Exception::NOT_AVAILABLE); std::ostringstream tmp; tmp << '"'; tmp << userInfo.getUid(); tmp << '"'; std::string query = boost::regex_replace(queryAddUser, boost::regex("\\{UID\\}"), tmp.str(), boost::match_default); tmp.str(std::string()); tmp << '"'; tmp << userInfo.getGid(); tmp << '"'; query = boost::regex_replace(query, boost::regex("\\{GID\\}"), tmp.str(), boost::match_default); const std::string &username = userInfo.getUsername(); boost::scoped_array escapedUsername(new char[username.length()*2+1]); mysql_real_escape_string(mysql, escapedUsername.get(), username.c_str(), username.length()); query = boost::regex_replace(query, boost::regex("\\{USER\\}"), "\"" + std::string(escapedUsername.get()) + "\"", boost::match_default); const std::string &fullName = userInfo.getFullName(); boost::scoped_array escapedFullName(new char[fullName.length()*2+1]); mysql_real_escape_string(mysql, escapedFullName.get(), fullName.c_str(), fullName.length()); query = boost::regex_replace(query, boost::regex("\\{FULL_NAME\\}"), "\"" + std::string(escapedFullName.get()) + "\"", boost::match_default); mysql_real_query(mysql, query.c_str(), query.length()); Result result(mysql); if(result.getErrno()) { if(result.getErrno() == ER_DUP_ENTRY) throw Core::Exception(Core::Exception::DUPLICATE_ENTRY); else throw Core::Exception(Core::Exception::NOT_AVAILABLE); } lastUpdate = boost::posix_time::microsec_clock::universal_time(); } } } }