Add optional zstd mbox archives

This commit is contained in:
DonVoo 2026-07-14 20:20:30 +02:00
parent 21844688ac
commit e612896e06
14 changed files with 620 additions and 39 deletions

View file

@ -14,6 +14,8 @@ import (
"path/filepath"
"strings"
"time"
"github.com/klauspost/compress/zstd"
)
type ArchiveStats struct {
@ -136,6 +138,9 @@ func archiveDownloadHandler(w http.ResponseWriter, r *http.Request) {
return err
}
header.Name = rel
if strings.HasSuffix(strings.ToLower(rel), ".mbox.zst") {
header.Name = strings.TrimSuffix(rel, ".zst")
}
header.Method = zip.Deflate
dst, err := zw.CreateHeader(header)
if err != nil {
@ -146,7 +151,11 @@ func archiveDownloadHandler(w http.ResponseWriter, r *http.Request) {
return err
}
defer src.Close()
_, err = io.Copy(dst, src)
if strings.HasSuffix(strings.ToLower(path), ".mbox.zst") {
err = exportPlainMbox(src, dst)
} else {
_, err = io.Copy(dst, src)
}
return err
})
}
@ -221,7 +230,7 @@ func renderArchiveUploadForm(w http.ResponseWriter, archives []string) {
<label class="label">Archiv-Mailbox`)
fmt.Fprint(w, archiveUploadSelect(archives))
fmt.Fprint(w, `</label>
<label class="label">Datei<input class="input" type="file" name="mailfile" accept=".mbox,.pst" required></label>
<label class="label">Datei<input class="input" type="file" name="mailfile" accept=".mbox,.pst,.zst" required></label>
<button class="btn" type="submit">Upload</button>
</form>
<div class="archive-help">
@ -278,7 +287,7 @@ func ArchiveStatsFor(name string) (ArchiveStats, error) {
}
stat.Files++
stat.Bytes += info.Size()
if strings.EqualFold(filepath.Ext(path), ".mbox") {
if isMboxArchivePath(path) {
msgs, err := readMboxMessages(path)
if err != nil {
return nil
@ -414,17 +423,37 @@ func safeArchiveFileName(value string) (string, error) {
if name == "" || name == "." || name == ".." {
return "", fmt.Errorf("Dateiname fehlt.")
}
lower := strings.ToLower(name)
ext := strings.ToLower(filepath.Ext(name))
if ext != ".mbox" && ext != ".pst" {
return "", fmt.Errorf("Nur mbox- und PST-Dateien koennen importiert werden.")
if ext != ".mbox" && ext != ".pst" && !strings.HasSuffix(lower, ".mbox.zst") {
return "", fmt.Errorf("Nur mbox-, mbox.zst- und PST-Dateien koennen importiert werden.")
}
clean := strings.NewReplacer("\\", "_", "/", "_", ":", "_").Replace(name)
if strings.TrimSuffix(clean, ext) == "" {
if strings.HasSuffix(strings.ToLower(clean), ".mbox.zst") {
if strings.TrimSuffix(strings.TrimSuffix(clean, ".zst"), ".mbox") == "" {
clean = "import.mbox.zst"
}
} else if strings.TrimSuffix(clean, ext) == "" {
clean = "import" + ext
}
return clean, nil
}
func isMboxArchivePath(path string) bool {
lower := strings.ToLower(path)
return strings.HasSuffix(lower, ".mbox") || strings.HasSuffix(lower, ".mbox.zst")
}
func exportPlainMbox(src io.Reader, dst io.Writer) error {
dec, err := zstd.NewReader(src)
if err != nil {
return err
}
defer dec.Close()
_, err = io.Copy(dst, dec)
return err
}
func formatBytes(n int64) string {
const unit = 1024
if n < unit {