From 37503dd3e838bfcdcfd784dce705b169700e0066 Mon Sep 17 00:00:00 2001 From: link2xt Date: Tue, 18 Apr 2023 12:52:36 +0000 Subject: [PATCH 1/6] JSON-RPC: add `get_http_blob` API --- CHANGELOG.md | 5 +++ deltachat-jsonrpc/src/api/mod.rs | 11 ++++++ src/configure.rs | 1 - src/configure/auto_mozilla.rs | 2 +- src/configure/auto_outlook.rs | 2 +- src/configure/read_url.rs | 44 ----------------------- src/http.rs | 24 ------------- src/lib.rs | 3 +- src/net.rs | 5 ++- src/net/http.rs | 62 ++++++++++++++++++++++++++++++++ src/oauth2.rs | 4 +-- src/qr.rs | 2 +- 12 files changed, 88 insertions(+), 77 deletions(-) delete mode 100644 src/configure/read_url.rs delete mode 100644 src/http.rs create mode 100644 src/net/http.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 781fee77e..b2e78974b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## Unreleased + +### Changes +- Add `get_http_blob` JSON-RPC API. + ## [1.112.7] - 2023-04-17 ### Fixes diff --git a/deltachat-jsonrpc/src/api/mod.rs b/deltachat-jsonrpc/src/api/mod.rs index 3ed2605e9..de51d2b37 100644 --- a/deltachat-jsonrpc/src/api/mod.rs +++ b/deltachat-jsonrpc/src/api/mod.rs @@ -1610,6 +1610,17 @@ impl CommandApi { Ok(general_purpose::STANDARD_NO_PAD.encode(blob)) } + /// Makes an HTTP GET request and returns base64-encoded contents. + /// + /// `url` is the HTTP or HTTPS URL. + async fn get_http_blob(&self, account_id: u32, url: String) -> Result { + let ctx = self.get_context(account_id).await?; + let blob = deltachat::net::read_url_blob(&ctx, &url).await?; + + use base64::{engine::general_purpose, Engine as _}; + Ok(general_purpose::STANDARD_NO_PAD.encode(blob)) + } + /// Forward messages to another chat. /// /// All types of messages can be forwarded, diff --git a/src/configure.rs b/src/configure.rs index eb69ad25c..eb929440e 100644 --- a/src/configure.rs +++ b/src/configure.rs @@ -2,7 +2,6 @@ mod auto_mozilla; mod auto_outlook; -mod read_url; mod server_params; use anyhow::{bail, ensure, Context as _, Result}; diff --git a/src/configure/auto_mozilla.rs b/src/configure/auto_mozilla.rs index d53b5a8c5..b71055d81 100644 --- a/src/configure/auto_mozilla.rs +++ b/src/configure/auto_mozilla.rs @@ -6,10 +6,10 @@ use std::str::FromStr; use quick_xml::events::{BytesStart, Event}; -use super::read_url::read_url; use super::{Error, ServerParams}; use crate::context::Context; use crate::login_param::LoginParam; +use crate::net::read_url; use crate::provider::{Protocol, Socket}; #[derive(Debug)] diff --git a/src/configure/auto_outlook.rs b/src/configure/auto_outlook.rs index 8d42fd353..c1cfbe416 100644 --- a/src/configure/auto_outlook.rs +++ b/src/configure/auto_outlook.rs @@ -7,9 +7,9 @@ use std::io::BufRead; use quick_xml::events::Event; -use super::read_url::read_url; use super::{Error, ServerParams}; use crate::context::Context; +use crate::net::read_url; use crate::provider::{Protocol, Socket}; /// Result of parsing a single `Protocol` tag. diff --git a/src/configure/read_url.rs b/src/configure/read_url.rs deleted file mode 100644 index d164a7007..000000000 --- a/src/configure/read_url.rs +++ /dev/null @@ -1,44 +0,0 @@ -use anyhow::{anyhow, format_err}; - -use crate::context::Context; -use crate::socks::Socks5Config; - -pub async fn read_url(context: &Context, url: &str) -> anyhow::Result { - match read_url_inner(context, url).await { - Ok(s) => { - info!(context, "Successfully read url {}", url); - Ok(s) - } - Err(e) => { - info!(context, "Can't read URL {}: {:#}", url, e); - Err(format_err!("Can't read URL {}: {:#}", url, e)) - } - } -} - -pub async fn read_url_inner(context: &Context, url: &str) -> anyhow::Result { - let socks5_config = Socks5Config::from_database(&context.sql).await?; - let client = crate::http::get_client(socks5_config)?; - let mut url = url.to_string(); - - // Follow up to 10 http-redirects - for _i in 0..10 { - let response = client.get(&url).send().await?; - if response.status().is_redirection() { - let headers = response.headers(); - let header = headers - .get_all("location") - .iter() - .last() - .ok_or_else(|| anyhow!("Redirection doesn't have a target location"))? - .to_str()?; - info!(context, "Following redirect to {}", header); - url = header.to_string(); - continue; - } - - return response.text().await.map_err(Into::into); - } - - Err(format_err!("Followed 10 redirections")) -} diff --git a/src/http.rs b/src/http.rs deleted file mode 100644 index 8eed8b55e..000000000 --- a/src/http.rs +++ /dev/null @@ -1,24 +0,0 @@ -//! # HTTP module. - -use std::time::Duration; - -use anyhow::Result; - -use crate::socks::Socks5Config; - -const HTTP_TIMEOUT: Duration = Duration::from_secs(30); - -pub(crate) fn get_client(socks5_config: Option) -> Result { - let builder = reqwest::ClientBuilder::new().timeout(HTTP_TIMEOUT); - let builder = if let Some(socks5_config) = socks5_config { - let proxy = reqwest::Proxy::all(socks5_config.to_url())?; - builder.proxy(proxy) - } else { - // Disable usage of "system" proxy configured via environment variables. - // It is enabled by default in `reqwest`, see - // - // for documentation. - builder.no_proxy() - }; - Ok(builder.build()?) -} diff --git a/src/lib.rs b/src/lib.rs index 51a72a1d5..23947813c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -68,7 +68,6 @@ mod decrypt; pub mod download; mod e2ee; pub mod ephemeral; -mod http; mod imap; pub mod imex; mod scheduler; @@ -104,7 +103,7 @@ mod dehtml; mod authres; mod color; pub mod html; -mod net; +pub mod net; pub mod plaintext; pub mod summary; diff --git a/src/net.rs b/src/net.rs index a0f3a3313..d4d529770 100644 --- a/src/net.rs +++ b/src/net.rs @@ -1,4 +1,4 @@ -///! # Common network utilities. +//! # Common network utilities. use std::net::{IpAddr, SocketAddr}; use std::pin::Pin; use std::str::FromStr; @@ -12,9 +12,12 @@ use tokio_io_timeout::TimeoutStream; use crate::context::Context; use crate::tools::time; +pub(crate) mod http; pub(crate) mod session; pub(crate) mod tls; +pub use http::{read_url, read_url_blob}; + async fn connect_tcp_inner(addr: SocketAddr, timeout_val: Duration) -> Result { let tcp_stream = timeout(timeout_val, TcpStream::connect(addr)) .await diff --git a/src/net/http.rs b/src/net/http.rs new file mode 100644 index 000000000..3dd49b40a --- /dev/null +++ b/src/net/http.rs @@ -0,0 +1,62 @@ +//! # HTTP module. + +use std::time::Duration; + +use anyhow::{anyhow, Result}; + +use crate::context::Context; +use crate::socks::Socks5Config; + +const HTTP_TIMEOUT: Duration = Duration::from_secs(30); + +/// Retrieves the text contents of URL using HTTP GET request. +pub async fn read_url(context: &Context, url: &str) -> Result { + Ok(read_url_inner(context, url).await?.text().await?) +} + +/// Retrieves the binary contents of URL using HTTP GET request. +pub async fn read_url_blob(context: &Context, url: &str) -> Result> { + Ok(read_url_inner(context, url).await?.bytes().await?.into()) +} + +async fn read_url_inner(context: &Context, url: &str) -> Result { + let socks5_config = Socks5Config::from_database(&context.sql).await?; + let client = get_client(socks5_config)?; + let mut url = url.to_string(); + + // Follow up to 10 http-redirects + for _i in 0..10 { + let response = client.get(&url).send().await?; + if response.status().is_redirection() { + let headers = response.headers(); + let header = headers + .get_all("location") + .iter() + .last() + .ok_or_else(|| anyhow!("Redirection doesn't have a target location"))? + .to_str()?; + info!(context, "Following redirect to {}", header); + url = header.to_string(); + continue; + } + + return Ok(response); + } + + Err(anyhow!("Followed 10 redirections")) +} + +pub(crate) fn get_client(socks5_config: Option) -> Result { + let builder = reqwest::ClientBuilder::new().timeout(HTTP_TIMEOUT); + let builder = if let Some(socks5_config) = socks5_config { + let proxy = reqwest::Proxy::all(socks5_config.to_url())?; + builder.proxy(proxy) + } else { + // Disable usage of "system" proxy configured via environment variables. + // It is enabled by default in `reqwest`, see + // + // for documentation. + builder.no_proxy() + }; + Ok(builder.build()?) +} diff --git a/src/oauth2.rs b/src/oauth2.rs index 2fe8795b4..f3e5dfe5e 100644 --- a/src/oauth2.rs +++ b/src/oauth2.rs @@ -160,7 +160,7 @@ pub(crate) async fn get_oauth2_access_token( // ... and POST let socks5_config = Socks5Config::from_database(&context.sql).await?; - let client = crate::http::get_client(socks5_config)?; + let client = crate::net::http::get_client(socks5_config)?; let response: Response = match client.post(post_url).form(&post_param).send().await { Ok(resp) => match resp.json().await { @@ -291,7 +291,7 @@ impl Oauth2 { // "picture": "https://lh4.googleusercontent.com/-Gj5jh_9R0BY/AAAAAAAAAAI/AAAAAAAAAAA/IAjtjfjtjNA/photo.jpg" // } let socks5_config = Socks5Config::from_database(&context.sql).await.ok()?; - let client = match crate::http::get_client(socks5_config) { + let client = match crate::net::http::get_client(socks5_config) { Ok(cl) => cl, Err(err) => { warn!(context, "failed to get HTTP client: {}", err); diff --git a/src/qr.rs b/src/qr.rs index 920776b5e..7bbe73ce6 100644 --- a/src/qr.rs +++ b/src/qr.rs @@ -534,7 +534,7 @@ struct CreateAccountErrorResponse { async fn set_account_from_qr(context: &Context, qr: &str) -> Result<()> { let url_str = &qr[DCACCOUNT_SCHEME.len()..]; let socks5_config = Socks5Config::from_database(&context.sql).await?; - let response = crate::http::get_client(socks5_config)? + let response = crate::net::http::get_client(socks5_config)? .post(url_str) .send() .await?; From f68a2fc387ba07d68ac7c7a3c8cc216172417968 Mon Sep 17 00:00:00 2001 From: link2xt Date: Wed, 19 Apr 2023 23:33:19 +0000 Subject: [PATCH 2/6] JSON-RPC: return mimetype and encoding for HTTP blobs --- CHANGELOG.md | 2 +- Cargo.lock | 5 ++-- Cargo.toml | 1 + deltachat-jsonrpc/src/api/mod.rs | 11 ++++---- deltachat-jsonrpc/src/api/types/http.rs | 29 ++++++++++++++++++++ deltachat-jsonrpc/src/api/types/mod.rs | 1 + src/net.rs | 2 +- src/net/http.rs | 36 +++++++++++++++++++++++-- 8 files changed, 75 insertions(+), 12 deletions(-) create mode 100644 deltachat-jsonrpc/src/api/types/http.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index b2e78974b..656f583af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ## Unreleased ### Changes -- Add `get_http_blob` JSON-RPC API. +- Add `get_http_response` JSON-RPC API. ## [1.112.7] - 2023-04-17 diff --git a/Cargo.lock b/Cargo.lock index 87198de03..1d9b43fb0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1080,6 +1080,7 @@ dependencies = [ "libc", "log", "mailparse", + "mime", "num-derive", "num-traits", "num_cpus", @@ -2591,9 +2592,9 @@ dependencies = [ [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "minimal-lexical" diff --git a/Cargo.toml b/Cargo.toml index cbd021e2c..e3519b421 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,6 +57,7 @@ kamadak-exif = "0.5" lettre_email = { git = "https://github.com/deltachat/lettre", branch = "master" } libc = "0.2" mailparse = "0.14" +mime = "0.3.17" num_cpus = "1.15" num-derive = "0.3" num-traits = "0.2" diff --git a/deltachat-jsonrpc/src/api/mod.rs b/deltachat-jsonrpc/src/api/mod.rs index de51d2b37..d161dcd48 100644 --- a/deltachat-jsonrpc/src/api/mod.rs +++ b/deltachat-jsonrpc/src/api/mod.rs @@ -43,6 +43,7 @@ use types::account::Account; use types::chat::FullChat; use types::chat_list::ChatListEntry; use types::contact::ContactObject; +use types::http::HttpResponse; use types::message::MessageData; use types::message::MessageObject; use types::provider_info::ProviderInfo; @@ -1610,15 +1611,13 @@ impl CommandApi { Ok(general_purpose::STANDARD_NO_PAD.encode(blob)) } - /// Makes an HTTP GET request and returns base64-encoded contents. + /// Makes an HTTP GET request and returns a response. /// /// `url` is the HTTP or HTTPS URL. - async fn get_http_blob(&self, account_id: u32, url: String) -> Result { + async fn get_http_response(&self, account_id: u32, url: String) -> Result { let ctx = self.get_context(account_id).await?; - let blob = deltachat::net::read_url_blob(&ctx, &url).await?; - - use base64::{engine::general_purpose, Engine as _}; - Ok(general_purpose::STANDARD_NO_PAD.encode(blob)) + let response = deltachat::net::read_url_blob(&ctx, &url).await?.into(); + Ok(response) } /// Forward messages to another chat. diff --git a/deltachat-jsonrpc/src/api/types/http.rs b/deltachat-jsonrpc/src/api/types/http.rs new file mode 100644 index 000000000..3b9d29509 --- /dev/null +++ b/deltachat-jsonrpc/src/api/types/http.rs @@ -0,0 +1,29 @@ +use deltachat::net::HttpResponse as CoreHttpResponse; +use serde::Serialize; +use typescript_type_def::TypeDef; + +#[derive(Serialize, TypeDef)] +pub struct HttpResponse { + /// base64-encoded response body. + blob: String, + + /// MIME type, e.g. "text/plain" or "text/html". + mimetype: Option, + + /// Encoding, e.g. "utf-8". + encoding: Option, +} + +impl From for HttpResponse { + fn from(response: CoreHttpResponse) -> Self { + use base64::{engine::general_purpose, Engine as _}; + let blob = general_purpose::STANDARD_NO_PAD.encode(response.blob); + let mimetype = response.mimetype; + let encoding = response.encoding; + HttpResponse { + blob, + mimetype, + encoding, + } + } +} diff --git a/deltachat-jsonrpc/src/api/types/mod.rs b/deltachat-jsonrpc/src/api/types/mod.rs index 2d783990e..ace49f14e 100644 --- a/deltachat-jsonrpc/src/api/types/mod.rs +++ b/deltachat-jsonrpc/src/api/types/mod.rs @@ -2,6 +2,7 @@ pub mod account; pub mod chat; pub mod chat_list; pub mod contact; +pub mod http; pub mod location; pub mod message; pub mod provider_info; diff --git a/src/net.rs b/src/net.rs index d4d529770..3e80c40e8 100644 --- a/src/net.rs +++ b/src/net.rs @@ -16,7 +16,7 @@ pub(crate) mod http; pub(crate) mod session; pub(crate) mod tls; -pub use http::{read_url, read_url_blob}; +pub use http::{read_url, read_url_blob, Response as HttpResponse}; async fn connect_tcp_inner(addr: SocketAddr, timeout_val: Duration) -> Result { let tcp_stream = timeout(timeout_val, TcpStream::connect(addr)) diff --git a/src/net/http.rs b/src/net/http.rs index 3dd49b40a..cbee57959 100644 --- a/src/net/http.rs +++ b/src/net/http.rs @@ -3,20 +3,52 @@ use std::time::Duration; use anyhow::{anyhow, Result}; +use mime::Mime; use crate::context::Context; use crate::socks::Socks5Config; const HTTP_TIMEOUT: Duration = Duration::from_secs(30); +/// HTTP(S) GET response. +#[derive(Debug)] +pub struct Response { + /// Response body. + pub blob: Vec, + + /// MIME type exntracted from the `Content-Type` header, if any. + pub mimetype: Option, + + /// Encoding extracted from the `Content-Type` header, if any. + pub encoding: Option, +} + /// Retrieves the text contents of URL using HTTP GET request. pub async fn read_url(context: &Context, url: &str) -> Result { Ok(read_url_inner(context, url).await?.text().await?) } /// Retrieves the binary contents of URL using HTTP GET request. -pub async fn read_url_blob(context: &Context, url: &str) -> Result> { - Ok(read_url_inner(context, url).await?.bytes().await?.into()) +pub async fn read_url_blob(context: &Context, url: &str) -> Result { + let response = read_url_inner(context, url).await?; + let content_type = response + .headers() + .get(reqwest::header::CONTENT_TYPE) + .and_then(|value| value.to_str().ok()) + .and_then(|value| value.parse::().ok()); + let mimetype = content_type + .as_ref() + .map(|mime| mime.essence_str().to_string()); + let encoding = content_type.as_ref().and_then(|mime| { + mime.get_param(mime::CHARSET) + .map(|charset| charset.as_str().to_string()) + }); + let blob: Vec = response.bytes().await?.into(); + Ok(Response { + blob, + mimetype, + encoding, + }) } async fn read_url_inner(context: &Context, url: &str) -> Result { From a8d4cbd5c15a019a0e23fa5caac22ba85cfad885 Mon Sep 17 00:00:00 2001 From: link2xt Date: Wed, 19 Apr 2023 19:38:37 +0000 Subject: [PATCH 3/6] Add C API to get HTTP responses --- CHANGELOG.md | 1 + deltachat-ffi/deltachat.h | 59 +++++++++++++++++++++++++- deltachat-ffi/src/lib.rs | 88 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 147 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 656f583af..88244f85d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Changes - Add `get_http_response` JSON-RPC API. +- Add C API to get HTTP responses. ## [1.112.7] - 2023-04-17 diff --git a/deltachat-ffi/deltachat.h b/deltachat-ffi/deltachat.h index c901ab738..b18d62296 100644 --- a/deltachat-ffi/deltachat.h +++ b/deltachat-ffi/deltachat.h @@ -25,6 +25,7 @@ typedef struct _dc_event dc_event_t; typedef struct _dc_event_emitter dc_event_emitter_t; typedef struct _dc_jsonrpc_instance dc_jsonrpc_instance_t; typedef struct _dc_backup_provider dc_backup_provider_t; +typedef struct _dc_http_response dc_http_response_t; // Alias for backwards compatibility, use dc_event_emitter_t instead. typedef struct _dc_event_emitter dc_accounts_event_emitter_t; @@ -5057,6 +5058,63 @@ int dc_provider_get_status (const dc_provider_t* prov void dc_provider_unref (dc_provider_t* provider); +/** + * Return an HTTP(S) GET response. + * This function can be used to download remote content for HTML emails. + * + * @memberof dc_context_t + * @param context The context object to take proxy settings from. + * @param url HTTP or HTTPS URL. + * @return The response must be released using dc_http_response_unref() after usage. + * NULL is returned on errors. + */ +dc_http_response_t* dc_get_http_response (const dc_context_t* context, const char* url); + +/** + * Returns HTTP response MIME type as a string, e.g. "text/plain" or "text/html". + * + * @memberof dc_http_response_t + * @param response HTTP response as returned by dc_get_http_response(). + * @return The string which must be released using dc_str_unref() after usage. May be NULL. + */ +char* dc_http_response_get_mimetype (const dc_http_response_t* response); + +/** + * Returns HTTP response encoding, e.g. "utf-8". + * + * @memberof dc_http_response_t + * @param response HTTP response as returned by dc_get_http_response(). + * @return The string which must be released using dc_str_unref() after usage. May be NULL. + */ +char* dc_http_response_get_encoding (const dc_http_response_t* response); + +/** + * Returns HTTP response contents. + * + * @memberof dc_http_response_t + * @param response HTTP response as returned by dc_get_http_response(). + * @return The blob which must be released using dc_str_unref() after usage. NULL is never returned. + */ +uint8_t* dc_http_response_get_blob (const dc_http_response_t* response); + +/** + * Returns HTTP response content size. + * + * @memberof dc_http_response_t + * @param response HTTP response as returned by dc_get_http_response(). + * @return The blob size. + */ +size_t dc_http_response_get_size (const dc_http_response_t* response); + +/** + * Free an HTTP response object. + * + * @memberof dc_http_response_t + * @param response HTTP response as returned by dc_get_http_response(). + */ +void dc_http_response_unref (const dc_http_response_t* response); + + /** * @class dc_lot_t * @@ -5534,7 +5592,6 @@ void dc_reactions_unref (dc_reactions_t* reactions); */ - /** * @class dc_jsonrpc_instance_t * diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index 968616a5d..23dc6de5c 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -31,6 +31,7 @@ use deltachat::ephemeral::Timer as EphemeralTimer; use deltachat::imex::BackupProvider; use deltachat::key::DcKey; use deltachat::message::MsgId; +use deltachat::net::read_url_blob; use deltachat::qr_code_generator::{generate_backup_qr, get_securejoin_qr_svg}; use deltachat::reaction::{get_msg_reactions, send_reaction, Reactions}; use deltachat::stock_str::StockMessage; @@ -4467,6 +4468,93 @@ pub unsafe extern "C" fn dc_provider_unref(provider: *mut dc_provider_t) { // this may change once we start localizing string. } +// dc_http_response_t + +pub type dc_http_response_t = net::HttpResponse; + +#[no_mangle] +pub unsafe extern "C" fn dc_get_http_response( + context: *const dc_context_t, + url: *const libc::c_char, +) -> *mut dc_http_response_t { + if context.is_null() || url.is_null() { + eprintln!("ignoring careless call to dc_get_http_response()"); + return ptr::null_mut(); + } + + let context = &*context; + let url = to_string_lossy(url); + if let Ok(response) = block_on(read_url_blob(context, &url)).log_err(context, "read_url_blob") { + Box::into_raw(Box::new(response)) + } else { + ptr::null_mut() + } +} + +#[no_mangle] +pub unsafe extern "C" fn dc_http_response_get_mimetype( + response: *const dc_http_response_t, +) -> *mut libc::c_char { + if response.is_null() { + eprintln!("ignoring careless call to dc_http_response_get_mimetype()"); + return ptr::null_mut(); + } + + let response = &*response; + response.mimetype.strdup() +} + +#[no_mangle] +pub unsafe extern "C" fn dc_http_response_get_encoding( + response: *const dc_http_response_t, +) -> *mut libc::c_char { + if response.is_null() { + eprintln!("ignoring careless call to dc_http_response_get_encoding()"); + return ptr::null_mut(); + } + + let response = &*response; + response.encoding.strdup() +} + +#[no_mangle] +pub unsafe extern "C" fn dc_http_response_get_blob( + response: *const dc_http_response_t, +) -> *mut libc::c_char { + if response.is_null() { + eprintln!("ignoring careless call to dc_http_response_get_blob()"); + return ptr::null_mut(); + } + + let response = &*response; + let blob_len = response.blob.len(); + let ptr = libc::malloc(blob_len); + libc::memcpy(ptr, response.blob.as_ptr() as *mut libc::c_void, blob_len); + ptr as *mut libc::c_char +} + +#[no_mangle] +pub unsafe extern "C" fn dc_http_response_get_size( + response: *const dc_http_response_t, +) -> libc::size_t { + if response.is_null() { + eprintln!("ignoring careless call to dc_http_response_get_size()"); + return 0; + } + + let response = &*response; + response.blob.len() +} + +#[no_mangle] +pub unsafe extern "C" fn dc_http_response_unref(response: *mut dc_http_response_t) { + if response.is_null() { + eprintln!("ignoring careless call to dc_http_response_unref()"); + return; + } + drop(Box::from_raw(response)); +} + // -- Accounts /// Reader-writer lock wrapper for accounts manager to guarantee thread safety when using From 0e9f8c47261e2084eac7b545edddca050bf28cfd Mon Sep 17 00:00:00 2001 From: "B. Petersen" Date: Thu, 20 Apr 2023 15:52:54 +0200 Subject: [PATCH 4/6] describe dc_http_response_t, needed for doxygen's @memberof --- deltachat-ffi/deltachat.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/deltachat-ffi/deltachat.h b/deltachat-ffi/deltachat.h index b18d62296..a08067b84 100644 --- a/deltachat-ffi/deltachat.h +++ b/deltachat-ffi/deltachat.h @@ -5070,6 +5070,15 @@ void dc_provider_unref (dc_provider_t* provider); */ dc_http_response_t* dc_get_http_response (const dc_context_t* context, const char* url); + +/** + * @class dc_http_response_t + * + * An object containing an HTTP(S) GET response. + * Created by dc_get_http_response(). + */ + + /** * Returns HTTP response MIME type as a string, e.g. "text/plain" or "text/html". * From fa95b269a5b8cbdc22f4dd87b4201764dedff379 Mon Sep 17 00:00:00 2001 From: link2xt Date: Thu, 20 Apr 2023 14:03:47 +0000 Subject: [PATCH 5/6] Backport new set_core_version.py --- scripts/set_core_version.py | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/scripts/set_core_version.py b/scripts/set_core_version.py index 7bc888d1b..cd6303cc9 100755 --- a/scripts/set_core_version.py +++ b/scripts/set_core_version.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 +import datetime import json import os import pathlib @@ -56,7 +57,8 @@ def update_package_json(relpath, newversion): json_data = json.loads(f.read()) json_data["version"] = newversion with open(p, "w") as f: - f.write(json.dumps(json_data, sort_keys=True, indent=2)) + json.dump(json_data, f, sort_keys=True, indent=2) + f.write("\n") def main(): @@ -90,15 +92,25 @@ def main(): ffi_toml = read_toml_version("deltachat-ffi/Cargo.toml") assert core_toml == ffi_toml, (core_toml, ffi_toml) + today = datetime.date.today().isoformat() + if "alpha" not in newversion: - for line in open("CHANGELOG.md"): + changelog_name = "CHANGELOG.md" + changelog_tmpname = changelog_name + ".tmp" + changelog_tmp = open(changelog_tmpname, "w") + found = False + for line in open(changelog_name): ## 1.25.0 - if line.startswith("## [") and line[4:].strip().startswith(newversion): - break - else: + if line == f"## [{newversion}]\n": + line = f"## [{newversion}] - {today}\n" + found = True + changelog_tmp.write(line) + if not found: raise SystemExit( - f"CHANGELOG.md contains no entry for version: {newversion}" + f"{changelog_name} contains no entry for version: {newversion}" ) + changelog_tmp.close() + os.rename(changelog_tmpname, changelog_name) for toml_filename in toml_list: replace_toml_version(toml_filename, newversion) @@ -106,6 +118,9 @@ def main(): for json_filename in json_list: update_package_json(json_filename, newversion) + with open("release-date.in", "w") as f: + f.write(today) + print("running cargo check") subprocess.call(["cargo", "check"]) From 2701c135db732f7c9f7f6770d8aa0def1d7cf121 Mon Sep 17 00:00:00 2001 From: link2xt Date: Thu, 20 Apr 2023 14:10:34 +0000 Subject: [PATCH 6/6] Prepare 1.112.8 --- CHANGELOG.md | 2 +- Cargo.lock | 10 +++++----- Cargo.toml | 2 +- deltachat-ffi/Cargo.toml | 2 +- deltachat-jsonrpc/Cargo.toml | 2 +- deltachat-jsonrpc/typescript/package.json | 4 ++-- deltachat-repl/Cargo.toml | 2 +- deltachat-rpc-server/Cargo.toml | 2 +- package.json | 4 ++-- release-date.in | 1 + 10 files changed, 16 insertions(+), 15 deletions(-) create mode 100644 release-date.in diff --git a/CHANGELOG.md b/CHANGELOG.md index 88244f85d..00c290869 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## [1.112.8] - 2023-04-20 ### Changes - Add `get_http_response` JSON-RPC API. diff --git a/Cargo.lock b/Cargo.lock index 1d9b43fb0..56a68e36d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1049,7 +1049,7 @@ dependencies = [ [[package]] name = "deltachat" -version = "1.112.7" +version = "1.112.8" dependencies = [ "ansi_term", "anyhow", @@ -1124,7 +1124,7 @@ dependencies = [ [[package]] name = "deltachat-jsonrpc" -version = "1.112.7" +version = "1.112.8" dependencies = [ "anyhow", "async-channel", @@ -1147,7 +1147,7 @@ dependencies = [ [[package]] name = "deltachat-repl" -version = "1.112.7" +version = "1.112.8" dependencies = [ "ansi_term", "anyhow", @@ -1162,7 +1162,7 @@ dependencies = [ [[package]] name = "deltachat-rpc-server" -version = "1.112.7" +version = "1.112.8" dependencies = [ "anyhow", "deltachat", @@ -1186,7 +1186,7 @@ dependencies = [ [[package]] name = "deltachat_ffi" -version = "1.112.7" +version = "1.112.8" dependencies = [ "anyhow", "deltachat", diff --git a/Cargo.toml b/Cargo.toml index e3519b421..971208643 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "deltachat" -version = "1.112.7" +version = "1.112.8" edition = "2021" license = "MPL-2.0" rust-version = "1.64" diff --git a/deltachat-ffi/Cargo.toml b/deltachat-ffi/Cargo.toml index 674dbbe93..bf7c46d26 100644 --- a/deltachat-ffi/Cargo.toml +++ b/deltachat-ffi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "deltachat_ffi" -version = "1.112.7" +version = "1.112.8" description = "Deltachat FFI" edition = "2018" readme = "README.md" diff --git a/deltachat-jsonrpc/Cargo.toml b/deltachat-jsonrpc/Cargo.toml index 0007a7b60..7947619da 100644 --- a/deltachat-jsonrpc/Cargo.toml +++ b/deltachat-jsonrpc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "deltachat-jsonrpc" -version = "1.112.7" +version = "1.112.8" description = "DeltaChat JSON-RPC API" edition = "2021" default-run = "deltachat-jsonrpc-server" diff --git a/deltachat-jsonrpc/typescript/package.json b/deltachat-jsonrpc/typescript/package.json index 7409bbe60..8a266051d 100644 --- a/deltachat-jsonrpc/typescript/package.json +++ b/deltachat-jsonrpc/typescript/package.json @@ -55,5 +55,5 @@ }, "type": "module", "types": "dist/deltachat.d.ts", - "version": "1.112.7" -} \ No newline at end of file + "version": "1.112.8" +} diff --git a/deltachat-repl/Cargo.toml b/deltachat-repl/Cargo.toml index 4546ec4b9..5632d5cd2 100644 --- a/deltachat-repl/Cargo.toml +++ b/deltachat-repl/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "deltachat-repl" -version = "1.112.7" +version = "1.112.8" license = "MPL-2.0" edition = "2021" diff --git a/deltachat-rpc-server/Cargo.toml b/deltachat-rpc-server/Cargo.toml index fb94b10bf..adf829c87 100644 --- a/deltachat-rpc-server/Cargo.toml +++ b/deltachat-rpc-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "deltachat-rpc-server" -version = "1.112.7" +version = "1.112.8" description = "DeltaChat JSON-RPC server" edition = "2021" readme = "README.md" diff --git a/package.json b/package.json index d4be1a946..14bee1111 100644 --- a/package.json +++ b/package.json @@ -60,5 +60,5 @@ "test:mocha": "mocha -r esm node/test/test.js --growl --reporter=spec --bail --exit" }, "types": "node/dist/index.d.ts", - "version": "1.112.7" -} \ No newline at end of file + "version": "1.112.8" +} diff --git a/release-date.in b/release-date.in new file mode 100644 index 000000000..c0762cd07 --- /dev/null +++ b/release-date.in @@ -0,0 +1 @@ +2023-04-20 \ No newline at end of file