Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/hgweb/webcommands.py @ 5591:08887121a652
split out hgweb commands into a separate file, move some code around
author | Dirkjan Ochtman <dirkjan@ochtman.nl> |
---|---|
date | Sun, 02 Dec 2007 23:26:40 +0100 |
parents | |
children | b95b2525c6e8 |
comparison
equal
deleted
inserted
replaced
5590:05451f6b5f07 | 5591:08887121a652 |
---|---|
1 # | |
2 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net> | |
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com> | |
4 # | |
5 # This software may be used and distributed according to the terms | |
6 # of the GNU General Public License, incorporated herein by reference. | |
7 | |
8 import cStringIO, zlib, tempfile, errno, os, sys | |
9 from mercurial import revlog, util, streamclone | |
10 from mercurial.i18n import gettext as _ | |
11 from mercurial.node import * | |
12 from common import staticfile | |
13 | |
14 def log(web, req): | |
15 if req.form.has_key('file') and req.form['file'][0]: | |
16 filelog(web, req) | |
17 else: | |
18 changelog(web, req) | |
19 | |
20 def file(web, req): | |
21 path = web.cleanpath(req.form.get('file', [''])[0]) | |
22 if path: | |
23 try: | |
24 req.write(web.filerevision(web.filectx(req))) | |
25 return | |
26 except revlog.LookupError: | |
27 pass | |
28 | |
29 req.write(web.manifest(web.changectx(req), path)) | |
30 | |
31 def changelog(web, req, shortlog = False): | |
32 if req.form.has_key('node'): | |
33 ctx = web.changectx(req) | |
34 else: | |
35 if req.form.has_key('rev'): | |
36 hi = req.form['rev'][0] | |
37 else: | |
38 hi = web.repo.changelog.count() - 1 | |
39 try: | |
40 ctx = web.repo.changectx(hi) | |
41 except hg.RepoError: | |
42 req.write(web.search(hi)) # XXX redirect to 404 page? | |
43 return | |
44 | |
45 req.write(web.changelog(ctx, shortlog = shortlog)) | |
46 | |
47 def shortlog(web, req): | |
48 changelog(web, req, shortlog = True) | |
49 | |
50 def changeset(web, req): | |
51 req.write(web.changeset(web.changectx(req))) | |
52 | |
53 rev = changeset | |
54 | |
55 def manifest(web, req): | |
56 req.write(web.manifest(web.changectx(req), | |
57 web.cleanpath(req.form['path'][0]))) | |
58 | |
59 def tags(web, req): | |
60 req.write(web.tags()) | |
61 | |
62 def summary(web, req): | |
63 req.write(web.summary()) | |
64 | |
65 def filediff(web, req): | |
66 req.write(web.filediff(web.filectx(req))) | |
67 | |
68 diff = filediff | |
69 | |
70 def annotate(web, req): | |
71 req.write(web.fileannotate(web.filectx(req))) | |
72 | |
73 def filelog(web, req): | |
74 req.write(web.filelog(web.filectx(req))) | |
75 | |
76 def lookup(web, req): | |
77 try: | |
78 r = hex(web.repo.lookup(req.form['key'][0])) | |
79 success = 1 | |
80 except Exception,inst: | |
81 r = str(inst) | |
82 success = 0 | |
83 resp = "%s %s\n" % (success, r) | |
84 req.httphdr("application/mercurial-0.1", length=len(resp)) | |
85 req.write(resp) | |
86 | |
87 def heads(web, req): | |
88 resp = " ".join(map(hex, web.repo.heads())) + "\n" | |
89 req.httphdr("application/mercurial-0.1", length=len(resp)) | |
90 req.write(resp) | |
91 | |
92 def branches(web, req): | |
93 nodes = [] | |
94 if req.form.has_key('nodes'): | |
95 nodes = map(bin, req.form['nodes'][0].split(" ")) | |
96 resp = cStringIO.StringIO() | |
97 for b in web.repo.branches(nodes): | |
98 resp.write(" ".join(map(hex, b)) + "\n") | |
99 resp = resp.getvalue() | |
100 req.httphdr("application/mercurial-0.1", length=len(resp)) | |
101 req.write(resp) | |
102 | |
103 def between(web, req): | |
104 if req.form.has_key('pairs'): | |
105 pairs = [map(bin, p.split("-")) | |
106 for p in req.form['pairs'][0].split(" ")] | |
107 resp = cStringIO.StringIO() | |
108 for b in web.repo.between(pairs): | |
109 resp.write(" ".join(map(hex, b)) + "\n") | |
110 resp = resp.getvalue() | |
111 req.httphdr("application/mercurial-0.1", length=len(resp)) | |
112 req.write(resp) | |
113 | |
114 def changegroup(web, req): | |
115 req.httphdr("application/mercurial-0.1") | |
116 nodes = [] | |
117 if not web.allowpull: | |
118 return | |
119 | |
120 if req.form.has_key('roots'): | |
121 nodes = map(bin, req.form['roots'][0].split(" ")) | |
122 | |
123 z = zlib.compressobj() | |
124 f = web.repo.changegroup(nodes, 'serve') | |
125 while 1: | |
126 chunk = f.read(4096) | |
127 if not chunk: | |
128 break | |
129 req.write(z.compress(chunk)) | |
130 | |
131 req.write(z.flush()) | |
132 | |
133 def changegroupsubset(web, req): | |
134 req.httphdr("application/mercurial-0.1") | |
135 bases = [] | |
136 heads = [] | |
137 if not web.allowpull: | |
138 return | |
139 | |
140 if req.form.has_key('bases'): | |
141 bases = [bin(x) for x in req.form['bases'][0].split(' ')] | |
142 if req.form.has_key('heads'): | |
143 heads = [bin(x) for x in req.form['heads'][0].split(' ')] | |
144 | |
145 z = zlib.compressobj() | |
146 f = web.repo.changegroupsubset(bases, heads, 'serve') | |
147 while 1: | |
148 chunk = f.read(4096) | |
149 if not chunk: | |
150 break | |
151 req.write(z.compress(chunk)) | |
152 | |
153 req.write(z.flush()) | |
154 | |
155 def archive(web, req): | |
156 type_ = req.form['type'][0] | |
157 allowed = web.configlist("web", "allow_archive") | |
158 if (type_ in web.archives and (type_ in allowed or | |
159 web.configbool("web", "allow" + type_, False))): | |
160 web.archive(req, req.form['node'][0], type_) | |
161 return | |
162 | |
163 req.respond(400, web.t('error', | |
164 error='Unsupported archive type: %s' % type_)) | |
165 | |
166 def static(web, req): | |
167 fname = req.form['file'][0] | |
168 # a repo owner may set web.static in .hg/hgrc to get any file | |
169 # readable by the user running the CGI script | |
170 static = web.config("web", "static", | |
171 os.path.join(web.templatepath, "static"), | |
172 untrusted=False) | |
173 req.write(staticfile(static, fname, req)) | |
174 | |
175 def capabilities(web, req): | |
176 caps = ['lookup', 'changegroupsubset'] | |
177 if web.configbool('server', 'uncompressed'): | |
178 caps.append('stream=%d' % web.repo.changelog.version) | |
179 # XXX: make configurable and/or share code with do_unbundle: | |
180 unbundleversions = ['HG10GZ', 'HG10BZ', 'HG10UN'] | |
181 if unbundleversions: | |
182 caps.append('unbundle=%s' % ','.join(unbundleversions)) | |
183 resp = ' '.join(caps) | |
184 req.httphdr("application/mercurial-0.1", length=len(resp)) | |
185 req.write(resp) | |
186 | |
187 def unbundle(web, req): | |
188 def bail(response, headers={}): | |
189 length = int(req.env['CONTENT_LENGTH']) | |
190 for s in util.filechunkiter(req, limit=length): | |
191 # drain incoming bundle, else client will not see | |
192 # response when run outside cgi script | |
193 pass | |
194 req.httphdr("application/mercurial-0.1", headers=headers) | |
195 req.write('0\n') | |
196 req.write(response) | |
197 | |
198 # require ssl by default, auth info cannot be sniffed and | |
199 # replayed | |
200 ssl_req = web.configbool('web', 'push_ssl', True) | |
201 if ssl_req: | |
202 if req.env.get('wsgi.url_scheme') != 'https': | |
203 bail(_('ssl required\n')) | |
204 return | |
205 proto = 'https' | |
206 else: | |
207 proto = 'http' | |
208 | |
209 # do not allow push unless explicitly allowed | |
210 if not web.check_perm(req, 'push', False): | |
211 bail(_('push not authorized\n'), | |
212 headers={'status': '401 Unauthorized'}) | |
213 return | |
214 | |
215 their_heads = req.form['heads'][0].split(' ') | |
216 | |
217 def check_heads(): | |
218 heads = map(hex, web.repo.heads()) | |
219 return their_heads == [hex('force')] or their_heads == heads | |
220 | |
221 # fail early if possible | |
222 if not check_heads(): | |
223 bail(_('unsynced changes\n')) | |
224 return | |
225 | |
226 req.httphdr("application/mercurial-0.1") | |
227 | |
228 # do not lock repo until all changegroup data is | |
229 # streamed. save to temporary file. | |
230 | |
231 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-') | |
232 fp = os.fdopen(fd, 'wb+') | |
233 try: | |
234 length = int(req.env['CONTENT_LENGTH']) | |
235 for s in util.filechunkiter(req, limit=length): | |
236 fp.write(s) | |
237 | |
238 try: | |
239 lock = web.repo.lock() | |
240 try: | |
241 if not check_heads(): | |
242 req.write('0\n') | |
243 req.write(_('unsynced changes\n')) | |
244 return | |
245 | |
246 fp.seek(0) | |
247 header = fp.read(6) | |
248 if not header.startswith("HG"): | |
249 # old client with uncompressed bundle | |
250 def generator(f): | |
251 yield header | |
252 for chunk in f: | |
253 yield chunk | |
254 elif not header.startswith("HG10"): | |
255 req.write("0\n") | |
256 req.write(_("unknown bundle version\n")) | |
257 return | |
258 elif header == "HG10GZ": | |
259 def generator(f): | |
260 zd = zlib.decompressobj() | |
261 for chunk in f: | |
262 yield zd.decompress(chunk) | |
263 elif header == "HG10BZ": | |
264 def generator(f): | |
265 zd = bz2.BZ2Decompressor() | |
266 zd.decompress("BZ") | |
267 for chunk in f: | |
268 yield zd.decompress(chunk) | |
269 elif header == "HG10UN": | |
270 def generator(f): | |
271 for chunk in f: | |
272 yield chunk | |
273 else: | |
274 req.write("0\n") | |
275 req.write(_("unknown bundle compression type\n")) | |
276 return | |
277 gen = generator(util.filechunkiter(fp, 4096)) | |
278 | |
279 # send addchangegroup output to client | |
280 | |
281 old_stdout = sys.stdout | |
282 sys.stdout = cStringIO.StringIO() | |
283 | |
284 try: | |
285 url = 'remote:%s:%s' % (proto, | |
286 req.env.get('REMOTE_HOST', '')) | |
287 try: | |
288 ret = web.repo.addchangegroup( | |
289 util.chunkbuffer(gen), 'serve', url) | |
290 except util.Abort, inst: | |
291 sys.stdout.write("abort: %s\n" % inst) | |
292 ret = 0 | |
293 finally: | |
294 val = sys.stdout.getvalue() | |
295 sys.stdout = old_stdout | |
296 req.write('%d\n' % ret) | |
297 req.write(val) | |
298 finally: | |
299 del lock | |
300 except (OSError, IOError), inst: | |
301 req.write('0\n') | |
302 filename = getattr(inst, 'filename', '') | |
303 # Don't send our filesystem layout to the client | |
304 if filename.startswith(web.repo.root): | |
305 filename = filename[len(web.repo.root)+1:] | |
306 else: | |
307 filename = '' | |
308 error = getattr(inst, 'strerror', 'Unknown error') | |
309 if inst.errno == errno.ENOENT: | |
310 code = 404 | |
311 else: | |
312 code = 500 | |
313 req.respond(code, '%s: %s\n' % (error, filename)) | |
314 finally: | |
315 fp.close() | |
316 os.unlink(tempname) | |
317 | |
318 def stream_out(web, req): | |
319 req.httphdr("application/mercurial-0.1") | |
320 streamclone.stream_out(web.repo, req, untrusted=True) |