Account.java

  1package eu.siacs.conversations.entities;
  2
  3import android.content.ContentValues;
  4import android.database.Cursor;
  5import android.net.Uri;
  6import android.os.SystemClock;
  7import android.util.Log;
  8
  9import com.google.common.base.Strings;
 10import com.google.common.collect.ImmutableList;
 11
 12import org.json.JSONException;
 13import org.json.JSONObject;
 14
 15import java.util.ArrayList;
 16import java.util.Collection;
 17import java.util.HashMap;
 18import java.util.HashSet;
 19import java.util.List;
 20import java.util.Map;
 21import java.util.Set;
 22import java.util.concurrent.CopyOnWriteArraySet;
 23
 24import eu.siacs.conversations.Config;
 25import eu.siacs.conversations.R;
 26import eu.siacs.conversations.crypto.PgpDecryptionService;
 27import eu.siacs.conversations.crypto.axolotl.AxolotlService;
 28import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession;
 29import eu.siacs.conversations.crypto.sasl.ChannelBinding;
 30import eu.siacs.conversations.crypto.sasl.ChannelBindingMechanism;
 31import eu.siacs.conversations.crypto.sasl.HashedToken;
 32import eu.siacs.conversations.crypto.sasl.HashedTokenSha256;
 33import eu.siacs.conversations.crypto.sasl.HashedTokenSha512;
 34import eu.siacs.conversations.crypto.sasl.SaslMechanism;
 35import eu.siacs.conversations.crypto.sasl.ScramPlusMechanism;
 36import eu.siacs.conversations.services.AvatarService;
 37import eu.siacs.conversations.services.XmppConnectionService;
 38import eu.siacs.conversations.utils.UIHelper;
 39import eu.siacs.conversations.utils.XmppUri;
 40import eu.siacs.conversations.xmpp.Jid;
 41import eu.siacs.conversations.xmpp.XmppConnection;
 42import eu.siacs.conversations.xmpp.jingle.RtpCapability;
 43
 44public class Account extends AbstractEntity implements AvatarService.Avatarable {
 45
 46    public static final String TABLENAME = "accounts";
 47
 48    public static final String USERNAME = "username";
 49    public static final String SERVER = "server";
 50    public static final String PASSWORD = "password";
 51    public static final String OPTIONS = "options";
 52    public static final String ROSTERVERSION = "rosterversion";
 53    public static final String KEYS = "keys";
 54    public static final String AVATAR = "avatar";
 55    public static final String DISPLAY_NAME = "display_name";
 56    public static final String HOSTNAME = "hostname";
 57    public static final String PORT = "port";
 58    public static final String STATUS = "status";
 59    public static final String STATUS_MESSAGE = "status_message";
 60    public static final String RESOURCE = "resource";
 61    public static final String PINNED_MECHANISM = "pinned_mechanism";
 62    public static final String PINNED_CHANNEL_BINDING = "pinned_channel_binding";
 63    public static final String FAST_MECHANISM = "fast_mechanism";
 64    public static final String FAST_TOKEN = "fast_token";
 65
 66    public static final int OPTION_DISABLED = 1;
 67    public static final int OPTION_REGISTER = 2;
 68    public static final int OPTION_MAGIC_CREATE = 4;
 69    public static final int OPTION_REQUIRES_ACCESS_MODE_CHANGE = 5;
 70    public static final int OPTION_LOGGED_IN_SUCCESSFULLY = 6;
 71    public static final int OPTION_HTTP_UPLOAD_AVAILABLE = 7;
 72    public static final int OPTION_UNVERIFIED = 8;
 73    public static final int OPTION_FIXED_USERNAME = 9;
 74    public static final int OPTION_QUICKSTART_AVAILABLE = 10;
 75
 76    private static final String KEY_PGP_SIGNATURE = "pgp_signature";
 77    private static final String KEY_PGP_ID = "pgp_id";
 78    private static final String KEY_PINNED_MECHANISM = "pinned_mechanism";
 79    public static final String KEY_PRE_AUTH_REGISTRATION_TOKEN = "pre_auth_registration";
 80
 81    protected final JSONObject keys;
 82    private final Roster roster = new Roster(this);
 83    private final Collection<Jid> blocklist = new CopyOnWriteArraySet<>();
 84    public final Set<Conversation> pendingConferenceJoins = new HashSet<>();
 85    public final Set<Conversation> pendingConferenceLeaves = new HashSet<>();
 86    public final Set<Conversation> inProgressConferenceJoins = new HashSet<>();
 87    public final Set<Conversation> inProgressConferencePings = new HashSet<>();
 88    protected Jid jid;
 89    protected String password;
 90    protected int options = 0;
 91    protected State status = State.OFFLINE;
 92    private State lastErrorStatus = State.OFFLINE;
 93    protected String resource;
 94    protected String avatar;
 95    protected String hostname = null;
 96    protected int port = 5222;
 97    protected boolean online = false;
 98    private String rosterVersion;
 99    private String displayName = null;
100    private AxolotlService axolotlService = null;
101    private PgpDecryptionService pgpDecryptionService = null;
102    private XmppConnection xmppConnection = null;
103    private long mEndGracePeriod = 0L;
104    private final Map<Jid, Bookmark> bookmarks = new HashMap<>();
105    private boolean bookmarksLoaded = false;
106    private Presence.Status presenceStatus;
107    private String presenceStatusMessage;
108    private String pinnedMechanism;
109    private String pinnedChannelBinding;
110    private String fastMechanism;
111    private String fastToken;
112
113    public Account(final Jid jid, final String password) {
114        this(
115                java.util.UUID.randomUUID().toString(),
116                jid,
117                password,
118                0,
119                null,
120                "",
121                null,
122                null,
123                null,
124                5222,
125                Presence.Status.ONLINE,
126                null,
127                null,
128                null,
129                null,
130                null);
131    }
132
133    private Account(
134            final String uuid,
135            final Jid jid,
136            final String password,
137            final int options,
138            final String rosterVersion,
139            final String keys,
140            final String avatar,
141            String displayName,
142            String hostname,
143            int port,
144            final Presence.Status status,
145            String statusMessage,
146            final String pinnedMechanism,
147            final String pinnedChannelBinding,
148            final String fastMechanism,
149            final String fastToken) {
150        this.uuid = uuid;
151        this.jid = jid;
152        this.password = password;
153        this.options = options;
154        this.rosterVersion = rosterVersion;
155        JSONObject tmp;
156        try {
157            tmp = new JSONObject(keys);
158        } catch (JSONException e) {
159            tmp = new JSONObject();
160        }
161        this.keys = tmp;
162        this.avatar = avatar;
163        this.displayName = displayName;
164        this.hostname = hostname;
165        this.port = port;
166        this.presenceStatus = status;
167        this.presenceStatusMessage = statusMessage;
168        this.pinnedMechanism = pinnedMechanism;
169        this.pinnedChannelBinding = pinnedChannelBinding;
170        this.fastMechanism = fastMechanism;
171        this.fastToken = fastToken;
172    }
173
174    public static Account fromCursor(final Cursor cursor) {
175        final Jid jid;
176        try {
177            final String resource = cursor.getString(cursor.getColumnIndexOrThrow(RESOURCE));
178            jid =
179                    Jid.of(
180                            cursor.getString(cursor.getColumnIndexOrThrow(USERNAME)),
181                            cursor.getString(cursor.getColumnIndexOrThrow(SERVER)),
182                            resource == null || resource.trim().isEmpty() ? null : resource);
183        } catch (final IllegalArgumentException e) {
184            Log.d(
185                    Config.LOGTAG,
186                    cursor.getString(cursor.getColumnIndexOrThrow(USERNAME))
187                            + "@"
188                            + cursor.getString(cursor.getColumnIndexOrThrow(SERVER)));
189            throw new AssertionError(e);
190        }
191        return new Account(
192                cursor.getString(cursor.getColumnIndexOrThrow(UUID)),
193                jid,
194                cursor.getString(cursor.getColumnIndexOrThrow(PASSWORD)),
195                cursor.getInt(cursor.getColumnIndexOrThrow(OPTIONS)),
196                cursor.getString(cursor.getColumnIndexOrThrow(ROSTERVERSION)),
197                cursor.getString(cursor.getColumnIndexOrThrow(KEYS)),
198                cursor.getString(cursor.getColumnIndexOrThrow(AVATAR)),
199                cursor.getString(cursor.getColumnIndexOrThrow(DISPLAY_NAME)),
200                cursor.getString(cursor.getColumnIndexOrThrow(HOSTNAME)),
201                cursor.getInt(cursor.getColumnIndexOrThrow(PORT)),
202                Presence.Status.fromShowString(
203                        cursor.getString(cursor.getColumnIndexOrThrow(STATUS))),
204                cursor.getString(cursor.getColumnIndexOrThrow(STATUS_MESSAGE)),
205                cursor.getString(cursor.getColumnIndexOrThrow(PINNED_MECHANISM)),
206                cursor.getString(cursor.getColumnIndexOrThrow(PINNED_CHANNEL_BINDING)),
207                cursor.getString(cursor.getColumnIndexOrThrow(FAST_MECHANISM)),
208                cursor.getString(cursor.getColumnIndexOrThrow(FAST_TOKEN)));
209    }
210
211    public boolean httpUploadAvailable(long size) {
212        return xmppConnection != null && xmppConnection.getFeatures().httpUpload(size);
213    }
214
215    public boolean httpUploadAvailable() {
216        return isOptionSet(OPTION_HTTP_UPLOAD_AVAILABLE) || httpUploadAvailable(0);
217    }
218
219    public String getDisplayName() {
220        return displayName;
221    }
222
223    public void setDisplayName(String displayName) {
224        this.displayName = displayName;
225    }
226
227    public Contact getSelfContact() {
228        return getRoster().getContact(jid);
229    }
230
231    public boolean hasPendingPgpIntent(Conversation conversation) {
232        return pgpDecryptionService != null && pgpDecryptionService.hasPendingIntent(conversation);
233    }
234
235    public boolean isPgpDecryptionServiceConnected() {
236        return pgpDecryptionService != null && pgpDecryptionService.isConnected();
237    }
238
239    public boolean setShowErrorNotification(boolean newValue) {
240        boolean oldValue = showErrorNotification();
241        setKey("show_error", Boolean.toString(newValue));
242        return newValue != oldValue;
243    }
244
245    public boolean showErrorNotification() {
246        String key = getKey("show_error");
247        return key == null || Boolean.parseBoolean(key);
248    }
249
250    public boolean isEnabled() {
251        return !isOptionSet(Account.OPTION_DISABLED);
252    }
253
254    public boolean isOptionSet(final int option) {
255        return ((options & (1 << option)) != 0);
256    }
257
258    public boolean setOption(final int option, final boolean value) {
259        final int before = this.options;
260        if (value) {
261            this.options |= 1 << option;
262        } else {
263            this.options &= ~(1 << option);
264        }
265        return before != this.options;
266    }
267
268    public String getUsername() {
269        return jid.getEscapedLocal();
270    }
271
272    public boolean setJid(final Jid next) {
273        final Jid previousFull = this.jid;
274        final Jid prev = this.jid != null ? this.jid.asBareJid() : null;
275        final boolean changed = prev == null || (next != null && !prev.equals(next.asBareJid()));
276        if (changed) {
277            final AxolotlService oldAxolotlService = this.axolotlService;
278            if (oldAxolotlService != null) {
279                oldAxolotlService.destroy();
280                this.jid = next;
281                this.axolotlService = oldAxolotlService.makeNew();
282            }
283        }
284        this.jid = next;
285        return next != null && !next.equals(previousFull);
286    }
287
288    public Jid getDomain() {
289        return jid.getDomain();
290    }
291
292    public String getServer() {
293        return jid.getDomain().toEscapedString();
294    }
295
296    public String getPassword() {
297        return password;
298    }
299
300    public void setPassword(final String password) {
301        this.password = password;
302    }
303
304    public String getHostname() {
305        return Strings.nullToEmpty(this.hostname);
306    }
307
308    public void setHostname(String hostname) {
309        this.hostname = hostname;
310    }
311
312    public boolean isOnion() {
313        final String server = getServer();
314        return server != null && server.endsWith(".onion");
315    }
316
317    public int getPort() {
318        return this.port;
319    }
320
321    public void setPort(int port) {
322        this.port = port;
323    }
324
325    public State getStatus() {
326        if (isOptionSet(OPTION_DISABLED)) {
327            return State.DISABLED;
328        } else {
329            return this.status;
330        }
331    }
332
333    public State getLastErrorStatus() {
334        return this.lastErrorStatus;
335    }
336
337    public void setStatus(final State status) {
338        this.status = status;
339        if (status.isError || status == State.ONLINE) {
340            this.lastErrorStatus = status;
341        }
342    }
343
344    public void setPinnedMechanism(final SaslMechanism mechanism) {
345        this.pinnedMechanism = mechanism.getMechanism();
346        if (mechanism instanceof ChannelBindingMechanism) {
347            this.pinnedChannelBinding =
348                    ((ChannelBindingMechanism) mechanism).getChannelBinding().toString();
349        } else {
350            this.pinnedChannelBinding = null;
351        }
352    }
353
354    public void setFastToken(final HashedToken.Mechanism mechanism, final String token) {
355        this.fastMechanism = mechanism.name();
356        this.fastToken = token;
357    }
358
359    public void resetFastToken() {
360        this.fastMechanism = null;
361        this.fastToken = null;
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    public 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 boolean areBookmarksLoaded() { return bookmarksLoaded; }
626
627    public void setBookmarks(final Map<Jid, Bookmark> bookmarks) {
628        synchronized (this.bookmarks) {
629            this.bookmarks.clear();
630            this.bookmarks.putAll(bookmarks);
631            this.bookmarksLoaded = true;
632        }
633    }
634
635    public void putBookmark(final Bookmark bookmark) {
636        synchronized (this.bookmarks) {
637            this.bookmarks.put(bookmark.getJid(), bookmark);
638        }
639    }
640
641    public void removeBookmark(Bookmark bookmark) {
642        synchronized (this.bookmarks) {
643            this.bookmarks.remove(bookmark.getJid());
644        }
645    }
646
647    public void removeBookmark(Jid jid) {
648        synchronized (this.bookmarks) {
649            this.bookmarks.remove(jid);
650        }
651    }
652
653    public Set<Jid> getBookmarkedJids() {
654        synchronized (this.bookmarks) {
655            return new HashSet<>(this.bookmarks.keySet());
656        }
657    }
658
659    public Bookmark getBookmark(final Jid jid) {
660        synchronized (this.bookmarks) {
661            return this.bookmarks.get(jid.asBareJid());
662        }
663    }
664
665    public boolean setAvatar(final String filename) {
666        if (this.avatar != null && this.avatar.equals(filename)) {
667            return false;
668        } else {
669            this.avatar = filename;
670            return true;
671        }
672    }
673
674    public String getAvatar() {
675        return this.avatar;
676    }
677
678    public void activateGracePeriod(final long duration) {
679        if (duration > 0) {
680            this.mEndGracePeriod = SystemClock.elapsedRealtime() + duration;
681        }
682    }
683
684    public void deactivateGracePeriod() {
685        this.mEndGracePeriod = 0L;
686    }
687
688    public boolean inGracePeriod() {
689        return SystemClock.elapsedRealtime() < this.mEndGracePeriod;
690    }
691
692    public String getShareableUri() {
693        List<XmppUri.Fingerprint> fingerprints = this.getFingerprints();
694        String uri = "xmpp:" + Uri.encode(getJid().asBareJid().toEscapedString(), "@/+");
695        if (fingerprints.size() > 0) {
696            return XmppUri.getFingerprintUri(uri, fingerprints, ';');
697        } else {
698            return uri;
699        }
700    }
701
702    public String getShareableLink() {
703        List<XmppUri.Fingerprint> fingerprints = this.getFingerprints();
704        String uri =
705                "https://conversations.im/i/"
706                        + XmppUri.lameUrlEncode(this.getJid().asBareJid().toEscapedString());
707        if (fingerprints.size() > 0) {
708            return XmppUri.getFingerprintUri(uri, fingerprints, '&');
709        } else {
710            return uri;
711        }
712    }
713
714    private List<XmppUri.Fingerprint> getFingerprints() {
715        ArrayList<XmppUri.Fingerprint> fingerprints = new ArrayList<>();
716        if (axolotlService == null) {
717            return fingerprints;
718        }
719        fingerprints.add(
720                new XmppUri.Fingerprint(
721                        XmppUri.FingerprintType.OMEMO,
722                        axolotlService.getOwnFingerprint().substring(2),
723                        axolotlService.getOwnDeviceId()));
724        for (XmppAxolotlSession session : axolotlService.findOwnSessions()) {
725            if (session.getTrust().isVerified() && session.getTrust().isActive()) {
726                fingerprints.add(
727                        new XmppUri.Fingerprint(
728                                XmppUri.FingerprintType.OMEMO,
729                                session.getFingerprint().substring(2).replaceAll("\\s", ""),
730                                session.getRemoteAddress().getDeviceId()));
731            }
732        }
733        return fingerprints;
734    }
735
736    public boolean isBlocked(final ListItem contact) {
737        final Jid jid = contact.getJid();
738        return jid != null
739                && (blocklist.contains(jid.asBareJid()) || blocklist.contains(jid.getDomain()));
740    }
741
742    public boolean isBlocked(final Jid jid) {
743        return jid != null && blocklist.contains(jid.asBareJid());
744    }
745
746    public Collection<Jid> getBlocklist() {
747        return this.blocklist;
748    }
749
750    public void clearBlocklist() {
751        getBlocklist().clear();
752    }
753
754    public boolean isOnlineAndConnected() {
755        return this.getStatus() == State.ONLINE && this.getXmppConnection() != null;
756    }
757
758    @Override
759    public int getAvatarBackgroundColor() {
760        return UIHelper.getColorForName(jid.asBareJid().toString());
761    }
762
763    @Override
764    public String getAvatarName() {
765        throw new IllegalStateException("This method should not be called");
766    }
767
768    public enum State {
769        DISABLED(false, false),
770        OFFLINE(false),
771        CONNECTING(false),
772        ONLINE(false),
773        NO_INTERNET(false),
774        UNAUTHORIZED,
775        TEMPORARY_AUTH_FAILURE,
776        SERVER_NOT_FOUND,
777        REGISTRATION_SUCCESSFUL(false),
778        REGISTRATION_FAILED(true, false),
779        REGISTRATION_WEB(true, false),
780        REGISTRATION_CONFLICT(true, false),
781        REGISTRATION_NOT_SUPPORTED(true, false),
782        REGISTRATION_PLEASE_WAIT(true, false),
783        REGISTRATION_INVALID_TOKEN(true, false),
784        REGISTRATION_PASSWORD_TOO_WEAK(true, false),
785        TLS_ERROR,
786        TLS_ERROR_DOMAIN,
787        INCOMPATIBLE_SERVER,
788        INCOMPATIBLE_CLIENT,
789        TOR_NOT_AVAILABLE,
790        DOWNGRADE_ATTACK,
791        SESSION_FAILURE,
792        BIND_FAILURE,
793        HOST_UNKNOWN,
794        STREAM_ERROR,
795        STREAM_OPENING_ERROR,
796        POLICY_VIOLATION,
797        PAYMENT_REQUIRED,
798        MISSING_INTERNET_PERMISSION(false);
799
800        private final boolean isError;
801        private final boolean attemptReconnect;
802
803        State(final boolean isError) {
804            this(isError, true);
805        }
806
807        State(final boolean isError, final boolean reconnect) {
808            this.isError = isError;
809            this.attemptReconnect = reconnect;
810        }
811
812        State() {
813            this(true, true);
814        }
815
816        public boolean isError() {
817            return this.isError;
818        }
819
820        public boolean isAttemptReconnect() {
821            return this.attemptReconnect;
822        }
823
824        public int getReadableId() {
825            switch (this) {
826                case DISABLED:
827                    return R.string.account_status_disabled;
828                case ONLINE:
829                    return R.string.account_status_online;
830                case CONNECTING:
831                    return R.string.account_status_connecting;
832                case OFFLINE:
833                    return R.string.account_status_offline;
834                case UNAUTHORIZED:
835                    return R.string.account_status_unauthorized;
836                case SERVER_NOT_FOUND:
837                    return R.string.account_status_not_found;
838                case NO_INTERNET:
839                    return R.string.account_status_no_internet;
840                case REGISTRATION_FAILED:
841                    return R.string.account_status_regis_fail;
842                case REGISTRATION_WEB:
843                    return R.string.account_status_regis_web;
844                case REGISTRATION_CONFLICT:
845                    return R.string.account_status_regis_conflict;
846                case REGISTRATION_SUCCESSFUL:
847                    return R.string.account_status_regis_success;
848                case REGISTRATION_NOT_SUPPORTED:
849                    return R.string.account_status_regis_not_sup;
850                case REGISTRATION_INVALID_TOKEN:
851                    return R.string.account_status_regis_invalid_token;
852                case TLS_ERROR:
853                    return R.string.account_status_tls_error;
854                case TLS_ERROR_DOMAIN:
855                    return R.string.account_status_tls_error_domain;
856                case INCOMPATIBLE_SERVER:
857                    return R.string.account_status_incompatible_server;
858                case INCOMPATIBLE_CLIENT:
859                    return R.string.account_status_incompatible_client;
860                case TOR_NOT_AVAILABLE:
861                    return R.string.account_status_tor_unavailable;
862                case BIND_FAILURE:
863                    return R.string.account_status_bind_failure;
864                case SESSION_FAILURE:
865                    return R.string.session_failure;
866                case DOWNGRADE_ATTACK:
867                    return R.string.sasl_downgrade;
868                case HOST_UNKNOWN:
869                    return R.string.account_status_host_unknown;
870                case POLICY_VIOLATION:
871                    return R.string.account_status_policy_violation;
872                case REGISTRATION_PLEASE_WAIT:
873                    return R.string.registration_please_wait;
874                case REGISTRATION_PASSWORD_TOO_WEAK:
875                    return R.string.registration_password_too_weak;
876                case STREAM_ERROR:
877                    return R.string.account_status_stream_error;
878                case STREAM_OPENING_ERROR:
879                    return R.string.account_status_stream_opening_error;
880                case PAYMENT_REQUIRED:
881                    return R.string.payment_required;
882                case MISSING_INTERNET_PERMISSION:
883                    return R.string.missing_internet_permission;
884                case TEMPORARY_AUTH_FAILURE:
885                    return R.string.account_status_temporary_auth_failure;
886                default:
887                    return R.string.account_status_unknown;
888            }
889        }
890    }
891}