Merge pull request #64 in ITERATE/cyberduck from feature/windows/9074 to master
[cyberduck.git] / source / ch / cyberduck / core / ftp / FTPDataFallback.java
blobb45e81dc58f788a739c6088997bff87a0b59b37c
1 package ch.cyberduck.core.ftp;
3 /*
4 * Copyright (c) 2002-2013 David Kocher. All rights reserved.
5 * http://cyberduck.ch/
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * Bug fixes, suggestions and comments should be sent to feedback@cyberduck.ch
20 import ch.cyberduck.core.DisabledHostKeyCallback;
21 import ch.cyberduck.core.DisabledLoginCallback;
22 import ch.cyberduck.core.DisabledPasswordStore;
23 import ch.cyberduck.core.DisabledTranscriptListener;
24 import ch.cyberduck.core.HostPasswordStore;
25 import ch.cyberduck.core.LoginCallback;
26 import ch.cyberduck.core.LoginConnectionService;
27 import ch.cyberduck.core.PathCache;
28 import ch.cyberduck.core.ProgressListener;
29 import ch.cyberduck.core.exception.AccessDeniedException;
30 import ch.cyberduck.core.exception.BackgroundException;
31 import ch.cyberduck.core.exception.ConnectionTimeoutException;
32 import ch.cyberduck.core.exception.InteroperabilityException;
33 import ch.cyberduck.core.exception.NotfoundException;
34 import ch.cyberduck.core.preferences.PreferencesFactory;
36 import org.apache.log4j.Logger;
38 import java.io.IOException;
40 /**
41 * @version $Id$
43 public class FTPDataFallback {
44 private static final Logger log = Logger.getLogger(FTPDataFallback.class);
46 private final FTPSession session;
48 private final HostPasswordStore keychain;
50 private final LoginCallback prompt;
52 public FTPDataFallback(final FTPSession session) {
53 this(session, new DisabledPasswordStore(), new DisabledLoginCallback());
56 public FTPDataFallback(final FTPSession session, final HostPasswordStore keychain, final LoginCallback prompt) {
57 this.session = session;
58 this.keychain = keychain;
59 this.prompt = prompt;
62 /**
63 * @param action Action that needs to open a data connection
64 * @param listener Progress callback
65 * @return True if action was successful
67 protected <T> T data(final DataConnectionAction<T> action, final ProgressListener listener)
68 throws IOException, BackgroundException {
69 try {
70 // Make sure to always configure data mode because connect event sets defaults.
71 if(session.getConnectMode().equals(FTPConnectMode.passive)) {
72 session.getClient().enterLocalPassiveMode();
74 else if(session.getConnectMode().equals(FTPConnectMode.active)) {
75 session.getClient().enterLocalActiveMode();
77 return action.execute();
79 catch(ConnectionTimeoutException failure) {
80 log.warn(String.format("Timeout opening data socket %s", failure.getMessage()));
81 // Fallback handling
82 if(PreferencesFactory.get().getBoolean("ftp.connectmode.fallback")) {
83 try {
84 try {
85 session.getClient().completePendingCommand();
86 // Expect 421 response
87 log.warn(String.format("Aborted connection %d %s",
88 session.getClient().getReplyCode(), session.getClient().getReplyString()));
90 catch(IOException e) {
91 log.warn(String.format("Ignore failure completing pending command %s", e.getMessage()));
92 // Reconnect
93 new LoginConnectionService(
94 prompt,
95 new DisabledHostKeyCallback(),
96 keychain,
97 listener,
98 new DisabledTranscriptListener()
99 ).connect(session, PathCache.empty());
101 return this.fallback(action);
103 catch(BackgroundException e) {
104 log.warn(String.format("Connect mode fallback failed with %s", e.getMessage()));
105 // Throw original error message
108 throw failure;
110 catch(InteroperabilityException | NotfoundException | AccessDeniedException failure) {
111 log.warn(String.format("Server denied data socket operation with %s", failure.getMessage()));
112 // Fallback handling
113 if(PreferencesFactory.get().getBoolean("ftp.connectmode.fallback")) {
114 try {
115 return this.fallback(action);
117 catch(BackgroundException e) {
118 log.warn(String.format("Connect mode fallback failed with %s", e.getMessage()));
119 // Throw original error message
122 throw failure;
127 * @param action Action that needs to open a data connection
128 * @return True if action was successful
130 protected <T> T fallback(final DataConnectionAction<T> action) throws BackgroundException {
131 // Fallback to other connect mode
132 if(session.getClient().getDataConnectionMode() == FTPClient.PASSIVE_LOCAL_DATA_CONNECTION_MODE) {
133 log.warn("Fallback to active data connection");
134 session.getClient().enterLocalActiveMode();
136 else if(session.getClient().getDataConnectionMode() == FTPClient.ACTIVE_LOCAL_DATA_CONNECTION_MODE) {
137 log.warn("Fallback to passive data connection");
138 session.getClient().enterLocalPassiveMode();
140 return action.execute();