Files
duplicati/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs
T

288 lines
13 KiB
C#
Raw Normal View History

2024-02-28 15:45:30 +01:00
// Copyright (C) 2024, The Duplicati Team
// https://duplicati.com, hello@duplicati.com
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Net;
using Duplicati.Library.Common;
2017-06-29 00:08:02 +02:00
using Duplicati.Library.Interface;
namespace Duplicati.GUI.TrayIcon
{
2011-11-28 11:05:42 +00:00
public static class Program
{
2018-02-25 13:54:20 +01:00
public enum PasswordSource
{
Database,
HostedServer
}
public static HttpServerConnection Connection;
private const string HOSTURL_OPTION = "hosturl";
private const string NOHOSTEDSERVER_OPTION = "no-hosted-server";
private const string READCONFIGFROMDB_OPTION = "read-config-from-db";
2017-10-11 08:57:12 +02:00
private const string DETACHED_PROCESS = "detached-process";
private const string BROWSER_COMMAND_OPTION = "browser-command";
2017-03-06 14:26:40 +01:00
private const string DEFAULT_HOSTURL = "http://localhost:8200";
2011-11-28 11:05:42 +00:00
private static string _browser_command = null;
2019-02-12 21:50:33 +01:00
private static bool disableTrayIconLogin = false;
private static bool openui = false;
private static Uri serverURL = new Uri(DEFAULT_HOSTURL);
public static string BrowserCommand { get { return _browser_command; } }
2017-05-02 21:39:04 +02:00
public static Server.Database.Connection databaseConnection = null;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
public static int Main(string[] _args)
{
2019-08-03 22:14:41 -04:00
List<string> args = new List<string>(_args);
Dictionary<string, string> options = Duplicati.Library.Utility.CommandLineParser.ExtractOptions(args);
2014-04-02 11:34:33 +02:00
if (Platform.IsClientWindows && !Duplicati.Library.Utility.Utility.ParseBoolOption(options, DETACHED_PROCESS))
2019-08-03 22:14:41 -04:00
Duplicati.Library.Utility.Win32.AttachConsole(Duplicati.Library.Utility.Win32.ATTACH_PARENT_PROCESS);
2019-02-12 21:47:17 +01:00
2019-08-03 22:14:41 -04:00
foreach (string s in args)
if (
s.Equals("help", StringComparison.OrdinalIgnoreCase) ||
s.Equals("/help", StringComparison.OrdinalIgnoreCase) ||
s.Equals("usage", StringComparison.OrdinalIgnoreCase) ||
s.Equals("/usage", StringComparison.OrdinalIgnoreCase))
options["help"] = "";
2014-04-02 11:34:33 +02:00
2019-08-03 22:14:41 -04:00
if (options.ContainsKey("help"))
{
Console.WriteLine("Supported commandline arguments:");
Console.WriteLine();
2014-04-02 11:34:33 +02:00
2019-08-03 22:14:41 -04:00
foreach (Library.Interface.ICommandLineArgument arg in SupportedCommands)
{
2019-08-03 22:14:41 -04:00
Console.WriteLine("--{0}: {1}", arg.Name, arg.LongDescription);
}
2014-04-02 11:34:33 +02:00
2019-08-03 22:14:41 -04:00
Console.WriteLine("Additionally, these server options are also supported:");
Console.WriteLine();
2014-04-02 11:34:33 +02:00
2019-08-03 22:14:41 -04:00
foreach (Library.Interface.ICommandLineArgument arg in Duplicati.Server.Program.SupportedCommands)
Console.WriteLine("--{0}: {1}", arg.Name, arg.LongDescription);
2017-06-22 15:10:31 +02:00
return 0;
2019-08-03 22:14:41 -04:00
}
2019-02-12 21:47:17 +01:00
2019-08-03 22:14:41 -04:00
options.TryGetValue(BROWSER_COMMAND_OPTION, out _browser_command);
2019-08-03 22:08:17 -04:00
2019-08-03 22:14:41 -04:00
HostedInstanceKeeper hosted = null;
string password = null;
var saltedpassword = false;
if (!Library.Utility.Utility.ParseBoolOption(options, NOHOSTEDSERVER_OPTION))
{
try
{
hosted = new HostedInstanceKeeper(_args);
}
2019-08-03 22:14:41 -04:00
catch (Server.SingleInstance.MultipleInstanceException)
{
return 1;
2019-08-03 22:14:41 -04:00
}
// We have a hosted server, if this is the first run,
// we should open the main page
openui = Duplicati.Server.Program.IsFirstRun || Duplicati.Server.Program.ServerPortChanged;
password = Duplicati.Server.Program.DataConnection.ApplicationSettings.WebserverPassword;
saltedpassword = true;
// Tell the hosted server it was started by the TrayIcon
Duplicati.Server.Program.Origin = "Tray icon";
2018-02-25 13:54:20 +01:00
2019-08-03 22:14:41 -04:00
var cert = Duplicati.Server.Program.DataConnection.ApplicationSettings.ServerSSLCertificate;
var scheme = "http";
2018-02-25 13:54:20 +01:00
2019-08-03 22:14:41 -04:00
if (cert != null && cert.HasPrivateKey)
scheme = "https";
2018-02-25 13:54:20 +01:00
2019-08-03 22:14:41 -04:00
serverURL = (new UriBuilder(serverURL)
2018-02-25 13:54:20 +01:00
{
2019-08-03 22:14:41 -04:00
Port = Duplicati.Server.Program.ServerPort,
Scheme = scheme
}).Uri;
}
else if (Library.Utility.Utility.ParseBoolOption(options, READCONFIGFROMDB_OPTION))
{
databaseConnection = Server.Program.GetDatabaseConnection(options);
2019-08-03 22:14:41 -04:00
if (databaseConnection != null)
{
disableTrayIconLogin = databaseConnection.ApplicationSettings.DisableTrayIconLogin;
password = databaseConnection.ApplicationSettings.WebserverPasswordTrayIcon;
saltedpassword = false;
2017-06-29 22:47:21 +02:00
2019-08-03 22:14:41 -04:00
var cert = databaseConnection.ApplicationSettings.ServerSSLCertificate;
var scheme = "http";
2017-06-29 22:47:21 +02:00
if (cert != null && cert.HasPrivateKey)
scheme = "https";
2017-06-29 22:47:21 +02:00
serverURL = (new UriBuilder(serverURL)
2017-06-29 22:47:21 +02:00
{
2019-08-03 22:14:41 -04:00
Port = databaseConnection.ApplicationSettings.LastWebserverPort == -1 ? serverURL.Port : databaseConnection.ApplicationSettings.LastWebserverPort,
2017-06-29 22:47:21 +02:00
Scheme = scheme
}).Uri;
}
2019-08-03 22:14:41 -04:00
}
2019-08-03 22:08:17 -04:00
2019-08-03 22:14:41 -04:00
string pwd;
2019-08-03 22:08:17 -04:00
2019-08-03 22:14:41 -04:00
if (options.TryGetValue("webserver-password", out pwd))
{
password = pwd;
saltedpassword = false;
}
2017-05-03 00:56:41 +02:00
2019-08-03 22:14:41 -04:00
string url;
2019-02-12 21:47:17 +01:00
2019-08-03 22:14:41 -04:00
if (options.TryGetValue(HOSTURL_OPTION, out url))
serverURL = new Uri(url);
2019-08-03 22:08:17 -04:00
2024-03-04 14:34:38 +01:00
StartTray(_args, options, hosted, password, saltedpassword);
return 0;
2019-02-12 21:47:17 +01:00
}
2024-03-04 14:34:38 +01:00
private static void StartTray(string[] _args, Dictionary<string, string> options, HostedInstanceKeeper hosted, string password, bool saltedpassword)
2019-02-12 21:47:17 +01:00
{
2019-08-03 22:14:41 -04:00
using (hosted)
{
2019-08-03 22:14:41 -04:00
var reSpawn = 0;
2017-06-28 23:45:56 +02:00
2019-08-03 22:14:41 -04:00
do
{
try
2019-08-03 22:08:17 -04:00
{
2022-03-19 14:25:51 +01:00
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
try
{
//try TLS 1.3 (type not available on .NET < 4.8)
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | (SecurityProtocolType)12288;
}
catch (NotSupportedException)
{
}
2019-08-03 22:14:41 -04:00
using (Connection = new HttpServerConnection(serverURL, password, saltedpassword, databaseConnection != null ? PasswordSource.Database : PasswordSource.HostedServer, disableTrayIconLogin, options))
{
2024-03-04 14:34:38 +01:00
using (var tk = RunTrayIcon())
2019-08-03 22:08:17 -04:00
{
2019-08-03 22:14:41 -04:00
if (hosted != null && Server.Program.ApplicationInstance != null)
Server.Program.ApplicationInstance.SecondInstanceDetected +=
new Server.SingleInstance.SecondInstanceDelegate(
x => { tk.ShowUrlInWindow(serverURL.ToString()); });
// TODO: If we change to hosted browser this should be a callback
if (openui)
{
2019-08-03 22:14:41 -04:00
try
{
tk.ShowUrlInWindow(Connection.StatusWindowURL);
2019-08-03 22:14:41 -04:00
Duplicati.Server.Program.IsFirstRun = false;
Duplicati.Server.Program.ServerPortChanged = false;
}
catch
{
}
2019-08-03 22:14:41 -04:00
}
2019-08-03 22:14:41 -04:00
// If the server shuts down, shut down the tray-icon as well
Action shutdownEvent = () =>
{
// Make sure we do not start again after
// a controlled exit
reSpawn = 100;
tk.InvokeExit();
};
2019-08-03 22:14:41 -04:00
if (hosted != null)
hosted.InstanceShutdown += shutdownEvent;
2019-08-03 22:14:41 -04:00
tk.Init(_args);
2019-08-03 22:14:41 -04:00
// If the tray-icon quits, stop the server
reSpawn = 100;
2019-08-03 22:14:41 -04:00
// Make sure that the server shutdown does not access the tray-icon,
// as it would be disposed by now
if (hosted != null)
hosted.InstanceShutdown -= shutdownEvent;
}
}
2019-08-03 22:14:41 -04:00
}
catch (WebException ex)
{
System.Diagnostics.Trace.WriteLine("Request error: " + ex);
Console.WriteLine("Request error: " + ex);
2019-08-03 22:14:41 -04:00
reSpawn++;
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine("Unexpected error: " + ex);
Console.WriteLine("Unexpected error: " + ex);
return;
}
} while (reSpawn < 3);
}
}
2024-03-04 14:34:38 +01:00
private static TrayIconBase RunTrayIcon()
=> new AvaloniaRunner();
2011-11-28 11:05:42 +00:00
public static Duplicati.Library.Interface.ICommandLineArgument[] SupportedCommands
{
get
{
2017-10-11 08:57:12 +02:00
var args = new List<Duplicati.Library.Interface.ICommandLineArgument>()
{
new Duplicati.Library.Interface.CommandLineArgument(HOSTURL_OPTION, CommandLineArgument.ArgumentType.String, "Selects the url to connect to", "Supply the url that the TrayIcon will connect to and show status for", DEFAULT_HOSTURL),
new Duplicati.Library.Interface.CommandLineArgument(NOHOSTEDSERVER_OPTION, CommandLineArgument.ArgumentType.String, "Disables local server", "Set this option to not spawn a local service, use if the TrayIcon should connect to a running service"),
new Duplicati.Library.Interface.CommandLineArgument(READCONFIGFROMDB_OPTION, CommandLineArgument.ArgumentType.String, "Read server connection info from DB", $"Set this option to read server connection info for running service from its database (only together with {NOHOSTEDSERVER_OPTION})"),
2017-10-11 08:57:12 +02:00
new Duplicati.Library.Interface.CommandLineArgument(BROWSER_COMMAND_OPTION, CommandLineArgument.ArgumentType.String, "Sets the browser command", "Set this option to override the default browser detection"),
};
2017-10-11 08:57:12 +02:00
2018-11-02 22:13:25 +01:00
if (Platform.IsClientWindows)
2017-10-11 08:57:12 +02:00
{
args.Add(new Duplicati.Library.Interface.CommandLineArgument(DETACHED_PROCESS, CommandLineArgument.ArgumentType.String, "Runs the tray-icon detached", "This option runs the tray-icon in detached mode, meaning that the process will exit immediately and not send output to the console of the caller"));
}
return args.ToArray();
}
}
}
}