001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.maven.scm.provider.svn.svnexe.command; 020 021import java.io.File; 022import java.io.FileOutputStream; 023import java.io.IOException; 024import java.io.PrintStream; 025import java.util.List; 026 027import org.apache.commons.lang3.StringUtils; 028import org.apache.maven.scm.provider.svn.repository.SvnScmProviderRepository; 029import org.apache.maven.scm.provider.svn.util.SvnUtil; 030import org.codehaus.plexus.util.Os; 031import org.codehaus.plexus.util.cli.CommandLineException; 032import org.codehaus.plexus.util.cli.CommandLineUtils; 033import org.codehaus.plexus.util.cli.Commandline; 034import org.codehaus.plexus.util.cli.StreamConsumer; 035import org.slf4j.Logger; 036import org.slf4j.LoggerFactory; 037 038/** 039 * Command line construction utility. 040 * 041 * @author Brett Porter 042 * @author Olivier Lamy 043 */ 044public final class SvnCommandLineUtils { 045 private static final Logger LOGGER = LoggerFactory.getLogger(SvnCommandLineUtils.class); 046 047 private SvnCommandLineUtils() {} 048 049 public static void addTarget(Commandline cl, List<File> files) throws IOException { 050 if (files == null || files.isEmpty()) { 051 return; 052 } 053 054 StringBuilder sb = new StringBuilder(); 055 String ls = System.getProperty("line.separator"); 056 for (File f : files) { 057 sb.append(f.getPath().replace('\\', '/')); 058 sb.append(ls); 059 } 060 061 File targets = File.createTempFile("maven-scm-", "-targets"); 062 try (PrintStream out = new PrintStream(new FileOutputStream(targets))) { 063 out.print(sb); 064 } 065 066 cl.createArg().setValue("--targets"); 067 cl.createArg().setValue(targets.getAbsolutePath()); 068 069 targets.deleteOnExit(); 070 } 071 072 public static Commandline getBaseSvnCommandLine( 073 File workingDirectory, SvnScmProviderRepository repository, boolean interactive) { 074 Commandline cl = new Commandline(); 075 076 cl.setExecutable("svn"); 077 try { 078 cl.addSystemEnvironment(); 079 cl.addEnvironment("LC_MESSAGES", "C"); 080 } catch (Exception e) { 081 // Do nothing 082 } 083 084 if (workingDirectory != null) { 085 cl.setWorkingDirectory(workingDirectory.getAbsolutePath()); 086 } 087 088 if (!StringUtils.isEmpty(System.getProperty("maven.scm.svn.config_directory"))) { 089 cl.createArg().setValue("--config-dir"); 090 cl.createArg().setValue(System.getProperty("maven.scm.svn.config_directory")); 091 } else if (!StringUtils.isEmpty(SvnUtil.getSettings().getConfigDirectory())) { 092 cl.createArg().setValue("--config-dir"); 093 cl.createArg().setValue(SvnUtil.getSettings().getConfigDirectory()); 094 } 095 096 boolean hasAuthInfo = false; 097 if (repository != null && !StringUtils.isEmpty(repository.getUser())) { 098 hasAuthInfo = true; 099 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}