
2002-11-11 Jeffrey Stedfast <fejj@ximian.com> * providers/local/camel-spool-summary.c (spool_summary_sync_full): Use g_strerror when setting an exception string (we need it to be in UTF-8). (spool_summary_check): Here too. * providers/local/camel-spool-store.c (construct): Use g_strerror when setting an exception string (we need it to be in UTF-8). (get_folder): Same. (scan_dir): Here too. * providers/local/camel-spool-folder.c (spool_lock): Use g_strerror when setting an exception string (we need it to be in UTF-8). * providers/local/camel-mh-summary.c (mh_summary_check): Use g_strerror when setting an exception string (we need it to be in UTF-8). * providers/local/camel-mh-store.c (delete_folder): Use g_strerror when setting an exception string (we need it to be in UTF-8). * providers/local/camel-mbox-summary.c (summary_update): Use g_strerror when setting an exception string (we need it to be in UTF-8). (mbox_summary_sync_full): Here too. (mbox_summary_sync_quick): Same. (mbox_summary_sync): Also here. (camel_mbox_summary_sync_mbox): Again here. * providers/local/camel-mbox-folder.c (mbox_lock): Use g_strerror when setting an exception string (we need it to be in UTF-8). (mbox_append_message): Same. (mbox_get_message): Here too. * providers/local/camel-maildir-summary.c (maildir_summary_load): Use g_strerror when setting an exception string (we need it to be in UTF-8). (maildir_summary_check): Same. * providers/local/camel-maildir-store.c (get_folder): Use g_strerror when setting an exception string (we need it to be in UTF-8). (delete_folder): Same. (delete_folder): Here too. * providers/local/camel-local-summary.c (local_summary_sync): Use g_strerror when setting an exception string (we need it to be in UTF-8). * providers/local/camel-local-store.c (get_folder): Use g_strerror when setting an exception string (we need it to be in UTF-8). (create_folder): Same. (xrename): Here too. (rename_folder): And here. (delete_folder): Also here. * camel-provider.c (camel_provider_init): For debugging printfs, we want to use normal strerror (we want locale charset, not UTF-8). * camel-movemail.c (camel_movemail): Use g_strerror when setting an exception string (we need it to be in UTF-8). (movemail_external): Same. (camel_movemail_copy_file): Here too. (camel_movemail_solaris): Also here. * camel-mime-utils.c (rfc2047_decode_word): For debugging printfs, we want to use normal strerror (we want locale charset, not UTF-8). (header_encode_param): Same. * camel-mime-part-utils.c (convert_buffer): For debugging printfs, we want to use normal strerror (we want locale charset, not UTF-8). * camel-lock-client.c (camel_lock_helper_init): Use g_strerror when setting an exception string (we need it to be in UTF-8). * camel-data-cache.c (camel_data_cache_remove): Use g_strerror when setting an exception string (we need it to be in UTF-8). * camel-tcp-stream-raw.c (flaky_tcp_write): For debugging printfs, we want to use normal strerror (we want locale charset, not UTF-8). (flaky_tcp_read): Same. * camel-gpg-context.c (gpg_ctx_op_step): For debugging printfs, we want to use normal strerror (we want locale charset, not UTF-8). * camel-service.c (camel_gethostbyname): Use g_strerror when setting an exception string (we need it to be in UTF-8). * camel-lock.c (camel_lock_dot): Use g_strerror when setting an exception string (we need it to be in UTF-8). (camel_lock_fcntl): Same. svn path=/trunk/; revision=18689
543 lines
13 KiB
C
543 lines
13 KiB
C
/* -*- 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;
|
|
|
|
camel_exception_clear(ex);
|
|
|
|
/* 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 _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) == HSCAN_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) != HSCAN_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 */
|
|
|