/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* camel-movemail.c: mbox copying function */ /* * Author: * Dan Winship * * Copyright 2000 Helix Code, Inc. (http://www.helixcode.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * 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 */ #include #include #include #include #include #include #include #include #include "camel-movemail.h" #include "camel-exception.h" /** * 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: 1 if mail was copied, 0 if the source file contained * no mail, -1 if an error occurred. **/ int camel_movemail (const char *source, const char *dest, CamelException *ex) { gboolean locked, error; int sfd, dfd, tmpfd; char *locktmpfile, *lockfile; struct stat st; time_t now, timeout; int nread, nwrote; char buf[BUFSIZ]; 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) return 0; 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; 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, 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; } /* Create the unique lock file. */ locktmpfile = g_strdup_printf ("%s.lock.XXXXXX", source); #ifdef HAVE_MKSTEMP tmpfd = mkstemp (locktmpfile); #else if (mktemp (locktmpfile)) { tmpfd = open (locktmpfile, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); } else tmpfd = -1; #endif if (tmpfd == -1) { camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, "Could not create lock file " "for %s: %s", source, g_strerror (errno)); close (sfd); close (dfd); unlink (dest); return -1; } close (tmpfd); lockfile = g_strdup_printf ("%s.lock", source); locked = FALSE; time (&timeout); timeout += 30; /* Loop trying to lock the file for 30 seconds. */ while (time (&now) < timeout) { /* Try to make the lock. */ if (symlink (locktmpfile, lockfile) == 0) { locked = TRUE; break; } /* If we fail for a reason other than that someone * else has the lock, then abort. */ if (errno != EEXIST) { camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, "Could not create lock " "file for %s: %s", source, g_strerror (errno)); break; } /* Check the modtime on the lock file. */ if (stat (lockfile, &st) == -1) { /* If the lockfile disappeared, try again. */ if (errno == ENOENT) continue; /* Some other error. Abort. */ camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, "Could not test lock " "file for %s: %s", source, g_strerror (errno)); break; } /* If the lock file is stale, remove it and try again. */ if (st.st_mtime < now - 60) { unlink (lockfile); continue; } /* Otherwise, sleep and try again. */ sleep (5); } if (!locked) { /* Something has gone awry. */ if (now >= timeout) { camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, "Timed out trying to get " "lock file on %s. Try again " "later.", source); } g_free (lockfile); unlink (locktmpfile); g_free (locktmpfile); close (sfd); close (dfd); unlink (dest); return -1; } /* OK. We have the file locked now. */ /* FIXME: Set a timer to keep the file locked. */ error = FALSE; while (1) { int written = 0; nread = read (sfd, buf, sizeof (buf)); if (nread == 0) break; else if (nread == -1) { camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, "Error reading mail file: %s", g_strerror (errno)); error = TRUE; break; } while (nread) { nwrote = write (dfd, buf + written, nread); if (nwrote == -1) { camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, "Error writing " "mail temp file: %s", g_strerror (errno)); error = TRUE; break; } written += nwrote; nread -= nwrote; } } /* If no errors occurred copying the data, and we successfully * close the destination file, then truncate the source file. * If there is some sort of error, delete the destination file. */ if (!error) { 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)); unlink (dest); error = TRUE; } } else { close (dfd); unlink (dest); } close (sfd); /* Clean up lock files. */ unlink (lockfile); g_free (lockfile); unlink (locktmpfile); g_free (locktmpfile); return error ? -1 : 1; }