set_flags.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. package imaputils
  2. import (
  3. "errors"
  4. "fmt"
  5. "git.sr.ht/~blallo/papero/config"
  6. "github.com/emersion/go-imap"
  7. )
  8. type tristate int
  9. const (
  10. undef tristate = 0
  11. doSet tristate = 1
  12. doUnset tristate = 2
  13. )
  14. func (t tristate) IsSet() bool {
  15. switch t {
  16. case undef:
  17. return false
  18. case doSet:
  19. return true
  20. case doUnset:
  21. return false
  22. default:
  23. panic(errors.New(fmt.Sprintf("unexpected tristate value %d", t)))
  24. }
  25. }
  26. func (t tristate) IsUnset() bool {
  27. switch t {
  28. case undef:
  29. return false
  30. case doSet:
  31. return false
  32. case doUnset:
  33. return true
  34. default:
  35. panic(errors.New(fmt.Sprintf("unexpected tristate value %d", t)))
  36. }
  37. }
  38. func (t tristate) String() string {
  39. switch t {
  40. case undef:
  41. return "undef"
  42. case doSet:
  43. return "set"
  44. case doUnset:
  45. return "unset"
  46. }
  47. panic("unexpected value for tristate")
  48. }
  49. type FlagsStatus struct {
  50. FlagSeen tristate
  51. FlagAnswered tristate
  52. FlagFlagged tristate
  53. FlagDeleted tristate
  54. FlagDraft tristate
  55. //FlagUnknown bool
  56. }
  57. func NewFlagsStatus() *FlagsStatus {
  58. return &FlagsStatus{
  59. undef,
  60. undef,
  61. undef,
  62. undef,
  63. undef,
  64. }
  65. }
  66. func (f *FlagsStatus) IntoSetFlagList() []interface{} {
  67. var result []interface{}
  68. switch {
  69. case f.FlagSeen.IsSet():
  70. result = append(result, imap.SeenFlag)
  71. case f.FlagAnswered.IsSet():
  72. result = append(result, imap.AnsweredFlag)
  73. case f.FlagFlagged.IsSet():
  74. result = append(result, imap.FlaggedFlag)
  75. case f.FlagDeleted.IsSet():
  76. result = append(result, imap.DeletedFlag)
  77. case f.FlagDraft.IsSet():
  78. result = append(result, imap.DraftFlag)
  79. }
  80. return result
  81. }
  82. func (f *FlagsStatus) IntoUnsetFlagList() []interface{} {
  83. var result []interface{}
  84. switch {
  85. case f.FlagSeen.IsUnset():
  86. result = append(result, imap.SeenFlag)
  87. case f.FlagAnswered.IsUnset():
  88. result = append(result, imap.AnsweredFlag)
  89. case f.FlagFlagged.IsUnset():
  90. result = append(result, imap.FlaggedFlag)
  91. case f.FlagDeleted.IsUnset():
  92. result = append(result, imap.DeletedFlag)
  93. case f.FlagDraft.IsUnset():
  94. result = append(result, imap.DraftFlag)
  95. }
  96. return result
  97. }
  98. func (f *FlagsStatus) WillSet() bool {
  99. switch {
  100. case f.FlagSeen.IsSet():
  101. return true
  102. case f.FlagAnswered.IsSet():
  103. return true
  104. case f.FlagFlagged.IsSet():
  105. return true
  106. case f.FlagDeleted.IsSet():
  107. return true
  108. case f.FlagDraft.IsSet():
  109. return true
  110. }
  111. return false
  112. }
  113. func (f *FlagsStatus) WillUnset() bool {
  114. switch {
  115. case f.FlagSeen.IsUnset():
  116. return true
  117. case f.FlagAnswered.IsUnset():
  118. return true
  119. case f.FlagFlagged.IsUnset():
  120. return true
  121. case f.FlagDeleted.IsUnset():
  122. return true
  123. case f.FlagDraft.IsUnset():
  124. return true
  125. }
  126. return false
  127. }
  128. func (f *FlagsStatus) SetSeen() {
  129. f.FlagSeen = doSet
  130. }
  131. func (f *FlagsStatus) UnsetSeen() {
  132. f.FlagSeen = doUnset
  133. }
  134. func (f *FlagsStatus) SetAnswered() {
  135. f.FlagAnswered = doSet
  136. }
  137. func (f *FlagsStatus) UnsetAnswered() {
  138. f.FlagAnswered = doUnset
  139. }
  140. func (f *FlagsStatus) SetFlagged() {
  141. f.FlagFlagged = doSet
  142. }
  143. func (f *FlagsStatus) UnsetFlagged() {
  144. f.FlagFlagged = doUnset
  145. }
  146. func (f *FlagsStatus) SetDeleted() {
  147. f.FlagDeleted = doSet
  148. }
  149. func (f *FlagsStatus) UnsetDeleted() {
  150. f.FlagDeleted = doUnset
  151. }
  152. func (f *FlagsStatus) SetDraft() {
  153. f.FlagDraft = doSet
  154. }
  155. func (f *FlagsStatus) UnsetDraft() {
  156. f.FlagDraft = doUnset
  157. }
  158. //func (f *FlagsStatus) SetUnknown() {
  159. // f.FlagUnknown = doSet
  160. //}
  161. //func (f *FlagsStatus) UnsetUnknown() {
  162. // f.FlagUnknown = doUnset
  163. //}
  164. type SetFlagsOpts struct {
  165. Mailbox string
  166. MessageSeq *imap.SeqSet
  167. Flags *FlagsStatus
  168. Debug bool
  169. }
  170. // SetFlags changes the flags on the specified message in the specified mailbox.
  171. // First it adds the new flags set, then in removes the flags explicitly unset.
  172. // Unfortunately, this operation is not atomic.
  173. func SetFlags(conf *config.AccountData, opts *SetFlagsOpts) error {
  174. conn := NewConnection(conf)
  175. err := conn.Start(opts.Debug)
  176. if err != nil {
  177. return err
  178. }
  179. defer conn.Close()
  180. return SetFlagsInSession(conn, opts)
  181. }
  182. // SetFlagsInSession does the same as SetFlags, but has to be provided
  183. // with a started *IMAPConnection.
  184. func SetFlagsInSession(conn *IMAPConnection, opts *SetFlagsOpts) error {
  185. var err error
  186. _, err = conn.client.Select(opts.Mailbox, false)
  187. if err != nil {
  188. return err
  189. }
  190. if opts.Flags.WillSet() {
  191. item := imap.FormatFlagsOp(imap.AddFlags, true)
  192. flags := opts.Flags.IntoSetFlagList()
  193. err = conn.client.Store(opts.MessageSeq, item, flags, nil)
  194. if err != nil {
  195. return err
  196. }
  197. }
  198. if opts.Flags.WillUnset() {
  199. item := imap.FormatFlagsOp(imap.RemoveFlags, true)
  200. flags := opts.Flags.IntoUnsetFlagList()
  201. err = conn.client.Store(opts.MessageSeq, item, flags, nil)
  202. if err != nil {
  203. return err
  204. }
  205. }
  206. return nil
  207. }