001/* 002 * Syncany, www.syncany.org 003 * Copyright (C) 2011-2016 Philipp C. Heckel <philipp.heckel@gmail.com> 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU General Public License as published by 007 * the Free Software Foundation, either version 3 of the License, or 008 * (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU General Public License for more details. 014 * 015 * You should have received a copy of the GNU General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018package org.syncany.cli; 019 020import java.util.List; 021 022import joptsimple.OptionParser; 023import joptsimple.OptionSet; 024import joptsimple.OptionSpec; 025 026import org.syncany.config.to.ConfigTO; 027import org.syncany.operations.OperationResult; 028import org.syncany.operations.init.ConnectOperation; 029import org.syncany.operations.init.ConnectOperationOptions; 030import org.syncany.operations.init.ConnectOperationOptions.ConnectOptionsStrategy; 031import org.syncany.operations.init.ConnectOperationResult; 032import org.syncany.operations.init.ConnectOperationResult.ConnectResultCode; 033import org.syncany.plugins.transfer.StorageTestResult; 034import org.syncany.plugins.transfer.TransferSettings; 035 036import static java.util.Arrays.asList; 037 038public class ConnectCommand extends AbstractInitCommand { 039 public ConnectCommand() { 040 super(); 041 } 042 043 @Override 044 public CommandScope getRequiredCommandScope() { 045 return CommandScope.UNINITIALIZED_LOCALDIR; 046 } 047 048 @Override 049 public boolean canExecuteInDaemonScope() { 050 return false; 051 } 052 053 @Override 054 public int execute(String[] operationArgs) throws Exception { 055 boolean retryNeeded = true; 056 boolean performOperation = true; 057 058 ConnectOperationOptions operationOptions = parseOptions(operationArgs); 059 060 while (retryNeeded && performOperation) { 061 ConnectOperationResult operationResult = new ConnectOperation(operationOptions, this).execute(); 062 printResults(operationResult); 063 064 retryNeeded = operationResult.getResultCode() != ConnectResultCode.OK 065 && operationResult.getResultCode() != ConnectResultCode.NOK_DECRYPT_ERROR; 066 067 if (retryNeeded) { 068 performOperation = isInteractive && askRetryConnection(); 069 070 if (performOperation) { 071 updateTransferSettings(operationOptions.getConfigTO().getTransferSettings()); 072 } 073 } 074 } 075 076 return 0; 077 } 078 079 @Override 080 public ConnectOperationOptions parseOptions(String[] operationArgs) throws Exception { 081 ConnectOperationOptions operationOptions = new ConnectOperationOptions(); 082 083 OptionParser parser = new OptionParser(); 084 OptionSpec<String> optionPlugin = parser.acceptsAll(asList("P", "plugin")).withRequiredArg(); 085 OptionSpec<String> optionPluginOpts = parser.acceptsAll(asList("o", "plugin-option")).withRequiredArg(); 086 OptionSpec<Void> optionAddDaemon = parser.acceptsAll(asList("n", "add-daemon")); 087 OptionSpec<String> optionPassword = parser.acceptsAll(asList("password")).withRequiredArg(); 088 089 OptionSet options = parser.parse(operationArgs); 090 List<?> nonOptionArgs = options.nonOptionArguments(); 091 092 // Set interactivity mode 093 isInteractive = !options.has(optionPlugin) && !options.has(optionPassword); 094 095 // Plugin 096 TransferSettings transferSettings = null; 097 098 if (nonOptionArgs.size() == 1) { 099 String connectLink = (String) nonOptionArgs.get(0); 100 101 operationOptions.setStrategy(ConnectOptionsStrategy.CONNECTION_LINK); 102 operationOptions.setConnectLink(connectLink); 103 104 transferSettings = null; 105 } 106 else if (nonOptionArgs.size() == 0) { 107 operationOptions.setStrategy(ConnectOptionsStrategy.CONNECTION_TO); 108 operationOptions.setConnectLink(null); 109 110 transferSettings = createTransferSettingsFromOptions(options, optionPlugin, optionPluginOpts); 111 } 112 else { 113 throw new Exception("Invalid syntax."); 114 } 115 116 ConfigTO configTO = createConfigTO(transferSettings); 117 118 operationOptions.setLocalDir(localDir); 119 operationOptions.setConfigTO(configTO); 120 operationOptions.setDaemon(options.has(optionAddDaemon)); 121 operationOptions.setPassword(validateAndGetPassword(options, optionPassword)); 122 123 return operationOptions; 124 } 125 126 private String validateAndGetPassword(OptionSet options, OptionSpec<String> optionPassword) { 127 if (!isInteractive) { 128 if (options.has(optionPassword)) { 129 return options.valueOf(optionPassword); 130 } 131 else { 132 return null; // No encryption, no password. 133 } 134 } 135 else { 136 return null; // Will be set in callback! 137 } 138 } 139 140 @Override 141 public void printResults(OperationResult operationResult) { 142 ConnectOperationResult concreteOperationResult = (ConnectOperationResult) operationResult; 143 144 if (concreteOperationResult.getResultCode() == ConnectResultCode.OK) { 145 out.println(); 146 out.println("Repository connected, and local folder initialized."); 147 out.println("You can now use 'sy up' and 'sy down' to sync your files."); 148 out.println(); 149 150 if (concreteOperationResult.isAddedToDaemon()) { 151 out.println("To automatically sync this folder, simply restart the daemon with 'sy daemon restart'."); 152 out.println(); 153 } 154 } 155 else if (concreteOperationResult.getResultCode() == ConnectResultCode.NOK_TEST_FAILED) { 156 StorageTestResult testResult = concreteOperationResult.getTestResult(); 157 out.println(); 158 159 if (!testResult.isTargetCanConnect()) { 160 out.println("ERROR: Cannot connect to the repository, because the connection to the storage backend failed."); 161 out.println(" Possible reasons for this could be connectivity issues (are you connect to the Internet?),"); 162 out.println(" or invalid user credentials (are username/password valid?)."); 163 } 164 else if (!testResult.isTargetExists()) { 165 out.println("ERROR: Cannot connect to the repository, because the target does not exist."); 166 out.println(" Please check if it really exists and if you can read from it / write to it."); 167 } 168 else if (!testResult.isTargetCanWrite()) { 169 out.println("ERROR: Cannot connect to the repository, because the target is not writable. This is probably"); 170 out.println(" a permission issue (does the user have write permissions to the target?)."); 171 } 172 else if (!testResult.isRepoFileExists()) { 173 out.println("ERROR: Cannot connect to the repository, because no repo file was found ('syncany' file)."); 174 out.println(" Are you sure that this is a valid Syncany repository? Use 'sy init' to create a new one."); 175 } 176 else { 177 out.println("ERROR: Cannot connect to the repository."); 178 } 179 180 out.println(); 181 printTestResult(testResult); 182 } 183 else if (concreteOperationResult.getResultCode() == ConnectResultCode.NOK_DECRYPT_ERROR) { 184 out.println(); 185 out.println("ERROR: Invalid password or corrupt ciphertext."); 186 out.println(); 187 out.println("The reason for this might be an invalid password, or that the"); 188 out.println("link/files have been tampered with."); 189 out.println(); 190 } 191 else { 192 out.println(); 193 out.println("ERROR: Cannot connect to repository. Unknown error code: " + operationResult); 194 out.println(); 195 } 196 } 197}