u2cli: server is allowed to reject dupes
This commit is contained in:
		
							parent
							
								
									29c212a60e
								
							
						
					
					
						commit
						3a800585bc
					
				
							
								
								
									
										70
									
								
								bin/up2k.py
									
									
									
									
									
								
							
							
						
						
									
										70
									
								
								bin/up2k.py
									
									
									
									
									
								
							| @ -3,7 +3,7 @@ from __future__ import print_function, unicode_literals | |||||||
| 
 | 
 | ||||||
| """ | """ | ||||||
| up2k.py: upload to copyparty | up2k.py: upload to copyparty | ||||||
| 2022-11-04, v0.21, ed <irc.rizon.net>, MIT-Licensed | 2022-11-29, v0.22, ed <irc.rizon.net>, MIT-Licensed | ||||||
| https://github.com/9001/copyparty/blob/hovudstraum/bin/up2k.py | https://github.com/9001/copyparty/blob/hovudstraum/bin/up2k.py | ||||||
| 
 | 
 | ||||||
| - dependencies: requests | - dependencies: requests | ||||||
| @ -94,6 +94,7 @@ class File(object): | |||||||
|         self.kchunks = {}  # type: dict[str, tuple[int, int]]  # hash: [ ofs, sz ] |         self.kchunks = {}  # type: dict[str, tuple[int, int]]  # hash: [ ofs, sz ] | ||||||
| 
 | 
 | ||||||
|         # set by handshake |         # set by handshake | ||||||
|  |         self.recheck = False  # duplicate; redo handshake after all files done | ||||||
|         self.ucids = []  # type: list[str]  # chunks which need to be uploaded |         self.ucids = []  # type: list[str]  # chunks which need to be uploaded | ||||||
|         self.wark = None  # type: str |         self.wark = None  # type: str | ||||||
|         self.url = None  # type: str |         self.url = None  # type: str | ||||||
| @ -472,8 +473,8 @@ def get_hashlist(file, pcb, mth): | |||||||
|         file.kchunks[k] = [v1, v2] |         file.kchunks[k] = [v1, v2] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def handshake(req_ses, url, file, pw, search): | def handshake(url, file, pw, search): | ||||||
|     # type: (requests.Session, str, File, any, bool) -> list[str] |     # type: (str, File, Any, bool) -> tuple[list[str], bool] | ||||||
|     """ |     """ | ||||||
|     performs a handshake with the server; reply is: |     performs a handshake with the server; reply is: | ||||||
|       if search, a list of search results |       if search, a list of search results | ||||||
| @ -507,6 +508,17 @@ def handshake(req_ses, url, file, pw, search): | |||||||
|             eprint("handshake failed, retrying: {0}\n  {1}\n\n".format(file.name, em)) |             eprint("handshake failed, retrying: {0}\n  {1}\n\n".format(file.name, em)) | ||||||
|             time.sleep(1) |             time.sleep(1) | ||||||
| 
 | 
 | ||||||
|  |     sc = r.status_code | ||||||
|  |     if sc >= 400: | ||||||
|  |         txt = r.text | ||||||
|  |         if sc == 422 or "<pre>partial upload exists at a different" in txt: | ||||||
|  |             file.recheck = True | ||||||
|  |             return [], False | ||||||
|  |         elif sc == 409 or "<pre>upload rejected, file already exists" in txt: | ||||||
|  |             return [], False | ||||||
|  | 
 | ||||||
|  |         raise Exception("http {0}: {1}".format(sc, txt)) | ||||||
|  | 
 | ||||||
|     try: |     try: | ||||||
|         r = r.json() |         r = r.json() | ||||||
|     except: |     except: | ||||||
| @ -528,8 +540,8 @@ def handshake(req_ses, url, file, pw, search): | |||||||
|     return r["hash"], r["sprs"] |     return r["hash"], r["sprs"] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def upload(req_ses, file, cid, pw): | def upload(file, cid, pw): | ||||||
|     # type: (requests.Session, File, str, any) -> None |     # type: (File, str, Any) -> None | ||||||
|     """upload one specific chunk, `cid` (a chunk-hash)""" |     """upload one specific chunk, `cid` (a chunk-hash)""" | ||||||
| 
 | 
 | ||||||
