View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.plugins.gpg;
20  
21  import java.io.ByteArrayInputStream;
22  import java.io.File;
23  import java.io.InputStream;
24  
25  import org.apache.maven.plugin.MojoExecutionException;
26  import org.codehaus.plexus.util.Os;
27  import org.codehaus.plexus.util.StringUtils;
28  import org.codehaus.plexus.util.cli.CommandLineException;
29  import org.codehaus.plexus.util.cli.CommandLineUtils;
30  import org.codehaus.plexus.util.cli.Commandline;
31  import org.codehaus.plexus.util.cli.DefaultConsumer;
32  
33  /**
34   * A signer implementation that uses the GnuPG command line executable.
35   */
36  public class GpgSigner extends AbstractGpgSigner {
37      private String executable;
38  
39      public GpgSigner(String executable) {
40          this.executable = executable;
41      }
42  
43      /**
44       * {@inheritDoc}
45       */
46      @Override
47      protected void generateSignatureForFile(File file, File signature) throws MojoExecutionException {
48          // ----------------------------------------------------------------------------
49          // Set up the command line
50          // ----------------------------------------------------------------------------
51  
52          Commandline cmd = new Commandline();
53  
54          if (StringUtils.isNotEmpty(executable)) {
55              cmd.setExecutable(executable);
56          } else {
57              cmd.setExecutable("gpg" + (Os.isFamily(Os.FAMILY_WINDOWS) ? ".exe" : ""));
58          }
59  
60          GpgVersionParser versionParser = GpgVersionParser.parse(executable);
61  
62          GpgVersion gpgVersion = versionParser.getGpgVersion();
63          if (gpgVersion == null) {
64              throw new MojoExecutionException("Could not determine gpg version");
65          }
66  
67          getLog().debug(gpgVersion.toString());
68  
69          if (args != null) {
70              for (String arg : args) {
71                  cmd.createArg().setValue(arg);
72              }
73          }
74  
75          if (homeDir != null) {
76              cmd.createArg().setValue("--homedir");
77              cmd.createArg().setFile(homeDir);
78          }
79  
80          if (gpgVersion.isBefore(GpgVersion.parse("2.1"))) {
81              if (useAgent) {
82                  cmd.createArg().setValue("--use-agent");
83              } else {
84                  cmd.createArg().setValue("--no-use-agent");
85              }
86          }
87  
88          InputStream in = null;
89          if (null != passphrase) {
90              if (gpgVersion.isAtLeast(GpgVersion.parse("2.0"))) {
91                  // required for option --passphrase-fd since GPG 2.0
92                  cmd.createArg().setValue("--batch");
93              }
94  
95              if (gpgVersion.isAtLeast(GpgVersion.parse("2.1"))) {
96                  // required for option --passphrase-fd since GPG 2.1
97                  cmd.createArg().setValue("--pinentry-mode");
98                  cmd.createArg().setValue("loopback");
99              }
100 
101             // make --passphrase-fd effective in gpg2
102             cmd.createArg().setValue("--passphrase-fd");
103             cmd.createArg().setValue("0");
104 
105             // Prepare the input stream which will be used to pass the passphrase to the executable
106             in = new ByteArrayInputStream(passphrase.getBytes());
107         }
108 
109         if (null != keyname) {
110             cmd.createArg().setValue("--local-user");
111 
112             cmd.createArg().setValue(keyname);
113         }
114 
115         cmd.createArg().setValue("--armor");
116 
117         cmd.createArg().setValue("--detach-sign");
118 
119         if (getLog().isDebugEnabled()) {
120             // instruct GPG to write status information to stdout
121             cmd.createArg().setValue("--status-fd");
122             cmd.createArg().setValue("1");
123         }
124 
125         if (!isInteractive) {
126             cmd.createArg().setValue("--batch");
127             cmd.createArg().setValue("--no-tty");
128 
129             if (null == passphrase && gpgVersion.isAtLeast(GpgVersion.parse("2.1"))) {
130                 // prevent GPG from spawning input prompts in Maven non-interactive mode
131                 cmd.createArg().setValue("--pinentry-mode");
132                 cmd.createArg().setValue("error");
133             }
134         }
135 
136         if (!defaultKeyring) {
137             cmd.createArg().setValue("--no-default-keyring");
138         }
139 
140         if (StringUtils.isNotEmpty(secretKeyring)) {
141             if (gpgVersion.isBefore(GpgVersion.parse("2.1"))) {
142                 cmd.createArg().setValue("--secret-keyring");
143                 cmd.createArg().setValue(secretKeyring);
144             } else {
145                 getLog().warn("'secretKeyring' is an obsolete option and ignored. All secret keys "
146                         + "are stored in the ‘private-keys-v1.d’ directory below the GnuPG home directory.");
147             }
148         }
149 
150         if (StringUtils.isNotEmpty(publicKeyring)) {
151             cmd.createArg().setValue("--keyring");
152             cmd.createArg().setValue(publicKeyring);
153         }
154 
155         if ("once".equalsIgnoreCase(lockMode)) {
156             cmd.createArg().setValue("--lock-once");
157         } else if ("multiple".equalsIgnoreCase(lockMode)) {
158             cmd.createArg().setValue("--lock-multiple");
159         } else if ("never".equalsIgnoreCase(lockMode)) {
160             cmd.createArg().setValue("--lock-never");
161         }
162 
163         cmd.createArg().setValue("--output");
164         cmd.createArg().setFile(signature);
165 
166         cmd.createArg().setFile(file);
167 
168         // ----------------------------------------------------------------------------
169         // Execute the command line
170         // ----------------------------------------------------------------------------
171 
172         try {
173             int exitCode = CommandLineUtils.executeCommandLine(cmd, in, new DefaultConsumer(), new DefaultConsumer());
174 
175             if (exitCode != 0) {
176                 throw new MojoExecutionException("Exit code: " + exitCode);
177             }
178         } catch (CommandLineException e) {
179             throw new MojoExecutionException("Unable to execute gpg command", e);
180         }
181     }
182 }