Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  contact.rs   Sprache: unbekannt

 
Spracherkennung für: .rs vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

//! Contact structs.
use std::collections::HashMap;

use duplicate::duplicate_item;

use super::provider::{ContactProvider, ProviderError, SettingsProvider};
use crate::{
    common::{Delta, FeatureMask, ThreemaId, config::Config, keys::PublicKey},
    protobuf::{self, d2d_sync::contact as protobuf_contact},
    utils::{
        apply::{Apply as _, TryApply},
        time::utc_now_ms,
    },
};

/// A contact could not be updated.
#[derive(Debug, thiserror::Error)]
pub(crate) enum ContactUpdateError {
    /// Contact could not be updated because the identity does not match.
    #[error("Identity mismatch: Expected {expected} but got {actual}")]
    IdentityMismatch { expected: ThreemaId, actual: ThreemaId },

    /// Contact could not be updated because the public key does not match.
    #[error("Public key mismatch")]
    PublicKeyMismatch,
}

#[derive(Debug)]
pub(crate) enum CommunicationPermission {
    /// Communication with this contact is allowed.
    Allow,

    /// Communication with this contact is disallowed due to it being explicitly blocked.
    BlockExplicit,

    /// Communication with this contact is disallowed due to it being implicitly blocked by the _block
    /// unknown_ setting.
    ///
    /// Note: This is purely informational, i.e. when encountering this, the behaviour must be the same as for
    /// [`CommunicationPermission::BlockExplicit`]. Communication would be allowed after the contact has been
    /// explicitly added or both the user and the contact are in a group not marked as _left_.
    BlockUnknown,
}

/// All protocol relevant data associated to a contact with the following exclusions:
///
/// - _contact-defined_ profile picture
/// - _user-defined_ profile picture
//-
// IMPORTANT: When touching these, always make sure to update [`ContactUpdate`] and
// `ContactUpdate::apply_to` accordingly.
#[derive(Debug, Clone)]
pub struct Contact {
    /// Threema ID of the contact.
    pub identity: ThreemaId,

    /// Public key of the contact.
    pub public_key: PublicKey,

    /// Unix-ish timestamp in milliseconds when the contact has been created (added) locally.
    pub created_at: u64,

    /// First name of the contact.
    pub first_name: Option<String>,

    /// Last name of the contact.
    pub last_name: Option<String>,

    /// Nickname of the contact (without `~` prefix).
    ///
    /// IMPORTANT: Do not provide the Threema ID as a fallback!
    pub nickname: Option<String>,

    /// Verification level of the contact.
    pub verification_level: protobuf_contact::VerificationLevel,

    /// Threema Work verification level of the contact.
    pub work_verification_level: protobuf_contact::WorkVerificationLevel,

    /// Identity type of the contact.
    pub identity_type: protobuf_contact::IdentityType,

    /// Acquaintance level of the contact.
    pub acquaintance_level: protobuf_contact::AcquaintanceLevel,

    /// Activity state of the contact.
    pub activity_state: protobuf_contact::ActivityState,

    /// Features available for the contact.
    pub feature_mask: FeatureMask,

    /// Contact synchronisation state.
    pub sync_state: protobuf_contact::SyncState,

    /// _Read_ receipt policy override for this contact.
    pub read_receipt_policy_override: Option<protobuf::d2d_sync::ReadReceiptPolicy>,

    /// Typing indicator policy override for this contact.
    pub typing_indicator_policy_override: Option<protobuf::d2d_sync::TypingIndicatorPolicy>,

    /// Notification trigger policy for the contact.
    pub notification_trigger_policy_override:
        Option<protobuf_contact::notification_trigger_policy_override::Policy>,

    /// Notification sound policy for the contact.
    pub notification_sound_policy_override: Option<protobuf::d2d_sync::NotificationSoundPolicy>,

    /// Conversation category of the contact.
    pub conversation_category: protobuf::d2d_sync::ConversationCategory,

    /// Conversation visibility of the contact.
    pub conversation_visibility: protobuf::d2d_sync::ConversationVisibility,
}

/// All protocol relevant data associated to create a contact with the following exclusions:
///
/// - _contact-defined_ profile picture
/// - _user-defined_ profile picture
pub type ContactInit = Contact;

