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