/* * Copyright (c) 2026 Kaiy Ragur * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package shell import ( "encoding/hex" "encoding/json" "flag" "fmt" "log" "os" "github.com/neilalexander/yggmail/internal/account" "github.com/neilalexander/yggmail/internal/config" "github.com/neilalexander/yggmail/internal/storage" "github.com/neilalexander/yggmail/internal/storage/types" "github.com/neilalexander/yggmail/internal/utils" ) type Shell struct { storage storage.Storage account *account.Manager cfg *config.Config } func NewShell(storage storage.Storage, log *log.Logger, cfg *config.Config) *Shell { return &Shell{ storage: storage, account: account.NewManager(storage, log), cfg: cfg, } } func passwordProvider() (string, error) { p, err := account.DefaultProvider("Enter password for your new mail account: ") if err != nil { return "", err } p2, err := account.DefaultProvider("Comfirm your password: ") if err != nil { return "", err } if p == p2 { return p2, nil } return "", fmt.Errorf("Password not match") } type shellCommand func(args []string) error func (s *Shell) Run(c string, args []string) { // fmt.Println(c, args) var err error commands := map[string]shellCommand{ "accountAdd": s.accountAdd, "accountCreateNew": s.accountCreateNew, "accountGet": s.accountGet, "accountList": s.accountList, "accountDelete": s.accountDelete, "nodeInfo": s.getNodeInfo, } cmd, ok := commands[c] if !ok { log.Fatal("Command not found!") os.Exit(1) } err = cmd(args) if err != nil { fmt.Println(err) } } func (s *Shell) accountAdd(args []string) error { flags := flag.NewFlagSet("addAccount", flag.ExitOnError) // All flags pubkey := flags.String("pub", "", "Account public key") file := flags.String("f", "", "Added from file") flags.Parse(args) var ( err error acc *types.Account = new(types.Account) ) if *file != "" { cont, err := os.ReadFile(*file) if err != nil { return err } if err := json.Unmarshal(cont, acc); err != nil { return err } if err := s.storage.AccountCreate(acc); err != nil { return err } fmt.Printf("Account %s@%s added", acc.PublicKey, utils.EncodeString(s.cfg.PublicKey)) for _, folder := range []string{"INBOX", "Outbox", "Sent", "Trash", "Drafts"} { s.storage.MailboxCreate(acc.PublicKey, folder) } return nil } if acc, err = s.account.AddAccount(*pubkey); err != nil { return err } fmt.Printf("Account %s@%s added", acc.PublicKey, utils.EncodeString(s.cfg.PublicKey)) return nil } func (s *Shell) getNodeInfo(args []string) error { flags := flag.NewFlagSet("nodeInfo", flag.ExitOnError) flags.Parse(args) pks := utils.EncodeString(s.cfg.PublicKey) accList, err := s.storage.AccountList() if err != nil { return err } fmt.Println("== Node Info == ") fmt.Println("Address base32:", pks) fmt.Println("Address hex16:", hex.EncodeToString(s.cfg.PublicKey)) fmt.Println("Accounts: ", len(accList)) return nil } func (s *Shell) accountList(args []string) error { accList, err := s.storage.AccountList() if err != nil { return err } fmt.Println("Accounts: ", len(accList)) for i, a := range accList { count, err := s.storage.MailCount(a.PublicKey, "INBOX") if err != nil { count = 0 } fmt.Printf("\n| %d | %s | %s | Mails: %d", i, a.PublicKey, a.Aliases, count) } return nil } func (s *Shell) accountCreateNew(args []string) error { a, err := s.account.CreateNewAccount(passwordProvider) if err != nil { return err } fmt.Printf("Created account: %s@%s", a.PublicKey, utils.EncodeString(s.cfg.PublicKey)) return nil } func (s *Shell) accountGet(args []string) error { flags := flag.NewFlagSet("getAccount", flag.ExitOnError) // All flags pubkey := flags.String("pub", "", "Account Publickey") file := flags.String("o", "", "Write to file") flags.Parse(args) var ( err error acc *types.Account ) if acc, err = s.account.GetAccount(*pubkey); err != nil { return err } b, err := json.Marshal(acc) if err != nil { return err } if *file == "" { fmt.Println(string(b)) } else { os.WriteFile(*file, b, 0600) fmt.Println("Write to ", *file) } return nil } func (s *Shell) accountDelete(args []string) error { flags := flag.NewFlagSet("getAccount", flag.ExitOnError) // All flags pubkey := flags.String("pub", "", "Account Publickey") flags.Parse(args) var ( err error ) if err = s.account.DeleteAccount(*pubkey); err != nil { return err } return nil }