mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-11-23 20:42:06 +01:00
Proper Statemachine
This commit is contained in:
parent
4d11422335
commit
7a9b819b18
18 changed files with 816 additions and 584 deletions
|
@ -0,0 +1,138 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.XMPPConnection;
|
||||||
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
|
import org.jivesoftware.smackx.jingle.JingleSessionHandler;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.Jingle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AbstractJingleSession that implements basic handler methods that basically do nothing.
|
||||||
|
*/
|
||||||
|
public abstract class AbstractJingleSession implements JingleSessionHandler {
|
||||||
|
|
||||||
|
protected final XMPPConnection connection;
|
||||||
|
protected AbstractJingleSession state;
|
||||||
|
|
||||||
|
public AbstractJingleSession(XMPPConnection connection) {
|
||||||
|
this.connection = connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IQ handleJingleSessionRequest(Jingle jingle) {
|
||||||
|
switch (jingle.getAction()) {
|
||||||
|
case content_accept:
|
||||||
|
return handleContentAccept(jingle);
|
||||||
|
case content_add:
|
||||||
|
return handleContentAdd(jingle);
|
||||||
|
case content_modify:
|
||||||
|
return handleContentModify(jingle);
|
||||||
|
case content_reject:
|
||||||
|
return handleContentReject(jingle);
|
||||||
|
case content_remove:
|
||||||
|
return handleContentRemove(jingle);
|
||||||
|
case description_info:
|
||||||
|
return handleDescriptionInfo(jingle);
|
||||||
|
case session_info:
|
||||||
|
return handleSessionInfo(jingle);
|
||||||
|
case security_info:
|
||||||
|
return handleSecurityInfo(jingle);
|
||||||
|
case session_accept:
|
||||||
|
return handleSessionAccept(jingle);
|
||||||
|
case transport_accept:
|
||||||
|
return handleTransportAccept(jingle);
|
||||||
|
case transport_info:
|
||||||
|
return handleTransportInfo(jingle);
|
||||||
|
case session_initiate:
|
||||||
|
return handleSessionInitiate(jingle);
|
||||||
|
case transport_reject:
|
||||||
|
return handleTransportReject(jingle);
|
||||||
|
case session_terminate:
|
||||||
|
return handleSessionTerminate(jingle);
|
||||||
|
case transport_replace:
|
||||||
|
return handleTransportReplace(jingle);
|
||||||
|
default:
|
||||||
|
return IQ.createResultIQ(jingle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IQ handleSessionInitiate(Jingle sessionInitiate) {
|
||||||
|
return IQ.createResultIQ(sessionInitiate);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IQ handleSessionTerminate(Jingle sessionTerminate) {
|
||||||
|
return IQ.createResultIQ(sessionTerminate);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IQ handleSessionInfo(Jingle sessionInfo) {
|
||||||
|
return IQ.createResultIQ(sessionInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IQ handleSessionAccept(Jingle sessionAccept) {
|
||||||
|
return IQ.createResultIQ(sessionAccept);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IQ handleContentAdd(Jingle contentAdd) {
|
||||||
|
return IQ.createResultIQ(contentAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IQ handleContentAccept(Jingle contentAccept) {
|
||||||
|
return IQ.createResultIQ(contentAccept);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IQ handleContentModify(Jingle contentModify) {
|
||||||
|
return IQ.createResultIQ(contentModify);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IQ handleContentReject(Jingle contentReject) {
|
||||||
|
return IQ.createResultIQ(contentReject);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IQ handleContentRemove(Jingle contentRemove) {
|
||||||
|
return IQ.createResultIQ(contentRemove);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IQ handleDescriptionInfo(Jingle descriptionInfo) {
|
||||||
|
return IQ.createResultIQ(descriptionInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IQ handleSecurityInfo(Jingle securityInfo) {
|
||||||
|
return IQ.createResultIQ(securityInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IQ handleTransportAccept(Jingle transportAccept) {
|
||||||
|
return IQ.createResultIQ(transportAccept);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IQ handleTransportInfo(Jingle transportInfo) {
|
||||||
|
return IQ.createResultIQ(transportInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IQ handleTransportReplace(Jingle transportReplace) {
|
||||||
|
return IQ.createResultIQ(transportReplace);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IQ handleTransportReject(Jingle transportReject) {
|
||||||
|
return IQ.createResultIQ(transportReject);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public XMPPConnection getConnection() {
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2017 Paul Schaub
|
* Copyright © 2017 Paul Schaub
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -17,87 +17,41 @@
|
||||||
package org.jivesoftware.smackx.jingle_filetransfer;
|
package org.jivesoftware.smackx.jingle_filetransfer;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.util.ArrayList;
|
||||||
import java.io.IOException;
|
import java.util.HashMap;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.Manager;
|
import org.jivesoftware.smack.Manager;
|
||||||
import org.jivesoftware.smack.SmackException;
|
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
import org.jivesoftware.smack.packet.IQ;
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
import org.jivesoftware.smack.util.StringUtils;
|
|
||||||
import org.jivesoftware.smackx.bytestreams.BytestreamSession;
|
|
||||||
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
|
||||||
import org.jivesoftware.smackx.hashes.HashManager;
|
|
||||||
import org.jivesoftware.smackx.hashes.element.HashElement;
|
|
||||||
import org.jivesoftware.smackx.jingle.AbstractJingleTransportManager;
|
|
||||||
import org.jivesoftware.smackx.jingle.JingleContentProviderManager;
|
|
||||||
import org.jivesoftware.smackx.jingle.JingleHandler;
|
import org.jivesoftware.smackx.jingle.JingleHandler;
|
||||||
import org.jivesoftware.smackx.jingle.JingleManager;
|
import org.jivesoftware.smackx.jingle.JingleManager;
|
||||||
import org.jivesoftware.smackx.jingle.JingleTransportEstablishedCallback;
|
|
||||||
import org.jivesoftware.smackx.jingle.JingleTransportHandler;
|
|
||||||
import org.jivesoftware.smackx.jingle.JingleTransportManager;
|
|
||||||
import org.jivesoftware.smackx.jingle.element.Jingle;
|
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.jivesoftware.smackx.jingle.element.JingleContentDescriptionChildElement;
|
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleContentTransport;
|
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleReason;
|
|
||||||
import org.jivesoftware.smackx.jingle.exception.JingleTransportFailureException;
|
|
||||||
import org.jivesoftware.smackx.jingle_filetransfer.callback.JingleFileTransferCallback;
|
import org.jivesoftware.smackx.jingle_filetransfer.callback.JingleFileTransferCallback;
|
||||||
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferChild;
|
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferChild;
|
||||||
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferContentDescription;
|
|
||||||
import org.jivesoftware.smackx.jingle_filetransfer.handler.InitiatorOutgoingFileTransferInitiated;
|
|
||||||
import org.jivesoftware.smackx.jingle_filetransfer.handler.ResponderIncomingFileTransferAccepted;
|
|
||||||
import org.jivesoftware.smackx.jingle_filetransfer.listener.IncomingJingleFileTransferListener;
|
import org.jivesoftware.smackx.jingle_filetransfer.listener.IncomingJingleFileTransferListener;
|
||||||
import org.jivesoftware.smackx.jingle_filetransfer.provider.JingleFileTransferContentDescriptionProvider;
|
import org.jivesoftware.smackx.jingle_ibb.JingleIBBTransportManager;
|
||||||
import org.jivesoftware.smackx.jingle_s5b.JingleS5BTransportManager;
|
|
||||||
import org.jxmpp.jid.FullJid;
|
import org.jxmpp.jid.FullJid;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manager for Jingle File Transfers.
|
* Manager for JingleFileTransfer.
|
||||||
*
|
|
||||||
* @author Paul Schaub
|
|
||||||
*/
|
*/
|
||||||
public final class JingleFileTransferManager extends Manager implements JingleHandler {
|
public final class JingleFileTransferManager extends Manager implements JingleHandler {
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(JingleFileTransferManager.class.getName());
|
|
||||||
|
|
||||||
public static final String NAMESPACE_V5 = "urn:xmpp:jingle:apps:file-transfer:5";
|
public static final String NAMESPACE_V5 = "urn:xmpp:jingle:apps:file-transfer:5";
|
||||||
|
|
||||||
private final JingleManager jingleManager;
|
|
||||||
private static final WeakHashMap<XMPPConnection, JingleFileTransferManager> INSTANCES = new WeakHashMap<>();
|
private static final WeakHashMap<XMPPConnection, JingleFileTransferManager> INSTANCES = new WeakHashMap<>();
|
||||||
private final HashSet<IncomingJingleFileTransferListener> incomingJingleFileTransferListeners = new HashSet<>();
|
|
||||||
|
|
||||||
/**
|
private final ArrayList<IncomingJingleFileTransferListener> incomingJFTListeners = new ArrayList<>();
|
||||||
* Private constructor. This registers a JingleContentDescriptionFileTransferProvider with the
|
|
||||||
* JingleContentProviderManager.
|
private HashMap<JingleManager.FullJidAndSessionId, JingleFileTransferSession> jingleSessions = new HashMap<>();
|
||||||
* @param connection connection
|
|
||||||
*/
|
|
||||||
private JingleFileTransferManager(XMPPConnection connection) {
|
private JingleFileTransferManager(XMPPConnection connection) {
|
||||||
super(connection);
|
super(connection);
|
||||||
ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection);
|
JingleManager.getInstanceFor(connection).registerDescriptionHandler(NAMESPACE_V5, this);
|
||||||
sdm.addFeature(NAMESPACE_V5);
|
JingleIBBTransportManager.getInstanceFor(connection);
|
||||||
jingleManager = JingleManager.getInstanceFor(connection);
|
//JingleS5BTransportManager.getInstanceFor(connection);
|
||||||
jingleManager.registerDescriptionHandler(
|
|
||||||
NAMESPACE_V5, this);
|
|
||||||
JingleContentProviderManager.addJingleContentDescriptionProvider(
|
|
||||||
NAMESPACE_V5, new JingleFileTransferContentDescriptionProvider());
|
|
||||||
//JingleIBBTransportManager.getInstanceFor(connection);
|
|
||||||
JingleS5BTransportManager.getInstanceFor(connection);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a new instance of the FileTransferManager for the given connection.
|
|
||||||
*
|
|
||||||
* @param connection XMPPConnection we wish to get a FileTransferManager for.
|
|
||||||
* @return manager instance.
|
|
||||||
*/
|
|
||||||
public static JingleFileTransferManager getInstanceFor(XMPPConnection connection) {
|
public static JingleFileTransferManager getInstanceFor(XMPPConnection connection) {
|
||||||
JingleFileTransferManager manager = INSTANCES.get(connection);
|
JingleFileTransferManager manager = INSTANCES.get(connection);
|
||||||
if (manager == null) {
|
if (manager == null) {
|
||||||
|
@ -107,211 +61,37 @@ public final class JingleFileTransferManager extends Manager implements JingleHa
|
||||||
return manager;
|
return manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addIncomingFileTransferListener(IncomingJingleFileTransferListener listener) {
|
public void addIncomingJingleFileTransferListener(IncomingJingleFileTransferListener listener) {
|
||||||
incomingJingleFileTransferListeners.add(listener);
|
incomingJFTListeners.add(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeIncomingFileTransferListener(IncomingJingleFileTransferListener listener) {
|
void notifyIncomingJingleFileTransferListeners(JingleFileTransferChild file, JingleFileTransferCallback callback) {
|
||||||
incomingJingleFileTransferListeners.remove(listener);
|
for (IncomingJingleFileTransferListener l : incomingJFTListeners) {
|
||||||
}
|
l.onIncomingJingleFileTransfer(file, callback);
|
||||||
|
|
||||||
void notifyIncomingFileTransferListeners(Jingle jingle, JingleFileTransferCallback callback) {
|
|
||||||
for (IncomingJingleFileTransferListener l : incomingJingleFileTransferListeners) {
|
|
||||||
l.onIncomingJingleFileTransfer(jingle, callback);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void addJingleSession(JingleFileTransferSession session) {
|
||||||
* QnD method.
|
jingleSessions.put(session.getFullJidAndSessionId(), session);
|
||||||
* @param file
|
|
||||||
*/
|
|
||||||
public void sendFile(final File file, final FullJid recipient) throws Exception {
|
|
||||||
AbstractJingleTransportManager<?> tm = JingleTransportManager.getInstanceFor(connection())
|
|
||||||
.getAvailableJingleBytestreamManagers().iterator().next();
|
|
||||||
|
|
||||||
JingleFileTransferChild.Builder b = JingleFileTransferChild.getBuilder();
|
|
||||||
b.setFile(file);
|
|
||||||
b.setDescription("File");
|
|
||||||
b.setMediaType("application/octet-stream");
|
|
||||||
|
|
||||||
JingleFileTransferContentDescription description = new JingleFileTransferContentDescription(
|
|
||||||
Collections.singletonList((JingleContentDescriptionChildElement) b.build()));
|
|
||||||
|
|
||||||
JingleContentTransport transport = tm.createJingleContentTransport(recipient);
|
|
||||||
Jingle initiate = sessionInitiate(recipient, description, transport);
|
|
||||||
|
|
||||||
JingleManager.FullJidAndSessionId fullJidAndSessionId =
|
|
||||||
new JingleManager.FullJidAndSessionId(recipient, initiate.getSessionId());
|
|
||||||
|
|
||||||
InitiatorOutgoingFileTransferInitiated sessionHandler =
|
|
||||||
new InitiatorOutgoingFileTransferInitiated(this, fullJidAndSessionId, file);
|
|
||||||
|
|
||||||
jingleManager.registerJingleSessionHandler(recipient, initiate.getSessionId(), sessionHandler);
|
|
||||||
|
|
||||||
JingleTransportHandler<?> transportHandler = tm.createJingleTransportHandler(sessionHandler);
|
|
||||||
connection().sendStanza(initiate);
|
|
||||||
transportHandler.establishOutgoingSession(initiate, new JingleTransportEstablishedCallback() {
|
|
||||||
@Override
|
|
||||||
public void onSessionEstablished(BytestreamSession bytestreamSession) {
|
|
||||||
try {
|
|
||||||
byte[] filebuf = new byte[(int) file.length()];
|
|
||||||
HashElement hashElement = FileAndHashReader.readAndCalculateHash(file, filebuf, HashManager.ALGORITHM.SHA_256);
|
|
||||||
bytestreamSession.getInputStream().read(filebuf);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
bytestreamSession.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public JingleFileTransferSession getJingleSession(JingleManager.FullJidAndSessionId fullJidAndSessionId) {
|
||||||
|
return jingleSessions.get(fullJidAndSessionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void removeJingleSession(JingleFileTransferSession session) {
|
||||||
|
jingleSessions.remove(session.getFullJidAndSessionId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSessionFailure(JingleTransportFailureException reason) {
|
public IQ handleJingleSessionInitiate(Jingle jingle) {
|
||||||
|
JingleFileTransferSession fresh = new JingleFileTransferSession(connection(), jingle);
|
||||||
}
|
addJingleSession(fresh);
|
||||||
});
|
return fresh.handleJingleSessionRequest(jingle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FullJid ourJid() {
|
public void sendFile(FullJid recipient, File file) {
|
||||||
return connection().getUser();
|
JingleFileTransferSession fresh = new JingleFileTransferSession(connection(), recipient, file);
|
||||||
}
|
addJingleSession(fresh);
|
||||||
|
|
||||||
@Override
|
|
||||||
public IQ handleJingleSessionInitiate(final Jingle jingle) {
|
|
||||||
if (jingle.getAction() != JingleAction.session_initiate) {
|
|
||||||
//TODO tie-break?
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
notifyIncomingFileTransferListeners(jingle, new JingleFileTransferCallback() {
|
|
||||||
@Override
|
|
||||||
public void acceptFileTransfer(final File target) throws Exception {
|
|
||||||
connection().sendStanza(sessionAccept(jingle));
|
|
||||||
ResponderIncomingFileTransferAccepted responded = new ResponderIncomingFileTransferAccepted(
|
|
||||||
JingleFileTransferManager.this, jingle, target);
|
|
||||||
jingleManager.registerJingleSessionHandler(jingle.getFrom().asFullJidIfPossible(), jingle.getSessionId(),
|
|
||||||
responded);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void declineFileTransfer() throws SmackException.NotConnectedException, InterruptedException {
|
|
||||||
connection().sendStanza(decline(jingle));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return IQ.createResultIQ(jingle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Jingle sessionInitiate(FullJid recipient, JingleContentDescription contentDescription, JingleContentTransport transport) {
|
|
||||||
Jingle.Builder jb = Jingle.getBuilder();
|
|
||||||
jb.setSessionId(StringUtils.randomString(24))
|
|
||||||
.setAction(JingleAction.session_initiate)
|
|
||||||
.setInitiator(connection().getUser());
|
|
||||||
|
|
||||||
JingleContent.Builder cb = JingleContent.getBuilder();
|
|
||||||
cb.setDescription(contentDescription)
|
|
||||||
.setName(StringUtils.randomString(24))
|
|
||||||
.setCreator(JingleContent.Creator.initiator)
|
|
||||||
.setSenders(JingleContent.Senders.initiator)
|
|
||||||
.addTransport(transport);
|
|
||||||
|
|
||||||
jb.addJingleContent(cb.build());
|
|
||||||
Jingle jingle = jb.build();
|
|
||||||
jingle.setFrom(connection().getUser());
|
|
||||||
jingle.setTo(recipient);
|
|
||||||
|
|
||||||
return jingle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Jingle sessionAccept(Jingle request) throws Exception {
|
|
||||||
JingleContent content = request.getContents().get(0);
|
|
||||||
|
|
||||||
Jingle.Builder jb = Jingle.getBuilder();
|
|
||||||
jb.setSessionId(request.getSessionId())
|
|
||||||
.setAction(JingleAction.session_accept)
|
|
||||||
.setResponder(connection().getUser());
|
|
||||||
|
|
||||||
JingleContent.Builder cb = JingleContent.getBuilder();
|
|
||||||
cb.setSenders(content.getSenders())
|
|
||||||
.setCreator(content.getCreator())
|
|
||||||
.setName(content.getName())
|
|
||||||
.setDescription(content.getDescription());
|
|
||||||
|
|
||||||
AbstractJingleTransportManager<?> tm = JingleTransportManager.getInstanceFor(connection())
|
|
||||||
.getJingleContentTransportManager(request);
|
|
||||||
|
|
||||||
JingleContentTransport transport = tm.createJingleContentTransport(request);
|
|
||||||
cb.addTransport(transport);
|
|
||||||
|
|
||||||
jb.addJingleContent(cb.build());
|
|
||||||
Jingle jingle = jb.build();
|
|
||||||
jingle.setFrom(connection().getUser());
|
|
||||||
jingle.setTo(request.getFrom());
|
|
||||||
|
|
||||||
return jingle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Jingle decline(Jingle request) {
|
|
||||||
Jingle.Builder jb = Jingle.getBuilder();
|
|
||||||
jb.setResponder(connection().getUser())
|
|
||||||
.setAction(JingleAction.session_terminate)
|
|
||||||
.setReason(JingleReason.Reason.decline)
|
|
||||||
.setSessionId(request.getSessionId());
|
|
||||||
Jingle jingle = jb.build();
|
|
||||||
jingle.setTo(request.getFrom());
|
|
||||||
jingle.setFrom(connection().getUser());
|
|
||||||
return jingle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void receiveFile(Jingle request, BytestreamSession session, File target) {
|
|
||||||
JingleFileTransferChild file = (JingleFileTransferChild)
|
|
||||||
request.getContents().get(0).getDescription().getJingleContentDescriptionChildren().get(0);
|
|
||||||
|
|
||||||
InputStream inputStream = null;
|
|
||||||
FileOutputStream outputStream = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (!target.exists()) {
|
|
||||||
target.createNewFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
inputStream = session.getInputStream();
|
|
||||||
outputStream = new FileOutputStream(target);
|
|
||||||
|
|
||||||
byte[] fileBuf = new byte[file.getSize()];
|
|
||||||
byte[] buf = new byte[2048];
|
|
||||||
int read = 0;
|
|
||||||
while (read < fileBuf.length) {
|
|
||||||
int r = inputStream.read(buf);
|
|
||||||
if (r >= 0) {
|
|
||||||
System.arraycopy(buf, 0, fileBuf, read, r);
|
|
||||||
read += r;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
outputStream.write(fileBuf);
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
if (inputStream != null) {
|
|
||||||
inputStream.close();
|
|
||||||
}
|
|
||||||
if (outputStream != null) {
|
|
||||||
outputStream.close();
|
|
||||||
}
|
|
||||||
session.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public XMPPConnection getConnection() {
|
|
||||||
return connection();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,513 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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 java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.SmackException;
|
||||||
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
|
import org.jivesoftware.smackx.bytestreams.BytestreamSession;
|
||||||
|
import org.jivesoftware.smackx.hashes.HashManager;
|
||||||
|
import org.jivesoftware.smackx.hashes.element.HashElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.AbstractJingleTransportManager;
|
||||||
|
import org.jivesoftware.smackx.jingle.JingleManager;
|
||||||
|
import org.jivesoftware.smackx.jingle.JingleTransportEstablishedCallback;
|
||||||
|
import org.jivesoftware.smackx.jingle.JingleTransportHandler;
|
||||||
|
import org.jivesoftware.smackx.jingle.JingleTransportManager;
|
||||||
|
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.element.JingleReason;
|
||||||
|
import org.jivesoftware.smackx.jingle.exception.JingleTransportFailureException;
|
||||||
|
import org.jivesoftware.smackx.jingle.exception.UnsupportedJingleTransportException;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.callback.JingleFileTransferCallback;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferChild;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferContentDescription;
|
||||||
|
import org.jxmpp.jid.FullJid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JingleSession.
|
||||||
|
*/
|
||||||
|
public class JingleFileTransferSession extends AbstractJingleSession {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(JingleFileTransferSession.class.getName());
|
||||||
|
|
||||||
|
private final File source;
|
||||||
|
private File target;
|
||||||
|
private final JingleContent proposedContent;
|
||||||
|
private final FullJid remote;
|
||||||
|
private final String sessionId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a file.
|
||||||
|
* @param connection
|
||||||
|
* @param send
|
||||||
|
*/
|
||||||
|
public JingleFileTransferSession(XMPPConnection connection, FullJid recipient, File send) {
|
||||||
|
super(connection);
|
||||||
|
this.remote = recipient;
|
||||||
|
this.source = send;
|
||||||
|
this.sessionId = StringUtils.randomString(24);
|
||||||
|
|
||||||
|
JingleFileTransferChild fileTransferChild = fileElementFromFile(send);
|
||||||
|
JingleContent.Builder cb = JingleContent.getBuilder();
|
||||||
|
cb.setSenders(JingleContent.Senders.initiator)
|
||||||
|
.setCreator(JingleContent.Creator.initiator)
|
||||||
|
.setName(StringUtils.randomString(24))
|
||||||
|
.setDescription(new JingleFileTransferContentDescription(
|
||||||
|
Collections.singletonList((JingleContentDescriptionChildElement) fileTransferChild)));
|
||||||
|
try {
|
||||||
|
cb.addTransport(defaultTransport());
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new AssertionError("At least IBB should work. " + e);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.proposedContent = cb.build();
|
||||||
|
|
||||||
|
try {
|
||||||
|
connection.sendStanza(createFileOffer());
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Could not send session-initiate: " + e, e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.state = new OutgoingInitiated(connection, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receive a file.
|
||||||
|
* @param connection
|
||||||
|
*/
|
||||||
|
public JingleFileTransferSession(XMPPConnection connection, Jingle initiate) {
|
||||||
|
super(connection);
|
||||||
|
this.sessionId = initiate.getSessionId();
|
||||||
|
this.remote = initiate.getInitiator();
|
||||||
|
this.source = null;
|
||||||
|
this.proposedContent = initiate.getContents().get(0);
|
||||||
|
|
||||||
|
this.state = new IncomingAccepted(connection, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* session-initiate has been sent.
|
||||||
|
*/
|
||||||
|
public static class OutgoingInitiated extends AbstractJingleSession {
|
||||||
|
|
||||||
|
private final JingleFileTransferSession parent;
|
||||||
|
|
||||||
|
public OutgoingInitiated(XMPPConnection connection, JingleFileTransferSession parent) {
|
||||||
|
super(connection);
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IQ handleSessionAccept(Jingle jingle) {
|
||||||
|
parent.state = new OutgoingAccepted(connection, parent);
|
||||||
|
//TODO: Notify parent
|
||||||
|
return IQ.createResultIQ(jingle);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IQ handleSessionTerminate(Jingle jingle) {
|
||||||
|
//TODO: notify client
|
||||||
|
JingleFileTransferManager.getInstanceFor(connection).removeJingleSession(parent);
|
||||||
|
return IQ.createResultIQ(jingle);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class OutgoingAccepted extends AbstractJingleSession {
|
||||||
|
private final JingleFileTransferSession parent;
|
||||||
|
|
||||||
|
public OutgoingAccepted(XMPPConnection connection, final JingleFileTransferSession parent) {
|
||||||
|
super(connection);
|
||||||
|
this.parent = parent;
|
||||||
|
|
||||||
|
AbstractJingleTransportManager<?> tm;
|
||||||
|
try {
|
||||||
|
tm = JingleTransportManager.getJingleContentTransportManager(connection,
|
||||||
|
parent.proposedContent.getJingleTransports().get(0));
|
||||||
|
} catch (UnsupportedJingleTransportException e) {
|
||||||
|
throw new AssertionError("Since we initiated the transport, we MUST know the transport method.");
|
||||||
|
}
|
||||||
|
|
||||||
|
JingleTransportHandler<?> transportHandler = tm.createJingleTransportHandler(this);
|
||||||
|
transportHandler.establishOutgoingSession(parent.getFullJidAndSessionId(),
|
||||||
|
parent.proposedContent, parent.outgoingFileTransferSessionEstablishedCallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class IncomingFresh extends AbstractJingleSession {
|
||||||
|
private final JingleFileTransferSession parent;
|
||||||
|
|
||||||
|
public IncomingFresh(XMPPConnection connection, JingleFileTransferSession parent) {
|
||||||
|
super(connection);
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IQ handleSessionInitiate(final Jingle initiate) {
|
||||||
|
if (initiate.getAction() != JingleAction.session_initiate) {
|
||||||
|
throw new IllegalArgumentException("Jingle action MUST be session-initiate!");
|
||||||
|
}
|
||||||
|
|
||||||
|
//Get <file/>
|
||||||
|
JingleFileTransferChild file = (JingleFileTransferChild) initiate.getContents().get(0)
|
||||||
|
.getDescription().getJingleContentDescriptionChildren().get(0);
|
||||||
|
|
||||||
|
final JingleFileTransferCallback callback = new JingleFileTransferCallback() {
|
||||||
|
@Override
|
||||||
|
public void acceptFileTransfer(File target) throws SmackException.NotConnectedException, InterruptedException {
|
||||||
|
Jingle response = null;
|
||||||
|
try {
|
||||||
|
response = parent.createSessionAccept(initiate);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOGGER.log(Level.WARNING, "Could not create accept-session stanza: " + e, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
connection.sendStanza(response);
|
||||||
|
parent.target = target;
|
||||||
|
parent.state = new IncomingAccepted(connection, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void declineFileTransfer() throws SmackException.NotConnectedException, InterruptedException {
|
||||||
|
connection.sendStanza(parent.createSessionDecline(initiate));
|
||||||
|
parent.state = null;
|
||||||
|
JingleFileTransferManager.getInstanceFor(connection).removeJingleSession(parent);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//Set callback
|
||||||
|
JingleFileTransferManager.getInstanceFor(connection).notifyIncomingJingleFileTransferListeners(file, callback);
|
||||||
|
return IQ.createResultIQ(initiate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class IncomingAccepted extends AbstractJingleSession {
|
||||||
|
|
||||||
|
public IncomingAccepted(XMPPConnection connection, JingleFileTransferSession parent) {
|
||||||
|
super(connection);
|
||||||
|
AbstractJingleTransportManager<?> tm = null;
|
||||||
|
try {
|
||||||
|
tm = JingleTransportManager.getJingleContentTransportManager(
|
||||||
|
connection, parent.proposedContent.getJingleTransports().get(0));
|
||||||
|
} catch (UnsupportedJingleTransportException e) {
|
||||||
|
throw new AssertionError("Since we accepted the transfer, we MUST know the transport method.");
|
||||||
|
}
|
||||||
|
|
||||||
|
JingleTransportHandler<?> transportHandler = tm.createJingleTransportHandler(this);
|
||||||
|
transportHandler.establishIncomingSession(parent.getFullJidAndSessionId(),
|
||||||
|
parent.proposedContent, parent.incomingFileTransferSessionEstablishedCallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Jingle createFileOffer() throws Exception {
|
||||||
|
Jingle.Builder jb = Jingle.getBuilder();
|
||||||
|
jb.setSessionId(sessionId)
|
||||||
|
.setAction(JingleAction.session_initiate)
|
||||||
|
.setInitiator(connection.getUser());
|
||||||
|
|
||||||
|
JingleContent.Builder cb = JingleContent.getBuilder();
|
||||||
|
cb.setSenders(JingleContent.Senders.initiator)
|
||||||
|
.setCreator(JingleContent.Creator.initiator)
|
||||||
|
.setName(StringUtils.randomString(24));
|
||||||
|
|
||||||
|
cb.setDescription(new JingleFileTransferContentDescription(Collections.singletonList(
|
||||||
|
proposedContent.getDescription().getJingleContentDescriptionChildren().get(0))));
|
||||||
|
|
||||||
|
cb.addTransport(proposedContent.getJingleTransports().get(0));
|
||||||
|
jb.addJingleContent(cb.build());
|
||||||
|
|
||||||
|
Jingle jingle = jb.build();
|
||||||
|
jingle.setTo(remote);
|
||||||
|
jingle.setFrom(connection.getUser());
|
||||||
|
|
||||||
|
return jingle;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Jingle createSessionAccept(Jingle jingle) throws Exception {
|
||||||
|
JingleContent content = jingle.getContents().get(0);
|
||||||
|
|
||||||
|
Jingle.Builder jb = Jingle.getBuilder();
|
||||||
|
jb.setAction(JingleAction.session_accept)
|
||||||
|
.setResponder(connection.getUser())
|
||||||
|
.setSessionId(sessionId);
|
||||||
|
|
||||||
|
JingleContent.Builder cb = JingleContent.getBuilder();
|
||||||
|
|
||||||
|
AbstractJingleTransportManager<?> tm;
|
||||||
|
try {
|
||||||
|
tm = JingleTransportManager.getJingleContentTransportManager(
|
||||||
|
connection, jingle);
|
||||||
|
} catch (UnsupportedJingleTransportException e) {
|
||||||
|
throw new AssertionError("Should never happen."); //TODO: Make sure.
|
||||||
|
}
|
||||||
|
|
||||||
|
cb.addTransport(tm.createJingleContentTransport(jingle))
|
||||||
|
.setDescription(content.getDescription())
|
||||||
|
.setName(content.getName())
|
||||||
|
.setCreator(content.getCreator())
|
||||||
|
.setSenders(content.getSenders());
|
||||||
|
|
||||||
|
jb.addJingleContent(cb.build());
|
||||||
|
Jingle accept = jb.build();
|
||||||
|
accept.setTo(remote);
|
||||||
|
accept.setFrom(connection.getUser());
|
||||||
|
|
||||||
|
return accept;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Jingle createSessionDecline(Jingle initiate) {
|
||||||
|
Jingle.Builder jb = Jingle.getBuilder();
|
||||||
|
jb.setAction(JingleAction.session_terminate)
|
||||||
|
.setReason(JingleReason.Reason.decline)
|
||||||
|
.setResponder(connection.getUser())
|
||||||
|
.setSessionId(sessionId);
|
||||||
|
|
||||||
|
return jb.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
static JingleFileTransferChild fileElementFromFile(File file) {
|
||||||
|
JingleFileTransferChild.Builder fb = JingleFileTransferChild.getBuilder();
|
||||||
|
fb.setFile(file)
|
||||||
|
.setDescription("A File")
|
||||||
|
.setMediaType("application/octetStream");
|
||||||
|
|
||||||
|
return fb.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
JingleContentTransport defaultTransport() {
|
||||||
|
JingleTransportManager transportManager = JingleTransportManager.getInstanceFor(connection);
|
||||||
|
JingleContentTransport transport = null;
|
||||||
|
Iterator<AbstractJingleTransportManager<?>> iterator =
|
||||||
|
transportManager.getAvailableJingleBytestreamManagers().iterator();
|
||||||
|
|
||||||
|
while (transport == null && iterator.hasNext()) {
|
||||||
|
AbstractJingleTransportManager<?> tm = iterator.next();
|
||||||
|
try {
|
||||||
|
transport = tm.createJingleContentTransport(remote);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOGGER.log(Level.WARNING, "Could not create JingleContentTransport " + tm.getNamespace() +
|
||||||
|
". Skip.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return transport;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected JingleTransportEstablishedCallback outgoingFileTransferSessionEstablishedCallback =
|
||||||
|
new JingleTransportEstablishedCallback() {
|
||||||
|
@Override
|
||||||
|
public void onSessionEstablished(BytestreamSession bytestreamSession) {
|
||||||
|
send(bytestreamSession);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSessionFailure(JingleTransportFailureException reason) {
|
||||||
|
//TODO: Send transport-failed or so.
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
protected JingleTransportEstablishedCallback incomingFileTransferSessionEstablishedCallback =
|
||||||
|
new JingleTransportEstablishedCallback() {
|
||||||
|
@Override
|
||||||
|
public void onSessionEstablished(BytestreamSession bytestreamSession) {
|
||||||
|
read(bytestreamSession);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSessionFailure(JingleTransportFailureException reason) {
|
||||||
|
//TODO: Send transport failed
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void send(BytestreamSession stream) {
|
||||||
|
if (source == null || !source.exists()) {
|
||||||
|
throw new IllegalStateException("Source file MUST NOT be null and MUST exist.");
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] filebuf = new byte[(int) source.length()];
|
||||||
|
HashElement hashElement = null;
|
||||||
|
try {
|
||||||
|
hashElement = FileAndHashReader.readAndCalculateHash(source, filebuf, HashManager.ALGORITHM.SHA_256);
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Could not read file: " + e, e);
|
||||||
|
//TODO: Terminate session.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: session-info with hash
|
||||||
|
|
||||||
|
try {
|
||||||
|
stream.getOutputStream().write(filebuf);
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Caught Exception while sending file: " + e, e);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
stream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Could not close OutputStream of ByteStream: " + e, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: session-info to signalize that file has been sent.
|
||||||
|
}
|
||||||
|
|
||||||
|
void read(BytestreamSession session) {
|
||||||
|
if (target == null) {
|
||||||
|
throw new IllegalStateException("Target file MUST NOT be null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
//Become mainstream
|
||||||
|
InputStream inputStream = null;
|
||||||
|
FileOutputStream fileOutputStream = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
inputStream = session.getInputStream();
|
||||||
|
fileOutputStream = new FileOutputStream(target);
|
||||||
|
|
||||||
|
int size = ((JingleFileTransferChild) proposedContent.getDescription()
|
||||||
|
.getJingleContentDescriptionChildren().get(0)).getSize();
|
||||||
|
|
||||||
|
byte[] filebuf = new byte[size];
|
||||||
|
byte[] readbuf = new byte[2048];
|
||||||
|
int read = 0;
|
||||||
|
|
||||||
|
while (read < size) {
|
||||||
|
int r = inputStream.read(readbuf);
|
||||||
|
|
||||||
|
if (r >= 0) {
|
||||||
|
System.arraycopy(readbuf, 0, filebuf, read, r);
|
||||||
|
read += r;
|
||||||
|
} else {
|
||||||
|
//TODO: Terminate?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileOutputStream.write(filebuf);
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Caught exception while receiving file: " + e, e);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (fileOutputStream != null) {
|
||||||
|
fileOutputStream.close();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Caught exception while closing FileOutputStream: " + e, e);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (inputStream != null) {
|
||||||
|
inputStream.close();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Caught exception while closing InputStream: " + e, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleManager.FullJidAndSessionId getFullJidAndSessionId() {
|
||||||
|
return new JingleManager.FullJidAndSessionId(remote, sessionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IQ handleSessionInitiate(Jingle sessionInitiate) {
|
||||||
|
return state.handleSessionInitiate(sessionInitiate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IQ handleSessionTerminate(Jingle sessionTerminate) {
|
||||||
|
return state.handleSessionTerminate(sessionTerminate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IQ handleSessionInfo(Jingle sessionInfo) {
|
||||||
|
return state.handleSessionInfo(sessionInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IQ handleSessionAccept(Jingle sessionAccept) {
|
||||||
|
return state.handleSessionAccept(sessionAccept);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IQ handleContentAdd(Jingle contentAdd) {
|
||||||
|
return state.handleContentAdd(contentAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IQ handleContentAccept(Jingle contentAccept) {
|
||||||
|
return state.handleContentAccept(contentAccept);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IQ handleContentModify(Jingle contentModify) {
|
||||||
|
return state.handleContentModify(contentModify);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IQ handleContentReject(Jingle contentReject) {
|
||||||
|
return state.handleContentReject(contentReject);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IQ handleContentRemove(Jingle contentRemove) {
|
||||||
|
return state.handleContentRemove(contentRemove);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IQ handleDescriptionInfo(Jingle descriptionInfo) {
|
||||||
|
return state.handleDescriptionInfo(descriptionInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IQ handleSecurityInfo(Jingle securityInfo) {
|
||||||
|
return state.handleSecurityInfo(securityInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IQ handleTransportAccept(Jingle transportAccept) {
|
||||||
|
return state.handleTransportAccept(transportAccept);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IQ handleTransportInfo(Jingle transportInfo) {
|
||||||
|
return state.handleTransportInfo(transportInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IQ handleTransportReplace(Jingle transportReplace) {
|
||||||
|
return state.handleTransportReplace(transportReplace);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IQ handleTransportReject(Jingle transportReject) {
|
||||||
|
return state.handleTransportReject(transportReject);
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,7 +25,7 @@ import org.jivesoftware.smack.SmackException;
|
||||||
*/
|
*/
|
||||||
public interface JingleFileTransferCallback {
|
public interface JingleFileTransferCallback {
|
||||||
|
|
||||||
void acceptFileTransfer(File target) throws Exception;
|
void acceptFileTransfer(File target) throws SmackException.NotConnectedException, InterruptedException;
|
||||||
|
|
||||||
void declineFileTransfer() throws SmackException.NotConnectedException, InterruptedException;
|
void declineFileTransfer() throws SmackException.NotConnectedException, InterruptedException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,124 +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.handler;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
|
||||||
import org.jivesoftware.smack.packet.IQ;
|
|
||||||
import org.jivesoftware.smackx.bytestreams.BytestreamSession;
|
|
||||||
import org.jivesoftware.smackx.hashes.HashManager;
|
|
||||||
import org.jivesoftware.smackx.hashes.element.HashElement;
|
|
||||||
import org.jivesoftware.smackx.jingle.AbstractJingleTransportManager;
|
|
||||||
import org.jivesoftware.smackx.jingle.JingleManager;
|
|
||||||
import org.jivesoftware.smackx.jingle.JingleSessionHandler;
|
|
||||||
import org.jivesoftware.smackx.jingle.JingleTransportEstablishedCallback;
|
|
||||||
import org.jivesoftware.smackx.jingle.JingleTransportHandler;
|
|
||||||
import org.jivesoftware.smackx.jingle.JingleTransportManager;
|
|
||||||
import org.jivesoftware.smackx.jingle.element.Jingle;
|
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleContentTransport;
|
|
||||||
import org.jivesoftware.smackx.jingle.exception.JingleTransportFailureException;
|
|
||||||
import org.jivesoftware.smackx.jingle.exception.UnsupportedJingleTransportException;
|
|
||||||
import org.jivesoftware.smackx.jingle_filetransfer.FileAndHashReader;
|
|
||||||
import org.jivesoftware.smackx.jingle_filetransfer.JingleFileTransferManager;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This handler represents the state of the initiators jingle session after session-initiate was sent.
|
|
||||||
*/
|
|
||||||
public class InitiatorOutgoingFileTransferInitiated implements JingleSessionHandler {
|
|
||||||
|
|
||||||
private final WeakReference<JingleFileTransferManager> manager;
|
|
||||||
private final JingleManager.FullJidAndSessionId fullJidAndSessionId;
|
|
||||||
private final File file;
|
|
||||||
|
|
||||||
public InitiatorOutgoingFileTransferInitiated(JingleFileTransferManager manager, JingleManager.FullJidAndSessionId fullJidAndSessionId, File file) {
|
|
||||||
this.fullJidAndSessionId = fullJidAndSessionId;
|
|
||||||
this.file = file;
|
|
||||||
this.manager = new WeakReference<>(manager);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IQ handleJingleSessionRequest(final Jingle jingle, String sessionId) {
|
|
||||||
final AbstractJingleTransportManager<?> bm;
|
|
||||||
try {
|
|
||||||
bm = JingleTransportManager.getInstanceFor(getConnection())
|
|
||||||
.getJingleContentTransportManager(jingle);
|
|
||||||
} catch (UnsupportedJingleTransportException e) {
|
|
||||||
// TODO
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
JingleContentTransport transport = jingle.getContents().get(0).getJingleTransports().get(0);
|
|
||||||
|
|
||||||
switch (jingle.getAction()) {
|
|
||||||
case session_accept:
|
|
||||||
bm.createJingleTransportHandler(this).establishOutgoingSession(jingle, new JingleTransportEstablishedCallback() {
|
|
||||||
@Override
|
|
||||||
public void onSessionEstablished(final BytestreamSession bytestreamSession) {
|
|
||||||
new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
startTransfer(bytestreamSession, jingle);
|
|
||||||
}
|
|
||||||
}.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSessionFailure(JingleTransportFailureException reason) {
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Runnable transfer = new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
transfer.run();
|
|
||||||
|
|
||||||
break;
|
|
||||||
case session_terminate:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
return IQ.createResultIQ(jingle);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public XMPPConnection getConnection() {
|
|
||||||
JingleFileTransferManager m = manager.get();
|
|
||||||
return m != null ? m.getConnection() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void startTransfer(BytestreamSession session, Jingle jingle) {
|
|
||||||
HashElement fileHash;
|
|
||||||
byte[] buf = new byte[(int) file.length()];
|
|
||||||
|
|
||||||
try {
|
|
||||||
fileHash = FileAndHashReader.readAndCalculateHash(file, buf, HashManager.ALGORITHM.SHA_256);
|
|
||||||
session.getOutputStream().write(buf);
|
|
||||||
session.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
//TODO:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,88 +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.handler;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
|
||||||
import org.jivesoftware.smack.packet.IQ;
|
|
||||||
import org.jivesoftware.smackx.bytestreams.BytestreamSession;
|
|
||||||
import org.jivesoftware.smackx.jingle.AbstractJingleTransportManager;
|
|
||||||
import org.jivesoftware.smackx.jingle.JingleSessionHandler;
|
|
||||||
import org.jivesoftware.smackx.jingle.JingleTransportEstablishedCallback;
|
|
||||||
import org.jivesoftware.smackx.jingle.JingleTransportManager;
|
|
||||||
import org.jivesoftware.smackx.jingle.element.Jingle;
|
|
||||||
import org.jivesoftware.smackx.jingle.exception.JingleTransportFailureException;
|
|
||||||
import org.jivesoftware.smackx.jingle.exception.UnsupportedJingleTransportException;
|
|
||||||
import org.jivesoftware.smackx.jingle_filetransfer.JingleFileTransferManager;
|
|
||||||
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferChild;
|
|
||||||
import org.jxmpp.jid.FullJid;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This handler represents the state of the responders jingle session after the responder sent session-accept.
|
|
||||||
*/
|
|
||||||
public class ResponderIncomingFileTransferAccepted implements JingleSessionHandler {
|
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(ResponderIncomingFileTransferAccepted.class.getName());
|
|
||||||
|
|
||||||
private final WeakReference<JingleFileTransferManager> manager;
|
|
||||||
private AbstractJingleTransportManager<?> transportManager;
|
|
||||||
private final File target;
|
|
||||||
private final int size;
|
|
||||||
private final FullJid initiator;
|
|
||||||
private final String sessionId;
|
|
||||||
|
|
||||||
public ResponderIncomingFileTransferAccepted(final JingleFileTransferManager manager, final Jingle initiate, final File target) {
|
|
||||||
this.manager = new WeakReference<>(manager);
|
|
||||||
this.target = target;
|
|
||||||
this.size = ((JingleFileTransferChild) initiate.getContents().get(0).getDescription()
|
|
||||||
.getJingleContentDescriptionChildren().get(0)).getSize();
|
|
||||||
try {
|
|
||||||
this.transportManager = JingleTransportManager.getInstanceFor(manager.getConnection()).getJingleContentTransportManager(initiate);
|
|
||||||
} catch (UnsupportedJingleTransportException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
this.initiator = initiate.getInitiator();
|
|
||||||
this.sessionId = initiate.getSessionId();
|
|
||||||
|
|
||||||
transportManager.createJingleTransportHandler(this).establishIncomingSession(
|
|
||||||
initiate, new JingleTransportEstablishedCallback() {
|
|
||||||
@Override
|
|
||||||
public void onSessionEstablished(BytestreamSession bytestreamSession) {
|
|
||||||
manager.receiveFile(initiate, bytestreamSession, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSessionFailure(JingleTransportFailureException reason) {
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IQ handleJingleSessionRequest(Jingle jingle, String sessionId) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public XMPPConnection getConnection() {
|
|
||||||
JingleFileTransferManager m = manager.get();
|
|
||||||
return m != null ? m.getConnection() : null;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,23 +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.handler;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This handler represents the state of the responders jingle session after the responder sent session-replace.
|
|
||||||
*/
|
|
||||||
public class ResponderIncomingFileTransferTransportReplaced {
|
|
||||||
}
|
|
|
@ -1,21 +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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Smack's API for <a href="https://xmpp.org/extensions/xep-0234.html">XEP-0234: Jingle File Transfer</a>.
|
|
||||||
*/
|
|
||||||
package org.jivesoftware.smackx.jingle_filetransfer.handler;
|
|
|
@ -16,13 +16,13 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.jingle_filetransfer.listener;
|
package org.jivesoftware.smackx.jingle_filetransfer.listener;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.jingle.element.Jingle;
|
|
||||||
import org.jivesoftware.smackx.jingle_filetransfer.callback.JingleFileTransferCallback;
|
import org.jivesoftware.smackx.jingle_filetransfer.callback.JingleFileTransferCallback;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferChild;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listener for incoming file transfers.
|
* Listener for incoming file transfers.
|
||||||
*/
|
*/
|
||||||
public interface IncomingJingleFileTransferListener {
|
public interface IncomingJingleFileTransferListener {
|
||||||
|
|
||||||
void onIncomingJingleFileTransfer(Jingle jingle, JingleFileTransferCallback callback);
|
void onIncomingJingleFileTransfer(JingleFileTransferChild file, JingleFileTransferCallback callback);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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_ibb;
|
package org.jivesoftware.smackx.jingle_ibb;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
|
@ -10,10 +26,11 @@ import org.jivesoftware.smackx.bytestreams.BytestreamRequest;
|
||||||
import org.jivesoftware.smackx.bytestreams.BytestreamSession;
|
import org.jivesoftware.smackx.bytestreams.BytestreamSession;
|
||||||
import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager;
|
import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager;
|
||||||
import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamSession;
|
import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamSession;
|
||||||
|
import org.jivesoftware.smackx.jingle.JingleManager;
|
||||||
import org.jivesoftware.smackx.jingle.JingleSessionHandler;
|
import org.jivesoftware.smackx.jingle.JingleSessionHandler;
|
||||||
import org.jivesoftware.smackx.jingle.JingleTransportEstablishedCallback;
|
import org.jivesoftware.smackx.jingle.JingleTransportEstablishedCallback;
|
||||||
import org.jivesoftware.smackx.jingle.JingleTransportHandler;
|
import org.jivesoftware.smackx.jingle.JingleTransportHandler;
|
||||||
import org.jivesoftware.smackx.jingle.element.Jingle;
|
import org.jivesoftware.smackx.jingle.element.JingleContent;
|
||||||
import org.jivesoftware.smackx.jingle.exception.JingleTransportFailureException;
|
import org.jivesoftware.smackx.jingle.exception.JingleTransportFailureException;
|
||||||
import org.jivesoftware.smackx.jingle_ibb.element.JingleIBBTransport;
|
import org.jivesoftware.smackx.jingle_ibb.element.JingleIBBTransport;
|
||||||
|
|
||||||
|
@ -29,12 +46,14 @@ public class JingleIBBTransportHandler implements JingleTransportHandler<JingleI
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void establishOutgoingSession(Jingle request, JingleTransportEstablishedCallback callback) {
|
public void establishOutgoingSession(JingleManager.FullJidAndSessionId fullJidAndSessionId,
|
||||||
|
JingleContent content,
|
||||||
|
JingleTransportEstablishedCallback callback) {
|
||||||
InBandBytestreamSession session;
|
InBandBytestreamSession session;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
session = InBandBytestreamManager.getByteStreamManager(getConnection())
|
session = InBandBytestreamManager.getByteStreamManager(getConnection())
|
||||||
.establishSession(request.getResponder(), request.getSessionId());
|
.establishSession(fullJidAndSessionId.getFullJid(), fullJidAndSessionId.getSessionId());
|
||||||
} catch (SmackException.NoResponseException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | InterruptedException e) {
|
} catch (SmackException.NoResponseException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | InterruptedException e) {
|
||||||
callback.onSessionFailure(new JingleTransportFailureException(e));
|
callback.onSessionFailure(new JingleTransportFailureException(e));
|
||||||
return;
|
return;
|
||||||
|
@ -44,12 +63,14 @@ public class JingleIBBTransportHandler implements JingleTransportHandler<JingleI
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void establishIncomingSession(final Jingle initiate, final JingleTransportEstablishedCallback callback) {
|
public void establishIncomingSession(final JingleManager.FullJidAndSessionId fullJidAndSessionId,
|
||||||
|
JingleContent content,
|
||||||
|
final JingleTransportEstablishedCallback callback) {
|
||||||
InBandBytestreamManager.getByteStreamManager(getConnection()).addIncomingBytestreamListener(new BytestreamListener() {
|
InBandBytestreamManager.getByteStreamManager(getConnection()).addIncomingBytestreamListener(new BytestreamListener() {
|
||||||
@Override
|
@Override
|
||||||
public void incomingBytestreamRequest(BytestreamRequest request) {
|
public void incomingBytestreamRequest(BytestreamRequest request) {
|
||||||
if (request.getFrom().asFullJidIfPossible().equals(initiate.getInitiator())
|
if (request.getFrom().asFullJidIfPossible().equals(fullJidAndSessionId.getFullJid())
|
||||||
&& request.getSessionID().equals(initiate.getSessionId())) {
|
&& request.getSessionID().equals(fullJidAndSessionId.getSessionId())) {
|
||||||
BytestreamSession session;
|
BytestreamSession session;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -76,50 +76,4 @@ public final class JingleIBBTransportManager extends AbstractJingleTransportMana
|
||||||
return JingleIBBTransport.NAMESPACE_V1;
|
return JingleIBBTransport.NAMESPACE_V1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
@Override
|
|
||||||
public Jingle createSessionInitiate(FullJid targetJID, JingleContentDescription application, String sessionID) throws XMPPException, IOException, InterruptedException, SmackException {
|
|
||||||
Jingle.Builder jb = Jingle.getBuilder();
|
|
||||||
JingleContent.Builder cb = JingleContent.getBuilder();
|
|
||||||
cb.setDescription(application)
|
|
||||||
.setName(JingleTransportManager.generateRandomId())
|
|
||||||
.setCreator(JingleContent.Creator.initiator)
|
|
||||||
.setSenders(JingleContent.Senders.initiator)
|
|
||||||
.addTransport(new JingleIBBTransport());
|
|
||||||
jb.addJingleContent(cb.build());
|
|
||||||
jb.setInitiator(connection().getUser())
|
|
||||||
.setResponder(targetJID)
|
|
||||||
.setSessionId(sessionID)
|
|
||||||
.setAction(JingleAction.session_initiate);
|
|
||||||
Jingle jingle = jb.build();
|
|
||||||
jingle.setTo(targetJID);
|
|
||||||
jingle.setFrom(connection().getUser());
|
|
||||||
jingle.setType(IQ.Type.set);
|
|
||||||
return jingle;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Jingle createSessionAccept(Jingle request) {
|
|
||||||
Jingle.Builder jb = Jingle.getBuilder();
|
|
||||||
jb.setAction(JingleAction.session_accept)
|
|
||||||
.setSessionId(request.getSid())
|
|
||||||
.setResponder(connection().getUser());
|
|
||||||
|
|
||||||
JingleContent requestContent = request.getContents().get(0);
|
|
||||||
JingleContent.Builder cb = JingleContent.getBuilder();
|
|
||||||
cb.setDescription(requestContent.getDescription())
|
|
||||||
.setSenders(requestContent.getSenders())
|
|
||||||
.setCreator(requestContent.getCreator())
|
|
||||||
.setName(requestContent.getName());
|
|
||||||
cb.addTransport(requestContent.getJingleTransports().get(0));
|
|
||||||
|
|
||||||
jb.addJingleContent(cb.build());
|
|
||||||
Jingle jingle = jb.build();
|
|
||||||
jingle.setType(IQ.Type.set);
|
|
||||||
jingle.setTo(request.getFrom());
|
|
||||||
jingle.setFrom(connection().getUser());
|
|
||||||
return jingle;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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_s5b;
|
package org.jivesoftware.smackx.jingle_s5b;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -15,6 +31,7 @@ import org.jivesoftware.smackx.bytestreams.socks5.Socks5Client;
|
||||||
import org.jivesoftware.smackx.bytestreams.socks5.Socks5Proxy;
|
import org.jivesoftware.smackx.bytestreams.socks5.Socks5Proxy;
|
||||||
import org.jivesoftware.smackx.bytestreams.socks5.Socks5Utils;
|
import org.jivesoftware.smackx.bytestreams.socks5.Socks5Utils;
|
||||||
import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;
|
import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;
|
||||||
|
import org.jivesoftware.smackx.jingle.JingleManager;
|
||||||
import org.jivesoftware.smackx.jingle.JingleSessionHandler;
|
import org.jivesoftware.smackx.jingle.JingleSessionHandler;
|
||||||
import org.jivesoftware.smackx.jingle.JingleTransportEstablishedCallback;
|
import org.jivesoftware.smackx.jingle.JingleTransportEstablishedCallback;
|
||||||
import org.jivesoftware.smackx.jingle.JingleTransportHandler;
|
import org.jivesoftware.smackx.jingle.JingleTransportHandler;
|
||||||
|
@ -44,10 +61,10 @@ public class JingleS5BTransportHandler implements JingleTransportHandler<JingleS
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void establishOutgoingSession(Jingle request, JingleTransportEstablishedCallback callback) {
|
public void establishOutgoingSession(JingleManager.FullJidAndSessionId fullJidAndSessionId,
|
||||||
JingleContent content = request.getContents().get(0);
|
JingleContent content,
|
||||||
|
JingleTransportEstablishedCallback callback) {
|
||||||
JingleContentTransport hopefullyS5BTransport = content.getJingleTransports().get(0);
|
JingleContentTransport hopefullyS5BTransport = content.getJingleTransports().get(0);
|
||||||
|
|
||||||
if (!hopefullyS5BTransport.getNamespace().equals(JingleS5BTransport.NAMESPACE_V1)) {
|
if (!hopefullyS5BTransport.getNamespace().equals(JingleS5BTransport.NAMESPACE_V1)) {
|
||||||
throw new IllegalArgumentException("Transport must be a JingleS5BTransport.");
|
throw new IllegalArgumentException("Transport must be a JingleS5BTransport.");
|
||||||
}
|
}
|
||||||
|
@ -55,8 +72,9 @@ public class JingleS5BTransportHandler implements JingleTransportHandler<JingleS
|
||||||
JingleS5BTransport transport = (JingleS5BTransport) hopefullyS5BTransport;
|
JingleS5BTransport transport = (JingleS5BTransport) hopefullyS5BTransport;
|
||||||
|
|
||||||
Socks5Proxy.getSocks5Proxy().addLocalAddress(Socks5Utils.createDigest(
|
Socks5Proxy.getSocks5Proxy().addLocalAddress(Socks5Utils.createDigest(
|
||||||
request.getSessionId(), getConnection().getUser().asFullJidIfPossible(), request.getResponder()));
|
fullJidAndSessionId.getSessionId(), //SessionID
|
||||||
|
getConnection().getUser().asFullJidIfPossible(), //Us
|
||||||
|
fullJidAndSessionId.getFullJid())); //Them
|
||||||
|
|
||||||
JingleS5BTransportCandidate usedCandidate = null;
|
JingleS5BTransportCandidate usedCandidate = null;
|
||||||
for (JingleContentTransportCandidate c : transport.getCandidates()) {
|
for (JingleContentTransportCandidate c : transport.getCandidates()) {
|
||||||
|
@ -86,7 +104,7 @@ public class JingleS5BTransportHandler implements JingleTransportHandler<JingleS
|
||||||
|
|
||||||
if (connectedSocket != null) {
|
if (connectedSocket != null) {
|
||||||
Jingle.Builder jb = Jingle.getBuilder();
|
Jingle.Builder jb = Jingle.getBuilder();
|
||||||
jb.setSessionId(request.getSessionId())
|
jb.setSessionId(fullJidAndSessionId.getSessionId())
|
||||||
.setAction(JingleAction.transport_info)
|
.setAction(JingleAction.transport_info)
|
||||||
.setInitiator(getConnection().getUser());
|
.setInitiator(getConnection().getUser());
|
||||||
|
|
||||||
|
@ -103,20 +121,19 @@ public class JingleS5BTransportHandler implements JingleTransportHandler<JingleS
|
||||||
|
|
||||||
Jingle jingle = jb.build();
|
Jingle jingle = jb.build();
|
||||||
jingle.setFrom(getConnection().getUser());
|
jingle.setFrom(getConnection().getUser());
|
||||||
jingle.setTo(request.getFrom());
|
jingle.setTo(fullJidAndSessionId.getFullJid());
|
||||||
try {
|
try {
|
||||||
getConnection().sendStanza(jingle);
|
getConnection().sendStanza(jingle);
|
||||||
} catch (SmackException.NotConnectedException e) {
|
} catch (SmackException.NotConnectedException | InterruptedException e) {
|
||||||
e.printStackTrace();
|
LOGGER.log(Level.WARNING, "Could not send candidate-used stanza: " + e, e);
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void establishIncomingSession(Jingle request, JingleTransportEstablishedCallback callback) {
|
public void establishIncomingSession(JingleManager.FullJidAndSessionId fullJidAndSessionId,
|
||||||
JingleContent content = request.getContents().get(0);
|
JingleContent content,
|
||||||
|
JingleTransportEstablishedCallback callback) {
|
||||||
JingleContentTransport hopefullyS5BTransport = content.getJingleTransports().get(0);
|
JingleContentTransport hopefullyS5BTransport = content.getJingleTransports().get(0);
|
||||||
if (!hopefullyS5BTransport.getNamespace().equals(JingleS5BTransport.NAMESPACE_V1)) {
|
if (!hopefullyS5BTransport.getNamespace().equals(JingleS5BTransport.NAMESPACE_V1)) {
|
||||||
throw new IllegalArgumentException("Transport must be a JingleS5BTransport.");
|
throw new IllegalArgumentException("Transport must be a JingleS5BTransport.");
|
||||||
|
|
|
@ -79,7 +79,7 @@ public final class JingleManager extends Manager {
|
||||||
.addExtension(JingleError.UNKNOWN_SESSION);
|
.addExtension(JingleError.UNKNOWN_SESSION);
|
||||||
return IQ.createErrorResponse(jingle, errorBuilder);
|
return IQ.createErrorResponse(jingle, errorBuilder);
|
||||||
}
|
}
|
||||||
return jingleSessionHandler.handleJingleSessionRequest(jingle, jingle.getSessionId());
|
return jingleSessionHandler.handleJingleSessionRequest(jingle);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (jingle.getContents().size() > 1) {
|
if (jingle.getContents().size() > 1) {
|
||||||
|
|
|
@ -22,7 +22,7 @@ import org.jivesoftware.smackx.jingle.element.Jingle;
|
||||||
|
|
||||||
public interface JingleSessionHandler {
|
public interface JingleSessionHandler {
|
||||||
|
|
||||||
IQ handleJingleSessionRequest(Jingle jingle, String sessionId);
|
IQ handleJingleSessionRequest(Jingle jingle);
|
||||||
|
|
||||||
XMPPConnection getConnection();
|
XMPPConnection getConnection();
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,26 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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;
|
package org.jivesoftware.smackx.jingle;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.bytestreams.BytestreamSession;
|
import org.jivesoftware.smackx.bytestreams.BytestreamSession;
|
||||||
import org.jivesoftware.smackx.jingle.exception.JingleTransportFailureException;
|
import org.jivesoftware.smackx.jingle.exception.JingleTransportFailureException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by vanitas on 10.06.17.
|
* Callback that's used to establish BytestreamSessions from transport methods.
|
||||||
*/
|
*/
|
||||||
public interface JingleTransportEstablishedCallback {
|
public interface JingleTransportEstablishedCallback {
|
||||||
void onSessionEstablished(BytestreamSession bytestreamSession);
|
void onSessionEstablished(BytestreamSession bytestreamSession);
|
||||||
|
|
|
@ -1,17 +1,37 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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;
|
package org.jivesoftware.smackx.jingle;
|
||||||
|
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
import org.jivesoftware.smackx.jingle.element.Jingle;
|
import org.jivesoftware.smackx.jingle.element.JingleContent;
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleContentTransport;
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransport;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for JingleTransports
|
* Handler for JingleTransports.
|
||||||
*/
|
*/
|
||||||
public interface JingleTransportHandler<D extends JingleContentTransport> {
|
public interface JingleTransportHandler<D extends JingleContentTransport> {
|
||||||
|
|
||||||
void establishOutgoingSession(Jingle request, JingleTransportEstablishedCallback callback);
|
void establishOutgoingSession(JingleManager.FullJidAndSessionId fullJidAndSessionId,
|
||||||
|
JingleContent content,
|
||||||
|
JingleTransportEstablishedCallback callback);
|
||||||
|
|
||||||
void establishIncomingSession(Jingle request, JingleTransportEstablishedCallback callback);
|
void establishIncomingSession(JingleManager.FullJidAndSessionId fullJidAndSessionId,
|
||||||
|
JingleContent content,
|
||||||
|
JingleTransportEstablishedCallback callback);
|
||||||
|
|
||||||
XMPPConnection getConnection();
|
XMPPConnection getConnection();
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import org.jivesoftware.smack.Manager;
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
import org.jivesoftware.smack.util.StringUtils;
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
import org.jivesoftware.smackx.jingle.element.Jingle;
|
import org.jivesoftware.smackx.jingle.element.Jingle;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransport;
|
||||||
import org.jivesoftware.smackx.jingle.exception.UnsupportedJingleTransportException;
|
import org.jivesoftware.smackx.jingle.exception.UnsupportedJingleTransportException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -49,6 +50,18 @@ public final class JingleTransportManager extends Manager {
|
||||||
return manager;
|
return manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static AbstractJingleTransportManager<?> getJingleContentTransportManager(XMPPConnection connection, String namespace) throws UnsupportedJingleTransportException {
|
||||||
|
return getInstanceFor(connection).getJingleContentTransportManager(namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AbstractJingleTransportManager<?> getJingleContentTransportManager(XMPPConnection connection, Jingle jingle) throws UnsupportedJingleTransportException {
|
||||||
|
return getInstanceFor(connection).getJingleContentTransportManager(jingle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AbstractJingleTransportManager<?> getJingleContentTransportManager(XMPPConnection connection, JingleContentTransport transport) throws UnsupportedJingleTransportException {
|
||||||
|
return getInstanceFor(connection).getJingleContentTransportManager(transport.getNamespace());
|
||||||
|
}
|
||||||
|
|
||||||
public AbstractJingleTransportManager<?> getJingleContentTransportManager(String namespace) throws UnsupportedJingleTransportException {
|
public AbstractJingleTransportManager<?> getJingleContentTransportManager(String namespace) throws UnsupportedJingleTransportException {
|
||||||
AbstractJingleTransportManager<?> manager = contentTransportManagers.get(namespace);
|
AbstractJingleTransportManager<?> manager = contentTransportManagers.get(namespace);
|
||||||
if (manager == null) {
|
if (manager == null) {
|
||||||
|
|
|
@ -1,7 +1,23 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.exception;
|
package org.jivesoftware.smackx.jingle.exception;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by vanitas on 10.06.17.
|
* Exception that gets thrown when establishing a session using a transport fails.
|
||||||
*/
|
*/
|
||||||
public class JingleTransportFailureException extends Exception {
|
public class JingleTransportFailureException extends Exception {
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue