001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.openpgp.ant;
018    
019    import org.apache.tools.ant.Task;
020    import org.apache.tools.ant.BuildException;
021    import org.apache.tools.ant.DirectoryScanner;
022    import org.apache.tools.ant.util.FileNameMapper;
023    import org.apache.tools.ant.util.GlobPatternMapper;
024    import org.apache.tools.ant.util.FileUtils;
025    import org.apache.tools.ant.types.FileSet;
026    import org.apache.tools.ant.types.Mapper;
027    import org.apache.commons.openpgp.*;
028    import org.bouncycastle.openpgp.PGPException;
029    
030    import java.io.*;
031    import java.util.Collection;
032    import java.util.ArrayList;
033    import java.util.Iterator;
034    
035    /**
036     */
037    public class OpenPgpSignerTask extends Task {
038        private File secring;
039        private File pubring;
040        private String password;
041        private String keyId;
042        private Collection<FileSet> tosign = new ArrayList<FileSet>();
043        private File artefact;
044        private boolean asciiarmor = true;
045        private Mapper mapperElement;
046    
047        /**
048         * set the secret keyring
049         * @param secring secret keyring file
050         */
051        public void setSecring(File secring) {
052            this.secring = secring;
053        }
054    
055        /**
056         * set the public keyring
057         * @param pubring public keyring file
058         */
059        public void setPubring(File pubring) {
060            this.pubring = pubring;
061        }
062    
063        /**
064         * set the key id
065         * @param keyId
066         */
067        public void setKeyId(String keyId) {
068            this.keyId = keyId;
069        }
070    
071        /**
072         * asciiarmor the signature ?
073         * @param asciiarmor ascii armored signature ?
074         */
075        public void setAsciiarmor(boolean asciiarmor) {
076            this.asciiarmor = asciiarmor;
077        }
078    
079        /**
080         * set the value of the password
081         * @param password value of the password
082         */
083        public void setPassword(String password) {
084            this.password = password;
085        }
086    
087        /**
088         * artefact to be signed
089         * @param artefact artefact to be signed
090         */
091        public void setArtefact(File artefact) {
092            this.artefact = artefact;
093        }
094    
095    
096        public void add(FileSet fs) {
097            tosign.add(fs);
098        }
099    
100        /**
101         * Define the mapper to map source to destination files.
102         * @return a mapper to be configured.
103         * @exception org.apache.tools.ant.BuildException if more than one mapper is defined.
104         */
105        public Mapper createMapper() throws BuildException {
106            if (mapperElement != null) {
107                throw new BuildException("Cannot define more than one mapper",
108                        getLocation());
109            }
110            mapperElement = new Mapper(getProject());
111            return mapperElement;
112        }
113    
114        public void execute() {
115            if (secring == null) {
116                throw new BuildException("secring attribute compulsory");
117            }
118            if (pubring == null) {
119                throw new BuildException("pubring attribute compulsory");
120            }
121            if (password == null) {
122                throw new BuildException("password attribute compulsory");
123            }
124            if (tosign.size() == 0 && artefact == null) {
125                throw new BuildException("supply the attribute 'artefact' or one nested fileset");
126            }
127            if (!secring.exists() || !secring.canRead()) {
128                throw new  BuildException("secret keyring file '" + secring.getAbsolutePath() + "' does not exist or is not readable");
129            }
130            if (!pubring.exists() || !pubring.canRead()) {
131                throw new  BuildException("public keyring file '" + pubring.getAbsolutePath() + "' does not exist or is not readable");
132            }
133            FileInputStream secStream;
134            FileInputStream pubStream;
135            KeyRing keyRing = null;
136            try {
137                secStream = new FileInputStream(secring);
138                pubStream = new FileInputStream(pubring);
139                keyRing = new BouncyCastleKeyRing(secStream,
140                        pubStream, password.toCharArray() );
141            } catch (IOException ioe) {
142                throw new BuildException(ioe);
143            } catch (PGPException pgpe) {
144                throw new BuildException(pgpe);
145            }
146            if (artefact != null) {
147                dosign(keyRing, artefact);
148            }
149            if (tosign.size() != 0) {
150                for (FileSet fileset : tosign) {
151                    dosign(keyRing, fileset);
152                }
153            }
154            FileUtils.close(secStream);
155            FileUtils.close(pubStream);
156        }
157        private void dosign(KeyRing keyRing, FileSet fs) {
158            DirectoryScanner ds = fs.getDirectoryScanner(getProject());
159            String[] artefacts = ds.getIncludedFiles();
160            for (String artefact : artefacts) {
161                dosign(keyRing, new File(fs.getDir(getProject()), artefact), fs.getDir(getProject()), artefact);
162            }
163        }
164        private void dosign(KeyRing keyRing, File oneartefact) {
165            dosign(keyRing, oneartefact, oneartefact.getParentFile(), oneartefact.getName());
166        }
167        private void dosign(KeyRing keyRing, File oneartefact, File basedir, String relpath) {
168            FileInputStream fis = null;
169            FileOutputStream fos = null;
170            File signature;
171    
172            try {
173                fis = new FileInputStream(oneartefact);
174                FileNameMapper mapper = getMapper();
175                String [] mappedFiles = mapper.mapFileName(relpath);
176                if (mappedFiles == null || mappedFiles.length != 1) {
177                    throw new BuildException("mapper returned more or less than one output");
178                }
179                signature = new File(basedir, mappedFiles[0]);
180                fos = new FileOutputStream(signature);
181                OpenPgpSigner signer = new BouncyCastleOpenPgpSigner();
182                signer.detachedSign(fis, fos, keyId, keyRing, asciiarmor);
183            } catch (FileNotFoundException fnfe) {
184                throw new BuildException(fnfe);
185            } catch (IOException ioe) {
186                throw new BuildException(ioe);
187            } catch (OpenPgpException opgpe) {
188                throw new BuildException(opgpe);
189            }
190            FileUtils.close(fos);
191            FileUtils.close(fis);
192    
193        }
194        /**
195         * returns the mapper to use based on nested elements or the
196         */
197        private FileNameMapper getMapper() {
198            FileNameMapper mapper = null;
199            if (mapperElement != null) {
200                mapper = mapperElement.getImplementation();
201            } else {
202                mapper = new GlobPatternMapper();
203                mapper.setFrom("*");
204                if (asciiarmor) {
205                    mapper.setTo("*.asc");
206                } else {
207                    mapper.setTo("*.sig");
208                }
209            }
210            return mapper;
211        }
212    
213    }