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.
 
 
wireguird/gui/tunnels.go

1196 lines
24 KiB

package gui
import (
"archive/zip"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"regexp"
"sort"
"strconv"
"strings"
"time"
"github.com/ungerik/go-dry"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"github.com/UnnoTed/wireguird/gui/get"
"github.com/UnnoTed/wireguird/settings"
"github.com/dustin/go-humanize"
"github.com/gotk3/gotk3/gdk"
"github.com/gotk3/gotk3/glib"
"github.com/gotk3/gotk3/gtk"
"github.com/rs/zerolog/log"
"gopkg.in/ini.v1"
)
var (
Connected = false
Settings = &settings.Settings{}
)
func init() {
if err := Settings.Init(); err != nil {
log.Error().Err(err).Msg("Error on settings init")
}
}
type Tunnels struct {
Interface struct {
Status *gtk.Label
PublicKey *gtk.Label
ListenPort *gtk.Label
Addresses *gtk.Label
DNS *gtk.Label
}
Peer struct {
PublicKey *gtk.Label
AllowedIPs *gtk.Label
Endpoint *gtk.Label
LatestHandshake *gtk.Label
Transfer *gtk.Label
}
Settings struct {
MultipleTunnels *gtk.CheckButton
StartOnTray *gtk.CheckButton
CheckUpdates *gtk.CheckButton
}
ButtonChangeState *gtk.Button
icons map[string]*gtk.Image
ticker *time.Ticker
lastSelected string
}
func (t *Tunnels) Create() error {
t.icons = map[string]*gtk.Image{}
t.ticker = time.NewTicker(1 * time.Second)
tl, err := get.ListBox("tunnel_list")
if err != nil {
return err
}
if err := t.ScanTunnels(); err != nil {
return err
}
window.Connect("key-press-event", func(win *gtk.ApplicationWindow, ev *gdk.Event) {
keyEvent := &gdk.EventKey{ev}
if keyEvent.KeyVal() == gdk.KEY_F5 {
wlog("INFO", "Scanning tunnels from keyboard command (F5)")
if err := t.ScanTunnels(); err != nil {
wlog("ERROR", err.Error())
}
}
})
// menu
{
mb, err := get.MenuButton("menu")
if err != nil {
return err
}
menu, err := gtk.MenuNew()
if err != nil {
return err
}
mSettings, err := gtk.MenuItemNew()
if err != nil {
return err
}
mSettings.SetLabel("Settings")
//mSettings.SetSensitive(false)
mSettings.Connect("activate", func() {
settingsWindow.ShowAll()
})
menu.Append(mSettings)
mVersion, err := gtk.MenuItemNew()
if err != nil {
return err
}
mVersion.SetLabel("VERSION: v" + Version)
//mVersion.SetSensitive(false)
mVersion.Connect("activate", func() {
if err := exec.Command("xdg-open", Repo).Start(); err != nil {
ShowError(window, err, "open repo url error")
}
})
menu.Append(mVersion)
menu.SetHAlign(gtk.ALIGN_CENTER)
menu.SetVAlign(gtk.ALIGN_CENTER)
menu.ShowAll()
mb.SetPopup(menu)
}
t.Interface.Status, err = get.Label("label_interface_status")
if err != nil {
return err
}
t.Interface.PublicKey, err = get.Label("label_interface_public_key")
if err != nil {
return err
}
t.Interface.ListenPort, err = get.Label("label_interface_listen_port")
if err != nil {
return err
}
t.Interface.Addresses, err = get.Label("label_interface_addresses")
if err != nil {
return err
}
t.Interface.DNS, err = get.Label("label_interface_dns_servers")
if err != nil {
return err
}
t.Peer.PublicKey, err = get.Label("label_peer_public_key")
if err != nil {
return err
}
t.Peer.AllowedIPs, err = get.Label("label_peer_allowed_ips")
if err != nil {
return err
}
t.Peer.Endpoint, err = get.Label("label_peer_endpoint")
if err != nil {
return err
}
t.Peer.LatestHandshake, err = get.Label("label_peer_latest_handshake")
if err != nil {
return err
}
t.Peer.Transfer, err = get.Label("label_peer_transfer")
if err != nil {
return err
}
t.ButtonChangeState, err = get.Button("button_change_state")
if err != nil {
return err
}
t.ButtonChangeState.Connect("clicked", func() {
err := func() error {
list, err := wgc.Devices()
if err != nil {
return err
}
activeNames := t.ActiveDeviceName()
row := tl.GetSelectedRow()
// row not found for config
if row == nil {
return nil
}
// conf name
name, err := row.GetName()
if err != nil {
return err
}
// https://github.com/UnnoTed/wireguird/issues/11#issuecomment-1332047191
if len(name) >= 16 {
ShowError(window, errors.New("Tunnel's file name is too long ("+strconv.Itoa(len(name))+"), max length: 15"))
}
// disconnect from given tunnel
dc := func(d *wgtypes.Device) error {
gray, err := gtk.ImageNewFromFile(IconPath + "not_connected.png")
if err != nil {
return err
}
glib.IdleAdd(func() {
t.icons[d.Name].SetFromPixbuf(gray.GetPixbuf())
})
c := exec.Command("wg-quick", "down", d.Name)
output, err := c.Output()
if err != nil {
es := string(err.(*exec.ExitError).Stderr)
log.Error().Err(err).Str("output", string(output)).Str("error", es).Msg("wg-quick down error")
oerr := err.Error() + "\nwg-quick's output:\n" + es
wlog("ERROR", oerr)
return errors.New(oerr)
}
indicator.SetIcon("wireguard_off")
return wlog("INFO", "Disconnected from "+d.Name)
}
// disconnects from all tunnels before connecting to a new one
// when the multipleTunnels option is disabled
if Settings.MultipleTunnels {
log.Info().Str("name", name).Msg("NAME")
d, err := wgc.Device(name)
if err != nil && !errors.Is(err, os.ErrNotExist) {
return err
}
if !errors.Is(err, os.ErrNotExist) {
if err := dc(d); err != nil {
return err
}
}
} else {
for _, d := range list {
if err := dc(d); err != nil {
return err
}
}
}
// dont connect to the new one as this is a disconnect action
if dry.StringListContains(activeNames, name) {
t.UpdateRow(row)
glib.IdleAdd(func() {
if len(activeNames) == 1 {
header.SetSubtitle("Not connected!")
} else {
activeNames := t.ActiveDeviceName()
header.SetSubtitle("Connected to " + strings.Join(activeNames, ", "))
}
})
return nil
}
// connect to a tunnel
c := exec.Command("wg-quick", "up", name)
output, err := c.Output()
if err != nil {
es := string(err.(*exec.ExitError).Stderr)
log.Error().Err(err).Str("output", string(output)).Str("error", es).Msg("wg-quick up error")
oerr := err.Error() + "\nwg-quick's output:\n" + es
wlog("ERROR", oerr)
return errors.New(oerr)
}
if err != nil {
return err
}
// update header label with tunnel names
glib.IdleAdd(func() {
activeNames := t.ActiveDeviceName()
header.SetSubtitle("Connected to " + strings.Join(activeNames, ", "))
})
green, err := gtk.ImageNewFromFile(IconPath + "connected.png")
if err != nil {
return err
}
// set icon to connected for the tunnel's row
glib.IdleAdd(func() {
t.icons[name].SetFromPixbuf(green.GetPixbuf())
t.UpdateRow(row)
indicator.SetIcon("wg_connected")
})
if err := wlog("INFO", "Connected to "+name); err != nil {
return err
}
return nil
}()
if err != nil {
ShowError(window, err)
}
})
// boxPeers, err := get.Box("box_peers")
// if err != nil {
// return err
// }
tl.Connect("row-activated", func(l *gtk.ListBox, row *gtk.ListBoxRow) {
t.UpdateRow(row)
})
// button: add tunnel
btnAddTunnel, err := get.Button("button_add_tunnel")
if err != nil {
return err
}
btnAddTunnel.Connect("clicked", func() {
err := func() error {
log.Print("btn add tunnel")
dialog, err := gtk.FileChooserNativeDialogNew("Wireguird - Choose tunnel files (*.conf, *.zip)", window, gtk.FILE_CHOOSER_ACTION_OPEN, "OK", "Cancel")
if err != nil {
return err
}
defer dialog.Destroy()
// filter *.conf and *.zip files
filter, err := gtk.FileFilterNew()
if err != nil {
return err
}
filter.AddPattern("*.conf")
filter.AddPattern("*.zip")
filter.SetName("*.conf / *.zip")
dialog.AddFilter(filter)
dialog.SetSelectMultiple(true)
res := dialog.Run()
if gtk.ResponseType(res) == gtk.RESPONSE_ACCEPT {
list, err := dialog.GetFilenames()
if err != nil {
return err
}
for _, fname := range list {
// if file is zip archive
if strings.HasSuffix(fname, ".zip") {
err := parseZipArchive(fname)
if err != nil {
return err
}
continue
}
data, err := ioutil.ReadFile(fname)
if err != nil {
return err
}
err = ioutil.WriteFile(filepath.Join(TunnelsPath, filepath.Base(fname)), data, 666)
if err != nil {
return err
}
}
if err := t.ScanTunnels(); err != nil {
return err
}
}
return nil
}()
if err != nil {
ShowError(window, err, "add tunnel error")
}
})
// button: delete tunnel
btnDelTunnel, err := get.Button("button_del_tunnel")
if err != nil {
return err
}
btnDelTunnel.Connect("clicked", func() {
err := func() error {
row := tl.GetSelectedRow()
if row == nil {
return errors.New("No row selected.")
}
name, err := row.GetName()
if err != nil {
return err
}
ext := ""
if !strings.HasSuffix(name, ".conf") {
ext = ".conf"
}
path := filepath.Join(TunnelsPath, name+ext)
d := gtk.MessageDialogNew(window, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, "Do you really want to delete "+name+"?")
defer d.Destroy()
res := d.Run()
if res == gtk.RESPONSE_YES {
if err := os.Remove(path); err != nil {
return err
}
if err := t.ScanTunnels(); err != nil {
return err
}
return nil
}
return nil
}()
if err != nil {
ShowError(window, err, "tunnel delete error")
}
})
// button: zip tunnels
btnZipTunnels, err := get.Button("button_zip_tunnel")
if err != nil {
return err
}
btnZipTunnels.Connect("clicked", func() {
err := func() error {
d, err := gtk.FileChooserDialogNewWith2Buttons("Wireguird - zip tunnels -> Save zip file", window, gtk.FILE_CHOOSER_ACTION_SAVE, "Cancel", gtk.RESPONSE_CANCEL, "Save", gtk.RESPONSE_ACCEPT)
if err != nil {
panic(err)
}
defer d.Destroy()
d.SetDoOverwriteConfirmation(true)
t := time.Now()
d.SetCurrentName(fmt.Sprint("wg_tunnels_", t.Day(), "_", t.Month(), "_", t.Year(), ".zip"))
res := d.Run()
if res == gtk.RESPONSE_ACCEPT {
dest := d.GetFilename()
base := strings.TrimSuffix(filepath.Base(dest), filepath.Ext(dest))
zf, err := os.Create(dest)
if err != nil {
return err
}
defer zf.Close()
archive := zip.NewWriter(zf)
defer archive.Close()
return filepath.Walk(TunnelsPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
header, err := zip.FileInfoHeader(info)
if err != nil {
return err
}
header.Name = filepath.Join(base, strings.TrimPrefix(path, TunnelsPath))
if info.IsDir() {
header.Name += "/"
} else {
header.Method = zip.Deflate
header.SetMode(0777)
}
log.Debug().Interface("header", header).Interface("info", info).Msg("walk")
writer, err := archive.CreateHeader(header)
if err != nil {
return err
}
if info.IsDir() {
return nil
}
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close()
if _, err = io.Copy(writer, f); err != nil {
return err
}
return nil
})
}
return nil
}()
if err != nil {
ShowError(window, err, "tunnel delete error")
}
})
// stores a modified state for the editor
modified := false
editorWindow.Connect("hide", func() {
if err := t.ScanTunnels(); err != nil {
ShowError(window, err, "scan tunnel after closing editor window error")
}
modified = false
})
ne, err := get.Entry("editor_name")
if err != nil {
return err
}
ne.Connect("changed", func() {
modified = true
})
et, err := get.TextView("editor_text")
if err != nil {
return err
}
etb, err := et.GetBuffer()
if err != nil {
return err
}
etb.Connect("changed", func() {
modified = true
})
// button: edit tunnel
btnEditTunnel, err := get.Button("button_edit_tunnel")
if err != nil {
return err
}
btnEditTunnel.Connect("clicked", func() {
modified = false
err := func() error {
row := tl.GetSelectedRow()
if row == nil {
return nil
}
name, err := row.GetName()
if err != nil {
return err
}
ext := ""
if !strings.HasSuffix(name, ".conf") {
ext = ".conf"
}
path := filepath.Join(TunnelsPath, name+ext)
log.Print(path)
ew, err := createEditor(name, true)
if err != nil {
return err
}
et, err := get.TextView("editor_text")
if err != nil {
return err
}
data, err := ioutil.ReadFile(path)
if err != nil {
return err
}
// low budget GtkSourceView
propertyColor := "purple"
sectionColor := "green"
conf := string(data)
// gets public key to use in the PublicKey entry
re := regexp.MustCompile(`(?m)PublicKey[\s]+=[\s]+(.*)`)
m := re.FindStringSubmatch(conf)
if len(m) >= 2 {
epk, err := get.Entry("editor_publickey")
if err != nil {
return err
}
epk.SetText(m[1])
}
r := strings.NewReplacer(
"&", "&",
"PrivateKey", "<span color=\""+propertyColor+"\">PrivateKey</span>",
"PublicKey", "<span color=\""+propertyColor+"\">PublicKey</span>",
"Address", "<span color=\""+propertyColor+"\">Address</span>",
"DNS", "<span color=\""+propertyColor+"\">DNS</span>",
"AllowedIPs", "<span color=\""+propertyColor+"\">AllowedIPs</span>",
"Endpoint", "<span color=\""+propertyColor+"\">Endpoint</span>",
"PostUp", "<span color=\""+propertyColor+"\">PostUp</span>",
"PreDown", "<span color=\""+propertyColor+"\">PreDown</span>",
"PreSharedKey", "<span color=\""+propertyColor+"\">PreSharedKey</span>",
"PersistentKeepalive", "<span color=\""+propertyColor+"\">PersistentKeepalive</span>",
"[Interface]", "<span color=\""+sectionColor+"\">[Interface]</span>",
"[Peer]", "<span color=\""+sectionColor+"\">[Peer]</span>",
)
conf = r.Replace(conf)
ttt, err := gtk.TextTagTableNew()
if err != nil {
return err
}
tb, err := gtk.TextBufferNew(ttt)
if err != nil {
return err
}
tb.InsertMarkup(tb.GetStartIter(), conf)
et.SetBuffer(tb)
ew.ShowAll()
return nil
}()
if err != nil {
ShowError(window, err, "edit tunnel error")
}
})
// button: editor's cancel
btnEditorCancel, err := get.Button("editor_button_cancel")
if err != nil {
return err
}
btnEditorCancel.Connect("clicked", func() {
if !modified {
editorWindow.Close()
return
}
err := func() error {
d := gtk.MessageDialogNew(editorWindow, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, "Do you want to cancel any modification done to the tunnel?")
defer d.Destroy()
res := d.Run()
if res == gtk.RESPONSE_YES {
modified = false
editorWindow.Close()
return nil
}
return nil
}()
if err != nil {
ShowError(window, err, "cancel tunnel error")
}
})
// button: editor's save
btnEditorSave, err := get.Button("editor_button_save")
if err != nil {
return err
}
btnEditorSave.Connect("clicked", func() {
err := func() error {
row := tl.GetSelectedRow()
if row == nil {
return nil
}
name, err := row.GetName()
if err != nil {
return err
}
// adds extension when empty
ext := ""
if !strings.HasSuffix(name, ".conf") {
ext = ".conf"
}
path := filepath.Join(TunnelsPath, name+ext)
// get the new tunnel name
nameEntry, err := get.Entry("editor_name")
if err != nil {
return err
}
newName, err := nameEntry.GetText()
if err != nil {
return err
}
newName = strings.TrimSpace(newName)
// rename the tunnel
if name != newName {
newPath := filepath.Join(TunnelsPath, newName+ext)
if err := os.Rename(path, newPath); err != nil {
return err
}
path = newPath
}
// get the tunnels' edited text through editor_text's buffer
etxt, err := get.TextView("editor_text")
if err != nil {
return err
}
b, err := etxt.GetBuffer()
if err != nil {
return err
}
data, err := b.GetText(b.GetStartIter(), b.GetEndIter(), false)
if err != nil {
return err
}
// write changes
if err := ioutil.WriteFile(path, []byte(data), 666); err != nil {
return err
}
modified = false
if err := t.ScanTunnels(); err != nil {
return err
}
editorWindow.Close()
return nil
}()
if err != nil {
ShowError(window, err, "save tunnel error")
}
})
//////////////
// Settings
// button: settings save
btnSettingsSave, err := get.Button("button_settings_save")
if err != nil {
return err
}
btnSettingsSave.Connect("clicked", func() {
err := func() error {
t.ToSettings()
Settings.Save()
settingsWindow.Close()
return nil
}()
if err != nil {
ShowError(window, err, "settings save error")
}
})
// button: settings cancel
btnSettingsCancel, err := get.Button("button_settings_cancel")
if err != nil {
return err
}
btnSettingsCancel.Connect("clicked", func() {
err := func() error {
Settings.Init()
settingsWindow.Close()
return nil
}()
if err != nil {
ShowError(window, err, "settings cancel error")
}
})
if err := t.FromSettings(); err != nil {
return err
}
go func() {
for {
<-t.ticker.C
if !window.HasToplevelFocus() {
continue
}
row := tl.GetSelectedRow()
if row == nil {
t.UnknownLabels()
continue
}
name, err := row.GetName()
if err != nil {
log.Error().Err(err).Msg("row get name err")
continue
}
if !dry.StringListContains(t.ActiveDeviceName(), name) {
continue
}
d, err := wgc.Device(name)
if err != nil {
log.Error().Err(err).Msg("wgc get device err")
continue
}
t.Interface.PublicKey.SetText(d.PublicKey.String())
t.Interface.ListenPort.SetText(strconv.Itoa(d.ListenPort))
for _, p := range d.Peers {
hs := humanize.Time(p.LastHandshakeTime)
glib.IdleAdd(func() {
t.Peer.LatestHandshake.SetText(hs)
t.Peer.Transfer.SetText(humanize.Bytes(uint64(p.ReceiveBytes)) + " received, " + humanize.Bytes(uint64(p.TransmitBytes)) + " sent")
})
}
}
}()
return nil
}
func (t *Tunnels) ToSettings() {
Settings.MultipleTunnels = t.Settings.MultipleTunnels.GetActive()
Settings.StartOnTray = t.Settings.StartOnTray.GetActive()
Settings.CheckUpdates = t.Settings.CheckUpdates.GetActive()
}
func (t *Tunnels) FromSettings() error {
log.Debug().Interface("settings", Settings).Msg("tunnel.FromSettings()")
var err error
// checkbox: multiple tunnels
t.Settings.MultipleTunnels, err = get.CheckButton("settings_multiple_active_tunnels")
if err != nil {
return err
}
t.Settings.MultipleTunnels.SetActive(Settings.MultipleTunnels)
// checkbox: start on tray
t.Settings.StartOnTray, err = get.CheckButton("settings_start_tray")
if err != nil {
return err
}
t.Settings.StartOnTray.SetActive(Settings.StartOnTray)
// checkbox: check updates
t.Settings.CheckUpdates, err = get.CheckButton("settings_check_updates")
if err != nil {
return err
}
t.Settings.CheckUpdates.SetActive(Settings.CheckUpdates)
return nil
}
func (t *Tunnels) UpdateRow(row *gtk.ListBoxRow) {
err := func() error {
ds, err := wgc.Devices()
if err != nil {
return err
}
log.Debug().Interface("list", ds).Msg("devices")
id, err := row.GetName()
if err != nil {
return err
}
cfg, err := ini.Load(TunnelsPath + id + ".conf")
if err != nil {
return err
}
t.lastSelected = id
t.UnknownLabels()
peersec := cfg.Section("Peer")
insec := cfg.Section("Interface")
glib.IdleAdd(func() {
t.Interface.Addresses.SetText(insec.Key("Address").String())
t.Interface.Status.SetText("Inactive")
t.Interface.DNS.SetText(insec.Key("DNS").String())
t.ButtonChangeState.SetLabel("Activate")
t.Peer.AllowedIPs.SetText(peersec.Key("AllowedIPs").String())
t.Peer.PublicKey.SetText(peersec.Key("PublicKey").String())
t.Peer.Endpoint.SetText(peersec.Key("Endpoint").String())
})
for _, d := range ds {
if d.Name != id {
continue
}
// i'll do this later
// _ = boxPeers
// boxPeers.GetChildren().Foreach(func(item interface{}) {
// boxPeers.Remove(item.(*gtk.Widget))
// })
glib.IdleAdd(func() {
t.Interface.Status.SetText("Active")
t.ButtonChangeState.SetLabel("Deactivate")
t.Interface.PublicKey.SetText(d.PublicKey.String())
t.Interface.ListenPort.SetText(strconv.Itoa(d.ListenPort))
})
for _, p := range d.Peers {
hs := humanize.Time(p.LastHandshakeTime)
glib.IdleAdd(func() {
t.Peer.LatestHandshake.SetText(hs)
t.Peer.Transfer.SetText(humanize.Bytes(uint64(p.ReceiveBytes)) + " received, " + humanize.Bytes(uint64(p.TransmitBytes)) + " sent")
})
}
break
}
return nil
}()
if err != nil {
log.Error().Err(err).Msg("row activated")
ShowError(window, err)
}
}
func (t *Tunnels) ScanTunnels() error {
var err error
var configList []string
list, err := dry.ListDirFiles(TunnelsPath)
if err != nil {
// showError(err)
return err
}
for _, fileName := range list {
if !strings.HasSuffix(fileName, ".conf") {
continue
}
configList = append(configList, strings.TrimSuffix(fileName, ".conf"))
}
tl, err := get.ListBox("tunnel_list")
if err != nil {
return err
}
for {
row := tl.GetRowAtIndex(0)
if row == nil {
break
}
row.Destroy()
}
sort.Slice(configList, func(a, b int) bool {
return configList[a] < configList[b]
})
activeNames := t.ActiveDeviceName()
header.SetSubtitle("Connected to " + strings.Join(activeNames, ", "))
lasti := len(configList) - 1
for i, name := range configList {
row, err := gtk.ListBoxRowNew()
if err != nil {
return err
}
row.SetName(name)
row.SetMarginStart(8)
row.SetMarginEnd(8)
if i == 0 {
row.SetMarginTop(8)
} else if i == lasti {
row.SetMarginBottom(8)
}
var img *gtk.Image
if dry.StringListContains(activeNames, name) {
green, err := gtk.ImageNewFromFile(IconPath + "connected.png")
if err != nil {
return err
}
img = green
} else {
gray, err := gtk.ImageNewFromFile(IconPath + "not_connected.png")
if err != nil {
return err
}
img = gray
}
t.icons[name] = img
img.SetVAlign(gtk.ALIGN_CENTER)
img.SetHAlign(gtk.ALIGN_START)
img.SetSizeRequest(10, 10)
img.SetVExpand(false)
img.SetHExpand(false)
label, err := gtk.LabelNew(name)
if err != nil {
return err
}
label.SetHAlign(gtk.ALIGN_START)
label.SetMarginStart(8)
label.SetMarginEnd(8)
label.SetMarginTop(8)
label.SetMarginBottom(8)
box, err := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 4)
if err != nil {
return err
}
box.Add(img)
box.Add(label)
row.SetName(name)
row.Add(box)
row.ShowAll()
tl.Insert(row, -1)
if name == t.lastSelected {
tl.SelectRow(row)
t.UpdateRow(row)
}
}
if t.lastSelected == "" {
row := tl.GetRowAtIndex(0)
if row != nil {
tl.SelectRow(row)
t.UpdateRow(row)
}
}
return nil
}
func (t *Tunnels) UnknownLabels() {
glib.IdleAdd(func() {
t.ButtonChangeState.SetLabel("unknown")
t.Interface.Addresses.SetText("unknown")
t.Interface.Status.SetText("unknown")
t.Interface.DNS.SetText("unknown")
t.Peer.AllowedIPs.SetText("unknown")
t.Peer.PublicKey.SetText("unknown")
t.Peer.Endpoint.SetText("unknown")
t.Interface.Status.SetText("unknown")
t.ButtonChangeState.SetLabel("unknown")
t.Interface.PublicKey.SetText("unknown")
t.Interface.ListenPort.SetText("unknown")
t.Peer.LatestHandshake.SetText("unknown")
t.Peer.Transfer.SetText("unknown")
})
}
func (t *Tunnels) ActiveDeviceName() []string {
ds, _ := wgc.Devices()
var names []string
for _, d := range ds {
names = append(names, d.Name)
}
return names
}
func wlog(t string, text string) error {
wlogs, err := get.ListBox("wireguard_logs")
if err != nil {
return err
}
l, err := gtk.LabelNew("")
if err != nil {
return err
}
if t == "ERROR" {
t = `<span color="#FF0000">` + t + "</span>"
}
l.SetMarkup(`<span color="#008080">[` + time.Now().Format("02/Jan/06 15:04:05 MST") + `]</span>[` + t + `]: ` + text)
l.SetHExpand(true)
l.SetHAlign(gtk.ALIGN_START)
glib.IdleAdd(func() {
l.Show()
wlogs.Add(l)
})
return nil
}
func parseZipArchive(target string) error {
rc, err := zip.OpenReader(target)
if err != nil {
return err
}
defer rc.Close()
for _, f := range rc.File {
// parse only .conf files from zip archive
if !f.FileInfo().IsDir() && strings.HasSuffix(f.FileInfo().Name(), ".conf") {
fr, err := f.Open()
if err != nil {
return err
}
defer fr.Close()
data, err := ioutil.ReadAll(fr)
if err != nil {
return err
}
err = ioutil.WriteFile(filepath.Join(TunnelsPath, f.FileInfo().Name()), data, 666)
if err != nil {
return err
}
}
}
return nil
}