2017-08-11 13:47:54 +02:00
|
|
|
package de.vanitasvitae.sync_client;
|
|
|
|
|
|
|
|
import static java.nio.file.LinkOption.NOFOLLOW_LINKS;
|
|
|
|
import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
|
|
|
|
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
|
|
|
|
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
|
|
|
|
import static java.nio.file.StandardWatchEventKinds.OVERFLOW;
|
|
|
|
|
|
|
|
import java.io.File;
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.nio.file.FileSystems;
|
|
|
|
import java.nio.file.FileVisitResult;
|
|
|
|
import java.nio.file.Files;
|
|
|
|
import java.nio.file.Path;
|
|
|
|
import java.nio.file.SimpleFileVisitor;
|
|
|
|
import java.nio.file.WatchEvent;
|
|
|
|
import java.nio.file.WatchKey;
|
|
|
|
import java.nio.file.WatchService;
|
|
|
|
import java.nio.file.attribute.BasicFileAttributes;
|
2017-08-15 17:44:02 +02:00
|
|
|
import java.security.InvalidAlgorithmParameterException;
|
|
|
|
import java.security.InvalidKeyException;
|
|
|
|
import java.security.NoSuchAlgorithmException;
|
|
|
|
import java.security.NoSuchProviderException;
|
2017-08-11 13:47:54 +02:00
|
|
|
import java.util.HashMap;
|
|
|
|
|
2017-08-15 17:44:02 +02:00
|
|
|
import javax.crypto.BadPaddingException;
|
|
|
|
import javax.crypto.IllegalBlockSizeException;
|
|
|
|
import javax.crypto.NoSuchPaddingException;
|
|
|
|
|
2017-08-11 13:47:54 +02:00
|
|
|
import org.jivesoftware.smack.SmackException;
|
|
|
|
import org.jivesoftware.smack.XMPPException;
|
|
|
|
import org.jivesoftware.smack.util.Async;
|
2017-08-15 17:44:02 +02:00
|
|
|
import org.jivesoftware.smackx.jet.JetManager;
|
|
|
|
import org.jivesoftware.smackx.jingle_filetransfer.JingleFileTransferManager;
|
|
|
|
import org.jivesoftware.smackx.omemo.OmemoManager;
|
|
|
|
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
2017-08-11 13:47:54 +02:00
|
|
|
|
|
|
|
import org.jxmpp.jid.FullJid;
|
|
|
|
|
|
|
|
public class Master extends Client {
|
|
|
|
|
|
|
|
private final WatchService fileWatcher;
|
|
|
|
private final HashMap<WatchKey, Path> fileKeys;
|
|
|
|
private boolean trace;
|
2017-08-15 17:44:02 +02:00
|
|
|
private final JingleFileTransferManager jftm;
|
|
|
|
private final JetManager jetm;
|
2017-08-11 13:47:54 +02:00
|
|
|
|
2017-08-15 17:44:02 +02:00
|
|
|
public Master(String username, String password, String directory) throws IOException, InterruptedException,
|
|
|
|
SmackException, XMPPException, CorruptedOmemoKeyException, InvalidAlgorithmParameterException,
|
|
|
|
NoSuchAlgorithmException, NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException,
|
|
|
|
NoSuchProviderException, InvalidKeyException {
|
2017-08-11 13:47:54 +02:00
|
|
|
super(username, password, directory);
|
|
|
|
fileWatcher = FileSystems.getDefault().newWatchService();
|
|
|
|
fileKeys = new HashMap<>();
|
|
|
|
registerFileWatcher();
|
|
|
|
jftm = JingleFileTransferManager.getInstanceFor(connection);
|
2017-08-15 17:44:02 +02:00
|
|
|
jetm = JetManager.getInstanceFor(connection);
|
2017-08-11 13:47:54 +02:00
|
|
|
jftm.addIncomingFileOfferListener(offer -> {
|
|
|
|
try {
|
|
|
|
offer.accept(connection, new File(root.toFile(), offer.getFile().getName()));
|
2017-08-15 17:44:02 +02:00
|
|
|
} catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NoResponseException | SmackException.NotConnectedException | IOException e) {
|
2017-08-11 13:47:54 +02:00
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
private void registerFileWatcher() throws IOException {
|
|
|
|
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
|
|
|
|
@Override
|
|
|
|
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
|
|
|
|
throws IOException
|
|
|
|
{
|
|
|
|
WatchKey key = dir.register(fileWatcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
|
|
|
|
if (trace) {
|
|
|
|
Path prev = fileKeys.get(key);
|
|
|
|
if (prev == null) {
|
|
|
|
System.out.format("register: %s\n", dir);
|
|
|
|
} else {
|
|
|
|
if (!dir.equals(prev)) {
|
|
|
|
System.out.format("update: %s -> %s\n", prev, dir);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fileKeys.put(key, dir);
|
|
|
|
return FileVisitResult.CONTINUE;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
trace = true;
|
|
|
|
|
|
|
|
Async.go(() -> {
|
|
|
|
try {
|
|
|
|
processEvents();
|
|
|
|
} catch (InterruptedException | SmackException.NoResponseException | SmackException.NotConnectedException | XMPPException.XMPPErrorException | SmackException.FeatureNotSupportedException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void processEvents() throws InterruptedException, SmackException.FeatureNotSupportedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException {
|
|
|
|
for (;;) {
|
|
|
|
|
|
|
|
// wait for key to be signalled
|
|
|
|
WatchKey key;
|
|
|
|
try {
|
|
|
|
key = fileWatcher.take();
|
|
|
|
} catch (InterruptedException x) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Path dir = fileKeys.get(key);
|
|
|
|
if (dir == null) {
|
|
|
|
System.err.println("WatchKey not recognized!!");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (WatchEvent<?> event: key.pollEvents()) {
|
|
|
|
WatchEvent.Kind kind = event.kind();
|
|
|
|
|
|
|
|
// TBD - provide example of how OVERFLOW event is handled
|
|
|
|
if (kind == OVERFLOW) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Context for directory entry event is the file name of entry
|
|
|
|
WatchEvent<Path> ev = (WatchEvent<Path>) event;
|
|
|
|
Path name = ev.context();
|
|
|
|
Path child = dir.resolve(name);
|
|
|
|
|
|
|
|
// print out event
|
|
|
|
System.out.format("%s: %s\n", event.kind().name(), child);
|
|
|
|
|
|
|
|
// if directory is created, and watching recursively, then
|
|
|
|
// register it and its sub-directories
|
|
|
|
if (kind == ENTRY_CREATE) {
|
|
|
|
try {
|
|
|
|
if (Files.isDirectory(child, NOFOLLOW_LINKS)) {
|
|
|
|
Files.walkFileTree(child, new SimpleFileVisitor<Path>() {
|
|
|
|
@Override
|
|
|
|
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
|
|
|
|
throws IOException
|
|
|
|
{
|
|
|
|
WatchKey key = dir.register(fileWatcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
|
|
|
|
if (trace) {
|
|
|
|
File[] files = dir.toFile().listFiles();
|
|
|
|
for (File f : files) {
|
|
|
|
if (f.isFile()) {
|
|
|
|
try {
|
|
|
|
sendFile(f);
|
|
|
|
} catch (InterruptedException | SmackException.FeatureNotSupportedException | SmackException.NoResponseException | SmackException.NotConnectedException | XMPPException.XMPPErrorException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fileKeys.put(key, dir);
|
|
|
|
return FileVisitResult.CONTINUE;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} else if (child.toFile().isFile()) {
|
|
|
|
sendFile(child.toFile());
|
|
|
|
}
|
|
|
|
} catch (IOException x) {
|
|
|
|
}
|
|
|
|
} else if (kind == ENTRY_MODIFY && child.toFile().isFile()) {
|
|
|
|
sendFile(child.toFile());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// reset key and remove from set if directory no longer accessible
|
|
|
|
boolean valid = key.reset();
|
|
|
|
if (!valid) {
|
|
|
|
fileKeys.remove(key);
|
|
|
|
|
|
|
|
// all directories are inaccessible
|
|
|
|
if (fileKeys.isEmpty()) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void sendFile(File file) throws InterruptedException, SmackException.FeatureNotSupportedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException {
|
|
|
|
String fileName = file.getAbsolutePath();
|
|
|
|
String rootPath = root.toAbsolutePath().toString();
|
|
|
|
if (fileName.startsWith(rootPath)) {
|
|
|
|
fileName = fileName.substring(rootPath.length());
|
|
|
|
} else {
|
|
|
|
throw new AssertionError("Illegal path! " + fileName);
|
|
|
|
}
|
|
|
|
for (FullJid recipient : remotes) {
|
2017-08-15 17:44:02 +02:00
|
|
|
try {
|
|
|
|
jetm.sendEncryptedFile(file, fileName, recipient, OmemoManager.getInstanceFor(connection));
|
|
|
|
} catch (Exception e) {
|
|
|
|
System.out.println("Could not send file: " + e);
|
|
|
|
}
|
|
|
|
//jftm.sendFile(file, fileName, recipient);
|
2017-08-11 13:47:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|