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