2015-01-20 21:44:52 +01:00
// Copyright (C) 2015, The Duplicati Team
2013-05-02 17:24:38 +02:00
2015-01-20 21:44:52 +01:00
// http://www.duplicati.com, info@duplicati.com
2013-05-02 17:24:38 +02:00
//
// 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 ;
2013-05-11 12:03:15 +02:00
using System.Linq ;
2013-05-02 17:24:38 +02:00
using System.Collections.Generic ;
2017-04-04 16:56:34 +02:00
using System.IO ;
2013-05-02 17:24:38 +02:00
namespace Duplicati.CommandLine
{
public static class Commands
{
2013-08-23 22:18:13 +02:00
private class PeriodicOutput : IDisposable
{
public event Action < float , long , long , bool > WriteOutput ;
private System . Threading . ManualResetEvent m_readyEvent ;
private System . Threading . ManualResetEvent m_finishEvent ;
private ConsoleOutput m_output ;
private System . Threading . Thread m_thread ;
private TimeSpan m_updateFrequency ;
public PeriodicOutput ( ConsoleOutput messageSink , TimeSpan updateFrequency )
{
m_output = messageSink ;
m_readyEvent = new System . Threading . ManualResetEvent ( false );
m_finishEvent = new System . Threading . ManualResetEvent ( false );
m_updateFrequency = updateFrequency ;
m_thread = new System . Threading . Thread ( this . ThreadMain );
m_thread . IsBackground = true ;
m_thread . Name = "Periodic console writer" ;
m_thread . Start ();
}
public void SetReady () { m_readyEvent . Set (); }
public void SetFinished () { m_finishEvent . Set (); }
public bool Join ( TimeSpan wait )
{
if ( m_thread != null )
return m_thread . Join ( wait );
return true ;
}
private void ThreadMain ()
{
m_readyEvent . WaitOne ();
if ( m_finishEvent . WaitOne ( TimeSpan . FromMilliseconds ( 10 ), true ))
return ;
var last_count = - 1L ;
var finished = false ;
while ( true )
{
Duplicati . Library . Main . OperationPhase phase ;
float progress ;
long filesprocessed ;
long filesizeprocessed ;
long filecount ;
long filesize ;
bool counting ;
m_output . OperationProgress . UpdateOverall ( out phase , out progress , out filesprocessed , out filesizeprocessed , out filecount , out filesize , out counting );
var files = Math . Max ( 0 , filecount - filesprocessed );
var size = Math . Max ( 0 , filesize - filesizeprocessed );
2013-08-24 13:43:33 +02:00
if ( finished )
{
files = 0 ;
size = 0 ;
}
else if ( size > 0 )
2013-08-24 21:16:24 +02:00
files = Math . Max ( 1 , files );
2013-08-24 13:43:33 +02:00
if ( last_count < 0 || files != last_count )
2013-08-23 22:18:13 +02:00
if ( WriteOutput != null )
WriteOutput ( progress , files , size , counting );
if (! counting )
last_count = files ;
if ( finished )
return ;
finished = m_finishEvent . WaitOne ( m_updateFrequency , true );
}
}
public void Dispose ()
{
if ( m_thread != null )
{
try
{
m_finishEvent . Set ();
m_readyEvent . Set ();
if ( m_thread != null && m_thread . IsAlive )
{
m_thread . Abort ();
m_thread . Join ( 500 );
}
}
finally
{
m_thread = null ;
}
}
}
}
2016-09-23 10:47:59 +02:00
2017-04-04 16:56:34 +02:00
public static int Examples ( TextWriter outwriter , Action < Duplicati . Library . Main . Controller > setup , List < string > args , Dictionary < string , string > options , Library . Utility . IFilter filter )
2016-09-23 14:11:53 +02:00
{
2017-04-04 16:56:34 +02:00
Duplicati . CommandLine . Help . PrintUsage ( outwriter , "example" , options );
2016-09-23 14:11:53 +02:00
return 0 ;
}
2013-08-23 22:18:13 +02:00
2017-04-04 16:56:34 +02:00
public static int Help ( TextWriter outwriter , Action < Duplicati . Library . Main . Controller > setup , List < string > args , Dictionary < string , string > options , Library . Utility . IFilter filter )
2013-05-02 17:24:38 +02:00
{
2018-05-23 20:09:03 +02:00
Duplicati . CommandLine . Help . PrintUsage ( outwriter , args . Count > 1 ? args [ 1 ] : "help" , options );
2013-05-02 17:24:38 +02:00
return 0 ;
}
2017-04-04 16:56:34 +02:00
public static int Affected ( TextWriter outwriter , Action < Duplicati . Library . Main . Controller > setup , List < string > args , Dictionary < string , string > options , Library . Utility . IFilter filter )
2014-08-19 20:27:14 +02:00
{
2018-03-12 14:07:11 +01:00
var fullresult = Duplicati . Library . Utility . Utility . ParseBoolOption ( options , "full-result" );
2014-08-19 20:27:14 +02:00
var backend = args [ 0 ];
args . RemoveAt ( 0 );
if ( args . Count == 0 )
{
2017-04-04 16:56:34 +02:00
outwriter . WriteLine ( "You must specify at least a remote filename" );
2014-08-19 20:27:14 +02:00
return 200 ;
}
// Support for not adding the --auth-username if possible
string dbpath ;
options . TryGetValue ( "dbpath" , out dbpath );
if ( string . IsNullOrEmpty ( dbpath ))
{
dbpath = Library . Main . DatabaseLocator . GetDatabasePath ( backend , new Duplicati . Library . Main . Options ( options ), false , true );
if ( dbpath != null )
options [ "dbpath" ] = dbpath ;
}
// Don't ask for passphrase if we have a local db
if (! string . IsNullOrEmpty ( dbpath ) && System . IO . File . Exists ( dbpath ) && ! options . ContainsKey ( "no-encryption" ) && ! Duplicati . Library . Utility . Utility . ParseBoolOption ( options , "no-local-db" ))
{
string passphrase ;
options . TryGetValue ( "passphrase" , out passphrase );
if ( string . IsNullOrEmpty ( passphrase ))
options [ "no-encryption" ] = "true" ;
}
2018-03-12 14:07:11 +01:00
using ( var console = new ConsoleOutput ( outwriter , options ))
using ( var i = new Library . Main . Controller ( backend , options , console ))
2014-08-19 20:27:14 +02:00
{
2017-04-04 16:56:34 +02:00
setup ( i );
2017-04-04 23:53:36 +02:00
i . ListAffected ( args , res =>
{
if ( res . Filesets != null && res . Filesets . Count () != 0 )
{
outwriter . WriteLine ( "The following filesets are affected:" );
foreach ( var e in res . Filesets )
outwriter . WriteLine ( "{0}\t: {1}" , e . Version , e . Time );
outwriter . WriteLine ();
}
2014-08-19 20:27:14 +02:00
2017-04-04 23:53:36 +02:00
if ( res . Files != null )
{
var filecount = res . Files . Count ();
if ( filecount == 0 )
{
outwriter . WriteLine ( "No files are affected" );
}
else
{
var c = 0 ;
outwriter . WriteLine ( "A total of {0} file(s) are affected:" , filecount );
foreach ( var file in res . Files )
{
c ++;
outwriter . WriteLine ( file . Path );
if ( c > 10 && filecount > 10 && ! fullresult )
{
outwriter . WriteLine ( " ... and {0} more (use --{1} to see all filenames)" , filecount - c , "full-result" );
break ;
}
}
2014-08-19 20:27:14 +02:00
2017-04-04 23:53:36 +02:00
}
2014-08-19 20:27:14 +02:00
2017-04-04 23:53:36 +02:00
outwriter . WriteLine ();
}
2014-08-19 20:27:14 +02:00
2017-04-04 23:53:36 +02:00
if ( res . LogMessages != null )
{
var logcount = res . LogMessages . Count ();
if ( logcount == 0 || ( logcount > 10 && ! fullresult ))
outwriter . WriteLine ( "Found {0} related log messages (use --{1} to see the data)" , res . Files . Count (), "full-result" );
2014-08-19 20:27:14 +02:00
else
2017-04-04 23:53:36 +02:00
{
outwriter . WriteLine ( "The following related log messages were found:" );
foreach ( var log in res . LogMessages )
if ( log . Message . Length > 100 && ! fullresult )
outwriter . WriteLine ( "{0}: {1}" , log . Timestamp , log . Message . Substring ( 0 , 96 ) + " ..." );
else
outwriter . WriteLine ( "{0}: {1}" , log . Timestamp , log . Message );
}
outwriter . WriteLine ();
}
});
2014-08-19 20:27:14 +02:00
return 0 ;
}
}
2017-04-04 16:56:34 +02:00
public static int List ( TextWriter outwriter , Action < Duplicati . Library . Main . Controller > setup , List < string > args , Dictionary < string , string > options , Library . Utility . IFilter filter )
2013-05-02 17:24:38 +02:00
{
2014-08-19 20:24:54 +02:00
filter = filter ?? new Duplicati . Library . Utility . FilterExpression ();
if ( Duplicati . Library . Utility . Utility . ParseBoolOption ( options , "list-sets-only" ))
filter = new Duplicati . Library . Utility . FilterExpression ();
2018-03-12 14:07:11 +01:00
using ( var console = new ConsoleOutput ( outwriter , options ))
using ( var i = new Library . Main . Controller ( args [ 0 ], options , console ))
2013-05-30 21:54:43 +02:00
{
2017-04-04 16:56:34 +02:00
setup ( i );
2013-09-27 20:49:36 +02:00
var backend = args [ 0 ];
2013-05-11 13:04:01 +02:00
args . RemoveAt ( 0 );
2013-05-30 21:54:43 +02:00
if ( args . Count == 1 )
{
2013-05-11 13:04:01 +02:00
long v ;
2013-05-30 21:54:43 +02:00
if ( long . TryParse ( args [ 0 ], out v ))
{
if (! options . ContainsKey ( "version" ))
{
2013-05-11 13:04:01 +02:00
args . RemoveAt ( 0 );
args . Add ( "*" );
options [ "version" ] = v . ToString ();
}
2013-05-30 21:54:43 +02:00
}
2017-11-26 10:53:14 -08:00
else if ( args [ 0 ]. IndexOfAny ( new char [] { '*' , '?' }) < 0 && ! args [ 0 ]. StartsWith ( "[" , StringComparison . Ordinal ))
2013-05-30 21:54:43 +02:00
{
try
{
2013-05-11 13:04:01 +02:00
var t = Library . Utility . Timeparser . ParseTimeInterval ( args [ 0 ], DateTime . Now , true );
args . RemoveAt ( 0 );
args . Add ( "*" );
options [ "time" ] = t . ToString ();
2013-05-30 21:54:43 +02:00
}
catch
{
2013-05-11 13:04:01 +02:00
}
}
}
2013-08-17 13:48:05 +02:00
// Prefix all filenames with "*/" so we search all folders
for ( var ix = 0 ; ix < args . Count ; ix ++)
2017-11-26 10:53:14 -08:00
if ( args [ ix ]. IndexOfAny ( new char [] { '*' , '?' , System . IO . Path . DirectorySeparatorChar , System . IO . Path . AltDirectorySeparatorChar }) < 0 && ! args [ ix ]. StartsWith ( "[" , StringComparison . Ordinal ))
2013-08-17 13:48:05 +02:00
args [ ix ] = "*" + System . IO . Path . DirectorySeparatorChar . ToString () + args [ ix ];
2013-09-13 13:24:11 +02:00
// Support for not adding the --auth-username if possible
string dbpath ;
options . TryGetValue ( "dbpath" , out dbpath );
if ( string . IsNullOrEmpty ( dbpath ))
{
2013-09-27 20:49:36 +02:00
dbpath = Library . Main . DatabaseLocator . GetDatabasePath ( backend , new Duplicati . Library . Main . Options ( options ), false , true );
2013-09-13 13:24:11 +02:00
if ( dbpath != null )
options [ "dbpath" ] = dbpath ;
}
// Don't ask for passphrase if we have a local db
if (! string . IsNullOrEmpty ( dbpath ) && System . IO . File . Exists ( dbpath ) && ! options . ContainsKey ( "no-encryption" ) && ! Duplicati . Library . Utility . Utility . ParseBoolOption ( options , "no-local-db" ))
{
string passphrase ;
options . TryGetValue ( "passphrase" , out passphrase );
if ( string . IsNullOrEmpty ( passphrase ))
2016-10-19 11:28:22 +02:00
{
string existing ;
options . TryGetValue ( "disable-module" , out existing );
if ( string . IsNullOrWhiteSpace ( existing ))
options [ "disable-module" ] = "console-password-input" ;
else
options [ "disable-module" ] = string . Join ( "," , new string [] { existing , "console-password-input" });
}
2013-09-13 13:24:11 +02:00
}
2014-08-19 20:24:54 +02:00
2013-09-13 13:24:11 +02:00
2013-05-30 21:54:43 +02:00
bool controlFiles = Library . Utility . Utility . ParseBoolOption ( options , "control-files" );
options . Remove ( "control-files" );
var res = controlFiles ? i . ListControlFiles ( args , filter ) : i . List ( args , filter );
2015-09-15 19:28:02 +02:00
2013-06-30 12:50:30 +02:00
//If there are no files matching, and we are looking for one or more files,
// try again with all-versions set
2015-09-15 19:28:02 +02:00
var compareFilter = Library . Utility . JoinedFilterExpression . Join ( new Library . Utility . FilterExpression ( args ), filter );
2013-08-06 23:17:08 +02:00
var isRequestForFiles =
2013-06-30 12:50:30 +02:00
! controlFiles && res . Filesets . Count () != 0 &&
( res . Files == null || res . Files . Count () == 0 ) &&
2015-09-15 19:28:02 +02:00
! compareFilter . Empty ;
2013-08-06 23:17:08 +02:00
if ( isRequestForFiles && ! Library . Utility . Utility . ParseBoolOption ( options , "all-versions" ))
2013-06-30 12:50:30 +02:00
{
2017-04-04 16:56:34 +02:00
outwriter . WriteLine ( "No files matching, looking in all versions" );
2013-06-30 12:50:30 +02:00
options [ "all-versions" ] = "true" ;
2013-08-06 23:17:08 +02:00
options . Remove ( "time" );
options . Remove ( "version" );
2013-06-30 12:50:30 +02:00
res = i . List ( args , filter );
}
2015-09-15 19:28:02 +02:00
if ( res . Filesets . Count () != 0 && ( res . Files == null || res . Files . Count () == 0 ) && compareFilter . Empty )
2013-05-30 21:54:43 +02:00
{
2017-04-04 16:56:34 +02:00
outwriter . WriteLine ( "Listing filesets:" );
2013-05-30 21:54:43 +02:00
foreach ( var e in res . Filesets )
{
if ( e . FileCount >= 0 )
2017-04-04 16:56:34 +02:00
outwriter . WriteLine ( "{0}\t: {1} ({2} files, {3})" , e . Version , e . Time , e . FileCount , Library . Utility . Utility . FormatSizeString ( e . FileSizes ));
2013-05-30 21:54:43 +02:00
else
2017-04-04 16:56:34 +02:00
outwriter . WriteLine ( "{0}\t: {1}" , e . Version , e . Time );
2013-05-30 21:54:43 +02:00
}
2013-08-06 23:17:08 +02:00
}
2015-09-15 19:28:02 +02:00
else if ( isRequestForFiles )
{
2017-04-04 16:56:34 +02:00
outwriter . WriteLine ( "No files matched expression" );
2015-09-15 19:28:02 +02:00
}
2013-08-06 23:17:08 +02:00
else
2013-05-30 21:54:43 +02:00
{
2013-08-06 23:17:08 +02:00
if ( res . Filesets . Count () == 0 )
{
2017-04-04 16:56:34 +02:00
outwriter . WriteLine ( "No time or version matched a fileset" );
2013-08-06 23:17:08 +02:00
}
else if ( res . Files == null || res . Files . Count () == 0 )
2013-05-11 13:04:01 +02:00
{
2017-04-04 16:56:34 +02:00
outwriter . WriteLine ( "Found {0} filesets, but no files matched" , res . Filesets . Count ());
2013-05-11 13:04:01 +02:00
}
else if ( res . Filesets . Count () == 1 )
2013-05-11 12:03:15 +02:00
{
2013-05-11 13:04:01 +02:00
var f = res . Filesets . First ();
2017-04-04 16:56:34 +02:00
outwriter . WriteLine ( "Listing contents {0} ({1}):" , f . Version , f . Time );
2013-05-11 12:03:15 +02:00
foreach ( var e in res . Files )
2017-11-26 10:53:14 -08:00
outwriter . WriteLine ( "{0} {1}" , e . Path , e . Path . EndsWith ( System . IO . Path . DirectorySeparatorChar . ToString (), StringComparison . Ordinal ) ? "" : "(" + Library . Utility . Utility . FormatSizeString ( e . Sizes . First ()) + ")" );
2013-05-11 12:03:15 +02:00
}
else
{
2017-04-04 16:56:34 +02:00
outwriter . WriteLine ( "Listing files and versions:" );
2013-05-11 12:03:15 +02:00
foreach ( var e in res . Files )
{
2017-04-04 16:56:34 +02:00
outwriter . WriteLine ( e . Path );
2013-05-30 21:54:43 +02:00
foreach ( var nx in res . Filesets . Zip ( e . Sizes , ( a , b ) => new { Index = a . Version , Time = a . Time , Size = b } ))
2017-04-04 16:56:34 +02:00
outwriter . WriteLine ( "{0}\t: {1} {2}" , nx . Index , nx . Time , nx . Size < 0 ? " - " : Library . Utility . Utility . FormatSizeString ( nx . Size ));
2013-05-11 12:03:15 +02:00
2017-04-04 16:56:34 +02:00
outwriter . WriteLine ();
2013-05-11 12:03:15 +02:00
}
}
}
}
2013-05-02 17:24:38 +02:00
return 0 ;
}
2013-05-11 22:56:21 +02:00
2017-04-04 16:56:34 +02:00
public static int Delete ( TextWriter outwriter , Action < Duplicati . Library . Main . Controller > setup , List < string > args , Dictionary < string , string > options , Library . Utility . IFilter filter )
2016-09-15 11:39:27 +02:00
{
var requiredOptions = new string [] { "keep-time" , "keep-versions" , "version" };
2013-05-11 22:56:21 +02:00
2017-09-18 23:23:45 -06:00
if (! options . Keys . Where ( x => requiredOptions . Contains ( x , StringComparer . OrdinalIgnoreCase )). Any ())
2016-09-15 11:39:27 +02:00
{
2017-04-04 16:56:34 +02:00
outwriter . WriteLine ( Strings . Program . DeleteCommandNeedsOptions ( "delete" , requiredOptions ));
2016-09-15 11:39:27 +02:00
return 200 ;
}
2013-05-11 22:56:21 +02:00
2018-03-12 14:07:11 +01:00
using ( var console = new ConsoleOutput ( outwriter , options ))
using ( var i = new Library . Main . Controller ( args [ 0 ], options , console ))
2016-09-15 11:39:27 +02:00
{
2017-04-04 16:56:34 +02:00
setup ( i );
2016-09-15 11:39:27 +02:00
args . RemoveAt ( 0 );
var res = i . Delete ();
2013-05-08 21:29:59 +02:00
2016-09-15 11:39:27 +02:00
if ( res . DeletedSets . Count () == 0 )
{
2017-04-04 16:56:34 +02:00
outwriter . WriteLine ( Strings . Program . NoFilesetsMatching );
2016-09-15 11:39:27 +02:00
}
else
{
if ( res . Dryrun )
2017-04-04 16:56:34 +02:00
outwriter . WriteLine ( Strings . Program . WouldDeleteBackups );
2016-09-15 11:39:27 +02:00
else
2017-04-04 16:56:34 +02:00
outwriter . WriteLine ( Strings . Program . DeletedBackups );
2016-09-15 11:39:27 +02:00
foreach ( var f in res . DeletedSets )
2017-04-04 16:56:34 +02:00
outwriter . WriteLine ( string . Format ( "{0}: {1}" , f . Item1 , f . Item2 ));
2016-09-15 11:39:27 +02:00
}
2013-05-02 17:24:38 +02:00
}
return 0 ;
2013-05-11 22:56:21 +02:00
2013-05-02 17:24:38 +02:00
}
2017-04-04 16:56:34 +02:00
public static int Repair ( TextWriter outwriter , Action < Duplicati . Library . Main . Controller > setup , List < string > args , Dictionary < string , string > options , Library . Utility . IFilter filter )
2013-05-02 17:24:38 +02:00
{
if ( args . Count != 1 )
2017-04-04 16:56:34 +02:00
return PrintWrongNumberOfArguments ( outwriter , args , 1 );
2013-05-02 17:24:38 +02:00
2018-03-12 14:07:11 +01:00
using ( var console = new ConsoleOutput ( outwriter , options ))
using ( var i = new Duplicati . Library . Main . Controller ( args [ 0 ], options , console ))
2017-04-04 16:56:34 +02:00
{
setup ( i );
2015-04-08 20:19:46 +02:00
i . Repair ( filter );
2017-04-04 16:56:34 +02:00
}
2013-05-08 21:29:59 +02:00
2013-05-02 17:24:38 +02:00
return 0 ;
}
2017-04-04 16:56:34 +02:00
public static int Restore ( TextWriter outwriter , Action < Duplicati . Library . Main . Controller > setup , List < string > args , Dictionary < string , string > options , Library . Utility . IFilter filter )
2013-05-02 17:24:38 +02:00
{
2013-05-13 22:32:05 +02:00
if ( args . Count < 1 )
2017-04-04 16:56:34 +02:00
return PrintWrongNumberOfArguments ( outwriter , args , 1 );
2013-05-13 22:32:05 +02:00
string backend = args [ 0 ];
2013-05-20 13:44:18 +02:00
args . RemoveAt ( 0 );
2013-05-30 23:00:09 +02:00
bool controlFiles = Library . Utility . Utility . ParseBoolOption ( options , "control-files" );
options . Remove ( "control-files" );
2013-08-25 13:15:40 +02:00
// Prefix all filenames with "*/" so we search all folders
for ( var ix = 0 ; ix < args . Count ; ix ++)
2017-11-26 10:53:14 -08:00
if ( args [ ix ]. IndexOfAny ( new char [] { '*' , '?' , System . IO . Path . DirectorySeparatorChar , System . IO . Path . AltDirectorySeparatorChar }) < 0 && ! args [ ix ]. StartsWith ( "[" , StringComparison . Ordinal ))
2013-08-25 13:15:40 +02:00
args [ ix ] = "*" + System . IO . Path . DirectorySeparatorChar . ToString () + args [ ix ];
2015-09-16 22:03:08 +02:00
// suffix all folders with "*" so we restore all contents in the folder
for ( var ix = 0 ; ix < args . Count ; ix ++)
2017-11-26 10:53:14 -08:00
if ( args [ ix ]. IndexOfAny ( new char [] { '*' , '?' }) < 0 && ! args [ ix ]. StartsWith ( "[" , StringComparison . Ordinal ) && args [ ix ]. EndsWith ( System . IO . Path . DirectorySeparatorChar . ToString (), StringComparison . Ordinal ))
2015-09-16 22:03:08 +02:00
args [ ix ] += "*" ;
2013-08-22 20:52:54 +02:00
2018-03-12 14:07:11 +01:00
using ( var output = new ConsoleOutput ( outwriter , options ))
2017-04-04 16:56:34 +02:00
using ( var i = new Library . Main . Controller ( backend , options , output ))
{
2018-03-12 14:07:11 +01:00
output . MessageEvent ( string . Format ( "Restore started at {0}" , DateTime . Now ));
2017-04-04 16:56:34 +02:00
setup ( i );
2013-05-30 23:00:09 +02:00
if ( controlFiles )
{
var res = i . RestoreControlFiles ( args . ToArray (), filter );
2013-08-22 20:52:54 +02:00
output . MessageEvent ( "Restore control files completed:" );
2017-04-04 16:56:34 +02:00
foreach ( var s in res . Files )
outwriter . WriteLine ( s );
2013-05-30 23:00:09 +02:00
}
else
{
2017-04-04 16:56:34 +02:00
using ( var periodicOutput = new PeriodicOutput ( output , TimeSpan . FromSeconds ( 5 )))
2013-08-23 22:18:13 +02:00
{
2017-04-04 16:56:34 +02:00
output . PhaseChanged += ( phase , previousPhase ) =>
{
2017-09-26 21:01:33 -07:00
switch ( phase )
2013-08-23 22:18:13 +02:00
{
2017-09-26 21:01:33 -07:00
case Duplicati . Library . Main . OperationPhase . Restore_PreRestoreVerify :
output . MessageEvent ( "Checking remote backup ..." );
break ;
case Duplicati . Library . Main . OperationPhase . Restore_ScanForExistingFiles :
output . MessageEvent ( "Checking existing target files ..." );
break ;
case Duplicati . Library . Main . OperationPhase . Restore_PatchWithLocalBlocks :
output . MessageEvent ( "Updating target files with local data ..." );
break ;
case Duplicati . Library . Main . OperationPhase . Restore_PostRestoreVerify :
periodicOutput . SetFinished ();
periodicOutput . Join ( TimeSpan . FromMilliseconds ( 100 ));
output . MessageEvent ( "Verifying restored files ..." );
break ;
case Duplicati . Library . Main . OperationPhase . Restore_ScanForLocalBlocks :
output . MessageEvent ( "Scanning local files for needed data ..." );
break ;
case Duplicati . Library . Main . OperationPhase . Restore_CreateTargetFolders :
periodicOutput . SetReady ();
break ;
2013-08-23 22:18:13 +02:00
}
};
2017-04-04 16:56:34 +02:00
periodicOutput . WriteOutput += ( progress , files , size , counting ) =>
{
2013-08-24 13:43:33 +02:00
output . MessageEvent ( string . Format ( " {0} files need to be restored ({1})" , files , Library . Utility . Utility . FormatSizeString ( size )));
2013-08-23 22:18:13 +02:00
};
2017-04-04 16:56:34 +02:00
2013-08-23 22:18:13 +02:00
var res = i . Restore ( args . ToArray (), filter );
string restorePath ;
options . TryGetValue ( "restore-path" , out restorePath );
2017-04-04 16:56:34 +02:00
2013-08-23 22:18:13 +02:00
output . MessageEvent ( string . Format ( "Restored {0} ({1}) files to {2}" , res . FilesRestored , Library . Utility . Utility . FormatSizeString ( res . SizeOfRestoredFiles ), string . IsNullOrEmpty ( restorePath ) ? "original path" : restorePath ));
2013-08-24 13:43:33 +02:00
output . MessageEvent ( string . Format ( "Duration of restore: {0:hh\\:mm\\:ss}" , res . Duration ));
2017-04-04 16:56:34 +02:00
2015-12-22 02:15:49 +01:00
if ( res . FilesRestored > 0 && ! Library . Main . Utility . SuppressDonationMessages )
2013-08-23 22:18:13 +02:00
{
2014-08-21 10:31:13 +02:00
output . MessageEvent ( "***********************************************" );
2014-08-19 11:14:31 +02:00
output . MessageEvent ( "Did we help save your files? If so, please support Duplicati with a donation. We suggest 10€ for private use and 100€ for commercial use." );
2013-08-23 22:18:13 +02:00
output . MessageEvent ( "" );
2018-01-18 13:48:40 +01:00
output . MessageEvent ( "https://www.duplicati.com/donate/" );
2014-08-21 10:31:13 +02:00
output . MessageEvent ( "***********************************************" );
2013-08-23 22:18:13 +02:00
}
2017-04-04 16:56:34 +02:00
2018-03-12 14:07:11 +01:00
if ( output . FullResults )
2017-04-04 16:56:34 +02:00
Library . Utility . Utility . PrintSerializeObject ( res , outwriter );
2015-01-27 22:06:23 +01:00
if ( res . Warnings . Count () > 0 )
return 2 ;
2013-08-23 22:18:13 +02:00
}
2013-05-30 23:00:09 +02:00
}
2017-04-04 16:56:34 +02:00
}
2013-05-02 17:24:38 +02:00
return 0 ;
}
2017-04-04 16:56:34 +02:00
public static int Backup ( TextWriter outwriter , Action < Duplicati . Library . Main . Controller > setup , List < string > args , Dictionary < string , string > options , Library . Utility . IFilter filter )
2013-05-02 17:24:38 +02:00
{
2013-05-13 22:32:05 +02:00
if ( args . Count < 2 )
2017-04-04 16:56:34 +02:00
return PrintWrongNumberOfArguments ( outwriter , args , 2 );
2013-05-13 22:32:05 +02:00
var backend = args [ 0 ];
args . RemoveAt ( 0 );
2013-08-20 22:52:50 +02:00
var dirs = args . ToArray ();
2013-05-25 16:40:15 +02:00
Library . Interface . IBackupResults result ;
2018-03-12 14:07:11 +01:00
using ( var output = new ConsoleOutput ( outwriter , options ))
2013-08-20 22:52:50 +02:00
{
2018-03-12 14:07:11 +01:00
using ( var periodicOutput = new PeriodicOutput ( output , TimeSpan . FromSeconds ( 5 )))
2013-08-20 22:52:50 +02:00
{
2018-03-11 10:37:52 -07:00
if (( new Duplicati . Library . Main . Options ( options )). DisableOnBattery && ( Duplicati . Library . Utility . Power . PowerSupply . GetSource () == Duplicati . Library . Utility . Power . PowerSupply . Source . Battery ))
{
output . MessageEvent ( "The \"disable-on-battery\" option only affects scheduled backups and is ignored by backups run manually or from the command line." );
}
2018-03-12 14:07:11 +01:00
output . MessageEvent ( string . Format ( "Backup started at {0}" , DateTime . Now ));
2017-09-26 21:32:58 -07:00
2018-03-12 14:07:11 +01:00
output . PhaseChanged += ( phase , previousPhase ) =>
2013-09-10 20:41:28 +02:00
{
2018-03-12 14:07:11 +01:00
if ( previousPhase == Duplicati . Library . Main . OperationPhase . Backup_PostBackupTest )
output . MessageEvent ( "Remote backup verification completed" );
2017-04-04 16:56:34 +02:00
2018-03-12 14:07:11 +01:00
switch ( phase )
{
case Duplicati . Library . Main . OperationPhase . Backup_ProcessingFiles :
output . MessageEvent ( "Scanning local files ..." );
periodicOutput . SetReady ();
break ;
case Duplicati . Library . Main . OperationPhase . Backup_Finalize :
periodicOutput . SetFinished ();
break ;
case Duplicati . Library . Main . OperationPhase . Backup_PreBackupVerify :
output . MessageEvent ( "Checking remote backup ..." );
break ;
case Duplicati . Library . Main . OperationPhase . Backup_PostBackupVerify :
output . MessageEvent ( "Checking remote backup ..." );
break ;
case Duplicati . Library . Main . OperationPhase . Backup_PostBackupTest :
output . MessageEvent ( "Verifying remote backup ..." );
break ;
case Duplicati . Library . Main . OperationPhase . Backup_Compact :
output . MessageEvent ( "Compacting remote backup ..." );
break ;
}
};
2018-02-07 10:35:16 -07:00
2018-03-12 14:07:11 +01:00
periodicOutput . WriteOutput += ( progress , files , size , counting ) =>
{
output . MessageEvent ( string . Format ( " {0} files need to be examined ({1}){2}" , files , Library . Utility . Utility . FormatSizeString ( size ), counting ? " (still counting)" : "" ));
};
using ( var i = new Library . Main . Controller ( backend , options , output ))
{
setup ( i );
result = i . Backup ( dirs , filter );
}
2018-02-07 10:35:16 -07:00
}
2017-09-22 00:30:10 -06:00
2018-03-12 14:07:11 +01:00
if ( output . FullResults )
2018-02-07 10:35:16 -07:00
{
2018-03-12 14:07:11 +01:00
Library . Utility . Utility . PrintSerializeObject ( result , outwriter );
outwriter . WriteLine ();
2018-02-07 10:35:16 -07:00
}
2017-09-22 00:30:10 -06:00
2018-03-12 14:07:11 +01:00
var parsedStats = result . BackendStatistics as Duplicati . Library . Interface . IParsedBackendStatistics ;
output . MessageEvent ( string . Format ( " Duration of backup: {0:hh\\:mm\\:ss}" , result . Duration ));
if ( parsedStats != null )
2018-02-07 10:35:16 -07:00
{
2018-03-12 14:07:11 +01:00
if ( parsedStats . KnownFileCount > 0 )
{
output . MessageEvent ( string . Format ( " Remote files: {0}" , parsedStats . KnownFileCount ));
output . MessageEvent ( string . Format ( " Remote size: {0}" , Library . Utility . Utility . FormatSizeString ( parsedStats . KnownFileSize )));
}
if ( parsedStats . TotalQuotaSpace >= 0 )
{
output . MessageEvent ( string . Format ( " Total remote quota: {0}" , Library . Utility . Utility . FormatSizeString ( parsedStats . TotalQuotaSpace )));
}
if ( parsedStats . FreeQuotaSpace >= 0 )
{
output . MessageEvent ( string . Format ( " Available remote quota: {0}" , Library . Utility . Utility . FormatSizeString ( parsedStats . FreeQuotaSpace )));
}
2013-08-21 22:23:58 +02:00
}
2017-09-22 00:30:10 -06:00
2018-03-12 14:07:11 +01:00
output . MessageEvent ( string . Format ( " Files added: {0}" , result . AddedFiles ));
output . MessageEvent ( string . Format ( " Files deleted: {0}" , result . DeletedFiles ));
output . MessageEvent ( string . Format ( " Files changed: {0}" , result . ModifiedFiles ));
2013-05-02 17:24:38 +02:00
2018-03-12 14:07:11 +01:00
output . MessageEvent ( string . Format ( " Data uploaded: {0}" , Library . Utility . Utility . FormatSizeString ( result . BackendStatistics . BytesUploaded )));
output . MessageEvent ( string . Format ( " Data downloaded: {0}" , Library . Utility . Utility . FormatSizeString ( result . BackendStatistics . BytesDownloaded )));
2014-07-15 14:15:26 +02:00
2018-03-12 14:07:11 +01:00
if ( result . ExaminedFiles == 0 && ( filter != null || ! filter . Empty ))
output . MessageEvent ( "No files were processed. If this was not intentional you may want to use the \"test-filters\" command" );
2013-05-02 17:24:38 +02:00
2018-03-12 14:07:11 +01:00
output . MessageEvent ( "Backup completed successfully!" );
2013-05-02 17:24:38 +02:00
2018-03-12 14:07:11 +01:00
//Interrupted = 50
if ( result . PartialBackup )
return 50 ;
//Completed with errors = 3
if ( result . ParsedResult == Library . Interface . ParsedResultType . Error )
return 3 ;
//Completed with warnings = 2
if ( result . ParsedResult == Library . Interface . ParsedResultType . Warning )
return 2 ;
//Success, but no upload = 1
if ( result . BackendStatistics . BytesUploaded == 0 )
return 1 ;
return 0 ;
}
2013-05-02 17:24:38 +02:00
}
2013-05-13 22:32:05 +02:00
2017-04-04 16:56:34 +02:00
public static int Compact ( TextWriter outwriter , Action < Duplicati . Library . Main . Controller > setup , List < string > args , Dictionary < string , string > options , Library . Utility . IFilter filter )
2013-05-15 21:50:16 +02:00
{
if ( args . Count != 1 )
2017-04-04 16:56:34 +02:00
return PrintWrongNumberOfArguments ( outwriter , args , 1 );
2018-03-12 14:07:11 +01:00
using ( var console = new ConsoleOutput ( outwriter , options ))
using ( var i = new Library . Main . Controller ( args [ 0 ], options , console ))
2017-04-04 16:56:34 +02:00
{
setup ( i );
2013-05-21 21:16:01 +02:00
i . Compact ();
2017-04-04 16:56:34 +02:00
}
2013-05-15 21:50:16 +02:00
return 0 ;
}
2013-06-26 21:59:01 +02:00
2017-04-04 16:56:34 +02:00
public static int Test ( TextWriter outwriter , Action < Duplicati . Library . Main . Controller > setup , List < string > args , Dictionary < string , string > options , Library . Utility . IFilter filter )
2013-06-26 21:59:01 +02:00
{
2018-03-12 14:07:11 +01:00
var fullResults = Duplicati . Library . Utility . Utility . ParseBoolOption ( options , "full-result" );
2013-06-26 21:59:01 +02:00
if ( args . Count != 1 && args . Count != 2 )
2017-04-04 16:56:34 +02:00
return PrintWrongNumberOfArguments ( outwriter , args , 1 );
2013-06-26 21:59:01 +02:00
2013-07-26 12:05:23 +02:00
var tests = 1L ;
if ( args . Count == 2 )
{
2017-09-18 23:23:45 -06:00
if ( new string [] { "all" , "everything" }. Contains ( args [ 1 ], StringComparer . OrdinalIgnoreCase ))
2013-07-26 12:05:23 +02:00
tests = long . MaxValue ;
else
tests = Convert . ToInt64 ( args [ 1 ]);
}
2013-06-26 21:59:01 +02:00
Library . Interface . ITestResults result ;
2018-03-12 14:07:11 +01:00
using ( var console = new ConsoleOutput ( outwriter , options ))
using ( var i = new Library . Main . Controller ( args [ 0 ], options , console ))
2017-04-04 16:56:34 +02:00
{
setup ( i );
2013-07-26 12:05:23 +02:00
result = i . Test ( tests );
2017-04-04 16:56:34 +02:00
}
2013-06-26 21:59:01 +02:00
2016-12-02 11:52:40 +01:00
var totalFiles = result . Verifications . Count ();
2013-06-26 21:59:01 +02:00
if ( totalFiles == 0 )
{
2017-04-04 16:56:34 +02:00
outwriter . WriteLine ( "No files examined, is the remote destination is empty?" );
2017-03-07 23:09:04 +01:00
return 100 ;
2013-06-26 21:59:01 +02:00
}
else
{
2016-12-02 11:52:40 +01:00
var filtered = from n in result . Verifications where n . Value . Count () != 0 select n ;
2013-06-26 21:59:01 +02:00
if ( filtered . Count () == 0 )
2017-03-07 23:09:04 +01:00
{
2017-04-04 16:56:34 +02:00
outwriter . WriteLine ( "Examined {0} files and found no errors" , totalFiles );
2017-03-07 23:09:04 +01:00
return 0 ;
}
2013-06-26 21:59:01 +02:00
else
{
2017-04-04 17:26:26 +02:00
foreach ( var n in result . Verifications )
2013-06-26 21:59:01 +02:00
{
2017-04-04 17:26:26 +02:00
var changecount = n . Value . Count ();
if ( changecount == 0 )
2013-06-26 21:59:01 +02:00
{
2018-03-12 14:07:11 +01:00
if ( fullResults )
2013-06-26 21:59:01 +02:00
Console . WriteLine ( "{0}: No errors" , n . Key );
2017-04-04 17:26:26 +02:00
}
else
{
Console . WriteLine ( "{0}: {1} errors" , n . Key , changecount );
var count = 0 ;
foreach ( var c in n . Value )
2013-06-26 21:59:01 +02:00
{
2017-04-04 17:26:26 +02:00
count ++;
Console . WriteLine ( "\t{0}: {1}" , c . Key , c . Value );
2018-03-12 14:07:11 +01:00
if (! fullResults && count == 10 && changecount > 10 )
2017-04-04 17:26:26 +02:00
{
Console . WriteLine ( "\t... and {0} more" , changecount - count );
break ;
}
2013-06-26 21:59:01 +02:00
}
2017-04-04 17:26:26 +02:00
Console . WriteLine ();
2013-06-26 21:59:01 +02:00
}
}
2017-03-07 23:09:04 +01:00
return 3 ;
}
2013-06-26 21:59:01 +02:00
}
}
2013-05-15 21:50:16 +02:00
2017-04-04 16:56:34 +02:00
private static int PrintWrongNumberOfArguments ( TextWriter outwriter , List < string > args , int expected )
2013-05-02 17:24:38 +02:00
{
2017-04-04 16:56:34 +02:00
outwriter . WriteLine ( Strings . Program . WrongNumberOfCommandsError_v2 ( args . Count , expected , args . Select ( n => "\"" + n + "\"" ). ToArray ()));
2013-05-02 17:24:38 +02:00
return 200 ;
}
2017-04-04 16:56:34 +02:00
public static int PrintInvalidCommand ( TextWriter outwriter , string command , List < string > args )
2013-05-02 17:24:38 +02:00
{
2017-04-04 16:56:34 +02:00
outwriter . WriteLine ( Strings . Program . InvalidCommandError ( command ));
2013-05-02 17:24:38 +02:00
return 200 ;
}
2013-05-04 14:07:04 +02:00
2017-04-04 16:56:34 +02:00
public static int CreateBugReport ( TextWriter outwriter , Action < Duplicati . Library . Main . Controller > setup , List < string > args , Dictionary < string , string > options , Library . Utility . IFilter filter )
2013-05-04 14:07:04 +02:00
{
2013-09-06 23:44:39 +02:00
// Support for not adding the --auth-username if possible
2013-09-25 23:29:35 +02:00
string dbpath = null ;
2013-09-06 23:44:39 +02:00
options . TryGetValue ( "dbpath" , out dbpath );
if ( string . IsNullOrEmpty ( dbpath ))
{
2013-09-25 23:29:35 +02:00
if ( args . Count > 0 )
dbpath = Library . Main . DatabaseLocator . GetDatabasePath ( args [ 0 ], new Duplicati . Library . Main . Options ( options ), false , true );
2013-09-06 23:44:39 +02:00
if ( dbpath == null )
{
2017-04-04 16:56:34 +02:00
outwriter . WriteLine ( "No local database found, please add --{0}" , "dbpath" );
2013-09-06 23:44:39 +02:00
return 100 ;
}
else
options [ "dbpath" ] = dbpath ;
2013-09-25 23:29:35 +02:00
2013-09-06 23:44:39 +02:00
}
2013-09-25 23:29:35 +02:00
if ( args . Count == 0 )
args = new List < string >( new string [] { "file://unused" , "report" });
else if ( args . Count == 1 )
args . Add ( "report" );
2017-04-04 16:56:34 +02:00
2018-03-12 14:07:11 +01:00
using ( var console = new ConsoleOutput ( outwriter , options ))
using ( var i = new Library . Main . Controller ( args [ 0 ], options , console ))
2017-04-04 16:56:34 +02:00
{
setup ( i );
2013-05-25 16:40:15 +02:00
i . CreateLogDatabase ( args [ 1 ]);
2017-04-04 16:56:34 +02:00
}
2013-05-08 21:29:59 +02:00
2017-04-04 16:56:34 +02:00
outwriter . WriteLine ( "Completed!" );
outwriter . WriteLine ();
outwriter . WriteLine ( "Please examine the log table of the database to see that no filenames are accidentially left over." );
outwriter . WriteLine ( "If you are concerned that your filenames may contain sensitive information," );
outwriter . WriteLine ( " do not attach the database to an issue!!!" );
outwriter . WriteLine ();
2013-08-24 14:02:12 +02:00
2013-05-04 14:07:04 +02:00
return 0 ;
}
2013-06-20 20:17:10 +02:00
2017-04-04 16:56:34 +02:00
public static int ListChanges ( TextWriter outwriter , Action < Duplicati . Library . Main . Controller > setup , List < string > args , Dictionary < string , string > options , Library . Utility . IFilter filter )
2013-06-20 20:17:10 +02:00
{
2018-03-12 14:07:11 +01:00
var fullresult = Duplicati . Library . Utility . Utility . ParseBoolOption ( options , "full-result" );
2017-04-04 23:51:40 +02:00
2013-06-20 20:17:10 +02:00
if ( args . Count < 1 )
2017-04-04 16:56:34 +02:00
return PrintWrongNumberOfArguments ( outwriter , args , 1 );
2013-06-20 20:17:10 +02:00
2013-09-13 13:24:11 +02:00
// Support for not adding the --auth-username if possible
string dbpath ;
options . TryGetValue ( "dbpath" , out dbpath );
if ( string . IsNullOrEmpty ( dbpath ))
{
dbpath = Library . Main . DatabaseLocator . GetDatabasePath ( args [ 0 ], new Duplicati . Library . Main . Options ( options ), false , true );
if ( dbpath != null )
options [ "dbpath" ] = dbpath ;
}
// Don't ask for passphrase if we have a local db
if (! string . IsNullOrEmpty ( dbpath ) && System . IO . File . Exists ( dbpath ) && ! options . ContainsKey ( "no-encryption" ) && ! Duplicati . Library . Utility . Utility . ParseBoolOption ( options , "no-local-db" ))
{
string passphrase ;
options . TryGetValue ( "passphrase" , out passphrase );
if ( string . IsNullOrEmpty ( passphrase ))
options [ "no-encryption" ] = "true" ;
}
2017-04-04 23:53:36 +02:00
Action < Duplicati . Library . Interface . IListChangesResults , IEnumerable < Tuple < Library . Interface . ListChangesChangeType , Library . Interface . ListChangesElementType , string >>> handler =
( result , items ) =>
{
outwriter . WriteLine ( "Listing changes" );
outwriter . WriteLine ( " {0}: {1}" , result . BaseVersionIndex , result . BaseVersionTimestamp );
outwriter . WriteLine ( " {0}: {1}" , result . CompareVersionIndex , result . CompareVersionTimestamp );
outwriter . WriteLine ();
outwriter . WriteLine ( "Size of backup {0}: {1}" , result . BaseVersionIndex , Library . Utility . Utility . FormatSizeString ( result . PreviousSize ));
if ( items != null )
{
outwriter . WriteLine ();
var added = result . ChangeDetails . Where ( x => x . Item1 == Library . Interface . ListChangesChangeType . Added );
var deleted = result . ChangeDetails . Where ( x => x . Item1 == Library . Interface . ListChangesChangeType . Deleted );
var modified = result . ChangeDetails . Where ( x => x . Item1 == Library . Interface . ListChangesChangeType . Modified );
var count = added . Count ();
if ( count > 0 )
{
var c = 0 ;
outwriter . WriteLine ( " {0} added entries:" , count );
foreach ( var n in added )
{
c ++;
outwriter . WriteLine ( " + {0}" , n . Item3 );
if ( c > 10 && count > 10 && ! fullresult )
{
outwriter . WriteLine ( " ... and {0} more" , count - c );
break ;
}
}
outwriter . WriteLine ();
}
count = modified . Count ();
if ( count > 0 )
{
var c = 0 ;
outwriter . WriteLine ( " {0} modified entries:" , count );
foreach ( var n in modified )
{
c ++;
outwriter . WriteLine ( " ~ {0}" , n . Item3 );
if ( c > 10 && count > 10 && ! fullresult )
{
outwriter . WriteLine ( " ... and {0} more" , count - c );
break ;
}
}
outwriter . WriteLine ();
}
count = deleted . Count ();
if ( count > 0 )
{
var c = 0 ;
outwriter . WriteLine ( "{0} deleted entries:" , count );
foreach ( var n in deleted )
{
c ++;
outwriter . WriteLine ( " - {0}" , n . Item3 );
if ( c > 10 && count > 10 && ! fullresult )
{
outwriter . WriteLine ( " ... and {0} more" , count - c );
break ;
}
}
outwriter . WriteLine ();
}
outwriter . WriteLine ();
}
if ( result . AddedFolders > 0 )
outwriter . WriteLine ( " Added folders: {0}" , result . AddedFolders );
if ( result . AddedSymlinks > 0 )
outwriter . WriteLine ( " Added symlinks: {0}" , result . AddedSymlinks );
if ( result . AddedFiles > 0 )
outwriter . WriteLine ( " Added files: {0}" , result . AddedFiles );
if ( result . DeletedFolders > 0 )
outwriter . WriteLine ( " Deleted folders: {0}" , result . DeletedFolders );
if ( result . DeletedSymlinks > 0 )
outwriter . WriteLine ( " Deleted symlinks: {0}" , result . DeletedSymlinks );
if ( result . DeletedFiles > 0 )
outwriter . WriteLine ( " Deleted files: {0}" , result . DeletedFiles );
if ( result . ModifiedFolders > 0 )
outwriter . WriteLine ( " Modified folders: {0}" , result . ModifiedFolders );
if ( result . ModifiedSymlinks > 0 )
outwriter . WriteLine ( " Modified symlinka: {0}" , result . ModifiedSymlinks );
if ( result . ModifiedFiles > 0 )
outwriter . WriteLine ( " Modified files: {0}" , result . ModifiedFiles );
if ( result . AddedFolders + result . AddedSymlinks + result . AddedFolders +
result . ModifiedFolders + result . ModifiedSymlinks + result . ModifiedFiles +
result . DeletedFolders + result . DeletedSymlinks + result . DeletedFiles == 0 )
outwriter . WriteLine ( " No changes found" );
outwriter . WriteLine ( "Size of backup {0}: {1}" , result . CompareVersionIndex , Library . Utility . Utility . FormatSizeString ( result . CurrentSize ));
};
2013-09-13 13:24:11 +02:00
2018-03-12 14:07:11 +01:00
using ( var console = new ConsoleOutput ( outwriter , options ))
using ( var i = new Library . Main . Controller ( args [ 0 ], options , console ))
2017-04-04 16:56:34 +02:00
{
setup ( i );
2013-06-20 20:17:10 +02:00
if ( args . Count == 2 )
2017-04-04 23:53:36 +02:00
i . ListChanges ( null , args [ 1 ], null , filter , handler );
2017-04-04 16:56:34 +02:00
else
2017-04-04 23:53:36 +02:00
i . ListChanges ( args . Count > 1 ? args [ 1 ] : null , args . Count > 2 ? args [ 2 ] : null , null , filter , handler );
2017-04-04 16:56:34 +02:00
}
2017-04-04 23:53:36 +02:00
2013-06-20 20:17:10 +02:00
return 0 ;
}
2013-12-07 15:22:51 +01:00
2017-04-04 16:56:34 +02:00
public static int TestFilters ( TextWriter outwriter , Action < Duplicati . Library . Main . Controller > setup , List < string > args , Dictionary < string , string > options , Library . Utility . IFilter filter )
2013-12-07 15:22:51 +01:00
{
if ( args == null || args . Count < 1 )
{
2017-04-04 16:56:34 +02:00
outwriter . WriteLine ( "No source paths given" );
2013-12-07 15:22:51 +01:00
return 200 ;
}
2018-03-12 14:07:11 +01:00
using ( var console = new ConsoleOutput ( outwriter , options ))
using ( var i = new Library . Main . Controller ( "dummy://" , options , console ))
2013-12-07 15:22:51 +01:00
{
2017-04-04 16:56:34 +02:00
setup ( i );
2013-12-07 15:22:51 +01:00
var result = i . TestFilter ( args . ToArray (), filter );
2017-04-04 16:56:34 +02:00
outwriter . WriteLine ( "Matched {0} files ({1})" , result . FileCount , Duplicati . Library . Utility . Utility . FormatSizeString ( result . FileSize ));
2013-12-07 15:22:51 +01:00
}
return 0 ;
}
2015-09-11 10:57:31 +02:00
2017-04-04 16:56:34 +02:00
public static int SystemInfo ( TextWriter outwriter , Action < Duplicati . Library . Main . Controller > setup , List < string > args , Dictionary < string , string > options , Library . Utility . IFilter filter )
2015-09-11 10:57:31 +02:00
{
if ( args != null && args . Count != 0 )
{
2017-04-04 16:56:34 +02:00
outwriter . WriteLine ( "Command takes no arguments" );
2015-09-11 10:57:31 +02:00
return 200 ;
}
2018-03-12 14:07:11 +01:00
using ( var console = new ConsoleOutput ( outwriter , options ))
using ( var i = new Library . Main . Controller ( "dummy://" , options , console ))
2017-04-04 16:56:34 +02:00
{
setup ( i );
foreach ( var line in i . SystemInfo (). Lines )
outwriter . WriteLine ( line );
}
2015-09-11 10:57:31 +02:00
2017-04-04 16:56:34 +02:00
outwriter . WriteLine ( "Know locales: {0}" , string . Join ( ", " , Library . Localization . LocalizationService . AllLocales ));
outwriter . WriteLine ( "Translated locales: {0}" , string . Join ( ", " , Library . Localization . LocalizationService . SupportedCultures ));
2016-09-27 20:44:19 +02:00
2015-09-11 10:57:31 +02:00
return 0 ;
}
2016-12-29 22:41:13 +01:00
2017-04-04 16:56:34 +02:00
public static int PurgeFiles ( TextWriter outwriter , Action < Duplicati . Library . Main . Controller > setup , List < string > args , Dictionary < string , string > options , Library . Utility . IFilter filter )
2016-12-29 22:41:13 +01:00
{
if ( args . Count < 1 )
2017-04-04 16:56:34 +02:00
return PrintWrongNumberOfArguments ( outwriter , args , 1 );
2016-12-29 22:41:13 +01:00
var backend = args [ 0 ];
var paths = args . Skip ( 1 ). ToArray ();
if ( paths . Length > 0 )
{
if ( filter == null || filter . Empty )
filter = new Library . Utility . FilterExpression ( paths );
else
{
2017-04-04 16:56:34 +02:00
outwriter . WriteLine ( "You cannot combine filters and paths on the commandline" );
2016-12-29 22:41:13 +01:00
return 200 ;
}
}
else if ( filter == null || filter . Empty )
{
2017-04-04 16:56:34 +02:00
outwriter . WriteLine ( "You must provide either filename filters, or a list of paths to remove" );
2016-12-29 22:41:13 +01:00
return 200 ;
}
2018-03-12 14:07:11 +01:00
using ( var console = new ConsoleOutput ( outwriter , options ))
using ( var i = new Library . Main . Controller ( args [ 0 ], options , console ))
2017-04-04 16:56:34 +02:00
{
setup ( i );
2016-12-29 22:41:13 +01:00
i . PurgeFiles ( filter );
2017-04-04 16:56:34 +02:00
}
2016-12-29 22:41:13 +01:00
return 0 ;
}
2017-04-04 16:56:34 +02:00
public static int ListBrokenFiles ( TextWriter outwriter , Action < Duplicati . Library . Main . Controller > setup , List < string > args , Dictionary < string , string > options , Library . Utility . IFilter filter )
2017-01-05 16:46:01 +01:00
{
if ( args . Count != 1 )
2017-04-04 16:56:34 +02:00
return PrintWrongNumberOfArguments ( outwriter , args , 1 );
2017-01-05 16:46:01 +01:00
var previd = - 1L ;
var outputcount = 0L ;
2018-03-12 14:07:11 +01:00
var fullresult = Duplicati . Library . Utility . Utility . ParseBoolOption ( options , "full-result" );
2017-01-05 16:46:01 +01:00
2018-03-12 14:07:11 +01:00
using ( var con = new ConsoleOutput ( outwriter , options ))
2017-01-05 16:46:01 +01:00
using ( var i = new Library . Main . Controller ( args [ 0 ], options , con ))
2017-04-04 16:56:34 +02:00
{
setup ( i );
i . ListBrokenFiles ( filter , ( id , time , count , path , size ) =>
2017-01-05 16:46:01 +01:00
{
if ( previd != id )
{
previd = id ;
outputcount = 0 ;
con . MessageEvent ( string . Format ( "{0}\t: {1}\t({2} match(es))" , id , time . ToLocalTime (), count ));
}
con . MessageEvent ( string . Format ( "\t{0} ({1})" , path , Library . Utility . Utility . FormatSizeString ( size )));
outputcount ++;
2018-03-12 14:07:11 +01:00
if ( outputcount >= 5 && ! fullresult && count != outputcount )
2017-01-05 16:46:01 +01:00
{
2017-04-04 23:51:40 +02:00
con . MessageEvent ( string . Format ( "\t ... and {0} more, (use --{1} to list all)" , count - outputcount , "full-result" ));
2017-01-05 16:46:01 +01:00
return false ;
}
return true ;
});
2017-04-04 16:56:34 +02:00
}
2017-01-05 16:46:01 +01:00
return 0 ;
}
2017-04-04 16:56:34 +02:00
public static int PurgeBrokenFiles ( TextWriter outwriter , Action < Duplicati . Library . Main . Controller > setup , List < string > args , Dictionary < string , string > options , Library . Utility . IFilter filter )
2017-01-05 16:46:01 +01:00
{
if ( args . Count != 1 )
2017-04-04 16:56:34 +02:00
return PrintWrongNumberOfArguments ( outwriter , args , 1 );
2017-01-05 16:46:01 +01:00
2018-03-12 14:07:11 +01:00
using ( var console = new ConsoleOutput ( outwriter , options ))
using ( var i = new Library . Main . Controller ( args [ 0 ], options , console ))
2017-01-05 16:46:01 +01:00
{
2017-04-04 16:56:34 +02:00
setup ( i );
2017-01-05 16:46:01 +01:00
var res = i . PurgeBrokenFiles ( filter );
}
return 0 ;
}
2017-04-04 16:56:34 +02:00
public static int SendMail ( TextWriter outwriter , Action < Duplicati . Library . Main . Controller > setup , List < string > args , Dictionary < string , string > options , Library . Utility . IFilter filter )
2017-01-09 23:21:00 +01:00
{
if ( args != null && args . Count != 0 )
{
2017-04-04 16:56:34 +02:00
outwriter . WriteLine ( "Command takes no arguments" );
2017-01-09 23:21:00 +01:00
return 200 ;
}
2018-03-12 14:07:11 +01:00
using ( var console = new ConsoleOutput ( outwriter , options ))
using ( var i = new Library . Main . Controller ( "dummy://" , options , console ))
2017-01-09 23:21:00 +01:00
{
2017-04-04 16:56:34 +02:00
setup ( i );
2017-01-09 23:21:00 +01:00
foreach ( var l in i . SendMail (). Lines )
2017-04-04 16:56:34 +02:00
outwriter . WriteLine ( l );
2017-01-09 23:21:00 +01:00
}
return 0 ;
}
2017-08-04 11:24:18 +01:00
public static int Vacuum (
TextWriter outwriter ,
Action < Duplicati . Library . Main . Controller > setup ,
List < string > args , Dictionary < string , string > options ,
Library . Utility . IFilter filter )
{
if ( args . Count != 1 )
return PrintWrongNumberOfArguments ( outwriter , args , 1 );
2018-03-12 14:07:11 +01:00
using ( var console = new ConsoleOutput ( outwriter , options ))
using ( var controller = new Library . Main . Controller ( args [ 0 ], options , console ))
2017-08-04 11:24:18 +01:00
{
setup ( controller );
controller . Vacuum ();
}
return 0 ;
}
2013-05-02 17:24:38 +02:00
}
}