Account.java

  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}