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