mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-11-30 16:02:07 +01:00
Proper(er) implementation
This commit is contained in:
parent
83cedbe00f
commit
2686fc1ccb
19 changed files with 674 additions and 469 deletions
|
@ -17,48 +17,34 @@
|
||||||
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.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.jivesoftware.smack.Manager;
|
import org.jivesoftware.smack.Manager;
|
||||||
import org.jivesoftware.smack.SmackException;
|
import org.jivesoftware.smack.SmackException;
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
import org.jivesoftware.smack.XMPPException;
|
|
||||||
import org.jivesoftware.smack.packet.IQ;
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
import org.jivesoftware.smack.packet.XMPPError;
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamListener;
|
|
||||||
import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager;
|
|
||||||
import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamRequest;
|
|
||||||
import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamSession;
|
|
||||||
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
||||||
import org.jivesoftware.smackx.hash.HashManager;
|
import org.jivesoftware.smackx.hash.HashManager;
|
||||||
import org.jivesoftware.smackx.hash.element.HashElement;
|
import org.jivesoftware.smackx.hash.element.HashElement;
|
||||||
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.JingleSession;
|
|
||||||
import org.jivesoftware.smackx.jingle.JingleSessionHandler;
|
|
||||||
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.JingleContent;
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleContentDescriptionChildElement;
|
import org.jivesoftware.smackx.jingle.element.JingleContentDescriptionChildElement;
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleError;
|
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleReason;
|
|
||||||
import org.jivesoftware.smackx.jingle.provider.JingleContentProviderManager;
|
import org.jivesoftware.smackx.jingle.provider.JingleContentProviderManager;
|
||||||
import org.jivesoftware.smackx.jingle_filetransfer.callback.IncomingJingleFileTransferCallback;
|
import org.jivesoftware.smackx.jingle_filetransfer.callback.IncomingJingleFileTransferCallback;
|
||||||
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferChildElement;
|
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferChildElement;
|
||||||
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferContentDescription;
|
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferContentDescription;
|
||||||
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_filetransfer.provider.JingleFileTransferContentDescriptionProvider;
|
||||||
import org.jivesoftware.smackx.jingle_ibb.JingleInBandByteStreamManager;
|
import org.jivesoftware.smackx.jingle_ibb.JingleInBandBytestreamTransportManager;
|
||||||
import org.jivesoftware.smackx.jingle_ibb.element.JingleInBandByteStreamTransport;
|
import org.jivesoftware.smackx.jingle_ibb.element.JingleInBandBytestreamTransport;
|
||||||
import org.jxmpp.jid.FullJid;
|
import org.jxmpp.jid.FullJid;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -66,15 +52,15 @@ import org.jxmpp.jid.FullJid;
|
||||||
*
|
*
|
||||||
* @author Paul Schaub
|
* @author Paul Schaub
|
||||||
*/
|
*/
|
||||||
public final class JingleFileTransferManager extends Manager implements JingleHandler, JingleSessionHandler {
|
public final class JingleFileTransferManager extends Manager implements JingleHandler {
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(JingleFileTransferManager.class.getName());
|
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 HashSet<IncomingJingleFileTransferListener> incomingJingleFileTransferListeners = new HashSet<>();
|
||||||
private final HashMap<String, JingleSession> sessions = new HashMap<>();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Private constructor. This registers a JingleContentDescriptionFileTransferProvider with the
|
* Private constructor. This registers a JingleContentDescriptionFileTransferProvider with the
|
||||||
|
@ -85,11 +71,12 @@ public final class JingleFileTransferManager extends Manager implements JingleHa
|
||||||
super(connection);
|
super(connection);
|
||||||
ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection);
|
ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection);
|
||||||
sdm.addFeature(NAMESPACE_V5);
|
sdm.addFeature(NAMESPACE_V5);
|
||||||
JingleManager.getInstanceFor(connection).registerDescriptionHandler(
|
jingleManager = JingleManager.getInstanceFor(connection);
|
||||||
|
jingleManager.registerDescriptionHandler(
|
||||||
NAMESPACE_V5, this);
|
NAMESPACE_V5, this);
|
||||||
JingleContentProviderManager.addJingleContentDescriptionProvider(
|
JingleContentProviderManager.addJingleContentDescriptionProvider(
|
||||||
NAMESPACE_V5, new JingleFileTransferContentDescriptionProvider());
|
NAMESPACE_V5, new JingleFileTransferContentDescriptionProvider());
|
||||||
|
JingleInBandBytestreamTransportManager.getInstanceFor(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -115,170 +102,10 @@ public final class JingleFileTransferManager extends Manager implements JingleHa
|
||||||
incomingJingleFileTransferListeners.remove(listener);
|
incomingJingleFileTransferListeners.remove(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public JingleFileTransferChildElement.Builder fileTransferPayloadBuilderFromFile(File file) {
|
void notifyIncomingFileTransferListeners(Jingle jingle, IncomingJingleFileTransferCallback callback) {
|
||||||
JingleFileTransferChildElement.Builder payloadBuilder = JingleFileTransferChildElement.getBuilder();
|
|
||||||
payloadBuilder.setDate(new Date(file.lastModified()));
|
|
||||||
payloadBuilder.setName(file.getAbsolutePath().substring(file.getAbsolutePath().lastIndexOf(File.pathSeparator) + 1));
|
|
||||||
payloadBuilder.setSize((int) file.length());
|
|
||||||
return payloadBuilder;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IQ handleJingleRequest(final Jingle jingle) {
|
|
||||||
LOGGER.log(Level.INFO, "handleJingleRequest");
|
|
||||||
final JingleSession session = new JingleSession(jingle.getInitiator(), jingle.getResponder(), jingle.getSid());
|
|
||||||
sessions.put(jingle.getSid(), session);
|
|
||||||
JingleManager.getInstanceFor(connection()).registerJingleSessionHandler(jingle.getInitiator(), session.getSid(), this);
|
|
||||||
|
|
||||||
for (IncomingJingleFileTransferListener l : incomingJingleFileTransferListeners) {
|
for (IncomingJingleFileTransferListener l : incomingJingleFileTransferListeners) {
|
||||||
l.onIncomingJingleFileTransfer(jingle, new IncomingJingleFileTransferCallback() {
|
l.onIncomingJingleFileTransfer(jingle, callback);
|
||||||
@Override
|
|
||||||
public void acceptFileTransfer(final File target) throws SmackException.NotConnectedException, InterruptedException {
|
|
||||||
|
|
||||||
InBandBytestreamManager.getByteStreamManager(connection()).addIncomingBytestreamListener(new InBandBytestreamListener() {
|
|
||||||
@Override
|
|
||||||
public void incomingBytestreamRequest(InBandBytestreamRequest request) {
|
|
||||||
try {
|
|
||||||
if (!target.exists()) {
|
|
||||||
target.createNewFile();
|
|
||||||
}
|
|
||||||
JingleFileTransferChildElement payload = (JingleFileTransferChildElement) jingle.getContents().get(0).getDescription().getJingleContentDescriptionChildren().get(0);
|
|
||||||
JingleInBandByteStreamTransport transport = (JingleInBandByteStreamTransport) jingle.getContents().get(0).getJingleTransports().get(0);
|
|
||||||
int s = payload.getSize();
|
|
||||||
int bs = transport.getBlockSize();
|
|
||||||
byte[] recv = new byte[s];
|
|
||||||
|
|
||||||
FileOutputStream o = new FileOutputStream(target);
|
|
||||||
InBandBytestreamSession ibs = request.accept();
|
|
||||||
InputStream i = ibs.getInputStream();
|
|
||||||
int read = 0;
|
|
||||||
int count = 0;
|
|
||||||
while (read > -1 && read < s) {
|
|
||||||
byte[] buf = new byte[bs];
|
|
||||||
int r = i.read(buf);
|
|
||||||
read += r;
|
|
||||||
|
|
||||||
LOGGER.log(Level.INFO, "Read " + r + " (" + read + ") bytes of " + s + " (" + count + " of size " + bs + ")");
|
|
||||||
System.arraycopy(buf, 0, recv,bs * count, r);
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
i.close();
|
|
||||||
o.write(recv);
|
|
||||||
o.close();
|
|
||||||
} catch (IOException | SmackException.NotConnectedException | InterruptedException e) {
|
|
||||||
LOGGER.log(Level.SEVERE, e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Jingle.Builder jb = Jingle.getBuilder();
|
|
||||||
jb.setAction(JingleAction.session_accept)
|
|
||||||
.setSessionId(jingle.getSid())
|
|
||||||
.setInitiator(jingle.getInitiator())
|
|
||||||
.setResponder(jingle.getResponder())
|
|
||||||
.addJingleContent(jingle.getContents().get(0));
|
|
||||||
Jingle j = jb.build();
|
|
||||||
j.setTo(jingle.getFrom());
|
|
||||||
j.setType(IQ.Type.set);
|
|
||||||
connection().sendStanza(j);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void cancelFileTransfer() throws SmackException.NotConnectedException, InterruptedException {
|
|
||||||
Jingle.Builder jb = Jingle.getBuilder();
|
|
||||||
jb.setInitiator(jingle.getInitiator())
|
|
||||||
.setResponder(jingle.getResponder())
|
|
||||||
.setSessionId(jingle.getSid())
|
|
||||||
.setAction(JingleAction.session_terminate)
|
|
||||||
.setReason(JingleReason.Reason.decline);
|
|
||||||
connection().sendStanza(jb.build());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return IQ.createResultIQ(jingle);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IQ handleJingleSessionRequest(Jingle jingle, String sessionId) {
|
|
||||||
LOGGER.log(Level.INFO, "handleJingleSessionRequest");
|
|
||||||
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
|
|
||||||
switch (jingle.getAction()) {
|
|
||||||
case session_initiate:
|
|
||||||
throw new AssertionError("Session is already initiated.");
|
|
||||||
case session_accept:
|
|
||||||
try {
|
|
||||||
connection().sendStanza(IQ.createResultIQ(jingle));
|
|
||||||
} catch (SmackException.NotConnectedException | InterruptedException e) {
|
|
||||||
LOGGER.log(Level.SEVERE, e.getMessage(), e);
|
|
||||||
}
|
|
||||||
LOGGER.log(Level.INFO, "Received session-accept");
|
|
||||||
// Remote accepts our session-initiate
|
|
||||||
InBandBytestreamManager ibm = InBandBytestreamManager.getByteStreamManager(connection());
|
|
||||||
ibm.setMaximumBlockSize(4096);
|
|
||||||
InBandBytestreamSession ibs;
|
|
||||||
try {
|
|
||||||
ibs = ibm.establishSession(jingle.getResponder(), sessionId);
|
|
||||||
} catch (SmackException.NoResponseException | InterruptedException | SmackException.NotConnectedException | XMPPException.XMPPErrorException e) {
|
|
||||||
LOGGER.log(Level.SEVERE, "Fail in handle request: " + e, e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
LOGGER.log(Level.INFO, "Writing bytes...");
|
|
||||||
OutgoingJingleFileTransferSession outgoing = (OutgoingJingleFileTransferSession) session;
|
|
||||||
ibs.getOutputStream().write(outgoing.getBytes());
|
|
||||||
ibs.close();
|
|
||||||
LOGGER.log(Level.INFO, "Bytes written.");
|
|
||||||
} catch (IOException e) {
|
|
||||||
LOGGER.log(Level.SEVERE, "Fail while writing: " + e, e);
|
|
||||||
}
|
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -286,42 +113,40 @@ public final class JingleFileTransferManager extends Manager implements JingleHa
|
||||||
* @param file
|
* @param file
|
||||||
*/
|
*/
|
||||||
public void sendFile(File file, final FullJid recipient) throws IOException, SmackException.NotConnectedException, InterruptedException {
|
public void sendFile(File file, final FullJid recipient) throws IOException, SmackException.NotConnectedException, InterruptedException {
|
||||||
final byte[] bytes = new byte[(int) file.length()];
|
JingleFileTransferSession session = new JingleFileTransferSession(connection(), recipient, connection().getUser(), recipient);
|
||||||
HashElement hashElement = FileAndHashReader.readAndCalculateHash(file, bytes, HashManager.ALGORITHM.SHA_256);
|
JingleFileTransferChildElement.Builder b = JingleFileTransferChildElement.getBuilder();
|
||||||
Date lastModified = new Date(file.lastModified());
|
b.setFile(file);
|
||||||
JingleFileTransferChildElement payload = new JingleFileTransferChildElement(
|
byte[] buf = new byte[(int) file.length()];
|
||||||
lastModified, "A file", hashElement,
|
HashElement hashElement = FileAndHashReader.readAndCalculateHash(file, buf, HashManager.ALGORITHM.SHA_256);
|
||||||
"application/octet-stream", file.getName(), (int) file.length(), null);
|
b.setHash(hashElement);
|
||||||
ArrayList<JingleContentDescriptionChildElement> payloadTypes = new ArrayList<>();
|
b.setDescription("File");
|
||||||
payloadTypes.add(payload);
|
b.setMediaType("text/plain");
|
||||||
|
|
||||||
JingleFileTransferContentDescription descriptionFileTransfer = new JingleFileTransferContentDescription(payloadTypes);
|
session.setBytes(buf);
|
||||||
final JingleInBandByteStreamTransport transport = new JingleInBandByteStreamTransport();
|
JingleManager.getInstanceFor(connection()).registerJingleSession(session);
|
||||||
JingleContent.Builder cb = JingleContent.getBuilder();
|
|
||||||
cb.setDescription(descriptionFileTransfer)
|
ArrayList<JingleContentDescriptionChildElement> payloads = new ArrayList<>();
|
||||||
.addTransport(transport)
|
payloads.add(b.build());
|
||||||
|
|
||||||
|
JingleContent.Builder bb = JingleContent.getBuilder();
|
||||||
|
bb.setDescription(new JingleFileTransferContentDescription(payloads))
|
||||||
.setCreator(JingleContent.Creator.initiator)
|
.setCreator(JingleContent.Creator.initiator)
|
||||||
.setSenders(JingleContent.Senders.initiator)
|
.setName(StringUtils.randomString(24))
|
||||||
.setName("file");
|
.addTransport(new JingleInBandBytestreamTransport());
|
||||||
JingleContent content = cb.build();
|
|
||||||
|
|
||||||
final String sid = JingleInBandByteStreamManager.generateSessionId();
|
Jingle jingle = (Jingle) session.initiate(Collections.singletonList(bb.build()));
|
||||||
|
|
||||||
Jingle.Builder jb = Jingle.getBuilder();
|
|
||||||
jb.setInitiator(connection().getUser())
|
|
||||||
.setResponder(recipient)
|
|
||||||
.setAction(JingleAction.session_initiate)
|
|
||||||
.addJingleContent(content)
|
|
||||||
.setSessionId(sid);
|
|
||||||
Jingle jingle = jb.build();
|
|
||||||
jingle.setTo(recipient);
|
jingle.setTo(recipient);
|
||||||
jingle.setType(IQ.Type.set);
|
|
||||||
|
|
||||||
OutgoingJingleFileTransferSession session = new OutgoingJingleFileTransferSession(jingle);
|
|
||||||
session.setBytes(bytes);
|
|
||||||
sessions.put(sid, session);
|
|
||||||
JingleManager.getInstanceFor(connection()).registerJingleSessionHandler(jingle.getResponder(), session.getSid(), this);
|
|
||||||
|
|
||||||
connection().sendStanza(jingle);
|
connection().sendStanza(jingle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FullJid ourJid() {
|
||||||
|
return connection().getUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IQ handleJingleRequest(Jingle jingle) {
|
||||||
|
JingleFileTransferSession session = new JingleFileTransferSession(connection(), jingle);
|
||||||
|
JingleManager.getInstanceFor(connection()).registerJingleSession(session);
|
||||||
|
return session.handleRequest(jingle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,145 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.io.OutputStream;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.SmackException;
|
||||||
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
|
import org.jivesoftware.smackx.jingle.JingleInputStream;
|
||||||
|
import org.jivesoftware.smackx.jingle.JingleSession;
|
||||||
|
import org.jivesoftware.smackx.jingle.JingleTransportInputStreamCallback;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.Jingle;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.callback.IncomingJingleFileTransferCallback;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferChildElement;
|
||||||
|
import org.jivesoftware.smackx.jingle_ibb.JingleInBandBytestreamTransportManager;
|
||||||
|
import org.jxmpp.jid.FullJid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represent a jingle file transfer session.
|
||||||
|
*/
|
||||||
|
public class JingleFileTransferSession extends JingleSession {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(JingleFileTransferSession.class.getName());
|
||||||
|
|
||||||
|
private byte[] buffer;
|
||||||
|
|
||||||
|
public JingleFileTransferSession(XMPPConnection connection, FullJid remote, FullJid initiator, FullJid responder, String sid) {
|
||||||
|
super(connection, remote, initiator, responder, sid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleFileTransferSession(XMPPConnection connection, FullJid remote, FullJid initiator, FullJid responder) {
|
||||||
|
super(connection, remote, initiator, responder);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleFileTransferSession(XMPPConnection connection, Jingle initiate) {
|
||||||
|
super(connection, initiate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBytes(byte[] bytes) {
|
||||||
|
this.buffer = bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getBytes() {
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSessionInitiate(final Jingle jingle) {
|
||||||
|
JingleFileTransferManager jfm = JingleFileTransferManager.getInstanceFor(connection);
|
||||||
|
jfm.notifyIncomingFileTransferListeners(jingle, new IncomingJingleFileTransferCallback() {
|
||||||
|
@Override
|
||||||
|
public void acceptFileTransfer(final File target) throws SmackException.NotConnectedException, InterruptedException {
|
||||||
|
connection.sendStanza(accept(jingle));
|
||||||
|
JingleInBandBytestreamTransportManager.getInstanceFor(connection).acceptInputStream(jingle, new JingleTransportInputStreamCallback() {
|
||||||
|
@Override
|
||||||
|
public void onInputStream(JingleInputStream inputStream) {
|
||||||
|
receive(inputStream, target);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancelFileTransfer() throws SmackException.NotConnectedException, InterruptedException {
|
||||||
|
connection.sendStanza(terminateFormally());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAccept(Jingle jingle) {
|
||||||
|
this.contents = jingle.getContents();
|
||||||
|
JingleInBandBytestreamTransportManager jibb = JingleInBandBytestreamTransportManager.getInstanceFor(connection);
|
||||||
|
OutputStream outputStream = jibb.createOutputStream(jingle);
|
||||||
|
|
||||||
|
if (outputStream == null) {
|
||||||
|
LOGGER.log(Level.SEVERE, "OutputStream is null!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
send(outputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
void send(OutputStream outputStream) {
|
||||||
|
try {
|
||||||
|
outputStream.write(buffer);
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Caught exception while writing to output stream: " + e, e);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
outputStream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Could not close output stream: " + e, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void receive(JingleInputStream in, File file) {
|
||||||
|
JingleFileTransferChildElement payload = (JingleFileTransferChildElement) contents.get(0).getDescription().getJingleContentDescriptionChildren().get(0);
|
||||||
|
InputStream inputStream = in.getInputStream();
|
||||||
|
byte[] fileBuffer = new byte[payload.getSize()];
|
||||||
|
byte[] packetBuffer = new byte[in.getBlockSize()];
|
||||||
|
|
||||||
|
try {
|
||||||
|
FileOutputStream outputStream = new FileOutputStream(file);
|
||||||
|
int read = 0, count = 0;
|
||||||
|
while (read > -1 && read < fileBuffer.length) {
|
||||||
|
int r = inputStream.read(packetBuffer);
|
||||||
|
read += r;
|
||||||
|
System.arraycopy(packetBuffer, 0, fileBuffer, packetBuffer.length * count, r);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
inputStream.close();
|
||||||
|
outputStream.write(fileBuffer);
|
||||||
|
outputStream.close();
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Caught exception while receiving and writing file: " + e, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTerminate(Jingle jingle) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
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.util.StringUtils;
|
||||||
|
import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamListener;
|
||||||
|
import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager;
|
||||||
|
import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamRequest;
|
||||||
|
import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamSession;
|
||||||
|
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
||||||
|
import org.jivesoftware.smackx.jingle.JingleContentTransportManager;
|
||||||
|
import org.jivesoftware.smackx.jingle.JingleInputStream;
|
||||||
|
import org.jivesoftware.smackx.jingle.JingleTransportInputStreamCallback;
|
||||||
|
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.provider.JingleContentProviderManager;
|
||||||
|
import org.jivesoftware.smackx.jingle_ibb.element.JingleInBandBytestreamTransport;
|
||||||
|
import org.jivesoftware.smackx.jingle_ibb.provider.JingleInBandByteStreamTransportProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manager for Jingle In-Band-Bytestreams.
|
||||||
|
*/
|
||||||
|
public final class JingleInBandBytestreamTransportManager extends Manager implements JingleContentTransportManager {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(JingleInBandBytestreamTransportManager.class.getName());
|
||||||
|
public static final String NAMESPACE_V1 = "urn:xmpp:jingle:transports:ibb:1";
|
||||||
|
|
||||||
|
private static final WeakHashMap<XMPPConnection, JingleInBandBytestreamTransportManager> INSTANCES = new WeakHashMap<>();
|
||||||
|
|
||||||
|
private JingleInBandBytestreamTransportManager(XMPPConnection connection) {
|
||||||
|
super(connection);
|
||||||
|
JingleContentProviderManager.addJingleContentTransportProvider(NAMESPACE_V1, new JingleInBandByteStreamTransportProvider());
|
||||||
|
ServiceDiscoveryManager.getInstanceFor(connection).addFeature(NAMESPACE_V1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JingleInBandBytestreamTransportManager getInstanceFor(XMPPConnection connection) {
|
||||||
|
JingleInBandBytestreamTransportManager manager = INSTANCES.get(connection);
|
||||||
|
if (manager == null) {
|
||||||
|
manager = new JingleInBandBytestreamTransportManager(connection);
|
||||||
|
INSTANCES.put(connection, manager);
|
||||||
|
}
|
||||||
|
return manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void acceptInputStream(final Jingle jingle, final JingleTransportInputStreamCallback callback) {
|
||||||
|
final int blockSize = ((JingleInBandBytestreamTransport)
|
||||||
|
jingle.getContents().get(0).getJingleTransports().get(0)).getBlockSize();
|
||||||
|
InBandBytestreamListener bytestreamListener = new InBandBytestreamListener() {
|
||||||
|
@Override
|
||||||
|
public void incomingBytestreamRequest(InBandBytestreamRequest request) {
|
||||||
|
if (request.getSessionID().equals(jingle.getSid())) {
|
||||||
|
try {
|
||||||
|
InBandBytestreamSession ibs = request.accept();
|
||||||
|
InputStream inputStream = ibs.getInputStream();
|
||||||
|
callback.onInputStream(new JingleInputStream(inputStream, blockSize));
|
||||||
|
} catch (SmackException.NotConnectedException | InterruptedException e) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Could not accept IBB session: " + e, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
InBandBytestreamManager.getByteStreamManager(connection())
|
||||||
|
.addIncomingBytestreamListener(bytestreamListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OutputStream createOutputStream(Jingle jingle) {
|
||||||
|
JingleInBandBytestreamTransport transport = null;
|
||||||
|
JingleContent content = jingle.getContents().get(0);
|
||||||
|
for (JingleContentTransport t : content.getJingleTransports()) {
|
||||||
|
if (t.getNamespace().equals(NAMESPACE_V1)) {
|
||||||
|
transport = (JingleInBandBytestreamTransport) t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transport == null) {
|
||||||
|
//TODO: Transport-failed
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
InBandBytestreamManager ibm = InBandBytestreamManager.getByteStreamManager(connection());
|
||||||
|
ibm.setMaximumBlockSize(transport.getBlockSize());
|
||||||
|
InBandBytestreamSession ibs;
|
||||||
|
try {
|
||||||
|
ibs = ibm.establishSession(jingle.getFrom(), jingle.getSid());
|
||||||
|
} catch (SmackException.NoResponseException | InterruptedException | SmackException.NotConnectedException | XMPPException.XMPPErrorException e) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Fail in handle request: " + e, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ibs.getOutputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a random session id.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String generateSessionId() {
|
||||||
|
return StringUtils.randomString(24);
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,12 +18,12 @@ package org.jivesoftware.smackx.jingle_ibb.element;
|
||||||
|
|
||||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleContentTransport;
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransport;
|
||||||
import org.jivesoftware.smackx.jingle_ibb.JingleInBandByteStreamManager;
|
import org.jivesoftware.smackx.jingle_ibb.JingleInBandBytestreamTransportManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Jingle In-Band-ByteStream transport.
|
* Jingle In-Band-ByteStream transport.
|
||||||
*/
|
*/
|
||||||
public class JingleInBandByteStreamTransport extends JingleContentTransport {
|
public class JingleInBandBytestreamTransport extends JingleContentTransport {
|
||||||
|
|
||||||
public static final String ATTR_BLOCK_SIZE = "block-size";
|
public static final String ATTR_BLOCK_SIZE = "block-size";
|
||||||
public static final String ATTR_SID = "sid";
|
public static final String ATTR_SID = "sid";
|
||||||
|
@ -32,15 +32,15 @@ public class JingleInBandByteStreamTransport extends JingleContentTransport {
|
||||||
private final short blockSize;
|
private final short blockSize;
|
||||||
private final String sid;
|
private final String sid;
|
||||||
|
|
||||||
public JingleInBandByteStreamTransport() {
|
public JingleInBandBytestreamTransport() {
|
||||||
this(DEFAULT_BLOCK_SIZE);
|
this(DEFAULT_BLOCK_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public JingleInBandByteStreamTransport(short blockSize) {
|
public JingleInBandBytestreamTransport(short blockSize) {
|
||||||
this(blockSize, JingleInBandByteStreamManager.generateSessionId());
|
this(blockSize, JingleInBandBytestreamTransportManager.generateSessionId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public JingleInBandByteStreamTransport(short blockSize, String sid) {
|
public JingleInBandBytestreamTransport(short blockSize, String sid) {
|
||||||
super(null);
|
super(null);
|
||||||
if (blockSize > 0) {
|
if (blockSize > 0) {
|
||||||
this.blockSize = blockSize;
|
this.blockSize = blockSize;
|
||||||
|
@ -66,12 +66,12 @@ public class JingleInBandByteStreamTransport extends JingleContentTransport {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getNamespace() {
|
public String getNamespace() {
|
||||||
return JingleInBandByteStreamManager.NAMESPACE_V1;
|
return JingleInBandBytestreamTransportManager.NAMESPACE_V1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object other) {
|
public boolean equals(Object other) {
|
||||||
if (other == null || !(other instanceof JingleInBandByteStreamTransport)) {
|
if (other == null || !(other instanceof JingleInBandBytestreamTransport)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,23 +17,23 @@
|
||||||
package org.jivesoftware.smackx.jingle_ibb.provider;
|
package org.jivesoftware.smackx.jingle_ibb.provider;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.jingle.provider.JingleContentTransportProvider;
|
import org.jivesoftware.smackx.jingle.provider.JingleContentTransportProvider;
|
||||||
import org.jivesoftware.smackx.jingle_ibb.element.JingleInBandByteStreamTransport;
|
import org.jivesoftware.smackx.jingle_ibb.element.JingleInBandBytestreamTransport;
|
||||||
import org.xmlpull.v1.XmlPullParser;
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse JingleByteStreamTransport elements.
|
* Parse JingleByteStreamTransport elements.
|
||||||
*/
|
*/
|
||||||
public class JingleInBandByteStreamTransportProvider extends JingleContentTransportProvider<JingleInBandByteStreamTransport> {
|
public class JingleInBandByteStreamTransportProvider extends JingleContentTransportProvider<JingleInBandBytestreamTransport> {
|
||||||
@Override
|
@Override
|
||||||
public JingleInBandByteStreamTransport parse(XmlPullParser parser, int initialDepth) throws Exception {
|
public JingleInBandBytestreamTransport parse(XmlPullParser parser, int initialDepth) throws Exception {
|
||||||
String blockSizeString = parser.getAttributeValue(null, JingleInBandByteStreamTransport.ATTR_BLOCK_SIZE);
|
String blockSizeString = parser.getAttributeValue(null, JingleInBandBytestreamTransport.ATTR_BLOCK_SIZE);
|
||||||
String sid = parser.getAttributeValue(null, JingleInBandByteStreamTransport.ATTR_SID);
|
String sid = parser.getAttributeValue(null, JingleInBandBytestreamTransport.ATTR_SID);
|
||||||
|
|
||||||
short blockSize = -1;
|
short blockSize = -1;
|
||||||
if (blockSizeString != null) {
|
if (blockSizeString != null) {
|
||||||
blockSize = Short.valueOf(blockSizeString);
|
blockSize = Short.valueOf(blockSizeString);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new JingleInBandByteStreamTransport(blockSize, sid);
|
return new JingleInBandBytestreamTransport(blockSize, sid);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -16,17 +16,17 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.jingle_ibb;
|
package org.jivesoftware.smackx.jingle_ibb;
|
||||||
|
|
||||||
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
|
||||||
import org.jivesoftware.smack.test.util.TestUtils;
|
|
||||||
import org.jivesoftware.smackx.jingle_ibb.element.JingleInBandByteStreamTransport;
|
|
||||||
import org.jivesoftware.smackx.jingle_ibb.provider.JingleInBandByteStreamTransportProvider;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import static junit.framework.TestCase.assertEquals;
|
import static junit.framework.TestCase.assertEquals;
|
||||||
import static junit.framework.TestCase.assertFalse;
|
import static junit.framework.TestCase.assertFalse;
|
||||||
import static junit.framework.TestCase.assertNotSame;
|
import static junit.framework.TestCase.assertNotSame;
|
||||||
import static junit.framework.TestCase.assertTrue;
|
import static junit.framework.TestCase.assertTrue;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
||||||
|
import org.jivesoftware.smack.test.util.TestUtils;
|
||||||
|
import org.jivesoftware.smackx.jingle_ibb.element.JingleInBandBytestreamTransport;
|
||||||
|
import org.jivesoftware.smackx.jingle_ibb.provider.JingleInBandByteStreamTransportProvider;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test JingleInBandByteStreamTransport provider and element.
|
* Test JingleInBandByteStreamTransport provider and element.
|
||||||
*/
|
*/
|
||||||
|
@ -34,37 +34,37 @@ public class JingleInBandByteStreamTransportTest extends SmackTestSuite {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void parserTest() throws Exception {
|
public void parserTest() throws Exception {
|
||||||
String sid = JingleInBandByteStreamManager.generateSessionId();
|
String sid = JingleInBandBytestreamTransportManager.generateSessionId();
|
||||||
short size = 8192;
|
short size = 8192;
|
||||||
|
|
||||||
String xml = "<transport xmlns='urn:xmpp:jingle:transports:ibb:1' block-size='8192' sid='" + sid + "'/>";
|
String xml = "<transport xmlns='urn:xmpp:jingle:transports:ibb:1' block-size='8192' sid='" + sid + "'/>";
|
||||||
|
|
||||||
JingleInBandByteStreamTransport transport = new JingleInBandByteStreamTransport(size, sid);
|
JingleInBandBytestreamTransport transport = new JingleInBandBytestreamTransport(size, sid);
|
||||||
assertEquals(xml, transport.toXML().toString());
|
assertEquals(xml, transport.toXML().toString());
|
||||||
assertEquals(size, transport.getBlockSize());
|
assertEquals(size, transport.getBlockSize());
|
||||||
assertEquals(sid, transport.getSessionId());
|
assertEquals(sid, transport.getSessionId());
|
||||||
|
|
||||||
JingleInBandByteStreamTransport parsed = new JingleInBandByteStreamTransportProvider()
|
JingleInBandBytestreamTransport parsed = new JingleInBandByteStreamTransportProvider()
|
||||||
.parse(TestUtils.getParser(xml));
|
.parse(TestUtils.getParser(xml));
|
||||||
assertEquals(transport, parsed);
|
assertEquals(transport, parsed);
|
||||||
assertTrue(transport.equals(parsed));
|
assertTrue(transport.equals(parsed));
|
||||||
assertEquals(xml, parsed.toXML().toString());
|
assertEquals(xml, parsed.toXML().toString());
|
||||||
|
|
||||||
JingleInBandByteStreamTransport transport1 = new JingleInBandByteStreamTransport((short) 1024);
|
JingleInBandBytestreamTransport transport1 = new JingleInBandBytestreamTransport((short) 1024);
|
||||||
assertEquals((short) 1024, transport1.getBlockSize());
|
assertEquals((short) 1024, transport1.getBlockSize());
|
||||||
assertNotSame(transport, transport1);
|
assertNotSame(transport, transport1);
|
||||||
assertNotSame(transport.getSessionId(), transport1.getSessionId());
|
assertNotSame(transport.getSessionId(), transport1.getSessionId());
|
||||||
|
|
||||||
assertFalse(transport.equals(null));
|
assertFalse(transport.equals(null));
|
||||||
|
|
||||||
JingleInBandByteStreamTransport transport2 = new JingleInBandByteStreamTransport();
|
JingleInBandBytestreamTransport transport2 = new JingleInBandBytestreamTransport();
|
||||||
assertEquals(JingleInBandByteStreamTransport.DEFAULT_BLOCK_SIZE, transport2.getBlockSize());
|
assertEquals(JingleInBandBytestreamTransport.DEFAULT_BLOCK_SIZE, transport2.getBlockSize());
|
||||||
assertFalse(transport1.equals(transport2));
|
assertFalse(transport1.equals(transport2));
|
||||||
|
|
||||||
JingleInBandByteStreamTransport transport3 = new JingleInBandByteStreamTransport((short) -1024);
|
JingleInBandBytestreamTransport transport3 = new JingleInBandBytestreamTransport((short) -1024);
|
||||||
assertEquals(JingleInBandByteStreamTransport.DEFAULT_BLOCK_SIZE, transport3.getBlockSize());
|
assertEquals(JingleInBandBytestreamTransport.DEFAULT_BLOCK_SIZE, transport3.getBlockSize());
|
||||||
|
|
||||||
assertEquals(transport3.getNamespace(), JingleInBandByteStreamManager.NAMESPACE_V1);
|
assertEquals(transport3.getNamespace(), JingleInBandBytestreamTransportManager.NAMESPACE_V1);
|
||||||
assertEquals(transport3.getElementName(), "transport");
|
assertEquals(transport3.getElementName(), "transport");
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -14,27 +14,18 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.jingle_filetransfer;
|
package org.jivesoftware.smackx.jingle;
|
||||||
|
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.jingle.JingleSession;
|
|
||||||
import org.jivesoftware.smackx.jingle.element.Jingle;
|
import org.jivesoftware.smackx.jingle.element.Jingle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by vanitas on 04.06.17.
|
* Interface with methods that JingleContentTransportManagers must implement.
|
||||||
*/
|
*/
|
||||||
public class OutgoingJingleFileTransferSession extends JingleSession {
|
public interface JingleContentTransportManager {
|
||||||
|
|
||||||
private byte[] bytes;
|
void acceptInputStream(Jingle jingle, JingleTransportInputStreamCallback callback);
|
||||||
|
|
||||||
public OutgoingJingleFileTransferSession(Jingle jingle) {
|
OutputStream createOutputStream(Jingle jingle);
|
||||||
super(jingle.getInitiator(), jingle.getResponder(), jingle.getSid());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBytes(byte[] bytes) {
|
|
||||||
this.bytes = bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getBytes() {
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by vanitas on 06.06.17.
|
||||||
|
*/
|
||||||
|
public class JingleInputStream {
|
||||||
|
private final InputStream inputStream;
|
||||||
|
private final int blockSize;
|
||||||
|
|
||||||
|
public JingleInputStream(InputStream inputStream, int blockSize) {
|
||||||
|
this.inputStream = inputStream;
|
||||||
|
this.blockSize = blockSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputStream getInputStream() {
|
||||||
|
return inputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBlockSize() {
|
||||||
|
return blockSize;
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,10 +16,11 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.jingle;
|
package org.jivesoftware.smackx.jingle;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.jivesoftware.smack.Manager;
|
import org.jivesoftware.smack.Manager;
|
||||||
|
@ -28,11 +29,13 @@ import org.jivesoftware.smack.iqrequest.AbstractIqRequestHandler;
|
||||||
import org.jivesoftware.smack.iqrequest.IQRequestHandler.Mode;
|
import org.jivesoftware.smack.iqrequest.IQRequestHandler.Mode;
|
||||||
import org.jivesoftware.smack.packet.IQ;
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
import org.jivesoftware.smack.packet.IQ.Type;
|
import org.jivesoftware.smack.packet.IQ.Type;
|
||||||
|
import org.jivesoftware.smack.packet.XMPPError;
|
||||||
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.JingleAction;
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleContent;
|
import org.jivesoftware.smackx.jingle.element.JingleContent;
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleContentDescription;
|
import org.jivesoftware.smackx.jingle.element.JingleContentDescription;
|
||||||
import org.jivesoftware.smackx.jingle_ibb.JingleInBandByteStreamManager;
|
import org.jivesoftware.smackx.jingle.element.JingleError;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleReason;
|
||||||
import org.jxmpp.jid.FullJid;
|
import org.jxmpp.jid.FullJid;
|
||||||
import org.jxmpp.jid.Jid;
|
import org.jxmpp.jid.Jid;
|
||||||
|
|
||||||
|
@ -52,66 +55,75 @@ public final class JingleManager extends Manager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Map<String, JingleHandler> descriptionHandlers = new ConcurrentHashMap<>();
|
private final Map<String, JingleHandler> descriptionHandlers = new ConcurrentHashMap<>();
|
||||||
|
private final Map<FullJidAndSessionId, JingleSession> jingleSessions = new ConcurrentHashMap<>();
|
||||||
|
private final Map<String, JingleContentTransportManager> transportManagers = new HashMap<>();
|
||||||
|
|
||||||
private final Map<FullJidAndSessionId, JingleSessionHandler> jingleSessionHandlers = new ConcurrentHashMap<>();
|
private JingleManager(final XMPPConnection connection) {
|
||||||
|
|
||||||
private JingleManager(XMPPConnection connection) {
|
|
||||||
super(connection);
|
super(connection);
|
||||||
|
|
||||||
connection.registerIQRequestHandler(
|
connection.registerIQRequestHandler(
|
||||||
new AbstractIqRequestHandler(Jingle.ELEMENT, Jingle.NAMESPACE, Type.set, Mode.async) {
|
new AbstractIqRequestHandler(Jingle.ELEMENT, Jingle.NAMESPACE, Type.set, Mode.async) {
|
||||||
@Override
|
@Override
|
||||||
public IQ handleIQRequest(IQ iqRequest) {
|
public IQ handleIQRequest(IQ iqRequest) {
|
||||||
LOGGER.log(Level.INFO, "handleIQRequest");
|
final Jingle jingle = (Jingle) iqRequest;
|
||||||
final Jingle jingle = (Jingle) iqRequest;
|
|
||||||
|
|
||||||
if (jingle.getAction() != JingleAction.session_initiate) {
|
if (jingle.getAction() != JingleAction.session_initiate) {
|
||||||
Jid from = jingle.getFrom();
|
|
||||||
assert (from != null);
|
|
||||||
FullJid fullFrom = from.asFullJidOrThrow();
|
|
||||||
String sid = jingle.getSid();
|
|
||||||
FullJidAndSessionId fullJidAndSessionId = new FullJidAndSessionId(fullFrom, sid);
|
|
||||||
JingleSessionHandler jingleSessionHandler = jingleSessionHandlers.get(fullJidAndSessionId);
|
|
||||||
if (jingleSessionHandler == null) {
|
|
||||||
// TODO handle non existing jingle session handler.
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return jingleSessionHandler.handleJingleSessionRequest(jingle, sid);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jingle.getContents().size() > 1) {
|
Jid from = jingle.getFrom();
|
||||||
LOGGER.severe("Jingle IQs with more then one content element are currently not supported by Smack");
|
assert (from != null);
|
||||||
return null;
|
FullJid fullFrom = from.asFullJidOrThrow();
|
||||||
}
|
FullJidAndSessionId fullJidAndSessionId = new FullJidAndSessionId(fullFrom, jingle.getSid());
|
||||||
|
JingleSession jingleSession = jingleSessions.get(fullJidAndSessionId);
|
||||||
|
|
||||||
JingleContent content = jingle.getContents().get(0);
|
if (jingleSession == null) {
|
||||||
JingleContentDescription description = content.getDescription();
|
// Handle unknown session (XEP-0166 §10)
|
||||||
JingleHandler jingleDescriptionHandler = descriptionHandlers.get(
|
XMPPError.Builder errorBuilder = XMPPError.getBuilder();
|
||||||
description.getNamespace());
|
errorBuilder.setCondition(XMPPError.Condition.item_not_found)
|
||||||
if (jingleDescriptionHandler == null) {
|
.addExtension(JingleError.UNKNOWN_SESSION);
|
||||||
// TODO handle non existing content description handler.
|
return IQ.createErrorResponse(jingle, errorBuilder);
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return jingleDescriptionHandler.handleJingleRequest(jingle);
|
|
||||||
}
|
}
|
||||||
});
|
return jingleSession.handleRequest(jingle);
|
||||||
JingleInBandByteStreamManager.getInstanceFor(connection);
|
}
|
||||||
|
|
||||||
|
if (jingle.getContents().size() > 1) {
|
||||||
|
LOGGER.severe("Jingle IQs with more then one content element are currently not supported by Smack");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
JingleContent content = jingle.getContents().get(0);
|
||||||
|
JingleContentDescription description = content.getDescription();
|
||||||
|
JingleHandler jingleDescriptionHandler = descriptionHandlers.get(
|
||||||
|
description.getNamespace());
|
||||||
|
if (jingleDescriptionHandler == null) {
|
||||||
|
//Unsupported Application
|
||||||
|
Jingle.Builder builder = Jingle.getBuilder();
|
||||||
|
builder.setAction(JingleAction.session_terminate)
|
||||||
|
.setSessionId(jingle.getSid())
|
||||||
|
.setReason(JingleReason.Reason.unsupported_applications);
|
||||||
|
Jingle response = builder.build();
|
||||||
|
response.setTo(jingle.getFrom());
|
||||||
|
response.setFrom(connection.getUser());
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
return jingleDescriptionHandler.handleJingleRequest(jingle);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerJingleSession(JingleSession session) {
|
||||||
|
FullJidAndSessionId jidAndSid = new FullJidAndSessionId(session.getRemote(), session.getSid());
|
||||||
|
jingleSessions.put(jidAndSid, session);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unregisterJingleSession(JingleSession session) {
|
||||||
|
jingleSessions.values().removeAll(Collections.singleton(session));
|
||||||
}
|
}
|
||||||
|
|
||||||
public JingleHandler registerDescriptionHandler(String namespace, JingleHandler handler) {
|
public JingleHandler registerDescriptionHandler(String namespace, JingleHandler handler) {
|
||||||
return descriptionHandlers.put(namespace, handler);
|
return descriptionHandlers.put(namespace, handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
public JingleSessionHandler registerJingleSessionHandler(FullJid otherJid, String sessionId, JingleSessionHandler sessionHandler) {
|
|
||||||
FullJidAndSessionId fullJidAndSessionId = new FullJidAndSessionId(otherJid, sessionId);
|
|
||||||
return jingleSessionHandlers.put(fullJidAndSessionId, sessionHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
public JingleSessionHandler unregisterJingleSessionHandler(FullJid otherJid, String sessionId, JingleSessionHandler sessionHandler) {
|
|
||||||
FullJidAndSessionId fullJidAndSessionId = new FullJidAndSessionId(otherJid, sessionId);
|
|
||||||
return jingleSessionHandlers.remove(fullJidAndSessionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class FullJidAndSessionId {
|
private static final class FullJidAndSessionId {
|
||||||
final FullJid fullJid;
|
final FullJid fullJid;
|
||||||
final String sessionId;
|
final String sessionId;
|
||||||
|
@ -135,7 +147,7 @@ public final class JingleManager extends Manager {
|
||||||
}
|
}
|
||||||
FullJidAndSessionId otherFullJidAndSessionId = (FullJidAndSessionId) other;
|
FullJidAndSessionId otherFullJidAndSessionId = (FullJidAndSessionId) other;
|
||||||
return fullJid.equals(otherFullJidAndSessionId.fullJid)
|
return fullJid.equals(otherFullJidAndSessionId.fullJid)
|
||||||
&& sessionId.equals(otherFullJidAndSessionId.sessionId);
|
&& sessionId.equals(otherFullJidAndSessionId.sessionId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2017 Florian Schmaus
|
* 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.
|
||||||
|
@ -16,80 +16,232 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.jingle;
|
package org.jivesoftware.smackx.jingle;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
import org.jivesoftware.smack.packet.IQ;
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
|
import org.jivesoftware.smack.packet.XMPPError;
|
||||||
|
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.JingleAction;
|
import org.jivesoftware.smackx.jingle.element.JingleAction;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContent;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleError;
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleReason;
|
import org.jivesoftware.smackx.jingle.element.JingleReason;
|
||||||
import org.jxmpp.jid.Jid;
|
import org.jxmpp.jid.FullJid;
|
||||||
|
|
||||||
// TODO: Is this class still required? If not, then remove it.
|
/**
|
||||||
public class JingleSession {
|
* JingleSession.
|
||||||
|
*/
|
||||||
|
public abstract class JingleSession {
|
||||||
|
|
||||||
public enum State {
|
public enum State {
|
||||||
|
fresh,
|
||||||
pending,
|
pending,
|
||||||
active,
|
accepted,
|
||||||
;
|
terminated,
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Jid initiator;
|
protected final XMPPConnection connection;
|
||||||
|
protected final FullJid initiator;
|
||||||
|
protected final FullJid responder;
|
||||||
|
protected final FullJid ourJid;
|
||||||
|
protected final FullJid remote;
|
||||||
|
protected final String sid;
|
||||||
|
protected State sessionState;
|
||||||
|
protected List<JingleContent> contents;
|
||||||
|
|
||||||
private final Jid responder;
|
public JingleSession(XMPPConnection connection, FullJid remote, FullJid initiator, FullJid responder, String sid) {
|
||||||
|
this.connection = connection;
|
||||||
private final String sid;
|
this.ourJid = connection.getUser();
|
||||||
|
this.remote = remote;
|
||||||
private State state = State.pending;
|
|
||||||
|
|
||||||
public JingleSession(Jid initiator, Jid responder, String sid) {
|
|
||||||
this.initiator = initiator;
|
this.initiator = initiator;
|
||||||
this.responder = responder;
|
this.responder = responder;
|
||||||
this.sid = sid;
|
this.sid = sid;
|
||||||
|
this.sessionState = State.fresh;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public JingleSession(XMPPConnection connection, FullJid remote, FullJid initiator, FullJid responder) {
|
||||||
public int hashCode() {
|
this(connection, remote, initiator, responder, StringUtils.randomString(24));
|
||||||
int hashCode = 31 + initiator.hashCode();
|
}
|
||||||
hashCode = 31 * hashCode + responder.hashCode();
|
|
||||||
hashCode = 31 * hashCode + sid.hashCode();
|
public JingleSession(XMPPConnection connection, Jingle initiate) {
|
||||||
return hashCode;
|
if (initiate.getAction() != JingleAction.session_initiate) {
|
||||||
|
throw new AssertionError("Session cannot be created without session-initiate");
|
||||||
|
}
|
||||||
|
this.connection = connection;
|
||||||
|
this.ourJid = connection.getUser();
|
||||||
|
this.remote = initiate.getFrom().asFullJidIfPossible();
|
||||||
|
this.initiator = initiate.getInitiator();
|
||||||
|
this.responder = initiate.getResponder();
|
||||||
|
this.sid = initiate.getSid();
|
||||||
|
this.sessionState = State.fresh;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FullJid getInitiator() {
|
||||||
|
return initiator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FullJid getResponder() {
|
||||||
|
return responder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FullJid getRemote() {
|
||||||
|
return remote;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FullJid getOurJid() {
|
||||||
|
return ourJid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getSid() {
|
public String getSid() {
|
||||||
return sid;
|
return sid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Jid getInitiator() {
|
public State getSessionState() {
|
||||||
return initiator;
|
return sessionState;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Jid getResponder() {
|
public IQ handleRequest(Jingle jingle) {
|
||||||
return responder;
|
switch (jingle.getAction()) {
|
||||||
|
case session_initiate:
|
||||||
|
// Did we already get another session-initiate?
|
||||||
|
if (sessionState != State.fresh) {
|
||||||
|
return outOfOrder(jingle);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Keep local copy of contents
|
||||||
|
contents = jingle.getContents();
|
||||||
|
|
||||||
|
onSessionInitiate(jingle);
|
||||||
|
|
||||||
|
return IQ.createResultIQ(jingle);
|
||||||
|
|
||||||
|
case session_accept:
|
||||||
|
if (sessionState != State.pending) {
|
||||||
|
return outOfOrder(jingle);
|
||||||
|
}
|
||||||
|
|
||||||
|
onAccept(jingle);
|
||||||
|
|
||||||
|
return IQ.createResultIQ(jingle);
|
||||||
|
|
||||||
|
case session_terminate:
|
||||||
|
|
||||||
|
onTerminate(jingle);
|
||||||
|
|
||||||
|
return IQ.createResultIQ(jingle);
|
||||||
|
|
||||||
|
case content_add:
|
||||||
|
//TODO: Inform listeners
|
||||||
|
return IQ.createResultIQ(jingle);
|
||||||
|
|
||||||
|
default: return IQ.createResultIQ(jingle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setState(State state) {
|
public abstract void onSessionInitiate(Jingle jingle);
|
||||||
this.state = state;
|
|
||||||
}
|
|
||||||
|
|
||||||
public State getState() {
|
public abstract void onAccept(Jingle jingle);
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public abstract void onTerminate(Jingle jingle);
|
||||||
public boolean equals(Object other) {
|
|
||||||
if (!(other instanceof JingleSession)) {
|
public IQ initiate(List<JingleContent> contents) {
|
||||||
return false;
|
Jingle.Builder b = Jingle.getBuilder();
|
||||||
|
b.setInitiator(initiator)
|
||||||
|
.setAction(JingleAction.session_initiate)
|
||||||
|
.setSessionId(sid);
|
||||||
|
for (JingleContent c : contents) {
|
||||||
|
b.addJingleContent(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
JingleSession otherJingleSession = (JingleSession) other;
|
Jingle j = b.build();
|
||||||
return initiator.equals(otherJingleSession.initiator) && responder.equals(otherJingleSession.responder)
|
j.setTo(remote);
|
||||||
&& sid.equals(otherJingleSession.sid);
|
j.setFrom(ourJid);
|
||||||
|
this.sessionState = State.pending;
|
||||||
|
return j;
|
||||||
}
|
}
|
||||||
|
|
||||||
IQ terminateSuccessfully() {
|
public IQ accept(Jingle jingle) {
|
||||||
Jingle.Builder builder = Jingle.getBuilder();
|
Jingle.Builder b = Jingle.getBuilder();
|
||||||
builder.setAction(JingleAction.session_terminate);
|
b.setResponder(ourJid)
|
||||||
builder.setSessionId(getSid());
|
.setAction(JingleAction.session_accept)
|
||||||
builder.setReason(JingleReason.Reason.success);
|
.setSessionId(sid);
|
||||||
|
for (JingleContent c : jingle.getContents()) {
|
||||||
|
b.addJingleContent(c);
|
||||||
|
}
|
||||||
|
|
||||||
return builder.build();
|
Jingle j = b.build();
|
||||||
|
j.setTo(remote);
|
||||||
|
j.setFrom(ourJid);
|
||||||
|
this.sessionState = State.accepted;
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ terminate(JingleReason.Reason reason) {
|
||||||
|
Jingle.Builder b = Jingle.getBuilder();
|
||||||
|
b.setAction(JingleAction.session_terminate)
|
||||||
|
.setSessionId(sid)
|
||||||
|
.setReason(reason);
|
||||||
|
Jingle j = b.build();
|
||||||
|
j.setTo(remote);
|
||||||
|
j.setFrom(ourJid);
|
||||||
|
this.sessionState = State.terminated;
|
||||||
|
return b.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ terminateFormally() {
|
||||||
|
return terminate(JingleReason.Reason.decline);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO Fix
|
||||||
|
public IQ terminateAlternativeSession(String alternative) {
|
||||||
|
Jingle.Builder b = Jingle.getBuilder();
|
||||||
|
b.setAction(JingleAction.session_terminate)
|
||||||
|
.setSessionId(sid)
|
||||||
|
.setReason(JingleReason.Reason.alternative_session); //Set alt. sessionId
|
||||||
|
Jingle j = b.build();
|
||||||
|
j.setTo(remote);
|
||||||
|
j.setFrom(ourJid);
|
||||||
|
this.sessionState = State.terminated;
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ terminateSuccessfully() {
|
||||||
|
return terminate(JingleReason.Reason.success);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ terminateBusy() {
|
||||||
|
return terminate(JingleReason.Reason.busy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ terminateUnsupportedTransports() {
|
||||||
|
return terminate(JingleReason.Reason.unsupported_transports);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ terminateFailedTransport() {
|
||||||
|
return terminate(JingleReason.Reason.failed_transport);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ terminateUnsupportedApplications() {
|
||||||
|
return terminate(JingleReason.Reason.unsupported_applications);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ terminateFailedApplication() {
|
||||||
|
return terminate(JingleReason.Reason.failed_application);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ terminateIncompatibleParameters() {
|
||||||
|
return terminate(JingleReason.Reason.incompatible_parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ unknownInitiator(Jingle jingle) {
|
||||||
|
return IQ.createErrorResponse(jingle, XMPPError.Condition.service_unavailable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ outOfOrder(Jingle jingle) {
|
||||||
|
XMPPError.Builder b = XMPPError.getBuilder();
|
||||||
|
b.setCondition(XMPPError.Condition.unexpected_request);
|
||||||
|
b.addExtension(JingleError.OUT_OF_ORDER);
|
||||||
|
return IQ.createErrorResponse(jingle, b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by vanitas on 06.06.17.
|
||||||
|
*/
|
||||||
|
public interface JingleTransportInputStreamCallback {
|
||||||
|
|
||||||
|
void onInputStream(JingleInputStream inputStream);
|
||||||
|
}
|
|
@ -16,12 +16,12 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.jingle.element;
|
package org.jivesoftware.smackx.jingle.element;
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.ExtensionElement;
|
|
||||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||||
|
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A jingle transport extension.
|
* A jingle transport extension.
|
||||||
*
|
*
|
||||||
|
@ -71,5 +71,4 @@ public abstract class JingleContentTransport implements ExtensionElement {
|
||||||
|
|
||||||
return xml;
|
return xml;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,59 +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_ibb;
|
|
||||||
|
|
||||||
import java.util.WeakHashMap;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.Manager;
|
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
|
||||||
import org.jivesoftware.smack.util.StringUtils;
|
|
||||||
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
|
||||||
import org.jivesoftware.smackx.jingle.provider.JingleContentProviderManager;
|
|
||||||
import org.jivesoftware.smackx.jingle_ibb.provider.JingleInBandByteStreamTransportProvider;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Manager for Jingle In-Band-ByteStreams.
|
|
||||||
*/
|
|
||||||
public final class JingleInBandByteStreamManager extends Manager {
|
|
||||||
|
|
||||||
public static final String NAMESPACE_V1 = "urn:xmpp:jingle:transports:ibb:1";
|
|
||||||
|
|
||||||
private static final WeakHashMap<XMPPConnection, JingleInBandByteStreamManager> INSTANCES = new WeakHashMap<>();
|
|
||||||
|
|
||||||
private JingleInBandByteStreamManager(XMPPConnection connection) {
|
|
||||||
super(connection);
|
|
||||||
JingleContentProviderManager.addJingleContentTransportProvider(NAMESPACE_V1, new JingleInBandByteStreamTransportProvider());
|
|
||||||
ServiceDiscoveryManager.getInstanceFor(connection).addFeature(NAMESPACE_V1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static JingleInBandByteStreamManager getInstanceFor(XMPPConnection connection) {
|
|
||||||
JingleInBandByteStreamManager manager = INSTANCES.get(connection);
|
|
||||||
if (manager == null) {
|
|
||||||
manager = new JingleInBandByteStreamManager(connection);
|
|
||||||
INSTANCES.put(connection, manager);
|
|
||||||
}
|
|
||||||
return manager;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a random session id.
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public static String generateSessionId() {
|
|
||||||
return StringUtils.randomString(24);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,51 +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;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
|
||||||
import org.jivesoftware.smack.util.StringUtils;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.jxmpp.jid.Jid;
|
|
||||||
import org.jxmpp.jid.impl.JidCreate;
|
|
||||||
import org.jxmpp.stringprep.XmppStringprepException;
|
|
||||||
|
|
||||||
import static junit.framework.TestCase.assertEquals;
|
|
||||||
import static junit.framework.TestCase.assertNotSame;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test JingleSession class.
|
|
||||||
*/
|
|
||||||
public class JingleSessionTest extends SmackTestSuite {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void sessionTest() throws XmppStringprepException {
|
|
||||||
Jid romeo = JidCreate.from("romeo@montague.lit");
|
|
||||||
Jid juliet = JidCreate.from("juliet@capulet.lit");
|
|
||||||
String sid = StringUtils.randomString(24);
|
|
||||||
|
|
||||||
JingleSession s1 = new JingleSession(romeo, juliet, sid);
|
|
||||||
JingleSession s2 = new JingleSession(juliet, romeo, sid);
|
|
||||||
JingleSession s3 = new JingleSession(romeo, juliet, StringUtils.randomString(23));
|
|
||||||
JingleSession s4 = new JingleSession(juliet, romeo, sid);
|
|
||||||
|
|
||||||
assertNotSame(s1, s2);
|
|
||||||
assertNotSame(s1, s3);
|
|
||||||
assertNotSame(s2, s3);
|
|
||||||
assertEquals(s2, s4);
|
|
||||||
assertEquals(s2.hashCode(), s4.hashCode());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -20,7 +20,6 @@ import org.jivesoftware.smack.XMPPConnection;
|
||||||
import org.jivesoftware.smack.TCPConnection;
|
import org.jivesoftware.smack.TCPConnection;
|
||||||
import org.jivesoftware.smack.XMPPException;
|
import org.jivesoftware.smack.XMPPException;
|
||||||
import org.jivesoftware.smackx.jingle.JingleManager;
|
import org.jivesoftware.smackx.jingle.JingleManager;
|
||||||
import org.jivesoftware.smackx.jingle.JingleSession;
|
|
||||||
import org.jivesoftware.smackx.jingle.JingleSessionRequest;
|
import org.jivesoftware.smackx.jingle.JingleSessionRequest;
|
||||||
import org.jivesoftware.smackx.jingle.listeners.JingleSessionRequestListener;
|
import org.jivesoftware.smackx.jingle.listeners.JingleSessionRequestListener;
|
||||||
import org.jivesoftware.smackx.jingle.media.JingleMediaManager;
|
import org.jivesoftware.smackx.jingle.media.JingleMediaManager;
|
||||||
|
|
|
@ -21,7 +21,6 @@ import de.javawi.jstun.util.UtilityException;
|
||||||
import org.jivesoftware.smack.XMPPException;
|
import org.jivesoftware.smack.XMPPException;
|
||||||
import org.jivesoftware.smack.test.SmackTestCase;
|
import org.jivesoftware.smack.test.SmackTestCase;
|
||||||
import org.jivesoftware.smackx.jingle.JingleManager;
|
import org.jivesoftware.smackx.jingle.JingleManager;
|
||||||
import org.jivesoftware.smackx.jingle.JingleSession;
|
|
||||||
import org.jivesoftware.smackx.jingle.JingleSessionRequest;
|
import org.jivesoftware.smackx.jingle.JingleSessionRequest;
|
||||||
import org.jivesoftware.smackx.jingle.listeners.JingleSessionListener;
|
import org.jivesoftware.smackx.jingle.listeners.JingleSessionListener;
|
||||||
import org.jivesoftware.smackx.jingle.listeners.JingleSessionRequestListener;
|
import org.jivesoftware.smackx.jingle.listeners.JingleSessionRequestListener;
|
||||||
|
|
Loading…
Reference in a new issue