public void Get(string remotename, System.IO.Stream output) { using (var ftpClient = CreateClient()) { var url = new Uri(_url); // Get the remote path var remotePath = url.AbsolutePath.EndsWith("/") ? url.AbsolutePath.Substring(0, url.AbsolutePath.Length - 1) : url.AbsolutePath; if (!string.IsNullOrEmpty(remotename)) { // Append the filename remotePath += "/" + remotename; } using (var inputStream = ftpClient.OpenRead(remotePath)) { try { CoreUtility.CopyStream(inputStream, output, false, _copybuffer); } finally { inputStream.Close(); } } } }
public void Get(string remotename, System.IO.Stream output) { var ftpClient = CreateClient(); // Get the remote path var remotePath = ""; if (!string.IsNullOrEmpty(remotename)) { // Append the filename remotePath += remotename; } using (var inputStream = ftpClient.OpenRead(remotePath)) { try { CoreUtility.CopyStream(inputStream, output, false, _copybuffer); } finally { inputStream.Close(); } } }
private void HandleValidateCertificate(FtpClient control, FtpSslValidationEventArgs e) { if (e.PolicyErrors == SslPolicyErrors.None || _accepAllCertificates) { e.Accept = true; return; } try { var certHash = (_validHashes != null && _validHashes.Length > 0) ? CoreUtility.ByteArrayAsHexString(e.Certificate.GetCertHash()) : null; if (certHash != null) { if (_validHashes.Any(hash => !string.IsNullOrEmpty(hash) && certHash.Equals(hash, StringComparison.OrdinalIgnoreCase))) { e.Accept = true; } } } catch { e.Accept = false; } }
public void Put(string remotename, System.IO.Stream input) { string remotePath = remotename; long streamLen = -1; try { var ftpClient = CreateClient(); try { streamLen = input.Length; } // ReSharper disable once EmptyGeneralCatchClause catch { } // Get the remote path remotePath = ""; if (!string.IsNullOrEmpty(remotename)) { // Append the filename remotePath += remotename; } using (var outputStream = ftpClient.OpenWrite(remotePath)) { try { CoreUtility.CopyStream(input, outputStream, true, _copybuffer); } finally { outputStream.Close(); } } if (_listVerify) { var fileEntries = List(remotename, true); foreach (var fileEntry in fileEntries) { if (fileEntry.Name.Equals(remotename) || fileEntry.Name.EndsWith("/" + remotename, StringComparison.Ordinal) || fileEntry.Name.EndsWith("\\" + remotename, StringComparison.Ordinal)) { if (fileEntry.Size < 0 || streamLen < 0 || fileEntry.Size == streamLen) { return; } throw new UserInformationException(Strings.ListVerifySizeFailure(remotename, fileEntry.Size, streamLen), "AftpListVerifySizeFailure"); } } throw new UserInformationException(Strings.ListVerifyFailure(remotename, fileEntries.Select(n => n.Name)), "AftpListVerifySizeFailure"); } } catch (FtpCommandException ex) { if (ex.Message == "Directory not found.") { throw new FolderMissingException(Strings.MissingFolderError(remotePath, ex.Message), ex); } throw; } }
/// <summary> /// Initialize a new instance/ /// </summary> /// <param name="url">Configured url.</param> /// <param name="options">Configured options. cannot be null.</param> public AlternativeFtpBackend(string url, Dictionary <string, string> options) { _accepAllCertificates = CoreUtility.ParseBoolOption(options, OPTION_ACCEPT_ANY_CERTIFICATE); string certHash; options.TryGetValue(OPTION_ACCEPT_SPECIFIED_CERTIFICATE, out certHash); _validHashes = certHash == null ? null : certHash.Split(new[] { ",", ";" }, StringSplitOptions.RemoveEmptyEntries); var u = new Utility.Uri(url); u.RequireHost(); if (!string.IsNullOrEmpty(u.Username)) { _userInfo = new System.Net.NetworkCredential(); _userInfo.UserName = u.Username; if (!string.IsNullOrEmpty(u.Password)) { _userInfo.Password = u.Password; } else if (options.ContainsKey("auth-password")) { _userInfo.Password = options["auth-password"]; } } else { if (options.ContainsKey("auth-username")) { _userInfo = new System.Net.NetworkCredential(); _userInfo.UserName = options["auth-username"]; if (options.ContainsKey("auth-password")) { _userInfo.Password = options["auth-password"]; } } } //Bugfix, see http://connect.microsoft.com/VisualStudio/feedback/details/695227/networkcredential-default-constructor-leaves-domain-null-leading-to-null-object-reference-exceptions-in-framework-code if (_userInfo != null) { _userInfo.Domain = ""; } _url = u.SetScheme("ftp").SetQuery(null).SetCredentials(null, null).ToString(); _url = Duplicati.Library.Utility.Utility.AppendDirSeparator(_url, "/"); _listVerify = !CoreUtility.ParseBoolOption(options, "disable-upload-verify"); // Process the aftp-data-connection-type option string dataConnectionTypeString; if (!options.TryGetValue(CONFIG_KEY_AFTP_DATA_CONNECTION_TYPE, out dataConnectionTypeString) || string.IsNullOrWhiteSpace(dataConnectionTypeString)) { dataConnectionTypeString = null; } if (dataConnectionTypeString == null || !Enum.TryParse(dataConnectionTypeString, true, out _dataConnectionType)) { _dataConnectionType = DEFAULT_DATA_CONNECTION_TYPE; } // Process the aftp-encryption-mode option string encryptionModeString; if (!options.TryGetValue(CONFIG_KEY_AFTP_ENCRYPTION_MODE, out encryptionModeString) || string.IsNullOrWhiteSpace(encryptionModeString)) { encryptionModeString = null; } if (encryptionModeString == null || !Enum.TryParse(encryptionModeString, true, out _encryptionMode)) { _encryptionMode = DEFAULT_ENCRYPTION_MODE; } // Process the aftp-ssl-protocols option string sslProtocolsString; if (!options.TryGetValue(CONFIG_KEY_AFTP_SSL_PROTOCOLS, out sslProtocolsString) || string.IsNullOrWhiteSpace(sslProtocolsString)) { sslProtocolsString = null; } if (sslProtocolsString == null || !Enum.TryParse(sslProtocolsString, true, out _sslProtocols)) { _sslProtocols = DEFAULT_SSL_PROTOCOLS; } }
public async Task StopNow() { // Choose a dblock size that is small enough so that more than one volume is needed. Dictionary <string, string> options = new Dictionary <string, string>(this.TestOptions) { ["dblock-size"] = "10mb", ["disable-synthetic-filelist"] = "true" }; // Run a complete backup. using (Controller c = new Controller("file://" + this.TARGETFOLDER, options, null)) { IBackupResults backupResults = c.Backup(new[] { this.DATAFOLDER }); Assert.AreEqual(0, backupResults.Errors.Count()); Assert.AreEqual(0, backupResults.Warnings.Count()); List <IListResultFileset> filesets = c.List().Filesets.ToList(); Assert.AreEqual(1, filesets.Count); Assert.AreEqual(BackupType.FULL_BACKUP, filesets[0].IsFullBackup); } // Interrupt a backup with "stop now". this.ModifySourceFiles(); using (Controller c = new Controller("file://" + this.TARGETFOLDER, options, null)) { // ReSharper disable once AccessToDisposedClosure Task backupTask = Task.Run(() => c.Backup(new[] { this.DATAFOLDER })); // Block for a small amount of time to allow the ITaskControl to be associated // with the Controller. Otherwise, the call to Stop will simply be a no-op. Thread.Sleep(1000); c.Stop(false); await backupTask.ConfigureAwait(false); } // The next backup should proceed without issues. using (Controller c = new Controller("file://" + this.TARGETFOLDER, options, null)) { IBackupResults backupResults = c.Backup(new[] { this.DATAFOLDER }); Assert.AreEqual(0, backupResults.Errors.Count()); Assert.AreEqual(0, backupResults.Warnings.Count()); List <IListResultFileset> filesets = c.List().Filesets.ToList(); Assert.AreEqual(2, filesets.Count); Assert.AreEqual(BackupType.FULL_BACKUP, filesets[1].IsFullBackup); Assert.AreEqual(BackupType.FULL_BACKUP, filesets[0].IsFullBackup); } // Restore from the backup that followed the interruption. Dictionary <string, string> restoreOptions = new Dictionary <string, string>(options) { ["restore-path"] = this.RESTOREFOLDER }; using (Controller c = new Controller("file://" + this.TARGETFOLDER, restoreOptions, null)) { IListResults lastResults = c.List("*"); string[] fullVersionFiles = lastResults.Files.Select(x => x.Path).Where(x => !Utility.IsFolder(x, File.GetAttributes)).ToArray(); Assert.AreEqual(this.fileSizes.Length, fullVersionFiles.Length); IRestoreResults restoreResults = c.Restore(fullVersionFiles); Assert.AreEqual(0, restoreResults.Errors.Count()); Assert.AreEqual(0, restoreResults.Warnings.Count()); foreach (string filepath in fullVersionFiles) { string filename = Path.GetFileName(filepath); TestUtils.AssertFilesAreEqual(filepath, Path.Combine(this.RESTOREFOLDER, filename ?? String.Empty), false, filename); } } }
public async Task StopAfterCurrentFile() { // Choose a dblock size that is small enough so that more than one volume is needed. Dictionary <string, string> options = new Dictionary <string, string>(this.TestOptions) { ["dblock-size"] = "10mb" }; // Run a complete backup. using (Controller c = new Controller("file://" + this.TARGETFOLDER, options, null)) { IBackupResults backupResults = c.Backup(new[] { this.DATAFOLDER }); Assert.AreEqual(0, backupResults.Errors.Count()); Assert.AreEqual(0, backupResults.Warnings.Count()); Assert.AreEqual(1, c.List().Filesets.Count()); Assert.AreEqual(BackupType.FULL_BACKUP, c.List().Filesets.Single(x => x.Version == 0).IsFullBackup); } // Run a partial backup. using (Controller c = new Controller("file://" + this.TARGETFOLDER, options, null)) { IBackupResults backupResults = await this.RunPartialBackup(c).ConfigureAwait(false); Assert.AreEqual(0, backupResults.Errors.Count()); Assert.AreEqual(1, backupResults.Warnings.Count()); // If we interrupt the backup, the most recent Fileset should be marked as partial. Assert.AreEqual(2, c.List().Filesets.Count()); Assert.AreEqual(BackupType.FULL_BACKUP, c.List().Filesets.Single(x => x.Version == 1).IsFullBackup); Assert.AreEqual(BackupType.PARTIAL_BACKUP, c.List().Filesets.Single(x => x.Version == 0).IsFullBackup); } // Restore files from the partial backup set. Dictionary <string, string> restoreOptions = new Dictionary <string, string>(options) { ["restore-path"] = this.RESTOREFOLDER }; using (Controller c = new Controller("file://" + this.TARGETFOLDER, restoreOptions, null)) { IListResults lastResults = c.List("*"); string[] partialVersionFiles = lastResults.Files.Select(x => x.Path).Where(x => !Utility.IsFolder(x, File.GetAttributes)).ToArray(); Assert.GreaterOrEqual(partialVersionFiles.Length, 1); c.Restore(partialVersionFiles); foreach (string filepath in partialVersionFiles) { string filename = Path.GetFileName(filepath); TestUtils.AssertFilesAreEqual(filepath, Path.Combine(this.RESTOREFOLDER, filename ?? String.Empty), false, filename); } } // Recreating the database should preserve the backup types. File.Delete(this.DBFILE); using (Controller c = new Controller("file://" + this.TARGETFOLDER, options, null)) { IRepairResults repairResults = c.Repair(); Assert.AreEqual(0, repairResults.Errors.Count()); Assert.AreEqual(0, repairResults.Warnings.Count()); Assert.AreEqual(2, c.List().Filesets.Count()); Assert.AreEqual(BackupType.FULL_BACKUP, c.List().Filesets.Single(x => x.Version == 1).IsFullBackup); Assert.AreEqual(BackupType.PARTIAL_BACKUP, c.List().Filesets.Single(x => x.Version == 0).IsFullBackup); } // Run a complete backup. Listing the Filesets should include both full and partial backups. using (Controller c = new Controller("file://" + this.TARGETFOLDER, options, null)) { IBackupResults backupResults = c.Backup(new[] { this.DATAFOLDER }); Assert.AreEqual(0, backupResults.Errors.Count()); Assert.AreEqual(0, backupResults.Warnings.Count()); Assert.AreEqual(3, c.List().Filesets.Count()); Assert.AreEqual(BackupType.FULL_BACKUP, c.List().Filesets.Single(x => x.Version == 2).IsFullBackup); Assert.AreEqual(BackupType.PARTIAL_BACKUP, c.List().Filesets.Single(x => x.Version == 1).IsFullBackup); Assert.AreEqual(BackupType.FULL_BACKUP, c.List().Filesets.Single(x => x.Version == 0).IsFullBackup); } // Restore files from the full backup set. restoreOptions["overwrite"] = "true"; using (Controller c = new Controller("file://" + this.TARGETFOLDER, restoreOptions, null)) { IListResults lastResults = c.List("*"); string[] fullVersionFiles = lastResults.Files.Select(x => x.Path).Where(x => !Utility.IsFolder(x, File.GetAttributes)).ToArray(); Assert.AreEqual(this.fileSizes.Length, fullVersionFiles.Length); IRestoreResults restoreResults = c.Restore(fullVersionFiles); Assert.AreEqual(0, restoreResults.Errors.Count()); Assert.AreEqual(0, restoreResults.Warnings.Count()); foreach (string filepath in fullVersionFiles) { string filename = Path.GetFileName(filepath); TestUtils.AssertFilesAreEqual(filepath, Path.Combine(this.RESTOREFOLDER, filename ?? String.Empty), false, filename); } } }
public async Task StopAfterCurrentFile() { // Choose a dblock size that is small enough so that more than one volume is needed. Dictionary <string, string> options = new Dictionary <string, string>(this.TestOptions) { ["dblock-size"] = "10mb" }; // Run a complete backup. using (Controller c = new Controller("file://" + this.TARGETFOLDER, options, null)) { c.Backup(new[] { this.DATAFOLDER }); Assert.AreEqual(1, c.List().Filesets.Count()); Assert.AreEqual(BackupType.FULL_BACKUP, c.List().Filesets.Single(x => x.Version == 0).IsFullBackup); } // Modify the source files and interrupt a backup. this.ModifySourceFiles(); using (Controller c = new Controller("file://" + this.TARGETFOLDER, options, null)) { // ReSharper disable once AccessToDisposedClosure Task <IBackupResults> backupTask = Task.Run(() => c.Backup(new[] { this.DATAFOLDER })); // Block for a small amount of time to allow the ITaskControl to be associated // with the Controller. Otherwise, the call to Stop will simply be a no-op. Thread.Sleep(1000); // If we interrupt the backup, the most recent Fileset should be marked as partial. c.Stop(true); await backupTask.ConfigureAwait(false); Assert.AreEqual(2, c.List().Filesets.Count()); Assert.AreEqual(BackupType.FULL_BACKUP, c.List().Filesets.Single(x => x.Version == 1).IsFullBackup); Assert.AreEqual(BackupType.PARTIAL_BACKUP, c.List().Filesets.Single(x => x.Version == 0).IsFullBackup); } // Restore files from the partial backup set. Dictionary <string, string> restoreOptions = new Dictionary <string, string>(options) { ["restore-path"] = this.RESTOREFOLDER }; using (Controller c = new Controller("file://" + this.TARGETFOLDER, restoreOptions, null)) { IListResults lastResults = c.List("*"); string[] partialVersionFiles = lastResults.Files.Select(x => x.Path).Where(x => !Utility.IsFolder(x, File.GetAttributes)).ToArray(); Assert.GreaterOrEqual(partialVersionFiles.Length, 1); c.Restore(partialVersionFiles); foreach (string filepath in partialVersionFiles) { string filename = Path.GetFileName(filepath); Assert.IsTrue(TestUtils.CompareFiles(filepath, Path.Combine(this.RESTOREFOLDER, filename ?? String.Empty), filename, false)); } } // Recreating the database should preserve the backup types. File.Delete(this.DBFILE); using (Controller c = new Controller("file://" + this.TARGETFOLDER, options, null)) { c.Repair(); Assert.AreEqual(2, c.List().Filesets.Count()); Assert.AreEqual(BackupType.FULL_BACKUP, c.List().Filesets.Single(x => x.Version == 1).IsFullBackup); Assert.AreEqual(BackupType.PARTIAL_BACKUP, c.List().Filesets.Single(x => x.Version == 0).IsFullBackup); } // Run a complete backup. Listing the Filesets should omit the previous partial backup. using (Controller c = new Controller("file://" + this.TARGETFOLDER, options, null)) { c.Backup(new[] { this.DATAFOLDER }); Assert.AreEqual(2, c.List().Filesets.Count()); Assert.AreEqual(BackupType.FULL_BACKUP, c.List().Filesets.Single(x => x.Version == 1).IsFullBackup); Assert.AreEqual(BackupType.FULL_BACKUP, c.List().Filesets.Single(x => x.Version == 0).IsFullBackup); } // Restore files from the full backup set. restoreOptions["overwrite"] = "true"; using (Controller c = new Controller("file://" + this.TARGETFOLDER, restoreOptions, null)) { IListResults lastResults = c.List("*"); string[] fullVersionFiles = lastResults.Files.Select(x => x.Path).Where(x => !Utility.IsFolder(x, File.GetAttributes)).ToArray(); Assert.AreEqual(this.fileSizes.Length, fullVersionFiles.Length); c.Restore(fullVersionFiles); foreach (string filepath in fullVersionFiles) { string filename = Path.GetFileName(filepath); Assert.IsTrue(TestUtils.CompareFiles(filepath, Path.Combine(this.RESTOREFOLDER, filename ?? String.Empty), filename, false)); } } }