mitmproxy/libmproxy/console/contentview.py

220 lines
6.0 KiB
Python
Raw Normal View History

import re
import urwid
import common
from .. import utils, encoding, flow
from ..contrib import jsbeautifier
VIEW_CUTOFF = 1024*100
VIEW_CONTENT_RAW = 0
VIEW_CONTENT_HEX = 1
VIEW_CONTENT_PRETTY = 2
CONTENT_VIEWS = {
VIEW_CONTENT_RAW: "raw",
VIEW_CONTENT_HEX: "hex",
VIEW_CONTENT_PRETTY: "pretty"
}
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
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",
}
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,
}
def trailer(clen, txt):
rem = clen - VIEW_CUTOFF
if rem > 0:
txt.append(urwid.Text(""))
txt.append(
urwid.Text(
[
("highlight", "... %s of data not shown"%utils.pretty_size(rem))
]
)
)
def _view_text(content):
"""
Generates a body for a chunk of text.
"""
txt = []
for i in utils.cleanBin(content[:VIEW_CUTOFF]).splitlines():
txt.append(
urwid.Text(("text", i))
)
trailer(len(content), txt)
return txt
def view_raw(hdrs, content):
txt = _view_text(content)
return "Raw", txt
def view_hex(hdrs, content):
txt = []
for offset, hexa, s in utils.hexdump(content[:VIEW_CUTOFF]):
txt.append(urwid.Text([
("offset", offset),
" ",
("text", hexa),
" ",
("text", s),
]))
trailer(len(content), txt)
return "HEX", txt
def view_xmlish(hdrs, content):
txt = []
for i in utils.pretty_xmlish(content[:VIEW_CUTOFF]):
txt.append(
urwid.Text(("text", i)),
)
trailer(len(content), txt)
return "XML-like data", txt
def view_json(hdrs, content):
lines = utils.pretty_json(content)
if lines:
txt = []
sofar = 0
for i in lines:
sofar += len(i)
txt.append(
urwid.Text(("text", i)),
)
if sofar > VIEW_CUTOFF:
break
trailer(sum(len(i) for i in lines), txt)
return "JSON", txt
def view_multipart(hdrs, content):
v = hdrs.get("content-type")
if v:
v = utils.parse_content_type(v[0])
if not v:
return
boundary = v[2].get("boundary")
if not boundary:
return
rx = re.compile(r'\bname="([^"]+)"')
keys = []
vals = []
for i in content.split("--" + boundary):
parts = i.splitlines()
if len(parts) > 1 and parts[0][0:2] != "--":
match = rx.search(parts[1])
if match:
keys.append(match.group(1) + ":")
vals.append(utils.cleanBin(
"\n".join(parts[3+parts[2:].index(""):])
))
r = [
urwid.Text(("highlight", "Form data:\n")),
]
r.extend(common.format_keyvals(
zip(keys, vals),
key = "header",
val = "text"
))
return "Multipart form", r
def view_urlencoded(hdrs, content):
lines = utils.urldecode(content)
if lines:
body = common.format_keyvals(
[(k+":", v) for (k, v) in lines],
key = "header",
val = "text"
)
return "URLEncoded form", body
def view_javascript(hdrs, content):
opts = jsbeautifier.default_options()
opts.indent_size = 2
res = jsbeautifier.beautify(content, opts)
return "JavaScript", _view_text(res)
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,
}
def get_view_func(viewmode, pretty_type, 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)
def get_content_view(viewmode, pretty_type, hdrItems, content):
"""
Returns a (msg, body) tuple.
"""
msg = []
hdrs = flow.ODictCaseless([list(i) for i in hdrItems])
enc = hdrs.get("content-encoding")
if enc and enc[0] != "identity":
decoded = encoding.decode(enc[0], 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)
ret = func(hdrs, content)
if not ret:
ret = view_raw(hdrs, content)
msg.append(ret[0])
return " ".join(msg), ret[1]