package votorola.a.mail; // Copyright 2007-2008, Michael Allan. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Votorola Software"), to deal in the Votorola Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Votorola Software, and to permit persons to whom the Votorola Software is furnished to do so, subject to the following conditions: The preceding copyright notice and this permission notice shall be included in all copies or substantial portions of the Votorola Software. THE VOTOROLA 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 VOTOROLA SOFTWARE OR THE USE OR OTHER DEALINGS IN THE VOTOROLA SOFTWARE. import java.io.*; import java.sql.*; import java.util.*; import java.util.concurrent.locks.*; import javax.script.*; import votorola.a.*; import votorola.a.election.*; import votorola.a.voter.*; import votorola.g.lang.*; import votorola.g.script.*; /** The mail responder's electoral meta-service. It provides bootstrap instructions * on accessing the electoral services, as well as general information * about the subserver. * * @see ../design.xht#meta-service * @see http://zelea.com/project/votorola/a/mail/guide.xht#meta-service */ public @ThreadRestricted("holds lock()") final class MailMetaService extends ElectoralService { /** Constructs a MailMetaService. * * @param s the compiled configuration script */ static @ThreadSafe MailMetaService newMetaService( ElectoralSubserver.Run subserverRun, JavaScriptIncluder s ) throws IOException, ScriptException, SQLException { return new MailMetaService( subserverRun, ConfigurationContext.configure( subserverRun.subserver(), s )); } public MailMetaService( ElectoralSubserver.Run subserverRun, ConfigurationContext cc ) throws IOException, ScriptException, SQLException { super( subserverRun, cc.getName(), cc ); subserverRun.init_ensureAllElectoralServices(); // this meta-service depends on all other services init( new ArrayList() ); configurationContext = null; // done with it, free the memory } private void init_2() { final ArrayList sList_election = new ArrayList(); final ArrayList sList_other = new ArrayList(); for( ElectoralService service: subserverRun.newElectoralServiceArray() ) { if( service instanceof Election ) sList_election.add( (Election)service ); else sList_other.add( service ); } serviceArray_elections = sList_election.toArray( new Election[sList_election.size()] ); serviceArray_other = sList_other.toArray( new ElectoralService[sList_other.size()] ); } private ConfigurationContext cc() { return (ConfigurationContext)configurationContext; } // nulled after init // - E l e c t o r a l - S e r v i c e ------------------------------------------------ /** The configuration file for this meta-service. It is located at: *
* {@linkplain ElectoralSubserver#votorolaDirectory() votorolaDirectory}/vomailrd.js *
*

* The language is JavaScript. There are restrictions * on the {@linkplain votorola.g.script.JavaScriptIncluder character encoding}. *

*/ public @ThreadSafe @Override File configurationFile() { return configurationFile; } private final File configurationFile = cc().configurationFile; public @Override Exception help( final String[] argv, final CommandResponder.Session session ) { final ReplyBuilder replyB = session.replyBuilder(); helpA( replyB, session ); replyB.lappendlnn( "a.mail.MailMetaService.help.reply.summary.trailer(1)", name ); helpB( replyB, session ); helpC( replyB, session ); final String generalTitle = replyB.bundle().getString( "a.mail.MailMetaService.help.reply.general" ); replyB.setWrapping( false ).appendln( generalTitle ); for( int c = generalTitle.length(); c > 0; --c ) replyB.append( '=' ); replyB.appendlnn().setWrapping( true ); replyB.indent( 4 ); replyB.lappendlnn( "a.mail.MailMetaService.help.reply.general.body" ); replyB.indent( 4 ); replyB.lappendlnn( "a.voter.CR_Help.commandName" ); replyB.exdent( 8 ); if( serviceArray_other == null ) init_2(); // lazilly, after all services have been created (not just the standard ones), including this meta-service assert serviceArray_other.length > 0: "this meta-service is listed, at the very least"; if( serviceArray_elections.length > 0 ) { listServices( replyB.bundle().getString( "a.mail.MailMetaService.serviceTypeTitle.elections" ), serviceArray_elections, session ); } listServices( replyB.bundle().getString( "a.mail.MailMetaService.serviceTypeTitle.other" ), serviceArray_other, session ); return null; } /** The email address that nominally identifies this meta-service. It is constructed * from the service and server names, as * '{@linkplain #name() service-name}@{@linkplain ElectoralSubserver#serverName() server-name}'. * The meta-service will reply to help requests it receives at this address. * It will also reply to any misaddressed mail that lands in the inbox, * and is not assignable to any other service. *

* Typically, this is the ordinary email address of the subserver account. *

*

* The authentication layer must not challenge any messages sent * to this address. It must allow them (and, if possible, all messages sent * to non-service email addresses) to bypass sender authentication * entirely. For reasons, see: * http://zelea.com/system/host/t/home/v/.tmda/filters/incoming *

* * @see ConfigurationContext#setName(String) */ public @ThreadSafe @Override String serviceEmail() { return super.serviceEmail(); } /** A brief description of this meta-service, same as * {@linkplain ElectoralSubserver#summaryDescription() the subserver's}. */ public @Override String summaryDescription() { return subserverRun.subserver().summaryDescription(); } /** The title of this meta-service, which is the same as the * {@linkplain ElectoralSubserver#title() subserver's title}. */ public @Override String title() { return subserverRun.subserver().title(); } // ==================================================================================== /** A context for configuring the mail responder's * {@linkplain MailMetaService electoral meta-service}. * The meta-service is configured by its * {@linkplain #configurationFile configuration file}, * which contains a script (s) for that purpose. * During construction of the meta-service, an instance of this context (metaCC) * is passed to s, via s::configureMailMetaService(metaCC). */ public static @ThreadSafe final class ConfigurationContext // public class and getters, accessible by configuration scripts { /** Constructs the complete configuration of the meta-service. * * @param s the compiled configuration script */ public static ConfigurationContext configure( final ElectoralSubserver subserver, JavaScriptIncluder s ) throws ScriptException { final ConfigurationContext cc = new ConfigurationContext( subserver, s ); s.invokeKnownFunction( "configureMailMetaService", cc ); return cc; } private ConfigurationContext( ElectoralSubserver subserver, JavaScriptIncluder s ) { configurationFile = s.scriptFile(); name = subserver.name(); } private final File configurationFile; // -------------------------------------------------------------------------------- /** @see MailMetaService#name() * @see MailMetaService#serviceEmail() * @see #setName(String) */ public String getName() { return name; } private String name; /** Sets the {@linkplain MailMetaService#name() name} that nominally * identifies the meta-service, and is used to construct its * {@linkplain MailMetaService#serviceEmail() service email} address. * The default value is the {@linkplain ElectoralSubserver#name() subserver name}. * * @see #getName() */ @ThreadRestricted("constructor") public void setName( String name ) { this.name = name; } } //// P r i v a t e /////////////////////////////////////////////////////////////////////// private void listServices( final String serviceTypeTitle, final ElectoralService[] serviceArray, final CommandResponder.Session session ) { assert lock.isHeldByCurrentThread(); final ReplyBuilder replyB = session.replyBuilder(); replyB.setWrapping( false ).appendln( serviceTypeTitle ); for( int c = serviceTypeTitle.length(); c > 0; --c ) replyB.append( '=' ); replyB.appendlnn().setWrapping( true ).indent( 4 ); for( ElectoralService service: serviceArray ) { replyB.appendlnn( service.serviceEmail() ); replyB.indent( 4 ); replyB.appendlnn( service.title() ); if( service.equals( MailMetaService.this )) { replyB.lappendlnn( "a.mail.MailMetaService.help.reply.self" ); } replyB.exdent( 4 ); } replyB.exdent( 4 ); } private Election[] serviceArray_elections; // final after init_2() private ElectoralService[] serviceArray_other; // final after init_2() }