Esempio n. 1
0
        public static int RealMain(string[] args)
        {
            bool verboseErrors = false;
            bool verbose       = false;

            try
            {
                List <string> cargs = new List <string>(args);

                var tmpparsed = FilterCollector.ExtractOptions(cargs);
                var options   = tmpparsed.Item1;
                var filter    = tmpparsed.Item2;

                verboseErrors = Library.Utility.Utility.ParseBoolOption(options, "debug-output");
                verbose       = Library.Utility.Utility.ParseBoolOption(options, "verbose");

                //If we are on Windows, append the bundled "win-tools" programs to the search path
                //We add it last, to allow the user to override with other versions
                if (!Library.Utility.Utility.IsClientLinux)
                {
                    string wintools = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "win-tools");
                    Environment.SetEnvironmentVariable("PATH",
                                                       Environment.GetEnvironmentVariable("PATH") +
                                                       System.IO.Path.PathSeparator.ToString() +
                                                       wintools +
                                                       System.IO.Path.PathSeparator.ToString() +
                                                       System.IO.Path.Combine(wintools, "gpg") //GPG needs to be in a subfolder for wrapping reasons
                                                       );
                }

#if DEBUG
                if (cargs.Count > 1 && cargs[0].ToLower() == "unittest")
                {
                    //The unit test is only enabled in DEBUG builds
                    //it works by getting a list of folders, and treats them as
                    //if they have the same data, but on different times

                    //The first folder is used to make a full backup,
                    //and each subsequent folder is used to make an incremental backup

                    //After all backups are made, the files are restored and verified against
                    //the original folders.

                    //The best way to test it, is to use SVN checkouts at different
                    //revisions, as this is how a regular folder would evolve

                    cargs.RemoveAt(0);
                    UnitTest.RunTest(cargs.ToArray(), options);
                    return(0);
                }
#endif

                if (cargs.Count == 1 && string.Equals(cargs[0], "changelog", StringComparison.InvariantCultureIgnoreCase))
                {
                    var path = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "changelog.txt");
                    Console.WriteLine(System.IO.File.ReadAllText(path));
                    return(0);
                }

                foreach (string internaloption in Library.Main.Options.InternalOptions)
                {
                    if (options.ContainsKey(internaloption))
                    {
                        Console.WriteLine(Strings.Program.InternalOptionUsedError, internaloption);
                        return(200);
                    }
                }

                // Probe for "help" to avoid extra processing
                bool isHelp = cargs.Count == 0 || (cargs.Count >= 1 && string.Equals(cargs[0], "help", StringComparison.InvariantCultureIgnoreCase));
                if (!isHelp && ((options.ContainsKey("parameters-file") && !string.IsNullOrEmpty("parameters-file")) || (options.ContainsKey("parameter-file") && !string.IsNullOrEmpty("parameter-file")) || (options.ContainsKey("parameterfile") && !string.IsNullOrEmpty("parameterfile"))))
                {
                    string filename;
                    if (options.ContainsKey("parameters-file") && !string.IsNullOrEmpty("parameters-file"))
                    {
                        filename = options["parameters-file"];
                        options.Remove("parameters-file");
                    }
                    else
                    {
                        filename = options["parameter-file"];
                        options.Remove("parameter-file");
                    }

                    if (!ReadOptionsFromFile(filename, ref filter, cargs, options))
                    {
                        return(100);
                    }
                }

                string command;
                if (cargs.Count > 0)
                {
                    command = cargs[0];
                    cargs.RemoveAt(0);
                }
                else
                {
                    command = "help";
                }

                // Update probe for help
                isHelp = string.Equals(command, "help", StringComparison.InvariantCultureIgnoreCase);

                // Skip the env read if the command is help, otherwise we may report weirdness
                if (!isHelp)
                {
                    if (!options.ContainsKey("passphrase"))
                    {
                        if (!string.IsNullOrEmpty(System.Environment.GetEnvironmentVariable("PASSPHRASE")))
                        {
                            options["passphrase"] = System.Environment.GetEnvironmentVariable("PASSPHRASE");
                        }
                    }

                    if (!options.ContainsKey("auth-password"))
                    {
                        if (!string.IsNullOrEmpty(System.Environment.GetEnvironmentVariable("AUTH_PASSWORD")))
                        {
                            options["auth-password"] = System.Environment.GetEnvironmentVariable("AUTH_PASSWORD");
                        }
                    }

                    if (!options.ContainsKey("auth-username"))
                    {
                        if (!string.IsNullOrEmpty(System.Environment.GetEnvironmentVariable("AUTH_USERNAME")))
                        {
                            options["auth-username"] = System.Environment.GetEnvironmentVariable("AUTH_USERNAME");
                        }
                    }
                }

                var knownCommands = new Dictionary <string, Func <List <string>, Dictionary <string, string>, Library.Utility.IFilter, int> >(StringComparer.InvariantCultureIgnoreCase);
                knownCommands["help"]    = Commands.Help;
                knownCommands["find"]    = Commands.List;
                knownCommands["list"]    = Commands.List;
                knownCommands["delete"]  = Commands.Delete;
                knownCommands["backup"]  = Commands.Backup;
                knownCommands["restore"] = Commands.Restore;

                knownCommands["repair"] = Commands.Repair;

                knownCommands["compact"]       = Commands.Compact;
                knownCommands["create-report"] = Commands.CreateBugReport;
                knownCommands["compare"]       = Commands.ListChanges;
                knownCommands["test"]          = Commands.Test;
                knownCommands["verify"]        = Commands.Test;
                knownCommands["test-filters"]  = Commands.TestFilters;
                knownCommands["affected"]      = Commands.Affected;

                if (!isHelp && verbose)
                {
                    Console.WriteLine("Input command: {0}", command);

                    Console.WriteLine("Input arguments: ");
                    foreach (var a in cargs)
                    {
                        Console.WriteLine("\t{0}", a);
                    }
                    Console.WriteLine();

                    Console.WriteLine("Input options: ");
                    foreach (var n in options)
                    {
                        Console.WriteLine("{0}: {1}", n.Key, n.Value);
                    }
                    Console.WriteLine();
                }

                Duplicati.Library.Utility.TempFile.RemoveOldApplicationTempFiles((path, ex) => {
                    if (verbose)
                    {
                        Console.WriteLine(string.Format("Failed to delete temp file: {0}", path));
                    }
                });

                var autoupdate = Library.Utility.Utility.ParseBoolOption(options, "auto-update");
                options.Remove("auto-update");

                if (knownCommands.ContainsKey(command))
                {
                    var res = knownCommands[command](cargs, options, filter);

                    if (autoupdate)
                    {
                        var update = Library.AutoUpdater.UpdaterManager.LastUpdateCheckVersion;
                        if (update == null)
                        {
                            update = Library.AutoUpdater.UpdaterManager.CheckForUpdate();
                        }

                        if (update != null && update.Version != Library.AutoUpdater.UpdaterManager.SelfVersion.Version)
                        {
                            Console.WriteLine("Found update \"{0}\", downloading ...", update.Displayname);
                            long lastpg = 0;
                            Library.AutoUpdater.UpdaterManager.DownloadAndUnpackUpdate(update, f => {
                                var npg = (long)(f * 100);
                                if (Math.Abs(npg - lastpg) >= 5 || (npg == 100 && lastpg != 100))
                                {
                                    lastpg = npg;
                                    Console.WriteLine("Downloading {0}% ...", npg);
                                }
                            });
                            Console.WriteLine("Update \"{0}\" ({1}) installed, using on next launch", update.Displayname, update.Version);
                        }
                    }

                    return(res);
                }
                else
                {
                    Commands.PrintInvalidCommand(command, cargs);
                    return(200);
                }
            }
            catch (Exception ex)
            {
                while (ex is System.Reflection.TargetInvocationException && ex.InnerException != null)
                {
                    ex = ex.InnerException;
                }

                if (!(ex is Library.Interface.CancelException))
                {
                    if (!string.IsNullOrEmpty(ex.Message))
                    {
                        Console.Error.WriteLine();
                        Console.Error.WriteLine(verboseErrors ? ex.ToString() : ex.Message);
                    }
                }
                else
                {
                    Console.Error.WriteLine(Strings.Program.UnhandledException, ex.ToString());

                    while (ex.InnerException != null)
                    {
                        ex = ex.InnerException;
                        Console.Error.WriteLine();
                        Console.Error.WriteLine(Strings.Program.UnhandledInnerException, ex.ToString());
                    }
                }

                //Error = 100
                return(100);
            }
        }
