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