public interface Command
{
    /**
     * Returns the name of this command.
     *
     * @return The command name.
     */
    String getName();

    /**
     * Returns a short, one line help for this command.
     *
     * @return A short help text.
     */
    String getHelp();

    /**
     * Returns true if this command should be available on the command line at startup.
     *
     * @true if this command should be available from the command line.
     */
    boolean hasCommandLineOption();

    /**
     * Returns an option object describing the command line option.
     *
     * @return An Option object describing the command line option or null if no command line option is wanted.
     */
    Option getCommandLineOption();

    /**
     * Returns true if this command offers completion in interactive mode.
     *
     * @return True if this command offers completion in interactive mode.
     */
    boolean hasCompletor();

    /**
     * Returns a Completor object for completion in interactive mode.
     *
     * @return A Completor object for completion in interactive mode or null if no completion is required.
     */
    Completor getCompletor();

    /**
     * Process a command line option. If a command has declared a command line option, this method will be
     * called at start up with the command line. The command is responsible for reacting on the passed command
     * line.
     *
     * @param line The CommandLine object representing all user command line options.
     */
    void processCommandLineOption(CommandLine line);

    /**
     * Returns true if this command object considers itself responsible for the given interactive command.
     *
     * @param command The current command entered by the user.
     * @return True if the command object wants to process this command.
     */
    boolean matchInteractiveCommand(final String command);

    /**
     * Allows the command to check prerequisites before actually executing the command. Commands that offer
     * one mode where parameters are processed and another mode where just the command name will display the
     * current setting of a command, should use this method to display the current setting when no arguments
     * are given.
     *
     * @param args An array of Strings containing the interactive command and its arguments. This array is never null its length is
     * always at least one.
     * @return True if the command considers all its prerequisites fulfilled, False otherwise.
     */
    boolean prerequisiteInteractiveCommand(final String[] args);

    /**
     * Execute the command.
     *
     * @param args An array of Strings containing the interactive command and its arguments. This array is never null its length is
     * always at least one.
     */
    void executeInteractiveCommand(final String[] args);

