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 Contact getSelfContact() {
226 return getRoster().getContact(jid);
227 }
228
229 public boolean hasPendingPgpIntent(Conversation conversation) {
230 return pgpDecryptionService != null && pgpDecryptionService.hasPendingIntent(conversation);
231 }
232
233 public boolean isPgpDecryptionServiceConnected() {
234 return pgpDecryptionService != null && pgpDecryptionService.isConnected();
235 }
236
237 public boolean setShowErrorNotification(boolean newValue) {
238 boolean oldValue = showErrorNotification();
239 setKey("show_error", Boolean.toString(newValue));
240 return newValue != oldValue;
241 }
242
243 public boolean showErrorNotification() {
244 String key = getKey("show_error");
245 return key == null || Boolean.parseBoolean(key);
246 }
247
248 public boolean isEnabled() {
249 return !isOptionSet(Account.OPTION_DISABLED);
250 }
251
252 public boolean isOptionSet(final int option) {
253 return ((options & (1 << option)) != 0);
254 }
255
256 public boolean setOption(final int option, final boolean value) {
257 final int before = this.options;
258 if (value) {
259 this.options |= 1 << option;
260 } else {
261 this.options &= ~(1 << option);
262 }
263 return before != this.options;
264 }
265
266 public String getUsername() {
267 return jid.getEscapedLocal();
268 }
269
270 public boolean setJid(final Jid next) {
271 final Jid previousFull = this.jid;
272 final Jid prev = this.jid != null ? this.jid.asBareJid() : null;
273 final boolean changed = prev == null || (next != null && !prev.equals(next.asBareJid()));
274 if (changed) {
275 final AxolotlService oldAxolotlService = this.axolotlService;
276 if (oldAxolotlService != null) {
277 oldAxolotlService.destroy();
278 this.jid = next;
279 this.axolotlService = oldAxolotlService.makeNew();
280 }
281 }
282 this.jid = next;
283 return next != null && !next.equals(previousFull);
284 }
285
286 public Jid getDomain() {
287 return jid.getDomain();
288 }
289
290 public String getServer() {
291 return jid.getDomain().toEscapedString();
292 }
293
294 public String getPassword() {
295 return password;
296 }
297
298 public void setPassword(final String password) {
299 this.password = password;
300 }
301
302 public String getHostname() {
303 return Strings.nullToEmpty(this.hostname);
304 }
305
306 public void setHostname(String hostname) {
307 this.hostname = hostname;
308 }
309
310 public boolean isOnion() {
311 final String server = getServer();
312 return server != null && server.endsWith(".onion");
313 }
314
315 public int getPort() {
316 return this.port;
317 }
318
319 public void setPort(int port) {
320 this.port = port;
321 }
322
323 public State getStatus() {
324 if (isOptionSet(OPTION_DISABLED)) {
325 return State.DISABLED;
326 } else {
327 return this.status;
328 }
329 }
330
331 public State getLastErrorStatus() {
332 return this.lastErrorStatus;
333 }
334
335 public void setStatus(final State status) {
336 this.status = status;
337 if (status.isError || status == State.ONLINE) {
338 this.lastErrorStatus = status;
339 }
340 }
341
342 public void setPinnedMechanism(final SaslMechanism mechanism) {
343 this.pinnedMechanism = mechanism.getMechanism();
344 if (mechanism instanceof ChannelBindingMechanism) {
345 this.pinnedChannelBinding =
346 ((ChannelBindingMechanism) mechanism).getChannelBinding().toString();
347 } else {
348 this.pinnedChannelBinding = null;
349 }
350 }
351
352 public void setFastToken(final HashedToken.Mechanism mechanism, final String token) {
353 this.fastMechanism = mechanism.name();
354 this.fastToken = token;
355 }
356
357 public void resetFastToken() {
358 this.fastMechanism = null;
359 this.fastToken = null;
360 }
361
362 public void resetPinnedMechanism() {
363 this.pinnedMechanism = null;
364 this.pinnedChannelBinding = null;
365 setKey(Account.KEY_PINNED_MECHANISM, String.valueOf(-1));
366 }
367
368 public int getPinnedMechanismPriority() {
369 final int fallback = getKeyAsInt(KEY_PINNED_MECHANISM, -1);
370 if (Strings.isNullOrEmpty(this.pinnedMechanism)) {
371 return fallback;
372 }
373 final SaslMechanism saslMechanism = getPinnedMechanism();
374 if (saslMechanism == null) {
375 return fallback;
376 } else {
377 return saslMechanism.getPriority();
378 }
379 }
380
381 private SaslMechanism getPinnedMechanism() {
382 final String mechanism = Strings.nullToEmpty(this.pinnedMechanism);
383 final ChannelBinding channelBinding = ChannelBinding.get(this.pinnedChannelBinding);
384 return new SaslMechanism.Factory(this).of(mechanism, channelBinding);
385 }
386
387 public HashedToken getFastMechanism() {
388 final HashedToken.Mechanism fastMechanism = HashedToken.Mechanism.ofOrNull(this.fastMechanism);
389 final String token = this.fastToken;
390 if (fastMechanism == null || Strings.isNullOrEmpty(token)) {
391 return null;
392 }
393 if (fastMechanism.hashFunction.equals("SHA-256")) {
394 return new HashedTokenSha256(this, fastMechanism.channelBinding);
395 } else if (fastMechanism.hashFunction.equals("SHA-512")) {
396 return new HashedTokenSha512(this, fastMechanism.channelBinding);
397 } else {
398 return null;
399 }
400 }
401
402 public SaslMechanism getQuickStartMechanism() {
403 final HashedToken hashedTokenMechanism = getFastMechanism();
404 if (hashedTokenMechanism != null) {
405 return hashedTokenMechanism;
406 }
407 return getPinnedMechanism();
408 }
409
410 public String getFastToken() {
411 return this.fastToken;
412 }
413
414 public State getTrueStatus() {
415 return this.status;
416 }
417
418 public boolean errorStatus() {
419 return getStatus().isError();
420 }
421
422 public boolean hasErrorStatus() {
423 return getXmppConnection() != null
424 && (getStatus().isError() || getStatus() == State.CONNECTING)
425 && getXmppConnection().getAttempt() >= 3;
426 }
427
428 public Presence.Status getPresenceStatus() {
429 return this.presenceStatus;
430 }
431
432 public void setPresenceStatus(Presence.Status status) {
433 this.presenceStatus = status;
434 }
435
436 public String getPresenceStatusMessage() {
437 return this.presenceStatusMessage;
438 }
439
440 public void setPresenceStatusMessage(String message) {
441 this.presenceStatusMessage = message;
442 }
443
444 public String getResource() {
445 return jid.getResource();
446 }
447
448 public void setResource(final String resource) {
449 this.jid = this.jid.withResource(resource);
450 }
451
452 public Jid getJid() {
453 return jid;
454 }
455
456 public JSONObject getKeys() {
457 return keys;
458 }
459
460 public String getKey(final String name) {
461 synchronized (this.keys) {
462 return this.keys.optString(name, null);
463 }
464 }
465
466 public int getKeyAsInt(final String name, int defaultValue) {
467 String key = getKey(name);
468 try {
469 return key == null ? defaultValue : Integer.parseInt(key);
470 } catch (NumberFormatException e) {
471 return defaultValue;
472 }
473 }
474
475 public boolean setKey(final String keyName, final String keyValue) {
476 synchronized (this.keys) {
477 try {
478 this.keys.put(keyName, keyValue);
479 return true;
480 } catch (final JSONException e) {
481 return false;
482 }
483 }
484 }
485
486 public void setPrivateKeyAlias(final String alias) {
487 setKey("private_key_alias", alias);
488 }
489
490 public String getPrivateKeyAlias() {
491 return getKey("private_key_alias");
492 }
493
494 @Override
495 public ContentValues getContentValues() {
496 final ContentValues values = new ContentValues();
497 values.put(UUID, uuid);
498 values.put(USERNAME, jid.getLocal());
499 values.put(SERVER, jid.getDomain().toEscapedString());
500 values.put(PASSWORD, password);
501 values.put(OPTIONS, options);
502 synchronized (this.keys) {
503 values.put(KEYS, this.keys.toString());
504 }
505 values.put(ROSTERVERSION, rosterVersion);
506 values.put(AVATAR, avatar);
507 values.put(DISPLAY_NAME, displayName);
508 values.put(HOSTNAME, hostname);
509 values.put(PORT, port);
510 values.put(STATUS, presenceStatus.toShowString());
511 values.put(STATUS_MESSAGE, presenceStatusMessage);
512 values.put(RESOURCE, jid.getResource());
513 values.put(PINNED_MECHANISM, pinnedMechanism);
514 values.put(PINNED_CHANNEL_BINDING, pinnedChannelBinding);
515 values.put(FAST_MECHANISM, this.fastMechanism);
516 values.put(FAST_TOKEN, this.fastToken);
517 return values;
518 }
519
520 public AxolotlService getAxolotlService() {
521 return axolotlService;
522 }
523
524 public void initAccountServices(final XmppConnectionService context) {
525 this.axolotlService = new AxolotlService(this, context);
526 this.pgpDecryptionService = new PgpDecryptionService(context);
527 if (xmppConnection != null) {
528 xmppConnection.addOnAdvancedStreamFeaturesAvailableListener(axolotlService);
529 }
530 }
531
532 public PgpDecryptionService getPgpDecryptionService() {
533 return this.pgpDecryptionService;
534 }
535
536 public XmppConnection getXmppConnection() {
537 return this.xmppConnection;
538 }
539
540 public void setXmppConnection(final XmppConnection connection) {
541 this.xmppConnection = connection;
542 }
543
544 public String getRosterVersion() {
545 if (this.rosterVersion == null) {
546 return "";
547 } else {
548 return this.rosterVersion;
549 }
550 }
551
552 public void setRosterVersion(final String version) {
553 this.rosterVersion = version;
554 }
555
556 public int countPresences() {
557 return this.getSelfContact().getPresences().size();
558 }
559
560 public int activeDevicesWithRtpCapability() {
561 int i = 0;
562 for (Presence presence : getSelfContact().getPresences().getPresences()) {
563 if (RtpCapability.check(presence) != RtpCapability.Capability.NONE) {
564 i++;
565 }
566 }
567 return i;
568 }
569
570 public String getPgpSignature() {
571 return getKey(KEY_PGP_SIGNATURE);
572 }
573
574 public boolean setPgpSignature(String signature) {
575 return setKey(KEY_PGP_SIGNATURE, signature);
576 }
577
578 public boolean unsetPgpSignature() {
579 synchronized (this.keys) {
580 return keys.remove(KEY_PGP_SIGNATURE) != null;
581 }
582 }
583
584 public long getPgpId() {
585 synchronized (this.keys) {
586 if (keys.has(KEY_PGP_ID)) {
587 try {
588 return keys.getLong(KEY_PGP_ID);
589 } catch (JSONException e) {
590 return 0;
591 }
592 } else {
593 return 0;
594 }
595 }
596 }
597
598 public boolean setPgpSignId(long pgpID) {
599 synchronized (this.keys) {
600 try {
601 if (pgpID == 0) {
602 keys.remove(KEY_PGP_ID);
603 } else {
604 keys.put(KEY_PGP_ID, pgpID);
605 }
606 } catch (JSONException e) {
607 return false;
608 }
609 return true;
610 }
611 }
612
613 public Roster getRoster() {
614 return this.roster;
615 }
616
617 public Collection<Bookmark> getBookmarks() {
618 synchronized (this.bookmarks) {
619 return ImmutableList.copyOf(this.bookmarks.values());
620 }
621 }
622
623 public void setBookmarks(final Map<Jid, Bookmark> bookmarks) {
624 synchronized (this.bookmarks) {
625 this.bookmarks.clear();
626 this.bookmarks.putAll(bookmarks);
627 }
628 }
629
630 public void putBookmark(final Bookmark bookmark) {
631 synchronized (this.bookmarks) {
632 this.bookmarks.put(bookmark.getJid(), bookmark);
633 }
634 }
635
636 public void removeBookmark(Bookmark bookmark) {
637 synchronized (this.bookmarks) {
638 this.bookmarks.remove(bookmark.getJid());
639 }
640 }
641
642 public void removeBookmark(Jid jid) {
643 synchronized (this.bookmarks) {
644 this.bookmarks.remove(jid);
645 }
646 }
647
648 public Set<Jid> getBookmarkedJids() {
649 synchronized (this.bookmarks) {
650 return new HashSet<>(this.bookmarks.keySet());
651 }
652 }
653
654 public Bookmark getBookmark(final Jid jid) {
655 synchronized (this.bookmarks) {
656 return this.bookmarks.get(jid.asBareJid());
657 }
658 }
659
660 public boolean setAvatar(final String filename) {
661 if (this.avatar != null && this.avatar.equals(filename)) {
662 return false;
663 } else {
664 this.avatar = filename;
665 return true;
666 }
667 }
668
669 public String getAvatar() {
670 return this.avatar;
671 }
672
673 public void activateGracePeriod(final long duration) {
674 if (duration > 0) {
675 this.mEndGracePeriod = SystemClock.elapsedRealtime() + duration;
676 }
677 }
678
679 public void deactivateGracePeriod() {
680 this.mEndGracePeriod = 0L;
681 }
682
683 public boolean inGracePeriod() {
684 return SystemClock.elapsedRealtime() < this.mEndGracePeriod;
685 }
686
687 public String getShareableUri() {
688 List<XmppUri.Fingerprint> fingerprints = this.getFingerprints();
689 String uri = "xmpp:" + this.getJid().asBareJid().toEscapedString();
690 if (fingerprints.size() > 0) {
691 return XmppUri.getFingerprintUri(uri, fingerprints, ';');
692 } else {
693 return uri;
694 }
695 }
696
697 public String getShareableLink() {
698 List<XmppUri.Fingerprint> fingerprints = this.getFingerprints();
699 String uri =
700 "https://conversations.im/i/"
701 + XmppUri.lameUrlEncode(this.getJid().asBareJid().toEscapedString());
702 if (fingerprints.size() > 0) {
703 return XmppUri.getFingerprintUri(uri, fingerprints, '&');
704 } else {
705 return uri;
706 }
707 }
708
709 private List<XmppUri.Fingerprint> getFingerprints() {
710 ArrayList<XmppUri.Fingerprint> fingerprints = new ArrayList<>();
711 if (axolotlService == null) {
712 return fingerprints;
713 }
714 fingerprints.add(
715 new XmppUri.Fingerprint(
716 XmppUri.FingerprintType.OMEMO,
717 axolotlService.getOwnFingerprint().substring(2),
718 axolotlService.getOwnDeviceId()));
719 for (XmppAxolotlSession session : axolotlService.findOwnSessions()) {
720 if (session.getTrust().isVerified() && session.getTrust().isActive()) {
721 fingerprints.add(
722 new XmppUri.Fingerprint(
723 XmppUri.FingerprintType.OMEMO,
724 session.getFingerprint().substring(2).replaceAll("\\s", ""),
725 session.getRemoteAddress().getDeviceId()));
726 }
727 }
728 return fingerprints;
729 }
730
731 public boolean isBlocked(final ListItem contact) {
732 final Jid jid = contact.getJid();
733 return jid != null
734 && (blocklist.contains(jid.asBareJid()) || blocklist.contains(jid.getDomain()));
735 }
736
737 public boolean isBlocked(final Jid jid) {
738 return jid != null && blocklist.contains(jid.asBareJid());
739 }
740
741 public Collection<Jid> getBlocklist() {
742 return this.blocklist;
743 }
744
745 public void clearBlocklist() {
746 getBlocklist().clear();
747 }
748
749 public boolean isOnlineAndConnected() {
750 return this.getStatus() == State.ONLINE && this.getXmppConnection() != null;
751 }
752
753 @Override
754 public int getAvatarBackgroundColor() {
755 return UIHelper.getColorForName(jid.asBareJid().toString());
756 }
757
758 @Override
759 public String getAvatarName() {
760 throw new IllegalStateException("This method should not be called");
761 }
762
763 public enum State {
764 DISABLED(false, false),
765 OFFLINE(false),
766 CONNECTING(false),
767 ONLINE(false),
768 NO_INTERNET(false),
769 UNAUTHORIZED,
770 TEMPORARY_AUTH_FAILURE,
771 SERVER_NOT_FOUND,
772 REGISTRATION_SUCCESSFUL(false),
773 REGISTRATION_FAILED(true, false),
774 REGISTRATION_WEB(true, false),
775 REGISTRATION_CONFLICT(true, false),
776 REGISTRATION_NOT_SUPPORTED(true, false),
777 REGISTRATION_PLEASE_WAIT(true, false),
778 REGISTRATION_INVALID_TOKEN(true, false),
779 REGISTRATION_PASSWORD_TOO_WEAK(true, false),
780 TLS_ERROR,
781 TLS_ERROR_DOMAIN,
782 INCOMPATIBLE_SERVER,
783 INCOMPATIBLE_CLIENT,
784 TOR_NOT_AVAILABLE,
785 DOWNGRADE_ATTACK,
786 SESSION_FAILURE,
787 BIND_FAILURE,
788 HOST_UNKNOWN,
789 STREAM_ERROR,
790 STREAM_OPENING_ERROR,
791 POLICY_VIOLATION,
792 PAYMENT_REQUIRED,
793 MISSING_INTERNET_PERMISSION(false);
794
795 private final boolean isError;
796 private final boolean attemptReconnect;
797
798 State(final boolean isError) {
799 this(isError, true);
800 }
801
802 State(final boolean isError, final boolean reconnect) {
803 this.isError = isError;
804 this.attemptReconnect = reconnect;
805 }
806
807 State() {
808 this(true, true);
809 }
810
811 public boolean isError() {
812 return this.isError;
813 }
814
815 public boolean isAttemptReconnect() {
816 return this.attemptReconnect;
817 }
818
819 public int getReadableId() {
820 switch (this) {
821 case DISABLED:
822 return R.string.account_status_disabled;
823 case ONLINE:
824 return R.string.account_status_online;
825 case CONNECTING:
826 return R.string.account_status_connecting;
827 case OFFLINE:
828 return R.string.account_status_offline;
829 case UNAUTHORIZED:
830 return R.string.account_status_unauthorized;
831 case SERVER_NOT_FOUND:
832 return R.string.account_status_not_found;
833 case NO_INTERNET:
834 return R.string.account_status_no_internet;
835 case REGISTRATION_FAILED:
836 return R.string.account_status_regis_fail;
837 case REGISTRATION_WEB:
838 return R.string.account_status_regis_web;
839 case REGISTRATION_CONFLICT:
840 return R.string.account_status_regis_conflict;
841 case REGISTRATION_SUCCESSFUL:
842 return R.string.account_status_regis_success;
843 case REGISTRATION_NOT_SUPPORTED:
844 return R.string.account_status_regis_not_sup;
845 case REGISTRATION_INVALID_TOKEN:
846 return R.string.account_status_regis_invalid_token;
847 case TLS_ERROR:
848 return R.string.account_status_tls_error;
849 case TLS_ERROR_DOMAIN:
850 return R.string.account_status_tls_error_domain;
851 case INCOMPATIBLE_SERVER:
852 return R.string.account_status_incompatible_server;
853 case INCOMPATIBLE_CLIENT:
854 return R.string.account_status_incompatible_client;
855 case TOR_NOT_AVAILABLE:
856 return R.string.account_status_tor_unavailable;
857 case BIND_FAILURE:
858 return R.string.account_status_bind_failure;
859 case SESSION_FAILURE:
860 return R.string.session_failure;
861 case DOWNGRADE_ATTACK:
862 return R.string.sasl_downgrade;
863 case HOST_UNKNOWN:
864 return R.string.account_status_host_unknown;
865 case POLICY_VIOLATION:
866 return R.string.account_status_policy_violation;
867 case REGISTRATION_PLEASE_WAIT:
868 return R.string.registration_please_wait;
869 case REGISTRATION_PASSWORD_TOO_WEAK:
870 return R.string.registration_password_too_weak;
871 case STREAM_ERROR:
872 return R.string.account_status_stream_error;
873 case STREAM_OPENING_ERROR:
874 return R.string.account_status_stream_opening_error;
875 case PAYMENT_REQUIRED:
876 return R.string.payment_required;
877 case MISSING_INTERNET_PERMISSION:
878 return R.string.missing_internet_permission;
879 case TEMPORARY_AUTH_FAILURE:
880 return R.string.account_status_temporary_auth_failure;
881 default:
882 return R.string.account_status_unknown;
883 }
884 }
885 }
886}