View Javadoc
1   package org.apache.maven.plugins.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.ByteArrayInputStream;
23  import java.io.File;
24  import java.io.InputStream;
25  
26  import org.apache.maven.plugin.MojoExecutionException;
27  import org.codehaus.plexus.util.Os;
28  import org.codehaus.plexus.util.StringUtils;
29  import org.codehaus.plexus.util.cli.CommandLineException;
30  import org.codehaus.plexus.util.cli.CommandLineUtils;
31  import org.codehaus.plexus.util.cli.Commandline;
32  import org.codehaus.plexus.util.cli.DefaultConsumer;
33  
34  /**
35   * A signer implementation that uses the GnuPG command line executable.
36   */
37  public class GpgSigner
38      extends AbstractGpgSigner
39  {
40      private String executable;
41  
42      public GpgSigner( String executable )
43      {
44          this.executable = executable;
45      }
46  
47      /**
48       * {@inheritDoc}
49       */
50      @Override
51      protected void generateSignatureForFile( File file, File signature )
52          throws MojoExecutionException
53      {
54          // ----------------------------------------------------------------------------
55          // Set up the command line
56          // ----------------------------------------------------------------------------
57  
58          Commandline cmd = new Commandline();
59  
60          if ( StringUtils.isNotEmpty( executable ) )
61          {
62              cmd.setExecutable( executable );
63          }
64          else
65          {
66              cmd.setExecutable( "gpg" + ( Os.isFamily( Os.FAMILY_WINDOWS ) ? ".exe" : "" ) );
67          }
68  
69          GpgVersionParser versionParser = GpgVersionParser.parse( executable );
70  
71          GpgVersion gpgVersion = versionParser.getGpgVersion();
72  
73          getLog().debug( gpgVersion.toString() );
74  
75          if ( args != null )
76          {
77              for ( String arg : args )
78              {
79                  cmd.createArg().setValue( arg );
80              }
81          }
82  
83          if ( homeDir != null )
84          {
85              cmd.createArg().setValue( "--homedir" );
86              cmd.createArg().setFile( homeDir );
87          }
88  
89          if ( gpgVersion.isBefore( GpgVersion.parse( "2.1" ) ) )
90          {
91              if ( useAgent )
92              {
93                  cmd.createArg().setValue( "--use-agent" );
94              }
95              else
96              {
97                  cmd.createArg().setValue( "--no-use-agent" );
98              }
99          }
100 
101         InputStream in = null;
102         if ( null != passphrase )
103         {
104             if ( gpgVersion.isAtLeast( GpgVersion.parse( "2.0" ) ) )
105             {
106                 // required for option --passphrase-fd since GPG 2.0
107                 cmd.createArg().setValue( "--batch" );
108             }
109 
110             if ( gpgVersion.isAtLeast( GpgVersion.parse( "2.1" ) ) )
111             {
112                 // required for option --passphrase-fd since GPG 2.1
113                 cmd.createArg().setValue( "--pinentry-mode" );
114                 cmd.createArg().setValue( "loopback" );
115             }
116 
117             // make --passphrase-fd effective in gpg2
118             cmd.createArg().setValue( "--passphrase-fd" );
119             cmd.createArg().setValue( "0" );
120 
121             // Prepare the input stream which will be used to pass the passphrase to the executable
122             in = new ByteArrayInputStream( passphrase.getBytes() );
123         }
124 
125         if ( null != keyname )
126         {
127             cmd.createArg().setValue( "--local-user" );
128 
129             cmd.createArg().setValue( keyname );
130         }
131 
132         cmd.createArg().setValue( "--armor" );
133 
134         cmd.createArg().setValue( "--detach-sign" );
135 
136         if ( getLog().isDebugEnabled() )
137         {
138             // instruct GPG to write status information to stdout
139             cmd.createArg().setValue( "--status-fd" );
140             cmd.createArg().setValue( "1" );
141         }
142 
143         if ( !isInteractive )
144         {
145             cmd.createArg().setValue( "--batch" );
146             cmd.createArg().setValue( "--no-tty" );
147 
148             if ( null == passphrase && gpgVersion.isAtLeast( GpgVersion.parse( "2.1" ) ) )
149             {
150                 // prevent GPG from spawning input prompts in Maven non-interactive mode
151                 cmd.createArg().setValue( "--pinentry-mode" );
152                 cmd.createArg().setValue( "error" );
153             }
154         }
155 
156         if ( !defaultKeyring )
157         {
158             cmd.createArg().setValue( "--no-default-keyring" );
159         }
160 
161         if ( StringUtils.isNotEmpty( secretKeyring ) )
162         {
163             if ( gpgVersion.isBefore( GpgVersion.parse( "2.1" ) ) )
164             {
165                 cmd.createArg().setValue( "--secret-keyring" );
166                 cmd.createArg().setValue( secretKeyring );
167             }
168             else
169             {
170                 getLog().warn( "'secretKeyring' is an obsolete option and ignored. All secret keys "
171                     + "are stored in the ‘private-keys-v1.d’ directory below the GnuPG home directory." );
172             }
173         }
174 
175         if ( StringUtils.isNotEmpty( publicKeyring ) )
176         {
177             cmd.createArg().setValue( "--keyring" );
178             cmd.createArg().setValue( publicKeyring );
179         }
180 
181         if ( "once".equalsIgnoreCase( lockMode ) )
182         {
183             cmd.createArg().setValue( "--lock-once" );
184         }
185         else if ( "multiple".equalsIgnoreCase( lockMode ) )
186         {
187             cmd.createArg().setValue( "--lock-multiple" );
188         }
189         else if ( "never".equalsIgnoreCase( lockMode ) )
190         {
191             cmd.createArg().setValue( "--lock-never" );
192         }
193 
194         cmd.createArg().setValue( "--output" );
195         cmd.createArg().setFile( signature );
196 
197         cmd.createArg().setFile( file );
198 
199         // ----------------------------------------------------------------------------
200         // Execute the command line
201         // ----------------------------------------------------------------------------
202 
203         try
204         {
205             int exitCode = CommandLineUtils.executeCommandLine( cmd, in, new DefaultConsumer(), new DefaultConsumer() );
206 
207             if ( exitCode != 0 )
208             {
209                 throw new MojoExecutionException( "Exit code: " + exitCode );
210             }
211         }
212         catch ( CommandLineException e )
213         {
214             throw new MojoExecutionException( "Unable to execute gpg command", e );
215         }
216     }
217 
218 }