/* * UserDBBackendMysql.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 Lesser 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along * with this program. If not, see . */ #include "UserDBBackendMysql.h" #include #include #include #include #include #include #include #include #include namespace Mad { namespace Modules { namespace UserDBBackendMysql { const Core::String UserDBBackendMysql::name("UserDBBackendMysql"); bool UserDBBackendMysql::handleConfigEntry(const Core::ConfigEntry &entry, bool /*handled*/) { if(!entry[0].getKey().matches("UserManager")) return false; if(entry[1].isEmpty()) return true; if(!entry[1].getKey().matches("Mysql")) return false; if(entry[2].getKey().matches("Host")) { if(entry[3].isEmpty()) host = entry[2][0]; } else if(entry[2].getKey().matches("Username")) { if(entry[3].isEmpty()) username = entry[2][0]; } else if(entry[2].getKey().matches("Password")) { if(entry[3].isEmpty()) passwd = entry[2][0]; } else if(entry[2].getKey().matches("Database")) { if(entry[3].isEmpty()) db = entry[2][0]; } else if(entry[2].getKey().matches("Port")) { if(entry[3].isEmpty()) { char *endptr; long val; val = strtol(entry[2][0].toString().c_str(), &endptr, 10); if(endptr != 0 || val < 0 || val > 65535) application->log(Core::Logger::LOG_WARNING, "UserDBBackendMysql: Invalid port"); else port = val; } } else if(entry[2].getKey().matches("UnixSocket")) { if(entry[3].isEmpty()) unixSocket = entry[2][0]; } else if(entry[2].getKey().matches("Queries")) { if(entry[3].getKey().matches("ListUsers")) { if(entry[4].isEmpty()) queryListUsers = entry[3][0]; } else if(entry[3].getKey().matches("ListGroups")) { if(entry[4].isEmpty()) queryListGroups = entry[3][0]; } else if(entry[3].getKey().matches("ListUserGroups")) { if(entry[4].isEmpty()) queryListUserGroups = entry[3][0]; } else if(entry[3].getKey().matches("ListGroupUsers")) { if(entry[4].isEmpty()) queryListGroupUsers = entry[3][0]; } else if(entry[3].getKey().matches("UserById")) { if(entry[4].isEmpty()) queryUserById = entry[3][0]; } else if(entry[3].getKey().matches("UserByName")) { if(entry[4].isEmpty()) queryUserByName = entry[3][0]; } else if(entry[3].getKey().matches("GroupById")) { if(entry[4].isEmpty()) queryGroupById = entry[3][0]; } else if(entry[3].getKey().matches("GroupByName")) { if(entry[4].isEmpty()) queryGroupByName = entry[3][0]; } else if(entry[3].getKey().matches("UserGroupTable")) { if(entry[4].isEmpty()) queryUserGroupTable = entry[3][0]; } else if(entry[3].getKey().matches("AddUser")) { if(entry[4].isEmpty()) queryAddUser = entry[3][0]; } else if(entry[3].getKey().matches("UpdateUser")) { if(entry[4].isEmpty()) queryUpdateUser = entry[3][0]; } else if(entry[3].getKey().matches("DeleteUser")) { if(entry[4].isEmpty()) queryDeleteUser = entry[3][0]; } else if(entry[3].getKey().matches("AddGroup")) { if(entry[4].isEmpty()) queryAddGroup = entry[3][0]; } else if(entry[3].getKey().matches("UpdateGroup")) { if(entry[4].isEmpty()) queryUpdateGroup = entry[3][0]; } else if(entry[3].getKey().matches("DeleteGroup")) { if(entry[4].isEmpty()) queryDeleteGroup = entry[3][0]; } else if(entry[3].getKey().matches("AddUserToGroup")) { if(entry[4].isEmpty()) queryAddUserToGroup = entry[3][0]; } else if(entry[3].getKey().matches("DeleteUserFromGroup")) { if(entry[4].isEmpty()) queryDeleteUserFromGroup = entry[3][0]; } else if(!entry[3].isEmpty()) return false; } else if(!entry[2].isEmpty()) return false; return true; } void UserDBBackendMysql::configFinished() { if(db.isEmpty()) { application->log(Core::Logger::LOG_ERROR, "UserDBBackendMysql: No database name given"); return; } boost::lock_guard lock(mutex); mysql = mysql_init(0); mysql_real_connect(mysql, host.toLocale().c_str(), username.toLocale().c_str(), passwd.toLocale().c_str(), db.toLocale().c_str(), port, unixSocket.isEmpty() ? 0 : unixSocket.toLocale().c_str(), 0); } UserDBBackendMysql::Result UserDBBackendMysql::query(const Core::String &query, const ArgumentMap &args) throw(Core::Exception) { if(!mysql || mysql_ping(mysql)) throw Core::Exception(Core::Exception::NOT_AVAILABLE); std::string queryStr = query.toLocale(); if(args.empty()) { mysql_real_query(mysql, queryStr.c_str(), queryStr.length()); } else { for(ArgumentMap::const_iterator arg = args.begin(); arg != args.end(); ++arg) { std::string argStr; try { argStr = boost::get(arg->second).toLocale(); } catch(...) { std::ostringstream stream; stream << arg->second; argStr = stream.str(); } boost::scoped_array escaped(new char[argStr.length()*2+1]); mysql_real_escape_string(mysql, escaped.get(), argStr.c_str(), argStr.length()); queryStr = boost::regex_replace(queryStr, boost::regex("\\{" + arg->first.toLocale() + "\\}"), "\"" + std::string(escaped.get()) + "\"", boost::match_default); } mysql_real_query(mysql, queryStr.c_str(), queryStr.length()); } return Result(mysql); } boost::shared_ptr > UserDBBackendMysql::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 >(); } Result result = query(queryListUsers); 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 UserDBBackendMysql::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(); } ArgumentMap args; args.insert(std::make_pair("UID", uid)); Result result = query(queryUserById, args); 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 UserDBBackendMysql::getUserInfoByName(const Core::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(); } ArgumentMap args; args.insert(std::make_pair("USER", name)); Result result = query(queryUserByName, args); 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 > UserDBBackendMysql::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 >(); } ArgumentMap args; args.insert(std::make_pair("UID", uid)); Result result = query(queryListUserGroups, args); 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 > UserDBBackendMysql::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 >(); } Result result = query(queryListGroups); 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 UserDBBackendMysql::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(); } ArgumentMap args; args.insert(std::make_pair("GID", gid)); Result result = query(queryGroupById, args); 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 UserDBBackendMysql::getGroupInfoByName(const Core::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(); } ArgumentMap args; args.insert(std::make_pair("GROUP", name)); Result result = query(queryGroupByName, args); 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 > UserDBBackendMysql::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 >(); } ArgumentMap args; args.insert(std::make_pair("GID", gid)); Result result = query(queryListGroupUsers, args); 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 > UserDBBackendMysql::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 >(); } Result result = query(queryUserGroupTable); 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 UserDBBackendMysql::addUser(const Common::UserInfo &userInfo) throw(Core::Exception) { application->getThreadManager()->detach(); boost::lock_guard lock(mutex); ArgumentMap args; args.insert(std::make_pair("UID", userInfo.getUid())); args.insert(std::make_pair("GID", userInfo.getGid())); args.insert(std::make_pair("USER", userInfo.getUsername())); args.insert(std::make_pair("FULL_NAME", userInfo.getFullName())); Result result = query(queryAddUser, args); 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(); } void UserDBBackendMysql::updateUser(unsigned long uid, const Common::UserInfo &userInfo) throw(Core::Exception) { application->getThreadManager()->detach(); boost::lock_guard lock(mutex); ArgumentMap args; args.insert(std::make_pair("ORIG_UID", uid)); args.insert(std::make_pair("UID", userInfo.getUid())); args.insert(std::make_pair("GID", userInfo.getGid())); args.insert(std::make_pair("USER", userInfo.getUsername())); args.insert(std::make_pair("FULL_NAME", userInfo.getFullName())); Result result = query(queryUpdateUser, args); 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(); } void UserDBBackendMysql::deleteUser(unsigned long uid) throw(Core::Exception) { application->getThreadManager()->detach(); boost::lock_guard lock(mutex); ArgumentMap args; args.insert(std::make_pair("UID", uid)); Result result = query(queryDeleteUser, args); if(result.getErrno()) throw Core::Exception(Core::Exception::NOT_AVAILABLE); lastUpdate = boost::posix_time::microsec_clock::universal_time(); } void UserDBBackendMysql::addGroup(const Common::GroupInfo &groupInfo) throw(Core::Exception) { application->getThreadManager()->detach(); boost::lock_guard lock(mutex); ArgumentMap args; args.insert(std::make_pair("GID", groupInfo.getGid())); args.insert(std::make_pair("GROUP", groupInfo.getName())); Result result = query(queryAddGroup, args); 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(); } void UserDBBackendMysql::updateGroup(unsigned long gid, const Common::GroupInfo &groupInfo) throw(Core::Exception) { application->getThreadManager()->detach(); boost::lock_guard lock(mutex); ArgumentMap args; args.insert(std::make_pair("ORIG_GID", gid)); args.insert(std::make_pair("GID", groupInfo.getGid())); args.insert(std::make_pair("GROUP", groupInfo.getName())); Result result = query(queryUpdateGroup, args); 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(); } void UserDBBackendMysql::deleteGroup(unsigned long gid) throw(Core::Exception) { application->getThreadManager()->detach(); boost::lock_guard lock(mutex); ArgumentMap args; args.insert(std::make_pair("GID", gid)); Result result = query(queryDeleteGroup, args); if(result.getErrno()) throw Core::Exception(Core::Exception::NOT_AVAILABLE); lastUpdate = boost::posix_time::microsec_clock::universal_time(); } void UserDBBackendMysql::addUserToGroup(unsigned long uid, unsigned long gid) throw(Core::Exception) { application->getThreadManager()->detach(); boost::lock_guard lock(mutex); ArgumentMap args; args.insert(std::make_pair("UID", uid)); args.insert(std::make_pair("GID", gid)); Result result = query(queryAddUserToGroup, args); if(result.getErrno()) { if(result.getErrno() == ER_DUP_ENTRY) return; else throw Core::Exception(Core::Exception::NOT_AVAILABLE); } lastUpdate = boost::posix_time::microsec_clock::universal_time(); } void UserDBBackendMysql::deleteUserFromGroup(unsigned long uid, unsigned long gid) throw(Core::Exception) { application->getThreadManager()->detach(); boost::lock_guard lock(mutex); ArgumentMap args; args.insert(std::make_pair("UID", uid)); args.insert(std::make_pair("GID", gid)); Result result = query(queryDeleteUserFromGroup, args); if(result.getErrno()) throw Core::Exception(Core::Exception::NOT_AVAILABLE); lastUpdate = boost::posix_time::microsec_clock::universal_time(); } } } }