//! Types for EPP requests use serde::{ser::SerializeStruct, ser::Serializer, Deserialize, Serialize}; use std::error::Error; use std::time::SystemTime; use crate::{ common::{ ElementName, EmptyTag, EppObject, Extension, Options, ServiceExtension, Services, StringValue, }, contact::EPP_CONTACT_XMLNS, domain::EPP_DOMAIN_XMLNS, host::EPP_HOST_XMLNS, }; use epp_client_macros::*; pub const EPP_VERSION: &str = "1.0"; pub const EPP_LANG: &str = "en"; /// Type corresponding to the <command> tag in an EPP XML request /// without an <extension> tag pub type Command = CommandWithExtension; /// The EPP Login Request pub type EppLogin = EppObject>; /// The EPP Logout request pub type EppLogout = EppObject>; #[derive(Deserialize, Debug, PartialEq, ElementName)] #[element_name(name = "command")] /// Type corresponding to the <command> tag in an EPP XML request /// with an <extension> tag pub struct CommandWithExtension { /// The instance that will be used to populate the <command> tag pub command: T, /// The client TRID pub extension: Option>, #[serde(rename = "clTRID")] pub client_tr_id: StringValue, } impl Serialize for CommandWithExtension { /// Serializes the generic type T to the proper XML tag (set by the `#[element_name(name = )]` attribute) for the request fn serialize(&self, serializer: S) -> Result where S: Serializer, { let mut state = serializer.serialize_struct("command", 3)?; state.serialize_field(T::ELEMENT, &self.command)?; state.serialize_field("extension", &self.extension)?; state.serialize_field("clTRID", &self.client_tr_id)?; state.end() } } impl Command { /// Creates a new <command> tag for an EPP document pub fn new(command: T, client_tr_id: &str) -> Command { Command { command, extension: None, client_tr_id: client_tr_id.into(), } } } impl CommandWithExtension { /// Creates a new <command> tag for an EPP document with a containing <extension> tag pub fn build(command: T, ext: E, client_tr_id: &str) -> CommandWithExtension { CommandWithExtension { command, extension: Some(Extension { data: ext }), client_tr_id: client_tr_id.into(), } } } /// Basic client TRID generation function. Mainly used for testing. Users of the library should use their own clTRID generation function. pub fn generate_client_tr_id(username: &str) -> Result> { let timestamp = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?; Ok(format!("{}:{}", username, timestamp.as_secs())) } #[derive(Serialize, Deserialize, Debug, PartialEq, ElementName)] #[element_name(name = "login")] /// Type corresponding to the <login> tag in an EPP XML login request pub struct Login { /// The username to use for the login #[serde(rename(serialize = "clID", deserialize = "clID"))] username: StringValue, /// The password to use for the login #[serde(rename = "pw", default)] password: StringValue, /// Data under the tag options: Options, /// Data under the tag #[serde(rename = "svcs")] services: Services, } impl EppLogin { /// Creates a new EPP Login request pub fn new( username: &str, password: &str, ext_uris: &Option>, client_tr_id: &str, ) -> EppLogin { let ext_uris = ext_uris .as_ref() .map(|uris| uris.iter().map(|u| u.as_str().into()).collect()); let login = Login { username: username.into(), password: password.into(), options: Options { version: EPP_VERSION.into(), lang: EPP_LANG.into(), }, services: Services { obj_uris: vec![ EPP_HOST_XMLNS.into(), EPP_CONTACT_XMLNS.into(), EPP_DOMAIN_XMLNS.into(), ], svc_ext: Some(ServiceExtension { ext_uris }), }, }; EppObject::build(Command:: { command: login, extension: None, client_tr_id: client_tr_id.into(), }) } /// Sets the tag data pub fn options(&mut self, options: Options) { self.data.command.options = options; } /// Sets the tag data pub fn services(&mut self, services: Services) { self.data.command.services = services; } } #[derive(Serialize, Deserialize, Debug, PartialEq, ElementName)] #[element_name(name = "logout")] /// Type corresponding to the <logout> tag in an EPP XML logout request pub struct Logout; impl EppLogout { /// Creates a new EPP Logout request pub fn new(client_tr_id: &str) -> EppLogout { EppObject::build(Command:: { command: Logout, extension: None, client_tr_id: client_tr_id.into(), }) } }