diff --git a/copyparty/__main__.py b/copyparty/__main__.py index 604c0210..50394fd9 100644 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -1570,8 +1570,9 @@ def add_opds(ap): def add_wopi(ap): ap2 = ap.add_argument_group("WOPI options") - ap2.add_argument("--wopi", action="store_true", help="enable WOPI -- allows for integrating with office suites (volflag=wopi)") - ap2.add_argument("--wopi-path", action="store_true", default="wopi", help="where to expose WOPI (needs to start with 'wopi'); defaults to /wopi/ (volflag=wopi-path)") + ap2.add_argument("--wopi", action="store_true", help="allows for integrating with office suites using WOPI (volflag=wopi)") + ap2.add_argument("--wopi-client", type=u, default="https://localhost:9980", help="where to find your WOPI client, this is what actually hosts e.g. Collabora Online") + ap2.add_argument("--wopi-self-signed", action="store_true", help="disable certificate verification of the WOPI client, only use with local use") def add_handlers(ap): diff --git a/copyparty/cfg.py b/copyparty/cfg.py index a45fa780..0c5f6c4f 100644 --- a/copyparty/cfg.py +++ b/copyparty/cfg.py @@ -240,7 +240,6 @@ flagcats = { "gz": "allows server-side gzip compression of uploads with ?gz", "xz": "allows server-side lzma compression of uploads with ?xz", "pk": "forces server-side compression, optional arg: xz,9", - "wopi": "enable WOPI support for integrating with online office suites", }, "upload rules": { "apnd_who=dw": "who can append? (aw/dw/w/no)", @@ -395,6 +394,11 @@ flagcats = { "og_no_head": "you want to add tags manually with og_tpl", "og_ua": "if defined: only send OG html if useragent matches this regex", }, + "wopi": { + "wopi": "enable WOPI support for integrating with online office suites", + "wopi-client": "address of WOPI client, e.g. Collabora Online", + "wopi-self-signed": "disable certificate verification of the WOPI client, only use with local use" + }, "opds": { "opds": "enable OPDS", "opds_exts": "file formats to list in OPDS feeds; leave empty to show everything", diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py index ed5b5ea8..4732611d 100644 --- a/copyparty/httpcli.py +++ b/copyparty/httpcli.py @@ -16,6 +16,10 @@ import sys import threading # typechk import time import uuid +import urllib.request +import urllib.parse +import ssl +import xml.etree.ElementTree as ET from datetime import datetime from operator import itemgetter @@ -1524,35 +1528,34 @@ class HttpCli(object): if "rss" in self.uparam: return self.tx_rss() - if "wopi" in self.uparam: - return self.tx_wopi() + if self.args.wopi: + if "wopi" in self.uparam: + return self.tx_wopi() + + if self.vpath.startswith("wopi"): + return self.tx_wopi_api() return self.tx_browser() - def tx_wopi(self) -> bool: - self.log("%s" % self.uparam.get("wopi")) - return False - # https://' + location.hostname + ':9980/browser/4610258811/cool.html?WOPISrc=' + location.origin + '/wopi/files' + top + bhref + + def tx_wopi_api(self) -> bool: path = self.vpath.split('/') - if path[1] == "files": - if path[-1] == "contents": - vfs, _ = self.asrv.vfs.get("/".join(path[2:-1]), self.uname, False, True) - full_path = vfs.realpath + "/" + "/".join(path[2:-1]) + if "files" in path: + real_path = self.conn.hsrv.wopi_files[path[2]] + vfs, _ = self.asrv.vfs.get(real_path, self.uname, False, True) + full_path = vfs.realpath + "/" + real_path + if "contents" in path: if self.do_log: self.log("WOPI GET 'contents': %s" % (full_path)) return self.tx_file("oh_f", full_path) else: - vfs, _ = self.asrv.vfs.get("/".join(path[2:]), self.uname, False, True) - full_path = vfs.realpath + "/" + "/".join(path[2:]) - if self.do_log: self.log("WOPI GET 'file_info': %s" % (full_path)) file_info = { - "BaseFileName": path[-1], + "BaseFileName": real_path.split("/")[-1], "OwnerId": self.uname, "Size": os.path.getsize(full_path), "UserId": self.uname, @@ -1567,7 +1570,7 @@ class HttpCli(object): # "HideRepairOption": , # "DisableExport": , # "DisableCopy": , - "EnableOwnerTermination": True, + # "EnableOwnerTermination": True, "LastModifiedTime": time.strftime( "%Y-%m-%dT%H:%M:%SZ", @@ -1582,7 +1585,61 @@ class HttpCli(object): self.reply(ret, 200, "application/json; charset=utf-8") return True - return self.tx_404(True) + return self.tx_404() + + def tx_wopi(self) -> bool: + path = self.vpath + "/" + str(self.uparam["wopi"]) + file_key = self.gen_fk(2, self.args.fk_salt, path, 0, 0) + self.conn.hsrv.wopi_files[file_key] = path + + try: + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) + ctx.verify_mode = ssl.CERT_NONE if self.args.wopi_self_signed else ssl.CERT_REQUIRED + discovery = urllib.request.urlopen(self.args.wopi_client + "/hosting/discovery", context=ctx) + response = ET.fromstring(discovery.read()) + ext = path.split('.')[-1] + wopi_url = response.find(".//action[@ext='%s'][@urlsrc]" % ext).get("urlsrc") + favicon_url = response.find(".//action[@ext='%s'].." % ext).get("favIconUrl") + url = wopi_url + "WOPISrc=https://" + self.host + "/wopi/files/" + file_key + except Exception as error: + self.log("Couldn't get urls from WOPI client: %s" % error) + return False + + + ret = [ + """\ + + + + + + + Load Collabora Online + + + +