mercurial/hgweb/common.py
author Yuya Nishihara <yuya@tcha.org>
Mon, 02 May 2016 12:09:00 +0900
changeset 29119 a032ebea4e97
parent 27046 37fcfe52c68c
child 29491 7f0498bd284e
permissions -rw-r--r--
revset: factor out public optimize() function from recursion New optimize() hides internal arguments and return values. This makes it easy to add more parameters and return values to _optimize().
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
2391
d351a3be3371 Fixing up comment headers for split up code.
Eric Hopper <hopper@omnifarious.org>
parents: 2356
diff changeset
     1
# hgweb/common.py - Utility functions needed by hgweb_mod and hgwebdir_mod
131
c9d51742471c moving hgweb to mercurial subdir
jake@edge2.net
parents:
diff changeset
     2
#
238
3b92f8fe47ae hgweb.py: kill #! line, clean up copyright notice
mpm@selenic.com
parents: 222
diff changeset
     3
# Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
2859
345bac2bc4ec update copyrights.
Vadim Gelfer <vadim.gelfer@gmail.com>
parents: 2514
diff changeset
     4
# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
131
c9d51742471c moving hgweb to mercurial subdir
jake@edge2.net
parents:
diff changeset
     5
#
8225
46293a0c7e9f updated license to be explicit about GPL version 2
Martin Geisler <mg@lazybytes.net>
parents: 7966
diff changeset
     6
# This software may be used and distributed according to the terms of the
10263
25e572394f5c Update license to GPLv2+
Matt Mackall <mpm@selenic.com>
parents: 9694
diff changeset
     7
# GNU General Public License version 2 or any later version.
131
c9d51742471c moving hgweb to mercurial subdir
jake@edge2.net
parents:
diff changeset
     8
27046
37fcfe52c68c hgweb: use absolute_import
Yuya Nishihara <yuya@tcha.org>
parents: 27044
diff changeset
     9
from __future__ import absolute_import
37fcfe52c68c hgweb: use absolute_import
Yuya Nishihara <yuya@tcha.org>
parents: 27044
diff changeset
    10
27044
1dde4914fb6c hgweb: import BaseHTTPServer as module at top level
Yuya Nishihara <yuya@tcha.org>
parents: 26200
diff changeset
    11
import BaseHTTPServer
27046
37fcfe52c68c hgweb: use absolute_import
Yuya Nishihara <yuya@tcha.org>
parents: 27044
diff changeset
    12
import errno
37fcfe52c68c hgweb: use absolute_import
Yuya Nishihara <yuya@tcha.org>
parents: 27044
diff changeset
    13
import mimetypes
37fcfe52c68c hgweb: use absolute_import
Yuya Nishihara <yuya@tcha.org>
parents: 27044
diff changeset
    14
import os
5561
22713dce19f6 hgweb: return meaningful HTTP status codes instead of nonsense
Bryan O'Sullivan <bos@serpentine.com>
parents: 4462
diff changeset
    15
5993
948a41e77902 hgweb: explicit response status
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5930
diff changeset
    16
HTTP_OK = 200
12183
f64b416b0ac8 hgweb: support very simple caching model (issue1845)
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 10264
diff changeset
    17
HTTP_NOT_MODIFIED = 304
5993
948a41e77902 hgweb: explicit response status
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5930
diff changeset
    18
HTTP_BAD_REQUEST = 400
6926
57b954d8d003 hgweb: raise ErrorResponses to communicate protocol errors
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6924
diff changeset
    19
HTTP_UNAUTHORIZED = 401
7029
b84d27386285 hgweb: Respond with HTTP 403 for disabled archive types instead of 404
Rocco Rutte <pdmef@gmx.net>
parents: 6926
diff changeset
    20
HTTP_FORBIDDEN = 403
5993
948a41e77902 hgweb: explicit response status
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5930
diff changeset
    21
HTTP_NOT_FOUND = 404
6926
57b954d8d003 hgweb: raise ErrorResponses to communicate protocol errors
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6924
diff changeset
    22
HTTP_METHOD_NOT_ALLOWED = 405
5993
948a41e77902 hgweb: explicit response status
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5930
diff changeset
    23
