Merge pull request #467 from mitmproxy/clipboard

Clipboard
This commit is contained in:
Maximilian Hils 2015-02-08 22:18:28 +01:00
commit e0c5f86b20
4 changed files with 165 additions and 32 deletions

View File

@ -1,9 +1,14 @@
from __future__ import absolute_import from __future__ import absolute_import
import urwid import urwid
import urwid.util import urwid.util
import os
from .. import utils from .. import utils
from ..protocol.http import CONTENT_MISSING from ..protocol.http import CONTENT_MISSING, decoded
try:
import pyperclip
except:
pyperclip = False
VIEW_LIST = 0 VIEW_LIST = 0
VIEW_FLOW = 1 VIEW_FLOW = 1
@ -162,6 +167,146 @@ def raw_format_flow(f, focus, extended, padding):
return urwid.Pile(pile) return urwid.Pile(pile)
# Save file to disk
def save_data(path, data, master, state):
if not path:
return
state.last_saveload = path
path = os.path.expanduser(path)
try:
with file(path, "wb") as f:
f.write(data)
except IOError, v:
master.statusbar.message(v.strerror)
def ask_save_path(prompt, data, master, state):
master.path_prompt(
prompt,
state.last_saveload,
save_data,
data,
master,
state
)
def copy_flow_format_data(part, scope, flow):
if part == "u":
data = flow.request.url
else:
data = ""
if scope in ("q", "a"):
with decoded(flow.request):
if part == "h":
data += flow.request.assemble()
elif part == "c":
data += flow.request.content
else:
raise ValueError("Unknown part: {}".format(part))
if scope == "a" and flow.request.content and flow.response:
# Add padding between request and response
data += "\r\n" * 2
if scope in ("s", "a") and flow.response:
with decoded(flow.response):
if part == "h":
data += flow.response.assemble()
elif part == "c":
data += flow.response.content
else:
raise ValueError("Unknown part: {}".format(part))
return data
def copy_flow(part, scope, flow, master, state):
"""
part: _c_ontent, _a_ll, _u_rl
scope: _a_ll, re_q_uest, re_s_ponse
"""
data = copy_flow_format_data(part, scope, flow)
if not data:
if scope == "q":
master.statusbar.message("No request content to copy.")
elif scope == "s":
master.statusbar.message("No response content to copy.")
else:
master.statusbar.message("No contents to copy.")
return
try:
master.add_event(str(len(data)))
pyperclip.copy(data)
except RuntimeError:
def save(k):
if k == "y":
ask_save_path("Save data: ", data, master, state)
master.prompt_onekey(
"Cannot copy binary data to clipboard. Save as file?",
(
("yes", "y"),
("no", "n"),
),
save
)
def ask_copy_part(scope, flow, master, state):
choices = [
("content", "c"),
("headers+content", "h")
]
if scope != "s":
choices.append(("url", "u"))
master.prompt_onekey(
"Copy",
choices,
copy_flow,
scope,
flow,
master,
state
)
def ask_save_body(part, master, state, flow):
"""
Save either the request or the response body to disk.
part can either be "q" (request), "s" (response) or None (ask user if necessary).
"""
request_has_content = flow.request and flow.request.content
response_has_content = flow.response and flow.response.content
if part is None:
# We first need to determine whether we want to save the request or the response content.
if request_has_content and response_has_content:
master.prompt_onekey(
"Save",
(
("request", "q"),
("response", "s"),
),
ask_save_body,
master,
state,
flow
)
elif response_has_content:
ask_save_body("s", master, state, flow)
else:
ask_save_body("q", master, state, flow)
elif part == "q" and request_has_content:
ask_save_path("Save request content: ", flow.request.get_decoded_content(), master, state)
elif part == "s" and response_has_content:
ask_save_path("Save response content: ", flow.response.get_decoded_content(), master, state)
else:
master.statusbar.message("No content to save.")
class FlowCache: class FlowCache:
@utils.LRUCache(200) @utils.LRUCache(200)
def format_flow(self, *args): def format_flow(self, *args):
@ -211,7 +356,6 @@ def format_flow(f, focus, extended=False, hostheader=False, padding=2):
return flowcache.format_flow(tuple(sorted(d.items())), focus, extended, padding) return flowcache.format_flow(tuple(sorted(d.items())), focus, extended, padding)
def int_version(v): def int_version(v):
SIG = 3 SIG = 3
v = urwid.__version__.split("-")[0].split(".") v = urwid.__version__.split("-")[0].split(".")

