-/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */
-
-/*
- * Copyright (c) 2010 Ali Polatel <alip@exherbo.org>
+/* notmuch-deliver - If you make the user a promise... make sure you deliver it!
+ *
+ * Copyright © 2010 Ali Polatel
* Based in part upon deliverquota of maildrop which is:
* Copyright 1998 - 2009 Double Precision, Inc.
*
- * This file is part of the notmuch-deliver. notmuch-deliver is free software;
- * you can redistribute it and/or modify it under the terms of the GNU General
- * Public License version 2, as published by the Free Software Foundation.
+ * 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 3 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.
*
- * notmuch-deliver 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, see http://www.gnu.org/licenses/ .
*
- * 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
+ * Author: Ali Polatel <polatel@gmail.com>
*/
#ifdef HAVE_CONFIG_H
#include <errno.h>
#include <stdio.h>
#include <string.h>
+#ifdef HAVE_UNISTD_H
#include <unistd.h>
+#endif
+#ifdef HAVE_SPLICE
+#include <fcntl.h>
+#endif
#ifdef HAVE_SYSEXITS_H
#include <sysexits.h>
return TRUE;
}
+#ifdef HAVE_SPLICE
static int
-save_maildir(int fdin, const char *dir, int auto_create, char **path)
+save_splice(int fdin, int fdout)
{
- int fd, ret, written;
- char buf[4096], *p;
- struct maildir_tmpcreate_info info;
+ int ret, written, pfd[2];
- maildir_tmpcreate_init(&info);
- info.openmode = 0666;
- info.maildir = dir;
- info.doordie = 1;
+ if (pipe(pfd) < 0) {
+ g_critical("Failed to create pipe: %s", g_strerror(errno));
+ return EX_IOERR;
+ }
- while ((fd = maildir_tmpcreate_fd(&info)) < 0)
- {
- if (errno == ENOENT && auto_create && maildir_mkdir(dir) == 0)
- {
- auto_create = 0;
- continue;
+ for (;;) {
+ ret = splice(fdin, NULL, pfd[1], NULL, 4096, 0);
+ if (!ret)
+ break;
+ if (ret < 0) {
+ g_critical("Splicing data from standard input failed: %s",
+ g_strerror(errno));
+ close(pfd[0]);
+ close(pfd[1]);
+ return EX_IOERR;
}
- g_critical("Failed to create temporary file `%s': %s",
- info.tmpname, g_strerror(errno));
- return EX_TEMPFAIL;
+ do {
+ written = splice(pfd[0], NULL, fdout, NULL, ret, 0);
+ if (!written) {
+ g_critical("Splicing data to temporary file failed: internal error");
+ close(pfd[0]);
+ close(pfd[1]);
+ return EX_IOERR;
+ }
+ if (written < 0) {
+ g_critical("Splicing data to temporary file failed: %s",
+ g_strerror(errno));
+ close(pfd[0]);
+ close(pfd[1]);
+ return EX_IOERR;
+ }
+ ret -= written;
+ } while (ret);
}
- g_debug("Reading from standard input and writing to `%s'", info.tmpname);
+ close(pfd[0]);
+ close(pfd[1]);
+ return 0;
+}
+#endif /* HAVE_SPLICE */
+
+static int
+save_readwrite(int fdin, int fdout)
+{
+ int ret, written;
+ char buf[4096], *p;
+
for (;;) {
ret = read(fdin, buf, 4096);
if (!ret)
if (ret < 0) {
if (errno == EINTR)
continue;
- g_critical("Reading from standard input failed: %s", g_strerror(errno));
- goto fail;
+ g_critical("Reading from standard input failed: %s",
+ g_strerror(errno));
+ return EX_IOERR;
}
p = buf;
do {
- written = write(fd, p, ret);
+ written = write(fdout, p, ret);
if (!written)
- goto fail;
+ return EX_IOERR;
if (written < 0) {
if (errno == EINTR)
continue;
- g_critical("Writing to temporary file `%s' failed: %s",
- info.tmpname, g_strerror(errno));
- goto fail;
+ g_critical("Writing to temporary file failed: %s",
+ g_strerror(errno));
+ return EX_IOERR;
}
p += written;
ret -= written;
} while (ret);
}
- close(fd);
+ return 0;
+}
+
+static int
+save_maildir(int fdin, const char *dir, int auto_create, char **path)
+{
+ int fdout, ret;
+ struct maildir_tmpcreate_info info;
+
+ maildir_tmpcreate_init(&info);
+ info.openmode = 0666;
+ info.maildir = dir;
+ info.doordie = 1;
+
+ while ((fdout = maildir_tmpcreate_fd(&info)) < 0)
+ {
+ if (errno == ENOENT && auto_create && maildir_mkdir(dir) == 0)
+ {
+ auto_create = 0;
+ continue;
+ }
+
+ g_critical("Failed to create temporary file `%s': %s",
+ info.tmpname, g_strerror(errno));
+ return EX_TEMPFAIL;
+ }
+
+ g_debug("Reading from standard input and writing to `%s'", info.tmpname);
+#ifdef HAVE_SPLICE
+ ret = g_getenv("NOTMUCH_DELIVER_NO_SPLICE")
+ ? save_readwrite(fdin, fdout)
+ : save_splice(fdin, fdout);
+#else
+ ret = save_readwrite(fdin, fdout);
+#endif /* HAVE_SPLICE */
+ if (ret)
+ goto fail;
+
+ close(fdout);
g_debug("Moving `%s' to `%s'", info.tmpname, info.newname);
if (maildir_movetmpnew(info.tmpname, info.newname)) {
g_critical("Moving `%s' to `%s' failed: %s",
case NOTMUCH_STATUS_SUCCESS:
break;
case NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID:
+ g_debug("Message is a duplicate, not adding tags");
return 0;
default:
g_warning("Failed to add `%s' to notmuch database: %s",
GOptionContext *ctx;
GError *error = NULL;
notmuch_database_t *db;
+ notmuch_status_t status;
ctx = g_option_context_new("[FOLDER]");
g_option_context_add_main_entries(ctx, options, PACKAGE);
" "PACKAGE" uses notmuch's configuration file to determine database path and\n"
" initial tags to add to new messages. You may set NOTMUCH_CONFIG environment\n"
" variable to specify an alternative configuration file.\n"
+ "\nEnvironment:\n"
+ " NOTMUCH_CONFIG: Path to notmuch configuration file\n"
+ " NOTMUCH_DELIVER_NO_SPLICE: Don't use splice() even if it's available\n"
"\nExit codes:\n"
" 0 => Successful run\n"
" 64 => Usage error\n"
}
g_free(conf_path);
+ if ((argc - 1) > 1) {
+ g_critical("Won't deliver to %d folders", argc - 1);
+ return EX_USAGE;
+ }
+
if (argc > 1) {
folder = g_strdup_printf("%s%s", opt_folder ? "." : "", argv[1]);
maildir = g_build_filename(db_path, folder, NULL);
maildir = g_strdup(db_path);
g_debug("Opening notmuch database `%s'", db_path);
- db = notmuch_database_open(db_path, NOTMUCH_DATABASE_MODE_READ_WRITE);
+ status = notmuch_database_open(db_path, NOTMUCH_DATABASE_MODE_READ_WRITE,
+ &db);
+ if (status) {
+ g_critical("Failed to open database `%s': %s",
+ db_path, notmuch_status_to_string(status));
+ g_free(maildir);
+ return EX_SOFTWARE;
+ }
g_free(db_path);
if (db == NULL)
return EX_SOFTWARE;
g_free(maildir);
if ((ret = save_database(db, mail, conf_tags)) != 0 && opt_fatal) {
+ g_warning("Unlinking `%s'", mail);
unlink(mail);
return ret;
}
g_strfreev(opt_rtags);
g_free(mail);
- notmuch_database_close(db);
+ notmuch_database_destroy(db);
return 0;
}