package main import ( "flag" "fmt" "log/slog" "os" "path/filepath" "github.com/archivmail/config" "github.com/archivmail/internal/index" "github.com/archivmail/internal/storage" ) func main() { configPath := flag.String("config", "/etc/archivmail/config.yml", "path to config file") format := flag.String("format", "eml", "export format: eml or pdf") outDir := flag.String("out", "./export", "output directory") flag.Parse() logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo})) if *format == "pdf" { fmt.Fprintln(os.Stdout, "PDF export not yet implemented") os.Exit(0) } if *format != "eml" { fmt.Fprintf(os.Stderr, "unknown format: %s (supported: eml, pdf)\n", *format) os.Exit(1) } cfg, err := config.Load(*configPath) if err != nil { logger.Error("failed to load config", "err", err) os.Exit(1) } mailStore, err := storage.New(cfg.Storage.StorePath) if err != nil { logger.Error("storage init failed", "err", err) os.Exit(1) } indexBackend := cfg.Index.Backend if indexBackend == "" { indexBackend = "xapian" } batchSize := cfg.Index.BatchSize if batchSize <= 0 { batchSize = 100 } idx, err := index.New(cfg.Index.Path, batchSize, indexBackend) if err != nil { logger.Error("index init failed", "err", err) os.Exit(1) } defer idx.Close() if err := os.MkdirAll(*outDir, 0o755); err != nil { logger.Error("cannot create output directory", "dir", *outDir, "err", err) os.Exit(1) } // Fetch all indexed mails using pagination page := 0 pageSize := 500 exported := 0 errors := 0 for { result, err := idx.Search(index.SearchRequest{ PageSize: pageSize, Page: page, }) if err != nil { logger.Error("search failed", "err", err) os.Exit(1) } if len(result.Hits) == 0 { break } for _, hit := range result.Hits { raw, err := mailStore.Load(hit.ID) if err != nil { logger.Error("load failed", "id", hit.ID, "err", err) errors++ continue } outPath := filepath.Join(*outDir, hit.ID+".eml") if err := os.WriteFile(outPath, raw, 0o644); err != nil { logger.Error("write failed", "path", outPath, "err", err) errors++ continue } exported++ } logger.Info("export progress", "page", page, "exported", exported, "errors", errors) if exported+errors >= result.Total { break } page++ } logger.Info("export complete", "format", *format, "out", *outDir, "exported", exported, "errors", errors, ) }