mirror of
https://github.com/9001/copyparty.git
synced 2025-08-16 16:42:13 -06:00
accept file POSTs without specifying the act field;
primarily to support uploading from Igloo IRC but also generally useful (not actually tested with Igloo IRC yet because it's a paid feature so just gonna wait for spiky to wake up and tell me it didn't work)
This commit is contained in:
parent
dc8e621d7c
commit
9bc09ce949
|
@ -1569,10 +1569,12 @@ interact with copyparty using non-browser clients
|
|||
* `var xhr = new XMLHttpRequest(); xhr.open('POST', '//127.0.0.1:3923/msgs?raw'); xhr.send('foo');`
|
||||
|
||||
* curl/wget: upload some files (post=file, chunk=stdin)
|
||||
* `post(){ curl -F act=bput -F f=@"$1" http://127.0.0.1:3923/?pw=wark;}`
|
||||
`post movie.mkv`
|
||||
* `post(){ curl -F f=@"$1" http://127.0.0.1:3923/?pw=wark;}`
|
||||
`post movie.mkv` (gives HTML in return)
|
||||
* `post(){ curl -F f=@"$1" 'http://127.0.0.1:3923/?want=url&pw=wark';}`
|
||||
`post movie.mkv` (gives hotlink in return)
|
||||
* `post(){ curl -H pw:wark -H rand:8 -T "$1" http://127.0.0.1:3923/;}`
|
||||
`post movie.mkv`
|
||||
`post movie.mkv` (randomized filename)
|
||||
* `post(){ wget --header='pw: wark' --post-file="$1" -O- http://127.0.0.1:3923/?raw;}`
|
||||
`post movie.mkv`
|
||||
* `chunk(){ curl -H pw:wark -T- http://127.0.0.1:3923/;}`
|
||||
|
|
|
@ -45,6 +45,7 @@ from .util import (
|
|||
ODict,
|
||||
Pebkac,
|
||||
UnrecvEOF,
|
||||
WrongPostKey,
|
||||
absreal,
|
||||
alltrace,
|
||||
atomic_move,
|
||||
|
@ -1862,7 +1863,16 @@ class HttpCli(object):
|
|||
self.parser = MultipartParser(self.log, self.sr, self.headers)
|
||||
self.parser.parse()
|
||||
|
||||
act = self.parser.require("act", 64)
|
||||
file0: list[tuple[str, Optional[str], Generator[bytes, None, None]]] = []
|
||||
try:
|
||||
act = self.parser.require("act", 64)
|
||||
except WrongPostKey as ex:
|
||||
if ex.got == "f" and ex.fname:
|
||||
self.log("missing 'act', but looks like an upload so assuming that")
|
||||
file0 = [(ex.got, ex.fname, ex.datagen)]
|
||||
act = "bput"
|
||||
else:
|
||||
raise
|
||||
|
||||
if act == "login":
|
||||
return self.handle_login()
|
||||
|
@ -1875,7 +1885,7 @@ class HttpCli(object):
|
|||
return self.handle_new_md()
|
||||
|
||||
if act == "bput":
|
||||
return self.handle_plain_upload()
|
||||
return self.handle_plain_upload(file0)
|
||||
|
||||
if act == "tput":
|
||||
return self.handle_text_upload()
|
||||
|
@ -2314,7 +2324,9 @@ class HttpCli(object):
|
|||
vfs.flags.get("xau") or [],
|
||||
)
|
||||
|
||||
def handle_plain_upload(self) -> bool:
|
||||
def handle_plain_upload(
|
||||
self, file0: list[tuple[str, Optional[str], Generator[bytes, None, None]]]
|
||||
) -> bool:
|
||||
assert self.parser
|
||||
nullwrite = self.args.nw
|
||||
vfs, rem = self.asrv.vfs.get(self.vpath, self.uname, False, True)
|
||||
|
@ -2340,7 +2352,8 @@ class HttpCli(object):
|
|||
t0 = time.time()
|
||||
try:
|
||||
assert self.parser.gen
|
||||
for nfile, (p_field, p_file, p_data) in enumerate(self.parser.gen):
|
||||
gens = itertools.chain(file0, self.parser.gen)
|
||||
for nfile, (p_field, p_file, p_data) in enumerate(gens):
|
||||
if not p_file:
|
||||
self.log("discarding incoming file without filename")
|
||||
# fallthrough
|
||||
|
|
|
@ -1537,11 +1537,9 @@ class MultipartParser(object):
|
|||
raises if the field name is not as expected
|
||||
"""
|
||||
assert self.gen
|
||||
p_field, _, p_data = next(self.gen)
|
||||
p_field, p_fname, p_data = next(self.gen)
|
||||
if p_field != field_name:
|
||||
raise Pebkac(
|
||||
422, 'expected field "{}", got "{}"'.format(field_name, p_field)
|
||||
)
|
||||
raise WrongPostKey(field_name, p_field, p_fname, p_data)
|
||||
|
||||
return self._read_value(p_data, max_len).decode("utf-8", "surrogateescape")
|
||||
|
||||
|
@ -3058,3 +3056,20 @@ class Pebkac(Exception):
|
|||
|
||||
def __repr__(self) -> str:
|
||||
return "Pebkac({}, {})".format(self.code, repr(self.args))
|
||||
|
||||
|
||||
class WrongPostKey(Pebkac):
|
||||
def __init__(
|
||||
self,
|
||||
expected: str,
|
||||
got: str,
|
||||
fname: Optional[str],
|
||||
datagen: Generator[bytes, None, None],
|
||||
) -> None:
|
||||
msg = 'expected field "{}", got "{}"'.format(expected, got)
|
||||
super(WrongPostKey, self).__init__(422, msg)
|
||||
|
||||
self.expected = expected
|
||||
self.got = got
|
||||
self.fname = fname
|
||||
self.datagen = datagen
|
||||
|
|
|
@ -162,8 +162,8 @@ authenticate using header `Cookie: cppwd=foo` or url param `&pw=foo`
|
|||
| PUT | | (binary data) | upload into file at URL |
|
||||
| PUT | `?gz` | (binary data) | compress with gzip and write into file at URL |
|
||||
| PUT | `?xz` | (binary data) | compress with xz and write into file at URL |
|
||||
| mPOST | | `act=bput`, `f=FILE` | upload `FILE` into the folder at URL |
|
||||
| mPOST | `?j` | `act=bput`, `f=FILE` | ...and reply with json |
|
||||
| mPOST | | `f=FILE` | upload `FILE` into the folder at URL |
|
||||
| mPOST | `?j` | `f=FILE` | ...and reply with json |
|
||||
| mPOST | | `act=mkdir`, `name=foo` | create directory `foo` at URL |
|
||||
| POST | `?delete` | | delete URL recursively |
|
||||
| jPOST | `?delete` | `["/foo","/bar"]` | delete `/foo` and `/bar` recursively |
|
||||
|
|
Loading…
Reference in a new issue