HTTP_SERVER_ERROR = 500
948a41e77902 hgweb: explicit response status
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5930
diff changeset
    24
9910
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    25
19032
7d31f2e42a8a hgweb: refactor checks for granting and revoking user permissions
Wagner Bruna <wbruna@softwareexpress.com.br>
parents: 18645
diff changeset
    26
def ismember(ui, username, userlist):
7d31f2e42a8a hgweb: refactor checks for granting and revoking user permissions
Wagner Bruna <wbruna@softwareexpress.com.br>
parents: 18645
diff changeset
    27
    """Check if username is a member of userlist.
7d31f2e42a8a hgweb: refactor checks for granting and revoking user permissions
Wagner Bruna <wbruna@softwareexpress.com.br>
parents: 18645
diff changeset
    28
7d31f2e42a8a hgweb: refactor checks for granting and revoking user permissions
Wagner Bruna <wbruna@softwareexpress.com.br>
parents: 18645
diff changeset
    29
    If userlist has a single '*' member, all users are considered members.
19951
d51c4d85ec23 spelling: random spell checker fixes
Mads Kiilerich <madski@unity3d.com>
parents: 19032
diff changeset
    30
    Can be overridden by extensions to provide more complex authorization
19032
7d31f2e42a8a hgweb: refactor checks for granting and revoking user permissions
Wagner Bruna <wbruna@softwareexpress.com.br>
parents: 18645
diff changeset
    31
    schemes.
7d31f2e42a8a hgweb: refactor checks for granting and revoking user permissions
Wagner Bruna <wbruna@softwareexpress.com.br>
parents: 18645
diff changeset
    32
    """
7d31f2e42a8a hgweb: refactor checks for granting and revoking user permissions
Wagner Bruna <wbruna@softwareexpress.com.br>
parents: 18645
diff changeset
    33
    return userlist == ['*'] or username in userlist
7d31f2e42a8a hgweb: refactor checks for granting and revoking user permissions
Wagner Bruna <wbruna@softwareexpress.com.br>
parents: 18645
diff changeset
    34
9910
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    35
def checkauthz(hgweb, req, op):
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    36
    '''Check permission for operation based on request data (including
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    37
    authentication info). Return if op allowed, else raise an ErrorResponse
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    38
    exception.'''
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    39
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    40
    user = req.env.get('REMOTE_USER')
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    41
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    42
    deny_read = hgweb.configlist('web', 'deny_read')
19032
7d31f2e42a8a hgweb: refactor checks for granting and revoking user permissions
Wagner Bruna <wbruna@softwareexpress.com.br>
parents: 18645
diff changeset
    43
    if deny_read and (not user or ismember(hgweb.repo.ui, user, deny_read)):
9910
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    44
        raise ErrorResponse(HTTP_UNAUTHORIZED, 'read not authorized')
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    45
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    46
    allow_read = hgweb.configlist('web', 'allow_read')
19032
7d31f2e42a8a hgweb: refactor checks for granting and revoking user permissions
Wagner Bruna <wbruna@softwareexpress.com.br>
parents: 18645
diff changeset
    47
    if allow_read and (not ismember(hgweb.repo.ui, user, allow_read)):
9910
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    48
        raise ErrorResponse(HTTP_UNAUTHORIZED, 'read not authorized')
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    49
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    50
    if op == 'pull' and not hgweb.allowpull:
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    51
        raise ErrorResponse(HTTP_UNAUTHORIZED, 'pull not authorized')
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    52
    elif op == 'pull' or op is None: # op is None for interface requests
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    53
        return
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    54
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    55
    # enforce that you can only push using POST requests
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    56
    if req.env['REQUEST_METHOD'] != 'POST':
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    57
        msg = 'push requires POST request'
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    58
        raise ErrorResponse(HTTP_METHOD_NOT_ALLOWED, msg)
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    59
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    60
    # require ssl by default for pushing, auth info cannot be sniffed
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    61
    # and replayed
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    62
    scheme = req.env.get('wsgi.url_scheme')
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    63
    if hgweb.configbool('web', 'push_ssl', True) and scheme != 'https':
17456
59a168019255 hgweb: respond 403 forbidden for ssl required error
Yuya Nishihara <yuya@tcha.org>
parents: 16687
diff changeset
    64
        raise ErrorResponse(HTTP_FORBIDDEN, 'ssl required')
9910
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    65
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    66
    deny = hgweb.configlist('web', 'deny_push')
19032
7d31f2e42a8a hgweb: refactor checks for granting and revoking user permissions
Wagner Bruna <wbruna@softwareexpress.com.br>
parents: 18645
diff changeset
    67
    if deny and (not user or ismember(hgweb.repo.ui, user, deny)):
9910
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    68
        raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized')
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    69
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    70
    allow = hgweb.configlist('web', 'allow_push')
19032
7d31f2e42a8a hgweb: refactor checks for granting and revoking user permissions
Wagner Bruna <wbruna@softwareexpress.com.br>
parents: 18645
diff changeset
    71
    if not (allow and ismember(hgweb.repo.ui, user, allow)):
9910
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    72
        raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized')
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    73
14058
3233b39d756f hgweb: initialize permhooks at definition time
Martin Geisler <mg@lazybytes.net>
parents: 13959
diff changeset
    74
# Hooks for hgweb permission checks; extensions can add hooks here.
3233b39d756f hgweb: initialize permhooks at definition time
Martin Geisler <mg@lazybytes.net>
parents: 13959
diff changeset
    75
# Each hook is invoked like this: hook(hgweb, request, operation),
3233b39d756f hgweb: initialize permhooks at definition time
Martin Geisler <mg@lazybytes.net>
parents: 13959
diff changeset
    76
# where operation is either read, pull or push. Hooks should either
3233b39d756f hgweb: initialize permhooks at definition time
Martin Geisler <mg@lazybytes.net>
parents: 13959
diff changeset
    77
# raise an ErrorResponse exception, or just return.
3233b39d756f hgweb: initialize permhooks at definition time
Martin Geisler <mg@lazybytes.net>
parents: 13959
diff changeset
    78
#
3233b39d756f hgweb: initialize permhooks at definition time
Martin Geisler <mg@lazybytes.net>
parents: 13959
diff changeset
    79
# It is possible to do both authentication and authorization through
3233b39d756f hgweb: initialize permhooks at definition time
Martin Geisler <mg@lazybytes.net>
parents: 13959
diff changeset
    80
# this.
3233b39d756f hgweb: initialize permhooks at definition time
Martin Geisler <mg@lazybytes.net>
parents: 13959
diff changeset
    81
permhooks = [checkauthz]
9910
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    82
6f92997dbdca hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents: 9694
diff changeset
    83
5561
22713dce19f6 hgweb: return meaningful HTTP status codes instead of nonsense
Bryan O'Sullivan <bos@serpentine.com>
parents: 4462
diff changeset
    84
class ErrorResponse(Exception):
7741
a3d7f99c23c0 hgweb: support custom http headers in ErrorResponse
Sune Foldager <cryo@cyanite.org>
parents: 7310
diff changeset
    85
    def __init__(self, code, message=None, headers=[]):
13444
75f5f312df5f hgweb: give ErrorResponse a descriptive string/Exception representation
Mads Kiilerich <mads@kiilerich.com>
parents: 13400
diff changeset
    86
        if message is None:
75f5f312df5f hgweb: give ErrorResponse a descriptive string/Exception representation
Mads Kiilerich <mads@kiilerich.com>
parents: 13400
diff changeset
    87
            message = _statusmessage(code)
26200
461e7b700fdf hgweb: remove ErrorResponse.message
timeless@mozdev.org
parents: 25717
diff changeset
    88
        Exception.__init__(self, message)
5561
22713dce19f6 hgweb: return meaningful HTTP status codes instead of nonsense
Bryan O'Sullivan <bos@serpentine.com>
parents: 4462
diff changeset
    89
        self.code = code
7741
a3d7f99c23c0 hgweb: support custom http headers in ErrorResponse
Sune Foldager <cryo@cyanite.org>
parents: 7310
diff changeset
    90
        self.headers = headers
5563
d61fea133f2d hgweb: fix breaking tests on Python < 2.5
Bryan O'Sullivan <bos@serpentine.com>
parents: 5561
diff changeset
    91
13570
617a87cb7eb2 hgweb: add support for 100-continue as recommended by PEP 333.
Augie Fackler <durin42@gmail.com>
parents: 13444
diff changeset
    92
class continuereader(object):
617a87cb7eb2 hgweb: add support for 100-continue as recommended by PEP 333.
Augie Fackler <durin42@gmail.com>
parents: 13444
diff changeset
    93
    def __init__(self, f, write):
617a87cb7eb2 hgweb: add support for 100-continue as recommended by PEP 333.
Augie Fackler <durin42@gmail.com>
parents: 13444
diff changeset
    94
        self.f = f
617a87cb7eb2 hgweb: add support for 100-continue as recommended by PEP 333.
Augie Fackler <durin42@gmail.com>
parents: 13444
diff changeset
    95
        self._write = write
617a87cb7eb2 hgweb: add support for 100-continue as recommended by PEP 333.
Augie Fackler <durin42@gmail.com>
parents: 13444
diff changeset
    96
        self.continued = False
617a87cb7eb2 hgweb: add support for 100-continue as recommended by PEP 333.
Augie Fackler <durin42@gmail.com>
parents: 13444
diff changeset
    97
617a87cb7eb2 hgweb: add support for 100-continue as recommended by PEP 333.
Augie Fackler <durin42@gmail.com>
parents: 13444
diff changeset
    98
    def read(self, amt=-1):
617a87cb7eb2 hgweb: add support for 100-continue as recommended by PEP 333.
Augie Fackler <durin42@gmail.com>
parents: 13444
diff changeset
    99
        if not self.continued:
617a87cb7eb2 hgweb: add support for 100-continue as recommended by PEP 333.
Augie Fackler <durin42@gmail.com>
parents: 13444
diff changeset
   100
            self.continued = True
617a87cb7eb2 hgweb: add support for 100-continue as recommended by PEP 333.
Augie Fackler <durin42@gmail.com>
parents: 13444
diff changeset
   101
            self._write('HTTP/1.1 100 Continue\r\n\r\n')
617a87cb7eb2 hgweb: add support for 100-continue as recommended by PEP 333.
Augie Fackler <durin42@gmail.com>
parents: 13444
diff changeset
   102
        return self.f.read(amt)
617a87cb7eb2 hgweb: add support for 100-continue as recommended by PEP 333.
Augie Fackler <durin42@gmail.com>
parents: 13444
diff changeset
   103
617a87cb7eb2 hgweb: add support for 100-continue as recommended by PEP 333.
Augie Fackler <durin42@gmail.com>
parents: 13444
diff changeset
   104
    def __getattr__(self, attr):
617a87cb7eb2 hgweb: add support for 100-continue as recommended by PEP 333.
Augie Fackler <durin42@gmail.com>
parents: 13444
diff changeset
   105
        if attr in ('close', 'readline', 'readlines', '__iter__'):
617a87cb7eb2 hgweb: add support for 100-continue as recommended by PEP 333.
Augie Fackler <durin42@gmail.com>
parents: 13444
diff changeset
   106
            return getattr(self.f, attr)
16687
e34106fa0dc3 cleanup: "raise SomeException()" -> "raise SomeException"
Brodie Rao <brodie@sf.io>
parents: 14058
diff changeset
   107
        raise AttributeError
5563
d61fea133f2d hgweb: fix breaking tests on Python < 2.5
Bryan O'Sullivan <bos@serpentine.com>
parents: 5561
diff changeset
   108
d61fea133f2d hgweb: fix breaking tests on Python < 2.5
Bryan O'Sullivan <bos@serpentine.com>
parents: 5561
diff changeset
   109
def _statusmessage(code):
27044
1dde4914fb6c hgweb: import BaseHTTPServer as module at top level
Yuya Nishihara <yuya@tcha.org>
parents: 26200
diff changeset
   110
    responses = BaseHTTPServer.BaseHTTPRequestHandler.responses
5563
d61fea133f2d hgweb: fix breaking tests on Python < 2.5
Bryan O'Sullivan <bos@serpentine.com>
parents: 5561
diff changeset
   111
    return responses.get(code, ('Error', 'Unknown error'))[0]