View File

@ -7,11 +7,13 @@ def _mkhelp():
keys = [ keys = [
("A", "accept all intercepted flows"), ("A", "accept all intercepted flows"),
("a", "accept this intercepted flow"), ("a", "accept this intercepted flow"),
("b", "save request/response body"),
("C", "clear flow list or eventlog"), ("C", "clear flow list or eventlog"),
("d", "delete flow"), ("d", "delete flow"),
("D", "duplicate flow"), ("D", "duplicate flow"),
("e", "toggle eventlog"), ("e", "toggle eventlog"),
("F", "toggle follow flow list"), ("F", "toggle follow flow list"),
("g", "copy flow to clipboard"),
("l", "set limit filter pattern"), ("l", "set limit filter pattern"),
("L", "load saved flows"), ("L", "load saved flows"),
("r", "replay request"), ("r", "replay request"),
@ -204,6 +206,10 @@ class ConnectionItem(common.WWrap):
self.master.run_script_once, self.master.run_script_once,
self.flow self.flow
) )
elif key == "g":
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)
else: else:
return key return key

View File

@ -19,6 +19,7 @@ def _mkhelp():
("D", "duplicate flow"), ("D", "duplicate flow"),
("e", "edit request/response"), ("e", "edit request/response"),
("f", "load full body data"), ("f", "load full body data"),
("g", "copy response(content/headers) to clipboard"),
("m", "change body display mode for this entity"), ("m", "change body display mode for this entity"),
(None, (None,
common.highlight_key("automatic", "a") + common.highlight_key("automatic", "a") +
@ -508,22 +509,6 @@ class FlowView(common.WWrap):
self.flow.request.method = i[0].upper() self.flow.request.method = i[0].upper()
self.master.refresh_flow(self.flow) self.master.refresh_flow(self.flow)
def save_body(self, path):
if not path:
return
self.state.last_saveload = path
if self.state.view_flow_mode == common.VIEW_FLOW_REQUEST:
c = self.flow.request
else:
c = self.flow.response
path = os.path.expanduser(path)
try:
f = file(path, "wb")
f.write(str(c.content))
f.close()
except IOError, v:
self.master.statusbar.message(v.strerror)
def set_url(self, url): def set_url(self, url):
request = self.flow.request request = self.flow.request
try: try:
@ -689,19 +674,10 @@ class FlowView(common.WWrap):
self.master.accept_all() self.master.accept_all()
self.master.view_flow(self.flow) self.master.view_flow(self.flow)
elif key == "b": elif key == "b":
if conn: if self.state.view_flow_mode == common.VIEW_FLOW_REQUEST:
if self.state.view_flow_mode == common.VIEW_FLOW_REQUEST: common.ask_save_body("q", self.master, self.state, self.flow)
self.master.path_prompt( else:
"Save request body: ", common.ask_save_body("s", self.master, self.state, self.flow)
self.state.last_saveload,
self.save_body
)
else:
self.master.path_prompt(
"Save response body: ",
self.state.last_saveload,
self.save_body
)
elif key == "d": elif key == "d":
if self.state.flow_count() == 1: if self.state.flow_count() == 1:
self.master.view_flowlist() self.master.view_flowlist()
@ -752,6 +728,12 @@ class FlowView(common.WWrap):
) )
self.master.refresh_flow(self.flow) self.master.refresh_flow(self.flow)
self.master.statusbar.message("") self.master.statusbar.message("")
elif key == "g":
if self.state.view_flow_mode == common.VIEW_FLOW_REQUEST:
scope = "q"
else:
scope = "s"
common.ask_copy_part(scope, self.flow, self.master, self.state)
elif key == "m": elif key == "m":
p = list(contentview.view_prompts) p = list(contentview.view_prompts)
p.insert(0, ("Clear", "C")) p.insert(0, ("Clear", "C"))

View File

@ -20,7 +20,8 @@ deps = {
"pyasn1>0.1.2", "pyasn1>0.1.2",
"pyOpenSSL>=0.14", "pyOpenSSL>=0.14",
"tornado>=4.0.2", "tornado>=4.0.2",
"configargparse>=0.9.3" "configargparse>=0.9.3",
"pyperclip>=1.5.8"
} }
script_deps = { script_deps = {
"mitmproxy": { "mitmproxy": {