This file is indexed.

/usr/share/pyshared/MoinMoin/web/utils.py is in python-moinmoin 1.9.3-1ubuntu2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# -*- coding: iso-8859-1 -*-
"""
    MoinMoin - Utility functions for the web-layer

    @copyright: 2003-2008 MoinMoin:ThomasWaldmann,
                2008-2008 MoinMoin:FlorianKrupicka
    @license: GNU GPL, see COPYING for details.
"""
import time

from werkzeug import abort, redirect, cookie_date, Response

from MoinMoin import caching
from MoinMoin import log
from MoinMoin import wikiutil
from MoinMoin.Page import Page
from MoinMoin.web.exceptions import Forbidden, SurgeProtection

logging = log.getLogger(__name__)

def check_forbidden(request):
    """ Simple action and host access checks

    Spider agents are checked against the called actions,
    hosts against the blacklist. Raises Forbidden if triggered.
    """
    args = request.args
    action = args.get('action')
    if ((args or request.method != 'GET') and
        action not in ['rss_rc', 'show', 'sitemap'] and
        not (action == 'AttachFile' and args.get('do') == 'get')):
        if request.isSpiderAgent:
            raise Forbidden()
    if request.cfg.hosts_deny:
        remote_addr = request.remote_addr
        for host in request.cfg.hosts_deny:
            if host[-1] == '.' and remote_addr.startswith(host):
                logging.debug("hosts_deny (net): %s" % remote_addr)
                raise Forbidden()
            if remote_addr == host:
                logging.debug("hosts_deny (ip): %s" % remote_addr)
                raise Forbidden()
    return False

def check_surge_protect(request, kick=False):
    """ Check for excessive requests

    Raises a SurgeProtection exception on wiki overuse.

    @param request: a moin request object
    """
    limits = request.cfg.surge_action_limits
    if not limits:
        return False

    remote_addr = request.remote_addr or ''
    if remote_addr.startswith('127.'):
        return False

    validuser = request.user.valid
    current_id = validuser and request.user.name or remote_addr
    current_action = request.action

    default_limit = limits.get('default', (30, 60))

    now = int(time.time())
    surgedict = {}
    surge_detected = False

    try:
        # if we have common farm users, we could also use scope='farm':
        cache = caching.CacheEntry(request, 'surgeprotect', 'surge-log', scope='wiki', use_encode=True)
        if cache.exists():
            data = cache.content()
            data = data.split("\n")
            for line in data:
                try:
                    id, t, action, surge_indicator = line.split("\t")
                    t = int(t)
                    maxnum, dt = limits.get(action, default_limit)
                    if t >= now - dt:
                        events = surgedict.setdefault(id, {})
                        timestamps = events.setdefault(action, [])
                        timestamps.append((t, surge_indicator))
                except StandardError:
                    pass

        maxnum, dt = limits.get(current_action, default_limit)
        events = surgedict.setdefault(current_id, {})
        timestamps = events.setdefault(current_action, [])
        surge_detected = len(timestamps) > maxnum

        surge_indicator = surge_detected and "!" or ""
        timestamps.append((now, surge_indicator))
        if surge_detected:
            if len(timestamps) < maxnum * 2:
                timestamps.append((now + request.cfg.surge_lockout_time, surge_indicator)) # continue like that and get locked out

        if current_action not in ('cache', 'AttachFile', ): # don't add cache/AttachFile accesses to all or picture galleries will trigger SP
            current_action = 'all' # put a total limit on user's requests
            maxnum, dt = limits.get(current_action, default_limit)
            events = surgedict.setdefault(current_id, {})
            timestamps = events.setdefault(current_action, [])

            if kick: # ban this guy, NOW
                timestamps.extend([(now + request.cfg.surge_lockout_time, "!")] * (2 * maxnum))

            surge_detected = surge_detected or len(timestamps) > maxnum

            surge_indicator = surge_detected and "!" or ""
            timestamps.append((now, surge_indicator))
            if surge_detected:
                if len(timestamps) < maxnum * 2:
                    timestamps.append((now + request.cfg.surge_lockout_time, surge_indicator)) # continue like that and get locked out

        data = []
        for id, events in surgedict.items():
            for action, timestamps in events.items():
                for t, surge_indicator in timestamps:
                    data.append("%s\t%d\t%s\t%s" % (id, t, action, surge_indicator))
        data = "\n".join(data)
        cache.update(data)
    except StandardError:
        pass

    if surge_detected and validuser and request.user.auth_method in request.cfg.auth_methods_trusted:
        logging.info("Trusted user %s would have triggered surge protection if not trusted.", request.user.name)
        return False
    elif surge_detected:
        raise SurgeProtection(retry_after=request.cfg.surge_lockout_time)
    else:
        return False

