Commit 051f80f3 authored by Alex Wenckus's avatar Alex Wenckus Committed by alex

Work on file transfer framework.

1) Added several interfaces to allow for easier extension and modification
2) Tracking of file transfer meta parameters
3) Default implementation forces proxy transfer to be associated with a file transfer negotiation that occured in the system

git-svn-id: http://svn.igniterealtime.org/svn/repos/wildfire/trunk@3762 b35dd754-fafc-0310-a699-88a17e54d16e
parent 49db6756
/**
* $RCSfile$
* $Revision: 1217 $
* $Date: 2005-04-11 18:11:06 -0300 (Mon, 11 Apr 2005) $
*
* Copyright (C) 1999-2006 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire.filetransfer;
import org.jivesoftware.util.CacheManager;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.wildfire.auth.UnauthorizedException;
import org.dom4j.Element;
import java.util.Map;
import java.util.List;
/**
* Provides several utility methods for file transfer manager implementaions to utilize.
*
* @author Alexander Wenckus
*/
public abstract class AbstractFileTransferManager implements FileTransferManager {
private static final String CACHE_NAME = "File Transfer Cache";
private final Map<String, FileTransfer> fileTransferMap;
/**
* True if a proxy transfer is required to have a matching file transfer.
*/
private boolean isMatchProxyTransfer;
/**
* Default constructor creates the cache.
*/
public AbstractFileTransferManager() {
fileTransferMap = CacheManager.initializeCache(CACHE_NAME, "fileTransfer", 128 * 1024, 1000 * 60 * 10);
isMatchProxyTransfer = JiveGlobals.getBooleanProperty("xmpp.proxy.transfer.required", true);
}
/**
* Returns true if the proxy transfer should be matched to an existing file transfer in the system.
*
* @return Returns true if the proxy transfer should be matched to an existing file transfer in the system.
*/
public boolean isMatchProxyTransfer() {
return isMatchProxyTransfer;
}
public void cacheFileTransfer(String key, FileTransfer transfer) {
fileTransferMap.put(key, transfer);
}
public FileTransfer retrieveFileTransfer(String key) {
return fileTransferMap.get(key);
}
public static Element getChildElement(Element element, String namespace) {
List elements = element.elements();
if (elements.isEmpty()) {
return null;
}
for (int i = 0; i < elements.size(); i++) {
Element childElement = (Element) elements.get(i);
String childNamespace = childElement.getNamespaceURI();
if (namespace.equals(childNamespace)) {
return childElement;
}
}
return null;
}
public void registerProxyTransfer(String transferDigest, ProxyTransfer proxyTransfer)
throws UnauthorizedException
{
FileTransfer transfer = retrieveFileTransfer(transferDigest);
if (isMatchProxyTransfer() && transfer == null) {
throw new UnauthorizedException("Unable to match proxy transfer with a file transfer");
}
else if (transfer == null) {
return;
}
transfer.setProgress(proxyTransfer);
cacheFileTransfer(transferDigest, transfer);
}
}
/**
* $RCSfile$
* $Revision: 1217 $
* $Date: 2005-04-11 18:11:06 -0300 (Mon, 11 Apr 2005) $
*
* Copyright (C) 1999-2006 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire.filetransfer;
/**
* Contains all of the meta information associated with a file transfer.
*
* @author Alexander Wenckus
*/
public class FileTransfer {
private String sessionID;
private String initiator;
private String target;
private String fileName;
private long fileSize;
private String mimeType;
private FileTransferProgress progress;
public FileTransfer(String initiator, String target, String sessionID, String fileName,
long fileSize, String mimeType)
{
this.initiator = initiator;
this.target = target;
this.sessionID = sessionID;
this.fileName = fileName;
this.fileSize = fileSize;
this.mimeType = mimeType;
}
public String getSessionID() {
return sessionID;
}
public void setSessionID(String sessionID) {
this.sessionID = sessionID;
}
public String getInitiator() {
return initiator;
}
public void setInitiator(String initiator) {
this.initiator = initiator;
}
public String getTarget() {
return target;
}
public void setTarget(String target) {
this.target = target;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public long getFileSize() {
return fileSize;
}
public void setFileSize(long fileSize) {
this.fileSize = fileSize;
}
public String getMimeType() {
return mimeType;
}
public void setMimeType(String mimeType) {
this.mimeType = mimeType;
}
public FileTransferProgress getProgress() {
return progress;
}
public void setProgress(FileTransferProgress progress) {
this.progress = progress;
}
}
/**
* $RCSfile$
* $Revision: 1217 $
* $Date: 2005-04-11 18:11:06 -0300 (Mon, 11 Apr 2005) $
*
* Copyright (C) 1999-2006 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire.filetransfer;
import org.xmpp.packet.JID;
import org.dom4j.Element;
import org.jivesoftware.wildfire.auth.UnauthorizedException;
/**
* Manages all file transfer currently happening originating from and/or ending at users of the server. From here,
* file transfers can be administered and stats can be tracked.
*
* @author Alexander Wenckus
*/
public interface FileTransferManager {
/**
* Checks an incoming file transfer request to see if it should be accepted or rejected.
* If it is accepted true will be returned and if it is rejected false will be returned.
*
* @param packetID The packet ID of the packet being parsed.
* @param from The offerer The offerer of the file transfer.
* @param to The receiver The potential reciever of the file transfer.
* @param siElement The Stream Initiation element
* @return True if it should be accepted false if it should not.
*/
boolean acceptIncomingFileTransferRequest(String packetID, JID from, JID to, Element siElement);
/**
* Registers that a transfer has begun through the proxy connected to the server.
*
* @param transferDigest The digest of the initiator + target + sessionID that uniquely identifies a file transfer
* @param proxyTransfer The related proxy transfer.
* @throws UnauthorizedException Thrown when in the current server configuration this transfer should not be
* permitted.
*/
public void registerProxyTransfer(String transferDigest, ProxyTransfer proxyTransfer) throws UnauthorizedException;
}
/**
* $RCSfile$
* $Revision: 1217 $
* $Date: 2005-04-11 18:11:06 -0300 (Mon, 11 Apr 2005) $
*
* Copyright (C) 1999-2006 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire.filetransfer;
/**
* An interface to track the progress of a file transfer through the server. This interface is used
* by {@link FileTransfer} to make this information available if it is in the system.
*/
public interface FileTransferProgress {
public long getAmountTransfered() throws UnsupportedOperationException;
public String getInitiator();
public String getTarget();
public String getSessionID();
}
/** /**
* $Revision $ * $RCSfile$
* $Date $ * $Revision: 1217 $
* $Date: 2005-04-11 18:11:06 -0300 (Mon, 11 Apr 2005) $
* *
* Copyright (C) 1999-2006 Jive Software. All rights reserved. * Copyright (C) 1999-2006 Jive Software. All rights reserved.
* *
...@@ -15,12 +16,13 @@ import org.dom4j.Element; ...@@ -15,12 +16,13 @@ import org.dom4j.Element;
import org.jivesoftware.util.JiveGlobals; import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log; import org.jivesoftware.util.Log;
import org.jivesoftware.wildfire.*; import org.jivesoftware.wildfire.*;
import org.jivesoftware.wildfire.filetransfer.spi.DefaultFileTransferManager;
import org.jivesoftware.wildfire.interceptor.InterceptorManager;
import org.jivesoftware.wildfire.interceptor.PacketInterceptor;
import org.jivesoftware.wildfire.interceptor.PacketRejectedException;
import org.jivesoftware.wildfire.auth.UnauthorizedException; import org.jivesoftware.wildfire.auth.UnauthorizedException;
import org.jivesoftware.wildfire.container.BasicModule; import org.jivesoftware.wildfire.container.BasicModule;
import org.jivesoftware.wildfire.disco.DiscoInfoProvider; import org.jivesoftware.wildfire.disco.*;
import org.jivesoftware.wildfire.disco.DiscoItemsProvider;
import org.jivesoftware.wildfire.disco.DiscoServerItem;
import org.jivesoftware.wildfire.disco.ServerItemsProvider;
import org.jivesoftware.wildfire.forms.spi.XDataFormImpl; import org.jivesoftware.wildfire.forms.spi.XDataFormImpl;
import org.xmpp.packet.IQ; import org.xmpp.packet.IQ;
import org.xmpp.packet.JID; import org.xmpp.packet.JID;
...@@ -54,12 +56,14 @@ public class FileTransferProxy extends BasicModule ...@@ -54,12 +56,14 @@ public class FileTransferProxy extends BasicModule
private PacketRouter router; private PacketRouter router;
private String proxyIP; private String proxyIP;
private ProxyConnectionManager connectionManager; private ProxyConnectionManager connectionManager;
private FileTransferManager transferManager;
public FileTransferProxy() { public FileTransferProxy() {
super("SOCKS5 file transfer proxy"); super("SOCKS5 file transfer proxy");
info = new IQHandlerInfo("query", NAMESPACE); info = new IQHandlerInfo("query", NAMESPACE);
InterceptorManager.getInstance().addInterceptor(new FileTransferInterceptor());
} }
public boolean handleIQ(IQ packet) throws UnauthorizedException { public boolean handleIQ(IQ packet) throws UnauthorizedException {
...@@ -147,8 +151,12 @@ public class FileTransferProxy extends BasicModule ...@@ -147,8 +151,12 @@ public class FileTransferProxy extends BasicModule
catch (UnknownHostException e) { catch (UnknownHostException e) {
Log.error("Couldn't discover local host", e); Log.error("Couldn't discover local host", e);
} }
transferManager = getFileTransferManager();
connectionManager = new ProxyConnectionManager(transferManager);
}
connectionManager = new ProxyConnectionManager(); private FileTransferManager getFileTransferManager() {
return new DefaultFileTransferManager();
} }
public void start() { public void start() {
...@@ -157,8 +165,9 @@ public class FileTransferProxy extends BasicModule ...@@ -157,8 +165,9 @@ public class FileTransferProxy extends BasicModule
if (isEnabled()) { if (isEnabled()) {
connectionManager.processConnections(getProxyPort()); connectionManager.processConnections(getProxyPort());
routingTable.addRoute(getAddress(), this); routingTable.addRoute(getAddress(), this);
XMPPServer.getInstance().getIQDiscoItemsHandler() XMPPServer server = XMPPServer.getInstance();
.addComponentItem(getAddress().toString(), "Socks 5 Bytestreams Proxy");
server.getIQDiscoItemsHandler().addComponentItem(getAddress().toString(), "Socks 5 Bytestreams Proxy");
} }
else { else {
XMPPServer.getInstance().getIQDiscoItemsHandler() XMPPServer.getInstance().getIQDiscoItemsHandler()
...@@ -295,4 +304,30 @@ public class FileTransferProxy extends BasicModule ...@@ -295,4 +304,30 @@ public class FileTransferProxy extends BasicModule
} }
} }
} }
/**
* Interceptor to grab and validate file transfer meta information.
*/
private class FileTransferInterceptor implements PacketInterceptor {
public void interceptPacket(Packet packet, Session session, boolean incoming, boolean processed)
throws PacketRejectedException {
// We only want packets recieved by the server
if (!processed && incoming && packet instanceof IQ) {
IQ iq = (IQ) packet;
Element childElement = iq.getChildElement();
String namespace = childElement.getNamespaceURI();
if ("http://jabber.org/protocol/si".equals(namespace)) {
// If this is a set, check the feature offer
if (iq.getType().equals(IQ.Type.set)) {
JID from = iq.getFrom();
JID to = iq.getTo();
String packetID = iq.getID();
if (!transferManager.acceptIncomingFileTransferRequest(packetID, from, to, childElement)) {
throw new PacketRejectedException();
}
}
}
}
}
}
} }
/** /**
* $Revision$ * $RCSfile$
* $Date$ * $Revision: 1217 $
* Copyright (C) 1999-2005 Jive Software. All rights reserved. * $Date: 2005-04-11 18:11:06 -0300 (Mon, 11 Apr 2005) $
*
* Copyright (C) 1999-2006 Jive Software. All rights reserved.
* *
* This software is published under the terms of the GNU Public License (GPL), * This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution. * a copy of which is included in this distribution.
...@@ -10,6 +12,10 @@ package org.jivesoftware.wildfire.filetransfer; ...@@ -10,6 +12,10 @@ package org.jivesoftware.wildfire.filetransfer;
import org.jivesoftware.util.CacheManager; import org.jivesoftware.util.CacheManager;
import org.jivesoftware.util.Log; import org.jivesoftware.util.Log;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.ClassUtils;
import org.jivesoftware.wildfire.auth.UnauthorizedException;
import org.jivesoftware.wildfire.filetransfer.spi.DefaultProxyTransfer;
import org.xmpp.packet.JID; import org.xmpp.packet.JID;
import java.io.*; import java.io.*;
...@@ -39,12 +45,24 @@ public class ProxyConnectionManager { ...@@ -39,12 +45,24 @@ public class ProxyConnectionManager {
private ExecutorService executor = Executors.newCachedThreadPool(); private ExecutorService executor = Executors.newCachedThreadPool();
private Future<?> socketProcess; private Future<?> socketProcess;
private int proxyPort; private int proxyPort;
public ProxyConnectionManager() { private FileTransferManager transferManager;
private String className;
static long amountTransfered = 0;
public ProxyConnectionManager(FileTransferManager manager) {
String cacheName = "File Transfer"; String cacheName = "File Transfer";
CacheManager.initializeCache(cacheName, "filetransfer", -1, 1000 * 60 * 10); CacheManager.initializeCache(cacheName, "filetransfer", -1, 1000 * 60 * 10);
connectionMap = CacheManager.getCache(cacheName); connectionMap = CacheManager.getCache(cacheName);
className = JiveGlobals.getProperty("provider.transfer.proxy",
"org.jivesoftware.wildfire.filetransfer.spi.DefaultProxyTransfer");
transferManager = manager;
} }
/* /*
...@@ -64,7 +82,7 @@ public class ProxyConnectionManager { ...@@ -64,7 +82,7 @@ public class ProxyConnectionManager {
socketProcess = executor.submit(new Runnable() { socketProcess = executor.submit(new Runnable() {
public void run() { public void run() {
ServerSocket serverSocket = null; ServerSocket serverSocket;
try { try {
serverSocket = new ServerSocket(port); serverSocket = new ServerSocket(port);
} }
...@@ -72,7 +90,7 @@ public class ProxyConnectionManager { ...@@ -72,7 +90,7 @@ public class ProxyConnectionManager {
Log.error("Error creating server socket", e); Log.error("Error creating server socket", e);
return; return;
} }
while (serverSocket != null) { while (serverSocket.isBound()) {
final Socket socket; final Socket socket;
try { try {
socket = serverSocket.accept(); socket = serverSocket.accept();
...@@ -89,6 +107,12 @@ public class ProxyConnectionManager { ...@@ -89,6 +107,12 @@ public class ProxyConnectionManager {
catch (IOException ie) { catch (IOException ie) {
Log.error("Error processing file transfer proxy connection", Log.error("Error processing file transfer proxy connection",
ie); ie);
try {
socket.close();
}
catch (IOException e) {
/* Do Nothing */
}
} }
} }
}); });
...@@ -140,42 +164,69 @@ public class ProxyConnectionManager { ...@@ -140,42 +164,69 @@ public class ProxyConnectionManager {
out.write(cmd); out.write(cmd);
String responseDigest = processIncomingSocks5Message(in); String responseDigest = processIncomingSocks5Message(in);
cmd = createOutgoingSocks5Message(0, responseDigest); try {
synchronized (connectionLock) { synchronized (connectionLock) {
ProxyTransfer transfer = connectionMap.get(responseDigest); ProxyTransfer transfer = connectionMap.get(responseDigest);
if (transfer == null) { if (transfer == null) {
connectionMap.put(responseDigest, new ProxyTransfer(responseDigest, connection)); transfer = createProxyTransfer(responseDigest, connection);
transferManager.registerProxyTransfer(responseDigest, transfer);
connectionMap.put(responseDigest, transfer);
} }
else { else {
transfer.setInitiatorSocket(connection); transfer.setInitiatorSocket(connection);
} }
} }
cmd = createOutgoingSocks5Message(0, responseDigest);
if (!connection.isConnected()) { out.write(cmd);
throw new IOException("Socket closed by remote user");
} }
catch (UnauthorizedException eu) {
cmd = createOutgoingSocks5Message(2, responseDigest);
out.write(cmd); out.write(cmd);
throw new IOException("Illegal proxy transfer");
}
}
private ProxyTransfer createProxyTransfer(String transferDigest, Socket initiatorSocket) {
ProxyTransfer provider;
try {
Class c = ClassUtils.forName(className);
provider = (ProxyTransfer) c.newInstance();
}
catch (Exception e) {
Log.error("Error loading proxy transfer provider: " + className, e);
provider = new DefaultProxyTransfer();
} }
private String processIncomingSocks5Message(InputStream in) provider.setTransferDigest(transferDigest);
throws IOException { provider.setTargetSocket(initiatorSocket);
return provider;
}
private static String processIncomingSocks5Message(InputStream in)
throws IOException
{
// read the version and command // read the version and command
byte[] cmd = new byte[5]; byte[] cmd = new byte[5];
in.read(cmd, 0, 5); int read = in.read(cmd, 0, 5);
if (read != 5) {
throw new IOException("Error reading Socks5 version and command");
}
// read the digest // read the digest
byte[] addr = new byte[cmd[4]]; byte[] addr = new byte[cmd[4]];
in.read(addr, 0, addr.length); read = in.read(addr, 0, addr.length);
if (read != addr.length) {
throw new IOException("Error reading provided address");
}
String digest = new String(addr); String digest = new String(addr);
in.read(); read = in.read();
in.read(); read = in.read();
return digest; return digest;
} }
private byte[] createOutgoingSocks5Message(int cmd, String digest) { private static byte[] createOutgoingSocks5Message(int cmd, String digest) {
byte addr[] = digest.getBytes(); byte addr[] = digest.getBytes();
byte[] data = new byte[7 + addr.length]; byte[] data = new byte[7 + addr.length];
...@@ -222,13 +273,13 @@ public class ProxyConnectionManager { ...@@ -222,13 +273,13 @@ public class ProxyConnectionManager {
throw new IllegalArgumentException("Transfer doesn't exist or is missing parameters"); throw new IllegalArgumentException("Transfer doesn't exist or is missing parameters");
} }
transfer.setInitiatorJID(initiator.toString()); transfer.setInitiator(initiator.toString());
transfer.setTargetJID(target.toString()); transfer.setTarget(target.toString());
transfer.setTransferSession(sid); transfer.setSessionID(sid);
transfer.setTransferFuture(executor.submit(new Runnable() { transfer.setTransferFuture(executor.submit(new Runnable() {
public void run() { public void run() {
try { try {
transfer(transfer); transfer.doTransfer();
} }
catch (IOException e) { catch (IOException e) {
Log.error("Error during file transfer", e); Log.error("Error during file transfer", e);
...@@ -240,29 +291,6 @@ public class ProxyConnectionManager { ...@@ -240,29 +291,6 @@ public class ProxyConnectionManager {
})); }));
} }
private void transfer(ProxyTransfer transfer) throws IOException {
InputStream in = transfer.getInitiatorSocket().getInputStream();
OutputStream out = transfer.getTargetSocket().getOutputStream();
final byte[] b = new byte[1000];
int count = 0;
int amountWritten = 0;
count = in.read(b);
while (count != -1) {
// write to the output stream
out.write(b, 0, count);
amountWritten += count;
// read more bytes from the input stream
count = in.read(b);
}
transfer.getInitiatorSocket().close();
transfer.getTargetSocket().close();
}
/** /**
* Creates the digest needed for a byte stream. It is the SHA1(sessionID + * Creates the digest needed for a byte stream. It is the SHA1(sessionID +
* initiator + target). * initiator + target).
...@@ -272,8 +300,9 @@ public class ProxyConnectionManager { ...@@ -272,8 +300,9 @@ public class ProxyConnectionManager {
* @param target The target of the stream negotiation * @param target The target of the stream negotiation
* @return SHA-1 hash of the three parameters * @return SHA-1 hash of the three parameters
*/ */
private String createDigest(final String sessionID, final JID initiator, public static String createDigest(final String sessionID, final JID initiator,
final JID target) { final JID target)
{
return hash(sessionID + initiator.getNode() return hash(sessionID + initiator.getNode()
+ "@" + initiator.getDomain() + "/" + "@" + initiator.getDomain() + "/"
+ initiator.getResource() + initiator.getResource()
......
...@@ -9,107 +9,61 @@ ...@@ -9,107 +9,61 @@
*/ */
package org.jivesoftware.wildfire.filetransfer; package org.jivesoftware.wildfire.filetransfer;
import org.jivesoftware.util.CacheSizes;
import org.jivesoftware.util.Cacheable; import org.jivesoftware.util.Cacheable;
import java.net.Socket; import java.net.Socket;
import java.io.IOException;
import java.util.concurrent.Future; import java.util.concurrent.Future;
/** /**
* Tracks the different connections related to a file transfer. There are two connections, the * Tracks the different connections related to a proxy file transfer. There are two connections, the
* initiator and the target and when both connections are completed the transfer can begin. * initiator and the target and when both connections are completed the transfer can begin.
*/ */
public class ProxyTransfer implements Cacheable { public interface ProxyTransfer extends Cacheable, FileTransferProgress {
/**
private String initiatorJID; * Returns the fully qualified JID of the initiator of the file transfer.
*
private Socket initiatorSocket; * @return Returns the fully qualified JID of the initiator of the file transfer.
*/
private Socket targetSocket; public String getInitiator();
private String targetJID;
private String transferDigest;
private String transferSession;
private Future<?> future; public void setInitiatorSocket(Socket initiatorSocket);
public ProxyTransfer(String transferDigest, Socket targetSocket) { public Socket getInitiatorSocket();
this.transferDigest = transferDigest;
this.targetSocket = targetSocket;
}
public String getInitiatorJID() { public void setTargetSocket(Socket targetSocket);
return initiatorJID;
}
public void setInitiatorJID(String initiatorJID) { public Socket getTargetSocket();
this.initiatorJID = initiatorJID;
}
public Socket getInitiatorSocket() { public void setTarget(String target);
return initiatorSocket;
}
public void setInitiatorSocket(Socket initiatorSocket) { public String getTarget();
this.initiatorSocket = initiatorSocket;
}
public Socket getTargetSocket() { public void setTransferDigest(String digest);
return targetSocket;
}
public void setTargetSocket(Socket targetSocket) { public String getTransferDigest();
this.targetSocket = targetSocket;
}
public String getTargetJID() { public String getSessionID();
return targetJID;
}
public void setTargetJID(String targetJID) { public void setSessionID(String streamID);
this.targetJID = targetJID;
}
public String getTransferDigest() { /**
return transferDigest; * Returns true if the Bytestream is ready to be activated and the transfer can begin.
} *
* @return Returns true if the Bytestream is ready to be activated.
*/
public boolean isActivatable();
public void setTransferDigest(String transferDigest) { public long getAmountTransfered();
this.transferDigest = transferDigest;
}
public String getTransferSession() { public int getCachedSize();
return transferSession;
}
public void setTransferSession(String transferSession) { public void setTransferFuture(Future<?> future);
this.transferSession = transferSession;
}
/** /**
* Returns true if the Bytestream is ready to be activated and the transfer can begin. * Transfers the file from the initiator to the target.
*
* @return Returns true if the Bytestream is ready to be activated.
*/ */
public boolean isActivatable() { public void doTransfer() throws IOException;
return ((initiatorSocket != null) && (targetSocket != null));
} void setInitiator(String s);
public void setTransferFuture(Future<?> future) {
this.future = future;
}
public int getCachedSize() {
// Approximate the size of the object in bytes by calculating the size
// of each field.
int size = 0;
size += CacheSizes.sizeOfObject(); // overhead of object
size += CacheSizes.sizeOfString(initiatorJID);
size += CacheSizes.sizeOfString(targetJID);
size += CacheSizes.sizeOfString(transferDigest);
size += CacheSizes.sizeOfString(transferSession);
return size;
}
} }
/**
* $RCSfile$
* $Revision: 1217 $
* $Date: 2005-04-11 18:11:06 -0300 (Mon, 11 Apr 2005) $
*
* Copyright (C) 1999-2006 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire.filetransfer.spi;
import org.xmpp.packet.JID;
import org.dom4j.Element;
import org.jivesoftware.wildfire.filetransfer.AbstractFileTransferManager;
import org.jivesoftware.wildfire.filetransfer.FileTransfer;
import org.jivesoftware.wildfire.filetransfer.ProxyConnectionManager;
/**
*
*/
public class DefaultFileTransferManager extends AbstractFileTransferManager {
public boolean acceptIncomingFileTransferRequest(String packetID, JID from, JID to, Element siElement) {
String streamID = siElement.attributeValue("id");
String mimeType = siElement.attributeValue("mime-type");
String profile = siElement.attributeValue("profile");
// Check profile, the only type we deal with currently is file transfer
if ("http://jabber.org/protocol/si/profile/file-transfer".equals(profile)) {
Element fileTransferElement =
getChildElement(siElement, "http://jabber.org/protocol/si/profile/file-transfer");
// Not valid form, reject
if (fileTransferElement == null) {
return false;
}
String fileName = fileTransferElement.attributeValue("name");
long size = Long.parseLong(fileTransferElement.attributeValue("size"));
FileTransfer transfer = new FileTransfer(from.toString(), to.toString(),
streamID, fileName, size, mimeType);
cacheFileTransfer(ProxyConnectionManager.createDigest(streamID, from, to), transfer);
}
return true;
}
}
/**
* $RCSfile$
* $Revision: 1217 $
* $Date: 2005-04-11 18:11:06 -0300 (Mon, 11 Apr 2005) $
*
* Copyright (C) 1999-2006 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire.filetransfer.spi;
import org.jivesoftware.util.CacheSizes;
import org.jivesoftware.wildfire.filetransfer.ProxyTransfer;
import java.net.Socket;
import java.util.concurrent.Future;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.DataOutputStream;
/**
* Tracks the different connections related to a file transfer. There are two connections, the
* initiator and the target and when both connections are completed the transfer can begin.
*/
public class DefaultProxyTransfer implements ProxyTransfer {
private static long amountTransfered;
private String initiator;
private Socket initiatorSocket;
private Socket targetSocket;
private String target;
private String transferDigest;
private String streamID;
private Future<?> future;
private long amountWritten;
public DefaultProxyTransfer() { }
public String getInitiator() {
return initiator;
}
/**
* Sets the fully qualified JID of the initiator of the file transfer.
*
* @param initiator The fully qualified JID of the initiator of the file transfer.
*/
public void setInitiator(String initiator) {
this.initiator = initiator;
}
public Socket getInitiatorSocket() {
return initiatorSocket;
}
public void setInitiatorSocket(Socket initiatorSocket) {
this.initiatorSocket = initiatorSocket;
}
public Socket getTargetSocket() {
return targetSocket;
}
public void setTargetSocket(Socket targetSocket) {
this.targetSocket = targetSocket;
}
public String getTarget() {
return target;
}
public void setTarget(String target) {
this.target = target;
}
public String getTransferDigest() {
return transferDigest;
}
public void setTransferDigest(String transferDigest) {
this.transferDigest = transferDigest;
}
public String getSessionID() {
return streamID;
}
public void setSessionID(String streamID) {
this.streamID = streamID;
}
public boolean isActivatable() {
return ((initiatorSocket != null) && (targetSocket != null));
}
public synchronized void setTransferFuture(Future<?> future) {
if(this.future != null) {
throw new IllegalStateException("Transfer is already in progress, or has completed.");
}
this.future = future;
}
public long getAmountTransfered() {
return amountWritten;
}
public void doTransfer() throws IOException {
if(!isActivatable()) {
throw new IOException("Transfer missing party");
}
InputStream in = getInitiatorSocket().getInputStream();
OutputStream out = new ProxyOutputStream(getTargetSocket().getOutputStream());
final byte[] b = new byte[1000];
int count = 0;
amountWritten = 0;
do {
// write to the output stream
out.write(b, 0, count);
amountWritten += count;
// read more bytes from the input stream
count = in.read(b);
} while (count >= 0);
getInitiatorSocket().close();
getTargetSocket().close();
}
public int getCachedSize() {
// Approximate the size of the object in bytes by calculating the size
// of each field.
int size = 0;
size += CacheSizes.sizeOfObject(); // overhead of object
size += CacheSizes.sizeOfString(initiator);
size += CacheSizes.sizeOfString(target);
size += CacheSizes.sizeOfString(transferDigest);
size += CacheSizes.sizeOfString(streamID);
size += CacheSizes.sizeOfLong(); // Amount written
size += CacheSizes.sizeOfObject(); // Initiatior Socket
size += CacheSizes.sizeOfObject(); // Target socket
size += CacheSizes.sizeOfObject(); // Future
return size;
}
static class ProxyOutputStream extends DataOutputStream {
public ProxyOutputStream(OutputStream out) {
super(out);
}
public synchronized void write(byte b[], int off, int len) throws IOException {
super.write(b, off, len);
amountTransfered += len;
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment