You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
fw-daemon/fw-prompt/fw-prompt.go

1028 lines
23 KiB

package main
import (
"encoding/json"
"errors"
"fmt"
"github.com/gotk3/gotk3/glib"
"github.com/gotk3/gotk3/gtk"
"io/ioutil"
"log"
"os"
"os/user"
"strconv"
"strings"
"sync"
"time"
"github.com/subgraph/fw-daemon/sgfw"
)
type fpPreferences struct {
Winheight uint
Winwidth uint
Wintop uint
Winleft uint
}
type decisionWaiter struct {
Cond *sync.Cond
Lock sync.Locker
Ready bool
Scope int
Rule string
}
type ruleColumns struct {
Path string
Proto string
Pid int
Target string
Hostname string
Port int
UID int
GID int
Uname string
Gname string
Origin string
Scope int
}
var userPrefs fpPreferences
var mainWin *gtk.Window
var Notebook *gtk.Notebook
var globalLS *gtk.ListStore
var globalTV *gtk.TreeView
var decisionWaiters []*decisionWaiter
var editApp, editTarget, editPort, editUser, editGroup *gtk.Entry
var comboProto *gtk.ComboBoxText
var radioOnce, radioProcess, radioParent, radioSession, radioPermanent *gtk.RadioButton
var btnApprove, btnDeny, btnIgnore *gtk.Button
var chkUser, chkGroup *gtk.CheckButton
func dumpDecisions() {
fmt.Println("XXX Total of decisions pending: ", len(decisionWaiters))
for i := 0; i < len(decisionWaiters); i++ {
fmt.Printf("XXX %d ready = %v, rule = %v\n", i+1, decisionWaiters[i].Ready, decisionWaiters[i].Rule)
}
}
func addDecision() *decisionWaiter {
decision := decisionWaiter{Lock: &sync.Mutex{}, Ready: false, Scope: int(sgfw.APPLY_ONCE), Rule: ""}
decision.Cond = sync.NewCond(decision.Lock)
decisionWaiters = append(decisionWaiters, &decision)
return &decision
}
func promptInfo(msg string) {
dialog := gtk.MessageDialogNew(mainWin, 0, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, "Displaying full log info:")
// dialog.SetDefaultGeometry(500, 200)
tv, err := gtk.TextViewNew()
if err != nil {
log.Fatal("Unable to create TextView:", err)
}
tvbuf, err := tv.GetBuffer()
if err != nil {
log.Fatal("Unable to get buffer:", err)
}
tvbuf.SetText(msg)
tv.SetEditable(false)
tv.SetWrapMode(gtk.WRAP_WORD)
scrollbox, err := gtk.ScrolledWindowNew(nil, nil)
if err != nil {
log.Fatal("Unable to create scrolled window:", err)
}
scrollbox.Add(tv)
scrollbox.SetSizeRequest(500, 100)
box, err := dialog.GetContentArea()
if err != nil {
log.Fatal("Unable to get content area of dialog:", err)
}
box.Add(scrollbox)
dialog.ShowAll()
dialog.Run()
dialog.Destroy()
//self.set_default_size(150, 100)
}
func promptChoice(msg string) int {
dialog := gtk.MessageDialogNew(mainWin, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_YES_NO, msg)
result := dialog.Run()
dialog.Destroy()
return result
}
func promptError(msg string) {
dialog := gtk.MessageDialogNew(mainWin, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, "Error: %s", msg)
dialog.Run()
dialog.Destroy()
}
func getConfigPath() string {
usr, err := user.Current()
if err != nil {
fmt.Fprintf(os.Stderr, "Error: could not determine location of user preferences file:", err, "\n")
return ""
}
prefPath := usr.HomeDir + "/.fwprompt.json"
return prefPath
}
func savePreferences() bool {
usr, err := user.Current()
if err != nil {
fmt.Fprintf(os.Stderr, "Error: could not determine location of user preferences file:", err, "\n")
return false
}
prefPath := usr.HomeDir + "/.fwprompt.json"
jsonPrefs, err := json.Marshal(userPrefs)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: could not generate user preferences data:", err, "\n")
return false
}
err = ioutil.WriteFile(prefPath, jsonPrefs, 0644)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: could not save user preferences data:", err, "\n")
return false
}
return true
}
func loadPreferences() bool {
usr, err := user.Current()
if err != nil {
fmt.Fprintf(os.Stderr, "Error: could not determine location of user preferences file: %v", err, "\n")
return false
}
prefPath := usr.HomeDir + "/.fwprompt.json"
jfile, err := ioutil.ReadFile(prefPath)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: could not read preference data from file: %v", err, "\n")
return false
}
err = json.Unmarshal(jfile, &userPrefs)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: could not load preferences data from file: %v", err, "\n")
return false
}
fmt.Println(userPrefs)
return true
}
func get_hbox() *gtk.Box {
hbox, err := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
if err != nil {
log.Fatal("Unable to create horizontal box:", err)
}
return hbox
}
func get_vbox() *gtk.Box {
vbox, err := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
if err != nil {
log.Fatal("Unable to create vertical box:", err)
}
return vbox
}
func get_checkbox(text string, activated bool) *gtk.CheckButton {
cb, err := gtk.CheckButtonNewWithLabel(text)
if err != nil {
log.Fatal("Unable to create new checkbox:", err)
}
cb.SetActive(activated)
return cb
}
func get_combobox() *gtk.ComboBoxText {
combo, err := gtk.ComboBoxTextNew()
if err != nil {
log.Fatal("Unable to create combo box:", err)
}
combo.Append("tcp", "TCP")
combo.Append("udp", "UDP")
combo.Append("icmp", "ICMP")
combo.SetActive(0)
return combo
}
func get_radiobutton(group *gtk.RadioButton, label string, activated bool) *gtk.RadioButton {
if group == nil {
radiobutton, err := gtk.RadioButtonNewWithLabel(nil, label)
if err != nil {
log.Fatal("Unable to create radio button:", err)
}
radiobutton.SetActive(activated)
return radiobutton
}
radiobutton, err := gtk.RadioButtonNewWithLabelFromWidget(group, label)
if err != nil {
log.Fatal("Unable to create radio button in group:", err)
}
radiobutton.SetActive(activated)
return radiobutton
}
func get_entry(text string) *gtk.Entry {
entry, err := gtk.EntryNew()
if err != nil {
log.Fatal("Unable to create text entry:", err)
}
entry.SetText(text)
return entry
}
func get_label(text string) *gtk.Label {
label, err := gtk.LabelNew(text)
if err != nil {
log.Fatal("Unable to create label in GUI:", err)
return nil
}
return label
}
func createColumn(title string, id int) *gtk.TreeViewColumn {
cellRenderer, err := gtk.CellRendererTextNew()
if err != nil {
log.Fatal("Unable to create text cell renderer:", err)
}
column, err := gtk.TreeViewColumnNewWithAttribute(title, cellRenderer, "text", id)
if err != nil {
log.Fatal("Unable to create cell column:", err)
}
column.SetSortColumnID(id)
column.SetResizable(true)
return column
}
func createListStore(general bool) *gtk.ListStore {
colData := []glib.Type{glib.TYPE_INT, glib.TYPE_STRING, glib.TYPE_STRING, glib.TYPE_INT, glib.TYPE_STRING, glib.TYPE_STRING, glib.TYPE_INT, glib.TYPE_INT, glib.TYPE_INT, glib.TYPE_STRING, glib.TYPE_STRING}
listStore, err := gtk.ListStoreNew(colData...)
if err != nil {
log.Fatal("Unable to create list store:", err)
}
return listStore
}
func addRequest(listStore *gtk.ListStore, path, proto string, pid int, ipaddr, hostname string, port, uid, gid int, origin string, is_socks bool, optstring string, sandbox string) *decisionWaiter {
if listStore == nil {
listStore = globalLS
waitTimes := []int{1, 2, 5, 10}
if listStore == nil {
log.Print("SGFW prompter was not ready to receive firewall request... waiting")
}
for _, wtime := range waitTimes {
time.Sleep(time.Duration(wtime) * time.Second)
listStore = globalLS
if listStore != nil {
break
}
log.Print("SGFW prompter is still waiting...")
}
}
if listStore == nil {
log.Fatal("SGFW prompter GUI failed to load for unknown reasons")
}
iter := listStore.Append()
if is_socks {
if (optstring != "") && (strings.Index(optstring, "SOCKS") == -1) {
optstring = "SOCKS5 / " + optstring
} else if optstring == "" {
optstring = "SOCKS5"
}
}
colVals := make([]interface{}, 11)
colVals[0] = 1
colVals[1] = path
colVals[2] = proto
colVals[3] = pid
if ipaddr == "" {
colVals[4] = "---"
} else {
colVals[4] = ipaddr
}
colVals[5] = hostname
colVals[6] = port
colVals[7] = uid
colVals[8] = gid
colVals[9] = origin
colVals[10] = optstring
colNums := make([]int, len(colVals))
for n := 0; n < len(colVals); n++ {
colNums[n] = n
}
err := listStore.Set(iter, colNums, colVals)
if err != nil {
log.Fatal("Unable to add row:", err)
}
decision := addDecision()
dumpDecisions()
toggleHover()
return decision
}
func setup_settings() {
box, err := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
if err != nil {
log.Fatal("Unable to create settings box:", err)
}
scrollbox, err := gtk.ScrolledWindowNew(nil, nil)
if err != nil {
log.Fatal("Unable to create settings scrolled window:", err)
}
hLabel, err := gtk.LabelNew("Settings")
if err != nil {
log.Fatal("Unable to create notebook label:", err)
}
scrollbox.Add(box)
scrollbox.SetSizeRequest(600, 400)
tv, err := gtk.TreeViewNew()
if err != nil {
log.Fatal("Unable to create treeview:", err)
}
h := get_hbox()
l := get_label("Log to file:")
b, err := gtk.ButtonNewWithLabel("Save")
if err != nil {
log.Fatal("Unable to create button:", err)
}
h.PackStart(l, false, true, 10)
h.PackStart(b, false, true, 10)
h.SetMarginTop(10)
box.Add(h)
h = get_hbox()
h.SetMarginTop(0)
h.SetMarginBottom(20)
box.Add(h)
box.Add(tv)
b.Connect("clicked", func() {
fmt.Println("CLICKED")
if err != nil {
promptError("Unexpected error saving log file info: " + err.Error())
return
}
})
Notebook.AppendPage(scrollbox, hLabel)
}
func lsGetStr(ls *gtk.ListStore, iter *gtk.TreeIter, idx int) (string, error) {
val, err := globalLS.GetValue(iter, idx)
if err != nil {
return "", err
}
sval, err := val.GetString()
if err != nil {
return "", err
}
return sval, nil
}
func lsGetInt(ls *gtk.ListStore, iter *gtk.TreeIter, idx int) (int, error) {
val, err := globalLS.GetValue(iter, idx)
if err != nil {
return 0, err
}
ival, err := val.GoValue()
if err != nil {
return 0, err
}
return ival.(int), nil
}
func makeDecision(idx int, rule string, scope int) {
decisionWaiters[idx].Cond.L.Lock()
decisionWaiters[idx].Rule = rule
decisionWaiters[idx].Scope = scope
decisionWaiters[idx].Ready = true
decisionWaiters[idx].Cond.Signal()
decisionWaiters[idx].Cond.L.Unlock()
}
func toggleHover() {
mainWin.SetKeepAbove(len(decisionWaiters) > 0)
}
func toggleValidRuleState() {
ok := true
if numSelections() <= 0 {
ok = false
}
str, err := editApp.GetText()
if err != nil || strings.Trim(str, "\t ") == "" {
ok = false
}
str, err = editTarget.GetText()
if err != nil || strings.Trim(str, "\t ") == "" {
ok = false
}
str, err = editPort.GetText()
if err != nil || strings.Trim(str, "\t ") == "" {
ok = false
} else {
pval, err := strconv.Atoi(str)
if err != nil || pval < 0 || pval > 65535 {
ok = false
}
}
if chkUser.GetActive() {
str, err = editUser.GetText()
if err != nil || strings.Trim(str, "\t ") == "" {
ok = false
}
}
if chkGroup.GetActive() {
str, err = editGroup.GetText()
if err != nil || strings.Trim(str, "\t ") == "" {
ok = false
}
}
btnApprove.SetSensitive(ok)
btnDeny.SetSensitive(ok)
btnIgnore.SetSensitive(ok)
}
func createCurrentRule() (ruleColumns, error) {
rule := ruleColumns{Scope: int(sgfw.APPLY_ONCE)}
var err error = nil
if radioProcess.GetActive() {
rule.Scope = int(sgfw.APPLY_PROCESS)
} else if radioParent.GetActive() {
return rule, errors.New("Parent process scope is unsupported at the moment")
} else if radioSession.GetActive() {
rule.Scope = int(sgfw.APPLY_SESSION)
} else if radioPermanent.GetActive() {
rule.Scope = int(sgfw.APPLY_FOREVER)
} else {
rule.Scope = int(sgfw.APPLY_ONCE)
}
rule.Path, err = editApp.GetText()
if err != nil {
return rule, err
}
ports, err := editPort.GetText()
if err != nil {
return rule, err
}
rule.Port, err = strconv.Atoi(ports)
if err != nil {
return rule, err
}
rule.Target, err = editTarget.GetText()
if err != nil {
return rule, err
}
rule.Proto = comboProto.GetActiveID()
rule.UID, rule.GID = 0, 0
rule.Uname, rule.Gname = "", ""
/* Pid int
Origin string */
return rule, nil
}
func clearEditor() {
editApp.SetText("")
editTarget.SetText("")
editPort.SetText("")
editUser.SetText("")
editGroup.SetText("")
comboProto.SetActive(0)
radioOnce.SetActive(true)
radioProcess.SetActive(false)
radioParent.SetActive(false)
radioSession.SetActive(false)
radioPermanent.SetActive(false)
chkUser.SetActive(false)
chkGroup.SetActive(false)
}
func removeSelectedRule(idx int, rmdecision bool) error {
fmt.Println("XXX: attempting to remove idx = ", idx)
path, err := gtk.TreePathNewFromString(fmt.Sprintf("%d", idx))
if err != nil {
return err
}
iter, err := globalLS.GetIter(path)
if err != nil {
return err
}
globalLS.Remove(iter)
if rmdecision {
decisionWaiters = append(decisionWaiters[:idx], decisionWaiters[idx+1:]...)
}
toggleHover()
return nil
}
func numSelections() int {
sel, err := globalTV.GetSelection()
if err != nil {
return -1
}
rows := sel.GetSelectedRows(globalLS)
return int(rows.Length())
}
func getSelectedRule() (ruleColumns, int, error) {
rule := ruleColumns{}
sel, err := globalTV.GetSelection()
if err != nil {
return rule, -1, err
}
rows := sel.GetSelectedRows(globalLS)
if rows.Length() <= 0 {
return rule, -1, errors.New("No selection was made")
}
rdata := rows.NthData(0)
lIndex, err := strconv.Atoi(rdata.(*gtk.TreePath).String())
if err != nil {
return rule, -1, err
}
fmt.Println("lindex = ", lIndex)
path, err := gtk.TreePathNewFromString(fmt.Sprintf("%d", lIndex))
if err != nil {
return rule, -1, err
}
iter, err := globalLS.GetIter(path)
if err != nil {
return rule, -1, err
}
rule.Path, err = lsGetStr(globalLS, iter, 1)
if err != nil {
return rule, -1, err
}
rule.Proto, err = lsGetStr(globalLS, iter, 2)
if err != nil {
return rule, -1, err
}
rule.Pid, err = lsGetInt(globalLS, iter, 3)
if err != nil {
return rule, -1, err
}
rule.Target, err = lsGetStr(globalLS, iter, 4)
if err != nil {
return rule, -1, err
}
rule.Hostname, err = lsGetStr(globalLS, iter, 5)
if err != nil {
return rule, -1, err
}
rule.Port, err = lsGetInt(globalLS, iter, 6)
if err != nil {
return rule, -1, err
}
rule.UID, err = lsGetInt(globalLS, iter, 7)
if err != nil {
return rule, -1, err
}
rule.GID, err = lsGetInt(globalLS, iter, 8)
if err != nil {
return rule, -1, err
}
rule.Origin, err = lsGetStr(globalLS, iter, 9)
if err != nil {
return rule, -1, err
}
return rule, lIndex, nil
}
func main() {
decisionWaiters = make([]*decisionWaiter, 0)
_, err := newDbusServer()
if err != nil {
log.Fatal("Error:", err)
return
}
loadPreferences()
gtk.Init(nil)
// Create a new toplevel window, set its title, and connect it to the "destroy" signal to exit the GTK main loop when it is destroyed.
mainWin, err = gtk.WindowNew(gtk.WINDOW_TOPLEVEL)
if err != nil {
log.Fatal("Unable to create window:", err)
}
mainWin.SetTitle("SGOS fw-daemon Prompter")
mainWin.Connect("destroy", func() {
fmt.Println("Shutting down...")
savePreferences()
gtk.MainQuit()
})
mainWin.Connect("configure-event", func() {
w, h := mainWin.GetSize()
userPrefs.Winwidth, userPrefs.Winheight = uint(w), uint(h)
l, t := mainWin.GetPosition()
userPrefs.Winleft, userPrefs.Wintop = uint(l), uint(t)
})
mainWin.SetPosition(gtk.WIN_POS_CENTER)
Notebook, err = gtk.NotebookNew()
if err != nil {
log.Fatal("Unable to create new notebook:", err)
}
loglevel := "Firewall Traffic Pending Approval"
nbLabel, err := gtk.LabelNew(loglevel)
if err != nil {
log.Fatal("Unable to create notebook label:", err)
}
box, err := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
if err != nil {
log.Fatal("Unable to create box:", err)
}
scrollbox, err := gtk.ScrolledWindowNew(nil, nil)
if err != nil {
log.Fatal("Unable to create scrolled window:", err)
}
tv, err := gtk.TreeViewNew()
if err != nil {
log.Fatal("Unable to create treeview:", err)
}
globalTV = tv
tv.SetSizeRequest(300, 300)
tv.SetHeadersClickable(true)
btnApprove, err = gtk.ButtonNewWithLabel("Approve")
if err != nil {
log.Fatal("Unable to create button:", err)
}
btnDeny, err = gtk.ButtonNewWithLabel("Deny")
if err != nil {
log.Fatal("Unable to create button:", err)
}
btnIgnore, err = gtk.ButtonNewWithLabel("Ignore")
if err != nil {
log.Fatal("Unable to create button:", err)
}
btnApprove.SetSensitive(false)
btnDeny.SetSensitive(false)
btnIgnore.SetSensitive(false)
bb := get_hbox()
bb.PackStart(btnApprove, false, false, 5)
bb.PackStart(btnDeny, false, false, 5)
bb.PackStart(btnIgnore, false, false, 5)
editbox := get_vbox()
hbox := get_hbox()
lbl := get_label("Application path:")
editApp = get_entry("")
editApp.Connect("changed", toggleValidRuleState)
hbox.PackStart(lbl, false, false, 10)
hbox.PackStart(editApp, true, true, 50)
editbox.PackStart(hbox, false, false, 5)
hbox = get_hbox()
lbl = get_label("Target host/IP:")
editTarget = get_entry("")
editTarget.Connect("changed", toggleValidRuleState)
hbox.PackStart(lbl, false, false, 10)
hbox.PackStart(editTarget, false, false, 5)
lbl = get_label("Port:")
editPort = get_entry("")
editPort.Connect("changed", toggleValidRuleState)
hbox.PackStart(lbl, false, false, 5)
hbox.PackStart(editPort, false, false, 5)
lbl = get_label("Protocol:")
comboProto = get_combobox()
hbox.PackStart(lbl, false, true, 5)
hbox.PackStart(comboProto, false, false, 5)
editbox.PackStart(hbox, false, false, 5)
hbox = get_hbox()
lbl = get_label("Apply rule:")
radioOnce = get_radiobutton(nil, "Once", true)
radioProcess = get_radiobutton(radioOnce, "This Process", false)
radioParent = get_radiobutton(radioOnce, "Parent Process", false)
radioSession = get_radiobutton(radioOnce, "Session", false)
radioPermanent = get_radiobutton(radioOnce, "Permanent", false)
radioParent.SetSensitive(false)
hbox.PackStart(lbl, false, false, 10)
hbox.PackStart(radioOnce, false, false, 5)
hbox.PackStart(radioProcess, false, false, 5)
hbox.PackStart(radioParent, false, false, 5)
hbox.PackStart(radioSession, false, false, 5)
hbox.PackStart(radioPermanent, false, false, 5)
editbox.PackStart(hbox, false, false, 5)
hbox = get_hbox()
chkUser = get_checkbox("Apply to UID/username", false)
chkUser.Connect("toggled", toggleValidRuleState)
editUser = get_entry("")
editUser.Connect("changed", toggleValidRuleState)
hbox.PackStart(chkUser, false, false, 10)
hbox.PackStart(editUser, false, false, 10)
chkGroup = get_checkbox("Apply to GID/group:", false)
chkGroup.Connect("toggled", toggleValidRuleState)
editGroup = get_entry("")
editGroup.Connect("changed", toggleValidRuleState)
hbox.PackStart(chkGroup, false, false, 10)
hbox.PackStart(editGroup, false, false, 10)
editbox.PackStart(hbox, false, false, 5)
box.PackStart(bb, false, false, 5)
box.PackStart(editbox, false, false, 5)
scrollbox.Add(tv)
// box.PackStart(tv, false, true, 5)
box.PackStart(scrollbox, false, true, 5)
tv.AppendColumn(createColumn("#", 0))
tv.AppendColumn(createColumn("Path", 1))
tv.AppendColumn(createColumn("Protocol", 2))
tv.AppendColumn(createColumn("PID", 3))
tv.AppendColumn(createColumn("IP Address", 4))
tv.AppendColumn(createColumn("Hostname", 5))
tv.AppendColumn(createColumn("Port", 6))
tv.AppendColumn(createColumn("UID", 7))
tv.AppendColumn(createColumn("GID", 8))
tv.AppendColumn(createColumn("Origin", 9))
tv.AppendColumn(createColumn("Details", 10))
listStore := createListStore(true)
globalLS = listStore
tv.SetModel(listStore)
btnApprove.Connect("clicked", func() {
rule, idx, err := getSelectedRule()
if err != nil {
promptError("Error occurred processing request: " + err.Error())
return
}
rule, err = createCurrentRule()
if err != nil {
promptError("Error occurred constructing new rule: " + err.Error())
return
}
fmt.Println("rule = ", rule)
rulestr := "ALLOW|" + rule.Proto + ":" + rule.Target + ":" + strconv.Itoa(rule.Port)
fmt.Println("RULESTR = ", rulestr)
makeDecision(idx, rulestr, int(rule.Scope))
fmt.Println("Decision made.")
err = removeSelectedRule(idx, true)
if err == nil {
clearEditor()
} else {
promptError("Error setting new rule: " + err.Error())
}
})
btnDeny.Connect("clicked", func() {
rule, idx, err := getSelectedRule()
if err != nil {
promptError("Error occurred processing request: " + err.Error())
return
}
rule, err = createCurrentRule()
if err != nil {
promptError("Error occurred constructing new rule: " + err.Error())
return
}
fmt.Println("rule = ", rule)
rulestr := "DENY|" + rule.Proto + ":" + rule.Target + ":" + strconv.Itoa(rule.Port)
fmt.Println("RULESTR = ", rulestr)
makeDecision(idx, rulestr, int(rule.Scope))
fmt.Println("Decision made.")
err = removeSelectedRule(idx, true)
if err == nil {
clearEditor()
} else {
promptError("Error setting new rule: " + err.Error())
}
})
btnIgnore.Connect("clicked", func() {
_, idx, err := getSelectedRule()
if err != nil {
promptError("Error occurred processing request: " + err.Error())
return
}
makeDecision(idx, "", 0)
fmt.Println("Decision made.")
err = removeSelectedRule(idx, true)
if err == nil {
clearEditor()
} else {
promptError("Error setting new rule: " + err.Error())
}
})
// tv.SetActivateOnSingleClick(true)
tv.Connect("row-activated", func() {
seldata, _, err := getSelectedRule()
if err != nil {
promptError("Unexpected error reading selected rule: " + err.Error())
return
}
editApp.SetText(seldata.Path)
if seldata.Hostname != "" {
editTarget.SetText(seldata.Hostname)
} else {
editTarget.SetText(seldata.Target)
}
editPort.SetText(strconv.Itoa(seldata.Port))
radioOnce.SetActive(true)
radioProcess.SetActive(false)
radioProcess.SetSensitive(seldata.Pid > 0)
radioParent.SetActive(false)
radioSession.SetActive(false)
radioPermanent.SetActive(false)
comboProto.SetActiveID(seldata.Proto)
if seldata.Uname != "" {
editUser.SetText(seldata.Uname)
} else if seldata.UID != -1 {
editUser.SetText(strconv.Itoa(seldata.UID))
} else {
editUser.SetText("")
}
if seldata.Gname != "" {
editGroup.SetText(seldata.Gname)
} else if seldata.GID != -1 {
editGroup.SetText(strconv.Itoa(seldata.GID))
} else {
editGroup.SetText("")
}
chkUser.SetActive(false)
chkGroup.SetActive(false)
return
})
scrollbox.SetSizeRequest(600, 400)
// Notebook.AppendPage(scrollbox, nbLabel)
Notebook.AppendPage(box, nbLabel)
// setup_settings()
mainWin.Add(Notebook)
if userPrefs.Winheight > 0 && userPrefs.Winwidth > 0 {
// fmt.Printf("height was %d, width was %d\n", userPrefs.Winheight, userPrefs.Winwidth)
mainWin.Resize(int(userPrefs.Winwidth), int(userPrefs.Winheight))
} else {
mainWin.SetDefaultSize(850, 450)
}
if userPrefs.Wintop > 0 && userPrefs.Winleft > 0 {
mainWin.Move(int(userPrefs.Winleft), int(userPrefs.Wintop))
}
mainWin.ShowAll()
// mainWin.SetKeepAbove(true)
gtk.Main()
}