1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-movemail.c: mbox copying function */
6 * Dan Winship <danw@ximian.com>
8 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of version 2 of the GNU Lesser General Public
12 * License as published by the Free Software Foundation.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
40 #include <glib/gstdio.h>
41 #include <glib/gi18n-lib.h>
43 #include "camel-lock-client.h"
44 #include "camel-mime-filter-from.h"
45 #include "camel-mime-filter.h"
46 #include "camel-mime-parser.h"
47 #include "camel-movemail.h"
54 static void movemail_external (const gchar *source, const gchar *dest,
58 #ifdef HAVE_BROKEN_SPOOL
59 static gint camel_movemail_copy_filter (gint fromfd, gint tofd, goffset start, gsize bytes, CamelMimeFilter *filter);
60 static gint camel_movemail_solaris (gint oldsfd, gint dfd, GError **error);
62 /* these could probably be exposed as a utility? (but only mbox needs it) */
63 static gint camel_movemail_copy_file (gint sfd, gint dfd, GError **error);
67 static gint camel_movemail_copy (gint fromfd, gint tofd, goffset start, gsize bytes);
72 * @source: source file
73 * @dest: destination file
74 * @error: return location for a #GError, or %NULL
76 * This copies an mbox file from a shared directory with multiple
77 * readers and writers into a private (presumably Camel-controlled)
78 * directory. Dot locking is used on the source file (but not the
81 * Return Value: Returns -1 on error.
84 camel_movemail (const gchar *source,
93 /* Stat and then open the spool file. If it doesn't exist or
94 * is empty, the user has no mail. (There's technically a race
95 * condition here in that an MDA might have just now locked it
96 * to deliver a message, but we don't care. In that case,
97 * assuming it's unlocked is equivalent to pretending we were
98 * called a fraction earlier.)
100 if (g_stat (source, &st) == -1) {
104 g_io_error_from_errno (errno),
105 _("Could not check mail file %s: %s"),
106 source, g_strerror (errno));
114 sfd = open (source, O_RDWR);
118 g_io_error_from_errno (errno),
119 _("Could not open mail file %s: %s"),
120 source, g_strerror (errno));
124 dfd = open (dest, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR);
128 g_io_error_from_errno (errno),
129 _("Could not open temporary mail file %s: %s"),
130 dest, g_strerror (errno));
135 /* lock our source mailbox */
136 lockid = camel_lock_helper_lock (source, error);
143 #ifdef HAVE_BROKEN_SPOOL
144 res = camel_movemail_solaris (sfd, dfd, ex);
146 res = camel_movemail_copy_file (sfd, dfd, error);
149 /* If no errors occurred copying the data, and we successfully
150 * close the destination file, then truncate the source file.
153 if (close (dfd) == 0) {
158 g_io_error_from_errno (errno),
159 _("Failed to store mail in temp file %s: %s"),
160 dest, g_strerror (errno));
167 camel_lock_helper_unlock (lockid);
174 movemail_external (const gchar *source,
178 sigset_t mask, omask;
180 gint fd[2], len = 0, nread, status;
181 gchar buf[BUFSIZ], *output = NULL;
183 /* Block SIGCHLD so the app can't mess us up. */
185 sigaddset (&mask, SIGCHLD);
186 sigprocmask (SIG_BLOCK, &mask, &omask);
188 if (pipe (fd) == -1) {
189 sigprocmask (SIG_SETMASK, &omask, NULL);
192 g_io_error_from_errno (errno),
193 _("Could not create pipe: %s"),
203 sigprocmask (SIG_SETMASK, &omask, NULL);
206 g_io_error_from_errno (errno),
207 _("Could not fork: %s"), g_strerror (errno));
213 close (STDIN_FILENO);
214 dup2 (fd[1], STDOUT_FILENO);
215 dup2 (fd[1], STDERR_FILENO);
217 execl (MOVEMAIL_PATH, MOVEMAIL_PATH, source, dest, NULL);
228 /* Read movemail's output. */
229 while ((nread = read (fd[0], buf, sizeof (buf))) > 0) {
230 output = g_realloc (output, len + nread + 1);
231 memcpy (output + len, buf, nread);
237 /* Now get the exit status. */
238 while (waitpid (pid, &status, 0) == -1 && errno == EINTR)
240 sigprocmask (SIG_SETMASK, &omask, NULL);
242 if (!WIFEXITED (status) || WEXITSTATUS (status) != 0) {
244 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
245 _("Movemail program failed: %s"),
246 output ? output : _("(Unknown error)"));
252 #ifndef HAVE_BROKEN_SPOOL
254 camel_movemail_copy_file (gint sfd,
264 nread = read (sfd, buf, sizeof (buf));
267 else if (nread == -1) {
272 g_io_error_from_errno (errno),
273 _("Error reading mail file: %s"),
279 nwrote = write (dfd, buf + written, nread);
282 continue; /* continues inner loop */
285 g_io_error_from_errno (errno),
286 _("Error writing mail temp file: %s"),
301 camel_movemail_copy (gint fromfd,
309 d (printf ("writing %d bytes ... ", bytes));
311 if (lseek (fromfd, start, SEEK_SET) != start)
315 gint toread, towrite;
323 towrite = read (fromfd, buffer, toread);
324 } while (towrite == -1 && errno == EINTR);
329 /* check for 'end of file' */
331 d (printf ("end of file?\n"));
336 toread = write (tofd, buffer, towrite);
337 } while (toread == -1 && errno == EINTR);
346 d (printf ("written %d bytes\n", written));
352 #define PRE_SIZE (32)
354 #ifdef HAVE_BROKEN_SPOOL
356 camel_movemail_copy_filter (gint fromfd,
360 CamelMimeFilter *filter)
362 gchar buffer[4096 + PRE_SIZE];
365 gint filterlen, filterpre;
367 d (printf ("writing %d bytes ... ", bytes));
369 camel_mime_filter_reset (filter);
371 if (lseek (fromfd, start, SEEK_SET) != start)
375 gint toread, towrite;
383 towrite = read (fromfd, buffer + PRE_SIZE, toread);
384 } while (towrite == -1 && errno == EINTR);
389 d (printf ("read %d unfiltered bytes\n", towrite));
391 /* check for 'end of file' */
393 d (printf ("end of file?\n"));
394 camel_mime_filter_complete (
395 filter, buffer + PRE_SIZE, towrite, PRE_SIZE,
396 &filterbuffer, &filterlen, &filterpre);
401 camel_mime_filter_filter (
402 filter, buffer + PRE_SIZE, towrite, PRE_SIZE,
403 &filterbuffer, &filterlen, &filterpre);
407 d (printf ("writing %d filtered bytes\n", towrite));
410 toread = write (tofd, filterbuffer, towrite);
411 } while (toread == -1 && errno == EINTR);
420 d (printf ("written %d bytes\n", written));
425 /* write the headers back out again, but not he Content-Length header, because we dont
426 * want to maintain it! */
428 solaris_header_write (gint fd,
429 struct _camel_header_raw *header)
432 gint outlen = 0, len;
434 iv[1].iov_base = ":";
436 iv[3].iov_base = "\n";
440 if (g_ascii_strcasecmp (header->name, "Content-Length")) {
441 iv[0].iov_base = header->name;
442 iv[0].iov_len = strlen (header->name);
443 iv[2].iov_base = header->value;
444 iv[2].iov_len = strlen (header->value);
447 len = writev (fd, iv, 4);
448 } while (len == -1 && errno == EINTR);
454 header = header->next;
458 len = write (fd, "\n", 1);
459 } while (len == -1 && errno == EINTR);
466 d (printf ("Wrote %d bytes of headers\n", outlen));
471 /* Well, since Solaris is a tad broken wrt its 'mbox' folder format,
472 * we must convert it to a real mbox format. Thankfully this is
473 * mostly pretty easy */
475 camel_movemail_solaris (gint oldsfd,
483 CamelMimeFilter *ffrom;
487 /* need to dup as the mime parser will close on finish */
492 g_io_error_from_errno (errno),
493 _("Error copying mail temp file: %s"),
498 mp = camel_mime_parser_new ();
499 camel_mime_parser_scan_from (mp, TRUE);
500 camel_mime_parser_init_with_fd (mp, sfd);
502 ffrom = camel_mime_filter_from_new ();
504 while (camel_mime_parser_step (mp, &buffer, &len) == CAMEL_MIME_PARSER_STATE_FROM) {
505 g_assert (camel_mime_parser_from_line (mp));
506 from = g_strdup (camel_mime_parser_from_line (mp));
507 if (camel_mime_parser_step (mp, &buffer, &len) != CAMEL_MIME_PARSER_STATE_FROM_END) {
515 start = camel_mime_parser_tell_start_from (mp);
516 body = camel_mime_parser_tell (mp);
518 if (write (dfd, from, strlen (from)) != strlen (from))
521 /* write out headers, but NOT content-length header */
522 if (solaris_header_write (dfd, camel_mime_parser_headers_raw (mp)) == -1)
525 cl = camel_mime_parser_header (mp, "content-length", NULL);
527 g_warning ("Required Content-Length header is missing from solaris mail box @ %d", (gint) camel_mime_parser_tell (mp));
528 camel_mime_parser_drop_step (mp);
529 camel_mime_parser_drop_step (mp);
530 camel_mime_parser_step (mp, &buffer, &len);
531 camel_mime_parser_unstep (mp);
532 length = camel_mime_parser_tell_start_from (mp) - body;
536 camel_mime_parser_drop_step (mp);
537 camel_mime_parser_drop_step (mp);
538 newpos = length + body;
540 /* copy body->length converting From lines */
541 if (camel_movemail_copy_filter (sfd, dfd, body, length, ffrom) == -1)
544 camel_mime_parser_seek (mp, newpos, SEEK_SET);
546 g_error ("Inalid parser state: %d", camel_mime_parser_state (mp));
552 g_object_unref (ffrom);
561 g_io_error_from_errno (errno),
562 _("Error copying mail temp file: %s"),
566 g_object_unref (ffrom);
570 #endif /* HAVE_BROKEN_SPOOL */