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