diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/JingleFileTransferManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/JingleFileTransferManager.java index 4139174d3..d24fd1581 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/JingleFileTransferManager.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/JingleFileTransferManager.java @@ -16,36 +16,6 @@ */ package org.jivesoftware.smackx.jingle_filetransfer; -import org.jivesoftware.smack.Manager; -import org.jivesoftware.smack.SmackException; -import org.jivesoftware.smack.XMPPConnection; -import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smack.packet.IQ; -import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager; -import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamSession; -import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; -import org.jivesoftware.smackx.hash.HashManager; -import org.jivesoftware.smackx.hash.element.HashElement; -import org.jivesoftware.smackx.jingle.JingleHandler; -import org.jivesoftware.smackx.jingle.JingleManager; -import org.jivesoftware.smackx.jingle.JingleSessionHandler; -import org.jivesoftware.smackx.jingle.element.Jingle; -import org.jivesoftware.smackx.jingle.element.JingleAction; -import org.jivesoftware.smackx.jingle.element.JingleContent; -import org.jivesoftware.smackx.jingle.element.JingleContentDescriptionChildElement; -import org.jivesoftware.smackx.jingle.element.JingleContentTransport; -import org.jivesoftware.smackx.jingle.provider.JingleContentProviderManager; -import org.jivesoftware.smackx.jingle_filetransfer.callback.IncomingJingleFileTransferCallback; -import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferContentDescription; -import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferChildElement; -import org.jivesoftware.smackx.jingle_filetransfer.handler.FileOfferHandler; -import org.jivesoftware.smackx.jingle_filetransfer.handler.FileRequestHandler; -import org.jivesoftware.smackx.jingle_filetransfer.listener.IncomingJingleFileTransferListener; -import org.jivesoftware.smackx.jingle_filetransfer.provider.JingleFileTransferContentDescriptionProvider; -import org.jivesoftware.smackx.jingle_ibb.JingleInBandByteStreamManager; -import org.jivesoftware.smackx.jingle_ibb.element.JingleInBandByteStreamTransport; -import org.jxmpp.jid.FullJid; - import java.io.File; import java.io.IOException; import java.util.ArrayList; @@ -56,12 +26,42 @@ import java.util.WeakHashMap; import java.util.logging.Level; import java.util.logging.Logger; +import org.jivesoftware.smack.Manager; +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.packet.XMPPError; +import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager; +import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamSession; +import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; +import org.jivesoftware.smackx.hash.HashManager; +import org.jivesoftware.smackx.hash.element.HashElement; +import org.jivesoftware.smackx.jingle.JingleHandler; +import org.jivesoftware.smackx.jingle.JingleManager; +import org.jivesoftware.smackx.jingle.JingleSession; +import org.jivesoftware.smackx.jingle.JingleSessionHandler; +import org.jivesoftware.smackx.jingle.element.Jingle; +import org.jivesoftware.smackx.jingle.element.JingleAction; +import org.jivesoftware.smackx.jingle.element.JingleContent; +import org.jivesoftware.smackx.jingle.element.JingleContentDescriptionChildElement; +import org.jivesoftware.smackx.jingle.element.JingleError; +import org.jivesoftware.smackx.jingle.provider.JingleContentProviderManager; +import org.jivesoftware.smackx.jingle_filetransfer.callback.IncomingJingleFileTransferCallback; +import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferChildElement; +import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferContentDescription; +import org.jivesoftware.smackx.jingle_filetransfer.listener.IncomingJingleFileTransferListener; +import org.jivesoftware.smackx.jingle_filetransfer.provider.JingleFileTransferContentDescriptionProvider; +import org.jivesoftware.smackx.jingle_ibb.JingleInBandByteStreamManager; +import org.jivesoftware.smackx.jingle_ibb.element.JingleInBandByteStreamTransport; +import org.jxmpp.jid.FullJid; + /** * Manager for Jingle File Transfers. * * @author Paul Schaub */ -public final class JingleFileTransferManager extends Manager implements JingleHandler, FileOfferHandler, FileRequestHandler { +public final class JingleFileTransferManager extends Manager implements JingleHandler, JingleSessionHandler { private static final Logger LOGGER = Logger.getLogger(JingleFileTransferManager.class.getName()); @@ -69,7 +69,7 @@ public final class JingleFileTransferManager extends Manager implements JingleHa private static final WeakHashMap INSTANCES = new WeakHashMap<>(); private final HashSet incomingJingleFileTransferListeners = new HashSet<>(); - private final HashMap sessions = new HashMap<>(); + private final HashMap sessions = new HashMap<>(); /** * Private constructor. This registers a JingleContentDescriptionFileTransferProvider with the @@ -120,6 +120,86 @@ public final class JingleFileTransferManager extends Manager implements JingleHa return payloadBuilder; } + @Override + public IQ handleJingleRequest(Jingle jingle) { + JingleSession session = new JingleSession(jingle.getInitiator(), jingle.getResponder(), jingle.getSid()); + sessions.put(jingle.getSid(), session); + + for (IncomingJingleFileTransferListener l : incomingJingleFileTransferListeners) { + l.onIncomingJingleFileTransfer(jingle, new IncomingJingleFileTransferCallback() { + @Override + public void acceptFileTransfer(File target) { + + } + + @Override + public void cancelFileTransfer() { + + } + }); + } + + return IQ.createResultIQ(jingle); + } + + @Override + public IQ handleJingleSessionRequest(Jingle jingle, String sessionId) { + JingleSession session = sessions.get(sessionId); + + if (session == null) { + // Handle unknown session (XEP-0166 §10) + XMPPError.Builder errorBuilder = XMPPError.getBuilder(); + errorBuilder.setCondition(XMPPError.Condition.item_not_found) + .addExtension(JingleError.UNKNOWN_SESSION); + return IQ.createErrorResponse(jingle, errorBuilder); + } + + for (int i = 0; i < jingle.getContents().size() && i < 1; i++) { //TODO: Remove && i<1 later + JingleContent content = jingle.getContents().get(i); + switch (jingle.getAction()) { + case session_initiate: + throw new AssertionError("Session is already initiated."); + case session_accept: + // Remote accepts our session-initiate + session.setState(JingleSession.State.active); + // ACK + return IQ.createResultIQ(jingle); + case session_info: + // Remote sends session-info (eg. hash) + case session_terminate: + // Remote wants to terminate our current session + sessions.remove(sessionId); + return IQ.createResultIQ(jingle); + + case content_accept: + // Remote accepts our content-add request. + case content_add: + // Remote wants to add content to the session. + case content_modify: + // Remote wants to change the directionality of the session + case content_reject: + // Remote rejects our content-add request + case content_remove: + // Remote wants to remove a content from the session/abort a single transfer + case description_info: + // Additional parameters of exchanged media + case security_info: + // Remote wants to exchange security information + case transport_accept: + // Remote accepts our transport-replace + case transport_info: + // Remote exchanges transport methods + case transport_reject: + // Remote rejects our transport-replace + case transport_replace: + // Remote wants to replace the transport + default: + return IQ.createErrorResponse(jingle, XMPPError.Condition.feature_not_implemented); + } + } + return null; + } + /** * QnD method. * @param file @@ -156,7 +236,7 @@ public final class JingleFileTransferManager extends Manager implements JingleHa JingleManager.getInstanceFor(connection()).registerJingleSessionHandler(recipient, sid, new JingleSessionHandler() { @Override - public IQ handleRequest(Jingle jingle, String sessionId) { + public IQ handleJingleSessionRequest(Jingle jingle, String sessionId) { if (sessionId.equals(sid)) { if (jingle.getAction() == JingleAction.session_accept) { @@ -182,107 +262,4 @@ public final class JingleFileTransferManager extends Manager implements JingleHa connection().sendStanza(jingle); } - - @Override - public IQ handleRequest(Jingle jingle) { - - for (int i = 0; i < jingle.getContents().size() && i < 1; i++) { //TODO: Remove && i<1 later - JingleContent content = jingle.getContents().get(i); - switch (jingle.getAction()) { - case content_accept: - // Remote accepts our content-add request. - case content_add: - // Remote wants to add content to the session. - case content_modify: - // Remote wants to change the directionality of the session - case content_reject: - // Remote rejects our content-add request - case content_remove: - // Remote wants to remove a content from the session/abort a single transfer - case description_info: - // Additional parameters of exchanged media - case security_info: - // Remote wants to exchange security information - case session_accept: - // Remote accepts our session-initiate - case session_info: - // Remote sends session-info (eg. hash) - case session_initiate: - // Remote offers file - if (content.getSenders() == JingleContent.Senders.initiator) { - handleFileOffer(jingle); - } - // Remote requests file - else if (content.getSenders() == JingleContent.Senders.responder) { - return handleFileRequest(jingle); - } - //Both or none - illegal - else { - throw new AssertionError("Undefined (see XEP-0234 §4.1)"); - } - break; - case session_terminate: - // Remote wants to terminate our current session - case transport_accept: - // Remote accepts our transport-replace - case transport_info: - // Remote exchanges transport methods - case transport_reject: - // Remote rejects our transport-replace - case transport_replace: - // Remote wants to replace the transport - } - } - return null; - } - - @Override - public void handleFileOffer(final Jingle jingle) { - IncomingJingleFileTransferCallback callback = new IncomingJingleFileTransferCallback() { - @Override - public void acceptFileTransfer(File target) throws SmackException.NotConnectedException, InterruptedException { - JingleContent content = jingle.getContents().get(0); - - //TODO: Find more suitable way to select transport methods. - JingleContentTransport preferredTransport = null; - for (JingleContentTransport t : content.getJingleTransports()) { - if (t.getNamespace().equals(JingleInBandByteStreamManager.NAMESPACE_V1)) { - preferredTransport = t; - } - } - - if (preferredTransport != null) { - Jingle.Builder acceptBuilder = Jingle.getBuilder(); - acceptBuilder.setResponder(connection().getUser()); - acceptBuilder.setSessionId(jingle.getSid()); - acceptBuilder.setAction(JingleAction.session_accept); - - JingleContent.Builder contentBuilder = JingleContent.getBuilder(); - contentBuilder.setCreator(content.getCreator()); - contentBuilder.setName(content.getName()); - contentBuilder.setSenders(content.getSenders()); - contentBuilder.setDescription(content.getDescription()); - contentBuilder.addTransport(preferredTransport); - - acceptBuilder.addJingleContent(contentBuilder.build()); - - connection().sendStanza(acceptBuilder.build()); - } - } - - @Override - public void cancelFileTransfer() { - // Tear down the session. - } - }; - - for (IncomingJingleFileTransferListener l : incomingJingleFileTransferListeners) { - l.onIncomingJingleFileTransfer(jingle, callback); - } - } - - @Override - public IQ handleFileRequest(Jingle jingle) { - return null; - } } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/JingleFileTransferSession.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/JingleFileTransferSession.java deleted file mode 100644 index c33670d57..000000000 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/JingleFileTransferSession.java +++ /dev/null @@ -1,79 +0,0 @@ -/** - * - * 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_filetransfer; - -import org.jivesoftware.smack.packet.IQ; -import org.jivesoftware.smackx.jingle.JingleSession; -import org.jivesoftware.smackx.jingle.element.Jingle; -import org.jivesoftware.smackx.jingle.element.JingleAction; -import org.jivesoftware.smackx.jingle.element.JingleContent; -import org.jivesoftware.smackx.jingle.element.JingleReason; -import org.jxmpp.jid.Jid; - -/** - * Class that represents a jingle session in the context of Jingle File Transfer. - */ -public class JingleFileTransferSession extends JingleSession { - - public JingleFileTransferSession(Jid initiator, Jid responder, String sid) { - super(initiator, responder, sid); - } - - /** - * A user might choose to abort all active transfers. - * @return Jingle IQ that will abort all active transfers of this session. - */ - IQ abortAllActiveFileTransfers() { - Jingle.Builder builder = Jingle.getBuilder(); - builder.setResponder(getResponder().asFullJidOrThrow()); - builder.setInitiator(getInitiator().asFullJidOrThrow()); - builder.setAction(JingleAction.session_terminate); - builder.setSessionId(getSid()); - builder.setReason(JingleReason.Reason.cancel); - return builder.build(); - } - - /** - * A user might want to abort the transfer of a single file. - * @param content content which's transfer will be aborted. - * @return Jingle IQ that will abort the transfer of the given content. - */ - IQ abortSingleFileTransfer(JingleContent content) { - Jingle.Builder builder = Jingle.getBuilder(); - builder.setResponder(getResponder().asFullJidOrThrow()); - builder.setInitiator(getInitiator().asFullJidOrThrow()); - builder.setAction(JingleAction.content_remove); - builder.setSessionId(getSid()); - builder.addJingleContent(content); - builder.setReason(JingleReason.Reason.cancel); - return builder.build(); - } - - /** - * Successfully end session after all files have been transferred. - * @return Jingle IQ that will end the session. - */ - IQ endSession() { - Jingle.Builder builder = Jingle.getBuilder(); - builder.setResponder(getResponder().asFullJidOrThrow()); - builder.setInitiator(getInitiator().asFullJidOrThrow()); - builder.setAction(JingleAction.session_terminate); - builder.setSessionId(getSid()); - builder.setReason(JingleReason.Reason.success); - return builder.build(); - } -} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/callback/IncomingJingleFileTransferCallback.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/callback/IncomingJingleFileTransferCallback.java index d14ad31a2..7039ae3a0 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/callback/IncomingJingleFileTransferCallback.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/callback/IncomingJingleFileTransferCallback.java @@ -16,8 +16,6 @@ */ package org.jivesoftware.smackx.jingle_filetransfer.callback; -import org.jivesoftware.smack.SmackException; - import java.io.File; /** @@ -25,7 +23,7 @@ import java.io.File; */ public interface IncomingJingleFileTransferCallback { - void acceptFileTransfer(File target) throws SmackException.NotConnectedException, InterruptedException; + void acceptFileTransfer(File target); void cancelFileTransfer(); } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleManager.java index 28da68a5b..8b200dbe9 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleManager.java @@ -16,6 +16,11 @@ */ package org.jivesoftware.smackx.jingle; +import java.util.Map; +import java.util.WeakHashMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Logger; + import org.jivesoftware.smack.Manager; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.iqrequest.AbstractIqRequestHandler; @@ -23,16 +28,12 @@ import org.jivesoftware.smack.iqrequest.IQRequestHandler.Mode; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.IQ.Type; import org.jivesoftware.smackx.jingle.element.Jingle; +import org.jivesoftware.smackx.jingle.element.JingleAction; import org.jivesoftware.smackx.jingle.element.JingleContent; import org.jivesoftware.smackx.jingle.element.JingleContentDescription; import org.jxmpp.jid.FullJid; import org.jxmpp.jid.Jid; -import java.util.Map; -import java.util.WeakHashMap; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Logger; - public final class JingleManager extends Manager { private static final Logger LOGGER = Logger.getLogger(JingleManager.class.getName()); @@ -61,7 +62,7 @@ public final class JingleManager extends Manager { public IQ handleIQRequest(IQ iqRequest) { final Jingle jingle = (Jingle) iqRequest; - if (jingle.getContents().isEmpty()) { + if (jingle.getAction() != JingleAction.session_initiate) { Jid from = jingle.getFrom(); assert (from != null); FullJid fullFrom = from.asFullJidOrThrow(); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleSession.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleSession.java index e9cbc30b7..19b8641d3 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleSession.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleSession.java @@ -16,17 +16,29 @@ */ package org.jivesoftware.smackx.jingle; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smackx.jingle.element.Jingle; +import org.jivesoftware.smackx.jingle.element.JingleAction; +import org.jivesoftware.smackx.jingle.element.JingleReason; import org.jxmpp.jid.Jid; // TODO: Is this class still required? If not, then remove it. public class JingleSession { + public enum State { + pending, + active, + ; + } + private final Jid initiator; private final Jid responder; private final String sid; + private State state = State.pending; + public JingleSession(Jid initiator, Jid responder, String sid) { this.initiator = initiator; this.responder = responder; @@ -53,6 +65,14 @@ public class JingleSession { return responder; } + public void setState(State state) { + this.state = state; + } + + public State getState() { + return state; + } + @Override public boolean equals(Object other) { if (!(other instanceof JingleSession)) { @@ -63,4 +83,13 @@ public class JingleSession { return initiator.equals(otherJingleSession.initiator) && responder.equals(otherJingleSession.responder) && sid.equals(otherJingleSession.sid); } + + IQ terminateSuccessfully() { + Jingle.Builder builder = Jingle.getBuilder(); + builder.setAction(JingleAction.session_terminate); + builder.setSessionId(getSid()); + builder.setReason(JingleReason.Reason.success); + + return builder.build(); + } }