PresenceParser.java

  1package eu.siacs.conversations.parser;
  2
  3import android.util.Log;
  4
  5import java.util.ArrayList;
  6import java.util.List;
  7
  8
  9import eu.siacs.conversations.Config;
 10import eu.siacs.conversations.crypto.PgpEngine;
 11import eu.siacs.conversations.entities.Account;
 12import eu.siacs.conversations.entities.Contact;
 13import eu.siacs.conversations.entities.Conversation;
 14import eu.siacs.conversations.entities.Message;
 15import eu.siacs.conversations.entities.MucOptions;
 16import eu.siacs.conversations.entities.Presences;
 17import eu.siacs.conversations.generator.PresenceGenerator;
 18import eu.siacs.conversations.services.XmppConnectionService;
 19import eu.siacs.conversations.xml.Element;
 20import eu.siacs.conversations.xmpp.OnPresencePacketReceived;
 21import eu.siacs.conversations.xmpp.jid.Jid;
 22import eu.siacs.conversations.xmpp.pep.Avatar;
 23import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
 24
 25public class PresenceParser extends AbstractParser implements
 26		OnPresencePacketReceived {
 27
 28	public PresenceParser(XmppConnectionService service) {
 29		super(service);
 30	}
 31
 32	public void parseConferencePresence(PresencePacket packet, Account account) {
 33		final Conversation conversation = packet.getFrom() == null ? null : mXmppConnectionService.find(account, packet.getFrom().toBareJid());
 34		if (conversation != null) {
 35			final MucOptions mucOptions = conversation.getMucOptions();
 36			boolean before = mucOptions.online();
 37			int count = mucOptions.getUserCount();
 38			final List<MucOptions.User> tileUserBefore = mucOptions.getUsers(5);
 39			processConferencePresence(packet, mucOptions);
 40			final List<MucOptions.User> tileUserAfter = mucOptions.getUsers(5);
 41			if (!tileUserAfter.equals(tileUserBefore)) {
 42				Log.d(Config.LOGTAG,account.getJid().toBareJid()+": update tiles for "+conversation.getName());
 43				mXmppConnectionService.getAvatarService().clear(mucOptions);
 44			}
 45			if (before != mucOptions.online() || (mucOptions.online() && count != mucOptions.getUserCount())) {
 46				mXmppConnectionService.updateConversationUi();
 47			} else if (mucOptions.online()) {
 48				mXmppConnectionService.updateMucRosterUi();
 49			}
 50		}
 51	}
 52
 53	private void processConferencePresence(PresencePacket packet, MucOptions mucOptions) {
 54		final Jid from = packet.getFrom();
 55		if (!from.isBareJid()) {
 56			final String type = packet.getAttribute("type");
 57			final Element x = packet.findChild("x", "http://jabber.org/protocol/muc#user");
 58			Avatar avatar = Avatar.parsePresence(packet.findChild("x", "vcard-temp:x:update"));
 59			final List<String> codes = getStatusCodes(x);
 60			if (type == null) {
 61				if (x != null) {
 62					Element item = x.findChild("item");
 63					if (item != null && !from.isBareJid()) {
 64						mucOptions.setError(MucOptions.ERROR_NO_ERROR);
 65						MucOptions.User user = new MucOptions.User(mucOptions,from);
 66						user.setAffiliation(item.getAttribute("affiliation"));
 67						user.setRole(item.getAttribute("role"));
 68						user.setJid(item.getAttributeAsJid("jid"));
 69						if (codes.contains(MucOptions.STATUS_CODE_SELF_PRESENCE) || packet.getFrom().equals(mucOptions.getConversation().getJid())) {
 70							mucOptions.setOnline();
 71							mucOptions.setSelf(user);
 72							if (mucOptions.mNickChangingInProgress) {
 73								if (mucOptions.onRenameListener != null) {
 74									mucOptions.onRenameListener.onSuccess();
 75								}
 76								mucOptions.mNickChangingInProgress = false;
 77							}
 78						} else {
 79							mucOptions.addUser(user);
 80						}
 81						if (mXmppConnectionService.getPgpEngine() != null) {
 82							Element signed = packet.findChild("x", "jabber:x:signed");
 83							if (signed != null) {
 84								Element status = packet.findChild("status");
 85								String msg = status == null ? "" : status.getContent();
 86								long keyId = mXmppConnectionService.getPgpEngine().fetchKeyId(mucOptions.getAccount(), msg, signed.getContent());
 87								if (keyId != 0) {
 88									user.setPgpKeyId(keyId);
 89								}
 90							}
 91						}
 92						if (avatar != null) {
 93							avatar.owner = from;
 94							if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) {
 95								if (user.setAvatar(avatar)) {
 96									mXmppConnectionService.getAvatarService().clear(user);
 97								}
 98							} else {
 99								mXmppConnectionService.fetchAvatar(mucOptions.getAccount(), avatar);
100							}
101						}
102					}
103				}
104			} else if (type.equals("unavailable")) {
105				if (codes.contains(MucOptions.STATUS_CODE_SELF_PRESENCE) ||
106						packet.getFrom().equals(mucOptions.getConversation().getJid())) {
107					if (codes.contains(MucOptions.STATUS_CODE_CHANGED_NICK)) {
108						mucOptions.mNickChangingInProgress = true;
109					} else if (codes.contains(MucOptions.STATUS_CODE_KICKED)) {
110						mucOptions.setError(MucOptions.KICKED_FROM_ROOM);
111					} else if (codes.contains(MucOptions.STATUS_CODE_BANNED)) {
112						mucOptions.setError(MucOptions.ERROR_BANNED);
113					} else if (codes.contains(MucOptions.STATUS_CODE_LOST_MEMBERSHIP)) {
114						mucOptions.setError(MucOptions.ERROR_MEMBERS_ONLY);
115					} else {
116						mucOptions.setError(MucOptions.ERROR_UNKNOWN);
117						Log.d(Config.LOGTAG, "unknown error in conference: " + packet);
118					}
119				} else if (!from.isBareJid()){
120					MucOptions.User user = mucOptions.deleteUser(from.getResourcepart());
121					if (user != null) {
122						mXmppConnectionService.getAvatarService().clear(user);
123					}
124				}
125			} else if (type.equals("error")) {
126				Element error = packet.findChild("error");
127				if (error != null && error.hasChild("conflict")) {
128					if (mucOptions.online()) {
129						if (mucOptions.onRenameListener != null) {
130							mucOptions.onRenameListener.onFailure();
131						}
132					} else {
133						mucOptions.setError(MucOptions.ERROR_NICK_IN_USE);
134					}
135				} else if (error != null && error.hasChild("not-authorized")) {
136					mucOptions.setError(MucOptions.ERROR_PASSWORD_REQUIRED);
137				} else if (error != null && error.hasChild("forbidden")) {
138					mucOptions.setError(MucOptions.ERROR_BANNED);
139				} else if (error != null && error.hasChild("registration-required")) {
140					mucOptions.setError(MucOptions.ERROR_MEMBERS_ONLY);
141				}
142			}
143		}
144	}
145
146	private static List<String> getStatusCodes(Element x) {
147		List<String> codes = new ArrayList<>();
148		if (x != null) {
149			for (Element child : x.getChildren()) {
150				if (child.getName().equals("status")) {
151					String code = child.getAttribute("code");
152					if (code != null) {
153						codes.add(code);
154					}
155				}
156			}
157		}
158		return codes;
159	}
160
161	public void parseContactPresence(final PresencePacket packet, final Account account) {
162		final PresenceGenerator mPresenceGenerator = mXmppConnectionService.getPresenceGenerator();
163		final Jid from = packet.getFrom();
164		if (from == null) {
165			return;
166		}
167		final String type = packet.getAttribute("type");
168		final Contact contact = account.getRoster().getContact(from);
169		if (type == null) {
170			String presence = from.isBareJid() ? "" : from.getResourcepart();
171			contact.setPresenceName(packet.findChildContent("nick", "http://jabber.org/protocol/nick"));
172			Avatar avatar = Avatar.parsePresence(packet.findChild("x", "vcard-temp:x:update"));
173			if (avatar != null && !contact.isSelf()) {
174				avatar.owner = from.toBareJid();
175				if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) {
176					if (contact.setAvatar(avatar)) {
177						mXmppConnectionService.getAvatarService().clear(contact);
178						mXmppConnectionService.updateConversationUi();
179						mXmppConnectionService.updateRosterUi();
180					}
181				} else {
182					mXmppConnectionService.fetchAvatar(account, avatar);
183				}
184			}
185			int sizeBefore = contact.getPresences().size();
186			contact.updatePresence(presence, Presences.parseShow(packet.findChild("show")));
187			PgpEngine pgp = mXmppConnectionService.getPgpEngine();
188			Element x = packet.findChild("x", "jabber:x:signed");
189			if (pgp != null && x != null) {
190				Element status = packet.findChild("status");
191				String msg = status != null ? status.getContent() : "";
192				contact.setPgpKeyId(pgp.fetchKeyId(account, msg, x.getContent()));
193			}
194			boolean online = sizeBefore < contact.getPresences().size();
195			updateLastseen(packet, account, false);
196			mXmppConnectionService.onContactStatusChanged.onContactStatusChanged(contact, online);
197		} else if (type.equals("unavailable")) {
198			if (from.isBareJid()) {
199				contact.clearPresences();
200			} else {
201				contact.removePresence(from.getResourcepart());
202			}
203			mXmppConnectionService.onContactStatusChanged.onContactStatusChanged(contact, false);
204		} else if (type.equals("subscribe")) {
205			if (contact.getOption(Contact.Options.PREEMPTIVE_GRANT)) {
206				mXmppConnectionService.sendPresencePacket(account,
207						mPresenceGenerator.sendPresenceUpdatesTo(contact));
208			} else {
209				contact.setOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST);
210				final Conversation conversation = mXmppConnectionService.findOrCreateConversation(
211						account, contact.getJid().toBareJid(), false);
212				final String statusMessage = packet.findChildContent("status");
213				if (statusMessage != null
214						&& !statusMessage.isEmpty()
215						&& conversation.countMessages() == 0) {
216					conversation.add(new Message(
217							conversation,
218							statusMessage,
219							Message.ENCRYPTION_NONE,
220							Message.STATUS_RECEIVED
221					));
222				}
223			}
224		}
225		mXmppConnectionService.updateRosterUi();
226	}
227
228	@Override
229	public void onPresencePacketReceived(Account account, PresencePacket packet) {
230		if (packet.hasChild("x", "http://jabber.org/protocol/muc#user")) {
231			this.parseConferencePresence(packet, account);
232		} else if (packet.hasChild("x", "http://jabber.org/protocol/muc")) {
233			this.parseConferencePresence(packet, account);
234		} else {
235			this.parseContactPresence(packet, account);
236		}
237	}
238
239}