Account.java

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