├── .gitignore
├── LICENSE
├── README.md
├── RoboBackup.sln
├── RoboBackupGUI
├── AboutForm.Designer.cs
├── AboutForm.cs
├── DirectoryTree.cs
├── IpcClient.cs
├── Lang.cs
├── MainForm.Designer.cs
├── MainForm.cs
├── OpenFolderDialog.cs
├── Program.cs
├── Properties
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ └── Resources.resx
├── RoboBackup.ico
├── RoboBackupGUI.csproj
├── SettingsForm.Designer.cs
├── SettingsForm.cs
├── TaskForm.Designer.cs
├── TaskForm.cs
└── lang
│ ├── cs.txt
│ └── en.txt
├── RoboBackupService
├── Config.cs
├── Credential.cs
├── IpcServer.cs
├── IpcService.cs
├── LogCleaner.cs
├── Logger.cs
├── Program.cs
├── ProjectInstaller.Designer.cs
├── ProjectInstaller.cs
├── Properties
│ └── AssemblyInfo.cs
├── RoboBackupService.csproj
├── Service.Designer.cs
├── Service.cs
├── SysUtils.cs
├── Task.cs
└── Unc.cs
└── Setup
└── Setup.vdproj
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 | [Ll]og/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | project.fragment.lock.json
46 | artifacts/
47 |
48 | *_i.c
49 | *_p.c
50 | *_i.h
51 | *.ilk
52 | *.meta
53 | *.obj
54 | *.pch
55 | *.pdb
56 | *.pgc
57 | *.pgd
58 | *.rsp
59 | *.sbr
60 | *.tlb
61 | *.tli
62 | *.tlh
63 | *.tmp
64 | *.tmp_proj
65 | *.log
66 | *.vspscc
67 | *.vssscc
68 | .builds
69 | *.pidb
70 | *.svclog
71 | *.scc
72 |
73 | # Chutzpah Test files
74 | _Chutzpah*
75 |
76 | # Visual C++ cache files
77 | ipch/
78 | *.aps
79 | *.ncb
80 | *.opendb
81 | *.opensdf
82 | *.sdf
83 | *.cachefile
84 | *.VC.db
85 | *.VC.VC.opendb
86 |
87 | # Visual Studio profiler
88 | *.psess
89 | *.vsp
90 | *.vspx
91 | *.sap
92 |
93 | # TFS 2012 Local Workspace
94 | $tf/
95 |
96 | # Guidance Automation Toolkit
97 | *.gpState
98 |
99 | # ReSharper is a .NET coding add-in
100 | _ReSharper*/
101 | *.[Rr]e[Ss]harper
102 | *.DotSettings.user
103 |
104 | # JustCode is a .NET coding add-in
105 | .JustCode
106 |
107 | # TeamCity is a build add-in
108 | _TeamCity*
109 |
110 | # DotCover is a Code Coverage Tool
111 | *.dotCover
112 |
113 | # NCrunch
114 | _NCrunch_*
115 | .*crunch*.local.xml
116 | nCrunchTemp_*
117 |
118 | # MightyMoose
119 | *.mm.*
120 | AutoTest.Net/
121 |
122 | # Web workbench (sass)
123 | .sass-cache/
124 |
125 | # Installshield output folder
126 | [Ee]xpress/
127 |
128 | # DocProject is a documentation generator add-in
129 | DocProject/buildhelp/
130 | DocProject/Help/*.HxT
131 | DocProject/Help/*.HxC
132 | DocProject/Help/*.hhc
133 | DocProject/Help/*.hhk
134 | DocProject/Help/*.hhp
135 | DocProject/Help/Html2
136 | DocProject/Help/html
137 |
138 | # Click-Once directory
139 | publish/
140 |
141 | # Publish Web Output
142 | *.[Pp]ublish.xml
143 | *.azurePubxml
144 | # TODO: Comment the next line if you want to checkin your web deploy settings
145 | # but database connection strings (with potential passwords) will be unencrypted
146 | #*.pubxml
147 | *.publishproj
148 |
149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
150 | # checkin your Azure Web App publish settings, but sensitive information contained
151 | # in these scripts will be unencrypted
152 | PublishScripts/
153 |
154 | # NuGet Packages
155 | *.nupkg
156 | # The packages folder can be ignored because of Package Restore
157 | **/packages/*
158 | # except build/, which is used as an MSBuild target.
159 | !**/packages/build/
160 | # Uncomment if necessary however generally it will be regenerated when needed
161 | #!**/packages/repositories.config
162 | # NuGet v3's project.json files produces more ignoreable files
163 | *.nuget.props
164 | *.nuget.targets
165 |
166 | # Microsoft Azure Build Output
167 | csx/
168 | *.build.csdef
169 |
170 | # Microsoft Azure Emulator
171 | ecf/
172 | rcf/
173 |
174 | # Windows Store app package directories and files
175 | AppPackages/
176 | BundleArtifacts/
177 | Package.StoreAssociation.xml
178 | _pkginfo.txt
179 |
180 | # Visual Studio cache files
181 | # files ending in .cache can be ignored
182 | *.[Cc]ache
183 | # but keep track of directories ending in .cache
184 | !*.[Cc]ache/
185 |
186 | # Others
187 | ClientBin/
188 | ~$*
189 | *~
190 | *.dbmdl
191 | *.dbproj.schemaview
192 | *.jfm
193 | *.pfx
194 | *.publishsettings
195 | node_modules/
196 | orleans.codegen.cs
197 |
198 | # Since there are multiple workflows, uncomment next line to ignore bower_components
199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
200 | #bower_components/
201 |
202 | # RIA/Silverlight projects
203 | Generated_Code/
204 |
205 | # Backup & report files from converting an old project file
206 | # to a newer Visual Studio version. Backup files are not needed,
207 | # because we have git ;-)
208 | _UpgradeReport_Files/
209 | Backup*/
210 | UpgradeLog*.XML
211 | UpgradeLog*.htm
212 |
213 | # SQL Server files
214 | *.mdf
215 | *.ldf
216 |
217 | # Business Intelligence projects
218 | *.rdl.data
219 | *.bim.layout
220 | *.bim_*.settings
221 |
222 | # Microsoft Fakes
223 | FakesAssemblies/
224 |
225 | # GhostDoc plugin setting file
226 | *.GhostDoc.xml
227 |
228 | # Node.js Tools for Visual Studio
229 | .ntvs_analysis.dat
230 |
231 | # Visual Studio 6 build log
232 | *.plg
233 |
234 | # Visual Studio 6 workspace options file
235 | *.opt
236 |
237 | # Visual Studio LightSwitch build output
238 | **/*.HTMLClient/GeneratedArtifacts
239 | **/*.DesktopClient/GeneratedArtifacts
240 | **/*.DesktopClient/ModelManifest.xml
241 | **/*.Server/GeneratedArtifacts
242 | **/*.Server/ModelManifest.xml
243 | _Pvt_Extensions
244 |
245 | # Paket dependency manager
246 | .paket/paket.exe
247 | paket-files/
248 |
249 | # FAKE - F# Make
250 | .fake/
251 |
252 | # JetBrains Rider
253 | .idea/
254 | *.sln.iml
255 |
256 | # CodeRush
257 | .cr/
258 |
259 | # Python Tools for Visual Studio (PTVS)
260 | __pycache__/
261 | *.pyc
262 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Description
2 |
3 | *RoboBackup* is a Windows service for orchestrating file backups via `robocopy` utility. The GUI aims to provide user-friendly interface for the backup tasks management without the need to understand the `robocopy` utility itself. The service runs with *Local System* privileges, allowing it to work independently on the GUI user or on any interactive session. The service manages scheduling, logging and retention of the backups.
4 |
5 | The goal of *RoboBackup* is not to be robust, secure and enterprise-ready, but to be small, simple and lower the difficulty of creating and scheduling `robocopy` commands.
6 |
7 | ## Usage
8 |
9 | Backups can be created in 3 modes:
10 |
11 | - **Incremental** means that there is a single destination directory. When the backup runs, files which are new or modified in the source directory are copied to the destination directory. Files which were deleted from the source directory are kept (i.e. not deleted) in the destination directory.
12 | - **Differential** creates timestamped subdirectories in `yyyy-MM-dd_HH-mm-ss` format under the destination directory for each backup run. Files are hardlinked from the subdirectory of the previous backup (if there is one) and then only the differences between the source directory and the previous backup are synchronized. This feature speeds up the whole backup process and reduces required filesystem space. In linux world, similar effect can be achieved using `rsync` tool with `--link-dest` parameter.
13 | - **Full** also creates timestamped subdirectories under the destination directory for each backup run, but the files are always fully copied from the source directory with no regard to the previous backups.
14 |
15 | Backup retention is set as a number of timestamped directories with previous backups. When the retention limit is exceeded, excessive subdirectories with the oldest timestamps are deleted. With retention set to 1, differential and full methods behave the same. They do not create timestamped directories and only differentially synchronize the contents of the source directory to the destination directory.
16 |
17 | **Note about network shares:** By default, Windows map drives only for interactive user sessions and open the mapping only when the drive is first used. This means that the mapped drives are not visible for the service account. GUI automatically resolves the mapped drives to UNC network paths, which *are* reachable for the service, but still requires you to enter the network credentials, so the service can open the resource to successfully run the backup. Both source and destination can be entered as UNC network paths and the service figures out when to use the credentials automatically, however they should not be network shares *both at the same time*.
18 |
19 | ## Acknowledgments
20 | - Toolbar icons are [Mark James' Silk icons](http://famfamfam.com/lab/icons/silk/), licensed under [CC BY 3.0](https://creativecommons.org/licenses/by/3.0/).
21 | - Main form icon is OpenIcon's one from [Pixabay](https://pixabay.com/en/hard-drive-disk-saving-data-add-97582/), licensed under [CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/).
22 | - [Simon Mourier](https://stackoverflow.com/a/15386992) for most of the folder selection dialog.
23 | - [PInvoke.net](https://pinvoke.net/) and [Microsoft](https://docs.microsoft.com/en-us/windows/desktop/api/index) for references on black magic with unmanaged API.
24 | - The idea for the tool is loosely based on [Create Synchronicity](https://sourceforge.net/projects/synchronicity/).
25 |
--------------------------------------------------------------------------------
/RoboBackup.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.26228.9
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RoboBackupGUI", "RoboBackupGUI\RoboBackupGUI.csproj", "{7767BF21-EE40-448B-90ED-179A17E320CC}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RoboBackupService", "RoboBackupService\RoboBackupService.csproj", "{43C4F764-ABBD-46A5-A184-C24DA8D7B203}"
9 | EndProject
10 | Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "Setup", "Setup\Setup.vdproj", "{67B792FA-B73D-4763-8030-82E09091B380}"
11 | EndProject
12 | Global
13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
14 | Debug|Any CPU = Debug|Any CPU
15 | Release|Any CPU = Release|Any CPU
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {7767BF21-EE40-448B-90ED-179A17E320CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19 | {7767BF21-EE40-448B-90ED-179A17E320CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
20 | {7767BF21-EE40-448B-90ED-179A17E320CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
21 | {7767BF21-EE40-448B-90ED-179A17E320CC}.Release|Any CPU.Build.0 = Release|Any CPU
22 | {43C4F764-ABBD-46A5-A184-C24DA8D7B203}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {43C4F764-ABBD-46A5-A184-C24DA8D7B203}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {43C4F764-ABBD-46A5-A184-C24DA8D7B203}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {43C4F764-ABBD-46A5-A184-C24DA8D7B203}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {67B792FA-B73D-4763-8030-82E09091B380}.Debug|Any CPU.ActiveCfg = Debug
27 | {67B792FA-B73D-4763-8030-82E09091B380}.Release|Any CPU.ActiveCfg = Release
28 | EndGlobalSection
29 | GlobalSection(SolutionProperties) = preSolution
30 | HideSolutionNode = FALSE
31 | EndGlobalSection
32 | GlobalSection(ExtensibilityGlobals) = postSolution
33 | SolutionGuid = {B4D7F289-357D-443E-A9F6-8DEA052502AA}
34 | EndGlobalSection
35 | EndGlobal
36 |
--------------------------------------------------------------------------------
/RoboBackupGUI/AboutForm.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace RoboBackup {
2 | partial class AboutForm {
3 | private System.ComponentModel.IContainer components = null;
4 |
5 | protected override void Dispose(bool disposing) {
6 | if (disposing && (components != null)) {
7 | components.Dispose();
8 | }
9 | base.Dispose(disposing);
10 | }
11 |
12 | #region Windows Form Designer generated code
13 |
14 | ///
15 | /// Required method for Designer support - do not modify
16 | /// the contents of this method with the code editor.
17 | ///
18 | private void InitializeComponent() {
19 | this.aboutNameLabel = new System.Windows.Forms.Label();
20 | this.aboutVersionLabel = new System.Windows.Forms.Label();
21 | this.aboutAuthorLabel = new System.Windows.Forms.Label();
22 | this.aboutSourceCodeLabel = new System.Windows.Forms.Label();
23 | this.sourceCodeLinkLabel = new System.Windows.Forms.LinkLabel();
24 | this.authorLabel = new System.Windows.Forms.Label();
25 | this.versionLabel = new System.Windows.Forms.Label();
26 | this.SuspendLayout();
27 | //
28 | // aboutNameLabel
29 | //
30 | this.aboutNameLabel.AutoSize = true;
31 | this.aboutNameLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 24F, System.Drawing.FontStyle.Bold);
32 | this.aboutNameLabel.ForeColor = System.Drawing.Color.Green;
33 | this.aboutNameLabel.Location = new System.Drawing.Point(12, 9);
34 | this.aboutNameLabel.Name = "aboutNameLabel";
35 | this.aboutNameLabel.Size = new System.Drawing.Size(210, 37);
36 | this.aboutNameLabel.TabIndex = 0;
37 | this.aboutNameLabel.Text = "RoboBackup";
38 | //
39 | // aboutVersionLabel
40 | //
41 | this.aboutVersionLabel.AutoSize = true;
42 | this.aboutVersionLabel.Location = new System.Drawing.Point(16, 56);
43 | this.aboutVersionLabel.Name = "aboutVersionLabel";
44 | this.aboutVersionLabel.Size = new System.Drawing.Size(45, 13);
45 | this.aboutVersionLabel.TabIndex = 1;
46 | this.aboutVersionLabel.Text = "Version:";
47 | //
48 | // aboutAuthorLabel
49 | //
50 | this.aboutAuthorLabel.AutoSize = true;
51 | this.aboutAuthorLabel.Location = new System.Drawing.Point(16, 82);
52 | this.aboutAuthorLabel.Name = "aboutAuthorLabel";
53 | this.aboutAuthorLabel.Size = new System.Drawing.Size(41, 13);
54 | this.aboutAuthorLabel.TabIndex = 3;
55 | this.aboutAuthorLabel.Text = "Author:";
56 | //
57 | // aboutSourceCodeLabel
58 | //
59 | this.aboutSourceCodeLabel.AutoSize = true;
60 | this.aboutSourceCodeLabel.Location = new System.Drawing.Point(16, 108);
61 | this.aboutSourceCodeLabel.Name = "aboutSourceCodeLabel";
62 | this.aboutSourceCodeLabel.Size = new System.Drawing.Size(69, 13);
63 | this.aboutSourceCodeLabel.TabIndex = 5;
64 | this.aboutSourceCodeLabel.Text = "SourceCode:";
65 | //
66 | // sourceCodeLinkLabel
67 | //
68 | this.sourceCodeLinkLabel.AutoSize = true;
69 | this.sourceCodeLinkLabel.Location = new System.Drawing.Point(93, 108);
70 | this.sourceCodeLinkLabel.Name = "sourceCodeLinkLabel";
71 | this.sourceCodeLinkLabel.Size = new System.Drawing.Size(236, 13);
72 | this.sourceCodeLinkLabel.TabIndex = 6;
73 | this.sourceCodeLinkLabel.TabStop = true;
74 | this.sourceCodeLinkLabel.Text = "https://github.com/Disassembler0/RoboBackup";
75 | this.sourceCodeLinkLabel.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.SourceCodeLinkLabel_LinkClicked);
76 | //
77 | // authorLabel
78 | //
79 | this.authorLabel.AutoSize = true;
80 | this.authorLabel.Location = new System.Drawing.Point(93, 82);
81 | this.authorLabel.Name = "authorLabel";
82 | this.authorLabel.Size = new System.Drawing.Size(194, 13);
83 | this.authorLabel.TabIndex = 4;
84 | this.authorLabel.Text = "Disassembler ";
85 | //
86 | // versionLabel
87 | //
88 | this.versionLabel.AutoSize = true;
89 | this.versionLabel.Location = new System.Drawing.Point(93, 56);
90 | this.versionLabel.Name = "versionLabel";
91 | this.versionLabel.Size = new System.Drawing.Size(40, 13);
92 | this.versionLabel.TabIndex = 2;
93 | this.versionLabel.Text = "0.0.0.0";
94 | //
95 | // AboutForm
96 | //
97 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
98 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
99 | this.ClientSize = new System.Drawing.Size(344, 138);
100 | this.Controls.Add(this.versionLabel);
101 | this.Controls.Add(this.authorLabel);
102 | this.Controls.Add(this.sourceCodeLinkLabel);
103 | this.Controls.Add(this.aboutSourceCodeLabel);
104 | this.Controls.Add(this.aboutAuthorLabel);
105 | this.Controls.Add(this.aboutVersionLabel);
106 | this.Controls.Add(this.aboutNameLabel);
107 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
108 | this.MaximizeBox = false;
109 | this.MinimizeBox = false;
110 | this.Name = "AboutForm";
111 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
112 | this.Text = "About";
113 | this.ResumeLayout(false);
114 | this.PerformLayout();
115 |
116 | }
117 |
118 | #endregion
119 |
120 | private System.Windows.Forms.Label aboutNameLabel;
121 | private System.Windows.Forms.Label aboutVersionLabel;
122 | private System.Windows.Forms.Label aboutAuthorLabel;
123 | private System.Windows.Forms.Label aboutSourceCodeLabel;
124 | private System.Windows.Forms.LinkLabel sourceCodeLinkLabel;
125 | private System.Windows.Forms.Label authorLabel;
126 | private System.Windows.Forms.Label versionLabel;
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/RoboBackupGUI/AboutForm.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.Reflection;
3 | using System.Windows.Forms;
4 |
5 | namespace RoboBackup {
6 | ///
7 | /// Form with brief information about the RoboBackup application and its author.
8 | ///
9 | public partial class AboutForm : Form {
10 | ///
11 | /// Initializes a new instance of the class.
12 | ///
13 | public AboutForm() {
14 | InitializeComponent();
15 | Localize();
16 | versionLabel.Text = Assembly.GetExecutingAssembly().GetName().Version.ToString(2);
17 | }
18 |
19 | ///
20 | /// Translates the form strings to the language set by
21 | ///
22 | private void Localize() {
23 | Text = Lang.Get("About");
24 | aboutVersionLabel.Text = Lang.Get("Version", ":");
25 | aboutAuthorLabel.Text = Lang.Get("Author", ":");
26 | aboutSourceCodeLabel.Text = Lang.Get("SourceCode", ":");
27 | }
28 |
29 | ///
30 | /// event handler. Opens a GitHub link in default web browser.
31 | ///
32 | private void SourceCodeLinkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) {
33 | Process.Start(sourceCodeLinkLabel.Text);
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/RoboBackupGUI/DirectoryTree.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 | using System.Windows.Forms;
4 |
5 | namespace RoboBackup {
6 | ///
7 | /// Node traversal helper class for 's object instance.
8 | ///
9 | public static class DirectoryTree {
10 | ///
11 | /// Locates and returns with given full path within a .
12 | ///
13 | /// to search in.
14 | /// Full node path of the searched .
15 | /// with given full path
16 | public static TreeNode FindNode(TreeNodeCollection nodes, string fullPath) {
17 | foreach (TreeNode child in nodes) {
18 | if (child.FullPath == fullPath) {
19 | return child;
20 | }
21 | TreeNode match = FindNode(child.Nodes, fullPath);
22 | if (match != null) {
23 | return match;
24 | }
25 | }
26 | return null;
27 | }
28 |
29 | ///
30 | /// Locates and returns collection of nodes with unchecked checkbox within a .
31 | ///
32 | /// to search in.
33 | /// of s with unchecked checkbox.
34 | public static List GetExcludedNodes(TreeNodeCollection nodes) {
35 | List list = new List();
36 | foreach (TreeNode child in nodes) {
37 | if (!child.Checked) {
38 | list.Add(child);
39 | } else {
40 | list.AddRange(GetExcludedNodes(child.Nodes));
41 | }
42 | }
43 | return list;
44 | }
45 |
46 | ///
47 | /// Traverses given filesystem path and creates an array of s corresponding to filesystem objects under that path.
48 | ///
49 | /// Filesystem path to traverse.
50 | /// Array of s corresponding to filesystem objects found under given path
51 | public static TreeNode[] LoadNodes(string path) {
52 | List nodes = new List();
53 | IEnumerable directories = new string[] { };
54 | IEnumerable files = new string[] { };
55 | try {
56 | directories = Directory.EnumerateDirectories(path);
57 | files = Directory.EnumerateFiles(path);
58 | } catch { /* Ignore if the subdirectory is inaccessible */ }
59 | foreach (string dir in directories) {
60 | TreeNode node = new TreeNode(Path.GetFileName(dir)) { Checked = true, Tag = EntryType.Directory };
61 | node.Nodes.AddRange(LoadNodes(dir));
62 | nodes.Add(node);
63 | }
64 | foreach (string file in files) {
65 | nodes.Add(new TreeNode(Path.GetFileName(file)) { Checked = true, Tag = EntryType.File });
66 | }
67 | return nodes.ToArray();
68 | }
69 |
70 | ///
71 | /// Checks or unchecks checkboxes for all of the given .
72 | ///
73 | /// to descend to.
74 | /// state to set.
75 | public static void ToggleChildren(TreeNode node, bool check) {
76 | foreach (TreeNode child in node.Nodes) {
77 | child.Checked = check;
78 | ToggleChildren(child, check);
79 | }
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/RoboBackupGUI/IpcClient.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.Remoting;
2 | using System.Runtime.Remoting.Channels;
3 | using System.Runtime.Remoting.Channels.Ipc;
4 |
5 | namespace RoboBackup {
6 | ///
7 | /// Client side for interprocess communication via named pipe between the GUI (client) and the service (server).
8 | ///
9 | public static class IpcClient {
10 | ///
11 | /// Interprocess communication client channel.
12 | ///
13 | private static IpcClientChannel _channel;
14 |
15 | ///
16 | /// Interprocess communication object proxy.
17 | ///
18 | private static IpcService _service;
19 |
20 | ///
21 | /// Tests connection via IPC and return object proxy.
22 | ///
23 | /// object proxy.
24 | public static IpcService GetService() {
25 | try {
26 | _service.Ping();
27 | } catch {
28 | RegisterIpcClient();
29 | }
30 | return _service;
31 | }
32 |
33 | ///
34 | /// Registers interprocess communication client channel and the object proxy.
35 | ///
36 | private static void RegisterIpcClient() {
37 | // Unregister the channel if it was previously registered but the underlying pipe has been closed (e.g. when the service was restarted but the GUI wasn't).
38 | if (_channel != null) {
39 | ChannelServices.UnregisterChannel(_channel);
40 | }
41 | _channel = new IpcClientChannel();
42 | ChannelServices.RegisterChannel(_channel, false);
43 | if (_service == null) {
44 | WellKnownClientTypeEntry remoteType = new WellKnownClientTypeEntry(typeof(IpcService), "ipc://RoboBackup/ipc");
45 | RemotingConfiguration.RegisterWellKnownClientType(remoteType);
46 | _service = new IpcService();
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/RoboBackupGUI/Lang.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 | using System.Linq;
4 |
5 | namespace RoboBackup {
6 | ///
7 | /// Localization class providing translations and language-related methods for the GUI part.
8 | ///
9 | public static class Lang {
10 | ///
11 | /// Collection of available languages, mainly used in . Key is the language string (e.g. "English"), Value is the ISO 639-1 code of the language (e.g. "en").
12 | ///
13 | public static Dictionary AvailableLocales {
14 | get {
15 | if (_availableLocales == null) {
16 | _availableLocales = GetAvailableLocales();
17 | }
18 | return _availableLocales;
19 | }
20 | }
21 |
22 | ///
23 | /// Private store for property.
24 | ///
25 | private static Dictionary _availableLocales;
26 |
27 | ///
28 | /// Private store for translation strings for the currently set language.
29 | ///
30 | private static Dictionary _translations = new Dictionary();
31 |
32 | ///
33 | /// Translates given placeholder string. Optionally appends culturally neutral string, e.g. ":" (colon).
34 | ///
35 | /// Placeholder to look up in translations.
36 | /// Optional to append to the translated string.
37 | /// A string translated into the currently set language.
38 | public static string Get(string key, string append = null) {
39 | if (_translations.ContainsKey(key)) {
40 | string translation = _translations[key];
41 | if (append != null) {
42 | return string.Format("{0}{1}", translation, append);
43 | }
44 | return translation;
45 | }
46 | return key;
47 | }
48 |
49 | ///
50 | /// Loads corresponding translation string from language under directory.
51 | ///
52 | public static void SetLang() {
53 | string langFile = Path.Combine(Config.LangRoot, string.Format("{0}.txt", Config.Language));
54 | if (!File.Exists(langFile)) {
55 | return;
56 | }
57 | // Read the lang file, trim whitespaces from lines, skip empty lines, and split the rest to key=value dictionary
58 | _translations = File.ReadAllLines(langFile).Select(line => line.Trim()).Where(line => !string.IsNullOrEmpty(line)).Select(line => line.Split(new char[] { '=' }, 2)).ToDictionary(line => line[0], line => line[1]);
59 | }
60 |
61 | ///
62 | /// Scans "lang" directory and compiles a collection of languages and translation files present in it.
63 | ///
64 | /// Collection of available languages, mainly used in . Key is the language string (e.g. "English"), Value is the ISO 639-1 code of the language (e.g. "en").
65 | private static Dictionary GetAvailableLocales() {
66 | Dictionary locales = new Dictionary();
67 | foreach (string file in Directory.EnumerateFiles(Config.LangRoot)) {
68 | try {
69 | // Get the value of line from lang file which starts with '='
70 | string langName = File.ReadAllLines(file).First(line => line.StartsWith("=")).Trim().Substring(1);
71 | string langCode = Path.GetFileNameWithoutExtension(file);
72 | locales.Add(langName, langCode);
73 | } catch { /* Ignore if the file doesn't contain what we expect */ }
74 | }
75 | return locales;
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/RoboBackupGUI/MainForm.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace RoboBackup
2 | {
3 | partial class MainForm
4 | {
5 | private System.ComponentModel.IContainer components = null;
6 |
7 | protected override void Dispose(bool disposing)
8 | {
9 | if (disposing && (components != null))
10 | {
11 | components.Dispose();
12 | }
13 | base.Dispose(disposing);
14 | }
15 |
16 | #region Windows Form Designer generated code
17 |
18 | ///
19 | /// Required method for Designer support - do not modify
20 | /// the contents of this method with the code editor.
21 | ///
22 | private void InitializeComponent()
23 | {
24 | this.toolStrip = new System.Windows.Forms.ToolStrip();
25 | this.newTaskToolStripButton = new System.Windows.Forms.ToolStripButton();
26 | this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
27 | this.editTaskToolStripButton = new System.Windows.Forms.ToolStripButton();
28 | this.deleteTaskToolStripButton = new System.Windows.Forms.ToolStripButton();
29 | this.aboutToolStripButton = new System.Windows.Forms.ToolStripButton();
30 | this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
31 | this.showTaskLogsToolStripButton = new System.Windows.Forms.ToolStripButton();
32 | this.runTaskToolStripButton = new System.Windows.Forms.ToolStripButton();
33 | this.settingsToolStripButton = new System.Windows.Forms.ToolStripButton();
34 | this.taskListView = new System.Windows.Forms.ListView();
35 | this.titleColumnHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
36 | this.scheduleColumnHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
37 | this.sourceColumnHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
38 | this.destinationColumnHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
39 | this.toolStrip.SuspendLayout();
40 | this.SuspendLayout();
41 | //
42 | // toolStrip
43 | //
44 | this.toolStrip.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden;
45 | this.toolStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
46 | this.newTaskToolStripButton,
47 | this.toolStripSeparator1,
48 | this.editTaskToolStripButton,
49 | this.deleteTaskToolStripButton,
50 | this.aboutToolStripButton,
51 | this.toolStripSeparator2,
52 | this.showTaskLogsToolStripButton,
53 | this.runTaskToolStripButton,
54 | this.settingsToolStripButton});
55 | this.toolStrip.Location = new System.Drawing.Point(0, 0);
56 | this.toolStrip.Name = "toolStrip";
57 | this.toolStrip.Size = new System.Drawing.Size(726, 25);
58 | this.toolStrip.TabIndex = 1;
59 | //
60 | // newTaskToolStripButton
61 | //
62 | this.newTaskToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
63 | this.newTaskToolStripButton.Image = global::RoboBackup.Properties.Resources.NewIcon;
64 | this.newTaskToolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta;
65 | this.newTaskToolStripButton.Name = "newTaskToolStripButton";
66 | this.newTaskToolStripButton.Size = new System.Drawing.Size(23, 22);
67 | this.newTaskToolStripButton.Text = "NewTask";
68 | this.newTaskToolStripButton.Click += new System.EventHandler(this.NewTaskToolStripButton_Click);
69 | //
70 | // toolStripSeparator1
71 | //
72 | this.toolStripSeparator1.Name = "toolStripSeparator1";
73 | this.toolStripSeparator1.Size = new System.Drawing.Size(6, 25);
74 | //
75 | // editTaskToolStripButton
76 | //
77 | this.editTaskToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
78 | this.editTaskToolStripButton.Enabled = false;
79 | this.editTaskToolStripButton.Image = global::RoboBackup.Properties.Resources.EditIcon;
80 | this.editTaskToolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta;
81 | this.editTaskToolStripButton.Name = "editTaskToolStripButton";
82 | this.editTaskToolStripButton.Size = new System.Drawing.Size(23, 22);
83 | this.editTaskToolStripButton.Text = "EditTask";
84 | this.editTaskToolStripButton.Click += new System.EventHandler(this.EditTaskToolStripButton_Click);
85 | //
86 | // deleteTaskToolStripButton
87 | //
88 | this.deleteTaskToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
89 | this.deleteTaskToolStripButton.Enabled = false;
90 | this.deleteTaskToolStripButton.Image = global::RoboBackup.Properties.Resources.DeleteIcon;
91 | this.deleteTaskToolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta;
92 | this.deleteTaskToolStripButton.Name = "deleteTaskToolStripButton";
93 | this.deleteTaskToolStripButton.Size = new System.Drawing.Size(23, 22);
94 | this.deleteTaskToolStripButton.Text = "DeleteTask";
95 | this.deleteTaskToolStripButton.Click += new System.EventHandler(this.DeleteTaskToolStripButton_Click);
96 | //
97 | // aboutToolStripButton
98 | //
99 | this.aboutToolStripButton.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right;
100 | this.aboutToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
101 | this.aboutToolStripButton.Image = global::RoboBackup.Properties.Resources.HelpIcon;
102 | this.aboutToolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta;
103 | this.aboutToolStripButton.Name = "aboutToolStripButton";
104 | this.aboutToolStripButton.Size = new System.Drawing.Size(23, 22);
105 | this.aboutToolStripButton.Text = "About";
106 | this.aboutToolStripButton.Click += new System.EventHandler(this.AboutToolStripButton_Click);
107 | //
108 | // toolStripSeparator2
109 | //
110 | this.toolStripSeparator2.Name = "toolStripSeparator2";
111 | this.toolStripSeparator2.Size = new System.Drawing.Size(6, 25);
112 | //
113 | // showTaskLogsToolStripButton
114 | //
115 | this.showTaskLogsToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
116 | this.showTaskLogsToolStripButton.Enabled = false;
117 | this.showTaskLogsToolStripButton.Image = global::RoboBackup.Properties.Resources.LogsIcon;
118 | this.showTaskLogsToolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta;
119 | this.showTaskLogsToolStripButton.Name = "showTaskLogsToolStripButton";
120 | this.showTaskLogsToolStripButton.Size = new System.Drawing.Size(23, 22);
121 | this.showTaskLogsToolStripButton.Text = "ShowTaskLogs";
122 | this.showTaskLogsToolStripButton.Click += new System.EventHandler(this.ShowTaskLogsToolStripButton_Click);
123 | //
124 | // runTaskToolStripButton
125 | //
126 | this.runTaskToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
127 | this.runTaskToolStripButton.Enabled = false;
128 | this.runTaskToolStripButton.Image = global::RoboBackup.Properties.Resources.RunIcon;
129 | this.runTaskToolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta;
130 | this.runTaskToolStripButton.Name = "runTaskToolStripButton";
131 | this.runTaskToolStripButton.Size = new System.Drawing.Size(23, 22);
132 | this.runTaskToolStripButton.Text = "RunTask";
133 | this.runTaskToolStripButton.Click += new System.EventHandler(this.RunTaskToolStripButton_Click);
134 | //
135 | // settingsToolStripButton
136 | //
137 | this.settingsToolStripButton.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right;
138 | this.settingsToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
139 | this.settingsToolStripButton.Image = global::RoboBackup.Properties.Resources.SettingsIcon;
140 | this.settingsToolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta;
141 | this.settingsToolStripButton.Name = "settingsToolStripButton";
142 | this.settingsToolStripButton.Size = new System.Drawing.Size(23, 22);
143 | this.settingsToolStripButton.Text = "Settings";
144 | this.settingsToolStripButton.Click += new System.EventHandler(this.SettingsToolStripButton_Click);
145 | //
146 | // taskListView
147 | //
148 | this.taskListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
149 | this.titleColumnHeader,
150 | this.scheduleColumnHeader,
151 | this.sourceColumnHeader,
152 | this.destinationColumnHeader});
153 | this.taskListView.Dock = System.Windows.Forms.DockStyle.Fill;
154 | this.taskListView.FullRowSelect = true;
155 | this.taskListView.GridLines = true;
156 | this.taskListView.Location = new System.Drawing.Point(0, 25);
157 | this.taskListView.Name = "taskListView";
158 | this.taskListView.Size = new System.Drawing.Size(726, 289);
159 | this.taskListView.Sorting = System.Windows.Forms.SortOrder.Ascending;
160 | this.taskListView.TabIndex = 2;
161 | this.taskListView.UseCompatibleStateImageBehavior = false;
162 | this.taskListView.View = System.Windows.Forms.View.Details;
163 | this.taskListView.SelectedIndexChanged += new System.EventHandler(this.TaskListView_SelectedIndexChanged);
164 | this.taskListView.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.TaskListView_MouseDoubleClick);
165 | //
166 | // titleColumnHeader
167 | //
168 | this.titleColumnHeader.Text = "Title";
169 | this.titleColumnHeader.Width = 160;
170 | //
171 | // scheduleColumnHeader
172 | //
173 | this.scheduleColumnHeader.Text = "Schedule";
174 | this.scheduleColumnHeader.Width = 160;
175 | //
176 | // sourceColumnHeader
177 | //
178 | this.sourceColumnHeader.Text = "Source";
179 | this.sourceColumnHeader.Width = 200;
180 | //
181 | // destinationColumnHeader
182 | //
183 | this.destinationColumnHeader.Text = "Destination";
184 | this.destinationColumnHeader.Width = 200;
185 | //
186 | // MainForm
187 | //
188 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
189 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
190 | this.ClientSize = new System.Drawing.Size(726, 314);
191 | this.Controls.Add(this.taskListView);
192 | this.Controls.Add(this.toolStrip);
193 | this.Name = "MainForm";
194 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
195 | this.Text = "RoboBackup";
196 | this.toolStrip.ResumeLayout(false);
197 | this.toolStrip.PerformLayout();
198 | this.ResumeLayout(false);
199 | this.PerformLayout();
200 |
201 | }
202 |
203 | #endregion
204 |
205 | private System.Windows.Forms.ToolStrip toolStrip;
206 | private System.Windows.Forms.ToolStripButton newTaskToolStripButton;
207 | private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
208 | private System.Windows.Forms.ToolStripButton editTaskToolStripButton;
209 | private System.Windows.Forms.ToolStripButton deleteTaskToolStripButton;
210 | private System.Windows.Forms.ToolStripButton aboutToolStripButton;
211 | private System.Windows.Forms.ListView taskListView;
212 | private System.Windows.Forms.ToolStripSeparator toolStripSeparator2;
213 | private System.Windows.Forms.ToolStripButton showTaskLogsToolStripButton;
214 | private System.Windows.Forms.ToolStripButton runTaskToolStripButton;
215 | private System.Windows.Forms.ColumnHeader titleColumnHeader;
216 | private System.Windows.Forms.ColumnHeader scheduleColumnHeader;
217 | private System.Windows.Forms.ColumnHeader sourceColumnHeader;
218 | private System.Windows.Forms.ColumnHeader destinationColumnHeader;
219 | private System.Windows.Forms.ToolStripButton settingsToolStripButton;
220 | }
221 | }
222 |
--------------------------------------------------------------------------------
/RoboBackupGUI/MainForm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.IO;
4 | using System.Windows.Forms;
5 | using System.Drawing;
6 |
7 | namespace RoboBackup {
8 | ///
9 | /// Main form with overview of scheduled backup tasks.
10 | ///
11 | public partial class MainForm : Form {
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | public MainForm() {
16 | InitializeComponent();
17 | Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
18 | Localize();
19 | RedrawTasks();
20 | }
21 |
22 | ///
23 | /// Formats and localizes a to fit .
24 | ///
25 | /// to be formatted.
26 | /// Localized representation of the .
27 | private static ListViewItem DrawTask(Task task) {
28 | string method = Lang.Get("IncrementalAbbr");
29 | if (task.Method == Method.Differential) {
30 | method = Lang.Get("DifferentialAbbr");
31 | } else if (task.Method == Method.Full) {
32 | method = Lang.Get("FullAbbr");
33 | }
34 | string schedule = Lang.Get("DailyAbbr");
35 | if (task.Period == Period.Weekly) {
36 | schedule = string.Format("{0}, {1}", Lang.Get("WeeklyAbbr"), Lang.Get(task.DayOfWeek.ToString()));
37 | } else if (task.Period == Period.Monthly) {
38 | schedule = string.Format("{0}, {1}.", Lang.Get("MonthlyAbbr"), task.DayOfMonth);
39 | }
40 | schedule = string.Format("{0}, {1} @ {2:D2}:{3:D2}", method, schedule, task.Hour, task.Minute);
41 | return new ListViewItem(new string[] { task.Title, schedule, task.Source, task.Destination }) { Tag = task.Guid };
42 | }
43 |
44 | ///
45 | /// Saves the configuration to file and sends a request for conf reload to the service via .
46 | ///
47 | private static void SaveConfig() {
48 | Config.Save();
49 | try {
50 | IpcClient.GetService().ReloadConfig();
51 | } catch {
52 | MessageBox.Show(Lang.Get("UnableToConnectService"), Lang.Get("Error"), MessageBoxButtons.OK, MessageBoxIcon.Error);
53 | }
54 | }
55 |
56 | ///
57 | /// event handler. Opens a new window.
58 | ///
59 | private void AboutToolStripButton_Click(object sender, EventArgs e) {
60 | AboutForm about = new AboutForm();
61 | about.ShowDialog();
62 | }
63 |
64 | ///
65 | /// event handler. Removes a task from the configuration.
66 | ///
67 | private void DeleteTaskToolStripButton_Click(object sender, EventArgs e) {
68 | if (MessageBox.Show(Lang.Get("DeleteConfirmation"), Lang.Get("Question"), MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) {
69 | string guid = (string)taskListView.SelectedItems[0].Tag;
70 | Config.Tasks.Remove(guid);
71 | SaveConfig();
72 | RedrawTasks();
73 | }
74 | }
75 |
76 | ///
77 | /// event handler. Opens a new and updates the selected task in the configuration.
78 | ///
79 | private void EditTaskToolStripButton_Click(object sender, EventArgs e) {
80 | string guid = (string)taskListView.SelectedItems[0].Tag;
81 | TaskForm editForm = new TaskForm(Config.Tasks[guid]);
82 | if (editForm.ShowDialog() == DialogResult.OK) {
83 | Config.Tasks[guid] = editForm.ResultTask;
84 | SaveConfig();
85 | RedrawTasks();
86 | }
87 | }
88 |
89 | ///
90 | /// Translates the form strings to the language set by .
91 | ///
92 | private void Localize() {
93 | newTaskToolStripButton.Text = Lang.Get("NewTask");
94 | editTaskToolStripButton.Text = Lang.Get("EditTask");
95 | deleteTaskToolStripButton.Text = Lang.Get("DeleteTask");
96 | showTaskLogsToolStripButton.Text = Lang.Get("ShowTaskLogs");
97 | runTaskToolStripButton.Text = Lang.Get("RunTask");
98 | settingsToolStripButton.Text = Lang.Get("Settings");
99 | aboutToolStripButton.Text = Lang.Get("About");
100 | titleColumnHeader.Text = Lang.Get("Title");
101 | scheduleColumnHeader.Text = Lang.Get("Schedule");
102 | sourceColumnHeader.Text = Lang.Get("Source");
103 | destinationColumnHeader.Text = Lang.Get("Destination");
104 | }
105 |
106 | ///
107 | /// event handler. Opens a new and inserts the new task into the configuration.
108 | ///
109 | private void NewTaskToolStripButton_Click(object sender, EventArgs e) {
110 | TaskForm newForm = new TaskForm();
111 | if (newForm.ShowDialog() == DialogResult.OK) {
112 | Task t = newForm.ResultTask;
113 | Config.Tasks[t.Guid] = t;
114 | SaveConfig();
115 | RedrawTasks();
116 | }
117 | }
118 |
119 | ///
120 | /// Redraws items using the current locale set by .
121 | ///
122 | private void RedrawTasks() {
123 | taskListView.Items.Clear();
124 | foreach (Task task in Config.Tasks.Values) {
125 | taskListView.Items.Add(DrawTask(task));
126 | }
127 | taskListView.SelectedIndices.Clear();
128 | TaskListView_SelectedIndexChanged(null, null);
129 | }
130 |
131 | ///
132 | /// event handler. Sends a request for immediate backup start to the service via .
133 | ///
134 | private void RunTaskToolStripButton_Click(object sender, EventArgs e) {
135 | string guid = (string)taskListView.SelectedItems[0].Tag;
136 | try {
137 | IpcClient.GetService().RunTask(guid);
138 | MessageBox.Show(Lang.Get("BackupStarted"), Lang.Get("Information"), MessageBoxButtons.OK, MessageBoxIcon.Information);
139 | } catch {
140 | MessageBox.Show(Lang.Get("UnableToConnectService"), Lang.Get("Error"), MessageBoxButtons.OK, MessageBoxIcon.Error);
141 | }
142 | }
143 |
144 | ///
145 | /// event handler. Opens a and updates the configuration for log retention and language.
146 | ///
147 | private void SettingsToolStripButton_Click(object sender, EventArgs e) {
148 | SettingsForm settings = new SettingsForm();
149 | if (settings.ShowDialog() == DialogResult.OK) {
150 | bool langChanged = settings.Language != Config.Language;
151 | Config.LogRetention = settings.LogRetention;
152 | Config.Language = settings.Language;
153 | SaveConfig();
154 | if (langChanged) {
155 | Lang.SetLang();
156 | Localize();
157 | RedrawTasks();
158 | }
159 | }
160 | }
161 |
162 | ///
163 | /// handler. Opens a directory with logs for the selected .
164 | ///
165 | private void ShowTaskLogsToolStripButton_Click(object sender, EventArgs e) {
166 | string guid = (string)taskListView.SelectedItems[0].Tag;
167 | string logDir = Path.Combine(Config.LogRoot, guid);
168 | if (!Directory.Exists(logDir)) {
169 | MessageBox.Show(Lang.Get("NoTaskLogs"), Lang.Get("Information"), MessageBoxButtons.OK, MessageBoxIcon.Information);
170 | return;
171 | }
172 | Process.Start(logDir);
173 | }
174 |
175 | ///
176 | /// event handler. Calls .
177 | ///
178 | private void TaskListView_MouseDoubleClick(object sender, MouseEventArgs e) {
179 | EditTaskToolStripButton_Click(sender, null);
180 | }
181 |
182 | ///
183 | /// event handler. Changes the state of the buttons according to the selection.
184 | ///
185 | private void TaskListView_SelectedIndexChanged(object sender, EventArgs e) {
186 | bool selected = taskListView.SelectedItems.Count != 0;
187 | editTaskToolStripButton.Enabled = selected;
188 | deleteTaskToolStripButton.Enabled = selected;
189 | showTaskLogsToolStripButton.Enabled = selected;
190 | runTaskToolStripButton.Enabled = selected;
191 | }
192 | }
193 | }
194 |
--------------------------------------------------------------------------------
/RoboBackupGUI/OpenFolderDialog.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 | using System.Runtime.InteropServices;
4 | using System.Windows.Forms;
5 |
6 | namespace RoboBackup {
7 | ///
8 | /// Windows Vista style folder selection dialog. Allows for selection of Libraries or UNC paths. Also allows copypasting the directory path.
9 | ///
10 | /// The class heavily relies on P/Invoke, COM imports and black magic. Courtesy of Simon Mourier - https://stackoverflow.com/a/15386992
11 | public class OpenFolderDialog : Component {
12 | ///
13 | /// Selected directory full path.
14 | ///
15 | public string SelectedPath { get; set; }
16 |
17 | ///
18 | /// Runs a common dialog box with a default owner.
19 | ///
20 | /// if the user clicks OK in the dialog box; otherwise, .
21 | public DialogResult ShowDialog() {
22 | return ShowDialog(IntPtr.Zero);
23 | }
24 |
25 | ///
26 | /// Runs a common dialog box with the specified owner.
27 | ///
28 | /// Any object that implements IWin32Window that represents the top-level window that will own the modal dialog box.
29 | /// if the user clicks OK in the dialog box; otherwise, .
30 | public DialogResult ShowDialog(IntPtr hwndOwner) {
31 | IFileOpenDialog dialog = (IFileOpenDialog)new FileOpenDialog();
32 | try {
33 | IShellItem item;
34 | if (!string.IsNullOrEmpty(SelectedPath)) {
35 | uint atts = 0;
36 | if (SHILCreateFromPath(SelectedPath, out IntPtr idl, ref atts) == 0) {
37 | if (SHCreateShellItem(IntPtr.Zero, IntPtr.Zero, idl, out item) == 0) {
38 | dialog.SetFolder(item);
39 | }
40 | Marshal.FreeCoTaskMem(idl);
41 | }
42 | }
43 | dialog.SetOptions(0x20 | 0x40); // FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM
44 | uint hresult = dialog.Show(hwndOwner);
45 | if (hresult == 0x800704C7) // ERROR_CANCELLED
46 | return DialogResult.Cancel;
47 | if (hresult != 0)
48 | return DialogResult.Abort;
49 | dialog.GetResult(out item);
50 | item.GetDisplayName(0x80058000, out string path); // SIGDN_FILESYSPATH
51 | SelectedPath = path;
52 | return DialogResult.OK;
53 | } finally {
54 | Marshal.ReleaseComObject(dialog);
55 | }
56 | }
57 |
58 | ///
59 | /// Creates a pointer to an item identifier list (PIDL) from a path.
60 | ///
61 | /// A pointer to a null-terminated string of maximum length MAX_PATH containing the path to be converted.
62 | /// The path in expressed as a PIDL.
63 | /// A pointer to a DWORD value that, on entry, indicates any attributes of the folder named in that the calling application would like to retrieve along with the PIDL. On exit, this value contains those requested attributes.
64 | /// If this function succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code.
65 | /// https://docs.microsoft.com/en-us/windows/desktop/api/shlobj_core/nf-shlobj_core-shilcreatefrompath
66 | [DllImport("shell32.dll", CharSet = CharSet.Unicode)]
67 | private static extern int SHILCreateFromPath(string pszPath, out IntPtr ppIdl, ref uint rgflnOut);
68 |
69 | ///
70 | /// Creates and initializes a Shell item object from a pointer to a parent item identifier list (PIDL). The resulting shell item object supports the interface.
71 | ///
72 | /// A PIDL to the parent. This value can be NULL.
73 | /// A pointer to the parent . This value can be NULL.
74 | /// A PIDL to the requested item. If parent information is not included in pidlParent or psfParent, this must be an absolute PIDL.
75 | /// When this method returns, contains the interface pointer to the new .
76 | /// If this function succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code.
77 | /// https://docs.microsoft.com/en-us/windows/desktop/api/shlobj_core/nf-shlobj_core-shcreateshellitem
78 | [DllImport("shell32.dll")]
79 | private static extern int SHCreateShellItem(IntPtr pidlParent, IntPtr psfParent, IntPtr pidl, out IShellItem ppsi);
80 |
81 | ///
82 | /// Basic Vista-style file open dialog. Allows for selection of Libraries or UNC paths. Also allows copypasting the directory path.
83 | ///
84 | [ComImport, Guid("DC1C5A9C-E88A-4DDE-A5A1-60F82A20AEF7")]
85 | private class FileOpenDialog { }
86 |
87 | ///
88 | /// Extends the IFileDialog interface by adding methods specific to the open dialog.
89 | ///
90 | /// https://docs.microsoft.com/en-us/windows/desktop/api/shobjidl_core/nn-shobjidl_core-ifileopendialog
91 | [ComImport, Guid("42F85136-DB7E-439C-85F1-E4075D135FC8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
92 | private interface IFileOpenDialog {
93 | [PreserveSig]
94 | uint Show([In] IntPtr parent); // IModalWindow
95 | void SetFileTypes(); // not fully defined
96 | void SetFileTypeIndex([In] uint iFileType);
97 | void GetFileTypeIndex(out uint piFileType);
98 | void Advise(); // not fully defined
99 | void Unadvise();
100 | void SetOptions([In] int fos);
101 | void GetOptions(out int pfos);
102 | void SetDefaultFolder(IShellItem psi);
103 | void SetFolder(IShellItem psi);
104 | void GetFolder(out IShellItem ppsi);
105 | void GetCurrentSelection(out IShellItem ppsi);
106 | void SetFileName([In, MarshalAs(UnmanagedType.LPWStr)] string pszName);
107 | void GetFileName([MarshalAs(UnmanagedType.LPWStr)] out string pszName);
108 | void SetTitle([In, MarshalAs(UnmanagedType.LPWStr)] string pszTitle);
109 | void SetOkButtonLabel([In, MarshalAs(UnmanagedType.LPWStr)] string pszText);
110 | void SetFileNameLabel([In, MarshalAs(UnmanagedType.LPWStr)] string pszLabel);
111 | void GetResult(out IShellItem ppsi);
112 | void AddPlace(IShellItem psi, int alignment);
113 | void SetDefaultExtension([In, MarshalAs(UnmanagedType.LPWStr)] string pszDefaultExtension);
114 | void Close(int hr);
115 | void SetClientGuid(); // not fully defined
116 | void ClearClientData();
117 | void SetFilter([MarshalAs(UnmanagedType.Interface)] IntPtr pFilter);
118 | void GetResults([MarshalAs(UnmanagedType.Interface)] out IntPtr ppenum); // not fully defined
119 | void GetSelectedItems([MarshalAs(UnmanagedType.Interface)] out IntPtr ppsai); // not fully defined
120 | }
121 |
122 | ///
123 | /// Exposes methods that retrieve information about a Shell item.
124 | ///
125 | /// https://docs.microsoft.com/en-us/windows/desktop/api/shobjidl_core/nn-shobjidl_core-ishellitem
126 | [ComImport, Guid("43826D1E-E718-42EE-BC55-A1E261C37BFE"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
127 | private interface IShellItem {
128 | void BindToHandler(); // not fully defined
129 | void GetParent(); // not fully defined
130 | void GetDisplayName([In] uint sigdnName, [MarshalAs(UnmanagedType.LPWStr)] out string ppszName);
131 | void GetAttributes(); // not fully defined
132 | void Compare(); // not fully defined
133 | }
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/RoboBackupGUI/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Forms;
3 |
4 | namespace RoboBackup {
5 | public static class Program {
6 | ///
7 | /// Main entry point to the GUI application.
8 | ///
9 | [STAThread]
10 | public static void Main() {
11 | Config.Load();
12 | Lang.SetLang();
13 | Application.EnableVisualStyles();
14 | Application.SetCompatibleTextRenderingDefault(false);
15 | Application.Run(new MainForm());
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/RoboBackupGUI/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | [assembly: AssemblyTitle("RoboBackup GUI")]
5 | [assembly: AssemblyDescription("")]
6 | [assembly: AssemblyConfiguration("")]
7 | [assembly: AssemblyCompany("")]
8 | [assembly: AssemblyProduct("RoboBackup GUI")]
9 | [assembly: AssemblyCopyright("Copyright © Disassembler 2018")]
10 | [assembly: AssemblyTrademark("")]
11 | [assembly: AssemblyCulture("")]
12 |
13 | [assembly: ComVisible(false)]
14 | [assembly: Guid("7767bf21-ee40-448b-90ed-179a17e320cc")]
15 |
16 | [assembly: AssemblyVersion("1.0.0.0")]
17 | [assembly: AssemblyFileVersion("1.0.0.0")]
18 |
--------------------------------------------------------------------------------
/RoboBackupGUI/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace RoboBackup.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | public class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | public static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RoboBackup.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | public static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 |
63 | ///
64 | /// Looks up a localized resource of type System.Drawing.Bitmap.
65 | ///
66 | public static System.Drawing.Bitmap DeleteIcon {
67 | get {
68 | object obj = ResourceManager.GetObject("DeleteIcon", resourceCulture);
69 | return ((System.Drawing.Bitmap)(obj));
70 | }
71 | }
72 |
73 | ///
74 | /// Looks up a localized resource of type System.Drawing.Bitmap.
75 | ///
76 | public static System.Drawing.Bitmap EditIcon {
77 | get {
78 | object obj = ResourceManager.GetObject("EditIcon", resourceCulture);
79 | return ((System.Drawing.Bitmap)(obj));
80 | }
81 | }
82 |
83 | ///
84 | /// Looks up a localized resource of type System.Drawing.Bitmap.
85 | ///
86 | public static System.Drawing.Bitmap HelpIcon {
87 | get {
88 | object obj = ResourceManager.GetObject("HelpIcon", resourceCulture);
89 | return ((System.Drawing.Bitmap)(obj));
90 | }
91 | }
92 |
93 | ///
94 | /// Looks up a localized resource of type System.Drawing.Bitmap.
95 | ///
96 | public static System.Drawing.Bitmap LogsIcon {
97 | get {
98 | object obj = ResourceManager.GetObject("LogsIcon", resourceCulture);
99 | return ((System.Drawing.Bitmap)(obj));
100 | }
101 | }
102 |
103 | ///
104 | /// Looks up a localized resource of type System.Drawing.Bitmap.
105 | ///
106 | public static System.Drawing.Bitmap NewIcon {
107 | get {
108 | object obj = ResourceManager.GetObject("NewIcon", resourceCulture);
109 | return ((System.Drawing.Bitmap)(obj));
110 | }
111 | }
112 |
113 | ///
114 | /// Looks up a localized resource of type System.Drawing.Bitmap.
115 | ///
116 | public static System.Drawing.Bitmap RunIcon {
117 | get {
118 | object obj = ResourceManager.GetObject("RunIcon", resourceCulture);
119 | return ((System.Drawing.Bitmap)(obj));
120 | }
121 | }
122 |
123 | ///
124 | /// Looks up a localized resource of type System.Drawing.Bitmap.
125 | ///
126 | public static System.Drawing.Bitmap SettingsIcon {
127 | get {
128 | object obj = ResourceManager.GetObject("SettingsIcon", resourceCulture);
129 | return ((System.Drawing.Bitmap)(obj));
130 | }
131 | }
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/RoboBackupGUI/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
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 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 |
122 |
123 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
124 | wwAADsMBx2+oZAAAAl9JREFUOE+tk/1LU2Ecxe/fMotggQTlD0UIFUmbL3ttuty8ya2LmxmLME3ZHaYZ
125 | lBhq9EOopetVMyGmmUFkzgRz9KKodFdMbbc2am3ubt4t5bStG2bOIOjA+e18zsPzfb4P8d81X1GR4TGb
126 | re9pmnWTZPhdwnOkkZ2hiq1TJYUZYiy9FsvLNazZzH1ta4Yw+hSxqUnEXjrBO3qwYKHxpkTDuQwKjRhf
127 | ryTsLivjl/p6sOqeQ3z4AeL2FsQ7LuH7vXasjA/DV2vBmCqPn9DnrC+ZN5kkbpr2hu/YscpOI37tAoIt
128 | NoSaayBcsSHWykBoOgNh8C4WT1IY1xz0jqoPSEScIDwmE+Ovt2Jl2pU6cbmNQeBiJaINJ9Y5fJaC0H8T
129 | rxXZeK7NZkScID4kBhbpv4V4bweCTVX4QmrTeqmKAt9cB5+tGs9Ue1gRJ4hpiorGHvcj3mZD8LwFm8mn
130 | z0fAXIJQx1UM5UijIp4sIKPLvV0QGi2InisT4xuVLPDTBoTa/yiYJYvYQFMdllsYhE8bU8G0LlYgWF+L
131 | hdpKDMszf7sCWciwulxEe7sRPKZDgFZvgLmiPHBH1IjYr8Op3IUhuXRtiG9L1ZJXRo13sbwUkftdqYH5
132 | DcqfoL4AnC4B65Tguzsxc1SLAZnU68jduvaMSbn0eZoxlYz3HDcgcrsL3xoZ+KhifDLqEWiwgr/RiVmj
133 | Cn1yCT8g25Z+G8cTG/bi8H5uUr0Xn2sqEWi9nPLH6lMYyd+Jh/Lt3KbwLzkL92WMKHZbn6iyWIc8K+yQ
134 | 7wg7CjLZR7lS6+ChLX//TP8ugvgBZP3SiZ3Tf+kAAAAASUVORK5CYII=
135 |
136 |
137 |
138 |
139 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
140 | wwAADsMBx2+oZAAAAU9JREFUOE9joBt4dMyC8/Ziv1f7J9v/PTHB7/+l2dr3Hyw2koRKEwY3VwT8v7E0
141 | 6//3hyf//3h8+v+pqU7/DzYZrINK4wdPdun//3Jl+f8fD4/+//n4DBh/v733//4J9n+hSnADkOYfbw//
142 | //Foxf+763L+fzm/FIxBBpzu9/8OVYYdwDT/+bL5/9ebGf8/n8//f2Fu5P+7q4v/n5zo/x+qDDtA0Xwn
143 | 4f+Xay7/f73r+/9sS+z/m/3WNNS8f4I6+Zp3dqr9//RsH+map8fLK4A0Pzy94f+Jlemk2zw5V+7/p4cn
144 | /v96fvH/1lqT/4dnGv6/cyT3//k5Xv+PN5vg1wwCIAN+AlPXm1WJ/1/McPh/KF30/6oEsf9bynQIawaB
145 | SCeh/1fmxP7/en7Z/zfnV/4/2O1BvGYQiHAQ/h/nJPp/TqYqWOPOLsu1UClaAwYGAIUsKp6HS3QnAAAA
146 | AElFTkSuQmCC
147 |
148 |
149 |
150 |
151 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
152 | wwAADsMBx2+oZAAAAqJJREFUOE+tk91LU3Ecxs/fov9BIBXUhQRdCRG76FJ0+dZaqBhCtplIhitmkTM1
153 | NcXcRi4R3XTu1F50c5pT9+7ezLntTHcc29w5O1oGT3OsTJAg6IHP3ffzwPf3Qvz3mANHpabAscjoY4Pq
154 | LZZRe1hmwc4E5+0Z0dxaprQ4dnH0WzneJ0cuHkp8w/Y+hwDFFAjt5eCN5aBcPogrzQlecfx8SFeW99GZ
155 | ZXfoI9i/pqAwRdE95UOXagtD5Das3gP44wz69CF2iAyfL1E7MyWzG2lqO8Fh2ZdER14aJEOIJjlEkjlM
156 | rcTQpnBCtxGHJ3II6WyQkqr8JUWdIFQrtNgdZbEeSkOs9KB11I7c8UmeH0VO0KF0oO39JpY8NBbdNDoV
157 | TnFRJ4hxUzzoz+86Qu7g4egmmkfWET3IQb0Wg8G1By5fYHBREPSb8UrthTOcQsvIarCoE8SA1s0FqCzE
158 | cgeahm24P2jND1swZ4sgxRxjP8Vh7LMf9b2LaHprgTeSAV+m5Yo6QfRMuzl/LItHEw4IBiy498aMhj4z
159 | stx37NIMhLJF1PTocTePUGaEJ5pGVfsfBZ1ye9C1m4ZM48f9fisaZEuoe21C7UtDAf5zEnzJAqolWnQp
160 | 1mAL0bjTqT5boXXYJl6wxWDx0mgasP4Wk4dHoDMcKrvnC/C7NSA3IpDn16lomzk7xMa+pZJaqYFa9dHQ
161 | rUch6DWi+oUOvdObBU7lqmcz+GAMwOiI4WbjJHWjUXV2jaeplOh5t9tJ1uzZg8FJ4al8FXVSLWokc3gy
162 | ZoHOFobeHsXV+kn2+gPlxa/xlkjDq3g8Ex8nt/AlkIAjnCyw4tvHkMaFKwJF/LJg4mL5V8pbpkrLmydF
163 | 14TyYJnwHVMmyFM7ESyrHxddqhn9+2f69xDETzT5CW1O8B7pAAAAAElFTkSuQmCC
164 |
165 |
166 |
167 |
168 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
169 | wwAADsMBx2+oZAAAAXRJREFUOE+Nk22LAWEUhu3+jt3a/0D+5yb5Bb5II+WDhEQhMj54S/KJiMgwxjv3
170 | nvvJzL4Y7KmrmZ4553rOeWbGc403wSv4n/AhvAo34d1sNsZgMDifTie4sdvtEAgEPiXXVeJn8Xq9hmVZ
171 | DsvlEovFApPJREnG4zGCweCN5EXwM2G73WK/36srMU0ThmFgOp0qgXQJbhIKhWyJCkfA4lwuh1QqhUQi
172 | gVgshkgkgnA4/Gscwppr7bfgeDw6Cbzn3PYos9lMdTIaje4LSKlUQj6fRzabRTKZVJ3E43FomoZoNIr5
173 | fP5Y8Be7E87PM1mtVmr9rkDXdVQqFRSLRRQKBXUmmUwG6XRanQ1FDwVusAseMGE3XLsraLfbaDQaqNfr
174 | qNVqqFarKJfLCnb1VGDDXcnhcLjhoaDf76PX66Hb7aLT6aDVaqHZbDpQ6ioYDocXW/IMyT1fBU745PVY
175 | fOBW8BPmyOdsSg3/XifeBZ9A639gsdR4PF9uQnJ8xr+yGQAAAABJRU5ErkJggg==
176 |
177 |
178 |
179 |
180 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
181 | wwAADsMBx2+oZAAAAnhJREFUOE+tk/1LU1EYx+/fMosgVpBoVCT4QpRgJF4qX1vkS+rSTV3k1LtpU+eu
182 | OfHtulFmQVu5TUa66ebcaIqZOF3pIvU6lGijCIJy+Ou3e+uaiRYEfeD57fl8zznPOYf479BeRVybW05p
183 | HBVs46hkS/Gcq2EJW22VUBVPr8UJbQdDe6pIrasq+mSxF/PRaYQ+B7H4aQ6usB1qdwXKTFeixYNZpNC+
184 | F15WD5fGPOt2bH5bh/fDGEzsfTxaZWDfMGHm4wvQU0pImMxYvjFjb4jWIxc1O+WR8TUL1r+uYGClGz0L
185 | bdAHmsGE2tEXoqFf0mB80w7KLQVpuBDJYtJEgs4FuGSq/tkWvPuyxK3Yh95lGrpXauzQsqBEw7QUqlkZ
186 | nBs2XH14DhldySpBJwh+YGNhG7dVM+7NN6F1rgFKb6WgA+W2PNyZvI6aiXwY37Sje6YR5/VnWUEnCNVI
187 | 4bbv/Rj637Zzcp2g7UfuyoHKXwZLaACp6qPbgk4QdfbC7dHwEOgghabAbaF9P3wA5bsJa+gBkn4PUNgk
188 | rCFAg1nWoe6lFHJ/IYqtlwXtp8hXtTsP/YEWdPjqcVpzfPcIsqECVamVhCNsgdJfhCqf5MeZd+DlSmc2
189 | FOP5cKyZkdZ5CglN4t0hlplyRSWPyQjlLscI++zXwHhR5uSLl/MwumrCLUsuEu6KI8fUh3evkefG4CWy
190 | gLkYq3UUwcmFGAJaUJMlaJgogmG+FQ5OllqykaAWxU5QRw5+jTnGdJLsSY1mGlPQOUXB/JqBOchA56tF
191 | asdJThZH/yjvkNmVEpeuT6LS6DNssi5xK1Ebv5XIDSy+UUzF1x/6+2f6dwjiO8B3zyKE8PtmAAAAAElF
192 | TkSuQmCC
193 |
194 |
195 |
196 |
197 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
198 | wwAADsMBx2+oZAAAA2tJREFUOE9VkF9MU1ccx0+WLCbL9rhsL3vZg8v+JDyAzLmYRed4wPigD5u4uQyD
199 | bIJiKiidKLQ6tAgM1nUFFLhsHTVeAb1Ya2TqrZeCtg5KqRBqsSwFLQxuaXutlAp8d85dH7Zv8s355pzz
200 | +f5yDmFCFXlJuU50z2zEG7cR95qdrFMP/iNFIG+wc3rPFbtB3klvU7iFvBzpJueiD/JmUgsiZPfnc/d7
201 | tztLbyUdZbeWZGaWB4WdUtxTsPB8XCtHuohLvkLeUgtmL5KKqGvvk+XZ6wjfy190cRsUg3MBpgdzaBsJ
202 | q2b5RymIkc71iahPn1BGSv+e7SRDbDgJceRkfEgrh/vzFwe5t18Y74VwyfsUVukvmHvGYLw8ho7bQVg9
203 | M2i/K0Ls/WQ1+PDySvjqu/6ZVvqUx+fJ5gnzK36xdX3inBjERVcIPeIUJqdjWIgpmI89gz8Ug6VvEu39
204 | U7gk1OG+JTs53vLacNCc/ovStmu15d0+nHdOgafTmCLRKB49CWOJZmZlDWi2TcLsCKCSl3CAG6hWYaYv
205 | m/50HO3yofmGH+N0cuLFGpREAr+0c5ijRfFkEt0370B6FMVZfhTsLmPSOCE7GkVZYx1GTacXc89TiCRX
206 | sUynfq/T4/ceAeVVevQNuiCGAzhidoHdZUwapwVnBLmw1QUd50FISdGSVUxH4/ihrl6FJc8oTriPQNNf
207 | iEN39qHwt14wJo0TklPZ5djdIEJvGcLdQBRP6aOn4ynYHQMIyjHMrgDlAyWwTDahfliPkpsF2HHWNJTG
208 | Cdly9Fddru4qDl8YhIH3Y55+WCQFxFepaV6kBaViEdr9RrRR6wfKkc/nYXtTTrZasKm4OeujItNYzslr
209 | 0LRIqOEn4JlahEL/Q0mk4KV5v5CPprE6nPYcg/GhAWV9xdhWuxlqAVP2/vpvDxgsiW3aK/i67g9ofpZQ
210 | 9pMTmoZ+HGwU8ZU1D42jZ3DcXYJa7ykcshUgQ5exlMYJcTqdW3w+H26L0srHxa3YcNCKDw/3qGZ514Wd
211 | qBmugsFTiaLefdhUmZXKPJbx7xPsdvvrNptNCQQCEARBzthzQv/eFxWOD3ZrZWaWcxs+W9K7tfhO+AYb
212 | KzKx17Bnlwoz8Tz/akdHx4TJZFqm6/vp7f9pqyErktv4KTYez0S1sXqe47g3CSHkHz26c4uO0Rz4AAAA
213 | AElFTkSuQmCC
214 |
215 |
216 |
217 |
218 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
219 | wwAADsMBx2+oZAAAApJJREFUOE99U01LamEQPvQD3PsT+hmBC5eVEUEg2AcUaLUsRZRSiNBNi0AsSaQS
220 | +8IgpdSO+VWpxygzEtOFJgeFXLk9Z+7M2+lGcbsDh5cz78wzzzwzL/ebFQqF6Ww2y2cyGf7q6mpacf/f
221 | yuXy4O3trQoABm5ubrrtdhve3t7g6Oioa7PZBiKRiOrk5GRQCf9u9/f33uvraymfz4t4qlOplNhoNKBe
222 | r8Pp6al4fHysRiDx4OBA8vv9XiXtwzDZ8vr6Cu/v71Cr1QCT++FwWH5+foanpycIBAIyJvexLRBFEaLR
223 | KGxsbFiUdI67u7uzolMmEEqoVqsM6BOA/h8fH4EAsEVYWVmRl5aWrEr6h6FgTgouFouAgHB5eQnb29uA
224 | dIkR5HI5wNZY9fX1daeSxnGIKKTT6VYsFuu9vLyAIAgQj8dhdXU1t7y8rEHxNC6XK4cxDIju8b/ncDha
225 | GCNwPM9LnU6H0aXKpVIJ9vb2YG1tTaPU4DwejwY1ABwpY0ZsSFy9Xi8xgGazyXqkC6qAAsLm5uZfgK2t
226 | LU0wGGQMqIVkMsnix8bGJG53d1dwu92tnZ2dHk6DgZAOPp8vR5UJiFqg/qm1i4sLMBgMvfHx8ZZOpxOU
227 | GhyHFJ0oJBOKTlwkCIVCgHNnPtxGOD8/Z775+fkvEckODw+tWE2uVCosmcb18PDANCEgYkT+RCLB7hYW
228 | FuTZ2dmvMeKiWIgebd7+/j6g8n38ZBKN+jaZTPLc3Fyf7qh3u90Oo6OjX4tEhk7v1NSUNDMzI5rNZjVO
229 | QaRqxAD94uTkpHpiYkIcGhqStFrt91X+tMXFxUGspqKHg1+XKJ+dnQEms8eEp2p4ePjfj+mnGY3GaazI
230 | j4yM8Ej3l+fMcX8AvAUd7zJNh5cAAAAASUVORK5CYII=
231 |
232 |
233 |
234 |
--------------------------------------------------------------------------------
/RoboBackupGUI/RoboBackup.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Disassembler0/RoboBackup/cfba7a2168e85d2625ff7b1d26257db9273636ee/RoboBackupGUI/RoboBackup.ico
--------------------------------------------------------------------------------
/RoboBackupGUI/RoboBackupGUI.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {7767BF21-EE40-448B-90ED-179A17E320CC}
8 | WinExe
9 | RoboBackup
10 | RoboBackupGUI
11 | v4.6.1
12 | 512
13 | false
14 |
15 | publish\
16 | true
17 | Disk
18 | false
19 | Foreground
20 | 7
21 | Days
22 | false
23 | false
24 | true
25 | 0
26 | 1.0.0.%2a
27 | false
28 | true
29 |
30 |
31 | AnyCPU
32 | true
33 | full
34 | false
35 | bin\Debug\
36 | DEBUG;TRACE
37 | prompt
38 | 4
39 | AllRules.ruleset
40 | false
41 |
42 |
43 | AnyCPU
44 | pdbonly
45 | true
46 | bin\Release\
47 | TRACE
48 | prompt
49 | 4
50 |
51 |
52 |
53 |
54 |
55 | RoboBackup.ico
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | Form
67 |
68 |
69 | AboutForm.cs
70 |
71 |
72 |
73 |
74 | Component
75 |
76 |
77 | True
78 | True
79 | Resources.resx
80 |
81 |
82 | Form
83 |
84 |
85 | SettingsForm.cs
86 |
87 |
88 | Form
89 |
90 |
91 | MainForm.cs
92 |
93 |
94 | Form
95 |
96 |
97 | TaskForm.cs
98 |
99 |
100 |
101 |
102 |
103 | PublicResXFileCodeGenerator
104 | Resources.Designer.cs
105 | Designer
106 |
107 |
108 |
109 |
110 | False
111 | .NET Framework 3.5 SP1
112 | false
113 |
114 |
115 |
116 |
117 | {43c4f764-abbd-46a5-a184-c24da8d7b203}
118 | RoboBackupService
119 |
120 |
121 |
122 |
123 | PreserveNewest
124 |
125 |
126 | PreserveNewest
127 |
128 |
129 |
130 |
131 |
132 |
--------------------------------------------------------------------------------
/RoboBackupGUI/SettingsForm.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace RoboBackup {
2 | partial class SettingsForm {
3 | private System.ComponentModel.IContainer components = null;
4 |
5 | protected override void Dispose(bool disposing) {
6 | if (disposing && (components != null)) {
7 | components.Dispose();
8 | }
9 | base.Dispose(disposing);
10 | }
11 |
12 | #region Windows Form Designer generated code
13 |
14 | ///
15 | /// Required method for Designer support - do not modify
16 | /// the contents of this method with the code editor.
17 | ///
18 | private void InitializeComponent() {
19 | this.languageLabel = new System.Windows.Forms.Label();
20 | this.logRetentionLabel = new System.Windows.Forms.Label();
21 | this.languageComboBox = new System.Windows.Forms.ComboBox();
22 | this.logRetentionNumericUpDown = new System.Windows.Forms.NumericUpDown();
23 | this.saveSettingsButton = new System.Windows.Forms.Button();
24 | ((System.ComponentModel.ISupportInitialize)(this.logRetentionNumericUpDown)).BeginInit();
25 | this.SuspendLayout();
26 | //
27 | // languageLabel
28 | //
29 | this.languageLabel.AutoSize = true;
30 | this.languageLabel.Location = new System.Drawing.Point(12, 15);
31 | this.languageLabel.Name = "languageLabel";
32 | this.languageLabel.Size = new System.Drawing.Size(58, 13);
33 | this.languageLabel.TabIndex = 0;
34 | this.languageLabel.Text = "Language:";
35 | //
36 | // logRetentionLabel
37 | //
38 | this.logRetentionLabel.AutoSize = true;
39 | this.logRetentionLabel.Location = new System.Drawing.Point(12, 41);
40 | this.logRetentionLabel.Name = "logRetentionLabel";
41 | this.logRetentionLabel.Size = new System.Drawing.Size(74, 13);
42 | this.logRetentionLabel.TabIndex = 2;
43 | this.logRetentionLabel.Text = "LogRetention:";
44 | //
45 | // languageComboBox
46 | //
47 | this.languageComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
48 | this.languageComboBox.FormattingEnabled = true;
49 | this.languageComboBox.Location = new System.Drawing.Point(76, 12);
50 | this.languageComboBox.Name = "languageComboBox";
51 | this.languageComboBox.Size = new System.Drawing.Size(116, 21);
52 | this.languageComboBox.TabIndex = 1;
53 | //
54 | // logRetentionNumericUpDown
55 | //
56 | this.logRetentionNumericUpDown.Location = new System.Drawing.Point(121, 39);
57 | this.logRetentionNumericUpDown.Maximum = new decimal(new int[] {
58 | 99999,
59 | 0,
60 | 0,
61 | 0});
62 | this.logRetentionNumericUpDown.Minimum = new decimal(new int[] {
63 | 1,
64 | 0,
65 | 0,
66 | 0});
67 | this.logRetentionNumericUpDown.Name = "logRetentionNumericUpDown";
68 | this.logRetentionNumericUpDown.Size = new System.Drawing.Size(71, 20);
69 | this.logRetentionNumericUpDown.TabIndex = 3;
70 | this.logRetentionNumericUpDown.Value = new decimal(new int[] {
71 | 1,
72 | 0,
73 | 0,
74 | 0});
75 | //
76 | // saveSettingsButton
77 | //
78 | this.saveSettingsButton.Location = new System.Drawing.Point(15, 65);
79 | this.saveSettingsButton.Name = "saveSettingsButton";
80 | this.saveSettingsButton.Size = new System.Drawing.Size(177, 23);
81 | this.saveSettingsButton.TabIndex = 4;
82 | this.saveSettingsButton.Text = "SaveSettings";
83 | this.saveSettingsButton.UseVisualStyleBackColor = true;
84 | this.saveSettingsButton.Click += new System.EventHandler(this.SaveSettingsButton_Click);
85 | //
86 | // SettingsForm
87 | //
88 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
89 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
90 | this.ClientSize = new System.Drawing.Size(205, 96);
91 | this.Controls.Add(this.saveSettingsButton);
92 | this.Controls.Add(this.logRetentionNumericUpDown);
93 | this.Controls.Add(this.languageComboBox);
94 | this.Controls.Add(this.logRetentionLabel);
95 | this.Controls.Add(this.languageLabel);
96 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
97 | this.MaximizeBox = false;
98 | this.MinimizeBox = false;
99 | this.Name = "SettingsForm";
100 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
101 | this.Text = "Settings";
102 | ((System.ComponentModel.ISupportInitialize)(this.logRetentionNumericUpDown)).EndInit();
103 | this.ResumeLayout(false);
104 | this.PerformLayout();
105 |
106 | }
107 |
108 | #endregion
109 |
110 | private System.Windows.Forms.Label languageLabel;
111 | private System.Windows.Forms.Label logRetentionLabel;
112 | private System.Windows.Forms.ComboBox languageComboBox;
113 | private System.Windows.Forms.NumericUpDown logRetentionNumericUpDown;
114 | private System.Windows.Forms.Button saveSettingsButton;
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/RoboBackupGUI/SettingsForm.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Windows.Forms;
3 |
4 | namespace RoboBackup {
5 | ///
6 | /// Form with global settings - language and log retention
7 | ///
8 | public partial class SettingsForm : Form {
9 | ///
10 | /// Language returned by successfully completed form.
11 | ///
12 | public string Language { get; private set; }
13 |
14 | ///
15 | /// Log retention in days returned by successfully completed form.
16 | ///
17 | public ushort LogRetention { get; private set; }
18 |
19 | ///
20 | /// Initializes a new instance of the class.
21 | ///
22 | public SettingsForm() {
23 | InitializeComponent();
24 | Localize();
25 | languageComboBox.Items.AddRange(Lang.AvailableLocales.Keys.ToArray());
26 | languageComboBox.Text = Lang.Get(""); // Name of the language is stored in the empty string key
27 | logRetentionNumericUpDown.Value = Config.LogRetention;
28 | }
29 |
30 | ///
31 | /// Translates the form strings to the language set by .
32 | ///
33 | private void Localize() {
34 | Text = Lang.Get("Settings");
35 | languageLabel.Text = Lang.Get("Language", ":");
36 | logRetentionLabel.Text = Lang.Get("LogRetention", ":");
37 | saveSettingsButton.Text = Lang.Get("SaveSettings");
38 | }
39 |
40 | ///
41 | /// event handler. Populates and and closes the form.
42 | ///
43 | private void SaveSettingsButton_Click(object sender, System.EventArgs e) {
44 | Language = Lang.AvailableLocales[languageComboBox.Text];
45 | LogRetention = (ushort)logRetentionNumericUpDown.Value;
46 | DialogResult = DialogResult.OK;
47 | Close();
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/RoboBackupGUI/TaskForm.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace RoboBackup
2 | {
3 | partial class TaskForm
4 | {
5 | private System.ComponentModel.IContainer components = null;
6 |
7 | protected override void Dispose(bool disposing)
8 | {
9 | if (disposing && (components != null))
10 | {
11 | components.Dispose();
12 | }
13 | base.Dispose(disposing);
14 | }
15 |
16 | #region Windows Form Designer generated code
17 |
18 | ///
19 | /// Required method for Designer support - do not modify
20 | /// the contents of this method with the code editor.
21 | ///
22 | private void InitializeComponent()
23 | {
24 | this.titleLabel = new System.Windows.Forms.Label();
25 | this.titleTextBox = new System.Windows.Forms.TextBox();
26 | this.sourceLabel = new System.Windows.Forms.Label();
27 | this.destinationLabel = new System.Windows.Forms.Label();
28 | this.fullRadioButton = new System.Windows.Forms.RadioButton();
29 | this.incrementalRadioButton = new System.Windows.Forms.RadioButton();
30 | this.sourceTreeView = new System.Windows.Forms.TreeView();
31 | this.sourceTextBox = new System.Windows.Forms.TextBox();
32 | this.sourceButton = new System.Windows.Forms.Button();
33 | this.destinationTextBox = new System.Windows.Forms.TextBox();
34 | this.destinationButton = new System.Windows.Forms.Button();
35 | this.differentialRadioButton = new System.Windows.Forms.RadioButton();
36 | this.retentionLabel = new System.Windows.Forms.Label();
37 | this.retentionNumericUpDown = new System.Windows.Forms.NumericUpDown();
38 | this.directoriesGroupBox = new System.Windows.Forms.GroupBox();
39 | this.methodGroupBox = new System.Windows.Forms.GroupBox();
40 | this.methodHelpButton = new System.Windows.Forms.Button();
41 | this.saveTaskButton = new System.Windows.Forms.Button();
42 | this.scheduleGroupBox = new System.Windows.Forms.GroupBox();
43 | this.timeDateTimePicker = new System.Windows.Forms.DateTimePicker();
44 | this.timeLabel = new System.Windows.Forms.Label();
45 | this.monthdayNumericUpDown = new System.Windows.Forms.NumericUpDown();
46 | this.dailyRadioButton = new System.Windows.Forms.RadioButton();
47 | this.weeklyRadioButton = new System.Windows.Forms.RadioButton();
48 | this.weekdayComboBox = new System.Windows.Forms.ComboBox();
49 | this.monthlyRadioButton = new System.Windows.Forms.RadioButton();
50 | this.networkCredentialsGroupBox = new System.Windows.Forms.GroupBox();
51 | this.passwordTextBox = new System.Windows.Forms.TextBox();
52 | this.usernameTextBox = new System.Windows.Forms.TextBox();
53 | this.passwordLabel = new System.Windows.Forms.Label();
54 | this.usernameLabel = new System.Windows.Forms.Label();
55 | this.sourceFolderBrowserDialog = new RoboBackup.OpenFolderDialog();
56 | this.destinationFolderBrowserDialog = new RoboBackup.OpenFolderDialog();
57 | ((System.ComponentModel.ISupportInitialize)(this.retentionNumericUpDown)).BeginInit();
58 | this.directoriesGroupBox.SuspendLayout();
59 | this.methodGroupBox.SuspendLayout();
60 | this.scheduleGroupBox.SuspendLayout();
61 | ((System.ComponentModel.ISupportInitialize)(this.monthdayNumericUpDown)).BeginInit();
62 | this.networkCredentialsGroupBox.SuspendLayout();
63 | this.SuspendLayout();
64 | //
65 | // titleLabel
66 | //
67 | this.titleLabel.AutoSize = true;
68 | this.titleLabel.Location = new System.Drawing.Point(18, 15);
69 | this.titleLabel.Name = "titleLabel";
70 | this.titleLabel.Size = new System.Drawing.Size(30, 13);
71 | this.titleLabel.TabIndex = 0;
72 | this.titleLabel.Text = "Title:";
73 | //
74 | // titleTextBox
75 | //
76 | this.titleTextBox.Location = new System.Drawing.Point(87, 12);
77 | this.titleTextBox.Name = "titleTextBox";
78 | this.titleTextBox.Size = new System.Drawing.Size(262, 20);
79 | this.titleTextBox.TabIndex = 1;
80 | //
81 | // sourceLabel
82 | //
83 | this.sourceLabel.AutoSize = true;
84 | this.sourceLabel.Location = new System.Drawing.Point(6, 22);
85 | this.sourceLabel.Name = "sourceLabel";
86 | this.sourceLabel.Size = new System.Drawing.Size(44, 13);
87 | this.sourceLabel.TabIndex = 0;
88 | this.sourceLabel.Text = "Source:";
89 | //
90 | // destinationLabel
91 | //
92 | this.destinationLabel.AutoSize = true;
93 | this.destinationLabel.Location = new System.Drawing.Point(6, 173);
94 | this.destinationLabel.Name = "destinationLabel";
95 | this.destinationLabel.Size = new System.Drawing.Size(63, 13);
96 | this.destinationLabel.TabIndex = 4;
97 | this.destinationLabel.Text = "Destination:";
98 | //
99 | // fullRadioButton
100 | //
101 | this.fullRadioButton.AutoSize = true;
102 | this.fullRadioButton.Location = new System.Drawing.Point(6, 72);
103 | this.fullRadioButton.Name = "fullRadioButton";
104 | this.fullRadioButton.Size = new System.Drawing.Size(41, 17);
105 | this.fullRadioButton.TabIndex = 2;
106 | this.fullRadioButton.Text = "Full";
107 | this.fullRadioButton.UseVisualStyleBackColor = true;
108 | this.fullRadioButton.CheckedChanged += new System.EventHandler(this.FullRadioButton_CheckedChanged);
109 | //
110 | // incrementalRadioButton
111 | //
112 | this.incrementalRadioButton.AutoSize = true;
113 | this.incrementalRadioButton.Checked = true;
114 | this.incrementalRadioButton.Location = new System.Drawing.Point(6, 20);
115 | this.incrementalRadioButton.Name = "incrementalRadioButton";
116 | this.incrementalRadioButton.Size = new System.Drawing.Size(80, 17);
117 | this.incrementalRadioButton.TabIndex = 0;
118 | this.incrementalRadioButton.TabStop = true;
119 | this.incrementalRadioButton.Text = "Incremental";
120 | this.incrementalRadioButton.UseVisualStyleBackColor = true;
121 | this.incrementalRadioButton.CheckedChanged += new System.EventHandler(this.IncrementalRadioButton_CheckedChanged);
122 | //
123 | // sourceTreeView
124 | //
125 | this.sourceTreeView.CheckBoxes = true;
126 | this.sourceTreeView.FullRowSelect = true;
127 | this.sourceTreeView.Location = new System.Drawing.Point(75, 45);
128 | this.sourceTreeView.Name = "sourceTreeView";
129 | this.sourceTreeView.Size = new System.Drawing.Size(255, 119);
130 | this.sourceTreeView.TabIndex = 3;
131 | this.sourceTreeView.AfterCheck += new System.Windows.Forms.TreeViewEventHandler(this.SourceTreeView_AfterCheck);
132 | //
133 | // sourceTextBox
134 | //
135 | this.sourceTextBox.BackColor = System.Drawing.SystemColors.Window;
136 | this.sourceTextBox.Location = new System.Drawing.Point(75, 19);
137 | this.sourceTextBox.Name = "sourceTextBox";
138 | this.sourceTextBox.ReadOnly = true;
139 | this.sourceTextBox.Size = new System.Drawing.Size(224, 20);
140 | this.sourceTextBox.TabIndex = 1;
141 | this.sourceTextBox.TextChanged += new System.EventHandler(this.SourceTextBox_TextChanged);
142 | //
143 | // sourceButton
144 | //
145 | this.sourceButton.Location = new System.Drawing.Point(305, 19);
146 | this.sourceButton.Name = "sourceButton";
147 | this.sourceButton.Size = new System.Drawing.Size(25, 21);
148 | this.sourceButton.TabIndex = 2;
149 | this.sourceButton.Text = "...";
150 | this.sourceButton.UseVisualStyleBackColor = true;
151 | this.sourceButton.Click += new System.EventHandler(this.SourceButton_Click);
152 | //
153 | // destinationTextBox
154 | //
155 | this.destinationTextBox.BackColor = System.Drawing.SystemColors.Window;
156 | this.destinationTextBox.Location = new System.Drawing.Point(75, 170);
157 | this.destinationTextBox.Name = "destinationTextBox";
158 | this.destinationTextBox.ReadOnly = true;
159 | this.destinationTextBox.Size = new System.Drawing.Size(224, 20);
160 | this.destinationTextBox.TabIndex = 5;
161 | this.destinationTextBox.TextChanged += new System.EventHandler(this.DestinationTextBox_TextChanged);
162 | //
163 | // destinationButton
164 | //
165 | this.destinationButton.Location = new System.Drawing.Point(305, 170);
166 | this.destinationButton.Name = "destinationButton";
167 | this.destinationButton.Size = new System.Drawing.Size(25, 21);
168 | this.destinationButton.TabIndex = 6;
169 | this.destinationButton.Text = "...";
170 | this.destinationButton.UseVisualStyleBackColor = true;
171 | this.destinationButton.Click += new System.EventHandler(this.DestinationButton_Click);
172 | //
173 | // differentialRadioButton
174 | //
175 | this.differentialRadioButton.AutoSize = true;
176 | this.differentialRadioButton.Location = new System.Drawing.Point(6, 46);
177 | this.differentialRadioButton.Name = "differentialRadioButton";
178 | this.differentialRadioButton.Size = new System.Drawing.Size(75, 17);
179 | this.differentialRadioButton.TabIndex = 1;
180 | this.differentialRadioButton.Text = "Differential";
181 | this.differentialRadioButton.UseVisualStyleBackColor = true;
182 | this.differentialRadioButton.CheckedChanged += new System.EventHandler(this.DifferentialRadioButton_CheckedChanged);
183 | //
184 | // retentionLabel
185 | //
186 | this.retentionLabel.AutoSize = true;
187 | this.retentionLabel.Location = new System.Drawing.Point(6, 99);
188 | this.retentionLabel.Name = "retentionLabel";
189 | this.retentionLabel.Size = new System.Drawing.Size(56, 13);
190 | this.retentionLabel.TabIndex = 3;
191 | this.retentionLabel.Text = "Retention:";
192 | //
193 | // retentionNumericUpDown
194 | //
195 | this.retentionNumericUpDown.Enabled = false;
196 | this.retentionNumericUpDown.Location = new System.Drawing.Point(95, 97);
197 | this.retentionNumericUpDown.Maximum = new decimal(new int[] {
198 | 99999,
199 | 0,
200 | 0,
201 | 0});
202 | this.retentionNumericUpDown.Minimum = new decimal(new int[] {
203 | 1,
204 | 0,
205 | 0,
206 | 0});
207 | this.retentionNumericUpDown.Name = "retentionNumericUpDown";
208 | this.retentionNumericUpDown.Size = new System.Drawing.Size(65, 20);
209 | this.retentionNumericUpDown.TabIndex = 4;
210 | this.retentionNumericUpDown.Value = new decimal(new int[] {
211 | 1,
212 | 0,
213 | 0,
214 | 0});
215 | //
216 | // directoriesGroupBox
217 | //
218 | this.directoriesGroupBox.Controls.Add(this.sourceLabel);
219 | this.directoriesGroupBox.Controls.Add(this.destinationLabel);
220 | this.directoriesGroupBox.Controls.Add(this.sourceTreeView);
221 | this.directoriesGroupBox.Controls.Add(this.sourceTextBox);
222 | this.directoriesGroupBox.Controls.Add(this.destinationButton);
223 | this.directoriesGroupBox.Controls.Add(this.sourceButton);
224 | this.directoriesGroupBox.Controls.Add(this.destinationTextBox);
225 | this.directoriesGroupBox.Location = new System.Drawing.Point(12, 38);
226 | this.directoriesGroupBox.Name = "directoriesGroupBox";
227 | this.directoriesGroupBox.Size = new System.Drawing.Size(339, 200);
228 | this.directoriesGroupBox.TabIndex = 2;
229 | this.directoriesGroupBox.TabStop = false;
230 | this.directoriesGroupBox.Text = "Directories";
231 | //
232 | // methodGroupBox
233 | //
234 | this.methodGroupBox.Controls.Add(this.methodHelpButton);
235 | this.methodGroupBox.Controls.Add(this.retentionLabel);
236 | this.methodGroupBox.Controls.Add(this.differentialRadioButton);
237 | this.methodGroupBox.Controls.Add(this.fullRadioButton);
238 | this.methodGroupBox.Controls.Add(this.retentionNumericUpDown);
239 | this.methodGroupBox.Controls.Add(this.incrementalRadioButton);
240 | this.methodGroupBox.Location = new System.Drawing.Point(12, 324);
241 | this.methodGroupBox.Name = "methodGroupBox";
242 | this.methodGroupBox.Size = new System.Drawing.Size(166, 125);
243 | this.methodGroupBox.TabIndex = 4;
244 | this.methodGroupBox.TabStop = false;
245 | this.methodGroupBox.Text = "Method";
246 | //
247 | // methodHelpButton
248 | //
249 | this.methodHelpButton.Image = global::RoboBackup.Properties.Resources.HelpIcon;
250 | this.methodHelpButton.Location = new System.Drawing.Point(136, 13);
251 | this.methodHelpButton.Name = "methodHelpButton";
252 | this.methodHelpButton.Size = new System.Drawing.Size(24, 24);
253 | this.methodHelpButton.TabIndex = 5;
254 | this.methodHelpButton.UseVisualStyleBackColor = true;
255 | this.methodHelpButton.Click += new System.EventHandler(this.MethodHelpButton_Click);
256 | //
257 | // saveTaskButton
258 | //
259 | this.saveTaskButton.Location = new System.Drawing.Point(12, 455);
260 | this.saveTaskButton.Name = "saveTaskButton";
261 | this.saveTaskButton.Size = new System.Drawing.Size(339, 23);
262 | this.saveTaskButton.TabIndex = 6;
263 | this.saveTaskButton.Text = "SaveTask";
264 | this.saveTaskButton.Click += new System.EventHandler(this.SaveTaskButton_Click);
265 | //
266 | // scheduleGroupBox
267 | //
268 | this.scheduleGroupBox.Controls.Add(this.timeDateTimePicker);
269 | this.scheduleGroupBox.Controls.Add(this.timeLabel);
270 | this.scheduleGroupBox.Controls.Add(this.monthdayNumericUpDown);
271 | this.scheduleGroupBox.Controls.Add(this.dailyRadioButton);
272 | this.scheduleGroupBox.Controls.Add(this.weeklyRadioButton);
273 | this.scheduleGroupBox.Controls.Add(this.weekdayComboBox);
274 | this.scheduleGroupBox.Controls.Add(this.monthlyRadioButton);
275 | this.scheduleGroupBox.Location = new System.Drawing.Point(185, 324);
276 | this.scheduleGroupBox.Name = "scheduleGroupBox";
277 | this.scheduleGroupBox.Size = new System.Drawing.Size(166, 125);
278 | this.scheduleGroupBox.TabIndex = 5;
279 | this.scheduleGroupBox.TabStop = false;
280 | this.scheduleGroupBox.Text = "Schedule";
281 | //
282 | // timeDateTimePicker
283 | //
284 | this.timeDateTimePicker.CustomFormat = "HH:mm";
285 | this.timeDateTimePicker.Format = System.Windows.Forms.DateTimePickerFormat.Custom;
286 | this.timeDateTimePicker.Location = new System.Drawing.Point(106, 97);
287 | this.timeDateTimePicker.Name = "timeDateTimePicker";
288 | this.timeDateTimePicker.ShowUpDown = true;
289 | this.timeDateTimePicker.Size = new System.Drawing.Size(51, 20);
290 | this.timeDateTimePicker.TabIndex = 6;
291 | this.timeDateTimePicker.Value = new System.DateTime(2018, 1, 1, 0, 0, 0, 0);
292 | //
293 | // timeLabel
294 | //
295 | this.timeLabel.AutoSize = true;
296 | this.timeLabel.Location = new System.Drawing.Point(6, 100);
297 | this.timeLabel.Name = "timeLabel";
298 | this.timeLabel.Size = new System.Drawing.Size(43, 13);
299 | this.timeLabel.TabIndex = 5;
300 | this.timeLabel.Text = "AtTime:";
301 | //
302 | // monthdayNumericUpDown
303 | //
304 | this.monthdayNumericUpDown.Location = new System.Drawing.Point(106, 72);
305 | this.monthdayNumericUpDown.Maximum = new decimal(new int[] {
306 | 31,
307 | 0,
308 | 0,
309 | 0});
310 | this.monthdayNumericUpDown.Minimum = new decimal(new int[] {
311 | 1,
312 | 0,
313 | 0,
314 | 0});
315 | this.monthdayNumericUpDown.Name = "monthdayNumericUpDown";
316 | this.monthdayNumericUpDown.Size = new System.Drawing.Size(51, 20);
317 | this.monthdayNumericUpDown.TabIndex = 4;
318 | this.monthdayNumericUpDown.Value = new decimal(new int[] {
319 | 1,
320 | 0,
321 | 0,
322 | 0});
323 | this.monthdayNumericUpDown.ValueChanged += new System.EventHandler(this.MonthdayNumericUpDown_ValueChanged);
324 | //
325 | // dailyRadioButton
326 | //
327 | this.dailyRadioButton.AutoSize = true;
328 | this.dailyRadioButton.Checked = true;
329 | this.dailyRadioButton.Location = new System.Drawing.Point(6, 20);
330 | this.dailyRadioButton.Name = "dailyRadioButton";
331 | this.dailyRadioButton.Size = new System.Drawing.Size(48, 17);
332 | this.dailyRadioButton.TabIndex = 0;
333 | this.dailyRadioButton.TabStop = true;
334 | this.dailyRadioButton.Text = "Daily";
335 | this.dailyRadioButton.UseVisualStyleBackColor = true;
336 | //
337 | // weeklyRadioButton
338 | //
339 | this.weeklyRadioButton.AutoSize = true;
340 | this.weeklyRadioButton.Location = new System.Drawing.Point(6, 46);
341 | this.weeklyRadioButton.Name = "weeklyRadioButton";
342 | this.weeklyRadioButton.Size = new System.Drawing.Size(87, 17);
343 | this.weeklyRadioButton.TabIndex = 1;
344 | this.weeklyRadioButton.Text = "DayOfWeek:";
345 | this.weeklyRadioButton.UseVisualStyleBackColor = true;
346 | //
347 | // weekdayComboBox
348 | //
349 | this.weekdayComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
350 | this.weekdayComboBox.Location = new System.Drawing.Point(106, 45);
351 | this.weekdayComboBox.Name = "weekdayComboBox";
352 | this.weekdayComboBox.Size = new System.Drawing.Size(51, 21);
353 | this.weekdayComboBox.TabIndex = 2;
354 | this.weekdayComboBox.SelectedIndexChanged += new System.EventHandler(this.WeekdayComboBox_SelectedIndexChanged);
355 | //
356 | // monthlyRadioButton
357 | //
358 | this.monthlyRadioButton.AutoSize = true;
359 | this.monthlyRadioButton.Location = new System.Drawing.Point(6, 72);
360 | this.monthlyRadioButton.Name = "monthlyRadioButton";
361 | this.monthlyRadioButton.Size = new System.Drawing.Size(88, 17);
362 | this.monthlyRadioButton.TabIndex = 3;
363 | this.monthlyRadioButton.Text = "DayOfMonth:";
364 | this.monthlyRadioButton.UseVisualStyleBackColor = true;
365 | //
366 | // networkCredentialsGroupBox
367 | //
368 | this.networkCredentialsGroupBox.Controls.Add(this.passwordTextBox);
369 | this.networkCredentialsGroupBox.Controls.Add(this.usernameTextBox);
370 | this.networkCredentialsGroupBox.Controls.Add(this.passwordLabel);
371 | this.networkCredentialsGroupBox.Controls.Add(this.usernameLabel);
372 | this.networkCredentialsGroupBox.Location = new System.Drawing.Point(12, 244);
373 | this.networkCredentialsGroupBox.Name = "networkCredentialsGroupBox";
374 | this.networkCredentialsGroupBox.Size = new System.Drawing.Size(339, 74);
375 | this.networkCredentialsGroupBox.TabIndex = 3;
376 | this.networkCredentialsGroupBox.TabStop = false;
377 | this.networkCredentialsGroupBox.Text = "NetworkCredentials";
378 | //
379 | // passwordTextBox
380 | //
381 | this.passwordTextBox.Enabled = false;
382 | this.passwordTextBox.Location = new System.Drawing.Point(75, 45);
383 | this.passwordTextBox.Name = "passwordTextBox";
384 | this.passwordTextBox.Size = new System.Drawing.Size(255, 20);
385 | this.passwordTextBox.TabIndex = 3;
386 | this.passwordTextBox.UseSystemPasswordChar = true;
387 | //
388 | // usernameTextBox
389 | //
390 | this.usernameTextBox.Enabled = false;
391 | this.usernameTextBox.Location = new System.Drawing.Point(75, 19);
392 | this.usernameTextBox.Name = "usernameTextBox";
393 | this.usernameTextBox.Size = new System.Drawing.Size(255, 20);
394 | this.usernameTextBox.TabIndex = 1;
395 | //
396 | // passwordLabel
397 | //
398 | this.passwordLabel.AutoSize = true;
399 | this.passwordLabel.Location = new System.Drawing.Point(6, 48);
400 | this.passwordLabel.Name = "passwordLabel";
401 | this.passwordLabel.Size = new System.Drawing.Size(56, 13);
402 | this.passwordLabel.TabIndex = 2;
403 | this.passwordLabel.Text = "Password:";
404 | //
405 | // usernameLabel
406 | //
407 | this.usernameLabel.AutoSize = true;
408 | this.usernameLabel.Location = new System.Drawing.Point(6, 22);
409 | this.usernameLabel.Name = "usernameLabel";
410 | this.usernameLabel.Size = new System.Drawing.Size(58, 13);
411 | this.usernameLabel.TabIndex = 0;
412 | this.usernameLabel.Text = "Username:";
413 | //
414 | // sourceFolderBrowserDialog
415 | //
416 | this.sourceFolderBrowserDialog.SelectedPath = null;
417 | //
418 | // destinationFolderBrowserDialog
419 | //
420 | this.destinationFolderBrowserDialog.SelectedPath = null;
421 | //
422 | // TaskForm
423 | //
424 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
425 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
426 | this.ClientSize = new System.Drawing.Size(361, 485);
427 | this.Controls.Add(this.networkCredentialsGroupBox);
428 | this.Controls.Add(this.scheduleGroupBox);
429 | this.Controls.Add(this.saveTaskButton);
430 | this.Controls.Add(this.methodGroupBox);
431 | this.Controls.Add(this.directoriesGroupBox);
432 | this.Controls.Add(this.titleTextBox);
433 | this.Controls.Add(this.titleLabel);
434 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
435 | this.MaximizeBox = false;
436 | this.MinimizeBox = false;
437 | this.Name = "TaskForm";
438 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
439 | this.Text = "Task";
440 | ((System.ComponentModel.ISupportInitialize)(this.retentionNumericUpDown)).EndInit();
441 | this.directoriesGroupBox.ResumeLayout(false);
442 | this.directoriesGroupBox.PerformLayout();
443 | this.methodGroupBox.ResumeLayout(false);
444 | this.methodGroupBox.PerformLayout();
445 | this.scheduleGroupBox.ResumeLayout(false);
446 | this.scheduleGroupBox.PerformLayout();
447 | ((System.ComponentModel.ISupportInitialize)(this.monthdayNumericUpDown)).EndInit();
448 | this.networkCredentialsGroupBox.ResumeLayout(false);
449 | this.networkCredentialsGroupBox.PerformLayout();
450 | this.ResumeLayout(false);
451 | this.PerformLayout();
452 |
453 | }
454 |
455 | #endregion
456 |
457 | private System.Windows.Forms.Label titleLabel;
458 | private System.Windows.Forms.TextBox titleTextBox;
459 | private System.Windows.Forms.Label sourceLabel;
460 | private System.Windows.Forms.Label destinationLabel;
461 | private System.Windows.Forms.RadioButton fullRadioButton;
462 | private System.Windows.Forms.RadioButton incrementalRadioButton;
463 | private System.Windows.Forms.TreeView sourceTreeView;
464 | private System.Windows.Forms.TextBox sourceTextBox;
465 | private System.Windows.Forms.Button sourceButton;
466 | private System.Windows.Forms.TextBox destinationTextBox;
467 | private System.Windows.Forms.Button destinationButton;
468 | private System.Windows.Forms.RadioButton differentialRadioButton;
469 | private System.Windows.Forms.Label retentionLabel;
470 | private System.Windows.Forms.NumericUpDown retentionNumericUpDown;
471 | private System.Windows.Forms.GroupBox directoriesGroupBox;
472 | private System.Windows.Forms.GroupBox methodGroupBox;
473 | private System.Windows.Forms.Button saveTaskButton;
474 | private System.Windows.Forms.GroupBox scheduleGroupBox;
475 | private System.Windows.Forms.Label timeLabel;
476 | private System.Windows.Forms.NumericUpDown monthdayNumericUpDown;
477 | private System.Windows.Forms.ComboBox weekdayComboBox;
478 | private System.Windows.Forms.RadioButton monthlyRadioButton;
479 | private System.Windows.Forms.RadioButton weeklyRadioButton;
480 | private System.Windows.Forms.RadioButton dailyRadioButton;
481 | private System.Windows.Forms.DateTimePicker timeDateTimePicker;
482 | private System.Windows.Forms.Button methodHelpButton;
483 | private OpenFolderDialog sourceFolderBrowserDialog;
484 | private OpenFolderDialog destinationFolderBrowserDialog;
485 | private System.Windows.Forms.GroupBox networkCredentialsGroupBox;
486 | private System.Windows.Forms.TextBox passwordTextBox;
487 | private System.Windows.Forms.TextBox usernameTextBox;
488 | private System.Windows.Forms.Label passwordLabel;
489 | private System.Windows.Forms.Label usernameLabel;
490 | }
491 | }
492 |
--------------------------------------------------------------------------------
/RoboBackupGUI/TaskForm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Windows.Forms;
4 |
5 | namespace RoboBackup {
6 | ///
7 | /// Form to create or edit backup tasks details.
8 | ///
9 | public partial class TaskForm : Form {
10 | ///
11 | /// returned by successfully completed form.
12 | ///
13 | public Task ResultTask { get; private set; }
14 |
15 | ///
16 | /// to be reused when updating a .
17 | ///
18 | private string _guid;
19 |
20 | ///
21 | /// Initializes a new instance of the class.
22 | ///
23 | public TaskForm() {
24 | InitializeComponent();
25 | Localize();
26 | }
27 |
28 | ///
29 | /// Initializes a new instance of the class and populates form controls with the properties.
30 | ///
31 | /// to be edited.
32 | public TaskForm(Task task) : this() {
33 | _guid = task.Guid;
34 | titleTextBox.Text = task.Title;
35 | sourceFolderBrowserDialog.SelectedPath = sourceTextBox.Text = task.Source;
36 | PopulateSourceTreeView();
37 | foreach (string path in task.ExcludedDirs) {
38 | TreeNode node = DirectoryTree.FindNode(sourceTreeView.Nodes, path);
39 | if (node != null) {
40 | node.Checked = false;
41 | }
42 | }
43 | foreach (string path in task.ExcludedFiles) {
44 | TreeNode node = DirectoryTree.FindNode(sourceTreeView.Nodes, path);
45 | if (node != null) {
46 | node.Checked = false;
47 | }
48 | }
49 | destinationFolderBrowserDialog.SelectedPath = destinationTextBox.Text = task.Destination;
50 | if (!string.IsNullOrEmpty(task.Credential)) {
51 | usernameTextBox.Text = Lang.Get("ReenterCredentials");
52 | }
53 | if (task.Method == Method.Differential) {
54 | differentialRadioButton.Checked = true;
55 | retentionNumericUpDown.Value = task.Retention;
56 | } else if (task.Method == Method.Full) {
57 | fullRadioButton.Checked = true;
58 | retentionNumericUpDown.Value = task.Retention;
59 | }
60 | if (task.Period == Period.Weekly) {
61 | weekdayComboBox.SelectedIndex = (int)task.DayOfWeek;
62 | } else if (task.Period == Period.Monthly) {
63 | monthdayNumericUpDown.Value = task.DayOfMonth;
64 | }
65 | timeDateTimePicker.Value = DateTime.Today.AddHours(task.Hour).AddMinutes(task.Minute); ;
66 | }
67 |
68 | ///
69 | /// Enables or disables network credentials controls based on the source and destination directory UNC conformance.
70 | ///
71 | private void CheckUncPath() {
72 | bool unc = Unc.IsUncPath(sourceTextBox.Text) || Unc.IsUncPath(destinationTextBox.Text);
73 | usernameTextBox.Enabled = unc;
74 | passwordTextBox.Enabled = unc;
75 | if (!unc) {
76 | usernameTextBox.Text = string.Empty;
77 | passwordTextBox.Text = string.Empty;
78 | }
79 | }
80 |
81 | ///
82 | /// Collects form control values and creates a form result.
83 | ///
84 | private void CreateTask() {
85 | _guid = _guid ?? Guid.NewGuid().ToString();
86 | Task task = new Task() { Guid = _guid, Title = titleTextBox.Text, Source = sourceTextBox.Text, Destination = destinationTextBox.Text };
87 | List excludedNodes = DirectoryTree.GetExcludedNodes(sourceTreeView.Nodes);
88 | List excludedDirs = new List();
89 | List excludedFiles = new List();
90 | foreach (TreeNode node in excludedNodes) {
91 | if ((EntryType)node.Tag == EntryType.Directory) {
92 | excludedDirs.Add(node.FullPath);
93 | } else {
94 | excludedFiles.Add(node.FullPath);
95 | }
96 | }
97 | task.ExcludedDirs = excludedDirs.ToArray();
98 | task.ExcludedFiles = excludedFiles.ToArray();
99 | task.Credential = Credential.Encrypt(_guid, new Credential() { Username = usernameTextBox.Text, Password = passwordTextBox.Text });
100 | if (incrementalRadioButton.Checked) {
101 | task.Method = Method.Incremental;
102 | task.Retention = 1;
103 | } else if (differentialRadioButton.Checked) {
104 | task.Method = Method.Differential;
105 | task.Retention = (ushort)retentionNumericUpDown.Value;
106 | } else {
107 | task.Method = Method.Full;
108 | task.Retention = (ushort)retentionNumericUpDown.Value;
109 | }
110 | if (dailyRadioButton.Checked) {
111 | task.Period = Period.Daily;
112 | } else if (weeklyRadioButton.Checked) {
113 | task.Period = Period.Weekly;
114 | task.DayOfWeek = (DayOfWeek)weekdayComboBox.SelectedIndex;
115 | } else {
116 | task.Period = Period.Monthly;
117 | task.DayOfMonth = (byte)monthdayNumericUpDown.Value;
118 | }
119 | task.Hour = (byte)timeDateTimePicker.Value.Hour;
120 | task.Minute = (byte)timeDateTimePicker.Value.Minute;
121 | ResultTask = task;
122 | }
123 |
124 | ///
125 | /// event handler. Displays destination folder selection dialog.
126 | ///
127 | private void DestinationButton_Click(object sender, EventArgs e) {
128 | if (destinationFolderBrowserDialog.ShowDialog() == DialogResult.OK) {
129 | destinationTextBox.Text = destinationFolderBrowserDialog.SelectedPath = Unc.TranslatePath(destinationFolderBrowserDialog.SelectedPath);
130 | }
131 | }
132 |
133 | ///
134 | /// event handler. Enables control.
135 | ///
136 | private void DifferentialRadioButton_CheckedChanged(object sender, EventArgs e) {
137 | retentionNumericUpDown.Enabled = true;
138 | }
139 |
140 | ///
141 | /// event handler. Enables or disables network credentials input fields.
142 | ///
143 | private void DestinationTextBox_TextChanged(object sender, EventArgs e) {
144 | CheckUncPath();
145 | }
146 |
147 | ///
148 | /// event handler. Enables control.
149 | ///
150 | private void FullRadioButton_CheckedChanged(object sender, EventArgs e) {
151 | retentionNumericUpDown.Enabled = true;
152 | }
153 |
154 | ///
155 | /// event handler. Disables control.
156 | ///
157 | private void IncrementalRadioButton_CheckedChanged(object sender, EventArgs e) {
158 | retentionNumericUpDown.Enabled = false;
159 | }
160 |
161 | ///
162 | /// Translates the form strings to the language set by .
163 | ///
164 | private void Localize() {
165 | Text = Lang.Get("Task");
166 | titleLabel.Text = Lang.Get("Title", ":");
167 | directoriesGroupBox.Text = Lang.Get("Directories");
168 | sourceLabel.Text = Lang.Get("Source", ":");
169 | destinationLabel.Text = Lang.Get("Destination", ":");
170 | networkCredentialsGroupBox.Text = Lang.Get("NetworkCredentials");
171 | usernameLabel.Text = Lang.Get("Username", ":");
172 | passwordLabel.Text = Lang.Get("Password", ":");
173 | methodGroupBox.Text = Lang.Get("Method");
174 | incrementalRadioButton.Text = Lang.Get("Incremental");
175 | differentialRadioButton.Text = Lang.Get("Differential");
176 | fullRadioButton.Text = Lang.Get("Full");
177 | retentionLabel.Text = Lang.Get("Retention", ":");
178 | scheduleGroupBox.Text = Lang.Get("Schedule");
179 | dailyRadioButton.Text = Lang.Get("Daily");
180 | weeklyRadioButton.Text = Lang.Get("DayOfWeek", ":");
181 | monthlyRadioButton.Text = Lang.Get("DayOfMonth", ":");
182 | timeLabel.Text = Lang.Get("AtTime", ":");
183 | saveTaskButton.Text = Lang.Get("SaveTask");
184 | foreach (DayOfWeek dow in Enum.GetValues(typeof(DayOfWeek))) {
185 | weekdayComboBox.Items.Add(Lang.Get(dow.ToString()));
186 | }
187 | weekdayComboBox.Text = Lang.Get(DayOfWeek.Sunday.ToString());
188 | dailyRadioButton.Checked = true;
189 | }
190 |
191 | ///
192 | /// event handler. Displays a with backup methods explanation.
193 | ///
194 | private void MethodHelpButton_Click(object sender, EventArgs e) {
195 | string message = string.Format("{0}\n\n{1}\n\n{2}\n\n{3}", Lang.Get("HelpIncremental"), Lang.Get("HelpDifferential"), Lang.Get("HelpFull"), Lang.Get("HelpRetention"));
196 | MessageBox.Show(message, Lang.Get("Help"), MessageBoxButtons.OK, MessageBoxIcon.Question);
197 | }
198 |
199 | ///
200 | /// monthdayNumericUpDown.ValueChanged event handler. Switches schedule selection to "monthly" when the day of month value changes.
201 | ///
202 | private void MonthdayNumericUpDown_ValueChanged(object sender, EventArgs e) {
203 | monthlyRadioButton.Checked = true;
204 | }
205 |
206 | ///
207 | /// Populates the with nodes corresponding to filesystem objects located under the directory selected by .
208 | ///
209 | private void PopulateSourceTreeView() {
210 | sourceTreeView.Nodes.Clear();
211 | sourceTreeView.Nodes.AddRange(DirectoryTree.LoadNodes(sourceFolderBrowserDialog.SelectedPath));
212 | }
213 |
214 | ///
215 | /// saveTaskButton.Click event handler. Populates and closes the form.
216 | ///
217 | private void SaveTaskButton_Click(object sender, EventArgs e) {
218 | if (string.IsNullOrEmpty(titleTextBox.Text) || string.IsNullOrEmpty(sourceTextBox.Text) || string.IsNullOrEmpty(destinationTextBox.Text)) {
219 | MessageBox.Show(Lang.Get("IncompleteTaskForm"), Lang.Get("Error"), MessageBoxButtons.OK, MessageBoxIcon.Error);
220 | return;
221 | }
222 | CreateTask();
223 | DialogResult = DialogResult.OK;
224 | Close();
225 | }
226 |
227 | ///
228 | /// event handler. Displays source folder selection dialog.
229 | ///
230 | private void SourceButton_Click(object sender, EventArgs e) {
231 | if (sourceFolderBrowserDialog.ShowDialog() == DialogResult.OK) {
232 | sourceTextBox.Text = sourceFolderBrowserDialog.SelectedPath = Unc.TranslatePath(sourceFolderBrowserDialog.SelectedPath);
233 | PopulateSourceTreeView();
234 | }
235 | }
236 |
237 | ///
238 | /// event handler. Enables or disables network credentials input fields.
239 | ///
240 | private void SourceTextBox_TextChanged(object sender, EventArgs e) {
241 | CheckUncPath();
242 | }
243 |
244 | ///
245 | /// event handler. Toggles for the of the node which received the click.
246 | ///
247 | private void SourceTreeView_AfterCheck(object sender, TreeViewEventArgs e) {
248 | DirectoryTree.ToggleChildren(e.Node, e.Node.Checked);
249 | }
250 |
251 | ///
252 | /// event handler. Switches schedule selection to "weekly" when the day of week value changes.
253 | ///
254 | private void WeekdayComboBox_SelectedIndexChanged(object sender, EventArgs e) {
255 | weeklyRadioButton.Checked = true;
256 | }
257 | }
258 | }
259 |
--------------------------------------------------------------------------------
/RoboBackupGUI/lang/cs.txt:
--------------------------------------------------------------------------------
1 | =Česky
2 | About=O programu
3 | AtTime=V čase
4 | Author=Autor
5 | BackupStarted=Požadavek na okamžité spuštění zálohy byl odeslán službě.
6 | Daily=Denně
7 | DailyAbbr=Denně
8 | DayOfMonth=Den v měsíci
9 | DayOfWeek=Den v týdnu
10 | DeleteConfirmation=Skutečně chcete označenou úlohu smazat?
11 | DeleteTask=Smazat úlohu
12 | Destination=Cíl
13 | Differential=Rozdílová
14 | DifferentialAbbr=Roz.
15 | Directories=Adresáře
16 | EditTask=Upravit úlohu
17 | Friday=Pá
18 | Full=Plná
19 | FullAbbr=Plná
20 | Help=Nápověda
21 | HelpDifferential=Rozdílová = Každý běh vytvoří podadresář s časovým razítkem v cílovém adresáři. Soubory jsou "hardlinkovány" z podadresáře předchozí zálohy (pokud existuje) a následně jsou synchronizovány rozdíly se zdrojovým adresářem.
22 | HelpFull=Plná = Každý běh vytvoří podadresář s časovým razítkem v cílovém adresáři. Soubory jsou pokaždé kopírovány ze zdrojového adresáře.
23 | HelpIncremental=Přírůstková = Jediný cílový adresář. Nové a změněné soubory jsou ze zdrojového adresáře zkopírovány do cílového adresáře. Soubory smazané ve zdrojovém adresáři jsou ponechány (nejsou smazány) v cílovém adresáři.
24 | HelpRetention=Retence je uvedena jako počet podadresářů s časovými razítky. Při dosažení limitu bude nejstarší adresář smazán. S retencí nastavenou na 1 nevytváří rozdílová a plná metoda podadresáře s časovými razítky, ale pouze synchronizuje obsah zdrojového adresáře s cílovým adresářem.
25 | IncompleteTaskForm=Vyplňte prosím název, zdroj a cíl.
26 | Incremental=Přírůstková
27 | IncrementalAbbr=Pří.
28 | Information=Informace
29 | Language=Jazyk
30 | LogRetention=Retence logů (dny)
31 | Method=Způsob
32 | Monday=Po
33 | MonthlyAbbr=Měs.
34 | NetworkCredentials=Síťová pověření
35 | NewTask=Nová úloha
36 | NoTaskLogs=Tato úloha ještě nemá žádné záznamy.
37 | Password=Heslo
38 | Question=Otázka
39 | ReenterCredentials=Před uložením znovu zadejte pověření
40 | Retention=Retence
41 | RunTask=Ihned spustit úlohu
42 | Saturday=So
43 | SaveSettings=Uložit nastavení
44 | SaveTask=Uložit úlohu
45 | Schedule=Plán
46 | Settings=Nastavení
47 | ShowTaskLogs=Zobrazit logy úlohy
48 | Source=Zdroj
49 | SourceCode=Zdrojový kód
50 | Sunday=Ne
51 | Task=Úloha
52 | Thursday=Čt
53 | Title=Název
54 | Tuesday=Út
55 | UnableToConnectService=Nepodařilo se připojit ke službě.
56 | Username=Uživatel
57 | Version=Verze
58 | Wednesday=St
59 | WeeklyAbbr=Týdně
60 |
--------------------------------------------------------------------------------
/RoboBackupGUI/lang/en.txt:
--------------------------------------------------------------------------------
1 | =English
2 | About=About
3 | AtTime=At time
4 | Author=Author
5 | BackupStarted=Request for immediate backup execution was sent to the service.
6 | Daily=Daily
7 | DailyAbbr=Daily
8 | DayOfMonth=Day of month
9 | DayOfWeek=Den of week
10 | DeleteConfirmation=Do you really want do delete selected task?
11 | DeleteTask=Delete task
12 | Destination=Destination
13 | Differential=Differential
14 | DifferentialAbbr=Diff
15 | Directories=Directories
16 | EditTask=Edit task
17 | Friday=Fri
18 | Full=Full
19 | FullAbbr=Full
20 | Help=Help
21 | HelpDifferential=Differential = Timestamped subdirectory under destination directory for each run. Files are hardlinked from the subdirectory of the previous backup (if there is one) and then the differences are synchronized from the source directory.
22 | HelpFull=Full = Timestamped subdirectory under destination directory for each run. Files are always fully copied from the source directory.
23 | HelpIncremental=Incremental = Single destination directory. New and modified files in the source directory are copied to the destination directory. Files deleted from the source directory are kept (not deleted) in the destination directory.
24 | HelpRetention=Retention is set as number of timestamped subdirectories. When the limit is reached, the oldest directory will be deleted. With retention set to 1, differential and full methods don't create timestamped directories and only synchronize the contents of the source directory to the destination directory.
25 | IncompleteTaskForm=Please fill in title, source and destination.
26 | Incremental=Incremental
27 | IncrementalAbbr=Inc
28 | Information=Information
29 | Language=Language
30 | LogRetention=Log retention (days)
31 | Method=Method
32 | Monday=Mon
33 | MonthlyAbbr=Monthly
34 | NetworkCredentials=Network credentials
35 | NewTask=New task
36 | NoTaskLogs=This task has no logs yet.
37 | Password=Password
38 | Question=Question
39 | ReenterCredentials=Re-enter credentials before saving
40 | Retention=Retention
41 | RunTask=Run task now
42 | Saturday=Sat
43 | SaveSettings=Save settings
44 | SaveTask=Save task
45 | Schedule=Schedule
46 | Settings=Settings
47 | ShowTaskLogs=Show task logs
48 | Source=Source
49 | SourceCode=Source code
50 | Sunday=Sun
51 | Task=Task
52 | Thursday=Thu
53 | Title=Title
54 | Tuesday=Tue
55 | UnableToConnectService=Unable to connect to the service.
56 | Username=Username
57 | Version=Version
58 | Wednesday=Wed
59 | WeeklyAbbr=Weekly
60 |
--------------------------------------------------------------------------------
/RoboBackupService/Config.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Reflection;
6 | using System.Xml.Serialization;
7 |
8 | namespace RoboBackup {
9 | ///
10 | /// Application configuration for both GUI and service parts.
11 | ///
12 | public static class Config {
13 | ///
14 | /// Full path to the language directory.
15 | ///
16 | public static string LangRoot { get; private set; }
17 |
18 | ///
19 | /// ISO 639-1 code of the selected language.
20 | ///
21 | public static string Language { get; set; } = "en";
22 |
23 | ///
24 | /// Log retention set in days.
25 | ///
26 | public static ushort LogRetention { get; set; } = 30;
27 |
28 | ///
29 | /// Full path to the log directory.
30 | ///
31 | public static string LogRoot { get; private set; }
32 |
33 | ///
34 | /// Collection of all backup tasks. Key is the , Value is the .
35 | ///
36 | public static Dictionary Tasks { get; private set; } = new Dictionary();
37 |
38 | ///
39 | /// Full path to the configuration file.
40 | ///
41 | private static readonly string _configFile;
42 |
43 | ///
44 | /// XML Serializer for serialization/deserialization for configuration saving/loading.
45 | ///
46 | private static XmlSerializer _xmlSerializer = new XmlSerializer(typeof(SerializedConfig));
47 |
48 | ///
49 | /// Static constructor to populate the file and directory paths according to the executable location.
50 | ///
51 | static Config() {
52 | string exeDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
53 | string progData = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
54 | _configFile = Path.Combine(progData, "RoboBackup/config.xml");
55 | LangRoot = Path.Combine(exeDir, "lang");
56 | LogRoot = Path.Combine(progData, "RoboBackup/logs");
57 | Directory.CreateDirectory(LogRoot);
58 | }
59 |
60 | ///
61 | /// Loads configuration from the default XML file.
62 | ///
63 | public static void Load() {
64 | Logger.LogConfigLoad();
65 | if (File.Exists(_configFile)) {
66 | using (StreamReader reader = new StreamReader(_configFile)) {
67 | SerializedConfig serializedConfig = (SerializedConfig)_xmlSerializer.Deserialize(reader);
68 | Language = serializedConfig.Language;
69 | LogRetention = serializedConfig.LogRetention;
70 | Tasks = serializedConfig.Tasks.ToDictionary(task => task.Guid, task => task);
71 | }
72 | }
73 | Logger.LogConfig();
74 | }
75 |
76 | ///
77 | /// saves configuration into the default XML file.
78 | ///
79 | public static void Save() {
80 | SerializedConfig serializedConfig = new SerializedConfig() { Language = Language, LogRetention = LogRetention, Tasks = Tasks.Values.ToArray() };
81 | using (StreamWriter writer = new StreamWriter(_configFile)) {
82 | _xmlSerializer.Serialize(writer, serializedConfig);
83 | }
84 | }
85 |
86 | ///
87 | /// Helper struct for XML serialization/deserialization.
88 | ///
89 | [Serializable]
90 | public struct SerializedConfig {
91 | ///
92 | /// ISO 639-1 code of the selected language.
93 | ///
94 | public string Language;
95 |
96 | ///
97 | /// Log retention set in days.
98 | ///
99 | public ushort LogRetention;
100 |
101 | ///
102 | /// Array of all backup tasks.
103 | ///
104 | public Task[] Tasks;
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/RoboBackupService/Credential.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Linq;
4 | using System.Security.Cryptography;
5 | using System.Text;
6 |
7 | namespace RoboBackup {
8 | ///
9 | /// Network credentials for use with UNC paths.
10 | ///
11 | [Serializable]
12 | public class Credential {
13 | ///
14 | /// Username associated with the credential.
15 | ///
16 | public string Username { get; set; }
17 |
18 | ///
19 | /// Password associated with the credential.
20 | ///
21 | public string Password { get; set; }
22 |
23 | ///
24 | /// Decrypts using AES128-CBC with as password for PBKDF2.
25 | ///
26 | /// to be used as PBKDF2 password.
27 | /// with encrypted .
28 | /// Decrypted .
29 | public static Credential Decrypt(string guid, string credential) {
30 | if (string.IsNullOrEmpty(credential)) {
31 | return null;
32 | }
33 | byte[] cipherbytes = Convert.FromBase64String(credential);
34 | byte[] salt = new byte[16];
35 | byte[] iv = new byte[16];
36 | Array.Copy(cipherbytes, salt, 16);
37 | Array.Copy(cipherbytes, 16, iv, 0, 16);
38 | using (AesManaged aes = new AesManaged() { KeySize = 128 } )
39 | using (Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(guid, salt, 1024))
40 | using (ICryptoTransform decryptor = aes.CreateDecryptor(pbkdf2.GetBytes(16), iv))
41 | using (MemoryStream memoryStream = new MemoryStream())
42 | using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Write))
43 | using (BinaryWriter writer = new BinaryWriter(cryptoStream)) {
44 | writer.Write(cipherbytes, 32, cipherbytes.Length - 32);
45 | cryptoStream.FlushFinalBlock();
46 | return Deserialize(Encoding.UTF8.GetString(memoryStream.ToArray()));
47 | }
48 | }
49 |
50 | ///
51 | /// Encrypts using AES128-CBC with as password for PBKDF2.
52 | ///
53 | /// to be used as PBKDF2 password.
54 | /// to be encrypted.
55 | /// with encrypted .
56 | /// This is really more obfuscation than encryption as all compounds are in the config, but it's still good enough as a defense against prying eyes.
57 | /// Security-wise this isn't worse than using native Windows Credential Manager as anyone can read passwords from that one too.
58 | public static string Encrypt(string guid, Credential credential) {
59 | if (string.IsNullOrEmpty(credential.Username) && string.IsNullOrEmpty(credential.Password)) {
60 | return null;
61 | }
62 | using (AesManaged aes = new AesManaged() { KeySize = 128 } ) {
63 | aes.GenerateIV();
64 | using (Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(guid, 16, 1024))
65 | using (ICryptoTransform encryptor = aes.CreateEncryptor(pbkdf2.GetBytes(16), aes.IV))
66 | using (MemoryStream memoryStream = new MemoryStream())
67 | using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
68 | using (BinaryWriter writer = new BinaryWriter(cryptoStream)) {
69 | writer.Write(Encoding.UTF8.GetBytes(Serialize(credential)));
70 | cryptoStream.FlushFinalBlock();
71 | return Convert.ToBase64String(pbkdf2.Salt.Concat(aes.IV).Concat(memoryStream.ToArray()).ToArray());
72 | }
73 | }
74 | }
75 |
76 | ///
77 | /// Deserializes into a .
78 | ///
79 | /// to be deserialized.
80 | /// Deserialized .
81 | private static Credential Deserialize(string credential) {
82 | string[] split = credential.Split(new char[] { '\n' }, 2);
83 | return new Credential() { Username = split[0], Password = split[1] };
84 | }
85 |
86 | ///
87 | /// Serializes into a .
88 | ///
89 | /// The to be serialized.
90 | /// Serialized as .
91 | private static string Serialize(Credential credential) {
92 | return string.Format("{0}\n{1}", credential.Username, credential.Password);
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/RoboBackupService/IpcServer.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using System.Runtime.Remoting;
3 | using System.Runtime.Remoting.Channels;
4 | using System.Runtime.Remoting.Channels.Ipc;
5 |
6 | namespace RoboBackup {
7 | ///
8 | /// Server side for interprocess communication via named pipe between the GUI (client) and the service (server).
9 | ///
10 | public static class IpcServer {
11 | ///
12 | /// Registers interprocess communication server channel and the object proxy.
13 | ///
14 | public static void Start() {
15 | Hashtable props = new Hashtable() {
16 | { "authorizedGroup", "Everyone" }, // Allow any user to connect
17 | { "exclusiveAddressUse", false }, // Allow to create channel if the client already has a handle on the named pipe
18 | { "portName", "RoboBackup" } };
19 | IpcServerChannel channel = new IpcServerChannel(props, null);
20 | ChannelServices.RegisterChannel(channel, false);
21 | RemotingConfiguration.RegisterWellKnownServiceType(typeof(IpcService), "ipc", WellKnownObjectMode.Singleton);
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/RoboBackupService/IpcService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace RoboBackup {
4 | ///
5 | /// Service object class proxied via IPC from the server to the client.
6 | ///
7 | public class IpcService : MarshalByRefObject {
8 | ///
9 | /// Method to test availability od the IPC channel.
10 | ///
11 | /// "pong".
12 | public string Ping() {
13 | return "pong";
14 | }
15 |
16 | ///
17 | /// Reloads service configuration.
18 | ///
19 | public void ReloadConfig() {
20 | Logger.LogIpcReloadMessage();
21 | Config.Load();
22 | LogCleaner.Start();
23 | }
24 |
25 | ///
26 | /// Requests service to immediately run backup task.
27 | ///
28 | /// GUID of the to be run.
29 | public void RunTask(string guid) {
30 | Logger.LogIpcRunTaskMessage(guid);
31 | if (Config.Tasks.ContainsKey(guid)) {
32 | Task task = Config.Tasks[guid];
33 | task.Start();
34 | }
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/RoboBackupService/LogCleaner.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 |
4 | namespace RoboBackup {
5 | ///
6 | /// Backup log file cleaner class.
7 | ///
8 | public static class LogCleaner {
9 | ///
10 | /// of the next log file deletion run.
11 | ///
12 | public static DateTime NextRunDate {
13 | get {
14 | if (_nextRunDate == DateTime.MinValue) {
15 | RefreshNextRunDate();
16 | }
17 | return _nextRunDate;
18 | }
19 | }
20 |
21 | ///
22 | /// used to execute the log file deletion process.
23 | ///
24 | private static Thread _cleanupThread;
25 |
26 | ///
27 | /// Private variable backing the property.
28 | ///
29 | private static DateTime _nextRunDate;
30 |
31 | ///
32 | /// Calculates of the next log file deletion run.
33 | ///
34 | public static void RefreshNextRunDate() {
35 | _nextRunDate = DateTime.Today.AddDays(1);
36 | Logger.LogCleanupNextRunDate(_nextRunDate);
37 | }
38 |
39 | ///
40 | /// Starts the file deletion in a separate thread.
41 | ///
42 | public static void Start() {
43 | Logger.LogCleanupStart();
44 | _cleanupThread = new Thread(new ThreadStart(SysUtils.DeleteOldLogs));
45 | _cleanupThread.Start();
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/RoboBackupService/Logger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace RoboBackup {
5 | ///
6 | /// Optional logging class for debugging purposes. Used when the is started with "/log" parameter.
7 | ///
8 | public static class Logger {
9 | ///
10 | /// Service log file stream writer.
11 | ///
12 | public static StreamWriter Writer { get; private set; }
13 |
14 | ///
15 | /// format used in logs.
16 | ///
17 | private const string _dateFormat = "yyyy-MM-dd HH:mm:ss.fff";
18 |
19 | ///
20 | /// Logs next run date of the .
21 | ///
22 | /// Next run date of the .
23 | public static void LogCleanupNextRunDate(DateTime nextCleanupDate) {
24 | if (Writer != null) {
25 | Log(string.Format("[Cleanup] Next log cleanup scheduled at {0}", nextCleanupDate.ToString(_dateFormat)));
26 | }
27 | }
28 |
29 | ///
30 | /// Logs start of deletion thread.
31 | ///
32 | public static void LogCleanupStart() {
33 | if (Writer != null) {
34 | Log("[Cleanup] Log cleanup started");
35 | }
36 | }
37 |
38 | ///
39 | /// Logs the values and tasks present in .
40 | ///
41 | public static void LogConfig() {
42 | if (Writer != null) {
43 | Log(string.Format("[Config] Got log retention period {0} days", Config.LogRetention));
44 | LogCleaner.RefreshNextRunDate(); // Produces log cleaner log line
45 | foreach (Task task in Config.Tasks.Values) {
46 | task.RefreshNextRunDate(); // Produces task detail log line
47 | }
48 | }
49 | }
50 |
51 | ///
52 | /// Logs the beginning of file load.
53 | ///
54 | public static void LogConfigLoad() {
55 | if (Writer != null) {
56 | Log("[Config] Loading config");
57 | }
58 | }
59 |
60 | ///
61 | /// Logs deletion of old backup directory.
62 | ///
63 | /// Deleted directory path.
64 | public static void LogDirectoryDeleted(string directory) {
65 | if (Writer != null) {
66 | Log(string.Format("[Task] Deleted old directory {0}", directory));
67 | }
68 | }
69 |
70 | ///
71 | /// Logs the reception of reload message received via IPC.
72 | ///
73 | public static void LogIpcReloadMessage() {
74 | if (Writer != null) {
75 | Log("[Service] Received IPC request for config reload");
76 | }
77 | }
78 |
79 | ///
80 | /// Logs the reception of immediate run message received via IPC.
81 | ///
82 | /// GUID of the to run.
83 | public static void LogIpcRunTaskMessage(string guid) {
84 | if (Writer != null) {
85 | Log(string.Format("[Service] Received IPC request for task GUID {0} run", guid));
86 | }
87 | }
88 |
89 | ///
90 | /// Logs deletion of old log file.
91 | ///
92 | /// Deleted log file path.
93 | public static void LogLogFileDeleted(string logFile) {
94 | if (Writer != null) {
95 | Log(string.Format("[Cleanup] Deleted old log file {0}", logFile));
96 | }
97 | }
98 |
99 | ///
100 | /// Logs the start
101 | ///
102 | public static void LogServiceStarted() {
103 | if (Writer != null) {
104 | Log("[Service] Service started");
105 | }
106 | }
107 |
108 | ///
109 | /// Logs the stop.
110 | ///
111 | public static void LogServiceStopped() {
112 | if (Writer != null) {
113 | Log("[Service] Service stopped");
114 | }
115 | }
116 |
117 | ///
118 | /// Logs next run date of a .
119 | ///
120 | /// The to be logged.
121 | public static void LogTaskNextRunDate(Task task) {
122 | if (Writer != null) {
123 | Log(string.Format("[Task] Next task GUID {0} run scheduled at {1}", task.Guid, task.NextRunDate.ToString(_dateFormat)));
124 | }
125 | }
126 |
127 | ///
128 | /// Logs the start of the run thread.
129 | ///
130 | /// The starting .
131 | public static void LogTaskStart(Task task) {
132 | if (Writer != null) {
133 | Log(string.Format("[Task] Task GUID {0} started", task.Guid));
134 | }
135 | }
136 |
137 | ///
138 | /// Logs timer next tick delay.
139 | ///
140 | /// The delay of the next tick in milliseconds.
141 | public static void LogTickDelay(int delay) {
142 | if (Writer != null) {
143 | Log(string.Format("[Timer] Next tick in {0} ms", delay));
144 | }
145 | }
146 |
147 | ///
148 | /// Logs the beginning of the timer tick.
149 | ///
150 | public static void LogTick() {
151 | if (Writer != null) {
152 | Log("[Timer] Tick");
153 | }
154 | }
155 |
156 | ///
157 | /// Logs timer tick skew warning.
158 | ///
159 | public static void LogTickSkew() {
160 | if (Writer != null) {
161 | Log("[Timer] Last tick more than 90 seconds ago or in the future, refreshing schedules");
162 | }
163 | }
164 |
165 | ///
166 | /// Opens a for the given log file name.
167 | ///
168 | /// Log file name.
169 | public static void Open(string path) {
170 | Writer = new StreamWriter(path, true) { AutoFlush = true };
171 | }
172 |
173 | ///
174 | /// Opens a for the given .
175 | ///
176 | /// to write into.
177 | public static void Open(Stream stream) {
178 | Writer = new StreamWriter(stream) { AutoFlush = true };
179 | }
180 |
181 | ///
182 | /// Logs a message using .
183 | ///
184 | /// The message to be logged.
185 | private static void Log(string message) {
186 | Writer.WriteLine(string.Format("{0} - {1}", DateTime.Now.ToString(_dateFormat), message));
187 | }
188 | }
189 | }
190 |
--------------------------------------------------------------------------------
/RoboBackupService/Program.cs:
--------------------------------------------------------------------------------
1 | using System.ServiceProcess;
2 |
3 | namespace RoboBackup {
4 | public static class Program {
5 | ///
6 | /// Main entry point to the service application.
7 | ///
8 | public static void Main() {
9 | ServiceBase.Run(new Service());
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/RoboBackupService/ProjectInstaller.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace RoboBackup {
2 | partial class ProjectInstaller {
3 | private System.ComponentModel.IContainer components = null;
4 |
5 | protected override void Dispose(bool disposing) {
6 | if (disposing && (components != null)) {
7 | components.Dispose();
8 | }
9 | base.Dispose(disposing);
10 | }
11 |
12 | #region Component Designer generated code
13 |
14 | ///
15 | /// Required method for Designer support - do not modify
16 | /// the contents of this method with the code editor.
17 | ///
18 | private void InitializeComponent() {
19 | this.serviceProcessInstaller = new System.ServiceProcess.ServiceProcessInstaller();
20 | this.serviceInstaller = new System.ServiceProcess.ServiceInstaller();
21 | //
22 | // serviceProcessInstaller
23 | //
24 | this.serviceProcessInstaller.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
25 | this.serviceProcessInstaller.Password = null;
26 | this.serviceProcessInstaller.Username = null;
27 | //
28 | // serviceInstaller
29 | //
30 | this.serviceInstaller.Description = "RoboBackup robocopy backup service";
31 | this.serviceInstaller.DisplayName = "RoboBackup";
32 | this.serviceInstaller.ServiceName = "RoboBackup";
33 | this.serviceInstaller.StartType = System.ServiceProcess.ServiceStartMode.Automatic;
34 | this.serviceInstaller.AfterInstall += new System.Configuration.Install.InstallEventHandler(this.ServiceInstaller_AfterInstall);
35 | //
36 | // ProjectInstaller
37 | //
38 | this.Installers.AddRange(new System.Configuration.Install.Installer[] {
39 | this.serviceProcessInstaller,
40 | this.serviceInstaller});
41 |
42 | }
43 |
44 | #endregion
45 |
46 | private System.ServiceProcess.ServiceProcessInstaller serviceProcessInstaller;
47 | private System.ServiceProcess.ServiceInstaller serviceInstaller;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/RoboBackupService/ProjectInstaller.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 | using System.Configuration.Install;
3 | using System.ServiceProcess;
4 |
5 | namespace RoboBackup {
6 | ///
7 | /// installer used by the Setup project.
8 | ///
9 | [RunInstaller(true)]
10 | public partial class ProjectInstaller : Installer {
11 | public ProjectInstaller() {
12 | InitializeComponent();
13 | }
14 |
15 | ///
16 | /// event hadler. Starts the installed service.
17 | ///
18 | private void ServiceInstaller_AfterInstall(object sender, InstallEventArgs e) {
19 | using (ServiceController serviceController = new ServiceController(serviceInstaller.ServiceName)) {
20 | serviceController.Start();
21 | }
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/RoboBackupService/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | [assembly: AssemblyTitle("RoboBackup Service")]
5 | [assembly: AssemblyDescription("")]
6 | [assembly: AssemblyConfiguration("")]
7 | [assembly: AssemblyCompany("")]
8 | [assembly: AssemblyProduct("RoboBackup Service")]
9 | [assembly: AssemblyCopyright("Copyright © Disassembler 2018")]
10 | [assembly: AssemblyTrademark("")]
11 | [assembly: AssemblyCulture("")]
12 |
13 | [assembly: ComVisible(false)]
14 | [assembly: Guid("43c4f764-abbd-46a5-a184-c24da8d7b203")]
15 |
16 | [assembly: AssemblyVersion("1.0.0.0")]
17 | [assembly: AssemblyFileVersion("1.0.0.0")]
18 |
--------------------------------------------------------------------------------
/RoboBackupService/RoboBackupService.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {43C4F764-ABBD-46A5-A184-C24DA8D7B203}
8 | WinExe
9 | RoboBackup
10 | RoboBackupService
11 | v4.6.1
12 | 512
13 | true
14 | true
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 | AllRules.ruleset
26 | false
27 |
28 |
29 | AnyCPU
30 | pdbonly
31 | true
32 | bin\Release\
33 | TRACE
34 | prompt
35 | 4
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | Component
53 |
54 |
55 | ProjectInstaller.cs
56 |
57 |
58 |
59 | Component
60 |
61 |
62 | Service.cs
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/RoboBackupService/Service.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace RoboBackup {
2 | partial class Service {
3 | private System.ComponentModel.IContainer components = null;
4 |
5 | protected override void Dispose(bool disposing) {
6 | if (disposing && (components != null)) {
7 | components.Dispose();
8 | }
9 | base.Dispose(disposing);
10 | }
11 |
12 | #region Component Designer generated code
13 |
14 | ///
15 | /// Required method for Designer support - do not modify
16 | /// the contents of this method with the code editor.
17 | ///
18 | private void InitializeComponent() {
19 | //
20 | // Service
21 | //
22 | this.ServiceName = "RoboBackup";
23 |
24 | }
25 |
26 | #endregion
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/RoboBackupService/Service.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.ServiceProcess;
4 | using System.Threading;
5 |
6 | namespace RoboBackup {
7 | ///
8 | /// RoboBackup service class.
9 | ///
10 | public partial class Service : ServiceBase {
11 | ///
12 | /// Tick period in milliseconds.
13 | ///
14 | private const int _period = 60000;
15 |
16 | ///
17 | /// Threshold limit for tick skew.
18 | ///
19 | private static readonly TimeSpan _skewLimit = TimeSpan.FromSeconds(90);
20 |
21 | ///
22 | /// of the last tick.
23 | ///
24 | private static DateTime _lastTick;
25 |
26 | ///
27 | /// Main timer for schedulling and execution.
28 | ///
29 | private static Timer _timer = new Timer(OnTick, null, Timeout.Infinite, Timeout.Infinite);
30 |
31 | ///
32 | /// Initializes a new instance of the class.
33 | ///
34 | public Service() {
35 | InitializeComponent();
36 | }
37 |
38 | ///
39 | /// handler. Opens (if configured), loads and starts the .
40 | ///
41 | /// Command line arguments.
42 | protected override void OnStart(string[] args) {
43 | if (args.Length == 1 && args[0] == "/log") {
44 | Logger.Open(Path.Combine(Config.LogRoot, "service.log"));
45 | }
46 | Logger.LogServiceStarted();
47 | Config.Load();
48 | LogCleaner.Start();
49 | IpcServer.Start();
50 | SetNextTick(DateTime.Now);
51 | }
52 |
53 | ///
54 | /// handler. Stops the and interrupts any currently running .
55 | ///
56 | protected override void OnStop() {
57 | _timer.Change(Timeout.Infinite, Timeout.Infinite);
58 | _timer = null;
59 | foreach (Task task in Config.Tasks.Values) {
60 | task.Stop();
61 | }
62 | Logger.LogServiceStopped();
63 | }
64 |
65 | ///
66 | /// Performs workload during normal tick. Runs or if they are scheduled to be run in the current tick.
67 | ///
68 | ///
69 | private static void DoTick(DateTime now) {
70 | foreach (Task task in Config.Tasks.Values) {
71 | if (task.NextRunDate <= now) {
72 | task.Start();
73 | task.RefreshNextRunDate();
74 | }
75 | }
76 | if (LogCleaner.NextRunDate <= now) {
77 | LogCleaner.Start();
78 | LogCleaner.RefreshNextRunDate();
79 | }
80 | }
81 |
82 | ///
83 | /// to be called every tick. Checks validity of the tick, performs workload and schedues the next tick.
84 | ///
85 | ///
86 | private static void OnTick(object state) {
87 | Logger.LogTick();
88 | DateTime now = DateTime.Now;
89 | // Recalculate next run dates if the last tick was more than 90 seconds ago or if it is in the future.
90 | // Such clock skew implies manual time change, huge NTP leap, machine awakened from hibernation/suspension or some other potentially unsafe state.
91 | if (now - _lastTick > _skewLimit || now < _lastTick) {
92 | Logger.LogTickSkew();
93 | foreach (Task task in Config.Tasks.Values) {
94 | task.RefreshNextRunDate();
95 | }
96 | LogCleaner.Start();
97 | LogCleaner.RefreshNextRunDate();
98 | } else {
99 | DoTick(now);
100 | }
101 | // The next tick time has to be readjusted often to maintain the precision of the scheduled tasks.
102 | // System.Threading.Timer is ridiculously imprecise. The best case scenario skews the timer by 1 ms per minute (1 minute per ~100 days).
103 | // An alternative would be to create a custom timer implementaion based on high-precision multimedia timers, but
104 | // System.Threading.Timer is simple to use and guaranteed to be supported pretty much everywhere, so it's good enough.
105 | SetNextTick(now);
106 | }
107 |
108 | ///
109 | /// Sets the delay until the next tick.
110 | ///
111 | /// of the previous tick.
112 | private static void SetNextTick(DateTime now) {
113 | int delay = _period - (now.Second * 1000) - now.Millisecond;
114 | Logger.LogTickDelay(delay);
115 | _timer.Change(delay, _period);
116 | _lastTick = now;
117 | }
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/RoboBackupService/SysUtils.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Runtime.InteropServices;
7 | using System.Text.RegularExpressions;
8 |
9 | namespace RoboBackup {
10 | ///
11 | /// Filesystem entry type.
12 | ///
13 | public enum EntryType {
14 | File,
15 | Directory
16 | }
17 |
18 | ///
19 | /// Filesystem modification and robocopy process utilities.
20 | ///
21 | public static class SysUtils {
22 | ///
23 | /// Deletes logs older than days.
24 | ///
25 | public static void DeleteOldLogs() {
26 | DateTime threshold = DateTime.Now.AddDays(-Config.LogRetention);
27 | DirectoryInfo logRoot = new DirectoryInfo(Config.LogRoot);
28 | foreach (DirectoryInfo directory in logRoot.EnumerateDirectories()) {
29 | foreach (FileInfo file in directory.EnumerateFiles().Where(f => f.LastWriteTime < threshold)) {
30 | try {
31 | file.Delete();
32 | Logger.LogLogFileDeleted(file.FullName);
33 | } catch { /* Ignore if the old log can't be deleted now */ }
34 | }
35 | }
36 | }
37 |
38 | ///
39 | /// Prepares and executes robocopy backup command.
40 | ///
41 | /// to run.
42 | public static void RunBackup(Task task) {
43 | string timestamp = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
44 | string logFile = GetLogName(task.Guid, timestamp);
45 | string destDir = Path.Combine(task.Destination, timestamp);
46 | string latestLink = Path.Combine(task.Destination, "latest");
47 | try {
48 | if (task.Method == Method.Differential && task.Retention > 1) {
49 | try {
50 | CopyDirectoryStructure(latestLink, destDir);
51 | CopyHardlinkStructure(latestLink, destDir);
52 | } catch { /* Ignore if the latestLink doesn't exist or points to invalid directory */ }
53 | }
54 | string uncPath = Unc.IsUncPath(task.Source) ? task.Source : Unc.IsUncPath(task.Destination) ? task.Destination : null;
55 | using (Unc unc = new Unc(uncPath)) {
56 | unc.Connect(Credential.Decrypt(task.Guid, task.Credential));
57 | RunRobocopy(task, logFile, destDir);
58 | }
59 | if (task.Retention > 1) {
60 | CreateLatestSymlink(destDir, latestLink);
61 | DeleteOldDirs(task.Destination, task.Retention);
62 | }
63 | } catch (Exception e) {
64 | using (StreamWriter streamWriter = new StreamWriter(logFile, true)) {
65 | streamWriter.Write(e.Message);
66 | streamWriter.Write(e.StackTrace);
67 | }
68 | }
69 | }
70 |
71 | ///
72 | /// Copies directory structure from source to destination.
73 | ///
74 | /// Source directory.
75 | /// Destination directory.
76 | private static void CopyDirectoryStructure(string sourceDir, string destDir) {
77 | string args = string.Format("{0} {1} /E /CREATE /DST /DCOPY:DAT /XF * /XJ /R:0 /W:0", EscapeArg(sourceDir), EscapeArg(destDir));
78 | ProcessStartInfo psi = new ProcessStartInfo("robocopy", args) { WindowStyle = ProcessWindowStyle.Hidden };
79 | Process robocopy = Process.Start(psi);
80 | robocopy.WaitForExit();
81 | }
82 |
83 | ///
84 | /// Copies file structure from source to destination as hardlinks.
85 | ///
86 | /// Source directory path.
87 | /// Destination directory path.
88 | private static void CopyHardlinkStructure(string sourceDir, string destDir) {
89 | foreach (string dir in Directory.EnumerateDirectories(sourceDir)) {
90 | string dest = Path.Combine(destDir, Path.GetFileName(dir));
91 | CopyHardlinkStructure(dir, dest);
92 | }
93 | foreach (string file in Directory.EnumerateFiles(sourceDir)) {
94 | string dest = Path.Combine(destDir, Path.GetFileName(file));
95 | CreateHardlink(dest, file);
96 | }
97 | }
98 |
99 | ///
100 | /// Creates a hardlink.
101 | ///
102 | /// New hardlink file path.
103 | /// Existing file path to point the hardlink to.
104 | private static void CreateHardlink(string lpFileName, string lpExistingFileName) {
105 | CreateHardlink(lpFileName, lpExistingFileName, IntPtr.Zero);
106 | }
107 |
108 | ///
109 | /// Creates a hardlink.
110 | ///
111 | /// New hardlink file path.
112 | /// Existing file path to point the hardlink to.
113 | /// Security attributes - unused.
114 | ///
115 | [DllImport("kernel32.dll", EntryPoint = "CreateHardLinkW", CharSet = CharSet.Unicode)]
116 | private static extern int CreateHardlink(string lpFileName, string lpExistingFileName, IntPtr lpSecurityAttributes);
117 |
118 | ///
119 | /// Creates a symlink to the latest backup directory.
120 | ///
121 | /// Path of the latest backup directory.
122 | /// Symlink path.
123 | private static void CreateLatestSymlink(string destDir, string latestLink) {
124 | if (Directory.Exists(latestLink)) {
125 | Directory.Delete(latestLink);
126 | }
127 | CreateSymlink(latestLink, destDir, (int)EntryType.Directory);
128 | }
129 |
130 | ///
131 | /// Creates a symbolic link.
132 | ///
133 | /// New symlink file path.
134 | /// Existing file or directory path to point the symlink to.
135 | /// of the existing file or directory.
136 | ///
137 | [DllImport("kernel32.dll", EntryPoint = "CreateSymbolicLinkW", CharSet = CharSet.Unicode)]
138 | private static extern int CreateSymlink(string lpSymlinkFileName, string lpTargetFileName, int dwFlags);
139 |
140 | ///
141 | /// Recursively deletes a directory.
142 | ///
143 | /// Path of the directory to delete.
144 | /// Directory.Delete() is slow as it enumerates the content of the directory. Furthermore it doesn't allow to delete files with read-only attribute without unsetting it first.
145 | private static void DeleteDir(string path) {
146 | ProcessStartInfo psi = new ProcessStartInfo("cmd", string.Format("/C rmdir /s /q {0}", EscapeArg(path))) { WindowStyle = ProcessWindowStyle.Hidden };
147 | Process rmdir = Process.Start(psi);
148 | rmdir.WaitForExit();
149 | Logger.LogDirectoryDeleted(path);
150 | }
151 |
152 | ///
153 | /// Deletes old backups. Leaves the latest directories and deletes the rest.
154 | ///
155 | /// log directory.
156 | /// Number of the latest backups to retain.
157 | private static void DeleteOldDirs(string dir, ushort retention) {
158 | string[] subdirs = Directory.GetDirectories(dir);
159 | if (subdirs.Length > ++retention) {
160 | foreach (string subdir in subdirs.Take(subdirs.Length - retention)) {
161 | DeleteDir(subdir);
162 | }
163 | }
164 | }
165 |
166 | ///
167 | /// Escapes command line argument.
168 | ///
169 | /// Argument to escape.
170 | ///
171 | private static string EscapeArg(string arg) {
172 | return "\"" + Regex.Replace(arg, @"(\\+)$", @"$1$1") + "\"";
173 | }
174 |
175 | ///
176 | /// Generates a log file name for the current run of the .
177 | ///
178 | /// GUID of the .
179 | /// Formatted of the call.
180 | ///
181 | private static string GetLogName(string guid, string timestamp) {
182 | string logDir = Path.Combine(Config.LogRoot, guid);
183 | if (!Directory.Exists(logDir)) {
184 | Directory.CreateDirectory(logDir);
185 | }
186 | return Path.Combine(logDir, string.Format("{0}.log", timestamp));
187 | }
188 |
189 | ///
190 | /// Compiles the robocopy backup arguments and executes the robocopy .
191 | ///
192 | /// to run.
193 | /// Robocopy log file path.
194 | /// Backup destination directory path.
195 | private static void RunRobocopy(Task task, string logFile, string destDir) {
196 | List args = new List() { EscapeArg(task.Source) };
197 | if (task.Retention == 1) {
198 | args.Add(EscapeArg(task.Destination));
199 | args.Add(task.Method == Method.Incremental ? "/E" : "/MIR");
200 | } else {
201 | args.Add(EscapeArg(destDir));
202 | args.Add("/MIR");
203 | }
204 | if (task.ExcludedDirs.Length > 0) {
205 | args.Add("/XD");
206 | args.AddRange(task.ExcludedDirs.Select(x => EscapeArg(Path.Combine(task.Source, x))));
207 | }
208 | if (task.ExcludedFiles.Length > 0) {
209 | args.Add("/XF");
210 | args.AddRange(task.ExcludedFiles.Select(x => EscapeArg(Path.Combine(task.Source, x))));
211 | }
212 | args.Add(string.Format("/XJ /SL /DST /ZB /DCOPY:DAT /COPY:DAT /R:2 /W:3 /NP \"/UNILOG+:{0}\"", logFile));
213 | ProcessStartInfo psi = new ProcessStartInfo("robocopy", string.Join(" ", args)) { WindowStyle = ProcessWindowStyle.Hidden };
214 | Process robocopy = Process.Start(psi);
215 | robocopy.WaitForExit();
216 | }
217 |
218 | /*
219 | * Usage :: ROBOCOPY source destination [file [file]...] [options]
220 | * /E :: copy subdirectories, including Empty ones.
221 | * /PURGE :: delete dest files/dirs that no longer exist in source.
222 | * /MIR :: MIRror a directory tree (equivalent to /E plus /PURGE).
223 | * /CREATE :: CREATE directory tree and zero-length files only.
224 | * /XF file [file]... :: eXclude Files matching given names/paths/wildcards.
225 | * /XD dirs [dirs]... :: eXclude Directories matching given names/paths.
226 | * /XJ :: eXclude Junction points and symbolic links. (normally included by default).
227 | * /SL :: copy symbolic links versus the target.
228 | * /DST :: compensate for one-hour DST time differences.
229 | * /ZB :: use restartable mode; if access denied use Backup mode.
230 | * /DCOPY:copyflag[s] :: what to COPY for directories (default is /DCOPY:DA).
231 | * (copyflags : D=Data, A=Attributes, T=Timestamps).
232 | * /COPY:copyflag[s] :: what to COPY for files (default is /COPY:DAT).
233 | * (copyflags : D=Data, A=Attributes, T=Timestamps).
234 | * (S=Security=NTFS ACLs, O=Owner info, U=aUditing info).
235 | * /R:n :: number of Retries on failed copies: default 1 million.
236 | * /W:n :: Wait time between retries: default is 30 seconds.
237 | * /NP :: No Progress - don't display percentage copied.
238 | * /UNILOG+:file :: output status to LOG file as UNICODE (append to existing log).
239 | */
240 | }
241 | }
242 |
--------------------------------------------------------------------------------
/RoboBackupService/Task.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 |
4 | namespace RoboBackup {
5 | ///
6 | /// method.
7 | ///
8 | public enum Method {
9 | Incremental,
10 | Differential,
11 | Full
12 | }
13 |
14 | ///
15 | /// period.
16 | ///
17 | public enum Period {
18 | Daily,
19 | Weekly,
20 | Monthly
21 | }
22 |
23 | ///
24 | /// Backup task definition - source and destination directories, exclusions, schedule etc.
25 | ///
26 | [Serializable]
27 | public class Task {
28 | ///
29 | /// Encrypted credentials for use with network shares.
30 | ///
31 | public string Credential { get; set; }
32 |
33 | ///
34 | /// Day of month for monthly schedule.
35 | ///
36 | public byte DayOfMonth { get; set; }
37 |
38 | ///
39 | /// Day of week for weekly schedule.
40 | ///
41 | public DayOfWeek DayOfWeek { get; set; }
42 |
43 | ///
44 | /// Backup destination directory.
45 | ///
46 | public string Destination { get; set; }
47 |
48 | ///
49 | /// Directories to be excluded from the backup.
50 | ///
51 | public string[] ExcludedDirs { get; set; }
52 |
53 | ///
54 | /// Files to be excluded from the backup.
55 | ///
56 | public string[] ExcludedFiles { get; set; }
57 |
58 | ///
59 | /// Global unique identifier of the .
60 | ///
61 | public string Guid { get; set; }
62 |
63 | ///
64 | /// Scheduled hour when to run the .
65 | ///
66 | public byte Hour { get; set; }
67 |
68 | ///
69 | /// Backup method.
70 | ///
71 | public Method Method { get; set; }
72 |
73 | ///
74 | /// Scheduled minute when to run the .
75 | ///
76 | public byte Minute { get; set; }
77 |
78 | ///
79 | /// of the next run.
80 | ///
81 | public DateTime NextRunDate {
82 | get {
83 | if (_nextRunDate == DateTime.MinValue) {
84 | RefreshNextRunDate();
85 | }
86 | return _nextRunDate;
87 | }
88 | }
89 |
90 | ///
91 | /// How often to run the .
92 | ///
93 | public Period Period { get; set; }
94 |
95 | ///
96 | /// How many backup directories to retain.
97 | ///
98 | public ushort Retention { get; set; }
99 |
100 | ///
101 | /// Backup source directory.
102 | ///
103 | public string Source { get; set; }
104 |
105 | ///
106 | /// Non-unique human readable identifier.
107 | ///
108 | public string Title { get; set; }
109 |
110 | ///
111 | /// Private variable backing the property.
112 | ///
113 | [NonSerialized]
114 | private DateTime _nextRunDate;
115 |
116 | ///
117 | /// used to execute the robocopy backup process.
118 | ///
119 | [NonSerialized]
120 | private Thread _thread;
121 |
122 | ///
123 | /// Calculates of the next run.
124 | ///
125 | public void RefreshNextRunDate() {
126 | DateTime now = DateTime.Now;
127 | DateTime nextRun = DateTime.Today.AddHours(Hour).AddMinutes(Minute);
128 | if (Period == Period.Daily) {
129 | if (nextRun < now) {
130 | nextRun = nextRun.AddDays(1);
131 | }
132 | } else if (Period == Period.Weekly) {
133 | nextRun = nextRun.AddDays(DayOfWeek - now.DayOfWeek);
134 | if (nextRun < now) {
135 | nextRun = nextRun.AddDays(7);
136 | }
137 | } else {
138 | while (DayOfMonth > DateTime.DaysInMonth(nextRun.Year, nextRun.Month)) {
139 | nextRun = nextRun.AddMonths(1);
140 | }
141 | nextRun = nextRun.AddDays(DayOfMonth - now.Day);
142 | if (nextRun < now) {
143 | nextRun = nextRun.AddMonths(1);
144 | }
145 | }
146 | _nextRunDate = nextRun;
147 | Logger.LogTaskNextRunDate(this);
148 | }
149 |
150 | ///
151 | /// Starts the robocopy backup process in a separate thread.
152 | ///
153 | public void Start() {
154 | Logger.LogTaskStart(this);
155 | _thread = new Thread(new ThreadStart(() => SysUtils.RunBackup(this)));
156 | _thread.Start();
157 | }
158 |
159 | ///
160 | /// Interrupts the thread with the robocopy backup process.
161 | ///
162 | public void Stop() {
163 | if (_thread != null && _thread.ThreadState == ThreadState.Running) {
164 | _thread.Abort();
165 | }
166 | }
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/RoboBackupService/Unc.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Runtime.InteropServices;
4 | using System.Text;
5 |
6 | namespace RoboBackup {
7 | ///
8 | /// Provides methods for accessing UNC paths with shared network resources requiring credentials.
9 | ///
10 | public class Unc : IDisposable {
11 | ///
12 | /// Lifecycle state of the object.
13 | ///
14 | private bool _disposed = false;
15 |
16 | ///
17 | /// UNC path with shared network resource to be accessed.
18 | ///
19 | private readonly string _path;
20 |
21 | ///
22 | /// Prepare a new instance for connection to a shared network resource.
23 | ///
24 | /// Network path to be accessed.
25 | public Unc(string path) {
26 | _path = path;
27 | }
28 |
29 | ///
30 | /// Destroys instance for shared network resource access.
31 | ///
32 | ~Unc() {
33 | Dispose();
34 | }
35 |
36 | ///
37 | /// Checks if resembles an UNC path.
38 | ///
39 | /// to be checked.
40 | /// if the resembles UNC path, otherwise .
41 | public static bool IsUncPath(string path) {
42 | return path.StartsWith(@"\\");
43 | }
44 |
45 | ///
46 | /// Resolves mapped drive into UNC network path.
47 | ///
48 | /// Path to be resolved.
49 | /// Resolved UNC network path if the root of the is on mapped drive, unchaged otherwise.
50 | public static string TranslatePath(string path) {
51 | if (IsUncPath(path)) {
52 | return path;
53 | }
54 | string[] path_split = path.Split(new char[] { '\\' }, 2);
55 | StringBuilder stringBuilder = new StringBuilder(512);
56 | int size = stringBuilder.Capacity;
57 | var error = WNetGetConnectionW(path_split[0], stringBuilder, ref size);
58 | if (error != 0)
59 | return path;
60 | return Path.Combine(stringBuilder.ToString(), path_split[1]);
61 | }
62 |
63 | ///
64 | /// Accesses the UNC path with shared network resource using .
65 | ///
66 | /// Username and password required for access to the shared network resource.
67 | public void Connect(Credential credential) {
68 | if (credential == null) {
69 | _disposed = true;
70 | return;
71 | }
72 | string[] path_split = _path.Split(new char[] { '\\' });
73 | string domain = path_split.Length > 2 ? path_split[2] : null;
74 | UseInfo2 useinfo = new UseInfo2 {
75 | ui2_remote = _path,
76 | ui2_domainname = domain,
77 | ui2_username = credential.Username,
78 | ui2_password = credential.Password,
79 | ui2_asg_type = 0, // USE_WILDCARD
80 | ui2_usecount = 1
81 | };
82 | NetUseAdd(null, 2, ref useinfo, out uint parmErr); // 2 = useinfo is USE_INFO_2
83 | }
84 |
85 | ///
86 | /// Disconnects from the shared network resource and sets the object as disposed.
87 | ///
88 | public void Dispose() {
89 | if (!_disposed) {
90 | NetUseDel(null, _path, 2); // 2 = USE_LOTS_OF_FORCE
91 | _disposed = true;
92 | }
93 | GC.SuppressFinalize(this);
94 | }
95 |
96 | ///
97 | /// The NetUseAdd function establishes a connection between the local computer and a remote server. You can specify a local drive letter or a printer device to connect. If you do not specify a local drive letter or printer device, the function authenticates the client with the server for future connections.
98 | ///
99 | /// The UNC name of the computer on which to execute this function. If this parameter is , then the local computer is used.
100 | /// A value that specifies the information level of the data.
101 | /// A pointer to the buffer that specifies the data. The format of this data depends on the value of the parameter.
102 | /// A pointer to a value that receives the index of the first member of the information structure in error. If this parameter is , the index is not returned on error.
103 | /// If the function succeeds, the return value is NERR_Success. If the function fails, the return value is a system error code.
104 | /// https://docs.microsoft.com/en-us/windows/desktop/api/lmuse/nf-lmuse-netuseadd
105 | [DllImport("Netapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
106 | private static extern uint NetUseAdd(string uncServerName, uint levelFlags, ref UseInfo2 buf, out uint parmErr);
107 |
108 | ///
109 | /// The NetUseDel function ends a connection to a shared resource.
110 | ///
111 | /// The UNC name of the computer on which to execute this function. If this is parameter is NULL, then the local computer is used.
112 | /// A pointer to a string that specifies the path of the connection to delete.
113 | /// The level of force to use in deleting the connection.
114 | /// If the function succeeds, the return value is NERR_Success. If the function fails, the return value is a system error code.
115 | /// https://docs.microsoft.com/en-us/windows/desktop/api/lmuse/nf-lmuse-netusedel
116 | [DllImport("Netapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
117 | private static extern uint NetUseDel(string uncServerName, string useName, uint forceLevelFlags);
118 |
119 | ///
120 | /// Retrieves the name of the network resource associated with a local device.
121 | ///
122 | /// Pointer to a constant null-terminated string that specifies the name of the local device to get the network name for.
123 | /// Pointer to a null-terminated string that receives the remote name used to make the connection.
124 | /// Pointer to a variable that specifies the size of the buffer pointed to by the lpRemoteName parameter, in characters. If the function fails because the buffer is not large enough, this parameter returns the required buffer size.
125 | /// If the function succeeds, the return value is NO_ERROR. If the function fails, the return value is a system error code.
126 | [DllImport("mpr.dll", CharSet = CharSet.Unicode, SetLastError = true)]
127 | private static extern int WNetGetConnectionW(string lpLocalName, StringBuilder lpRemoteName, ref int lpnLength);
128 |
129 | ///
130 | /// The USE_INFO_2 structure contains information about a connection between a local computer and a shared resource, including connection type, connection status, user name, and domain name.
131 | ///
132 | /// https://docs.microsoft.com/en-us/windows/desktop/api/lmuse/ns-lmuse-_use_info_2
133 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
134 | private struct UseInfo2 {
135 | internal string ui2_local;
136 | internal string ui2_remote;
137 | internal string ui2_password;
138 | internal uint ui2_status;
139 | internal uint ui2_asg_type;
140 | internal uint ui2_refcount;
141 | internal uint ui2_usecount;
142 | internal string ui2_username;
143 | internal string ui2_domainname;
144 | }
145 | }
146 | }
147 |
--------------------------------------------------------------------------------