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