Files
duplicati/Duplicati/Server/WebServer/RESTMethods/CommandLine.cs
T
2017-03-29 09:53:28 +02:00

299 lines
13 KiB
C#

// Copyright (C) 2017, The Duplicati Team
// http://www.duplicati.com, info@duplicati.com
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Duplicati.Server.WebServer.RESTMethods
{
public class CommandLine : IRESTMethodGET, IRESTMethodPOST
{
private readonly Dictionary<string, System.Reflection.MethodInfo> m_supportedCommands;
private class ArgumentCountAttribute : Attribute
{
public int Min = -1;
public int Max = -1;
}
private class DocumentationAttribute : Attribute
{
public string Key;
public string Description;
}
private class ParsedResults
{
public List<string> Arguments;
public Dictionary<string, string> Options;
public Library.Utility.IFilter Filter;
public ParsedResults(RequestInfo info)
{
using (var sr = new StreamReader(info.Request.Body, System.Text.Encoding.UTF8, true))
Arguments = Newtonsoft.Json.JsonConvert.DeserializeObject<List<string>>(sr.ReadToEnd());
var tmpparsed = Library.Utility.FilterCollector.ExtractOptions(Arguments);
Options = tmpparsed.Item1;
Filter = tmpparsed.Item2;
}
}
public CommandLine()
{
m_supportedCommands = new Dictionary<string, System.Reflection.MethodInfo>(StringComparer.OrdinalIgnoreCase);
foreach (var m in this.GetType().GetMethods(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.DeclaredOnly))
foreach (var attr in m.GetCustomAttributes(typeof(DocumentationAttribute), true).Cast<DocumentationAttribute>())
m_supportedCommands[attr.Key] = m;
}
[ArgumentCount(Min = 0, Max = 0)]
[Documentation(Key = "System-Info", Description = "Lists various system properties")]
private Duplicati.Library.Interface.IBasicResults System_Info(ParsedResults pr)
{
using (var controller = new Library.Main.Controller("dummy://", pr.Options, null))
return controller.SystemInfo();
}
[ArgumentCount(Min = 1)]
[Documentation(Key = "Find", Description = "Finds files in a backup")]
private Duplicati.Library.Interface.IBasicResults Find(ParsedResults pr)
{
using (var controller = new Library.Main.Controller(pr.Arguments.First(), pr.Options, null))
return controller.List(pr.Arguments.Skip(1), pr.Filter);
}
[ArgumentCount(Min = 1, Max = 1)]
[Documentation(Key = "List", Description = "Lists all remote backup sets")]
private Duplicati.Library.Interface.IBasicResults List(ParsedResults pr)
{
using (var controller = new Library.Main.Controller(pr.Arguments.First(), pr.Options, null))
return controller.List(pr.Arguments.Skip(1), pr.Filter);
}
[ArgumentCount(Min = 1, Max = 1)]
[Documentation(Key = "Delete", Description = "Deletes specific remote backup sets")]
private Duplicati.Library.Interface.IBasicResults Delete(ParsedResults pr)
{
using (var controller = new Library.Main.Controller(pr.Arguments.First(), pr.Options, null))
return controller.Delete();
}
[ArgumentCount(Min = 1, Max = 1)]
[Documentation(Key = "Repair", Description = "Performs a repair of the remote data")]
private Duplicati.Library.Interface.IBasicResults Repair(ParsedResults pr)
{
using (var controller = new Library.Main.Controller(pr.Arguments.First(), pr.Options, null))
return controller.Repair(pr.Filter);
}
[ArgumentCount(Min = 2)]
[Documentation(Key = "Restore-Control-Files", Description = "Restores control files found in the remote backup")]
private Duplicati.Library.Interface.IBasicResults Restore_Control_Files(ParsedResults pr)
{
using (var controller = new Library.Main.Controller(pr.Arguments.First(), pr.Options, null))
return controller.RestoreControlFiles(pr.Arguments.Skip(1), pr.Filter);
}
[ArgumentCount(Min = 1)]
[Documentation(Key = "List-Control-Files", Description = "Lists control files found in the remote backup")]
private Duplicati.Library.Interface.IBasicResults List_Control_Files(ParsedResults pr)
{
using (var controller = new Library.Main.Controller(pr.Arguments.First(), pr.Options, null))
return controller.ListControlFiles(pr.Arguments.Skip(1), pr.Filter);
}
[ArgumentCount(Min = 1, Max = 1)]
[Documentation(Key = "List-Remote", Description = "Lists files in the remote folder")]
private Duplicati.Library.Interface.IBasicResults List_Remote(ParsedResults pr)
{
using (var controller = new Library.Main.Controller(pr.Arguments.First(), pr.Options, null))
return controller.ListRemote();
}
[ArgumentCount(Min = 1, Max = 1)]
[Documentation(Key = "Compact", Description = "Performs a compact procedure")]
private Duplicati.Library.Interface.IBasicResults Compact(ParsedResults pr)
{
using (var controller = new Library.Main.Controller(pr.Arguments.First(), pr.Options, null))
return controller.Compact();
}
[ArgumentCount(Min = 2, Max = 2)]
[Documentation(Key = "Recreate-Database", Description = "Rebuilds the local database from remote data")]
private Duplicati.Library.Interface.IBasicResults Recreate_Database(ParsedResults pr)
{
using (var controller = new Library.Main.Controller(pr.Arguments.First(), pr.Options, null))
return controller.RecreateDatabase(pr.Arguments.Skip(1).First(), pr.Filter);
}
[ArgumentCount(Min = 2, Max = 2)]
[Documentation(Key = "Create-Bug-Report", Description = "Creates a bug-report database")]
private Duplicati.Library.Interface.IBasicResults Create_Log_Database(ParsedResults pr)
{
using (var controller = new Library.Main.Controller(pr.Arguments.First(), pr.Options, null))
return controller.CreateLogDatabase(pr.Arguments.Skip(1).First());
}
[ArgumentCount(Min = 3)]
[Documentation(Key = "List-Changes", Description = "Lists changes between two versions")]
private Duplicati.Library.Interface.IBasicResults List_Changes(ParsedResults pr)
{
using (var controller = new Library.Main.Controller(pr.Arguments.First(), pr.Options, null))
return controller.ListChanges(pr.Arguments.Skip(1).First(), pr.Arguments.Skip(2).First(), pr.Arguments.Skip(3), pr.Filter);
}
[ArgumentCount(Min = 2)]
[Documentation(Key = "Affected", Description = "Lists files affected by a named remote volume")]
private Duplicati.Library.Interface.IBasicResults List_Affected(ParsedResults pr)
{
using (var controller = new Library.Main.Controller(pr.Arguments.First(), pr.Options, null))
return controller.ListAffected(pr.Arguments.Skip(1).ToList());
}
[ArgumentCount(Min = 1, Max = 2)]
[Documentation(Key = "Test", Description = "Performs verification on the remote files")]
private Duplicati.Library.Interface.IBasicResults Test(ParsedResults pr)
{
long samples = 1;
if (pr.Arguments.Count > 1)
samples = long.Parse(pr.Arguments[2]);
using (var controller = new Library.Main.Controller(pr.Arguments.First(), pr.Options, null))
return controller.Test(samples);
}
[ArgumentCount(Min = 2)]
[Documentation(Key = "Test-Filter", Description = "Tests filters")]
private Duplicati.Library.Interface.IBasicResults Test_Filter(ParsedResults pr)
{
using (var controller = new Library.Main.Controller(pr.Arguments.First(), pr.Options, null))
return controller.TestFilter(pr.Arguments.Skip(1).ToArray(), pr.Filter);
}
[ArgumentCount(Min = 2)]
[Documentation(Key = "Backup", Description = "Runs the backup operation")]
private Duplicati.Library.Interface.IBasicResults Backup(ParsedResults pr)
{
using (var controller = new Library.Main.Controller(pr.Arguments.First(), pr.Options, null))
return controller.TestFilter(pr.Arguments.Skip(1).ToArray(), pr.Filter);
}
[ArgumentCount(Min = 1, Max = 1)]
[Documentation(Key = "Purge", Description = "Purges named files from remote filesets")]
private Duplicati.Library.Interface.IBasicResults Purge_Files(ParsedResults pr)
{
using (var controller = new Library.Main.Controller(pr.Arguments.First(), pr.Options, null))
return controller.PurgeFiles(pr.Filter);
}
[ArgumentCount(Min = 1, Max = 1)]
[Documentation(Key = "List-Broken-Files", Description = "Lists files that cannot be recovered due to broken or missing remote files")]
private Duplicati.Library.Interface.IBasicResults List_Broken_Files(ParsedResults pr)
{
using (var controller = new Library.Main.Controller(pr.Arguments.First(), pr.Options, null))
return controller.ListBrokenFiles(pr.Filter);
}
[ArgumentCount(Min = 1, Max = 1)]
[Documentation(Key = "Purge-Broken-Files", Description = "Removes files that cannot be restored from the remote, due to missing or broken files")]
private Duplicati.Library.Interface.IBasicResults Purge_Broken_Files(ParsedResults pr)
{
using (var controller = new Library.Main.Controller(pr.Arguments.First(), pr.Options, null))
return controller.ListBrokenFiles(pr.Filter);
}
public void POST(string key, RequestInfo info)
{
var pr = new ParsedResults(info);
if (string.IsNullOrWhiteSpace(key) && pr.Arguments.Count > 0)
{
key = pr.Arguments[0];
pr.Arguments.RemoveAt(0);
}
System.Reflection.MethodInfo method;
if (!m_supportedCommands.TryGetValue(key ?? string.Empty, out method))
{
info.ReportClientError("Not Found", System.Net.HttpStatusCode.NotFound);
return;
}
var limits = method.GetCustomAttributes(typeof(ArgumentCountAttribute), true).Cast<ArgumentCountAttribute>().FirstOrDefault();
if (limits != null)
{
if (limits.Min >= 0 && pr.Arguments.Count < limits.Min)
{
info.ReportClientError("Too few arguments");
return;
}
if (limits.Max >= 0 && pr.Arguments.Count > limits.Max)
{
info.ReportClientError("Too many arguments");
return;
}
}
try
{
var res = method.Invoke(this, new object[] { pr });
info.OutputOK(res.ToString());
}
catch (Exception ex)
{
var rx = ex;
if (rx is System.Reflection.TargetInvocationException)
rx = rx.InnerException;
if (rx is Library.Interface.UserInformationException)
{
info.OutputError(rx.Message, System.Net.HttpStatusCode.BadRequest, rx.Message);
return;
}
throw rx;
}
}
public void GET(string key, RequestInfo info)
{
info.OutputOK(
m_supportedCommands
.Values
.SelectMany(
x =>
x.GetCustomAttributes(typeof(DocumentationAttribute), true)
.Cast<DocumentationAttribute>()
.Select(y => new { Method = x, Attr = y })
)
.Select(
x =>
new
{
Key = x.Attr.Key,
Description = x.Attr.Description,
}
)
);
}
}
}