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