Esempio n. 2
0
        static void Main(string[] args)
        {
            try
            {
                List <string> cargs  = new List <string>(args);
                string        filter = Duplicati.Library.Utility.FilenameFilter.EncodeAsFilter(Duplicati.Library.Utility.FilenameFilter.ParseCommandLine(cargs, true));
                Dictionary <string, string> options = CommandLineParser.ExtractOptions(cargs);

                //If we are on Windows, append the bundled "win-tools" programs to the search path
                //We add it last, to allow the user to override with other versions
                if (!Library.Utility.Utility.IsClientLinux)
                {
                    string wintools = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "win-tools");
                    Environment.SetEnvironmentVariable("PATH",
                                                       Environment.GetEnvironmentVariable("PATH") +
                                                       System.IO.Path.PathSeparator.ToString() +
                                                       wintools +
                                                       System.IO.Path.PathSeparator.ToString() +
                                                       System.IO.Path.Combine(wintools, "gpg") //GPG needs to be in a subfolder for wrapping reasons
                                                       );
                }

#if DEBUG
                if (cargs.Count > 1 && cargs[0].ToLower() == "unittest")
                {
                    //The unit test is only enabled in DEBUG builds
                    //it works by getting a list of folders, and treats them as
                    //if they have the same data, but on different times

                    //The first folder is used to make a full backup,
                    //and each subsequent folder is used to make an incremental backup

                    //After all backups are made, the files are restored and verified against
                    //the original folders.

                    //The best way to test it, is to use SVN checkouts at different
                    //revisions, as this is how a regular folder would evolve

                    cargs.RemoveAt(0);
                    UnitTest.RunTest(cargs.ToArray(), options);
                    return;
                }
#endif

                if (cargs.Count > 0)
                {
                    //Because options are of the format --name=value, it seems natural to write "delete-all-but-n-full=5",
                    // so we allow that format as well
                    foreach (string s in COMMANDS_AS_ARGUMENTS)
                    {
                        if (cargs[0].Trim().ToLower().StartsWith(s + "="))
                        {
                            cargs.Insert(1, cargs[0].Substring(s.Length + 1));
                            cargs[0] = s;
                        }
                    }
                }

                //AFTER converting options to commands, we check for internal switches
                foreach (string internaloption in Library.Main.Options.InternalOptions)
                {
                    if (options.ContainsKey(internaloption))
                    {
                        Console.WriteLine(Strings.Program.InternalOptionUsedError, internaloption);
                        return;
                    }
                }

                if ((options.ContainsKey("parameters-file") && !string.IsNullOrEmpty("parameters-file")) || (options.ContainsKey("parameter-file") && !string.IsNullOrEmpty("parameter-file")))
                {
                    string filename;
                    if (options.ContainsKey("parameters-file") && !string.IsNullOrEmpty("parameters-file"))
                    {
                        filename = options["parameters-file"];
                        options.Remove("parameters-file");
                    }
                    else
                    {
                        filename = options["parameter-file"];
                        options.Remove("parameter-file");
                    }

                    if (!ReadOptionsFromFile(filename, ref filter, cargs, options))
                    {
                        return;
                    }
                }

                //After checking for internal options, we set the filter option
                if (!string.IsNullOrEmpty(filter))
                {
                    options["filter"] = filter;
                }

                if (cargs.Count == 1)
                {
                    switch (cargs[0].Trim().ToLower())
                    {
                    case "purge-signature-cache":
                        Library.Main.Interface.PurgeSignatureCache(options);
                        return;
                    }
                }

                if (cargs.Count < 2 || cargs[0].Trim().Equals("help", StringComparison.InvariantCultureIgnoreCase))
                {
                    if (cargs.Count < 2)
                    {
                        Help.PrintUsage("help", options);
                    }
                    else
                    {
                        Help.PrintUsage(cargs[1], options);
                    }


                    return;
                }


                string source = cargs[0];
                string target = cargs[1];

                if (source.Trim().ToLower() == "restore" && cargs.Count == 3)
                {
                    source             = target;
                    target             = cargs[2];
                    options["restore"] = null;
                    cargs.RemoveAt(0);
                }
                else if (source.Trim().ToLower() == "backup" && cargs.Count == 3)
                {
                    source = target;
                    target = cargs[2];
                    cargs.RemoveAt(0);
                }

                if (!options.ContainsKey("passphrase"))
                {
                    if (!string.IsNullOrEmpty(System.Environment.GetEnvironmentVariable("PASSPHRASE")))
                    {
                        options["passphrase"] = System.Environment.GetEnvironmentVariable("PASSPHRASE");
                    }
                }

                if (!options.ContainsKey("ftp-password"))
                {
                    if (!string.IsNullOrEmpty(System.Environment.GetEnvironmentVariable("FTP_PASSWORD")))
                    {
                        options["ftp-password"] = System.Environment.GetEnvironmentVariable("FTP_PASSWORD");
                    }
                }

                if (!options.ContainsKey("ftp-username"))
                {
                    if (!string.IsNullOrEmpty(System.Environment.GetEnvironmentVariable("FTP_USERNAME")))
                    {
                        options["ftp-username"] = System.Environment.GetEnvironmentVariable("FTP_USERNAME");
                    }
                }

                if (source.Trim().ToLower() == "list")
                {
                    Console.WriteLine(string.Join("\r\n", Duplicati.Library.Main.Interface.List(target, options)));
                }
                else if (source.Trim().ToLower() == "list-current-files")
                {
                    cargs.RemoveAt(0);

                    if (cargs.Count != 1)
                    {
                        PrintWrongNumberOfArguments(cargs, 1);
                        return;
                    }

                    Console.WriteLine(string.Join("\r\n", new List <string>(Duplicati.Library.Main.Interface.ListCurrentFiles(target, options)).ToArray()));
                }
                else if (source.Trim().ToLower() == "list-source-folders")
                {
                    cargs.RemoveAt(0);

                    if (cargs.Count != 1)
                    {
                        PrintWrongNumberOfArguments(cargs, 1);
                        return;
                    }

                    Console.WriteLine(string.Join("\r\n", Duplicati.Library.Main.Interface.ListSourceFolders(target, options) ?? new string[0]));
                }
                else if (source.Trim().ToLower() == "list-actual-signature-files")
                {
                    cargs.RemoveAt(0);

                    if (cargs.Count != 1)
                    {
                        PrintWrongNumberOfArguments(cargs, 1);
                        return;
                    }

                    List <KeyValuePair <Duplicati.Library.Main.RSync.RSyncDir.PatchFileType, string> > files = Duplicati.Library.Main.Interface.ListActualSignatureFiles(cargs[0], options);

                    Console.WriteLine("* " + Strings.Program.DeletedFoldersHeader + ":");
                    foreach (KeyValuePair <Duplicati.Library.Main.RSync.RSyncDir.PatchFileType, string> x in files)
                    {
                        if (x.Key == Duplicati.Library.Main.RSync.RSyncDir.PatchFileType.DeletedFolder)
                        {
                            Console.WriteLine(x.Value);
                        }
                    }

                    Console.WriteLine();
                    Console.WriteLine("* " + Strings.Program.AddedFoldersHeader + ":");
                    foreach (KeyValuePair <Duplicati.Library.Main.RSync.RSyncDir.PatchFileType, string> x in files)
                    {
                        if (x.Key == Duplicati.Library.Main.RSync.RSyncDir.PatchFileType.AddedFolder)
                        {
                            Console.WriteLine(x.Value);
                        }
                    }

                    Console.WriteLine();
                    Console.WriteLine("* " + Strings.Program.DeletedFilesHeader + ":");
                    foreach (KeyValuePair <Duplicati.Library.Main.RSync.RSyncDir.PatchFileType, string> x in files)
                    {
                        if (x.Key == Duplicati.Library.Main.RSync.RSyncDir.PatchFileType.DeletedFile)
                        {
                            Console.WriteLine(x.Value);
                        }
                    }

                    bool hasCombinedSignatures = false;
                    foreach (KeyValuePair <Duplicati.Library.Main.RSync.RSyncDir.PatchFileType, string> x in files)
                    {
                        if (x.Key == Duplicati.Library.Main.RSync.RSyncDir.PatchFileType.AddedOrUpdatedFile)
                        {
                            hasCombinedSignatures = true;
                            break;
                        }
                    }

                    if (hasCombinedSignatures)
                    {
                        Console.WriteLine();
                        Console.WriteLine("* " + Strings.Program.NewOrModifiedFilesHeader + ":");
                        foreach (KeyValuePair <Duplicati.Library.Main.RSync.RSyncDir.PatchFileType, string> x in files)
                        {
                            if (x.Key == Duplicati.Library.Main.RSync.RSyncDir.PatchFileType.AddedOrUpdatedFile)
                            {
                                Console.WriteLine(x.Value);
                            }
                        }
                    }
                    else
                    {
                        Console.WriteLine();
                        Console.WriteLine("* " + Strings.Program.NewFilesHeader + ":");
                        foreach (KeyValuePair <Duplicati.Library.Main.RSync.RSyncDir.PatchFileType, string> x in files)
                        {
                            if (x.Key == Duplicati.Library.Main.RSync.RSyncDir.PatchFileType.AddedFile)
                            {
                                Console.WriteLine(x.Value);
                            }
                        }

                        Console.WriteLine();
                        Console.WriteLine("* " + Strings.Program.ModifiedFilesHeader + ":");
                        foreach (KeyValuePair <Duplicati.Library.Main.RSync.RSyncDir.PatchFileType, string> x in files)
                        {
                            if (x.Key == Duplicati.Library.Main.RSync.RSyncDir.PatchFileType.UpdatedFile)
                            {
                                Console.WriteLine(x.Value);
                            }
                        }
                    }

                    Console.WriteLine();
                    Console.WriteLine("* " + Strings.Program.ControlFilesHeader + ":");
                    foreach (KeyValuePair <Duplicati.Library.Main.RSync.RSyncDir.PatchFileType, string> x in files)
                    {
                        if (x.Key == Duplicati.Library.Main.RSync.RSyncDir.PatchFileType.ControlFile)
                        {
                            Console.WriteLine(x.Value);
                        }
                    }
                }
                else if (source.Trim().ToLower() == "collection-status")
                {
                    cargs.RemoveAt(0);

                    if (cargs.Count != 1)
                    {
                        PrintWrongNumberOfArguments(cargs, 1);
                        return;
                    }

                    List <Duplicati.Library.Main.ManifestEntry> entries = Duplicati.Library.Main.Interface.ParseFileList(cargs[0], options);

                    Console.WriteLine(Strings.Program.CollectionStatusHeader.Replace("\\t", "\t"), entries.Count);

                    foreach (Duplicati.Library.Main.ManifestEntry m in entries)
                    {
                        Console.WriteLine();

                        long size = Math.Max(m.Fileentry.Size, 0);
                        foreach (KeyValuePair <Duplicati.Library.Main.SignatureEntry, Duplicati.Library.Main.ContentEntry> x in m.Volumes)
                        {
                            size += Math.Max(x.Key.Fileentry.Size, 0) + Math.Max(x.Value.Fileentry.Size, 0);
                        }

                        Console.WriteLine(Strings.Program.CollectionStatusLineFull.Replace("\\t", "\t"), m.Time.ToString(), m.Volumes.Count, Library.Utility.Utility.FormatSizeString(size));

                        foreach (Duplicati.Library.Main.ManifestEntry mi in m.Incrementals)
                        {
                            size = Math.Max(mi.Fileentry.Size, 0);
                            foreach (KeyValuePair <Duplicati.Library.Main.SignatureEntry, Duplicati.Library.Main.ContentEntry> x in mi.Volumes)
                            {
                                size += Math.Max(x.Key.Fileentry.Size, 0) + Math.Max(x.Value.Fileentry.Size, 0);
                            }

                            Console.WriteLine(Strings.Program.CollectionStatusLineInc.Replace("\\t", "\t"), mi.Time.ToString(), mi.Volumes.Count, Library.Utility.Utility.FormatSizeString(size));
                        }
                    }
                }
                else if (source.Trim().ToLower() == "delete-all-but-n-full" || source.Trim().ToLower() == "delete-all-but-n")
                {
                    int n = 0;
                    if (!int.TryParse(target, out n) || n < 0)
                    {
                        Console.WriteLine(string.Format(Strings.Program.IntegerParseError, target));
                        return;
                    }

                    options["delete-all-but-n-full"] = n.ToString();

                    cargs.RemoveAt(0);
                    cargs.RemoveAt(0);

                    if (cargs.Count != 1)
                    {
                        PrintWrongNumberOfArguments(cargs, 1);
                        return;
                    }

                    if (source.Trim().ToLower() == "delete-all-but-n")
                    {
                        Console.WriteLine(Duplicati.Library.Main.Interface.DeleteAllButN(cargs[0], options));
                    }
                    else
                    {
                        Console.WriteLine(Duplicati.Library.Main.Interface.DeleteAllButNFull(cargs[0], options));
                    }
                }
                else if (source.Trim().ToLower() == "delete-older-than")
                {
                    try
                    {
                        Duplicati.Library.Utility.Timeparser.ParseTimeSpan(target);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(string.Format(Strings.Program.TimeParseError, target, ex.Message));
                        return;
                    }

                    options["delete-older-than"] = target;

                    cargs.RemoveAt(0);
                    cargs.RemoveAt(0);

                    if (cargs.Count != 1)
                    {
                        PrintWrongNumberOfArguments(cargs, 1);
                        return;
                    }

                    Console.WriteLine(Duplicati.Library.Main.Interface.DeleteOlderThan(cargs[0], options));
                }
                else if (source.Trim().ToLower() == "cleanup")
                {
                    cargs.RemoveAt(0);

                    if (cargs.Count != 1)
                    {
                        PrintWrongNumberOfArguments(cargs, 1);
                        return;
                    }

                    Console.WriteLine(Duplicati.Library.Main.Interface.Cleanup(cargs[0], options));
                }
                else if (source.Trim().ToLower() == "create-folder")
                {
                    cargs.RemoveAt(0);

                    if (cargs.Count != 1)
                    {
                        PrintWrongNumberOfArguments(cargs, 1);
                        return;
                    }

                    Duplicati.Library.Main.Interface.CreateFolder(cargs[0], options);
                    Console.WriteLine(string.Format(Strings.Program.FolderCreatedMessage, cargs[0]));
                }
                else if (source.Trim().ToLower() == "find-last-version")
                {
                    cargs.RemoveAt(0);

                    if (cargs.Count != 1)
                    {
                        PrintWrongNumberOfArguments(cargs, 1);
                        return;
                    }

                    List <KeyValuePair <string, DateTime> > results = Duplicati.Library.Main.Interface.FindLastFileVersion(cargs[0], options);
                    Console.WriteLine(Strings.Program.FindLastVersionHeader.Replace("\\t", "\t"));
                    foreach (KeyValuePair <string, DateTime> k in results)
                    {
                        Console.WriteLine(string.Format(Strings.Program.FindLastVersionEntry.Replace("\\t", "\t"), k.Value.Ticks == 0 ? Strings.Program.FileEntryNotFound : k.Value.ToLocalTime().ToString("yyyyMMdd hhmmss"), k.Key));
                    }
                }
                else if (source.Trim().ToLower() == "verify")
                {
                    cargs.RemoveAt(0);

                    if (cargs.Count != 1)
                    {
                        PrintWrongNumberOfArguments(cargs, 1);
                        return;
                    }

                    List <KeyValuePair <Duplicati.Library.Main.BackupEntryBase, Exception> > results = Duplicati.Library.Main.Interface.VerifyBackup(cargs[0], options);

                    int manifests    = 0;
                    int signatures   = 0;
                    int contentfiles = 0;
                    int errors       = 0;

                    foreach (KeyValuePair <Duplicati.Library.Main.BackupEntryBase, Exception> x in results)
                    {
                        if (x.Key is Duplicati.Library.Main.ManifestEntry)
                        {
                            manifests++;
                        }
                        else if (x.Key is Duplicati.Library.Main.SignatureEntry)
                        {
                            signatures++;
                        }
                        else if (x.Key is Duplicati.Library.Main.ContentEntry)
                        {
                            contentfiles++;
                        }

                        if (x.Value != null)
                        {
                            errors++;
                        }
                    }

                    Console.WriteLine(string.Format(Strings.Program.VerificationCompleted, manifests, signatures, contentfiles, errors));
                    if (errors > 0)
                    {
                        Console.WriteLine();
                        Console.WriteLine(Strings.Program.VerificationErrorHeader);
                        Console.WriteLine();

                        foreach (KeyValuePair <Duplicati.Library.Main.BackupEntryBase, Exception> x in results)
                        {
                            if (x.Value != null)
                            {
                                Console.WriteLine(string.Format("{0}: {1}", x.Key.Filename, x.Value.Message));
                            }
                        }
                    }
                }
                else if (source.IndexOf("://") > 0 || options.ContainsKey("restore"))
                {
                    if (cargs.Count != 2)
                    {
                        PrintWrongNumberOfArguments(cargs, 2);
                        return;
                    }

                    Console.WriteLine(Duplicati.Library.Main.Interface.Restore(source, target.Split(System.IO.Path.PathSeparator), options));
                }
                else
                {
                    if (cargs.Count != 2)
                    {
                        PrintWrongNumberOfArguments(cargs, 2);
                        return;
                    }

                    Console.WriteLine(Duplicati.Library.Main.Interface.Backup(source.Split(System.IO.Path.PathSeparator), target, options));
                }
            }
            catch (Exception ex)
            {
                while (ex is System.Reflection.TargetInvocationException && ex.InnerException != null)
                {
                    ex = ex.InnerException;
                }

                if (!(ex is Library.Interface.CancelException))
                {
                    if (!string.IsNullOrEmpty(ex.Message))
                    {
                        Console.Error.WriteLine();
                        Console.Error.WriteLine(ex.Message);
                    }
                }
                else
                {
                    Console.Error.WriteLine(Strings.Program.UnhandledException, ex.ToString());

                    while (ex.InnerException != null)
                    {
                        ex = ex.InnerException;
                        Console.Error.WriteLine();
                        Console.Error.WriteLine(Strings.Program.UnhandledInnerException, ex.ToString());
                    }
                }
            }
        }