use std::default::Default;
use std::io;
use std::mem;
use std::panic;
use std::ptr;
use std::borrow::Cow;
use std::io::{Read, Write, Seek, SeekFrom};
use libc::{self, c_char, c_int, c_void, mode_t, off_t};
use smbclient_sys::*;
use util::*;
use result::Result;
const SMBC_FALSE: smbc_bool = 0;
const SMBC_TRUE: smbc_bool = 1;
pub struct SmbClient<'a> {
ctx: *mut SMBCCTX,
#[allow(dead_code)]
auth_fn: &'a for<'b> Fn(&'b str, &'b str) -> (Cow<'a, str>, Cow<'a, str>, Cow<'a, str>),
}
pub struct SmbFile<'a: 'b, 'b> {
smbc: &'b SmbClient<'a>,
fd: *mut SMBCFILE,
}
const DEF_CRED: (Cow<'static, str>, Cow<'static, str>, Cow<'static, str>) = (Cow::Borrowed("WORKGROUP"), Cow::Borrowed("guest"), Cow::Borrowed(""));
impl<'a> SmbClient<'a> {
pub fn new<F>(auth_fn: &'a F) -> Result<SmbClient<'a>>
where F: for<'b> Fn(&'b str, &'b str) -> (Cow<'a, str>, Cow<'a, str>, Cow<'a, str>) {
let mut smbc = SmbClient {
ctx: ptr::null_mut(),
auth_fn: auth_fn,
};
unsafe {
let ctx = try!(result_from_ptr_mut(smbc_new_context()));
smbc_setOptionUserData(ctx, auth_fn as *const _ as *mut c_void);
smbc_setFunctionAuthDataWithContext(ctx, Some(Self::auth_wrapper::<F>));
smbc_setOptionOneSharePerServer(ctx, SMBC_TRUE);
smbc_setOptionDebugToStderr(ctx, SMBC_TRUE);
smbc.ctx = try!(result_from_ptr_mut(smbc_init_context(ctx)));
}
trace!(target: "smbc", "new smbclient");
Ok(smbc)
}
extern "C" fn auth_wrapper<F: 'a>(ctx: *mut SMBCCTX,
srv: *const c_char,
shr: *const c_char,
wg: *mut c_char,
wglen: c_int,
un: *mut c_char,
unlen: c_int,
pw: *mut c_char,
pwlen: c_int)
-> ()
where F: for<'b> Fn(&'b str, &'b str) -> (Cow<'a, str>, Cow<'a, str>, Cow<'a, str>) {
unsafe {
let srv = cstr(srv);
let shr = cstr(shr);
trace!(target: "smbc", "authenticating on {}\\{}", &srv, &shr);
let auth: &'a F = mem::transmute(smbc_getOptionUserData(ctx) as *const c_void);
let auth = panic::AssertUnwindSafe(auth);
let r = panic::catch_unwind(|| {
trace!(target: "smbc", "auth with {:?}\\{:?}", srv, shr);
auth(&srv, &shr)
});
let (workgroup, username, password) = r.unwrap_or(DEF_CRED);
trace!(target: "smbc", "cred: {}\\{} {}", &workgroup, &username, &password);
write_to_cstr(wg as *mut u8, wglen as usize, &workgroup);
write_to_cstr(un as *mut u8, unlen as usize, &username);
write_to_cstr(pw as *mut u8, pwlen as usize, &password);
}
()
}
pub fn open_with<'b, P: AsRef<str>>(&'b self,
path: P,
options: OpenOptions)
-> Result<SmbFile<'a, 'b>> {
trace!(target: "smbc", "open_with {:?}", options);
let open_fn = try_ufn!(smbc_getFunctionOpen <- self);
let path = try!(cstring(path));
trace!(target: "smbc", "opening {:?} with {:?}", path, open_fn);
unsafe {
let fd = try!(result_from_ptr_mut(open_fn(self.ctx,
path.as_ptr(),
try!(options.to_flags()),
options.mode)));
if (fd as i64) < 0 {
trace!(target: "smbc", "neg fd");
}
Ok(SmbFile {
smbc: &self,
fd: fd,
})
}
}
pub fn open<'b, P: AsRef<str>>(&'b self, path: P) -> Result<SmbFile<'a, 'b>> {
self.open_ro(path)
}
pub fn create<'b, P: AsRef<str>>(&'b self, path: P) -> Result<SmbFile<'a, 'b>> {
self.open_wo(path)
}
pub fn open_ro<'b, P: AsRef<str>>(&'b self, path: P) -> Result<SmbFile<'a, 'b>> {
self.open_with(path, OpenOptions::default())
}
pub fn open_wo<'b, P: AsRef<str>>(&'b self, path: P) -> Result<SmbFile<'a, 'b>> {
self.open_with(path, OpenOptions::default().read(false).write(true).create(true).truncate(true))
}
pub fn open_rw<'b, P: AsRef<str>>(&'b self, path: P) -> Result<SmbFile<'a, 'b>> {
self.open_with(path, OpenOptions::default().read(true).write(true).create(true))
}
#[doc(hidden)]
pub fn metadata<P: AsRef<str>>(&self, path: P) -> Result<()> {
let stat_fn = try_ufn!(smbc_getFunctionStat <- self);
let path = try!(cstring(path));
unimplemented!();
}
pub fn create_dir<P: AsRef<str>>(&self, path: P) -> Result<()> {
let mkdir_fn = try_ufn!(smbc_getFunctionMkdir <- self);
let path = try!(cstring(path));
try!(to_result_with_le(unsafe { mkdir_fn(self.ctx, path.as_ptr(), 0o755) }));
Ok(())
}
pub fn remove_dir<P: AsRef<str>>(&self, path: P) -> Result<()> {
let rmdir_fn = try_ufn!(smbc_getFunctionRmdir <- self);
let path = try!(cstring(path));
try!(to_result_with_le(unsafe { rmdir_fn(self.ctx, path.as_ptr()) }));
Ok(())
}
}
impl<'a> Drop for SmbClient<'a> {
fn drop(&mut self) {
trace!(target: "smbc", "closing smbclient");
unsafe {
smbc_free_context(self.ctx, 1 as c_int);
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct OpenOptions {
flags: c_int,
read: bool,
write: bool,
mode: mode_t,
}
impl OpenOptions {
pub fn read(mut self, read: bool) -> Self {
self.read = read;
self
}
pub fn write(mut self, write: bool) -> Self {
self.write = write;
self
}
pub fn append(mut self, append: bool) -> Self {
self.flag(libc::O_APPEND, append);
self
}
pub fn create(mut self, create: bool) -> Self {
self.flag(libc::O_CREAT, create);
self
}
pub fn truncate(mut self, truncate: bool) -> Self {
self.flag(libc::O_TRUNC, truncate);
self
}
pub fn exclusive(mut self, exclusive: bool) -> Self {
self.flag(libc::O_EXCL, exclusive);
self
}
pub fn mode(mut self, mode: mode_t) -> Self {
self.mode = mode;
self
}
fn flag(&mut self, flag: c_int, on: bool) {
if on {
self.flags |= flag;
} else {
self.flags &= !flag;
}
}
fn to_flags(&self) -> Result<c_int> {
let base_mode = match (self.read, self.write) {
(false, false) |
(true, false) => libc::O_RDONLY,
(false, true) => libc::O_WRONLY,
(true, true) => libc::O_RDWR,
};
Ok(base_mode | self.flags)
}
}
impl Default for OpenOptions {
fn default() -> OpenOptions {
OpenOptions {
flags: 0,
read: true,
write: false,
mode: 0o644,
}
}
}
impl<'a, 'b> SmbFile<'a, 'b> {
}
impl<'a, 'b> Read for SmbFile<'a, 'b> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
trace!(target: "smbc", "reading file to buf [{:?};{}]", buf.as_ptr(), buf.len());
let read_fn = try_ufn!(smbc_getFunctionRead <- self.smbc);
let bytes_read = try!(to_result_with_le(unsafe {
read_fn(self.smbc.ctx,
self.fd,
buf.as_mut_ptr() as *mut c_void,
buf.len() as _)
}));
Ok(bytes_read as usize)
}
}
impl<'a, 'b> Write for SmbFile<'a, 'b> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
trace!(target: "smbc", "writing buf [{:?};{}] to file", buf.as_ptr(), buf.len());
let write_fn = try_ufn!(smbc_getFunctionWrite <- self.smbc);
let bytes_wrote = try!(to_result_with_le(unsafe {
write_fn(self.smbc.ctx,
self.fd,
buf.as_ptr() as *const c_void,
buf.len() as _)
}));
Ok(bytes_wrote as usize)
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl<'a, 'b> Seek for SmbFile<'a, 'b> {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
trace!(target: "smbc", "seeking file {:?}", pos);
let lseek_fn = try_ufn!(smbc_getFunctionLseek <- self.smbc);
let (whence, off) = match pos {
SeekFrom::Start(p) => (libc::SEEK_SET, p as off_t),
SeekFrom::End(p) => (libc::SEEK_END, p as off_t),
SeekFrom::Current(p) => (libc::SEEK_CUR, p as off_t),
};
let res = try!(to_result_with_errno(unsafe { lseek_fn(self.smbc.ctx, self.fd, off, whence) }, libc::EINVAL));
Ok(res as u64)
}
}
impl<'a, 'b> Drop for SmbFile<'a, 'b> {
fn drop(&mut self) {
trace!(target: "smbc", "closing file");
unsafe {
smbc_getFunctionClose(self.smbc.ctx).map(|f| f(self.smbc.ctx, self.fd));
}
}
}