extern crate dbus;
use self::dbus::arg::{Dict, Variant};
use self::dbus::Path as ObjectPath;
use self::dbus::{BusType, Connection, Message, MessageItem, Props};
use std::collections::HashMap;
use std::error::Error;
use std::path::Path;
use std::thread::sleep;
use std::time::Duration;
use bluetooth_device::BluetoothDevice;
use bluetooth_session::BluetoothSession;
const OBEX_BUS: &str = "org.bluez.obex";
const OBEX_PATH: &str = "/org/bluez/obex";
const OBJECT_PUSH_INTERFACE: &str = "org.bluez.obex.ObjectPush1";
const CLIENT_INTERFACE: &str = "org.bluez.obex.Client1";
const TRANSFER_INTERFACE: &str = "org.bluez.obex.Transfer1";
pub enum SessionTarget {
Ftp,
Map,
Opp,
Pbap,
Sync_,
}
impl SessionTarget {
fn as_str(&self) -> &str {
match self {
SessionTarget::Ftp => "ftp",
SessionTarget::Map => "map",
SessionTarget::Opp => "opp",
SessionTarget::Pbap => "pbap",
SessionTarget::Sync_ => "sync",
}
}
}
pub enum TransferState {
Queued,
Active,
Complete,
Suspended,
Error,
}
impl TransferState {
fn as_str(&self) -> &str {
match self {
TransferState::Queued => "queued",
TransferState::Active => "active",
TransferState::Complete => "complete",
TransferState::Suspended => "suspended",
TransferState::Error => "error",
}
}
}
pub fn open_bus_connection() -> Result<Connection, Box<Error>> {
let c = Connection::get_private(BusType::Session)?;
Ok(c)
}
pub struct BluetoothOBEXSession<'a> {
session: &'a BluetoothSession,
object_path: String,
}
impl<'a> BluetoothOBEXSession<'a> {
pub fn new(
session: &'a BluetoothSession,
device: &BluetoothDevice,
) -> Result<BluetoothOBEXSession<'a>, Box<Error>> {
let device_address: String = device.get_address()?;
let mut map = HashMap::new();
map.insert("Target", Variant(SessionTarget::Opp.as_str()));
let args: Dict<&str, Variant<&str>, _> = Dict::new(map);
let m = Message::new_method_call(OBEX_BUS, OBEX_PATH, CLIENT_INTERFACE, "CreateSession")?
.append2(device_address, args);
let r = session
.get_connection()
.send_with_reply_and_block(m, 1000)?;
let session_path: ObjectPath = r.read1()?;
let session_str: String = session_path.parse()?;
let obex_session = BluetoothOBEXSession {
session,
object_path: session_str,
};
Ok(obex_session)
}
pub fn remove_session(&self) -> Result<(), Box<Error>> {
let object_path = ObjectPath::new(self.object_path.as_bytes())?;
let m = Message::new_method_call(OBEX_BUS, OBEX_PATH, CLIENT_INTERFACE, "RemoveSession")?
.append1(object_path);
let _r = self
.session
.get_connection()
.send_with_reply_and_block(m, 1000)?;
Ok(())
}
}
pub struct BluetoothOBEXTransfer<'a> {
session: &'a BluetoothOBEXSession<'a>,
object_path: String,
_name: String,
}
impl<'a> BluetoothOBEXTransfer<'a> {
pub fn send_file(
session: &'a BluetoothOBEXSession,
file_path: &str,
) -> Result<BluetoothOBEXTransfer<'a>, Box<Error>> {
let session_path: String = session.object_path.clone();
let m =
Message::new_method_call(OBEX_BUS, session_path, OBJECT_PUSH_INTERFACE, "SendFile")?
.append1(file_path);
let r = session
.session
.get_connection()
.send_with_reply_and_block(m, 1000)?;
let transfer_path: ObjectPath = r.read1()?;
let transfer_str: String = transfer_path.parse()?;
let file_name: String = match Path::new(file_path).file_name() {
Some(value) => value.to_string_lossy().to_string(),
None => file_path.to_string(),
};
let obex_transfer = BluetoothOBEXTransfer {
session,
object_path: transfer_str,
_name: file_name,
};
Ok(obex_transfer)
}
pub fn status(&self) -> Result<String, Box<Error>> {
let transfer_path = self.object_path.clone();
let p = Props::new(
&self.session.session.get_connection(),
OBEX_BUS,
transfer_path,
TRANSFER_INTERFACE,
1000,
);
let status: MessageItem = p.get("Status")?;
match status.inner::<&str>() {
Ok(value) => Ok(value.to_string()),
Err(_) => Err("Failed to get status.".into()),
}
}
pub fn wait_until_transfer_completed(&self) -> Result<(), Box<Error>> {
sleep(Duration::from_millis(500));
let mut transfer_status: String = self.status()?;
while transfer_status != TransferState::Complete.as_str() {
sleep(Duration::from_millis(500));
transfer_status = match self.status() {
Ok(value) => {
if value == TransferState::Error.as_str() {
break;
} else {
value
}
}
Err(_) => break,
}
}
Ok(())
}
}