Initial commit of settings dialog

pull/16/head
Bruce Leidl 9 years ago
parent d8f63bc60a
commit 8678419026

@ -0,0 +1,114 @@
package main
import (
"bufio"
"fmt"
"log"
"os"
"path/filepath"
"reflect"
"github.com/gotk3/gotk3/glib"
"github.com/gotk3/gotk3/gtk"
"github.com/subgraph/fw-daemon/fw-settings/definitions"
)
const (
defsFolder = "definitions"
xmlExtension = ".xml"
)
func getDefinitionWithFileFallback(uiName string) string {
// this makes sure a missing definition wont break only when the app is released
uiDef := getDefinition(uiName)
fileName := filepath.Join(defsFolder, uiName+xmlExtension)
if fileNotFound(fileName) {
log.Printf("gui: loading compiled definition %q\n", uiName)
return uiDef.String()
}
return readFile(fileName)
}
// This must be called from the UI thread - otherwise bad things will happen sooner or later
func builderForDefinition(uiName string) *gtk.Builder {
// assertInUIThread()
template := getDefinitionWithFileFallback(uiName)
builder, err := gtk.BuilderNew()
if err != nil {
//We cant recover from this
panic(err)
}
err = builder.AddFromString(template)
if err != nil {
//This is a programming error
panic(fmt.Sprintf("gui: failed load %s: %s\n", uiName, err.Error()))
}
return builder
}
func fileNotFound(fileName string) bool {
_, fnf := os.Stat(fileName)
return os.IsNotExist(fnf)
}
func readFile(fileName string) string {
file, _ := os.Open(fileName)
reader := bufio.NewScanner(file)
var content string
for reader.Scan() {
content = content + reader.Text()
}
file.Close()
return content
}
func getDefinition(uiName string) fmt.Stringer {
def, ok := definitions.Get(uiName)
if !ok {
panic(fmt.Sprintf("No definition found for %s", uiName))
}
return def
}
type builder struct {
*gtk.Builder
}
func newBuilder(uiName string) *builder {
return &builder{builderForDefinition(uiName)}
}
func (b *builder) getItem(name string, target interface{}) {
v := reflect.ValueOf(target)
if v.Kind() != reflect.Ptr {
panic("builder.getItem() target argument must be a pointer")
}
elem := v.Elem()
elem.Set(reflect.ValueOf(b.get(name)))
}
func (b *builder) getItems(args ...interface{}) {
for len(args) >= 2 {
name, ok := args[0].(string)
if !ok {
panic("string argument expected in builder.getItems()")
}
b.getItem(name, args[1])
args = args[2:]
}
}
func (b *builder) get(name string) glib.IObject {
obj, err := b.GetObject(name)
if err != nil {
panic("builder.GetObject() failed: " + err.Error())
}
return obj
}

@ -0,0 +1,57 @@
package main
import (
"github.com/gotk3/gotk3/gtk"
"github.com/op/go-logging"
)
var levelToId = map[int32]string{
int32(logging.ERROR): "error",
int32(logging.WARNING): "warning",
int32(logging.NOTICE): "notice",
int32(logging.INFO): "info",
int32(logging.DEBUG): "debug",
}
var idToLevel = func() map[string]int32 {
m := make(map[string]int32)
for k, v := range levelToId {
m[v] = k
}
return m
}()
func loadConfig(win *gtk.Window, b *builder, dbus *dbusObject) {
var levelCombo *gtk.ComboBoxText
var redactCheck *gtk.CheckButton
b.getItems(
"level_combo", &levelCombo,
"redact_checkbox", &redactCheck,
)
conf, err := dbus.getConfig()
if err != nil {
failDialog(win, "Failed to load config from fw daemon: %v", err)
}
if lvl, ok := conf["loglevel"].(int32); ok {
if id, ok := levelToId[lvl]; ok {
levelCombo.SetActiveID(id)
}
}
if v, ok := conf["logredact"].(bool); ok {
redactCheck.SetActive(v)
}
b.ConnectSignals(map[string]interface{}{
"on_level_combo_changed": func() {
if lvl, ok := idToLevel[levelCombo.GetActiveID()]; ok {
dbus.setConfig("loglevel", lvl)
}
},
"on_redact_checkbox_toggled": func() {
dbus.setConfig("logredact", redactCheck.GetActive())
},
})
}

