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