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