2020-05-27 13:48:31 +00:00
package gui
import (
2021-08-15 01:05:17 +00:00
"archive/zip"
2023-03-30 02:26:00 +00:00
"errors"
2021-08-15 01:05:17 +00:00
"fmt"
"io"
"io/ioutil"
"os"
2020-05-27 13:48:31 +00:00
"os/exec"
2021-08-15 01:05:17 +00:00
"path/filepath"
"regexp"
2020-05-27 13:48:31 +00:00
"sort"
"strconv"
"strings"
"time"
2023-03-30 02:26:00 +00:00
"github.com/ungerik/go-dry"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
2020-05-27 13:48:31 +00:00
"github.com/UnnoTed/wireguird/gui/get"
2023-03-30 02:26:00 +00:00
"github.com/UnnoTed/wireguird/settings"
2020-05-27 13:48:31 +00:00
"github.com/dustin/go-humanize"
2023-03-30 02:26:00 +00:00
"github.com/gotk3/gotk3/gdk"
2020-05-27 13:48:31 +00:00
"github.com/gotk3/gotk3/glib"
"github.com/gotk3/gotk3/gtk"
"github.com/rs/zerolog/log"
"gopkg.in/ini.v1"
)
2023-03-30 02:26:00 +00:00
var (
Connected = false
Settings = & settings . Settings { }
)
func init ( ) {
if err := Settings . Init ( ) ; err != nil {
log . Error ( ) . Err ( err ) . Msg ( "Error on settings init" )
}
}
2020-05-27 13:48:31 +00:00
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
}
2023-03-30 02:26:00 +00:00
Settings struct {
MultipleTunnels * gtk . CheckButton
StartOnTray * gtk . CheckButton
CheckUpdates * gtk . CheckButton
}
2020-05-27 13:48:31 +00:00
ButtonChangeState * gtk . Button
icons map [ string ] * gtk . Image
ticker * time . Ticker
2021-08-15 01:05:17 +00:00
lastSelected string
2020-05-27 13:48:31 +00:00
}
func ( t * Tunnels ) Create ( ) error {
t . icons = map [ string ] * gtk . Image { }
t . ticker = time . NewTicker ( 1 * time . Second )
2021-08-15 01:05:17 +00:00
tl , err := get . ListBox ( "tunnel_list" )
2020-05-27 13:48:31 +00:00
if err != nil {
return err
}
2021-08-15 01:05:17 +00:00
if err := t . ScanTunnels ( ) ; err != nil {
2020-05-27 13:48:31 +00:00
return err
}
2023-03-30 02:26:00 +00:00
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 ( ) )
}
}
} )
2021-08-15 01:05:17 +00:00
// menu
{
mb , err := get . MenuButton ( "menu" )
2020-05-27 13:48:31 +00:00
if err != nil {
return err
}
2021-08-15 01:05:17 +00:00
menu , err := gtk . MenuNew ( )
2020-05-27 13:48:31 +00:00
if err != nil {
return err
}
2023-03-30 02:26:00 +00:00
mSettings , err := gtk . MenuItemNew ( )
if err != nil {
return err
}
mSettings . SetLabel ( "Settings" )
//mSettings.SetSensitive(false)
mSettings . Connect ( "activate" , func ( ) {
settingsWindow . ShowAll ( )
} )
menu . Append ( mSettings )
2021-08-15 01:05:17 +00:00
mVersion , err := gtk . MenuItemNew ( )
2020-05-27 13:48:31 +00:00
if err != nil {
return err
}
2021-08-15 01:05:17 +00:00
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 )
2020-05-27 13:48:31 +00:00
2021-08-15 01:05:17 +00:00
menu . SetHAlign ( gtk . ALIGN_CENTER )
menu . SetVAlign ( gtk . ALIGN_CENTER )
menu . ShowAll ( )
mb . SetPopup ( menu )
2020-05-27 13:48:31 +00:00
}
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
}
2023-03-30 02:26:00 +00:00
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" )
2020-05-27 13:48:31 +00:00
if err != nil {
return err
}
glib . IdleAdd ( func ( ) {
t . icons [ d . Name ] . SetFromPixbuf ( gray . GetPixbuf ( ) )
} )
2023-03-30 02:26:00 +00:00
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 )
2020-05-27 13:48:31 +00:00
}
indicator . SetIcon ( "wireguard_off" )
2023-03-30 02:26:00 +00:00
return wlog ( "INFO" , "Disconnected from " + d . Name )
2020-05-27 13:48:31 +00:00
}
2023-03-30 02:26:00 +00:00
// 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
}
2021-02-28 20:47:03 +00:00
2023-03-30 02:26:00 +00:00
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
}
}
2020-05-27 13:48:31 +00:00
}
2023-03-30 02:26:00 +00:00
// dont connect to the new one as this is a disconnect action
if dry . StringListContains ( activeNames , name ) {
2020-05-27 13:48:31 +00:00
t . UpdateRow ( row )
2021-02-28 20:47:03 +00:00
glib . IdleAdd ( func ( ) {
2023-03-30 02:26:00 +00:00
if len ( activeNames ) == 1 {
header . SetSubtitle ( "Not connected!" )
} else {
activeNames := t . ActiveDeviceName ( )
header . SetSubtitle ( "Connected to " + strings . Join ( activeNames , ", " ) )
}
2021-02-28 20:47:03 +00:00
} )
2020-05-27 13:48:31 +00:00
return nil
}
2023-03-30 02:26:00 +00:00
// 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 {
2020-05-27 13:48:31 +00:00
return err
}
2023-03-30 02:26:00 +00:00
// update header label with tunnel names
2020-05-27 13:48:31 +00:00
glib . IdleAdd ( func ( ) {
2023-03-30 02:26:00 +00:00
activeNames := t . ActiveDeviceName ( )
header . SetSubtitle ( "Connected to " + strings . Join ( activeNames , ", " ) )
2020-05-27 13:48:31 +00:00
} )
2023-03-30 02:26:00 +00:00
green , err := gtk . ImageNewFromFile ( IconPath + "connected.png" )
2020-05-27 13:48:31 +00:00
if err != nil {
return err
}
2023-03-30 02:26:00 +00:00
// set icon to connected for the tunnel's row
2020-05-27 13:48:31 +00:00
glib . IdleAdd ( func ( ) {
t . icons [ name ] . SetFromPixbuf ( green . GetPixbuf ( ) )
t . UpdateRow ( row )
2021-08-15 01:05:17 +00:00
indicator . SetIcon ( "wg_connected" )
2020-05-27 13:48:31 +00:00
} )
2021-02-28 20:47:03 +00:00
if err := wlog ( "INFO" , "Connected to " + name ) ; err != nil {
return err
}
2020-05-27 13:48:31 +00:00
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 )
} )
2021-08-15 01:05:17 +00:00
// 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" )
2023-09-24 21:14:11 +00:00
dialog , err := gtk . FileChooserNativeDialogNew ( "Wireguird - Choose tunnel files (*.conf, *.zip)" , window , gtk . FILE_CHOOSER_ACTION_OPEN , "OK" , "Cancel" )
2021-08-15 01:05:17 +00:00
if err != nil {
return err
}
defer dialog . Destroy ( )
2023-09-24 21:14:11 +00:00
// filter *.conf and *.zip files
2021-08-15 01:05:17 +00:00
filter , err := gtk . FileFilterNew ( )
if err != nil {
return err
}
filter . AddPattern ( "*.conf" )
2023-09-24 21:14:11 +00:00
filter . AddPattern ( "*.zip" )
filter . SetName ( "*.conf / *.zip" )
2021-08-15 01:05:17 +00:00
dialog . AddFilter ( filter )
2023-03-30 02:26:00 +00:00
dialog . SetSelectMultiple ( true )
2021-08-15 01:05:17 +00:00
res := dialog . Run ( )
if gtk . ResponseType ( res ) == gtk . RESPONSE_ACCEPT {
2023-03-30 02:26:00 +00:00
list , err := dialog . GetFilenames ( )
2021-08-15 01:05:17 +00:00
if err != nil {
return err
}
2023-03-30 02:26:00 +00:00
for _ , fname := range list {
2023-09-24 21:14:11 +00:00
// if file is zip archive
if strings . HasSuffix ( fname , ".zip" ) {
err := parseZipArchive ( fname )
if err != nil {
return err
}
continue
}
2023-03-30 02:26:00 +00:00
data , err := ioutil . ReadFile ( fname )
if err != nil {
return err
}
2021-08-15 01:05:17 +00:00
2023-03-30 02:26:00 +00:00
err = ioutil . WriteFile ( filepath . Join ( TunnelsPath , filepath . Base ( fname ) ) , data , 666 )
if err != nil {
return err
}
2021-08-15 01:05:17 +00:00
}
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 ( )
2023-09-30 14:54:16 +00:00
if row == nil {
2023-09-30 15:01:44 +00:00
return errors . New ( "No row selected." )
2023-09-30 14:54:16 +00:00
}
2021-08-15 01:05:17 +00:00
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" )
}
} )
2023-03-30 02:26:00 +00:00
//////////////
// 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
}
2020-05-27 13:48:31 +00:00
go func ( ) {
for {
<- t . ticker . C
if ! window . HasToplevelFocus ( ) {
continue
}
row := tl . GetSelectedRow ( )
2021-02-28 20:47:03 +00:00
if row == nil {
t . UnknownLabels ( )
continue
}
2020-05-27 13:48:31 +00:00
name , err := row . GetName ( )
if err != nil {
log . Error ( ) . Err ( err ) . Msg ( "row get name err" )
continue
}
2023-03-30 02:26:00 +00:00
if ! dry . StringListContains ( t . ActiveDeviceName ( ) , name ) {
2020-05-27 13:48:31 +00:00
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 )
2021-02-28 20:47:03 +00:00
glib . IdleAdd ( func ( ) {
t . Peer . LatestHandshake . SetText ( hs )
t . Peer . Transfer . SetText ( humanize . Bytes ( uint64 ( p . ReceiveBytes ) ) + " received, " + humanize . Bytes ( uint64 ( p . TransmitBytes ) ) + " sent" )
} )
2020-05-27 13:48:31 +00:00
}
}
} ( )
return nil
}
2023-03-30 02:26:00 +00:00
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
}
2020-05-27 13:48:31 +00:00
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
}
2021-08-15 01:05:17 +00:00
cfg , err := ini . Load ( TunnelsPath + id + ".conf" )
2020-05-27 13:48:31 +00:00
if err != nil {
return err
}
2021-08-15 01:05:17 +00:00
t . lastSelected = id
2020-05-27 13:48:31 +00:00
t . UnknownLabels ( )
peersec := cfg . Section ( "Peer" )
insec := cfg . Section ( "Interface" )
2021-02-28 20:47:03 +00:00
glib . IdleAdd ( func ( ) {
t . Interface . Addresses . SetText ( insec . Key ( "Address" ) . String ( ) )
t . Interface . Status . SetText ( "Inactive" )
t . Interface . DNS . SetText ( insec . Key ( "DNS" ) . String ( ) )
2020-05-27 13:48:31 +00:00
2021-02-28 20:47:03 +00:00
t . ButtonChangeState . SetLabel ( "Activate" )
2020-05-27 13:48:31 +00:00
2021-02-28 20:47:03 +00:00
t . Peer . AllowedIPs . SetText ( peersec . Key ( "AllowedIPs" ) . String ( ) )
t . Peer . PublicKey . SetText ( peersec . Key ( "PublicKey" ) . String ( ) )
t . Peer . Endpoint . SetText ( peersec . Key ( "Endpoint" ) . String ( ) )
} )
2020-05-27 13:48:31 +00:00
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))
// })
2021-02-28 20:47:03 +00:00
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 ) )
} )
2020-05-27 13:48:31 +00:00
for _ , p := range d . Peers {
hs := humanize . Time ( p . LastHandshakeTime )
2021-02-28 20:47:03 +00:00
glib . IdleAdd ( func ( ) {
t . Peer . LatestHandshake . SetText ( hs )
t . Peer . Transfer . SetText ( humanize . Bytes ( uint64 ( p . ReceiveBytes ) ) + " received, " + humanize . Bytes ( uint64 ( p . TransmitBytes ) ) + " sent" )
} )
2020-05-27 13:48:31 +00:00
}
break
}
return nil
} ( )
if err != nil {
log . Error ( ) . Err ( err ) . Msg ( "row activated" )
ShowError ( window , err )
}
}
2021-08-15 01:05:17 +00:00
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 ]
} )
2023-03-30 02:26:00 +00:00
activeNames := t . ActiveDeviceName ( )
header . SetSubtitle ( "Connected to " + strings . Join ( activeNames , ", " ) )
2021-08-15 01:05:17 +00:00
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
2023-03-30 02:26:00 +00:00
if dry . StringListContains ( activeNames , name ) {
green , err := gtk . ImageNewFromFile ( IconPath + "connected.png" )
2021-08-15 01:05:17 +00:00
if err != nil {
return err
}
img = green
} else {
2023-03-30 02:26:00 +00:00
gray , err := gtk . ImageNewFromFile ( IconPath + "not_connected.png" )
2021-08-15 01:05:17 +00:00
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 )
}
}
2023-03-30 02:26:00 +00:00
if t . lastSelected == "" {
row := tl . GetRowAtIndex ( 0 )
if row != nil {
tl . SelectRow ( row )
t . UpdateRow ( row )
}
}
2021-08-15 01:05:17 +00:00
return nil
}
2020-05-27 13:48:31 +00:00
func ( t * Tunnels ) UnknownLabels ( ) {
2021-02-28 20:47:03 +00:00
glib . IdleAdd ( func ( ) {
t . ButtonChangeState . SetLabel ( "unknown" )
2020-05-27 13:48:31 +00:00
2021-02-28 20:47:03 +00:00
t . Interface . Addresses . SetText ( "unknown" )
t . Interface . Status . SetText ( "unknown" )
t . Interface . DNS . SetText ( "unknown" )
2020-05-27 13:48:31 +00:00
2021-02-28 20:47:03 +00:00
t . Peer . AllowedIPs . SetText ( "unknown" )
t . Peer . PublicKey . SetText ( "unknown" )
t . Peer . Endpoint . SetText ( "unknown" )
2020-05-27 13:48:31 +00:00
2021-02-28 20:47:03 +00:00
t . Interface . Status . SetText ( "unknown" )
t . ButtonChangeState . SetLabel ( "unknown" )
t . Interface . PublicKey . SetText ( "unknown" )
t . Interface . ListenPort . SetText ( "unknown" )
t . Peer . LatestHandshake . SetText ( "unknown" )
2020-05-27 13:48:31 +00:00
2021-02-28 20:47:03 +00:00
t . Peer . Transfer . SetText ( "unknown" )
} )
2020-05-27 13:48:31 +00:00
}
2023-03-30 02:26:00 +00:00
func ( t * Tunnels ) ActiveDeviceName ( ) [ ] string {
2020-05-27 13:48:31 +00:00
ds , _ := wgc . Devices ( )
2023-03-30 02:26:00 +00:00
var names [ ] string
2020-05-27 13:48:31 +00:00
for _ , d := range ds {
2023-03-30 02:26:00 +00:00
names = append ( names , d . Name )
2020-05-27 13:48:31 +00:00
}
2023-03-30 02:26:00 +00:00
return names
2020-05-27 13:48:31 +00:00
}
2021-02-28 20:47:03 +00:00
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
}
2023-09-24 21:14:11 +00:00
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
}