Skip to content

Commit

Permalink
add mime support
Browse files Browse the repository at this point in the history
  • Loading branch information
tsnoam committed Nov 26, 2023
1 parent de57280 commit 2ed79cb
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 0 deletions.
18 changes: 18 additions & 0 deletions curl-sys/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ pub enum curl_httppost {
// pub userp: *mut c_void,
}

pub enum curl_mime {}
pub enum curl_mimepart {}

// pub const HTTPPOST_FILENAME: c_long = 1 << 0;
// pub const HTTPPOST_READFILE: c_long = 1 << 1;
// pub const HTTPPOST_PTRNAME: c_long = 1 << 2;
Expand Down Expand Up @@ -607,6 +610,8 @@ pub const CURLOPT_PROXY_SSL_OPTIONS: CURLoption = CURLOPTTYPE_LONG + 261;

pub const CURLOPT_ABSTRACT_UNIX_SOCKET: CURLoption = CURLOPTTYPE_OBJECTPOINT + 264;

pub const CURLOPT_MIMEPOST: CURLoption = CURLOPTTYPE_OBJECTPOINT + 269;

pub const CURLOPT_DOH_URL: CURLoption = CURLOPTTYPE_OBJECTPOINT + 279;
pub const CURLOPT_UPLOAD_BUFFERSIZE: CURLoption = CURLOPTTYPE_LONG + 280;

Expand Down Expand Up @@ -1161,6 +1166,19 @@ extern "C" {
sockfd: curl_socket_t,
sockp: *mut c_void,
) -> CURLMcode;

pub fn curl_mime_init(easy_handle: *mut CURL) -> *mut curl_mime;
pub fn curl_mime_free(mime_handle: *mut curl_mime);
pub fn curl_mime_addpart(mime_handle: *mut curl_mime) -> *mut curl_mimepart;
pub fn curl_mime_data(
mimepart: *mut curl_mimepart,
data: *const c_char,
datasize: size_t,
) -> CURLcode;
pub fn curl_mime_name(part: *mut curl_mimepart, name: *const c_char) -> CURLcode;
pub fn curl_mime_filename(part: *mut curl_mimepart, filename: *const c_char) -> CURLcode;
pub fn curl_mime_type(part: *mut curl_mimepart, mimetype: *const c_char) -> CURLcode;
pub fn curl_mime_subparts(part: *mut curl_mimepart, subparts: *mut curl_mime) -> CURLcode;
}

pub fn rust_crate_version() -> &'static str {
Expand Down
6 changes: 6 additions & 0 deletions src/easy/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use libc::c_void;
use crate::easy::handler::{self, InfoType, ReadError, SeekResult, WriteError};
use crate::easy::handler::{Auth, NetRc, PostRedirections, ProxyType, SslOpt};
use crate::easy::handler::{HttpVersion, IpResolve, SslVersion, TimeCondition};
use crate::easy::mime::Mime;
use crate::easy::{Easy2, Handler};
use crate::easy::{Form, List};
use crate::Error;
Expand Down Expand Up @@ -1470,6 +1471,11 @@ impl Easy {
pub fn take_error_buf(&self) -> Option<String> {
self.inner.take_error_buf()
}

/// Same as [`Easy2::add_mime`](struct.Easy2.html#method.add_mime)
pub fn add_mime(&mut self) -> Mime<EasyData> {
self.inner.add_mime()
}
}

impl EasyData {
Expand Down
21 changes: 21 additions & 0 deletions src/easy/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use socket2::Socket;

use crate::easy::form;
use crate::easy::list;
use crate::easy::mime::Mime;
use crate::easy::windows;
use crate::easy::{Form, List};
use crate::panic;
Expand Down Expand Up @@ -378,6 +379,8 @@ pub fn ssl_ctx(cx: *mut c_void) -> Result<(), Error> {
/// ```
pub struct Easy2<H> {
inner: Box<Inner<H>>,
/// Mime handles to free upon drop
mimes: Vec<*mut curl_sys::curl_mime>,
}

struct Inner<H> {
Expand Down Expand Up @@ -599,6 +602,7 @@ impl<H: Handler> Easy2<H> {
error_buf: RefCell::new(vec![0; curl_sys::CURL_ERROR_SIZE]),
handler,
}),
mimes: vec![],
};
ret.default_configure();
ret
Expand Down Expand Up @@ -3509,6 +3513,20 @@ impl<H> Easy2<H> {
}
Err(err)
}

/// Create a mime handle attached to this [Easy2] instance.
pub fn add_mime(&mut self) -> Mime<H> {
Mime::new(self)
}

