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