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