summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2014-09-19 10:22:59 -0700
committerAlex Crichton <alex@alexcrichton.com>2014-09-19 10:22:59 -0700
commitfb3186e7285977d88babc4ce13bf93a6bc76ccb5 (patch)
tree5ebac5e6612e0c20a0e67c57246e48c4033f8c0c
parent2c33e0bd60b96846298da06c5c70fdc7c029dae5 (diff)
downloadssh2-rs-fb3186e7285977d88babc4ce13bf93a6bc76ccb5.zip
Start SFTP bindings
-rw-r--r--libssh2-sys/lib.rs9
-rw-r--r--src/error.rs53
-rw-r--r--src/lib.rs2
-rw-r--r--src/session.rs19
-rw-r--r--src/sftp.rs42
-rw-r--r--tests/all.rs1
-rw-r--r--tests/sftp.rs5
7 files changed, 129 insertions, 2 deletions
diff --git a/libssh2-sys/lib.rs b/libssh2-sys/lib.rs
index 628b397..8270c72 100644
--- a/libssh2-sys/lib.rs
+++ b/libssh2-sys/lib.rs
@@ -5,7 +5,7 @@ extern crate libc;
#[phase(plugin)]
extern crate "link-config" as link_conifg;
-use libc::{c_int, size_t, c_void, c_char, c_long, c_uchar, c_uint};
+use libc::{c_int, size_t, c_void, c_char, c_long, c_uchar, c_uint, c_ulong};
pub static SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT: c_int = 1;
pub static SSH_DISCONNECT_PROTOCOL_ERROR: c_int = 2;
@@ -117,6 +117,8 @@ pub enum LIBSSH2_AGENT {}
pub enum LIBSSH2_CHANNEL {}
pub enum LIBSSH2_LISTENER {}
pub enum LIBSSH2_KNOWNHOSTS {}
+pub enum LIBSSH2_SFTP {}
+pub enum LIBSSH2_SFTP_HANDLE {}
#[repr(C)]
pub struct libssh2_agent_publickey {
@@ -403,4 +405,9 @@ extern {
size: u64,
mtime: libc::time_t,
atime: libc::time_t) -> *mut LIBSSH2_CHANNEL;
+
+ // sftp
+ pub fn libssh2_sftp_init(sess: *mut LIBSSH2_SESSION) -> *mut LIBSSH2_SFTP;
+ pub fn libssh2_sftp_shutdown(sftp: *mut LIBSSH2_SFTP) -> c_int;
+ pub fn libssh2_sftp_last_error(sftp: *mut LIBSSH2_SFTP) -> c_ulong;
}
diff --git a/src/error.rs b/src/error.rs
index 6d71362..e7d9542 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -40,6 +40,59 @@ impl Error {
Error::new(raw::LIBSSH2_ERROR_CHANNEL_EOF_SENT, "end of file")
}
+ /// Construct an error from an error code from libssh2
+ pub fn from_errno(code: libc::c_int) -> Error {
+ let msg = match code {
+ raw::LIBSSH2_ERROR_BANNER_RECV => "banner recv failure",
+ raw::LIBSSH2_ERROR_BANNER_SEND => "banner send failure",
+ raw::LIBSSH2_ERROR_INVALID_MAC => "invalid mac",
+ raw::LIBSSH2_ERROR_KEX_FAILURE => "kex failure",
+ raw::LIBSSH2_ERROR_ALLOC => "alloc failure",
+ raw::LIBSSH2_ERROR_SOCKET_SEND => "socket send faiulre",
+ raw::LIBSSH2_ERROR_KEY_EXCHANGE_FAILURE => "key exchange failure",
+ raw::LIBSSH2_ERROR_TIMEOUT => "timed out",
+ raw::LIBSSH2_ERROR_HOSTKEY_INIT => "hostkey init error",
+ raw::LIBSSH2_ERROR_HOSTKEY_SIGN => "hostkey sign error",
+ raw::LIBSSH2_ERROR_DECRYPT => "decrypt error",
+ raw::LIBSSH2_ERROR_SOCKET_DISCONNECT => "socket disconnected",
+ raw::LIBSSH2_ERROR_PROTO => "protocol error",
+ raw::LIBSSH2_ERROR_PASSWORD_EXPIRED => "password expired",
+ raw::LIBSSH2_ERROR_FILE => "file error",
+ raw::LIBSSH2_ERROR_METHOD_NONE => "bad method name",
+ raw::LIBSSH2_ERROR_AUTHENTICATION_FAILED => "authentication failed",
+ raw::LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED => "public key unverified",
+ raw::LIBSSH2_ERROR_CHANNEL_OUTOFORDER => "channel out of order",
+ raw::LIBSSH2_ERROR_CHANNEL_FAILURE => "channel failure",
+ raw::LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED => "request denied",
+ raw::LIBSSH2_ERROR_CHANNEL_UNKNOWN => "unknown channel error",
+ raw::LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED => "window exceeded",
+ raw::LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED => "packet exceeded",
+ raw::LIBSSH2_ERROR_CHANNEL_CLOSED => "closed channel",
+ raw::LIBSSH2_ERROR_CHANNEL_EOF_SENT => "eof sent",
+ raw::LIBSSH2_ERROR_SCP_PROTOCOL => "scp protocol error",
+ raw::LIBSSH2_ERROR_ZLIB => "zlib error",
+ raw::LIBSSH2_ERROR_SOCKET_TIMEOUT => "socket timeout",
+ raw::LIBSSH2_ERROR_SFTP_PROTOCOL => "sftp protocol error",
+ raw::LIBSSH2_ERROR_REQUEST_DENIED => "request denied",
+ raw::LIBSSH2_ERROR_METHOD_NOT_SUPPORTED => "method not supported",
+ raw::LIBSSH2_ERROR_INVAL => "invalid",
+ raw::LIBSSH2_ERROR_INVALID_POLL_TYPE => "invalid poll type",
+ raw::LIBSSH2_ERROR_PUBLICKEY_PROTOCOL => "public key protocol error",
+ raw::LIBSSH2_ERROR_EAGAIN => "operation would block",
+ raw::LIBSSH2_ERROR_BUFFER_TOO_SMALL => "buffer too small",
+ raw::LIBSSH2_ERROR_BAD_USE => "bad use error",
+ raw::LIBSSH2_ERROR_COMPRESS => "compression error",
+ raw::LIBSSH2_ERROR_OUT_OF_BOUNDARY => "out of bounds",
+ raw::LIBSSH2_ERROR_AGENT_PROTOCOL => "invalid agent protocol",
+ raw::LIBSSH2_ERROR_SOCKET_RECV => "error receiving on socket",
+ raw::LIBSSH2_ERROR_ENCRYPT => "bad encrypt",
+ raw::LIBSSH2_ERROR_BAD_SOCKET => "bad socket",
+ raw::LIBSSH2_ERROR_KNOWN_HOSTS => "known hosts error",
+ _ => "unknown error"
+ };
+ Error::new(code, msg)
+ }
+
/// Get the message corresponding to this error
pub fn message(&self) -> &str { self.msg }
}
diff --git a/src/lib.rs b/src/lib.rs
index 5b4e168..da6a70f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -64,6 +64,7 @@ pub use error::Error;
pub use knownhosts::{KnownHosts, Hosts, Host};
pub use listener::Listener;
pub use session::Session;
+pub use sftp::{Sftp};
mod agent;
mod channel;
@@ -71,6 +72,7 @@ mod error;
mod knownhosts;
mod listener;
mod session;
+mod sftp;
/// Initialize the libssh2 library.
///
diff --git a/src/session.rs b/src/session.rs
index 24f658e..49948f1 100644
--- a/src/session.rs
+++ b/src/session.rs
@@ -6,7 +6,7 @@ use std::str;
use libc::{mod, c_uint, c_int, c_void, c_long};
use {raw, Error, DisconnectCode, ByApplication, SessionFlag, HostKeyType};
-use {MethodType, Agent, Channel, Listener, HashType, KnownHosts};
+use {MethodType, Agent, Channel, Listener, HashType, KnownHosts, Sftp};
pub struct Session {
raw: *mut raw::LIBSSH2_SESSION,
@@ -529,6 +529,23 @@ impl Session {
}
}
+ /// Open a channel and initialize the SFTP subsystem.
+ ///
+ /// Although the SFTP subsystem operates over the same type of channel as
+ /// those exported by the Channel API, the protocol itself implements its
+ /// own unique binary packet protocol which must be managed with the
+ /// methods on `Sftp`.
+ pub fn sftp(&self) -> Result<Sftp, Error> {
+ unsafe {
+ let ret = raw::libssh2_sftp_init(self.raw);
+ if ret.is_null() {
+ Err(Error::last_error(self).unwrap())
+ } else {
+ Ok(Sftp::from_raw(self, ret))
+ }
+ }
+ }
+
/// Gain access to the underlying raw libssh2 session pointer.
pub fn raw(&self) -> *mut raw::LIBSSH2_SESSION { self.raw }
diff --git a/src/sftp.rs b/src/sftp.rs
new file mode 100644
index 0000000..08391fa
--- /dev/null
+++ b/src/sftp.rs
@@ -0,0 +1,42 @@
+use std::kinds::marker;
+use libc::c_int;
+
+use {raw, Session, Error};
+
+pub struct Sftp<'a> {
+ raw: *mut raw::LIBSSH2_SFTP,
+ marker1: marker::NoSync,
+ marker2: marker::ContravariantLifetime<'a>,
+ marker3: marker::NoSend,
+}
+
+impl<'a> Sftp<'a> {
+ /// Wraps a raw pointer in a new Sftp structure tied to the lifetime of the
+ /// given session.
+ ///
+ /// This consumes ownership of `raw`.
+ pub unsafe fn from_raw(_sess: &Session,
+ raw: *mut raw::LIBSSH2_SFTP) -> Sftp {
+ Sftp {
+ raw: raw,
+ marker1: marker::NoSync,
+ marker2: marker::ContravariantLifetime,
+ marker3: marker::NoSend,
+ }
+ }
+
+ /// Peel off the last error to happen on this SFTP instance.
+ pub fn last_error(&self) -> Error {
+ let code = unsafe { raw::libssh2_sftp_last_error(self.raw) };
+ Error::from_errno(code as c_int)
+ }
+}
+
+#[unsafe_destructor]
+impl<'a> Drop for Sftp<'a> {
+ fn drop(&mut self) {
+ unsafe { assert_eq!(raw::libssh2_sftp_shutdown(self.raw), 0) }
+ }
+}
+
+
diff --git a/tests/all.rs b/tests/all.rs
index bd5f085..5bb7257 100644
--- a/tests/all.rs
+++ b/tests/all.rs
@@ -10,6 +10,7 @@ mod agent;
mod session;
mod channel;
mod knownhosts;
+mod sftp;
pub fn socket() -> TcpStream {
let stream = TcpStream::connect(SocketAddr {
diff --git a/tests/sftp.rs b/tests/sftp.rs
new file mode 100644
index 0000000..f600abd
--- /dev/null
+++ b/tests/sftp.rs
@@ -0,0 +1,5 @@
+#[test]
+fn smoke() {
+ let (_tcp, sess) = ::authed_session();
+ sess.sftp().unwrap();
+}