diff options
Diffstat (limited to '')
13 files changed, 482 insertions, 310 deletions
diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java index 58e5a0957..ab3aefac0 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java @@ -1,10 +1,10 @@ package eu.siacs.conversations.crypto.axolotl; import android.security.KeyChain; -import android.security.KeyChainException; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Log; +import android.util.Pair; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.whispersystems.libaxolotl.AxolotlAddress; @@ -20,11 +20,9 @@ import org.whispersystems.libaxolotl.state.PreKeyRecord; import org.whispersystems.libaxolotl.state.SignedPreKeyRecord; import org.whispersystems.libaxolotl.util.KeyHelper; -import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.Security; import java.security.Signature; -import java.security.SignatureException; import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.HashMap; @@ -43,12 +41,13 @@ import eu.siacs.conversations.parser.IqParser; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.utils.SerialSingleThreadExecutor; import eu.siacs.conversations.xml.Element; +import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesLoaded; import eu.siacs.conversations.xmpp.OnIqPacketReceived; import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.stanzas.IqPacket; -public class AxolotlService { +public class AxolotlService implements OnAdvancedStreamFeaturesLoaded { public static final String PEP_PREFIX = "eu.siacs.conversations.axolotl"; public static final String PEP_DEVICE_LIST = PEP_PREFIX + ".devicelist"; @@ -71,6 +70,15 @@ public class AxolotlService { private int numPublishTriesOnEmptyPep = 0; private boolean pepBroken = false; + @Override + public void onAdvancedStreamFeaturesAvailable(Account account) { + if (account.getXmppConnection().getFeatures().pep()) { + publishBundlesIfNeeded(true, false); + } else { + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": skipping OMEMO initialization"); + } + } + private static class AxolotlAddressMap<T> { protected Map<String, Map<Integer, T>> map; protected final Object MAP_LOCK = new Object(); @@ -167,9 +175,11 @@ public class AxolotlService { } } - private enum FetchStatus { + public enum FetchStatus { PENDING, SUCCESS, + SUCCESS_VERIFIED, + TIMEOUT, ERROR } @@ -313,7 +323,8 @@ public class AxolotlService { setTrustOnSessions(jid, newDevices, XmppAxolotlSession.Trust.INACTIVE_UNTRUSTED, XmppAxolotlSession.Trust.UNTRUSTED); this.deviceIds.put(jid, deviceIds); - mXmppConnectionService.keyStatusUpdated(); + findDevicesWithoutSession(jid); + mXmppConnectionService.keyStatusUpdated(null); } public void wipeOtherPepDevices() { @@ -402,7 +413,6 @@ public class AxolotlService { byte[] signature = verifier.sign(); IqPacket packet = mXmppConnectionService.getIqGenerator().publishVerification(signature, chain, getOwnDeviceId()); Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + ": publish verification for device "+getOwnDeviceId()); - Log.d(Config.LOGTAG,"verification : "+packet.toString()); mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() { @Override public void onIqPacketReceived(Account account, IqPacket packet) { @@ -565,6 +575,61 @@ public class AxolotlService { axolotlStore.setFingerprintTrust(fingerprint, trust); } + private void verifySessionWithPEP(final XmppAxolotlSession session, final IdentityKey identityKey) { + final AxolotlAddress address = session.getRemoteAddress(); + try { + IqPacket packet = mXmppConnectionService.getIqGenerator().retrieveVerificationForDevice(Jid.fromString(address.getName()), address.getDeviceId()); + mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + Pair<X509Certificate[],byte[]> verification = mXmppConnectionService.getIqParser().verification(packet); + if (verification != null) { + try { + Signature verifier = Signature.getInstance("sha256WithRSA"); + verifier.initVerify(verification.first[0]); + verifier.update(identityKey.serialize()); + if (verifier.verify(verification.second)) { + try { + mXmppConnectionService.getMemorizingTrustManager().getNonInteractive().checkClientTrusted(verification.first, "RSA"); + Log.d(Config.LOGTAG, "verified session with x.509 signature. fingerprint was: "+session.getFingerprint()); + setFingerprintTrust(session.getFingerprint(), XmppAxolotlSession.Trust.TRUSTED); + fetchStatusMap.put(address, FetchStatus.SUCCESS_VERIFIED); + finishBuildingSessionsFromPEP(address); + return; + } catch (Exception e) { + Log.d(Config.LOGTAG,"could not verify certificate"); + } + } + } catch (Exception e) { + Log.d(Config.LOGTAG, "error during verification " + e.getMessage()); + } + } + fetchStatusMap.put(address, FetchStatus.SUCCESS); + finishBuildingSessionsFromPEP(address); + } + }); + } catch (InvalidJidException e) { + fetchStatusMap.put(address, FetchStatus.SUCCESS); + finishBuildingSessionsFromPEP(address); + } + } + + private void finishBuildingSessionsFromPEP(final AxolotlAddress address) { + AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toString(), 0); + if (!fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.PENDING) + && !fetchStatusMap.getAll(address).containsValue(FetchStatus.PENDING)) { + FetchStatus report = null; + if (fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.SUCCESS_VERIFIED) + | fetchStatusMap.getAll(address).containsValue(FetchStatus.SUCCESS_VERIFIED)) { + report = FetchStatus.SUCCESS_VERIFIED; + } else if (fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.ERROR) + || fetchStatusMap.getAll(address).containsValue(FetchStatus.ERROR)) { + report = FetchStatus.ERROR; + } + mXmppConnectionService.keyStatusUpdated(report); + } + } + private void buildSessionFromPEP(final AxolotlAddress address) { Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Building new sesstion for " + address.toString()); if (address.getDeviceId() == getOwnDeviceId()) { @@ -576,17 +641,12 @@ public class AxolotlService { Jid.fromString(address.getName()), address.getDeviceId()); Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Retrieving bundle: " + bundlesPacket); mXmppConnectionService.sendIqPacket(account, bundlesPacket, new OnIqPacketReceived() { - private void finish() { - AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toString(), 0); - if (!fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.PENDING) - && !fetchStatusMap.getAll(address).containsValue(FetchStatus.PENDING)) { - mXmppConnectionService.keyStatusUpdated(); - } - } @Override public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType() == IqPacket.TYPE.RESULT) { + if (packet.getType() == IqPacket.TYPE.TIMEOUT) { + fetchStatusMap.put(address, FetchStatus.TIMEOUT); + } else if (packet.getType() == IqPacket.TYPE.RESULT) { Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Received preKey IQ packet, processing..."); final IqParser parser = mXmppConnectionService.getIqParser(); final List<PreKeyBundle> preKeyBundleList = parser.preKeys(packet); @@ -594,7 +654,7 @@ public class AxolotlService { if (preKeyBundleList.isEmpty() || bundle == null) { Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "preKey IQ packet invalid: " + packet); fetchStatusMap.put(address, FetchStatus.ERROR); - finish(); + finishBuildingSessionsFromPEP(address); return; } Random random = new Random(); @@ -602,7 +662,7 @@ public class AxolotlService { if (preKey == null) { //should never happen fetchStatusMap.put(address, FetchStatus.ERROR); - finish(); + finishBuildingSessionsFromPEP(address); return; } @@ -616,18 +676,22 @@ public class AxolotlService { builder.process(preKeyBundle); XmppAxolotlSession session = new XmppAxolotlSession(account, axolotlStore, address, bundle.getIdentityKey().getFingerprint().replaceAll("\\s", "")); sessions.put(address, session); - fetchStatusMap.put(address, FetchStatus.SUCCESS); + if (Config.X509_VERIFICATION) { + verifySessionWithPEP(session, bundle.getIdentityKey()); + } else { + fetchStatusMap.put(address, FetchStatus.SUCCESS); + finishBuildingSessionsFromPEP(address); + } } catch (UntrustedIdentityException | InvalidKeyException e) { Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error building session for " + address + ": " + e.getClass().getName() + ", " + e.getMessage()); fetchStatusMap.put(address, FetchStatus.ERROR); + finishBuildingSessionsFromPEP(address); } - - finish(); } else { fetchStatusMap.put(address, FetchStatus.ERROR); Log.d(Config.LOGTAG, getLogprefix(account) + "Error received while building session:" + packet.findChild("error")); - finish(); + finishBuildingSessionsFromPEP(address); } } }); @@ -637,8 +701,11 @@ public class AxolotlService { } public Set<AxolotlAddress> findDevicesWithoutSession(final Conversation conversation) { - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Finding devices without session for " + conversation.getContact().getJid().toBareJid()); - Jid contactJid = conversation.getContact().getJid().toBareJid(); + return findDevicesWithoutSession(conversation.getContact().getJid().toBareJid()); + } + + public Set<AxolotlAddress> findDevicesWithoutSession(final Jid contactJid) { + Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Finding devices without session for " + contactJid); Set<AxolotlAddress> addresses = new HashSet<>(); if (deviceIds.get(contactJid) != null) { for (Integer foreignId : this.deviceIds.get(contactJid)) { @@ -651,7 +718,11 @@ public class AxolotlService { sessions.put(address, session); } else { Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Found device " + account.getJid().toBareJid() + ":" + foreignId); - addresses.add(new AxolotlAddress(contactJid.toString(), foreignId)); + if (fetchStatusMap.get(address) != FetchStatus.ERROR) { + addresses.add(address); + } else { + Log.d(Config.LOGTAG,getLogprefix(account)+"skipping over "+address+" because it's broken"); + } } } } @@ -669,7 +740,11 @@ public class AxolotlService { sessions.put(address, session); } else { Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Found device " + account.getJid().toBareJid() + ":" + ownId); - addresses.add(new AxolotlAddress(account.getJid().toBareJid().toString(), ownId)); + if (fetchStatusMap.get(address) != FetchStatus.ERROR) { + addresses.add(address); + } else { + Log.d(Config.LOGTAG,getLogprefix(account)+"skipping over "+address+" because it's broken"); + } } } } @@ -685,7 +760,7 @@ public class AxolotlService { for (AxolotlAddress address : addresses) { Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Processing device: " + address.toString()); FetchStatus status = fetchStatusMap.get(address); - if (status == null || status == FetchStatus.ERROR) { + if (status == null || status == FetchStatus.TIMEOUT) { fetchStatusMap.put(address, FetchStatus.PENDING); this.buildSessionFromPEP(address); newSessions = true; @@ -699,9 +774,9 @@ public class AxolotlService { return newSessions; } - public boolean hasPendingKeyFetches(Conversation conversation) { + public boolean hasPendingKeyFetches(Account account, Contact contact) { AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toString(), 0); - AxolotlAddress foreignAddress = new AxolotlAddress(conversation.getJid().toBareJid().toString(), 0); + AxolotlAddress foreignAddress = new AxolotlAddress(contact.getJid().toBareJid().toString(), 0); return fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.PENDING) || fetchStatusMap.getAll(foreignAddress).containsValue(FetchStatus.PENDING); diff --git a/src/main/java/eu/siacs/conversations/entities/Account.java b/src/main/java/eu/siacs/conversations/entities/Account.java index 0eb38c9fd..ebfd98055 100644 --- a/src/main/java/eu/siacs/conversations/entities/Account.java +++ b/src/main/java/eu/siacs/conversations/entities/Account.java @@ -297,6 +297,9 @@ public class Account extends AbstractEntity { public void initAccountServices(final XmppConnectionService context) { this.mOtrService = new OtrService(context, this); this.axolotlService = new AxolotlService(this, context); + if (xmppConnection != null) { + xmppConnection.addOnAdvancedStreamFeaturesAvailableListener(axolotlService); + } } public OtrService getOtrService() { diff --git a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java index fb69860db..7457cad8b 100644 --- a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java @@ -137,9 +137,13 @@ public class IqGenerator extends AbstractGenerator { public IqPacket retrieveBundlesForDevice(final Jid to, final int deviceid) { final IqPacket packet = retrieve(AxolotlService.PEP_BUNDLES+":"+deviceid, null); - if(to != null) { - packet.setTo(to); - } + packet.setTo(to); + return packet; + } + + public IqPacket retrieveVerificationForDevice(final Jid to, final int deviceid) { + final IqPacket packet = retrieve(AxolotlService.PEP_VERIFICATION+":"+deviceid, null); + packet.setTo(to); return packet; } diff --git a/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java index 5fa7bf816..792a71e8b 100644 --- a/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java +++ b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java @@ -22,6 +22,7 @@ import eu.siacs.conversations.R; import eu.siacs.conversations.entities.DownloadableFile; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.Transferable; +import eu.siacs.conversations.entities.TransferablePlaceholder; import eu.siacs.conversations.persistance.FileBackend; import eu.siacs.conversations.services.AbstractConnectionManager; import eu.siacs.conversations.services.XmppConnectionService; @@ -66,7 +67,11 @@ public class HttpDownloadConnection implements Transferable { this.message = message; this.message.setTransferable(this); try { - mUrl = new URL(message.getBody()); + if (message.hasFileOnRemoteHost()) { + mUrl = message.getFileParams().url; + } else { + mUrl = new URL(message.getBody()); + } String[] parts = mUrl.getPath().toLowerCase().split("\\."); String lastPart = parts.length >= 1 ? parts[parts.length - 1] : null; String secondToLast = parts.length >= 2 ? parts[parts.length -2] : null; @@ -106,7 +111,11 @@ public class HttpDownloadConnection implements Transferable { public void cancel() { mHttpConnectionManager.finishConnection(this); - message.setTransferable(null); + if (message.isFileOrImage()) { + message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED)); + } else { + message.setTransferable(null); + } mXmppConnectionService.updateConversationUi(); } diff --git a/src/main/java/eu/siacs/conversations/parser/IqParser.java b/src/main/java/eu/siacs/conversations/parser/IqParser.java index f6446cfd1..e26a493f5 100644 --- a/src/main/java/eu/siacs/conversations/parser/IqParser.java +++ b/src/main/java/eu/siacs/conversations/parser/IqParser.java @@ -3,6 +3,7 @@ package eu.siacs.conversations.parser; import android.support.annotation.NonNull; import android.util.Base64; import android.util.Log; +import android.util.Pair; import org.whispersystems.libaxolotl.IdentityKey; import org.whispersystems.libaxolotl.InvalidKeyException; @@ -10,6 +11,10 @@ import org.whispersystems.libaxolotl.ecc.Curve; import org.whispersystems.libaxolotl.ecc.ECPublicKey; import org.whispersystems.libaxolotl.state.PreKeyBundle; +import java.io.ByteArrayInputStream; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -204,6 +209,30 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived { return preKeyRecords; } + public Pair<X509Certificate[],byte[]> verification(final IqPacket packet) { + Element item = getItem(packet); + Element verification = item != null ? item.findChild("verification",AxolotlService.PEP_PREFIX) : null; + Element chain = verification != null ? verification.findChild("chain") : null; + Element signature = verification != null ? verification.findChild("signature") : null; + if (chain != null && signature != null) { + List<Element> certElements = chain.getChildren(); + X509Certificate[] certificates = new X509Certificate[certElements.size()]; + try { + CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); + int i = 0; + for(Element cert : certElements) { + certificates[i] = (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(Base64.decode(cert.getContent(),Base64.DEFAULT))); + ++i; + } + return new Pair<>(certificates,Base64.decode(signature.getContent(),Base64.DEFAULT)); + } catch (CertificateException e) { + return null; + } + } else { + return null; + } + } + public PreKeyBundle bundle(final IqPacket bundle) { Element bundleItem = getItem(bundle); if(bundleItem == null) { diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index f9f9b71e2..d9ae6a921 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -26,10 +26,9 @@ import android.os.SystemClock; import android.preference.PreferenceManager; import android.provider.ContactsContract; import android.security.KeyChain; -import android.security.KeyChainException; +import android.util.DisplayMetrics; import android.util.Log; import android.util.LruCache; -import android.util.DisplayMetrics; import android.util.Pair; import net.java.otr4j.OtrException; @@ -38,16 +37,11 @@ import net.java.otr4j.session.SessionID; import net.java.otr4j.session.SessionImpl; import net.java.otr4j.session.SessionStatus; -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.openintents.openpgp.util.OpenPgpApi; import org.openintents.openpgp.util.OpenPgpServiceConnection; import java.math.BigInteger; import java.security.SecureRandom; -import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; @@ -66,6 +60,7 @@ import de.duenndns.ssl.MemorizingTrustManager; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.PgpEngine; +import eu.siacs.conversations.crypto.axolotl.AxolotlService; import eu.siacs.conversations.crypto.axolotl.XmppAxolotlMessage; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Blockable; @@ -124,9 +119,16 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa public static final String ACTION_CLEAR_NOTIFICATION = "clear_notification"; public static final String ACTION_DISABLE_FOREGROUND = "disable_foreground"; - private static final String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts"; public static final String ACTION_TRY_AGAIN = "try_again"; public static final String ACTION_DISABLE_ACCOUNT = "disable_account"; + private static final String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts"; + private final SerialSingleThreadExecutor mFileAddingExecutor = new SerialSingleThreadExecutor(); + private final SerialSingleThreadExecutor mDatabaseExecutor = new SerialSingleThreadExecutor(); + private final IBinder mBinder = new XmppConnectionBinder(); + private final List<Conversation> conversations = new CopyOnWriteArrayList<>(); + private final IqGenerator mIqGenerator = new IqGenerator(this); + private final List<String> mInProgressAvatarFetches = new ArrayList<>(); + public DatabaseBackend databaseBackend; private ContentObserver contactObserver = new ContentObserver(null) { @Override public void onChange(boolean selfChange) { @@ -137,63 +139,30 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa startService(intent); } }; - - private final SerialSingleThreadExecutor mFileAddingExecutor = new SerialSingleThreadExecutor(); - private final SerialSingleThreadExecutor mDatabaseExecutor = new SerialSingleThreadExecutor(); - - private final IBinder mBinder = new XmppConnectionBinder(); - private final List<Conversation> conversations = new CopyOnWriteArrayList<>(); - private final FileObserver fileObserver = new FileObserver( - FileBackend.getConversationsImageDirectory()) { - - @Override - public void onEvent(int event, String path) { - if (event == FileObserver.DELETE) { - markFileDeleted(path.split("\\.")[0]); - } - } - }; - private final OnJinglePacketReceived jingleListener = new OnJinglePacketReceived() { - - @Override - public void onJinglePacketReceived(Account account, JinglePacket packet) { - mJingleConnectionManager.deliverPacket(account, packet); - } - }; - private final OnBindListener mOnBindListener = new OnBindListener() { - - @Override - public void onBind(final Account account) { - account.getRoster().clearPresences(); - fetchRosterFromServer(account); - fetchBookmarks(account); - sendPresence(account); - connectMultiModeConversations(account); - mMessageArchiveService.executePendingQueries(account); - mJingleConnectionManager.cancelInTransmission(); - syncDirtyContacts(account); - account.getAxolotlService().publishBundlesIfNeeded(true, false); - } - }; - private final OnMessageAcknowledged mOnMessageAcknowledgedListener = new OnMessageAcknowledged() { - + private FileBackend fileBackend = new FileBackend(this); + private MemorizingTrustManager mMemorizingTrustManager; + private NotificationService mNotificationService = new NotificationService( + this); + private OnMessagePacketReceived mMessageParser = new MessageParser(this); + private OnPresencePacketReceived mPresenceParser = new PresenceParser(this); + private IqParser mIqParser = new IqParser(this); + private OnIqPacketReceived mDefaultIqHandler = new OnIqPacketReceived() { @Override - public void onMessageAcknowledged(Account account, String uuid) { - for (final Conversation conversation : getConversations()) { - if (conversation.getAccount() == account) { - Message message = conversation.findUnsentMessageWithUuid(uuid); - if (message != null) { - markMessage(message, Message.STATUS_SEND); - if (conversation.setLastMessageTransmitted(System.currentTimeMillis())) { - databaseBackend.updateConversation(conversation); - } - } + public void onIqPacketReceived(Account account, IqPacket packet) { + if (packet.getType() != IqPacket.TYPE.RESULT) { + Element error = packet.findChild("error"); + String text = error != null ? error.findChildContent("text") : null; + if (text != null) { + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": received iq error - " + text); } } } }; - private final IqGenerator mIqGenerator = new IqGenerator(this); - public DatabaseBackend databaseBackend; + private MessageGenerator mMessageGenerator = new MessageGenerator(this); + private PresenceGenerator mPresenceGenerator = new PresenceGenerator(this); + private List<Account> accounts; + private JingleConnectionManager mJingleConnectionManager = new JingleConnectionManager( + this); public OnContactStatusChanged onContactStatusChanged = new OnContactStatusChanged() { @Override @@ -220,42 +189,76 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } } }; - private FileBackend fileBackend = new FileBackend(this); - private MemorizingTrustManager mMemorizingTrustManager; - private NotificationService mNotificationService = new NotificationService( + private HttpConnectionManager mHttpConnectionManager = new HttpConnectionManager( this); - private OnMessagePacketReceived mMessageParser = new MessageParser(this); - private OnPresencePacketReceived mPresenceParser = new PresenceParser(this); - private IqParser mIqParser = new IqParser(this); - private OnIqPacketReceived mDefaultIqHandler = new OnIqPacketReceived() { + private AvatarService mAvatarService = new AvatarService(this); + private MessageArchiveService mMessageArchiveService = new MessageArchiveService(this); + private OnConversationUpdate mOnConversationUpdate = null; + private final FileObserver fileObserver = new FileObserver( + FileBackend.getConversationsImageDirectory()) { + @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType() != IqPacket.TYPE.RESULT) { - Element error = packet.findChild("error"); - String text = error != null ? error.findChildContent("text") : null; - if (text != null) { - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": received iq error - "+text); + public void onEvent(int event, String path) { + if (event == FileObserver.DELETE) { + markFileDeleted(path.split("\\.")[0]); + } + } + }; + private final OnJinglePacketReceived jingleListener = new OnJinglePacketReceived() { + + @Override + public void onJinglePacketReceived(Account account, JinglePacket packet) { + mJingleConnectionManager.deliverPacket(account, packet); + } + }; + private final OnMessageAcknowledged mOnMessageAcknowledgedListener = new OnMessageAcknowledged() { + + @Override + public void onMessageAcknowledged(Account account, String uuid) { + for (final Conversation conversation : getConversations()) { + if (conversation.getAccount() == account) { + Message message = conversation.findUnsentMessageWithUuid(uuid); + if (message != null) { + markMessage(message, Message.STATUS_SEND); + if (conversation.setLastMessageTransmitted(System.currentTimeMillis())) { + databaseBackend.updateConversation(conversation); + } + } } } } }; - private MessageGenerator mMessageGenerator = new MessageGenerator(this); - private PresenceGenerator mPresenceGenerator = new PresenceGenerator(this); - private List<Account> accounts; - private JingleConnectionManager mJingleConnectionManager = new JingleConnectionManager( - this); - private HttpConnectionManager mHttpConnectionManager = new HttpConnectionManager( - this); - private AvatarService mAvatarService = new AvatarService(this); - private final List<String> mInProgressAvatarFetches = new ArrayList<>(); - private MessageArchiveService mMessageArchiveService = new MessageArchiveService(this); - private OnConversationUpdate mOnConversationUpdate = null; private int convChangedListenerCount = 0; private OnShowErrorToast mOnShowErrorToast = null; private int showErrorToastListenerCount = 0; private int unreadCount = -1; private OnAccountUpdate mOnAccountUpdate = null; private OnCaptchaRequested mOnCaptchaRequested = null; + private int accountChangedListenerCount = 0; + private int captchaRequestedListenerCount = 0; + private OnRosterUpdate mOnRosterUpdate = null; + private OnUpdateBlocklist mOnUpdateBlocklist = null; + private int updateBlocklistListenerCount = 0; + private int rosterChangedListenerCount = 0; + private OnMucRosterUpdate mOnMucRosterUpdate = null; + private int mucRosterChangedListenerCount = 0; + private OnKeyStatusUpdated mOnKeyStatusUpdated = null; + private int keyStatusUpdatedListenerCount = 0; + private SecureRandom mRandom; + private final OnBindListener mOnBindListener = new OnBindListener() { + + @Override + public void onBind(final Account account) { + account.getRoster().clearPresences(); + fetchRosterFromServer(account); + fetchBookmarks(account); + sendPresence(account); + connectMultiModeConversations(account); + mMessageArchiveService.executePendingQueries(account); + mJingleConnectionManager.cancelInTransmission(); + syncDirtyContacts(account); + } + }; private OnStatusChanged statusListener = new OnStatusChanged() { @Override @@ -267,10 +270,10 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa if (account.getStatus() == Account.State.ONLINE) { if (connection != null && connection.getFeatures().csi()) { if (checkListeners()) { - Log.d(Config.LOGTAG, account.getJid().toBareJid()+ " sending csi//inactive"); + Log.d(Config.LOGTAG, account.getJid().toBareJid() + " sending csi//inactive"); connection.sendInactive(); } else { - Log.d(Config.LOGTAG, account.getJid().toBareJid()+ " sending csi//active"); + Log.d(Config.LOGTAG, account.getJid().toBareJid() + " sending csi//active"); connection.sendActive(); } } @@ -294,7 +297,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa resetSendingToWaiting(account); if (!account.isOptionSet(Account.OPTION_DISABLED)) { int timeToReconnect = mRandom.nextInt(20) + 10; - scheduleWakeUpCall(timeToReconnect,account.getUuid().hashCode()); + scheduleWakeUpCall(timeToReconnect, account.getUuid().hashCode()); } } else if (account.getStatus() == Account.State.REGISTRATION_SUCCESSFUL) { databaseBackend.updateAccount(account); @@ -307,23 +310,12 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa + ": error connecting account. try again in " + next + "s for the " + (connection.getAttempt() + 1) + " time"); - scheduleWakeUpCall(next,account.getUuid().hashCode()); + scheduleWakeUpCall(next, account.getUuid().hashCode()); } - } + } getNotificationService().updateErrorNotification(); } }; - private int accountChangedListenerCount = 0; - private int captchaRequestedListenerCount = 0; - private OnRosterUpdate mOnRosterUpdate = null; - private OnUpdateBlocklist mOnUpdateBlocklist = null; - private int updateBlocklistListenerCount = 0; - private int rosterChangedListenerCount = 0; - private OnMucRosterUpdate mOnMucRosterUpdate = null; - private int mucRosterChangedListenerCount = 0; - private OnKeyStatusUpdated mOnKeyStatusUpdated = null; - private int keyStatusUpdatedListenerCount = 0; - private SecureRandom mRandom; private OpenPgpServiceConnection pgpServiceConnection; private PgpEngine mPgpEngine = null; private WakeLock wakeLock; @@ -333,6 +325,11 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa private EventReceiver mEventReceiver = new EventReceiver(); private boolean mRestoredFromDatabase = false; + + private static String generateFetchKey(Account account, final Avatar avatar) { + return account.getJid().toBareJid() + "_" + avatar.owner + "_" + avatar.sha1sum; + } + public boolean areMessagesInitialized() { return this.mRestoredFromDatabase; } @@ -341,8 +338,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa if (pgpServiceConnection.isBound()) { if (this.mPgpEngine == null) { this.mPgpEngine = new PgpEngine(new OpenPgpApi( - getApplicationContext(), - pgpServiceConnection.getService()), this); + getApplicationContext(), + pgpServiceConnection.getService()), this); } return mPgpEngine; } else { @@ -366,7 +363,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa if (encryption == Message.ENCRYPTION_PGP) { encryption = Message.ENCRYPTION_DECRYPTED; } - Message message = new Message(conversation,uri.toString(),encryption); + Message message = new Message(conversation, uri.toString(), encryption); if (conversation.getNextCounterpart() != null) { message.setCounterpart(conversation.getNextCounterpart()); } @@ -378,8 +375,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } public void attachFileToConversation(final Conversation conversation, - final Uri uri, - final UiCallback<Message> callback) { + final Uri uri, + final UiCallback<Message> callback) { final Message message; if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) { message = new Message(conversation, "", Message.ENCRYPTION_DECRYPTED); @@ -389,7 +386,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa message.setCounterpart(conversation.getNextCounterpart()); message.setType(Message.TYPE_FILE); String path = getFileBackend().getOriginalPath(uri); - if (path!=null) { + if (path != null) { message.setRelativeFilePath(path); getFileBackend().updateFileParams(message); if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { @@ -419,7 +416,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa public void attachImageToConversation(final Conversation conversation, final Uri uri, final UiCallback<Message> callback) { if (getFileBackend().useImageAsIs(uri)) { - Log.d(Config.LOGTAG,"using image as is"); + Log.d(Config.LOGTAG, "using image as is"); attachFileToConversation(conversation, uri, callback); return; } @@ -427,7 +424,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) { message = new Message(conversation, "", Message.ENCRYPTION_DECRYPTED); } else { - message = new Message(conversation, "",conversation.getNextEncryption()); + message = new Message(conversation, "", conversation.getNextEncryption()); } message.setCounterpart(conversation.getNextCounterpart()); message.setType(Message.TYPE_IMAGE); @@ -462,7 +459,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa final String action = intent == null ? null : intent.getAction(); boolean interactive = false; if (action != null) { - Log.d(Config.LOGTAG,"action: "+action); switch (action) { case ConnectivityManager.CONNECTIVITY_ACTION: if (hasInternetConnection() && Config.RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE) { @@ -483,7 +479,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa mNotificationService.clear(); break; case ACTION_DISABLE_FOREGROUND: - getPreferences().edit().putBoolean("keep_foreground_service",false).commit(); + getPreferences().edit().putBoolean("keep_foreground_service", false).commit(); toggleForegroundService(); break; case ACTION_TRY_AGAIN: @@ -495,7 +491,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa String jid = intent.getStringExtra("account"); Account account = jid == null ? null : findAccountByJid(Jid.fromString(jid)); if (account != null) { - account.setOption(Account.OPTION_DISABLED,true); + account.setOption(Account.OPTION_DISABLED, true); updateAccount(account); } } catch (final InvalidJidException ignored) { @@ -535,7 +531,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa long lastReceived = account.getXmppConnection().getLastPacketReceived(); long lastSent = account.getXmppConnection().getLastPingSent(); long pingInterval = "ui".equals(action) ? Config.PING_MIN_INTERVAL * 1000 : Config.PING_MAX_INTERVAL * 1000; - long msToNextPing = (Math.max(lastReceived,lastSent) + pingInterval) - SystemClock.elapsedRealtime(); + long msToNextPing = (Math.max(lastReceived, lastSent) + pingInterval) - SystemClock.elapsedRealtime(); long pingTimeoutIn = (lastSent + Config.PING_TIMEOUT * 1000) - SystemClock.elapsedRealtime(); if (lastSent > lastReceived) { if (pingTimeoutIn < 0) { @@ -543,24 +539,24 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa this.reconnectAccount(account, true, interactive); } else { int secs = (int) (pingTimeoutIn / 1000); - this.scheduleWakeUpCall(secs,account.getUuid().hashCode()); + this.scheduleWakeUpCall(secs, account.getUuid().hashCode()); } } else if (msToNextPing <= 0) { account.getXmppConnection().sendPing(); - Log.d(Config.LOGTAG, account.getJid().toBareJid()+" send ping"); - this.scheduleWakeUpCall(Config.PING_TIMEOUT,account.getUuid().hashCode()); + Log.d(Config.LOGTAG, account.getJid().toBareJid() + " send ping"); + this.scheduleWakeUpCall(Config.PING_TIMEOUT, account.getUuid().hashCode()); } else { this.scheduleWakeUpCall((int) (msToNextPing / 1000), account.getUuid().hashCode()); } } else if (account.getStatus() == Account.State.OFFLINE) { - reconnectAccount(account,true, interactive); + reconnectAccount(account, true, interactive); } else if (account.getStatus() == Account.State.CONNECTING) { long timeout = Config.CONNECT_TIMEOUT - ((SystemClock.elapsedRealtime() - account.getXmppConnection().getLastConnect()) / 1000); if (timeout < 0) { Log.d(Config.LOGTAG, account.getJid() + ": time out during connect reconnecting"); reconnectAccount(account, true, interactive); } else { - scheduleWakeUpCall((int) timeout,account.getUuid().hashCode()); + scheduleWakeUpCall((int) timeout, account.getUuid().hashCode()); } } else { if (account.getXmppConnection().getTimeToNextAttempt() <= 0) { @@ -621,8 +617,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } private void resetAllAttemptCounts(boolean reallyAll) { - Log.d(Config.LOGTAG,"resetting all attepmt counts"); - for(Account account : accounts) { + Log.d(Config.LOGTAG, "resetting all attepmt counts"); + for (Account account : accounts) { if (account.hasErrorStatus() || reallyAll) { final XmppConnection connection = account.getXmppConnection(); if (connection != null) { @@ -634,7 +630,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa public boolean hasInternetConnection() { ConnectivityManager cm = (ConnectivityManager) getApplicationContext() - .getSystemService(Context.CONNECTIVITY_SERVICE); + .getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); return activeNetwork != null && activeNetwork.isConnected(); } @@ -667,7 +663,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa this.pgpServiceConnection.bindToService(); this.pm = (PowerManager) getSystemService(Context.POWER_SERVICE); - this.wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"XmppConnectionService"); + this.wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "XmppConnectionService"); toggleForegroundService(); updateUnreadCountBadge(); toggleScreenEventReceiver(); @@ -677,7 +673,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa public void onTrimMemory(int level) { super.onTrimMemory(level); if (level >= TRIM_MEMORY_COMPLETE) { - Log.d(Config.LOGTAG,"clear cache due to low memory"); + Log.d(Config.LOGTAG, "clear cache due to low memory"); getBitmapCache().evictAll(); } } @@ -717,7 +713,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa @Override public void onTaskRemoved(final Intent rootIntent) { super.onTaskRemoved(rootIntent); - if (!getPreferences().getBoolean("keep_foreground_service",false)) { + if (!getPreferences().getBoolean("keep_foreground_service", false)) { this.logoutAndSave(); } } @@ -763,6 +759,10 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa connection.setOnBindListener(this.mOnBindListener); connection.setOnMessageAcknowledgeListener(this.mOnMessageAcknowledgedListener); connection.addOnAdvancedStreamFeaturesAvailableListener(this.mMessageArchiveService); + AxolotlService axolotlService = account.getAxolotlService(); + if (axolotlService != null) { + connection.addOnAdvancedStreamFeaturesAvailableListener(axolotlService); + } return connection; } @@ -800,11 +800,11 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa message.getConversation().endOtrIfNeeded(); message.getConversation().findUnsentMessagesWithEncryption(Message.ENCRYPTION_OTR, new Conversation.OnMessageFound() { - @Override - public void onMessageFound(Message message) { - markMessage(message,Message.STATUS_SEND_FAILED); - } - }); + @Override + public void onMessageFound(Message message) { + markMessage(message, Message.STATUS_SEND_FAILED); + } + }); } if (account.isOnlineAndConnected()) { @@ -812,7 +812,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa case Message.ENCRYPTION_NONE: if (message.needsUploading()) { if (account.httpUploadAvailable() || message.fixCounterpart()) { - this.sendFileMessage(message,delay); + this.sendFileMessage(message, delay); } else { break; } @@ -824,7 +824,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa case Message.ENCRYPTION_DECRYPTED: if (message.needsUploading()) { if (account.httpUploadAvailable() || message.fixCounterpart()) { - this.sendFileMessage(message,delay); + this.sendFileMessage(message, delay); } else { break; } @@ -857,7 +857,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa message.setAxolotlFingerprint(account.getAxolotlService().getOwnFingerprint()); if (message.needsUploading()) { if (account.httpUploadAvailable() || message.fixCounterpart()) { - this.sendFileMessage(message,delay); + this.sendFileMessage(message, delay); } else { break; } @@ -880,7 +880,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } } } else { - switch(message.getEncryption()) { + switch (message.getEncryption()) { case Message.ENCRYPTION_DECRYPTED: if (!message.needsUploading()) { String pgpBody = message.getEncryptedBody(); @@ -907,9 +907,9 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa if (resend) { if (packet != null) { if (account.getXmppConnection().getFeatures().sm() || conversation.getMode() == Conversation.MODE_MULTI) { - markMessage(message,Message.STATUS_UNSEND); + markMessage(message, Message.STATUS_UNSEND); } else { - markMessage(message,Message.STATUS_SEND); + markMessage(message, Message.STATUS_SEND); } } } else { @@ -921,7 +921,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } if (packet != null) { if (delay) { - mMessageGenerator.addDelay(packet,message.getTimeSent()); + mMessageGenerator.addDelay(packet, message.getTimeSent()); } if (conversation.setOutgoingChatState(Config.DEFAULT_CHATSTATE)) { if (this.sendChatStates()) { @@ -989,7 +989,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } account.setBookmarks(bookmarks); } else { - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could not fetch bookmarks"); + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not fetch bookmarks"); } } }; @@ -1013,12 +1013,12 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa mPhoneContactMergerThread = new Thread(new Runnable() { @Override public void run() { - Log.d(Config.LOGTAG,"start merging phone contacts with roster"); + Log.d(Config.LOGTAG, "start merging phone contacts with roster"); for (Account account : accounts) { List<Contact> withSystemAccounts = account.getRoster().getWithSystemAccounts(); for (Bundle phoneContact : phoneContacts) { if (Thread.interrupted()) { - Log.d(Config.LOGTAG,"interrupted merging phone contacts"); + Log.d(Config.LOGTAG, "interrupted merging phone contacts"); return; } Jid jid; @@ -1029,8 +1029,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } final Contact contact = account.getRoster().getContact(jid); String systemAccount = phoneContact.getInt("phoneid") - + "#" - + phoneContact.getString("lookup"); + + "#" + + phoneContact.getString("lookup"); contact.setSystemAccount(systemAccount); if (contact.setPhotoUri(phoneContact.getString("photouri"))) { getAvatarService().clear(contact); @@ -1038,7 +1038,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa contact.setSystemName(phoneContact.getString("displayname")); withSystemAccounts.remove(contact); } - for(Contact contact : withSystemAccounts) { + for (Contact contact : withSystemAccounts) { contact.setSystemAccount(null); contact.setSystemName(null); if (contact.setPhotoUri(null)) { @@ -1046,7 +1046,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } } } - Log.d(Config.LOGTAG,"finished merging phone contacts"); + Log.d(Config.LOGTAG, "finished merging phone contacts"); updateAccountUi(); } }); @@ -1064,20 +1064,20 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa Account account = accountLookupTable.get(conversation.getAccountUuid()); conversation.setAccount(account); } - Runnable runnable =new Runnable() { + Runnable runnable = new Runnable() { @Override public void run() { - Log.d(Config.LOGTAG,"restoring roster"); - for(Account account : accounts) { - databaseBackend.readRoster(account.getRoster()); + Log.d(Config.LOGTAG, "restoring roster"); + for (Account account : accounts) { account.initAccountServices(XmppConnectionService.this); + databaseBackend.readRoster(account.getRoster()); } getBitmapCache().evictAll(); Looper.prepare(); PhoneHelper.loadPhoneContacts(getApplicationContext(), new CopyOnWriteArrayList<Bundle>(), XmppConnectionService.this); - Log.d(Config.LOGTAG,"restoring messages"); + Log.d(Config.LOGTAG, "restoring messages"); for (Conversation conversation : conversations) { conversation.addAll(0, databaseBackend.getMessages(conversation, Config.PAGE_SIZE)); checkDeletedFiles(conversation); @@ -1090,7 +1090,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } mNotificationService.finishBacklog(); mRestoredFromDatabase = true; - Log.d(Config.LOGTAG,"restored all messages"); + Log.d(Config.LOGTAG, "restored all messages"); updateConversationUi(); } }; @@ -1110,8 +1110,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa if (!getFileBackend().isFileAvailable(message)) { message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED)); final int s = message.getStatus(); - if(s == Message.STATUS_WAITING || s == Message.STATUS_OFFERED || s == Message.STATUS_UNSEND) { - markMessage(message,Message.STATUS_SEND_FAILED); + if (s == Message.STATUS_WAITING || s == Message.STATUS_OFFERED || s == Message.STATUS_UNSEND) { + markMessage(message, Message.STATUS_SEND_FAILED); } } } @@ -1125,8 +1125,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa if (!getFileBackend().isFileAvailable(message)) { message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED)); final int s = message.getStatus(); - if(s == Message.STATUS_WAITING || s == Message.STATUS_OFFERED || s == Message.STATUS_UNSEND) { - markMessage(message,Message.STATUS_SEND_FAILED); + if (s == Message.STATUS_WAITING || s == Message.STATUS_OFFERED || s == Message.STATUS_UNSEND) { + markMessage(message, Message.STATUS_SEND_FAILED); } else { updateConversationUi(); } @@ -1169,7 +1169,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } public void loadMoreMessages(final Conversation conversation, final long timestamp, final OnMoreMessagesLoaded callback) { - if (XmppConnectionService.this.getMessageArchiveService().queryInProgress(conversation,callback)) { + if (XmppConnectionService.this.getMessageArchiveService().queryInProgress(conversation, callback)) { return; } Log.d(Config.LOGTAG, "load more messages for " + conversation.getName() + " prior to " + MessageGenerator.getTimestamp(timestamp)); @@ -1177,7 +1177,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa @Override public void run() { final Account account = conversation.getAccount(); - List<Message> messages = databaseBackend.getMessages(conversation, 50,timestamp); + List<Message> messages = databaseBackend.getMessages(conversation, 50, timestamp); if (messages.size() > 0) { conversation.addAll(0, messages); checkDeletedFiles(conversation); @@ -1186,7 +1186,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa && account.isOnlineAndConnected()) { if ((conversation.getMode() == Conversation.MODE_SINGLE && account.getXmppConnection().getFeatures().mam()) || (conversation.getMode() == Conversation.MODE_MULTI && conversation.getMucOptions().mamSupport())) { - MessageArchiveService.Query query = getMessageArchiveService().query(conversation,0,timestamp - 1); + MessageArchiveService.Query query = getMessageArchiveService().query(conversation, 0, timestamp - 1); if (query != null) { query.setCallback(callback); } @@ -1320,7 +1320,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa public void run() { try { X509Certificate[] chain = KeyChain.getCertificateChain(XmppConnectionService.this, alias); - Pair<Jid,String> info = CryptoHelper.extractJidAndName(chain[0]); + Pair<Jid, String> info = CryptoHelper.extractJidAndName(chain[0]); if (findAccountByJid(info.first) == null) { Account account = new Account(info.first, ""); account.setPrivateKeyAlias(alias); @@ -1338,6 +1338,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa callback.informUser(R.string.account_already_exists); } } catch (Exception e) { + e.printStackTrace(); callback.informUser(R.string.unable_to_parse_certificate); } } @@ -1364,13 +1365,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } else { showErrorToastInUi(R.string.jid_does_not_match_certificate); } - } catch (InterruptedException e) { - e.printStackTrace(); - } catch (KeyChainException e) { - e.printStackTrace(); - } catch (InvalidJidException e) { - e.printStackTrace(); - } catch (CertificateEncodingException e) { + } catch (Exception e) { e.printStackTrace(); } } @@ -1378,7 +1373,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa public void updateAccount(final Account account) { this.statusListener.onStatusChanged(account); databaseBackend.updateAccount(account); - reconnectAccount(account, false, true); + reconnectAccountInBackground(account); updateAccountUi(); getNotificationService().updateErrorNotification(); } @@ -1655,7 +1650,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } } } - for(Conversation conversation : getConversations()) { + for (Conversation conversation : getConversations()) { conversation.setIncomingChatState(ChatState.ACTIVE); } this.mNotificationService.setIsInForeground(false); @@ -1666,7 +1661,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa List<Conversation> conversations = getConversations(); for (Conversation conversation : conversations) { if (conversation.getMode() == Conversation.MODE_MULTI && conversation.getAccount() == account) { - joinMuc(conversation,true); + joinMuc(conversation, true); } } } @@ -1791,10 +1786,14 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } public void leaveMuc(Conversation conversation) { + leaveMuc(conversation, false); + } + + private void leaveMuc(Conversation conversation, boolean now) { Account account = conversation.getAccount(); account.pendingConferenceJoins.remove(conversation); account.pendingConferenceLeaves.remove(conversation); - if (account.getStatus() == Account.State.ONLINE) { + if (account.getStatus() == Account.State.ONLINE || now) { PresencePacket packet = new PresencePacket(); packet.setTo(conversation.getJid()); packet.setFrom(conversation.getAccount().getJid()); @@ -2006,7 +2005,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa }); } - public void disconnect(Account account, boolean force) { + private void disconnect(Account account, boolean force) { if ((account.getStatus() == Account.State.ONLINE) || (account.getStatus() == Account.State.DISABLED)) { if (!force) { @@ -2014,7 +2013,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa for (Conversation conversation : conversations) { if (conversation.getAccount() == account) { if (conversation.getMode() == Conversation.MODE_MULTI) { - leaveMuc(conversation); + leaveMuc(conversation, true); } else { if (conversation.endOtrIfNeeded()) { Log.d(Config.LOGTAG, account.getJid().toBareJid() @@ -2204,13 +2203,9 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa fetchAvatar(account, avatar, null); } - private static String generateFetchKey(Account account, final Avatar avatar) { - return account.getJid().toBareJid()+"_"+avatar.owner+"_"+avatar.sha1sum; - } - public void fetchAvatar(Account account, final Avatar avatar, final UiCallback<Avatar> callback) { final String KEY = generateFetchKey(account, avatar); - synchronized(this.mInProgressAvatarFetches) { + synchronized (this.mInProgressAvatarFetches) { if (this.mInProgressAvatarFetches.contains(KEY)) { return; } else { @@ -2375,9 +2370,9 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa if (!account.isOptionSet(Account.OPTION_DISABLED)) { synchronized (this.mInProgressAvatarFetches) { - for(Iterator<String> iterator = this.mInProgressAvatarFetches.iterator(); iterator.hasNext();) { + for (Iterator<String> iterator = this.mInProgressAvatarFetches.iterator(); iterator.hasNext(); ) { final String KEY = iterator.next(); - if (KEY.startsWith(account.getJid().toBareJid()+"_")) { + if (KEY.startsWith(account.getJid().toBareJid() + "_")) { iterator.remove(); } } @@ -2385,6 +2380,13 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa if (account.getXmppConnection() == null) { account.setXmppConnection(createConnection(account)); + } else if (!force) { + try { + Log.d(Config.LOGTAG, "wait for disconnect"); + Thread.sleep(500); //sleep wait for disconnect + } catch (InterruptedException e) { + //ignored + } } Thread thread = new Thread(account.getXmppConnection()); account.getXmppConnection().setInteractive(interactive); @@ -2401,20 +2403,20 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa new Thread(new Runnable() { @Override public void run() { - reconnectAccount(account,false,true); + reconnectAccount(account, false, true); } }).start(); } public void invite(Conversation conversation, Jid contact) { - Log.d(Config.LOGTAG,conversation.getAccount().getJid().toBareJid()+": inviting "+contact+" to "+conversation.getJid().toBareJid()); + Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": inviting " + contact + " to " + conversation.getJid().toBareJid()); MessagePacket packet = mMessageGenerator.invite(conversation, contact); sendMessagePacket(conversation.getAccount(), packet); } public void directInvite(Conversation conversation, Jid jid) { MessagePacket packet = mMessageGenerator.directInvite(conversation, jid); - sendMessagePacket(conversation.getAccount(),packet); + sendMessagePacket(conversation.getAccount(), packet); } public void resetSendingToWaiting(Account account) { @@ -2499,7 +2501,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa public int unreadCount() { int count = 0; - for(Conversation conversation : getConversations()) { + for (Conversation conversation : getConversations()) { count += conversation.unreadCount(); } return count; @@ -2534,8 +2536,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa boolean rc = false; if (mOnCaptchaRequested != null) { DisplayMetrics metrics = getApplicationContext().getResources().getDisplayMetrics(); - Bitmap scaled = Bitmap.createScaledBitmap(captcha, (int)(captcha.getWidth() * metrics.scaledDensity), - (int)(captcha.getHeight() * metrics.scaledDensity), false); + Bitmap scaled = Bitmap.createScaledBitmap(captcha, (int) (captcha.getWidth() * metrics.scaledDensity), + (int) (captcha.getHeight() * metrics.scaledDensity), false); mOnCaptchaRequested.onCaptchaRequested(account, id, data, scaled); rc = true; @@ -2556,9 +2558,9 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } } - public void keyStatusUpdated() { - if(mOnKeyStatusUpdated != null) { - mOnKeyStatusUpdated.onKeyStatusUpdated(); + public void keyStatusUpdated(AxolotlService.FetchStatus report) { + if (mOnKeyStatusUpdated != null) { + mOnKeyStatusUpdated.onKeyStatusUpdated(report); } } @@ -2582,7 +2584,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa public void markRead(final Conversation conversation) { mNotificationService.clear(conversation); - for(Message message : conversation.markRead()) { + for (Message message : conversation.markRead()) { databaseBackend.updateMessage(message); } updateUnreadCountBadge(); @@ -2630,7 +2632,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa final MemorizingTrustManager tm; final boolean dontTrustSystemCAs = getPreferences().getBoolean("dont_trust_system_cas", false); if (dontTrustSystemCAs) { - tm = new MemorizingTrustManager(getApplicationContext(), null); + tm = new MemorizingTrustManager(getApplicationContext(), null); } else { tm = new MemorizingTrustManager(getApplicationContext()); } @@ -2791,7 +2793,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa for (final Message msg : messages) { msg.setTime(System.currentTimeMillis()); markMessage(msg, Message.STATUS_WAITING); - this.resendMessage(msg,false); + this.resendMessage(msg, false); } } @@ -2840,6 +2842,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa public interface OnAccountCreated { void onAccountCreated(Account account); + void informUser(int r); } @@ -2877,9 +2880,9 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa public interface OnCaptchaRequested { void onCaptchaRequested(Account account, - String id, - Data data, - Bitmap captcha); + String id, + Data data, + Bitmap captcha); } public interface OnRosterUpdate { diff --git a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java index c610d2c89..aff50502c 100644 --- a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java @@ -35,6 +35,7 @@ import java.util.List; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.PgpEngine; +import eu.siacs.conversations.crypto.axolotl.AxolotlService; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.ListItem; @@ -479,7 +480,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd } @Override - public void onKeyStatusUpdated() { + public void onKeyStatusUpdated(AxolotlService.FetchStatus report) { refreshUi(); } } diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index 3a52cf36c..16ccceb28 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -489,7 +489,8 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa || m.treatAsDownloadable() == Message.Decision.MUST) { copyUrl.setVisible(true); } - if (m.getType() == Message.TYPE_TEXT && m.getTransferable() == null && m.treatAsDownloadable() != Message.Decision.NEVER) { + if ((m.getType() == Message.TYPE_TEXT && m.getTransferable() == null && m.treatAsDownloadable() != Message.Decision.NEVER) + || (m.isFileOrImage() && m.getTransferable() instanceof TransferablePlaceholder && m.hasFileOnRemoteHost())){ downloadFile.setVisible(true); downloadFile.setTitle(activity.getString(R.string.download_x_file,UIHelper.getFileDescriptionString(activity, m))); } diff --git a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java index 2ab60800d..1712a717c 100644 --- a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java @@ -708,7 +708,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate } @Override - public void onKeyStatusUpdated() { + public void onKeyStatusUpdated(AxolotlService.FetchStatus report) { refreshUi(); } diff --git a/src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java b/src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java index ab3130748..4bd0d42da 100644 --- a/src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java @@ -8,6 +8,7 @@ import android.widget.Button; import android.widget.CompoundButton; import android.widget.LinearLayout; import android.widget.TextView; +import android.widget.Toast; import org.whispersystems.libaxolotl.IdentityKey; @@ -16,10 +17,10 @@ import java.util.Map; import java.util.Set; import eu.siacs.conversations.R; +import eu.siacs.conversations.crypto.axolotl.AxolotlService; import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; -import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.xmpp.OnKeyStatusUpdated; import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.Jid; @@ -27,11 +28,9 @@ import eu.siacs.conversations.xmpp.jid.Jid; public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdated { private Jid accountJid; private Jid contactJid; - private boolean hasOtherTrustedKeys = false; - private boolean hasPendingFetches = false; - private boolean hasNoTrustedKeys = true; private Contact contact; + private Account mAccount; private TextView keyErrorMessage; private LinearLayout keyErrorMessageCard; private TextView ownKeysTitle; @@ -50,10 +49,7 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate @Override public void onClick(View v) { commitTrusts(); - Intent data = new Intent(); - data.putExtra("choice", getIntent().getIntExtra("choice", ConversationActivity.ATTACHMENT_CHOICE_INVALID)); - setResult(RESULT_OK, data); - finish(); + finishOk(); } }; @@ -92,7 +88,6 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate this.contactJid = Jid.fromString(getIntent().getExtras().getString("contact")); } catch (final InvalidJidException ignored) { } - hasNoTrustedKeys = getIntent().getBooleanExtra("has_no_trusted", false); keyErrorMessageCard = (LinearLayout) findViewById(R.id.key_error_message_card); keyErrorMessage = (TextView) findViewById(R.id.key_error_message); @@ -157,11 +152,11 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate foreignKeysTitle.setText(contactJid.toString()); foreignKeysCard.setVisibility(View.VISIBLE); } - if(hasPendingFetches) { + if(hasPendingKeyFetches()) { setFetching(); lock(); } else { - if (!hasForeignKeys && !hasOtherTrustedKeys) { + if (!hasForeignKeys && hasNoOtherTrustedKeys()) { keyErrorMessageCard.setVisibility(View.VISIBLE); keyErrorMessage.setText(R.string.error_no_keys_to_trust); ownKeys.removeAllViews(); ownKeysCard.setVisibility(View.GONE); @@ -172,12 +167,14 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate } } - private void getFingerprints(final Account account) { - Set<IdentityKey> ownKeysSet = account.getAxolotlService().getKeysWithTrust(XmppAxolotlSession.Trust.UNDECIDED); - Set<IdentityKey> foreignKeysSet = account.getAxolotlService().getKeysWithTrust(XmppAxolotlSession.Trust.UNDECIDED, contact); - if (hasNoTrustedKeys) { - ownKeysSet.addAll(account.getAxolotlService().getKeysWithTrust(XmppAxolotlSession.Trust.UNTRUSTED)); - foreignKeysSet.addAll(account.getAxolotlService().getKeysWithTrust(XmppAxolotlSession.Trust.UNTRUSTED, contact)); + private boolean reloadFingerprints() { + ownKeysToTrust.clear(); + foreignKeysToTrust.clear(); + AxolotlService service = this.mAccount.getAxolotlService(); + Set<IdentityKey> ownKeysSet = service.getKeysWithTrust(XmppAxolotlSession.Trust.UNDECIDED); + Set<IdentityKey> foreignKeysSet = service.getKeysWithTrust(XmppAxolotlSession.Trust.UNDECIDED, contact); + if (hasNoOtherTrustedKeys() && ownKeysSet.size() == 0) { + foreignKeysSet.addAll(service.getKeysWithTrust(XmppAxolotlSession.Trust.UNTRUSTED, contact)); } for(final IdentityKey identityKey : ownKeysSet) { if(!ownKeysToTrust.containsKey(identityKey)) { @@ -189,39 +186,68 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate foreignKeysToTrust.put(identityKey.getFingerprint().replaceAll("\\s", ""), false); } } + return ownKeysSet.size() + foreignKeysSet.size() > 0; } @Override public void onBackendConnected() { if ((accountJid != null) && (contactJid != null)) { - final Account account = xmppConnectionService - .findAccountByJid(accountJid); - if (account == null) { + this.mAccount = xmppConnectionService.findAccountByJid(accountJid); + if (this.mAccount == null) { return; } - this.contact = account.getRoster().getContact(contactJid); - ownKeysToTrust.clear(); - foreignKeysToTrust.clear(); - getFingerprints(account); - - if(account.getAxolotlService().getNumTrustedKeys(contact) > 0) { - hasOtherTrustedKeys = true; - } - Conversation conversation = xmppConnectionService.findOrCreateConversation(account, contactJid, false); - if(account.getAxolotlService().hasPendingKeyFetches(conversation)) { - hasPendingFetches = true; - } - + this.contact = this.mAccount.getRoster().getContact(contactJid); + reloadFingerprints(); populateView(); } } + private boolean hasNoOtherTrustedKeys() { + return mAccount == null || mAccount.getAxolotlService().getNumTrustedKeys(contact) == 0; + } + + private boolean hasPendingKeyFetches() { + return mAccount != null && contact != null && mAccount.getAxolotlService().hasPendingKeyFetches(mAccount,contact); + } + + @Override - public void onKeyStatusUpdated() { - final Account account = xmppConnectionService.findAccountByJid(accountJid); - hasPendingFetches = false; - getFingerprints(account); - refreshUi(); + public void onKeyStatusUpdated(final AxolotlService.FetchStatus report) { + if (report != null) { + runOnUiThread(new Runnable() { + @Override + public void run() { + switch (report) { + case ERROR: + Toast.makeText(TrustKeysActivity.this,R.string.error_fetching_omemo_key,Toast.LENGTH_SHORT).show(); + break; + case SUCCESS_VERIFIED: + Toast.makeText(TrustKeysActivity.this,R.string.verified_omemo_key_with_certificate,Toast.LENGTH_LONG).show(); + break; + } + } + }); + + } + boolean keysToTrust = reloadFingerprints(); + if (keysToTrust || hasPendingKeyFetches() || hasNoOtherTrustedKeys()) { + refreshUi(); + } else { + runOnUiThread(new Runnable() { + @Override + public void run() { + finishOk(); + } + }); + + } + } + + private void finishOk() { + Intent data = new Intent(); + data.putExtra("choice", getIntent().getIntExtra("choice", ConversationActivity.ATTACHMENT_CHOICE_INVALID)); + setResult(RESULT_OK, data); + finish(); } private void commitTrusts() { @@ -248,7 +274,7 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate } private void lockOrUnlockAsNeeded() { - if (!hasOtherTrustedKeys && !foreignKeysToTrust.values().contains(true)){ + if (hasNoOtherTrustedKeys() && !foreignKeysToTrust.values().contains(true)){ lock(); } else { unlock(); diff --git a/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java b/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java index e9ad71971..8091a9966 100644 --- a/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java @@ -1,16 +1,21 @@ package eu.siacs.conversations.utils; +import android.util.Log; import android.util.Pair; 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 java.security.SecureRandom; import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateParsingException; import java.security.cert.X509Certificate; +import java.security.cert.X509Extension; import java.text.Normalizer; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; @@ -137,11 +142,26 @@ public final class CryptoHelper { } } - public static Pair<Jid,String> extractJidAndName(X509Certificate certificate) throws CertificateEncodingException, InvalidJidException { + public static Pair<Jid,String> extractJidAndName(X509Certificate certificate) throws CertificateEncodingException, InvalidJidException, CertificateParsingException { + Collection<List<?>> alternativeNames = certificate.getSubjectAlternativeNames(); + List<String> emails = new ArrayList<>(); + if (alternativeNames != null) { + for(List<?> san : alternativeNames) { + Integer type = (Integer) san.get(0); + if (type == 1) { + emails.add((String) san.get(1)); + } + } + } X500Name x500name = new JcaX509CertificateHolder(certificate).getSubject(); - //String xmpp = IETFUtils.valueToString(x500name.getRDNs(new ASN1ObjectIdentifier("1.3.6.1.5.5.7.8.5"))[0].getFirst().getValue()); - String email = IETFUtils.valueToString(x500name.getRDNs(BCStyle.EmailAddress)[0].getFirst().getValue()); + if (emails.size() == 0) { + emails.add(IETFUtils.valueToString(x500name.getRDNs(BCStyle.EmailAddress)[0].getFirst().getValue())); + } String name = IETFUtils.valueToString(x500name.getRDNs(BCStyle.CN)[0].getFirst().getValue()); - return new Pair<>(Jid.fromString(email),name); + if (emails.size() >= 1) { + return new Pair<>(Jid.fromString(emails.get(0)), name); + } else { + return null; + } } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/OnKeyStatusUpdated.java b/src/main/java/eu/siacs/conversations/xmpp/OnKeyStatusUpdated.java index 65ae133d5..e7fc582e4 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/OnKeyStatusUpdated.java +++ b/src/main/java/eu/siacs/conversations/xmpp/OnKeyStatusUpdated.java @@ -1,5 +1,7 @@ package eu.siacs.conversations.xmpp; +import eu.siacs.conversations.crypto.axolotl.AxolotlService; + public interface OnKeyStatusUpdated { - public void onKeyStatusUpdated(); + public void onKeyStatusUpdated(AxolotlService.FetchStatus report); } diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index d21682a79..186672488 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -783,7 +783,7 @@ public class XmppConnection implements Runnable { String urlString = url.findChildContent("value"); URL uri = new URL(urlString); captcha = BitmapFactory.decodeStream(uri.openConnection().getInputStream()); - } catch(IOException e) { + } catch (IOException e) { Log.e(Config.LOGTAG, e.toString()); } } @@ -884,7 +884,7 @@ public class XmppConnection implements Runnable { public void onIqPacketReceived(Account account, IqPacket packet) { if (packet.getType() == IqPacket.TYPE.RESULT) { sendPostBindInitialization(); - } else if (packet.getType() != IqPacket.TYPE.TIMEOUT){ + } else if (packet.getType() != IqPacket.TYPE.TIMEOUT) { Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not init sessions"); disconnect(true); } @@ -947,11 +947,10 @@ public class XmppConnection implements Runnable { } } disco.put(jid, info); - if (account.getServer().equals(jid)) { + if ((jid.equals(account.getServer()) || jid.equals(account.getJid().toBareJid())) + && disco.containsKey(account.getServer()) + && disco.containsKey(account.getJid().toBareJid())) { enableAdvancedStreamFeatures(); - for (final OnAdvancedStreamFeaturesLoaded listener : advancedStreamFeaturesLoadedListeners) { - listener.onAdvancedStreamFeaturesAvailable(account); - } } } else { Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could not query disco info for "+jid.toString()); @@ -969,6 +968,9 @@ public class XmppConnection implements Runnable { Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": Requesting block list"); this.sendIqPacket(getIqGenerator().generateGetBlockList(), mXmppConnectionService.getIqParser()); } + for (final OnAdvancedStreamFeaturesLoaded listener : advancedStreamFeaturesLoadedListeners) { + listener.onAdvancedStreamFeaturesAvailable(account); + } } private void sendServiceDiscoveryItems(final Jid server) { @@ -1139,44 +1141,41 @@ public class XmppConnection implements Runnable { } public void disconnect(final boolean force) { - Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": disconnecting"); - try { - if (force) { + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": disconnecting force="+Boolean.valueOf(force)); + if (force) { + try { socket.close(); - return; + } catch(Exception e) { + Log.d(Config.LOGTAG,account.getJid().toBareJid().toString()+": exception during force close ("+e.getMessage()+")"); } - new Thread(new Runnable() { - - @Override - public void run() { - if (tagWriter.isActive()) { - tagWriter.finish(); - try { - int i = 0; - boolean warned = false; - while (!tagWriter.finished() && socket.isConnected() && i <= 10) { - if (!warned) { - Log.d(Config.LOGTAG, account.getJid().toBareJid()+": waiting for tag writer to finish"); - warned = true; - } - Thread.sleep(200); - i++; - } - if (warned) { - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": tag writer has finished"); - } - tagWriter.writeTag(Tag.end("stream:stream")); - socket.close(); - } catch (final IOException e) { - Log.d(Config.LOGTAG,"io exception during disconnect"); - } catch (final InterruptedException e) { - Log.d(Config.LOGTAG, "interrupted"); + return; + } else { + resetStreamId(); + if (tagWriter.isActive()) { + tagWriter.finish(); + try { + int i = 0; + boolean warned = false; + while (!tagWriter.finished() && socket.isConnected() && i <= 10) { + if (!warned) { + Log.d(Config.LOGTAG, account.getJid().toBareJid()+": waiting for tag writer to finish"); + warned = true; } + Thread.sleep(200); + i++; + } + if (warned) { + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": tag writer has finished"); } + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": closing stream"); + tagWriter.writeTag(Tag.end("stream:stream")); + socket.close(); + } catch (final IOException e) { + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": io exception during disconnect ("+e.getMessage()+")"); + } catch (final InterruptedException e) { + Log.d(Config.LOGTAG, "interrupted"); } - }).start(); - } catch (final IOException e) { - Log.d(Config.LOGTAG, "io exception during disconnect"); + } } } |