diff --git a/src/main/java/eu/siacs/conversations/entities/Account.java b/src/main/java/eu/siacs/conversations/entities/Account.java index 10f2940c2..1d0a025f7 100644 --- a/src/main/java/eu/siacs/conversations/entities/Account.java +++ b/src/main/java/eu/siacs/conversations/entities/Account.java @@ -280,10 +280,11 @@ public class Account extends AbstractEntity { return values; } - public OtrEngine getOtrEngine(XmppConnectionService context) { - if (otrEngine == null) { - otrEngine = new OtrEngine(context, this); - } + public void initOtrEngine(XmppConnectionService context) { + this.otrEngine = new OtrEngine(context, this); + } + + public OtrEngine getOtrEngine() { return this.otrEngine; } @@ -298,23 +299,21 @@ public class Account extends AbstractEntity { public String getOtrFingerprint() { if (this.otrFingerprint == null) { try { - DSAPublicKey pubkey = (DSAPublicKey) this.otrEngine - .getPublicKey(); - if (pubkey == null) { + if (this.otrEngine == null) { return null; } - StringBuilder builder = new StringBuilder( - new OtrCryptoEngineImpl().getFingerprint(pubkey)); - builder.insert(8, " "); - builder.insert(17, " "); - builder.insert(26, " "); - builder.insert(35, " "); - this.otrFingerprint = builder.toString(); + DSAPublicKey publicKey = (DSAPublicKey) this.otrEngine.getPublicKey(); + if (publicKey == null) { + return null; + } + this.otrFingerprint = new OtrCryptoEngineImpl().getFingerprint(publicKey); + return this.otrFingerprint; } catch (final OtrCryptoException ignored) { - + return null; } + } else { + return this.otrFingerprint; } - return this.otrFingerprint; } public String getRosterVersion() { @@ -329,11 +328,6 @@ public class Account extends AbstractEntity { this.rosterVersion = version; } - public String getOtrFingerprint(XmppConnectionService service) { - this.getOtrEngine(service); - return this.getOtrFingerprint(); - } - public void updatePresence(String resource, int status) { this.presences.updatePresence(resource, status); } @@ -411,4 +405,13 @@ public class Account extends AbstractEntity { public boolean inGracePeriod() { return SystemClock.elapsedRealtime() < this.mEndGracePeriod; } + + public String getShareableUri() { + String fingerprint = this.getOtrFingerprint(); + if (fingerprint != null) { + return "xmpp:" + this.getJid().toBareJid().toString() + "?otr-fingerprint="+fingerprint; + } else { + return "xmpp:" + this.getJid().toBareJid().toString(); + } + } } diff --git a/src/main/java/eu/siacs/conversations/entities/Contact.java b/src/main/java/eu/siacs/conversations/entities/Contact.java index 9a827f854..32e4601dc 100644 --- a/src/main/java/eu/siacs/conversations/entities/Contact.java +++ b/src/main/java/eu/siacs/conversations/entities/Contact.java @@ -7,6 +7,7 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import java.util.ArrayList; import java.util.HashSet; import java.util.Set; @@ -206,24 +207,26 @@ public class Contact implements ListItem { return systemAccount; } - public Set getOtrFingerprints() { - Set set = new HashSet<>(); + public ArrayList getOtrFingerprints() { + ArrayList fingerprints = new ArrayList(); try { if (this.keys.has("otr_fingerprints")) { - JSONArray fingerprints = this.keys + JSONArray prints = this.keys .getJSONArray("otr_fingerprints"); - for (int i = 0; i < fingerprints.length(); ++i) { - set.add(fingerprints.getString(i)); + for (int i = 0; i < prints.length(); ++i) { + fingerprints.add(prints.getString(i)); } } - } catch (JSONException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + } catch (final JSONException ignored) { + } - return set; + return fingerprints; } - public void addOtrFingerprint(String print) { + public boolean addOtrFingerprint(String print) { + if (getOtrFingerprints().contains(print)) { + return false; + } try { JSONArray fingerprints; if (!this.keys.has("otr_fingerprints")) { @@ -234,8 +237,9 @@ public class Contact implements ListItem { } fingerprints.put(print); this.keys.put("otr_fingerprints", fingerprints); + return true; } catch (final JSONException ignored) { - + return false; } } @@ -396,4 +400,13 @@ public class Contact implements ListItem { public boolean trusted() { return getOption(Options.FROM) && getOption(Options.TO); } + + public String getShareableUri() { + if (getOtrFingerprints().size() >= 1) { + String otr = getOtrFingerprints().get(0); + return "xmpp:"+getJid().toBareJid().toString()+"?otr-fingerprint="+otr.replace(" ",""); + } else { + return "xmpp:"+getJid().toBareJid().toString(); + } + } } diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java index a9f4c0f1c..b1df7ec77 100644 --- a/src/main/java/eu/siacs/conversations/entities/Conversation.java +++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java @@ -240,16 +240,14 @@ public class Conversation extends AbstractEntity { this.mode = mode; } - public SessionImpl startOtrSession(XmppConnectionService service, - String presence, boolean sendStart) { + public SessionImpl startOtrSession(String presence, boolean sendStart) { if (this.otrSession != null) { return this.otrSession; } else { final SessionID sessionId = new SessionID(this.getContactJid().toBareJid().toString(), presence, "xmpp"); - this.otrSession = new SessionImpl(sessionId, getAccount() - .getOtrEngine(service)); + this.otrSession = new SessionImpl(sessionId, getAccount().getOtrEngine()); try { if (sendStart) { this.otrSession.startSession(); diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index bc2db87fc..b7aef9313 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -76,8 +76,7 @@ public class MessageParser extends AbstractParser implements } if (!conversation.hasValidOtrSession()) { if (properlyAddressed) { - conversation.startOtrSession(mXmppConnectionService, presence, - false); + conversation.startOtrSession(presence,false); } else { return null; } @@ -87,8 +86,7 @@ public class MessageParser extends AbstractParser implements if (!foreignPresence.equals(presence)) { conversation.endOtrIfNeeded(); if (properlyAddressed) { - conversation.startOtrSession(mXmppConnectionService, - presence, false); + conversation.startOtrSession(presence, false); } else { return null; } diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 0c5dfd961..509030c6e 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -491,6 +491,7 @@ public class XmppConnectionService extends Service { this.accounts = databaseBackend.getAccounts(); for (Account account : this.accounts) { + account.initOtrEngine(this); this.databaseBackend.readRoster(account.getRoster()); } this.mergePhoneContactsWithRoster(); @@ -608,8 +609,7 @@ public class XmppConnectionService extends Service { if (message.getCounterpart() != null) { if (message.getEncryption() == Message.ENCRYPTION_OTR) { if (!conv.hasValidOtrSession()) { - conv.startOtrSession(this, message.getCounterpart().getResourcepart(), - true); + conv.startOtrSession(message.getCounterpart().getResourcepart(),true); message.setStatus(Message.STATUS_WAITING); } else if (conv.hasValidOtrSession() && conv.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) { @@ -628,7 +628,7 @@ public class XmppConnectionService extends Service { } else { if (message.getEncryption() == Message.ENCRYPTION_OTR) { if (!conv.hasValidOtrSession() && (message.getCounterpart() != null)) { - conv.startOtrSession(this, message.getCounterpart().getResourcepart(), true); + conv.startOtrSession(message.getCounterpart().getResourcepart(), true); message.setStatus(Message.STATUS_WAITING); } else if (conv.hasValidOtrSession()) { if (conv.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) { @@ -672,7 +672,7 @@ public class XmppConnectionService extends Service { } else if (message.getEncryption() == Message.ENCRYPTION_OTR) { if (!conv.hasValidOtrSession() && message.getCounterpart() != null) { - conv.startOtrSession(this, message.getCounterpart().getResourcepart(), false); + conv.startOtrSession(message.getCounterpart().getResourcepart(), false); } } } @@ -709,13 +709,11 @@ public class XmppConnectionService extends Service { if (!message.getConversation().hasValidOtrSession()) { if ((message.getCounterpart() != null) && (presences.has(message.getCounterpart().getResourcepart()))) { - message.getConversation().startOtrSession(this, - message.getCounterpart().getResourcepart(), true); + message.getConversation().startOtrSession(message.getCounterpart().getResourcepart(), true); } else { if (presences.size() == 1) { String presence = presences.asStringArray()[0]; - message.getConversation().startOtrSession(this, - presence, true); + message.getConversation().startOtrSession(presence, true); } } } else { @@ -1061,6 +1059,7 @@ public class XmppConnectionService extends Service { } public void createAccount(Account account) { + account.initOtrEngine(this); databaseBackend.createAccount(account); this.accounts.add(account); this.reconnectAccount(account, false); diff --git a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java index 96fb7b618..788244e36 100644 --- a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java @@ -154,7 +154,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd @Override protected String getShareableUri() { if (contact != null) { - return "xmpp:" + contact.getJid(); + return contact.getShareableUri(); } else { return ""; } @@ -326,10 +326,8 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd keys.removeAllViews(); boolean hasKeys = false; LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); - for (Iterator iterator = contact.getOtrFingerprints() - .iterator(); iterator.hasNext(); ) { + for(final String otrFingerprint : contact.getOtrFingerprints()) { hasKeys = true; - final String otrFingerprint = iterator.next(); View view = inflater.inflate(R.layout.contact_key, keys, false); TextView key = (TextView) view.findViewById(R.id.key); TextView keyType = (TextView) view.findViewById(R.id.key_type); diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java index 7e28cc6c3..5dccde184 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java @@ -108,7 +108,7 @@ public class ConversationActivity extends XmppActivity implements protected String getShareableUri() { Conversation conversation = getSelectedConversation(); if (conversation != null) { - return "xmpp:" + conversation.getAccount().getJid().toBareJid(); + return conversation.getAccount().getShareableUri(); } else { return ""; } diff --git a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java index dc8185994..b9772bd7a 100644 --- a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java @@ -26,6 +26,7 @@ import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate; import eu.siacs.conversations.ui.adapter.KnownHostsAdapter; +import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.utils.Validator; import eu.siacs.conversations.xmpp.XmppConnection.Features; @@ -270,7 +271,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate @Override protected String getShareableUri() { if (mAccount!=null) { - return "xmpp:"+mAccount.getJid().toBareJid(); + return mAccount.getShareableUri(); } else { return ""; } @@ -402,11 +403,10 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate } else { this.mServerInfoPep.setText(R.string.server_info_unavailable); } - final String fingerprint = this.mAccount - .getOtrFingerprint(xmppConnectionService); + final String fingerprint = this.mAccount.getOtrFingerprint(); if (fingerprint != null) { this.mOtrFingerprintBox.setVisibility(View.VISIBLE); - this.mOtrFingerprint.setText(fingerprint); + this.mOtrFingerprint.setText(CryptoHelper.prettifyFingerprint(fingerprint)); this.mOtrFingerprintToClipboardButton .setVisibility(View.VISIBLE); this.mOtrFingerprintToClipboardButton diff --git a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java index 766b39619..fe188737f 100644 --- a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java @@ -62,6 +62,7 @@ import eu.siacs.conversations.entities.ListItem; import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate; import eu.siacs.conversations.ui.adapter.KnownHostsAdapter; import eu.siacs.conversations.ui.adapter.ListItemAdapter; +import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.Validator; import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.Jid; @@ -316,7 +317,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU } @SuppressLint("InflateParams") - protected void showCreateContactDialog(String prefilledJid) { + protected void showCreateContactDialog(final String prefilledJid, final String fingerprint) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(R.string.create_contact); View dialogView = getLayoutInflater().inflate( @@ -328,6 +329,12 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU android.R.layout.simple_list_item_1, mKnownHosts)); if (prefilledJid != null) { jid.append(prefilledJid); + if (fingerprint!=null) { + jid.setFocusable(false); + jid.setFocusableInTouchMode(false); + jid.setClickable(false); + jid.setCursorVisible(false); + } } populateAccountSpinner(spinner); builder.setView(dialogView); @@ -367,6 +374,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU if (contact.showInRoster()) { jid.setError(getString(R.string.contact_already_exists)); } else { + contact.addOtrFingerprint(fingerprint); xmppConnectionService.createContact(contact); dialog.dismiss(); switchToConversation(contact); @@ -511,7 +519,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.action_create_contact: - showCreateContactDialog(null); + showCreateContactDialog(null,null); return true; case R.id.action_join_conference: showJoinConferenceDialog(null); @@ -615,22 +623,29 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU return false; } - private boolean handleJid(String jid) { - List contacts = xmppConnectionService.findContacts(jid); + private boolean handleJid(Invite invite) { + List contacts = xmppConnectionService.findContacts(invite.jid); if (contacts.size() == 0) { - showCreateContactDialog(jid); + showCreateContactDialog(invite.jid,invite.fingerprint); return false; } else if (contacts.size() == 1) { - switchToConversation(contacts.get(0)); + Contact contact = contacts.get(0); + if (invite.fingerprint != null) { + if (contact.addOtrFingerprint(invite.fingerprint)) { + Log.d(Config.LOGTAG,"added new fingerprint"); + xmppConnectionService.syncRosterToDisk(contact.getAccount()); + } + } + switchToConversation(contact); return true; } else { if (mMenuSearchView != null) { mMenuSearchView.expandActionView(); mSearchEditText.setText(""); - mSearchEditText.append(jid); - filter(jid); + mSearchEditText.append(invite.jid); + filter(invite.jid); } else { - mInitialJid = jid; + mInitialJid = invite.jid; } return true; } @@ -743,6 +758,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU private class Invite { private String jid; private boolean muc; + private String fingerprint; Invite(Uri uri) { parse(uri); @@ -761,7 +777,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU if (muc) { showJoinConferenceDialog(jid); } else { - return handleJid(jid); + return handleJid(this); } } return false; @@ -777,6 +793,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU } else { jid = uri.getSchemeSpecificPart().split("\\?")[0]; } + fingerprint = parseFingerprint(uri.getQuery()); } else if ("imto".equals(scheme)) { // sample: imto://xmpp/jid@foo.com try { @@ -785,5 +802,19 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU } } } + + String parseFingerprint(String query) { + if (query == null) { + return null; + } else { + final String NEEDLE = "otr-fingerprint="; + int index = query.indexOf(NEEDLE); + if (index >= 0 && query.length() >= (NEEDLE.length() + index + 40)) { + return CryptoHelper.prettifyFingerprint(query.substring(index + NEEDLE.length(), index + NEEDLE.length() + 40)); + } else { + return null; + } + } + } } } diff --git a/src/main/java/eu/siacs/conversations/ui/VerifyOTRActivity.java b/src/main/java/eu/siacs/conversations/ui/VerifyOTRActivity.java index af3194e9f..bffc97433 100644 --- a/src/main/java/eu/siacs/conversations/ui/VerifyOTRActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/VerifyOTRActivity.java @@ -16,6 +16,7 @@ import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.services.XmppConnectionService; +import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.Jid; @@ -184,7 +185,7 @@ public class VerifyOTRActivity extends XmppActivity implements XmppConnectionSer this.mVerificationAreaOne.setVisibility(View.VISIBLE); this.mVerificationAreaTwo.setVisibility(View.VISIBLE); this.mErrorNoSession.setVisibility(View.GONE); - this.mYourFingerprint.setText(this.mAccount.getOtrFingerprint(xmppConnectionService)); + this.mYourFingerprint.setText(CryptoHelper.prettifyFingerprint(this.mAccount.getOtrFingerprint())); this.mRemoteFingerprint.setText(this.mConversation.getOtrFingerprint()); this.mRemoteJid.setText(this.mConversation.getContact().getJid().toBareJid().toString()); Conversation.Smp smp = mConversation.smp(); @@ -279,7 +280,7 @@ public class VerifyOTRActivity extends XmppActivity implements XmppConnectionSer @Override protected String getShareableUri() { if (mAccount!=null) { - return "xmpp:"+mAccount.getJid().toBareJid(); + return mAccount.getShareableUri(); } else { return ""; } diff --git a/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java b/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java index f7126a2f9..b4a6e65c5 100644 --- a/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java @@ -82,4 +82,13 @@ public class CryptoHelper { public static String saslPrep(final String s) { return saslEscape(Normalizer.normalize(s, Normalizer.Form.NFKC)); } + + public static String prettifyFingerprint(String fingerprint) { + StringBuilder builder = new StringBuilder(fingerprint); + builder.insert(8, " "); + builder.insert(17, " "); + builder.insert(26, " "); + builder.insert(35, " "); + return builder.toString(); + } }