pub(crate) fn mimepost(&mut self, mime_handle: *mut curl_sys::curl_mime) -> Result<(), Error> {
self.mimes.push(mime_handle);

let rc = unsafe {
curl_sys::curl_easy_setopt(self.raw(), curl_sys::CURLOPT_MIMEPOST, mime_handle)
};
self.cvt(rc)
}
}

impl<H: fmt::Debug> fmt::Debug for Easy2<H> {
Expand All @@ -3524,6 +3542,9 @@ impl<H> Drop for Easy2<H> {
fn drop(&mut self) {
unsafe {
curl_sys::curl_easy_cleanup(self.inner.handle);
for &mime_handle in self.mimes.iter() {
curl_sys::curl_mime_free(mime_handle);
}
}
}
}
Expand Down
112 changes: 112 additions & 0 deletions src/easy/mime.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
use crate::easy::Easy2;
use crate::error::Error;
use curl_sys::{
curl_mime_addpart, curl_mime_data, curl_mime_filename, curl_mime_free, curl_mime_init,
curl_mime_name, curl_mime_type, curl_mimepart, CURLcode, CURLE_OK,
};
use std::ffi::CString;
use std::marker::PhantomData;
use std::ptr::null_mut;

#[derive(Debug)]
pub struct Mime<'e, E> {
handle: *mut curl_sys::curl_mime,
easy: &'e mut Easy2<E>,
}

impl<'a, T> Mime<'a, T> {
/// Create a mime handle
pub(crate) fn new(easy: &'a mut Easy2<T>) -> Self {
let handle = unsafe { curl_mime_init(easy.raw()) };
assert!(!handle.is_null());

Self { handle, easy }
}

/// Finalize creation of a mime post.
pub fn post(mut self) -> Result<(), Error> {
// once giving the mime handle to `Easy2` it is now their responsibility to free the handle.
// so we need to make sure `Drop` below won't try to free it.
let mime_handle = self.handle;
self.handle = null_mut();
self.easy.mimepost(mime_handle)
}

/// Append a new empty part to a mime structure
pub fn add_part(&mut self) -> MimePart<'a> {
MimePart::new(self)
}
}

impl<E> Drop for Mime<'_, E> {
fn drop(&mut self) {
// we only need to free mime handles which hadn't been given to the ownership of `Easy2`.
if !self.handle.is_null() {
unsafe { curl_mime_free(self.handle) }
}
}
}

#[derive(Debug)]
pub struct MimePart<'a> {
handle: *mut curl_mimepart,
// attach to the lifetime of our [Mime] handle, but without taking ownership
_lifetime: PhantomData<&'a ()>,
}

impl<'a> MimePart<'a> {
fn new<E>(mime: &mut Mime<E>) -> Self {
let handle = unsafe { curl_mime_addpart(mime.handle) };
assert!(!handle.is_null());

Self {
handle,
_lifetime: Default::default(),
}
}

/// Set a mime part's body data
pub fn set_data(self, data: impl AsRef<[u8]>) -> Result<Self, Error> {
let data = data.as_ref();
let code = unsafe { curl_mime_data(self.handle, data.as_ptr() as *const _, data.len()) };
code_ok(code).map(|_| self)
}

/// Set a mime part's name
///
/// # Panics
/// If `name` contains nul bytes, panic will occur.
pub fn set_name(self, name: &str) -> Result<Self, Error> {
let data = CString::new(name).unwrap();
let code = unsafe { curl_mime_name(self.handle, data.as_ptr()) };
code_ok(code).map(|_| self)
}

/// Set a mime part's remote file name
///
/// # Panics
/// If `filename` contains nul bytes, panic will occur.
pub fn set_filename(self, filename: &str) -> Result<Self, Error> {
let data = CString::new(filename).unwrap();
let code = unsafe { curl_mime_filename(self.handle, data.as_ptr()) };
code_ok(code).map(|_| self)
}

/// Set a mime part's content type
///
/// # Panics
/// If `content_type` contains nul bytes, panic will occur.
pub fn set_content_type(self, content_type: &str) -> Result<Self, Error> {
let data = CString::new(content_type).unwrap();
let code = unsafe { curl_mime_type(self.handle, data.as_ptr()) };
code_ok(code).map(|_| self)
}
}

fn code_ok(code: CURLcode) -> Result<(), Error> {
if code == CURLE_OK {
Ok(())
} else {
Err(Error::new(code))
}
}
1 change: 1 addition & 0 deletions src/easy/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ mod form;
mod handle;
mod handler;
mod list;
mod mime;
mod windows;

pub use self::form::{Form, Part};
Expand Down

0 comments on commit 2ed79cb

Please sign in to comment.