[Libreoffice-commits] online.git: kit/ChildSession.cpp kit/ChildSession.hpp kit/Kit.cpp loleaflet/src test/WhiteBoxTests.cpp wsd/ClientSession.cpp

Libreoffice Gerrit user logerrit at kemper.freedesktop.org
Sun Jan 13 22:50:40 UTC 2019


 kit/ChildSession.cpp             |  168 +++++++++++++++++++++++++++++++++++++++
 kit/ChildSession.hpp             |    4 
 kit/Kit.cpp                      |    6 +
 loleaflet/src/control/Signing.js |  120 +++++++++++++++++----------
 test/WhiteBoxTests.cpp           |    5 +
 wsd/ClientSession.cpp            |    1 
 6 files changed, 260 insertions(+), 44 deletions(-)

New commits:
commit 7137c953d88c14ddd6e9f6f56e338e0d54544b34
Author:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
AuthorDate: Sun Jan 13 23:45:44 2019 +0100
Commit:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
CommitDate: Sun Jan 13 23:50:18 2019 +0100

    add export, sign and upload as one function for PDF signing
    
    PDF export, signing and upload needs to be done in one operation
    as PDF doesn't change the current document. The workflow is just
    a bit different to the ODF / OOXML that it needs a change in
    behaviour.
    
    Change-Id: I752b293494a2d677fa7f12f2317954cfcf47859b

diff --git a/kit/ChildSession.cpp b/kit/ChildSession.cpp
index 7f856b252..e4c899cca 100644
--- a/kit/ChildSession.cpp
+++ b/kit/ChildSession.cpp
@@ -274,6 +274,7 @@ bool ChildSession::_handleInput(const char *buffer, int length)
                tokens[0] == "asksignaturestatus" ||
                tokens[0] == "signdocument" ||
                tokens[0] == "uploadsigneddocument" ||
+               tokens[0] == "exportsignanduploaddocument" ||
                tokens[0] == "rendershapeselection");
 
         if (tokens[0] == "clientzoom")
@@ -373,6 +374,10 @@ bool ChildSession::_handleInput(const char *buffer, int length)
         {
             return uploadSignedDocument(buffer, length, tokens);
         }
