diff --git a/BackupManager/BackupManager.csproj b/BackupManager/BackupManager.csproj
index a16cc9d..81ce1d8 100644
--- a/BackupManager/BackupManager.csproj
+++ b/BackupManager/BackupManager.csproj
@@ -7,6 +7,7 @@
+
diff --git a/BackupManager/Config.cs b/BackupManager/Config.cs
index 8b68343..2160ee3 100644
--- a/BackupManager/Config.cs
+++ b/BackupManager/Config.cs
@@ -2,6 +2,8 @@
{
public class Config
{
+ public StorageMethod StorageMethod { get; set; } = StorageMethod.GoogleDrive;
+
public string GoogleClientId { get; set; }
public string GoogleClientSecret { get; set; }
public string GoogleBackupDirectory { get; set; } = @"Backups";
@@ -13,6 +15,14 @@
public string GoogleRefreshToken { get; set; }
public string GoogleTokenIssued { get; set; }
+ public string SftpHost { get; set; }
+ public ushort SftpPort { get; set; }
+ public string SftpUsername { get; set; }
+ public string SftpPassphrase { get; set; }
+ public string SftpPrivateKey { get; set; }
+ public string SftpBackupDirectoryPath { get; set; }
+ public string SftpTrustedHost { get; set; }
+
public string MySqlDumpPathWindows { get; set; } = @"C:\Program Files\MySQL\MySQL Server 8.0\bin\mysqldump.exe";
public string MySqlDumpPath { get; set; } = @"mysqldump";
public string MySqlHost { get; set; } = @"localhost";
diff --git a/BackupManager/Program.cs b/BackupManager/Program.cs
index 32a402b..0614a0e 100644
--- a/BackupManager/Program.cs
+++ b/BackupManager/Program.cs
@@ -1,6 +1,9 @@
using Google.Apis.Auth.OAuth2;
using Google.Apis.Drive.v3;
using Google.Apis.Services;
+using Renci.SshNet;
+using Renci.SshNet.Common;
+using Renci.SshNet.Sftp;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -42,7 +45,9 @@ namespace BackupManager
);
private static DriveService DriveService;
- private static GFile BackupStorage;
+ private static object BackupStorage;
+
+ private static SftpClient SFTP;
public static bool Headless;
@@ -103,19 +108,69 @@ namespace BackupManager
LoadConfig();
- UserCredential uc = GoogleAuthenticate(
- new ClientSecrets
- {
- ClientId = Config.GoogleClientId,
- ClientSecret = Config.GoogleClientSecret,
- },
- new[] {
- DriveService.Scope.Drive,
- DriveService.Scope.DriveFile,
- }
- );
+ switch (Config.StorageMethod)
+ {
+ case StorageMethod.GoogleDrive:
+ UserCredential uc = GoogleAuthenticate(
+ new ClientSecrets
+ {
+ ClientId = Config.GoogleClientId,
+ ClientSecret = Config.GoogleClientSecret,
+ },
+ new[] {
+ DriveService.Scope.Drive,
+ DriveService.Scope.DriveFile,
+ }
+ );
+
+ CreateDriveService(uc);
+ break;
+
+ case StorageMethod.Sftp:
+ if (string.IsNullOrWhiteSpace(Config.SftpHost) || string.IsNullOrWhiteSpace(Config.SftpUsername))
+ {
+ sw.Stop();
+ Config.SftpHost = Config.SftpHost ?? @"";
+ Config.SftpPort = Config.SftpPort < 1 ? (ushort)22 : Config.SftpPort;
+ Config.SftpUsername = Config.SftpUsername ?? @"";
+ Config.SftpPassphrase = Config.SftpPassphrase ?? @"";
+ Config.SftpPrivateKey = Config.SftpPrivateKey ?? @"";
+ Config.SftpTrustedHost = Config.SftpTrustedHost ?? @"";
+ Config.SftpBackupDirectoryPath = Config.SftpBackupDirectoryPath ?? @"";
+ SaveConfig();
+ Error(@"No Sftp host/auth details found in the configuration.");
+ }
+
+ if (!string.IsNullOrEmpty(Config.SftpPrivateKey))
+ SFTP = new SftpClient(Config.SftpHost, Config.SftpPort, Config.SftpUsername, new PrivateKeyFile(Config.SftpPrivateKey, Config.SftpPassphrase ?? string.Empty));
+ else
+ SFTP = new SftpClient(Config.SftpHost, Config.SftpPort, Config.SftpUsername, Config.SftpPassphrase ?? string.Empty);
+
+ using (ManualResetEvent mre = new ManualResetEvent(false))
+ {
+ if (!string.IsNullOrWhiteSpace(Config.SftpTrustedHost))
+ SFTP.HostKeyReceived += (s, e) =>
+ {
+ string checkString = e.HostKeyName + @"#" + Convert.ToBase64String(e.HostKey) + @"#" + Convert.ToBase64String(e.FingerPrint);
+ e.CanTrust = Config.SftpTrustedHost.SequenceEqual(checkString);
+ mre.Set();
+ };
+ else
+ mre.Set();
+
+ try
+ {
+ SFTP.Connect();
+ } catch (SshConnectionException)
+ {
+ Error(@"Error during SFTP connect, it's possible the server key changed.");
+ }
+
+ mre.WaitOne();
+ }
+ break;
+ }
- CreateDriveService(uc);
GetBackupStorage();
Log(@"Database backup...");
@@ -123,8 +178,18 @@ namespace BackupManager
using (Stream s = CreateMySqlDump())
using (Stream g = GZipEncodeStream(s))
{
- GFile f = Upload(DatabaseDumpName, @"application/sql+gzip", g);
- Log($@"MySQL dump uploaded: {f.Name} ({f.Id})");
+ object f = Upload(DatabaseDumpName, @"application/sql+gzip", g);
+
+ switch (f)
+ {
+ case GFile fgf:
+ Log($@"MySQL dump uploaded: {fgf.Name} ({fgf.Id})");
+ break;
+
+ default:
+ Log($@"MySQL dump uploaded.");
+ break;
+ }
}
if (Directory.Exists(Config.MisuzuPath))
@@ -144,8 +209,18 @@ namespace BackupManager
using (FileStream fs = File.OpenRead(archivePath))
{
- GFile f = Upload(UserDataName, @"application/zip", fs);
- Log($@"Misuzu data uploaded: {f.Name} ({f.Id})");
+ object f = Upload(UserDataName, @"application/zip", fs);
+
+ switch (f)
+ {
+ case GFile fgf:
+ Log($@"Misuzu data uploaded: {fgf.Name} ({fgf.Id})");
+ break;
+
+ default:
+ Log($@"Misuzu data uploaded.");
+ break;
+ }
}
File.Delete(archivePath);
@@ -185,22 +260,37 @@ namespace BackupManager
Console.ResetColor();
}
+#if DEBUG
+ Console.ReadLine();
+#endif
+
Environment.Exit(exit);
}
- public static GFile Upload(string name, string type, Stream stream)
+ public static object Upload(string name, string type, Stream stream)
{
Log($@"Uploading '{name}'...");
- FilesResource.CreateMediaUpload request = DriveService.Files.Create(new GFile
+
+ switch (BackupStorage)
{
- Name = name,
- Parents = new List {
- BackupStorage.Id,
- },
- }, stream, type);
- request.Fields = @"id, name";
- request.Upload();
- return request.ResponseBody;
+ case GFile gfile:
+ FilesResource.CreateMediaUpload request = DriveService.Files.Create(new GFile
+ {
+ Name = name,
+ Parents = new List {
+ gfile.Id,
+ },
+ }, stream, type);
+ request.Fields = @"id, name";
+ request.Upload();
+ return request.ResponseBody;
+
+ case string scpName:
+ SFTP.UploadFile(stream, scpName + @"/" + name);
+ break;
+ }
+
+ return null;
}
public static string GetMisuzuConfig()
@@ -278,7 +368,7 @@ namespace BackupManager
sw.WriteLine(@"default-character-set=utf8");
}
- ProcessStartInfo psi = new ProcessStartInfo
+ Process p = Process.Start(new ProcessStartInfo
{
FileName = IsWindows ? Config.MySqlDumpPathWindows : Config.MySqlDumpPath,
RedirectStandardError = false,
@@ -287,8 +377,7 @@ namespace BackupManager
Arguments = $@"--defaults-file={tmpFile} --add-locks -l --order-by-primary -B {Config.MySqlDatabases}",
UseShellExecute = false,
CreateNoWindow = true,
- };
- Process p = Process.Start(psi);
+ });
int read;
byte[] buffer = new byte[1024];
@@ -341,27 +430,43 @@ namespace BackupManager
public static void GetBackupStorage(string name = null)
{
- name = name ?? Config.GoogleBackupDirectory;
- Log(@"Getting backup folder...");
- FilesResource.ListRequest lr = DriveService.Files.List();
- lr.Q = $@"name = '{name}' and mimeType = '{FOLDER_MIME}'";
- lr.PageSize = 1;
- lr.Fields = @"files(id)";
- GFile backupFolder = lr.Execute().Files.FirstOrDefault();
-
- if (backupFolder == null)
+ switch (Config.StorageMethod)
{
- Log(@"Backup folder doesn't exist yet, creating it...");
- FilesResource.CreateRequest dcr = DriveService.Files.Create(new GFile
- {
- Name = name,
- MimeType = FOLDER_MIME,
- });
- dcr.Fields = @"id";
- backupFolder = dcr.Execute();
- }
+ case StorageMethod.GoogleDrive:
+ name = name ?? Config.GoogleBackupDirectory;
+ Log(@"Getting backup folder...");
+ FilesResource.ListRequest lr = DriveService.Files.List();
+ lr.Q = $@"name = '{name}' and mimeType = '{FOLDER_MIME}'";
+ lr.PageSize = 1;
+ lr.Fields = @"files(id)";
+ GFile backupFolder = lr.Execute().Files.FirstOrDefault();
- BackupStorage = backupFolder;
+ if (backupFolder == null)
+ {
+ Log(@"Backup folder doesn't exist yet, creating it...");
+ FilesResource.CreateRequest dcr = DriveService.Files.Create(new GFile
+ {
+ Name = name,
+ MimeType = FOLDER_MIME,
+ });
+ dcr.Fields = @"id";
+ backupFolder = dcr.Execute();
+ }
+
+ BackupStorage = backupFolder;
+ break;
+
+ case StorageMethod.Sftp:
+ string directory = (BackupStorage = name ?? Config.SftpBackupDirectoryPath) as string;
+ try
+ {
+ SFTP.ListDirectory(directory);
+ } catch (SftpPathNotFoundException)
+ {
+ SFTP.CreateDirectory(directory);
+ }
+ break;
+ }
}
}
}