Message.java

  1package eu.siacs.conversations.entities;
  2
  3import java.net.MalformedURLException;
  4import java.net.URL;
  5import java.util.Arrays;
  6
  7import eu.siacs.conversations.Config;
  8import eu.siacs.conversations.R;
  9import android.content.ContentValues;
 10import android.content.Context;
 11import android.database.Cursor;
 12
 13public class Message extends AbstractEntity {
 14
 15	public static final String TABLENAME = "messages";
 16
 17	public static final int STATUS_RECEIVED = 0;
 18	public static final int STATUS_UNSEND = 1;
 19	public static final int STATUS_SEND = 2;
 20	public static final int STATUS_SEND_FAILED = 3;
 21	public static final int STATUS_SEND_REJECTED = 4;
 22	public static final int STATUS_WAITING = 5;
 23	public static final int STATUS_OFFERED = 6;
 24	public static final int STATUS_SEND_RECEIVED = 7;
 25	public static final int STATUS_SEND_DISPLAYED = 8;
 26
 27	public static final int ENCRYPTION_NONE = 0;
 28	public static final int ENCRYPTION_PGP = 1;
 29	public static final int ENCRYPTION_OTR = 2;
 30	public static final int ENCRYPTION_DECRYPTED = 3;
 31	public static final int ENCRYPTION_DECRYPTION_FAILED = 4;
 32
 33	public static final int TYPE_TEXT = 0;
 34	public static final int TYPE_IMAGE = 1;
 35	public static final int TYPE_AUDIO = 2;
 36	public static final int TYPE_STATUS = 3;
 37	public static final int TYPE_PRIVATE = 4;
 38
 39	public static String CONVERSATION = "conversationUuid";
 40	public static String COUNTERPART = "counterpart";
 41	public static String TRUE_COUNTERPART = "trueCounterpart";
 42	public static String BODY = "body";
 43	public static String TIME_SENT = "timeSent";
 44	public static String ENCRYPTION = "encryption";
 45	public static String STATUS = "status";
 46	public static String TYPE = "type";
 47	public static String REMOTE_MSG_ID = "remoteMsgId";
 48
 49	protected String conversationUuid;
 50	protected String counterpart;
 51	protected String trueCounterpart;
 52	protected String body;
 53	protected String encryptedBody;
 54	protected long timeSent;
 55	protected int encryption;
 56	protected int status;
 57	protected int type;
 58	protected boolean read = true;
 59	protected String remoteMsgId = null;
 60
 61	protected Conversation conversation = null;
 62	protected Downloadable downloadable = null;
 63	public boolean markable = false;
 64
 65	private Message() {
 66
 67	}
 68
 69	public Message(Conversation conversation, String body, int encryption) {
 70		this(java.util.UUID.randomUUID().toString(), conversation.getUuid(),
 71				conversation.getContactJid(), null, body, System
 72						.currentTimeMillis(), encryption,
 73				Message.STATUS_UNSEND, TYPE_TEXT, null);
 74		this.conversation = conversation;
 75	}
 76
 77	public Message(Conversation conversation, String counterpart, String body,
 78			int encryption, int status) {
 79		this(java.util.UUID.randomUUID().toString(), conversation.getUuid(),
 80				counterpart, null, body, System.currentTimeMillis(),
 81				encryption, status, TYPE_TEXT, null);
 82		this.conversation = conversation;
 83	}
 84
 85	public Message(String uuid, String conversationUUid, String counterpart,
 86			String trueCounterpart, String body, long timeSent, int encryption,
 87			int status, int type, String remoteMsgId) {
 88		this.uuid = uuid;
 89		this.conversationUuid = conversationUUid;
 90		this.counterpart = counterpart;
 91		this.trueCounterpart = trueCounterpart;
 92		this.body = body;
 93		this.timeSent = timeSent;
 94		this.encryption = encryption;
 95		this.status = status;
 96		this.type = type;
 97		this.remoteMsgId = remoteMsgId;
 98	}
 99
100	@Override
101	public ContentValues getContentValues() {
102		ContentValues values = new ContentValues();
103		values.put(UUID, uuid);
104		values.put(CONVERSATION, conversationUuid);
105		values.put(COUNTERPART, counterpart);
106		values.put(TRUE_COUNTERPART, trueCounterpart);
107		values.put(BODY, body);
108		values.put(TIME_SENT, timeSent);
109		values.put(ENCRYPTION, encryption);
110		values.put(STATUS, status);
111		values.put(TYPE, type);
112		values.put(REMOTE_MSG_ID, remoteMsgId);
113		return values;
114	}
115
116	public String getConversationUuid() {
117		return conversationUuid;
118	}
119
120	public Conversation getConversation() {
121		return this.conversation;
122	}
123
124	public String getCounterpart() {
125		return counterpart;
126	}
127
128	public Contact getContact() {
129		if (this.conversation.getMode() == Conversation.MODE_SINGLE) {
130			return this.conversation.getContact();
131		} else {
132			if (this.trueCounterpart == null) {
133				return null;
134			} else {
135				return this.conversation.getAccount().getRoster()
136						.getContactFromRoster(this.trueCounterpart);
137			}
138		}
139	}
140
141	public String getBody() {
142		return body;
143	}
144
145	public String getReadableBody(Context context) {
146		if (encryption == ENCRYPTION_PGP) {
147			return context.getText(R.string.encrypted_message_received)
148					.toString();
149		} else if (encryption == ENCRYPTION_DECRYPTION_FAILED) {
150			return context.getText(R.string.decryption_failed).toString();
151		} else if (type == TYPE_IMAGE) {
152			return context.getText(R.string.image_file).toString();
153		} else {
154			return body.trim();
155		}
156	}
157
158	public long getTimeSent() {
159		return timeSent;
160	}
161
162	public int getEncryption() {
163		return encryption;
164	}
165
166	public int getStatus() {
167		return status;
168	}
169
170	public String getRemoteMsgId() {
171		return this.remoteMsgId;
172	}
173
174	public void setRemoteMsgId(String id) {
175		this.remoteMsgId = id;
176	}
177
178	public static Message fromCursor(Cursor cursor) {
179		return new Message(cursor.getString(cursor.getColumnIndex(UUID)),
180				cursor.getString(cursor.getColumnIndex(CONVERSATION)),
181				cursor.getString(cursor.getColumnIndex(COUNTERPART)),
182				cursor.getString(cursor.getColumnIndex(TRUE_COUNTERPART)),
183				cursor.getString(cursor.getColumnIndex(BODY)),
184				cursor.getLong(cursor.getColumnIndex(TIME_SENT)),
185				cursor.getInt(cursor.getColumnIndex(ENCRYPTION)),
186				cursor.getInt(cursor.getColumnIndex(STATUS)),
187				cursor.getInt(cursor.getColumnIndex(TYPE)),
188				cursor.getString(cursor.getColumnIndex(REMOTE_MSG_ID)));
189	}
190
191	public void setConversation(Conversation conv) {
192		this.conversation = conv;
193	}
194
195	public void setStatus(int status) {
196		this.status = status;
197	}
198
199	public boolean isRead() {
200		return this.read;
201	}
202
203	public void markRead() {
204		this.read = true;
205	}
206
207	public void markUnread() {
208		this.read = false;
209	}
210
211	public void setTime(long time) {
212		this.timeSent = time;
213	}
214
215	public void setEncryption(int encryption) {
216		this.encryption = encryption;
217	}
218
219	public void setBody(String body) {
220		this.body = body;
221	}
222
223	public String getEncryptedBody() {
224		return this.encryptedBody;
225	}
226
227	public void setEncryptedBody(String body) {
228		this.encryptedBody = body;
229	}
230
231	public void setType(int type) {
232		this.type = type;
233	}
234
235	public int getType() {
236		return this.type;
237	}
238
239	public void setPresence(String presence) {
240		if (presence == null) {
241			this.counterpart = this.counterpart.split("/", 2)[0];
242		} else {
243			this.counterpart = this.counterpart.split("/", 2)[0] + "/"
244					+ presence;
245		}
246	}
247
248	public void setTrueCounterpart(String trueCounterpart) {
249		this.trueCounterpart = trueCounterpart;
250	}
251
252	public String getPresence() {
253		String[] counterparts = this.counterpart.split("/", 2);
254		if (counterparts.length == 2) {
255			return counterparts[1];
256		} else {
257			if (this.counterpart.contains("/")) {
258				return "";
259			} else {
260				return null;
261			}
262		}
263	}
264
265	public void setDownloadable(Downloadable downloadable) {
266		this.downloadable = downloadable;
267	}
268
269	public Downloadable getDownloadable() {
270		return this.downloadable;
271	}
272
273	public static Message createStatusMessage(Conversation conversation) {
274		Message message = new Message();
275		message.setType(Message.TYPE_STATUS);
276		message.setConversation(conversation);
277		return message;
278	}
279
280	public void setCounterpart(String counterpart) {
281		this.counterpart = counterpart;
282	}
283
284	public boolean equals(Message message) {
285		if ((this.remoteMsgId != null) && (this.body != null)
286				&& (this.counterpart != null)) {
287			return this.remoteMsgId.equals(message.getRemoteMsgId())
288					&& this.body.equals(message.getBody())
289					&& this.counterpart.equals(message.getCounterpart());
290		} else {
291			return false;
292		}
293	}
294
295	public Message next() {
296		int index = this.conversation.getMessages().indexOf(this);
297		if (index < 0 || index >= this.conversation.getMessages().size() - 1) {
298			return null;
299		} else {
300			return this.conversation.getMessages().get(index + 1);
301		}
302	}
303
304	public Message prev() {
305		int index = this.conversation.getMessages().indexOf(this);
306		if (index <= 0 || index > this.conversation.getMessages().size()) {
307			return null;
308		} else {
309			return this.conversation.getMessages().get(index - 1);
310		}
311	}
312
313	public boolean mergable(Message message) {
314		if (message == null) {
315			return false;
316		}
317		return (message.getType() == Message.TYPE_TEXT
318				&& this.getDownloadable() == null
319				&& message.getDownloadable() == null
320				&& message.getEncryption() != Message.ENCRYPTION_PGP
321				&& this.getType() == message.getType()
322				&& this.getEncryption() == message.getEncryption()
323				&& this.getCounterpart().equals(message.getCounterpart())
324				&& (message.getTimeSent() - this.getTimeSent()) <= (Config.MESSAGE_MERGE_WINDOW * 1000) && ((this
325				.getStatus() == message.getStatus() || ((this.getStatus() == Message.STATUS_SEND || this
326				.getStatus() == Message.STATUS_SEND_RECEIVED) && (message
327				.getStatus() == Message.STATUS_UNSEND
328				|| message.getStatus() == Message.STATUS_SEND || message
329					.getStatus() == Message.STATUS_SEND_DISPLAYED)))));
330	}
331
332	public String getMergedBody() {
333		Message next = this.next();
334		if (this.mergable(next)) {
335			return body.trim() + '\n' + next.getMergedBody();
336		}
337		return body.trim();
338	}
339
340	public int getMergedStatus() {
341		Message next = this.next();
342		if (this.mergable(next)) {
343			return next.getMergedStatus();
344		} else {
345			return getStatus();
346		}
347	}
348
349	public long getMergedTimeSent() {
350		Message next = this.next();
351		if (this.mergable(next)) {
352			return next.getMergedTimeSent();
353		} else {
354			return getTimeSent();
355		}
356	}
357
358	public boolean wasMergedIntoPrevious() {
359		Message prev = this.prev();
360		if (prev == null) {
361			return false;
362		} else {
363			return prev.mergable(this);
364		}
365	}
366
367	public boolean bodyContainsDownloadable() {
368		Contact contact = this.getContact();
369		if (status <= STATUS_RECEIVED
370				&& (contact == null || !contact.trusted())) {
371			return false;
372		}
373		try {
374			URL url = new URL(this.getBody());
375			if (!url.getProtocol().equalsIgnoreCase("http")
376					&& !url.getProtocol().equalsIgnoreCase("https")) {
377				return false;
378			}
379			if (url.getPath() == null) {
380				return false;
381			}
382			String[] pathParts = url.getPath().split("/");
383			String filename = pathParts[pathParts.length - 1];
384			String[] extensionParts = filename.split("\\.");
385			if (extensionParts.length == 2
386					&& Arrays.asList(Downloadable.VALID_EXTENSIONS).contains(
387							extensionParts[extensionParts.length - 1])) {
388				return true;
389			} else if (extensionParts.length == 3
390					&& Arrays.asList(Downloadable.VALID_CRYPTO_EXTENSIONS)
391							.contains(extensionParts.length - 1)
392					&& Arrays.asList(Downloadable.VALID_EXTENSIONS).contains(
393							extensionParts[extensionParts.length - 2])) {
394				return true;
395			} else {
396				return false;
397			}
398		} catch (MalformedURLException e) {
399			return false;
400		}
401	}
402
403	public ImageParams getImageParams() {
404		ImageParams params = new ImageParams();
405		if (this.downloadable != null) {
406			params.size = this.downloadable.getFileSize();
407		}
408		if (body == null) {
409			return params;
410		}
411		String parts[] = body.split(",");
412		if (parts.length == 1) {
413			try {
414				params.size = Long.parseLong(parts[0]);
415			} catch (NumberFormatException e) {
416				params.origin = parts[0];
417			}
418		} else if (parts.length == 3) {
419			try {
420				params.size = Long.parseLong(parts[0]);
421			} catch (NumberFormatException e) {
422				params.size = 0;
423			}
424			try {
425				params.width = Integer.parseInt(parts[1]);
426			} catch (NumberFormatException e) {
427				params.width = 0;
428			}
429			try {
430				params.height = Integer.parseInt(parts[2]);
431			} catch (NumberFormatException e) {
432				params.height = 0;
433			}
434		} else if (parts.length == 4) {
435			params.origin = parts[0];
436			try {
437				params.size = Long.parseLong(parts[1]);
438			} catch (NumberFormatException e) {
439				params.size = 0;
440			}
441			try {
442				params.width = Integer.parseInt(parts[2]);
443			} catch (NumberFormatException e) {
444				params.width = 0;
445			}
446			try {
447				params.height = Integer.parseInt(parts[3]);
448			} catch (NumberFormatException e) {
449				params.height = 0;
450			}
451		}
452		return params;
453	}
454
455	public class ImageParams {
456		public long size = 0;
457		public int width = 0;
458		public int height = 0;
459		public String origin;
460	}
461}