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.getLocal();
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().toString();
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 boolean isDirectToOnion() {
321        final var hostname = Strings.nullToEmpty(this.hostname).trim();
322        return isOnion() && (hostname.isEmpty() || hostname.endsWith(".onion"));
323    }
324
325    public int getPort() {
326        return this.port;
327    }
328
329    public void setPort(int port) {
330        this.port = port;
331    }
332
333    public State getStatus() {
334        if (isOptionSet(OPTION_DISABLED)) {
335            return State.DISABLED;
336        } else if (isOptionSet(OPTION_SOFT_DISABLED)) {
337            return State.LOGGED_OUT;
338        } else {
339            return this.status;
340        }
341    }
342
343    public boolean unauthorized() {
344        return this.status == State.UNAUTHORIZED || this.lastErrorStatus == State.UNAUTHORIZED;
345    }
346
347    public State getLastErrorStatus() {
348        return this.lastErrorStatus;
349    }
350
351    public void setStatus(final State status) {
352        this.status = status;
353        if (status.isError || status == State.ONLINE) {
354            this.lastErrorStatus = status;
355        }
356    }
357
358    public void setPinnedMechanism(final SaslMechanism mechanism) {
359        this.pinnedMechanism = mechanism.getMechanism();
360        if (mechanism instanceof ChannelBindingMechanism) {
361            this.pinnedChannelBinding =
362                    ((ChannelBindingMechanism) mechanism).getChannelBinding().toString();
363        } else {
364            this.pinnedChannelBinding = null;
365        }
366    }
367
368    public void setFastToken(final HashedToken.Mechanism mechanism, final String token) {
369        this.fastMechanism = mechanism.name();
370        this.fastToken = token;
371    }
372
373    public void resetFastToken() {
374        this.fastMechanism = null;
375        this.fastToken = null;
376    }
377
378    public void resetPinnedMechanism() {
379        this.pinnedMechanism = null;
380        this.pinnedChannelBinding = null;
381        setKey(Account.KEY_PINNED_MECHANISM, String.valueOf(-1));
382    }
383
384    public int getPinnedMechanismPriority() {
385        final int fallback = getKeyAsInt(KEY_PINNED_MECHANISM, -1);
386        if (Strings.isNullOrEmpty(this.pinnedMechanism)) {
387            return fallback;
388        }
389        final SaslMechanism saslMechanism = getPinnedMechanism();
390        if (saslMechanism == null) {
391            return fallback;
392        } else {
393            return saslMechanism.getPriority();
394        }
395    }
396
397    private SaslMechanism getPinnedMechanism() {
398        final String mechanism = Strings.nullToEmpty(this.pinnedMechanism);
399        final ChannelBinding channelBinding = ChannelBinding.get(this.pinnedChannelBinding);
400        return new SaslMechanism.Factory(this).of(mechanism, channelBinding);
401    }
402
403    public HashedToken getFastMechanism() {
404        final HashedToken.Mechanism fastMechanism =
405                HashedToken.Mechanism.ofOrNull(this.fastMechanism);
406        final String token = this.fastToken;
407        if (fastMechanism == null || Strings.isNullOrEmpty(token)) {
408            return null;
409        }
410        if (fastMechanism.hashFunction.equals("SHA-256")) {
411            return new HashedTokenSha256(this, fastMechanism.channelBinding);
412        } else if (fastMechanism.hashFunction.equals("SHA-512")) {
413            return new HashedTokenSha512(this, fastMechanism.channelBinding);
414        } else {
415            return null;
416        }
417    }
418
419    public SaslMechanism getQuickStartMechanism() {
420        final HashedToken hashedTokenMechanism = getFastMechanism();
421        if (hashedTokenMechanism != null) {
422            return hashedTokenMechanism;
423        }
424        return getPinnedMechanism();
425    }
426
427    public String getFastToken() {
428        return this.fastToken;
429    }
430
431    public State getTrueStatus() {
432        return this.status;
433    }
434
435    public boolean errorStatus() {
436        return getStatus().isError();
437    }
438
439    public boolean hasErrorStatus() {
440        return getXmppConnection() != null
441                && (getStatus().isError() || getStatus() == State.CONNECTING)
442                && getXmppConnection().getAttempt() >= 3;
443    }
444
445    public Presence.Status getPresenceStatus() {
446        return this.presenceStatus;
447    }
448
449    public void setPresenceStatus(Presence.Status status) {
450        this.presenceStatus = status;
451    }
452
453    public String getPresenceStatusMessage() {
454        return this.presenceStatusMessage;
455    }
456
457    public void setPresenceStatusMessage(String message) {
458        this.presenceStatusMessage = message;
459    }
460
461    public String getResource() {
462        return jid.getResource();
463    }
464
465    public void setResource(final String resource) {
466        this.jid = this.jid.withResource(resource);
467    }
468
469    public Jid getJid() {
470        return jid;
471    }
472
473    public JSONObject getKeys() {
474        return keys;
475    }
476
477    public String getKey(final String name) {
478        synchronized (this.keys) {
479            return this.keys.optString(name, null);
480        }
481    }
482
483    public int getKeyAsInt(final String name, int defaultValue) {
484        String key = getKey(name);
485        try {
486            return key == null ? defaultValue : Integer.parseInt(key);
487        } catch (NumberFormatException e) {
488            return defaultValue;
489        }
490    }
491
492    public boolean setKey(final String keyName, final String keyValue) {
493        synchronized (this.keys) {
494            try {
495                this.keys.put(keyName, keyValue);
496                return true;
497            } catch (final JSONException e) {
498                return false;
499            }
500        }
501    }
502
503    public void setPrivateKeyAlias(final String alias) {
504        setKey("private_key_alias", alias);
505    }
506
507    public String getPrivateKeyAlias() {
508        return getKey("private_key_alias");
509    }
510
511    @Override
512    public ContentValues getContentValues() {
513        final ContentValues values = new ContentValues();
514        values.put(UUID, uuid);
515        values.put(USERNAME, jid.getLocal());
516        values.put(SERVER, jid.getDomain().toString());
517        values.put(PASSWORD, password);
518        values.put(OPTIONS, options);
519        synchronized (this.keys) {
520            values.put(KEYS, this.keys.toString());
521        }
522        values.put(ROSTERVERSION, rosterVersion);
523        values.put(AVATAR, avatar);
524        values.put(DISPLAY_NAME, displayName);
525        values.put(HOSTNAME, hostname);
526        values.put(PORT, port);
527        values.put(STATUS, presenceStatus.toShowString());
528        values.put(STATUS_MESSAGE, presenceStatusMessage);
529        values.put(RESOURCE, jid.getResource());
530        values.put(PINNED_MECHANISM, pinnedMechanism);
531        values.put(PINNED_CHANNEL_BINDING, pinnedChannelBinding);
532        values.put(FAST_MECHANISM, this.fastMechanism);
533        values.put(FAST_TOKEN, this.fastToken);
534        return values;
535    }
536
537    public AxolotlService getAxolotlService() {
538        return axolotlService;
539    }
540
541    public void initAccountServices(final XmppConnectionService context) {
542        this.axolotlService = new AxolotlService(this, context);
543        this.pgpDecryptionService = new PgpDecryptionService(context);
544        if (xmppConnection != null) {
545            xmppConnection.addOnAdvancedStreamFeaturesAvailableListener(axolotlService);
546        }
547    }
548
549    public PgpDecryptionService getPgpDecryptionService() {
550        return this.pgpDecryptionService;
551    }
552
553    public XmppConnection getXmppConnection() {
554        return this.xmppConnection;
555    }
556
557    public void setXmppConnection(final XmppConnection connection) {
558        this.xmppConnection = connection;
559    }
560
561    public String getRosterVersion() {
562        if (this.rosterVersion == null) {
563            return "";
564        } else {
565            return this.rosterVersion;
566        }
567    }
568
569    public void setRosterVersion(final String version) {
570        this.rosterVersion = version;
571    }
572
573    public int countPresences() {
574        return this.getSelfContact().getPresences().size();
575    }
576
577    public int activeDevicesWithRtpCapability() {
578        int i = 0;
579        for (Presence presence : getSelfContact().getPresences().getPresences()) {
580            if (RtpCapability.check(presence) != RtpCapability.Capability.NONE) {
581                i++;
582            }
583        }
584        return i;
585    }
586
587    public String getPgpSignature() {
588        return getKey(KEY_PGP_SIGNATURE);
589    }
590
591    public boolean setPgpSignature(String signature) {
592        return setKey(KEY_PGP_SIGNATURE, signature);
593    }
594
595    public boolean unsetPgpSignature() {
596        synchronized (this.keys) {
597            return keys.remove(KEY_PGP_SIGNATURE) != null;
598        }
599    }
600
601    public long getPgpId() {
602        synchronized (this.keys) {
603            if (keys.has(KEY_PGP_ID)) {
604                try {
605                    return keys.getLong(KEY_PGP_ID);
606                } catch (JSONException e) {
607                    return 0;
608                }
609            } else {
610                return 0;
611            }
612        }
613    }
614
615    public boolean setPgpSignId(long pgpID) {
616        synchronized (this.keys) {
617            try {
618                if (pgpID == 0) {
619                    keys.remove(KEY_PGP_ID);
620                } else {
621                    keys.put(KEY_PGP_ID, pgpID);
622                }
623            } catch (JSONException e) {
624                return false;
625            }
626            return true;
627        }
628    }
629
630    public Roster getRoster() {
631        return this.roster;
632    }
633
634    public Collection<Bookmark> getBookmarks() {
635        synchronized (this.bookmarks) {
636            return ImmutableList.copyOf(this.bookmarks.values());
637        }
638    }
639
640    public void setBookmarks(final Map<Jid, Bookmark> bookmarks) {
641        synchronized (this.bookmarks) {
642            this.bookmarks.clear();
643            this.bookmarks.putAll(bookmarks);
644        }
645    }
646
647    public void putBookmark(final Bookmark bookmark) {
648        synchronized (this.bookmarks) {
649            this.bookmarks.put(bookmark.getJid(), bookmark);
650        }
651    }
652
653    public void removeBookmark(Bookmark bookmark) {
654        synchronized (this.bookmarks) {
655            this.bookmarks.remove(bookmark.getJid());
656        }
657    }
658
659    public void removeBookmark(Jid jid) {
660        synchronized (this.bookmarks) {
661            this.bookmarks.remove(jid);
662        }
663    }
664
665    public Set<Jid> getBookmarkedJids() {
666        synchronized (this.bookmarks) {
667            return new HashSet<>(this.bookmarks.keySet());
668        }
669    }
670
671    public Bookmark getBookmark(final Jid jid) {
672        synchronized (this.bookmarks) {
673            return this.bookmarks.get(jid.asBareJid());
674        }
675    }
676
677    public boolean setAvatar(final String filename) {
678        if (this.avatar != null && this.avatar.equals(filename)) {
679            return false;
680        } else {
681            this.avatar = filename;
682            return true;
683        }
684    }
685
686    public String getAvatar() {
687        return this.avatar;
688    }
689
690    public void activateGracePeriod(final long duration) {
691        if (duration > 0) {
692            this.mEndGracePeriod = SystemClock.elapsedRealtime() + duration;
693        }
694    }
695
696    public void deactivateGracePeriod() {
697        this.mEndGracePeriod = 0L;
698    }
699
700    public boolean inGracePeriod() {
701        return SystemClock.elapsedRealtime() < this.mEndGracePeriod;
702    }
703
704    public String getShareableUri() {
705        List<XmppUri.Fingerprint> fingerprints = this.getFingerprints();
706        final String uri = "xmpp:" + this.getJid().asBareJid().toString();
707        if (fingerprints.isEmpty()) {
708            return uri;
709        } else {
710            return XmppUri.getFingerprintUri(uri, fingerprints, ';');
711        }
712    }
713
714    public String getShareableLink() {
715        List<XmppUri.Fingerprint> fingerprints = this.getFingerprints();
716        String uri =
717                "https://conversations.im/i/"
718                        + XmppUri.lameUrlEncode(this.getJid().asBareJid().toString());
719        if (fingerprints.isEmpty()) {
720            return uri;
721        } else {
722            return XmppUri.getFingerprintUri(uri, fingerprints, '&');
723        }
724    }
725
726    private List<XmppUri.Fingerprint> getFingerprints() {
727        ArrayList<XmppUri.Fingerprint> fingerprints = new ArrayList<>();
728        if (axolotlService == null) {
729            return fingerprints;
730        }
731        fingerprints.add(
732                new XmppUri.Fingerprint(
733                        XmppUri.FingerprintType.OMEMO,
734                        axolotlService.getOwnFingerprint().substring(2),
735                        axolotlService.getOwnDeviceId()));
736        for (XmppAxolotlSession session : axolotlService.findOwnSessions()) {
737            if (session.getTrust().isVerified() && session.getTrust().isActive()) {
738                fingerprints.add(
739                        new XmppUri.Fingerprint(
740                                XmppUri.FingerprintType.OMEMO,
741                                session.getFingerprint().substring(2).replaceAll("\\s", ""),
742                                session.getRemoteAddress().getDeviceId()));
743            }
744        }
745        return fingerprints;
746    }
747
748    public boolean isBlocked(final ListItem contact) {
749        final Jid jid = contact.getJid();
750        return jid != null
751                && (blocklist.contains(jid.asBareJid()) || blocklist.contains(jid.getDomain()));
752    }
753
754    public boolean isBlocked(final Jid jid) {
755        return jid != null && blocklist.contains(jid.asBareJid());
756    }
757
758    public Collection<Jid> getBlocklist() {
759        return this.blocklist;
760    }
761
762    public void clearBlocklist() {
763        getBlocklist().clear();
764    }
765
766    public boolean isOnlineAndConnected() {
767        return this.getStatus() == State.ONLINE && this.getXmppConnection() != null;
768    }
769
770    @Override
771    public int getAvatarBackgroundColor() {
772        return UIHelper.getColorForName(jid.asBareJid().toString());
773    }
774
775    @Override
776    public String getAvatarName() {
777        throw new IllegalStateException("This method should not be called");
778    }
779
780    public enum State {
781        DISABLED(false, false),
782        LOGGED_OUT(false, false),
783        OFFLINE(false),
784        CONNECTING(false),
785        ONLINE(false),
786        NO_INTERNET(false),
787        CONNECTION_TIMEOUT,
788        UNAUTHORIZED,
789        TEMPORARY_AUTH_FAILURE,
790        SERVER_NOT_FOUND,
791        REGISTRATION_SUCCESSFUL(false),
792        REGISTRATION_FAILED(true, false),
793        REGISTRATION_WEB(true, false),
794        REGISTRATION_CONFLICT(true, false),
795        REGISTRATION_NOT_SUPPORTED(true, false),
796        REGISTRATION_PLEASE_WAIT(true, false),
797        REGISTRATION_INVALID_TOKEN(true, false),
798        REGISTRATION_PASSWORD_TOO_WEAK(true, false),
799        TLS_ERROR,
800        TLS_ERROR_DOMAIN,
801        CHANNEL_BINDING,
802        INCOMPATIBLE_SERVER,
803        INCOMPATIBLE_CLIENT,
804        TOR_NOT_AVAILABLE,
805        DOWNGRADE_ATTACK,
806        SESSION_FAILURE,
807        BIND_FAILURE,
808        HOST_UNKNOWN,
809        STREAM_ERROR,
810        SEE_OTHER_HOST,
811        STREAM_OPENING_ERROR,
812        POLICY_VIOLATION,
813        PAYMENT_REQUIRED,
814        MISSING_INTERNET_PERMISSION(false);
815
816        private final boolean isError;
817        private final boolean attemptReconnect;
818
819        State(final boolean isError) {
820            this(isError, true);
821        }
822
823        State(final boolean isError, final boolean reconnect) {
824            this.isError = isError;
825            this.attemptReconnect = reconnect;
826        }
827
828        State() {
829            this(true, true);
830        }
831
832        public boolean isError() {
833            return this.isError;
834        }
835
836        public boolean isAttemptReconnect() {
837            return this.attemptReconnect;
838        }
839
840        public int getReadableId() {
841            switch (this) {
842                case DISABLED:
843                    return R.string.account_status_disabled;
844                case LOGGED_OUT:
845                    return R.string.account_state_logged_out;
846                case ONLINE:
847                    return R.string.account_status_online;
848                case CONNECTING:
849                    return R.string.account_status_connecting;
850                case OFFLINE:
851                    return R.string.account_status_offline;
852                case UNAUTHORIZED:
853                    return R.string.account_status_unauthorized;
854                case SERVER_NOT_FOUND:
855                    return R.string.account_status_not_found;
856                case NO_INTERNET:
857                    return R.string.account_status_no_internet;
858                case CONNECTION_TIMEOUT:
859                    return R.string.account_status_connection_timeout;
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 CHANNEL_BINDING:
881                    return R.string.account_status_channel_binding;
882                case TOR_NOT_AVAILABLE:
883                    return R.string.account_status_tor_unavailable;
884                case BIND_FAILURE:
885                    return R.string.account_status_bind_failure;
886                case SESSION_FAILURE:
887                    return R.string.session_failure;
888                case DOWNGRADE_ATTACK:
889                    return R.string.sasl_downgrade;
890                case HOST_UNKNOWN:
891                    return R.string.account_status_host_unknown;
892                case POLICY_VIOLATION:
893                    return R.string.account_status_policy_violation;
894                case REGISTRATION_PLEASE_WAIT:
895                    return R.string.registration_please_wait;
896                case REGISTRATION_PASSWORD_TOO_WEAK:
897                    return R.string.registration_password_too_weak;
898                case STREAM_ERROR:
899                    return R.string.account_status_stream_error;
900                case STREAM_OPENING_ERROR:
901                    return R.string.account_status_stream_opening_error;
902                case PAYMENT_REQUIRED:
903                    return R.string.payment_required;
904                case SEE_OTHER_HOST:
905                    return R.string.reconnect_on_other_host;
906                case MISSING_INTERNET_PERMISSION:
907                    return R.string.missing_internet_permission;
908                case TEMPORARY_AUTH_FAILURE:
909                    return R.string.account_status_temporary_auth_failure;
910                default:
911                    return R.string.account_status_unknown;
912            }
913        }
914    }
915}