mercurial/hgweb.py
changeset 1830 4ced57680ce7
parent 1829 b0f6af327fd4
parent 1825 a9343f9d7365
child 1834 24881eaebee3
equal deleted inserted replaced
1829:b0f6af327fd4 1830:4ced57680ce7
     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, sys, urllib
     9 import os, cgi, sys, urllib
       
    10 import mimetypes
    10 from demandload import demandload
    11 from demandload import demandload
    11 demandload(globals(), "mdiff time re socket zlib errno ui hg ConfigParser")
    12 demandload(globals(), "mdiff time re socket zlib errno ui hg ConfigParser")
    12 demandload(globals(), "zipfile tempfile StringIO tarfile BaseHTTPServer util")
    13 demandload(globals(), "zipfile tempfile StringIO tarfile BaseHTTPServer util")
    13 demandload(globals(), "mimetypes")
    14 demandload(globals(), "mimetypes")
    14 from node import *
    15 from node import *
    16 
    17 
    17 def templatepath():
    18 def templatepath():
    18     for f in "templates", "../templates":
    19     for f in "templates", "../templates":
    19         p = os.path.join(os.path.dirname(__file__), f)
    20         p = os.path.join(os.path.dirname(__file__), f)
    20         if os.path.isdir(p):
    21         if os.path.isdir(p):
    21             return p
    22             return os.path.normpath(p)
    22 
    23 
    23 def age(x):
    24 def age(x):
    24     def plural(t, c):
    25     def plural(t, c):
    25         if c == 1:
    26         if c == 1:
    26             return t
    27             return t
    68     cl_path = os.path.join(hg_path, "00changelog.i")
    69     cl_path = os.path.join(hg_path, "00changelog.i")
    69     if os.path.exists(os.path.join(cl_path)):
    70     if os.path.exists(os.path.join(cl_path)):
    70         return os.stat(cl_path).st_mtime
    71         return os.stat(cl_path).st_mtime
    71     else:
    72     else:
    72         return os.stat(hg_path).st_mtime
    73         return os.stat(hg_path).st_mtime
       
    74 
       
    75 def staticfile(directory, fname):
       
    76     """return a file inside directory with guessed content-type header
       
    77 
       
    78     fname always uses '/' as directory separator and isn't allowed to
       
    79     contain unusual path components.
       
    80     Content-type is guessed using the mimetypes module.
       
    81     Return an empty string if fname is illegal or file not found.
       
    82 
       
    83     """
       
    84     parts = fname.split('/')
       
    85     path = directory
       
    86     for part in parts:
       
    87         if (part in ('', os.curdir, os.pardir) or
       
    88             os.sep in part or os.altsep is not None and os.altsep in part):
       
    89             return ""
       
    90         path = os.path.join(path, part)
       
    91     try:
       
    92         os.stat(path)
       
    93         ct = mimetypes.guess_type(path)[0] or "text/plain"
       
    94         return "Content-type: %s\n\n%s" % (ct, file(path).read())
       
    95     except (TypeError, OSError):
       
    96         # illegal fname or unreadable file
       
    97         return ""
    73 
    98 
    74 class hgrequest(object):
    99 class hgrequest(object):
    75     def __init__(self, inp=None, out=None, env=None):
   100     def __init__(self, inp=None, out=None, env=None):
    76         self.inp = inp or sys.stdin
   101         self.inp = inp or sys.stdin
    77         self.out = out or sys.stdout
   102         self.out = out or sys.stdout
   658         mf = cl.read(cl.tip())[0]
   683         mf = cl.read(cl.tip())[0]
   659 
   684 
   660         i = self.repo.tagslist()
   685         i = self.repo.tagslist()
   661         i.reverse()
   686         i.reverse()
   662 
   687 
   663         def entries(**map):
   688         def entries(notip=False, **map):
   664             parity = 0
   689             parity = 0
   665             for k,n in i:
   690             for k,n in i:
       
   691                 if notip and k == "tip": continue
   666                 yield {"parity": parity,
   692                 yield {"parity": parity,
   667                        "tag": k,
   693                        "tag": k,
   668                        "tagmanifest": hex(cl.read(n)[0]),
   694                        "tagmanifest": hex(cl.read(n)[0]),
   669                        "date": cl.read(n)[2],
   695                        "date": cl.read(n)[2],
   670                        "node": hex(n)}
   696                        "node": hex(n)}
   671                 parity = 1 - parity
   697                 parity = 1 - parity
   672 
   698 
   673         yield self.t("tags",
   699         yield self.t("tags",
   674                      manifest=hex(mf),
   700                      manifest=hex(mf),
   675                      entries=entries)
   701                      entries=lambda **x: entries(False, **x),
       
   702                      entriesnotip=lambda **x: entries(True, **x))
   676 
   703 
   677     def summary(self):
   704     def summary(self):
   678         cl = self.repo.changelog
   705         cl = self.repo.changelog
   679         mf = cl.read(cl.tip())[0]
   706         mf = cl.read(cl.tip())[0]
   680 
   707 
   841                 'fa': [('cmd', ['annotate']), ('filenode', None)],
   868                 'fa': [('cmd', ['annotate']), ('filenode', None)],
   842                 'mf': [('cmd', ['manifest']), ('manifest', None)],
   869                 'mf': [('cmd', ['manifest']), ('manifest', None)],
   843                 'ca': [('cmd', ['archive']), ('node', None)],
   870                 'ca': [('cmd', ['archive']), ('node', None)],
   844                 'tags': [('cmd', ['tags'])],
   871                 'tags': [('cmd', ['tags'])],
   845                 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
   872                 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
       
   873                 'static': [('cmd', ['static']), ('file', None)]
   846             }
   874             }
   847 
   875 
   848             for k in shortcuts.iterkeys():
   876             for k in shortcuts.iterkeys():
   849                 if form.has_key(k):
   877                 if form.has_key(k):
   850                     for name, value in shortcuts[k]:
   878                     for name, value in shortcuts[k]:
   856         self.refresh()
   884         self.refresh()
   857 
   885 
   858         expand_form(req.form)
   886         expand_form(req.form)
   859 
   887 
   860         t = self.repo.ui.config("web", "templates", templatepath())
   888         t = self.repo.ui.config("web", "templates", templatepath())
       
   889         static = self.repo.ui.config("web", "static", os.path.join(t,"static"))
   861         m = os.path.join(t, "map")
   890         m = os.path.join(t, "map")
   862         style = self.repo.ui.config("web", "style", "")
   891         style = self.repo.ui.config("web", "style", "")
   863         if req.form.has_key('style'):
   892         if req.form.has_key('style'):
   864             style = req.form['style'][0]
   893             style = req.form['style'][0]
   865         if style:
   894         if style:
   960 
   989 
   961             if req.form.has_key('roots'):
   990             if req.form.has_key('roots'):
   962                 nodes = map(bin, req.form['roots'][0].split(" "))
   991                 nodes = map(bin, req.form['roots'][0].split(" "))
   963 
   992 
   964             z = zlib.compressobj()
   993             z = zlib.compressobj()
   965             f = self.repo.changegroup(nodes)
   994             f = self.repo.changegroup(nodes, 'serve')
   966             while 1:
   995             while 1:
   967                 chunk = f.read(4096)
   996                 chunk = f.read(4096)
   968                 if not chunk:
   997                 if not chunk:
   969                     break
   998                     break
   970                 req.write(z.compress(chunk))
   999                 req.write(z.compress(chunk))
   978                 self.repo.ui.configbool("web", "allow" + type, False)):
  1007                 self.repo.ui.configbool("web", "allow" + type, False)):
   979                 self.archive(req, changeset, type)
  1008                 self.archive(req, changeset, type)
   980                 return
  1009                 return
   981 
  1010 
   982             req.write(self.t("error"))
  1011             req.write(self.t("error"))
       
  1012 
       
  1013         elif req.form['cmd'][0] == 'static':
       
  1014             fname = req.form['file'][0]
       
  1015             req.write(staticfile(static, fname)
       
  1016                       or self.t("error", error="%r not found" % fname))
   983 
  1017 
   984         else:
  1018         else:
   985             req.write(self.t("error"))
  1019             req.write(self.t("error"))
   986 
  1020 
   987 def create_server(repo):
  1021 def create_server(repo):
  1150                 except hg.RepoError, inst:
  1184                 except hg.RepoError, inst:
  1151                     req.write(tmpl("error", error=str(inst)))
  1185                     req.write(tmpl("error", error=str(inst)))
  1152             else:
  1186             else:
  1153                 req.write(tmpl("notfound", repo=virtual))
  1187                 req.write(tmpl("notfound", repo=virtual))
  1154         else:
  1188         else:
  1155             req.write(tmpl("index", entries=entries))
  1189             if req.form.has_key('static'):
       
  1190                 static = os.path.join(templatepath(), "static")
       
  1191                 fname = req.form['static'][0]
       
  1192                 req.write(staticfile(static, fname)
       
  1193                           or tmpl("error", error="%r not found" % fname))
       
  1194             else:
       
  1195                 req.write(tmpl("index", entries=entries))