View Javadoc
1   /*
2    * This file is part of dependency-check-core.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   *
16   * Copyright (c) 2012 Jeremy Long. All Rights Reserved.
17   */
18  package org.owasp.dependencycheck.utils;
19  
20  import java.io.BufferedOutputStream;
21  import java.io.File;
22  import java.io.FileOutputStream;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.net.HttpURLConnection;
26  import java.net.URISyntaxException;
27  import java.net.URL;
28  import java.util.logging.Level;
29  import java.util.logging.Logger;
30  import java.util.zip.GZIPInputStream;
31  import java.util.zip.InflaterInputStream;
32  
33  /**
34   * A utility to download files from the Internet.
35   *
36   * @author Jeremy Long <jeremy.long@owasp.org>
37   */
38  public final class Downloader {
39  
40      /**
41       * The logger.
42       */
43      private static final Logger LOGGER = Logger.getLogger(Downloader.class.getName());
44  
45      /**
46       * Private constructor for utility class.
47       */
48      private Downloader() {
49      }
50  
51      /**
52       * Retrieves a file from a given URL and saves it to the outputPath.
53       *
54       * @param url the URL of the file to download
55       * @param outputPath the path to the save the file to
56       * @throws DownloadFailedException is thrown if there is an error downloading the file
57       */
58      public static void fetchFile(URL url, File outputPath) throws DownloadFailedException {
59          fetchFile(url, outputPath, true);
60      }
61  
62      /**
63       * Retrieves a file from a given URL and saves it to the outputPath.
64       *
65       * @param url the URL of the file to download
66       * @param outputPath the path to the save the file to
67       * @param useProxy whether to use the configured proxy when downloading files
68       * @throws DownloadFailedException is thrown if there is an error downloading the file
69       */
70      public static void fetchFile(URL url, File outputPath, boolean useProxy) throws DownloadFailedException {
71          if ("file".equalsIgnoreCase(url.getProtocol())) {
72              File file;
73              try {
74                  file = new File(url.toURI());
75              } catch (URISyntaxException ex) {
76                  final String msg = String.format("Download failed, unable to locate '%s'", url.toString());
77                  throw new DownloadFailedException(msg);
78              }
79              if (file.exists()) {
80                  try {
81                      org.apache.commons.io.FileUtils.copyFile(file, outputPath);
82                  } catch (IOException ex) {
83                      final String msg = String.format("Download failed, unable to copy '%s'", url.toString());
84                      throw new DownloadFailedException(msg);
85                  }
86              } else {
87                  final String msg = String.format("Download failed, file does not exist '%s'", url.toString());
88                  throw new DownloadFailedException(msg);
89              }
90          } else {
91              HttpURLConnection conn = null;
92              try {
93                  conn = URLConnectionFactory.createHttpURLConnection(url, useProxy);
94                  conn.setRequestProperty("Accept-Encoding", "gzip, deflate");
95                  conn.connect();
96              } catch (IOException ex) {
97                  try {
98                      if (conn != null) {
99                          conn.disconnect();
100                     }
101                 } finally {
102                     conn = null;
103                 }
104                 throw new DownloadFailedException("Error downloading file.", ex);
105             }
106             final String encoding = conn.getContentEncoding();
107 
108             BufferedOutputStream writer = null;
109             InputStream reader = null;
110             try {
111                 if (encoding != null && "gzip".equalsIgnoreCase(encoding)) {
112                     reader = new GZIPInputStream(conn.getInputStream());
113                 } else if (encoding != null && "deflate".equalsIgnoreCase(encoding)) {
114                     reader = new InflaterInputStream(conn.getInputStream());
115                 } else {
116                     reader = conn.getInputStream();
117                 }
118 
119                 writer = new BufferedOutputStream(new FileOutputStream(outputPath));
120                 final byte[] buffer = new byte[4096];
121                 int bytesRead;
122                 while ((bytesRead = reader.read(buffer)) > 0) {
123                     writer.write(buffer, 0, bytesRead);
124                 }
125             } catch (Throwable ex) {
126                 throw new DownloadFailedException("Error saving downloaded file.", ex);
127             } finally {
128                 if (writer != null) {
129                     try {
130                         writer.close();
131                     } catch (Throwable ex) {
132                         LOGGER.log(Level.FINEST,
133                                 "Error closing the writer in Downloader.", ex);
134                     }
135                 }
136                 if (reader != null) {
137                     try {
138                         reader.close();
139                     } catch (Throwable ex) {
140                         LOGGER.log(Level.FINEST,
141                                 "Error closing the reader in Downloader.", ex);
142                     }
143                 }
144                 try {
145                     conn.disconnect();
146                 } finally {
147                     conn = null;
148                 }
149             }
150         }
151     }
152 
153     /**
154      * Makes an HTTP Head request to retrieve the last modified date of the given URL. If the file:// protocol is
155      * specified, then the lastTimestamp of the file is returned.
156      *
157      * @param url the URL to retrieve the timestamp from
158      * @return an epoch timestamp
159      * @throws DownloadFailedException is thrown if an exception occurs making the HTTP request
160      */
161     public static long getLastModified(URL url) throws DownloadFailedException {
162         long timestamp = 0;
163         //TODO add the FTP protocol?
164         if ("file".equalsIgnoreCase(url.getProtocol())) {
165             File lastModifiedFile;
166             try {
167                 lastModifiedFile = new File(url.toURI());
168             } catch (URISyntaxException ex) {
169                 final String msg = String.format("Unable to locate '%s'", url.toString());
170                 throw new DownloadFailedException(msg);
171             }
172             timestamp = lastModifiedFile.lastModified();
173         } else {
174             HttpURLConnection conn = null;
175             try {
176                 conn = URLConnectionFactory.createHttpURLConnection(url);
177                 conn.setRequestMethod("HEAD");
178                 conn.connect();
179                 final int t = conn.getResponseCode();
180                 if (t >= 200 && t < 300) {
181                     timestamp = conn.getLastModified();
182                 } else {
183                     throw new DownloadFailedException("HEAD request returned a non-200 status code");
184                 }
185             } catch (URLConnectionFailureException ex) {
186                 throw new DownloadFailedException("Error creating URL Connection for HTTP HEAD request.", ex);
187             } catch (IOException ex) {
188                 throw new DownloadFailedException("Error making HTTP HEAD request.", ex);
189             } finally {
190                 if (conn != null) {
191                     try {
192                         conn.disconnect();
193                     } finally {
194                         conn = null;
195                     }
196                 }
197             }
198         }
199         return timestamp;
200     }
201 }