melonvpn-original-cs/MelonVPNClient/MainWindow.cs

398 lines
12 KiB
C#
Raw Normal View History

2020-09-22 23:10:16 +01:00
using System;
2020-09-23 20:22:51 +01:00
using System.IO;
using System.Net.NetworkInformation;
using System.Reflection;
using System.Threading;
2020-10-06 10:54:25 +01:00
using AppIndicator3;
2020-09-23 20:22:51 +01:00
using Gtk;
using MelonVPNClient;
2020-09-23 20:22:51 +01:00
using MelonVPNCore;
2020-10-05 13:06:11 +01:00
using Notify;
2020-09-23 20:22:51 +01:00
public partial class MainWindow : Window
2020-09-22 23:10:16 +01:00
{
private Label statusLabel;
private Button startBtn;
private Button stopBtn;
private Button refreshBtn;
2020-09-23 20:22:51 +01:00
private ScrolledWindow clientsListScroller;
private TextView clientsListText;
2020-09-22 23:10:16 +01:00
private Thread startupCheckThread;
private ThreadWrapper wrapper;
2020-10-06 10:54:25 +01:00
private Indicator trayIcon;
private PopupMenu trayMenu;
2020-10-05 13:06:11 +01:00
private bool ConnectedToVPN;
2020-10-22 22:59:42 +01:00
private bool IsHidden;
2020-09-23 20:22:51 +01:00
public static readonly string MelonIconImg = "MiniMelonVPNIcon.png";
public static readonly string MelonOnlineImg = "MiniMelonVPNOnline.png";
public MainWindow() : base(WindowType.Toplevel)
{
2020-09-22 23:10:16 +01:00
Build();
Title = "Melon VPN";
2020-09-23 20:22:51 +01:00
UpdateIcon(MelonIconImg);
2020-09-22 23:10:16 +01:00
SetSizeRequest(300, 300);
SetDefaultSize(300, 300);
2020-09-23 20:22:51 +01:00
SetPosition(WindowPosition.CenterAlways);
2020-09-22 23:10:16 +01:00
Resizable = false;
2020-10-05 13:06:11 +01:00
TypeHint = Gdk.WindowTypeHint.Dialog;
ShowAll();
2020-09-22 23:10:16 +01:00
Present();
2020-09-23 20:22:51 +01:00
statusLabel = new Label
{
2020-09-22 23:10:16 +01:00
Visible = true,
2020-09-23 20:22:51 +01:00
Text = "Loading..."
2020-09-22 23:10:16 +01:00
};
2020-09-23 20:22:51 +01:00
fixed1.Put(statusLabel, 10, 10);
startBtn = new Button
{
Visible = true,
Label = "Start"
2020-09-22 23:10:16 +01:00
};
startBtn.SetSizeRequest(80, 30);
startBtn.Clicked += OnStartClicked;
fixed1.Put(startBtn, 10, 40);
2020-09-23 20:22:51 +01:00
stopBtn = new Button
{
Visible = true,
Label = "Stop"
2020-09-22 23:10:16 +01:00
};
stopBtn.SetSizeRequest(80, 30);
stopBtn.Clicked += OnStopClicked;
fixed1.Put(stopBtn, 100, 40);
2020-09-23 20:22:51 +01:00
refreshBtn = new Button
{
2020-09-22 23:10:16 +01:00
Visible = true,
2020-09-23 20:22:51 +01:00
Label = "Refresh"
2020-09-22 23:10:16 +01:00
};
refreshBtn.SetSizeRequest(80, 30);
refreshBtn.Clicked += OnRefreshClicked;
fixed1.Put(refreshBtn, 190, 40);
2020-09-23 20:22:51 +01:00
clientsListText = new TextView
2020-09-22 23:10:16 +01:00
{
Visible = true,
2020-09-23 20:22:51 +01:00
Editable = false
};
clientsListScroller = new ScrolledWindow
{
Visible = true
2020-09-22 23:10:16 +01:00
};
2020-09-23 20:22:51 +01:00
clientsListScroller.SetSizeRequest(280, 200);
clientsListScroller.Add(clientsListText);
fixed1.Put(clientsListScroller, 10, 80);
2020-09-22 23:10:16 +01:00
2020-09-23 20:22:51 +01:00
GUISocketServer.Receive += delegate (object sender, ClientResponseState s)
2020-09-22 23:10:16 +01:00
{
Console.WriteLine(s);
2020-09-23 20:22:51 +01:00
Application.Invoke(delegate
{
UpdateStatus(s);
2020-09-22 23:10:16 +01:00
});
};
2020-09-23 20:22:51 +01:00
GUISocketServer.ClientListUpdate += delegate (object sender, ConnectedClient[] clients)
{
2020-09-22 23:10:16 +01:00
Console.WriteLine(clients.Length);
2020-09-23 20:22:51 +01:00
string[][] a = new string[clients.Length][];
for (int i = 0; i < clients.Length; i++) a[i] = new string[] { clients[i].name, clients[i].ip };
clientsListText.Buffer.Text = GenerateTable(a);
};
2020-09-22 23:10:16 +01:00
wrapper = new ThreadWrapper(GUISocketServer.StartServer);
2020-09-23 20:22:51 +01:00
startupCheckThread = new Thread(StartupCheckMethod)
{
2020-09-22 23:10:16 +01:00
IsBackground = true
};
2020-09-23 20:22:51 +01:00
startupCheckThread.Start();
2020-10-06 10:54:25 +01:00
trayMenu = new PopupMenu(OnActivateTrayIcon, OnToggleMenuItem, QuitApp);
trayIcon = new Indicator("melonvpn", "MiniMelonVPNIcon", (int)IndicatorCategory.ApplicationStatus)
{
Menu = trayMenu.GetMenu(),
Status = (int)IndicatorStatus.Active
2020-09-23 20:22:51 +01:00
};
}
2020-10-06 10:54:25 +01:00
private void OnActivateTrayIcon()
2020-09-23 20:22:51 +01:00
{
2020-10-22 22:59:42 +01:00
if (IsHidden) JumpFromTray();
else SendToTray();
}
2020-10-06 10:54:25 +01:00
void OnToggleMenuItem()
{
if (ConnectedToVPN) OnStopClicked(this, new EventArgs());
else OnStartClicked(this, new EventArgs());
}
void UpdateTrayMenu()
{
if (trayMenu != null) trayMenu.Update(ConnectedToVPN, !IsHidden);
2020-09-22 23:10:16 +01:00
}
2020-10-06 10:54:25 +01:00
void UpdateIcon(string file)
2020-09-23 20:22:51 +01:00
{
2020-10-06 10:54:25 +01:00
DirectoryInfo ExeLocation = Directory.GetParent(Assembly.GetEntryAssembly().Location);
string IconPath = System.IO.Path.Combine(ExeLocation.FullName, file);
Console.WriteLine("Trying to update icon to: " + IconPath);
if (File.Exists(IconPath))
{
Icon = new Gdk.Pixbuf(IconPath);
if (trayIcon != null) trayIcon.Icon = file.Replace(".png", "");
}
}
2020-09-23 20:22:51 +01:00
2020-10-06 10:54:25 +01:00
string GenerateTable(string[][] a)
{
if (a.Length == 0) return "";
int maxlength = a[0][0].Length;
foreach (string b in a[0])
{
if (b.Length > maxlength) maxlength = b.Length;
}
string[] c = new string[a.Length];
for (int i = 0; i < a.Length; i++) c[i] = a[i][0].PadRight(maxlength, ' ') + a[i][1];
return string.Join("\n", c);
2020-09-23 20:22:51 +01:00
}
2020-10-06 10:54:25 +01:00
string GetVpnInternalIp()
2020-09-22 23:10:16 +01:00
{
2020-10-06 10:54:25 +01:00
if (NetworkInterface.GetIsNetworkAvailable())
{
foreach (NetworkInterface f in NetworkInterface.GetAllNetworkInterfaces())
{
IPInterfaceProperties p = f.GetIPProperties();
IPAddressInformationCollection addressesColl = p.AnycastAddresses;
foreach (IPAddressInformation ip in addressesColl)
{
if (ip.Address.ToString().StartsWith("10.137.248.", StringComparison.CurrentCulture))
{
return ip.Address.ToString();
}
}
}
}
return "";
}
void StartupCheckMethod()
{
ClientResponseState s = Client.SendDataMessage(DataMessage.Status);
Console.WriteLine(s);
Application.Invoke(delegate
{
UpdateStatus(s);
});
}
void Refresh()
{
ClientResponseState s = Client.SendDataMessage(DataMessage.Status);
switch (s)
{
case ClientResponseState.Error:
case ClientResponseState.ServerError:
UpdateStatus(s);
MessageDialog md = new MessageDialog(this,
DialogFlags.DestroyWithParent, MessageType.Error,
ButtonsType.Ok, "Error stopping VPN. Is the daemon running?");
md.Run();
md.Destroy();
break;
case ClientResponseState.Online:
case ClientResponseState.Offline:
UpdateStatus(s);
break;
}
}
void UpdateStatus(ClientResponseState s)
{
bool oldState = ConnectedToVPN;
switch (s)
{
case ClientResponseState.Error:
statusLabel.Text = "Client Error";
ConnectedToVPN = false;
break;
case ClientResponseState.ServerError:
statusLabel.Text = "Server Error";
ConnectedToVPN = false;
break;
case ClientResponseState.Blank:
statusLabel.Text = "No reply";
ConnectedToVPN = false;
break;
case ClientResponseState.Online:
statusLabel.Text = "Online - " + GetVpnInternalIp();
ConnectedToVPN = true;
break;
case ClientResponseState.Offline:
statusLabel.Text = "Offline";
ConnectedToVPN = false;
break;
case ClientResponseState.Starting:
statusLabel.Text = "Starting";
ConnectedToVPN = false;
break;
case ClientResponseState.Restarting:
statusLabel.Text = "Restarting";
ConnectedToVPN = true;
2020-10-06 10:54:25 +01:00
break;
}
if (ConnectedToVPN) UpdateIcon(MelonOnlineImg);
2020-10-05 13:06:11 +01:00
else UpdateIcon(MelonIconImg);
2020-10-06 10:54:25 +01:00
UpdateTrayMenu();
NotifyStateUpdate(oldState, ConnectedToVPN);
2020-09-23 20:22:51 +01:00
}
2020-10-06 10:54:25 +01:00
void NotifyStateUpdate(bool old, bool current)
2020-09-22 23:10:16 +01:00
{
2020-10-06 10:54:25 +01:00
if (old != current)
{
string msg = (current ? "Connected to" : "Disconnected from") + " the VPN";
2020-10-22 22:59:42 +01:00
string icon = "/usr/lib/melon-vpn/MiniMelonVPN" + (current ? "Online" : "Icon") + ".png";
Notification pop = new Notification("Melon VPN", msg, icon);
pop.Show();
CloseNotificationAfterWait(pop, 1000);
2020-10-06 10:54:25 +01:00
}
}
void CloseNotificationAfterWait(Notification pop, int timeout)
2020-10-22 22:59:42 +01:00
{
// Close the notification after a wait
// so it doesn't hang around
Thread wait = new Thread(() =>
{
Thread.Sleep(timeout);
pop.Close();
})
{
IsBackground = true
};
wait.Start();
2020-10-06 10:54:25 +01:00
}
void SendToTray()
2020-10-06 10:54:25 +01:00
{
2020-10-22 22:59:42 +01:00
IsHidden = true;
// Try hiding the window
Iconify();
SkipPagerHint = true;
SkipTaskbarHint = true;
// Changing the visible property needs to be
// delayed for the window to start minimizing
Thread thread = new Thread(() =>
2020-10-06 10:54:25 +01:00
{
Thread.Sleep(100);
Visible = false;
2020-10-22 22:59:42 +01:00
})
{
IsBackground = true
};
thread.Start();
UpdateTrayMenu();
2020-10-22 22:59:42 +01:00
}
void JumpFromTray()
2020-10-22 22:59:42 +01:00
{
IsHidden = false;
// Present triggers `Visible = true;`
// it is then called again once the window is visible
// so it triggers `Deiconify();`
Present();
2020-10-22 22:59:42 +01:00
SkipPagerHint = false;
SkipTaskbarHint = false;
UpdateTrayMenu();
2020-10-22 22:59:42 +01:00
}
protected void OnDeleteEvent(object sender, DeleteEventArgs a)
{
// Try to prevent destroying the tray icon
// while connected to VPN
if (ConnectedToVPN) SendToTray();
2020-10-06 10:54:25 +01:00
else QuitApp();
a.RetVal = true;
}
protected void QuitApp()
{
2020-10-22 22:59:42 +01:00
// Kill this thread if it exists
2020-10-06 10:54:25 +01:00
if (wrapper != null)
{
wrapper.Kill();
wrapper.Join();
}
2020-10-22 22:59:42 +01:00
// Destroy the tray icon first
2020-10-06 10:54:25 +01:00
if (trayIcon != null) trayIcon.Dispose();
Application.Quit();
}
void OnStartClicked(object sender, EventArgs e)
{
ClientResponseState s = Client.SendDataMessage(DataMessage.Start);
switch (s)
{
case ClientResponseState.Error:
case ClientResponseState.ServerError:
case ClientResponseState.Offline:
UpdateStatus(s);
MessageDialog md = new MessageDialog(this,
DialogFlags.DestroyWithParent, MessageType.Error,
ButtonsType.Ok, "Error starting VPN. Is the daemon running?");
md.Run();
md.Destroy();
break;
}
}
void OnStopClicked(object sender, EventArgs e)
{
ClientResponseState s = Client.SendDataMessage(DataMessage.Stop);
switch (s)
{
case ClientResponseState.Error:
case ClientResponseState.ServerError:
case ClientResponseState.Online:
UpdateStatus(s);
MessageDialog md = new MessageDialog(this,
DialogFlags.DestroyWithParent, MessageType.Error,
ButtonsType.Ok, "Error stopping VPN. Is the daemon running?");
md.Run();
md.Destroy();
break;
}
}
void OnRefreshClicked(object sender, EventArgs e) => Refresh();
protected void OnVisibilityNotifyEvent(object o, VisibilityNotifyEventArgs args)
2020-10-06 10:54:25 +01:00
{
// Calling present again forces the window to `Deiconify();`
Thread thread = new Thread(() =>
{
Thread.Sleep(100);
Present();
})
{
IsBackground = true
};
thread.Start();
}
2020-09-23 20:22:51 +01:00
}