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 resetPinnedMechanism() {
366        this.pinnedMechanism = null;
367        this.pinnedChannelBinding = null;
368        setKey(Account.KEY_PINNED_MECHANISM, String.valueOf(-1));
369    }
370
371    public int getPinnedMechanismPriority() {
372        final int fallback = getKeyAsInt(KEY_PINNED_MECHANISM, -1);
373        if (Strings.isNullOrEmpty(this.pinnedMechanism)) {
374            return fallback;
375        }
376        final SaslMechanism saslMechanism = getPinnedMechanism();
377        if (saslMechanism == null) {
378            return fallback;
379        } else {
380            return saslMechanism.getPriority();
381        }
382    }
383
384    private SaslMechanism getPinnedMechanism() {
385        final String mechanism = Strings.nullToEmpty(this.pinnedMechanism);
386        final ChannelBinding channelBinding = ChannelBinding.get(this.pinnedChannelBinding);
387        return new SaslMechanism.Factory(this).of(mechanism, channelBinding);
388    }
389
390    public HashedToken getFastMechanism() {
391        final HashedToken.Mechanism fastMechanism = HashedToken.Mechanism.ofOrNull(this.fastMechanism);
392        final String token = this.fastToken;
393        if (fastMechanism == null || Strings.isNullOrEmpty(token)) {
394            return null;
395        }
396        if (fastMechanism.hashFunction.equals("SHA-256")) {
397            return new HashedTokenSha256(this, fastMechanism.channelBinding);
398        } else if (fastMechanism.hashFunction.equals("SHA-512")) {
399            return new HashedTokenSha512(this, fastMechanism.channelBinding);
400        } else {
401            return null;
402        }
403    }
404
405    public SaslMechanism getQuickStartMechanism() {
406        final HashedToken hashedTokenMechanism = getFastMechanism();
407        if (hashedTokenMechanism != null) {
408            return hashedTokenMechanism;
409        }
410        return getPinnedMechanism();
411    }
412
413    public String getFastToken() {
414        return this.fastToken;
415    }
416
417    public State getTrueStatus() {
418        return this.status;
419    }
420
421    public boolean errorStatus() {
422        return getStatus().isError();
423    }
424
425    public boolean hasErrorStatus() {
426        return getXmppConnection() != null
427                && (getStatus().isError() || getStatus() == State.CONNECTING)
428                && getXmppConnection().getAttempt() >= 3;
429    }
430
431    public Presence.Status getPresenceStatus() {
432        return this.presenceStatus;
433    }
434
435    public void setPresenceStatus(Presence.Status status) {
436        this.presenceStatus = status;
437    }
438
439    public String getPresenceStatusMessage() {
440        return this.presenceStatusMessage;
441    }
442
443    public void setPresenceStatusMessage(String message) {
444        this.presenceStatusMessage = message;
445    }
446
447    public String getResource() {
448        return jid.getResource();
449    }
450
451    public void setResource(final String resource) {
452        this.jid = this.jid.withResource(resource);
453    }
454
455    public Jid getJid() {
456        return jid;
457    }
458
459    public JSONObject getKeys() {
460        return keys;
461    }
462
463    public String getKey(final String name) {
464        synchronized (this.keys) {
465            return this.keys.optString(name, null);
466        }
467    }
468
469    public int getKeyAsInt(final String name, int defaultValue) {
470        String key = getKey(name);
471        try {
472            return key == null ? defaultValue : Integer.parseInt(key);
473        } catch (NumberFormatException e) {
474            return defaultValue;
475        }
476    }
477
478    public boolean setKey(final String keyName, final String keyValue) {
479        synchronized (this.keys) {
480            try {
481                this.keys.put(keyName, keyValue);
482                return true;
483            } catch (final JSONException e) {
484                return false;
485            }
486        }
487    }
488
489    public void setPrivateKeyAlias(final String alias) {
490        setKey("private_key_alias", alias);
491    }
492
493    public String getPrivateKeyAlias() {
494        return getKey("private_key_alias");
495    }
496
497    @Override
498    public ContentValues getContentValues() {
499        final ContentValues values = new ContentValues();
500        values.put(UUID, uuid);
501        values.put(USERNAME, jid.getLocal());
502        values.put(SERVER, jid.getDomain().toEscapedString());
503        values.put(PASSWORD, password);
504        values.put(OPTIONS, options);
505        synchronized (this.keys) {
506            values.put(KEYS, this.keys.toString());
507        }
508        values.put(ROSTERVERSION, rosterVersion);
509        values.put(AVATAR, avatar);
510        values.put(DISPLAY_NAME, displayName);
511        values.put(HOSTNAME, hostname);
512        values.put(PORT, port);
513        values.put(STATUS, presenceStatus.toShowString());
514        values.put(STATUS_MESSAGE, presenceStatusMessage);
515        values.put(RESOURCE, jid.getResource());
516        values.put(PINNED_MECHANISM, pinnedMechanism);
517        values.put(PINNED_CHANNEL_BINDING, pinnedChannelBinding);
518        values.put(FAST_MECHANISM, this.fastMechanism);
519        values.put(FAST_TOKEN, this.fastToken);
520        return values;
521    }
522
523    public AxolotlService getAxolotlService() {
524        return axolotlService;
525    }
526
527    public void initAccountServices(final XmppConnectionService context) {
528        this.axolotlService = new AxolotlService(this, context);
529        this.pgpDecryptionService = new PgpDecryptionService(context);
530        if (xmppConnection != null) {
531            xmppConnection.addOnAdvancedStreamFeaturesAvailableListener(axolotlService);
532        }
533    }
534
535    public PgpDecryptionService getPgpDecryptionService() {
536        return this.pgpDecryptionService;
537    }
538
539    public XmppConnection getXmppConnection() {
540        return this.xmppConnection;
541    }
542
543    public void setXmppConnection(final XmppConnection connection) {
544        this.xmppConnection = connection;
545    }
546
547    public String getRosterVersion() {
548        if (this.rosterVersion == null) {
549            return "";
550        } else {
551            return this.rosterVersion;
552        }
553    }
554
555    public void setRosterVersion(final String version) {
556        this.rosterVersion = version;
557    }
558
559    public int countPresences() {
560        return this.getSelfContact().getPresences().size();
561    }
562
563    public int activeDevicesWithRtpCapability() {
564        int i = 0;
565        for (Presence presence : getSelfContact().getPresences().getPresences()) {
566            if (RtpCapability.check(presence) != RtpCapability.Capability.NONE) {
567                i++;
568            }
569        }
570        return i;
571    }
572
573    public String getPgpSignature() {
574        return getKey(KEY_PGP_SIGNATURE);
575    }
576
577    public boolean setPgpSignature(String signature) {
578        return setKey(KEY_PGP_SIGNATURE, signature);
579    }
580
581    public boolean unsetPgpSignature() {
582        synchronized (this.keys) {
583            return keys.remove(KEY_PGP_SIGNATURE) != null;
584        }
585    }
586
587    public long getPgpId() {
588        synchronized (this.keys) {
589            if (keys.has(KEY_PGP_ID)) {
590                try {
591                    return keys.getLong(KEY_PGP_ID);
592                } catch (JSONException e) {
593                    return 0;
594                }
595            } else {
596                return 0;
597            }
598        }
599    }
600
601    public boolean setPgpSignId(long pgpID) {
602        synchronized (this.keys) {
603            try {
604                if (pgpID == 0) {
605                    keys.remove(KEY_PGP_ID);
606                } else {
607                    keys.put(KEY_PGP_ID, pgpID);
608                }
609            } catch (JSONException e) {
610                return false;
611            }
612            return true;
613        }
614    }
615
616    public Roster getRoster() {
617        return this.roster;
618    }
619
620    public Collection<Bookmark> getBookmarks() {
621        synchronized (this.bookmarks) {
622            return ImmutableList.copyOf(this.bookmarks.values());
623        }
624    }
625
626    public void setBookmarks(final Map<Jid, Bookmark> bookmarks) {
627        synchronized (this.bookmarks) {
628            this.bookmarks.clear();
629            this.bookmarks.putAll(bookmarks);
630        }
631    }
632
633    public void putBookmark(final Bookmark bookmark) {
634        synchronized (this.bookmarks) {
635            this.bookmarks.put(bookmark.getJid(), bookmark);
636        }
637    }
638
639    public void removeBookmark(Bookmark bookmark) {
640        synchronized (this.bookmarks) {
641            this.bookmarks.remove(bookmark.getJid());
642        }
643    }
644
645    public void removeBookmark(Jid jid) {
646        synchronized (this.bookmarks) {
647            this.bookmarks.remove(jid);
648        }
649    }
650
651    public Set<Jid> getBookmarkedJids() {
652        synchronized (this.bookmarks) {
653            return new HashSet<>(this.bookmarks.keySet());
654        }
655    }
656
657    public Bookmark getBookmark(final Jid jid) {
658        synchronized (this.bookmarks) {
659            return this.bookmarks.get(jid.asBareJid());
660        }
661    }
662
663    public boolean setAvatar(final String filename) {
664        if (this.avatar != null && this.avatar.equals(filename)) {
665            return false;
666        } else {
667            this.avatar = filename;
668            return true;
669        }
670    }
671
672    public String getAvatar() {
673        return this.avatar;
674    }
675
676    public void activateGracePeriod(final long duration) {
677        if (duration > 0) {
678            this.mEndGracePeriod = SystemClock.elapsedRealtime() + duration;
679        }
680    }
681
682    public void deactivateGracePeriod() {
683        this.mEndGracePeriod = 0L;
684    }
685
686    public boolean inGracePeriod() {
687        return SystemClock.elapsedRealtime() < this.mEndGracePeriod;
688    }
689
690    public String getShareableUri() {
691        List<XmppUri.Fingerprint> fingerprints = this.getFingerprints();
692        String uri = "xmpp:" + this.getJid().asBareJid().toEscapedString();
693        if (fingerprints.size() > 0) {
694            return XmppUri.getFingerprintUri(uri, fingerprints, ';');
695        } else {
696            return uri;
697        }
698    }
699
700    public String getShareableLink() {
701        List<XmppUri.Fingerprint> fingerprints = this.getFingerprints();
702        String uri =
703                "https://conversations.im/i/"
704                        + XmppUri.lameUrlEncode(this.getJid().asBareJid().toEscapedString());
705        if (fingerprints.size() > 0) {
706            return XmppUri.getFingerprintUri(uri, fingerprints, '&');
707        } else {
708            return uri;
709        }
710    }
711
712    private List<XmppUri.Fingerprint> getFingerprints() {
713        ArrayList<XmppUri.Fingerprint> fingerprints = new ArrayList<>();
714        if (axolotlService == null) {
715            return fingerprints;
716        }
717        fingerprints.add(
718                new XmppUri.Fingerprint(
719                        XmppUri.FingerprintType.OMEMO,
720                        axolotlService.getOwnFingerprint().substring(2),
721                        axolotlService.getOwnDeviceId()));
722        for (XmppAxolotlSession session : axolotlService.findOwnSessions()) {
723            if (session.getTrust().isVerified() && session.getTrust().isActive()) {
724                fingerprints.add(
725                        new XmppUri.Fingerprint(
726                                XmppUri.FingerprintType.OMEMO,
727                                session.getFingerprint().substring(2).replaceAll("\\s", ""),
728                                session.getRemoteAddress().getDeviceId()));
729            }
730        }
731        return fingerprints;
732    }
733
734    public boolean isBlocked(final ListItem contact) {
735        final Jid jid = contact.getJid();
736        return jid != null
737                && (blocklist.contains(jid.asBareJid()) || blocklist.contains(jid.getDomain()));
738    }
739
740    public boolean isBlocked(final Jid jid) {
741        return jid != null && blocklist.contains(jid.asBareJid());
742    }
743
744    public Collection<Jid> getBlocklist() {
745        return this.blocklist;
746    }
747
748    public void clearBlocklist() {
749        getBlocklist().clear();
750    }
751
752    public boolean isOnlineAndConnected() {
753        return this.getStatus() == State.ONLINE && this.getXmppConnection() != null;
754    }
755
756    @Override
757    public int getAvatarBackgroundColor() {
758        return UIHelper.getColorForName(jid.asBareJid().toString());
759    }
760
761    @Override
762    public String getAvatarName() {
763        throw new IllegalStateException("This method should not be called");
764    }
765
766    public enum State {
767        DISABLED(false, false),
768        OFFLINE(false),
769        CONNECTING(false),
770        ONLINE(false),
771        NO_INTERNET(false),
772        UNAUTHORIZED,
773        TEMPORARY_AUTH_FAILURE,
774        SERVER_NOT_FOUND,
775        REGISTRATION_SUCCESSFUL(false),
776        REGISTRATION_FAILED(true, false),
777        REGISTRATION_WEB(true, false),
778        REGISTRATION_CONFLICT(true, false),
779        REGISTRATION_NOT_SUPPORTED(true, false),
780        REGISTRATION_PLEASE_WAIT(true, false),
781        REGISTRATION_INVALID_TOKEN(true, false),
782        REGISTRATION_PASSWORD_TOO_WEAK(true, false),
783        TLS_ERROR,
784        TLS_ERROR_DOMAIN,
785        INCOMPATIBLE_SERVER,
786        INCOMPATIBLE_CLIENT,
787        TOR_NOT_AVAILABLE,
788        DOWNGRADE_ATTACK,
789        SESSION_FAILURE,
790        BIND_FAILURE,
791        HOST_UNKNOWN,
792        STREAM_ERROR,
793        STREAM_OPENING_ERROR,
794        POLICY_VIOLATION,
795        PAYMENT_REQUIRED,
796        MISSING_INTERNET_PERMISSION(false);
797
798        private final boolean isError;
799        private final boolean attemptReconnect;
800
801        State(final boolean isError) {
802            this(isError, true);
803        }
804
805        State(final boolean isError, final boolean reconnect) {
806            this.isError = isError;
807            this.attemptReconnect = reconnect;
808        }
809
810        State() {
811            this(true, true);
812        }
813
814        public boolean isError() {
815            return this.isError;
816        }
817
818        public boolean isAttemptReconnect() {
819            return this.attemptReconnect;
820        }
821
822        public int getReadableId() {
823            switch (this) {
824                case DISABLED:
825                    return R.string.account_status_disabled;
826                case ONLINE:
827                    return R.string.account_status_online;
828                case CONNECTING:
829                    return R.string.account_status_connecting;
830                case OFFLINE:
831                    return R.string.account_status_offline;
832                case UNAUTHORIZED:
833                    return R.string.account_status_unauthorized;
834                case SERVER_NOT_FOUND:
835                    return R.string.account_status_not_found;
836                case NO_INTERNET:
837                    return R.string.account_status_no_internet;
838                case REGISTRATION_FAILED:
839                    return R.string.account_status_regis_fail;
840                case REGISTRATION_WEB:
841                    return R.string.account_status_regis_web;
842                case REGISTRATION_CONFLICT:
843                    return R.string.account_status_regis_conflict;
844                case REGISTRATION_SUCCESSFUL:
845                    return R.string.account_status_regis_success;
846                case REGISTRATION_NOT_SUPPORTED:
847                    return R.string.account_status_regis_not_sup;
848                case REGISTRATION_INVALID_TOKEN:
849                    return R.string.account_status_regis_invalid_token;
850                case TLS_ERROR:
851                    return R.string.account_status_tls_error;
852                case TLS_ERROR_DOMAIN:
853                    return R.string.account_status_tls_error_domain;
854                case INCOMPATIBLE_SERVER:
855                    return R.string.account_status_incompatible_server;
856                case INCOMPATIBLE_CLIENT:
857                    return R.string.account_status_incompatible_client;
858                case TOR_NOT_AVAILABLE:
859                    return R.string.account_status_tor_unavailable;
860                case BIND_FAILURE:
861                    return R.string.account_status_bind_failure;
862                case SESSION_FAILURE:
863                    return R.string.session_failure;
864                case DOWNGRADE_ATTACK:
865                    return R.string.sasl_downgrade;
866                case HOST_UNKNOWN:
867                    return R.string.account_status_host_unknown;
868                case POLICY_VIOLATION:
869                    return R.string.account_status_policy_violation;
870                case REGISTRATION_PLEASE_WAIT:
871                    return R.string.registration_please_wait;
872                case REGISTRATION_PASSWORD_TOO_WEAK:
873                    return R.string.registration_password_too_weak;
874                case STREAM_ERROR:
875                    return R.string.account_status_stream_error;
876                case STREAM_OPENING_ERROR:
877                    return R.string.account_status_stream_opening_error;
878                case PAYMENT_REQUIRED:
879                    return R.string.payment_required;
880                case MISSING_INTERNET_PERMISSION:
881                    return R.string.missing_internet_permission;
882                case TEMPORARY_AUTH_FAILURE:
883                    return R.string.account_status_temporary_auth_failure;
884                default:
885                    return R.string.account_status_unknown;
886            }
887        }
888    }
889}