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