syncevolution/src/dbus/server/dbus-sync.cpp

336 lines
13 KiB
C++

/*
* Copyright (C) 2011 Intel Corporation
*
* 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 "dbus-sync.h"
#include "session-helper.h"
#include "dbus-transport-agent.h"
#include <syncevo/SyncSource.h>
#include <syncevo/SuspendFlags.h>
#include <syncevo/ForkExec.h>
SE_BEGIN_CXX
DBusSync::DBusSync(const SessionCommon::SyncParams &params,
SessionHelper &helper) :
SyncContext(params.m_config, true),
m_helper(helper),
m_params(params),
m_waiting(false)
{
setUserInterface(this);
setServerAlerted(params.m_serverAlerted);
if (params.m_serverMode) {
initServer(params.m_sessionID,
params.m_initialMessage,
params.m_initialMessageType);
}
if (params.m_remoteInitiated) {
setRemoteInitiated(true);
}
// Watch status of parent and our own process and cancel
// any pending password request if parent or we go down.
boost::shared_ptr<ForkExecChild> forkexec = m_helper.getForkExecChild();
if (forkexec) {
m_parentWatch = forkexec->m_onQuit.connect(boost::bind(&DBusSync::passwordResponse, this, true, false, ""));
}
m_suspendFlagsWatch = SuspendFlags::getSuspendFlags().m_stateChanged.connect(boost::bind(&DBusSync::suspendFlagsChanged, this, _1));
// Apply temporary config filters. The parameters of this function
// override the source filters, if set.
setConfigFilter(true, "", params.m_syncFilter);
FilterConfigNode::ConfigFilter filter;
filter = params.m_sourceFilter;
if (!params.m_mode.empty()) {
if (params.m_mode == "ephemeral") {
makeEphemeral();
} else if (params.m_mode == "pbap") {
// "pbap" may only be used by caller when it knows that
// the mode is safe to use.
makeEphemeral();
const char *sync = getenv("SYNCEVOLUTION_PBAP_SYNC");
if (!sync) {
SE_LOG_DEBUG(NULL, "enabling default SYNCEVOLUTION_PBAP_SYNC=incremental");
setenv("SYNCEVOLUTION_PBAP_SYNC", "incremental", true);
} else {
SE_LOG_DEBUG(NULL, "using SYNCEVOLUTION_PBAP_SYNC=%s from environment", sync);
}
} else {
filter["sync"] = params.m_mode;
}
}
setConfigFilter(false, "", filter);
BOOST_FOREACH(const std::string &source,
getSyncSources()) {
SessionCommon::SourceFilters_t::const_iterator fit = params.m_sourceFilters.find(source);
filter = fit == params.m_sourceFilters.end() ?
FilterConfigNode::ConfigFilter() :
fit->second;
SessionCommon::SourceModes_t::const_iterator it = params.m_sourceModes.find(source);
if (it != params.m_sourceModes.end()) {
filter["sync"] = it->second;
}
setConfigFilter(false, source, filter);
}
// Create source status and progress entries for each source in
// the parent. See Session::sourceProgress().
BOOST_FOREACH(const std::string &source,
getSyncSources()) {
m_helper.emitSourceProgress(sysync::PEV_PREPARING,
source,
SYNC_NONE,
0, 0, 0);
}
// Forward the SourceSyncedSignal via D-Bus.
m_sourceSyncedSignal.connect(boost::bind(m_helper.emitSourceSynced, _1, _2));
}
DBusSync::~DBusSync()
{
m_parentWatch.disconnect();
m_suspendFlagsWatch.disconnect();
}
boost::shared_ptr<TransportAgent> DBusSync::createTransportAgent()
{
if (m_params.m_serverAlerted || m_params.m_serverMode) {
// Use the D-Bus Connection to send and receive messages.
boost::shared_ptr<DBusTransportAgent> agent(new DBusTransportAgent(m_helper));
// Hook up agent with D-Bus in the helper. The agent may go
// away at any time, so use instance tracking.
m_helper.m_messageSignal.connect(SessionHelper::MessageSignal_t::slot_type(&DBusTransportAgent::storeMessage,
agent.get(),
_1,
_2).track(agent));
m_helper.m_connectionStateSignal.connect(SessionHelper::ConnectionStateSignal_t::slot_type(&DBusTransportAgent::storeState,
agent.get(),
_1).track(agent));
if (m_params.m_serverAlerted) {
// A SAN message was sent to us, need to reply.
agent->serverAlerted();
} else if (m_params.m_serverMode) {
// Let transport return initial message to engine.
agent->storeMessage(GDBusCXX::DBusArray<uint8_t>(m_params.m_initialMessage.size(),
reinterpret_cast<const uint8_t *>(m_params.m_initialMessage.get())),
m_params.m_initialMessageType);
}
return agent;
} else {
// no connection, use HTTP via libsoup/GMainLoop
GMainLoop *loop = m_helper.getLoop();
boost::shared_ptr<TransportAgent> agent = SyncContext::createTransportAgent(loop);
return agent;
}
}
void DBusSync::displaySyncProgress(sysync::TProgressEventEnum type,
int32_t extra1, int32_t extra2, int32_t extra3)
{
SyncContext::displaySyncProgress(type, extra1, extra2, extra3);
m_helper.emitSyncProgress(type, extra1, extra2, extra3);
}
bool DBusSync::displaySourceProgress(SyncSource &source,
const SyncSourceEvent &event,
bool flush)
{
bool cached = SyncContext::displaySourceProgress(source, event, flush);
if (!cached) {
// Tell parent about current source statistics directly before
// PEV_ITEMRECEIVED. The PIM Manager relies on that extra
// information.
if (event.m_type == sysync::PEV_ITEMRECEIVED) {
m_helper.emitSourceProgress(sysync::PEV_ITEMPROCESSED, source.getName(),
source.getFinalSyncMode(),
source.getAdded(),
source.getUpdated(),
source.getDeleted());
}
m_helper.emitSourceProgress(event.m_type, source.getName(), source.getFinalSyncMode(),
event.m_extra1, event.m_extra2, event.m_extra3);
}
return cached;
}
void DBusSync::reportStepCmd(sysync::uInt16 stepCmd)
{
switch(stepCmd) {
case sysync::STEPCMD_SENDDATA:
case sysync::STEPCMD_RESENDDATA:
case sysync::STEPCMD_NEEDDATA:
// sending or waiting
if (!m_waiting) {
m_helper.emitWaiting(true);
m_waiting = true;
}
break;
default:
// otherwise, processing
if (m_waiting) {
m_helper.emitWaiting(false);
m_waiting = false;
}
break;
}
}
void DBusSync::syncSuccessStart()
{
m_helper.emitSyncSuccessStart();
}
string DBusSync::askPassword(const string &passwordName,
const string &descr,
const ConfigPasswordKey &key)
{
std::string password;
std::string error;
askPasswordAsync(passwordName, descr, key,
boost::bind(static_cast<std::string & (std::string::*)(const std::string &)>(&std::string::assign),
&password, _1),
boost::bind(static_cast<SyncMLStatus (*)(std::string &, HandleExceptionFlags)>(&Exception::handle),
boost::ref(error), HANDLE_EXCEPTION_NO_ERROR));
// We know that askPasswordAsync() is done when it cleared the
// callback functors.
while (m_passwordSuccess) {
g_main_context_iteration(NULL, true);
}
if (!error.empty()) {
Exception::tryRethrow(error);
SE_THROW(StringPrintf("password request failed: %s", error.c_str()));
}
return password;
}
void DBusSync::askPasswordAsync(const std::string &passwordName,
const std::string &descr,
const ConfigPasswordKey &key,
const boost::function<void (const std::string &)> &success,
const boost::function<void ()> &failureException)
{
// cannot handle more than one password request at a time
m_passwordSuccess.clear();
m_passwordFailure.clear();
m_passwordDescr = descr;
InitStateString password;
if (GetLoadPasswordSignal()(getKeyring(), passwordName, descr, key, password) &&
password.wasSet()) {
// handled
success(password);
return;
}
try {
SE_LOG_DEBUG(NULL, "asking parent for password");
m_passwordSuccess = success;
m_passwordFailure = failureException;
m_helper.emitPasswordRequest(descr, key);
if (!m_helper.connected()) {
SE_LOG_DEBUG(NULL, "password request failed, lost connection");
SE_THROW_EXCEPTION_STATUS(StatusException,
StringPrintf("Could not get the '%s' password from user, no connection to UI.",
descr.c_str()),
STATUS_PASSWORD_TIMEOUT);
}
if (SuspendFlags::getSuspendFlags().getState() != SuspendFlags::NORMAL) {
SE_LOG_DEBUG(NULL, "password request failed, was asked to terminate");
SE_THROW_EXCEPTION_STATUS(StatusException,
StringPrintf("Could not get the '%s' password from user, was asked to shut down.",
descr.c_str()),
STATUS_PASSWORD_TIMEOUT);
}
} catch (...) {
m_passwordSuccess.clear();
m_passwordFailure.clear();
failureException();
}
}
void DBusSync::passwordResponse(bool timedOut, bool aborted, const std::string &password)
{
boost::function<void (const std::string &)> success;
boost::function<void ()> failureException;
std::swap(success, m_passwordSuccess);
std::swap(failureException, m_passwordFailure);
if (success && failureException) {
SE_LOG_DEBUG(NULL, "password result: %s",
timedOut ? "timeout or parent gone" :
aborted ? "user abort" :
password.empty() ? "empty password" :
"valid password");
try {
if (timedOut) {
SE_THROW_EXCEPTION_STATUS(StatusException,
StringPrintf("Could not get the '%s' password from user.",
m_passwordDescr.c_str()),
STATUS_PASSWORD_TIMEOUT);
} else if (aborted) {
SE_THROW_EXCEPTION_STATUS(StatusException,
StringPrintf("User did not provide the '%s' password.",
m_passwordDescr.c_str()),
SyncMLStatus(sysync::LOCERR_USERABORT));
} else {
success(password);
}
} catch (...) {
failureException();
}
}
}
void DBusSync::suspendFlagsChanged(SuspendFlags &flags)
{
if (flags.getState() != SuspendFlags::NORMAL) {
passwordResponse(true, false, "");
}
}
bool DBusSync::savePassword(const std::string &passwordName,
const std::string &password,
const ConfigPasswordKey &key)
{
if (GetSavePasswordSignal()(getKeyring(), passwordName, password, key)) {
return true;
}
// not saved
return false;
}
void DBusSync::readStdin(std::string &content)
{
// might get called, must be avoided by user
SE_THROW("reading from stdin not supported when running with daemon, use --daemon=no");
}
SE_END_CXX