extsmail-1.4004075500017500000012000000000001157715611100124215ustar00ltrattusersextsmail-1.4/CREDITS010064400017500000012000000004661157715611100135230ustar00ltrattusers================================================================================ extsmail credits ================================================================================ I thank the following people for their role in extsmail's development (in alphabetical order): Geerd-Dietger "Didi" Hoffmann extsmail-1.4/extsmaild.1010064400017500000012000000042531157715611100145550ustar00ltrattusers.\" Copyright (C)2008 Laurence Tratt http://tratt.net/laurie/ .\" .\" Permission is hereby granted, free of charge, to any person obtaining a copy .\" of this software and associated documentation files (the "Software"), to .\" deal in the Software without restriction, including without limitation the .\" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or .\" sell copies of the Software, and to permit persons to whom the Software is .\" furnished to do so, subject to the following conditions: .\" .\" The above copyright notice and this permission notice shall be included in .\" all copies or substantial portions of the Software. .\" .\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR .\" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, .\" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE .\" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER .\" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING .\" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS .\" IN THE SOFTWARE. .Dd $Mdocdate: November 2 2008 $ .Dt EXTSMAIL 1 .Os .Sh NAME .Nm extsmaild .Nd robust sending of e-mail to external commands .Sh SYNOPSIS .Nm extsmaild .Op Fl m Ar mode .Sh DESCRIPTION .Nm sends messages spooled by .Xr extsmail 1 . If called without .Fl m specified, or if .Fl m Ar batch is specified, .Nm operates in batch mode, running as a foreground process and terminating after trying to send all messages; it returns .Er 0 if all messages were sent successfully, or .Er 1 if any messages remain unsent. .Pp If called with the .Fl m Ar daemon switch, .Nm operates as a long running daemon; it monitors the spool directory and when it notices changes to the directory (or a fixed period has elapsed) it tries to send any unsent messages. This means that even on unreliable networks, .Nm can easily be left alone and relied upon to ultimately send all messages - without manual intervention. .Sh SEE ALSO .Xr extsmail.conf 5 , .Xr extsmail.externals 5 , .Xr extsmail 1 .Sh AUTHORS .An -nosplit .Nm was written by .An Laurence Tratt Aq http://tratt.net/laurie/extsmail-1.4/extsmail.externals.5010064400017500000012000000103171157715611100164170ustar00ltrattusers.\" Copyright (C)2008 Laurence Tratt http://tratt.net/laurie/ .\" .\" Permission is hereby granted, free of charge, to any person obtaining a copy .\" of this software and associated documentation files (the "Software"), to .\" deal in the Software without restriction, including without limitation the .\" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or .\" sell copies of the Software, and to permit persons to whom the Software is .\" furnished to do so, subject to the following conditions: .\" .\" The above copyright notice and this permission notice shall be included in .\" all copies or substantial portions of the Software. .\" .\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR .\" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, .\" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE .\" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER .\" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING .\" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS .\" IN THE SOFTWARE. .Dd $Mdocdate: November 2 2008 $ .Dt EXTSMAIL.EXTERNALS 1 .Os .Sh NAME .Nm extsmail.externals .Nd configure which external commands to robustly send e-mail via .Sh DESCRIPTION .Nm is used to configure .Xr extsmaild 1 . It consists of one or more .Em group declarations. Each group consists of zero or more .Em match / .Em reject clauses followed by one or more .Em external declarations. An external consists of one or more assignments of .Em key = value pairs. .Pp When sending messages .Xr extsmaild 1 first searches through the externals file, in order, for a group whose match / reject clauses match the message in question. If a group does not contain any such clauses it automatically matches all messages. Match / reject clauses currently match only against headers, and use standard POSiX extended regular expressions (see .Xr re_format 7 for more details). .Xr extsmaild 1 then tries each external in the group, in order, to send the message successfully. .Pp The grammar for this file is as follows: .Bd -literal -offset indent group ::= { matches* external+ } matches ::= match | reject match ::= MATCH HEADER string reject ::= REJECT HEADER string external ::= EXTERNAL ID { defn+ } defn ::= ID = STRING | ID = TIME TIME ::= [0-9]+[dhms] .Ed .Pp Valid assignments within an external are: .Bl -tag -width Ds .It sendmail Defines the external shell command used to send e-mail. .It timeout If .Xr extsmaild 1 is executed in daemon mode, this value defines the length of time that .Xr extsmaild 1 will retry this external before giving up and trying the next external in the group. Times are specified as a number followed by .Em d (days), .Em h (hours) .Em m (minutes), or .Em s (seconds). If .Xr extsmaild 1 is executed in batch mode, the .Em timeout value is ignored. .El .Sh FILES The .Em extsmail configuration file is searched for, in order, in the following locations: .Pp .Bl -tag -width Ds -compact .It ~/.extsmail/externals Per-user configuration. .Pp .It /etc/extsmail/externals System-wide configuration. .El .Sh EXAMPLES The simplest externals file sending e-mail via .Xr ssh 1 looks as follows: .Bd -literal -offset indent group { external mymachine { sendmail = "/usr/bin/ssh -q -C -l user mymachine.net /usr/sbin/sendmail" } } .Ed where .Em mymachine is a human-friendly name given to an external (it does not effect processing), and .Em user is the username on the remote machine .Em mymachine.net . .Pp A more complex example using multiple groups, message matching, and multiple external commands looks as follows: .Bd -literal -offset indent group { match header "^To:.*@foo.com" external foo { sendmail = "/usr/bin/ssh -q -C -l user shell.foo.com /usr/sbin/sendmail" } } group { external mymachine { sendmail = "/usr/bin/ssh -q -C -l user mymachine.net /usr/sbin/sendmail" } external bk { sendmail = "/usr/bin/ssh -q -C -l user bk.mymachine.net /usr/sbin/sendmail" } } .Ed .Sh SEE ALSO .Xr extsmail 1 , .Xr extsmail.conf 5 , .Xr extsmaild 1 .Sh AUTHORS .An Laurence Tratt Aq http://tratt.net/laurie/extsmail-1.4/extsmail.conf.5010064400017500000012000000036751157715611100153500ustar00ltrattusers.\" Copyright (C)2008 Laurence Tratt http://tratt.net/laurie/ .\" .\" Permission is hereby granted, free of charge, to any person obtaining a copy .\" of this software and associated documentation files (the "Software"), to .\" deal in the Software without restriction, including without limitation the .\" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or .\" sell copies of the Software, and to permit persons to whom the Software is .\" furnished to do so, subject to the following conditions: .\" .\" The above copyright notice and this permission notice shall be included in .\" all copies or substantial portions of the Software. .\" .\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR .\" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, .\" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE .\" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER .\" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING .\" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS .\" IN THE SOFTWARE. .Dd $Mdocdate: November 2 2008 $ .Dt EXTSMAIL.CONF 1 .Os .Sh NAME .Nm extsmail.conf .Nd configure robust sending of e-mail to external commands .Sh DESCRIPTION .Nm is used to configure .Xr extsmail 1 and .Xr extsmaild 1 . It consists of one or more .Em key = value pairs. The following variables are defined: .Bl -tag -width Ds .It spool_dir Sets the location of the spool directory used by .Xr extsmail 1 and .Xr extsmaild 1 . .El .Sh FILES The .Em extsmail configuration file is searched for, in order, in the following locations: .Pp .Bl -tag -width Ds -compact .It ~/.extsmail/conf Per-user .Em extsmail configuration. .Pp .It /etc/extsmail/conf System-wide .Em extsmail configuration. .El .Sh SEE ALSO .Xr extsmail 1 , .Xr extsmail.externals 5 , .Xr extsmaild 1 .Sh AUTHORS .An Laurence Tratt Aq http://tratt.net/laurie/extsmail-1.4/extsmail.c010064400017500000012000000101421157715611100144650ustar00ltrattusers// Copyright (C)2008 Laurence Tratt http://tratt.net/laurie/ // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. #include "Config.h" #include #include #include #include #include #include #include #include #include #include "conf.h" #include "common.h" extern char* __progname; int main(int argc, char** argv) { Conf *conf = read_conf(); // Check that everything to do with the spool dir is OK. if (!check_spool_dir(conf)) exit(1); // Create the spool file. char *sp; // spool path if (asprintf(&sp, "%s%s%s%sXXXXXXXXXX", conf->spool_dir, DIR_SEP, MSGS_DIR, DIR_SEP) == -1) { errx(1, "main: asprintf: unable to allocate memory"); } int sfd; if ((sfd = mkstemp(sp)) == -1) err(1, "mkstemp: when creating spool file %s", sp); // We immediately try to gain an exclusive lock on the newly created spool // file. If, in between the spool file being created, and us gaining the // lock extsmaild gains a lock it will notice that the file is currently // 0 bytes long, and that therefore the file is incomplete. extsmaild will // then relinquish its lock, allowing us to gain it and write the file in // full. if (flock(sfd, LOCK_EX) == -1) err(1, "flock: when locking spool file %s", sp); // Open the spool file for writing. The format of the spool file is: // // 1) The file format version number (of the format "v1" etc.) // 2) Newline // 3) The number of command line arguments // 4) Newline // 5) Each command line argument, with the format "\n\n". // 6) The mail contents as read from stdin FILE *sf; if ((sf = fdopen(sfd, "w")) == NULL) err(1, "main: fdopen"); # define SPOOL_WRITE(fmt, args...) if (fprintf(sf, fmt, ##args) == -1) \ err(1, "%s: When writing to spool file", sp) // Write out the file format version number SPOOL_WRITE("%s\n", VERSION1_ID); // Write out all the command-line args SPOOL_WRITE("%d\n", argc - 1); for (int i = 1; i < argc; i += 1) SPOOL_WRITE("%zd\n%s\n", strlen(argv[i]), argv[i]); # define BUF_SIZE 1024 char buf[BUF_SIZE]; size_t total_nr = 0; // All bytes read from stdin. while (1) { size_t nr; // Number of bytes read if ((nr = fread(buf, 1, BUF_SIZE, stdin)) < BUF_SIZE && ferror(stdin)) { errx(1, "main: ferror"); } if (fwrite(buf, 1, nr, sf) < nr) errx(1, "main: fwrite: when writing to spool file"); total_nr += nr; if (feof(stdin) != 0) break; } fflush(sf); fclose(sf); // If we didn't read any bytes in from stdin, then we remove the spool file // since there's no message to send. This is designed to prevent // annoying-ness when extsmail is incorrectly called and ctrl-D pressed // immediately. if (total_nr == 0) unlink(sp); flock(sfd, LOCK_UN); close(sfd); return 0; } extsmail-1.4/extsmail.1010064400017500000012000000033621157715611100144110ustar00ltrattusers.\" Copyright (C)2008 Laurence Tratt http://tratt.net/laurie/ .\" .\" Permission is hereby granted, free of charge, to any person obtaining a copy .\" of this software and associated documentation files (the "Software"), to .\" deal in the Software without restriction, including without limitation the .\" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or .\" sell copies of the Software, and to permit persons to whom the Software is .\" furnished to do so, subject to the following conditions: .\" .\" The above copyright notice and this permission notice shall be included in .\" all copies or substantial portions of the Software. .\" .\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR .\" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, .\" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE .\" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER .\" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING .\" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS .\" IN THE SOFTWARE. .Dd $Mdocdate: November 2 2008 $ .Dt EXTSMAIL 1 .Os .Sh NAME .Nm extsmail .Nd robust sending of e-mail to external commands .Sh SYNOPSIS .Nm extsmail .Op Ar options ... .Sh DESCRIPTION .Nm masquerades as the standard UNIX .Xr sendmail 1 when messages are sent; effectively, it notes the command line switches passed to it, and puts those, and the message contents read from stdin, into a file in a spool directory for later sending with .Xr extsmaild 1 . .Sh SEE ALSO .Xr extsmail.conf 5 , .Xr extsmail.externals 5 , .Xr extsmaild 1 .Sh AUTHORS .An -nosplit .Nm was written by .An Laurence Tratt Aq http://tratt.net/laurie/extsmail-1.4/externals_tokenizer.l010064400017500000012000000075721157715611100167640ustar00ltrattusers%{ // Copyright (C)2008 Laurence Tratt http://tratt.net/laurie/ // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. #include "Config.h" #include #include #include #include #include #include #include "conf.h" #include "common.h" #include "externals.h" #include "externals_parser.tab.h" char *mk_str(char *); %} %% \/\/.*$ { } [ \t\n]+ { } "=" { return TASSIGN; } "{" { return TLCB; } "}" { return TRCB; } "group" { return TGROUP; } "external" { return TEXTERNAL; } "match" { return TMATCH; } "reject" { return TREJECT; } "header" { return THEADER; } [_a-zA-Z][_a-zA-Z0-9]* { yyelval.str = mk_str(yytext); return TID; } \" { size_t len = 128; int i = 0; char *buf = malloc(len); int c; while ((c = input()) != 0) { if (c == '"') { break; } // Note we always ensure there's 1 spare byte so that we can null // terminate the string later. if (i + 1 == len) { len *= 2; buf = realloc(buf, len); if (buf == NULL) errx(1, "Out of memory"); } if (c == '\\') { int c2 = input(); switch (c2) { case 'n': buf[i++] = '\n'; break; case 'r': buf[i++] = '\r'; break; case 't': buf[i++] = '\t'; break; default: buf[i++] = c2; break; } } else buf[i++] = c; } buf = realloc(buf, i + 1); // We probably over allocated earlier. if (buf == NULL) errx(1, "Fatal error"); buf[i] = 0; yyelval.str = buf; return TSTRING; } [0-9]+[dhms] { char *ep; errno = 0; time_t num = strtol(yytext, &ep, 10); if (ep != yytext + strlen(yytext) - 1) errx(1, "Invalid number '%.*s'\n", (int) strlen(yytext) - 1, yytext); if (num <= 0 || num >= 365 * 24 * 60 * 60) { errx(1, "Time value '%s' out of range", yytext); } switch (yytext[strlen(yytext) - 1]) { case 'd': yyelval.time = num * 24 * 60 * 60; break; case 'h': yyelval.time = num * 60 * 60; break; case 'm': yyelval.time = num * 60; break; case 's': yyelval.time = num; break; } return TTIME; } . { errx(1, "Illegal char '%s'\n", yytext ); } %% extsmail-1.4/externals_parser.y010064400017500000012000000211461157715611100162540ustar00ltrattusers%{ // Copyright (C)2008 Laurence Tratt http://tratt.net/laurie/ // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. #include "Config.h" #include #include #include #include #include #include #include #include "conf.h" #include "externals.h" extern int yyelex(void); void yyeerror(const char *); extern Group *groups; char *expand_path(const char *); External *_wk_external; Match *add_match(Match_Type, const char *); %} %union { const char *str; time_t time; Match *match; External *external; Group *group; } %token TGROUP %token TEXTERNAL %token TASSIGN %token TLCB %token TRCB %token TMATCH %token TREJECT %token THEADER %token TID %token TSTRING %token TTIME %% start : groups { groups = $1; } ; groups : group groups { $1->next = $2; $$ = $1; } | group ; group : TGROUP TLCB matches externals TRCB { Group *group = malloc(sizeof(Group)); group->matches = $3; group->externals = $4; group->next = NULL; $$ = group; } | TGROUP TLCB externals TRCB { Group *group = malloc(sizeof(Group)); group->matches = NULL; group->externals = $3; group->next = NULL; $$ = group; } ; matches : mr matches { $1->next = $2; $$ = $1; } | mr ; mr : match | reject ; match : TMATCH THEADER TSTRING { Match *m = add_match(MATCH, $3); if (m == NULL) YYABORT; $$ = m; } ; reject : TREJECT THEADER TSTRING { Match *m = add_match(REJECT, $3); if (m == NULL) YYABORT; $$ = m; } ; externals : external externals { External *lhs = $1; External *rhs = $2; lhs->next = rhs; $$ = lhs; } | external ; external : TEXTERNAL TID { _wk_external = malloc(sizeof(External)); _wk_external->name = $2; _wk_external->sendmail = NULL; _wk_external->last_success = 0; _wk_external->timeout = 0; } TLCB defns TRCB { _wk_external->next = NULL; $$ = _wk_external; } ; defns : defn defns | defn ; defn : TID TASSIGN TSTRING { if (strcmp($1, "sendmail") == 0) { if (_wk_external->sendmail != NULL) { warnx("Multiple definitions of 'sendmail' in '%s'", _wk_external->name); YYABORT; } # define IS_WHITESPACE(x) ($3[i] == ' ' || $3[i] == '\t' \ || $3[i] == '\n') int nargv = 0; // Number of arguments int nargv_alloced = 16; char **argv = malloc(sizeof(char *) * nargv_alloced); if (argv == NULL) errx(1, "Unable to allocate memory"); int len = strlen($3); for (int i = 0; i < len; i++) { // Skip whitespace at beginning of arg while (i < len && IS_WHITESPACE(i)) i += 1; if (i == len) break; // Identify an argument. This is nominally a string of non // whitespace characters - unless it's within quotes, when // it can contain whitespace. int start, end; if ($3[i] == '\'') { start = i + 1; i += 1; while (i < len) { if ($3[i] == '\\') i += 2; else if ($3[i] == '\'') break; else i += 1; } if (i == len) end = i; else end = i - 1; } else if ($3[i] == '"') { start = i + 1; i += 1; while (i < len) { if ($3[i] == '\\') i += 2; else if ($3[i] == '"') break; else i += 1; } if (i == len) end = i; else end = i - 1; } else { start = i; while (i < len && ! IS_WHITESPACE(i)) i += 1; end = i; } char *arg = malloc(i - start + 1); memcpy(arg, $3 + start, i - start); arg[i - start] = 0; if (nargv == nargv_alloced) { nargv_alloced *= 2; argv = malloc(sizeof(char *) * nargv_alloced); if (argv == NULL) errx(1, "Unable to allocate memory"); } argv[nargv++] = arg; } _wk_external->sendmail = $3; _wk_external->sendmail_argv = (const char**) argv; _wk_external->sendmail_nargv = nargv; } else if (strcmp($1, "timeout") == 0) { warnx("Value of incorrect type for 'timeout'"); YYABORT; } else { warnx("Unknown externals var '%s'", $1); YYABORT; } free((void *) $1); } | TID TASSIGN TTIME { if (strcmp($1, "timeout") == 0) { _wk_external->timeout = $