diff --git a/mitmproxy/addons/core.py b/mitmproxy/addons/core.py index 875da32b1..426c47ad8 100644 --- a/mitmproxy/addons/core.py +++ b/mitmproxy/addons/core.py @@ -4,6 +4,7 @@ from mitmproxy import ctx from mitmproxy import exceptions from mitmproxy import command from mitmproxy import flow +from mitmproxy import optmanager from mitmproxy.net.http import status_codes @@ -212,3 +213,47 @@ class Core: """ return ["gzip", "deflate", "br"] + + @command.command("options.load") + def options_load(self, path: str) -> None: + """ + Load options from a file. + """ + try: + optmanager.load_paths(ctx.options, path) + except (OSError, exceptions.OptionsError) as e: + raise exceptions.CommandError( + "Could not load options - %s" % e + ) from e + + @command.command("options.save") + def options_save(self, path: str) -> None: + """ + Save options to a file. + """ + try: + optmanager.save(ctx.options, path) + except OSError as e: + raise exceptions.CommandError( + "Could not save options - %s" % e + ) from e + + @command.command("options.reset") + def options_reset(self) -> None: + """ + Reset all options to defaults. + """ + ctx.options.reset() + + @command.command("options.reset.one") + def options_reset_one(self, name: str) -> None: + """ + Reset one option to its default value. + """ + if name not in ctx.options: + raise exceptions.CommandError("No such option: %s" % name) + setattr( + ctx.options, + name, + ctx.options.default(name), + ) diff --git a/mitmproxy/tools/console/master.py b/mitmproxy/tools/console/master.py index 3f46c5e39..10b194f02 100644 --- a/mitmproxy/tools/console/master.py +++ b/mitmproxy/tools/console/master.py @@ -83,6 +83,16 @@ class ConsoleAddon: self.master = master self.started = False + @command.command("console.options.reset.current") + def options_reset_current(self) -> None: + """ + Reset the current option in the options editor. + """ + if self.master.window.focus.keyctx != "options": + raise exceptions.CommandError("Not viewing options.") + name = self.master.window.windows["options"].current_name() + self.master.commands.call("options.reset.one %s" % name) + @command.command("console.nav.start") def nav_start(self) -> None: """ @@ -434,6 +444,11 @@ def default_keymap(km): ["flowview"] ) + km.add("L", "console.command options.load ", ["options"]) + km.add("S", "console.command options.save ", ["options"]) + km.add("D", "options.reset", ["options"]) + km.add("d", "console.options.reset.current", ["options"]) + class ConsoleMaster(master.Master): diff --git a/mitmproxy/tools/console/options.py b/mitmproxy/tools/console/options.py index 03b54f36c..a359eaf93 100644 --- a/mitmproxy/tools/console/options.py +++ b/mitmproxy/tools/console/options.py @@ -187,12 +187,6 @@ class OptionsList(urwid.ListBox): except exceptions.OptionsError as e: signals.status_message.send(message=str(e)) - def load_config(self, path): - try: - optmanager.load_paths(self.master.options, path) - except exceptions.OptionsError as e: - signals.status_message.send(message=str(e)) - def keypress(self, size, key): if self.walker.editing: if key == "enter": @@ -207,29 +201,12 @@ class OptionsList(urwid.ListBox): elif key == "esc": self.walker.stop_editing() else: - if key == "d": - foc, idx = self.get_focus() - setattr( - self.master.options, - foc.opt.name, - self.master.options.default(foc.opt.name) - ) - elif key == "m_start": + if key == "m_start": self.set_focus(0) self.walker._modified() elif key == "m_end": self.set_focus(len(self.walker.opts) - 1) self.walker._modified() - elif key == "l": - signals.status_prompt_path.send( - prompt = "Load config from", - callback = self.load_config - ) - elif key == "w": - signals.status_prompt_path.send( - prompt = "Save config to", - callback = self.save_config - ) elif key == "enter": foc, idx = self.get_focus() if foc.opt.typespec == bool: @@ -290,14 +267,19 @@ class Options(urwid.Pile): def __init__(self, master): oh = OptionHelp(master) + self.optionslist = OptionsList(master) super().__init__( [ - OptionsList(master), + self.optionslist, (HELP_HEIGHT, oh), ] ) self.master = master + def current_name(self): + foc, idx = self.optionslist.get_focus() + return foc.opt.name + def keypress(self, size, key): if key == "tab": self.focus_position = ( @@ -305,9 +287,6 @@ class Options(urwid.Pile): ) % len(self.widget_list) self.widget_list[1].set_active(self.focus_position == 1) key = None - elif key == "D": - self.master.options.reset() - key = None # This is essentially a copypasta from urwid.Pile's keypress handler. # So much for "closed for modification, but open for extension". diff --git a/test/mitmproxy/addons/test_core.py b/test/mitmproxy/addons/test_core.py index eaf0dfa08..3c3c7bf7f 100644 --- a/test/mitmproxy/addons/test_core.py +++ b/test/mitmproxy/addons/test_core.py @@ -128,3 +128,38 @@ def test_encoding(): with pytest.raises(exceptions.CommandError): sa.encode([f], "request", "invalid") + + +def test_options(tmpdir): + p = str(tmpdir.join("path")) + sa = core.Core() + with taddons.context() as tctx: + tctx.options.stickycookie = "foo" + assert tctx.options.stickycookie == "foo" + sa.options_reset() + assert tctx.options.stickycookie is None + + tctx.options.stickycookie = "foo" + tctx.options.stickyauth = "bar" + sa.options_reset_one("stickycookie") + assert tctx.options.stickycookie is None + assert tctx.options.stickyauth == "bar" + + with pytest.raises(exceptions.CommandError): + sa.options_reset_one("unknown") + + sa.options_save(p) + with pytest.raises(exceptions.CommandError): + sa.options_save("/nonexistent") + + sa.options_reset() + assert tctx.options.stickyauth is None + sa.options_load(p) + assert tctx.options.stickyauth == "bar" + + sa.options_load("/nonexistent") + + with open(p, 'a') as f: + f.write("'''") + with pytest.raises(exceptions.CommandError): + sa.options_load(p) diff --git a/test/mitmproxy/tools/console/test_help.py b/test/mitmproxy/tools/console/test_help.py index ac3011e6f..0ebc2d6ae 100644 --- a/test/mitmproxy/tools/console/test_help.py +++ b/test/mitmproxy/tools/console/test_help.py @@ -9,9 +9,3 @@ class TestHelp: def test_helptext(self): h = help.HelpView(None) assert h.helptext() - - def test_keypress(self): - h = help.HelpView([1, 2, 3]) - assert not h.keypress((0, 0), "q") - assert not h.keypress((0, 0), "?") - assert h.keypress((0, 0), "o") == "o"