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