keepass2android/src/keepass2android/CreateDatabaseActivity.cs

507 lines
14 KiB
C#

using System;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Graphics.Drawables;
using Android.OS;
using Android.Preferences;
using Android.Support.V7.App;
using Android.Text;
using Android.Views;
using Android.Widget;
using Java.IO;
using KeePassLib.Keys;
using KeePassLib.Serialization;
using keepass2android.Io;
using Environment = Android.OS.Environment;
using IOException = Java.IO.IOException;
namespace keepass2android
{
[Activity(Label = "@string/app_name",
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden,
Theme = "@style/MyTheme_ActionBar")]
public class CreateDatabaseActivity : AppCompatActivity
{
private IOConnectionInfo _ioc;
private string _keyfileFilename;
private bool _restoringInstanceState;
private bool _showPassword;
private readonly ActivityDesign _design;
private AppTask _appTask;
public CreateDatabaseActivity()
{
_design = new ActivityDesign(this);
}
private const int RequestCodeKeyFile = 0;
private const int RequestCodeDbFilename = 1;
private const string KeyfilefilenameBundleKey = "KeyfileFilename";
protected override void OnSaveInstanceState(Bundle outState)
{
base.OnSaveInstanceState(outState);
outState.PutString(PasswordActivity.KeyFilename, _ioc.Path);
outState.PutString(PasswordActivity.KeyServerusername, _ioc.UserName);
outState.PutString(PasswordActivity.KeyServerpassword, _ioc.Password);
outState.PutInt(PasswordActivity.KeyServercredmode, (int)_ioc.CredSaveMode);
if (_keyfileFilename != null)
outState.PutString(KeyfilefilenameBundleKey, _keyfileFilename);
}
protected override void OnCreate(Bundle bundle)
{
_design.ApplyTheme();
base.OnCreate(bundle);
SupportActionBar.SetDisplayHomeAsUpEnabled(true);
SupportActionBar.SetHomeButtonEnabled(true);
SetContentView(Resource.Layout.create_database);
_appTask = AppTask.GetTaskInOnCreate(bundle, Intent);
SetDefaultIoc();
FindViewById(Resource.Id.keyfile_filename).Visibility = ViewStates.Gone;
var keyfileCheckbox = FindViewById<CheckBox>(Resource.Id.use_keyfile);
if (bundle != null)
{
_keyfileFilename = bundle.GetString(KeyfilefilenameBundleKey, null);
if (_keyfileFilename != null)
{
FindViewById<TextView>(Resource.Id.keyfile_filename).Text = ConvertFilenameToIocPath(_keyfileFilename);
FindViewById(Resource.Id.keyfile_filename).Visibility = ViewStates.Visible;
keyfileCheckbox.Checked = true;
}
if (bundle.GetString(PasswordActivity.KeyFilename, null) != null)
{
_ioc = new IOConnectionInfo
{
Path = bundle.GetString(PasswordActivity.KeyFilename),
UserName = bundle.GetString(PasswordActivity.KeyServerusername),
Password = bundle.GetString(PasswordActivity.KeyServerpassword),
CredSaveMode = (IOCredSaveMode) bundle.GetInt(PasswordActivity.KeyServercredmode),
};
}
}
UpdateIocView();
keyfileCheckbox.CheckedChange += (sender, args) =>
{
if (keyfileCheckbox.Checked)
{
if (_restoringInstanceState)
return;
string defaulFilename = _keyfileFilename;
if (_keyfileFilename == null)
{
defaulFilename = _keyfileFilename = SdDir + "keyfile.txt";
if (defaulFilename.StartsWith("file://") == false)
defaulFilename = "file://" + defaulFilename;
}
new FileSelectHelper(this, false, RequestCodeKeyFile).StartFileChooser(defaulFilename);
}
else
{
FindViewById(Resource.Id.keyfile_filename).Visibility = ViewStates.Gone;
_keyfileFilename = null;
}
};
FindViewById(Resource.Id.btn_change_location).Click += (sender, args) =>
{
Intent intent = new Intent(this, typeof(FileStorageSelectionActivity));
StartActivityForResult(intent, 0);
};
Button generatePassword = (Button)FindViewById(Resource.Id.generate_button);
generatePassword.Click += (sender, e) =>
{
GeneratePasswordActivity.LaunchWithoutLockCheck(this);
};
FindViewById(Resource.Id.btn_create).Click += (sender, evt) =>
{
CreateDatabase();
};
ImageButton btnTogglePassword = (ImageButton)FindViewById(Resource.Id.toggle_password);
btnTogglePassword.Click += (sender, e) =>
{
_showPassword = !_showPassword;
MakePasswordMaskedOrVisible();
};
Android.Graphics.PorterDuff.Mode mMode = Android.Graphics.PorterDuff.Mode.SrcAtop;
Android.Graphics.Color color = new Android.Graphics.Color (224, 224, 224);
btnTogglePassword.SetColorFilter (color, mMode);
}
private void MakePasswordMaskedOrVisible()
{
TextView password = (TextView)FindViewById(Resource.Id.entry_password);
TextView confpassword = (TextView)FindViewById(Resource.Id.entry_confpassword);
if (_showPassword)
{
password.InputType = InputTypes.ClassText | InputTypes.TextVariationVisiblePassword;
confpassword.Visibility = ViewStates.Gone;
}
else
{
password.InputType = InputTypes.ClassText | InputTypes.TextVariationPassword;
confpassword.Visibility = ViewStates.Visible;
}
}
private void CreateDatabase()
{
var keyfileCheckbox = FindViewById<CheckBox>(Resource.Id.use_keyfile);
string password;
if (!TryGetPassword(out password)) return;
// Verify that a password or keyfile is set
if (password.Length == 0 && !keyfileCheckbox.Checked)
{
Toast.MakeText(this, Resource.String.error_nopass, ToastLength.Long).Show();
return;
}
//create the key
CompositeKey newKey = new CompositeKey();
if (String.IsNullOrEmpty(password) == false)
{
newKey.AddUserKey(new KcpPassword(password));
}
if (String.IsNullOrEmpty(_keyfileFilename) == false)
{
try
{
newKey.AddUserKey(new KcpKeyFile(_keyfileFilename));
}
catch (Exception)
{
Toast.MakeText(this, Resource.String.error_adding_keyfile, ToastLength.Long).Show();
return;
}
}
// Create the new database
CreateDb create = new CreateDb(App.Kp2a, this, _ioc, new LaunchGroupActivity(_ioc, this), false, newKey);
ProgressTask createTask = new ProgressTask(
App.Kp2a,
this, create);
createTask.Run();
}
private bool TryGetPassword(out string pass)
{
TextView passView = (TextView)FindViewById(Resource.Id.entry_password);
pass = passView.Text;
if (_showPassword)
return true;
TextView passConfView = (TextView) FindViewById(Resource.Id.entry_confpassword);
String confpass = passConfView.Text;
// Verify that passwords match
if (! pass.Equals(confpass))
{
// Passwords do not match
Toast.MakeText(this, Resource.String.error_pass_match, ToastLength.Long).Show();
return false;
}
return true;
}
protected override void OnResume()
{
base.OnResume();
_design.ReapplyTheme();
}
protected override void OnRestoreInstanceState(Bundle savedInstanceState)
{
_restoringInstanceState = true;
base.OnRestoreInstanceState(savedInstanceState);
_restoringInstanceState = false;
}
private void UpdateIocView()
{
string displayPath = App.Kp2a.GetFileStorage(_ioc).GetDisplayName(_ioc);
int protocolSeparatorPos = displayPath.IndexOf("://", StringComparison.Ordinal);
string protocolId = protocolSeparatorPos < 0 ?
"file" : displayPath.Substring(0, protocolSeparatorPos);
Drawable drawable = App.Kp2a.GetResourceDrawable("ic_storage_" + protocolId);
FindViewById<ImageView>(Resource.Id.filestorage_logo).SetImageDrawable(drawable);
String title = App.Kp2a.GetResourceString("filestoragename_" + protocolId);
FindViewById<TextView>(Resource.Id.filestorage_label).Text = title;
FindViewById<TextView>(Resource.Id.label_filename).Text = protocolSeparatorPos < 0 ?
displayPath :
displayPath.Substring(protocolSeparatorPos + 3);
}
private void SetDefaultIoc()
{
File directory = GetExternalFilesDir(null);
if (directory == null)
directory = FilesDir;
string strDir = directory.CanonicalPath;
if (!strDir.EndsWith(File.Separator))
strDir += File.Separator;
string filename = strDir + "keepass.kdbx";
filename = ConvertFilenameToIocPath(filename);
int count = 2;
while (new File(filename).Exists())
{
filename = ConvertFilenameToIocPath(strDir + "keepass" + count + ".kdbx");
count++;
}
_ioc = new IOConnectionInfo
{
Path = filename
};
}
private static string SdDir
{
get
{
string sdDir = Environment.ExternalStorageDirectory.AbsolutePath;
if (!sdDir.EndsWith("/"))
sdDir += "/";
if (!sdDir.StartsWith("file://"))
sdDir = "file://" + sdDir;
return sdDir;
}
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (resultCode == KeePass.ResultOkPasswordGenerator)
{
String generatedPassword = data.GetStringExtra("keepass2android.password.generated_password");
FindViewById<TextView>(Resource.Id.entry_password).Text = generatedPassword;
FindViewById<TextView>(Resource.Id.entry_confpassword).Text = generatedPassword;
}
if (resultCode == KeePass.ExitFileStorageSelectionOk)
{
string protocolId = data.GetStringExtra("protocolId");
if (protocolId == "content")
{
Util.ShowBrowseDialog(this, RequestCodeDbFilename, true, true);
}
else
{
FileSelectHelper fileSelectHelper = new FileSelectHelper(this, true, RequestCodeDbFilename)
{
DefaultExtension = "kdbx"
};
fileSelectHelper.OnOpen += (sender, info) =>
{
_ioc = info;
UpdateIocView();
};
App.Kp2a.GetFileStorage(protocolId).StartSelectFile(
new FileStorageSetupInitiatorActivity(this,OnActivityResult,s => fileSelectHelper.PerformManualFileSelect(s)),
true,
RequestCodeDbFilename,
protocolId);
}
}
if (resultCode == Result.Ok)
{
if (requestCode == RequestCodeKeyFile)
{
string filename = Util.IntentToFilename(data, this);
if (filename != null)
{
_keyfileFilename = ConvertFilenameToIocPath(filename);
FindViewById<TextView>(Resource.Id.keyfile_filename).Text = _keyfileFilename;
FindViewById(Resource.Id.keyfile_filename).Visibility = ViewStates.Visible;
}
}
if (requestCode == RequestCodeDbFilename)
{
if (data.Data.Scheme == "content")
{
if ((int)Build.VERSION.SdkInt >= 19)
{
//try to take persistable permissions
try
{
Kp2aLog.Log("TakePersistableUriPermission");
var takeFlags = data.Flags
& (ActivityFlags.GrantReadUriPermission
| ActivityFlags.GrantWriteUriPermission);
this.ContentResolver.TakePersistableUriPermission(data.Data, takeFlags);
}
catch (Exception e)
{
Kp2aLog.Log(e.ToString());
}
}
}
string filename = Util.IntentToFilename(data, this);
if (filename == null)
filename = data.DataString;
bool fileExists = data.GetBooleanExtra("group.pals.android.lib.ui.filechooser.FileChooserActivity.result_file_exists", true);
if (fileExists)
{
_ioc = new IOConnectionInfo { Path = ConvertFilenameToIocPath(filename) };
UpdateIocView();
}
else
{
var task = new CreateNewFilename(new ActionOnFinish((success, messageOrFilename) =>
{
if (!success)
{
Toast.MakeText(this, messageOrFilename, ToastLength.Long).Show();
return;
}
_ioc = new IOConnectionInfo { Path = ConvertFilenameToIocPath(messageOrFilename) };
UpdateIocView();
}), filename);
new ProgressTask(App.Kp2a, this, task).Run();
}
}
}
if (resultCode == (Result)FileStorageResults.FileUsagePrepared)
{
_ioc = new IOConnectionInfo();
PasswordActivity.SetIoConnectionFromIntent(_ioc, data);
UpdateIocView();
}
if (resultCode == (Result)FileStorageResults.FileChooserPrepared)
{
IOConnectionInfo ioc = new IOConnectionInfo();
PasswordActivity.SetIoConnectionFromIntent(ioc, data);
new FileSelectHelper(this, true, RequestCodeDbFilename) { DefaultExtension = "kdbx" }
.StartFileChooser(ioc.Path);
}
}
private static string ConvertFilenameToIocPath(string filename)
{
if ((filename != null) && (filename.StartsWith("file://")))
{
filename = filename.Substring(7);
filename = Java.Net.URLDecoder.Decode(filename);
}
return filename;
}
private void AfterQueryCredentials(IOConnectionInfo ioc)
{
_ioc = ioc;
UpdateIocView();
}
private class LaunchGroupActivity : FileOnFinish
{
readonly CreateDatabaseActivity _activity;
private readonly IOConnectionInfo _ioc;
public LaunchGroupActivity(IOConnectionInfo ioc, CreateDatabaseActivity activity)
: base(null)
{
_activity = activity;
_ioc = ioc;
}
public override void Run()
{
if (Success)
{
// Update the ongoing notification
App.Kp2a.UpdateOngoingNotification();
if (PreferenceManager.GetDefaultSharedPreferences(_activity).GetBoolean(_activity.GetString(Resource.String.RememberRecentFiles_key), _activity.Resources.GetBoolean(Resource.Boolean.RememberRecentFiles_default)))
{
// Add to recent files
FileDbHelper dbHelper = App.Kp2a.FileDbHelper;
//TODO: getFilename always returns "" -> bug?
dbHelper.CreateFile(_ioc, Filename);
}
GroupActivity.Launch(_activity, _activity._appTask);
_activity.Finish();
}
else
{
DisplayMessage(_activity);
try
{
App.Kp2a.GetFileStorage(_ioc).Delete(_ioc);
}
catch (Exception e)
{
//not nice, but not a catastrophic failure if we can't delete the file:
Kp2aLog.Log("couldn't delete file after failure! " + e);
}
}
}
}
public override bool OnOptionsItemSelected(IMenuItem item)
{
switch (item.ItemId)
{
case Android.Resource.Id.Home:
OnBackPressed();
return true;
}
return false;
}
}
}