From d759150734e19c1b253ca112b723440c4e773074 Mon Sep 17 00:00:00 2001 From: Aldo Cortesi Date: Fri, 17 Mar 2017 11:16:34 +1300 Subject: console: options/help pane switching, toggle bools with enter --- mitmproxy/options.py | 36 +++++++++---------- mitmproxy/tools/console/options.py | 72 +++++++++++++++++++++++++++++++------- 2 files changed, 77 insertions(+), 31 deletions(-) diff --git a/mitmproxy/options.py b/mitmproxy/options.py index 1b66790f..9232378f 100644 --- a/mitmproxy/options.py +++ b/mitmproxy/options.py @@ -43,8 +43,9 @@ class Options(optmanager.OptManager): self.add_option( "onboarding_host", str, APP_HOST, """ - Domain to serve the onboarding app from. For transparent mode, use - an IP when a DNS entry for the app domain is not present. """ + Onboarding app domain. For transparent mode, use an IP when a DNS + entry for the app domain is not present. + """ ) self.add_option( "onboarding_port", int, APP_PORT, @@ -72,9 +73,9 @@ class Options(optmanager.OptManager): self.add_option( "keepserving", bool, False, """ - Instructs mitmdump to continue serving after client playback, server - playback or file read. This option is ignored by interactive tools, - which always keep serving. + Continue serving after client playback, server playback or file + read. This option is ignored by interactive tools, which always keep + serving. """ ) self.add_option( @@ -84,8 +85,8 @@ class Options(optmanager.OptManager): self.add_option( "server_replay_nopop", bool, False, """ - Disable response pop from response flow. This makes it possible to - replay same response multiple times. + Don't remove flows from server replay state after use. This makes it + possible to replay same response multiple times. """ ) self.add_option( @@ -190,11 +191,10 @@ class Options(optmanager.OptManager): self.add_option( "proxyauth", Optional[str], None, """ - Require authentication before proxying requests. If the value is - "any", we prompt for authentication, but permit any values. If it - starts with an "@", it is treated as a path to an Apache htpasswd - file. If its is of the form "username:password", it is treated as a - single-user credential. + Require proxy authentication. Value may be "any" to require + authenticaiton but accept any credentials, start with "@" to specify + a path to an Apache htpasswd file, or be of the form + "username:password". """ ) self.add_option( @@ -218,12 +218,12 @@ class Options(optmanager.OptManager): self.add_option( "certs", Sequence[str], [], """ - SSL certificates. SPEC is of the form "[domain=]path". The - domain may include a wildcard, and is equal to "*" if not specified. - The file at path is a certificate in PEM format. If a private key is - included in the PEM, it is used, else the default key in the conf - dir is used. The PEM file should contain the full certificate chain, - with the leaf certificate as the first entry. + SSL certificates of the form "[domain=]path". The domain may include + a wildcard, and is equal to "*" if not specified. The file at path + is a certificate in PEM format. If a private key is included in the + PEM, it is used, else the default key in the conf dir is used. The + PEM file should contain the full certificate chain, with the leaf + certificate as the first entry. """ ) self.add_option( diff --git a/mitmproxy/tools/console/options.py b/mitmproxy/tools/console/options.py index 903bde5b..94b80f83 100644 --- a/mitmproxy/tools/console/options.py +++ b/mitmproxy/tools/console/options.py @@ -55,15 +55,22 @@ class OptionItem(urwid.WidgetWrap): displayval = str(val) changed = self.master.options.has_changed(self.opt.name) - namestyle = "option_selected" if self.focused else "title" - valstyle = "option_active" if changed else "text" + if self.focused: + valstyle = "option_active_selected" if changed else "option_selected" + else: + valstyle = "option_active" if changed else "text" return urwid.Columns( [ ( self.namewidth, - urwid.Text([(namestyle, self.opt.name.ljust(self.namewidth))]) + urwid.Text([("title", self.opt.name.ljust(self.namewidth))]) ), - urwid.Text([(valstyle, displayval)]) + urwid.AttrMap( + urwid.Padding( + urwid.Text([(valstyle, displayval)]) + ), + valstyle + ) ], dividechars=2 ) @@ -72,8 +79,15 @@ class OptionItem(urwid.WidgetWrap): return True def keypress(self, xxx_todo_changeme, key): - key = common.shortcuts(key) - return key + if key == "enter": + if self.opt.typespec == bool: + setattr( + self.master.options, + self.opt.name, + not self.opt.current() + ) + else: + return key class OptionListWalker(urwid.ListWalker): @@ -86,6 +100,10 @@ class OptionListWalker(urwid.ListWalker): # Trigger a help text update for the first selected item first = self.master.options._options[self.opts[0]] option_focus_change.send(first.help) + self.master.options.changed.connect(self.sig_mod) + + def sig_mod(self, *args, **kwargs): + self._modified() def _get(self, pos): name = self.opts[pos] @@ -121,19 +139,28 @@ class OptionsList(urwid.ListBox): class OptionHelp(urwid.Frame): - def __init__(self): + def __init__(self, master): + self.master = master + h = urwid.Text("Option Help") h = urwid.Padding(h, align="left", width=("relative", 100)) - h = urwid.AttrWrap(h, "heading") - super().__init__(self.widget(""), header=h) + + self.inactive_header = urwid.AttrWrap(h, "heading_inactive") + self.active_header = urwid.AttrWrap(h, "heading") + + super().__init__(self.widget(""), header=self.inactive_header) option_focus_change.connect(self.sig_mod) - def selectable(self): - return False + def active(self, val): + if val: + self.header = self.active_header + else: + self.header = self.inactive_header def widget(self, txt): + cols, _ = self.master.ui.get_cols_rows() return urwid.ListBox( - [urwid.Text(i) for i in textwrap.wrap(txt)] + [urwid.Text(i) for i in textwrap.wrap(txt, cols)] ) def sig_mod(self, txt): @@ -142,7 +169,7 @@ class OptionHelp(urwid.Frame): class Options(urwid.Pile): def __init__(self, master): - oh = OptionHelp() + oh = OptionHelp(master) super().__init__( [ OptionsList(master), @@ -150,3 +177,22 @@ class Options(urwid.Pile): ] ) self.master = master + + def keypress(self, size, key): + key = common.shortcuts(key) + if key == "tab": + self.focus_position = ( + self.focus_position + 1 + ) % len(self.widget_list) + self.widget_list[1].active(self.focus_position == 1) + key = None + + # This is essentially a copypasta from urwid.Pile's keypress handler. + # So much for "closed for modification, but open for extension". + item_rows = None + if len(size) == 2: + item_rows = self.get_item_rows(size, focus = True) + i = self.widget_list.index(self.focus_item) + tsize = self.get_item_size(size, i, True, item_rows) + return self.focus_item.keypress(tsize, key) + -- cgit v1.2.3