diff --git a/copyparty/__main__.py b/copyparty/__main__.py index 0910be10d..6971c813e 100644 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -1631,6 +1631,7 @@ def add_opds(ap): def add_wopi(ap): ap2 = ap.add_argument_group("WOPI options") ap2.add_argument("--wopi", action="store_true", help="enable integration with office suites using WOPI") + ap2.add_argument("--wopi-accs", metavar="TXT", type=u, default="", help="restrict wopi to this comma-separated list of usernames; default is everyone") ap2.add_argument("--wopi-api", metavar="URL", type=u, default="", help="URL that the WOPI-client should use to communicate with copyparty; default is same as user's webbrowser. Example: [\033[32mhttps://party.example.com/\033[0m]") ap2.add_argument("--wopi-url", metavar="URL", type=u, default="", help="URL to your WOPI client; the host of e.g. Collabora Online. Example: [\033[32mhttps://code.example.com/\033[0m]") ap2.add_argument("--wopi-urls", metavar="H=U", type=u, action="append", help="\033[34mREPEATABLE:\033[0m maps http \033[33mH\033[0mOST copyparty being accessed by to specific WOPI client instance \033[33mU\033[0mRL; falls back to \033[33m--wopi-url\033[0m; examples: [\033[32mparty.public.com=https://office.public.com/\033[0m], [\033[32mparty.internal.net:8443=https://office.internal.net:8443/\033[0m]") diff --git a/copyparty/authsrv.py b/copyparty/authsrv.py index b0fb095f9..c522e1617 100644 --- a/copyparty/authsrv.py +++ b/copyparty/authsrv.py @@ -2040,6 +2040,15 @@ class AuthSrv(object): umap[usr].sort() setattr(vfs, "a" + perm, umap) + if self.args.wopi: + zsl = unames[:] + if self.args.wopi_accs: + zsl = [x.strip() for x in self.args.wopi_accs.split(",")] + axs = AXS(zsl, zsl, None, zsl) + vn = VFS(self.log_func, "", "wopi", "", axs, self.vf0()) + vn.flags["unlistcr"] = vn.flags["unlistcw"] = True + vfs.nodes["wopi"] = vfs.all_nodes["wopi"] = vn + for vol in vfs.all_nodes.values(): za = vol.axs vol.uaxs = { diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py index 7559fba4a..a8e5da8eb 100644 --- a/copyparty/httpcli.py +++ b/copyparty/httpcli.py @@ -849,6 +849,11 @@ class HttpCli(object): self.asrv.vfs.get( wopi_f["vp"], uname, True, True, False, self.args.wopi_wdel ) + vn, rem = self.asrv.vfs.get( + self.vpath, uname, True, True, False, self.args.wopi_wdel + ) + if vn.vpath != "wopi": + raise Exception("bad url") self.uname = uname except Exception as ex: self.conn.hsrv.wopi_files.pop(wopi_a, None) @@ -1578,13 +1583,17 @@ class HttpCli(object): if "wopi" in self.uparam: return self.tx_wopi() - if self.vpath.startswith("wopi"): + if self.vn.vpath == "wopi": return self.tx_wopi_api() return self.tx_browser() def tx_wopi_api(self) -> bool: - atoken = self.uparam["access_token"] + try: + atoken = self.uparam["access_token"] + except: + raise Pebkac(400, "wopi access_token is mandatory") + session = self.conn.hsrv.wopi_files[atoken] if self.do_log: self.log(" `-- wopi: %r" % (session["vp"],)) @@ -1620,6 +1629,7 @@ class HttpCli(object): return self.tx_404() def tx_wopi(self) -> bool: + self.asrv.vfs.get("wopi", self.uname, True, True, False, self.args.wopi_wdel) vpath = vjoin(self.vpath, self.uparam["wopi"]) vfs, rem = self.asrv.vfs.get( vpath, self.uname, True, True, False, self.args.wopi_wdel @@ -3553,7 +3563,11 @@ class HttpCli(object): return True def rx_wopi(self, postsize: int) -> bool: - atoken = self.uparam["access_token"] + try: + atoken = self.uparam["access_token"] + except: + raise Pebkac(400, "wopi access_token is mandatory") + session = self.conn.hsrv.wopi_files[atoken] if self.do_log: self.log(" `-- wopi: %r" % (session["vp"],)) @@ -3562,8 +3576,7 @@ class HttpCli(object): if not self.vpath.startswith(zs): return self.tx_404() - vpath = self.conn.hsrv.wopi_files[self.uparam["access_token"]]["vp"] - vfs, rem = self.asrv.vfs.get(vpath, self.uname, False, True) + vfs, rem = self.asrv.vfs.get(session["vp"], self.uname, False, True) vpath = vjoin(vfs.vpath, rem) ap = vfs.canonical(rem) st = bos.stat(ap) @@ -6153,6 +6166,8 @@ class HttpCli(object): continue try: dvn, drem = vfs.get(vjoin(top, x), self.uname, False, False) + if not dvn.realpath and not dvn.nodes: + continue if ( self.uname not in dvn.axs.uread and self.uname not in dvn.axs.uwrite diff --git a/copyparty/httpsrv.py b/copyparty/httpsrv.py index 20916745b..2d13776b7 100644 --- a/copyparty/httpsrv.py +++ b/copyparty/httpsrv.py @@ -180,7 +180,7 @@ class HttpSrv(object): self.u2idx_free: dict[str, U2idx] = {} self.u2idx_n = 0 - self.wopi_files: dict[str, dict[str, str]] = {} + self.wopi_files: dict[str, dict[str, Any]] = {} assert jinja2 # type: ignore # !rm env = jinja2.Environment()