1package eu.siacs.conversations.entities;
2
3import android.content.ContentValues;
4import android.database.Cursor;
5import android.os.SystemClock;
6import android.util.Log;
7import androidx.annotation.NonNull;
8import com.google.common.base.Strings;
9import com.google.common.collect.ImmutableList;
10import eu.siacs.conversations.Config;
11import eu.siacs.conversations.R;
12import eu.siacs.conversations.crypto.PgpDecryptionService;
13import eu.siacs.conversations.crypto.axolotl.AxolotlService;
14import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession;
15import eu.siacs.conversations.crypto.sasl.ChannelBinding;
16import eu.siacs.conversations.crypto.sasl.ChannelBindingMechanism;
17import eu.siacs.conversations.crypto.sasl.HashedToken;
18import eu.siacs.conversations.crypto.sasl.HashedTokenSha256;
19import eu.siacs.conversations.crypto.sasl.HashedTokenSha512;
20import eu.siacs.conversations.crypto.sasl.SaslMechanism;
21import eu.siacs.conversations.services.AvatarService;
22import eu.siacs.conversations.services.XmppConnectionService;
23import eu.siacs.conversations.utils.UIHelper;
24import eu.siacs.conversations.utils.XmppUri;
25import eu.siacs.conversations.xmpp.Jid;
26import eu.siacs.conversations.xmpp.XmppConnection;
27import eu.siacs.conversations.xmpp.jingle.RtpCapability;
28import java.util.ArrayList;
29import java.util.Collection;
30import java.util.HashMap;
31import java.util.HashSet;
32import java.util.List;
33import java.util.Map;
34import java.util.Set;
35import java.util.concurrent.CopyOnWriteArraySet;
36import org.json.JSONException;
37import org.json.JSONObject;
38
39public class Account extends AbstractEntity implements AvatarService.Avatarable {
40
41 public static final String TABLENAME = "accounts";
42
43 public static final String USERNAME = "username";
44 public static final String SERVER = "server";
45 public static final String PASSWORD = "password";
46 public static final String OPTIONS = "options";
47 public static final String ROSTERVERSION = "rosterversion";
48 public static final String KEYS = "keys";
49 public static final String AVATAR = "avatar";
50 public static final String DISPLAY_NAME = "display_name";
51 public static final String HOSTNAME = "hostname";
52 public static final String PORT = "port";
53 public static final String STATUS = "status";
54 public static final String STATUS_MESSAGE = "status_message";
55 public static final String RESOURCE = "resource";
56 public static final String PINNED_MECHANISM = "pinned_mechanism";
57 public static final String PINNED_CHANNEL_BINDING = "pinned_channel_binding";
58 public static final String FAST_MECHANISM = "fast_mechanism";
59 public static final String FAST_TOKEN = "fast_token";
60
61 public static final int OPTION_DISABLED = 1;
62 public static final int OPTION_REGISTER = 2;
63 public static final int OPTION_MAGIC_CREATE = 4;
64 public static final int OPTION_REQUIRES_ACCESS_MODE_CHANGE = 5;
65 public static final int OPTION_LOGGED_IN_SUCCESSFULLY = 6;
66 public static final int OPTION_HTTP_UPLOAD_AVAILABLE = 7;
67 public static final int OPTION_UNVERIFIED = 8;
68 public static final int OPTION_FIXED_USERNAME = 9;
69 public static final int OPTION_QUICKSTART_AVAILABLE = 10;
70 public static final int OPTION_SOFT_DISABLED = 11;
71
72 private static final String KEY_PGP_SIGNATURE = "pgp_signature";
73 private static final String KEY_PGP_ID = "pgp_id";
74 private static final String KEY_PINNED_MECHANISM = "pinned_mechanism";
75 public static final String KEY_PRE_AUTH_REGISTRATION_TOKEN = "pre_auth_registration";
76
77 protected final JSONObject keys;
78 private final Roster roster = new Roster(this);
79 private final Collection<Jid> blocklist = new CopyOnWriteArraySet<>();
80 public final Set<Conversation> pendingConferenceJoins = new HashSet<>();
81 public final Set<Conversation> pendingConferenceLeaves = new HashSet<>();
82 public final Set<Conversation> inProgressConferenceJoins = new HashSet<>();
83 public final Set<Conversation> inProgressConferencePings = new HashSet<>();
84 protected Jid jid;
85 protected String password;
86 protected int options = 0;
87 protected State status = State.OFFLINE;
88 private State lastErrorStatus = State.OFFLINE;
89 protected String resource;
90 protected String avatar;
91 protected String hostname = null;
92 protected int port = 5222;
93 protected boolean online = false;
94 private String rosterVersion;
95 private String displayName = null;
96 private AxolotlService axolotlService = null;
97 private PgpDecryptionService pgpDecryptionService = null;
98 private XmppConnection xmppConnection = null;
99 private long mEndGracePeriod = 0L;
100 private final Map<Jid, Bookmark> bookmarks = new HashMap<>();
101 private Presence.Status presenceStatus;
102 private String presenceStatusMessage;
103 private String pinnedMechanism;
104 private String pinnedChannelBinding;
105 private String fastMechanism;
106 private String fastToken;
107
108 public Account(final Jid jid, final String password) {
109 this(
110 java.util.UUID.randomUUID().toString(),
111 jid,
112 password,
113 0,
114 null,
115 "",
116 null,
117 null,
118 null,
119 5222,
120 Presence.Status.ONLINE,
121 null,
122 null,
123 null,
124 null,
125 null);
126 }
127
128 private Account(
129 final String uuid,
130 final Jid jid,
131 final String password,
132 final int options,
133 final String rosterVersion,
134 final String keys,
135 final String avatar,
136 String displayName,
137 String hostname,
138 int port,
139 final Presence.Status status,
140 String statusMessage,
141 final String pinnedMechanism,
142 final String pinnedChannelBinding,
143 final String fastMechanism,
144 final String fastToken) {
145 this.uuid = uuid;
146 this.jid = jid;
147 this.password = password;
148 this.options = options;
149 this.rosterVersion = rosterVersion;
150 JSONObject tmp;
151 try {
152 tmp = new JSONObject(keys);
153 } catch (JSONException e) {
154 tmp = new JSONObject();
155 }
156 this.keys = tmp;
157 this.avatar = avatar;
158 this.displayName = displayName;
159 this.hostname = hostname;
160 this.port = port;
161 this.presenceStatus = status;
162 this.presenceStatusMessage = statusMessage;
163 this.pinnedMechanism = pinnedMechanism;
164 this.pinnedChannelBinding = pinnedChannelBinding;
165 this.fastMechanism = fastMechanism;
166 this.fastToken = fastToken;
167 }
168
169 public static Account fromCursor(final Cursor cursor) {
170 final Jid jid;
171 try {
172 final String resource = cursor.getString(cursor.getColumnIndexOrThrow(RESOURCE));
173 jid =
174 Jid.of(
175 cursor.getString(cursor.getColumnIndexOrThrow(USERNAME)),
176 cursor.getString(cursor.getColumnIndexOrThrow(SERVER)),
177 resource == null || resource.trim().isEmpty() ? null : resource);
178 } catch (final IllegalArgumentException e) {
179 Log.d(
180 Config.LOGTAG,
181 cursor.getString(cursor.getColumnIndexOrThrow(USERNAME))
182 + "@"
183 + cursor.getString(cursor.getColumnIndexOrThrow(SERVER)));
184 throw new AssertionError(e);
185 }
186 return new Account(
187 cursor.getString(cursor.getColumnIndexOrThrow(UUID)),
188 jid,
189 cursor.getString(cursor.getColumnIndexOrThrow(PASSWORD)),
190 cursor.getInt(cursor.getColumnIndexOrThrow(OPTIONS)),
191 cursor.getString(cursor.getColumnIndexOrThrow(ROSTERVERSION)),
192 cursor.getString(cursor.getColumnIndexOrThrow(KEYS)),
193 cursor.getString(cursor.getColumnIndexOrThrow(AVATAR)),
194 cursor.getString(cursor.getColumnIndexOrThrow(DISPLAY_NAME)),
195 cursor.getString(cursor.getColumnIndexOrThrow(HOSTNAME)),
196 cursor.getInt(cursor.getColumnIndexOrThrow(PORT)),
197 Presence.Status.fromShowString(
198 cursor.getString(cursor.getColumnIndexOrThrow(STATUS))),
199 cursor.getString(cursor.getColumnIndexOrThrow(STATUS_MESSAGE)),
200 cursor.getString(cursor.getColumnIndexOrThrow(PINNED_MECHANISM)),
201 cursor.getString(cursor.getColumnIndexOrThrow(PINNED_CHANNEL_BINDING)),
202 cursor.getString(cursor.getColumnIndexOrThrow(FAST_MECHANISM)),
203 cursor.getString(cursor.getColumnIndexOrThrow(FAST_TOKEN)));
204 }
205
206 public boolean httpUploadAvailable(long size) {
207 return xmppConnection != null && xmppConnection.getFeatures().httpUpload(size);
208 }
209
210 public boolean httpUploadAvailable() {
211 return isOptionSet(OPTION_HTTP_UPLOAD_AVAILABLE) || httpUploadAvailable(0);
212 }
213
214 public String getDisplayName() {
215 return displayName;
216 }
217
218 public void setDisplayName(String displayName) {
219 this.displayName = displayName;
220 }
221
222 public Contact getSelfContact() {
223 return getRoster().getContact(jid);
224 }
225
226 public boolean hasPendingPgpIntent(Conversation conversation) {
227 return pgpDecryptionService != null && pgpDecryptionService.hasPendingIntent(conversation);
228 }
229
230 public boolean isPgpDecryptionServiceConnected() {
231 return pgpDecryptionService != null && pgpDecryptionService.isConnected();
232 }
233
234 public boolean setShowErrorNotification(boolean newValue) {
235 boolean oldValue = showErrorNotification();
236 setKey("show_error", Boolean.toString(newValue));
237 return newValue != oldValue;
238 }
239
240 public boolean showErrorNotification() {
241 String key = getKey("show_error");
242 return key == null || Boolean.parseBoolean(key);
243 }
244
245 public boolean isEnabled() {
246 return !isOptionSet(Account.OPTION_DISABLED);
247 }
248
249 public boolean isConnectionEnabled() {
250 return !isOptionSet(Account.OPTION_DISABLED) && !isOptionSet(Account.OPTION_SOFT_DISABLED);
251 }
252
253 public boolean isOptionSet(final int option) {
254 return ((options & (1 << option)) != 0);
255 }
256
257 public boolean setOption(final int option, final boolean value) {
258 if (value && (option == OPTION_DISABLED || option == OPTION_SOFT_DISABLED)) {
259 this.setStatus(State.OFFLINE);
260 }
261 final int before = this.options;
262 if (value) {
263 this.options |= 1 << option;
264 } else {
265 this.options &= ~(1 << option);
266 }
267 return before != this.options;
268 }
269
270 public String getUsername() {
271 return jid.getEscapedLocal();
272 }
273
274 public boolean setJid(final Jid next) {
275 final Jid previousFull = this.jid;
276 final Jid prev = this.jid != null ? this.jid.asBareJid() : null;
277 final boolean changed = prev == null || (next != null && !prev.equals(next.asBareJid()));
278 if (changed) {
279 final AxolotlService oldAxolotlService = this.axolotlService;
280 if (oldAxolotlService != null) {
281 oldAxolotlService.destroy();
282 this.jid = next;
283 this.axolotlService = oldAxolotlService.makeNew();
284 }
285 }
286 this.jid = next;
287 return next != null && !next.equals(previousFull);
288 }
289
290 public Jid getDomain() {
291 return jid.getDomain();
292 }
293
294 public String getServer() {
295 return jid.getDomain().toEscapedString();
296 }
297
298 public String getPassword() {
299 return password;
300 }
301
302 public void setPassword(final String password) {
303 this.password = password;
304 }
305
306 @NonNull
307 public String getHostname() {
308 return Strings.nullToEmpty(this.hostname);
309 }
310
311 public void setHostname(final String hostname) {
312 this.hostname = hostname;
313 }
314
315 public boolean isOnion() {
316 final String server = getServer();
317 return server != null && server.endsWith(".onion");
318 }
319
320 public int getPort() {
321 return this.port;
322 }
323
324 public void setPort(int port) {
325 this.port = port;
326 }
327
328 public State getStatus() {
329 if (isOptionSet(OPTION_DISABLED)) {
330 return State.DISABLED;
331 } else if (isOptionSet(OPTION_SOFT_DISABLED)) {
332 return State.LOGGED_OUT;
333 } else {
334 return this.status;
335 }
336 }
337
338 public boolean unauthorized() {
339 return this.status == State.UNAUTHORIZED || this.lastErrorStatus == State.UNAUTHORIZED;
340 }
341
342 public State getLastErrorStatus() {
343 return this.lastErrorStatus;
344 }
345
346 public void setStatus(final State status) {
347 this.status = status;
348 if (status.isError || status == State.ONLINE) {
349 this.lastErrorStatus = status;
350 }
351 }
352
353 public void setPinnedMechanism(final SaslMechanism mechanism) {
354 this.pinnedMechanism = mechanism.getMechanism();
355 if (mechanism instanceof ChannelBindingMechanism) {
356 this.pinnedChannelBinding =
357 ((ChannelBindingMechanism) mechanism).getChannelBinding().toString();
358 } else {
359 this.pinnedChannelBinding = null;
360 }
361 }
362
363 public void setFastToken(final HashedToken.Mechanism mechanism, final String token) {
364 this.fastMechanism = mechanism.name();
365 this.fastToken = token;
366 }
367
368 public void resetFastToken() {
369 this.fastMechanism = null;
370 this.fastToken = null;
371 }
372
373 public void resetPinnedMechanism() {
374 this.pinnedMechanism = null;
375 this.pinnedChannelBinding = null;
376 setKey(Account.KEY_PINNED_MECHANISM, String.valueOf(-1));
377 }
378
379 public int getPinnedMechanismPriority() {
380 final int fallback = getKeyAsInt(KEY_PINNED_MECHANISM, -1);
381 if (Strings.isNullOrEmpty(this.pinnedMechanism)) {
382 return fallback;
383 }
384 final SaslMechanism saslMechanism = getPinnedMechanism();
385 if (saslMechanism == null) {
386 return fallback;
387 } else {
388 return saslMechanism.getPriority();
389 }
390 }
391
392 private SaslMechanism getPinnedMechanism() {
393 final String mechanism = Strings.nullToEmpty(this.pinnedMechanism);
394 final ChannelBinding channelBinding = ChannelBinding.get(this.pinnedChannelBinding);
395 return new SaslMechanism.Factory(this).of(mechanism, channelBinding);
396 }
397
398 public HashedToken getFastMechanism() {
399 final HashedToken.Mechanism fastMechanism =
400 HashedToken.Mechanism.ofOrNull(this.fastMechanism);
401 final String token = this.fastToken;
402 if (fastMechanism == null || Strings.isNullOrEmpty(token)) {
403 return null;
404 }
405 if (fastMechanism.hashFunction.equals("SHA-256")) {
406 return new HashedTokenSha256(this, fastMechanism.channelBinding);
407 } else if (fastMechanism.hashFunction.equals("SHA-512")) {
408 return new HashedTokenSha512(this, fastMechanism.channelBinding);
409 } else {
410 return null;
411 }
412 }
413
414 public SaslMechanism getQuickStartMechanism() {
415 final HashedToken hashedTokenMechanism = getFastMechanism();
416 if (hashedTokenMechanism != null) {
417 return hashedTokenMechanism;
418 }
419 return getPinnedMechanism();
420 }
421
422 public String getFastToken() {
423 return this.fastToken;
424 }
425
426 public State getTrueStatus() {
427 return this.status;
428 }
429
430 public boolean errorStatus() {
431 return getStatus().isError();
432 }
433
434 public boolean hasErrorStatus() {
435 return getXmppConnection() != null
436 && (getStatus().isError() || getStatus() == State.CONNECTING)
437 && getXmppConnection().getAttempt() >= 3;
438 }
439
440 public Presence.Status getPresenceStatus() {
441 return this.presenceStatus;
442 }
443
444 public void setPresenceStatus(Presence.Status status) {
445 this.presenceStatus = status;
446 }
447
448 public String getPresenceStatusMessage() {
449 return this.presenceStatusMessage;
450 }
451
452 public void setPresenceStatusMessage(String message) {
453 this.presenceStatusMessage = message;
454 }
455
456 public String getResource() {
457 return jid.getResource();
458 }
459
460 public void setResource(final String resource) {
461 this.jid = this.jid.withResource(resource);
462 }
463
464 public Jid getJid() {
465 return jid;
466 }
467
468 public JSONObject getKeys() {
469 return keys;
470 }
471
472 public String getKey(final String name) {
473 synchronized (this.keys) {
474 return this.keys.optString(name, null);
475 }
476 }
477
478 public int getKeyAsInt(final String name, int defaultValue) {
479 String key = getKey(name);
480 try {
481 return key == null ? defaultValue : Integer.parseInt(key);
482 } catch (NumberFormatException e) {
483 return defaultValue;
484 }
485 }
486
487 public boolean setKey(final String keyName, final String keyValue) {
488 synchronized (this.keys) {
489 try {
490 this.keys.put(keyName, keyValue);
491 return true;
492 } catch (final JSONException e) {
493 return false;
494 }
495 }
496 }
497
498 public void setPrivateKeyAlias(final String alias) {
499 setKey("private_key_alias", alias);
500 }
501
502 public String getPrivateKeyAlias() {
503 return getKey("private_key_alias");
504 }
505
506 @Override
507 public ContentValues getContentValues() {
508 final ContentValues values = new ContentValues();
509 values.put(UUID, uuid);
510 values.put(USERNAME, jid.getLocal());
511 values.put(SERVER, jid.getDomain().toEscapedString());
512 values.put(PASSWORD, password);
513 values.put(OPTIONS, options);
514 synchronized (this.keys) {
515 values.put(KEYS, this.keys.toString());
516 }
517 values.put(ROSTERVERSION, rosterVersion);
518 values.put(AVATAR, avatar);
519 values.put(DISPLAY_NAME, displayName);
520 values.put(HOSTNAME, hostname);
521 values.put(PORT, port);
522 values.put(STATUS, presenceStatus.toShowString());
523 values.put(STATUS_MESSAGE, presenceStatusMessage);
524 values.put(RESOURCE, jid.getResource());
525 values.put(PINNED_MECHANISM, pinnedMechanism);
526 values.put(PINNED_CHANNEL_BINDING, pinnedChannelBinding);
527 values.put(FAST_MECHANISM, this.fastMechanism);
528 values.put(FAST_TOKEN, this.fastToken);
529 return values;
530 }
531
532 public AxolotlService getAxolotlService() {
533 return axolotlService;
534 }
535
536 public void initAccountServices(final XmppConnectionService context) {
537 this.axolotlService = new AxolotlService(this, context);
538 this.pgpDecryptionService = new PgpDecryptionService(context);
539 if (xmppConnection != null) {
540 xmppConnection.addOnAdvancedStreamFeaturesAvailableListener(axolotlService);
541 }
542 }
543
544 public PgpDecryptionService getPgpDecryptionService() {
545 return this.pgpDecryptionService;
546 }
547
548 public XmppConnection getXmppConnection() {
549 return this.xmppConnection;
550 }
551
552 public void setXmppConnection(final XmppConnection connection) {
553 this.xmppConnection = connection;
554 }
555
556 public String getRosterVersion() {
557 if (this.rosterVersion == null) {
558 return "";
559 } else {
560 return this.rosterVersion;
561 }
562 }
563
564 public void setRosterVersion(final String version) {
565 this.rosterVersion = version;
566 }
567
568 public int countPresences() {
569 return this.getSelfContact().getPresences().size();
570 }
571
572 public int activeDevicesWithRtpCapability() {
573 int i = 0;
574 for (Presence presence : getSelfContact().getPresences().getPresences()) {
575 if (RtpCapability.check(presence) != RtpCapability.Capability.NONE) {
576 i++;
577 }
578 }
579 return i;
580 }
581
582 public String getPgpSignature() {
583 return getKey(KEY_PGP_SIGNATURE);
584 }
585
586 public boolean setPgpSignature(String signature) {
587 return setKey(KEY_PGP_SIGNATURE, signature);
588 }
589
590 public boolean unsetPgpSignature() {
591 synchronized (this.keys) {
592 return keys.remove(KEY_PGP_SIGNATURE) != null;
593 }
594 }
595
596 public long getPgpId() {
597 synchronized (this.keys) {
598 if (keys.has(KEY_PGP_ID)) {
599 try {
600 return keys.getLong(KEY_PGP_ID);
601 } catch (JSONException e) {
602 return 0;
603 }
604 } else {
605 return 0;
606 }
607 }
608 }
609
610 public boolean setPgpSignId(long pgpID) {
611 synchronized (this.keys) {
612 try {
613 if (pgpID == 0) {
614 keys.remove(KEY_PGP_ID);
615 } else {
616 keys.put(KEY_PGP_ID, pgpID);
617 }
618 } catch (JSONException e) {
619 return false;
620 }
621 return true;
622 }
623 }
624
625 public Roster getRoster() {
626 return this.roster;
627 }
628
629 public Collection<Bookmark> getBookmarks() {
630 synchronized (this.bookmarks) {
631 return ImmutableList.copyOf(this.bookmarks.values());
632 }
633 }
634
635 public void setBookmarks(final Map<Jid, Bookmark> bookmarks) {
636 synchronized (this.bookmarks) {
637 this.bookmarks.clear();
638 this.bookmarks.putAll(bookmarks);
639 }
640 }
641
642 public void putBookmark(final Bookmark bookmark) {
643 synchronized (this.bookmarks) {
644 this.bookmarks.put(bookmark.getJid(), bookmark);
645 }
646 }
647
648 public void removeBookmark(Bookmark bookmark) {
649 synchronized (this.bookmarks) {
650 this.bookmarks.remove(bookmark.getJid());
651 }
652 }
653
654 public void removeBookmark(Jid jid) {
655 synchronized (this.bookmarks) {
656 this.bookmarks.remove(jid);
657 }
658 }
659
660 public Set<Jid> getBookmarkedJids() {
661 synchronized (this.bookmarks) {
662 return new HashSet<>(this.bookmarks.keySet());
663 }
664 }
665
666 public Bookmark getBookmark(final Jid jid) {
667 synchronized (this.bookmarks) {
668 return this.bookmarks.get(jid.asBareJid());
669 }
670 }
671
672 public boolean setAvatar(final String filename) {
673 if (this.avatar != null && this.avatar.equals(filename)) {
674 return false;
675 } else {
676 this.avatar = filename;
677 return true;
678 }
679 }
680
681 public String getAvatar() {
682 return this.avatar;
683 }
684
685 public void activateGracePeriod(final long duration) {
686 if (duration > 0) {
687 this.mEndGracePeriod = SystemClock.elapsedRealtime() + duration;
688 }
689 }
690
691 public void deactivateGracePeriod() {
692 this.mEndGracePeriod = 0L;
693 }
694
695 public boolean inGracePeriod() {
696 return SystemClock.elapsedRealtime() < this.mEndGracePeriod;
697 }
698
699 public String getShareableUri() {
700 List<XmppUri.Fingerprint> fingerprints = this.getFingerprints();
701 String uri = "xmpp:" + this.getJid().asBareJid().toEscapedString();
702 if (fingerprints.size() > 0) {
703 return XmppUri.getFingerprintUri(uri, fingerprints, ';');
704 } else {
705 return uri;
706 }
707 }
708
709 public String getShareableLink() {
710 List<XmppUri.Fingerprint> fingerprints = this.getFingerprints();
711 String uri =
712 "https://conversations.im/i/"
713 + XmppUri.lameUrlEncode(this.getJid().asBareJid().toEscapedString());
714 if (fingerprints.size() > 0) {
715 return XmppUri.getFingerprintUri(uri, fingerprints, '&');
716 } else {
717 return uri;
718 }
719 }
720
721 private List<XmppUri.Fingerprint> getFingerprints() {
722 ArrayList<XmppUri.Fingerprint> fingerprints = new ArrayList<>();
723 if (axolotlService == null) {
724 return fingerprints;
725 }
726 fingerprints.add(
727 new XmppUri.Fingerprint(
728 XmppUri.FingerprintType.OMEMO,
729 axolotlService.getOwnFingerprint().substring(2),
730 axolotlService.getOwnDeviceId()));
731 for (XmppAxolotlSession session : axolotlService.findOwnSessions()) {
732 if (session.getTrust().isVerified() && session.getTrust().isActive()) {
733 fingerprints.add(
734 new XmppUri.Fingerprint(
735 XmppUri.FingerprintType.OMEMO,
736 session.getFingerprint().substring(2).replaceAll("\\s", ""),
737 session.getRemoteAddress().getDeviceId()));
738 }
739 }
740 return fingerprints;
741 }
742
743 public boolean isBlocked(final ListItem contact) {
744 final Jid jid = contact.getJid();
745 return jid != null
746 && (blocklist.contains(jid.asBareJid()) || blocklist.contains(jid.getDomain()));
747 }
748
749 public boolean isBlocked(final Jid jid) {
750 return jid != null && blocklist.contains(jid.asBareJid());
751 }
752
753 public Collection<Jid> getBlocklist() {
754 return this.blocklist;
755 }
756
757 public void clearBlocklist() {
758 getBlocklist().clear();
759 }
760
761 public boolean isOnlineAndConnected() {
762 return this.getStatus() == State.ONLINE && this.getXmppConnection() != null;
763 }
764
765 @Override
766 public int getAvatarBackgroundColor() {
767 return UIHelper.getColorForName(jid.asBareJid().toString());
768 }
769
770 @Override
771 public String getAvatarName() {
772 throw new IllegalStateException("This method should not be called");
773 }
774
775 public enum State {
776 DISABLED(false, false),
777 LOGGED_OUT(false, false),
778 OFFLINE(false),
779 CONNECTING(false),
780 ONLINE(false),
781 NO_INTERNET(false),
782 UNAUTHORIZED,
783 TEMPORARY_AUTH_FAILURE,
784 SERVER_NOT_FOUND,
785 REGISTRATION_SUCCESSFUL(false),
786 REGISTRATION_FAILED(true, false),
787 REGISTRATION_WEB(true, false),
788 REGISTRATION_CONFLICT(true, false),
789 REGISTRATION_NOT_SUPPORTED(true, false),
790 REGISTRATION_PLEASE_WAIT(true, false),
791 REGISTRATION_INVALID_TOKEN(true, false),
792 REGISTRATION_PASSWORD_TOO_WEAK(true, false),
793 TLS_ERROR,
794 TLS_ERROR_DOMAIN,
795 INCOMPATIBLE_SERVER,
796 INCOMPATIBLE_CLIENT,
797 TOR_NOT_AVAILABLE,
798 DOWNGRADE_ATTACK,
799 SESSION_FAILURE,
800 BIND_FAILURE,
801 HOST_UNKNOWN,
802 STREAM_ERROR,
803 SEE_OTHER_HOST,
804 STREAM_OPENING_ERROR,
805 POLICY_VIOLATION,
806 PAYMENT_REQUIRED,
807 MISSING_INTERNET_PERMISSION(false);
808
809 private final boolean isError;
810 private final boolean attemptReconnect;
811
812 State(final boolean isError) {
813 this(isError, true);
814 }
815
816 State(final boolean isError, final boolean reconnect) {
817 this.isError = isError;
818 this.attemptReconnect = reconnect;
819 }
820
821 State() {
822 this(true, true);
823 }
824
825 public boolean isError() {
826 return this.isError;
827 }
828
829 public boolean isAttemptReconnect() {
830 return this.attemptReconnect;
831 }
832
833 public int getReadableId() {
834 switch (this) {
835 case DISABLED:
836 return R.string.account_status_disabled;
837 case LOGGED_OUT:
838 return R.string.account_state_logged_out;
839 case ONLINE:
840 return R.string.account_status_online;
841 case CONNECTING:
842 return R.string.account_status_connecting;
843 case OFFLINE:
844 return R.string.account_status_offline;
845 case UNAUTHORIZED:
846 return R.string.account_status_unauthorized;
847 case SERVER_NOT_FOUND:
848 return R.string.account_status_not_found;
849 case NO_INTERNET:
850 return R.string.account_status_no_internet;
851 case REGISTRATION_FAILED:
852 return R.string.account_status_regis_fail;
853 case REGISTRATION_WEB:
854 return R.string.account_status_regis_web;
855 case REGISTRATION_CONFLICT:
856 return R.string.account_status_regis_conflict;
857 case REGISTRATION_SUCCESSFUL:
858 return R.string.account_status_regis_success;
859 case REGISTRATION_NOT_SUPPORTED:
860 return R.string.account_status_regis_not_sup;
861 case REGISTRATION_INVALID_TOKEN:
862 return R.string.account_status_regis_invalid_token;
863 case TLS_ERROR:
864 return R.string.account_status_tls_error;
865 case TLS_ERROR_DOMAIN:
866 return R.string.account_status_tls_error_domain;
867 case INCOMPATIBLE_SERVER:
868 return R.string.account_status_incompatible_server;
869 case INCOMPATIBLE_CLIENT:
870 return R.string.account_status_incompatible_client;
871 case TOR_NOT_AVAILABLE:
872 return R.string.account_status_tor_unavailable;
873 case BIND_FAILURE:
874 return R.string.account_status_bind_failure;
875 case SESSION_FAILURE:
876 return R.string.session_failure;
877 case DOWNGRADE_ATTACK:
878 return R.string.sasl_downgrade;
879 case HOST_UNKNOWN:
880 return R.string.account_status_host_unknown;
881 case POLICY_VIOLATION:
882 return R.string.account_status_policy_violation;
883 case REGISTRATION_PLEASE_WAIT:
884 return R.string.registration_please_wait;
885 case REGISTRATION_PASSWORD_TOO_WEAK:
886 return R.string.registration_password_too_weak;
887 case STREAM_ERROR:
888 return R.string.account_status_stream_error;
889 case STREAM_OPENING_ERROR:
890 return R.string.account_status_stream_opening_error;
891 case PAYMENT_REQUIRED:
892 return R.string.payment_required;
893 case SEE_OTHER_HOST:
894 return R.string.reconnect_on_other_host;
895 case MISSING_INTERNET_PERMISSION:
896 return R.string.missing_internet_permission;
897 case TEMPORARY_AUTH_FAILURE:
898 return R.string.account_status_temporary_auth_failure;
899 default:
900 return R.string.account_status_unknown;
901 }
902 }
903 }
904}