diff options
Diffstat (limited to 'camel/camel-movemail.c')
-rw-r--r-- | camel/camel-movemail.c | 540 |
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 */ + |