##// END OF EJS Templates
Use wsgi.url_scheme instead of ad-hoc CGI checks....
Wesley J. Landaker -
r4872:419a6f71 default
parent child Browse files
Show More
@@ -1,1187 +1,1188
1 # hgweb/hgweb_mod.py - Web interface for a repository.
1 # hgweb/hgweb_mod.py - Web interface for a repository.
2 #
2 #
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 #
5 #
6 # This software may be used and distributed according to the terms
6 # This software may be used and distributed according to the terms
7 # of the GNU General Public License, incorporated herein by reference.
7 # of the GNU General Public License, incorporated herein by reference.
8
8
9 import os, mimetypes, re, zlib, mimetools, cStringIO, sys
9 import os, mimetypes, re, zlib, mimetools, cStringIO, sys
10 import tempfile, urllib, bz2
10 import tempfile, urllib, bz2
11 from mercurial.node import *
11 from mercurial.node import *
12 from mercurial.i18n import gettext as _
12 from mercurial.i18n import gettext as _
13 from mercurial import mdiff, ui, hg, util, archival, streamclone, patch
13 from mercurial import mdiff, ui, hg, util, archival, streamclone, patch
14 from mercurial import revlog, templater
14 from mercurial import revlog, templater
15 from common import get_mtime, staticfile, style_map, paritygen
15 from common import get_mtime, staticfile, style_map, paritygen
16
16
17 def _up(p):
17 def _up(p):
18 if p[0] != "/":
18 if p[0] != "/":
19 p = "/" + p
19 p = "/" + p
20 if p[-1] == "/":
20 if p[-1] == "/":
21 p = p[:-1]
21 p = p[:-1]
22 up = os.path.dirname(p)
22 up = os.path.dirname(p)
23 if up == "/":
23 if up == "/":
24 return "/"
24 return "/"
25 return up + "/"
25 return up + "/"
26
26
27 def revnavgen(pos, pagelen, limit, nodefunc):
27 def revnavgen(pos, pagelen, limit, nodefunc):
28 def seq(factor, limit=None):
28 def seq(factor, limit=None):
29 if limit:
29 if limit:
30 yield limit
30 yield limit
31 if limit >= 20 and limit <= 40:
31 if limit >= 20 and limit <= 40:
32 yield 50
32 yield 50
33 else:
33 else:
34 yield 1 * factor
34 yield 1 * factor
35 yield 3 * factor
35 yield 3 * factor
36 for f in seq(factor * 10):
36 for f in seq(factor * 10):
37 yield f
37 yield f
38
38
39 def nav(**map):
39 def nav(**map):
40 l = []
40 l = []
41 last = 0
41 last = 0
42 for f in seq(1, pagelen):
42 for f in seq(1, pagelen):
43 if f < pagelen or f <= last:
43 if f < pagelen or f <= last:
44 continue
44 continue
45 if f > limit:
45 if f > limit:
46 break
46 break
47 last = f
47 last = f
48 if pos + f < limit:
48 if pos + f < limit:
49 l.append(("+%d" % f, hex(nodefunc(pos + f).node())))
49 l.append(("+%d" % f, hex(nodefunc(pos + f).node())))
50 if pos - f >= 0:
50 if pos - f >= 0:
51 l.insert(0, ("-%d" % f, hex(nodefunc(pos - f).node())))
51 l.insert(0, ("-%d" % f, hex(nodefunc(pos - f).node())))
52
52
53 try:
53 try:
54 yield {"label": "(0)", "node": hex(nodefunc('0').node())}
54 yield {"label": "(0)", "node": hex(nodefunc('0').node())}
55
55
56 for label, node in l:
56 for label, node in l:
57 yield {"label": label, "node": node}
57 yield {"label": label, "node": node}
58
58
59 yield {"label": "tip", "node": "tip"}
59 yield {"label": "tip", "node": "tip"}
60 except hg.RepoError:
60 except hg.RepoError:
61 pass
61 pass
62
62
63 return nav
63 return nav
64
64
65 class hgweb(object):
65 class hgweb(object):
66 def __init__(self, repo, name=None):
66 def __init__(self, repo, name=None):
67 if type(repo) == type(""):
67 if type(repo) == type(""):
68 self.repo = hg.repository(ui.ui(report_untrusted=False), repo)
68 self.repo = hg.repository(ui.ui(report_untrusted=False), repo)
69 else:
69 else:
70 self.repo = repo
70 self.repo = repo
71
71
72 self.mtime = -1
72 self.mtime = -1
73 self.reponame = name
73 self.reponame = name
74 self.archives = 'zip', 'gz', 'bz2'
74 self.archives = 'zip', 'gz', 'bz2'
75 self.stripecount = 1
75 self.stripecount = 1
76 # a repo owner may set web.templates in .hg/hgrc to get any file
76 # a repo owner may set web.templates in .hg/hgrc to get any file
77 # readable by the user running the CGI script
77 # readable by the user running the CGI script
78 self.templatepath = self.config("web", "templates",
78 self.templatepath = self.config("web", "templates",
79 templater.templatepath(),
79 templater.templatepath(),
80 untrusted=False)
80 untrusted=False)
81
81
82 # The CGI scripts are often run by a user different from the repo owner.
82 # The CGI scripts are often run by a user different from the repo owner.
83 # Trust the settings from the .hg/hgrc files by default.
83 # Trust the settings from the .hg/hgrc files by default.
84 def config(self, section, name, default=None, untrusted=True):
84 def config(self, section, name, default=None, untrusted=True):
85 return self.repo.ui.config(section, name, default,
85 return self.repo.ui.config(section, name, default,
86 untrusted=untrusted)
86 untrusted=untrusted)
87
87
88 def configbool(self, section, name, default=False, untrusted=True):
88 def configbool(self, section, name, default=False, untrusted=True):
89 return self.repo.ui.configbool(section, name, default,
89 return self.repo.ui.configbool(section, name, default,
90 untrusted=untrusted)
90 untrusted=untrusted)
91
91
92 def configlist(self, section, name, default=None, untrusted=True):
92 def configlist(self, section, name, default=None, untrusted=True):
93 return self.repo.ui.configlist(section, name, default,
93 return self.repo.ui.configlist(section, name, default,
94 untrusted=untrusted)
94 untrusted=untrusted)
95
95
96 def refresh(self):
96 def refresh(self):
97 mtime = get_mtime(self.repo.root)
97 mtime = get_mtime(self.repo.root)
98 if mtime != self.mtime:
98 if mtime != self.mtime:
99 self.mtime = mtime
99 self.mtime = mtime
100 self.repo = hg.repository(self.repo.ui, self.repo.root)
100 self.repo = hg.repository(self.repo.ui, self.repo.root)
101 self.maxchanges = int(self.config("web", "maxchanges", 10))
101 self.maxchanges = int(self.config("web", "maxchanges", 10))
102 self.stripecount = int(self.config("web", "stripes", 1))
102 self.stripecount = int(self.config("web", "stripes", 1))
103 self.maxshortchanges = int(self.config("web", "maxshortchanges", 60))
103 self.maxshortchanges = int(self.config("web", "maxshortchanges", 60))
104 self.maxfiles = int(self.config("web", "maxfiles", 10))
104 self.maxfiles = int(self.config("web", "maxfiles", 10))
105 self.allowpull = self.configbool("web", "allowpull", True)
105 self.allowpull = self.configbool("web", "allowpull", True)
106 self.encoding = self.config("web", "encoding", util._encoding)
106 self.encoding = self.config("web", "encoding", util._encoding)
107
107
108 def archivelist(self, nodeid):
108 def archivelist(self, nodeid):
109 allowed = self.configlist("web", "allow_archive")
109 allowed = self.configlist("web", "allow_archive")
110 for i, spec in self.archive_specs.iteritems():
110 for i, spec in self.archive_specs.iteritems():
111 if i in allowed or self.configbool("web", "allow" + i):
111 if i in allowed or self.configbool("web", "allow" + i):
112 yield {"type" : i, "extension" : spec[2], "node" : nodeid}
112 yield {"type" : i, "extension" : spec[2], "node" : nodeid}
113
113
114 def listfilediffs(self, files, changeset):
114 def listfilediffs(self, files, changeset):
115 for f in files[:self.maxfiles]:
115 for f in files[:self.maxfiles]:
116 yield self.t("filedifflink", node=hex(changeset), file=f)
116 yield self.t("filedifflink", node=hex(changeset), file=f)
117 if len(files) > self.maxfiles:
117 if len(files) > self.maxfiles:
118 yield self.t("fileellipses")
118 yield self.t("fileellipses")
119
119
120 def siblings(self, siblings=[], hiderev=None, **args):
120 def siblings(self, siblings=[], hiderev=None, **args):
121 siblings = [s for s in siblings if s.node() != nullid]
121 siblings = [s for s in siblings if s.node() != nullid]
122 if len(siblings) == 1 and siblings[0].rev() == hiderev:
122 if len(siblings) == 1 and siblings[0].rev() == hiderev:
123 return
123 return
124 for s in siblings:
124 for s in siblings:
125 d = {'node': hex(s.node()), 'rev': s.rev()}
125 d = {'node': hex(s.node()), 'rev': s.rev()}
126 if hasattr(s, 'path'):
126 if hasattr(s, 'path'):
127 d['file'] = s.path()
127 d['file'] = s.path()
128 d.update(args)
128 d.update(args)
129 yield d
129 yield d
130
130
131 def renamelink(self, fl, node):
131 def renamelink(self, fl, node):
132 r = fl.renamed(node)
132 r = fl.renamed(node)
133 if r:
133 if r:
134 return [dict(file=r[0], node=hex(r[1]))]
134 return [dict(file=r[0], node=hex(r[1]))]
135 return []
135 return []
136
136
137 def nodetagsdict(self, node):
137 def nodetagsdict(self, node):
138 return [{"name": i} for i in self.repo.nodetags(node)]
138 return [{"name": i} for i in self.repo.nodetags(node)]
139
139
140 def nodebranchdict(self, ctx):
140 def nodebranchdict(self, ctx):
141 branches = []
141 branches = []
142 branch = ctx.branch()
142 branch = ctx.branch()
143 if self.repo.branchtags()[branch] == ctx.node():
143 if self.repo.branchtags()[branch] == ctx.node():
144 branches.append({"name": branch})
144 branches.append({"name": branch})
145 return branches
145 return branches
146
146
147 def showtag(self, t1, node=nullid, **args):
147 def showtag(self, t1, node=nullid, **args):
148 for t in self.repo.nodetags(node):
148 for t in self.repo.nodetags(node):
149 yield self.t(t1, tag=t, **args)
149 yield self.t(t1, tag=t, **args)
150
150
151 def diff(self, node1, node2, files):
151 def diff(self, node1, node2, files):
152 def filterfiles(filters, files):
152 def filterfiles(filters, files):
153 l = [x for x in files if x in filters]
153 l = [x for x in files if x in filters]
154
154
155 for t in filters:
155 for t in filters:
156 if t and t[-1] != os.sep:
156 if t and t[-1] != os.sep:
157 t += os.sep
157 t += os.sep
158 l += [x for x in files if x.startswith(t)]
158 l += [x for x in files if x.startswith(t)]
159 return l
159 return l
160
160
161 parity = paritygen(self.stripecount)
161 parity = paritygen(self.stripecount)
162 def diffblock(diff, f, fn):
162 def diffblock(diff, f, fn):
163 yield self.t("diffblock",
163 yield self.t("diffblock",
164 lines=prettyprintlines(diff),
164 lines=prettyprintlines(diff),
165 parity=parity.next(),
165 parity=parity.next(),
166 file=f,
166 file=f,
167 filenode=hex(fn or nullid))
167 filenode=hex(fn or nullid))
168
168
169 def prettyprintlines(diff):
169 def prettyprintlines(diff):
170 for l in diff.splitlines(1):
170 for l in diff.splitlines(1):
171 if l.startswith('+'):
171 if l.startswith('+'):
172 yield self.t("difflineplus", line=l)
172 yield self.t("difflineplus", line=l)
173 elif l.startswith('-'):
173 elif l.startswith('-'):
174 yield self.t("difflineminus", line=l)
174 yield self.t("difflineminus", line=l)
175 elif l.startswith('@'):
175 elif l.startswith('@'):
176 yield self.t("difflineat", line=l)
176 yield self.t("difflineat", line=l)
177 else:
177 else:
178 yield self.t("diffline", line=l)
178 yield self.t("diffline", line=l)
179
179
180 r = self.repo
180 r = self.repo
181 c1 = r.changectx(node1)
181 c1 = r.changectx(node1)
182 c2 = r.changectx(node2)
182 c2 = r.changectx(node2)
183 date1 = util.datestr(c1.date())
183 date1 = util.datestr(c1.date())
184 date2 = util.datestr(c2.date())
184 date2 = util.datestr(c2.date())
185
185
186 modified, added, removed, deleted, unknown = r.status(node1, node2)[:5]
186 modified, added, removed, deleted, unknown = r.status(node1, node2)[:5]
187 if files:
187 if files:
188 modified, added, removed = map(lambda x: filterfiles(files, x),
188 modified, added, removed = map(lambda x: filterfiles(files, x),
189 (modified, added, removed))
189 (modified, added, removed))
190
190
191 diffopts = patch.diffopts(self.repo.ui, untrusted=True)
191 diffopts = patch.diffopts(self.repo.ui, untrusted=True)
192 for f in modified:
192 for f in modified:
193 to = c1.filectx(f).data()
193 to = c1.filectx(f).data()
194 tn = c2.filectx(f).data()
194 tn = c2.filectx(f).data()
195 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
195 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
196 opts=diffopts), f, tn)
196 opts=diffopts), f, tn)
197 for f in added:
197 for f in added:
198 to = None
198 to = None
199 tn = c2.filectx(f).data()
199 tn = c2.filectx(f).data()
200 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
200 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
201 opts=diffopts), f, tn)
201 opts=diffopts), f, tn)
202 for f in removed:
202 for f in removed:
203 to = c1.filectx(f).data()
203 to = c1.filectx(f).data()
204 tn = None
204 tn = None
205 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
205 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
206 opts=diffopts), f, tn)
206 opts=diffopts), f, tn)
207
207
208 def changelog(self, ctx, shortlog=False):
208 def changelog(self, ctx, shortlog=False):
209 def changelist(**map):
209 def changelist(**map):
210 cl = self.repo.changelog
210 cl = self.repo.changelog
211 l = [] # build a list in forward order for efficiency
211 l = [] # build a list in forward order for efficiency
212 for i in xrange(start, end):
212 for i in xrange(start, end):
213 ctx = self.repo.changectx(i)
213 ctx = self.repo.changectx(i)
214 n = ctx.node()
214 n = ctx.node()
215
215
216 l.insert(0, {"parity": parity.next(),
216 l.insert(0, {"parity": parity.next(),
217 "author": ctx.user(),
217 "author": ctx.user(),
218 "parent": self.siblings(ctx.parents(), i - 1),
218 "parent": self.siblings(ctx.parents(), i - 1),
219 "child": self.siblings(ctx.children(), i + 1),
219 "child": self.siblings(ctx.children(), i + 1),
220 "changelogtag": self.showtag("changelogtag",n),
220 "changelogtag": self.showtag("changelogtag",n),
221 "desc": ctx.description(),
221 "desc": ctx.description(),
222 "date": ctx.date(),
222 "date": ctx.date(),
223 "files": self.listfilediffs(ctx.files(), n),
223 "files": self.listfilediffs(ctx.files(), n),
224 "rev": i,
224 "rev": i,
225 "node": hex(n),
225 "node": hex(n),
226 "tags": self.nodetagsdict(n),
226 "tags": self.nodetagsdict(n),
227 "branches": self.nodebranchdict(ctx)})
227 "branches": self.nodebranchdict(ctx)})
228
228
229 for e in l:
229 for e in l:
230 yield e
230 yield e
231
231
232 maxchanges = shortlog and self.maxshortchanges or self.maxchanges
232 maxchanges = shortlog and self.maxshortchanges or self.maxchanges
233 cl = self.repo.changelog
233 cl = self.repo.changelog
234 count = cl.count()
234 count = cl.count()
235 pos = ctx.rev()
235 pos = ctx.rev()
236 start = max(0, pos - maxchanges + 1)
236 start = max(0, pos - maxchanges + 1)
237 end = min(count, start + maxchanges)
237 end = min(count, start + maxchanges)
238 pos = end - 1
238 pos = end - 1
239 parity = paritygen(self.stripecount, offset=start-end)
239 parity = paritygen(self.stripecount, offset=start-end)
240
240
241 changenav = revnavgen(pos, maxchanges, count, self.repo.changectx)
241 changenav = revnavgen(pos, maxchanges, count, self.repo.changectx)
242
242
243 yield self.t(shortlog and 'shortlog' or 'changelog',
243 yield self.t(shortlog and 'shortlog' or 'changelog',
244 changenav=changenav,
244 changenav=changenav,
245 node=hex(cl.tip()),
245 node=hex(cl.tip()),
246 rev=pos, changesets=count, entries=changelist,
246 rev=pos, changesets=count, entries=changelist,
247 archives=self.archivelist("tip"))
247 archives=self.archivelist("tip"))
248
248
249 def search(self, query):
249 def search(self, query):
250
250
251 def changelist(**map):
251 def changelist(**map):
252 cl = self.repo.changelog
252 cl = self.repo.changelog
253 count = 0
253 count = 0
254 qw = query.lower().split()
254 qw = query.lower().split()
255
255
256 def revgen():
256 def revgen():
257 for i in xrange(cl.count() - 1, 0, -100):
257 for i in xrange(cl.count() - 1, 0, -100):
258 l = []
258 l = []
259 for j in xrange(max(0, i - 100), i):
259 for j in xrange(max(0, i - 100), i):
260 ctx = self.repo.changectx(j)
260 ctx = self.repo.changectx(j)
261 l.append(ctx)
261 l.append(ctx)
262 l.reverse()
262 l.reverse()
263 for e in l:
263 for e in l:
264 yield e
264 yield e
265
265
266 for ctx in revgen():
266 for ctx in revgen():
267 miss = 0
267 miss = 0
268 for q in qw:
268 for q in qw:
269 if not (q in ctx.user().lower() or
269 if not (q in ctx.user().lower() or
270 q in ctx.description().lower() or
270 q in ctx.description().lower() or
271 q in " ".join(ctx.files()).lower()):
271 q in " ".join(ctx.files()).lower()):
272 miss = 1
272 miss = 1
273 break
273 break
274 if miss:
274 if miss:
275 continue
275 continue
276
276
277 count += 1
277 count += 1
278 n = ctx.node()
278 n = ctx.node()
279
279
280 yield self.t('searchentry',
280 yield self.t('searchentry',
281 parity=parity.next(),
281 parity=parity.next(),
282 author=ctx.user(),
282 author=ctx.user(),
283 parent=self.siblings(ctx.parents()),
283 parent=self.siblings(ctx.parents()),
284 child=self.siblings(ctx.children()),
284 child=self.siblings(ctx.children()),
285 changelogtag=self.showtag("changelogtag",n),
285 changelogtag=self.showtag("changelogtag",n),
286 desc=ctx.description(),
286 desc=ctx.description(),
287 date=ctx.date(),
287 date=ctx.date(),
288 files=self.listfilediffs(ctx.files(), n),
288 files=self.listfilediffs(ctx.files(), n),
289 rev=ctx.rev(),
289 rev=ctx.rev(),
290 node=hex(n),
290 node=hex(n),
291 tags=self.nodetagsdict(n),
291 tags=self.nodetagsdict(n),
292 branches=self.nodebranchdict(ctx))
292 branches=self.nodebranchdict(ctx))
293
293
294 if count >= self.maxchanges:
294 if count >= self.maxchanges:
295 break
295 break
296
296
297 cl = self.repo.changelog
297 cl = self.repo.changelog
298 parity = paritygen(self.stripecount)
298 parity = paritygen(self.stripecount)
299
299
300 yield self.t('search',
300 yield self.t('search',
301 query=query,
301 query=query,
302 node=hex(cl.tip()),
302 node=hex(cl.tip()),
303 entries=changelist,
303 entries=changelist,
304 archives=self.archivelist("tip"))
304 archives=self.archivelist("tip"))
305
305
306 def changeset(self, ctx):
306 def changeset(self, ctx):
307 n = ctx.node()
307 n = ctx.node()
308 parents = ctx.parents()
308 parents = ctx.parents()
309 p1 = parents[0].node()
309 p1 = parents[0].node()
310
310
311 files = []
311 files = []
312 parity = paritygen(self.stripecount)
312 parity = paritygen(self.stripecount)
313 for f in ctx.files():
313 for f in ctx.files():
314 files.append(self.t("filenodelink",
314 files.append(self.t("filenodelink",
315 node=hex(n), file=f,
315 node=hex(n), file=f,
316 parity=parity.next()))
316 parity=parity.next()))
317
317
318 def diff(**map):
318 def diff(**map):
319 yield self.diff(p1, n, None)
319 yield self.diff(p1, n, None)
320
320
321 yield self.t('changeset',
321 yield self.t('changeset',
322 diff=diff,
322 diff=diff,
323 rev=ctx.rev(),
323 rev=ctx.rev(),
324 node=hex(n),
324 node=hex(n),
325 parent=self.siblings(parents),
325 parent=self.siblings(parents),
326 child=self.siblings(ctx.children()),
326 child=self.siblings(ctx.children()),
327 changesettag=self.showtag("changesettag",n),
327 changesettag=self.showtag("changesettag",n),
328 author=ctx.user(),
328 author=ctx.user(),
329 desc=ctx.description(),
329 desc=ctx.description(),
330 date=ctx.date(),
330 date=ctx.date(),
331 files=files,
331 files=files,
332 archives=self.archivelist(hex(n)),
332 archives=self.archivelist(hex(n)),
333 tags=self.nodetagsdict(n),
333 tags=self.nodetagsdict(n),
334 branches=self.nodebranchdict(ctx))
334 branches=self.nodebranchdict(ctx))
335
335
336 def filelog(self, fctx):
336 def filelog(self, fctx):
337 f = fctx.path()
337 f = fctx.path()
338 fl = fctx.filelog()
338 fl = fctx.filelog()
339 count = fl.count()
339 count = fl.count()
340 pagelen = self.maxshortchanges
340 pagelen = self.maxshortchanges
341 pos = fctx.filerev()
341 pos = fctx.filerev()
342 start = max(0, pos - pagelen + 1)
342 start = max(0, pos - pagelen + 1)
343 end = min(count, start + pagelen)
343 end = min(count, start + pagelen)
344 pos = end - 1
344 pos = end - 1
345 parity = paritygen(self.stripecount, offset=start-end)
345 parity = paritygen(self.stripecount, offset=start-end)
346
346
347 def entries(**map):
347 def entries(**map):
348 l = []
348 l = []
349
349
350 for i in xrange(start, end):
350 for i in xrange(start, end):
351 ctx = fctx.filectx(i)
351 ctx = fctx.filectx(i)
352 n = fl.node(i)
352 n = fl.node(i)
353
353
354 l.insert(0, {"parity": parity.next(),
354 l.insert(0, {"parity": parity.next(),
355 "filerev": i,
355 "filerev": i,
356 "file": f,
356 "file": f,
357 "node": hex(ctx.node()),
357 "node": hex(ctx.node()),
358 "author": ctx.user(),
358 "author": ctx.user(),
359 "date": ctx.date(),
359 "date": ctx.date(),
360 "rename": self.renamelink(fl, n),
360 "rename": self.renamelink(fl, n),
361 "parent": self.siblings(fctx.parents()),
361 "parent": self.siblings(fctx.parents()),
362 "child": self.siblings(fctx.children()),
362 "child": self.siblings(fctx.children()),
363 "desc": ctx.description()})
363 "desc": ctx.description()})
364
364
365 for e in l:
365 for e in l:
366 yield e
366 yield e
367
367
368 nodefunc = lambda x: fctx.filectx(fileid=x)
368 nodefunc = lambda x: fctx.filectx(fileid=x)
369 nav = revnavgen(pos, pagelen, count, nodefunc)
369 nav = revnavgen(pos, pagelen, count, nodefunc)
370 yield self.t("filelog", file=f, node=hex(fctx.node()), nav=nav,
370 yield self.t("filelog", file=f, node=hex(fctx.node()), nav=nav,
371 entries=entries)
371 entries=entries)
372
372
373 def filerevision(self, fctx):
373 def filerevision(self, fctx):
374 f = fctx.path()
374 f = fctx.path()
375 text = fctx.data()
375 text = fctx.data()
376 fl = fctx.filelog()
376 fl = fctx.filelog()
377 n = fctx.filenode()
377 n = fctx.filenode()
378 parity = paritygen(self.stripecount)
378 parity = paritygen(self.stripecount)
379
379
380 mt = mimetypes.guess_type(f)[0]
380 mt = mimetypes.guess_type(f)[0]
381 rawtext = text
381 rawtext = text
382 if util.binary(text):
382 if util.binary(text):
383 mt = mt or 'application/octet-stream'
383 mt = mt or 'application/octet-stream'
384 text = "(binary:%s)" % mt
384 text = "(binary:%s)" % mt
385 mt = mt or 'text/plain'
385 mt = mt or 'text/plain'
386
386
387 def lines():
387 def lines():
388 for l, t in enumerate(text.splitlines(1)):
388 for l, t in enumerate(text.splitlines(1)):
389 yield {"line": t,
389 yield {"line": t,
390 "linenumber": "% 6d" % (l + 1),
390 "linenumber": "% 6d" % (l + 1),
391 "parity": parity.next()}
391 "parity": parity.next()}
392
392
393 yield self.t("filerevision",
393 yield self.t("filerevision",
394 file=f,
394 file=f,
395 path=_up(f),
395 path=_up(f),
396 text=lines(),
396 text=lines(),
397 raw=rawtext,
397 raw=rawtext,
398 mimetype=mt,
398 mimetype=mt,
399 rev=fctx.rev(),
399 rev=fctx.rev(),
400 node=hex(fctx.node()),
400 node=hex(fctx.node()),
401 author=fctx.user(),
401 author=fctx.user(),
402 date=fctx.date(),
402 date=fctx.date(),
403 desc=fctx.description(),
403 desc=fctx.description(),
404 parent=self.siblings(fctx.parents()),
404 parent=self.siblings(fctx.parents()),
405 child=self.siblings(fctx.children()),
405 child=self.siblings(fctx.children()),
406 rename=self.renamelink(fl, n),
406 rename=self.renamelink(fl, n),
407 permissions=fctx.manifest().flags(f))
407 permissions=fctx.manifest().flags(f))
408
408
409 def fileannotate(self, fctx):
409 def fileannotate(self, fctx):
410 f = fctx.path()
410 f = fctx.path()
411 n = fctx.filenode()
411 n = fctx.filenode()
412 fl = fctx.filelog()
412 fl = fctx.filelog()
413 parity = paritygen(self.stripecount)
413 parity = paritygen(self.stripecount)
414
414
415 def annotate(**map):
415 def annotate(**map):
416 last = None
416 last = None
417 for f, l in fctx.annotate(follow=True):
417 for f, l in fctx.annotate(follow=True):
418 fnode = f.filenode()
418 fnode = f.filenode()
419 name = self.repo.ui.shortuser(f.user())
419 name = self.repo.ui.shortuser(f.user())
420
420
421 if last != fnode:
421 if last != fnode:
422 last = fnode
422 last = fnode
423
423
424 yield {"parity": parity.next(),
424 yield {"parity": parity.next(),
425 "node": hex(f.node()),
425 "node": hex(f.node()),
426 "rev": f.rev(),
426 "rev": f.rev(),
427 "author": name,
427 "author": name,
428 "file": f.path(),
428 "file": f.path(),
429 "line": l}
429 "line": l}
430
430
431 yield self.t("fileannotate",
431 yield self.t("fileannotate",
432 file=f,
432 file=f,
433 annotate=annotate,
433 annotate=annotate,
434 path=_up(f),
434 path=_up(f),
435 rev=fctx.rev(),
435 rev=fctx.rev(),
436 node=hex(fctx.node()),
436 node=hex(fctx.node()),
437 author=fctx.user(),
437 author=fctx.user(),
438 date=fctx.date(),
438 date=fctx.date(),
439 desc=fctx.description(),
439 desc=fctx.description(),
440 rename=self.renamelink(fl, n),
440 rename=self.renamelink(fl, n),
441 parent=self.siblings(fctx.parents()),
441 parent=self.siblings(fctx.parents()),
442 child=self.siblings(fctx.children()),
442 child=self.siblings(fctx.children()),
443 permissions=fctx.manifest().flags(f))
443 permissions=fctx.manifest().flags(f))
444
444
445 def manifest(self, ctx, path):
445 def manifest(self, ctx, path):
446 mf = ctx.manifest()
446 mf = ctx.manifest()
447 node = ctx.node()
447 node = ctx.node()
448
448
449 files = {}
449 files = {}
450 parity = paritygen(self.stripecount)
450 parity = paritygen(self.stripecount)
451
451
452 if path and path[-1] != "/":
452 if path and path[-1] != "/":
453 path += "/"
453 path += "/"
454 l = len(path)
454 l = len(path)
455 abspath = "/" + path
455 abspath = "/" + path
456
456
457 for f, n in mf.items():
457 for f, n in mf.items():
458 if f[:l] != path:
458 if f[:l] != path:
459 continue
459 continue
460 remain = f[l:]
460 remain = f[l:]
461 if "/" in remain:
461 if "/" in remain:
462 short = remain[:remain.index("/") + 1] # bleah
462 short = remain[:remain.index("/") + 1] # bleah
463 files[short] = (f, None)
463 files[short] = (f, None)
464 else:
464 else:
465 short = os.path.basename(remain)
465 short = os.path.basename(remain)
466 files[short] = (f, n)
466 files[short] = (f, n)
467
467
468 def filelist(**map):
468 def filelist(**map):
469 fl = files.keys()
469 fl = files.keys()
470 fl.sort()
470 fl.sort()
471 for f in fl:
471 for f in fl:
472 full, fnode = files[f]
472 full, fnode = files[f]
473 if not fnode:
473 if not fnode:
474 continue
474 continue
475
475
476 yield {"file": full,
476 yield {"file": full,
477 "parity": parity.next(),
477 "parity": parity.next(),
478 "basename": f,
478 "basename": f,
479 "size": ctx.filectx(full).size(),
479 "size": ctx.filectx(full).size(),
480 "permissions": mf.flags(full)}
480 "permissions": mf.flags(full)}
481
481
482 def dirlist(**map):
482 def dirlist(**map):
483 fl = files.keys()
483 fl = files.keys()
484 fl.sort()
484 fl.sort()
485 for f in fl:
485 for f in fl:
486 full, fnode = files[f]
486 full, fnode = files[f]
487 if fnode:
487 if fnode:
488 continue
488 continue
489
489
490 yield {"parity": parity.next(),
490 yield {"parity": parity.next(),
491 "path": os.path.join(abspath, f),
491 "path": os.path.join(abspath, f),
492 "basename": f[:-1]}
492 "basename": f[:-1]}
493
493
494 yield self.t("manifest",
494 yield self.t("manifest",
495 rev=ctx.rev(),
495 rev=ctx.rev(),
496 node=hex(node),
496 node=hex(node),
497 path=abspath,
497 path=abspath,
498 up=_up(abspath),
498 up=_up(abspath),
499 upparity=parity.next(),
499 upparity=parity.next(),
500 fentries=filelist,
500 fentries=filelist,
501 dentries=dirlist,
501 dentries=dirlist,
502 archives=self.archivelist(hex(node)),
502 archives=self.archivelist(hex(node)),
503 tags=self.nodetagsdict(node),
503 tags=self.nodetagsdict(node),
504 branches=self.nodebranchdict(ctx))
504 branches=self.nodebranchdict(ctx))
505
505
506 def tags(self):
506 def tags(self):
507 i = self.repo.tagslist()
507 i = self.repo.tagslist()
508 i.reverse()
508 i.reverse()
509 parity = paritygen(self.stripecount)
509 parity = paritygen(self.stripecount)
510
510
511 def entries(notip=False, **map):
511 def entries(notip=False, **map):
512 for k, n in i:
512 for k, n in i:
513 if notip and k == "tip":
513 if notip and k == "tip":
514 continue
514 continue
515 yield {"parity": parity.next(),
515 yield {"parity": parity.next(),
516 "tag": k,
516 "tag": k,
517 "date": self.repo.changectx(n).date(),
517 "date": self.repo.changectx(n).date(),
518 "node": hex(n)}
518 "node": hex(n)}
519
519
520 yield self.t("tags",
520 yield self.t("tags",
521 node=hex(self.repo.changelog.tip()),
521 node=hex(self.repo.changelog.tip()),
522 entries=lambda **x: entries(False, **x),
522 entries=lambda **x: entries(False, **x),
523 entriesnotip=lambda **x: entries(True, **x))
523 entriesnotip=lambda **x: entries(True, **x))
524
524
525 def summary(self):
525 def summary(self):
526 i = self.repo.tagslist()
526 i = self.repo.tagslist()
527 i.reverse()
527 i.reverse()
528
528
529 def tagentries(**map):
529 def tagentries(**map):
530 parity = paritygen(self.stripecount)
530 parity = paritygen(self.stripecount)
531 count = 0
531 count = 0
532 for k, n in i:
532 for k, n in i:
533 if k == "tip": # skip tip
533 if k == "tip": # skip tip
534 continue;
534 continue;
535
535
536 count += 1
536 count += 1
537 if count > 10: # limit to 10 tags
537 if count > 10: # limit to 10 tags
538 break;
538 break;
539
539
540 yield self.t("tagentry",
540 yield self.t("tagentry",
541 parity=parity.next(),
541 parity=parity.next(),
542 tag=k,
542 tag=k,
543 node=hex(n),
543 node=hex(n),
544 date=self.repo.changectx(n).date())
544 date=self.repo.changectx(n).date())
545
545
546
546
547 def branches(**map):
547 def branches(**map):
548 parity = paritygen(self.stripecount)
548 parity = paritygen(self.stripecount)
549
549
550 b = self.repo.branchtags()
550 b = self.repo.branchtags()
551 l = [(-self.repo.changelog.rev(n), n, t) for t, n in b.items()]
551 l = [(-self.repo.changelog.rev(n), n, t) for t, n in b.items()]
552 l.sort()
552 l.sort()
553
553
554 for r,n,t in l:
554 for r,n,t in l:
555 ctx = self.repo.changectx(n)
555 ctx = self.repo.changectx(n)
556
556
557 yield {'parity': parity.next(),
557 yield {'parity': parity.next(),
558 'branch': t,
558 'branch': t,
559 'node': hex(n),
559 'node': hex(n),
560 'date': ctx.date()}
560 'date': ctx.date()}
561
561
562 def changelist(**map):
562 def changelist(**map):
563 parity = paritygen(self.stripecount, offset=start-end)
563 parity = paritygen(self.stripecount, offset=start-end)
564 l = [] # build a list in forward order for efficiency
564 l = [] # build a list in forward order for efficiency
565 for i in xrange(start, end):
565 for i in xrange(start, end):
566 ctx = self.repo.changectx(i)
566 ctx = self.repo.changectx(i)
567 n = ctx.node()
567 n = ctx.node()
568 hn = hex(n)
568 hn = hex(n)
569
569
570 l.insert(0, self.t(
570 l.insert(0, self.t(
571 'shortlogentry',
571 'shortlogentry',
572 parity=parity.next(),
572 parity=parity.next(),
573 author=ctx.user(),
573 author=ctx.user(),
574 desc=ctx.description(),
574 desc=ctx.description(),
575 date=ctx.date(),
575 date=ctx.date(),
576 rev=i,
576 rev=i,
577 node=hn,
577 node=hn,
578 tags=self.nodetagsdict(n),
578 tags=self.nodetagsdict(n),
579 branches=self.nodebranchdict(ctx)))
579 branches=self.nodebranchdict(ctx)))
580
580
581 yield l
581 yield l
582
582
583 cl = self.repo.changelog
583 cl = self.repo.changelog
584 count = cl.count()
584 count = cl.count()
585 start = max(0, count - self.maxchanges)
585 start = max(0, count - self.maxchanges)
586 end = min(count, start + self.maxchanges)
586 end = min(count, start + self.maxchanges)
587
587
588 yield self.t("summary",
588 yield self.t("summary",
589 desc=self.config("web", "description", "unknown"),
589 desc=self.config("web", "description", "unknown"),
590 owner=(self.config("ui", "username") or # preferred
590 owner=(self.config("ui", "username") or # preferred
591 self.config("web", "contact") or # deprecated
591 self.config("web", "contact") or # deprecated
592 self.config("web", "author", "unknown")), # also
592 self.config("web", "author", "unknown")), # also
593 lastchange=cl.read(cl.tip())[2],
593 lastchange=cl.read(cl.tip())[2],
594 tags=tagentries,
594 tags=tagentries,
595 branches=branches,
595 branches=branches,
596 shortlog=changelist,
596 shortlog=changelist,
597 node=hex(cl.tip()),
597 node=hex(cl.tip()),
598 archives=self.archivelist("tip"))
598 archives=self.archivelist("tip"))
599
599
600 def filediff(self, fctx):
600 def filediff(self, fctx):
601 n = fctx.node()
601 n = fctx.node()
602 path = fctx.path()
602 path = fctx.path()
603 parents = fctx.parents()
603 parents = fctx.parents()
604 p1 = parents and parents[0].node() or nullid
604 p1 = parents and parents[0].node() or nullid
605
605
606 def diff(**map):
606 def diff(**map):
607 yield self.diff(p1, n, [path])
607 yield self.diff(p1, n, [path])
608
608
609 yield self.t("filediff",
609 yield self.t("filediff",
610 file=path,
610 file=path,
611 node=hex(n),
611 node=hex(n),
612 rev=fctx.rev(),
612 rev=fctx.rev(),
613 parent=self.siblings(parents),
613 parent=self.siblings(parents),
614 child=self.siblings(fctx.children()),
614 child=self.siblings(fctx.children()),
615 diff=diff)
615 diff=diff)
616
616
617 archive_specs = {
617 archive_specs = {
618 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
618 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
619 'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
619 'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
620 'zip': ('application/zip', 'zip', '.zip', None),
620 'zip': ('application/zip', 'zip', '.zip', None),
621 }
621 }
622
622
623 def archive(self, req, key, type_):
623 def archive(self, req, key, type_):
624 reponame = re.sub(r"\W+", "-", os.path.basename(self.reponame))
624 reponame = re.sub(r"\W+", "-", os.path.basename(self.reponame))
625 cnode = self.repo.lookup(key)
625 cnode = self.repo.lookup(key)
626 arch_version = key
626 arch_version = key
627 if cnode == key or key == 'tip':
627 if cnode == key or key == 'tip':
628 arch_version = short(cnode)
628 arch_version = short(cnode)
629 name = "%s-%s" % (reponame, arch_version)
629 name = "%s-%s" % (reponame, arch_version)
630 mimetype, artype, extension, encoding = self.archive_specs[type_]
630 mimetype, artype, extension, encoding = self.archive_specs[type_]
631 headers = [('Content-type', mimetype),
631 headers = [('Content-type', mimetype),
632 ('Content-disposition', 'attachment; filename=%s%s' %
632 ('Content-disposition', 'attachment; filename=%s%s' %
633 (name, extension))]
633 (name, extension))]
634 if encoding:
634 if encoding:
635 headers.append(('Content-encoding', encoding))
635 headers.append(('Content-encoding', encoding))
636 req.header(headers)
636 req.header(headers)
637 archival.archive(self.repo, req.out, cnode, artype, prefix=name)
637 archival.archive(self.repo, req.out, cnode, artype, prefix=name)
638
638
639 # add tags to things
639 # add tags to things
640 # tags -> list of changesets corresponding to tags
640 # tags -> list of changesets corresponding to tags
641 # find tag, changeset, file
641 # find tag, changeset, file
642
642
643 def cleanpath(self, path):
643 def cleanpath(self, path):
644 path = path.lstrip('/')
644 path = path.lstrip('/')
645 return util.canonpath(self.repo.root, '', path)
645 return util.canonpath(self.repo.root, '', path)
646
646
647 def run(self):
647 def run(self):
648 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
648 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
649 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
649 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
650 import mercurial.hgweb.wsgicgi as wsgicgi
650 import mercurial.hgweb.wsgicgi as wsgicgi
651 from request import wsgiapplication
651 from request import wsgiapplication
652 def make_web_app():
652 def make_web_app():
653 return self
653 return self
654 wsgicgi.launch(wsgiapplication(make_web_app))
654 wsgicgi.launch(wsgiapplication(make_web_app))
655
655
656 def run_wsgi(self, req):
656 def run_wsgi(self, req):
657 def header(**map):
657 def header(**map):
658 header_file = cStringIO.StringIO(
658 header_file = cStringIO.StringIO(
659 ''.join(self.t("header", encoding=self.encoding, **map)))
659 ''.join(self.t("header", encoding=self.encoding, **map)))
660 msg = mimetools.Message(header_file, 0)
660 msg = mimetools.Message(header_file, 0)
661 req.header(msg.items())
661 req.header(msg.items())
662 yield header_file.read()
662 yield header_file.read()
663
663
664 def rawfileheader(**map):
664 def rawfileheader(**map):
665 req.header([('Content-type', map['mimetype']),
665 req.header([('Content-type', map['mimetype']),
666 ('Content-disposition', 'filename=%s' % map['file']),
666 ('Content-disposition', 'filename=%s' % map['file']),
667 ('Content-length', str(len(map['raw'])))])
667 ('Content-length', str(len(map['raw'])))])
668 yield ''
668 yield ''
669
669
670 def footer(**map):
670 def footer(**map):
671 yield self.t("footer", **map)
671 yield self.t("footer", **map)
672
672
673 def motd(**map):
673 def motd(**map):
674 yield self.config("web", "motd", "")
674 yield self.config("web", "motd", "")
675
675
676 def expand_form(form):
676 def expand_form(form):
677 shortcuts = {
677 shortcuts = {
678 'cl': [('cmd', ['changelog']), ('rev', None)],
678 'cl': [('cmd', ['changelog']), ('rev', None)],
679 'sl': [('cmd', ['shortlog']), ('rev', None)],
679 'sl': [('cmd', ['shortlog']), ('rev', None)],
680 'cs': [('cmd', ['changeset']), ('node', None)],
680 'cs': [('cmd', ['changeset']), ('node', None)],
681 'f': [('cmd', ['file']), ('filenode', None)],
681 'f': [('cmd', ['file']), ('filenode', None)],
682 'fl': [('cmd', ['filelog']), ('filenode', None)],
682 'fl': [('cmd', ['filelog']), ('filenode', None)],
683 'fd': [('cmd', ['filediff']), ('node', None)],
683 'fd': [('cmd', ['filediff']), ('node', None)],
684 'fa': [('cmd', ['annotate']), ('filenode', None)],
684 'fa': [('cmd', ['annotate']), ('filenode', None)],
685 'mf': [('cmd', ['manifest']), ('manifest', None)],
685 'mf': [('cmd', ['manifest']), ('manifest', None)],
686 'ca': [('cmd', ['archive']), ('node', None)],
686 'ca': [('cmd', ['archive']), ('node', None)],
687 'tags': [('cmd', ['tags'])],
687 'tags': [('cmd', ['tags'])],
688 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
688 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
689 'static': [('cmd', ['static']), ('file', None)]
689 'static': [('cmd', ['static']), ('file', None)]
690 }
690 }
691
691
692 for k in shortcuts.iterkeys():
692 for k in shortcuts.iterkeys():
693 if form.has_key(k):
693 if form.has_key(k):
694 for name, value in shortcuts[k]:
694 for name, value in shortcuts[k]:
695 if value is None:
695 if value is None:
696 value = form[k]
696 value = form[k]
697 form[name] = value
697 form[name] = value
698 del form[k]
698 del form[k]
699
699
700 def rewrite_request(req):
700 def rewrite_request(req):
701 '''translate new web interface to traditional format'''
701 '''translate new web interface to traditional format'''
702
702
703 def spliturl(req):
703 def spliturl(req):
704 def firstitem(query):
704 def firstitem(query):
705 return query.split('&', 1)[0].split(';', 1)[0]
705 return query.split('&', 1)[0].split(';', 1)[0]
706
706
707 def normurl(url):
707 def normurl(url):
708 inner = '/'.join([x for x in url.split('/') if x])
708 inner = '/'.join([x for x in url.split('/') if x])
709 tl = len(url) > 1 and url.endswith('/') and '/' or ''
709 tl = len(url) > 1 and url.endswith('/') and '/' or ''
710
710
711 return '%s%s%s' % (url.startswith('/') and '/' or '',
711 return '%s%s%s' % (url.startswith('/') and '/' or '',
712 inner, tl)
712 inner, tl)
713
713
714 root = normurl(urllib.unquote(req.env.get('REQUEST_URI', '').split('?', 1)[0]))
714 root = normurl(urllib.unquote(req.env.get('REQUEST_URI', '').split('?', 1)[0]))
715 pi = normurl(req.env.get('PATH_INFO', ''))
715 pi = normurl(req.env.get('PATH_INFO', ''))
716 if pi:
716 if pi:
717 # strip leading /
717 # strip leading /
718 pi = pi[1:]
718 pi = pi[1:]
719 if pi:
719 if pi:
720 root = root[:root.rfind(pi)]
720 root = root[:root.rfind(pi)]
721 if req.env.has_key('REPO_NAME'):
721 if req.env.has_key('REPO_NAME'):
722 rn = req.env['REPO_NAME'] + '/'
722 rn = req.env['REPO_NAME'] + '/'
723 root += rn
723 root += rn
724 query = pi[len(rn):]
724 query = pi[len(rn):]
725 else:
725 else:
726 query = pi
726 query = pi
727 else:
727 else:
728 root += '?'
728 root += '?'
729 query = firstitem(req.env['QUERY_STRING'])
729 query = firstitem(req.env['QUERY_STRING'])
730
730
731 return (root, query)
731 return (root, query)
732
732
733 req.url, query = spliturl(req)
733 req.url, query = spliturl(req)
734
734
735 if req.form.has_key('cmd'):
735 if req.form.has_key('cmd'):
736 # old style
736 # old style
737 return
737 return
738
738
739 args = query.split('/', 2)
739 args = query.split('/', 2)
740 if not args or not args[0]:
740 if not args or not args[0]:
741 return
741 return
742
742
743 cmd = args.pop(0)
743 cmd = args.pop(0)
744 style = cmd.rfind('-')
744 style = cmd.rfind('-')
745 if style != -1:
745 if style != -1:
746 req.form['style'] = [cmd[:style]]
746 req.form['style'] = [cmd[:style]]
747 cmd = cmd[style+1:]
747 cmd = cmd[style+1:]
748 # avoid accepting e.g. style parameter as command
748 # avoid accepting e.g. style parameter as command
749 if hasattr(self, 'do_' + cmd):
749 if hasattr(self, 'do_' + cmd):
750 req.form['cmd'] = [cmd]
750 req.form['cmd'] = [cmd]
751
751
752 if args and args[0]:
752 if args and args[0]:
753 node = args.pop(0)
753 node = args.pop(0)
754 req.form['node'] = [node]
754 req.form['node'] = [node]
755 if args:
755 if args:
756 req.form['file'] = args
756 req.form['file'] = args
757
757
758 if cmd == 'static':
758 if cmd == 'static':
759 req.form['file'] = req.form['node']
759 req.form['file'] = req.form['node']
760 elif cmd == 'archive':
760 elif cmd == 'archive':
761 fn = req.form['node'][0]
761 fn = req.form['node'][0]
762 for type_, spec in self.archive_specs.iteritems():
762 for type_, spec in self.archive_specs.iteritems():
763 ext = spec[2]
763 ext = spec[2]
764 if fn.endswith(ext):
764 if fn.endswith(ext):
765 req.form['node'] = [fn[:-len(ext)]]
765 req.form['node'] = [fn[:-len(ext)]]
766 req.form['type'] = [type_]
766 req.form['type'] = [type_]
767
767
768 def sessionvars(**map):
768 def sessionvars(**map):
769 fields = []
769 fields = []
770 if req.form.has_key('style'):
770 if req.form.has_key('style'):
771 style = req.form['style'][0]
771 style = req.form['style'][0]
772 if style != self.config('web', 'style', ''):
772 if style != self.config('web', 'style', ''):
773 fields.append(('style', style))
773 fields.append(('style', style))
774
774
775 separator = req.url[-1] == '?' and ';' or '?'
775 separator = req.url[-1] == '?' and ';' or '?'
776 for name, value in fields:
776 for name, value in fields:
777 yield dict(name=name, value=value, separator=separator)
777 yield dict(name=name, value=value, separator=separator)
778 separator = ';'
778 separator = ';'
779
779
780 self.refresh()
780 self.refresh()
781
781
782 expand_form(req.form)
782 expand_form(req.form)
783 rewrite_request(req)
783 rewrite_request(req)
784
784
785 style = self.config("web", "style", "")
785 style = self.config("web", "style", "")
786 if req.form.has_key('style'):
786 if req.form.has_key('style'):
787 style = req.form['style'][0]
787 style = req.form['style'][0]
788 mapfile = style_map(self.templatepath, style)
788 mapfile = style_map(self.templatepath, style)
789
789
790 if req.env.get('HTTPS'):
790 proto = req.env.get('wsgi.url_scheme')
791 if proto == 'https':
791 proto = 'https'
792 proto = 'https'
792 default_port = "443"
793 default_port = "443"
793 else:
794 else:
794 proto = 'http'
795 proto = 'http'
795 default_port = "80"
796 default_port = "80"
796
797
797 port = req.env["SERVER_PORT"]
798 port = req.env["SERVER_PORT"]
798 port = port != default_port and (":" + port) or ""
799 port = port != default_port and (":" + port) or ""
799 urlbase = '%s://%s%s' % (proto, req.env['SERVER_NAME'], port)
800 urlbase = '%s://%s%s' % (proto, req.env['SERVER_NAME'], port)
800 staticurl = self.config("web", "staticurl") or req.url + 'static/'
801 staticurl = self.config("web", "staticurl") or req.url + 'static/'
801 if not staticurl.endswith('/'):
802 if not staticurl.endswith('/'):
802 staticurl += '/'
803 staticurl += '/'
803
804
804 if not self.reponame:
805 if not self.reponame:
805 self.reponame = (self.config("web", "name")
806 self.reponame = (self.config("web", "name")
806 or req.env.get('REPO_NAME')
807 or req.env.get('REPO_NAME')
807 or req.url.strip('/') or self.repo.root)
808 or req.url.strip('/') or self.repo.root)
808
809
809 self.t = templater.templater(mapfile, templater.common_filters,
810 self.t = templater.templater(mapfile, templater.common_filters,
810 defaults={"url": req.url,
811 defaults={"url": req.url,
811 "staticurl": staticurl,
812 "staticurl": staticurl,
812 "urlbase": urlbase,
813 "urlbase": urlbase,
813 "repo": self.reponame,
814 "repo": self.reponame,
814 "header": header,
815 "header": header,
815 "footer": footer,
816 "footer": footer,
816 "motd": motd,
817 "motd": motd,
817 "rawfileheader": rawfileheader,
818 "rawfileheader": rawfileheader,
818 "sessionvars": sessionvars
819 "sessionvars": sessionvars
819 })
820 })
820
821
821 try:
822 try:
822 if not req.form.has_key('cmd'):
823 if not req.form.has_key('cmd'):
823 req.form['cmd'] = [self.t.cache['default']]
824 req.form['cmd'] = [self.t.cache['default']]
824
825
825 cmd = req.form['cmd'][0]
826 cmd = req.form['cmd'][0]
826
827
827 method = getattr(self, 'do_' + cmd, None)
828 method = getattr(self, 'do_' + cmd, None)
828 if method:
829 if method:
829 try:
830 try:
830 method(req)
831 method(req)
831 except (hg.RepoError, revlog.RevlogError), inst:
832 except (hg.RepoError, revlog.RevlogError), inst:
832 req.write(self.t("error", error=str(inst)))
833 req.write(self.t("error", error=str(inst)))
833 else:
834 else:
834 req.write(self.t("error", error='No such method: ' + cmd))
835 req.write(self.t("error", error='No such method: ' + cmd))
835 finally:
836 finally:
836 self.t = None
837 self.t = None
837
838
838 def changectx(self, req):
839 def changectx(self, req):
839 if req.form.has_key('node'):
840 if req.form.has_key('node'):
840 changeid = req.form['node'][0]
841 changeid = req.form['node'][0]
841 elif req.form.has_key('manifest'):
842 elif req.form.has_key('manifest'):
842 changeid = req.form['manifest'][0]
843 changeid = req.form['manifest'][0]
843 else:
844 else:
844 changeid = self.repo.changelog.count() - 1
845 changeid = self.repo.changelog.count() - 1
845
846
846 try:
847 try:
847 ctx = self.repo.changectx(changeid)
848 ctx = self.repo.changectx(changeid)
848 except hg.RepoError:
849 except hg.RepoError:
849 man = self.repo.manifest
850 man = self.repo.manifest
850 mn = man.lookup(changeid)
851 mn = man.lookup(changeid)
851 ctx = self.repo.changectx(man.linkrev(mn))
852 ctx = self.repo.changectx(man.linkrev(mn))
852
853
853 return ctx
854 return ctx
854
855
855 def filectx(self, req):
856 def filectx(self, req):
856 path = self.cleanpath(req.form['file'][0])
857 path = self.cleanpath(req.form['file'][0])
857 if req.form.has_key('node'):
858 if req.form.has_key('node'):
858 changeid = req.form['node'][0]
859 changeid = req.form['node'][0]
859 else:
860 else:
860 changeid = req.form['filenode'][0]
861 changeid = req.form['filenode'][0]
861 try:
862 try:
862 ctx = self.repo.changectx(changeid)
863 ctx = self.repo.changectx(changeid)
863 fctx = ctx.filectx(path)
864 fctx = ctx.filectx(path)
864 except hg.RepoError:
865 except hg.RepoError:
865 fctx = self.repo.filectx(path, fileid=changeid)
866 fctx = self.repo.filectx(path, fileid=changeid)
866
867
867 return fctx
868 return fctx
868
869
869 def do_log(self, req):
870 def do_log(self, req):
870 if req.form.has_key('file') and req.form['file'][0]:
871 if req.form.has_key('file') and req.form['file'][0]:
871 self.do_filelog(req)
872 self.do_filelog(req)
872 else:
873 else:
873 self.do_changelog(req)
874 self.do_changelog(req)
874
875
875 def do_rev(self, req):
876 def do_rev(self, req):
876 self.do_changeset(req)
877 self.do_changeset(req)
877
878
878 def do_file(self, req):
879 def do_file(self, req):
879 path = self.cleanpath(req.form.get('file', [''])[0])
880 path = self.cleanpath(req.form.get('file', [''])[0])
880 if path:
881 if path:
881 try:
882 try:
882 req.write(self.filerevision(self.filectx(req)))
883 req.write(self.filerevision(self.filectx(req)))
883 return
884 return
884 except revlog.LookupError:
885 except revlog.LookupError:
885 pass
886 pass
886
887
887 req.write(self.manifest(self.changectx(req), path))
888 req.write(self.manifest(self.changectx(req), path))
888
889
889 def do_diff(self, req):
890 def do_diff(self, req):
890 self.do_filediff(req)
891 self.do_filediff(req)
891
892
892 def do_changelog(self, req, shortlog = False):
893 def do_changelog(self, req, shortlog = False):
893 if req.form.has_key('node'):
894 if req.form.has_key('node'):
894 ctx = self.changectx(req)
895 ctx = self.changectx(req)
895 else:
896 else:
896 if req.form.has_key('rev'):
897 if req.form.has_key('rev'):
897 hi = req.form['rev'][0]
898 hi = req.form['rev'][0]
898 else:
899 else:
899 hi = self.repo.changelog.count() - 1
900 hi = self.repo.changelog.count() - 1
900 try:
901 try:
901 ctx = self.repo.changectx(hi)
902 ctx = self.repo.changectx(hi)
902 except hg.RepoError:
903 except hg.RepoError:
903 req.write(self.search(hi)) # XXX redirect to 404 page?
904 req.write(self.search(hi)) # XXX redirect to 404 page?
904 return
905 return
905
906
906 req.write(self.changelog(ctx, shortlog = shortlog))
907 req.write(self.changelog(ctx, shortlog = shortlog))
907
908
908 def do_shortlog(self, req):
909 def do_shortlog(self, req):
909 self.do_changelog(req, shortlog = True)
910 self.do_changelog(req, shortlog = True)
910
911
911 def do_changeset(self, req):
912 def do_changeset(self, req):
912 req.write(self.changeset(self.changectx(req)))
913 req.write(self.changeset(self.changectx(req)))
913
914
914 def do_manifest(self, req):
915 def do_manifest(self, req):
915 req.write(self.manifest(self.changectx(req),
916 req.write(self.manifest(self.changectx(req),
916 self.cleanpath(req.form['path'][0])))
917 self.cleanpath(req.form['path'][0])))
917
918
918 def do_tags(self, req):
919 def do_tags(self, req):
919 req.write(self.tags())
920 req.write(self.tags())
920
921
921 def do_summary(self, req):
922 def do_summary(self, req):
922 req.write(self.summary())
923 req.write(self.summary())
923
924
924 def do_filediff(self, req):
925 def do_filediff(self, req):
925 req.write(self.filediff(self.filectx(req)))
926 req.write(self.filediff(self.filectx(req)))
926
927
927 def do_annotate(self, req):
928 def do_annotate(self, req):
928 req.write(self.fileannotate(self.filectx(req)))
929 req.write(self.fileannotate(self.filectx(req)))
929
930
930 def do_filelog(self, req):
931 def do_filelog(self, req):
931 req.write(self.filelog(self.filectx(req)))
932 req.write(self.filelog(self.filectx(req)))
932
933
933 def do_lookup(self, req):
934 def do_lookup(self, req):
934 try:
935 try:
935 r = hex(self.repo.lookup(req.form['key'][0]))
936 r = hex(self.repo.lookup(req.form['key'][0]))
936 success = 1
937 success = 1
937 except Exception,inst:
938 except Exception,inst:
938 r = str(inst)
939 r = str(inst)
939 success = 0
940 success = 0
940 resp = "%s %s\n" % (success, r)
941 resp = "%s %s\n" % (success, r)
941 req.httphdr("application/mercurial-0.1", length=len(resp))
942 req.httphdr("application/mercurial-0.1", length=len(resp))
942 req.write(resp)
943 req.write(resp)
943
944
944 def do_heads(self, req):
945 def do_heads(self, req):
945 resp = " ".join(map(hex, self.repo.heads())) + "\n"
946 resp = " ".join(map(hex, self.repo.heads())) + "\n"
946 req.httphdr("application/mercurial-0.1", length=len(resp))
947 req.httphdr("application/mercurial-0.1", length=len(resp))
947 req.write(resp)
948 req.write(resp)
948
949
949 def do_branches(self, req):
950 def do_branches(self, req):
950 nodes = []
951 nodes = []
951 if req.form.has_key('nodes'):
952 if req.form.has_key('nodes'):
952 nodes = map(bin, req.form['nodes'][0].split(" "))
953 nodes = map(bin, req.form['nodes'][0].split(" "))
953 resp = cStringIO.StringIO()
954 resp = cStringIO.StringIO()
954 for b in self.repo.branches(nodes):
955 for b in self.repo.branches(nodes):
955 resp.write(" ".join(map(hex, b)) + "\n")
956 resp.write(" ".join(map(hex, b)) + "\n")
956 resp = resp.getvalue()
957 resp = resp.getvalue()
957 req.httphdr("application/mercurial-0.1", length=len(resp))
958 req.httphdr("application/mercurial-0.1", length=len(resp))
958 req.write(resp)
959 req.write(resp)
959
960
960 def do_between(self, req):
961 def do_between(self, req):
961 if req.form.has_key('pairs'):
962 if req.form.has_key('pairs'):
962 pairs = [map(bin, p.split("-"))
963 pairs = [map(bin, p.split("-"))
963 for p in req.form['pairs'][0].split(" ")]
964 for p in req.form['pairs'][0].split(" ")]
964 resp = cStringIO.StringIO()
965 resp = cStringIO.StringIO()
965 for b in self.repo.between(pairs):
966 for b in self.repo.between(pairs):
966 resp.write(" ".join(map(hex, b)) + "\n")
967 resp.write(" ".join(map(hex, b)) + "\n")
967 resp = resp.getvalue()
968 resp = resp.getvalue()
968 req.httphdr("application/mercurial-0.1", length=len(resp))
969 req.httphdr("application/mercurial-0.1", length=len(resp))
969 req.write(resp)
970 req.write(resp)
970
971
971 def do_changegroup(self, req):
972 def do_changegroup(self, req):
972 req.httphdr("application/mercurial-0.1")
973 req.httphdr("application/mercurial-0.1")
973 nodes = []
974 nodes = []
974 if not self.allowpull:
975 if not self.allowpull:
975 return
976 return
976
977
977 if req.form.has_key('roots'):
978 if req.form.has_key('roots'):
978 nodes = map(bin, req.form['roots'][0].split(" "))
979 nodes = map(bin, req.form['roots'][0].split(" "))
979
980
980 z = zlib.compressobj()
981 z = zlib.compressobj()
981 f = self.repo.changegroup(nodes, 'serve')
982 f = self.repo.changegroup(nodes, 'serve')
982 while 1:
983 while 1:
983 chunk = f.read(4096)
984 chunk = f.read(4096)
984 if not chunk:
985 if not chunk:
985 break
986 break
986 req.write(z.compress(chunk))
987 req.write(z.compress(chunk))
987
988
988 req.write(z.flush())
989 req.write(z.flush())
989
990
990 def do_changegroupsubset(self, req):
991 def do_changegroupsubset(self, req):
991 req.httphdr("application/mercurial-0.1")
992 req.httphdr("application/mercurial-0.1")
992 bases = []
993 bases = []
993 heads = []
994 heads = []
994 if not self.allowpull:
995 if not self.allowpull:
995 return
996 return
996
997
997 if req.form.has_key('bases'):
998 if req.form.has_key('bases'):
998 bases = [bin(x) for x in req.form['bases'][0].split(' ')]
999 bases = [bin(x) for x in req.form['bases'][0].split(' ')]
999 if req.form.has_key('heads'):
1000 if req.form.has_key('heads'):
1000 heads = [bin(x) for x in req.form['heads'][0].split(' ')]
1001 heads = [bin(x) for x in req.form['heads'][0].split(' ')]
1001
1002
1002 z = zlib.compressobj()
1003 z = zlib.compressobj()
1003 f = self.repo.changegroupsubset(bases, heads, 'serve')
1004 f = self.repo.changegroupsubset(bases, heads, 'serve')
1004 while 1:
1005 while 1:
1005 chunk = f.read(4096)
1006 chunk = f.read(4096)
1006 if not chunk:
1007 if not chunk:
1007 break
1008 break
1008 req.write(z.compress(chunk))
1009 req.write(z.compress(chunk))
1009
1010
1010 req.write(z.flush())
1011 req.write(z.flush())
1011
1012
1012 def do_archive(self, req):
1013 def do_archive(self, req):
1013 type_ = req.form['type'][0]
1014 type_ = req.form['type'][0]
1014 allowed = self.configlist("web", "allow_archive")
1015 allowed = self.configlist("web", "allow_archive")
1015 if (type_ in self.archives and (type_ in allowed or
1016 if (type_ in self.archives and (type_ in allowed or
1016 self.configbool("web", "allow" + type_, False))):
1017 self.configbool("web", "allow" + type_, False))):
1017 self.archive(req, req.form['node'][0], type_)
1018 self.archive(req, req.form['node'][0], type_)
1018 return
1019 return
1019
1020
1020 req.write(self.t("error"))
1021 req.write(self.t("error"))
1021
1022
1022 def do_static(self, req):
1023 def do_static(self, req):
1023 fname = req.form['file'][0]
1024 fname = req.form['file'][0]
1024 # a repo owner may set web.static in .hg/hgrc to get any file
1025 # a repo owner may set web.static in .hg/hgrc to get any file
1025 # readable by the user running the CGI script
1026 # readable by the user running the CGI script
1026 static = self.config("web", "static",
1027 static = self.config("web", "static",
1027 os.path.join(self.templatepath, "static"),
1028 os.path.join(self.templatepath, "static"),
1028 untrusted=False)
1029 untrusted=False)
1029 req.write(staticfile(static, fname, req)
1030 req.write(staticfile(static, fname, req)
1030 or self.t("error", error="%r not found" % fname))
1031 or self.t("error", error="%r not found" % fname))
1031
1032
1032 def do_capabilities(self, req):
1033 def do_capabilities(self, req):
1033 caps = ['lookup', 'changegroupsubset']
1034 caps = ['lookup', 'changegroupsubset']
1034 if self.configbool('server', 'uncompressed'):
1035 if self.configbool('server', 'uncompressed'):
1035 caps.append('stream=%d' % self.repo.changelog.version)
1036 caps.append('stream=%d' % self.repo.changelog.version)
1036 # XXX: make configurable and/or share code with do_unbundle:
1037 # XXX: make configurable and/or share code with do_unbundle:
1037 unbundleversions = ['HG10GZ', 'HG10BZ', 'HG10UN']
1038 unbundleversions = ['HG10GZ', 'HG10BZ', 'HG10UN']
1038 if unbundleversions:
1039 if unbundleversions:
1039 caps.append('unbundle=%s' % ','.join(unbundleversions))
1040 caps.append('unbundle=%s' % ','.join(unbundleversions))
1040 resp = ' '.join(caps)
1041 resp = ' '.join(caps)
1041 req.httphdr("application/mercurial-0.1", length=len(resp))
1042 req.httphdr("application/mercurial-0.1", length=len(resp))
1042 req.write(resp)
1043 req.write(resp)
1043
1044
1044 def check_perm(self, req, op, default):
1045 def check_perm(self, req, op, default):
1045 '''check permission for operation based on user auth.
1046 '''check permission for operation based on user auth.
1046 return true if op allowed, else false.
1047 return true if op allowed, else false.
1047 default is policy to use if no config given.'''
1048 default is policy to use if no config given.'''
1048
1049
1049 user = req.env.get('REMOTE_USER')
1050 user = req.env.get('REMOTE_USER')
1050
1051
1051 deny = self.configlist('web', 'deny_' + op)
1052 deny = self.configlist('web', 'deny_' + op)
1052 if deny and (not user or deny == ['*'] or user in deny):
1053 if deny and (not user or deny == ['*'] or user in deny):
1053 return False
1054 return False
1054
1055
1055 allow = self.configlist('web', 'allow_' + op)
1056 allow = self.configlist('web', 'allow_' + op)
1056 return (allow and (allow == ['*'] or user in allow)) or default
1057 return (allow and (allow == ['*'] or user in allow)) or default
1057
1058
1058 def do_unbundle(self, req):
1059 def do_unbundle(self, req):
1059 def bail(response, headers={}):
1060 def bail(response, headers={}):
1060 length = int(req.env['CONTENT_LENGTH'])
1061 length = int(req.env['CONTENT_LENGTH'])
1061 for s in util.filechunkiter(req, limit=length):
1062 for s in util.filechunkiter(req, limit=length):
1062 # drain incoming bundle, else client will not see
1063 # drain incoming bundle, else client will not see
1063 # response when run outside cgi script
1064 # response when run outside cgi script
1064 pass
1065 pass
1065 req.httphdr("application/mercurial-0.1", headers=headers)
1066 req.httphdr("application/mercurial-0.1", headers=headers)
1066 req.write('0\n')
1067 req.write('0\n')
1067 req.write(response)
1068 req.write(response)
1068
1069
1069 # require ssl by default, auth info cannot be sniffed and
1070 # require ssl by default, auth info cannot be sniffed and
1070 # replayed
1071 # replayed
1071 ssl_req = self.configbool('web', 'push_ssl', True)
1072 ssl_req = self.configbool('web', 'push_ssl', True)
1072 if ssl_req:
1073 if ssl_req:
1073 if not req.env.get('HTTPS'):
1074 if req.env.get('wsgi.url_scheme') != 'https':
1074 bail(_('ssl required\n'))
1075 bail(_('ssl required\n'))
1075 return
1076 return
1076 proto = 'https'
1077 proto = 'https'
1077 else:
1078 else:
1078 proto = 'http'
1079 proto = 'http'
1079
1080
1080 # do not allow push unless explicitly allowed
1081 # do not allow push unless explicitly allowed
1081 if not self.check_perm(req, 'push', False):
1082 if not self.check_perm(req, 'push', False):
1082 bail(_('push not authorized\n'),
1083 bail(_('push not authorized\n'),
1083 headers={'status': '401 Unauthorized'})
1084 headers={'status': '401 Unauthorized'})
1084 return
1085 return
1085
1086
1086 their_heads = req.form['heads'][0].split(' ')
1087 their_heads = req.form['heads'][0].split(' ')
1087
1088
1088 def check_heads():
1089 def check_heads():
1089 heads = map(hex, self.repo.heads())
1090 heads = map(hex, self.repo.heads())
1090 return their_heads == [hex('force')] or their_heads == heads
1091 return their_heads == [hex('force')] or their_heads == heads
1091
1092
1092 # fail early if possible
1093 # fail early if possible
1093 if not check_heads():
1094 if not check_heads():
1094 bail(_('unsynced changes\n'))
1095 bail(_('unsynced changes\n'))
1095 return
1096 return
1096
1097
1097 req.httphdr("application/mercurial-0.1")
1098 req.httphdr("application/mercurial-0.1")
1098
1099
1099 # do not lock repo until all changegroup data is
1100 # do not lock repo until all changegroup data is
1100 # streamed. save to temporary file.
1101 # streamed. save to temporary file.
1101
1102
1102 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
1103 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
1103 fp = os.fdopen(fd, 'wb+')
1104 fp = os.fdopen(fd, 'wb+')
1104 try:
1105 try:
1105 length = int(req.env['CONTENT_LENGTH'])
1106 length = int(req.env['CONTENT_LENGTH'])
1106 for s in util.filechunkiter(req, limit=length):
1107 for s in util.filechunkiter(req, limit=length):
1107 fp.write(s)
1108 fp.write(s)
1108
1109
1109 try:
1110 try:
1110 lock = self.repo.lock()
1111 lock = self.repo.lock()
1111 try:
1112 try:
1112 if not check_heads():
1113 if not check_heads():
1113 req.write('0\n')
1114 req.write('0\n')
1114 req.write(_('unsynced changes\n'))
1115 req.write(_('unsynced changes\n'))
1115 return
1116 return
1116
1117
1117 fp.seek(0)
1118 fp.seek(0)
1118 header = fp.read(6)
1119 header = fp.read(6)
1119 if not header.startswith("HG"):
1120 if not header.startswith("HG"):
1120 # old client with uncompressed bundle
1121 # old client with uncompressed bundle
1121 def generator(f):
1122 def generator(f):
1122 yield header
1123 yield header
1123 for chunk in f:
1124 for chunk in f:
1124 yield chunk
1125 yield chunk
1125 elif not header.startswith("HG10"):
1126 elif not header.startswith("HG10"):
1126 req.write("0\n")
1127 req.write("0\n")
1127 req.write(_("unknown bundle version\n"))
1128 req.write(_("unknown bundle version\n"))
1128 return
1129 return
1129 elif header == "HG10GZ":
1130 elif header == "HG10GZ":
1130 def generator(f):
1131 def generator(f):
1131 zd = zlib.decompressobj()
1132 zd = zlib.decompressobj()
1132 for chunk in f:
1133 for chunk in f:
1133 yield zd.decompress(chunk)
1134 yield zd.decompress(chunk)
1134 elif header == "HG10BZ":
1135 elif header == "HG10BZ":
1135 def generator(f):
1136 def generator(f):
1136 zd = bz2.BZ2Decompressor()
1137 zd = bz2.BZ2Decompressor()
1137 zd.decompress("BZ")
1138 zd.decompress("BZ")
1138 for chunk in f:
1139 for chunk in f:
1139 yield zd.decompress(chunk)
1140 yield zd.decompress(chunk)
1140 elif header == "HG10UN":
1141 elif header == "HG10UN":
1141 def generator(f):
1142 def generator(f):
1142 for chunk in f:
1143 for chunk in f:
1143 yield chunk
1144 yield chunk
1144 else:
1145 else:
1145 req.write("0\n")
1146 req.write("0\n")
1146 req.write(_("unknown bundle compression type\n"))
1147 req.write(_("unknown bundle compression type\n"))
1147 return
1148 return
1148 gen = generator(util.filechunkiter(fp, 4096))
1149 gen = generator(util.filechunkiter(fp, 4096))
1149
1150
1150 # send addchangegroup output to client
1151 # send addchangegroup output to client
1151
1152
1152 old_stdout = sys.stdout
1153 old_stdout = sys.stdout
1153 sys.stdout = cStringIO.StringIO()
1154 sys.stdout = cStringIO.StringIO()
1154
1155
1155 try:
1156 try:
1156 url = 'remote:%s:%s' % (proto,
1157 url = 'remote:%s:%s' % (proto,
1157 req.env.get('REMOTE_HOST', ''))
1158 req.env.get('REMOTE_HOST', ''))
1158 try:
1159 try:
1159 ret = self.repo.addchangegroup(
1160 ret = self.repo.addchangegroup(
1160 util.chunkbuffer(gen), 'serve', url)
1161 util.chunkbuffer(gen), 'serve', url)
1161 except util.Abort, inst:
1162 except util.Abort, inst:
1162 sys.stdout.write("abort: %s\n" % inst)
1163 sys.stdout.write("abort: %s\n" % inst)
1163 ret = 0
1164 ret = 0
1164 finally:
1165 finally:
1165 val = sys.stdout.getvalue()
1166 val = sys.stdout.getvalue()
1166 sys.stdout = old_stdout
1167 sys.stdout = old_stdout
1167 req.write('%d\n' % ret)
1168 req.write('%d\n' % ret)
1168 req.write(val)
1169 req.write(val)
1169 finally:
1170 finally:
1170 lock.release()
1171 lock.release()
1171 except (OSError, IOError), inst:
1172 except (OSError, IOError), inst:
1172 req.write('0\n')
1173 req.write('0\n')
1173 filename = getattr(inst, 'filename', '')
1174 filename = getattr(inst, 'filename', '')
1174 # Don't send our filesystem layout to the client
1175 # Don't send our filesystem layout to the client
1175 if filename.startswith(self.repo.root):
1176 if filename.startswith(self.repo.root):
1176 filename = filename[len(self.repo.root)+1:]
1177 filename = filename[len(self.repo.root)+1:]
1177 else:
1178 else:
1178 filename = ''
1179 filename = ''
1179 error = getattr(inst, 'strerror', 'Unknown error')
1180 error = getattr(inst, 'strerror', 'Unknown error')
1180 req.write('%s: %s\n' % (error, filename))
1181 req.write('%s: %s\n' % (error, filename))
1181 finally:
1182 finally:
1182 fp.close()
1183 fp.close()
1183 os.unlink(tempname)
1184 os.unlink(tempname)
1184
1185
1185 def do_stream_out(self, req):
1186 def do_stream_out(self, req):
1186 req.httphdr("application/mercurial-0.1")
1187 req.httphdr("application/mercurial-0.1")
1187 streamclone.stream_out(self.repo, req, untrusted=True)
1188 streamclone.stream_out(self.repo, req, untrusted=True)
General Comments 0
You need to be logged in to leave comments. Login now