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