#[derive(Debug)]
pub(crate) enum ContactOrInit {
    ExistingContact(Contact),
    NewContact(ContactInit),
}
impl ContactOrInit {
    #[inline]
    pub(crate) const fn inner(&self) -> &Contact {
        match self {
            ContactOrInit::NewContact(contact) | ContactOrInit::ExistingContact(contact) => contact,
        }
    }

    pub(crate) fn communication_permission(
        &self,
        config: &Config,
        settings_provider: &dyn SettingsProvider,
        contact_provider: &dyn ContactProvider,
    ) -> Result<CommunicationPermission, ProviderError> {
        let inner = self.inner();
        let predefined_contact = config.predefined_contacts.get(&inner.identity);

        // Special contacts cannot be blocked
        if predefined_contact.is_some_and(|contact| contact.special) {
            return Ok(CommunicationPermission::Allow);
        }

        // Check if the user explicitly blocked the contact
        if contact_provider.is_explicitly_blocked(inner.identity)? {
            return Ok(CommunicationPermission::BlockExplicit);
        }

        // Check if the user wants to block _unknown_ identities.
        if !settings_provider.block_unknown_identities()? {
            return Ok(CommunicationPermission::Allow);
        }

        // Predefined contacts should not be implicitly blocked
        if predefined_contact.is_some() {
            return Ok(CommunicationPermission::Allow);
        }

        Ok(match &self {
            ContactOrInit::NewContact(_) => {
                // The contact does not exist and block unknown is active
                CommunicationPermission::BlockUnknown
            },

            ContactOrInit::ExistingContact(inner) => {
                // Check if the contact has acquaintance level _direct_
                if inner.acquaintance_level == protobuf_contact::AcquaintanceLevel::Direct {
                    return Ok(CommunicationPermission::Allow);
                }

                // Check if the user and the contact are part of an active group
                if contact_provider.is_member_of_active_group(inner.identity)? {
                    CommunicationPermission::Allow
                } else {
                    CommunicationPermission::BlockUnknown
                }
            },
        })
    }
}

#[duplicate_item(
    [
        input_type [ protobuf::d2d_sync::ReadReceiptPolicy ]
        output_type [ protobuf_contact::ReadReceiptPolicyOverride ]
        override_type [ protobuf_contact::read_receipt_policy_override::Override ]
        policy_transform [ policy.into() ]
    ]
    [
        input_type [ protobuf::d2d_sync::TypingIndicatorPolicy ]
        output_type [ protobuf_contact::TypingIndicatorPolicyOverride ]
        override_type [ protobuf_contact::typing_indicator_policy_override::Override ]
        policy_transform [ policy.into() ]
    ]
    [
        input_type [ protobuf_contact::notification_trigger_policy_override::Policy ]
        output_type [ protobuf_contact::NotificationTriggerPolicyOverride ]
        override_type [ protobuf_contact::notification_trigger_policy_override::Override ]
        policy_transform [ policy ]
    ]
    [
        input_type [ protobuf::d2d_sync::NotificationSoundPolicy ]
        output_type [ protobuf_contact::NotificationSoundPolicyOverride ]
        override_type [ protobuf_contact::notification_sound_policy_override::Override ]
        policy_transform [ policy.into() ]
    ]
)]
impl From<Option<input_type>> for output_type {
    fn from(policy: Option<input_type>) -> Self {
        output_type {
            r#override: Some(match policy {
                Some(policy) => override_type::Policy(policy_transform),
                None => override_type::Default(protobuf::common::Unit {}),
            }),
        }
    }
}

impl From<&ContactInit> for protobuf::d2d_sync::Contact {
    fn from(contact: &ContactInit) -> Self {
        Self {
            identity: contact.identity.as_str().to_owned(),
            public_key: Some(contact.public_key.0.to_bytes().into()),
            created_at: Some(contact.created_at),
            first_name: contact.first_name.clone(),
            last_name: contact.last_name.clone(),
            nickname: contact.nickname.clone(),
            verification_level: Some(contact.verification_level.into()),
            work_verification_level: Some(contact.work_verification_level.into()),
            identity_type: Some(contact.identity_type.into()),
            acquaintance_level: Some(contact.acquaintance_level.into()),
            activity_state: Some(contact.activity_state.into()),
            feature_mask: Some(contact.feature_mask.0),
            sync_state: Some(contact.sync_state.into()),
            read_receipt_policy_override: Some(contact.read_receipt_policy_override.into()),
            typing_indicator_policy_override: Some(contact.typing_indicator_policy_override.into()),
            notification_trigger_policy_override: Some(contact.notification_trigger_policy_override.into()),
            notification_sound_policy_override: Some(contact.notification_sound_policy_override.into()),
            contact_defined_profile_picture: None,
            user_defined_profile_picture: None,
            conversation_category: Some(contact.conversation_category.into()),
            conversation_visibility: Some(contact.conversation_visibility.into()),
        }
    }
}

