diff --git a/README.md b/README.md index 587c6ac..a41b2d1 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,12 @@ Yggdrasil is well-suited for ad-hoc mail delivery and allows Yggmail to work eve Use a recent version of Go to install Yggmail: ``` -go install github.com/neilalexander/yggmail/cmd/yggmail +go install github.com/neilalexander/yggmail/cmd/yggmail@latest +``` + +It will then be installed into your `GOPATH`, so add that to your environment: +``` +export PATH=$PATH:`go env GOPATH`/bin ``` Create a mailbox and set your password. Your Yggmail database will automatically be created in your working directory if it doesn't already exist: diff --git a/cmd/yggmail/main.go b/cmd/yggmail/main.go index 00b3cf9..03657c2 100644 --- a/cmd/yggmail/main.go +++ b/cmd/yggmail/main.go @@ -66,9 +66,6 @@ func main() { if err := storage.ConfigSet("private_key", hex.EncodeToString(sk)); err != nil { panic(err) } - if err := storage.MailboxCreate("INBOX"); err != nil { - panic(err) - } log.Printf("Generated new server identity") } else { skBytes, err := hex.DecodeString(skStr) @@ -80,6 +77,12 @@ func main() { pk := sk.Public().(ed25519.PublicKey) log.Printf("Mail address: %s@%s\n", hex.EncodeToString(pk), utils.Domain) + for _, name := range []string{"INBOX", "Outbox"} { + if err := storage.MailboxCreate(name); err != nil { + panic(err) + } + } + switch { case password != nil && *password: log.Println("Please enter your new password:") diff --git a/internal/imapserver/backend.go b/internal/imapserver/backend.go index 415933d..9bc6f7a 100644 --- a/internal/imapserver/backend.go +++ b/internal/imapserver/backend.go @@ -18,7 +18,7 @@ type Backend struct { Storage storage.Storage } -func (b *Backend) Login(_ *imap.ConnInfo, username, password string) (backend.User, error) { +func (b *Backend) Login(conn *imap.ConnInfo, username, password string) (backend.User, error) { // If our username is email-like, then take just the localpart if pk, err := utils.ParseAddress(username); err == nil { if !pk.Equal(b.Config.PublicKey) { @@ -34,7 +34,7 @@ func (b *Backend) Login(_ *imap.ConnInfo, username, password string) (backend.Us b.Log.Printf("Failed to authenticate IMAP user %q\n", username) return nil, backend.ErrInvalidCredentials } - defer b.Log.Printf("Authenticated IMAP user %q\n", username) + defer b.Log.Printf("Authenticated IMAP user from %s as %q\n", conn.RemoteAddr.String(), username) user := &User{ backend: b, username: username, diff --git a/internal/imapserver/mailbox.go b/internal/imapserver/mailbox.go index 95c93ea..7c805a4 100644 --- a/internal/imapserver/mailbox.go +++ b/internal/imapserver/mailbox.go @@ -273,6 +273,10 @@ func (mbox *Mailbox) UpdateMessagesFlags(uid bool, seqSet *imap.SeqSet, op imap. } func (mbox *Mailbox) CopyMessages(uid bool, seqSet *imap.SeqSet, destName string) error { + if destName == "Outbox" { + return fmt.Errorf("can't copy into Outbox as it is a protected folder") + } + ids, err := mbox.getIDsFromSeqSet(uid, seqSet) if err != nil { return fmt.Errorf("mbox.getIDsFromSeqSet: %w", err) diff --git a/internal/imapserver/user.go b/internal/imapserver/user.go index b399495..32bdaae 100644 --- a/internal/imapserver/user.go +++ b/internal/imapserver/user.go @@ -58,17 +58,21 @@ func (u *User) CreateMailbox(name string) error { } func (u *User) DeleteMailbox(name string) error { - if name == "INBOX" { - return errors.New("Cannot delete INBOX") + switch name { + case "INBOX", "Outbox": + return errors.New("Cannot delete " + name) + default: + return u.backend.Storage.MailboxDelete(name) } - return u.backend.Storage.MailboxDelete(name) } func (u *User) RenameMailbox(existingName, newName string) error { - if existingName == "INBOX" { - return errors.New("Cannot rename INBOX") + switch existingName { + case "INBOX", "Outbox": + return errors.New("Cannot rename " + existingName) + default: + return u.backend.Storage.MailboxRename(existingName, newName) } - return u.backend.Storage.MailboxRename(existingName, newName) } func (u *User) Logout() error { diff --git a/internal/smtpserver/backend.go b/internal/smtpserver/backend.go index 3a98c3c..5279074 100644 --- a/internal/smtpserver/backend.go +++ b/internal/smtpserver/backend.go @@ -45,7 +45,7 @@ func (b *Backend) Login(state *smtp.ConnectionState, username, password string) b.Log.Printf("Failed to authenticate SMTP user %q\n", username) return nil, smtp.ErrAuthRequired } - defer b.Log.Printf("Authenticated SMTP user %q\n", username) + defer b.Log.Printf("Authenticated SMTP user from %s as %q\n", state.RemoteAddr.String(), username) return &SessionLocal{ backend: b, state: state, diff --git a/internal/storage/sqlite3/table_mailboxes.go b/internal/storage/sqlite3/table_mailboxes.go index d36618f..f6cf886 100644 --- a/internal/storage/sqlite3/table_mailboxes.go +++ b/internal/storage/sqlite3/table_mailboxes.go @@ -37,7 +37,7 @@ const mailboxesSelect = ` ` const mailboxesCreate = ` - INSERT INTO mailboxes (mailbox) VALUES($1) + INSERT OR IGNORE INTO mailboxes (mailbox) VALUES($1) ` const mailboxesRename = `