    /**
     * Set the application context for this command. This method is guaranteed to be called as the very first method call
     * on the command and it is called only once.
     *
     * @param context The current Application context object.
     */
    void setContext(AppContext context);
} public class HelpCommand extends AbstractCommand implements Command
{

    public HelpCommand()
    {
    }

    public String getName()
    {
        return "help";
    }

    public String getHelp()
    {
        return "display help";
    }

    @Override
    public void processCommandLineOption(final CommandLine line)
    {
        if (line.hasOption(getName()))
        {
            usage();
            System.exit(0);
        }
    }

    @Override
    public void executeInteractiveCommand(final String[] args)
    {
        for (Command command : getContext().getCommands())
        {
            System.out.println(command.getName() + " - " + command.getHelp());
        }
    }

    private void usage()
    {
        HelpFormatter formatter = new HelpFormatter();
        formatter.printHelp(PGPSigner.APPLICATION_NAME, getContext().getOptions());
    }
} public class KeyServerCommand extends AbstractCommand implements Command
{

    public String getName()
    {
        return "keyserver";
    }

    public String getHelp()
    {
        return "sets the keyserver for uploading keys";
    }

    @Override
    public Option getCommandLineOption()
    {
        Option option = super.getCommandLineOption();
        option.setArgs(1);
        option.setArgName("hostname");
        return option;
    }

    @Override
    public boolean prerequisiteInteractiveCommand(final String[] args)
    {
        if (args.length < 2)
        {
            System.out.println("Current key server is " + DisplayHelpers.showNullValue(getContext().getKeyServer()));
            return false;
        }

        return true;
    }

    @Override
    public void executeInteractiveCommand(final String[] args)
    {
        getContext().setKeyServer(args[1]);

        for (PublicKey key : getContext().getPartyRing().getVisibleKeys().values())
        {
            key.setUploaded(false);
        }
    }
} public class ListCommand extends AbstractCommand implements Command
{

    public String getName()
    {
        return "list";
    }

    public String getHelp()
    {
        return "list the keys available for signing";
    }

    @Override
    public Option getCommandLineOption()
    {
        return null;
    }

    @Override
    public void executeInteractiveCommand(final String[] args)
    {
        PublicKeyRing partyRing = getContext().getPartyRing().getVisibleKeys();

        int count = 0;
        for (PublicKey key : partyRing.values())
        {
            System.out.print(++count + " | " + key.getKeyId() + " | ");

            System.out.print(key.isSigned() ? "S" : " ");
            System.out.print(key.isMailed() ? "M" : " ");
            System.out.print(key.isUploaded() ? "U" : " ");

            Iterator it = key.getUserIds();
            if (it.hasNext())
            {
                System.out.println(" | " + it.next());
            }
        }
    }
} public class MailCommand extends AbstractCommand implements Command
{

    public String getName()
    {
        return "mail";
    }

    public String getHelp()
    {
        return "mail signed keys to their owners";
    }

    @Override
    public Option getCommandLineOption()
    {
        return null;
    }

    @Override
    public boolean prerequisiteInteractiveCommand(final String[] args)
    {
        if (getContext().getSignKey() == null)
        {
            System.out.println("Sign key must be selected for sender mail address!");
            return false;
        }

        if (StringUtils.isEmpty(getContext().getMailServerHost()))
        {
            System.out.println("Mail server must be set!");
            return false;
        }

        if (StringUtils.isEmpty(getContext().getSignEvent()))
        {
            System.out.println("Sign event name must be set!");
            return false;
        }

        return true;
    }

    @Override
    public void executeInteractiveCommand(final String[] args)
    {

        SecretKey secretKey = getContext().getSignKey();

        String senderMail = secretKey.getMailAddress();
        String senderName = secretKey.getName();

        if (StringUtils.isEmpty(senderMail))
        {
            System.out.println("Could not extract sender mail from sign key.");
            return;
        }

        for (final PublicKey key : getContext().getPartyRing().getVisibleKeys().values())
        {

            if (key.isSigned() && !key.isMailed())
            {

                try
                {
                    String recipient = getContext().isSimulation() ? senderMail : key.getMailAddress();

                    if (StringUtils.isEmpty(recipient))
                    {
                        System.out.println("No mail address for key " + key.getKeyId() + ", skipping.");
                        continue;
                    }

                    System.out.println("Sending Key " + key.getKeyId() + " to " + recipient);

                    MultiPartEmail mail = new MultiPartEmail();
                    mail.setHostName(getContext().getMailServerHost());
                    mail.setSmtpPort(getContext().getMailServerPort());
                    mail.setFrom(senderMail, senderName);
                    mail.addTo(recipient);

                    if (!getContext().isSimulation())
                    {
                        mail.addBcc(senderMail);
                    }

                    mail.setSubject("Your signed PGP key - " + key.getKeyId());
                    mail.setMsg("This is your signed PGP key " + key.getKeyId() + " from the " + getContext().getSignEvent()
                            + " key signing event.");

                    final String name = key.getKeyId() + ".asc";

                    mail.attach(new DataSource()
                    {

                        public String getContentType()
                        {
                            return "application/pgp-keys";
                        }

                        public InputStream getInputStream() throws IOException
                        {
                            return new ByteArrayInputStream(key.getArmor());
                        }

                        public String getName()
                        {
                            return name;
                        }

                        public OutputStream getOutputStream() throws IOException
                        {
                            throw new UnsupportedOperationException();
                        }
                    }, name, "Signed Key " + key.getKeyId());

                    mail.send();
                    key.setMailed(true);

                }
                catch (EmailException ee)
                {
                    System.out.println("Could not send mail for Key " + key.getKeyId());
                }
            }
        }
    }
} public class MailServerCommand extends AbstractCommand implements Command
{

    public String getName()
    {
        return "mailserver";
    }

    public String getHelp()
    {
        return "sets the mailserver for mailing keys";
    }

    @Override
    public Option getCommandLineOption()
    {
        Option option = super.getCommandLineOption();
        option.setArgs(1);
        option.setArgName("hostname");
        return option;
    }

    @Override
    public boolean prerequisiteInteractiveCommand(final String[] args)
    {
        if (args.length < 2)
        {
            System.out.println("Current mail server is " + DisplayHelpers.showNullValue(getContext().getMailServerHost()));
            return false;
        }

        return true;
    }

    @Override
    public void executeInteractiveCommand(final String[] args)
    {
        String mailServerHost = args[1];
        String mailServerPort = "25";

        int colonIndex = mailServerHost.indexOf(':');
        if (colonIndex > -1) {
            mailServerPort = mailServerHost.substring(colonIndex +1);
            mailServerHost = mailServerHost.substring(0, colonIndex);
        }
        getContext().setMailServerHost(mailServerHost);
        getContext().setMailServerPort(Integer.parseInt(mailServerPort));

        for (PublicKey key : getContext().getPartyRing().getVisibleKeys().values())
        {
            key.setUploaded(false);
        }
    }
} public class PartyRingCommand extends AbstractCommand implements Command
{

    public String getName()
    {
        return "partyring";
    }

    public String getHelp()
    {
        return "sets the party key ring";
    }

    @Override
    public Option getCommandLineOption()
    {
        Option option = super.getCommandLineOption();
        option.setArgs(1);
        option.setArgName("file");
        return option;
    }

    @Override
    public Completor getCompletor()
    {
        return new ArgumentCompletor(
                new Completor[] { new SimpleCompletor(getName()), new FileNameCompletor(), new NullCompletor() });
    }

    @Override
    public boolean prerequisiteInteractiveCommand(final String[] args)
    {
        if (args.length < 2)
        {
            System.out.println("Current party key ring is "
                    + (getContext().getPartyRing() == null ? "<unset>" : getContext().getPartyRing().getRingFileName()));
            return false;
        }

        return true;
    }

    @Override
    public void executeInteractiveCommand(final String[] args)
    {
        String ringFile = args[1];

        System.out.print("Loading key ring " + ringFile + "...");

        try
        {
            getContext().getPartyRing().load(ringFile);
            System.out.println("...ok! " + getContext().getPartyRing().size() + " Keys loaded");

        }
        catch (RuntimeException re)
        {
            throw re;
        }
        catch (Exception e)
        {
            System.out.println("... failed! Could not read keyRing " + ringFile + ": " + e.getMessage());
        }
        finally
        {
            getContext().updatePartyRing(true);
        }
    }
} public class PublicRingCommand extends AbstractCommand implements Command
{

    public PublicRingCommand()
    {
    }

    public String getName()
    {
        return "publicring";
    }

    public String getHelp()
    {
        return "sets the public key ring";
    }

    public Option getCommandLineOption()
    {
        Option option = super.getCommandLineOption();
        option.setArgs(1);
        option.setArgName("file");
        return option;
    }

    public Completor getCompletor()
    {
        return new ArgumentCompletor(
                new Completor[] { new SimpleCompletor(getName()), new FileNameCompletor(), new NullCompletor() });
    }

    @Override
    public boolean prerequisiteInteractiveCommand(final String[] args)
    {
        if (args.length < 2)
        {
            System.out.println("Current public key ring is "
                    + (getContext().getPublicRing() == null ? "<unset>" : getContext().getPublicRing().getRingFileName()));
            return false;
        }

        return true;
    }

    @Override
    public void executeInteractiveCommand(final String[] args)
    {
        String ringFile = args[1];

        System.out.print("Loading key ring " + ringFile + "...");

        try
        {
            getContext().getPublicRing().load(ringFile);
            System.out.println("...ok! " + getContext().getPublicRing().size() + " Keys loaded");

        }
        catch (RuntimeException re)
        {
            throw re;
        }
        catch (Exception e)
        {
            System.out.println("... failed! Could not read keyRing " + ringFile + ": " + e.getMessage());
        }
        finally
        {
            getContext().updatePartyRing(false);
        }
    }
} public class QuitCommand extends AbstractCommand implements Command
{

    public QuitCommand()
    {
    }

    public String getName()
    {
        return "quit";
    }

    public String getHelp()
    {
        return "quit " + PGPSigner.APPLICATION_NAME;
    }

    @Override
    public Option getCommandLineOption()
    {
        return null;
    }

    public void executeInteractiveCommand(final String[] args)
    {
        getContext().setShutdown(true);
    }
} public class RemoveCommand extends AbstractCommand implements Command
{

    public String getName()
    {
        return "remove";
    }

    public String getHelp()
    {
        return "remove a key from the party list";
    }

    @Override
    public Option getCommandLineOption()
    {
        return null;
    }

    @Override
    public Completor getCompletor()
    {
        return new ArgumentCompletor(new Completor[] { new SimpleCompletor(getName()), new PartyKeyCompletor(getContext()),
                new NullCompletor() });
    }

    @Override
    public boolean prerequisiteInteractiveCommand(final String[] args)
    {
        if (args.length < 2)
        {
            System.out.println("Missing argument for " + args[0]);
            return false;
        }

        if (!getContext().getPartyRing().getVisibleKeys().containsId(new KeyId(args[1])))
        {
            System.out.println("Key " + args[1] + " is not on the party key ring!");
            return false;
        }

        return true;
    }

    @Override
    public void executeInteractiveCommand(final String[] args)
    {
        getContext().getPartyRing().setVisible(new KeyId(args[1]), false);
    }
} public class ResetCommand extends AbstractCommand implements Command
{

    public String getName()
    {
        return "reset";
    }

    public String getHelp()
    {
        return "reset all application state";
    }

    @Override
    public Option getCommandLineOption()
    {
        return null;
    }

    @Override
    public void executeInteractiveCommand(final String[] args)
    {
        getContext().reset();
    }
} public class SecretRingCommand extends AbstractCommand implements Command
{

    public SecretRingCommand()
    {
    }

    public String getName()
    {
        return "secretring";
    }

    public String getHelp()
    {
        return "sets the secret key ring";
    }

    public Option getCommandLineOption()
    {
        Option option = super.getCommandLineOption();
        option.setArgs(1);
        option.setArgName("file");
        return option;
    }

    public Completor getCompletor()
    {
        return new ArgumentCompletor(
                new Completor[] { new SimpleCompletor(getName()), new FileNameCompletor(), new NullCompletor() });
    }

    @Override
    public boolean prerequisiteInteractiveCommand(final String[] args)
    {
        if (args.length < 2)
        {
            System.out.println("Current secret key ring is "
                    + (getContext().getSecretRing() == null ? "<unset>" : getContext().getSecretRing().getRingFileName()));
            return false;
        }

        return true;
    }

    @Override
    public void executeInteractiveCommand(final String[] args)
    {
        String ringFile = args[1];

        System.out.print("Loading key ring " + ringFile + "...");

        try
        {
            getContext().getSecretRing().load(ringFile);
            System.out.println("...ok! " + getContext().getSecretRing().size() + " Keys loaded");

        }
        catch (RuntimeException re)
        {
            throw re;
        }
        catch (Exception e)
        {
            System.out.println("... failed! Could not read keyRing " + ringFile + ": " + e.getMessage());
        }
        finally
        {
            getContext().updatePartyRing(false);
        }
    }
} public class ShowCommand extends AbstractCommand implements Command
{

    public String getName()
    {
        return "show";
    }

    public String getHelp()
    {
        return "show the current options";
    }

    @Override
    public Option getCommandLineOption()
    {
        return null;
    }

    @Override
    public void executeInteractiveCommand(final String[] args)
    {
        SecretKeyRing secretRing = getContext().getSecretRing();
        PublicKeyRing publicRing = getContext().getPublicRing();
        PublicKeyRing partyRing = getContext().getPartyRing().getVisibleKeys();
        SecretKey signKey = getContext().getSignKey();

        System.out.println("Secret File: " + DisplayHelpers.showNullValue(secretRing.getRingFileName())
                + ((secretRing.size() > 0) ? " (" + secretRing.size() + " Keys loaded)" : ""));
        System.out.println("Public File: " + DisplayHelpers.showNullValue(publicRing.getRingFileName())
                + ((publicRing.size() > 0) ? " (" + publicRing.size() + " Keys loaded)" : ""));
        System.out.println("Party File:  " + DisplayHelpers.showNullValue(partyRing.getRingFileName())
                + ((partyRing.size() > 0) ? " (" + partyRing.size() + " Keys loaded)" : ""));
        System.out.println("Sign key:    " + DisplayHelpers.showNullValue(DisplayHelpers.showKey(signKey))
                + (signKey != null && signKey.isUnlocked() ? " (unlocked)" : ""));
        System.out.println("Mail Server: " + DisplayHelpers.showNullValue(getContext().getMailServerHost())+ ":" + getContext().getMailServerPort());
        System.out.println("Key Server:  " + DisplayHelpers.showNullValue(getContext().getKeyServer()));
        System.out.println("Sign Event:  " + DisplayHelpers.showNullValue(getContext().getSignEvent()));
        System.out.println("Simulation:  " + (getContext().isSimulation() ? "yes" : "no"));
    }
} public class SignCommand extends AbstractCommand implements Command
{

    public SignCommand()
    {
    }

    public String getName()
    {
        return "sign";
    }

    public String getHelp()
    {
        return "sign keys on the party key ring";
    }

    @Override
    public Option getCommandLineOption()
    {
        return null;
    }

    @Override
    public boolean prerequisiteInteractiveCommand(final String[] args)
    {
        if (getContext().getSignKey() == null)
        {
            System.out.println("No sign key has been selected!");
            return false;
        }

        if (!getContext().getSignKey().isUnlocked())
        {
            System.out.println("Sign key must be unlocked before signing!");
            return false;
        }

        return true;
    }

    @Override
    public void executeInteractiveCommand(final String[] args)
    {

        PGPSignatureGenerator signatureGenerator = null;

        SecretKey signKey = getContext().getSignKey();
        PGPPublicKey pubKey = signKey.getPGPPublicKey();

        try
        {
            signatureGenerator = new PGPSignatureGenerator(pubKey.getAlgorithm(), PGPUtil.SHA1, "BC");
            signatureGenerator.initSign(PGPSignature.DEFAULT_CERTIFICATION, signKey.getPGPPrivateKey());

            PGPSignatureSubpacketGenerator subpacketGenerator = new PGPSignatureSubpacketGenerator();
            for (Iterator it = pubKey.getUserIDs(); it.hasNext();)
            {
                subpacketGenerator.setSignerUserID(false, (String) it.next());
                signatureGenerator.setHashedSubpackets(subpacketGenerator.generate());
            }
        }
        catch (RuntimeException re)
        {
            throw re;
        }
        catch (Exception e)
        {
            System.out.println("Could not generate signature for signing.");
            return;
        }

        for (PublicKey key : getContext().getPartyRing().getVisibleKeys().values())
        {

            if (!key.isSigned())
            {
                try
                {
                    PGPPublicKey newKey = key.getPGPPublicKey();
                    PGPSignature signature = signatureGenerator.generateCertification(newKey);

                    for (Iterator it = key.getUserIds(); it.hasNext();)
                    {
                        String userId = (String) it.next();
                        newKey = PGPPublicKey.addCertification(newKey, userId, signature);
                    }

                    key.setPGPPublicKey(newKey);
                    key.setSigned(true);
                    System.out.println("Signed Key " + key.getKeyId() + " with " + signKey.getKeyId());

                }
                catch (RuntimeException re)
                {
                    throw re;
                }
                catch (Exception e)
                {
                    System.out.println("Could not sign key " + DisplayHelpers.showKey(key) + ", skipping.");
                }
            }
        }
    }
} public class SignEventCommand extends AbstractCommand implements Command
{

    public String getName()
    {
        return "signevent";
    }

    public String getHelp()
    {
        return "sets the key signing event name";
    }

    @Override
    public Option getCommandLineOption()
    {
        Option option = super.getCommandLineOption();
        option.setArgs(1);
        option.setArgName("eventname");
        return option;
    }

    @Override
    public boolean prerequisiteInteractiveCommand(final String[] args)
    {
        if (args.length < 2)
        {
            System.out.println("Current key signing event is " + DisplayHelpers.showNullValue(getContext().getSignEvent()));
            return false;
        }

        return true;
    }

    @Override
    public void executeInteractiveCommand(final String[] args)
    {
        getContext().setSignEvent(StringUtils.join(ArrayUtils.subarray(args, 1, args.length), ' '));
    }
} public class SignKeyCommand extends AbstractCommand implements Command
{

    public String getName()
    {
        return "signkey";
    }

    public String getHelp()
    {
        return "sets the signing key";
    }

    @Override
    public Option getCommandLineOption()
    {
        Option option = super.getCommandLineOption();
        option.setArgs(1);
        option.setArgName("key");
        return option;
    }

    @Override
    public Completor getCompletor()
    {
        return new ArgumentCompletor(new Completor[] { new SimpleCompletor(getName()), new SecretKeyCompletor(getContext()),
                new NullCompletor() });
    }

    @Override
    public boolean prerequisiteInteractiveCommand(final String[] args)
    {
        if (args.length < 2)
        {
            System.out.println("Current sign key is " + DisplayHelpers.showKey(getContext().getSignKey()));
            return false;
        }

        if (getContext().getSecretRing() == null)
        {
            System.out.println("Secret key ring has not yet been set, can not set sign key.");
            return false;
        }

        if (!getContext().getSecretRing().containsId(new KeyId(args[1])))
        {
            System.out.println("Sign key " + args[1] + " is not on secret keyring, sign key unchanged.");
            return false;
        }

        return true;
    }

    @Override
    public void executeInteractiveCommand(final String[] args)
    {
        SecretKey secretKey = getContext().getSecretRing().getKey(new KeyId(args[1]));
        getContext().setSignKey(secretKey);
        System.out.println("Sign Key is now " + DisplayHelpers.showKey(secretKey));
    }
} public class SimulateCommand extends AbstractCommand implements Command
{

    public SimulateCommand()
    {
    }

    public String getName()
    {
        return "simulate";
    }

    public String getHelp()
    {
        return "toggle simulation flag";
    }

    public void executeInteractiveCommand(final String[] args)
    {
        getContext().toggleSimulation();

        System.out.println(getContext().isSimulation() ? "Running in simulation mode" : "Running in life mode");
    }
} public class UnlockCommand extends AbstractCommand implements Command
{

    public String getName()
    {
        return "unlock";
    }

    public String getHelp()
    {
        return "unlock the current sign key";
    }

    @Override
    public Option getCommandLineOption()
    {
        return null;
    }

    @Override
    public boolean prerequisiteInteractiveCommand(final String[] args)
    {

        if (getContext().getSignKey() == null)
        {
            System.out.println("No sign key has been selected!");
            return false;
        }

        if (getContext().getSignKey().isUnlocked())
        {
            System.out.println("Sign key is already unlocked!");
            return false;
        }

        return true;
    }

    @Override
    public void executeInteractiveCommand(final String[] args)
    {

        SecretKey signKey = getContext().getSignKey();

        try
        {
            ConsoleReader reader = new ConsoleReader();
            reader.setUseHistory(false);
            reader.setBellEnabled(false);
            reader.setDefaultPrompt("Passphrase for " + signKey.getKeyId() + "> ");
            String passPhrase = reader.readLine('*');

            signKey.unlock(passPhrase);

            System.out.println("Private key " + DisplayHelpers.showKey(signKey) + " unlocked!");
        }
        catch (IOException ioe)
        {
            System.out.println("Could not read pass phrase: " + ioe.getMessage());
            return;
        }
        catch (PGPException pgpe)
        {
            System.out.println("Could not unlock private key.");
            return;
        }
        catch (NoSuchProviderException nspe)
        {
            System.out.println("Could not decrypt private key.");
            return;
        }
    }
} public class UploadCommand extends AbstractCommand implements Command
{

    public String getName()
    {
        return "upload";
    }

    public String getHelp()
    {
        return "upload signed keys to a public key server";
    }

    @Override
    public boolean prerequisiteInteractiveCommand(final String[] args)
    {
        if (StringUtils.isEmpty(getContext().getKeyServer()))
        {
            System.out.println("Key server must be set!");
            return false;
        }

        return true;
    }

    @Override
    public Option getCommandLineOption()
    {
        return null;
    }

    @Override
    public void executeInteractiveCommand(final String[] args)
    {
        HKPSender sender = new HKPSender(getContext().getKeyServer());

        for (PublicKey key : getContext().getPartyRing().getVisibleKeys().values())
        {

            if (key.isSigned() && !key.isUploaded())
            {
                System.out.print("Uploading Key " + key.getKeyId() + " to key server " + getContext().getKeyServer() + "...");

                try
                {
                    if (getContext().isSimulation())
                    {
                        System.out.println("... ok! (simulated)");
                        key.setUploaded(true);
                    }
                    else
                    {

                        if (sender.uploadKey(key.getArmor()))
                        {
                            System.out.println("... ok!");
                            key.setUploaded(true);
                        }
                        else
                        {
                            System.out.println("... failed!");
                        }
                    }
                }
                catch (IOException ioe)
                {
                    System.out.println("... failed (exception!");
                }
            }
        }
    }
} public class WriteCommand extends AbstractCommand implements Command
{

    public String getName()
    {
        return "write";
    }

    public String getHelp()
    {
        return "write an ASCII-armored key from the party list";
    }

    @Override
    public Option getCommandLineOption()
    {
        return null;
    }

    @Override
    public Completor getCompletor()
    {
        return new ArgumentCompletor(new Completor[] { new SimpleCompletor(getName()), new PartyKeyCompletor(getContext()),
                new NullCompletor() });
    }

    @Override
    public boolean prerequisiteInteractiveCommand(final String[] args)
    {
        if (args.length < 2)
        {
            System.out.println("Missing argument for " + args[0]);
            return false;
        }

        if (!getContext().getPartyRing().getVisibleKeys().containsId(new KeyId(args[1])))
        {
            System.out.println("Key " + args[1] + " is not on the party key ring!");
            return false;
        }

        return true;
    }

    @Override
    public void executeInteractiveCommand(final String[] args)
    {

        PublicKey key = getContext().getPartyRing().getVisibleKeys().getKey(new KeyId(args[1]));

        try
        {
            String armoredKey = new String(key.getArmor(), "ISO-8859-1");
            System.out.println(armoredKey);
        }
        catch (IOException ioe)
        {
            System.out.println("Could not write key " + DisplayHelpers.showKey(key));
        }
    }
} public class AppContext
{

    @SuppressWarnings("unchecked")
    private Map<String, Command> commandMap = (Map<String, Command>) ListOrderedMap.decorate(new HashMap<String, Command>());

    private Options options = new Options();

    private final PublicKeyRing partyRing = new PublicKeyRing();

    private final PublicKeyRing publicRing = new PublicKeyRing();

    private final SecretKeyRing secretRing = new SecretKeyRing();

    private SecretKey signKey = null;

    private String mailServerHost = null;

    private int mailServerPort = -1;

    private String keyServer = null;

    private String signEvent = null;

    private boolean shutdown = false;

    private boolean simulation = false;

    public void reset()
    {
        this.partyRing.clear();
        this.publicRing.clear();
        this.secretRing.clear();
        this.signKey = null;
        this.mailServerHost = null;
        this.keyServer = null;
        this.signEvent = null;
        this.simulation = false;
    }

    public Options getOptions()
    {
        return options;
    }

    public void addCommand(final Command command)
    {
        command.setContext(this);
        commandMap.put(command.getName(), command);

        if (command.hasCommandLineOption())
        {
            options.addOption(command.getCommandLineOption());
        }
    }

    public Collection<Command> getCommands()
    {
        return commandMap.values();
    }

    public void toggleSimulation()
    {
        simulation = !simulation;
    }

    public boolean isSimulation()
    {
        return simulation;
    }

    public boolean isShutdown()
    {
        return shutdown;
    }

    public void setShutdown(final boolean shutdown)
    {
        this.shutdown = shutdown;
    }

    public PublicKeyRing getPartyRing()
    {
        return partyRing;
    }

    public SecretKeyRing getSecretRing()
    {
        return secretRing;
    }

    public PublicKeyRing getPublicRing()
    {
        return publicRing;
    }

    public SecretKey getSignKey()
    {
        return this.signKey;
    }

    public void setSignKey(final SecretKey signKey)
    {
        this.signKey = signKey;
        updatePartyRing(true);
    }

    public String getSignEvent()
    {
        return this.signEvent;
    }

    public void setSignEvent(final String signEvent)
    {
        this.signEvent = signEvent;
    }

    public String getKeyServer()
    {
        return this.keyServer;
    }

    public void setKeyServer(final String keyServer)
    {
        this.keyServer = keyServer;
    }

    public String getMailServerHost()
    {
        return this.mailServerHost;
    }

    public void setMailServerHost(final String mailServerHost)
    {
        this.mailServerHost = mailServerHost;
    }

    public int getMailServerPort()
    {
        if (this.mailServerPort == -1) {
            return 25; // SMTP
        }
        return this.mailServerPort;
    }

    public void setMailServerPort(final int mailServerPort)
    {
        this.mailServerPort = mailServerPort;
    }

    public void updatePartyRing(boolean resetAllFlags)
    {

        for (KeyId entry : partyRing.getIds())
        {
            if (resetAllFlags)
            {
                partyRing.resetAllFlags(entry);
            }

            partyRing.setVisible(entry, true);

            if (signKey != null)
            {
                // If we ever signed that key, there is a good chance, that it
                // is on our public keyring. Check there.
                if (publicRing.containsId(entry))
                {
                    PublicKey pubKey = publicRing.getKey(entry);
                    if (pubKey.isSignedWith(signKey))
                    {
                        System.out.println("Party Key " + pubKey.getKeyId() + " has already been signed with Key "
                                + signKey.getKeyId() + ", skipping.");
                        partyRing.setVisible(entry, false);
                    }
                }
            }
        }

        for (KeyId entry : secretRing.getIds())
        {
            if (partyRing.containsId(entry))
            {
                System.out.println("Party Key " + entry + " is on secret keyring, skipping.");
                partyRing.setVisible(entry, false);
            }
        }
    }
}
import java.security.Provider;
import java.security.Security;
import java.util.ArrayList;
import java.util.List;
import jline.Completor;
import jline.ConsoleReader;
import jline.History;
import jline.MultiCompletor;
import jline.Terminal;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.ParseException;
import org.apache.commons.lang.StringUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import de.softwareforge.pgpsigner.commands.Command;
import de.softwareforge.pgpsigner.commands.HelpCommand;
import de.softwareforge.pgpsigner.commands.KeyServerCommand;
import de.softwareforge.pgpsigner.commands.ListCommand;
import de.softwareforge.pgpsigner.commands.MailCommand;
import de.softwareforge.pgpsigner.commands.MailServerCommand;
import de.softwareforge.pgpsigner.commands.PartyRingCommand;
import de.softwareforge.pgpsigner.commands.PublicRingCommand;
import de.softwareforge.pgpsigner.commands.QuitCommand;
import de.softwareforge.pgpsigner.commands.RemoveCommand;
import de.softwareforge.pgpsigner.commands.ResetCommand;
import de.softwareforge.pgpsigner.commands.SecretRingCommand;
import de.softwareforge.pgpsigner.commands.ShowCommand;
import de.softwareforge.pgpsigner.commands.SignCommand;
import de.softwareforge.pgpsigner.commands.SignEventCommand;
import de.softwareforge.pgpsigner.commands.SignKeyCommand;
import de.softwareforge.pgpsigner.commands.SimulateCommand;
import de.softwareforge.pgpsigner.commands.UnlockCommand;
import de.softwareforge.pgpsigner.commands.UploadCommand;
import de.softwareforge.pgpsigner.commands.WriteCommand;
import de.softwareforge.pgpsigner.util.AppContext;
package de.softwareforge.pgpsigner;

