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