/* * Copyright (C) 2016 mendhak * * This file is part of GPSLogger for Android. * * GPSLogger for Android 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 2 of the License, or * (at your option) any later version. * * GPSLogger for Android 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 GPSLogger for Android. If not, see <http://www.gnu.org/licenses/>. */ package com.mendhak.gpslogger.senders.ftp; import com.mendhak.gpslogger.common.AppSettings; import com.mendhak.gpslogger.common.network.Networks; import com.mendhak.gpslogger.common.Strings; import com.mendhak.gpslogger.common.events.UploadEvents; import com.mendhak.gpslogger.common.slf4j.LoggingOutputStream; import com.mendhak.gpslogger.common.slf4j.Logs; import com.path.android.jobqueue.Job; import com.path.android.jobqueue.Params; import de.greenrobot.event.EventBus; import org.apache.commons.net.PrintCommandListener; import org.apache.commons.net.ftp.FTP; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPFile; import org.apache.commons.net.ftp.FTPSClient; import org.slf4j.Logger; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; public class FtpJob extends Job { private static final Logger LOG = Logs.of(FtpJob.class); String server; int port; String username; String password; boolean useFtps; String protocol; boolean implicit; File gpxFile; String fileName; String directory; static UploadEvents.Ftp jobResult; static ArrayList<String> ftpServerResponses; protected FtpJob(String server, int port, String username, String password, String directory, boolean useFtps, String protocol, boolean implicit, File gpxFile, String fileName) { super(new Params(1).requireNetwork().persist().addTags(getJobTag(gpxFile))); this.server = server; this.port = port; this.username = username; this.password = password; this.useFtps = useFtps; this.protocol = protocol; this.implicit = implicit; this.gpxFile = gpxFile; this.fileName = fileName; this.directory = directory; ftpServerResponses = new ArrayList<>(); jobResult = null; } public synchronized static boolean upload(String server, String username, String password, String directory, int port, boolean useFtps, String protocol, boolean implicit, File gpxFile, String fileName) { FTPClient client; try { if (useFtps) { client = new FTPSClient(protocol, implicit); KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(Networks.getKnownServersStore(AppSettings.getInstance()), null); KeyManager km = kmf.getKeyManagers()[0]; ((FTPSClient) client).setKeyManager(km); ((FTPSClient) client).setTrustManager(Networks.getTrustManager(AppSettings.getInstance())); } else { client = new FTPClient(); } } catch (Exception e) { jobResult = new UploadEvents.Ftp().failed( "Could not create FTP Client" , e); LOG.error("Could not create FTP Client", e); return false; } try { client.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(new LoggingOutputStream(LOG)))); client.setDefaultTimeout(60000); client.setConnectTimeout(60000); client.connect(server, port); client.setSoTimeout(60000); client.setDataTimeout(60000); logServerReply(client); if (client.login(username, password)) { if(useFtps){ ((FTPSClient)client).execPBSZ(0); logServerReply(client); ((FTPSClient)client).execPROT("P"); logServerReply(client); } client.enterLocalPassiveMode(); logServerReply(client); LOG.debug("Uploading file to FTP server " + server); LOG.debug("Checking for FTP directory " + directory); FTPFile[] existingDirectory = client.listFiles(directory); logServerReply(client); if (existingDirectory.length <= 0) { LOG.debug("Attempting to create FTP directory " + directory); ftpCreateDirectoryTree(client, directory); logServerReply(client); } FileInputStream inputStream = new FileInputStream(gpxFile); client.changeWorkingDirectory(directory); client.setFileType(FTP.BINARY_FILE_TYPE); boolean result = client.storeFile(fileName, inputStream); inputStream.close(); logServerReply(client); if (result) { LOG.debug("Successfully FTPd file " + fileName); } else { jobResult = new UploadEvents.Ftp().failed( "Failed to FTP file " + fileName , null); LOG.debug("Failed to FTP file " + fileName); return false; } } else { logServerReply(client); jobResult = new UploadEvents.Ftp().failed( "Could not log in to FTP server" , null); LOG.debug("Could not log in to FTP server"); return false; } } catch (Exception e) { logServerReply(client); jobResult = new UploadEvents.Ftp().failed( "Could not connect or upload to FTP server.", e); LOG.error("Could not connect or upload to FTP server.", e); return false; } finally { try { client.logout(); logServerReply(client); client.disconnect(); logServerReply(client); } catch (Exception e) { if(jobResult == null){ jobResult = new UploadEvents.Ftp().failed( "Could not logout or disconnect", e); } LOG.error("Could not logout or disconnect", e); return false; } } return true; } private static void ftpCreateDirectoryTree(FTPClient client, String dirTree) throws IOException { boolean dirExists = true; //tokenize the string and attempt to change into each directory level. If you cannot, then start creating. String[] directories = dirTree.split("/"); for (String dir : directories) { if (dir.length() > 0) { if (dirExists) { dirExists = client.changeWorkingDirectory(dir); logServerReply(client); } if (!dirExists) { client.makeDirectory(dir); logServerReply(client); client.changeWorkingDirectory(dir); logServerReply(client); } } } } private static void logServerReply(FTPClient client) { String singleReply = client.getReplyString(); if(!Strings.isNullOrEmpty(singleReply)){ ftpServerResponses.add(singleReply); } String[] replies = client.getReplyStrings(); if (replies != null && replies.length > 0) { for (String aReply : replies) { if(!Strings.isNullOrEmpty(aReply)){ ftpServerResponses.add(aReply); } } } } @Override public void onAdded() { } @Override public void onRun() throws Throwable { if (upload(server, username, password, directory, port, useFtps, protocol, implicit, gpxFile, fileName)) { EventBus.getDefault().post(new UploadEvents.Ftp().succeeded()); } else { jobResult.ftpMessages = ftpServerResponses; EventBus.getDefault().post(jobResult); } } @Override protected void onCancel() { } @Override protected boolean shouldReRunOnThrowable(Throwable throwable) { EventBus.getDefault().post(new UploadEvents.Ftp().failed("Could not FTP file", throwable)); LOG.error("Could not FTP file", throwable); return false; } public static String getJobTag(File testFile) { return "FTP"+testFile.getName(); } }