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