Proper(er) implementation

This commit is contained in:
vanitasvitae 2017-06-06 16:51:45 +02:00
parent 83cedbe00f
commit 2686fc1ccb
Signed by: vanitasvitae
GPG Key ID: 62BEE9264BF17311
19 changed files with 674 additions and 469 deletions

View File

@ -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);
}
} }

View File

@ -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) {
}
}

View File

@ -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);
}
}

View File

@ -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;
} }

View File

@ -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);
} }
} }

View File

@ -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");
} }
} }

View File

@ -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;
}
} }

View File

@ -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;
}
}

View File

@ -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);
} }
} }
} }

View File

@ -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);
} }
} }

View File

@ -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);
}

View File

@ -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;
} }
} }

View File

@ -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);
}
}

View File

@ -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());
}
}

View File

@ -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;

View File

@ -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;