5760
0145f9afb0e7 Removed tabs and trailing whitespace in python files
Thomas Arendsen Hein <thomas@intevation.de>
parents: 5563
diff changeset
   112
9694
8269fe2d48f6 hgweb: send proper error messages to the client
Sune Foldager <cryo@cyanite.org>
parents: 9031
diff changeset
   113
def statusmessage(code, message=None):
8269fe2d48f6 hgweb: send proper error messages to the client
Sune Foldager <cryo@cyanite.org>
parents: 9031
diff changeset
   114
    return '%d %s' % (code, message or _statusmessage(code))
131
c9d51742471c moving hgweb to mercurial subdir
jake@edge2.net
parents:
diff changeset
   115
25717
46e2c57026bc hgweb: drop the default argument for get_stat
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 25660
diff changeset
   116
def get_stat(spath, fn):
46e2c57026bc hgweb: drop the default argument for get_stat
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 25660
diff changeset
   117
    """stat fn if it exists, spath otherwise"""
22577
a111e460318a hgweb: refresh hgweb.repo on phase change (issue4061)
Anton Shestakov <engored@ya.ru>
parents: 19951
diff changeset
   118
    cl_path = os.path.join(spath, fn)
3853
c0b449154a90 switch to the .hg/store layout, fix the tests
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents: 3276
diff changeset
   119
    if os.path.exists(cl_path):
13958
71f51cc71652 hgweb: detect change based on changelog size too
Martin Geisler <mg@lazybytes.net>
parents: 13444
diff changeset
   120
        return os.stat(cl_path)
1418
68f81ba07b2a Make hgweb work when the repository is empty (no 00changelog.i)
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents: 1416
diff changeset
   121
    else:
13958
71f51cc71652 hgweb: detect change based on changelog size too
Martin Geisler <mg@lazybytes.net>
parents: 13444
diff changeset
   122
        return os.stat(spath)
71f51cc71652 hgweb: detect change based on changelog size too
Martin Geisler <mg@lazybytes.net>
parents: 13444
diff changeset
   123
71f51cc71652 hgweb: detect change based on changelog size too
Martin Geisler <mg@lazybytes.net>
parents: 13444
diff changeset
   124
def get_mtime(spath):
25717
46e2c57026bc hgweb: drop the default argument for get_stat
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 25660
diff changeset
   125
    return get_stat(spath, "00changelog.i").st_mtime
1418
68f81ba07b2a Make hgweb work when the repository is empty (no 00changelog.i)
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents: 1416
diff changeset
   126
2514
419c42223bee Really fix http headers for web UI and issue 254.
Eric Hopper <hopper@omnifarious.org>
parents: 2391
diff changeset
   127
def staticfile(directory, fname, req):
5930
c301f15c965a send conservatively capitalized HTTP headers
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5779
diff changeset
   128
    """return a file inside directory with guessed Content-Type header
1825
a9343f9d7365 Make hgweb.staticfile() more secure and portable.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 1796
diff changeset
   129
a9343f9d7365 Make hgweb.staticfile() more secure and portable.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 1796
diff changeset
   130
    fname always uses '/' as directory separator and isn't allowed to
a9343f9d7365 Make hgweb.staticfile() more secure and portable.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 1796
diff changeset
   131
    contain unusual path components.
5930
c301f15c965a send conservatively capitalized HTTP headers
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5779
diff changeset
   132
    Content-Type is guessed using the mimetypes module.
1825
a9343f9d7365 Make hgweb.staticfile() more secure and portable.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 1796
diff changeset
   133
    Return an empty string if fname is illegal or file not found.
1793
83c6d8355909 Allow serving static files from hgwebdir to fix CSS and favicon.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 1792
diff changeset
   134
1825
a9343f9d7365 Make hgweb.staticfile() more secure and portable.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 1796
diff changeset
   135
    """
a9343f9d7365 Make hgweb.staticfile() more secure and portable.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 1796
diff changeset
   136
    parts = fname.split('/')
a9343f9d7365 Make hgweb.staticfile() more secure and portable.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 1796
diff changeset
   137
    for part in parts:
