aboutsummaryrefslogtreecommitdiffstats
path: root/libmproxy/console
diff options
context:
space:
mode:
Diffstat (limited to 'libmproxy/console')
-rw-r--r--libmproxy/console/__init__.py1
-rw-r--r--libmproxy/console/common.py121
-rw-r--r--libmproxy/console/flowdetailview.py5
-rw-r--r--libmproxy/console/flowlist.py19
-rw-r--r--libmproxy/console/flowview.py18
-rw-r--r--libmproxy/console/grideditor.py6
-rw-r--r--libmproxy/console/help.py1
-rw-r--r--libmproxy/console/options.py1
-rw-r--r--libmproxy/console/palettepicker.py1
-rw-r--r--libmproxy/console/palettes.py2
-rw-r--r--libmproxy/console/pathedit.py2
-rw-r--r--libmproxy/console/searchable.py2
-rw-r--r--libmproxy/console/select.py5
-rw-r--r--libmproxy/console/signals.py2
-rw-r--r--libmproxy/console/statusbar.py2
-rw-r--r--libmproxy/console/tabs.py3
-rw-r--r--libmproxy/console/window.py1
17 files changed, 151 insertions, 41 deletions
diff --git a/libmproxy/console/__init__.py b/libmproxy/console/__init__.py
index 3d0fb1aa..80a6e28a 100644
--- a/libmproxy/console/__init__.py
+++ b/libmproxy/console/__init__.py
@@ -22,6 +22,7 @@ EVENTLOG_SIZE = 500
class ConsoleState(flow.State):
+
def __init__(self):
flow.State.__init__(self)
self.focus = None
diff --git a/libmproxy/console/common.py b/libmproxy/console/common.py
index 12fdfe27..276e45c2 100644
--- a/libmproxy/console/common.py
+++ b/libmproxy/console/common.py
@@ -207,7 +207,7 @@ def raw_format_flow(f, focus, extended, padding):
# Save file to disk
-def save_data(path, data, master, state):
+def save_data(path, data):
if not path:
return
try:
@@ -217,32 +217,32 @@ def save_data(path, data, master, state):
signals.status_message.send(message=v.strerror)
-def ask_save_overwite(path, data, master, state):
+def ask_save_overwrite(path, data):
if not path:
return
path = os.path.expanduser(path)
if os.path.exists(path):
- def save_overwite(k):
+ def save_overwrite(k):
if k == "y":
- save_data(path, data, master, state)
+ save_data(path, data)
signals.status_prompt_onekey.send(
- prompt = "'" + path + "' already exists. Overwite?",
+ prompt = "'" + path + "' already exists. Overwrite?",
keys = (
("yes", "y"),
("no", "n"),
),
- callback = save_overwite
+ callback = save_overwrite
)
else:
- save_data(path, data, master, state)
+ save_data(path, data)
-def ask_save_path(prompt, data, master, state):
+def ask_save_path(prompt, data):
signals.status_prompt_path.send(
prompt = prompt,
- callback = ask_save_overwite,
- args = (data, master, state)
+ callback = ask_save_overwrite,
+ args = (data, )
)
@@ -277,26 +277,60 @@ def copy_flow_format_data(part, scope, flow):
return data, False
-def copy_flow(part, scope, flow, master, state):
- """
- part: _c_ontent, _h_eaders+content, _u_rl
- scope: _a_ll, re_q_uest, re_s_ponse
- """
- data, err = copy_flow_format_data(part, scope, flow)
+def export_prompt(k, flow):
+ if k == "c":
+ copy_as_curl_command(flow)
+ elif k == "p":
+ copy_as_python_code(flow)
+ elif k == "r":
+ copy_as_raw_request(flow)
- if err:
- signals.status_message.send(message=err)
- return
- if not data:
- if scope == "q":
- signals.status_message.send(message="No request content to copy.")
- elif scope == "s":
- signals.status_message.send(message="No response content to copy.")
- else:
- signals.status_message.send(message="No contents to copy.")
+def copy_as_curl_command(flow):
+ data = "curl "
+
+ for k, v in flow.request.headers.fields:
+ data += "-H '%s:%s' " % (k, v)
+
+ if flow.request.method != "GET":
+ data += "-X %s " % flow.request.method
+
+ full_url = flow.request.scheme + "://" + flow.request.host + flow.request.path
+ data += "'%s'" % full_url
+
+ if flow.request.content:
+ data += " --data-binary '%s'" % flow.request.content
+
+ copy_to_clipboard_or_prompt(data)
+
+
+def copy_as_python_code(flow):
+ if flow.request.method != "GET":
+ signals.status_message.send(message="Currently, only GET methods are supported")
return
+ data = ("import requests\n"
+ "headers = {%s}\n"
+ "url = '%s'\n"
+ "resp = requests.get(url, headers=headers)")
+
+ headers = "\n"
+ for k, v in flow.request.headers.fields:
+ headers += " '%s': '%s',\n" % (k, v)
+
+ full_url = flow.request.scheme + "://" + flow.request.host + flow.request.path
+
+ data = data % (headers, full_url)
+
+ copy_to_clipboard_or_prompt(data)
+
+
+def copy_as_raw_request(flow):
+ data = netlib.http.http1.assemble_request(flow.request)
+ copy_to_clipboard_or_prompt(data)
+
+
+def copy_to_clipboard_or_prompt(data):
# pyperclip calls encode('utf-8') on data to be copied without checking.
# if data are already encoded that way UnicodeDecodeError is thrown.
toclip = ""
@@ -310,7 +344,7 @@ def copy_flow(part, scope, flow, master, state):
except (RuntimeError, UnicodeDecodeError, AttributeError):
def save(k):
if k == "y":
- ask_save_path("Save data", data, master, state)
+ ask_save_path("Save data", data)
signals.status_prompt_onekey.send(
prompt = "Cannot copy data to clipboard. Save as file?",
keys = (
@@ -321,6 +355,29 @@ def copy_flow(part, scope, flow, master, state):
)
+def copy_flow(part, scope, flow, master, state):
+ """
+ part: _c_ontent, _h_eaders+content, _u_rl
+ scope: _a_ll, re_q_uest, re_s_ponse
+ """
+ data, err = copy_flow_format_data(part, scope, flow)
+
+ if err:
+ signals.status_message.send(message=err)
+ return
+
+ if not data:
+ if scope == "q":
+ signals.status_message.send(message="No request content to copy.")
+ elif scope == "s":
+ signals.status_message.send(message="No response content to copy.")
+ else:
+ signals.status_message.send(message="No contents to copy.")
+ return
+
+ copy_to_clipboard_or_prompt(data)
+
+
def ask_copy_part(scope, flow, master, state):
choices = [
("content", "c"),
@@ -367,16 +424,12 @@ def ask_save_body(part, master, state, flow):
elif part == "q" and request_has_content:
ask_save_path(
"Save request content",
- flow.request.get_decoded_content(),
- master,
- state
+ flow.request.get_decoded_content()
)
elif part == "s" and response_has_content:
ask_save_path(
"Save response content",
- flow.response.get_decoded_content(),
- master,
- state
+ flow.response.get_decoded_content()
)
else:
signals.status_message.send(message="No content to save.")
@@ -386,7 +439,7 @@ flowcache = utils.LRUCache(800)
def format_flow(f, focus, extended=False, hostheader=False, padding=2,
- marked=False):
+ marked=False):
d = dict(
intercepted = f.intercepted,
acked = f.reply.acked,
diff --git a/libmproxy/console/flowdetailview.py b/libmproxy/console/flowdetailview.py
index 40769c95..f4b4262e 100644
--- a/libmproxy/console/flowdetailview.py
+++ b/libmproxy/console/flowdetailview.py
@@ -9,7 +9,6 @@ def maybe_timestamp(base, attr):
return utils.format_timestamp_with_milli(getattr(base, attr))
else:
return "active"
- pass
def flowdetails(state, flow):
@@ -20,7 +19,7 @@ def flowdetails(state, flow):
req = flow.request
resp = flow.response
- if sc:
+ if sc is not None:
text.append(urwid.Text([("head", "Server Connection:")]))
parts = [
["Address", "%s:%s" % sc.address()],
@@ -76,7 +75,7 @@ def flowdetails(state, flow):
common.format_keyvals(parts, key="key", val="text", indent=4)
)
- if cc:
+ if cc is not None:
text.append(urwid.Text([("head", "Client Connection:")]))
parts = [
diff --git a/libmproxy/console/flowlist.py b/libmproxy/console/flowlist.py
index 2b77f4a3..c2201055 100644
--- a/libmproxy/console/flowlist.py
+++ b/libmproxy/console/flowlist.py
@@ -1,7 +1,6 @@
from __future__ import absolute_import
import urwid
-from netlib import http
import netlib.utils
from . import common, signals
@@ -16,6 +15,7 @@ def _mkhelp():
("C", "clear flow list or eventlog"),
("d", "delete flow"),
("D", "duplicate flow"),
+ ("E", "export"),
("e", "toggle eventlog"),
("F", "toggle follow flow list"),
("l", "set limit filter pattern"),
@@ -43,6 +43,7 @@ footer = [
class EventListBox(urwid.ListBox):
+
def __init__(self, master):
self.master = master
urwid.ListBox.__init__(self, master.eventlist)
@@ -60,6 +61,7 @@ class EventListBox(urwid.ListBox):
class BodyPile(urwid.Pile):
+
def __init__(self, master):
h = urwid.Text("Event log")
h = urwid.Padding(h, align="left", width=("relative", 100))
@@ -103,6 +105,7 @@ class BodyPile(urwid.Pile):
class ConnectionItem(urwid.WidgetWrap):
+
def __init__(self, master, state, flow, focus):
self.master, self.state, self.flow = master, state, flow
self.f = focus
@@ -254,6 +257,18 @@ class ConnectionItem(urwid.WidgetWrap):
)
elif key == "P":
common.ask_copy_part("a", self.flow, self.master, self.state)
+ elif key == "E":
+ signals.status_prompt_onekey.send(
+ self,
+ prompt = "Export",
+ keys = (
+ ("as curl command", "c"),
+ ("as python code", "p"),
+ ("as raw request", "r"),
+ ),
+ callback = common.export_prompt,
+ args = (self.flow,)
+ )
elif key == "b":
common.ask_save_body(None, self.master, self.state, self.flow)
else:
@@ -261,6 +276,7 @@ class ConnectionItem(urwid.WidgetWrap):
class FlowListWalker(urwid.ListWalker):
+
def __init__(self, master, state):
self.master, self.state = master, state
signals.flowlist_change.connect(self.sig_flowlist_change)
@@ -289,6 +305,7 @@ class FlowListWalker(urwid.ListWalker):
class FlowListBox(urwid.ListBox):
+
def __init__(self, master):
self.master = master
urwid.ListBox.__init__(
diff --git a/libmproxy/console/flowview.py b/libmproxy/console/flowview.py
index 0038558b..8102de55 100644
--- a/libmproxy/console/flowview.py
+++ b/libmproxy/console/flowview.py
@@ -25,8 +25,9 @@ def _mkhelp():
("A", "accept all intercepted flows"),
("a", "accept this intercepted flow"),
("b", "save request/response body"),
- ("d", "delete flow"),
("D", "duplicate flow"),
+ ("d", "delete flow"),
+ ("E", "export"),
("e", "edit request/response"),
("f", "load full body data"),
("m", "change body display mode for this entity"),
@@ -95,6 +96,7 @@ footer = [
class FlowViewHeader(urwid.WidgetWrap):
+
def __init__(self, master, f):
self.master, self.flow = master, f
self._w = common.format_flow(
@@ -217,7 +219,7 @@ class FlowView(tabs.Tabs):
txt = []
for (style, text) in line:
if total_chars + len(text) > max_chars:
- text = text[:max_chars-total_chars]
+ text = text[:max_chars - total_chars]
txt.append((style, text))
total_chars += len(text)
if total_chars == max_chars:
@@ -574,6 +576,18 @@ class FlowView(tabs.Tabs):
callback = self.master.save_one_flow,
args = (self.flow,)
)
+ elif key == "E":
+ signals.status_prompt_onekey.send(
+ self,
+ prompt = "Export",
+ keys = (
+ ("as curl command", "c"),
+ ("as python code", "p"),
+ ("as raw request", "r"),
+ ),
+ callback = common.export_prompt,
+ args = (self.flow,)
+ )
elif key == "|":
signals.status_prompt_path.send(
prompt = "Send flow to script",
diff --git a/libmproxy/console/grideditor.py b/libmproxy/console/grideditor.py
index 237eea28..a11c962c 100644
--- a/libmproxy/console/grideditor.py
+++ b/libmproxy/console/grideditor.py
@@ -63,6 +63,7 @@ class TextColumn:
class SubgridColumn:
+
def __init__(self, heading, subeditor):
self.heading = heading
self.subeditor = subeditor
@@ -97,6 +98,7 @@ class SubgridColumn:
class SEscaped(urwid.WidgetWrap):
+
def __init__(self, txt):
txt = txt.encode("string-escape")
w = urwid.Text(txt, wrap="any")
@@ -113,6 +115,7 @@ class SEscaped(urwid.WidgetWrap):
class SEdit(urwid.WidgetWrap):
+
def __init__(self, txt):
txt = txt.encode("string-escape")
w = urwid.Edit(edit_text=txt, wrap="any", multiline=True)
@@ -127,6 +130,7 @@ class SEdit(urwid.WidgetWrap):
class GridRow(urwid.WidgetWrap):
+
def __init__(self, focused, editing, editor, values):
self.focused, self.editing, self.editor = focused, editing, editor
@@ -172,6 +176,7 @@ class GridRow(urwid.WidgetWrap):
class GridWalker(urwid.ListWalker):
+
"""
Stores rows as a list of (rows, errors) tuples, where rows is a list
and errors is a set with an entry of each offset in rows that is an
@@ -311,6 +316,7 @@ class GridWalker(urwid.ListWalker):
class GridListBox(urwid.ListBox):
+
def __init__(self, lw):
urwid.ListBox.__init__(self, lw)
diff --git a/libmproxy/console/help.py b/libmproxy/console/help.py
index 74748030..0c264ebf 100644
--- a/libmproxy/console/help.py
+++ b/libmproxy/console/help.py
@@ -12,6 +12,7 @@ footer = [
class HelpView(urwid.ListBox):
+
def __init__(self, help_context):
self.help_context = help_context or []
urwid.ListBox.__init__(
diff --git a/libmproxy/console/options.py b/libmproxy/console/options.py
index a365a78c..5c9e0cc9 100644
--- a/libmproxy/console/options.py
+++ b/libmproxy/console/options.py
@@ -22,6 +22,7 @@ help_context = _mkhelp()
class Options(urwid.WidgetWrap):
+
def __init__(self, master):
self.master = master
self.lb = select.Select(
diff --git a/libmproxy/console/palettepicker.py b/libmproxy/console/palettepicker.py
index 7e2c10cd..51ad0606 100644
--- a/libmproxy/console/palettepicker.py
+++ b/libmproxy/console/palettepicker.py
@@ -18,6 +18,7 @@ help_context = _mkhelp()
class PalettePicker(urwid.WidgetWrap):
+
def __init__(self, master):
self.master = master
low, high = [], []
diff --git a/libmproxy/console/palettes.py b/libmproxy/console/palettes.py
index d897a0a2..bd370181 100644
--- a/libmproxy/console/palettes.py
+++ b/libmproxy/console/palettes.py
@@ -65,6 +65,7 @@ class Palette:
class LowDark(Palette):
+
"""
Low-color dark background
"""
@@ -129,6 +130,7 @@ class Dark(LowDark):
class LowLight(Palette):
+
"""
Low-color light background
"""
diff --git a/libmproxy/console/pathedit.py b/libmproxy/console/pathedit.py
index dccec14a..4447070b 100644
--- a/libmproxy/console/pathedit.py
+++ b/libmproxy/console/pathedit.py
@@ -5,6 +5,7 @@ import urwid
class _PathCompleter:
+
def __init__(self, _testing=False):
"""
_testing: disables reloading of the lookup table to make testing
@@ -55,6 +56,7 @@ class _PathCompleter:
class PathEdit(urwid.Edit, _PathCompleter):
+
def __init__(self, *args, **kwargs):
urwid.Edit.__init__(self, *args, **kwargs)
_PathCompleter.__init__(self)
diff --git a/libmproxy/console/searchable.py b/libmproxy/console/searchable.py
index dea0ac7f..cff1f0a1 100644
--- a/libmproxy/console/searchable.py
+++ b/libmproxy/console/searchable.py
@@ -4,6 +4,7 @@ from . import signals
class Highlight(urwid.AttrMap):
+
def __init__(self, t):
urwid.AttrMap.__init__(
self,
@@ -14,6 +15,7 @@ class Highlight(urwid.AttrMap):
class Searchable(urwid.ListBox):
+
def __init__(self, state, contents):
self.walker = urwid.SimpleFocusListWalker(contents)
urwid.ListBox.__init__(self, self.walker)
diff --git a/libmproxy/console/select.py b/libmproxy/console/select.py
index bf96a785..928a7ca5 100644
--- a/libmproxy/console/select.py
+++ b/libmproxy/console/select.py
@@ -4,6 +4,7 @@ from . import common
class _OptionWidget(urwid.WidgetWrap):
+
def __init__(self, option, text, shortcut, active, focus):
self.option = option
textattr = "text"
@@ -36,6 +37,7 @@ class _OptionWidget(urwid.WidgetWrap):
class OptionWalker(urwid.ListWalker):
+
def __init__(self, options):
urwid.ListWalker.__init__(self)
self.options = options
@@ -59,6 +61,7 @@ class OptionWalker(urwid.ListWalker):
class Heading:
+
def __init__(self, text):
self.text = text
@@ -73,6 +76,7 @@ _neg = lambda: False
class Option:
+
def __init__(self, text, shortcut, getstate=None, activate=None):
self.text = text
self.shortcut = shortcut
@@ -89,6 +93,7 @@ class Option:
class Select(urwid.ListBox):
+
def __init__(self, options):
self.walker = OptionWalker(options)
urwid.ListBox.__init__(
diff --git a/libmproxy/console/signals.py b/libmproxy/console/signals.py
index 52f72d34..6a439bf3 100644
--- a/libmproxy/console/signals.py
+++ b/libmproxy/console/signals.py
@@ -2,6 +2,8 @@ import blinker
# Show a status message in the action bar
sig_add_event = blinker.Signal()
+
+
def add_event(e, level):
sig_add_event.send(
None,
diff --git a/libmproxy/console/statusbar.py b/libmproxy/console/statusbar.py
index fd084f8c..4cc63a54 100644
--- a/libmproxy/console/statusbar.py
+++ b/libmproxy/console/statusbar.py
@@ -7,6 +7,7 @@ from . import pathedit, signals, common
class ActionBar(urwid.WidgetWrap):
+
def __init__(self):
urwid.WidgetWrap.__init__(self, None)
self.clear()
@@ -108,6 +109,7 @@ class ActionBar(urwid.WidgetWrap):
class StatusBar(urwid.WidgetWrap):
+
def __init__(self, master, helptext):
self.master, self.helptext = master, helptext
self.ab = ActionBar()
diff --git a/libmproxy/console/tabs.py b/libmproxy/console/tabs.py
index fea9bbde..b5423038 100644
--- a/libmproxy/console/tabs.py
+++ b/libmproxy/console/tabs.py
@@ -1,8 +1,8 @@
import urwid
-import signals
class Tab(urwid.WidgetWrap):
+
def __init__(self, offset, content, attr, onclick):
"""
onclick is called on click with the tab offset as argument
@@ -21,6 +21,7 @@ class Tab(urwid.WidgetWrap):
class Tabs(urwid.WidgetWrap):
+
def __init__(self, tabs, tab_offset=0):
urwid.WidgetWrap.__init__(self, "")
self.tab_offset = tab_offset
diff --git a/libmproxy/console/window.py b/libmproxy/console/window.py
index 69d5e242..47c284e4 100644
--- a/libmproxy/console/window.py
+++ b/libmproxy/console/window.py
@@ -3,6 +3,7 @@ from . import signals
class Window(urwid.Frame):
+
def __init__(self, master, body, header, footer, helpctx):
urwid.Frame.__init__(
self,