This file is indexed.

/usr/lib/python2.7/dist-packages/examples/utilapplication.py is in python-starpy 1.0.1.0.git.20140806-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
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
#
# StarPy -- Asterisk Protocols for Twisted
# 
# Copyright (c) 2006, Michael C. Fletcher
#
# Michael C. Fletcher <mcfletch@vrplumber.com>
#
# See http://asterisk-org.github.com/starpy/ for more information about the
# StarPy project. Please do not directly contact any of the maintainers of this
# project for assistance; the project provides a web site, mailing lists and
# IRC channels for your use.
#
# This program is free software, distributed under the terms of the
# BSD 3-Clause License. See the LICENSE file at the top of the source tree for
# details.

"""Class providing utility applications with common support code"""
from basicproperty import common, propertied, basic, weak
from ConfigParser import ConfigParser
from starpy import fastagi, manager
from twisted.internet import defer, reactor
import logging,os

log = logging.getLogger( 'app' )

class UtilApplication( propertied.Propertied ):
    """Utility class providing simple application-level operations

    FastAGI entry points are waitForCallOn and handleCallsFor, which allow
    for one-shot and permanant handling of calls for an extension
    (respectively), and agiSpecifier, which is loaded from configuration file
    (as specified in self.configFiles).
    """
    amiSpecifier = basic.BasicProperty(
        "amiSpecifier", """AMI connection specifier for the application see AMISpecifier""",
        defaultFunction = lambda prop,client: AMISpecifier()
    )
    agiSpecifier = basic.BasicProperty(
        "agiSpecifier", """FastAGI server specifier for the application see AGISpecifier""",
        defaultFunction = lambda prop,client: AGISpecifier()
    )
    extensionWaiters = common.DictionaryProperty(
        "extensionWaiters", """Set of deferreds waiting for incoming extensions""",
    )
    extensionHandlers = common.DictionaryProperty(
        "extensionHandlers", """Set of permanant callbacks waiting for incoming extensions""",
    )
    configFiles = configFiles=('starpy.conf','~/.starpy.conf')
    def __init__( self ):
        """Initialise the application from options in configFile"""
        self.loadConfigurations()
    def loadConfigurations( self ):
        parser = self._loadConfigFiles( self.configFiles )
        self._copyPropertiesFrom( parser, 'AMI', self.amiSpecifier )
        self._copyPropertiesFrom( parser, 'FastAGI', self.agiSpecifier )
        return parser
    def _loadConfigFiles( self, configFiles ):
        """Load options from configuration files given (if present)"""
        parser = ConfigParser( )
        filenames = [
            os.path.abspath( os.path.expandvars( os.path.expanduser( file ) ))
            for file in configFiles
        ]
        log.info( "Possible configuration files:\n\t%s", "\n\t".join(filenames) or None)
        filenames = [
            file for file in filenames
            if os.path.isfile(file)
        ]
        log.info( "Actual configuration files:\n\t%s", "\n\t".join(filenames) or None)
        parser.read( filenames )
        return parser
    def _copyPropertiesFrom( self, parser, section, client, properties=None ):
        """Copy properties from the config-parser's given section into client"""
        if properties is None:
            properties = client.getProperties()
        for property in properties:
            if parser.has_option( section, property.name ):
                try:
                    value = parser.get( section, property.name )
                    setattr( client, property.name, value )
                except (TypeError,ValueError,AttributeError,NameError), err:
                    log( """Unable to set property %r of %r to config-file value %r: %s"""%(
                        property.name, client, parser.get( section, property.name, 1), err,
                    ))
        return client
    def dispatchIncomingCall( self, agi ):
        """Handle an incoming call (dispatch to the appropriate registered handler)"""
        extension = agi.variables['agi_extension']
        log.info( """AGI connection with extension: %r""",  extension )
        try:
            df = self.extensionWaiters.pop( extension )
        except KeyError, err:
            try:
                callback = self.extensionHandlers[ extension ]
            except KeyError, err:
                try:
                    callback = self.extensionHandlers[ None ]
                except KeyError, err:
                    log.warn( """Unexpected connection to extension %r: %s""", extension, agi.variables )
                    agi.finish()
                    return
            try:
                return callback( agi )
            except Exception, err:
                log.error( """Failure during callback %s for agi %s: %s""", callback, agi.variables, err )
                # XXX return a -1 here
        else:
            if not df.called:
                df.callback( agi )
    def waitForCallOn( self, extension, timeout=15 ):
        """Wait for an AGI call on extension given

        extension -- string extension for which to wait
        timeout -- duration in seconds to wait before defer.TimeoutError is
            returned to the deferred.

        Note that waiting callback overrides any registered handler; that is,
        if you register one callback with waitForCallOn and another with
        handleCallsFor, the first incoming call will trigger the waitForCallOn
        handler.

        returns deferred returning connected FastAGIProtocol or an error
        """
        extension = str(extension)
        log.info( 'Waiting for extension %r for %s seconds', extension, timeout )
        df = defer.Deferred( )
        self.extensionWaiters[ extension ] = df
        def onTimeout( ):
            if not df.called:
                df.errback( defer.TimeoutError(
                    """Timeout waiting for call on extension: %r"""%(extension,)
                ))
        reactor.callLater( timeout, onTimeout )
        return df
    def handleCallsFor( self, extension, callback ):
        """Register permanant handler for given extension

        extension -- string extension for which to wait or None to define
            a default handler (that chosen if there is not explicit handler
            or waiter)
        callback -- callback function to be called for each incoming channel
            to the given extension.

        Note that waiting callback overrides any registered handler; that is,
        if you register one callback with waitForCallOn and another with
        handleCallsFor, the first incoming call will trigger the waitForCallOn
        handler.

        returns None
        """
        if extension is not None:
            extension = str(extension)
        self.extensionHandlers[ extension ] = callback

class AMISpecifier( propertied.Propertied ):
    """Manager interface setup/specifier"""
    username = common.StringLocaleProperty(
        "username", """Login username for the manager interface""",
    )
    secret = common.StringLocaleProperty(
        "secret", """Login secret for the manager interface""",
    )
    password = secret
    server = common.StringLocaleProperty(
        "server", """Server IP address to which to connect""",
        defaultValue = '127.0.0.1',
    )
    port = common.IntegerProperty(
        "port", """Server IP port to which to connect""",
        defaultValue = 5038,
    )
    timeout = common.FloatProperty(
        "timeout", """Timeout in seconds for an AMI connection timeout""",
        defaultValue = 5.0,
    )
    def login( self ):
        """Login to the specified manager via the AMI"""
        theManager = manager.AMIFactory(self.username, self.secret)
        return theManager.login(self.server, self.port, timeout=self.timeout)

class AGISpecifier( propertied.Propertied ):
    """Specifier of where we send the user to connect to our AGI"""
    port = common.IntegerProperty(
        "port", """IP port on which to listen""",
        defaultValue = 4573,
    )
    interface = common.StringLocaleProperty(
        "interface", """IP interface on which to listen (local only by default)""",
        defaultValue = '127.0.0.1',
    )
    context = common.StringLocaleProperty(
        "context", """Asterisk context to which to connect incoming calls""",
        defaultValue = 'survey',
    )
    def run( self, mainFunction ):
        """Start up the AGI server with the given mainFunction"""
        f = fastagi.FastAGIFactory(mainFunction)
        return reactor.listenTCP(self.port, f, 50, self.interface)