2015-01-20 21:44:52 +01:00
// Copyright (C) 2015, The Duplicati Team
2013-05-06 09:58:19 +02:00
2015-01-20 21:44:52 +01:00
// http://www.duplicati.com, info@duplicati.com
2013-05-06 09:58:19 +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.Linq ;
using System.Collections.Generic ;
2013-05-06 20:51:15 +02:00
using Newtonsoft.Json.Serialization ;
2013-05-06 09:58:19 +02:00
2013-05-08 20:17:07 +02:00
namespace Duplicati.Library.Main
2013-05-06 09:58:19 +02:00
{
public static class DatabaseLocator
{
public class BackendEntry
{
public string Type ;
public string Server ;
public string Path ;
public string Prefix ;
public string Username ;
2013-05-11 13:14:07 +02:00
//public string Passwordhash;
2013-05-06 09:58:19 +02:00
public int Port ;
public string Databasepath ;
public string ParameterFile ;
}
2013-09-06 23:44:39 +02:00
public static string GetDatabasePath ( string backend , Options options , bool autoCreate = true , bool anyUsername = false )
2013-05-06 09:58:19 +02:00
{
2014-07-25 22:17:48 +02:00
if ( options == null )
options = new Options ( new Dictionary < string , string >());
2013-05-08 19:57:13 +02:00
if (! string . IsNullOrEmpty ( options . Dbpath ))
2017-09-18 23:23:45 -06:00
return options . Dbpath ;
//Normal mode uses the systems "(Local) Application Data" folder
// %LOCALAPPDATA% on Windows, ~/.config on Linux
// Special handling for Windows:
// - Older versions use %APPDATA%
// - but new versions use %LOCALAPPDATA%
//
// If we find a new version, lets use that
// otherwise use the older location
//
var folder = System . IO . Path . Combine ( System . Environment . GetFolderPath ( Environment . SpecialFolder . ApplicationData ), "Duplicati" );
if ( Duplicati . Library . Utility . Utility . IsClientWindows )
{
var newlocation = System . IO . Path . Combine ( System . Environment . GetFolderPath ( Environment . SpecialFolder . LocalApplicationData ), "Duplicati" );
var prevfile = System . IO . Path . Combine ( folder , "dbconfig.json" );
var curfile = System . IO . Path . Combine ( newlocation , "dbconfig.json" );
// If the new file exists, we use that
// If the new file does not exist, and the old file exists we use the old
// Otherwise we use the new location
if ( System . IO . File . Exists ( curfile ) || ! System . IO . File . Exists ( prevfile ))
folder = newlocation ;
2017-06-27 22:00:44 +02:00
}
2017-06-27 21:45:04 +02:00
2013-05-06 09:58:19 +02:00
if (! System . IO . Directory . Exists ( folder ))
System . IO . Directory . CreateDirectory ( folder );
2013-05-06 20:51:15 +02:00
var file = System . IO . Path . Combine ( folder , "dbconfig.json" );
2013-05-06 09:58:19 +02:00
List < BackendEntry > configs ;
if (! System . IO . File . Exists ( file ))
configs = new List < BackendEntry >();
else
2013-05-06 20:51:15 +02:00
configs = Newtonsoft . Json . JsonConvert . DeserializeObject < List < BackendEntry >>( System . IO . File . ReadAllText ( file , System . Text . Encoding . UTF8 ));
2013-05-06 09:58:19 +02:00
2013-05-08 21:29:59 +02:00
var uri = new Library . Utility . Uri ( backend );
2013-05-06 09:58:19 +02:00
string server = uri . Host ;
string path = uri . Path ;
string type = uri . Scheme ;
int port = uri . Port ;
string username = uri . Username ;
string password = uri . Password ;
2013-05-13 22:32:05 +02:00
string prefix = options . Prefix ;
2013-05-06 09:58:19 +02:00
if ( username == null || password == null )
{
var sopts = DynamicLoader . BackendLoader . GetSupportedCommands ( backend );
var ropts = new Dictionary < string , string >( options . RawOptions );
foreach ( var k in uri . QueryParameters . AllKeys )
ropts [ k ] = uri . QueryParameters [ k ];
2014-03-10 00:23:37 +01:00
if ( sopts != null )
2013-05-06 09:58:19 +02:00
{
2014-03-10 00:23:37 +01:00
foreach ( var o in sopts )
{
2017-09-18 23:23:45 -06:00
if ( username == null && o . Aliases != null && o . Aliases . Contains ( "auth-username" , StringComparer . OrdinalIgnoreCase ) && ropts . ContainsKey ( o . Name ))
2014-03-10 00:23:37 +01:00
username = ropts [ o . Name ];
2017-09-18 23:23:45 -06:00
if ( password == null && o . Aliases != null && o . Aliases . Contains ( "auth-password" , StringComparer . OrdinalIgnoreCase ) && ropts . ContainsKey ( o . Name ))
2014-03-10 00:23:37 +01:00
password = ropts [ o . Name ];
}
2013-05-06 09:58:19 +02:00
2014-03-10 00:23:37 +01:00
foreach ( var o in sopts )
{
2017-09-18 23:23:45 -06:00
if ( username == null && o . Name . Equals ( "auth-username" , StringComparison . OrdinalIgnoreCase ) && ropts . ContainsKey ( "auth-username" ))
2014-03-10 00:23:37 +01:00
username = ropts [ "auth-username" ];
2017-09-18 23:23:45 -06:00
if ( password == null && o . Name . Equals ( "auth-password" , StringComparison . OrdinalIgnoreCase ) && ropts . ContainsKey ( "auth-password" ))
2014-03-10 00:23:37 +01:00
password = ropts [ "auth-password" ];
}
2013-05-06 09:58:19 +02:00
}
}
if ( password != null )
2013-05-11 13:14:07 +02:00
password = Library . Utility . Utility . ByteArrayAsHexString ( System . Security . Cryptography . SHA256 . Create (). ComputeHash ( System . Text . Encoding . UTF8 . GetBytes ( password + "!" + uri . Scheme + "!" + uri . HostAndPath )));
2013-05-06 09:58:19 +02:00
//Now find the one that matches :)
var matches = ( from n in configs
where
n . Type == type &&
2013-05-11 13:14:07 +02:00
//n.Passwordhash == password &&
2013-05-06 09:58:19 +02:00
n . Username == username &&
n . Port == port &&
n . Server == server &&
n . Path == path &&
n . Prefix == prefix
select n ). ToList ();
if ( matches . Count > 1 )
2018-03-12 14:07:11 +01:00
throw new Duplicati . Library . Interface . UserInformationException ( string . Format ( "Multiple sources found for: {0}" , backend ), "MultipleLocalDatabaseSourcesFound" );
2013-05-06 09:58:19 +02:00
2013-09-06 23:44:39 +02:00
// Re-select
if ( matches . Count == 0 && anyUsername && string . IsNullOrEmpty ( username ))
{
matches = ( from n in configs
where
n . Type == type &&
n . Port == port &&
n . Server == server &&
n . Path == path &&
n . Prefix == prefix
select n ). ToList ();
if ( matches . Count > 1 )
2018-03-12 14:07:11 +01:00
throw new Duplicati . Library . Interface . UserInformationException ( String . Format ( "Multiple sources found for \"{0}\", try supplying --{1}" , backend , "auth-username" ), "MultipleLocalDatabaseSourcesFound" );
2013-09-06 23:44:39 +02:00
}
if ( matches . Count == 0 && ! autoCreate )
return null ;
2013-05-06 09:58:19 +02:00
if ( matches . Count == 0 )
{
var backupname = options . BackupName ;
2013-05-06 17:46:23 +02:00
if ( string . IsNullOrEmpty ( backupname ) || backupname == Options . DefaultBackupName )
2013-05-06 09:58:19 +02:00
backupname = GenerateRandomName ();
else
{
foreach ( var c in System . IO . Path . GetInvalidFileNameChars ())
backupname = backupname . Replace ( c . ToString (), "" );
}
var newpath = System . IO . Path . Combine ( folder , backupname + ".sqlite" );
int max_tries = 100 ;
while ( System . IO . File . Exists ( newpath ) && max_tries -- > 0 )
newpath = System . IO . Path . Combine ( folder , GenerateRandomName ());
if ( System . IO . File . Exists ( newpath ))
2018-03-12 14:07:11 +01:00
throw new Duplicati . Library . Interface . UserInformationException ( "Unable to find a unique name for the database, please use --dbpath" , "CannotCreateRandomName" );
2013-05-06 09:58:19 +02:00
//Create a new one, add it to the list, and save it
configs . Add ( new BackendEntry () {
Type = type ,
Server = server ,
Path = path ,
Prefix = prefix ,
Username = username ,
2013-05-11 13:14:07 +02:00
//Passwordhash = password,
2013-05-06 09:58:19 +02:00
Port = port ,
Databasepath = newpath ,
ParameterFile = null
});
2013-05-06 20:51:15 +02:00
var settings = new Newtonsoft . Json . JsonSerializerSettings ();
settings . Formatting = Newtonsoft . Json . Formatting . Indented ;
System . IO . File . WriteAllText ( file , Newtonsoft . Json . JsonConvert . SerializeObject ( configs , settings ), System . Text . Encoding . UTF8 );
2013-05-06 09:58:19 +02:00
return newpath ;
}
else
{
return matches [ 0 ]. Databasepath ;
}
}
2014-03-05 15:28:52 +01:00
public static string GenerateRandomName ()
2013-05-06 09:58:19 +02:00
{
var rnd = new Random ();
2018-05-13 15:29:55 -07:00
System . Text . StringBuilder backupName = new System . Text . StringBuilder ();
for ( var i = 0 ; i < 10 ; i ++)
backupName . Append ( rnd . Next ( 'A' , 'Z' + 1 ));
return backupName . ToString ();
2013-05-06 09:58:19 +02:00
}
2016-10-13 21:45:31 +02:00
public static bool IsDatabasePathInUse ( string path )
{
var folder = System . IO . Path . Combine ( System . Environment . GetFolderPath ( Environment . SpecialFolder . ApplicationData ), "Duplicati" );
if (! System . IO . Directory . Exists ( folder ))
return false ;
var file = System . IO . Path . Combine ( folder , "dbconfig.json" );
if (! System . IO . File . Exists ( file ))
return false ;
2018-05-16 19:40:51 +02:00
return Newtonsoft . Json . JsonConvert . DeserializeObject < List < BackendEntry >>( System . IO . File . ReadAllText ( file , System . Text . Encoding . UTF8 )). Any ( x => string . Equals ( path , x . Databasepath , Library . Utility . Utility . ClientFilenameStringComparison ));
2016-10-13 21:45:31 +02:00
}
2013-05-06 09:58:19 +02:00
}
}