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				x.setAttribute("name", url.getFile());
110				x.setAttribute("fileid", url.getHost());
111				return packet;
112			} else {
113				content = url.toString();
114				packet.addChild("x", Namespace.OOB).addChild("url").setContent(content);
115			}
116		} else {
117			content = message.getBody();
118		}
119		packet.setBody(content);
120		return packet;
121	}
122
123	public MessagePacket generatePgpChat(Message message) {
124		MessagePacket packet = preparePacket(message);
125		if (message.hasFileOnRemoteHost()) {
126			final String url = message.getFileParams().url.toString();
127			packet.setBody(url);
128			packet.addChild("x", Namespace.OOB).addChild("url").setContent(url);
129		} else {
130			if (Config.supportUnencrypted()) {
131				packet.setBody(PGP_FALLBACK_MESSAGE);
132			}
133			if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
134				packet.addChild("x", "jabber:x:encrypted").setContent(message.getEncryptedBody());
135			} else if (message.getEncryption() == Message.ENCRYPTION_PGP) {
136				packet.addChild("x", "jabber:x:encrypted").setContent(message.getBody());
137			}
138			packet.addChild("encryption", "urn:xmpp:eme:0")
139					.setAttribute("namespace", "jabber:x:encrypted");
140		}
141		return packet;
142	}
143
144	public MessagePacket generateChatState(Conversation conversation) {
145		final Account account = conversation.getAccount();
146		MessagePacket packet = new MessagePacket();
147		packet.setType(conversation.getMode() == Conversation.MODE_MULTI ? MessagePacket.TYPE_GROUPCHAT : MessagePacket.TYPE_CHAT);
148		packet.setTo(conversation.getJid().asBareJid());
149		packet.setFrom(account.getJid());
150		packet.addChild(ChatState.toElement(conversation.getOutgoingChatState()));
151		packet.addChild("no-store", "urn:xmpp:hints");
152		packet.addChild("no-storage", "urn:xmpp:hints"); //wrong! don't copy this. Its *store*
153		return packet;
154	}
155
156	public MessagePacket confirm(final Account account, final Jid to, final String id, final Jid counterpart, final boolean groupChat) {
157		MessagePacket packet = new MessagePacket();
158		packet.setType(groupChat ? MessagePacket.TYPE_GROUPCHAT : MessagePacket.TYPE_CHAT);
159		packet.setTo(groupChat ? to.asBareJid() : to);
160		packet.setFrom(account.getJid());
161		Element displayed = packet.addChild("displayed", "urn:xmpp:chat-markers:0");
162		displayed.setAttribute("id", id);
163		if (groupChat && counterpart != null) {
164			displayed.setAttribute("sender", counterpart.toString());
165		}
166		packet.addChild("store", "urn:xmpp:hints");
167		return packet;
168	}
169
170	public MessagePacket conferenceSubject(Conversation conversation, String subject) {
171		MessagePacket packet = new MessagePacket();
172		packet.setType(MessagePacket.TYPE_GROUPCHAT);
173		packet.setTo(conversation.getJid().asBareJid());
174		Element subjectChild = new Element("subject");
175		subjectChild.setContent(subject);
176		packet.addChild(subjectChild);
177		packet.setFrom(conversation.getAccount().getJid().asBareJid());
178		return packet;
179	}
180
181	public MessagePacket directInvite(final Conversation conversation, final Jid contact) {
182		MessagePacket packet = new MessagePacket();
183		packet.setType(MessagePacket.TYPE_NORMAL);
184		packet.setTo(contact);
185		packet.setFrom(conversation.getAccount().getJid());
186		Element x = packet.addChild("x", "jabber:x:conference");
187		x.setAttribute("jid", conversation.getJid().asBareJid().toString());
188		String password = conversation.getMucOptions().getPassword();
189		if (password != null) {
190			x.setAttribute("password", password);
191		}
192		return packet;
193	}
194
195	public MessagePacket invite(Conversation conversation, Jid contact) {
196		MessagePacket packet = new MessagePacket();
197		packet.setTo(conversation.getJid().asBareJid());
198		packet.setFrom(conversation.getAccount().getJid());
199		Element x = new Element("x");
200		x.setAttribute("xmlns", "http://jabber.org/protocol/muc#user");
201		Element invite = new Element("invite");
202		invite.setAttribute("to", contact.asBareJid().toString());
203		x.addChild(invite);
204		packet.addChild(x);
205		return packet;
206	}
207
208	public MessagePacket received(Account account, MessagePacket originalMessage, ArrayList<String> namespaces, int type) {
209		MessagePacket receivedPacket = new MessagePacket();
210		receivedPacket.setType(type);
211		receivedPacket.setTo(originalMessage.getFrom());
212		receivedPacket.setFrom(account.getJid());
213		for (String namespace : namespaces) {
214			receivedPacket.addChild("received", namespace).setAttribute("id", originalMessage.getId());
215		}
216		receivedPacket.addChild("store", "urn:xmpp:hints");
217		return receivedPacket;
218	}
219
220	public MessagePacket received(Account account, Jid to, String id) {
221		MessagePacket packet = new MessagePacket();
222		packet.setFrom(account.getJid());
223		packet.setTo(to);
224		packet.addChild("received", "urn:xmpp:receipts").setAttribute("id", id);
225		packet.addChild("store", "urn:xmpp:hints");
226		return packet;
227	}
228}