feat(mail): implement UTF-8 mojibake repair functionality
- Added repairUTF8Mojibake function to fix UTF-8 text misread as Latin-1, addressing common encoding issues in email bodies. - Enhanced RepairStoredBodies and RepairSnippetWithBodies functions to utilize the new mojibake repair logic. - Introduced unit tests for mojibake repair functionality to ensure accurate text restoration. - Updated charset handling in repairLegacyCharsetString to incorporate mojibake repair, improving overall text processing reliability.
This commit is contained in:
parent
125169edee
commit
e6a04fdd31
@ -44,6 +44,8 @@ DOMAIN={{PUBLIC_HOST}}
|
||||
# Origine publique dérivée (http:// ou https:// + hôte)
|
||||
SUITE_ORIGIN=http{{SECURE}}://{{PUBLIC_HOST}}
|
||||
CLOUDFLARE_TUNNEL_PUBLIC_URL={{SUITE_ORIGIN}}
|
||||
# X-Forwarded-Proto envoyé aux upstreams (nginx :80 + tunnel → https côté navigateur)
|
||||
FORWARDED_PROTO=http{{SECURE}}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# General
|
||||
|
||||
@ -10,6 +10,7 @@ services:
|
||||
- ./nginx/patches/QGuclOcQ.js:/etc/nginx/patches/QGuclOcQ.js:ro
|
||||
environment:
|
||||
DOMAIN: ${DOMAIN:-localhost}
|
||||
FORWARDED_PROTO: ${FORWARDED_PROTO:-http}
|
||||
MAIL_FRONTEND_UPSTREAM: ${MAIL_FRONTEND_UPSTREAM:-host.docker.internal:3004}
|
||||
env_file: ../.env.resolved
|
||||
extra_hosts:
|
||||
|
||||
@ -40,7 +40,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
}
|
||||
location ^~ /api/v1/migration/ {
|
||||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||||
@ -63,7 +63,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
}
|
||||
location ^~ /api/v1/admin/ {
|
||||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||||
@ -86,7 +86,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
}
|
||||
location ^~ /api/v1/drive/ {
|
||||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||||
@ -109,7 +109,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
}
|
||||
location ^~ /api/v1/office/ {
|
||||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||||
@ -132,7 +132,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
}
|
||||
location ^~ /api/v1/richtext/ {
|
||||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||||
@ -155,7 +155,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
}
|
||||
location ^~ /api/v1/ultidraw/ {
|
||||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||||
@ -178,7 +178,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
}
|
||||
location ^~ /api/v1/ai/mcp {
|
||||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||||
@ -208,7 +208,7 @@ server {
|
||||
proxy_set_header X-OpenWebUI-User-Role $http_x_openwebui_user_role;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
proxy_read_timeout 86400s;
|
||||
@ -242,7 +242,7 @@ server {
|
||||
proxy_set_header X-OpenWebUI-User-Role $http_x_openwebui_user_role;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
proxy_read_timeout 86400s;
|
||||
@ -269,7 +269,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
}
|
||||
location ^~ /api/v1/calendar/ {
|
||||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||||
@ -292,7 +292,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
}
|
||||
location ^~ /api/v1/contacts/ {
|
||||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||||
@ -315,7 +315,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
}
|
||||
location ^~ /api/v1/meet/ {
|
||||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||||
@ -338,7 +338,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
}
|
||||
location ^~ /api/v1/photos/ {
|
||||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||||
@ -361,7 +361,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
}
|
||||
location ^~ /api/v1/search {
|
||||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||||
@ -384,7 +384,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
}
|
||||
|
||||
# OpenWebUI trusted-header signin — ultid injects X-Ulti-User-* (auth_request + POST body deadlocks)
|
||||
@ -411,7 +411,7 @@ server {
|
||||
proxy_set_header Authorization $http_authorization;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
}
|
||||
|
||||
# --- OpenWebUI API at site root (prod SPA: WEBUI_BASE_URL="" → fetch("/api/…")) ---
|
||||
@ -425,7 +425,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
proxy_set_header Accept-Encoding "";
|
||||
@ -444,7 +444,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
proxy_read_timeout 86400s;
|
||||
@ -476,7 +476,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
}
|
||||
|
||||
# OpenWebUI socket.io (ultid uses /ws without /socket.io)
|
||||
@ -490,7 +490,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
proxy_read_timeout 86400s;
|
||||
@ -509,7 +509,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
proxy_read_timeout 86400s;
|
||||
@ -532,7 +532,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
}
|
||||
|
||||
# OpenWebUI Ollama/OpenAI proxy mounts (SPA uses /ollama, /openai at root)
|
||||
@ -544,7 +544,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
}
|
||||
location ^~ /openai/ {
|
||||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||||
@ -554,7 +554,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
}
|
||||
|
||||
# TipTap / Hocuspocus — proxy WS without redirect (301 breaks upgrade)
|
||||
@ -570,7 +570,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
proxy_read_timeout 86400s;
|
||||
proxy_send_timeout 86400s;
|
||||
}
|
||||
@ -584,7 +584,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
}
|
||||
@ -597,7 +597,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
proxy_read_timeout 86400;
|
||||
@ -616,7 +616,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
}
|
||||
|
||||
# Public Nextcloud share links → UltiDrive viewer
|
||||
@ -633,7 +633,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
}
|
||||
|
||||
location = /cloud {
|
||||
@ -649,7 +649,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
proxy_read_timeout 300s;
|
||||
}
|
||||
|
||||
@ -664,7 +664,7 @@ server {
|
||||
proxy_set_header Authorization $http_authorization;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
}
|
||||
|
||||
location /ai/ {
|
||||
@ -685,7 +685,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
proxy_set_header X-Forwarded-Prefix /ai;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
@ -775,7 +775,7 @@ server {
|
||||
proxy_set_header X-Ulti-User-Role $ulti_user_role;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
}
|
||||
|
||||
# OpenWebUI API (prefix /ai/api — évite collision avec ultid /api/v1/)
|
||||
@ -789,7 +789,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
proxy_buffering off;
|
||||
@ -825,7 +825,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
proxy_set_header X-Ulti-User-Email $ulti_user_email;
|
||||
proxy_set_header X-Ulti-User-Name $ulti_user_name;
|
||||
proxy_set_header X-Ulti-User-Role $ulti_user_role;
|
||||
@ -859,7 +859,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
proxy_set_header X-Ulti-User-Email $ulti_user_email;
|
||||
proxy_set_header X-Ulti-User-Name $ulti_user_name;
|
||||
proxy_set_header X-Ulti-User-Role $ulti_user_role;
|
||||
@ -893,7 +893,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
proxy_set_header X-Ulti-User-Email $ulti_user_email;
|
||||
proxy_set_header X-Ulti-User-Name $ulti_user_name;
|
||||
proxy_set_header X-Ulti-User-Role $ulti_user_role;
|
||||
@ -907,7 +907,7 @@ server {
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Host $host/office;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
@ -928,7 +928,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
}
|
||||
@ -941,7 +941,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
}
|
||||
@ -957,7 +957,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
}
|
||||
@ -971,7 +971,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
}
|
||||
|
||||
location ^~ /api/auth/ {
|
||||
@ -982,7 +982,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
}
|
||||
|
||||
location ^~ /mail/ {
|
||||
@ -993,7 +993,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
}
|
||||
@ -1007,7 +1007,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
}
|
||||
@ -1024,7 +1024,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
}
|
||||
@ -1037,7 +1037,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
}
|
||||
@ -1050,7 +1050,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
}
|
||||
@ -1063,7 +1063,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
}
|
||||
@ -1077,7 +1077,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
}
|
||||
@ -1091,7 +1091,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
}
|
||||
@ -1114,7 +1114,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
}
|
||||
@ -1127,7 +1127,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
}
|
||||
@ -1141,7 +1141,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
}
|
||||
|
||||
location ^~ /brand/ {
|
||||
@ -1150,7 +1150,7 @@ server {
|
||||
proxy_pass http://$mail_upstream;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
}
|
||||
|
||||
location ^~ /mail-backgrounds/ {
|
||||
@ -1159,7 +1159,7 @@ server {
|
||||
proxy_pass http://$mail_upstream;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
}
|
||||
|
||||
# Public assets at repo root (launcher icons, etc.)
|
||||
@ -1169,7 +1169,7 @@ server {
|
||||
proxy_pass http://$mail_upstream$request_uri;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
}
|
||||
|
||||
# Landing page de la suite (servie par le frontend Next).
|
||||
@ -1181,7 +1181,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
}
|
||||
|
||||
# OpenWebUI SPA routes without /ai prefix (client nav + hard reload in UltiAI iframe).
|
||||
@ -1212,7 +1212,7 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto ${FORWARDED_PROTO};
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
}
|
||||
|
||||
@ -21,6 +21,8 @@ func RepairStoredBodies(text, html string) (string, string) {
|
||||
text = decodeBareBase64IfNeeded(text)
|
||||
html = decodeBareBase64IfNeeded(html)
|
||||
text = stripPlainTextPreheaderPadding(text)
|
||||
text = repairUTF8Mojibake(text)
|
||||
html = repairUTF8Mojibake(html)
|
||||
return text, html
|
||||
}
|
||||
|
||||
@ -49,7 +51,7 @@ func RepairSnippet(snippet string) string {
|
||||
|
||||
// RepairSnippetWithBodies decodes a stored snippet and optionally rebuilds from bodies.
|
||||
func RepairSnippetWithBodies(snippet, bodyText, bodyHTML string) string {
|
||||
snippet = stripSnippetMarkup(snippet)
|
||||
snippet = repairUTF8Mojibake(stripSnippetMarkup(snippet))
|
||||
if decoded := decodeBareQuotedPrintableIfNeeded(snippet); decoded != snippet {
|
||||
snippet = stripSnippetMarkup(decoded)
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ package imap
|
||||
|
||||
import (
|
||||
"mime"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
@ -11,6 +12,9 @@ import (
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
// UTF-8 misread as Latin-1: "réactivité" (U+00C3 U+00A9 …).
|
||||
var utf8MojibakeRE = regexp.MustCompile(`[\xC2\xC3][\x80-\xBF]`)
|
||||
|
||||
func charsetFromContentType(contentType string) string {
|
||||
if contentType == "" {
|
||||
return ""
|
||||
@ -77,10 +81,79 @@ func repairRawBytesToUTF8(data []byte) string {
|
||||
return strings.ToValidUTF8(string(data), "")
|
||||
}
|
||||
|
||||
// repairLegacyCharsetString fixes text already loaded as a Go string with invalid UTF-8 bytes.
|
||||
func repairLegacyCharsetString(s string) string {
|
||||
if s == "" || utf8.ValidString(s) {
|
||||
func looksLikeUTF8Mojibake(s string) bool {
|
||||
return utf8MojibakeRE.MatchString(s)
|
||||
}
|
||||
|
||||
// repairUTF8Mojibake fixes UTF-8 text misread as Latin-1 (e.g. "réactivité" → "réactivité").
|
||||
// Repairs pair-by-pair so mixed/corrupted sequences (e.g. NBSP → space in "Déjà") still partially fix.
|
||||
func repairUTF8Mojibake(s string) string {
|
||||
if s == "" || !looksLikeUTF8Mojibake(s) {
|
||||
return s
|
||||
}
|
||||
return repairRawBytesToUTF8([]byte(s))
|
||||
runes := []rune(s)
|
||||
var b strings.Builder
|
||||
b.Grow(len(s))
|
||||
for i := 0; i < len(runes); i++ {
|
||||
r := runes[i]
|
||||
if (r == 0xC2 || r == 0xC3) && i+1 < len(runes) {
|
||||
next := runes[i+1]
|
||||
if next >= 0x80 && next <= 0xBF {
|
||||
seq := []byte{byte(r), byte(next)}
|
||||
if utf8.Valid(seq) {
|
||||
decoded, _ := utf8.DecodeRune(seq)
|
||||
b.WriteRune(decoded)
|
||||
i++
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
b.WriteRune(r)
|
||||
}
|
||||
out := b.String()
|
||||
if out == s {
|
||||
return s
|
||||
}
|
||||
return repairLoneMojibakeLeaders(out)
|
||||
}
|
||||
|
||||
func repairLoneMojibakeLeaders(s string) string {
|
||||
runes := []rune(s)
|
||||
var b strings.Builder
|
||||
b.Grow(len(s))
|
||||
for i := 0; i < len(runes); i++ {
|
||||
r := runes[i]
|
||||
if r == 0xC3 || r == 0xC2 {
|
||||
if i+1 < len(runes) && isLoneMojibakeLeaderBoundary(runes[i+1]) {
|
||||
if r == 0xC3 {
|
||||
b.WriteRune('à')
|
||||
} else {
|
||||
b.WriteRune('Â')
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
b.WriteRune(r)
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func isLoneMojibakeLeaderBoundary(r rune) bool {
|
||||
switch r {
|
||||
case ' ', '\t', ',', '.', ';', ':', '!', '?':
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// repairLegacyCharsetString fixes text already loaded as a Go string with invalid UTF-8 bytes.
|
||||
func repairLegacyCharsetString(s string) string {
|
||||
if s == "" {
|
||||
return s
|
||||
}
|
||||
if !utf8.ValidString(s) {
|
||||
s = repairRawBytesToUTF8([]byte(s))
|
||||
}
|
||||
return repairUTF8Mojibake(s)
|
||||
}
|
||||
|
||||
@ -28,6 +28,44 @@ func TestParseBody_iso88591Charset(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepairUTF8Mojibake_doubleEncodedFrench(t *testing.T) {
|
||||
raw := "Si elle bouge, tu perds en réactivité. La NEXOR a été pensée pour ça."
|
||||
repaired := repairUTF8Mojibake(raw)
|
||||
want := "Si elle bouge, tu perds en réactivité. La NEXOR a été pensée pour ça."
|
||||
if repaired != want {
|
||||
t.Fatalf("repaired = %q, want %q", repaired, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepairUTF8Mojibake_longMarketingBody(t *testing.T) {
|
||||
raw := "Si elle bouge, tu perds en réactivité. La NEXOR a été pensée pour ça. Nylon ultra résistant, boucle rapide, compatible 100% F1 et TSI. Sobre, solide, zéro fioritures. Détache la sangle, passe-la dans les passants, c'est prêt. Déjà adoptée par +8 000 pompiers en Europe. Elle était en rupture. Elle est de retour."
|
||||
repaired := repairUTF8Mojibake(raw)
|
||||
if strings.Contains(repaired, "Ã") {
|
||||
t.Fatalf("repaired still has mojibake: %q", repaired)
|
||||
}
|
||||
if !strings.Contains(repaired, "réactivité") || !strings.Contains(repaired, "Déjà") {
|
||||
t.Fatalf("repaired = %q", repaired)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepairUTF8Mojibake_leavesValidUTF8Untouched(t *testing.T) {
|
||||
raw := "Si elle bouge, tu perds en réactivité."
|
||||
if got := repairUTF8Mojibake(raw); got != raw {
|
||||
t.Fatalf("repaired = %q, want unchanged", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepairSnippetWithBodies_mojibakePreview(t *testing.T) {
|
||||
stored := "Nylon ultra résistant, boucle rapide, zéro fioritures."
|
||||
got := RepairSnippetWithBodies(stored, "", "")
|
||||
if strings.Contains(got, "Ã") {
|
||||
t.Fatalf("snippet = %q, want accents repaired", got)
|
||||
}
|
||||
if !strings.Contains(got, "résistant") || !strings.Contains(got, "zéro") {
|
||||
t.Fatalf("snippet = %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepairLegacyCharsetString_latin1BytesInString(t *testing.T) {
|
||||
// Simulates DB row stored before charset decode (raw Latin-1 bytes in text column).
|
||||
raw := string([]byte{0x56, 0x6f, 0x75, 0x73, 0x20, 0x72, 0xe9, 0x75, 0x6e, 0x69, 0x6f, 0x6e})
|
||||
|
||||
Loading…
Reference in New Issue
Block a user