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 void setAvatar(Avatar avatar) {
217 this.avatar = avatar;
218 }
219
220 public String getAvatar() {
221 return avatar == null ? null : avatar.getFilename();
222 }
223
224 public Account getAccount() {
225 return options.getAccount();
226 }
227
228 public Jid getFullJid() {
229 return fullJid;
230 }
231 }
232
233 private Account account;
234 private final List<User> users = new ArrayList<>();
235 private List<String> features = new ArrayList<>();
236 private Data form = new Data();
237 private Conversation conversation;
238 private boolean isOnline = false;
239 private int error = ERROR_UNKNOWN;
240 public OnRenameListener onRenameListener = null;
241 private User self;
242 private String subject = null;
243 private String password = null;
244 public boolean mNickChangingInProgress = false;
245
246 public MucOptions(Conversation conversation) {
247 this.account = conversation.getAccount();
248 this.conversation = conversation;
249 this.self = new User(this,conversation.getJid());
250 }
251
252 public void updateFeatures(ArrayList<String> features) {
253 this.features.clear();
254 this.features.addAll(features);
255 }
256
257 public void updateFormData(Data form) {
258 this.form = form;
259 }
260
261 public boolean hasFeature(String feature) {
262 return this.features.contains(feature);
263 }
264
265 public boolean canInvite() {
266 Field field = this.form.getFieldByName("muc#roomconfig_allowinvites");
267 return !membersOnly() || self.getRole().ranks(Role.MODERATOR) || (field != null && "1".equals(field.getValue()));
268 }
269
270 public boolean canChangeSubject() {
271 Field field = this.form.getFieldByName("muc#roomconfig_changesubject");
272 return self.getRole().ranks(Role.MODERATOR) || (field != null && "1".equals(field.getValue()));
273 }
274
275 public boolean participating() {
276 return !online() || self.getRole().ranks(Role.PARTICIPANT);
277 }
278
279 public boolean membersOnly() {
280 return hasFeature("muc_membersonly");
281 }
282
283 public boolean mamSupport() {
284 // Update with "urn:xmpp:mam:1" once we support it
285 return hasFeature("urn:xmpp:mam:0");
286 }
287
288 public boolean nonanonymous() {
289 return hasFeature("muc_nonanonymous");
290 }
291
292 public boolean persistent() {
293 return hasFeature("muc_persistent");
294 }
295
296 public boolean moderated() {
297 return hasFeature("muc_moderated");
298 }
299
300 public void deleteUser(String name) {
301 synchronized (this.users) {
302 for (int i = 0; i < users.size(); ++i) {
303 if (users.get(i).getName().equals(name)) {
304 users.remove(i);
305 return;
306 }
307 }
308 }
309 }
310
311 public void addUser(User user) {
312 synchronized (this.users) {
313 for (int i = 0; i < users.size(); ++i) {
314 if (users.get(i).getName().equals(user.getName())) {
315 users.set(i, user);
316 return;
317 }
318 }
319 users.add(user);
320 }
321 }
322
323 public User findUser(String name) {
324 if (name == null) {
325 return null;
326 }
327 synchronized (this.users) {
328 for (User user : users) {
329 if (user.getName().equals(name)) {
330 return user;
331 }
332 }
333 }
334 return null;
335 }
336
337 public boolean isUserInRoom(String name) {
338 return findUser(name) != null;
339 }
340
341 public void setError(int error) {
342 this.isOnline = error == ERROR_NO_ERROR;
343 this.error = error;
344 }
345
346 public ArrayList<User> getUsers() {
347 synchronized (this.users) {
348 return new ArrayList(this.users);
349 }
350 }
351
352 public List<User> getUsers(int max) {
353 synchronized (this.users) {
354 return new ArrayList<>(users.subList(0,Math.min(users.size(),5)));
355 }
356 }
357
358 public int getUserCount() {
359 synchronized (this.users) {
360 return this.users.size();
361 }
362 }
363
364 public String getProposedNick() {
365 if (conversation.getBookmark() != null
366 && conversation.getBookmark().getNick() != null
367 && !conversation.getBookmark().getNick().isEmpty()) {
368 return conversation.getBookmark().getNick();
369 } else if (!conversation.getJid().isBareJid()) {
370 return conversation.getJid().getResourcepart();
371 } else {
372 return account.getUsername();
373 }
374 }
375
376 public String getActualNick() {
377 if (this.self.getName() != null) {
378 return this.self.getName();
379 } else {
380 return this.getProposedNick();
381 }
382 }
383
384 public boolean online() {
385 return this.isOnline;
386 }
387
388 public int getError() {
389 return this.error;
390 }
391
392 public void setOnRenameListener(OnRenameListener listener) {
393 this.onRenameListener = listener;
394 }
395
396 public void setOffline() {
397 this.users.clear();
398 this.error = 0;
399 this.isOnline = false;
400 }
401
402 public User getSelf() {
403 return self;
404 }
405
406 public void setSubject(String content) {
407 this.subject = content;
408 }
409
410 public String getSubject() {
411 return this.subject;
412 }
413
414 public String createNameFromParticipants() {
415 synchronized (this.users) {
416 if (users.size() >= 2) {
417 List<String> names = new ArrayList<String>();
418 for (User user : users) {
419 Contact contact = user.getContact();
420 if (contact != null && !contact.getDisplayName().isEmpty()) {
421 names.add(contact.getDisplayName().split("\\s+")[0]);
422 } else {
423 names.add(user.getName());
424 }
425 }
426 StringBuilder builder = new StringBuilder();
427 for (int i = 0; i < names.size(); ++i) {
428 builder.append(names.get(i));
429 if (i != names.size() - 1) {
430 builder.append(", ");
431 }
432 }
433 return builder.toString();
434 } else {
435 return null;
436 }
437 }
438 }
439
440 public long[] getPgpKeyIds() {
441 List<Long> ids = new ArrayList<>();
442 synchronized (this.users) {
443 for (User user : this.users) {
444 if (user.getPgpKeyId() != 0) {
445 ids.add(user.getPgpKeyId());
446 }
447 }
448 }
449 ids.add(account.getPgpId());
450 long[] primitiveLongArray = new long[ids.size()];
451 for (int i = 0; i < ids.size(); ++i) {
452 primitiveLongArray[i] = ids.get(i);
453 }
454 return primitiveLongArray;
455 }
456
457 public boolean pgpKeysInUse() {
458 synchronized (this.users) {
459 for (User user : this.users) {
460 if (user.getPgpKeyId() != 0) {
461 return true;
462 }
463 }
464 }
465 return false;
466 }
467
468 public boolean everybodyHasKeys() {
469 synchronized (this.users) {
470 for (User user : this.users) {
471 if (user.getPgpKeyId() == 0) {
472 return false;
473 }
474 }
475 }
476 return true;
477 }
478
479 public Jid createJoinJid(String nick) {
480 try {
481 return Jid.fromString(this.conversation.getJid().toBareJid().toString() + "/" + nick);
482 } catch (final InvalidJidException e) {
483 return null;
484 }
485 }
486
487 public Jid getTrueCounterpart(String counterpart) {
488 synchronized (this.users) {
489 for (User user : this.users) {
490 if (user.getName().equals(counterpart)) {
491 return user.getJid();
492 }
493 }
494 }
495 return null;
496 }
497
498 public String getPassword() {
499 this.password = conversation.getAttribute(Conversation.ATTRIBUTE_MUC_PASSWORD);
500 if (this.password == null && conversation.getBookmark() != null
501 && conversation.getBookmark().getPassword() != null) {
502 return conversation.getBookmark().getPassword();
503 } else {
504 return this.password;
505 }
506 }
507
508 public void setPassword(String password) {
509 if (conversation.getBookmark() != null) {
510 conversation.getBookmark().setPassword(password);
511 } else {
512 this.password = password;
513 }
514 conversation.setAttribute(Conversation.ATTRIBUTE_MUC_PASSWORD, password);
515 }
516
517 public Conversation getConversation() {
518 return this.conversation;
519 }
520}