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        STREAM_OPENING_ERROR,
791        POLICY_VIOLATION,
792        PAYMENT_REQUIRED,
793        MISSING_INTERNET_PERMISSION(false);
794
795        private final boolean isError;
796        private final boolean attemptReconnect;
797
798        State(final boolean isError) {
799            this(isError, true);
800        }
801
802        State(final boolean isError, final boolean reconnect) {
803            this.isError = isError;
804            this.attemptReconnect = reconnect;
805        }
806
807        State() {
808            this(true, true);
809        }
810
811        public boolean isError() {
812            return this.isError;
813        }
814
815        public boolean isAttemptReconnect() {
816            return this.attemptReconnect;
817        }
818
819        public int getReadableId() {
820            switch (this) {
821                case DISABLED:
822                    return R.string.account_status_disabled;
823                case ONLINE:
824                    return R.string.account_status_online;
825                case CONNECTING:
826                    return R.string.account_status_connecting;
827                case OFFLINE:
828                    return R.string.account_status_offline;
829                case UNAUTHORIZED:
830                    return R.string.account_status_unauthorized;
831                case SERVER_NOT_FOUND:
832                    return R.string.account_status_not_found;
833                case NO_INTERNET:
834                    return R.string.account_status_no_internet;
835                case REGISTRATION_FAILED:
836                    return R.string.account_status_regis_fail;
837                case REGISTRATION_WEB:
838                    return R.string.account_status_regis_web;
839                case REGISTRATION_CONFLICT:
840                    return R.string.account_status_regis_conflict;
841                case REGISTRATION_SUCCESSFUL:
842                    return R.string.account_status_regis_success;
843                case REGISTRATION_NOT_SUPPORTED:
844                    return R.string.account_status_regis_not_sup;
845                case REGISTRATION_INVALID_TOKEN:
846                    return R.string.account_status_regis_invalid_token;
847                case TLS_ERROR:
848                    return R.string.account_status_tls_error;
849                case TLS_ERROR_DOMAIN:
850                    return R.string.account_status_tls_error_domain;
851                case INCOMPATIBLE_SERVER:
852                    return R.string.account_status_incompatible_server;
853                case INCOMPATIBLE_CLIENT:
854                    return R.string.account_status_incompatible_client;
855                case TOR_NOT_AVAILABLE:
856                    return R.string.account_status_tor_unavailable;
857                case BIND_FAILURE:
858                    return R.string.account_status_bind_failure;
859                case SESSION_FAILURE:
860                    return R.string.session_failure;
861                case DOWNGRADE_ATTACK:
862                    return R.string.sasl_downgrade;
863                case HOST_UNKNOWN:
864                    return R.string.account_status_host_unknown;
865                case POLICY_VIOLATION:
866                    return R.string.account_status_policy_violation;
867                case REGISTRATION_PLEASE_WAIT:
868                    return R.string.registration_please_wait;
869                case REGISTRATION_PASSWORD_TOO_WEAK:
870                    return R.string.registration_password_too_weak;
871                case STREAM_ERROR:
872                    return R.string.account_status_stream_error;
873                case STREAM_OPENING_ERROR:
874                    return R.string.account_status_stream_opening_error;
875                case PAYMENT_REQUIRED:
876                    return R.string.payment_required;
877                case MISSING_INTERNET_PERMISSION:
878                    return R.string.missing_internet_permission;
879                case TEMPORARY_AUTH_FAILURE:
880                    return R.string.account_status_temporary_auth_failure;
881                default:
882                    return R.string.account_status_unknown;
883            }
884        }
885    }
886}