2026-08-10 03:10:50 -04:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
# © 2026 TechnoLibre (http://www.technolibre.ca)
|
|
|
|
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
|
|
|
|
|
"""Décoder des octets dont le nom de charset vient du serveur, sans jamais lever.
|
|
|
|
|
|
|
|
|
|
Un charset annoncé par un en-tête (`Content-Type`, un mot encodé RFC 2047)
|
|
|
|
|
peut être n'importe quelle chaîne — y compris une étiquette que Python ne
|
|
|
|
|
reconnaît pas, comme `unknown-8bit`, que certains MTA posent sur un en-tête
|
|
|
|
|
8 bits mal formé. `bytes.decode(charset, errors="replace")` lève quand même
|
|
|
|
|
`LookupError` dans ce cas : la RECHERCHE du codec échoue AVANT que `errors`
|
|
|
|
|
ne soit consulté — `errors="replace"` ne protège donc de rien ici.
|
|
|
|
|
|
|
|
|
|
Cette fonction a été réinventée quatre fois dans ce paquet
|
|
|
|
|
(`imap_transport.decode_header_value`, `imap_sync.snippet_from_raw`,
|
|
|
|
|
`smtp_send._plain_text`, `tui_text._decode_part`) avant d'être extraite ici :
|
|
|
|
|
un charset non fiable ne doit jamais faire tomber l'affichage ou la
|
|
|
|
|
synchronisation d'un message entier.
|
|
|
|
|
"""
|
[REF] format : passer l'outillage et les tests sous ruff
Le formateur de ce dépôt est ruff depuis qu'il remplace black, qui ne connaît
aucune cible au-delà de py313 ; ce passage applique sa norme à l'arbre entier,
d'un coup, pour qu'aucun commit de fond n'ait à porter du style. L'écart tient
presque entièrement aux chaînes coupées à la main que ruff recolle quand elles
tiennent sur une ligne, et aux « with » multiples qu'il regroupe : aucune
valeur ne change, et les clés de traduction non plus.
Vérifié : la suite unitaire reste verte après le passage, et le contrôle de
syntaxe ne signale rien.
--- EN ---
This repository's formatter is ruff since it replaced black, which knows no
target beyond py313; this pass applies its standard to the whole tree at once,
so that no substantive commit has to carry style. The difference is almost
entirely the hand-split strings ruff joins back when they fit on one line, and
the multiple "with" it merges: no value changes, nor do the translation keys.
Checked: the unit suite stays green after the pass, and the syntax check
reports nothing.
Assisted-by: Claude Opus 5
2026-09-24 13:30:31 -04:00
|
|
|
|
2026-08-10 03:10:50 -04:00
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def decode_bytes(payload: bytes, charset: str | None) -> str:
|
|
|
|
|
"""`payload` décodé avec `charset`, replié sur UTF-8 si `charset` est
|
|
|
|
|
absent ou inconnu de Python."""
|
|
|
|
|
try:
|
|
|
|
|
return payload.decode(charset or "utf-8", "replace")
|
|
|
|
|
except LookupError:
|
|
|
|
|
return payload.decode("utf-8", "replace")
|