MucOptions.java

  1package eu.siacs.conversations.entities;
  2
  3import android.annotation.SuppressLint;
  4
  5import java.util.ArrayList;
  6import java.util.Collections;
  7import java.util.HashSet;
  8import java.util.List;
  9import java.util.Set;
 10
 11import eu.siacs.conversations.R;
 12import eu.siacs.conversations.xmpp.forms.Data;
 13import eu.siacs.conversations.xmpp.forms.Field;
 14import eu.siacs.conversations.xmpp.jid.InvalidJidException;
 15import eu.siacs.conversations.xmpp.jid.Jid;
 16import eu.siacs.conversations.xmpp.pep.Avatar;
 17
 18@SuppressLint("DefaultLocale")
 19public class MucOptions {
 20
 21	public Account getAccount() {
 22		return this.conversation.getAccount();
 23	}
 24
 25	public void setSelf(User user) {
 26		this.self = user;
 27	}
 28
 29	public void changeAffiliation(Jid jid, Affiliation affiliation) {
 30		User user = findUserByRealJid(jid);
 31		if (user != null) {
 32			users.remove(user);
 33			user.affiliation = affiliation;
 34			users.add(user);
 35		}
 36	}
 37
 38	public enum Affiliation {
 39		OWNER("owner", 4, R.string.owner),
 40		ADMIN("admin", 3, R.string.admin),
 41		MEMBER("member", 2, R.string.member),
 42		OUTCAST("outcast", 0, R.string.outcast),
 43		NONE("none", 1, R.string.no_affiliation);
 44
 45		Affiliation(String string, int rank, int resId) {
 46			this.string = string;
 47			this.resId = resId;
 48			this.rank = rank;
 49		}
 50
 51		private String string;
 52		private int resId;
 53		private int rank;
 54
 55		public int getResId() {
 56			return resId;
 57		}
 58
 59		@Override
 60		public String toString() {
 61			return this.string;
 62		}
 63
 64		public boolean outranks(Affiliation affiliation) {
 65			return rank > affiliation.rank;
 66		}
 67
 68		public boolean ranks(Affiliation affiliation) {
 69			return rank >= affiliation.rank;
 70		}
 71	}
 72
 73	public enum Role {
 74		MODERATOR("moderator", R.string.moderator,3),
 75		VISITOR("visitor", R.string.visitor,1),
 76		PARTICIPANT("participant", R.string.participant,2),
 77		NONE("none", R.string.no_role,0);
 78
 79		Role(String string, int resId, int rank) {
 80			this.string = string;
 81			this.resId = resId;
 82			this.rank = rank;
 83		}
 84
 85		private String string;
 86		private int resId;
 87		private int rank;
 88
 89		public int getResId() {
 90			return resId;
 91		}
 92
 93		@Override
 94		public String toString() {
 95			return this.string;
 96		}
 97
 98		public boolean ranks(Role role) {
 99			return rank >= role.rank;
100		}
101	}
102
103	public enum Error {
104		NO_RESPONSE,
105		NONE,
106		NICK_IN_USE,
107		PASSWORD_REQUIRED,
108		BANNED,
109		MEMBERS_ONLY,
110		KICKED,
111		SHUTDOWN,
112		UNKNOWN
113	}
114
115	public static final String STATUS_CODE_ROOM_CONFIG_CHANGED = "104";
116	public static final String STATUS_CODE_SELF_PRESENCE = "110";
117	public static final String STATUS_CODE_BANNED = "301";
118	public static final String STATUS_CODE_CHANGED_NICK = "303";
119	public static final String STATUS_CODE_KICKED = "307";
120	public static final String STATUS_CODE_AFFILIATION_CHANGE = "321";
121	public static final String STATUS_CODE_LOST_MEMBERSHIP = "322";
122	public static final String STATUS_CODE_SHUTDOWN = "332";
123
124	private interface OnEventListener {
125		void onSuccess();
126
127		void onFailure();
128	}
129
130	public interface OnRenameListener extends OnEventListener {
131
132	}
133
134	public static class User {
135		private Role role = Role.NONE;
136		private Affiliation affiliation = Affiliation.NONE;
137		private Jid realJid;
138		private Jid fullJid;
139		private long pgpKeyId = 0;
140		private Avatar avatar;
141		private MucOptions options;
142
143		public User(MucOptions options, Jid from) {
144			this.options = options;
145			this.fullJid = from;
146		}
147
148		public String getName() {
149			return fullJid == null ? null : fullJid.getResourcepart();
150		}
151
152		public void setRealJid(Jid jid) {
153			this.realJid = jid != null ? jid.toBareJid() : null;
154		}
155
156		public Jid getRealJid() {
157			return this.realJid;
158		}
159
160		public Role getRole() {
161			return this.role;
162		}
163
164		public void setRole(String role) {
165			if (role == null) {
166				this.role = Role.NONE;
167				return;
168			}
169			role = role.toLowerCase();
170			switch (role) {
171				case "moderator":
172					this.role = Role.MODERATOR;
173					break;
174				case "participant":
175					this.role = Role.PARTICIPANT;
176					break;
177				case "visitor":
178					this.role = Role.VISITOR;
179					break;
180				default:
181					this.role = Role.NONE;
182					break;
183			}
184		}
185
186		public Affiliation getAffiliation() {
187			return this.affiliation;
188		}
189
190		public void setAffiliation(String affiliation) {
191			if (affiliation == null) {
192				this.affiliation = Affiliation.NONE;
193				return;
194			}
195			affiliation = affiliation.toLowerCase();
196			switch (affiliation) {
197				case "admin":
198					this.affiliation = Affiliation.ADMIN;
199					break;
200				case "owner":
201					this.affiliation = Affiliation.OWNER;
202					break;
203				case "member":
204					this.affiliation = Affiliation.MEMBER;
205					break;
206				case "outcast":
207					this.affiliation = Affiliation.OUTCAST;
208					break;
209				default:
210					this.affiliation = Affiliation.NONE;
211			}
212		}
213
214		public void setPgpKeyId(long id) {
215			this.pgpKeyId = id;
216		}
217
218		public long getPgpKeyId() {
219			return this.pgpKeyId;
220		}
221
222		public Contact getContact() {
223			return getAccount().getRoster().getContactFromRoster(getRealJid());
224		}
225
226		public boolean setAvatar(Avatar avatar) {
227			if (this.avatar != null && this.avatar.equals(avatar)) {
228				return false;
229			} else {
230				this.avatar = avatar;
231				return true;
232			}
233		}
234
235		public String getAvatar() {
236			return avatar == null ? null : avatar.getFilename();
237		}
238
239		public Account getAccount() {
240			return options.getAccount();
241		}
242
243		public Jid getFullJid() {
244			return fullJid;
245		}
246
247		@Override
248		public boolean equals(Object o) {
249			if (this == o) return true;
250			if (o == null || getClass() != o.getClass()) return false;
251
252			User user = (User) o;
253
254			if (role != user.role) return false;
255			if (affiliation != user.affiliation) return false;
256			if (realJid != null ? !realJid.equals(user.realJid) : user.realJid != null)
257				return false;
258			return fullJid != null ? fullJid.equals(user.fullJid) : user.fullJid == null;
259
260		}
261
262		@Override
263		public int hashCode() {
264			int result = role != null ? role.hashCode() : 0;
265			result = 31 * result + (affiliation != null ? affiliation.hashCode() : 0);
266			result = 31 * result + (realJid != null ? realJid.hashCode() : 0);
267			result = 31 * result + (fullJid != null ? fullJid.hashCode() : 0);
268			return result;
269		}
270
271		@Override
272		public String toString() {
273			return "[fulljid:"+String.valueOf(fullJid)+",realjid:"+String.valueOf(realJid)+",affiliation"+affiliation.toString()+"]";
274		}
275	}
276
277	private Account account;
278	private final Set<User> users = Collections.synchronizedSet(new HashSet<User>());
279	private final List<String> features = new ArrayList<>();
280	private Data form = new Data();
281	private Conversation conversation;
282	private boolean isOnline = false;
283	private Error error = Error.NONE;
284	public OnRenameListener onRenameListener = null;
285	private User self;
286	private String subject = null;
287	private String password = null;
288	public boolean mNickChangingInProgress = false;
289
290	public MucOptions(Conversation conversation) {
291		this.account = conversation.getAccount();
292		this.conversation = conversation;
293		this.self = new User(this,createJoinJid(getProposedNick()));
294	}
295
296	public void updateFeatures(ArrayList<String> features) {
297		this.features.clear();
298		this.features.addAll(features);
299	}
300
301	public void updateFormData(Data form) {
302		this.form = form;
303	}
304
305	public boolean hasFeature(String feature) {
306		return this.features.contains(feature);
307	}
308
309	public boolean canInvite() {
310		Field field = this.form.getFieldByName("muc#roomconfig_allowinvites");
311		return !membersOnly() || self.getRole().ranks(Role.MODERATOR) || (field != null && "1".equals(field.getValue()));
312	}
313
314	public boolean canChangeSubject() {
315		Field field = this.form.getFieldByName("muc#roomconfig_changesubject");
316		return self.getRole().ranks(Role.MODERATOR) || (field != null && "1".equals(field.getValue()));
317	}
318
319	public boolean participating() {
320		return !online()
321				|| self.getRole().ranks(Role.PARTICIPANT)
322				|| hasFeature("muc_unmoderated");
323	}
324
325	public boolean membersOnly() {
326		return hasFeature("muc_membersonly");
327	}
328
329	public boolean mamSupport() {
330		// Update with "urn:xmpp:mam:1" once we support it
331		return hasFeature("urn:xmpp:mam:0");
332	}
333
334	public boolean nonanonymous() {
335		return hasFeature("muc_nonanonymous");
336	}
337
338	public boolean persistent() {
339		return hasFeature("muc_persistent");
340	}
341
342	public boolean moderated() {
343		return hasFeature("muc_moderated");
344	}
345
346	public User deleteUser(Jid jid) {
347		User user = findUserByFullJid(jid);
348		if (user != null) {
349			users.remove(user);
350			if (user.affiliation.ranks(Affiliation.MEMBER)) {
351				user.role = Role.NONE;
352				user.avatar = null;
353				user.fullJid = null;
354				users.add(user);
355			}
356		}
357		return user;
358	}
359
360	public void addUser(User user) {
361		User old;
362		if (user.fullJid == null && user.realJid != null) {
363			old = findUserByRealJid(user.realJid);
364			if (old != null) {
365				return; //don't add. user already exists
366			}
367		} else if (user.realJid != null) {
368			old = findUserByRealJid(user.realJid);
369			if (old != null && old.fullJid == null) {
370				users.remove(old);
371			}
372		}
373		old = findUserByFullJid(user.getFullJid());
374		if (old != null) {
375			users.remove(old);
376		}
377		this.users.add(user);
378	}
379
380	public User findUserByFullJid(Jid jid) {
381		if (jid == null) {
382			return null;
383		}
384		for(User user : users) {
385			if (jid.equals(user.getFullJid())) {
386				return user;
387			}
388		}
389		return null;
390	}
391
392	public User findUserByRealJid(Jid jid) {
393		if (jid == null) {
394			return null;
395		}
396		for(User user : users) {
397			if (jid.equals(user.getRealJid())) {
398				return user;
399			}
400		}
401		return null;
402	}
403
404	public boolean isUserInRoom(Jid jid) {
405		return findUserByFullJid(jid) != null;
406	}
407
408	public void setError(Error error) {
409		this.isOnline = isOnline && error == Error.NONE;
410		this.error = error;
411	}
412
413	public void setOnline() {
414		this.isOnline = true;
415	}
416
417	public ArrayList<User> getUsers() {
418		return new ArrayList<>(users);
419	}
420
421	public List<User> getUsers(int max) {
422		ArrayList<User> users = getUsers();
423		return users.subList(0, Math.min(max, users.size()));
424	}
425
426	public int getUserCount() {
427		return this.users.size();
428	}
429
430	public String getProposedNick() {
431		if (conversation.getBookmark() != null
432				&& conversation.getBookmark().getNick() != null
433				&& !conversation.getBookmark().getNick().isEmpty()) {
434			return conversation.getBookmark().getNick();
435		} else if (!conversation.getJid().isBareJid()) {
436			return conversation.getJid().getResourcepart();
437		} else {
438			return account.getUsername();
439		}
440	}
441
442	public String getActualNick() {
443		if (this.self.getName() != null) {
444			return this.self.getName();
445		} else {
446			return this.getProposedNick();
447		}
448	}
449
450	public boolean online() {
451		return this.isOnline;
452	}
453
454	public Error getError() {
455		return this.error;
456	}
457
458	public void setOnRenameListener(OnRenameListener listener) {
459		this.onRenameListener = listener;
460	}
461
462	public void setOffline() {
463		this.users.clear();
464		this.error = Error.NO_RESPONSE;
465		this.isOnline = false;
466	}
467
468	public User getSelf() {
469		return self;
470	}
471
472	public void setSubject(String content) {
473		this.subject = content;
474	}
475
476	public String getSubject() {
477		return this.subject;
478	}
479
480	public String createNameFromParticipants() {
481		if (users.size() >= 2) {
482			List<String> names = new ArrayList<>();
483			for (User user : getUsers(5)) {
484				Contact contact = user.getContact();
485				if (contact != null && !contact.getDisplayName().isEmpty()) {
486					names.add(contact.getDisplayName().split("\\s+")[0]);
487				} else if (user.getName() != null){
488					names.add(user.getName());
489				}
490			}
491			StringBuilder builder = new StringBuilder();
492			for (int i = 0; i < names.size(); ++i) {
493				builder.append(names.get(i));
494				if (i != names.size() - 1) {
495					builder.append(", ");
496				}
497			}
498			return builder.toString();
499		} else {
500			return null;
501		}
502	}
503
504	public long[] getPgpKeyIds() {
505		List<Long> ids = new ArrayList<>();
506		for (User user : this.users) {
507			if (user.getPgpKeyId() != 0) {
508				ids.add(user.getPgpKeyId());
509			}
510		}
511		ids.add(account.getPgpId());
512		long[] primitiveLongArray = new long[ids.size()];
513		for (int i = 0; i < ids.size(); ++i) {
514			primitiveLongArray[i] = ids.get(i);
515		}
516		return primitiveLongArray;
517	}
518
519	public boolean pgpKeysInUse() {
520		for (User user : this.users) {
521			if (user.getPgpKeyId() != 0) {
522				return true;
523			}
524		}
525		return false;
526	}
527
528	public boolean everybodyHasKeys() {
529		for (User user : this.users) {
530			if (user.getPgpKeyId() == 0) {
531				return false;
532			}
533		}
534		return true;
535	}
536
537	public Jid createJoinJid(String nick) {
538		try {
539			return Jid.fromString(this.conversation.getJid().toBareJid().toString() + "/" + nick);
540		} catch (final InvalidJidException e) {
541			return null;
542		}
543	}
544
545	public Jid getTrueCounterpart(Jid jid) {
546		if (jid.equals(getSelf().getFullJid())) {
547			return account.getJid().toBareJid();
548		}
549		User user = findUserByFullJid(jid);
550		return user == null ? null : user.getRealJid();
551	}
552
553	public String getPassword() {
554		this.password = conversation.getAttribute(Conversation.ATTRIBUTE_MUC_PASSWORD);
555		if (this.password == null && conversation.getBookmark() != null
556				&& conversation.getBookmark().getPassword() != null) {
557			return conversation.getBookmark().getPassword();
558		} else {
559			return this.password;
560		}
561	}
562
563	public void setPassword(String password) {
564		if (conversation.getBookmark() != null) {
565			conversation.getBookmark().setPassword(password);
566		} else {
567			this.password = password;
568		}
569		conversation.setAttribute(Conversation.ATTRIBUTE_MUC_PASSWORD, password);
570	}
571
572	public Conversation getConversation() {
573		return this.conversation;
574	}
575
576	public void putMember(Jid fullJid, Jid realJid, String affiliation, String role) {
577		User user = new User(this, fullJid);
578		user.setRealJid(realJid);
579		user.setAffiliation(affiliation);
580		user.setRole(role);
581		addUser(user);
582	}
583
584	public List<Jid> getMembers() {
585		ArrayList<Jid> members = new ArrayList<>();
586		for(User user : users) {
587			if (user.affiliation.ranks(Affiliation.MEMBER) && user.getRealJid() != null) {
588				members.add(user.getRealJid());
589			}
590		}
591		return members;
592	}
593}