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