Coverage Report - org.apache.maven.plugin.gpg.GpgSigner
 
Classes in this File Line Coverage Branch Coverage Complexity
GpgSigner
0%
0/108
0%
0/54
2.765
GpgSigner$MaskingThread
0%
0/15
0%
0/2
2.765
 
 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.ByteArrayInputStream;
 24  
 import java.io.File;
 25  
 import java.io.IOException;
 26  
 import java.io.InputStream;
 27  
 import java.io.InputStreamReader;
 28  
 
 29  
 import org.apache.maven.plugin.MojoExecutionException;
 30  
 import org.apache.maven.project.MavenProject;
 31  
 import org.codehaus.plexus.util.Os;
 32  
 import org.codehaus.plexus.util.StringUtils;
 33  
 import org.codehaus.plexus.util.cli.CommandLineException;
 34  
 import org.codehaus.plexus.util.cli.CommandLineUtils;
 35  
 import org.codehaus.plexus.util.cli.Commandline;
 36  
 import org.codehaus.plexus.util.cli.DefaultConsumer;
 37  
 
 38  0
 public class GpgSigner
 39  
 {
 40  
 
 41  
     public static final String SIGNATURE_EXTENSION = ".asc";
 42  
 
 43  
     private String executable;
 44  
  
 45  
     private boolean useAgent;
 46  
 
 47  0
     private boolean isInteractive = true;
 48  
 
 49  0
     private boolean defaultKeyring = true;
 50  
 
 51  
     private String keyname;
 52  
 
 53  
     private String passphrase;
 54  
 
 55  
     private File outputDir;
 56  
 
 57  
     private File buildDir;
 58  
 
 59  
     private File baseDir;
 60  
 
 61  
     private File homeDir;
 62  
 
 63  
     private String secretKeyring;
 64  
 
 65  
     private String publicKeyring;
 66  
 
 67  
     public void setExecutable( String executable )
 68  
     {
 69  0
         this.executable = executable;
 70  0
     }
 71  
 
 72  
     public void setInteractive( boolean b )
 73  
     {
 74  0
         isInteractive = b;
 75  0
     }
 76  
 
 77  
     public void setUseAgent( boolean b )
 78  
     {
 79  0
         useAgent = b;
 80  0
     }
 81  
 
 82  
     public void setDefaultKeyring( boolean enabled )
 83  
     {
 84  0
         defaultKeyring = enabled;
 85  0
     }
 86  
 
 87  
     public void setKeyName( String s )
 88  
     {
 89  0
         keyname = s;
 90  0
     }
 91  
 
 92  
     public void setPassPhrase( String s )
 93  
     {
 94  0
         passphrase = s;
 95  0
     }
 96  
 
 97  
     public void setOutputDirectory( File out )
 98  
     {
 99  0
         outputDir = out;
 100  0
     }
 101  
 
 102  
     public void setBuildDirectory( File out )
 103  
     {
 104  0
         buildDir = out;
 105  0
     }
 106  
 
 107  
     public void setBaseDirectory( File out )
 108  
     {
 109  0
         baseDir = out;
 110  0
     }
 111  
 
 112  
     public void setHomeDirectory( File homeDirectory )
 113  
     {
 114  0
         homeDir = homeDirectory;
 115  0
     }
 116  
 
 117  
     public void setSecretKeyring( String path )
 118  
     {
 119  0
         secretKeyring = path;
 120  0
     }
 121  
 
 122  
     public void setPublicKeyring( String path )
 123  
     {
 124  0
         publicKeyring = path;
 125  0
     }
 126  
 
 127  
     public File generateSignatureForArtifact( File file )
 128  
         throws MojoExecutionException
 129  
     {
 130  0
         File signature = new File( file + SIGNATURE_EXTENSION );
 131  
 
 132  0
         boolean isInBuildDir = false;
 133  0
         if ( buildDir != null )
 134  
         {
 135  0
             File parent = signature.getParentFile();
 136  0
             if ( buildDir.equals( parent ) )
 137  
             {
 138  0
                 isInBuildDir = true;
 139  
             }
 140  
         }
 141  0
         if ( !isInBuildDir && outputDir != null )
 142  
         {
 143  0
             String fileDirectory = "";
 144  0
             File signatureDirectory = signature;
 145  
 
 146  0
             while ( ( signatureDirectory = signatureDirectory.getParentFile() ) != null )
 147  
             {
 148  0
                 if ( !signatureDirectory.equals( baseDir ) )
 149  
                 {
 150  0
                     fileDirectory = signatureDirectory.getName() + File.separatorChar + fileDirectory;
 151  
                 }
 152  
                 else
 153  
                 {
 154  
                     break;
 155  
                 }
 156  
             }
 157  0
             signatureDirectory = new File( outputDir, fileDirectory );
 158  0
             if ( !signatureDirectory.exists() )
 159  
             {
 160  0
                 signatureDirectory.mkdirs();
 161  
             }
 162  0
             signature = new File( signatureDirectory, file.getName() + SIGNATURE_EXTENSION );
 163  
         }
 164  
 
 165  0
         if ( signature.exists() )
 166  
         {
 167  0
             signature.delete();
 168  
         }
 169  
 
 170  0
         Commandline cmd = new Commandline();
 171  
 
 172  0
         if ( StringUtils.isNotEmpty( executable ) )
 173  
         {
 174  0
             cmd.setExecutable( executable );
 175  
         }
 176  
         else
 177  
         {
 178  0
             cmd.setExecutable( "gpg" + ( Os.isFamily( Os.FAMILY_WINDOWS ) ? ".exe" : "" ) );
 179  
         }
 180  
 
 181  0
         if ( homeDir != null )
 182  
         {
 183  0
             cmd.createArg().setValue( "--homedir" );
 184  0
             cmd.createArg().setFile( homeDir );
 185  
         }
 186  
 
 187  0
         if ( useAgent )
 188  
         {
 189  0
             cmd.createArg().setValue( "--use-agent" );
 190  
         }
 191  
         else
 192  
         {
 193  0
             cmd.createArg().setValue( "--no-use-agent" );
 194  
         }
 195  
 
 196  0
         InputStream in = null;
 197  0
         if ( null != passphrase )
 198  
         {
 199  
             // make --passphrase-fd effective in gpg2
 200  0
             cmd.createArg().setValue( "--batch" );
 201  
 
 202  0
             cmd.createArg().setValue( "--passphrase-fd" );
 203  
 
 204  0
             cmd.createArg().setValue( "0" );
 205  
 
 206  
             // Prepare the input stream which will be used to pass the passphrase to the executable
 207  0
             in = new ByteArrayInputStream( passphrase.getBytes() );
 208  
         }
 209  
 
 210  0
         if ( null != keyname )
 211  
         {
 212  0
             cmd.createArg().setValue( "--local-user" );
 213  
 
 214  0
             cmd.createArg().setValue( keyname );
 215  
         }
 216  
 
 217  0
         cmd.createArg().setValue( "--armor" );
 218  
 
 219  0
         cmd.createArg().setValue( "--detach-sign" );
 220  
 
 221  0
         if ( !isInteractive )
 222  
         {
 223  0
             cmd.createArg().setValue( "--no-tty" );
 224  
         }
 225  
 
 226  0
         if ( !defaultKeyring )
 227  
         {
 228  0
             cmd.createArg().setValue( "--no-default-keyring" );
 229  
         }
 230  
 
 231  0
         if ( StringUtils.isNotEmpty( secretKeyring ) )
 232  
         {
 233  0
             cmd.createArg().setValue( "--secret-keyring" );
 234  0
             cmd.createArg().setValue( secretKeyring );
 235  
         }
 236  
 
 237  0
         if ( StringUtils.isNotEmpty( publicKeyring ) )
 238  
         {
 239  0
             cmd.createArg().setValue( "--keyring" );
 240  0
             cmd.createArg().setValue( publicKeyring );
 241  
         }
 242  
 
 243  0
         cmd.createArg().setValue( "--output" );
 244  0
         cmd.createArg().setFile( signature );
 245  
 
 246  0
         cmd.createArg().setFile( file );
 247  
 
 248  
         try
 249  
         {
 250  0
             int exitCode = CommandLineUtils.executeCommandLine( cmd, in, new DefaultConsumer(), new DefaultConsumer() );
 251  
 
 252  0
             if ( exitCode != 0 )
 253  
             {
 254  0
                 throw new MojoExecutionException( "Exit code: " + exitCode );
 255  
             }
 256  
         }
 257  0
         catch ( CommandLineException e )
 258  
         {
 259  0
             throw new MojoExecutionException( "Unable to execute gpg command", e );
 260  0
         }
 261  
 
 262  0
         return signature;
 263  
     }
 264  
 
 265  
     private MavenProject findReactorProject( MavenProject prj )
 266  
     {
 267  0
         if ( prj.getParent() != null && prj.getParent().getBasedir() != null && prj.getParent().getBasedir().exists() )
 268  
         {
 269  0
             return findReactorProject( prj.getParent() );
 270  
         }
 271  0
         return prj;
 272  
     }
 273  
 
 274  
     public String getPassphrase( MavenProject project )
 275  
         throws IOException
 276  
     {
 277  0
         String pass = null;
 278  
 
 279  0
         if ( project != null )
 280  
         {
 281  0
             pass = project.getProperties().getProperty( "gpg.passphrase" );
 282  0
             if ( pass == null )
 283  
             {
 284  0
                 MavenProject prj2 = findReactorProject( project );
 285  0
                 pass = prj2.getProperties().getProperty( "gpg.passphrase" );
 286  
             }
 287  
         }
 288  0
         if ( pass == null )
 289  
         {
 290  
             // TODO: with JDK 1.6, we could call System.console().readPassword("GPG Passphrase: ", null);
 291  
 
 292  0
             BufferedReader in = new BufferedReader( new InputStreamReader( System.in ) );
 293  0
             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  0
                 System.in.read();
 298  
             }
 299  
 
 300  0
             System.out.print( "GPG Passphrase:  " );
 301  0
             MaskingThread thread = new MaskingThread();
 302  0
             thread.start();
 303  
 
 304  0
             pass = in.readLine();
 305  
 
 306  
             // stop masking
 307  0
             thread.stopMasking();
 308  
         }
 309  0
         if ( project != null )
 310  
         {
 311  0
             findReactorProject( project ).getProperties().setProperty( "gpg.passphrase", pass );
 312  
         }
 313  0
         return pass;
 314  
     }
 315  
 
 316  
     // based on ideas from http://java.sun.com/developer/technicalArticles/Security/pwordmask/
 317  0
     class MaskingThread
 318  
         extends Thread
 319  
     {
 320  
         private volatile boolean stop;
 321  
 
 322  
         /**
 323  
          * Begin masking until asked to stop.
 324  
          */
 325  
         public void run()
 326  
         {
 327  
             // this needs to be high priority to make sure the characters don't
 328  
             // really get to the screen.
 329  
 
 330  0
             int priority = Thread.currentThread().getPriority();
 331  0
             Thread.currentThread().setPriority( Thread.MAX_PRIORITY );
 332  
 
 333  
             try
 334  
             {
 335  0
                 stop = false;
 336  0
                 while ( !stop )
 337  
                 {
 338  
                     // print a backspace + * to overwrite anything they type
 339  0
                     System.out.print( "\010*" );
 340  
                     try
 341  
                     {
 342  
                         // attempt masking at this rate
 343  0
                         Thread.sleep( 1 );
 344  
                     }
 345  0
                     catch ( InterruptedException iex )
 346  
                     {
 347  0
                         Thread.currentThread().interrupt();
 348  
                         return;
 349  0
                     }
 350  
                 }
 351  
             }
 352  
             finally
 353  
             {
 354  
                 // restore the original priority
 355  0
                 Thread.currentThread().setPriority( priority );
 356  0
             }
 357  0
         }
 358  
 
 359  
         /**
 360  
          * Instruct the thread to stop masking.
 361  
          */
 362  
         public void stopMasking()
 363  
         {
 364  0
             this.stop = true;
 365  0
         }
 366  
     }
 367  
 
 368  
 }