##// END OF EJS Templates
Correct Content-Type header values for archive downloads....
Ry4an Brase -
r12570:a72c5ff1 stable
parent child Browse files
Show More
@@ -1,291 +1,291
1 1 # hgweb/hgweb_mod.py - Web interface for a repository.
2 2 #
3 3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9 import os
10 10 from mercurial import ui, hg, hook, error, encoding, templater
11 11 from common import get_mtime, ErrorResponse, permhooks
12 12 from common import HTTP_OK, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
13 13 from request import wsgirequest
14 14 import webcommands, protocol, webutil
15 15
16 16 perms = {
17 17 'changegroup': 'pull',
18 18 'changegroupsubset': 'pull',
19 19 'stream_out': 'pull',
20 20 'listkeys': 'pull',
21 21 'unbundle': 'push',
22 22 'pushkey': 'push',
23 23 }
24 24
25 25 class hgweb(object):
26 26 def __init__(self, repo, name=None, baseui=None):
27 27 if isinstance(repo, str):
28 28 if baseui:
29 29 u = baseui.copy()
30 30 else:
31 31 u = ui.ui()
32 32 self.repo = hg.repository(u, repo)
33 33 else:
34 34 self.repo = repo
35 35
36 36 self.repo.ui.setconfig('ui', 'report_untrusted', 'off')
37 37 self.repo.ui.setconfig('ui', 'interactive', 'off')
38 38 hook.redirect(True)
39 39 self.mtime = -1
40 40 self.reponame = name
41 41 self.archives = 'zip', 'gz', 'bz2'
42 42 self.stripecount = 1
43 43 # a repo owner may set web.templates in .hg/hgrc to get any file
44 44 # readable by the user running the CGI script
45 45 self.templatepath = self.config('web', 'templates')
46 46
47 47 # The CGI scripts are often run by a user different from the repo owner.
48 48 # Trust the settings from the .hg/hgrc files by default.
49 49 def config(self, section, name, default=None, untrusted=True):
50 50 return self.repo.ui.config(section, name, default,
51 51 untrusted=untrusted)
52 52
53 53 def configbool(self, section, name, default=False, untrusted=True):
54 54 return self.repo.ui.configbool(section, name, default,
55 55 untrusted=untrusted)
56 56
57 57 def configlist(self, section, name, default=None, untrusted=True):
58 58 return self.repo.ui.configlist(section, name, default,
59 59 untrusted=untrusted)
60 60
61 61 def refresh(self, request=None):
62 62 if request:
63 63 self.repo.ui.environ = request.env
64 64 mtime = get_mtime(self.repo.spath)
65 65 if mtime != self.mtime:
66 66 self.mtime = mtime
67 67 self.repo = hg.repository(self.repo.ui, self.repo.root)
68 68 self.maxchanges = int(self.config("web", "maxchanges", 10))
69 69 self.stripecount = int(self.config("web", "stripes", 1))
70 70 self.maxshortchanges = int(self.config("web", "maxshortchanges", 60))
71 71 self.maxfiles = int(self.config("web", "maxfiles", 10))
72 72 self.allowpull = self.configbool("web", "allowpull", True)
73 73 encoding.encoding = self.config("web", "encoding",
74 74 encoding.encoding)
75 75
76 76 def run(self):
77 77 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
78 78 raise RuntimeError("This function is only intended to be "
79 79 "called while running as a CGI script.")
80 80 import mercurial.hgweb.wsgicgi as wsgicgi
81 81 wsgicgi.launch(self)
82 82
83 83 def __call__(self, env, respond):
84 84 req = wsgirequest(env, respond)
85 85 return self.run_wsgi(req)
86 86
87 87 def run_wsgi(self, req):
88 88
89 89 self.refresh(req)
90 90
91 91 # work with CGI variables to create coherent structure
92 92 # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME
93 93
94 94 req.url = req.env['SCRIPT_NAME']
95 95 if not req.url.endswith('/'):
96 96 req.url += '/'
97 97 if 'REPO_NAME' in req.env:
98 98 req.url += req.env['REPO_NAME'] + '/'
99 99
100 100 if 'PATH_INFO' in req.env:
101 101 parts = req.env['PATH_INFO'].strip('/').split('/')
102 102 repo_parts = req.env.get('REPO_NAME', '').split('/')
103 103 if parts[:len(repo_parts)] == repo_parts:
104 104 parts = parts[len(repo_parts):]
105 105 query = '/'.join(parts)
106 106 else:
107 107 query = req.env['QUERY_STRING'].split('&', 1)[0]
108 108 query = query.split(';', 1)[0]
109 109
110 110 # process this if it's a protocol request
111 111 # protocol bits don't need to create any URLs
112 112 # and the clients always use the old URL structure
113 113
114 114 cmd = req.form.get('cmd', [''])[0]
115 115 if cmd and cmd in protocol.__all__:
116 116 if query:
117 117 raise ErrorResponse(HTTP_NOT_FOUND)
118 118 try:
119 119 if cmd in perms:
120 120 try:
121 121 self.check_perm(req, perms[cmd])
122 122 except ErrorResponse, inst:
123 123 if cmd == 'unbundle':
124 124 req.drain()
125 125 raise
126 126 method = getattr(protocol, cmd)
127 127 return method(self.repo, req)
128 128 except ErrorResponse, inst:
129 129 req.respond(inst, protocol.HGTYPE)
130 130 if not inst.message:
131 131 return []
132 132 return '0\n%s\n' % inst.message,
133 133
134 134 # translate user-visible url structure to internal structure
135 135
136 136 args = query.split('/', 2)
137 137 if 'cmd' not in req.form and args and args[0]:
138 138
139 139 cmd = args.pop(0)
140 140 style = cmd.rfind('-')
141 141 if style != -1:
142 142 req.form['style'] = [cmd[:style]]
143 143 cmd = cmd[style + 1:]
144 144
145 145 # avoid accepting e.g. style parameter as command
146 146 if hasattr(webcommands, cmd):
147 147 req.form['cmd'] = [cmd]
148 148 else:
149 149 cmd = ''
150 150
151 151 if cmd == 'static':
152 152 req.form['file'] = ['/'.join(args)]
153 153 else:
154 154 if args and args[0]:
155 155 node = args.pop(0)
156 156 req.form['node'] = [node]
157 157 if args:
158 158 req.form['file'] = args
159 159
160 160 ua = req.env.get('HTTP_USER_AGENT', '')
161 161 if cmd == 'rev' and 'mercurial' in ua:
162 162 req.form['style'] = ['raw']
163 163
164 164 if cmd == 'archive':
165 165 fn = req.form['node'][0]
166 166 for type_, spec in self.archive_specs.iteritems():
167 167 ext = spec[2]
168 168 if fn.endswith(ext):
169 169 req.form['node'] = [fn[:-len(ext)]]
170 170 req.form['type'] = [type_]
171 171
172 172 # process the web interface request
173 173
174 174 try:
175 175 tmpl = self.templater(req)
176 176 ctype = tmpl('mimetype', encoding=encoding.encoding)
177 177 ctype = templater.stringify(ctype)
178 178
179 179 # check read permissions non-static content
180 180 if cmd != 'static':
181 181 self.check_perm(req, None)
182 182
183 183 if cmd == '':
184 184 req.form['cmd'] = [tmpl.cache['default']]
185 185 cmd = req.form['cmd'][0]
186 186
187 187 if cmd not in webcommands.__all__:
188 188 msg = 'no such method: %s' % cmd
189 189 raise ErrorResponse(HTTP_BAD_REQUEST, msg)
190 190 elif cmd == 'file' and 'raw' in req.form.get('style', []):
191 191 self.ctype = ctype
192 192 content = webcommands.rawfile(self, req, tmpl)
193 193 else:
194 194 content = getattr(webcommands, cmd)(self, req, tmpl)
195 195 req.respond(HTTP_OK, ctype)
196 196
197 197 return content
198 198
199 199 except error.LookupError, err:
200 200 req.respond(HTTP_NOT_FOUND, ctype)
201 201 msg = str(err)
202 202 if 'manifest' not in msg:
203 203 msg = 'revision not found: %s' % err.name
204 204 return tmpl('error', error=msg)
205 205 except (error.RepoError, error.RevlogError), inst:
206 206 req.respond(HTTP_SERVER_ERROR, ctype)
207 207 return tmpl('error', error=str(inst))
208 208 except ErrorResponse, inst:
209 209 req.respond(inst, ctype)
210 210 return tmpl('error', error=inst.message)
211 211
212 212 def templater(self, req):
213 213
214 214 # determine scheme, port and server name
215 215 # this is needed to create absolute urls
216 216
217 217 proto = req.env.get('wsgi.url_scheme')
218 218 if proto == 'https':
219 219 proto = 'https'
220 220 default_port = "443"
221 221 else:
222 222 proto = 'http'
223 223 default_port = "80"
224 224
225 225 port = req.env["SERVER_PORT"]
226 226 port = port != default_port and (":" + port) or ""
227 227 urlbase = '%s://%s%s' % (proto, req.env['SERVER_NAME'], port)
228 228 staticurl = self.config("web", "staticurl") or req.url + 'static/'
229 229 if not staticurl.endswith('/'):
230 230 staticurl += '/'
231 231
232 232 # some functions for the templater
233 233
234 234 def header(**map):
235 235 yield tmpl('header', encoding=encoding.encoding, **map)
236 236
237 237 def footer(**map):
238 238 yield tmpl("footer", **map)
239 239
240 240 def motd(**map):
241 241 yield self.config("web", "motd", "")
242 242
243 243 # figure out which style to use
244 244
245 245 vars = {}
246 246 styles = (
247 247 req.form.get('style', [None])[0],
248 248 self.config('web', 'style'),
249 249 'paper',
250 250 )
251 251 style, mapfile = templater.stylemap(styles, self.templatepath)
252 252 if style == styles[0]:
253 253 vars['style'] = style
254 254
255 255 start = req.url[-1] == '?' and '&' or '?'
256 256 sessionvars = webutil.sessionvars(vars, start)
257 257
258 258 if not self.reponame:
259 259 self.reponame = (self.config("web", "name")
260 260 or req.env.get('REPO_NAME')
261 261 or req.url.strip('/') or self.repo.root)
262 262
263 263 # create the templater
264 264
265 265 tmpl = templater.templater(mapfile,
266 266 defaults={"url": req.url,
267 267 "staticurl": staticurl,
268 268 "urlbase": urlbase,
269 269 "repo": self.reponame,
270 270 "header": header,
271 271 "footer": footer,
272 272 "motd": motd,
273 273 "sessionvars": sessionvars
274 274 })
275 275 return tmpl
276 276
277 277 def archivelist(self, nodeid):
278 278 allowed = self.configlist("web", "allow_archive")
279 279 for i, spec in self.archive_specs.iteritems():
280 280 if i in allowed or self.configbool("web", "allow" + i):
281 281 yield {"type" : i, "extension" : spec[2], "node" : nodeid}
282 282
283 283 archive_specs = {
284 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
285 'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
284 'bz2': ('application/x-bzip2', 'tbz2', '.tar.bz2', None),
285 'gz': ('application/x-gzip', 'tgz', '.tar.gz', None),
286 286 'zip': ('application/zip', 'zip', '.zip', None),
287 287 }
288 288
289 289 def check_perm(self, req, op):
290 290 for hook in permhooks:
291 291 hook(self, req, op)
General Comments 0
You need to be logged in to leave comments. Login now