MessageGenerator.java

  1package eu.siacs.conversations.generator;
  2
  3import android.util.Log;
  4
  5import java.net.URL;
  6import java.text.SimpleDateFormat;
  7import java.util.ArrayList;
  8import java.util.Date;
  9import java.util.Locale;
 10import java.util.TimeZone;
 11
 12import eu.siacs.conversations.Config;
 13import eu.siacs.conversations.crypto.axolotl.AxolotlService;
 14import eu.siacs.conversations.crypto.axolotl.XmppAxolotlMessage;
 15import eu.siacs.conversations.entities.Account;
 16import eu.siacs.conversations.entities.Contact;
 17import eu.siacs.conversations.entities.Conversation;
 18import eu.siacs.conversations.entities.Message;
 19import eu.siacs.conversations.http.P1S3UrlStreamHandler;
 20import eu.siacs.conversations.services.XmppConnectionService;
 21import eu.siacs.conversations.xml.Element;
 22import eu.siacs.conversations.xml.Namespace;
 23import eu.siacs.conversations.xmpp.chatstate.ChatState;
 24import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
 25import rocks.xmpp.addr.Jid;
 26
 27public class MessageGenerator extends AbstractGenerator {
 28	private static final String OMEMO_FALLBACK_MESSAGE = "I sent you an OMEMO encrypted message but your client doesn’t seem to support that. Find more information on https://conversations.im/omemo";
 29	private static final String PGP_FALLBACK_MESSAGE = "I sent you a PGP encrypted message but your client doesn’t seem to support that.";
 30
 31	public MessageGenerator(XmppConnectionService service) {
 32		super(service);
 33	}
 34
 35	private MessagePacket preparePacket(Message message) {
 36		Conversation conversation = (Conversation) message.getConversation();
 37		Account account = conversation.getAccount();
 38		MessagePacket packet = new MessagePacket();
 39		final boolean isWithSelf = conversation.getContact().isSelf();
 40		if (conversation.getMode() == Conversation.MODE_SINGLE) {
 41			packet.setTo(message.getCounterpart());
 42			packet.setType(MessagePacket.TYPE_CHAT);
 43			if (this.mXmppConnectionService.indicateReceived() && !isWithSelf) {
 44				packet.addChild("request", "urn:xmpp:receipts");
 45			}
 46		} else if (message.getType() == Message.TYPE_PRIVATE) { //TODO files and images might be private as well
 47			packet.setTo(message.getCounterpart());
 48			packet.setType(MessagePacket.TYPE_CHAT);
 49			packet.addChild("x", "http://jabber.org/protocol/muc#user");
 50			if (this.mXmppConnectionService.indicateReceived()) {
 51				packet.addChild("request", "urn:xmpp:receipts");
 52			}
 53		} else {
 54			packet.setTo(message.getCounterpart().asBareJid());
 55			packet.setType(MessagePacket.TYPE_GROUPCHAT);
 56		}
 57		if (conversation.isSingleOrPrivateAndNonAnonymous() && message.getType() != Message.TYPE_PRIVATE) {
 58			packet.addChild("markable", "urn:xmpp:chat-markers:0");
 59		}
 60		packet.setFrom(account.getJid());
 61		packet.setId(message.getUuid());
 62		packet.addChild("origin-id", Namespace.STANZA_IDS).setAttribute("id", message.getUuid());
 63		if (message.edited()) {
 64			packet.addChild("replace", "urn:xmpp:message-correct:0").setAttribute("id", message.getEditedId());
 65		}
 66		return packet;
 67	}
 68
 69	public void addDelay(MessagePacket packet, long timestamp) {
 70		final SimpleDateFormat mDateFormat = new SimpleDateFormat(
 71				"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
 72		mDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
 73		Element delay = packet.addChild("delay", "urn:xmpp:delay");
 74		Date date = new Date(timestamp);
 75		delay.setAttribute("stamp", mDateFormat.format(date));
 76	}
 77
 78	public MessagePacket generateAxolotlChat(Message message, XmppAxolotlMessage axolotlMessage) {
 79		MessagePacket packet = preparePacket(message);
 80		if (axolotlMessage == null) {
 81			return null;
 82		}
 83		packet.setAxolotlMessage(axolotlMessage.toElement());
 84		packet.setBody(OMEMO_FALLBACK_MESSAGE);
 85		packet.addChild("store", "urn:xmpp:hints");
 86		packet.addChild("encryption", "urn:xmpp:eme:0")
 87				.setAttribute("name", "OMEMO")
 88				.setAttribute("namespace", AxolotlService.PEP_PREFIX);
 89		return packet;
 90	}
 91
 92	public MessagePacket generateKeyTransportMessage(Jid to, XmppAxolotlMessage axolotlMessage) {
 93		MessagePacket packet = new MessagePacket();
 94		packet.setType(MessagePacket.TYPE_CHAT);
 95		packet.setTo(to);
 96		packet.setAxolotlMessage(axolotlMessage.toElement());
 97		packet.addChild("store", "urn:xmpp:hints");
 98		return packet;
 99	}
100
101	public MessagePacket generateChat(Message message) {
102		MessagePacket packet = preparePacket(message);
103		String content;
104		if (message.hasFileOnRemoteHost()) {
105			Message.FileParams fileParams = message.getFileParams();
106			final URL url = fileParams.url;
107			if (P1S3UrlStreamHandler.PROTOCOL_NAME.equals(url.getProtocol())) {
108				Element x = packet.addChild("x", Namespace.P1_S3_FILE_TRANSFER);
109				final String file = url.getFile();
110				x.setAttribute("name", file.charAt(0) == '/' ? file.substring(1) : file);
111				x.setAttribute("fileid", url.getHost());
112				return packet;
113			} else {
114				content = url.toString();
115				packet.addChild("x", Namespace.OOB).addChild("url").setContent(content);
116			}
117		} else {
118			content = message.getBody();
119		}
120		packet.setBody(content);
121		return packet;
122	}
123
124	public MessagePacket generatePgpChat(Message message) {
125		MessagePacket packet = preparePacket(message);
126		if (message.hasFileOnRemoteHost()) {
127			Message.FileParams fileParams = message.getFileParams();
128			final URL url = fileParams.url;
129			if (P1S3UrlStreamHandler.PROTOCOL_NAME.equals(url.getProtocol())) {
130				Element x = packet.addChild("x", Namespace.P1_S3_FILE_TRANSFER);
131				final String file = url.getFile();
132				x.setAttribute("name", file.charAt(0) == '/' ? file.substring(1) : file);
133				x.setAttribute("fileid", url.getHost());
134			} else {
135				packet.setBody(url.toString());
136				packet.addChild("x", Namespace.OOB).addChild("url").setContent(url.toString());
137			}
138		} else {
139			if (Config.supportUnencrypted()) {
140				packet.setBody(PGP_FALLBACK_MESSAGE);
141			}
142			if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
143				packet.addChild("x", "jabber:x:encrypted").setContent(message.getEncryptedBody());
144			} else if (message.getEncryption() == Message.ENCRYPTION_PGP) {
145				packet.addChild("x", "jabber:x:encrypted").setContent(message.getBody());
146			}
147			packet.addChild("encryption", "urn:xmpp:eme:0")
148					.setAttribute("namespace", "jabber:x:encrypted");
149		}
150		return packet;
151	}
152
153	public MessagePacket generateChatState(Conversation conversation) {
154		final Account account = conversation.getAccount();
155		MessagePacket packet = new MessagePacket();
156		packet.setType(conversation.getMode() == Conversation.MODE_MULTI ? MessagePacket.TYPE_GROUPCHAT : MessagePacket.TYPE_CHAT);
157		packet.setTo(conversation.getJid().asBareJid());
158		packet.setFrom(account.getJid());
159		packet.addChild(ChatState.toElement(conversation.getOutgoingChatState()));
160		packet.addChild("no-store", "urn:xmpp:hints");
161		packet.addChild("no-storage", "urn:xmpp:hints"); //wrong! don't copy this. Its *store*
162		return packet;
163	}
164
165	public MessagePacket confirm(final Account account, final Jid to, final String id, final Jid counterpart, final boolean groupChat) {
166		MessagePacket packet = new MessagePacket();
167		packet.setType(groupChat ? MessagePacket.TYPE_GROUPCHAT : MessagePacket.TYPE_CHAT);
168		packet.setTo(groupChat ? to.asBareJid() : to);
169		packet.setFrom(account.getJid());
170		Element displayed = packet.addChild("displayed", "urn:xmpp:chat-markers:0");
171		displayed.setAttribute("id", id);
172		if (groupChat && counterpart != null) {
173			displayed.setAttribute("sender", counterpart.toString());
174		}
175		packet.addChild("store", "urn:xmpp:hints");
176		return packet;
177	}
178
179	public MessagePacket conferenceSubject(Conversation conversation, String subject) {
180		MessagePacket packet = new MessagePacket();
181		packet.setType(MessagePacket.TYPE_GROUPCHAT);
182		packet.setTo(conversation.getJid().asBareJid());
183		packet.addChild("subject").setContent(subject);
184		packet.setFrom(conversation.getAccount().getJid().asBareJid());
185		return packet;
186	}
187
188	public MessagePacket directInvite(final Conversation conversation, final Jid contact) {
189		MessagePacket packet = new MessagePacket();
190		packet.setType(MessagePacket.TYPE_NORMAL);
191		packet.setTo(contact);
192		packet.setFrom(conversation.getAccount().getJid());
193		Element x = packet.addChild("x", "jabber:x:conference");
194		x.setAttribute("jid", conversation.getJid().asBareJid().toString());
195		String password = conversation.getMucOptions().getPassword();
196		if (password != null) {
197			x.setAttribute("password", password);
198		}
199		return packet;
200	}
201
202	public MessagePacket invite(Conversation conversation, Jid contact) {
203		MessagePacket packet = new MessagePacket();
204		packet.setTo(conversation.getJid().asBareJid());
205		packet.setFrom(conversation.getAccount().getJid());
206		Element x = new Element("x");
207		x.setAttribute("xmlns", "http://jabber.org/protocol/muc#user");
208		Element invite = new Element("invite");
209		invite.setAttribute("to", contact.asBareJid().toString());
210		x.addChild(invite);
211		packet.addChild(x);
212		return packet;
213	}
214
215	public MessagePacket received(Account account, MessagePacket originalMessage, ArrayList<String> namespaces, int type) {
216		MessagePacket receivedPacket = new MessagePacket();
217		receivedPacket.setType(type);
218		receivedPacket.setTo(originalMessage.getFrom());
219		receivedPacket.setFrom(account.getJid());
220		for (String namespace : namespaces) {
221			receivedPacket.addChild("received", namespace).setAttribute("id", originalMessage.getId());
222		}
223		receivedPacket.addChild("store", "urn:xmpp:hints");
224		return receivedPacket;
225	}
226
227	public MessagePacket received(Account account, Jid to, String id) {
228		MessagePacket packet = new MessagePacket();
229		packet.setFrom(account.getJid());
230		packet.setTo(to);
231		packet.addChild("received", "urn:xmpp:receipts").setAttribute("id", id);
232		packet.addChild("store", "urn:xmpp:hints");
233		return packet;
234	}
235}