[Libreoffice-commits] online.git: 3 commits - capabilities.json common/Common.hpp discovery.xml test/DeltaTests.cpp test/integration-http-server.cpp wsd/LOOLWSD.cpp wsd/Storage.cpp wsd/Storage.hpp

Libreoffice Gerrit user logerrit at kemper.freedesktop.org
Sat Oct 20 10:20:05 UTC 2018


 capabilities.json                |    5 ++
 common/Common.hpp                |    2 +
 discovery.xml                    |    4 ++
 test/DeltaTests.cpp              |    2 -
 test/integration-http-server.cpp |   74 +++++++++++++++++++++++++++++++++++++
 wsd/LOOLWSD.cpp                  |   78 ++++++++++++++++++++++++++++++++++++---
 wsd/Storage.cpp                  |    5 ++
 wsd/Storage.hpp                  |    2 +
 8 files changed, 165 insertions(+), 7 deletions(-)

New commits:
commit 683cedb17a32b75e10ae4ed11d996779f65ef87c
Author:     Tamás Zolnai <tamas.zolnai at collabora.com>
AuthorDate: Fri Oct 19 18:14:14 2018 +0200
Commit:     Tamás Zolnai <tamas.zolnai at collabora.com>
CommitDate: Fri Oct 19 18:15:22 2018 +0200

    Delta test: avoid dumping out useless data
    
    Change-Id: I05bbd05b3ee1e0a5b0687b7b6c981938a1493946

diff --git a/test/DeltaTests.cpp b/test/DeltaTests.cpp
index d3f020d25..6e230e17b 100644
--- a/test/DeltaTests.cpp
+++ b/test/DeltaTests.cpp
@@ -70,8 +70,6 @@ std::vector<char> DeltaTests::applyDelta(
     CPPUNIT_ASSERT(delta.size() >= 4);
     CPPUNIT_ASSERT(delta[0] == 'D');
 
-    std::cout << "apply delta of size " << delta.size() << "\n";
-
     // start with the same state.
     std::vector<char> output = pixmap;
     CPPUNIT_ASSERT_EQUAL(output.size(), size_t(pixmap.size()));
commit 8dd28ed6649bbd9506ca097547f898e5d4f1bddd
Author:     Tamás Zolnai <tamas.zolnai at collabora.com>
AuthorDate: Fri Oct 19 18:15:02 2018 +0200
Commit:     Tamás Zolnai <tamas.zolnai at collabora.com>
CommitDate: Fri Oct 19 18:15:22 2018 +0200

    Enable convert-to features for wopi hosts
    
    Change-Id: I8aa37c61f3df5356fb7a14d93cda6279e7ec3cf1

diff --git a/test/integration-http-server.cpp b/test/integration-http-server.cpp
index e8eb1197e..ef8476c9e 100644
--- a/test/integration-http-server.cpp
+++ b/test/integration-http-server.cpp
@@ -173,6 +173,7 @@ void HTTPServerTest::testCapabilities()
 
         Poco::JSON::Object::Ptr convert_to = features->get("convert-to").extract<Poco::JSON::Object::Ptr>();
         CPPUNIT_ASSERT(convert_to->has("available"));
+        CPPUNIT_ASSERT(convert_to->get("available"));
     }
 }
 
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index 90c3e31ba..4e87c6125 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -2256,7 +2256,7 @@ private:
 
             std::string format = (form.has("format") ? form.get("format") : "");
 
-            if (!allowPostFrom(socket->clientAddress()))
+            if (!allowPostFrom(socket->clientAddress()) || StorageBase::alowedWopiHost(socket->clientAddress()) )
             {
                 LOG_ERR("client address DENY: " << socket->clientAddress());
 
@@ -2717,7 +2717,7 @@ private:
         Poco::JSON::Object::Ptr features = jsonFile.extract<Poco::JSON::Object::Ptr>();
         Poco::JSON::Object::Ptr convert_to = features->get("convert-to").extract<Poco::JSON::Object::Ptr>();
 
-        Poco::Dynamic::Var available = allowPostFrom(socket->clientAddress());
+        Poco::Dynamic::Var available = allowPostFrom(socket->clientAddress()) || StorageBase::alowedWopiHost(socket->clientAddress());
         convert_to->set("available", available);
 
         std::ostringstream ostrJSON;
diff --git a/wsd/Storage.cpp b/wsd/Storage.cpp
index 3ceaf673e..722893abd 100644
--- a/wsd/Storage.cpp
+++ b/wsd/Storage.cpp
@@ -141,6 +141,11 @@ void StorageBase::initialize()
 #endif
 }
 