+        else if (tokens[0] == "exportsignanduploaddocument")
+        {
+            return exportSignAndUploadDocument(buffer, length, tokens);
+        }
 #endif
         else if (tokens[0] == "rendershapeselection")
         {
@@ -1397,6 +1402,169 @@ bool ChildSession::signDocumentContent(const char* buffer, int length, const std
     return bResult;
 }
 
+bool ChildSession::exportSignAndUploadDocument(const char* buffer, int length, const std::vector<std::string>& /*tokens*/)
+{
+    bool bResult = false;
+
+    std::string filename;
+    std::string wopiUrl;
+    std::string token;
+    std::string filetype;
+    std::string x509Certificate;
+    std::string privateKey;
+    std::vector<std::string> certificateChain;
+
+    { // parse JSON
+        const std::string firstLine = getFirstLine(buffer, length);
+        const char* data = buffer + firstLine.size() + 1;
+        const int size = length - firstLine.size() - 1;
+        std::string json(data, size);
+
+        Poco::JSON::Parser parser;
+        Poco::JSON::Object::Ptr root = parser.parse(json).extract<Poco::JSON::Object::Ptr>();
+
+        filename = JsonUtil::getJSONValue<std::string>(root, "filename");
+        wopiUrl = JsonUtil::getJSONValue<std::string>(root, "wopiUrl");
+        token = JsonUtil::getJSONValue<std::string>(root, "token");
+        filetype = JsonUtil::getJSONValue<std::string>(root, "type");
+        x509Certificate = JsonUtil::getJSONValue<std::string>(root, "x509Certificate");
+        privateKey = JsonUtil::getJSONValue<std::string>(root, "privateKey");
+
+        for (auto& rChainPtr : *root->getArray("chain"))
+        {
+            if (!rChainPtr.isString())
+            {
+                sendTextFrame("error: cmd=exportsignanduploaddocument kind=syntax");
+                return false;
+            }
+            std::string chainCertificate = rChainPtr;
+            certificateChain.push_back(chainCertificate);
+        }
+    }
+
+    if (filetype.empty() || filename.empty() || wopiUrl.empty() || token.empty() || x509Certificate.empty() || privateKey.empty())
+    {
+        sendTextFrame("error: cmd=exportsignanduploaddocument kind=syntax");
+        return false;
+    }
+
+    std::string mimetype = getMimeFromFileType(filetype);
+    if (mimetype.empty())
+    {
+        sendTextFrame("error: cmd=exportsignanduploaddocument kind=syntax");
+        return false;
+    }
+
+    // add certificate chain
+    for (auto const & certificate : certificateChain)
+    {
+        std::vector<unsigned char> binaryChainCertificate = decodeBase64(extractCertificate(certificate));
+
+        bResult = getLOKitDocument()->addCertificate(
+            binaryChainCertificate.data(),
+            binaryChainCertificate.size());
+
+        if (!bResult)
+        {
+            sendTextFrame("error: cmd=exportsignanduploaddocument kind=syntax");
+            return false;
+        }
+    }
+
+    // export document to a temp file
+    const std::string aTempDir = FileUtil::createRandomDir(JAILED_DOCUMENT_ROOT);
+    const Poco::Path filenameParam(filename);
+    const std::string aTempDocumentURL = JAILED_DOCUMENT_ROOT + aTempDir + "/" + filenameParam.getFileName();
+
+    {
+        std::unique_lock<std::mutex> lock(_docManager.getDocumentMutex());
+
+        getLOKitDocument()->saveAs(aTempDocumentURL.c_str(), filetype.c_str(), nullptr);
+    }
+
+    // sign document
+    {
+        std::vector<unsigned char> binaryCertificate = decodeBase64(extractCertificate(x509Certificate));
+        std::vector<unsigned char> binaryPrivateKey = decodeBase64(extractPrivateKey(privateKey));
+
+        bResult = _docManager.getLOKit()->signDocument(aTempDocumentURL.c_str(),
+                        binaryCertificate.data(), binaryCertificate.size(),
+                        binaryPrivateKey.data(), binaryPrivateKey.size());
+
+        if (!bResult)
+        {
+            sendTextFrame("error: cmd=exportsignanduploaddocument kind=syntax");
+            return false;
+        }
+    }
+
+    // upload
+    Authorization authorization(Authorization::Type::Token, token);
+    Poco::URI uriObject(wopiUrl + "/" + filename + "/contents");
+
+    authorization.authorizeURI(uriObject);
+
+    try
+    {
+        Poco::Net::initializeSSL();
+        Poco::Net::Context::Params sslClientParams;
+        sslClientParams.verificationMode = Poco::Net::Context::VERIFY_NONE;
+        Poco::SharedPtr<Poco::Net::PrivateKeyPassphraseHandler> consoleClientHandler = new Poco::Net::KeyConsoleHandler(false);
+        Poco::SharedPtr<Poco::Net::InvalidCertificateHandler> invalidClientCertHandler = new Poco::Net::AcceptCertificateHandler(false);
+        Poco::Net::Context::Ptr sslClientContext = new Poco::Net::Context(Poco::Net::Context::CLIENT_USE, sslClientParams);
+        Poco::Net::SSLManager::instance().initializeClient(consoleClientHandler, invalidClientCertHandler, sslClientContext);
+
+        std::unique_ptr<Poco::Net::HTTPClientSession> psession;
+        psession.reset(new Poco::Net::HTTPSClientSession(
+                        uriObject.getHost(),
+                        uriObject.getPort(),
+                        Poco::Net::SSLManager::instance().defaultClientContext()));
+
+        Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_POST, uriObject.getPathAndQuery(), Poco::Net::HTTPMessage::HTTP_1_1);
+        request.set("User-Agent", WOPI_AGENT_STRING);
+        authorization.authorizeRequest(request);
+
+        request.set("X-WOPI-Override", "PUT");
+
+        const size_t filesize = getFileSize(aTempDocumentURL);
+
+        request.setContentType(mimetype);
+        request.setContentLength(filesize);
+
+        std::ostream& httpOutputStream = psession->sendRequest(request);
+
+        std::ifstream inputFileStream(aTempDocumentURL);
+        Poco::StreamCopier::copyStream(inputFileStream, httpOutputStream);
+
+        Poco::Net::HTTPResponse response;
+        std::istream& responseStream = psession->receiveResponse(response);
+
+        std::ostringstream outputStringStream;
+        Poco::StreamCopier::copyStream(responseStream, outputStringStream);
+        std::string responseString = outputStringStream.str();
+
+        if (response.getStatus() != Poco::Net::HTTPResponse::HTTP_OK &&
+            response.getStatus() != Poco::Net::HTTPResponse::HTTP_CREATED)
+        {
+            LOG_ERR("Upload signed document HTTP Response Error: " << response.getStatus() << ' ' << response.getReason());
+
+            sendTextFrame("error: cmd=exportsignanduploaddocument kind=httpresponse");
+
+            return false;
+        }
+    }
+    catch (const Poco::Exception& pocoException)
+    {
+        LOG_ERR("Upload signed document Exception: " + pocoException.displayText());
+
+        sendTextFrame("error: cmd=exportsignanduploaddocument kind=failure");
+
+        return false;
+    }
+
+    return true;
+}
+
 bool ChildSession::askSignatureStatus(const char* buffer, int length, const std::vector<std::string>& /*tokens*/)
 {
     std::unique_lock<std::mutex> lock(_docManager.getDocumentMutex());
diff --git a/kit/ChildSession.hpp b/kit/ChildSession.hpp
index 50a909cb9..f40110b15 100644
--- a/kit/ChildSession.hpp
+++ b/kit/ChildSession.hpp
@@ -55,6 +55,9 @@ public:
     /// Access to the document instance.
     virtual std::shared_ptr<lok::Document> getLOKitDocument() = 0;
 
+    /// Access to the office instance.
+    virtual std::shared_ptr<lok::Office> getLOKit() = 0;
+
     /// Send updated view info to all active sessions.
     virtual void notifyViewInfo() = 0;
     virtual void updateEditorSpeeds(int id, int speed) = 0;
@@ -264,6 +267,7 @@ private:
     bool signDocumentContent(const char* buffer, int length, const std::vector<std::string>& tokens);
     bool askSignatureStatus(const char* buffer, int length, const std::vector<std::string>& tokens);
     bool uploadSignedDocument(const char* buffer, int length, const std::vector<std::string>& tokens);
+    bool exportSignAndUploadDocument(const char* buffer, int length, const std::vector<std::string>& tokens);
     bool renderShapeSelection(const char* buffer, int length, const std::vector<std::string>& tokens);
 
     void rememberEventsForInactiveUser(const int type, const std::string& payload);
diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index 72bf5a289..f5c63dd08 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -2010,6 +2010,12 @@ private:
         LOG_DBG("Thread finished.");
     }
 
