From c406c90289baa12663a382c7ed664f2cf93b75ab Mon Sep 17 00:00:00 2001 From: Jan Holesovsky Date: Tue, 2 Feb 2016 22:59:34 +0100 Subject: lok interaction handler: Add handling of io and network errors. Change-Id: If7c84a7b24f2072439718fb0c473b73243f2ecc1 --- desktop/source/lib/init.cxx | 39 ++++-- desktop/source/lib/lokinteractionhandler.cxx | 194 ++++++++++++++++++++++++--- desktop/source/lib/lokinteractionhandler.hxx | 32 ++++- include/LibreOfficeKit/LibreOfficeKitEnums.h | 14 ++ libreofficekit/source/gtk/lokdocview.cxx | 17 +++ 5 files changed, 263 insertions(+), 33 deletions(-) diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx index 5010bd0347c3..3ae825ae4966 100644 --- a/desktop/source/lib/init.cxx +++ b/desktop/source/lib/init.cxx @@ -252,7 +252,7 @@ static OUString getAbsoluteURL(const char* pURL) return OUString(); } -static void jsonToPropertyValues(const char* pJSON, uno::Sequence& rPropertyValues) +static std::vector jsonToPropertyValuesVector(const char* pJSON) { std::vector aArguments; if (pJSON && pJSON[0] != '\0') @@ -279,11 +279,11 @@ static void jsonToPropertyValues(const char* pJSON, uno::Sequence(OString(rValue.c_str()).toUInt32()); else - SAL_WARN("desktop.lib", "jsonToPropertyValues: unhandled type '"< const pInteraction( - new LOKInteractionHandler(::comphelper::getProcessComponentContext(), pLib)); + new LOKInteractionHandler(::comphelper::getProcessComponentContext(), "load", pLib)); auto const pair(pLib->mInteractionMap.insert(std::make_pair(aURL.toUtf8(), pInteraction))); comphelper::ScopeGuard const g([&] () { if (pair.second) @@ -985,9 +985,9 @@ static void doc_initializeForRendering(LibreOfficeKitDocument* pThis, if (pDoc) { doc_iniUnoCommands(); - uno::Sequence aPropertyValues; - jsonToPropertyValues(pArguments, aPropertyValues); - pDoc->initializeForTiledRendering(aPropertyValues); + + pDoc->initializeForTiledRendering( + comphelper::containerToSequence(jsonToPropertyValuesVector(pArguments))); } } @@ -1077,20 +1077,33 @@ public: static void doc_postUnoCommand(LibreOfficeKitDocument* pThis, const char* pCommand, const char* pArguments, bool bNotifyWhenFinished) { OUString aCommand(pCommand, strlen(pCommand), RTL_TEXTENCODING_UTF8); + LibLODocument_Impl* pDocument = static_cast(pThis); - uno::Sequence aPropertyValues; - jsonToPropertyValues(pArguments, aPropertyValues); - bool bResult = false; + std::vector aPropertyValuesVector(jsonToPropertyValuesVector(pArguments)); - LibLODocument_Impl* pDocument = static_cast(pThis); + // handle potential interaction + if (aCommand == ".uno:Save") + { + rtl::Reference const pInteraction( + new LOKInteractionHandler(::comphelper::getProcessComponentContext(), "save", gImpl, pDocument)); + uno::Reference const xInteraction(pInteraction.get()); + + beans::PropertyValue aValue; + aValue.Name = "InteractionHandler"; + aValue.Value <<= xInteraction; + + aPropertyValuesVector.push_back(aValue); + } + + bool bResult = false; if (bNotifyWhenFinished && pDocument->mpCallback) { - bResult = comphelper::dispatchCommand(aCommand, aPropertyValues, + bResult = comphelper::dispatchCommand(aCommand, comphelper::containerToSequence(aPropertyValuesVector), new DispatchResultListener(pCommand, pDocument->mpCallback, pDocument->mpCallbackData)); } else - bResult = comphelper::dispatchCommand(aCommand, aPropertyValues); + bResult = comphelper::dispatchCommand(aCommand, comphelper::containerToSequence(aPropertyValuesVector)); if (!bResult) { diff --git a/desktop/source/lib/lokinteractionhandler.cxx b/desktop/source/lib/lokinteractionhandler.cxx index 5bb164c7d2ca..bb287ff3af96 100644 --- a/desktop/source/lib/lokinteractionhandler.cxx +++ b/desktop/source/lib/lokinteractionhandler.cxx @@ -19,12 +19,21 @@ #include "lokinteractionhandler.hxx" +#include + #include #include #include #include #include +#include +#include + +#include +#include +#include +#include #include <../../inc/lib/init.hxx> @@ -34,8 +43,12 @@ using namespace com::sun::star; LOKInteractionHandler::LOKInteractionHandler( uno::Reference const & /*rxContext*/, - desktop::LibLibreOffice_Impl *const pLOKit) + const OString& rCommand, + desktop::LibLibreOffice_Impl *const pLOKit, + desktop::LibLODocument_Impl *const pLOKDocument) : m_pLOKit(pLOKit) + , m_pLOKDocument(pLOKDocument) + , m_command(rCommand) , m_usePassword(false) { assert(m_pLOKit); @@ -78,8 +91,157 @@ throw (uno::RuntimeException, std::exception) handleInteractionRequest(xRequest); } -sal_Bool LOKInteractionHandler::handlePasswordRequest(const uno::Sequence> &rContinuations, const task::DocumentPasswordRequest2& passwordRequest) +void LOKInteractionHandler::postError(css::task::InteractionClassification classif, const char* kind, ErrCode code, const OUString &message) { + const char *classification = "error"; + switch (classif) + { + case task::InteractionClassification_ERROR: break; + case task::InteractionClassification_WARNING: classification = "warning"; break; + case task::InteractionClassification_INFO: classification = "info"; break; + case task::InteractionClassification_QUERY: classification = "query"; break; + default: assert(false); break; + } + + // create the JSON representation + boost::property_tree::ptree aTree; + aTree.put("classification", classification); + aTree.put("cmd", m_command.getStr()); + aTree.put("kind", kind); + aTree.put("code", code); + aTree.put("message", message.toUtf8()); + + std::stringstream aStream; + boost::property_tree::write_json(aStream, aTree); + + if (m_pLOKDocument) + m_pLOKDocument->mpCallback(LOK_CALLBACK_ERROR, aStream.str().c_str(), m_pLOKDocument->mpCallbackData); + else + m_pLOKit->mpCallback(LOK_CALLBACK_ERROR, aStream.str().c_str(), m_pLOKit->mpCallbackData); +} + +namespace { + +/// Just approve the interaction. +void selectApproved(uno::Sequence> const &rContinuations) +{ + for (sal_Int32 i = 0; i < rContinuations.getLength(); ++i) + { + uno::Reference xApprove(rContinuations[i], uno::UNO_QUERY); + if (xApprove.is()) + xApprove->select(); + } +} + +} + +bool LOKInteractionHandler::handleIOException(const css::uno::Sequence> &rContinuations, const css::uno::Any& rRequest) +{ + ucb::InteractiveIOException aIoException; + if (!(rRequest >>= aIoException)) + return false; + + static ErrCode const aErrorCode[ucb::IOErrorCode_WRONG_VERSION + 1] = + { + ERRCODE_IO_ABORT, + ERRCODE_IO_ACCESSDENIED, + ERRCODE_IO_ALREADYEXISTS, + ERRCODE_IO_BADCRC, + ERRCODE_IO_CANTCREATE, + ERRCODE_IO_CANTREAD, + ERRCODE_IO_CANTSEEK, + ERRCODE_IO_CANTTELL, + ERRCODE_IO_CANTWRITE, + ERRCODE_IO_CURRENTDIR, + ERRCODE_IO_DEVICENOTREADY, + ERRCODE_IO_NOTSAMEDEVICE, + ERRCODE_IO_GENERAL, + ERRCODE_IO_INVALIDACCESS, + ERRCODE_IO_INVALIDCHAR, + ERRCODE_IO_INVALIDDEVICE, + ERRCODE_IO_INVALIDLENGTH, + ERRCODE_IO_INVALIDPARAMETER, + ERRCODE_IO_ISWILDCARD, + ERRCODE_IO_LOCKVIOLATION, + ERRCODE_IO_MISPLACEDCHAR, + ERRCODE_IO_NAMETOOLONG, + ERRCODE_IO_NOTEXISTS, + ERRCODE_IO_NOTEXISTSPATH, + ERRCODE_IO_NOTSUPPORTED, + ERRCODE_IO_NOTADIRECTORY, + ERRCODE_IO_NOTAFILE, + ERRCODE_IO_OUTOFSPACE, + ERRCODE_IO_TOOMANYOPENFILES, + ERRCODE_IO_OUTOFMEMORY, + ERRCODE_IO_PENDING, + ERRCODE_IO_RECURSIVE, + ERRCODE_IO_UNKNOWN, + ERRCODE_IO_WRITEPROTECTED, + ERRCODE_IO_WRONGFORMAT, + ERRCODE_IO_WRONGVERSION, + }; + + postError(aIoException.Classification, "io", aErrorCode[aIoException.Code], ""); + selectApproved(rContinuations); + + return true; +} + +bool LOKInteractionHandler::handleNetworkException(const uno::Sequence> &rContinuations, const uno::Any &rRequest) +{ + ucb::InteractiveNetworkException aNetworkException; + if (!(rRequest >>= aNetworkException)) + return false; + + ErrCode nErrorCode; + OUString aMessage; + + ucb::InteractiveNetworkOffLineException aOffLineException; + ucb::InteractiveNetworkResolveNameException aResolveNameException; + ucb::InteractiveNetworkConnectException aConnectException; + ucb::InteractiveNetworkReadException aReadException; + ucb::InteractiveNetworkWriteException aWriteException; + if (rRequest >>= aOffLineException) + { + nErrorCode = ERRCODE_INET_OFFLINE; + } + else if (rRequest >>= aResolveNameException) + { + nErrorCode = ERRCODE_INET_NAME_RESOLVE; + aMessage = aResolveNameException.Server; + } + else if (rRequest >>= aConnectException) + { + nErrorCode = ERRCODE_INET_CONNECT; + aMessage = aConnectException.Server; + } + else if (rRequest >>= aReadException) + { + nErrorCode = ERRCODE_INET_READ; + aMessage = aReadException.Diagnostic; + } + else if (rRequest >>= aWriteException) + { + nErrorCode = ERRCODE_INET_WRITE; + aMessage = aWriteException.Diagnostic; + } + else + { + nErrorCode = ERRCODE_INET_GENERAL; + } + + postError(aNetworkException.Classification, "network", nErrorCode, aMessage); + selectApproved(rContinuations); + + return true; +} + +bool LOKInteractionHandler::handlePasswordRequest(const uno::Sequence> &rContinuations, const uno::Any &rRequest) +{ + task::DocumentPasswordRequest2 passwordRequest; + if (!(rRequest >>= passwordRequest)) + return false; + if (m_pLOKit->hasOptionalFeature((passwordRequest.IsRequestPasswordToModify) ? LOK_FEATURE_DOCUMENT_PASSWORD_TO_MODIFY : LOK_FEATURE_DOCUMENT_PASSWORD)) @@ -134,28 +296,26 @@ sal_Bool LOKInteractionHandler::handlePasswordRequest(const uno::Sequence& xRequest) -throw (uno::RuntimeException, std::exception) + const uno::Reference& xRequest) throw (uno::RuntimeException, std::exception) { uno::Sequence> const &rContinuations = xRequest->getContinuations(); - uno::Any const request(xRequest->getRequest()); - task::DocumentPasswordRequest2 passwordRequest; - if (request >>= passwordRequest) - return handlePasswordRequest(rContinuations, passwordRequest); - // TODO: add LOK api that allows handling this for real, for the moment we - // just set the interaction as 'Approved' - for (sal_Int32 i = 0; i < rContinuations.getLength(); ++i) - { - uno::Reference xApprove(rContinuations[i], uno::UNO_QUERY); - if (xApprove.is()) - xApprove->select(); - } + if (handleIOException(rContinuations, request)) + return true; + + if (handleNetworkException(rContinuations, request)) + return true; + + if (handlePasswordRequest(rContinuations, request)) + return true; + + // TODO: perform more interactions 'for real' like the above + selectApproved(rContinuations); return sal_True; } diff --git a/desktop/source/lib/lokinteractionhandler.hxx b/desktop/source/lib/lokinteractionhandler.hxx index e21d7570f253..337e21591cc2 100644 --- a/desktop/source/lib/lokinteractionhandler.hxx +++ b/desktop/source/lib/lokinteractionhandler.hxx @@ -22,13 +22,18 @@ #include #include +#include #include #include #include #include +#include -namespace desktop { struct LibLibreOffice_Impl; } +namespace desktop { + struct LibLibreOffice_Impl; + struct LibLODocument_Impl; +} /** InteractionHandler is an interface that provides the user with various dialogs / error messages. @@ -44,6 +49,11 @@ class LOKInteractionHandler: public cppu::WeakImplHelper> &rContinuations, const css::task::DocumentPasswordRequest2& passwordRequest); + /** Call the LOK_CALLBACK_ERROR on the LOK document (if available) or LOK lib. + + The error itself is a JSON message, like: + { + "classification": "error" | "warning" | "info" + "kind": "network" etc. + "code": 403 | 404 | ... + "message": freeform description + } + */ + void postError(css::task::InteractionClassification classif, const char* kind, ErrCode code, const OUString &message); + + bool handleIOException(const css::uno::Sequence> &rContinuations, const css::uno::Any& rRequest); + bool handleNetworkException(const css::uno::Sequence> &rContinuations, const css::uno::Any& rRequest); + bool handlePasswordRequest(const css::uno::Sequence> &rContinuations, const css::uno::Any& rRequest); public: void SetPassword(char const* pPassword); explicit LOKInteractionHandler( com::sun::star::uno::Reference const & rxContext, - desktop::LibLibreOffice_Impl *); + const OString& rCommand, + desktop::LibLibreOffice_Impl *, + desktop::LibLODocument_Impl *pLOKDocumt = nullptr); virtual ~LOKInteractionHandler(); diff --git a/include/LibreOfficeKit/LibreOfficeKitEnums.h b/include/LibreOfficeKit/LibreOfficeKitEnums.h index b615bd94f9d1..5ce8610267ec 100644 --- a/include/LibreOfficeKit/LibreOfficeKitEnums.h +++ b/include/LibreOfficeKit/LibreOfficeKitEnums.h @@ -265,6 +265,20 @@ typedef enum * lok::Office::setDocumentPassword(). */ LOK_CALLBACK_DOCUMENT_PASSWORD_TO_MODIFY, + + /** + * An error happened. + * + * The payload returns information further identifying the error, like: + * + * { + * "classification": "error" | "warning" | "info" + * "kind": "network" etc. + * "code": 403 | 404 | ... + * "message": freeform description + * } + */ + LOK_CALLBACK_ERROR, } LibreOfficeKitCallbackType; diff --git a/libreofficekit/source/gtk/lokdocview.cxx b/libreofficekit/source/gtk/lokdocview.cxx index 17104a00b838..737157bf515c 100644 --- a/libreofficekit/source/gtk/lokdocview.cxx +++ b/libreofficekit/source/gtk/lokdocview.cxx @@ -781,6 +781,18 @@ static void formulaChanged(LOKDocView* pDocView, const std::string& rString) g_signal_emit(pDocView, doc_view_signals[FORMULA_CHANGED], 0, rString.c_str()); } +static void reportError(LOKDocView* /*pDocView*/, const std::string& rString) +{ + GtkWidget *dialog = gtk_message_dialog_new(nullptr, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + "%s", + rString.c_str()); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); +} + static void setPart(LOKDocView* pDocView, const std::string& rString) { @@ -1119,6 +1131,11 @@ callback (gpointer pData) formulaChanged(pDocView, pCallback->m_aPayload); } break; + case LOK_CALLBACK_ERROR: + { + reportError(pDocView, pCallback->m_aPayload); + } + break; default: g_assert(false); break; -- cgit