a9343f9d7365 Make hgweb.staticfile() more secure and portable.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 1796
diff changeset
   138
        if (part in ('', os.curdir, os.pardir) or
a9343f9d7365 Make hgweb.staticfile() more secure and portable.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 1796
diff changeset
   139
            os.sep in part or os.altsep is not None and os.altsep in part):
18645
76ff3a715cf2 hgweb: simplify internal staticfile return codes
Mads Kiilerich <mads@kiilerich.com>
parents: 18352
diff changeset
   140
            return
7288
9c399c53469d Allow per-file shadowing of static directory in templatepath
Brendan Cully <brendan@kublai.com>
parents: 7107
diff changeset
   141
    fpath = os.path.join(*parts)
9c399c53469d Allow per-file shadowing of static directory in templatepath
Brendan Cully <brendan@kublai.com>
parents: 7107
diff changeset
   142
    if isinstance(directory, str):
9c399c53469d Allow per-file shadowing of static directory in templatepath
Brendan Cully <brendan@kublai.com>
parents: 7107
diff changeset
   143
        directory = [directory]
9c399c53469d Allow per-file shadowing of static directory in templatepath
Brendan Cully <brendan@kublai.com>
parents: 7107
diff changeset
   144
    for d in directory:
9c399c53469d Allow per-file shadowing of static directory in templatepath
Brendan Cully <brendan@kublai.com>
parents: 7107
diff changeset
   145
        path = os.path.join(d, fpath)
9c399c53469d Allow per-file shadowing of static directory in templatepath
Brendan Cully <brendan@kublai.com>
parents: 7107
diff changeset
   146
        if os.path.exists(path):
9c399c53469d Allow per-file shadowing of static directory in templatepath
Brendan Cully <brendan@kublai.com>
parents: 7107
diff changeset
   147
            break
1793
83c6d8355909 Allow serving static files from hgwebdir to fix CSS and favicon.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 1792
diff changeset
   148
    try:
1825
a9343f9d7365 Make hgweb.staticfile() more secure and portable.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 1796
diff changeset
   149
        os.stat(path)
a9343f9d7365 Make hgweb.staticfile() more secure and portable.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 1796
diff changeset
   150
        ct = mimetypes.guess_type(path)[0] or "text/plain"
13400
14f3795a5ed7 explicitly close files
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 12183
diff changeset
   151
        fp = open(path, 'rb')
14f3795a5ed7 explicitly close files
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 12183
diff changeset
   152
        data = fp.read()
14f3795a5ed7 explicitly close files
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 12183
diff changeset
   153
        fp.close()
18352
e33b9b92a200 hgweb: pass the actual response body to request.response, not just the length
Mads Kiilerich <mads@kiilerich.com>
parents: 17456
diff changeset
   154
        req.respond(HTTP_OK, ct, body=data)
5561
22713dce19f6 hgweb: return meaningful HTTP status codes instead of nonsense
Bryan O'Sullivan <bos@serpentine.com>
parents: 4462
diff changeset
   155
    except TypeError:
8761
0289f384e1e5 Generally replace "file name" with "filename" in help and comments.
timeless <timeless@gmail.com>
parents: 8225
diff changeset
   156
        raise ErrorResponse(HTTP_SERVER_ERROR, 'illegal filename')
25660
328739ea70c3 global: mass rewrite to use modern exception syntax
Gregory Szorc <gregory.szorc@gmail.com>
parents: 22577
diff changeset
   157
    except OSError as err:
5561
22713dce19f6 hgweb: return meaningful HTTP status codes instead of nonsense
Bryan O'Sullivan <bos@serpentine.com>
parents: 4462
diff changeset
   158
        if err.errno == errno.ENOENT:
5993
948a41e77902 hgweb: explicit response status
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5930
diff changeset
   159
            raise ErrorResponse(HTTP_NOT_FOUND)
5561
22713dce19f6 hgweb: return meaningful HTTP status codes instead of nonsense
Bryan O'Sullivan <bos@serpentine.com>
parents: 4462
diff changeset
   160
        else:
5993
948a41e77902 hgweb: explicit response status
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5930
diff changeset
   161
            raise ErrorResponse(HTTP_SERVER_ERROR, err.strerror)
