#include #include "../shared/LogFile.h" #include "../shared/CommandDispatcher.h" #include "../shared/ContainerThread.h" #include "../shared/Locker.h" #include "../shared/MultiCast.h" #include "UserInfo.h" /* A very simple version of user info. It takes what the proxy gives us and * stores that. Later another thread can ask what user id is associated with * a particular socket. All of the heavy lifting is done by ../micro_proxy. */ class UserInfoThreadUser : public ForeverThreadUser, private ThreadMonitor::Extra { private: enum {mtProxyLogin}; class CurrentlyLoggedIn { private: std::unordered_map< SocketInfo *, UserId > _bySocket; std::unordered_map< UserId, SocketInfo * > _byUserId; public: bool isLoggedIn(UserId userId) const { return _byUserId.count(userId); } UserId getUserId(SocketInfo *socket) const { const auto it = _bySocket.find(socket); if (it == _bySocket.end()) return 0; else return it->second; } TclList getInfoForThreadMonitor() { TclList msg; msg<<"currently logged in"<<_bySocket.size(); // Might be nice to also report the number of demo users. return msg; } void setUserId(SocketInfo *socket, UserId userId) { // Ideally each socked would call setUserId exactly once and the userId // field would never be duplicated. We do not enforce either. Start // by calling remove(), even though it shouldn't be required, because // that could possibly cause a memory leak if we didn't. Note the // remaining issue: If you log in via two seperate sockets, then you // log out of one of them, the user is no longer in _byUserId and we // could miss a call to PING_User. I'm willing to ignore that, but not // a memory leak. remove(socket); if (userId) { // Don't even try to track all the sockets logged in as demo. We could // with more code, but there's no point. _bySocket[socket] = userId; _byUserId[userId] = socket; } } void remove(SocketInfo *socket) { const auto it = _bySocket.find(socket); if (it != _bySocket.end()) { const UserId userId = it->second; _byUserId.erase(userId); _bySocket.erase(it); } } }; Locker< CurrentlyLoggedIn > _currentlyLoggedIn; // From ThreadMonitor::Extra virtual std::string getInfoForThreadMonitor() { return _currentlyLoggedIn.lock()->getInfoForThreadMonitor(); } virtual void handleRequestInThread(Request *original) { ExternalRequest *request = dynamic_cast< ExternalRequest * >(original); assert(request->callbackId == mtProxyLogin); SocketInfo *const socket = request->getSocketInfo(); UserId userId = strtolDefault(request->getProperty("user_id"), 0); TclList msg; msg<setUserId(socket, userId); CommandDispatcher::getInstance()->unlock(socket); } virtual void socketClosed(SocketInfo *socket) { _currentlyLoggedIn.lock()->remove(socket); } virtual void initializeInThread() { ThreadMonitor::find().add(this); MultiCast::getInstance().addCallback([&](PropertyList const &message) { if (const UserId userId = strtolDefault(MultiCast::getUserId(message), 0)) { const bool found = _currentlyLoggedIn.lock()->isLoggedIn(userId); if (found) MultiCast::respond(message); } }); } public: UserInfoThreadUser() { CommandDispatcher::getInstance() ->listenForCommand("proxy_login", this, mtProxyLogin, true); start(); } UserInfoExport getUserInfo(SocketInfo *socket) { UserInfoExport result; result.userId = _currentlyLoggedIn.lock()->getUserId(socket); result.status = result.userId?sFull:sLimited; return result; } }; static UserInfoThreadUser *instance = NULL; UserInfoExport userInfoGetInfo(SocketInfo *socket) { return instance->getUserInfo(socket); } void initUserInfoManager() { static UserInfoThreadUser *i = new UserInfoThreadUser(); instance = i; }