1package eu.siacs.conversations.generator;
2
3
4import android.os.Bundle;
5import android.util.Base64;
6import android.util.Log;
7
8import org.whispersystems.libaxolotl.IdentityKey;
9import org.whispersystems.libaxolotl.ecc.ECPublicKey;
10import org.whispersystems.libaxolotl.state.PreKeyRecord;
11import org.whispersystems.libaxolotl.state.SignedPreKeyRecord;
12
13import java.security.cert.CertificateEncodingException;
14import java.security.cert.X509Certificate;
15import java.util.ArrayList;
16import java.util.List;
17import java.util.Set;
18
19import eu.siacs.conversations.Config;
20import eu.siacs.conversations.crypto.axolotl.AxolotlService;
21import eu.siacs.conversations.entities.Account;
22import eu.siacs.conversations.entities.Conversation;
23import eu.siacs.conversations.entities.DownloadableFile;
24import eu.siacs.conversations.services.MessageArchiveService;
25import eu.siacs.conversations.services.XmppConnectionService;
26import eu.siacs.conversations.utils.Xmlns;
27import eu.siacs.conversations.xml.Element;
28import eu.siacs.conversations.xmpp.forms.Data;
29import eu.siacs.conversations.xmpp.jid.Jid;
30import eu.siacs.conversations.xmpp.pep.Avatar;
31import eu.siacs.conversations.xmpp.stanzas.IqPacket;
32
33public class IqGenerator extends AbstractGenerator {
34
35 public IqGenerator(final XmppConnectionService service) {
36 super(service);
37 }
38
39 public IqPacket discoResponse(final IqPacket request) {
40 final IqPacket packet = new IqPacket(IqPacket.TYPE.RESULT);
41 packet.setId(request.getId());
42 packet.setTo(request.getFrom());
43 final Element query = packet.addChild("query",
44 "http://jabber.org/protocol/disco#info");
45 query.setAttribute("node", request.query().getAttribute("node"));
46 final Element identity = query.addChild("identity");
47 identity.setAttribute("category", "client");
48 identity.setAttribute("type", IDENTITY_TYPE);
49 identity.setAttribute("name", getIdentityName());
50 for (final String feature : getFeatures()) {
51 query.addChild("feature").setAttribute("var", feature);
52 }
53 return packet;
54 }
55
56 public IqPacket versionResponse(final IqPacket request) {
57 final IqPacket packet = request.generateResponse(IqPacket.TYPE.RESULT);
58 Element query = packet.query("jabber:iq:version");
59 query.addChild("name").setContent(IDENTITY_NAME);
60 query.addChild("version").setContent(getIdentityVersion());
61 return packet;
62 }
63
64 protected IqPacket publish(final String node, final Element item) {
65 final IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
66 final Element pubsub = packet.addChild("pubsub",
67 "http://jabber.org/protocol/pubsub");
68 final Element publish = pubsub.addChild("publish");
69 publish.setAttribute("node", node);
70 publish.addChild(item);
71 return packet;
72 }
73
74 protected IqPacket retrieve(String node, Element item) {
75 final IqPacket packet = new IqPacket(IqPacket.TYPE.GET);
76 final Element pubsub = packet.addChild("pubsub",
77 "http://jabber.org/protocol/pubsub");
78 final Element items = pubsub.addChild("items");
79 items.setAttribute("node", node);
80 if (item != null) {
81 items.addChild(item);
82 }
83 return packet;
84 }
85
86 public IqPacket publishNick(String nick) {
87 final Element item = new Element("item");
88 item.addChild("nick","http://jabber.org/protocol/nick").setContent(nick);
89 return publish("http://jabber.org/protocol/nick", item);
90 }
91
92 public IqPacket publishAvatar(Avatar avatar) {
93 final Element item = new Element("item");
94 item.setAttribute("id", avatar.sha1sum);
95 final Element data = item.addChild("data", "urn:xmpp:avatar:data");
96 data.setContent(avatar.image);
97 return publish("urn:xmpp:avatar:data", item);
98 }
99
100 public IqPacket publishAvatarMetadata(final Avatar avatar) {
101 final Element item = new Element("item");
102 item.setAttribute("id", avatar.sha1sum);
103 final Element metadata = item
104 .addChild("metadata", "urn:xmpp:avatar:metadata");
105 final Element info = metadata.addChild("info");
106 info.setAttribute("bytes", avatar.size);
107 info.setAttribute("id", avatar.sha1sum);
108 info.setAttribute("height", avatar.height);
109 info.setAttribute("width", avatar.height);
110 info.setAttribute("type", avatar.type);
111 return publish("urn:xmpp:avatar:metadata", item);
112 }
113
114 public IqPacket retrievePepAvatar(final Avatar avatar) {
115 final Element item = new Element("item");
116 item.setAttribute("id", avatar.sha1sum);
117 final IqPacket packet = retrieve("urn:xmpp:avatar:data", item);
118 packet.setTo(avatar.owner);
119 return packet;
120 }
121
122 public IqPacket retrieveVcardAvatar(final Avatar avatar) {
123 final IqPacket packet = new IqPacket(IqPacket.TYPE.GET);
124 packet.setTo(avatar.owner);
125 packet.addChild("vCard", "vcard-temp");
126 return packet;
127 }
128
129 public IqPacket retrieveAvatarMetaData(final Jid to) {
130 final IqPacket packet = retrieve("urn:xmpp:avatar:metadata", null);
131 if (to != null) {
132 packet.setTo(to);
133 }
134 return packet;
135 }
136
137 public IqPacket retrieveDeviceIds(final Jid to) {
138 final IqPacket packet = retrieve(AxolotlService.PEP_DEVICE_LIST, null);
139 if(to != null) {
140 packet.setTo(to);
141 }
142 return packet;
143 }
144
145 public IqPacket retrieveBundlesForDevice(final Jid to, final int deviceid) {
146 final IqPacket packet = retrieve(AxolotlService.PEP_BUNDLES+":"+deviceid, null);
147 packet.setTo(to);
148 return packet;
149 }
150
151 public IqPacket retrieveVerificationForDevice(final Jid to, final int deviceid) {
152 final IqPacket packet = retrieve(AxolotlService.PEP_VERIFICATION+":"+deviceid, null);
153 packet.setTo(to);
154 return packet;
155 }
156
157 public IqPacket publishDeviceIds(final Set<Integer> ids) {
158 final Element item = new Element("item");
159 final Element list = item.addChild("list", AxolotlService.PEP_PREFIX);
160 for(Integer id:ids) {
161 final Element device = new Element("device");
162 device.setAttribute("id", id);
163 list.addChild(device);
164 }
165 return publish(AxolotlService.PEP_DEVICE_LIST, item);
166 }
167
168 public IqPacket publishBundles(final SignedPreKeyRecord signedPreKeyRecord, final IdentityKey identityKey,
169 final Set<PreKeyRecord> preKeyRecords, final int deviceId) {
170 final Element item = new Element("item");
171 final Element bundle = item.addChild("bundle", AxolotlService.PEP_PREFIX);
172 final Element signedPreKeyPublic = bundle.addChild("signedPreKeyPublic");
173 signedPreKeyPublic.setAttribute("signedPreKeyId", signedPreKeyRecord.getId());
174 ECPublicKey publicKey = signedPreKeyRecord.getKeyPair().getPublicKey();
175 signedPreKeyPublic.setContent(Base64.encodeToString(publicKey.serialize(),Base64.DEFAULT));
176 final Element signedPreKeySignature = bundle.addChild("signedPreKeySignature");
177 signedPreKeySignature.setContent(Base64.encodeToString(signedPreKeyRecord.getSignature(),Base64.DEFAULT));
178 final Element identityKeyElement = bundle.addChild("identityKey");
179 identityKeyElement.setContent(Base64.encodeToString(identityKey.serialize(), Base64.DEFAULT));
180
181 final Element prekeys = bundle.addChild("prekeys", AxolotlService.PEP_PREFIX);
182 for(PreKeyRecord preKeyRecord:preKeyRecords) {
183 final Element prekey = prekeys.addChild("preKeyPublic");
184 prekey.setAttribute("preKeyId", preKeyRecord.getId());
185 prekey.setContent(Base64.encodeToString(preKeyRecord.getKeyPair().getPublicKey().serialize(), Base64.DEFAULT));
186 }
187
188 return publish(AxolotlService.PEP_BUNDLES+":"+deviceId, item);
189 }
190
191 public IqPacket publishVerification(byte[] signature, X509Certificate[] certificates, final int deviceId) {
192 final Element item = new Element("item");
193 final Element verification = item.addChild("verification", AxolotlService.PEP_PREFIX);
194 final Element chain = verification.addChild("chain");
195 for(int i = 0; i < certificates.length; ++i) {
196 try {
197 Element certificate = chain.addChild("certificate");
198 certificate.setContent(Base64.encodeToString(certificates[i].getEncoded(), Base64.DEFAULT));
199 certificate.setAttribute("index",i);
200 } catch (CertificateEncodingException e) {
201 Log.d(Config.LOGTAG, "could not encode certificate");
202 }
203 }
204 verification.addChild("signature").setContent(Base64.encodeToString(signature, Base64.DEFAULT));
205 return publish(AxolotlService.PEP_VERIFICATION+":"+deviceId, item);
206 }
207
208 public IqPacket queryMessageArchiveManagement(final MessageArchiveService.Query mam) {
209 final IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
210 final Element query = packet.query("urn:xmpp:mam:0");
211 query.setAttribute("queryid", mam.getQueryId());
212 final Data data = new Data();
213 data.setFormType("urn:xmpp:mam:0");
214 if (mam.muc()) {
215 packet.setTo(mam.getWith());
216 } else if (mam.getWith()!=null) {
217 data.put("with", mam.getWith().toString());
218 }
219 data.put("start", getTimestamp(mam.getStart()));
220 data.put("end", getTimestamp(mam.getEnd()));
221 data.submit();
222 query.addChild(data);
223 if (mam.getPagingOrder() == MessageArchiveService.PagingOrder.REVERSE) {
224 query.addChild("set", "http://jabber.org/protocol/rsm").addChild("before").setContent(mam.getReference());
225 } else if (mam.getReference() != null) {
226 query.addChild("set", "http://jabber.org/protocol/rsm").addChild("after").setContent(mam.getReference());
227 }
228 return packet;
229 }
230 public IqPacket generateGetBlockList() {
231 final IqPacket iq = new IqPacket(IqPacket.TYPE.GET);
232 iq.addChild("blocklist", Xmlns.BLOCKING);
233
234 return iq;
235 }
236
237 public IqPacket generateSetBlockRequest(final Jid jid) {
238 final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
239 final Element block = iq.addChild("block", Xmlns.BLOCKING);
240 block.addChild("item").setAttribute("jid", jid.toBareJid().toString());
241 return iq;
242 }
243
244 public IqPacket generateSetUnblockRequest(final Jid jid) {
245 final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
246 final Element block = iq.addChild("unblock", Xmlns.BLOCKING);
247 block.addChild("item").setAttribute("jid", jid.toBareJid().toString());
248 return iq;
249 }
250
251 public IqPacket generateSetPassword(final Account account, final String newPassword) {
252 final IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
253 packet.setTo(account.getServer());
254 final Element query = packet.addChild("query", Xmlns.REGISTER);
255 final Jid jid = account.getJid();
256 query.addChild("username").setContent(jid.getLocalpart());
257 query.addChild("password").setContent(newPassword);
258 return packet;
259 }
260
261 public IqPacket changeAffiliation(Conversation conference, Jid jid, String affiliation) {
262 List<Jid> jids = new ArrayList<>();
263 jids.add(jid);
264 return changeAffiliation(conference,jids,affiliation);
265 }
266
267 public IqPacket changeAffiliation(Conversation conference, List<Jid> jids, String affiliation) {
268 IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
269 packet.setTo(conference.getJid().toBareJid());
270 packet.setFrom(conference.getAccount().getJid());
271 Element query = packet.query("http://jabber.org/protocol/muc#admin");
272 for(Jid jid : jids) {
273 Element item = query.addChild("item");
274 item.setAttribute("jid", jid.toString());
275 item.setAttribute("affiliation", affiliation);
276 }
277 return packet;
278 }
279
280 public IqPacket changeRole(Conversation conference, String nick, String role) {
281 IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
282 packet.setTo(conference.getJid().toBareJid());
283 packet.setFrom(conference.getAccount().getJid());
284 Element item = packet.query("http://jabber.org/protocol/muc#admin").addChild("item");
285 item.setAttribute("nick", nick);
286 item.setAttribute("role", role);
287 return packet;
288 }
289
290 public IqPacket requestHttpUploadSlot(Jid host, DownloadableFile file, String mime) {
291 IqPacket packet = new IqPacket(IqPacket.TYPE.GET);
292 packet.setTo(host);
293 Element request = packet.addChild("request", Xmlns.HTTP_UPLOAD);
294 request.addChild("filename").setContent(file.getName());
295 request.addChild("size").setContent(String.valueOf(file.getExpectedSize()));
296 if (mime != null) {
297 request.addChild("content-type").setContent(mime);
298 }
299 return packet;
300 }
301
302 public IqPacket generateCreateAccountWithCaptcha(Account account, String id, Data data) {
303 final IqPacket register = new IqPacket(IqPacket.TYPE.SET);
304
305 register.setTo(account.getServer());
306 register.setId(id);
307 Element query = register.query("jabber:iq:register");
308 if (data != null) {
309 query.addChild(data);
310 }
311 return register;
312 }
313
314 public IqPacket pushTokenToAppServer(Jid appServer, String token, String deviceId) {
315 IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
316 packet.setTo(appServer);
317 Element command = packet.addChild("command", "http://jabber.org/protocol/commands");
318 command.setAttribute("node","register-push-gcm");
319 command.setAttribute("action","execute");
320 Data data = new Data();
321 data.put("token", token);
322 data.put("device-id", deviceId);
323 data.submit();
324 command.addChild(data);
325 return packet;
326 }
327
328 public IqPacket enablePush(Jid jid, String node, String secret) {
329 IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
330 Element enable = packet.addChild("enable","urn:xmpp:push:0");
331 enable.setAttribute("jid",jid.toString());
332 enable.setAttribute("node", node);
333 Data data = new Data();
334 data.setFormType("http://jabber.org/protocol/pubsub#publish-options");
335 data.put("secret",secret);
336 data.submit();
337 enable.addChild(data);
338 return packet;
339 }
340
341 public IqPacket queryAffiliation(Conversation conversation, String affiliation) {
342 IqPacket packet = new IqPacket(IqPacket.TYPE.GET);
343 packet.setTo(conversation.getJid().toBareJid());
344 packet.query("http://jabber.org/protocol/muc#admin").addChild("item").setAttribute("affiliation",affiliation);
345 return packet;
346 }
347
348 public static Bundle defaultRoomConfiguration() {
349 Bundle options = new Bundle();
350 options.putString("muc#roomconfig_persistentroom", "1");
351 options.putString("muc#roomconfig_membersonly", "1");
352 options.putString("muc#roomconfig_publicroom", "0");
353 options.putString("muc#roomconfig_whois", "anyone");
354 return options;
355 }
356}