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