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 2000 Ximian, Inc. (www.ximian.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
45 #include <glib/gi18n-lib.h>
47 #include "camel-exception.h"
48 #include "camel-lock-client.h"
49 #include "camel-mime-filter-from.h"
50 #include "camel-mime-filter.h"
51 #include "camel-mime-parser.h"
52 #include "camel-movemail.h"
59 static void movemail_external (const char *source, const char *dest,
63 #ifdef HAVE_BROKEN_SPOOL
64 static int camel_movemail_copy_filter(int fromfd, int tofd, off_t start, size_t bytes, CamelMimeFilter *filter);
65 static int camel_movemail_solaris (int oldsfd, int dfd, CamelException *ex);
67 /* these could probably be exposed as a utility? (but only mbox needs it) */
68 static int camel_movemail_copy_file(int sfd, int dfd, CamelException *ex);
72 static int camel_movemail_copy(int fromfd, int tofd, off_t start, size_t bytes);
77 * @source: source file
78 * @dest: destination file
79 * @ex: a CamelException
81 * This copies an mbox file from a shared directory with multiple
82 * readers and writers into a private (presumably Camel-controlled)
83 * directory. Dot locking is used on the source file (but not the
86 * Return Value: Returns -1 on error.
89 camel_movemail(const char *source, const char *dest, CamelException *ex)
96 /* Stat and then open the spool file. If it doesn't exist or
97 * is empty, the user has no mail. (There's technically a race
98 * condition here in that an MDA might have just now locked it
99 * to deliver a message, but we don't care. In that case,
100 * assuming it's unlocked is equivalent to pretending we were
101 * called a fraction earlier.)
103 if (stat (source, &st) == -1) {
104 if (errno != ENOENT) {
105 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
106 _("Could not check mail file %s: %s"),
107 source, g_strerror (errno));
116 sfd = open (source, O_RDWR);
118 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
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);
126 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
127 _("Could not open temporary mail "
128 "file %s: %s"), dest,
134 /* lock our source mailbox */
135 lockid = camel_lock_helper_lock(source, ex);
142 #ifdef HAVE_BROKEN_SPOOL
143 res = camel_movemail_solaris(sfd, dfd, ex);
145 res = camel_movemail_copy_file(sfd, dfd, ex);
148 /* If no errors occurred copying the data, and we successfully
149 * close the destination file, then truncate the source file.
152 if (close (dfd) == 0) {
155 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
156 _("Failed to store mail in temp file %s: %s"),
157 dest, g_strerror (errno));
164 camel_lock_helper_unlock(lockid);
171 movemail_external (const char *source, const char *dest, CamelException *ex)
173 sigset_t mask, omask;
175 int fd[2], len = 0, nread, status;
176 char buf[BUFSIZ], *output = NULL;
178 /* Block SIGCHLD so the app can't mess us up. */
180 sigaddset (&mask, SIGCHLD);
181 sigprocmask (SIG_BLOCK, &mask, &omask);
183 if (pipe (fd) == -1) {
184 sigprocmask (SIG_SETMASK, &omask, NULL);
185 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
186 _("Could not create pipe: %s"),
196 sigprocmask (SIG_SETMASK, &omask, NULL);
197 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
198 _("Could not fork: %s"),
205 close (STDIN_FILENO);
206 dup2 (fd[1], STDOUT_FILENO);
207 dup2 (fd[1], STDERR_FILENO);
209 execl (MOVEMAIL_PATH, MOVEMAIL_PATH, source, dest, NULL);
220 /* Read movemail's output. */
221 while ((nread = read (fd[0], buf, sizeof (buf))) > 0) {
222 output = g_realloc (output, len + nread + 1);
223 memcpy (output + len, buf, nread);
229 /* Now get the exit status. */
230 while (waitpid (pid, &status, 0) == -1 && errno == EINTR)
232 sigprocmask (SIG_SETMASK, &omask, NULL);
234 if (!WIFEXITED (status) || WEXITSTATUS (status) != 0) {
235 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
236 _("Movemail program failed: %s"),
237 output ? output : _("(Unknown error)"));
243 #ifndef HAVE_BROKEN_SPOOL
245 camel_movemail_copy_file(int sfd, int dfd, CamelException *ex)
253 nread = read (sfd, buf, sizeof (buf));
256 else if (nread == -1) {
259 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
260 _("Error reading mail file: %s"),
266 nwrote = write (dfd, buf + written, nread);
269 continue; /* continues inner loop */
270 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
271 _("Error writing mail temp file: %s"),
286 camel_movemail_copy(int fromfd, int tofd, off_t start, size_t bytes)
291 d(printf("writing %d bytes ... ", bytes));
293 if (lseek(fromfd, start, SEEK_SET) != start)
305 towrite = read(fromfd, buffer, toread);
306 } while (towrite == -1 && errno == EINTR);
311 /* check for 'end of file' */
313 d(printf("end of file?\n"));
318 toread = write(tofd, buffer, towrite);
319 } while (toread == -1 && errno == EINTR);
328 d(printf("written %d bytes\n", written));
334 #define PRE_SIZE (32)
336 #ifdef HAVE_BROKEN_SPOOL
338 camel_movemail_copy_filter(int fromfd, int tofd, off_t start, size_t bytes, CamelMimeFilter *filter)
340 char buffer[4096+PRE_SIZE];
343 int filterlen, filterpre;
345 d(printf("writing %d bytes ... ", bytes));
347 camel_mime_filter_reset(filter);
349 if (lseek(fromfd, start, SEEK_SET) != start)
361 towrite = read(fromfd, buffer+PRE_SIZE, toread);
362 } while (towrite == -1 && errno == EINTR);
367 d(printf("read %d unfiltered bytes\n", towrite));
369 /* check for 'end of file' */
371 d(printf("end of file?\n"));
372 camel_mime_filter_complete(filter, buffer+PRE_SIZE, towrite, PRE_SIZE,
373 &filterbuffer, &filterlen, &filterpre);
378 camel_mime_filter_filter(filter, buffer+PRE_SIZE, towrite, PRE_SIZE,
379 &filterbuffer, &filterlen, &filterpre);
383 d(printf("writing %d filtered bytes\n", towrite));
386 toread = write(tofd, filterbuffer, towrite);
387 } while (toread == -1 && errno == EINTR);
396 d(printf("written %d bytes\n", written));
401 /* write the headers back out again, but not he Content-Length header, because we dont
402 want to maintain it! */
404 solaris_header_write(int fd, struct _camel_header_raw *header)
409 iv[1].iov_base = ":";
411 iv[3].iov_base = "\n";
415 if (g_ascii_strcasecmp(header->name, "Content-Length")) {
416 iv[0].iov_base = header->name;
417 iv[0].iov_len = strlen(header->name);
418 iv[2].iov_base = header->value;
419 iv[2].iov_len = strlen(header->value);
422 len = writev(fd, iv, 4);
423 } while (len == -1 && errno == EINTR);
429 header = header->next;
433 len = write(fd, "\n", 1);
434 } while (len == -1 && errno == EINTR);
441 d(printf("Wrote %d bytes of headers\n", outlen));
446 /* Well, since Solaris is a tad broken wrt its 'mbox' folder format,
447 we must convert it to a real mbox format. Thankfully this is
448 mostly pretty easy */
450 camel_movemail_solaris (int oldsfd, int dfd, CamelException *ex)
456 CamelMimeFilterFrom *ffrom;
460 /* need to dup as the mime parser will close on finish */
463 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
464 _("Error copying mail temp file: %s"),
469 mp = camel_mime_parser_new();
470 camel_mime_parser_scan_from(mp, TRUE);
471 camel_mime_parser_init_with_fd(mp, sfd);
473 ffrom = camel_mime_filter_from_new();
475 while (camel_mime_parser_step(mp, &buffer, &len) == CAMEL_MIME_PARSER_STATE_FROM) {
476 g_assert(camel_mime_parser_from_line(mp));
477 from = g_strdup(camel_mime_parser_from_line(mp));
478 if (camel_mime_parser_step(mp, &buffer, &len) != CAMEL_MIME_PARSER_STATE_FROM_END) {
486 start = camel_mime_parser_tell_start_from(mp);
487 body = camel_mime_parser_tell(mp);
489 if (write(dfd, from, strlen(from)) != strlen(from))
492 /* write out headers, but NOT content-length header */
493 if (solaris_header_write(dfd, camel_mime_parser_headers_raw(mp)) == -1)
496 cl = camel_mime_parser_header(mp, "content-length", NULL);
498 g_warning("Required Content-Length header is missing from solaris mail box @ %d", (int)camel_mime_parser_tell(mp));
499 camel_mime_parser_drop_step(mp);
500 camel_mime_parser_drop_step(mp);
501 camel_mime_parser_step(mp, &buffer, &len);
502 camel_mime_parser_unstep(mp);
503 length = camel_mime_parser_tell_start_from(mp) - body;
507 camel_mime_parser_drop_step(mp);
508 camel_mime_parser_drop_step(mp);
509 newpos = length+body;
511 /* copy body->length converting From lines */
512 if (camel_movemail_copy_filter(sfd, dfd, body, length, (CamelMimeFilter *)ffrom) == -1)
515 camel_mime_parser_seek(mp, newpos, SEEK_SET);
517 g_error("Inalid parser state: %d", camel_mime_parser_state(mp));
522 camel_object_unref((CamelObject *)mp);
523 camel_object_unref((CamelObject *)ffrom);
530 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
531 _("Error copying mail temp file: %s"),
535 camel_object_unref((CamelObject *)mp);
536 camel_object_unref((CamelObject *)ffrom);
540 #endif /* HAVE_BROKEN_SPOOL */