diff --git a/libmproxy/console/__init__.py b/libmproxy/console/__init__.py index ee2957552..1e5524d8d 100644 --- a/libmproxy/console/__init__.py +++ b/libmproxy/console/__init__.py @@ -231,7 +231,7 @@ class ConsoleState(flow.State): def __init__(self): flow.State.__init__(self) self.focus = None - self.view_body_mode = contentview.VIEW_CONTENT_PRETTY + self.default_body_view = contentview.VIEW_AUTO self.view_flow_mode = common.VIEW_FLOW_REQUEST self.last_script = "" self.last_saveload = "" @@ -686,15 +686,6 @@ class ConsoleMaster(flow.FlowMaster): return "Invalid reverse proxy specification" self.server.config.reverse_proxy = s - def changeview(self, v): - if v == "r": - self.state.view_body_mode = contentview.VIEW_CONTENT_RAW - elif v == "h": - self.state.view_body_mode = contentview.VIEW_CONTENT_HEX - elif v == "p": - self.state.view_body_mode = contentview.VIEW_CONTENT_PRETTY - self.refresh_flow(self.currentflow) - def drawscreen(self): size = self.ui.get_cols_rows() canvas = self.view.render(size, focus=1) diff --git a/libmproxy/console/contentview.py b/libmproxy/console/contentview.py index 10a33e05f..1d621ae25 100644 --- a/libmproxy/console/contentview.py +++ b/libmproxy/console/contentview.py @@ -8,47 +8,53 @@ from ..contrib import jsbeautifier VIEW_CUTOFF = 1024*20 -VIEW_CONTENT_RAW = 0 -VIEW_CONTENT_HEX = 1 -VIEW_CONTENT_PRETTY = 2 +VIEW_AUTO = 0 +VIEW_JSON = 1 +VIEW_XML = 2 +VIEW_URLENCODED = 3 +VIEW_MULTIPART = 4 +VIEW_JAVASCRIPT = 5 +VIEW_IMAGE = 6 +VIEW_RAW = 7 +VIEW_HEX = 8 -CONTENT_VIEWS = { - VIEW_CONTENT_RAW: "raw", - VIEW_CONTENT_HEX: "hex", - VIEW_CONTENT_PRETTY: "pretty" +VIEW_NAMES = { + VIEW_AUTO: "Auto", + VIEW_JSON: "JSON", + VIEW_XML: "XML", + VIEW_URLENCODED: "URL-encoded", + VIEW_MULTIPART: "Multipart Form", + VIEW_JAVASCRIPT: "JavaScript", + VIEW_IMAGE: "Image", + VIEW_RAW: "Raw", + VIEW_HEX: "Hex", } -VIEW_CONTENT_PRETTY_TYPE_AUTO = 0 -VIEW_CONTENT_PRETTY_TYPE_JSON = 1 -VIEW_CONTENT_PRETTY_TYPE_XML = 2 -VIEW_CONTENT_PRETTY_TYPE_URLENCODED = 3 -VIEW_CONTENT_PRETTY_TYPE_MULTIPART = 4 -VIEW_CONTENT_PRETTY_TYPE_JAVASCRIPT = 5 -VIEW_CONTENT_PRETTY_TYPE_IMAGE = 6 - -CONTENT_PRETTY_NAMES = { - VIEW_CONTENT_PRETTY_TYPE_JSON: "JSON", - VIEW_CONTENT_PRETTY_TYPE_XML: "XML", - VIEW_CONTENT_PRETTY_TYPE_URLENCODED: "URL-encoded", - VIEW_CONTENT_PRETTY_TYPE_MULTIPART: "Multipart Form", - VIEW_CONTENT_PRETTY_TYPE_JAVASCRIPT: "JavaScript", - VIEW_CONTENT_PRETTY_TYPE_IMAGE: "Image", +VIEW_SHORTCUTS = { + "a": VIEW_AUTO, + "i": VIEW_IMAGE, + "j": VIEW_JAVASCRIPT, + "s": VIEW_JSON, + "u": VIEW_URLENCODED, + "x": VIEW_XML, + "r": VIEW_RAW, + "h": VIEW_HEX, } CONTENT_TYPES_MAP = { - "text/html": VIEW_CONTENT_PRETTY_TYPE_XML, - "application/json": VIEW_CONTENT_PRETTY_TYPE_JSON, - "text/xml": VIEW_CONTENT_PRETTY_TYPE_XML, - "multipart/form-data": VIEW_CONTENT_PRETTY_TYPE_MULTIPART, - "application/x-www-form-urlencoded": VIEW_CONTENT_PRETTY_TYPE_URLENCODED, - "application/x-javascript": VIEW_CONTENT_PRETTY_TYPE_JAVASCRIPT, - "application/javascript": VIEW_CONTENT_PRETTY_TYPE_JAVASCRIPT, - "text/javascript": VIEW_CONTENT_PRETTY_TYPE_JAVASCRIPT, - "image/png": VIEW_CONTENT_PRETTY_TYPE_IMAGE, - "image/jpeg": VIEW_CONTENT_PRETTY_TYPE_IMAGE, - "image/gif": VIEW_CONTENT_PRETTY_TYPE_IMAGE, - "image/vnd.microsoft.icon": VIEW_CONTENT_PRETTY_TYPE_IMAGE, - "image/x-icon": VIEW_CONTENT_PRETTY_TYPE_IMAGE, + "text/html": VIEW_XML, + "application/json": VIEW_JSON, + "text/xml": VIEW_XML, + "multipart/form-data": VIEW_MULTIPART, + "application/x-www-form-urlencoded": VIEW_URLENCODED, + "application/x-javascript": VIEW_JAVASCRIPT, + "application/javascript": VIEW_JAVASCRIPT, + "text/javascript": VIEW_JAVASCRIPT, + "image/png": VIEW_IMAGE, + "image/jpeg": VIEW_IMAGE, + "image/gif": VIEW_IMAGE, + "image/vnd.microsoft.icon": VIEW_IMAGE, + "image/x-icon": VIEW_IMAGE, } def trailer(clen, txt): @@ -209,36 +215,33 @@ def view_image(hdrs, content): PRETTY_FUNCTION_MAP = { - VIEW_CONTENT_PRETTY_TYPE_XML: view_xmlish, - VIEW_CONTENT_PRETTY_TYPE_JSON: view_json, - VIEW_CONTENT_PRETTY_TYPE_URLENCODED: view_urlencoded, - VIEW_CONTENT_PRETTY_TYPE_MULTIPART: view_multipart, - VIEW_CONTENT_PRETTY_TYPE_JAVASCRIPT: view_javascript, - VIEW_CONTENT_PRETTY_TYPE_IMAGE: view_image, + VIEW_XML: view_xmlish, + VIEW_JSON: view_json, + VIEW_URLENCODED: view_urlencoded, + VIEW_MULTIPART: view_multipart, + VIEW_JAVASCRIPT: view_javascript, + VIEW_IMAGE: view_image, + VIEW_HEX: view_hex, + VIEW_RAW: view_raw, } -def get_view_func(viewmode, pretty_type, hdrs, content): +def get_view_func(viewmode, hdrs, content): """ Returns a function object. """ - if viewmode == VIEW_CONTENT_HEX: - return view_hex - elif viewmode == VIEW_CONTENT_RAW: - return view_raw - else: - if pretty_type == VIEW_CONTENT_PRETTY_TYPE_AUTO: - ctype = hdrs.get("content-type") - if ctype: - ctype = ctype[0] - ct = utils.parse_content_type(ctype) if ctype else None - if ct: - pretty_type = CONTENT_TYPES_MAP.get("%s/%s"%(ct[0], ct[1])) - if not pretty_type and utils.isXML(content): - pretty_type = VIEW_CONTENT_PRETTY_TYPE_XML - return PRETTY_FUNCTION_MAP.get(pretty_type, view_raw) + if viewmode == VIEW_AUTO: + ctype = hdrs.get("content-type") + if ctype: + ctype = ctype[0] + ct = utils.parse_content_type(ctype) if ctype else None + if ct: + viewmode = CONTENT_TYPES_MAP.get("%s/%s"%(ct[0], ct[1])) + if not viewmode and utils.isXML(content): + viewmode = VIEW_XML + return PRETTY_FUNCTION_MAP.get(viewmode, view_raw) -def get_content_view(viewmode, pretty_type, hdrItems, content): +def get_content_view(viewmode, hdrItems, content): """ Returns a (msg, body) tuple. """ @@ -252,11 +255,7 @@ def get_content_view(viewmode, pretty_type, hdrItems, content): if decoded: content = decoded msg.append("[decoded %s]"%enc[0]) - - if viewmode == VIEW_CONTENT_PRETTY and pretty_type != VIEW_CONTENT_PRETTY_TYPE_AUTO: - msg.append("[forced to %s]"%(CONTENT_PRETTY_NAMES[pretty_type])) - func = get_view_func(viewmode, pretty_type, hdrs, content) - + func = get_view_func(viewmode, hdrs, content) ret = func(hdrs, content) if not ret: ret = view_raw(hdrs, content) diff --git a/libmproxy/console/flowview.py b/libmproxy/console/flowview.py index bbf3a89aa..9dbbcf50d 100644 --- a/libmproxy/console/flowview.py +++ b/libmproxy/console/flowview.py @@ -27,26 +27,15 @@ def _mkhelp(): ("d", "delete flow"), ("D", "duplicate flow"), ("e", "edit request/response"), - ("m", "change body display mode"), - (None, - common.highlight_key("raw", "r") + - [("text", ": raw data")] - ), - (None, - common.highlight_key("pretty", "p") + - [("text", ": pretty-print XML, HTML and JSON")] - ), - (None, - common.highlight_key("hex", "h") + - [("text", ": hex dump")] - ), - ("p", "previous flow"), - ("r", "replay request"), - ("T", "override content-type for pretty-printed body"), + ("m", "change body display mode for this entity"), (None, common.highlight_key("automatic", "a") + [("text", ": automatic detection")] ), + (None, + common.highlight_key("hex", "h") + + [("text", ": Hex")] + ), (None, common.highlight_key("image", "i") + [("text", ": Image")] @@ -63,10 +52,17 @@ def _mkhelp(): 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")] ), + ("M", "change default body display mode"), + ("p", "previous flow"), + ("r", "replay request"), ("V", "revert changes to request"), ("v", "view body in external viewer"), ("w", "save all flows matching current limit"), @@ -128,14 +124,14 @@ class FlowView(common.WWrap): else: self.view_request() - def _cached_conn_text(self, content, hdrItems, viewmode, pretty_type): + def _cached_conn_text(self, content, hdrItems, viewmode): txt = common.format_keyvals( [(h+":", v) for (h, v) in hdrItems], key = "header", val = "text" ) if content: - msg, body = contentview.get_content_view(viewmode, pretty_type, hdrItems, content) + msg, body = contentview.get_content_view(viewmode, hdrItems, content) title = urwid.AttrWrap(urwid.Columns([ urwid.Text( [ @@ -147,7 +143,7 @@ class FlowView(common.WWrap): " ", ('heading', "["), ('heading_key', "m"), - ('heading', (":%s]"%contentview.CONTENT_VIEWS[self.master.state.view_body_mode])), + ('heading', (":%s]"%contentview.VIEW_NAMES[viewmode])), ], align="right" ), @@ -190,24 +186,22 @@ class FlowView(common.WWrap): ) return f - def _conn_text(self, conn, viewmode, pretty_type): + def _conn_text(self, conn, viewmode): return cache.callback( self, "_cached_conn_text", conn.content, tuple(tuple(i) for i in conn.headers.lst), - viewmode, - pretty_type + viewmode ) def view_request(self): self.state.view_flow_mode = common.VIEW_FLOW_REQUEST body = self._conn_text( self.flow.request, - self.state.view_body_mode, self.state.get_flow_setting( self.flow, (common.VIEW_FLOW_REQUEST, "prettyview"), - contentview.VIEW_CONTENT_PRETTY_TYPE_AUTO + self.state.default_body_view ) ) self.w = self.wrap_body(common.VIEW_FLOW_REQUEST, body) @@ -218,11 +212,10 @@ class FlowView(common.WWrap): if self.flow.response: body = self._conn_text( self.flow.response, - self.state.view_body_mode, self.state.get_flow_setting( self.flow, (common.VIEW_FLOW_RESPONSE, "prettyview"), - contentview.VIEW_CONTENT_PRETTY_TYPE_AUTO + self.state.default_body_view ) ) else: @@ -377,22 +370,19 @@ class FlowView(common.WWrap): def view_prev_flow(self, flow): return self._view_nextprev_flow("prev", flow) - def change_pretty_type(self, t): - d = { - "a": contentview.VIEW_CONTENT_PRETTY_TYPE_AUTO, - "i": contentview.VIEW_CONTENT_PRETTY_TYPE_IMAGE, - "j": contentview.VIEW_CONTENT_PRETTY_TYPE_JAVASCRIPT, - "s": contentview.VIEW_CONTENT_PRETTY_TYPE_JSON, - "u": contentview.VIEW_CONTENT_PRETTY_TYPE_URLENCODED, - "x": contentview.VIEW_CONTENT_PRETTY_TYPE_XML, - } + def change_this_display_mode(self, t): self.state.add_flow_setting( self.flow, (self.state.view_flow_mode, "prettyview"), - d.get(t) + contentview.VIEW_SHORTCUTS.get(t) ) self.master.refresh_flow(self.flow) + def change_default_display_mode(self, t): + v = contentview.VIEW_SHORTCUTS.get(t) + self.state.default_body_view = v + self.master.refresh_flow(self.flow) + def keypress(self, size, key): if key == " ": self.view_next_flow(self.flow) @@ -464,13 +454,34 @@ class FlowView(common.WWrap): key = None elif key == "m": self.master.prompt_onekey( - "View", + "Display mode", ( - ("raw", "r"), - ("pretty", "p"), + ("auto detect", "a"), ("hex", "h"), + ("image", "i"), + ("javascript", "j"), + ("json", "s"), + ("raw", "r"), + ("urlencoded", "u"), + ("xmlish", "x"), ), - self.master.changeview + self.change_this_display_mode + ) + key = None + elif key == "M": + self.master.prompt_onekey( + "Global default display mode", + ( + ("auto detect", "a"), + ("hex", "h"), + ("image", "i"), + ("javascript", "j"), + ("json", "s"), + ("raw", "r"), + ("urlencoded", "u"), + ("xmlish", "x"), + ), + self.change_default_display_mode ) key = None elif key == "p": @@ -480,20 +491,6 @@ class FlowView(common.WWrap): if r: self.master.statusbar.message(r) self.master.refresh_flow(self.flow) - elif key == "T": - self.master.prompt_onekey( - "Pretty-Print format", - ( - ("auto detect", "a"), - ("image", "i"), - ("javascript", "j"), - ("json", "s"), - ("urlencoded", "u"), - ("xmlish", "x"), - ), - self.change_pretty_type - ) - key = None elif key == "V": self.state.revert(self.flow) self.master.refresh_flow(self.flow) diff --git a/test/test_console_contentview.py b/test/test_console_contentview.py index cc8645df1..28265dec2 100644 --- a/test/test_console_contentview.py +++ b/test/test_console_contentview.py @@ -12,24 +12,21 @@ class uContentView(libpry.AutoTree): def test_get_view_func(self): f = cv.get_view_func( - cv.VIEW_CONTENT_HEX, - cv.VIEW_CONTENT_PRETTY_TYPE_AUTO, + cv.VIEW_HEX, flow.ODictCaseless(), "foo" ) assert f is cv.view_hex f = cv.get_view_func( - cv.VIEW_CONTENT_RAW, - cv.VIEW_CONTENT_PRETTY_TYPE_AUTO, + cv.VIEW_AUTO, flow.ODictCaseless(), "foo" ) assert f is cv.view_raw f = cv.get_view_func( - cv.VIEW_CONTENT_PRETTY, - cv.VIEW_CONTENT_PRETTY_TYPE_AUTO, + cv.VIEW_AUTO, flow.ODictCaseless( [["content-type", "text/html"]], ), @@ -38,8 +35,7 @@ class uContentView(libpry.AutoTree): assert f is cv.view_xmlish f = cv.get_view_func( - cv.VIEW_CONTENT_PRETTY, - cv.VIEW_CONTENT_PRETTY_TYPE_AUTO, + cv.VIEW_AUTO, flow.ODictCaseless( [["content-type", "text/flibble"]], ), @@ -48,8 +44,7 @@ class uContentView(libpry.AutoTree): assert f is cv.view_raw f = cv.get_view_func( - cv.VIEW_CONTENT_PRETTY, - cv.VIEW_CONTENT_PRETTY_TYPE_AUTO, + cv.VIEW_AUTO, flow.ODictCaseless( [["content-type", "text/flibble"]], ), @@ -118,33 +113,28 @@ Larry def test_get_content_view(self): r = cv.get_content_view( - cv.VIEW_CONTENT_RAW, - cv.VIEW_CONTENT_PRETTY_TYPE_AUTO, + cv.VIEW_RAW, [["content-type", "application/json"]], "[1, 2, 3]" ) - assert r[0] == "Raw" + assert "Raw" in r[0] r = cv.get_content_view( - cv.VIEW_CONTENT_PRETTY, - cv.VIEW_CONTENT_PRETTY_TYPE_AUTO, + cv.VIEW_AUTO, [["content-type", "application/json"]], "[1, 2, 3]" ) assert r[0] == "JSON" - r = cv.get_content_view( - cv.VIEW_CONTENT_PRETTY, - cv.VIEW_CONTENT_PRETTY_TYPE_AUTO, + cv.VIEW_AUTO, [["content-type", "application/json"]], "[1, 2" ) assert r[0] == "Raw" r = cv.get_content_view( - cv.VIEW_CONTENT_PRETTY, - cv.VIEW_CONTENT_PRETTY_TYPE_AUTO, + cv.VIEW_AUTO, [ ["content-type", "application/json"], ["content-encoding", "gzip"] @@ -154,10 +144,8 @@ Larry assert "decoded gzip" in r[0] assert "JSON" in r[0] - r = cv.get_content_view( - cv.VIEW_CONTENT_PRETTY, - cv.VIEW_CONTENT_PRETTY_TYPE_XML, + cv.VIEW_XML, [ ["content-type", "application/json"], ["content-encoding", "gzip"]