/// All protocol relevant data associated to update a contact with the following exclusions:
///
/// - _contact-defined_ profile picture
/// - _user-defined_ profile picture
///
/// Note: This may only include changes to a contact.
#[derive(Debug, Clone, PartialEq)]
pub struct ContactUpdate {
    /// Threema ID of the contact.
    pub identity: ThreemaId,

    /// First name of the contact.
    pub first_name: Delta<String>,

    /// Last name of the contact.
    pub last_name: Delta<String>,

    /// Nickname of the contact (without `~` prefix).
    ///
    /// IMPORTANT: Do not provide the Threema ID as a fallback!
    pub nickname: Delta<String>,

    /// Verification level of the contact.
    pub verification_level: Option<protobuf_contact::VerificationLevel>,

    /// Threema Work verification level of the contact.
    pub work_verification_level: Option<protobuf_contact::WorkVerificationLevel>,

    /// Identity type of the contact.
    pub identity_type: Option<protobuf_contact::IdentityType>,

    /// Acquaintance level of the contact.
    pub acquaintance_level: Option<protobuf_contact::AcquaintanceLevel>,

    /// Activity state of the contact.
    pub activity_state: Option<protobuf_contact::ActivityState>,

    /// Features available for the contact.
    pub feature_mask: Option<FeatureMask>,

    /// Contact synchronisation state.
    pub sync_state: Option<protobuf_contact::SyncState>,

    /// _Read_ receipt policy override for this contact.
    pub read_receipt_policy_override: Delta<protobuf::d2d_sync::ReadReceiptPolicy>,

    /// Typing indicator policy override for this contact.
    pub typing_indicator_policy_override: Delta<protobuf::d2d_sync::TypingIndicatorPolicy>,

    /// Notification trigger policy for the contact.
    pub notification_trigger_policy_override:
        Delta<protobuf_contact::notification_trigger_policy_override::Policy>,

    /// Notification sound policy for the contact.
    pub notification_sound_policy_override: Delta<protobuf::d2d_sync::NotificationSoundPolicy>,

    /// Conversation category of the contact.
    pub conversation_category: Option<protobuf::d2d_sync::ConversationCategory>,

    /// Conversation visibility of the contact.
    pub conversation_visibility: Option<protobuf::d2d_sync::ConversationVisibility>,
}

impl ContactUpdate {
    pub(crate) fn default(identity: ThreemaId) -> Self {
        Self {
            identity,
            first_name: Delta::Unchanged,
            last_name: Delta::Unchanged,
            nickname: Delta::Unchanged,
            verification_level: None,
            work_verification_level: None,
            identity_type: None,
            acquaintance_level: None,
            activity_state: None,
            feature_mask: None,
            sync_state: None,
            read_receipt_policy_override: Delta::Unchanged,
            typing_indicator_policy_override: Delta::Unchanged,
            notification_trigger_policy_override: Delta::Unchanged,
            notification_sound_policy_override: Delta::Unchanged,
            conversation_category: None,
            conversation_visibility: None,
        }
    }

    pub(crate) fn has_changes(&self) -> bool {
        self != &Self::default(self.identity)
    }
}
impl TryApply<ContactUpdate> for Contact {
    type Error = ContactUpdateError;

