package com.amaze.filemanager.services.ftpservice;
/**
* Created by yashwanthreddyg on 09-06-2016.
*/
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.WifiManager;
import android.os.Environment;
import android.os.IBinder;
import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.util.Log;
import com.amaze.filemanager.R;
import org.apache.commons.compress.utils.IOUtils;
import org.apache.ftpserver.ConnectionConfigFactory;
import org.apache.ftpserver.FtpServer;
import org.apache.ftpserver.FtpServerFactory;
import org.apache.ftpserver.ftplet.Authority;
import org.apache.ftpserver.ftplet.FtpException;
import org.apache.ftpserver.listener.ListenerFactory;
import org.apache.ftpserver.ssl.SslConfigurationFactory;
import org.apache.ftpserver.usermanager.impl.BaseUser;
import org.apache.ftpserver.usermanager.impl.WritePermission;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
public class FTPService extends Service implements Runnable {
public static final int DEFAULT_PORT = 2211;
public static final String DEFAULT_USERNAME = "";
public static final int DEFAULT_TIMEOUT = 600; // default timeout, in sec
public static final boolean DEFAULT_SECURE = false;
public static final String PORT_PREFERENCE_KEY = "ftpPort";
public static final String KEY_PREFERENCE_PATH = "ftp_path";
public static final String KEY_PREFERENCE_USERNAME = "ftp_username";
public static final String KEY_PREFERENCE_PASSWORD = "ftp_password";
public static final String KEY_PREFERENCE_TIMEOUT = "ftp_timeout";
public static final String KEY_PREFERENCE_SECURE = "ftp_secure";
public static final String DEFAULT_PATH = Environment.getExternalStorageDirectory().getAbsolutePath();
private static final String TAG = FTPService.class.getSimpleName();
/**
* TODO: 25/10/16 This is ugly
*/
private static int port = 2211;
// Service will (global) broadcast when server start/stop
static public final String ACTION_STARTED = "com.amaze.filemanager.services.ftpservice.FTPReceiver.FTPSERVER_STARTED";
static public final String ACTION_STOPPED = "com.amaze.filemanager.services.ftpservice.FTPReceiver.FTPSERVER_STOPPED";
static public final String ACTION_FAILEDTOSTART = "com.amaze.filemanager.services.ftpservice.FTPReceiver.FTPSERVER_FAILEDTOSTART";
// RequestStartStopReceiver listens for these actions to start/stop this server
static public final String ACTION_START_FTPSERVER = "com.amaze.filemanager.services.ftpservice.FTPReceiver.ACTION_START_FTPSERVER";
static public final String ACTION_STOP_FTPSERVER = "com.amaze.filemanager.services.ftpservice.FTPReceiver.ACTION_STOP_FTPSERVER";
private String username, password;
private boolean isPasswordProtected = false;
private FtpServer server;
protected static Thread serverThread = null;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
int attempts = 10;
while (serverThread != null) {
if (attempts > 0) {
attempts--;
FTPService.sleepIgnoreInterupt(1000);
} else {
return START_STICKY;
}
}
serverThread = new Thread(this);
serverThread.start();
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void run() {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
FtpServerFactory serverFactory = new FtpServerFactory();
ConnectionConfigFactory connectionConfigFactory = new ConnectionConfigFactory();
connectionConfigFactory.setAnonymousLoginEnabled(true);
serverFactory.setConnectionConfig(connectionConfigFactory.createConnectionConfig());
String usernamePreference = preferences.getString(KEY_PREFERENCE_USERNAME, DEFAULT_USERNAME);
if (!usernamePreference.equals(DEFAULT_USERNAME)) {
username = usernamePreference;
password = preferences.getString(KEY_PREFERENCE_PASSWORD, "");
isPasswordProtected = true;
}
BaseUser user = new BaseUser();
if (!isPasswordProtected) {
user.setName("anonymous");
} else {
user.setName(username);
user.setPassword(password);
}
user.setHomeDirectory(preferences.getString(KEY_PREFERENCE_PATH, DEFAULT_PATH));
List<Authority> list = new ArrayList<>();
list.add(new WritePermission());
user.setAuthorities(list);
try {
serverFactory.getUserManager().save(user);
} catch (FtpException e) {
e.printStackTrace();
}
ListenerFactory fac = new ListenerFactory();
port = preferences.getInt(PORT_PREFERENCE_KEY, DEFAULT_PORT);
if (preferences.getBoolean(KEY_PREFERENCE_SECURE, DEFAULT_SECURE)) {
SslConfigurationFactory sslConfigurationFactory = new SslConfigurationFactory();
File file;
try {
InputStream stream = getResources().openRawResource(R.raw.key);
file = File.createTempFile("keystore", "bks");
FileOutputStream outputStream = new FileOutputStream(file);
IOUtils.copy(stream, outputStream);
} catch (Exception e) {
e.printStackTrace();
file = null;
}
if (file != null) {
sslConfigurationFactory.setKeystoreFile(file);
sslConfigurationFactory.setKeystorePassword("vishal007");
fac.setSslConfiguration(sslConfigurationFactory.createSslConfiguration());
fac.setImplicitSsl(true);
} else {
// no keystore found
preferences.edit().putBoolean(KEY_PREFERENCE_SECURE, false).apply();
}
}
fac.setPort(port);
fac.setIdleTimeout(preferences.getInt(KEY_PREFERENCE_TIMEOUT, DEFAULT_TIMEOUT));
serverFactory.addListener("default", fac.createListener());
try {
server = serverFactory.createServer();
server.start();
sendBroadcast(new Intent(FTPService.ACTION_STARTED));
} catch (Exception e) {
sendBroadcast(new Intent(FTPService.ACTION_FAILEDTOSTART));
}
}
@Override
public void onDestroy() {
Log.i(TAG, "onDestroy() Stopping server");
if (serverThread == null) {
Log.w(TAG, "Stopping with null serverThread");
return;
}
serverThread.interrupt();
try {
serverThread.join(10000); // wait 10 sec for server thread to finish
} catch (InterruptedException e) {
}
if (serverThread.isAlive()) {
Log.w(TAG, "Server thread failed to exit");
} else {
Log.d(TAG, "serverThread join()ed ok");
serverThread = null;
}
if (server != null) {
server.stop();
sendBroadcast(new Intent(FTPService.ACTION_STOPPED));
}
Log.d(TAG, "FTPServerService.onDestroy() finished");
}
//Restart the service if the app is closed from the recent list
@Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
Intent restartService = new Intent(getApplicationContext(), this.getClass());
restartService.setPackage(getPackageName());
PendingIntent restartServicePI = PendingIntent.getService(
getApplicationContext(), 1, restartService, PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmService = (AlarmManager) getApplicationContext()
.getSystemService(Context.ALARM_SERVICE);
alarmService.set(AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + 2000, restartServicePI);
}
public static boolean isRunning() {
// return true if and only if a server Thread is running
if (serverThread == null) {
Log.d(TAG, "Server is not running (null serverThread)");
return false;
}
if (!serverThread.isAlive()) {
Log.d(TAG, "serverThread non-null but !isAlive()");
} else {
Log.d(TAG, "Server is alive");
}
return true;
}
public static void sleepIgnoreInterupt(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException ignored) {
}
}
public static boolean isConnectedToLocalNetwork(Context context) {
boolean connected = false;
ConnectivityManager cm = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni = cm.getActiveNetworkInfo();
connected = ni != null
&& ni.isConnected()
&& (ni.getType() & (ConnectivityManager.TYPE_WIFI | ConnectivityManager.TYPE_ETHERNET)) != 0;
if (!connected) {
Log.d(TAG, "isConnectedToLocalNetwork: see if it is an WIFI AP");
WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
try {
Method method = wm.getClass().getDeclaredMethod("isWifiApEnabled");
connected = (Boolean) method.invoke(wm);
} catch (Exception e) {
e.printStackTrace();
}
}
if (!connected) {
Log.d(TAG, "isConnectedToLocalNetwork: see if it is an USB AP");
try {
for (NetworkInterface netInterface : Collections.list(NetworkInterface
.getNetworkInterfaces())) {
if (netInterface.getDisplayName().startsWith("rndis")) {
connected = true;
}
}
} catch (SocketException e) {
e.printStackTrace();
}
}
return connected;
}
public static boolean isConnectedToWifi(Context context) {
ConnectivityManager cm = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni = cm.getActiveNetworkInfo();
return ni != null && ni.isConnected()
&& ni.getType() == ConnectivityManager.TYPE_WIFI;
}
public static InetAddress getLocalInetAddress(Context context) {
if (!isConnectedToLocalNetwork(context)) {
Log.e(TAG, "getLocalInetAddress called and no connection");
return null;
}
if (isConnectedToWifi(context)) {
WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
int ipAddress = wm.getConnectionInfo().getIpAddress();
if (ipAddress == 0)
return null;
return intToInet(ipAddress);
}
try {
Enumeration<NetworkInterface> netinterfaces = NetworkInterface
.getNetworkInterfaces();
while (netinterfaces.hasMoreElements()) {
NetworkInterface netinterface = netinterfaces.nextElement();
Enumeration<InetAddress> adresses = netinterface.getInetAddresses();
while (adresses.hasMoreElements()) {
InetAddress address = adresses.nextElement();
// this is the condition that sometimes gives problems
if (!address.isLoopbackAddress()
&& !address.isLinkLocalAddress())
return address;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static InetAddress intToInet(int value) {
byte[] bytes = new byte[4];
for (int i = 0; i < 4; i++) {
bytes[i] = byteOfInt(value, i);
}
try {
return InetAddress.getByAddress(bytes);
} catch (UnknownHostException e) {
// This only happens if the byte array has a bad length
return null;
}
}
public static byte byteOfInt(int value, int which) {
int shift = which * 8;
return (byte) (value >> shift);
}
public static int getPort() {
return port;
}
public static boolean isPortAvailable(int port) {
ServerSocket ss = null;
DatagramSocket ds = null;
try {
ss = new ServerSocket(port);
ss.setReuseAddress(true);
ds = new DatagramSocket(port);
ds.setReuseAddress(true);
return true;
} catch (IOException e) {
} finally {
if (ds != null) {
ds.close();
}
if (ss != null) {
try {
ss.close();
} catch (IOException e) {
/* should not be thrown */
}
}
}
return false;
}
}