srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'cmd')
-rw-r--r--cmd/mitmux/main.go92
1 files changed, 89 insertions, 3 deletions
diff --git a/cmd/mitmux/main.go b/cmd/mitmux/main.go
index 83e59cd..dbc919d 100644
--- a/cmd/mitmux/main.go
+++ b/cmd/mitmux/main.go
@@ -203,6 +203,14 @@ type model struct {
decoderInput viTextarea
decoderOutput viewport.Model
+ // confirmPrompt, when non-empty, takes over the status line and
+ // every keypress until resolved: 'y' runs confirmYes, anything else
+ // cancels. Used to gate destructive actions (deleting a history
+ // entry, clearing all of it) behind an explicit second keypress
+ // rather than a single accidental one.
+ confirmPrompt string
+ confirmYes func() tea.Cmd
+
statusMsg string
width int
height int
@@ -352,6 +360,27 @@ func (m *model) setFlagged(id int64, flagged bool) tea.Cmd {
}
}
+type entryDeletedMsg struct {
+ id int64
+ err error
+}
+
+func (m *model) deleteEntry(id int64) tea.Cmd {
+ return func() tea.Msg {
+ return entryDeletedMsg{id: id, err: m.client.DeleteEntry(id)}
+ }
+}
+
+type historyClearedMsg struct {
+ err error
+}
+
+func (m *model) clearHistory() tea.Cmd {
+ return func() tea.Msg {
+ return historyClearedMsg{err: m.client.ClearHistory()}
+ }
+}
+
func (m *model) loadList() tea.Msg {
var entries []store.Summary
var err error
@@ -750,6 +779,24 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
return m, nil
+ case entryDeletedMsg:
+ if msg.err != nil {
+ m.statusMsg = "delete error: " + msg.err.Error()
+ return m, nil
+ }
+ m.statusMsg = fmt.Sprintf("deleted #%d", msg.id)
+ return m, tea.Batch(m.loadList, m.loadStatus)
+
+ case historyClearedMsg:
+ if msg.err != nil {
+ m.statusMsg = "clear error: " + msg.err.Error()
+ return m, nil
+ }
+ m.compareBaseID = 0
+ m.pendingNew = 0
+ m.statusMsg = "history cleared"
+ return m, tea.Batch(m.loadList, m.loadStatus)
+
case detailLoadedMsg:
if msg.err != nil {
m.statusMsg = "get error: " + msg.err.Error()
@@ -850,6 +897,17 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case tea.KeyMsg:
switch m.mode {
case viewList:
+ if m.confirmPrompt != "" {
+ yes := msg.String() == "y" || msg.String() == "Y"
+ action := m.confirmYes
+ m.confirmPrompt = ""
+ m.confirmYes = nil
+ if yes && action != nil {
+ return m, action()
+ }
+ m.statusMsg = "cancelled"
+ return m, nil
+ }
if m.searching {
switch msg.String() {
case "enter":
@@ -902,6 +960,29 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if row := m.table.Cursor(); row >= 0 && row < len(m.entries) {
return m, m.markOrCompare(m.entries[row].ID)
}
+ case "x":
+ if row := m.table.Cursor(); row >= 0 && row < len(m.entries) {
+ id := m.entries[row].ID
+ m.confirmPrompt = fmt.Sprintf("delete #%d? y/n", id)
+ m.confirmYes = func() tea.Cmd { return m.deleteEntry(id) }
+ }
+ return m, nil
+ case "X":
+ // Always clears the whole database, never just the
+ // current filtered view - m.entries under an active
+ // search query would understate the real count and make
+ // the prompt itself misleading about what's about to
+ // happen.
+ count := "all"
+ if m.daemonStatus != nil {
+ count = fmt.Sprintf("all %d", m.daemonStatus.HistoryCount)
+ }
+ if m.daemonStatus != nil && m.daemonStatus.HistoryCount == 0 {
+ return m, nil
+ }
+ m.confirmPrompt = fmt.Sprintf("clear %s history entries (not just this view)? y/n", count)
+ m.confirmYes = func() tea.Cmd { return m.clearHistory() }
+ return m, nil
case "d":
m.mode = viewDecoder
m.statusMsg = ""
@@ -1424,6 +1505,8 @@ func (m *model) helpView() string {
"i open in Intruder",
"f toggle flag (★ mark this, revisit later)",
"c mark for comparison, then press c on another entry to diff",
+ "x delete the selected entry (asks to confirm)",
+ "X clear ALL history, not just the current filter (asks to confirm)",
"d decoder (URL/Base64/Hex/HTML encode/decode)",
"/ search: plain text, host:value, AND/OR/NOT,",
" status:404 / status:4xx / status:>=400,",
@@ -1506,13 +1589,16 @@ func (m *model) listView() string {
}
b.WriteString(m.table.View())
b.WriteString("\n")
- if m.statusMsg != "" {
+ if m.confirmPrompt != "" {
+ b.WriteString(statusStyle.Render(m.confirmPrompt))
+ b.WriteString("\n")
+ } else if m.statusMsg != "" {
b.WriteString(statusStyle.Render(sanitizeLine(m.statusMsg)))
b.WriteString("\n")
}
- help := "enter view · r/i/c/d tools · f flag · / search · m rules · ? help · q quit"
+ help := "enter view · r/i/c/d tools · f flag · x delete · X clear all · / search · m rules · ? help · q quit"
if m.query != "" {
- help = "enter view · r/i/c/d tools · f flag · / search · esc clear filter · ? help · q quit"
+ help = "enter view · r/i/c/d tools · f flag · x delete · X clear all · / search · esc clear filter · ? help · q quit"
}
b.WriteString(helpStyle.Render(help))
return b.String()