2013-03-31 19:33:34 +02:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Text ;
2017-01-09 11:35:38 +01:00
using Duplicati.Library.Interface ;
2013-03-31 19:33:34 +02:00
2013-05-08 20:17:07 +02:00
namespace Duplicati.Library.Main.Operation
2013-05-11 22:56:21 +02:00
{
2013-05-25 16:40:15 +02:00
internal class ListFilesHandler
2013-05-11 12:03:15 +02:00
{
2018-03-12 14:07:11 +01:00
/// <summary>
/// The tag used for logging
/// </summary>
private static readonly string LOGTAG = Logging . Log . LogTagFromType < ListFilesHandler >();
2013-03-31 19:33:34 +02:00
private string m_backendurl ;
2013-05-06 09:58:19 +02:00
private Options m_options ;
2013-05-25 16:40:15 +02:00
private ListResults m_result ;
2013-03-31 19:33:34 +02:00
2013-05-25 16:40:15 +02:00
public ListFilesHandler ( string backend , Options options , ListResults result )
2013-03-31 19:33:34 +02:00
{
m_backendurl = backend ;
m_options = options ;
2013-05-25 16:40:15 +02:00
m_result = result ;
2013-03-31 19:33:34 +02:00
}
2013-05-25 16:40:15 +02:00
public void Run ( IEnumerable < string > filterstrings = null , Library . Utility . IFilter compositefilter = null )
{
var parsedfilter = new Library . Utility . FilterExpression ( filterstrings );
2013-06-29 11:41:53 +02:00
var filter = Library . Utility . JoinedFilterExpression . Join ( parsedfilter , compositefilter );
2017-04-06 11:54:22 +02:00
var simpleList = !(( filter is Library . Utility . FilterExpression && (( Library . Utility . FilterExpression ) filter ). Type == Library . Utility . FilterType . Simple ) || m_options . AllVersions );
2013-05-11 12:03:15 +02:00
2013-03-31 19:33:34 +02:00
//Use a speedy local query
2013-05-13 22:32:05 +02:00
if (! m_options . NoLocalDb && System . IO . File . Exists ( m_options . Dbpath ))
2013-05-25 16:40:15 +02:00
using ( var db = new Database . LocalListDatabase ( m_options . Dbpath ))
{
m_result . SetDatabase ( db );
using ( var filesets = db . SelectFileSets ( m_options . Time , m_options . Version ))
2013-05-11 12:03:15 +02:00
{
2017-04-06 11:54:22 +02:00
if (! filter . Empty )
2014-03-21 15:01:49 +01:00
{
if ( simpleList || ( m_options . ListFolderContents && ! m_options . AllVersions ))
filesets . TakeFirst ();
}
IEnumerable < Database . LocalListDatabase . IFileversion > files ;
2014-03-21 19:23:54 +01:00
if ( m_options . ListFolderContents )
2014-03-21 15:01:49 +01:00
files = filesets . SelectFolderContents ( filter );
else if ( m_options . ListPrefixOnly )
files = filesets . GetLargestPrefix ( filter );
2017-04-06 11:54:22 +02:00
else if ( filter . Empty )
2014-03-21 19:23:54 +01:00
files = null ;
2014-03-21 15:01:49 +01:00
else
files = filesets . SelectFiles ( filter );
2014-08-19 20:24:54 +02:00
if ( m_options . ListSetsOnly )
m_result . SetResult (
filesets . QuickSets . Select ( x => new ListResultFileset ( x . Version , x . Time , x . FileCount , x . FileSizes )). ToArray (),
null
);
else
m_result . SetResult (
filesets . Sets . Select ( x => new ListResultFileset ( x . Version , x . Time , x . FileCount , x . FileSizes )). ToArray (),
files == null ? null :
( from n in files
select ( Duplicati . Library . Interface . IListResultFile )( new ListResultFile ( n . Path , n . Sizes . ToArray ())))
. ToArray ()
);
2014-03-21 15:01:49 +01:00
2013-05-25 16:40:15 +02:00
return ;
2013-05-11 12:03:15 +02:00
}
2013-05-25 16:40:15 +02:00
}
2013-05-11 12:03:15 +02:00
2018-03-12 14:07:11 +01:00
Logging . Log . WriteInformationMessage ( LOGTAG , "NoLocalDatabase" , "No local database, accessing remote store" );
2016-04-19 10:04:22 +02:00
2014-03-21 15:01:49 +01:00
//TODO: Add prefix and foldercontents
2016-04-19 10:04:22 +02:00
if ( m_options . ListFolderContents )
2018-03-12 14:07:11 +01:00
throw new UserInformationException ( "Listing folder contents is not supported without a local database, consider using the \"repair\" option to rebuild the database." , "FolderContentListingRequiresLocalDatabase" );
2016-04-19 10:04:22 +02:00
else if ( m_options . ListPrefixOnly )
2018-03-12 14:07:11 +01:00
throw new UserInformationException ( "Listing prefixes is not supported without a local database, consider using the \"repair\" option to rebuild the database." , "PrefixListingRequiresLocalDatabase" );
2013-04-01 13:05:55 +02:00
2013-03-31 19:33:34 +02:00
// Otherwise, grab info from remote location
2013-05-08 21:29:59 +02:00
using ( var tmpdb = new Library . Utility . TempFile ())
2016-04-06 20:40:34 +02:00
using ( var db = new Database . LocalDatabase ( tmpdb , "List" , true ))
2013-05-25 16:40:15 +02:00
using ( var backend = new BackendManager ( m_backendurl , m_options , m_result . BackendWriter , db ))
2013-03-31 19:33:34 +02:00
{
2013-05-25 16:40:15 +02:00
m_result . SetDatabase ( db );
2013-05-30 21:54:43 +02:00
var filteredList = ParseAndFilterFilesets ( backend . List (), m_options );
2013-05-11 12:03:15 +02:00
if ( filteredList . Count == 0 )
2018-03-12 14:07:11 +01:00
throw new UserInformationException ( "No filesets found on remote target" , "EmptyRemoteFolder" );
2013-05-30 21:54:43 +02:00
2016-02-10 17:18:48 +01:00
var numberSeq = CreateResultSequence ( filteredList );
2017-04-06 11:54:22 +02:00
if ( filter . Empty )
2013-05-25 16:40:15 +02:00
{
m_result . SetResult ( numberSeq , null );
2016-02-10 17:18:48 +01:00
m_result . EncryptedFiles = filteredList . Any ( x => ! string . IsNullOrWhiteSpace ( x . Value . EncryptionModule ));
2013-05-25 16:40:15 +02:00
return ;
}
2013-05-11 12:03:15 +02:00
var firstEntry = filteredList [ 0 ]. Value ;
filteredList . RemoveAt ( 0 );
Dictionary < string , List < long >> res ;
2014-05-15 12:47:16 +02:00
if ( m_result . TaskControlRendevouz () == TaskControlState . Stop )
return ;
2013-05-11 12:03:15 +02:00
using ( var tmpfile = backend . Get ( firstEntry . File . Name , firstEntry . File . Size , null ))
using ( var rd = new Volumes . FilesetVolumeReader ( RestoreHandler . GetCompressionModule ( firstEntry . File . Name ), tmpfile , m_options ))
if ( simpleList )
{
2013-05-25 16:40:15 +02:00
m_result . SetResult (
numberSeq . Take ( 1 ),
2013-05-11 12:03:15 +02:00
( from n in rd . Files
2013-06-30 12:42:13 +02:00
where Library . Utility . FilterExpression . Matches ( filter , n . Path )
2013-05-11 13:04:01 +02:00
orderby n . Path
2013-05-11 12:03:15 +02:00
select new ListResultFile ( n . Path , new long [] { n . Size }))
. ToArray ()
2013-05-25 16:40:15 +02:00
);
2013-05-11 12:03:15 +02:00
2013-05-25 16:40:15 +02:00
return ;
2013-05-11 12:03:15 +02:00
}
else
{
res = rd . Files
2013-06-30 12:42:13 +02:00
. Where ( x => Library . Utility . FilterExpression . Matches ( filter , x . Path ))
2013-05-11 12:03:15 +02:00
. ToDictionary (
x => x . Path ,
y =>
{
var lst = new List < long >();
lst . Add ( y . Size );
return lst ;
},
Library . Utility . Utility . ClientFilenameStringComparer
);
}
long flindex = 1 ;
foreach ( var flentry in filteredList )
2015-09-15 19:30:37 +02:00
using ( var tmpfile = backend . Get ( flentry . Value . File . Name , flentry . Value . File == null ? - 1 : flentry . Value . File . Size , null ))
2013-05-11 12:03:15 +02:00
using ( var rd = new Volumes . FilesetVolumeReader ( flentry . Value . CompressionModule , tmpfile , m_options ))
{
2014-05-15 12:47:16 +02:00
if ( m_result . TaskControlRendevouz () == TaskControlState . Stop )
return ;
2013-06-30 12:42:13 +02:00
foreach ( var p in from n in rd . Files where Library . Utility . FilterExpression . Matches ( filter , n . Path ) select n )
2013-05-11 12:03:15 +02:00
{
List < long > lst ;
if (! res . TryGetValue ( p . Path , out lst ))
{
lst = new List < long >();
res [ p . Path ] = lst ;
for ( var i = 0 ; i < flindex ; i ++)
lst . Add (- 1 );
}
lst . Add ( p . Size );
}
foreach ( var n in from i in res where i . Value . Count < flindex + 1 select i )
n . Value . Add (- 1 );
2013-04-09 20:43:27 +02:00
2013-05-11 12:03:15 +02:00
flindex ++;
}
2013-05-25 16:40:15 +02:00
m_result . SetResult (
2013-05-11 12:03:15 +02:00
numberSeq ,
from n in res
orderby n . Key
2013-05-25 16:40:15 +02:00
select ( Duplicati . Library . Interface . IListResultFile )( new ListResultFile ( n . Key , n . Value ))
);
2013-03-31 19:33:34 +02:00
}
}
2013-05-30 21:54:43 +02:00
public static List < KeyValuePair < long , Volumes . IParsedVolume >> ParseAndFilterFilesets ( IEnumerable < Duplicati . Library . Interface . IFileEntry > rawlist , Options options )
{
var parsedlist = ( from n in rawlist
let p = Volumes . VolumeBase . ParseFilename ( n )
where p != null && p . FileType == RemoteVolumeType . Files
orderby p . Time descending
select p ). ToArray ();
var filelistFilter = RestoreHandler . FilterNumberedFilelist ( options . Time , options . Version );
return filelistFilter ( parsedlist ). ToList ();
}
public static IEnumerable < Library . Interface . IListResultFileset > CreateResultSequence ( IEnumerable < KeyValuePair < long , Volumes . IParsedVolume >> filteredList )
{
return ( from n in filteredList select ( Library . Interface . IListResultFileset )( new ListResultFileset ( n . Key , n . Value . Time . ToLocalTime (), - 1 , - 1 ))). ToArray ();
2013-06-29 11:41:53 +02:00
}
2013-03-31 19:33:34 +02:00
}
}