MucOptions.java

  1package eu.siacs.conversations.entities;
  2
  3import java.util.ArrayList;
  4import java.util.List;
  5import java.util.concurrent.CopyOnWriteArrayList;
  6
  7import eu.siacs.conversations.R;
  8import eu.siacs.conversations.crypto.PgpEngine;
  9import eu.siacs.conversations.xml.Element;
 10import eu.siacs.conversations.xmpp.jid.InvalidJidException;
 11import eu.siacs.conversations.xmpp.jid.Jid;
 12import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
 13
 14import android.annotation.SuppressLint;
 15
 16@SuppressLint("DefaultLocale")
 17public class MucOptions {
 18
 19	public enum Affiliation {
 20		OWNER("owner", 4, R.string.owner),
 21		ADMIN("admin", 3, R.string.admin),
 22		MEMBER("member", 2, R.string.member),
 23		OUTCAST("outcast", 0, R.string.outcast),
 24		NONE("none", 1, R.string.no_affiliation);
 25
 26		private Affiliation(String string, int rank, int resId) {
 27			this.string = string;
 28			this.resId = resId;
 29			this.rank = rank;
 30		}
 31
 32		private String string;
 33		private int resId;
 34		private int rank;
 35
 36		public int getResId() {
 37			return resId;
 38		}
 39
 40		@Override
 41		public String toString() {
 42			return this.string;
 43		}
 44
 45		public boolean outranks(Affiliation affiliation) {
 46			return rank > affiliation.rank;
 47		}
 48
 49		public boolean ranks(Affiliation affiliation) {
 50			return rank >= affiliation.rank;
 51		}
 52	}
 53
 54	;
 55
 56	public enum Role {
 57		MODERATOR("moderator", R.string.moderator),
 58		VISITOR("visitor", R.string.visitor),
 59		PARTICIPANT("participant", R.string.participant),
 60		NONE("none", R.string.no_role);
 61
 62		private Role(String string, int resId) {
 63			this.string = string;
 64			this.resId = resId;
 65		}
 66
 67		private String string;
 68		private int resId;
 69
 70		public int getResId() {
 71			return resId;
 72		}
 73
 74		@Override
 75		public String toString() {
 76			return this.string;
 77		}
 78	}
 79
 80	public static final int ERROR_NO_ERROR = 0;
 81	public static final int ERROR_NICK_IN_USE = 1;
 82	public static final int ERROR_UNKNOWN = 2;
 83	public static final int ERROR_PASSWORD_REQUIRED = 3;
 84	public static final int ERROR_BANNED = 4;
 85	public static final int ERROR_MEMBERS_ONLY = 5;
 86
 87	public static final int KICKED_FROM_ROOM = 9;
 88
 89	public static final String STATUS_CODE_SELF_PRESENCE = "110";
 90	public static final String STATUS_CODE_BANNED = "301";
 91	public static final String STATUS_CODE_CHANGED_NICK = "303";
 92	public static final String STATUS_CODE_KICKED = "307";
 93	public static final String STATUS_CODE_LOST_MEMBERSHIP = "321";
 94
 95	private interface OnEventListener {
 96		public void onSuccess();
 97
 98		public void onFailure();
 99	}
100
101	public interface OnRenameListener extends OnEventListener {
102
103	}
104
105	public interface OnJoinListener extends OnEventListener {
106
107	}
108
109	public class User {
110		private Role role;
111		private Affiliation affiliation;
112		private String name;
113		private Jid jid;
114		private long pgpKeyId = 0;
115
116		public String getName() {
117			return name;
118		}
119
120		public void setName(String user) {
121			this.name = user;
122		}
123
124		public void setJid(Jid jid) {
125			this.jid = jid;
126		}
127
128		public Jid getJid() {
129			return this.jid;
130		}
131
132		public Role getRole() {
133			return this.role;
134		}
135
136		public void setRole(String role) {
137			role = role.toLowerCase();
138			switch (role) {
139				case "moderator":
140					this.role = Role.MODERATOR;
141					break;
142				case "participant":
143					this.role = Role.PARTICIPANT;
144					break;
145				case "visitor":
146					this.role = Role.VISITOR;
147					break;
148				default:
149					this.role = Role.NONE;
150					break;
151			}
152		}
153
154		public Affiliation getAffiliation() {
155			return this.affiliation;
156		}
157
158		public void setAffiliation(String affiliation) {
159			affiliation = affiliation.toLowerCase();
160			switch (affiliation) {
161				case "admin":
162					this.affiliation = Affiliation.ADMIN;
163					break;
164				case "owner":
165					this.affiliation = Affiliation.OWNER;
166					break;
167				case "member":
168					this.affiliation = Affiliation.MEMBER;
169					break;
170				case "outcast":
171					this.affiliation = Affiliation.OUTCAST;
172					break;
173				default:
174					this.affiliation = Affiliation.NONE;
175			}
176		}
177
178		public void setPgpKeyId(long id) {
179			this.pgpKeyId = id;
180		}
181
182		public long getPgpKeyId() {
183			return this.pgpKeyId;
184		}
185
186		public Contact getContact() {
187			return account.getRoster().getContactFromRoster(getJid());
188		}
189	}
190
191	private Account account;
192	private List<User> users = new CopyOnWriteArrayList<>();
193	private Conversation conversation;
194	private boolean isOnline = false;
195	private int error = ERROR_UNKNOWN;
196	private OnRenameListener onRenameListener = null;
197	private OnJoinListener onJoinListener = null;
198	private User self = new User();
199	private String subject = null;
200	private String password = null;
201	private boolean mNickChangingInProgress = false;
202
203	public MucOptions(Conversation conversation) {
204		this.account = conversation.getAccount();
205		this.conversation = conversation;
206	}
207
208	public void deleteUser(String name) {
209		for (int i = 0; i < users.size(); ++i) {
210			if (users.get(i).getName().equals(name)) {
211				users.remove(i);
212				return;
213			}
214		}
215	}
216
217	public void addUser(User user) {
218		for (int i = 0; i < users.size(); ++i) {
219			if (users.get(i).getName().equals(user.getName())) {
220				users.set(i, user);
221				return;
222			}
223		}
224		users.add(user);
225	}
226
227	public void processPacket(PresencePacket packet, PgpEngine pgp) {
228		final Jid from = packet.getFrom();
229		if (!from.isBareJid()) {
230			final String name = from.getResourcepart();
231			final String type = packet.getAttribute("type");
232			final Element x = packet.findChild("x", "http://jabber.org/protocol/muc#user");
233			final List<String> codes = getStatusCodes(x);
234			if (type == null) {
235				User user = new User();
236				if (x != null) {
237					Element item = x.findChild("item");
238					if (item != null) {
239						user.setName(name);
240						user.setAffiliation(item.getAttribute("affiliation"));
241						user.setRole(item.getAttribute("role"));
242						user.setJid(item.getAttributeAsJid("jid"));
243						if (codes.contains(STATUS_CODE_SELF_PRESENCE) || packet.getFrom().equals(this.conversation.getJid())) {
244							this.isOnline = true;
245							this.error = ERROR_NO_ERROR;
246							self = user;
247							if (mNickChangingInProgress) {
248								onRenameListener.onSuccess();
249								mNickChangingInProgress = false;
250							} else if (this.onJoinListener != null) {
251								this.onJoinListener.onSuccess();
252								this.onJoinListener = null;
253							}
254						} else {
255							addUser(user);
256						}
257						if (pgp != null) {
258							Element signed = packet.findChild("x", "jabber:x:signed");
259							if (signed != null) {
260								Element status = packet.findChild("status");
261								String msg;
262								if (status != null) {
263									msg = status.getContent();
264								} else {
265									msg = "";
266								}
267								user.setPgpKeyId(pgp.fetchKeyId(account, msg,
268										signed.getContent()));
269							}
270						}
271					}
272				}
273			} else if (type.equals("unavailable")) {
274				if (codes.contains(STATUS_CODE_SELF_PRESENCE) ||
275						packet.getFrom().equals(this.conversation.getJid())) {
276					if (codes.contains(STATUS_CODE_CHANGED_NICK)) {
277						this.mNickChangingInProgress = true;
278					} else if (codes.contains(STATUS_CODE_KICKED)) {
279						setError(KICKED_FROM_ROOM);
280					} else if (codes.contains(STATUS_CODE_BANNED)) {
281						setError(ERROR_BANNED);
282					} else if (codes.contains(STATUS_CODE_LOST_MEMBERSHIP)) {
283						setError(ERROR_MEMBERS_ONLY);
284					} else {
285						setError(ERROR_UNKNOWN);
286					}
287				} else {
288					deleteUser(name);
289				}
290			} else if (type.equals("error")) {
291				Element error = packet.findChild("error");
292				if (error != null && error.hasChild("conflict")) {
293					if (isOnline) {
294						if (onRenameListener != null) {
295							onRenameListener.onFailure();
296						}
297					} else {
298						setError(ERROR_NICK_IN_USE);
299					}
300				} else if (error != null && error.hasChild("not-authorized")) {
301					setError(ERROR_PASSWORD_REQUIRED);
302				} else if (error != null && error.hasChild("forbidden")) {
303					setError(ERROR_BANNED);
304				} else if (error != null && error.hasChild("registration-required")) {
305					setError(ERROR_MEMBERS_ONLY);
306				} else {
307					setError(ERROR_UNKNOWN);
308				}
309			}
310		}
311	}
312
313	private void setError(int error) {
314		this.isOnline = false;
315		this.error = error;
316		if (onJoinListener != null) {
317			onJoinListener.onFailure();
318			onJoinListener = null;
319		}
320	}
321
322	private List<String> getStatusCodes(Element x) {
323		List<String> codes = new ArrayList<String>();
324		if (x != null) {
325			for (Element child : x.getChildren()) {
326				if (child.getName().equals("status")) {
327					String code = child.getAttribute("code");
328					if (code != null) {
329						codes.add(code);
330					}
331				}
332			}
333		}
334		return codes;
335	}
336
337	public List<User> getUsers() {
338		return this.users;
339	}
340
341	public String getProposedNick() {
342		if (conversation.getBookmark() != null
343				&& conversation.getBookmark().getNick() != null
344				&& !conversation.getBookmark().getNick().isEmpty()) {
345			return conversation.getBookmark().getNick();
346		} else if (!conversation.getJid().isBareJid()) {
347			return conversation.getJid().getResourcepart();
348		} else {
349			return account.getUsername();
350		}
351	}
352
353	public String getActualNick() {
354		if (this.self.getName() != null) {
355			return this.self.getName();
356		} else {
357			return this.getProposedNick();
358		}
359	}
360
361	public boolean online() {
362		return this.isOnline;
363	}
364
365	public int getError() {
366		return this.error;
367	}
368
369	public void setOnRenameListener(OnRenameListener listener) {
370		this.onRenameListener = listener;
371	}
372
373	public void setOnJoinListener(OnJoinListener listener) {
374		this.onJoinListener = listener;
375	}
376
377	public void setOffline() {
378		this.users.clear();
379		this.error = 0;
380		this.isOnline = false;
381	}
382
383	public User getSelf() {
384		return self;
385	}
386
387	public void setSubject(String content) {
388		this.subject = content;
389	}
390
391	public String getSubject() {
392		return this.subject;
393	}
394
395	public String createNameFromParticipants() {
396		if (users.size() >= 2) {
397			List<String> names = new ArrayList<String>();
398			for (User user : users) {
399				Contact contact = user.getContact();
400				if (contact != null && !contact.getDisplayName().isEmpty()) {
401					names.add(contact.getDisplayName().split("\\s+")[0]);
402				} else {
403					names.add(user.getName());
404				}
405			}
406			StringBuilder builder = new StringBuilder();
407			for (int i = 0; i < names.size(); ++i) {
408				builder.append(names.get(i));
409				if (i != names.size() - 1) {
410					builder.append(", ");
411				}
412			}
413			return builder.toString();
414		} else {
415			return null;
416		}
417	}
418
419	public long[] getPgpKeyIds() {
420		List<Long> ids = new ArrayList<>();
421		for (User user : getUsers()) {
422			if (user.getPgpKeyId() != 0) {
423				ids.add(user.getPgpKeyId());
424			}
425		}
426		long[] primitivLongArray = new long[ids.size()];
427		for (int i = 0; i < ids.size(); ++i) {
428			primitivLongArray[i] = ids.get(i);
429		}
430		return primitivLongArray;
431	}
432
433	public boolean pgpKeysInUse() {
434		for (User user : getUsers()) {
435			if (user.getPgpKeyId() != 0) {
436				return true;
437			}
438		}
439		return false;
440	}
441
442	public boolean everybodyHasKeys() {
443		for (User user : getUsers()) {
444			if (user.getPgpKeyId() == 0) {
445				return false;
446			}
447		}
448		return true;
449	}
450
451	public Jid createJoinJid(String nick) {
452		try {
453			return Jid.fromString(this.conversation.getJid().toBareJid().toString() + "/" + nick);
454		} catch (final InvalidJidException e) {
455			return null;
456		}
457	}
458
459	public Jid getTrueCounterpart(String counterpart) {
460		for (User user : this.getUsers()) {
461			if (user.getName().equals(counterpart)) {
462				return user.getJid();
463			}
464		}
465		return null;
466	}
467
468	public String getPassword() {
469		this.password = conversation.getAttribute(Conversation.ATTRIBUTE_MUC_PASSWORD);
470		if (this.password == null && conversation.getBookmark() != null
471				&& conversation.getBookmark().getPassword() != null) {
472			return conversation.getBookmark().getPassword();
473		} else {
474			return this.password;
475		}
476	}
477
478	public void setPassword(String password) {
479		if (conversation.getBookmark() != null) {
480			conversation.getBookmark().setPassword(password);
481		} else {
482			this.password = password;
483		}
484		conversation.setAttribute(Conversation.ATTRIBUTE_MUC_PASSWORD, password);
485	}
486
487	public Conversation getConversation() {
488		return this.conversation;
489	}
490}