package eu.siacs.conversations.parser; import android.util.Log; import java.util.ArrayList; import java.util.List; import de.thedevstack.android.logcat.Logging; import de.thedevstack.conversationsplus.utils.AvatarUtil; import de.thedevstack.conversationsplus.utils.UiUpdateHelper; import eu.siacs.conversations.Config; import eu.siacs.conversations.crypto.PgpEngine; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.MucOptions; import eu.siacs.conversations.entities.Presence; import eu.siacs.conversations.entities.Presences; import eu.siacs.conversations.entities.ServiceDiscoveryResult; import eu.siacs.conversations.generator.PresenceGenerator; import eu.siacs.conversations.services.AvatarService; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.OnIqPacketReceived; import eu.siacs.conversations.xmpp.OnPresencePacketReceived; import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.pep.Avatar; import eu.siacs.conversations.xmpp.stanzas.IqPacket; import eu.siacs.conversations.xmpp.stanzas.PresencePacket; public class PresenceParser extends AbstractParser implements OnPresencePacketReceived { public PresenceParser(XmppConnectionService service) { super(service); } public void parseConferencePresence(PresencePacket packet, Account account) { final Conversation conversation = packet.getFrom() == null ? null : mXmppConnectionService.find(account, packet.getFrom().toBareJid()); if (conversation != null) { final MucOptions mucOptions = conversation.getMucOptions(); boolean before = mucOptions.online(); int count = mucOptions.getUserCount(); final List tileUserBefore = mucOptions.getUsers(5); processConferencePresence(packet, mucOptions); final List tileUserAfter = mucOptions.getUsers(5); if (!tileUserAfter.equals(tileUserBefore)) { Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": update tiles for " + conversation.getName()); AvatarService.getInstance().clear(conversation); } if (before != mucOptions.online() || (mucOptions.online() && count != mucOptions.getUserCount())) { UiUpdateHelper.updateConversationUi(); } else if (mucOptions.online()) { UiUpdateHelper.updateMucRosterUi(); } } } private void processConferencePresence(PresencePacket packet, MucOptions mucOptions) { final Jid from = packet.getFrom(); if (!from.isBareJid()) { final String type = packet.getAttribute("type"); final Element x = packet.findChild("x", "http://jabber.org/protocol/muc#user"); Avatar avatar = Avatar.parsePresence(packet.findChild("x", "vcard-temp:x:update")); final List codes = getStatusCodes(x); if (type == null) { if (x != null) { Element item = x.findChild("item"); if (item != null && !from.isBareJid()) { mucOptions.setError(MucOptions.Error.NONE); MucOptions.User user = new MucOptions.User(mucOptions, from); user.setAffiliation(item.getAttribute("affiliation")); user.setRole(item.getAttribute("role")); Jid real = item.getAttributeAsJid("jid"); if (real != null) { user.setJid(real); mucOptions.putMember(real.toBareJid()); } if (codes.contains(MucOptions.STATUS_CODE_SELF_PRESENCE) || packet.getFrom().equals(mucOptions.getConversation().getJid())) { mucOptions.setOnline(); mucOptions.setSelf(user); if (mucOptions.mNickChangingInProgress) { if (mucOptions.onRenameListener != null) { mucOptions.onRenameListener.onSuccess(); } mucOptions.mNickChangingInProgress = false; } } else { mucOptions.addUser(user); } if (mXmppConnectionService.getPgpEngine() != null) { Element signed = packet.findChild("x", "jabber:x:signed"); if (signed != null) { Element status = packet.findChild("status"); String msg = status == null ? "" : status.getContent(); long keyId = mXmppConnectionService.getPgpEngine().fetchKeyId(mucOptions.getAccount(), msg, signed.getContent()); if (keyId != 0) { user.setPgpKeyId(keyId); } } } if (avatar != null) { avatar.owner = from; if (AvatarUtil.isAvatarCached(avatar)) { if (user.setAvatar(avatar)) { AvatarService.getInstance().clear(user); } } else { AvatarService.getInstance().fetchAvatar(mucOptions.getAccount(), avatar); } } } } } else if (type.equals("unavailable")) { if (codes.contains(MucOptions.STATUS_CODE_SELF_PRESENCE) || packet.getFrom().equals(mucOptions.getConversation().getJid())) { if (codes.contains(MucOptions.STATUS_CODE_CHANGED_NICK)) { mucOptions.mNickChangingInProgress = true; } else if (codes.contains(MucOptions.STATUS_CODE_KICKED)) { mucOptions.setError(MucOptions.Error.KICKED); } else if (codes.contains(MucOptions.STATUS_CODE_BANNED)) { mucOptions.setError(MucOptions.Error.BANNED); } else if (codes.contains(MucOptions.STATUS_CODE_LOST_MEMBERSHIP)) { mucOptions.setError(MucOptions.Error.MEMBERS_ONLY); } else if (codes.contains(MucOptions.STATUS_CODE_AFFILIATION_CHANGE)) { mucOptions.setError(MucOptions.Error.MEMBERS_ONLY); } else if (codes.contains(MucOptions.STATUS_CODE_SHUTDOWN)) { mucOptions.setError(MucOptions.Error.SHUTDOWN); } else { mucOptions.setError(MucOptions.Error.UNKNOWN); Log.d(Config.LOGTAG, "unknown error in conference: " + packet); } } else if (!from.isBareJid()){ MucOptions.User user = mucOptions.deleteUser(from.getResourcepart()); if (user != null) { AvatarService.getInstance().clear(user); } } } else if (type.equals("error")) { Element error = packet.findChild("error"); if (error != null && error.hasChild("conflict")) { if (mucOptions.online()) { if (mucOptions.onRenameListener != null) { mucOptions.onRenameListener.onFailure(); } } else { mucOptions.setError(MucOptions.Error.NICK_IN_USE); } } else if (error != null && error.hasChild("not-authorized")) { mucOptions.setError(MucOptions.Error.PASSWORD_REQUIRED); } else if (error != null && error.hasChild("forbidden")) { mucOptions.setError(MucOptions.Error.BANNED); } else if (error != null && error.hasChild("registration-required")) { mucOptions.setError(MucOptions.Error.BANNED); } } } } private static List getStatusCodes(Element x) { List codes = new ArrayList<>(); if (x != null) { for (Element child : x.getChildren()) { if (child.getName().equals("status")) { String code = child.getAttribute("code"); if (code != null) { codes.add(code); } } } } return codes; } public void parseContactPresence(final PresencePacket packet, final Account account) { final PresenceGenerator mPresenceGenerator = mXmppConnectionService.getPresenceGenerator(); final Jid from = packet.getFrom(); if (from == null) { return; } final String type = packet.getAttribute("type"); final Contact contact = account.getRoster().getContact(from); if (type == null) { final String resource = from.isBareJid() ? "" : from.getResourcepart(); contact.setPresenceName(packet.findChildContent("nick", "http://jabber.org/protocol/nick")); Avatar avatar = Avatar.parsePresence(packet.findChild("x", "vcard-temp:x:update")); if (avatar != null && !contact.isSelf()) { avatar.owner = from.toBareJid(); if (AvatarUtil.isAvatarCached(avatar)) { if (contact.setAvatar(avatar)) { AvatarService.getInstance().clear(contact); UiUpdateHelper.updateConversationUi(); UiUpdateHelper.updateRosterUi(); } } else { AvatarService.getInstance().fetchAvatar(account, avatar); } } int sizeBefore = contact.getPresences().size(); final String show = packet.findChildContent("show"); final Element caps = packet.findChild("c", "http://jabber.org/protocol/caps"); final Presence presence = Presence.parse(show, caps); contact.updatePresence(resource, presence); if (presence.hasCaps() && Config.REQUEST_DISCO) { mXmppConnectionService.fetchCaps(account, from, presence); } PgpEngine pgp = mXmppConnectionService.getPgpEngine(); Element x = packet.findChild("x", "jabber:x:signed"); if (pgp != null && x != null) { Element status = packet.findChild("status"); String msg = status != null ? status.getContent() : ""; contact.setPgpKeyId(pgp.fetchKeyId(account, msg, x.getContent())); } boolean online = sizeBefore < contact.getPresences().size(); updateLastseen(packet, account, false); mXmppConnectionService.onContactStatusChanged.onContactStatusChanged(contact, online); } else if (type.equals("unavailable")) { if (from.isBareJid()) { contact.clearPresences(); } else { contact.removePresence(from.getResourcepart()); } mXmppConnectionService.onContactStatusChanged.onContactStatusChanged(contact, false); } else if (type.equals("subscribe")) { if (contact.getOption(Contact.Options.PREEMPTIVE_GRANT)) { mXmppConnectionService.sendPresencePacket(account, mPresenceGenerator.sendPresenceUpdatesTo(contact)); } else { contact.setOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST); final Conversation conversation = mXmppConnectionService.findOrCreateConversation( account, contact.getJid().toBareJid(), false); final String statusMessage = packet.findChildContent("status"); if (statusMessage != null && !statusMessage.isEmpty() && conversation.countMessages() == 0) { conversation.add(new Message( conversation, statusMessage, Message.ENCRYPTION_NONE, Message.STATUS_RECEIVED )); } } } UiUpdateHelper.updateRosterUi(); } @Override public void onPresencePacketReceived(Account account, PresencePacket packet) { if (packet.hasChild("x", "http://jabber.org/protocol/muc#user")) { this.parseConferencePresence(packet, account); } else if (packet.hasChild("x", "http://jabber.org/protocol/muc")) { this.parseConferencePresence(packet, account); } else { this.parseContactPresence(packet, account); } } }