initial UI work to allow setting up accounts from certifcates
This commit is contained in:
		
							parent
							
								
									ef605e4cbd
								
							
						
					
					
						commit
						b23cb5a9e4
					
				| @ -29,6 +29,7 @@ dependencies { | ||||
| 	compile project(':libs:MemorizingTrustManager') | ||||
| 	compile 'com.android.support:support-v13:23.0.1' | ||||
| 	compile 'org.bouncycastle:bcprov-jdk15on:1.52' | ||||
| 	compile 'org.bouncycastle:bcmail-jdk15on:1.52' | ||||
| 	compile 'org.jitsi:org.otr4j:0.22' | ||||
| 	compile 'org.gnu.inet:libidn:1.15' | ||||
| 	compile 'com.google.zxing:core:3.2.1' | ||||
|  | ||||
| @ -194,18 +194,14 @@ public class Account extends AbstractEntity { | ||||
| 		return jid.getLocalpart(); | ||||
| 	} | ||||
| 
 | ||||
| 	public void setUsername(final String username) throws InvalidJidException { | ||||
| 		jid = Jid.fromParts(username, jid.getDomainpart(), jid.getResourcepart()); | ||||
| 	public void setJid(final Jid jid) { | ||||
| 		this.jid = jid; | ||||
| 	} | ||||
| 
 | ||||
| 	public Jid getServer() { | ||||
| 		return jid.toDomainJid(); | ||||
| 	} | ||||
| 
 | ||||
| 	public void setServer(final String server) throws InvalidJidException { | ||||
| 		jid = Jid.fromParts(jid.getLocalpart(), server, jid.getResourcepart()); | ||||
| 	} | ||||
| 
 | ||||
| 	public String getPassword() { | ||||
| 		return password; | ||||
| 	} | ||||
| @ -272,6 +268,14 @@ public class Account extends AbstractEntity { | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	public boolean setPrivateKeyAlias(String alias) { | ||||
| 		return setKey("private_key_alias", alias); | ||||
| 	} | ||||
| 
 | ||||
| 	public String getPrivateKeyAlias() { | ||||
| 		return getKey("private_key_alias"); | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public ContentValues getContentValues() { | ||||
| 		final ContentValues values = new ContentValues(); | ||||
|  | ||||
| @ -25,6 +25,8 @@ import android.os.PowerManager.WakeLock; | ||||
| import android.os.SystemClock; | ||||
| import android.preference.PreferenceManager; | ||||
| import android.provider.ContactsContract; | ||||
| import android.security.KeyChain; | ||||
| import android.security.KeyChainException; | ||||
| import android.util.Log; | ||||
| import android.util.LruCache; | ||||
| 
 | ||||
| @ -34,11 +36,22 @@ import net.java.otr4j.session.SessionID; | ||||
| import net.java.otr4j.session.SessionImpl; | ||||
| import net.java.otr4j.session.SessionStatus; | ||||
| 
 | ||||
| import org.bouncycastle.asn1.x500.RDN; | ||||
| import org.bouncycastle.asn1.x500.X500Name; | ||||
| import org.bouncycastle.asn1.x500.style.BCStyle; | ||||
| import org.bouncycastle.asn1.x500.style.IETFUtils; | ||||
| import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; | ||||
| import org.bouncycastle.jce.PrincipalUtil; | ||||
| import org.bouncycastle.jce.X509Principal; | ||||
| import org.openintents.openpgp.util.OpenPgpApi; | ||||
| import org.openintents.openpgp.util.OpenPgpServiceConnection; | ||||
| 
 | ||||
| import java.math.BigInteger; | ||||
| import java.security.PrivateKey; | ||||
| import java.security.SecureRandom; | ||||
| import java.security.cert.CertificateEncodingException; | ||||
| import java.security.cert.CertificateParsingException; | ||||
| import java.security.cert.X509Certificate; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Arrays; | ||||
| import java.util.Collection; | ||||
| @ -1285,6 +1298,43 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa | ||||
| 		updateAccountUi(); | ||||
| 	} | ||||
| 
 | ||||
| 	public void createAccountFromKey(final String alias, final OnAccountCreated callback) { | ||||
| 		new Thread(new Runnable() { | ||||
| 			@Override | ||||
| 			public void run() { | ||||
| 				try { | ||||
| 					X509Certificate[] chain = KeyChain.getCertificateChain(XmppConnectionService.this, alias); | ||||
| 					PrivateKey key = KeyChain.getPrivateKey(XmppConnectionService.this, alias); | ||||
| 					X500Name x500name = new JcaX509CertificateHolder(chain[0]).getSubject(); | ||||
| 					String email = IETFUtils.valueToString(x500name.getRDNs(BCStyle.EmailAddress)[0].getFirst().getValue()); | ||||
| 					String name = IETFUtils.valueToString(x500name.getRDNs(BCStyle.CN)[0].getFirst().getValue()); | ||||
| 					Jid jid = Jid.fromString(email); | ||||
| 					if (findAccountByJid(jid) == null) { | ||||
| 						Account account = new Account(jid, ""); | ||||
| 						account.setPrivateKeyAlias(alias); | ||||
| 						account.setOption(Account.OPTION_DISABLED, true); | ||||
| 						createAccount(account); | ||||
| 						callback.onAccountCreated(account); | ||||
| 					} else { | ||||
| 						callback.informUser(R.string.account_already_exists); | ||||
| 					} | ||||
| 				} catch (KeyChainException e) { | ||||
| 					callback.informUser(R.string.unable_to_parse_certificate); | ||||
| 				} catch (InterruptedException e) { | ||||
| 					callback.informUser(R.string.unable_to_parse_certificate); | ||||
| 					e.printStackTrace(); | ||||
| 				} catch (CertificateEncodingException e) { | ||||
| 					callback.informUser(R.string.unable_to_parse_certificate); | ||||
| 					e.printStackTrace(); | ||||
| 				} catch (InvalidJidException e) { | ||||
| 					callback.informUser(R.string.unable_to_parse_certificate); | ||||
| 					e.printStackTrace(); | ||||
| 				} | ||||
| 			} | ||||
| 		}).start(); | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	public void updateAccount(final Account account) { | ||||
| 		this.statusListener.onStatusChanged(account); | ||||
| 		databaseBackend.updateAccount(account); | ||||
| @ -2699,54 +2749,59 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	public interface OnMoreMessagesLoaded { | ||||
| 		public void onMoreMessagesLoaded(int count, Conversation conversation); | ||||
| 	public interface OnAccountCreated { | ||||
| 		void onAccountCreated(Account account); | ||||
| 		void informUser(int r); | ||||
| 	} | ||||
| 
 | ||||
| 		public void informUser(int r); | ||||
| 	public interface OnMoreMessagesLoaded { | ||||
| 		void onMoreMessagesLoaded(int count, Conversation conversation); | ||||
| 
 | ||||
| 		void informUser(int r); | ||||
| 	} | ||||
| 
 | ||||
| 	public interface OnAccountPasswordChanged { | ||||
| 		public void onPasswordChangeSucceeded(); | ||||
| 		void onPasswordChangeSucceeded(); | ||||
| 
 | ||||
| 		public void onPasswordChangeFailed(); | ||||
| 		void onPasswordChangeFailed(); | ||||
| 	} | ||||
| 
 | ||||
| 	public interface OnAffiliationChanged { | ||||
| 		public void onAffiliationChangedSuccessful(Jid jid); | ||||
| 		void onAffiliationChangedSuccessful(Jid jid); | ||||
| 
 | ||||
| 		public void onAffiliationChangeFailed(Jid jid, int resId); | ||||
| 		void onAffiliationChangeFailed(Jid jid, int resId); | ||||
| 	} | ||||
| 
 | ||||
| 	public interface OnRoleChanged { | ||||
| 		public void onRoleChangedSuccessful(String nick); | ||||
| 		void onRoleChangedSuccessful(String nick); | ||||
| 
 | ||||
| 		public void onRoleChangeFailed(String nick, int resid); | ||||
| 		void onRoleChangeFailed(String nick, int resid); | ||||
| 	} | ||||
| 
 | ||||
| 	public interface OnConversationUpdate { | ||||
| 		public void onConversationUpdate(); | ||||
| 		void onConversationUpdate(); | ||||
| 	} | ||||
| 
 | ||||
| 	public interface OnAccountUpdate { | ||||
| 		public void onAccountUpdate(); | ||||
| 		void onAccountUpdate(); | ||||
| 	} | ||||
| 
 | ||||
| 	public interface OnRosterUpdate { | ||||
| 		public void onRosterUpdate(); | ||||
| 		void onRosterUpdate(); | ||||
| 	} | ||||
| 
 | ||||
| 	public interface OnMucRosterUpdate { | ||||
| 		public void onMucRosterUpdate(); | ||||
| 		void onMucRosterUpdate(); | ||||
| 	} | ||||
| 
 | ||||
| 	public interface OnConferenceConfigurationFetched { | ||||
| 		public void onConferenceConfigurationFetched(Conversation conversation); | ||||
| 		void onConferenceConfigurationFetched(Conversation conversation); | ||||
| 	} | ||||
| 
 | ||||
| 	public interface OnConferenceOptionsPushed { | ||||
| 		public void onPushSucceeded(); | ||||
| 		void onPushSucceeded(); | ||||
| 
 | ||||
| 		public void onPushFailed(); | ||||
| 		void onPushFailed(); | ||||
| 	} | ||||
| 
 | ||||
| 	public interface OnShowErrorToast { | ||||
|  | ||||
| @ -74,6 +74,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate | ||||
| 	private LinearLayout keysCard; | ||||
| 
 | ||||
| 	private Jid jidToEdit; | ||||
| 	private boolean mInitMode = false; | ||||
| 	private Account mAccount; | ||||
| 	private String messageFingerprint; | ||||
| 
 | ||||
| @ -83,6 +84,9 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate | ||||
| 
 | ||||
| 		@Override | ||||
| 		public void onClick(final View v) { | ||||
| 			if (mInitMode && mAccount != null) { | ||||
| 				mAccount.setOption(Account.OPTION_DISABLED, false); | ||||
| 			} | ||||
| 			if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED && !accountInfoEdited()) { | ||||
| 				mAccount.setOption(Account.OPTION_DISABLED, false); | ||||
| 				xmppConnectionService.updateAccount(mAccount); | ||||
| @ -129,12 +133,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate | ||||
| 				} | ||||
| 			} | ||||
| 			if (mAccount != null) { | ||||
| 				try { | ||||
| 					mAccount.setUsername(jid.hasLocalpart() ? jid.getLocalpart() : ""); | ||||
| 					mAccount.setServer(jid.getDomainpart()); | ||||
| 				} catch (final InvalidJidException ignored) { | ||||
| 					return; | ||||
| 				} | ||||
| 				mAccount.setJid(jid); | ||||
| 				mAccountJid.setError(null); | ||||
| 				mPasswordConfirm.setError(null); | ||||
| 				mAccount.setPassword(password); | ||||
| @ -152,9 +151,9 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate | ||||
| 				mAccount.setOption(Account.OPTION_REGISTER, registerNewAccount); | ||||
| 				xmppConnectionService.createAccount(mAccount); | ||||
| 			} | ||||
| 			if (jidToEdit != null | ||||
| 					&& !mAccount.isOptionSet(Account.OPTION_DISABLED) | ||||
| 					&& !registerNewAccount) { | ||||
| 			if (!mAccount.isOptionSet(Account.OPTION_DISABLED) | ||||
| 					&& !registerNewAccount | ||||
| 					&& !mInitMode) { | ||||
| 				finish(); | ||||
| 			} else { | ||||
| 				updateSaveButton(); | ||||
| @ -179,12 +178,10 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate | ||||
| 			startActivity(new Intent(getApplicationContext(), | ||||
| 						ManageAccountActivity.class)); | ||||
| 			finish(); | ||||
| 		} else if (jidToEdit == null && mAccount != null | ||||
| 				&& mAccount.getStatus() == Account.State.ONLINE) { | ||||
| 		} else if (mInitMode && mAccount != null && mAccount.getStatus() == Account.State.ONLINE) { | ||||
| 			if (!mFetchingAvatar) { | ||||
| 				mFetchingAvatar = true; | ||||
| 				xmppConnectionService.checkForAvatar(mAccount, | ||||
| 						mAvatarFetchCallback); | ||||
| 				xmppConnectionService.checkForAvatar(mAccount, mAvatarFetchCallback); | ||||
| 			} | ||||
| 		} else { | ||||
| 			updateSaveButton(); | ||||
| @ -236,8 +233,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate | ||||
| 		@Override | ||||
| 		public void onClick(final View view) { | ||||
| 			if (mAccount != null) { | ||||
| 				final Intent intent = new Intent(getApplicationContext(), | ||||
| 						PublishProfilePictureActivity.class); | ||||
| 				final Intent intent = new Intent(getApplicationContext(), PublishProfilePictureActivity.class); | ||||
| 				intent.putExtra("account", mAccount.getJid().toBareJid().toString()); | ||||
| 				startActivity(intent); | ||||
| 			} | ||||
| @ -269,7 +265,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate | ||||
| 	} | ||||
| 
 | ||||
| 	protected void updateSaveButton() { | ||||
| 		if (accountInfoEdited() && jidToEdit != null) { | ||||
| 		if (accountInfoEdited() && !mInitMode) { | ||||
| 			this.mSaveButton.setText(R.string.save); | ||||
| 			this.mSaveButton.setEnabled(true); | ||||
| 			this.mSaveButton.setTextColor(getPrimaryTextColor()); | ||||
| @ -277,14 +273,14 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate | ||||
| 			this.mSaveButton.setEnabled(false); | ||||
| 			this.mSaveButton.setTextColor(getSecondaryTextColor()); | ||||
| 			this.mSaveButton.setText(R.string.account_status_connecting); | ||||
| 		} else if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED) { | ||||
| 		} else if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED && !mInitMode) { | ||||
| 			this.mSaveButton.setEnabled(true); | ||||
| 			this.mSaveButton.setTextColor(getPrimaryTextColor()); | ||||
| 			this.mSaveButton.setText(R.string.enable); | ||||
| 		} else { | ||||
| 			this.mSaveButton.setEnabled(true); | ||||
| 			this.mSaveButton.setTextColor(getPrimaryTextColor()); | ||||
| 			if (jidToEdit != null) { | ||||
| 			if (!mInitMode) { | ||||
| 				if (mAccount != null && mAccount.isOnlineAndConnected()) { | ||||
| 					this.mSaveButton.setText(R.string.save); | ||||
| 					if (!accountInfoEdited()) { | ||||
| @ -421,8 +417,9 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate | ||||
| 			} catch (final InvalidJidException | NullPointerException ignored) { | ||||
| 				this.jidToEdit = null; | ||||
| 			} | ||||
| 			this.mInitMode = getIntent().getBooleanExtra("init", false) || this.jidToEdit == null; | ||||
| 			this.messageFingerprint = getIntent().getStringExtra("fingerprint"); | ||||
| 			if (this.jidToEdit != null) { | ||||
| 			if (!mInitMode) { | ||||
| 				this.mRegisterNew.setVisibility(View.GONE); | ||||
| 				if (getActionBar() != null) { | ||||
| 					getActionBar().setTitle(getString(R.string.account_details)); | ||||
| @ -440,7 +437,14 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate | ||||
| 	protected void onBackendConnected() { | ||||
| 		if (this.jidToEdit != null) { | ||||
| 			this.mAccount = xmppConnectionService.findAccountByJid(jidToEdit); | ||||
| 			updateAccountInformation(true); | ||||
| 			if (this.mAccount != null) { | ||||
| 				if (this.mAccount.getPrivateKeyAlias() != null) { | ||||
| 				this.mPassword.setHint(R.string.authenticate_with_certificate); | ||||
| 				if (this.mInitMode) { | ||||
| 					this.mPassword.requestFocus(); | ||||
| 				} | ||||
| 			}	updateAccountInformation(true); | ||||
| 			} | ||||
| 		} else if (this.xmppConnectionService.getAccounts().size() == 0) { | ||||
| 			if (getActionBar() != null) { | ||||
| 				getActionBar().setDisplayHomeAsUpEnabled(false); | ||||
| @ -492,7 +496,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate | ||||
| 			} | ||||
| 			this.mPassword.setText(this.mAccount.getPassword()); | ||||
| 		} | ||||
| 		if (this.jidToEdit != null) { | ||||
| 		if (!mInitMode) { | ||||
| 			this.mAvatar.setVisibility(View.VISIBLE); | ||||
| 			this.mAvatar.setImageBitmap(avatarService().get(this.mAccount, getPixel(72))); | ||||
| 		} | ||||
|  | ||||
| @ -5,6 +5,9 @@ import android.content.DialogInterface; | ||||
| import android.content.DialogInterface.OnClickListener; | ||||
| import android.content.Intent; | ||||
| import android.os.Bundle; | ||||
| import android.security.KeyChain; | ||||
| import android.security.KeyChainAliasCallback; | ||||
| import android.util.Log; | ||||
| import android.view.ContextMenu; | ||||
| import android.view.ContextMenu.ContextMenuInfo; | ||||
| import android.view.Menu; | ||||
| @ -14,6 +17,7 @@ import android.widget.AdapterView; | ||||
| import android.widget.AdapterView.AdapterContextMenuInfo; | ||||
| import android.widget.AdapterView.OnItemClickListener; | ||||
| import android.widget.ListView; | ||||
| import android.widget.Toast; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| @ -21,10 +25,11 @@ import java.util.List; | ||||
| import eu.siacs.conversations.Config; | ||||
| import eu.siacs.conversations.R; | ||||
| import eu.siacs.conversations.entities.Account; | ||||
| import eu.siacs.conversations.services.XmppConnectionService; | ||||
| import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate; | ||||
| import eu.siacs.conversations.ui.adapter.AccountAdapter; | ||||
| 
 | ||||
| public class ManageAccountActivity extends XmppActivity implements OnAccountUpdate { | ||||
| public class ManageAccountActivity extends XmppActivity implements OnAccountUpdate, KeyChainAliasCallback, XmppConnectionService.OnAccountCreated { | ||||
| 
 | ||||
| 	protected Account selectedAccount = null; | ||||
| 
 | ||||
| @ -61,7 +66,7 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda | ||||
| 
 | ||||
| 			@Override | ||||
| 			public void onItemClick(AdapterView<?> arg0, View view, | ||||
| 					int position, long arg3) { | ||||
| 									int position, long arg3) { | ||||
| 				switchToAccount(accountList.get(position)); | ||||
| 			} | ||||
| 		}); | ||||
| @ -144,6 +149,9 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda | ||||
| 			case R.id.action_enable_all: | ||||
| 				enableAllAccounts(); | ||||
| 				break; | ||||
| 			case R.id.action_add_account_from_key: | ||||
| 				addAccountFromKey(); | ||||
| 				break; | ||||
| 			default: | ||||
| 				break; | ||||
| 		} | ||||
| @ -179,6 +187,10 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	private void addAccountFromKey() { | ||||
| 		KeyChain.choosePrivateKeyAlias(this, this, null, null, null, -1, null); | ||||
| 	} | ||||
| 
 | ||||
| 	private void publishAvatar(Account account) { | ||||
| 		Intent intent = new Intent(getApplicationContext(), | ||||
| 				PublishProfilePictureActivity.class); | ||||
| @ -281,4 +293,26 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void alias(String alias) { | ||||
| 		if (alias != null) { | ||||
| 			xmppConnectionService.createAccountFromKey(alias, this); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void onAccountCreated(Account account) { | ||||
| 		switchToAccount(account, true); | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void informUser(final int r) { | ||||
| 		runOnUiThread(new Runnable() { | ||||
| 			@Override | ||||
| 			public void run() { | ||||
| 				Toast.makeText(ManageAccountActivity.this,r,Toast.LENGTH_LONG).show(); | ||||
| 			} | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -435,8 +435,13 @@ public abstract class XmppActivity extends Activity { | ||||
| 	} | ||||
| 
 | ||||
| 	public void switchToAccount(Account account) { | ||||
| 		switchToAccount(account,false); | ||||
| 	} | ||||
| 
 | ||||
| 	public void switchToAccount(Account account, boolean init) { | ||||
| 		Intent intent = new Intent(this, EditAccountActivity.class); | ||||
| 		intent.putExtra("jid", account.getJid().toBareJid().toString()); | ||||
| 		intent.putExtra("init", init); | ||||
| 		startActivity(intent); | ||||
| 	} | ||||
| 
 | ||||
|  | ||||
| @ -6,6 +6,12 @@ | ||||
| 		android:icon="?attr/icon_add_person" | ||||
| 		android:showAsAction="always" | ||||
| 		android:title="@string/action_add_account"/> | ||||
| 	<item | ||||
| 		android:id="@+id/action_add_account_from_key" | ||||
| 		android:showAsAction="never" | ||||
| 		android:icon="?attr/icon_add_person" | ||||
| 		android:title="@string/action_add_account_from_key" | ||||
| 		android:visible="false"/> | ||||
| 	<item | ||||
| 		android:id="@+id/action_enable_all" | ||||
| 		android:title="@string/enable_all_accounts"/> | ||||
|  | ||||
| @ -524,4 +524,7 @@ | ||||
| 	<string name="pref_away_when_screen_off_summary">Marks your resource as away when the screen is turned off</string> | ||||
| 	<string name="pref_xa_on_silent_mode">Not available in silent mode</string> | ||||
| 	<string name="pref_xa_on_silent_mode_summary">Marks your resource as not available when phone is in silent mode</string> | ||||
| 	<string name="action_add_account_from_key">Add account from key</string> | ||||
| 	<string name="unable_to_parse_certificate">Unable to parse certificate</string> | ||||
| 	<string name="authenticate_with_certificate">Leave empty to authenticate w/ certificate</string> | ||||
| </resources> | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Daniel Gultsch
						Daniel Gultsch