/*
* Copyright (C) 2010-2015, Martin Goellnitz
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301, USA
*/
package jfs.sync.webdav;
import com.github.sardine.DavResource;
import com.github.sardine.Sardine;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.List;
import jfs.conf.JFSConfig;
import jfs.sync.JFSFile;
import jfs.sync.JFSFileProducer;
import jfs.sync.base.AbstractJFSFileProducerFactory;
import jfs.sync.encryption.FileInfo;
import jfs.sync.util.DavUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Represents an external file and uses a WebDAV backend.
*
* @author Martin Goellnitz
*/
public class JFSWebDavFile extends JFSFile {
private static final Logger LOG = LoggerFactory.getLogger(JFSWebDavFile.class);
/**
* The retrieved file information object from the server.
*/
private FileInfo info = null;
/**
* The list of included files.
*/
private JFSFile[] list = null;
/**
* The server access object to use.
*/
private Sardine access = null;
private OutputStream output = null;
private InputStream input = null;
private FileInfo createFileInfo(String folder, DavResource resource) {
LOG.debug("createFileInfo() {} [{}] {}", folder+"/"+resource.getName(), resource.isDirectory(), resource.getCustomProps());
FileInfo result = new FileInfo();
result.setCanRead(true);
result.setCanWrite(true);
result.setExists(true);
result.setPath(folder);
result.setName(resource.getName());
result.setDirectory(resource.isDirectory());
result.setSize(resource.isDirectory() ? 0 : resource.getContentLength());
long time = 0;
if (!resource.isDirectory()) {
time = DavUtils.getModificationDate(resource);
} // if
result.setModificationDate(time);
return result;
} // createFileInfo()
private String getUrl(String urlSegment) {
String url = urlSegment;
try {
url = URLEncoder.encode(url, "UTF-8").replace("%2F", "/").replace("%3A", ":").replace("+", "%20");
} catch (UnsupportedEncodingException e) {
LOG.error("getUrl()", e);
} // try/catch
return url;
}
/**
* Creates a new external file for a certain path using a specific file producer.
*
* @param access The server access object to use.
* @param fileProducer The assigned file producer.
* @param path The path to create the external file for.
* @param isDirectory tell if the given path describes a directory
*/
public JFSWebDavFile(Sardine access, JFSFileProducer fileProducer, String path, boolean isDirectory) {
super(fileProducer, path);
this.access = access;
String url = (fileProducer.getRootPath()+path).replace('\\', '/');
String[] pathAndName = AbstractJFSFileProducerFactory.getPathAndName(url, "/");
info = new FileInfo();
info.setCanRead(true);
info.setCanWrite(true);
info.setPath(pathAndName[0]);
info.setName(pathAndName[1]);
info.setDirectory(isDirectory);
if (LOG.isDebugEnabled()) {
LOG.debug("(_) "+(info.isDirectory() ? "d" : "-")+(info.isExists() ? "e" : "-")+" | "+info.getPath()+"/"+info.getName());
} // if
try {
String folderUrl = getUrl(pathAndName[0])+"/";
List<DavResource> parentListing = ((JFSWebDavFileProducer) getFileProducer()).getListing(folderUrl);
for (DavResource resource : parentListing) {
if (pathAndName[1].equals(resource.getName())) {
info = createFileInfo(pathAndName[0], resource);
} // if
} // for
} catch (IOException ioe) {
LOG.error("() getting parent folder list for '"+pathAndName[0]+"': "+ioe.getMessage(), ioe);
} // try/catch
if (LOG.isInfoEnabled()) {
LOG.info("() "+(info.isDirectory() ? "d" : "-")+(info.isExists() ? "e" : "-")+" | "+info.getPath()+"/"+info.getName());
} // if
} // JFSWebDavFile()
/**
* Creates a new external root file and reads the structure from server.
*
* @param access
* The server access object to use.
* @param fileProducer
* The assigned file producer.
*/
public JFSWebDavFile(Sardine access, JFSFileProducer fileProducer) {
this(access, fileProducer, "", true);
} // JFSWebDavFile()
/**
* @see JFSFile#canRead()
*/
@Override
public boolean canRead() {
return info.isCanRead();
}
/**
* @see JFSFile#canWrite()
*/
@Override
public boolean canWrite() {
return info.isCanWrite();
}
/**
* @see JFSFile#getInputStream()
*/
@Override
protected InputStream getInputStream() {
String url = getUrl(info.getPath()+"/"+info.getName());
LOG.debug("getInputStream() url {}", url);
try {
input = access.get(url);
} catch (IOException e) {
LOG.error("getInputStream()", e);
} // try/catch
return input;
} // getInputStream()
private Sardine getAccess() {
return access;
} // getAccess()
/**
* @see JFSFile#getOutputStream()
*/
@Override
protected OutputStream getOutputStream() {
LOG.debug("getOutputStream()");
final String url = getUrl(info.getPath()+"/"+info.getName());
try {
OutputStream result = new com.gc.iotools.stream.os.OutputStreamToInputStream<String>() {
@Override
protected String doRead(InputStream input) throws Exception {
// Date d = new Date(146801458666L);
// String lastModified = DATE_FORMAT.format(d);
// Map<String, String> headers = new HashMap<>();
// headers.put("Last-Modified", lastModified);
// headers.put("Date", lastModified);
// LOG.debug("getOutputStream() headers {}", headers);
// getAccess().put(url, input, headers);
getAccess().put(url, input);
return "";
}
};
output = result;
return result;
} catch (Exception e) {
throw new RuntimeException(e);
} // try/catch
} // getOutputStream()
/**
* @see JFSFile#closeInputStream()
*/
@Override
protected void closeInputStream() {
if (input!=null) {
try {
input.close();
} catch (IOException e) {
LOG.error("closeInputStream()", e);
} // try/catch
input = null;
} // if
} // closeInputStream()
/**
* @see JFSFile#closeOutputStream()
*/
@Override
protected void closeOutputStream() {
if (output!=null) {
try {
output.close();
} catch (IOException e) {
LOG.error("closeOutputStream()", e);
} // try/catch
output = null;
} // if
} // closeOutputStream()
/**
* @see JFSFile#delete()
*/
@Override
public boolean delete() {
boolean result = false;
try {
String url = getUrl(info.getPath()+"/"+getName());
LOG.debug("delete() deleting {}", url);
access.delete(url+(info.isDirectory() ? "/" : ""));
result = true;
} catch (IOException e) {
LOG.error("delete()", e);
} // try/catch
return result;
} // delete()
/**
* @see JFSFile#exists()
*/
@Override
public boolean exists() {
return info.isExists();
}
/**
* @see JFSFile#getLastModified()
*/
@Override
public long getLastModified() {
return info.getModificationDate();
}
/**
* @see JFSFile#getLength()
*/
@Override
public long getLength() {
return info.getSize();
}
/**
* @see JFSFile#getList()
*/
@Override
public JFSFile[] getList() {
if (list==null) {
list = new JFSWebDavFile[0];
if (isDirectory()) {
try {
String folder = info.getPath()+"/"+info.getName()+"/";
String url = getUrl(folder);
List<DavResource> listing = ((JFSWebDavFileProducer) getFileProducer()).getListing(url);
if (listing.size()>1) {
list = new JFSWebDavFile[listing.size()-1];
int rootLength = new URL(getFileProducer().getRootPath()).getPath().length();
int i = 0;
for (DavResource resource : listing) {
LOG.debug("getList({}) {} / {}", i, folder, resource.getPath());
if (!folder.endsWith(resource.getPath())) {
String path = resource.getPath().substring(rootLength);
if (resource.isDirectory()) {
path = path.substring(0, path.length()-1);
} // if
if (LOG.isDebugEnabled()) {
LOG.debug("getList("+i+") resource uri: "+path+(resource.isDirectory() ? "/" : ""));
} // if
list[i++] = new JFSWebDavFile(access, getFileProducer(), path, resource.isDirectory());
} // if
} // for
} // if
} catch (Exception e) {
LOG.error("getList()", e);
} // try/catch
} // if
} // getList()
return list;
} // getList()
/**
* @see JFSFile#getName()
*/
@Override
public String getName() {
return info.getName();
}
/**
* @see JFSFile#getPath()
*/
@Override
public String getPath() {
return info.getPath()+"/"+getName();
}
/**
* @see JFSFile#isDirectory()
*/
@Override
public boolean isDirectory() {
return info.isDirectory();
}
/**
* @see JFSFile#mkdir()
*/
@Override
public boolean mkdir() {
boolean result = false;
try {
String url = getUrl(info.getPath()+"/"+info.getName());
LOG.debug("mkdir() creating {}", url);
access.createDirectory(url);
result = true;
} catch (IOException e) {
LOG.error("mkdir()", e);
} // try/catch
return result;
} // mkdir()
/**
* @see JFSFile#setLastModified(long)
*/
@Override
public boolean setLastModified(long time) {
info.setModificationDate(time);
String url = getUrl(info.getPath()+"/"+info.getName())+(isDirectory() ? "/" : "");
return DavUtils.setLastModified(access, url, time);
} // setLastModified()
/**
* @see JFSFile#setReadOnly()
*/
@Override
public boolean setReadOnly() {
if (JFSConfig.getInstance().isSetCanWrite()) {
info.setCanWrite(false);
} // if
return true;
} // setReadOnly()
/**
* @see JFSFile#preCopyTgt(JFSFile)
*/
@Override
protected boolean preCopyTgt(JFSFile srcFile) {
info.setModificationDate(srcFile.getLastModified());
// Set last modified and read-only only when file is no directory:
if (!srcFile.isDirectory()) {
info.setSize(srcFile.getLength());
if (!srcFile.canWrite()) {
info.setCanWrite(false);
} // if
} // if
return true;
} // preCopyTgt()
/**
* @see JFSFile#preCopySrc(JFSFile)
*/
@Override
protected boolean preCopySrc(JFSFile tgtFile) {
return true;
}
/**
* @see JFSFile#postCopyTgt(JFSFile)
*/
@Override
protected boolean postCopyTgt(JFSFile srcFile) {
// Update information object after copy. This method is only
// called if all operations were performed successfully:
info.setDirectory(srcFile.isDirectory());
info.setExists(srcFile.exists());
info.setSize(srcFile.getLength());
LOG.info("postCopyTgt() {}", getName());
setLastModified(srcFile.getLastModified());
return true;
} // postCopyTgt()
/**
* @see JFSFile#postCopySrc(JFSFile)
*/
@Override
protected boolean postCopySrc(JFSFile tgtFile) {
return true;
}
/**
* @see JFSFile#flush()
*/
@Override
public boolean flush() {
return true;
}
} // JFSWebDavFile