    /// Try applying the contact update to the contact.
    ///
    /// # Errors
    ///
    /// Returns [`ContactUpdateError`] if the identity mismatches.
    fn try_apply(&mut self, value: ContactUpdate) -> Result<(), Self::Error> {
        // Unpacking here, so we can't miss updating this function when a new field is being added
        let ContactUpdate {
            identity,
            first_name,
            last_name,
            nickname,
            verification_level,
            work_verification_level,
            identity_type,
            acquaintance_level,
            activity_state,
            feature_mask,
            sync_state,
            read_receipt_policy_override,
            typing_indicator_policy_override,
            notification_trigger_policy_override,
            notification_sound_policy_override,
            conversation_category,
            conversation_visibility,
        } = value;

        // Ensure the identity equals before updating
        if identity != self.identity {
            return Err(ContactUpdateError::IdentityMismatch {
                expected: self.identity,
                actual: identity,
            });
        }

        // Update all properties
        self.first_name.apply(first_name);
        self.last_name.apply(last_name);
        self.nickname.apply(nickname);
        self.verification_level = verification_level.unwrap_or(self.verification_level);
        self.work_verification_level = work_verification_level.unwrap_or(self.work_verification_level);
        self.identity_type = identity_type.unwrap_or(self.identity_type);
        self.acquaintance_level = acquaintance_level.unwrap_or(self.acquaintance_level);
        self.activity_state = activity_state.unwrap_or(self.activity_state);
        self.feature_mask = feature_mask.unwrap_or(self.feature_mask);
        self.sync_state = sync_state.unwrap_or(self.sync_state);
        self.read_receipt_policy_override
            .apply(read_receipt_policy_override);
        self.typing_indicator_policy_override
            .apply(typing_indicator_policy_override);
        self.notification_trigger_policy_override
            .apply(notification_trigger_policy_override);
        self.notification_sound_policy_override
            .apply(notification_sound_policy_override);
        self.conversation_category = conversation_category.unwrap_or(self.conversation_category);
        self.conversation_visibility = conversation_visibility.unwrap_or(self.conversation_visibility);

        // Done
        Ok(())
    }
}

#[duplicate_item(
    [
        input_type [ protobuf::d2d_sync::ReadReceiptPolicy ]
        output_type [ protobuf_contact::ReadReceiptPolicyOverride ]
        override_type [ protobuf_contact::read_receipt_policy_override::Override ]
        policy_transform [ (*policy).into() ]
    ]
    [
        input_type [ protobuf::d2d_sync::TypingIndicatorPolicy ]
        output_type [ protobuf_contact::TypingIndicatorPolicyOverride ]
        override_type [ protobuf_contact::typing_indicator_policy_override::Override ]
        policy_transform [ (*policy).into() ]
    ]
    [
        input_type [ protobuf_contact::notification_trigger_policy_override::Policy ]
        output_type [ protobuf_contact::NotificationTriggerPolicyOverride ]
        override_type [ protobuf_contact::notification_trigger_policy_override::Override ]
        policy_transform [ *policy ]
    ]
    [
        input_type [ protobuf::d2d_sync::NotificationSoundPolicy ]
        output_type [ protobuf_contact::NotificationSoundPolicyOverride ]
        override_type [ protobuf_contact::notification_sound_policy_override::Override ]
        policy_transform [ (*policy).into() ]
    ]
)]
impl From<&Delta<input_type>> for Option<output_type> {
    fn from(policy: &Delta<input_type>) -> Self {
        match policy {
            Delta::Unchanged => None,
            Delta::Update(policy) => Some(output_type {
                r#override: Some(override_type::Policy(policy_transform)),
            }),
            Delta::Remove => Some(output_type {
                r#override: Some(override_type::Default(protobuf::common::Unit {})),
            }),
        }
    }
}

impl From<&ContactUpdate> for protobuf::d2d_sync::Contact {
    fn from(contact: &ContactUpdate) -> Self {
        Self {
            identity: contact.identity.as_str().to_owned(),
            public_key: None,
            created_at: None,
            first_name: contact.first_name.clone().into_non_empty(),
            last_name: contact.last_name.clone().into_non_empty(),
            nickname: contact.nickname.clone().into_non_empty(),
            verification_level: contact.verification_level.map(Into::into),
            work_verification_level: contact.work_verification_level.map(Into::into),
            identity_type: contact.identity_type.map(Into::into),
            acquaintance_level: contact.acquaintance_level.map(Into::into),
            activity_state: contact.activity_state.map(Into::into),
            feature_mask: contact.feature_mask.map(|feature_mask| feature_mask.0),
            sync_state: contact.sync_state.map(Into::into),
            read_receipt_policy_override: (&contact.read_receipt_policy_override).into(),
            typing_indicator_policy_override: (&contact.typing_indicator_policy_override).into(),
            notification_trigger_policy_override: (&contact.notification_trigger_policy_override).into(),
            notification_sound_policy_override: (&contact.notification_sound_policy_override).into(),
            contact_defined_profile_picture: None,
            user_defined_profile_picture: None,
            conversation_category: contact.conversation_category.map(Into::into),
            conversation_visibility: contact.conversation_visibility.map(Into::into),
        }
    }
}

