Norm Jacobs
2011-01-14 4b89a19a52eb2439c6209050c1e465d2d5e2c55e
commit | author | age
3a319e 1 #!/usr/bin/python2.6
NJ 2 #
3 # CDDL HEADER START
4 #
5 # The contents of this file are subject to the terms of the
6 # Common Development and Distribution License (the "License").
7 # You may not use this file except in compliance with the License.
8 #
9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 # or http://www.opensolaris.org/os/licensing.
11 # See the License for the specific language governing permissions
12 # and limitations under the License.
13 #
14 # When distributing Covered Code, include this CDDL HEADER in each
15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 # If applicable, add the following below this CDDL HEADER, with the
17 # fields enclosed by brackets "[]" replaced with your own identifying
18 # information: Portions Copyright [yyyy] [name of copyright owner]
19 #
20 # CDDL HEADER END
21 #
4b89a1 22 # Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
3a319e 23 #
NJ 24 #
25 # fetch.py - a file download utility
26 #
27 #  A simple program similiar to wget(1), but handles local file copy, ignores
28 #  directories, and verifies file hashes.
29 #
30
31 import os
32 import sys
6ddf48 33 import shutil
NJ 34 from urllib import splittype, urlopen
3a319e 35
NJ 36 def validate(filename, hash):
37     import hashlib
38
39     try:
40         file = open(filename, 'r')
41     except IOError:
42         return False
43
4b89a1 44     algorithm, hashvalue = hash.split(':')
3a319e 45     try:
NJ 46         m = hashlib.new(algorithm)
47     except ValueError:
48         return False
49
50     while True:
51         block = file.read()
52         m.update(block)
53         if block == '':
54             break
55
4b89a1 56     return "%s:%s" % (algorithm, m.hexdigest())
3a319e 57
6ddf48 58 def download(url, filename = None):
3a319e 59     src = None
NJ 60
6ddf48 61     try:
NJ 62         src = urlopen(url)
63     except IOError:
3a319e 64         return None
NJ 65
66     if filename == None:
6ddf48 67         filename = src.geturl().split('/')[-1]
3a319e 68
6ddf48 69     try:
3a319e 70         dst = open(filename, 'wb');
6ddf48 71     except IOError:
NJ 72         src.close()
73         return None
74
75     while True:
76         block = src.read()
77         if block == '':
78             break;
79         dst.write(block)
80
81     src.close()
82     dst.close()
3a319e 83
NJ 84     # return the name of the file that we downloaded the data to.
85     return filename
86
6ddf48 87 def download_paths(search, filename, url):
3a319e 88     urls = list()
NJ 89
6ddf48 90     if filename != None:
NJ 91         tmp = os.getenv('DOWNLOAD_SEARCH_PATH')
92         if tmp:
93             search += tmp.split(' ')
94
95         file = os.path.basename(filename)
96
97         urls = [ base + '/' + file for base in search ]
98
99         # filename should always be first
100         if filename in urls:
101             urls.remove(filename)
102         urls.insert(0, filename)
103
104     # command line url is a fallback, so it's last
105     if url != None and url not in urls:
106         urls.append(url)
3a319e 107
NJ 108     return urls
109
110 def usage():
6ddf48 111     print "Usage: %s [-f|--file (file)] [-l|--link] [-h|--hash (hash)] [-s|--search (search-dir)] --url (url)" % (sys.argv[0].split('/')[-1])
3a319e 112     sys.exit(1)
NJ 113
114 def main():
115     import getopt
116
117     # FLUSH STDOUT 
118     sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
119
6ddf48 120     file_arg = None
NJ 121     link_arg = False
122     hash_arg = None
123     url_arg = None
124     search_list = list()
3a319e 125
NJ 126     try:
6ddf48 127         opts, args = getopt.getopt(sys.argv[1:], "f:h:ls:u:",
NJ 128             ["file=", "link", "hash=", "search=", "url="])
3a319e 129     except getopt.GetoptError, err:
NJ 130         print str(err)
131         usage()
132
133     for opt, arg in opts:
134         if opt in [ "-f", "--file" ]:
6ddf48 135             file_arg = arg
NJ 136         elif opt in [ "-l", "--link" ]:
137             link_arg = True
3a319e 138         elif opt in [ "-h", "--hash" ]:
6ddf48 139             hash_arg = arg
3a319e 140         elif opt in [ "-s", "--search" ]:
6ddf48 141             search_list.append(arg)
3a319e 142         elif opt in [ "-u", "--url" ]:
6ddf48 143             url_arg = arg
3a319e 144         else:
NJ 145             assert False, "unknown option"
146
6ddf48 147     if url_arg == None:
3a319e 148         usage()
NJ 149
6ddf48 150     for url in download_paths(search_list, file_arg, url_arg):
NJ 151         print "Source %s..." % url,
3a319e 152
6ddf48 153         scheme, path = splittype(url)
NJ 154         name = file_arg
3a319e 155
6ddf48 156         if scheme in [ None, 'file' ]:
NJ 157             if os.path.exists(path) == False:
4b89a1 158                 print "not found, skipping file copy"
6ddf48 159                 continue
NJ 160             elif name != path:
161                 if link_arg == False:
4b89a1 162                     print "\n    copying..."
6ddf48 163                     shutil.copy2(path, name)
NJ 164                 else:
4b89a1 165                     print "\n    linking..."
6ddf48 166                     os.symlink(path, name)
NJ 167             else:
168                 pass
169         elif scheme in [ 'http', 'https', 'ftp' ]:
4b89a1 170             print "\n    downloading...",
6ddf48 171             name = download(url, file_arg)
NJ 172             if name == None:
3a319e 173                 print "failed"
NJ 174                 continue
175
4b89a1 176         print "\n    validating...",
NJ 177         if hash_arg == None:
178             print "skipping (no hash)"
179             sys.exit(0)
180             
181         realhash = validate(name, hash_arg)
182         if realhash == hash_arg:
6ddf48 183             print "ok"
3a319e 184             sys.exit(0)
NJ 185         else:
4b89a1 186             print "corruption detected"
NJ 187             print "    expected: %s" % hash_arg
188             print "    actual:   %s" % realhash
3a319e 189
NJ 190         try:
6ddf48 191             os.remove(name)
3a319e 192         except OSError:
NJ 193             pass
194
195     sys.exit(1)
196
197 if __name__ == "__main__":
198     main()