package imaputils import ( "errors" "fmt" "git.sr.ht/~blallo/papero/config" "github.com/emersion/go-imap" ) type tristate int const ( undef tristate = 0 doSet tristate = 1 doUnset tristate = 2 ) func (t tristate) IsSet() bool { switch t { case undef: return false case doSet: return true case doUnset: return false default: panic(errors.New(fmt.Sprintf("unexpected tristate value %d", t))) } } func (t tristate) IsUnset() bool { switch t { case undef: return false case doSet: return false case doUnset: return true default: panic(errors.New(fmt.Sprintf("unexpected tristate value %d", t))) } } func (t tristate) String() string { switch t { case undef: return "undef" case doSet: return "set" case doUnset: return "unset" } panic("unexpected value for tristate") } type FlagsStatus struct { FlagSeen tristate FlagAnswered tristate FlagFlagged tristate FlagDeleted tristate FlagDraft tristate //FlagUnknown bool } func NewFlagsStatus() *FlagsStatus { return &FlagsStatus{ undef, undef, undef, undef, undef, } } func (f *FlagsStatus) IntoSetFlagList() []interface{} { var result []interface{} switch { case f.FlagSeen.IsSet(): result = append(result, imap.SeenFlag) case f.FlagAnswered.IsSet(): result = append(result, imap.AnsweredFlag) case f.FlagFlagged.IsSet(): result = append(result, imap.FlaggedFlag) case f.FlagDeleted.IsSet(): result = append(result, imap.DeletedFlag) case f.FlagDraft.IsSet(): result = append(result, imap.DraftFlag) } return result } func (f *FlagsStatus) IntoUnsetFlagList() []interface{} { var result []interface{} switch { case f.FlagSeen.IsUnset(): result = append(result, imap.SeenFlag) case f.FlagAnswered.IsUnset(): result = append(result, imap.AnsweredFlag) case f.FlagFlagged.IsUnset(): result = append(result, imap.FlaggedFlag) case f.FlagDeleted.IsUnset(): result = append(result, imap.DeletedFlag) case f.FlagDraft.IsUnset(): result = append(result, imap.DraftFlag) } return result } func (f *FlagsStatus) WillSet() bool { switch { case f.FlagSeen.IsSet(): return true case f.FlagAnswered.IsSet(): return true case f.FlagFlagged.IsSet(): return true case f.FlagDeleted.IsSet(): return true case f.FlagDraft.IsSet(): return true } return false } func (f *FlagsStatus) WillUnset() bool { switch { case f.FlagSeen.IsUnset(): return true case f.FlagAnswered.IsUnset(): return true case f.FlagFlagged.IsUnset(): return true case f.FlagDeleted.IsUnset(): return true case f.FlagDraft.IsUnset(): return true } return false } func (f *FlagsStatus) SetSeen() { f.FlagSeen = doSet } func (f *FlagsStatus) UnsetSeen() { f.FlagSeen = doUnset } func (f *FlagsStatus) SetAnswered() { f.FlagAnswered = doSet } func (f *FlagsStatus) UnsetAnswered() { f.FlagAnswered = doUnset } func (f *FlagsStatus) SetFlagged() { f.FlagFlagged = doSet } func (f *FlagsStatus) UnsetFlagged() { f.FlagFlagged = doUnset } func (f *FlagsStatus) SetDeleted() { f.FlagDeleted = doSet } func (f *FlagsStatus) UnsetDeleted() { f.FlagDeleted = doUnset } func (f *FlagsStatus) SetDraft() { f.FlagDraft = doSet } func (f *FlagsStatus) UnsetDraft() { f.FlagDraft = doUnset } //func (f *FlagsStatus) SetUnknown() { // f.FlagUnknown = doSet //} //func (f *FlagsStatus) UnsetUnknown() { // f.FlagUnknown = doUnset //} type SetFlagsOpts struct { Mailbox string MessageSeq *imap.SeqSet Flags *FlagsStatus Debug bool } // SetFlags changes the flags on the specified message in the specified mailbox. // First it adds the new flags set, then in removes the flags explicitly unset. // Unfortunately, this operation is not atomic. func SetFlags(conf *config.AccountData, opts *SetFlagsOpts) error { conn := NewConnection(conf) err := conn.Start(opts.Debug) if err != nil { return err } defer conn.Close() _, err = conn.client.Select(opts.Mailbox, false) if err != nil { return err } if opts.Flags.WillSet() { item := imap.FormatFlagsOp(imap.AddFlags, true) flags := opts.Flags.IntoSetFlagList() err = conn.client.Store(opts.MessageSeq, item, flags, nil) if err != nil { return err } } if opts.Flags.WillUnset() { item := imap.FormatFlagsOp(imap.RemoveFlags, true) flags := opts.Flags.IntoUnsetFlagList() err = conn.client.Store(opts.MessageSeq, item, flags, nil) if err != nil { return err } } return nil }