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