package ch.cyberduck.core; /* * Copyright (c) 2005 David Kocher. All rights reserved. * http://cyberduck.ch/ * * 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. * * Bug fixes, suggestions and comments should be sent to: * dkocher@cyberduck.ch */ import ch.cyberduck.core.io.BandwidthThrottle; import ch.cyberduck.core.serializer.Serializer; //import ch.cyberduck.ui.growl.Growl; import ch.cyberduck.core.service.DownloadTransferService; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang.StringUtils; import java.util.Iterator; import java.util.List; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; /** * @version $Id: DownloadTransfer.java 5830 2010-03-03 12:02:26Z dkocher $ */ public class DownloadTransfer extends Transfer implements DownloadTransferService { public DownloadTransfer(Path root) { super(root); } public DownloadTransfer(List<Path> roots) { super(roots); } public <T> DownloadTransfer(T dict, Session s) { super(dict, s); } @Override protected void setRoots(List<Path> downloads) { final List<Path> normalized = new Collection<Path>(); for(Path download : downloads) { boolean duplicate = false; for(Iterator<Path> iter = normalized.iterator(); iter.hasNext();) { Path n = iter.next(); if(download.isChild(n)) { // The selected file is a child of a directory already included duplicate = true; break; } if(n.isChild(download)) { iter.remove(); } if(download.getLocal().getName().equals(n.getLocal().getName())) { // The selected file has the same name; if downloaded as a root element // it would overwrite the earlier final String parent = download.getLocal().getParent().getAbsolute(); final String filename = download.getName(); String proposal; int no = 0; do { no++; proposal = FilenameUtils.getBaseName(filename) + "-" + no; if(StringUtils.isNotBlank(FilenameUtils.getExtension(filename))) { proposal += "." + FilenameUtils.getExtension(filename); } download.setLocal(LocalFactory.createLocal(parent, proposal)); } while(download.getLocal().exists()); log.info("Changed local name to:" + download.getName()); } } // Prunes the list of selected files. Files which are a child of an already included directory // are removed from the returned list. if(!duplicate) { normalized.add(download); } } super.setRoots(normalized); } /* (non-Javadoc) * @see ch.cyberduck.core.DownloadTransferService#getAsDictionary() */ @Override public <T> T getAsDictionary() { final Serializer dict = super.getSerializer(); dict.setStringForKey(String.valueOf(KIND_DOWNLOAD), "Kind"); return dict.<T>getSerialized(); } /** * Set download bandwidth */ @Override protected void init() { log.debug("init"); this.bandwidth = new BandwidthThrottle( Preferences.instance().getFloat("queue.download.bandwidth.bytes")); } /** * */ private abstract class DownloadTransferFilter extends TransferFilter { @Override public void prepare(Path p) { if(p.attributes.getSize() == -1) { p.readSize(); } if(p.attributes.getModificationDate() == -1) { if(Preferences.instance().getBoolean("queue.download.preserveDate")) { p.readTimestamp(); } } if(p.attributes.getPermission() == null) { if(Preferences.instance().getBoolean("queue.download.changePermissions")) { p.readPermission(); } } // Read file size if(p.attributes.isFile()) { if(p.attributes.isSymbolicLink()) { if(null != p.getSymlinkTarget()) { Path symlink = PathFactory.createPath(getSession(), p.getSymlinkTarget(), Path.FILE_TYPE); if(symlink.attributes.getSize() == -1) { symlink.readSize(); } size += symlink.attributes.getSize(); } } else { size += p.attributes.getSize(); } if(p.getStatus().isResume()) { transferred += p.getLocal().attributes.getSize(); } } if(!p.getLocal().getParent().exists()) { // Create download folder if missing p.getLocal().getParent().mkdir(true); } } } private final PathFilter<Path> childFilter = new PathFilter<Path>() { public boolean accept(Path child) { if(Preferences.instance().getBoolean("queue.download.skip.enable")) { try { if(Pattern.compile(Preferences.instance().getProperty("queue.download.skip.regex")).matcher(child.getName()).matches()) { return false; } } catch(PatternSyntaxException e) { log.warn(e.getMessage()); } } return true; } }; /* (non-Javadoc) * @see ch.cyberduck.core.DownloadTransferService#childs(ch.cyberduck.core.Path) */ @Override public AttributedList<Path> childs(final Path parent) { final AttributedList<Path> list = parent.childs(childFilter); for(Path download : list) { // Change download path relative to parent local folder download.setLocal(LocalFactory.createLocal(parent.getLocal(), download.getLocal().getName())); } return list; } private final TransferFilter ACTION_OVERWRITE = new DownloadTransferFilter() { public boolean accept(final Path p) { if(p.attributes.isDirectory()) { return !p.getLocal().exists(); } return true; } @Override public void prepare(final Path p) { if(p.attributes.isFile()) { p.getStatus().setResume(false); } super.prepare(p); } }; private final TransferFilter ACTION_RESUME = new DownloadTransferFilter() { public boolean accept(final Path p) { if(p.getStatus().isComplete() || p.getLocal().attributes.getSize() == p.attributes.getSize()) { // No need to resume completed transfers p.getStatus().setComplete(true); return false; } if(p.attributes.isDirectory()) { return !p.getLocal().exists(); } return true; } @Override public void prepare(final Path p) { if(p.attributes.isFile()) { final boolean resume = p.getLocal().exists() && p.getLocal().attributes.getSize() > 0; p.getStatus().setResume(resume); long skipped = p.getLocal().attributes.getSize(); p.getStatus().setCurrent(skipped); } super.prepare(p); } }; private final TransferFilter ACTION_RENAME = new DownloadTransferFilter() { public boolean accept(final Path p) { return true; } @Override public void prepare(final Path p) { if(p.attributes.isFile()) { p.getStatus().setResume(false); } if(p.getLocal().exists() && p.getLocal().attributes.getSize() > 0) { final String parent = p.getLocal().getParent().getAbsolute(); final String filename = p.getName(); int no = 0; while(p.getLocal().exists()) { no++; String proposal = FilenameUtils.getBaseName(filename) + "-" + no; if(StringUtils.isNotBlank(FilenameUtils.getExtension(filename))) { proposal += "." + FilenameUtils.getExtension(filename); } p.setLocal(LocalFactory.createLocal(parent, proposal)); } log.info("Changed local name to:" + p.getLocal().getName()); } super.prepare(p); } }; private final DownloadTransferFilter ACTION_SKIP = new DownloadTransferFilter() { public boolean accept(final Path p) { return !p.getLocal().exists(); } }; /* (non-Javadoc) * @see ch.cyberduck.core.DownloadTransferService#filter(ch.cyberduck.core.TransferAction) */ @Override public TransferFilter filter(final TransferAction action) { log.debug("filter:" + action); if(action.equals(TransferAction.ACTION_OVERWRITE)) { return ACTION_OVERWRITE; } if(action.equals(TransferAction.ACTION_RESUME)) { return ACTION_RESUME; } if(action.equals(TransferAction.ACTION_RENAME)) { return ACTION_RENAME; } if(action.equals(TransferAction.ACTION_SKIP)) { return ACTION_SKIP; } if(action.equals(TransferAction.ACTION_CALLBACK)) { for(Path root : this.getRoots()) { if(root.getLocal().exists()) { if(root.getLocal().attributes.isDirectory()) { if(0 == root.getLocal().childs().size()) { // Do not prompt for existing empty directories continue; } } if(root.getLocal().attributes.isFile()) { if(root.getLocal().attributes.getSize() == 0) { // Do not prompt for zero sized files continue; } } // Prompt user to choose a filter TransferAction result = prompt.prompt(); return this.filter(result); //break out of loop } } // No files exist yet therefore it is most straightforward to use the overwrite action return this.filter(TransferAction.ACTION_OVERWRITE); } return super.filter(action); } /* (non-Javadoc) * @see ch.cyberduck.core.DownloadTransferService#action(boolean, boolean) */ @Override public TransferAction action(final boolean resumeRequested, final boolean reloadRequested) { log.debug("action:" + resumeRequested + "," + reloadRequested); if(resumeRequested) { // Force resume return TransferAction.ACTION_RESUME; } if(reloadRequested) { return TransferAction.forName( Preferences.instance().getProperty("queue.download.reload.fileExists") ); } // Use default return TransferAction.forName( Preferences.instance().getProperty("queue.download.fileExists") ); } @Override protected void _transferImpl(final Path p) { p.download(bandwidth, new AbstractStreamListener() { @Override public void bytesReceived(long bytes) { transferred += bytes; } }); if(Preferences.instance().getBoolean("queue.download.changePermissions")) { log.info("Updating permissions"); Permission perm; if(Preferences.instance().getBoolean("queue.download.permissions.useDefault") && p.attributes.isFile()) { perm = new Permission( Preferences.instance().getInteger("queue.download.permissions.file.default") ); } else { perm = p.attributes.getPermission(); } if(null != perm) { if(p.attributes.isDirectory()) { perm.getOwnerPermissions()[Permission.WRITE] = true; perm.getOwnerPermissions()[Permission.EXECUTE] = true; } p.getLocal().writePermissions(perm, false); } } if(Preferences.instance().getBoolean("queue.download.preserveDate")) { log.info("Updating timestamp"); if(-1 == p.attributes.getModificationDate()) { p.readTimestamp(); } if(p.attributes.getModificationDate() != -1) { long timestamp = p.attributes.getModificationDate(); p.getLocal().writeModificationDate(timestamp/*, this.getHost().getTimezone()*/); } } } @Override protected void fireTransferDidEnd() { if(this.isReset() && this.isComplete() && !this.isCanceled() && !(this.getTransferred() == 0)) { // Growl.instance().notify("Download complete", getName()); if(this.shouldOpenWhenComplete()) { this.getRoot().getLocal().open(); } this.getRoot().getLocal().bounce(); } super.fireTransferDidEnd(); } /** * @return */ protected boolean shouldOpenWhenComplete() { return Preferences.instance().getBoolean("queue.postProcessItemWhenComplete"); } /* (non-Javadoc) * @see ch.cyberduck.core.DownloadTransferService#isResumable() */ @Override public boolean isResumable() { return getSession().isDownloadResumable(); } }