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