View Javadoc

1   package org.apache.maven.plugin.gpg;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.BufferedReader;
23  import java.io.File;
24  import java.io.IOException;
25  import java.io.InputStreamReader;
26  import java.lang.reflect.InvocationTargetException;
27  import java.lang.reflect.Method;
28  import java.util.List;
29  
30  import org.apache.maven.plugin.MojoExecutionException;
31  import org.apache.maven.plugin.logging.Log;
32  import org.apache.maven.project.MavenProject;
33  
34  /**
35   * A base class for all classes that implements signing of files.
36   *
37   * @author Dennis Lundberg
38   * @since 1.5
39   */
40  public abstract class AbstractGpgSigner
41  {
42      public static final String SIGNATURE_EXTENSION = ".asc";
43  
44      protected boolean useAgent;
45  
46      protected boolean isInteractive = true;
47  
48      protected boolean defaultKeyring = true;
49  
50      protected String keyname;
51  
52      private Log log;
53  
54      protected String passphrase;
55  
56      private File outputDir;
57  
58      private File buildDir;
59  
60      private File baseDir;
61  
62      protected File homeDir;
63  
64      protected String secretKeyring;
65  
66      protected String publicKeyring;
67  
68      protected String lockMode;
69  
70      protected List<String> args;
71  
72      public Log getLog()
73      {
74          return log;
75      }
76  
77      public void setArgs( List<String> args )
78      {
79          this.args = args;
80      }
81  
82      public void setInteractive( boolean b )
83      {
84          isInteractive = b;
85      }
86  
87      public void setLockMode( String lockMode )
88      {
89          this.lockMode = lockMode;
90      }
91  
92      public void setUseAgent( boolean b )
93      {
94          useAgent = b;
95      }
96  
97      public void setDefaultKeyring( boolean enabled )
98      {
99          defaultKeyring = enabled;
100     }
101 
102     public void setKeyName( String s )
103     {
104         keyname = s;
105     }
106 
107     public void setLog( Log log )
108     {
109         this.log = log;
110     }
111 
112     public void setPassPhrase( String s )
113     {
114         passphrase = s;
115     }
116 
117     public void setOutputDirectory( File out )
118     {
119         outputDir = out;
120     }
121 
122     public void setBuildDirectory( File out )
123     {
124         buildDir = out;
125     }
126 
127     public void setBaseDirectory( File out )
128     {
129         baseDir = out;
130     }
131 
132     public void setHomeDirectory( File homeDirectory )
133     {
134         homeDir = homeDirectory;
135     }
136 
137     public void setSecretKeyring( String path )
138     {
139         secretKeyring = path;
140     }
141 
142     public void setPublicKeyring( String path )
143     {
144         publicKeyring = path;
145     }
146 
147     /**
148      * Create a detached signature file for the provided file.
149      *
150      * @param file The file to sign
151      * @return A reference to the generated signature file
152      * @throws org.apache.maven.plugin.MojoExecutionException
153      */
154     public File generateSignatureForArtifact( File file )
155         throws MojoExecutionException
156     {
157         // ----------------------------------------------------------------------------
158         // Set up the file and directory for the signature file
159         // ----------------------------------------------------------------------------
160 
161         File signature = new File( file + SIGNATURE_EXTENSION );
162 
163         boolean isInBuildDir = false;
164         if ( buildDir != null )
165         {
166             File parent = signature.getParentFile();
167             if ( buildDir.equals( parent ) )
168             {
169                 isInBuildDir = true;
170             }
171         }
172         if ( !isInBuildDir && outputDir != null )
173         {
174             String fileDirectory = "";
175             File signatureDirectory = signature;
176 
177             while ( ( signatureDirectory = signatureDirectory.getParentFile() ) != null )
178             {
179                 if ( !signatureDirectory.equals( baseDir ) )
180                 {
181                     fileDirectory = signatureDirectory.getName() + File.separatorChar + fileDirectory;
182                 }
183                 else
184                 {
185                     break;
186                 }
187             }
188             signatureDirectory = new File( outputDir, fileDirectory );
189             if ( !signatureDirectory.exists() )
190             {
191                 signatureDirectory.mkdirs();
192             }
193             signature = new File( signatureDirectory, file.getName() + SIGNATURE_EXTENSION );
194         }
195 
196         if ( signature.exists() )
197         {
198             signature.delete();
199         }
200 
201         // ----------------------------------------------------------------------------
202         // Generate the signature file
203         // ----------------------------------------------------------------------------
204 
205         generateSignatureForFile( file, signature );
206 
207         return signature;
208     }
209 
210     /**
211      * Generate the detached signature file for the provided file.
212      *
213      * @param file The file to sign
214      * @param signature The file in which the generate signature will be put
215      * @throws MojoExecutionException
216      */
217     protected abstract void generateSignatureForFile( File file, File signature )
218         throws MojoExecutionException;
219 
220     private MavenProject findReactorProject( MavenProject prj )
221     {
222         if ( prj.getParent() != null && prj.getParent().getBasedir() != null && prj.getParent().getBasedir().exists() )
223         {
224             return findReactorProject( prj.getParent() );
225         }
226         return prj;
227     }
228 
229     public String getPassphrase( MavenProject project )
230         throws IOException
231     {
232         String pass = null;
233 
234         if ( project != null )
235         {
236             pass = project.getProperties().getProperty( "gpg.passphrase" );
237             if ( pass == null )
238             {
239                 MavenProject prj2 = findReactorProject( project );
240                 pass = prj2.getProperties().getProperty( "gpg.passphrase" );
241             }
242         }
243         if ( pass == null )
244         {
245             pass = readPassword( "GPG Passphrase: " );
246         }
247         if ( project != null )
248         {
249             findReactorProject( project ).getProperties().setProperty( "gpg.passphrase", pass );
250         }
251         return pass;
252     }
253 
254     private String readPassword( String prompt )
255         throws IOException
256     {
257         try {
258             return readPasswordJava16( prompt );
259         } catch ( IOException e ) {
260             throw e;
261         }
262         catch ( NoSuchMethodException e )
263         {
264             return readPasswordJava15( prompt );
265         }
266         catch ( IllegalAccessException e )
267         {
268             return readPasswordJava15( prompt );
269         }
270         catch ( InvocationTargetException e )
271         {
272             return readPasswordJava15( prompt );
273         }
274     }
275 
276     private String readPasswordJava16( String prompt )
277         throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException
278     {
279         Method consoleMethod = System.class.getMethod( "console" );
280         Object console = consoleMethod.invoke( null );
281         if ( console == null )
282         {
283             throw new IllegalAccessException( "console was null" );
284         }
285         Method readPasswordMethod = console.getClass().getMethod( "readPassword", String.class, Object[].class );
286         return new String( (char[]) readPasswordMethod.invoke( console, prompt, null ) );
287     }
288 
289     private String readPasswordJava15( String prompt )
290         throws IOException
291     {
292         BufferedReader in = new BufferedReader( new InputStreamReader( System.in ) );
293         while ( System.in.available() != 0 )
294         {
295             // there's some junk already on the input stream, consume it
296             // so we can get the real passphrase
297             System.in.read();
298         }
299 
300         System.out.print( prompt );
301         System.out.print( ' ' );
302         MaskingThread thread = new MaskingThread();
303         thread.start();
304         try
305         {
306 
307             return in.readLine();
308         }
309         finally
310         {
311             // stop masking
312             thread.stopMasking();
313 
314         }
315     }
316 
317     // based on ideas from http://java.sun.com/developer/technicalArticles/Security/pwordmask/
318     class MaskingThread
319         extends Thread
320     {
321         private volatile boolean stop;
322 
323         /**
324          * Begin masking until asked to stop.
325          */
326         public void run()
327         {
328             // this needs to be high priority to make sure the characters don't
329             // really get to the screen.
330 
331             int priority = Thread.currentThread().getPriority();
332             Thread.currentThread().setPriority( Thread.MAX_PRIORITY );
333 
334             try
335             {
336                 stop = false;
337                 while ( !stop )
338                 {
339                     // print a backspace + * to overwrite anything they type
340                     System.out.print( "\010*" );
341                     try
342                     {
343                         // attempt masking at this rate
344                         Thread.sleep( 1 );
345                     }
346                     catch ( InterruptedException iex )
347                     {
348                         Thread.currentThread().interrupt();
349                         return;
350                     }
351                 }
352             }
353             finally
354             {
355                 // restore the original priority
356                 Thread.currentThread().setPriority( priority );
357             }
358         }
359 
360         /**
361          * Instruct the thread to stop masking.
362          */
363         public void stopMasking()
364         {
365             this.stop = true;
366         }
367     }
368 }