[Libreoffice-commits] online.git: 2 commits - loleaflet/README loleaflet/reference.html loleaflet/src loolwsd/LOOLSession.cpp loolwsd/LOOLSession.hpp loolwsd/LOOLWSD.cpp loolwsd/LOOLWSD.hpp loolwsd/protocol.txt

Mihai Varga mihai.varga at collabora.com
Fri Oct 9 06:15:07 PDT 2015


 loleaflet/README                      |    1 
 loleaflet/reference.html              |   18 ++++++++++
 loleaflet/src/control/Toolbar.js      |   13 +++++++
 loleaflet/src/core/Socket.js          |   12 +++++++
 loleaflet/src/layer/tile/TileLayer.js |   12 +++++++
 loleaflet/src/map/Map.js              |    1 
 loolwsd/LOOLSession.cpp               |   57 +++++++++++++++++++++++++++++++---
 loolwsd/LOOLSession.hpp               |    9 ++---
 loolwsd/LOOLWSD.cpp                   |   37 +++++++++++++++++-----
 loolwsd/LOOLWSD.hpp                   |    2 -
 loolwsd/protocol.txt                  |    9 +++++
 11 files changed, 154 insertions(+), 17 deletions(-)

New commits:
commit cf089ccf2a1dd6bf9602daa374b83ed4ad9d3c7d
Author: Mihai Varga <mihai.varga at collabora.com>
Date:   Fri Oct 9 16:12:07 2015 +0300

    tdf#94607 downloadAs command handler
    
    We use a hidden iframe to download the document. Once we have the url
    for it, we set iframr.src = url
    Also added map.downloadAs(name, format, options) method

diff --git a/loleaflet/README b/loleaflet/README
index 8cc2739..2011c33 100644
--- a/loleaflet/README
+++ b/loleaflet/README
@@ -133,6 +133,7 @@ Statusindicator (when the document is loading):
 Save:
     - API:
         map.saveAs(url, [format, options])
+        map.downloadAs(name, [format, options])
 
 Scroll (the following are measured in pixels):
     - API:
