A fork of pappy proxy
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

195 lines
6.9 KiB

9 years ago
"""
This module contains all the api calls written for use in plugins. If you want
to do anything that is't allowed through these function calls or through the
functions provided for macros, contact me and I'll see what I can do to add some
more functionality into the next version.
"""
import glob
import imp
import os
import pappyproxy
import stat
from .proxy import add_intercepting_macro as proxy_add_intercepting_macro
from .proxy import remove_intercepting_macro as proxy_remove_intercepting_macro
9 years ago
from .colors import Colors
9 years ago
from .util import PappyException
class Plugin(object):
def __init__(self, cmd, fname=None):
self.cmd = cmd
self.filename = ''
self.source = None
self.module_name = ''
if fname:
self.filename = fname
self.load_file(fname)
def load_file(self, fname):
module_name = os.path.basename(os.path.splitext(fname)[0])
if os.path.basename(fname) == '__init__.py':
return
st = os.stat(fname)
if (st.st_mode & stat.S_IWOTH):
raise PappyException("Refusing to load world-writable plugin: %s" % fname)
self.source = imp.load_source('%s'%module_name, fname)
if hasattr(self.source, 'load_cmds'):
self.source.load_cmds(self.cmd)
else:
print ('WARNING: %s does not define load_cmds. It will not be '
'possible to interact with the plugin through the console.' % fname)
self.module_name = module_name
class PluginLoader(object):
def __init__(self, cmd):
self.cmd = cmd
self.loaded_plugins = []
self.plugins_by_name = {}
def load_plugin(self, fname):
p = Plugin(self.cmd, fname)
self.loaded_plugins.append(p)
self.plugins_by_name[p.module_name] = p
def load_directory(self, directory):
fnames = glob.glob(os.path.join(directory, '*.py'))
for fname in fnames:
try:
self.load_plugin(fname)
except PappyException as e:
print str(e)
##########################
## Plugin helper functions
def plugin_by_name(name):
"""
9 years ago
Returns an interface to access the methods of a plugin from its
name. For example, to call the ``foo`` function from the ``bar``
plugin you would call ``plugin_by_name('bar').foo()``.
9 years ago
"""
import pappyproxy.pappy
if name in pappyproxy.pappy.plugin_loader.plugins_by_name:
return pappyproxy.pappy.plugin_loader.plugins_by_name[name].source
else:
raise PappyException('No plugin with name %s is loaded' % name)
def add_intercepting_macro(name, macro):
"""
Adds an intercepting macro to the proxy. You can either use a
9 years ago
:class:`pappyproxy.macros.FileInterceptMacro` to load an
intercepting macro from the disk, or you can create your own using
an :class:`pappyproxy.macros.InterceptMacro` for a base class. You
must give a unique name that will be used in
:func:`pappyproxy.plugin.remove_intercepting_macro` to deactivate
it. Remember that activating an intercepting macro will disable
request streaming and will affect performance. So please try and
only use this if you may need to modify messages before they are
passed along.
9 years ago
"""
9 years ago
for factory in pappyproxy.pappy.session.server_factories:
9 years ago
proxy_add_intercepting_macro(name, macro, factory.intercepting_macros)
9 years ago
def remove_intercepting_macro(name):
"""
9 years ago
Stops an active intercepting macro. You must pass in the name that
you used when calling
:func:`pappyproxy.plugin.add_intercepting_macro` to identify which
macro you would like to stop.
9 years ago
"""
9 years ago
for factory in pappyproxy.pappy.session.server_factories:
9 years ago
proxy_remove_intercepting_macro(name, factory.intercepting_macros)
9 years ago
def active_intercepting_macros():
"""
9 years ago
Returns a dict of the active intercepting macro objects. Modifying
9 years ago
this list will not affect which macros are active.
9 years ago
"""
9 years ago
ret = {}
9 years ago
for factory in pappyproxy.pappy.session.server_factories:
9 years ago
for k, v in factory.intercepting_macros.iteritems():
ret[k] = v
9 years ago
return ret
9 years ago
def in_memory_reqs():
"""
9 years ago
Returns a list containing the ids of the requests which exist in
memory only (requests with an m## style id). You can call either
:func:`pappyproxy.http.Request.save` or
:func:`pappyproxy.http.Request.async_deep_save` to save the
request to the data file.
9 years ago
"""
9 years ago
return list(pappyproxy.http.Request.cache.inmem_reqs)
9 years ago
9 years ago
def req_history(num=-1, ids=None, include_unmangled=False):
9 years ago
"""
9 years ago
Returns an a generator that generates deferreds which resolve to
requests in history, ignoring the current context. If ``n`` is
given, it will stop after ``n`` requests have been generated. If
``ids`` is given, it will only include those IDs. If
``include_unmangled`` is True, then the iterator will include
requests which are the unmangled version of other requests.
9 years ago
9 years ago
An example of using the iterator to print the 10 most recent requests::
@defer.inlineCallbacks
def find_food():
for req_d in req_history(10):
req = yield req_d
print '-'*10
print req.full_message_pretty
9 years ago
"""
9 years ago
return pappyproxy.Request.cache.req_it(num=num, ids=ids, include_unmangled=include_unmangled)
9 years ago
9 years ago
def main_context_ids(n=-1):
9 years ago
"""
9 years ago
Returns a deferred that resolves into a list of up to ``n`` of the
most recent requests in the main context. You can then use
:func:`pappyproxy.http.Request.load_request` to load the requests
in the current context. If no value is passed for ``n``, this will
return all of the IDs in the context.
9 years ago
"""
9 years ago
return pappyproxy.pappy.main_context.get_reqs(n)
9 years ago
def run_cmd(cmd):
"""
9 years ago
Run a command as if you typed it into the console. Try and use
existing APIs to do what you want before using this.
9 years ago
"""
pappyproxy.pappy.cons.onecmd(cmd)
9 years ago
def require_modules(*largs):
"""
A wrapper to make sure that plugin dependencies are installed. For example,
if a command requires the ``psutil`` and ``objgraph`` package, you should
format your command like::
@require_modules('psutil', 'objgraph')
def my_command(line):
import objgraph
import psutil
# ... rest of command ...
If you try to run the command without being able to import all of the required
modules, the command will print an error and not run the command.
"""
def wr(func):
def wr2(*args, **kwargs):
missing = []
for l in largs:
try:
imp.find_module(l)
except ImportError:
missing.append(l)
if missing:
print 'Command requires %s module(s)' % (', '.join([Colors.RED+m+Colors.ENDC for m in missing]))
else:
return func(*args, **kwargs)
return wr2
return wr