mirror of
https://github.com/moparisthebest/open-keychain
synced 2024-11-24 01:32:16 -05:00
Merge branch 'development' of github.com:open-keychain/open-keychain into development
This commit is contained in:
commit
7d273ecfa6
@ -624,6 +624,23 @@
|
|||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
|
<!-- VIEW from keyserver urls opened in a browser -->
|
||||||
|
<intent-filter android:label="@string/intent_import_key">
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
|
||||||
|
<data android:scheme="https"/>
|
||||||
|
<data android:scheme="http"/>
|
||||||
|
<!-- if we don't specify a host, pathPattern will be ignored-->
|
||||||
|
<data android:host="*"/>
|
||||||
|
<!-- convention for keyserver paths specified by internet draft
|
||||||
|
draft-shaw-openpgp-hkp-00.txt
|
||||||
|
(http://tools.ietf.org/html/draft-shaw-openpgp-hkp-00#section-3) -->
|
||||||
|
<data android:pathPattern="/pks/lookup.*"/>
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
<!-- Keychain's own Actions -->
|
<!-- Keychain's own Actions -->
|
||||||
<!-- IMPORT_KEY with files TODO: does this work? -->
|
<!-- IMPORT_KEY with files TODO: does this work? -->
|
||||||
<intent-filter android:label="@string/intent_import_key">
|
<intent-filter android:label="@string/intent_import_key">
|
||||||
|
@ -123,7 +123,8 @@ public class CreateKeyYubiImportFragment extends Fragment implements NfcListener
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
mListFragment = ImportKeysListFragment.newInstance(null, null, "0x" + mNfcFingerprint, true);
|
mListFragment = ImportKeysListFragment.newInstance(null, null,
|
||||||
|
"0x" + mNfcFingerprint, true, null);
|
||||||
|
|
||||||
view.findViewById(R.id.button_search).setOnClickListener(new OnClickListener() {
|
view.findViewById(R.id.button_search).setOnClickListener(new OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -39,6 +39,7 @@ import org.sufficientlysecure.keychain.ui.base.BaseNfcActivity;
|
|||||||
import org.sufficientlysecure.keychain.service.CloudImportService;
|
import org.sufficientlysecure.keychain.service.CloudImportService;
|
||||||
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
|
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
|
||||||
import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
|
import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
|
||||||
|
import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
|
||||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
@ -62,6 +63,8 @@ public class ImportKeysActivity extends BaseNfcActivity {
|
|||||||
// Actions for internal use only:
|
// Actions for internal use only:
|
||||||
public static final String ACTION_IMPORT_KEY_FROM_FILE = Constants.INTENT_PREFIX
|
public static final String ACTION_IMPORT_KEY_FROM_FILE = Constants.INTENT_PREFIX
|
||||||
+ "IMPORT_KEY_FROM_FILE";
|
+ "IMPORT_KEY_FROM_FILE";
|
||||||
|
public static final String ACTION_SEARCH_KEYSERVER_FROM_URL = Constants.INTENT_PREFIX
|
||||||
|
+ "SEARCH_KEYSERVER_FROM_URL";
|
||||||
public static final String EXTRA_RESULT = "result";
|
public static final String EXTRA_RESULT = "result";
|
||||||
|
|
||||||
// only used by ACTION_IMPORT_KEY
|
// only used by ACTION_IMPORT_KEY
|
||||||
@ -112,15 +115,19 @@ public class ImportKeysActivity extends BaseNfcActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (action == null) {
|
if (action == null) {
|
||||||
startCloudFragment(savedInstanceState, null, false);
|
startCloudFragment(savedInstanceState, null, false, null);
|
||||||
startListFragment(savedInstanceState, null, null, null);
|
startListFragment(savedInstanceState, null, null, null, null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Intent.ACTION_VIEW.equals(action)) {
|
if (Intent.ACTION_VIEW.equals(action)) {
|
||||||
// Android's Action when opening file associated to Keychain (see AndroidManifest.xml)
|
if (scheme.equals("http") || scheme.equals("https")) {
|
||||||
// delegate action to ACTION_IMPORT_KEY
|
action = ACTION_SEARCH_KEYSERVER_FROM_URL;
|
||||||
action = ACTION_IMPORT_KEY;
|
} else {
|
||||||
|
// Android's Action when opening file associated to Keychain (see AndroidManifest.xml)
|
||||||
|
// delegate action to ACTION_IMPORT_KEY
|
||||||
|
action = ACTION_IMPORT_KEY;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
@ -130,12 +137,12 @@ public class ImportKeysActivity extends BaseNfcActivity {
|
|||||||
|
|
||||||
if (dataUri != null) {
|
if (dataUri != null) {
|
||||||
// action: directly load data
|
// action: directly load data
|
||||||
startListFragment(savedInstanceState, null, dataUri, null);
|
startListFragment(savedInstanceState, null, dataUri, null, null);
|
||||||
} else if (extras.containsKey(EXTRA_KEY_BYTES)) {
|
} else if (extras.containsKey(EXTRA_KEY_BYTES)) {
|
||||||
byte[] importData = extras.getByteArray(EXTRA_KEY_BYTES);
|
byte[] importData = extras.getByteArray(EXTRA_KEY_BYTES);
|
||||||
|
|
||||||
// action: directly load data
|
// action: directly load data
|
||||||
startListFragment(savedInstanceState, importData, null, null);
|
startListFragment(savedInstanceState, importData, null, null, null);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -162,10 +169,10 @@ public class ImportKeysActivity extends BaseNfcActivity {
|
|||||||
|
|
||||||
if (query != null && query.length() > 0) {
|
if (query != null && query.length() > 0) {
|
||||||
// display keyserver fragment with query
|
// display keyserver fragment with query
|
||||||
startCloudFragment(savedInstanceState, query, false);
|
startCloudFragment(savedInstanceState, query, false, null);
|
||||||
|
|
||||||
// action: search immediately
|
// action: search immediately
|
||||||
startListFragment(savedInstanceState, null, null, query);
|
startListFragment(savedInstanceState, null, null, query, null);
|
||||||
} else {
|
} else {
|
||||||
Log.e(Constants.TAG, "Query is empty!");
|
Log.e(Constants.TAG, "Query is empty!");
|
||||||
return;
|
return;
|
||||||
@ -181,10 +188,10 @@ public class ImportKeysActivity extends BaseNfcActivity {
|
|||||||
String query = "0x" + fingerprint;
|
String query = "0x" + fingerprint;
|
||||||
|
|
||||||
// display keyserver fragment with query
|
// display keyserver fragment with query
|
||||||
startCloudFragment(savedInstanceState, query, true);
|
startCloudFragment(savedInstanceState, query, true, null);
|
||||||
|
|
||||||
// action: search immediately
|
// action: search immediately
|
||||||
startListFragment(savedInstanceState, null, null, query);
|
startListFragment(savedInstanceState, null, null, query, null);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.e(Constants.TAG,
|
Log.e(Constants.TAG,
|
||||||
@ -200,7 +207,29 @@ public class ImportKeysActivity extends BaseNfcActivity {
|
|||||||
startFileFragment(savedInstanceState);
|
startFileFragment(savedInstanceState);
|
||||||
|
|
||||||
// no immediate actions!
|
// no immediate actions!
|
||||||
startListFragment(savedInstanceState, null, null, null);
|
startListFragment(savedInstanceState, null, null, null, null);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ACTION_SEARCH_KEYSERVER_FROM_URL: {
|
||||||
|
// need to process URL to get search query and keyserver authority
|
||||||
|
String query = dataUri.getQueryParameter("search");
|
||||||
|
String keyserver = dataUri.getAuthority();
|
||||||
|
// if query not specified, we still allow users to search the keyserver in the link
|
||||||
|
if (query == null) {
|
||||||
|
Notify.create(this, R.string.import_url_warn_no_search_parameter, Notify.LENGTH_INDEFINITE,
|
||||||
|
Notify.Style.WARN).show(mTopFragment);
|
||||||
|
// we just set the keyserver
|
||||||
|
startCloudFragment(savedInstanceState, null, false, keyserver);
|
||||||
|
// it's not necessary to set the keyserver for ImportKeysListFragment since
|
||||||
|
// it'll be taken care of by ImportKeysCloudFragment when the user clicks
|
||||||
|
// the search button
|
||||||
|
startListFragment(savedInstanceState, null, null, null, null);
|
||||||
|
} else {
|
||||||
|
// we allow our users to edit the query if they wish
|
||||||
|
startCloudFragment(savedInstanceState, query, false, keyserver);
|
||||||
|
// search immediately
|
||||||
|
startListFragment(savedInstanceState, null, null, query, keyserver);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN: {
|
case ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN: {
|
||||||
@ -208,18 +237,31 @@ public class ImportKeysActivity extends BaseNfcActivity {
|
|||||||
startFileFragment(savedInstanceState);
|
startFileFragment(savedInstanceState);
|
||||||
|
|
||||||
// no immediate actions!
|
// no immediate actions!
|
||||||
startListFragment(savedInstanceState, null, null, null);
|
startListFragment(savedInstanceState, null, null, null, null);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
startCloudFragment(savedInstanceState, null, false);
|
startCloudFragment(savedInstanceState, null, false, null);
|
||||||
startListFragment(savedInstanceState, null, null, null);
|
startListFragment(savedInstanceState, null, null, null, null);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startListFragment(Bundle savedInstanceState, byte[] bytes, Uri dataUri, String serverQuery) {
|
|
||||||
|
/**
|
||||||
|
* if the fragment is started with non-null bytes/dataUri/serverQuery, it will immediately
|
||||||
|
* load content
|
||||||
|
*
|
||||||
|
* @param savedInstanceState
|
||||||
|
* @param bytes bytes containing list of keyrings to import
|
||||||
|
* @param dataUri uri to file to import keyrings from
|
||||||
|
* @param serverQuery query to search for on the keyserver
|
||||||
|
* @param keyserver keyserver authority to search on. If null will use keyserver from
|
||||||
|
* user preferences
|
||||||
|
*/
|
||||||
|
private void startListFragment(Bundle savedInstanceState, byte[] bytes, Uri dataUri,
|
||||||
|
String serverQuery, String keyserver) {
|
||||||
// However, if we're being restored from a previous state,
|
// However, if we're being restored from a previous state,
|
||||||
// then we don't need to do anything and should return or else
|
// then we don't need to do anything and should return or else
|
||||||
// we could end up with overlapping fragments.
|
// we could end up with overlapping fragments.
|
||||||
@ -227,8 +269,8 @@ public class ImportKeysActivity extends BaseNfcActivity {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create an instance of the fragment
|
mListFragment = ImportKeysListFragment.newInstance(bytes, dataUri, serverQuery, false,
|
||||||
mListFragment = ImportKeysListFragment.newInstance(bytes, dataUri, serverQuery);
|
keyserver);
|
||||||
|
|
||||||
// Add the fragment to the 'fragment_container' FrameLayout
|
// Add the fragment to the 'fragment_container' FrameLayout
|
||||||
// NOTE: We use commitAllowingStateLoss() to prevent weird crashes!
|
// NOTE: We use commitAllowingStateLoss() to prevent weird crashes!
|
||||||
@ -259,7 +301,18 @@ public class ImportKeysActivity extends BaseNfcActivity {
|
|||||||
getSupportFragmentManager().executePendingTransactions();
|
getSupportFragmentManager().executePendingTransactions();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startCloudFragment(Bundle savedInstanceState, String query, boolean disableQueryEdit) {
|
/**
|
||||||
|
* loads the CloudFragment, which consists of the search bar, search button and settings icon
|
||||||
|
* visually.
|
||||||
|
*
|
||||||
|
* @param savedInstanceState
|
||||||
|
* @param query search query
|
||||||
|
* @param disableQueryEdit if true, user will not be able to edit the search query
|
||||||
|
* @param keyserver keyserver authority to use for search, if null will use keyserver
|
||||||
|
* specified in user preferences
|
||||||
|
*/
|
||||||
|
|
||||||
|
private void startCloudFragment(Bundle savedInstanceState, String query, boolean disableQueryEdit, String keyserver) {
|
||||||
// However, if we're being restored from a previous state,
|
// However, if we're being restored from a previous state,
|
||||||
// then we don't need to do anything and should return or else
|
// then we don't need to do anything and should return or else
|
||||||
// we could end up with overlapping fragments.
|
// we could end up with overlapping fragments.
|
||||||
@ -268,7 +321,7 @@ public class ImportKeysActivity extends BaseNfcActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create an instance of the fragment
|
// Create an instance of the fragment
|
||||||
mTopFragment = ImportKeysCloudFragment.newInstance(query, disableQueryEdit);
|
mTopFragment = ImportKeysCloudFragment.newInstance(query, disableQueryEdit, keyserver);
|
||||||
|
|
||||||
// Add the fragment to the 'fragment_container' FrameLayout
|
// Add the fragment to the 'fragment_container' FrameLayout
|
||||||
// NOTE: We use commitAllowingStateLoss() to prevent weird crashes!
|
// NOTE: We use commitAllowingStateLoss() to prevent weird crashes!
|
||||||
|
@ -45,6 +45,7 @@ import java.util.List;
|
|||||||
public class ImportKeysCloudFragment extends Fragment {
|
public class ImportKeysCloudFragment extends Fragment {
|
||||||
public static final String ARG_QUERY = "query";
|
public static final String ARG_QUERY = "query";
|
||||||
public static final String ARG_DISABLE_QUERY_EDIT = "disable_query_edit";
|
public static final String ARG_DISABLE_QUERY_EDIT = "disable_query_edit";
|
||||||
|
public static final String ARG_KEYSERVER = "keyserver";
|
||||||
|
|
||||||
private ImportKeysActivity mImportActivity;
|
private ImportKeysActivity mImportActivity;
|
||||||
|
|
||||||
@ -54,13 +55,20 @@ public class ImportKeysCloudFragment extends Fragment {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates new instance of this fragment
|
* Creates new instance of this fragment
|
||||||
|
*
|
||||||
|
* @param query query to search for
|
||||||
|
* @param disableQueryEdit if true, user cannot edit query
|
||||||
|
* @param keyserver specified keyserver authority to use. If null, will use keyserver
|
||||||
|
* specified in user preferences
|
||||||
*/
|
*/
|
||||||
public static ImportKeysCloudFragment newInstance(String query, boolean disableQueryEdit) {
|
public static ImportKeysCloudFragment newInstance(String query, boolean disableQueryEdit,
|
||||||
|
String keyserver) {
|
||||||
ImportKeysCloudFragment frag = new ImportKeysCloudFragment();
|
ImportKeysCloudFragment frag = new ImportKeysCloudFragment();
|
||||||
|
|
||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
args.putString(ARG_QUERY, query);
|
args.putString(ARG_QUERY, query);
|
||||||
args.putBoolean(ARG_DISABLE_QUERY_EDIT, disableQueryEdit);
|
args.putBoolean(ARG_DISABLE_QUERY_EDIT, disableQueryEdit);
|
||||||
|
args.putString(ARG_KEYSERVER, keyserver);
|
||||||
|
|
||||||
frag.setArguments(args);
|
frag.setArguments(args);
|
||||||
|
|
||||||
@ -151,8 +159,17 @@ public class ImportKeysCloudFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void search(String query) {
|
private void search(String query) {
|
||||||
Preferences prefs = Preferences.getPreferences(getActivity());
|
Preferences.CloudSearchPrefs cloudSearchPrefs;
|
||||||
mImportActivity.loadCallback(new ImportKeysListFragment.CloudLoaderState(query, prefs.getCloudSearchPrefs()));
|
String explicitKeyserver = getArguments().getString(ARG_KEYSERVER);
|
||||||
|
// no explicit keyserver passed
|
||||||
|
if (explicitKeyserver == null) {
|
||||||
|
cloudSearchPrefs = Preferences.getPreferences(getActivity()).getCloudSearchPrefs();
|
||||||
|
} else {
|
||||||
|
// assume we are also meant to search keybase.io
|
||||||
|
cloudSearchPrefs = new Preferences.CloudSearchPrefs(true, true, explicitKeyserver);
|
||||||
|
}
|
||||||
|
mImportActivity.loadCallback(
|
||||||
|
new ImportKeysListFragment.CloudLoaderState(query, cloudSearchPrefs));
|
||||||
toggleKeyboard(false);
|
toggleKeyboard(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,6 +58,7 @@ public class ImportKeysListFragment extends ListFragment implements
|
|||||||
private static final String ARG_BYTES = "bytes";
|
private static final String ARG_BYTES = "bytes";
|
||||||
public static final String ARG_SERVER_QUERY = "query";
|
public static final String ARG_SERVER_QUERY = "query";
|
||||||
public static final String ARG_NON_INTERACTIVE = "non_interactive";
|
public static final String ARG_NON_INTERACTIVE = "non_interactive";
|
||||||
|
public static final String ARG_KEYSERVER_URL = "keyserver_url";
|
||||||
|
|
||||||
private Activity mActivity;
|
private Activity mActivity;
|
||||||
private ImportKeysAdapter mAdapter;
|
private ImportKeysAdapter mAdapter;
|
||||||
@ -78,7 +79,8 @@ public class ImportKeysListFragment extends ListFragment implements
|
|||||||
return mAdapter.getData();
|
return mAdapter.getData();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns an Iterator (with size) of the selected data items.
|
/**
|
||||||
|
* Returns an Iterator (with size) of the selected data items.
|
||||||
* This iterator is sort of a tradeoff, it's slightly more complex than an
|
* This iterator is sort of a tradeoff, it's slightly more complex than an
|
||||||
* ArrayList would have been, but we save some memory by just returning
|
* ArrayList would have been, but we save some memory by just returning
|
||||||
* relevant elements on demand.
|
* relevant elements on demand.
|
||||||
@ -121,12 +123,36 @@ public class ImportKeysListFragment extends ListFragment implements
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ImportKeysListFragment newInstance(byte[] bytes, Uri dataUri, String serverQuery) {
|
/**
|
||||||
return newInstance(bytes, dataUri, serverQuery, false);
|
* Creates an interactive ImportKeyListFragment which reads keyrings from bytes, or file specified
|
||||||
|
* by dataUri, or searches a keyserver for serverQuery, if parameter is not null, in that order
|
||||||
|
*
|
||||||
|
* @param bytes byte data containing list of keyrings to be imported
|
||||||
|
* @param dataUri file from which keyrings are to be imported
|
||||||
|
* @param serverQuery query to search for on keyserver
|
||||||
|
* @param keyserver if not null, will perform search on specified keyserver. Else, uses
|
||||||
|
* keyserver specified in user preferences
|
||||||
|
* @return fragment with arguments set based on passed parameters
|
||||||
|
*/
|
||||||
|
public static ImportKeysListFragment newInstance(byte[] bytes, Uri dataUri, String serverQuery,
|
||||||
|
String keyserver) {
|
||||||
|
return newInstance(bytes, dataUri, serverQuery, false, keyserver);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ImportKeysListFragment newInstance(byte[] bytes, Uri dataUri,
|
/**
|
||||||
String serverQuery, boolean nonInteractive) {
|
* Visually consists of a list of keyrings with checkboxes to specify which are to be imported
|
||||||
|
* Can immediately load keyrings specified by any of its parameters
|
||||||
|
*
|
||||||
|
* @param bytes byte data containing list of keyrings to be imported
|
||||||
|
* @param dataUri file from which keyrings are to be imported
|
||||||
|
* @param serverQuery query to search for on keyserver
|
||||||
|
* @param nonInteractive if true, users will not be able to check/uncheck items in the list
|
||||||
|
* @param keyserver if set, will perform search on specified keyserver. If null, falls back
|
||||||
|
* to keyserver specified in user preferences
|
||||||
|
* @return fragment with arguments set based on passed parameters
|
||||||
|
*/
|
||||||
|
public static ImportKeysListFragment newInstance(byte[] bytes, Uri dataUri, String serverQuery,
|
||||||
|
boolean nonInteractive, String keyserver) {
|
||||||
ImportKeysListFragment frag = new ImportKeysListFragment();
|
ImportKeysListFragment frag = new ImportKeysListFragment();
|
||||||
|
|
||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
@ -134,6 +160,7 @@ public class ImportKeysListFragment extends ListFragment implements
|
|||||||
args.putParcelable(ARG_DATA_URI, dataUri);
|
args.putParcelable(ARG_DATA_URI, dataUri);
|
||||||
args.putString(ARG_SERVER_QUERY, serverQuery);
|
args.putString(ARG_SERVER_QUERY, serverQuery);
|
||||||
args.putBoolean(ARG_NON_INTERACTIVE, nonInteractive);
|
args.putBoolean(ARG_NON_INTERACTIVE, nonInteractive);
|
||||||
|
args.putString(ARG_KEYSERVER_URL, keyserver);
|
||||||
|
|
||||||
frag.setArguments(args);
|
frag.setArguments(args);
|
||||||
|
|
||||||
@ -180,16 +207,23 @@ public class ImportKeysListFragment extends ListFragment implements
|
|||||||
setListAdapter(mAdapter);
|
setListAdapter(mAdapter);
|
||||||
|
|
||||||
Bundle args = getArguments();
|
Bundle args = getArguments();
|
||||||
Uri dataUri = args.containsKey(ARG_DATA_URI) ? args.<Uri>getParcelable(ARG_DATA_URI) : null;
|
Uri dataUri = args.getParcelable(ARG_DATA_URI);
|
||||||
byte[] bytes = args.containsKey(ARG_BYTES) ? args.getByteArray(ARG_BYTES) : null;
|
byte[] bytes = args.getByteArray(ARG_BYTES);
|
||||||
String query = args.containsKey(ARG_SERVER_QUERY) ? args.getString(ARG_SERVER_QUERY) : null;
|
String query = args.getString(ARG_SERVER_QUERY);
|
||||||
mNonInteractive = args.containsKey(ARG_NON_INTERACTIVE) ? args.getBoolean(ARG_NON_INTERACTIVE) : false;
|
String keyserver = args.getString(ARG_KEYSERVER_URL);
|
||||||
|
mNonInteractive = args.getBoolean(ARG_NON_INTERACTIVE, false);
|
||||||
|
|
||||||
if (dataUri != null || bytes != null) {
|
if (dataUri != null || bytes != null) {
|
||||||
mLoaderState = new BytesLoaderState(bytes, dataUri);
|
mLoaderState = new BytesLoaderState(bytes, dataUri);
|
||||||
} else if (query != null) {
|
} else if (query != null) {
|
||||||
Preferences prefs = Preferences.getPreferences(getActivity());
|
Preferences.CloudSearchPrefs cloudSearchPrefs;
|
||||||
mLoaderState = new CloudLoaderState(query, prefs.getCloudSearchPrefs());
|
if (keyserver != null) {
|
||||||
|
cloudSearchPrefs = Preferences.getPreferences(getActivity()).getCloudSearchPrefs();
|
||||||
|
} else {
|
||||||
|
cloudSearchPrefs = new Preferences.CloudSearchPrefs(true, true, keyserver);
|
||||||
|
}
|
||||||
|
|
||||||
|
mLoaderState = new CloudLoaderState(query, cloudSearchPrefs);
|
||||||
}
|
}
|
||||||
|
|
||||||
getListView().setOnTouchListener(new OnTouchListener() {
|
getListView().setOnTouchListener(new OnTouchListener() {
|
||||||
|
@ -58,13 +58,12 @@ import java.util.Locale;
|
|||||||
public class ImportKeysProxyActivity extends FragmentActivity {
|
public class ImportKeysProxyActivity extends FragmentActivity {
|
||||||
|
|
||||||
public static final String ACTION_QR_CODE_API = OpenKeychainIntents.IMPORT_KEY_FROM_QR_CODE;
|
public static final String ACTION_QR_CODE_API = OpenKeychainIntents.IMPORT_KEY_FROM_QR_CODE;
|
||||||
|
// implies activity returns scanned fingerprint as extra and does not import
|
||||||
public static final String ACTION_SCAN_WITH_RESULT = Constants.INTENT_PREFIX + "SCAN_QR_CODE_WITH_RESULT";
|
public static final String ACTION_SCAN_WITH_RESULT = Constants.INTENT_PREFIX + "SCAN_QR_CODE_WITH_RESULT";
|
||||||
public static final String ACTION_SCAN_IMPORT = Constants.INTENT_PREFIX + "SCAN_QR_CODE_IMPORT";
|
public static final String ACTION_SCAN_IMPORT = Constants.INTENT_PREFIX + "SCAN_QR_CODE_IMPORT";
|
||||||
|
|
||||||
public static final String EXTRA_FINGERPRINT = "fingerprint";
|
public static final String EXTRA_FINGERPRINT = "fingerprint";
|
||||||
|
|
||||||
boolean returnResult;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@ -82,10 +81,8 @@ public class ImportKeysProxyActivity extends FragmentActivity {
|
|||||||
if (scheme != null && scheme.toLowerCase(Locale.ENGLISH).equals(Constants.FINGERPRINT_SCHEME)) {
|
if (scheme != null && scheme.toLowerCase(Locale.ENGLISH).equals(Constants.FINGERPRINT_SCHEME)) {
|
||||||
// Scanning a fingerprint directly with Barcode Scanner, thus we already have scanned
|
// Scanning a fingerprint directly with Barcode Scanner, thus we already have scanned
|
||||||
|
|
||||||
returnResult = false;
|
|
||||||
processScannedContent(dataUri);
|
processScannedContent(dataUri);
|
||||||
} else if (ACTION_SCAN_IMPORT.equals(action) || ACTION_QR_CODE_API.equals(action)) {
|
} else if (ACTION_SCAN_IMPORT.equals(action) || ACTION_QR_CODE_API.equals(action)) {
|
||||||
returnResult = false;
|
|
||||||
IntentIntegrator integrator = new IntentIntegrator(this);
|
IntentIntegrator integrator = new IntentIntegrator(this);
|
||||||
integrator.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE_TYPES)
|
integrator.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE_TYPES)
|
||||||
.setPrompt(getString(R.string.import_qr_code_text))
|
.setPrompt(getString(R.string.import_qr_code_text))
|
||||||
@ -93,7 +90,6 @@ public class ImportKeysProxyActivity extends FragmentActivity {
|
|||||||
integrator.setOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
integrator.setOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
||||||
integrator.initiateScan();
|
integrator.initiateScan();
|
||||||
} else if (ACTION_SCAN_WITH_RESULT.equals(action)) {
|
} else if (ACTION_SCAN_WITH_RESULT.equals(action)) {
|
||||||
returnResult = true;
|
|
||||||
IntentIntegrator integrator = new IntentIntegrator(this);
|
IntentIntegrator integrator = new IntentIntegrator(this);
|
||||||
integrator.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE_TYPES)
|
integrator.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE_TYPES)
|
||||||
.setPrompt(getString(R.string.import_qr_code_text))
|
.setPrompt(getString(R.string.import_qr_code_text))
|
||||||
@ -103,7 +99,6 @@ public class ImportKeysProxyActivity extends FragmentActivity {
|
|||||||
} else if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {
|
} else if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {
|
||||||
// Check to see if the Activity started due to an Android Beam
|
// Check to see if the Activity started due to an Android Beam
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||||
returnResult = false;
|
|
||||||
handleActionNdefDiscovered(getIntent());
|
handleActionNdefDiscovered(getIntent());
|
||||||
} else {
|
} else {
|
||||||
Log.e(Constants.TAG, "Android Beam not supported by Android < 4.1");
|
Log.e(Constants.TAG, "Android Beam not supported by Android < 4.1");
|
||||||
@ -148,6 +143,8 @@ public class ImportKeysProxyActivity extends FragmentActivity {
|
|||||||
|
|
||||||
private void processScannedContent(Uri uri) {
|
private void processScannedContent(Uri uri) {
|
||||||
|
|
||||||
|
String action = getIntent().getAction();
|
||||||
|
|
||||||
Log.d(Constants.TAG, "scanned: " + uri);
|
Log.d(Constants.TAG, "scanned: " + uri);
|
||||||
|
|
||||||
String fingerprint = null;
|
String fingerprint = null;
|
||||||
@ -166,7 +163,7 @@ public class ImportKeysProxyActivity extends FragmentActivity {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (returnResult) {
|
if (ACTION_SCAN_WITH_RESULT.equals(action)) {
|
||||||
Intent result = new Intent();
|
Intent result = new Intent();
|
||||||
result.putExtra(EXTRA_FINGERPRINT, fingerprint);
|
result.putExtra(EXTRA_FINGERPRINT, fingerprint);
|
||||||
setResult(RESULT_OK, result);
|
setResult(RESULT_OK, result);
|
||||||
@ -177,15 +174,17 @@ public class ImportKeysProxyActivity extends FragmentActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void returnResult(Intent data) {
|
public void returnResult(Intent data) {
|
||||||
if (returnResult) {
|
String action = getIntent().getAction();
|
||||||
setResult(RESULT_OK, data);
|
|
||||||
finish();
|
if (ACTION_QR_CODE_API.equals(action)) {
|
||||||
} else {
|
|
||||||
// display last log message but as Toast for calls from outside OpenKeychain
|
// display last log message but as Toast for calls from outside OpenKeychain
|
||||||
OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
|
OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
|
||||||
String str = getString(result.getLog().getLast().mType.getMsgId());
|
String str = getString(result.getLog().getLast().mType.getMsgId());
|
||||||
Toast.makeText(this, str, Toast.LENGTH_LONG).show();
|
Toast.makeText(this, str, Toast.LENGTH_LONG).show();
|
||||||
finish();
|
finish();
|
||||||
|
} else {
|
||||||
|
setResult(RESULT_OK, data);
|
||||||
|
finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,7 +208,7 @@ public class ImportKeysProxyActivity extends FragmentActivity {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startImportService (ArrayList<ParcelableKeyRing> keyRings) {
|
private void startImportService(ArrayList<ParcelableKeyRing> keyRings) {
|
||||||
|
|
||||||
// Message is received after importing is done in KeychainIntentService
|
// Message is received after importing is done in KeychainIntentService
|
||||||
ServiceProgressHandler serviceHandler = new ServiceProgressHandler(
|
ServiceProgressHandler serviceHandler = new ServiceProgressHandler(
|
||||||
|
@ -82,12 +82,12 @@ public class ImportKeysListLoader
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStartLoading() {
|
protected void onStartLoading() {
|
||||||
forceLoad();
|
super.forceLoad();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStopLoading() {
|
protected void onStopLoading() {
|
||||||
cancelLoad();
|
super.cancelLoad();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -57,6 +57,9 @@ public abstract class BaseNfcActivity extends BaseActivity {
|
|||||||
public static final int REQUEST_CODE_PASSPHRASE = 1;
|
public static final int REQUEST_CODE_PASSPHRASE = 1;
|
||||||
|
|
||||||
protected Passphrase mPin;
|
protected Passphrase mPin;
|
||||||
|
protected boolean mPw1ValidForMultipleSignatures;
|
||||||
|
protected boolean mPw1ValidatedForSignature;
|
||||||
|
protected boolean mPw1ValidatedForDecrypt; // Mode 82 does other things; consider renaming?
|
||||||
private NfcAdapter mNfcAdapter;
|
private NfcAdapter mNfcAdapter;
|
||||||
private IsoDep mIsoDep;
|
private IsoDep mIsoDep;
|
||||||
|
|
||||||
@ -197,10 +200,15 @@ public abstract class BaseNfcActivity extends BaseActivity {
|
|||||||
+ "06" // Lc (number of bytes)
|
+ "06" // Lc (number of bytes)
|
||||||
+ "D27600012401" // Data (6 bytes)
|
+ "D27600012401" // Data (6 bytes)
|
||||||
+ "00"; // Le
|
+ "00"; // Le
|
||||||
if ( ! nfcCommunicate(opening).equals(accepted)) { // activate connection
|
if ( ! nfcCommunicate(opening).endsWith(accepted)) { // activate connection
|
||||||
throw new IOException("Initialization failed!");
|
throw new IOException("Initialization failed!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
byte[] pwStatusBytes = nfcGetPwStatusBytes();
|
||||||
|
mPw1ValidForMultipleSignatures = (pwStatusBytes[0] == 1);
|
||||||
|
mPw1ValidatedForSignature = false;
|
||||||
|
mPw1ValidatedForDecrypt = false;
|
||||||
|
|
||||||
onNfcPerform();
|
onNfcPerform();
|
||||||
|
|
||||||
mIsoDep.close();
|
mIsoDep.close();
|
||||||
@ -278,6 +286,15 @@ public abstract class BaseNfcActivity extends BaseActivity {
|
|||||||
return fptlv.mV;
|
return fptlv.mV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Return the PW Status Bytes from the card. This is a simple DO; no TLV decoding needed.
|
||||||
|
*
|
||||||
|
* @return Seven bytes in fixed format, plus 0x9000 status word at the end.
|
||||||
|
*/
|
||||||
|
public byte[] nfcGetPwStatusBytes() throws IOException {
|
||||||
|
String data = "00CA00C400";
|
||||||
|
return mIsoDep.transceive(Hex.decode(data));
|
||||||
|
}
|
||||||
|
|
||||||
/** Return the fingerprint from application specific data stored on tag, or
|
/** Return the fingerprint from application specific data stored on tag, or
|
||||||
* null if it doesn't exist.
|
* null if it doesn't exist.
|
||||||
*
|
*
|
||||||
@ -316,7 +333,9 @@ public abstract class BaseNfcActivity extends BaseActivity {
|
|||||||
* @return a big integer representing the MPI for the given hash
|
* @return a big integer representing the MPI for the given hash
|
||||||
*/
|
*/
|
||||||
public byte[] nfcCalculateSignature(byte[] hash, int hashAlgo) throws IOException {
|
public byte[] nfcCalculateSignature(byte[] hash, int hashAlgo) throws IOException {
|
||||||
nfcVerifyPIN(0x81); // (Verify PW1 with mode 81 for signing)
|
if (!mPw1ValidatedForSignature) {
|
||||||
|
nfcVerifyPIN(0x81); // (Verify PW1 with mode 81 for signing)
|
||||||
|
}
|
||||||
|
|
||||||
// dsi, including Lc
|
// dsi, including Lc
|
||||||
String dsi;
|
String dsi;
|
||||||
@ -391,6 +410,10 @@ public abstract class BaseNfcActivity extends BaseActivity {
|
|||||||
|
|
||||||
Log.d(Constants.TAG, "final response:" + status);
|
Log.d(Constants.TAG, "final response:" + status);
|
||||||
|
|
||||||
|
if (!mPw1ValidForMultipleSignatures) {
|
||||||
|
mPw1ValidatedForSignature = false;
|
||||||
|
}
|
||||||
|
|
||||||
if ( ! "9000".equals(status)) {
|
if ( ! "9000".equals(status)) {
|
||||||
throw new IOException("Bad NFC response code: " + status);
|
throw new IOException("Bad NFC response code: " + status);
|
||||||
}
|
}
|
||||||
@ -410,7 +433,9 @@ public abstract class BaseNfcActivity extends BaseActivity {
|
|||||||
* @return the decoded session key
|
* @return the decoded session key
|
||||||
*/
|
*/
|
||||||
public byte[] nfcDecryptSessionKey(byte[] encryptedSessionKey) throws IOException {
|
public byte[] nfcDecryptSessionKey(byte[] encryptedSessionKey) throws IOException {
|
||||||
nfcVerifyPIN(0x82); // (Verify PW1 with mode 82 for decryption)
|
if (!mPw1ValidatedForDecrypt) {
|
||||||
|
nfcVerifyPIN(0x82); // (Verify PW1 with mode 82 for decryption)
|
||||||
|
}
|
||||||
|
|
||||||
String firstApdu = "102a8086fe";
|
String firstApdu = "102a8086fe";
|
||||||
String secondApdu = "002a808603";
|
String secondApdu = "002a808603";
|
||||||
@ -458,6 +483,12 @@ public abstract class BaseNfcActivity extends BaseActivity {
|
|||||||
handlePinError();
|
handlePinError();
|
||||||
throw new IOException("Bad PIN!");
|
throw new IOException("Bad PIN!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mode == 0x81) {
|
||||||
|
mPw1ValidatedForSignature = true;
|
||||||
|
} else if (mode == 0x82) {
|
||||||
|
mPw1ValidatedForDecrypt = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,6 +193,11 @@ public class Preferences {
|
|||||||
public final boolean searchKeybase;
|
public final boolean searchKeybase;
|
||||||
public final String keyserver;
|
public final String keyserver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param searchKeyserver should passed keyserver be searched
|
||||||
|
* @param searchKeybase should keybase.io be searched
|
||||||
|
* @param keyserver the keyserver url authority to search on
|
||||||
|
*/
|
||||||
public CloudSearchPrefs(boolean searchKeyserver, boolean searchKeybase, String keyserver) {
|
public CloudSearchPrefs(boolean searchKeyserver, boolean searchKeybase, String keyserver) {
|
||||||
this.searchKeyserver = searchKeyserver;
|
this.searchKeyserver = searchKeyserver;
|
||||||
this.searchKeybase = searchKeybase;
|
this.searchKeybase = searchKeybase;
|
||||||
|
@ -404,6 +404,9 @@
|
|||||||
<string name="import_qr_code_button">"Scan QR Code"</string>
|
<string name="import_qr_code_button">"Scan QR Code"</string>
|
||||||
<string name="import_qr_code_text">"Place your camera over the QR Code!"</string>
|
<string name="import_qr_code_text">"Place your camera over the QR Code!"</string>
|
||||||
|
|
||||||
|
<!-- Import from URL -->
|
||||||
|
<string name="import_url_warn_no_search_parameter">"No search parameter found. You may still attempt manually searching the keyserver."</string>
|
||||||
|
|
||||||
<!-- Generic result toast -->
|
<!-- Generic result toast -->
|
||||||
<string name="view_log">"Details"</string>
|
<string name="view_log">"Details"</string>
|
||||||
<string name="with_warnings">", with warnings"</string>
|
<string name="with_warnings">", with warnings"</string>
|
||||||
|
Loading…
Reference in New Issue
Block a user