Private
Public Access
1
0

xpc: adds OpenAttachmentFd

This commit is contained in:
2025-08-24 23:20:25 -07:00
parent f2353461b3
commit eaa5966e99
2 changed files with 68 additions and 0 deletions

View File

@@ -113,12 +113,47 @@ impl XpcAgent {
let response = rt_conn.block_on(super::rpc::dispatch(&agent_conn, &conns_for_handler, client, &map));
let reply = xpc_sys::xpc_dictionary_create_reply(msg);
if !reply.is_null() {
use std::ffi::CString as StdCString;
use std::os::fd::AsRawFd;
// Precompute optional fd_path instruction from the message map
let mut maybe_fd_path: Option<String> = None;
if let Message::Dictionary(ref resp_map) = response {
let attach = super::util::dict_get_str(resp_map, "attach_fd").unwrap_or_default() == "true";
if attach {
maybe_fd_path = super::util::dict_get_str(resp_map, "fd_path");
}
}
let payload = message_to_xpc_object(response);
let apply_block = ConcreteBlock::new(move |key: *const c_char, value: xpc_sys::xpc_object_t| {
xpc_sys::xpc_dictionary_set_value(reply, key, value);
})
.copy();
xpc_sys::xpc_dictionary_apply(payload, apply_block.deref() as *const _ as *mut _);
// Optional FD attachment if requested by response
if let Some(fd_path) = maybe_fd_path {
match std::fs::OpenOptions::new().read(true).open(&fd_path) {
Ok(file) => {
let raw_fd = file.as_raw_fd();
unsafe {
let fd_obj = xpc_sys::xpc_fd_create(raw_fd);
let key = StdCString::new("fd").unwrap();
xpc_sys::xpc_dictionary_set_value(reply, key.as_ptr(), fd_obj);
// fd_obj is retained by reply; release our reference
xpc_sys::xpc_release(fd_obj);
}
// Keep file alive until after send
std::mem::forget(file);
}
Err(e) => {
log::warn!(target: LOG_TARGET, "Failed to open fd_path '{}': {}", fd_path, e);
}
}
}
xpc_sys::xpc_connection_send_message(client, reply);
xpc_sys::xpc_release(payload);
xpc_sys::xpc_release(reply);

View File

@@ -303,6 +303,39 @@ pub async fn dispatch(
}
}
// OpenAttachmentFd (attach file descriptor in reply)
"OpenAttachmentFd" => {
let args = match get_dictionary_field(root, "arguments") {
Some(a) => a,
None => return make_error_reply("InvalidRequest", "Missing arguments"),
};
let attachment_id = match dict_get_str(args, "attachment_id") {
Some(v) => v,
None => return make_error_reply("InvalidRequest", "Missing attachment_id"),
};
let preview = dict_get_str(args, "preview")
.map(|s| s == "true")
.unwrap_or(false);
match agent
.send_event(|r| Event::GetAttachment(attachment_id, r))
.await
{
Ok(attachment) => {
let path = attachment.get_path_for_preview(preview);
let mut reply: XpcMap = HashMap::new();
// The agent resolves fd_path to a file descriptor and returns it in the reply
dict_put_str(&mut reply, "type", "OpenAttachmentFdResponse");
dict_put_str(&mut reply, "fd_path", &path.to_string_lossy());
dict_put_str(&mut reply, "attach_fd", "true");
Message::Dictionary(reply)
}
Err(e) => make_error_reply("DaemonError", &format!("{}", e)),
}
}
// DownloadAttachment
"DownloadAttachment" => {
let args = match get_dictionary_field(root, "arguments") {