mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-12-24 19:47:58 +01:00
Basic Sending/Receiving Files works again
This commit is contained in:
parent
af069ffc49
commit
1dbdafe28c
9 changed files with 236 additions and 88 deletions
|
@ -63,6 +63,18 @@ public abstract class JingleFileTransfer extends JingleDescription<JingleFileTra
|
|||
}
|
||||
}
|
||||
|
||||
public void notifyProgressListenersFinished() {
|
||||
for (ProgressListener p : progressListeners) {
|
||||
p.finished();
|
||||
}
|
||||
}
|
||||
|
||||
public void notifyProgressListenersStarted() {
|
||||
for (ProgressListener p : progressListeners) {
|
||||
p.started();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return JingleFileTransfer.NAMESPACE;
|
||||
|
|
|
@ -17,8 +17,10 @@
|
|||
package org.jivesoftware.smackx.jft.internal;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
@ -41,6 +43,8 @@ public class JingleIncomingFileOffer extends AbstractJingleFileOffer<RemoteFile>
|
|||
|
||||
private static final Logger LOGGER = Logger.getLogger(JingleIncomingFileOffer.class.getName());
|
||||
|
||||
private File target;
|
||||
|
||||
public JingleIncomingFileOffer(JingleFileTransferChildElement offer) {
|
||||
super(new RemoteFile(offer));
|
||||
}
|
||||
|
@ -52,13 +56,61 @@ public class JingleIncomingFileOffer extends AbstractJingleFileOffer<RemoteFile>
|
|||
|
||||
@Override
|
||||
public void onTransportReady(BytestreamSession bytestreamSession) {
|
||||
InputStream inputStream;
|
||||
LOGGER.log(Level.INFO, "Receive file to " + target.getAbsolutePath());
|
||||
File mFile = target;
|
||||
if (!mFile.exists()) {
|
||||
try {
|
||||
mFile.createNewFile();
|
||||
} catch (IOException e) {
|
||||
LOGGER.log(Level.SEVERE, "Could not create new File!");
|
||||
}
|
||||
}
|
||||
|
||||
InputStream inputStream = null;
|
||||
OutputStream outputStream = null;
|
||||
try {
|
||||
inputStream = bytestreamSession.getInputStream();
|
||||
outputStream = new FileOutputStream(mFile);
|
||||
|
||||
byte[] filebuf = new byte[(int) file.getSize()];
|
||||
int read = 0;
|
||||
byte[] bufbuf = new byte[4096];
|
||||
LOGGER.log(Level.INFO, "Begin receiving bytes.");
|
||||
while (read < filebuf.length) {
|
||||
int r = inputStream.read(bufbuf);
|
||||
if (r >= 0) {
|
||||
System.arraycopy(bufbuf, 0, filebuf, read, r);
|
||||
read += r;
|
||||
LOGGER.log(Level.INFO, "Read " + r + " (" + read + " of " + filebuf.length + ") bytes.");
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
outputStream.write(filebuf);
|
||||
outputStream.flush();
|
||||
|
||||
} catch (IOException e) {
|
||||
LOGGER.log(Level.SEVERE, "Cannot get InputStream from BytestreamSession: " + e, e);
|
||||
return;
|
||||
} finally {
|
||||
if (inputStream != null) {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
LOGGER.log(Level.SEVERE, "Could not close InputStream: " + e, e);
|
||||
}
|
||||
}
|
||||
|
||||
if (outputStream != null) {
|
||||
try {
|
||||
outputStream.close();
|
||||
} catch (IOException e) {
|
||||
LOGGER.log(Level.SEVERE, "Could not close OutputStream: " + e, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
notifyProgressListenersFinished();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -75,6 +127,7 @@ public class JingleIncomingFileOffer extends AbstractJingleFileOffer<RemoteFile>
|
|||
public Future<Void> accept(XMPPConnection connection, File target)
|
||||
throws InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException,
|
||||
SmackException.NoResponseException {
|
||||
this.target = target;
|
||||
JingleSession session = getParent().getParent();
|
||||
if (session.getSessionState() == JingleSession.SessionState.pending) {
|
||||
session.accept(connection);
|
||||
|
|
|
@ -17,6 +17,12 @@
|
|||
package org.jivesoftware.smackx.jft.internal;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
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.smackx.bytestreams.BytestreamSession;
|
||||
import org.jivesoftware.smackx.jft.controller.OutgoingFileOfferController;
|
||||
|
@ -28,6 +34,7 @@ import org.jivesoftware.smackx.jingle.element.JingleElement;
|
|||
* Created by vanitas on 26.07.17.
|
||||
*/
|
||||
public class JingleOutgoingFileOffer extends AbstractJingleFileOffer<LocalFile> implements OutgoingFileOfferController {
|
||||
private static final Logger LOGGER = Logger.getLogger(JingleOutgoingFileOffer.class.getName());
|
||||
|
||||
public JingleOutgoingFileOffer(File file) {
|
||||
super(new LocalFile(file));
|
||||
|
@ -40,7 +47,34 @@ public class JingleOutgoingFileOffer extends AbstractJingleFileOffer<LocalFile>
|
|||
|
||||
@Override
|
||||
public void onTransportReady(BytestreamSession bytestreamSession) {
|
||||
File mFile = ((LocalFile) file).getFile();
|
||||
OutputStream outputStream = null;
|
||||
InputStream inputStream = null;
|
||||
|
||||
try {
|
||||
outputStream = bytestreamSession.getOutputStream();
|
||||
inputStream = new FileInputStream(mFile);
|
||||
|
||||
byte[] fileBuf = new byte[(int) mFile.length()];
|
||||
|
||||
inputStream.read(fileBuf);
|
||||
|
||||
outputStream.write(fileBuf);
|
||||
outputStream.flush();
|
||||
|
||||
} catch (IOException e) {
|
||||
LOGGER.log(Level.SEVERE, "Exception while sending file: " + e, e);
|
||||
} finally {
|
||||
if (inputStream != null) {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
LOGGER.log(Level.SEVERE, "Could not close FileInputStream: " + e, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
notifyProgressListenersFinished();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -88,4 +88,8 @@ public class LocalFile extends AbstractJingleFileTransferFile {
|
|||
public void setHashElement(HashElement hashElement) {
|
||||
this.hashElement = hashElement;
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import org.jivesoftware.smack.SmackException;
|
|||
import org.jivesoftware.smack.XMPPConnection;
|
||||
import org.jivesoftware.smack.XMPPException;
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.util.Async;
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
import org.jivesoftware.smackx.bytestreams.BytestreamSession;
|
||||
import org.jivesoftware.smackx.jingle.Callback;
|
||||
|
@ -60,72 +61,11 @@ public class JingleContent implements JingleTransportCallback {
|
|||
private JingleTransport<?> transport;
|
||||
private JingleSecurity<?> security;
|
||||
|
||||
private JingleTransport<?> replaceTransport = null;
|
||||
|
||||
private final List<Callback> callbacks = Collections.synchronizedList(new ArrayList<Callback>());
|
||||
private final Set<String> transportBlacklist = Collections.synchronizedSet(new HashSet<String>());
|
||||
|
||||
public IQ handleJingleRequest(JingleElement request, XMPPConnection connection) {
|
||||
switch (request.getAction()) {
|
||||
case content_modify:
|
||||
return handleContentModify(request, connection);
|
||||
case description_info:
|
||||
return handleDescriptionInfo(request, connection);
|
||||
case security_info:
|
||||
return handleSecurityInfo(request, connection);
|
||||
case session_info:
|
||||
return handleSessionInfo(request, connection);
|
||||
case transport_accept:
|
||||
return handleTransportAccept(request, connection);
|
||||
case transport_info:
|
||||
return handleTransportInfo(request, connection);
|
||||
case transport_reject:
|
||||
return handleTransportReject(request, connection);
|
||||
case transport_replace:
|
||||
return handleTransportReplace(request, connection);
|
||||
default:
|
||||
throw new AssertionError("Illegal jingle action: " + request.getAction() + " is not allowed here.");
|
||||
}
|
||||
}
|
||||
|
||||
public IQ handleSessionAccept(JingleElement request, XMPPConnection connection) {
|
||||
return IQ.createResultIQ(request);
|
||||
}
|
||||
|
||||
public IQ handleContentModify(JingleElement request, XMPPConnection connection) {
|
||||
return IQ.createResultIQ(request);
|
||||
}
|
||||
|
||||
public IQ handleDescriptionInfo(JingleElement request, XMPPConnection connection) {
|
||||
return IQ.createResultIQ(request);
|
||||
}
|
||||
|
||||
public void handleContentRemove(JingleSession session, XMPPConnection connection) {
|
||||
|
||||
}
|
||||
|
||||
public IQ handleSecurityInfo(JingleElement request, XMPPConnection connection) {
|
||||
return IQ.createResultIQ(request);
|
||||
}
|
||||
|
||||
public IQ handleSessionInfo(JingleElement request, XMPPConnection connection) {
|
||||
return IQ.createResultIQ(request);
|
||||
}
|
||||
|
||||
public IQ handleTransportAccept(JingleElement request, XMPPConnection connection) {
|
||||
return IQ.createResultIQ(request);
|
||||
}
|
||||
|
||||
public IQ handleTransportInfo(JingleElement request, XMPPConnection connection) {
|
||||
return IQ.createResultIQ(request);
|
||||
}
|
||||
|
||||
public IQ handleTransportReject(JingleElement request, XMPPConnection connection) {
|
||||
return IQ.createResultIQ(request);
|
||||
}
|
||||
|
||||
public IQ handleTransportReplace(JingleElement request, XMPPConnection connection) {
|
||||
return IQ.createResultIQ(request);
|
||||
}
|
||||
|
||||
public enum STATE {
|
||||
pending_accept,
|
||||
pending_transmission_start,
|
||||
|
@ -191,6 +131,91 @@ public class JingleContent implements JingleTransportCallback {
|
|||
return new JingleContent(description, transport, security, content.getName(), content.getDisposition(), content.getCreator(), content.getSenders());
|
||||
}
|
||||
|
||||
/* HANDLEXYZ */
|
||||
|
||||
public IQ handleJingleRequest(JingleElement request, XMPPConnection connection) {
|
||||
switch (request.getAction()) {
|
||||
case content_modify:
|
||||
return handleContentModify(request, connection);
|
||||
case description_info:
|
||||
return handleDescriptionInfo(request, connection);
|
||||
case security_info:
|
||||
return handleSecurityInfo(request, connection);
|
||||
case session_info:
|
||||
return handleSessionInfo(request, connection);
|
||||
case transport_accept:
|
||||
return handleTransportAccept(request, connection);
|
||||
case transport_info:
|
||||
return handleTransportInfo(request, connection);
|
||||
case transport_reject:
|
||||
return handleTransportReject(request, connection);
|
||||
case transport_replace:
|
||||
return handleTransportReplace(request, connection);
|
||||
default:
|
||||
throw new AssertionError("Illegal jingle action: " + request.getAction() + " is not allowed here.");
|
||||
}
|
||||
}
|
||||
|
||||
public void handleContentAccept(JingleElement request, XMPPConnection connection) {
|
||||
onAccept(connection);
|
||||
}
|
||||
|
||||
|
||||
public IQ handleSessionAccept(JingleElement request, XMPPConnection connection) {
|
||||
LOGGER.log(Level.INFO, "RECEIVED SESSION ACCEPT!");
|
||||
onAccept(connection);
|
||||
return IQ.createResultIQ(request);
|
||||
}
|
||||
|
||||
public IQ handleContentModify(JingleElement request, XMPPConnection connection) {
|
||||
return IQ.createResultIQ(request);
|
||||
}
|
||||
|
||||
public IQ handleDescriptionInfo(JingleElement request, XMPPConnection connection) {
|
||||
return IQ.createResultIQ(request);
|
||||
}
|
||||
|
||||
public void handleContentRemove(JingleSession session, XMPPConnection connection) {
|
||||
|
||||
}
|
||||
|
||||
public IQ handleSecurityInfo(JingleElement request, XMPPConnection connection) {
|
||||
return IQ.createResultIQ(request);
|
||||
}
|
||||
|
||||
public IQ handleSessionInfo(JingleElement request, XMPPConnection connection) {
|
||||
return IQ.createResultIQ(request);
|
||||
}
|
||||
|
||||
public IQ handleTransportAccept(JingleElement request, XMPPConnection connection) {
|
||||
if (replaceTransport == null) {
|
||||
LOGGER.log(Level.WARNING, "Received transport-accept, but apparently we did not try to replace the transport.");
|
||||
return JingleElement.createJingleErrorOutOfOrder(request);
|
||||
}
|
||||
transport = replaceTransport;
|
||||
|
||||
onAccept(connection);
|
||||
|
||||
return IQ.createResultIQ(request);
|
||||
}
|
||||
|
||||
public IQ handleTransportInfo(JingleElement request, XMPPConnection connection) {
|
||||
assert request.getContents().size() == 1;
|
||||
JingleContentElement content = request.getContents().get(0);
|
||||
|
||||
return transport.handleTransportInfo(content.getTransport().getInfo(), request);
|
||||
}
|
||||
|
||||
public IQ handleTransportReject(JingleElement request, XMPPConnection connection) {
|
||||
return IQ.createResultIQ(request);
|
||||
}
|
||||
|
||||
public IQ handleTransportReplace(JingleElement request, XMPPConnection connection) {
|
||||
return IQ.createResultIQ(request);
|
||||
}
|
||||
|
||||
/* MISCELLANEOUS */
|
||||
|
||||
public void addCallback(Callback callback) {
|
||||
callbacks.add(callback);
|
||||
}
|
||||
|
@ -282,9 +307,29 @@ public class JingleContent implements JingleTransportCallback {
|
|||
getSenders() == JingleContentElement.Senders.both;
|
||||
}
|
||||
|
||||
public void onAccept(final XMPPConnection connection) {
|
||||
//Establish transport
|
||||
Async.go(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if (isReceiving()) {
|
||||
LOGGER.log(Level.INFO, "Establish incoming bytestream.");
|
||||
getTransport().establishIncomingBytestreamSession(connection, JingleContent.this, getParent());
|
||||
} else if (isSending()) {
|
||||
LOGGER.log(Level.INFO, "Establish outgoing bytestream.");
|
||||
getTransport().establishOutgoingBytestreamSession(connection, JingleContent.this, getParent());
|
||||
}
|
||||
} catch (SmackException.NotConnectedException | InterruptedException e) {
|
||||
LOGGER.log(Level.SEVERE, "Error establishing connection: " + e, e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransportReady(BytestreamSession bytestreamSession) {
|
||||
|
||||
LOGGER.log(Level.INFO, "TransportReady: " + (isReceiving() ? "Send" : "Receive"));
|
||||
if (bytestreamSession == null) {
|
||||
throw new AssertionError("bytestreamSession MUST NOT be null at this point.");
|
||||
}
|
||||
|
@ -330,19 +375,6 @@ public class JingleContent implements JingleTransportCallback {
|
|||
connection.createStanzaCollectorAndSend(transportReplace).nextResultOrThrow();
|
||||
}
|
||||
|
||||
public void handleContentAccept(JingleElement request, XMPPConnection connection) {
|
||||
//Establish transport
|
||||
try {
|
||||
if (isReceiving()) {
|
||||
getTransport().establishIncomingBytestreamSession(connection, this, getParent());
|
||||
} else if (isSending()) {
|
||||
getTransport().establishOutgoingBytestreamSession(connection, this, getParent());
|
||||
}
|
||||
} catch (SmackException.NotConnectedException | InterruptedException e) {
|
||||
LOGGER.log(Level.SEVERE, "Error establishing connection: " + e, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static String randomName() {
|
||||
return "cont-" + StringUtils.randomString(16);
|
||||
}
|
||||
|
|
|
@ -110,6 +110,10 @@ public class JingleSession {
|
|||
throw new IllegalStateException("Session is not in pending state.");
|
||||
}
|
||||
|
||||
for (JingleContent content : contents.values()) {
|
||||
content.onAccept(connection);
|
||||
}
|
||||
|
||||
connection.createStanzaCollectorAndSend(createSessionAccept()).nextResultOrThrow();
|
||||
this.sessionState = SessionState.active;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.util.List;
|
|||
import org.jivesoftware.smack.SmackException;
|
||||
import org.jivesoftware.smack.SmackFuture;
|
||||
import org.jivesoftware.smack.XMPPConnection;
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smackx.bytestreams.BytestreamSession;
|
||||
import org.jivesoftware.smackx.jingle.callbacks.JingleTransportCallback;
|
||||
import org.jivesoftware.smackx.jingle.element.JingleContentTransportElement;
|
||||
|
@ -95,7 +96,7 @@ public abstract class JingleTransport<D extends JingleContentTransportElement> e
|
|||
return peersProposal;
|
||||
}
|
||||
|
||||
public abstract void handleTransportInfo(JingleContentTransportInfoElement info, JingleElement wrapping);
|
||||
public abstract IQ handleTransportInfo(JingleContentTransportInfoElement info, JingleElement wrapping);
|
||||
|
||||
public void setParent(JingleContent parent) {
|
||||
if (this.parent != parent) {
|
||||
|
|
|
@ -16,9 +16,13 @@
|
|||
*/
|
||||
package org.jivesoftware.smackx.jingle.transport.jingle_ibb;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.jivesoftware.smack.SmackException;
|
||||
import org.jivesoftware.smack.XMPPConnection;
|
||||
import org.jivesoftware.smack.XMPPException;
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.packet.Stanza;
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
import org.jivesoftware.smackx.bytestreams.BytestreamSession;
|
||||
|
@ -37,6 +41,7 @@ import org.jivesoftware.smackx.jingle.transport.jingle_ibb.element.JingleIBBTran
|
|||
* Jingle InBandBytestream Transport component.
|
||||
*/
|
||||
public class JingleIBBTransport extends JingleTransport<JingleIBBTransportElement> {
|
||||
private static final Logger LOGGER = Logger.getLogger(JingleIBBTransport.class.getName());
|
||||
|
||||
public static final String NAMESPACE_V1 = "urn:xmpp:jingle:transports:ibb:1";
|
||||
public static final String NAMESPACE = NAMESPACE_V1;
|
||||
|
@ -74,10 +79,11 @@ public class JingleIBBTransport extends JingleTransport<JingleIBBTransportElemen
|
|||
@Override
|
||||
public void establishIncomingBytestreamSession(final XMPPConnection connection, final JingleTransportCallback callback, final JingleSession session) {
|
||||
final InBandBytestreamManager inBandBytestreamManager = InBandBytestreamManager.getByteStreamManager(connection);
|
||||
|
||||
LOGGER.log(Level.INFO, "Listen for incoming IBB transports from " + session.getPeer() + ":" + getSid());
|
||||
InBandBytestreamListener bytestreamListener = new InBandBytestreamListener() {
|
||||
@Override
|
||||
public void incomingBytestreamRequest(InBandBytestreamRequest request) {
|
||||
LOGGER.log(Level.INFO, "Incoming IBB stream: " + request.getFrom().asFullJidIfPossible() + ":" + request.getSessionID());
|
||||
if (request.getFrom().asFullJidIfPossible().equals(session.getPeer())
|
||||
&& request.getSessionID().equals(getSid())) {
|
||||
|
||||
|
@ -119,8 +125,8 @@ public class JingleIBBTransport extends JingleTransport<JingleIBBTransportElemen
|
|||
}
|
||||
|
||||
@Override
|
||||
public void handleTransportInfo(JingleContentTransportInfoElement info, JingleElement wrapping) {
|
||||
// Nothing to do.
|
||||
public IQ handleTransportInfo(JingleContentTransportInfoElement info, JingleElement wrapping) {
|
||||
return IQ.createResultIQ(wrapping);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -275,28 +275,30 @@ public class JingleS5BTransport extends JingleTransport<JingleS5BTransportElemen
|
|||
}
|
||||
|
||||
@Override
|
||||
public void handleTransportInfo(JingleContentTransportInfoElement info, JingleElement wrapping) {
|
||||
public IQ handleTransportInfo(JingleContentTransportInfoElement info, JingleElement wrapping) {
|
||||
switch (info.getElementName()) {
|
||||
|
||||
case JingleS5BTransportInfoElement.CandidateUsed.ELEMENT:
|
||||
handleCandidateUsed((JingleS5BTransportInfoElement) info, wrapping);
|
||||
return;
|
||||
break;
|
||||
|
||||
case JingleS5BTransportInfoElement.CandidateActivated.ELEMENT:
|
||||
handleCandidateActivate((JingleS5BTransportInfoElement) info);
|
||||
return;
|
||||
break;
|
||||
|
||||
case JingleS5BTransportInfoElement.CandidateError.ELEMENT:
|
||||
handleCandidateError((JingleS5BTransportInfoElement) info);
|
||||
return;
|
||||
break;
|
||||
|
||||
case JingleS5BTransportInfoElement.ProxyError.ELEMENT:
|
||||
handleProxyError((JingleS5BTransportInfoElement) info);
|
||||
return;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new AssertionError("Unknown transport-info element: " + info.getElementName());
|
||||
}
|
||||
|
||||
return IQ.createResultIQ(wrapping);
|
||||
}
|
||||
|
||||
private void handleCandidateUsed(JingleS5BTransportInfoElement info, JingleElement wrapping) {
|
||||
|
|
Loading…
Reference in a new issue