diff options
author | Andrew Colin Kissa <andrew@topdog.za.net> | 2016-10-15 18:33:31 +0100 |
---|---|---|
committer | Jeremy Harris <jgh146exb@wizmail.org> | 2016-10-15 18:33:31 +0100 |
commit | 3369a853fbc0fe454ac65fef7adf7e51845ff6a2 (patch) | |
tree | 88661c1de89b0e2e7fb5a15efbadbf35f34f0607 /src | |
parent | 4e192008ab7db2b7b9f157bba50f71a46e1a0cd9 (diff) |
New: queuefile transport, under EXPERIMENTAL_QUEUEFILE
Diffstat (limited to 'src')
-rwxr-xr-x | src/scripts/MakeLinks | 4 | ||||
-rw-r--r-- | src/src/EDITME | 3 | ||||
-rw-r--r-- | src/src/config.h.defaults | 1 | ||||
-rw-r--r-- | src/src/drtables.c | 18 | ||||
-rw-r--r-- | src/src/exim.c | 6 | ||||
-rw-r--r-- | src/src/functions.h | 1 | ||||
-rw-r--r-- | src/src/queue.c | 2 | ||||
-rw-r--r-- | src/src/transports/Makefile | 3 | ||||
-rw-r--r-- | src/src/transports/queuefile.c | 339 | ||||
-rw-r--r-- | src/src/transports/queuefile.h | 29 |
10 files changed, 402 insertions, 4 deletions
diff --git a/src/scripts/MakeLinks b/src/scripts/MakeLinks index 86f748c15..b710c2fd8 100755 --- a/src/scripts/MakeLinks +++ b/src/scripts/MakeLinks @@ -59,8 +59,8 @@ cd .. mkdir transports cd transports for f in README Makefile appendfile.h appendfile.c autoreply.h \ - autoreply.c lmtp.h lmtp.c pipe.h pipe.c smtp.h smtp.c smtp_socks.c \ - tf_maildir.c tf_maildir.h + autoreply.c lmtp.h lmtp.c pipe.h pipe.c queuefile.c queuefile.h \ + smtp.h smtp.c smtp_socks.c tf_maildir.c tf_maildir.h do ln -s ../../src/transports/$f $f done diff --git a/src/src/EDITME b/src/src/EDITME index 10e5ccacb..e2d8cf921 100644 --- a/src/src/EDITME +++ b/src/src/EDITME @@ -495,6 +495,9 @@ EXIM_MONITOR=eximon.bin # CFLAGS += -I/usr/local/include # LDFLAGS += -llmdb +# Uncomment the following line to add queuefile transport support +# EXPERIMENTAL_QUEUEFILE=yes + ############################################################################### # THESE ARE THINGS YOU MIGHT WANT TO SPECIFY # ############################################################################### diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults index fe5878aaf..bafdc1ba4 100644 --- a/src/src/config.h.defaults +++ b/src/src/config.h.defaults @@ -178,6 +178,7 @@ it's a default value. */ #define EXPERIMENTAL_DSN_INFO #define EXPERIMENTAL_DMARC #define EXPERIMENTAL_LMDB +#define EXPERIMENTAL_QUEUEFILE #define EXPERIMENTAL_SPF #define EXPERIMENTAL_SRS diff --git a/src/src/drtables.c b/src/src/drtables.c index c807d86c7..a3fa328b9 100644 --- a/src/src/drtables.c +++ b/src/src/drtables.c @@ -228,6 +228,10 @@ exim binary. */ #include "transports/pipe.h" #endif +#ifdef EXPERIMENTAL_QUEUEFILE +#include "transports/queuefile.h" +#endif + #ifdef TRANSPORT_SMTP #include "transports/smtp.h" #endif @@ -389,6 +393,20 @@ transport_info transports_available[] = { TRUE /* local flag */ }, #endif +#ifdef EXPERIMENTAL_QUEUEFILE + { + US"queuefile", /* driver name */ + queuefile_transport_options, /* local options table */ + &queuefile_transport_options_count, /* number of entries */ + &queuefile_transport_option_defaults, /* private options defaults */ + sizeof(queuefile_transport_options_block), /* size of private block */ + queuefile_transport_init, /* init entry point */ + queuefile_transport_entry, /* main entry point */ + NULL, /* no tidyup entry */ + NULL, /* no closedown entry */ + TRUE /* local flag */ + }, +#endif #ifdef TRANSPORT_SMTP { US"smtp", /* driver name */ diff --git a/src/src/exim.c b/src/src/exim.c index 905314cad..3fdfa62b5 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -843,6 +843,9 @@ fprintf(f, "Support for:"); #ifdef EXPERIMENTAL_LMDB fprintf(f, " Experimental_LMDB"); #endif +#ifdef EXPERIMENTAL_QUEUEFILE + fprintf(f, " Experimental_QUEUEFILE"); +#endif #ifdef EXPERIMENTAL_SPF fprintf(f, " Experimental_SPF"); #endif @@ -996,6 +999,9 @@ fprintf(f, "Transports:"); #ifdef TRANSPORT_PIPE fprintf(f, " pipe"); #endif +#ifdef EXPERIMENTAL_QUEUEFILE + fprintf(f, " queuefile"); +#endif #ifdef TRANSPORT_SMTP fprintf(f, " smtp"); #endif diff --git a/src/src/functions.h b/src/src/functions.h index 03751a8e0..b23c1441b 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -415,6 +415,7 @@ extern int spam(const uschar **); extern FILE *spool_mbox(unsigned long *, const uschar *); #endif extern BOOL spool_move_message(uschar *, uschar *, uschar *, uschar *); +extern uschar *spool_dname(const uschar *, uschar *); extern uschar *spool_fname(const uschar *, uschar *, uschar *, uschar *); extern uschar *spool_sname(const uschar *, uschar *); extern int spool_open_datafile(uschar *); diff --git a/src/src/queue.c b/src/src/queue.c index 969eceba4..eddb8d85c 100644 --- a/src/src/queue.c +++ b/src/src/queue.c @@ -21,7 +21,7 @@ spool_pname_buf(uschar * buf, int len) snprintf(CS buf, len, "%s/%s/input", spool_directory, queue_name); } -static uschar * +uschar * spool_dname(const uschar * purpose, uschar * subdir) { return string_sprintf("%s/%s/%s/%s", diff --git a/src/src/transports/Makefile b/src/src/transports/Makefile index 25973d5df..4eea141ec 100644 --- a/src/src/transports/Makefile +++ b/src/src/transports/Makefile @@ -2,7 +2,7 @@ # calling it transports.a. This is called from the main make file, after cd'ing # to the transports subdirectory. -OBJ = appendfile.o autoreply.o lmtp.o pipe.o smtp.o smtp_socks.o tf_maildir.o +OBJ = appendfile.o autoreply.o lmtp.o pipe.o queuefile.o smtp.o smtp_socks.o tf_maildir.o transports.a: $(OBJ) @$(RM_COMMAND) -f transports.a @@ -18,6 +18,7 @@ appendfile.o: $(HDRS) appendfile.c appendfile.h tf_maildir.h autoreply.o: $(HDRS) autoreply.c autoreply.h lmtp.o: $(HDRS) lmtp.c lmtp.h pipe.o: $(HDRS) pipe.c pipe.h +queuefile.o: $(HDRS) queuefile.c queuefile.h smtp.o: $(HDRS) smtp.c smtp.h smtp_socks.o: $(HDRS) smtp_socks.c smtp.h diff --git a/src/src/transports/queuefile.c b/src/src/transports/queuefile.c new file mode 100644 index 000000000..3f711c145 --- /dev/null +++ b/src/src/transports/queuefile.c @@ -0,0 +1,339 @@ +/************************************************* +* Exim - an Internet mail transport agent * +*************************************************/ + +/* Copyright (c) Andrew Colin Kissa <andrew@topdog.za.net> 2016 */ +/* Copyright (c) University of Cambridge 2016 */ +/* See the file NOTICE for conditions of use and distribution. */ + + +#include "../exim.h" +#include "queuefile.h" + +/* Options specific to the appendfile transport. They must be in alphabetic +order (note that "_" comes before the lower case letters). Some of them are +stored in the publicly visible instance block - these are flagged with the +opt_public flag. */ + +optionlist queuefile_transport_options[] = { + { "directory", opt_stringptr, + (void *)offsetof(queuefile_transport_options_block, dirname) }, +}; + +/* Size of the options list. An extern variable has to be used so that its +address can appear in the tables drtables.c. */ + +int queuefile_transport_options_count = + sizeof(queuefile_transport_options) / sizeof(optionlist); + +/* Default private options block for the appendfile transport. */ + +queuefile_transport_options_block queuefile_transport_option_defaults = { + NULL, /* dirname */ +}; + +/************************************************* +* Initialization entry point * +*************************************************/ + +void queuefile_transport_init(transport_instance *tblock) +{ +queuefile_transport_options_block *ob = + (queuefile_transport_options_block *)(tblock->options_block); + +if (ob->dirname == NULL) + log_write(0, LOG_PANIC_DIE | LOG_CONFIG, + "directory must be set for the %s transport", tblock->name); +} + +/* This function will copy from a file to another + +Arguments: + to_fd FILE to write to (the destination queue file) + from_fd FILE to read from (the spool queue file) + +Returns: TRUE if all went well, FALSE otherwise +*/ + +static BOOL copy_spool_file (FILE *to_fd, FILE *from_fd) +{ +int i, j; +uschar buffer[16384]; + +rewind(from_fd); + +do + { + j = fread(buffer, 1, sizeof(buffer), from_fd); + if (j > 0) + { + i = fwrite(buffer, j, 1, to_fd); + if (i != 1) + return FALSE; + } + } +while (j > 0); +return TRUE; +} + +/* This function performs the actual copying of the header +and data files to the destination directory + +Arguments: + tname uschar the transport name + addr address_item being processed + sdfd int Source directory fd + ddfd int Destination directory fd + suffix uschar file suffix + dirname uschar Destination directory + link_file BOOL use linkat instead of data copy + is_data_file BOOL the file is a data file not a header file + dst_file FILE to write to + src_file FILE to read from + +Returns: TRUE if all went well, FALSE otherwise +*/ + +static BOOL copy_spool_files(uschar *tname, address_item *addr, + int sdfd, int ddfd, uschar *suffix, uschar *dirname, BOOL link_file, + BOOL is_data_file, FILE *dst_file, FILE *src_file) +{ +int dstfd, srcfd; +/* +uschar message_subdir[2]; +message_subdir[1] = '\0'; +message_subdir[0] = split_spool_directory? message_id[5] : 0; +*/ +uschar *filename = string_sprintf("%s-%s", message_id, suffix); +/* +uschar *srcpath = string_sprintf("%s/%s/%s/%s-%s", spool_directory, + US"input", message_subdir, message_id, suffix); +*/ +uschar *srcpath = spool_fname(US"input", message_subdir, message_id, suffix); +uschar *dstpath = string_sprintf("%s/%s-%s", dirname, message_id, suffix); + +if (link_file) + { + /* use linkat */ + DEBUG(D_transport) + debug_printf("%s transport, linking %s => %s\n", tname, + srcpath, dstpath); + if (linkat(sdfd, CCS filename, ddfd, CCS filename, 0) < 0) + return FALSE; + return TRUE; + } +else + { + /* use data copy */ + DEBUG(D_transport) + debug_printf("%s transport, copying %s => %s\n", tname, + srcpath, dstpath); + if ((dstfd = + openat(ddfd, CCS filename, O_RDWR|O_CREAT|O_EXCL, SPOOL_MODE)) < 0) + { + addr->transport_return = DEFER; + addr->basic_errno = errno; + addr->message = string_sprintf("%s transport opening file: %s " + "failed with error: %s", tname, dstpath, strerror(errno)); + return FALSE; + } + + fchmod(dstfd, SPOOL_MODE); + + if ((dst_file = fdopen(dstfd, "wb")) < 0) + { + addr->transport_return = DEFER; + addr->basic_errno = errno; + addr->message = string_sprintf("%s transport opening file fd: %s " + "failed with error: %s", tname, dstpath, strerror(errno)); + (void)close(dstfd); + return FALSE; + } + + if (is_data_file) + srcfd = deliver_datafile; + else + { + if ((srcfd = openat(sdfd, CCS filename, O_RDONLY)) < 0) + { + addr->transport_return = DEFER; + addr->basic_errno = errno; + addr->message = string_sprintf("%s transport opening file: %s " + "failed with error: %s", tname, srcpath, strerror(errno)); + return FALSE; + } + } + + if ((src_file = fdopen(srcfd, "rb")) < 0) + { + addr->transport_return = DEFER; + addr->basic_errno = errno; + addr->message = string_sprintf("%s transport opening file fd: " + "%s failed with error: %s", tname, srcpath, strerror(errno)); + if (!is_data_file) (void)close(srcfd); + return FALSE; + } + + if (!copy_spool_file(dst_file, src_file)) + { + addr->transport_return = DEFER; + addr->message = string_sprintf("%s transport creating file: " + "%s failed with error: %s", tname, dstpath, strerror(errno)); + return FALSE; + } + + if (!is_data_file) + { + (void)fclose(src_file); + src_file = NULL; + } + + (void)fclose(dst_file); + dst_file = NULL; + + } /* end data copy */ + +return TRUE; +} + +/************************************************* +* Main entry point * +*************************************************/ + +/* This transport always returns FALSE, indicating that the status in +the first address is the status for all addresses in a batch. */ + +BOOL queuefile_transport_entry(transport_instance *tblock, + address_item *addr) +{ +BOOL link_file; +BOOL is_data_file; +uschar *sourcedir; +struct stat dstatbuf; +struct stat sstatbuf; +FILE *dst_file = NULL; +FILE *src_file = NULL; +/* uschar message_subdir[2]; */ +int ddfd, sdfd, dfd_oflags; +queuefile_transport_options_block *ob = + (queuefile_transport_options_block *)(tblock->options_block); + +DEBUG(D_transport) + debug_printf("%s transport entered\n", tblock->name); + +# ifndef O_DIRECTORY +# define O_DIRECTORY 0 +# endif + +dfd_oflags = O_RDONLY|O_DIRECTORY; +#ifdef O_NOFOLLOW +dfd_oflags |= O_NOFOLLOW; +#endif + +if (ob->dirname[0] != '/') + { + addr->transport_return = PANIC; + addr->message = string_sprintf("%s transport directory: " + "%s is not absolute", tblock->name, ob->dirname); + return FALSE; + } + +if ((ddfd = Uopen(ob->dirname, dfd_oflags, 0)) < 0) + { + addr->transport_return = PANIC; + addr->basic_errno = errno; + addr->message = string_sprintf("%s transport accessing directory: %s " + "failed with error: %s", tblock->name, ob->dirname, strerror(errno)); + return FALSE; + } + + +if ((fstat(ddfd, &dstatbuf)) < 0) + { + addr->transport_return = PANIC; + addr->basic_errno = errno; + addr->message = string_sprintf("%s transport fstat on directory fd: " + "%s failed with error: %s", tblock->name, ob->dirname, strerror(errno)); + goto RETURN; + } + +sourcedir = spool_dname(US"input", message_subdir); +/* +message_subdir[1] = '\0'; +message_subdir[0] = split_spool_directory? message_id[5] : 0; +sourcedir = string_sprintf("%s/%s/%s", spool_directory, + US"input", message_subdir); +*/ + +if ((sdfd = Uopen(sourcedir, dfd_oflags, 0)) < 0) + { + addr->transport_return = PANIC; + addr->basic_errno = errno; + addr->message = string_sprintf("%s transport accessing directory: %s " + "failed with error: %s", tblock->name, sourcedir, strerror(errno)); + goto RETURN; + } + +if ((fstat(sdfd, &sstatbuf)) < 0) + { + addr->transport_return = PANIC; + addr->basic_errno = errno; + addr->message = string_sprintf("%s transport fstat on directory fd: " + "%s failed with error: %s", tblock->name, sourcedir, strerror(errno)); + goto RETURN; + } + +if (dont_deliver) + { + DEBUG(D_transport) + debug_printf("*** delivery by %s transport bypassed by -N option\n", + tblock->name); + addr->transport_return = OK; + goto RETURN; + } + +/* process the header file */ +DEBUG(D_transport) + debug_printf("%s transport, copying header file\n", tblock->name); + +is_data_file = FALSE; +link_file = (dstatbuf.st_dev == sstatbuf.st_dev); + +if ((copy_spool_files(tblock->name, addr, sdfd, ddfd, US"H", ob->dirname, + link_file, is_data_file, dst_file, src_file)) == FALSE) + goto RETURN; + +/* process the data file */ +DEBUG(D_transport) + debug_printf("%s transport, copying data file\n", tblock->name); + +is_data_file = TRUE; + +if ((copy_spool_files(tblock->name, addr, sdfd, ddfd, US"D", ob->dirname, + link_file, is_data_file, dst_file, src_file)) == FALSE) + { + DEBUG(D_transport) + debug_printf("%s transport, copying data file failed, " + "unlinking the header file\n", tblock->name); + Uunlink(string_sprintf("%s/%s-H", ob->dirname, message_id)); + goto RETURN; + } + +(void)close(ddfd); +(void)close(sdfd); + +DEBUG(D_transport) + debug_printf("%s transport succeeded\n", tblock->name); + +addr->transport_return = OK; + +RETURN: +if (dst_file) (void)fclose(dst_file); +if (src_file && !is_data_file) (void)fclose(src_file); +if (ddfd) (void)close(ddfd); +if (sdfd) (void)close(sdfd); + +/* A return of FALSE means that if there was an error, a common error was +put in the first address of a batch. */ +return FALSE; +} diff --git a/src/src/transports/queuefile.h b/src/src/transports/queuefile.h new file mode 100644 index 000000000..0e45b51b0 --- /dev/null +++ b/src/src/transports/queuefile.h @@ -0,0 +1,29 @@ +/************************************************* +* Exim - an Internet mail transport agent * +*************************************************/ + +/* Copyright (c) Andrew Colin Kissa <andrew@topdog.za.net> 2016 */ +/* Copyright (c) University of Cambridge 2016 */ +/* See the file NOTICE for conditions of use and distribution. */ + +/* Private structure for the private options. */ + +typedef struct { + uschar *dirname; +} queuefile_transport_options_block; + +/* Data for reading the private options. */ + +extern optionlist queuefile_transport_options[]; +extern int queuefile_transport_options_count; + +/* Block containing default values. */ + +extern queuefile_transport_options_block queuefile_transport_option_defaults; + +/* The main and init entry points for the transport */ + +extern BOOL queuefile_transport_entry(transport_instance *, address_item *); +extern void queuefile_transport_init(transport_instance *); + +/* End of transports/queuefile.h */ |