This file is indexed.

/usr/share/pyshared/includemacro/macros.py is in trac-includemacro 3.0.0~r12030-1.

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
# -*- coding: utf-8 -*-
#
# Copyright (C) 2007-2008 Noah Kantrowitz <noah@coderanger.net>
# Copyright (C) 2012 Ryan J Ollos <ryan.j.ollos@gmail.com>
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution.
#

import urllib2
from StringIO import StringIO

from genshi.core import escape
from genshi.filters.html import HTMLSanitizer
from genshi.input import HTMLParser, ParseError
from trac.core import *
from trac.mimeview.api import Mimeview, get_mimetype, Context
from trac.perm import IPermissionRequestor
from trac.resource import ResourceNotFound
from trac.ticket.model import Ticket
from trac.versioncontrol.api import RepositoryManager
from trac.wiki.formatter import system_message
from trac.wiki.macros import WikiMacroBase
from trac.wiki.model import WikiPage

__all__ = ['IncludeMacro']

class IncludeMacro(WikiMacroBase):
    """A macro to include other resources in wiki pages.
    More documentation to follow.
    """
    
    implements(IPermissionRequestor)
    
    # Default output formats for sources that need them
    default_formats = {
        'wiki': 'text/x-trac-wiki',
    }
    
    # IWikiMacroProvider methods
    def expand_macro(self, formatter, name, content):
        args = [x.strip() for x in content.split(',')]
        if len(args) == 1:
            args.append(None)
        elif len(args) != 2:
            return system_message('Invalid arguments "%s"'%content)
            
        # Pull out the arguments
        source, dest_format = args
        try:
            source_format, source_obj = source.split(':', 1)
        except ValueError: # If no : is present, assume its a wiki page
            source_format, source_obj = 'wiki', source
            
        # Apply a default format if needed
        if dest_format is None:
            try:
                dest_format = self.default_formats[source_format]
            except KeyError:
                pass
        
        if source_format in ('http', 'https', 'ftp'):
            # Since I can't really do recursion checking, and because this 
            # could be a source of abuse allow selectively blocking it.
            # RFE: Allow blacklist/whitelist patterns for URLS. <NPK>
            # RFE: Track page edits and prevent unauthorized users from ever entering a URL include. <NPK>
            if not formatter.perm.has_permission('INCLUDE_URL'):
                self.log.info('IncludeMacro: Blocking attempt by %s to include URL %s on page %s',
                              formatter.req.authname, source, formatter.req.path_info)
                return ''
            try:
                urlf = urllib2.urlopen(source)
                out = urlf.read()  
            except urllib2.URLError, e:
                return system_message('Error while retrieving file', str(e))
            except TracError, e:
                return system_message('Error while previewing', str(e))
            ctxt = Context.from_request(formatter.req)
        elif source_format == 'wiki':
            # XXX: Check for recursion in page includes. <NPK>
            if '@' in source_obj:
                page_name, page_version = source_obj.split('@', 1)
            else:
                page_name, page_version = source_obj, None
            page = WikiPage(self.env, page_name, page_version)
            if not 'WIKI_VIEW' in formatter.perm(page.resource):
                return ''
            if not page.exists:
                if page_version:
                    return system_message('No version "%s" for wiki page "%s"' % (page_version, page_name))
                else:
                    return system_message('Wiki page "%s" does not exist' % page_name)
            out = page.text
            ctxt = Context.from_request(formatter.req, 'wiki', source_obj)
        elif source_format == 'source':
            if not formatter.perm.has_permission('FILE_VIEW'):
                return ''
            out, ctxt, dest_format = self._get_source(formatter, source_obj, dest_format)
        elif source_format == 'ticket':
            if ':' in source_obj:
                ticket_num, source_obj = source_obj.split(':', 1)
                if not Ticket.id_is_valid(ticket_num):
                    return system_message("%s is not a valid ticket id" % ticket_num)
                try:
                    ticket = Ticket(self.env, ticket_num)
                    if not 'TICKET_VIEW' in formatter.perm(ticket.resource):
                        return ''
                except ResourceNotFound, e:
                    return system_message("Ticket %s does not exist" % ticket_num)
                if ':' in source_obj:
                    source_format, comment_num = source_obj.split(':', 1)
                    if source_format == 'comment':
                        changelog = ticket.get_changelog()
                        out = []
                        if changelog:
                            for (ts, author, field, oldval, newval, permanent) in changelog:
                                if field == 'comment' and oldval == comment_num:
                                    dest_format = 'text/x-trac-wiki'
                                    ctxt = Context.from_request(formatter.req, 'ticket', ticket_num)
                                    out = newval
                                    break
                        if not out:
                            return system_message("Comment %s does not exist for Ticket %s" % (comment_num, ticket_num))
                    else:
                        system_message("Unsupported ticket field %s" % source_format)
            else:
                return system_message('Ticket field must be specified')
        else:
            # RFE: Add ticket: and comment: sources. <NPK>
            # RFE: Add attachment: source. <NPK>
            return system_message('Unsupported realm %s' % source)
        
        # If we have a preview format, use it
        if dest_format:
            out = Mimeview(self.env).render(ctxt, dest_format, out)
        
        # Escape if needed
        if not self.config.getbool('wiki', 'render_unsafe_content', False):
            try:
                out = HTMLParser(StringIO(out)).parse() | HTMLSanitizer()
            except ParseError:
                out = escape(out)
        
        return out
            
    # IPermissionRequestor methods
    def get_permission_actions(self):
        yield 'INCLUDE_URL'
    
    # Private methods
    def _get_source(self, formatter, source_obj, dest_format):
        repos_mgr = RepositoryManager(self.env)
        try: #0.12+
            repos_name, repos,source_obj = repos_mgr.get_repository_by_path(source_obj)
        except AttributeError, e: #0.11
            repos = repos_mgr.get_repository(formatter.req.authname)
        path, rev = _split_path(source_obj)
        node = repos.get_node(path, rev)
        out = node.get_content().read()
        if dest_format is None:
            dest_format = node.content_type or get_mimetype(path, out)
        ctxt = Context.from_request(formatter.req, 'source', path)
        
        return out, ctxt, dest_format
    
def _split_path(source_obj):
    if '@' in source_obj:
        path, rev = source_obj.split('@', 1)
    else:
        path, rev = source_obj, None
    return path, rev