Account.java

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