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