Removable drive finders for Windows, Mac and Linux (untested).

This commit is contained in:
akwizgran
2011-10-04 21:04:22 +01:00
parent 9c48ad032f
commit f02e6b3f89
10 changed files with 378 additions and 0 deletions

View File

@@ -0,0 +1,121 @@
package net.sf.briar.plugins.file;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Map;
import org.apache.commons.io.FileSystemUtils;
import net.sf.briar.api.ContactId;
import net.sf.briar.api.TransportId;
import net.sf.briar.api.transport.InvalidConfigException;
import net.sf.briar.api.transport.InvalidTransportException;
import net.sf.briar.api.transport.TransportConstants;
import net.sf.briar.api.transport.batch.BatchTransportCallback;
import net.sf.briar.api.transport.batch.BatchTransportPlugin;
import net.sf.briar.api.transport.batch.BatchTransportReader;
import net.sf.briar.api.transport.batch.BatchTransportWriter;
abstract class FilePlugin implements BatchTransportPlugin {
public static final int TRANSPORT_ID = 0;
private static final TransportId id = new TransportId(TRANSPORT_ID);
private boolean started = false;
protected Map<String, String> localProperties = null;
protected Map<ContactId, Map<String, String>> remoteProperties = null;
protected Map<String, String> config = null;
protected BatchTransportCallback callback = null;
protected abstract File chooseOutputDirectory();
protected abstract void writerFinished(File f);
public TransportId getId() {
return id;
}
public synchronized void start(Map<String, String> localProperties,
Map<ContactId, Map<String, String>> remoteProperties,
Map<String, String> config, BatchTransportCallback callback)
throws InvalidTransportException, InvalidConfigException {
if(started) throw new IllegalStateException();
started = true;
this.localProperties = localProperties;
this.remoteProperties = remoteProperties;
this.config = config;
this.callback = callback;
}
public synchronized void stop() {
if(!started) throw new IllegalStateException();
started = false;
}
public synchronized void setLocalProperties(Map<String, String> properties)
throws InvalidTransportException {
if(!started) throw new IllegalStateException();
localProperties = properties;
}
public synchronized void setRemoteProperties(ContactId c,
Map<String, String> properties)
throws InvalidTransportException {
if(!started) throw new IllegalStateException();
remoteProperties.put(c, properties);
}
public synchronized void setConfig(Map<String, String> config)
throws InvalidConfigException {
if(!started) throw new IllegalStateException();
this.config = config;
}
public boolean shouldPoll() {
return false;
}
public int getPollingInterval() {
return 0;
}
public void poll() {
throw new UnsupportedOperationException();
}
public BatchTransportReader createReader(ContactId c) {
return null;
}
public BatchTransportWriter createWriter(ContactId c) {
if(!started) throw new IllegalStateException();
File dir = chooseOutputDirectory();
if(dir == null) return null;
if(!dir.exists()) return null;
if(!dir.isDirectory()) return null;
File f = new File(dir, createFilename());
try {
long capacity = getCapacity(f.getAbsolutePath());
if(capacity < TransportConstants.MIN_CONNECTION_LENGTH) return null;
OutputStream out = new FileOutputStream(f);
return new FileTransportWriter(f, out, capacity, this);
} catch(IOException e) {
f.delete();
return null;
}
}
protected String createFilename() {
StringBuilder s = new StringBuilder(12);
for(int i = 0; i < 8; i++) s.append((char) ('a' + Math.random() * 26));
s.append(".dat");
System.out.println(s);
return s.toString();
}
protected long getCapacity(String path) throws IOException {
return FileSystemUtils.freeSpaceKb(path) * 1024L;
}
}

View File

@@ -0,0 +1,44 @@
package net.sf.briar.plugins.file;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import net.sf.briar.api.transport.batch.BatchTransportWriter;
class FileTransportWriter implements BatchTransportWriter {
private final File file;
private final OutputStream out;
private final long capacity;
private final FilePlugin plugin;
private boolean streamInUse = false;
FileTransportWriter(File file, OutputStream out, long capacity,
FilePlugin plugin) {
this.file = file;
this.out = out;
this.capacity = capacity;
this.plugin = plugin;
}
public long getCapacity() {
return capacity;
}
public OutputStream getOutputStream() {
streamInUse = true;
return out;
}
public void finish() {
streamInUse = false;
plugin.writerFinished(file);
}
public void dispose() throws IOException {
if(streamInUse) out.close();
file.delete();
}
}

View File

@@ -0,0 +1,21 @@
package net.sf.briar.plugins.file;
class LinuxRemovableDriveFinder extends UnixRemovableDriveFinder {
@Override
protected String getMountCommand() {
return "/bin/mount";
}
@Override
protected String parseMountPoint(String line) {
// The format is "/dev/foo on /bar/baz type bam (opt1,opt2)"
line = line.replaceFirst("^/dev/[^ ]+ on ", "");
return line.replaceFirst(" type [^ ]+ \\([^)]+\\)$", "");
}
@Override
protected boolean isRemovableDriveMountPoint(String path) {
return path.startsWith("/mnt/") || path.startsWith("/media/");
}
}

