mail: robustes Decoding gegen unbekannte Charsets (x-unknown); OAuth-Token 30 Tage
- _safe_decode() faengt LookupError bei unbekannten/kaputten Mail-Charsets (z.B. 'x-unknown') ab, Fallback utf-8 -> latin-1. Verhindert Crash der Mail-Suche/Read bei einzelnen kaputt-kodierten Mails. - common.py: OAuth access_token Lifetime 24h -> 30 Tage (weniger Re-Auth in claude.ai) - .gitignore: *.before-* Backups ausschliessen Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,3 +4,4 @@ __pycache__/
|
|||||||
tokens.json
|
tokens.json
|
||||||
config.json
|
config.json
|
||||||
.active_tokens.json
|
.active_tokens.json
|
||||||
|
*.before-*
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ async def oauth_token(request: Request):
|
|||||||
return JSONResponse({"error": "unsupported_grant_type"}, status_code=400)
|
return JSONResponse({"error": "unsupported_grant_type"}, status_code=400)
|
||||||
|
|
||||||
access_token = secrets.token_urlsafe(48)
|
access_token = secrets.token_urlsafe(48)
|
||||||
expires_in = 86400
|
expires_in = 2592000 # 30 days
|
||||||
tokens = _load_access_tokens()
|
tokens = _load_access_tokens()
|
||||||
# Cleanup expired
|
# Cleanup expired
|
||||||
now = time.time()
|
now = time.time()
|
||||||
|
|||||||
+19
-4
@@ -46,6 +46,21 @@ mcp = FastMCP("Mail", stateless_http=True,
|
|||||||
transport_security={"enable_dns_rebinding_protection": False})
|
transport_security={"enable_dns_rebinding_protection": False})
|
||||||
|
|
||||||
|
|
||||||
|
def _safe_decode(payload, charset):
|
||||||
|
"""Decode bytes robust gegen unbekannte/kaputte Charsets (z.B. 'x-unknown')."""
|
||||||
|
if not isinstance(payload, bytes):
|
||||||
|
return str(payload)
|
||||||
|
for cs in (charset, "utf-8", "latin-1"):
|
||||||
|
if not cs:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
return payload.decode(cs, errors="replace")
|
||||||
|
except (LookupError, TypeError):
|
||||||
|
continue
|
||||||
|
# Letzter Fallback: latin-1 akzeptiert jedes Byte
|
||||||
|
return payload.decode("latin-1", errors="replace")
|
||||||
|
|
||||||
|
|
||||||
def _decode_hdr(raw):
|
def _decode_hdr(raw):
|
||||||
if not raw:
|
if not raw:
|
||||||
return ""
|
return ""
|
||||||
@@ -53,7 +68,7 @@ def _decode_hdr(raw):
|
|||||||
decoded = []
|
decoded = []
|
||||||
for data, charset in parts:
|
for data, charset in parts:
|
||||||
if isinstance(data, bytes):
|
if isinstance(data, bytes):
|
||||||
decoded.append(data.decode(charset or "utf-8", errors="replace"))
|
decoded.append(_safe_decode(data, charset))
|
||||||
else:
|
else:
|
||||||
decoded.append(str(data))
|
decoded.append(str(data))
|
||||||
return " ".join(decoded)
|
return " ".join(decoded)
|
||||||
@@ -65,16 +80,16 @@ def _get_body(msg):
|
|||||||
if part.get_content_type() == "text/plain":
|
if part.get_content_type() == "text/plain":
|
||||||
payload = part.get_payload(decode=True)
|
payload = part.get_payload(decode=True)
|
||||||
if payload:
|
if payload:
|
||||||
return payload.decode(part.get_content_charset() or "utf-8", errors="replace")
|
return _safe_decode(payload, part.get_content_charset())
|
||||||
for part in msg.walk():
|
for part in msg.walk():
|
||||||
if part.get_content_type() == "text/html":
|
if part.get_content_type() == "text/html":
|
||||||
payload = part.get_payload(decode=True)
|
payload = part.get_payload(decode=True)
|
||||||
if payload:
|
if payload:
|
||||||
return "[HTML] " + payload.decode(part.get_content_charset() or "utf-8", errors="replace")
|
return "[HTML] " + _safe_decode(payload, part.get_content_charset())
|
||||||
else:
|
else:
|
||||||
payload = msg.get_payload(decode=True)
|
payload = msg.get_payload(decode=True)
|
||||||
if payload:
|
if payload:
|
||||||
return payload.decode(msg.get_content_charset() or "utf-8", errors="replace")
|
return _safe_decode(payload, msg.get_content_charset())
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user