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 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 General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
43 #include "camel-movemail.h"
44 #include "camel-exception.h"
46 #include "camel-mime-parser.h"
47 #include "camel-mime-filter.h"
48 #include "camel-mime-filter-from.h"
50 #include "camel-lock-client.h"
57 static void movemail_external (const char *source, const char *dest,
61 #ifdef HAVE_BROKEN_SPOOL
62 static int camel_movemail_copy_filter(int fromfd, int tofd, off_t start, size_t bytes, CamelMimeFilter *filter);
63 static int camel_movemail_solaris (int oldsfd, int dfd, CamelException *ex);
65 /* these could probably be exposed as a utility? (but only mbox needs it) */
66 static int camel_movemail_copy_file(int sfd, int dfd, CamelException *ex);
70 static int camel_movemail_copy(int fromfd, int tofd, off_t start, size_t bytes);
74 * camel_movemail: Copy an mbox file from a shared spool directory to a
75 * new folder in a Camel store
76 * @source: source file
77 * @dest: destination file
78 * @ex: a CamelException
80 * This copies an mbox file from a shared directory with multiple
81 * readers and writers into a private (presumably Camel-controlled)
82 * directory. Dot locking is used on the source file (but not the
85 * Return Value: Returns -1 on error.
88 camel_movemail(const char *source, const char *dest, CamelException *ex)
95 camel_exception_clear(ex);
97 /* Stat and then open the spool file. If it doesn't exist or
98 * is empty, the user has no mail. (There's technically a race
99 * condition here in that an MDA might have just now locked it
100 * to deliver a message, but we don't care. In that case,
101 * assuming it's unlocked is equivalent to pretending we were
102 * called a fraction earlier.)
104 if (stat (source, &st) == -1) {
105 if (errno != ENOENT) {
106 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
107 _("Could not check mail file %s: %s"),
108 source, g_strerror (errno));
117 sfd = open (source, O_RDWR);
119 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
120 _("Could not open mail file %s: %s"),
121 source, g_strerror (errno));
125 dfd = open (dest, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR);
127 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
128 _("Could not open temporary mail "
129 "file %s: %s"), dest,
135 /* lock our source mailbox */
136 lockid = camel_lock_helper_lock(source, ex);
143 #ifdef HAVE_BROKEN_SPOOL
144 res = camel_movemail_solaris(sfd, dfd, ex);
146 res = camel_movemail_copy_file(sfd, dfd, ex);
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) {
156 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
157 _("Failed to store mail in temp file %s: %s"),
158 dest, g_strerror (errno));
165 camel_lock_helper_unlock(lockid);
172 movemail_external (const char *source, const char *dest, CamelException *ex)
174 sigset_t mask, omask;
176 int fd[2], len = 0, nread, status;
177 char buf[BUFSIZ], *output = NULL;
179 /* Block SIGCHLD so the app can't mess us up. */
181 sigaddset (&mask, SIGCHLD);
182 sigprocmask (SIG_BLOCK, &mask, &omask);
184 if (pipe (fd) == -1) {
185 sigprocmask (SIG_SETMASK, &omask, NULL);
186 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
187 _("Could not create pipe: %s"),
197 sigprocmask (SIG_SETMASK, &omask, NULL);
198 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
199 _("Could not fork: %s"),
206 close (STDIN_FILENO);
207 dup2 (fd[1], STDOUT_FILENO);
208 dup2 (fd[1], STDERR_FILENO);
210 execl (MOVEMAIL_PATH, MOVEMAIL_PATH, source, dest, NULL);
221 /* Read movemail's output. */
222 while ((nread = read (fd[0], buf, sizeof (buf))) > 0) {
223 output = g_realloc (output, len + nread + 1);
224 memcpy (output + len, buf, nread);
230 /* Now get the exit status. */
231 while (waitpid (pid, &status, 0) == -1 && errno == EINTR)
233 sigprocmask (SIG_SETMASK, &omask, NULL);
235 if (!WIFEXITED (status) || WEXITSTATUS (status) != 0) {
236 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
237 _("Movemail program failed: %s"),
238 output ? output : _("(Unknown error)"));
244 #ifndef HAVE_BROKEN_SPOOL
246 camel_movemail_copy_file(int sfd, int dfd, CamelException *ex)
254 nread = read (sfd, buf, sizeof (buf));
257 else if (nread == -1) {
260 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
261 _("Error reading mail file: %s"),
267 nwrote = write (dfd, buf + written, nread);
270 continue; /* continues inner loop */
271 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
272 _("Error writing mail temp file: %s"),
287 camel_movemail_copy(int fromfd, int tofd, off_t start, size_t bytes)
292 d(printf("writing %d bytes ... ", bytes));
294 if (lseek(fromfd, start, SEEK_SET) != start)
306 towrite = read(fromfd, buffer, toread);
307 } while (towrite == -1 && errno == EINTR);
312 /* check for 'end of file' */
314 d(printf("end of file?\n"));
319 toread = write(tofd, buffer, towrite);
320 } while (toread == -1 && errno == EINTR);
329 d(printf("written %d bytes\n", written));
335 #define PRE_SIZE (32)
337 #ifdef HAVE_BROKEN_SPOOL
339 camel_movemail_copy_filter(int fromfd, int tofd, off_t start, size_t bytes, CamelMimeFilter *filter)
341 char buffer[4096+PRE_SIZE];
344 int filterlen, filterpre;
346 d(printf("writing %d bytes ... ", bytes));
348 camel_mime_filter_reset(filter);
350 if (lseek(fromfd, start, SEEK_SET) != start)
362 towrite = read(fromfd, buffer+PRE_SIZE, toread);
363 } while (towrite == -1 && errno == EINTR);
368 d(printf("read %d unfiltered bytes\n", towrite));
370 /* check for 'end of file' */
372 d(printf("end of file?\n"));
373 camel_mime_filter_complete(filter, buffer+PRE_SIZE, towrite, PRE_SIZE,
374 &filterbuffer, &filterlen, &filterpre);
379 camel_mime_filter_filter(filter, buffer+PRE_SIZE, towrite, PRE_SIZE,
380 &filterbuffer, &filterlen, &filterpre);
384 d(printf("writing %d filtered bytes\n", towrite));
387 toread = write(tofd, filterbuffer, towrite);
388 } while (toread == -1 && errno == EINTR);
397 d(printf("written %d bytes\n", written));
402 /* write the headers back out again, but not he Content-Length header, because we dont
403 want to maintain it! */
405 solaris_header_write(int fd, struct _camel_header_raw *header)
410 iv[1].iov_base = ":";
412 iv[3].iov_base = "\n";
416 if (strcasecmp(header->name, "Content-Length")) {
417 iv[0].iov_base = header->name;
418 iv[0].iov_len = strlen(header->name);
419 iv[2].iov_base = header->value;
420 iv[2].iov_len = strlen(header->value);
423 len = writev(fd, iv, 4);
424 } while (len == -1 && errno == EINTR);
430 header = header->next;
434 len = write(fd, "\n", 1);
435 } while (len == -1 && errno == EINTR);
442 d(printf("Wrote %d bytes of headers\n", outlen));
447 /* Well, since Solaris is a tad broken wrt its 'mbox' folder format,
448 we must convert it to a real mbox format. Thankfully this is
449 mostly pretty easy */
451 camel_movemail_solaris (int oldsfd, int dfd, CamelException *ex)
457 CamelMimeFilterFrom *ffrom;
461 /* need to dup as the mime parser will close on finish */
464 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
465 _("Error copying mail temp file: %s"),
470 mp = camel_mime_parser_new();
471 camel_mime_parser_scan_from(mp, TRUE);
472 camel_mime_parser_init_with_fd(mp, sfd);
474 ffrom = camel_mime_filter_from_new();
476 while (camel_mime_parser_step(mp, &buffer, &len) == CAMEL_MIME_PARSER_STATE_FROM) {
477 g_assert(camel_mime_parser_from_line(mp));
478 from = g_strdup(camel_mime_parser_from_line(mp));
479 if (camel_mime_parser_step(mp, &buffer, &len) != CAMEL_MIME_PARSER_STATE_FROM_END) {
487 start = camel_mime_parser_tell_start_from(mp);
488 body = camel_mime_parser_tell(mp);
490 if (write(dfd, from, strlen(from)) != strlen(from))
493 /* write out headers, but NOT content-length header */
494 if (solaris_header_write(dfd, camel_mime_parser_headers_raw(mp)) == -1)
497 cl = camel_mime_parser_header(mp, "content-length", NULL);
499 g_warning("Required Content-Length header is missing from solaris mail box @ %d", (int)camel_mime_parser_tell(mp));
500 camel_mime_parser_drop_step(mp);
501 camel_mime_parser_drop_step(mp);
502 camel_mime_parser_step(mp, &buffer, &len);
503 camel_mime_parser_unstep(mp);
504 length = camel_mime_parser_tell_start_from(mp) - body;
508 camel_mime_parser_drop_step(mp);
509 camel_mime_parser_drop_step(mp);
510 newpos = length+body;
512 /* copy body->length converting From lines */
513 if (camel_movemail_copy_filter(sfd, dfd, body, length, (CamelMimeFilter *)ffrom) == -1)
516 camel_mime_parser_seek(mp, newpos, SEEK_SET);
518 g_error("Inalid parser state: %d", camel_mime_parser_state(mp));
523 camel_object_unref((CamelObject *)mp);
524 camel_object_unref((CamelObject *)ffrom);
531 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
532 _("Error copying mail temp file: %s"),
536 camel_object_unref((CamelObject *)mp);
537 camel_object_unref((CamelObject *)ffrom);
541 #endif /* HAVE_BROKEN_SPOOL */