[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