mercurial/hgweb.py
changeset 1159 b6f5a947e62e
parent 1143 4fffb3d84b7c
child 1160 0da98529a476
equal deleted inserted replaced
1154:c3cb9f39a91f 1159:b6f5a947e62e
    58     up = os.path.dirname(p)
    58     up = os.path.dirname(p)
    59     if up == "/":
    59     if up == "/":
    60         return "/"
    60         return "/"
    61     return up + "/"
    61     return up + "/"
    62 
    62 
    63 def httphdr(type, file="", size=0):
    63 class hgrequest:
    64     sys.stdout.write('Content-type: %s\n' % type)
    64     def __init__(self, inp=None, out=None, env=None):
    65     if file:
    65         self.inp = inp or sys.stdin
    66         sys.stdout.write('Content-disposition: attachment; filename=%s\n'
    66         self.out = out or sys.stdout
    67             % file)
    67         self.env = env or os.environ
    68     if size > 0:
    68         self.form = cgi.parse(self.inp, self.env)
    69         sys.stdout.write('Content-length: %d\n' % size)
    69 
    70     sys.stdout.write('\n')
    70     def write(self, *things):
    71 
    71         for thing in things:
    72 def write(*things):
    72             if hasattr(thing, "__iter__"):
    73     for thing in things:
    73                 for part in thing:
    74         if hasattr(thing, "__iter__"):
    74                     self.write(part)
    75             for part in thing:
    75             else:
    76                 write(part)
    76                 try:
    77         else:
    77                     self.out.write(thing)
    78             try:
    78                 except socket.error, x:
    79                 sys.stdout.write(str(thing))
    79                     if x[0] != errno.ECONNRESET:
    80             except socket.error, x:
    80                         raise
    81                 if x[0] != errno.ECONNRESET:
    81 
    82                     raise
    82     def header(self, headers=[('Content-type','text/html')]):
       
    83         for header in headers:
       
    84             self.out.write("%s: %s\r\n" % header)
       
    85         self.out.write("\r\n")
       
    86 
       
    87     def httphdr(self, type, file="", size=0):
       
    88 
       
    89         headers = [('Content-type', type)]
       
    90         if file:
       
    91             headers.append(('Content-disposition', 'attachment; filename=%s' % file))
       
    92         if size > 0:
       
    93             headers.append(('Content-length', str(size)))
       
    94         self.header(headers)
    83 
    95 
    84 class templater:
    96 class templater:
    85     def __init__(self, mapfile, filters={}, defaults={}):
    97     def __init__(self, mapfile, filters={}, defaults={}):
    86         self.cache = {}
    98         self.cache = {}
    87         self.map = {}
    99         self.map = {}
   151     "short": (lambda x: x[:12]),
   163     "short": (lambda x: x[:12]),
   152     "firstline": (lambda x: x.splitlines(1)[0]),
   164     "firstline": (lambda x: x.splitlines(1)[0]),
   153     "permissions": (lambda x: x and "-rwxr-xr-x" or "-rw-r--r--"),
   165     "permissions": (lambda x: x and "-rwxr-xr-x" or "-rw-r--r--"),
   154     "rfc822date": rfc822date,
   166     "rfc822date": rfc822date,
   155     }
   167     }
       
   168 
       
   169 
   156 
   170 
   157 class hgweb:
   171 class hgweb:
   158     def __init__(self, repo, name=None):
   172     def __init__(self, repo, name=None):
   159         if type(repo) == type(""):
   173         if type(repo) == type(""):
   160             self.repo = repository(ui(), repo)
   174             self.repo = repository(ui(), repo)
   633                      rev=self.repo.changelog.rev(n),
   647                      rev=self.repo.changelog.rev(n),
   634                      parent=self.parents("filediffparent",
   648                      parent=self.parents("filediffparent",
   635                                          cl.parents(n), cl.rev),
   649                                          cl.parents(n), cl.rev),
   636                      diff=diff)
   650                      diff=diff)
   637 
   651 
   638     def archive(self, cnode, type):
   652     def archive(self, req, cnode, type):
   639         cs = self.repo.changelog.read(cnode)
   653         cs = self.repo.changelog.read(cnode)
   640         mnode = cs[0]
   654         mnode = cs[0]
   641         mf = self.repo.manifest.read(mnode)
   655         mf = self.repo.manifest.read(mnode)
   642         rev = self.repo.manifest.rev(mnode)
   656         rev = self.repo.manifest.rev(mnode)
   643         reponame = re.sub(r"\W+", "-", self.reponame)
   657         reponame = re.sub(r"\W+", "-", self.reponame)
   656                 for f in files:
   670                 for f in files:
   657                     zf.writestr(name + f, self.repo.file(f).read(mf[f]))
   671                     zf.writestr(name + f, self.repo.file(f).read(mf[f]))
   658                 zf.close()
   672                 zf.close()
   659 
   673 
   660                 f = open(tmp, 'r')
   674                 f = open(tmp, 'r')
   661                 httphdr('application/zip', name[:-1] + '.zip',
   675                 req.httphdr('application/zip', name[:-1] + '.zip',
   662                         os.path.getsize(tmp))
   676                         os.path.getsize(tmp))
   663                 sys.stdout.write(f.read())
   677                 req.write(f.read())
   664                 f.close()
   678                 f.close()
   665             finally:
   679             finally:
   666                 os.unlink(tmp)
   680                 os.unlink(tmp)
   667 
   681 
   668         else:
   682         else:
   669             import StringIO
   683             import StringIO
   670             import time
   684             import time
   671             import tarfile
   685             import tarfile
   672 
   686 
   673             tf = tarfile.TarFile.open(mode='w|' + type, fileobj=sys.stdout)
   687             tf = tarfile.TarFile.open(mode='w|' + type, fileobj=req.out)
   674             mff = self.repo.manifest.readflags(mnode)
   688             mff = self.repo.manifest.readflags(mnode)
   675             mtime = int(time.time())
   689             mtime = int(time.time())
   676 
   690 
   677             httphdr('application/octet-stream', name[:-1] + '.tar.' + type)
   691             req.httphdr('application/octet-stream', name[:-1] + '.tar.' + type)
   678             for fname in files:
   692             for fname in files:
   679                 rcont = self.repo.file(fname).read(mf[fname])
   693                 rcont = self.repo.file(fname).read(mf[fname])
   680                 finfo = tarfile.TarInfo(name + fname)
   694                 finfo = tarfile.TarInfo(name + fname)
   681                 finfo.mtime = mtime
   695                 finfo.mtime = mtime
   682                 finfo.size = len(rcont)
   696                 finfo.size = len(rcont)
   686 
   700 
   687     # add tags to things
   701     # add tags to things
   688     # tags -> list of changesets corresponding to tags
   702     # tags -> list of changesets corresponding to tags
   689     # find tag, changeset, file
   703     # find tag, changeset, file
   690 
   704 
   691     def run(self):
   705     def run(self, req=hgrequest()):
   692         def header(**map):
   706         def header(**map):
   693             yield self.t("header", **map)
   707             yield self.t("header", **map)
   694 
   708 
   695         def footer(**map):
   709         def footer(**map):
   696             yield self.t("footer", **map)
   710             yield self.t("footer", **map)
   697 
   711 
   698         self.refresh()
   712         self.refresh()
   699         args = cgi.parse()
       
   700 
   713 
   701         t = self.repo.ui.config("web", "templates", templatepath())
   714         t = self.repo.ui.config("web", "templates", templatepath())
   702         m = os.path.join(t, "map")
   715         m = os.path.join(t, "map")
   703         style = self.repo.ui.config("web", "style", "")
   716         style = self.repo.ui.config("web", "style", "")
   704         if args.has_key('style'):
   717         if req.form.has_key('style'):
   705             style = args['style'][0]
   718             style = req.form['style'][0]
   706         if style:
   719         if style:
   707             b = os.path.basename("map-" + style)
   720             b = os.path.basename("map-" + style)
   708             p = os.path.join(t, b)
   721             p = os.path.join(t, b)
   709             if os.path.isfile(p):
   722             if os.path.isfile(p):
   710                 m = p
   723                 m = p
   711 
   724 
   712         port = os.environ["SERVER_PORT"]
   725         port = req.env["SERVER_PORT"]
   713         port = port != "80" and (":" + port) or ""
   726         port = port != "80" and (":" + port) or ""
   714         uri = os.environ["REQUEST_URI"]
   727         uri = req.env["REQUEST_URI"]
   715         if "?" in uri:
   728         if "?" in uri:
   716             uri = uri.split("?")[0]
   729             uri = uri.split("?")[0]
   717         url = "http://%s%s%s" % (os.environ["SERVER_NAME"], port, uri)
   730         url = "http://%s%s%s" % (req.env["SERVER_NAME"], port, uri)
   718 
   731 
   719         self.t = templater(m, common_filters,
   732         self.t = templater(m, common_filters,
   720                            {"url": url,
   733                            {"url": url,
   721                             "repo": self.reponame,
   734                             "repo": self.reponame,
   722                             "header": header,
   735                             "header": header,
   723                             "footer": footer,
   736                             "footer": footer,
   724                            })
   737                            })
   725 
   738 
   726         if not args.has_key('cmd'):
   739         if not req.form.has_key('cmd'):
   727             args['cmd'] = [self.t.cache['default'],]
   740             req.form['cmd'] = [self.t.cache['default'],]
   728 
   741 
   729         if args['cmd'][0] == 'changelog':
   742         if req.form['cmd'][0] == 'changelog':
   730             c = self.repo.changelog.count() - 1
   743             c = self.repo.changelog.count() - 1
   731             hi = c
   744             hi = c
   732             if args.has_key('rev'):
   745             if req.form.has_key('rev'):
   733                 hi = args['rev'][0]
   746                 hi = req.form['rev'][0]
   734                 try:
   747                 try:
   735                     hi = self.repo.changelog.rev(self.repo.lookup(hi))
   748                     hi = self.repo.changelog.rev(self.repo.lookup(hi))
   736                 except RepoError:
   749                 except RepoError:
   737                     write(self.search(hi))
   750                     req.write(self.search(hi))
   738                     return
   751                     return
   739 
   752 
   740             write(self.changelog(hi))
   753             req.write(self.changelog(hi))
   741 
   754 
   742         elif args['cmd'][0] == 'changeset':
   755         elif req.form['cmd'][0] == 'changeset':
   743             write(self.changeset(args['node'][0]))
   756             req.write(self.changeset(req.form['node'][0]))
   744 
   757 
   745         elif args['cmd'][0] == 'manifest':
   758         elif req.form['cmd'][0] == 'manifest':
   746             write(self.manifest(args['manifest'][0], args['path'][0]))
   759             req.write(self.manifest(req.form['manifest'][0], req.form['path'][0]))
   747 
   760 
   748         elif args['cmd'][0] == 'tags':
   761         elif req.form['cmd'][0] == 'tags':
   749             write(self.tags())
   762             req.write(self.tags())
   750 
   763 
   751         elif args['cmd'][0] == 'filediff':
   764         elif req.form['cmd'][0] == 'filediff':
   752             write(self.filediff(args['file'][0], args['node'][0]))
   765             req.write(self.filediff(req.form['file'][0], req.form['node'][0]))
   753 
   766 
   754         elif args['cmd'][0] == 'file':
   767         elif req.form['cmd'][0] == 'file':
   755             write(self.filerevision(args['file'][0], args['filenode'][0]))
   768             req.write(self.filerevision(req.form['file'][0], req.form['filenode'][0]))
   756 
   769 
   757         elif args['cmd'][0] == 'annotate':
   770         elif req.form['cmd'][0] == 'annotate':
   758             write(self.fileannotate(args['file'][0], args['filenode'][0]))
   771             req.write(self.fileannotate(req.form['file'][0], req.form['filenode'][0]))
   759 
   772 
   760         elif args['cmd'][0] == 'filelog':
   773         elif req.form['cmd'][0] == 'filelog':
   761             write(self.filelog(args['file'][0], args['filenode'][0]))
   774             req.write(self.filelog(req.form['file'][0], req.form['filenode'][0]))
   762 
   775 
   763         elif args['cmd'][0] == 'heads':
   776         elif req.form['cmd'][0] == 'heads':
   764             httphdr("application/mercurial-0.1")
   777             req.httphdr("application/mercurial-0.1")
   765             h = self.repo.heads()
   778             h = self.repo.heads()
   766             sys.stdout.write(" ".join(map(hex, h)) + "\n")
   779             req.write(" ".join(map(hex, h)) + "\n")
   767 
   780 
   768         elif args['cmd'][0] == 'branches':
   781         elif req.form['cmd'][0] == 'branches':
   769             httphdr("application/mercurial-0.1")
   782             req.httphdr("application/mercurial-0.1")
   770             nodes = []
   783             nodes = []
   771             if args.has_key('nodes'):
   784             if req.form.has_key('nodes'):
   772                 nodes = map(bin, args['nodes'][0].split(" "))
   785                 nodes = map(bin, req.form['nodes'][0].split(" "))
   773             for b in self.repo.branches(nodes):
   786             for b in self.repo.branches(nodes):
   774                 sys.stdout.write(" ".join(map(hex, b)) + "\n")
   787                 req.write(" ".join(map(hex, b)) + "\n")
   775 
   788 
   776         elif args['cmd'][0] == 'between':
   789         elif req.form['cmd'][0] == 'between':
   777             httphdr("application/mercurial-0.1")
   790             req.httphdr("application/mercurial-0.1")
   778             nodes = []
   791             nodes = []
   779             if args.has_key('pairs'):
   792             if req.form.has_key('pairs'):
   780                 pairs = [map(bin, p.split("-"))
   793                 pairs = [map(bin, p.split("-"))
   781                          for p in args['pairs'][0].split(" ")]
   794                          for p in req.form['pairs'][0].split(" ")]
   782             for b in self.repo.between(pairs):
   795             for b in self.repo.between(pairs):
   783                 sys.stdout.write(" ".join(map(hex, b)) + "\n")
   796                 req.write(" ".join(map(hex, b)) + "\n")
   784 
   797 
   785         elif args['cmd'][0] == 'changegroup':
   798         elif req.form['cmd'][0] == 'changegroup':
   786             httphdr("application/mercurial-0.1")
   799             req.httphdr("application/mercurial-0.1")
   787             nodes = []
   800             nodes = []
   788             if not self.allowpull:
   801             if not self.allowpull:
   789                 return
   802                 return
   790 
   803 
   791             if args.has_key('roots'):
   804             if req.form.has_key('roots'):
   792                 nodes = map(bin, args['roots'][0].split(" "))
   805                 nodes = map(bin, req.form['roots'][0].split(" "))
   793 
   806 
   794             z = zlib.compressobj()
   807             z = zlib.compressobj()
   795             f = self.repo.changegroup(nodes)
   808             f = self.repo.changegroup(nodes)
   796             while 1:
   809             while 1:
   797                 chunk = f.read(4096)
   810                 chunk = f.read(4096)
   798                 if not chunk:
   811                 if not chunk:
   799                     break
   812                     break
   800                 sys.stdout.write(z.compress(chunk))
   813                 req.write(z.compress(chunk))
   801 
   814 
   802             sys.stdout.write(z.flush())
   815             req.write(z.flush())
   803 
   816 
   804         elif args['cmd'][0] == 'archive':
   817         elif req.form['cmd'][0] == 'archive':
   805             changeset = bin(args['node'][0])
   818             changeset = bin(req.form['node'][0])
   806             type = args['type'][0]
   819             type = req.form['type'][0]
   807             if (type in self.archives and
   820             if (type in self.archives and
   808                 self.repo.ui.configbool("web", "allow" + type, False)):
   821                 self.repo.ui.configbool("web", "allow" + type, False)):
   809                 self.archive(changeset, type)
   822                 self.archive(req, changeset, type)
   810                 return
   823                 return
   811 
   824 
   812             write(self.t("error"))
   825             req.write(self.t("error"))
   813 
   826 
   814         else:
   827         else:
   815             write(self.t("error"))
   828             req.write(self.t("error"))
   816 
   829 
   817 def create_server(repo):
   830 def create_server(repo):
   818 
   831 
   819     def openlog(opt, default):
   832     def openlog(opt, default):
   820         if opt and opt != '-':
   833         if opt and opt != '-':
   931             cp = ConfigParser.SafeConfigParser()
   944             cp = ConfigParser.SafeConfigParser()
   932             cp.read(config)
   945             cp.read(config)
   933             self.repos = cp.items("paths")
   946             self.repos = cp.items("paths")
   934             self.repos.sort()
   947             self.repos.sort()
   935 
   948 
   936     def run(self):
   949     def run(self, req=hgrequest()):
   937         def header(**map):
   950         def header(**map):
   938             yield tmpl("header", **map)
   951             yield tmpl("header", **map)
   939 
   952 
   940         def footer(**map):
   953         def footer(**map):
   941             yield tmpl("footer", **map)
   954             yield tmpl("footer", **map)
   949             for name, path in self.repos:
   962             for name, path in self.repos:
   950                 u = ui()
   963                 u = ui()
   951                 u.readconfig(file(os.path.join(path, '.hg', 'hgrc')))
   964                 u.readconfig(file(os.path.join(path, '.hg', 'hgrc')))
   952                 get = u.config
   965                 get = u.config
   953 
   966 
   954                 url = ('/'.join([os.environ["REQUEST_URI"], name])
   967                 url = ('/'.join([req.env["REQUEST_URI"], name])
   955                        .replace("//", "/"))
   968                        .replace("//", "/"))
   956 
   969 
   957                 yield dict(contact=get("web", "contact") or
   970                 yield dict(contact=get("web", "contact") or
   958                                    get("web", "author", "unknown"),
   971                                    get("web", "author", "unknown"),
   959                            name=get("web", "name", name),
   972                            name=get("web", "name", name),
   963                            lastupdate=os.stat(os.path.join(path, ".hg",
   976                            lastupdate=os.stat(os.path.join(path, ".hg",
   964                                               "00changelog.d")).st_mtime)
   977                                               "00changelog.d")).st_mtime)
   965 
   978 
   966                 parity = 1 - parity
   979                 parity = 1 - parity
   967 
   980 
   968         virtual = os.environ.get("PATH_INFO", "").strip('/')
   981         virtual = req.env.get("PATH_INFO", "").strip('/')
   969         if virtual:
   982         if virtual:
   970             real = dict(self.repos).get(virtual)
   983             real = dict(self.repos).get(virtual)
   971             if real:
   984             if real:
   972                 hgweb(real).run()
   985                 hgweb(real).run(req)
   973             else:
   986             else:
   974                 write(tmpl("notfound", repo=virtual))
   987                 req.write(tmpl("notfound", repo=virtual))
   975         else:
   988         else:
   976             write(tmpl("index", entries=entries))
   989             req.write(tmpl("index", entries=entries))