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