mirror of
https://github.com/moparisthebest/mailiverse
synced 2024-11-24 17:42:16 -05:00
add files
This commit is contained in:
parent
10028cd6e8
commit
e822c224af
19
java/core/src/core/connector/ConnectorException.java
Normal file
19
java/core/src/core/connector/ConnectorException.java
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* Author: Timothy Prepscius
|
||||||
|
* License: GPLv3 Affero + keep my name in the code!
|
||||||
|
*/
|
||||||
|
|
||||||
|
package core.connector;
|
||||||
|
|
||||||
|
public class ConnectorException extends Exception
|
||||||
|
{
|
||||||
|
public ConnectorException (Exception e)
|
||||||
|
{
|
||||||
|
super(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnectorException (String message)
|
||||||
|
{
|
||||||
|
super (message);
|
||||||
|
}
|
||||||
|
}
|
53
java/core/src/core/connector/FileInfo.java
Normal file
53
java/core/src/core/connector/FileInfo.java
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/**
|
||||||
|
* Author: Timothy Prepscius
|
||||||
|
* License: GPLv3 Affero + keep my name in the code!
|
||||||
|
*/
|
||||||
|
|
||||||
|
package core.connector;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
public class FileInfo
|
||||||
|
{
|
||||||
|
static public class SortByDateAscending implements Comparator<FileInfo>
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public int compare(FileInfo o1, FileInfo o2)
|
||||||
|
{
|
||||||
|
long time = o1.date.getTime() - o2.date.getTime();
|
||||||
|
return time == 0 ? 0 : (time > 0 ? 1 : -1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Type
|
||||||
|
{
|
||||||
|
Directory
|
||||||
|
}
|
||||||
|
|
||||||
|
public String path;
|
||||||
|
public String relativePath;
|
||||||
|
public long size;
|
||||||
|
public Type type;
|
||||||
|
public Date date;
|
||||||
|
public String version;
|
||||||
|
public Object user;
|
||||||
|
|
||||||
|
public FileInfo(String path, String relativePath, long size, Date date, String version)
|
||||||
|
{
|
||||||
|
this.path = path;
|
||||||
|
this.relativePath = relativePath;
|
||||||
|
this.size = size;
|
||||||
|
this.date = date;
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFileName()
|
||||||
|
{
|
||||||
|
int lastSlash = path.lastIndexOf('/');
|
||||||
|
if (lastSlash == -1)
|
||||||
|
return path;
|
||||||
|
|
||||||
|
return path.substring(lastSlash+1);
|
||||||
|
}
|
||||||
|
}
|
26
java/core/src/core/connector/async/AsyncStoreConnector.java
Normal file
26
java/core/src/core/connector/async/AsyncStoreConnector.java
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* Author: Timothy Prepscius
|
||||||
|
* License: GPLv3 Affero + keep my name in the code!
|
||||||
|
*/
|
||||||
|
|
||||||
|
package core.connector.async;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import core.callback.Callback;
|
||||||
|
|
||||||
|
public interface AsyncStoreConnector
|
||||||
|
{
|
||||||
|
Callback list_ (String path);
|
||||||
|
Callback createDirectory_ (String path);
|
||||||
|
Callback ensureDirectories_ (String[] directories);
|
||||||
|
|
||||||
|
Callback put_ (String path, byte[] bytes);
|
||||||
|
Callback get_ (String path);
|
||||||
|
|
||||||
|
Callback put_ (String path);
|
||||||
|
Callback get_ ();
|
||||||
|
|
||||||
|
Callback move_ (String from, String to);
|
||||||
|
Callback delete_ (String path);
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
/**
|
||||||
|
* Author: Timothy Prepscius
|
||||||
|
* License: GPLv3 Affero + keep my name in the code!
|
||||||
|
*/
|
||||||
|
|
||||||
|
package core.connector.async;
|
||||||
|
|
||||||
|
import core.callback.Callback;
|
||||||
|
import core.callback.CallbackDefault;
|
||||||
|
import core.util.Base64;
|
||||||
|
import core.util.Zip;
|
||||||
|
|
||||||
|
public abstract class AsyncStoreConnectorAdapter implements AsyncStoreConnector
|
||||||
|
{
|
||||||
|
AsyncStoreConnector connector;
|
||||||
|
|
||||||
|
AsyncStoreConnectorAdapter (AsyncStoreConnector connector)
|
||||||
|
{
|
||||||
|
this.connector = connector;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Callback list_(String path)
|
||||||
|
{
|
||||||
|
return connector.list_(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Callback createDirectory_(String path)
|
||||||
|
{
|
||||||
|
return connector.createDirectory_(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Callback ensureDirectories_(String[] directories)
|
||||||
|
{
|
||||||
|
return connector.ensureDirectories_(directories);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Callback get_(String path)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
new CallbackDefault(path) {
|
||||||
|
public void onSuccess(Object... arguments) throws Exception {
|
||||||
|
String path = V(0);
|
||||||
|
get_().setReturn(callback).invoke((String)path);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
public Callback get_()
|
||||||
|
{
|
||||||
|
return connector.get_();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Callback put_(String path, byte[] bytes)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
new CallbackDefault(path, bytes) {
|
||||||
|
public void onSuccess(Object... arguments) throws Exception {
|
||||||
|
String path = V(0);
|
||||||
|
byte[] bytes = V(1);
|
||||||
|
put_(path).setReturn(callback).invoke(bytes);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public Callback put_(String path)
|
||||||
|
{
|
||||||
|
return connector.put_(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Callback move_(String from, String to)
|
||||||
|
{
|
||||||
|
return connector.move_(from, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Callback delete_(String path)
|
||||||
|
{
|
||||||
|
return connector.delete_(path);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* Author: Timothy Prepscius
|
||||||
|
* License: GPLv3 Affero + keep my name in the code!
|
||||||
|
*/
|
||||||
|
|
||||||
|
package core.connector.async;
|
||||||
|
|
||||||
|
import core.callback.Callback;
|
||||||
|
import core.callback.CallbackDefault;
|
||||||
|
import core.callback.CallbackWithVariables;
|
||||||
|
import core.callbacks.SaveArguments;
|
||||||
|
import core.util.Base64;
|
||||||
|
|
||||||
|
|
||||||
|
public class AsyncStoreConnectorBase64 extends AsyncStoreConnectorAdapter
|
||||||
|
{
|
||||||
|
public AsyncStoreConnectorBase64(AsyncStoreConnector connector)
|
||||||
|
{
|
||||||
|
super(connector);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Callback get_()
|
||||||
|
{
|
||||||
|
SaveArguments save = new SaveArguments();
|
||||||
|
|
||||||
|
return super.get_()
|
||||||
|
.addCallback(save)
|
||||||
|
.addCallback(Base64.decodeBytes_())
|
||||||
|
.addCallback(save.restore_(0,101));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Callback put_(String path)
|
||||||
|
{
|
||||||
|
return Base64.encodeBytes_().addCallback(super.put_(path));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* Author: Timothy Prepscius
|
||||||
|
* License: GPLv3 Affero + keep my name in the code!
|
||||||
|
*/
|
||||||
|
|
||||||
|
package core.connector.async;
|
||||||
|
|
||||||
|
import core.callback.Callback;
|
||||||
|
import core.callback.CallbackDefault;
|
||||||
|
import core.callbacks.SaveArguments;
|
||||||
|
import core.crypt.Cryptor;
|
||||||
|
import core.util.Base64;
|
||||||
|
import core.util.Zip;
|
||||||
|
|
||||||
|
public class AsyncStoreConnectorEncrypted extends AsyncStoreConnectorAdapter
|
||||||
|
{
|
||||||
|
Cryptor cryptor;
|
||||||
|
|
||||||
|
public AsyncStoreConnectorEncrypted(Cryptor cryptor, AsyncStoreConnector connector)
|
||||||
|
{
|
||||||
|
super(connector);
|
||||||
|
this.cryptor = cryptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Callback get_()
|
||||||
|
{
|
||||||
|
SaveArguments save = new SaveArguments();
|
||||||
|
|
||||||
|
return super.get_()
|
||||||
|
.addCallback(save)
|
||||||
|
.addCallback(cryptor.decrypt_())
|
||||||
|
.addCallback(Zip.inflate_())
|
||||||
|
.addCallback(save.restore_(0,101));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Callback put_(String path)
|
||||||
|
{
|
||||||
|
return Zip.deflate_().addCallback(cryptor.encrypt_()).addCallback(super.put_(path));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,136 @@
|
|||||||
|
/**
|
||||||
|
* Author: Timothy Prepscius
|
||||||
|
* License: GPLv3 Affero + keep my name in the code!
|
||||||
|
*/
|
||||||
|
|
||||||
|
package core.connector.async;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import core.callback.Callback;
|
||||||
|
import core.callback.CallbackChain;
|
||||||
|
import core.callback.CallbackDefault;
|
||||||
|
import core.callback.CallbackEmpty;
|
||||||
|
import core.callback.CallbackWithVariables;
|
||||||
|
import core.connector.FileInfo;
|
||||||
|
|
||||||
|
public abstract class AsyncStoreConnectorHelper implements AsyncStoreConnector
|
||||||
|
{
|
||||||
|
abstract public void list(String path, Callback callback);
|
||||||
|
abstract public void createDirectory(String path, Callback callback);
|
||||||
|
abstract public void get(String path, Callback callback);
|
||||||
|
abstract public void put(String path, byte[] bytes, Callback callback);
|
||||||
|
abstract public void delete(String path, Callback callback);
|
||||||
|
// abstract public void move(String from, String to, Callback callback);
|
||||||
|
|
||||||
|
public void ensureDirectories(String[] folders, Callback callback)
|
||||||
|
{
|
||||||
|
CallbackChain chain = new CallbackChain();
|
||||||
|
|
||||||
|
for (String path : folders)
|
||||||
|
{
|
||||||
|
chain.addCallback(new CallbackWithVariables(path) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invoke(Object... arguments)
|
||||||
|
{
|
||||||
|
String path = V(0);
|
||||||
|
createDirectory(path, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
chain.setReturn(callback);
|
||||||
|
|
||||||
|
chain.invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Callback createDirectory_(String path)
|
||||||
|
{
|
||||||
|
return new CallbackDefault(path) {
|
||||||
|
public void onSuccess(Object... arguments) throws Exception {
|
||||||
|
createDirectory((String)V(0), callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Callback list_(String path)
|
||||||
|
{
|
||||||
|
return new CallbackDefault(path) {
|
||||||
|
public void onSuccess(Object... arguments) throws Exception {
|
||||||
|
list((String)V(0), callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Callback get_()
|
||||||
|
{
|
||||||
|
return new CallbackDefault() {
|
||||||
|
public void onSuccess(Object... arguments) throws Exception {
|
||||||
|
get((String)arguments[0], callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public Callback get_(String path)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
new CallbackDefault(path) {
|
||||||
|
public void onSuccess(Object... arguments) throws Exception {
|
||||||
|
get((String)V(0), callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Callback put_(String path)
|
||||||
|
{
|
||||||
|
return new CallbackDefault(path) {
|
||||||
|
public void onSuccess(Object... arguments) throws Exception {
|
||||||
|
put((String)V(0), (byte[])arguments[0], callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public Callback put_(String path, byte[] bytes)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
new CallbackDefault(path, bytes) {
|
||||||
|
public void onSuccess(Object... arguments) throws Exception {
|
||||||
|
put((String)V(0), (byte[])V(1), callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Callback move_(String from, String to)
|
||||||
|
{
|
||||||
|
return new CallbackEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Callback delete_(String path) {
|
||||||
|
return
|
||||||
|
new CallbackDefault(path) {
|
||||||
|
public void onSuccess(Object... arguments) throws Exception {
|
||||||
|
delete((String)V(0), callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Callback ensureDirectories_(String[] folders)
|
||||||
|
{
|
||||||
|
return new CallbackDefault(new Object[] { folders }) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Object... arguments) throws Exception {
|
||||||
|
ensureDirectories((String[])V(0), callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
244
java/core/src/core/connector/async/Lock.java
Normal file
244
java/core/src/core/connector/async/Lock.java
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
/**
|
||||||
|
* Author: Timothy Prepscius
|
||||||
|
* License: GPLv3 Affero + keep my name in the code!
|
||||||
|
*/
|
||||||
|
|
||||||
|
package core.connector.async;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import core.util.SecureRandom;
|
||||||
|
|
||||||
|
import core.callback.Callback;
|
||||||
|
import core.callback.CallbackDefault;
|
||||||
|
import core.connector.FileInfo;
|
||||||
|
import core.util.LogNull;
|
||||||
|
import core.util.LogOut;
|
||||||
|
|
||||||
|
public class Lock
|
||||||
|
{
|
||||||
|
static LogNull log = new LogNull(Lock.class);
|
||||||
|
static SecureRandom random = new SecureRandom();
|
||||||
|
|
||||||
|
public AsyncStoreConnector connector;
|
||||||
|
String path;
|
||||||
|
int intervalSeconds;
|
||||||
|
int remainingBeforeRelockSecond;
|
||||||
|
Date expiration;
|
||||||
|
String version;
|
||||||
|
|
||||||
|
public Lock(AsyncStoreConnector connector, String path, int intervalSeconds, int remainingBeforeRelockSecond)
|
||||||
|
{
|
||||||
|
this.connector = connector;
|
||||||
|
this.path = path;
|
||||||
|
this.intervalSeconds = intervalSeconds;
|
||||||
|
this.remainingBeforeRelockSecond = remainingBeforeRelockSecond;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void reset ()
|
||||||
|
{
|
||||||
|
version = null;
|
||||||
|
expiration = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Date getExpirationFor (Date timeLocked)
|
||||||
|
{
|
||||||
|
return new Date(timeLocked.getTime() + intervalSeconds * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean hasExpired (Date expiration)
|
||||||
|
{
|
||||||
|
Date now = new Date();
|
||||||
|
return now.after(expiration);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean closeToExpiration (Date expiration)
|
||||||
|
{
|
||||||
|
return getRemainingTimeInSeconds(expiration) < 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected long getRemainingTimeInSeconds (Date expiration)
|
||||||
|
{
|
||||||
|
Date now = new Date();
|
||||||
|
return (expiration.getTime() - now.getTime())/1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Callback lock_()
|
||||||
|
{
|
||||||
|
return
|
||||||
|
connector.list_(path)
|
||||||
|
.addCallback(lockOnInfo_())
|
||||||
|
.addCallback(possiblyLockIfNecessary_());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Callback relock_()
|
||||||
|
{
|
||||||
|
return new CallbackDefault()
|
||||||
|
{
|
||||||
|
public void onSuccess(Object... arguments) throws Exception
|
||||||
|
{
|
||||||
|
if (expiration == null || closeToExpiration(expiration))
|
||||||
|
{
|
||||||
|
log.debug(this, "lock close to expired or expired, going to fully lock");
|
||||||
|
call(lock_());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log.debug(this, "lock still active, relock only if necesary.");
|
||||||
|
call(possiblyLockIfNecessary_());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} ;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Callback possiblyLockIfNecessary_ ()
|
||||||
|
{
|
||||||
|
return new CallbackDefault() {
|
||||||
|
public void onSuccess(Object... arguments) throws Exception
|
||||||
|
{
|
||||||
|
long remainingTime = 0;
|
||||||
|
|
||||||
|
if (expiration != null)
|
||||||
|
{
|
||||||
|
remainingTime = getRemainingTimeInSeconds(expiration);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remainingTime < remainingBeforeRelockSecond)
|
||||||
|
{
|
||||||
|
log.debug(this, "remainingTime",remainingTime,"<", remainingBeforeRelockSecond, "LOCKING!");
|
||||||
|
|
||||||
|
byte[] bytes = new byte[8];
|
||||||
|
random.nextBytes(bytes);
|
||||||
|
|
||||||
|
call(
|
||||||
|
connector.put_(path, bytes)
|
||||||
|
.addCallback(storeLock_())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log.debug(this, "remainingTime",remainingTime,">=", remainingBeforeRelockSecond);
|
||||||
|
next(arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public Callback lockOnInfo_ ()
|
||||||
|
{
|
||||||
|
return new CallbackDefault () {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Object... arguments) throws Exception {
|
||||||
|
List<FileInfo> fileInfo = (List<FileInfo>) arguments[0];
|
||||||
|
|
||||||
|
boolean locked = false;
|
||||||
|
if (!fileInfo.isEmpty())
|
||||||
|
{
|
||||||
|
FileInfo info = fileInfo.get(0);
|
||||||
|
Date lockExpiration = getExpirationFor(info.date);
|
||||||
|
|
||||||
|
// it's not our lock
|
||||||
|
if (!info.version.equals(version))
|
||||||
|
{
|
||||||
|
locked = !hasExpired(lockExpiration);
|
||||||
|
reset();
|
||||||
|
|
||||||
|
log.debug(this, "file.date", info.date,"lockExpiration",lockExpiration,"locked",locked);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log.debug(this, "we have the lock!! setting expiration..", lockExpiration);
|
||||||
|
expiration = lockExpiration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (locked)
|
||||||
|
throw new Exception("Someone else has the lock");
|
||||||
|
|
||||||
|
next(arguments);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public Callback storeLock_ ()
|
||||||
|
{
|
||||||
|
return new CallbackDefault() {
|
||||||
|
public void onSuccess(Object... arguments) throws Exception {
|
||||||
|
expiration = getExpirationFor(new Date());
|
||||||
|
version = (String) arguments[0];
|
||||||
|
|
||||||
|
next(arguments);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testLock (List<FileInfo> fileInfo) throws Exception
|
||||||
|
{
|
||||||
|
boolean lockFound = false;
|
||||||
|
|
||||||
|
for (FileInfo i : fileInfo)
|
||||||
|
{
|
||||||
|
log.trace("testLock", i.path, path);
|
||||||
|
if (i.path.equals(path))
|
||||||
|
{
|
||||||
|
lockFound = true;
|
||||||
|
|
||||||
|
if (i.version != version)
|
||||||
|
{
|
||||||
|
throw new Exception("Lock was not obtained");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log.debug(this, "test lock found the lock, setting the expiration time to the file system.", i.date);
|
||||||
|
expiration = getExpirationFor(i.date);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasExpired(expiration))
|
||||||
|
throw new Exception("Lock has already expired");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lockFound)
|
||||||
|
throw new Exception("Lock not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Callback testLock_ ()
|
||||||
|
{
|
||||||
|
return new CallbackDefault () {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Object... arguments) throws Exception {
|
||||||
|
List<FileInfo> fileInfo = (List<FileInfo>) arguments[0];
|
||||||
|
testLock(fileInfo);
|
||||||
|
next(arguments);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public Callback unlock_()
|
||||||
|
{
|
||||||
|
return new CallbackDefault ()
|
||||||
|
{
|
||||||
|
public void onSuccess(Object... arguments) throws Exception
|
||||||
|
{
|
||||||
|
// never unlock, just let them expire
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (hasExpired(expiration))
|
||||||
|
{
|
||||||
|
next(new Exception("Lock has already expired"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
connector.delete_(path).setReturn(callback).invoke();
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
next(arguments);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
53
java/core/src/core/connector/dropbox/ClientInfoDropbox.java
Normal file
53
java/core/src/core/connector/dropbox/ClientInfoDropbox.java
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/**
|
||||||
|
* Author: Timothy Prepscius
|
||||||
|
* License: GPLv3 Affero + keep my name in the code!
|
||||||
|
*/
|
||||||
|
|
||||||
|
package core.connector.dropbox;
|
||||||
|
|
||||||
|
import core.constants.ConstantsDropbox;
|
||||||
|
import core.util.Environment;
|
||||||
|
|
||||||
|
|
||||||
|
public class ClientInfoDropbox
|
||||||
|
{
|
||||||
|
private String userPrefix;
|
||||||
|
private String appKey;
|
||||||
|
private String appSecret;
|
||||||
|
private String tokenKey;
|
||||||
|
private String tokenSecret;
|
||||||
|
|
||||||
|
public ClientInfoDropbox (Environment e)
|
||||||
|
{
|
||||||
|
userPrefix = e.get(ConstantsDropbox.DropboxUserPrefix);
|
||||||
|
appKey = e.checkGet(ConstantsDropbox.DropboxAppKey);
|
||||||
|
appSecret = e.checkGet(ConstantsDropbox.DropboxAppSecret);
|
||||||
|
tokenKey = e.checkGet(ConstantsDropbox.DropboxTokenKey);
|
||||||
|
tokenSecret = e.checkGet(ConstantsDropbox.DropboxTokenSecret);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserPrefix ()
|
||||||
|
{
|
||||||
|
return userPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAppKey ()
|
||||||
|
{
|
||||||
|
return appKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAppSecret ()
|
||||||
|
{
|
||||||
|
return appSecret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTokenKey ()
|
||||||
|
{
|
||||||
|
return tokenKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTokenSecret ()
|
||||||
|
{
|
||||||
|
return tokenSecret;
|
||||||
|
}
|
||||||
|
}
|
375
java/core/src/core/connector/dropbox/async/ConnectorDropbox.java
Normal file
375
java/core/src/core/connector/dropbox/async/ConnectorDropbox.java
Normal file
@ -0,0 +1,375 @@
|
|||||||
|
/**
|
||||||
|
* Author: Timothy Prepscius
|
||||||
|
* License: GPLv3 Affero + keep my name in the code!
|
||||||
|
*/
|
||||||
|
|
||||||
|
package core.connector.dropbox.async;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import core.callback.Callback;
|
||||||
|
import core.callback.CallbackDefault;
|
||||||
|
import core.callback.CallbackWithVariables;
|
||||||
|
import core.connector.FileInfo;
|
||||||
|
import core.connector.async.AsyncStoreConnectorHelper;
|
||||||
|
import core.connector.dropbox.ClientInfoDropbox;
|
||||||
|
import core.util.DateFormat;
|
||||||
|
import core.util.FastRandom;
|
||||||
|
import core.util.HttpDelegate;
|
||||||
|
import core.util.LogNull;
|
||||||
|
|
||||||
|
public class ConnectorDropbox extends AsyncStoreConnectorHelper
|
||||||
|
{
|
||||||
|
static LogNull log = new LogNull(ConnectorDropbox.class);
|
||||||
|
|
||||||
|
ClientInfoDropbox info;
|
||||||
|
HttpDelegate httpDelegate;
|
||||||
|
FastRandom fastRandom;
|
||||||
|
|
||||||
|
public ConnectorDropbox(ClientInfoDropbox clientInfo, HttpDelegate httpDelegate)
|
||||||
|
{
|
||||||
|
this.info = clientInfo;
|
||||||
|
this.httpDelegate = httpDelegate;
|
||||||
|
fastRandom = new FastRandom();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getGlobalPath (String path)
|
||||||
|
{
|
||||||
|
if (info.getUserPrefix() != null)
|
||||||
|
return info.getUserPrefix() + "/" + path;
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void listDirectoryFinished (String containingPath, Callback callback, Object... arguments)
|
||||||
|
{
|
||||||
|
log.debug("listDirectoryFinished");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (arguments[0] instanceof Exception)
|
||||||
|
throw (Exception)arguments[0];
|
||||||
|
|
||||||
|
String result = (String)arguments[0];
|
||||||
|
List<FileInfo> fileInfos = new ArrayList<FileInfo>();
|
||||||
|
|
||||||
|
JSONObject o = new JSONObject(result);
|
||||||
|
JSONArray contents = (JSONArray) o.get("contents");
|
||||||
|
|
||||||
|
DateFormat dateTimeFormat = new DateFormat("EEE, d MMM yyyy HH:mm:ss Z");
|
||||||
|
|
||||||
|
for (int i=0; i<contents.length(); ++i)
|
||||||
|
{
|
||||||
|
JSONObject f = (JSONObject) contents.get(i);
|
||||||
|
String dropboxPath = (String) f.get("path");
|
||||||
|
|
||||||
|
int realPath = dropboxPath.indexOf(containingPath);
|
||||||
|
if (realPath == -1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
String path = dropboxPath.substring(realPath);
|
||||||
|
String relativePath = path.substring(containingPath.length());
|
||||||
|
|
||||||
|
long size = ((Integer)f.get("bytes")).longValue();
|
||||||
|
String time = (String) f.get("modified");
|
||||||
|
String revision = (String) f.getString("rev");
|
||||||
|
|
||||||
|
Date date = dateTimeFormat.parse(time);
|
||||||
|
FileInfo fi = new FileInfo(path, relativePath, size, date, revision);
|
||||||
|
|
||||||
|
fileInfos.add(fi);
|
||||||
|
}
|
||||||
|
|
||||||
|
Collections.sort(fileInfos, new FileInfo.SortByDateAscending());
|
||||||
|
for (FileInfo i : fileInfos)
|
||||||
|
log.debug ("path: ", i.path, " date:", i.date);
|
||||||
|
|
||||||
|
callback.invoke(fileInfos);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
|
||||||
|
callback.invoke(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void searchDirectoryFinished (String containingPath, Callback callback, Object... arguments)
|
||||||
|
{
|
||||||
|
log.debug("listDirectoryFinished");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (arguments[0] instanceof Exception)
|
||||||
|
throw (Exception)arguments[0];
|
||||||
|
|
||||||
|
String result = (String)arguments[0];
|
||||||
|
List<FileInfo> fileInfos = new ArrayList<FileInfo>();
|
||||||
|
|
||||||
|
JSONArray contents = new JSONArray(result);
|
||||||
|
DateFormat dateTimeFormat = new DateFormat("EEE, d MMM yyyy HH:mm:ss Z");
|
||||||
|
|
||||||
|
for (int i=0; i<contents.length(); ++i)
|
||||||
|
{
|
||||||
|
JSONObject f = (JSONObject) contents.get(i);
|
||||||
|
String dropboxPath = (String) f.get("path");
|
||||||
|
|
||||||
|
int realPath = dropboxPath.indexOf(containingPath);
|
||||||
|
if (realPath == -1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
String path = dropboxPath.substring(realPath);
|
||||||
|
String relativePath = path.substring(containingPath.length());
|
||||||
|
|
||||||
|
long size = ((Integer)f.get("bytes")).longValue();
|
||||||
|
String time = (String) f.get("modified");
|
||||||
|
String revision = (String) f.getString("rev");
|
||||||
|
|
||||||
|
Date date = dateTimeFormat.parse(time);
|
||||||
|
FileInfo fi = new FileInfo(path, relativePath, size, date, revision);
|
||||||
|
|
||||||
|
fileInfos.add(fi);
|
||||||
|
}
|
||||||
|
|
||||||
|
Collections.sort(fileInfos, new FileInfo.SortByDateAscending());
|
||||||
|
for (FileInfo i : fileInfos)
|
||||||
|
log.debug ("path: ", i.path, " date:", i.date);
|
||||||
|
|
||||||
|
callback.invoke(fileInfos);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
|
||||||
|
callback.invoke(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void list (String path, Callback callback)
|
||||||
|
{
|
||||||
|
if (path.endsWith("/"))
|
||||||
|
doList(path, callback);
|
||||||
|
else
|
||||||
|
doSearch(path, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doSearch(String path, Callback callback)
|
||||||
|
{
|
||||||
|
log.debug("searchDirectory",path);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String API_METADATA_URL="https://api.dropbox.com/1/search/sandbox";
|
||||||
|
|
||||||
|
String globalPath = getGlobalPath(path);
|
||||||
|
String directory = globalPath.substring(0, globalPath.lastIndexOf('/'));
|
||||||
|
String file = globalPath.substring(globalPath.lastIndexOf('/')+1);
|
||||||
|
|
||||||
|
String url =
|
||||||
|
API_METADATA_URL +
|
||||||
|
"/" + directory +
|
||||||
|
"?query=" + file +
|
||||||
|
"&oauth_consumer_key=" + info.getAppKey() +
|
||||||
|
"&oauth_token=" + info.getTokenKey() +
|
||||||
|
"&oauth_signature_method=PLAINTEXT" +
|
||||||
|
"&oauth_signature=" + info.getAppSecret() + "%26" + info.getTokenSecret() +
|
||||||
|
"&oauth_timestamp=" + new Date().getTime() +
|
||||||
|
"&oauth_nonce=" + fastRandom.nextInt();
|
||||||
|
|
||||||
|
httpDelegate.execute (HttpDelegate.GET, url, null, false, false, null,
|
||||||
|
new CallbackWithVariables(callback, path) {
|
||||||
|
@Override
|
||||||
|
public void invoke(Object... arguments)
|
||||||
|
{
|
||||||
|
Callback callback = V(0);
|
||||||
|
String path = V(1);
|
||||||
|
searchDirectoryFinished(path, callback, arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
callback.invoke(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doList(String path, Callback callback)
|
||||||
|
{
|
||||||
|
log.debug("listDirectory",path);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String API_METADATA_URL="https://api.dropbox.com/1/metadata/sandbox";
|
||||||
|
|
||||||
|
String url =
|
||||||
|
API_METADATA_URL +
|
||||||
|
"/" + getGlobalPath(path) + "?" +
|
||||||
|
"oauth_consumer_key=" + info.getAppKey() + "&" +
|
||||||
|
"oauth_token=" + info.getTokenKey() + "&" +
|
||||||
|
"oauth_signature_method=PLAINTEXT" + "&" +
|
||||||
|
"oauth_signature=" + info.getAppSecret() + "%26" + info.getTokenSecret() + "&" +
|
||||||
|
"oauth_timestamp=" + new Date().getTime() + "&" +
|
||||||
|
"oauth_nonce=" + fastRandom.nextInt();
|
||||||
|
|
||||||
|
httpDelegate.execute (HttpDelegate.GET, url, null, false, false, null,
|
||||||
|
new CallbackWithVariables(callback, path) {
|
||||||
|
@Override
|
||||||
|
public void invoke(Object... arguments)
|
||||||
|
{
|
||||||
|
Callback callback = V(0);
|
||||||
|
String path = V(1);
|
||||||
|
listDirectoryFinished(path, callback, arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
callback.invoke(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createDirectory(String path, Callback callback)
|
||||||
|
{
|
||||||
|
log.debug("createDirectory",path);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String API_METADATA_URL="https://api.dropbox.com/1/fileops/create_folder";
|
||||||
|
|
||||||
|
String url =
|
||||||
|
API_METADATA_URL +
|
||||||
|
"?root=sandbox" +
|
||||||
|
"&path="+ getGlobalPath(path) +
|
||||||
|
"&locale=en" +
|
||||||
|
"&oauth_consumer_key=" + info.getAppKey() +
|
||||||
|
"&oauth_token=" + info.getTokenKey() +
|
||||||
|
"&oauth_signature_method=PLAINTEXT" +
|
||||||
|
"&oauth_signature=" + info.getAppSecret() + "%26" + info.getTokenSecret() +
|
||||||
|
"&oauth_timestamp=" + new Date().getTime() +
|
||||||
|
"&oauth_nonce=" + fastRandom.nextInt();
|
||||||
|
|
||||||
|
httpDelegate.execute(HttpDelegate.GET, url, null, false, false, null, callback);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
callback.invoke(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void get(String path, Callback callback)
|
||||||
|
{
|
||||||
|
log.debug("get",path);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String API_METADATA_URL="https://api-content.dropbox.com/1/files/sandbox";
|
||||||
|
|
||||||
|
String url =
|
||||||
|
API_METADATA_URL +
|
||||||
|
"/" + getGlobalPath(path) + "?" +
|
||||||
|
"oauth_consumer_key=" + info.getAppKey() + "&" +
|
||||||
|
"oauth_token=" + info.getTokenKey() + "&" +
|
||||||
|
"oauth_signature_method=PLAINTEXT" + "&" +
|
||||||
|
"oauth_signature=" + info.getAppSecret() + "%26" + info.getTokenSecret() + "&" +
|
||||||
|
"oauth_timestamp=" + new Date().getTime() + "&" +
|
||||||
|
"oauth_nonce=" + fastRandom.nextInt();
|
||||||
|
|
||||||
|
log.debug(url);
|
||||||
|
|
||||||
|
httpDelegate.execute(HttpDelegate.GET, url, null, false, true, null, grabVersionGet_().setReturn(callback));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
callback.invoke(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void put(String path, byte[] contents, Callback callback)
|
||||||
|
{
|
||||||
|
log.debug("put",path);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String API_METADATA_URL="https://api-content.dropbox.com/1/files_put/sandbox";
|
||||||
|
|
||||||
|
String url =
|
||||||
|
API_METADATA_URL +
|
||||||
|
"/" + getGlobalPath(path) + "?" +
|
||||||
|
"oauth_consumer_key=" + info.getAppKey() + "&" +
|
||||||
|
"oauth_token=" + info.getTokenKey() + "&" +
|
||||||
|
"oauth_signature_method=PLAINTEXT" + "&" +
|
||||||
|
"oauth_signature=" + info.getAppSecret() + "%26" + info.getTokenSecret() + "&" +
|
||||||
|
"oauth_timestamp=" + new Date().getTime() + "&" +
|
||||||
|
"oauth_nonce=" + fastRandom.nextInt();
|
||||||
|
|
||||||
|
log.debug(url);
|
||||||
|
|
||||||
|
httpDelegate.execute(HttpDelegate.PUT, url, null, true, false, contents, grabVersionPut_().setReturn(callback));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
callback.invoke(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete(String path, Callback callback)
|
||||||
|
{
|
||||||
|
log.debug("delete",path);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String API_METADATA_URL="https://api.dropbox.com/1/fileops/delete";
|
||||||
|
|
||||||
|
String url =
|
||||||
|
API_METADATA_URL +
|
||||||
|
"?root=sandbox" +
|
||||||
|
"&path=" + getGlobalPath(path) +
|
||||||
|
"&oauth_consumer_key=" + info.getAppKey() +
|
||||||
|
"&oauth_token=" + info.getTokenKey() +
|
||||||
|
"&oauth_signature_method=PLAINTEXT" +
|
||||||
|
"&oauth_signature=" + info.getAppSecret() + "%26" + info.getTokenSecret() +
|
||||||
|
"&oauth_timestamp=" + new Date().getTime() +
|
||||||
|
"&oauth_nonce=" + fastRandom.nextInt();
|
||||||
|
|
||||||
|
log.debug(url);
|
||||||
|
|
||||||
|
httpDelegate.execute(HttpDelegate.POST, url, null, false, false, null, callback);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
callback.invoke(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Callback grabVersionPut_()
|
||||||
|
{
|
||||||
|
return new CallbackDefault() {
|
||||||
|
public void onSuccess(Object... arguments) throws Exception {
|
||||||
|
String response = (String) arguments[0];
|
||||||
|
JSONObject json = new JSONObject(response);
|
||||||
|
next(json.getString("rev"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public Callback grabVersionGet_()
|
||||||
|
{
|
||||||
|
return new CallbackDefault() {
|
||||||
|
public void onSuccess(Object... arguments) throws Exception {
|
||||||
|
String[][] headers = (String[][])arguments[1];
|
||||||
|
for (String[] pair : headers)
|
||||||
|
{
|
||||||
|
if (pair[0].equals("x-dropbox-metadata"))
|
||||||
|
{
|
||||||
|
JSONObject json = new JSONObject(pair[1]);
|
||||||
|
next(arguments[0], json.getString("rev"));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception("No x-dropbox-metadata response header");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
229
java/core/src/core/connector/dropbox/sync/DropboxConnector.java
Normal file
229
java/core/src/core/connector/dropbox/sync/DropboxConnector.java
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
/**
|
||||||
|
* Author: Timothy Prepscius
|
||||||
|
* License: GPLv3 Affero + keep my name in the code!
|
||||||
|
*/
|
||||||
|
|
||||||
|
package core.connector.dropbox.sync;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import core.connector.dropbox.ClientInfoDropbox;
|
||||||
|
|
||||||
|
import com.dropbox.client2.DropboxAPI;
|
||||||
|
import com.dropbox.client2.DropboxAPI.Entry;
|
||||||
|
import com.dropbox.client2.session.AccessTokenPair;
|
||||||
|
import com.dropbox.client2.session.AppKeyPair;
|
||||||
|
import com.dropbox.client2.session.Session;
|
||||||
|
import com.dropbox.client2.session.WebAuthSession;
|
||||||
|
|
||||||
|
import core.connector.ConnectorException;
|
||||||
|
import core.connector.FileInfo;
|
||||||
|
import core.connector.sync.StoreConnector;
|
||||||
|
import core.util.Streams;
|
||||||
|
|
||||||
|
|
||||||
|
public class DropboxConnector implements StoreConnector
|
||||||
|
{
|
||||||
|
ClientInfoDropbox clientInfo;
|
||||||
|
DropboxAPI<?> db;
|
||||||
|
|
||||||
|
public DropboxConnector (ClientInfoDropbox clientInfo)
|
||||||
|
{
|
||||||
|
this.clientInfo = clientInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DropboxAPI<?> createConnection (ClientInfoDropbox info)
|
||||||
|
{
|
||||||
|
AppKeyPair appKeyPair = new AppKeyPair(info.getAppKey(),info.getAppSecret());
|
||||||
|
AccessTokenPair userTokenKeyPair = new AccessTokenPair(info.getTokenKey(), info.getTokenSecret());
|
||||||
|
|
||||||
|
WebAuthSession sourceSession =
|
||||||
|
new WebAuthSession(appKeyPair, Session.AccessType.APP_FOLDER, userTokenKeyPair);
|
||||||
|
|
||||||
|
DropboxAPI<?> sourceClient = new DropboxAPI<WebAuthSession>(sourceSession);
|
||||||
|
|
||||||
|
return sourceClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void open () throws ConnectorException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
db = createConnection(clientInfo);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new ConnectorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close ()
|
||||||
|
{
|
||||||
|
db = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getGlobalPath (String path)
|
||||||
|
{
|
||||||
|
if (clientInfo.getUserPrefix() != null)
|
||||||
|
return "/" + clientInfo.getUserPrefix() + "/" + path;
|
||||||
|
|
||||||
|
return "/" + path;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getUserPath (String path)
|
||||||
|
{
|
||||||
|
if (clientInfo.getUserPrefix() != null)
|
||||||
|
return path.substring(2 + clientInfo.getUserPrefix().length());
|
||||||
|
|
||||||
|
return path.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<FileInfo> listDirectory(String path) throws ConnectorException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!path.endsWith("/"))
|
||||||
|
path = path + "/";
|
||||||
|
|
||||||
|
Entry directory = db.metadata(getGlobalPath(path), 10000, null, true, null);
|
||||||
|
List<FileInfo> listing = new ArrayList<FileInfo>(directory.contents.size());
|
||||||
|
|
||||||
|
SimpleDateFormat dateTimeFormat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z");
|
||||||
|
for (Entry file : directory.contents)
|
||||||
|
{
|
||||||
|
if (file.isDeleted)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
String fullPath = getUserPath(file.path);
|
||||||
|
String relativePath = fullPath.substring(path.length());
|
||||||
|
|
||||||
|
listing.add(
|
||||||
|
new FileInfo(
|
||||||
|
fullPath,
|
||||||
|
relativePath,
|
||||||
|
file.bytes, dateTimeFormat.parse(file.modified),
|
||||||
|
file.rev
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Collections.sort(listing, new FileInfo.SortByDateAscending());
|
||||||
|
|
||||||
|
return listing;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new ConnectorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createDirectory(String path) throws ConnectorException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
db.createFolder(getGlobalPath(path));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new ConnectorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] get(String path) throws ConnectorException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Entry meta = db.metadata(getGlobalPath(path), 1, null, true, null);
|
||||||
|
if (meta.isDeleted)
|
||||||
|
throw new ConnectorException("File deleted");
|
||||||
|
|
||||||
|
return Streams.readFullyBytes(db.getFileStream(getGlobalPath(path), null));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new ConnectorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] get(String path, long size) throws ConnectorException
|
||||||
|
{
|
||||||
|
return get(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void put(String path, byte[] contents) throws ConnectorException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
db.putFileOverwrite(getGlobalPath(path), new ByteArrayInputStream(contents), contents.length, null);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new ConnectorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void move(String from, String to) throws ConnectorException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
db.move(getGlobalPath(from), getGlobalPath(to));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new ConnectorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete(String path) throws ConnectorException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
db.delete(getGlobalPath(path));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new ConnectorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean ensureDirectories (String ... folders)
|
||||||
|
{
|
||||||
|
for (String folder : folders)
|
||||||
|
{
|
||||||
|
String[] parts = folder.split("/");
|
||||||
|
|
||||||
|
String path = "";
|
||||||
|
for (String part : parts)
|
||||||
|
{
|
||||||
|
if (!path.isEmpty())
|
||||||
|
path += "/";
|
||||||
|
|
||||||
|
path += part;
|
||||||
|
String fullPath = getGlobalPath(path);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
db.createFolder(fullPath);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
System.out.format("Folder[%s] already exists.\n", fullPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
82
java/core/src/core/connector/dropbox/sync/DropboxSignup.java
Normal file
82
java/core/src/core/connector/dropbox/sync/DropboxSignup.java
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
/**
|
||||||
|
* Author: Timothy Prepscius
|
||||||
|
* License: GPLv3 Affero + keep my name in the code!
|
||||||
|
*/
|
||||||
|
|
||||||
|
package core.connector.dropbox.sync;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import com.dropbox.client2.session.AccessTokenPair;
|
||||||
|
import com.dropbox.client2.session.AppKeyPair;
|
||||||
|
|
||||||
|
import core.util.HttpDelegate;
|
||||||
|
import core.util.LogNull;
|
||||||
|
import core.util.LogOut;
|
||||||
|
import core.util.Pair;
|
||||||
|
import core.util.Streams;
|
||||||
|
|
||||||
|
public class DropboxSignup
|
||||||
|
{
|
||||||
|
static LogNull log = new LogNull (DropboxSignup.class);
|
||||||
|
|
||||||
|
static public AccessTokenPair getDropboxRequestToken (AppKeyPair appKeyPair) throws Exception
|
||||||
|
{
|
||||||
|
log.debug("getDropboxUserToken");
|
||||||
|
URL url = new URL(
|
||||||
|
"https://api.dropbox.com/1/oauth/request_token" +
|
||||||
|
"?oauth_consumer_key=" + appKeyPair.key +
|
||||||
|
"&oauth_signature_method=PLAINTEXT" +
|
||||||
|
"&oauth_signature=" + appKeyPair.secret + "%26" +
|
||||||
|
"&oauth_nonce=\"" + (new Date()).getTime() + "\""
|
||||||
|
);
|
||||||
|
URLConnection c = url.openConnection();
|
||||||
|
String response = Streams.readFullyString(c.getInputStream(), "UTF-8");
|
||||||
|
|
||||||
|
Pair<String,String> token = parseAuthToken(response);
|
||||||
|
return new AccessTokenPair(token.first, token.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
static public AccessTokenPair getDropboxAccessToken (AppKeyPair appKeyPair, AccessTokenPair accessToken) throws Exception
|
||||||
|
{
|
||||||
|
URL url = new URL(
|
||||||
|
"https://api.dropbox.com/1/oauth/access_token" +
|
||||||
|
"?oauth_consumer_key=" + appKeyPair.key +
|
||||||
|
"&oauth_token=" + accessToken.key + "&" +
|
||||||
|
"&oauth_signature_method=PLAINTEXT" +
|
||||||
|
"&oauth_signature=" + appKeyPair.secret + "%26" + accessToken.secret +
|
||||||
|
"&oauth_nonce=\"" + (new Date()).getTime() + "\""
|
||||||
|
);
|
||||||
|
URLConnection c = url.openConnection();
|
||||||
|
String response = Streams.readFullyString(c.getInputStream(), "UTF-8");
|
||||||
|
|
||||||
|
Pair<String,String> token = parseAuthToken(response);
|
||||||
|
return new AccessTokenPair(token.first, token.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Pair<String,String> parseAuthToken (String response) throws Exception
|
||||||
|
{
|
||||||
|
String userKey=null, userSecret=null;
|
||||||
|
String[] parts = response.split("&");
|
||||||
|
for (String part : parts)
|
||||||
|
{
|
||||||
|
String[] keyValue = part.split("=");
|
||||||
|
String key = keyValue[0];
|
||||||
|
String value = keyValue[1];
|
||||||
|
|
||||||
|
if (key.equalsIgnoreCase("oauth_token_secret"))
|
||||||
|
userSecret = value;
|
||||||
|
else
|
||||||
|
if (key.equalsIgnoreCase("oauth_token"))
|
||||||
|
userKey = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userSecret == null || userKey == null)
|
||||||
|
throw new Exception ("Could parse authToken");
|
||||||
|
|
||||||
|
return new Pair<String,String>(userKey, userSecret);
|
||||||
|
}
|
||||||
|
}
|
124
java/core/src/core/connector/misc/sync/FileSystemConnector.java
Normal file
124
java/core/src/core/connector/misc/sync/FileSystemConnector.java
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
/**
|
||||||
|
* Author: Timothy Prepscius
|
||||||
|
* License: GPLv3 Affero + keep my name in the code!
|
||||||
|
*/
|
||||||
|
|
||||||
|
package core.connector.misc.sync;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import core.connector.ConnectorException;
|
||||||
|
import core.connector.FileInfo;
|
||||||
|
import core.connector.sync.StoreConnector;
|
||||||
|
import core.util.FileSystem;
|
||||||
|
import core.util.Streams;
|
||||||
|
|
||||||
|
|
||||||
|
public class FileSystemConnector implements StoreConnector
|
||||||
|
{
|
||||||
|
String prefix;
|
||||||
|
|
||||||
|
public FileSystemConnector (String root)
|
||||||
|
{
|
||||||
|
this.prefix = root + "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void open() throws ConnectorException
|
||||||
|
{
|
||||||
|
File directory = new File(prefix);
|
||||||
|
if (!directory.exists())
|
||||||
|
throw new ConnectorException("Store directory does not exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<FileInfo> listDirectory(String path) throws ConnectorException
|
||||||
|
{
|
||||||
|
return FileSystem.allFilesFor(new File(prefix + path));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createDirectory(String path) throws ConnectorException
|
||||||
|
{
|
||||||
|
File dir = new File (prefix + path);
|
||||||
|
if (!dir.mkdir())
|
||||||
|
{
|
||||||
|
throw new ConnectorException("Unable to create directory");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] get(String path) throws ConnectorException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Streams.readFullyBytes(new FileInputStream(prefix + path));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new ConnectorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] get(String path, long size) throws ConnectorException
|
||||||
|
{
|
||||||
|
return get(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void put(String path, byte[] contents) throws ConnectorException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (path.contains("/"))
|
||||||
|
ensureDirectories(path.substring(0, path.lastIndexOf("/")));
|
||||||
|
|
||||||
|
FileOutputStream fos = new FileOutputStream(prefix + path);
|
||||||
|
fos.write(contents);
|
||||||
|
fos.close();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
throw new ConnectorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void move(String from, String to) throws ConnectorException
|
||||||
|
{
|
||||||
|
File fromFile = new File(prefix + from);
|
||||||
|
File toFile = new File (prefix, to);
|
||||||
|
|
||||||
|
if (!fromFile.renameTo(toFile))
|
||||||
|
throw new ConnectorException("move file failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete(String path) throws ConnectorException
|
||||||
|
{
|
||||||
|
File file = new File(prefix + path);
|
||||||
|
if (!file.delete())
|
||||||
|
throw new ConnectorException("delete file failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean ensureDirectories (String ... folders)
|
||||||
|
{
|
||||||
|
for (String folder : folders)
|
||||||
|
{
|
||||||
|
File path = new File (prefix + folder);
|
||||||
|
path.mkdirs();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
58
java/core/src/core/connector/s3/ClientInfoS3.java
Normal file
58
java/core/src/core/connector/s3/ClientInfoS3.java
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/**
|
||||||
|
* Author: Timothy Prepscius
|
||||||
|
* License: GPLv3 Affero + keep my name in the code!
|
||||||
|
*/
|
||||||
|
|
||||||
|
package core.connector.s3;
|
||||||
|
|
||||||
|
import core.constants.ConstantsS3;
|
||||||
|
import core.util.Environment;
|
||||||
|
|
||||||
|
|
||||||
|
public class ClientInfoS3
|
||||||
|
{
|
||||||
|
private String bucketName;
|
||||||
|
private String bucketRegion;
|
||||||
|
private String accessId;
|
||||||
|
private String secretKey;
|
||||||
|
|
||||||
|
public ClientInfoS3 (Environment e)
|
||||||
|
{
|
||||||
|
bucketName = e.get(ConstantsS3.AWSBucketName);
|
||||||
|
bucketRegion = e.get(ConstantsS3.AWSBucketRegion);
|
||||||
|
accessId = e.checkGet(ConstantsS3.AWSAccessKeyId);
|
||||||
|
secretKey = e.checkGet(ConstantsS3.AWSSecretKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBucketEndpoint ()
|
||||||
|
{
|
||||||
|
String bucketEndPoint = null;
|
||||||
|
|
||||||
|
if (bucketRegion == null || bucketRegion.equals(""))
|
||||||
|
bucketEndPoint = "s3.amazonaws.com";
|
||||||
|
else
|
||||||
|
bucketEndPoint = "s3-" + bucketRegion + ".amazonaws.com";
|
||||||
|
|
||||||
|
return bucketEndPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBucketName()
|
||||||
|
{
|
||||||
|
return bucketName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBucketRegion ()
|
||||||
|
{
|
||||||
|
return bucketRegion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAccessId()
|
||||||
|
{
|
||||||
|
return accessId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSecretKey()
|
||||||
|
{
|
||||||
|
return secretKey;
|
||||||
|
}
|
||||||
|
}
|
351
java/core/src/core/connector/s3/async/S3Connector.java
Normal file
351
java/core/src/core/connector/s3/async/S3Connector.java
Normal file
@ -0,0 +1,351 @@
|
|||||||
|
/**
|
||||||
|
* Author: Timothy Prepscius
|
||||||
|
* License: GPLv3 Affero + keep my name in the code!
|
||||||
|
*/
|
||||||
|
|
||||||
|
package core.connector.s3.async;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import core.util.Base64;
|
||||||
|
import core.callback.Callback;
|
||||||
|
import core.callback.CallbackDefault;
|
||||||
|
import core.callback.CallbackWithVariables;
|
||||||
|
import core.connector.FileInfo;
|
||||||
|
import core.connector.async.AsyncStoreConnectorHelper;
|
||||||
|
import core.connector.s3.ClientInfoS3;
|
||||||
|
import core.crypt.HashSha256;
|
||||||
|
import core.crypt.HmacSha1;
|
||||||
|
import core.util.DateFormat;
|
||||||
|
import core.util.FastRandom;
|
||||||
|
import core.util.HttpDelegate;
|
||||||
|
import core.util.LogNull;
|
||||||
|
import core.util.Strings;
|
||||||
|
import core.util.XML;
|
||||||
|
|
||||||
|
public class S3Connector extends AsyncStoreConnectorHelper
|
||||||
|
{
|
||||||
|
static LogNull log = new LogNull(S3Connector.class);
|
||||||
|
final int LOCK_INTERVAL = 10 * 1000;
|
||||||
|
|
||||||
|
ClientInfoS3 info;
|
||||||
|
HttpDelegate httpDelegate;
|
||||||
|
|
||||||
|
HmacSha1 mac;
|
||||||
|
static FastRandom fastRandom = new FastRandom();
|
||||||
|
|
||||||
|
|
||||||
|
protected String createUrlPrefix ()
|
||||||
|
{
|
||||||
|
return "https://" + info.getBucketEndpoint() + "/" + info.getBucketName() + "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String createRandomPostfix ()
|
||||||
|
{
|
||||||
|
return "random=" + fastRandom.nextLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method converts AWSSecretKey into crypto instance.
|
||||||
|
protected void setKey(String AWSSecretKey) throws Exception
|
||||||
|
{
|
||||||
|
mac = new HmacSha1(Strings.toBytes(AWSSecretKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method creates S3 signature for a given String.
|
||||||
|
protected String sign(String data) throws Exception
|
||||||
|
{
|
||||||
|
// Signed String must be BASE64 encoded.
|
||||||
|
byte[] signBytes = mac.mac(Strings.toBytes(data));
|
||||||
|
String signature = Base64.encode(signBytes);
|
||||||
|
return signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String format(String format, Date date)
|
||||||
|
{
|
||||||
|
DateFormat df = new DateFormat(format);
|
||||||
|
String dateString = df.format(date, 0) + " GMT";
|
||||||
|
return dateString;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String[][] makeHeaders (String keyId, String method, String contentMD5, String contentType, int contentLength, Date date, String resource) throws Exception
|
||||||
|
{
|
||||||
|
String fmt = "EEE, dd MMM yyyy HH:mm:ss";
|
||||||
|
String dateString = format(fmt, date);
|
||||||
|
|
||||||
|
// Generate signature
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
buf.append(method).append("\n");
|
||||||
|
buf.append(contentMD5).append("\n");
|
||||||
|
buf.append(contentType).append("\n");
|
||||||
|
buf.append("\n"); // empty real date header
|
||||||
|
buf.append("x-amz-date:");
|
||||||
|
buf.append(dateString).append("\n");
|
||||||
|
buf.append(resource);
|
||||||
|
|
||||||
|
log.debug("Signing:{" + buf.toString() + "}");
|
||||||
|
String signature = sign(buf.toString());
|
||||||
|
|
||||||
|
String[][] headers;
|
||||||
|
if (method.equals("PUT"))
|
||||||
|
{
|
||||||
|
headers = new String[][] {
|
||||||
|
{"X-Amz-Date" , dateString },
|
||||||
|
{"Content-Type", contentType },
|
||||||
|
{"Content-Length", ""+contentLength },
|
||||||
|
{"Authorization", "AWS " + keyId + ":" + signature }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
headers = new String[][] {
|
||||||
|
{"X-Amz-Date" , dateString },
|
||||||
|
{"Authorization", "AWS " + keyId + ":" + signature }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public S3Connector(ClientInfoS3 clientInfo, HttpDelegate httpDelegate) throws Exception
|
||||||
|
{
|
||||||
|
this.info = clientInfo;
|
||||||
|
this.httpDelegate = httpDelegate;
|
||||||
|
|
||||||
|
setKey (info.getSecretKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
long toVersionFromString (String s) throws Exception
|
||||||
|
{
|
||||||
|
HashSha256 hash = new HashSha256();
|
||||||
|
byte[] result = hash.hash(Strings.toBytes(s));
|
||||||
|
|
||||||
|
long l =
|
||||||
|
((long)result[0]) |
|
||||||
|
((long)result[1] << 8) |
|
||||||
|
((long)result[2] << 16) |
|
||||||
|
((long)result[3] << 24);
|
||||||
|
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void listDirectoryFinished (List<FileInfo> files, Callback callback, String path, Object... arguments)
|
||||||
|
{
|
||||||
|
log.debug("listDirectoryFinished");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (arguments[0] instanceof Exception)
|
||||||
|
throw (Exception)arguments[0];
|
||||||
|
|
||||||
|
String result = (String)arguments[0];
|
||||||
|
log.trace(result);
|
||||||
|
|
||||||
|
DateFormat dateTimeFormat = new DateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z' Z");
|
||||||
|
|
||||||
|
Object doc = XML.parse(result);
|
||||||
|
Object[] nodes = XML.getElementsByTagName(doc, "Contents");
|
||||||
|
for (Object currentNode : nodes)
|
||||||
|
{
|
||||||
|
if ( XML.getNodeType(currentNode) == XML.ELEMENT_NODE )
|
||||||
|
{
|
||||||
|
Object keyNode = XML.getElementsByTagName(currentNode, "Key")[0];
|
||||||
|
Object etagNode = XML.getElementsByTagName(currentNode, "ETag")[0];
|
||||||
|
Object sizeNode = XML.getElementsByTagName(currentNode, "Size")[0];
|
||||||
|
Object lastModifiedNode = XML.getElementsByTagName(currentNode, "LastModified")[0];
|
||||||
|
|
||||||
|
log.trace(XML.textOf(keyNode), XML.textOf(sizeNode), XML.textOf(etagNode), XML.textOf(lastModifiedNode));
|
||||||
|
|
||||||
|
String fullPath = XML.textOf(keyNode);
|
||||||
|
String relativePath = fullPath.substring(path.length());
|
||||||
|
|
||||||
|
FileInfo fi = new FileInfo(
|
||||||
|
fullPath,
|
||||||
|
relativePath,
|
||||||
|
Long.parseLong(XML.textOf(sizeNode)),
|
||||||
|
dateTimeFormat.parse(XML.textOf(lastModifiedNode) + " GMT"),
|
||||||
|
XML.textOf(etagNode)
|
||||||
|
);
|
||||||
|
|
||||||
|
files.add(fi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (XML.textOf(XML.getElementsByTagName(doc, "IsTruncated")[0]).equals("true"))
|
||||||
|
{
|
||||||
|
log.debug("results were truncated, requesting more...");
|
||||||
|
listIterative(files, path, callback);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log.debug("results were complete, invoking callback");
|
||||||
|
|
||||||
|
Collections.sort(files, new FileInfo.SortByDateAscending());
|
||||||
|
for (FileInfo i : files)
|
||||||
|
log.trace ("path: ", i.path, " date:", i.date);
|
||||||
|
|
||||||
|
callback.invoke(files);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Throwable e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
|
||||||
|
callback.invoke(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void list(String path, Callback callback)
|
||||||
|
{
|
||||||
|
listIterative(new ArrayList<FileInfo>(), path, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void listIterative(List<FileInfo> files, String path, Callback callback)
|
||||||
|
{
|
||||||
|
log.debug("listDirectory",path);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String url =
|
||||||
|
createUrlPrefix() +
|
||||||
|
"?prefix=" + path + "&max-keys=1000" +
|
||||||
|
(!files.isEmpty() ? ("&marker=" + files.get(files.size()-1).path) : "") +
|
||||||
|
"&" + createRandomPostfix();
|
||||||
|
|
||||||
|
log.debug(url);
|
||||||
|
|
||||||
|
String[][] headers = makeHeaders (
|
||||||
|
info.getAccessId(), "GET", "", "", 0, new Date(), "/" + info.getBucketName() + "/"
|
||||||
|
);
|
||||||
|
|
||||||
|
httpDelegate.execute (HttpDelegate.GET, url, headers, false, false, null,
|
||||||
|
new CallbackWithVariables(files, callback, path) {
|
||||||
|
@Override
|
||||||
|
public void invoke(Object... arguments)
|
||||||
|
{
|
||||||
|
List<FileInfo> files = V(0);
|
||||||
|
Callback callback = V(1);
|
||||||
|
String path = V(2);
|
||||||
|
listDirectoryFinished(files, callback, path, arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch (Throwable e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
|
||||||
|
callback.invoke(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createDirectory(String path, Callback callback)
|
||||||
|
{
|
||||||
|
log.debug("createDirectory",path);
|
||||||
|
callback.invoke(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void get(String path, Callback callback)
|
||||||
|
{
|
||||||
|
log.debug("get",path);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String url =
|
||||||
|
createUrlPrefix() +
|
||||||
|
path +
|
||||||
|
"?" + createRandomPostfix();
|
||||||
|
|
||||||
|
log.debug(url);
|
||||||
|
|
||||||
|
String[][] headers = makeHeaders (
|
||||||
|
info.getAccessId(), "GET", "", "", 0, new Date(), "/" + info.getBucketName() + "/" + path
|
||||||
|
);
|
||||||
|
|
||||||
|
httpDelegate.execute(HttpDelegate.GET, url, headers, false, true, null, grabVersion_(true).setReturn(callback));
|
||||||
|
}
|
||||||
|
catch (Throwable e)
|
||||||
|
{
|
||||||
|
callback.invoke(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Callback grabVersion_(boolean includeResponseData)
|
||||||
|
{
|
||||||
|
return new CallbackDefault(includeResponseData) {
|
||||||
|
public void onSuccess(Object... arguments) throws Exception {
|
||||||
|
|
||||||
|
boolean includeResponseData = V(0);
|
||||||
|
String[][] headers = (String[][])arguments[1];
|
||||||
|
|
||||||
|
for (String[] pair : headers)
|
||||||
|
{
|
||||||
|
if (pair[0].equals("ETag"))
|
||||||
|
{
|
||||||
|
if (includeResponseData)
|
||||||
|
next(arguments[0], pair[1]);
|
||||||
|
else
|
||||||
|
next(pair[1]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception("No ETag Response header");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void put(String path, byte[] contents, Callback callback)
|
||||||
|
{
|
||||||
|
log.debug("put",path);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String url =
|
||||||
|
createUrlPrefix() +
|
||||||
|
path +
|
||||||
|
"?" + createRandomPostfix();
|
||||||
|
|
||||||
|
log.debug(url);
|
||||||
|
|
||||||
|
String[][] headers = makeHeaders (
|
||||||
|
info.getAccessId(), "PUT", "", "application/octet-stream", contents.length, new Date(), "/" + info.getBucketName() + "/" + path
|
||||||
|
);
|
||||||
|
|
||||||
|
httpDelegate.execute(HttpDelegate.PUT, url, headers, true, false, contents, grabVersion_(false).setReturn(callback));
|
||||||
|
}
|
||||||
|
catch (Throwable e)
|
||||||
|
{
|
||||||
|
callback.invoke(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete(String path, Callback callback)
|
||||||
|
{
|
||||||
|
log.debug("delete",path);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String url =
|
||||||
|
createUrlPrefix() +
|
||||||
|
path +
|
||||||
|
"?" + createRandomPostfix();
|
||||||
|
|
||||||
|
log.debug(url);
|
||||||
|
|
||||||
|
String[][] headers = makeHeaders (
|
||||||
|
info.getAccessId(), "DELETE", "", "", 0, new Date(), "/" + info.getBucketName() + "/" + path
|
||||||
|
);
|
||||||
|
|
||||||
|
httpDelegate.execute(HttpDelegate.DELETE, url, headers, true, false, null, callback);
|
||||||
|
}
|
||||||
|
catch (Throwable e)
|
||||||
|
{
|
||||||
|
callback.invoke(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
179
java/core/src/core/connector/s3/sync/S3Connector.java
Normal file
179
java/core/src/core/connector/s3/sync/S3Connector.java
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
/**
|
||||||
|
* Author: Timothy Prepscius
|
||||||
|
* License: GPLv3 Affero + keep my name in the code!
|
||||||
|
*/
|
||||||
|
|
||||||
|
package core.connector.s3.sync;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.amazonaws.services.s3.AmazonS3;
|
||||||
|
import com.amazonaws.services.s3.AmazonS3Client;
|
||||||
|
import com.amazonaws.services.s3.model.GetObjectRequest;
|
||||||
|
import com.amazonaws.services.s3.model.ListObjectsRequest;
|
||||||
|
import com.amazonaws.services.s3.model.ObjectListing;
|
||||||
|
import com.amazonaws.services.s3.model.S3ObjectSummary;
|
||||||
|
|
||||||
|
import core.connector.ConnectorException;
|
||||||
|
import core.connector.FileInfo;
|
||||||
|
import core.connector.s3.ClientInfoS3;
|
||||||
|
import core.connector.sync.StoreConnector;
|
||||||
|
import core.util.Streams;
|
||||||
|
|
||||||
|
|
||||||
|
public class S3Connector implements StoreConnector
|
||||||
|
{
|
||||||
|
AmazonS3 s3;
|
||||||
|
ClientInfoS3 info;
|
||||||
|
|
||||||
|
public S3Connector (ClientInfoS3 info)
|
||||||
|
{
|
||||||
|
this.info = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void open () throws ConnectorException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
s3 = new AmazonS3Client(new SimpleAWSCredentials(info.getAccessId(), info.getSecretKey()));
|
||||||
|
s3.setEndpoint(info.getBucketEndpoint());
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new ConnectorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close ()
|
||||||
|
{
|
||||||
|
s3 = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<FileInfo> listDirectory(String path) throws ConnectorException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ObjectListing bucketListing =
|
||||||
|
s3.listObjects(
|
||||||
|
new ListObjectsRequest()
|
||||||
|
.withBucketName(info.getBucketName())
|
||||||
|
.withPrefix(path)
|
||||||
|
);
|
||||||
|
|
||||||
|
List<FileInfo> listing = new ArrayList<FileInfo>(bucketListing.getObjectSummaries().size());
|
||||||
|
|
||||||
|
boolean finished = false;
|
||||||
|
while (!finished)
|
||||||
|
{
|
||||||
|
for (S3ObjectSummary s3s : bucketListing.getObjectSummaries())
|
||||||
|
{
|
||||||
|
String key = s3s.getKey();
|
||||||
|
long size = s3s.getSize();
|
||||||
|
Date date = s3s.getLastModified();
|
||||||
|
|
||||||
|
listing.add(new FileInfo(key, key.substring(path.length()+1), size, date, s3s.getETag()));
|
||||||
|
}
|
||||||
|
if (bucketListing.isTruncated())
|
||||||
|
bucketListing = s3.listNextBatchOfObjects(bucketListing);
|
||||||
|
else
|
||||||
|
finished = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return listing;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new ConnectorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createDirectory(String path) throws ConnectorException
|
||||||
|
{
|
||||||
|
// no need in s3
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] get(String path, long size) throws ConnectorException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
GetObjectRequest request = new GetObjectRequest(info.getBucketName(), path);
|
||||||
|
if (size >= 0)
|
||||||
|
request.withRange(0, size);
|
||||||
|
|
||||||
|
return Streams.readFullyBytes(s3.getObject(request).getObjectContent());
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new ConnectorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] get(String path) throws ConnectorException
|
||||||
|
{
|
||||||
|
return get(path, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void put(String path, byte[] contents) throws ConnectorException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
s3.putObject(info.getBucketName(), path, new ByteArrayInputStream(contents), null);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new ConnectorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void move(String from, String to) throws ConnectorException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
s3.copyObject(info.getBucketName(), from, info.getBucketName(), to);
|
||||||
|
s3.deleteObject(info.getBucketName(), from);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new ConnectorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete(String path) throws ConnectorException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
s3.deleteObject(info.getBucketName(), path);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new ConnectorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean ensureDirectories (String ... folders)
|
||||||
|
{
|
||||||
|
for (String folder : folders)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
createDirectory(folder);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
System.out.format("Folder[%s] already exists.\n", folder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* Author: Timothy Prepscius
|
||||||
|
* License: GPLv3 Affero + keep my name in the code!
|
||||||
|
*/
|
||||||
|
|
||||||
|
package core.connector.s3.sync;
|
||||||
|
|
||||||
|
|
||||||
|
import com.amazonaws.auth.AWSCredentials;
|
||||||
|
|
||||||
|
import core.constants.ConstantsS3;
|
||||||
|
import core.util.Environment;
|
||||||
|
|
||||||
|
public class SimpleAWSCredentials implements AWSCredentials
|
||||||
|
{
|
||||||
|
private String a, s;
|
||||||
|
|
||||||
|
public SimpleAWSCredentials(String a, String s)
|
||||||
|
{
|
||||||
|
this.a = a;
|
||||||
|
this.s = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimpleAWSCredentials(Environment e)
|
||||||
|
{
|
||||||
|
this(e.checkGet(ConstantsS3.AWSAccessKeyId), e.checkGet(ConstantsS3.AWSSecretKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAWSAccessKeyId()
|
||||||
|
{
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAWSSecretKey()
|
||||||
|
{
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
102
java/core/src/core/connector/sync/EncryptedStoreConnector.java
Normal file
102
java/core/src/core/connector/sync/EncryptedStoreConnector.java
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
/**
|
||||||
|
* Author: Timothy Prepscius
|
||||||
|
* License: GPLv3 Affero + keep my name in the code!
|
||||||
|
*/
|
||||||
|
|
||||||
|
package core.connector.sync;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import core.connector.ConnectorException;
|
||||||
|
import core.connector.FileInfo;
|
||||||
|
import core.crypt.Cryptor;
|
||||||
|
import core.util.Streams;
|
||||||
|
import core.util.Zip;
|
||||||
|
|
||||||
|
|
||||||
|
public class EncryptedStoreConnector implements StoreConnector
|
||||||
|
{
|
||||||
|
StoreConnector store;
|
||||||
|
Cryptor cryptor;
|
||||||
|
|
||||||
|
public EncryptedStoreConnector (Cryptor cryptor, StoreConnector store)
|
||||||
|
{
|
||||||
|
this.store = store;
|
||||||
|
this.cryptor = cryptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void open() throws ConnectorException
|
||||||
|
{
|
||||||
|
store.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws ConnectorException
|
||||||
|
{
|
||||||
|
store.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<FileInfo> listDirectory(String path) throws ConnectorException
|
||||||
|
{
|
||||||
|
return store.listDirectory(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createDirectory(String path) throws ConnectorException
|
||||||
|
{
|
||||||
|
store.createDirectory(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] get(String path) throws ConnectorException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Zip.inflate(cryptor.decrypt(store.get(path)));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new ConnectorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] get(String path, long size) throws ConnectorException
|
||||||
|
{
|
||||||
|
return get(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void put(String path, byte[] contents) throws ConnectorException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
store.put(path, cryptor.encrypt(Zip.deflate(contents)));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new ConnectorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void move(String from, String to) throws ConnectorException
|
||||||
|
{
|
||||||
|
store.move(from, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete(String path) throws ConnectorException
|
||||||
|
{
|
||||||
|
store.delete(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean ensureDirectories(String... folders)
|
||||||
|
{
|
||||||
|
return store.ensureDirectories(folders);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
29
java/core/src/core/connector/sync/StoreConnector.java
Normal file
29
java/core/src/core/connector/sync/StoreConnector.java
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Author: Timothy Prepscius
|
||||||
|
* License: GPLv3 Affero + keep my name in the code!
|
||||||
|
*/
|
||||||
|
|
||||||
|
package core.connector.sync;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import core.connector.ConnectorException;
|
||||||
|
import core.connector.FileInfo;
|
||||||
|
|
||||||
|
public interface StoreConnector
|
||||||
|
{
|
||||||
|
public void open () throws ConnectorException;
|
||||||
|
public void close () throws ConnectorException;
|
||||||
|
|
||||||
|
List<FileInfo> listDirectory (String path) throws ConnectorException;
|
||||||
|
void createDirectory (String path) throws ConnectorException;
|
||||||
|
|
||||||
|
byte[] get (String path) throws ConnectorException;
|
||||||
|
byte[] get (String path, long size) throws ConnectorException;
|
||||||
|
|
||||||
|
void put (String path, byte[] contents) throws ConnectorException;
|
||||||
|
void move (String from, String to) throws ConnectorException;
|
||||||
|
void delete (String path) throws ConnectorException;
|
||||||
|
|
||||||
|
public boolean ensureDirectories (String ... folders);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user