syncevolution/src/syncevo/Exception.cpp

165 lines
5.9 KiB
C++

/*
* Copyright (C) 2008-2009 Patrick Ohly <patrick.ohly@gmx.de>
* Copyright (C) 2014 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.
*/
#include <syncevo/Exception.h>
#include <src/syncevo/SynthesisEngine.h>
#include <synthesis/syerror.h>
#include "gdbus-cxx-bridge.h"
#include <pcrecpp.h>
#include <errno.h>
SE_BEGIN_CXX
static const char * const TRANSPORT_PROBLEM = "transport problem: ";
static const char * const SYNTHESIS_PROBLEM = "error code from Synthesis engine ";
static const char * const SYNCEVOLUTION_PROBLEM = "error code from SyncEvolution ";
SyncMLStatus Exception::handle(SyncMLStatus *status,
const std::string *logPrefix,
std::string *explanation,
Logger::Level level,
HandleExceptionFlags flags)
{
// any problem here is a fatal local problem, unless set otherwise
// by the specific exception
SyncMLStatus new_status = SyncMLStatus(STATUS_FATAL + sysync::LOCAL_STATUS_CODE);
std::string error;
try {
throw;
} catch (const TransportException &ex) {
SE_LOG_DEBUG(logPrefix, "TransportException thrown at %s:%d",
ex.m_file.c_str(), ex.m_line);
error = std::string(TRANSPORT_PROBLEM) + ex.what();
new_status = SyncMLStatus(sysync::LOCERR_TRANSPFAIL);
} catch (const BadSynthesisResult &ex) {
new_status = SyncMLStatus(ex.result());
error = StringPrintf("%s%s",
SYNTHESIS_PROBLEM,
Status2String(new_status).c_str());
} catch (const StatusException &ex) {
new_status = ex.syncMLStatus();
SE_LOG_DEBUG(logPrefix, "exception thrown at %s:%d",
ex.m_file.c_str(), ex.m_line);
error = StringPrintf("%s%s: %s",
SYNCEVOLUTION_PROBLEM,
Status2String(new_status).c_str(), ex.what());
if (new_status == STATUS_NOT_FOUND &&
(flags & HANDLE_EXCEPTION_404_IS_OKAY)) {
level = Logger::DEBUG;
}
} catch (const Exception &ex) {
SE_LOG_DEBUG(logPrefix, "exception thrown at %s:%d",
ex.m_file.c_str(), ex.m_line);
error = ex.what();
} catch (const std::exception &ex) {
error = ex.what();
} catch (...) {
error = "unknown error";
}
if (flags & HANDLE_EXCEPTION_FATAL) {
level = Logger::ERROR;
}
if (flags & HANDLE_EXCEPTION_NO_ERROR) {
level = Logger::DEBUG;
}
SE_LOG(logPrefix, level, "%s", error.c_str());
if (flags & HANDLE_EXCEPTION_FATAL) {
// Something unexpected went wrong, can only shut down.
::abort();
}
if (explanation) {
*explanation = error;
}
if (status && *status == STATUS_OK) {
*status = new_status;
}
return status ? *status : new_status;
}
void Exception::tryRethrow(const std::string &explanation, bool mustThrow)
{
static const std::string statusre = ".* \\((?:local|remote), status (\\d+)\\)";
int status;
if (boost::starts_with(explanation, TRANSPORT_PROBLEM)) {
SE_THROW_EXCEPTION(TransportException, explanation.substr(strlen(TRANSPORT_PROBLEM)));
} else if (boost::starts_with(explanation, SYNTHESIS_PROBLEM)) {
static const pcrecpp::RE re(statusre);
if (re.FullMatch(explanation.substr(strlen(SYNTHESIS_PROBLEM)), &status)) {
SE_THROW_EXCEPTION_1(BadSynthesisResult, "Synthesis engine failure", (sysync::TSyErrorEnum)status);
}
} else if (boost::starts_with(explanation, SYNCEVOLUTION_PROBLEM)) {
static const pcrecpp::RE re(statusre + ": (.*)",
pcrecpp::RE_Options().set_dotall(true));
std::string details;
if (re.FullMatch(explanation.substr(strlen(SYNCEVOLUTION_PROBLEM)), &status, &details)) {
SE_THROW_EXCEPTION_STATUS(StatusException, details, (SyncMLStatus)status);
}
}
if (mustThrow) {
throw std::runtime_error(explanation);
}
}
void Exception::tryRethrowDBus(const std::string &error)
{
static const pcrecpp::RE re("(org\\.syncevolution(?:\\.\\w+)+): (.*)",
pcrecpp::RE_Options().set_dotall(true));
std::string exName, explanation;
if (re.FullMatch(error, &exName, &explanation)) {
// found SyncEvolution exception explanation, parse it
tryRethrow(explanation);
// explanation not parsed, fall back to D-Bus exception
throw GDBusCXX::dbus_error(exName, explanation);
}
}
void Exception::throwError(const SourceLocation &where, const std::string &error)
{
throwError(where, SyncMLStatus(STATUS_FATAL + sysync::LOCAL_STATUS_CODE), error);
}
void Exception::throwError(const SourceLocation &where, SyncMLStatus status, const std::string &error)
{
throw StatusException(where.m_file, where.m_line, error, status);
}
void Exception::throwError(const SourceLocation &where, const std::string &action, int error)
{
std::string what = action + ": " + strerror(error);
// be as specific if we can be: relevant for the file backend,
// which is expected to return STATUS_NOT_FOUND == 404 for "file
// not found"
if (error == ENOENT) {
throwError(where, STATUS_NOT_FOUND, what);
} else {
throwError(where, what);
}
}
void Exception::fatalError(void *object, const char *error)
{
SE_LOG_ERROR(NULL, "%s", error);
exit(1);
}
SE_END_CXX