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