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 * 044 */ 045public final class SvnCommandLineUtils { 046 private static final Logger LOGGER = LoggerFactory.getLogger(SvnCommandLineUtils.class); 047 048 private SvnCommandLineUtils() {} 049 050 public static void addTarget(Commandline cl, List<File> files) throws IOException { 051 if (files == null || files.isEmpty()) { 052 return; 053 } 054 055 StringBuilder sb = new StringBuilder(); 056 String ls = System.getProperty("line.separator"); 057 for (File f : files) { 058 sb.append(f.getPath().replace('\\', '/')); 059 sb.append(ls); 060 } 061 062 File targets = File.createTempFile("maven-scm-", "-targets"); 063 PrintStream out = new PrintStream(new FileOutputStream(targets)); 064 out.print(sb); 065 out.flush(); 066 out.close(); 067 068 cl.createArg().setValue("--targets"); 069 cl.createArg().setValue(targets.getAbsolutePath()); 070 071 targets.deleteOnExit(); 072 } 073 074 public static Commandline getBaseSvnCommandLine(File workingDirectory, SvnScmProviderRepository repository) { 075 Commandline cl = new Commandline(); 076 077 cl.setExecutable("svn"); 078 try { 079 cl.addSystemEnvironment(); 080 cl.addEnvironment("LC_MESSAGES", "C"); 081 } catch (Exception e) { 082 // Do nothing 083 } 084 085 if (workingDirectory != null) { 086 cl.setWorkingDirectory(workingDirectory.getAbsolutePath()); 087 } 088 089 if (!StringUtils.isEmpty(System.getProperty("maven.scm.svn.config_directory"))) { 090 cl.createArg().setValue("--config-dir"); 091 cl.createArg().setValue(System.getProperty("maven.scm.svn.config_directory")); 092 } else if (!StringUtils.isEmpty(SvnUtil.getSettings().getConfigDirectory())) { 093 cl.createArg().setValue("--config-dir"); 094 cl.createArg().setValue(SvnUtil.getSettings().getConfigDirectory()); 095 } 096 097 boolean hasAuthInfo = false; 098 if (repository != null && !StringUtils.isEmpty(repository.getUser())) { 099 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}