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