+    /// Return access to the lok::Office instance.
+    std::shared_ptr<lok::Office> getLOKit() override
+    {
+        return _loKit;
+    }
+
     /// Return access to the lok::Document instance.
     std::shared_ptr<lok::Document> getLOKitDocument() override
     {
diff --git a/loleaflet/src/control/Signing.js b/loleaflet/src/control/Signing.js
index 1c3230caa..4195cb185 100644
--- a/loleaflet/src/control/Signing.js
+++ b/loleaflet/src/control/Signing.js
@@ -15,6 +15,7 @@ var _map = null;
 var currentDocumentSigningStatus = 'N/A'
 
 var awaitForDocumentStatusToUpload = false;
+var currentDocumentType = null;
 
 function isSuccess(result) {
 	return result.code == '200';
@@ -205,7 +206,7 @@ function vereignPinCodeDialog(selectedIdentityKey) {
 
 function vereignLogin() {
 	if (library && identity) {
-		library.login(identity, 'previousaddeddevice', '', '').then(function(result) {
+		library.login(identity, 'previousaddeddevice').then(function(result) {
 			if (isSuccess(result)) {
 				updatePassportList();
 				updateIndentity();
@@ -318,46 +319,27 @@ function vereignRestoreIdentity() {
 	});
 }
 
-function vereignSign() {
+function vereignSignAndUploadDocument() {
 	if (library == null) {
 		return;
 	}
 	if (currentPassport == null) {
 		return;
 	}
-	library.getOneTimeCertificateByPassport(currentPassport.uuid).then(function(result) {
-		if (isSuccess(result)) {
-			var otp = result.data;
-			var blob = new Blob(['signdocument\n', JSON.stringify(otp)]);
-			_map._socket.sendMessage(blob);
-			awaitForDocumentStatusToUpload = true;
-			checkCurrentDocument();
+	vex.dialog.open({
+		message: _('Select document type to upload'),
+		input: _('Type:') + '<select name="selection"><option value="ODT">ODT</option><option value="DOCX">DOCX</option><option value="PDF">PDF</option></select>',
+		callback: function(data) {
+			vereignSignAndUploadForType(data.selection);
 		}
 	});
 }
 
-function vereignUploadForType(uploadDocType) {
-	var vereignWopiUrl = getVereignWopiURL();
-	if (vereignWopiUrl == null || vereignWopiUrl == '')
+function vereignUpload(documentType) {
+	if (library == null || documentType == null) {
 		return;
-
-	var documentType = null;
-
-	switch (uploadDocType) {
-	case 'ODT':
-		documentType = 'odt';
-		break;
-	case 'DOCX':
-		documentType = 'docx';
-		break;
-	case 'PDF':
-		documentType = 'pdf';
-		break;
 	}
 
-	if (documentType == null)
-		return;
-
 	var filename = getCurrentDocumentFilename(documentType);
 
 	library.getPassports(filename).then(function(result) {
@@ -369,7 +351,7 @@ function vereignUploadForType(uploadDocType) {
 			if (currentPassport.uuid == resultArray[i].PassportUUID) {
 				var jsonRequest = {
 					filename: filename,
-					wopiUrl: vereignWopiUrl + 'files/',
+					wopiUrl: getVereignWopiURL() + 'files',
 					token: resultArray[i].AccessToken,
 					type: documentType
 				};
@@ -386,20 +368,72 @@ function vereignUploadForType(uploadDocType) {
 	});
 }
 
-function vereignUploadDialog() {
-	if (library == null) {
-		return;
-	}
-
-	vex.dialog.open({
-		message: _('Select document type to upload'),
-		input: _('Type:') + '<select name="selection"><option value="ODT">ODT</option><option value="DOCX">DOCX</option><option value="PDF">PDF</option></select>',
-		callback: function(data) {
-			vereignUploadForType(data.selection);
+function vereignSignAndUploadPDF(documentType) {
+	library.getOneTimeCertificateByPassport(currentPassport.uuid).then(function(result) {
+		if (!isSuccess(result)) {
+			return;
 		}
+		var otp = result.data;
+		var filename = getCurrentDocumentFilename(documentType);
+		library.getPassports(filename).then(function(result) {
+			if (!isSuccess(result)) {
+				return;
+			}
+			var resultArray = result.data;
+			for (var i = 0; i < resultArray.length; i++) {
+				if (currentPassport.uuid == resultArray[i].PassportUUID) {
+					var parameters = {
+						x509Certificate: otp.x509Certificate,
+						privateKey: otp.privateKey,
+						chain: otp.chain,
+						filename: filename,
+						wopiUrl: getVereignWopiURL() + 'files',
+						token: resultArray[i].AccessToken,
+						type: documentType
+					}
+					var blob = new Blob(['exportsignanduploaddocument\n', JSON.stringify(parameters)]);
+					_map._socket.sendMessage(blob);
+				}
+			}
+		});
 	});
 }
 
+function vereignSignAndUploadForType(uploadDocType) {
+	var documentType = null;
+
+	switch (uploadDocType) {
+	case 'ODT':
+		documentType = 'odt';
+		break;
+	case 'DOCX':
+		documentType = 'docx';
+		break;
+	case 'PDF':
+		documentType = 'pdf';
+		break;
+	}
+
+	if (documentType == null)
+		return;
+
+	if (uploadDocType == 'PDF') {
+		vereignSignAndUploadPDF(documentType);
+	}
+	else if (uploadDocType == 'DOCX' || uploadDocType == 'ODT') {
+		library.getOneTimeCertificateByPassport(currentPassport.uuid).then(function(result) {
+			if (isSuccess(result)) {
+				var otp = result.data;
+				var blob = new Blob(['signdocument\n', JSON.stringify(otp)]);
+				_map._socket.sendMessage(blob);
+				awaitForDocumentStatusToUpload = true;
+				currentDocumentType = documentType;
+				checkCurrentDocument();
+			}
+		});
+	}
+}
+
 L.Map.include({
 	showSignDocument: function() {
 		$('#document-signing-bar').show();
@@ -424,9 +458,6 @@ L.Map.include({
 		$('#document-signing-bar').hide();
 		adjustUIState();
 	},
-	signAndUploadDocument: function() {
-		vereignSign();
-	},
 	signingLogout: function() {
 		if (library) {
 			library.logout().then(function() {
@@ -555,7 +586,7 @@ L.Map.include({
 			this.signingLogout();
 		}
 		else if (id === 'sign-upload') {
-			this.signAndUploadDocument();
+			vereignSignAndUploadDocument();
 		}
 		else if (id.startsWith('passport:')) {
 			this.setCurrentPassport(item.value, item.text);
@@ -604,8 +635,9 @@ L.Map.include({
 
 		if (awaitForDocumentStatusToUpload) {
 			awaitForDocumentStatusToUpload = false;
-			vereignUploadDialog();
+			vereignUpload(currentDocumentType);
 		}
 		awaitForDocumentStatusToUpload = false;
+		currentDocumentType = null;
 	}
 });
diff --git a/test/WhiteBoxTests.cpp b/test/WhiteBoxTests.cpp
index 1da160d4e..21ca04bd1 100644
--- a/test/WhiteBoxTests.cpp
+++ b/test/WhiteBoxTests.cpp
@@ -509,6 +509,11 @@ public:
     {
     }
 
+    std::shared_ptr<lok::Office> getLOKit() override
+    {
+        return nullptr;
+    }
+
     std::shared_ptr<lok::Document> getLOKitDocument() override
     {
         return nullptr;
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index 9cdadf82d..3c53fdb53 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -175,6 +175,7 @@ bool ClientSession::_handleInput(const char *buffer, int length)
              tokens[0] != "signdocument" &&
              tokens[0] != "asksignaturestatus" &&
              tokens[0] != "uploadsigneddocument" &&
+             tokens[0] != "exportsignanduploaddocument" &&
              tokens[0] != "rendershapeselection" &&
              tokens[0] != "removesession")
     {


More information about the Libreoffice-commits mailing list