diff --git a/loleaflet/reference.html b/loleaflet/reference.html
index 057c2d5..9a7b3e8 100644
--- a/loleaflet/reference.html
+++ b/loleaflet/reference.html
@@ -1507,6 +1507,24 @@ var map = L.map('map', {
 		<td><code>undefined</code></td>
 		<td>Toggles the state for the given UNO command.</td>
 	</tr>
+	<tr>
+		<td><code><b>saveAs</b>(
+			<nobr><String><i>url</i>,</nobr>
+            <nobr><String><i>format?</i>,</nobr>
+			<nobr><String><i>options?</i>)</nobr>
+		</code></td>
+		<td><code>undefined</code></td>
+        <td>Save the document as "format" at the given url by applying the filter options.</td>
+	</tr>
+	<tr>
+		<td><code><b>downloadAs</b>(
+			<nobr><String><i>name</i>,</nobr>
+			<nobr><String><i>format?</i>,</nobr>
+            <nobr><String><i>options?</i>)</nobr>
+		</code></td>
+		<td><code>undefined</code></td>
+        <td>Download the document as "format" with the name "name" by applying the filter options.</td>
+	</tr>
 </table>
 
 <h2 id="loleaflet-page">Page oriented</h2>
diff --git a/loleaflet/src/control/Toolbar.js b/loleaflet/src/control/Toolbar.js
index b8b316d..243b24a 100644
--- a/loleaflet/src/control/Toolbar.js
+++ b/loleaflet/src/control/Toolbar.js
@@ -31,6 +31,19 @@ L.Map.include({
 		return this._docLayer._toolbarCommandValues[command];
 	},
 
+	downloadAs: function (name, format, options) {
+		if (format === undefined || format === null) {
+			format = '';
+		}
+		if (options === undefined || options === null) {
+			options = '';
+		}
+		L.Socket.sendMessage('downloadas ' +
+			'name=' + name + ' ' +
+			'format=' + format + ' ' +
+			'options=' + options);
+	},
+
 	saveAs: function (url, format, options) {
 		if (format === undefined || format === null) {
 			format = '';
diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index 99a8c19..947adf5 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -196,6 +196,18 @@ L.Socket = {
 			else if (tokens[i].substring(0, 5) === 'kind=') {
 				command.errorKind = tokens[i].substring(5);
 			}
+			else if (tokens[i].substring(0, 5) === 'jail=') {
+				command.jail = tokens[i].substring(5);
+			}
+			else if (tokens[i].substring(0, 4) === 'dir=') {
+				command.dir = tokens[i].substring(4);
+			}
+			else if (tokens[i].substring(0, 5) === 'name=') {
+				command.name = tokens[i].substring(5);
+			}
+			else if (tokens[i].substring(0, 5) === 'port=') {
+				command.port = tokens[i].substring(5);
+			}
 		}
 		if (command.tileWidth && command.tileHeight && this._map._docLayer) {
 			var scale = command.tileWidth / this._map._docLayer.options.tileWidthTwips;
diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index 6be61de..1054e73 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -185,6 +185,9 @@ L.TileLayer = L.GridLayer.extend({
 		else if (textMsg.startsWith('cursorvisible:')) {
 			this._onCursorVisibleMsg(textMsg);
 		}
+		else if (textMsg.startsWith('downloadas:')) {
+			this._onDownloadAsMsg(textMsg);
+		}
 		else if (textMsg.startsWith('error:')) {
 			this._onErrorMsg(textMsg);
 		}
@@ -264,6 +267,15 @@ L.TileLayer = L.GridLayer.extend({
 		this._onUpdateCursor();
 	},
 
+	_onDownloadAsMsg: function (textMsg) {
+		var command = L.Socket.parseServerCmd(textMsg);
+		var parser = document.createElement('a');
+		parser.href = this._map.options.server;
+		var url = 'http://' + parser.hostname + ':' + command.port + '/' +
+			command.jail + '/' + command.dir + '/' + command.name;
+		this._map._fileDownloader.src = url;
+	},
+
 	_onErrorMsg: function (textMsg) {
 		var command = L.Socket.parseServerCmd(textMsg);
 		this._map.fire('error', {cmd: command.errorCmd, kind: command.errorKind});
diff --git a/loleaflet/src/map/Map.js b/loleaflet/src/map/Map.js
index c1db931..df230b9 100644
--- a/loleaflet/src/map/Map.js
+++ b/loleaflet/src/map/Map.js
@@ -458,6 +458,7 @@ L.Map = L.Evented.extend({
 		var textAreaContainer = L.DomUtil.create('div', 'clipboard-container', container.parentElement);
 		this._textArea = L.DomUtil.create('textarea', 'clipboard', textAreaContainer);
 		this._resizeDetector = L.DomUtil.create('iframe', 'resize-detector', container);
+		this._fileDownloader = L.DomUtil.create('iframe', 'resize-detector', container);
 
 		container._leaflet = true;
 	},
commit 676c6d60d1dfd02435f2fc4440c21241116c6539
Author: Mihai Varga <mihai.varga at collabora.com>
Date:   Fri Oct 9 15:55:49 2015 +0300

    tdf#94607 downloadAs command that generates an URL for the doc
    
    When requested, the document is exported under
    /jail_path/CHILD_ID/user/thedocument/RANDOMDIR/filename
    and CHILD_ID, RANDOMDIR and the filename are communicated to the client.
    
    When the client requests
    http://server:port/CHILD_ID/RANDOMDIR/filename, the exported document
    is served and then RANDOMDIR is removed

diff --git a/loolwsd/LOOLSession.cpp b/loolwsd/LOOLSession.cpp
index d8b7dd0..0b2780c 100644
--- a/loolwsd/LOOLSession.cpp
+++ b/loolwsd/LOOLSession.cpp
@@ -290,11 +290,12 @@ bool MasterProcessSession::handleInput(const char *buffer, int length)
     }
     else if (tokens[0] != "canceltiles" &&
              tokens[0] != "commandvalues" &&
-             tokens[0] != "partpagerectangles" &&
+             tokens[0] != "downloadas" &&
              tokens[0] != "gettextselection" &&
              tokens[0] != "invalidatetiles" &&
              tokens[0] != "key" &&
              tokens[0] != "mouse" &&
+             tokens[0] != "partpagerectangles" &&
              tokens[0] != "requestloksession" &&
              tokens[0] != "resetselection" &&
              tokens[0] != "saveas" &&
@@ -632,10 +633,11 @@ void MasterProcessSession::forwardToPeer(const char *buffer, int length)
     peer->sendBinaryFrame(buffer, length);
 }
 
-ChildProcessSession::ChildProcessSession(std::shared_ptr<WebSocket> ws, LibreOfficeKit *loKit) :
+ChildProcessSession::ChildProcessSession(std::shared_ptr<WebSocket> ws, LibreOfficeKit *loKit, std::string childId) :
     LOOLSession(ws, Kind::ToMaster),
     _loKitDocument(NULL),
     _loKit(loKit),
+    _childId(childId),
     _clientPart(0)
 {
     std::cout << Util::logPrefix() << "ChildProcessSession ctor this=" << this << " ws=" << _ws.get() << std::endl;
@@ -699,7 +701,8 @@ bool ChildProcessSession::handleInput(const char *buffer, int length)
         // All other commands are such that they always require a LibreOfficeKitDocument session,
         // i.e. need to be handled in a child process.
 
-        assert(tokens[0] == "gettextselection" ||
+        assert(tokens[0] == "downloadas" ||
+               tokens[0] == "gettextselection" ||
                tokens[0] == "key" ||
                tokens[0] == "mouse" ||
                tokens[0] == "uno" ||
@@ -712,7 +715,11 @@ bool ChildProcessSession::handleInput(const char *buffer, int length)
         {
             _loKitDocument->pClass->setPart(_loKitDocument, _clientPart);
         }
-        if (tokens[0] == "gettextselection")
+        if (tokens[0] == "downloadas")
+        {
+            return downloadAs(buffer, length, tokens);
+        }
+        else if (tokens[0] == "gettextselection")
         {
             return getTextSelection(buffer, length, tokens);
         }
@@ -992,6 +999,48 @@ void ChildProcessSession::sendTile(const char *buffer, int length, StringTokeniz
     sendBinaryFrame(output.data(), output.size());
 }
 
+bool ChildProcessSession::downloadAs(const char *buffer, int length, StringTokenizer& tokens)
+{
+    std::string name, format, filterOptions;
+
+    if (tokens.count() < 4 ||
+        !getTokenString(tokens[1], "name", name))
+    {
+        sendTextFrame("error: cmd=saveas kind=syntax");
+        return false;
+    }
+
+    getTokenString(tokens[2], "format", format);
+
+    if (getTokenString(tokens[3], "options", filterOptions)) {
+        if (tokens.count() > 4) {
+            filterOptions += Poco::cat(std::string(" "), tokens.begin() + 4, tokens.end());
+        }
+    }
+
+    std::string tmpDir, url;
+    File *file = NULL;
+    do
+    {
+        if (file != NULL)
+        {
+            delete file;
+        }
+        tmpDir = std::to_string((((Poco::UInt64)LOOLWSD::_rng.next()) << 32) | LOOLWSD::_rng.next() | 1);
+        url = jailDocumentURL + "/" + tmpDir + "/" + name;
+        file = new File(url);
+    } while (file->exists());
+    delete file;
+
+    _loKitDocument->pClass->saveAs(_loKitDocument, url.c_str(),
+            format.size() == 0 ? NULL :format.c_str(),
+            filterOptions.size() == 0 ? NULL : filterOptions.c_str());
+
+    sendTextFrame("downloadas: jail=" + _childId + " dir=" + tmpDir + " name=" + name +
+            " port=" + std::to_string(LOOLWSD::portNumber));
+    return true;
+}
+
 bool ChildProcessSession::getTextSelection(const char *buffer, int length, StringTokenizer& tokens)
 {
     std::string mimeType;
diff --git a/loolwsd/LOOLSession.hpp b/loolwsd/LOOLSession.hpp
index 13970c6..70210ae 100644
--- a/loolwsd/LOOLSession.hpp
+++ b/loolwsd/LOOLSession.hpp
@@ -51,12 +51,12 @@ public:
 
     virtual bool handleInput(const char *buffer, int length) = 0;
 
+    static const std::string jailDocumentURL;
+
 protected:
     LOOLSession(std::shared_ptr<Poco::Net::WebSocket> ws, Kind kind);
     virtual ~LOOLSession();
 
-    static const std::string jailDocumentURL;
-
     const Kind _kind;
 
     std::string _kindString;
@@ -156,7 +156,7 @@ private:
 class ChildProcessSession final : public LOOLSession
 {
 public:
-    ChildProcessSession(std::shared_ptr<Poco::Net::WebSocket> ws, LibreOfficeKit *loKit);
+    ChildProcessSession(std::shared_ptr<Poco::Net::WebSocket> ws, LibreOfficeKit *loKit, std::string _childId);
     virtual ~ChildProcessSession();
 
     virtual bool handleInput(const char *buffer, int length) override;
@@ -175,6 +175,7 @@ public:
 
     virtual void sendTile(const char *buffer, int length, Poco::StringTokenizer& tokens);
 
+    bool downloadAs(const char *buffer, int length, Poco::StringTokenizer& tokens);
     bool getTextSelection(const char *buffer, int length, Poco::StringTokenizer& tokens);
     bool keyEvent(const char *buffer, int length, Poco::StringTokenizer& tokens);
     bool mouseEvent(const char *buffer, int length, Poco::StringTokenizer& tokens);
@@ -186,9 +187,9 @@ public:
     bool setClientPart(const char *buffer, int length, Poco::StringTokenizer& tokens);
     bool setPage(const char *buffer, int length, Poco::StringTokenizer& tokens);
 
-    std::string _jail;
     std::string _loSubPath;
     LibreOfficeKit *_loKit;
+    std::string _childId;
 
  private:
     int _clientPart;
diff --git a/loolwsd/LOOLWSD.cpp b/loolwsd/LOOLWSD.cpp
index e12cbf4..867be4f 100644
--- a/loolwsd/LOOLWSD.cpp
+++ b/loolwsd/LOOLWSD.cpp
@@ -175,11 +175,11 @@ private:
     tsqueue<std::string>& _queue;
 };
 
-class WebSocketRequestHandler: public HTTPRequestHandler
-    /// Handle a WebSocket connection.
+class RequestHandler: public HTTPRequestHandler
+    /// Handle a WebSocket connection or a simple HTTP request.
 {
 public:
-    WebSocketRequestHandler()
+    RequestHandler()
     {
     }
 
@@ -198,9 +198,30 @@ public:
 
         if(!(request.find("Upgrade") != request.end() && Poco::icompare(request["Upgrade"], "websocket") == 0))
         {
-            response.setStatusAndReason(HTTPResponse::HTTP_BAD_REQUEST);
-            response.setContentLength(0);
-            response.send();
+            // The user might request a file to download
+            StringTokenizer tokens(request.getURI(), "/");
+            if (tokens.count() != 4)
+            {
+                response.setStatus(HTTPResponse::HTTP_BAD_REQUEST);
+                response.setContentLength(0);
+                response.send();
+            }
+            std::string dirPath = LOOLWSD::childRoot + "/" + tokens[1] + LOOLSession::jailDocumentURL + "/" + tokens[2];
+            std::string filePath = dirPath + "/" + tokens[3];
+            std::cout << Util::logPrefix() << "HTTP request for: " << filePath << std::endl;
+            File file(filePath);
+            if (file.exists())
+            {
+                response.sendFile(filePath, "application/octet-stream");
+                File dir(dirPath);
+                dir.remove(true);
+            }
+            else
+            {
+                response.setStatus(HTTPResponse::HTTP_NOT_FOUND);
+                response.setContentLength(0);
+                response.send();
+            }
             return;
         }
 
@@ -340,7 +361,7 @@ public:
         }
 
         Application::instance().logger().information(line);
-        return new WebSocketRequestHandler();
+        return new RequestHandler();
     }
 };
 
@@ -777,7 +798,7 @@ void LOOLWSD::componentMain()
         HTTPResponse response;
         std::shared_ptr<WebSocket> ws(new WebSocket(cs, request, response));
 
-        std::shared_ptr<ChildProcessSession> session(new ChildProcessSession(ws, loKit));
+        std::shared_ptr<ChildProcessSession> session(new ChildProcessSession(ws, loKit, std::to_string(_childId)));
 
         ws->setReceiveTimeout(0);
 
diff --git a/loolwsd/LOOLWSD.hpp b/loolwsd/LOOLWSD.hpp
index aa419c6..964115f 100644
--- a/loolwsd/LOOLWSD.hpp
+++ b/loolwsd/LOOLWSD.hpp
@@ -41,6 +41,7 @@ public:
     static std::string jail;
     static Poco::SharedMemory _sharedForkChild;
     static Poco::NamedMutex _namedMutexLOOL;
+    static Poco::Random _rng;
 
     static const int DEFAULT_CLIENT_PORT_NUMBER = 9980;
     static const int MASTER_PORT_NUMBER = 9981;
@@ -70,7 +71,6 @@ private:
     Poco::UInt64 _childId;
     static int _numPreSpawnedChildren;
     static std::mutex _rngMutex;
-    static Poco::Random _rng;
 
 #if ENABLE_DEBUG
 public:
diff --git a/loolwsd/protocol.txt b/loolwsd/protocol.txt
index 88cc062..3c8369f 100644
--- a/loolwsd/protocol.txt
+++ b/loolwsd/protocol.txt
@@ -17,6 +17,10 @@ canceltiles
     dropped and will not be handled. There is no guarantee of exactly
     which tile: messages might still be sent back to the client.
 
+downloadas downloadas name=<fileName> format=<document format> options=<SkipImages, etc>
+
+    Exports the current document to the desired format and returns a download URL
+
 gettextselection mimetype=<mimeType>
 
     Request selection's content
@@ -82,6 +86,11 @@ partpagerectangles
 server -> client
 ================
 
+downloadas: jail=<jail directory> dir=<a tmp dir> name=<name> port=<port>
+
+    The client should then request http://server:port/jail/dir/name in order to download
+    the document
+
 error: cmd=<command> kind=<kind>
 <freeErrorText>
 


More information about the Libreoffice-commits mailing list