diff --git a/components/net/sf/briar/plugins/file/FilePlugin.java b/components/net/sf/briar/plugins/file/FilePlugin.java new file mode 100644 index 0000000000000000000000000000000000000000..69537cb53d0fd414449ca6147310031324b86c66 --- /dev/null +++ b/components/net/sf/briar/plugins/file/FilePlugin.java @@ -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; + } +} diff --git a/components/net/sf/briar/plugins/file/FileTransportWriter.java b/components/net/sf/briar/plugins/file/FileTransportWriter.java new file mode 100644 index 0000000000000000000000000000000000000000..b5d3532555b6118fedb48ec9d5bb3063614d91d5 --- /dev/null +++ b/components/net/sf/briar/plugins/file/FileTransportWriter.java @@ -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(); + } +} diff --git a/components/net/sf/briar/plugins/file/LinuxRemovableDriveFinder.java b/components/net/sf/briar/plugins/file/LinuxRemovableDriveFinder.java new file mode 100644 index 0000000000000000000000000000000000000000..ccdf0bc0e2d6777573fe5acbfe0cfde42aa771bb --- /dev/null +++ b/components/net/sf/briar/plugins/file/LinuxRemovableDriveFinder.java @@ -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/"); + } +} diff --git a/components/net/sf/briar/plugins/file/MacRemovableDriveFinder.java b/components/net/sf/briar/plugins/file/MacRemovableDriveFinder.java new file mode 100644 index 0000000000000000000000000000000000000000..ec1ae07086c929841664ce439c9cde3dc0fdc8dc --- /dev/null +++ b/components/net/sf/briar/plugins/file/MacRemovableDriveFinder.java @@ -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/"); + } +} diff --git a/components/net/sf/briar/plugins/file/RemovableDriveFinder.java b/components/net/sf/briar/plugins/file/RemovableDriveFinder.java new file mode 100644 index 0000000000000000000000000000000000000000..46157a71c01753e8c084653c0af457bf874160cb --- /dev/null +++ b/components/net/sf/briar/plugins/file/RemovableDriveFinder.java @@ -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; +} diff --git a/components/net/sf/briar/plugins/file/RemovableDriveFinderImpl.java b/components/net/sf/briar/plugins/file/RemovableDriveFinderImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..26060e74fce8020756230b3ae33ad394be367919 --- /dev/null +++ b/components/net/sf/briar/plugins/file/RemovableDriveFinderImpl.java @@ -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(); + } +} diff --git a/components/net/sf/briar/plugins/file/RemovableDrivePlugin.java b/components/net/sf/briar/plugins/file/RemovableDrivePlugin.java new file mode 100644 index 0000000000000000000000000000000000000000..2a890d5b8c2567065ad6a4792cf06a84e2a1afbe --- /dev/null +++ b/components/net/sf/briar/plugins/file/RemovableDrivePlugin.java @@ -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"); + } +} diff --git a/components/net/sf/briar/plugins/file/UnixRemovableDriveFinder.java b/components/net/sf/briar/plugins/file/UnixRemovableDriveFinder.java new file mode 100644 index 0000000000000000000000000000000000000000..17a551bf7b614059b087efff30e659b2dd1b3380 --- /dev/null +++ b/components/net/sf/briar/plugins/file/UnixRemovableDriveFinder.java @@ -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; + } +} diff --git a/components/net/sf/briar/plugins/file/WindowsRemovableDriveFinder.java b/components/net/sf/briar/plugins/file/WindowsRemovableDriveFinder.java new file mode 100644 index 0000000000000000000000000000000000000000..78c2dfab14684c668fd10b954033eaaf4e85d32e --- /dev/null +++ b/components/net/sf/briar/plugins/file/WindowsRemovableDriveFinder.java @@ -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; + } + +} diff --git a/test/net/sf/briar/plugins/file/TestFilePlugin.java b/test/net/sf/briar/plugins/file/TestFilePlugin.java new file mode 100644 index 0000000000000000000000000000000000000000..b3a065a6cbbd30b9d0742512836c657fa630c756 --- /dev/null +++ b/test/net/sf/briar/plugins/file/TestFilePlugin.java @@ -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; + } +}