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