/// A predefined contact that does not need to be fetched from the directory.
///
/// It is automatically elevated to the highest verification level.
#[derive(Debug, Clone)]
pub struct PredefinedContact {
    /// Threema ID of the predefined contact.
    pub identity: ThreemaId,

    /// Whether the predefined contact is marked as a _special contact_.
    pub special: bool,

    /// Public key of the predefined contact.
    pub public_key: PublicKey,

    /// Nickname of the contact (without `~` prefix).
    pub nickname: String,
}
impl PredefinedContact {
    pub(crate) const _3MAPUSH_IDENTITY: ThreemaId = ThreemaId::predefined(*b"*3MAPUSH");

    pub(crate) fn production() -> HashMap<ThreemaId, Self> {
        [
            Self {
                identity: Self::_3MAPUSH_IDENTITY,
                special: true,
                #[rustfmt::skip]
                public_key: PublicKey::from([
                    0xfd, 0x71, 0x1e, 0x1a, 0x0d, 0xb0, 0xe2, 0xf0,
                    0x3f, 0xca, 0xab, 0x6c, 0x43, 0xda, 0x25, 0x75,
                    0xb9, 0x51, 0x36, 0x64, 0xa6, 0x2a, 0x12, 0xbd,
                    0x07, 0x28, 0xd8, 0x7f, 0x71, 0x25, 0xcc, 0x24,
                ]),
                nickname: "Threema Push".to_owned(),
            },
            Self {
                identity: ThreemaId::predefined(*b"*3MATOKN"),
                special: false,
                #[rustfmt::skip]
                public_key: PublicKey::from([
                    0x04, 0x88, 0x4d, 0x12, 0xd6, 0x68, 0xf8, 0x55,
                    0xd0, 0x0d, 0x71, 0xfb, 0x1d, 0x9d, 0x41, 0x3c,
                    0x95, 0xf2, 0x71, 0x31, 0x2f, 0x7e, 0x07, 0x78,
                    0x46, 0xaf, 0x67, 0x18, 0x75, 0xc4, 0x10, 0x1b,
                ]),
                nickname: "Threema Token".to_owned(),
            },
            Self {
                identity: ThreemaId::predefined(*b"*3MAWORK"),
                special: false,
                #[rustfmt::skip]
                public_key: PublicKey::from([
                    0x9a, 0xa0, 0xa7, 0x2a, 0x8f, 0xb6, 0xf0, 0xcc,
                    0x53, 0x72, 0x7f, 0xea, 0x60, 0x96, 0xf1, 0xb7,
                    0xb0, 0xeb, 0xef, 0xcc, 0x26, 0x50, 0xad, 0x39,
                    0xa1, 0xe5, 0x48, 0x37, 0xbb, 0xa0, 0xbc, 0x4b,
                ]),
                nickname: "Threema Work Channel".to_owned(),
            },
            Self {
                identity: ThreemaId::predefined(*b"*BETAFBK"),
                special: false,
                #[rustfmt::skip]
                public_key: PublicKey::from([
                    0x56, 0x84, 0xd6, 0xdc, 0xd3, 0x2a, 0x16, 0x48,
                    0x8d, 0xf8, 0x37, 0x10, 0x95, 0xfc, 0x9a, 0x1f,
                    0xc2, 0x5b, 0xae, 0xb6, 0xb9, 0x73, 0x66, 0xd9,
                    0x9f, 0xdf, 0x2a, 0xba, 0x00, 0xe2, 0xbc, 0x5c,
                ]),
                nickname: "Threema Beta Feedback".to_owned(),
            },
            Self {
                identity: ThreemaId::predefined(*b"*MY3DATA"),
                special: false,
                #[rustfmt::skip]
                public_key: PublicKey::from([
                    0x3b, 0x01, 0x85, 0x4f, 0x24, 0x73, 0x6e, 0x2d,
                    0x0d, 0x2d, 0xc3, 0x87, 0xea, 0xf2, 0xc0, 0x27,
                    0x3c, 0x50, 0x49, 0x05, 0x21, 0x47, 0x13, 0x23,
                    0x69, 0xbf, 0x39, 0x60, 0xd0, 0xa0, 0xbf, 0x02,
                ]),
                nickname: "My Threema Data".to_owned(),
            },
            Self {
                identity: ThreemaId::predefined(*b"*SUPPORT"),
                special: false,
                #[rustfmt::skip]
                public_key: PublicKey::from([
                    0x0f, 0x94, 0x4d, 0x18, 0x32, 0x4b, 0x21, 0x32,
                    0xc6, 0x1d, 0x8e, 0x40, 0xaf, 0xce, 0x60, 0xa0,
                    0xeb, 0xd7, 0x01, 0xbb, 0x11, 0xe8, 0x9b, 0xe9,
                    0x49, 0x72, 0xd4, 0x22, 0x9e, 0x94, 0x72, 0x2a,
                ]),
                nickname: "Threema Support".to_owned(),
            },
            Self {
                identity: ThreemaId::predefined(*b"*THREEMA"),
                special: false,
                #[rustfmt::skip]
                public_key: PublicKey::from([
                    0x3a, 0x38, 0x65, 0x0c, 0x68, 0x14, 0x35, 0xbd,
                    0x1f, 0xb8, 0x49, 0x8e, 0x21, 0x3a, 0x29, 0x19,
                    0xb0, 0x93, 0x88, 0xf5, 0x80, 0x3a, 0xa4, 0x46,
                    0x40, 0xe0, 0xf7, 0x06, 0x32, 0x6a, 0x86, 0x5c,
                ]),
                nickname: "Threema Channel".to_owned(),
            },
        ]
        .into_iter()
        .map(|contact| (contact.identity, contact))
        .collect()
    }

