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