syncevolution/src/dbus/server/server.cpp

1085 lines
37 KiB
C++

/*
* Copyright (C) 2009 Intel Corporation
* Copyright (C) 2011 Symbio, Ville Nummela
*
* This library 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 2.1 of the License, or (at your option) version 3.
*
* This library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include <fstream>
#include <boost/bind.hpp>
#include <syncevo/GLibSupport.h>
#include <syncevo/timeout.h>
#include "server.h"
#include "info-req.h"
#include "connection.h"
#include "bluez-manager.h"
#include "session.h"
#include "restart.h"
#include "client.h"
#include "auto-sync-manager.h"
#include "connman-client.h"
#include "network-manager-client.h"
#include "presence-status.h"
#include <boost/pointer_cast.hpp>
using namespace GDBusCXX;
SE_BEGIN_CXX
void Server::onIdleChange(bool idle)
{
SE_LOG_DEBUG(NULL, "server is %s", idle ? "idle" : "not idle");
if (idle) {
autoTermUnref();
} else {
autoTermRef();
}
}
class ServerLogger : public Logger
{
Logger::Handle m_parentLogger;
// Currently a strong reference. Would be a weak reference
// if we had proper reference counting for Server.
boost::shared_ptr<Server> m_server;
public:
ServerLogger(const boost::shared_ptr<Server> &server) :
m_parentLogger(Logger::instance()),
m_server(server)
{
}
virtual void remove() throw ()
{
// Hold the Logger mutex while cutting our connection to the
// server. The code using m_server below does the same and
// holds the mutex while logging. That way we prevent threads
// from holding onto the server while it tries to shut down.
//
// This is important because the server's live time is not
// really controlled via the boost::shared_ptr, it may
// destruct while there are still references. See
// Server::m_logger instantiation below.
RecMutex::Guard guard = lock();
m_server.reset();
}
virtual void messagev(const MessageOptions &options,
const char *format,
va_list args)
{
// Ensure that remove() cannot proceed while we have the
// server in use.
RecMutex::Guard guard = lock();
Server *server = m_server.get();
message2DBus(server,
options,
format,
args,
server ? server->getPath() : "",
getProcessName());
}
/**
* @param server may be NULL, in which case logging only goes to parent
*/
void message2DBus(Server *server,
const MessageOptions &options,
const char *format,
va_list args,
const std::string &dbusPath,
const std::string &procname)
{
// Keeps logging consistent: otherwise thread A might log to
// parent, thread B to parent and D-Bus, then thread A
// finishes its logging via D-Bus. The order of log messages
// would then not be the same in the parent and D-Bus.
RecMutex::Guard guard = lock();
// iterating over args in messagev() is destructive, must make a copy first
va_list argsCopy;
va_copy(argsCopy, args);
m_parentLogger.messagev(options, format, args);
if (server) {
try {
if (options.m_level <= server->getDBusLogLevel()) {
string log = StringPrintfV(format, argsCopy);
server->logOutput(dbusPath, options.m_level, log, procname);
}
} catch (...) {
remove();
// Give up on server logging silently.
}
}
va_end(argsCopy);
}
};
void Server::clientGone(Client *c)
{
for (Clients_t::iterator it = m_clients.begin();
it != m_clients.end();
++it) {
if (it->second.get() == c) {
SE_LOG_DEBUG(NULL, "D-Bus client %s has disconnected",
c->m_ID.c_str());
autoTermUnref(it->second->getAttachCount());
m_clients.erase(it);
return;
}
}
SE_LOG_DEBUG(NULL, "unknown client has disconnected?!");
}
std::string Server::getNextSession()
{
// Make the session ID somewhat random. This protects to
// some extend against injecting unwanted messages into the
// communication.
m_lastSession++;
if (!m_lastSession) {
m_lastSession++;
}
return StringPrintf("%u%u", rand(), m_lastSession);
}
vector<string> Server::getCapabilities()
{
// Note that this is tested by test-dbus.py in
// TestServer.testCapabilities, update the test when adding
// capabilities.
vector<string> capabilities;
capabilities.push_back("ConfigChanged");
capabilities.push_back("GetConfigName");
capabilities.push_back("NamedConfig");
capabilities.push_back("Notifications");
capabilities.push_back("Version");
capabilities.push_back("SessionFlags");
capabilities.push_back("SessionAttach");
capabilities.push_back("DatabaseProperties");
return capabilities;
}
StringMap Server::getVersions()
{
StringMap versions;
versions["version"] = VERSION;
versions["system"] = EDSAbiWrapperInfo();
versions["backends"] = SyncSource::backendsInfo();
return versions;
}
void Server::attachClient(const Caller_t &caller,
const boost::shared_ptr<Watch> &watch)
{
boost::shared_ptr<Client> client = addClient(caller, watch);
autoTermRef();
client->increaseAttachCount();
}
void Server::detachClient(const Caller_t &caller)
{
boost::shared_ptr<Client> client = findClient(caller);
if (client) {
autoTermUnref();
client->decreaseAttachCount();
}
}
void Server::setNotifications(bool enabled,
const Caller_t &caller,
const string & /* notifications */)
{
boost::shared_ptr<Client> client = findClient(caller);
if (client && client->getAttachCount()) {
client->setNotificationsEnabled(enabled);
} else {
SE_THROW("client not attached, not allowed to change notifications");
}
}
bool Server::notificationsEnabled()
{
for (Clients_t::iterator it = m_clients.begin();
it != m_clients.end();
++it) {
if (!it->second->getNotificationsEnabled()) {
return false;
}
}
return true;
}
void Server::connect(const Caller_t &caller,
const boost::shared_ptr<Watch> &watch,
const StringMap &peer,
bool must_authenticate,
const std::string &session,
DBusObject_t &object)
{
if (m_shutdownRequested) {
// don't allow new connections, we cannot activate them
SE_THROW("server shutting down");
}
if (!session.empty()) {
// reconnecting to old connection is not implemented yet
throw std::runtime_error("not implemented");
}
std::string new_session = getNextSession();
boost::shared_ptr<Connection> c(Connection::createConnection(*this,
getConnection(),
new_session,
peer,
must_authenticate));
SE_LOG_DEBUG(NULL, "connecting D-Bus client %s with connection %s '%s'",
caller.c_str(),
c->getPath(),
c->m_description.c_str());
boost::shared_ptr<Client> client = addClient(caller,
watch);
client->attach(c);
c->activate();
object = c->getPath();
}
void Server::startSessionWithFlags(const Caller_t &caller,
const boost::shared_ptr<Watch> &watch,
const std::string &server,
const std::vector<std::string> &flags,
DBusObject_t &object)
{
if (m_shutdownRequested) {
// don't allow new sessions, we cannot activate them
SE_THROW("server shutting down");
}
boost::shared_ptr<Client> client = addClient(caller,
watch);
std::string new_session = getNextSession();
boost::shared_ptr<Session> session = Session::createSession(*this,
"is this a client or server session?",
server,
new_session,
flags);
client->attach(session);
session->activate();
enqueue(session);
object = session->getPath();
}
boost::shared_ptr<Session> Server::startInternalSession(const std::string &server,
SessionFlags flags,
const boost::function<void (const boost::weak_ptr<Session> &session)> &callback)
{
if (m_shutdownRequested) {
// don't allow new sessions, we cannot activate them
SE_THROW("server shutting down");
}
std::vector<std::string> dbusFlags;
if (flags & SESSION_FLAG_NO_SYNC) {
dbusFlags.push_back("no-sync");
}
if (flags & SESSION_FLAG_ALL_CONFIGS) {
dbusFlags.push_back("all-configs");
}
std::string new_session = getNextSession();
boost::shared_ptr<Session> session = Session::createSession(*this,
"is this a client or server session?",
server,
new_session,
dbusFlags);
session->m_sessionActiveSignal.connect(boost::bind(callback, boost::weak_ptr<Session>(session)));
session->activate();
enqueue(session);
return session;
}
void Server::checkPresence(const std::string &server,
std::string &status,
std::vector<std::string> &transports)
{
return getPresenceStatus().checkPresence(server, status, transports);
}
void Server::getSessions(std::vector<DBusObject_t> &sessions)
{
sessions.reserve(m_workQueue.size() + 1);
if (m_activeSession) {
sessions.push_back(m_activeSession->getPath());
}
BOOST_FOREACH(boost::weak_ptr<Session> &session, m_workQueue) {
boost::shared_ptr<Session> s = session.lock();
if (s) {
sessions.push_back(s->getPath());
}
}
}
Server::Server(GMainLoop *loop,
boost::shared_ptr<Restart> &restart,
const DBusConnectionPtr &conn,
int duration) :
DBusObjectHelper(conn,
SessionCommon::SERVER_PATH,
SessionCommon::SERVER_IFACE,
boost::bind(&Server::autoTermCallback, this)),
m_loop(loop),
m_suspendFlagsSource(0),
m_shutdownRequested(false),
m_restart(restart),
m_conn(conn),
m_lastSession(time(NULL)),
m_activeSession(NULL),
m_lastInfoReq(0),
m_bluezManager(new BluezManager(*this)),
sessionChanged(*this, "SessionChanged"),
presence(*this, "Presence"),
templatesChanged(*this, "TemplatesChanged"),
configChanged(*this, "ConfigChanged"),
infoRequest(*this, "InfoRequest"),
m_logOutputSignal(*this, "LogOutput"),
m_autoTerm(m_loop, m_shutdownRequested, duration),
m_dbusLogLevel(Logger::INFO),
// TODO (?): turn Server into a proper reference counted instance.
// This would help with dangling references to it when other threads
// use it for logging, see ServerLogger. However, with mutex locking
// in ServerLogger that shouldn't be a problem.
m_logger(new ServerLogger(boost::shared_ptr<Server>(this, NopDestructor())))
{
struct timeval tv;
gettimeofday(&tv, NULL);
srand(tv.tv_usec);
add(this, &Server::getCapabilities, "GetCapabilities");
add(this, &Server::getVersions, "GetVersions");
add(this, &Server::attachClient, "Attach");
add(this, &Server::detachClient, "Detach");
add(this, &Server::enableNotifications, "EnableNotifications");
add(this, &Server::disableNotifications, "DisableNotifications");
add(this, &Server::notificationAction, "NotificationAction");
add(this, &Server::connect, "Connect");
add(this, &Server::startSession, "StartSession");
add(this, &Server::startSessionWithFlags, "StartSessionWithFlags");
add(this, &Server::getConfigs, "GetConfigs");
add(this, &Server::getConfig, "GetConfig");
add(this, &Server::getReports, "GetReports");
add(this, &Server::checkSource, "CheckSource");
add(this, &Server::getDatabases, "GetDatabases");
add(this, &Server::checkPresence, "CheckPresence");
add(this, &Server::getSessions, "GetSessions");
add(this, &Server::infoResponse, "InfoResponse");
add(sessionChanged);
add(templatesChanged);
add(configChanged);
add(presence);
add(infoRequest);
add(m_logOutputSignal);
// Log entering and leaving idle state and
// allow/prevent auto-termination.
m_idleSignal.connect(boost::bind(&Server::onIdleChange, this, _1));
// connect ConfigChanged signal to source for that information
m_configChangedSignal.connect(boost::bind(boost::ref(configChanged)));
}
gboolean Server::onSuspendFlagsChange(GIOChannel *source,
GIOCondition condition,
gpointer data) throw ()
{
Server *me = static_cast<Server *>(data);
try {
if (!SuspendFlags::getSuspendFlags().isNormal()) {
me->m_shutdownRequested = true;
g_main_loop_quit(me->m_loop);
SE_LOG_INFO(NULL, "server shutting down because of SIGINT or SIGTERM");
}
} catch (...) {
Exception::handle();
}
// Keep watching, just in case that we catch multiple signals.
return TRUE;
}
void Server::activate()
{
// Watch SuspendFlags fd to react to signals quickly.
int fd = SuspendFlags::getSuspendFlags().getEventFD();
GIOChannelCXX channel(g_io_channel_unix_new(fd), TRANSFER_REF);
m_suspendFlagsSource = g_io_add_watch(channel, G_IO_IN, onSuspendFlagsChange, this);
// Activate our D-Bus object *before* interacting with D-Bus
// any further. Otherwise GIO D-Bus will start processing
// messages for us while we start up and reject them because
// out object isn't visible to it yet.
GDBusCXX::DBusObjectHelper::activate();
// Push ourselves as logger for the time being.
m_logger->setLevel(Logger::DEBUG);
m_pushLogger.reset(m_logger);
m_presence.reset(new PresenceStatus(*this));
// Assume that Bluetooth is available. Neither ConnMan nor Network
// manager can tell us about that. The "Bluetooth" ConnMan technology
// is about IP connection via Bluetooth - not what we need.
getPresenceStatus().updatePresenceStatus(true, PresenceStatus::BT_TRANSPORT);
m_connman.reset(new ConnmanClient(*this));
m_networkManager.reset(new NetworkManagerClient(*this));
if ((!m_connman || !m_connman->isAvailable()) &&
(!m_networkManager || !m_networkManager->isAvailable())) {
// assume that we are online if no network manager was found at all
getPresenceStatus().updatePresenceStatus(true, PresenceStatus::HTTP_TRANSPORT);
}
// create auto sync manager, now that server is ready
m_autoSync = AutoSyncManager::createAutoSyncManager(*this);
}
Server::~Server()
{
// make sure all other objects are gone before destructing ourselves
if (m_suspendFlagsSource) {
g_source_remove(m_suspendFlagsSource);
}
m_syncSession.reset();
m_workQueue.clear();
m_clients.clear();
m_autoSync.reset();
m_infoReqMap.clear();
m_timeouts.clear();
m_delayDeletion.clear();
m_connman.reset();
m_networkManager.reset();
m_presence.reset();
m_pushLogger.reset();
m_logger.reset();
}
bool Server::shutdown()
{
Timespec now = Timespec::monotonic();
bool autosync = m_autoSync && m_autoSync->preventTerm();
SE_LOG_DEBUG(NULL, "shut down or restart server at %lu.%09lu because of file modifications, auto sync %s",
now.tv_sec, now.tv_nsec, autosync ? "on" : "off");
if (autosync) {
// suitable exec() call which restarts the server using the same environment it was in
// when it was started
SE_LOG_INFO(NULL, "server restarting because files loaded into memory were modified on disk");
m_restart->restart();
} else {
// leave server now
g_main_loop_quit(m_loop);
SE_LOG_INFO(NULL, "server shutting down because files loaded into memory were modified on disk");
}
return false;
}
void Server::fileModified(const std::string &file)
{
SE_LOG_DEBUG(NULL, "file %s modified, %s shutdown: %s, %s",
file.c_str(),
m_shutdownRequested ? "continuing" : "initiating",
m_shutdownTimer ? "timer already active" : "timer not yet active",
m_activeSession ? "waiting for active session to finish" : "setting timer");
m_lastFileMod = Timespec::monotonic();
if (!m_activeSession) {
m_shutdownTimer.activate(SHUTDOWN_QUIESENCE_SECONDS,
boost::bind(&Server::shutdown, this));
}
m_shutdownRequested = true;
}
void Server::run()
{
// This has the intended side effect that it loads everything into
// memory which might be dynamically loadable, like backend
// plugins.
StringMap map = getVersions();
SE_LOG_DEBUG(NULL, "D-Bus server ready to run, versions:");
BOOST_FOREACH(const StringPair &entry, map) {
SE_LOG_DEBUG(NULL, "%s: %s", entry.first.c_str(), entry.second.c_str());
}
// Now that everything is loaded, check memory map for files which we have to monitor.
set<string> files;
std::ifstream in("/proc/self/maps");
while (!in.eof()) {
string line;
getline(in, line);
size_t off = line.find('/');
if (off != line.npos &&
line.find(" r-xp ") != line.npos) {
files.insert(line.substr(off));
}
}
in.close();
BOOST_FOREACH(const string &file, files) {
try {
SE_LOG_DEBUG(NULL, "watching: %s", file.c_str());
boost::shared_ptr<SyncEvo::GLibNotify> notify(new GLibNotify(file.c_str(), boost::bind(&Server::fileModified, this, file)));
m_files.push_back(notify);
} catch (...) {
// ignore errors for indidividual files
Exception::handle();
}
}
SE_LOG_INFO(NULL, "ready to run");
// Note that with GDBus GIO, this will also finally request the
// "org.syncevolution" name. This relies on preserving the name in
// m_conn that we originally passed to dbus_get_bus_connection().
// getConnection() works with a plain GDBusConnection and doesn't
// have the name, so we really need our own copy of
// DBusConnectionPtr here.
dbus_bus_connection_undelay(m_conn);
if (!m_shutdownRequested) {
g_main_loop_run(m_loop);
}
SE_LOG_DEBUG(NULL, "%s", "Exiting Server::run");
}
/**
* look up client by its ID
*/
boost::shared_ptr<Client> Server::findClient(const Caller_t &ID)
{
for (Clients_t::iterator it = m_clients.begin();
it != m_clients.end();
++it) {
if (it->second->m_ID == ID) {
return it->second;
}
}
return boost::shared_ptr<Client>();
}
boost::shared_ptr<Client> Server::addClient(const Caller_t &ID,
const boost::shared_ptr<Watch> &watch)
{
boost::shared_ptr<Client> client(findClient(ID));
if (client) {
return client;
}
client.reset(new Client(*this, ID));
// add to our list *before* checking that peer exists, so
// that clientGone() can remove it if the check fails
m_clients.push_back(std::make_pair(watch, client));
watch->setCallback(boost::bind(&Server::clientGone, this, client.get()));
return client;
}
void Server::detach(Resource *resource)
{
BOOST_FOREACH(const Clients_t::value_type &client_entry,
m_clients) {
client_entry.second->detachAll(resource);
}
}
void Server::enqueue(const boost::shared_ptr<Session> &session)
{
bool idle = isIdle();
WorkQueue_t::iterator it = m_workQueue.end();
while (it != m_workQueue.begin()) {
--it;
// skip over dead sessions, they will get cleaned up elsewhere
boost::shared_ptr<Session> session = it->lock();
if (session && session->getPriority() <= session->getPriority()) {
++it;
break;
}
}
m_workQueue.insert(it, session);
checkQueue();
if (idle) {
m_idleSignal(false);
}
}
void Server::killSessionsAsync(const std::string &peerDeviceID,
const SimpleResult &onResult)
{
WorkQueue_t::iterator it = m_workQueue.begin();
while (it != m_workQueue.end()) {
boost::shared_ptr<Session> session = it->lock();
if (session && session->getPeerDeviceID() == peerDeviceID) {
SE_LOG_DEBUG(NULL, "removing pending session %s because it matches deviceID %s",
session->getSessionID().c_str(),
peerDeviceID.c_str());
// remove session and its corresponding connection
boost::shared_ptr<Connection> c = session->getStubConnection().lock();
if (c) {
c->shutdown();
}
it = m_workQueue.erase(it);
} else {
++it;
}
}
// Check active session. We need to wait for it to shut down cleanly.
boost::shared_ptr<Session> active = m_activeSessionRef.lock();
if (active &&
active->getPeerDeviceID() == peerDeviceID) {
SE_LOG_DEBUG(NULL, "aborting active session %s because it matches deviceID %s",
active->getSessionID().c_str(),
peerDeviceID.c_str());
// hand over work to session
active->abortAsync(onResult);
} else {
onResult.done();
}
}
void Server::dequeue(Session *session)
{
bool idle = isIdle();
if (m_syncSession.get() == session) {
// This is the running sync session.
// It's not in the work queue and we have to
// keep it active, so nothing to do.
return;
}
for (WorkQueue_t::iterator it = m_workQueue.begin();
it != m_workQueue.end();
++it) {
if (it->lock().get() == session) {
// remove from queue
m_workQueue.erase(it);
break;
}
}
if (m_activeSession == session) {
// The session is releasing the lock, so someone else might
// run now.
sessionChanged(session->getPath(), false);
m_activeSession = NULL;
m_activeSessionRef.reset();
if (m_lastFileMod && !m_shutdownTimer) {
// File modification was detected while a session was active.
// Trigger the shutdown now that it is gone. As without a
// a session, the goal is to shut down SHUTDOWN_QUIESENCE_SECONDS
// after the last file modification.
Timespec now = Timespec::monotonic();
Timespec shutdown = m_lastFileMod + SHUTDOWN_QUIESENCE_SECONDS;
if (shutdown >= now) {
SE_LOG_DEBUG(NULL, "file modified, initiating shutdown after session finished");
m_shutdownTimer.activate((shutdown - now).seconds(),
boost::bind(&Server::shutdown, this));
}
}
checkQueue();
}
if (!idle && isIdle()) {
m_idleSignal(true);
}
}
void Server::addSyncSession(Session *session)
{
// Only one session can run a sync, and only the active session
// can make itself the sync session.
if (m_syncSession) {
if (m_syncSession.get() != session) {
SE_THROW("already have a sync session");
} else {
return;
}
}
m_syncSession = m_activeSessionRef.lock();
m_newSyncSessionSignal(m_syncSession);
if (!m_syncSession) {
SE_THROW("session should not start a sync, all clients already detached");
}
if (m_syncSession.get() != session) {
m_syncSession.reset();
SE_THROW("inactive session asked to become sync session");
}
}
void Server::removeSyncSession(Session *session)
{
if (session == m_syncSession.get()) {
// Normally the owner calls this, but if it is already gone,
// then do it again and thus effectively start counting from
// now.
delaySessionDestruction(m_syncSession);
m_syncSession.reset();
} else {
SE_LOG_DEBUG(NULL, "ignoring removeSyncSession() for session %s, it is not the sync session",
session->getSessionID().c_str());
}
}
static void quitLoop(GMainLoop *loop)
{
SE_LOG_DEBUG(NULL, "stopping server's event loop");
g_main_loop_quit(loop);
}
void Server::checkQueue()
{
if (m_activeSession) {
// still busy
return;
}
if (m_shutdownRequested) {
// Don't schedule new sessions. Instead return to Server::run().
// But don't do it immediately: when done inside the Session.Detach()
// call, the D-Bus response was not delivered reliably to the client
// which caused the shutdown. When Server::fileModified() already set
// a timeout then we don't need to do it again (solves a race
// condition in test-dbus.py's testSession2: without this check,
// if the file modification is detected before handling the Detach(),
// then the server shut down immediately after Detach()).
if (!m_shutdownTimer) {
SE_LOG_DEBUG(NULL, "shutting down in checkQueue(), idle and shutdown was requested");
addTimeout(boost::bind(quitLoop, m_loop), 0);
}
return;
}
while (!m_workQueue.empty()) {
boost::shared_ptr<Session> session = m_workQueue.front().lock();
m_workQueue.pop_front();
if (session) {
// activate the session
m_activeSession = session.get();
m_activeSessionRef = session;
SE_LOG_DEBUG(NULL, "activating session %p", m_activeSession);
session->activateSession();
sessionChanged(session->getPath(), true);
return;
}
}
}
void Server::sessionExpired(const boost::shared_ptr<Session> &session)
{
SE_LOG_DEBUG(NULL, "session %s expired",
session->getSessionID().c_str());
}
void Server::delaySessionDestruction(const boost::shared_ptr<Session> &session)
{
if (!session) {
return;
}
SE_LOG_DEBUG(NULL, "delaying destruction of session %s by one minute",
session->getSessionID().c_str());
addTimeout(boost::bind(&Server::sessionExpired,
session),
60 /* 1 minute */);
}
inline void insertPair(std::map<string, string> &params,
const string &key,
const string &value)
{
if(!value.empty()) {
params.insert(pair<string, string>(key, value));
}
}
boost::shared_ptr<InfoReq> Server::passwordRequest(const string &descr,
const ConfigPasswordKey &key,
const boost::weak_ptr<Session> &s)
{
boost::shared_ptr<Session> session = s.lock();
if (!session) {
// already gone, ignore request
return boost::shared_ptr<InfoReq>();
}
std::map<string, string> params;
insertPair(params, "description", descr);
insertPair(params, "user", key.user);
insertPair(params, "SyncML server", key.server);
insertPair(params, "domain", key.domain);
insertPair(params, "object", key.object);
insertPair(params, "protocol", key.protocol);
insertPair(params, "authtype", key.authtype);
insertPair(params, "port", key.port ? StringPrintf("%u",key.port) : "");
boost::shared_ptr<InfoReq> req = createInfoReq("password", params, *session);
// Return password or failure to Session and thus the session helper.
req->m_responseSignal.connect(boost::bind(&Server::passwordResponse,
this,
_1,
s));
// Tell session about timeout.
req->m_timeoutSignal.connect(InfoReq::TimeoutSignal_t::slot_type(&Session::passwordResponse,
session.get(),
true,
false,
"").track(s));
// Request becomes obsolete when session is done.
session->m_doneSignal.connect(boost::bind(&Server::removeInfoReq,
this,
req->getId()));
return req;
}
void Server::passwordResponse(const InfoReq::InfoMap &response,
const boost::weak_ptr<Session> &s)
{
boost::shared_ptr<Session> session = s.lock();
if (!session) {
// already gone, ignore request
return;
}
InfoReq::InfoMap::const_iterator it = response.find("password");
if (it == response.end()) {
// no password provided, user wants to abort
session->passwordResponse(false, true, "");
} else {
// password provided, might be empty
session->passwordResponse(false, false, it->second);
}
}
bool Server::callTimeout(const boost::shared_ptr<Timeout> &timeout, const boost::function<void ()> &callback)
{
callback();
// We are executing the timeout, don't invalidate the instance
// until later when our caller is no longer using the instance to
// call us.
delayDeletion(timeout);
m_timeouts.remove(timeout);
return false;
}
void Server::addTimeout(const boost::function<void ()> &callback,
int seconds)
{
boost::shared_ptr<Timeout> timeout(new Timeout);
m_timeouts.push_back(timeout);
timeout->activate(seconds,
boost::bind(&Server::callTimeout,
this,
// avoid copying the shared pointer here,
// otherwise the Timeout will never be deleted
boost::ref(m_timeouts.back()),
callback));
}
void Server::infoResponse(const Caller_t &caller,
const std::string &id,
const std::string &state,
const std::map<string, string> &response)
{
InfoReqMap::iterator it = m_infoReqMap.find(id);
// if not found, ignore
if (it != m_infoReqMap.end()) {
const boost::shared_ptr<InfoReq> infoReq = it->second.lock();
if (infoReq) {
infoReq->setResponse(caller, state, response);
}
}
}
boost::shared_ptr<InfoReq> Server::createInfoReq(const string &type,
const std::map<string, string> &parameters,
const Session &session)
{
boost::shared_ptr<InfoReq> infoReq(new InfoReq(*this, type, parameters, session.getPath()));
m_infoReqMap.insert(std::make_pair(infoReq->getId(), infoReq));
// will be removed automatically
infoReq->m_responseSignal.connect(boost::bind(&Server::removeInfoReq,
this,
infoReq->getId()));
infoReq->m_timeoutSignal.connect(boost::bind(&Server::removeInfoReq,
this,
infoReq->getId()));
return infoReq;
}
std::string Server::getNextInfoReq()
{
return StringPrintf("%u", ++m_lastInfoReq);
}
void Server::emitInfoReq(const InfoReq &req)
{
infoRequest(req.getId(),
req.getSessionPath(),
req.getInfoStateStr(),
req.getHandler(),
req.getType(),
req.getParam());
}
void Server::removeInfoReq(const std::string &id)
{
// remove InfoRequest from hash map
m_infoReqMap.erase(id);
}
PresenceStatus &Server::getPresenceStatus()
{
if (!m_presence) {
SE_THROW("internal error: Server::getPresenceStatus() called while server has no instance");
}
return *m_presence;
}
void Server::getDeviceList(SyncConfig::DeviceList &devices)
{
//wait bluez or other device managers
// TODO: make this asynchronous?!
while(!m_bluezManager->isDone()) {
g_main_loop_run(m_loop);
}
devices.clear();
devices = m_syncDevices;
}
void Server::addPeerTempl(const string &templName,
const boost::shared_ptr<SyncConfig::TemplateDescription> peerTempl)
{
std::string lower = templName;
boost::to_lower(lower);
m_matchedTempls.insert(MatchedTemplates::value_type(lower, peerTempl));
}
boost::shared_ptr<SyncConfig::TemplateDescription> Server::getPeerTempl(const string &peer)
{
std::string lower = peer;
boost::to_lower(lower);
MatchedTemplates::iterator it = m_matchedTempls.find(lower);
if(it != m_matchedTempls.end()) {
return it->second;
} else {
return boost::shared_ptr<SyncConfig::TemplateDescription>();
}
}
bool Server::getDevice(const string &deviceId, SyncConfig::DeviceDescription &device)
{
SyncConfig::DeviceList::iterator syncDevIt;
for (syncDevIt = m_syncDevices.begin(); syncDevIt != m_syncDevices.end(); ++syncDevIt) {
if (boost::equals(syncDevIt->m_deviceId, deviceId)) {
device = *syncDevIt;
if (syncDevIt->m_pnpInformation) {
device.m_pnpInformation = boost::shared_ptr<SyncConfig::PnpInformation>(
new SyncConfig::PnpInformation(syncDevIt->m_pnpInformation->m_vendor,
syncDevIt->m_pnpInformation->m_product));
}
return true;
}
}
return false;
}
void Server::addDevice(const SyncConfig::DeviceDescription &device)
{
SyncConfig::DeviceList::iterator it;
for (it = m_syncDevices.begin(); it != m_syncDevices.end(); ++it) {
if (boost::iequals(it->m_deviceId, device.m_deviceId)) {
break;
}
}
if (it == m_syncDevices.end()) {
m_syncDevices.push_back(device);
templatesChanged();
}
}
void Server::removeDevice(const string &deviceId)
{
SyncConfig::DeviceList::iterator syncDevIt;
for (syncDevIt = m_syncDevices.begin(); syncDevIt != m_syncDevices.end(); ++syncDevIt) {
if (boost::equals(syncDevIt->m_deviceId, deviceId)) {
m_syncDevices.erase(syncDevIt);
templatesChanged();
break;
}
}
}
void Server::updateDevice(const string &deviceId,
const SyncConfig::DeviceDescription &device)
{
SyncConfig::DeviceList::iterator it;
for (it = m_syncDevices.begin(); it != m_syncDevices.end(); ++it) {
if (boost::iequals(it->m_deviceId, deviceId)) {
(*it) = device;
templatesChanged();
break;
}
}
}
void Server::message2DBus(const Logger::MessageOptions &options,
const char *format,
va_list args,
const std::string &dbusPath,
const std::string &procname)
{
// prefix is used to set session path
// for general server output, the object path field is dbus server
// the object path can't be empty for object paths prevent using empty string.
m_logger->message2DBus(this, options, format, args, dbusPath, procname);
}
void Server::logOutput(const GDBusCXX::DBusObject_t &path,
Logger::Level level,
const std::string &explanation,
const std::string &procname)
{
if (level <= m_dbusLogLevel) {
string strLevel = Logger::levelToStr(level);
m_logOutputSignal(path, strLevel, explanation, procname);
}
}
SE_END_CXX