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