##// END OF EJS Templates
hgweb: fix traceback by skipping invalid repo paths
TK Soh -
r1524:0d47bb88 default
parent child Browse files
Show More
@@ -1,1023 +1,1026 b''
1 # hgweb.py - web interface to a mercurial repository
1 # hgweb.py - web interface to a mercurial 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 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005 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, cgi, sys
9 import os, cgi, sys
10 from demandload import demandload
10 from demandload import demandload
11 demandload(globals(), "mdiff time re socket zlib errno ui hg ConfigParser")
11 demandload(globals(), "mdiff time re socket zlib errno ui hg ConfigParser")
12 demandload(globals(), "zipfile tempfile StringIO tarfile BaseHTTPServer util")
12 demandload(globals(), "zipfile tempfile StringIO tarfile BaseHTTPServer util")
13 demandload(globals(), "mimetypes")
13 demandload(globals(), "mimetypes")
14 from node import *
14 from node import *
15 from i18n import gettext as _
15 from i18n import gettext as _
16
16
17 def templatepath():
17 def templatepath():
18 for f in "templates", "../templates":
18 for f in "templates", "../templates":
19 p = os.path.join(os.path.dirname(__file__), f)
19 p = os.path.join(os.path.dirname(__file__), f)
20 if os.path.isdir(p):
20 if os.path.isdir(p):
21 return p
21 return p
22
22
23 def age(x):
23 def age(x):
24 def plural(t, c):
24 def plural(t, c):
25 if c == 1:
25 if c == 1:
26 return t
26 return t
27 return t + "s"
27 return t + "s"
28 def fmt(t, c):
28 def fmt(t, c):
29 return "%d %s" % (c, plural(t, c))
29 return "%d %s" % (c, plural(t, c))
30
30
31 now = time.time()
31 now = time.time()
32 then = x[0]
32 then = x[0]
33 delta = max(1, int(now - then))
33 delta = max(1, int(now - then))
34
34
35 scales = [["second", 1],
35 scales = [["second", 1],
36 ["minute", 60],
36 ["minute", 60],
37 ["hour", 3600],
37 ["hour", 3600],
38 ["day", 3600 * 24],
38 ["day", 3600 * 24],
39 ["week", 3600 * 24 * 7],
39 ["week", 3600 * 24 * 7],
40 ["month", 3600 * 24 * 30],
40 ["month", 3600 * 24 * 30],
41 ["year", 3600 * 24 * 365]]
41 ["year", 3600 * 24 * 365]]
42
42
43 scales.reverse()
43 scales.reverse()
44
44
45 for t, s in scales:
45 for t, s in scales:
46 n = delta / s
46 n = delta / s
47 if n >= 2 or s == 1:
47 if n >= 2 or s == 1:
48 return fmt(t, n)
48 return fmt(t, n)
49
49
50 def nl2br(text):
50 def nl2br(text):
51 return text.replace('\n', '<br/>\n')
51 return text.replace('\n', '<br/>\n')
52
52
53 def obfuscate(text):
53 def obfuscate(text):
54 return ''.join(['&#%d;' % ord(c) for c in text])
54 return ''.join(['&#%d;' % ord(c) for c in text])
55
55
56 def up(p):
56 def up(p):
57 if p[0] != "/":
57 if p[0] != "/":
58 p = "/" + p
58 p = "/" + p
59 if p[-1] == "/":
59 if p[-1] == "/":
60 p = p[:-1]
60 p = p[:-1]
61 up = os.path.dirname(p)
61 up = os.path.dirname(p)
62 if up == "/":
62 if up == "/":
63 return "/"
63 return "/"
64 return up + "/"
64 return up + "/"
65
65
66 def get_mtime(repo_path):
66 def get_mtime(repo_path):
67 hg_path = os.path.join(repo_path, ".hg")
67 hg_path = os.path.join(repo_path, ".hg")
68 cl_path = os.path.join(hg_path, "00changelog.i")
68 cl_path = os.path.join(hg_path, "00changelog.i")
69 if os.path.exists(os.path.join(cl_path)):
69 if os.path.exists(os.path.join(cl_path)):
70 return os.stat(cl_path).st_mtime
70 return os.stat(cl_path).st_mtime
71 else:
71 else:
72 return os.stat(hg_path).st_mtime
72 return os.stat(hg_path).st_mtime
73
73
74 class hgrequest:
74 class hgrequest:
75 def __init__(self, inp=None, out=None, env=None):
75 def __init__(self, inp=None, out=None, env=None):
76 self.inp = inp or sys.stdin
76 self.inp = inp or sys.stdin
77 self.out = out or sys.stdout
77 self.out = out or sys.stdout
78 self.env = env or os.environ
78 self.env = env or os.environ
79 self.form = cgi.parse(self.inp, self.env, keep_blank_values=1)
79 self.form = cgi.parse(self.inp, self.env, keep_blank_values=1)
80
80
81 def write(self, *things):
81 def write(self, *things):
82 for thing in things:
82 for thing in things:
83 if hasattr(thing, "__iter__"):
83 if hasattr(thing, "__iter__"):
84 for part in thing:
84 for part in thing:
85 self.write(part)
85 self.write(part)
86 else:
86 else:
87 try:
87 try:
88 self.out.write(str(thing))
88 self.out.write(str(thing))
89 except socket.error, inst:
89 except socket.error, inst:
90 if inst[0] != errno.ECONNRESET:
90 if inst[0] != errno.ECONNRESET:
91 raise
91 raise
92
92
93 def header(self, headers=[('Content-type','text/html')]):
93 def header(self, headers=[('Content-type','text/html')]):
94 for header in headers:
94 for header in headers:
95 self.out.write("%s: %s\r\n" % header)
95 self.out.write("%s: %s\r\n" % header)
96 self.out.write("\r\n")
96 self.out.write("\r\n")
97
97
98 def httphdr(self, type, file="", size=0):
98 def httphdr(self, type, file="", size=0):
99
99
100 headers = [('Content-type', type)]
100 headers = [('Content-type', type)]
101 if file:
101 if file:
102 headers.append(('Content-disposition', 'attachment; filename=%s' % file))
102 headers.append(('Content-disposition', 'attachment; filename=%s' % file))
103 if size > 0:
103 if size > 0:
104 headers.append(('Content-length', str(size)))
104 headers.append(('Content-length', str(size)))
105 self.header(headers)
105 self.header(headers)
106
106
107 class templater:
107 class templater:
108 def __init__(self, mapfile, filters={}, defaults={}):
108 def __init__(self, mapfile, filters={}, defaults={}):
109 self.cache = {}
109 self.cache = {}
110 self.map = {}
110 self.map = {}
111 self.base = os.path.dirname(mapfile)
111 self.base = os.path.dirname(mapfile)
112 self.filters = filters
112 self.filters = filters
113 self.defaults = defaults
113 self.defaults = defaults
114
114
115 for l in file(mapfile):
115 for l in file(mapfile):
116 m = re.match(r'(\S+)\s*=\s*"(.*)"$', l)
116 m = re.match(r'(\S+)\s*=\s*"(.*)"$', l)
117 if m:
117 if m:
118 self.cache[m.group(1)] = m.group(2)
118 self.cache[m.group(1)] = m.group(2)
119 else:
119 else:
120 m = re.match(r'(\S+)\s*=\s*(\S+)', l)
120 m = re.match(r'(\S+)\s*=\s*(\S+)', l)
121 if m:
121 if m:
122 self.map[m.group(1)] = os.path.join(self.base, m.group(2))
122 self.map[m.group(1)] = os.path.join(self.base, m.group(2))
123 else:
123 else:
124 raise LookupError(_("unknown map entry '%s'") % l)
124 raise LookupError(_("unknown map entry '%s'") % l)
125
125
126 def __call__(self, t, **map):
126 def __call__(self, t, **map):
127 m = self.defaults.copy()
127 m = self.defaults.copy()
128 m.update(map)
128 m.update(map)
129 try:
129 try:
130 tmpl = self.cache[t]
130 tmpl = self.cache[t]
131 except KeyError:
131 except KeyError:
132 tmpl = self.cache[t] = file(self.map[t]).read()
132 tmpl = self.cache[t] = file(self.map[t]).read()
133 return self.template(tmpl, self.filters, **m)
133 return self.template(tmpl, self.filters, **m)
134
134
135 def template(self, tmpl, filters={}, **map):
135 def template(self, tmpl, filters={}, **map):
136 while tmpl:
136 while tmpl:
137 m = re.search(r"#([a-zA-Z0-9]+)((%[a-zA-Z0-9]+)*)((\|[a-zA-Z0-9]+)*)#", tmpl)
137 m = re.search(r"#([a-zA-Z0-9]+)((%[a-zA-Z0-9]+)*)((\|[a-zA-Z0-9]+)*)#", tmpl)
138 if m:
138 if m:
139 yield tmpl[:m.start(0)]
139 yield tmpl[:m.start(0)]
140 v = map.get(m.group(1), "")
140 v = map.get(m.group(1), "")
141 v = callable(v) and v(**map) or v
141 v = callable(v) and v(**map) or v
142
142
143 format = m.group(2)
143 format = m.group(2)
144 fl = m.group(4)
144 fl = m.group(4)
145
145
146 if format:
146 if format:
147 q = v.__iter__
147 q = v.__iter__
148 for i in q():
148 for i in q():
149 lm = map.copy()
149 lm = map.copy()
150 lm.update(i)
150 lm.update(i)
151 yield self(format[1:], **lm)
151 yield self(format[1:], **lm)
152
152
153 v = ""
153 v = ""
154
154
155 elif fl:
155 elif fl:
156 for f in fl.split("|")[1:]:
156 for f in fl.split("|")[1:]:
157 v = filters[f](v)
157 v = filters[f](v)
158
158
159 yield v
159 yield v
160 tmpl = tmpl[m.end(0):]
160 tmpl = tmpl[m.end(0):]
161 else:
161 else:
162 yield tmpl
162 yield tmpl
163 return
163 return
164
164
165 common_filters = {
165 common_filters = {
166 "escape": cgi.escape,
166 "escape": cgi.escape,
167 "strip": lambda x: x.strip(),
167 "strip": lambda x: x.strip(),
168 "age": age,
168 "age": age,
169 "date": lambda x: util.datestr(x),
169 "date": lambda x: util.datestr(x),
170 "addbreaks": nl2br,
170 "addbreaks": nl2br,
171 "obfuscate": obfuscate,
171 "obfuscate": obfuscate,
172 "short": (lambda x: x[:12]),
172 "short": (lambda x: x[:12]),
173 "firstline": (lambda x: x.splitlines(1)[0]),
173 "firstline": (lambda x: x.splitlines(1)[0]),
174 "permissions": (lambda x: x and "-rwxr-xr-x" or "-rw-r--r--"),
174 "permissions": (lambda x: x and "-rwxr-xr-x" or "-rw-r--r--"),
175 "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S"),
175 "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S"),
176 }
176 }
177
177
178 class hgweb:
178 class hgweb:
179 def __init__(self, repo, name=None):
179 def __init__(self, repo, name=None):
180 if type(repo) == type(""):
180 if type(repo) == type(""):
181 self.repo = hg.repository(ui.ui(), repo)
181 self.repo = hg.repository(ui.ui(), repo)
182 else:
182 else:
183 self.repo = repo
183 self.repo = repo
184
184
185 self.mtime = -1
185 self.mtime = -1
186 self.reponame = name
186 self.reponame = name
187 self.archives = 'zip', 'gz', 'bz2'
187 self.archives = 'zip', 'gz', 'bz2'
188
188
189 def refresh(self):
189 def refresh(self):
190 mtime = get_mtime(self.repo.root)
190 mtime = get_mtime(self.repo.root)
191 if mtime != self.mtime:
191 if mtime != self.mtime:
192 self.mtime = mtime
192 self.mtime = mtime
193 self.repo = hg.repository(self.repo.ui, self.repo.root)
193 self.repo = hg.repository(self.repo.ui, self.repo.root)
194 self.maxchanges = int(self.repo.ui.config("web", "maxchanges", 10))
194 self.maxchanges = int(self.repo.ui.config("web", "maxchanges", 10))
195 self.maxfiles = int(self.repo.ui.config("web", "maxfiles", 10))
195 self.maxfiles = int(self.repo.ui.config("web", "maxfiles", 10))
196 self.allowpull = self.repo.ui.configbool("web", "allowpull", True)
196 self.allowpull = self.repo.ui.configbool("web", "allowpull", True)
197
197
198 def archivelist(self, nodeid):
198 def archivelist(self, nodeid):
199 for i in self.archives:
199 for i in self.archives:
200 if self.repo.ui.configbool("web", "allow" + i, False):
200 if self.repo.ui.configbool("web", "allow" + i, False):
201 yield {"type" : i, "node" : nodeid}
201 yield {"type" : i, "node" : nodeid}
202
202
203 def listfiles(self, files, mf):
203 def listfiles(self, files, mf):
204 for f in files[:self.maxfiles]:
204 for f in files[:self.maxfiles]:
205 yield self.t("filenodelink", node=hex(mf[f]), file=f)
205 yield self.t("filenodelink", node=hex(mf[f]), file=f)
206 if len(files) > self.maxfiles:
206 if len(files) > self.maxfiles:
207 yield self.t("fileellipses")
207 yield self.t("fileellipses")
208
208
209 def listfilediffs(self, files, changeset):
209 def listfilediffs(self, files, changeset):
210 for f in files[:self.maxfiles]:
210 for f in files[:self.maxfiles]:
211 yield self.t("filedifflink", node=hex(changeset), file=f)
211 yield self.t("filedifflink", node=hex(changeset), file=f)
212 if len(files) > self.maxfiles:
212 if len(files) > self.maxfiles:
213 yield self.t("fileellipses")
213 yield self.t("fileellipses")
214
214
215 def parents(self, node, parents=[], rev=None, hide=False, **args):
215 def parents(self, node, parents=[], rev=None, hide=False, **args):
216 if not rev:
216 if not rev:
217 rev = lambda x: ""
217 rev = lambda x: ""
218 parents = [p for p in parents if p != nullid]
218 parents = [p for p in parents if p != nullid]
219 if hide and len(parents) == 1 and rev(parents[0]) == rev(node) - 1:
219 if hide and len(parents) == 1 and rev(parents[0]) == rev(node) - 1:
220 return
220 return
221 for p in parents:
221 for p in parents:
222 yield dict(node=hex(p), rev=rev(p), **args)
222 yield dict(node=hex(p), rev=rev(p), **args)
223
223
224 def showtag(self, t1, node=nullid, **args):
224 def showtag(self, t1, node=nullid, **args):
225 for t in self.repo.nodetags(node):
225 for t in self.repo.nodetags(node):
226 yield self.t(t1, tag=t, **args)
226 yield self.t(t1, tag=t, **args)
227
227
228 def diff(self, node1, node2, files):
228 def diff(self, node1, node2, files):
229 def filterfiles(list, files):
229 def filterfiles(list, files):
230 l = [x for x in list if x in files]
230 l = [x for x in list if x in files]
231
231
232 for f in files:
232 for f in files:
233 if f[-1] != os.sep:
233 if f[-1] != os.sep:
234 f += os.sep
234 f += os.sep
235 l += [x for x in list if x.startswith(f)]
235 l += [x for x in list if x.startswith(f)]
236 return l
236 return l
237
237
238 parity = [0]
238 parity = [0]
239 def diffblock(diff, f, fn):
239 def diffblock(diff, f, fn):
240 yield self.t("diffblock",
240 yield self.t("diffblock",
241 lines=prettyprintlines(diff),
241 lines=prettyprintlines(diff),
242 parity=parity[0],
242 parity=parity[0],
243 file=f,
243 file=f,
244 filenode=hex(fn or nullid))
244 filenode=hex(fn or nullid))
245 parity[0] = 1 - parity[0]
245 parity[0] = 1 - parity[0]
246
246
247 def prettyprintlines(diff):
247 def prettyprintlines(diff):
248 for l in diff.splitlines(1):
248 for l in diff.splitlines(1):
249 if l.startswith('+'):
249 if l.startswith('+'):
250 yield self.t("difflineplus", line=l)
250 yield self.t("difflineplus", line=l)
251 elif l.startswith('-'):
251 elif l.startswith('-'):
252 yield self.t("difflineminus", line=l)
252 yield self.t("difflineminus", line=l)
253 elif l.startswith('@'):
253 elif l.startswith('@'):
254 yield self.t("difflineat", line=l)
254 yield self.t("difflineat", line=l)
255 else:
255 else:
256 yield self.t("diffline", line=l)
256 yield self.t("diffline", line=l)
257
257
258 r = self.repo
258 r = self.repo
259 cl = r.changelog
259 cl = r.changelog
260 mf = r.manifest
260 mf = r.manifest
261 change1 = cl.read(node1)
261 change1 = cl.read(node1)
262 change2 = cl.read(node2)
262 change2 = cl.read(node2)
263 mmap1 = mf.read(change1[0])
263 mmap1 = mf.read(change1[0])
264 mmap2 = mf.read(change2[0])
264 mmap2 = mf.read(change2[0])
265 date1 = util.datestr(change1[2])
265 date1 = util.datestr(change1[2])
266 date2 = util.datestr(change2[2])
266 date2 = util.datestr(change2[2])
267
267
268 c, a, d, u = r.changes(node1, node2)
268 c, a, d, u = r.changes(node1, node2)
269 if files:
269 if files:
270 c, a, d = map(lambda x: filterfiles(x, files), (c, a, d))
270 c, a, d = map(lambda x: filterfiles(x, files), (c, a, d))
271
271
272 for f in c:
272 for f in c:
273 to = r.file(f).read(mmap1[f])
273 to = r.file(f).read(mmap1[f])
274 tn = r.file(f).read(mmap2[f])
274 tn = r.file(f).read(mmap2[f])
275 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
275 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
276 for f in a:
276 for f in a:
277 to = None
277 to = None
278 tn = r.file(f).read(mmap2[f])
278 tn = r.file(f).read(mmap2[f])
279 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
279 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
280 for f in d:
280 for f in d:
281 to = r.file(f).read(mmap1[f])
281 to = r.file(f).read(mmap1[f])
282 tn = None
282 tn = None
283 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
283 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
284
284
285 def changelog(self, pos):
285 def changelog(self, pos):
286 def changenav(**map):
286 def changenav(**map):
287 def seq(factor=1):
287 def seq(factor=1):
288 yield 1 * factor
288 yield 1 * factor
289 yield 3 * factor
289 yield 3 * factor
290 #yield 5 * factor
290 #yield 5 * factor
291 for f in seq(factor * 10):
291 for f in seq(factor * 10):
292 yield f
292 yield f
293
293
294 l = []
294 l = []
295 for f in seq():
295 for f in seq():
296 if f < self.maxchanges / 2:
296 if f < self.maxchanges / 2:
297 continue
297 continue
298 if f > count:
298 if f > count:
299 break
299 break
300 r = "%d" % f
300 r = "%d" % f
301 if pos + f < count:
301 if pos + f < count:
302 l.append(("+" + r, pos + f))
302 l.append(("+" + r, pos + f))
303 if pos - f >= 0:
303 if pos - f >= 0:
304 l.insert(0, ("-" + r, pos - f))
304 l.insert(0, ("-" + r, pos - f))
305
305
306 yield {"rev": 0, "label": "(0)"}
306 yield {"rev": 0, "label": "(0)"}
307
307
308 for label, rev in l:
308 for label, rev in l:
309 yield {"label": label, "rev": rev}
309 yield {"label": label, "rev": rev}
310
310
311 yield {"label": "tip", "rev": "tip"}
311 yield {"label": "tip", "rev": "tip"}
312
312
313 def changelist(**map):
313 def changelist(**map):
314 parity = (start - end) & 1
314 parity = (start - end) & 1
315 cl = self.repo.changelog
315 cl = self.repo.changelog
316 l = [] # build a list in forward order for efficiency
316 l = [] # build a list in forward order for efficiency
317 for i in range(start, end):
317 for i in range(start, end):
318 n = cl.node(i)
318 n = cl.node(i)
319 changes = cl.read(n)
319 changes = cl.read(n)
320 hn = hex(n)
320 hn = hex(n)
321
321
322 l.insert(0, {"parity": parity,
322 l.insert(0, {"parity": parity,
323 "author": changes[1],
323 "author": changes[1],
324 "parent": self.parents(n, cl.parents(n), cl.rev,
324 "parent": self.parents(n, cl.parents(n), cl.rev,
325 hide=True),
325 hide=True),
326 "changelogtag": self.showtag("changelogtag",n),
326 "changelogtag": self.showtag("changelogtag",n),
327 "manifest": hex(changes[0]),
327 "manifest": hex(changes[0]),
328 "desc": changes[4],
328 "desc": changes[4],
329 "date": changes[2],
329 "date": changes[2],
330 "files": self.listfilediffs(changes[3], n),
330 "files": self.listfilediffs(changes[3], n),
331 "rev": i,
331 "rev": i,
332 "node": hn})
332 "node": hn})
333 parity = 1 - parity
333 parity = 1 - parity
334
334
335 for e in l:
335 for e in l:
336 yield e
336 yield e
337
337
338 cl = self.repo.changelog
338 cl = self.repo.changelog
339 mf = cl.read(cl.tip())[0]
339 mf = cl.read(cl.tip())[0]
340 count = cl.count()
340 count = cl.count()
341 start = max(0, pos - self.maxchanges + 1)
341 start = max(0, pos - self.maxchanges + 1)
342 end = min(count, start + self.maxchanges)
342 end = min(count, start + self.maxchanges)
343 pos = end - 1
343 pos = end - 1
344
344
345 yield self.t('changelog',
345 yield self.t('changelog',
346 changenav=changenav,
346 changenav=changenav,
347 manifest=hex(mf),
347 manifest=hex(mf),
348 rev=pos, changesets=count, entries=changelist)
348 rev=pos, changesets=count, entries=changelist)
349
349
350 def search(self, query):
350 def search(self, query):
351
351
352 def changelist(**map):
352 def changelist(**map):
353 cl = self.repo.changelog
353 cl = self.repo.changelog
354 count = 0
354 count = 0
355 qw = query.lower().split()
355 qw = query.lower().split()
356
356
357 def revgen():
357 def revgen():
358 for i in range(cl.count() - 1, 0, -100):
358 for i in range(cl.count() - 1, 0, -100):
359 l = []
359 l = []
360 for j in range(max(0, i - 100), i):
360 for j in range(max(0, i - 100), i):
361 n = cl.node(j)
361 n = cl.node(j)
362 changes = cl.read(n)
362 changes = cl.read(n)
363 l.append((n, j, changes))
363 l.append((n, j, changes))
364 l.reverse()
364 l.reverse()
365 for e in l:
365 for e in l:
366 yield e
366 yield e
367
367
368 for n, i, changes in revgen():
368 for n, i, changes in revgen():
369 miss = 0
369 miss = 0
370 for q in qw:
370 for q in qw:
371 if not (q in changes[1].lower() or
371 if not (q in changes[1].lower() or
372 q in changes[4].lower() or
372 q in changes[4].lower() or
373 q in " ".join(changes[3][:20]).lower()):
373 q in " ".join(changes[3][:20]).lower()):
374 miss = 1
374 miss = 1
375 break
375 break
376 if miss:
376 if miss:
377 continue
377 continue
378
378
379 count += 1
379 count += 1
380 hn = hex(n)
380 hn = hex(n)
381
381
382 yield self.t('searchentry',
382 yield self.t('searchentry',
383 parity=count & 1,
383 parity=count & 1,
384 author=changes[1],
384 author=changes[1],
385 parent=self.parents(n, cl.parents(n), cl.rev),
385 parent=self.parents(n, cl.parents(n), cl.rev),
386 changelogtag=self.showtag("changelogtag",n),
386 changelogtag=self.showtag("changelogtag",n),
387 manifest=hex(changes[0]),
387 manifest=hex(changes[0]),
388 desc=changes[4],
388 desc=changes[4],
389 date=changes[2],
389 date=changes[2],
390 files=self.listfilediffs(changes[3], n),
390 files=self.listfilediffs(changes[3], n),
391 rev=i,
391 rev=i,
392 node=hn)
392 node=hn)
393
393
394 if count >= self.maxchanges:
394 if count >= self.maxchanges:
395 break
395 break
396
396
397 cl = self.repo.changelog
397 cl = self.repo.changelog
398 mf = cl.read(cl.tip())[0]
398 mf = cl.read(cl.tip())[0]
399
399
400 yield self.t('search',
400 yield self.t('search',
401 query=query,
401 query=query,
402 manifest=hex(mf),
402 manifest=hex(mf),
403 entries=changelist)
403 entries=changelist)
404
404
405 def changeset(self, nodeid):
405 def changeset(self, nodeid):
406 cl = self.repo.changelog
406 cl = self.repo.changelog
407 n = self.repo.lookup(nodeid)
407 n = self.repo.lookup(nodeid)
408 nodeid = hex(n)
408 nodeid = hex(n)
409 changes = cl.read(n)
409 changes = cl.read(n)
410 p1 = cl.parents(n)[0]
410 p1 = cl.parents(n)[0]
411
411
412 files = []
412 files = []
413 mf = self.repo.manifest.read(changes[0])
413 mf = self.repo.manifest.read(changes[0])
414 for f in changes[3]:
414 for f in changes[3]:
415 files.append(self.t("filenodelink",
415 files.append(self.t("filenodelink",
416 filenode=hex(mf.get(f, nullid)), file=f))
416 filenode=hex(mf.get(f, nullid)), file=f))
417
417
418 def diff(**map):
418 def diff(**map):
419 yield self.diff(p1, n, None)
419 yield self.diff(p1, n, None)
420
420
421 yield self.t('changeset',
421 yield self.t('changeset',
422 diff=diff,
422 diff=diff,
423 rev=cl.rev(n),
423 rev=cl.rev(n),
424 node=nodeid,
424 node=nodeid,
425 parent=self.parents(n, cl.parents(n), cl.rev),
425 parent=self.parents(n, cl.parents(n), cl.rev),
426 changesettag=self.showtag("changesettag",n),
426 changesettag=self.showtag("changesettag",n),
427 manifest=hex(changes[0]),
427 manifest=hex(changes[0]),
428 author=changes[1],
428 author=changes[1],
429 desc=changes[4],
429 desc=changes[4],
430 date=changes[2],
430 date=changes[2],
431 files=files,
431 files=files,
432 archives=self.archivelist(nodeid))
432 archives=self.archivelist(nodeid))
433
433
434 def filelog(self, f, filenode):
434 def filelog(self, f, filenode):
435 cl = self.repo.changelog
435 cl = self.repo.changelog
436 fl = self.repo.file(f)
436 fl = self.repo.file(f)
437 filenode = hex(fl.lookup(filenode))
437 filenode = hex(fl.lookup(filenode))
438 count = fl.count()
438 count = fl.count()
439
439
440 def entries(**map):
440 def entries(**map):
441 l = []
441 l = []
442 parity = (count - 1) & 1
442 parity = (count - 1) & 1
443
443
444 for i in range(count):
444 for i in range(count):
445 n = fl.node(i)
445 n = fl.node(i)
446 lr = fl.linkrev(n)
446 lr = fl.linkrev(n)
447 cn = cl.node(lr)
447 cn = cl.node(lr)
448 cs = cl.read(cl.node(lr))
448 cs = cl.read(cl.node(lr))
449
449
450 l.insert(0, {"parity": parity,
450 l.insert(0, {"parity": parity,
451 "filenode": hex(n),
451 "filenode": hex(n),
452 "filerev": i,
452 "filerev": i,
453 "file": f,
453 "file": f,
454 "node": hex(cn),
454 "node": hex(cn),
455 "author": cs[1],
455 "author": cs[1],
456 "date": cs[2],
456 "date": cs[2],
457 "parent": self.parents(n, fl.parents(n),
457 "parent": self.parents(n, fl.parents(n),
458 fl.rev, file=f),
458 fl.rev, file=f),
459 "desc": cs[4]})
459 "desc": cs[4]})
460 parity = 1 - parity
460 parity = 1 - parity
461
461
462 for e in l:
462 for e in l:
463 yield e
463 yield e
464
464
465 yield self.t("filelog", file=f, filenode=filenode, entries=entries)
465 yield self.t("filelog", file=f, filenode=filenode, entries=entries)
466
466
467 def filerevision(self, f, node):
467 def filerevision(self, f, node):
468 fl = self.repo.file(f)
468 fl = self.repo.file(f)
469 n = fl.lookup(node)
469 n = fl.lookup(node)
470 node = hex(n)
470 node = hex(n)
471 text = fl.read(n)
471 text = fl.read(n)
472 changerev = fl.linkrev(n)
472 changerev = fl.linkrev(n)
473 cl = self.repo.changelog
473 cl = self.repo.changelog
474 cn = cl.node(changerev)
474 cn = cl.node(changerev)
475 cs = cl.read(cn)
475 cs = cl.read(cn)
476 mfn = cs[0]
476 mfn = cs[0]
477
477
478 mt = mimetypes.guess_type(f)[0]
478 mt = mimetypes.guess_type(f)[0]
479 rawtext = text
479 rawtext = text
480 if util.binary(text):
480 if util.binary(text):
481 text = "(binary:%s)" % mt
481 text = "(binary:%s)" % mt
482
482
483 def lines():
483 def lines():
484 for l, t in enumerate(text.splitlines(1)):
484 for l, t in enumerate(text.splitlines(1)):
485 yield {"line": t,
485 yield {"line": t,
486 "linenumber": "% 6d" % (l + 1),
486 "linenumber": "% 6d" % (l + 1),
487 "parity": l & 1}
487 "parity": l & 1}
488
488
489 yield self.t("filerevision",
489 yield self.t("filerevision",
490 file=f,
490 file=f,
491 filenode=node,
491 filenode=node,
492 path=up(f),
492 path=up(f),
493 text=lines(),
493 text=lines(),
494 raw=rawtext,
494 raw=rawtext,
495 mimetype=mt,
495 mimetype=mt,
496 rev=changerev,
496 rev=changerev,
497 node=hex(cn),
497 node=hex(cn),
498 manifest=hex(mfn),
498 manifest=hex(mfn),
499 author=cs[1],
499 author=cs[1],
500 date=cs[2],
500 date=cs[2],
501 parent=self.parents(n, fl.parents(n), fl.rev, file=f),
501 parent=self.parents(n, fl.parents(n), fl.rev, file=f),
502 permissions=self.repo.manifest.readflags(mfn)[f])
502 permissions=self.repo.manifest.readflags(mfn)[f])
503
503
504 def fileannotate(self, f, node):
504 def fileannotate(self, f, node):
505 bcache = {}
505 bcache = {}
506 ncache = {}
506 ncache = {}
507 fl = self.repo.file(f)
507 fl = self.repo.file(f)
508 n = fl.lookup(node)
508 n = fl.lookup(node)
509 node = hex(n)
509 node = hex(n)
510 changerev = fl.linkrev(n)
510 changerev = fl.linkrev(n)
511
511
512 cl = self.repo.changelog
512 cl = self.repo.changelog
513 cn = cl.node(changerev)
513 cn = cl.node(changerev)
514 cs = cl.read(cn)
514 cs = cl.read(cn)
515 mfn = cs[0]
515 mfn = cs[0]
516
516
517 def annotate(**map):
517 def annotate(**map):
518 parity = 1
518 parity = 1
519 last = None
519 last = None
520 for r, l in fl.annotate(n):
520 for r, l in fl.annotate(n):
521 try:
521 try:
522 cnode = ncache[r]
522 cnode = ncache[r]
523 except KeyError:
523 except KeyError:
524 cnode = ncache[r] = self.repo.changelog.node(r)
524 cnode = ncache[r] = self.repo.changelog.node(r)
525
525
526 try:
526 try:
527 name = bcache[r]
527 name = bcache[r]
528 except KeyError:
528 except KeyError:
529 cl = self.repo.changelog.read(cnode)
529 cl = self.repo.changelog.read(cnode)
530 bcache[r] = name = self.repo.ui.shortuser(cl[1])
530 bcache[r] = name = self.repo.ui.shortuser(cl[1])
531
531
532 if last != cnode:
532 if last != cnode:
533 parity = 1 - parity
533 parity = 1 - parity
534 last = cnode
534 last = cnode
535
535
536 yield {"parity": parity,
536 yield {"parity": parity,
537 "node": hex(cnode),
537 "node": hex(cnode),
538 "rev": r,
538 "rev": r,
539 "author": name,
539 "author": name,
540 "file": f,
540 "file": f,
541 "line": l}
541 "line": l}
542
542
543 yield self.t("fileannotate",
543 yield self.t("fileannotate",
544 file=f,
544 file=f,
545 filenode=node,
545 filenode=node,
546 annotate=annotate,
546 annotate=annotate,
547 path=up(f),
547 path=up(f),
548 rev=changerev,
548 rev=changerev,
549 node=hex(cn),
549 node=hex(cn),
550 manifest=hex(mfn),
550 manifest=hex(mfn),
551 author=cs[1],
551 author=cs[1],
552 date=cs[2],
552 date=cs[2],
553 parent=self.parents(n, fl.parents(n), fl.rev, file=f),
553 parent=self.parents(n, fl.parents(n), fl.rev, file=f),
554 permissions=self.repo.manifest.readflags(mfn)[f])
554 permissions=self.repo.manifest.readflags(mfn)[f])
555
555
556 def manifest(self, mnode, path):
556 def manifest(self, mnode, path):
557 man = self.repo.manifest
557 man = self.repo.manifest
558 mn = man.lookup(mnode)
558 mn = man.lookup(mnode)
559 mnode = hex(mn)
559 mnode = hex(mn)
560 mf = man.read(mn)
560 mf = man.read(mn)
561 rev = man.rev(mn)
561 rev = man.rev(mn)
562 node = self.repo.changelog.node(rev)
562 node = self.repo.changelog.node(rev)
563 mff = man.readflags(mn)
563 mff = man.readflags(mn)
564
564
565 files = {}
565 files = {}
566
566
567 p = path[1:]
567 p = path[1:]
568 l = len(p)
568 l = len(p)
569
569
570 for f,n in mf.items():
570 for f,n in mf.items():
571 if f[:l] != p:
571 if f[:l] != p:
572 continue
572 continue
573 remain = f[l:]
573 remain = f[l:]
574 if "/" in remain:
574 if "/" in remain:
575 short = remain[:remain.find("/") + 1] # bleah
575 short = remain[:remain.find("/") + 1] # bleah
576 files[short] = (f, None)
576 files[short] = (f, None)
577 else:
577 else:
578 short = os.path.basename(remain)
578 short = os.path.basename(remain)
579 files[short] = (f, n)
579 files[short] = (f, n)
580
580
581 def filelist(**map):
581 def filelist(**map):
582 parity = 0
582 parity = 0
583 fl = files.keys()
583 fl = files.keys()
584 fl.sort()
584 fl.sort()
585 for f in fl:
585 for f in fl:
586 full, fnode = files[f]
586 full, fnode = files[f]
587 if not fnode:
587 if not fnode:
588 continue
588 continue
589
589
590 yield {"file": full,
590 yield {"file": full,
591 "manifest": mnode,
591 "manifest": mnode,
592 "filenode": hex(fnode),
592 "filenode": hex(fnode),
593 "parity": parity,
593 "parity": parity,
594 "basename": f,
594 "basename": f,
595 "permissions": mff[full]}
595 "permissions": mff[full]}
596 parity = 1 - parity
596 parity = 1 - parity
597
597
598 def dirlist(**map):
598 def dirlist(**map):
599 parity = 0
599 parity = 0
600 fl = files.keys()
600 fl = files.keys()
601 fl.sort()
601 fl.sort()
602 for f in fl:
602 for f in fl:
603 full, fnode = files[f]
603 full, fnode = files[f]
604 if fnode:
604 if fnode:
605 continue
605 continue
606
606
607 yield {"parity": parity,
607 yield {"parity": parity,
608 "path": os.path.join(path, f),
608 "path": os.path.join(path, f),
609 "manifest": mnode,
609 "manifest": mnode,
610 "basename": f[:-1]}
610 "basename": f[:-1]}
611 parity = 1 - parity
611 parity = 1 - parity
612
612
613 yield self.t("manifest",
613 yield self.t("manifest",
614 manifest=mnode,
614 manifest=mnode,
615 rev=rev,
615 rev=rev,
616 node=hex(node),
616 node=hex(node),
617 path=path,
617 path=path,
618 up=up(path),
618 up=up(path),
619 fentries=filelist,
619 fentries=filelist,
620 dentries=dirlist,
620 dentries=dirlist,
621 archives=self.archivelist(hex(node)))
621 archives=self.archivelist(hex(node)))
622
622
623 def tags(self):
623 def tags(self):
624 cl = self.repo.changelog
624 cl = self.repo.changelog
625 mf = cl.read(cl.tip())[0]
625 mf = cl.read(cl.tip())[0]
626
626
627 i = self.repo.tagslist()
627 i = self.repo.tagslist()
628 i.reverse()
628 i.reverse()
629
629
630 def entries(**map):
630 def entries(**map):
631 parity = 0
631 parity = 0
632 for k,n in i:
632 for k,n in i:
633 yield {"parity": parity,
633 yield {"parity": parity,
634 "tag": k,
634 "tag": k,
635 "node": hex(n)}
635 "node": hex(n)}
636 parity = 1 - parity
636 parity = 1 - parity
637
637
638 yield self.t("tags",
638 yield self.t("tags",
639 manifest=hex(mf),
639 manifest=hex(mf),
640 entries=entries)
640 entries=entries)
641
641
642 def filediff(self, file, changeset):
642 def filediff(self, file, changeset):
643 cl = self.repo.changelog
643 cl = self.repo.changelog
644 n = self.repo.lookup(changeset)
644 n = self.repo.lookup(changeset)
645 changeset = hex(n)
645 changeset = hex(n)
646 p1 = cl.parents(n)[0]
646 p1 = cl.parents(n)[0]
647 cs = cl.read(n)
647 cs = cl.read(n)
648 mf = self.repo.manifest.read(cs[0])
648 mf = self.repo.manifest.read(cs[0])
649
649
650 def diff(**map):
650 def diff(**map):
651 yield self.diff(p1, n, file)
651 yield self.diff(p1, n, file)
652
652
653 yield self.t("filediff",
653 yield self.t("filediff",
654 file=file,
654 file=file,
655 filenode=hex(mf.get(file, nullid)),
655 filenode=hex(mf.get(file, nullid)),
656 node=changeset,
656 node=changeset,
657 rev=self.repo.changelog.rev(n),
657 rev=self.repo.changelog.rev(n),
658 parent=self.parents(n, cl.parents(n), cl.rev),
658 parent=self.parents(n, cl.parents(n), cl.rev),
659 diff=diff)
659 diff=diff)
660
660
661 def archive(self, req, cnode, type):
661 def archive(self, req, cnode, type):
662 cs = self.repo.changelog.read(cnode)
662 cs = self.repo.changelog.read(cnode)
663 mnode = cs[0]
663 mnode = cs[0]
664 mf = self.repo.manifest.read(mnode)
664 mf = self.repo.manifest.read(mnode)
665 rev = self.repo.manifest.rev(mnode)
665 rev = self.repo.manifest.rev(mnode)
666 reponame = re.sub(r"\W+", "-", self.reponame)
666 reponame = re.sub(r"\W+", "-", self.reponame)
667 name = "%s-%s/" % (reponame, short(cnode))
667 name = "%s-%s/" % (reponame, short(cnode))
668
668
669 files = mf.keys()
669 files = mf.keys()
670 files.sort()
670 files.sort()
671
671
672 if type == 'zip':
672 if type == 'zip':
673 tmp = tempfile.mkstemp()[1]
673 tmp = tempfile.mkstemp()[1]
674 try:
674 try:
675 zf = zipfile.ZipFile(tmp, "w", zipfile.ZIP_DEFLATED)
675 zf = zipfile.ZipFile(tmp, "w", zipfile.ZIP_DEFLATED)
676
676
677 for f in files:
677 for f in files:
678 zf.writestr(name + f, self.repo.file(f).read(mf[f]))
678 zf.writestr(name + f, self.repo.file(f).read(mf[f]))
679 zf.close()
679 zf.close()
680
680
681 f = open(tmp, 'r')
681 f = open(tmp, 'r')
682 req.httphdr('application/zip', name[:-1] + '.zip',
682 req.httphdr('application/zip', name[:-1] + '.zip',
683 os.path.getsize(tmp))
683 os.path.getsize(tmp))
684 req.write(f.read())
684 req.write(f.read())
685 f.close()
685 f.close()
686 finally:
686 finally:
687 os.unlink(tmp)
687 os.unlink(tmp)
688
688
689 else:
689 else:
690 tf = tarfile.TarFile.open(mode='w|' + type, fileobj=req.out)
690 tf = tarfile.TarFile.open(mode='w|' + type, fileobj=req.out)
691 mff = self.repo.manifest.readflags(mnode)
691 mff = self.repo.manifest.readflags(mnode)
692 mtime = int(time.time())
692 mtime = int(time.time())
693
693
694 if type == "gz":
694 if type == "gz":
695 encoding = "gzip"
695 encoding = "gzip"
696 else:
696 else:
697 encoding = "x-bzip2"
697 encoding = "x-bzip2"
698 req.header([('Content-type', 'application/x-tar'),
698 req.header([('Content-type', 'application/x-tar'),
699 ('Content-disposition', 'attachment; filename=%s%s%s' %
699 ('Content-disposition', 'attachment; filename=%s%s%s' %
700 (name[:-1], '.tar.', type)),
700 (name[:-1], '.tar.', type)),
701 ('Content-encoding', encoding)])
701 ('Content-encoding', encoding)])
702 for fname in files:
702 for fname in files:
703 rcont = self.repo.file(fname).read(mf[fname])
703 rcont = self.repo.file(fname).read(mf[fname])
704 finfo = tarfile.TarInfo(name + fname)
704 finfo = tarfile.TarInfo(name + fname)
705 finfo.mtime = mtime
705 finfo.mtime = mtime
706 finfo.size = len(rcont)
706 finfo.size = len(rcont)
707 finfo.mode = mff[fname] and 0755 or 0644
707 finfo.mode = mff[fname] and 0755 or 0644
708 tf.addfile(finfo, StringIO.StringIO(rcont))
708 tf.addfile(finfo, StringIO.StringIO(rcont))
709 tf.close()
709 tf.close()
710
710
711 # add tags to things
711 # add tags to things
712 # tags -> list of changesets corresponding to tags
712 # tags -> list of changesets corresponding to tags
713 # find tag, changeset, file
713 # find tag, changeset, file
714
714
715 def run(self, req=hgrequest()):
715 def run(self, req=hgrequest()):
716 def header(**map):
716 def header(**map):
717 yield self.t("header", **map)
717 yield self.t("header", **map)
718
718
719 def footer(**map):
719 def footer(**map):
720 yield self.t("footer", **map)
720 yield self.t("footer", **map)
721
721
722 def expand_form(form):
722 def expand_form(form):
723 shortcuts = {
723 shortcuts = {
724 'cl': [('cmd', ['changelog']), ('rev', None)],
724 'cl': [('cmd', ['changelog']), ('rev', None)],
725 'cs': [('cmd', ['changeset']), ('node', None)],
725 'cs': [('cmd', ['changeset']), ('node', None)],
726 'f': [('cmd', ['file']), ('filenode', None)],
726 'f': [('cmd', ['file']), ('filenode', None)],
727 'fl': [('cmd', ['filelog']), ('filenode', None)],
727 'fl': [('cmd', ['filelog']), ('filenode', None)],
728 'fd': [('cmd', ['filediff']), ('node', None)],
728 'fd': [('cmd', ['filediff']), ('node', None)],
729 'fa': [('cmd', ['annotate']), ('filenode', None)],
729 'fa': [('cmd', ['annotate']), ('filenode', None)],
730 'mf': [('cmd', ['manifest']), ('manifest', None)],
730 'mf': [('cmd', ['manifest']), ('manifest', None)],
731 'ca': [('cmd', ['archive']), ('node', None)],
731 'ca': [('cmd', ['archive']), ('node', None)],
732 'tags': [('cmd', ['tags'])],
732 'tags': [('cmd', ['tags'])],
733 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
733 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
734 }
734 }
735
735
736 for k in shortcuts.iterkeys():
736 for k in shortcuts.iterkeys():
737 if form.has_key(k):
737 if form.has_key(k):
738 for name, value in shortcuts[k]:
738 for name, value in shortcuts[k]:
739 if value is None:
739 if value is None:
740 value = form[k]
740 value = form[k]
741 form[name] = value
741 form[name] = value
742 del form[k]
742 del form[k]
743
743
744 self.refresh()
744 self.refresh()
745
745
746 expand_form(req.form)
746 expand_form(req.form)
747
747
748 t = self.repo.ui.config("web", "templates", templatepath())
748 t = self.repo.ui.config("web", "templates", templatepath())
749 m = os.path.join(t, "map")
749 m = os.path.join(t, "map")
750 style = self.repo.ui.config("web", "style", "")
750 style = self.repo.ui.config("web", "style", "")
751 if req.form.has_key('style'):
751 if req.form.has_key('style'):
752 style = req.form['style'][0]
752 style = req.form['style'][0]
753 if style:
753 if style:
754 b = os.path.basename("map-" + style)
754 b = os.path.basename("map-" + style)
755 p = os.path.join(t, b)
755 p = os.path.join(t, b)
756 if os.path.isfile(p):
756 if os.path.isfile(p):
757 m = p
757 m = p
758
758
759 port = req.env["SERVER_PORT"]
759 port = req.env["SERVER_PORT"]
760 port = port != "80" and (":" + port) or ""
760 port = port != "80" and (":" + port) or ""
761 uri = req.env["REQUEST_URI"]
761 uri = req.env["REQUEST_URI"]
762 if "?" in uri:
762 if "?" in uri:
763 uri = uri.split("?")[0]
763 uri = uri.split("?")[0]
764 url = "http://%s%s%s" % (req.env["SERVER_NAME"], port, uri)
764 url = "http://%s%s%s" % (req.env["SERVER_NAME"], port, uri)
765 if not self.reponame:
765 if not self.reponame:
766 self.reponame = (self.repo.ui.config("web", "name")
766 self.reponame = (self.repo.ui.config("web", "name")
767 or uri.strip('/') or self.repo.root)
767 or uri.strip('/') or self.repo.root)
768
768
769 self.t = templater(m, common_filters,
769 self.t = templater(m, common_filters,
770 {"url": url,
770 {"url": url,
771 "repo": self.reponame,
771 "repo": self.reponame,
772 "header": header,
772 "header": header,
773 "footer": footer,
773 "footer": footer,
774 })
774 })
775
775
776 if not req.form.has_key('cmd'):
776 if not req.form.has_key('cmd'):
777 req.form['cmd'] = [self.t.cache['default'],]
777 req.form['cmd'] = [self.t.cache['default'],]
778
778
779 if req.form['cmd'][0] == 'changelog':
779 if req.form['cmd'][0] == 'changelog':
780 c = self.repo.changelog.count() - 1
780 c = self.repo.changelog.count() - 1
781 hi = c
781 hi = c
782 if req.form.has_key('rev'):
782 if req.form.has_key('rev'):
783 hi = req.form['rev'][0]
783 hi = req.form['rev'][0]
784 try:
784 try:
785 hi = self.repo.changelog.rev(self.repo.lookup(hi))
785 hi = self.repo.changelog.rev(self.repo.lookup(hi))
786 except hg.RepoError:
786 except hg.RepoError:
787 req.write(self.search(hi))
787 req.write(self.search(hi))
788 return
788 return
789
789
790 req.write(self.changelog(hi))
790 req.write(self.changelog(hi))
791
791
792 elif req.form['cmd'][0] == 'changeset':
792 elif req.form['cmd'][0] == 'changeset':
793 req.write(self.changeset(req.form['node'][0]))
793 req.write(self.changeset(req.form['node'][0]))
794
794
795 elif req.form['cmd'][0] == 'manifest':
795 elif req.form['cmd'][0] == 'manifest':
796 req.write(self.manifest(req.form['manifest'][0], req.form['path'][0]))
796 req.write(self.manifest(req.form['manifest'][0], req.form['path'][0]))
797
797
798 elif req.form['cmd'][0] == 'tags':
798 elif req.form['cmd'][0] == 'tags':
799 req.write(self.tags())
799 req.write(self.tags())
800
800
801 elif req.form['cmd'][0] == 'filediff':
801 elif req.form['cmd'][0] == 'filediff':
802 req.write(self.filediff(req.form['file'][0], req.form['node'][0]))
802 req.write(self.filediff(req.form['file'][0], req.form['node'][0]))
803
803
804 elif req.form['cmd'][0] == 'file':
804 elif req.form['cmd'][0] == 'file':
805 req.write(self.filerevision(req.form['file'][0], req.form['filenode'][0]))
805 req.write(self.filerevision(req.form['file'][0], req.form['filenode'][0]))
806
806
807 elif req.form['cmd'][0] == 'annotate':
807 elif req.form['cmd'][0] == 'annotate':
808 req.write(self.fileannotate(req.form['file'][0], req.form['filenode'][0]))
808 req.write(self.fileannotate(req.form['file'][0], req.form['filenode'][0]))
809
809
810 elif req.form['cmd'][0] == 'filelog':
810 elif req.form['cmd'][0] == 'filelog':
811 req.write(self.filelog(req.form['file'][0], req.form['filenode'][0]))
811 req.write(self.filelog(req.form['file'][0], req.form['filenode'][0]))
812
812
813 elif req.form['cmd'][0] == 'heads':
813 elif req.form['cmd'][0] == 'heads':
814 req.httphdr("application/mercurial-0.1")
814 req.httphdr("application/mercurial-0.1")
815 h = self.repo.heads()
815 h = self.repo.heads()
816 req.write(" ".join(map(hex, h)) + "\n")
816 req.write(" ".join(map(hex, h)) + "\n")
817
817
818 elif req.form['cmd'][0] == 'branches':
818 elif req.form['cmd'][0] == 'branches':
819 req.httphdr("application/mercurial-0.1")
819 req.httphdr("application/mercurial-0.1")
820 nodes = []
820 nodes = []
821 if req.form.has_key('nodes'):
821 if req.form.has_key('nodes'):
822 nodes = map(bin, req.form['nodes'][0].split(" "))
822 nodes = map(bin, req.form['nodes'][0].split(" "))
823 for b in self.repo.branches(nodes):
823 for b in self.repo.branches(nodes):
824 req.write(" ".join(map(hex, b)) + "\n")
824 req.write(" ".join(map(hex, b)) + "\n")
825
825
826 elif req.form['cmd'][0] == 'between':
826 elif req.form['cmd'][0] == 'between':
827 req.httphdr("application/mercurial-0.1")
827 req.httphdr("application/mercurial-0.1")
828 nodes = []
828 nodes = []
829 if req.form.has_key('pairs'):
829 if req.form.has_key('pairs'):
830 pairs = [map(bin, p.split("-"))
830 pairs = [map(bin, p.split("-"))
831 for p in req.form['pairs'][0].split(" ")]
831 for p in req.form['pairs'][0].split(" ")]
832 for b in self.repo.between(pairs):
832 for b in self.repo.between(pairs):
833 req.write(" ".join(map(hex, b)) + "\n")
833 req.write(" ".join(map(hex, b)) + "\n")
834
834
835 elif req.form['cmd'][0] == 'changegroup':
835 elif req.form['cmd'][0] == 'changegroup':
836 req.httphdr("application/mercurial-0.1")
836 req.httphdr("application/mercurial-0.1")
837 nodes = []
837 nodes = []
838 if not self.allowpull:
838 if not self.allowpull:
839 return
839 return
840
840
841 if req.form.has_key('roots'):
841 if req.form.has_key('roots'):
842 nodes = map(bin, req.form['roots'][0].split(" "))
842 nodes = map(bin, req.form['roots'][0].split(" "))
843
843
844 z = zlib.compressobj()
844 z = zlib.compressobj()
845 f = self.repo.changegroup(nodes)
845 f = self.repo.changegroup(nodes)
846 while 1:
846 while 1:
847 chunk = f.read(4096)
847 chunk = f.read(4096)
848 if not chunk:
848 if not chunk:
849 break
849 break
850 req.write(z.compress(chunk))
850 req.write(z.compress(chunk))
851
851
852 req.write(z.flush())
852 req.write(z.flush())
853
853
854 elif req.form['cmd'][0] == 'archive':
854 elif req.form['cmd'][0] == 'archive':
855 changeset = self.repo.lookup(req.form['node'][0])
855 changeset = self.repo.lookup(req.form['node'][0])
856 type = req.form['type'][0]
856 type = req.form['type'][0]
857 if (type in self.archives and
857 if (type in self.archives and
858 self.repo.ui.configbool("web", "allow" + type, False)):
858 self.repo.ui.configbool("web", "allow" + type, False)):
859 self.archive(req, changeset, type)
859 self.archive(req, changeset, type)
860 return
860 return
861
861
862 req.write(self.t("error"))
862 req.write(self.t("error"))
863
863
864 else:
864 else:
865 req.write(self.t("error"))
865 req.write(self.t("error"))
866
866
867 def create_server(repo):
867 def create_server(repo):
868
868
869 def openlog(opt, default):
869 def openlog(opt, default):
870 if opt and opt != '-':
870 if opt and opt != '-':
871 return open(opt, 'w')
871 return open(opt, 'w')
872 return default
872 return default
873
873
874 address = repo.ui.config("web", "address", "")
874 address = repo.ui.config("web", "address", "")
875 port = int(repo.ui.config("web", "port", 8000))
875 port = int(repo.ui.config("web", "port", 8000))
876 use_ipv6 = repo.ui.configbool("web", "ipv6")
876 use_ipv6 = repo.ui.configbool("web", "ipv6")
877 accesslog = openlog(repo.ui.config("web", "accesslog", "-"), sys.stdout)
877 accesslog = openlog(repo.ui.config("web", "accesslog", "-"), sys.stdout)
878 errorlog = openlog(repo.ui.config("web", "errorlog", "-"), sys.stderr)
878 errorlog = openlog(repo.ui.config("web", "errorlog", "-"), sys.stderr)
879
879
880 class IPv6HTTPServer(BaseHTTPServer.HTTPServer):
880 class IPv6HTTPServer(BaseHTTPServer.HTTPServer):
881 address_family = getattr(socket, 'AF_INET6', None)
881 address_family = getattr(socket, 'AF_INET6', None)
882
882
883 def __init__(self, *args, **kwargs):
883 def __init__(self, *args, **kwargs):
884 if self.address_family is None:
884 if self.address_family is None:
885 raise hg.RepoError(_('IPv6 not available on this system'))
885 raise hg.RepoError(_('IPv6 not available on this system'))
886 BaseHTTPServer.HTTPServer.__init__(self, *args, **kwargs)
886 BaseHTTPServer.HTTPServer.__init__(self, *args, **kwargs)
887
887
888 class hgwebhandler(BaseHTTPServer.BaseHTTPRequestHandler):
888 class hgwebhandler(BaseHTTPServer.BaseHTTPRequestHandler):
889 def log_error(self, format, *args):
889 def log_error(self, format, *args):
890 errorlog.write("%s - - [%s] %s\n" % (self.address_string(),
890 errorlog.write("%s - - [%s] %s\n" % (self.address_string(),
891 self.log_date_time_string(),
891 self.log_date_time_string(),
892 format % args))
892 format % args))
893
893
894 def log_message(self, format, *args):
894 def log_message(self, format, *args):
895 accesslog.write("%s - - [%s] %s\n" % (self.address_string(),
895 accesslog.write("%s - - [%s] %s\n" % (self.address_string(),
896 self.log_date_time_string(),
896 self.log_date_time_string(),
897 format % args))
897 format % args))
898
898
899 def do_POST(self):
899 def do_POST(self):
900 try:
900 try:
901 self.do_hgweb()
901 self.do_hgweb()
902 except socket.error, inst:
902 except socket.error, inst:
903 if inst[0] != errno.EPIPE:
903 if inst[0] != errno.EPIPE:
904 raise
904 raise
905
905
906 def do_GET(self):
906 def do_GET(self):
907 self.do_POST()
907 self.do_POST()
908
908
909 def do_hgweb(self):
909 def do_hgweb(self):
910 query = ""
910 query = ""
911 p = self.path.find("?")
911 p = self.path.find("?")
912 if p:
912 if p:
913 query = self.path[p + 1:]
913 query = self.path[p + 1:]
914 query = query.replace('+', ' ')
914 query = query.replace('+', ' ')
915
915
916 env = {}
916 env = {}
917 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
917 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
918 env['REQUEST_METHOD'] = self.command
918 env['REQUEST_METHOD'] = self.command
919 env['SERVER_NAME'] = self.server.server_name
919 env['SERVER_NAME'] = self.server.server_name
920 env['SERVER_PORT'] = str(self.server.server_port)
920 env['SERVER_PORT'] = str(self.server.server_port)
921 env['REQUEST_URI'] = "/"
921 env['REQUEST_URI'] = "/"
922 if query:
922 if query:
923 env['QUERY_STRING'] = query
923 env['QUERY_STRING'] = query
924 host = self.address_string()
924 host = self.address_string()
925 if host != self.client_address[0]:
925 if host != self.client_address[0]:
926 env['REMOTE_HOST'] = host
926 env['REMOTE_HOST'] = host
927 env['REMOTE_ADDR'] = self.client_address[0]
927 env['REMOTE_ADDR'] = self.client_address[0]
928
928
929 if self.headers.typeheader is None:
929 if self.headers.typeheader is None:
930 env['CONTENT_TYPE'] = self.headers.type
930 env['CONTENT_TYPE'] = self.headers.type
931 else:
931 else:
932 env['CONTENT_TYPE'] = self.headers.typeheader
932 env['CONTENT_TYPE'] = self.headers.typeheader
933 length = self.headers.getheader('content-length')
933 length = self.headers.getheader('content-length')
934 if length:
934 if length:
935 env['CONTENT_LENGTH'] = length
935 env['CONTENT_LENGTH'] = length
936 accept = []
936 accept = []
937 for line in self.headers.getallmatchingheaders('accept'):
937 for line in self.headers.getallmatchingheaders('accept'):
938 if line[:1] in "\t\n\r ":
938 if line[:1] in "\t\n\r ":
939 accept.append(line.strip())
939 accept.append(line.strip())
940 else:
940 else:
941 accept = accept + line[7:].split(',')
941 accept = accept + line[7:].split(',')
942 env['HTTP_ACCEPT'] = ','.join(accept)
942 env['HTTP_ACCEPT'] = ','.join(accept)
943
943
944 req = hgrequest(self.rfile, self.wfile, env)
944 req = hgrequest(self.rfile, self.wfile, env)
945 self.send_response(200, "Script output follows")
945 self.send_response(200, "Script output follows")
946 hg.run(req)
946 hg.run(req)
947
947
948 hg = hgweb(repo)
948 hg = hgweb(repo)
949 if use_ipv6:
949 if use_ipv6:
950 return IPv6HTTPServer((address, port), hgwebhandler)
950 return IPv6HTTPServer((address, port), hgwebhandler)
951 else:
951 else:
952 return BaseHTTPServer.HTTPServer((address, port), hgwebhandler)
952 return BaseHTTPServer.HTTPServer((address, port), hgwebhandler)
953
953
954 def server(path, name, templates, address, port, use_ipv6=False,
954 def server(path, name, templates, address, port, use_ipv6=False,
955 accesslog=sys.stdout, errorlog=sys.stderr):
955 accesslog=sys.stdout, errorlog=sys.stderr):
956 httpd = create_server(path, name, templates, address, port, use_ipv6,
956 httpd = create_server(path, name, templates, address, port, use_ipv6,
957 accesslog, errorlog)
957 accesslog, errorlog)
958 httpd.serve_forever()
958 httpd.serve_forever()
959
959
960 # This is a stopgap
960 # This is a stopgap
961 class hgwebdir:
961 class hgwebdir:
962 def __init__(self, config):
962 def __init__(self, config):
963 def cleannames(items):
963 def cleannames(items):
964 return [(name.strip('/'), path) for name, path in items]
964 return [(name.strip('/'), path) for name, path in items]
965
965
966 if type(config) == type([]):
966 if type(config) == type([]):
967 self.repos = cleannames(config)
967 self.repos = cleannames(config)
968 elif type(config) == type({}):
968 elif type(config) == type({}):
969 self.repos = cleannames(config.items())
969 self.repos = cleannames(config.items())
970 self.repos.sort()
970 self.repos.sort()
971 else:
971 else:
972 cp = ConfigParser.SafeConfigParser()
972 cp = ConfigParser.SafeConfigParser()
973 cp.read(config)
973 cp.read(config)
974 self.repos = cleannames(cp.items("paths"))
974 self.repos = cleannames(cp.items("paths"))
975 self.repos.sort()
975 self.repos.sort()
976
976
977 def run(self, req=hgrequest()):
977 def run(self, req=hgrequest()):
978 def header(**map):
978 def header(**map):
979 yield tmpl("header", **map)
979 yield tmpl("header", **map)
980
980
981 def footer(**map):
981 def footer(**map):
982 yield tmpl("footer", **map)
982 yield tmpl("footer", **map)
983
983
984 m = os.path.join(templatepath(), "map")
984 m = os.path.join(templatepath(), "map")
985 tmpl = templater(m, common_filters,
985 tmpl = templater(m, common_filters,
986 {"header": header, "footer": footer})
986 {"header": header, "footer": footer})
987
987
988 def entries(**map):
988 def entries(**map):
989 parity = 0
989 parity = 0
990 for name, path in self.repos:
990 for name, path in self.repos:
991 u = ui.ui()
991 u = ui.ui()
992 try:
992 try:
993 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
993 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
994 except IOError:
994 except IOError:
995 pass
995 pass
996 get = u.config
996 get = u.config
997
997
998 url = ('/'.join([req.env["REQUEST_URI"].split('?')[0], name])
998 url = ('/'.join([req.env["REQUEST_URI"].split('?')[0], name])
999 .replace("//", "/"))
999 .replace("//", "/"))
1000
1000
1001 # update time with local timezone
1001 # update time with local timezone
1002 d = (get_mtime(path), util.makedate()[1])
1002 try:
1003 d = (get_mtime(path), util.makedate()[1])
1004 except OSError:
1005 continue
1003
1006
1004 yield dict(contact=(get("ui", "username") or # preferred
1007 yield dict(contact=(get("ui", "username") or # preferred
1005 get("web", "contact") or # deprecated
1008 get("web", "contact") or # deprecated
1006 get("web", "author", "unknown")), # also
1009 get("web", "author", "unknown")), # also
1007 name=get("web", "name", name),
1010 name=get("web", "name", name),
1008 url=url,
1011 url=url,
1009 parity=parity,
1012 parity=parity,
1010 shortdesc=get("web", "description", "unknown"),
1013 shortdesc=get("web", "description", "unknown"),
1011 lastupdate=d)
1014 lastupdate=d)
1012
1015
1013 parity = 1 - parity
1016 parity = 1 - parity
1014
1017
1015 virtual = req.env.get("PATH_INFO", "").strip('/')
1018 virtual = req.env.get("PATH_INFO", "").strip('/')
1016 if virtual:
1019 if virtual:
1017 real = dict(self.repos).get(virtual)
1020 real = dict(self.repos).get(virtual)
1018 if real:
1021 if real:
1019 hgweb(real).run(req)
1022 hgweb(real).run(req)
1020 else:
1023 else:
1021 req.write(tmpl("notfound", repo=virtual))
1024 req.write(tmpl("notfound", repo=virtual))
1022 else:
1025 else:
1023 req.write(tmpl("index", entries=entries))
1026 req.write(tmpl("index", entries=entries))
General Comments 0
You need to be logged in to leave comments. Login now