/* * This program 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. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * * For information about the authors of this project Have a look * at the AUTHORS file in the root of this project. */ package net.sourceforge.fullsync.cli; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; import java.nio.channels.FileChannel; import java.nio.file.Paths; import java.rmi.RemoteException; import java.util.Date; import net.sourceforge.fullsync.ExceptionHandler; import net.sourceforge.fullsync.Profile; import net.sourceforge.fullsync.ProfileManager; import net.sourceforge.fullsync.Synchronizer; import net.sourceforge.fullsync.TaskTree; import net.sourceforge.fullsync.Util; import net.sourceforge.fullsync.impl.ConfigurationPreferences; import net.sourceforge.fullsync.remote.RemoteController; import net.sourceforge.fullsync.ui.GuiController; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.OptionBuilder; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.commons.cli.PosixParser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Main { // NO_UCD private static Options options; private static void initOptions() { options = new Options(); options.addOption("h", "help", false, "this help"); options.addOption("v", "verbose", false, "verbose output to stdout"); options.addOption("V", "version", false, "display the version and exit"); options.addOption("m", "minimized", false, "starts fullsync gui in system tray "); OptionBuilder.withLongOpt("profiles-file"); OptionBuilder.withDescription("uses the specified file instead of profiles.xml"); OptionBuilder.hasArg(); OptionBuilder.withArgName("filename"); options.addOption(OptionBuilder.create("P")); OptionBuilder.withLongOpt("run"); OptionBuilder.withDescription("run the specified profile"); OptionBuilder.hasArg(); OptionBuilder.withArgName("profile"); options.addOption(OptionBuilder.create("r")); options.addOption("d", "daemon", false, "disables the gui and runs in daemon mode with scheduler"); // REVISIT somehow i don't like -p so much, but it's the standard for specifying ports. // the problem is that it implies "enable remote connection" in our case what // i consider more important, espec because the port is optional. OptionBuilder.withLongOpt("remoteport"); OptionBuilder.withDescription("accept incoming connection on the specified port or 10000"); OptionBuilder.hasOptionalArg(); OptionBuilder.withArgName("port"); options.addOption(OptionBuilder.create("p")); OptionBuilder.withLongOpt("password"); OptionBuilder.withDescription("password for incoming connections"); OptionBuilder.hasOptionalArg(); OptionBuilder.withArgName("passwd"); options.addOption(OptionBuilder.create("a")); // + interactive mode } private static void printHelp() { HelpFormatter formatter = new HelpFormatter(); formatter.printHelp("fullsync [-hvrdp]", options); } public static String getConfigDir() { String configDir = System.getProperty("net.sourceforge.fullsync.configDir"); if (null == configDir) { configDir = System.getenv("XDG_CONFIG_HOME"); } if (null == configDir) { configDir = System.getProperty("user.home") + File.separator + ".config"; } configDir = configDir + File.separator + "fullsync" + File.separator; File dir = new File(configDir); if (!dir.exists()) { dir.mkdirs(); } return configDir; } public static String getLogFileName() { return Paths.get(getConfigDir(), "fullsync.log").toFile().getAbsolutePath(); } private static void backupFile(final File old, final File current, final String backupName) throws IOException { try (FileInputStream fis = new FileInputStream(old); FileOutputStream fos = new FileOutputStream(current)) { FileChannel in = fis.getChannel(); FileChannel out = fos.getChannel(); in.transferTo(0, in.size(), out); in.close(); out.close(); old.renameTo(new File(backupName)); } } public static void main(String[] args) { initOptions(); String configDir = getConfigDir(); try { CommandLineParser parser = new PosixParser(); CommandLine line = null; try { line = parser.parse(options, args); } catch (ParseException ex) { System.err.println(ex.getMessage()); printHelp(); return; } if (line.hasOption('V')) { System.out.println(String.format("FullSync version %s", Util.getFullSyncVersion())); System.exit(0); } // Apply modifying options if (!line.hasOption("v")) { System.setErr(new PrintStream(new FileOutputStream(getLogFileName()))); } if (line.hasOption("h")) { printHelp(); return; } // Initialize basic facilities // upgrade code... do { File newPreferences = new File(configDir + "preferences.properties"); File oldPreferences = new File("preferences.properties"); if (!newPreferences.exists() && oldPreferences.exists()) { backupFile(oldPreferences, newPreferences, "preferences_old.properties"); } } while (false); // variable scope final ConfigurationPreferences preferences = new ConfigurationPreferences(configDir + "preferences.properties"); String profilesFile = "profiles.xml"; if (line.hasOption("P")) { profilesFile = line.getOptionValue("P"); } else { profilesFile = configDir + "profiles.xml"; // upgrade code... File newProfiles = new File(profilesFile); File oldProfiles = new File("profiles.xml"); if (!newProfiles.exists()) { if (!oldProfiles.exists()) { // on windows FullSync 0.9.1 installs itself into %ProgramFiles%\FullSync while 0.10.0 installs itself into %ProgramFiles%\FullSync\FullSync by default oldProfiles = new File(".." + File.separator + "profiles.xml"); } if (oldProfiles.exists()) { backupFile(oldProfiles, newProfiles, "profiles_old.xml"); } } } ProfileManager profileManager = new ProfileManager(profilesFile); final Synchronizer sync = new Synchronizer(); // Apply executing options if (line.hasOption("r")) { Profile p = profileManager.getProfile(line.getOptionValue("r")); TaskTree tree = sync.executeProfile(p, false); sync.performActions(tree); p.setLastUpdate(new Date()); profileManager.save(); return; } boolean activateRemote = false; int port = 10000; String password = "admin"; RemoteException listenerStarupException = null; if (line.hasOption("p")) { activateRemote = true; try { String portStr = line.getOptionValue("p"); port = Integer.parseInt(portStr); } catch (NumberFormatException e) { e.printStackTrace(); } if (line.hasOption("a")) { password = line.getOptionValue("a"); } } else { activateRemote = preferences.listeningForRemoteConnections(); port = preferences.getRemoteConnectionsPort(); password = preferences.getRemoteConnectionsPassword(); } if (activateRemote) { try { Logger logger = LoggerFactory.getLogger("FullSync"); RemoteController.getInstance().startServer(port, password, profileManager, sync); logger.info("Remote Interface available on port: " + port); } catch (RemoteException e) { ExceptionHandler.reportException(e); listenerStarupException = e; } } if (line.hasOption("d")) { profileManager.addSchedulerListener(profile -> { TaskTree tree = sync.executeProfile(profile, false); if (tree == null) { profile.setLastError(1, "An error occured while comparing filesystems."); } else { int errorLevel = sync.performActions(tree); if (errorLevel > 0) { profile.setLastError(errorLevel, "An error occured while copying files."); } else { profile.setLastUpdate(new Date()); } } }); profileManager.startScheduler(); } else { try { GuiController guiController = GuiController.initialize(preferences, profileManager, sync); guiController.startGui(line.hasOption('m')); if (!line.hasOption('P') && !preferences.getHelpShown() && (null == System.getProperty("net.sourceforge.fullsync.skipHelp"))) { preferences.setHelpShown(true); preferences.save(); File f = new File("docs/manual/manual.html"); if (f.exists()) { GuiController.launchProgram(f.getAbsolutePath()); } } if (listenerStarupException != null) { ExceptionHandler.reportException("Unable to start incoming connections listener.", listenerStarupException); } if (preferences.getAutostartScheduler()) { profileManager.startScheduler(); } guiController.run(); guiController.disposeGui(); } catch (Exception ex) { ExceptionHandler.reportException(ex); } finally { profileManager.save(); } // FIXME [Michele] For some reasons there is some thread still alive if you run the remote interface RemoteController.getInstance().stopServer(); if (null == System.getProperty("net.sourceforge.fullsync.skipExit")) { System.exit(-1); } } } catch (Exception ex) { ExceptionHandler.reportException(ex); } } }