3276
db9d2a624521 hgweb: Search templates in templatepath/style/map, too, using a common function.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 3244
diff changeset
   162
4462
12e4d9524951 hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4038
diff changeset
   163
def paritygen(stripecount, offset=0):
12e4d9524951 hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4038
diff changeset
   164
    """count parity of horizontal stripes for easier reading"""
12e4d9524951 hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4038
diff changeset
   165
    if stripecount and offset:
12e4d9524951 hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4038
diff changeset
   166
        # account for offset, e.g. due to building the list in reverse
12e4d9524951 hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4038
diff changeset
   167
        count = (stripecount + offset) % stripecount
12e4d9524951 hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4038
diff changeset
   168
        parity = (stripecount + offset) / stripecount & 1
12e4d9524951 hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4038
diff changeset
   169
    else:
12e4d9524951 hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4038
diff changeset
   170
        count = 0
12e4d9524951 hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4038
diff changeset
   171
        parity = 0
12e4d9524951 hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4038
diff changeset
   172
    while True:
12e4d9524951 hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4038
diff changeset
   173
        yield parity
12e4d9524951 hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4038
diff changeset
   174
        count += 1
12e4d9524951 hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4038
diff changeset
   175
        if stripecount and count >= stripecount:
12e4d9524951 hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4038
diff changeset
   176
            parity = 1 - parity
12e4d9524951 hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4038
diff changeset
   177
            count = 0
12e4d9524951 hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4038
diff changeset
   178
5779
e9f68860d5ed Don't let ui.username override web.contact (issue900)
Thomas Arendsen Hein <thomas@intevation.de>
parents: 5760
diff changeset
   179
def get_contact(config):
e9f68860d5ed Don't let ui.username override web.contact (issue900)
Thomas Arendsen Hein <thomas@intevation.de>
parents: 5760
diff changeset
   180
    """Return repo contact information or empty string.
e9f68860d5ed Don't let ui.username override web.contact (issue900)
Thomas Arendsen Hein <thomas@intevation.de>
parents: 5760
diff changeset
   181
e9f68860d5ed Don't let ui.username override web.contact (issue900)
Thomas Arendsen Hein <thomas@intevation.de>
parents: 5760
diff changeset
   182
    web.contact is the primary source, but if that is not set, try
e9f68860d5ed Don't let ui.username override web.contact (issue900)
Thomas Arendsen Hein <thomas@intevation.de>
parents: 5760
diff changeset
   183
    ui.username or $EMAIL as a fallback to display something useful.
e9f68860d5ed Don't let ui.username override web.contact (issue900)
Thomas Arendsen Hein <thomas@intevation.de>
parents: 5760
diff changeset
   184
    """
e9f68860d5ed Don't let ui.username override web.contact (issue900)
Thomas Arendsen Hein <thomas@intevation.de>
parents: 5760
diff changeset
   185
    return (config("web", "contact") or
e9f68860d5ed Don't let ui.username override web.contact (issue900)
Thomas Arendsen Hein <thomas@intevation.de>
parents: 5760
diff changeset
   186
            config("ui", "username") or
e9f68860d5ed Don't let ui.username override web.contact (issue900)
Thomas Arendsen Hein <thomas@intevation.de>
parents: 5760
diff changeset
   187
            os.environ.get("EMAIL") or "")
12183
f64b416b0ac8 hgweb: support very simple caching model (issue1845)
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 10264
diff changeset
   188
f64b416b0ac8 hgweb: support very simple caching model (issue1845)
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 10264
diff changeset
   189
def caching(web, req):
f64b416b0ac8 hgweb: support very simple caching model (issue1845)
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 10264
diff changeset
   190
    tag = str(web.mtime)
f64b416b0ac8 hgweb: support very simple caching model (issue1845)
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 10264
diff changeset
   191
    if req.env.get('HTTP_IF_NONE_MATCH') == tag:
f64b416b0ac8 hgweb: support very simple caching model (issue1845)
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 10264
diff changeset
   192
        raise ErrorResponse(HTTP_NOT_MODIFIED)
f64b416b0ac8 hgweb: support very simple caching model (issue1845)
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 10264
diff changeset
   193
    req.headers.append(('ETag', tag))