aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/de/thedevstack
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/Config.java87
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/ConversationsPlusPreferences.java8
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlService.java2
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlServiceImpl.java17
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlServiceStub.java5
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/entities/Conversation.java10
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/entities/MucOptions.java3
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/http/HttpUploadConnection.java1
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/services/AvatarService.java144
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/services/ContactChooserTargetService.java2
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/services/MessageArchiveService.java16
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/services/NotificationService.java5
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/services/XmppConnectionService.java25
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/ui/ConferenceDetailsActivity.java17
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/ui/ConversationActivity.java10
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/ui/EditAccountActivity.java6
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/ui/ShareWithActivity.java29
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/AvatarUtil.java40
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/Xmlns.java2
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/XmppConnection.java143
20 files changed, 384 insertions, 188 deletions
diff --git a/src/main/java/de/thedevstack/conversationsplus/Config.java b/src/main/java/de/thedevstack/conversationsplus/Config.java
index 2f35ad6e..a6825246 100644
--- a/src/main/java/de/thedevstack/conversationsplus/Config.java
+++ b/src/main/java/de/thedevstack/conversationsplus/Config.java
@@ -34,77 +34,62 @@ public final class Config {
return (ENCRYPTION_MASK & (ENCRYPTION_MASK - 1)) != 0;
}
- public static final String LOGTAG = "conversations";
+ public static final String LOGTAG = BuildConfig.LOGTAG;
+ public static final String DOMAIN_LOCK = BuildConfig.LOCKED_IN_DOMAIN; //only allow account creation for this domain
+ public static final String CONFERENCE_DOMAIN_LOCK = BuildConfig.LOCKED_IN_DOMAIN_CONFERENCES; //only allow conference creation for this domain
+ public static final boolean LOCK_DOMAINS_IN_CONVERSATIONS = BuildConfig.CONTACTS_CONFERENCES_LOCKED_TO_DOMAIN; //only add contacts and conferences for own domains
- public static final String DOMAIN_LOCK = null; //only allow account creation for this domain
- public static final String CONFERENCE_DOMAIN_LOCK = null; //only allow conference creation for this domain
- public static final boolean LOCK_DOMAINS_IN_CONVERSATIONS = false; //only add contacts and conferences for own domains
+ public static final boolean LOCK_SETTINGS = BuildConfig.ACCOUNT_SETTINGS_LOCKED; //set to true to disallow account and settings editing
+ public static final boolean DISALLOW_REGISTRATION_IN_UI = BuildConfig.DISALLOW_REGISTRATION_IN_UI; //hide the register checkbox
- public static final boolean LOCK_SETTINGS = false; //set to true to disallow account and settings editing
- public static final boolean DISALLOW_REGISTRATION_IN_UI = false; //hide the register checkbox
+ public static final boolean ALLOW_NON_TLS_CONNECTIONS = BuildConfig.ALLOW_NON_TLS_CONNECTIONS; //very dangerous. you should have a good reason to set this to true
+ public static final boolean HIDE_MESSAGE_TEXT_IN_NOTIFICATION = BuildConfig.HIDE_MESSAGE_TEXT_IN_NOTIFICATION;
+ public static final boolean SHOW_CONNECTED_ACCOUNTS = BuildConfig.SHOW_CONNECTED_ACCOUNTS_IN_FOREGROUND_NOTIFICATION; //show number of connected accounts in foreground notification
- public static final boolean ALLOW_NON_TLS_CONNECTIONS = false; //very dangerous. you should have a good reason to set this to true
- public static final boolean HIDE_MESSAGE_TEXT_IN_NOTIFICATION = false;
- public static final boolean SHOW_CONNECTED_ACCOUNTS = false; //show number of connected accounts in foreground notification
+ public static final int PING_MAX_INTERVAL = BuildConfig.PING_MAX_INTERVAL;
+ public static final int PING_MIN_INTERVAL = BuildConfig.PING_MIN_INTERVAL;
+ public static final int PING_TIMEOUT = BuildConfig.PING_TIMEOUT;
+ public static final int SOCKET_TIMEOUT = BuildConfig.SOCKET_TIMEOUT;
+ public static final int CONNECT_TIMEOUT = BuildConfig.CONNECT_TIMEOUT;
+ public static final int CONNECT_DISCO_TIMEOUT = BuildConfig.CONNECT_DISCO_TIMEOUT;
+ public static final int CARBON_GRACE_PERIOD = BuildConfig.CARBON_GRACE_PERIOD;
+ public static final int MINI_GRACE_PERIOD = BuildConfig.MINI_GRACE_PERIOD;
- public static final boolean ALWAYS_NOTIFY_BY_DEFAULT = false;
+ public static final boolean CLOSE_TCP_WHEN_SWITCHING_TO_BACKGROUND = BuildConfig.CLOSE_TCP_WHEN_SWITCHING_TO_BACKGROUND;
- public static final boolean LEGACY_NAMESPACE_HTTP_UPLOAD = false;
+ public static final int AVATAR_SIZE = BuildConfig.AVATAR_SIZE;
+ public static final Bitmap.CompressFormat AVATAR_FORMAT = BuildConfig.AVATAR_FORMAT;
- public static final int PING_MAX_INTERVAL = 300;
- public static final int PING_MIN_INTERVAL = 30;
- public static final int PING_TIMEOUT = 15;
- public static final int SOCKET_TIMEOUT = 15;
- public static final int CONNECT_TIMEOUT = 90;
- public static final int CONNECT_DISCO_TIMEOUT = 20;
- public static final int CARBON_GRACE_PERIOD = 90;
- public static final int MINI_GRACE_PERIOD = 750;
+ public static final int PAGE_SIZE = BuildConfig.PAGE_SIZE;
+ public static final int MAX_NUM_PAGES = BuildConfig.MAX_NUM_PAGES;
- public static final boolean CLOSE_TCP_WHEN_SWITCHING_TO_BACKGROUND = false;
+ public static final int REFRESH_UI_INTERVAL = BuildConfig.REFRESH_UI_INTERVAL;
- public static final int AVATAR_SIZE = 192;
- public static final Bitmap.CompressFormat AVATAR_FORMAT = Bitmap.CompressFormat.PNG;
+ public static final boolean DISABLE_PROXY_LOOKUP = BuildConfig.DISABLE_PROXY_LOOKUP; //useful to debug ibb
+ public static final boolean DISABLE_HTTP_UPLOAD = BuildConfig.DISABLE_HTTP_UPLOAD;
+ public static final boolean DISABLE_STRING_PREP = BuildConfig.DISABLE_STRING_PREP; // setting to true might increase startup performance
+ public static final boolean EXTENDED_SM_LOGGING = BuildConfig.EXTENDED_SM_LOGGING; // log stanza counts
+ public static final boolean RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE = BuildConfig.RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE; //setting to true might increase power consumption
- public static final int IMAGE_SIZE = 1920;
- public static final Bitmap.CompressFormat IMAGE_FORMAT = Bitmap.CompressFormat.JPEG;
- public static final int IMAGE_QUALITY = 75;
- public static final int IMAGE_MAX_SIZE = 524288; //512KiB
+ public static final boolean ENCRYPT_ON_HTTP_UPLOADED = BuildConfig.ENCRYPT_ON_HTTP_UPLOADED;
- public static final int MESSAGE_MERGE_WINDOW = 20;
+ public static final boolean REPORT_WRONG_FILESIZE_IN_OTR_JINGLE = BuildConfig.REPORT_WRONG_FILESIZE_IN_OTR_JINGLE;
- public static final boolean UTF8_EMOTICONS = false;
+ public static final boolean SHOW_REGENERATE_AXOLOTL_KEYS_BUTTON = BuildConfig.SHOW_REGENERATE_AXOLOTL_KEYS_BUTTON;
- public static final int PAGE_SIZE = 50;
- public static final int MAX_NUM_PAGES = 3;
+ public static final boolean X509_VERIFICATION = BuildConfig.X509_VERIFICATION_OF_OMEMO_KEYS; //use x509 certificates to verify OMEMO keys
- public static final int PROGRESS_UI_UPDATE_INTERVAL = 750;
- public static final int REFRESH_UI_INTERVAL = 500;
+ public static final boolean IGNORE_ID_REWRITE_IN_MUC = BuildConfig.IGNORE_ID_REWRITE_IN_MUC;
- public static final boolean DISABLE_PROXY_LOOKUP = false; //useful to debug ibb
- public static final boolean DISABLE_HTTP_UPLOAD = false;
- public static final boolean DISABLE_STRING_PREP = false; // setting to true might increase startup performance
- public static final boolean EXTENDED_SM_LOGGING = false; // log stanza counts
- public static final boolean RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE = true; //setting to true might increase power consumption
-
- public static final boolean ENCRYPT_ON_HTTP_UPLOADED = false;
-
- public static final boolean REPORT_WRONG_FILESIZE_IN_OTR_JINGLE = true;
-
- public static final boolean SHOW_REGENERATE_AXOLOTL_KEYS_BUTTON = false;
-
- public static final boolean X509_VERIFICATION = false; //use x509 certificates to verify OMEMO keys
-
- public static final boolean IGNORE_ID_REWRITE_IN_MUC = true;
-
- public static final boolean REQUEST_DISCO = true;
+ public static final boolean REQUEST_DISCO = BuildConfig.REQUEST_DISCO;
public static final long MILLISECONDS_IN_DAY = 24 * 60 * 60 * 1000;
public static final long MAM_MAX_CATCHUP = MILLISECONDS_IN_DAY / 2;
- public static final int MAM_MAX_MESSAGES = 500;
+ public static final int MAM_MAX_MESSAGES = BuildConfig.MAM_MAX_MESSAGES;
public static final ChatState DEFAULT_CHATSTATE = ChatState.ACTIVE;
- public static final int TYPING_TIMEOUT = 8;
+ public static final int TYPING_TIMEOUT = BuildConfig.TYPING_TIMEOUT;
public static final String ENABLED_CIPHERS[] = {
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
diff --git a/src/main/java/de/thedevstack/conversationsplus/ConversationsPlusPreferences.java b/src/main/java/de/thedevstack/conversationsplus/ConversationsPlusPreferences.java
index d22a10d6..70eec7a2 100644
--- a/src/main/java/de/thedevstack/conversationsplus/ConversationsPlusPreferences.java
+++ b/src/main/java/de/thedevstack/conversationsplus/ConversationsPlusPreferences.java
@@ -237,6 +237,14 @@ public class ConversationsPlusPreferences extends Settings {
return getBoolean("allow_message_correction", true);
}
+ public static boolean returnToPrevious() {
+ return getBoolean("return_to_previous", false);
+ }
+
+ public static boolean led() {
+ return getBoolean("led", true);
+ }
+
private ConversationsPlusPreferences(SharedPreferences sharedPreferences) {
this.sharedPreferences = sharedPreferences;
}
diff --git a/src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlService.java b/src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlService.java
index 49cd6033..52418553 100644
--- a/src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlService.java
+++ b/src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlService.java
@@ -115,4 +115,6 @@ public interface AxolotlService extends OnAdvancedStreamFeaturesLoaded {
boolean hasPendingKeyFetches(Account account, List<Jid> jids);
void prepareKeyTransportMessage(Conversation conversation, OnMessageCreatedCallback onMessageCreatedCallback);
+
+ void resetBrokenness();
}
diff --git a/src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlServiceImpl.java b/src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlServiceImpl.java
index 2463a795..f8856f90 100644
--- a/src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlServiceImpl.java
+++ b/src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlServiceImpl.java
@@ -43,12 +43,13 @@ import de.thedevstack.conversationsplus.services.XmppConnectionService;
import de.thedevstack.conversationsplus.utils.CryptoHelper;
import de.thedevstack.conversationsplus.utils.SerialSingleThreadExecutor;
import de.thedevstack.conversationsplus.xml.Element;
+import de.thedevstack.conversationsplus.xmpp.OnAdvancedStreamFeaturesLoaded;
import de.thedevstack.conversationsplus.xmpp.OnIqPacketReceived;
import de.thedevstack.conversationsplus.xmpp.jid.InvalidJidException;
import de.thedevstack.conversationsplus.xmpp.jid.Jid;
import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket;
-public class AxolotlServiceImpl implements AxolotlService {
+public class AxolotlServiceImpl implements OnAdvancedStreamFeaturesLoaded, AxolotlService {
public static final int publishTriesThreshold = 3;
@@ -223,6 +224,7 @@ public class AxolotlServiceImpl implements AxolotlService {
return axolotlStore.getIdentityKeyPair().getPublicKey().getFingerprint().replaceAll("\\s", "");
}
+ @Override
public Set<IdentityKey> getKeysWithTrust(XmppAxolotlSession.Trust trust) {
return axolotlStore.getContactKeysWithTrust(account.getJid().toBareJid().toString(), trust);
}
@@ -302,6 +304,11 @@ public class AxolotlServiceImpl implements AxolotlService {
return this.pepBroken;
}
+ public void resetBrokenness() {
+ this.pepBroken = false;
+ numPublishTriesOnEmptyPep = 0;
+ }
+
public void regenerateKeys(boolean wipeOther) {
axolotlStore.regenerate();
sessions.clear();
@@ -436,7 +443,8 @@ public class AxolotlServiceImpl implements AxolotlService {
mXmppConnectionService.sendIqPacket(account, publish, new OnIqPacketReceived() {
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
- if (packet.getType() != IqPacket.TYPE.RESULT) {
+ if (packet.getType() == IqPacket.TYPE.ERROR) {
+ pepBroken = true;
Log.d(Config.LOGTAG, getLogprefix(account) + "Error received while publishing own device id" + packet.findChild("error"));
}
}
@@ -600,7 +608,8 @@ public class AxolotlServiceImpl implements AxolotlService {
Log.d(Config.LOGTAG, getLogprefix(account) + "Announcing device " + getOwnDeviceId());
publishOwnDeviceIdIfNeeded();
}
- } else {
+ } else if (packet.getType() == IqPacket.TYPE.ERROR) {
+ pepBroken = true;
Log.d(Config.LOGTAG, getLogprefix(account) + "Error received while publishing bundle: " + packet.findChild("error"));
}
}
@@ -1037,4 +1046,4 @@ public class AxolotlServiceImpl implements AxolotlService {
}
}
}
-} \ No newline at end of file
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlServiceStub.java b/src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlServiceStub.java
index 858cdf08..7db99b75 100644
--- a/src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlServiceStub.java
+++ b/src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlServiceStub.java
@@ -204,4 +204,9 @@ public class AxolotlServiceStub implements AxolotlService {
public void onAdvancedStreamFeaturesAvailable(Account account) {
}
+
+ @Override
+ public void resetBrokenness() {
+
+ }
}
diff --git a/src/main/java/de/thedevstack/conversationsplus/entities/Conversation.java b/src/main/java/de/thedevstack/conversationsplus/entities/Conversation.java
index 50b1e4a7..9e9366d7 100644
--- a/src/main/java/de/thedevstack/conversationsplus/entities/Conversation.java
+++ b/src/main/java/de/thedevstack/conversationsplus/entities/Conversation.java
@@ -317,14 +317,6 @@ public class Conversation extends AbstractEntity implements Blockable {
setAttribute(ATTRIBUTE_CRYPTO_TARGETS, acceptedTargets);
}
- public void setCorrectingMessage(Message correctingMessage) {
- this.correctingMessage = correctingMessage;
- }
-
- public Message getCorrectingMessage() {
- return this.correctingMessage;
- }
-
public interface OnMessageFound {
void onMessageFound(final Message message);
}
@@ -790,7 +782,7 @@ public class Conversation extends AbstractEntity implements Blockable {
}
public boolean alwaysNotify() {
- return mode == MODE_SINGLE || getBooleanAttribute(ATTRIBUTE_ALWAYS_NOTIFY, Config.ALWAYS_NOTIFY_BY_DEFAULT || isPnNA());
+ return mode == MODE_SINGLE || getBooleanAttribute(ATTRIBUTE_ALWAYS_NOTIFY, true);
}
public boolean setAttribute(String key, String value) {
diff --git a/src/main/java/de/thedevstack/conversationsplus/entities/MucOptions.java b/src/main/java/de/thedevstack/conversationsplus/entities/MucOptions.java
index 4a330f30..1af538ec 100644
--- a/src/main/java/de/thedevstack/conversationsplus/entities/MucOptions.java
+++ b/src/main/java/de/thedevstack/conversationsplus/entities/MucOptions.java
@@ -469,6 +469,9 @@ public class MucOptions {
}
public Jid getTrueCounterpart(String name) {
+ if (name.equals(getSelf().getName())) {
+ return account.getJid().toBareJid();
+ }
User user = findUser(name);
return user == null ? null : user.getJid();
}
diff --git a/src/main/java/de/thedevstack/conversationsplus/http/HttpUploadConnection.java b/src/main/java/de/thedevstack/conversationsplus/http/HttpUploadConnection.java
index 6834fdd2..6c546fee 100644
--- a/src/main/java/de/thedevstack/conversationsplus/http/HttpUploadConnection.java
+++ b/src/main/java/de/thedevstack/conversationsplus/http/HttpUploadConnection.java
@@ -97,7 +97,6 @@ public class HttpUploadConnection implements Transferable {
public void init(Message message, boolean delay) {
this.message = message;
- this.message.setHttpUploaded(true);
this.account = message.getConversation().getAccount();
this.file = FileBackend.getFile(message, false);
this.mime = this.file.getMimeType();
diff --git a/src/main/java/de/thedevstack/conversationsplus/services/AvatarService.java b/src/main/java/de/thedevstack/conversationsplus/services/AvatarService.java
index 6311d739..ed9c259f 100644
--- a/src/main/java/de/thedevstack/conversationsplus/services/AvatarService.java
+++ b/src/main/java/de/thedevstack/conversationsplus/services/AvatarService.java
@@ -31,14 +31,17 @@ import de.thedevstack.conversationsplus.entities.Message;
import de.thedevstack.conversationsplus.entities.MucOptions;
import de.thedevstack.conversationsplus.generator.IqGenerator;
import de.thedevstack.conversationsplus.persistance.DatabaseBackend;
+import de.thedevstack.conversationsplus.persistance.FileBackend;
import de.thedevstack.conversationsplus.ui.UiCallback;
import de.thedevstack.conversationsplus.utils.UIHelper;
import de.thedevstack.conversationsplus.xml.Element;
+import de.thedevstack.conversationsplus.xmpp.OnAdvancedStreamFeaturesLoaded;
import de.thedevstack.conversationsplus.xmpp.OnIqPacketReceived;
+import de.thedevstack.conversationsplus.xmpp.XmppConnection;
import de.thedevstack.conversationsplus.xmpp.pep.Avatar;
import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket;
-public class AvatarService {
+public class AvatarService implements OnAdvancedStreamFeaturesLoaded {
private static final int FG_COLOR = 0xFFFAFAFA;
private static final int TRANSPARENT = 0x00000000;
@@ -408,6 +411,17 @@ public class AvatarService {
return true;
}
+ @Override
+ public void onAdvancedStreamFeaturesAvailable(Account account) {
+ XmppConnection.Features features = account.getXmppConnection().getFeatures();
+ if (features.pep() && !features.pepPersistent()) {
+ Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": has pep but is not persistent");
+ if (account.getAvatar() != null) {
+ republishAvatarIfNeeded(account);
+ }
+ }
+ }
+
public void publishAvatar(final Account account,
final Uri image,
final UiCallback<Avatar> callback) {
@@ -428,47 +442,79 @@ public class AvatarService {
callback.error(R.string.error_saving_avatar, avatar);
return;
}
- final IqPacket packet = AvatarPacketGenerator.generatePublishAvatarPacket(avatar);
- XmppSendUtil.sendIqPacket(account, packet, new OnIqPacketReceived() {
-
- @Override
- public void onIqPacketReceived(Account account, IqPacket result) {
- if (result.getType() == IqPacket.TYPE.RESULT) {
- final IqPacket packet = AvatarPacketGenerator.generatePublishAvatarMetadataPacket(avatar);
- XmppSendUtil.sendIqPacket(account, packet, new OnIqPacketReceived() {
-
- @Override
- public void onIqPacketReceived(Account account,
- IqPacket result) {
- if (result.getType() == IqPacket.TYPE.RESULT) {
- if (account.setAvatar(avatar.getFilename())) {
- AvatarService.getInstance().clear(account);
- DatabaseBackend.getInstance(ConversationsPlusApplication.getAppContext()).updateAccount(account);
- }
- callback.success(avatar);
- } else {
- callback.error(
- R.string.error_publish_avatar_server_reject,
- avatar);
- }
- }
- });
- } else {
+ sendAndReceiveIqPackages(avatar, account, callback);
+ } else {
+ callback.error(R.string.error_publish_avatar_converting, null);
+ }
+ }
+
+ public void publishAvatar(Account account, final Avatar avatar, final UiCallback<Avatar> callback) {
+ final IqPacket packet = AvatarPacketGenerator.generatePublishAvatarPacket(avatar);
+ XmppSendUtil.sendIqPacket(account, packet, new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket result) {
+ if (result.getType() == IqPacket.TYPE.RESULT) {
+ sendAndReceiveIqPackages(avatar, account, callback);
+ } else {
+ if (callback != null) {
callback.error(
R.string.error_publish_avatar_server_reject,
avatar);
}
}
- });
- } else {
- callback.error(R.string.error_publish_avatar_converting, null);
- }
+ }
+ });
}
private static String generateFetchKey(Account account, final Avatar avatar) {
return account.getJid().toBareJid()+"_"+avatar.owner+"_"+avatar.sha1sum;
}
+ public void republishAvatarIfNeeded(Account account) {
+ if (account.getAxolotlService().isPepBroken()) {
+ Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": skipping republication of avatar because pep is broken");
+ return;
+ }
+ IqPacket packet = AvatarPacketGenerator.generateRetrieveAvatarMetadataPacket(null);
+ XmppSendUtil.sendIqPacket(account, packet, new OnIqPacketReceived() {
+
+ private Avatar parseAvatar(IqPacket packet) {
+ Element pubsub = packet.findChild("pubsub", "http://jabber.org/protocol/pubsub");
+ if (pubsub != null) {
+ Element items = pubsub.findChild("items");
+ if (items != null) {
+ return Avatar.parseMetadata(items);
+ }
+ }
+ return null;
+ }
+
+ private boolean errorIsItemNotFound(IqPacket packet) {
+ Element error = packet.findChild("error");
+ return packet.getType() == IqPacket.TYPE.ERROR
+ && error != null
+ && error.hasChild("item-not-found");
+ }
+
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.RESULT || errorIsItemNotFound(packet)) {
+ Avatar serverAvatar = parseAvatar(packet);
+ if (serverAvatar == null && account.getAvatar() != null) {
+ Avatar avatar = AvatarUtil.getStoredPepAvatar(account.getAvatar());
+ if (avatar != null) {
+ Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": avatar on server was null. republishing");
+ publishAvatar(account, AvatarUtil.getStoredPepAvatar(account.getAvatar()), null);
+ } else {
+ Logging.e(Config.LOGTAG, account.getJid().toBareJid()+": error rereading avatar");
+ }
+ }
+ }
+ }
+ });
+ }
+
public void fetchAvatar(Account account, final Avatar avatar, final UiCallback<Avatar> callback) {
final String KEY = generateFetchKey(account, avatar);
synchronized(this.mInProgressAvatarFetches) {
@@ -624,4 +670,40 @@ public class AvatarService {
}
}
}
+
+ private void sendAndReceiveIqPackages(final Avatar avatar, final Account account, final UiCallback callback) {
+
+ final IqPacket packet = AvatarPacketGenerator.generatePublishAvatarPacket(avatar);
+ XmppSendUtil.sendIqPacket(account, packet, new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket result) {
+ if (result.getType() == IqPacket.TYPE.RESULT) {
+ final IqPacket packet = AvatarPacketGenerator.generatePublishAvatarMetadataPacket(avatar);
+ XmppSendUtil.sendIqPacket(account, packet, new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(Account account,
+ IqPacket result) {
+ if (result.getType() == IqPacket.TYPE.RESULT) {
+ if (account.setAvatar(avatar.getFilename())) {
+ AvatarService.getInstance().clear(account);
+ DatabaseBackend.getInstance(ConversationsPlusApplication.getAppContext()).updateAccount(account);
+ }
+ callback.success(avatar);
+ } else {
+ callback.error(
+ R.string.error_publish_avatar_server_reject,
+ avatar);
+ }
+ }
+ });
+ } else {
+ callback.error(
+ R.string.error_publish_avatar_server_reject,
+ avatar);
+ }
+ }
+ });
+ }
}
diff --git a/src/main/java/de/thedevstack/conversationsplus/services/ContactChooserTargetService.java b/src/main/java/de/thedevstack/conversationsplus/services/ContactChooserTargetService.java
index 8d48d686..6256609c 100644
--- a/src/main/java/de/thedevstack/conversationsplus/services/ContactChooserTargetService.java
+++ b/src/main/java/de/thedevstack/conversationsplus/services/ContactChooserTargetService.java
@@ -48,7 +48,7 @@ public class ContactChooserTargetService extends ChooserTargetService implements
final Conversation conversation = conversations.get(i);
final String name = conversation.getName();
final Icon icon = Icon.createWithBitmap(AvatarService.getInstance().get(conversation, pixel));
- final float score = (1.0f / MAX_TARGETS) * i;
+ final float score = 1 - (1.0f / MAX_TARGETS) * i;
final Bundle extras = new Bundle();
extras.putString("uuid", conversation.getUuid());
chooserTargets.add(new ChooserTarget(name, icon, score, componentName, extras));
diff --git a/src/main/java/de/thedevstack/conversationsplus/services/MessageArchiveService.java b/src/main/java/de/thedevstack/conversationsplus/services/MessageArchiveService.java
index f2298d8c..cd55b42f 100644
--- a/src/main/java/de/thedevstack/conversationsplus/services/MessageArchiveService.java
+++ b/src/main/java/de/thedevstack/conversationsplus/services/MessageArchiveService.java
@@ -45,8 +45,10 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
}
}
}
- long startCatchup = getLastMessageTransmitted(account);
+ Pair<Long,String> pair = mXmppConnectionService.databaseBackend.getLastMessageReceived(account);
+ long startCatchup = pair == null ? 0 : pair.first;
long endCatchup = account.getXmppConnection().getLastSessionEstablished();
+ final Query query;
if (startCatchup == 0) {
return;
} else if (endCatchup - startCatchup >= Config.MAM_MAX_CATCHUP) {
@@ -57,8 +59,11 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
this.query(conversation,startCatchup);
}
}
+ query = new Query(account, startCatchup, endCatchup);
+ } else {
+ query = new Query(account, startCatchup, endCatchup);
+ query.reference = pair.second;
}
- final Query query = new Query(account, startCatchup, endCatchup);
this.queries.add(query);
this.execute(query);
}
@@ -75,11 +80,6 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
}
}
- private long getLastMessageTransmitted(final Account account) {
- Pair<Long,String> pair = mXmppConnectionService.databaseBackend.getLastMessageReceived(account);
- return pair == null ? 0 : pair.first;
- }
-
public Query query(final Conversation conversation) {
if (conversation.getLastMessageTransmitted() < 0 && conversation.countMessages() == 0) {
return query(conversation,
@@ -292,7 +292,7 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
this.end = end;
this.queryId = new BigInteger(50, mXmppConnectionService.getRNG()).toString(32);
}
-
+
private Query page(String reference) {
Query query = new Query(this.account,this.start,this.end);
query.reference = reference;
diff --git a/src/main/java/de/thedevstack/conversationsplus/services/NotificationService.java b/src/main/java/de/thedevstack/conversationsplus/services/NotificationService.java
index 488a6075..a3142df4 100644
--- a/src/main/java/de/thedevstack/conversationsplus/services/NotificationService.java
+++ b/src/main/java/de/thedevstack/conversationsplus/services/NotificationService.java
@@ -176,6 +176,7 @@ public class NotificationService {
final String ringtone = ConversationsPlusPreferences.notificationRingtone();
final boolean vibrate = ConversationsPlusPreferences.vibrateOnNotification();
+ final boolean led = ConversationsPlusPreferences.led();
if (notifications.size() == 0) {
notificationManager.cancel(NOTIFICATION_ID);
@@ -206,7 +207,9 @@ public class NotificationService {
mBuilder.setDefaults(0);
mBuilder.setSmallIcon(R.drawable.ic_notification);
mBuilder.setDeleteIntent(createDeleteIntent());
- mBuilder.setLights(Settings.LED_COLOR, 2000, 4000);
+ if (led) {
+ mBuilder.setLights(Settings.LED_COLOR, 2000, 4000);
+ }
final Notification notification = mBuilder.build();
notificationManager.notify(NOTIFICATION_ID, notification);
}
diff --git a/src/main/java/de/thedevstack/conversationsplus/services/XmppConnectionService.java b/src/main/java/de/thedevstack/conversationsplus/services/XmppConnectionService.java
index f9540bdc..b7b4db2c 100644
--- a/src/main/java/de/thedevstack/conversationsplus/services/XmppConnectionService.java
+++ b/src/main/java/de/thedevstack/conversationsplus/services/XmppConnectionService.java
@@ -455,7 +455,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
return START_STICKY;
case Intent.ACTION_SHUTDOWN:
- logoutAndSave();
+ logoutAndSave(true);
return START_NOT_STICKY;
case ACTION_CLEAR_NOTIFICATION:
mNotificationService.clear();
@@ -738,12 +738,16 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
public void onTaskRemoved(final Intent rootIntent) {
super.onTaskRemoved(rootIntent);
if (!ConversationsPlusPreferences.keepForegroundService()) {
- this.logoutAndSave();
+ this.logoutAndSave(false);
}
}
- private void logoutAndSave() {
+ private void logoutAndSave(boolean stop) {
+ int activeAccounts = 0;
for (final Account account : accounts) {
+ if (account.getStatus() != Account.State.DISABLED) {
+ activeAccounts++;
+ }
databaseBackend.writeRoster(account.getRoster());
if (account.getXmppConnection() != null) {
new Thread(new Runnable() {
@@ -752,11 +756,12 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
disconnect(account, false);
}
}).start();
- cancelWakeUpCall(account.getUuid().hashCode());
}
}
- Logging.d(Config.LOGTAG, "good bye");
- stopSelf();
+ if (stop || activeAccounts == 0) {
+ Logging.d(Config.LOGTAG, "good bye");
+ stopSelf();
+ }
}
private void cancelWakeUpCall(int requestCode) {
@@ -789,6 +794,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
connection.setOnBindListener(this.mOnBindListener);
connection.setOnMessageAcknowledgeListener(this.mOnMessageAcknowledgedListener);
connection.addOnAdvancedStreamFeaturesAvailableListener(this.mMessageArchiveService);
+ connection.addOnAdvancedStreamFeaturesAvailableListener(AvatarService.getInstance());
AxolotlService axolotlService = account.getAxolotlService();
if (axolotlService != null) {
connection.addOnAdvancedStreamFeaturesAvailableListener(axolotlService);
@@ -2326,12 +2332,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
if (!account.isOptionSet(Account.OPTION_DISABLED)) {
if (!force) {
disconnect(account, false);
- try {
- Logging.d(Config.LOGTAG, "wait for disconnect");
- Thread.sleep(500); //sleep wait for disconnect
- } catch (InterruptedException e) {
- //ignored
- }
}
Thread thread = new Thread(connection);
connection.setInteractive(interactive);
@@ -2342,6 +2342,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
disconnect(account, force);
account.getRoster().clearPresences();
connection.resetEverything();
+ account.getAxolotlService().resetBrokenness();
}
}
}
diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/ConferenceDetailsActivity.java b/src/main/java/de/thedevstack/conversationsplus/ui/ConferenceDetailsActivity.java
index aa040fa2..2a3cd7fe 100644
--- a/src/main/java/de/thedevstack/conversationsplus/ui/ConferenceDetailsActivity.java
+++ b/src/main/java/de/thedevstack/conversationsplus/ui/ConferenceDetailsActivity.java
@@ -3,8 +3,10 @@ package de.thedevstack.conversationsplus.ui;
import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.app.PendingIntent;
+import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.Intent;
import android.content.IntentSender.SendIntentException;
import android.os.Build;
import android.os.Bundle;
@@ -283,6 +285,9 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
quickEdit(mConversation.getName(),this.onSubjectEdited);
}
break;
+ case R.id.action_share:
+ share();
+ break;
case R.id.action_save_as_bookmark:
saveAsBookmark();
break;
@@ -310,6 +315,18 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
}
}
+ private void share() {
+ Intent shareIntent = new Intent();
+ shareIntent.setAction(Intent.ACTION_SEND);
+ shareIntent.putExtra(Intent.EXTRA_TEXT, getShareableUri());
+ shareIntent.setType("text/plain");
+ try {
+ startActivity(Intent.createChooser(shareIntent, getText(R.string.share_uri_with)));
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(this, R.string.no_application_to_share_uri, Toast.LENGTH_SHORT).show();
+ }
+ }
+
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem menuItemSaveBookmark = menu.findItem(R.id.action_save_as_bookmark);
diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/ConversationActivity.java b/src/main/java/de/thedevstack/conversationsplus/ui/ConversationActivity.java
index f068036b..301ba563 100644
--- a/src/main/java/de/thedevstack/conversationsplus/ui/ConversationActivity.java
+++ b/src/main/java/de/thedevstack/conversationsplus/ui/ConversationActivity.java
@@ -1311,10 +1311,6 @@ public class ConversationActivity extends XmppActivity
} else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_FILE || requestCode == ATTACHMENT_CHOICE_RECORD_VOICE) {
final List<Uri> uris = extractUriFromIntent(data);
final Conversation c = getSelectedConversation();
- final long max = c.getAccount()
- .getXmppConnection()
- .getFeatures()
- .getMaxHttpUploadSize();
final OnPresenceSelected callback = new OnPresenceSelected() {
@Override
public void onPresenceSelected() {
@@ -1328,7 +1324,7 @@ public class ConversationActivity extends XmppActivity
}
};
if (c.getMode() == Conversation.MODE_MULTI
- || FileUtils.allFilesUnderSize(this, uris, max)
+ || FileUtils.allFilesUnderSize(this, uris, getMaxHttpUploadSize(c))
|| c.getNextEncryption() == Message.ENCRYPTION_OTR) {
callback.onPresenceSelected();
} else {
@@ -1378,6 +1374,10 @@ public class ConversationActivity extends XmppActivity
}
}
+ private long getMaxHttpUploadSize(Conversation conversation) {
+ return conversation.getAccount().getXmppConnection().getFeatures().getMaxHttpUploadSize();
+ }
+
private void openBatteryOptimizationDialogIfNeeded() {
if (hasAccountWithoutPush()
&& isOptimizingBattery()
diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/EditAccountActivity.java b/src/main/java/de/thedevstack/conversationsplus/ui/EditAccountActivity.java
index 08c63c3c..1a868949 100644
--- a/src/main/java/de/thedevstack/conversationsplus/ui/EditAccountActivity.java
+++ b/src/main/java/de/thedevstack/conversationsplus/ui/EditAccountActivity.java
@@ -247,12 +247,11 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
mFetchingAvatar = true;
AvatarService.getInstance().checkForAvatar(mAccount, mAvatarFetchCallback);
}
- } else {
- updateSaveButton();
}
if (mAccount != null) {
updateAccountInformation(false);
}
+ updateSaveButton();
}
@Override
@@ -531,6 +530,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
protected void onBackendConnected() {
if (this.jidToEdit != null) {
this.mAccount = xmppConnectionService.findAccountByJid(jidToEdit);
+ this.mInitMode |= this.mAccount.isOptionSet(Account.OPTION_REGISTER);
if (this.mAccount != null) {
if (this.mAccount.getPrivateKeyAlias() != null) {
this.mPassword.setHint(R.string.authenticate_with_certificate);
@@ -625,6 +625,8 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
if (!mInitMode) {
this.mAvatar.setVisibility(View.VISIBLE);
this.mAvatar.setImageBitmap(AvatarService.getInstance().get(this.mAccount, getPixel(72)));
+ } else {
+ this.mAvatar.setVisibility(View.GONE);
}
if (this.mAccount.isOptionSet(Account.OPTION_REGISTER)) {
this.mRegisterNew.setVisibility(View.VISIBLE);
diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/ShareWithActivity.java b/src/main/java/de/thedevstack/conversationsplus/ui/ShareWithActivity.java
index 8a61e00c..fe3ed73f 100644
--- a/src/main/java/de/thedevstack/conversationsplus/ui/ShareWithActivity.java
+++ b/src/main/java/de/thedevstack/conversationsplus/ui/ShareWithActivity.java
@@ -37,6 +37,8 @@ import de.thedevstack.conversationsplus.xmpp.jid.Jid;
public class ShareWithActivity extends XmppActivity implements XmppConnectionService.OnConversationUpdate {
+ private boolean mReturnToPrevious = false;
+
@Override
public void onConversationUpdate() {
refreshUi();
@@ -85,7 +87,7 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer
resId = R.string.shared_file_with_x;
}
replaceToast(getString(resId, message.getConversation().getName()));
- if (share.uuid != null) {
+ if (mReturnToPrevious) {
finish();
} else {
switchToConversation(message.getConversation());
@@ -186,6 +188,7 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer
if (intent == null) {
return;
}
+ this.mReturnToPrevious = ConversationsPlusPreferences.returnToPrevious();
final String type = intent.getType();
final String action = intent.getAction();
Log.d(Config.LOGTAG, "action: "+action+ ", type:"+type);
@@ -314,8 +317,28 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer
selectPresence(conversation, callback);
}
} else {
- switchToConversation(conversation, this.share.text, true);
- finish();
+ if (mReturnToPrevious && this.share.text != null && !this.share.text.isEmpty() ) {
+ final OnPresenceSelected callback = new OnPresenceSelected() {
+ @Override
+ public void onPresenceSelected() {
+ Message message = new Message(conversation,share.text, conversation.getNextEncryption());
+ if (conversation.getNextEncryption() == Message.ENCRYPTION_OTR) {
+ message.setCounterpart(conversation.getNextCounterpart());
+ }
+ xmppConnectionService.sendMessage(message);
+ replaceToast(getString(R.string.shared_text_with_x, conversation.getName()));
+ finish();
+ }
+ };
+ if (conversation.getNextEncryption() == Message.ENCRYPTION_OTR) {
+ selectPresence(conversation, callback);
+ } else {
+ callback.onPresenceSelected();
+ }
+ } else {
+ switchToConversation(conversation, this.share.text, true);
+ finish();
+ }
}
}
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/AvatarUtil.java b/src/main/java/de/thedevstack/conversationsplus/utils/AvatarUtil.java
index 206ee668..9f6071bb 100644
--- a/src/main/java/de/thedevstack/conversationsplus/utils/AvatarUtil.java
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/AvatarUtil.java
@@ -1,12 +1,14 @@
package de.thedevstack.conversationsplus.utils;
import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.net.Uri;
import android.util.Base64;
import android.util.Base64OutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -59,6 +61,44 @@ public final class AvatarUtil {
} catch (IOException e) {
return null;
}
+
+ }
+
+ public static Avatar getStoredPepAvatar(String hash) {
+ if (hash == null) {
+ return null;
+ }
+ Avatar avatar = new Avatar();
+ File file = new File(getAvatarPath(hash));
+ FileInputStream is = null;
+ try {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeFile(file.getAbsolutePath(), options);
+ is = new FileInputStream(file);
+ ByteArrayOutputStream mByteArrayOutputStream = new ByteArrayOutputStream();
+ Base64OutputStream mBase64OutputStream = new Base64OutputStream(mByteArrayOutputStream, Base64.DEFAULT);
+ MessageDigest digest = MessageDigest.getInstance("SHA-1");
+ DigestOutputStream os = new DigestOutputStream(mBase64OutputStream, digest);
+ byte[] buffer = new byte[4096];
+ int length;
+ while ((length = is.read(buffer)) > 0) {
+ os.write(buffer, 0, length);
+ }
+ os.flush();
+ os.close();
+ avatar.sha1sum = CryptoHelper.bytesToHex(digest.digest());
+ avatar.image = new String(mByteArrayOutputStream.toByteArray());
+ avatar.height = options.outHeight;
+ avatar.width = options.outWidth;
+ return avatar;
+ } catch (IOException e) {
+ return null;
+ } catch (NoSuchAlgorithmException e) {
+ return null;
+ } finally {
+ StreamUtil.close(is);
+ }
}
/**
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/Xmlns.java b/src/main/java/de/thedevstack/conversationsplus/utils/Xmlns.java
index f937e35f..35adf6ec 100644
--- a/src/main/java/de/thedevstack/conversationsplus/utils/Xmlns.java
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/Xmlns.java
@@ -7,5 +7,5 @@ public final class Xmlns {
public static final String ROSTER = "jabber:iq:roster";
public static final String REGISTER = "jabber:iq:register";
public static final String BYTE_STREAMS = "http://jabber.org/protocol/bytestreams";
- public static final String HTTP_UPLOAD = Config.LEGACY_NAMESPACE_HTTP_UPLOAD ? "eu:siacs:conversations:http:upload" : "urn:xmpp:http:upload";
+ public static final String HTTP_UPLOAD = "urn:xmpp:http:upload";
}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/XmppConnection.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/XmppConnection.java
index 8f0ca78b..a89f308f 100644
--- a/src/main/java/de/thedevstack/conversationsplus/xmpp/XmppConnection.java
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/XmppConnection.java
@@ -37,6 +37,8 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.TreeSet;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManager;
@@ -114,7 +116,9 @@ public class XmppConnection implements Runnable {
private long lastConnect = 0;
private long lastSessionStarted = 0;
private long lastDiscoStarted = 0;
- private int mPendingServiceDiscoveries = 0;
+ private AtomicInteger mPendingServiceDiscoveries = new AtomicInteger(0);
+ private AtomicBoolean mIsServiceItemsDiscoveryPending = new AtomicBoolean(true);
+ private boolean mWaitForDisco = true;
private final ArrayList<String> mPendingServiceDiscoveriesIds = new ArrayList<>();
private boolean mInteractive = false;
private int attempt = 0;
@@ -526,7 +530,6 @@ public class XmppConnection implements Runnable {
}
nextTag = tagReader.readTag();
}
- throw new IOException("reached end of stream. last tag was "+nextTag);
}
private void acknowledgeStanzaUpTo(int serverCount) {
@@ -712,42 +715,7 @@ public class XmppConnection implements Runnable {
} else if (this.streamFeatures.hasChild("mechanisms")
&& shouldAuthenticate
&& (features.encryptionEnabled || Config.ALLOW_NON_TLS_CONNECTIONS)) {
- final List<String> mechanisms = extractMechanisms(streamFeatures
- .findChild("mechanisms"));
- final Element auth = new Element("auth");
- auth.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl");
- if (mechanisms.contains("EXTERNAL") && account.getPrivateKeyAlias() != null) {
- saslMechanism = new External(tagWriter, account, mXmppConnectionService.getRNG());
- } else if (mechanisms.contains("SCRAM-SHA-1")) {
- saslMechanism = new ScramSha1(tagWriter, account, mXmppConnectionService.getRNG());
- } else if (mechanisms.contains("PLAIN")) {
- saslMechanism = new Plain(tagWriter, account);
- } else if (mechanisms.contains("DIGEST-MD5")) {
- saslMechanism = new DigestMd5(tagWriter, account, mXmppConnectionService.getRNG());
- }
- if (saslMechanism != null) {
- final JSONObject keys = account.getKeys();
- try {
- if (keys.has(Account.PINNED_MECHANISM_KEY) &&
- keys.getInt(Account.PINNED_MECHANISM_KEY) > saslMechanism.getPriority()) {
- Logging.e(Config.LOGTAG, "Auth failed. Authentication mechanism " + saslMechanism.getMechanism() +
- " has lower priority (" + String.valueOf(saslMechanism.getPriority()) +
- ") than pinned priority (" + keys.getInt(Account.PINNED_MECHANISM_KEY) +
- "). Possible downgrade attack?");
- throw new SecurityException();
- }
- } catch (final JSONException e) {
- Logging.d(Config.LOGTAG, "Parse error while checking pinned auth mechanism");
- }
- Logging.d(Config.LOGTAG, account.getJid().toString() + ": Authenticating with " + saslMechanism.getMechanism());
- auth.setAttribute("mechanism", saslMechanism.getMechanism());
- if (!saslMechanism.getClientFirstMessage().isEmpty()) {
- auth.setContent(saslMechanism.getClientFirstMessage());
- }
- tagWriter.writeElement(auth);
- } else {
- throw new IncompatibleServerException();
- }
+ authenticate();
} else if (this.streamFeatures.hasChild("sm", "urn:xmpp:sm:" + smVersion) && streamId != null) {
if (Config.EXTENDED_SM_LOGGING) {
Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": resuming after stanza #"+stanzasReceived);
@@ -763,6 +731,45 @@ public class XmppConnection implements Runnable {
}
}
+ private void authenticate() throws IOException {
+ final List<String> mechanisms = extractMechanisms(streamFeatures
+ .findChild("mechanisms"));
+ final Element auth = new Element("auth");
+ auth.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl");
+ if (mechanisms.contains("EXTERNAL") && account.getPrivateKeyAlias() != null) {
+ saslMechanism = new External(tagWriter, account, mXmppConnectionService.getRNG());
+ } else if (mechanisms.contains("SCRAM-SHA-1")) {
+ saslMechanism = new ScramSha1(tagWriter, account, mXmppConnectionService.getRNG());
+ } else if (mechanisms.contains("PLAIN")) {
+ saslMechanism = new Plain(tagWriter, account);
+ } else if (mechanisms.contains("DIGEST-MD5")) {
+ saslMechanism = new DigestMd5(tagWriter, account, mXmppConnectionService.getRNG());
+ }
+ if (saslMechanism != null) {
+ final JSONObject keys = account.getKeys();
+ try {
+ if (keys.has(Account.PINNED_MECHANISM_KEY) &&
+ keys.getInt(Account.PINNED_MECHANISM_KEY) > saslMechanism.getPriority()) {
+ Logging.e(Config.LOGTAG, "Auth failed. Authentication mechanism " + saslMechanism.getMechanism() +
+ " has lower priority (" + String.valueOf(saslMechanism.getPriority()) +
+ ") than pinned priority (" + keys.getInt(Account.PINNED_MECHANISM_KEY) +
+ "). Possible downgrade attack?");
+ throw new SecurityException();
+ }
+ } catch (final JSONException e) {
+ Logging.d(Config.LOGTAG, "Parse error while checking pinned auth mechanism");
+ }
+ Logging.d(Config.LOGTAG, account.getJid().toString() + ": Authenticating with " + saslMechanism.getMechanism());
+ auth.setAttribute("mechanism", saslMechanism.getMechanism());
+ if (!saslMechanism.getClientFirstMessage().isEmpty()) {
+ auth.setContent(saslMechanism.getClientFirstMessage());
+ }
+ tagWriter.writeElement(auth);
+ } else {
+ throw new IncompatibleServerException();
+ }
+ }
+
private List<String> extractMechanisms(final Element stream) {
final ArrayList<String> mechanisms = new ArrayList<>(stream
.getChildren().size());
@@ -980,11 +987,12 @@ public class XmppConnection implements Runnable {
synchronized (this.disco) {
this.disco.clear();
}
- mPendingServiceDiscoveries = mServerIdentity == Identity.NIMBUZZ ? 1 : 0;
+ mPendingServiceDiscoveries.set(0);
+ mIsServiceItemsDiscoveryPending.set(true);
+ mWaitForDisco = mServerIdentity != Identity.NIMBUZZ;
lastDiscoStarted = SystemClock.elapsedRealtime();
Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": starting service discovery");
mXmppConnectionService.scheduleWakeUpCall(Config.CONNECT_DISCO_TIMEOUT, account.getUuid().hashCode());
- sendServiceDiscoveryItems(account.getServer());
Element caps = streamFeatures.findChild("c");
final String hash = caps == null ? null : caps.getAttribute("hash");
final String ver = caps == null ? null : caps.getAttribute("ver");
@@ -999,13 +1007,15 @@ public class XmppConnection implements Runnable {
disco.put(account.getServer(), discoveryResult);
}
sendServiceDiscoveryInfo(account.getJid().toBareJid());
+ sendServiceDiscoveryItems(account.getServer());
+ if (!mWaitForDisco) {
+ finalizeBind();
+ }
this.lastSessionStarted = SystemClock.elapsedRealtime();
}
private void sendServiceDiscoveryInfo(final Jid jid) {
- if (mServerIdentity != Identity.NIMBUZZ) {
- mPendingServiceDiscoveries++;
- }
+ mPendingServiceDiscoveries.incrementAndGet();
final IqPacket iq = new IqPacket(IqPacket.TYPE.GET);
iq.setTo(jid);
iq.query("http://jabber.org/protocol/disco#info");
@@ -1049,14 +1059,10 @@ public class XmppConnection implements Runnable {
Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not query disco info for " + jid.toString());
}
if (packet.getType() != IqPacket.TYPE.TIMEOUT) {
- mPendingServiceDiscoveries--;
- if (mPendingServiceDiscoveries == 0) {
- Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": done with service discovery");
- Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": online with resource " + account.getResource());
- if (bindListener != null) {
- bindListener.onBind(account);
- }
- changeStatus(Account.State.ONLINE);
+ if (mPendingServiceDiscoveries.decrementAndGet() == 0
+ && !mIsServiceItemsDiscoveryPending.get()
+ && mWaitForDisco) {
+ finalizeBind();
}
}
}
@@ -1066,6 +1072,14 @@ public class XmppConnection implements Runnable {
}
}
+ private void finalizeBind() {
+ Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": online with resource " + account.getResource());
+ if (bindListener != null) {
+ bindListener.onBind(account);
+ }
+ changeStatus(Account.State.ONLINE);
+ }
+
private void enableAdvancedStreamFeatures() {
if (getFeatures().carbons() && !features.carbonsEnabled) {
sendEnableCarbons();
@@ -1083,7 +1097,7 @@ public class XmppConnection implements Runnable {
final IqPacket iq = new IqPacket(IqPacket.TYPE.GET);
iq.setTo(server.toDomainJid());
iq.query("http://jabber.org/protocol/disco#items");
- this.sendIqPacket(iq, new OnIqPacketReceived() {
+ String id = this.sendIqPacket(iq, new OnIqPacketReceived() {
@Override
public void onIqPacketReceived(final Account account, final IqPacket packet) {
@@ -1100,8 +1114,17 @@ public class XmppConnection implements Runnable {
} else {
Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not query disco items of " + server);
}
+ if (packet.getType() != IqPacket.TYPE.TIMEOUT) {
+ mIsServiceItemsDiscoveryPending.set(false);
+ if (mPendingServiceDiscoveries.get() == 0 && mWaitForDisco) {
+ finalizeBind();
+ }
+ }
}
});
+ synchronized (this.mPendingServiceDiscoveriesIds) {
+ this.mPendingServiceDiscoveriesIds.add(id);
+ }
}
private void sendEnableCarbons() {
@@ -1482,13 +1505,15 @@ public class XmppConnection implements Runnable {
public boolean pep() {
synchronized (XmppConnection.this.disco) {
- ServiceDiscoveryResult info = disco.get(account.getServer());
- if (info != null && info.hasIdentity("pubsub", "pep")) {
- return true;
- } else {
- info = disco.get(account.getJid().toBareJid());
- return info != null && info.hasIdentity("pubsub", "pep");
- }
+ ServiceDiscoveryResult info = disco.get(account.getJid().toBareJid());
+ return info != null && info.hasIdentity("pubsub", "pep");
+ }
+ }
+
+ public boolean pepPersistent() {
+ synchronized (XmppConnection.this.disco) {
+ ServiceDiscoveryResult info = disco.get(account.getJid().toBareJid());
+ return info != null && info.getFeatures().contains("http://jabber.org/protocol/pubsub#persistent-items");
}
}