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.s3.S3Session;
import ch.cyberduck.core.serializer.Serializer;
//import ch.cyberduck.ui.growl.Growl;
import ch.cyberduck.core.service.UploadTransferService;
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: UploadTransfer.java 5830 2010-03-03 12:02:26Z dkocher $
*/
public class UploadTransfer extends Transfer implements UploadTransferService {
public UploadTransfer(Path root) {
super(root);
}
public UploadTransfer(List<Path> roots) {
super(roots);
}
public <T> UploadTransfer(T dict, Session s) {
super(dict, s);
}
/* (non-Javadoc)
* @see ch.cyberduck.core.UploadTransferService#getAsDictionary()
*/
@Override
public <T> T getAsDictionary() {
final Serializer dict = super.getSerializer();
dict.setStringForKey(String.valueOf(KIND_UPLOAD), "Kind");
return dict.<T>getSerialized();
}
@Override
protected void init() {
log.debug("init");
this.bandwidth = new BandwidthThrottle(
Preferences.instance().getFloat("queue.upload.bandwidth.bytes"));
}
@Override
protected void setRoots(List<Path> uploads) {
final List<Path> normalized = new Collection<Path>();
for(Path upload : uploads) {
boolean duplicate = false;
for(Iterator<Path> iter = normalized.iterator(); iter.hasNext();) {
Path n = iter.next();
if(upload.getLocal().isChild(n.getLocal())) {
// The selected file is a child of a directory already included
duplicate = true;
break;
}
if(n.getLocal().isChild(upload.getLocal())) {
iter.remove();
}
if(upload.getName().equals(n.getName())) {
// The selected file has the same name; if downloaded as a root element
// it would overwrite the earlier
final String parent = upload.getParent().getAbsolute();
final String filename = upload.getName();
String proposal;
int no = 0;
int index = filename.lastIndexOf(".");
do {
no++;
if(index != -1 && index != 0) {
proposal = filename.substring(0, index)
+ "-" + no + filename.substring(index);
}
else {
proposal = filename + "-" + no;
}
upload.setPath(parent, proposal);
}
while(false);//(upload.exists());
log.info("Changed name to:" + upload.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(upload);
}
}
super.setRoots(normalized);
}
/**
*
*/
private abstract class UploadTransferFilter extends TransferFilter {
public boolean accept(final Path file) {
return file.getLocal().exists();
}
@Override
public void prepare(Path p) {
if(p.attributes.isFile()) {
// Read file size
size += p.getLocal().attributes.getSize();
if(p.getStatus().isResume()) {
transferred += p.attributes.getSize();
}
}
if(p.attributes.isDirectory()) {
if(!p.exists()) {
p.cache().put(p, new AttributedList<Path>());
}
}
}
}
/**
* @uml.property name="childFilter"
* @uml.associationEnd
*/
private final PathFilter<Local> childFilter = new PathFilter<Local>() {
public boolean accept(Local child) {
try {
if(Preferences.instance().getBoolean("queue.upload.skip.enable")) {
if(Pattern.compile(Preferences.instance().getProperty("queue.upload.skip.regex")).matcher(child.getName()).matches()) {
return false;
}
}
}
catch(PatternSyntaxException e) {
log.warn(e.getMessage());
}
return true;
}
};
/**
* File listing cache for children of the root paths not part of the session cache because they only exist on the local file system.
* @uml.property name="cache"
* @uml.associationEnd
*/
private final Cache<Path> cache = new Cache<Path>();
/* (non-Javadoc)
* @see ch.cyberduck.core.UploadTransferService#childs(ch.cyberduck.core.Path)
*/
@Override
public AttributedList<Path> childs(final Path parent) {
if(!cache.containsKey(parent)) {
if(!parent.getLocal().exists()) {
// Cannot fetch file listing of non existant file
return AttributedList.emptyList();
}
final AttributedList<Path> childs = new AttributedList<Path>();
for(AbstractPath child : parent.getLocal().childs(childFilter)) {
final Local local = LocalFactory.createLocal(child.getAbsolute());
Path upload = PathFactory.createPath(getSession(), parent.getAbsolute(), local);
if(upload.exists()) {
upload = this.getSession().cache().lookup(upload.getReference());
upload.setLocal(local);
}
childs.add(upload);
}
cache.put(parent, childs);
}
return cache.get(parent);
}
/* (non-Javadoc)
* @see ch.cyberduck.core.UploadTransferService#lookup(ch.cyberduck.core.PathReference)
*/
@Override
public Path lookup(PathReference r) {
return cache.lookup(r);
}
@Override
protected void clear(final TransferOptions options) {
cache.clear();
super.clear(options);
}
/* (non-Javadoc)
* @see ch.cyberduck.core.UploadTransferService#cache()
*/
@Override
public Cache<Path> cache() {
return cache;
}
/* (non-Javadoc)
* @see ch.cyberduck.core.UploadTransferService#isResumable()
*/
@Override
public boolean isResumable() {
return this.getSession().isUploadResumable();
}
/**
* @uml.property name="aCTION_OVERWRITE"
* @uml.associationEnd
*/
private final TransferFilter ACTION_OVERWRITE = new UploadTransferFilter() {
@Override
public boolean accept(final Path p) {
if(super.accept(p)) {
if(p.attributes.isDirectory()) {
// Do not attempt to create a directory that already exists
return !p.exists();
}
return true;
}
return false;
}
@Override
public void prepare(final Path p) {
if(p.exists()) {
if(p.attributes.getPermission() == null) {
if(Preferences.instance().getBoolean("queue.upload.changePermissions")) {
p.readPermission();
}
}
}
if(p.attributes.isFile()) {
p.getStatus().setResume(false);
}
super.prepare(p);
}
};
/**
* @uml.property name="aCTION_RESUME"
* @uml.associationEnd
*/
private final TransferFilter ACTION_RESUME = new UploadTransferFilter() {
@Override
public boolean accept(final Path p) {
if(super.accept(p)) {
if(p.attributes.isDirectory()) {
return !p.exists();
}
if(p.getStatus().isComplete() || p.getLocal().attributes.getSize() == p.attributes.getSize()) {
// No need to resume completed transfers
p.getStatus().setComplete(true);
return false;
}
return true;
}
return false;
}
@Override
public void prepare(final Path p) {
if(p.exists()) {
if(p.attributes.getSize() == -1) {
p.readSize();
}
if(p.attributes.getModificationDate() == -1) {
if(Preferences.instance().getBoolean("queue.upload.preserveDate")) {
p.readTimestamp();
}
}
if(p.attributes.getPermission() == null) {
if(Preferences.instance().getBoolean("queue.upload.changePermissions")) {
p.readPermission();
}
}
}
if(p.attributes.isFile()) {
// Append to file if size is not zero
final boolean resume = p.exists()
&& p.attributes.getSize() > 0;
p.getStatus().setResume(resume);
if(p.getStatus().isResume()) {
p.getStatus().setCurrent(p.attributes.getSize());
}
}
super.prepare(p);
}
};
/**
* @uml.property name="aCTION_RENAME"
* @uml.associationEnd
*/
private final TransferFilter ACTION_RENAME = new UploadTransferFilter() {
@Override
public boolean accept(final Path p) {
// Rename every file
return super.accept(p);
}
@Override
public void prepare(final Path p) {
if(p.exists()) {
final String parent = p.getParent().getAbsolute();
final String filename = p.getName();
int no = 0;
while(p.exists()) { // Do not use cached value of exists!
no++;
String proposal = FilenameUtils.getBaseName(filename) + "-" + no;
if(StringUtils.isNotBlank(FilenameUtils.getExtension(filename))) {
proposal += "." + FilenameUtils.getExtension(filename);
}
p.setPath(parent, proposal);
}
log.info("Changed local name to:" + p.getName());
}
if(p.attributes.isFile()) {
p.getStatus().setResume(false);
}
super.prepare(p);
}
};
/**
* @uml.property name="aCTION_SKIP"
* @uml.associationEnd
*/
private final TransferFilter ACTION_SKIP = new UploadTransferFilter() {
@Override
public boolean accept(final Path p) {
if(super.accept(p)) {
if(!p.exists()) {
return true;
}
}
return false;
}
};
/* (non-Javadoc)
* @see ch.cyberduck.core.UploadTransferService#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.exists()) {
if(root.getLocal().attributes.isDirectory()) {
if(0 == this.childs(root).size()) {
// Do not prompt for existing empty directories
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.UploadTransferService#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.upload.reload.fileExists")
);
}
// Use default
return TransferAction.forName(
Preferences.instance().getProperty("queue.upload.fileExists")
);
}
@Override
protected void _transferImpl(final Path p) {
Permission permission = null;
if(Preferences.instance().getBoolean("queue.upload.changePermissions")) {
permission = p.attributes.getPermission();
if(null == permission) {
if(Preferences.instance().getBoolean("queue.upload.permissions.useDefault")) {
if(p.attributes.isFile()) {
permission = new Permission(
Preferences.instance().getInteger("queue.upload.permissions.file.default"));
}
if(p.attributes.isDirectory()) {
permission = new Permission(
Preferences.instance().getInteger("queue.upload.permissions.folder.default"));
}
}
else {
permission = p.getLocal().attributes.getPermission();
}
}
}
p.upload(bandwidth, new AbstractStreamListener() {
@Override
public void bytesSent(long bytes) {
transferred += bytes;
}
}, permission);
}
@Override
protected void fireTransferDidEnd() {
if(this.isReset() && this.isComplete() && !this.isCanceled() && !(this.getTransferred() == 0)) {
// Growl.instance().notify("Upload complete", getName());
}
super.fireTransferDidEnd();
}
}