+bool StorageBase::alowedWopiHost(const std::string& uri)
+{
+    return WopiEnabled && WopiHosts.match(Poco::URI(uri).getHost());
+}
+
 #ifndef MOBILEAPP
 
 bool isLocalhost(const std::string& targetHost)
diff --git a/wsd/Storage.hpp b/wsd/Storage.hpp
index 1e9544c2e..a2025a4fd 100644
--- a/wsd/Storage.hpp
+++ b/wsd/Storage.hpp
@@ -174,6 +174,8 @@ public:
     static std::unique_ptr<StorageBase> create(const Poco::URI& uri,
                                                const std::string& jailRoot,
                                                const std::string& jailPath);
+
+    static bool alowedWopiHost(const std::string& uri);
 protected:
 
     /// Returns the root path of the jail directory of docs.
commit 0bb8b7c7a8a05ec95a7668c42b8b53c6f4da07c6
Author:     Tamás Zolnai <tamas.zolnai at collabora.com>
AuthorDate: Fri Oct 19 18:14:48 2018 +0200
Commit:     Tamás Zolnai <tamas.zolnai at collabora.com>
CommitDate: Fri Oct 19 18:15:22 2018 +0200

    Add /hosting/capabilities endpoint to advertise online features
    
    Add an entry to discovery.xml with the urlsrc where capabilities end
    point can be found. Use json format to send back the feature list.
    
    Change-Id: I390a53d956d53ca79e5a8090aead7f4131ec4ca0

diff --git a/capabilities.json b/capabilities.json
new file mode 100644
index 000000000..32ed3801f
--- /dev/null
+++ b/capabilities.json
@@ -0,0 +1,5 @@
+{
+  "convert-to": {
+    "available": false
+  }
+}
diff --git a/common/Common.hpp b/common/Common.hpp
index c7e246b88..013b30798 100644
--- a/common/Common.hpp
+++ b/common/Common.hpp
@@ -36,6 +36,8 @@ constexpr const char CHILD_URI[] = "/loolws/child?";
 constexpr const char NEW_CHILD_URI[] = "/loolws/newchild";
 constexpr const char LO_JAIL_SUBPATH[] = "lo";
 
+constexpr const char CAPABILITIES_END_POINT[] = "/hosting/capabilities";
+
 /// The HTTP response User-Agent.
 constexpr const char* HTTP_AGENT_STRING = "LOOLWSD HTTP Agent " LOOLWSD_VERSION;
 
diff --git a/discovery.xml b/discovery.xml
index 0a9ca48c7..b2999b8cf 100644
--- a/discovery.xml
+++ b/discovery.xml
@@ -339,5 +339,9 @@
         <app name="application/pdf">
             <action name="view" ext="pdf"/>
         </app>
+
+        <app name="Capabilities">
+            <action name="getinfo" ext=""/>
+        </app>
     </net-zone>
 </wopi-discovery>
diff --git a/test/integration-http-server.cpp b/test/integration-http-server.cpp
index 25fee24de..e8eb1197e 100644
--- a/test/integration-http-server.cpp
+++ b/test/integration-http-server.cpp
@@ -9,6 +9,11 @@
 
 #include <config.h>
 
+#include <Poco/DOM/AutoPtr.h>
+#include <Poco/DOM/DOMParser.h>
+#include <Poco/DOM/Document.h>
+#include <Poco/DOM/Element.h>
+#include <Poco/DOM/NodeList.h>
 #include <Poco/Net/AcceptCertificateHandler.h>
 #include <Poco/Net/FilePartSource.h>
 #include <Poco/Net/HTMLForm.h>
@@ -40,6 +45,7 @@ class HTTPServerTest : public CPPUNIT_NS::TestFixture
     CPPUNIT_TEST_SUITE(HTTPServerTest);
 
     CPPUNIT_TEST(testDiscovery);
+    CPPUNIT_TEST(testCapabilities);
     CPPUNIT_TEST(testLoleafletGet);
     CPPUNIT_TEST(testLoleafletPost);
     CPPUNIT_TEST(testScriptsAndLinksGet);
@@ -49,6 +55,7 @@ class HTTPServerTest : public CPPUNIT_NS::TestFixture
     CPPUNIT_TEST_SUITE_END();
 
     void testDiscovery();
