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