diff options
Diffstat (limited to 'src/main/java/eu/siacs')
19 files changed, 377 insertions, 338 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 f3775b793..d700b644b 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java @@ -307,21 +307,25 @@ public class AxolotlService { mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() { @Override public void onIqPacketReceived(Account account, IqPacket packet) { - Element item = mXmppConnectionService.getIqParser().getItem(packet); - Set<Integer> deviceIds = mXmppConnectionService.getIqParser().deviceIds(item); - if (deviceIds == null) { - deviceIds = new HashSet<Integer>(); - } - if (!deviceIds.contains(getOwnDeviceId())) { - deviceIds.add(getOwnDeviceId()); - IqPacket publish = mXmppConnectionService.getIqGenerator().publishDeviceIds(deviceIds); - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Own device " + getOwnDeviceId() + " not in PEP devicelist. Publishing: " + publish); - mXmppConnectionService.sendIqPacket(account, publish, new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - // TODO: implement this! - } - }); + if (packet.getType() == IqPacket.TYPE.RESULT) { + Element item = mXmppConnectionService.getIqParser().getItem(packet); + Set<Integer> deviceIds = mXmppConnectionService.getIqParser().deviceIds(item); + if (deviceIds == null) { + deviceIds = new HashSet<Integer>(); + } + if (!deviceIds.contains(getOwnDeviceId())) { + deviceIds.add(getOwnDeviceId()); + IqPacket publish = mXmppConnectionService.getIqGenerator().publishDeviceIds(deviceIds); + Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Own device " + getOwnDeviceId() + " not in PEP devicelist. Publishing: " + publish); + mXmppConnectionService.sendIqPacket(account, publish, new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + // TODO: implement this! + } + }); + } + } else { + Log.d(Config.LOGTAG, getLogprefix(account) + "Error received while publishing device ID:" + packet.findChild("error")); } } }); @@ -332,88 +336,92 @@ public class AxolotlService { mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() { @Override public void onIqPacketReceived(Account account, IqPacket packet) { - PreKeyBundle bundle = mXmppConnectionService.getIqParser().bundle(packet); - Map<Integer, ECPublicKey> keys = mXmppConnectionService.getIqParser().preKeyPublics(packet); - boolean flush = false; - if (bundle == null) { - Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Received invalid bundle:" + packet); - bundle = new PreKeyBundle(-1, -1, -1, null, -1, null, null, null); - flush = true; - } - if (keys == null) { - Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Received invalid prekeys:" + packet); - } - try { - boolean changed = false; - // Validate IdentityKey - IdentityKeyPair identityKeyPair = axolotlStore.getIdentityKeyPair(); - if (flush || !identityKeyPair.getPublicKey().equals(bundle.getIdentityKey())) { - Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Adding own IdentityKey " + identityKeyPair.getPublicKey() + " to PEP."); - changed = true; + if (packet.getType() == IqPacket.TYPE.RESULT) { + PreKeyBundle bundle = mXmppConnectionService.getIqParser().bundle(packet); + Map<Integer, ECPublicKey> keys = mXmppConnectionService.getIqParser().preKeyPublics(packet); + boolean flush = false; + if (bundle == null) { + Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Received invalid bundle:" + packet); + bundle = new PreKeyBundle(-1, -1, -1, null, -1, null, null, null); + flush = true; + } + if (keys == null) { + Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Received invalid prekeys:" + packet); } - - // Validate signedPreKeyRecord + ID - SignedPreKeyRecord signedPreKeyRecord; - int numSignedPreKeys = axolotlStore.loadSignedPreKeys().size(); try { - signedPreKeyRecord = axolotlStore.loadSignedPreKey(bundle.getSignedPreKeyId()); - if (flush - || !bundle.getSignedPreKey().equals(signedPreKeyRecord.getKeyPair().getPublicKey()) - || !Arrays.equals(bundle.getSignedPreKeySignature(), signedPreKeyRecord.getSignature())) { + boolean changed = false; + // Validate IdentityKey + IdentityKeyPair identityKeyPair = axolotlStore.getIdentityKeyPair(); + if (flush || !identityKeyPair.getPublicKey().equals(bundle.getIdentityKey())) { + Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Adding own IdentityKey " + identityKeyPair.getPublicKey() + " to PEP."); + changed = true; + } + + // Validate signedPreKeyRecord + ID + SignedPreKeyRecord signedPreKeyRecord; + int numSignedPreKeys = axolotlStore.loadSignedPreKeys().size(); + try { + signedPreKeyRecord = axolotlStore.loadSignedPreKey(bundle.getSignedPreKeyId()); + if (flush + || !bundle.getSignedPreKey().equals(signedPreKeyRecord.getKeyPair().getPublicKey()) + || !Arrays.equals(bundle.getSignedPreKeySignature(), signedPreKeyRecord.getSignature())) { + Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Adding new signedPreKey with ID " + (numSignedPreKeys + 1) + " to PEP."); + signedPreKeyRecord = KeyHelper.generateSignedPreKey(identityKeyPair, numSignedPreKeys + 1); + axolotlStore.storeSignedPreKey(signedPreKeyRecord.getId(), signedPreKeyRecord); + changed = true; + } + } catch (InvalidKeyIdException e) { Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Adding new signedPreKey with ID " + (numSignedPreKeys + 1) + " to PEP."); signedPreKeyRecord = KeyHelper.generateSignedPreKey(identityKeyPair, numSignedPreKeys + 1); axolotlStore.storeSignedPreKey(signedPreKeyRecord.getId(), signedPreKeyRecord); changed = true; } - } catch (InvalidKeyIdException e) { - Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Adding new signedPreKey with ID " + (numSignedPreKeys + 1) + " to PEP."); - signedPreKeyRecord = KeyHelper.generateSignedPreKey(identityKeyPair, numSignedPreKeys + 1); - axolotlStore.storeSignedPreKey(signedPreKeyRecord.getId(), signedPreKeyRecord); - changed = true; - } - // Validate PreKeys - Set<PreKeyRecord> preKeyRecords = new HashSet<>(); - if (keys != null) { - for (Integer id : keys.keySet()) { - try { - PreKeyRecord preKeyRecord = axolotlStore.loadPreKey(id); - if (preKeyRecord.getKeyPair().getPublicKey().equals(keys.get(id))) { - preKeyRecords.add(preKeyRecord); + // Validate PreKeys + Set<PreKeyRecord> preKeyRecords = new HashSet<>(); + if (keys != null) { + for (Integer id : keys.keySet()) { + try { + PreKeyRecord preKeyRecord = axolotlStore.loadPreKey(id); + if (preKeyRecord.getKeyPair().getPublicKey().equals(keys.get(id))) { + preKeyRecords.add(preKeyRecord); + } + } catch (InvalidKeyIdException ignored) { } - } catch (InvalidKeyIdException ignored) { } } - } - int newKeys = NUM_KEYS_TO_PUBLISH - preKeyRecords.size(); - if (newKeys > 0) { - List<PreKeyRecord> newRecords = KeyHelper.generatePreKeys( - axolotlStore.getCurrentPreKeyId() + 1, newKeys); - preKeyRecords.addAll(newRecords); - for (PreKeyRecord record : newRecords) { - axolotlStore.storePreKey(record.getId(), record); + int newKeys = NUM_KEYS_TO_PUBLISH - preKeyRecords.size(); + if (newKeys > 0) { + List<PreKeyRecord> newRecords = KeyHelper.generatePreKeys( + axolotlStore.getCurrentPreKeyId() + 1, newKeys); + preKeyRecords.addAll(newRecords); + for (PreKeyRecord record : newRecords) { + axolotlStore.storePreKey(record.getId(), record); + } + changed = true; + Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Adding " + newKeys + " new preKeys to PEP."); } - changed = true; - Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Adding " + newKeys + " new preKeys to PEP."); - } - if (changed) { - IqPacket publish = mXmppConnectionService.getIqGenerator().publishBundles( - signedPreKeyRecord, axolotlStore.getIdentityKeyPair().getPublicKey(), - preKeyRecords, getOwnDeviceId()); - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + ": Bundle " + getOwnDeviceId() + " in PEP not current. Publishing: " + publish); - mXmppConnectionService.sendIqPacket(account, publish, new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - // TODO: implement this! - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Published bundle, got: " + packet); - } - }); + if (changed) { + IqPacket publish = mXmppConnectionService.getIqGenerator().publishBundles( + signedPreKeyRecord, axolotlStore.getIdentityKeyPair().getPublicKey(), + preKeyRecords, getOwnDeviceId()); + Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + ": Bundle " + getOwnDeviceId() + " in PEP not current. Publishing: " + publish); + mXmppConnectionService.sendIqPacket(account, publish, new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + // TODO: implement this! + Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Published bundle, got: " + packet); + } + }); + } + } catch (InvalidKeyException e) { + Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Failed to publish bundle " + getOwnDeviceId() + ", reason: " + e.getMessage()); + return; } - } catch (InvalidKeyException e) { - Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Failed to publish bundle " + getOwnDeviceId() + ", reason: " + e.getMessage()); - return; + } else { + Log.d(Config.LOGTAG, getLogprefix(account) + "Error received while publishing Bundle:" + packet.findChild("error")); } } }); @@ -453,45 +461,52 @@ public class AxolotlService { @Override public void onIqPacketReceived(Account account, IqPacket packet) { - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Received preKey IQ packet, processing..."); - final IqParser parser = mXmppConnectionService.getIqParser(); - final List<PreKeyBundle> preKeyBundleList = parser.preKeys(packet); - final PreKeyBundle bundle = parser.bundle(packet); - if (preKeyBundleList.isEmpty() || bundle == null) { - Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "preKey IQ packet invalid: " + packet); - fetchStatusMap.put(address, FetchStatus.ERROR); + 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); + final PreKeyBundle bundle = parser.bundle(packet); + if (preKeyBundleList.isEmpty() || bundle == null) { + Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "preKey IQ packet invalid: " + packet); + fetchStatusMap.put(address, FetchStatus.ERROR); + finish(); + return; + } + Random random = new Random(); + final PreKeyBundle preKey = preKeyBundleList.get(random.nextInt(preKeyBundleList.size())); + if (preKey == null) { + //should never happen + fetchStatusMap.put(address, FetchStatus.ERROR); + finish(); + return; + } + + final PreKeyBundle preKeyBundle = new PreKeyBundle(0, address.getDeviceId(), + preKey.getPreKeyId(), preKey.getPreKey(), + bundle.getSignedPreKeyId(), bundle.getSignedPreKey(), + bundle.getSignedPreKeySignature(), bundle.getIdentityKey()); + + axolotlStore.saveIdentity(address.getName(), bundle.getIdentityKey()); + + try { + SessionBuilder builder = new SessionBuilder(axolotlStore, address); + builder.process(preKeyBundle); + XmppAxolotlSession session = new XmppAxolotlSession(account, axolotlStore, address, bundle.getIdentityKey().getFingerprint().replaceAll("\\s", "")); + sessions.put(address, session); + fetchStatusMap.put(address, FetchStatus.SUCCESS); + } 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); + } + finish(); - return; - } - Random random = new Random(); - final PreKeyBundle preKey = preKeyBundleList.get(random.nextInt(preKeyBundleList.size())); - if (preKey == null) { - //should never happen + } else { fetchStatusMap.put(address, FetchStatus.ERROR); + Log.d(Config.LOGTAG, getLogprefix(account) + "Error received while building session:" + packet.findChild("error")); finish(); return; } - - final PreKeyBundle preKeyBundle = new PreKeyBundle(0, address.getDeviceId(), - preKey.getPreKeyId(), preKey.getPreKey(), - bundle.getSignedPreKeyId(), bundle.getSignedPreKey(), - bundle.getSignedPreKeySignature(), bundle.getIdentityKey()); - - axolotlStore.saveIdentity(address.getName(), bundle.getIdentityKey()); - - try { - SessionBuilder builder = new SessionBuilder(axolotlStore, address); - builder.process(preKeyBundle); - XmppAxolotlSession session = new XmppAxolotlSession(account, axolotlStore, address, bundle.getIdentityKey().getFingerprint().replaceAll("\\s", "")); - sessions.put(address, session); - fetchStatusMap.put(address, FetchStatus.SUCCESS); - } 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); - } - - finish(); } }); } catch (InvalidJidException e) { diff --git a/src/main/java/eu/siacs/conversations/crypto/sasl/ScramSha1.java b/src/main/java/eu/siacs/conversations/crypto/sasl/ScramSha1.java index c95a62df3..f47677f6e 100644 --- a/src/main/java/eu/siacs/conversations/crypto/sasl/ScramSha1.java +++ b/src/main/java/eu/siacs/conversations/crypto/sasl/ScramSha1.java @@ -101,6 +101,9 @@ public class ScramSha1 extends SaslMechanism { public String getResponse(final String challenge) throws AuthenticationException { switch (state) { case AUTH_TEXT_SENT: + if (challenge == null) { + throw new AuthenticationException("challenge can not be null"); + } serverFirstMessage = Base64.decode(challenge, Base64.DEFAULT); final Tokenizer tokenizer = new Tokenizer(serverFirstMessage); String nonce = ""; diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java index 3a73f78bf..cea0d700f 100644 --- a/src/main/java/eu/siacs/conversations/entities/Conversation.java +++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java @@ -344,14 +344,14 @@ public class Conversation extends AbstractEntity implements Blockable { return this.getContact().getDisplayName(); } } - + public String getParticipants() { if (getMode() == MODE_MULTI) { String generatedName = getMucOptions().createNameFromParticipants(); if (generatedName != null) { return generatedName; } else { - return null; //getJid().getLocalpart(); + return null; } } else { return null; @@ -566,7 +566,7 @@ public class Conversation extends AbstractEntity implements Blockable { private int getMostRecentlyUsedOutgoingEncryption() { synchronized (this.messages) { for(int i = this.messages.size() -1; i >= 0; --i) { - final Message m = this.messages.get(0); + final Message m = this.messages.get(i); if (!m.isCarbon() && m.getStatus() != Message.STATUS_RECEIVED) { final int e = m.getEncryption(); if (e == Message.ENCRYPTION_DECRYPTED || e == Message.ENCRYPTION_DECRYPTION_FAILED) { @@ -583,7 +583,7 @@ public class Conversation extends AbstractEntity implements Blockable { private int getMostRecentlyUsedIncomingEncryption() { synchronized (this.messages) { for(int i = this.messages.size() -1; i >= 0; --i) { - final Message m = this.messages.get(0); + final Message m = this.messages.get(i); if (m.getStatus() == Message.STATUS_RECEIVED) { final int e = m.getEncryption(); if (e == Message.ENCRYPTION_DECRYPTED || e == Message.ENCRYPTION_DECRYPTION_FAILED) { diff --git a/src/main/java/eu/siacs/conversations/parser/IqParser.java b/src/main/java/eu/siacs/conversations/parser/IqParser.java index e74cb65c0..cef5b03d9 100644 --- a/src/main/java/eu/siacs/conversations/parser/IqParser.java +++ b/src/main/java/eu/siacs/conversations/parser/IqParser.java @@ -236,7 +236,9 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived { @Override public void onIqPacketReceived(final Account account, final IqPacket packet) { - if (packet.hasChild("query", Xmlns.ROSTER) && packet.fromServer(account)) { + if (packet.getType() == IqPacket.TYPE.ERROR || packet.getType() == IqPacket.TYPE.TIMEOUT) { + return; + } else if (packet.hasChild("query", Xmlns.ROSTER) && packet.fromServer(account)) { final Element query = packet.findChild("query"); // If this is in response to a query for the whole roster: if (packet.getType() == IqPacket.TYPE.RESULT) { @@ -306,15 +308,13 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived { final IqPacket response = packet.generateResponse(IqPacket.TYPE.RESULT); mXmppConnectionService.sendIqPacket(account, response, null); } else { - if ((packet.getType() == IqPacket.TYPE.GET) - || (packet.getType() == IqPacket.TYPE.SET)) { + if (packet.getType() == IqPacket.TYPE.GET || packet.getType() == IqPacket.TYPE.SET) { final IqPacket response = packet.generateResponse(IqPacket.TYPE.ERROR); final Element error = response.addChild("error"); error.setAttribute("type", "cancel"); - error.addChild("feature-not-implemented", - "urn:ietf:params:xml:ns:xmpp-stanzas"); + error.addChild("feature-not-implemented","urn:ietf:params:xml:ns:xmpp-stanzas"); account.getXmppConnection().sendIqPacket(response, null); - } + } } } diff --git a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java index d610fcab8..a0c676bf9 100644 --- a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java @@ -133,7 +133,6 @@ public class FileBackend { if (path == null) { return false; } - Log.d(Config.LOGTAG,"using image as is. path: "+path); BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; try { @@ -358,11 +357,7 @@ public class FileBackend { file.delete(); return false; } - } catch (FileNotFoundException e) { - return false; - } catch (IOException e) { - return false; - } catch (NoSuchAlgorithmException e) { + } catch (IllegalArgumentException | IOException | NoSuchAlgorithmException e) { return false; } finally { close(os); @@ -389,6 +384,9 @@ public class FileBackend { BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = calcSampleSize(image, size); is = mXmppConnectionService.getContentResolver().openInputStream(image); + if (is == null) { + return null; + } Bitmap input = BitmapFactory.decodeStream(is, null, options); if (input == null) { return null; @@ -415,6 +413,9 @@ public class FileBackend { BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = calcSampleSize(image,Math.max(newHeight, newWidth)); is = mXmppConnectionService.getContentResolver().openInputStream(image); + if (is == null) { + return null; + } Bitmap source = BitmapFactory.decodeStream(is, null, options); if (source == null) { return null; diff --git a/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java b/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java index 0bc428c88..a31848a7c 100644 --- a/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java +++ b/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java @@ -111,7 +111,7 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded { this.mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() { @Override public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType() == IqPacket.TYPE.ERROR) { + if (packet.getType() != IqPacket.TYPE.RESULT) { Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": error executing mam: " + packet.toString()); finalizeQuery(query); } diff --git a/src/main/java/eu/siacs/conversations/services/NotificationService.java b/src/main/java/eu/siacs/conversations/services/NotificationService.java index 7dcf2e83a..3b8da6b39 100644 --- a/src/main/java/eu/siacs/conversations/services/NotificationService.java +++ b/src/main/java/eu/siacs/conversations/services/NotificationService.java @@ -62,12 +62,12 @@ public class NotificationService { public boolean notify(final Message message) { return (message.getStatus() == Message.STATUS_RECEIVED) - && notificationsEnabled() - && !message.getConversation().isMuted() - && (message.getConversation().isPnNA() - || conferenceNotificationsEnabled() - || wasHighlightedOrPrivate(message) - ); + && notificationsEnabled() + && !message.getConversation().isMuted() + && (message.getConversation().isPnNA() + || conferenceNotificationsEnabled() + || wasHighlightedOrPrivate(message) + ); } public void notifyPebble(final Message message) { @@ -153,7 +153,9 @@ public class NotificationService { notifications.put(conversationUuid, mList); } final Account account = message.getConversation().getAccount(); - final boolean doNotify = !account.inGracePeriod() && !this.inMiniGracePeriod(account); + final boolean doNotify = (!(this.mIsInForeground && this.mOpenConversation == null) || !isScreenOn) + && !account.inGracePeriod() + && !this.inMiniGracePeriod(account); updateNotification(doNotify); if (doNotify) { notifyPebble(message); @@ -295,7 +297,7 @@ public class NotificationService { final ArrayList<Message> messages, final boolean notify) { try { final Bitmap bitmap = mXmppConnectionService.getFileBackend() - .getThumbnail(message, getPixel(288), false); + .getThumbnail(message, getPixel(200), false); final ArrayList<Message> tmp = new ArrayList<>(); for (final Message msg : messages) { if (msg.getType() == Message.TYPE_TEXT @@ -436,7 +438,7 @@ public class NotificationService { private PendingIntent createDisableAccountIntent(final Account account) { final Intent intent = new Intent(mXmppConnectionService,XmppConnectionService.class); intent.setAction(XmppConnectionService.ACTION_DISABLE_ACCOUNT); - intent.putExtra("account", account.getJid().toBareJid().toString()); + intent.putExtra("account",account.getJid().toBareJid().toString()); return PendingIntent.getService(mXmppConnectionService,0,intent,PendingIntent.FLAG_UPDATE_CURRENT); } @@ -557,4 +559,4 @@ public class NotificationService { mBuilder.setContentIntent(resultPendingIntent); mNotificationManager.notify(ERROR_NOTIFICATION_ID, mBuilder.build()); } -} +}
\ No newline at end of file diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index a9a2f2117..b08a71bfa 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -150,13 +150,23 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa @Override public void onBind(final Account account) { account.getRoster().clearPresences(); - account.pendingConferenceJoins.clear(); - account.pendingConferenceLeaves.clear(); fetchRosterFromServer(account); fetchBookmarks(account); sendPresence(account); connectMultiModeConversations(account); - updateConversationUi(); + for (Conversation conversation : account.pendingConferenceLeaves) { + leaveMuc(conversation); + } + account.pendingConferenceLeaves.clear(); + for (Conversation conversation : account.pendingConferenceJoins) { + joinMuc(conversation); + } + account.pendingConferenceJoins.clear(); + mMessageArchiveService.executePendingQueries(account); + mJingleConnectionManager.cancelInTransmission(); + syncDirtyContacts(account); + account.getAxolotlService().publishOwnDeviceIdIfNeeded(); + account.getAxolotlService().publishBundlesIfNeeded(); } }; private final OnMessageAcknowledged mOnMessageAcknowledgedListener = new OnMessageAcknowledged() { @@ -214,7 +224,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa private OnIqPacketReceived mDefaultIqHandler = new OnIqPacketReceived() { @Override public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType() == IqPacket.TYPE.ERROR) { + if (packet.getType() != IqPacket.TYPE.RESULT) { Element error = packet.findChild("error"); String text = error != null ? error.findChildContent("text") : null; if (text != null) { @@ -248,14 +258,15 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa mOnAccountUpdate.onAccountUpdate(); } if (account.getStatus() == Account.State.ONLINE) { - for (Conversation conversation : account.pendingConferenceLeaves) { - leaveMuc(conversation); - } - for (Conversation conversation : account.pendingConferenceJoins) { - joinMuc(conversation); + if (connection != null && connection.getFeatures().csi()) { + if (checkListeners()) { + Log.d(Config.LOGTAG, account.getJid().toBareJid()+ " sending csi//inactive"); + connection.sendInactive(); + } else { + Log.d(Config.LOGTAG, account.getJid().toBareJid()+ " sending csi//active"); + connection.sendActive(); + } } - mMessageArchiveService.executePendingQueries(account); - mJingleConnectionManager.cancelInTransmission(); List<Conversation> conversations = getConversations(); for (Conversation conversation : conversations) { if (conversation.getAccount() == account) { @@ -263,23 +274,9 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa sendUnsentMessages(conversation); } } - if (connection != null && connection.getFeatures().csi()) { - if (checkListeners()) { - Log.d(Config.LOGTAG, account.getJid().toBareJid() - + " sending csi//inactive"); - connection.sendInactive(); - } else { - Log.d(Config.LOGTAG, account.getJid().toBareJid() - + " sending csi//active"); - connection.sendActive(); - } - } - syncDirtyContacts(account); - account.getAxolotlService().publishOwnDeviceIdIfNeeded(); - account.getAxolotlService().publishBundlesIfNeeded(); - scheduleWakeUpCall(Config.PING_MAX_INTERVAL, account.getUuid().hashCode()); } else if (account.getStatus() == Account.State.OFFLINE) { + resetSendingToWaiting(account); if (!account.isOptionSet(Account.OPTION_DISABLED)) { int timeToReconnect = mRandom.nextInt(20) + 10; scheduleWakeUpCall(timeToReconnect,account.getUuid().hashCode()); @@ -859,7 +856,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": fetching roster"); } iqPacket.query(Xmlns.ROSTER).setAttribute("ver", account.getRosterVersion()); - sendIqPacket(account,iqPacket,mIqParser); + sendIqPacket(account, iqPacket, mIqParser); } public void fetchBookmarks(final Account account) { @@ -870,28 +867,31 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa @Override public void onIqPacketReceived(final Account account, final IqPacket packet) { - final Element query = packet.query(); - final List<Bookmark> bookmarks = new CopyOnWriteArrayList<>(); - final Element storage = query.findChild("storage", - "storage:bookmarks"); - if (storage != null) { - for (final Element item : storage.getChildren()) { - if (item.getName().equals("conference")) { - final Bookmark bookmark = Bookmark.parse(item, account); - bookmarks.add(bookmark); - Conversation conversation = find(bookmark); - if (conversation != null) { - conversation.setBookmark(bookmark); - } else if (bookmark.autojoin() && bookmark.getJid() != null) { - conversation = findOrCreateConversation( - account, bookmark.getJid(), true); - conversation.setBookmark(bookmark); - joinMuc(conversation); + if (packet.getType() == IqPacket.TYPE.RESULT) { + final Element query = packet.query(); + final List<Bookmark> bookmarks = new CopyOnWriteArrayList<>(); + final Element storage = query.findChild("storage", "storage:bookmarks"); + if (storage != null) { + for (final Element item : storage.getChildren()) { + if (item.getName().equals("conference")) { + final Bookmark bookmark = Bookmark.parse(item, account); + bookmarks.add(bookmark); + Conversation conversation = find(bookmark); + if (conversation != null) { + conversation.setBookmark(bookmark); + } else if (bookmark.autojoin() && bookmark.getJid() != null) { + conversation = findOrCreateConversation( + account, bookmark.getJid(), true); + conversation.setBookmark(bookmark); + joinMuc(conversation); + } } } } + account.setBookmarks(bookmarks); + } else { + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could not fetch bookmarks"); } - account.setBookmarks(bookmarks); } }; sendIqPacket(account, iqPacket, callback); @@ -1478,6 +1478,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } } + public void joinMuc(Conversation conversation) { Account account = conversation.getAccount(); account.pendingConferenceJoins.remove(conversation); @@ -1674,7 +1675,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa sendIqPacket(conversation.getAccount(), request, new OnIqPacketReceived() { @Override public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType() != IqPacket.TYPE.ERROR) { + if (packet.getType() == IqPacket.TYPE.RESULT) { ArrayList<String> features = new ArrayList<>(); for (Element child : packet.query().getChildren()) { if (child != null && child.getName().equals("feature")) { @@ -1698,7 +1699,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa sendIqPacket(conversation.getAccount(), request, new OnIqPacketReceived() { @Override public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType() != IqPacket.TYPE.ERROR) { + if (packet.getType() == IqPacket.TYPE.RESULT) { Data data = Data.parse(packet.query().findChild("x", "jabber:x:data")); for (Field field : data.getFields()) { if (options.containsKey(field.getName())) { @@ -1712,12 +1713,10 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa sendIqPacket(account, set, new OnIqPacketReceived() { @Override public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType() == IqPacket.TYPE.RESULT) { - if (callback != null) { + if (callback != null) { + if (packet.getType() == IqPacket.TYPE.RESULT) { callback.onPushSucceeded(); - } - } else { - if (callback != null) { + } else { callback.onPushFailed(); } } @@ -1954,10 +1953,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa final IqPacket packet = XmppConnectionService.this.mIqGenerator .publishAvatarMetadata(avatar); sendIqPacket(account, packet, new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(Account account, - IqPacket result) { + public void onIqPacketReceived(Account account, IqPacket result) { if (result.getType() == IqPacket.TYPE.RESULT) { if (account.setAvatar(avatar.getFilename())) { getAvatarService().clear(account); diff --git a/src/main/java/eu/siacs/conversations/utils/MimeUtils.java b/src/main/java/eu/siacs/conversations/utils/MimeUtils.java index a9e89d1b7..d45444240 100644 --- a/src/main/java/eu/siacs/conversations/utils/MimeUtils.java +++ b/src/main/java/eu/siacs/conversations/utils/MimeUtils.java @@ -458,7 +458,7 @@ public final class MimeUtils { if (extension == null || extension.isEmpty()) { return null; } - return extensionToMimeTypeMap.get(extension); + return extensionToMimeTypeMap.get(extension.toLowerCase()); } /** * Returns true if the given extension has a registered MIME type. diff --git a/src/main/java/eu/siacs/conversations/utils/PhoneHelper.java b/src/main/java/eu/siacs/conversations/utils/PhoneHelper.java index b90f06ff9..a37f60a0f 100644 --- a/src/main/java/eu/siacs/conversations/utils/PhoneHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/PhoneHelper.java @@ -97,7 +97,7 @@ public class PhoneHelper { if (packageName != null) { try { return context.getPackageManager().getPackageInfo(packageName, 0).versionName; - } catch (final PackageManager.NameNotFoundException e) { + } catch (final PackageManager.NameNotFoundException | RuntimeException e) { return "unknown"; } } else { diff --git a/src/main/java/eu/siacs/conversations/xml/Element.java b/src/main/java/eu/siacs/conversations/xml/Element.java index dc5a68f6c..7b4937b26 100644 --- a/src/main/java/eu/siacs/conversations/xml/Element.java +++ b/src/main/java/eu/siacs/conversations/xml/Element.java @@ -69,10 +69,9 @@ public class Element { public Element findChild(String name, String xmlns) { for (Element child : this.children) { - if (child.getName().equals(name) - && (child.getAttribute("xmlns").equals(xmlns))) { + if (name.equals(child.getName()) && xmlns.equals(child.getAttribute("xmlns"))) { return child; - } + } } return null; } diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index 5ac089766..376858323 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -1,13 +1,10 @@ package eu.siacs.conversations.xmpp; -import android.content.Context; -import android.content.SharedPreferences; import android.os.Bundle; import android.os.Parcelable; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.os.SystemClock; -import android.preference.PreferenceManager; import android.util.Log; import android.util.Pair; import android.util.SparseArray; @@ -37,7 +34,6 @@ import java.util.Hashtable; import java.util.Iterator; import java.util.LinkedList; import java.util.List; -import java.util.Map; import java.util.Map.Entry; import javax.net.ssl.HostnameVerifier; @@ -52,6 +48,7 @@ import eu.siacs.conversations.crypto.sasl.Plain; import eu.siacs.conversations.crypto.sasl.SaslMechanism; import eu.siacs.conversations.crypto.sasl.ScramSha1; import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.generator.IqGenerator; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.utils.CryptoHelper; @@ -65,6 +62,7 @@ import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.jingle.OnJinglePacketReceived; import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket; +import eu.siacs.conversations.xmpp.stanzas.AbstractAcknowledgeableStanza; import eu.siacs.conversations.xmpp.stanzas.AbstractStanza; import eu.siacs.conversations.xmpp.stanzas.IqPacket; import eu.siacs.conversations.xmpp.stanzas.MessagePacket; @@ -87,14 +85,14 @@ public class XmppConnection implements Runnable { private XmlReader tagReader; private TagWriter tagWriter; private final Features features = new Features(this); - private boolean shouldBind = true; + private boolean needsBinding = true; private boolean shouldAuthenticate = true; private Element streamFeatures; private final HashMap<Jid, Info> disco = new HashMap<>(); private String streamId = null; private int smVersion = 3; - private final SparseArray<String> mStanzaReceipts = new SparseArray<>(); + private final SparseArray<AbstractAcknowledgeableStanza> mStanzaQueue = new SparseArray<>(); private int stanzasReceived = 0; private int stanzasSent = 0; @@ -103,7 +101,7 @@ public class XmppConnection implements Runnable { private long lastConnect = 0; private long lastSessionStarted = 0; private int attempt = 0; - private final Map<String, Pair<IqPacket, OnIqPacketReceived>> packetCallbacks = new Hashtable<>(); + private final Hashtable<String, Pair<IqPacket, OnIqPacketReceived>> packetCallbacks = new Hashtable<>(); private OnPresencePacketReceived presenceListener = null; private OnJinglePacketReceived jingleListener = null; private OnIqPacketReceived unregisteredIqListener = null; @@ -149,10 +147,9 @@ public class XmppConnection implements Runnable { lastPingSent = SystemClock.elapsedRealtime(); this.attempt++; try { - shouldAuthenticate = shouldBind = !account.isOptionSet(Account.OPTION_REGISTER); + shouldAuthenticate = needsBinding = !account.isOptionSet(Account.OPTION_REGISTER); tagReader = new XmlReader(wakeLock); tagWriter = new TagWriter(); - packetCallbacks.clear(); this.changeStatus(Account.State.CONNECTING); if (DNSHelper.isIp(account.getServer().toString())) { socket = new Socket(); @@ -199,10 +196,7 @@ public class XmppConnection implements Runnable { socket = new Socket(); socket.connect(addr, Config.SOCKET_TIMEOUT * 1000); socketError = false; - } catch (final UnknownHostException e) { - Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage()); - i++; - } catch (final IOException e) { + } catch (final Throwable e) { Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage()); i++; } @@ -322,7 +316,7 @@ public class XmppConnection implements Runnable { + ") enabled (resumable)"); } else { Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() - + ": stream managment(" + smVersion + ") enabled"); + + ": stream management(" + smVersion + ") enabled"); } this.lastSessionStarted = SystemClock.elapsedRealtime(); this.stanzasReceived = 0; @@ -342,22 +336,26 @@ public class XmppConnection implements Runnable { Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": session resumed"); } acknowledgeStanzaUpTo(serverCount); - ArrayList<IqPacket> failedIqPackets = new ArrayList<>(); - for(int i = 0; i < this.mStanzaReceipts.size(); ++i) { - String id = mStanzaReceipts.valueAt(i); - Pair<IqPacket,OnIqPacketReceived> pair = id == null ? null : this.packetCallbacks.get(id); - if (pair != null) { - failedIqPackets.add(pair.first); - } + ArrayList<AbstractAcknowledgeableStanza> failedStanzas = new ArrayList<>(); + for(int i = 0; i < this.mStanzaQueue.size(); ++i) { + failedStanzas.add(mStanzaQueue.valueAt(i)); } - mStanzaReceipts.clear(); - Log.d(Config.LOGTAG,"resending "+failedIqPackets.size()+" iq stanza"); - for(IqPacket packet : failedIqPackets) { - sendUnmodifiedIqPacket(packet,null); + mStanzaQueue.clear(); + Log.d(Config.LOGTAG,"resending "+failedStanzas.size()+" stanzas"); + for(AbstractAcknowledgeableStanza packet : failedStanzas) { + if (packet instanceof MessagePacket) { + MessagePacket message = (MessagePacket) packet; + mXmppConnectionService.markMessage(account, + message.getTo().toBareJid(), + message.getId(), + Message.STATUS_UNSEND); + } + sendPacket(packet); } } catch (final NumberFormatException ignored) { } - sendInitialPing(); + Log.d(Config.LOGTAG, account.getJid().toBareJid()+ ": online with resource " + account.getResource()); + changeStatus(Account.State.ONLINE); } else if (nextTag.isStart("r")) { tagReader.readElement(nextTag); if (Config.EXTENDED_SM_LOGGING) { @@ -399,36 +397,22 @@ public class XmppConnection implements Runnable { } private void acknowledgeStanzaUpTo(int serverCount) { - for (int i = 0; i < mStanzaReceipts.size(); ++i) { - if (serverCount >= mStanzaReceipts.keyAt(i)) { + for (int i = 0; i < mStanzaQueue.size(); ++i) { + if (serverCount >= mStanzaQueue.keyAt(i)) { if (Config.EXTENDED_SM_LOGGING) { - Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": server acknowledged stanza #" + mStanzaReceipts.keyAt(i)); + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": server acknowledged stanza #" + mStanzaQueue.keyAt(i)); } - String id = mStanzaReceipts.valueAt(i); - if (acknowledgedListener != null) { - acknowledgedListener.onMessageAcknowledged(account, id); + AbstractAcknowledgeableStanza stanza = mStanzaQueue.valueAt(i); + if (stanza instanceof MessagePacket && acknowledgedListener != null) { + MessagePacket packet = (MessagePacket) stanza; + acknowledgedListener.onMessageAcknowledged(account, packet.getId()); } - mStanzaReceipts.removeAt(i); + mStanzaQueue.removeAt(i); i--; } } } - private void sendInitialPing() { - Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": sending intial ping"); - final IqPacket iq = new IqPacket(IqPacket.TYPE.GET); - iq.setFrom(account.getJid()); - iq.addChild("ping", "urn:xmpp:ping"); - this.sendIqPacket(iq, new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(final Account account, final IqPacket packet) { - Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() - + ": online with resource " + account.getResource()); - changeStatus(Account.State.ONLINE); - } - }); - } - private Element processPacket(final Tag currentTag, final int packetType) throws XmlPullParserException, IOException { Element element; @@ -489,26 +473,28 @@ public class XmppConnection implements Runnable { this.jingleListener.onJinglePacketReceived(account,(JinglePacket) packet); } } else { - if (packetCallbacks.containsKey(packet.getId())) { - final Pair<IqPacket, OnIqPacketReceived> packetCallbackDuple = packetCallbacks.get(packet.getId()); - // Packets to the server should have responses from the server - if (packetCallbackDuple.first.toServer(account)) { - if (packet.fromServer(account)) { - packetCallbackDuple.second.onIqPacketReceived(account, packet); - packetCallbacks.remove(packet.getId()); - } else { - Log.e(Config.LOGTAG,account.getJid().toBareJid().toString()+": ignoring spoofed iq packet"); - } - } else { - if (packet.getFrom().equals(packetCallbackDuple.first.getTo())) { - packetCallbackDuple.second.onIqPacketReceived(account, packet); - packetCallbacks.remove(packet.getId()); + synchronized (this.packetCallbacks) { + if (packetCallbacks.containsKey(packet.getId())) { + final Pair<IqPacket, OnIqPacketReceived> packetCallbackDuple = packetCallbacks.get(packet.getId()); + // Packets to the server should have responses from the server + if (packetCallbackDuple.first.toServer(account)) { + if (packet.fromServer(account)) { + packetCallbackDuple.second.onIqPacketReceived(account, packet); + packetCallbacks.remove(packet.getId()); + } else { + Log.e(Config.LOGTAG, account.getJid().toBareJid().toString() + ": ignoring spoofed iq packet"); + } } else { - Log.e(Config.LOGTAG,account.getJid().toBareJid().toString()+": ignoring spoofed iq packet"); + if (packet.getFrom().equals(packetCallbackDuple.first.getTo())) { + packetCallbackDuple.second.onIqPacketReceived(account, packet); + packetCallbacks.remove(packet.getId()); + } else { + Log.e(Config.LOGTAG, account.getJid().toBareJid().toString() + ": ignoring spoofed iq packet"); + } } + } else if (packet.getType() == IqPacket.TYPE.GET || packet.getType() == IqPacket.TYPE.SET) { + this.unregisteredIqListener.onIqPacketReceived(account, packet); } - } else if (packet.getType() == IqPacket.TYPE.GET|| packet.getType() == IqPacket.TYPE.SET) { - this.unregisteredIqListener.onIqPacketReceived(account, packet); } } } @@ -629,19 +615,18 @@ public class XmppConnection implements Runnable { } else { throw new IncompatibleServerException(); } - } else if (this.streamFeatures.hasChild("sm", "urn:xmpp:sm:" - + smVersion) - && streamId != null) { + } else if (this.streamFeatures.hasChild("sm", "urn:xmpp:sm:" + smVersion) && streamId != null) { if (Config.EXTENDED_SM_LOGGING) { Log.d(Config.LOGTAG,account.getJid().toBareJid()+": resuming after stanza #"+stanzasReceived); } final ResumePacket resume = new ResumePacket(this.streamId, stanzasReceived, smVersion); this.tagWriter.writeStanzaAsync(resume); - } else if (this.streamFeatures.hasChild("bind") && shouldBind) { - sendBindRequest(); - } else { - disconnect(true); - changeStatus(Account.State.INCOMPATIBLE_SERVER); + } else if (needsBinding) { + if (this.streamFeatures.hasChild("bind")) { + sendBindRequest(); + } else { + throw new IncompatibleServerException(); + } } } @@ -662,8 +647,8 @@ public class XmppConnection implements Runnable { @Override public void onIqPacketReceived(final Account account, final IqPacket packet) { - final Element instructions = packet.query().findChild("instructions"); - if (packet.query().hasChild("username") + if (packet.getType() == IqPacket.TYPE.RESULT + && packet.query().hasChild("username") && (packet.query().hasChild("password"))) { final IqPacket register = new IqPacket(IqPacket.TYPE.SET); final Element username = new Element("username").setContent(account.getUsername()); @@ -690,6 +675,7 @@ public class XmppConnection implements Runnable { } }); } else { + final Element instructions = packet.query().findChild("instructions"); changeStatus(Account.State.REGISTRATION_FAILED); disconnect(true); Log.d(Config.LOGTAG, account.getJid().toBareJid() @@ -707,6 +693,7 @@ public class XmppConnection implements Runnable { } catch (final InterruptedException ignored) { } } + needsBinding = false; clearIqCallbacks(); final IqPacket iq = new IqPacket(IqPacket.TYPE.SET); iq.addChild("bind", "urn:ietf:params:xml:ns:xmpp-bind") @@ -729,9 +716,11 @@ public class XmppConnection implements Runnable { sendPostBindInitialization(); } } else { + Log.d(Config.LOGTAG,account.getJid()+": disconnecting because of bind failure"); disconnect(true); } } else { + Log.d(Config.LOGTAG,account.getJid()+": disconnecting because of bind failure"); disconnect(true); } } @@ -739,14 +728,24 @@ public class XmppConnection implements Runnable { } private void clearIqCallbacks() { - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": clearing iq iq callbacks"); - final IqPacket failurePacket = new IqPacket(IqPacket.TYPE.ERROR); - Iterator<Entry<String, Pair<IqPacket, OnIqPacketReceived>>> iterator = this.packetCallbacks.entrySet().iterator(); - while(iterator.hasNext()) { - Entry<String, Pair<IqPacket, OnIqPacketReceived>> entry = iterator.next(); - entry.getValue().second.onIqPacketReceived(account,failurePacket); - iterator.remove(); + final IqPacket failurePacket = new IqPacket(IqPacket.TYPE.TIMEOUT); + final ArrayList<OnIqPacketReceived> callbacks = new ArrayList<>(); + synchronized (this.packetCallbacks) { + if (this.packetCallbacks.size() == 0) { + return; + } + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": clearing "+this.packetCallbacks.size()+" iq callbacks"); + final Iterator<Pair<IqPacket, OnIqPacketReceived>> iterator = this.packetCallbacks.values().iterator(); + while (iterator.hasNext()) { + Pair<IqPacket, OnIqPacketReceived> entry = iterator.next(); + callbacks.add(entry.second); + iterator.remove(); + } + } + for(OnIqPacketReceived callback : callbacks) { + callback.onIqPacketReceived(account,failurePacket); } + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": done clearing iq callbacks. "+this.packetCallbacks.size()+" left"); } private void sendStartSession() { @@ -758,6 +757,7 @@ public class XmppConnection implements Runnable { if (packet.getType() == IqPacket.TYPE.RESULT) { sendPostBindInitialization(); } else { + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not init sessions"); disconnect(true); } } @@ -775,7 +775,7 @@ public class XmppConnection implements Runnable { final EnablePacket enable = new EnablePacket(smVersion); tagWriter.writeStanzaAsync(enable); stanzasSent = 0; - mStanzaReceipts.clear(); + mStanzaQueue.clear(); } features.carbonsEnabled = false; features.blockListRequested = false; @@ -783,10 +783,11 @@ public class XmppConnection implements Runnable { sendServiceDiscoveryInfo(account.getServer()); sendServiceDiscoveryInfo(account.getJid().toBareJid()); sendServiceDiscoveryItems(account.getServer()); + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": online with resource " + account.getResource()); + changeStatus(Account.State.ONLINE); if (bindListener != null) { bindListener.onBind(account); } - sendInitialPing(); } private void sendServiceDiscoveryInfo(final Jid jid) { @@ -802,26 +803,29 @@ public class XmppConnection implements Runnable { @Override public void onIqPacketReceived(final Account account, final IqPacket packet) { - final List<Element> elements = packet.query().getChildren(); - final Info info = new Info(); - for (final Element element : elements) { - if (element.getName().equals("identity")) { - String type = element.getAttribute("type"); - String category = element.getAttribute("category"); - if (type != null && category != null) { - info.identities.add(new Pair<>(category,type)); + if (packet.getType() == IqPacket.TYPE.RESULT) { + final List<Element> elements = packet.query().getChildren(); + final Info info = new Info(); + for (final Element element : elements) { + if (element.getName().equals("identity")) { + String type = element.getAttribute("type"); + String category = element.getAttribute("category"); + if (type != null && category != null) { + info.identities.add(new Pair<>(category, type)); + } + } else if (element.getName().equals("feature")) { + info.features.add(element.getAttribute("var")); } - } else if (element.getName().equals("feature")) { - info.features.add(element.getAttribute("var")); } - } - disco.put(jid, info); - - if (account.getServer().equals(jid)) { - enableAdvancedStreamFeatures(); - for (final OnAdvancedStreamFeaturesLoaded listener : advancedStreamFeaturesLoadedListeners) { - listener.onAdvancedStreamFeaturesAvailable(account); + disco.put(jid, info); + if (account.getServer().equals(jid)) { + 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()); } } }); @@ -846,14 +850,18 @@ public class XmppConnection implements Runnable { @Override public void onIqPacketReceived(final Account account, final IqPacket packet) { - final List<Element> elements = packet.query().getChildren(); - for (final Element element : elements) { - if (element.getName().equals("item")) { - final Jid jid = element.getAttributeAsJid("jid"); - if (jid != null && !jid.equals(account.getServer())) { - sendServiceDiscoveryInfo(jid); + if (packet.getType() == IqPacket.TYPE.RESULT) { + final List<Element> elements = packet.query().getChildren(); + for (final Element element : elements) { + if (element.getName().equals("item")) { + final Jid jid = element.getAttributeAsJid("jid"); + if (jid != null && !jid.equals(account.getServer())) { + sendServiceDiscoveryInfo(jid); + } } } + } else { + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could not query disco items of "+server); } } }); @@ -887,6 +895,8 @@ public class XmppConnection implements Runnable { Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": switching resource due to conflict (" + account.getResource() + ")"); + } else if (streamError != null) { + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": stream error "+streamError.toString()); } } @@ -917,7 +927,9 @@ public class XmppConnection implements Runnable { packet.setAttribute("id", id); } if (callback != null) { - packetCallbacks.put(packet.getId(), new Pair<>(packet, callback)); + synchronized (this.packetCallbacks) { + packetCallbacks.put(packet.getId(), new Pair<>(packet, callback)); + } } this.sendPacket(packet); } @@ -937,16 +949,17 @@ public class XmppConnection implements Runnable { return; } final String name = packet.getName(); - if (name.equals("iq") || name.equals("message") || name.equals("presence")) { - ++stanzasSent; - } tagWriter.writeStanzaAsync(packet); - if ((packet instanceof MessagePacket || packet instanceof IqPacket) && packet.getId() != null && this.streamId != null) { - if (Config.EXTENDED_SM_LOGGING) { - Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": requesting ack for stanza #" + stanzasSent); + if (packet instanceof AbstractAcknowledgeableStanza) { + AbstractAcknowledgeableStanza stanza = (AbstractAcknowledgeableStanza) packet; + ++stanzasSent; + this.mStanzaQueue.put(stanzasSent, stanza); + if (stanza instanceof MessagePacket && stanza.getId() != null && this.streamId != null) { + if (Config.EXTENDED_SM_LOGGING) { + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": requesting ack for message stanza #" + stanzasSent); + } + tagWriter.writeStanzaAsync(new RequestPacket(this.smVersion)); } - this.mStanzaReceipts.put(stanzasSent, packet.getId()); - tagWriter.writeStanzaAsync(new RequestPacket(this.smVersion)); } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java index 7b140842a..4f733b10e 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java @@ -85,7 +85,7 @@ public class JingleConnection implements Transferable { @Override public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType() == IqPacket.TYPE.ERROR) { + if (packet.getType() != IqPacket.TYPE.RESULT) { fail(); } } @@ -449,7 +449,7 @@ public class JingleConnection implements Transferable { @Override public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType() != IqPacket.TYPE.ERROR) { + if (packet.getType() == IqPacket.TYPE.RESULT) { Log.d(Config.LOGTAG,account.getJid().toBareJid()+": other party received offer"); mJingleStatus = JINGLE_STATUS_INITIATED; mXmppConnectionService.markMessage(message, Message.STATUS_OFFERED); @@ -634,12 +634,11 @@ public class JingleConnection implements Transferable { @Override public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType() == IqPacket.TYPE.ERROR) { + if (packet.getType() != IqPacket.TYPE.RESULT) { onProxyActivated.failed(); } else { onProxyActivated.success(); - sendProxyActivated(connection - .getCandidate().getCid()); + sendProxyActivated(connection.getCandidate().getCid()); } } }); diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java index ab7ab73b8..85280c5ce 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java @@ -74,7 +74,7 @@ public class JingleInbandTransport extends JingleTransport { @Override public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType() == IqPacket.TYPE.ERROR) { + if (packet.getType() != IqPacket.TYPE.RESULT) { callback.failed(); } else { callback.established(); diff --git a/src/main/java/eu/siacs/conversations/xmpp/stanzas/AbstractAcknowledgeableStanza.java b/src/main/java/eu/siacs/conversations/xmpp/stanzas/AbstractAcknowledgeableStanza.java new file mode 100644 index 000000000..a5de4a847 --- /dev/null +++ b/src/main/java/eu/siacs/conversations/xmpp/stanzas/AbstractAcknowledgeableStanza.java @@ -0,0 +1,17 @@ +package eu.siacs.conversations.xmpp.stanzas; + +abstract public class AbstractAcknowledgeableStanza extends AbstractStanza { + + protected AbstractAcknowledgeableStanza(String name) { + super(name); + } + + + public String getId() { + return this.getAttribute("id"); + } + + public void setId(final String id) { + setAttribute("id", id); + } +} diff --git a/src/main/java/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java b/src/main/java/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java index bd706b57b..a6144df28 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java +++ b/src/main/java/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java @@ -18,10 +18,6 @@ public class AbstractStanza extends Element { return getAttributeAsJid("from"); } - public String getId() { - return this.getAttribute("id"); - } - public void setTo(final Jid to) { if (to != null) { setAttribute("to", to.toString()); @@ -34,10 +30,6 @@ public class AbstractStanza extends Element { } } - public void setId(final String id) { - setAttribute("id", id); - } - public boolean fromServer(final Account account) { return getFrom() == null || getFrom().equals(account.getServer()) diff --git a/src/main/java/eu/siacs/conversations/xmpp/stanzas/IqPacket.java b/src/main/java/eu/siacs/conversations/xmpp/stanzas/IqPacket.java index 398102e12..4d16f2e5d 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/stanzas/IqPacket.java +++ b/src/main/java/eu/siacs/conversations/xmpp/stanzas/IqPacket.java @@ -2,14 +2,15 @@ package eu.siacs.conversations.xmpp.stanzas; import eu.siacs.conversations.xml.Element; -public class IqPacket extends AbstractStanza { +public class IqPacket extends AbstractAcknowledgeableStanza { - public static enum TYPE { + public enum TYPE { ERROR, SET, RESULT, GET, - INVALID + INVALID, + TIMEOUT } public IqPacket(final TYPE type) { diff --git a/src/main/java/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java b/src/main/java/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java index 6b6909125..941b4b5f6 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java +++ b/src/main/java/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java @@ -5,7 +5,7 @@ import android.util.Pair; import eu.siacs.conversations.parser.AbstractParser; import eu.siacs.conversations.xml.Element; -public class MessagePacket extends AbstractStanza { +public class MessagePacket extends AbstractAcknowledgeableStanza { public static final int TYPE_CHAT = 0; public static final int TYPE_NORMAL = 2; public static final int TYPE_GROUPCHAT = 3; diff --git a/src/main/java/eu/siacs/conversations/xmpp/stanzas/PresencePacket.java b/src/main/java/eu/siacs/conversations/xmpp/stanzas/PresencePacket.java index 7ea320995..c321498d8 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/stanzas/PresencePacket.java +++ b/src/main/java/eu/siacs/conversations/xmpp/stanzas/PresencePacket.java @@ -1,6 +1,6 @@ package eu.siacs.conversations.xmpp.stanzas; -public class PresencePacket extends AbstractStanza { +public class PresencePacket extends AbstractAcknowledgeableStanza { public PresencePacket() { super("presence"); |