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.
150 lines
5.6 KiB
150 lines
5.6 KiB
""" |
|
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 |
|
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): |
|
""" |
|
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()``. |
|
""" |
|
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 |
|
: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. |
|
""" |
|
proxy_add_intercepting_macro(name, macro, pappyproxy.pappy.server_factory.intercepting_macros) |
|
|
|
def remove_intercepting_macro(name): |
|
""" |
|
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. |
|
""" |
|
proxy_remove_intercepting_macro(name, pappyproxy.pappy.server_factory.intercepting_macros) |
|
|
|
def active_intercepting_macros(): |
|
""" |
|
Returns a list of the active intercepting macro objects. Modifying this list |
|
will not affect which macros are active. |
|
""" |
|
return pappyproxy.pappy.server_factory.intercepting_macros[:] |
|
|
|
def in_memory_reqs(): |
|
""" |
|
Returns a list containing all out 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_save` to save the request to the data file. |
|
""" |
|
return list(pappyproxy.context.Context.in_memory_requests) |
|
|
|
def all_reqs(): |
|
""" |
|
Returns a list containing all the requests in history (including requests |
|
that only exist in memory). Modifying this list will not modify requests |
|
included in the history. However, you can edit the requests |
|
in this list then call either :func:`pappyproxy.http.Request.save` or |
|
:func:`pappyproxy.http.Request.async_save` to modify the actual request. |
|
""" |
|
return list(pappyproxy.context.Context.all_reqs) |
|
|
|
def main_context(): |
|
""" |
|
Returns the context object representing the main context. Use this to interact |
|
with the context. The returned object can be modified |
|
at will. Avoid modifying any class values (ie all_reqs, in_memory_requests) |
|
and use the class methods to add/remove requests. See the documentation on |
|
:class:`pappyproxy.context.Context` for more information. |
|
""" |
|
return pappyproxy.pappy.main_context |
|
|
|
def add_req(req): |
|
""" |
|
Adds a request to the history. Will not do anything to requests which are |
|
already in history. If the request is not saved, it will be given an m## id. |
|
""" |
|
pappyproxy.pappy.main_context.add_request(req) |
|
|
|
def run_cmd(cmd): |
|
""" |
|
Run a command as if you typed it into the console. Try and use existing APIs |
|
to do what you want before using this. |
|
""" |
|
pappyproxy.pappy.cons.onecmd(cmd)
|
|
|