From 5012ff3545a443c4f9317bb9ff38275c33715330 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Fri, 9 Nov 2018 17:47:36 +0100 Subject: [PATCH] added interface to edit nick --- .../conversations/entities/MucOptions.java | 8 +- .../generator/AbstractGenerator.java | 2 +- .../conversations/generator/IqGenerator.java | 11 +- .../conversations/parser/MessageParser.java | 1524 +++++------ .../services/XmppConnectionService.java | 33 +- .../conversations/ui/EditAccountActivity.java | 2321 +++++++++-------- .../eu/siacs/conversations/xml/Namespace.java | 1 + src/main/res/layout/activity_edit_account.xml | 46 +- src/main/res/values/strings.xml | 8 + src/main/res/values/styles.xml | 4 + src/quicksy/AndroidManifest.xml | 10 + .../conversations/ui/EnterNameActivity.java | 54 + .../siacs/conversations/ui/TosActivity.java | 80 + .../conversations/ui/VerifyActivity.java | 4 +- .../ui/util/ApiDialogHelper.java | 3 + .../conversations/utils/SignupUtils.java | 10 +- .../res/layout/activity_enter_name.xml | 60 + src/quicksy/res/layout/activity_tos.xml | 77 + 18 files changed, 2332 insertions(+), 1924 deletions(-) create mode 100644 src/quicksy/java/eu/siacs/conversations/ui/EnterNameActivity.java create mode 100644 src/quicksy/java/eu/siacs/conversations/ui/TosActivity.java create mode 100644 src/quicksy/res/layout/activity_enter_name.xml create mode 100644 src/quicksy/res/layout/activity_tos.xml diff --git a/src/main/java/eu/siacs/conversations/entities/MucOptions.java b/src/main/java/eu/siacs/conversations/entities/MucOptions.java index 133916856..952f755c5 100644 --- a/src/main/java/eu/siacs/conversations/entities/MucOptions.java +++ b/src/main/java/eu/siacs/conversations/entities/MucOptions.java @@ -2,6 +2,7 @@ package eu.siacs.conversations.entities; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.text.TextUtils; import java.util.ArrayList; import java.util.Collections; @@ -380,7 +381,12 @@ public class MucOptions { } else if (!conversation.getJid().isBareJid()) { return conversation.getJid().getResource(); } else { - return JidHelper.localPartOrFallback(account.getJid()); + final String displayName = account.getDisplayName(); + if (TextUtils.isEmpty(displayName)) { + return JidHelper.localPartOrFallback(account.getJid()); + } else { + return displayName; + } } } diff --git a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java index 74e5af850..d320168bb 100644 --- a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java @@ -35,7 +35,7 @@ public abstract class AbstractGenerator { "http://jabber.org/protocol/caps", "http://jabber.org/protocol/disco#info", "urn:xmpp:avatar:metadata+notify", - "http://jabber.org/protocol/nick+notify", + Namespace.NICK+"+notify", Namespace.BOOKMARKS+"+notify", "urn:xmpp:ping", "jabber:iq:version", diff --git a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java index 5fa292068..91b56ac91 100644 --- a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java @@ -127,8 +127,15 @@ public class IqGenerator extends AbstractGenerator { public IqPacket publishNick(String nick) { final Element item = new Element("item"); - item.addChild("nick", "http://jabber.org/protocol/nick").setContent(nick); - return publish("http://jabber.org/protocol/nick", item); + item.addChild("nick", Namespace.NICK).setContent(nick); + return publish(Namespace.NICK, item); + } + + public IqPacket deleteNode(String node) { + IqPacket packet = new IqPacket(IqPacket.TYPE.SET); + final Element pubsub = packet.addChild("pubsub", Namespace.PUBSUB_OWNER); + pubsub.addChild("delete").setAttribute("node",node); + return packet; } public IqPacket publishAvatar(Avatar avatar) { diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index 9b2636703..d6f79a163 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -43,795 +43,819 @@ import rocks.xmpp.addr.Jid; public class MessageParser extends AbstractParser implements OnMessagePacketReceived { - private static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("HH:mm:ss", Locale.ENGLISH); + private static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("HH:mm:ss", Locale.ENGLISH); - public MessageParser(XmppConnectionService service) { - super(service); - } + public MessageParser(XmppConnectionService service) { + super(service); + } - private static String extractStanzaId(Element packet, boolean isTypeGroupChat, Conversation conversation) { - final Jid by; - final boolean safeToExtract; - if (isTypeGroupChat) { - by = conversation.getJid().asBareJid(); - safeToExtract = conversation.getMucOptions().hasFeature(Namespace.STANZA_IDS); - } else { - Account account = conversation.getAccount(); - by = account.getJid().asBareJid(); - safeToExtract = account.getXmppConnection().getFeatures().stanzaIds(); - } - return safeToExtract ? extractStanzaId(packet, by) : null; - } + private static String extractStanzaId(Element packet, boolean isTypeGroupChat, Conversation conversation) { + final Jid by; + final boolean safeToExtract; + if (isTypeGroupChat) { + by = conversation.getJid().asBareJid(); + safeToExtract = conversation.getMucOptions().hasFeature(Namespace.STANZA_IDS); + } else { + Account account = conversation.getAccount(); + by = account.getJid().asBareJid(); + safeToExtract = account.getXmppConnection().getFeatures().stanzaIds(); + } + return safeToExtract ? extractStanzaId(packet, by) : null; + } - private static String extractStanzaId(Element packet, Jid by) { - for (Element child : packet.getChildren()) { - if (child.getName().equals("stanza-id") - && Namespace.STANZA_IDS.equals(child.getNamespace()) - && by.equals(InvalidJid.getNullForInvalid(child.getAttributeAsJid("by")))) { - return child.getAttribute("id"); - } - } - return null; - } + private static String extractStanzaId(Element packet, Jid by) { + for (Element child : packet.getChildren()) { + if (child.getName().equals("stanza-id") + && Namespace.STANZA_IDS.equals(child.getNamespace()) + && by.equals(InvalidJid.getNullForInvalid(child.getAttributeAsJid("by")))) { + return child.getAttribute("id"); + } + } + return null; + } - private static Jid getTrueCounterpart(Element mucUserElement, Jid fallback) { - final Element item = mucUserElement == null ? null : mucUserElement.findChild("item"); - Jid result = item == null ? null : InvalidJid.getNullForInvalid(item.getAttributeAsJid("jid")); - return result != null ? result : fallback; - } + private static Jid getTrueCounterpart(Element mucUserElement, Jid fallback) { + final Element item = mucUserElement == null ? null : mucUserElement.findChild("item"); + Jid result = item == null ? null : InvalidJid.getNullForInvalid(item.getAttributeAsJid("jid")); + return result != null ? result : fallback; + } - private boolean extractChatState(Conversation c, final boolean isTypeGroupChat, final MessagePacket packet) { - ChatState state = ChatState.parse(packet); - if (state != null && c != null) { - final Account account = c.getAccount(); - Jid from = packet.getFrom(); - if (from.asBareJid().equals(account.getJid().asBareJid())) { - c.setOutgoingChatState(state); - if (state == ChatState.ACTIVE || state == ChatState.COMPOSING) { - mXmppConnectionService.markRead(c); - activateGracePeriod(account); - } - return false; - } else { - if (isTypeGroupChat) { - MucOptions.User user = c.getMucOptions().findUserByFullJid(from); - if (user != null) { - return user.setChatState(state); - } else { - return false; - } - } else { - return c.setIncomingChatState(state); - } - } - } - return false; - } + private boolean extractChatState(Conversation c, final boolean isTypeGroupChat, final MessagePacket packet) { + ChatState state = ChatState.parse(packet); + if (state != null && c != null) { + final Account account = c.getAccount(); + Jid from = packet.getFrom(); + if (from.asBareJid().equals(account.getJid().asBareJid())) { + c.setOutgoingChatState(state); + if (state == ChatState.ACTIVE || state == ChatState.COMPOSING) { + mXmppConnectionService.markRead(c); + activateGracePeriod(account); + } + return false; + } else { + if (isTypeGroupChat) { + MucOptions.User user = c.getMucOptions().findUserByFullJid(from); + if (user != null) { + return user.setChatState(state); + } else { + return false; + } + } else { + return c.setIncomingChatState(state); + } + } + } + return false; + } - private Message parseAxolotlChat(Element axolotlMessage, Jid from, Conversation conversation, int status, boolean postpone) { - final AxolotlService service = conversation.getAccount().getAxolotlService(); - final XmppAxolotlMessage xmppAxolotlMessage; - try { - xmppAxolotlMessage = XmppAxolotlMessage.fromElement(axolotlMessage, from.asBareJid()); - } catch (Exception e) { - Log.d(Config.LOGTAG, conversation.getAccount().getJid().asBareJid() + ": invalid omemo message received " + e.getMessage()); - return null; - } - if (xmppAxolotlMessage.hasPayload()) { - final XmppAxolotlMessage.XmppAxolotlPlaintextMessage plaintextMessage; - try { - plaintextMessage = service.processReceivingPayloadMessage(xmppAxolotlMessage, postpone); - } catch (NotEncryptedForThisDeviceException e) { - return new Message(conversation, "", Message.ENCRYPTION_AXOLOTL_NOT_FOR_THIS_DEVICE, status); - } - if (plaintextMessage != null) { - Message finishedMessage = new Message(conversation, plaintextMessage.getPlaintext(), Message.ENCRYPTION_AXOLOTL, status); - finishedMessage.setFingerprint(plaintextMessage.getFingerprint()); - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(finishedMessage.getConversation().getAccount()) + " Received Message with session fingerprint: " + plaintextMessage.getFingerprint()); - return finishedMessage; - } - } else { - Log.d(Config.LOGTAG, conversation.getAccount().getJid().asBareJid() + ": received OMEMO key transport message"); - service.processReceivingKeyTransportMessage(xmppAxolotlMessage, postpone); - } - return null; - } + private Message parseAxolotlChat(Element axolotlMessage, Jid from, Conversation conversation, int status, boolean postpone) { + final AxolotlService service = conversation.getAccount().getAxolotlService(); + final XmppAxolotlMessage xmppAxolotlMessage; + try { + xmppAxolotlMessage = XmppAxolotlMessage.fromElement(axolotlMessage, from.asBareJid()); + } catch (Exception e) { + Log.d(Config.LOGTAG, conversation.getAccount().getJid().asBareJid() + ": invalid omemo message received " + e.getMessage()); + return null; + } + if (xmppAxolotlMessage.hasPayload()) { + final XmppAxolotlMessage.XmppAxolotlPlaintextMessage plaintextMessage; + try { + plaintextMessage = service.processReceivingPayloadMessage(xmppAxolotlMessage, postpone); + } catch (NotEncryptedForThisDeviceException e) { + return new Message(conversation, "", Message.ENCRYPTION_AXOLOTL_NOT_FOR_THIS_DEVICE, status); + } + if (plaintextMessage != null) { + Message finishedMessage = new Message(conversation, plaintextMessage.getPlaintext(), Message.ENCRYPTION_AXOLOTL, status); + finishedMessage.setFingerprint(plaintextMessage.getFingerprint()); + Log.d(Config.LOGTAG, AxolotlService.getLogprefix(finishedMessage.getConversation().getAccount()) + " Received Message with session fingerprint: " + plaintextMessage.getFingerprint()); + return finishedMessage; + } + } else { + Log.d(Config.LOGTAG, conversation.getAccount().getJid().asBareJid() + ": received OMEMO key transport message"); + service.processReceivingKeyTransportMessage(xmppAxolotlMessage, postpone); + } + return null; + } - private Invite extractInvite(Account account, Element message) { - Element x = message.findChild("x", "http://jabber.org/protocol/muc#user"); - if (x != null) { - Element invite = x.findChild("invite"); - if (invite != null) { - String password = x.findChildContent("password"); - Jid from = InvalidJid.getNullForInvalid(invite.getAttributeAsJid("from")); - Contact contact = from == null ? null : account.getRoster().getContact(from); - Jid room = InvalidJid.getNullForInvalid(message.getAttributeAsJid("from")); - if (room == null) { - return null; - } - return new Invite(room, password, contact); - } - } else { - x = message.findChild("x", "jabber:x:conference"); - if (x != null) { - Jid from = InvalidJid.getNullForInvalid(message.getAttributeAsJid("from")); - Contact contact = from == null ? null : account.getRoster().getContact(from); - Jid room = InvalidJid.getNullForInvalid(x.getAttributeAsJid("jid")); - if (room == null) { - return null; - } - return new Invite(room, x.getAttribute("password"), contact); - } - } - return null; - } + private Invite extractInvite(Account account, Element message) { + Element x = message.findChild("x", "http://jabber.org/protocol/muc#user"); + if (x != null) { + Element invite = x.findChild("invite"); + if (invite != null) { + String password = x.findChildContent("password"); + Jid from = InvalidJid.getNullForInvalid(invite.getAttributeAsJid("from")); + Contact contact = from == null ? null : account.getRoster().getContact(from); + Jid room = InvalidJid.getNullForInvalid(message.getAttributeAsJid("from")); + if (room == null) { + return null; + } + return new Invite(room, password, contact); + } + } else { + x = message.findChild("x", "jabber:x:conference"); + if (x != null) { + Jid from = InvalidJid.getNullForInvalid(message.getAttributeAsJid("from")); + Contact contact = from == null ? null : account.getRoster().getContact(from); + Jid room = InvalidJid.getNullForInvalid(x.getAttributeAsJid("jid")); + if (room == null) { + return null; + } + return new Invite(room, x.getAttribute("password"), contact); + } + } + return null; + } - private void parseEvent(final Element event, final Jid from, final Account account) { - Element items = event.findChild("items"); - String node = items == null ? null : items.getAttribute("node"); - if ("urn:xmpp:avatar:metadata".equals(node)) { - Avatar avatar = Avatar.parseMetadata(items); - if (avatar != null) { - avatar.owner = from.asBareJid(); - if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) { - if (account.getJid().asBareJid().equals(from)) { - if (account.setAvatar(avatar.getFilename())) { - mXmppConnectionService.databaseBackend.updateAccount(account); - } - mXmppConnectionService.getAvatarService().clear(account); - mXmppConnectionService.updateConversationUi(); - mXmppConnectionService.updateAccountUi(); - } else { - Contact contact = account.getRoster().getContact(from); - if (contact.setAvatar(avatar)) { - mXmppConnectionService.syncRoster(account); - mXmppConnectionService.getAvatarService().clear(contact); - mXmppConnectionService.updateConversationUi(); - mXmppConnectionService.updateRosterUi(); - } - } - } else if (mXmppConnectionService.isDataSaverDisabled()) { - mXmppConnectionService.fetchAvatar(account, avatar); - } - } - } else if ("http://jabber.org/protocol/nick".equals(node)) { - final Element i = items.findChild("item"); - final String nick = i == null ? null : i.findChildContent("nick", Namespace.NICK); - if (nick != null) { - Contact contact = account.getRoster().getContact(from); - if (contact.setPresenceName(nick)) { - mXmppConnectionService.getAvatarService().clear(contact); - } - mXmppConnectionService.updateConversationUi(); - mXmppConnectionService.updateAccountUi(); - } - } else if (AxolotlService.PEP_DEVICE_LIST.equals(node)) { - Element item = items.findChild("item"); - Set deviceIds = mXmppConnectionService.getIqParser().deviceIds(item); - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Received PEP device list " + deviceIds + " update from " + from + ", processing... "); - AxolotlService axolotlService = account.getAxolotlService(); - axolotlService.registerDevices(from, deviceIds); - } else if (Namespace.BOOKMARKS.equals(node)) { - Log.d(Config.LOGTAG,"received bookmarks from "+from); - if (account.getJid().asBareJid().equals(from)) { - final Element i = items.findChild("item"); - final Element storage = i == null ? null : i.findChild("storage", Namespace.BOOKMARKS); - mXmppConnectionService.processBookmarks(account,storage); - } - } - } + private void parseEvent(final Element event, final Jid from, final Account account) { + Element items = event.findChild("items"); + String node = items == null ? null : items.getAttribute("node"); + if ("urn:xmpp:avatar:metadata".equals(node)) { + Avatar avatar = Avatar.parseMetadata(items); + if (avatar != null) { + avatar.owner = from.asBareJid(); + if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) { + if (account.getJid().asBareJid().equals(from)) { + if (account.setAvatar(avatar.getFilename())) { + mXmppConnectionService.databaseBackend.updateAccount(account); + } + mXmppConnectionService.getAvatarService().clear(account); + mXmppConnectionService.updateConversationUi(); + mXmppConnectionService.updateAccountUi(); + } else { + Contact contact = account.getRoster().getContact(from); + if (contact.setAvatar(avatar)) { + mXmppConnectionService.syncRoster(account); + mXmppConnectionService.getAvatarService().clear(contact); + mXmppConnectionService.updateConversationUi(); + mXmppConnectionService.updateRosterUi(); + } + } + } else if (mXmppConnectionService.isDataSaverDisabled()) { + mXmppConnectionService.fetchAvatar(account, avatar); + } + } + } else if (Namespace.NICK.equals(node)) { + final Element i = items.findChild("item"); + final String nick = i == null ? null : i.findChildContent("nick", Namespace.NICK); + if (nick != null) { + setNick(account, from, nick); + } + } else if (AxolotlService.PEP_DEVICE_LIST.equals(node)) { + Element item = items.findChild("item"); + Set deviceIds = mXmppConnectionService.getIqParser().deviceIds(item); + Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Received PEP device list " + deviceIds + " update from " + from + ", processing... "); + AxolotlService axolotlService = account.getAxolotlService(); + axolotlService.registerDevices(from, deviceIds); + } else if (Namespace.BOOKMARKS.equals(node)) { + Log.d(Config.LOGTAG, "received bookmarks from " + from); + if (account.getJid().asBareJid().equals(from)) { + final Element i = items.findChild("item"); + final Element storage = i == null ? null : i.findChild("storage", Namespace.BOOKMARKS); + mXmppConnectionService.processBookmarks(account, storage); + } + } + } - private boolean handleErrorMessage(Account account, MessagePacket packet) { - if (packet.getType() == MessagePacket.TYPE_ERROR) { - Jid from = packet.getFrom(); - if (from != null) { - mXmppConnectionService.markMessage(account, - from.asBareJid(), - packet.getId(), - Message.STATUS_SEND_FAILED, - extractErrorMessage(packet)); - } - return true; - } - return false; - } + private void parseDeleteEvent(final Element event, final Jid from, final Account account) { + final Element delete = event.findChild("delete"); + if (delete == null) { + return; + } + String node = delete.getAttribute("node"); + if (Namespace.NICK.equals(node)) { + Log.d(Config.LOGTAG, "parsing nick delete event from " + from); + setNick(account, from, null); + } + } - @Override - public void onMessagePacketReceived(Account account, MessagePacket original) { - if (handleErrorMessage(account, original)) { - return; - } - final MessagePacket packet; - Long timestamp = null; - boolean isCarbon = false; - String serverMsgId = null; - final Element fin = original.findChild("fin", MessageArchiveService.Version.MAM_0.namespace); - if (fin != null) { - mXmppConnectionService.getMessageArchiveService().processFinLegacy(fin, original.getFrom()); - return; - } - final Element result = MessageArchiveService.Version.findResult(original); - final MessageArchiveService.Query query = result == null ? null : mXmppConnectionService.getMessageArchiveService().findQuery(result.getAttribute("queryid")); - if (query != null && query.validFrom(original.getFrom())) { - Pair f = original.getForwardedMessagePacket("result", query.version.namespace); - if (f == null) { - return; - } - timestamp = f.second; - packet = f.first; - serverMsgId = result.getAttribute("id"); - query.incrementMessageCount(); - } else if (query != null) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received mam result from invalid sender"); - return; - } else if (original.fromServer(account)) { - Pair f; - f = original.getForwardedMessagePacket("received", "urn:xmpp:carbons:2"); - f = f == null ? original.getForwardedMessagePacket("sent", "urn:xmpp:carbons:2") : f; - packet = f != null ? f.first : original; - if (handleErrorMessage(account, packet)) { - return; - } - timestamp = f != null ? f.second : null; - isCarbon = f != null; - } else { - packet = original; - } + private void setNick(Account account, Jid user, String nick) { + if (user.asBareJid().equals(account.getJid().asBareJid())) { + account.setDisplayName(nick); + } else { + Contact contact = account.getRoster().getContact(user); + if (contact.setPresenceName(nick)) { + mXmppConnectionService.getAvatarService().clear(contact); + } + } + mXmppConnectionService.updateConversationUi(); + mXmppConnectionService.updateAccountUi(); + } - if (timestamp == null) { - timestamp = AbstractParser.parseTimestamp(original, AbstractParser.parseTimestamp(packet)); - } - final String body = packet.getBody(); - final Element mucUserElement = packet.findChild("x", "http://jabber.org/protocol/muc#user"); - final String pgpEncrypted = packet.findChildContent("x", "jabber:x:encrypted"); - final Element replaceElement = packet.findChild("replace", "urn:xmpp:message-correct:0"); - final Element oob = packet.findChild("x", Namespace.OOB); - final Element xP1S3 = packet.findChild("x", Namespace.P1_S3_FILE_TRANSFER); - final URL xP1S3url = xP1S3 == null ? null : P1S3UrlStreamHandler.of(xP1S3); - final String oobUrl = oob != null ? oob.findChildContent("url") : null; - final String replacementId = replaceElement == null ? null : replaceElement.getAttribute("id"); - final Element axolotlEncrypted = packet.findChild(XmppAxolotlMessage.CONTAINERTAG, AxolotlService.PEP_PREFIX); - int status; - final Jid counterpart; - final Jid to = packet.getTo(); - final Jid from = packet.getFrom(); - final Element originId = packet.findChild("origin-id", Namespace.STANZA_IDS); - final String remoteMsgId; - if (originId != null && originId.getAttribute("id") != null) { - remoteMsgId = originId.getAttribute("id"); - } else { - remoteMsgId = packet.getId(); - } - boolean notify = false; + private boolean handleErrorMessage(Account account, MessagePacket packet) { + if (packet.getType() == MessagePacket.TYPE_ERROR) { + Jid from = packet.getFrom(); + if (from != null) { + mXmppConnectionService.markMessage(account, + from.asBareJid(), + packet.getId(), + Message.STATUS_SEND_FAILED, + extractErrorMessage(packet)); + } + return true; + } + return false; + } - if (from == null || !InvalidJid.isValid(from) || !InvalidJid.isValid(to)) { - Log.e(Config.LOGTAG, "encountered invalid message from='" + from + "' to='" + to + "'"); - return; - } + @Override + public void onMessagePacketReceived(Account account, MessagePacket original) { + if (handleErrorMessage(account, original)) { + return; + } + final MessagePacket packet; + Long timestamp = null; + boolean isCarbon = false; + String serverMsgId = null; + final Element fin = original.findChild("fin", MessageArchiveService.Version.MAM_0.namespace); + if (fin != null) { + mXmppConnectionService.getMessageArchiveService().processFinLegacy(fin, original.getFrom()); + return; + } + final Element result = MessageArchiveService.Version.findResult(original); + final MessageArchiveService.Query query = result == null ? null : mXmppConnectionService.getMessageArchiveService().findQuery(result.getAttribute("queryid")); + if (query != null && query.validFrom(original.getFrom())) { + Pair f = original.getForwardedMessagePacket("result", query.version.namespace); + if (f == null) { + return; + } + timestamp = f.second; + packet = f.first; + serverMsgId = result.getAttribute("id"); + query.incrementMessageCount(); + } else if (query != null) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received mam result from invalid sender"); + return; + } else if (original.fromServer(account)) { + Pair f; + f = original.getForwardedMessagePacket("received", "urn:xmpp:carbons:2"); + f = f == null ? original.getForwardedMessagePacket("sent", "urn:xmpp:carbons:2") : f; + packet = f != null ? f.first : original; + if (handleErrorMessage(account, packet)) { + return; + } + timestamp = f != null ? f.second : null; + isCarbon = f != null; + } else { + packet = original; + } - boolean isTypeGroupChat = packet.getType() == MessagePacket.TYPE_GROUPCHAT; - if (query != null && !query.muc() && isTypeGroupChat) { - Log.e(Config.LOGTAG, account.getJid().asBareJid() + ": received groupchat (" + from + ") message on regular MAM request. skipping"); - return; - } - boolean isMucStatusMessage = InvalidJid.hasValidFrom(packet) && from.isBareJid() && mucUserElement != null && mucUserElement.hasChild("status"); - boolean selfAddressed; - if (packet.fromAccount(account)) { - status = Message.STATUS_SEND; - selfAddressed = to == null || account.getJid().asBareJid().equals(to.asBareJid()); - if (selfAddressed) { - counterpart = from; - } else { - counterpart = to != null ? to : account.getJid(); - } - } else { - status = Message.STATUS_RECEIVED; - counterpart = from; - selfAddressed = false; - } + if (timestamp == null) { + timestamp = AbstractParser.parseTimestamp(original, AbstractParser.parseTimestamp(packet)); + } + final String body = packet.getBody(); + final Element mucUserElement = packet.findChild("x", "http://jabber.org/protocol/muc#user"); + final String pgpEncrypted = packet.findChildContent("x", "jabber:x:encrypted"); + final Element replaceElement = packet.findChild("replace", "urn:xmpp:message-correct:0"); + final Element oob = packet.findChild("x", Namespace.OOB); + final Element xP1S3 = packet.findChild("x", Namespace.P1_S3_FILE_TRANSFER); + final URL xP1S3url = xP1S3 == null ? null : P1S3UrlStreamHandler.of(xP1S3); + final String oobUrl = oob != null ? oob.findChildContent("url") : null; + final String replacementId = replaceElement == null ? null : replaceElement.getAttribute("id"); + final Element axolotlEncrypted = packet.findChild(XmppAxolotlMessage.CONTAINERTAG, AxolotlService.PEP_PREFIX); + int status; + final Jid counterpart; + final Jid to = packet.getTo(); + final Jid from = packet.getFrom(); + final Element originId = packet.findChild("origin-id", Namespace.STANZA_IDS); + final String remoteMsgId; + if (originId != null && originId.getAttribute("id") != null) { + remoteMsgId = originId.getAttribute("id"); + } else { + remoteMsgId = packet.getId(); + } + boolean notify = false; - Invite invite = extractInvite(account, packet); - if (invite != null && invite.execute(account)) { - return; - } + if (from == null || !InvalidJid.isValid(from) || !InvalidJid.isValid(to)) { + Log.e(Config.LOGTAG, "encountered invalid message from='" + from + "' to='" + to + "'"); + return; + } - if ((body != null || pgpEncrypted != null || (axolotlEncrypted != null && axolotlEncrypted.hasChild("payload")) || oobUrl != null || xP1S3 != null) && !isMucStatusMessage) { - final boolean conversationIsProbablyMuc = isTypeGroupChat || mucUserElement != null || account.getXmppConnection().getMucServersWithholdAccount().contains(counterpart.getDomain()); - final Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, counterpart.asBareJid(), conversationIsProbablyMuc, false, query, false); - final boolean conversationMultiMode = conversation.getMode() == Conversation.MODE_MULTI; + boolean isTypeGroupChat = packet.getType() == MessagePacket.TYPE_GROUPCHAT; + if (query != null && !query.muc() && isTypeGroupChat) { + Log.e(Config.LOGTAG, account.getJid().asBareJid() + ": received groupchat (" + from + ") message on regular MAM request. skipping"); + return; + } + boolean isMucStatusMessage = InvalidJid.hasValidFrom(packet) && from.isBareJid() && mucUserElement != null && mucUserElement.hasChild("status"); + boolean selfAddressed; + if (packet.fromAccount(account)) { + status = Message.STATUS_SEND; + selfAddressed = to == null || account.getJid().asBareJid().equals(to.asBareJid()); + if (selfAddressed) { + counterpart = from; + } else { + counterpart = to != null ? to : account.getJid(); + } + } else { + status = Message.STATUS_RECEIVED; + counterpart = from; + selfAddressed = false; + } - if (serverMsgId == null) { - serverMsgId = extractStanzaId(packet, isTypeGroupChat, conversation); - } + Invite invite = extractInvite(account, packet); + if (invite != null && invite.execute(account)) { + return; + } + + if ((body != null || pgpEncrypted != null || (axolotlEncrypted != null && axolotlEncrypted.hasChild("payload")) || oobUrl != null || xP1S3 != null) && !isMucStatusMessage) { + final boolean conversationIsProbablyMuc = isTypeGroupChat || mucUserElement != null || account.getXmppConnection().getMucServersWithholdAccount().contains(counterpart.getDomain()); + final Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, counterpart.asBareJid(), conversationIsProbablyMuc, false, query, false); + final boolean conversationMultiMode = conversation.getMode() == Conversation.MODE_MULTI; + + if (serverMsgId == null) { + serverMsgId = extractStanzaId(packet, isTypeGroupChat, conversation); + } - if (selfAddressed) { - if (mXmppConnectionService.markMessage(conversation, remoteMsgId, Message.STATUS_SEND_RECEIVED, serverMsgId)) { - return; - } - status = Message.STATUS_RECEIVED; - if (remoteMsgId != null && conversation.findMessageWithRemoteId(remoteMsgId, counterpart) != null) { - return; - } - } + if (selfAddressed) { + if (mXmppConnectionService.markMessage(conversation, remoteMsgId, Message.STATUS_SEND_RECEIVED, serverMsgId)) { + return; + } + status = Message.STATUS_RECEIVED; + if (remoteMsgId != null && conversation.findMessageWithRemoteId(remoteMsgId, counterpart) != null) { + return; + } + } - if (isTypeGroupChat) { - if (conversation.getMucOptions().isSelf(counterpart)) { - status = Message.STATUS_SEND_RECEIVED; - isCarbon = true; //not really carbon but received from another resource - if (mXmppConnectionService.markMessage(conversation, remoteMsgId, status, serverMsgId)) { - return; - } else if (remoteMsgId == null || Config.IGNORE_ID_REWRITE_IN_MUC) { - Message message = conversation.findSentMessageWithBody(packet.getBody()); - if (message != null) { - mXmppConnectionService.markMessage(message, status); - return; - } - } - } else { - status = Message.STATUS_RECEIVED; - } - } - final Message message; - if (xP1S3url != null) { - message = new Message(conversation, xP1S3url.toString(), Message.ENCRYPTION_NONE, status); - message.setOob(true); - if (CryptoHelper.isPgpEncryptedUrl(xP1S3url.toString())) { - message.setEncryption(Message.ENCRYPTION_DECRYPTED); - } - } else if (pgpEncrypted != null && Config.supportOpenPgp()) { - message = new Message(conversation, pgpEncrypted, Message.ENCRYPTION_PGP, status); - } else if (axolotlEncrypted != null && Config.supportOmemo()) { - Jid origin; - Set fallbacksBySourceId = Collections.emptySet(); - if (conversationMultiMode) { - final Jid fallback = conversation.getMucOptions().getTrueCounterpart(counterpart); - origin = getTrueCounterpart(query != null ? mucUserElement : null, fallback); - if (origin == null) { - try { - fallbacksBySourceId = account.getAxolotlService().findCounterpartsBySourceId(XmppAxolotlMessage.parseSourceId(axolotlEncrypted)); - } catch (IllegalArgumentException e) { - //ignoring - } - } - if (origin == null && fallbacksBySourceId.size() == 0) { - Log.d(Config.LOGTAG, "axolotl message in anonymous conference received and no possible fallbacks"); - return; - } - } else { - fallbacksBySourceId = Collections.emptySet(); - origin = from; - } - if (origin != null) { - message = parseAxolotlChat(axolotlEncrypted, origin, conversation, status, query != null); - } else { - Message trial = null; - for (Jid fallback : fallbacksBySourceId) { - trial = parseAxolotlChat(axolotlEncrypted, fallback, conversation, status, query != null); - if (trial != null) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": decoded muc message using fallback"); - origin = fallback; - break; - } - } - message = trial; - } - if (message == null) { - if (query == null && extractChatState(mXmppConnectionService.find(account, counterpart.asBareJid()), isTypeGroupChat, packet)) { - mXmppConnectionService.updateConversationUi(); - } - if (query != null && status == Message.STATUS_SEND && remoteMsgId != null) { - Message previouslySent = conversation.findSentMessageWithUuid(remoteMsgId); - if (previouslySent != null && previouslySent.getServerMsgId() == null && serverMsgId != null) { - previouslySent.setServerMsgId(serverMsgId); - mXmppConnectionService.databaseBackend.updateMessage(previouslySent, false); - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": encountered previously sent OMEMO message without serverId. updating..."); - } - } - return; - } - if (conversationMultiMode) { - message.setTrueCounterpart(origin); - } - } else if (body == null && oobUrl != null) { - message = new Message(conversation, oobUrl, Message.ENCRYPTION_NONE, status); - message.setOob(true); - if (CryptoHelper.isPgpEncryptedUrl(oobUrl)) { - message.setEncryption(Message.ENCRYPTION_DECRYPTED); - } - } else { - message = new Message(conversation, body, Message.ENCRYPTION_NONE, status); - } + if (isTypeGroupChat) { + if (conversation.getMucOptions().isSelf(counterpart)) { + status = Message.STATUS_SEND_RECEIVED; + isCarbon = true; //not really carbon but received from another resource + if (mXmppConnectionService.markMessage(conversation, remoteMsgId, status, serverMsgId)) { + return; + } else if (remoteMsgId == null || Config.IGNORE_ID_REWRITE_IN_MUC) { + Message message = conversation.findSentMessageWithBody(packet.getBody()); + if (message != null) { + mXmppConnectionService.markMessage(message, status); + return; + } + } + } else { + status = Message.STATUS_RECEIVED; + } + } + final Message message; + if (xP1S3url != null) { + message = new Message(conversation, xP1S3url.toString(), Message.ENCRYPTION_NONE, status); + message.setOob(true); + if (CryptoHelper.isPgpEncryptedUrl(xP1S3url.toString())) { + message.setEncryption(Message.ENCRYPTION_DECRYPTED); + } + } else if (pgpEncrypted != null && Config.supportOpenPgp()) { + message = new Message(conversation, pgpEncrypted, Message.ENCRYPTION_PGP, status); + } else if (axolotlEncrypted != null && Config.supportOmemo()) { + Jid origin; + Set fallbacksBySourceId = Collections.emptySet(); + if (conversationMultiMode) { + final Jid fallback = conversation.getMucOptions().getTrueCounterpart(counterpart); + origin = getTrueCounterpart(query != null ? mucUserElement : null, fallback); + if (origin == null) { + try { + fallbacksBySourceId = account.getAxolotlService().findCounterpartsBySourceId(XmppAxolotlMessage.parseSourceId(axolotlEncrypted)); + } catch (IllegalArgumentException e) { + //ignoring + } + } + if (origin == null && fallbacksBySourceId.size() == 0) { + Log.d(Config.LOGTAG, "axolotl message in anonymous conference received and no possible fallbacks"); + return; + } + } else { + fallbacksBySourceId = Collections.emptySet(); + origin = from; + } + if (origin != null) { + message = parseAxolotlChat(axolotlEncrypted, origin, conversation, status, query != null); + } else { + Message trial = null; + for (Jid fallback : fallbacksBySourceId) { + trial = parseAxolotlChat(axolotlEncrypted, fallback, conversation, status, query != null); + if (trial != null) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": decoded muc message using fallback"); + origin = fallback; + break; + } + } + message = trial; + } + if (message == null) { + if (query == null && extractChatState(mXmppConnectionService.find(account, counterpart.asBareJid()), isTypeGroupChat, packet)) { + mXmppConnectionService.updateConversationUi(); + } + if (query != null && status == Message.STATUS_SEND && remoteMsgId != null) { + Message previouslySent = conversation.findSentMessageWithUuid(remoteMsgId); + if (previouslySent != null && previouslySent.getServerMsgId() == null && serverMsgId != null) { + previouslySent.setServerMsgId(serverMsgId); + mXmppConnectionService.databaseBackend.updateMessage(previouslySent, false); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": encountered previously sent OMEMO message without serverId. updating..."); + } + } + return; + } + if (conversationMultiMode) { + message.setTrueCounterpart(origin); + } + } else if (body == null && oobUrl != null) { + message = new Message(conversation, oobUrl, Message.ENCRYPTION_NONE, status); + message.setOob(true); + if (CryptoHelper.isPgpEncryptedUrl(oobUrl)) { + message.setEncryption(Message.ENCRYPTION_DECRYPTED); + } + } else { + message = new Message(conversation, body, Message.ENCRYPTION_NONE, status); + } - message.setCounterpart(counterpart); - message.setRemoteMsgId(remoteMsgId); - message.setServerMsgId(serverMsgId); - message.setCarbon(isCarbon); - message.setTime(timestamp); - if (body != null && body.equals(oobUrl)) { - message.setOob(true); - if (CryptoHelper.isPgpEncryptedUrl(oobUrl)) { - message.setEncryption(Message.ENCRYPTION_DECRYPTED); - } - } - message.markable = packet.hasChild("markable", "urn:xmpp:chat-markers:0"); - if (conversationMultiMode) { - message.setMucUser(conversation.getMucOptions().findUserByFullJid(counterpart)); - final Jid fallback = conversation.getMucOptions().getTrueCounterpart(counterpart); - Jid trueCounterpart; - if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) { - trueCounterpart = message.getTrueCounterpart(); - } else if (query != null && query.safeToExtractTrueCounterpart()) { - trueCounterpart = getTrueCounterpart(mucUserElement, fallback); - } else { - trueCounterpart = fallback; - } - if (trueCounterpart != null && trueCounterpart.asBareJid().equals(account.getJid().asBareJid())) { - status = isTypeGroupChat ? Message.STATUS_SEND_RECEIVED : Message.STATUS_SEND; - } - message.setStatus(status); - message.setTrueCounterpart(trueCounterpart); - if (!isTypeGroupChat) { - message.setType(Message.TYPE_PRIVATE); - } - } else { - updateLastseen(account, from); - } + message.setCounterpart(counterpart); + message.setRemoteMsgId(remoteMsgId); + message.setServerMsgId(serverMsgId); + message.setCarbon(isCarbon); + message.setTime(timestamp); + if (body != null && body.equals(oobUrl)) { + message.setOob(true); + if (CryptoHelper.isPgpEncryptedUrl(oobUrl)) { + message.setEncryption(Message.ENCRYPTION_DECRYPTED); + } + } + message.markable = packet.hasChild("markable", "urn:xmpp:chat-markers:0"); + if (conversationMultiMode) { + message.setMucUser(conversation.getMucOptions().findUserByFullJid(counterpart)); + final Jid fallback = conversation.getMucOptions().getTrueCounterpart(counterpart); + Jid trueCounterpart; + if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) { + trueCounterpart = message.getTrueCounterpart(); + } else if (query != null && query.safeToExtractTrueCounterpart()) { + trueCounterpart = getTrueCounterpart(mucUserElement, fallback); + } else { + trueCounterpart = fallback; + } + if (trueCounterpart != null && trueCounterpart.asBareJid().equals(account.getJid().asBareJid())) { + status = isTypeGroupChat ? Message.STATUS_SEND_RECEIVED : Message.STATUS_SEND; + } + message.setStatus(status); + message.setTrueCounterpart(trueCounterpart); + if (!isTypeGroupChat) { + message.setType(Message.TYPE_PRIVATE); + } + } else { + updateLastseen(account, from); + } - if (replacementId != null && mXmppConnectionService.allowMessageCorrection()) { - final Message replacedMessage = conversation.findMessageWithRemoteIdAndCounterpart(replacementId, - counterpart, - message.getStatus() == Message.STATUS_RECEIVED, - message.isCarbon()); - if (replacedMessage != null) { - final boolean fingerprintsMatch = replacedMessage.getFingerprint() == null - || replacedMessage.getFingerprint().equals(message.getFingerprint()); - final boolean trueCountersMatch = replacedMessage.getTrueCounterpart() != null - && replacedMessage.getTrueCounterpart().equals(message.getTrueCounterpart()); - final boolean mucUserMatches = query == null && replacedMessage.sameMucUser(message); //can not be checked when using mam - final boolean duplicate = conversation.hasDuplicateMessage(message); - if (fingerprintsMatch && (trueCountersMatch || !conversationMultiMode || mucUserMatches) && !duplicate) { - Log.d(Config.LOGTAG, "replaced message '" + replacedMessage.getBody() + "' with '" + message.getBody() + "'"); - synchronized (replacedMessage) { - final String uuid = replacedMessage.getUuid(); - replacedMessage.setUuid(UUID.randomUUID().toString()); - replacedMessage.setBody(message.getBody()); - replacedMessage.setEdited(replacedMessage.getRemoteMsgId()); - replacedMessage.setRemoteMsgId(remoteMsgId); - if (replacedMessage.getServerMsgId() == null || message.getServerMsgId() != null) { - replacedMessage.setServerMsgId(message.getServerMsgId()); - } - replacedMessage.setEncryption(message.getEncryption()); - if (replacedMessage.getStatus() == Message.STATUS_RECEIVED) { - replacedMessage.markUnread(); - } - extractChatState(mXmppConnectionService.find(account, counterpart.asBareJid()), isTypeGroupChat, packet); - mXmppConnectionService.updateMessage(replacedMessage, uuid); - mXmppConnectionService.getNotificationService().updateNotification(false); - if (mXmppConnectionService.confirmMessages() - && replacedMessage.getStatus() == Message.STATUS_RECEIVED - && (replacedMessage.trusted() || replacedMessage.getType() == Message.TYPE_PRIVATE) - && remoteMsgId != null - && !selfAddressed - && !isTypeGroupChat) { - processMessageReceipts(account, packet, query); - } - if (replacedMessage.getEncryption() == Message.ENCRYPTION_PGP) { - conversation.getAccount().getPgpDecryptionService().discard(replacedMessage); - conversation.getAccount().getPgpDecryptionService().decrypt(replacedMessage, false); - } - } - return; - } else { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received message correction but verification didn't check out"); - } - } - } + if (replacementId != null && mXmppConnectionService.allowMessageCorrection()) { + final Message replacedMessage = conversation.findMessageWithRemoteIdAndCounterpart(replacementId, + counterpart, + message.getStatus() == Message.STATUS_RECEIVED, + message.isCarbon()); + if (replacedMessage != null) { + final boolean fingerprintsMatch = replacedMessage.getFingerprint() == null + || replacedMessage.getFingerprint().equals(message.getFingerprint()); + final boolean trueCountersMatch = replacedMessage.getTrueCounterpart() != null + && replacedMessage.getTrueCounterpart().equals(message.getTrueCounterpart()); + final boolean mucUserMatches = query == null && replacedMessage.sameMucUser(message); //can not be checked when using mam + final boolean duplicate = conversation.hasDuplicateMessage(message); + if (fingerprintsMatch && (trueCountersMatch || !conversationMultiMode || mucUserMatches) && !duplicate) { + Log.d(Config.LOGTAG, "replaced message '" + replacedMessage.getBody() + "' with '" + message.getBody() + "'"); + synchronized (replacedMessage) { + final String uuid = replacedMessage.getUuid(); + replacedMessage.setUuid(UUID.randomUUID().toString()); + replacedMessage.setBody(message.getBody()); + replacedMessage.setEdited(replacedMessage.getRemoteMsgId()); + replacedMessage.setRemoteMsgId(remoteMsgId); + if (replacedMessage.getServerMsgId() == null || message.getServerMsgId() != null) { + replacedMessage.setServerMsgId(message.getServerMsgId()); + } + replacedMessage.setEncryption(message.getEncryption()); + if (replacedMessage.getStatus() == Message.STATUS_RECEIVED) { + replacedMessage.markUnread(); + } + extractChatState(mXmppConnectionService.find(account, counterpart.asBareJid()), isTypeGroupChat, packet); + mXmppConnectionService.updateMessage(replacedMessage, uuid); + mXmppConnectionService.getNotificationService().updateNotification(false); + if (mXmppConnectionService.confirmMessages() + && replacedMessage.getStatus() == Message.STATUS_RECEIVED + && (replacedMessage.trusted() || replacedMessage.getType() == Message.TYPE_PRIVATE) + && remoteMsgId != null + && !selfAddressed + && !isTypeGroupChat) { + processMessageReceipts(account, packet, query); + } + if (replacedMessage.getEncryption() == Message.ENCRYPTION_PGP) { + conversation.getAccount().getPgpDecryptionService().discard(replacedMessage); + conversation.getAccount().getPgpDecryptionService().decrypt(replacedMessage, false); + } + } + return; + } else { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received message correction but verification didn't check out"); + } + } + } - long deletionDate = mXmppConnectionService.getAutomaticMessageDeletionDate(); - if (deletionDate != 0 && message.getTimeSent() < deletionDate) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": skipping message from " + message.getCounterpart().toString() + " because it was sent prior to our deletion date"); - return; - } + long deletionDate = mXmppConnectionService.getAutomaticMessageDeletionDate(); + if (deletionDate != 0 && message.getTimeSent() < deletionDate) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": skipping message from " + message.getCounterpart().toString() + " because it was sent prior to our deletion date"); + return; + } - boolean checkForDuplicates = (isTypeGroupChat && packet.hasChild("delay", "urn:xmpp:delay")) - || message.getType() == Message.TYPE_PRIVATE - || message.getServerMsgId() != null - || (query == null && mXmppConnectionService.getMessageArchiveService().isCatchupInProgress(conversation)); - if (checkForDuplicates) { - final Message duplicate = conversation.findDuplicateMessage(message); - if (duplicate != null) { - final boolean serverMsgIdUpdated; - if (duplicate.getStatus() != Message.STATUS_RECEIVED - && duplicate.getUuid().equals(message.getRemoteMsgId()) - && duplicate.getServerMsgId() == null - && message.getServerMsgId() != null) { - duplicate.setServerMsgId(message.getServerMsgId()); - if (mXmppConnectionService.databaseBackend.updateMessage(duplicate, false)) { - serverMsgIdUpdated = true; - } else { - serverMsgIdUpdated = false; - Log.e(Config.LOGTAG,"failed to update message"); - } - } else { - serverMsgIdUpdated = false; - } - Log.d(Config.LOGTAG, "skipping duplicate message with " + message.getCounterpart() + ". serverMsgIdUpdated=" + Boolean.toString(serverMsgIdUpdated)); - return; - } - } + boolean checkForDuplicates = (isTypeGroupChat && packet.hasChild("delay", "urn:xmpp:delay")) + || message.getType() == Message.TYPE_PRIVATE + || message.getServerMsgId() != null + || (query == null && mXmppConnectionService.getMessageArchiveService().isCatchupInProgress(conversation)); + if (checkForDuplicates) { + final Message duplicate = conversation.findDuplicateMessage(message); + if (duplicate != null) { + final boolean serverMsgIdUpdated; + if (duplicate.getStatus() != Message.STATUS_RECEIVED + && duplicate.getUuid().equals(message.getRemoteMsgId()) + && duplicate.getServerMsgId() == null + && message.getServerMsgId() != null) { + duplicate.setServerMsgId(message.getServerMsgId()); + if (mXmppConnectionService.databaseBackend.updateMessage(duplicate, false)) { + serverMsgIdUpdated = true; + } else { + serverMsgIdUpdated = false; + Log.e(Config.LOGTAG, "failed to update message"); + } + } else { + serverMsgIdUpdated = false; + } + Log.d(Config.LOGTAG, "skipping duplicate message with " + message.getCounterpart() + ". serverMsgIdUpdated=" + Boolean.toString(serverMsgIdUpdated)); + return; + } + } - if (query != null && query.getPagingOrder() == MessageArchiveService.PagingOrder.REVERSE) { - conversation.prepend(query.getActualInThisQuery(), message); - } else { - conversation.add(message); - } - if (query != null) { - query.incrementActualMessageCount(); - } + if (query != null && query.getPagingOrder() == MessageArchiveService.PagingOrder.REVERSE) { + conversation.prepend(query.getActualInThisQuery(), message); + } else { + conversation.add(message); + } + if (query != null) { + query.incrementActualMessageCount(); + } - if (query == null || query.isCatchup()) { //either no mam or catchup - if (status == Message.STATUS_SEND || status == Message.STATUS_SEND_RECEIVED) { - mXmppConnectionService.markRead(conversation); - if (query == null) { - activateGracePeriod(account); - } - } else { - message.markUnread(); - notify = true; - } - } + if (query == null || query.isCatchup()) { //either no mam or catchup + if (status == Message.STATUS_SEND || status == Message.STATUS_SEND_RECEIVED) { + mXmppConnectionService.markRead(conversation); + if (query == null) { + activateGracePeriod(account); + } + } else { + message.markUnread(); + notify = true; + } + } - if (message.getEncryption() == Message.ENCRYPTION_PGP) { - notify = conversation.getAccount().getPgpDecryptionService().decrypt(message, notify); - } else if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL_NOT_FOR_THIS_DEVICE) { - notify = false; - } + if (message.getEncryption() == Message.ENCRYPTION_PGP) { + notify = conversation.getAccount().getPgpDecryptionService().decrypt(message, notify); + } else if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL_NOT_FOR_THIS_DEVICE) { + notify = false; + } - if (query == null) { - extractChatState(mXmppConnectionService.find(account, counterpart.asBareJid()), isTypeGroupChat, packet); - mXmppConnectionService.updateConversationUi(); - } + if (query == null) { + extractChatState(mXmppConnectionService.find(account, counterpart.asBareJid()), isTypeGroupChat, packet); + mXmppConnectionService.updateConversationUi(); + } - if (mXmppConnectionService.confirmMessages() - && message.getStatus() == Message.STATUS_RECEIVED - && (message.trusted() || message.getType() == Message.TYPE_PRIVATE) - && remoteMsgId != null - && !selfAddressed - && !isTypeGroupChat) { - processMessageReceipts(account, packet, query); - } + if (mXmppConnectionService.confirmMessages() + && message.getStatus() == Message.STATUS_RECEIVED + && (message.trusted() || message.getType() == Message.TYPE_PRIVATE) + && remoteMsgId != null + && !selfAddressed + && !isTypeGroupChat) { + processMessageReceipts(account, packet, query); + } - mXmppConnectionService.databaseBackend.createMessage(message); - final HttpConnectionManager manager = this.mXmppConnectionService.getHttpConnectionManager(); - if (message.trusted() && message.treatAsDownloadable() && manager.getAutoAcceptFileSize() > 0) { - manager.createNewDownloadConnection(message); - } else if (notify) { - if (query != null && query.isCatchup()) { - mXmppConnectionService.getNotificationService().pushFromBacklog(message); - } else { - mXmppConnectionService.getNotificationService().push(message); - } - } - } else if (!packet.hasChild("body")) { //no body + mXmppConnectionService.databaseBackend.createMessage(message); + final HttpConnectionManager manager = this.mXmppConnectionService.getHttpConnectionManager(); + if (message.trusted() && message.treatAsDownloadable() && manager.getAutoAcceptFileSize() > 0) { + manager.createNewDownloadConnection(message); + } else if (notify) { + if (query != null && query.isCatchup()) { + mXmppConnectionService.getNotificationService().pushFromBacklog(message); + } else { + mXmppConnectionService.getNotificationService().push(message); + } + } + } else if (!packet.hasChild("body")) { //no body - final Conversation conversation = mXmppConnectionService.find(account, from.asBareJid()); - if (axolotlEncrypted != null) { - Jid origin; - if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) { - final Jid fallback = conversation.getMucOptions().getTrueCounterpart(counterpart); - origin = getTrueCounterpart(query != null ? mucUserElement : null, fallback); - if (origin == null) { - Log.d(Config.LOGTAG, "omemo key transport message in anonymous conference received"); - return; - } - } else if (isTypeGroupChat) { - return; - } else { - origin = from; - } - try { - final XmppAxolotlMessage xmppAxolotlMessage = XmppAxolotlMessage.fromElement(axolotlEncrypted, origin.asBareJid()); - account.getAxolotlService().processReceivingKeyTransportMessage(xmppAxolotlMessage, query != null); - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": omemo key transport message received from " + origin); - } catch (Exception e) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": invalid omemo key transport message received " + e.getMessage()); - return; - } - } + final Conversation conversation = mXmppConnectionService.find(account, from.asBareJid()); + if (axolotlEncrypted != null) { + Jid origin; + if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) { + final Jid fallback = conversation.getMucOptions().getTrueCounterpart(counterpart); + origin = getTrueCounterpart(query != null ? mucUserElement : null, fallback); + if (origin == null) { + Log.d(Config.LOGTAG, "omemo key transport message in anonymous conference received"); + return; + } + } else if (isTypeGroupChat) { + return; + } else { + origin = from; + } + try { + final XmppAxolotlMessage xmppAxolotlMessage = XmppAxolotlMessage.fromElement(axolotlEncrypted, origin.asBareJid()); + account.getAxolotlService().processReceivingKeyTransportMessage(xmppAxolotlMessage, query != null); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": omemo key transport message received from " + origin); + } catch (Exception e) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": invalid omemo key transport message received " + e.getMessage()); + return; + } + } - if (query == null && extractChatState(mXmppConnectionService.find(account, counterpart.asBareJid()), isTypeGroupChat, packet)) { - mXmppConnectionService.updateConversationUi(); - } + if (query == null && extractChatState(mXmppConnectionService.find(account, counterpart.asBareJid()), isTypeGroupChat, packet)) { + mXmppConnectionService.updateConversationUi(); + } - if (isTypeGroupChat) { - if (packet.hasChild("subject")) { - if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) { - conversation.setHasMessagesLeftOnServer(conversation.countMessages() > 0); - String subject = packet.findInternationalizedChildContent("subject"); - if (conversation.getMucOptions().setSubject(subject)) { - mXmppConnectionService.updateConversation(conversation); - } - mXmppConnectionService.updateConversationUi(); - return; - } - } - } - if (conversation != null && mucUserElement != null && InvalidJid.hasValidFrom(packet) && from.isBareJid()) { - for (Element child : mucUserElement.getChildren()) { - if ("status".equals(child.getName())) { - try { - int code = Integer.parseInt(child.getAttribute("code")); - if ((code >= 170 && code <= 174) || (code >= 102 && code <= 104)) { - mXmppConnectionService.fetchConferenceConfiguration(conversation); - break; - } - } catch (Exception e) { - //ignored - } - } else if ("item".equals(child.getName())) { - MucOptions.User user = AbstractParser.parseItem(conversation, child); - Log.d(Config.LOGTAG, account.getJid() + ": changing affiliation for " - + user.getRealJid() + " to " + user.getAffiliation() + " in " - + conversation.getJid().asBareJid()); - if (!user.realJidMatchesAccount()) { - boolean isNew = conversation.getMucOptions().updateUser(user); - mXmppConnectionService.getAvatarService().clear(conversation); - mXmppConnectionService.updateMucRosterUi(); - mXmppConnectionService.updateConversationUi(); - Contact contact = user.getContact(); - if (!user.getAffiliation().ranks(MucOptions.Affiliation.MEMBER)) { - Jid jid = user.getRealJid(); - List cryptoTargets = conversation.getAcceptedCryptoTargets(); - if (cryptoTargets.remove(user.getRealJid())) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": removed " + jid + " from crypto targets of " + conversation.getName()); - conversation.setAcceptedCryptoTargets(cryptoTargets); - mXmppConnectionService.updateConversation(conversation); - } - } else if (isNew - && user.getRealJid() != null - && conversation.getMucOptions().isPrivateAndNonAnonymous() - && (contact == null || !contact.mutualPresenceSubscription()) - && account.getAxolotlService().hasEmptyDeviceList(user.getRealJid())) { - account.getAxolotlService().fetchDeviceIds(user.getRealJid()); - } - } - } - } - } - } + if (isTypeGroupChat) { + if (packet.hasChild("subject")) { + if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) { + conversation.setHasMessagesLeftOnServer(conversation.countMessages() > 0); + String subject = packet.findInternationalizedChildContent("subject"); + if (conversation.getMucOptions().setSubject(subject)) { + mXmppConnectionService.updateConversation(conversation); + } + mXmppConnectionService.updateConversationUi(); + return; + } + } + } + if (conversation != null && mucUserElement != null && InvalidJid.hasValidFrom(packet) && from.isBareJid()) { + for (Element child : mucUserElement.getChildren()) { + if ("status".equals(child.getName())) { + try { + int code = Integer.parseInt(child.getAttribute("code")); + if ((code >= 170 && code <= 174) || (code >= 102 && code <= 104)) { + mXmppConnectionService.fetchConferenceConfiguration(conversation); + break; + } + } catch (Exception e) { + //ignored + } + } else if ("item".equals(child.getName())) { + MucOptions.User user = AbstractParser.parseItem(conversation, child); + Log.d(Config.LOGTAG, account.getJid() + ": changing affiliation for " + + user.getRealJid() + " to " + user.getAffiliation() + " in " + + conversation.getJid().asBareJid()); + if (!user.realJidMatchesAccount()) { + boolean isNew = conversation.getMucOptions().updateUser(user); + mXmppConnectionService.getAvatarService().clear(conversation); + mXmppConnectionService.updateMucRosterUi(); + mXmppConnectionService.updateConversationUi(); + Contact contact = user.getContact(); + if (!user.getAffiliation().ranks(MucOptions.Affiliation.MEMBER)) { + Jid jid = user.getRealJid(); + List cryptoTargets = conversation.getAcceptedCryptoTargets(); + if (cryptoTargets.remove(user.getRealJid())) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": removed " + jid + " from crypto targets of " + conversation.getName()); + conversation.setAcceptedCryptoTargets(cryptoTargets); + mXmppConnectionService.updateConversation(conversation); + } + } else if (isNew + && user.getRealJid() != null + && conversation.getMucOptions().isPrivateAndNonAnonymous() + && (contact == null || !contact.mutualPresenceSubscription()) + && account.getAxolotlService().hasEmptyDeviceList(user.getRealJid())) { + account.getAxolotlService().fetchDeviceIds(user.getRealJid()); + } + } + } + } + } + } - Element received = packet.findChild("received", "urn:xmpp:chat-markers:0"); - if (received == null) { - received = packet.findChild("received", "urn:xmpp:receipts"); - } - if (received != null) { - String id = received.getAttribute("id"); - if (packet.fromAccount(account)) { - if (query != null && id != null && packet.getTo() != null) { - query.removePendingReceiptRequest(new ReceiptRequest(packet.getTo(), id)); - } - } else { - mXmppConnectionService.markMessage(account, from.asBareJid(), received.getAttribute("id"), Message.STATUS_SEND_RECEIVED); - } - } - Element displayed = packet.findChild("displayed", "urn:xmpp:chat-markers:0"); - if (displayed != null) { - final String id = displayed.getAttribute("id"); - final Jid sender = InvalidJid.getNullForInvalid(displayed.getAttributeAsJid("sender")); - if (packet.fromAccount(account) && !selfAddressed) { - dismissNotification(account, counterpart, query); - } else if (isTypeGroupChat) { - Conversation conversation = mXmppConnectionService.find(account, counterpart.asBareJid()); - if (conversation != null && id != null && sender != null) { - Message message = conversation.findMessageWithRemoteId(id, sender); - if (message != null) { - final Jid fallback = conversation.getMucOptions().getTrueCounterpart(counterpart); - final Jid trueJid = getTrueCounterpart((query != null && query.safeToExtractTrueCounterpart()) ? mucUserElement : null, fallback); - final boolean trueJidMatchesAccount = account.getJid().asBareJid().equals(trueJid == null ? null : trueJid.asBareJid()); - if (trueJidMatchesAccount || conversation.getMucOptions().isSelf(counterpart)) { - if (!message.isRead() && (query == null || query.isCatchup())) { //checking if message is unread fixes race conditions with reflections - mXmppConnectionService.markRead(conversation); - } - } else if (!counterpart.isBareJid() && trueJid != null) { - ReadByMarker readByMarker = ReadByMarker.from(counterpart, trueJid); - if (message.addReadByMarker(readByMarker)) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": added read by (" + readByMarker.getRealJid() + ") to message '" + message.getBody() + "'"); - mXmppConnectionService.updateMessage(message, false); - } - } - } - } - } else { - final Message displayedMessage = mXmppConnectionService.markMessage(account, from.asBareJid(), id, Message.STATUS_SEND_DISPLAYED); - Message message = displayedMessage == null ? null : displayedMessage.prev(); - while (message != null - && message.getStatus() == Message.STATUS_SEND_RECEIVED - && message.getTimeSent() < displayedMessage.getTimeSent()) { - mXmppConnectionService.markMessage(message, Message.STATUS_SEND_DISPLAYED); - message = message.prev(); - } - if (displayedMessage != null && selfAddressed) { - dismissNotification(account, counterpart, query); - } - } - } + Element received = packet.findChild("received", "urn:xmpp:chat-markers:0"); + if (received == null) { + received = packet.findChild("received", "urn:xmpp:receipts"); + } + if (received != null) { + String id = received.getAttribute("id"); + if (packet.fromAccount(account)) { + if (query != null && id != null && packet.getTo() != null) { + query.removePendingReceiptRequest(new ReceiptRequest(packet.getTo(), id)); + } + } else { + mXmppConnectionService.markMessage(account, from.asBareJid(), received.getAttribute("id"), Message.STATUS_SEND_RECEIVED); + } + } + Element displayed = packet.findChild("displayed", "urn:xmpp:chat-markers:0"); + if (displayed != null) { + final String id = displayed.getAttribute("id"); + final Jid sender = InvalidJid.getNullForInvalid(displayed.getAttributeAsJid("sender")); + if (packet.fromAccount(account) && !selfAddressed) { + dismissNotification(account, counterpart, query); + } else if (isTypeGroupChat) { + Conversation conversation = mXmppConnectionService.find(account, counterpart.asBareJid()); + if (conversation != null && id != null && sender != null) { + Message message = conversation.findMessageWithRemoteId(id, sender); + if (message != null) { + final Jid fallback = conversation.getMucOptions().getTrueCounterpart(counterpart); + final Jid trueJid = getTrueCounterpart((query != null && query.safeToExtractTrueCounterpart()) ? mucUserElement : null, fallback); + final boolean trueJidMatchesAccount = account.getJid().asBareJid().equals(trueJid == null ? null : trueJid.asBareJid()); + if (trueJidMatchesAccount || conversation.getMucOptions().isSelf(counterpart)) { + if (!message.isRead() && (query == null || query.isCatchup())) { //checking if message is unread fixes race conditions with reflections + mXmppConnectionService.markRead(conversation); + } + } else if (!counterpart.isBareJid() && trueJid != null) { + ReadByMarker readByMarker = ReadByMarker.from(counterpart, trueJid); + if (message.addReadByMarker(readByMarker)) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": added read by (" + readByMarker.getRealJid() + ") to message '" + message.getBody() + "'"); + mXmppConnectionService.updateMessage(message, false); + } + } + } + } + } else { + final Message displayedMessage = mXmppConnectionService.markMessage(account, from.asBareJid(), id, Message.STATUS_SEND_DISPLAYED); + Message message = displayedMessage == null ? null : displayedMessage.prev(); + while (message != null + && message.getStatus() == Message.STATUS_SEND_RECEIVED + && message.getTimeSent() < displayedMessage.getTimeSent()) { + mXmppConnectionService.markMessage(message, Message.STATUS_SEND_DISPLAYED); + message = message.prev(); + } + if (displayedMessage != null && selfAddressed) { + dismissNotification(account, counterpart, query); + } + } + } - Element event = original.findChild("event", "http://jabber.org/protocol/pubsub#event"); - if (event != null && InvalidJid.hasValidFrom(original)) { - parseEvent(event, original.getFrom(), account); - } + final Element event = original.findChild("event", "http://jabber.org/protocol/pubsub#event"); + if (event != null && InvalidJid.hasValidFrom(original)) { + if (event.hasChild("items")) { + parseEvent(event, original.getFrom(), account); + } else if (event.hasChild("delete")) { + parseDeleteEvent(event, original.getFrom(), account); + } + } - final String nick = packet.findChildContent("nick", Namespace.NICK); - if (nick != null && InvalidJid.hasValidFrom(original)) { - Contact contact = account.getRoster().getContact(from); - if (contact.setPresenceName(nick)) { - mXmppConnectionService.getAvatarService().clear(contact); - } - } - } + final String nick = packet.findChildContent("nick", Namespace.NICK); + if (nick != null && InvalidJid.hasValidFrom(original)) { + Contact contact = account.getRoster().getContact(from); + if (contact.setPresenceName(nick)) { + mXmppConnectionService.getAvatarService().clear(contact); + } + } + } - private void dismissNotification(Account account, Jid counterpart, MessageArchiveService.Query query) { - Conversation conversation = mXmppConnectionService.find(account, counterpart.asBareJid()); - if (conversation != null && (query == null || query.isCatchup())) { - mXmppConnectionService.markRead(conversation); //TODO only mark messages read that are older than timestamp - } - } + private void dismissNotification(Account account, Jid counterpart, MessageArchiveService.Query query) { + Conversation conversation = mXmppConnectionService.find(account, counterpart.asBareJid()); + if (conversation != null && (query == null || query.isCatchup())) { + mXmppConnectionService.markRead(conversation); //TODO only mark messages read that are older than timestamp + } + } - private void processMessageReceipts(Account account, MessagePacket packet, MessageArchiveService.Query query) { - final boolean markable = packet.hasChild("markable", "urn:xmpp:chat-markers:0"); - final boolean request = packet.hasChild("request", "urn:xmpp:receipts"); - if (query == null) { - final ArrayList receiptsNamespaces = new ArrayList<>(); - if (markable) { - receiptsNamespaces.add("urn:xmpp:chat-markers:0"); - } - if (request) { - receiptsNamespaces.add("urn:xmpp:receipts"); - } - if (receiptsNamespaces.size() > 0) { - MessagePacket receipt = mXmppConnectionService.getMessageGenerator().received(account, - packet, - receiptsNamespaces, - packet.getType()); - mXmppConnectionService.sendMessagePacket(account, receipt); - } - } else if (query.isCatchup()) { - if (request) { - query.addPendingReceiptRequest(new ReceiptRequest(packet.getFrom(), packet.getId())); - } - } - } + private void processMessageReceipts(Account account, MessagePacket packet, MessageArchiveService.Query query) { + final boolean markable = packet.hasChild("markable", "urn:xmpp:chat-markers:0"); + final boolean request = packet.hasChild("request", "urn:xmpp:receipts"); + if (query == null) { + final ArrayList receiptsNamespaces = new ArrayList<>(); + if (markable) { + receiptsNamespaces.add("urn:xmpp:chat-markers:0"); + } + if (request) { + receiptsNamespaces.add("urn:xmpp:receipts"); + } + if (receiptsNamespaces.size() > 0) { + MessagePacket receipt = mXmppConnectionService.getMessageGenerator().received(account, + packet, + receiptsNamespaces, + packet.getType()); + mXmppConnectionService.sendMessagePacket(account, receipt); + } + } else if (query.isCatchup()) { + if (request) { + query.addPendingReceiptRequest(new ReceiptRequest(packet.getFrom(), packet.getId())); + } + } + } - private void activateGracePeriod(Account account) { - long duration = mXmppConnectionService.getLongPreference("grace_period_length", R.integer.grace_period) * 1000; - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": activating grace period till " + TIME_FORMAT.format(new Date(System.currentTimeMillis() + duration))); - account.activateGracePeriod(duration); - } + private void activateGracePeriod(Account account) { + long duration = mXmppConnectionService.getLongPreference("grace_period_length", R.integer.grace_period) * 1000; + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": activating grace period till " + TIME_FORMAT.format(new Date(System.currentTimeMillis() + duration))); + account.activateGracePeriod(duration); + } - private class Invite { - final Jid jid; - final String password; - final Contact inviter; + private class Invite { + final Jid jid; + final String password; + final Contact inviter; - Invite(Jid jid, String password, Contact inviter) { - this.jid = jid; - this.password = password; - this.inviter = inviter; - } + Invite(Jid jid, String password, Contact inviter) { + this.jid = jid; + this.password = password; + this.inviter = inviter; + } - public boolean execute(Account account) { - if (jid != null) { - Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, jid, true, false); - if (!conversation.getMucOptions().online()) { - conversation.getMucOptions().setPassword(password); - mXmppConnectionService.databaseBackend.updateConversation(conversation); - mXmppConnectionService.joinMuc(conversation, inviter != null && inviter.mutualPresenceSubscription()); - mXmppConnectionService.updateConversationUi(); - } - return true; - } - return false; - } - } + public boolean execute(Account account) { + if (jid != null) { + Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, jid, true, false); + if (!conversation.getMucOptions().online()) { + conversation.getMucOptions().setPassword(password); + mXmppConnectionService.databaseBackend.updateConversation(conversation); + mXmppConnectionService.joinMuc(conversation, inviter != null && inviter.mutualPresenceSubscription()); + mXmppConnectionService.updateConversationUi(); + } + return true; + } + return false; + } + } } diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 0165cbbea..e33870dae 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -285,11 +285,19 @@ public class XmppConnectionService extends Service { } } } - boolean needsUpdating = account.setOption(Account.OPTION_LOGGED_IN_SUCCESSFULLY, true); - needsUpdating |= account.setOption(Account.OPTION_HTTP_UPLOAD_AVAILABLE, account.getXmppConnection().getFeatures().httpUpload(0)); - if (needsUpdating) { + boolean loggedInSuccessfully = account.setOption(Account.OPTION_LOGGED_IN_SUCCESSFULLY, true); + boolean gainedFeature = account.setOption(Account.OPTION_HTTP_UPLOAD_AVAILABLE, account.getXmppConnection().getFeatures().httpUpload(0)); + if (loggedInSuccessfully || gainedFeature) { databaseBackend.updateAccount(account); } + + if (loggedInSuccessfully) { + if (!TextUtils.isEmpty(account.getDisplayName())) { + Log.d(Config.LOGTAG,account.getJid().asBareJid()+": display name wasn't empty on first log in. publishing"); + publishDisplayName(account); + } + } + account.getRoster().clearPresences(); mJingleConnectionManager.cancelInTransmission(); mQuickConversationsService.considerSyncBackground(false); @@ -3828,14 +3836,17 @@ public class XmppConnectionService extends Service { public void publishDisplayName(Account account) { String displayName = account.getDisplayName(); - if (displayName != null && !displayName.isEmpty()) { - IqPacket publish = mIqGenerator.publishNick(displayName); - sendIqPacket(account, publish, (account1, packet) -> { - if (packet.getType() == IqPacket.TYPE.ERROR) { - Log.d(Config.LOGTAG, account1.getJid().asBareJid() + ": could not publish nick"); - } - }); - } + final IqPacket request; + if (TextUtils.isEmpty(displayName)) { + request = mIqGenerator.deleteNode(Namespace.NICK); + } else { + request = mIqGenerator.publishNick(displayName); + } + sendIqPacket(account, request, (account1, packet) -> { + if (packet.getType() == IqPacket.TYPE.ERROR) { + Log.d(Config.LOGTAG, account1.getJid().asBareJid() + ": unable to modify nick name "+packet.toString()); + } + }); } public ServiceDiscoveryResult getCachedServiceDiscoveryResult(Pair key) { diff --git a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java index 760486ce3..52d54f142 100644 --- a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java @@ -74,1157 +74,1172 @@ import eu.siacs.conversations.xmpp.pep.Avatar; import rocks.xmpp.addr.Jid; public class EditAccountActivity extends OmemoActivity implements OnAccountUpdate, OnUpdateBlocklist, - OnKeyStatusUpdated, OnCaptchaRequested, KeyChainAliasCallback, XmppConnectionService.OnShowErrorToast, XmppConnectionService.OnMamPreferencesFetched { - - public static final String EXTRA_OPENED_FROM_NOTIFICATION = "opened_from_notification"; - - private static final int REQUEST_DATA_SAVER = 0xf244; - private static final int REQUEST_CHANGE_STATUS = 0xee11; - - private AlertDialog mCaptchaDialog = null; - - private Jid jidToEdit; - private boolean mInitMode = false; - private boolean mUsernameMode = Config.DOMAIN_LOCK != null; - private boolean mShowOptions = false; - private Account mAccount; - private String messageFingerprint; - - private final PendingItem mPendingPresenceTemplate = new PendingItem<>(); - - private boolean mFetchingAvatar = false; - - private final OnClickListener mSaveButtonClickListener = new OnClickListener() { - - @Override - public void onClick(final View v) { - final String password = binding.accountPassword.getText().toString(); - final boolean wasDisabled = mAccount != null && mAccount.getStatus() == Account.State.DISABLED; - final boolean accountInfoEdited = accountInfoEdited(); - - if (!mInitMode && passwordChangedInMagicCreateMode()) { - gotoChangePassword(password); - return; - } - if (mInitMode && mAccount != null) { - mAccount.setOption(Account.OPTION_DISABLED, false); - } - if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED && !accountInfoEdited) { - mAccount.setOption(Account.OPTION_DISABLED, false); - if (!xmppConnectionService.updateAccount(mAccount)) { - Toast.makeText(EditAccountActivity.this, R.string.unable_to_update_account, Toast.LENGTH_SHORT).show(); - } - return; - } - final boolean registerNewAccount = binding.accountRegisterNew.isChecked() && !Config.DISALLOW_REGISTRATION_IN_UI; - if (mUsernameMode && binding.accountJid.getText().toString().contains("@")) { - binding.accountJidLayout.setError(getString(R.string.invalid_username)); - removeErrorsOnAllBut(binding.accountJidLayout); - binding.accountJid.requestFocus(); - return; - } - - XmppConnection connection = mAccount == null ? null : mAccount.getXmppConnection(); - boolean openRegistrationUrl = registerNewAccount && !accountInfoEdited && mAccount != null && mAccount.getStatus() == Account.State.REGISTRATION_WEB; - boolean openPaymentUrl = mAccount != null && mAccount.getStatus() == Account.State.PAYMENT_REQUIRED; - final boolean redirectionWorthyStatus = openPaymentUrl || openRegistrationUrl; - URL url = connection != null && redirectionWorthyStatus ? connection.getRedirectionUrl() : null; - if (url != null && !wasDisabled) { - try { - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url.toString()))); - return; - } catch (ActivityNotFoundException e) { - Toast.makeText(EditAccountActivity.this, R.string.application_found_to_open_website, Toast.LENGTH_SHORT).show(); - return; - } - } - - final Jid jid; - try { - if (mUsernameMode) { - jid = Jid.of(binding.accountJid.getText().toString(), getUserModeDomain(), null); - } else { - jid = Jid.of(binding.accountJid.getText().toString()); - } - } catch (final NullPointerException | IllegalArgumentException e) { - if (mUsernameMode) { - binding.accountJidLayout.setError(getString(R.string.invalid_username)); - } else { - binding.accountJidLayout.setError(getString(R.string.invalid_jid)); - } - binding.accountJid.requestFocus(); - removeErrorsOnAllBut(binding.accountJidLayout); - return; - } - String hostname = null; - int numericPort = 5222; - if (mShowOptions) { - hostname = binding.hostname.getText().toString().replaceAll("\\s", ""); - final String port = binding.port.getText().toString().replaceAll("\\s", ""); - if (hostname.contains(" ")) { - binding.hostnameLayout.setError(getString(R.string.not_valid_hostname)); - binding.hostname.requestFocus(); - removeErrorsOnAllBut(binding.hostnameLayout); - return; - } - try { - numericPort = Integer.parseInt(port); - if (numericPort < 0 || numericPort > 65535) { - binding.portLayout.setError(getString(R.string.not_a_valid_port)); - removeErrorsOnAllBut(binding.portLayout); - binding.port.requestFocus(); - return; - } - - } catch (NumberFormatException e) { - binding.portLayout.setError(getString(R.string.not_a_valid_port)); - removeErrorsOnAllBut(binding.portLayout); - binding.port.requestFocus(); - return; - } - } - - if (jid.getLocal() == null) { - if (mUsernameMode) { - binding.accountJidLayout.setError(getString(R.string.invalid_username)); - } else { - binding.accountJidLayout.setError(getString(R.string.invalid_jid)); - } - removeErrorsOnAllBut(binding.accountJidLayout); - binding.accountJid.requestFocus(); - return; - } - if (mAccount != null) { - if (mInitMode && mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)) { - mAccount.setOption(Account.OPTION_MAGIC_CREATE, mAccount.getPassword().contains(password)); - } - mAccount.setJid(jid); - mAccount.setPort(numericPort); - mAccount.setHostname(hostname); - binding.accountJidLayout.setError(null); - mAccount.setPassword(password); - mAccount.setOption(Account.OPTION_REGISTER, registerNewAccount); - if (!xmppConnectionService.updateAccount(mAccount)) { - Toast.makeText(EditAccountActivity.this, R.string.unable_to_update_account, Toast.LENGTH_SHORT).show(); - return; - } - } else { - if (xmppConnectionService.findAccountByJid(jid) != null) { - binding.accountJidLayout.setError(getString(R.string.account_already_exists)); - removeErrorsOnAllBut(binding.accountJidLayout); - binding.accountJid.requestFocus(); - return; - } - mAccount = new Account(jid.asBareJid(), password); - mAccount.setPort(numericPort); - mAccount.setHostname(hostname); - mAccount.setOption(Account.OPTION_USETLS, true); - mAccount.setOption(Account.OPTION_USECOMPRESSION, true); - mAccount.setOption(Account.OPTION_REGISTER, registerNewAccount); - xmppConnectionService.createAccount(mAccount); - } - binding.hostnameLayout.setError(null); - binding.portLayout.setError(null); - if (mAccount.isEnabled() - && !registerNewAccount - && !mInitMode) { - finish(); - } else { - updateSaveButton(); - updateAccountInformation(true); - } - - } - }; - private final OnClickListener mCancelButtonClickListener = v -> { - deleteAccountAndReturnIfNecessary(); - finish(); - }; - private Toast mFetchingMamPrefsToast; - private String mSavedInstanceAccount; - private boolean mSavedInstanceInit = false; - private XmppUri pendingUri = null; - private boolean mUseTor; - private ActivityEditAccountBinding binding; - - public void refreshUiReal() { - invalidateOptionsMenu(); - if (mAccount != null - && mAccount.getStatus() != Account.State.ONLINE - && mFetchingAvatar) { - Intent intent = new Intent(this, StartConversationActivity.class); - StartConversationActivity.addInviteUri(intent, getIntent()); - startActivity(intent); - finish(); - } else if (mInitMode && mAccount != null && mAccount.getStatus() == Account.State.ONLINE) { - if (!mFetchingAvatar) { - mFetchingAvatar = true; - xmppConnectionService.checkForAvatar(mAccount, mAvatarFetchCallback); - } - } - if (mAccount != null) { - updateAccountInformation(false); - } - updateSaveButton(); - } - - @Override - public boolean onNavigateUp() { - deleteAccountAndReturnIfNecessary(); - return super.onNavigateUp(); - } - - @Override - public void onBackPressed() { - deleteAccountAndReturnIfNecessary(); - super.onBackPressed(); - } - - private void deleteAccountAndReturnIfNecessary() { - if (mInitMode && mAccount != null && !mAccount.isOptionSet(Account.OPTION_LOGGED_IN_SUCCESSFULLY)) { - xmppConnectionService.deleteAccount(mAccount); - } - - if (xmppConnectionService.getAccounts().size() == 0 && Config.MAGIC_CREATE_DOMAIN != null) { - Intent intent = SignupUtils.getSignUpIntent(this); - startActivity(intent); - } - } - - @Override - public void onAccountUpdate() { - refreshUi(); - } - - private final UiCallback mAvatarFetchCallback = new UiCallback() { - - @Override - public void userInputRequried(final PendingIntent pi, final Avatar avatar) { - finishInitialSetup(avatar); - } - - @Override - public void success(final Avatar avatar) { - finishInitialSetup(avatar); - } - - @Override - public void error(final int errorCode, final Avatar avatar) { - finishInitialSetup(avatar); - } - }; - private final TextWatcher mTextWatcher = new TextWatcher() { - - @Override - public void onTextChanged(final CharSequence s, final int start, final int before, final int count) { - updatePortLayout(); - updateSaveButton(); - } - - @Override - public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) { - } - - @Override - public void afterTextChanged(final Editable s) { - - } - }; - - private View.OnFocusChangeListener mEditTextFocusListener = new View.OnFocusChangeListener() { - @Override - public void onFocusChange(View view, boolean b) { - EditText et = (EditText) view; - if (b) { - int resId = mUsernameMode ? R.string.username : R.string.account_settings_example_jabber_id; - if (view.getId() == R.id.hostname) { - resId = mUseTor ? R.string.hostname_or_onion : R.string.hostname_example; - } - final int res = resId; - new Handler().postDelayed(() -> et.setHint(res), 200); - } else { - et.setHint(null); - } - } - }; - - - private final OnClickListener mAvatarClickListener = new OnClickListener() { - @Override - public void onClick(final View view) { - if (mAccount != null) { - final Intent intent = new Intent(getApplicationContext(), PublishProfilePictureActivity.class); - intent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().asBareJid().toString()); - startActivity(intent); - } - } - }; - - protected void finishInitialSetup(final Avatar avatar) { - runOnUiThread(() -> { - SoftKeyboardUtils.hideSoftKeyboard(EditAccountActivity.this); - final Intent intent; - final XmppConnection connection = mAccount.getXmppConnection(); - final boolean wasFirstAccount = xmppConnectionService != null && xmppConnectionService.getAccounts().size() == 1; - if (avatar != null || (connection != null && !connection.getFeatures().pep())) { - intent = new Intent(getApplicationContext(), StartConversationActivity.class); - if (wasFirstAccount) { - intent.putExtra("init", true); - } - } else { - intent = new Intent(getApplicationContext(), PublishProfilePictureActivity.class); - intent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().asBareJid().toString()); - intent.putExtra("setup", true); - } - if (wasFirstAccount) { - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); - } - StartConversationActivity.addInviteUri(intent, getIntent()); - startActivity(intent); - finish(); - }); - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (requestCode == REQUEST_BATTERY_OP || requestCode == REQUEST_DATA_SAVER) { - updateAccountInformation(mAccount == null); - } - if (requestCode == REQUEST_CHANGE_STATUS) { - PresenceTemplate template = mPendingPresenceTemplate.pop(); - if (template != null && resultCode == Activity.RESULT_OK) { - generateSignature(data, template); - } else { - Log.d(Config.LOGTAG, "pgp result not ok"); - } - } - } - - @Override - protected void processFingerprintVerification(XmppUri uri) { - processFingerprintVerification(uri, true); - } - - - protected void processFingerprintVerification(XmppUri uri, boolean showWarningToast) { - if (mAccount != null && mAccount.getJid().asBareJid().equals(uri.getJid()) && uri.hasFingerprints()) { - if (xmppConnectionService.verifyFingerprints(mAccount, uri.getFingerprints())) { - Toast.makeText(this, R.string.verified_fingerprints, Toast.LENGTH_SHORT).show(); - updateAccountInformation(false); - } - } else if (showWarningToast) { - Toast.makeText(this, R.string.invalid_barcode, Toast.LENGTH_SHORT).show(); - } - } - - private void updatePortLayout() { - String hostname = this.binding.hostname.getText().toString(); - this.binding.portLayout.setEnabled(!TextUtils.isEmpty(hostname)); - } - - protected void updateSaveButton() { - boolean accountInfoEdited = accountInfoEdited(); - - if (!mInitMode && passwordChangedInMagicCreateMode()) { - this.binding.saveButton.setText(R.string.change_password); - this.binding.saveButton.setEnabled(true); - } else if (accountInfoEdited && !mInitMode) { - this.binding.saveButton.setText(R.string.save); - this.binding.saveButton.setEnabled(true); - } else if (mAccount != null - && (mAccount.getStatus() == Account.State.CONNECTING || mAccount.getStatus() == Account.State.REGISTRATION_SUCCESSFUL || mFetchingAvatar)) { - this.binding.saveButton.setEnabled(false); - this.binding.saveButton.setText(R.string.account_status_connecting); - } else if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED && !mInitMode) { - this.binding.saveButton.setEnabled(true); - this.binding.saveButton.setText(R.string.enable); - } else { - this.binding.saveButton.setEnabled(true); - if (!mInitMode) { - if (mAccount != null && mAccount.isOnlineAndConnected()) { - this.binding.saveButton.setText(R.string.save); - if (!accountInfoEdited) { - this.binding.saveButton.setEnabled(false); - } - } else { - XmppConnection connection = mAccount == null ? null : mAccount.getXmppConnection(); - URL url = connection != null && mAccount.getStatus() == Account.State.PAYMENT_REQUIRED ? connection.getRedirectionUrl() : null; - if (url != null) { - this.binding.saveButton.setText(R.string.open_website); - } else { - this.binding.saveButton.setText(R.string.connect); - } - } - } else { - XmppConnection connection = mAccount == null ? null : mAccount.getXmppConnection(); - URL url = connection != null && mAccount.getStatus() == Account.State.REGISTRATION_WEB ? connection.getRedirectionUrl() : null; - if (url != null && this.binding.accountRegisterNew.isChecked() && !accountInfoEdited) { - this.binding.saveButton.setText(R.string.open_website); - } else { - this.binding.saveButton.setText(R.string.next); - } - } - } - } - - protected boolean accountInfoEdited() { - if (this.mAccount == null) { - return false; - } - return jidEdited() || - !this.mAccount.getPassword().equals(this.binding.accountPassword.getText().toString()) || - !this.mAccount.getHostname().equals(this.binding.hostname.getText().toString()) || - !String.valueOf(this.mAccount.getPort()).equals(this.binding.port.getText().toString()); - } - - protected boolean jidEdited() { - final String unmodified; - if (mUsernameMode) { - unmodified = this.mAccount.getJid().getLocal(); - } else { - unmodified = this.mAccount.getJid().asBareJid().toString(); - } - return !unmodified.equals(this.binding.accountJid.getText().toString()); - } - - protected boolean passwordChangedInMagicCreateMode() { - return mAccount != null - && mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE) - && !this.mAccount.getPassword().equals(this.binding.accountPassword.getText().toString()) - && !this.jidEdited() - && mAccount.isOnlineAndConnected(); - } - - @Override - protected String getShareableUri(boolean http) { - if (mAccount != null) { - return http ? mAccount.getShareableLink() : mAccount.getShareableUri(); - } else { - return null; - } - } - - @Override - protected void onCreate(final Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (savedInstanceState != null) { - this.mSavedInstanceAccount = savedInstanceState.getString("account"); - this.mSavedInstanceInit = savedInstanceState.getBoolean("initMode", false); - } - this.binding = DataBindingUtil.setContentView(this, R.layout.activity_edit_account); - setSupportActionBar((Toolbar) binding.toolbar); - binding.accountJid.addTextChangedListener(this.mTextWatcher); - binding.accountJid.setOnFocusChangeListener(this.mEditTextFocusListener); - this.binding.accountPassword.addTextChangedListener(this.mTextWatcher); - - this.binding.avater.setOnClickListener(this.mAvatarClickListener); - this.binding.hostname.addTextChangedListener(mTextWatcher); - this.binding.hostname.setOnFocusChangeListener(mEditTextFocusListener); - this.binding.clearDevices.setOnClickListener(v -> showWipePepDialog()); - this.binding.port.setText("5222"); - this.binding.port.addTextChangedListener(mTextWatcher); - this.binding.saveButton.setOnClickListener(this.mSaveButtonClickListener); - this.binding.cancelButton.setOnClickListener(this.mCancelButtonClickListener); - if (savedInstanceState != null && savedInstanceState.getBoolean("showMoreTable")) { - changeMoreTableVisibility(true); - } - final OnCheckedChangeListener OnCheckedShowConfirmPassword = (buttonView, isChecked) -> updateSaveButton(); - this.binding.accountRegisterNew.setOnCheckedChangeListener(OnCheckedShowConfirmPassword); - if (Config.DISALLOW_REGISTRATION_IN_UI) { - this.binding.accountRegisterNew.setVisibility(View.GONE); - } - } - - @Override - public boolean onCreateOptionsMenu(final Menu menu) { - super.onCreateOptionsMenu(menu); - getMenuInflater().inflate(R.menu.editaccount, menu); - final MenuItem showBlocklist = menu.findItem(R.id.action_show_block_list); - final MenuItem showMoreInfo = menu.findItem(R.id.action_server_info_show_more); - final MenuItem changePassword = menu.findItem(R.id.action_change_password_on_server); - final MenuItem renewCertificate = menu.findItem(R.id.action_renew_certificate); - final MenuItem mamPrefs = menu.findItem(R.id.action_mam_prefs); - final MenuItem changePresence = menu.findItem(R.id.action_change_presence); - final MenuItem share = menu.findItem(R.id.action_share); - renewCertificate.setVisible(mAccount != null && mAccount.getPrivateKeyAlias() != null); - - share.setVisible(mAccount != null && !mInitMode); - - if (mAccount != null && mAccount.isOnlineAndConnected()) { - if (!mAccount.getXmppConnection().getFeatures().blocking()) { - showBlocklist.setVisible(false); - } - - if (!mAccount.getXmppConnection().getFeatures().register()) { - changePassword.setVisible(false); - } - mamPrefs.setVisible(mAccount.getXmppConnection().getFeatures().mam()); - changePresence.setVisible(!mInitMode); - } else { - showBlocklist.setVisible(false); - showMoreInfo.setVisible(false); - changePassword.setVisible(false); - mamPrefs.setVisible(false); - changePresence.setVisible(false); - } - return super.onCreateOptionsMenu(menu); - } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - final MenuItem showMoreInfo = menu.findItem(R.id.action_server_info_show_more); - if (showMoreInfo.isVisible()) { - showMoreInfo.setChecked(binding.serverInfoMore.getVisibility() == View.VISIBLE); - } - return super.onPrepareOptionsMenu(menu); - } - - @Override - protected void onStart() { - super.onStart(); - final Intent intent = getIntent(); - final int theme = findTheme(); - if (this.mTheme != theme) { - recreate(); - } else if (intent != null) { - try { - this.jidToEdit = Jid.of(intent.getStringExtra("jid")); - } catch (final IllegalArgumentException | NullPointerException ignored) { - this.jidToEdit = null; - } - if (jidToEdit != null && intent.getData() != null && intent.getBooleanExtra("scanned", false)) { - final XmppUri uri = new XmppUri(intent.getData()); - if (xmppConnectionServiceBound) { - processFingerprintVerification(uri, false); - } else { - this.pendingUri = uri; - } - } - boolean init = intent.getBooleanExtra("init", false); - boolean openedFromNotification = intent.getBooleanExtra(EXTRA_OPENED_FROM_NOTIFICATION, false); - this.mInitMode = init || this.jidToEdit == null; - this.messageFingerprint = intent.getStringExtra("fingerprint"); - if (!mInitMode) { - this.binding.accountRegisterNew.setVisibility(View.GONE); - setTitle(getString(R.string.account_details)); - configureActionBar(getSupportActionBar(), !openedFromNotification); - } else { - this.binding.avater.setVisibility(View.GONE); - configureActionBar(getSupportActionBar(), !(init && Config.MAGIC_CREATE_DOMAIN == null)); - setTitle(R.string.action_add_account); - } - } - SharedPreferences preferences = getPreferences(); - mUseTor = QuickConversationsService.isConversations() && preferences.getBoolean("use_tor", getResources().getBoolean(R.bool.use_tor)); - this.mShowOptions = mUseTor || (QuickConversationsService.isConversations() && preferences.getBoolean("show_connection_options", getResources().getBoolean(R.bool.show_connection_options))); - this.binding.namePort.setVisibility(mShowOptions ? View.VISIBLE : View.GONE); - } - - @Override - public void onNewIntent(Intent intent) { - if (intent != null && intent.getData() != null) { - final XmppUri uri = new XmppUri(intent.getData()); - if (xmppConnectionServiceBound) { - processFingerprintVerification(uri, false); - } else { - this.pendingUri = uri; - } - } - } - - @Override - public void onSaveInstanceState(final Bundle savedInstanceState) { - if (mAccount != null) { - savedInstanceState.putString("account", mAccount.getJid().asBareJid().toString()); - savedInstanceState.putBoolean("initMode", mInitMode); - savedInstanceState.putBoolean("showMoreTable", binding.serverInfoMore.getVisibility() == View.VISIBLE); - } - super.onSaveInstanceState(savedInstanceState); - } - - protected void onBackendConnected() { - boolean init = true; - if (mSavedInstanceAccount != null) { - try { - this.mAccount = xmppConnectionService.findAccountByJid(Jid.of(mSavedInstanceAccount)); - this.mInitMode = mSavedInstanceInit; - init = false; - } catch (IllegalArgumentException e) { - this.mAccount = null; - } - - } else if (this.jidToEdit != null) { - this.mAccount = xmppConnectionService.findAccountByJid(jidToEdit); - } - - if (mAccount != null) { - this.mInitMode |= this.mAccount.isOptionSet(Account.OPTION_REGISTER); - this.mUsernameMode |= mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE) && mAccount.isOptionSet(Account.OPTION_REGISTER); - if (this.mAccount.getPrivateKeyAlias() != null) { - this.binding.accountPassword.setHint(R.string.authenticate_with_certificate); - if (this.mInitMode) { - this.binding.accountPassword.requestFocus(); - } - } - if (mPendingFingerprintVerificationUri != null) { - processFingerprintVerification(mPendingFingerprintVerificationUri, false); - mPendingFingerprintVerificationUri = null; - } - updateAccountInformation(init); - } - - - if (Config.MAGIC_CREATE_DOMAIN == null && this.xmppConnectionService.getAccounts().size() == 0) { - this.binding.cancelButton.setEnabled(false); - } - if (mUsernameMode) { - this.binding.accountJidLayout.setHint(getString(R.string.username_hint)); - this.binding.accountJid.setHint(R.string.username_hint); - } else { - final KnownHostsAdapter mKnownHostsAdapter = new KnownHostsAdapter(this, - R.layout.simple_list_item, - xmppConnectionService.getKnownHosts()); - this.binding.accountJid.setAdapter(mKnownHostsAdapter); - } - - if (pendingUri != null) { - processFingerprintVerification(pendingUri, false); - pendingUri = null; - } - updatePortLayout(); - updateSaveButton(); - invalidateOptionsMenu(); - } - - private String getUserModeDomain() { - if (mAccount != null && mAccount.getJid().getDomain() != null) { - return mAccount.getJid().getDomain(); - } else { - return Config.DOMAIN_LOCK; - } - } - - @Override - public boolean onOptionsItemSelected(final MenuItem item) { - if (MenuDoubleTabUtil.shouldIgnoreTap()) { - return false; - } - switch (item.getItemId()) { - case R.id.action_show_block_list: - final Intent showBlocklistIntent = new Intent(this, BlocklistActivity.class); - showBlocklistIntent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toString()); - startActivity(showBlocklistIntent); - break; - case R.id.action_server_info_show_more: - changeMoreTableVisibility(!item.isChecked()); - break; - case R.id.action_share_barcode: - shareBarcode(); - break; - case R.id.action_share_http: - shareLink(true); - break; - case R.id.action_share_uri: - shareLink(false); - break; - case R.id.action_change_password_on_server: - gotoChangePassword(null); - break; - case R.id.action_mam_prefs: - editMamPrefs(); - break; - case R.id.action_renew_certificate: - renewCertificate(); - break; - case R.id.action_change_presence: - changePresence(); - break; - } - return super.onOptionsItemSelected(item); - } - - private void shareBarcode() { - Intent intent = new Intent(Intent.ACTION_SEND); - intent.putExtra(Intent.EXTRA_STREAM, BarcodeProvider.getUriForAccount(this, mAccount)); - intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - intent.setType("image/png"); - startActivity(Intent.createChooser(intent, getText(R.string.share_with))); - } - - private void changeMoreTableVisibility(boolean visible) { - binding.serverInfoMore.setVisibility(visible ? View.VISIBLE : View.GONE); - } - - private void gotoChangePassword(String newPassword) { - final Intent changePasswordIntent = new Intent(this, ChangePasswordActivity.class); - changePasswordIntent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toString()); - if (newPassword != null) { - changePasswordIntent.putExtra("password", newPassword); - } - startActivity(changePasswordIntent); - } - - private void renewCertificate() { - KeyChain.choosePrivateKeyAlias(this, this, null, null, null, -1, null); - } - - private void changePresence() { - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); - boolean manualStatus = sharedPreferences.getBoolean(SettingsActivity.MANUALLY_CHANGE_PRESENCE, getResources().getBoolean(R.bool.manually_change_presence)); - AlertDialog.Builder builder = new AlertDialog.Builder(this); - final DialogPresenceBinding binding = DataBindingUtil.inflate(getLayoutInflater(), R.layout.dialog_presence, null, false); - String current = mAccount.getPresenceStatusMessage(); - if (current != null && !current.trim().isEmpty()) { - binding.statusMessage.append(current); - } - setAvailabilityRadioButton(mAccount.getPresenceStatus(), binding); - binding.show.setVisibility(manualStatus ? View.VISIBLE : View.GONE); - List templates = xmppConnectionService.getPresenceTemplates(mAccount); - PresenceTemplateAdapter presenceTemplateAdapter = new PresenceTemplateAdapter(this, R.layout.simple_list_item, templates); - binding.statusMessage.setAdapter(presenceTemplateAdapter); - binding.statusMessage.setOnItemClickListener((parent, view, position, id) -> { - PresenceTemplate template = (PresenceTemplate) parent.getItemAtPosition(position); - setAvailabilityRadioButton(template.getStatus(), binding); - }); - builder.setTitle(R.string.edit_status_message_title); - builder.setView(binding.getRoot()); - builder.setNegativeButton(R.string.cancel, null); - builder.setPositiveButton(R.string.confirm, (dialog, which) -> { - PresenceTemplate template = new PresenceTemplate(getAvailabilityRadioButton(binding), binding.statusMessage.getText().toString().trim()); - if (mAccount.getPgpId() != 0 && hasPgp()) { - generateSignature(null, template); - } else { - xmppConnectionService.changeStatus(mAccount, template, null); - } - }); - builder.create().show(); - } - - private void generateSignature(Intent intent, PresenceTemplate template) { - xmppConnectionService.getPgpEngine().generateSignature(intent, mAccount, template.getStatusMessage(), new UiCallback() { - @Override - public void success(String signature) { - xmppConnectionService.changeStatus(mAccount, template, signature); - } - - @Override - public void error(int errorCode, String object) { - - } - - @Override - public void userInputRequried(PendingIntent pi, String object) { - mPendingPresenceTemplate.push(template); - try { - startIntentSenderForResult(pi.getIntentSender(), REQUEST_CHANGE_STATUS, null, 0, 0, 0); - } catch (final IntentSender.SendIntentException ignored) { - } - } - }); - } - - private static void setAvailabilityRadioButton(Presence.Status status, DialogPresenceBinding binding) { - if (status == null) { - binding.online.setChecked(true); - return; - } - switch (status) { - case DND: - binding.dnd.setChecked(true); - break; - case XA: - binding.xa.setChecked(true); - break; - case AWAY: - binding.xa.setChecked(true); - break; - default: - binding.online.setChecked(true); - } - } - - private static Presence.Status getAvailabilityRadioButton(DialogPresenceBinding binding) { - if (binding.dnd.isChecked()) { - return Presence.Status.DND; - } else if (binding.xa.isChecked()) { - return Presence.Status.XA; - } else if (binding.away.isChecked()) { - return Presence.Status.AWAY; - } else { - return Presence.Status.ONLINE; - } - } - - @Override - public void alias(String alias) { - if (alias != null) { - xmppConnectionService.updateKeyInAccount(mAccount, alias); - } - } - - private void updateAccountInformation(boolean init) { - if (init) { - this.binding.accountJid.getEditableText().clear(); - if (mUsernameMode) { - this.binding.accountJid.getEditableText().append(this.mAccount.getJid().getLocal()); - } else { - this.binding.accountJid.getEditableText().append(this.mAccount.getJid().asBareJid().toString()); - } - this.binding.accountPassword.getEditableText().clear(); - this.binding.accountPassword.getEditableText().append(this.mAccount.getPassword()); - this.binding.hostname.setText(""); - this.binding.hostname.getEditableText().append(this.mAccount.getHostname()); - this.binding.port.setText(""); - this.binding.port.getEditableText().append(String.valueOf(this.mAccount.getPort())); - this.binding.namePort.setVisibility(mShowOptions ? View.VISIBLE : View.GONE); - - } - - final boolean editable = !mAccount.isOptionSet(Account.OPTION_LOGGED_IN_SUCCESSFULLY) && QuickConversationsService.isConversations(); - this.binding.accountJid.setEnabled(editable); - this.binding.accountJid.setFocusable(editable); - this.binding.accountJid.setFocusableInTouchMode(editable); - - - if (mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE) || !mAccount.isOptionSet(Account.OPTION_LOGGED_IN_SUCCESSFULLY)) { - this.binding.accountPasswordLayout.setPasswordVisibilityToggleEnabled(true); - } else { - this.binding.accountPasswordLayout.setPasswordVisibilityToggleEnabled(false); - } - - if (!mInitMode) { - this.binding.avater.setVisibility(View.VISIBLE); - this.binding.avater.setImageBitmap(avatarService().get(this.mAccount, (int) getResources().getDimension(R.dimen.avatar_on_details_screen_size))); - } else { - this.binding.avater.setVisibility(View.GONE); - } - this.binding.accountRegisterNew.setChecked(this.mAccount.isOptionSet(Account.OPTION_REGISTER)); - if (this.mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)) { - if (this.mAccount.isOptionSet(Account.OPTION_REGISTER)) { - ActionBar actionBar = getSupportActionBar(); - if (actionBar != null) { - actionBar.setTitle(R.string.create_account); - } - } - this.binding.accountRegisterNew.setVisibility(View.GONE); - } else if (this.mAccount.isOptionSet(Account.OPTION_REGISTER)) { - this.binding.accountRegisterNew.setVisibility(View.VISIBLE); - } else { - this.binding.accountRegisterNew.setVisibility(View.GONE); - } - if (this.mAccount.isOnlineAndConnected() && !this.mFetchingAvatar) { - Features features = this.mAccount.getXmppConnection().getFeatures(); - this.binding.stats.setVisibility(View.VISIBLE); - boolean showBatteryWarning = !xmppConnectionService.getPushManagementService().available(mAccount) && isOptimizingBattery(); - boolean showDataSaverWarning = isAffectedByDataSaver(); - showOsOptimizationWarning(showBatteryWarning, showDataSaverWarning); - this.binding.sessionEst.setText(UIHelper.readableTimeDifferenceFull(this, this.mAccount.getXmppConnection() - .getLastSessionEstablished())); - if (features.rosterVersioning()) { - this.binding.serverInfoRosterVersion.setText(R.string.server_info_available); - } else { - this.binding.serverInfoRosterVersion.setText(R.string.server_info_unavailable); - } - if (features.carbons()) { - this.binding.serverInfoCarbons.setText(R.string.server_info_available); - } else { - this.binding.serverInfoCarbons.setText(R.string.server_info_unavailable); - } - if (features.mam()) { - this.binding.serverInfoMam.setText(R.string.server_info_available); - } else { - this.binding.serverInfoMam.setText(R.string.server_info_unavailable); - } - if (features.csi()) { - this.binding.serverInfoCsi.setText(R.string.server_info_available); - } else { - this.binding.serverInfoCsi.setText(R.string.server_info_unavailable); - } - if (features.blocking()) { - this.binding.serverInfoBlocking.setText(R.string.server_info_available); - } else { - this.binding.serverInfoBlocking.setText(R.string.server_info_unavailable); - } - if (features.sm()) { - this.binding.serverInfoSm.setText(R.string.server_info_available); - } else { - this.binding.serverInfoSm.setText(R.string.server_info_unavailable); - } - if (features.pep()) { - AxolotlService axolotlService = this.mAccount.getAxolotlService(); - if (axolotlService != null && axolotlService.isPepBroken()) { - this.binding.serverInfoPep.setText(R.string.server_info_broken); - } else if (features.pepPublishOptions() || features.pepOmemoWhitelisted()) { - this.binding.serverInfoPep.setText(R.string.server_info_available); - } else { - this.binding.serverInfoPep.setText(R.string.server_info_partial); - } - } else { - this.binding.serverInfoPep.setText(R.string.server_info_unavailable); - } - if (features.httpUpload(0)) { - this.binding.serverInfoHttpUpload.setText(R.string.server_info_available); - } else if (features.p1S3FileTransfer()) { - this.binding.serverInfoHttpUploadDescription.setText(R.string.p1_s3_filetransfer); - this.binding.serverInfoHttpUpload.setText(R.string.server_info_available); - } else { - this.binding.serverInfoHttpUpload.setText(R.string.server_info_unavailable); - } - - this.binding.pushRow.setVisibility(xmppConnectionService.getPushManagementService().isStub() ? View.GONE : View.VISIBLE); - - if (xmppConnectionService.getPushManagementService().available(mAccount)) { - this.binding.serverInfoPush.setText(R.string.server_info_available); - } else { - this.binding.serverInfoPush.setText(R.string.server_info_unavailable); - } - final long pgpKeyId = this.mAccount.getPgpId(); - if (pgpKeyId != 0 && Config.supportOpenPgp()) { - OnClickListener openPgp = view -> launchOpenKeyChain(pgpKeyId); - OnClickListener delete = view -> showDeletePgpDialog(); - this.binding.pgpFingerprintBox.setVisibility(View.VISIBLE); - this.binding.pgpFingerprint.setText(OpenPgpUtils.convertKeyIdToHex(pgpKeyId)); - this.binding.pgpFingerprint.setOnClickListener(openPgp); - if ("pgp".equals(messageFingerprint)) { - this.binding.pgpFingerprintDesc.setTextAppearance(this, R.style.TextAppearance_Conversations_Caption_Highlight); - } - this.binding.pgpFingerprintDesc.setOnClickListener(openPgp); - this.binding.actionDeletePgp.setOnClickListener(delete); - } else { - this.binding.pgpFingerprintBox.setVisibility(View.GONE); - } - final String ownAxolotlFingerprint = this.mAccount.getAxolotlService().getOwnFingerprint(); - if (ownAxolotlFingerprint != null && Config.supportOmemo()) { - this.binding.axolotlFingerprintBox.setVisibility(View.VISIBLE); - if (ownAxolotlFingerprint.equals(messageFingerprint)) { - this.binding.ownFingerprintDesc.setTextAppearance(this, R.style.TextAppearance_Conversations_Caption_Highlight); - this.binding.ownFingerprintDesc.setText(R.string.omemo_fingerprint_selected_message); - } else { - this.binding.ownFingerprintDesc.setTextAppearance(this, R.style.TextAppearance_Conversations_Caption); - this.binding.ownFingerprintDesc.setText(R.string.omemo_fingerprint); - } - this.binding.axolotlFingerprint.setText(CryptoHelper.prettifyFingerprint(ownAxolotlFingerprint.substring(2))); - this.binding.actionCopyAxolotlToClipboard.setVisibility(View.VISIBLE); - this.binding.actionCopyAxolotlToClipboard.setOnClickListener(v -> copyOmemoFingerprint(ownAxolotlFingerprint)); - } else { - this.binding.axolotlFingerprintBox.setVisibility(View.GONE); - } - boolean hasKeys = false; - binding.otherDeviceKeys.removeAllViews(); - for (XmppAxolotlSession session : mAccount.getAxolotlService().findOwnSessions()) { - if (!session.getTrust().isCompromised()) { - boolean highlight = session.getFingerprint().equals(messageFingerprint); - addFingerprintRow(binding.otherDeviceKeys, session, highlight); - hasKeys = true; - } - } - if (hasKeys && Config.supportOmemo()) { //TODO: either the button should be visible if we print an active device or the device list should be fed with reactived devices - this.binding.otherDeviceKeysCard.setVisibility(View.VISIBLE); - Set otherDevices = mAccount.getAxolotlService().getOwnDeviceIds(); - if (otherDevices == null || otherDevices.isEmpty()) { - binding.clearDevices.setVisibility(View.GONE); - } else { - binding.clearDevices.setVisibility(View.VISIBLE); - } - } else { - this.binding.otherDeviceKeysCard.setVisibility(View.GONE); - } - } else { - final TextInputLayout errorLayout; - if (this.mAccount.errorStatus()) { - if (this.mAccount.getStatus() == Account.State.UNAUTHORIZED) { - errorLayout = this.binding.accountPasswordLayout; - } else if (mShowOptions - && this.mAccount.getStatus() == Account.State.SERVER_NOT_FOUND - && this.binding.hostname.getText().length() > 0) { - errorLayout = this.binding.hostnameLayout; - } else { - errorLayout = this.binding.accountJidLayout; - } - errorLayout.setError(getString(this.mAccount.getStatus().getReadableId())); - if (init || !accountInfoEdited()) { - errorLayout.requestFocus(); - } - } else { - errorLayout = null; - } - removeErrorsOnAllBut(errorLayout); - this.binding.stats.setVisibility(View.GONE); - this.binding.otherDeviceKeysCard.setVisibility(View.GONE); - } - } - - private void removeErrorsOnAllBut(TextInputLayout exception) { - if (this.binding.accountJidLayout != exception) { - this.binding.accountJidLayout.setErrorEnabled(false); - this.binding.accountJidLayout.setError(null); - } - if (this.binding.accountPasswordLayout != exception) { - this.binding.accountPasswordLayout.setErrorEnabled(false); - this.binding.accountPasswordLayout.setError(null); - } - if (this.binding.hostnameLayout != exception) { - this.binding.hostnameLayout.setErrorEnabled(false); - this.binding.hostnameLayout.setError(null); - } - if (this.binding.portLayout != exception) { - this.binding.portLayout.setErrorEnabled(false); - this.binding.portLayout.setError(null); - } - } - - private void showDeletePgpDialog() { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(R.string.unpublish_pgp); - builder.setMessage(R.string.unpublish_pgp_message); - builder.setNegativeButton(R.string.cancel, null); - builder.setPositiveButton(R.string.confirm, (dialogInterface, i) -> { - mAccount.setPgpSignId(0); - mAccount.unsetPgpSignature(); - xmppConnectionService.databaseBackend.updateAccount(mAccount); - xmppConnectionService.sendPresence(mAccount); - refreshUiReal(); - }); - builder.create().show(); - } - - private void showOsOptimizationWarning(boolean showBatteryWarning, boolean showDataSaverWarning) { - this.binding.osOptimization.setVisibility(showBatteryWarning || showDataSaverWarning ? View.VISIBLE : View.GONE); - if (showDataSaverWarning) { - this.binding.osOptimizationHeadline.setText(R.string.data_saver_enabled); - this.binding.osOptimizationBody.setText(R.string.data_saver_enabled_explained); - this.binding.osOptimizationDisable.setText(R.string.allow); - this.binding.osOptimizationDisable.setOnClickListener(v -> { - Intent intent = new Intent(Settings.ACTION_IGNORE_BACKGROUND_DATA_RESTRICTIONS_SETTINGS); - Uri uri = Uri.parse("package:" + getPackageName()); - intent.setData(uri); - try { - startActivityForResult(intent, REQUEST_DATA_SAVER); - } catch (ActivityNotFoundException e) { - Toast.makeText(EditAccountActivity.this, R.string.device_does_not_support_data_saver, Toast.LENGTH_SHORT).show(); - } - }); - } else if (showBatteryWarning) { - this.binding.osOptimizationDisable.setText(R.string.disable); - this.binding.osOptimizationHeadline.setText(R.string.battery_optimizations_enabled); - this.binding.osOptimizationBody.setText(R.string.battery_optimizations_enabled_explained); - this.binding.osOptimizationDisable.setOnClickListener(v -> { - Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); - Uri uri = Uri.parse("package:" + getPackageName()); - intent.setData(uri); - try { - startActivityForResult(intent, REQUEST_BATTERY_OP); - } catch (ActivityNotFoundException e) { - Toast.makeText(EditAccountActivity.this, R.string.device_does_not_support_battery_op, Toast.LENGTH_SHORT).show(); - } - }); - } - } - - public void showWipePepDialog() { - Builder builder = new Builder(this); - builder.setTitle(getString(R.string.clear_other_devices)); - builder.setIconAttribute(android.R.attr.alertDialogIcon); - builder.setMessage(getString(R.string.clear_other_devices_desc)); - builder.setNegativeButton(getString(R.string.cancel), null); - builder.setPositiveButton(getString(R.string.accept), - (dialog, which) -> mAccount.getAxolotlService().wipeOtherPepDevices()); - builder.create().show(); - } - - private void editMamPrefs() { - this.mFetchingMamPrefsToast = Toast.makeText(this, R.string.fetching_mam_prefs, Toast.LENGTH_LONG); - this.mFetchingMamPrefsToast.show(); - xmppConnectionService.fetchMamPreferences(mAccount, this); - } - - @Override - public void onKeyStatusUpdated(AxolotlService.FetchStatus report) { - refreshUi(); - } - - @Override - public void onCaptchaRequested(final Account account, final String id, final Data data, final Bitmap captcha) { - runOnUiThread(() -> { - if (mCaptchaDialog != null && mCaptchaDialog.isShowing()) { - mCaptchaDialog.dismiss(); - } - final Builder builder = new Builder(EditAccountActivity.this); - final View view = getLayoutInflater().inflate(R.layout.captcha, null); - final ImageView imageView = view.findViewById(R.id.captcha); - final EditText input = view.findViewById(R.id.input); - imageView.setImageBitmap(captcha); - - builder.setTitle(getString(R.string.captcha_required)); - builder.setView(view); - - builder.setPositiveButton(getString(R.string.ok), - (dialog, which) -> { - String rc = input.getText().toString(); - data.put("username", account.getUsername()); - data.put("password", account.getPassword()); - data.put("ocr", rc); - data.submit(); - - if (xmppConnectionServiceBound) { - xmppConnectionService.sendCreateAccountWithCaptchaPacket(account, id, data); - } - }); - builder.setNegativeButton(getString(R.string.cancel), (dialog, which) -> { - if (xmppConnectionService != null) { - xmppConnectionService.sendCreateAccountWithCaptchaPacket(account, null, null); - } - }); - - builder.setOnCancelListener(dialog -> { - if (xmppConnectionService != null) { - xmppConnectionService.sendCreateAccountWithCaptchaPacket(account, null, null); - } - }); - mCaptchaDialog = builder.create(); - mCaptchaDialog.show(); - input.requestFocus(); - }); - } - - public void onShowErrorToast(final int resId) { - runOnUiThread(() -> Toast.makeText(EditAccountActivity.this, resId, Toast.LENGTH_SHORT).show()); - } - - @Override - public void onPreferencesFetched(final Element prefs) { - runOnUiThread(() -> { - if (mFetchingMamPrefsToast != null) { - mFetchingMamPrefsToast.cancel(); - } - Builder builder = new Builder(EditAccountActivity.this); - builder.setTitle(R.string.server_side_mam_prefs); - String defaultAttr = prefs.getAttribute("default"); - final List defaults = Arrays.asList("never", "roster", "always"); - final AtomicInteger choice = new AtomicInteger(Math.max(0, defaults.indexOf(defaultAttr))); - builder.setSingleChoiceItems(R.array.mam_prefs, choice.get(), (dialog, which) -> choice.set(which)); - builder.setNegativeButton(R.string.cancel, null); - builder.setPositiveButton(R.string.ok, (dialog, which) -> { - prefs.setAttribute("default", defaults.get(choice.get())); - xmppConnectionService.pushMamPreferences(mAccount, prefs); - }); - builder.create().show(); - }); - } - - @Override - public void onPreferencesFetchFailed() { - runOnUiThread(() -> { - if (mFetchingMamPrefsToast != null) { - mFetchingMamPrefsToast.cancel(); - } - Toast.makeText(EditAccountActivity.this, R.string.unable_to_fetch_mam_prefs, Toast.LENGTH_LONG).show(); - }); - } - - @Override - public void OnUpdateBlocklist(Status status) { - refreshUi(); - } + OnKeyStatusUpdated, OnCaptchaRequested, KeyChainAliasCallback, XmppConnectionService.OnShowErrorToast, XmppConnectionService.OnMamPreferencesFetched { + + public static final String EXTRA_OPENED_FROM_NOTIFICATION = "opened_from_notification"; + + private static final int REQUEST_DATA_SAVER = 0xf244; + private static final int REQUEST_CHANGE_STATUS = 0xee11; + private final PendingItem mPendingPresenceTemplate = new PendingItem<>(); + private AlertDialog mCaptchaDialog = null; + private Jid jidToEdit; + private boolean mInitMode = false; + private boolean mUsernameMode = Config.DOMAIN_LOCK != null; + private boolean mShowOptions = false; + private Account mAccount; + private final OnClickListener mCancelButtonClickListener = v -> { + deleteAccountAndReturnIfNecessary(); + finish(); + }; + private final UiCallback mAvatarFetchCallback = new UiCallback() { + + @Override + public void userInputRequried(final PendingIntent pi, final Avatar avatar) { + finishInitialSetup(avatar); + } + + @Override + public void success(final Avatar avatar) { + finishInitialSetup(avatar); + } + + @Override + public void error(final int errorCode, final Avatar avatar) { + finishInitialSetup(avatar); + } + }; + private final OnClickListener mAvatarClickListener = new OnClickListener() { + @Override + public void onClick(final View view) { + if (mAccount != null) { + final Intent intent = new Intent(getApplicationContext(), PublishProfilePictureActivity.class); + intent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().asBareJid().toString()); + startActivity(intent); + } + } + }; + private String messageFingerprint; + private boolean mFetchingAvatar = false; + private Toast mFetchingMamPrefsToast; + private String mSavedInstanceAccount; + private boolean mSavedInstanceInit = false; + private XmppUri pendingUri = null; + private boolean mUseTor; + private ActivityEditAccountBinding binding; + private final OnClickListener mSaveButtonClickListener = new OnClickListener() { + + @Override + public void onClick(final View v) { + final String password = binding.accountPassword.getText().toString(); + final boolean wasDisabled = mAccount != null && mAccount.getStatus() == Account.State.DISABLED; + final boolean accountInfoEdited = accountInfoEdited(); + + if (!mInitMode && passwordChangedInMagicCreateMode()) { + gotoChangePassword(password); + return; + } + if (mInitMode && mAccount != null) { + mAccount.setOption(Account.OPTION_DISABLED, false); + } + if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED && !accountInfoEdited) { + mAccount.setOption(Account.OPTION_DISABLED, false); + if (!xmppConnectionService.updateAccount(mAccount)) { + Toast.makeText(EditAccountActivity.this, R.string.unable_to_update_account, Toast.LENGTH_SHORT).show(); + } + return; + } + final boolean registerNewAccount = binding.accountRegisterNew.isChecked() && !Config.DISALLOW_REGISTRATION_IN_UI; + if (mUsernameMode && binding.accountJid.getText().toString().contains("@")) { + binding.accountJidLayout.setError(getString(R.string.invalid_username)); + removeErrorsOnAllBut(binding.accountJidLayout); + binding.accountJid.requestFocus(); + return; + } + + XmppConnection connection = mAccount == null ? null : mAccount.getXmppConnection(); + boolean openRegistrationUrl = registerNewAccount && !accountInfoEdited && mAccount != null && mAccount.getStatus() == Account.State.REGISTRATION_WEB; + boolean openPaymentUrl = mAccount != null && mAccount.getStatus() == Account.State.PAYMENT_REQUIRED; + final boolean redirectionWorthyStatus = openPaymentUrl || openRegistrationUrl; + URL url = connection != null && redirectionWorthyStatus ? connection.getRedirectionUrl() : null; + if (url != null && !wasDisabled) { + try { + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url.toString()))); + return; + } catch (ActivityNotFoundException e) { + Toast.makeText(EditAccountActivity.this, R.string.application_found_to_open_website, Toast.LENGTH_SHORT).show(); + return; + } + } + + final Jid jid; + try { + if (mUsernameMode) { + jid = Jid.of(binding.accountJid.getText().toString(), getUserModeDomain(), null); + } else { + jid = Jid.of(binding.accountJid.getText().toString()); + } + } catch (final NullPointerException | IllegalArgumentException e) { + if (mUsernameMode) { + binding.accountJidLayout.setError(getString(R.string.invalid_username)); + } else { + binding.accountJidLayout.setError(getString(R.string.invalid_jid)); + } + binding.accountJid.requestFocus(); + removeErrorsOnAllBut(binding.accountJidLayout); + return; + } + String hostname = null; + int numericPort = 5222; + if (mShowOptions) { + hostname = binding.hostname.getText().toString().replaceAll("\\s", ""); + final String port = binding.port.getText().toString().replaceAll("\\s", ""); + if (hostname.contains(" ")) { + binding.hostnameLayout.setError(getString(R.string.not_valid_hostname)); + binding.hostname.requestFocus(); + removeErrorsOnAllBut(binding.hostnameLayout); + return; + } + try { + numericPort = Integer.parseInt(port); + if (numericPort < 0 || numericPort > 65535) { + binding.portLayout.setError(getString(R.string.not_a_valid_port)); + removeErrorsOnAllBut(binding.portLayout); + binding.port.requestFocus(); + return; + } + + } catch (NumberFormatException e) { + binding.portLayout.setError(getString(R.string.not_a_valid_port)); + removeErrorsOnAllBut(binding.portLayout); + binding.port.requestFocus(); + return; + } + } + + if (jid.getLocal() == null) { + if (mUsernameMode) { + binding.accountJidLayout.setError(getString(R.string.invalid_username)); + } else { + binding.accountJidLayout.setError(getString(R.string.invalid_jid)); + } + removeErrorsOnAllBut(binding.accountJidLayout); + binding.accountJid.requestFocus(); + return; + } + if (mAccount != null) { + if (mInitMode && mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)) { + mAccount.setOption(Account.OPTION_MAGIC_CREATE, mAccount.getPassword().contains(password)); + } + mAccount.setJid(jid); + mAccount.setPort(numericPort); + mAccount.setHostname(hostname); + binding.accountJidLayout.setError(null); + mAccount.setPassword(password); + mAccount.setOption(Account.OPTION_REGISTER, registerNewAccount); + if (!xmppConnectionService.updateAccount(mAccount)) { + Toast.makeText(EditAccountActivity.this, R.string.unable_to_update_account, Toast.LENGTH_SHORT).show(); + return; + } + } else { + if (xmppConnectionService.findAccountByJid(jid) != null) { + binding.accountJidLayout.setError(getString(R.string.account_already_exists)); + removeErrorsOnAllBut(binding.accountJidLayout); + binding.accountJid.requestFocus(); + return; + } + mAccount = new Account(jid.asBareJid(), password); + mAccount.setPort(numericPort); + mAccount.setHostname(hostname); + mAccount.setOption(Account.OPTION_USETLS, true); + mAccount.setOption(Account.OPTION_USECOMPRESSION, true); + mAccount.setOption(Account.OPTION_REGISTER, registerNewAccount); + xmppConnectionService.createAccount(mAccount); + } + binding.hostnameLayout.setError(null); + binding.portLayout.setError(null); + if (mAccount.isEnabled() + && !registerNewAccount + && !mInitMode) { + finish(); + } else { + updateSaveButton(); + updateAccountInformation(true); + } + + } + }; + private final TextWatcher mTextWatcher = new TextWatcher() { + + @Override + public void onTextChanged(final CharSequence s, final int start, final int before, final int count) { + updatePortLayout(); + updateSaveButton(); + } + + @Override + public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) { + } + + @Override + public void afterTextChanged(final Editable s) { + + } + }; + private View.OnFocusChangeListener mEditTextFocusListener = new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View view, boolean b) { + EditText et = (EditText) view; + if (b) { + int resId = mUsernameMode ? R.string.username : R.string.account_settings_example_jabber_id; + if (view.getId() == R.id.hostname) { + resId = mUseTor ? R.string.hostname_or_onion : R.string.hostname_example; + } + final int res = resId; + new Handler().postDelayed(() -> et.setHint(res), 200); + } else { + et.setHint(null); + } + } + }; + + private static void setAvailabilityRadioButton(Presence.Status status, DialogPresenceBinding binding) { + if (status == null) { + binding.online.setChecked(true); + return; + } + switch (status) { + case DND: + binding.dnd.setChecked(true); + break; + case XA: + binding.xa.setChecked(true); + break; + case AWAY: + binding.xa.setChecked(true); + break; + default: + binding.online.setChecked(true); + } + } + + private static Presence.Status getAvailabilityRadioButton(DialogPresenceBinding binding) { + if (binding.dnd.isChecked()) { + return Presence.Status.DND; + } else if (binding.xa.isChecked()) { + return Presence.Status.XA; + } else if (binding.away.isChecked()) { + return Presence.Status.AWAY; + } else { + return Presence.Status.ONLINE; + } + } + + public void refreshUiReal() { + invalidateOptionsMenu(); + if (mAccount != null + && mAccount.getStatus() != Account.State.ONLINE + && mFetchingAvatar) { + Intent intent = new Intent(this, StartConversationActivity.class); + StartConversationActivity.addInviteUri(intent, getIntent()); + startActivity(intent); + finish(); + } else if (mInitMode && mAccount != null && mAccount.getStatus() == Account.State.ONLINE) { + if (!mFetchingAvatar) { + mFetchingAvatar = true; + xmppConnectionService.checkForAvatar(mAccount, mAvatarFetchCallback); + } + } + if (mAccount != null) { + updateAccountInformation(false); + } + updateSaveButton(); + } + + @Override + public boolean onNavigateUp() { + deleteAccountAndReturnIfNecessary(); + return super.onNavigateUp(); + } + + @Override + public void onBackPressed() { + deleteAccountAndReturnIfNecessary(); + super.onBackPressed(); + } + + private void deleteAccountAndReturnIfNecessary() { + if (mInitMode && mAccount != null && !mAccount.isOptionSet(Account.OPTION_LOGGED_IN_SUCCESSFULLY)) { + xmppConnectionService.deleteAccount(mAccount); + } + + if (xmppConnectionService.getAccounts().size() == 0 && Config.MAGIC_CREATE_DOMAIN != null) { + Intent intent = SignupUtils.getSignUpIntent(this); + startActivity(intent); + } + } + + @Override + public void onAccountUpdate() { + refreshUi(); + } + + protected void finishInitialSetup(final Avatar avatar) { + runOnUiThread(() -> { + SoftKeyboardUtils.hideSoftKeyboard(EditAccountActivity.this); + final Intent intent; + final XmppConnection connection = mAccount.getXmppConnection(); + final boolean wasFirstAccount = xmppConnectionService != null && xmppConnectionService.getAccounts().size() == 1; + if (avatar != null || (connection != null && !connection.getFeatures().pep())) { + intent = new Intent(getApplicationContext(), StartConversationActivity.class); + if (wasFirstAccount) { + intent.putExtra("init", true); + } + } else { + intent = new Intent(getApplicationContext(), PublishProfilePictureActivity.class); + intent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().asBareJid().toString()); + intent.putExtra("setup", true); + } + if (wasFirstAccount) { + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + } + StartConversationActivity.addInviteUri(intent, getIntent()); + startActivity(intent); + finish(); + }); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (requestCode == REQUEST_BATTERY_OP || requestCode == REQUEST_DATA_SAVER) { + updateAccountInformation(mAccount == null); + } + if (requestCode == REQUEST_CHANGE_STATUS) { + PresenceTemplate template = mPendingPresenceTemplate.pop(); + if (template != null && resultCode == Activity.RESULT_OK) { + generateSignature(data, template); + } else { + Log.d(Config.LOGTAG, "pgp result not ok"); + } + } + } + + @Override + protected void processFingerprintVerification(XmppUri uri) { + processFingerprintVerification(uri, true); + } + + protected void processFingerprintVerification(XmppUri uri, boolean showWarningToast) { + if (mAccount != null && mAccount.getJid().asBareJid().equals(uri.getJid()) && uri.hasFingerprints()) { + if (xmppConnectionService.verifyFingerprints(mAccount, uri.getFingerprints())) { + Toast.makeText(this, R.string.verified_fingerprints, Toast.LENGTH_SHORT).show(); + updateAccountInformation(false); + } + } else if (showWarningToast) { + Toast.makeText(this, R.string.invalid_barcode, Toast.LENGTH_SHORT).show(); + } + } + + private void updatePortLayout() { + String hostname = this.binding.hostname.getText().toString(); + this.binding.portLayout.setEnabled(!TextUtils.isEmpty(hostname)); + } + + protected void updateSaveButton() { + boolean accountInfoEdited = accountInfoEdited(); + + if (!mInitMode && passwordChangedInMagicCreateMode()) { + this.binding.saveButton.setText(R.string.change_password); + this.binding.saveButton.setEnabled(true); + } else if (accountInfoEdited && !mInitMode) { + this.binding.saveButton.setText(R.string.save); + this.binding.saveButton.setEnabled(true); + } else if (mAccount != null + && (mAccount.getStatus() == Account.State.CONNECTING || mAccount.getStatus() == Account.State.REGISTRATION_SUCCESSFUL || mFetchingAvatar)) { + this.binding.saveButton.setEnabled(false); + this.binding.saveButton.setText(R.string.account_status_connecting); + } else if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED && !mInitMode) { + this.binding.saveButton.setEnabled(true); + this.binding.saveButton.setText(R.string.enable); + } else { + this.binding.saveButton.setEnabled(true); + if (!mInitMode) { + if (mAccount != null && mAccount.isOnlineAndConnected()) { + this.binding.saveButton.setText(R.string.save); + if (!accountInfoEdited) { + this.binding.saveButton.setEnabled(false); + } + } else { + XmppConnection connection = mAccount == null ? null : mAccount.getXmppConnection(); + URL url = connection != null && mAccount.getStatus() == Account.State.PAYMENT_REQUIRED ? connection.getRedirectionUrl() : null; + if (url != null) { + this.binding.saveButton.setText(R.string.open_website); + } else { + this.binding.saveButton.setText(R.string.connect); + } + } + } else { + XmppConnection connection = mAccount == null ? null : mAccount.getXmppConnection(); + URL url = connection != null && mAccount.getStatus() == Account.State.REGISTRATION_WEB ? connection.getRedirectionUrl() : null; + if (url != null && this.binding.accountRegisterNew.isChecked() && !accountInfoEdited) { + this.binding.saveButton.setText(R.string.open_website); + } else { + this.binding.saveButton.setText(R.string.next); + } + } + } + } + + protected boolean accountInfoEdited() { + if (this.mAccount == null) { + return false; + } + return jidEdited() || + !this.mAccount.getPassword().equals(this.binding.accountPassword.getText().toString()) || + !this.mAccount.getHostname().equals(this.binding.hostname.getText().toString()) || + !String.valueOf(this.mAccount.getPort()).equals(this.binding.port.getText().toString()); + } + + protected boolean jidEdited() { + final String unmodified; + if (mUsernameMode) { + unmodified = this.mAccount.getJid().getLocal(); + } else { + unmodified = this.mAccount.getJid().asBareJid().toString(); + } + return !unmodified.equals(this.binding.accountJid.getText().toString()); + } + + protected boolean passwordChangedInMagicCreateMode() { + return mAccount != null + && mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE) + && !this.mAccount.getPassword().equals(this.binding.accountPassword.getText().toString()) + && !this.jidEdited() + && mAccount.isOnlineAndConnected(); + } + + @Override + protected String getShareableUri(boolean http) { + if (mAccount != null) { + return http ? mAccount.getShareableLink() : mAccount.getShareableUri(); + } else { + return null; + } + } + + @Override + protected void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (savedInstanceState != null) { + this.mSavedInstanceAccount = savedInstanceState.getString("account"); + this.mSavedInstanceInit = savedInstanceState.getBoolean("initMode", false); + } + this.binding = DataBindingUtil.setContentView(this, R.layout.activity_edit_account); + setSupportActionBar((Toolbar) binding.toolbar); + binding.accountJid.addTextChangedListener(this.mTextWatcher); + binding.accountJid.setOnFocusChangeListener(this.mEditTextFocusListener); + this.binding.accountPassword.addTextChangedListener(this.mTextWatcher); + + this.binding.avater.setOnClickListener(this.mAvatarClickListener); + this.binding.hostname.addTextChangedListener(mTextWatcher); + this.binding.hostname.setOnFocusChangeListener(mEditTextFocusListener); + this.binding.clearDevices.setOnClickListener(v -> showWipePepDialog()); + this.binding.port.setText("5222"); + this.binding.port.addTextChangedListener(mTextWatcher); + this.binding.saveButton.setOnClickListener(this.mSaveButtonClickListener); + this.binding.cancelButton.setOnClickListener(this.mCancelButtonClickListener); + if (savedInstanceState != null && savedInstanceState.getBoolean("showMoreTable")) { + changeMoreTableVisibility(true); + } + final OnCheckedChangeListener OnCheckedShowConfirmPassword = (buttonView, isChecked) -> updateSaveButton(); + this.binding.accountRegisterNew.setOnCheckedChangeListener(OnCheckedShowConfirmPassword); + if (Config.DISALLOW_REGISTRATION_IN_UI) { + this.binding.accountRegisterNew.setVisibility(View.GONE); + } + this.binding.actionEditYourName.setOnClickListener(this::onEditYourNameClicked); + } + + private void onEditYourNameClicked(View view) { + quickEdit(mAccount.getDisplayName(), R.string.your_name, value -> { + final String displayName = value.trim(); + updateDisplayName(displayName); + mAccount.setDisplayName(displayName); + xmppConnectionService.publishDisplayName(mAccount); + return null; + }, true); + } + + @Override + public boolean onCreateOptionsMenu(final Menu menu) { + super.onCreateOptionsMenu(menu); + getMenuInflater().inflate(R.menu.editaccount, menu); + final MenuItem showBlocklist = menu.findItem(R.id.action_show_block_list); + final MenuItem showMoreInfo = menu.findItem(R.id.action_server_info_show_more); + final MenuItem changePassword = menu.findItem(R.id.action_change_password_on_server); + final MenuItem renewCertificate = menu.findItem(R.id.action_renew_certificate); + final MenuItem mamPrefs = menu.findItem(R.id.action_mam_prefs); + final MenuItem changePresence = menu.findItem(R.id.action_change_presence); + final MenuItem share = menu.findItem(R.id.action_share); + renewCertificate.setVisible(mAccount != null && mAccount.getPrivateKeyAlias() != null); + + share.setVisible(mAccount != null && !mInitMode); + + if (mAccount != null && mAccount.isOnlineAndConnected()) { + if (!mAccount.getXmppConnection().getFeatures().blocking()) { + showBlocklist.setVisible(false); + } + + if (!mAccount.getXmppConnection().getFeatures().register()) { + changePassword.setVisible(false); + } + mamPrefs.setVisible(mAccount.getXmppConnection().getFeatures().mam()); + changePresence.setVisible(!mInitMode); + } else { + showBlocklist.setVisible(false); + showMoreInfo.setVisible(false); + changePassword.setVisible(false); + mamPrefs.setVisible(false); + changePresence.setVisible(false); + } + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + final MenuItem showMoreInfo = menu.findItem(R.id.action_server_info_show_more); + if (showMoreInfo.isVisible()) { + showMoreInfo.setChecked(binding.serverInfoMore.getVisibility() == View.VISIBLE); + } + return super.onPrepareOptionsMenu(menu); + } + + @Override + protected void onStart() { + super.onStart(); + final Intent intent = getIntent(); + final int theme = findTheme(); + if (this.mTheme != theme) { + recreate(); + } else if (intent != null) { + try { + this.jidToEdit = Jid.of(intent.getStringExtra("jid")); + } catch (final IllegalArgumentException | NullPointerException ignored) { + this.jidToEdit = null; + } + if (jidToEdit != null && intent.getData() != null && intent.getBooleanExtra("scanned", false)) { + final XmppUri uri = new XmppUri(intent.getData()); + if (xmppConnectionServiceBound) { + processFingerprintVerification(uri, false); + } else { + this.pendingUri = uri; + } + } + boolean init = intent.getBooleanExtra("init", false); + boolean openedFromNotification = intent.getBooleanExtra(EXTRA_OPENED_FROM_NOTIFICATION, false); + this.mInitMode = init || this.jidToEdit == null; + this.messageFingerprint = intent.getStringExtra("fingerprint"); + if (!mInitMode) { + this.binding.accountRegisterNew.setVisibility(View.GONE); + setTitle(getString(R.string.account_details)); + configureActionBar(getSupportActionBar(), !openedFromNotification); + } else { + this.binding.avater.setVisibility(View.GONE); + configureActionBar(getSupportActionBar(), !(init && Config.MAGIC_CREATE_DOMAIN == null)); + setTitle(R.string.action_add_account); + } + } + SharedPreferences preferences = getPreferences(); + mUseTor = QuickConversationsService.isConversations() && preferences.getBoolean("use_tor", getResources().getBoolean(R.bool.use_tor)); + this.mShowOptions = mUseTor || (QuickConversationsService.isConversations() && preferences.getBoolean("show_connection_options", getResources().getBoolean(R.bool.show_connection_options))); + this.binding.namePort.setVisibility(mShowOptions ? View.VISIBLE : View.GONE); + } + + @Override + public void onNewIntent(Intent intent) { + if (intent != null && intent.getData() != null) { + final XmppUri uri = new XmppUri(intent.getData()); + if (xmppConnectionServiceBound) { + processFingerprintVerification(uri, false); + } else { + this.pendingUri = uri; + } + } + } + + @Override + public void onSaveInstanceState(final Bundle savedInstanceState) { + if (mAccount != null) { + savedInstanceState.putString("account", mAccount.getJid().asBareJid().toString()); + savedInstanceState.putBoolean("initMode", mInitMode); + savedInstanceState.putBoolean("showMoreTable", binding.serverInfoMore.getVisibility() == View.VISIBLE); + } + super.onSaveInstanceState(savedInstanceState); + } + + protected void onBackendConnected() { + boolean init = true; + if (mSavedInstanceAccount != null) { + try { + this.mAccount = xmppConnectionService.findAccountByJid(Jid.of(mSavedInstanceAccount)); + this.mInitMode = mSavedInstanceInit; + init = false; + } catch (IllegalArgumentException e) { + this.mAccount = null; + } + + } else if (this.jidToEdit != null) { + this.mAccount = xmppConnectionService.findAccountByJid(jidToEdit); + } + + if (mAccount != null) { + this.mInitMode |= this.mAccount.isOptionSet(Account.OPTION_REGISTER); + this.mUsernameMode |= mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE) && mAccount.isOptionSet(Account.OPTION_REGISTER); + if (this.mAccount.getPrivateKeyAlias() != null) { + this.binding.accountPassword.setHint(R.string.authenticate_with_certificate); + if (this.mInitMode) { + this.binding.accountPassword.requestFocus(); + } + } + if (mPendingFingerprintVerificationUri != null) { + processFingerprintVerification(mPendingFingerprintVerificationUri, false); + mPendingFingerprintVerificationUri = null; + } + updateAccountInformation(init); + } + + + if (Config.MAGIC_CREATE_DOMAIN == null && this.xmppConnectionService.getAccounts().size() == 0) { + this.binding.cancelButton.setEnabled(false); + } + if (mUsernameMode) { + this.binding.accountJidLayout.setHint(getString(R.string.username_hint)); + this.binding.accountJid.setHint(R.string.username_hint); + } else { + final KnownHostsAdapter mKnownHostsAdapter = new KnownHostsAdapter(this, + R.layout.simple_list_item, + xmppConnectionService.getKnownHosts()); + this.binding.accountJid.setAdapter(mKnownHostsAdapter); + } + + if (pendingUri != null) { + processFingerprintVerification(pendingUri, false); + pendingUri = null; + } + updatePortLayout(); + updateSaveButton(); + invalidateOptionsMenu(); + } + + private String getUserModeDomain() { + if (mAccount != null && mAccount.getJid().getDomain() != null) { + return mAccount.getJid().getDomain(); + } else { + return Config.DOMAIN_LOCK; + } + } + + @Override + public boolean onOptionsItemSelected(final MenuItem item) { + if (MenuDoubleTabUtil.shouldIgnoreTap()) { + return false; + } + switch (item.getItemId()) { + case R.id.action_show_block_list: + final Intent showBlocklistIntent = new Intent(this, BlocklistActivity.class); + showBlocklistIntent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toString()); + startActivity(showBlocklistIntent); + break; + case R.id.action_server_info_show_more: + changeMoreTableVisibility(!item.isChecked()); + break; + case R.id.action_share_barcode: + shareBarcode(); + break; + case R.id.action_share_http: + shareLink(true); + break; + case R.id.action_share_uri: + shareLink(false); + break; + case R.id.action_change_password_on_server: + gotoChangePassword(null); + break; + case R.id.action_mam_prefs: + editMamPrefs(); + break; + case R.id.action_renew_certificate: + renewCertificate(); + break; + case R.id.action_change_presence: + changePresence(); + break; + } + return super.onOptionsItemSelected(item); + } + + private void shareBarcode() { + Intent intent = new Intent(Intent.ACTION_SEND); + intent.putExtra(Intent.EXTRA_STREAM, BarcodeProvider.getUriForAccount(this, mAccount)); + intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.setType("image/png"); + startActivity(Intent.createChooser(intent, getText(R.string.share_with))); + } + + private void changeMoreTableVisibility(boolean visible) { + binding.serverInfoMore.setVisibility(visible ? View.VISIBLE : View.GONE); + } + + private void gotoChangePassword(String newPassword) { + final Intent changePasswordIntent = new Intent(this, ChangePasswordActivity.class); + changePasswordIntent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toString()); + if (newPassword != null) { + changePasswordIntent.putExtra("password", newPassword); + } + startActivity(changePasswordIntent); + } + + private void renewCertificate() { + KeyChain.choosePrivateKeyAlias(this, this, null, null, null, -1, null); + } + + private void changePresence() { + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); + boolean manualStatus = sharedPreferences.getBoolean(SettingsActivity.MANUALLY_CHANGE_PRESENCE, getResources().getBoolean(R.bool.manually_change_presence)); + AlertDialog.Builder builder = new AlertDialog.Builder(this); + final DialogPresenceBinding binding = DataBindingUtil.inflate(getLayoutInflater(), R.layout.dialog_presence, null, false); + String current = mAccount.getPresenceStatusMessage(); + if (current != null && !current.trim().isEmpty()) { + binding.statusMessage.append(current); + } + setAvailabilityRadioButton(mAccount.getPresenceStatus(), binding); + binding.show.setVisibility(manualStatus ? View.VISIBLE : View.GONE); + List templates = xmppConnectionService.getPresenceTemplates(mAccount); + PresenceTemplateAdapter presenceTemplateAdapter = new PresenceTemplateAdapter(this, R.layout.simple_list_item, templates); + binding.statusMessage.setAdapter(presenceTemplateAdapter); + binding.statusMessage.setOnItemClickListener((parent, view, position, id) -> { + PresenceTemplate template = (PresenceTemplate) parent.getItemAtPosition(position); + setAvailabilityRadioButton(template.getStatus(), binding); + }); + builder.setTitle(R.string.edit_status_message_title); + builder.setView(binding.getRoot()); + builder.setNegativeButton(R.string.cancel, null); + builder.setPositiveButton(R.string.confirm, (dialog, which) -> { + PresenceTemplate template = new PresenceTemplate(getAvailabilityRadioButton(binding), binding.statusMessage.getText().toString().trim()); + if (mAccount.getPgpId() != 0 && hasPgp()) { + generateSignature(null, template); + } else { + xmppConnectionService.changeStatus(mAccount, template, null); + } + }); + builder.create().show(); + } + + private void generateSignature(Intent intent, PresenceTemplate template) { + xmppConnectionService.getPgpEngine().generateSignature(intent, mAccount, template.getStatusMessage(), new UiCallback() { + @Override + public void success(String signature) { + xmppConnectionService.changeStatus(mAccount, template, signature); + } + + @Override + public void error(int errorCode, String object) { + + } + + @Override + public void userInputRequried(PendingIntent pi, String object) { + mPendingPresenceTemplate.push(template); + try { + startIntentSenderForResult(pi.getIntentSender(), REQUEST_CHANGE_STATUS, null, 0, 0, 0); + } catch (final IntentSender.SendIntentException ignored) { + } + } + }); + } + + @Override + public void alias(String alias) { + if (alias != null) { + xmppConnectionService.updateKeyInAccount(mAccount, alias); + } + } + + private void updateAccountInformation(boolean init) { + if (init) { + this.binding.accountJid.getEditableText().clear(); + if (mUsernameMode) { + this.binding.accountJid.getEditableText().append(this.mAccount.getJid().getLocal()); + } else { + this.binding.accountJid.getEditableText().append(this.mAccount.getJid().asBareJid().toString()); + } + this.binding.accountPassword.getEditableText().clear(); + this.binding.accountPassword.getEditableText().append(this.mAccount.getPassword()); + this.binding.hostname.setText(""); + this.binding.hostname.getEditableText().append(this.mAccount.getHostname()); + this.binding.port.setText(""); + this.binding.port.getEditableText().append(String.valueOf(this.mAccount.getPort())); + this.binding.namePort.setVisibility(mShowOptions ? View.VISIBLE : View.GONE); + + } + + final boolean editable = !mAccount.isOptionSet(Account.OPTION_LOGGED_IN_SUCCESSFULLY) && QuickConversationsService.isConversations(); + this.binding.accountJid.setEnabled(editable); + this.binding.accountJid.setFocusable(editable); + this.binding.accountJid.setFocusableInTouchMode(editable); + + + final String displayName = mAccount.getDisplayName(); + updateDisplayName(displayName); + + + if (mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE) || !mAccount.isOptionSet(Account.OPTION_LOGGED_IN_SUCCESSFULLY)) { + this.binding.accountPasswordLayout.setPasswordVisibilityToggleEnabled(true); + } else { + this.binding.accountPasswordLayout.setPasswordVisibilityToggleEnabled(false); + } + + if (!mInitMode) { + this.binding.avater.setVisibility(View.VISIBLE); + this.binding.avater.setImageBitmap(avatarService().get(this.mAccount, (int) getResources().getDimension(R.dimen.avatar_on_details_screen_size))); + } else { + this.binding.avater.setVisibility(View.GONE); + } + this.binding.accountRegisterNew.setChecked(this.mAccount.isOptionSet(Account.OPTION_REGISTER)); + if (this.mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)) { + if (this.mAccount.isOptionSet(Account.OPTION_REGISTER)) { + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.setTitle(R.string.create_account); + } + } + this.binding.accountRegisterNew.setVisibility(View.GONE); + } else if (this.mAccount.isOptionSet(Account.OPTION_REGISTER)) { + this.binding.accountRegisterNew.setVisibility(View.VISIBLE); + } else { + this.binding.accountRegisterNew.setVisibility(View.GONE); + } + if (this.mAccount.isOnlineAndConnected() && !this.mFetchingAvatar) { + Features features = this.mAccount.getXmppConnection().getFeatures(); + this.binding.stats.setVisibility(View.VISIBLE); + boolean showBatteryWarning = !xmppConnectionService.getPushManagementService().available(mAccount) && isOptimizingBattery(); + boolean showDataSaverWarning = isAffectedByDataSaver(); + showOsOptimizationWarning(showBatteryWarning, showDataSaverWarning); + this.binding.sessionEst.setText(UIHelper.readableTimeDifferenceFull(this, this.mAccount.getXmppConnection() + .getLastSessionEstablished())); + if (features.rosterVersioning()) { + this.binding.serverInfoRosterVersion.setText(R.string.server_info_available); + } else { + this.binding.serverInfoRosterVersion.setText(R.string.server_info_unavailable); + } + if (features.carbons()) { + this.binding.serverInfoCarbons.setText(R.string.server_info_available); + } else { + this.binding.serverInfoCarbons.setText(R.string.server_info_unavailable); + } + if (features.mam()) { + this.binding.serverInfoMam.setText(R.string.server_info_available); + } else { + this.binding.serverInfoMam.setText(R.string.server_info_unavailable); + } + if (features.csi()) { + this.binding.serverInfoCsi.setText(R.string.server_info_available); + } else { + this.binding.serverInfoCsi.setText(R.string.server_info_unavailable); + } + if (features.blocking()) { + this.binding.serverInfoBlocking.setText(R.string.server_info_available); + } else { + this.binding.serverInfoBlocking.setText(R.string.server_info_unavailable); + } + if (features.sm()) { + this.binding.serverInfoSm.setText(R.string.server_info_available); + } else { + this.binding.serverInfoSm.setText(R.string.server_info_unavailable); + } + if (features.pep()) { + AxolotlService axolotlService = this.mAccount.getAxolotlService(); + if (axolotlService != null && axolotlService.isPepBroken()) { + this.binding.serverInfoPep.setText(R.string.server_info_broken); + } else if (features.pepPublishOptions() || features.pepOmemoWhitelisted()) { + this.binding.serverInfoPep.setText(R.string.server_info_available); + } else { + this.binding.serverInfoPep.setText(R.string.server_info_partial); + } + } else { + this.binding.serverInfoPep.setText(R.string.server_info_unavailable); + } + if (features.httpUpload(0)) { + this.binding.serverInfoHttpUpload.setText(R.string.server_info_available); + } else if (features.p1S3FileTransfer()) { + this.binding.serverInfoHttpUploadDescription.setText(R.string.p1_s3_filetransfer); + this.binding.serverInfoHttpUpload.setText(R.string.server_info_available); + } else { + this.binding.serverInfoHttpUpload.setText(R.string.server_info_unavailable); + } + + this.binding.pushRow.setVisibility(xmppConnectionService.getPushManagementService().isStub() ? View.GONE : View.VISIBLE); + + if (xmppConnectionService.getPushManagementService().available(mAccount)) { + this.binding.serverInfoPush.setText(R.string.server_info_available); + } else { + this.binding.serverInfoPush.setText(R.string.server_info_unavailable); + } + final long pgpKeyId = this.mAccount.getPgpId(); + if (pgpKeyId != 0 && Config.supportOpenPgp()) { + OnClickListener openPgp = view -> launchOpenKeyChain(pgpKeyId); + OnClickListener delete = view -> showDeletePgpDialog(); + this.binding.pgpFingerprintBox.setVisibility(View.VISIBLE); + this.binding.pgpFingerprint.setText(OpenPgpUtils.convertKeyIdToHex(pgpKeyId)); + this.binding.pgpFingerprint.setOnClickListener(openPgp); + if ("pgp".equals(messageFingerprint)) { + this.binding.pgpFingerprintDesc.setTextAppearance(this, R.style.TextAppearance_Conversations_Caption_Highlight); + } + this.binding.pgpFingerprintDesc.setOnClickListener(openPgp); + this.binding.actionDeletePgp.setOnClickListener(delete); + } else { + this.binding.pgpFingerprintBox.setVisibility(View.GONE); + } + final String ownAxolotlFingerprint = this.mAccount.getAxolotlService().getOwnFingerprint(); + if (ownAxolotlFingerprint != null && Config.supportOmemo()) { + this.binding.axolotlFingerprintBox.setVisibility(View.VISIBLE); + if (ownAxolotlFingerprint.equals(messageFingerprint)) { + this.binding.ownFingerprintDesc.setTextAppearance(this, R.style.TextAppearance_Conversations_Caption_Highlight); + this.binding.ownFingerprintDesc.setText(R.string.omemo_fingerprint_selected_message); + } else { + this.binding.ownFingerprintDesc.setTextAppearance(this, R.style.TextAppearance_Conversations_Caption); + this.binding.ownFingerprintDesc.setText(R.string.omemo_fingerprint); + } + this.binding.axolotlFingerprint.setText(CryptoHelper.prettifyFingerprint(ownAxolotlFingerprint.substring(2))); + this.binding.actionCopyAxolotlToClipboard.setVisibility(View.VISIBLE); + this.binding.actionCopyAxolotlToClipboard.setOnClickListener(v -> copyOmemoFingerprint(ownAxolotlFingerprint)); + } else { + this.binding.axolotlFingerprintBox.setVisibility(View.GONE); + } + boolean hasKeys = false; + binding.otherDeviceKeys.removeAllViews(); + for (XmppAxolotlSession session : mAccount.getAxolotlService().findOwnSessions()) { + if (!session.getTrust().isCompromised()) { + boolean highlight = session.getFingerprint().equals(messageFingerprint); + addFingerprintRow(binding.otherDeviceKeys, session, highlight); + hasKeys = true; + } + } + if (hasKeys && Config.supportOmemo()) { //TODO: either the button should be visible if we print an active device or the device list should be fed with reactived devices + this.binding.otherDeviceKeysCard.setVisibility(View.VISIBLE); + Set otherDevices = mAccount.getAxolotlService().getOwnDeviceIds(); + if (otherDevices == null || otherDevices.isEmpty()) { + binding.clearDevices.setVisibility(View.GONE); + } else { + binding.clearDevices.setVisibility(View.VISIBLE); + } + } else { + this.binding.otherDeviceKeysCard.setVisibility(View.GONE); + } + } else { + final TextInputLayout errorLayout; + if (this.mAccount.errorStatus()) { + if (this.mAccount.getStatus() == Account.State.UNAUTHORIZED) { + errorLayout = this.binding.accountPasswordLayout; + } else if (mShowOptions + && this.mAccount.getStatus() == Account.State.SERVER_NOT_FOUND + && this.binding.hostname.getText().length() > 0) { + errorLayout = this.binding.hostnameLayout; + } else { + errorLayout = this.binding.accountJidLayout; + } + errorLayout.setError(getString(this.mAccount.getStatus().getReadableId())); + if (init || !accountInfoEdited()) { + errorLayout.requestFocus(); + } + } else { + errorLayout = null; + } + removeErrorsOnAllBut(errorLayout); + this.binding.stats.setVisibility(View.GONE); + this.binding.otherDeviceKeysCard.setVisibility(View.GONE); + } + } + + private void updateDisplayName(String displayName) { + if (TextUtils.isEmpty(displayName)) { + this.binding.yourName.setText(R.string.no_name_set_instructions); + this.binding.yourName.setTextAppearance(this, R.style.TextAppearance_Conversations_Body1_Tertiary); + } else { + this.binding.yourName.setText(displayName); + this.binding.yourName.setTextAppearance(this, R.style.TextAppearance_Conversations_Body1); + } + } + + private void removeErrorsOnAllBut(TextInputLayout exception) { + if (this.binding.accountJidLayout != exception) { + this.binding.accountJidLayout.setErrorEnabled(false); + this.binding.accountJidLayout.setError(null); + } + if (this.binding.accountPasswordLayout != exception) { + this.binding.accountPasswordLayout.setErrorEnabled(false); + this.binding.accountPasswordLayout.setError(null); + } + if (this.binding.hostnameLayout != exception) { + this.binding.hostnameLayout.setErrorEnabled(false); + this.binding.hostnameLayout.setError(null); + } + if (this.binding.portLayout != exception) { + this.binding.portLayout.setErrorEnabled(false); + this.binding.portLayout.setError(null); + } + } + + private void showDeletePgpDialog() { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.unpublish_pgp); + builder.setMessage(R.string.unpublish_pgp_message); + builder.setNegativeButton(R.string.cancel, null); + builder.setPositiveButton(R.string.confirm, (dialogInterface, i) -> { + mAccount.setPgpSignId(0); + mAccount.unsetPgpSignature(); + xmppConnectionService.databaseBackend.updateAccount(mAccount); + xmppConnectionService.sendPresence(mAccount); + refreshUiReal(); + }); + builder.create().show(); + } + + private void showOsOptimizationWarning(boolean showBatteryWarning, boolean showDataSaverWarning) { + this.binding.osOptimization.setVisibility(showBatteryWarning || showDataSaverWarning ? View.VISIBLE : View.GONE); + if (showDataSaverWarning) { + this.binding.osOptimizationHeadline.setText(R.string.data_saver_enabled); + this.binding.osOptimizationBody.setText(R.string.data_saver_enabled_explained); + this.binding.osOptimizationDisable.setText(R.string.allow); + this.binding.osOptimizationDisable.setOnClickListener(v -> { + Intent intent = new Intent(Settings.ACTION_IGNORE_BACKGROUND_DATA_RESTRICTIONS_SETTINGS); + Uri uri = Uri.parse("package:" + getPackageName()); + intent.setData(uri); + try { + startActivityForResult(intent, REQUEST_DATA_SAVER); + } catch (ActivityNotFoundException e) { + Toast.makeText(EditAccountActivity.this, R.string.device_does_not_support_data_saver, Toast.LENGTH_SHORT).show(); + } + }); + } else if (showBatteryWarning) { + this.binding.osOptimizationDisable.setText(R.string.disable); + this.binding.osOptimizationHeadline.setText(R.string.battery_optimizations_enabled); + this.binding.osOptimizationBody.setText(R.string.battery_optimizations_enabled_explained); + this.binding.osOptimizationDisable.setOnClickListener(v -> { + Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); + Uri uri = Uri.parse("package:" + getPackageName()); + intent.setData(uri); + try { + startActivityForResult(intent, REQUEST_BATTERY_OP); + } catch (ActivityNotFoundException e) { + Toast.makeText(EditAccountActivity.this, R.string.device_does_not_support_battery_op, Toast.LENGTH_SHORT).show(); + } + }); + } + } + + public void showWipePepDialog() { + Builder builder = new Builder(this); + builder.setTitle(getString(R.string.clear_other_devices)); + builder.setIconAttribute(android.R.attr.alertDialogIcon); + builder.setMessage(getString(R.string.clear_other_devices_desc)); + builder.setNegativeButton(getString(R.string.cancel), null); + builder.setPositiveButton(getString(R.string.accept), + (dialog, which) -> mAccount.getAxolotlService().wipeOtherPepDevices()); + builder.create().show(); + } + + private void editMamPrefs() { + this.mFetchingMamPrefsToast = Toast.makeText(this, R.string.fetching_mam_prefs, Toast.LENGTH_LONG); + this.mFetchingMamPrefsToast.show(); + xmppConnectionService.fetchMamPreferences(mAccount, this); + } + + @Override + public void onKeyStatusUpdated(AxolotlService.FetchStatus report) { + refreshUi(); + } + + @Override + public void onCaptchaRequested(final Account account, final String id, final Data data, final Bitmap captcha) { + runOnUiThread(() -> { + if (mCaptchaDialog != null && mCaptchaDialog.isShowing()) { + mCaptchaDialog.dismiss(); + } + final Builder builder = new Builder(EditAccountActivity.this); + final View view = getLayoutInflater().inflate(R.layout.captcha, null); + final ImageView imageView = view.findViewById(R.id.captcha); + final EditText input = view.findViewById(R.id.input); + imageView.setImageBitmap(captcha); + + builder.setTitle(getString(R.string.captcha_required)); + builder.setView(view); + + builder.setPositiveButton(getString(R.string.ok), + (dialog, which) -> { + String rc = input.getText().toString(); + data.put("username", account.getUsername()); + data.put("password", account.getPassword()); + data.put("ocr", rc); + data.submit(); + + if (xmppConnectionServiceBound) { + xmppConnectionService.sendCreateAccountWithCaptchaPacket(account, id, data); + } + }); + builder.setNegativeButton(getString(R.string.cancel), (dialog, which) -> { + if (xmppConnectionService != null) { + xmppConnectionService.sendCreateAccountWithCaptchaPacket(account, null, null); + } + }); + + builder.setOnCancelListener(dialog -> { + if (xmppConnectionService != null) { + xmppConnectionService.sendCreateAccountWithCaptchaPacket(account, null, null); + } + }); + mCaptchaDialog = builder.create(); + mCaptchaDialog.show(); + input.requestFocus(); + }); + } + + public void onShowErrorToast(final int resId) { + runOnUiThread(() -> Toast.makeText(EditAccountActivity.this, resId, Toast.LENGTH_SHORT).show()); + } + + @Override + public void onPreferencesFetched(final Element prefs) { + runOnUiThread(() -> { + if (mFetchingMamPrefsToast != null) { + mFetchingMamPrefsToast.cancel(); + } + Builder builder = new Builder(EditAccountActivity.this); + builder.setTitle(R.string.server_side_mam_prefs); + String defaultAttr = prefs.getAttribute("default"); + final List defaults = Arrays.asList("never", "roster", "always"); + final AtomicInteger choice = new AtomicInteger(Math.max(0, defaults.indexOf(defaultAttr))); + builder.setSingleChoiceItems(R.array.mam_prefs, choice.get(), (dialog, which) -> choice.set(which)); + builder.setNegativeButton(R.string.cancel, null); + builder.setPositiveButton(R.string.ok, (dialog, which) -> { + prefs.setAttribute("default", defaults.get(choice.get())); + xmppConnectionService.pushMamPreferences(mAccount, prefs); + }); + builder.create().show(); + }); + } + + @Override + public void onPreferencesFetchFailed() { + runOnUiThread(() -> { + if (mFetchingMamPrefsToast != null) { + mFetchingMamPrefsToast.cancel(); + } + Toast.makeText(EditAccountActivity.this, R.string.unable_to_fetch_mam_prefs, Toast.LENGTH_LONG).show(); + }); + } + + @Override + public void OnUpdateBlocklist(Status status) { + refreshUi(); + } } diff --git a/src/main/java/eu/siacs/conversations/xml/Namespace.java b/src/main/java/eu/siacs/conversations/xml/Namespace.java index 99a9e3dec..b28b10f9f 100644 --- a/src/main/java/eu/siacs/conversations/xml/Namespace.java +++ b/src/main/java/eu/siacs/conversations/xml/Namespace.java @@ -16,6 +16,7 @@ public final class Namespace { public static final String PUBSUB = "http://jabber.org/protocol/pubsub"; public static final String PUBSUB_PUBLISH_OPTIONS = PUBSUB+"#publish-options"; public static final String PUBSUB_ERROR = PUBSUB+"#errors"; + public static final String PUBSUB_OWNER = PUBSUB+"#owner"; public static final String NICK = "http://jabber.org/protocol/nick"; public static final String FLEXIBLE_OFFLINE_MESSAGE_RETRIEVAL = "http://jabber.org/protocol/offline"; public static final String BIND = "urn:ietf:params:xml:ns:xmpp-bind"; diff --git a/src/main/res/layout/activity_edit_account.xml b/src/main/res/layout/activity_edit_account.xml index b5d7b8d3d..f314c9279 100644 --- a/src/main/res/layout/activity_edit_account.xml +++ b/src/main/res/layout/activity_edit_account.xml @@ -470,11 +470,53 @@ + + + + + + + + + + + + + android:layout_marginTop="16dp"> Share URI with… Join the Conversation Jabber is a provider independent instant messaging network. You can use this client with what ever Jabber server you choose.\nHowever for your convenience we made it easy to create an account on conversations.im¹; a provider specially suited for the use with Conversations. + Have some Quick Conversations +
You sign up with your phone number and Quicksy will automatically—based on the phone numbers in your address book—suggest possible contacts to you.

By signing up you agree to our privacy policy.]]>
+ Agree & continue We will guide you through the process of creating an account on conversations.im.¹\nWhen picking conversations.im as a provider you will be able to communicate with users of other providers by giving them your full Jabber ID. Your full Jabber ID will be: %s Create Account @@ -789,4 +792,9 @@ Too many attempts You are using an out of date version of this app. Update + This phone number is currently logged in with another device. + Please enter your name to let people, who don’t have you in their address books, know who you are. + Your name + Enter your name + Use the edit button to set your name. diff --git a/src/main/res/values/styles.xml b/src/main/res/values/styles.xml index 57c55b572..8ae7d8aae 100644 --- a/src/main/res/values/styles.xml +++ b/src/main/res/values/styles.xml @@ -80,6 +80,10 @@ ?android:textColorSecondary + +