// Copyright (c) 2020 Proton Technologies AG // // This file is part of ProtonMail Bridge. // // ProtonMail Bridge is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // ProtonMail Bridge is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with ProtonMail Bridge. If not, see . package cliie import ( "fmt" "os" "strings" "time" "github.com/ProtonMail/proton-bridge/internal/frontend/types" "github.com/ProtonMail/proton-bridge/internal/transfer" "github.com/ProtonMail/proton-bridge/pkg/pmapi" "github.com/abiosoft/ishell" ) func (f *frontendCLI) importLocalMessages(c *ishell.Context) { f.ShowPrompt(false) defer f.ShowPrompt(true) user, path := f.getUserAndPath(c, false) if user == nil || path == "" { return } t, err := f.ie.GetLocalImporter(user.GetPrimaryAddress(), path) f.transfer(t, err, false, true) } func (f *frontendCLI) importRemoteMessages(c *ishell.Context) { f.ShowPrompt(false) defer f.ShowPrompt(true) user := f.askUserByIndexOrName(c) if user == nil { return } username := f.readStringInAttempts("IMAP username", c.ReadLine, isNotEmpty) if username == "" { return } password := f.readStringInAttempts("IMAP password", c.ReadPassword, isNotEmpty) if password == "" { return } host := f.readStringInAttempts("IMAP host", c.ReadLine, isNotEmpty) if host == "" { return } port := f.readStringInAttempts("IMAP port", c.ReadLine, isNotEmpty) if port == "" { return } t, err := f.ie.GetRemoteImporter(user.GetPrimaryAddress(), username, password, host, port) f.transfer(t, err, false, true) } func (f *frontendCLI) exportMessagesToEML(c *ishell.Context) { f.ShowPrompt(false) defer f.ShowPrompt(true) user, path := f.getUserAndPath(c, true) if user == nil || path == "" { return } t, err := f.ie.GetEMLExporter(user.GetPrimaryAddress(), path) f.transfer(t, err, true, false) } func (f *frontendCLI) exportMessagesToMBOX(c *ishell.Context) { f.ShowPrompt(false) defer f.ShowPrompt(true) user, path := f.getUserAndPath(c, true) if user == nil || path == "" { return } t, err := f.ie.GetMBOXExporter(user.GetPrimaryAddress(), path) f.transfer(t, err, true, false) } func (f *frontendCLI) getUserAndPath(c *ishell.Context, createPath bool) (types.User, string) { user := f.askUserByIndexOrName(c) if user == nil { return nil, "" } path := f.readStringInAttempts("Path of EML and MBOX files", c.ReadLine, isNotEmpty) if path == "" { return nil, "" } if createPath { _ = os.Mkdir(path, os.ModePerm) } return user, path } func (f *frontendCLI) transfer(t *transfer.Transfer, err error, askSkipEncrypted bool, askGlobalMailbox bool) { if err != nil { f.printAndLogError("Failed to init transferrer: ", err) return } if askSkipEncrypted { skipEncryptedMessages := f.yesNoQuestion("Skip encrypted messages") t.SetSkipEncryptedMessages(skipEncryptedMessages) } if !f.setTransferRules(t) { return } if askGlobalMailbox { if err := f.setTransferGlobalMailbox(t); err != nil { f.printAndLogError("Failed to create global mailbox: ", err) return } } progress := t.Start() for range progress.GetUpdateChannel() { f.printTransferProgress(progress) } f.printTransferResult(progress) } func (f *frontendCLI) setTransferGlobalMailbox(t *transfer.Transfer) error { labelName := fmt.Sprintf("Imported %s", time.Now().Format("Jan-02-2006 15:04")) useGlobalLabel := f.yesNoQuestion("Use global label " + labelName) if !useGlobalLabel { return nil } globalMailbox, err := t.CreateTargetMailbox(transfer.Mailbox{ Name: labelName, Color: pmapi.LabelColors[0], IsExclusive: false, }) if err != nil { return err } t.SetGlobalMailbox(&globalMailbox) return nil } func (f *frontendCLI) setTransferRules(t *transfer.Transfer) bool { f.Println("Rules:") for _, rule := range t.GetRules() { if !rule.Active { continue } targets := strings.Join(rule.TargetMailboxNames(), ", ") if rule.HasTimeLimit() { f.Printf(" %-30s → %s (%s - %s)\n", rule.SourceMailbox.Name, targets, rule.FromDate(), rule.ToDate()) } else { f.Printf(" %-30s → %s\n", rule.SourceMailbox.Name, targets) } } return f.yesNoQuestion("Proceed") } func (f *frontendCLI) printTransferProgress(progress *transfer.Progress) { failed, imported, exported, added, total := progress.GetCounts() f.Println(fmt.Sprintf("Progress update: %d (%d / %d) / %d, failed: %d", imported, exported, added, total, failed)) if progress.IsPaused() { f.Printf("Transfer is paused bacause %s", progress.PauseReason()) if !f.yesNoQuestion("Continue (y) or stop (n)") { progress.Stop() } } } func (f *frontendCLI) printTransferResult(progress *transfer.Progress) { err := progress.GetFatalError() if err != nil { f.Println("Transfer failed: " + err.Error()) return } statuses := progress.GetFailedMessages() if len(statuses) == 0 { f.Println("Transfer finished!") return } f.Println("Transfer finished with errors:") for _, messageStatus := range statuses { f.Printf( " %-17s | %-30s | %-30s\n %s: %s\n", messageStatus.Time.Format("Jan 02 2006 15:04"), messageStatus.From, messageStatus.Subject, messageStatus.SourceID, messageStatus.GetErrorMessage(), ) } }