View Javadoc
1   package org.apache.maven.scm.provider.svn.svnexe.command;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   * http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.io.FileOutputStream;
24  import java.io.IOException;
25  import java.io.PrintStream;
26  import java.util.List;
27  
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.StringUtils;
32  import org.codehaus.plexus.util.cli.CommandLineException;
33  import org.codehaus.plexus.util.cli.CommandLineUtils;
34  import org.codehaus.plexus.util.cli.Commandline;
35  import org.codehaus.plexus.util.cli.StreamConsumer;
36  import org.slf4j.Logger;
37  import org.slf4j.LoggerFactory;
38  
39  /**
40   * Command line construction utility.
41   *
42   * @author Brett Porter
43   * @author Olivier Lamy
44   *
45   */
46  public final class SvnCommandLineUtils
47  {
48      private static final Logger LOGGER = LoggerFactory.getLogger( SvnCommandLineUtils.class );
49  
50      private SvnCommandLineUtils()
51      {
52      }
53  
54      public static void addTarget( Commandline cl, List<File> files )
55          throws IOException
56      {
57          if ( files == null || files.isEmpty() )
58          {
59              return;
60          }
61  
62          StringBuilder sb = new StringBuilder();
63          String ls = System.getProperty( "line.separator" );
64          for ( File f : files )
65          {
66              sb.append( f.getPath().replace( '\\', '/' ) );
67              sb.append( ls );
68          }
69  
70          File targets = File.createTempFile( "maven-scm-", "-targets" );
71          PrintStream out = new PrintStream( new FileOutputStream( targets ) );
72          out.print( sb );
73          out.flush();
74          out.close();
75  
76          cl.createArg().setValue( "--targets" );
77          cl.createArg().setValue( targets.getAbsolutePath() );
78  
79          targets.deleteOnExit();
80      }
81  
82      public static Commandline getBaseSvnCommandLine( File workingDirectory, SvnScmProviderRepository repository )
83      {
84          Commandline cl = new Commandline();
85  
86          cl.setExecutable( "svn" );
87          try
88          {
89              cl.addSystemEnvironment();
90              cl.addEnvironment( "LC_MESSAGES", "C" );
91          }
92          catch ( Exception e )
93          {
94              //Do nothing
95          }
96  
97          if ( workingDirectory != null )
98          {
99              cl.setWorkingDirectory( workingDirectory.getAbsolutePath() );
100         }
101 
102         if ( !StringUtils.isEmpty( System.getProperty( "maven.scm.svn.config_directory" ) ) )
103         {
104             cl.createArg().setValue( "--config-dir" );
105             cl.createArg().setValue( System.getProperty( "maven.scm.svn.config_directory" ) );
106         }
107         else if ( !StringUtils.isEmpty( SvnUtil.getSettings().getConfigDirectory() ) )
108         {
109             cl.createArg().setValue( "--config-dir" );
110             cl.createArg().setValue( SvnUtil.getSettings().getConfigDirectory() );
111         }
112 
113         boolean hasAuthInfo = false;
114         if ( repository != null && !StringUtils.isEmpty( repository.getUser() ) )
115         {
116             hasAuthInfo = true;
117             cl.createArg().setValue( "--username" );
118             cl.createArg().setValue( repository.getUser() );
119         }
120 
121         if ( repository != null && !StringUtils.isEmpty( repository.getPassword() ) )
122         {
123             hasAuthInfo = true;
124             cl.createArg().setValue( "--password" );
125             cl.createArg().setValue( repository.getPassword() );
126         }
127 
128         // [by Lenik] don't overwrite existing auth cache by default.
129         if ( hasAuthInfo && !SvnUtil.getSettings().isUseAuthCache() )
130         {
131             cl.createArg().setValue( "--no-auth-cache" );
132         }
133 
134         if ( SvnUtil.getSettings().isUseNonInteractive() )
135         {
136             cl.createArg().setValue( "--non-interactive" );
137         }
138 
139         if ( SvnUtil.getSettings().isTrustServerCert() )
140         {
141             cl.createArg().setValue( "--trust-server-cert" );
142         }
143 
144         return cl;
145     }
146 
147     public static int execute( Commandline cl, StreamConsumer consumer, CommandLineUtils.StringStreamConsumer stderr )
148         throws CommandLineException
149     {
150         // SCM-482: force English resource bundle
151         cl.addEnvironment( "LC_MESSAGES", "en" );
152 
153         int exitCode = CommandLineUtils.executeCommandLine( cl, consumer, stderr );
154 
155         exitCode = checkIfCleanUpIsNeeded( exitCode, cl, consumer, stderr );
156 
157         return exitCode;
158     }
159 
160     public static int execute( Commandline cl, CommandLineUtils.StringStreamConsumer stdout,
161                                CommandLineUtils.StringStreamConsumer stderr )
162         throws CommandLineException
163     {
164         int exitCode = CommandLineUtils.executeCommandLine( cl, stdout, stderr );
165 
166         exitCode = checkIfCleanUpIsNeeded( exitCode, cl, stdout, stderr );
167 
168         return exitCode;
169     }
170 
171     private static int checkIfCleanUpIsNeeded( int exitCode, Commandline cl, StreamConsumer consumer,
172                                                CommandLineUtils.StringStreamConsumer stderr )
173         throws CommandLineException
174     {
175         if ( exitCode != 0 && stderr.getOutput() != null && stderr.getOutput().indexOf( "'svn cleanup'" ) > 0
176             && stderr.getOutput().indexOf( "'svn help cleanup'" ) > 0 )
177         {
178             if ( LOGGER.isInfoEnabled() )
179             {
180                 LOGGER.info( "Svn command failed due to some locks in working copy. We try to run a 'svn cleanup'." );
181             }
182 
183             if ( executeCleanUp( cl.getWorkingDirectory(), consumer, stderr ) == 0 )
184             {
185                 exitCode = CommandLineUtils.executeCommandLine( cl, consumer, stderr );
186             }
187         }
188         return exitCode;
189     }
190 
191     public static int executeCleanUp( File workinDirectory, StreamConsumer stdout, StreamConsumer stderr )
192         throws CommandLineException
193     {
194         Commandline cl = new Commandline();
195 
196         cl.setExecutable( "svn" );
197 
198         cl.setWorkingDirectory( workinDirectory.getAbsolutePath() );
199 
200         if ( LOGGER.isInfoEnabled() )
201         {
202             LOGGER.info( "Executing: " + SvnCommandLineUtils.cryptPassword( cl ) );
203 
204             if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
205             {
206                 LOGGER.info( "Working directory: " + cl.getWorkingDirectory().getAbsolutePath() );
207             }
208         }
209 
210         return CommandLineUtils.executeCommandLine( cl, stdout, stderr );
211     }
212 
213     public static String cryptPassword( Commandline cl )
214     {
215         String clString = cl.toString();
216 
217         final String passwordOpt = "--password";
218         String quoteChar;
219         String escapedQuoteChar;
220         String cryptedPassword;
221 
222         int pos = clString.indexOf( passwordOpt );
223 
224         if ( pos > 0 )
225         {
226            if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
227            {
228                 quoteChar = "\"";
229                 escapedQuoteChar = "\"\"";
230                 cryptedPassword = "*****";
231            }
232            else
233            {
234                quoteChar = "'";
235                escapedQuoteChar = "'\"'\"'";
236                cryptedPassword = "'*****'";
237            }
238 
239            // Move pointer after password option
240            pos += passwordOpt.length();
241 
242            // Skip quote after password option
243            if ( clString.substring( pos,  pos + 1 ).equals( quoteChar ) )
244            {
245                pos++;
246            }
247 
248            // Skip space after password option
249            pos++;
250 
251             String beforePassword = clString.substring( 0, pos );
252             String afterPassword = clString.substring( pos );
253 
254             if ( afterPassword.startsWith( quoteChar ) )
255             {
256                 pos = 1;
257                 while ( afterPassword.indexOf( escapedQuoteChar, pos ) != -1 )
258                 {
259                     pos = afterPassword.indexOf( escapedQuoteChar, pos ) + escapedQuoteChar.length();
260                 }
261                 afterPassword = afterPassword.substring ( afterPassword.indexOf( quoteChar, pos )
262                                                           + quoteChar.length() );
263             }
264             else
265             {
266                 // We assume that the password arg ist not the last one on the arg list
267                 afterPassword = afterPassword.substring( afterPassword.indexOf( ' ' ) );
268             }
269 
270             clString = beforePassword + cryptedPassword + afterPassword;
271 
272         }
273 
274         return clString;
275     }
276 }