2015-01-20 21:44:52 +01:00
// Copyright (C) 2015, The Duplicati Team
2013-05-08 21:29:59 +02:00
2015-01-20 21:44:52 +01:00
// http://www.duplicati.com, info@duplicati.com
2013-05-08 21:29:59 +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 ;
using System.Collections.Generic ;
using System.IO ;
using Newtonsoft.Json ;
using System.Text ;
using Duplicati.Library.Main.Database ;
namespace Duplicati.Library.Main
{
public class Utility
{
/// <summary>
/// Implementation of the IMetahash interface
/// </summary>
private class Metahash : IMetahash
{
/// <summary>
/// The base64 encoded hash
/// </summary>
2016-04-03 23:10:47 +02:00
private readonly string m_filehash ;
2013-05-08 21:29:59 +02:00
/// <summary>
/// The UTF-8 encoded json element with the metadata
/// </summary>
private readonly byte [] m_blob ;
/// <summary>
/// The lookup table with elements
/// </summary>
private readonly Dictionary < string , string > m_values ;
public Metahash ( Dictionary < string , string > values , Options options )
{
m_values = values ;
using ( var ms = new System . IO . MemoryStream ())
using ( var w = new StreamWriter ( ms , Encoding . UTF8 ))
2017-09-20 09:58:43 +02:00
using ( var filehasher = Library . Utility . HashAlgorithmHelper . Create ( options . FileHashAlgorithm ))
2013-05-08 21:29:59 +02:00
{
2016-04-03 23:10:47 +02:00
if ( filehasher == null )
2018-03-12 14:07:11 +01:00
throw new Duplicati . Library . Interface . UserInformationException ( Strings . Common . InvalidHashAlgorithm ( options . FileHashAlgorithm ), "FileHashAlgorithmNotSupported" );
2016-04-03 23:10:47 +02:00
2013-05-08 21:29:59 +02:00
w . Write ( JsonConvert . SerializeObject ( values ));
w . Flush ();
m_blob = ms . ToArray ();
ms . Position = 0 ;
2016-04-03 23:10:47 +02:00
m_filehash = Convert . ToBase64String ( filehasher . ComputeHash ( ms ));
2013-05-08 21:29:59 +02:00
}
}
2016-04-03 23:10:47 +02:00
public string FileHash
2013-05-08 21:29:59 +02:00
{
2016-04-03 23:10:47 +02:00
get { return m_filehash ; }
2013-05-08 21:29:59 +02:00
}
2016-04-03 23:10:47 +02:00
2013-05-08 21:29:59 +02:00
public byte [] Blob
{
get { return m_blob ; }
}
public Dictionary < string , string > Values
{
get { return m_values ; }
}
}
/// <summary>
/// Constructs a container for a given metadata dictionary
/// </summary>
/// <param name="values">The metadata values to wrap</param>
/// <returns>A IMetahash instance</returns>
public static IMetahash WrapMetadata ( Dictionary < string , string > values , Options options )
{
return new Metahash ( values , options );
}
2015-04-08 20:33:30 +02:00
internal static void UpdateOptionsFromDb ( LocalDatabase db , Options options , System . Data . IDbTransaction transaction = null )
{
string n = null ;
var opts = db . GetDbOptions ( transaction );
if ( opts . ContainsKey ( "blocksize" ) && (! options . RawOptions . TryGetValue ( "blocksize" , out n ) || string . IsNullOrEmpty ( n )))
options . RawOptions [ "blocksize" ] = opts [ "blocksize" ] + "b" ;
if ( opts . ContainsKey ( "blockhash" ) && (! options . RawOptions . TryGetValue ( "block-hash-algorithm" , out n ) || string . IsNullOrEmpty ( n )))
options . RawOptions [ "block-hash-algorithm" ] = opts [ "blockhash" ];
if ( opts . ContainsKey ( "filehash" ) && (! options . RawOptions . TryGetValue ( "file-hash-algorithm" , out n ) || string . IsNullOrEmpty ( n )))
options . RawOptions [ "file-hash-algorithm" ] = opts [ "filehash" ];
}
internal static void VerifyParameters ( LocalDatabase db , Options options , System . Data . IDbTransaction transaction = null )
2013-05-08 21:29:59 +02:00
{
2018-11-20 20:08:39 +01:00
var newDict = new Dictionary < string , string >
{
{ "blocksize" , options . Blocksize . ToString () },
{ "blockhash" , options . BlockHashAlgorithm },
{ "filehash" , options . FileHashAlgorithm }
};
2015-04-08 20:33:30 +02:00
var opts = db . GetDbOptions ( transaction );
2013-08-17 22:05:40 +02:00
if ( options . NoEncryption )
{
newDict . Add ( "passphrase" , "no-encryption" );
}
else
{
string salt ;
opts . TryGetValue ( "passphrase-salt" , out salt );
if ( string . IsNullOrEmpty ( salt ))
{
// Not Crypto-class PRNG salts
var buf = new byte [ 32 ];
new Random (). NextBytes ( buf );
//Add version so we can detect and change the algorithm
salt = "v1:" + Library . Utility . Utility . ByteArrayAsHexString ( buf );
}
newDict [ "passphrase-salt" ] = salt ;
// We avoid storing the passphrase directly,
// instead we salt and rehash repeatedly
newDict . Add ( "passphrase" , Library . Utility . Utility . ByteArrayAsHexString ( Library . Utility . Utility . RepeatedHashWithSalt ( options . Passphrase , salt , 1200 )));
}
2013-05-08 21:29:59 +02:00
var needsUpdate = false ;
foreach ( var k in newDict )
if (! opts . ContainsKey ( k . Key ))
needsUpdate = true ;
else if ( opts [ k . Key ] != k . Value )
2013-08-24 21:17:12 +02:00
{
if ( k . Key == "passphrase" )
{
2013-10-29 11:43:44 +01:00
if (! options . AllowPassphraseChange )
{
if ( newDict [ k . Key ] == "no-encryption" )
2018-03-12 14:07:11 +01:00
throw new Duplicati . Library . Interface . UserInformationException ( "You have attempted to remove the passphrase on an existing backup, which is not supported. Please configure a new clean backup if you want to remove the passphrase." , "PassphraseRemovalNotSupported" );
2013-10-29 11:43:44 +01:00
else if ( opts [ k . Key ] == "no-encryption" )
2018-03-12 14:07:11 +01:00
throw new Duplicati . Library . Interface . UserInformationException ( "You have attempted to add a passphrase to an existing backup, which is not supported. Please configure a new clean backup if you want to add a passphrase." , "PassphraseAdditionNotSupported" );
2013-10-29 11:43:44 +01:00
else
2018-03-12 14:07:11 +01:00
throw new Duplicati . Library . Interface . UserInformationException ( "You have attempted to change a passphrase to an existing backup, which is not supported. Please configure a new clean backup if you want to change the passphrase." , "PassphraseChangeNotSupported" );
2013-10-29 11:43:44 +01:00
}
2013-08-24 21:17:12 +02:00
}
else
2018-03-12 14:07:11 +01:00
throw new Duplicati . Library . Interface . UserInformationException ( string . Format ( "You have attempted to change the parameter \"{0}\" from \"{1}\" to \"{2}\", which is not supported. Please configure a new clean backup if you want to change the parameter." , k . Key , opts [ k . Key ], k . Value ), "ParameterChangeNotSupported" );
2013-08-24 21:17:12 +02:00
}
2016-03-18 13:26:12 +01:00
2013-05-08 21:29:59 +02:00
//Extra sanity check
if ( db . GetBlocksLargerThan ( options . Blocksize ) > 0 )
2018-03-12 14:07:11 +01:00
throw new Duplicati . Library . Interface . UserInformationException ( "You have attempted to change the block-size on an existing backup, which is not supported. Please configure a new clean backup if you want to change the block-size." , "BlockSizeChangeNotSupported" );
2013-05-08 21:29:59 +02:00
if ( needsUpdate )
2016-03-18 13:26:12 +01:00
{
2017-11-05 13:32:01 -05:00
// Make sure we do not lose values
2016-03-18 13:26:12 +01:00
foreach ( var k in opts )
if (! newDict . ContainsKey ( k . Key ))
newDict [ k . Key ] = k . Value ;
2015-04-08 20:33:30 +02:00
db . SetDbOptions ( newDict , transaction );
2016-03-18 13:26:12 +01:00
}
2015-12-22 02:15:49 +01:00
}
/// <summary>
/// The filename for the marker file that the user can add to suppress donation messages
/// </summary>
private const string SUPPRESS_DONATIONS_FILENAME = "suppress_donation_messages.txt" ;
/// <summary>
/// Gets or sets donation message suppression
/// </summary>
public static bool SuppressDonationMessages
{
get
{
try
{
var folder = Path . Combine ( System . Environment . GetFolderPath ( Environment . SpecialFolder . ApplicationData ), AutoUpdater . AutoUpdateSettings . AppName );
return File . Exists ( Path . Combine ( folder , SUPPRESS_DONATIONS_FILENAME ));
}
catch
{
}
return true ;
}
set
{
try
{
var folder = Path . Combine ( System . Environment . GetFolderPath ( Environment . SpecialFolder . ApplicationData ), AutoUpdater . AutoUpdateSettings . AppName );
if (! Directory . Exists ( folder ))
Directory . CreateDirectory ( folder );
var path = Path . Combine ( folder , SUPPRESS_DONATIONS_FILENAME );
if ( value )
using ( File . OpenWrite ( path ))
{
}
else
File . Delete ( path );
}
catch
{
}
}
}
2013-05-08 21:29:59 +02:00
}
}