Hi Mark, I think this is the patch you need. Please test it.
It can be made smaller, of course. Rename the files was not necessary. Arnt
commit 90f9bfec17aad4044cc1e8df69edf0631d07beea Author: Arnt Gulbrandsen <[email protected]> Date: Thu Jun 7 10:49:20 2012 +0000 rename deliver as aoxdeliver diff --git a/deliver/Jamfile b/deliver/Jamfile index 80fce87..26bd4a1 100644 --- a/deliver/Jamfile +++ b/deliver/Jamfile @@ -3,9 +3,9 @@ SubInclude TOP server ; SubInclude TOP message ; SubInclude TOP db ; -Build deliver : deliver.cpp ; +Build aoxdeliver : aoxdeliver.cpp ; -Program deliver : - deliver database message server mailbox core encodings user +Program aoxdeliver : + aoxdeliver database message server mailbox core encodings user extractors abnf ; diff --git a/deliver/aoxdeliver.cpp b/deliver/aoxdeliver.cpp new file mode 100644 index 0000000..9ee50ff --- /dev/null +++ b/deliver/aoxdeliver.cpp @@ -0,0 +1,257 @@ +// Copyright 2009 The Archiveopteryx Developers <[email protected]> + +#include "scope.h" +#include "event.h" +#include "estring.h" +#include "allocator.h" +#include "stderrlogger.h" +#include "configuration.h" +#include "permissions.h" +#include "logclient.h" +#include "eventloop.h" +#include "injector.h" +#include "mailbox.h" +#include "query.h" +#include "file.h" +#include "user.h" +#include "log.h" +#include "utf.h" + +#include <stdlib.h> +#include <stdio.h> + +// time, ctime +#include <time.h> +// all the exit codes +#include <sysexits.h> + + +static void quit( uint s, const EString & m ) +{ + if ( !m.isEmpty() ) + fprintf( stderr, "aoxdeliver: %s\n", m.cstr() ); + exit( s ); +} + + +class Deliverator + : public EventHandler +{ +public: + Query * q; + Injector * i; + Injectee * m; + UString mbn; + EString un; + Permissions * p; + Mailbox * mb; + + Deliverator( Injectee * message, + const UString & mailbox, const EString & user ) + : q( 0 ), i( 0 ), m( message ), mbn( mailbox ), un( user ), + p( 0 ), mb( 0 ) + { + Allocator::addEternal( this, "deliver object" ); + q = new Query( "select al.mailbox, n.name as namespace, u.login " + "from aliases al " + "join addresses a on (al.address=a.id) " + "left join users u on (al.id=u.alias) " + "left join namespaces n on (u.parentspace=n.id) " + "where (lower(a.localpart)=$1 and lower(a.domain)=$2) " + "or (lower(u.login)=$3)", this ); + if ( user.contains( '@' ) ) { + int at = user.find( '@' ); + q->bind( 1, user.mid( 0, at ).lower() ); + q->bind( 2, user.mid( at + 1 ).lower() ); + } + else { + q->bindNull( 1 ); + q->bindNull( 2 ); + } + q->bind( 3, user.lower() ); + q->execute(); + } + + virtual ~Deliverator() + { + quit( EX_TEMPFAIL, "Delivery object unexpectedly deleted" ); + } + + void execute() + { + if ( q && !q->done() ) + return; + + if ( q && q->done() && !p ) { + Row * r = q->nextRow(); + q = 0; + if ( !r ) + quit( EX_NOUSER, "No such user: " + un ); + if ( !r->isNull( "login" ) && + r->getEString( "login" ) == "anonymous" ) + quit( EX_DATAERR, "Cannot deliver to the anonymous user" ); + if ( mbn.isEmpty() ) { + mb = Mailbox::find( r->getInt( "mailbox" ) ); + } + else { + UString pre; + if ( !r->isNull( "namespace" ) && !mbn.startsWith( "/" ) ) + pre = r->getUString( "namespace" ) + "/" + + r->getUString( "login" ) + "/"; + mb = Mailbox::find( pre + mbn ); + User * u = new User; + UString anyone; + anyone.append( "anyone" ); + u->setLogin( anyone ); + if ( mb ) + p = new Permissions( mb, u, this ); + } + if ( !mb ) + quit( EX_CANTCREAT, "No such mailbox" ); + } + + if ( p && !p->ready() ) + return; + + if ( p && !p->allowed( Permissions::Post ) ) + quit( EX_NOPERM, + "User 'anyone' does not have 'p' right on mailbox " + + mbn.ascii().quoted( '\'' ) ); + + if ( !i ) { + EStringList x; + m->setFlags( mb, &x ); + i = new Injector( this ); + List<Injectee> y; + y.append( m ); + i->addInjection( &y ); + i->execute(); + } + + if ( !i->done() ) + return; + + if ( i->failed() ) + quit( EX_SOFTWARE, "Injection error: " + i->error() ); + + i = 0; + EventLoop::shutdown(); + } +}; + + +int main( int argc, char *argv[] ) +{ + Scope global; + + EString sender; + UString mailbox; + EString recipient; + EString filename; + int verbose = 0; + bool error = false; + + int n = 1; + while ( n < argc ) { + if ( argv[n][0] == '-' ) { + switch ( argv[n][1] ) { + case 'f': + if ( argc - n > 1 ) + sender = argv[++n]; + break; + + case 't': + if ( argc - n > 1 ) { + Utf8Codec c; + mailbox = c.toUnicode( argv[++n] ); + if ( !c.valid() ) + error = true; + } + break; + + case 'v': + { + int i = 1; + while ( argv[n][i] == 'v' ) { + verbose++; + i++; + } + if ( argv[n][i] != '\0' ) + error = true; + } + break; + + default: + error = true; + break; + } + } + else if ( recipient.isEmpty() ) { + recipient = argv[n]; + } + else if ( filename.isEmpty() ) { + filename = argv[n]; + } + else { + error = true; + } + n++; + } + + if ( error || recipient.isEmpty() ) { + fprintf( stderr, + "Syntax: aoxdeliver [-v] [-f sender] recipient [filename]\n" ); + exit( -1 ); + } + + EString contents; + if ( filename.isEmpty() ) { + char s[128]; + while ( fgets( s, 128, stdin ) != 0 ) + contents.append( s ); + } + else { + File message( filename ); + if ( !message.valid() ) { + fprintf( stderr, "Unable to open message file %s\n", + filename.cstr() ); + exit( -1 ); + } + contents = message.contents(); + } + + Configuration::setup( "archiveopteryx.conf" ); + + Injectee * message = new Injectee; + message->parse( contents ); + if ( !message->error().isEmpty() ) { + fprintf( stderr, + "Message parsing failed: %s", message->error().cstr( ) ); + exit( EX_DATAERR ); + } + + if ( verbose > 0 ) + fprintf( stderr, "Sending to <%s>\n", recipient.cstr() ); + + EventLoop::setup(); + Database::setup( 1 ); + Log * l = new Log; + Allocator::addEternal( l, "delivery log" ); + global.setLog( l ); + Allocator::addEternal( new StderrLogger( "aoxdeliver", verbose ), + "log object" ); + + Configuration::report(); + Mailbox::setup(); + Deliverator * d = new Deliverator( message, mailbox, recipient ); + EventLoop::global()->start(); + if ( !d->i || d->i->failed() ) + return EX_UNAVAILABLE; + + if ( verbose ) + fprintf( stderr, + "aoxdeliver: Stored in %s as UID %d\n", + d->mb->name().utf8().cstr(), + d->m->uid( d->mb ) ); + return 0; +} diff --git a/deliver/deliver.cpp b/deliver/deliver.cpp deleted file mode 100644 index fc77f87..0000000 --- a/deliver/deliver.cpp +++ /dev/null @@ -1,257 +0,0 @@ -// Copyright 2009 The Archiveopteryx Developers <[email protected]> - -#include "scope.h" -#include "event.h" -#include "estring.h" -#include "allocator.h" -#include "stderrlogger.h" -#include "configuration.h" -#include "permissions.h" -#include "logclient.h" -#include "eventloop.h" -#include "injector.h" -#include "mailbox.h" -#include "query.h" -#include "file.h" -#include "user.h" -#include "log.h" -#include "utf.h" - -#include <stdlib.h> -#include <stdio.h> - -// time, ctime -#include <time.h> -// all the exit codes -#include <sysexits.h> - - -static void quit( uint s, const EString & m ) -{ - if ( !m.isEmpty() ) - fprintf( stderr, "deliver: %s\n", m.cstr() ); - exit( s ); -} - - -class Deliverator - : public EventHandler -{ -public: - Query * q; - Injector * i; - Injectee * m; - UString mbn; - EString un; - Permissions * p; - Mailbox * mb; - - Deliverator( Injectee * message, - const UString & mailbox, const EString & user ) - : q( 0 ), i( 0 ), m( message ), mbn( mailbox ), un( user ), - p( 0 ), mb( 0 ) - { - Allocator::addEternal( this, "deliver object" ); - q = new Query( "select al.mailbox, n.name as namespace, u.login " - "from aliases al " - "join addresses a on (al.address=a.id) " - "left join users u on (al.id=u.alias) " - "left join namespaces n on (u.parentspace=n.id) " - "where (lower(a.localpart)=$1 and lower(a.domain)=$2) " - "or (lower(u.login)=$3)", this ); - if ( user.contains( '@' ) ) { - int at = user.find( '@' ); - q->bind( 1, user.mid( 0, at ).lower() ); - q->bind( 2, user.mid( at + 1 ).lower() ); - } - else { - q->bindNull( 1 ); - q->bindNull( 2 ); - } - q->bind( 3, user.lower() ); - q->execute(); - } - - virtual ~Deliverator() - { - quit( EX_TEMPFAIL, "Delivery object unexpectedly deleted" ); - } - - void execute() - { - if ( q && !q->done() ) - return; - - if ( q && q->done() && !p ) { - Row * r = q->nextRow(); - q = 0; - if ( !r ) - quit( EX_NOUSER, "No such user: " + un ); - if ( !r->isNull( "login" ) && - r->getEString( "login" ) == "anonymous" ) - quit( EX_DATAERR, "Cannot deliver to the anonymous user" ); - if ( mbn.isEmpty() ) { - mb = Mailbox::find( r->getInt( "mailbox" ) ); - } - else { - UString pre; - if ( !r->isNull( "namespace" ) && !mbn.startsWith( "/" ) ) - pre = r->getUString( "namespace" ) + "/" + - r->getUString( "login" ) + "/"; - mb = Mailbox::find( pre + mbn ); - User * u = new User; - UString anyone; - anyone.append( "anyone" ); - u->setLogin( anyone ); - if ( mb ) - p = new Permissions( mb, u, this ); - } - if ( !mb ) - quit( EX_CANTCREAT, "No such mailbox" ); - } - - if ( p && !p->ready() ) - return; - - if ( p && !p->allowed( Permissions::Post ) ) - quit( EX_NOPERM, - "User 'anyone' does not have 'p' right on mailbox " + - mbn.ascii().quoted( '\'' ) ); - - if ( !i ) { - EStringList x; - m->setFlags( mb, &x ); - i = new Injector( this ); - List<Injectee> y; - y.append( m ); - i->addInjection( &y ); - i->execute(); - } - - if ( !i->done() ) - return; - - if ( i->failed() ) - quit( EX_SOFTWARE, "Injection error: " + i->error() ); - - i = 0; - EventLoop::shutdown(); - } -}; - - -int main( int argc, char *argv[] ) -{ - Scope global; - - EString sender; - UString mailbox; - EString recipient; - EString filename; - int verbose = 0; - bool error = false; - - int n = 1; - while ( n < argc ) { - if ( argv[n][0] == '-' ) { - switch ( argv[n][1] ) { - case 'f': - if ( argc - n > 1 ) - sender = argv[++n]; - break; - - case 't': - if ( argc - n > 1 ) { - Utf8Codec c; - mailbox = c.toUnicode( argv[++n] ); - if ( !c.valid() ) - error = true; - } - break; - - case 'v': - { - int i = 1; - while ( argv[n][i] == 'v' ) { - verbose++; - i++; - } - if ( argv[n][i] != '\0' ) - error = true; - } - break; - - default: - error = true; - break; - } - } - else if ( recipient.isEmpty() ) { - recipient = argv[n]; - } - else if ( filename.isEmpty() ) { - filename = argv[n]; - } - else { - error = true; - } - n++; - } - - if ( error || recipient.isEmpty() ) { - fprintf( stderr, - "Syntax: deliver [-v] [-f sender] recipient [filename]\n" ); - exit( -1 ); - } - - EString contents; - if ( filename.isEmpty() ) { - char s[128]; - while ( fgets( s, 128, stdin ) != 0 ) - contents.append( s ); - } - else { - File message( filename ); - if ( !message.valid() ) { - fprintf( stderr, "Unable to open message file %s\n", - filename.cstr() ); - exit( -1 ); - } - contents = message.contents(); - } - - Configuration::setup( "archiveopteryx.conf" ); - - Injectee * message = new Injectee; - message->parse( contents ); - if ( !message->error().isEmpty() ) { - fprintf( stderr, - "Message parsing failed: %s", message->error().cstr( ) ); - exit( EX_DATAERR ); - } - - if ( verbose > 0 ) - fprintf( stderr, "Sending to <%s>\n", recipient.cstr() ); - - EventLoop::setup(); - Database::setup( 1 ); - Log * l = new Log; - Allocator::addEternal( l, "delivery log" ); - global.setLog( l ); - Allocator::addEternal( new StderrLogger( "deliver", verbose ), - "log object" ); - - Configuration::report(); - Mailbox::setup(); - Deliverator * d = new Deliverator( message, mailbox, recipient ); - EventLoop::global()->start(); - if ( !d->i || d->i->failed() ) - return EX_UNAVAILABLE; - - if ( verbose ) - fprintf( stderr, - "deliver: Stored in %s as UID %d\n", - d->mb->name().utf8().cstr(), - d->m->uid( d->mb ) ); - return 0; -} diff --git a/doc/Jamfile b/doc/Jamfile index 8958257..f1c3a5b 100644 --- a/doc/Jamfile +++ b/doc/Jamfile @@ -25,5 +25,5 @@ rule Man { Man 5 : archiveopteryx.conf.man aoxsuper.conf.man ; Man 8 : - aoximport.man aox.man archiveopteryx.man deliver.man installer.man + aoximport.man aox.man archiveopteryx.man aoxdeliver.man installer.man logd.man recorder.man ; diff --git a/doc/aoxdeliver.man b/doc/aoxdeliver.man new file mode 100644 index 0000000..a28d6c6 --- /dev/null +++ b/doc/aoxdeliver.man @@ -0,0 +1,85 @@ +.\" Copyright 2009 The Archiveopteryx Developers <[email protected]> +.TH aoxdeliver 8 2011-02-10 aox.org "Archiveopteryx Documentation" +.SH NAME +aoxdeliver - deliver mail into Archiveopteryx. +.SH SYNOPSIS +.B $BINDIR/aoxdeliver [-f sender] [-t mailbox] [-v] destination [filename] +.SH DESCRIPTION +.nh +.PP +The +.B aoxdeliver +program injects a single mail message in RFC-822 format into +Archiveopteryx. +It connects to Archiveopteryx's backend database and injects the message +into the correct mailbox. +.PP +.B aoxdeliver +is meant as a compatibility shim for use with e.g. +.BR formail (1) +and with MTAs that want to deliver to a program. +Note that you will generally get better performance by using LMTP. +.PP +.B aoxdeliver +bypasses Sieve and always stores mail directly into the target mailbox. +.SH OPTIONS +.IP "-f sender" +specifies the fully qualified address of the message sender. This is +the same as the SMTP envelope sender. +.IP +Starting with version 2.01, the +.I -f +argument is ignored. It is still accepted to keep old scripts working. +.IP "-t mailbox" +directs +.B aoxdeliver +to store the message into the named mailbox. The "p" right on the +mailbox must be granted to "anyone". ("p" controls who is permitted to +send mail to the mailbox, see RFC 4314 for more details.) +.IP "-v" +requests more verbosity during delivery. May be specified twice. +.SH EXAMPLES +To deliver an entire berkeley mbox into the inbox of user [email protected]: +.IP +formail -s aoxdeliver [email protected] < mbox +.PP +To deliver each message in the MH folder +blah into the +inbox of user [email protected]: +.IP +folder +blah +.br +seq all | xargs -n1 aoxdeliver [email protected] +.PP +To deliver each message in the MH folder +ramble into folder stumble +of user [email protected]: +.IP +folder +blah +.br +seq all | xargs -n1 aoxdeliver -t stumble [email protected] +.PP +If raj has not granted "anyone" the "p" right ("p"ost mail to +mailbox), this example fails. +.SH DIAGNOSTICS +The normal exit status of +.B aoxdeliver +is 0. In case of errors, +.B aoxdeliver +returns an error code from sysexits.h, such as EX_TEMPFAIL, EX_NOUSER, etc. +.SH BUGS +Delivering multiple messages would also be good, for those big mailbox +migrations. In that case, +.B +aoxdeliver +should report which messages could be delivered and which could not. +.PP +There is no command-line option to set the configuration file. +.SH AUTHOR +The Archiveopteryx Developers, [email protected]. +.SH VERSION +This man page covers Archiveopteryx version 3.1.4, released 2011-02-10, +http://archiveopteryx.org/3.1.3 +.SH SEE ALSO +.BR archiveopteryx (8), +.BR archiveopteryx.conf (5), +.BR logd (8), +http://archiveopteryx.org diff --git a/doc/deliver.man b/doc/deliver.man deleted file mode 100644 index 7f3f82a..0000000 --- a/doc/deliver.man +++ /dev/null @@ -1,85 +0,0 @@ -.\" Copyright 2009 The Archiveopteryx Developers <[email protected]> -.TH deliver 8 2011-02-10 aox.org "Archiveopteryx Documentation" -.SH NAME -deliver - deliver mail into Archiveopteryx. -.SH SYNOPSIS -.B $BINDIR/deliver [-f sender] [-t mailbox] [-v] destination [filename] -.SH DESCRIPTION -.nh -.PP -The -.B deliver -program injects a single mail message in RFC-822 format into -Archiveopteryx. -It connects to Archiveopteryx's backend ddatabase injects the message -into the correct mailbox. -.PP -.B deliver -is meant as a compatibility shim for use with e.g. -.BR formail (1) -and with MTAs that want to deliver to a program. -Note that you will generally get better performance by using LMTP. -.PP -.B deliver -bypasses Sieve and always stores mail directly into the target mailbox. -.SH OPTIONS -.IP "-f sender" -specifies the fully qualified address of the message sender. This is -the same as the SMTP envelope sender. -.IP -Starting with version 2.01, the -.I -f -argument is ignored. It is still accepted to keep old scripts working. -.IP "-t mailbox" -directs -.B deliver -to store the message into the named mailbox. The "p" right on the -mailbox must be granted to "anyone". ("p" controls who is permitted to -send mail to the mailbox, see RFC 4314 for more details.) -.IP "-v" -requests more verbosity during delivery. May be specified twice. -.SH EXAMPLES -To deliver an entire berkeley mbox into the inbox of user [email protected]: -.IP -formail -s deliver [email protected] < mbox -.PP -To deliver each message in the MH folder +blah into the -inbox of user [email protected]: -.IP -folder +blah -.br -seq all | xargs -n1 deliver [email protected] -.PP -To deliver each message in the MH folder +ramble into folder stumble -of user [email protected]: -.IP -folder +blah -.br -seq all | xargs -n1 deliver -t stumble [email protected] -.PP -If raj has not granted "anyone" the "p" right ("p"ost mail to -mailbox), this example fails. -.SH DIAGNOSTICS -The normal exit status of -.B deliver -is 0. In case of errors, -.B deliver -returns an error code from sysexits.h, such as EX_TEMPFAIL, EX_NOUSER, etc. -.SH BUGS -Delivering multiple messages would also be good, for those big mailbox -migrations. In that case, -.B -deliver -should report which messages could be delivered and which could not. -.PP -There is no command-line option to set the configuration file. -.SH AUTHOR -The Archiveopteryx Developers, [email protected]. -.SH VERSION -This man page covers Archiveopteryx version 3.1.4, released 2011-02-10, -http://archiveopteryx.org/3.1.3 -.SH SEE ALSO -.BR archiveopteryx (8), -.BR archiveopteryx.conf (5), -.BR logd (8), -http://archiveopteryx.org
