diff options
Diffstat (limited to 'libmproxy')
-rw-r--r-- | libmproxy/cmdline.py | 7 | ||||
-rw-r--r-- | libmproxy/console/__init__.py | 58 | ||||
-rw-r--r-- | libmproxy/console/flowlist.py | 29 | ||||
-rw-r--r-- | libmproxy/console/flowview.py | 4 | ||||
-rw-r--r-- | libmproxy/console/help.py | 59 | ||||
-rw-r--r-- | libmproxy/console/options.py | 310 | ||||
-rw-r--r-- | libmproxy/console/palettepicker.py | 81 | ||||
-rw-r--r-- | libmproxy/console/palettes.py | 71 | ||||
-rw-r--r-- | libmproxy/console/searchable.py | 16 | ||||
-rw-r--r-- | libmproxy/console/select.py | 107 | ||||
-rw-r--r-- | libmproxy/console/signals.py | 2 | ||||
-rw-r--r-- | libmproxy/console/statusbar.py | 1 | ||||
-rw-r--r-- | libmproxy/console/window.py | 67 | ||||
-rw-r--r-- | libmproxy/flow.py | 6 | ||||
-rw-r--r-- | libmproxy/main.py | 1 |
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 |