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 ? 20 : 15
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() { return bookmarksLoaded; }
659
660 public void setBookmarks(final Map<Jid, Bookmark> bookmarks) {
661 synchronized (this.bookmarks) {
662 this.bookmarks.clear();
663 this.bookmarks.putAll(bookmarks);
664 this.bookmarksLoaded = true;
665 }
666 }
667
668 public void putBookmark(final Bookmark bookmark) {
669 synchronized (this.bookmarks) {
670 this.bookmarks.put(bookmark.getJid(), bookmark);
671 }
672 }
673
674 public void removeBookmark(Bookmark bookmark) {
675 synchronized (this.bookmarks) {
676 this.bookmarks.remove(bookmark.getJid());
677 }
678 }
679
680 public void removeBookmark(Jid jid) {
681 synchronized (this.bookmarks) {
682 this.bookmarks.remove(jid);
683 }
684 }
685
686 public Set<Jid> getBookmarkedJids() {
687 synchronized (this.bookmarks) {
688 return new HashSet<>(this.bookmarks.keySet());
689 }
690 }
691
692 public Bookmark getBookmark(final Jid jid) {
693 synchronized (this.bookmarks) {
694 return this.bookmarks.get(jid.asBareJid());
695 }
696 }
697
698 public boolean setAvatar(final String filename) {
699 if (this.avatar != null && this.avatar.equals(filename)) {
700 return false;
701 } else {
702 this.avatar = filename;
703 return true;
704 }
705 }
706
707 public String getAvatar() {
708 return this.avatar;
709 }
710
711 public void activateGracePeriod(final long duration) {
712 if (duration > 0) {
713 this.mEndGracePeriod = SystemClock.elapsedRealtime() + duration;
714 }
715 }
716
717 public void deactivateGracePeriod() {
718 this.mEndGracePeriod = 0L;
719 }
720
721 public boolean inGracePeriod() {
722 return SystemClock.elapsedRealtime() < this.mEndGracePeriod;
723 }
724
725 public String getShareableUri() {
726 List<XmppUri.Fingerprint> fingerprints = this.getFingerprints();
727 String uri = "xmpp:" + Uri.encode(getJid().asBareJid().toEscapedString(), "@/+");
728 if (fingerprints.size() > 0) {
729 return XmppUri.getFingerprintUri(uri, fingerprints, ';');
730 } else {
731 return uri;
732 }
733 }
734
735 public String getShareableLink() {
736 List<XmppUri.Fingerprint> fingerprints = this.getFingerprints();
737 String uri =
738 "https://conversations.im/i/"
739 + XmppUri.lameUrlEncode(this.getJid().asBareJid().toEscapedString());
740 if (fingerprints.size() > 0) {
741 return XmppUri.getFingerprintUri(uri, fingerprints, '&');
742 } else {
743 return uri;
744 }
745 }
746
747 private List<XmppUri.Fingerprint> getFingerprints() {
748 ArrayList<XmppUri.Fingerprint> fingerprints = new ArrayList<>();
749 if (axolotlService == null) {
750 return fingerprints;
751 }
752 fingerprints.add(
753 new XmppUri.Fingerprint(
754 XmppUri.FingerprintType.OMEMO,
755 axolotlService.getOwnFingerprint().substring(2),
756 axolotlService.getOwnDeviceId()));
757 for (XmppAxolotlSession session : axolotlService.findOwnSessions()) {
758 if (session.getTrust().isVerified() && session.getTrust().isActive()) {
759 fingerprints.add(
760 new XmppUri.Fingerprint(
761 XmppUri.FingerprintType.OMEMO,
762 session.getFingerprint().substring(2).replaceAll("\\s", ""),
763 session.getRemoteAddress().getDeviceId()));
764 }
765 }
766 return fingerprints;
767 }
768
769 public boolean isBlocked(final ListItem contact) {
770 final Jid jid = contact.getJid();
771 return jid != null
772 && (blocklist.contains(jid.asBareJid()) || blocklist.contains(jid.getDomain()));
773 }
774
775 public boolean isBlocked(final Jid jid) {
776 return jid != null && blocklist.contains(jid.asBareJid());
777 }
778
779 public Collection<Jid> getBlocklist() {
780 return this.blocklist;
781 }
782
783 public void clearBlocklist() {
784 getBlocklist().clear();
785 }
786
787 public boolean isOnlineAndConnected() {
788 return this.getStatus() == State.ONLINE && this.getXmppConnection() != null;
789 }
790
791 @Override
792 public int getAvatarBackgroundColor() {
793 return UIHelper.getColorForName(jid.asBareJid().toString());
794 }
795
796 @Override
797 public String getAvatarName() {
798 throw new IllegalStateException("This method should not be called");
799 }
800
801 public enum State {
802 DISABLED(false, false),
803 LOGGED_OUT(false,false),
804 OFFLINE(false),
805 CONNECTING(false),
806 ONLINE(false),
807 NO_INTERNET(false),
808 UNAUTHORIZED,
809 TEMPORARY_AUTH_FAILURE,
810 SERVER_NOT_FOUND,
811 REGISTRATION_SUCCESSFUL(false),
812 REGISTRATION_FAILED(true, false),
813 REGISTRATION_WEB(true, false),
814 REGISTRATION_CONFLICT(true, false),
815 REGISTRATION_NOT_SUPPORTED(true, false),
816 REGISTRATION_PLEASE_WAIT(true, false),
817 REGISTRATION_INVALID_TOKEN(true, false),
818 REGISTRATION_PASSWORD_TOO_WEAK(true, false),
819 TLS_ERROR,
820 TLS_ERROR_DOMAIN,
821 INCOMPATIBLE_SERVER,
822 INCOMPATIBLE_CLIENT,
823 TOR_NOT_AVAILABLE,
824 DOWNGRADE_ATTACK,
825 SESSION_FAILURE,
826 BIND_FAILURE,
827 HOST_UNKNOWN,
828 STREAM_ERROR,
829 SEE_OTHER_HOST,
830 STREAM_OPENING_ERROR,
831 POLICY_VIOLATION,
832 PAYMENT_REQUIRED,
833 MISSING_INTERNET_PERMISSION(false);
834
835 private final boolean isError;
836 private final boolean attemptReconnect;
837
838 State(final boolean isError) {
839 this(isError, true);
840 }
841
842 State(final boolean isError, final boolean reconnect) {
843 this.isError = isError;
844 this.attemptReconnect = reconnect;
845 }
846
847 State() {
848 this(true, true);
849 }
850
851 public boolean isError() {
852 return this.isError;
853 }
854
855 public boolean isAttemptReconnect() {
856 return this.attemptReconnect;
857 }
858
859 public int getReadableId() {
860 switch (this) {
861 case DISABLED:
862 return R.string.account_status_disabled;
863 case LOGGED_OUT:
864 return R.string.account_state_logged_out;
865 case ONLINE:
866 return R.string.account_status_online;
867 case CONNECTING:
868 return R.string.account_status_connecting;
869 case OFFLINE:
870 return R.string.account_status_offline;
871 case UNAUTHORIZED:
872 return R.string.account_status_unauthorized;
873 case SERVER_NOT_FOUND:
874 return R.string.account_status_not_found;
875 case NO_INTERNET:
876 return R.string.account_status_no_internet;
877 case REGISTRATION_FAILED:
878 return R.string.account_status_regis_fail;
879 case REGISTRATION_WEB:
880 return R.string.account_status_regis_web;
881 case REGISTRATION_CONFLICT:
882 return R.string.account_status_regis_conflict;
883 case REGISTRATION_SUCCESSFUL:
884 return R.string.account_status_regis_success;
885 case REGISTRATION_NOT_SUPPORTED:
886 return R.string.account_status_regis_not_sup;
887 case REGISTRATION_INVALID_TOKEN:
888 return R.string.account_status_regis_invalid_token;
889 case TLS_ERROR:
890 return R.string.account_status_tls_error;
891 case TLS_ERROR_DOMAIN:
892 return R.string.account_status_tls_error_domain;
893 case INCOMPATIBLE_SERVER:
894 return R.string.account_status_incompatible_server;
895 case INCOMPATIBLE_CLIENT:
896 return R.string.account_status_incompatible_client;
897 case TOR_NOT_AVAILABLE:
898 return R.string.account_status_tor_unavailable;
899 case BIND_FAILURE:
900 return R.string.account_status_bind_failure;
901 case SESSION_FAILURE:
902 return R.string.session_failure;
903 case DOWNGRADE_ATTACK:
904 return R.string.sasl_downgrade;
905 case HOST_UNKNOWN:
906 return R.string.account_status_host_unknown;
907 case POLICY_VIOLATION:
908 return R.string.account_status_policy_violation;
909 case REGISTRATION_PLEASE_WAIT:
910 return R.string.registration_please_wait;
911 case REGISTRATION_PASSWORD_TOO_WEAK:
912 return R.string.registration_password_too_weak;
913 case STREAM_ERROR:
914 return R.string.account_status_stream_error;
915 case STREAM_OPENING_ERROR:
916 return R.string.account_status_stream_opening_error;
917 case PAYMENT_REQUIRED:
918 return R.string.payment_required;
919 case SEE_OTHER_HOST:
920 return R.string.reconnect_on_other_host;
921 case MISSING_INTERNET_PERMISSION:
922 return R.string.missing_internet_permission;
923 case TEMPORARY_AUTH_FAILURE:
924 return R.string.account_status_temporary_auth_failure;
925 default:
926 return R.string.account_status_unknown;
927 }
928 }
929 }
930}