@ -0,0 +1,65 @@
package main
import (
"github.com/godbus/dbus"
)
type dbusObject struct {
dbus.BusObject
}
type dbusRule struct {
Id uint32
App string
Path string
Verb uint32
Target string
}
func newDbusObject() (*dbusObject, error) {
conn, err := dbus.SystemBus()
if err != nil {
return nil, err
}
return &dbusObject{conn.Object("com.subgraph.Firewall", "/com/subgraph/Firewall")}, nil
}
func (ob *dbusObject) isEnabled() (bool, error) {
var flag bool
if err := ob.Call("com.subgraph.Firewall.IsEnabled", 0).Store(&flag); err != nil {
return false, err
}
return flag, nil
}
func (ob *dbusObject) listRules() ([]dbusRule, error) {
rules := []dbusRule{}
if err := ob.Call("com.subgraph.Firewall.ListRules", 0).Store(&rules); err != nil {
return nil, err
}
return rules, nil
}
func (ob *dbusObject) deleteRule(id uint32) {
ob.Call("com.subgraph.Firewall.DeleteRule", 0, id)
}
func (ob *dbusObject) updateRule(rule *dbusRule) {
ob.Call("com.subgraph.Firewall.UpdateRule", 0, rule)
}
func (ob *dbusObject) getConfig() (map[string]interface{}, error) {
res := make(map[string]dbus.Variant)
if err := ob.Call("com.subgraph.Firewall.GetConfig", 0).Store(&res); err != nil {
return nil, err
}
config := make(map[string]interface{})
for k, v := range res {
config[k] = v.Value()
}
return config, nil
}
func (ob *dbusObject) setConfig(key string, val interface{}) {
ob.Call("com.subgraph.Firewall.SetConfig", 0, key, dbus.MakeVariant(val))
}

@ -0,0 +1,187 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.19.0 -->
<interface>
<requires lib="gtk+" version="3.16"/>
<object class="GtkWindow" id="window">
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="title" translatable="yes">Firewall Settings</property>
<property name="default_width">600</property>
<property name="default_height">400</property>
<property name="type_hint">dialog</property>
<child>
<object class="GtkNotebook" id="notebook">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="margin_bottom">5</property>
<child>
<object class="GtkGrid" id="grid3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">12</property>
<property name="margin_right">12</property>
<property name="margin_top">12</property>
<property name="margin_bottom">12</property>
<child>
<object class="GtkLabel" id="label4">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Firewall Rules</property>
<property name="xalign">0</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="grid4">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">12</property>
<property name="margin_right">12</property>
<property name="margin_top">8</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="hscrollbar_policy">never</property>
<property name="shadow_type">in</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Rules</property>
</object>
<packing>
<property name="position">1</property>
<property name="tab_fill">False</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="grid1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">12</property>
<property name="margin_top">12</property>
<child>
<object class="GtkGrid" id="grid2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">12</property>
<property name="margin_top">8</property>
<property name="row_spacing">6</property>
<property name="column_spacing">6</property>
<child>
<object class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Log Level:</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkComboBoxText" id="level_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="active">0</property>
<items>
<item id="error" translatable="yes">Error</item>
<item id="warning" translatable="yes">Warning</item>
<item id="notice" translatable="yes">Notice</item>
<item id="info" translatable="yes">Info</item>
<item id="debug" translatable="yes">Debug</item>
</items>
<signal name="changed" handler="on_level_combo_changed" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="redact_checkbox">
<property name="label" translatable="yes">Remove host names and addresses from logs</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="xalign">0</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_redact_checkbox_toggled" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
<property name="width">2</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label5">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Logging</property>
<property name="ellipsize">start</property>
<property name="xalign">0</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Options</property>
</object>
<packing>
<property name="position">1</property>
<property name="tab_fill">False</property>
</packing>
</child>
</object>
</child>
</object>
</interface>

@ -0,0 +1,8 @@
generate:
ruby ./generate.rb
touch:
ls *.xml | xargs -n1 touch
doctor: touch generate
git diff --exit-code .

