userland-fetch revision 4427
4496171313bed39e96f21bc2f9faf2868e267ae3girish#!/usr/bin/python2.7
4496171313bed39e96f21bc2f9faf2868e267ae3girish#
4496171313bed39e96f21bc2f9faf2868e267ae3girish# CDDL HEADER START
4496171313bed39e96f21bc2f9faf2868e267ae3girish#
4496171313bed39e96f21bc2f9faf2868e267ae3girish# The contents of this file are subject to the terms of the
4496171313bed39e96f21bc2f9faf2868e267ae3girish# Common Development and Distribution License (the "License").
4496171313bed39e96f21bc2f9faf2868e267ae3girish# You may not use this file except in compliance with the License.
4496171313bed39e96f21bc2f9faf2868e267ae3girish#
4496171313bed39e96f21bc2f9faf2868e267ae3girish# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
4496171313bed39e96f21bc2f9faf2868e267ae3girish# or http://www.opensolaris.org/os/licensing.
4496171313bed39e96f21bc2f9faf2868e267ae3girish# See the License for the specific language governing permissions
4496171313bed39e96f21bc2f9faf2868e267ae3girish# and limitations under the License.
4496171313bed39e96f21bc2f9faf2868e267ae3girish#
4496171313bed39e96f21bc2f9faf2868e267ae3girish# When distributing Covered Code, include this CDDL HEADER in each
4496171313bed39e96f21bc2f9faf2868e267ae3girish# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
4496171313bed39e96f21bc2f9faf2868e267ae3girish# If applicable, add the following below this CDDL HEADER, with the
4496171313bed39e96f21bc2f9faf2868e267ae3girish# fields enclosed by brackets "[]" replaced with your own identifying
4496171313bed39e96f21bc2f9faf2868e267ae3girish# information: Portions Copyright [yyyy] [name of copyright owner]
4496171313bed39e96f21bc2f9faf2868e267ae3girish#
4496171313bed39e96f21bc2f9faf2868e267ae3girish# CDDL HEADER END
4496171313bed39e96f21bc2f9faf2868e267ae3girish#
b80c1b6352b6730ba463305c5aad8ab1b7814a1fSree Vemuri# Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
4496171313bed39e96f21bc2f9faf2868e267ae3girish#
4496171313bed39e96f21bc2f9faf2868e267ae3girish#
4496171313bed39e96f21bc2f9faf2868e267ae3girish# userland-fetch - a file download utility
4496171313bed39e96f21bc2f9faf2868e267ae3girish#
4496171313bed39e96f21bc2f9faf2868e267ae3girish# A simple program similiar to wget(1), but handles local file copy, ignores
4496171313bed39e96f21bc2f9faf2868e267ae3girish# directories, and verifies file hashes.
4496171313bed39e96f21bc2f9faf2868e267ae3girish#
4496171313bed39e96f21bc2f9faf2868e267ae3girish
4496171313bed39e96f21bc2f9faf2868e267ae3girishimport errno
4496171313bed39e96f21bc2f9faf2868e267ae3girishimport os
4496171313bed39e96f21bc2f9faf2868e267ae3girishimport sys
4496171313bed39e96f21bc2f9faf2868e267ae3girishimport shutil
4496171313bed39e96f21bc2f9faf2868e267ae3girishimport subprocess
4496171313bed39e96f21bc2f9faf2868e267ae3girishfrom urllib import splittype
4496171313bed39e96f21bc2f9faf2868e267ae3girishfrom urllib2 import urlopen
4496171313bed39e96f21bc2f9faf2868e267ae3girishimport hashlib
4496171313bed39e96f21bc2f9faf2868e267ae3girish
4496171313bed39e96f21bc2f9faf2868e267ae3girishdef printIOError(e, txt):
59ac0c1669407488b67ae9e273667a340dccc611davemq """ Function to decode and print IOError type exception """
59ac0c1669407488b67ae9e273667a340dccc611davemq print "I/O Error: " + txt + ": "
59ac0c1669407488b67ae9e273667a340dccc611davemq try:
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu (code, message) = e
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu print str(message) + " (" + str(code) + ")"
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu except:
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu print str(e)
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu
4df55fde49134f9735f84011f23a767c75e393c7Janie Ludef validate_signature(path, signature):
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu """Given paths to a file and a detached PGP signature, verify that
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu the signature is valid for the file. Current configuration allows for
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu unrecognized keys to be downloaded as necessary."""
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu
4496171313bed39e96f21bc2f9faf2868e267ae3girish # Find the root of the repo so that we can point GnuPG at the right
4496171313bed39e96f21bc2f9faf2868e267ae3girish # configuration and keyring.
4496171313bed39e96f21bc2f9faf2868e267ae3girish proc = subprocess.Popen(["hg", "root"], stdout=subprocess.PIPE)
4496171313bed39e96f21bc2f9faf2868e267ae3girish proc.wait()
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu if proc.returncode != 0:
4496171313bed39e96f21bc2f9faf2868e267ae3girish return False
4496171313bed39e96f21bc2f9faf2868e267ae3girish out, err = proc.communicate()
4496171313bed39e96f21bc2f9faf2868e267ae3girish gpgdir = os.path.join(out.strip(), "tools", ".gnupg")
4496171313bed39e96f21bc2f9faf2868e267ae3girish
4496171313bed39e96f21bc2f9faf2868e267ae3girish # Skip the permissions warning: none of the information here is private,
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu # so not having to worry about getting mercurial keeping the directory
4496171313bed39e96f21bc2f9faf2868e267ae3girish # unreadable is just simplest.
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu try:
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu proc = subprocess.Popen(["gpg2", "--verify",
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu "--no-permission-warning", "--homedir", gpgdir, signature,
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu path], stdin=open("/dev/null"),
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu except OSError as e:
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu # If the executable simply couldn't be found, just skip the
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu # validation.
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu if e.errno == errno.ENOENT:
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu return False
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu raise
8d4e547db823a866b8f73efc0acdc423e2963cafae
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu proc.wait()
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu if proc.returncode != 0:
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu # Only print GnuPG's output when there was a problem.
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu print proc.stdout.read()
8d4e547db823a866b8f73efc0acdc423e2963cafae return False
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu return True
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu
4df55fde49134f9735f84011f23a767c75e393c7Janie Ludef validate(file, hash):
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu """Given a file-like object and a hash string, verify that the hash
4496171313bed39e96f21bc2f9faf2868e267ae3girish matches the file contents."""
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu
4496171313bed39e96f21bc2f9faf2868e267ae3girish try:
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu algorithm, hashvalue = hash.split(':')
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu except:
8d4e547db823a866b8f73efc0acdc423e2963cafae algorithm = "sha256"
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu # force migration away from sha1
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu if algorithm == "sha1":
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu algorithm = "sha256"
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu try:
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu m = hashlib.new(algorithm)
4496171313bed39e96f21bc2f9faf2868e267ae3girish except ValueError:
4496171313bed39e96f21bc2f9faf2868e267ae3girish return False
4496171313bed39e96f21bc2f9faf2868e267ae3girish
59ac0c1669407488b67ae9e273667a340dccc611davemq while True:
4496171313bed39e96f21bc2f9faf2868e267ae3girish try:
4496171313bed39e96f21bc2f9faf2868e267ae3girish block = file.read()
4496171313bed39e96f21bc2f9faf2868e267ae3girish except IOError, err:
4496171313bed39e96f21bc2f9faf2868e267ae3girish print str(err),
59ac0c1669407488b67ae9e273667a340dccc611davemq break
59ac0c1669407488b67ae9e273667a340dccc611davemq
59ac0c1669407488b67ae9e273667a340dccc611davemq m.update(block)
59ac0c1669407488b67ae9e273667a340dccc611davemq if block == '':
59ac0c1669407488b67ae9e273667a340dccc611davemq break
59ac0c1669407488b67ae9e273667a340dccc611davemq
59ac0c1669407488b67ae9e273667a340dccc611davemq return "%s:%s" % (algorithm, m.hexdigest())
4496171313bed39e96f21bc2f9faf2868e267ae3girish
4df55fde49134f9735f84011f23a767c75e393c7Janie Ludef validate_container(filename, hash):
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu """Given a file path and a hash string, verify that the hash matches the
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu file contents."""
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu try:
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu file = open(filename, 'r')
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu except IOError as e:
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu printIOError(e, "Can't open file " + filename)
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu return False
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu return validate(file, hash)
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu
4df55fde49134f9735f84011f23a767c75e393c7Janie Ludef validate_payload(filename, hash):
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu """Given a file path and a hash string, verify that the hash matches the
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu payload (uncompressed content) of the file."""
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu import re
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu import gzip
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu import bz2
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu expr_bz = re.compile('.+\.bz2$', re.IGNORECASE)
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu expr_gz = re.compile('.+\.gz$', re.IGNORECASE)
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu expr_tgz = re.compile('.+\.tgz$', re.IGNORECASE)
4496171313bed39e96f21bc2f9faf2868e267ae3girish
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu try:
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu if expr_bz.match(filename):
4496171313bed39e96f21bc2f9faf2868e267ae3girish file = bz2.BZ2File(filename, 'r')
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu elif expr_gz.match(filename):
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu file = gzip.GzipFile(filename, 'r')
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu elif expr_tgz.match(filename):
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu file = gzip.GzipFile(filename, 'r')
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu else:
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu return False
4496171313bed39e96f21bc2f9faf2868e267ae3girish except IOError as e:
59ac0c1669407488b67ae9e273667a340dccc611davemq printIOError(e, "Can't open archive " + filename)
4496171313bed39e96f21bc2f9faf2868e267ae3girish return False
4496171313bed39e96f21bc2f9faf2868e267ae3girish return validate(file, hash)
59ac0c1669407488b67ae9e273667a340dccc611davemq
4496171313bed39e96f21bc2f9faf2868e267ae3girish
4df55fde49134f9735f84011f23a767c75e393c7Janie Ludef download(url, filename=None, quiet=False):
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu """Download the content at the given URL to the given filename
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu (defaulting to the basename of the URL if not given. If 'quiet' is
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu True, throw away any error messages. Returns the name of the file to
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu which the content was donloaded."""
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu src = None
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu try:
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu src = urlopen(url)
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu except IOError as e:
4496171313bed39e96f21bc2f9faf2868e267ae3girish if not quiet:
59ac0c1669407488b67ae9e273667a340dccc611davemq printIOError(e, "Can't open url " + url)
59ac0c1669407488b67ae9e273667a340dccc611davemq return None
59ac0c1669407488b67ae9e273667a340dccc611davemq
59ac0c1669407488b67ae9e273667a340dccc611davemq # 3xx, 4xx and 5xx (f|ht)tp codes designate unsuccessfull action
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri if 3 <= int(src.getcode()/100) <= 5:
59ac0c1669407488b67ae9e273667a340dccc611davemq if not quiet:
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu print "Error code: " + str(src.getcode())
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu return None
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu if filename == None:
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu filename = src.geturl().split('/')[-1]
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu try:
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu dst = open(filename, 'wb');
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu except IOError as e:
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu if not quiet:
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu printIOError(e, "Can't open file " + filename + " for writing")
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu src.close()
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu return None
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu while True:
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu block = src.read()
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu if block == '':
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu break;
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu dst.write(block)
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu src.close()
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu dst.close()
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu
b80c1b6352b6730ba463305c5aad8ab1b7814a1fSree Vemuri # return the name of the file that we downloaded the data to.
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu return filename
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu
4df55fde49134f9735f84011f23a767c75e393c7Janie Ludef download_paths(search, filename, url):
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu """Returns a list of URLs where the file 'filename' might be found,
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu using 'url', 'search', and $DOWNLOAD_SEARCH_PATH as places to look.
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu If 'filename' is None, then the list will simply contain 'url'.
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu """
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu urls = list()
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu if filename != None:
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu tmp = os.getenv('DOWNLOAD_SEARCH_PATH')
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu if tmp:
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu search += tmp.split(' ')
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu file = os.path.basename(filename)
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu urls = [ base + '/' + file for base in search ]
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu # filename should always be first
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu if filename in urls:
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu urls.remove(filename)
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu urls.insert(0, filename)
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu # command line url is a fallback, so it's last
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu if url != None and url not in urls:
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu urls.append(url)
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu return urls
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu
b80c1b6352b6730ba463305c5aad8ab1b7814a1fSree Vemuridef download_from_paths(search_list, file_arg, url, link_arg, quiet=False):
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu """Attempts to download a file from a number of possible locations.
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu Generates a list of paths where the file ends up on the local
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu filesystem. This is a generator because while a download might be
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu successful, the signature or hash may not validate, and the caller may
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu want to try again from the next location. The 'link_arg' argument is a
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu boolean which, when True, specifies that if the source is not a remote
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu URL and not already found where it should be, to make a symlink to the
4df55fde49134f9735f84011f23a767c75e393c7Janie Lu source rather than copying it.
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri """
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri for url in download_paths(search_list, file_arg, url):
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri if not quiet:
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri print "Source %s..." % url,
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri scheme, path = splittype(url)
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri name = file_arg
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri if scheme in [ None, 'file' ]:
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri if os.path.exists(path) == False:
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri if not quiet:
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri print "not found, skipping file copy"
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri continue
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri elif name and name != path:
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri if link_arg == False:
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri if not quiet:
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri print "\n copying..."
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri shutil.copy2(path, name)
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri else:
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri if not quiet:
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri print "\n linking..."
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri os.symlink(path, name)
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri elif scheme in [ 'http', 'https', 'ftp' ]:
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri if not quiet:
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri print "\n downloading...",
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri name = download(url, file_arg, quiet)
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri if name == None:
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri if not quiet:
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri print "failed"
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri continue
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri yield name
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuridef usage():
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri print "Usage: %s [-f|--file (file)] [-l|--link] [-h|--hash (hash)] " \
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri "[-s|--search (search-dir)] [-S|--sigurl (signature-url)] --url (url)" % \
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri (sys.argv[0].split('/')[-1])
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri sys.exit(1)
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuridef main():
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri import getopt
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri # FLUSH STDOUT
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri file_arg = None
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri link_arg = False
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri hash_arg = None
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri url_arg = None
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri sig_arg = None
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri search_list = list()
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri try:
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri opts, args = getopt.getopt(sys.argv[1:], "f:h:ls:S:u:",
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri ["file=", "link", "hash=", "search=", "sigurl=", "url="])
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri except getopt.GetoptError, err:
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri print str(err)
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri usage()
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri for opt, arg in opts:
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri if opt in [ "-f", "--file" ]:
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri file_arg = arg
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri elif opt in [ "-l", "--link" ]:
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri link_arg = True
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri elif opt in [ "-h", "--hash" ]:
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri hash_arg = arg
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri elif opt in [ "-s", "--search" ]:
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri search_list.append(arg)
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri elif opt in [ "-S", "--sigurl" ]:
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri sig_arg = arg
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri elif opt in [ "-u", "--url" ]:
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri url_arg = arg
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri else:
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri assert False, "unknown option"
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri for name in download_from_paths(search_list, file_arg, url_arg, link_arg):
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri print "\n validating signature...",
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri sig_valid = False
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri if not sig_arg:
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri print "skipping (no signature URL)"
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri else:
64cfc8ed0c5da83f827c424588bfe709e3fb5a7asvemuri # Put the signature file in the same directory as the
59ac0c1669407488b67ae9e273667a340dccc611davemq # file we're downloading.
59ac0c1669407488b67ae9e273667a340dccc611davemq sig_file = os.path.join(
59ac0c1669407488b67ae9e273667a340dccc611davemq os.path.dirname(file_arg),
4496171313bed39e96f21bc2f9faf2868e267ae3girish os.path.basename(sig_arg))
4496171313bed39e96f21bc2f9faf2868e267ae3girish # Validate with the first signature we find.
4496171313bed39e96f21bc2f9faf2868e267ae3girish for sig_file in download_from_paths(search_list, sig_file,
4496171313bed39e96f21bc2f9faf2868e267ae3girish sig_arg, link_arg, True):
4496171313bed39e96f21bc2f9faf2868e267ae3girish if sig_file:
4496171313bed39e96f21bc2f9faf2868e267ae3girish if validate_signature(name, sig_file):
4496171313bed39e96f21bc2f9faf2868e267ae3girish print "ok"
4496171313bed39e96f21bc2f9faf2868e267ae3girish sig_valid = True
4496171313bed39e96f21bc2f9faf2868e267ae3girish else:
4496171313bed39e96f21bc2f9faf2868e267ae3girish print "failed"
4496171313bed39e96f21bc2f9faf2868e267ae3girish break
4496171313bed39e96f21bc2f9faf2868e267ae3girish else:
4496171313bed39e96f21bc2f9faf2868e267ae3girish continue
4496171313bed39e96f21bc2f9faf2868e267ae3girish else:
print "failed (couldn't fetch signature)"
print " validating hash...",
realhash = validate_container(name, hash_arg)
if not hash_arg:
print "skipping (no hash)"
print "hash is: %s" % realhash
elif realhash == hash_arg:
print "ok"
else:
payloadhash = validate_payload(name, hash_arg)
if payloadhash == hash_arg:
print "ok"
else:
# If the signature validated, then we assume
# that the expected hash is just a typo, but we
# warn just in case.
if sig_valid:
print "invalid hash! Did you forget " \
"to update it?"
else:
print "corruption detected"
print " expected: %s" % hash_arg
print " actual: %s" % realhash
print " payload: %s" % payloadhash
# If the hash is invalid, but the signature
# validation succeeded, rename the archive (so
# the user doesn't have to re-download it) and
# fail. Otherwise, try to remove the file and
# try again.
if sig_valid:
newname = name + ".invalid-hash"
try:
os.rename(name, newname)
except OSError:
pass
else:
print "archive saved as %s; " \
"if it isn't corrupt, " \
"rename to %s" % (newname,
name)
sys.exit(1)
else:
try:
os.remove(name)
except OSError:
pass
continue
sys.exit(0)
sys.exit(1)
if __name__ == "__main__":
main()