/*
Copyright 2009 David Revell
This file is part of SwiFTP.
SwiFTP is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
SwiFTP is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with SwiFTP. If not, see <http://www.gnu.org/licenses/>.
*/
package org.swiftp;
import net.micode.fileexplorer.FTPServerService;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import android.util.Log;
public class NormalDataSocketFactory extends DataSocketFactory {
/**
* This class implements normal, traditional opening and closing of data sockets
* used for transmitting directory listings and file contents. PORT and PASV
* work according to the FTP specs. This is in contrast to a
* ProxyDataSocketFactory, which performs contortions to allow data sockets
* to be proxied through a server out in the cloud.
*
*/
// Listener socket used for PASV mode
ServerSocket server = null;
// Remote IP & port information used for PORT mode
InetAddress remoteAddr;
int remotePort;
boolean isPasvMode = true;
public NormalDataSocketFactory() {
clearState();
}
private void clearState() {
/**
* Clears the state of this object, as if no pasv() or port() had occurred.
* All sockets are closed.
*/
if(server != null) {
try {
server.close();
} catch (IOException e) {}
}
server = null;
remoteAddr = null;
remotePort = 0;
myLog.l(Log.DEBUG, "NormalDataSocketFactory state cleared");
}
public int onPasv() {
clearState();
try {
// Listen on any port (port parameter 0)
server = new ServerSocket(0, Defaults.tcpConnectionBacklog);
myLog.l(Log.DEBUG, "Data socket pasv() listen successful");
return server.getLocalPort();
} catch(IOException e) {
myLog.l(Log.ERROR, "Data socket creation error");
clearState();
return 0;
}
}
public boolean onPort(InetAddress remoteAddr, int remotePort) {
clearState();
this.remoteAddr = remoteAddr;
this.remotePort = remotePort;
return true;
}
public Socket onTransfer() {
if(server == null) {
// We're in PORT mode (not PASV)
if(remoteAddr == null || remotePort == 0) {
myLog.l(Log.INFO, "PORT mode but not initialized correctly");
clearState();
return null;
}
Socket socket;
try {
socket = new Socket(remoteAddr, remotePort);
} catch (IOException e) {
myLog.l(Log.INFO,
"Couldn't open PORT data socket to: " +
remoteAddr.toString() + ":" + remotePort);
clearState();
return null;
}
// Kill the socket if nothing happens for X milliseconds
try {
socket.setSoTimeout(Defaults.SO_TIMEOUT_MS);
} catch (Exception e) {
myLog.l(Log.ERROR, "Couldn't set SO_TIMEOUT");
clearState();
return null;
}
return socket;
} else {
// We're in PASV mode (not PORT)
Socket socket = null;
try {
socket = server.accept();
myLog.l(Log.DEBUG, "onTransfer pasv accept successful");
} catch (Exception e) {
myLog.l(Log.INFO, "Exception accepting PASV socket");
socket = null;
}
clearState();
return socket; // will be null if error occurred
}
}
/**
* Return the port number that the remote client should be informed of (in the body
* of the PASV response).
* @return The port number, or -1 if error.
*/
public int getPortNumber() {
if(server != null) {
return server.getLocalPort(); // returns -1 if serversocket is unbound
} else {
return -1;
}
}
public InetAddress getPasvIp() {
//String retVal = server.getInetAddress().getHostAddress();
return FTPServerService.getWifiIp();
}
public void reportTraffic(long bytes) {
// ignore, we don't care about how much traffic goes over wifi.
}
}