kdeconnect-proto 0.1.1

A pure Rust modular implementation of the KDE Connect protocol
Documentation
//! Define the [`IdentityPacket`] structure which is used to identify devices and their
//! capabilities during the connection phase.
use serde::{Deserialize, Serialize};

#[cfg(not(feature = "std"))]
use alloc::{string::String, vec, vec::Vec};

use crate::{device::DeviceType, packet::NetworkPacketType};

/// The KDE Connect identity packet is used to identify devices and their capabilities.
///
/// <https://invent.kde.org/network/kdeconnect-meta/blob/master/protocol.md#kdeconnectidentity>
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct IdentityPacket {
    /// A unique ID for the device. Device IDs must be generated to be between 32 and 38
    /// alphanumerical characters, and will be also set as the Common Name in the device
    /// TLS certificate. For backwards compatibility, however, implementations must accept
    /// (but not generate new) device IDs that also include hyphens (-) and/or underscores (_).
    /// A way to generate a valid device ID is getting a random UUIDv4 string and removing the
    /// hyphens (-), such as 740bd4b9b4184ee497d6caf1da8151be.
    pub device_id: String,

    /// The latest protocol version implemented by the device. The current version of the protocol described in this document is 8.
    pub protocol_version: u16,

    /// A human-readable label for the device. Must be 1-32 characters in length.
    /// Shouldn't contain any of the following punctuation marks "',;:.!?()[]<> If a name
    /// is received with invalid characters, it should be sanitized by removing the invalid
    /// characters before being used. When displayed to the user for pairing or other privileged
    /// interactions, it should always be displayed within quotes.
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    pub device_name: Option<String>,

    /// A device type string. Since the incomingCapabilities and outgoingCapabilities fields
    /// describe the functionality of a device, the deviceType field is typically only used to select an icon.
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    pub device_type: Option<DeviceType>,

    /// A list of packet types the device can consume. Note that this is only an indication a
    /// device can consume a packet type, not that it will.
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    pub incoming_capabilities: Option<Vec<NetworkPacketType>>,

    /// A list of packet types the device can provide. Note that this is only an indication a
    /// device can provide a packet type, not that it will.
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    pub outgoing_capabilities: Option<Vec<NetworkPacketType>>,

    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    tcp_port: Option<u16>,
}

impl IdentityPacket {
    /// Make a new [`IdentityPacket`].
    pub(crate) fn new<A: Into<String>, B: Into<String>>(
        device_id: A,
        device_name: B,
        device_type: DeviceType,
        tcp_port: u16,
    ) -> Self {
        Self {
            device_id: device_id.into(),
            device_name: Some(device_name.into()),
            device_type: Some(device_type),
            incoming_capabilities: Some(vec![]),
            outgoing_capabilities: Some(vec![]),
            protocol_version: crate::config::KDECONNECT_PROTOCOL_VERSION,
            tcp_port: Some(tcp_port),
        }
    }

    #[must_use]
    pub(crate) fn with_incoming_capabilities<I: IntoIterator<Item = NetworkPacketType>>(
        mut self,
        caps: I,
    ) -> Self {
        self.incoming_capabilities
            .get_or_insert(vec![])
            .extend(caps);
        self
    }

    #[must_use]
    #[allow(unused)]
    pub(crate) fn with_all_incoming_capabilities(self) -> Self {
        self.with_incoming_capabilities([
            NetworkPacketType::Battery,
            NetworkPacketType::Clipboard,
            NetworkPacketType::ClipboardConnect,
            NetworkPacketType::ConnectivityReport,
            NetworkPacketType::ContactsRequestAllUidsTimestamps,
            NetworkPacketType::ContactsRequestVcardByUid,
            NetworkPacketType::FindmyphoneRequest,
            NetworkPacketType::MousepadKeyboardState,
            NetworkPacketType::MousepadEcho,
            NetworkPacketType::MousepadRequest,
            NetworkPacketType::Mpris,
            NetworkPacketType::MprisRequest,
            NetworkPacketType::NotificationRequest,
            NetworkPacketType::NotificationReply,
            NetworkPacketType::NotificationAction,
            NetworkPacketType::Ping,
            NetworkPacketType::Notification,
            NetworkPacketType::RunCommand,
            NetworkPacketType::RunCommandRequest,
            NetworkPacketType::Sftp,
            NetworkPacketType::ShareRequest,
            NetworkPacketType::ShareRequestUpdate,
            NetworkPacketType::SmsRequest,
            NetworkPacketType::SmsRequestConversations,
            NetworkPacketType::SmsRequestConversation,
            NetworkPacketType::SmsRequestAttachment,
            NetworkPacketType::SystemVolume,
            NetworkPacketType::SystemVolumeRequest,
            NetworkPacketType::TelephonyRequestMute,
            NetworkPacketType::DigitizerSession,
            NetworkPacketType::Digitizer,
            NetworkPacketType::Presenter,
        ])
    }

    #[must_use]
    pub(crate) fn with_outgoing_capabilities<I: IntoIterator<Item = NetworkPacketType>>(
        mut self,
        caps: I,
    ) -> Self {
        self.outgoing_capabilities
            .get_or_insert(vec![])
            .extend(caps);
        self
    }

    #[must_use]
    #[allow(unused)]
    pub(crate) fn with_all_outgoing_capabilities(self) -> Self {
        self.with_outgoing_capabilities([
            NetworkPacketType::RunCommand,
            NetworkPacketType::RunCommandRequest,
            NetworkPacketType::Sftp,
            NetworkPacketType::ShareRequest,
            NetworkPacketType::ShareRequestUpdate,
            NetworkPacketType::SmsRequest,
            NetworkPacketType::SmsRequestConversations,
            NetworkPacketType::SmsRequestConversation,
            NetworkPacketType::SmsRequestAttachment,
            NetworkPacketType::SystemVolume,
            NetworkPacketType::SystemVolumeRequest,
            NetworkPacketType::TelephonyRequestMute,
            NetworkPacketType::DigitizerSession,
            NetworkPacketType::Digitizer,
            NetworkPacketType::Presenter,
            NetworkPacketType::Sftp,
            NetworkPacketType::ShareRequest,
            NetworkPacketType::ShareRequestUpdate,
        ])
    }

    pub(crate) fn get_tcp_port(&self) -> Option<u16> {
        self.tcp_port
    }
}