mirror of
https://github.com/chatmail/core.git
synced 2026-05-08 17:36:29 +03:00
fix: normalize proxy URLs before saving into proxy_url
This commit is contained in:
@@ -4,12 +4,16 @@
|
|||||||
|
|
||||||
use deltachat_derive::{FromSql, ToSql};
|
use deltachat_derive::{FromSql, ToSql};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
use percent_encoding::{AsciiSet, NON_ALPHANUMERIC};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::chat::ChatId;
|
use crate::chat::ChatId;
|
||||||
|
|
||||||
pub static DC_VERSION_STR: Lazy<String> = Lazy::new(|| env!("CARGO_PKG_VERSION").to_string());
|
pub static DC_VERSION_STR: Lazy<String> = Lazy::new(|| env!("CARGO_PKG_VERSION").to_string());
|
||||||
|
|
||||||
|
/// Set of characters to percent-encode in email addresses and names.
|
||||||
|
pub(crate) const NON_ALPHANUMERIC_WITHOUT_DOT: &AsciiSet = &NON_ALPHANUMERIC.remove(b'.');
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug,
|
Debug,
|
||||||
Default,
|
Default,
|
||||||
|
|||||||
@@ -12,13 +12,14 @@ use fast_socks5::client::Socks5Stream;
|
|||||||
use fast_socks5::util::target_addr::ToTargetAddr;
|
use fast_socks5::util::target_addr::ToTargetAddr;
|
||||||
use fast_socks5::AuthenticationMethod;
|
use fast_socks5::AuthenticationMethod;
|
||||||
use fast_socks5::Socks5Command;
|
use fast_socks5::Socks5Command;
|
||||||
use percent_encoding::{percent_encode, NON_ALPHANUMERIC};
|
use percent_encoding::{percent_encode, utf8_percent_encode, NON_ALPHANUMERIC};
|
||||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
use tokio_io_timeout::TimeoutStream;
|
use tokio_io_timeout::TimeoutStream;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
|
use crate::constants::NON_ALPHANUMERIC_WITHOUT_DOT;
|
||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
use crate::net::connect_tcp;
|
use crate::net::connect_tcp;
|
||||||
use crate::net::session::SessionStream;
|
use crate::net::session::SessionStream;
|
||||||
@@ -41,6 +42,12 @@ impl PartialEq for ShadowsocksConfig {
|
|||||||
|
|
||||||
impl Eq for ShadowsocksConfig {}
|
impl Eq for ShadowsocksConfig {}
|
||||||
|
|
||||||
|
impl ShadowsocksConfig {
|
||||||
|
fn to_url(&self) -> String {
|
||||||
|
self.server_config.to_url()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct HttpConfig {
|
pub struct HttpConfig {
|
||||||
/// HTTP proxy host.
|
/// HTTP proxy host.
|
||||||
@@ -84,6 +91,17 @@ impl HttpConfig {
|
|||||||
};
|
};
|
||||||
Ok(http_config)
|
Ok(http_config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn to_url(&self, scheme: &str) -> String {
|
||||||
|
let host = utf8_percent_encode(&self.host, NON_ALPHANUMERIC_WITHOUT_DOT);
|
||||||
|
if let Some((user, password)) = &self.user_password {
|
||||||
|
let user = utf8_percent_encode(user, NON_ALPHANUMERIC);
|
||||||
|
let password = utf8_percent_encode(password, NON_ALPHANUMERIC);
|
||||||
|
format!("{scheme}://{user}:{password}@{host}:{}", self.port)
|
||||||
|
} else {
|
||||||
|
format!("{scheme}://{host}:{}", self.port)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
@@ -123,6 +141,17 @@ impl Socks5Config {
|
|||||||
|
|
||||||
Ok(socks_stream)
|
Ok(socks_stream)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn to_url(&self) -> String {
|
||||||
|
let host = utf8_percent_encode(&self.host, NON_ALPHANUMERIC_WITHOUT_DOT);
|
||||||
|
if let Some((user, password)) = &self.user_password {
|
||||||
|
let user = utf8_percent_encode(user, NON_ALPHANUMERIC);
|
||||||
|
let password = utf8_percent_encode(password, NON_ALPHANUMERIC);
|
||||||
|
format!("socks5://{user}:{password}@{host}:{}", self.port)
|
||||||
|
} else {
|
||||||
|
format!("socks5://{host}:{}", self.port)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
@@ -217,7 +246,7 @@ where
|
|||||||
|
|
||||||
impl ProxyConfig {
|
impl ProxyConfig {
|
||||||
/// Creates a new proxy configuration by parsing given proxy URL.
|
/// Creates a new proxy configuration by parsing given proxy URL.
|
||||||
fn from_url(url: &str) -> Result<Self> {
|
pub(crate) fn from_url(url: &str) -> Result<Self> {
|
||||||
let url = Url::parse(url).context("Cannot parse proxy URL")?;
|
let url = Url::parse(url).context("Cannot parse proxy URL")?;
|
||||||
match url.scheme() {
|
match url.scheme() {
|
||||||
"http" => {
|
"http" => {
|
||||||
@@ -272,6 +301,19 @@ impl ProxyConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Serializes proxy config into an URL.
|
||||||
|
///
|
||||||
|
/// This function can be used to normalize proxy URL
|
||||||
|
/// by parsing it and serializing back.
|
||||||
|
pub(crate) fn to_url(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Self::Http(http_config) => http_config.to_url("http"),
|
||||||
|
Self::Https(http_config) => http_config.to_url("https"),
|
||||||
|
Self::Socks5(socks5_config) => socks5_config.to_url(),
|
||||||
|
Self::Shadowsocks(shadowsocks_config) => shadowsocks_config.to_url(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Migrates legacy `socks5_host`, `socks5_port`, `socks5_user` and `socks5_password`
|
/// Migrates legacy `socks5_host`, `socks5_port`, `socks5_user` and `socks5_password`
|
||||||
/// config into `proxy_url` if `proxy_url` is unset or empty.
|
/// config into `proxy_url` if `proxy_url` is unset or empty.
|
||||||
///
|
///
|
||||||
|
|||||||
17
src/qr.rs
17
src/qr.rs
@@ -20,7 +20,7 @@ use crate::events::EventType;
|
|||||||
use crate::key::Fingerprint;
|
use crate::key::Fingerprint;
|
||||||
use crate::message::Message;
|
use crate::message::Message;
|
||||||
use crate::net::http::post_empty;
|
use crate::net::http::post_empty;
|
||||||
use crate::net::proxy::DEFAULT_SOCKS_PORT;
|
use crate::net::proxy::{ProxyConfig, DEFAULT_SOCKS_PORT};
|
||||||
use crate::peerstate::Peerstate;
|
use crate::peerstate::Peerstate;
|
||||||
use crate::token;
|
use crate::token;
|
||||||
use crate::tools::validate_id;
|
use crate::tools::validate_id;
|
||||||
@@ -723,6 +723,10 @@ pub async fn set_config_from_qr(context: &Context, qr: &str) -> Result<()> {
|
|||||||
.get_config(Config::ProxyUrl)
|
.get_config(Config::ProxyUrl)
|
||||||
.await?
|
.await?
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
// Normalize the URL.
|
||||||
|
let url = ProxyConfig::from_url(&url)?.to_url();
|
||||||
|
|
||||||
let proxy_urls: Vec<&str> = std::iter::once(url.as_str())
|
let proxy_urls: Vec<&str> = std::iter::once(url.as_str())
|
||||||
.chain(
|
.chain(
|
||||||
old_proxy_url_value
|
old_proxy_url_value
|
||||||
@@ -1787,6 +1791,17 @@ mod tests {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// SOCKS5 config does not have port 1080 explicitly specified,
|
||||||
|
// but should bring `socks5://1.2.3.4:1080` to the top instead of creating another entry.
|
||||||
|
set_config_from_qr(&t, "socks5://1.2.3.4").await?;
|
||||||
|
assert_eq!(
|
||||||
|
t.get_config(Config::ProxyUrl).await?,
|
||||||
|
Some(
|
||||||
|
"socks5://1.2.3.4:1080\nss://YWVzLTEyOC1nY206dGVzdA@192.168.100.1:8888#Example1\nsocks5://foo:666\nsocks5://Da:x%26%25%24X@jau:1080"
|
||||||
|
.to_string()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
//! Implementation of [SecureJoin protocols](https://securejoin.delta.chat/).
|
//! Implementation of [SecureJoin protocols](https://securejoin.delta.chat/).
|
||||||
|
|
||||||
use anyhow::{ensure, Context as _, Error, Result};
|
use anyhow::{ensure, Context as _, Error, Result};
|
||||||
use percent_encoding::{utf8_percent_encode, AsciiSet, NON_ALPHANUMERIC};
|
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
|
||||||
|
|
||||||
use crate::aheader::EncryptPreference;
|
use crate::aheader::EncryptPreference;
|
||||||
use crate::chat::{self, get_chat_id_by_grpid, Chat, ChatId, ChatIdBlocked, ProtectionStatus};
|
use crate::chat::{self, get_chat_id_by_grpid, Chat, ChatId, ChatIdBlocked, ProtectionStatus};
|
||||||
use crate::chatlist_events;
|
use crate::chatlist_events;
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::constants::{Blocked, Chattype};
|
use crate::constants::{Blocked, Chattype, NON_ALPHANUMERIC_WITHOUT_DOT};
|
||||||
use crate::contact::{Contact, ContactId, Origin};
|
use crate::contact::{Contact, ContactId, Origin};
|
||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
use crate::e2ee::ensure_secret_key_exists;
|
use crate::e2ee::ensure_secret_key_exists;
|
||||||
@@ -34,9 +34,6 @@ use qrinvite::QrInvite;
|
|||||||
|
|
||||||
use crate::token::Namespace;
|
use crate::token::Namespace;
|
||||||
|
|
||||||
/// Set of characters to percent-encode in email addresses and names.
|
|
||||||
pub const NON_ALPHANUMERIC_WITHOUT_DOT: &AsciiSet = &NON_ALPHANUMERIC.remove(b'.');
|
|
||||||
|
|
||||||
fn inviter_progress(context: &Context, contact_id: ContactId, progress: usize) {
|
fn inviter_progress(context: &Context, contact_id: ContactId, progress: usize) {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
progress <= 1000,
|
progress <= 1000,
|
||||||
|
|||||||
Reference in New Issue
Block a user