-
-
Notifications
You must be signed in to change notification settings - Fork 8
/
message.go
236 lines (212 loc) · 7.87 KB
/
message.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
package notmuch
// Copyright © 2015 The go.notmuch Authors. Authors can be found in the AUTHORS file.
// Licensed under the GPLv3 or later.
// See COPYING at the root of the repository for details.
// #cgo LDFLAGS: -lnotmuch
// #include <stdlib.h>
// #include <notmuch.h>
import "C"
import (
"time"
"unsafe"
)
// Message represents a notmuch message.
type Message cStruct
func (m *Message) toC() *C.notmuch_message_t {
return (*C.notmuch_message_t)(m.cptr)
}
func (m *Message) Close() error {
return (*cStruct)(m).doClose(func() error {
C.notmuch_message_destroy(m.toC())
return nil
})
}
// ID returns the message ID.
func (m *Message) ID() string {
return C.GoString(C.notmuch_message_get_message_id(m.toC()))
}
// ThreadID returns the ID of the thread to which this message belongs to.
func (m *Message) ThreadID() string {
return C.GoString(C.notmuch_message_get_thread_id(m.toC()))
}
// Replies returns the replies of a message.
func (m *Message) Replies() (*Messages, error) {
cmsgs := C.notmuch_message_get_replies(m.toC())
if unsafe.Pointer(cmsgs) == nil {
return nil, ErrNoRepliesOrPointerNotFromThread
}
// We point the messages object directly at our thread, rather than having
// the gc reference go through this message:
msgs := &Messages{
cptr: unsafe.Pointer(cmsgs),
parent: (*cStruct)(m),
}
setGcClose(msgs)
return msgs, nil
}
// Filename returns the absolute path of the email message.
//
// Note: If this message corresponds to multiple files in the mail store, (that
// is, multiple files contain identical message IDs), this function will
// arbitrarily return a single one of those filenames. See Filenames for
// returning the complete list of filenames.
func (m *Message) Filename() string {
return C.GoString(C.notmuch_message_get_filename(m.toC()))
}
// Filenames returns *Filenames an iterator to get the message's filenames.
// Each filename in the iterator is an absolute filename.
func (m *Message) Filenames() *Filenames {
return &Filenames{
cptr: C.notmuch_message_get_filenames(m.toC()),
message: m,
}
}
// Date returns the date of the message.
func (m *Message) Date() time.Time {
ctime := C.notmuch_message_get_date(m.toC())
return time.Unix(int64(ctime), 0)
}
// Header returns the value of the header.
func (m *Message) Header(name string) string {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
return C.GoString(C.notmuch_message_get_header(m.toC(), cname))
}
// Tags returns the tags for the current message, returning a *Tags which can
// be used to iterate over all tags using `Tags.Next(Tag)`
func (m *Message) Tags() *Tags {
ctags := C.notmuch_message_get_tags(m.toC())
tags := &Tags{
cptr: unsafe.Pointer(ctags),
parent: (*cStruct)(m),
}
setGcClose(tags)
return tags
}
// AddTag adds a tag to the message.
func (m *Message) AddTag(tag string) error {
ctag := C.CString(tag)
defer C.free(unsafe.Pointer(ctag))
return statusErr(C.notmuch_message_add_tag(m.toC(), ctag))
}
// RemoveTag removes a tag from the message.
func (m *Message) RemoveTag(tag string) error {
ctag := C.CString(tag)
defer C.free(unsafe.Pointer(ctag))
return statusErr(C.notmuch_message_remove_tag(m.toC(), ctag))
}
// RemoveAllTags removes all tags from the message.
func (m *Message) RemoveAllTags() error {
return statusErr(C.notmuch_message_remove_all_tags(m.toC()))
}
// Properties returns the properties for the current message, returning a
// *MessageProperties which can be used to iterate over all properties using
// `MessageProperties.Next(MessageProperty)`
func (m *Message) Properties(key string, exact bool) *MessageProperties {
cexact := 0
if exact {
cexact = 1
}
ckey := C.CString(key)
defer C.free(unsafe.Pointer(ckey))
cprops := C.notmuch_message_get_properties(m.toC(), ckey, C.int(cexact))
props := &MessageProperties{
cptr: unsafe.Pointer(cprops),
parent: (*cStruct)(m),
}
setGcClose(props)
return props
}
// AddProperty adds a property to the message.
func (m *Message) AddProperty(key string, value string) error {
ckey := C.CString(key)
defer C.free(unsafe.Pointer(ckey))
cvalue := C.CString(value)
defer C.free(unsafe.Pointer(cvalue))
return statusErr(C.notmuch_message_add_property(m.toC(), ckey, cvalue))
}
// RemoveProperty removes a key/value pair from the message properties.
func (m *Message) RemoveProperty(key string, value string) error {
ckey := C.CString(key)
defer C.free(unsafe.Pointer(ckey))
cvalue := C.CString(value)
defer C.free(unsafe.Pointer(cvalue))
return statusErr(C.notmuch_message_remove_property(m.toC(), ckey, cvalue))
}
// RemoveAllProperties removes all properties with key from the message.
func (m *Message) RemoveAllProperties(key string) error {
ckey := C.CString(key)
defer C.free(unsafe.Pointer(ckey))
return statusErr(C.notmuch_message_remove_all_properties(m.toC(), ckey))
}
// Atomic allows a transactional change of tags to the message.
func (m *Message) Atomic(callback func(*Message)) error {
if err := statusErr(C.notmuch_message_freeze(m.toC())); err != nil {
return err
}
callback(m)
return statusErr(C.notmuch_message_thaw(m.toC()))
}
// MaildirFlagsToTags adds/removes tags according to maildir flags in the message filename(s).
// This function examines the filenames of 'message' for maildir
// flags, and adds or removes tags on 'message' as follows when these
// flags are present:
//
// Flag Action if present
// ---- -----------------
// 'D' Adds the "draft" tag to the message
// 'F' Adds the "flagged" tag to the message
// 'P' Adds the "passed" tag to the message
// 'R' Adds the "replied" tag to the message
// 'S' Removes the "unread" tag from the message
//
// For each flag that is not present, the opposite action (add/remove)
// is performed for the corresponding tags.
//
// Flags are identified as trailing components of the filename after a
// sequence of ":2,".
//
// If there are multiple filenames associated with this message, the
// flag is considered present if it appears in one or more
// filenames. (That is, the flags from the multiple filenames are
// combined with the logical OR operator.)
//
// A client can ensure that notmuch database tags remain synchronized
// with maildir flags by calling this function after each call to
// DB.AddMessage. See also Message.TagsToMaildirFlags for synchronizing
// tag changes back to maildir flags.
func (m *Message) MaildirFlagsToTags() error {
return statusErr(C.notmuch_message_maildir_flags_to_tags(m.toC()))
}
// TagsToMaildirFlags renames message filename(s) to encode tags as maildir flags.
// Specifically, for each filename corresponding to this message:
//
// If the filename is not in a maildir directory, do nothing. (A
// maildir directory is determined as a directory named "new" or
// "cur".) Similarly, if the filename has invalid maildir info,
// (repeated or outof-ASCII-order flag characters after ":2,"), then
// do nothing.
//
// If the filename is in a maildir directory, rename the file so that
// its filename ends with the sequence ":2," followed by zero or more
// of the following single-character flags (in ASCII order):
//
// * flag 'D' if the message has the "draft" tag
// * flag 'F' if the message has the "flagged" tag
// * flag 'P' if the message has the "passed" tag
// * flag 'R' if the message has the "replied" tag
// * flag 'S' if the message does not have the "unread" tag
//
// Any existing flags unmentioned in the list above will be preserved
// in the renaming.
//
// Also, if this filename is in a directory named "new", rename it to
// be within the neighboring directory named "cur".
//
// A client can ensure that maildir filename flags remain synchronized
// with notmuch database tags by calling this function after changing
// tags. See also Message.MaildirFlagsToTags for synchronizing maildir flag
// changes back to tags.
func (m *Message) TagsToMaildirFlags() error {
return statusErr(C.notmuch_message_tags_to_maildir_flags(m.toC()))
}