|     headers = { |     headers = { | ||||||
| @ -626,8 +638,8 @@ class Ctl(object): | |||||||
| 
 | 
 | ||||||
|             self.mutex = threading.Lock() |             self.mutex = threading.Lock() | ||||||
|             self.q_handshake = Queue()  # type: Queue[File] |             self.q_handshake = Queue()  # type: Queue[File] | ||||||
|             self.q_recheck = Queue()  # type: Queue[File]  # partial upload exists [...] |  | ||||||
|             self.q_upload = Queue()  # type: Queue[tuple[File, str]] |             self.q_upload = Queue()  # type: Queue[tuple[File, str]] | ||||||
|  |             self.recheck = []  # type: list[File] | ||||||
| 
 | 
 | ||||||
|             self.st_hash = [None, "(idle, starting...)"]  # type: tuple[File, int] |             self.st_hash = [None, "(idle, starting...)"]  # type: tuple[File, int] | ||||||
|             self.st_up = [None, "(idle, starting...)"]  # type: tuple[File, int] |             self.st_up = [None, "(idle, starting...)"]  # type: tuple[File, int] | ||||||
| @ -649,7 +661,7 @@ class Ctl(object): | |||||||
|             burl = self.ar.url[:12] + self.ar.url[8:].split("/")[0] + "/" |             burl = self.ar.url[:12] + self.ar.url[8:].split("/")[0] + "/" | ||||||
|             while True: |             while True: | ||||||
|                 print("  hs...") |                 print("  hs...") | ||||||
|                 hs, _ = handshake(req_ses, self.ar.url, file, self.ar.a, search) |                 hs, _ = handshake(self.ar.url, file, self.ar.a, search) | ||||||
|                 if search: |                 if search: | ||||||
|                     if hs: |                     if hs: | ||||||
|                         for hit in hs: |                         for hit in hs: | ||||||
| @ -666,9 +678,18 @@ class Ctl(object): | |||||||
|                 ncs = len(hs) |                 ncs = len(hs) | ||||||
|                 for nc, cid in enumerate(hs): |                 for nc, cid in enumerate(hs): | ||||||
|                     print("  {0} up {1}".format(ncs - nc, cid)) |                     print("  {0} up {1}".format(ncs - nc, cid)) | ||||||
|                     upload(req_ses, file, cid, self.ar.a) |                     upload(file, cid, self.ar.a) | ||||||
| 
 | 
 | ||||||
|             print("  ok!") |             print("  ok!") | ||||||
|  |             if file.recheck: | ||||||
|  |                 self.recheck.append(file) | ||||||
|  | 
 | ||||||
|  |         if not self.recheck: | ||||||
|  |             return | ||||||
|  | 
 | ||||||
|  |         eprint("finalizing {0} duplicate files".format(len(self.recheck))) | ||||||
|  |         for file in self.recheck: | ||||||
|  |             handshake(self.ar.url, file, self.ar.a, search) | ||||||
| 
 | 
 | ||||||
|     def _fancy(self): |     def _fancy(self): | ||||||
|         if VT100: |         if VT100: | ||||||
| @ -740,6 +761,13 @@ class Ctl(object): | |||||||
|             t = "{0} eta @ {1}/s, {2}, {3}# left".format(eta, spd, sleft, nleft) |             t = "{0} eta @ {1}/s, {2}, {3}# left".format(eta, spd, sleft, nleft) | ||||||
|             eprint(txt + "\033]0;{0}\033\\\r{0}{1}".format(t, tail)) |             eprint(txt + "\033]0;{0}\033\\\r{0}{1}".format(t, tail)) | ||||||
| 
 | 
 | ||||||
|  |         if not self.recheck: | ||||||
|  |             return | ||||||
|  | 
 | ||||||
|  |         eprint("finalizing {0} duplicate files".format(len(self.recheck))) | ||||||
|  |         for file in self.recheck: | ||||||
|  |             handshake(self.ar.url, file, self.ar.a, False) | ||||||
|  | 
 | ||||||
|     def cleanup_vt100(self): |     def cleanup_vt100(self): | ||||||
|         ss.scroll_region(None) |         ss.scroll_region(None) | ||||||
|         eprint("\033[J\033]0;\033\\") |         eprint("\033[J\033]0;\033\\") | ||||||
| @ -812,16 +840,10 @@ class Ctl(object): | |||||||
| 
 | 
 | ||||||
|     def handshaker(self): |     def handshaker(self): | ||||||
|         search = self.ar.s |         search = self.ar.s | ||||||
|         q = self.q_handshake |  | ||||||
|         burl = self.ar.url[:8] + self.ar.url[8:].split("/")[0] + "/" |         burl = self.ar.url[:8] + self.ar.url[8:].split("/")[0] + "/" | ||||||
|         while True: |         while True: | ||||||
|             file = q.get() |             file = self.q_handshake.get() | ||||||
|             if not file: |             if not file: | ||||||
|                 if q == self.q_handshake: |  | ||||||
|                     q = self.q_recheck |  | ||||||
|                     q.put(None) |  | ||||||
|                     continue |  | ||||||
| 
 |  | ||||||
|                 self.q_upload.put(None) |                 self.q_upload.put(None) | ||||||
|                 break |                 break | ||||||
| 
 | 
 | ||||||
| @ -829,16 +851,7 @@ class Ctl(object): | |||||||
|                 self.handshaker_busy += 1 |                 self.handshaker_busy += 1 | ||||||
| 
 | 
 | ||||||
|             upath = file.abs.decode("utf-8", "replace") |             upath = file.abs.decode("utf-8", "replace") | ||||||
| 
 |             hs, sprs = handshake(self.ar.url, file, self.ar.a, search) | ||||||
|             try: |  | ||||||
|                 hs, sprs = handshake(req_ses, self.ar.url, file, self.ar.a, search) |  | ||||||
|             except Exception as ex: |  | ||||||
|                 if q == self.q_handshake and "<pre>partial upload exists" in str(ex): |  | ||||||
|                     self.q_recheck.put(file) |  | ||||||
|                     hs = [] |  | ||||||
|                 else: |  | ||||||
|                     raise |  | ||||||
| 
 |  | ||||||
|             if search: |             if search: | ||||||
|                 if hs: |                 if hs: | ||||||
|                     for hit in hs: |                     for hit in hs: | ||||||
| @ -855,8 +868,11 @@ class Ctl(object): | |||||||
| 
 | 
 | ||||||
|                 continue |                 continue | ||||||
| 
 | 
 | ||||||
|  |             if file.recheck: | ||||||
|  |                 self.recheck.append(file) | ||||||
|  | 
 | ||||||
|             with self.mutex: |             with self.mutex: | ||||||
|                 if not sprs and not self.serialized: |                 if hs and not sprs and not self.serialized: | ||||||
|                     t = "server filesystem does not support sparse files; serializing uploads\n" |                     t = "server filesystem does not support sparse files; serializing uploads\n" | ||||||
|                     eprint(t) |                     eprint(t) | ||||||
|                     self.serialized = True |                     self.serialized = True | ||||||
| @ -899,7 +915,7 @@ class Ctl(object): | |||||||
| 
 | 
 | ||||||
|             file, cid = task |             file, cid = task | ||||||
|             try: |             try: | ||||||
|                 upload(req_ses, file, cid, self.ar.a) |                 upload(file, cid, self.ar.a) | ||||||
|             except: |             except: | ||||||
|                 eprint("upload failed, retrying: {0} #{1}\n".format(file.name, cid[:8])) |                 eprint("upload failed, retrying: {0} #{1}\n".format(file.name, cid[:8])) | ||||||
|                 pass  # handshake will fix it |                 pass  # handshake will fix it | ||||||
|  | |||||||
| @ -2005,13 +2005,13 @@ class Up2k(object): | |||||||
|                             except: |                             except: | ||||||
|                                 self.dupesched[src] = [dupe] |                                 self.dupesched[src] = [dupe] | ||||||
| 
 | 
 | ||||||
|                         raise Pebkac(400, err) |                         raise Pebkac(422, err) | ||||||
| 
 | 
 | ||||||
|                     elif "nodupe" in self.flags[job["ptop"]]: |                     elif "nodupe" in self.flags[job["ptop"]]: | ||||||
|                         self.log("dupe-reject:\n  {0}\n  {1}".format(src, dst)) |                         self.log("dupe-reject:\n  {0}\n  {1}".format(src, dst)) | ||||||
|                         err = "upload rejected, file already exists:\n" |                         err = "upload rejected, file already exists:\n" | ||||||
|                         err += "/" + quotep(vsrc) + " " |                         err += "/" + quotep(vsrc) + " " | ||||||
|                         raise Pebkac(400, err) |                         raise Pebkac(409, err) | ||||||
|                     else: |                     else: | ||||||
|                         # symlink to the client-provided name, |                         # symlink to the client-provided name, | ||||||
|                         # returning the previous upload info |                         # returning the previous upload info | ||||||
|  | |||||||
| @ -2297,8 +2297,8 @@ function up2k_init(subtle) { | |||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 var err_pend = rsp.indexOf('partial upload exists') + 1, |                 var err_pend = rsp.indexOf('partial upload exists at a different') + 1, | ||||||
|                     err_dupe = rsp.indexOf('file already exists') + 1; |                     err_dupe = rsp.indexOf('upload rejected, file already exists') + 1; | ||||||
| 
 | 
 | ||||||
|                 if (err_pend || err_dupe) { |                 if (err_pend || err_dupe) { | ||||||
|                     err = rsp; |                     err = rsp; | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 ed
						ed