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