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