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