console: first pass of a Set-cookie editor for responses

This commit is contained in:
Aldo Cortesi 2015-04-16 10:57:12 +12:00
parent 850a50262b
commit 52716e3439
3 changed files with 195 additions and 65 deletions

View File

@ -66,20 +66,26 @@ def format_keyvals(lst, key="key", val="text", indent=0):
if kv is None:
ret.append(urwid.Text(""))
else:
cols = []
# This cumbersome construction process is here for a reason:
# Urwid < 1.0 barfs if given a fixed size column of size zero.
if indent:
cols.append(("fixed", indent, urwid.Text("")))
cols.extend([
if isinstance(kv[1], urwid.Widget):
v = kv[1]
elif kv[1] is None:
v = urwid.Text("")
else:
v = urwid.Text([(val, kv[1])])
ret.append(
urwid.Columns(
[
("fixed", indent, urwid.Text("")),
(
"fixed",
maxk,
urwid.Text([(key, kv[0] or "")])
),
kv[1] if isinstance(kv[1], urwid.Widget) else urwid.Text([(val, kv[1])])
])
ret.append(urwid.Columns(cols, dividechars = 2))
v
],
dividechars = 2
)
)
return ret

View File

@ -316,6 +316,19 @@ class FlowView(tabs.Tabs):
conn.set_cookies(od)
signals.flow_change.send(self, flow = self.flow)
def set_setcookies(self, lst, conn):
vals = []
for i in lst:
vals.append(
[
i[0],
[i[1], odict.ODictCaseless(i[2])]
]
)
od = odict.ODict(vals)
conn.set_cookies(od)
signals.flow_change.send(self, flow = self.flow)
def edit(self, part):
if self.tab_offset == TAB_REQ:
message = self.flow.request
@ -338,7 +351,18 @@ class FlowView(tabs.Tabs):
message
)
)
pass
if message == self.flow.response and part == "c":
flattened = []
for k, v in message.get_cookies().items():
flattened.append([k, v[0], v[1].lst])
self.master.view_grideditor(
grideditor.SetCookieEditor(
self.master,
flattened,
self.set_setcookies,
message
)
)
if part == "r":
with decoded(message):
# Fix an issue caused by some editors when editing a
@ -541,7 +565,7 @@ class FlowView(tabs.Tabs):
signals.status_prompt_onekey.send(
prompt = "Edit request",
keys = (
("cookie", "c"),
("cookies", "c"),
("query", "q"),
("path", "p"),
("url", "u"),
@ -556,6 +580,7 @@ class FlowView(tabs.Tabs):
signals.status_prompt_onekey.send(
prompt = "Edit response",
keys = (
("cookies", "c"),
("code", "o"),
("message", "m"),
("header", "h"),

View File

@ -7,7 +7,7 @@ import urwid
from . import common, signals
from .. import utils, filt, script
from netlib import http_uastrings
from netlib import http_uastrings, http_cookies
FOOTER = [
@ -19,17 +19,36 @@ FOOTER_EDITING = [
]
class SText(urwid.WidgetWrap):
def __init__(self, txt, focused, error):
class TextColumn:
subeditor = None
def __init__(self, heading):
self.heading = heading
def text(self, obj):
return SEscaped(obj or "")
def blank(self):
return ""
class SubgridColumn:
def __init__(self, heading, subeditor):
self.heading = heading
self.subeditor = subeditor
def text(self, obj):
p = http_cookies._format_pairs(obj, sep="\n")
return urwid.Text(p)
def blank(self):
return []
class SEscaped(urwid.WidgetWrap):
def __init__(self, txt):
txt = txt.encode("string-escape")
w = urwid.Text(txt, wrap="any")
if focused:
if error:
w = urwid.AttrWrap(w, "focusfield_error")
else:
w = urwid.AttrWrap(w, "focusfield")
elif error:
w = urwid.AttrWrap(w, "field_error")
urwid.WidgetWrap.__init__(self, w)
def get_text(self):
@ -50,7 +69,7 @@ class SEdit(urwid.WidgetWrap):
urwid.WidgetWrap.__init__(self, w)
def get_text(self):
return self._w.get_text()[0]
return self._w.get_text()[0].strip()
def selectable(self):
return True
@ -67,9 +86,15 @@ class GridRow(urwid.WidgetWrap):
self.editing = SEdit(v)
self.fields.append(self.editing)
else:
self.fields.append(
SText(v, True if focused == i else False, i in errors)
)
w = self.editor.columns[i].text(v)
if focused == i:
if i in errors:
w = urwid.AttrWrap(w, "focusfield_error")
else:
w = urwid.AttrWrap(w, "focusfield")
elif i in errors:
w = urwid.AttrWrap(w, "field_error")
self.fields.append(w)
fspecs = self.fields[:]
if len(self.fields) > 1:
@ -126,7 +151,9 @@ class GridWalker(urwid.ListWalker):
val = val.decode("string-escape")
except ValueError:
signals.status_message.send(
self, message = "Invalid Python-style string encoding.", expure = 1000
self,
message = "Invalid Python-style string encoding.",
expire = 1000
)
return
errors = self.lst[self.focus][1]
@ -136,10 +163,15 @@ class GridWalker(urwid.ListWalker):
errors.add(self.focus_col)
else:
errors.discard(self.focus_col)
self.set_value(val, self.focus, self.focus_col, errors)
row = list(self.lst[self.focus][0])
row[self.focus_col] = val
self.lst[self.focus] = [tuple(row), errors]
def set_value(self, val, focus, focus_col, errors=None):
if not errors:
errors = set([])
row = list(self.lst[focus][0])
row[focus_col] = val
self.lst[focus] = [tuple(row), errors]
self._modified()
def delete_focus(self):
if self.lst:
@ -149,7 +181,10 @@ class GridWalker(urwid.ListWalker):
def _insert(self, pos):
self.focus = pos
self.lst.insert(self.focus, [[""]*self.editor.columns, set([])])
self.lst.insert(
self.focus, [
[c.blank() for c in self.editor.columns], set([])]
)
self.focus_col = 0
self.start_edit()
@ -179,12 +214,12 @@ class GridWalker(urwid.ListWalker):
self._modified()
def right(self):
self.focus_col = min(self.focus_col + 1, self.editor.columns-1)
self.focus_col = min(self.focus_col + 1, len(self.editor.columns)-1)
self._modified()
def tab_next(self):
self.stop_edit()
if self.focus_col < self.editor.columns-1:
if self.focus_col < len(self.editor.columns)-1:
self.focus_col += 1
elif self.focus != len(self.lst)-1:
self.focus_col = 0
@ -231,7 +266,6 @@ FIRST_WIDTH_MIN = 20
class GridEditor(urwid.WidgetWrap):
title = None
columns = None
headings = None
def __init__(self, master, value, callback, *cb_args, **cb_kwargs):
value = copy.deepcopy(value)
@ -241,7 +275,7 @@ class GridEditor(urwid.WidgetWrap):
first_width = 20
if value:
for r in value:
assert len(r) == self.columns
assert len(r) == len(self.columns)
first_width = max(len(r), first_width)
self.first_width = min(first_width, FIRST_WIDTH_MAX)
@ -250,9 +284,9 @@ class GridEditor(urwid.WidgetWrap):
title = urwid.AttrWrap(title, "heading")
headings = []
for i, h in enumerate(self.headings):
c = urwid.Text(h)
if i == 0 and len(self.headings) > 1:
for i, col in enumerate(self.columns):
c = urwid.Text(col.heading)
if i == 0 and len(self.columns) > 1:
headings.append(("fixed", first_width + 2, c))
else:
headings.append(c)
@ -303,6 +337,9 @@ class GridEditor(urwid.WidgetWrap):
except IOError, v:
return str(v)
def set_subeditor_value(self, val, focus, focus_col):
self.walker.set_value(val, focus, focus_col)
def keypress(self, size, key):
if self.walker.editing:
if key in ["esc"]:
@ -317,10 +354,11 @@ class GridEditor(urwid.WidgetWrap):
return None
key = common.shortcuts(key)
column = self.columns[self.walker.focus_col]
if key in ["q", "esc"]:
res = []
for i in self.walker.lst:
if not i[1] and any([x.strip() for x in i[0]]):
if not i[1] and any([x for x in i[0]]):
res.append(i[0])
self.callback(res, *self.cb_args, **self.cb_kwargs)
signals.pop_view_state.send(self)
@ -337,6 +375,13 @@ class GridEditor(urwid.WidgetWrap):
elif key == "d":
self.walker.delete_focus()
elif key == "r":
if column.subeditor:
signals.status_message.send(
self,
message = "Press enter to edit this field.",
expire = 1000
)
return
if self.walker.get_current_value() is not None:
signals.status_prompt_path.send(
self,
@ -344,6 +389,13 @@ class GridEditor(urwid.WidgetWrap):
callback = self.read_file
)
elif key == "R":
if column.subeditor:
signals.status_message.send(
self,
message = "Press enter to edit this field.",
expire = 1000
)
return
if self.walker.get_current_value() is not None:
signals.status_prompt_path.send(
self,
@ -352,6 +404,13 @@ class GridEditor(urwid.WidgetWrap):
args = (True,)
)
elif key == "e":
if column.subeditor:
signals.status_message.send(
self,
message = "Press enter to edit this field.",
expire = 1000
)
return
o = self.walker.get_current_value()
if o is not None:
n = self.master.spawn_editor(o.encode("string-escape"))
@ -359,7 +418,19 @@ class GridEditor(urwid.WidgetWrap):
self.walker.set_current_value(n, False)
self.walker._modified()
elif key in ["enter"]:
if column.subeditor:
self.master.view_grideditor(
self.columns[self.walker.focus_col].subeditor(
self.master,
self.walker.get_current_value(),
self.set_subeditor_value,
self.walker.focus,
self.walker.focus_col
)
)
else:
self.walker.start_edit()
elif not self.handle_key(key):
return self._w.keypress(size, key)
@ -403,14 +474,18 @@ class GridEditor(urwid.WidgetWrap):
class QueryEditor(GridEditor):
title = "Editing query"
columns = 2
headings = ("Key", "Value")
columns = [
TextColumn("Key"),
TextColumn("Value")
]
class HeaderEditor(GridEditor):
title = "Editing headers"
columns = 2
headings = ("Key", "Value")
columns = [
TextColumn("Key"),
TextColumn("Value")
]
def make_help(self):
h = GridEditor.make_help(self)
@ -448,14 +523,19 @@ class HeaderEditor(GridEditor):
class URLEncodedFormEditor(GridEditor):
title = "Editing URL-encoded form"
columns = 2
headings = ("Key", "Value")
columns = [
TextColumn("Key"),
TextColumn("Value")
]
class ReplaceEditor(GridEditor):
title = "Editing replacement patterns"
columns = 3
headings = ("Filter", "Regex", "Replacement")
columns = [
TextColumn("Filter"),
TextColumn("Regex"),
TextColumn("Replacement"),
]
def is_error(self, col, val):
if col == 0:
@ -471,8 +551,11 @@ class ReplaceEditor(GridEditor):
class SetHeadersEditor(GridEditor):
title = "Editing header set patterns"
columns = 3
headings = ("Filter", "Header", "Value")
columns = [
TextColumn("Filter"),
TextColumn("Header"),
TextColumn("Value"),
]
def is_error(self, col, val):
if col == 0:
@ -517,14 +600,16 @@ class SetHeadersEditor(GridEditor):
class PathEditor(GridEditor):
title = "Editing URL path components"
columns = 1
headings = ("Component",)
columns = [
TextColumn("Component"),
]
class ScriptEditor(GridEditor):
title = "Editing scripts"
columns = 1
headings = ("Command",)
columns = [
TextColumn("Command"),
]
def is_error(self, col, val):
try:
@ -535,8 +620,9 @@ class ScriptEditor(GridEditor):
class HostPatternEditor(GridEditor):
title = "Editing host patterns"
columns = 1
headings = ("Regex (matched on hostname:port / ip:port)",)
columns = [
TextColumn("Regex (matched on hostname:port / ip:port)")
]
def is_error(self, col, val):
try:
@ -547,11 +633,24 @@ class HostPatternEditor(GridEditor):
class CookieEditor(GridEditor):
title = "Editing request Cookie header"
columns = 2
headings = ("Name", "Value")
columns = [
TextColumn("Name"),
TextColumn("Value"),
]
class CookieAttributeEditor(GridEditor):
title = "Editing Set-Cookie attributes"
columns = [
TextColumn("Name"),
TextColumn("Value"),
]
class SetCookieEditor(GridEditor):
title = "Editing request SetCookie header"
columns = 2
headings = ("Name", "Value")
title = "Editing response SetCookie header"
columns = [
TextColumn("Name"),
TextColumn("Value"),
SubgridColumn("Attributes", CookieAttributeEditor),
]