Moved from ../
Showing
1 changed file
with
1533 additions
and
0 deletions
mailbox/mh/mbox.c
0 → 100644
1 | /* GNU Mailutils -- a suite of utilities for electronic mail | ||
2 | Copyright (C) 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. | ||
3 | |||
4 | This library is free software; you can redistribute it and/or | ||
5 | modify it under the terms of the GNU Lesser General Public | ||
6 | License as published by the Free Software Foundation; either | ||
7 | version 2 of the License, or (at your option) any later version. | ||
8 | |||
9 | This library is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | Lesser General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU Lesser General Public | ||
15 | License along with this library; if not, write to the Free Software | ||
16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ | ||
17 | |||
18 | /* First draft by Sergey Poznyakoff */ | ||
19 | |||
20 | #ifdef HAVE_CONFIG_H | ||
21 | # include <config.h> | ||
22 | #endif | ||
23 | |||
24 | #ifdef ENABLE_MH | ||
25 | |||
26 | #include <sys/types.h> | ||
27 | #include <stdlib.h> | ||
28 | #include <stdio.h> | ||
29 | #include <time.h> | ||
30 | #include <sys/stat.h> | ||
31 | #include <fcntl.h> | ||
32 | #include <unistd.h> | ||
33 | #include <errno.h> | ||
34 | #include <ctype.h> | ||
35 | #include <dirent.h> | ||
36 | |||
37 | #ifdef WITH_PTHREAD | ||
38 | # ifdef HAVE_PTHREAD_H | ||
39 | # define _XOPEN_SOURCE 500 | ||
40 | # include <pthread.h> | ||
41 | # endif | ||
42 | #endif | ||
43 | |||
44 | #include <string.h> | ||
45 | #ifdef HAVE_STRINGS_H | ||
46 | # include <strings.h> | ||
47 | #endif | ||
48 | |||
49 | #include <mailutils/attribute.h> | ||
50 | #include <mailutils/body.h> | ||
51 | #include <mailutils/debug.h> | ||
52 | #include <mailutils/envelope.h> | ||
53 | #include <mailutils/error.h> | ||
54 | #include <mailutils/header.h> | ||
55 | #include <mailutils/locker.h> | ||
56 | #include <mailutils/message.h> | ||
57 | #include <mailutils/mutil.h> | ||
58 | #include <mailutils/property.h> | ||
59 | #include <mailutils/stream.h> | ||
60 | #include <mailutils/url.h> | ||
61 | #include <mailutils/observer.h> | ||
62 | #include <mailbox0.h> | ||
63 | #include <registrar0.h> | ||
64 | |||
65 | #define MAX_OPEN_STREAMS 16 | ||
66 | |||
67 | /* Notifications ADD_MESG. */ | ||
68 | #define DISPATCH_ADD_MSG(mbox,mhd) \ | ||
69 | do \ | ||
70 | { \ | ||
71 | int bailing = 0; \ | ||
72 | monitor_unlock (mbox->monitor); \ | ||
73 | if (mbox->observable) \ | ||
74 | bailing = observable_notify (mbox->observable, MU_EVT_MESSAGE_ADD); \ | ||
75 | if (bailing != 0) \ | ||
76 | { \ | ||
77 | if (pcount) \ | ||
78 | *pcount = (mhd)->msg_count; \ | ||
79 | locker_unlock (mbox->locker); \ | ||
80 | return EINTR; \ | ||
81 | } \ | ||
82 | monitor_wrlock (mbox->monitor); \ | ||
83 | } while (0); | ||
84 | |||
85 | /* Note: In this particular implementation the message sequence number | ||
86 | serves also as its UID. This allows to avoid many problems related | ||
87 | to keeping the uids in the headers of the messages. */ | ||
88 | |||
89 | struct _mh_data; | ||
90 | struct _mh_message | ||
91 | { | ||
92 | struct _mh_message *next; | ||
93 | struct _mh_message *prev; | ||
94 | |||
95 | stream_t stream; /* Associated file stream */ | ||
96 | off_t body_start; /* Offset of body start in the message file */ | ||
97 | off_t body_end; /* Offset of body end (size of file, effectively)*/ | ||
98 | |||
99 | size_t seq_number; /* message sequence number */ | ||
100 | |||
101 | int attr_flags; /* Attribute flags */ | ||
102 | int deleted; /* Was the message originally deleted */ | ||
103 | |||
104 | time_t mtime; /* Time of last modification */ | ||
105 | size_t header_lines; /* Number of lines in the header part */ | ||
106 | size_t body_lines; /* Number of lines in the body */ | ||
107 | |||
108 | message_t message; /* Corresponding message_t */ | ||
109 | struct _mh_data *mhd; /* Back pointer. */ | ||
110 | }; | ||
111 | |||
112 | struct _mh_data | ||
113 | { | ||
114 | /* List of messages: */ | ||
115 | size_t msg_count; /* number of messages in the list */ | ||
116 | struct _mh_message *msg_head; /* First */ | ||
117 | struct _mh_message *msg_tail; /* Last */ | ||
118 | |||
119 | unsigned long uidvalidity; | ||
120 | |||
121 | char *name; /* Directory name */ | ||
122 | |||
123 | /* Pool of open message streams */ | ||
124 | struct _mh_message *msg_pool[MAX_OPEN_STREAMS]; | ||
125 | int pool_first; /* Index to the first used entry in msg_pool */ | ||
126 | int pool_last; /* Index to the first free entry in msg_pool */ | ||
127 | |||
128 | time_t mtime; /* Time of last modification */ | ||
129 | |||
130 | mailbox_t mailbox; /* Back pointer. */ | ||
131 | }; | ||
132 | |||
133 | static void mh_destroy __P((mailbox_t mailbox)); | ||
134 | static int mh_open __P ((mailbox_t, int)); | ||
135 | static int mh_close __P ((mailbox_t)); | ||
136 | static int mh_get_message __P ((mailbox_t, size_t, message_t *)); | ||
137 | static int mh_append_message __P ((mailbox_t, message_t)); | ||
138 | static int mh_messages_count __P ((mailbox_t, size_t *)); | ||
139 | static int mh_messages_recent __P ((mailbox_t, size_t *)); | ||
140 | static int mh_message_unseen __P ((mailbox_t, size_t *)); | ||
141 | static int mh_expunge __P ((mailbox_t)); | ||
142 | static int mh_save_attributes __P ((mailbox_t)); | ||
143 | static int mh_uidvalidity __P ((mailbox_t, unsigned long *)); | ||
144 | static int mh_uidnext __P ((mailbox_t, size_t *)); | ||
145 | static int mh_scan __P ((mailbox_t, size_t, size_t *)); | ||
146 | static int mh_scan0 __P ((mailbox_t mailbox, size_t msgno, size_t *pcount, | ||
147 | int do_notify)); | ||
148 | static int mh_is_updated __P ((mailbox_t)); | ||
149 | static int mh_get_size __P ((mailbox_t, off_t *)); | ||
150 | |||
151 | static int mh_body_read __P ((stream_t, char *, size_t, off_t, size_t *)); | ||
152 | static int mh_body_readline __P ((stream_t, char *, size_t, off_t, size_t *)); | ||
153 | static int mh_stream_size __P ((stream_t stream, off_t *psize)); | ||
154 | |||
155 | static int mh_body_size __P ((body_t body, size_t *psize)); | ||
156 | static int mh_body_lines __P ((body_t body, size_t *plines)); | ||
157 | |||
158 | static int mh_message_uid __P ((message_t msg, size_t *puid)); | ||
159 | |||
160 | static int mh_message_stream_open __P((struct _mh_message *mhm)); | ||
161 | static void mh_message_stream_close __P((struct _mh_message *mhm)); | ||
162 | |||
163 | static int mh_header_fill __P((header_t header, char *buffer, size_t len, | ||
164 | off_t off, size_t *pnread)); | ||
165 | static int mh_header_size __P((header_t header, size_t *psize)); | ||
166 | static int mh_header_lines __P((header_t header, size_t *plines)); | ||
167 | |||
168 | static int mh_get_attr_flags __P((attribute_t attr, int *pflags)); | ||
169 | static int mh_set_attr_flags __P((attribute_t attr, int flags)); | ||
170 | static int mh_unset_attr_flags __P((attribute_t attr, int flags)); | ||
171 | |||
172 | static void _mh_message_insert __P((struct _mh_data *mhd, | ||
173 | struct _mh_message *msg)); | ||
174 | static void _mh_message_delete __P((struct _mh_data *mhd, | ||
175 | struct _mh_message *msg)); | ||
176 | static int mh_pool_open __P((struct _mh_message *mhm)); | ||
177 | |||
178 | static int mh_envelope_date __P((envelope_t envelope, char *buf, size_t len, | ||
179 | size_t *psize)); | ||
180 | static int mh_envelope_sender __P((envelope_t envelope, char *buf, size_t len, | ||
181 | size_t *psize)); | ||
182 | |||
183 | /* Should be in an other header file. */ | ||
184 | extern int mh_message_number __P ((message_t msg, size_t *pnum)); | ||
185 | |||
186 | /* Return filename for the message. | ||
187 | NOTE: Allocates memory. */ | ||
188 | static char * | ||
189 | _mh_message_name (struct _mh_message *mhm, int deleted) | ||
190 | { | ||
191 | char *filename; | ||
192 | size_t len = strlen (mhm->mhd->name) + 32; | ||
193 | filename = malloc (len); | ||
194 | if (deleted) | ||
195 | snprintf (filename, len, "%s/,%lu", mhm->mhd->name, | ||
196 | (unsigned long) mhm->seq_number); | ||
197 | else | ||
198 | snprintf (filename, len, "%s/%lu", mhm->mhd->name, | ||
199 | (unsigned long) mhm->seq_number); | ||
200 | return filename; | ||
201 | } | ||
202 | |||
203 | int | ||
204 | _mailbox_mh_init (mailbox_t mailbox) | ||
205 | { | ||
206 | struct _mh_data *mhd; | ||
207 | size_t name_len; | ||
208 | |||
209 | if (mailbox == NULL) | ||
210 | return EINVAL; | ||
211 | |||
212 | mhd = mailbox->data = calloc (1, sizeof (*mhd)); | ||
213 | if (mailbox->data == NULL) | ||
214 | return ENOMEM; | ||
215 | |||
216 | /* Back pointer. */ | ||
217 | mhd->mailbox = mailbox; | ||
218 | |||
219 | url_get_path (mailbox->url, NULL, 0, &name_len); | ||
220 | mhd->name = calloc (name_len + 1, sizeof (char)); | ||
221 | if (mhd->name == NULL) | ||
222 | { | ||
223 | free (mhd); | ||
224 | mailbox->data = NULL; | ||
225 | return ENOMEM; | ||
226 | } | ||
227 | url_get_path (mailbox->url, mhd->name, name_len + 1, NULL); | ||
228 | |||
229 | /* Overloading the defaults. */ | ||
230 | mailbox->_destroy = mh_destroy; | ||
231 | |||
232 | mailbox->_open = mh_open; | ||
233 | mailbox->_close = mh_close; | ||
234 | |||
235 | /* Overloading of the entire mailbox object methods. */ | ||
236 | mailbox->_get_message = mh_get_message; | ||
237 | mailbox->_append_message = mh_append_message; | ||
238 | mailbox->_messages_count = mh_messages_count; | ||
239 | mailbox->_messages_recent = mh_messages_recent; | ||
240 | mailbox->_message_unseen = mh_message_unseen; | ||
241 | mailbox->_expunge = mh_expunge; | ||
242 | mailbox->_save_attributes = mh_save_attributes; | ||
243 | mailbox->_uidvalidity = mh_uidvalidity; | ||
244 | mailbox->_uidnext = mh_uidnext; | ||
245 | |||
246 | mailbox->_scan = mh_scan; | ||
247 | mailbox->_is_updated = mh_is_updated; | ||
248 | |||
249 | mailbox->_get_size = mh_get_size; | ||
250 | |||
251 | /* Set our properties. */ | ||
252 | { | ||
253 | property_t property = NULL; | ||
254 | mailbox_get_property (mailbox, &property); | ||
255 | property_set_value (property, "TYPE", "MH", 1); | ||
256 | } | ||
257 | |||
258 | MAILBOX_DEBUG1 (mailbox, MU_DEBUG_TRACE, "mh_init(%s)\n", mhd->name); | ||
259 | return 0; | ||
260 | } | ||
261 | |||
262 | static void | ||
263 | mh_destroy (mailbox_t mailbox) | ||
264 | { | ||
265 | struct _mh_data *mhd = mailbox->data; | ||
266 | struct _mh_message *msg, *next; | ||
267 | |||
268 | if (!mhd) | ||
269 | return; | ||
270 | |||
271 | monitor_wrlock (mailbox->monitor); | ||
272 | msg = mhd->msg_head; | ||
273 | while (msg) | ||
274 | { | ||
275 | next = msg->next; | ||
276 | message_destroy (&msg->message, msg); | ||
277 | free (msg); | ||
278 | msg = next; | ||
279 | } | ||
280 | |||
281 | if (mhd->name) | ||
282 | free (mhd->name); | ||
283 | |||
284 | free (mhd); | ||
285 | mailbox->data = NULL; | ||
286 | monitor_unlock (mailbox->monitor); | ||
287 | } | ||
288 | |||
289 | static int | ||
290 | mh_open (mailbox_t mailbox, int flags) | ||
291 | { | ||
292 | struct _mh_data *mhd = mailbox->data; | ||
293 | int status = 0; | ||
294 | struct stat st; | ||
295 | |||
296 | mailbox->flags = flags; | ||
297 | if (stat (mhd->name, &st) < 0) | ||
298 | return errno; | ||
299 | |||
300 | if (!S_ISDIR (st.st_mode)) | ||
301 | return EINVAL; | ||
302 | |||
303 | mhd->mtime = st.st_mtime; | ||
304 | |||
305 | /* FIXME: is this the right kind of locking for mh folders? */ | ||
306 | if (mailbox->locker == NULL) | ||
307 | status = locker_create (&mailbox->locker, mhd->name, 0); | ||
308 | return 0; | ||
309 | } | ||
310 | |||
311 | static int | ||
312 | mh_close (mailbox_t mailbox) | ||
313 | { | ||
314 | if (!mailbox) | ||
315 | return EINVAL; | ||
316 | return locker_unlock (mailbox->locker); | ||
317 | } | ||
318 | |||
319 | static struct _mh_message * | ||
320 | _mh_get_message (struct _mh_data *mhd, size_t msgno) | ||
321 | { | ||
322 | size_t n; | ||
323 | struct _mh_message *msg; | ||
324 | |||
325 | for (n = 1, msg = mhd->msg_head; msg && n < msgno; n++, msg = msg->next) | ||
326 | ; | ||
327 | |||
328 | return msg; | ||
329 | } | ||
330 | |||
331 | /* Find the message with the given sequence number */ | ||
332 | static struct _mh_message * | ||
333 | _mh_get_message_seq (struct _mh_data *mhd, size_t seq) | ||
334 | { | ||
335 | struct _mh_message *msg; | ||
336 | |||
337 | for (msg = mhd->msg_head; msg && msg->seq_number < seq; msg = msg->next) | ||
338 | ; | ||
339 | |||
340 | if (msg) | ||
341 | return msg->seq_number == seq ? msg : NULL; | ||
342 | return NULL; | ||
343 | } | ||
344 | |||
345 | static int | ||
346 | _mh_attach_message (mailbox_t mailbox, struct _mh_message *mhm, | ||
347 | message_t *pmsg) | ||
348 | { | ||
349 | int status; | ||
350 | message_t msg; | ||
351 | |||
352 | /* Check if we already have it. */ | ||
353 | if (mhm->message) | ||
354 | { | ||
355 | if (pmsg) | ||
356 | *pmsg = mhm->message; | ||
357 | return 0; | ||
358 | } | ||
359 | |||
360 | /* Get an empty message struct. */ | ||
361 | status = message_create (&msg, mhm); | ||
362 | if (status != 0) | ||
363 | return status; | ||
364 | |||
365 | /* Set the header. */ | ||
366 | { | ||
367 | header_t header = NULL; | ||
368 | status = header_create (&header, NULL, 0, msg); | ||
369 | if (status != 0) | ||
370 | { | ||
371 | message_destroy (&msg, mhm); | ||
372 | return status; | ||
373 | } | ||
374 | header_set_fill (header, mh_header_fill, msg); | ||
375 | header_set_size (header, mh_header_size, msg); | ||
376 | header_set_lines (header, mh_header_lines, msg); | ||
377 | /*FIXME: | ||
378 | header_set_get_fvalue (header, mh_header_get_fvalue, msg); | ||
379 | */ | ||
380 | message_set_header (msg, header, mhm); | ||
381 | } | ||
382 | |||
383 | /* Set the attribute. */ | ||
384 | { | ||
385 | attribute_t attribute; | ||
386 | status = attribute_create (&attribute, msg); | ||
387 | if (status != 0) | ||
388 | { | ||
389 | message_destroy (&msg, mhm); | ||
390 | return status; | ||
391 | } | ||
392 | attribute_set_get_flags (attribute, mh_get_attr_flags, msg); | ||
393 | attribute_set_set_flags (attribute, mh_set_attr_flags, msg); | ||
394 | attribute_set_unset_flags (attribute, mh_unset_attr_flags, msg); | ||
395 | message_set_attribute (msg, attribute, mhm); | ||
396 | } | ||
397 | |||
398 | /* Prepare the body. */ | ||
399 | { | ||
400 | body_t body = NULL; | ||
401 | stream_t stream = NULL; | ||
402 | if ((status = body_create (&body, msg)) != 0 | ||
403 | || (status = stream_create (&stream, | ||
404 | mailbox->flags | MU_STREAM_SEEKABLE, | ||
405 | body)) != 0) | ||
406 | { | ||
407 | body_destroy (&body, msg); | ||
408 | stream_destroy (&stream, body); | ||
409 | message_destroy (&msg, mhm); | ||
410 | return status; | ||
411 | } | ||
412 | stream_set_read (stream, mh_body_read, body); | ||
413 | stream_set_readline (stream, mh_body_readline, body); | ||
414 | stream_set_size (stream, mh_stream_size, body); | ||
415 | body_set_stream (body, stream, msg); | ||
416 | body_set_size (body, mh_body_size, msg); | ||
417 | body_set_lines (body, mh_body_lines, msg); | ||
418 | message_set_body (msg, body, mhm); | ||
419 | } | ||
420 | |||
421 | /* Set the envelope. */ | ||
422 | { | ||
423 | envelope_t envelope = NULL; | ||
424 | status = envelope_create (&envelope, msg); | ||
425 | if (status != 0) | ||
426 | { | ||
427 | message_destroy (&msg, mhm); | ||
428 | return status; | ||
429 | } | ||
430 | envelope_set_sender (envelope, mh_envelope_sender, msg); | ||
431 | envelope_set_date (envelope, mh_envelope_date, msg); | ||
432 | message_set_envelope (msg, envelope, mhm); | ||
433 | } | ||
434 | |||
435 | /* Set the UID. */ | ||
436 | message_set_uid (msg, mh_message_uid, mhm); | ||
437 | |||
438 | /* Attach the message to the mailbox mbox data. */ | ||
439 | mhm->message = msg; | ||
440 | message_set_mailbox (msg, mailbox, mhm); | ||
441 | |||
442 | if (pmsg) | ||
443 | *pmsg = msg; | ||
444 | |||
445 | return 0; | ||
446 | } | ||
447 | |||
448 | static int | ||
449 | mh_get_message (mailbox_t mailbox, size_t msgno, message_t *pmsg) | ||
450 | { | ||
451 | int status; | ||
452 | struct _mh_data *mhd = mailbox->data; | ||
453 | struct _mh_message *mhm; | ||
454 | |||
455 | /* Sanity checks. */ | ||
456 | if (pmsg == NULL || mhd == NULL) | ||
457 | return EINVAL; | ||
458 | |||
459 | /* If we did not start a scanning yet do it now. */ | ||
460 | if (mhd->msg_count == 0) | ||
461 | { | ||
462 | status = mh_scan0 (mailbox, 1, NULL, 0); | ||
463 | if (status != 0) | ||
464 | return status; | ||
465 | } | ||
466 | |||
467 | if ((mhm = _mh_get_message (mhd, msgno)) == NULL) | ||
468 | return EINVAL; | ||
469 | return _mh_attach_message (mailbox, mhm, pmsg); | ||
470 | } | ||
471 | |||
472 | static size_t | ||
473 | _mh_next_seq (struct _mh_data *mhd) | ||
474 | { | ||
475 | return (mhd->msg_tail ? mhd->msg_tail->seq_number : 0) + 1; | ||
476 | } | ||
477 | |||
478 | static FILE * | ||
479 | _mh_tempfile(struct _mh_data *mhd, char **namep) | ||
480 | { | ||
481 | int fd = mu_tempfile (mhd->name, namep); | ||
482 | if (fd == -1) | ||
483 | return NULL; | ||
484 | return fdopen (fd, "w"); | ||
485 | } | ||
486 | |||
487 | static int | ||
488 | _mh_delim (char *str) | ||
489 | { | ||
490 | if (str[0] == '-') | ||
491 | { | ||
492 | for (; *str == '-'; str++) | ||
493 | ; | ||
494 | for (; *str == ' ' || *str == '\t'; str++) | ||
495 | ; | ||
496 | } | ||
497 | return str[0] == '\n'; | ||
498 | } | ||
499 | |||
500 | static int | ||
501 | _mh_message_save (struct _mh_data *mhd, struct _mh_message *mhm, int expunge) | ||
502 | { | ||
503 | stream_t stream = NULL; | ||
504 | char *name = NULL, *buf = NULL, *msg_name; | ||
505 | size_t n, off = 0; | ||
506 | size_t bsize; | ||
507 | size_t nlines, nbytes; | ||
508 | size_t new_body_start, new_header_lines; | ||
509 | FILE *fp; | ||
510 | message_t msg = mhm->message; | ||
511 | header_t hdr; | ||
512 | int status; | ||
513 | attribute_t attr; | ||
514 | body_t body; | ||
515 | char buffer[512]; | ||
516 | envelope_t env = NULL; | ||
517 | |||
518 | fp = _mh_tempfile (mhm->mhd, &name); | ||
519 | if (!fp) | ||
520 | return errno; | ||
521 | |||
522 | message_size (msg, &bsize); | ||
523 | |||
524 | /* Try to allocate large buffer */ | ||
525 | for (; bsize > 1; bsize /= 2) | ||
526 | if ((buf = malloc (bsize))) | ||
527 | break; | ||
528 | |||
529 | if (!bsize) | ||
530 | return ENOMEM; | ||
531 | |||
532 | /* Copy flags */ | ||
533 | message_get_header (msg, &hdr); | ||
534 | header_get_stream (hdr, &stream); | ||
535 | off = 0; | ||
536 | nlines = nbytes = 0; | ||
537 | while ((status = stream_readline (stream, buf, bsize, off, &n)) == 0 | ||
538 | && n != 0) | ||
539 | { | ||
540 | if (_mh_delim(buf)) | ||
541 | break; | ||
542 | |||
543 | if (!(strncasecmp (buf, "status:", 7) == 0 | ||
544 | || strncasecmp (buf, "x-imapbase:", 11) == 0 | ||
545 | || strncasecmp (buf, "x-uid:", 6) == 0 | ||
546 | || strncasecmp (buf, MU_HEADER_ENV_DATE ":", sizeof (MU_HEADER_ENV_DATE)) == 0 | ||
547 | || strncasecmp (buf, MU_HEADER_ENV_SENDER ":", sizeof (MU_HEADER_ENV_SENDER)) == 0)) | ||
548 | { | ||
549 | nlines++; | ||
550 | nbytes += fprintf (fp, "%s", buf); | ||
551 | } | ||
552 | |||
553 | off += n; | ||
554 | } | ||
555 | |||
556 | /* Add imapbase */ | ||
557 | if (!mhd->msg_head || (mhd->msg_head == mhm)) /*FIXME*/ | ||
558 | { | ||
559 | nbytes += fprintf (fp, "X-IMAPbase: %lu %u\n", | ||
560 | (unsigned long) mhd->uidvalidity, | ||
561 | (unsigned) _mh_next_seq(mhd)); | ||
562 | nlines++; | ||
563 | } | ||
564 | |||
565 | message_get_envelope (msg, &env); | ||
566 | if (envelope_date (env, buffer, sizeof buffer, &n) == 0 && n > 0) | ||
567 | { | ||
568 | /* NOTE: buffer is terminated with \n */ | ||
569 | char *p = buffer; | ||
570 | while (isspace (*p)) | ||
571 | p++; | ||
572 | nbytes += fprintf (fp, "%s: %s", MU_HEADER_ENV_DATE, p); | ||
573 | |||
574 | if (*p && p[strlen (p) - 1] != '\n') | ||
575 | nbytes += fprintf (fp, "\n"); | ||
576 | |||
577 | nlines++; | ||
578 | } | ||
579 | |||
580 | if (envelope_sender (env, buffer, sizeof buffer, &n) == 0 && n > 0) | ||
581 | { | ||
582 | fprintf (fp, "%s: %s\n", MU_HEADER_ENV_SENDER, buffer); | ||
583 | nlines++; | ||
584 | } | ||
585 | |||
586 | /* Add status */ | ||
587 | message_get_attribute (msg, &attr); | ||
588 | attribute_to_string (attr, buf, bsize, &n); | ||
589 | if (n) | ||
590 | { | ||
591 | nbytes += fprintf (fp, "%s", buf); | ||
592 | nlines++; | ||
593 | } | ||
594 | nbytes += fprintf (fp, "\n"); | ||
595 | nlines++; | ||
596 | |||
597 | new_header_lines = nlines; | ||
598 | new_body_start = nbytes; | ||
599 | |||
600 | /* Copy message body */ | ||
601 | |||
602 | message_get_body (msg, &body); | ||
603 | body_get_stream (body, &stream); | ||
604 | off = 0; | ||
605 | nlines = 0; | ||
606 | while (stream_read (stream, buf, bsize, off, &n) == 0 && n != 0) | ||
607 | { | ||
608 | char *p; | ||
609 | for (p = buf; p < buf + n; p++) | ||
610 | if (*p == '\n') | ||
611 | nlines++; | ||
612 | fwrite (buf, 1, n, fp); | ||
613 | off += n; | ||
614 | nbytes += n; | ||
615 | } | ||
616 | |||
617 | mhm->header_lines = new_header_lines; | ||
618 | mhm->body_start = new_body_start; | ||
619 | mhm->body_lines = nlines; | ||
620 | mhm->body_end = nbytes; | ||
621 | |||
622 | free (buf); | ||
623 | fclose (fp); | ||
624 | |||
625 | msg_name = _mh_message_name (mhm, mhm->deleted); | ||
626 | rename (name, msg_name); | ||
627 | free (name); | ||
628 | free (msg_name); | ||
629 | |||
630 | return 0; | ||
631 | } | ||
632 | |||
633 | static int | ||
634 | mh_append_message (mailbox_t mailbox, message_t msg) | ||
635 | { | ||
636 | int status; | ||
637 | struct _mh_data *mhd = mailbox->data; | ||
638 | struct _mh_message *mhm; | ||
639 | |||
640 | if (!mailbox || !msg) | ||
641 | return EINVAL; | ||
642 | |||
643 | mhm = calloc (1, sizeof(*mhm)); | ||
644 | if (!mhm) | ||
645 | return ENOMEM; | ||
646 | |||
647 | /* If we did not start a scanning yet do it now. */ | ||
648 | if (mhd->msg_count == 0) | ||
649 | { | ||
650 | status = mh_scan0 (mailbox, 1, NULL, 0); | ||
651 | if (status != 0) | ||
652 | return status; | ||
653 | } | ||
654 | |||
655 | mhm->mhd = mhd; | ||
656 | mhm->seq_number = _mh_next_seq (mhd); | ||
657 | mhm->message = msg; | ||
658 | status = _mh_message_save (mhd, mhm, 0); | ||
659 | mhm->message = NULL; | ||
660 | /* Insert and re-scan the message */ | ||
661 | _mh_message_insert (mhd, mhm); | ||
662 | return status; | ||
663 | } | ||
664 | |||
665 | static int | ||
666 | mh_messages_count (mailbox_t mailbox, size_t *pcount) | ||
667 | { | ||
668 | struct _mh_data *mhd = mailbox->data; | ||
669 | |||
670 | if (mhd == NULL) | ||
671 | return EINVAL; | ||
672 | |||
673 | if (!mh_is_updated (mailbox)) | ||
674 | return mh_scan0 (mailbox, mhd->msg_count, pcount, 0); | ||
675 | |||
676 | if (pcount) | ||
677 | *pcount = mhd->msg_count; | ||
678 | |||
679 | return 0; | ||
680 | } | ||
681 | |||
682 | /* A "recent" message is the one not marked with MU_ATTRIBUTE_SEEN | ||
683 | ('O' in the Status header), i.e. a message that is first seen | ||
684 | by the current session (see attributes.h) */ | ||
685 | static int | ||
686 | mh_messages_recent (mailbox_t mailbox, size_t *pcount) | ||
687 | { | ||
688 | struct _mh_data *mhd = mailbox->data; | ||
689 | struct _mh_message *mhm; | ||
690 | size_t count; | ||
691 | |||
692 | /* If we did not start a scanning yet do it now. */ | ||
693 | if (mhd->msg_count == 0) | ||
694 | { | ||
695 | int status = mh_scan0 (mailbox, 1, NULL, 0); | ||
696 | if (status != 0) | ||
697 | return status; | ||
698 | } | ||
699 | count = 0; | ||
700 | for (mhm = mhd->msg_head; mhm; mhm = mhm->next) | ||
701 | { | ||
702 | if (MU_ATTRIBUTE_IS_UNSEEN(mhm->attr_flags)) | ||
703 | count++; | ||
704 | } | ||
705 | *pcount = count; | ||
706 | return 0; | ||
707 | } | ||
708 | |||
709 | /* An "unseen" message is the one that has not been read yet */ | ||
710 | static int | ||
711 | mh_message_unseen (mailbox_t mailbox, size_t *pmsgno) | ||
712 | { | ||
713 | struct _mh_data *mhd = mailbox->data; | ||
714 | struct _mh_message *mhm; | ||
715 | size_t i, unseen; | ||
716 | |||
717 | /* If we did not start a scanning yet do it now. */ | ||
718 | if (mhd->msg_count == 0) | ||
719 | { | ||
720 | int status = mh_scan0 (mailbox, 1, NULL, 0); | ||
721 | if (status != 0) | ||
722 | return status; | ||
723 | } | ||
724 | for (unseen = i = 1, mhm = mhd->msg_head; mhm; i++, mhm = mhm->next) | ||
725 | { | ||
726 | if (MU_ATTRIBUTE_IS_UNREAD(mhm->attr_flags)) | ||
727 | { | ||
728 | unseen = i; | ||
729 | break; | ||
730 | } | ||
731 | } | ||
732 | *pmsgno = unseen; | ||
733 | return 0; | ||
734 | } | ||
735 | |||
736 | static int | ||
737 | mh_expunge (mailbox_t mailbox) | ||
738 | { | ||
739 | struct _mh_data *mhd = mailbox->data; | ||
740 | struct _mh_message *mhm; | ||
741 | |||
742 | if (mhd == NULL) | ||
743 | return EINVAL; | ||
744 | |||
745 | if (mhd->msg_count == 0) | ||
746 | return 0; | ||
747 | |||
748 | /* Find the first dirty(modified) message. */ | ||
749 | for (mhm = mhd->msg_head; mhm; mhm = mhm->next) | ||
750 | { | ||
751 | if ((mhm->attr_flags & MU_ATTRIBUTE_MODIFIED) || | ||
752 | (mhm->attr_flags & MU_ATTRIBUTE_DELETED) || | ||
753 | (mhm->message && message_is_modified (mhm->message))) | ||
754 | break; | ||
755 | } | ||
756 | |||
757 | if (!mhm) | ||
758 | return 0; /* Nothing changed, just return. */ | ||
759 | |||
760 | while (mhm) | ||
761 | { | ||
762 | struct _mh_message *next = mhm->next; | ||
763 | |||
764 | if (mhm->attr_flags & MU_ATTRIBUTE_DELETED) | ||
765 | { | ||
766 | if (!mhm->deleted) | ||
767 | { | ||
768 | char *old_name, *new_name; | ||
769 | /* Rename original message */ | ||
770 | old_name = _mh_message_name (mhm, 0); | ||
771 | new_name = _mh_message_name (mhm, 1); | ||
772 | rename (old_name, new_name); | ||
773 | free (old_name); | ||
774 | free (new_name); | ||
775 | } | ||
776 | _mh_message_delete (mhd, mhm); | ||
777 | } | ||
778 | else if ((mhm->attr_flags & MU_ATTRIBUTE_MODIFIED) | ||
779 | || (mhm->message && message_is_modified (mhm->message))) | ||
780 | { | ||
781 | _mh_attach_message (mailbox, mhm, NULL); | ||
782 | mhm->deleted = mhm->attr_flags & MU_ATTRIBUTE_DELETED; | ||
783 | _mh_message_save (mhd, mhm, 1); | ||
784 | } | ||
785 | mhm = next; | ||
786 | } | ||
787 | |||
788 | return 0; | ||
789 | } | ||
790 | |||
791 | static int | ||
792 | mh_save_attributes (mailbox_t mailbox) | ||
793 | { | ||
794 | struct _mh_data *mhd = mailbox->data; | ||
795 | struct _mh_message *mhm; | ||
796 | |||
797 | if (mhd == NULL) | ||
798 | return EINVAL; | ||
799 | |||
800 | if (mhd->msg_count == 0) | ||
801 | return 0; | ||
802 | |||
803 | /* Find the first dirty(modified) message. */ | ||
804 | for (mhm = mhd->msg_head; mhm; mhm = mhm->next) | ||
805 | { | ||
806 | if ((mhm->attr_flags & MU_ATTRIBUTE_MODIFIED) | ||
807 | || (mhm->message && message_is_modified (mhm->message))) | ||
808 | break; | ||
809 | } | ||
810 | |||
811 | if (!mhm) | ||
812 | return 0; /* Nothing changed, just return. */ | ||
813 | |||
814 | while (mhm) | ||
815 | { | ||
816 | struct _mh_message *next = mhm->next; | ||
817 | |||
818 | if ((mhm->attr_flags & MU_ATTRIBUTE_MODIFIED) | ||
819 | || (mhm->message && message_is_modified (mhm->message))) | ||
820 | { | ||
821 | _mh_attach_message (mailbox, mhm, NULL); | ||
822 | mhm->deleted = mhm->attr_flags & MU_ATTRIBUTE_DELETED; | ||
823 | _mh_message_save (mhd, mhm, 0); | ||
824 | } | ||
825 | mhm = next; | ||
826 | } | ||
827 | |||
828 | return 0; | ||
829 | } | ||
830 | |||
831 | static int | ||
832 | mh_uidvalidity (mailbox_t mailbox, unsigned long *puidvalidity) | ||
833 | { | ||
834 | struct _mh_data *mhd = mailbox->data; | ||
835 | int status = mh_messages_count (mailbox, NULL); | ||
836 | if (status != 0) | ||
837 | return status; | ||
838 | /* If we did not start a scanning yet do it now. */ | ||
839 | if (mhd->msg_count == 0) | ||
840 | { | ||
841 | status = mh_scan0 (mailbox, 1, NULL, 0); | ||
842 | if (status != 0) | ||
843 | return status; | ||
844 | } | ||
845 | if (puidvalidity) | ||
846 | *puidvalidity = mhd->uidvalidity; | ||
847 | return 0; | ||
848 | } | ||
849 | |||
850 | static int | ||
851 | mh_uidnext (mailbox_t mailbox, size_t *puidnext) | ||
852 | { | ||
853 | struct _mh_data *mhd = mailbox->data; | ||
854 | int status = mh_messages_count (mailbox, NULL); | ||
855 | if (status != 0) | ||
856 | return status; | ||
857 | /* If we did not start a scanning yet do it now. */ | ||
858 | if (mhd->msg_count == 0) | ||
859 | { | ||
860 | status = mh_scan0 (mailbox, 1, NULL, 0); | ||
861 | if (status != 0) | ||
862 | return status; | ||
863 | } | ||
864 | if (puidnext) | ||
865 | *puidnext = _mh_next_seq(mhd); | ||
866 | return 0; | ||
867 | } | ||
868 | |||
869 | /* FIXME: effectively the same as mbox_cleanup */ | ||
870 | static void | ||
871 | mh_cleanup (void *arg) | ||
872 | { | ||
873 | mailbox_t mailbox = arg; | ||
874 | monitor_unlock (mailbox->monitor); | ||
875 | locker_unlock (mailbox->locker); | ||
876 | } | ||
877 | |||
878 | /* Insert message msg into the message list on the appropriate position */ | ||
879 | static void | ||
880 | _mh_message_insert (struct _mh_data *mhd, struct _mh_message *msg) | ||
881 | { | ||
882 | struct _mh_message *p; | ||
883 | struct _mh_message *prev; | ||
884 | size_t n = msg->seq_number; | ||
885 | |||
886 | for (p = mhd->msg_head; p && p->seq_number < n; p = p->next) | ||
887 | ; | ||
888 | |||
889 | if (!p) | ||
890 | { | ||
891 | msg->next = NULL; | ||
892 | msg->prev = mhd->msg_tail; | ||
893 | mhd->msg_tail = msg; | ||
894 | if (!mhd->msg_head) | ||
895 | mhd->msg_head = msg; | ||
896 | } | ||
897 | else | ||
898 | { | ||
899 | msg->next = p; | ||
900 | msg->prev = p->prev; | ||
901 | p->prev = msg; | ||
902 | } | ||
903 | if ((prev = msg->prev) != NULL) | ||
904 | prev->next = msg; | ||
905 | else | ||
906 | mhd->msg_head = msg; | ||
907 | msg->mhd = mhd; | ||
908 | mhd->msg_count++; | ||
909 | } | ||
910 | |||
911 | static void | ||
912 | _mh_message_delete (struct _mh_data *mhd, struct _mh_message *msg) | ||
913 | { | ||
914 | struct _mh_message *p; | ||
915 | |||
916 | if ((p = msg->next) != NULL) | ||
917 | p->prev = msg->prev; | ||
918 | else | ||
919 | mhd->msg_tail = msg->prev; | ||
920 | |||
921 | if ((p = msg->prev) != NULL) | ||
922 | p->next = msg->next; | ||
923 | else | ||
924 | mhd->msg_head = msg->next; | ||
925 | |||
926 | message_destroy (&msg->message, msg); | ||
927 | free (msg); | ||
928 | mhd->msg_count--; | ||
929 | } | ||
930 | |||
931 | /* Scan given message and fill mh_message_t fields. | ||
932 | NOTE: the function assumes mhm->stream != NULL. */ | ||
933 | static int | ||
934 | mh_scan_message (struct _mh_message *mhm) | ||
935 | { | ||
936 | stream_t stream = mhm->stream; | ||
937 | char buf[1024]; | ||
938 | off_t off = 0; | ||
939 | size_t n; | ||
940 | int status; | ||
941 | int in_header = 1; | ||
942 | size_t hlines = 0; | ||
943 | size_t blines = 0; | ||
944 | size_t body_start = 0; | ||
945 | |||
946 | /* Check if the message was modified after the last scan */ | ||
947 | if (mhm->mtime) | ||
948 | { | ||
949 | struct stat st; | ||
950 | char *msg_name = _mh_message_name (mhm, mhm->deleted); | ||
951 | |||
952 | if (stat (msg_name, &st) == 0 && st.st_mtime == mhm->mtime) | ||
953 | { | ||
954 | /* Nothing to do */ | ||
955 | free (msg_name); | ||
956 | return 0; | ||
957 | } | ||
958 | free (msg_name); | ||
959 | } | ||
960 | |||
961 | while ((status = stream_readline (stream, buf, sizeof (buf), off, &n) == 0) | ||
962 | && n != 0) | ||
963 | { | ||
964 | if (in_header) | ||
965 | { | ||
966 | if (buf[0] == '\n') | ||
967 | { | ||
968 | in_header = 0; | ||
969 | body_start = off+1; | ||
970 | } | ||
971 | if (buf[n - 1] == '\n') | ||
972 | hlines++; | ||
973 | |||
974 | /* Process particular attributes */ | ||
975 | if (strncasecmp (buf, "status:", 7) == 0) | ||
976 | { | ||
977 | int deleted = mhm->attr_flags & MU_ATTRIBUTE_DELETED; | ||
978 | string_to_flags (buf, &mhm->attr_flags); | ||
979 | mhm->attr_flags |= deleted; | ||
980 | } | ||
981 | else if (strncasecmp (buf, "x-imapbase:", 11) == 0) | ||
982 | { | ||
983 | char *p; | ||
984 | mhm->mhd->uidvalidity = strtoul (buf + 11, &p, 10); | ||
985 | /* second number is next uid. Ignored */ | ||
986 | } | ||
987 | } | ||
988 | else | ||
989 | { | ||
990 | if (buf[n - 1] == '\n') | ||
991 | blines++; | ||
992 | } | ||
993 | off += n; | ||
994 | } | ||
995 | |||
996 | if (!body_start) | ||
997 | body_start = off; | ||
998 | mhm->header_lines = hlines; | ||
999 | mhm->body_lines = blines; | ||
1000 | mhm->body_start = body_start; | ||
1001 | mhm->body_end = off; | ||
1002 | return 0; | ||
1003 | } | ||
1004 | |||
1005 | /* Scan the mailbox */ | ||
1006 | static int | ||
1007 | mh_scan0 (mailbox_t mailbox, size_t msgno, size_t *pcount, int do_notify) | ||
1008 | { | ||
1009 | struct _mh_data *mhd = mailbox->data; | ||
1010 | struct _mh_message *msg; | ||
1011 | DIR *dir; | ||
1012 | struct dirent *entry; | ||
1013 | int status = 0; | ||
1014 | struct stat st; | ||
1015 | (void)msgno; | ||
1016 | |||
1017 | if (mhd == NULL) | ||
1018 | return EINVAL; | ||
1019 | |||
1020 | dir = opendir (mhd->name); | ||
1021 | if (!dir) | ||
1022 | return errno; | ||
1023 | |||
1024 | monitor_wrlock (mailbox->monitor); | ||
1025 | |||
1026 | #ifdef WITH_PTHREAD | ||
1027 | pthread_cleanup_push (mh_cleanup, (void *)mailbox); | ||
1028 | #endif | ||
1029 | |||
1030 | locker_lock (mailbox->locker); | ||
1031 | |||
1032 | /* Do actual work. */ | ||
1033 | |||
1034 | while ((entry = readdir (dir))) | ||
1035 | { | ||
1036 | char *namep; | ||
1037 | int attr_flags; | ||
1038 | size_t num; | ||
1039 | |||
1040 | attr_flags = 0; | ||
1041 | switch (entry->d_name[0]) | ||
1042 | { | ||
1043 | case '.': | ||
1044 | /* FIXME: .mh_sequences */ | ||
1045 | continue; | ||
1046 | case ',': | ||
1047 | continue; | ||
1048 | #if 0 | ||
1049 | attr_flags |= MU_ATTRIBUTE_DELETED; | ||
1050 | namep = entry->d_name+1; | ||
1051 | break; | ||
1052 | #endif | ||
1053 | case '0':case '1':case '2':case '3':case '4': | ||
1054 | case '5':case '6':case '7':case '8':case '9': | ||
1055 | namep = entry->d_name; | ||
1056 | break; | ||
1057 | default: | ||
1058 | /*FIXME: Invalid entry. Report? */ | ||
1059 | continue; | ||
1060 | } | ||
1061 | |||
1062 | num = strtoul (namep, &namep, 10); | ||
1063 | if (namep[0]) | ||
1064 | continue; | ||
1065 | |||
1066 | msg = _mh_get_message_seq (mhd, num); | ||
1067 | if (!msg) | ||
1068 | { | ||
1069 | msg = calloc (1, sizeof(*msg)); | ||
1070 | |||
1071 | msg->seq_number = num; | ||
1072 | msg->attr_flags = attr_flags; | ||
1073 | msg->deleted = attr_flags & MU_ATTRIBUTE_DELETED; | ||
1074 | |||
1075 | _mh_message_insert (mhd, msg); | ||
1076 | |||
1077 | } | ||
1078 | else | ||
1079 | { | ||
1080 | msg->attr_flags = attr_flags; | ||
1081 | } | ||
1082 | } | ||
1083 | |||
1084 | closedir (dir); | ||
1085 | |||
1086 | if (do_notify) | ||
1087 | for (msg = mhd->msg_head; msg; msg = msg->next) | ||
1088 | { | ||
1089 | DISPATCH_ADD_MSG(mailbox, mhd); | ||
1090 | } | ||
1091 | |||
1092 | if (stat (mhd->name, &st) == 0) | ||
1093 | mhd->mtime = st.st_mtime; | ||
1094 | |||
1095 | if (pcount) | ||
1096 | *pcount = mhd->msg_count; | ||
1097 | |||
1098 | /* Reset the uidvalidity. */ | ||
1099 | if (mhd->msg_count > 0) | ||
1100 | { | ||
1101 | if (mhd->uidvalidity == 0) | ||
1102 | { | ||
1103 | mhd->uidvalidity = (unsigned long)time (NULL); | ||
1104 | /* FIXME mhd->uidnext = mhd->msg_count + 1;*/ | ||
1105 | /* Tell that we have been modified for expunging. */ | ||
1106 | if (mhd->msg_head) | ||
1107 | { | ||
1108 | mh_message_stream_open (mhd->msg_head); | ||
1109 | mh_message_stream_close (mhd->msg_head); | ||
1110 | mhd->msg_head->attr_flags |= MU_ATTRIBUTE_MODIFIED; | ||
1111 | } | ||
1112 | } | ||
1113 | } | ||
1114 | |||
1115 | /* Clean up the things */ | ||
1116 | |||
1117 | mh_cleanup (mailbox); | ||
1118 | #ifdef WITH_PTHREAD | ||
1119 | pthread_cleanup_pop (0); | ||
1120 | #endif | ||
1121 | return status; | ||
1122 | } | ||
1123 | |||
1124 | |||
1125 | static int | ||
1126 | mh_scan (mailbox_t mailbox, size_t msgno, size_t *pcount) | ||
1127 | { | ||
1128 | struct _mh_data *mhd = mailbox->data; | ||
1129 | |||
1130 | if (! mh_is_updated (mailbox)) | ||
1131 | return mh_scan0 (mailbox, msgno, pcount, 1); | ||
1132 | |||
1133 | if (pcount) | ||
1134 | *pcount = mhd->msg_count; | ||
1135 | |||
1136 | return 0; | ||
1137 | } | ||
1138 | |||
1139 | /* Is the internal representation of the mailbox up to date. | ||
1140 | Return 1 if so, 0 otherwise. */ | ||
1141 | static int | ||
1142 | mh_is_updated (mailbox_t mailbox) | ||
1143 | { | ||
1144 | struct stat st; | ||
1145 | struct _mh_data *mhd = mailbox->data; | ||
1146 | |||
1147 | if (!mhd->msg_head) | ||
1148 | return 0; | ||
1149 | |||
1150 | if (stat (mhd->name, &st) < 0) | ||
1151 | return 1; | ||
1152 | |||
1153 | return mhd->mtime == st.st_mtime; | ||
1154 | } | ||
1155 | |||
1156 | static int | ||
1157 | mh_get_size (mailbox_t mailbox, off_t *psize) | ||
1158 | { | ||
1159 | /*FIXME*/ | ||
1160 | (void)mailbox; | ||
1161 | (void)psize; | ||
1162 | return ENOSYS; | ||
1163 | } | ||
1164 | |||
1165 | /* Return number of open streams residing in a message pool */ | ||
1166 | static int | ||
1167 | mh_pool_open_count(struct _mh_data *mhd) | ||
1168 | { | ||
1169 | int cnt = mhd->pool_last - mhd->pool_first; | ||
1170 | if (cnt < 0) | ||
1171 | cnt += MAX_OPEN_STREAMS; | ||
1172 | return cnt; | ||
1173 | } | ||
1174 | |||
1175 | /* Look up a _mh_message in the pool of open messages. | ||
1176 | Returns 1 if the message is found in the pool, and 0 otherwise. */ | ||
1177 | static int | ||
1178 | mh_pool_lookup (struct _mh_message *mhm) | ||
1179 | { | ||
1180 | struct _mh_data *mhd = mhm->mhd; | ||
1181 | int i; | ||
1182 | |||
1183 | for (i = mhd->pool_first; i != mhd->pool_last; ) | ||
1184 | { | ||
1185 | if (mhd->msg_pool[i] == mhm) | ||
1186 | return 1; | ||
1187 | if (++i == MAX_OPEN_STREAMS) | ||
1188 | i = 0; | ||
1189 | } | ||
1190 | return 0; | ||
1191 | } | ||
1192 | |||
1193 | /* Open a stream associated with the message mhm. If the stream is | ||
1194 | already open, do nothing */ | ||
1195 | static int | ||
1196 | mh_pool_open (struct _mh_message *mhm) | ||
1197 | { | ||
1198 | struct _mh_data *mhd = mhm->mhd; | ||
1199 | if (mh_pool_lookup (mhm)) | ||
1200 | return 0; | ||
1201 | if (mh_pool_open_count(mhd) == MAX_OPEN_STREAMS-1) | ||
1202 | { | ||
1203 | mh_message_stream_close (mhd->msg_pool[mhd->pool_first++]); | ||
1204 | mhd->pool_first %= MAX_OPEN_STREAMS; | ||
1205 | } | ||
1206 | mh_message_stream_open (mhm); | ||
1207 | mhd->msg_pool[mhd->pool_last++] = mhm; | ||
1208 | mhd->pool_last %= MAX_OPEN_STREAMS; | ||
1209 | return 0; | ||
1210 | } | ||
1211 | |||
1212 | /* Attach a stream to a given message structure. The latter is supposed | ||
1213 | to be already added to the open message pool. */ | ||
1214 | int | ||
1215 | mh_message_stream_open (struct _mh_message *mhm) | ||
1216 | { | ||
1217 | struct _mh_data *mhd = mhm->mhd; | ||
1218 | char *filename = NULL; | ||
1219 | int status; | ||
1220 | int flags = MU_STREAM_ALLOW_LINKS; | ||
1221 | filename = _mh_message_name (mhm, mhm->deleted); | ||
1222 | |||
1223 | if (!filename) | ||
1224 | return ENOMEM; | ||
1225 | |||
1226 | /* The message should be at least readable */ | ||
1227 | if (mhd->mailbox->flags & (MU_STREAM_RDWR|MU_STREAM_WRITE|MU_STREAM_APPEND)) | ||
1228 | flags |= MU_STREAM_RDWR; | ||
1229 | else | ||
1230 | flags |= MU_STREAM_READ; | ||
1231 | status = file_stream_create (&mhm->stream, filename, flags); | ||
1232 | |||
1233 | free (filename); | ||
1234 | |||
1235 | if (status != 0) | ||
1236 | return status; | ||
1237 | |||
1238 | status = stream_open (mhm->stream); | ||
1239 | |||
1240 | if (status != 0) | ||
1241 | stream_destroy (&mhm->stream, NULL); | ||
1242 | |||
1243 | if (status == 0) | ||
1244 | status = mh_scan_message (mhm); | ||
1245 | |||
1246 | return status; | ||
1247 | } | ||
1248 | |||
1249 | /* Close the stream associated with the given message. */ | ||
1250 | void | ||
1251 | mh_message_stream_close (struct _mh_message *mhm) | ||
1252 | { | ||
1253 | if (mhm) | ||
1254 | { | ||
1255 | stream_close (mhm->stream); | ||
1256 | mhm->stream = NULL; | ||
1257 | } | ||
1258 | } | ||
1259 | |||
1260 | void | ||
1261 | mh_check_message (struct _mh_message *mhm) | ||
1262 | { | ||
1263 | if (mhm->body_end == 0) | ||
1264 | mh_pool_open (mhm); | ||
1265 | } | ||
1266 | |||
1267 | /* Reading functions */ | ||
1268 | |||
1269 | static int | ||
1270 | mh_readstream (struct _mh_message *mhm, char *buffer, size_t buflen, | ||
1271 | off_t off, size_t *pnread, int isreadline, | ||
1272 | off_t start, off_t end) | ||
1273 | { | ||
1274 | size_t nread = 0; | ||
1275 | int status = 0; | ||
1276 | off_t ln; | ||
1277 | |||
1278 | if (buffer == NULL || buflen == 0) | ||
1279 | { | ||
1280 | if (pnread) | ||
1281 | *pnread = nread; | ||
1282 | return 0; | ||
1283 | } | ||
1284 | |||
1285 | monitor_rdlock (mhm->mhd->mailbox->monitor); | ||
1286 | #ifdef WITH_PTHREAD | ||
1287 | /* read() is cancellation point since we're doing a potentially | ||
1288 | long operation. Lets make sure we clean the state. */ | ||
1289 | pthread_cleanup_push (mh_cleanup, (void *)mhm->mhd->mailbox); | ||
1290 | #endif | ||
1291 | |||
1292 | ln = end - (start + off); | ||
1293 | if (ln > 0) | ||
1294 | { | ||
1295 | /* Position the file pointer and the buffer. */ | ||
1296 | nread = ((size_t)ln < buflen) ? (size_t)ln : buflen; | ||
1297 | if (isreadline) | ||
1298 | status = stream_readline (mhm->stream, buffer, buflen, | ||
1299 | start + off, &nread); | ||
1300 | else | ||
1301 | status = stream_read (mhm->stream, buffer, nread, | ||
1302 | start + off, &nread); | ||
1303 | } | ||
1304 | |||
1305 | monitor_unlock (mhm->mhd->mailbox->monitor); | ||
1306 | #ifdef WITH_PTHREAD | ||
1307 | pthread_cleanup_pop (0); | ||
1308 | #endif | ||
1309 | |||
1310 | if (pnread) | ||
1311 | *pnread = nread; | ||
1312 | return status; | ||
1313 | } | ||
1314 | |||
1315 | static int | ||
1316 | mh_body_read (stream_t is, char *buffer, size_t buflen, off_t off, | ||
1317 | size_t *pnread) | ||
1318 | { | ||
1319 | body_t body = stream_get_owner (is); | ||
1320 | message_t msg = body_get_owner (body); | ||
1321 | struct _mh_message *mhm = message_get_owner (msg); | ||
1322 | mh_pool_open (mhm); | ||
1323 | return mh_readstream (mhm, buffer, buflen, off, pnread, 0, | ||
1324 | mhm->body_start, mhm->body_end); | ||
1325 | } | ||
1326 | |||
1327 | static int | ||
1328 | mh_body_readline (stream_t is, char *buffer, size_t buflen, | ||
1329 | off_t off, size_t *pnread) | ||
1330 | { | ||
1331 | body_t body = stream_get_owner (is); | ||
1332 | message_t msg = body_get_owner (body); | ||
1333 | struct _mh_message *mhm = message_get_owner (msg); | ||
1334 | mh_pool_open (mhm); | ||
1335 | return mh_readstream (mhm, buffer, buflen, off, pnread, 1, | ||
1336 | mhm->body_start, mhm->body_end); | ||
1337 | } | ||
1338 | |||
1339 | /* Return corresponding sizes */ | ||
1340 | |||
1341 | static int | ||
1342 | mh_stream_size (stream_t stream, off_t *psize) | ||
1343 | { | ||
1344 | body_t body = stream_get_owner (stream); | ||
1345 | return mh_body_size (body, (size_t*) psize); | ||
1346 | } | ||
1347 | |||
1348 | static int | ||
1349 | mh_body_size (body_t body, size_t *psize) | ||
1350 | { | ||
1351 | message_t msg = body_get_owner (body); | ||
1352 | struct _mh_message *mhm = message_get_owner (msg); | ||
1353 | if (mhm == NULL) | ||
1354 | return EINVAL; | ||
1355 | mh_check_message (mhm); | ||
1356 | if (psize) | ||
1357 | *psize = mhm->body_end - mhm->body_start; | ||
1358 | return 0; | ||
1359 | } | ||
1360 | |||
1361 | static int | ||
1362 | mh_body_lines (body_t body, size_t *plines) | ||
1363 | { | ||
1364 | message_t msg = body_get_owner (body); | ||
1365 | struct _mh_message *mhm = message_get_owner (msg); | ||
1366 | if (mhm == NULL) | ||
1367 | return EINVAL; | ||
1368 | mh_check_message (mhm); | ||
1369 | if (plines) | ||
1370 | *plines = mhm->body_lines; | ||
1371 | return 0; | ||
1372 | } | ||
1373 | |||
1374 | static int | ||
1375 | mh_message_uid (message_t msg, size_t *puid) | ||
1376 | { | ||
1377 | struct _mh_message *mhm = message_get_owner (msg); | ||
1378 | if (puid) | ||
1379 | *puid = mhm->seq_number; | ||
1380 | return 0; | ||
1381 | } | ||
1382 | |||
1383 | /* Headers */ | ||
1384 | static int | ||
1385 | mh_header_fill (header_t header, char *buffer, size_t len, | ||
1386 | off_t off, size_t *pnread) | ||
1387 | { | ||
1388 | message_t msg = header_get_owner (header); | ||
1389 | struct _mh_message *mhm = message_get_owner (msg); | ||
1390 | |||
1391 | mh_pool_open (mhm); | ||
1392 | return mh_readstream (mhm, buffer, len, off, pnread, 0, | ||
1393 | 0, mhm->body_start); | ||
1394 | } | ||
1395 | |||
1396 | static int | ||
1397 | mh_header_size (header_t header, size_t *psize) | ||
1398 | { | ||
1399 | message_t msg = header_get_owner (header); | ||
1400 | struct _mh_message *mhm = message_get_owner (msg); | ||
1401 | if (mhm == NULL) | ||
1402 | return EINVAL; | ||
1403 | mh_check_message (mhm); | ||
1404 | if (psize) | ||
1405 | *psize = mhm->body_start; | ||
1406 | return 0; | ||
1407 | } | ||
1408 | |||
1409 | static int | ||
1410 | mh_header_lines (header_t header, size_t *plines) | ||
1411 | { | ||
1412 | message_t msg = header_get_owner (header); | ||
1413 | struct _mh_message *mhm = message_get_owner (msg); | ||
1414 | if (mhm == NULL) | ||
1415 | return EINVAL; | ||
1416 | mh_check_message (mhm); | ||
1417 | if (plines) | ||
1418 | *plines = mhm->header_lines; | ||
1419 | return 0; | ||
1420 | } | ||
1421 | |||
1422 | /* Attributes */ | ||
1423 | static int | ||
1424 | mh_get_attr_flags (attribute_t attr, int *pflags) | ||
1425 | { | ||
1426 | message_t msg = attribute_get_owner (attr); | ||
1427 | struct _mh_message *mhm = message_get_owner (msg); | ||
1428 | |||
1429 | if (mhm == NULL) | ||
1430 | return EINVAL; | ||
1431 | if (pflags) | ||
1432 | *pflags = mhm->attr_flags; | ||
1433 | return 0; | ||
1434 | } | ||
1435 | |||
1436 | static int | ||
1437 | mh_set_attr_flags (attribute_t attr, int flags) | ||
1438 | { | ||
1439 | message_t msg = attribute_get_owner (attr); | ||
1440 | struct _mh_message *mhm = message_get_owner (msg); | ||
1441 | |||
1442 | if (mhm == NULL) | ||
1443 | return EINVAL; | ||
1444 | mhm->attr_flags |= flags; | ||
1445 | return 0; | ||
1446 | } | ||
1447 | |||
1448 | static int | ||
1449 | mh_unset_attr_flags (attribute_t attr, int flags) | ||
1450 | { | ||
1451 | message_t msg = attribute_get_owner (attr); | ||
1452 | struct _mh_message *mhm = message_get_owner (msg); | ||
1453 | |||
1454 | if (mhm == NULL) | ||
1455 | return EINVAL; | ||
1456 | mhm->attr_flags &= ~flags; | ||
1457 | return 0; | ||
1458 | } | ||
1459 | |||
1460 | /* Envelope */ | ||
1461 | static int | ||
1462 | mh_envelope_date (envelope_t envelope, char *buf, size_t len, | ||
1463 | size_t *psize) | ||
1464 | { | ||
1465 | message_t msg = envelope_get_owner (envelope); | ||
1466 | struct _mh_message *mhm = message_get_owner (msg); | ||
1467 | header_t hdr = NULL; | ||
1468 | char *from; | ||
1469 | int status; | ||
1470 | |||
1471 | if (mhm == NULL) | ||
1472 | return EINVAL; | ||
1473 | |||
1474 | if ((status = message_get_header (msg, &hdr)) != 0) | ||
1475 | return status; | ||
1476 | if (header_aget_value (hdr, MU_HEADER_ENV_DATE, &from)) | ||
1477 | return ENOSYS; | ||
1478 | |||
1479 | /* Format: "sender date" */ | ||
1480 | if (buf && len > 0) | ||
1481 | { | ||
1482 | len--; /* Leave space for the null. */ | ||
1483 | strncpy (buf, from, len); | ||
1484 | if (strlen (from) < len) | ||
1485 | { | ||
1486 | len = strlen (buf); | ||
1487 | buf[len++] = '\n'; | ||
1488 | } | ||
1489 | buf[len] = '\0'; | ||
1490 | } | ||
1491 | else | ||
1492 | len = 0; | ||
1493 | |||
1494 | if (psize) | ||
1495 | *psize = len; | ||
1496 | return 0; | ||
1497 | } | ||
1498 | |||
1499 | static int | ||
1500 | mh_envelope_sender (envelope_t envelope, char *buf, size_t len, size_t *psize) | ||
1501 | { | ||
1502 | message_t msg = envelope_get_owner (envelope); | ||
1503 | struct _mh_message *mhm = message_get_owner (msg); | ||
1504 | header_t hdr = NULL; | ||
1505 | char *from; | ||
1506 | int status; | ||
1507 | |||
1508 | if (mhm == NULL) | ||
1509 | return EINVAL; | ||
1510 | |||
1511 | if ((status = message_get_header (msg, &hdr)) != 0) | ||
1512 | return status; | ||
1513 | if (header_aget_value (hdr, MU_HEADER_ENV_SENDER, &from)) | ||
1514 | return ENOSYS; | ||
1515 | |||
1516 | if (buf && len > 0) | ||
1517 | { | ||
1518 | int slen = strlen (from); | ||
1519 | |||
1520 | if (len < slen + 1) | ||
1521 | slen = len - 1; | ||
1522 | memcpy (buf, from, slen); | ||
1523 | buf[slen] = 0; | ||
1524 | } | ||
1525 | else | ||
1526 | len = 0; | ||
1527 | |||
1528 | if (psize) | ||
1529 | *psize = len; | ||
1530 | return 0; | ||
1531 | } | ||
1532 | |||
1533 | #endif |
-
Please register or sign in to post a comment