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