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