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.report.projectinfo;
20  
21  import javax.net.ssl.HostnameVerifier;
22  import javax.net.ssl.HttpsURLConnection;
23  import javax.net.ssl.SSLContext;
24  import javax.net.ssl.SSLSession;
25  import javax.net.ssl.SSLSocketFactory;
26  import javax.net.ssl.TrustManager;
27  import javax.net.ssl.X509TrustManager;
28  
29  import java.io.IOException;
30  import java.io.InputStream;
31  import java.net.Authenticator;
32  import java.net.PasswordAuthentication;
33  import java.net.URI;
34  import java.net.URL;
35  import java.net.URLConnection;
36  import java.security.KeyManagementException;
37  import java.security.NoSuchAlgorithmException;
38  import java.security.SecureRandom;
39  import java.security.cert.X509Certificate;
40  import java.util.Properties;
41  
42  import org.apache.commons.validator.routines.RegexValidator;
43  import org.apache.commons.validator.routines.UrlValidator;
44  import org.apache.maven.artifact.Artifact;
45  import org.apache.maven.artifact.ArtifactUtils;
46  import org.apache.maven.project.MavenProject;
47  import org.apache.maven.project.ProjectBuilder;
48  import org.apache.maven.project.ProjectBuildingException;
49  import org.apache.maven.project.ProjectBuildingRequest;
50  import org.apache.maven.reporting.AbstractMavenReportRenderer;
51  import org.apache.maven.repository.RepositorySystem;
52  import org.apache.maven.settings.Proxy;
53  import org.apache.maven.settings.Server;
54  import org.apache.maven.settings.Settings;
55  import org.codehaus.plexus.util.Base64;
56  import org.codehaus.plexus.util.IOUtil;
57  import org.codehaus.plexus.util.StringUtils;
58  
59  /**
60   * Utilities methods.
61   *
62   * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
63   * @version $Id$
64   * @since 2.1
65   */
66  public class ProjectInfoReportUtils {
67      private static final UrlValidator URL_VALIDATOR = new UrlValidator(
68              new String[] {"http", "https"}, new RegexValidator("^([" + "\\p{Alnum}\\-\\." + "]*)(:\\d*)?(.*)?"), 0);
69  
70      /** The timeout when getting the url input stream */
71      private static final int TIMEOUT = 1000 * 5;
72  
73      /** The default encoding used to transform bytes to characters */
74      private static final String DEFAULT_ENCODING = "UTF-8";
75  
76      /**
77       * Get the input stream using UTF-8 as character encoding from a URL.
78       *
79       * @param url not null
80       * @param settings not null to handle proxy settings
81       * @return the UTF-8 decoded input stream as string
82       * @throws IOException if any
83       * @see #getContent(URL, Settings, String)
84       */
85      public static String getContent(URL url, Settings settings) throws IOException {
86          return getContent(url, settings, DEFAULT_ENCODING);
87      }
88  
89      /**
90       * Get the input stream from a URL.
91       *
92       * @param url not null
93       * @param settings not null to handle proxy settings
94       * @param encoding the wanted encoding for the URL input stream. If null, UTF-8 will be used.
95       * @return the input stream decoded with the wanted encoding as string
96       * @throws IOException if any
97       */
98      public static String getContent(URL url, Settings settings, String encoding) throws IOException {
99          return getContent(url, null, settings, encoding);
100     }
101 
102     /**
103      * Get the input stream from a URL.
104      *
105      * @param url not null
106      * @param project could be null
107      * @param settings not null to handle proxy settings
108      * @param encoding the wanted encoding for the URL input stream. If null, UTF-8 will be used.
109      * @return the input stream decoded with the wanted encoding as string
110      * @throws IOException if any
111      * @since 2.3
112      */
113     public static String getContent(URL url, MavenProject project, Settings settings, String encoding)
114             throws IOException {
115         String scheme = url.getProtocol();
116 
117         if (encoding == null || encoding.isEmpty()) {
118             encoding = DEFAULT_ENCODING;
119         }
120 
121         if ("file".equals(scheme)) {
122             InputStream in = null;
123             try {
124                 URLConnection conn = url.openConnection();
125                 in = conn.getInputStream();
126 
127                 final String content = IOUtil.toString(in, encoding);
128 
129                 in.close();
130                 in = null;
131 
132                 return content;
133             } finally {
134                 IOUtil.close(in);
135             }
136         }
137 
138         Proxy proxy = settings.getActiveProxy();
139         if (proxy != null) {
140             if ("http".equals(scheme) || "https".equals(scheme) || "ftp".equals(scheme)) {
141                 scheme += ".";
142             } else {
143                 scheme = "";
144             }
145 
146             String host = proxy.getHost();
147             if (!(host == null || host.isEmpty())) {
148                 Properties p = System.getProperties();
149                 p.setProperty(scheme + "proxySet", "true");
150                 p.setProperty(scheme + "proxyHost", host);
151                 p.setProperty(scheme + "proxyPort", String.valueOf(proxy.getPort()));
152                 if (!StringUtils.isEmpty(proxy.getNonProxyHosts())) {
153                     p.setProperty(scheme + "nonProxyHosts", proxy.getNonProxyHosts());
154                 }
155 
156                 final String userName = proxy.getUsername();
157                 if (!(userName == null || userName.isEmpty())) {
158                     final String pwd = StringUtils.isEmpty(proxy.getPassword()) ? "" : proxy.getPassword();
159                     Authenticator.setDefault(new Authenticator() {
160                         /** {@inheritDoc} */
161                         @Override
162                         protected PasswordAuthentication getPasswordAuthentication() {
163                             return new PasswordAuthentication(userName, pwd.toCharArray());
164                         }
165                     });
166                 }
167             }
168         }
169 
170         InputStream in = null;
171         try {
172             URLConnection conn = getURLConnection(url, project, settings);
173             in = conn.getInputStream();
174 
175             final String string = IOUtil.toString(in, encoding);
176 
177             in.close();
178             in = null;
179 
180             return string;
181         } finally {
182             IOUtil.close(in);
183         }
184     }
185 
186     /**
187      * @param repositorySystem not null
188      * @param artifact not null
189      * @param projectBuilder not null
190      * @param buildingRequest not null
191      * @return the artifact url or null if an error occurred.
192      */
193     public static String getArtifactUrl(
194             RepositorySystem repositorySystem,
195             Artifact artifact,
196             ProjectBuilder projectBuilder,
197             ProjectBuildingRequest buildingRequest) {
198         if (Artifact.SCOPE_SYSTEM.equals(artifact.getScope())) {
199             return null;
200         }
201 
202         Artifact copyArtifact = ArtifactUtils.copyArtifact(artifact);
203         if (!"pom".equals(copyArtifact.getType())) {
204             copyArtifact = repositorySystem.createProjectArtifact(
205                     copyArtifact.getGroupId(), copyArtifact.getArtifactId(), copyArtifact.getVersion());
206         }
207         try {
208             MavenProject pluginProject =
209                     projectBuilder.build(copyArtifact, buildingRequest).getProject();
210 
211             if (isArtifactUrlValid(pluginProject.getUrl())) {
212                 return pluginProject.getUrl();
213             }
214 
215             return null;
216         } catch (ProjectBuildingException e) {
217             return null;
218         }
219     }
220 
221     /**
222      * @param artifactId not null
223      * @param link could be null
224      * @return the artifactId cell with or without a link pattern
225      * @see AbstractMavenReportRenderer#linkPatternedText(String)
226      */
227     public static String getArtifactIdCell(String artifactId, String link) {
228         if (link == null || link.isEmpty()) {
229             return artifactId;
230         }
231 
232         return "{" + artifactId + "," + link + "}";
233     }
234 
235     /**
236      * @param url not null
237      * @return <code>true</code> if the url is valid, <code>false</code> otherwise.
238      */
239     public static boolean isArtifactUrlValid(String url) {
240         if (url == null || url.isEmpty()) {
241             return false;
242         }
243 
244         return URL_VALIDATOR.isValid(url);
245     }
246 
247     /**
248      * Convenience method to return the name of a web-based mailing list archive server.
249      * For instance, if the archive URI is <code>http://www.mail-archive.com/dev@maven.apache.org</code>, this
250      * method returns <code>www.mail-archive.com</code>
251      *
252      * @param uri the URI parse
253      * @return the server host of a web-based mailing list archive server
254      */
255     public static String getArchiveServer(String uri) {
256         if (uri == null) {
257             return "???UNKNOWN???";
258         }
259         return URI.create(uri).getHost();
260     }
261 
262     /**
263      * @param url not null
264      * @param project not null
265      * @param settings not null
266      * @return the url connection with auth if required. Don't check the certificate if SSL scheme.
267      * @throws IOException if any
268      */
269     private static URLConnection getURLConnection(URL url, MavenProject project, Settings settings) throws IOException {
270         URLConnection conn = url.openConnection();
271         conn.setConnectTimeout(TIMEOUT);
272         conn.setReadTimeout(TIMEOUT);
273 
274         // conn authorization
275         // @formatter:off
276         if (settings.getServers() != null
277                 && !settings.getServers().isEmpty()
278                 && project != null
279                 && project.getDistributionManagement() != null
280                 && (project.getDistributionManagement().getRepository() != null
281                         || project.getDistributionManagement().getSnapshotRepository() != null)
282                 && (StringUtils.isNotEmpty(project.getDistributionManagement()
283                                 .getRepository()
284                                 .getUrl())
285                         || StringUtils.isNotEmpty(project.getDistributionManagement()
286                                 .getSnapshotRepository()
287                                 .getUrl())))
288         // @formatter:on
289         {
290             Server server = null;
291             if (url.toString()
292                     .contains(
293                             project.getDistributionManagement().getRepository().getUrl())) {
294                 server = settings.getServer(
295                         project.getDistributionManagement().getRepository().getId());
296             }
297             if (server == null
298                     && url.toString()
299                             .contains(project.getDistributionManagement()
300                                     .getSnapshotRepository()
301                                     .getUrl())) {
302                 server = settings.getServer(project.getDistributionManagement()
303                         .getSnapshotRepository()
304                         .getId());
305             }
306 
307             if (server != null
308                     && StringUtils.isNotEmpty(server.getUsername())
309                     && StringUtils.isNotEmpty(server.getPassword())) {
310                 String up =
311                         server.getUsername().trim() + ":" + server.getPassword().trim();
312                 String upEncoded = new String(Base64.encodeBase64Chunked(up.getBytes())).trim();
313 
314                 conn.setRequestProperty("Authorization", "Basic " + upEncoded);
315             }
316         }
317 
318         if (conn instanceof HttpsURLConnection) {
319             HostnameVerifier hostnameverifier = new HostnameVerifier() {
320                 /** {@inheritDoc} */
321                 public boolean verify(String urlHostName, SSLSession session) {
322                     return true;
323                 }
324             };
325             ((HttpsURLConnection) conn).setHostnameVerifier(hostnameverifier);
326 
327             TrustManager[] trustAllCerts = new TrustManager[] {
328                 new X509TrustManager() {
329                     /** {@inheritDoc} */
330                     public void checkClientTrusted(final X509Certificate[] chain, final String authType) {}
331 
332                     /** {@inheritDoc} */
333                     public void checkServerTrusted(final X509Certificate[] chain, final String authType) {}
334 
335                     /** {@inheritDoc} */
336                     public X509Certificate[] getAcceptedIssuers() {
337                         return null;
338                     }
339                 }
340             };
341 
342             try {
343                 SSLContext sslContext = SSLContext.getInstance("SSL");
344                 sslContext.init(null, trustAllCerts, new SecureRandom());
345 
346                 SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
347 
348                 ((HttpsURLConnection) conn).setSSLSocketFactory(sslSocketFactory);
349             } catch (NoSuchAlgorithmException | KeyManagementException e1) {
350                 // ignore
351             }
352         }
353 
354         return conn;
355     }
356 }