1 /* 2 * This file is part of dependency-check-utils. 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.ByteArrayOutputStream; 21 import java.io.File; 22 import java.io.FileOutputStream; 23 import java.io.IOException; 24 import java.io.InputStream; 25 import java.io.OutputStream; 26 import java.net.URL; 27 28 import static java.lang.String.format; 29 30 import java.nio.Buffer; 31 import java.nio.ByteBuffer; 32 import java.nio.channels.Channels; 33 import java.nio.channels.FileChannel; 34 import java.nio.channels.ReadableByteChannel; 35 import java.nio.charset.StandardCharsets; 36 import java.util.zip.GZIPInputStream; 37 38 import org.apache.commons.io.IOUtils; 39 import org.slf4j.Logger; 40 import org.slf4j.LoggerFactory; 41 42 /** 43 * A utility to download files from the Internet. 44 * 45 * @author Jeremy Long 46 * @version $Id: $Id 47 */ 48 public final class Downloader { 49 50 /** 51 * UTF-8 character set name. 52 */ 53 private static final String UTF8 = StandardCharsets.UTF_8.name(); 54 /** 55 * The Logger for use throughout the class. 56 */ 57 private static final Logger LOGGER = LoggerFactory.getLogger(Downloader.class); 58 /** 59 * The configured settings. 60 */ 61 private final Settings settings; 62 63 /** 64 * Constructs a new Downloader object. 65 * 66 * @param settings the configured settings 67 */ 68 public Downloader(Settings settings) { 69 this.settings = settings; 70 } 71 72 /** 73 * Retrieves a file from a given URL and saves it to the outputPath. 74 * 75 * @param url the URL of the file to download 76 * @param outputPath the path to the save the file to 77 * @throws org.owasp.dependencycheck.utils.DownloadFailedException is thrown 78 * if there is an error downloading the file 79 * @throws TooManyRequestsException thrown when a 429 is received 80 * @throws ResourceNotFoundException thrown when a 404 is received 81 */ 82 public void fetchFile(URL url, File outputPath) throws DownloadFailedException, TooManyRequestsException, ResourceNotFoundException { 83 fetchFile(url, outputPath, true, null, null); 84 } 85 86 /** 87 * Retrieves a file from a given URL and saves it to the outputPath. 88 * 89 * @param url the URL of the file to download 90 * @param outputPath the path to the save the file to 91 * @param userKey the settings key for the username to be used 92 * @param passwordKey the settings key for the password to be used 93 * @throws org.owasp.dependencycheck.utils.DownloadFailedException is thrown 94 * if there is an error downloading the file 95 * @throws TooManyRequestsException thrown when a 429 is received 96 * @throws ResourceNotFoundException thrown when a 404 is received 97 */ 98 public void fetchFile(URL url, File outputPath, String userKey, String passwordKey) 99 throws DownloadFailedException, TooManyRequestsException, ResourceNotFoundException { 100 fetchFile(url, outputPath, true, userKey, passwordKey); 101 } 102 103 /** 104 * Retrieves a file from a given URL and saves it to the outputPath. 105 * 106 * @param url the URL of the file to download 107 * @param outputPath the path to the save the file to 108 * @param useProxy whether to use the configured proxy when downloading 109 * files 110 * @throws org.owasp.dependencycheck.utils.DownloadFailedException is thrown 111 * if there is an error downloading the file 112 * @throws TooManyRequestsException thrown when a 429 is received 113 * @throws ResourceNotFoundException thrown when a 404 is received 114 */ 115 public void fetchFile(URL url, File outputPath, boolean useProxy) throws DownloadFailedException, 116 TooManyRequestsException, ResourceNotFoundException { 117 fetchFile(url, outputPath, useProxy, null, null); 118 } 119 120 /** 121 * Retrieves a file from a given URL and saves it to the outputPath. 122 * 123 * @param url the URL of the file to download 124 * @param outputPath the path to the save the file to 125 * @param useProxy whether to use the configured proxy when downloading 126 * files 127 * @param userKey the settings key for the username to be used 128 * @param passwordKey the settings key for the password to be used 129 * @throws org.owasp.dependencycheck.utils.DownloadFailedException is thrown 130 * if there is an error downloading the file 131 * @throws TooManyRequestsException thrown when a 429 is received 132 * @throws ResourceNotFoundException thrown when a 404 is received 133 */ 134 public void fetchFile(URL url, File outputPath, boolean useProxy, String userKey, String passwordKey) throws DownloadFailedException, 135 TooManyRequestsException, ResourceNotFoundException { 136 InputStream in = null; 137 try (HttpResourceConnection conn = new HttpResourceConnection(settings, useProxy, userKey, passwordKey)) { 138 in = conn.fetch(url); 139 try (ReadableByteChannel sourceChannel = Channels.newChannel(in); 140 FileChannel destChannel = new FileOutputStream(outputPath).getChannel()) { 141 ByteBuffer buffer = ByteBuffer.allocateDirect(8192); 142 while (sourceChannel.read(buffer) != -1) { 143 // cast is a workaround, see https://github.com/plasma-umass/doppio/issues/497#issuecomment-334740243 144 ((Buffer)buffer).flip(); 145 destChannel.write(buffer); 146 buffer.compact(); 147 } 148 } 149 } catch (IOException ex) { 150 final String msg = format("Download failed, unable to copy '%s' to '%s'; %s", 151 url.toString(), outputPath.getAbsolutePath(), ex.getMessage()); 152 throw new DownloadFailedException(msg, ex); 153 } finally { 154 if (in != null) { 155 try { 156 in.close(); 157 } catch (IOException ex) { 158 LOGGER.trace("Ignorable error", ex); 159 } 160 } 161 } 162 } 163 164 /** 165 * Retrieves a file from a given URL and returns the contents. 166 * 167 * @param url the URL of the file to download 168 * @param useProxy whether to use the configured proxy when downloading 169 * files 170 * @return the content of the file 171 * @throws DownloadFailedException is thrown if there is an error 172 * downloading the file 173 * @throws TooManyRequestsException thrown when a 429 is received 174 * @throws ResourceNotFoundException thrown when a 404 is received 175 */ 176 public String fetchContent(URL url, boolean useProxy) throws DownloadFailedException, TooManyRequestsException, ResourceNotFoundException { 177 return fetchContent(url, useProxy, null, null); 178 } 179 180 /** 181 * Retrieves a file from a given URL and returns the contents. 182 * 183 * @param url the URL of the file to download 184 * @param useProxy whether to use the configured proxy when downloading 185 * files 186 * @param userKey the settings key for the username to be used 187 * @param passwordKey the settings key for the password to be used 188 * @return the content of the file 189 * @throws DownloadFailedException is thrown if there is an error 190 * downloading the file 191 * @throws TooManyRequestsException thrown when a 429 is received 192 * @throws ResourceNotFoundException thrown when a 404 is received 193 */ 194 public String fetchContent(URL url, boolean useProxy, String userKey, String passwordKey) 195 throws DownloadFailedException, TooManyRequestsException, ResourceNotFoundException { 196 InputStream in = null; 197 try (HttpResourceConnection conn = new HttpResourceConnection(settings, useProxy, userKey, passwordKey); 198 ByteArrayOutputStream out = new ByteArrayOutputStream()) { 199 in = conn.fetch(url); 200 IOUtils.copy(in, out); 201 return out.toString(UTF8); 202 } catch (IOException ex) { 203 final String msg = format("Download failed, unable to retrieve '%s'; %s", url, ex.getMessage()); 204 throw new DownloadFailedException(msg, ex); 205 } finally { 206 if (in != null) { 207 try { 208 in.close(); 209 } catch (IOException ex) { 210 LOGGER.trace("Ignorable error", ex); 211 } 212 } 213 } 214 } 215 }