+    void testCapabilities();
     void testLoleafletGet();
     void testLoleafletPost();
     void testScriptsAndLinksGet();
@@ -104,6 +111,72 @@ void HTTPServerTest::testDiscovery()
     CPPUNIT_ASSERT_EQUAL(std::string("text/xml"), response.getContentType());
 }
 
+
+void HTTPServerTest::testCapabilities()
+{
+    std::unique_ptr<Poco::Net::HTTPClientSession> session(helpers::createSession(_uri));
+
+    // Get discovery first and extract the urlsrc of the capabilities end point
+    std::string capabiltiesURI;
+    {
+
+        Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, "/hosting/discovery");
+        session->sendRequest(request);
+
+        Poco::Net::HTTPResponse response;
+        std::istream& rs = session->receiveResponse(response);
+        CPPUNIT_ASSERT_EQUAL(Poco::Net::HTTPResponse::HTTP_OK, response.getStatus());
+        CPPUNIT_ASSERT_EQUAL(std::string("text/xml"), response.getContentType());
+
+        std::string discoveryXML;
+        Poco::StreamCopier::copyToString(rs, discoveryXML);
+
+        Poco::XML::DOMParser parser;
+        Poco::XML::AutoPtr<Poco::XML::Document> docXML = parser.parseString(discoveryXML);
+        Poco::XML::AutoPtr<Poco::XML::NodeList> listNodes = docXML->getElementsByTagName("action");
+        bool foundCapabilities = false;
+        for (unsigned long index = 0; index < listNodes->length(); ++index)
+        {
+            Poco::XML::Element* elem = static_cast<Poco::XML::Element*>(listNodes->item(index));
+            Poco::XML::Element* parent = elem->parentNode() ? static_cast<Poco::XML::Element*>(elem->parentNode()) : nullptr;
+            if(parent && parent->getAttribute("name") == "Capabilities")
+            {
+                foundCapabilities = true;
+                capabiltiesURI = elem->getAttribute("urlsrc");
+                break;
+            }
+        }
+
+        CPPUNIT_ASSERT(foundCapabilities);
+        CPPUNIT_ASSERT_EQUAL(_uri.toString() + CAPABILITIES_END_POINT, capabiltiesURI);
+    }
+
+    // Then get the capabilities json
+    {
+        Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, CAPABILITIES_END_POINT);
+        session->sendRequest(request);
+
+        Poco::Net::HTTPResponse response;
+        std::istream& rs = session->receiveResponse(response);
+        CPPUNIT_ASSERT_EQUAL(Poco::Net::HTTPResponse::HTTP_OK, response.getStatus());
+        CPPUNIT_ASSERT_EQUAL(std::string("application/json"), response.getContentType());
+
+        std::ostringstream oss;
+        Poco::StreamCopier::copyStream(rs, oss);
+        std::string responseString = oss.str();
+
+        Poco::JSON::Parser parser;
+        Poco::Dynamic::Var jsonFile = parser.parse(responseString);
+        Poco::JSON::Object::Ptr features = jsonFile.extract<Poco::JSON::Object::Ptr>();
+        CPPUNIT_ASSERT(features);
+        CPPUNIT_ASSERT(features->has("convert-to"));
+
+        Poco::JSON::Object::Ptr convert_to = features->get("convert-to").extract<Poco::JSON::Object::Ptr>();
+        CPPUNIT_ASSERT(convert_to->has("available"));
+    }
+}
+
+
 void HTTPServerTest::testLoleafletGet()
 {
     std::unique_ptr<Poco::Net::HTTPClientSession> session(helpers::createSession(_uri));
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index 1f0c112b8..90c3e31ba 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -2015,6 +2015,10 @@ private:
             {
                 handleWopiDiscoveryRequest(request);
             }
+            else if (request.getMethod() == HTTPRequest::HTTP_GET && request.getURI() == CAPABILITIES_END_POINT)
+            {
+                handleCapabilitiesRequest(request);
+            }
             else if (request.getMethod() == HTTPRequest::HTTP_GET && request.getURI() == "/robots.txt")
             {
                 handleRobotsTxtRequest(request);
@@ -2162,6 +2166,28 @@ private:
         LOG_INF("Sent discovery.xml successfully.");
     }
 
+    void handleCapabilitiesRequest(const Poco::Net::HTTPRequest& request)
+    {
+        LOG_DBG("Wopi capabilities request: " << request.getURI());
+
+        std::string capabilities = getCapabilitiesJson();
+
+        std::ostringstream oss;
+        oss << "HTTP/1.1 200 OK\r\n"
+            << "Last-Modified: " << Poco::DateTimeFormatter::format(Poco::Timestamp(), Poco::DateTimeFormat::HTTP_FORMAT) << "\r\n"
+            << "User-Agent: " << WOPI_AGENT_STRING << "\r\n"
+            << "Content-Length: " << capabilities.size() << "\r\n"
+            << "Content-Type: application/json\r\n"
+            << "X-Content-Type-Options: nosniff\r\n"
+            << "\r\n"
+            << capabilities;
+
+        auto socket = _socket.lock();
+        socket->send(oss.str());
+        socket->shutdown();
+        LOG_INF("Sent cpabilities.json successfully.");
+    }
+
     void handleRobotsTxtRequest(const Poco::Net::HTTPRequest& request)
     {
         LOG_DBG("HTTP request: " << request.getURI());
@@ -2626,15 +2652,16 @@ private:
         const std::string urlsrc = "urlsrc";
         const auto& config = Application::instance().config();
         const std::string loleafletHtml = config.getString("loleaflet_html", "loleaflet.html");
-        const std::string uriValue =
+        const std::string rootUriValue =
 #if ENABLE_SSL
             ((LOOLWSD::isSSLEnabled() || LOOLWSD::isSSLTermination()) ? "https://" : "http://")
 #else
             "http://"
 #endif
             + std::string("%SERVER_HOST%")
-            + LOOLWSD::ServiceRoot
-            + "/loleaflet/" LOOLWSD_VERSION_HASH "/" + loleafletHtml + '?';
+            + LOOLWSD::ServiceRoot;
+        const std::string uriValue = rootUriValue
+                                   + "/loleaflet/" LOOLWSD_VERSION_HASH "/" + loleafletHtml + '?';
 
         InputSource inputSrc(discoveryPath);
         DOMParser parser;
@@ -2644,7 +2671,15 @@ private:
         for (unsigned long it = 0; it < listNodes->length(); ++it)
         {
             Element* elem = static_cast<Element*>(listNodes->item(it));
-            elem->setAttribute(urlsrc, uriValue);
+            Element* parent = elem->parentNode() ? static_cast<Element*>(elem->parentNode()) : nullptr;
+            if(parent && parent->getAttribute("name") == "Capabilities")
+            {
+                elem->setAttribute(urlsrc, rootUriValue + CAPABILITIES_END_POINT);
+            }
+            else
+            {
+                elem->setAttribute(urlsrc, uriValue);
+            }
 
             // Set the View extensions cache as well.
             if (elem->getAttribute("name") == "edit")
@@ -2657,6 +2692,39 @@ private:
         return ostrXML.str();
     }
 
+    /// Process the capabilities.json file and return as string.
+    std::string getCapabilitiesJson()
+    {
+        std::shared_ptr<StreamSocket> socket = _socket.lock();
+
+        // http://server/hosting/capabilities
+#if defined __linux && defined MOBILEAPP
+        std::string capabilitiesPath = Path(Application::instance().commandPath()).parent().parent().toString() + "capabilities.json";
+#else
+        std::string capabilitiesPath = Path(Application::instance().commandPath()).parent().toString() + "capabilities.json";
+#endif
+        if (!File(capabilitiesPath).exists())
+        {
+            capabilitiesPath = LOOLWSD::FileServerRoot + "/capabilities.json";
+        }
+        std::ifstream ifs (capabilitiesPath.c_str(), std::ifstream::in);
+
+        if(!ifs.is_open())
+            return "";
+
+        Poco::JSON::Parser parser;
+        Poco::Dynamic::Var jsonFile = parser.parse(ifs);
+        Poco::JSON::Object::Ptr features = jsonFile.extract<Poco::JSON::Object::Ptr>();
+        Poco::JSON::Object::Ptr convert_to = features->get("convert-to").extract<Poco::JSON::Object::Ptr>();
+
+        Poco::Dynamic::Var available = allowPostFrom(socket->clientAddress());
+        convert_to->set("available", available);
+
+        std::ostringstream ostrJSON;
+        features->stringify(ostrJSON);
+        return ostrJSON.str();
+    }
+
 private:
     // The socket that owns us (we can't own it).
     std::weak_ptr<StreamSocket> _socket;


More information about the Libreoffice-commits mailing list