use std::time::Instant;
use crate::ffi::api::common::Limits;
use crate::ffi::errors::{ErrorHandle, Status};
use crate::ffi::{borrow_text, Buffer, Slice};
use crate::headers::{Cookie, CookieJar, SameSite, SetCookie};
use crate::models::Url;
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_cookie_new() -> *mut Cookie {
Box::into_raw(Box::new(Cookie::new()))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_cookie_parse(value: *const u8, value_len: usize) -> *mut Cookie {
match unsafe { borrow_text(value, value_len) } {
Some(value) => Box::into_raw(Box::new(Cookie::parse(value))),
None => std::ptr::null_mut(),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_cookie_free(cookie: *mut Cookie) {
if !cookie.is_null() {
drop(unsafe { Box::from_raw(cookie) });
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_cookie_count(cookie: *const Cookie) -> usize {
unsafe { cookie.as_ref() }.map_or(0, |cookie| cookie.pairs.len())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_cookie_name(cookie: *const Cookie, index: usize) -> Slice {
Slice::maybe(unsafe { cookie.as_ref() }.and_then(|cookie| cookie.pairs.get(index)).map(|(name, _)| name.as_str()))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_cookie_value(cookie: *const Cookie, index: usize) -> Slice {
Slice::maybe(unsafe { cookie.as_ref() }.and_then(|cookie| cookie.pairs.get(index)).map(|(_, value)| value.as_str()))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_cookie_get(cookie: *const Cookie, name: *const u8, name_len: usize) -> Slice {
let Some(name) = (unsafe { borrow_text(name, name_len) }) else {
return Slice::ABSENT;
};
Slice::maybe(unsafe { cookie.as_ref() }.and_then(|cookie| cookie.get(name)))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_cookie_append(cookie: *mut Cookie, name: *const u8, name_len: usize, value: *const u8, value_len: usize) -> bool {
let (Some(cookie), Some(name), Some(value)) = (unsafe { cookie.as_mut() }, unsafe { borrow_text(name, name_len) }, unsafe { borrow_text(value, value_len) })
else {
return false;
};
cookie.pairs.push((name.to_owned(), value.to_owned()));
true
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_cookie_build(cookie: *const Cookie) -> Buffer {
match unsafe { cookie.as_ref() } {
Some(cookie) => Buffer::new(cookie.build().into_bytes()),
None => Buffer::EMPTY,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_setcookie_new(name: *const u8, name_len: usize, value: *const u8, value_len: usize) -> *mut SetCookie {
let (Some(name), Some(value)) = (unsafe { borrow_text(name, name_len) }, unsafe { borrow_text(value, value_len) }) else {
return std::ptr::null_mut();
};
Box::into_raw(Box::new(SetCookie::new(name, value)))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_setcookie_parse(value: *const u8, value_len: usize, out: *mut *mut SetCookie, error: *mut *mut ErrorHandle) -> Status {
if out.is_null() {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
}
let Some(value) = (unsafe { borrow_text(value, value_len) }) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
match SetCookie::parse(value) {
Ok(cookie) => {
unsafe { *out = Box::into_raw(Box::new(cookie)) };
Status::Ok
}
Err(failure) => unsafe { ErrorHandle::report(error, &failure) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_setcookie_free(cookie: *mut SetCookie) {
if !cookie.is_null() {
drop(unsafe { Box::from_raw(cookie) });
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_setcookie_name(cookie: *const SetCookie) -> Slice {
Slice::maybe(unsafe { cookie.as_ref() }.map(|cookie| cookie.name.as_str()))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_setcookie_value(cookie: *const SetCookie) -> Slice {
Slice::maybe(unsafe { cookie.as_ref() }.map(|cookie| cookie.value.as_str()))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_setcookie_expires(cookie: *const SetCookie) -> Slice {
Slice::maybe(unsafe { cookie.as_ref() }.and_then(|cookie| cookie.expires.as_deref()))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_setcookie_max_age(cookie: *const SetCookie, out: *mut i64) -> bool {
let Some(max_age) = unsafe { cookie.as_ref() }.and_then(|cookie| cookie.max_age) else {
return false;
};
if !out.is_null() {
unsafe { *out = max_age };
}
true
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_setcookie_domain(cookie: *const SetCookie) -> Slice {
Slice::maybe(unsafe { cookie.as_ref() }.and_then(|cookie| cookie.domain.as_deref()))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_setcookie_path(cookie: *const SetCookie) -> Slice {
Slice::maybe(unsafe { cookie.as_ref() }.and_then(|cookie| cookie.path.as_deref()))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_setcookie_secure(cookie: *const SetCookie) -> bool {
unsafe { cookie.as_ref() }.is_some_and(|cookie| cookie.secure)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_setcookie_httponly(cookie: *const SetCookie) -> bool {
unsafe { cookie.as_ref() }.is_some_and(|cookie| cookie.httponly)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_setcookie_samesite(cookie: *const SetCookie) -> i32 {
match unsafe { cookie.as_ref() }.and_then(|cookie| cookie.samesite) {
Some(SameSite::Strict) => 0,
Some(SameSite::Lax) => 1,
Some(SameSite::None) => 2,
None => -1,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_setcookie_set_value(cookie: *mut SetCookie, value: *const u8, value_len: usize) -> bool {
let (Some(cookie), Some(value)) = (unsafe { cookie.as_mut() }, unsafe { borrow_text(value, value_len) }) else {
return false;
};
cookie.value = value.to_owned();
true
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_setcookie_set_expires(cookie: *mut SetCookie, value: *const u8, value_len: usize) -> bool {
let Some(cookie) = (unsafe { cookie.as_mut() }) else {
return false;
};
if value.is_null() {
cookie.expires = None;
return true;
}
let Some(value) = (unsafe { borrow_text(value, value_len) }) else {
return false;
};
cookie.expires = Some(value.to_owned());
true
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_setcookie_set_max_age(cookie: *mut SetCookie, present: bool, max_age: i64) -> bool {
let Some(cookie) = (unsafe { cookie.as_mut() }) else {
return false;
};
cookie.max_age = present.then_some(max_age);
true
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_setcookie_set_domain(cookie: *mut SetCookie, value: *const u8, value_len: usize) -> bool {
let Some(cookie) = (unsafe { cookie.as_mut() }) else {
return false;
};
if value.is_null() {
cookie.domain = None;
return true;
}
let Some(value) = (unsafe { borrow_text(value, value_len) }) else {
return false;
};
cookie.domain = Some(value.to_owned());
true
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_setcookie_set_path(cookie: *mut SetCookie, value: *const u8, value_len: usize) -> bool {
let Some(cookie) = (unsafe { cookie.as_mut() }) else {
return false;
};
if value.is_null() {
cookie.path = None;
return true;
}
let Some(value) = (unsafe { borrow_text(value, value_len) }) else {
return false;
};
cookie.path = Some(value.to_owned());
true
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_setcookie_set_secure(cookie: *mut SetCookie, secure: bool) -> bool {
let Some(cookie) = (unsafe { cookie.as_mut() }) else {
return false;
};
cookie.secure = secure;
true
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_setcookie_set_httponly(cookie: *mut SetCookie, httponly: bool) -> bool {
let Some(cookie) = (unsafe { cookie.as_mut() }) else {
return false;
};
cookie.httponly = httponly;
true
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_setcookie_set_samesite(cookie: *mut SetCookie, samesite: i32) -> bool {
let Some(cookie) = (unsafe { cookie.as_mut() }) else {
return false;
};
cookie.samesite = match samesite {
0 => Some(SameSite::Strict),
1 => Some(SameSite::Lax),
2 => Some(SameSite::None),
-1 => None,
_ => return false,
};
true
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_setcookie_build(cookie: *const SetCookie, out: *mut Buffer, error: *mut *mut ErrorHandle) -> Status {
let Some(cookie) = (unsafe { cookie.as_ref() }) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
if out.is_null() {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
}
match cookie.build() {
Ok(value) => {
unsafe { *out = Buffer::new(value.into_bytes()) };
Status::Ok
}
Err(failure) => unsafe { ErrorHandle::report(error, &failure) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_cookiejar_new(limits: *const Limits) -> *mut CookieJar {
Box::into_raw(Box::new(CookieJar::new().with_limits(unsafe { Limits::or_default(limits) })))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_cookiejar_free(jar: *mut CookieJar) {
if !jar.is_null() {
drop(unsafe { Box::from_raw(jar) });
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_cookiejar_learn(jar: *const CookieJar, url: *const Url, values: *const Slice, value_count: usize) -> bool {
let (Some(jar), Some(url)) = (unsafe { jar.as_ref() }, unsafe { url.as_ref() }) else {
return false;
};
if values.is_null() && value_count > 0 {
return false;
}
let mut parsed = Vec::with_capacity(value_count);
for index in 0..value_count {
let slice = unsafe { *values.add(index) };
let Some(value) = (unsafe { borrow_text(slice.data, slice.len) }) else {
return false;
};
parsed.push(value);
}
jar.learn(url, &parsed, Instant::now());
true
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_cookiejar_cookie(jar: *const CookieJar, url: *const Url) -> Buffer {
let (Some(jar), Some(url)) = (unsafe { jar.as_ref() }, unsafe { url.as_ref() }) else {
return Buffer::EMPTY;
};
match jar.cookie(url, Instant::now()) {
Some(value) => Buffer::new(value.into_bytes()),
None => Buffer::EMPTY,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_cookiejar_prune(jar: *const CookieJar) {
if let Some(jar) = unsafe { jar.as_ref() } {
jar.prune(Instant::now());
}
}