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