aboutsummaryrefslogtreecommitdiffstats
path: root/libmproxy
diff options
context:
space:
mode:
Diffstat (limited to 'libmproxy')
-rw-r--r--libmproxy/cmdline.py7
-rw-r--r--libmproxy/console/__init__.py58
-rw-r--r--libmproxy/console/flowlist.py29
-rw-r--r--libmproxy/console/flowview.py4
-rw-r--r--libmproxy/console/help.py59
-rw-r--r--libmproxy/console/options.py310
-rw-r--r--libmproxy/console/palettepicker.py81
-rw-r--r--libmproxy/console/palettes.py71
-rw-r--r--libmproxy/console/searchable.py16
-rw-r--r--libmproxy/console/select.py107
-rw-r--r--libmproxy/console/signals.py2
-rw-r--r--libmproxy/console/statusbar.py1
-rw-r--r--libmproxy/console/window.py67
-rw-r--r--libmproxy/flow.py6
-rw-r--r--libmproxy/main.py1
15 files changed, 516 insertions, 303 deletions
diff --git a/libmproxy/cmdline.py b/libmproxy/cmdline.py
index 28aa3ea6..7ea9be8c 100644
--- a/libmproxy/cmdline.py
+++ b/libmproxy/cmdline.py
@@ -575,12 +575,17 @@ def mitmproxy():
)
common_options(parser)
parser.add_argument(
- "--palette", type=str, default="dark",
+ "--palette", type=str, default=palettes.DEFAULT,
action="store", dest="palette",
choices=sorted(palettes.palettes.keys()),
help="Select color palette: " + ", ".join(palettes.palettes.keys())
)
parser.add_argument(
+ "--palette-transparent",
+ action="store_true", dest="palette_transparent", default=False,
+ help="Set transparent background for palette."
+ )
+ parser.add_argument(
"-e", "--eventlog",
action="store_true", dest="eventlog",
help="Show event log."
diff --git a/libmproxy/console/__init__.py b/libmproxy/console/__init__.py
index 3b3f2df2..5c2402f6 100644
--- a/libmproxy/console/__init__.py
+++ b/libmproxy/console/__init__.py
@@ -16,7 +16,7 @@ import weakref
from .. import controller, flow, script
from . import flowlist, flowview, help, window, signals, options
-from . import grideditor, palettes, contentview, statusbar
+from . import grideditor, palettes, contentview, statusbar, palettepicker
EVENTLOG_SIZE = 500
@@ -132,6 +132,7 @@ class Options(object):
"wfile",
"nopop",
"palette",
+ "palette_transparent"
]
def __init__(self, **kwargs):
@@ -156,9 +157,6 @@ class ConsoleMaster(flow.FlowMaster):
for i in options.setheaders:
self.setheaders.add(*i)
- self.flow_list_walker = None
- self.set_palette(options.palette)
-
r = self.set_intercept(options.intercept)
if r:
print >> sys.stderr, "Intercept error:", r
@@ -183,6 +181,8 @@ class ConsoleMaster(flow.FlowMaster):
self.rheaders = options.rheaders
self.nopop = options.nopop
self.showhost = options.showhost
+ self.palette = options.palette
+ self.palette_transparent = options.palette_transparent
self.eventlog = options.eventlog
self.eventlist = urwid.SimpleListWalker([])
@@ -391,7 +391,11 @@ class ConsoleMaster(flow.FlowMaster):
os.unlink(name)
def set_palette(self, name):
- self.palette = palettes.palettes[name]
+ self.palette = name
+ self.ui.register_palette(
+ palettes.palettes[name].palette(self.palette_transparent)
+ )
+ self.ui.clear()
def ticker(self, *userdata):
changed = self.tick(self.masterq, timeout=0)
@@ -403,8 +407,7 @@ class ConsoleMaster(flow.FlowMaster):
def run(self):
self.ui = urwid.raw_display.Screen()
self.ui.set_terminal_properties(256)
- self.ui.register_palette(self.palette.palette())
- self.flow_list_walker = flowlist.FlowListWalker(self, self.state)
+ self.set_palette(self.palette)
self.loop = urwid.MainLoop(
urwid.SolidFill("x"),
screen = self.ui,
@@ -466,13 +469,26 @@ class ConsoleMaster(flow.FlowMaster):
)
def view_options(self):
+ for i in self.view_stack:
+ if isinstance(i["body"], options.Options):
+ return
signals.push_view_state.send(self)
self.loop.widget = window.Window(
self,
options.Options(self),
None,
statusbar.StatusBar(self, options.footer),
- None
+ options.help_context,
+ )
+
+ def view_palette_picker(self):
+ signals.push_view_state.send(self)
+ self.loop.widget = window.Window(
+ self,
+ palettepicker.PalettePicker(self),
+ None,
+ statusbar.StatusBar(self, palettepicker.footer),
+ palettepicker.help_context,
)
def view_grideditor(self, ge):
@@ -547,8 +563,7 @@ class ConsoleMaster(flow.FlowMaster):
flow.FlowMaster.load_flows_file(self, path)
except flow.FlowReadError, v:
reterr = str(v)
- if self.flow_list_walker:
- self.sync_list_view()
+ signals.flowlist_change.send(self)
return reterr
def accept_all(self):
@@ -556,7 +571,7 @@ class ConsoleMaster(flow.FlowMaster):
def set_limit(self, txt):
v = self.state.set_limit(txt)
- self.sync_list_view()
+ signals.flowlist_change.send(self)
return v
def set_intercept(self, txt):
@@ -575,14 +590,7 @@ class ConsoleMaster(flow.FlowMaster):
self.unload_scripts()
for command in commands:
self.load_script(command)
-
- def edit_ignore_filter(self, ignore):
- patterns = (x[0] for x in ignore)
- self.set_ignore_filter(patterns)
-
- def edit_tcp_filter(self, tcp):
- patterns = (x[0] for x in tcp)
- self.set_tcp_filter(patterns)
+ signals.update_settings.send(self)
def stop_client_playback_prompt(self, a):
if a != "n":
@@ -600,13 +608,9 @@ class ConsoleMaster(flow.FlowMaster):
self.state.killall(self)
flow.FlowMaster.shutdown(self)
- def sync_list_view(self):
- self.flow_list_walker._modified()
- signals.update_settings.send(self)
-
def clear_flows(self):
self.state.clear()
- self.sync_list_view()
+ signals.flowlist_change.send(self)
def toggle_follow_flows(self):
# toggle flow follow
@@ -614,11 +618,11 @@ class ConsoleMaster(flow.FlowMaster):
# jump to most recent flow if follow is now on
if self.state.follow_focus:
self.state.set_focus(self.state.flow_count())
- self.sync_list_view()
+ signals.flowlist_change.send(self)
def delete_flow(self, f):
self.state.delete_flow(f)
- self.sync_list_view()
+ signals.flowlist_change.send(self)
def refresh_focus(self):
if self.state.view:
@@ -632,7 +636,7 @@ class ConsoleMaster(flow.FlowMaster):
f.intercept(self)
else:
f.reply()
- self.sync_list_view()
+ signals.flowlist_change.send(self)
signals.flow_change.send(self, flow = f)
def clear_events(self):
diff --git a/libmproxy/console/flowlist.py b/libmproxy/console/flowlist.py
index 946bd97b..aa3fd718 100644
--- a/libmproxy/console/flowlist.py
+++ b/libmproxy/console/flowlist.py
@@ -15,10 +15,10 @@ def _mkhelp():
("D", "duplicate flow"),
("e", "toggle eventlog"),
("F", "toggle follow flow list"),
- ("g", "copy flow to clipboard"),
("l", "set limit filter pattern"),
("L", "load saved flows"),
("n", "create a new request"),
+ ("P", "copy flow to clipboard"),
("r", "replay request"),
("V", "revert changes to request"),
("w", "save flows "),
@@ -157,11 +157,11 @@ class ConnectionItem(urwid.WidgetWrap):
key = common.shortcuts(key)
if key == "a":
self.flow.accept_intercept(self.master)
- self.master.sync_list_view()
+ signals.flowlist_change.send(self)
elif key == "d":
self.flow.kill(self.master)
self.state.delete_flow(self.flow)
- self.master.sync_list_view()
+ signals.flowlist_change.send(self)
elif key == "D":
f = self.master.duplicate_flow(self.flow)
self.master.view_flow(f)
@@ -169,7 +169,7 @@ class ConnectionItem(urwid.WidgetWrap):
r = self.master.replay_request(self.flow)
if r:
signals.status_message.send(message=r)
- self.master.sync_list_view()
+ signals.flowlist_change.send(self)
elif key == "S":
if not self.master.server_playback:
signals.status_prompt_onekey.send(
@@ -195,7 +195,7 @@ class ConnectionItem(urwid.WidgetWrap):
signals.status_message.send(message="Flow not modified.")
return
self.state.revert(self.flow)
- self.master.sync_list_view()
+ signals.flowlist_change.send(self)
signals.status_message.send(message="Reverted.")
elif key == "w":
signals.status_prompt_onekey.send(
@@ -218,7 +218,7 @@ class ConnectionItem(urwid.WidgetWrap):
callback = self.master.run_script_once,
args = (self.flow,)
)
- elif key == "g":
+ elif key == "P":
common.ask_copy_part("a", self.flow, self.master, self.state)
elif key == "b":
common.ask_save_body(None, self.master, self.state, self.flow)
@@ -231,6 +231,10 @@ class FlowListWalker(urwid.ListWalker):
self.master, self.state = master, state
if self.state.flow_count():
self.set_focus(0)
+ signals.flowlist_change.connect(self.sig_flowlist_change)
+
+ def sig_flowlist_change(self, sender):
+ self._modified()
def get_focus(self):
f, i = self.state.get_focus()
@@ -255,7 +259,10 @@ class FlowListWalker(urwid.ListWalker):
class FlowListBox(urwid.ListBox):
def __init__(self, master):
self.master = master
- urwid.ListBox.__init__(self, master.flow_list_walker)
+ urwid.ListBox.__init__(
+ self,
+ FlowListWalker(master, master.state)
+ )
def get_method_raw(self, k):
if k:
@@ -297,11 +304,17 @@ class FlowListBox(urwid.ListBox):
key = common.shortcuts(key)
if key == "A":
self.master.accept_all()
- self.master.sync_list_view()
+ signals.flowlist_change.send(self)
elif key == "C":
self.master.clear_flows()
elif key == "e":
self.master.toggle_eventlog()
+ elif key == "G":
+ self.master.state.set_focus(0)
+ signals.flowlist_change.send(self)
+ elif key == "g":
+ self.master.state.set_focus(self.master.state.flow_count())
+ signals.flowlist_change.send(self)
elif key == "l":
signals.status_prompt.send(
prompt = "Limit",
diff --git a/libmproxy/console/flowview.py b/libmproxy/console/flowview.py
index 99844cb7..9776f2b1 100644
--- a/libmproxy/console/flowview.py
+++ b/libmproxy/console/flowview.py
@@ -20,7 +20,6 @@ def _mkhelp():
("D", "duplicate flow"),
("e", "edit request/response"),
("f", "load full body data"),
- ("g", "copy response(content/headers) to clipboard"),
("m", "change body display mode for this entity"),
(None,
common.highlight_key("automatic", "a") +
@@ -60,6 +59,7 @@ def _mkhelp():
),
("M", "change default body display mode"),
("p", "previous flow"),
+ ("P", "copy response(content/headers) to clipboard"),
("r", "replay request"),
("V", "revert changes to request"),
("v", "view body in external viewer"),
@@ -546,7 +546,7 @@ class FlowView(tabs.Tabs):
)
signals.flow_change.send(self, flow = self.flow)
signals.status_message.send(message="")
- elif key == "g":
+ elif key == "P":
if self.tab_offset == TAB_REQ:
scope = "q"
else:
diff --git a/libmproxy/console/help.py b/libmproxy/console/help.py
index 19f17625..6d36ee71 100644
--- a/libmproxy/console/help.py
+++ b/libmproxy/console/help.py
@@ -28,6 +28,7 @@ class HelpView(urwid.ListBox):
keys = [
("j, k", "down, up"),
("h, l", "left, right (in some contexts)"),
+ ("g, G", "go to end, beginning"),
("space", "page down"),
("pg up/down", "page up/down"),
("arrows", "up, down, left, right"),
@@ -37,67 +38,11 @@ class HelpView(urwid.ListBox):
text.append(urwid.Text([("head", "\n\nGlobal keys:\n")]))
keys = [
("c", "client replay"),
- ("H", "edit global header set patterns"),
- ("I", "set ignore pattern"),
("i", "set interception pattern"),
- ("M", "change global default display mode"),
- (None,
- common.highlight_key("automatic", "a") +
- [("text", ": automatic detection")]
- ),
- (None,
- common.highlight_key("hex", "e") +
- [("text", ": Hex")]
- ),
- (None,
- common.highlight_key("html", "h") +
- [("text", ": HTML")]
- ),
- (None,
- common.highlight_key("image", "i") +
- [("text", ": Image")]
- ),
- (None,
- common.highlight_key("javascript", "j") +
- [("text", ": JavaScript")]
- ),
- (None,
- common.highlight_key("json", "s") +
- [("text", ": JSON")]
- ),
- (None,
- common.highlight_key("css", "c") +
- [("text", ": CSS")]
- ),
- (None,
- common.highlight_key("urlencoded", "u") +
- [("text", ": URL-encoded data")]
- ),
- (None,
- common.highlight_key("raw", "r") +
- [("text", ": raw data")]
- ),
- (None,
- common.highlight_key("xml", "x") +
- [("text", ": XML")]
- ),
- (None,
- common.highlight_key("wbxml", "w") +
- [("text", ": WBXML")]
- ),
- (None,
- common.highlight_key("amf", "f") +
- [("text", ": AMF (requires PyAMF)")]
- ),
("o", "options"),
- ("q", "quit / return to flow list"),
+ ("q", "quit / return to previous page"),
("Q", "quit without confirm prompt"),
- ("R", "edit replacement patterns"),
- ("s", "add/remove scripts"),
("S", "server replay"),
- ("t", "set sticky cookie expression"),
- ("T", "set tcp proxying pattern"),
- ("u", "set sticky auth expression"),
]
text.extend(
common.format_keyvals(keys, key="key", val="text", indent=4)
diff --git a/libmproxy/console/options.py b/libmproxy/console/options.py
index db6cc151..dc7e00d5 100644
--- a/libmproxy/console/options.py
+++ b/libmproxy/console/options.py
@@ -1,154 +1,125 @@
import urwid
-from . import common, signals
+from . import common, signals, grideditor, contentview
+from . import select, palettes
-help_context = None
footer = [
('heading_key', "enter/space"), ":toggle ",
('heading_key', "C"), ":clear all ",
]
-
-class OptionWidget(urwid.WidgetWrap):
- def __init__(self, option, text, shortcut, active, focus):
- self.option = option
- textattr = "text"
- keyattr = "key"
- if focus and active:
- textattr = "option_active_selected"
- keyattr = "option_selected_key"
- elif focus:
- textattr = "option_selected"
- keyattr = "option_selected_key"
- elif active:
- textattr = "option_active"
- text = common.highlight_key(
- text,
- shortcut,
- textattr=textattr,
- keyattr=keyattr
- )
- opt = urwid.Text(text, align="center")
- opt = urwid.AttrWrap(opt, textattr)
- opt = urwid.Padding(opt, align="center", width=("relative", 20))
- urwid.WidgetWrap.__init__(self, opt)
-
- def keypress(self, size, key):
- return key
-
- def selectable(self):
- return True
-
-
-class OptionWalker(urwid.ListWalker):
- def __init__(self, options):
- urwid.ListWalker.__init__(self)
- self.options = options
- self.focus = 0
- signals.update_settings.connect(self.sig_update_settings)
-
- def sig_update_settings(self, sender):
- self._modified()
-
- def set_focus(self, pos):
- self.focus = pos
-
- def get_focus(self):
- return self.options[self.focus].render(True), self.focus
-
- def get_next(self, pos):
- if pos >= len(self.options)-1:
- return None, None
- return self.options[pos+1].render(False), pos+1
-
- def get_prev(self, pos):
- if pos <= 0:
- return None, None
- return self.options[pos-1].render(False), pos-1
-
-
-class OptionListBox(urwid.ListBox):
- def __init__(self, options):
- urwid.ListBox.__init__(
- self,
- OptionWalker(options)
- )
- self.options = options
- self.keymap = {}
- for i in options:
- self.keymap[i.shortcut] = i
-
- def keypress(self, size, key):
- if key == "enter" or key == " ":
- self.get_focus()[0].option.activate()
- return None
- key = common.shortcuts(key)
- if key in self.keymap:
- self.keymap[key].activate()
- self.set_focus(self.options.index(self.keymap[key]))
- return None
- return super(self.__class__, self).keypress(size, key)
-
-
-_neg = lambda: False
-class Option:
- def __init__(self, text, shortcut, getstate=None, activate=None):
- self.text = text
- self.shortcut = shortcut
- self.getstate = getstate or _neg
- self.activate = activate or _neg
-
- def render(self, focus):
- return OptionWidget(self, self.text, self.shortcut, self.getstate(), focus)
+def _mkhelp():
+ text = []
+ keys = [
+ ("enter/space", "activate option"),
+ ("C", "clear all options"),
+ ]
+ text.extend(common.format_keyvals(keys, key="key", val="text", indent=4))
+ return text
+help_context = _mkhelp()
class Options(urwid.WidgetWrap):
def __init__(self, master):
self.master = master
- self.lb = OptionListBox(
+ self.lb = select.Select(
[
- Option(
+ select.Heading("Traffic Manipulation"),
+ select.Option(
+ "Header Set Patterns",
+ "H",
+ lambda: master.setheaders.count(),
+ self.setheaders
+ ),
+ select.Option(
+ "Ignore Patterns",
+ "I",
+ lambda: master.server.config.check_ignore,
+ self.ignorepatterns
+ ),
+ select.Option(
+ "Replacement Patterns",
+ "R",
+ lambda: master.replacehooks.count(),
+ self.replacepatterns
+ ),
+ select.Option(
+ "Scripts",
+ "S",
+ lambda: master.scripts,
+ self.scripts
+ ),
+
+ select.Heading("Interface"),
+ select.Option(
+ "Default Display Mode",
+ "M",
+ self.has_default_displaymode,
+ self.default_displaymode
+ ),
+ select.Option(
+ "Palette",
+ "P",
+ lambda: self.master.palette != palettes.DEFAULT,
+ self.palette
+ ),
+ select.Option(
+ "Show Host",
+ "w",
+ lambda: master.showhost,
+ self.toggle_showhost
+ ),
+
+ select.Heading("Network"),
+ select.Option(
+ "No Upstream Certs",
+ "U",
+ lambda: master.server.config.no_upstream_cert,
+ self.toggle_upstream_cert
+ ),
+ select.Option(
+ "TCP Proxying",
+ "T",
+ lambda: master.server.config.check_tcp,
+ self.tcp_proxy
+ ),
+
+ select.Heading("Utility"),
+ select.Option(
"Anti-Cache",
"a",
lambda: master.anticache,
self.toggle_anticache
),
- Option(
+ select.Option(
"Anti-Compression",
"o",
lambda: master.anticomp,
self.toggle_anticomp
),
- #Option("Header Set Patterns"),
- #Option("Ignore Patterns"),
- Option(
+ select.Option(
"Kill Extra",
- "E",
+ "x",
lambda: master.killextra,
self.toggle_killextra
),
- #Option("Manage Scripts"),
- #Option("Replacement Patterns"),
- Option(
- "Show Host",
- "H",
- lambda: master.showhost,
- self.toggle_showhost
- ),
- #Option("Sticky Cookies"),
- #Option("Sticky Auth"),
- #Option("TCP Proxying"),
- Option(
+ select.Option(
"No Refresh",
- "R",
+ "f",
lambda: not master.refresh_server_playback,
self.toggle_refresh_server_playback
),
- Option(
- "No Upstream Certs",
- "U",
- lambda: master.server.config.no_upstream_cert,
- self.toggle_upstream_cert
+ select.Option(
+ "Sticky Auth",
+ "A",
+ lambda: master.stickyauth_txt,
+ self.sticky_auth
+ ),
+ select.Option(
+ "Sticky Cookies",
+ "t",
+ lambda: master.stickycookie_txt,
+ self.sticky_cookie
),
]
)
@@ -160,6 +131,10 @@ class Options(urwid.WidgetWrap):
header = title
)
self.master.loop.widget.footer.update("")
+ signals.update_settings.connect(self.sig_update_settings)
+
+ def sig_update_settings(self, sender):
+ self.lb.walker._modified()
def keypress(self, size, key):
if key == "C":
@@ -174,9 +149,18 @@ class Options(urwid.WidgetWrap):
self.master.showhost = False
self.master.refresh_server_playback = True
self.master.server.config.no_upstream_cert = False
+ self.master.setheaders.clear()
+ self.master.replacehooks.clear()
+ self.master.set_ignore_filter([])
+ self.master.set_tcp_filter([])
+ self.master.scripts = []
+ self.master.set_stickyauth(None)
+ self.master.set_stickycookie(None)
+ self.master.state.default_body_view = contentview.get("Auto")
+
signals.update_settings.send(self)
signals.status_message.send(
- message = "All options cleared",
+ message = "All select.Options cleared",
expire = 1
)
@@ -198,3 +182,89 @@ class Options(urwid.WidgetWrap):
def toggle_upstream_cert(self):
self.master.server.config.no_upstream_cert = not self.master.server.config.no_upstream_cert
signals.update_settings.send(self)
+
+ def setheaders(self):
+ def _set(*args, **kwargs):
+ self.master.setheaders.set(*args, **kwargs)
+ signals.update_settings.send(self)
+ self.master.view_grideditor(
+ grideditor.SetHeadersEditor(
+ self.master,
+ self.master.setheaders.get_specs(),
+ _set
+ )
+ )
+
+ def ignorepatterns(self):
+ def _set(ignore):
+ patterns = (x[0] for x in ignore)
+ self.master.set_ignore_filter(patterns)
+ signals.update_settings.send(self)
+ self.master.view_grideditor(
+ grideditor.HostPatternEditor(
+ self.master,
+ [[x] for x in self.master.get_ignore_filter()],
+ _set
+ )
+ )
+
+ def replacepatterns(self):
+ def _set(*args, **kwargs):
+ self.master.replacehooks.set(*args, **kwargs)
+ signals.update_settings.send(self)
+ self.master.view_grideditor(
+ grideditor.ReplaceEditor(
+ self.master,
+ self.master.replacehooks.get_specs(),
+ _set
+ )
+ )
+
+ def scripts(self):
+ self.master.view_grideditor(
+ grideditor.ScriptEditor(
+ self.master,
+ [[i.command] for i in self.master.scripts],
+ self.master.edit_scripts
+ )
+ )
+
+ def default_displaymode(self):
+ signals.status_prompt_onekey.send(
+ prompt = "Global default display mode",
+ keys = contentview.view_prompts,
+ callback = self.master.change_default_display_mode
+ )
+
+ def has_default_displaymode(self):
+ return self.master.state.default_body_view.name != "Auto"
+
+ def tcp_proxy(self):
+ def _set(tcp):
+ patterns = (x[0] for x in tcp)
+ self.master.set_tcp_filter(patterns)
+ signals.update_settings.send(self)
+ self.master.view_grideditor(
+ grideditor.HostPatternEditor(
+ self.master,
+ [[x] for x in self.master.get_tcp_filter()],
+ _set
+ )
+ )
+
+ def sticky_auth(self):
+ signals.status_prompt.send(
+ prompt = "Sticky auth filter",
+ text = self.master.stickyauth_txt,
+ callback = self.master.set_stickyauth
+ )
+
+ def sticky_cookie(self):
+ signals.status_prompt.send(
+ prompt = "Sticky cookie filter",
+ text = self.master.stickycookie_txt,
+ callback = self.master.set_stickycookie
+ )
+
+ def palette(self):
+ self.master.view_palette_picker()
diff --git a/libmproxy/console/palettepicker.py b/libmproxy/console/palettepicker.py
new file mode 100644
index 00000000..7e2c10cd
--- /dev/null
+++ b/libmproxy/console/palettepicker.py
@@ -0,0 +1,81 @@
+import urwid
+
+from . import select, common, palettes, signals
+
+footer = [
+ ('heading_key', "enter/space"), ":select",
+]
+
+
+def _mkhelp():
+ text = []
+ keys = [
+ ("enter/space", "select"),
+ ]
+ text.extend(common.format_keyvals(keys, key="key", val="text", indent=4))
+ return text
+help_context = _mkhelp()
+
+
+class PalettePicker(urwid.WidgetWrap):
+ def __init__(self, master):
+ self.master = master
+ low, high = [], []
+ for k, v in palettes.palettes.items():
+ if v.high:
+ high.append(k)
+ else:
+ low.append(k)
+ high.sort()
+ low.sort()
+
+ options = [
+ select.Heading("High Colour")
+ ]
+
+ def mkopt(name):
+ return select.Option(
+ i,
+ None,
+ lambda: self.master.palette == name,
+ lambda: self.select(name)
+ )
+
+ for i in high:
+ options.append(mkopt(i))
+ options.append(select.Heading("Low Colour"))
+ for i in low:
+ options.append(mkopt(i))
+
+ options.extend(
+ [
+ select.Heading("Options"),
+ select.Option(
+ "Transparent",
+ "T",
+ lambda: master.palette_transparent,
+ self.toggle_palette_transparent
+ )
+ ]
+ )
+
+ self.lb = select.Select(options)
+ title = urwid.Text("Palettes")
+ title = urwid.Padding(title, align="left", width=("relative", 100))
+ title = urwid.AttrWrap(title, "heading")
+ self._w = urwid.Frame(
+ self.lb,
+ header = title
+ )
+ signals.update_settings.connect(self.sig_update_settings)
+
+ def sig_update_settings(self, sender):
+ self.lb.walker._modified()
+
+ def select(self, name):
+ self.master.set_palette(name)
+
+ def toggle_palette_transparent(self):
+ self.master.palette_transparent = not self.master.palette_transparent
+ self.master.set_palette(self.master.palette)
+ signals.update_settings.send(self)
diff --git a/libmproxy/console/palettes.py b/libmproxy/console/palettes.py
index b55a34aa..e34233a6 100644
--- a/libmproxy/console/palettes.py
+++ b/libmproxy/console/palettes.py
@@ -1,4 +1,3 @@
-
# Low-color themes should ONLY use the standard foreground and background
# colours listed here:
#
@@ -6,9 +5,9 @@
#
-
class Palette:
_fields = [
+ 'background',
'title',
# Status bar & heading
@@ -35,15 +34,33 @@ class Palette:
]
high = None
- def palette(self):
+ def palette(self, transparent):
l = []
+ highback, lowback = None, None
+ if not transparent:
+ if self.high and self.high.get("background"):
+ highback = self.high["background"][1]
+ lowback = self.low["background"][1]
+
for i in self._fields:
- v = [i]
- v.extend(self.low[i])
- if self.high and i in self.high:
- v.append(None)
- v.extend(self.high[i])
- l.append(tuple(v))
+ if transparent and i == "background":
+ l.append(["background", "default", "default"])
+ else:
+ v = [i]
+ low = list(self.low[i])
+ if lowback and low[1] == "default":
+ low[1] = lowback
+ v.extend(low)
+ if self.high and i in self.high:
+ v.append(None)
+ high = list(self.high[i])
+ if highback and high[1] == "default":
+ high[1] = highback
+ v.extend(high)
+ elif highback and self.low[i][1] == "default":
+ high = [None, low[0], highback]
+ v.extend(high)
+ l.append(tuple(v))
return l
@@ -52,12 +69,13 @@ class LowDark(Palette):
Low-color dark background
"""
low = dict(
+ background = ('white', 'black'),
title = ('white,bold', 'default'),
# Status bar & heading
- heading = ('light gray', 'dark blue'),
+ heading = ('white', 'dark blue'),
heading_key = ('light cyan', 'dark blue'),
- heading_inactive = ('white', 'dark gray'),
+ heading_inactive = ('dark gray', 'light gray'),
# Help
key = ('light cyan', 'default'),
@@ -65,10 +83,10 @@ class LowDark(Palette):
text = ('light gray', 'default'),
# Options
- option_selected = ('light gray', 'dark blue'),
- option_selected_key = ('light cyan', 'dark blue'),
+ option_selected = ('black', 'light gray'),
+ option_selected_key = ('light cyan', 'light gray'),
option_active = ('light red', 'default'),
- option_active_selected = ('light red', 'dark blue'),
+ option_active_selected = ('light red', 'light gray'),
# List and Connections
method = ('dark cyan', 'default'),
@@ -102,6 +120,10 @@ class Dark(LowDark):
high = dict(
heading_inactive = ('g58', 'g11'),
intercept = ('#f60', 'default'),
+
+ option_selected = ('g85', 'g45'),
+ option_selected_key = ('light cyan', 'g50'),
+ option_active_selected = ('light red', 'g50'),
)
@@ -110,6 +132,7 @@ class LowLight(Palette):
Low-color light background
"""
low = dict(
+ background = ('black', 'white'),
title = ('dark magenta', 'default'),
# Status bar & heading
@@ -158,10 +181,15 @@ class LowLight(Palette):
class Light(LowLight):
high = dict(
+ background = ('black', 'g100'),
heading = ('g99', '#08f'),
heading_key = ('#0ff,bold', '#08f'),
heading_inactive = ('g35', 'g85'),
replay = ('#0a0,bold', 'default'),
+
+ option_selected = ('black', 'g85'),
+ option_selected_key = ('dark blue', 'g85'),
+ option_active_selected = ('light red', 'g85'),
)
@@ -171,10 +199,10 @@ sol_base03 = "h234"
sol_base02 = "h235"
sol_base01 = "h240"
sol_base00 = "h241"
-sol_base0 = "h244"
-sol_base1 = "h245"
-sol_base2 = "h254"
-sol_base3 = "h230"
+sol_base0 = "h244"
+sol_base1 = "h245"
+sol_base2 = "h254"
+sol_base3 = "h230"
sol_yellow = "h136"
sol_orange = "h166"
sol_red = "h160"
@@ -183,9 +211,12 @@ sol_violet = "h61"
sol_blue = "h33"
sol_cyan = "h37"
sol_green = "h64"
+
+
class SolarizedLight(LowLight):
high = dict(
- title = (sol_blue, 'default'),
+ background = (sol_base00, sol_base3),
+ title = (sol_cyan, 'default'),
text = (sol_base00, 'default'),
# Status bar & heading
@@ -233,6 +264,7 @@ class SolarizedLight(LowLight):
class SolarizedDark(LowDark):
high = dict(
+ background = (sol_base2, sol_base03),
title = (sol_blue, 'default'),
text = (sol_base1, 'default'),
@@ -279,6 +311,7 @@ class SolarizedDark(LowDark):
)
+DEFAULT = "dark"
palettes = {
"lowlight": LowLight(),
"lowdark": LowDark(),
diff --git a/libmproxy/console/searchable.py b/libmproxy/console/searchable.py
index 8f63c3f5..a9572ae3 100644
--- a/libmproxy/console/searchable.py
+++ b/libmproxy/console/searchable.py
@@ -15,10 +15,8 @@ class Highlight(urwid.AttrMap):
class Searchable(urwid.ListBox):
def __init__(self, state, contents):
- urwid.ListBox.__init__(
- self,
- urwid.SimpleFocusListWalker(contents)
- )
+ self.walker = urwid.SimpleFocusListWalker(contents)
+ urwid.ListBox.__init__(self, self.walker)
self.state = state
self.search_offset = 0
self.current_highlight = None
@@ -31,10 +29,16 @@ class Searchable(urwid.ListBox):
text = "",
callback = self.set_search
)
- if key == "n":
+ elif key == "n":
self.find_next(False)
- if key == "N":
+ elif key == "N":
self.find_next(True)
+ elif key == "G":
+ self.set_focus(0)
+ self.walker._modified()
+ elif key == "g":
+ self.set_focus(len(self.walker)-1)
+ self.walker._modified()
else:
return super(self.__class__, self).keypress(size, key)
diff --git a/libmproxy/console/select.py b/libmproxy/console/select.py
new file mode 100644
index 00000000..61ee50e4
--- /dev/null
+++ b/libmproxy/console/select.py
@@ -0,0 +1,107 @@
+import urwid
+
+from . import common
+
+class _OptionWidget(urwid.WidgetWrap):
+ def __init__(self, option, text, shortcut, active, focus):
+ self.option = option
+ textattr = "text"
+ keyattr = "key"
+ if focus and active:
+ textattr = "option_active_selected"
+ keyattr = "option_selected_key"
+ elif focus:
+ textattr = "option_selected"
+ keyattr = "option_selected_key"
+ elif active:
+ textattr = "option_active"
+ if shortcut:
+ text = common.highlight_key(
+ text,
+ shortcut,
+ textattr = textattr,
+ keyattr = keyattr
+ )
+ opt = urwid.Text(text, align="left")
+ opt = urwid.AttrWrap(opt, textattr)
+ opt = urwid.Padding(opt, align = "center", width = 40)
+ urwid.WidgetWrap.__init__(self, opt)
+
+ def keypress(self, size, key):
+ return key
+
+ def selectable(self):
+ return True
+
+
+class OptionWalker(urwid.ListWalker):
+ def __init__(self, options):
+ urwid.ListWalker.__init__(self)
+ self.options = options
+ self.focus = 0
+
+ def set_focus(self, pos):
+ self.focus = pos
+
+ def get_focus(self):
+ return self.options[self.focus].render(True), self.focus
+
+ def get_next(self, pos):
+ if pos >= len(self.options)-1:
+ return None, None
+ return self.options[pos+1].render(False), pos+1
+
+ def get_prev(self, pos):
+ if pos <= 0:
+ return None, None
+ return self.options[pos-1].render(False), pos-1
+
+
+class Heading:
+ def __init__(self, text):
+ self.text = text
+
+ def render(self, focus):
+ opt = urwid.Text("\n" + self.text, align="left")
+ opt = urwid.AttrWrap(opt, "title")
+ opt = urwid.Padding(opt, align = "center", width = 40)
+ return opt
+
+
+_neg = lambda: False
+class Option:
+ def __init__(self, text, shortcut, getstate=None, activate=None):
+ self.text = text
+ self.shortcut = shortcut
+ self.getstate = getstate or _neg
+ self.activate = activate or _neg
+
+ def render(self, focus):
+ return _OptionWidget(self, self.text, self.shortcut, self.getstate(), focus)
+
+
+class Select(urwid.ListBox):
+ def __init__(self, options):
+ self.walker = OptionWalker(options)
+ urwid.ListBox.__init__(
+ self,
+ self.walker
+ )
+ self.options = options
+ self.keymap = {}
+ for i in options:
+ if hasattr(i, "shortcut") and i.shortcut:
+ if i.shortcut in self.keymap:
+ raise ValueError("Duplicate shortcut key: %s"%i.shortcut)
+ self.keymap[i.shortcut] = i
+
+ def keypress(self, size, key):
+ if key == "enter" or key == " ":
+ self.get_focus()[0].option.activate()
+ return None
+ key = common.shortcuts(key)
+ if key in self.keymap:
+ self.keymap[key].activate()
+ self.set_focus(self.options.index(self.keymap[key]))
+ return None
+ return super(self.__class__, self).keypress(size, key)
diff --git a/libmproxy/console/signals.py b/libmproxy/console/signals.py
index e4c11f5a..c1bcf201 100644
--- a/libmproxy/console/signals.py
+++ b/libmproxy/console/signals.py
@@ -24,6 +24,8 @@ update_settings = blinker.Signal()
# Fired when a flow changes
flow_change = blinker.Signal()
+# Fired when the flow list or focus changes
+flowlist_change = blinker.Signal()
# Pop and push view state onto a stack
pop_view_state = blinker.Signal()
diff --git a/libmproxy/console/statusbar.py b/libmproxy/console/statusbar.py
index 7fb15aa6..5455ad6e 100644
--- a/libmproxy/console/statusbar.py
+++ b/libmproxy/console/statusbar.py
@@ -114,6 +114,7 @@ class StatusBar(urwid.WidgetWrap):
self.ib = urwid.WidgetWrap(urwid.Text(""))
self._w = urwid.Pile([self.ib, self.ab])
signals.update_settings.connect(self.sig_update_settings)
+ signals.flowlist_change.connect(self.sig_update_settings)
self.redraw()
def sig_update_settings(self, sender):
diff --git a/libmproxy/console/window.py b/libmproxy/console/window.py
index 1d22f280..d64e83df 100644
--- a/libmproxy/console/window.py
+++ b/libmproxy/console/window.py
@@ -1,10 +1,15 @@
import urwid
-from . import grideditor, signals, contentview
+from . import signals
class Window(urwid.Frame):
def __init__(self, master, body, header, footer, helpctx):
- urwid.Frame.__init__(self, body, header=header, footer=footer)
+ urwid.Frame.__init__(
+ self,
+ urwid.AttrWrap(body, "background"),
+ header = urwid.AttrWrap(header, "background") if header else None,
+ footer = urwid.AttrWrap(footer, "background") if footer else None
+ )
self.master = master
self.helpctx = helpctx
signals.focus.connect(self.sig_focus)
@@ -33,30 +38,6 @@ class Window(urwid.Frame):
),
callback = self.master.stop_client_playback_prompt,
)
- elif k == "H":
- self.master.view_grideditor(
- grideditor.SetHeadersEditor(
- self.master,
- self.master.setheaders.get_specs(),
- self.master.setheaders.set
- )
- )
- elif k == "I":
- self.master.view_grideditor(
- grideditor.HostPatternEditor(
- self.master,
- [[x] for x in self.master.get_ignore_filter()],
- self.master.edit_ignore_filter
- )
- )
- elif k == "T":
- self.master.view_grideditor(
- grideditor.HostPatternEditor(
- self.master,
- [[x] for x in self.master.get_tcp_filter()],
- self.master.edit_tcp_filter
- )
- )
elif k == "i":
signals.status_prompt.send(
self,
@@ -70,28 +51,6 @@ class Window(urwid.Frame):
raise urwid.ExitMainLoop
elif k == "q":
signals.pop_view_state.send(self)
- elif k == "M":
- signals.status_prompt_onekey.send(
- prompt = "Global default display mode",
- keys = contentview.view_prompts,
- callback = self.master.change_default_display_mode
- )
- elif k == "R":
- self.master.view_grideditor(
- grideditor.ReplaceEditor(
- self.master,
- self.master.replacehooks.get_specs(),
- self.master.replacehooks.set
- )
- )
- elif k == "s":
- self.master.view_grideditor(
- grideditor.ScriptEditor(
- self.master,
- [[i.command] for i in self.master.scripts],
- self.master.edit_scripts
- )
- )
elif k == "S":
if not self.master.server_playback:
signals.status_prompt_path.send(
@@ -109,17 +68,5 @@ class Window(urwid.Frame):
),
callback = self.master.stop_server_playback_prompt,
)
- elif k == "t":
- signals.status_prompt.send(
- prompt = "Sticky cookie filter",
- text = self.master.stickycookie_txt,
- callback = self.master.set_stickycookie
- )
- elif k == "u":
- signals.status_prompt.send(
- prompt = "Sticky auth filter",
- text = self.master.stickyauth_txt,
- callback = self.master.set_stickyauth
- )
else:
return k
diff --git a/libmproxy/flow.py b/libmproxy/flow.py
index eb8ee16a..680b6a20 100644
--- a/libmproxy/flow.py
+++ b/libmproxy/flow.py
@@ -736,7 +736,7 @@ class FlowMaster(controller.Master):
ignore_payload_params: list of content params to ignore in server replay
ignore_host: true if request host should be ignored in server replay
"""
- self.server_playback = ServerPlaybackState(headers, flows, exit, nopop,
+ self.server_playback = ServerPlaybackState(headers, flows, exit, nopop,
ignore_params, ignore_content,
ignore_payload_params, ignore_host)
self.kill_nonreplay = kill
@@ -786,7 +786,7 @@ class FlowMaster(controller.Master):
def create_request(self, method, scheme, host, port, path):
"""
this method creates a new artificial and minimalist request also adds it to flowlist
- """
+ """
c = ClientConnection.from_state(dict(
address=dict(address=(host, port), use_ipv6=False),
clientcert=None
@@ -802,7 +802,7 @@ class FlowMaster(controller.Master):
))
f = http.HTTPFlow(c,s);
headers = ODictCaseless()
-
+
req = http.HTTPRequest("absolute", method, scheme, host, port, path, (1, 1), headers, None,
None, None, None)
f.request = req
diff --git a/libmproxy/main.py b/libmproxy/main.py
index e5b7f56b..7d22b48c 100644
--- a/libmproxy/main.py
+++ b/libmproxy/main.py
@@ -89,6 +89,7 @@ def mitmproxy(): # pragma: nocover
proxy_config = process_proxy_options(parser, options)
console_options = console.Options(**cmdline.get_common_options(options))
console_options.palette = options.palette
+ console_options.palette_transparent = options.palette_transparent
console_options.eventlog = options.eventlog
console_options.intercept = options.intercept