@ -0,0 +1,201 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.19.0 -->
<interface>
<requires lib="gtk+" version="3.16"/>
<object class="GtkDialog" id="dialog">
<property name="can_focus">False</property>
<property name="title" translatable="yes">Edit Rule</property>
<property name="type_hint">dialog</property>
<child internal-child="vbox">
<object class="GtkBox" id="dialog-vbox1">
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">2</property>
<child internal-child="action_area">
<object class="GtkButtonBox" id="dialog-action_area1">
<property name="can_focus">False</property>
<property name="layout_style">end</property>
<child>
<object class="GtkButton" id="cancel_button">
<property name="label" translatable="yes">Cancel</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="ok_button">
<property name="label" translatable="yes">Ok</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="yalign">0.60000002384185791</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="grid1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">12</property>
<property name="margin_right">12</property>
<property name="margin_top">12</property>
<property name="margin_bottom">24</property>
<property name="row_spacing">24</property>
<child>
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Enter changes for firewall rule below. The character &lt;b&gt;*&lt;/b&gt; can entered alone into either the &lt;b&gt;host&lt;/b&gt; or &lt;b&gt;port&lt;/b&gt; fields to match any value.
</property>
<property name="use_markup">True</property>
<property name="wrap">True</property>
<property name="max_width_chars">40</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="path_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="grid2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">12</property>
<property name="column_spacing">10</property>
<child>
<object class="GtkComboBoxText" id="verb_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="active">0</property>
<items>
<item id="allow" translatable="yes">Allow</item>
<item id="deny" translatable="yes">Deny</item>
</items>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Connections to &lt;b&gt;host:&lt;/b&gt;</property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="host_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="max_width_chars">34</property>
<property name="placeholder_text" translatable="yes">hostname or ip address</property>
<signal name="changed" handler="on_host_changed" swapped="no"/>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label4">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">on &lt;b&gt;port:&lt;/b&gt;</property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="left_attach">3</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="port_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="max_length">5</property>
<property name="width_chars">4</property>
<property name="max_width_chars">5</property>
<property name="placeholder_text" translatable="yes">Port</property>
<signal name="changed" handler="on_port_changed" swapped="no"/>
<signal name="insert-text" handler="on_port_insert_text" swapped="no"/>
</object>
<packing>
<property name="left_attach">4</property>
<property name="top_attach">0</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">12</property>
<property name="margin_right">10</property>
<property name="label" translatable="yes">Path:</property>
<property name="xalign">1</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<action-widgets>
<action-widget response="1">cancel_button</action-widget>
<action-widget response="2">ok_button</action-widget>
</action-widgets>
</object>
</interface>

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.19.0 -->
<interface>
<requires lib="gtk+" version="3.16"/>
<object class="GtkGrid" id="grid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<child>
<object class="GtkLabel" id="app_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="margin_left">8</property>
<property name="margin_right">10</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="verb_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_right">10</property>
<property name="xalign">1</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="target_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="hexpand">True</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="edit_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="relief">none</property>
<signal name="clicked" handler="on_edit_rule" swapped="no"/>
<child>
<object class="GtkImage" id="image2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">document-properties-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="left_attach">3</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="delete_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="relief">none</property>
<signal name="clicked" handler="on_delete_rule" swapped="no"/>
<child>
<object class="GtkImage" id="image1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">edit-delete-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="left_attach">4</property>
<property name="top_attach">0</property>
</packing>
</child>
</object>
</interface>

