EString MimeField::rfc822() const { EString s = baseValue(); uint lineLength = name().length() + 2 + s.length(); EStringList words; List< MimeFieldData::Parameter >::Iterator it( d->parameters ); while ( it ) { EString s = it->value; if ( !s.boring( EString::MIME ) ) s = s.quoted(); words.append( it->name + "=" + s ); ++it; } while ( !words.isEmpty() ) { EStringList::Iterator i( words ); while ( i && lineLength + 2 + i->length() > 78 ) ++i; if ( i ) { s.append( "; " ); lineLength += 2; } else { i = words; s.append( ";\r\n " ); lineLength = 1; } s.append( *i ); // XXX need more elaboration for 2231 lineLength += i->length(); words.take( i ); } return s; }
static void headerSummary( Header * h, int n ) { EStringList l; ContentType * ct = h->contentType(); if ( ct ) l.append( ct->type() + "/" + ct->subtype() ); ContentTransferEncoding * cte = h->contentTransferEncoding(); if ( cte ) { EString s; switch ( cte->encoding() ) { case EString::QP: s = "quoted-printable"; break; case EString::Base64: s = "base64"; break; case EString::Uuencode: s = "x-uuencode"; break; case EString::Binary: s = "7bit"; break; } l.append( s ); } HeaderField * cd = h->field( HeaderField::ContentDescription ); if ( cd ) l.append( cd->rfc822( false ) ); if ( !l.isEmpty() ) { spaces( n ); fprintf( stderr, "%s\n", l.join( ";" ).cstr() ); } }
int main( int argc, char ** argv ) { Scope global; EventLoop::setup(); const char * error = 0; bool ok = true; if ( argc != 5 ) { error = "Wrong number of arguments"; ok = false; } uint port = 0; if ( ok ) { port = EString( argv[1] ).number( &ok ); if ( !ok ) error = "Could not parse own port number"; } if ( ok ) { Listener<RecorderServer> * l4 = new Listener<RecorderServer>( Endpoint( "0.0.0.0", port ), "recording relay/4" ); Allocator::addEternal( l4, "recording listener" ); Listener<RecorderServer> * l6 = new Listener<RecorderServer>( Endpoint( "::", port ), "recording relay/6" ); Allocator::addEternal( l6, "recording listener" ); if ( l4->state() != Connection::Listening && l6->state() != Connection::Listening ) error = "Could not listen for connections"; } if ( ok ) { port = EString( argv[3] ).number( &ok ); if ( !ok ) error = "Could not parse server's port number"; } if ( ok ) { EStringList l = Resolver::resolve( argv[2] ); if ( l.isEmpty() ) { ok = false; error = (EString("Cannot resolve ") + argv[2] + ": " + Resolver::errors().join( ", " ) ).cstr(); } else { ep = new Endpoint( *l.first(), port ); Allocator::addEternal( ep, "target server endpoint" ); } if ( ep && !ep->valid() ) { ok = false; error = "Invalid server address"; } } if ( !ok ) { fprintf( stderr, "Error: %s\n" "Usage: recorder port address port filebase\n" " First port: The recorder's own port.\n" " Address: The IP address of the server to forward to.\n" " Second port: The server port to forward to.\n" " Filebase: The filename base (.<blah> is added).\n", error ); exit( 1 ); } ::base = new EString( argv[4] ); Allocator::addEternal( ::base, "base of recorded file names" ); global.setLog( new Log ); EventLoop::global()->start(); }
void POP::parse() { Buffer *b = readBuffer(); while ( b->size() > 0 ) { if ( !d->reader ) { if ( d->reserved ) break; EString * s = b->removeLine( 255 ); if ( !s && b->size() < 255 ) return; if ( !s ) { log( "Connection closed due to overlong line (" + fn( b->size() ) + " bytes)", Log::Error ); err( "Line too long. Closing connection." ); Connection::setState( Closing ); return; } bool unknown = false; EStringList * args = EStringList::split( ' ', *s ); EString cmd = args->take( args->first() )->lower(); if ( d->sawUser && !( cmd == "quit" || cmd == "pass" ) ) { d->sawUser = false; unknown = true; } else if ( cmd == "quit" && args->isEmpty() ) { newCommand( d->commands, this, PopCommand::Quit ); } else if ( cmd == "capa" && args->isEmpty() ) { newCommand( d->commands, this, PopCommand::Capa ); } else if ( d->state == Authorization ) { if ( cmd == "stls" ) { if ( hasTls() ) err( "Nested STLS" ); else newCommand( d->commands, this, PopCommand::Stls ); } else if ( cmd == "auth" ) { newCommand( d->commands, this, PopCommand::Auth, args ); } else if ( cmd == "user" && args->count() == 1 ) { d->sawUser = true; newCommand( d->commands, this, PopCommand::User, args ); } else if ( d->sawUser && cmd == "pass" && args->count() >= 1 ) { d->sawUser = false; newCommand( d->commands, this, PopCommand::Pass, args ); } else if ( cmd == "apop" && args->count() == 2 ) { newCommand( d->commands, this, PopCommand::Apop, args ); } else { unknown = true; } } else if ( d->state == Transaction ) { if ( cmd == "stat" && args->isEmpty() ) { newCommand( d->commands, this, PopCommand::Stat ); } else if ( cmd == "list" && args->count() < 2 ) { newCommand( d->commands, this, PopCommand::List, args ); } else if ( cmd == "top" && args->count() == 2 ) { newCommand( d->commands, this, PopCommand::Top, args ); } else if ( cmd == "retr" && args->count() == 1 ) { newCommand( d->commands, this, PopCommand::Retr, args ); } else if ( cmd == "dele" && args->count() == 1 ) { newCommand( d->commands, this, PopCommand::Dele, args ); } else if ( cmd == "noop" && args->isEmpty() ) { newCommand( d->commands, this, PopCommand::Noop ); } else if ( cmd == "rset" && args->isEmpty() ) { newCommand( d->commands, this, PopCommand::Rset ); } else if ( cmd == "uidl" && args->count() < 2 ) { newCommand( d->commands, this, PopCommand::Uidl, args ); } else { unknown = true; } } else { unknown = true; } if ( unknown ) { err( "Bad command" ); recordSyntaxError(); } } else { d->reader->read(); } runCommands(); } }
void SieveScript::parse( const EString & script ) { d->source = script; SieveParser p( script ); d->script = p.commands(); // if we're not yet at the end, treat whatever follows as another // command, which will have a nice big error message. p.whitespace(); if ( !p.atEnd() ) { SieveCommand * sc = p.command(); sc->setError( "Junk at end of script" ); d->script->append( sc ); } // require is only permitted at the start List<SieveCommand>::Iterator s( d->script ); while ( s && s->identifier() == "require" ) { s->setRequirePermitted( true ); ++s; } // do the semantic bits of parsing s = d->script->first(); EString prev; while ( s ) { s->setParent( this ); s->parse( prev ); prev = s->identifier(); ++s; } // check that require lists the right extensions EStringList * extensions = p.extensionsNeeded(); EStringList declared; s = d->script->first(); while ( s && s->identifier() == "require" ) { if ( s->error().isEmpty() ) { UStringList * r = 0; if ( s->arguments() && s->arguments()->arguments() && s->arguments()->arguments()->first() ) r = s->arguments()->arguments()->first()->stringList(); UStringList::Iterator i( r ); while ( i ) { if ( i->isAscii() ) declared.append( i->ascii() ); ++i; } } ++s; } declared.append( "comparator-i;octet" ); declared.append( "comparator-i;ascii-casemap" ); declared.append( "fileinto" ); declared.append( "reject" ); EStringList::Iterator i( extensions ); EStringList undeclared; while ( i ) { if ( !declared.contains( *i ) ) undeclared.append( i->quoted() ); ++i; } if ( !undeclared.isEmpty() ) { SieveCommand * f = d->script->first(); if ( f->identifier() == "require" ) f->setError( "Extensions used but not declared: " + undeclared.join( ", " ) ); else f->setError( "Missing require: require [ " + undeclared.join( ", " ) + " ];" ); } // and find all the errors d->errors = p.bad( this ); }
void SieveCommand::parse( const EString & previous ) { if ( identifier().isEmpty() ) setError( "Command name is empty" ); bool test = false; bool blk = false; EString i = identifier(); if ( i == "if" || i == "elsif" ) { test = true; blk = true; if ( i == "elsif" && previous != "if" && previous != "elsif" ) setError( "elsif is only permitted after if/elsif" ); } else if ( i == "else" ) { blk = true; if ( previous != "if" && previous != "elsif" ) setError( "else is only permitted after if/elsif" ); } else if ( i == "require" ) { arguments()->numberRemainingArguments(); UStringList::Iterator i( arguments()->takeStringList( 1 ) ); EStringList a; EStringList e; while ( i ) { if ( supportedExtensions()->contains( i->ascii() ) ) a.append( i->ascii().quoted() ); else e.append( i->ascii().quoted() ); ++i; } if ( !e.isEmpty() ) setError( "Each string must be a supported " "sieve extension. " "These are not: " + e.join( ", " ) ); if ( !d->require ) setError( "require is only permitted as the first command." ); else if ( parent() ) parent()->addExtensions( &a ); } else if ( i == "stop" ) { // nothing needed } else if ( i == "reject" ) { require( "reject" ); if ( arguments()->arguments()->isEmpty() ) { // we accept reject without reason } else { // if there is an argument, it must be a string arguments()->numberRemainingArguments(); (void)arguments()->takeString( 1 ); } } else if ( i == "ereject" ) { require( "reject" ); arguments()->numberRemainingArguments(); (void)arguments()->takeString( 1 ); } else if ( i == "fileinto" ) { require( "fileinto" ); if ( arguments()->findTag( ":copy" ) ) require( "copy" ); if ( arguments()->findTag( ":flags" ) ) { require( "imap4flags" ); (void)arguments()->takeTaggedStringList( ":copy" ); } arguments()->numberRemainingArguments(); UString mailbox = arguments()->takeString( 1 ); UString p; p.append( "/" ); p.append( mailbox ); if ( !Mailbox::validName( mailbox ) && !Mailbox::validName( p ) ) { setError( "Expected mailbox name, but got: " + mailbox.utf8() ); } else if ( mailbox.startsWith( "INBOX." ) ) { // a sieve script which wants to reference a // mailbox called INBOX.X must use lower case // (inbox.x). UString aox = UStringList::split( '.', mailbox.mid( 6 ) )->join( "/" ); setError( mailbox.utf8().quoted() + " is Cyrus syntax. Archiveopteryx uses " + aox.utf8().quoted() ); } } else if ( i == "redirect" ) { (void)arguments()->findTag( ":copy" ); arguments()->numberRemainingArguments(); EString s = arguments()->takeString( 1 ).utf8(); AddressParser ap( s ); ap.assertSingleAddress(); if ( !ap.error().isEmpty() ) setError( "Expected one normal address (local@domain), but got: " + s ); } else if ( i == "keep" ) { // nothing needed } else if ( i == "discard" ) { // nothing needed } else if ( i == "vacation" ) { // vacation [":days" number] [":subject" string] // [":from" string] [":addresses" string-list] // [":mime"] [":handle" string] <reason: string> require( "vacation" ); // :days uint days = 7; if ( arguments()->findTag( ":days" ) ) days = arguments()->takeTaggedNumber( ":days" ); if ( days < 1 || days > 365 ) arguments()->tagError( ":days", "Number must be 1..365" ); // :subject (void)arguments()->takeTaggedString( ":subject" ); // anything is acceptable, right? // :from if ( arguments()->findTag( ":from" ) ) { parseAsAddress( arguments()->takeTaggedString( ":from" ), ":from" ); // XXX we don't enforce its being a local address. } // :addresses if ( arguments()->findTag( ":addresses" ) ) { UStringList * addresses = arguments()->takeTaggedStringList( ":addresses" ); UStringList::Iterator i( addresses ); while ( i ) { parseAsAddress( *i, ":addresses" ); ++i; } } // :mime bool mime = false; if ( arguments()->findTag( ":mime" ) ) mime = true; // :handle (void)arguments()->takeTaggedString( ":handle" ); // reason arguments()->numberRemainingArguments(); UString reason = arguments()->takeString( 1 ); if ( mime ) { if ( !reason.isAscii() ) setError( ":mime bodies must be all-ASCII, " "8-bit text is not permitted" ); // so says the RFC EString x = reason.utf8(); uint i = 0; Header * h = Message::parseHeader( i, x.length(), x, Header::Mime ); Bodypart * bp = Bodypart::parseBodypart( i, x.length(), x, h, 0 ); if ( !h->error().isEmpty() ) setError( "While parsing MIME header: " + h->error() ); else if ( !bp->error().isEmpty() ) setError( "While parsing MIME bodypart: " + bp->error() ); List<HeaderField>::Iterator f( h->fields() ); while ( f ) { if ( !f->name().startsWith( "Content-" ) ) setError( "Header field not permitted: " + f->name() ); ++f; } if ( bp->children()->isEmpty() && bp->text().isEmpty() ) setError( "Vacation reply does not contain any text" ); } else { if ( reason.isEmpty() ) setError( "Empty vacation text does not make sense" ); } } else if ( i == "setflag" || i == "addflags" || i == "removeflag" ) { arguments()->numberRemainingArguments(); (void)arguments()->takeStringList( 1 ); } else if ( i == "notify" ) { require( "enotify" ); UString from; if ( arguments()->findTag( ":from" )) from = arguments()->takeTaggedString( ":from" ); UString importance; importance.append( "2" ); if ( arguments()->findTag( ":importance" ) ) importance = arguments()->takeTaggedString( ":from" ); uint c = importance[0]; if ( c < '1' || c > '3' ) arguments()->tagError( ":importance", "Importance must be 1, 2 or 3" ); UStringList * options; if ( arguments()->findTag( ":options" ) ) options = arguments()->takeTaggedStringList( ":options" ); UString message; if ( arguments()->findTag( ":message" ) ) message = arguments()->takeTaggedString( ":message" ); arguments()->numberRemainingArguments(); UString method = arguments()->takeString( 1 ); SieveNotifyMethod * m = new SieveNotifyMethod( method, arguments()->takeArgument( 1 ), this ); if ( m->valid() ) { if ( arguments()->findTag( ":from" ) ) m->setFrom( from, arguments()->findTag( ":from" ) ); if ( arguments()->findTag( ":message" ) ) m->setMessage( message, arguments()->findTag( ":message" ) ); } } else { setError( "Command unknown: " + identifier() ); } arguments()->flagUnparsedAsBad(); if ( test ) { // we must have a test if ( !arguments() || arguments()->tests()->count() != 1 ) setError( "Command " + identifier() + " requires one test" ); if ( arguments() ) { List<SieveTest>::Iterator i( arguments()->tests() ); while ( i ) { i->parse(); if ( blk && block() ) { if ( i->ihaveFailed() ) block()->setIhaveFailed(); else block()->addExtensions( i->addedExtensions() ); } ++i; } } } else { // we cannot have a test if ( arguments() && arguments()->tests()->isEmpty() ) { List<SieveTest>::Iterator i( arguments()->tests() ); while ( i ) { i->setError( "Command " + identifier() + " does not use tests" ); ++i; } } } if ( blk ) { // we must have a subsidiary block if ( !block() ) { setError( "Command " + identifier() + " requires a subsidiary {..} block" ); } else { EString prev; List<SieveCommand>::Iterator i( block()->commands() ); while ( i ) { i->parse( prev ); prev = i->identifier(); ++i; } } } else { // we cannot have a subsidiary block if ( block() ) block()->setError( "Command " + identifier() + " does not use a subsidiary command block" ); // in this case we don't even bother syntax-checking the test // or block } }