[1066] | 1 | import os |
---|
| 2 | import socket |
---|
| 3 | import sys |
---|
[1587] | 4 | from time import time |
---|
[1066] | 5 | from paste.script.appinstall import Installer as BaseInstaller |
---|
| 6 | from paste.fileapp import FileApp |
---|
[1082] | 7 | from paste import urlparser |
---|
| 8 | from paste import request |
---|
[1066] | 9 | from paste.httpexceptions import HTTPNotFound |
---|
[1603] | 10 | import urllib2 |
---|
[1066] | 11 | |
---|
[3172] | 12 | FILES = ['ico','txt','tgz','.gz','egg','zip','exe','cfg'] |
---|
[1603] | 13 | |
---|
[3172] | 14 | |
---|
[1066] | 15 | class PackageProxyApp(object): |
---|
| 16 | |
---|
[3172] | 17 | def __init__(self, index_url=None, pack_dir=None, username=None, |
---|
| 18 | password=None, realm=None, stimeout=None, ptimeout=None): |
---|
[1066] | 19 | if not index_url: |
---|
| 20 | print "No repository index provided" |
---|
| 21 | sys.exit() |
---|
| 22 | if not pack_dir: |
---|
| 23 | print "No packages cache directory provided" |
---|
| 24 | sys.exit() |
---|
| 25 | if not os.path.isdir(pack_dir): |
---|
| 26 | print 'You must create the %r directory' % pack_dir |
---|
| 27 | sys.exit() |
---|
[1603] | 28 | if username and password: |
---|
| 29 | # authenticate |
---|
| 30 | password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm() |
---|
| 31 | top_level_url = index_url |
---|
| 32 | password_mgr.add_password(realm, top_level_url, username, password) |
---|
| 33 | handler = urllib2.HTTPBasicAuthHandler(password_mgr) |
---|
| 34 | opener = urllib2.build_opener(handler) |
---|
| 35 | urllib2.install_opener(opener) |
---|
[3172] | 36 | if stimeout: |
---|
| 37 | socket.setdefaulttimeout(float(stimeout)) |
---|
[1066] | 38 | self.index_url = index_url |
---|
| 39 | self.pack_dir = pack_dir |
---|
[3172] | 40 | self.ptimeout = ptimeout or 3600 |
---|
[1066] | 41 | |
---|
| 42 | def __call__(self, environ, start_response): |
---|
[1603] | 43 | """ serve the static files """ |
---|
[1066] | 44 | path = environ.get('PATH_INFO', '').strip() |
---|
[1083] | 45 | path_parts = path.split('/') |
---|
| 46 | if len(path_parts) > 1 and path_parts[1] == "favicon.ico": |
---|
[1082] | 47 | return HTTPNotFound()(environ, start_response) |
---|
[1603] | 48 | filename = self.checkCache(path[1:]) |
---|
[1066] | 49 | if filename is None: |
---|
| 50 | return HTTPNotFound()(environ, start_response) |
---|
| 51 | return FileApp(filename)(environ, start_response) |
---|
| 52 | |
---|
[1603] | 53 | def checkCache(self, path): |
---|
| 54 | """check if we already have the file and download it if not""" |
---|
[1066] | 55 | pth = self.pack_dir + path |
---|
[3172] | 56 | index = 0 |
---|
| 57 | if not (path[-3:] in FILES): |
---|
[1603] | 58 | if not os.path.exists(pth): |
---|
[3172] | 59 | os.makedirs(pth) # create dir if it is not there |
---|
[1603] | 60 | # add index.html for supposedly folders |
---|
| 61 | pth = pth + 'index.html' |
---|
| 62 | index = 1 |
---|
[1066] | 63 | else: |
---|
[1603] | 64 | pth1 = '/'.join(pth.split('/')[:-1]) |
---|
[1813] | 65 | if not os.path.exists(pth1): |
---|
[3172] | 66 | os.makedirs(pth1) # create parent dir if it is not there |
---|
[1603] | 67 | url = self.index_url+path |
---|
| 68 | #if we dont have download it |
---|
| 69 | if not os.path.exists(pth): |
---|
| 70 | f = urllib2.urlopen(url) |
---|
| 71 | lf = open(pth,'wb') |
---|
| 72 | lf.write(f.read()) |
---|
| 73 | lf.close() |
---|
| 74 | #if we have the index.html file if it is older the 1 hour update |
---|
[3172] | 75 | elif index and int(time()) - os.path.getmtime(pth) > self.ptimeout: |
---|
| 76 | try: |
---|
| 77 | f = urllib2.urlopen(url) |
---|
| 78 | lf = open(pth,'wb') |
---|
| 79 | lf.write(f.read()) |
---|
| 80 | lf.close() |
---|
| 81 | except urllib2.URLError: |
---|
| 82 | pass |
---|
[1066] | 83 | return pth |
---|
| 84 | |
---|
| 85 | def app_factory(global_config, **local_conf): |
---|
| 86 | # Grab config from wsgi .ini file. If not specified, config.py's values |
---|
| 87 | # take over. |
---|
| 88 | pack_dir = local_conf.get('pack_directory', None) |
---|
| 89 | index_url = local_conf.get('index', None) |
---|
[1603] | 90 | username = local_conf.get('username', None) |
---|
| 91 | password = local_conf.get('password', None) |
---|
| 92 | realm = local_conf.get('realm', None) |
---|
[3172] | 93 | stimeout = local_conf.get('stimeout', None) |
---|
| 94 | ptimeout = local_conf.get('ptimeout', None) |
---|
| 95 | return PackageProxyApp(index_url, pack_dir, username, password, realm, stimeout, ptimeout) |
---|
[1082] | 96 | |
---|
| 97 | class StaticURLParser(urlparser.StaticURLParser): |
---|
| 98 | |
---|
| 99 | def __call__(self, environ, start_response): |
---|
| 100 | path_info = environ.get('PATH_INFO', '') |
---|
| 101 | if not path_info: |
---|
| 102 | return self.add_slash(environ, start_response) |
---|
| 103 | if path_info == '/': |
---|
| 104 | # @@: This should obviously be configurable |
---|
| 105 | filename = 'index.html' |
---|
| 106 | else: |
---|
| 107 | filename = request.path_info_pop(environ) |
---|
| 108 | full = os.path.normcase(os.path.normpath( |
---|
| 109 | os.path.join(self.directory, filename))) |
---|
| 110 | if os.path.sep != '/': |
---|
| 111 | full = full.replace('/', os.path.sep) |
---|
| 112 | if self.root_directory is not None and not full.startswith(self.root_directory): |
---|
| 113 | # Out of bounds |
---|
| 114 | return self.not_found(environ, start_response) |
---|
| 115 | if not os.path.exists(full): |
---|
| 116 | if full.endswith('index.html') and not os.path.isfile(full): |
---|
[1083] | 117 | start_response('200 OK', [('Content-Type', 'text/html')]) |
---|
[1082] | 118 | return [self.get_index_html()] |
---|
| 119 | return self.not_found(environ, start_response) |
---|
| 120 | if os.path.isdir(full): |
---|
| 121 | # @@: Cache? |
---|
| 122 | child_root = self.root_directory is not None and \ |
---|
| 123 | self.root_directory or self.directory |
---|
| 124 | return self.__class__(full, root_directory=child_root, |
---|
| 125 | cache_max_age=self.cache_max_age)(environ, |
---|
| 126 | start_response) |
---|
| 127 | if environ.get('PATH_INFO') and environ.get('PATH_INFO') != '/': |
---|
| 128 | return self.error_extra_path(environ, start_response) |
---|
| 129 | if_none_match = environ.get('HTTP_IF_NONE_MATCH') |
---|
| 130 | if if_none_match: |
---|
| 131 | mytime = os.stat(full).st_mtime |
---|
| 132 | if str(mytime) == if_none_match: |
---|
| 133 | headers = [] |
---|
| 134 | ETAG.update(headers, mytime) |
---|
| 135 | start_response('304 Not Modified', headers) |
---|
| 136 | return [''] # empty body |
---|
| 137 | |
---|
| 138 | fa = self.make_app(full) |
---|
| 139 | if self.cache_max_age: |
---|
| 140 | fa.cache_control(max_age=self.cache_max_age) |
---|
| 141 | return fa(environ, start_response) |
---|
| 142 | |
---|
| 143 | def get_index_html(self): |
---|
| 144 | path = self.directory |
---|
| 145 | # create sorted lists of directories and files |
---|
[1083] | 146 | names = [i for i in os.listdir(path) if not i.startswith('.')] |
---|
[1082] | 147 | dirs = [i for i in names if os.path.isdir(os.path.join(path, i))] |
---|
| 148 | dirs.sort() |
---|
| 149 | files = [i for i in names if os.path.isfile(os.path.join(path, i))] |
---|
| 150 | files.sort() |
---|
| 151 | names = dirs + files |
---|
| 152 | links = '\n'.join(['<li><a href="%s">%s</a></li>' % (i, i) for i in names]) |
---|
| 153 | template = open(os.path.join(os.path.dirname(__file__), 'index.html')).read() |
---|
| 154 | return template % {'path': path[len(self.root_directory):], 'links': links} |
---|
| 155 | |
---|
| 156 | def make_static(global_conf, document_root, cache_max_age=None): |
---|
| 157 | """ |
---|
| 158 | Return a WSGI application that serves a directory (configured |
---|
| 159 | with document_root) |
---|
| 160 | |
---|
| 161 | cache_max_age - integer specifies CACHE_CONTROL max_age in seconds |
---|
| 162 | """ |
---|
| 163 | if cache_max_age is not None: |
---|
| 164 | cache_max_age = int(cache_max_age) |
---|
| 165 | return StaticURLParser( |
---|
| 166 | document_root, cache_max_age=cache_max_age) |
---|
| 167 | |
---|
| 168 | |
---|
[1066] | 169 | """ |
---|
| 170 | class Installer(BaseInstaller): |
---|
| 171 | use_cheetah = False |
---|
| 172 | config_file = 'deployment.ini_tmpl' |
---|
| 173 | |
---|
| 174 | def config_content(self, command, vars): |
---|
| 175 | import pkg_resources |
---|
| 176 | module = 'collective.eggproxy' |
---|
| 177 | if pkg_resources.resource_exists(module, self.config_file): |
---|
| 178 | return self.template_renderer( |
---|
| 179 | pkg_resources.resource_string(module, self.config_file), |
---|
| 180 | vars, |
---|
| 181 | filename=self.config_file) |
---|
[1260] | 182 | """ |
---|