/src/dovecot/src/lib-storage/index/dbox-common/dbox-mail.c
Line | Count | Source |
1 | | /* Copyright (c) Dovecot authors, see top-level COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "istream.h" |
5 | | #include "str.h" |
6 | | #include "index-storage.h" |
7 | | #include "index-mail.h" |
8 | | #include "index-pop3-uidl.h" |
9 | | #include "dbox-attachment.h" |
10 | | #include "dbox-storage.h" |
11 | | #include "dbox-file.h" |
12 | | #include "dbox-mail.h" |
13 | | |
14 | | |
15 | | struct mail * |
16 | | dbox_mail_alloc(struct mailbox_transaction_context *t, |
17 | | enum mail_fetch_field wanted_fields, |
18 | | struct mailbox_header_lookup_ctx *wanted_headers) |
19 | 0 | { |
20 | 0 | struct dbox_mail *mail; |
21 | 0 | pool_t pool; |
22 | |
|
23 | 0 | pool = pool_alloconly_create("mail", 2048); |
24 | 0 | mail = p_new(pool, struct dbox_mail, 1); |
25 | |
|
26 | 0 | index_mail_init(&mail->imail, t, wanted_fields, wanted_headers, pool, NULL); |
27 | 0 | return &mail->imail.mail.mail; |
28 | 0 | } |
29 | | |
30 | | void dbox_mail_close(struct mail *_mail) |
31 | 0 | { |
32 | 0 | struct dbox_mail *mail = DBOX_MAIL(_mail); |
33 | |
|
34 | 0 | index_mail_close(_mail); |
35 | | /* close the dbox file only after index is closed, since it may still |
36 | | try to read from it. */ |
37 | 0 | if (mail->open_file != NULL) |
38 | 0 | dbox_file_unref(&mail->open_file); |
39 | 0 | } |
40 | | |
41 | | int dbox_mail_metadata_read(struct dbox_mail *mail, struct dbox_file **file_r) |
42 | 0 | { |
43 | 0 | struct dbox_storage *storage = |
44 | 0 | DBOX_STORAGE(mail->imail.mail.mail.box->storage); |
45 | 0 | uoff_t offset; |
46 | |
|
47 | 0 | if (storage->v.mail_open(mail, &offset, file_r) < 0) |
48 | 0 | return -1; |
49 | | |
50 | 0 | if (dbox_file_seek(*file_r, offset) <= 0) |
51 | 0 | return -1; |
52 | 0 | if (dbox_file_metadata_read(*file_r) <= 0) |
53 | 0 | return -1; |
54 | | |
55 | 0 | if (mail->imail.data.stream != NULL) { |
56 | | /* we just messed up mail's input stream by reading metadata */ |
57 | 0 | i_stream_seek((*file_r)->input, offset); |
58 | 0 | i_stream_sync(mail->imail.data.stream); |
59 | 0 | } else |
60 | 0 | mail_metadata_accessed_event(mail_event(&mail->imail.mail.mail)); |
61 | |
|
62 | 0 | return 0; |
63 | 0 | } |
64 | | |
65 | | static int |
66 | | dbox_mail_metadata_get(struct dbox_mail *mail, enum dbox_metadata_key key, |
67 | | const char **value_r) |
68 | 0 | { |
69 | 0 | struct dbox_file *file; |
70 | |
|
71 | 0 | if (dbox_mail_metadata_read(mail, &file) < 0) |
72 | 0 | return -1; |
73 | | |
74 | 0 | *value_r = dbox_file_metadata_get(file, key); |
75 | 0 | return 0; |
76 | 0 | } |
77 | | |
78 | | int dbox_mail_get_physical_size(struct mail *_mail, uoff_t *size_r) |
79 | 0 | { |
80 | 0 | struct dbox_mail *mail = DBOX_MAIL(_mail); |
81 | 0 | struct index_mail_data *data = &mail->imail.data; |
82 | 0 | struct dbox_file *file; |
83 | |
|
84 | 0 | if (index_mail_get_physical_size(_mail, size_r) == 0) |
85 | 0 | return 0; |
86 | | |
87 | 0 | if (dbox_mail_metadata_read(mail, &file) < 0) |
88 | 0 | return -1; |
89 | | |
90 | 0 | data->physical_size = dbox_file_get_plaintext_size(file); |
91 | 0 | *size_r = data->physical_size; |
92 | 0 | return 0; |
93 | 0 | } |
94 | | |
95 | | int dbox_mail_get_virtual_size(struct mail *_mail, uoff_t *size_r) |
96 | 0 | { |
97 | 0 | struct dbox_mail *mail = DBOX_MAIL(_mail); |
98 | 0 | struct index_mail_data *data = &mail->imail.data; |
99 | 0 | const char *value; |
100 | 0 | uintmax_t size; |
101 | |
|
102 | 0 | if (index_mail_get_cached_virtual_size(&mail->imail, size_r)) |
103 | 0 | return 0; |
104 | | |
105 | 0 | if (dbox_mail_metadata_get(mail, DBOX_METADATA_VIRTUAL_SIZE, |
106 | 0 | &value) < 0) |
107 | 0 | return -1; |
108 | 0 | if (value == NULL) |
109 | 0 | return index_mail_get_virtual_size(_mail, size_r); |
110 | | |
111 | 0 | if (str_to_uintmax_hex(value, &size) < 0 || size > UOFF_T_MAX) |
112 | 0 | return -1; |
113 | 0 | data->virtual_size = (uoff_t)size; |
114 | 0 | *size_r = data->virtual_size; |
115 | 0 | return 0; |
116 | 0 | } |
117 | | |
118 | | int dbox_mail_get_received_date(struct mail *_mail, time_t *date_r) |
119 | 0 | { |
120 | 0 | struct dbox_mail *mail = DBOX_MAIL(_mail); |
121 | 0 | struct index_mail_data *data = &mail->imail.data; |
122 | 0 | const char *value; |
123 | 0 | uintmax_t time; |
124 | |
|
125 | 0 | if (index_mail_get_received_date(_mail, date_r) == 0) |
126 | 0 | return 0; |
127 | | |
128 | 0 | if (dbox_mail_metadata_get(mail, DBOX_METADATA_RECEIVED_TIME, |
129 | 0 | &value) < 0) |
130 | 0 | return -1; |
131 | | |
132 | 0 | time = 0; |
133 | 0 | if (value != NULL && str_to_uintmax_hex(value, &time) < 0) |
134 | 0 | return -1; |
135 | | |
136 | 0 | data->received_date = (time_t)time; |
137 | 0 | *date_r = data->received_date; |
138 | 0 | return 0; |
139 | 0 | } |
140 | | |
141 | | int dbox_mail_get_save_date(struct mail *_mail, time_t *date_r) |
142 | 0 | { |
143 | 0 | struct dbox_storage *storage = DBOX_STORAGE(_mail->box->storage); |
144 | 0 | struct dbox_mail *mail = DBOX_MAIL(_mail); |
145 | 0 | struct index_mail_data *data = &mail->imail.data; |
146 | 0 | struct stat st; |
147 | |
|
148 | 0 | if (index_mail_get_save_date(_mail, date_r) > 0) |
149 | 0 | return 1; |
150 | | |
151 | 0 | if (storage->v.mail_file_set(mail) < 0) |
152 | 0 | return -1; |
153 | | |
154 | 0 | _mail->transaction->stats.stat_lookup_count++; |
155 | 0 | if (dbox_file_stat(mail->open_file, |
156 | 0 | mail_event(&mail->imail.mail.mail), &st) < 0) { |
157 | 0 | if (errno == ENOENT) |
158 | 0 | mail_set_expunged(_mail); |
159 | 0 | return -1; |
160 | 0 | } |
161 | 0 | *date_r = data->save_date = st.st_ctime; |
162 | 0 | return 1; |
163 | 0 | } |
164 | | |
165 | | static int |
166 | | dbox_get_cached_metadata(struct dbox_mail *mail, enum dbox_metadata_key key, |
167 | | enum index_cache_field cache_field, |
168 | | const char **value_r) |
169 | 0 | { |
170 | 0 | struct index_mail *imail = &mail->imail; |
171 | 0 | struct index_mailbox_context *ibox = |
172 | 0 | INDEX_STORAGE_CONTEXT(imail->mail.mail.box); |
173 | 0 | const char *value; |
174 | 0 | string_t *str; |
175 | 0 | uint32_t order; |
176 | |
|
177 | 0 | str = str_new(imail->mail.data_pool, 64); |
178 | 0 | if (mail_cache_lookup_field(imail->mail.mail.transaction->cache_view, |
179 | 0 | str, imail->mail.mail.seq, |
180 | 0 | ibox->cache_fields[cache_field].idx) > 0) { |
181 | 0 | if (cache_field == MAIL_CACHE_POP3_ORDER) { |
182 | 0 | i_assert(str_len(str) == sizeof(order)); |
183 | 0 | memcpy(&order, str_data(str), sizeof(order)); |
184 | 0 | str_truncate(str, 0); |
185 | 0 | if (order != 0) |
186 | 0 | str_printfa(str, "%u", order); |
187 | 0 | else { |
188 | | /* order=0 means it doesn't exist. we don't |
189 | | want to return "0" though, because then the |
190 | | mails get ordered to beginning, while |
191 | | nonexistent are supposed to be ordered at |
192 | | the end. */ |
193 | 0 | } |
194 | 0 | } |
195 | 0 | *value_r = str_c(str); |
196 | 0 | return 0; |
197 | 0 | } |
198 | | |
199 | 0 | if (dbox_mail_metadata_get(mail, key, &value) < 0) |
200 | 0 | return -1; |
201 | | |
202 | 0 | if (value == NULL) |
203 | 0 | value = ""; |
204 | 0 | if (cache_field != MAIL_CACHE_POP3_ORDER) { |
205 | 0 | index_mail_cache_add_idx(imail, ibox->cache_fields[cache_field].idx, |
206 | 0 | value, strlen(value)); |
207 | 0 | } else { |
208 | 0 | if (str_to_uint(value, &order) < 0) |
209 | 0 | order = 0; |
210 | 0 | index_mail_cache_add_idx(imail, ibox->cache_fields[cache_field].idx, |
211 | 0 | &order, sizeof(order)); |
212 | 0 | } |
213 | | |
214 | | /* don't return pointer to dbox metadata directly, since it may |
215 | | change unexpectedly */ |
216 | 0 | str_truncate(str, 0); |
217 | 0 | str_append(str, value); |
218 | 0 | *value_r = str_c(str); |
219 | 0 | return 0; |
220 | 0 | } |
221 | | |
222 | | int dbox_mail_get_special(struct mail *_mail, enum mail_fetch_field field, |
223 | | const char **value_r) |
224 | 0 | { |
225 | 0 | struct dbox_mail *mail = DBOX_MAIL(_mail); |
226 | 0 | int ret; |
227 | | |
228 | | /* keep the UIDL in cache file, otherwise POP3 would open all |
229 | | mail files and read the metadata. same for GUIDs if they're |
230 | | used. */ |
231 | 0 | switch (field) { |
232 | 0 | case MAIL_FETCH_UIDL_BACKEND: |
233 | 0 | if (!index_pop3_uidl_can_exist(_mail)) { |
234 | 0 | *value_r = ""; |
235 | 0 | return 0; |
236 | 0 | } |
237 | 0 | ret = dbox_get_cached_metadata(mail, DBOX_METADATA_POP3_UIDL, |
238 | 0 | MAIL_CACHE_POP3_UIDL, value_r); |
239 | 0 | if (ret == 0) { |
240 | 0 | index_pop3_uidl_update_exists(&mail->imail.mail.mail, |
241 | 0 | (*value_r)[0] != '\0'); |
242 | 0 | } |
243 | 0 | return ret; |
244 | 0 | case MAIL_FETCH_POP3_ORDER: |
245 | 0 | if (!index_pop3_uidl_can_exist(_mail)) { |
246 | | /* we're assuming that if there's a POP3 order, there's |
247 | | also a UIDL */ |
248 | 0 | *value_r = ""; |
249 | 0 | return 0; |
250 | 0 | } |
251 | 0 | return dbox_get_cached_metadata(mail, DBOX_METADATA_POP3_ORDER, |
252 | 0 | MAIL_CACHE_POP3_ORDER, value_r); |
253 | 0 | case MAIL_FETCH_GUID: |
254 | 0 | return dbox_get_cached_metadata(mail, DBOX_METADATA_GUID, |
255 | 0 | MAIL_CACHE_GUID, value_r); |
256 | 0 | default: |
257 | 0 | break; |
258 | 0 | } |
259 | | |
260 | 0 | return index_mail_get_special(_mail, field, value_r); |
261 | 0 | } |
262 | | |
263 | | static int |
264 | | get_mail_stream(struct dbox_mail *mail, uoff_t offset, |
265 | | struct istream **stream_r) |
266 | 0 | { |
267 | 0 | struct mail_private *pmail = &mail->imail.mail; |
268 | 0 | struct dbox_file *file = mail->open_file; |
269 | 0 | int ret; |
270 | |
|
271 | 0 | if ((ret = dbox_file_seek(file, offset)) <= 0) { |
272 | 0 | *stream_r = NULL; |
273 | 0 | return ret; |
274 | 0 | } |
275 | | |
276 | 0 | *stream_r = i_stream_create_limit(file->input, file->cur_physical_size); |
277 | 0 | if (pmail->v.istream_opened != NULL) { |
278 | 0 | if (pmail->v.istream_opened(&pmail->mail, stream_r) < 0) |
279 | 0 | return -1; |
280 | 0 | } |
281 | 0 | return dbox_attachment_file_get_stream(file, stream_r); |
282 | 0 | } |
283 | | |
284 | | int dbox_mail_get_stream(struct mail *_mail, bool get_body ATTR_UNUSED, |
285 | | struct message_size *hdr_size, |
286 | | struct message_size *body_size, |
287 | | struct istream **stream_r) |
288 | 0 | { |
289 | 0 | struct dbox_storage *storage = DBOX_STORAGE(_mail->box->storage); |
290 | 0 | struct dbox_mail *mail = DBOX_MAIL(_mail); |
291 | 0 | struct index_mail_data *data = &mail->imail.data; |
292 | 0 | struct istream *input; |
293 | 0 | uoff_t offset; |
294 | 0 | int ret; |
295 | |
|
296 | 0 | if (data->stream == NULL) { |
297 | 0 | if (storage->v.mail_open(mail, &offset, &mail->open_file) < 0) |
298 | 0 | return -1; |
299 | | |
300 | 0 | ret = get_mail_stream(mail, offset, &input); |
301 | 0 | if (ret <= 0) { |
302 | 0 | i_stream_unref(&input); |
303 | 0 | return -1; |
304 | 0 | } |
305 | 0 | data->stream = input; |
306 | 0 | index_mail_set_read_buffer_size(_mail, input); |
307 | 0 | } |
308 | | |
309 | 0 | return index_mail_init_stream(&mail->imail, hdr_size, body_size, |
310 | 0 | stream_r); |
311 | 0 | } |