    pub(crate) fn sandbox() -> HashMap<ThreemaId, Self> {
        [
            Self {
                identity: Self::_3MAPUSH_IDENTITY,
                special: true,
                #[rustfmt::skip]
                public_key: PublicKey::from([
                    0xfd, 0x71, 0x1e, 0x1a, 0x0d, 0xb0, 0xe2, 0xf0,
                    0x3f, 0xca, 0xab, 0x6c, 0x43, 0xda, 0x25, 0x75,
                    0xb9, 0x51, 0x36, 0x64, 0xa6, 0x2a, 0x12, 0xbd,
                    0x07, 0x28, 0xd8, 0x7f, 0x71, 0x25, 0xcc, 0x24,
                ]),
                nickname: "Threema Push".to_owned(),
            },
            Self {
                identity: ThreemaId::predefined(*b"*3MATOKN"),
                special: false,
                #[rustfmt::skip]
                public_key: PublicKey::from([
                    0x04, 0x88, 0x4d, 0x12, 0xd6, 0x68, 0xf8, 0x55,
                    0xd0, 0x0d, 0x71, 0xfb, 0x1d, 0x9d, 0x41, 0x3c,
                    0x95, 0xf2, 0x71, 0x31, 0x2f, 0x7e, 0x07, 0x78,
                    0x46, 0xaf, 0x67, 0x18, 0x75, 0xc4, 0x10, 0x1b,
                ]),
                nickname: "Threema Token".to_owned(),
            },
            Self {
                identity: ThreemaId::predefined(*b"*3MAWORK"),
                special: false,
                #[rustfmt::skip]
                public_key: PublicKey::from([
                    0x9a, 0xa0, 0xa7, 0x2a, 0x8f, 0xb6, 0xf0, 0xcc,
                    0x53, 0x72, 0x7f, 0xea, 0x60, 0x96, 0xf1, 0xb7,
                    0xb0, 0xeb, 0xef, 0xcc, 0x26, 0x50, 0xad, 0x39,
                    0xa1, 0xe5, 0x48, 0x37, 0xbb, 0xa0, 0xbc, 0x4b,
                ]),
                nickname: "Threema Work Channel".to_owned(),
            },
            Self {
                identity: ThreemaId::predefined(*b"*MY3DATA"),
                special: false,
                #[rustfmt::skip]
                public_key: PublicKey::from([
                    0x83, 0xad, 0xfe, 0xe6, 0x55, 0x8b, 0x68, 0xae,
                    0x3c, 0xd6, 0xbb, 0xe2, 0xa3, 0x3f, 0x4e, 0x44,
                    0x09, 0xd5, 0x62, 0x4a, 0x7c, 0xea, 0x23, 0xa1,
                    0x89, 0x75, 0xae, 0xa6, 0x27, 0x2a, 0x00, 0x70,
                ]),
                nickname: "My Threema Data".to_owned(),
            },
            Self {
                identity: ThreemaId::predefined(*b"*SUPPORT"),
                special: false,
                #[rustfmt::skip]
                public_key: PublicKey::from([
                    0x0f, 0x94, 0x4d, 0x18, 0x32, 0x4b, 0x21, 0x32,
                    0xc6, 0x1d, 0x8e, 0x40, 0xaf, 0xce, 0x60, 0xa0,
                    0xeb, 0xd7, 0x01, 0xbb, 0x11, 0xe8, 0x9b, 0xe9,
                    0x49, 0x72, 0xd4, 0x22, 0x9e, 0x94, 0x72, 0x2a,
                ]),
                nickname: "Threema Support".to_owned(),
            },
            Self {
                identity: ThreemaId::predefined(*b"*THREEMA"),
                special: false,
                #[rustfmt::skip]
                public_key: PublicKey::from([
                    0x3a, 0x38, 0x65, 0x0c, 0x68, 0x14, 0x35, 0xbd,
                    0x1f, 0xb8, 0x49, 0x8e, 0x21, 0x3a, 0x29, 0x19,
                    0xb0, 0x93, 0x88, 0xf5, 0x80, 0x3a, 0xa4, 0x46,
                    0x40, 0xe0, 0xf7, 0x06, 0x32, 0x6a, 0x86, 0x5c,
                ]),
                nickname: "Threema Channel".to_owned(),
            },
        ]
        .into_iter()
        .map(|contact| (contact.identity, contact))
        .collect()
    }

