Deploy msgboard
Browse files- src/msgboard/transfer.py +25 -5
src/msgboard/transfer.py
CHANGED
|
@@ -30,6 +30,14 @@ MAX_ATTACHMENTS = 1_000
|
|
| 30 |
HASH = re.compile(r"^[0-9a-f]{64}$")
|
| 31 |
ATT_LINK = re.compile(r"(?:/v1/attachments/|#file/)(att_[A-Za-z0-9_-]+)")
|
| 32 |
STORAGE_LINK = re.compile(r"(?:https?://[^\s<>\"'\[\]]+)?/v1/storage/[^/\s<>\"'()\[\]]+/[^\s<>\"'\[\]]+")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
|
| 34 |
|
| 35 |
def storage_reference(text, source_url):
|
|
@@ -37,8 +45,15 @@ def storage_reference(text, source_url):
|
|
| 37 |
suffix = ""
|
| 38 |
# Keep balanced filename parentheses, but leave Markdown's closing delimiter
|
| 39 |
# outside the URL when a rewritten link is put back into the body.
|
| 40 |
-
|
| 41 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
url, source = urlsplit(text), urlsplit(source_url)
|
| 43 |
prefix = "/v1/storage/"
|
| 44 |
path = url.path
|
|
@@ -108,7 +123,7 @@ def export_manifest(conn, p, channel_id: int, board_url: str):
|
|
| 108 |
attachments.append(dict(row))
|
| 109 |
storage_files = {}
|
| 110 |
for body in [m["body"] for m in messages] + ([doc["body"]] if doc else []):
|
| 111 |
-
for match in
|
| 112 |
ref = storage_reference(match.group(), board_url)
|
| 113 |
if ref is None:
|
| 114 |
continue
|
|
@@ -310,7 +325,7 @@ def validate_manifest(m):
|
|
| 310 |
_text(doc.get("body"), "channel doc", store.MAX_DOC_BYTES)
|
| 311 |
_time(doc.get("created_at"), "doc timestamp")
|
| 312 |
for body in [msg["body"] for msg in m["messages"]] + ([doc["body"]] if doc else []):
|
| 313 |
-
for match in
|
| 314 |
ref = storage_reference(match.group(), source["board_url"])
|
| 315 |
if ref is not None and (ref[0], ref[1]) not in aliases:
|
| 316 |
raise bad_request("bundle omits a linked same-board storage file")
|
|
@@ -448,7 +463,12 @@ def _rewrite_storage(body, source_url, files, handles):
|
|
| 448 |
if not url.netloc and target == handle:
|
| 449 |
return match.group()
|
| 450 |
return urlunsplit(("", "", "/v1/storage/" + quote(target, safe="") + "/" + quote(path, safe="/"), url.query, url.fragment)) + suffix
|
| 451 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 452 |
|
| 453 |
|
| 454 |
def _rewrite_attachments(body, source_url, ids):
|
|
|
|
| 30 |
HASH = re.compile(r"^[0-9a-f]{64}$")
|
| 31 |
ATT_LINK = re.compile(r"(?:/v1/attachments/|#file/)(att_[A-Za-z0-9_-]+)")
|
| 32 |
STORAGE_LINK = re.compile(r"(?:https?://[^\s<>\"'\[\]]+)?/v1/storage/[^/\s<>\"'()\[\]]+/[^\s<>\"'\[\]]+")
|
| 33 |
+
CODE_SPAN = re.compile(r"(`+)([\s\S]*?)\1|(?m:^ {0,3}(~{3,})[^\n]*\n[\s\S]*?^ {0,3}\3[ \t]*$)")
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def storage_links(body):
|
| 37 |
+
# Preserve offsets so the same matches can drive capture and rewriting.
|
| 38 |
+
# Paths shown as code are examples, not rendered file links.
|
| 39 |
+
masked = CODE_SPAN.sub(lambda match: " " * len(match.group()), body)
|
| 40 |
+
return STORAGE_LINK.finditer(masked)
|
| 41 |
|
| 42 |
|
| 43 |
def storage_reference(text, source_url):
|
|
|
|
| 45 |
suffix = ""
|
| 46 |
# Keep balanced filename parentheses, but leave Markdown's closing delimiter
|
| 47 |
# outside the URL when a rewritten link is put back into the body.
|
| 48 |
+
depth = 0
|
| 49 |
+
for i, char in enumerate(text):
|
| 50 |
+
if char == "(":
|
| 51 |
+
depth += 1
|
| 52 |
+
elif char == ")":
|
| 53 |
+
if not depth:
|
| 54 |
+
text, suffix = text[:i], text[i:]
|
| 55 |
+
break
|
| 56 |
+
depth -= 1
|
| 57 |
url, source = urlsplit(text), urlsplit(source_url)
|
| 58 |
prefix = "/v1/storage/"
|
| 59 |
path = url.path
|
|
|
|
| 123 |
attachments.append(dict(row))
|
| 124 |
storage_files = {}
|
| 125 |
for body in [m["body"] for m in messages] + ([doc["body"]] if doc else []):
|
| 126 |
+
for match in storage_links(body):
|
| 127 |
ref = storage_reference(match.group(), board_url)
|
| 128 |
if ref is None:
|
| 129 |
continue
|
|
|
|
| 325 |
_text(doc.get("body"), "channel doc", store.MAX_DOC_BYTES)
|
| 326 |
_time(doc.get("created_at"), "doc timestamp")
|
| 327 |
for body in [msg["body"] for msg in m["messages"]] + ([doc["body"]] if doc else []):
|
| 328 |
+
for match in storage_links(body):
|
| 329 |
ref = storage_reference(match.group(), source["board_url"])
|
| 330 |
if ref is not None and (ref[0], ref[1]) not in aliases:
|
| 331 |
raise bad_request("bundle omits a linked same-board storage file")
|
|
|
|
| 463 |
if not url.netloc and target == handle:
|
| 464 |
return match.group()
|
| 465 |
return urlunsplit(("", "", "/v1/storage/" + quote(target, safe="") + "/" + quote(path, safe="/"), url.query, url.fragment)) + suffix
|
| 466 |
+
parts, start = [], 0
|
| 467 |
+
for match in storage_links(body):
|
| 468 |
+
parts.extend((body[start:match.start()], replace(match)))
|
| 469 |
+
start = match.end()
|
| 470 |
+
parts.append(body[start:])
|
| 471 |
+
return "".join(parts)
|
| 472 |
|
| 473 |
|
| 474 |
def _rewrite_attachments(body, source_url, ids):
|