@ -0,0 +1,200 @@
package definitions
func init() {
add(`Dialog`, &defDialog{})
}
type defDialog struct{}
func (*defDialog) String() string {
return `
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.19.0 -->
<interface>
<requires lib="gtk+" version="3.16"/>
<object class="GtkWindow" id="window">
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="title" translatable="yes">Firewall Settings</property>
<property name="default_width">600</property>
<property name="default_height">400</property>
<property name="type_hint">dialog</property>
<child>
<object class="GtkNotebook" id="notebook">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="margin_bottom">5</property>
<child>
<object class="GtkGrid" id="grid3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">12</property>
<property name="margin_right">12</property>
<property name="margin_top">12</property>
<property name="margin_bottom">12</property>
<child>
<object class="GtkLabel" id="label4">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Firewall Rules</property>
<property name="xalign">0</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="grid4">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">12</property>
<property name="margin_right">12</property>
<property name="margin_top">8</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="hscrollbar_policy">never</property>
<property name="shadow_type">in</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Rules</property>
</object>
<packing>
<property name="position">1</property>
<property name="tab_fill">False</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="grid1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">12</property>
<property name="margin_top">12</property>
<child>
<object class="GtkGrid" id="grid2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">12</property>
<property name="margin_top">8</property>
<property name="row_spacing">6</property>
<property name="column_spacing">6</property>
<child>
<object class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Log Level:</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkComboBoxText" id="level_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="active">0</property>
<items>
<item id="error" translatable="yes">Error</item>
<item id="warning" translatable="yes">Warning</item>
<item id="notice" translatable="yes">Notice</item>
<item id="info" translatable="yes">Info</item>
<item id="debug" translatable="yes">Debug</item>
</items>
<signal name="changed" handler="on_level_combo_changed" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="redact_checkbox">
<property name="label" translatable="yes">Remove host names and addresses from logs</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="xalign">0</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_redact_checkbox_toggled" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
<property name="width">2</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label5">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Logging</property>
<property name="ellipsize">start</property>
<property name="xalign">0</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Options</property>
</object>
<packing>
<property name="position">1</property>
<property name="tab_fill">False</property>
</packing>
</child>
</object>
</child>
</object>
</interface>
`
}

