2013-02-23 11:43:42 -05:00
/ *
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
2014-01-26 08:27:27 -05:00
the Free Software Foundation , either version 3 of the License , or
2013-02-23 11:43:42 -05:00
( 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 ;
2013-11-17 01:17:15 -05:00
using System.Collections.Generic ;
2013-11-20 13:14:57 -05:00
using System.IO ;
2013-07-17 14:19:17 -04:00
using System.Threading.Tasks ;
2013-11-20 13:14:57 -05:00
using System.Xml ;
using System.Xml.Serialization ;
2013-02-23 11:43:42 -05:00
using Android.App ;
using Android.Content ;
2013-11-17 01:17:15 -05:00
using Android.Database ;
2013-02-23 11:43:42 -05:00
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 ;
2013-11-20 13:14:57 -05:00
using KeePassLib.Utility ;
2013-11-17 01:17:15 -05:00
using OtpKeyProv ;
2013-10-07 00:28:06 -04:00
using keepass2android.Io ;
2013-11-17 01:17:15 -05:00
using keepass2android.Utils ;
using Exception = System . Exception ;
2013-11-20 13:14:57 -05:00
using File = Java . IO . File ;
using FileNotFoundException = Java . IO . FileNotFoundException ;
2013-07-17 14:19:17 -04:00
using MemoryStream = System . IO . MemoryStream ;
2013-11-17 01:17:15 -05:00
using Object = Java . Lang . Object ;
using Process = Android . OS . Process ;
using String = System . String ;
2014-03-25 15:51:41 -04:00
using KeeChallenge ;
2013-02-23 11:43:42 -05:00
namespace keepass2android
{
2014-01-28 01:47:08 -05:00
[ Activity ( Label = "@string/app_name" ,
ConfigurationChanges = ConfigChanges . Orientation | ConfigChanges . KeyboardHidden ,
LaunchMode = LaunchMode . SingleInstance ,
Theme = "@style/Base" ) ]
2013-02-23 11:43:42 -05:00
public class PasswordActivity : LockingActivity {
2013-11-17 01:17:15 -05:00
enum KeyProviders
{
//int values correspond to indices in passwordSpinner
None = 0 ,
KeyFile = 1 ,
2013-11-20 13:14:57 -05:00
Otp = 2 ,
2014-03-25 15:51:41 -04:00
OtpRecovery = 3 ,
2014-05-24 23:36:52 -04:00
Challenge = 4 ,
2014-03-25 15:51:41 -04:00
ChalRecovery = 5
2013-11-17 01:17:15 -05:00
}
2013-06-15 06:40:01 -04:00
public const String KeyDefaultFilename = "defaultFileName" ;
2013-02-23 11:43:42 -05:00
2013-06-15 06:40:01 -04:00
public const String KeyFilename = "fileName" ;
private const String KeyKeyfile = "keyFile" ;
2014-01-27 06:44:03 -05:00
private const String KeyPassword = "password" ;
2013-09-17 00:53:18 -04:00
public const String KeyServerusername = "serverCredUser" ;
public const String KeyServerpassword = "serverCredPwd" ;
public const String KeyServercredmode = "serverCredRememberMode" ;
2013-02-23 11:43:42 -05:00
2013-06-15 06:40:01 -04:00
private const String ViewIntent = "android.intent.action.VIEW" ;
2013-09-16 22:53:14 -04:00
private const string ShowpasswordKey = "ShowPassword" ;
2013-11-17 01:17:15 -05:00
private const string KeyProviderIdOtp = "KP2A-OTP" ;
2013-11-20 13:14:57 -05:00
private const string KeyProviderIdOtpRecovery = "KP2A-OTPSecret" ;
2014-03-25 15:51:41 -04:00
private const string KeyProviderIdChallenge = "KP2A-Chal" ;
private const string KeyProviderIdChallengeRecovery = "KP2A-ChalSecret" ;
2013-11-20 13:14:57 -05:00
private const int RequestCodePrepareDbFile = 1000 ;
private const int RequestCodePrepareOtpAuxFile = 1001 ;
2014-04-02 00:57:04 -04:00
private const int RequestCodeChallengeYubikey = 1002 ;
2013-11-20 13:14:57 -05:00
2013-07-25 08:47:05 -04:00
private Task < MemoryStream > _loadDbTask ;
2013-06-15 06:40:01 -04:00
private IOConnectionInfo _ioConnection ;
2013-11-17 01:17:15 -05:00
private String _keyFileOrProvider ;
2013-11-20 13:14:57 -05:00
bool _showPassword ;
2013-11-17 01:17:15 -05:00
internal AppTask AppTask ;
private bool _killOnDestroy ;
private string _password = "" ;
2013-11-17 11:01:53 -05:00
//OTPs which should be entered into the OTP fields as soon as these become visible
2013-11-20 13:14:57 -05:00
private List < String > _pendingOtps = new List < string > ( ) ;
2013-11-17 11:01:53 -05:00
2013-11-17 01:17:15 -05:00
KeyProviders KeyProviderType
{
get
{
if ( _keyFileOrProvider = = null )
return KeyProviders . None ;
if ( _keyFileOrProvider = = KeyProviderIdOtp )
return KeyProviders . Otp ;
2013-11-20 13:14:57 -05:00
if ( _keyFileOrProvider = = KeyProviderIdOtpRecovery )
return KeyProviders . OtpRecovery ;
2014-03-25 15:51:41 -04:00
if ( _keyFileOrProvider = = KeyProviderIdChallenge )
2014-05-24 23:36:52 -04:00
return KeyProviders . Challenge ;
2014-03-25 15:51:41 -04:00
if ( _keyFileOrProvider = = KeyProviderIdChallengeRecovery )
return KeyProviders . ChalRecovery ;
2013-11-17 01:17:15 -05:00
return KeyProviders . KeyFile ;
}
}
2013-06-15 06:40:01 -04:00
private bool _rememberKeyfile ;
ISharedPreferences _prefs ;
2013-02-23 11:43:42 -05:00
2013-08-10 14:49:59 -04:00
private bool _starting ;
2013-11-17 01:17:15 -05:00
private OtpInfo _otpInfo ;
2014-03-25 15:51:41 -04:00
private ChallengeInfo _chalInfo ;
2014-05-22 00:42:43 -04:00
private byte [ ] _challengeSecret ;
2013-11-20 13:14:57 -05:00
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" ;
2013-08-07 13:34:43 -04:00
2014-03-25 15:51:41 -04:00
2014-02-02 01:03:40 -05:00
private ActivityDesign _design ;
2014-03-31 01:24:02 -04:00
private bool _performingLoad ;
2014-02-02 01:03:40 -05:00
2013-02-23 11:43:42 -05:00
public PasswordActivity ( IntPtr javaReference , JniHandleOwnership transfer )
: base ( javaReference , transfer )
{
2014-02-02 01:03:40 -05:00
_design = new ActivityDesign ( this ) ;
2013-02-23 11:43:42 -05:00
}
public PasswordActivity ( )
{
2014-02-02 01:03:40 -05:00
_design = new ActivityDesign ( this ) ;
2013-02-23 11:43:42 -05:00
}
2013-09-17 00:53:18 -04:00
public static void PutIoConnectionToIntent ( IOConnectionInfo ioc , Intent i )
2013-02-23 11:43:42 -05:00
{
2013-06-15 06:40:01 -04:00
i . PutExtra ( KeyFilename , ioc . Path ) ;
i . PutExtra ( KeyServerusername , ioc . UserName ) ;
i . PutExtra ( KeyServerpassword , ioc . Password ) ;
i . PutExtra ( KeyServercredmode , ( int ) ioc . CredSaveMode ) ;
2013-02-23 11:43:42 -05:00
}
public static void SetIoConnectionFromIntent ( IOConnectionInfo ioc , Intent i )
{
2013-06-15 06:40:01 -04:00
ioc . Path = i . GetStringExtra ( KeyFilename ) ;
ioc . UserName = i . GetStringExtra ( KeyServerusername ) ? ? "" ;
ioc . Password = i . GetStringExtra ( KeyServerpassword ) ? ? "" ;
ioc . CredSaveMode = ( IOCredSaveMode ) i . GetIntExtra ( KeyServercredmode , ( int ) IOCredSaveMode . NoSave ) ;
2013-02-23 11:43:42 -05:00
}
2013-05-30 02:29:08 -04:00
public static void Launch ( Activity act , String fileName , AppTask appTask ) {
2013-06-15 06:40:01 -04:00
File dbFile = new File ( fileName ) ;
2013-02-23 11:43:42 -05:00
if ( ! dbFile . Exists ( ) ) {
2013-06-15 06:40:01 -04:00
throw new FileNotFoundException ( ) ;
2013-02-23 11:43:42 -05:00
}
Intent i = new Intent ( act , typeof ( PasswordActivity ) ) ;
2014-05-21 00:43:56 -04:00
i . SetFlags ( ActivityFlags . ForwardResult ) ;
2013-06-15 06:40:01 -04:00
i . PutExtra ( KeyFilename , fileName ) ;
2013-05-30 00:54:25 -04:00
appTask . ToIntent ( i ) ;
2013-11-17 11:01:53 -05:00
2014-05-21 00:43:56 -04:00
act . StartActivity ( i ) ;
2013-02-23 11:43:42 -05:00
}
2013-05-30 02:29:08 -04:00
public static void Launch ( Activity act , IOConnectionInfo ioc , AppTask appTask )
2013-02-23 11:43:42 -05:00
{
if ( ioc . IsLocalFile ( ) )
{
2013-05-30 00:54:25 -04:00
Launch ( act , ioc . Path , appTask ) ;
2013-02-23 11:43:42 -05:00
return ;
}
Intent i = new Intent ( act , typeof ( PasswordActivity ) ) ;
2013-11-17 11:01:53 -05:00
2013-02-23 11:43:42 -05:00
PutIoConnectionToIntent ( ioc , i ) ;
2014-05-21 00:43:56 -04:00
i . SetFlags ( ActivityFlags . ForwardResult ) ;
2013-02-23 11:43:42 -05:00
2013-05-30 00:54:25 -04:00
appTask . ToIntent ( i ) ;
2013-02-23 11:43:42 -05:00
2014-05-16 11:15:43 -04:00
act . StartActivity ( i ) ;
2013-02-23 11:43:42 -05:00
}
public void LaunchNextActivity ( )
{
2013-06-15 06:40:01 -04:00
AppTask . AfterUnlockDatabase ( this ) ;
2013-02-23 11:43:42 -05:00
}
protected override void OnActivityResult ( int requestCode , Result resultCode , Intent data )
{
base . OnActivityResult ( requestCode , resultCode , data ) ;
2013-07-06 10:11:38 -04:00
Kp2aLog . Log ( "PasswordActivity.OnActivityResult " + resultCode + "/" + requestCode ) ;
2013-02-23 11:43:42 -05:00
2014-05-16 11:15:43 -04:00
AppTask . TryGetFromActivityResult ( data , ref AppTask ) ;
2013-02-23 11:43:42 -05:00
//NOTE: original code from k eepassdroid used switch ((Android.App.Result)requestCode) { (but doesn't work here, although k eepassdroid works)
switch ( resultCode ) {
2013-08-07 13:34:43 -04:00
2014-05-16 11:15:43 -04:00
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 ( ) ;
2013-08-07 13:34:43 -04:00
break ;
2013-06-15 06:40:01 -04:00
case KeePass . ExitLock :
2013-08-03 14:58:01 -04:00
// The database has already been locked, and the quick unlock screen will be shown if appropriate
2013-02-23 11:43:42 -05:00
break ;
2013-06-15 06:40:01 -04:00
case KeePass . ExitCloseAfterTaskComplete :
2013-08-07 13:34:43 -04:00
// Do not lock the database
2013-06-15 06:40:01 -04:00
SetResult ( KeePass . ExitCloseAfterTaskComplete ) ;
2013-02-23 11:43:42 -05:00
Finish ( ) ;
break ;
2013-09-03 15:57:13 -04:00
case KeePass . ExitClose :
SetResult ( KeePass . ExitClose ) ;
Finish ( ) ;
break ;
2013-06-15 06:40:01 -04:00
case KeePass . ExitReloadDb :
2013-03-15 02:07:45 -04:00
//if the activity was killed, fill password/keyfile so the user can directly hit load again
2013-06-14 00:14:50 -04:00
if ( App . Kp2a . GetDb ( ) . Loaded )
2013-03-15 02:07:45 -04:00
{
2013-06-15 06:40:01 -04:00
if ( App . Kp2a . GetDb ( ) . KpDatabase . MasterKey . ContainsType ( typeof ( KcpPassword ) ) )
2013-03-15 02:07:45 -04:00
{
2013-06-15 06:40:01 -04:00
KcpPassword kcpPassword = ( KcpPassword ) App . Kp2a . GetDb ( ) . KpDatabase . MasterKey . GetUserKey ( typeof ( KcpPassword ) ) ;
2013-03-15 02:07:45 -04:00
String password = kcpPassword . Password . ReadString ( ) ;
2013-06-15 06:40:01 -04:00
SetEditText ( Resource . Id . password , password ) ;
2013-03-15 02:07:45 -04:00
}
2013-06-15 06:40:01 -04:00
if ( App . Kp2a . GetDb ( ) . KpDatabase . MasterKey . ContainsType ( typeof ( KcpKeyFile ) ) )
2013-03-15 02:07:45 -04:00
{
2013-06-15 06:40:01 -04:00
KcpKeyFile kcpKeyfile = ( KcpKeyFile ) App . Kp2a . GetDb ( ) . KpDatabase . MasterKey . GetUserKey ( typeof ( KcpKeyFile ) ) ;
2013-02-23 11:43:42 -05:00
2013-06-15 06:40:01 -04:00
SetEditText ( Resource . Id . pass_keyfile , kcpKeyfile . Path ) ;
2013-11-20 13:14:57 -05:00
2013-03-15 02:07:45 -04:00
}
}
2013-08-07 13:34:43 -04:00
App . Kp2a . LockDatabase ( false ) ;
2013-03-15 02:07:45 -04:00
break ;
2013-08-07 13:34:43 -04:00
case Result . Ok : // Key file browse dialog OK'ed.
2013-06-15 06:40:01 -04:00
if ( requestCode = = Intents . RequestCodeFileBrowseForKeyfile ) {
2013-09-28 01:46:44 -04:00
string filename = Util . IntentToFilename ( data , this ) ;
2013-02-23 11:43:42 -05:00
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 ;
2013-11-20 13:14:57 -05:00
2013-02-23 11:43:42 -05:00
}
}
break ;
2013-10-07 00:28:06 -04:00
case ( Result ) FileStorageResults . FileUsagePrepared :
2013-11-17 01:17:15 -05:00
if ( requestCode = = RequestCodePrepareDbFile )
PerformLoadDatabase ( ) ;
if ( requestCode = = RequestCodePrepareOtpAuxFile )
2014-03-25 15:51:41 -04:00
{
if ( _keyFileOrProvider = = KeyProviderIdChallenge )
{
2014-04-03 23:16:32 -04:00
LoadChalFile ( ) ;
2014-03-25 15:51:41 -04:00
} else {
LoadOtpFile ( ) ;
}
}
2014-04-02 00:57:04 -04:00
break ;
2013-02-23 11:43:42 -05:00
}
2014-05-22 00:42:43 -04:00
if ( requestCode = = RequestCodeChallengeYubikey & & resultCode = = Result . Ok )
{
byte [ ] challengeResponse = data . GetByteArrayExtra ( "response" ) ;
_challengeSecret = KeeChallengeProv . GetSecret ( _chalInfo , challengeResponse ) ;
UpdateOkButtonState ( ) ;
2014-05-24 23:36:52 -04:00
FindViewById ( Resource . Id . otpInitView ) . Visibility = ViewStates . Gone ;
2014-05-22 00:42:43 -04:00
if ( _challengeSecret ! = null )
{
new LoadingDialog < object , object , object > ( this , true ,
//doInBackground
delegate
{
//save aux file
try
{
ChallengeInfo temp = KeeChallengeProv . Encrypt ( _challengeSecret ) ;
IFileStorage fileStorage = App . Kp2a . GetOtpAuxFileStorage ( _ioConnection ) ;
IOConnectionInfo iocAux = fileStorage . GetFilePath ( fileStorage . GetParentPath ( _ioConnection ) ,
fileStorage . GetFilenameWithoutPathAndExt ( _ioConnection ) + ".xml" ) ;
if ( ! temp . Save ( iocAux ) )
{
Toast . MakeText ( this , Resource . String . ErrorUpdatingChalAuxFile , ToastLength . Long ) . Show ( ) ;
return false ;
}
Array . Clear ( challengeResponse , 0 , challengeResponse . Length ) ;
}
catch ( Exception e )
{
Kp2aLog . Log ( e . ToString ( ) ) ;
}
return null ;
}
, delegate
{
} ) . Execute ( ) ;
}
else
{
Toast . MakeText ( this , Resource . String . bad_resp , ToastLength . Long ) . Show ( ) ;
return ;
}
2013-02-23 11:43:42 -05:00
}
}
2014-05-16 11:15:43 -04:00
2013-11-17 01:17:15 -05:00
private void LoadOtpFile ( )
{
new LoadingDialog < object , object , object > ( this , true ,
//doInBackground
delegate
{
2013-11-22 15:47:13 -05:00
try
{
_otpInfo = OathHotpKeyProv . LoadOtpInfo ( new KeyProviderQueryContext ( _ioConnection , false , false ) ) ;
}
catch ( Exception e )
{
Kp2aLog . Log ( e . ToString ( ) ) ;
}
2013-11-17 01:17:15 -05:00
return null ;
} ,
//onPostExecute
delegate
{
if ( _otpInfo = = null )
{
2013-11-30 08:44:03 -05:00
Toast . MakeText ( this ,
GetString ( Resource . String . CouldntLoadOtpAuxFile ) + " " + GetString ( Resource . String . CouldntLoadOtpAuxFile_Hint )
, ToastLength . Long ) . Show ( ) ;
2013-11-17 01:17:15 -05:00
return ;
}
2013-11-17 11:01:53 -05:00
2013-11-20 13:14:57 -05:00
IList < string > prefilledOtps = _pendingOtps ;
ShowOtpEntry ( prefilledOtps ) ;
2013-11-17 11:01:53 -05:00
_pendingOtps . Clear ( ) ;
2013-11-17 01:17:15 -05:00
}
) . Execute ( ) ;
}
2013-11-11 00:38:15 -05:00
2014-04-03 23:16:32 -04:00
private void LoadChalFile ( )
2014-03-25 15:51:41 -04:00
{
2014-04-03 23:16:32 -04:00
new LoadingDialog < object , object , object > ( this , true ,
//doInBackground
delegate
{
try
{
IFileStorage fileStorage =
App . Kp2a . GetOtpAuxFileStorage ( _ioConnection ) ;
IOConnectionInfo iocAux =
fileStorage . GetFilePath (
fileStorage . GetParentPath ( _ioConnection ) ,
fileStorage . GetFilenameWithoutPathAndExt ( _ioConnection ) +
".xml" ) ;
_chalInfo = ChallengeInfo . Load ( iocAux ) ;
}
catch ( Exception e )
{
Kp2aLog . Log ( e . ToString ( ) ) ;
}
return null ;
}
, delegate
{
if ( _chalInfo = = null )
{
Toast . MakeText ( this ,
GetString ( Resource . String . CouldntLoadChalAuxFile ) +
" " +
GetString (
Resource . String . CouldntLoadChalAuxFile_Hint )
, ToastLength . Long ) . Show ( ) ;
return ;
}
Intent chalIntent = new Intent ( "com.yubichallenge.NFCActivity.CHALLENGE" ) ;
chalIntent . PutExtra ( "challenge" , _chalInfo . Challenge ) ;
chalIntent . PutExtra ( "slot" , 2 ) ;
IList < ResolveInfo > activities = PackageManager . QueryIntentActivities ( chalIntent , 0 ) ;
bool isIntentSafe = activities . Count > 0 ;
if ( isIntentSafe )
{
StartActivityForResult ( chalIntent , RequestCodeChallengeYubikey ) ;
}
else
{
2014-05-24 23:36:52 -04:00
AlertDialog . Builder b = new AlertDialog . Builder ( this ) ;
b . SetMessage ( Resource . String . YubiChallengeNotInstalled ) ;
b . SetPositiveButton ( Resource . String . ok , delegate {
Util . GotoUrl ( this , GetString ( Resource . String . MarketURL ) + "com.yubichallenge" ) ;
} ) ;
b . SetNegativeButton ( Resource . String . cancel , delegate { } ) ;
b . Create ( ) . Show ( ) ;
2014-04-03 23:16:32 -04:00
}
} ) . Execute ( ) ;
2014-03-25 15:51:41 -04:00
}
2013-11-20 13:14:57 -05:00
private void ShowOtpEntry ( IList < string > prefilledOtps )
{
FindViewById ( Resource . Id . otpInitView ) . Visibility = ViewStates . Gone ;
FindViewById ( Resource . Id . otpEntry ) . Visibility = ViewStates . Visible ;
int c = 0 ;
foreach ( int otpId in _otpTextViewIds )
{
c + + ;
var otpTextView = FindViewById < EditText > ( otpId ) ;
if ( c < = prefilledOtps . Count )
{
otpTextView . Text = prefilledOtps [ c - 1 ] ;
}
else
{
otpTextView . Text = "" ;
}
otpTextView . Hint = GetString ( Resource . String . otp_hint , new Object [ ] { c } ) ;
otpTextView . SetFilters ( new IInputFilter [ ] { new InputFilterLengthFilter ( ( int ) _otpInfo . OtpLength ) } ) ;
if ( c > _otpInfo . OtpsRequired )
{
otpTextView . Visibility = ViewStates . Gone ;
}
else
{
otpTextView . TextChanged + = ( sender , args ) = > { UpdateOkButtonState ( ) ; } ;
}
}
}
2013-02-23 11:43:42 -05:00
protected override void OnCreate ( Bundle savedInstanceState )
{
base . OnCreate ( savedInstanceState ) ;
2014-02-02 01:03:40 -05:00
_design . ApplyTheme ( ) ;
2013-11-17 11:01:53 -05:00
2013-02-23 11:43:42 -05:00
Intent i = Intent ;
2013-11-20 13:14:57 -05:00
2014-05-16 11:15:43 -04:00
//only load the AppTask if this is the "first" OnCreate (not because of kill/resume, i.e. savedInstanceState==null)
// and if the activity is not launched from history (i.e. recent tasks) because this would mean that
// the Activity was closed already (user cancelling the task or task complete) but is restarted due recent tasks.
// Don't re-start the task (especially bad if tak was complete already)
2014-05-21 00:43:56 -04:00
if ( Intent . Flags . HasFlag ( ActivityFlags . LaunchedFromHistory ) )
2014-05-16 11:15:43 -04:00
{
AppTask = new NullTask ( ) ;
}
else
{
AppTask = AppTask . GetTaskInOnCreate ( savedInstanceState , Intent ) ;
}
2013-11-20 13:14:57 -05:00
2013-02-23 11:43:42 -05:00
String action = i . Action ;
2013-06-15 06:40:01 -04:00
_prefs = PreferenceManager . GetDefaultSharedPreferences ( this ) ;
_rememberKeyfile = _prefs . GetBoolean ( GetString ( Resource . String . keyfile_key ) , Resources . GetBoolean ( Resource . Boolean . keyfile_default ) ) ;
2013-02-23 11:43:42 -05:00
2013-06-15 06:40:01 -04:00
_ioConnection = new IOConnectionInfo ( ) ;
2013-02-23 11:43:42 -05:00
2013-03-20 20:01:02 -04:00
2013-06-15 06:40:01 -04:00
if ( action ! = null & & action . Equals ( ViewIntent ) )
2013-02-23 11:43:42 -05:00
{
2013-11-20 13:14:57 -05:00
if ( ! GetIocFromViewIntent ( i ) ) return ;
2013-11-17 11:01:53 -05:00
}
else if ( ( action ! = null ) & & ( action . Equals ( Intents . StartWithOtp ) ) )
{
2013-11-20 13:14:57 -05:00
if ( ! GetIocFromOtpIntent ( savedInstanceState , i ) ) return ;
2013-11-17 11:01:53 -05:00
}
else
2013-02-23 11:43:42 -05:00
{
2013-06-15 06:40:01 -04:00
SetIoConnectionFromIntent ( _ioConnection , i ) ;
2013-11-17 01:17:15 -05:00
_keyFileOrProvider = i . GetStringExtra ( KeyKeyfile ) ;
2014-01-27 06:44:03 -05:00
_password = i . GetStringExtra ( KeyPassword ) ? ? "" ;
2013-11-17 01:17:15 -05:00
if ( string . IsNullOrEmpty ( _keyFileOrProvider ) )
2013-02-23 11:43:42 -05:00
{
2013-11-17 01:17:15 -05:00
_keyFileOrProvider = GetKeyFile ( _ioConnection . Path ) ;
2013-02-23 11:43:42 -05:00
}
}
2013-08-07 13:34:43 -04:00
if ( App . Kp2a . GetDb ( ) . Loaded & & App . Kp2a . GetDb ( ) . Ioc ! = null & &
App . Kp2a . GetDb ( ) . Ioc . GetDisplayName ( ) ! = _ioConnection . GetDisplayName ( ) )
{
// A different database is currently loaded, unload it before loading the new one requested
App . Kp2a . LockDatabase ( false ) ;
}
2013-11-17 11:01:53 -05:00
2013-02-23 11:43:42 -05:00
SetContentView ( Resource . Layout . password ) ;
2013-11-20 13:14:57 -05:00
InitializeFilenameView ( ) ;
2013-06-01 13:24:42 -04:00
2013-11-20 13:14:57 -05:00
if ( KeyProviderType = = KeyProviders . KeyFile )
SetEditText ( Resource . Id . pass_keyfile , _keyFileOrProvider ) ;
2013-06-01 13:24:42 -04:00
2013-11-17 01:17:15 -05:00
FindViewById < EditText > ( Resource . Id . pass_keyfile ) . TextChanged + =
( sender , args ) = >
2013-11-20 13:14:57 -05:00
{
_keyFileOrProvider = FindViewById < EditText > ( Resource . Id . pass_keyfile ) . Text ;
UpdateOkButtonState ( ) ;
} ;
2013-11-17 01:17:15 -05:00
FindViewById < EditText > ( Resource . Id . password ) . TextChanged + =
( sender , args ) = >
{
_password = FindViewById < EditText > ( Resource . Id . password ) . Text ;
UpdateOkButtonState ( ) ;
} ;
2013-06-01 13:24:42 -04:00
2013-11-20 13:14:57 -05:00
FindViewById < EditText > ( Resource . Id . pass_otpsecret ) . TextChanged + = ( sender , args ) = > UpdateOkButtonState ( ) ;
EditText passwordEdit = FindViewById < EditText > ( Resource . Id . password ) ;
2014-01-27 06:44:03 -05:00
passwordEdit . Text = _password ;
2013-06-01 13:24:42 -04:00
passwordEdit . RequestFocus ( ) ;
Window . SetSoftInputMode ( SoftInput . StateVisible ) ;
2013-11-20 13:14:57 -05:00
InitializeOkButton ( ) ;
InitializePasswordModeSpinner ( ) ;
InitializeOtpSecretSpinner ( ) ;
UpdateOkButtonState ( ) ;
InitializeTogglePasswordButton ( ) ;
InitializeKeyfileBrowseButton ( ) ;
InitializeQuickUnlockCheckbox ( ) ;
RestoreState ( savedInstanceState ) ;
2014-01-27 06:44:03 -05:00
if ( i . GetBooleanExtra ( "launchImmediately" , false ) )
{
App . Kp2a . GetFileStorage ( _ioConnection )
. PrepareFileUsage ( new FileStorageSetupInitiatorActivity ( this , OnActivityResult , null ) , _ioConnection ,
RequestCodePrepareDbFile , false ) ;
}
2013-11-20 13:14:57 -05:00
}
private void InitializeOtpSecretSpinner ( )
{
Spinner spinner = FindViewById < Spinner > ( Resource . Id . otpsecret_format_spinner ) ;
ArrayAdapter < String > spinnerArrayAdapter = new ArrayAdapter < String > ( this , Android . Resource . Layout . SimpleSpinnerDropDownItem , EncodingUtil . Formats ) ;
spinner . Adapter = spinnerArrayAdapter ;
}
private bool GetIocFromOtpIntent ( Bundle savedInstanceState , Intent i )
{
//create called after detecting an OTP via NFC
//this means the Activity was not on the back stack before, i.e. no database has been selected
_ioConnection = null ;
//see if we can get a database from recent:
if ( App . Kp2a . FileDbHelper . HasRecentFiles ( ) )
{
ICursor filesCursor = App . Kp2a . FileDbHelper . FetchAllFiles ( ) ;
StartManagingCursor ( filesCursor ) ;
filesCursor . MoveToFirst ( ) ;
IOConnectionInfo ioc = App . Kp2a . FileDbHelper . CursorToIoc ( filesCursor ) ;
if ( App . Kp2a . GetFileStorage ( ioc ) . RequiresSetup ( ioc ) = = false )
{
IFileStorage fileStorage = App . Kp2a . GetFileStorage ( ioc ) ;
if ( ! fileStorage . RequiresCredentials ( ioc ) )
{
//ok, we can use this file
_ioConnection = ioc ;
}
}
}
if ( _ioConnection = = null )
{
//We need to go to FileSelectActivity first.
//For security reasons: discard the OTP (otherwise the user might not select a database now and forget
//about the OTP, but it would still be stored in the Intents and later be passed to PasswordActivity again.
Toast . MakeText ( this , GetString ( Resource . String . otp_discarded_because_no_db ) , ToastLength . Long ) . Show ( ) ;
GoToFileSelectActivity ( ) ;
return false ;
}
//user obviously wants to use OTP:
_keyFileOrProvider = KeyProviderIdOtp ;
if ( savedInstanceState = = null ) //only when not re-creating
{
//remember the OTP for later use
_pendingOtps . Add ( i . GetStringExtra ( Intents . OtpExtraKey ) ) ;
i . RemoveExtra ( Intents . OtpExtraKey ) ;
}
return true ;
}
private bool GetIocFromViewIntent ( Intent i )
{
//started from "view" intent (e.g. from file browser)
_ioConnection . Path = i . DataString ;
if ( ! _ioConnection . Path . Substring ( 0 , 7 ) . Equals ( "file://" ) )
{
//TODO: this might no longer be required as we can handle http(s) and ftp as well (but we need server credentials therefore)
Toast . MakeText ( this , Resource . String . error_can_not_handle_uri , ToastLength . Long ) . Show ( ) ;
Finish ( ) ;
return false ;
}
_ioConnection . Path = URLDecoder . Decode ( _ioConnection . Path . Substring ( 7 ) ) ;
if ( _ioConnection . Path . Length = = 0 )
{
// No file name
Toast . MakeText ( this , Resource . String . FileNotFound , ToastLength . Long ) . Show ( ) ;
Finish ( ) ;
return false ;
}
File dbFile = new File ( _ioConnection . Path ) ;
if ( ! dbFile . Exists ( ) )
{
// File does not exist
Toast . MakeText ( this , Resource . String . FileNotFound , ToastLength . Long ) . Show ( ) ;
Finish ( ) ;
return false ;
}
_keyFileOrProvider = GetKeyFile ( _ioConnection . Path ) ;
return true ;
}
private void InitializeOkButton ( )
{
Button confirmButton = ( Button ) FindViewById ( Resource . Id . pass_ok ) ;
2013-10-07 00:28:06 -04:00
confirmButton . Click + = ( sender , e ) = >
2013-02-23 11:43:42 -05:00
{
2013-10-07 00:28:06 -04:00
App . Kp2a . GetFileStorage ( _ioConnection )
2013-11-20 13:14:57 -05:00
. PrepareFileUsage ( new FileStorageSetupInitiatorActivity ( this , OnActivityResult , null ) , _ioConnection ,
RequestCodePrepareDbFile , false ) ;
} ;
}
private void InitializeTogglePasswordButton ( )
{
ImageButton btnTogglePassword = ( ImageButton ) FindViewById ( Resource . Id . toggle_password ) ;
btnTogglePassword . Click + = ( sender , e ) = >
{
_showPassword = ! _showPassword ;
MakePasswordMaskedOrVisible ( ) ;
2013-10-07 00:28:06 -04:00
} ;
2013-11-20 13:14:57 -05:00
}
2013-11-11 00:38:15 -05:00
2013-11-20 13:14:57 -05:00
private void InitializeKeyfileBrowseButton ( )
{
ImageButton browse = ( ImageButton ) FindViewById ( Resource . Id . browse_button ) ;
browse . Click + = ( sender , evt ) = >
{
string filename = null ;
if ( ! String . IsNullOrEmpty ( _ioConnection . Path ) )
{
File keyfile = new File ( _ioConnection . Path ) ;
File parent = keyfile . ParentFile ;
if ( parent ! = null )
{
filename = parent . AbsolutePath ;
}
}
Util . ShowBrowseDialog ( filename , this , Intents . RequestCodeFileBrowseForKeyfile , false ) ;
} ;
}
private void InitializePasswordModeSpinner ( )
{
2013-11-17 01:17:15 -05:00
Spinner passwordModeSpinner = FindViewById < Spinner > ( Resource . Id . password_mode_spinner ) ;
if ( passwordModeSpinner ! = null )
{
UpdateKeyProviderUiState ( ) ;
passwordModeSpinner . SetSelection ( ( int ) KeyProviderType ) ;
passwordModeSpinner . ItemSelected + = ( sender , args ) = >
{
switch ( args . Position )
{
case 0 :
_keyFileOrProvider = null ;
break ;
case 1 :
2013-11-25 15:06:37 -05:00
//don't set to "" to prevent losing the filename. (ItemSelected is also called during recreation!)
_keyFileOrProvider = FindViewById < EditText > ( Resource . Id . pass_keyfile ) . Text ;
2013-11-17 01:17:15 -05:00
break ;
case 2 :
_keyFileOrProvider = KeyProviderIdOtp ;
break ;
2013-11-20 13:14:57 -05:00
case 3 :
_keyFileOrProvider = KeyProviderIdOtpRecovery ;
break ;
2014-03-25 15:51:41 -04:00
case 4 :
_keyFileOrProvider = KeyProviderIdChallenge ;
break ;
case 5 :
_keyFileOrProvider = KeyProviderIdChallengeRecovery ;
break ;
2013-11-17 01:17:15 -05:00
default :
2013-11-20 13:14:57 -05:00
throw new Exception ( "Unexpected position " + args . Position + " / " +
( ( ICursor ) ( ( AdapterView ) sender ) . GetItemAtPosition ( args . Position ) ) . GetString ( 1 ) ) ;
2013-11-17 01:17:15 -05:00
}
UpdateKeyProviderUiState ( ) ;
} ;
FindViewById ( Resource . Id . init_otp ) . Click + = ( sender , args ) = >
{
2014-03-25 15:51:41 -04:00
App . Kp2a . GetOtpAuxFileStorage ( _ioConnection )
. PrepareFileUsage ( new FileStorageSetupInitiatorActivity ( this , OnActivityResult , null ) , _ioConnection ,
RequestCodePrepareOtpAuxFile , false ) ;
2013-11-17 01:17:15 -05:00
} ;
}
else
{
//android 2.x
//TODO test
}
2013-11-20 13:14:57 -05:00
}
2013-11-17 01:17:15 -05:00
2013-11-20 13:14:57 -05:00
private void RestoreState ( Bundle savedInstanceState )
{
if ( savedInstanceState ! = null )
2013-02-23 11:43:42 -05:00
{
2013-11-20 13:14:57 -05:00
_showPassword = savedInstanceState . GetBoolean ( ShowpasswordKey , false ) ;
MakePasswordMaskedOrVisible ( ) ;
_keyFileOrProvider = FindViewById < EditText > ( Resource . Id . pass_keyfile ) . Text = savedInstanceState . GetString ( KeyFileOrProviderKey ) ;
_password = FindViewById < EditText > ( Resource . Id . password ) . Text = savedInstanceState . GetString ( PasswordKey ) ;
_pendingOtps = new List < string > ( savedInstanceState . GetStringArrayList ( PendingOtpsKey ) ) ;
string otpInfoString = savedInstanceState . GetString ( OtpInfoKey ) ;
if ( otpInfoString ! = null )
2013-02-23 11:43:42 -05:00
{
2013-11-20 13:14:57 -05:00
XmlSerializer xs = new XmlSerializer ( typeof ( OtpInfo ) ) ;
_otpInfo = ( OtpInfo ) xs . Deserialize ( new StringReader ( otpInfoString ) ) ;
2013-04-26 06:43:06 -04:00
2013-11-20 13:14:57 -05:00
var enteredOtps = savedInstanceState . GetStringArrayList ( EnteredOtpsKey ) ;
ShowOtpEntry ( enteredOtps ) ;
}
UpdateKeyProviderUiState ( ) ;
}
2013-07-25 08:47:05 -04:00
}
2013-11-17 01:17:15 -05:00
private void UpdateOkButtonState ( )
2013-10-07 00:28:06 -04:00
{
2013-11-17 01:17:15 -05:00
switch ( KeyProviderType )
2013-10-07 00:28:06 -04:00
{
2013-11-17 01:17:15 -05:00
case KeyProviders . None :
FindViewById ( Resource . Id . pass_ok ) . Enabled = true ;
break ;
case KeyProviders . KeyFile :
FindViewById ( Resource . Id . pass_ok ) . Enabled = _keyFileOrProvider ! = "" | | _password ! = "" ;
break ;
case KeyProviders . Otp :
bool enabled = true ;
if ( _otpInfo = = null )
enabled = false ;
else
{
int c = 0 ;
foreach ( int otpId in _otpTextViewIds )
{
c + + ;
var otpTextView = FindViewById < EditText > ( otpId ) ;
if ( ( c < = _otpInfo . OtpsRequired ) & & ( otpTextView . Text = = "" ) )
{
enabled = false ;
break ;
}
}
}
FindViewById ( Resource . Id . pass_ok ) . Enabled = enabled ;
break ;
2013-11-20 13:14:57 -05:00
case KeyProviders . OtpRecovery :
2014-05-24 23:36:52 -04:00
case KeyProviders . ChalRecovery :
FindViewById ( Resource . Id . pass_ok ) . Enabled = FindViewById < EditText > ( Resource . Id . pass_otpsecret ) . Text ! = "" ;
2013-11-20 13:14:57 -05:00
break ;
2014-05-24 23:36:52 -04:00
case KeyProviders . Challenge :
2014-05-22 00:42:43 -04:00
FindViewById ( Resource . Id . pass_ok ) . Enabled = _challengeSecret ! = null ;
2014-03-25 15:51:41 -04:00
break ;
2013-11-17 01:17:15 -05:00
default :
throw new ArgumentOutOfRangeException ( ) ;
}
}
private void UpdateKeyProviderUiState ( )
{
FindViewById ( Resource . Id . keyfileLine ) . Visibility = KeyProviderType = = KeyProviders . KeyFile
? ViewStates . Visible
: ViewStates . Gone ;
FindViewById ( Resource . Id . otpView ) . Visibility = KeyProviderType = = KeyProviders . Otp
? ViewStates . Visible
: ViewStates . Gone ;
2013-11-20 13:14:57 -05:00
2014-05-24 23:36:52 -04:00
FindViewById ( Resource . Id . otpSecretLine ) . Visibility = ( KeyProviderType = = KeyProviders . OtpRecovery | | KeyProviderType = = KeyProviders . ChalRecovery )
2013-11-20 13:14:57 -05:00
? ViewStates . Visible
: ViewStates . Gone ;
2013-11-17 11:01:53 -05:00
if ( KeyProviderType = = KeyProviders . Otp )
{
FindViewById ( Resource . Id . otps_pending ) . Visibility = _pendingOtps . Count > 0 ? ViewStates . Visible : ViewStates . Gone ;
}
2014-03-25 15:51:41 -04:00
2014-05-24 23:36:52 -04:00
if ( KeyProviderType = = KeyProviders . Challenge )
2014-03-25 15:51:41 -04:00
{
FindViewById ( Resource . Id . otpView ) . Visibility = ViewStates . Visible ;
2014-05-22 00:42:43 -04:00
FindViewById ( Resource . Id . otps_pending ) . Visibility = ViewStates . Gone ;
2014-03-25 15:51:41 -04:00
}
2013-11-17 01:17:15 -05:00
UpdateOkButtonState ( ) ;
}
private void PerformLoadDatabase ( )
{
2014-03-31 01:24:02 -04:00
2013-11-17 01:17:15 -05:00
//no need to check for validity of password because if this method is called, the Ok button was enabled (i.e. there was a valid password)
CompositeKey compositeKey = new CompositeKey ( ) ;
compositeKey . AddUserKey ( new KcpPassword ( _password ) ) ;
2013-11-25 15:06:37 -05:00
if ( ( KeyProviderType = = KeyProviders . KeyFile ) & & ( _keyFileOrProvider ! = "" ) )
2013-11-17 01:17:15 -05:00
{
try
{
compositeKey . AddUserKey ( new KcpKeyFile ( _keyFileOrProvider ) ) ;
}
catch ( Exception e )
{
Kp2aLog . Log ( e . ToString ( ) ) ;
2013-11-20 13:14:57 -05:00
Toast . MakeText ( this , App . Kp2a . GetResourceString ( UiStringKey . keyfile_does_not_exist ) , ToastLength . Long ) . Show ( ) ;
return ;
2013-11-17 01:17:15 -05:00
}
}
else if ( KeyProviderType = = KeyProviders . Otp )
{
try
{
2013-11-22 15:47:13 -05:00
var lOtps = GetOtpsFromUi ( ) ;
OathHotpKeyProv . CreateOtpSecret ( lOtps , _otpInfo ) ;
2013-11-17 01:17:15 -05:00
}
catch ( Exception )
{
2013-11-20 13:14:57 -05:00
Toast . MakeText ( this , GetString ( Resource . String . OtpKeyError ) , ToastLength . Long ) . Show ( ) ;
2013-11-17 01:17:15 -05:00
return ;
}
compositeKey . AddUserKey ( new KcpCustomKey ( OathHotpKeyProv . Name , _otpInfo . Secret , true ) ) ;
2013-10-07 00:28:06 -04:00
}
2014-05-24 23:36:52 -04:00
else if ( ( KeyProviderType = = KeyProviders . OtpRecovery ) | | ( KeyProviderType = = KeyProviders . ChalRecovery ) )
2013-11-20 13:14:57 -05:00
{
Spinner stpDataFmtSpinner = FindViewById < Spinner > ( Resource . Id . otpsecret_format_spinner ) ;
EditText secretEdit = FindViewById < EditText > ( Resource . Id . pass_otpsecret ) ;
byte [ ] pbSecret = EncodingUtil . ParseKey ( secretEdit . Text , ( OtpDataFmt ) stpDataFmtSpinner . SelectedItemPosition ) ;
if ( pbSecret ! = null )
{
compositeKey . AddUserKey ( new KcpCustomKey ( OathHotpKeyProv . Name , pbSecret , true ) ) ;
}
else
{
Toast . MakeText ( this , Resource . String . CouldntParseOtpSecret , ToastLength . Long ) . Show ( ) ;
return ;
}
}
2014-05-24 23:36:52 -04:00
else if ( KeyProviderType = = KeyProviders . Challenge )
2014-03-25 15:51:41 -04:00
{
2014-05-22 00:42:43 -04:00
compositeKey . AddUserKey ( new KcpCustomKey ( KeeChallengeProv . Name , _challengeSecret , true ) ) ;
2014-03-25 15:51:41 -04:00
}
2013-10-07 00:28:06 -04:00
CheckBox cbQuickUnlock = ( CheckBox ) FindViewById ( Resource . Id . enable_quickunlock ) ;
App . Kp2a . SetQuickUnlockEnabled ( cbQuickUnlock . Checked ) ;
//avoid password being visible while loading:
_showPassword = false ;
MakePasswordMaskedOrVisible ( ) ;
Handler handler = new Handler ( ) ;
2013-11-22 15:47:13 -05:00
OnFinish onFinish = new AfterLoad ( handler , this ) ;
2014-03-31 01:24:02 -04:00
_performingLoad = true ;
2013-11-22 15:47:13 -05:00
LoadDb task = ( KeyProviderType = = KeyProviders . Otp ) ?
new SaveOtpAuxFileAndLoadDb ( App . Kp2a , _ioConnection , _loadDbTask , compositeKey , _keyFileOrProvider , onFinish , this )
:
new LoadDb ( App . Kp2a , _ioConnection , _loadDbTask , compositeKey , _keyFileOrProvider , onFinish ) ;
2013-10-07 00:28:06 -04:00
_loadDbTask = null ; // prevent accidental re-use
SetNewDefaultFile ( ) ;
new ProgressTask ( App . Kp2a , this , task ) . Run ( ) ;
}
2013-11-22 15:47:13 -05:00
private List < string > GetOtpsFromUi ( )
2013-11-20 13:14:57 -05:00
{
List < string > lOtps = new List < string > ( ) ;
foreach ( int otpId in _otpTextViewIds )
{
string otpText = FindViewById < EditText > ( otpId ) . Text ;
if ( ! String . IsNullOrEmpty ( otpText ) )
lOtps . Add ( otpText ) ;
}
return lOtps ;
}
2013-11-17 01:17:15 -05:00
2013-08-31 07:58:00 -04:00
private void MakePasswordMaskedOrVisible ( )
{
TextView password = ( TextView ) FindViewById ( Resource . Id . password ) ;
if ( _showPassword )
{
password . InputType = InputTypes . ClassText | InputTypes . TextVariationVisiblePassword ;
}
else
{
password . InputType = InputTypes . ClassText | InputTypes . TextVariationPassword ;
}
}
2013-08-09 16:31:30 -04:00
private void SetNewDefaultFile ( )
{
//Don't allow the current file to be the default if we don't have stored credentials
bool makeFileDefault ;
if ( ( _ioConnection . IsLocalFile ( ) = = false ) & & ( _ioConnection . CredSaveMode ! = IOCredSaveMode . SaveCred ) )
{
makeFileDefault = false ;
}
else
{
makeFileDefault = true ;
}
String newDefaultFileName ;
if ( makeFileDefault )
{
newDefaultFileName = _ioConnection . Path ;
}
else
{
newDefaultFileName = "" ;
}
ISharedPreferencesEditor editor = _prefs . Edit ( ) ;
editor . PutString ( KeyDefaultFilename , newDefaultFileName ) ;
EditorCompat . Apply ( editor ) ;
}
2013-07-25 08:47:05 -04:00
protected override void OnStart ( )
{
base . OnStart ( ) ;
2013-08-10 14:49:59 -04:00
_starting = true ;
2013-09-06 16:56:29 -04:00
ISharedPreferences prefs = PreferenceManager . GetDefaultSharedPreferences ( this ) ;
long usageCount = prefs . GetLong ( GetString ( Resource . String . UsageCount_key ) , 0 ) ;
2014-04-01 01:26:10 -04:00
if ( usageCount > 5 )
2013-09-06 16:56:29 -04:00
{
2014-04-01 01:26:10 -04:00
DonateReminder . ShowDonateReminderIfAppropriate ( this ) ;
2013-09-06 16:56:29 -04:00
}
2013-07-17 14:19:17 -04:00
}
2014-01-25 22:38:12 -05:00
private MemoryStream PreloadDbFile ( )
2013-07-17 14:19:17 -04:00
{
2013-08-15 13:49:27 -04:00
if ( KdbpFile . GetFormatToUse ( _ioConnection ) = = KdbxFormat . ProtocolBuffers )
{
Kp2aLog . Log ( "Preparing kdbp serializer" ) ;
KdbpFile . PrepareSerializer ( ) ;
}
2013-07-25 08:47:05 -04:00
Kp2aLog . Log ( "Pre-loading database file starting" ) ;
2013-07-17 14:19:17 -04:00
var fileStorage = App . Kp2a . GetFileStorage ( _ioConnection ) ;
var stream = fileStorage . OpenFileForRead ( _ioConnection ) ;
var memoryStream = stream as MemoryStream ;
if ( memoryStream = = null )
{
// Read the file into memory
int capacity = 4096 ; // Default initial capacity, if stream can't report it.
if ( stream . CanSeek )
{
capacity = ( int ) stream . Length ;
}
memoryStream = new MemoryStream ( capacity ) ;
2013-08-04 04:38:15 -04:00
stream . CopyTo ( memoryStream ) ;
2013-07-17 14:19:17 -04:00
stream . Close ( ) ;
memoryStream . Seek ( 0 , System . IO . SeekOrigin . Begin ) ;
}
2013-07-25 08:47:05 -04:00
Kp2aLog . Log ( "Pre-loading database file completed" ) ;
2013-02-23 11:43:42 -05:00
2013-07-17 14:19:17 -04:00
return memoryStream ;
2013-02-23 11:43:42 -05:00
}
2013-05-30 00:54:25 -04:00
protected override void OnSaveInstanceState ( Bundle outState )
{
base . OnSaveInstanceState ( outState ) ;
2013-06-15 06:40:01 -04:00
AppTask . ToBundle ( outState ) ;
2013-09-16 22:53:14 -04:00
outState . PutBoolean ( ShowpasswordKey , _showPassword ) ;
2013-11-20 13:14:57 -05:00
outState . PutString ( KeyFileOrProviderKey , _keyFileOrProvider ) ;
outState . PutString ( PasswordKey , _password ) ;
outState . PutStringArrayList ( PendingOtpsKey , _pendingOtps ) ;
if ( _otpInfo ! = null )
{
2013-11-22 15:47:13 -05:00
outState . PutStringArrayList ( EnteredOtpsKey , GetOtpsFromUi ( ) ) ;
2013-11-20 13:14:57 -05:00
var sw = new StringWriter ( ) ;
var xws = OtpInfo . XmlWriterSettings ( ) ;
XmlWriter xw = XmlWriter . Create ( sw , xws ) ;
XmlSerializer xs = new XmlSerializer ( typeof ( OtpInfo ) ) ;
xs . Serialize ( xw , _otpInfo ) ;
xw . Close ( ) ;
outState . PutString ( OtpInfoKey , sw . ToString ( ) ) ;
}
2013-11-17 11:01:53 -05:00
//more OTP TODO:
// * Caching of aux file
// * -> implement IFileStorage in JavaFileStorage based on ListFiles
2013-11-20 13:14:57 -05:00
// * -> Sync
2013-11-17 11:01:53 -05:00
}
protected override void OnNewIntent ( Intent intent )
{
base . OnNewIntent ( intent ) ;
//this method is called from the NfcOtpActivity's startActivity() if the activity is already running
//note: it's not called in other cases because OnNewIntent requires the activity to be on top already
//which is never the case when started from another activity (in the same task).
//NfcOtpActivity sets the ClearTop flag to get OnNewIntent called.
if ( ( intent ! = null ) & & ( intent . HasExtra ( Intents . OtpExtraKey ) ) )
{
string otp = intent . GetStringExtra ( Intents . OtpExtraKey ) ;
if ( _otpInfo = = null )
{
//Entering OTPs not yet initialized:
_pendingOtps . Add ( otp ) ;
UpdateKeyProviderUiState ( ) ;
}
else
{
//Entering OTPs is initialized. Write OTP into first empty field:
bool foundEmptyField = false ;
foreach ( int otpId in _otpTextViewIds )
{
EditText otpEdit = FindViewById < EditText > ( otpId ) ;
if ( ( otpEdit . Visibility = = ViewStates . Visible ) & & String . IsNullOrEmpty ( otpEdit . Text ) )
{
otpEdit . Text = otp ;
foundEmptyField = true ;
break ;
}
}
//did we find a field?
if ( ! foundEmptyField )
{
Toast . MakeText ( this , GetString ( Resource . String . otp_discarded_no_space ) , ToastLength . Long ) . Show ( ) ;
}
}
Spinner passwordModeSpinner = FindViewById < Spinner > ( Resource . Id . password_mode_spinner ) ;
if ( passwordModeSpinner . SelectedItemPosition ! = ( int ) KeyProviders . Otp )
{
passwordModeSpinner . SetSelection ( ( int ) KeyProviders . Otp ) ;
}
}
2013-05-30 00:54:25 -04:00
}
2013-02-23 11:43:42 -05:00
2013-08-07 13:34:43 -04:00
protected override void OnResume ( )
{
2013-02-23 11:43:42 -05:00
base . OnResume ( ) ;
2014-02-02 01:03:40 -05:00
_design . ReapplyTheme ( ) ;
2013-11-11 00:38:15 -05:00
View killButton = FindViewById ( Resource . Id . kill_app ) ;
if ( PreferenceManager . GetDefaultSharedPreferences ( this )
. GetBoolean ( GetString ( Resource . String . show_kill_app_key ) , false ) )
{
killButton . Click + = ( sender , args ) = >
{
_killOnDestroy = true ;
Finish ( ) ;
} ;
killButton . Visibility = ViewStates . Visible ;
}
else
{
killButton . Visibility = ViewStates . Gone ;
}
2013-08-31 07:58:00 -04:00
MakePasswordMaskedOrVisible ( ) ;
2013-11-25 15:06:37 -05:00
UpdateOkButtonState ( ) ;
2014-05-24 23:36:52 -04:00
if ( KeyProviderType = = KeyProviders . Challenge )
{
FindViewById ( Resource . Id . otpInitView ) . Visibility = _challengeSecret = = null ? ViewStates . Visible : ViewStates . Gone ;
}
2013-08-07 13:34:43 -04:00
// OnResume is run every time the activity comes to the foreground. This code should only run when the activity is started (OnStart), but must
// be run in OnResume rather than OnStart so that it always occurrs after OnActivityResult (when re-creating a killed activity, OnStart occurs before OnActivityResult)
2014-03-31 01:24:02 -04:00
//use !IsFinishing to make sure we're not starting another activity when we're already finishing (e.g. due to TaskComplete in OnActivityResult)
//use !performingLoad to make sure we're not already loading the database (after ActivityResult from File-Prepare-Activity; this would cause _loadDbTask to exist when we reload later!)
if ( _starting & & ! IsFinishing & & ! _performingLoad )
2013-02-23 11:43:42 -05:00
{
2013-08-10 14:49:59 -04:00
_starting = false ;
2013-08-07 13:34:43 -04:00
if ( App . Kp2a . DatabaseIsUnlocked )
2013-02-23 11:43:42 -05:00
{
LaunchNextActivity ( ) ;
}
2013-08-07 13:34:43 -04:00
else if ( App . Kp2a . QuickUnlockEnabled & & App . Kp2a . QuickLocked )
{
var i = new Intent ( this , typeof ( QuickUnlock ) ) ;
PutIoConnectionToIntent ( _ioConnection , i ) ;
Kp2aLog . Log ( "Starting QuickUnlock" ) ;
StartActivityForResult ( i , 0 ) ;
}
2013-09-17 00:53:18 -04:00
else
2013-02-23 11:43:42 -05:00
{
2013-09-17 00:53:18 -04:00
//database not yet loaded.
//check if pre-loading is enabled but wasn't started yet:
2013-10-08 11:01:47 -04:00
if ( _loadDbTask = = null & & _prefs . GetBoolean ( GetString ( Resource . String . PreloadDatabaseEnabled_key ) , true ) )
2013-09-17 00:53:18 -04:00
{
// Create task to kick off file loading while the user enters the password
2014-01-25 22:38:12 -05:00
_loadDbTask = Task . Factory . StartNew < MemoryStream > ( PreloadDbFile ) ;
2013-09-17 00:53:18 -04:00
}
2013-02-23 11:43:42 -05:00
}
}
}
2013-11-20 13:14:57 -05:00
private void InitializeQuickUnlockCheckbox ( ) {
2013-02-23 11:43:42 -05:00
CheckBox cbQuickUnlock = ( CheckBox ) FindViewById ( Resource . Id . enable_quickunlock ) ;
2013-06-15 06:40:01 -04:00
cbQuickUnlock . Checked = _prefs . GetBoolean ( GetString ( Resource . String . QuickUnlockDefaultEnabled_key ) , true ) ;
2013-02-23 11:43:42 -05:00
}
2013-06-15 06:40:01 -04:00
private String GetKeyFile ( String filename ) {
if ( _rememberKeyfile ) {
2013-11-25 15:06:37 -05:00
string keyfile = App . Kp2a . FileDbHelper . GetKeyFileForFile ( filename ) ;
if ( keyfile = = "" )
return null ; //signal no key file
return keyfile ;
2013-02-23 11:43:42 -05:00
} else {
2013-11-17 01:17:15 -05:00
return null ;
2013-02-23 11:43:42 -05:00
}
}
2013-11-20 13:14:57 -05:00
private void InitializeFilenameView ( ) {
2013-10-27 10:06:57 -04:00
SetEditText ( Resource . Id . filename , App . Kp2a . GetFileStorage ( _ioConnection ) . GetDisplayName ( _ioConnection ) ) ;
2013-11-09 00:51:54 -05:00
if ( App . Kp2a . FileDbHelper . NumberOfRecentFiles ( ) < 2 )
{
FindViewById ( Resource . Id . filename_group ) . Visibility = ViewStates . Gone ;
}
else
{
FindViewById ( Resource . Id . filename_group ) . Visibility = ViewStates . Visible ;
}
2013-11-20 13:14:57 -05:00
2013-02-23 11:43:42 -05:00
}
2013-11-11 00:38:15 -05:00
protected override void OnDestroy ( )
{
base . OnDestroy ( ) ;
if ( _killOnDestroy )
Process . KillProcess ( Process . MyPid ( ) ) ;
}
2013-02-23 11:43:42 -05:00
/ *
private void errorMessage ( CharSequence text )
{
Toast . MakeText ( this , text , ToastLength . Long ) . Show ( ) ;
}
* /
2013-11-20 13:14:57 -05:00
2013-06-15 06:40:01 -04:00
private void SetEditText ( int resId , String str ) {
2013-02-23 11:43:42 -05:00
TextView te = ( TextView ) FindViewById ( resId ) ;
//assert(te == null);
if ( te ! = null ) {
te . Text = str ;
}
}
public override bool OnCreateOptionsMenu ( IMenu menu ) {
base . OnCreateOptionsMenu ( menu ) ;
MenuInflater inflate = MenuInflater ;
inflate . Inflate ( Resource . Menu . password , menu ) ;
return true ;
}
public override bool OnOptionsItemSelected ( IMenuItem item ) {
switch ( item . ItemId ) {
2013-08-09 16:31:30 -04:00
case Resource . Id . menu_about :
AboutDialog dialog = new AboutDialog ( this ) ;
dialog . Show ( ) ;
return true ;
2013-02-23 11:43:42 -05:00
2013-08-09 16:31:30 -04:00
case Resource . Id . menu_app_settings :
AppSettingsActivity . Launch ( this ) ;
return true ;
case Resource . Id . menu_change_db :
2013-09-17 00:53:18 -04:00
GoToFileSelectActivity ( ) ;
2013-08-09 16:31:30 -04:00
return true ;
2013-02-23 11:43:42 -05:00
}
return base . OnOptionsItemSelected ( item ) ;
}
2013-09-17 00:53:18 -04:00
private void GoToFileSelectActivity ( )
{
Intent intent = new Intent ( this , typeof ( FileSelectActivity ) ) ;
2013-10-02 22:15:40 -04:00
intent . PutExtra ( FileSelectActivity . NoForwardToPasswordActivity , true ) ;
2013-09-17 00:53:18 -04:00
AppTask . ToIntent ( intent ) ;
2014-05-16 11:15:43 -04:00
intent . AddFlags ( ActivityFlags . ForwardResult ) ;
StartActivity ( intent ) ;
2013-09-17 00:53:18 -04:00
Finish ( ) ;
}
2013-02-23 11:43:42 -05:00
private class AfterLoad : OnFinish {
2013-06-15 06:40:01 -04:00
readonly PasswordActivity _act ;
2013-08-04 04:38:15 -04:00
public AfterLoad ( Handler handler , PasswordActivity act ) : base ( handler )
{
2013-06-15 16:02:48 -04:00
_act = act ;
2013-02-23 11:43:42 -05:00
}
2013-11-20 13:14:57 -05:00
2013-11-22 15:47:13 -05:00
public override void Run ( )
{
2013-08-04 04:38:15 -04:00
if ( Success )
{
2014-05-22 00:42:43 -04:00
_act . ClearEnteredPassword ( ) ;
2013-07-25 08:47:05 -04:00
2013-06-15 06:40:01 -04:00
_act . LaunchNextActivity ( ) ;
2013-08-04 04:38:15 -04:00
2013-11-20 13:14:57 -05:00
GC . Collect ( ) ; // Ensure temporary memory used while loading is collected
2013-08-04 04:38:15 -04:00
}
2014-03-25 01:15:05 -04:00
DisplayMessage ( _act ) ;
2014-03-31 01:24:02 -04:00
_act . _performingLoad = false ;
2013-02-23 11:43:42 -05:00
}
}
2013-11-22 15:47:13 -05:00
2014-05-22 00:42:43 -04:00
private void ClearEnteredPassword ( )
{
SetEditText ( Resource . Id . password , "" ) ;
SetEditText ( Resource . Id . pass_otpsecret , "" ) ;
foreach ( int otpId in _otpTextViewIds )
{
SetEditText ( otpId , "" ) ;
}
2014-05-24 23:36:52 -04:00
if ( _challengeSecret ! = null )
{
Array . Clear ( _challengeSecret , 0 , _challengeSecret . Length ) ;
_challengeSecret = null ;
}
2014-05-22 00:42:43 -04:00
}
2013-11-22 15:47:13 -05:00
class SaveOtpAuxFileAndLoadDb : LoadDb
{
private readonly PasswordActivity _act ;
public SaveOtpAuxFileAndLoadDb ( IKp2aApp app , IOConnectionInfo ioc , Task < MemoryStream > databaseData , CompositeKey compositeKey , string keyfileOrProvider , OnFinish finish , PasswordActivity act ) : base ( app , ioc , databaseData , compositeKey , keyfileOrProvider , finish )
{
_act = act ;
}
public override void Run ( )
{
try
{
StatusLogger . UpdateMessage ( UiStringKey . SavingOtpAuxFile ) ;
KeyProviderQueryContext ctx = new KeyProviderQueryContext ( _act . _ioConnection , false , false ) ;
IOConnectionInfo auxFileIoc = OathHotpKeyProv . GetAuxFileIoc ( _act . _ioConnection ) ;
if ( ! OathHotpKeyProv . CreateAuxFile ( _act . _otpInfo , ctx , auxFileIoc ) )
Toast . MakeText ( _act , _act . GetString ( Resource . String . ErrorUpdatingOtpAuxFile ) , ToastLength . Long ) . Show ( ) ;
App . Kp2a . GetDb ( ) . OtpAuxFileIoc = auxFileIoc ;
}
catch ( Exception e )
{
Kp2aLog . Log ( e . Message ) ;
Toast . MakeText ( _act , _act . GetString ( Resource . String . ErrorUpdatingOtpAuxFile ) + " " + e . Message ,
ToastLength . Long ) . Show ( ) ;
}
base . Run ( ) ;
}
}
2013-02-23 11:43:42 -05:00
}
}