    pub(crate) fn update(&self, contact: &mut Contact) -> Result<(), ContactUpdateError> {
        let Self {
            identity,
            special: _special,
            public_key,
            nickname,
        } = self;

        // Ensure the identity and public key equal before updating
        if *identity != contact.identity {
            return Err(ContactUpdateError::IdentityMismatch {
                expected: contact.identity,
                actual: *identity,
            });
        }
        if *public_key != contact.public_key {
            return Err(ContactUpdateError::PublicKeyMismatch);
        }

        // Bump verification level
        contact.verification_level = protobuf_contact::VerificationLevel::FullyVerified;

        // Update nickname
        contact.nickname = Some(nickname.clone());

        // Done
        Ok(())
    }
}

impl From<&PredefinedContact> for ContactInit {
    fn from(predefined_contact: &PredefinedContact) -> Self {
        Self {
            identity: predefined_contact.identity,
            public_key: predefined_contact.public_key,
            created_at: utc_now_ms(),
            first_name: None,
            last_name: None,
            nickname: Some(predefined_contact.nickname.clone()),
            verification_level: protobuf_contact::VerificationLevel::FullyVerified,
            work_verification_level: protobuf_contact::WorkVerificationLevel::None,
            // Gateway IDs are semantically neither identity type but, eh, whatever.
            identity_type: protobuf_contact::IdentityType::Regular,
            acquaintance_level: protobuf_contact::AcquaintanceLevel::GroupOrDeleted,
            activity_state: protobuf_contact::ActivityState::Active,
            feature_mask: FeatureMask(FeatureMask::NONE),
            sync_state: protobuf_contact::SyncState::Initial,
            read_receipt_policy_override: None,
            typing_indicator_policy_override: None,
            notification_trigger_policy_override: None,
            notification_sound_policy_override: None,
            conversation_category: protobuf::d2d_sync::ConversationCategory::Default,
            conversation_visibility: protobuf::d2d_sync::ConversationVisibility::Normal,
        }
    }
}

[Dauer der Verarbeitung: 0.22 Sekunden, vorverarbeitet 2026-05-05]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge