String protocol  ConnectionOptions options
src/main/java/com/xebialabs/overthere/Overthere.java
final Class<? extends OverthereConnectionBuilder> connectionBuilder = protocols.get().get(protocol);
			final Constructor<? extends OverthereConnectionBuilder> constructor = connectionBuilder.getConstructor(String.class, ConnectionOptions.class);
			if (logger.isDebugEnabled()) {
				logger.debug("Opening connection with protocol " + protocol);
OverthereConnection getConnection(String protocol, ConnectionOptions options)
OverthereConnection connection
src/main/java/com/xebialabs/overthere/OverthereFile.java
* The connection through which this file is accessible. If the connection is closed, this file may no longer be accessible.
	public final OverthereConnection getConnection() {
	 * The full path of the file on the remote system.
	 * @return the path of the file.
	public abstract String getPath();
	/**
	 * The name of the file on the remote system.
	 * 
	 * @return the name of the file.
	 */
	public abstract String getName();
	/**
	 * The parent file of 
	 * @return
	 */
	public abstract OverthereFile getParentFile();
	public OverthereFile getFile(String child) {
		return getConnection().getFile(this, child);
	public abstract long length();
	public abstract long lastModified();
	public abstract boolean exists();
	public abstract boolean isDirectory();
	public abstract boolean isHidden();

	public abstract boolean canRead();

	public abstract boolean canWrite();

	public abstract boolean canExecute();
	public abstract InputStream getInputStream();

	public abstract OutputStream getOutputStream(long length);

	public abstract void delete();

	public void deleteRecursively() throws RuntimeIOException {
			for (OverthereFile each : listFiles()) {
				each.deleteRecursively();
		delete();
	public abstract List<OverthereFile> listFiles();
	public abstract void mkdir();
	public abstract void mkdirs();
	public abstract void renameTo(OverthereFile dest);

	public final void copyTo(final OverthereFile dest) {
		dest.copyFrom(this);
protected OverthereFile(OverthereConnection connection)
OverthereConnection connection  SmbFile smbFile
src/main/java/com/xebialabs/overthere/cifs/CifsOverthereFile.java
super(connection);
	protected SmbFile getSmbFile() {
	public OverthereFile getParentFile() {
		try {
			return new CifsOverthereFile(getConnection(), new SmbFile(smbFile.getParent()));
protected CifsOverthereFile(OverthereConnection connection, SmbFile smbFile)
long length
src/main/java/com/xebialabs/overthere/cifs/CifsOverthereFile.java
throw new RuntimeIOException("Cannot open " + this + " for writing: " + exc.toString(), exc);
	@Override
	public boolean equals(Object that) {
		if (!(that instanceof CifsOverthereFile)) {
			return false;
OutputStream getOutputStream(long length)
src/main/java/com/xebialabs/overthere/cifs/CifsOverthereFile.java
return smbFile.getPath().hashCode();
int hashCode()
OverthereConnection connection  File file
src/main/java/com/xebialabs/overthere/local/LocalOverthereFile.java
super(connection);
		this.file = file;
public LocalOverthereFile(OverthereConnection connection, File file)
src/main/java/com/xebialabs/overthere/local/LocalOverthereFile.java
return file;
File getFile()
src/main/java/com/xebialabs/overthere/local/LocalOverthereFile.java
return file.getPath();
String getPath()
src/main/java/com/xebialabs/overthere/local/LocalOverthereFile.java
return file.getName();
String getName()
src/main/java/com/xebialabs/overthere/local/LocalOverthereFile.java
return getConnection().getFile(file.getParent());
OverthereFile getParentFile()
src/main/java/com/xebialabs/overthere/local/LocalOverthereFile.java
return file.lastModified();
long lastModified()
src/main/java/com/xebialabs/overthere/local/LocalOverthereFile.java
return file.length();
long length()
src/main/java/com/xebialabs/overthere/local/LocalOverthereFile.java
return file.exists();
boolean exists()
src/main/java/com/xebialabs/overthere/local/LocalOverthereFile.java
return file.isDirectory();
boolean isDirectory()
src/main/java/com/xebialabs/overthere/local/LocalOverthereFile.java
return file.isHidden();
boolean isHidden()
src/main/java/com/xebialabs/overthere/local/LocalOverthereFile.java
return file.canRead();
boolean canRead()
src/main/java/com/xebialabs/overthere/local/LocalOverthereFile.java
return file.canWrite();
boolean canWrite()
src/main/java/com/xebialabs/overthere/local/LocalOverthereFile.java
return file.canExecute();
boolean canExecute()
src/main/java/com/xebialabs/overthere/local/LocalOverthereFile.java
if (!file.delete()) {
			throw new RuntimeIOException("Cannot delete " + this);
void delete()
src/main/java/com/xebialabs/overthere/local/LocalOverthereFile.java
if (!file.mkdir()) {
			throw new RuntimeIOException("Cannot mkdir " + this);
void mkdir()
src/main/java/com/xebialabs/overthere/local/LocalOverthereFile.java
if (!file.mkdirs()) {
			throw new RuntimeIOException("Cannot mkdir " + this);
void mkdirs()
OverthereFile dest
src/main/java/com/xebialabs/overthere/local/LocalOverthereFile.java
if (!(dest instanceof LocalOverthereFile)) {
			throw new RuntimeIOException("Destination is not a " + LocalOverthereFile.class.getName());
void renameTo(OverthereFile dest)
src/main/java/com/xebialabs/overthere/local/LocalOverthereFile.java
return new FileInputStream(file);
	public OutputStream getOutputStream(long length) {
			return new FileOutputStream(file);
	@Override
	public boolean equals(Object that) {
		if (!(that instanceof LocalOverthereFile))
			return false;

		return this.file.equals(((LocalOverthereFile) that).file);
InputStream getInputStream()
src/main/java/com/xebialabs/overthere/local/LocalOverthereFile.java
return file.hashCode();
int hashCode()
src/main/java/com/xebialabs/overthere/local/LocalOverthereFile.java
return file.toString();
String toString()
src/main/java/com/xebialabs/overthere/ssh/SshOverthereFile.java
return path;
String getPath()
src/main/java/com/xebialabs/overthere/ssh/SshOverthereFile.java
String fileSep = connection.getHostOperatingSystem().getFileSeparator();
		int lastFileSepPos = path.lastIndexOf(fileSep);
		if (lastFileSepPos < 0) {
			return path;
String getName()
src/main/java/com/xebialabs/overthere/ssh/SshOverthereFile.java
String fileSep = connection.getHostOperatingSystem().getFileSeparator();
		int lastFileSepPos = path.lastIndexOf(fileSep);
		if (lastFileSepPos < 0 || path.equals(fileSep)) {
			return null;
OverthereFile getParentFile()
lastFileSepPos == 0
src/main/java/com/xebialabs/overthere/ssh/SshOverthereFile.java
// the parent of something in the root directory is the root
			// directory itself.
			return connection.getFile(fileSep);
			return connection.getFile(path.substring(0, lastFileSepPos));
else if (lastFileSepPos == 0)
OverthereProcessOutputHandler handler  CmdLine commandLine
src/main/java/com/xebialabs/overthere/ssh/SshOverthereFile.java
return ((SshOverthereConnection) connection).execute(handler, commandLine);
int executeCommand(OverthereProcessOutputHandler handler, CmdLine commandLine)
Object obj
src/main/java/com/xebialabs/overthere/ssh/SshOverthereFile.java
if (!(obj instanceof SshOverthereFile)) {
			return false;
boolean equals(Object obj)
src/main/java/com/xebialabs/overthere/ssh/SshOverthereFile.java
return path.hashCode();
int hashCode()
SshOverthereConnection connection  String remotePath
src/main/java/com/xebialabs/overthere/ssh/SshScpOverthereFile.java
@Override
	@Override
		// FIXME: Implement by parsing the date output of `ls -l`
	@Override
	@Override
	@Override
	@Override
	@Override
	public List<OverthereFile> listFiles() {
		if (logger.isDebugEnabled()) {
			logger.debug("Listing directory " + this);
public SshScpOverthereFile(SshOverthereConnection connection, String remotePath)
src/main/java/com/xebialabs/overthere/ssh/SshScpOverthereFile.java
mkdir(new String[0]);
	public void mkdirs() {
		mkdir("-p");
	protected void mkdir(String... mkdirOptions) throws RuntimeIOException {
		if (logger.isDebugEnabled()) {
			logger.debug("Creating directory " + this + " (with options:" + Joiner.on(' ').join(mkdirOptions));
void mkdir()
OverthereFile destFile
src/main/java/com/xebialabs/overthere/ssh/SshScpOverthereFile.java
if (logger.isDebugEnabled()) {
					logger.debug("Renaming " + this + " to " + destFile);
void renameTo(OverthereFile destFile)
SshSudoOverthereFile destFile  long length  OverthereFile tempFile
src/main/java/com/xebialabs/overthere/ssh/SshSudoOutputStream.java
this.destFile = destFile;
		tempFileOutputStream = tempFile.getOutputStream(length);
		if (logger.isDebugEnabled()) {
			logger.debug("Copying " + tempFile + " to " + destFile + " after writing");
public SshSudoOutputStream(SshSudoOverthereFile destFile, long length, OverthereFile tempFile)
SshSudoOverthereConnection connection  String remotePath  boolean isTempFile
src/main/java/com/xebialabs/overthere/ssh/SshSudoOverthereFile.java
super(connection, remotePath);
	protected int executeCommand(OverthereProcessOutputHandler handler, CmdLine commandLine) {
			return ((SshSudoOverthereConnection) connection).noSudoExecute(handler, commandLine);
			return super.executeCommand(handler, commandLine);
	public InputStream getInputStream() throws RuntimeIOException {
			return super.getInputStream();
			OverthereFile tempFile = connection.getTempFile(getName());
			return tempFile.getInputStream();
		if (logger.isDebugEnabled()) {
public SshSudoOverthereFile(SshSudoOverthereConnection connection, String remotePath, boolean isTempFile)
String... cmdarray
src/main/java/com/xebialabs/overthere/OperatingSystemFamily.java
return encodeCommandLine(false, cmdarray);
String encodeCommandLineForExecution(String... cmdarray)
String... cmdarray
src/main/java/com/xebialabs/overthere/OperatingSystemFamily.java
return encodeCommandLine(true, cmdarray);
String encodeCommandLineForLogging(String... cmdarray)
boolean hidePassword  String... cmdarray
src/main/java/com/xebialabs/overthere/OperatingSystemFamily.java
if (cmdarray == null)
			throw new NullPointerException("Command line is null");
		if (cmdarray.length == 0)
			throw new IllegalArgumentException("Command line has length zero");

		StringBuilder sb = new StringBuilder();
		boolean passwordKeywordSeen = false;
		for (int i = 0; i < cmdarray.length; i++) {
			if (i != 0) {
				sb.append(' ');
String encodeCommandLine(boolean hidePassword, String... cmdarray)
String argument  StringBuilder collector
src/main/java/com/xebialabs/overthere/OperatingSystemFamily.java
collector.append("********");
void encodePasswordArgument(String argument, StringBuilder collector)
String argument  StringBuilder collector
src/main/java/com/xebialabs/overthere/OperatingSystemFamily.java
if (argument.length() == 0) {
			encodeEmptyArgument(collector);
void encodeArgument(String argument, StringBuilder collector)
StringBuilder collector
src/main/java/com/xebialabs/overthere/OperatingSystemFamily.java
collector.append("\"\"");
void encodeEmptyArgument(StringBuilder collector)
String argument  StringBuilder collector
src/main/java/com/xebialabs/overthere/OperatingSystemFamily.java
collector.append(argument);
void encodeRegularArgument(String argument, StringBuilder collector)
String argument  StringBuilder collector
src/main/java/com/xebialabs/overthere/OperatingSystemFamily.java
collector.append("\"");
		for (int j = 0; j < argument.length(); j++) {
			char c = argument.charAt(j);
			if (c == '\"') {
				collector.append(c);
void encodeArgumentWithSpecialCharactersForWindows(String argument, StringBuilder collector)
String argument  StringBuilder collector
src/main/java/com/xebialabs/overthere/OperatingSystemFamily.java
for (int j = 0; j < argument.length(); j++) {
			char c = argument.charAt(j);
			if (SPECIAL_CHARS.indexOf(c) != -1) {
				collector.append('\\');
void encodeArgumentWithSpecialCharactersForNonWindows(String argument, StringBuilder collector)
HostConnection connection  String path
src/main/java/com/xebialabs/overthere/OverthereFile.java
super(path);
	 * Returns the connection this file is associated with.
	 * 
	 * @return the connection
	public HostConnection getConnection() {
	 * Returns a new HostFile with this file as its parent. Identical to invoking <code>this.getConnection().getFile(this, name)</code>
	 * @param name
	 *            the name of the file in this directory
	 * @return a reference to the file in this directory
	public OverthereFile getFile(String name) {
		return connection.getFile(this, name);
protected OverthereFile(HostConnection connection, String path)
src/main/java/com/xebialabs/overthere/OverthereFile.java
String fileSep = connection.getHostOperatingSystem().getFileSeparator();
		int lastFileSepPos = getPath().lastIndexOf(fileSep);
		if (lastFileSepPos < 0) {
			return getPath();
String getName()
src/main/java/com/xebialabs/overthere/OverthereFile.java
String fileSep = connection.getHostOperatingSystem().getFileSeparator();
		int lastFileSepPos = getPath().lastIndexOf(fileSep);
		if (lastFileSepPos < 0 || getPath().equals(fileSep)) {
			return null;
String getParent()
lastFileSepPos == 0
src/main/java/com/xebialabs/overthere/OverthereFile.java
// the parent of something in the root directory is the root
			// directory itself.
			return fileSep;
else if (lastFileSepPos == 0)
src/main/java/com/xebialabs/overthere/OverthereFile.java
String parent = getParent();
		if (parent == null || parent.length() == 0) {
			return null;
OverthereFile getParentFile()
src/main/java/com/xebialabs/overthere/OverthereFile.java
if (!exists()) {
			return false;
boolean deleteRecursively()
OutputStream out
src/main/java/com/xebialabs/overthere/OverthereFile.java
try {
			InputStream in = get();
			try {
				copy(in, out);
void get(OutputStream out)
File dest
src/main/java/com/xebialabs/overthere/OverthereFile.java
try {
			OutputStream out;
			if (dest instanceof OverthereFile) {
				out = ((OverthereFile) dest).put(length());
void get(File dest)
InputStream in  long length
src/main/java/com/xebialabs/overthere/OverthereFile.java
try {
			OutputStream out = put(length);
			try {
				copy(in, out);
void put(InputStream in, long length)
File src
src/main/java/com/xebialabs/overthere/OverthereFile.java
try {
			InputStream in;
			if (src instanceof OverthereFile) {
				in = ((OverthereFile) src).get();
void put(File src)
src/main/java/com/xebialabs/overthere/cifs/CifsOverthereFile.java
public String getParent() {
		return smbFile.getParent();
	public String[] list() throws RuntimeIOException {
			return smbFile.list();
	public boolean mkdir() throws RuntimeIOException {
			return true;
	public boolean mkdirs() throws RuntimeIOException {
			return true;
	public boolean renameTo(File dest) throws RuntimeIOException {
				return true;
			throw new RuntimeIOException("Cannot move/rename SMB file/directory " + this + " to non-SMB file/directory " + dest);
	public boolean delete() throws RuntimeIOException {
			if (smbFile.exists()) {
				if (smbFile.isDirectory()) {
					upgradeToDirectorySmbFile();
					String[] files = smbFile.list();
					if (files.length != 0) {
						throw new RuntimeIOException("Cannot delete non-empty directory " + this);
SmbFile getSmbFile()
SshHostConnection connection  String remotePath
src/main/java/com/xebialabs/overthere/ssh/SshScpOverthereFile.java
public String[] list() throws RuntimeIOException {
		CapturingCommandExecutionCallbackHandler capturedOutput = new CapturingCommandExecutionCallbackHandler();
		// yes, this *is* meant to be 'el es min one'!
		int errno = executeCommand(capturedOutput, "ls", "-1", getPath());
		if (logger.isDebugEnabled())
		List<String> lsLines = capturedOutput.getOutputLines();
		return lsLines.toArray(new String[lsLines.size()]);
	public boolean mkdir() {
		return mkdir(new String[0]);
	public boolean mkdirs() {
		return mkdir(new String[] { "-p"
public SshScpOverthereFile(SshHostConnection connection, String remotePath)
SshSftpHostConnection connection  String path
src/main/java/com/xebialabs/overthere/ssh/SshSftpOverthereFile.java
sshSftpHostConnection = connection;
			SftpATTRS attrs = sshSftpHostConnection.getSharedSftpChannel().stat(convertWindowsPathToWinSshdPath(getPath()));
			sshSftpHostConnection.getSharedSftpChannel().stat(convertWindowsPathToWinSshdPath(getPath()));
	public String[] list() throws RuntimeIOException {
			Vector<LsEntry> ls = (Vector<LsEntry>) sshSftpHostConnection.getSharedSftpChannel().ls(convertWindowsPathToWinSshdPath(getPath()));
			List<String> filenames = new ArrayList<String>(ls.size());
				filenames.add(filename);
			return filenames.toArray(new String[filenames.size()]);
	public boolean mkdir() throws RuntimeIOException {
			sshSftpHostConnection.getSharedSftpChannel().mkdir(compatibleRemotePath);
			return true;
	public boolean mkdirs() throws RuntimeIOException {
				if (!each.mkdir()) {
					return false;
public SshSftpOverthereFile(SshSftpHostConnection connection, String path)
File dest
src/main/java/com/xebialabs/overthere/ssh/SshSftpOverthereFile.java
sshSftpHostConnection.getSharedSftpChannel().rename(getPath(), sftpDest.getPath());
					return true;
			sshSftpHostConnection.getSharedSftpChannel().rm(convertWindowsPathToWinSshdPath(getPath()));
			sshSftpHostConnection.getSharedSftpChannel().rmdir(convertWindowsPathToWinSshdPath(getPath()));
	public InputStream get() throws RuntimeIOException {
			ChannelSftp sftpChannel = sshSftpHostConnection.openSftpChannel();
			InputStream in = new SshSftpInputStream(sshSftpHostConnection, sftpChannel, sftpChannel.get(convertWindowsPathToWinSshdPath(getPath())));
	public void get(OutputStream out) throws RuntimeIOException {
		try {
			sshSftpHostConnection.getSharedSftpChannel().get(convertWindowsPathToWinSshdPath(getPath()), out);
			if (logger.isDebugEnabled())
				logger.debug("Wrote output stream from file " + this);
boolean renameTo(File dest)
SshSudoHostConnection session  String remotePath  boolean isTempFile
src/main/java/com/xebialabs/overthere/ssh/SshSudoOverthereFile.java
super(session, remotePath);
		this.sshSudoHostSession = session;
	protected int executeCommand(CommandExecutionCallbackHandler handler, String... command) {
			return sshSudoHostSession.noSudoExecute(handler, command);
			return super.executeCommand(handler, command);
	public InputStream get() throws RuntimeIOException {
			return super.get();
			OverthereFile tempFile = getTempFile(true);
			return tempFile.get();
		if (logger.isDebugEnabled())
		CapturingCommandExecutionCallbackHandler capturedOutput = new CapturingCommandExecutionCallbackHandler();
		int result = connection.execute(capturedOutput, "cp", this.getPath(), tempFile.getPath());
	public OutputStream put(long length) throws RuntimeIOException {
			// XXX: this should really be returning a "ChmoddingSudoOutputStream"
			return super.put(length);
			/*
			 * XXX: this should really be returning a "ChmoddingSudoOutputStream" wrapped in a "CopyOnCompletionOutputStream"
			 */
			SshSudoOutputStream out = new SshSudoOutputStream(this, length, getTempFile(false));
	protected OverthereFile getTempFile(boolean useSudoForDeletion) {
		String prefix = getBaseName(getPath());
		String suffix = getExtension(getPath());
		return sshSudoHostSession.getTempFile(prefix, suffix);
	public boolean mkdir() throws RuntimeIOException {
			return super.mkdir();
			logger.debug("Making directory world-writable (with sticky bit)");
			return mkdir(new String[] { "-m", "1777"
public SshSudoOverthereFile(SshSudoHostConnection session, String remotePath, boolean isTempFile)