2013-05-02 17:24:38 +02:00
// Copyright (C) 2011, Kenneth Skovhede
// http://www.hexad.dk, opensource@hexad.dk
//
// 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 ;
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 ;
}
}
}
}
2013-05-13 22:32:05 +02:00
public static int Help ( List < string > args , Dictionary < string , string > options , Library . Utility . IFilter filter )
2013-05-02 17:24:38 +02:00
{
if ( args . Count < 1 )
Duplicati . CommandLine . Help . PrintUsage ( "help" , options );
else
Duplicati . CommandLine . Help . PrintUsage ( args [ 0 ], options );
return 0 ;
}
2013-05-30 21:54:43 +02:00
public static int List ( List < string > args , Dictionary < string , string > options , Library . Utility . IFilter filter )
2013-05-02 17:24:38 +02:00
{
2013-08-18 23:16:58 +02:00
using ( var i = new Library . Main . Controller ( args [ 0 ], options , new ConsoleOutput ( options )))
2013-05-30 21:54:43 +02:00
{
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
}
2013-08-06 23:17:08 +02:00
else if ( args [ 0 ]. IndexOfAny ( new char [] { '*' , '?' }) < 0 && ! args [ 0 ]. StartsWith ( "[" ))
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 ++)
if ( args [ ix ]. IndexOfAny ( new char [] { '*' , '?' , System . IO . Path . DirectorySeparatorChar , System . IO . Path . AltDirectorySeparatorChar }) < 0 && ! args [ ix ]. StartsWith ( "[" ))
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 ))
options [ "no-encryption" ] = "true" ;
}
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 );
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
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 ) &&
2013-08-06 23:17:08 +02:00
new Library . Utility . FilterExpression ( args ). Type != Duplicati . Library . Utility . FilterType . Empty ;
if ( isRequestForFiles && ! Library . Utility . Utility . ParseBoolOption ( options , "all-versions" ))
2013-06-30 12:50:30 +02:00
{
Console . WriteLine ( "No files matching, looking in all versions" );
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 );
}
2013-08-06 23:17:08 +02:00
if ( res . Filesets . Count () != 0 && ( res . Files == null || res . Files . Count () == 0 ) && new Library . Utility . FilterExpression ( args ). Type == Duplicati . Library . Utility . FilterType . Empty )
2013-05-30 21:54:43 +02:00
{
Console . WriteLine ( "Listing filesets:" );
foreach ( var e in res . Filesets )
{
if ( e . FileCount >= 0 )
Console . WriteLine ( "{0}\t: {1} ({2} files, {3})" , e . Version , e . Time , e . FileCount , Library . Utility . Utility . FormatSizeString ( e . FileSizes ));
else
Console . WriteLine ( "{0}\t: {1}" , e . Version , e . Time );
}
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 )
{
Console . WriteLine ( "No time or version matched a fileset" );
}
else if ( res . Files == null || res . Files . Count () == 0 )
2013-05-11 13:04:01 +02:00
{
2013-08-06 23:17:08 +02:00
Console . 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 ();
2013-05-30 21:54:43 +02:00
Console . WriteLine ( "Listing contents {0} ({1}):" , f . Version , f . Time );
2013-05-11 12:03:15 +02:00
foreach ( var e in res . Files )
2013-05-11 13:04:01 +02:00
Console . WriteLine ( "{0} {1}" , e . Path , e . Path . EndsWith ( System . IO . Path . DirectorySeparatorChar . ToString ()) ? "" : "(" + Library . Utility . Utility . FormatSizeString ( e . Sizes . First ()) + ")" );
2013-05-11 12:03:15 +02:00
}
else
{
Console . WriteLine ( "Listing files and versions:" );
foreach ( var e in res . Files )
{
Console . 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 } ))
2013-05-11 12:03:15 +02:00
Console . WriteLine ( "{0}\t: {1} {2}" , nx . Index , nx . Time , nx . Size < 0 ? " - " : Library . Utility . Utility . FormatSizeString ( nx . Size ));
Console . WriteLine ();
}
}
}
}
2013-05-02 17:24:38 +02:00
return 0 ;
}
2013-05-11 22:56:21 +02:00
2013-05-13 22:32:05 +02:00
public static int Delete ( List < string > args , Dictionary < string , string > options , Library . Utility . IFilter filter )
2013-05-11 22:56:21 +02:00
{
var requiredOptions = new string [] { "keep-time" , "keep-versions" , "version" };
if (! options . Keys . Where ( x => requiredOptions . Contains ( x , StringComparer . InvariantCultureIgnoreCase )). Any ())
{
Console . WriteLine ( string . Format ( Strings . Program . DeleteCommandNeedsOptions , "delete" , string . Join ( "," , requiredOptions )));
return 200 ;
}
2013-08-18 23:16:58 +02:00
using ( var i = new Library . Main . Controller ( args [ 0 ], options , new ConsoleOutput ( options )))
2013-05-11 22:56:21 +02:00
{
args . RemoveAt ( 0 );
var res = i . Delete ();
2013-05-08 21:29:59 +02:00
2013-05-11 22:56:21 +02:00
if ( res . DeletedSets . Count () == 0 )
{
Console . WriteLine ( Strings . Program . NoFilesetsMatching );
}
else
{
if ( res . Dryrun )
Console . WriteLine ( Strings . Program . WouldDeleteBackups );
else
Console . WriteLine ( Strings . Program . DeletedBackups );
foreach ( var f in res . DeletedSets )
2013-08-26 15:25:37 +02:00
Console . WriteLine ( string . Format ( "{0}: {1}" , f . Item1 , f . Item2 ));
2013-05-11 22:56:21 +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
}
2013-05-13 22:32:05 +02:00
public static int Repair ( List < string > args , Dictionary < string , string > options , Library . Utility . IFilter filter )
2013-05-02 17:24:38 +02:00
{
if ( args . Count != 1 )
return PrintWrongNumberOfArguments ( args , 1 );
2013-08-18 23:16:58 +02:00
using ( var i = new Duplicati . Library . Main . Controller ( args [ 0 ], options , new ConsoleOutput ( options )))
2013-05-08 21:29:59 +02:00
i . Repair ();
2013-05-02 17:24:38 +02:00
return 0 ;
}
2013-05-13 22:32:05 +02:00
public static int Restore ( 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 )
return PrintWrongNumberOfArguments ( args , 1 );
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 ++)
if ( args [ ix ]. IndexOfAny ( new char [] { '*' , '?' , System . IO . Path . DirectorySeparatorChar , System . IO . Path . AltDirectorySeparatorChar }) < 0 && ! args [ ix ]. StartsWith ( "[" ))
args [ ix ] = "*" + System . IO . Path . DirectorySeparatorChar . ToString () + args [ ix ];
2013-08-22 20:52:54 +02:00
var output = new ConsoleOutput ( options );
2013-08-23 22:18:13 +02:00
output . MessageEvent ( string . Format ( "Restore started at {0}" , DateTime . Now ));
2013-05-02 17:24:38 +02:00
2013-08-22 20:52:54 +02:00
using ( var i = new Library . Main . Controller ( backend , options , output ))
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:" );
2013-05-30 23:00:09 +02:00
foreach ( var s in res . Files )
Console . WriteLine ( s );
}
else
{
2013-08-23 22:18:13 +02:00
using ( var periodicOutput = new PeriodicOutput ( output , TimeSpan . FromSeconds ( 5 )))
{
output . PhaseChanged += ( phase , previousPhase ) => {
if ( phase == Duplicati . Library . Main . OperationPhase . Restore_PreRestoreVerify )
output . MessageEvent ( "Checking remote backup ..." );
else if ( phase == Duplicati . Library . Main . OperationPhase . Restore_ScanForExistingFiles )
output . MessageEvent ( "Checking existing target files ..." );
2013-08-22 20:52:54 +02:00
/*else if (phase == Duplicati.Library.Main.OperationPhase.Restore_DownloadingRemoteFiles)
output.MessageEvent("Downloading remote files ..."); */
else if ( phase == Duplicati . Library . Main . OperationPhase . Restore_PatchWithLocalBlocks )
2013-08-23 22:18:13 +02:00
output . MessageEvent ( "Updating target files with local data ..." );
else if ( phase == Duplicati . Library . Main . OperationPhase . Restore_PostRestoreVerify )
{
periodicOutput . SetFinished ();
periodicOutput . Join ( TimeSpan . FromMilliseconds ( 100 ));
output . MessageEvent ( "Verifying restored files ..." );
}
else if ( phase == Duplicati . Library . Main . OperationPhase . Restore_ScanForLocalBlocks )
output . MessageEvent ( "Scanning local files for needed data ..." );
else if ( phase == Duplicati . Library . Main . OperationPhase . Restore_CreateTargetFolders )
periodicOutput . SetReady ();
};
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
};
2013-08-22 20:52:54 +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 );
2013-08-22 20:52:54 +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 ));
2013-08-23 22:18:13 +02:00
if ( res . FilesRestored > 0 )
{
output . MessageEvent ( "Did we help save your files? If so, please support Duplicati with a donation." );
output . MessageEvent ( "" );
output . MessageEvent ( "Paypal: http://goo.gl/P4XJ6S" );
2013-08-28 17:18:29 +02:00
output . MessageEvent ( "Bitcoin: 1L74qa1n5SFKwwyHhECTHBJgcf6WT2rJKf" );
2013-08-23 22:18:13 +02:00
}
if ( output . VerboseOutput )
Library . Utility . Utility . PrintSerializeObject ( res , Console . Out );
}
2013-05-30 23:00:09 +02:00
}
2013-05-02 17:24:38 +02:00
return 0 ;
}
2013-05-13 22:32:05 +02:00
public static int Backup ( 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 )
2013-05-02 17:24:38 +02:00
return PrintWrongNumberOfArguments ( 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-08-18 23:16:58 +02:00
var output = new ConsoleOutput ( options );
2013-05-25 16:40:15 +02:00
Library . Interface . IBackupResults result ;
2013-08-23 22:18:13 +02:00
using ( var periodicOutput = new PeriodicOutput ( output , TimeSpan . FromSeconds ( 5 )))
2013-08-20 22:52:50 +02:00
{
2013-08-23 22:18:13 +02:00
output . MessageEvent ( string . Format ( "Backup started at {0}" , DateTime . Now ));
output . PhaseChanged += ( phase , previousPhase ) =>
2013-08-20 22:52:50 +02:00
{
2013-08-26 12:44:04 +02:00
if ( previousPhase == Duplicati . Library . Main . OperationPhase . Backup_PostBackupTest )
output . MessageEvent ( "Remote backup verification completed" );
2013-08-23 22:18:13 +02:00
if ( phase == Duplicati . Library . Main . OperationPhase . Backup_ProcessingFiles )
2013-09-10 20:41:28 +02:00
{
output . MessageEvent ( "Scanning local files ..." );
2013-08-23 22:18:13 +02:00
periodicOutput . SetReady ();
2013-09-10 20:41:28 +02:00
}
2013-08-23 22:18:13 +02:00
else if ( phase == Duplicati . Library . Main . OperationPhase . Backup_Finalize )
periodicOutput . SetFinished ();
2013-08-26 12:44:04 +02:00
else if ( phase == Duplicati . Library . Main . OperationPhase . Backup_PreBackupVerify )
output . MessageEvent ( "Checking remote backup ..." );
2013-08-23 22:18:13 +02:00
else if ( phase == Duplicati . Library . Main . OperationPhase . Backup_PostBackupVerify )
output . MessageEvent ( "Checking remote backup ..." );
2013-08-26 12:44:04 +02:00
else if ( phase == Duplicati . Library . Main . OperationPhase . Backup_PostBackupTest )
output . MessageEvent ( "Verifying remote backup ..." );
2013-08-23 22:18:13 +02:00
else if ( phase == Duplicati . Library . Main . OperationPhase . Backup_Compact )
output . MessageEvent ( "Compacting remote backup ..." );
};
periodicOutput . WriteOutput += ( progress , files , size , counting ) => {
2013-08-24 13:43:33 +02:00
output . MessageEvent ( string . Format ( " {0} files need to be examined ({1}){2}" , files , Library . Utility . Utility . FormatSizeString ( size ), counting ? " (still counting)" : "" ));
2013-08-23 22:18:13 +02:00
};
using ( var i = new Library . Main . Controller ( backend , options , output ))
2013-08-20 22:52:50 +02:00
result = i . Backup ( dirs , filter );
}
2013-08-21 22:23:58 +02:00
if ( output . VerboseOutput )
{
Library . Utility . Utility . PrintSerializeObject ( result , Console . Out );
}
else
{
var parsedStats = result . BackendStatistics as Duplicati . Library . Interface . IParsedBackendStatistics ;
2013-08-28 17:18:29 +02:00
output . MessageEvent ( string . Format ( " Duration of backup: {0:hh\\:mm\\:ss}" , result . Duration ));
2013-08-21 22:23:58 +02:00
if ( parsedStats != null && parsedStats . KnownFileCount > 0 )
{
2013-08-28 17:18:29 +02:00
output . MessageEvent ( string . Format ( " Remote files: {0}" , parsedStats . KnownFileCount ));
output . MessageEvent ( string . Format ( " Remote size: {0}" , Library . Utility . Utility . FormatSizeString ( parsedStats . KnownFileSize )));
2013-08-21 22:23:58 +02:00
}
2013-08-28 17:18:29 +02: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-08-21 22:23:58 +02:00
2013-08-28 17:18:29 +02: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 )));
2013-08-21 22:23:58 +02:00
}
2013-05-02 17:24:38 +02:00
2013-08-28 17:18:29 +02:00
output . MessageEvent ( "Backup completed successfully!" );
2013-05-02 17:24:38 +02:00
//Interrupted = 50
2013-05-13 22:32:05 +02:00
if ( result . PartialBackup )
2013-05-02 17:24:38 +02:00
return 50 ;
//Completed with warnings = 2
2013-05-25 16:40:15 +02:00
if ( result . Warnings . Count () > 0 || result . Errors . Count () > 0 )
2013-05-02 17:24:38 +02:00
return 2 ;
//Success, but no upload = 1
2013-05-25 16:40:15 +02:00
if ( result . BackendStatistics . BytesUploaded == 0 )
2013-05-13 22:32:05 +02:00
return 1 ;
2013-05-25 16:40:15 +02:00
2013-05-02 17:24:38 +02:00
return 0 ;
}
2013-05-13 22:32:05 +02:00
2013-05-15 21:50:16 +02:00
public static int Compact ( List < string > args , Dictionary < string , string > options , Library . Utility . IFilter filter )
{
if ( args . Count != 1 )
return PrintWrongNumberOfArguments ( args , 1 );
2013-08-18 23:16:58 +02:00
using ( var i = new Library . Main . Controller ( args [ 0 ], options , new ConsoleOutput ( options )))
2013-05-21 21:16:01 +02:00
i . Compact ();
2013-05-15 21:50:16 +02:00
return 0 ;
}
2013-06-26 21:59:01 +02:00
public static int Test ( List < string > args , Dictionary < string , string > options , Library . Utility . IFilter filter )
{
if ( args . Count != 1 && args . Count != 2 )
return PrintWrongNumberOfArguments ( args , 1 );
2013-07-26 12:05:23 +02:00
var tests = 1L ;
if ( args . Count == 2 )
{
if ( new string [] { "all" , "everything" }. Contains ( args [ 1 ], StringComparer . InvariantCultureIgnoreCase ))
tests = long . MaxValue ;
else
tests = Convert . ToInt64 ( args [ 1 ]);
}
2013-06-26 21:59:01 +02:00
Library . Interface . ITestResults result ;
2013-08-18 23:16:58 +02:00
using ( var i = new Library . Main . Controller ( args [ 0 ], options , new ConsoleOutput ( options )))
2013-07-26 12:05:23 +02:00
result = i . Test ( tests );
2013-06-26 21:59:01 +02:00
var totalFiles = result . Changes . Count ();
if ( totalFiles == 0 )
{
Console . WriteLine ( "No files examined, is the remote destination is empty?" );
}
else
{
var filtered = from n in result . Changes where n . Value . Count () != 0 select n ;
if ( filtered . Count () == 0 )
Console . WriteLine ( "Examined {0} files and found no errors" , totalFiles );
else
{
if ( Library . Utility . Utility . ParseBoolOption ( options , "verbose" ))
{
foreach ( var n in result . Changes )
{
var changecount = n . Value . Count ();
if ( changecount == 0 )
Console . WriteLine ( "{0}: No errors" , n . Key );
else
{
Console . WriteLine ( "{0}: {1} errors" , n . Key , changecount );
foreach ( var c in n . Value )
Console . WriteLine ( "{0}: {1}" , c . Key , c . Value );
Console . WriteLine ();
}
}
}
else
{
Console . WriteLine ( "Examined {0} files and found errors in the following files:" , totalFiles );
foreach ( var n in filtered )
Console . WriteLine ( n . Key );
Console . WriteLine ();
}
}
}
return 0 ;
}
2013-05-15 21:50:16 +02:00
2013-05-02 17:24:38 +02:00
private static int PrintWrongNumberOfArguments ( List < string > args , int expected )
{
Console . WriteLine ( Strings . Program . WrongNumberOfCommandsError_v2 , args . Count , expected , args . Count == 0 ? "" : "\"" + string . Join ( "\", \"" , args . ToArray ()) + "\"" );
return 200 ;
}
2013-06-26 21:50:09 +02:00
public static int PrintInvalidCommand ( string command , List < string > args )
2013-05-02 17:24:38 +02:00
{
2013-06-26 21:50:09 +02:00
Console . WriteLine ( Strings . Program . InvalidCommandError , command );
2013-05-02 17:24:38 +02:00
return 200 ;
}
2013-05-04 14:07:04 +02:00
2013-06-16 22:22:57 +02:00
public static int CreateBugReport ( 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 )
{
Console . WriteLine ( "No local database found, please add --{0}" , "dbpath" );
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" );
2013-08-18 23:16:58 +02:00
using ( var i = new Library . Main . Controller ( args [ 0 ], options , new ConsoleOutput ( options )))
2013-05-25 16:40:15 +02:00
i . CreateLogDatabase ( args [ 1 ]);
2013-05-08 21:29:59 +02:00
2013-08-24 14:02:12 +02:00
Console . WriteLine ( "Completed!" );
Console . WriteLine ();
Console . WriteLine ( "Please examine the log table of the database to see that no filenames are accidentially left over." );
Console . WriteLine ( "If you are concerned that your filenames may contain sensitive information," );
Console . WriteLine ( " do not attach the database to an issue!!!" );
Console . WriteLine ();
2013-05-04 14:07:04 +02:00
return 0 ;
}
2013-06-20 20:17:10 +02:00
public static int ListChanges ( List < string > args , Dictionary < string , string > options , Library . Utility . IFilter filter )
{
if ( args . Count < 1 )
return PrintWrongNumberOfArguments ( args , 1 );
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" ;
}
2013-06-20 20:17:10 +02:00
Library . Interface . IListChangesResults result ;
2013-08-18 23:16:58 +02:00
using ( var i = new Library . Main . Controller ( args [ 0 ], options , new ConsoleOutput ( options )))
2013-06-20 20:17:10 +02:00
if ( args . Count == 2 )
result = i . ListChanges ( null , args [ 1 ], null , filter );
else
result = i . ListChanges ( args . Count > 1 ? args [ 1 ] : null , args . Count > 2 ? args [ 2 ] : null , null , filter );
2013-08-26 12:44:04 +02:00
Console . WriteLine ( "Listing changes" );
Console . WriteLine ( " {0}: {1}" , result . BaseVersionIndex , result . BaseVersionTimestamp );
Console . WriteLine ( " {0}: {1}" , result . CompareVersionIndex , result . CompareVersionTimestamp );
2013-06-20 20:17:10 +02:00
Console . WriteLine ();
2013-08-28 17:18:29 +02:00
Console . WriteLine ( "Size of backup {0}: {1}" , result . BaseVersionIndex , Library . Utility . Utility . FormatSizeString ( result . PreviousSize ));
2013-06-20 20:17:10 +02:00
if ( result . ChangeDetails != null )
{
2013-08-28 17:18:29 +02:00
Console . WriteLine ();
2013-06-20 20:17:10 +02:00
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 )
{
2013-08-28 17:18:29 +02:00
Console . WriteLine ( " {0} added entries:" , count );
2013-06-20 20:17:10 +02:00
foreach ( var n in added )
2013-08-28 17:18:29 +02:00
Console . WriteLine ( " + {0}" , n . Item3 );
2013-06-20 20:17:10 +02:00
Console . WriteLine ();
}
count = modified . Count ();
if ( count > 0 )
{
2013-08-28 17:18:29 +02:00
Console . WriteLine ( " {0} modified entries:" , count );
2013-06-20 20:17:10 +02:00
foreach ( var n in modified )
2013-08-28 17:18:29 +02:00
Console . WriteLine ( " ~ {0}" , n . Item3 );
2013-06-20 20:17:10 +02:00
Console . WriteLine ();
}
count = deleted . Count ();
if ( count > 0 )
{
Console . WriteLine ( "{0} deleted entries:" , count );
foreach ( var n in deleted )
2013-08-28 17:18:29 +02:00
Console . WriteLine ( " - {0}" , n . Item3 );
2013-06-20 20:17:10 +02:00
Console . WriteLine ();
}
2013-08-28 17:18:29 +02:00
Console . WriteLine ();
2013-06-20 20:17:10 +02:00
}
else
{
if ( result . AddedFolders > 0 )
2013-08-28 17:18:29 +02:00
Console . WriteLine ( " Added folders: {0}" , result . AddedFolders );
2013-06-20 20:17:10 +02:00
if ( result . AddedSymlinks > 0 )
2013-08-28 17:18:29 +02:00
Console . WriteLine ( " Added symlinks: {0}" , result . AddedSymlinks );
2013-06-20 20:17:10 +02:00
if ( result . AddedFiles > 0 )
2013-08-28 17:18:29 +02:00
Console . WriteLine ( " Added files: {0}" , result . AddedFiles );
2013-06-20 20:17:10 +02:00
if ( result . DeletedFolders > 0 )
2013-08-28 17:18:29 +02:00
Console . WriteLine ( " Deleted folders: {0}" , result . DeletedFolders );
2013-06-20 20:17:10 +02:00
if ( result . DeletedSymlinks > 0 )
2013-08-28 17:18:29 +02:00
Console . WriteLine ( " Deleted symlinks: {0}" , result . DeletedSymlinks );
2013-06-20 20:17:10 +02:00
if ( result . DeletedFiles > 0 )
2013-08-28 17:18:29 +02:00
Console . WriteLine ( " Deleted files: {0}" , result . DeletedFiles );
2013-06-20 20:17:10 +02:00
if ( result . ModifiedFolders > 0 )
2013-08-28 17:18:29 +02:00
Console . WriteLine ( " Modified folders: {0}" , result . ModifiedFolders );
2013-06-20 20:17:10 +02:00
if ( result . ModifiedSymlinks > 0 )
2013-08-28 17:18:29 +02:00
Console . WriteLine ( " Modified symlinka: {0}" , result . ModifiedSymlinks );
2013-06-20 20:17:10 +02:00
if ( result . ModifiedFiles > 0 )
2013-08-28 17:18:29 +02:00
Console . WriteLine ( " Modified files: {0}" , result . ModifiedFiles );
2013-06-20 20:17:10 +02:00
if ( result . AddedFolders + result . AddedSymlinks + result . AddedFolders +
result . ModifiedFolders + result . ModifiedSymlinks + result . ModifiedFiles +
result . DeletedFolders + result . DeletedSymlinks + result . DeletedFiles == 0 )
2013-08-28 17:18:29 +02:00
Console . WriteLine ( " No changes found" );
2013-06-20 20:17:10 +02:00
}
2013-08-26 12:44:04 +02:00
Console . WriteLine ( "Size of backup {0}: {1}" , result . CompareVersionIndex , Library . Utility . Utility . FormatSizeString ( result . CurrentSize ));
2013-06-20 20:17:10 +02:00
return 0 ;
}
2013-12-07 15:22:51 +01:00
public static int TestFilters ( List < string > args , Dictionary < string , string > options , Library . Utility . IFilter filter )
{
if ( args == null || args . Count < 1 )
{
Console . WriteLine ( "No source paths given" );
return 200 ;
}
options [ "verbose" ] = "true" ;
using ( var i = new Library . Main . Controller ( "dummy://" , options , new ConsoleOutput ( options )))
{
var result = i . TestFilter ( args . ToArray (), filter );
Console . WriteLine ( "Matched {0} files ({1})" , result . FileCount , Duplicati . Library . Utility . Utility . FormatSizeString ( result . FileSize ));
}
return 0 ;
}
2013-05-02 17:24:38 +02:00
}
}