package config import ( "encoding/json" "flag" "fmt" "os" "path/filepath" ) type Config struct { CouchDb CouchDbConfig `json:"couchDb"` MailSources []MailSource `json:"mailSources"` } type CouchDbConfig struct { URL string `json:"url"` User string `json:"user"` Password string `json:"password"` Database string `json:"database"` } type MailSource struct { Name string `json:"name"` Enabled bool `json:"enabled"` Protocol string `json:"protocol"` Host string `json:"host"` Port int `json:"port"` User string `json:"user"` Password string `json:"password"` Mode string `json:"mode"` // "sync" or "archive" FolderFilter FolderFilter `json:"folderFilter"` MessageFilter MessageFilter `json:"messageFilter"` } type FolderFilter struct { Include []string `json:"include"` Exclude []string `json:"exclude"` } type MessageFilter struct { Since string `json:"since,omitempty"` SubjectKeywords []string `json:"subjectKeywords,omitempty"` // Filter by keywords in subject SenderKeywords []string `json:"senderKeywords,omitempty"` // Filter by keywords in sender addresses RecipientKeywords []string `json:"recipientKeywords,omitempty"` // Filter by keywords in recipient addresses } func LoadConfig(path string) (*Config, error) { configFile, err := os.Open(path) if err != nil { return nil, err } defer configFile.Close() var config Config jsonParser := json.NewDecoder(configFile) if err = jsonParser.Decode(&config); err != nil { return nil, err } // Validate and set defaults for mail sources for i := range config.MailSources { source := &config.MailSources[i] if source.Mode == "" { source.Mode = "archive" // Default to archive mode } if source.Mode != "sync" && source.Mode != "archive" { return nil, fmt.Errorf("invalid mode '%s' for mail source '%s': must be 'sync' or 'archive'", source.Mode, source.Name) } } return &config, nil } // IsSyncMode returns true if the mail source is in sync mode func (ms *MailSource) IsSyncMode() bool { return ms.Mode == "sync" } // IsArchiveMode returns true if the mail source is in archive mode func (ms *MailSource) IsArchiveMode() bool { return ms.Mode == "archive" || ms.Mode == "" // Default to archive } // CommandLineArgs holds parsed command line arguments type CommandLineArgs struct { ConfigPath string MaxMessages int } // ParseCommandLine parses command line arguments func ParseCommandLine() *CommandLineArgs { configFlag := flag.String("config", "", "Path to configuration file") maxMessagesFlag := flag.Int("max-messages", 0, "Maximum number of messages to process per mailbox per run (0 = no limit)") flag.Parse() return &CommandLineArgs{ ConfigPath: *configFlag, MaxMessages: *maxMessagesFlag, } } // FindConfigFile searches for config.json in the following order: // 1. Path specified by -config flag // 2. ./config.json (current directory) // 3. ~/.config/mail2couch/config.json (user config directory) // 4. ~/.mail2couch.json (user home directory) func FindConfigFile(args *CommandLineArgs) (string, error) { if args.ConfigPath != "" { if _, err := os.Stat(args.ConfigPath); err == nil { return args.ConfigPath, nil } return "", fmt.Errorf("specified config file not found: %s", args.ConfigPath) } // List of possible config file locations in order of preference candidates := []string{ "config.json", // Current directory "config/config.json", // Config subdirectory } // Add user directory paths if homeDir, err := os.UserHomeDir(); err == nil { candidates = append(candidates, filepath.Join(homeDir, ".config", "mail2couch", "config.json"), filepath.Join(homeDir, ".mail2couch.json"), ) } // Try each candidate location for _, candidate := range candidates { if _, err := os.Stat(candidate); err == nil { return candidate, nil } } return "", fmt.Errorf("no configuration file found. Searched locations: %v", candidates) } // LoadConfigWithDiscovery loads configuration using automatic file discovery func LoadConfigWithDiscovery(args *CommandLineArgs) (*Config, error) { configPath, err := FindConfigFile(args) if err != nil { return nil, err } fmt.Printf("Using configuration file: %s\n", configPath) if args.MaxMessages > 0 { fmt.Printf("Maximum messages per mailbox: %d\n", args.MaxMessages) } else { fmt.Printf("Maximum messages per mailbox: unlimited\n") } return LoadConfig(configPath) }