17 # fields enclosed by brackets "[]" replaced with your own identifying |
17 # fields enclosed by brackets "[]" replaced with your own identifying |
18 # information: Portions Copyright [yyyy] [name of copyright owner] |
18 # information: Portions Copyright [yyyy] [name of copyright owner] |
19 # |
19 # |
20 # CDDL HEADER END |
20 # CDDL HEADER END |
21 # |
21 # |
22 # Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. |
22 # Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. |
23 # |
23 # |
24 # |
24 # |
25 # userland-fetch - a file download utility |
25 # userland-fetch - a file download utility |
26 # |
26 # |
27 # A simple program similiar to wget(1), but handles local file copy, ignores |
27 # A simple program similiar to wget(1), but handles local file copy, ignores |
79 if proc.returncode != 0: |
79 if proc.returncode != 0: |
80 # Only print GnuPG's output when there was a problem. |
80 # Only print GnuPG's output when there was a problem. |
81 print proc.stdout.read() |
81 print proc.stdout.read() |
82 return False |
82 return False |
83 return True |
83 return True |
84 |
84 |
85 def validate(file, hash): |
85 def validate(file, hash): |
86 """Given a file-like object and a hash string, verify that the hash |
86 """Given a file-like object and a hash string, verify that the hash |
87 matches the file contents.""" |
87 matches the file contents.""" |
88 |
88 |
89 try: |
89 try: |
149 printIOError(e, "Can't open archive " + filename) |
149 printIOError(e, "Can't open archive " + filename) |
150 return False |
150 return False |
151 return validate(file, hash) |
151 return validate(file, hash) |
152 |
152 |
153 |
153 |
154 def download(url, filename=None, quiet=False): |
154 def download(url, timeout, filename=None, quiet=False): |
155 """Download the content at the given URL to the given filename |
155 """Download the content at the given URL to the given filename |
156 (defaulting to the basename of the URL if not given. If 'quiet' is |
156 (defaulting to the basename of the URL if not given. If 'quiet' is |
157 True, throw away any error messages. Returns the name of the file to |
157 True, throw away any error messages. Returns the name of the file to |
158 which the content was donloaded.""" |
158 which the content was donloaded.""" |
159 |
159 |
160 src = None |
160 src = None |
161 |
161 |
162 try: |
162 try: |
163 src = urlopen(url) |
163 src = urlopen(url=url, timeout=timeout) |
164 except IOError as e: |
164 except IOError as e: |
165 if not quiet: |
165 if not quiet: |
166 printIOError(e, "Can't open url " + url) |
166 printIOError(e, "Can't open url " + url) |
167 return None |
167 return None |
168 |
168 |
222 if url != None and url not in urls: |
222 if url != None and url not in urls: |
223 urls.append(url) |
223 urls.append(url) |
224 |
224 |
225 return urls |
225 return urls |
226 |
226 |
227 def download_from_paths(search_list, file_arg, url, link_arg, quiet=False): |
227 def download_from_paths(search_list, file_arg, url, timeout_arg, link_arg, quiet=False): |
228 """Attempts to download a file from a number of possible locations. |
228 """Attempts to download a file from a number of possible locations. |
229 Generates a list of paths where the file ends up on the local |
229 Generates a list of paths where the file ends up on the local |
230 filesystem. This is a generator because while a download might be |
230 filesystem. This is a generator because while a download might be |
231 successful, the signature or hash may not validate, and the caller may |
231 successful, the signature or hash may not validate, and the caller may |
232 want to try again from the next location. The 'link_arg' argument is a |
232 want to try again from the next location. The 'link_arg' argument is a |
256 print "\n linking..." |
256 print "\n linking..." |
257 os.symlink(path, name) |
257 os.symlink(path, name) |
258 elif scheme in [ 'http', 'https', 'ftp' ]: |
258 elif scheme in [ 'http', 'https', 'ftp' ]: |
259 if not quiet: |
259 if not quiet: |
260 print "\n downloading...", |
260 print "\n downloading...", |
261 name = download(url, file_arg, quiet) |
261 name = download(url, timeout_arg, file_arg, quiet) |
262 if name == None: |
262 if name == None: |
263 if not quiet: |
263 if not quiet: |
264 print "failed" |
264 print "failed" |
265 continue |
265 continue |
266 |
266 |
267 yield name |
267 yield name |
268 |
268 |
269 def usage(): |
269 def usage(): |
270 print "Usage: %s [-f|--file (file)] [-l|--link] [-h|--hash (hash)] " \ |
270 print "Usage: %s [-f|--file (file)] [-l|--link] [-h|--hash (hash)] " \ |
271 "[-s|--search (search-dir)] [-S|--sigurl (signature-url)] --url (url)" % \ |
271 "[-s|--search (search-dir)] [-S|--sigurl (signature-url)] " \ |
|
272 "[-t|--timeout (timeout)] --url (url)" % \ |
272 (sys.argv[0].split('/')[-1]) |
273 (sys.argv[0].split('/')[-1]) |
273 sys.exit(1) |
274 sys.exit(1) |
274 |
275 |
275 def main(): |
276 def main(): |
276 import getopt |
277 import getopt |
277 |
278 |
278 # FLUSH STDOUT |
279 # FLUSH STDOUT |
279 sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0) |
280 sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0) |
280 |
281 |
281 file_arg = None |
282 file_arg = None |
282 link_arg = False |
283 link_arg = False |
283 hash_arg = None |
284 hash_arg = None |
284 url_arg = None |
285 url_arg = None |
285 sig_arg = None |
286 sig_arg = None |
|
287 timeout_arg = 300 |
286 search_list = list() |
288 search_list = list() |
287 |
289 |
288 try: |
290 try: |
289 opts, args = getopt.getopt(sys.argv[1:], "f:h:ls:S:u:", |
291 opts, args = getopt.getopt(sys.argv[1:], "f:h:ls:S:t:u:", |
290 ["file=", "link", "hash=", "search=", "sigurl=", "url="]) |
292 ["file=", "link", "hash=", "search=", "sigurl=", |
|
293 "timeout=", "url="]) |
291 except getopt.GetoptError, err: |
294 except getopt.GetoptError, err: |
292 print str(err) |
295 print str(err) |
293 usage() |
296 usage() |
294 |
297 |
295 for opt, arg in opts: |
298 for opt, arg in opts: |
301 hash_arg = arg |
304 hash_arg = arg |
302 elif opt in [ "-s", "--search" ]: |
305 elif opt in [ "-s", "--search" ]: |
303 search_list.append(arg) |
306 search_list.append(arg) |
304 elif opt in [ "-S", "--sigurl" ]: |
307 elif opt in [ "-S", "--sigurl" ]: |
305 sig_arg = arg |
308 sig_arg = arg |
|
309 elif opt in [ "-t", "--timeout" ]: |
|
310 try: |
|
311 timeout_arg = int(arg) |
|
312 except ValueError: |
|
313 print "Invalid argument for %s, should be a " \ |
|
314 "number, but is %s" % (opt, arg) |
|
315 sys.exit(1) |
|
316 if timeout_arg < 0: |
|
317 print "Invalid argument for %s, should be a " \ |
|
318 "positive number, but is %s" % (opt, arg) |
|
319 sys.exit(1) |
306 elif opt in [ "-u", "--url" ]: |
320 elif opt in [ "-u", "--url" ]: |
307 url_arg = arg |
321 url_arg = arg |
308 else: |
322 else: |
309 assert False, "unknown option" |
323 assert False, "unknown option" |
310 |
324 |
311 for name in download_from_paths(search_list, file_arg, url_arg, link_arg): |
325 for name in download_from_paths(search_list, file_arg, url_arg, |
|
326 timeout_arg, link_arg): |
312 print "\n validating signature...", |
327 print "\n validating signature...", |
313 |
328 |
314 sig_valid = False |
329 sig_valid = False |
315 if not sig_arg: |
330 if not sig_arg: |
316 print "skipping (no signature URL)" |
331 print "skipping (no signature URL)" |
320 sig_file = os.path.join( |
335 sig_file = os.path.join( |
321 os.path.dirname(file_arg), |
336 os.path.dirname(file_arg), |
322 os.path.basename(sig_arg)) |
337 os.path.basename(sig_arg)) |
323 # Validate with the first signature we find. |
338 # Validate with the first signature we find. |
324 for sig_file in download_from_paths(search_list, sig_file, |
339 for sig_file in download_from_paths(search_list, sig_file, |
325 sig_arg, link_arg, True): |
340 sig_arg, timeout_arg, link_arg, True): |
326 if sig_file: |
341 if sig_file: |
327 if validate_signature(name, sig_file): |
342 if validate_signature(name, sig_file): |
328 print "ok" |
343 print "ok" |
329 sig_valid = True |
344 sig_valid = True |
330 else: |
345 else: |