View File

@@ -0,0 +1,21 @@
package net.sf.briar.plugins.file;
class MacRemovableDriveFinder extends UnixRemovableDriveFinder {
@Override
protected String getMountCommand() {
return "/sbin/mount";
}
@Override
protected String parseMountPoint(String line) {
// The format is "/dev/foo on /bar/baz (opt1, opt2)"
line = line.replaceFirst("^/dev/[^ ]+ on ", "");
return line.replaceFirst(" \\([^)]+\\)$", "");
}
@Override
protected boolean isRemovableDriveMountPoint(String path) {
return path.startsWith("/Volumes/");
}
}

View File

@@ -0,0 +1,10 @@
package net.sf.briar.plugins.file;
import java.io.File;
import java.io.IOException;
import java.util.List;
interface RemovableDriveFinder {
List<File> findRemovableDrives() throws IOException;
}

View File

@@ -0,0 +1,25 @@
package net.sf.briar.plugins.file;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import net.sf.briar.util.OsUtils;
class RemovableDriveFinderImpl implements RemovableDriveFinder {
private final LinuxRemovableDriveFinder linux =
new LinuxRemovableDriveFinder();
private final MacRemovableDriveFinder mac =
new MacRemovableDriveFinder();
private final WindowsRemovableDriveFinder windows =
new WindowsRemovableDriveFinder();
public List<File> findRemovableDrives() throws IOException {
if(OsUtils.isLinux()) return linux.findRemovableDrives();
else if(OsUtils.isMac()) return mac.findRemovableDrives();
else if(OsUtils.isWindows()) return windows.findRemovableDrives();
else return Collections.emptyList();
}
}

View File

@@ -0,0 +1,36 @@
package net.sf.briar.plugins.file;
import java.io.File;
import java.io.IOException;
import java.util.List;
class RemovableDrivePlugin extends FilePlugin {
private final RemovableDriveFinder finder;
RemovableDrivePlugin(RemovableDriveFinder finder) {
this.finder = finder;
}
@Override
protected File chooseOutputDirectory() {
try {
List<File> drives = finder.findRemovableDrives();
if(drives.isEmpty()) return null;
String[] paths = new String[drives.size()];
for(int i = 0; i < paths.length; i++) {
paths[i] = drives.get(i).getAbsolutePath();
}
int i = callback.showChoice("REMOVABLE_DRIVE_CHOOSE_DRIVE", paths);
if(i == -1) return null;
return drives.get(i);
} catch(IOException e) {
return null;
}
}
@Override
protected void writerFinished(File f) {
callback.showMessage("REMOVABLE_DRIVE_WRITE_FINISHED");
}
}

View File

@@ -0,0 +1,41 @@
package net.sf.briar.plugins.file;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
abstract class UnixRemovableDriveFinder implements RemovableDriveFinder {
protected abstract String getMountCommand();
protected abstract String parseMountPoint(String line);
protected abstract boolean isRemovableDriveMountPoint(String path);
public List<File> findRemovableDrives() throws IOException {
List<File> drives = new ArrayList<File>();
Process p = new ProcessBuilder(getMountCommand()).start();
Scanner s = new Scanner(p.getInputStream(), "UTF-8");
try {
while(s.hasNextLine()) {
String line = s.nextLine();
String[] tokens = line.split(" ");
if(tokens.length < 3) continue;
// The general format is "/dev/foo on /bar/baz ..."
if(tokens[0].startsWith("/dev/") && tokens[1].equals("on")) {
// The path may contain spaces so we can't use tokens[2]
String path = parseMountPoint(line);
if(isRemovableDriveMountPoint(path)) {
File f = new File(path);
if(f.exists() && f.isDirectory()) drives.add(f);
}
}
}
} finally {
s.close();
}
return drives;
}
}

View File

@@ -0,0 +1,30 @@
package net.sf.briar.plugins.file;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import com.sun.jna.platform.win32.Kernel32;
class WindowsRemovableDriveFinder implements RemovableDriveFinder {
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa364939.aspx
private static final int DRIVE_REMOVABLE = 2;
public List<File> findRemovableDrives() throws IOException {
File[] roots = File.listRoots();
if(roots == null) throw new IOException();
List<File> drives = new ArrayList<File>();
for(File root : roots) {
try {
int type = Kernel32.INSTANCE.GetDriveType(root.getPath());
if(type == DRIVE_REMOVABLE) drives.add(root);
} catch(RuntimeException e) {
throw new IOException(e.getMessage());
}
}
return drives;
}
}

View File

@@ -0,0 +1,29 @@
package net.sf.briar.plugins.file;
import java.io.File;
public class TestFilePlugin extends FilePlugin {
private final File outputDir;
private final long capacity;
public TestFilePlugin(File outputDir, long capacity) {
this.outputDir = outputDir;
this.capacity = capacity;
}
@Override
protected File chooseOutputDirectory() {
return outputDir;
}
@Override
protected void writerFinished(File f) {
// Nothing to do
}
@Override
protected long getCapacity(String path) {
return capacity;
}
}