mirror of
https://github.com/chatmail/core.git
synced 2026-04-11 01:52:11 +03:00
Compare commits
3 Commits
provider_j
...
fix1079
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3800bb08e | ||
|
|
ec40dd1b6f | ||
|
|
f2f8898004 |
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -716,13 +716,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat-provider-database"
|
||||
version = "0.3.0"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -739,7 +738,7 @@ name = "deltachat_ffi"
|
||||
version = "1.0.0-beta.20"
|
||||
dependencies = [
|
||||
"deltachat 1.0.0-beta.20",
|
||||
"deltachat-provider-database 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"deltachat-provider-database 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"human-panic 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -3535,7 +3534,7 @@ dependencies = [
|
||||
"checksum darling_macro 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72"
|
||||
"checksum debug_stub_derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "496b7f8a2f853313c3ca370641d7ff3e42c32974fdccda8f0684599ed0a3ff6b"
|
||||
"checksum deflate 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)" = "707b6a7b384888a70c8d2e8650b3e60170dfc6a67bb4aa67b6dfca57af4bedb4"
|
||||
"checksum deltachat-provider-database 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc18fb898a7bc90565c801d7bd17a4b61478f0da0ea06ecf7f0428bd20242c52"
|
||||
"checksum deltachat-provider-database 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "814dba060d9fdc7a989fccdc4810ada9d1c7a1f09131c78e42412bc6c634b93b"
|
||||
"checksum derive_builder 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a2658621297f2cf68762a6f7dc0bb7e1ff2cfd6583daef8ee0fed6f7ec468ec0"
|
||||
"checksum derive_builder_core 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2791ea3e372c8495c0bc2033991d76b512cd799d07491fbd6890124db9458bef"
|
||||
"checksum des 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "74ba5f1b5aee9772379c2670ba81306e65a93c0ee3caade7a1d22b188d88a3af"
|
||||
|
||||
@@ -16,7 +16,7 @@ crate-type = ["cdylib", "staticlib"]
|
||||
|
||||
[dependencies]
|
||||
deltachat = { path = "../", default-features = false }
|
||||
deltachat-provider-database = "0.3.0"
|
||||
deltachat-provider-database = "0.2.1"
|
||||
libc = "0.2"
|
||||
human-panic = "1.0.1"
|
||||
num-traits = "0.2.6"
|
||||
|
||||
@@ -18,6 +18,7 @@ typedef struct _dc_chat dc_chat_t;
|
||||
typedef struct _dc_msg dc_msg_t;
|
||||
typedef struct _dc_contact dc_contact_t;
|
||||
typedef struct _dc_lot dc_lot_t;
|
||||
typedef struct _dc_provider dc_provider_t;
|
||||
|
||||
|
||||
/**
|
||||
@@ -3659,30 +3660,106 @@ int dc_contact_is_verified (dc_contact_t* contact);
|
||||
|
||||
/**
|
||||
* @class dc_provider_t
|
||||
*
|
||||
* Opaque object containing information about one single email provider.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Get the provider json object for the given domain.
|
||||
* For more documentation see https://docs.rs/deltachat-provider-database/
|
||||
* Create a provider struct for the given domain.
|
||||
*
|
||||
* @memberof dc_provider_t
|
||||
* @param domain The domain to get provider info for.
|
||||
* @return a provider json object as string. If no provider info is found, an empty string will be returned.
|
||||
* @return a dc_provider_t struct which can be used with the dc_provider_get_*
|
||||
* accessor functions. If no provider info is found, NULL will be
|
||||
* returned.
|
||||
*/
|
||||
char* dc_provider_json_from_domain (const char* domain);
|
||||
dc_provider_t* dc_provider_new_from_domain (const char* domain);
|
||||
|
||||
|
||||
/**
|
||||
* Get the provider json object for the given email address.
|
||||
* For more documentation see https://docs.rs/deltachat-provider-database/
|
||||
* Create a provider struct for the given email address.
|
||||
*
|
||||
* The provider is extracted from the email address and it's information is returned.
|
||||
*
|
||||
* @memberof dc_provider_t
|
||||
* @param email The user's email address to extract the provider info form.
|
||||
* @return a provider json object as string. If no provider info is found, an empty string will be returned.
|
||||
* @return a dc_provider_t struct which can be used with the dc_provider_get_*
|
||||
* accessor functions. If no provider info is found, NULL will be
|
||||
* returned.
|
||||
*/
|
||||
char* dc_provider_json_from_email (const char* email);
|
||||
dc_provider_t* dc_provider_new_from_email (const char* email);
|
||||
|
||||
|
||||
/**
|
||||
* URL of the overview page.
|
||||
*
|
||||
* This URL allows linking to the providers page on providers.delta.chat.
|
||||
*
|
||||
* @memberof dc_provider_t
|
||||
* @param provider The dc_provider_t struct.
|
||||
* @return A string which must be released using dc_str_unref().
|
||||
*/
|
||||
char* dc_provider_get_overview_page (const dc_provider_t* provider);
|
||||
|
||||
|
||||
/**
|
||||
* The provider's name.
|
||||
*
|
||||
* The name of the provider, e.g. "POSTEO".
|
||||
*
|
||||
* @memberof dc_provider_t
|
||||
* @param provider The dc_provider_t struct.
|
||||
* @return A string which must be released using dc_str_unref().
|
||||
*/
|
||||
char* dc_provider_get_name (const dc_provider_t* provider);
|
||||
|
||||
|
||||
/**
|
||||
* The markdown content of the providers page.
|
||||
*
|
||||
* This contains the preparation steps or additional information if the status
|
||||
* is @ref DC_PROVIDER_STATUS_BROKEN.
|
||||
*
|
||||
* @memberof dc_provider_t
|
||||
* @param provider The dc_provider_t struct.
|
||||
* @return A string which must be released using dc_str_unref().
|
||||
*/
|
||||
char* dc_provider_get_markdown (const dc_provider_t* provider);
|
||||
|
||||
|
||||
/**
|
||||
* Date of when the state was last checked/updated.
|
||||
*
|
||||
* This is returned as a string.
|
||||
*
|
||||
* @memberof dc_provider_t
|
||||
* @param provider The dc_provider_t struct.
|
||||
* @return A string which must be released using dc_str_unref().
|
||||
*/
|
||||
char* dc_provider_get_status_date (const dc_provider_t* provider);
|
||||
|
||||
|
||||
/**
|
||||
* Whether DC works with this provider.
|
||||
*
|
||||
* Can be one of @ref DC_PROVIDER_STATUS_OK, @ref
|
||||
* DC_PROVIDER_STATUS_PREPARATION and @ref DC_PROVIDER_STATUS_BROKEN.
|
||||
*
|
||||
* @memberof dc_provider_t
|
||||
* @param provider The dc_provider_t struct.
|
||||
* @return The status as a constant number.
|
||||
*/
|
||||
int dc_provider_get_status (const dc_provider_t* provider);
|
||||
|
||||
|
||||
/**
|
||||
* Free the provider info struct.
|
||||
*
|
||||
* @memberof dc_provider_t
|
||||
* @param provider The dc_provider_t struct.
|
||||
*/
|
||||
void dc_provider_unref (const dc_provider_t* provider);
|
||||
|
||||
|
||||
/**
|
||||
@@ -4398,6 +4475,41 @@ void dc_array_add_id (dc_array_t*, uint32_t); // depreca
|
||||
#define DC_SHOW_EMAILS_ALL 2
|
||||
|
||||
|
||||
/**
|
||||
* @defgroup DC_PROVIDER_STATUS DC_PROVIDER_STATUS
|
||||
*
|
||||
* These constants are used as return values for dc_provider_get_status().
|
||||
*
|
||||
* @addtogroup DC_PROVIDER_STATUS
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provider status returned by dc_provider_get_status().
|
||||
*
|
||||
* Works right out of the box without any preperation steps needed
|
||||
*/
|
||||
#define DC_PROVIDER_STATUS_OK 1
|
||||
|
||||
/**
|
||||
* Provider status returned by dc_provider_get_status().
|
||||
*
|
||||
* Works, but preparation steps are needed
|
||||
*/
|
||||
#define DC_PROVIDER_STATUS_PREPARATION 2
|
||||
|
||||
/**
|
||||
* Provider status returned by dc_provider_get_status().
|
||||
*
|
||||
* Doesn't work (too unstable to use falls also in this category)
|
||||
*/
|
||||
#define DC_PROVIDER_STATUS_BROKEN 3
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* TODO: Strings need some doumentation about used placeholders.
|
||||
*
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#[macro_use]
|
||||
extern crate human_panic;
|
||||
extern crate num_traits;
|
||||
extern crate serde_json;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryInto;
|
||||
@@ -2390,12 +2391,27 @@ pub unsafe extern "C" fn dc_chat_get_info_json(
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| match chat::get_info_json(ctx, chat_id) {
|
||||
Ok(s) => s.strdup(),
|
||||
Err(err) => {
|
||||
error!(ctx, "get_info_json({}) returned: {}", chat_id, err);
|
||||
return "".strdup();
|
||||
}
|
||||
.with_inner(|ctx| {
|
||||
let chat = match chat::Chat::load_from_db(ctx, chat_id) {
|
||||
Ok(chat) => chat,
|
||||
Err(err) => {
|
||||
error!(ctx, "dc_get_chat_info_json() failed to load chat: {}", err);
|
||||
return "".strdup();
|
||||
}
|
||||
};
|
||||
let info = match chat.get_info(ctx) {
|
||||
Ok(info) => info,
|
||||
Err(err) => {
|
||||
error!(
|
||||
ctx,
|
||||
"dc_get_chat_info_json() failed to get chat info: {}", err
|
||||
);
|
||||
return "".strdup();
|
||||
}
|
||||
};
|
||||
serde_json::to_string(&info)
|
||||
.unwrap_or_log_default(ctx, "dc_get_chat_info_json() failed to serialise to json")
|
||||
.strdup()
|
||||
})
|
||||
.unwrap_or_else(|_| "".strdup())
|
||||
}
|
||||
|
||||
@@ -1,35 +1,93 @@
|
||||
extern crate deltachat_provider_database;
|
||||
|
||||
use std::ptr;
|
||||
|
||||
use crate::string::{to_string_lossy, StrExt};
|
||||
use deltachat_provider_database::StatusState;
|
||||
|
||||
#[no_mangle]
|
||||
pub type dc_provider_t = deltachat_provider_database::Provider;
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_provider_json_from_domain(
|
||||
pub unsafe extern "C" fn dc_provider_new_from_domain(
|
||||
domain: *const libc::c_char,
|
||||
) -> *mut libc::c_char {
|
||||
let domain = to_string_lossy(domain);
|
||||
match deltachat_provider_database::get_provider_info(&domain) {
|
||||
Some(provider) => serde_json::to_string(provider)
|
||||
.unwrap_or("".to_owned())
|
||||
.strdup(),
|
||||
None => "".strdup(),
|
||||
) -> *const dc_provider_t {
|
||||
match deltachat_provider_database::get_provider_info(&to_string_lossy(domain)) {
|
||||
Some(provider) => provider,
|
||||
None => ptr::null(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_provider_json_from_email(
|
||||
pub unsafe extern "C" fn dc_provider_new_from_email(
|
||||
email: *const libc::c_char,
|
||||
) -> *mut libc::c_char {
|
||||
) -> *const dc_provider_t {
|
||||
let email = to_string_lossy(email);
|
||||
let domain = deltachat_provider_database::get_domain_from_email(&email);
|
||||
match deltachat_provider_database::get_provider_info(domain) {
|
||||
Some(provider) => serde_json::to_string(provider)
|
||||
.unwrap_or("".to_owned())
|
||||
.strdup(),
|
||||
None => "".strdup(),
|
||||
Some(provider) => provider,
|
||||
None => ptr::null(),
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! null_guard {
|
||||
($context:tt) => {
|
||||
if $context.is_null() {
|
||||
return ptr::null_mut() as *mut libc::c_char;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_provider_get_overview_page(
|
||||
provider: *const dc_provider_t,
|
||||
) -> *mut libc::c_char {
|
||||
null_guard!(provider);
|
||||
format!(
|
||||
"{}/{}",
|
||||
deltachat_provider_database::PROVIDER_OVERVIEW_URL,
|
||||
(*provider).overview_page
|
||||
)
|
||||
.strdup()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_provider_get_name(provider: *const dc_provider_t) -> *mut libc::c_char {
|
||||
null_guard!(provider);
|
||||
(*provider).name.strdup()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_provider_get_markdown(
|
||||
provider: *const dc_provider_t,
|
||||
) -> *mut libc::c_char {
|
||||
null_guard!(provider);
|
||||
(*provider).markdown.strdup()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_provider_get_status_date(
|
||||
provider: *const dc_provider_t,
|
||||
) -> *mut libc::c_char {
|
||||
null_guard!(provider);
|
||||
(*provider).status.date.strdup()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_provider_get_status(provider: *const dc_provider_t) -> u32 {
|
||||
if provider.is_null() {
|
||||
return 0;
|
||||
}
|
||||
match (*provider).status.state {
|
||||
StatusState::OK => 1,
|
||||
StatusState::PREPARATION => 2,
|
||||
StatusState::BROKEN => 3,
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_provider_unref(_provider: *const dc_provider_t) {
|
||||
()
|
||||
}
|
||||
|
||||
// TODO expose general provider overview url?
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
"""Provider info class."""
|
||||
|
||||
from .capi import lib
|
||||
from .capi import ffi, lib
|
||||
from .cutil import as_dc_charpointer, from_dc_charpointer
|
||||
import json
|
||||
|
||||
|
||||
class ProviderNotFoundError(Exception):
|
||||
@@ -17,12 +16,13 @@ class Provider(object):
|
||||
"""
|
||||
|
||||
def __init__(self, domain):
|
||||
provider = from_dc_charpointer(
|
||||
lib.dc_provider_json_from_domain(as_dc_charpointer(domain))
|
||||
provider = ffi.gc(
|
||||
lib.dc_provider_new_from_domain(as_dc_charpointer(domain)),
|
||||
lib.dc_provider_unref,
|
||||
)
|
||||
if provider == "":
|
||||
if provider == ffi.NULL:
|
||||
raise ProviderNotFoundError("Provider not found")
|
||||
self._provider = json.loads(provider)
|
||||
self._provider = provider
|
||||
|
||||
@classmethod
|
||||
def from_email(cls, email):
|
||||
@@ -35,30 +35,33 @@ class Provider(object):
|
||||
@property
|
||||
def overview_page(self):
|
||||
"""URL to the overview page of the provider on providers.delta.chat."""
|
||||
return "https://providers.delta.chat/" + self._provider['overview_page']
|
||||
return from_dc_charpointer(
|
||||
lib.dc_provider_get_overview_page(self._provider))
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""The name of the provider."""
|
||||
return self._provider['name']
|
||||
return from_dc_charpointer(lib.dc_provider_get_name(self._provider))
|
||||
|
||||
@property
|
||||
def markdown(self):
|
||||
"""Content of the information page, formatted as markdown."""
|
||||
return self._provider['markdown']
|
||||
return from_dc_charpointer(
|
||||
lib.dc_provider_get_markdown(self._provider))
|
||||
|
||||
@property
|
||||
def status_date(self):
|
||||
"""The date the provider info was last updated, as a string."""
|
||||
return self._provider['status']['date']
|
||||
return from_dc_charpointer(
|
||||
lib.dc_provider_get_status_date(self._provider))
|
||||
|
||||
@property
|
||||
def status(self):
|
||||
"""The status of the provider information.
|
||||
|
||||
This is
|
||||
:attr:`"OK"`,
|
||||
:attr:`"PREPARATION"` or
|
||||
:attr:`"BROKEN"`.
|
||||
This is one of the
|
||||
:attr:`deltachat.const.DC_PROVIDER_STATUS_OK`,
|
||||
:attr:`deltachat.const.DC_PROVIDER_STATUS_PREPARATION` or
|
||||
:attr:`deltachat.const.DC_PROVIDER_STATUS_BROKEN` constants.
|
||||
"""
|
||||
return self._provider['status']['state']
|
||||
return lib.dc_provider_get_status(self._provider)
|
||||
|
||||
@@ -3,7 +3,6 @@ from deltachat import capi, cutil, const, set_context_callback, clear_context_ca
|
||||
from deltachat.capi import ffi
|
||||
from deltachat.capi import lib
|
||||
from deltachat.account import EventLogger
|
||||
import json
|
||||
|
||||
|
||||
def test_empty_context():
|
||||
@@ -104,23 +103,18 @@ def test_get_special_message_id_returns_empty_message(acfactory):
|
||||
|
||||
|
||||
def test_provider_info():
|
||||
provider_json = cutil.from_dc_charpointer(
|
||||
lib.dc_provider_json_from_email(cutil.as_dc_charpointer("ex@example.com"))
|
||||
)
|
||||
provider = json.loads(provider_json)
|
||||
|
||||
assert provider['overview_page'] == "example.com"
|
||||
assert provider['name'] == "Example"
|
||||
assert provider['markdown'] == "\n..."
|
||||
assert provider['status']['date'] == "2018-09"
|
||||
assert provider['status']['state'] == "PREPARATION"
|
||||
provider = lib.dc_provider_new_from_email(cutil.as_dc_charpointer("ex@example.com"))
|
||||
assert cutil.from_dc_charpointer(
|
||||
lib.dc_provider_get_overview_page(provider)
|
||||
) == "https://providers.delta.chat/example.com"
|
||||
assert cutil.from_dc_charpointer(lib.dc_provider_get_name(provider)) == "Example"
|
||||
assert cutil.from_dc_charpointer(lib.dc_provider_get_markdown(provider)) == "\n..."
|
||||
assert cutil.from_dc_charpointer(lib.dc_provider_get_status_date(provider)) == "2018-09"
|
||||
assert lib.dc_provider_get_status(provider) == const.DC_PROVIDER_STATUS_PREPARATION
|
||||
|
||||
|
||||
def test_provider_info_none():
|
||||
provider_json = cutil.from_dc_charpointer(
|
||||
lib.dc_provider_json_from_email(cutil.as_dc_charpointer("email@unexistent.no"))
|
||||
)
|
||||
assert provider_json == ""
|
||||
assert lib.dc_provider_new_from_email(cutil.as_dc_charpointer("email@unexistent.no")) == ffi.NULL
|
||||
|
||||
|
||||
def test_get_info_closed():
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import pytest
|
||||
|
||||
from deltachat import const
|
||||
from deltachat import provider
|
||||
|
||||
|
||||
@@ -9,7 +10,7 @@ def test_provider_info_from_email():
|
||||
assert example.name == "Example"
|
||||
assert example.markdown == "\n..."
|
||||
assert example.status_date == "2018-09"
|
||||
assert example.status == "PREPARATION"
|
||||
assert example.status == const.DC_PROVIDER_STATUS_PREPARATION
|
||||
|
||||
|
||||
def test_provider_info_from_domain():
|
||||
@@ -18,7 +19,7 @@ def test_provider_info_from_domain():
|
||||
assert example.name == "Example"
|
||||
assert example.markdown == "\n..."
|
||||
assert example.status_date == "2018-09"
|
||||
assert example.status == "PREPARATION"
|
||||
assert example.status == const.DC_PROVIDER_STATUS_PREPARATION
|
||||
|
||||
|
||||
def test_provider_info_none():
|
||||
|
||||
169
src/chat.rs
169
src/chat.rs
@@ -4,7 +4,7 @@ use std::path::{Path, PathBuf};
|
||||
|
||||
use itertools::Itertools;
|
||||
use num_traits::FromPrimitive;
|
||||
use serde_json::json;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::blob::{BlobError, BlobObject};
|
||||
use crate::chatlist::*;
|
||||
@@ -240,6 +240,30 @@ impl Chat {
|
||||
color
|
||||
}
|
||||
|
||||
/// Returns a struct describing the current state of the chat.
|
||||
///
|
||||
/// This is somewhat experimental, even more so than the rest of
|
||||
/// deltachat, and the data returned is still subject to change.
|
||||
pub fn get_info(&self, context: &Context) -> Result<ChatInfo, Error> {
|
||||
let draft = match get_draft(context, self.id)? {
|
||||
Some(message) => message.text.unwrap_or_else(String::new),
|
||||
_ => String::new(),
|
||||
};
|
||||
Ok(ChatInfo {
|
||||
id: self.id,
|
||||
type_: self.typ as u32,
|
||||
name: self.name.clone(),
|
||||
archived: self.archived,
|
||||
param: self.param.to_string(),
|
||||
gossiped_timestamp: self.get_gossiped_timestamp(context),
|
||||
is_sending_locations: self.is_sending_locations,
|
||||
color: self.get_color(context),
|
||||
profile_image: self.get_profile_image(context).unwrap_or_else(PathBuf::new),
|
||||
subtitle: self.get_subtitle(context),
|
||||
draft,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns true if the chat is archived.
|
||||
pub fn is_archived(&self) -> bool {
|
||||
self.archived
|
||||
@@ -497,6 +521,68 @@ impl Chat {
|
||||
}
|
||||
}
|
||||
|
||||
/// The current state of a chat.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct ChatInfo {
|
||||
/// The chat ID.
|
||||
pub id: u32,
|
||||
|
||||
/// The type of chat as a `u32` representation of [Chattype].
|
||||
///
|
||||
/// On the C API this number is one of the
|
||||
/// `DC_CHAT_TYPE_UNDEFINED`, `DC_CHAT_TYPE_SINGLE`,
|
||||
/// `DC_CHAT_TYPE_GROUP` or `DC_CHAT_TYPE_VERIFIED_GROUP`
|
||||
/// constants.
|
||||
#[serde(rename = "type")]
|
||||
pub type_: u32,
|
||||
|
||||
/// The name of the chat.
|
||||
pub name: String,
|
||||
|
||||
/// Whether the chat is archived.
|
||||
pub archived: bool,
|
||||
|
||||
/// The "params" of the chat.
|
||||
///
|
||||
/// This is the string-serialised version of [Params] currently.
|
||||
pub param: String,
|
||||
|
||||
/// Something to do with gossiping and timestamps?
|
||||
pub gossiped_timestamp: i64,
|
||||
|
||||
/// Whether this chat is currently sending location-stream messages.
|
||||
pub is_sending_locations: bool,
|
||||
|
||||
/// Colour this chat should be represented in by the UI.
|
||||
///
|
||||
/// Yes, spelling colour is hard.
|
||||
pub color: u32,
|
||||
|
||||
/// The path to the profile image.
|
||||
///
|
||||
/// If there is no profile image set this will be an empty string
|
||||
/// currently.
|
||||
pub profile_image: PathBuf,
|
||||
|
||||
/// Subtitle for the chat.
|
||||
pub subtitle: String,
|
||||
|
||||
/// The draft message text.
|
||||
///
|
||||
/// If the chat has not draft this is an empty string.
|
||||
///
|
||||
/// TODO: This doesn't seem rich enough, it can not handle drafts
|
||||
/// which contain non-text parts. Perhaps it should be a
|
||||
/// simple `has_draft` bool instead.
|
||||
pub draft: String,
|
||||
// ToDo:
|
||||
// - [ ] deaddrop,
|
||||
// - [ ] summary,
|
||||
// - [ ] lastUpdated,
|
||||
// - [ ] freshMessageCounter,
|
||||
// - [ ] email
|
||||
}
|
||||
|
||||
/// Create a normal chat or a group chat by a messages ID that comes typically
|
||||
/// from the deaddrop, DC_CHAT_ID_DEADDROP (1).
|
||||
///
|
||||
@@ -1990,54 +2076,6 @@ pub fn forward_msgs(context: &Context, msg_ids: &[MsgId], chat_id: u32) -> Resul
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_info_json(context: &Context, chat_id: u32) -> Result<String, Error> {
|
||||
let chat = Chat::load_from_db(context, chat_id).unwrap();
|
||||
|
||||
// ToDo:
|
||||
// - [x] id
|
||||
// - [x] type
|
||||
// - [x] name
|
||||
// - [x] archived
|
||||
// - [x] color
|
||||
// - [x] profileImage
|
||||
// - [x] subtitle
|
||||
// - [x] draft,
|
||||
// - [ ] deaddrop,
|
||||
// - [ ] summary,
|
||||
// - [ ] lastUpdated,
|
||||
// - [ ] freshMessageCounter,
|
||||
// - [ ] email
|
||||
|
||||
let profile_image = match chat.get_profile_image(context) {
|
||||
Some(path) => path.into_os_string().into_string().unwrap(),
|
||||
None => "".to_string(),
|
||||
};
|
||||
|
||||
let draft = match get_draft(context, chat_id) {
|
||||
Ok(message) => match message {
|
||||
Some(m) => m.text.unwrap_or_else(|| "".to_string()),
|
||||
None => "".to_string(),
|
||||
},
|
||||
Err(_) => "".to_string(),
|
||||
};
|
||||
|
||||
let s = json!({
|
||||
"id": chat.id,
|
||||
"type": chat.typ as u32,
|
||||
"name": chat.name,
|
||||
"archived": chat.archived,
|
||||
"param": chat.param.to_string(),
|
||||
"gossiped_timestamp": chat.get_gossiped_timestamp(context),
|
||||
"is_sending_locations": chat.is_sending_locations,
|
||||
"color": chat.get_color(context),
|
||||
"profile_image": profile_image,
|
||||
"subtitle": chat.get_subtitle(context),
|
||||
"draft": draft
|
||||
});
|
||||
|
||||
Ok(s.to_string())
|
||||
}
|
||||
|
||||
pub fn get_chat_contact_cnt(context: &Context, chat_id: u32) -> usize {
|
||||
context
|
||||
.sql
|
||||
@@ -2192,8 +2230,41 @@ pub fn add_info_msg(context: &Context, chat_id: u32, text: impl AsRef<str>) {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use crate::contact::Contact;
|
||||
use crate::test_utils::*;
|
||||
|
||||
#[test]
|
||||
fn test_chat_info() {
|
||||
let t = dummy_context();
|
||||
let bob = Contact::create(&t.ctx, "bob", "bob@example.com").unwrap();
|
||||
let chat_id = create_by_contact_id(&t.ctx, bob).unwrap();
|
||||
let chat = Chat::load_from_db(&t.ctx, chat_id).unwrap();
|
||||
let info = chat.get_info(&t.ctx).unwrap();
|
||||
|
||||
// Ensure we can serialise this.
|
||||
println!("{}", serde_json::to_string_pretty(&info).unwrap());
|
||||
|
||||
let expected = r#"
|
||||
{
|
||||
"id": 10,
|
||||
"type": 100,
|
||||
"name": "bob",
|
||||
"archived": false,
|
||||
"param": "",
|
||||
"gossiped_timestamp": 0,
|
||||
"is_sending_locations": false,
|
||||
"color": 15895624,
|
||||
"profile_image": "",
|
||||
"subtitle": "bob@example.com",
|
||||
"draft": ""
|
||||
}
|
||||
"#;
|
||||
|
||||
// Ensure we can deserialise this.
|
||||
let loaded: ChatInfo = serde_json::from_str(expected).unwrap();
|
||||
assert_eq!(info, loaded);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_draft_no_draft() {
|
||||
let t = dummy_context();
|
||||
|
||||
@@ -877,10 +877,15 @@ fn create_or_lookup_group(
|
||||
mime_parser.repl_msg_by_error(s);
|
||||
}
|
||||
}
|
||||
// check if the sender is a member of the existing group -
|
||||
// if not, we'll recreate the group list
|
||||
if !chat::is_contact_in_chat(context, chat_id, from_id as u32) {
|
||||
recreate_member_list = true;
|
||||
// The From-address is not part of this group.
|
||||
// It could be a new user or a DSN from a mailer-daemon.
|
||||
// in any case we do not want to recreate the member list
|
||||
// but still show the message as part of the chat.
|
||||
// After all, the sender has a reference/in-reply-to that
|
||||
// points to this chat.
|
||||
let s = context.stock_str(StockMessage::UnknownSenderForChat);
|
||||
mime_parser.repl_msg_by_error(s.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1002,6 +1002,21 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns base64-encoded buffer `buf` split into 78-bytes long
|
||||
/// chunks separated by CRLF.
|
||||
///
|
||||
/// This line length limit is an
|
||||
/// [RFC5322 requirement](https://tools.ietf.org/html/rfc5322#section-2.1.1).
|
||||
fn wrapped_base64_encode(buf: &[u8]) -> String {
|
||||
let base64 = base64::encode(&buf);
|
||||
let mut chars = base64.chars();
|
||||
(0..)
|
||||
.map(|_| chars.by_ref().take(78).collect::<String>())
|
||||
.take_while(|s| !s.is_empty())
|
||||
.collect::<Vec<_>>()
|
||||
.join("\r\n")
|
||||
}
|
||||
|
||||
fn build_body_file(
|
||||
context: &Context,
|
||||
msg: &Message,
|
||||
@@ -1062,7 +1077,7 @@ fn build_body_file(
|
||||
};
|
||||
|
||||
let body = std::fs::read(blob.to_abs_path())?;
|
||||
let encoded_body = base64::encode(&body);
|
||||
let encoded_body = wrapped_base64_encode(&body);
|
||||
|
||||
let mail = PartBuilder::new()
|
||||
.content_type(&mimetype)
|
||||
@@ -1084,7 +1099,7 @@ fn build_selfavatar_file(context: &Context, path: String) -> Result<(PartBuilder
|
||||
None => mime::APPLICATION_OCTET_STREAM,
|
||||
};
|
||||
let body = std::fs::read(blob.to_abs_path())?;
|
||||
let encoded_body = base64::encode(&body);
|
||||
let encoded_body = wrapped_base64_encode(&body);
|
||||
|
||||
let part = PartBuilder::new()
|
||||
.content_type(&mimetype)
|
||||
@@ -1203,4 +1218,13 @@ mod tests {
|
||||
"<123@q> <456@d>".to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wrapped_base64_encode() {
|
||||
let input = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||
let output =
|
||||
"QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQU\r\n\
|
||||
FBQUFBQUFBQQ==";
|
||||
assert_eq!(wrapped_base64_encode(input), output);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,6 +185,9 @@ pub enum StockMessage {
|
||||
Recipients don't need to install Delta Chat, visit websites or sign up anywhere - \
|
||||
however, of course, if they like, you may point them to 👉 https://get.delta.chat"))]
|
||||
WelcomeMessage = 71,
|
||||
|
||||
#[strum(props(fallback = "Unknown Sender for this chat. See 'info' for more details."))]
|
||||
UnknownSenderForChat = 72,
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user