144 lines
6.1 KiB
Nim
144 lines
6.1 KiB
Nim
import times, tables, strformat
|
|
import imguin/[cimgui, glfw_opengl, simple]
|
|
|
|
import ./console
|
|
import ../utils/appImGui
|
|
import ../../common/[types, utils]
|
|
|
|
type
|
|
SessionsTableComponent* = ref object of RootObj
|
|
title: string
|
|
agents*: seq[UIAgent]
|
|
selection: ptr ImGuiSelectionBasicStorage
|
|
consoles: ptr Table[string, ConsoleComponent]
|
|
|
|
proc SessionsTable*(title: string, consoles: ptr Table[string, ConsoleComponent]): SessionsTableComponent =
|
|
result = new SessionsTableComponent
|
|
result.title = title
|
|
result.agents = @[]
|
|
result.selection = ImGuiSelectionBasicStorage_ImGuiSelectionBasicStorage()
|
|
result.consoles = consoles
|
|
|
|
proc interact(component: SessionsTableComponent) =
|
|
# Open a new console for each selected agent session
|
|
var it: pointer = nil
|
|
var row: ImGuiID
|
|
while ImGuiSelectionBasicStorage_GetNextSelectedItem(component.selection, addr it, addr row):
|
|
let agent = component.agents[cast[int](row)]
|
|
|
|
|
|
# Create a new console window
|
|
if not component.consoles[].hasKey(agent.agentId):
|
|
component.consoles[][agent.agentId] = Console(agent)
|
|
|
|
# Focus the existing console window
|
|
else:
|
|
igSetWindowFocus_Str(fmt"[{agent.agentId}] {agent.username}@{agent.hostname}")
|
|
|
|
# TODO: Clear selection properly
|
|
ImGuiSelectionBasicStorage_Clear(component.selection)
|
|
|
|
proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
|
|
igBegin(component.title, showComponent, 0)
|
|
defer: igEnd()
|
|
|
|
let tableFlags = (
|
|
ImGuiTableFlags_Resizable.int32 or
|
|
ImGuiTableFlags_Reorderable.int32 or
|
|
ImGuiTableFlags_Hideable.int32 or
|
|
ImGuiTableFlags_HighlightHoveredColumn.int32 or
|
|
ImGuiTableFlags_RowBg.int32 or
|
|
ImGuiTableFlags_BordersV.int32 or
|
|
ImGuiTableFlags_BordersH.int32 or
|
|
ImGuiTableFlags_ScrollY.int32 or
|
|
ImGuiTableFlags_ScrollX.int32 or
|
|
ImGuiTableFlags_NoBordersInBodyUntilResize.int32 or
|
|
ImGui_TableFlags_SizingStretchSame.int32
|
|
)
|
|
|
|
let cols: int32 = 8
|
|
if igBeginTable("Sessions", cols, tableFlags, vec2(0.0f, 0.0f), 0.0f):
|
|
|
|
igTableSetupColumn("AgentID", ImGuiTableColumnFlags_NoReorder.int32 or ImGuiTableColumnFlags_NoHide.int32, 0.0f, 0)
|
|
igTableSetupColumn("Address", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
|
|
igTableSetupColumn("Username", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
|
|
igTableSetupColumn("Hostname", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
|
|
igTableSetupColumn("OS", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
|
|
igTableSetupColumn("Process", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
|
|
igTableSetupColumn("PID", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
|
|
igTableSetupColumn("Last seen", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
|
|
|
|
igTableSetupScrollFreeze(0, 1)
|
|
igTableHeadersRow()
|
|
|
|
var multiSelectIO = igBeginMultiSelect(ImGuiMultiSelectFlags_ClearOnEscape.int32 or ImGuiMultiSelectFlags_BoxSelect1d.int32, component.selection[].Size, int32(component.agents.len()))
|
|
ImGuiSelectionBasicStorage_ApplyRequests(component.selection, multiSelectIO)
|
|
|
|
for row in 0 ..< component.agents.len():
|
|
|
|
igTableNextRow(ImGuiTableRowFlags_None.int32, 0.0f)
|
|
let agent = component.agents[row]
|
|
|
|
if igTableSetColumnIndex(0):
|
|
# Enable multi-select functionality
|
|
igSetNextItemSelectionUserData(row)
|
|
var isSelected = ImGuiSelectionBasicStorage_Contains(component.selection, cast[ImGuiID](row))
|
|
discard igSelectable_Bool(agent.agentId, isSelected, ImGuiSelectableFlags_SpanAllColumns.int32, vec2(0.0f, 0.0f))
|
|
|
|
# Interact with session on double-click
|
|
if igIsMouseDoubleClicked_Nil(ImGui_MouseButton_Left.int32):
|
|
component.interact()
|
|
|
|
if igTableSetColumnIndex(1):
|
|
igText(agent.ip)
|
|
if igTableSetColumnIndex(2):
|
|
igText(agent.username)
|
|
if igTableSetColumnIndex(3):
|
|
igText(agent.hostname)
|
|
if igTableSetColumnIndex(4):
|
|
igText(agent.os)
|
|
if igTableSetColumnIndex(5):
|
|
igText(agent.process)
|
|
if igTableSetColumnIndex(6):
|
|
igText($agent.pid)
|
|
if igTableSetColumnIndex(7):
|
|
let duration = now() - agent.latestCheckin.fromUnix().utc()
|
|
let totalSeconds = duration.inSeconds
|
|
|
|
let hours = totalSeconds div 3600
|
|
let minutes = (totalSeconds mod 3600) div 60
|
|
let seconds = totalSeconds mod 60
|
|
|
|
let dummyTime = dateTime(2000, mJan, 1, hours.int, minutes.int, seconds.int)
|
|
let timeText = dummyTime.format("HH:mm:ss")
|
|
|
|
igText(fmt"{timeText} ago")
|
|
|
|
# Handle right-click context menu
|
|
# Right-clicking the table header to hide/show columns or reset the layout is only possible when no sessions are selected
|
|
if component.selection[].Size > 0 and igBeginPopupContextWindow("TableContextMenu", ImGui_PopupFlags_MouseButtonRight.int32):
|
|
|
|
if igMenuItem("Interact", nil, false, true):
|
|
component.interact()
|
|
igCloseCurrentPopup()
|
|
|
|
if igMenuItem("Remove", nil, false, true):
|
|
# Update agents table with only non-selected ones
|
|
var newAgents: seq[UIAgent] = @[]
|
|
for i in 0 ..< component.agents.len():
|
|
if not ImGuiSelectionBasicStorage_Contains(component.selection, cast[ImGuiID](i)):
|
|
newAgents.add(component.agents[i])
|
|
|
|
component.agents = newAgents
|
|
ImGuiSelectionBasicStorage_Clear(component.selection)
|
|
igCloseCurrentPopup()
|
|
|
|
igEndPopup()
|
|
|
|
multiSelectIO = igEndMultiSelect()
|
|
ImGuiSelectionBasicStorage_ApplyRequests(component.selection, multiSelectIO)
|
|
|
|
igEndTable()
|
|
|
|
|
|
|