001/**
002 *
003 * Copyright 2017 Paul Schaub
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.jivesoftware.smackx.jingle;
018
019import org.jivesoftware.smack.SmackException;
020import org.jivesoftware.smack.XMPPConnection;
021import org.jivesoftware.smack.XMPPException;
022import org.jivesoftware.smack.packet.IQ;
023import org.jivesoftware.smack.packet.StanzaError;
024
025import org.jivesoftware.smackx.jingle.element.Jingle;
026import org.jivesoftware.smackx.jingle.element.JingleAction;
027import org.jivesoftware.smackx.jingle.element.JingleContent;
028import org.jivesoftware.smackx.jingle.element.JingleContentDescription;
029import org.jivesoftware.smackx.jingle.element.JingleContentTransport;
030import org.jivesoftware.smackx.jingle.element.JingleError;
031import org.jivesoftware.smackx.jingle.element.JingleReason;
032
033import org.jxmpp.jid.FullJid;
034
035/**
036 * Util to quickly create and send jingle stanzas.
037 */
038public class JingleUtil {
039
040    private final XMPPConnection connection;
041
042    public JingleUtil(XMPPConnection connection) {
043        this.connection = connection;
044    }
045
046    public Jingle createSessionInitiate(FullJid recipient,
047                                        String sessionId,
048                                        JingleContent.Creator contentCreator,
049                                        String contentName,
050                                        JingleContent.Senders contentSenders,
051                                        JingleContentDescription description,
052                                        JingleContentTransport transport) {
053
054        Jingle.Builder jb = Jingle.builder(connection);
055        jb.setAction(JingleAction.session_initiate)
056                .setSessionId(sessionId)
057                .setInitiator(connection.getUser());
058
059        JingleContent.Builder cb = JingleContent.getBuilder();
060        cb.setCreator(contentCreator)
061                .setName(contentName)
062                .setSenders(contentSenders)
063                .setDescription(description)
064                .setTransport(transport);
065
066        Jingle jingle = jb.addJingleContent(cb.build()).build();
067        jingle.setFrom(connection.getUser());
068        jingle.setTo(recipient);
069
070        return jingle;
071    }
072
073    public Jingle createSessionInitiateFileOffer(FullJid recipient,
074                                                 String sessionId,
075                                                 JingleContent.Creator contentCreator,
076                                                 String contentName,
077                                                 JingleContentDescription description,
078                                                 JingleContentTransport transport) {
079        return createSessionInitiate(recipient, sessionId, contentCreator, contentName,
080                JingleContent.Senders.initiator, description, transport);
081    }
082
083    public IQ sendSessionInitiateFileOffer(FullJid recipient,
084                                           String sessionId,
085                                           JingleContent.Creator contentCreator,
086                                           String contentName,
087                                           JingleContentDescription description,
088                                           JingleContentTransport transport)
089            throws SmackException.NotConnectedException, InterruptedException,
090            XMPPException.XMPPErrorException, SmackException.NoResponseException {
091
092        Jingle jingle = createSessionInitiateFileOffer(recipient, sessionId, contentCreator, contentName, description, transport);
093        return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow();
094    }
095
096    public IQ sendSessionInitiate(FullJid recipient,
097                                  String sessionId,
098                                  JingleContent.Creator contentCreator,
099                                  String contentName,
100                                  JingleContent.Senders contentSenders,
101                                  JingleContentDescription description,
102                                  JingleContentTransport transport)
103            throws SmackException.NotConnectedException, InterruptedException {
104
105        Jingle jingle = createSessionInitiate(recipient, sessionId, contentCreator, contentName, contentSenders,
106                description, transport);
107
108        return connection.createStanzaCollectorAndSend(jingle).nextResult();
109    }
110
111    public Jingle createSessionAccept(FullJid recipient,
112                                      String sessionId,
113                                      JingleContent.Creator contentCreator,
114                                      String contentName,
115                                      JingleContent.Senders contentSenders,
116                                      JingleContentDescription description,
117                                      JingleContentTransport transport) {
118
119        Jingle.Builder jb = Jingle.builder(connection);
120        jb.setResponder(connection.getUser())
121                .setAction(JingleAction.session_accept)
122                .setSessionId(sessionId);
123
124        JingleContent.Builder cb = JingleContent.getBuilder();
125        cb.setCreator(contentCreator)
126                .setName(contentName)
127                .setSenders(contentSenders)
128                .setDescription(description)
129                .setTransport(transport);
130
131        Jingle jingle = jb.addJingleContent(cb.build()).build();
132        jingle.setTo(recipient);
133        jingle.setFrom(connection.getUser());
134
135        return jingle;
136    }
137
138    public IQ sendSessionAccept(FullJid recipient,
139                                String sessionId,
140                                JingleContent.Creator contentCreator,
141                                String contentName,
142                                JingleContent.Senders contentSenders,
143                                JingleContentDescription description,
144                                JingleContentTransport transport)
145            throws SmackException.NotConnectedException, InterruptedException {
146
147        Jingle jingle = createSessionAccept(recipient, sessionId, contentCreator, contentName, contentSenders,
148                description, transport);
149
150        return connection.createStanzaCollectorAndSend(jingle).nextResult();
151    }
152
153    public Jingle createSessionTerminate(FullJid recipient, String sessionId, JingleReason reason) {
154        Jingle.Builder jb = Jingle.builder(connection);
155        jb.setAction(JingleAction.session_terminate)
156                .setSessionId(sessionId)
157                .setReason(reason);
158
159        Jingle jingle = jb.build();
160        jingle.setFrom(connection.getUser());
161        jingle.setTo(recipient);
162
163        return jingle;
164    }
165
166    public Jingle createSessionTerminate(FullJid recipient, String sessionId, JingleReason.Reason reason) {
167        return createSessionTerminate(recipient, sessionId, new JingleReason(reason));
168    }
169
170    public Jingle createSessionTerminateDecline(FullJid recipient, String sessionId) {
171        return createSessionTerminate(recipient, sessionId, JingleReason.Reason.decline);
172    }
173
174    public IQ sendSessionTerminateDecline(FullJid recipient, String sessionId)
175            throws SmackException.NotConnectedException, InterruptedException,
176            XMPPException.XMPPErrorException, SmackException.NoResponseException {
177
178        Jingle jingle = createSessionTerminateDecline(recipient, sessionId);
179        return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow();
180    }
181
182    public Jingle createSessionTerminateSuccess(FullJid recipient, String sessionId) {
183        return createSessionTerminate(recipient, sessionId, JingleReason.Reason.success);
184    }
185
186    public IQ sendSessionTerminateSuccess(FullJid recipient, String sessionId)
187            throws InterruptedException, XMPPException.XMPPErrorException,
188            SmackException.NotConnectedException, SmackException.NoResponseException {
189
190        Jingle jingle = createSessionTerminateSuccess(recipient, sessionId);
191        return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow();
192    }
193
194    public Jingle createSessionTerminateBusy(FullJid recipient, String sessionId) {
195        return createSessionTerminate(recipient, sessionId, JingleReason.Reason.busy);
196    }
197
198    public IQ sendSessionTerminateBusy(FullJid recipient, String sessionId)
199            throws InterruptedException, XMPPException.XMPPErrorException,
200            SmackException.NotConnectedException, SmackException.NoResponseException {
201
202        Jingle jingle = createSessionTerminateBusy(recipient, sessionId);
203        return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow();
204    }
205
206    public Jingle createSessionTerminateAlternativeSession(FullJid recipient, String sessionId, String altSessionId) {
207        return createSessionTerminate(recipient, sessionId, JingleReason.AlternativeSession(altSessionId));
208    }
209
210    public IQ sendSessionTerminateAlternativeSession(FullJid recipient, String sessionId, String altSessionId)
211            throws InterruptedException, XMPPException.XMPPErrorException,
212            SmackException.NotConnectedException, SmackException.NoResponseException {
213
214        Jingle jingle = createSessionTerminateAlternativeSession(recipient, sessionId, altSessionId);
215        return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow();
216    }
217
218    public Jingle createSessionTerminateCancel(FullJid recipient, String sessionId) {
219        return createSessionTerminate(recipient, sessionId, JingleReason.Reason.cancel);
220    }
221
222    public IQ sendSessionTerminateCancel(FullJid recipient,
223                                  String sessionId)
224            throws InterruptedException, XMPPException.XMPPErrorException,
225            SmackException.NotConnectedException, SmackException.NoResponseException {
226
227        Jingle jingle = createSessionTerminateCancel(recipient, sessionId);
228        return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow();
229    }
230
231    public Jingle createSessionTerminateContentCancel(FullJid recipient, String sessionId,
232                                                      JingleContent.Creator contentCreator, String contentName) {
233        Jingle.Builder jb = Jingle.builder(connection);
234        jb.setAction(JingleAction.session_terminate)
235                .setSessionId(sessionId);
236
237        JingleContent.Builder cb = JingleContent.getBuilder();
238        cb.setCreator(contentCreator).setName(contentName);
239
240        Jingle jingle = jb.addJingleContent(cb.build()).build();
241        jingle.setFrom(connection.getUser());
242        jingle.setTo(recipient);
243
244        return jingle;
245    }
246
247    public IQ sendSessionTerminateContentCancel(FullJid recipient, String sessionId,
248                                  JingleContent.Creator contentCreator, String contentName)
249            throws SmackException.NotConnectedException, InterruptedException,
250            XMPPException.XMPPErrorException, SmackException.NoResponseException {
251        Jingle jingle = createSessionTerminateContentCancel(recipient, sessionId, contentCreator, contentName);
252        return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow();
253    }
254
255    public Jingle createSessionTerminateUnsupportedTransports(FullJid recipient, String sessionId) {
256        return createSessionTerminate(recipient, sessionId, JingleReason.Reason.unsupported_transports);
257    }
258
259    public IQ sendSessionTerminateUnsupportedTransports(FullJid recipient, String sessionId)
260            throws InterruptedException, XMPPException.XMPPErrorException,
261            SmackException.NotConnectedException, SmackException.NoResponseException {
262        Jingle jingle = createSessionTerminateUnsupportedTransports(recipient, sessionId);
263        return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow();
264    }
265
266    public Jingle createSessionTerminateFailedTransport(FullJid recipient, String sessionId) {
267        return createSessionTerminate(recipient, sessionId, JingleReason.Reason.failed_transport);
268    }
269
270    public IQ sendSessionTerminateFailedTransport(FullJid recipient, String sessionId)
271            throws InterruptedException, XMPPException.XMPPErrorException,
272            SmackException.NotConnectedException, SmackException.NoResponseException {
273        Jingle jingle = createSessionTerminateFailedTransport(recipient, sessionId);
274        return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow();
275    }
276
277    public Jingle createSessionTerminateUnsupportedApplications(FullJid recipient, String sessionId) {
278        return createSessionTerminate(recipient, sessionId, JingleReason.Reason.unsupported_applications);
279    }
280
281    public IQ sendSessionTerminateUnsupportedApplications(FullJid recipient, String sessionId)
282            throws InterruptedException, XMPPException.XMPPErrorException,
283            SmackException.NotConnectedException, SmackException.NoResponseException {
284        Jingle jingle = createSessionTerminateUnsupportedApplications(recipient, sessionId);
285        return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow();
286    }
287
288    public Jingle createSessionTerminateFailedApplication(FullJid recipient, String sessionId) {
289        return createSessionTerminate(recipient, sessionId, JingleReason.Reason.failed_application);
290    }
291
292    public IQ sendSessionTerminateFailedApplication(FullJid recipient, String sessionId)
293            throws InterruptedException, XMPPException.XMPPErrorException,
294            SmackException.NotConnectedException, SmackException.NoResponseException {
295        Jingle jingle = createSessionTerminateFailedApplication(recipient, sessionId);
296        return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow();
297    }
298
299    public Jingle createSessionTerminateIncompatibleParameters(FullJid recipient, String sessionId) {
300        return createSessionTerminate(recipient, sessionId, JingleReason.Reason.incompatible_parameters);
301    }
302
303    public IQ sendSessionTerminateIncompatibleParameters(FullJid recipient, String sessionId)
304            throws InterruptedException, XMPPException.XMPPErrorException,
305            SmackException.NotConnectedException, SmackException.NoResponseException {
306        Jingle jingle = createSessionTerminateIncompatibleParameters(recipient, sessionId);
307        return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow();
308    }
309
310    public IQ sendContentRejectFileNotAvailable(FullJid recipient, String sessionId, JingleContentDescription description) {
311        return null; //TODO Later
312    }
313
314    public Jingle createSessionPing(FullJid recipient, String sessionId) {
315        Jingle.Builder jb = Jingle.builder(connection);
316        jb.setSessionId(sessionId)
317                .setAction(JingleAction.session_info);
318
319        Jingle jingle = jb.build();
320        jingle.setFrom(connection.getUser());
321        jingle.setTo(recipient);
322
323        return jingle;
324    }
325
326    public IQ sendSessionPing(FullJid recipient, String sessionId)
327            throws SmackException.NotConnectedException, InterruptedException,
328            XMPPException.XMPPErrorException, SmackException.NoResponseException {
329        Jingle jingle = createSessionPing(recipient, sessionId);
330        return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow();
331    }
332
333    public IQ createAck(Jingle jingle) {
334        return IQ.createResultIQ(jingle);
335    }
336
337    public void sendAck(Jingle jingle) throws SmackException.NotConnectedException, InterruptedException {
338        connection.sendStanza(createAck(jingle));
339    }
340
341    public Jingle createTransportReplace(FullJid recipient, FullJid initiator, String sessionId,
342                                         JingleContent.Creator contentCreator, String contentName,
343                                         JingleContentTransport transport) {
344        Jingle.Builder jb = Jingle.builder(connection);
345        jb.setInitiator(initiator)
346                .setSessionId(sessionId)
347                .setAction(JingleAction.transport_replace);
348
349        JingleContent.Builder cb = JingleContent.getBuilder();
350        cb.setName(contentName).setCreator(contentCreator).setTransport(transport);
351        Jingle jingle = jb.addJingleContent(cb.build()).build();
352
353        jingle.setTo(recipient);
354        jingle.setFrom(connection.getUser());
355
356        return jingle;
357    }
358
359    public IQ sendTransportReplace(FullJid recipient, FullJid initiator, String sessionId,
360                                   JingleContent.Creator contentCreator, String contentName,
361                                   JingleContentTransport transport)
362            throws SmackException.NotConnectedException, InterruptedException,
363            XMPPException.XMPPErrorException, SmackException.NoResponseException {
364        Jingle jingle = createTransportReplace(recipient, initiator, sessionId, contentCreator, contentName, transport);
365        return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow();
366    }
367
368    public Jingle createTransportAccept(FullJid recipient, FullJid initiator, String sessionId,
369                                        JingleContent.Creator contentCreator, String contentName,
370                                        JingleContentTransport transport) {
371        Jingle.Builder jb = Jingle.builder(connection);
372        jb.setAction(JingleAction.transport_accept)
373                .setInitiator(initiator)
374                .setSessionId(sessionId);
375
376        JingleContent.Builder cb = JingleContent.getBuilder();
377        cb.setCreator(contentCreator).setName(contentName).setTransport(transport);
378
379        Jingle jingle = jb.addJingleContent(cb.build()).build();
380        jingle.setTo(recipient);
381        jingle.setFrom(connection.getUser());
382
383        return jingle;
384    }
385
386    public IQ sendTransportAccept(FullJid recipient, FullJid initiator, String sessionId,
387                                  JingleContent.Creator contentCreator, String contentName,
388                                  JingleContentTransport transport)
389            throws SmackException.NotConnectedException, InterruptedException,
390            XMPPException.XMPPErrorException, SmackException.NoResponseException {
391        Jingle jingle = createTransportAccept(recipient, initiator, sessionId, contentCreator, contentName, transport);
392        return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow();
393    }
394
395    public Jingle createTransportReject(FullJid recipient, FullJid initiator, String sessionId,
396                                        JingleContent.Creator contentCreator, String contentName,
397                                        JingleContentTransport transport) {
398        Jingle.Builder jb = Jingle.builder(connection);
399        jb.setAction(JingleAction.transport_reject)
400                .setInitiator(initiator)
401                .setSessionId(sessionId);
402
403        JingleContent.Builder cb = JingleContent.getBuilder();
404        cb.setCreator(contentCreator).setName(contentName).setTransport(transport);
405
406        Jingle jingle = jb.addJingleContent(cb.build()).build();
407        jingle.setTo(recipient);
408        jingle.setFrom(connection.getUser());
409
410        return jingle;
411    }
412
413    public IQ sendTransportReject(FullJid recipient, FullJid initiator, String sessionId,
414                                  JingleContent.Creator contentCreator, String contentName,
415                                  JingleContentTransport transport)
416            throws SmackException.NotConnectedException, InterruptedException,
417            XMPPException.XMPPErrorException, SmackException.NoResponseException {
418        Jingle jingle = createTransportReject(recipient, initiator, sessionId, contentCreator, contentName, transport);
419        return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow();
420    }
421
422    /*
423     * ####################################################################################################
424     */
425
426    public IQ createErrorUnknownSession(Jingle request) {
427        StanzaError error = StanzaError.getBuilder()
428                        .setCondition(StanzaError.Condition.item_not_found)
429                        .addExtension(JingleError.UNKNOWN_SESSION)
430                        .build();
431        return IQ.createErrorResponse(request, error);
432    }
433
434    public void sendErrorUnknownSession(Jingle request)
435            throws SmackException.NotConnectedException, InterruptedException {
436        connection.sendStanza(createErrorUnknownSession(request));
437    }
438
439    public IQ createErrorUnknownInitiator(Jingle request) {
440        return IQ.createErrorResponse(request, StanzaError.Condition.service_unavailable);
441    }
442
443    public void sendErrorUnknownInitiator(Jingle request)
444            throws SmackException.NotConnectedException, InterruptedException {
445        connection.sendStanza(createErrorUnknownInitiator(request));
446    }
447
448    public IQ createErrorUnsupportedInfo(Jingle request) {
449        StanzaError error = StanzaError.getBuilder()
450                        .setCondition(StanzaError.Condition.feature_not_implemented)
451                        .addExtension(JingleError.UNSUPPORTED_INFO)
452                        .build();
453        return IQ.createErrorResponse(request, error);
454    }
455
456    public void sendErrorUnsupportedInfo(Jingle request)
457            throws SmackException.NotConnectedException, InterruptedException {
458        connection.sendStanza(createErrorUnsupportedInfo(request));
459    }
460
461    public IQ createErrorTieBreak(Jingle request) {
462        StanzaError error = StanzaError.getBuilder()
463                        .setCondition(StanzaError.Condition.conflict)
464                        .addExtension(JingleError.TIE_BREAK)
465                        .build();
466        return IQ.createErrorResponse(request, error);
467    }
468
469    public void sendErrorTieBreak(Jingle request)
470            throws SmackException.NotConnectedException, InterruptedException {
471        connection.sendStanza(createErrorTieBreak(request));
472    }
473
474    public IQ createErrorOutOfOrder(Jingle request) {
475        StanzaError error = StanzaError.getBuilder()
476                        .setCondition(StanzaError.Condition.unexpected_request)
477                        .addExtension(JingleError.OUT_OF_ORDER)
478                        .build();
479        return IQ.createErrorResponse(request, error);
480    }
481
482    public void sendErrorOutOfOrder(Jingle request)
483            throws SmackException.NotConnectedException, InterruptedException {
484        connection.sendStanza(createErrorOutOfOrder(request));
485    }
486
487    public IQ createErrorMalformedRequest(Jingle request) {
488        return IQ.createErrorResponse(request, StanzaError.Condition.bad_request);
489    }
490
491    public void sendErrorMalformedRequest(Jingle request)
492            throws SmackException.NotConnectedException, InterruptedException {
493        connection.sendStanza(createErrorMalformedRequest(request));
494    }
495}