public MenuItemWrapper(RumpsRunner parent, string text, Duplicati.GUI.TrayIcon.MenuIcons icon, Action callback, IList<Duplicati.GUI.TrayIcon.IMenuItem> subitems) { m_parent = parent; Key = Guid.NewGuid().ToString("N"); m_text = text ?? ""; Callback = callback; m_enabled = true; m_default = false; if (subitems != null) Subitems = subitems.Cast<MenuItemWrapper>().ToList(); }
private Dictionary<string, string> GetSourceNames(Duplicati.Server.Serialization.Interface.IBackup backup) { var systemIO = Duplicati.Library.Snapshots.SnapshotUtility.SystemIO; return backup.Sources.Distinct().Select(x => { var sp = SpecialFolders.TranslateToDisplayString(x); if (sp != null) return new KeyValuePair<string, string>(x, sp); x = SpecialFolders.ExpandEnvironmentVariables(x); try { var nx = x; if (nx.EndsWith(System.IO.Path.DirectorySeparatorChar.ToString())) nx = nx.Substring(0, nx.Length - 1); var n = systemIO.PathGetFileName(nx); if (!string.IsNullOrWhiteSpace(n)) return new KeyValuePair<string, string>(x, n); } catch { } if (x.EndsWith(System.IO.Path.DirectorySeparatorChar.ToString()) && x.Length > 1) return new KeyValuePair<string, string>(x, x.Substring(0, x.Length - 1).Substring(x.Substring(0, x.Length - 1).LastIndexOf("/") + 1)); else return new KeyValuePair<string, string>(x, x); }).ToDictionary(x => x.Key, x => x.Value); }
/// <summary> /// Constructor for webmodule interface /// </summary> public DynamicModule(Duplicati.Library.Interface.IWebModule module) { this.Key = module.Key; this.Description = module.Description; this.DisplayName = module.DisplayName; if (module.SupportedCommands != null) this.Options = module.SupportedCommands.ToArray(); }
/// <summary> /// Constructor for backend interface /// </summary> public DynamicModule(Duplicati.Library.Interface.IBackend backend) { this.Key = backend.ProtocolKey; this.Description = backend.Description; this.DisplayName = backend.DisplayName; if (backend.SupportedCommands != null) this.Options = backend.SupportedCommands.ToArray(); }
/// <summary> /// Edit Settings /// </summary> /// <remarks> /// Here a global password may be set. If set, User may use it for backups, or not as User is want. /// The bubbles thing is not used now. /// </remarks> /// <param name="aSettings">Settings to edit</param> public SettingsDialog(Duplicati.Scheduler.Data.SchedulerDataSet.SettingsDataTable aSettings) { InitializeComponent(); this.Settings = aSettings; OriginalXML = XmlFromTable(aSettings); this.checkBox1.Checked = this.Settings.UseGlobalPassword; this.passwordControl1.CheckMod = this.Settings.Values.CheckMod; this.passwordControl1.Checksum = this.Settings.Values.Checksum; this.numericUpDown1.Value = (decimal)this.Settings.Values.LogFileAgeDays; if (this.Settings.Values.IsShowBubblesNull()) this.Settings.Values.ShowBubbles = true; this.BubbleCheckBox.Checked = this.Settings.Values.ShowBubbles; }
/// <summary> /// Set the values /// </summary> /// <param name="aRow">The job row</param> /// <param name="aDetails">Text description of the job trigger</param> public void SetSummary(Duplicati.Scheduler.Data.SchedulerDataSet.JobsRow aRow, string aDetails) { this.nameTextBox.Text = aRow.Name; this.DescriptionTextBox.Text = aDetails; this.sourceTextBox.Text = aRow.Source; this.destinationTextBox.Text = aRow.Destination; this.LastModLabel.Text = (aRow.IsLastModNull() || aRow.LastMod == DateTime.MinValue) ? "Never edited" : "Last edited: " + aRow.LastMod.ToString("ddd, dd MMMM yyyy hh:mm tt"); if (aRow.FullOnly || (aRow.FullRepeatDays == 0 && aRow.FullAfterN == 0)) this.fullRepeatStrTextBox.Text = "Always do full backups"; else this.fullRepeatStrTextBox.Text = aRow.FullRepeatDays > 0 ? aRow.FullRepeatDays + " days " : aRow.FullAfterN.ToString()+" incrementals"; this.MaxFullTextBox.Text = aRow.MaxFulls > 0 ? "the last "+aRow.MaxFulls.ToString("N0") : "all" ; this.MaxAgeTextBox.Text = aRow.MaxAgeDays > 0 ? aRow.MaxAgeDays.ToString("N0") + " days" : "<no limit>" ; this.PassRichTextBox.Text = new string[] { "not password protected.", "protected by local password.", "protected by global password." }[aRow.GetCheckSrc()]; }
public static IEnumerable<Duplicati.Library.Main.Volumes.IFileEntry> EnumerateFilesInDList(string file, Duplicati.Library.Utility.IFilter filter, Dictionary<string, string> options) { var p = Library.Main.Volumes.VolumeBase.ParseFilename(file); using(var cm = Library.DynamicLoader.CompressionLoader.GetModule(p.CompressionModule, file, options)) using(var filesetreader = new Library.Main.Volumes.FilesetVolumeReader(cm, new Duplicati.Library.Main.Options(options))) foreach(var f in filesetreader.Files) { if (f.Type != Duplicati.Library.Main.FilelistEntryType.File) continue; bool result; Library.Utility.IFilter evfilter; bool match = filter.Matches(f.Path, out result, out evfilter); if (!match || (match && result)) yield return f; } }
public MenuItemWrapper(string text, Duplicati.GUI.TrayIcon.MenuIcons icon, Action callback, IList<Duplicati.GUI.TrayIcon.IMenuItem> subitems) { if (text == "-") m_item = NSMenuItem.SeparatorItem; else { m_item = new NSMenuItem(text, ClickHandler); m_callback = callback; if (subitems != null && subitems.Count > 0) { m_item.Submenu = new NSMenu(); foreach(var itm in subitems) m_item.Submenu.AddItem(((MenuItemWrapper)itm).MenuItem); } } }
public static IRunnerData CreateListTask(Duplicati.Server.Serialization.Interface.IBackup backup, string[] filters, bool onlyPrefix, bool allVersions, bool folderContents, DateTime time) { var dict = new Dictionary<string, string>(); if (onlyPrefix) dict["list-prefix-only"] = "true"; if (allVersions) dict["all-versions"] = "true"; if (time.Ticks > 0) dict["time"] = Duplicati.Library.Utility.Utility.SerializeDateTime(time.ToUniversalTime()); if (folderContents) dict["list-folder-contents"] = "true"; return CreateTask( DuplicatiOperation.List, backup, dict, filters); }
/// <summary> /// Edit a Job /// </summary> /// <param name="aRow">Row to edit</param> public JobDialog(Duplicati.Scheduler.Data.SchedulerDataSet.JobsRow aRow) { InitializeComponent(); if (!string.IsNullOrEmpty(Properties.Settings.Default.TreeViewState)) this.folderSelectControl1.State = Properties.Settings.Default.TreeViewState; this.Text = "Edit job " + aRow.Name + "(" + Utility.User.UserName + ")"; // Dang background colors in tab pages - can't trust them. foreach (TabPage P in this.MainTabControl.TabPages) P.BackColor = this.BackColor; foreach (TabPage P in this.SourceTabControl.TabPages) P.BackColor = this.BackColor; this.folderSelectControl1.BackColor = this.BackColor; // Add radio buttons for backends; put the backend in the tag this.BackEndTableLayoutPanel.Controls.AddRange( (from Library.Interface.IBackend qB in Duplicati.Library.DynamicLoader.BackendLoader.Backends orderby qB.DisplayName select BackendRadioButton(qB)).ToArray()); // Put the tooltip text at the bottom of the screen for easy user viewage this.ExplainToolStripLabel.Text = this.MainTabControl.SelectedTab.ToolTipText; // And use the row SetRow(aRow); }
public FilterHandler(Snapshots.ISnapshotService snapshot, FileAttributes attributeFilter, Duplicati.Library.Utility.IFilter sourcefilter, Duplicati.Library.Utility.IFilter filter, Options.SymlinkStrategy symlinkPolicy, Options.HardlinkStrategy hardlinkPolicy, ILogWriter logWriter) { m_snapshot = snapshot; m_attributeFilter = attributeFilter; m_sourcefilter = sourcefilter; m_emitfilter = filter; m_symlinkPolicy = symlinkPolicy; m_hardlinkPolicy = hardlinkPolicy; m_logWriter = logWriter; m_hardlinkmap = new Dictionary<string, string>(); m_mixinqueue = new Queue<string>(); bool includes; bool excludes; Library.Utility.FilterExpression.AnalyzeFilters(filter, out includes, out excludes); if (includes && !excludes) { m_enumeratefilter = Library.Utility.FilterExpression.Combine(filter, new Duplicati.Library.Utility.FilterExpression("*" + System.IO.Path.DirectorySeparatorChar, true)); } else m_enumeratefilter = m_emitfilter; }
/// <summary> /// Enumerates all files and folders in the snapshot /// </summary> /// <param name="startpath">The path from which to retrieve files and folders</param> /// <param name="callback">The callback to invoke with each found path</param> public void EnumerateFilesAndFolders(string startpath, Duplicati.Library.Utility.Utility.EnumerationCallbackDelegate callback) { foreach (string s in m_sourcefolders) if (s.Equals(startpath, Utility.Utility.ClientFilenameStringComparision)) { Utility.Utility.EnumerateFileSystemEntries(s, callback, this.ListFolders, this.ListFiles, this.GetAttributes); return; } throw new InvalidOperationException(string.Format(Strings.Shared.InvalidEnumPathError, startpath)); }
private static bool TestIfOptionApplies(Duplicati.Server.Serialization.Interface.IBackup backup, DuplicatiOperation mode, string filter) { //TODO: Implement to avoid warnings return true; }
public static IRunnerData CreateRestoreTask(Duplicati.Server.Serialization.Interface.IBackup backup, string[] filters, DateTime time, string restoreTarget, bool overwrite, bool restore_permissions, bool skip_metadata) { var dict = new Dictionary<string, string>(); dict["time"] = Duplicati.Library.Utility.Utility.SerializeDateTime(time.ToUniversalTime()); if (!string.IsNullOrWhiteSpace(restoreTarget)) dict["restore-path"] = SpecialFolders.ExpandEnvironmentVariables(restoreTarget); if (overwrite) dict["overwrite"] = "true"; if (restore_permissions) dict["restore-permissions"] = "true"; if (skip_metadata) dict["skip-metadata"] = "true"; return CreateTask( DuplicatiOperation.Restore, backup, dict, filters); }
/// <summary> /// Enumerates all files and folders in the snapshot /// </summary> /// <param name="callback">The callback to invoke with each found path</param> public void EnumerateFilesAndFolders(Duplicati.Library.Utility.Utility.EnumerationCallbackDelegate callback) { foreach (string s in m_sourcefolders) Utility.Utility.EnumerateFileSystemEntries(s, callback, this.ListFolders, this.ListFiles, this.GetAttributes); }
public void BackendEvent(Duplicati.Library.Main.BackendActionType action, Duplicati.Library.Main.BackendEventType type, string path, long size) { lock(m_lock) { m_state.m_backendAction = action; m_state.m_backendPath = path; if (type == Duplicati.Library.Main.BackendEventType.Started) m_state.m_backendFileSize = size; else if (type == Duplicati.Library.Main.BackendEventType.Progress) m_state.m_backendFileProgress = size; else { m_state.m_backendFileSize = 0; m_state.m_backendFileProgress = 0; m_state.m_backendSpeed = 0; } } }
public FilterHandler(Snapshots.ISnapshotService snapshot, FileAttributes attributeFilter, Duplicati.Library.Utility.IFilter sourcefilter, Duplicati.Library.Utility.IFilter filter, Options.SymlinkStrategy symlinkPolicy, Options.HardlinkStrategy hardlinkPolicy, ILogWriter logWriter) { m_snapshot = snapshot; m_attributeFilter = attributeFilter; m_sourcefilter = sourcefilter; m_filter = filter; m_symlinkPolicy = symlinkPolicy; m_hardlinkPolicy = hardlinkPolicy; m_logWriter = logWriter; m_hardlinkmap = new Dictionary<string, string>(); }
public BackupEntryBase ParseFilename(Duplicati.Library.Interface.IFileEntry fe) { bool oldFilename = false; Match m = m_filenameRegExp.Match(fe.Name); if (!m.Success) m = m_shortRegExp.Match(fe.Name); if (!m.Success) { m = m_oldFilenameRegExp.Match(fe.Name); oldFilename = true; } if (!m.Success) { m = m_verificationRegExp.Match(fe.Name); if (m.Success && m.Value == fe.Name) { DateTime verificationtime = DateTime.ParseExact(m.Groups["time"].Value, TIMESTAMP_FORMAT, System.Globalization.CultureInfo.InvariantCulture).ToLocalTime(); return new VerificationEntry(fe.Name, fe, verificationtime, m.Groups["time"].Value); } m = m_deleteTransactionRegExp.Match(fe.Name); if (m.Success && m.Value == fe.Name) { return new DeleteTransactionEntry(fe, m.Groups["encryption"].Value); } } if (!m.Success) return null; if (m.Value != fe.Name) return null; //Accept only full matches bool isFull = m.Groups["inc"].Value == FULL || m.Groups["inc"].Value == FULL_SHORT; bool isShortName = m.Groups["inc"].Value.Length == 1; string timeString = m.Groups["time"].Value; DateTime time; if (isShortName) time = new DateTime(long.Parse(timeString, System.Globalization.NumberStyles.HexNumber) * TimeSpan.TicksPerSecond, DateTimeKind.Utc).ToLocalTime(); else { if (oldFilename) time = DateTime.Parse(timeString.Replace(m.Groups["timeseparator"].Value, ":")); else time = DateTime.ParseExact(timeString, TIMESTAMP_FORMAT, System.Globalization.CultureInfo.InvariantCulture).ToLocalTime(); } string extension = m.Groups["extension"].Value; int volNumber = -1; if (m.Groups["volumenumber"].Success) volNumber = int.Parse(m.Groups["volumenumber"].Value); else if (m.Groups["extension"].Success && m.Groups["extension"].Value.StartsWith(VOLUME)) { //The extension must be present, but the volumenumber is optional, so the greedy regexp takes it if (!int.TryParse(m.Groups["extension"].Value.Substring(VOLUME.Length), out volNumber)) volNumber = -1; } string compression = extension; string encryption = null; int dotIndex = compression.IndexOf("."); if (dotIndex > 0) { encryption = compression.Substring(dotIndex + 1); compression = compression.Substring(0, dotIndex); } if (Array.IndexOf<string>(new string[] { MANIFEST_OLD , MANIFEST_OLD_SHORT ,MANIFEST_A , MANIFEST_A_SHORT, MANIFEST_B, MANIFEST_B_SHORT }, m.Groups["type"].Value) >= 0) return new ManifestEntry(fe.Name, fe, time, isFull, timeString, encryption, m.Groups["type"].Value != MANIFEST_B && m.Groups["type"].Value != MANIFEST_B_SHORT); else if (m.Groups["type"].Value == SIGNATURE || m.Groups["type"].Value == SIGNATURE_SHORT) return new SignatureEntry(fe.Name, fe, time, isFull, timeString, encryption, compression, volNumber); else if (m.Groups["type"].Value == CONTENT || m.Groups["type"].Value == CONTENT_SHORT) return new ContentEntry(fe.Name, fe, time, isFull, timeString, encryption, compression, volNumber); else return null; }
private static void UpdateMetadataError(Duplicati.Server.Serialization.Interface.IBackup backup, Exception ex) { backup.Metadata["LastErrorDate"] = Library.Utility.Utility.SerializeDateTime(DateTime.UtcNow); backup.Metadata["LastErrorMessage"] = ex.Message; if (!backup.IsTemporary) Program.DataConnection.SetMetadata(backup.Metadata, long.Parse(backup.ID), null); System.Threading.Interlocked.Increment(ref Program.LastDataUpdateID); Program.DataConnection.RegisterNotification( NotificationType.Error, backup.IsTemporary ? "Error" : string.Format("Error while running {0}", backup.Name), ex.Message, ex, backup.ID, "backup:show-log", (n, a) => { return a.Where(x => x.BackupID == backup.ID).FirstOrDefault() ?? n; } ); }
/// <summary> /// Returns a localized name for a task type /// </summary> /// <param name="type"></param> /// <returns></returns> public static string LocalizeTaskType(Duplicati.Server.Serialization.DuplicatiOperation type) { switch (type) { case Duplicati.Server.Serialization.DuplicatiOperation.Backup: return Strings.TaskType.FullBackup; case Duplicati.Server.Serialization.DuplicatiOperation.List: return Strings.TaskType.IncrementalBackup; case Duplicati.Server.Serialization.DuplicatiOperation.Remove: return Strings.TaskType.ListActualFiles; case Duplicati.Server.Serialization.DuplicatiOperation.Verify: return Strings.TaskType.ListBackupEntries; case Duplicati.Server.Serialization.DuplicatiOperation.Restore: return Strings.TaskType.ListBackups; default: return type.ToString(); } }
protected override Duplicati.GUI.TrayIcon.IMenuItem CreateMenuItem (string text, Duplicati.GUI.TrayIcon.MenuIcons icon, Action callback, System.Collections.Generic.IList<Duplicati.GUI.TrayIcon.IMenuItem> subitems) { return new MenuItemWrapper(this, text, icon, callback, subitems); }
/// <summary> /// Enumerates all files and folders in the snapshot /// </summary> /// <param name="filter">The filter to apply when evaluating files and folders</param> /// <param name="callback">The callback to invoke with each found path</param> public IEnumerable<string> EnumerateFilesAndFolders(Duplicati.Library.Utility.Utility.EnumerationFilterDelegate callback) { return m_entries.SelectMany( s => Utility.Utility.EnumerateFileSystemEntries(s.Key, callback, this.ListFolders, this.ListFiles, this.GetAttributes) ); }
private static Duplicati.Library.Utility.IFilter GetCommonFilter(Duplicati.Server.Serialization.Interface.IBackup backup, DuplicatiOperation mode) { var filters = Program.DataConnection.Filters; if (filters == null || filters.Length == 0) return null; return (from n in filters orderby n.Order let exp = Library.Utility.Utility.ExpandEnvironmentVariables(n.Expression) select (Duplicati.Library.Utility.IFilter)(new Duplicati.Library.Utility.FilterExpression(exp, n.Include))) .Aggregate((a, b) => Duplicati.Library.Utility.FilterExpression.Combine(a, b)); }
private static Dictionary<string, string> ApplyOptions(Duplicati.Server.Serialization.Interface.IBackup backup, DuplicatiOperation mode, Dictionary<string, string> options) { options["backup-name"] = backup.Name; options["dbpath"] = backup.DBPath; // Apply normal options foreach(var o in backup.Settings) if (!o.Name.StartsWith("--") && TestIfOptionApplies(backup, mode, o.Filter)) options[o.Name] = o.Value; // Apply override options foreach(var o in backup.Settings) if (o.Name.StartsWith("--") && TestIfOptionApplies(backup, mode, o.Filter)) options[o.Name.Substring(2)] = o.Value; // The server hangs if the module is enabled as there is no console attached DisableModule("console-password-input", options); return options; }
private static Duplicati.Library.Utility.IFilter ApplyFilter(Duplicati.Server.Serialization.Interface.IBackup backup, DuplicatiOperation mode, Duplicati.Library.Utility.IFilter filter) { var f2 = backup.Filters; if (f2 != null && f2.Length > 0) { var nf = (from n in f2 let exp = n.Expression.StartsWith("[") && n.Expression.EndsWith("]") ? SpecialFolders.ExpandEnvironmentVariablesRegexp(n.Expression) : SpecialFolders.ExpandEnvironmentVariables(n.Expression) orderby n.Order select (Duplicati.Library.Utility.IFilter)(new Duplicati.Library.Utility.FilterExpression(exp, n.Include))) .Aggregate((a, b) => Duplicati.Library.Utility.FilterExpression.Combine(a, b)); return Duplicati.Library.Utility.FilterExpression.Combine(filter, nf); } else return filter; }
public static IRunnerData CreateTask(Duplicati.Server.Serialization.DuplicatiOperation operation, Duplicati.Server.Serialization.Interface.IBackup backup, IDictionary<string, string> extraOptions = null, string[] filterStrings = null) { return new RunnerData() { Operation = operation, Backup = backup, ExtraOptions = extraOptions, FilterStrings = filterStrings }; }
private string GetIcon(Duplicati.GUI.TrayIcon.TrayIcons icon) { if (!m_images.ContainsKey(icon)) { switch(icon) { case Duplicati.GUI.TrayIcon.TrayIcons.IdleError: m_images[icon] = LoadStream(ASSEMBLY.GetManifestResourceStream(ICON_ERROR)); break; case Duplicati.GUI.TrayIcon.TrayIcons.Paused: m_images[icon] = LoadStream(ASSEMBLY.GetManifestResourceStream(ICON_PAUSED)); break; case Duplicati.GUI.TrayIcon.TrayIcons.PausedError: m_images[icon] = LoadStream(ASSEMBLY.GetManifestResourceStream(ICON_PAUSED)); break; case Duplicati.GUI.TrayIcon.TrayIcons.Running: m_images[icon] = LoadStream(ASSEMBLY.GetManifestResourceStream(ICON_RUNNING)); break; case Duplicati.GUI.TrayIcon.TrayIcons.RunningError: m_images[icon] = LoadStream(ASSEMBLY.GetManifestResourceStream(ICON_RUNNING)); break; case Duplicati.GUI.TrayIcon.TrayIcons.Idle: default: m_images[icon] = LoadStream(ASSEMBLY.GetManifestResourceStream(ICON_NORMAL)); break; } } return m_images[icon]; }
private static void UpdateMetadata(Duplicati.Server.Serialization.Interface.IBackup backup, Duplicati.Library.Interface.IParsedBackendStatistics r) { if (r != null) { backup.Metadata["LastBackupDate"] = Library.Utility.Utility.SerializeDateTime(r.LastBackupDate.ToUniversalTime()); backup.Metadata["BackupListCount"] = r.BackupListCount.ToString(); backup.Metadata["TotalQuotaSpace"] = r.TotalQuotaSpace.ToString(); backup.Metadata["FreeQuotaSpace"] = r.FreeQuotaSpace.ToString(); backup.Metadata["AssignedQuotaSpace"] = r.AssignedQuotaSpace.ToString(); backup.Metadata["TargetFilesSize"] = r.KnownFileSize.ToString(); backup.Metadata["TargetFilesCount"] = r.KnownFileCount.ToString(); backup.Metadata["TargetSizeString"] = Duplicati.Library.Utility.Utility.FormatSizeString(r.KnownFileSize); } }
private static Dictionary<string, string> GetCommonOptions(Duplicati.Server.Serialization.Interface.IBackup backup, DuplicatiOperation mode) { return (from n in Program.DataConnection.Settings where TestIfOptionApplies(backup, mode, n.Filter) select n).ToDictionary(k => k.Name.StartsWith("--") ? k.Name.Substring(2) : k.Name, k => k.Value); }
private static void UpdateMetadata(Duplicati.Server.Serialization.Interface.IBackup backup, object o) { if (o is Duplicati.Library.Interface.IBasicResults) { var r = (Duplicati.Library.Interface.IBasicResults)o; backup.Metadata["LastDuration"] = r.Duration.ToString(); backup.Metadata["LastStarted"] = Library.Utility.Utility.SerializeDateTime(((Duplicati.Library.Interface.IBasicResults)o).BeginTime.ToUniversalTime()); backup.Metadata["LastFinished"] = Library.Utility.Utility.SerializeDateTime(((Duplicati.Library.Interface.IBasicResults)o).EndTime.ToUniversalTime()); } if (o is Duplicati.Library.Interface.IParsedBackendStatistics) { var r = (Duplicati.Library.Interface.IParsedBackendStatistics)o; UpdateMetadata(backup, r); } if (o is Duplicati.Library.Interface.IBackupResults) { var r = (Duplicati.Library.Interface.IBackupResults)o; backup.Metadata["SourceFilesSize"] = r.SizeOfExaminedFiles.ToString(); backup.Metadata["SourceFilesCount"] = r.ExaminedFiles.ToString(); backup.Metadata["SourceSizeString"] = Duplicati.Library.Utility.Utility.FormatSizeString(r.SizeOfExaminedFiles); backup.Metadata["LastBackupStarted"] = Library.Utility.Utility.SerializeDateTime(((Duplicati.Library.Interface.IBasicResults)o).BeginTime.ToUniversalTime()); backup.Metadata["LastBackupFinished"] = Library.Utility.Utility.SerializeDateTime(((Duplicati.Library.Interface.IBasicResults)o).EndTime.ToUniversalTime()); if (r.BackendStatistics is Duplicati.Library.Interface.IParsedBackendStatistics) UpdateMetadata(backup, (Duplicati.Library.Interface.IParsedBackendStatistics)r.BackendStatistics); if (r.FilesWithError > 0 || r.Warnings.Any()) { Program.DataConnection.RegisterNotification( NotificationType.Error, backup.IsTemporary ? "Warning" : string.Format("Warning while running {0}", backup.Name), r.FilesWithError > 0 ? string.Format("Errors affected {0} file(s) ", r.FilesWithError) : string.Format("Got {0} warning(s) ", r.Warnings.Count()) , null, backup.ID, "backup:show-log", (n, a) => { var existing = (a.Where(x => x.BackupID == backup.ID)).FirstOrDefault(); if (existing == null) return n; if (existing.Type == NotificationType.Error) return existing; return n; } ); } } if (!backup.IsTemporary) Program.DataConnection.SetMetadata(backup.Metadata, long.Parse(backup.ID), null); System.Threading.Interlocked.Increment(ref Program.LastDataUpdateID); Program.StatusEventNotifyer.SignalNewEvent(); }