Source code: cvebrowser/dictionary/data/net/MitreDataRepository.java
1 package cvebrowser.dictionary.data.net;
2
3 import java.net.URL;
4 import java.net.URLConnection;
5
6 import java.io.InputStream;
7 import java.io.FileOutputStream;
8 import java.io.BufferedOutputStream;
9 import java.io.OutputStream;
10 import java.io.IOException;
11 import java.io.FileNotFoundException;
12 import java.io.ObjectOutputStream;
13 import java.io.ObjectInputStream;
14
15 import java.util.zip.GZIPInputStream;
16 import java.util.zip.GZIPOutputStream;
17 import java.util.Properties;
18 import java.util.ResourceBundle;
19 import java.util.Locale;
20
21 import gnu.getopt.Getopt;
22
23 import cvebrowser.util.CommandLine;
24 import cvebrowser.util.parser.FileType;
25 import cvebrowser.util.parser.ParserException;
26 import cvebrowser.util.parser.Types;
27 import cvebrowser.dictionary.data.parser.CSVMitreFileFactory;
28 import cvebrowser.dictionary.data.parser.CSVFile;
29 import cvebrowser.dictionary.data.parser.DataParserException;
30
31 /**
32 * MitreDataRepository - Manages access to the CVE Mitre data file.
33 * @version 0.2 - 07/06/2003
34 * @since 0.1
35 * @author Jose Vicente Nunez Zuleta (josevnz@users.sourceforge.net)
36 *
37 */
38 public final class MitreDataRepository {
39
40 /**
41 * Size of the network buffer size
42 */
43 public static final int NET_BUFFER_SIZE = 8192;
44
45 /**
46 * Size of the local file buffer
47 */
48 public static final int FILE_BUFFER_SIZE = 8192;
49
50 /**
51 * Number of retry atempts after a failed connection
52 */
53 public static final int NUMBER_OF_RETRY = 3;
54
55 /**
56 * Amount of time to wait between retry, in miliseconds
57 */
58 public static final int RETRY_WAIT = 10000;
59
60 /**
61 * Expected number of arguments
62 */
63 public static final int EXPECTED_PARAMETERS=2;
64
65 private URL _url;
66 private String _fileName;
67 private byte [] _buffer;
68 private Properties _config = new Properties();
69 private static ResourceBundle _bundle = null;
70 private int _numberOfRetries = NUMBER_OF_RETRY;
71 private int _retryWait = RETRY_WAIT;
72
73 /**
74 * Parametric constructor.
75 * @param url_ URL where the Mitre CVE data file is located
76 * @param fileName_ Destination filename for the downloaded file.
77 * @throws IllegalArgumentException if any of the arguments is missing.
78 * @since 0.1
79 */
80 public MitreDataRepository(URL url_, String fileName_) {
81 if (url_ == null) {
82 throw new IllegalArgumentException(_bundle.getString("cvebrowser.dictionary.data.net.MitreDataRepository.MitreDataRepository.message.error.MissingUrl"));
83 }
84 if (fileName_ == null) {
85 throw new IllegalArgumentException(_bundle.getString("cvebrowser.dictionary.data.net.MitreDataRepository.MitreDataRepository.message.error.MissingFilename"));
86 }
87 _url = url_;
88 _fileName = fileName_;
89 }
90
91 /**
92 * Set the default amount of retries after a failure
93 * @param number_ number of retries
94 * @throws IllegalArgumentException If the number of retries is invalid
95 * @since 0.1
96 */
97 public void setNumberOfRetries(int number_) throws IllegalArgumentException {
98 if (number_ < 0) {
99 throw new IllegalArgumentException(_bundle.getString("cvebrowser.dictionary.data.net.MitreDataRepository.setNumberOfRetries.message.error.IncorrectNumberOfRetries"));
100 } else {
101 _numberOfRetries = number_;
102 }
103 }
104
105 /**
106 * Get the current number of retries
107 * @return int Number of retries
108 * @since 0.1
109 */
110 public int getNumberOfRetries() {
111 return _numberOfRetries;
112 }
113
114 /**
115 * Set the number of retries when getting a conection
116 * @param time_ Number of miliseconds to wait
117 *@throws IllegalArgumentException if the timeout time is invalid
118 * @since 0.1
119 */
120 public void setRetriesWait(int time_) throws IllegalArgumentException {
121 if (time_ < 0) {
122 throw new IllegalArgumentException(_bundle.getString("cvebrowser.dictionary.data.net.MitreDataRepository.setNumberOfRetries.message.error.IncorrectTimeout"));
123 } else {
124 _retryWait = time_;
125 }
126 }
127
128 /**
129 * Return the current retry time
130 * @return int retry time in miliseconds
131 * @since 0.1
132 */
133 public int getRetriesTimeout() {
134 return _retryWait;
135 }
136
137 /**
138 * Returns a new URLConnection object
139 * @return URLConnection conection to the desired url
140 * @throws IOException
141 * @throws InterruptedException
142 * @since 0.1
143 */
144 private URLConnection getConnection() throws IOException, InterruptedException {
145 URLConnection conn = null;
146 int currentAttempt = 1;
147 while ( (currentAttempt <= _numberOfRetries) && (conn == null) ) {
148 try {
149 conn = _url.openConnection();
150 } catch (IOException ioexp) {
151 if (currentAttempt <= _numberOfRetries) {
152 conn = null;
153 wait(_retryWait); // wait some time before retrying again...
154 } else {
155 throw ioexp; // Re - launch the exception
156 }
157 }
158 currentAttempt++;
159 }
160 return conn;
161 }
162
163 /**
164 * Returns the type of the polled data file, using content heuristics.
165 * @return int The data type.
166 * @throws IOException Error getting the data type.
167 * @throws InterruptedException if the inputstream cannot be obtained
168 * @throws DataParserException
169 * @throws ParserException
170 * @see cvebrowser.util.parser.Types
171 * @since 0.1
172 */
173 public int getType() throws IOException, InterruptedException, DataParserException, ParserException {
174 int type = Types.DATA_TYPE_UNKNOWN;
175 InputStream inputstream = null;
176 CSVFile csvFile = null;
177 try {
178 inputstream = getInputStream();
179 csvFile = CSVMitreFileFactory.getCSVFile(inputstream);
180 type = csvFile.getType();
181 } catch (IOException ioexp) {
182 throw ioexp;
183 } finally {
184 if (csvFile != null) {
185 try {
186 csvFile.close();
187 } catch (Exception ignore) {};
188 }
189 }
190 return type;
191 }
192
193 /**
194 * Tells if the CVE file is compressed or not
195 * @return boolean
196 * @throws IOException Error getting the stream
197 * @throws InterruptedException
198 * @since 0.1
199 */
200 public boolean isCompressed() throws IOException, InterruptedException {
201 boolean isCompressed = true;
202 InputStream inputstream = null;
203 try {
204 inputstream = new GZIPInputStream( (getConnection()).getInputStream());
205 } catch (IOException ioexp) {
206 if (ioexp.toString().indexOf("Not in GZIP format") != -1) { // Is not compressed
207 isCompressed = false;
208 } else { // other type of error, let the caller to deal with it.
209 throw ioexp;
210 }
211 } finally {
212 if (inputstream != null) {
213 try {
214 inputstream.close();
215 } catch (Exception ignore) {};
216 }
217 }
218 return isCompressed;
219 }
220
221 /**
222 * Get an InputStream, regardless if the data file is compressed or not.
223 * @return InputStream. Can be null on errors.
224 * @throws IOException If the inputstream cannot be obtained
225 * @throws InterruptedException if the inputstream cannot be obtained
226 * @since 0.1
227 */
228 public InputStream getInputStream() throws IOException, InterruptedException {
229 URLConnection conn = null;
230 InputStream inputstream = null;
231 try {
232 conn = getConnection();
233 // Check if the Stream is compressed and deal with it in the most appropiate way
234 inputstream = conn.getInputStream();
235 } catch (IOException ioexp) {
236 throw ioexp;
237 } finally {
238 // empty
239 }
240 return inputstream;
241 }
242
243 /**
244 * Returns the version of the CVE / CAN datafile. An internal check is made to check wich type of data file is being polled.
245 * @return String Version of the CVE file
246 * @throws IOException Error getting the version
247 * @throws InterruptedException Error getting the version
248 * @throws NumberFormatException If the serial is not a number
249 * @throws DataParserException
250 * @throws ParserException
251 * @since 0.1
252 */
253 public String getVersion() throws IOException, InterruptedException, NumberFormatException, DataParserException, ParserException {
254 String version = null;
255 InputStream inputstream = null;
256 CSVFile csvFile = null;
257 try {
258 csvFile = CSVMitreFileFactory.getCSVFile(getInputStream());
259 version = csvFile.getVersion();
260 Integer.parseInt(version); // final test, check if is a number
261 } catch (NumberFormatException nfexp) {
262 throw nfexp;
263 } catch (IOException ioexp) {
264 throw ioexp;
265 } finally {
266 if (csvFile != null) {
267 try {
268 csvFile.close();
269 } catch (Exception ignore) {};
270 }
271 }
272 return version;
273 }
274
275 /**
276 * Download the file to the desired location. If the file is not compressed then is compressed on the fly using GZIP.
277 * @return int Number of bytes downloaded
278 * @throws IOException
279 * @throws InterruptedException
280 * @since 0.1
281 */
282 public int download() throws IOException, InterruptedException {
283 InputStream input = null;
284 OutputStream localFile = null;
285 byte [] buffer = null;
286 int readBytes = 0;
287 try {
288 URLConnection urlcon = _url.openConnection();
289 input = urlcon.getInputStream();
290 if (! isCompressed()) {
291 localFile = new BufferedOutputStream(new GZIPOutputStream(new FileOutputStream(_fileName), FILE_BUFFER_SIZE));
292 } else {
293 localFile = new BufferedOutputStream(new FileOutputStream(_fileName), FILE_BUFFER_SIZE);
294 }
295 buffer = new byte[urlcon.getContentLength()];
296 int currbytes = 0;
297 while((currbytes = input.read(buffer)) != -1) {
298 localFile.write(buffer, 0, currbytes);
299 readBytes += currbytes;
300 }
301 ((BufferedOutputStream) localFile).flush();
302 } catch (FileNotFoundException fne) {
303 // Add extra information to the exception, the regular Java message is too simple.
304 throw new FileNotFoundException(_bundle.getString("cvebrowser.dictionary.data.net.MitreDataRepository.MitreDataRepository.message.error.downloadUrlIsIncorrect") + ", " + fne);
305 } catch (IOException ioe) {
306 throw ioe;
307 } finally {
308 if (localFile != null) {
309 try { localFile.close(); } catch (IOException ignore) {};
310 }
311 }
312 return readBytes;
313 }
314
315 /**
316 * Command line entry point
317 * Currently recognized parameters.
318 * <ul>
319 * <li> -s : Where to download the file.
320 * <li> -d : Where to save the file
321 * </ul>
322 * Optional parameters:
323 * <ul>
324 * <li> -l : Languaje to use
325 * <li> -k : Country to use
326 * <li> -h : Show the localized mini help.
327 * </ul>
328 * @param argv_
329 * @throws IllegalArgumentException
330 * @throws NetException
331 */
332 public static void main (String [] argv_) throws NetException {
333 Getopt optList = new Getopt(MitreDataRepository.class.getName(), argv_, "s:d:l:k:h");
334 MitreDataRepository instance = null;
335 String sourceUrl = null;
336 String destFileName = null;
337 boolean getHelp = false;
338 String languaje = null;
339 String country = null;
340 int requiredParametersCounter = 0;
341 try {
342 int option;
343 String arg;
344 while ((option = optList.getopt()) != -1) {
345 switch(option) {
346 case 's':
347 sourceUrl = optList.getOptarg().trim();
348 requiredParametersCounter++;
349 break;
350 case 'd':
351 destFileName = optList.getOptarg().trim();
352 requiredParametersCounter++;
353 break;
354 case 'h':
355 getHelp = true;
356 break;
357 case 'l':
358 languaje = optList.getOptarg().trim();
359 break;
360 case 'k':
361 country = optList.getOptarg().trim();
362 break;
363 case '?':
364 break; // getopt() already printed an error
365 default:
366 //
367 }
368 }
369 CommandLine.setDefaultLanguaje(languaje, country);
370 _bundle = ResourceBundle.getBundle(MitreDataRepository.class.getName(), Locale.getDefault());
371 if (getHelp) {
372 System.out.println(_bundle.getString("cvebrowser.dictionary.data.net.MitreDataRepository.main.info.usageMode"));
373 return;
374 }
375 CommandLine.checkAllTheParameters(requiredParametersCounter, EXPECTED_PARAMETERS, true);
376 System.out.println(_bundle.getString("cvebrowser.dictionary.data.net.MitreDataRepository.main.info.downloadInfo") + "('" + sourceUrl + "', '" + destFileName + "')");
377 instance = new MitreDataRepository(new URL(sourceUrl), destFileName);
378 int bytes = instance.download();
379 System.out.println(_bundle.getString("cvebrowser.dictionary.data.net.MitreDataRepository.main.info.bytesDownloaded") + ": " + bytes);
380 } catch (Exception exp) {
381 throw new NetException(exp.toString(), exp);
382 }
383 }
384
385 /**
386 * Make this class uncloneable. Anyone who wants to use this class must use the constructor.
387 * @throws CloneNotSupportedException
388 */
389 public final Object clone() throws java.lang.CloneNotSupportedException {
390 throw new java.lang.CloneNotSupportedException();
391 }
392
393 /**
394 * Make this class unserializable. Any attempt to serialize will throw an exception.
395 * @param out_
396 * @throws IOException
397 */
398 private final void writeObject(ObjectOutputStream out_) throws java.io.IOException {
399 throw new java.io.IOException();
400 }
401
402 /**
403 * Make this class undeserializeable. Throw an exception if this method is ever called.
404 * @param in_
405 * @throws IOException
406 */
407 private final void readObject(ObjectInputStream in_) throws java.io.IOException {
408 throw new java.io.IOException();
409 }
410 } // end of class