-
Notifications
You must be signed in to change notification settings - Fork 0
/
vpnc.tray.cs
427 lines (390 loc) · 21 KB
/
vpnc.tray.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
using System.Diagnostics;
using System.Timers;
public class TrayProgram {
// Переменная для хранения процесса API
private Process? apiProcess;
// Логическая переменная для отслеживания статуса API (запущен или остановлен)
private bool isApiRunning = false;
// Объект для отображения иконки в системном трее
private NotifyIcon notifyIcon;
// Контекстное меню, которое будет отображаться при клике правой кнопкой на иконку
private ContextMenuStrip contextMenu = new ContextMenuStrip();
private ToolStripMenuItem pingCheckItem;
private ToolStripMenuItem vpnCheckItem;
private ToolStripMenuItem toggleApiItem;
private ToolStripMenuItem openSwaggerItem;
private ToolStripMenuItem openConfigItem;
private ToolStripMenuItem startVPN;
private ToolStripMenuItem stopVPN;
private ToolStripMenuItem statusRegion;
// Таймер для проверки интернет соединения
private System.Timers.Timer pingTimer;
private bool isPingCheckEnabled = false;
// Таймер для проверки VPN соединения
private System.Timers.Timer vpnTimer;
private bool isVpnCheckEnabled = false;
// Переменные для хранения иконок
private Icon iconPingAvailable;
private Icon iconPingNotAvailable;
private Icon iconVPN;
// Элементы интерфейса
public TrayProgram() {
pingCheckItem = new ToolStripMenuItem("Ping Monitoring", null, TogglePingCheck);
vpnCheckItem = new ToolStripMenuItem("VPN Monitoring", null, ToggleVpnCheck);
toggleApiItem = new ToolStripMenuItem("API", null, ToggleApi);
openSwaggerItem = new ToolStripMenuItem("Open Swagger", null, OpenSwagger);
openConfigItem = new ToolStripMenuItem("Open Configuration", null, OpenConfiguration);
openConfigItem = new ToolStripMenuItem("Open Log", null, OpenLogFile);
startVPN = new ToolStripMenuItem("Start VPN", null, StartVPN);
stopVPN = new ToolStripMenuItem("Stop VPN", null, StopVPN);
statusRegion = new ToolStripMenuItem("Show Region", null, async (sender, e) => await ShowStatusConnectionAsync());
// Конвертируем png в ico и сохраняем их в переменные
string pngPingAvailable = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "img\\ping-available.png");
using Bitmap bitmapPingAvailable = new Bitmap(pngPingAvailable);
iconPingAvailable = Icon.FromHandle(bitmapPingAvailable.GetHicon());
string pngPingNotAvailable = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "img\\ping-not-available.png");
using Bitmap bitmapPingNotAvailable = new Bitmap(pngPingNotAvailable);
iconPingNotAvailable = Icon.FromHandle(bitmapPingNotAvailable.GetHicon());
string pngVPN = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "img\\vpn.png");
using Bitmap bitmapVPN = new Bitmap(pngVPN);
iconVPN = Icon.FromHandle(bitmapVPN.GetHicon());
// Устанавливаем начальную иконку
notifyIcon = new NotifyIcon {
Icon = iconPingAvailable,
ContextMenuStrip = CreateContextMenu(),
Visible = true
};
// Событие статуса при двойном клике на иконку
notifyIcon.MouseDoubleClick += async (sender, args) => await ShowStatusConnectionAsync();
// Устанавливаем таймеры для запуска функций при включении чекера (по умолчанию, 5 секунд)
int pingTimeout = int.TryParse(MainProgram.config?.PingTimeout, out int pingTimeoutResult) ? pingTimeoutResult*1000 : 5000;
pingTimer = new System.Timers.Timer(pingTimeout);
pingTimer.Elapsed += PerformPingCheck;
pingTimer.AutoReset = true;
int vpnTimeout = int.TryParse(MainProgram.config?.VpnTimeout, out int vpnTimeoutResult) ? vpnTimeoutResult*1000 : 5000;
vpnTimer = new System.Timers.Timer(vpnTimeout);
vpnTimer.Elapsed += PerformVpnCheck;
vpnTimer.AutoReset = true;
// Автозапуск чекеров при запуске трея в зависимости от настроек в конфигурации
bool pingStartup = MainProgram.config?.PingStartup ?? false;
if (pingStartup) {
pingCheckItem.Checked = true;
isPingCheckEnabled = true;
pingTimer.Start();
};
bool vpnStartup = MainProgram.config?.VpnStartup ?? false;
if (vpnStartup) {
vpnCheckItem.Checked = true;
isVpnCheckEnabled = true;
vpnTimer.Start();
};
bool apiStartup = MainProgram.config?.ApiStartup ?? false;
if (apiStartup) {
toggleApiItem.Checked = true;
StartApiProcess();
};
}
// Создание контекстного меню
private ContextMenuStrip CreateContextMenu() {
// Кнопка выхода из приложения
ToolStripMenuItem exitItem = new ToolStripMenuItem("Exit", null, ExitApplication);
// Добавление кнопок в контекстное меню
contextMenu.Items.AddRange(new ToolStripItem[] {
pingCheckItem,
vpnCheckItem,
toggleApiItem,
openSwaggerItem,
openConfigItem,
startVPN,
stopVPN,
statusRegion,
new ToolStripSeparator(),
exitItem
});
// Возвращаем созданное контекстное меню
return contextMenu;
}
// Метод для остановки и запуска проверки интернета
private void TogglePingCheck(object? sender, EventArgs? e) {
isPingCheckEnabled = !isPingCheckEnabled;
pingCheckItem.Checked = isPingCheckEnabled;
if (isPingCheckEnabled) {
pingTimer.Start();
} else {
pingTimer.Stop();
}
}
// Переменная для отслеживания состояния интернета и отправки уведомлений
private bool wasInternetUnavailable = false;
private bool isStableConnectionCheck = false; // Переменная для отслеживания состояния проверки стабильного соединения
// Метод для проверки Ping
private void PerformPingCheck(object? sender, ElapsedEventArgs e) {
string pingHost = MainProgram.config?.PingHost?.ToString() ?? "8.8.8.8";
if (string.IsNullOrEmpty(pingHost)) return;
bool isConnected = false;
// Первичная проверка на подключение
for (int i = 0; i < 3; i++) {
var (internetStatus, responseTimeOut) = MainProgram.StatusPing(pingHost);
// Debug
// Console.WriteLine($"Ping status: {internetStatus}");
if (internetStatus == "Connected") {
isConnected = true;
break;
}
}
// Если ping недоступен
if (!isConnected) {
// Проверка, чтобы оповещение о недоступности отправлялось только один раз
if (!wasInternetUnavailable) {
notifyIcon.ShowBalloonTip(5000, "Internet connection", "Internet unavailable", ToolTipIcon.Warning);
wasInternetUnavailable = true; // Установить флаг недоступности
}
pingTimer.Interval = 2000; // Устанавливаем интервал на 2 секунды
isStableConnectionCheck = false; // Сбрасываем проверку стабильного соединения
notifyIcon.Icon = iconPingNotAvailable;
} else {
// Если ping доступен и проверка стабильности не выполняется
if (!isStableConnectionCheck) {
isStableConnectionCheck = true; // Устанавливаем флаг проверки стабильности
bool stableConnection = true;
// Выполняем дополнительные проверки стабильности
for (int i = 0; i < 2; i++) {
Thread.Sleep(2000); // Пауза 2 секунды между проверками
var (internetStatus, responseTimeOut) = MainProgram.StatusPing(pingHost);
if (internetStatus != "Connected") {
stableConnection = false;
break;
}
}
// Если соединение стабильно, отправляем уведомление о доступности интернета и изменяем иконку
if (stableConnection) {
notifyIcon.ShowBalloonTip(5000, "Internet connection", "Internet available", ToolTipIcon.Info);
wasInternetUnavailable = false; // Сбрасываем переменную
int pingTimeout = int.TryParse(MainProgram.config?.PingTimeout, out int pingTimeoutResult) ? pingTimeoutResult*1000 : 5000;
pingTimer.Interval = pingTimeout;
// Проверяем интерфейс и изменяем иконку
string? interfaceName = MainProgram.config?.InterfaceName?.ToString();
if (string.IsNullOrEmpty(interfaceName)) return;
string interfaceStatus = MainProgram.StatusInterface(interfaceName);
if (interfaceStatus == "Up") {
notifyIcon.Icon = iconVPN;
} else {
notifyIcon.Icon = iconPingAvailable;
}
}
}
}
}
// Метод для остановки и запуска проверки VPN
private void ToggleVpnCheck(object? sender, EventArgs? e) {
isVpnCheckEnabled = !isVpnCheckEnabled;
vpnCheckItem.Checked = isVpnCheckEnabled;
if (isVpnCheckEnabled) {
vpnTimer.Start();
} else {
vpnTimer.Stop();
}
}
private bool wasVpnUnavailable = false;
// Метод для проверки работоспособности VPN и автоматического переподключения, если процесс не остановлен вручную
private void PerformVpnCheck(object? sender, ElapsedEventArgs e) {
string? interfaceName = MainProgram.config?.InterfaceName?.ToString();
string? processName = MainProgram.config?.ProcessName?.ToString();
string? processPath = MainProgram.config?.ProcessPath?.ToString();
if (string.IsNullOrEmpty(interfaceName) || string.IsNullOrEmpty(processName) || string.IsNullOrEmpty(processPath)) return;
Process[] processes = Process.GetProcessesByName(processName);
if (processes.Length == 0) {
if (notifyIcon.Icon == iconVPN || notifyIcon.Icon != iconPingNotAvailable) {
notifyIcon.Icon = iconPingAvailable;
}
return;
}
string interfaceStatus = MainProgram.StatusInterface(interfaceName);
// Debug
// Console.WriteLine($"Interface status: {interfaceStatus}");
if (interfaceStatus != "Up" && processes.Length != 0) {
if (notifyIcon.Icon != iconPingNotAvailable) {
notifyIcon.Icon = iconPingAvailable;
}
if (!wasVpnUnavailable) {
notifyIcon.ShowBalloonTip(5000, "VPN connection", "VPN unavailable", ToolTipIcon.Warning);
wasVpnUnavailable = true;
}
Task.Delay(2000);
processes = Process.GetProcessesByName(processName);
if (processes.Length != 0) {
// Задержка для перезапуска процесса (по умолчанию, 2 минуты)
int VpnRestartTimeout = int.TryParse(MainProgram.config?.VpnRestartTimeout, out int VpnRestartTimeoutResult) ? VpnRestartTimeoutResult*1000 : 120000;
vpnTimer.Interval = VpnRestartTimeout;
MainProgram.StopProcess(processName, true);
MainProgram.StartProcess(processPath);
}
} else {
if (notifyIcon.Icon != iconPingNotAvailable) {
notifyIcon.Icon = iconVPN;
}
if (wasVpnUnavailable) {
interfaceStatus = MainProgram.StatusInterface(interfaceName);
if (interfaceStatus == "Up") {
notifyIcon.ShowBalloonTip(5000, "VPN connection", "VPN available", ToolTipIcon.Info);
wasVpnUnavailable = false;
int vpnTimeout = int.TryParse(MainProgram.config?.VpnTimeout, out int vpnTimeoutResult) ? vpnTimeoutResult*1000 : 5000;
vpnTimer.Interval = vpnTimeout;
}
}
}
}
// Обработчик для переключения состояния API
private void ToggleApi(object? sender, EventArgs? e) {
// Проверка запуска API для остановки или запуска
if (isApiRunning) {
StopApiProcess();
} else {
StartApiProcess();
}
// Обновить статус кнопки после выполнения
UpdateCheckedButton();
}
// Метод для запуска процесса API
private void StartApiProcess() {
// Проверяем, если процесс API не существует или завершён
if (apiProcess == null || apiProcess.HasExited) {
string exec = Path.Combine(Environment.CurrentDirectory, "vpnc.exe");
string arguments = "api";
if (!File.Exists(exec)) {
exec = "dotnet";
arguments = "run api";
}
// Создание нового процесса для запуска API
apiProcess = new Process {
StartInfo = new ProcessStartInfo {
FileName = exec,
Arguments = arguments,
// RedirectStandardOutput = true,
// RedirectStandardError = true,
// UseShellExecute = false,
CreateNoWindow = true
}
};
// Включить отслеживание событий для завершения процесса
apiProcess.EnableRaisingEvents = true;
// Обработчик события завершения процесса
apiProcess.Exited += (s, e) => {
isApiRunning = false; // Обновить статус API на "остановлен"
UpdateCheckedButton(); // Обновляем текст кнопки
};
// Запуск процесса API
try {
apiProcess.Start(); // Запуск процесса
isApiRunning = true; // Обновляем статус
} catch (Exception ex) {
// Обработка ошибок при запуске API
MessageBox.Show($"Failed to run API: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
// Метод для остановки процесса API
private void StopApiProcess() {
// Проверка на существование процесса и что он не завершен
if (apiProcess != null && !apiProcess.HasExited) {
apiProcess.Kill(); // Завершение процесса
apiProcess.Dispose(); // Освобождение ресурсов процесса
apiProcess = null; // Обнуляем переменную процесса
isApiRunning = false; // Обновляем статус
}
}
// Метод для обновления статуса работы API
private void UpdateCheckedButton() {
toggleApiItem.Checked = isApiRunning;
}
// Метод открытия URL с Swagger API
private void OpenSwagger(object? sender, EventArgs? e) {
// Получение порта из конфигурации
string? port = MainProgram.config?.ApiPort?.ToString();
if (!string.IsNullOrEmpty(port)) {
string url = $"http://localhost:{port}/swagger";
try {
// Открытие URL в браузере
Process.Start(new ProcessStartInfo {
FileName = url,
UseShellExecute = true
});
} catch (Exception ex) {
MessageBox.Show($"Failed to open the browser: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
} else {
MessageBox.Show("Port not specified in configuration.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
// Обработчик открытия файла конфигурации
private void OpenConfiguration(object? sender, EventArgs? e) {
string configFilePath = "vpnc.json";
// Проверка, что файл существует
if (File.Exists(configFilePath)) {
try {
// Открытие файла в редакторе по умолчанию
Process.Start(new ProcessStartInfo {
FileName = configFilePath,
UseShellExecute = true // Используем оболочку для открытия
});
} catch (Exception ex) {
MessageBox.Show($"Failed to open configuration file: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
} else {
MessageBox.Show("Configuration file not found.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
private void OpenLogFile(object? sender, EventArgs? e) {
string configFilePath = "vpnc.log";
if (File.Exists(configFilePath)) {
try {
// Открытие файла в редакторе по умолчанию
Process.Start(new ProcessStartInfo {
FileName = configFilePath,
UseShellExecute = true
});
} catch (Exception ex) {
MessageBox.Show($"Failed to open log file: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
} else {
MessageBox.Show("Log file not found.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
// Методы для остановки и запуска процесса VPN
private void StartVPN(object? sender, EventArgs? e) {
string? processPath = MainProgram.config?.ProcessPath?.ToString();
if (!string.IsNullOrEmpty(processPath)) {
MainProgram.StartProcess(processPath);
}
}
private void StopVPN(object? sender, EventArgs? e) {
string? processName = MainProgram.config?.ProcessName?.ToString();
if (!string.IsNullOrEmpty(processName)) {
MainProgram.StopProcess(processName, true);
}
}
private async Task ShowStatusConnectionAsync() {
dynamic statusResult = await MainProgram.GetLocationInfo("Connected");
// Debug
// Console.WriteLine($"statusResult: {statusResult}");
var statusText= $"Time Zone: {statusResult.timezone}\n" +
$"Region: {statusResult.region}\n" +
$"City: {statusResult.city}\n" +
$"External IP: {statusResult.ip}";
notifyIcon.ShowBalloonTip(5000, $"Connection country: {statusResult.country}", statusText, ToolTipIcon.Info);
}
// Обработчик выхода из приложения
private void ExitApplication(object? sender, EventArgs? e) {
StopApiProcess(); // Остановить API перед выходом
notifyIcon.Visible = false; // Скрыть иконку в трее
Application.Exit(); // Завершить приложение
}
// Основной метод приложения
[STAThread]
public static void Main() {
Application.EnableVisualStyles(); // Включение визуальных стилей
Application.SetCompatibleTextRenderingDefault(false); // Установка совместимости с рендерингом текста
new TrayProgram(); // Создание экземпляра класса TrayProgram
Application.Run(); // Запуск цикла обработки событий
}
}