/** * This file Copyright (c) 2005-2010 Aptana, Inc. This program is * dual-licensed under both the Aptana Public License and the GNU General * Public license. You may elect to use one or the other of these licenses. * * This program is distributed in the hope that it will be useful, but * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or * NONINFRINGEMENT. Redistribution, except as permitted by whichever of * the GPL or APL you select, is prohibited. * * 1. For the GPL license (GPL), you can redistribute and/or modify this * program under the terms of the GNU General Public License, * Version 3, as published by the Free Software Foundation. You should * have received a copy of the GNU General Public License, Version 3 along * with this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Aptana provides a special exception to allow redistribution of this file * with certain other free and open source software ("FOSS") code and certain additional terms * pursuant to Section 7 of the GPL. You may view the exception and these * terms on the web at http://www.aptana.com/legal/gpl/. * * 2. For the Aptana Public License (APL), this program and the * accompanying materials are made available under the terms of the APL * v1.0 which accompanies this distribution, and is available at * http://www.aptana.com/legal/apl/. * * You may view the GPL, Aptana's exception and additional terms, and the * APL in the file titled license.html at the root of the corresponding * plugin containing this source file. * * Any modifications to this file must keep this entire header intact. */ package com.aptana.ide.filesystem.ftp; import java.io.ByteArrayInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URI; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileInfo; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import com.aptana.ide.core.IdeLog; import com.aptana.ide.core.StringUtils; import com.aptana.ide.core.URLEncoder; import com.aptana.ide.core.io.CoreIOPlugin; import com.aptana.ide.core.io.InfiniteProgressMonitor; import com.aptana.ide.core.io.preferences.PreferenceUtils; import com.aptana.ide.core.io.vfs.ExtendedFileInfo; import com.aptana.ide.core.io.vfs.IConnectionFileManager; import com.aptana.ide.core.io.vfs.IExtendedFileInfo; import com.aptana.ide.core.io.vfs.IExtendedFileStore; import com.enterprisedt.net.ftp.FTPException; /** * @author Max Stepanov * */ public abstract class BaseFTPConnectionFileManager implements IConnectionFileManager { protected static final int TIMEOUT = 30000; protected static final int RETRY = 3; protected static final int RETRY_DELAY = 10000; protected static final int KEEPALIVE_INTERVAL = 15000; protected static final int TRANSFER_BUFFER_SIZE = 32768; protected static final int CHECK_CONNECTION_TIMEOUT = 30000; protected static final int CACHE_TTL = 60000; /* 1min */ protected static final String TMP_UPLOAD_PREFIX = "_tmp_upload."; //$NON-NLS-1$ protected static final Pattern PASS_COMMAND_PATTERN = Pattern.compile("^(.*PASS ).+$"); //$NON-NLS-1$ protected String host; protected int port; protected String login; protected char[] password = new char[0]; protected IPath basePath; protected String authId; private long lastOperationTime; private Map<IPath, ExtendedFileInfo> fileInfoCache; private Map<IPath, ExtendedFileInfo[]> fileInfosCache; protected void promptPassword(String title, String message) { password = CoreIOPlugin.getAuthenticationManager().promptPassword( authId, login, title, message); if (password == null) { password = new char[0]; throw new OperationCanceledException(); } } protected void getOrPromptPassword(String title, String message) { password = CoreIOPlugin.getAuthenticationManager().getPassword(authId); if (password == null) { password = new char[0]; promptPassword(title, message); } } protected synchronized void setCaching(boolean enabled) { if ((fileInfoCache != null) == enabled) { return; } if (enabled) { fileInfoCache = new ExpiringMap<IPath, ExtendedFileInfo>(CACHE_TTL); fileInfosCache = new ExpiringMap<IPath, ExtendedFileInfo[]>(CACHE_TTL); } else { fileInfoCache = null; fileInfosCache = null; } } /* (non-Javadoc) * @see com.aptana.ide.core.io.vfs.IConnectionFileManager#fetchInfo(org.eclipse.core.runtime.IPath, int, org.eclipse.core.runtime.IProgressMonitor) */ public synchronized IExtendedFileInfo fetchInfo(IPath path, int options, IProgressMonitor monitor) throws CoreException { monitor = Policy.monitorFor(monitor); monitor.beginTask(StringUtils.format(Messages.BaseFTPConnectionFileManager_gethering_details, path.toPortableString()), 2); try { ExtendedFileInfo fileInfo = getCachedFileInfo(path); if (fileInfo == null) { testOrConnect(monitor); try { fileInfo = fetchAndCacheFileInfo(path, options, monitor); } finally { setLastOperationTime(); } } return (IExtendedFileInfo) fileInfo.clone(); } finally { monitor.done(); } } /* (non-Javadoc) * @see com.aptana.ide.core.io.vfs.IConnectionFileManager#childInfos(org.eclipse.core.runtime.IPath, int, org.eclipse.core.runtime.IProgressMonitor) */ public synchronized IExtendedFileInfo[] childInfos(IPath path, int options, IProgressMonitor monitor) throws CoreException { monitor = Policy.monitorFor(monitor); monitor.beginTask(StringUtils.format(Messages.BaseFTPConnectionFileManager_gethering_details, path.toPortableString()), 2); options = (options & IExtendedFileStore.DETAILED); try { ExtendedFileInfo[] fileInfos = getCachedFileInfos(path); if (fileInfos == null) { testOrConnect(monitor); try { fileInfos = cache(path, fetchFiles(basePath.append(path), options, monitor)); for (ExtendedFileInfo fileInfo : fileInfos) { postProcessFileInfo(fileInfo, basePath.append(path), options, monitor); cache(path.append(fileInfo.getName()), fileInfo); } } catch (FileNotFoundException e) { return new IExtendedFileInfo[0]; } finally { setLastOperationTime(); } } return fileInfos.clone(); } finally { monitor.done(); } } /* (non-Javadoc) * @see com.aptana.ide.core.io.vfs.IConnectionFileManager#childNames(org.eclipse.core.runtime.IPath, int, org.eclipse.core.runtime.IProgressMonitor) */ public synchronized String[] childNames(IPath path, int options, IProgressMonitor monitor) throws CoreException { monitor = Policy.monitorFor(monitor); monitor.beginTask(StringUtils.format(Messages.BaseFTPConnectionFileManager_listing_directory, path.toPortableString()), 2); try { ExtendedFileInfo[] fileInfos = getCachedFileInfos(path); if (fileInfos != null) { List<String> list = new ArrayList<String>(); for (ExtendedFileInfo fileInfo : fileInfos) { list.add(fileInfo.getName()); } return list.toArray(new String[list.size()]); } testOrConnect(monitor); try { return listDirectory(basePath.append(path), monitor); } finally { setLastOperationTime(); } } catch (FileNotFoundException e) { return new String[0]; } finally { monitor.done(); } } /* (non-Javadoc) * @see com.aptana.ide.core.io.vfs.IConnectionFileManager#openInputStream(org.eclipse.core.runtime.IPath, int, org.eclipse.core.runtime.IProgressMonitor) */ public synchronized InputStream openInputStream(IPath path, int options, IProgressMonitor monitor) throws CoreException { monitor = Policy.monitorFor(monitor); monitor.beginTask(StringUtils.format(Messages.BaseFTPConnectionFileManager_opening_file, path.toPortableString()), 3); testOrConnect(monitor); try { ExtendedFileInfo fileInfo = fetchAndCacheFileInfo(path, Policy.subMonitorFor(monitor, 1)); if (!fileInfo.exists()) { throw new CoreException(new Status(IStatus.ERROR, FTPPlugin.PLUGIN_ID, Messages.BaseFTPConnectionFileManager_no_such_file, new FileNotFoundException(path.toPortableString()))); } if (fileInfo.isDirectory()) { throw new CoreException(new Status(IStatus.ERROR, FTPPlugin.PLUGIN_ID, Messages.BaseFTPConnectionFileManager_file_is_directory, new FileNotFoundException(path.toPortableString()))); } if (fileInfo.getLength() == 0) { return new ByteArrayInputStream(new byte[0]); } return readFile(basePath.append(path), Policy.subMonitorFor(monitor, 1)); } catch (FileNotFoundException e) { throw new CoreException(new Status(IStatus.ERROR, FTPPlugin.PLUGIN_ID, Messages.BaseFTPConnectionFileManager_no_such_file, new FileNotFoundException(path.toPortableString()))); } finally { setLastOperationTime(); monitor.done(); } } /* (non-Javadoc) * @see com.aptana.ide.core.io.vfs.IConnectionFileManager#openOutputStream(org.eclipse.core.runtime.IPath, int, org.eclipse.core.runtime.IProgressMonitor) */ public synchronized OutputStream openOutputStream(IPath path, int options, IProgressMonitor monitor) throws CoreException { monitor = Policy.monitorFor(monitor); monitor.beginTask(StringUtils.format(Messages.BaseFTPConnectionFileManager_opening_file, path.toPortableString()), 3); testOrConnect(monitor); try { ExtendedFileInfo fileInfo = fetchAndCacheFileInfo(path, Policy.subMonitorFor(monitor, 1)); if (fileInfo.exists() && fileInfo.isDirectory()) { throw new CoreException(new Status(IStatus.ERROR, FTPPlugin.PLUGIN_ID, Messages.BaseFTPConnectionFileManager_file_is_directory, new FileNotFoundException(path.toPortableString()))); } clearCache(path); long permissions; if (fileInfo.exists()) { permissions = fileInfo.getPermissions(); } else { // new file; uses the user-defined default permissions permissions = PreferenceUtils.getFilePermissions(); } return writeFile(basePath.append(path), permissions, Policy.subMonitorFor(monitor, 1)); } catch (FileNotFoundException e) { throw new CoreException(new Status(IStatus.ERROR, FTPPlugin.PLUGIN_ID, Messages.BaseFTPConnectionFileManager_parent_doesnt_exist, new FileNotFoundException(path.toPortableString()))); } finally { setLastOperationTime(); monitor.done(); } } /* (non-Javadoc) * @see com.aptana.ide.core.io.vfs.IConnectionFileManager#delete(org.eclipse.core.runtime.IPath, int, org.eclipse.core.runtime.IProgressMonitor) */ public synchronized void delete(IPath path, int options, IProgressMonitor monitor) throws CoreException { monitor = Policy.monitorFor(monitor); monitor = new InfiniteProgressMonitor(monitor); monitor.beginTask(Messages.BaseFTPConnectionFileManager_deleting, 20); testOrConnect(monitor); try { ExtendedFileInfo fileInfo = getCachedFileInfo(path); if (fileInfo == null) { fileInfo = fetchAndCacheFileInfo(path, Policy.subMonitorFor(monitor, 1)); } if (!fileInfo.exists()) { return; } Policy.checkCanceled(monitor); try { if (fileInfo.isDirectory()) { deleteDirectory(basePath.append(path), monitor); } else { deleteFile(basePath.append(path), monitor); } } catch (FileNotFoundException ignore) { } finally { clearCache(path); } } finally { setLastOperationTime(); monitor.done(); } } /* (non-Javadoc) * @see com.aptana.ide.core.io.vfs.IConnectionFileManager#mkdir(org.eclipse.core.runtime.IPath, int, org.eclipse.core.runtime.IProgressMonitor) */ public synchronized void mkdir(IPath path, int options, IProgressMonitor monitor) throws CoreException { monitor = Policy.monitorFor(monitor); monitor.beginTask(StringUtils.format(Messages.BaseFTPConnectionFileManager_creating_folder, path.toPortableString()), 3); testOrConnect(monitor); try { ExtendedFileInfo fileInfo = fetchAndCacheFileInfo(path, Policy.subMonitorFor(monitor, 1)); if (fileInfo.exists()) { if (!fileInfo.isDirectory()) { throw new CoreException(new Status(IStatus.ERROR, FTPPlugin.PLUGIN_ID, Messages.BaseFTPConnectionFileManager_file_already_exists, new FileNotFoundException(path.toPortableString()))); } return; } if ((options & EFS.SHALLOW) != 0 && path.segmentCount() > 1) { fileInfo = fetchAndCacheFileInfo(path.removeLastSegments(1), Policy.subMonitorFor(monitor, 1)); if (!fileInfo.exists()) { throw new CoreException(new Status(IStatus.ERROR, FTPPlugin.PLUGIN_ID, Messages.BaseFTPConnectionFileManager_parent_doesnt_exist, new FileNotFoundException(path.toPortableString()))); } if (!fileInfo.isDirectory()) { throw new CoreException(new Status(IStatus.ERROR, FTPPlugin.PLUGIN_ID, Messages.BaseFTPConnectionFileManager_parent_is_not_directory, new FileNotFoundException(path.toPortableString()))); } createDirectory(basePath.append(path), Policy.subMonitorFor(monitor, 1)); } else if (path.segmentCount() == 1) { createDirectory(basePath.append(path), Policy.subMonitorFor(monitor, 1)); } else { IProgressMonitor subMonitor = Policy.subMonitorFor(monitor, 1); subMonitor.beginTask(Messages.BaseFTPConnectionFileManager_creating_folders, path.segmentCount()); for (int i = path.segmentCount() - 1; i >= 0; --i) { createDirectory(basePath.append(path).removeLastSegments(i), subMonitor); subMonitor.worked(1); } subMonitor.done(); } } catch (FileNotFoundException e) { throw new CoreException(new Status(IStatus.ERROR, FTPPlugin.PLUGIN_ID, Messages.BaseFTPConnectionFileManager_parent_doesnt_exist, e)); } finally { setLastOperationTime(); monitor.done(); } } /* (non-Javadoc) * @see com.aptana.ide.core.io.vfs.IConnectionFileManager#putInfo(org.eclipse.core.runtime.IPath, org.eclipse.core.filesystem.IFileInfo, int, org.eclipse.core.runtime.IProgressMonitor) */ public synchronized void putInfo(IPath path, IFileInfo info, int options, IProgressMonitor monitor) throws CoreException { monitor = Policy.monitorFor(monitor); monitor.beginTask(StringUtils.format(Messages.BaseFTPConnectionFileManager_putting_changes, path.toPortableString()), 5); testOrConnect(monitor); try { if ((options & EFS.SET_LAST_MODIFIED) != 0) { setModificationTime(basePath.append(path), info.getLastModified(), Policy.subMonitorFor(monitor, 1)); } if ((options & EFS.SET_ATTRIBUTES) != 0 && (options & IExtendedFileInfo.SET_PERMISSIONS) == 0) { ExtendedFileInfo fileInfo = fetchAndCacheFileInfo(path, Policy.subMonitorFor(monitor, 1)); if (fileInfo.exists()) { long permissions = fileInfo.getPermissions(); if (!info.getAttribute(EFS.ATTRIBUTE_READ_ONLY)) { permissions |= IExtendedFileInfo.PERMISSION_OWNER_WRITE; } else { permissions &= ~IExtendedFileInfo.PERMISSION_OWNER_WRITE; } if (info.getAttribute(EFS.ATTRIBUTE_EXECUTABLE)) { permissions |= IExtendedFileInfo.PERMISSION_OWNER_EXECUTE; } else { permissions &= ~IExtendedFileInfo.PERMISSION_OWNER_EXECUTE; } changeFilePermissions(basePath.append(path), permissions, Policy.subMonitorFor(monitor, 1)); } } if (info instanceof IExtendedFileInfo) { IExtendedFileInfo extInfo = (IExtendedFileInfo) info; if ((options & IExtendedFileInfo.SET_PERMISSIONS) != 0) { changeFilePermissions(basePath.append(path), extInfo.getPermissions(), Policy.subMonitorFor(monitor, 1)); } if ((options & IExtendedFileInfo.SET_GROUP) != 0) { changeFileGroup(basePath.append(path), extInfo.getGroup(), Policy.subMonitorFor(monitor, 1)); } } } catch (FileNotFoundException e) { throw new CoreException(new Status(IStatus.ERROR, FTPPlugin.PLUGIN_ID, Messages.BaseFTPConnectionFileManager_no_such_file, new FileNotFoundException(path.toPortableString()))); } finally { clearCache(path); setLastOperationTime(); monitor.done(); } } /* (non-Javadoc) * @see com.aptana.ide.core.io.vfs.IConnectionFileManager#move(org.eclipse.core.runtime.IPath, org.eclipse.core.runtime.IPath, int, org.eclipse.core.runtime.IProgressMonitor) */ public synchronized void move(IPath sourcePath, IPath destinationPath, int options, IProgressMonitor monitor) throws CoreException { monitor = Policy.monitorFor(monitor); monitor.beginTask(StringUtils.format(Messages.BaseFTPConnectionFileManager_moving, sourcePath.toPortableString()), 5); testOrConnect(monitor); try { ExtendedFileInfo fileInfo = fetchAndCacheFileInfo(sourcePath, Policy.subMonitorFor(monitor, 1)); if (!fileInfo.exists()) { throw new CoreException(new Status(IStatus.ERROR, FTPPlugin.PLUGIN_ID, Messages.BaseFTPConnectionFileManager_no_such_file, new FileNotFoundException(sourcePath.toPortableString()))); } boolean isDirectory = fileInfo.isDirectory(); fileInfo = fetchAndCacheFileInfo(destinationPath, Policy.subMonitorFor(monitor, 1)); if (fileInfo.exists()) { if ((options & EFS.OVERWRITE) == 0) { throw new CoreException(new Status(IStatus.ERROR, FTPPlugin.PLUGIN_ID, Messages.BaseFTPConnectionFileManager_file_already_exists, new FileNotFoundException(destinationPath.toPortableString()))); } if (fileInfo.isDirectory() != isDirectory) { throw new CoreException(new Status(IStatus.ERROR, FTPPlugin.PLUGIN_ID, Messages.BaseFTPConnectionFileManager_cant_move)); } } else { fileInfo = fetchAndCacheFileInfo(destinationPath.removeLastSegments(1), Policy.subMonitorFor(monitor, 1)); if (!fileInfo.exists()) { throw new CoreException(new Status(IStatus.ERROR, FTPPlugin.PLUGIN_ID, Messages.BaseFTPConnectionFileManager_parent_doesnt_exist, new FileNotFoundException(destinationPath.toPortableString()))); } } clearCache(sourcePath); clearCache(destinationPath); renameFile(basePath.append(sourcePath), basePath.append(destinationPath), Policy.subMonitorFor(monitor, 2)); } catch (FileNotFoundException e) { throw new CoreException(new Status(IStatus.ERROR, FTPPlugin.PLUGIN_ID, Messages.BaseFTPConnectionFileManager_no_such_file, new FileNotFoundException(sourcePath.toPortableString()))); } finally { setLastOperationTime(); monitor.done(); } } /* (non-Javadoc) * @see com.aptana.ide.core.io.vfs.IConnectionFileManager#getCanonicalURI(org.eclipse.core.runtime.IPath) */ public URI getCanonicalURI(IPath path) { // TODO:max - trace links here return getRootCanonicalURI().resolve(URLEncoder.encode(basePath.append(path).toPortableString(), null, null)); } protected abstract void checkConnected() throws Exception; protected abstract URI getRootCanonicalURI(); // all methods here accept absolute path protected abstract void changeCurrentDir(IPath path) throws FTPException, IOException, CoreException; protected abstract ExtendedFileInfo fetchFile(IPath path, int options, IProgressMonitor monitor) throws CoreException, FileNotFoundException; protected abstract ExtendedFileInfo[] fetchFiles(IPath path, int options, IProgressMonitor monitor) throws CoreException, FileNotFoundException; protected abstract String[] listDirectory(IPath path, IProgressMonitor monitor) throws CoreException, FileNotFoundException; protected abstract InputStream readFile(IPath path, IProgressMonitor monitor) throws CoreException, FileNotFoundException; protected abstract OutputStream writeFile(IPath path, long permissions, IProgressMonitor monitor) throws CoreException, FileNotFoundException; protected abstract void deleteFile(IPath path, IProgressMonitor monitor) throws CoreException, FileNotFoundException; protected abstract void deleteDirectory(IPath path, IProgressMonitor monitor) throws CoreException, FileNotFoundException; protected abstract void createDirectory(IPath path, IProgressMonitor monitor) throws CoreException, FileNotFoundException; protected abstract void renameFile(IPath sourcePath, IPath destinationPath, IProgressMonitor monitor) throws CoreException, FileNotFoundException; protected abstract void setModificationTime(IPath path, long modificationTime, IProgressMonitor monitor) throws CoreException, FileNotFoundException; protected abstract void changeFilePermissions(IPath path, long permissions, IProgressMonitor monitor) throws CoreException, FileNotFoundException; protected abstract void changeFileGroup(IPath path, String group, IProgressMonitor monitor) throws CoreException, FileNotFoundException; private ExtendedFileInfo fetchAndCacheFileInfo(IPath path, IProgressMonitor monitor) throws CoreException { return fetchAndCacheFileInfo(path, EFS.NONE, monitor); } private ExtendedFileInfo fetchAndCacheFileInfo(IPath path, int options, IProgressMonitor monitor) throws CoreException { ExtendedFileInfo fileInfo; try { fileInfo = fetchFile(basePath.append(path), options, monitor); } catch (FileNotFoundException e) { fileInfo = new ExtendedFileInfo(path.segmentCount() > 0 ? path.lastSegment() : Path.ROOT.toPortableString()); fileInfo.setExists(false); return fileInfo; } if (path.segmentCount() == 0) { fileInfo.setName(Path.ROOT.toPortableString()); } postProcessFileInfo(fileInfo, path, options, monitor); return cache(path, fileInfo); } private void postProcessFileInfo(ExtendedFileInfo fileInfo, IPath dirPath, int options, IProgressMonitor monitor) throws CoreException { if (fileInfo.getAttribute(EFS.ATTRIBUTE_SYMLINK)) { try { ExtendedFileInfo targetFileInfo = resolveSymlink(dirPath, fileInfo.getStringAttribute(EFS.ATTRIBUTE_LINK_TARGET), options, monitor); fileInfo.setExists(targetFileInfo.exists()); if (targetFileInfo.exists()) { fileInfo.setDirectory(targetFileInfo.isDirectory()); fileInfo.setLength(targetFileInfo.getLength()); fileInfo.setLastModified(targetFileInfo.getLastModified()); fileInfo.setOwner(targetFileInfo.getOwner()); fileInfo.setGroup(targetFileInfo.getGroup()); fileInfo.setPermissions(targetFileInfo.getPermissions()); } } catch (FileNotFoundException e) { try { changeCurrentDir(dirPath.append(fileInfo.getName())); fileInfo.setExists(true); fileInfo.setDirectory(true); } catch (FileNotFoundException fnfe) { fileInfo.setExists(false); } catch (Exception ignore) { IdeLog.logImportant(FTPPlugin.getDefault(), com.aptana.ide.filesystem.ftp.Messages.BaseFTPConnectionFileManager_symlink_resolve_failed, e); } } } long permissions = fileInfo.getPermissions(); fileInfo.setAttribute(EFS.ATTRIBUTE_READ_ONLY, (permissions & IExtendedFileInfo.PERMISSION_OWNER_WRITE) == 0); fileInfo.setAttribute(EFS.ATTRIBUTE_EXECUTABLE, (permissions & IExtendedFileInfo.PERMISSION_OWNER_EXECUTE) != 0); } private ExtendedFileInfo resolveSymlink(IPath dirPath, String linkTarget, int options, IProgressMonitor monitor) throws CoreException, FileNotFoundException { Set<IPath> visited = new HashSet<IPath>(); visited.add(dirPath); while (linkTarget != null && linkTarget.length() > 0) { IPath targetPath = Path.fromPortableString(linkTarget); if (!targetPath.isAbsolute()) { targetPath = dirPath.append(targetPath); } if (visited.contains(targetPath)) { break; } visited.add(targetPath); ExtendedFileInfo targetFileInfo = getCachedFileInfo(targetPath); if (targetFileInfo == null) { Policy.checkCanceled(monitor); targetFileInfo = cache(targetPath, fetchFile(targetPath, options, Policy.subMonitorFor(monitor, 1))); } cache(targetPath, targetFileInfo); if (targetFileInfo.getAttribute(EFS.ATTRIBUTE_SYMLINK)) { linkTarget = targetFileInfo.getStringAttribute(EFS.ATTRIBUTE_LINK_TARGET); dirPath = targetPath.removeLastSegments(1); continue; } return targetFileInfo; } return new ExtendedFileInfo(); } private ExtendedFileInfo getCachedFileInfo(IPath path) { return fileInfoCache != null ? fileInfoCache.get(path) : null; } private ExtendedFileInfo[] getCachedFileInfos(IPath path) { return fileInfosCache != null ? fileInfosCache.get(path) : null; } protected ExtendedFileInfo cache(IPath path, ExtendedFileInfo fileInfo) { if (fileInfoCache != null && fileInfo.exists()) { fileInfoCache.put(path, fileInfo); } return fileInfo; } protected ExtendedFileInfo[] cache(IPath path, ExtendedFileInfo[] fileInfos) { if (fileInfosCache != null) { fileInfosCache.put(path, fileInfos); } return fileInfos; } protected void clearCache(IPath path) { int segments = path.segmentCount(); if (fileInfoCache != null) { for (IPath p : new ArrayList<IPath>(fileInfoCache.keySet())) { if (p.segmentCount() >= segments && path.matchingFirstSegments(p) == segments) { fileInfoCache.remove(p); } } } if (fileInfosCache != null) { for (IPath p : new ArrayList<IPath>(fileInfosCache.keySet())) { if (p.segmentCount() >= segments && path.matchingFirstSegments(p) == segments) { fileInfosCache.remove(p); } } } } protected void cleanup() { if (fileInfoCache != null) { fileInfoCache.clear(); } if (fileInfosCache != null) { fileInfosCache.clear(); } } protected void testOrConnect(IProgressMonitor monitor) throws CoreException { Policy.checkCanceled(monitor); testConnection(); if (!isConnected()) { connect(Policy.subMonitorFor(monitor, 1)); Policy.checkCanceled(monitor); } } private void testConnection() { if (!isConnected()) { return; } if (System.currentTimeMillis() - lastOperationTime > CHECK_CONNECTION_TIMEOUT) { try { checkConnected(); } catch (Exception e) { IdeLog.logImportant(FTPPlugin.getDefault(), Messages.BaseFTPConnectionFileManager_connection_check_failed, e); } } if (isConnected()) { setLastOperationTime(); } } private void setLastOperationTime() { lastOperationTime = System.currentTimeMillis(); } }