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.crypto.sasl.ScramPlusMechanism;
38import eu.siacs.conversations.services.AvatarService;
39import eu.siacs.conversations.services.XmppConnectionService;
40import eu.siacs.conversations.utils.UIHelper;
41import eu.siacs.conversations.utils.XmppUri;
42import eu.siacs.conversations.xmpp.Jid;
43import eu.siacs.conversations.xmpp.XmppConnection;
44import eu.siacs.conversations.xmpp.jingle.RtpCapability;
45
46public class Account extends AbstractEntity implements AvatarService.Avatarable {
47
48 public static final String TABLENAME = "accounts";
49
50 public static final String USERNAME = "username";
51 public static final String SERVER = "server";
52 public static final String PASSWORD = "password";
53 public static final String OPTIONS = "options";
54 public static final String ROSTERVERSION = "rosterversion";
55 public static final String KEYS = "keys";
56 public static final String AVATAR = "avatar";
57 public static final String DISPLAY_NAME = "display_name";
58 public static final String HOSTNAME = "hostname";
59 public static final String PORT = "port";
60 public static final String STATUS = "status";
61 public static final String STATUS_MESSAGE = "status_message";
62 public static final String RESOURCE = "resource";
63 public static final String PINNED_MECHANISM = "pinned_mechanism";
64 public static final String PINNED_CHANNEL_BINDING = "pinned_channel_binding";
65 public static final String FAST_MECHANISM = "fast_mechanism";
66 public static final String FAST_TOKEN = "fast_token";
67
68 public static final int OPTION_DISABLED = 1;
69 public static final int OPTION_REGISTER = 2;
70 public static final int OPTION_MAGIC_CREATE = 4;
71 public static final int OPTION_REQUIRES_ACCESS_MODE_CHANGE = 5;
72 public static final int OPTION_LOGGED_IN_SUCCESSFULLY = 6;
73 public static final int OPTION_HTTP_UPLOAD_AVAILABLE = 7;
74 public static final int OPTION_UNVERIFIED = 8;
75 public static final int OPTION_FIXED_USERNAME = 9;
76 public static final int OPTION_QUICKSTART_AVAILABLE = 10;
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 isOptionSet(final int option) {
275 return ((options & (1 << option)) != 0);
276 }
277
278 public boolean setOption(final int option, final boolean value) {
279 final int before = this.options;
280 if (value) {
281 this.options |= 1 << option;
282 } else {
283 this.options &= ~(1 << option);
284 }
285 return before != this.options;
286 }
287
288 public String getUsername() {
289 return jid.getEscapedLocal();
290 }
291
292 public boolean setJid(final Jid next) {
293 final Jid previousFull = this.jid;
294 final Jid prev = this.jid != null ? this.jid.asBareJid() : null;
295 final boolean changed = prev == null || (next != null && !prev.equals(next.asBareJid()));
296 if (changed) {
297 final AxolotlService oldAxolotlService = this.axolotlService;
298 if (oldAxolotlService != null) {
299 oldAxolotlService.destroy();
300 this.jid = next;
301 this.axolotlService = oldAxolotlService.makeNew();
302 }
303 }
304 this.jid = next;
305 return next != null && !next.equals(previousFull);
306 }
307
308 public Jid getDomain() {
309 return jid.getDomain();
310 }
311
312 public String getServer() {
313 return jid.getDomain().toEscapedString();
314 }
315
316 public String getPassword() {
317 return password;
318 }
319
320 public void setPassword(final String password) {
321 this.password = password;
322 }
323
324 public String getHostname() {
325 return Strings.nullToEmpty(this.hostname);
326 }
327
328 public void setHostname(String hostname) {
329 this.hostname = hostname;
330 }
331
332 public boolean isOnion() {
333 final String server = getServer();
334 return server != null && server.endsWith(".onion");
335 }
336
337 public int getPort() {
338 return this.port;
339 }
340
341 public void setPort(int port) {
342 this.port = port;
343 }
344
345 public State getStatus() {
346 if (isOptionSet(OPTION_DISABLED)) {
347 return State.DISABLED;
348 } else {
349 return this.status;
350 }
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 = HashedToken.Mechanism.ofOrNull(this.fastMechanism);
411 final String token = this.fastToken;
412 if (fastMechanism == null || Strings.isNullOrEmpty(token)) {
413 return null;
414 }
415 if (fastMechanism.hashFunction.equals("SHA-256")) {
416 return new HashedTokenSha256(this, fastMechanism.channelBinding);
417 } else if (fastMechanism.hashFunction.equals("SHA-512")) {
418 return new HashedTokenSha512(this, fastMechanism.channelBinding);
419 } else {
420 return null;
421 }
422 }
423
424 public SaslMechanism getQuickStartMechanism() {
425 final HashedToken hashedTokenMechanism = getFastMechanism();
426 if (hashedTokenMechanism != null) {
427 return hashedTokenMechanism;
428 }
429 return getPinnedMechanism();
430 }
431
432 public String getFastToken() {
433 return this.fastToken;
434 }
435
436 public State getTrueStatus() {
437 return this.status;
438 }
439
440 public boolean errorStatus() {
441 return getStatus().isError();
442 }
443
444 public boolean hasErrorStatus() {
445 return getXmppConnection() != null
446 && (getStatus().isError() || getStatus() == State.CONNECTING)
447 && getXmppConnection().getAttempt() >= 3;
448 }
449
450 public Presence.Status getPresenceStatus() {
451 return this.presenceStatus;
452 }
453
454 public void setPresenceStatus(Presence.Status status) {
455 this.presenceStatus = status;
456 }
457
458 public String getPresenceStatusMessage() {
459 return this.presenceStatusMessage;
460 }
461
462 public void setPresenceStatusMessage(String message) {
463 this.presenceStatusMessage = message;
464 }
465
466 public String getResource() {
467 return jid.getResource();
468 }
469
470 public void setResource(final String resource) {
471 this.jid = this.jid.withResource(resource);
472 }
473
474 public Jid getJid() {
475 return jid;
476 }
477
478 public JSONObject getKeys() {
479 return keys;
480 }
481
482 public String getKey(final String name) {
483 synchronized (this.keys) {
484 return this.keys.optString(name, null);
485 }
486 }
487
488 public int getKeyAsInt(final String name, int defaultValue) {
489 String key = getKey(name);
490 try {
491 return key == null ? defaultValue : Integer.parseInt(key);
492 } catch (NumberFormatException e) {
493 return defaultValue;
494 }
495 }
496
497 public boolean setKey(final String keyName, final String keyValue) {
498 synchronized (this.keys) {
499 try {
500 this.keys.put(keyName, keyValue);
501 return true;
502 } catch (final JSONException e) {
503 return false;
504 }
505 }
506 }
507
508 public void setPrivateKeyAlias(final String alias) {
509 setKey("private_key_alias", alias);
510 }
511
512 public String getPrivateKeyAlias() {
513 return getKey("private_key_alias");
514 }
515
516 @Override
517 public ContentValues getContentValues() {
518 final ContentValues values = new ContentValues();
519 values.put(UUID, uuid);
520 values.put(USERNAME, jid.getLocal());
521 values.put(SERVER, jid.getDomain().toEscapedString());
522 values.put(PASSWORD, password);
523 values.put(OPTIONS, options);
524 synchronized (this.keys) {
525 values.put(KEYS, this.keys.toString());
526 }
527 values.put(ROSTERVERSION, rosterVersion);
528 values.put(AVATAR, avatar);
529 values.put(DISPLAY_NAME, displayName);
530 values.put(HOSTNAME, hostname);
531 values.put(PORT, port);
532 values.put(STATUS, presenceStatus.toShowString());
533 values.put(STATUS_MESSAGE, presenceStatusMessage);
534 values.put(RESOURCE, jid.getResource());
535 values.put(PINNED_MECHANISM, pinnedMechanism);
536 values.put(PINNED_CHANNEL_BINDING, pinnedChannelBinding);
537 values.put(FAST_MECHANISM, this.fastMechanism);
538 values.put(FAST_TOKEN, this.fastToken);
539 return values;
540 }
541
542 public AxolotlService getAxolotlService() {
543 return axolotlService;
544 }
545
546 public void initAccountServices(final XmppConnectionService context) {
547 this.axolotlService = new AxolotlService(this, context);
548 this.pgpDecryptionService = new PgpDecryptionService(context);
549 if (xmppConnection != null) {
550 xmppConnection.addOnAdvancedStreamFeaturesAvailableListener(axolotlService);
551 }
552 }
553
554 public PgpDecryptionService getPgpDecryptionService() {
555 return this.pgpDecryptionService;
556 }
557
558 public XmppConnection getXmppConnection() {
559 return this.xmppConnection;
560 }
561
562 public void setXmppConnection(final XmppConnection connection) {
563 this.xmppConnection = connection;
564 }
565
566 public String getRosterVersion() {
567 if (this.rosterVersion == null) {
568 return "";
569 } else {
570 return this.rosterVersion;
571 }
572 }
573
574 public void setRosterVersion(final String version) {
575 this.rosterVersion = version;
576 }
577
578 public int countPresences() {
579 return this.getSelfContact().getPresences().size();
580 }
581
582 public int activeDevicesWithRtpCapability() {
583 int i = 0;
584 for (Presence presence : getSelfContact().getPresences().getPresences()) {
585 if (RtpCapability.check(presence) != RtpCapability.Capability.NONE) {
586 i++;
587 }
588 }
589 return i;
590 }
591
592 public String getPgpSignature() {
593 return getKey(KEY_PGP_SIGNATURE);
594 }
595
596 public boolean setPgpSignature(String signature) {
597 return setKey(KEY_PGP_SIGNATURE, signature);
598 }
599
600 public boolean unsetPgpSignature() {
601 synchronized (this.keys) {
602 return keys.remove(KEY_PGP_SIGNATURE) != null;
603 }
604 }
605
606 public long getPgpId() {
607 synchronized (this.keys) {
608 if (keys.has(KEY_PGP_ID)) {
609 try {
610 return keys.getLong(KEY_PGP_ID);
611 } catch (JSONException e) {
612 return 0;
613 }
614 } else {
615 return 0;
616 }
617 }
618 }
619
620 public boolean setPgpSignId(long pgpID) {
621 synchronized (this.keys) {
622 try {
623 if (pgpID == 0) {
624 keys.remove(KEY_PGP_ID);
625 } else {
626 keys.put(KEY_PGP_ID, pgpID);
627 }
628 } catch (JSONException e) {
629 return false;
630 }
631 return true;
632 }
633 }
634
635 public Roster getRoster() {
636 return this.roster;
637 }
638
639 public Collection<Bookmark> getBookmarks() {
640 synchronized (this.bookmarks) {
641 return ImmutableList.copyOf(this.bookmarks.values());
642 }
643 }
644
645 public boolean areBookmarksLoaded() { return bookmarksLoaded; }
646
647 public void setBookmarks(final Map<Jid, Bookmark> bookmarks) {
648 synchronized (this.bookmarks) {
649 this.bookmarks.clear();
650 this.bookmarks.putAll(bookmarks);
651 this.bookmarksLoaded = true;
652 }
653 }
654
655 public void putBookmark(final Bookmark bookmark) {
656 synchronized (this.bookmarks) {
657 this.bookmarks.put(bookmark.getJid(), bookmark);
658 }
659 }
660
661 public void removeBookmark(Bookmark bookmark) {
662 synchronized (this.bookmarks) {
663 this.bookmarks.remove(bookmark.getJid());
664 }
665 }
666
667 public void removeBookmark(Jid jid) {
668 synchronized (this.bookmarks) {
669 this.bookmarks.remove(jid);
670 }
671 }
672
673 public Set<Jid> getBookmarkedJids() {
674 synchronized (this.bookmarks) {
675 return new HashSet<>(this.bookmarks.keySet());
676 }
677 }
678
679 public Bookmark getBookmark(final Jid jid) {
680 synchronized (this.bookmarks) {
681 return this.bookmarks.get(jid.asBareJid());
682 }
683 }
684
685 public boolean setAvatar(final String filename) {
686 if (this.avatar != null && this.avatar.equals(filename)) {
687 return false;
688 } else {
689 this.avatar = filename;
690 return true;
691 }
692 }
693
694 public String getAvatar() {
695 return this.avatar;
696 }
697
698 public void activateGracePeriod(final long duration) {
699 if (duration > 0) {
700 this.mEndGracePeriod = SystemClock.elapsedRealtime() + duration;
701 }
702 }
703
704 public void deactivateGracePeriod() {
705 this.mEndGracePeriod = 0L;
706 }
707
708 public boolean inGracePeriod() {
709 return SystemClock.elapsedRealtime() < this.mEndGracePeriod;
710 }
711
712 public String getShareableUri() {
713 List<XmppUri.Fingerprint> fingerprints = this.getFingerprints();
714 String uri = "xmpp:" + Uri.encode(getJid().asBareJid().toEscapedString(), "@/+");
715 if (fingerprints.size() > 0) {
716 return XmppUri.getFingerprintUri(uri, fingerprints, ';');
717 } else {
718 return uri;
719 }
720 }
721
722 public String getShareableLink() {
723 List<XmppUri.Fingerprint> fingerprints = this.getFingerprints();
724 String uri =
725 "https://conversations.im/i/"
726 + XmppUri.lameUrlEncode(this.getJid().asBareJid().toEscapedString());
727 if (fingerprints.size() > 0) {
728 return XmppUri.getFingerprintUri(uri, fingerprints, '&');
729 } else {
730 return uri;
731 }
732 }
733
734 private List<XmppUri.Fingerprint> getFingerprints() {
735 ArrayList<XmppUri.Fingerprint> fingerprints = new ArrayList<>();
736 if (axolotlService == null) {
737 return fingerprints;
738 }
739 fingerprints.add(
740 new XmppUri.Fingerprint(
741 XmppUri.FingerprintType.OMEMO,
742 axolotlService.getOwnFingerprint().substring(2),
743 axolotlService.getOwnDeviceId()));
744 for (XmppAxolotlSession session : axolotlService.findOwnSessions()) {
745 if (session.getTrust().isVerified() && session.getTrust().isActive()) {
746 fingerprints.add(
747 new XmppUri.Fingerprint(
748 XmppUri.FingerprintType.OMEMO,
749 session.getFingerprint().substring(2).replaceAll("\\s", ""),
750 session.getRemoteAddress().getDeviceId()));
751 }
752 }
753 return fingerprints;
754 }
755
756 public boolean isBlocked(final ListItem contact) {
757 final Jid jid = contact.getJid();
758 return jid != null
759 && (blocklist.contains(jid.asBareJid()) || blocklist.contains(jid.getDomain()));
760 }
761
762 public boolean isBlocked(final Jid jid) {
763 return jid != null && blocklist.contains(jid.asBareJid());
764 }
765
766 public Collection<Jid> getBlocklist() {
767 return this.blocklist;
768 }
769
770 public void clearBlocklist() {
771 getBlocklist().clear();
772 }
773
774 public boolean isOnlineAndConnected() {
775 return this.getStatus() == State.ONLINE && this.getXmppConnection() != null;
776 }
777
778 @Override
779 public int getAvatarBackgroundColor() {
780 return UIHelper.getColorForName(jid.asBareJid().toString());
781 }
782
783 @Override
784 public String getAvatarName() {
785 throw new IllegalStateException("This method should not be called");
786 }
787
788 public enum State {
789 DISABLED(false, false),
790 OFFLINE(false),
791 CONNECTING(false),
792 ONLINE(false),
793 NO_INTERNET(false),
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 INCOMPATIBLE_SERVER,
808 INCOMPATIBLE_CLIENT,
809 TOR_NOT_AVAILABLE,
810 DOWNGRADE_ATTACK,
811 SESSION_FAILURE,
812 BIND_FAILURE,
813 HOST_UNKNOWN,
814 STREAM_ERROR,
815 STREAM_OPENING_ERROR,
816 POLICY_VIOLATION,
817 PAYMENT_REQUIRED,
818 MISSING_INTERNET_PERMISSION(false);
819
820 private final boolean isError;
821 private final boolean attemptReconnect;
822
823 State(final boolean isError) {
824 this(isError, true);
825 }
826
827 State(final boolean isError, final boolean reconnect) {
828 this.isError = isError;
829 this.attemptReconnect = reconnect;
830 }
831
832 State() {
833 this(true, true);
834 }
835
836 public boolean isError() {
837 return this.isError;
838 }
839
840 public boolean isAttemptReconnect() {
841 return this.attemptReconnect;
842 }
843
844 public int getReadableId() {
845 switch (this) {
846 case DISABLED:
847 return R.string.account_status_disabled;
848 case ONLINE:
849 return R.string.account_status_online;
850 case CONNECTING:
851 return R.string.account_status_connecting;
852 case OFFLINE:
853 return R.string.account_status_offline;
854 case UNAUTHORIZED:
855 return R.string.account_status_unauthorized;
856 case SERVER_NOT_FOUND:
857 return R.string.account_status_not_found;
858 case NO_INTERNET:
859 return R.string.account_status_no_internet;
860 case REGISTRATION_FAILED:
861 return R.string.account_status_regis_fail;
862 case REGISTRATION_WEB:
863 return R.string.account_status_regis_web;
864 case REGISTRATION_CONFLICT:
865 return R.string.account_status_regis_conflict;
866 case REGISTRATION_SUCCESSFUL:
867 return R.string.account_status_regis_success;
868 case REGISTRATION_NOT_SUPPORTED:
869 return R.string.account_status_regis_not_sup;
870 case REGISTRATION_INVALID_TOKEN:
871 return R.string.account_status_regis_invalid_token;
872 case TLS_ERROR:
873 return R.string.account_status_tls_error;
874 case TLS_ERROR_DOMAIN:
875 return R.string.account_status_tls_error_domain;
876 case INCOMPATIBLE_SERVER:
877 return R.string.account_status_incompatible_server;
878 case INCOMPATIBLE_CLIENT:
879 return R.string.account_status_incompatible_client;
880 case TOR_NOT_AVAILABLE:
881 return R.string.account_status_tor_unavailable;
882 case BIND_FAILURE:
883 return R.string.account_status_bind_failure;
884 case SESSION_FAILURE:
885 return R.string.session_failure;
886 case DOWNGRADE_ATTACK:
887 return R.string.sasl_downgrade;
888 case HOST_UNKNOWN:
889 return R.string.account_status_host_unknown;
890 case POLICY_VIOLATION:
891 return R.string.account_status_policy_violation;
892 case REGISTRATION_PLEASE_WAIT:
893 return R.string.registration_please_wait;
894 case REGISTRATION_PASSWORD_TOO_WEAK:
895 return R.string.registration_password_too_weak;
896 case STREAM_ERROR:
897 return R.string.account_status_stream_error;
898 case STREAM_OPENING_ERROR:
899 return R.string.account_status_stream_opening_error;
900 case PAYMENT_REQUIRED:
901 return R.string.payment_required;
902 case MISSING_INTERNET_PERMISSION:
903 return R.string.missing_internet_permission;
904 case TEMPORARY_AUTH_FAILURE:
905 return R.string.account_status_temporary_auth_failure;
906 default:
907 return R.string.account_status_unknown;
908 }
909 }
910 }
911}