View Javadoc
1   package org.apache.maven.plugins.site.deploy.wagon;
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 org.apache.maven.wagon.WagonConstants;
23  
24  import java.io.File;
25  import java.util.StringTokenizer;
26  
27  /**
28   * Various path (URL) manipulation routines.
29   *
30   * <strong>Note: </strong> This is a copy of a file from Wagon. It was copied here to be able to work around WAGON-307.
31   * This class can be removed when the prerequisite Maven version uses wagon-provider-api:1.0-beta-7,
32   * ie Maven &gt;= 3.0.1.
33   *
34   * @author <a href="michal.maczka@dimatics.com">Michal Maczka</a>
35   * @version $Id: PathUtils.html 979873 2016-02-08 19:06:46Z michaelo $
36   */
37  public final class PathUtils
38  {
39      private PathUtils()
40      {
41          // no op
42      }
43      
44      /**
45       * Returns the directory path portion of a file specification string.
46       * Matches the equally named unix command.
47       *
48       * @return The directory portion excluding the ending file separator.
49       */
50      public static String dirname( final String path )
51      {
52          final int i = path.lastIndexOf( '/' );
53  
54          return ( ( i >= 0 ) ? path.substring( 0, i ) : "" );
55      }
56  
57      /**
58       * Returns the filename portion of a file specification string.
59       *
60       * @return The filename string with extension.
61       */
62      public static String filename( final String path )
63      {
64          final int i = path.lastIndexOf( '/' );
65          return ( ( i >= 0 ) ? path.substring( i + 1 ) : path );
66      }
67  
68      public static String[] dirnames( final String path )
69      {
70          final String dirname = PathUtils.dirname( path );
71          return split( dirname, "/", -1 );
72  
73      }
74  
75      private static String[] split( final String str, final String separator, final int max )
76      {
77          final StringTokenizer tok;
78  
79          if ( separator == null )
80          {
81              // Null separator means we're using StringTokenizer's default
82              // delimiter, which comprises all whitespace characters.
83              tok = new StringTokenizer( str );
84          }
85          else
86          {
87              tok = new StringTokenizer( str, separator );
88          }
89  
90          int listSize = tok.countTokens();
91  
92          if ( max > 0 && listSize > max )
93          {
94              listSize = max;
95          }
96  
97          final String[] list = new String[listSize];
98  
99          int i = 0;
100 
101         int lastTokenBegin;
102         int lastTokenEnd = 0;
103 
104         while ( tok.hasMoreTokens() )
105         {
106             if ( max > 0 && i == listSize - 1 )
107             {
108                 // In the situation where we hit the max yet have
109                 // tokens left over in our input, the last list
110                 // element gets all remaining text.
111                 final String endToken = tok.nextToken();
112 
113                 lastTokenBegin = str.indexOf( endToken, lastTokenEnd );
114 
115                 list[i] = str.substring( lastTokenBegin );
116 
117                 break;
118 
119             }
120             else
121             {
122                 list[i] = tok.nextToken();
123 
124                 lastTokenBegin = str.indexOf( list[i], lastTokenEnd );
125 
126                 lastTokenEnd = lastTokenBegin + list[i].length();
127             }
128 
129             i++;
130         }
131         return list;
132     }
133 
134     /**
135      * Return the host name (Removes protocol and path from the URL) E.g: for input
136      * <code>http://www.codehause.org</code> this method will return <code>www.apache.org</code>
137      *
138      * @param url the url
139      * @return the host name
140      */
141     public static String host( final String url )
142     {
143         String authorization = authorization( url );
144         int index = authorization.indexOf( '@' );
145         if ( index >= 0 )
146         {
147             return authorization.substring( index + 1 );
148         }
149         else
150         {
151             return authorization;
152         }
153     }
154 
155     /**
156      * This was changed from private to package local so that it can be unit tested.
157      */
158     static String authorization( final String url )
159     {
160         if ( url == null )
161         {
162             return "localhost";
163         }
164 
165         final String protocol = PathUtils.protocol( url );
166 
167         if ( protocol == null || protocol.equalsIgnoreCase( "file" ) )
168         {
169             return "localhost";
170         }
171 
172         String host = url;
173         if ( protocol.equalsIgnoreCase( "scm" ) )
174         {
175             // skip over type
176             host = host.substring( host.indexOf( ':', 4 ) + 1 ).trim();
177         }
178 
179         // skip over protocol
180         host = host.substring( host.indexOf( ':' ) + 1 ).trim();
181         if ( host.startsWith( "//" ) )
182         {
183             host = host.substring( 2 );
184         }
185 
186         int pos = host.indexOf( '/' );
187 
188         if ( pos > 0 )
189         {
190             host = host.substring( 0, pos );
191         }
192 
193         pos = host.indexOf( '@' );
194 
195         if ( pos > 0 )
196         {
197             pos = host.indexOf( ':', pos );
198         }
199         else
200         {
201             pos = host.indexOf( ':' );
202         }
203 
204         if ( pos > 0 )
205         {
206             host = host.substring( 0, pos );
207         }
208         return host;
209     }
210 
211     /**
212      * /**
213      * Return the protocol name.
214      * <br/>
215      * E.g: for input
216      * <code>http://www.codehause.org</code> this method will return <code>http</code>
217      *
218      * @param url the url
219      * @return the host name
220      */
221     public static String protocol( final String url )
222     {
223         final int pos = url.indexOf( ':' );
224 
225         if ( pos == -1 )
226         {
227             return "";
228         }
229         return url.substring( 0, pos ).trim();
230     }
231 
232     /**
233      * @param url
234      * @return the port or {@link WagonConstants#UNKNOWN_PORT} if not existent
235      */
236     public static int port( String url )
237     {
238 
239         final String protocol = PathUtils.protocol( url );
240 
241         if ( protocol == null || protocol.equalsIgnoreCase( "file" ) )
242         {
243             return WagonConstants.UNKNOWN_PORT;
244         }
245 
246         final String authorization = PathUtils.authorization( url );
247 
248         if ( authorization == null )
249         {
250             return WagonConstants.UNKNOWN_PORT;
251         }
252 
253         if ( protocol.equalsIgnoreCase( "scm" ) )
254         {
255             // skip over type
256             url = url.substring( url.indexOf( ':', 4 ) + 1 ).trim();
257         }
258 
259         if ( url.regionMatches( true, 0, "file:", 0, 5 ) || url.regionMatches( true, 0, "local:", 0, 6 ) )
260         {
261             return WagonConstants.UNKNOWN_PORT;
262         }
263 
264         // skip over protocol
265         url = url.substring( url.indexOf( ':' ) + 1 ).trim();
266         if ( url.startsWith( "//" ) )
267         {
268             url = url.substring( 2 );
269         }
270 
271         int start = authorization.length();
272 
273         if ( url.length() > start && url.charAt( start ) == ':' )
274         {
275             int end = url.indexOf( '/', start );
276 
277             if ( end == start + 1 )
278             {
279                 // it is :/
280                 return WagonConstants.UNKNOWN_PORT;
281             }
282 
283             if ( end == -1 )
284             {
285                 end = url.length();
286             }
287 
288             return Integer.parseInt( url.substring( start + 1, end ) );
289         }
290         else
291         {
292             return WagonConstants.UNKNOWN_PORT;
293         }
294 
295     }
296 
297     /**
298      * Derive the path portion of the given URL.
299      * 
300      * @param url the repository URL
301      * @return the basedir of the repository
302      * @todo need to URL decode for spaces?
303      */
304     public static String basedir( String url )
305     {
306         String protocol = PathUtils.protocol( url );
307 
308         String retValue = null;
309 
310         if ( protocol.equalsIgnoreCase( "scm" ) )
311         {
312             // skip over SCM bits
313             if ( url.regionMatches( true, 0, "scm:svn:", 0, 8 ) )
314             {
315                 url = url.substring( url.indexOf( ':', 4 ) + 1 );
316                 protocol = PathUtils.protocol( url );
317             }
318         }
319 
320         if ( protocol.equalsIgnoreCase( "file" ) )
321         {
322             retValue = url.substring( protocol.length() + 1 );
323             retValue = decode( retValue );
324             // special case: if omitted // on protocol, keep path as is
325             if ( retValue.startsWith( "//" ) )
326             {
327                 retValue = retValue.substring( 2 );
328 
329                 if ( retValue.length() >= 2 && ( retValue.charAt( 1 ) == '|' || retValue.charAt( 1 ) == ':' ) )
330                 {
331                     // special case: if there is a windows drive letter, then keep the original return value
332                     retValue = retValue.charAt( 0 ) + ":" + retValue.substring( 2 );
333                 }
334                 else
335                 {
336                     // Now we expect the host
337                     int index = retValue.indexOf( '/' );
338                     if ( index >= 0 )
339                     {
340                         retValue = retValue.substring( index + 1 );
341                     }
342 
343                     // special case: if there is a windows drive letter, then keep the original return value
344                     if ( retValue.length() >= 2 && ( retValue.charAt( 1 ) == '|' || retValue.charAt( 1 ) == ':' ) )
345                     {
346                         retValue = retValue.charAt( 0 ) + ":" + retValue.substring( 2 );
347                     }
348                     else if ( index >= 0 )
349                     {
350                         // leading / was previously stripped
351                         retValue = "/" + retValue;
352                     }
353                 }
354             }
355 
356             // special case: if there is a windows drive letter using |, switch to :
357             if ( retValue.length() >= 2 && retValue.charAt( 1 ) == '|' )
358             {
359                 retValue = retValue.charAt( 0 ) + ":" + retValue.substring( 2 );
360             }
361         }
362         else
363         {
364             final String authorization = PathUtils.authorization( url );
365 
366             final int port = PathUtils.port( url );
367 
368             int pos = 0;
369 
370             if ( protocol.equalsIgnoreCase( "scm" ) )
371             {
372                 pos = url.indexOf( ':', 4 ) + 1;
373                 pos = url.indexOf( ':', pos ) + 1;
374             }
375             else
376             {
377                 int index = url.indexOf( "://" );
378                 if ( index != -1 )
379                 {
380                     pos = index + 3;
381                 }
382             }
383 
384             pos += authorization.length();
385 
386             if ( port != WagonConstants.UNKNOWN_PORT )
387             {
388                 pos = pos + Integer.toString( port ).length() + 1;
389             }
390 
391             if ( url.length() > pos )
392             {
393                 retValue = url.substring( pos );
394                 if ( retValue.startsWith( ":" ) )
395                 {
396                     // this is for :/ after the host
397                     retValue = retValue.substring( 1 );
398                 }
399 
400                 // one module may be allowed in the path in CVS
401                 retValue = retValue.replace( ':', '/' );
402             }
403         }
404 
405         if ( retValue == null )
406         {
407             retValue = "/";
408         }
409         return retValue.trim();
410     }
411 
412     /**
413      * Decodes the specified (portion of a) URL. <strong>Note:</strong> This decoder assumes that ISO-8859-1 is used to
414      * convert URL-encoded bytes to characters.
415      * 
416      * @param url The URL to decode, may be <code>null</code>.
417      * @return The decoded URL or <code>null</code> if the input was <code>null</code>.
418      */
419     private static String decode( String url )
420     {
421         String decoded = url;
422         if ( url != null )
423         {
424             int pos = -1;
425             while ( ( pos = decoded.indexOf( '%', pos + 1 ) ) >= 0 )
426             {
427                 if ( pos + 2 < decoded.length() )
428                 {
429                     String hexStr = decoded.substring( pos + 1, pos + 3 );
430                     char ch = (char) Integer.parseInt( hexStr, 16 );
431                     decoded = decoded.substring( 0, pos ) + ch + decoded.substring( pos + 3 );
432                 }
433             }
434         }
435         return decoded;
436     }
437 
438     public static String user( String url )
439     {
440         String host = authorization( url );
441         int index = host.indexOf( '@' );
442         if ( index > 0 )
443         {
444             String userInfo = host.substring( 0, index );
445             index = userInfo.indexOf( ':' );
446             if ( index > 0 )
447             {
448                 return userInfo.substring( 0, index );
449             }
450             else if ( index < 0 )
451             {
452                 return userInfo;
453             }
454         }
455         return null;
456     }
457 
458     public static String password( String url )
459     {
460         String host = authorization( url );
461         int index = host.indexOf( '@' );
462         if ( index > 0 )
463         {
464             String userInfo = host.substring( 0, index );
465             index = userInfo.indexOf( ':' );
466             if ( index >= 0 )
467             {
468                 return userInfo.substring( index + 1 );
469             }
470         }
471         return null;
472     }
473 
474     // TODO: move to plexus-utils or use something appropriate from there
475     public static String toRelative( File basedir, String absolutePath )
476     {
477         String relative;
478 
479         absolutePath = absolutePath.replace( '\\', '/' );
480         String basedirPath = basedir.getAbsolutePath().replace( '\\', '/' );
481 
482         if ( absolutePath.startsWith( basedirPath ) )
483         {
484             relative = absolutePath.substring( basedirPath.length() );
485             if ( relative.startsWith( "/" ) )
486             {
487                 relative = relative.substring( 1 );
488             }
489             if ( relative.length() <= 0 )
490             {
491                 relative = ".";
492             }
493         }
494         else
495         {
496             relative = absolutePath;
497         }
498 
499         return relative;
500     }
501 }