From e3833190bf0899bd624b5f4c369b6c71ebc1d5b4 Mon Sep 17 00:00:00 2001 From: shw Date: Thu, 18 May 2017 23:12:18 +0000 Subject: [PATCH] Created pre-functional replacement GUI for fw-daemon prompter that is independent of gnome-shell. --- fw-prompt/README.txt | 3 + fw-prompt/dbus.go | 73 ++++++ fw-prompt/fw-prompt.go | 510 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 586 insertions(+) create mode 100644 fw-prompt/README.txt create mode 100644 fw-prompt/dbus.go create mode 100644 fw-prompt/fw-prompt.go diff --git a/fw-prompt/README.txt b/fw-prompt/README.txt new file mode 100644 index 0000000..8a4378a --- /dev/null +++ b/fw-prompt/README.txt @@ -0,0 +1,3 @@ +To get this to work, first edit /usr/share/gnome-shell/modes/subgraph.json +Remove the entry for "firewall@subgraph.com", hit CTRL+F2 and then issue the reload "r" command. +Reset these changes to revert back to the old gnome-shell prompt. diff --git a/fw-prompt/dbus.go b/fw-prompt/dbus.go new file mode 100644 index 0000000..b84d755 --- /dev/null +++ b/fw-prompt/dbus.go @@ -0,0 +1,73 @@ +package main + +import ( + "errors" + "github.com/godbus/dbus" + "log" +// "github.com/gotk3/gotk3/glib" +) + + +type dbusServer struct { + conn *dbus.Conn + run bool +} + +type promptData struct { + Application string + Icon string + Path string + Address string + Port int + IP string + Origin string + Proto string + UID int + GID int + Username string + Groupname string + Pid int + OptString string + Expanded bool + Expert bool + Action int +} + + +func newDbusServer() (*dbusServer, error) { + conn, err := dbus.SystemBus() + + if err != nil { + return nil, err + } + + reply, err := conn.RequestName("com.subgraph.FirewallPrompt", dbus.NameFlagDoNotQueue) + + if err != nil { + return nil, err + } + + if reply != dbus.RequestNameReplyPrimaryOwner { + return nil, errors.New("Bus name is already owned") + } + + ds := &dbusServer{} + + if err := conn.Export(ds, "/com/subgraph/FirewallPrompt", "com.subgraph.FirewallPrompt"); err != nil { + return nil, err + } + + ds.conn = conn + ds.run = true + + return ds, nil +} + +func (ds *dbusServer) RequestPrompt(application, icon, path, address string, port int32, ip, origin, proto string, uid, gid int32, username, groupname string, pid int32, + optstring string, expanded, expert bool, action int32) (int32, string, *dbus.Error) { + log.Printf("REALLY GOT IT!") + log.Printf("app = %s, icon = %s, path = %s, address = %s, action = %v\n", application, icon, path, address, action) + addRequest(nil, path, proto, int(pid), ip, address, int(port), int(uid), int(gid), origin, optstring) +// glib.IdleAdd(func, data) + return 0, "bla bla bla", nil +} diff --git a/fw-prompt/fw-prompt.go b/fw-prompt/fw-prompt.go new file mode 100644 index 0000000..01f87d3 --- /dev/null +++ b/fw-prompt/fw-prompt.go @@ -0,0 +1,510 @@ +package main + + +import ( + "github.com/gotk3/gotk3/gtk" + "github.com/gotk3/gotk3/glib" + "log" + "fmt" + "os" + "io/ioutil" + "encoding/json" + "os/user" + "strconv" +) + +type fpPreferences struct { + Winheight uint + Winwidth uint + Wintop uint + Winleft uint +} + + +var userPrefs fpPreferences +var mainWin *gtk.Window +var Notebook *gtk.Notebook +var globalLS *gtk.ListStore + + +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(600, 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" + fmt.Println("xxxxxxxxxxxxxxxxxxxxxx preferences path = ", prefPath) + + 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_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, optstring string) { + if listStore == nil { + listStore = globalLS + } + + iter := listStore.Append() + + colVals := make([]interface{}, 11) + colVals[0] = 1 + colVals[1] = path + colVals[2] = proto + colVals[3] = pid + 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) + } + +} + +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, 800) + + 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 main() { + _, 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 := "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) + } + + scrollbox.Add(box) + + + tv, err := gtk.TreeViewNew() + + if err != nil { + log.Fatal("Unable to create treeview:", err) + } + + tv.SetSizeRequest(300, 300) + tv.SetHeadersClickable(true) + + ab, err := gtk.ButtonNewWithLabel("Approve") + if err != nil { + log.Fatal("Unable to create button:", err) + } + + db, err := gtk.ButtonNewWithLabel("Deny") + if err != nil { + log.Fatal("Unable to create button:", err) + } + + ib, err := gtk.ButtonNewWithLabel("Ignore") + if err != nil { + log.Fatal("Unable to create button:", err) + } + + bb := get_hbox() + bb.PackStart(ab, false, false, 5) + bb.PackStart(db, false, false, 5) + bb.PackStart(ib, false, false, 5) + +// box.Add(tv) + box.PackStart(bb, false, false, 5) + box.PackStart(tv, 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) + + ab.Connect("clicked", func() { + promptError("Approving firewall request.") +/* choice := promptChoice("hmm?") + + if choice != int(gtk.RESPONSE_YES) { + return + } */ + + }) + + db.Connect("clicked", func() { + promptError("Denying firewall request.") + }) + + db.Connect("clicked", func() { + promptError("Ignoring firewall request.") + }) + + tv.Connect("row-activated", func() { + fmt.Println("DOUBLE CLICK") + + sel, err := tv.GetSelection() + + if err != nil { + promptError("Unexpected error retrieving selection: "+err.Error()) + return + } + + rows := sel.GetSelectedRows(listStore) + // func (v *TreeSelection) GetSelected() (model ITreeModel, iter *TreeIter, ok bool) ??? + fmt.Println("RETURNED ROWS: ", rows.Length()) + + if rows.Length() > 0 { + rdata := rows.NthData(0) + + lIndex, err := strconv.Atoi(rdata.(*gtk.TreePath).String()) + + if err != nil { + promptError("Unexpected error reading selection data: "+err.Error()) + return + } + + + path, err := gtk.TreePathNewFromString(fmt.Sprintf("%d", lIndex)) + + if err != nil { + promptError("Unexpected error reading data from selection: "+err.Error()) + return + } + + iter, err := listStore.GetIter(path) + + if err != nil { + promptError("Unexpected error looking up log entry: "+err.Error()) + return + } + + val, err := listStore.GetValue(iter, 6) + + if err != nil { + promptError("Unexpected error getting data from log entry: "+err.Error()) + return + } + + sval, err := val.GetString() + + if err != nil { + promptError("Unexpected error reading data from log entry: "+err.Error()) + return + } + + fmt.Println("HEH: ", sval) + promptInfo(sval) + } + + }) + + + scrollbox.SetSizeRequest(600, 800) + Notebook.AppendPage(scrollbox, 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(800, 600) + } + + if userPrefs.Wintop > 0 && userPrefs.Winleft > 0 { + mainWin.Move(int(userPrefs.Winleft), int(userPrefs.Wintop)) + } + +// addRequest(listStore, "/bin/bla", "proto", 666, "loglevel", "provider", 23, 100, 1000) + mainWin.ShowAll() + mainWin.SetKeepAbove(true) + gtk.Main() +}