/*
 * Copyright (C) 2007 Henning P. Schmiedehausen
 *
 * See the NOTICE file distributed with this work for additional
 * information
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License.
 *
 */





/**
 * This is the PGPSigner main class.
 *
 * @author <a href="mailto:henning@schmiedehausen.org">Henning P. Schmiedehausen</a>
 * @version $Id$
 */

public class PGPSigner
{

    public static final String APPLICATION_NAME = "PGPSigner";

    public static final String APPLICATION_VERSION = APPLICATION_NAME + " V1.0";

    private final AppContext context = new AppContext();

    private Thread shutdownHook = new Thread()
    {
        public void run()
        {
            System.out.println();
            System.out.println("Goodbye");
        }
    };

    public static void main(final String[] args)
    {
        Security.addProvider(new BouncyCastleProvider());

        /*
         * Debug Code. Add a Security Manager and a policy. 
         * Suggested by Simon Tuffs, unfortunately does not really help
         * the one-jar problem.

        if (System.getSecurityManager() == null) {
            String policy = System.getProperty("java.security.policy");

            if (policy == null) {
                System.setProperty("java.security.policy", "onejar:/pgpsigner.policy");
            }
            System.setSecurityManager(new SecurityManager());
        }
        */

        /*
         * DEBUG: Display all registered Security providers
         *
        Provider[] providers = Security.getProviders();
        for (int i=0; i < providers.length; i++) {
            System.out.println(providers[i].toString());
        }
        */

        PGPSigner pgpSigner = new PGPSigner(args);

        try
        {
            pgpSigner.run();
        }
        catch (RuntimeException re)
        {
            re.printStackTrace();
            throw re;
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        System.exit(0);
    }

    private PGPSigner(final String[] args)
    {
        registerCommand(new HelpCommand());
        registerCommand(new QuitCommand());
        registerCommand(new ResetCommand());