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