@ -0,0 +1,47 @@
#!/usr/bin/env ruby
require 'fileutils'
def parse_go_name(file_name)
File.basename(file_name, ".xml").
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
gsub(/([a-z\d])([A-Z])/,'\1_\2').
tr("-", "_").
gsub(/\/_/, '/').
downcase + ".go"
end
def gen_go_file(xml_file, go_file)
xml_definition = File.read(xml_file)
ui_name = File.basename(xml_file, '.xml')
File.open(go_file, 'w+') do |target|
target.puts <<TEMPLATE
package definitions
func init() {
\tadd(`#{ui_name}`, &def#{ui_name}{})
}
type def#{ui_name} struct{}
func (*def#{ui_name}) String() string {
\treturn `
#{xml_definition}
`
}
TEMPLATE
end
end
def file_mtime(nm)
return Time.at(0) unless File.exists?(nm)
File.mtime(nm)
end
Dir[File.join(File.dirname(__FILE__), '*.xml')].each do |file_name|
go_file = parse_go_name file_name
if file_mtime(file_name) > file_mtime(go_file) || file_mtime(__FILE__) > file_mtime(go_file)
STDERR.puts " - #{file_name} -> #{go_file}"
gen_go_file(file_name, go_file)
end
end

@ -0,0 +1,25 @@
package definitions
import (
"fmt"
"sync"
)
var lock sync.RWMutex
var definitions = make(map[string]fmt.Stringer)
// Get returns the XML description of a UI definition and whether it was found
func Get(uiName string) (fmt.Stringer, bool) {
lock.RLock()
defer lock.RUnlock()
def, ok := definitions[uiName]
return def, ok
}
func add(uiName string, def fmt.Stringer) {
lock.Lock()
defer lock.Unlock()
definitions[uiName] = def
}

@ -0,0 +1,214 @@
package definitions
func init() {
add(`RuleEdit`, &defRuleEdit{})
}
type defRuleEdit struct{}
func (*defRuleEdit) String() string {
return `
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.19.0 -->
<interface>
<requires lib="gtk+" version="3.16"/>
<object class="GtkDialog" id="dialog">
<property name="can_focus">False</property>
<property name="title" translatable="yes">Edit Rule</property>
<property name="type_hint">dialog</property>
<child internal-child="vbox">
<object class="GtkBox" id="dialog-vbox1">
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">2</property>
<child internal-child="action_area">
<object class="GtkButtonBox" id="dialog-action_area1">
<property name="can_focus">False</property>
<property name="layout_style">end</property>
<child>
<object class="GtkButton" id="cancel_button">
<property name="label" translatable="yes">Cancel</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="ok_button">
<property name="label" translatable="yes">Ok</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="yalign">0.60000002384185791</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="grid1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">12</property>
<property name="margin_right">12</property>
<property name="margin_top">12</property>
<property name="margin_bottom">24</property>
<property name="row_spacing">24</property>
<child>
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Enter changes for firewall rule below. The character &lt;b&gt;*&lt;/b&gt; can entered alone into either the &lt;b&gt;host&lt;/b&gt; or &lt;b&gt;port&lt;/b&gt; fields to match any value.
</property>
<property name="use_markup">True</property>
<property name="wrap">True</property>
<property name="max_width_chars">40</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="path_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="grid2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">12</property>
<property name="column_spacing">10</property>
<child>
<object class="GtkComboBoxText" id="verb_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="active">0</property>
<items>
<item id="allow" translatable="yes">Allow</item>
<item id="deny" translatable="yes">Deny</item>
</items>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Connections to &lt;b&gt;host:&lt;/b&gt;</property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="host_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="max_width_chars">34</property>
<property name="placeholder_text" translatable="yes">hostname or ip address</property>
<signal name="changed" handler="on_host_changed" swapped="no"/>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label4">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">on &lt;b&gt;port:&lt;/b&gt;</property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="left_attach">3</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="port_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="max_length">5</property>
<property name="width_chars">4</property>
<property name="max_width_chars">5</property>
<property name="placeholder_text" translatable="yes">Port</property>
<signal name="changed" handler="on_port_changed" swapped="no"/>
<signal name="insert-text" handler="on_port_insert_text" swapped="no"/>
</object>
<packing>
<property name="left_attach">4</property>
<property name="top_attach">0</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">12</property>
<property name="margin_right">10</property>
<property name="label" translatable="yes">Path:</property>
<property name="xalign">1</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<action-widgets>
<action-widget response="1">cancel_button</action-widget>
<action-widget response="2">ok_button</action-widget>
</action-widgets>
</object>
</interface>
`
}

@ -0,0 +1,101 @@
package definitions
func init() {
add(`RuleItem`, &defRuleItem{})
}
type defRuleItem struct{}
func (*defRuleItem) String() string {
return `
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.19.0 -->
<interface>
<requires lib="gtk+" version="3.16"/>
<object class="GtkGrid" id="grid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<child>
<object class="GtkLabel" id="app_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="margin_right">10</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="verb_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_right">10</property>
<property name="xalign">1</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="target_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="hexpand">True</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="edit_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="relief">none</property>
<signal name="clicked" handler="on_edit_rule" swapped="no"/>
<child>
<object class="GtkImage" id="image2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">document-properties-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="left_attach">3</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="delete_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="relief">none</property>
<signal name="clicked" handler="on_delete_rule" swapped="no"/>
<child>
<object class="GtkImage" id="image1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">edit-delete-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="left_attach">4</property>
<property name="top_attach">0</property>
</packing>
</child>
</object>
</interface>
`
}

@ -0,0 +1,60 @@
package main
import (
"os"
"fmt"
"github.com/gotk3/gotk3/glib"
"github.com/gotk3/gotk3/gtk"
)
func failDialog(parent *gtk.Window, format string, args ...interface{}) {
d := gtk.MessageDialogNew(parent, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE,
format, args...)
d.Run()
os.Exit(1)
}
func activate(app *gtk.Application) {
win := app.GetActiveWindow()
if win != nil {
win.Present()
return
}
var scrolled *gtk.ScrolledWindow
b := newBuilder("Dialog")
b.getItems(
"window", &win,
"scrolledwindow", &scrolled,
)
win.SetIconName("security-high-symbolic")
box, _ := gtk.ListBoxNew()
scrolled.Add(box)
dbus, err := newDbusObject()
if err != nil {
failDialog(win, "Failed to connect to dbus system bus: %v", err)
}
rl := NewRuleList(dbus, win, box)
if _, err := dbus.isEnabled(); err != nil {
failDialog(win, "Unable is connect to firewall daemon. Is it running?")
}
rl.loadRules()
loadConfig(win, b, dbus)
app.AddWindow(win)
win.ShowAll()
}
func main() {
app, err := gtk.ApplicationNew("com.subgraph.Firewall.settings", glib.APPLICATION_FLAGS_NONE)
if err != nil {
panic(fmt.Sprintf("gtk.ApplicationNew() failed: %v", err))
}
app.Connect("activate", activate)
app.Run(os.Args)
}

@ -0,0 +1,161 @@
package main
import (
"fmt"
"github.com/gotk3/gotk3/gtk"
"net"
"strconv"
"strings"
"unicode"
)
const (
editDialogCancel = 1
editDialogOk = 2
)
type ruleEdit struct {
row *ruleRow
dialog *gtk.Dialog
pathLabel *gtk.Label
verbCombo *gtk.ComboBoxText
hostEntry *gtk.Entry
portEntry *gtk.Entry
ok *gtk.Button
}
func newRuleEdit(rr *ruleRow) *ruleEdit {
redit := &ruleEdit{row: rr}
b := newBuilder("RuleEdit")
b.getItems(
"dialog", &redit.dialog,
"path_label", &redit.pathLabel,
"verb_combo", &redit.verbCombo,
"host_entry", &redit.hostEntry,
"port_entry", &redit.portEntry,
"ok_button", &redit.ok,
)
b.ConnectSignals(map[string]interface{}{
"on_port_insert_text": redit.onPortInsertText,
"on_port_changed": redit.onChanged,
"on_host_changed": redit.onChanged,
})
return redit
}
func (re *ruleEdit) updateDialogFields() {
r := re.row.rule
re.pathLabel.SetText(r.Path)
if r.Verb == RULE_ALLOW {
re.verbCombo.SetActiveID("allow")
} else {
re.verbCombo.SetActiveID("deny")
}
target := strings.Split(r.Target, ":")
if len(target) != 2 {
return
}
re.hostEntry.SetText(target[0])
re.portEntry.SetText(target[1])
}
func (re *ruleEdit) validateFields() bool {
id := re.verbCombo.GetActiveID()
if id != "allow" && id != "deny" {
return false
}
host, _ := re.hostEntry.GetText()
port, _ := re.portEntry.GetText()
if !isValidHost(host) {
return false
}
if !isValidPort(port) {
return false
}
return true
}
func isValidHost(host string) bool {
if host == "*" {
return true
}
if net.ParseIP(host) != nil {
return true
}
parts := strings.Split(host, ".")
if len(parts) < 2 {
return false
}
for _, part := range parts {
if part == "" {
return false
}
}
return true
}
func isValidPort(port string) bool {
if port == "*" {
return true
}
pval, err := strconv.Atoi(port)
if err != nil {
return false
}
return pval > 0 && pval <= 0xFFFF
}
func (re *ruleEdit) updateRow() {
if !re.validateFields() {
return
}
r := re.row.rule
switch re.verbCombo.GetActiveID() {
case "allow":
r.Verb = RULE_ALLOW
case "deny":
r.Verb = RULE_DENY
}
host, _ := re.hostEntry.GetText()
port, _ := re.portEntry.GetText()
r.Target = fmt.Sprintf("%s:%s", host, port)
re.row.update()
}
func (re *ruleEdit) run() {
re.dialog.SetTransientFor(re.row.rl.win)
if re.dialog.Run() == editDialogOk {
re.updateRow()
re.row.rl.dbus.updateRule(re.row.rule)
}
re.dialog.Destroy()
}
func (rr *ruleRow) runEditor() {
redit := newRuleEdit(rr)
redit.updateDialogFields()
redit.run()
}
func (re *ruleEdit) onPortInsertText(entry *gtk.Entry, text string) {
current, _ := entry.GetText()
if current == "" && text == "*" {
return
}
if current == "*" {
entry.StopEmission("insert-text")
return
}
for _, c := range text {
if !unicode.IsDigit(c) {
entry.StopEmission("insert-text")
return
}
}
}
func (re *ruleEdit) onChanged() {
re.ok.SetSensitive(re.validateFields())
}

@ -0,0 +1,147 @@
package main
import (
"fmt"
"github.com/gotk3/gotk3/gtk"
"strings"
)
type ruleList struct {
dbus *dbusObject
win *gtk.Window
list *gtk.ListBox
col1 *gtk.SizeGroup
col2 *gtk.SizeGroup
col3 *gtk.SizeGroup
}
type ruleRow struct {
rl *ruleList
rule *dbusRule
widget *gtk.ListBoxRow
app_label *gtk.Label
verb_label *gtk.Label
target_label *gtk.Label
}
func NewRuleList(dbus *dbusObject, win *gtk.Window, list *gtk.ListBox) *ruleList {
rl := &ruleList{dbus: dbus, win: win, list: list}
rl.col1, _ = gtk.SizeGroupNew(gtk.SIZE_GROUP_HORIZONTAL)
rl.col2, _ = gtk.SizeGroupNew(gtk.SIZE_GROUP_HORIZONTAL)
rl.col3, _ = gtk.SizeGroupNew(gtk.SIZE_GROUP_HORIZONTAL)
return rl
}
func (rl *ruleList) loadRules() error {
rules, err := rl.dbus.listRules()
if err != nil {
return err
}
rl.addRules(rules)
return nil
}
func (rl *ruleList) addRules(rules []dbusRule) {
for i := 0; i < len(rules); i++ {
row := createWidget(&rules[i])
row.rl = rl
rl.col1.AddWidget(row.app_label)
rl.col2.AddWidget(row.verb_label)
rl.col3.AddWidget(row.target_label)
rl.list.Add(row.widget)
}
}
const RULE_DENY = 0
const RULE_ALLOW = 1
func createWidget(rule *dbusRule) *ruleRow {
row := &ruleRow{}
row.rule = rule
builder := newBuilder("RuleItem")
var grid *gtk.Grid
builder.getItems(
"grid", &grid,
"app_label", &row.app_label,
"verb_label", &row.verb_label,
"target_label", &row.target_label,
)
builder.ConnectSignals(map[string]interface{}{
"on_edit_rule": row.onEdit,
"on_delete_rule": row.onDelete,
})
row.widget, _ = gtk.ListBoxRowNew()
row.widget.Add(grid)
row.update()
return row
}
func (rr *ruleRow) update() {
rr.app_label.SetText(rr.rule.App)
rr.app_label.SetTooltipText(rr.rule.Path)
rr.verb_label.SetText(getVerbText(rr.rule))
rr.target_label.SetText(getTargetText(rr.rule))
}
func getVerbText(rule *dbusRule) string {
if rule.Verb == RULE_ALLOW {
return "ALLOW:"
}
return "DENY:"
}
func getTargetText(rule *dbusRule) string {
if rule.Target == "*:*" {
return "All connections"
}
items := strings.Split(rule.Target, ":")
if len(items) != 2 {
return rule.Target
}
if items[0] == "*" {
return fmt.Sprintf("Connections to All hosts on port %s", items[1])
}
if items[1] == "*" {
return fmt.Sprintf("All connections to host %s")
}
return fmt.Sprintf("Connections to %s on port %s", items[0], items[1])
}
func (rr *ruleRow) onEdit() {
rr.runEditor()
}
func (rr *ruleRow) onDelete() {
body := fmt.Sprintf(`Are you sure you want to delete this rule:
<b>Path:</b> %s
<b>Rule:</b> %s %s`, rr.rule.Path, getVerbText(rr.rule), getTargetText(rr.rule))
d := gtk.MessageDialogNewWithMarkup(
rr.rl.win,
gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_QUESTION,
gtk.BUTTONS_OK_CANCEL,
"")
d.SetMarkup(body)
if d.Run() == (int)(gtk.RESPONSE_OK) {
rr.delete()
}
d.Destroy()
}
func (rl *ruleList) remove(rr *ruleRow) {
rl.col1.RemoveWidget(rr.app_label)
rl.col2.RemoveWidget(rr.verb_label)
rl.col3.RemoveWidget(rr.target_label)
rl.list.Remove(rr.widget)
}
func (rr *ruleRow) delete() {
rr.rl.remove(rr)
rr.rl.dbus.deleteRule(rr.rule.Id)
}
Loading…
Cancel
Save