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.scm.provider.svn.svnexe.command;
20  
21  import java.io.File;
22  import java.io.FileOutputStream;
23  import java.io.IOException;
24  import java.io.PrintStream;
25  import java.util.List;
26  
27  import org.apache.commons.lang3.StringUtils;
28  import org.apache.maven.scm.provider.svn.repository.SvnScmProviderRepository;
29  import org.apache.maven.scm.provider.svn.util.SvnUtil;
30  import org.codehaus.plexus.util.Os;
31  import org.codehaus.plexus.util.cli.CommandLineException;
32  import org.codehaus.plexus.util.cli.CommandLineUtils;
33  import org.codehaus.plexus.util.cli.Commandline;
34  import org.codehaus.plexus.util.cli.StreamConsumer;
35  import org.slf4j.Logger;
36  import org.slf4j.LoggerFactory;
37  
38  /**
39   * Command line construction utility.
40   *
41   * @author Brett Porter
42   * @author Olivier Lamy
43   */
44  public final class SvnCommandLineUtils {
45      private static final Logger LOGGER = LoggerFactory.getLogger(SvnCommandLineUtils.class);
46  
47      private SvnCommandLineUtils() {}
48  
49      public static void addTarget(Commandline cl, List<File> files) throws IOException {
50          if (files == null || files.isEmpty()) {
51              return;
52          }
53  
54          StringBuilder sb = new StringBuilder();
55          String ls = System.getProperty("line.separator");
56          for (File f : files) {
57              sb.append(f.getPath().replace('\\', '/'));
58              sb.append(ls);
59          }
60  
61          File targets = File.createTempFile("maven-scm-", "-targets");
62          try (PrintStream out = new PrintStream(new FileOutputStream(targets))) {
63              out.print(sb);
64          }
65  
66          cl.createArg().setValue("--targets");
67          cl.createArg().setValue(targets.getAbsolutePath());
68  
69          targets.deleteOnExit();
70      }
71  
72      public static Commandline getBaseSvnCommandLine(
73              File workingDirectory, SvnScmProviderRepository repository, boolean interactive) {
74          Commandline cl = new Commandline();
75  
76          cl.setExecutable("svn");
77          try {
78              cl.addSystemEnvironment();
79              cl.addEnvironment("LC_MESSAGES", "C");
80          } catch (Exception e) {
81              // Do nothing
82          }
83  
84          if (workingDirectory != null) {
85              cl.setWorkingDirectory(workingDirectory.getAbsolutePath());
86          }
87  
88          if (!StringUtils.isEmpty(System.getProperty("maven.scm.svn.config_directory"))) {
89              cl.createArg().setValue("--config-dir");
90              cl.createArg().setValue(System.getProperty("maven.scm.svn.config_directory"));
91          } else if (!StringUtils.isEmpty(SvnUtil.getSettings().getConfigDirectory())) {
92              cl.createArg().setValue("--config-dir");
93              cl.createArg().setValue(SvnUtil.getSettings().getConfigDirectory());
94          }
95  
96          boolean hasAuthInfo = false;
97          if (repository != null && !StringUtils.isEmpty(repository.getUser())) {
98              hasAuthInfo = true;
99              cl.createArg().setValue("--username");
100             cl.createArg().setValue(repository.getUser());
101         }
102 
103         if (repository != null && !StringUtils.isEmpty(repository.getPassword())) {
104             hasAuthInfo = true;
105             cl.createArg().setValue("--password");
106             cl.createArg().setValue(repository.getPassword());
107         }
108 
109         // [by Lenik] don't overwrite existing auth cache by default.
110         if (hasAuthInfo && !SvnUtil.getSettings().isUseAuthCache()) {
111             cl.createArg().setValue("--no-auth-cache");
112         }
113 
114         if (!interactive || SvnUtil.getSettings().isUseNonInteractive()) {
115             cl.createArg().setValue("--non-interactive");
116         }
117 
118         if (SvnUtil.getSettings().isTrustServerCert()) {
119             cl.createArg().setValue("--trust-server-cert");
120         }
121 
122         return cl;
123     }
124 
125     public static int execute(Commandline cl, StreamConsumer consumer, CommandLineUtils.StringStreamConsumer stderr)
126             throws CommandLineException {
127         // SCM-482: force English resource bundle
128         cl.addEnvironment("LC_MESSAGES", "en");
129 
130         int exitCode = CommandLineUtils.executeCommandLine(cl, consumer, stderr);
131 
132         exitCode = checkIfCleanUpIsNeeded(exitCode, cl, consumer, stderr);
133 
134         return exitCode;
135     }
136 
137     public static int execute(
138             Commandline cl, CommandLineUtils.StringStreamConsumer stdout, CommandLineUtils.StringStreamConsumer stderr)
139             throws CommandLineException {
140         int exitCode = CommandLineUtils.executeCommandLine(cl, stdout, stderr);
141 
142         exitCode = checkIfCleanUpIsNeeded(exitCode, cl, stdout, stderr);
143 
144         return exitCode;
145     }
146 
147     private static int checkIfCleanUpIsNeeded(
148             int exitCode, Commandline cl, StreamConsumer consumer, CommandLineUtils.StringStreamConsumer stderr)
149             throws CommandLineException {
150         if (exitCode != 0
151                 && stderr.getOutput() != null
152                 && stderr.getOutput().indexOf("'svn cleanup'") > 0
153                 && stderr.getOutput().indexOf("'svn help cleanup'") > 0) {
154             if (LOGGER.isInfoEnabled()) {
155                 LOGGER.info("Svn command failed due to some locks in working copy. We try to run a 'svn cleanup'.");
156             }
157 
158             if (executeCleanUp(cl.getWorkingDirectory(), consumer, stderr) == 0) {
159                 exitCode = CommandLineUtils.executeCommandLine(cl, consumer, stderr);
160             }
161         }
162         return exitCode;
163     }
164 
165     public static int executeCleanUp(File workinDirectory, StreamConsumer stdout, StreamConsumer stderr)
166             throws CommandLineException {
167         Commandline cl = new Commandline();
168 
169         cl.setExecutable("svn");
170 
171         cl.setWorkingDirectory(workinDirectory.getAbsolutePath());
172 
173         if (LOGGER.isInfoEnabled()) {
174             LOGGER.info("Executing: " + SvnCommandLineUtils.cryptPassword(cl));
175 
176             if (Os.isFamily(Os.FAMILY_WINDOWS)) {
177                 LOGGER.info("Working directory: " + cl.getWorkingDirectory().getAbsolutePath());
178             }
179         }
180 
181         return CommandLineUtils.executeCommandLine(cl, stdout, stderr);
182     }
183 
184     public static String cryptPassword(Commandline cl) {
185         String clString = cl.toString();
186 
187         final String passwordOpt = "--password";
188         String quoteChar;
189         String escapedQuoteChar;
190         String cryptedPassword;
191 
192         int pos = clString.indexOf(passwordOpt);
193 
194         if (pos > 0) {
195             if (Os.isFamily(Os.FAMILY_WINDOWS)) {
196                 quoteChar = "\"";
197                 escapedQuoteChar = "\"\"";
198                 cryptedPassword = "*****";
199             } else {
200                 quoteChar = "'";
201                 escapedQuoteChar = "'\"'\"'";
202                 cryptedPassword = "'*****'";
203             }
204 
205             // Move pointer after password option
206             pos += passwordOpt.length();
207 
208             // Skip quote after password option
209             if (clString.substring(pos, pos + 1).equals(quoteChar)) {
210                 pos++;
211             }
212 
213             // Skip space after password option
214             pos++;
215 
216             String beforePassword = clString.substring(0, pos);
217             String afterPassword = clString.substring(pos);
218 
219             if (afterPassword.startsWith(quoteChar)) {
220                 pos = 1;
221                 while (afterPassword.indexOf(escapedQuoteChar, pos) != -1) {
222                     pos = afterPassword.indexOf(escapedQuoteChar, pos) + escapedQuoteChar.length();
223                 }
224                 afterPassword = afterPassword.substring(afterPassword.indexOf(quoteChar, pos) + quoteChar.length());
225             } else {
226                 // We assume that the password arg ist not the last one on the arg list
227                 afterPassword = afterPassword.substring(afterPassword.indexOf(' '));
228             }
229 
230             clString = beforePassword + cryptedPassword + afterPassword;
231         }
232 
233         return clString;
234     }
235 }