aboutsummaryrefslogtreecommitdiffstats
path: root/camel/camel-movemail.c
diff options
context:
space:
mode:
Diffstat (limited to 'camel/camel-movemail.c')
-rw-r--r--camel/camel-movemail.c540
1 files changed, 540 insertions, 0 deletions
diff --git a/camel/camel-movemail.c b/camel/camel-movemail.c
new file mode 100644
index 0000000000..9684bf25e6
--- /dev/null
+++ b/camel/camel-movemail.c
@@ -0,0 +1,540 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* camel-movemail.c: mbox copying function */
+
+/*
+ * Author:
+ * Dan Winship <danw@ximian.com>
+ *
+ * Copyright 2000 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
+
+#include "camel-movemail.h"
+#include "camel-exception.h"
+
+#include "camel-mime-parser.h"
+#include "camel-mime-filter.h"
+#include "camel-mime-filter-from.h"
+
+#include "camel-lock-client.h"
+
+#define d(x)
+
+#ifdef MOVEMAIL_PATH
+#include <sys/wait.h>
+
+static void movemail_external (const char *source, const char *dest,
+ CamelException *ex);
+#endif
+
+#ifdef HAVE_BROKEN_SPOOL
+static int camel_movemail_copy_filter(int fromfd, int tofd, off_t start, size_t bytes, CamelMimeFilter *filter);
+static int camel_movemail_solaris (int oldsfd, int dfd, CamelException *ex);
+#else
+/* these could probably be exposed as a utility? (but only mbox needs it) */
+static int camel_movemail_copy_file(int sfd, int dfd, CamelException *ex);
+#endif
+
+#if 0
+static int camel_movemail_copy(int fromfd, int tofd, off_t start, size_t bytes);
+#endif
+
+/**
+ * camel_movemail: Copy an mbox file from a shared spool directory to a
+ * new folder in a Camel store
+ * @source: source file
+ * @dest: destination file
+ * @ex: a CamelException
+ *
+ * This copies an mbox file from a shared directory with multiple
+ * readers and writers into a private (presumably Camel-controlled)
+ * directory. Dot locking is used on the source file (but not the
+ * destination).
+ *
+ * Return Value: Returns -1 on error.
+ **/
+int
+camel_movemail(const char *source, const char *dest, CamelException *ex)
+{
+ int lockid = -1;
+ int res = -1;
+ int sfd, dfd;
+ struct stat st;
+
+ /* Stat and then open the spool file. If it doesn't exist or
+ * is empty, the user has no mail. (There's technically a race
+ * condition here in that an MDA might have just now locked it
+ * to deliver a message, but we don't care. In that case,
+ * assuming it's unlocked is equivalent to pretending we were
+ * called a fraction earlier.)
+ */
+ if (stat (source, &st) == -1) {
+ if (errno != ENOENT) {
+ camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Could not check mail file %s: %s"),
+ source, g_strerror (errno));
+ }
+ return -1;
+ }
+
+ if (st.st_size == 0)
+ return 0;
+
+ /* open files */
+ sfd = open (source, O_RDWR);
+ if (sfd == -1) {
+ camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Could not open mail file %s: %s"),
+ source, g_strerror (errno));
+ return -1;
+ }
+
+ dfd = open (dest, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR);
+ if (dfd == -1) {
+ camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Could not open temporary mail "
+ "file %s: %s"), dest,
+ g_strerror (errno));
+ close (sfd);
+ return -1;
+ }
+
+ /* lock our source mailbox */
+ lockid = camel_lock_helper_lock(source, ex);
+ if (lockid == -1) {
+ close(sfd);
+ close(dfd);
+ return -1;
+ }
+
+#ifdef HAVE_BROKEN_SPOOL
+ res = camel_movemail_solaris(sfd, dfd, ex);
+#else
+ res = camel_movemail_copy_file(sfd, dfd, ex);
+#endif
+
+ /* If no errors occurred copying the data, and we successfully
+ * close the destination file, then truncate the source file.
+ */
+ if (res != -1) {
+ if (close (dfd) == 0) {
+ ftruncate (sfd, 0);
+ } else {
+ camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Failed to store mail in temp file %s: %s"),
+ dest, g_strerror (errno));
+ res = -1;
+ }
+ } else
+ close (dfd);
+ close (sfd);
+
+ camel_lock_helper_unlock(lockid);
+
+ return res;
+}
+
+#ifdef MOVEMAIL_PATH
+static void
+movemail_external (const char *source, const char *dest, CamelException *ex)
+{
+ sigset_t mask, omask;
+ pid_t pid;
+ int fd[2], len = 0, nread, status;
+ char buf[BUFSIZ], *output = NULL;
+
+ /* Block SIGCHLD so the app can't mess us up. */
+ sigemptyset (&mask);
+ sigaddset (&mask, SIGCHLD);
+ sigprocmask (SIG_BLOCK, &mask, &omask);
+
+ if (pipe (fd) == -1) {
+ sigprocmask (SIG_SETMASK, &omask, NULL);
+ camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Could not create pipe: %s"),
+ g_strerror (errno));
+ return;
+ }
+
+ pid = fork ();
+ switch (pid) {
+ case -1:
+ close (fd[0]);
+ close (fd[1]);
+ sigprocmask (SIG_SETMASK, &omask, NULL);
+ camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Could not fork: %s"),
+ g_strerror (errno));
+ return;
+
+ case 0:
+ /* Child */
+ close (fd[0]);
+ close (STDIN_FILENO);
+ dup2 (fd[1], STDOUT_FILENO);
+ dup2 (fd[1], STDERR_FILENO);
+
+ execl (MOVEMAIL_PATH, MOVEMAIL_PATH, source, dest, NULL);
+ _exit (255);
+ break;
+
+ default:
+ break;
+ }
+
+ /* Parent */
+ close (fd[1]);
+
+ /* Read movemail's output. */
+ while ((nread = read (fd[0], buf, sizeof (buf))) > 0) {
+ output = g_realloc (output, len + nread + 1);
+ memcpy (output + len, buf, nread);
+ len += nread;
+ output[len] = '\0';
+ }
+ close (fd[0]);
+
+ /* Now get the exit status. */
+ while (waitpid (pid, &status, 0) == -1 && errno == EINTR)
+ ;
+ sigprocmask (SIG_SETMASK, &omask, NULL);
+
+ if (!WIFEXITED (status) || WEXITSTATUS (status) != 0) {
+ camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Movemail program failed: %s"),
+ output ? output : _("(Unknown error)"));
+ }
+ g_free (output);
+}
+#endif
+
+#ifndef HAVE_BROKEN_SPOOL
+static int
+camel_movemail_copy_file(int sfd, int dfd, CamelException *ex)
+{
+ int nread, nwrote;
+ char buf[4096];
+
+ while (1) {
+ int written = 0;
+
+ nread = read (sfd, buf, sizeof (buf));
+ if (nread == 0)
+ break;
+ else if (nread == -1) {
+ if (errno == EINTR)
+ continue;
+ camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Error reading mail file: %s"),
+ g_strerror (errno));
+ return -1;
+ }
+
+ while (nread) {
+ nwrote = write (dfd, buf + written, nread);
+ if (nwrote == -1) {
+ if (errno == EINTR)
+ continue; /* continues inner loop */
+ camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Error writing mail temp file: %s"),
+ g_strerror (errno));
+ return -1;
+ }
+ written += nwrote;
+ nread -= nwrote;
+ }
+ }
+
+ return 0;
+}
+#endif
+
+#if 0
+static int
+camel_movemail_copy(int fromfd, int tofd, off_t start, size_t bytes)
+{
+ char buffer[4096];
+ int written = 0;
+
+ d(printf("writing %d bytes ... ", bytes));
+
+ if (lseek(fromfd, start, SEEK_SET) != start)
+ return -1;
+
+ while (bytes>0) {
+ int toread, towrite;
+
+ toread = bytes;
+ if (bytes>4096)
+ toread = 4096;
+ else
+ toread = bytes;
+ do {
+ towrite = read(fromfd, buffer, toread);
+ } while (towrite == -1 && errno == EINTR);
+
+ if (towrite == -1)
+ return -1;
+
+ /* check for 'end of file' */
+ if (towrite == 0) {
+ d(printf("end of file?\n"));
+ break;
+ }
+
+ do {
+ toread = write(tofd, buffer, towrite);
+ } while (toread == -1 && errno == EINTR);
+
+ if (toread == -1)
+ return -1;
+
+ written += toread;
+ bytes -= toread;
+ }
+
+ d(printf("written %d bytes\n", written));
+
+ return written;
+}
+#endif
+
+#define PRE_SIZE (32)
+
+#ifdef HAVE_BROKEN_SPOOL
+static int
+camel_movemail_copy_filter(int fromfd, int tofd, off_t start, size_t bytes, CamelMimeFilter *filter)
+{
+ char buffer[4096+PRE_SIZE];
+ int written = 0;
+ char *filterbuffer;
+ int filterlen, filterpre;
+
+ d(printf("writing %d bytes ... ", bytes));
+
+ camel_mime_filter_reset(filter);
+
+ if (lseek(fromfd, start, SEEK_SET) != start)
+ return -1;
+
+ while (bytes>0) {
+ int toread, towrite;
+
+ toread = bytes;
+ if (bytes>4096)
+ toread = 4096;
+ else
+ toread = bytes;
+ do {
+ towrite = read(fromfd, buffer+PRE_SIZE, toread);
+ } while (towrite == -1 && errno == EINTR);
+
+ if (towrite == -1)
+ return -1;
+
+ d(printf("read %d unfiltered bytes\n", towrite));
+
+ /* check for 'end of file' */
+ if (towrite == 0) {
+ d(printf("end of file?\n"));
+ camel_mime_filter_complete(filter, buffer+PRE_SIZE, towrite, PRE_SIZE,
+ &filterbuffer, &filterlen, &filterpre);
+ towrite = filterlen;
+ if (towrite == 0)
+ break;
+ } else {
+ camel_mime_filter_filter(filter, buffer+PRE_SIZE, towrite, PRE_SIZE,
+ &filterbuffer, &filterlen, &filterpre);
+ towrite = filterlen;
+ }
+
+ d(printf("writing %d filtered bytes\n", towrite));
+
+ do {
+ toread = write(tofd, filterbuffer, towrite);
+ } while (toread == -1 && errno == EINTR);
+
+ if (toread == -1)
+ return -1;
+
+ written += toread;
+ bytes -= toread;
+ }
+
+ d(printf("written %d bytes\n", written));
+
+ return written;
+}
+
+/* write the headers back out again, but not he Content-Length header, because we dont
+ want to maintain it! */
+static int
+solaris_header_write(int fd, struct _camel_header_raw *header)
+{
+ struct iovec iv[4];
+ int outlen = 0, len;
+
+ iv[1].iov_base = ":";
+ iv[1].iov_len = 1;
+ iv[3].iov_base = "\n";
+ iv[3].iov_len = 1;
+
+ while (header) {
+ if (strcasecmp(header->name, "Content-Length")) {
+ iv[0].iov_base = header->name;
+ iv[0].iov_len = strlen(header->name);
+ iv[2].iov_base = header->value;
+ iv[2].iov_len = strlen(header->value);
+
+ do {
+ len = writev(fd, iv, 4);
+ } while (len == -1 && errno == EINTR);
+
+ if (len == -1)
+ return -1;
+ outlen += len;
+ }
+ header = header->next;
+ }
+
+ do {
+ len = write(fd, "\n", 1);
+ } while (len == -1 && errno == EINTR);
+
+ if (len == -1)
+ return -1;
+
+ outlen += 1;
+
+ d(printf("Wrote %d bytes of headers\n", outlen));
+
+ return outlen;
+}
+
+/* Well, since Solaris is a tad broken wrt its 'mbox' folder format,
+ we must convert it to a real mbox format. Thankfully this is
+ mostly pretty easy */
+static int
+camel_movemail_solaris (int oldsfd, int dfd, CamelException *ex)
+{
+ CamelMimeParser *mp;
+ char *buffer;
+ int len;
+ int sfd;
+ CamelMimeFilterFrom *ffrom;
+ int ret = 1;
+ char *from = NULL;
+
+ /* need to dup as the mime parser will close on finish */
+ sfd = dup(oldsfd);
+ if (sfd == -1) {
+ camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Error copying mail temp file: %s"),
+ g_strerror (errno));
+ return -1;
+ }
+
+ mp = camel_mime_parser_new();
+ camel_mime_parser_scan_from(mp, TRUE);
+ camel_mime_parser_init_with_fd(mp, sfd);
+
+ ffrom = camel_mime_filter_from_new();
+
+ while (camel_mime_parser_step(mp, &buffer, &len) == CAMEL_MIME_PARSER_STATE_FROM) {
+ g_assert(camel_mime_parser_from_line(mp));
+ from = g_strdup(camel_mime_parser_from_line(mp));
+ if (camel_mime_parser_step(mp, &buffer, &len) != CAMEL_MIME_PARSER_STATE_FROM_END) {
+ const char *cl;
+ int length;
+ int start, body;
+ off_t newpos;
+
+ ret = 0;
+
+ start = camel_mime_parser_tell_start_from(mp);
+ body = camel_mime_parser_tell(mp);
+
+ if (write(dfd, from, strlen(from)) != strlen(from))
+ goto fail;
+
+ /* write out headers, but NOT content-length header */
+ if (solaris_header_write(dfd, camel_mime_parser_headers_raw(mp)) == -1)
+ goto fail;
+
+ cl = camel_mime_parser_header(mp, "content-length", NULL);
+ if (cl == NULL) {
+ g_warning("Required Content-Length header is missing from solaris mail box @ %d", (int)camel_mime_parser_tell(mp));
+ camel_mime_parser_drop_step(mp);
+ camel_mime_parser_drop_step(mp);
+ camel_mime_parser_step(mp, &buffer, &len);
+ camel_mime_parser_unstep(mp);
+ length = camel_mime_parser_tell_start_from(mp) - body;
+ newpos = -1;
+ } else {
+ length = atoi(cl);
+ camel_mime_parser_drop_step(mp);
+ camel_mime_parser_drop_step(mp);
+ newpos = length+body;
+ }
+ /* copy body->length converting From lines */
+ if (camel_movemail_copy_filter(sfd, dfd, body, length, (CamelMimeFilter *)ffrom) == -1)
+ goto fail;
+ if (newpos != -1)
+ camel_mime_parser_seek(mp, newpos, SEEK_SET);
+ } else {
+ g_error("Inalid parser state: %d", camel_mime_parser_state(mp));
+ }
+ g_free(from);
+ }
+
+ camel_object_unref((CamelObject *)mp);
+ camel_object_unref((CamelObject *)ffrom);
+
+ return ret;
+
+fail:
+ g_free(from);
+
+ camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Error copying mail temp file: %s"),
+ g_strerror (errno));
+
+
+ camel_object_unref((CamelObject *)mp);
+ camel_object_unref((CamelObject *)ffrom);
+
+ return -1;
+}
+#endif /* HAVE_BROKEN_SPOOL */
+