Account.java

  1package eu.siacs.conversations.entities;
  2
  3import android.content.ContentValues;
  4import android.database.Cursor;
  5import android.os.SystemClock;
  6
  7import net.java.otr4j.crypto.OtrCryptoEngineImpl;
  8import net.java.otr4j.crypto.OtrCryptoException;
  9
 10import org.json.JSONException;
 11import org.json.JSONObject;
 12
 13import java.security.PublicKey;
 14import java.security.interfaces.DSAPublicKey;
 15import java.util.Collection;
 16import java.util.List;
 17import java.util.concurrent.CopyOnWriteArrayList;
 18import java.util.concurrent.CopyOnWriteArraySet;
 19
 20import eu.siacs.conversations.Config;
 21import eu.siacs.conversations.R;
 22import eu.siacs.conversations.crypto.OtrEngine;
 23import eu.siacs.conversations.services.XmppConnectionService;
 24import eu.siacs.conversations.xmpp.XmppConnection;
 25import eu.siacs.conversations.xmpp.jid.InvalidJidException;
 26import eu.siacs.conversations.xmpp.jid.Jid;
 27
 28public class Account extends AbstractEntity {
 29
 30	public static final String TABLENAME = "accounts";
 31
 32	public static final String USERNAME = "username";
 33	public static final String SERVER = "server";
 34	public static final String PASSWORD = "password";
 35	public static final String OPTIONS = "options";
 36	public static final String ROSTERVERSION = "rosterversion";
 37	public static final String KEYS = "keys";
 38	public static final String AVATAR = "avatar";
 39
 40	public static final String PINNED_MECHANISM_KEY = "pinned_mechanism";
 41
 42	public static final int OPTION_USETLS = 0;
 43	public static final int OPTION_DISABLED = 1;
 44	public static final int OPTION_REGISTER = 2;
 45	public static final int OPTION_USECOMPRESSION = 3;
 46
 47	public static enum State {
 48		DISABLED,
 49		OFFLINE,
 50		CONNECTING,
 51		ONLINE,
 52		NO_INTERNET,
 53		UNAUTHORIZED(true),
 54		SERVER_NOT_FOUND(true),
 55		REGISTRATION_FAILED(true),
 56		REGISTRATION_CONFLICT(true),
 57		REGISTRATION_SUCCESSFUL,
 58		REGISTRATION_NOT_SUPPORTED(true),
 59		SECURITY_ERROR(true),
 60		INCOMPATIBLE_SERVER(true);
 61
 62		private final boolean isError;
 63
 64		public boolean isError() {
 65			return this.isError;
 66		}
 67
 68		private State(final boolean isError) {
 69			this.isError = isError;
 70		}
 71
 72		private State() {
 73			this(false);
 74		}
 75
 76		public int getReadableId() {
 77			switch (this) {
 78				case DISABLED:
 79					return R.string.account_status_disabled;
 80				case ONLINE:
 81					return R.string.account_status_online;
 82				case CONNECTING:
 83					return R.string.account_status_connecting;
 84				case OFFLINE:
 85					return R.string.account_status_offline;
 86				case UNAUTHORIZED:
 87					return R.string.account_status_unauthorized;
 88				case SERVER_NOT_FOUND:
 89					return R.string.account_status_not_found;
 90				case NO_INTERNET:
 91					return R.string.account_status_no_internet;
 92				case REGISTRATION_FAILED:
 93					return R.string.account_status_regis_fail;
 94				case REGISTRATION_CONFLICT:
 95					return R.string.account_status_regis_conflict;
 96				case REGISTRATION_SUCCESSFUL:
 97					return R.string.account_status_regis_success;
 98				case REGISTRATION_NOT_SUPPORTED:
 99					return R.string.account_status_regis_not_sup;
100				case SECURITY_ERROR:
101					return R.string.account_status_security_error;
102				case INCOMPATIBLE_SERVER:
103					return R.string.account_status_incompatible_server;
104				default:
105					return R.string.account_status_unknown;
106			}
107		}
108	}
109
110	public List<Conversation> pendingConferenceJoins = new CopyOnWriteArrayList<>();
111	public List<Conversation> pendingConferenceLeaves = new CopyOnWriteArrayList<>();
112	protected Jid jid;
113	protected String password;
114	protected int options = 0;
115	protected String rosterVersion;
116	protected State status = State.OFFLINE;
117	protected JSONObject keys = new JSONObject();
118	protected String avatar;
119	protected boolean online = false;
120	private OtrEngine otrEngine = null;
121	private XmppConnection xmppConnection = null;
122	private long mEndGracePeriod = 0L;
123	private String otrFingerprint;
124	private final Roster roster = new Roster(this);
125	private List<Bookmark> bookmarks = new CopyOnWriteArrayList<>();
126	private final Collection<Jid> blocklist = new CopyOnWriteArraySet<>();
127
128	public Account() {
129		this.uuid = "0";
130	}
131
132	public Account(final Jid jid, final String password) {
133		this(java.util.UUID.randomUUID().toString(), jid,
134				password, 0, null, "", null);
135	}
136
137	public Account(final String uuid, final Jid jid,
138			final String password, final int options, final String rosterVersion, final String keys,
139			final String avatar) {
140		this.uuid = uuid;
141		this.jid = jid;
142		if (jid.isBareJid()) {
143			this.setResource("mobile");
144		}
145		this.password = password;
146		this.options = options;
147		this.rosterVersion = rosterVersion;
148		try {
149			this.keys = new JSONObject(keys);
150		} catch (final JSONException ignored) {
151
152		}
153		this.avatar = avatar;
154	}
155
156	public static Account fromCursor(final Cursor cursor) {
157		Jid jid = null;
158		try {
159			jid = Jid.fromParts(cursor.getString(cursor.getColumnIndex(USERNAME)),
160					cursor.getString(cursor.getColumnIndex(SERVER)), "mobile");
161		} catch (final InvalidJidException ignored) {
162		}
163		return new Account(cursor.getString(cursor.getColumnIndex(UUID)),
164				jid,
165				cursor.getString(cursor.getColumnIndex(PASSWORD)),
166				cursor.getInt(cursor.getColumnIndex(OPTIONS)),
167				cursor.getString(cursor.getColumnIndex(ROSTERVERSION)),
168				cursor.getString(cursor.getColumnIndex(KEYS)),
169				cursor.getString(cursor.getColumnIndex(AVATAR)));
170	}
171
172	public boolean isOptionSet(final int option) {
173		return ((options & (1 << option)) != 0);
174	}
175
176	public void setOption(final int option, final boolean value) {
177		if (value) {
178			this.options |= 1 << option;
179		} else {
180			this.options &= ~(1 << option);
181		}
182	}
183
184	public String getUsername() {
185		return jid.getLocalpart();
186	}
187
188	public void setUsername(final String username) throws InvalidJidException {
189		jid = Jid.fromParts(username, jid.getDomainpart(), jid.getResourcepart());
190	}
191
192	public Jid getServer() {
193		return jid.toDomainJid();
194	}
195
196	public void setServer(final String server) throws InvalidJidException {
197		jid = Jid.fromParts(jid.getLocalpart(), server, jid.getResourcepart());
198	}
199
200	public String getPassword() {
201		return password;
202	}
203
204	public void setPassword(final String password) {
205		this.password = password;
206	}
207
208	public State getStatus() {
209		if (isOptionSet(OPTION_DISABLED)) {
210			return State.DISABLED;
211		} else {
212			return this.status;
213		}
214	}
215
216	public void setStatus(final State status) {
217		this.status = status;
218	}
219
220	public boolean errorStatus() {
221		return getStatus().isError();
222	}
223
224	public boolean hasErrorStatus() {
225		return getXmppConnection() != null && getStatus().isError() && getXmppConnection().getAttempt() >= 2;
226	}
227
228	public String getResource() {
229		return jid.getResourcepart();
230	}
231
232	public void setResource(final String resource) {
233		try {
234			jid = Jid.fromParts(jid.getLocalpart(), jid.getDomainpart(), resource);
235		} catch (final InvalidJidException ignored) {
236		}
237	}
238
239	public Jid getJid() {
240		return jid;
241	}
242
243	public JSONObject getKeys() {
244		return keys;
245	}
246
247	public boolean setKey(final String keyName, final String keyValue) {
248		try {
249			this.keys.put(keyName, keyValue);
250			return true;
251		} catch (final JSONException e) {
252			return false;
253		}
254	}
255
256	@Override
257	public ContentValues getContentValues() {
258		final ContentValues values = new ContentValues();
259		values.put(UUID, uuid);
260		values.put(USERNAME, jid.getLocalpart());
261		values.put(SERVER, jid.getDomainpart());
262		values.put(PASSWORD, password);
263		values.put(OPTIONS, options);
264		values.put(KEYS, this.keys.toString());
265		values.put(ROSTERVERSION, rosterVersion);
266		values.put(AVATAR, avatar);
267		return values;
268	}
269
270	public void initOtrEngine(final XmppConnectionService context) {
271		this.otrEngine = new OtrEngine(context, this);
272	}
273
274	public OtrEngine getOtrEngine() {
275		return this.otrEngine;
276	}
277
278	public XmppConnection getXmppConnection() {
279		return this.xmppConnection;
280	}
281
282	public void setXmppConnection(final XmppConnection connection) {
283		this.xmppConnection = connection;
284	}
285
286	public String getOtrFingerprint() {
287		if (this.otrFingerprint == null) {
288			try {
289				if (this.otrEngine == null) {
290					return null;
291				}
292				final PublicKey publicKey = this.otrEngine.getPublicKey();
293				if (publicKey == null || !(publicKey instanceof DSAPublicKey)) {
294					return null;
295				}
296				this.otrFingerprint = new OtrCryptoEngineImpl().getFingerprint(publicKey);
297				return this.otrFingerprint;
298			} catch (final OtrCryptoException ignored) {
299				return null;
300			}
301		} else {
302			return this.otrFingerprint;
303		}
304	}
305
306	public String getRosterVersion() {
307		if (this.rosterVersion == null) {
308			return "";
309		} else {
310			return this.rosterVersion;
311		}
312	}
313
314	public void setRosterVersion(final String version) {
315		this.rosterVersion = version;
316	}
317
318	public int countPresences() {
319		return this.getRoster().getContact(this.getJid().toBareJid()).getPresences().size();
320	}
321
322	public String getPgpSignature() {
323		if (keys.has("pgp_signature")) {
324			try {
325				return keys.getString("pgp_signature");
326			} catch (final JSONException e) {
327				return null;
328			}
329		} else {
330			return null;
331		}
332	}
333
334	public Roster getRoster() {
335		return this.roster;
336	}
337
338	public List<Bookmark> getBookmarks() {
339		return this.bookmarks;
340	}
341
342	public void setBookmarks(final List<Bookmark> bookmarks) {
343		this.bookmarks = bookmarks;
344	}
345
346	public boolean hasBookmarkFor(final Jid conferenceJid) {
347		for (final Bookmark bookmark : this.bookmarks) {
348			final Jid jid = bookmark.getJid();
349			if (jid != null && jid.equals(conferenceJid.toBareJid())) {
350				return true;
351			}
352		}
353		return false;
354	}
355
356	public boolean setAvatar(final String filename) {
357		if (this.avatar != null && this.avatar.equals(filename)) {
358			return false;
359		} else {
360			this.avatar = filename;
361			return true;
362		}
363	}
364
365	public String getAvatar() {
366		return this.avatar;
367	}
368
369	public void activateGracePeriod() {
370		this.mEndGracePeriod = SystemClock.elapsedRealtime()
371			+ (Config.CARBON_GRACE_PERIOD * 1000);
372	}
373
374	public void deactivateGracePeriod() {
375		this.mEndGracePeriod = 0L;
376	}
377
378	public boolean inGracePeriod() {
379		return SystemClock.elapsedRealtime() < this.mEndGracePeriod;
380	}
381
382	public String getShareableUri() {
383		final String fingerprint = this.getOtrFingerprint();
384		if (fingerprint != null) {
385			return "xmpp:" + this.getJid().toBareJid().toString() + "?otr-fingerprint="+fingerprint;
386		} else {
387			return "xmpp:" + this.getJid().toBareJid().toString();
388		}
389	}
390
391	public boolean isBlocked(final ListItem contact) {
392		final Jid jid = contact.getJid();
393		return jid != null && (blocklist.contains(jid.toBareJid()) || blocklist.contains(jid.toDomainJid()));
394	}
395
396	public boolean isBlocked(final Jid jid) {
397		return jid != null && blocklist.contains(jid.toBareJid());
398	}
399
400	public Collection<Jid> getBlocklist() {
401		return this.blocklist;
402	}
403
404	public void clearBlocklist() {
405		getBlocklist().clear();
406	}
407
408	public boolean isOnlineAndConnected() {
409		return this.getStatus() == State.ONLINE && this.getXmppConnection() != null;
410	}
411}