/*
Copyright (c) 2013 The MITRE Corporation, All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this work except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package org.mitre.svmp.activities;
import android.annotation.TargetApi;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.security.KeyChain;
import android.security.KeyChainAliasCallback;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import org.mitre.svmp.common.ConnectionInfo;
import org.mitre.svmp.common.Constants;
import org.mitre.svmp.auth.AuthRegistry;
import org.mitre.svmp.auth.module.CertificateModule;
import org.mitre.svmp.auth.type.IAuthType;
import org.mitre.svmp.client.R;
import org.mitre.svmp.common.Utility;
import org.mitre.svmp.widgets.AuthModuleArrayAdapter;
/**
* @author Joe Portner
*/
public class ConnectionDetails extends SvmpActivity {
private static String TAG = ConnectionDetails.class.getName();
// the ID of the ConnectionInfo we are updating (0 is a new ConnectionInfo)
private int updateID = 0;
private boolean certAliasIsSet = false; // determines whether a valid certificate is selected
// views
private EditText
descriptionView,
usernameView,
hostView,
portView;
private Spinner
encryptionView,
authTypeView;
private Button certificateView;
private IAuthType[] authTypes;
public void onCreate(Bundle savedInstanceState) {
this.repopulateOnResume = false;
super.onCreate(savedInstanceState, R.layout.connection_details);
}
@Override
protected void populateLayout() {
// get views
descriptionView = (EditText) findViewById(R.id.connectionDetails_editText_description);
usernameView = (EditText) findViewById(R.id.connectionDetails_editText_username);
hostView = (EditText) findViewById(R.id.connectionDetails_editText_host);
portView = (EditText) findViewById(R.id.connectionDetails_editText_port);
encryptionView = (Spinner) findViewById(R.id.connectionDetails_spinner_encryption);
authTypeView = (Spinner) findViewById(R.id.connectionDetails_spinner_authType);
certificateView = (Button) findViewById(R.id.connectionDetails_button_certificate);
// populate items for AuthType spinner
authTypes = AuthRegistry.getAuthTypes();
AuthModuleArrayAdapter adapter = new AuthModuleArrayAdapter(this, authTypes);
authTypeView.setAdapter(adapter);
// check if an existing ConnectionInfo ID was sent with the Intent
Intent intent = getIntent();
if( intent.hasExtra("id") ) {
// get the ID that was sent with the Intent
int id = intent.getIntExtra("id", 0);
// get the associated ConnectionInfo
ConnectionInfo connectionInfo = dbHandler.getConnectionInfo(id);
// if ConnectionInfo is null, stop
if( connectionInfo == null ) {
Log.d(TAG, "Expected ConnectionInfo is null");
finishMessage(R.string.connectionList_toast_notFound, RESULT_REPOPULATE);
}
// ConnectionInfo is good, continue
else {
// populate views based on ConnectionInfo
descriptionView.setText(connectionInfo.getDescription());
usernameView.setText(connectionInfo.getUsername());
hostView.setText(connectionInfo.getHost());
portView.setText(String.valueOf(connectionInfo.getPort()));
encryptionView.setSelection(connectionInfo.getEncryptionType());
for (int i = 0; i < authTypes.length; i++)
if (authTypes[i].getID() == connectionInfo.getAuthType()) {
authTypeView.setSelection(i);
break;
}
if (connectionInfo.getCertificateAlias().length() > 0)
setCertAlias(connectionInfo.getCertificateAlias());
// flag so we know this is an update, not insert
updateID = connectionInfo.getConnectionID();
}
}
// this is a new connection, fill in the default port
else {
portView.setText(String.valueOf(Constants.DEFAULT_PORT));
encryptionView.setSelection(ENCRYPTION_SSLTLS); // by default, encryption is turned on
}
if (!Constants.API_14) {
// the SDK is lower than ICS, remove the Certificate selection table rows
findViewById(R.id.connectionDetails_tableRow_certificate_1).setVisibility(View.GONE);
findViewById(R.id.connectionDetails_tableRow_certificate_2).setVisibility(View.GONE);
}
else {
// add listener for AuthType spinner
authTypeView.setOnItemSelectedListener(authTypeSelectedListener);
// check if the selected AuthType uses the CertificateModule
boolean enabled = checkAuthTypeCert(authTypeView.getSelectedItemPosition());
// set the Certificate button to either enabled or disabled, based on the AuthType
certificateView.setEnabled(enabled);
}
}
// called onResume so preference changes take effect in the layout
@Override
protected void refreshPreferences() {
// only allow changing the encryption type if the correct Preference is set
encryptionView.setEnabled(Utility.getPrefBool(this,
R.string.preferenceKey_connection_showEncryption,
R.string.preferenceValue_connection_showEncryption));
}
// save button is clicked
public void onClick_Save(View v) {
// get user input
String description = descriptionView.getText().toString(),
username = usernameView.getText().toString(),
host = hostView.getText().toString(),
portString = portView.getText().toString();
int port = 0;
try {
port = Integer.parseInt(portString);
} catch( NumberFormatException e ) {
// don't care
}
int encryptionType = encryptionView.getSelectedItemPosition(),
authType = authTypes[authTypeView.getSelectedItemPosition()].getID();
String certificateAlias = getCertificateAlias();
boolean certAuthType = checkAuthTypeCert(authTypeView.getSelectedItemPosition());
// validate input
if( port < 1 || port > 65535 )
toastShort(R.string.connectionDetails_toast_invalidPort);
else if( description.length() == 0 )
toastShort(R.string.connectionDetails_toast_blankDescription);
else if( dbHandler.getConnectionInfo(updateID, description) != null )
toastShort(R.string.connectionDetails_toast_ambiguousDescription);
else if( username.length() == 0 && !certAuthType ) // username is needed if not using certificate authentication
toastShort(R.string.connectionDetails_toast_blankUsername);
else if( host.length() == 0 )
toastShort(R.string.connectionDetails_toast_blankHost);
else if( encryptionType == Constants.ENCRYPTION_NONE && certAuthType )
toastShort(R.string.connectionDetails_toast_certAuthNeedsSsl);
else if( certAuthType && !certAliasIsSet)
toastShort(R.string.connectionDetails_toast_certAuthNeedsAlias);
else {
// create a new ConnectionInfo object
ConnectionInfo connectionInfo = new ConnectionInfo(updateID, description, username, host, port,
encryptionType, authType, certificateAlias, 0);
// insert or update the ConnectionInfo in the database
long result;
if( updateID > 0 )
result = dbHandler.updateConnectionInfo(connectionInfo);
else
result = dbHandler.insertConnectionInfo(connectionInfo);
// exit and resume previous activity, report results in the intent
if( result > -1 && updateID > 0 ) {
// we have updated this ConnectionInfo; if session info is stored for this ConnectionInfo, remove it
dbHandler.clearSessionInfo(connectionInfo);
finishMessage(R.string.connectionList_toast_updated, RESULT_REPOPULATE);
}
else if( result > -1 )
finishMessage(R.string.connectionList_toast_added, RESULT_REPOPULATE);
else
finishMessage(R.string.connectionList_toast_error, RESULT_REPOPULATE);
}
}
// cancel button is clicked
public void onClick_Cancel(View v) {
finish();
}
// certificate button is clicked
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public void onClick_Certificate(View v) {
if (Constants.API_14) {
// get the prior certificate alias (if it exists)
String certificateAlias = getCertificateAlias();
if (certificateAlias.length() == 0)
certificateAlias = null;
KeyChain.choosePrivateKeyAlias(this,
new KeyChainAliasCallback() {
@Override
public void alias(String s) {
final String alias = s;
// you can only modify views from the UI thread
runOnUiThread(new Runnable() {
@Override
public void run() {
setCertAlias(alias);
}
});
}
}, // KeyChainAliasCallback
null, // any key type
null, // any issuer
null, // any host
-1, // any port
certificateAlias // alias to preselect if available
);
}
}
// detects when the AuthType is changed
private OnItemSelectedListener authTypeSelectedListener = new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
// check if the selected AuthType uses the CertificateModule
boolean enabled = checkAuthTypeCert(position);
// set the Certificate button to either enabled or disabled, based on the AuthType
certificateView.setEnabled(enabled);
// if certificate authentication is enabled, disable the Username text box
usernameView.setEnabled(!enabled);
}
@Override
public void onNothingSelected(AdapterView<?> parentView) {
// set the Certificate button to disabled
certificateView.setEnabled(false);
// set the Username text box to enabled
usernameView.setEnabled(true);
}
};
// check if the selected AuthType uses the CertificateModule
private boolean checkAuthTypeCert(int position) {
return (authTypes[position].getID() & CertificateModule.AUTH_MODULE_ID) == CertificateModule.AUTH_MODULE_ID;
}
private String getCertificateAlias() {
String certificateAlias = certificateView.getText().toString();
if (!certAliasIsSet)
certificateAlias = "";
return certificateAlias;
}
private void setCertAlias(String s) {
certAliasIsSet = s != null;
if (certAliasIsSet)
certificateView.setText(s);
else
certificateView.setText(R.string.connectionDetails_button_certificate_none_text);
}
}