context: /*
  This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.
  
    Keepass2Android is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
  
    Keepass2Android 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 General Public License for more details.
  
    You should have received a copy of the GNU General Public License
    along with Keepass2Android.  If not, see <http://www.gnu.org/licenses/>.
    */
  
  using System;
  using System.Collections.Generic;
  using System.IO;
  using System.Threading.Tasks;
  using System.Xml;
  using System.Xml.Serialization;
  using Android.App;
  using Android.Content;
  using Android.Database;
  using Android.OS;
  using Android.Runtime;
  using Android.Views;
  using Android.Widget;
  using Java.Net;
  using Android.Preferences;
  using Android.Text;
  using Android.Content.PM;
  using KeePassLib.Keys;
  using KeePassLib.Serialization;
  using KeePassLib.Utility;
  using Keepass2android.Pluginsdk;
  using OtpKeyProv;
  using keepass2android.Io;
  using keepass2android.Utils;
  using Exception = System.Exception;
  using File = Java.IO.File;
  using FileNotFoundException = Java.IO.FileNotFoundException;
  using MemoryStream = System.IO.MemoryStream;
  using Object = Java.Lang.Object;
  using Process = Android.OS.Process;
  using String = System.String;
  using KeeChallenge;
  
  namespace keepass2android
  {
  	[Activity(Label = "@string/app_name",
  		ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden,
  		LaunchMode = LaunchMode.SingleInstance,
  		Theme = "@style/Base")]
  	public class PasswordActivity : LockingActivity {
  
  		enum KeyProviders
  		{
  			//int values correspond to indices in passwordSpinner
  			None = 0,
  			KeyFile = 1,
  			Otp = 2,
  			OtpRecovery = 3,
  			Challenge = 4,
  			ChalRecovery = 5
  		}
  
  		public const String KeyDefaultFilename = "defaultFileName";
  
  		public const String KeyFilename = "fileName";
  		private const String KeyKeyfile = "keyFile";
  		private const String KeyPassword = "password";
  		public const String KeyServerusername = "serverCredUser";
  		public const String KeyServerpassword = "serverCredPwd";
  		public const String KeyServercredmode = "serverCredRememberMode";
  
  		private const String ViewIntent = "android.intent.action.VIEW";
  		private const string ShowpasswordKey = "ShowPassword";
  		private const string KeyProviderIdOtp = "KP2A-OTP";
  		private const string KeyProviderIdOtpRecovery = "KP2A-OTPSecret";
  		private const string KeyProviderIdChallenge = "KP2A-Chal";
  		private const string KeyProviderIdChallengeRecovery = "KP2A-ChalSecret";
  
  		private const int RequestCodePrepareDbFile = 1000;
  		private const int RequestCodePrepareOtpAuxFile = 1001;
          private const int RequestCodeChallengeYubikey = 1002;
  
  
  		private Task<MemoryStream> _loadDbTask;
  		private IOConnectionInfo _ioConnection;
  		private String _keyFileOrProvider;
  		bool _showPassword;
  
  		internal AppTask AppTask;
  		private bool _killOnDestroy;
  		private string _password = "";
  		//OTPs which should be entered into the OTP fields as soon as these become visible
  		private List<String> _pendingOtps = new List<string>();
  
  
  
  		KeyProviders KeyProviderType
  		{
  			get
  			{
  				if (_keyFileOrProvider == null)
  					return KeyProviders.None;
  				if (_keyFileOrProvider == KeyProviderIdOtp)
  					return KeyProviders.Otp;
  				if (_keyFileOrProvider == KeyProviderIdOtpRecovery)
  					return KeyProviders.OtpRecovery;
  				if (_keyFileOrProvider == KeyProviderIdChallenge)
  					return KeyProviders.Challenge;
  				if (_keyFileOrProvider == KeyProviderIdChallengeRecovery)
  					return KeyProviders.ChalRecovery;
  				return KeyProviders.KeyFile;
  			}
  		}
  
  		private bool _rememberKeyfile;
  		ISharedPreferences _prefs;
  
  		private bool _starting;
  		private OtpInfo _otpInfo;
          private ChallengeInfo _chalInfo;
          private byte[] _challengeSecret;
  		private readonly int[] _otpTextViewIds = new[] {Resource.Id.otp1, Resource.Id.otp2, Resource.Id.otp3, Resource.Id.otp4, Resource.Id.otp5, Resource.Id.otp6};
  		private const string OtpInfoKey = "OtpInfoKey";
  		private const string EnteredOtpsKey = "EnteredOtpsKey";
  		private const string PendingOtpsKey = "PendingOtpsKey";
  		private const string PasswordKey = "PasswordKey";
  		private const string KeyFileOrProviderKey = "KeyFileOrProviderKey";
  
  
  		private ActivityDesign _design;
  		private bool _performingLoad;
  
  		public PasswordActivity (IntPtr javaReference, JniHandleOwnership transfer)
  			: base(javaReference, transfer)
  		{
  			_design = new ActivityDesign(this);
  		}
  
  		public PasswordActivity()
  		{
  			_design = new ActivityDesign(this);
  		}
  
  
  		public static void PutIoConnectionToIntent(IOConnectionInfo ioc, Intent i)
  		{
  			i.PutExtra(KeyFilename, ioc.Path);
  			i.PutExtra(KeyServerusername, ioc.UserName);
  			i.PutExtra(KeyServerpassword, ioc.Password);
  			i.PutExtra(KeyServercredmode, (int)ioc.CredSaveMode);
  		}
  		
  		public static void SetIoConnectionFromIntent(IOConnectionInfo ioc, Intent i)
  		{
  			ioc.Path = i.GetStringExtra(KeyFilename);
  			ioc.UserName = i.GetStringExtra(KeyServerusername) ?? "";
  			ioc.Password = i.GetStringExtra(KeyServerpassword) ?? "";
  			ioc.CredSaveMode  = (IOCredSaveMode)i.GetIntExtra(KeyServercredmode, (int) IOCredSaveMode.NoSave);
  		}
  
  		public static void Launch(Activity act, String fileName, AppTask appTask)  {
  			File dbFile = new File(fileName);
  			if ( ! dbFile.Exists() ) {
  				throw new FileNotFoundException();
  			}
  	
  			
  			Intent i = new Intent(act, typeof(PasswordActivity));
  			i.SetFlags(ActivityFlags.ForwardResult);
  			i.PutExtra(KeyFilename, fileName);
  			appTask.ToIntent(i);
  
  			act.StartActivity(i);
  			
  		}
  		
  
  		public static void Launch(Activity act, IOConnectionInfo ioc, AppTask appTask)
  		{
  			if (ioc.IsLocalFile())
  			{
  				Launch(act, ioc.Path, appTask);
  				return;
  			}
  
  			Intent i = new Intent(act, typeof(PasswordActivity));
  			
  			PutIoConnectionToIntent(ioc, i);
  			i.SetFlags(ActivityFlags.ForwardResult);
  
  			appTask.ToIntent(i);
  
  			act.StartActivity(i);
  			
  		}
  
  		public void LaunchNextActivity()
  		{
  			AppTask.AfterUnlockDatabase(this);
  		}
  
  		protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
  		{
  			base.OnActivityResult(requestCode, resultCode, data);
  
  			Kp2aLog.Log("PasswordActivity.OnActivityResult "+resultCode+"/"+requestCode);
  
  			AppTask.TryGetFromActivityResult(data, ref AppTask);
  
  			//NOTE: original code from k eepassdroid used switch ((Android.App.Result)requestCode) { (but doesn't work here, although k eepassdroid works)
  			switch(resultCode) {
  
  				case KeePass.ExitNormal: // Returned to this screen using the Back key
  					if (PreferenceManager.GetDefaultSharedPreferences(this)
  						                 .GetBoolean(GetString(Resource.String.LockWhenNavigateBack_key), false))
  					{
  						App.Kp2a.LockDatabase();	
  					}
  					//by leaving the app with the back button, the user probably wants to cancel the task
  					//The activity might be resumed (through Android's recent tasks list), then use a NullTask:
  					AppTask = new NullTask();
  					Finish();
  					break;
  				case KeePass.ExitLock:
  					// The database has already been locked, and the quick unlock screen will be shown if appropriate
  					break;
  				case KeePass.ExitCloseAfterTaskComplete:
  					// Do not lock the database
  					SetResult(KeePass.ExitCloseAfterTaskComplete);
  					Finish();
  					break;
  				case KeePass.ExitClose:
  					SetResult(KeePass.ExitClose);
  					Finish();
  					break;
  				case KeePass.ExitReloadDb:
  					//if the activity was killed, fill password/keyfile so the user can directly hit load again
  					if (App.Kp2a.GetDb().Loaded)
  					{
  						if (App.Kp2a.GetDb().KpDatabase.MasterKey.ContainsType(typeof(KcpPassword)))
  						{
  
  							KcpPassword kcpPassword = (KcpPassword)App.Kp2a.GetDb().KpDatabase.MasterKey.GetUserKey(typeof(KcpPassword));
  							String password = kcpPassword.Password.ReadString();
  
  							SetEditText(Resource.Id.password, password);
  						
  						}
  						if (App.Kp2a.GetDb().KpDatabase.MasterKey.ContainsType(typeof(KcpKeyFile)))
  						{
  							
  							KcpKeyFile kcpKeyfile = (KcpKeyFile)App.Kp2a.GetDb().KpDatabase.MasterKey.GetUserKey(typeof(KcpKeyFile));
  
  							SetEditText(Resource.Id.pass_keyfile, kcpKeyfile.Path);
  							
  						}
  					}
  					App.Kp2a.LockDatabase(false);
  					break;
  				case Result.Ok: // Key file browse dialog OK'ed.
  					if (requestCode == Intents.RequestCodeFileBrowseForKeyfile) {
  						string filename = Util.IntentToFilename(data, this);
  						if (filename != null) {
  							if (filename.StartsWith("file://")) {
  								filename = filename.Substring(7);
  							}
  							
  							filename = URLDecoder.Decode(filename);
  							
  							EditText fn = (EditText) FindViewById(Resource.Id.pass_keyfile);
  							fn.Text = filename;
  							
  						}
  					}
  					break;
  				case (Result)FileStorageResults.FileUsagePrepared:
  					if (requestCode == RequestCodePrepareDbFile)
  						PerformLoadDatabase();
  					if (requestCode == RequestCodePrepareOtpAuxFile)
  					{
  						if (_keyFileOrProvider == KeyProviderIdChallenge) 
                          {
                              LoadChalFile();
                              
  						} else {
  							LoadOtpFile ();
  						}
  					}
  					break;              
  			}
              if (requestCode == RequestCodeChallengeYubikey && resultCode == Result.Ok) 
  			{
  				try
  				{
  
gt: 					byte[] challengeResponse = data.GetByteArrayExtra("response");