def redirect_last_visited(request):
    pagetrail = request.user.getTrail()
    if pagetrail:
        # Redirect to last page visited
        last_visited = pagetrail[-1]
        wikiname, pagename = wikiutil.split_interwiki(last_visited)
        if wikiname != 'Self':
            wikitag, wikiurl, wikitail, error = wikiutil.resolve_interwiki(request, wikiname, pagename)
            url = wikiurl + wikiutil.quoteWikinameURL(wikitail)
        else:
            url = Page(request, pagename).url(request)
    else:
        # Or to localized FrontPage
        url = wikiutil.getFrontPage(request).url(request)
    url = request.getQualifiedURL(url)
    return abort(redirect(url))

class UniqueIDGenerator(object):
    def __init__(self, pagename=None):
        self.unique_stack = []
        self.include_stack = []
        self.include_id = None
        self.page_ids = {None: {}}
        self.pagename = pagename

    def push(self):
        """
        Used by the TOC macro, this ensures that the ID namespaces
        are reset to the status when the current include started.
        This guarantees that doing the ID enumeration twice results
        in the same results, on any level.
        """
        self.unique_stack.append((self.page_ids, self.include_id))
        self.include_id, pids = self.include_stack[-1]
        self.page_ids = {}
        for namespace in pids:
            self.page_ids[namespace] = pids[namespace].copy()

    def pop(self):
        """
        Used by the TOC macro to reset the ID namespaces after
        having parsed the page for TOC generation and after
        printing the TOC.
        """
        self.page_ids, self.include_id = self.unique_stack.pop()
        return self.page_ids, self.include_id

    def begin(self, base):
        """
        Called by the formatter when a document begins, which means
        that include causing nested documents gives us an include
        stack in self.include_id_stack.
        """
        pids = {}
        for namespace in self.page_ids:
            pids[namespace] = self.page_ids[namespace].copy()
        self.include_stack.append((self.include_id, pids))
        self.include_id = self(base)
        # if it's the page name then set it to None so we don't
        # prepend anything to IDs, but otherwise keep it.
        if self.pagename and self.pagename == self.include_id:
            self.include_id = None

    def end(self):
        """
        Called by the formatter when a document ends, restores
        the current include ID to the previous one and discards
        the page IDs state we kept around for push().
        """
        self.include_id, pids = self.include_stack.pop()

    def __call__(self, base, namespace=None):
        """
        Generates a unique ID using a given base name. Appends a running count to the base.

        Needs to stay deterministic!

        @param base: the base of the id
        @type base: unicode
        @param namespace: the namespace for the ID, used when including pages

        @returns: a unique (relatively to the namespace) ID
        @rtype: unicode
        """
        if not isinstance(base, unicode):
            base = unicode(str(base), 'ascii', 'ignore')
        if not namespace in self.page_ids:
            self.page_ids[namespace] = {}
        count = self.page_ids[namespace].get(base, -1) + 1
        self.page_ids[namespace][base] = count
        if not count:
            return base
        return u'%s-%d' % (base, count)

FATALTMPL = """
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head><title>%(title)s</title></head>
<body><h1>%(title)s</h1>
<pre>
%(body)s
</pre></body></html>
"""
def fatal_response(error):
    """ Create a response from MoinMoin.error.FatalError instances. """
    html = FATALTMPL % dict(title=error.__class__.__name__,
                            body=str(error))
    return Response(html, status=500, mimetype='text/html')