From af069ffc49cfa94f8d61089a11e5f0954a057228 Mon Sep 17 00:00:00 2001 From: vanitasvitae Date: Sat, 29 Jul 2017 19:31:16 +0200 Subject: [PATCH] Pass events down to contents --- .../smackx/jft/JingleFileTransferManager.java | 2 +- .../smackx/jft/adapter/package-info.java | 22 + .../IncomingFileOfferController.java | 6 +- .../jft/internal/JingleIncomingFileOffer.java | 9 +- .../jingle/JingleDescriptionManager.java | 2 +- .../jingle/components/JingleContent.java | 91 +++- .../jingle/components/JingleSession.java | 515 ++++++++++-------- .../JingleContentDescriptionInfoElement.java | 16 + .../smackx/jft/JingleFileTransferTest.java | 11 +- 9 files changed, 423 insertions(+), 251 deletions(-) create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/jft/adapter/package-info.java diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jft/JingleFileTransferManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jft/JingleFileTransferManager.java index 08b31bd63..339ab9e18 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/jft/JingleFileTransferManager.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jft/JingleFileTransferManager.java @@ -166,7 +166,7 @@ public final class JingleFileTransferManager extends Manager implements JingleDe } @Override - public void notifyContentAdd(JingleContent content) { + public void notifyContentAdd(JingleSession session, JingleContent content) { notifyTransfer((JingleFileTransfer) content.getDescription()); } } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jft/adapter/package-info.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jft/adapter/package-info.java new file mode 100644 index 000000000..bb55e4942 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jft/adapter/package-info.java @@ -0,0 +1,22 @@ +/** + * + * Copyright 2017 Paul Schaub + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Smack's API for XEP-0234: Jingle File Transfer. + * Adapters. + */ +package org.jivesoftware.smackx.jft.adapter; diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jft/controller/IncomingFileOfferController.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jft/controller/IncomingFileOfferController.java index 50f27cb33..56c665519 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/jft/controller/IncomingFileOfferController.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jft/controller/IncomingFileOfferController.java @@ -19,9 +19,13 @@ package org.jivesoftware.smackx.jft.controller; import java.io.File; import java.util.concurrent.Future; +import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.XMPPException; + /** * Created by vanitas on 27.07.17. */ public interface IncomingFileOfferController extends JingleFileTransferController { - Future accept(File target); + Future accept(XMPPConnection connection, File target) throws InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException; } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jft/internal/JingleIncomingFileOffer.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jft/internal/JingleIncomingFileOffer.java index 2d593149d..5621be947 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/jft/internal/JingleIncomingFileOffer.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jft/internal/JingleIncomingFileOffer.java @@ -23,6 +23,9 @@ import java.util.concurrent.Future; import java.util.logging.Level; import java.util.logging.Logger; +import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smackx.bytestreams.BytestreamSession; import org.jivesoftware.smackx.jft.controller.IncomingFileOfferController; import org.jivesoftware.smackx.jft.element.JingleFileTransferChildElement; @@ -69,10 +72,12 @@ public class JingleIncomingFileOffer extends AbstractJingleFileOffer } @Override - public Future accept(File target) { + public Future accept(XMPPConnection connection, File target) + throws InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, + SmackException.NoResponseException { JingleSession session = getParent().getParent(); if (session.getSessionState() == JingleSession.SessionState.pending) { - //session.accept(); + session.accept(connection); } return null; diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleDescriptionManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleDescriptionManager.java index db908830f..6acc585d8 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleDescriptionManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleDescriptionManager.java @@ -28,5 +28,5 @@ public interface JingleDescriptionManager { void notifySessionInitiate(JingleSession session); - void notifyContentAdd(JingleContent content); + void notifyContentAdd(JingleSession session, JingleContent content); } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/components/JingleContent.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/components/JingleContent.java index 0c4990c1e..39a4386ee 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/components/JingleContent.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/components/JingleContent.java @@ -27,6 +27,7 @@ import java.util.logging.Logger; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smackx.bytestreams.BytestreamSession; import org.jivesoftware.smackx.jingle.Callback; @@ -62,6 +63,69 @@ public class JingleContent implements JingleTransportCallback { private final List callbacks = Collections.synchronizedList(new ArrayList()); private final Set transportBlacklist = Collections.synchronizedSet(new HashSet()); + public IQ handleJingleRequest(JingleElement request, XMPPConnection connection) { + switch (request.getAction()) { + case content_modify: + return handleContentModify(request, connection); + case description_info: + return handleDescriptionInfo(request, connection); + case security_info: + return handleSecurityInfo(request, connection); + case session_info: + return handleSessionInfo(request, connection); + case transport_accept: + return handleTransportAccept(request, connection); + case transport_info: + return handleTransportInfo(request, connection); + case transport_reject: + return handleTransportReject(request, connection); + case transport_replace: + return handleTransportReplace(request, connection); + default: + throw new AssertionError("Illegal jingle action: " + request.getAction() + " is not allowed here."); + } + } + + public IQ handleSessionAccept(JingleElement request, XMPPConnection connection) { + return IQ.createResultIQ(request); + } + + public IQ handleContentModify(JingleElement request, XMPPConnection connection) { + return IQ.createResultIQ(request); + } + + public IQ handleDescriptionInfo(JingleElement request, XMPPConnection connection) { + return IQ.createResultIQ(request); + } + + public void handleContentRemove(JingleSession session, XMPPConnection connection) { + + } + + public IQ handleSecurityInfo(JingleElement request, XMPPConnection connection) { + return IQ.createResultIQ(request); + } + + public IQ handleSessionInfo(JingleElement request, XMPPConnection connection) { + return IQ.createResultIQ(request); + } + + public IQ handleTransportAccept(JingleElement request, XMPPConnection connection) { + return IQ.createResultIQ(request); + } + + public IQ handleTransportInfo(JingleElement request, XMPPConnection connection) { + return IQ.createResultIQ(request); + } + + public IQ handleTransportReject(JingleElement request, XMPPConnection connection) { + return IQ.createResultIQ(request); + } + + public IQ handleTransportReplace(JingleElement request, XMPPConnection connection) { + return IQ.createResultIQ(request); + } + public enum STATE { pending_accept, pending_transmission_start, @@ -77,9 +141,9 @@ public class JingleContent implements JingleTransportCallback { } public JingleContent(JingleDescription description, JingleTransport transport, JingleSecurity security, String name, String disposition, JingleContentElement.Creator creator, JingleContentElement.Senders senders) { - this.description = description; - this.transport = transport; - this.security = security; + setDescription(description); + setTransport(transport); + setSecurity(security); this.name = name; this.disposition = disposition; this.creator = creator; @@ -178,7 +242,7 @@ public class JingleContent implements JingleTransportCallback { } public void setDescription(JingleDescription description) { - if (this.description != description) { + if (description != null && this.description != description) { this.description = description; description.setParent(this); } @@ -189,7 +253,7 @@ public class JingleContent implements JingleTransportCallback { } public void setTransport(JingleTransport transport) { - if (this.transport != transport) { + if (transport != null && this.transport != transport) { this.transport = transport; transport.setParent(this); } @@ -200,7 +264,7 @@ public class JingleContent implements JingleTransportCallback { } public void setSecurity(JingleSecurity security) { - if (this.security != security) { + if (security != null && this.security != security) { this.security = security; security.setParent(this); } @@ -266,13 +330,16 @@ public class JingleContent implements JingleTransportCallback { connection.createStanzaCollectorAndSend(transportReplace).nextResultOrThrow(); } - public void onContentAccept(XMPPConnection connection) - throws SmackException.NotConnectedException, InterruptedException { + public void handleContentAccept(JingleElement request, XMPPConnection connection) { //Establish transport - if (isReceiving()) { - getTransport().establishIncomingBytestreamSession(connection, this, getParent()); - } else if (isSending()) { - getTransport().establishOutgoingBytestreamSession(connection, this, getParent()); + try { + if (isReceiving()) { + getTransport().establishIncomingBytestreamSession(connection, this, getParent()); + } else if (isSending()) { + getTransport().establishOutgoingBytestreamSession(connection, this, getParent()); + } + } catch (SmackException.NotConnectedException | InterruptedException e) { + LOGGER.log(Level.SEVERE, "Error establishing connection: " + e, e); } } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/components/JingleSession.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/components/JingleSession.java index 6f0cc0d71..fbfa7ba5a 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/components/JingleSession.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/components/JingleSession.java @@ -21,7 +21,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.Logger; @@ -33,12 +32,8 @@ import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.util.Async; import org.jivesoftware.smackx.jingle.JingleDescriptionManager; import org.jivesoftware.smackx.jingle.JingleManager; -import org.jivesoftware.smackx.jingle.adapter.JingleTransportAdapter; import org.jivesoftware.smackx.jingle.element.JingleAction; -import org.jivesoftware.smackx.jingle.element.JingleContentDescriptionElement; import org.jivesoftware.smackx.jingle.element.JingleContentElement; -import org.jivesoftware.smackx.jingle.element.JingleContentTransportElement; -import org.jivesoftware.smackx.jingle.element.JingleContentTransportInfoElement; import org.jivesoftware.smackx.jingle.element.JingleElement; import org.jivesoftware.smackx.jingle.element.JingleReasonElement; import org.jivesoftware.smackx.jingle.exception.UnsupportedDescriptionException; @@ -55,6 +50,7 @@ public class JingleSession { private static final Logger LOGGER = Logger.getLogger(JingleSession.class.getName()); private final ConcurrentHashMap contents = new ConcurrentHashMap<>(); + private final ConcurrentHashMap proposedContents = new ConcurrentHashMap<>(); private final JingleManager jingleManager; private final FullJid initiator, responder; @@ -82,36 +78,6 @@ public class JingleSession { this.sessionState = SessionState.fresh; } - public void addContent(JingleContent content) { - contents.put(content.getName(), content); - content.setParent(this); - } - - public void addContent(JingleContentElement content) - throws UnsupportedSecurityException, UnsupportedTransportException, UnsupportedDescriptionException { - addContent(JingleContent.fromElement(content)); - } - - public ConcurrentHashMap getContents() { - return contents; - } - - public JingleContent getContent(String name) { - return contents.get(name); - } - - public JingleContent getSoleContentOrThrow() { - if (contents.isEmpty()) { - return null; - } - - if (contents.size() > 1) { - throw new IllegalStateException(); - } - - return contents.values().iterator().next(); - } - public static JingleSession fromSessionInitiate(JingleManager manager, JingleElement initiate) throws UnsupportedSecurityException, UnsupportedDescriptionException, UnsupportedTransportException { if (initiate.getAction() != JingleAction.session_initiate) { @@ -131,10 +97,23 @@ public class JingleSession { } public void initiate(XMPPConnection connection) throws SmackException.NotConnectedException, InterruptedException, XMPPException.XMPPErrorException, SmackException.NoResponseException { + if (this.sessionState != SessionState.fresh) { + throw new IllegalStateException("Session is not in fresh state."); + } + connection.createStanzaCollectorAndSend(createSessionInitiate()).nextResultOrThrow(); this.sessionState = SessionState.pending; } + public void accept(XMPPConnection connection) throws SmackException.NotConnectedException, InterruptedException, XMPPException.XMPPErrorException, SmackException.NoResponseException { + if (this.sessionState != SessionState.pending) { + throw new IllegalStateException("Session is not in pending state."); + } + + connection.createStanzaCollectorAndSend(createSessionAccept()).nextResultOrThrow(); + this.sessionState = SessionState.active; + } + public JingleElement createSessionInitiate() { if (role != Role.initiator) { throw new IllegalStateException("Sessions role is not initiator."); @@ -148,87 +127,81 @@ public class JingleSession { return JingleElement.createSessionInitiate(getInitiator(), getResponder(), getSessionId(), contentElements); } + public JingleElement createSessionAccept() { + if (role != Role.responder) { + throw new IllegalStateException("Sessions role is not responder."); + } + + List contentElements = new ArrayList<>(); + for (JingleContent c : contents.values()) { + contentElements.add(c.getElement()); + } + + return JingleElement.createSessionAccept(getInitiator(), getResponder(), getSessionId(), contentElements); + } + public IQ handleJingleRequest(JingleElement request) { switch (request.getAction()) { + case content_modify: + case description_info: + case security_info: + case session_info: + case transport_accept: + case transport_info: + case transport_reject: + case transport_replace: + return getSoleAffectedContentOrThrow(request).handleJingleRequest(request, jingleManager.getConnection()); case content_accept: return handleContentAccept(request); case content_add: return handleContentAdd(request); - case content_modify: - return handleContentModify(request); case content_reject: return handleContentReject(request); case content_remove: return handleContentRemove(request); - case description_info: - return handleDescriptionInfo(request); - case session_info: - return handleSessionInfo(request); - case security_info: - return handleSecurityInfo(request); case session_accept: return handleSessionAccept(request); - case transport_accept: - return handleTransportAccept(request); - case transport_info: - return handleTransportInfo(request); case session_initiate: return handleSessionInitiate(request); - case transport_reject: - return handleTransportReject(request); case session_terminate: return handleSessionTerminate(request); - case transport_replace: - return handleTransportReplace(request); default: - throw new AssertionError("Unknown Jingle Action enum! " + request.getAction()); + throw new AssertionError("Illegal jingle action: " + request.getAction()); } } - private IQ handleTransportReplace(final JingleElement request) { - Async.go(new Runnable() { - @Override - public void run() { - List affectedContents = request.getContents(); - List responses = new ArrayList<>(); + /* ############## Processed in this class ############## */ - for (JingleContentElement affected : affectedContents) { - JingleContent content = contents.get(affected.getName()); - JingleContentTransportElement newTransport = affected.getTransport(); - Set blacklist = content.getTransportBlacklist(); + /** + * Handle incoming session-accept stanza. + * @param request session-accept stanza. + * @return result. + */ + private IQ handleSessionAccept(final JingleElement request) { + this.sessionState = SessionState.active; - // Proposed transport method might already be on the blacklist (eg. because of previous failures) - if (blacklist.contains(newTransport.getNamespace())) { - responses.add(JingleElement.createTransportReject(getInitiator(), getPeer(), getSessionId(), - content.getCreator(), content.getName(), newTransport)); - continue; - } - - JingleTransportAdapter transportAdapter = JingleManager.getJingleTransportAdapter( - newTransport.getNamespace()); - // This might be an unknown transport. - if (transportAdapter == null) { - responses.add(JingleElement.createTransportReject(getInitiator(), getPeer(), getSessionId(), - content.getCreator(), content.getName(), newTransport)); - continue; - } - - //Otherwise, when all went well so far, accept the transport-replace - content.setTransport(JingleManager.getJingleTransportAdapter(newTransport.getNamespace()) - .transportFromElement(newTransport)); - responses.add(JingleElement.createTransportAccept(getInitiator(), getPeer(), getSessionId(), - content.getCreator(), content.getName(), newTransport)); + for (final JingleContent content : contents.values()) { + Async.go(new Runnable() { + @Override + public void run() { + content.handleSessionAccept(request, jingleManager.getConnection()); } + }); + } - for (JingleElement response : responses) { - try { - jingleManager.getConnection().createStanzaCollectorAndSend(response).nextResultOrThrow(); - } catch (SmackException.NoResponseException | XMPPException.XMPPErrorException | InterruptedException | SmackException.NotConnectedException e) { - LOGGER.log(Level.SEVERE, "Could not send response to transport-replace: " + e, e); - } - } - } - }); + return IQ.createResultIQ(request); + } + + private IQ handleSessionInitiate(JingleElement request) { + JingleDescription description = getSoleContentOrThrow().getDescription(); + JingleDescriptionManager descriptionManager = jingleManager.getDescriptionManager(description.getNamespace()); + + if (descriptionManager == null) { + LOGGER.log(Level.WARNING, "Unsupported description type: " + description.getNamespace()); + return JingleElement.createSessionTerminate(getPeer(), getSessionId(), JingleReasonElement.Reason.unsupported_applications); + } + + descriptionManager.notifySessionInitiate(this); return IQ.createResultIQ(request); } @@ -247,168 +220,193 @@ public class JingleSession { return IQ.createResultIQ(request); } - private IQ handleTransportReject(JingleElement request) { - HashMap affectedContents = getAffectedContents(request); - for (JingleContent c : affectedContents.values()) { + private IQ handleContentAccept(final JingleElement request) { + for (JingleContentElement a : request.getContents()) { + final JingleContent accepted = proposedContents.get(a.getName()); - } - return null; - } - - private IQ handleSessionInitiate(JingleElement request) { - JingleDescription description = getSoleContentOrThrow().getDescription(); - JingleDescriptionManager descriptionManager = jingleManager.getDescriptionManager(description.getNamespace()); - - if (descriptionManager == null) { - LOGGER.log(Level.WARNING, "Unsupported description type: " + description.getNamespace()); - return JingleElement.createSessionTerminate(getPeer(), getSessionId(), JingleReasonElement.Reason.unsupported_applications); - } - - descriptionManager.notifySessionInitiate(this); - - return IQ.createResultIQ(request); - } - - private IQ handleTransportInfo(JingleElement request) { - HashMap affectedContents = getAffectedContents(request); - - for (Map.Entry entry : affectedContents.entrySet()) { - JingleTransport transport = entry.getValue().getTransport(); - JingleContentTransportInfoElement info = entry.getKey().getTransport().getInfo(); - transport.handleTransportInfo(info, request); - } - - return IQ.createResultIQ(request); - } - - private IQ handleTransportAccept(JingleElement request) { - HashMap affectedContents = getAffectedContents(request); - for (Map.Entry entry : affectedContents.entrySet()) { - - PendingJingleAction pending = pendingJingleActions.get(entry.getValue()); - if (pending == null) { - continue; + if (accepted == null) { + throw new AssertionError("Illegal content name!"); } - if (pending.getAction() != JingleAction.transport_replace) { - //TODO: Are multiple contents even possible here? - //TODO: How to react to partially illegal requests? - return JingleElement.createJingleErrorOutOfOrder(request); - } + proposedContents.remove(accepted.getName()); + contents.put(accepted.getName(), accepted); - entry.getValue().setTransport(((PendingJingleAction.TransportReplace) pending).getNewTransport()); + Async.go(new Runnable() { + @Override + public void run() { + accepted.handleContentAccept(request, jingleManager.getConnection()); + } + }); } return IQ.createResultIQ(request); } - private IQ handleSessionAccept(JingleElement request) { - this.sessionState = SessionState.active; - - return null; - } - - private IQ handleSecurityInfo(JingleElement request) { - HashMap affectedContents = getAffectedContents(request); - List responses = new ArrayList<>(); - - for (Map.Entry entry : affectedContents.entrySet()) { - responses.add(entry.getValue().getSecurity().handleSecurityInfo(entry.getKey().getSecurity().getSecurityInfo(), request)); - } - - for (JingleElement response : responses) { - try { - getJingleManager().getConnection().createStanzaCollectorAndSend(response).nextResultOrThrow(); - } catch (SmackException.NoResponseException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | InterruptedException e) { - LOGGER.log(Level.SEVERE, "Could not send response to security-info: " + e, e); - } - } - - return IQ.createResultIQ(request); - } - - private IQ handleSessionInfo(JingleElement request) { - return null; - } - - private IQ handleDescriptionInfo(JingleElement request) { - HashMap affectedContents = getAffectedContents(request); - List responses = new ArrayList<>(); - - for (Map.Entry entry : affectedContents.entrySet()) { - responses.add(entry.getValue().getDescription().handleDescriptionInfo(entry.getKey().getDescription().getDescriptionInfo())); - } - - for (JingleElement response : responses) { - try { - getJingleManager().getConnection().createStanzaCollectorAndSend(response).nextResultOrThrow(); - } catch (SmackException.NoResponseException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | InterruptedException e) { - LOGGER.log(Level.SEVERE, "Could not send response to description-info: " + e, e); - } - } - - return IQ.createResultIQ(request); - } - - private IQ handleContentRemove(JingleElement request) { - return null; - } - - private IQ handleContentReject(JingleElement request) { - return null; - } - - private IQ handleContentModify(JingleElement request) { - return null; - } - private IQ handleContentAdd(JingleElement request) { - final List proposedContents = request.getContents(); - final List acceptedContents = new ArrayList<>(); + final JingleContent proposed = getSoleProposedContentOrThrow(request); - final HashMap> contentsByDescription = new HashMap<>(); + final JingleDescriptionManager descriptionManager = jingleManager.getDescriptionManager(proposed.getDescription().getNamespace()); - for (JingleContentElement p : proposedContents) { - JingleContentDescriptionElement description = p.getDescription(); - List list = contentsByDescription.get(description.getNamespace()); - if (list == null) { - list = new ArrayList<>(); - contentsByDescription.put(description.getNamespace(), list); - } - list.add(JingleContent.fromElement(p)); + if (descriptionManager == null) { + throw new AssertionError("DescriptionManager is null: " + proposed.getDescription().getNamespace()); } - for (Map.Entry> descriptionCategory : contentsByDescription.entrySet()) { - JingleDescriptionManager descriptionManager = JingleManager.getInstanceFor(getJingleManager().getConnection()).getDescriptionManager(descriptionCategory.getKey()); - - if (descriptionManager == null) { - //blabla - continue; + Async.go(new Runnable() { + @Override + public void run() { + descriptionManager.notifyContentAdd(JingleSession.this, proposed); } - - for (final JingleContent content : descriptionCategory.getValue()) { - descriptionManager.notifyContentAdd(content); - } - } - - if (acceptedContents.size() > 0) { - JingleElement accept = JingleElement.createContentAccept(getPeer(), getSessionId(), acceptedContents); - try { - getJingleManager().getConnection().createStanzaCollectorAndSend(accept).nextResultOrThrow(); - } catch (SmackException.NoResponseException | XMPPException.XMPPErrorException | InterruptedException | SmackException.NotConnectedException e) { - LOGGER.log(Level.SEVERE, "Could not send response to content-add: " + e, e); - } - } - - //TODO: send content-reject for rejected contents! + }); return IQ.createResultIQ(request); } - private IQ handleContentAccept(JingleElement request) { - return null; + private IQ handleContentReject(JingleElement request) { + for (JingleContentElement r : request.getContents()) { + final JingleContent rejected = proposedContents.get(r.getName()); + + if (rejected == null) { + throw new AssertionError("Illegal content name!"); + } + + proposedContents.remove(rejected.getName()); + + /* + Async.go(new Runnable() { + @Override + public void run() { + rejected.handleContentReject(request, jingleManager.getConnection()); + } + }); + */ + } + + return IQ.createResultIQ(request); } + private IQ handleContentRemove(final JingleElement request) { + for (JingleContentElement r : request.getContents()) { + final JingleContent removed = contents.get(r.getName()); + + if (removed == null) { + throw new AssertionError("Illegal content name!"); + } + + contents.remove(removed.getName()); + + Async.go(new Runnable() { + @Override + public void run() { + removed.handleContentRemove(JingleSession.this, jingleManager.getConnection()); + } + }); + } + + return IQ.createResultIQ(request); + } + + /* ############## Processed further down ############## */ + + private IQ handleContentModify(final JingleElement request) { + final JingleContent content = getSoleAffectedContentOrThrow(request); + + Async.go(new Runnable() { + @Override + public void run() { + content.handleContentModify(request, jingleManager.getConnection()); + } + }); + + return IQ.createResultIQ(request); + } + + private IQ handleDescriptionInfo(final JingleElement request) { + final JingleContent content = getSoleAffectedContentOrThrow(request); + + Async.go(new Runnable() { + @Override + public void run() { + content.handleDescriptionInfo(request, jingleManager.getConnection()); + } + }); + + return IQ.createResultIQ(request); + } + + private IQ handleSecurityInfo(final JingleElement request) { + final JingleContent content = getSoleAffectedContentOrThrow(request); + Async.go(new Runnable() { + @Override + public void run() { + content.handleSecurityInfo(request, jingleManager.getConnection()); + } + }); + + return IQ.createResultIQ(request); + } + + private IQ handleSessionInfo(final JingleElement request) { + final JingleContent content = getSoleAffectedContentOrThrow(request); + Async.go(new Runnable() { + @Override + public void run() { + content.handleSessionInfo(request, jingleManager.getConnection()); + } + }); + + return IQ.createResultIQ(request); + } + + private IQ handleTransportAccept(final JingleElement request) { + final JingleContent content = getSoleAffectedContentOrThrow(request); + Async.go(new Runnable() { + @Override + public void run() { + content.handleTransportAccept(request, jingleManager.getConnection()); + } + }); + + return IQ.createResultIQ(request); + } + + private IQ handleTransportInfo(final JingleElement request) { + final JingleContent content = getSoleAffectedContentOrThrow(request); + Async.go(new Runnable() { + @Override + public void run() { + content.handleTransportInfo(request, jingleManager.getConnection()); + } + }); + + return IQ.createResultIQ(request); + } + + private IQ handleTransportReject(final JingleElement request) { + final JingleContent content = getSoleAffectedContentOrThrow(request); + Async.go(new Runnable() { + @Override + public void run() { + content.handleTransportReject(request, jingleManager.getConnection()); + } + }); + + return IQ.createResultIQ(request); + } + + private IQ handleTransportReplace(final JingleElement request) { + final JingleContent content = getSoleAffectedContentOrThrow(request); + Async.go(new Runnable() { + @Override + public void run() { + content.handleTransportReplace(request, jingleManager.getConnection()); + } + }); + + return IQ.createResultIQ(request); + } + + /* ################ Other getters and setters ############### */ + public FullJid getInitiator() { return initiator; } @@ -453,6 +451,57 @@ public class JingleSession { return map; } + private JingleContent getSoleAffectedContentOrThrow(JingleElement request) { + if (request.getContents().size() != 1) { + throw new AssertionError("More/less than 1 content in request!"); + } + + JingleContent content = contents.get(request.getContents().get(0).getName()); + if (content == null) { + throw new AssertionError("Illegal content name!"); + } + + return content; + } + + private JingleContent getSoleProposedContentOrThrow(JingleElement request) { + if (request.getContents().size() != 1) { + throw new AssertionError("More/less than 1 content in request!"); + } + + return JingleContent.fromElement(request.getContents().get(0)); + } + + public void addContent(JingleContent content) { + contents.put(content.getName(), content); + content.setParent(this); + } + + public void addContent(JingleContentElement content) + throws UnsupportedSecurityException, UnsupportedTransportException, UnsupportedDescriptionException { + addContent(JingleContent.fromElement(content)); + } + + public ConcurrentHashMap getContents() { + return contents; + } + + public JingleContent getContent(String name) { + return contents.get(name); + } + + public JingleContent getSoleContentOrThrow() { + if (contents.isEmpty()) { + return null; + } + + if (contents.size() > 1) { + throw new IllegalStateException(); + } + + return contents.values().iterator().next(); + } + public SessionState getSessionState() { return sessionState; } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleContentDescriptionInfoElement.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleContentDescriptionInfoElement.java index af1785dbe..1287970fe 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleContentDescriptionInfoElement.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleContentDescriptionInfoElement.java @@ -1,3 +1,19 @@ +/** + * + * Copyright 2017 Paul Schaub + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.jivesoftware.smackx.jingle.element; import org.jivesoftware.smack.packet.NamedElement; diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/jft/JingleFileTransferTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/jft/JingleFileTransferTest.java index 95b68c28f..a89be4bb8 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/jft/JingleFileTransferTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/jft/JingleFileTransferTest.java @@ -27,6 +27,8 @@ import java.util.ArrayList; import java.util.concurrent.Future; import java.util.logging.Level; +import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smackx.bytestreams.socks5.Socks5Proxy; import org.jivesoftware.smackx.jft.controller.IncomingFileOfferController; import org.jivesoftware.smackx.jft.controller.OutgoingFileOfferController; @@ -86,6 +88,7 @@ public class JingleFileTransferTest extends AbstractSmackIntegrationTest { @Override public void onIncomingFileOffer(IncomingFileOfferController offer) { LOGGER.log(Level.INFO, "INCOMING FILE TRANSFER!"); + offer.addProgressListener(new ProgressListener() { @Override public void started() { @@ -102,11 +105,17 @@ public class JingleFileTransferTest extends AbstractSmackIntegrationTest { resultSyncPoint2.signal(); } }); - receiveFuture.add(offer.accept(target)); + + try { + receiveFuture.add(offer.accept(conTwo, target)); + } catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | SmackException.NoResponseException e) { + fail(e.toString()); + } } }); OutgoingFileOfferController sending = aftm.sendFile(source, bob); + sending.addProgressListener(new ProgressListener() { @Override public void started() {