/*-
 * syslog-rs - a syslog client translated from libc to rust
 * 
 * Copyright 2025 Aleksandr Morozov
 * 
 * The syslog-rs crate can be redistributed and/or modified
 * under the terms of either of the following licenses:
 *
 *   1. the Mozilla Public License Version 2.0 (the “MPL”) OR
 *
 *   2. The MIT License (MIT)
 *                     
 *   3. EUROPEAN UNION PUBLIC LICENCE v. 1.2 EUPL © the European Union 2007, 2016
 */

use std::fmt;

#[cfg(feature = "build_sync")]
pub(super) use crate::sync::socket::{SyslogTap, Tap};

#[cfg(feature = "async_enabled")]
pub use crate::a_sync::{AsyncTap, AsyncSyslogTap};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DestinationType
{
    Local, Network, File
}

impl DestinationType
{
    #[inline]
    pub 
    fn is_network(&self) -> bool
    {
        return self == &Self::Network;
    }
}

/// This trait is a common interface for the syslog realization which holds
/// necessary info for compile time initialization and initialization during
/// runtime.
#[cfg(feature = "build_sync")]
pub trait SyslogDestination: fmt::Debug + fmt::Display + Send + Clone + SyslogDestMsg + 'static
{
    /// A provider of the connection to syslog for sync code.
    type SocketTap: SyslogTap<Self>;

    const DEST_TYPE: DestinationType;
}

/// This trait is a common interface for the syslog realization which holds
/// necessary info for compile time initialization and initialization during
/// runtime. For async portion.
#[cfg(feature = "async_enabled")]
pub trait AsyncSyslogDestination: fmt::Debug + fmt::Display + Send + Clone + SyslogDestMsg + 'static
{
    /// A provider of the connection to syslog for async code.
    type SocketTap: AsyncSyslogTap<Self>;
}

/// A commin trait for both sync and async which implements methods that 
/// returns some specific values for each type of the `destination`.
pub trait SyslogDestMsg: fmt::Debug + fmt::Display + Send + Clone + 'static
{
    /// Should return the max message length for specific syslog server type.
    fn get_max_msg_len() -> usize;
}


/// A separate implementation of the `SyslogLocal` but for Windows. Due to the 
/// complete incompat with the syslog methods, the implementation is separated.
/// The user should use conditional compilation and have 2 static records i.e
/// for target_family = "unix" and target_family = "windows".
#[cfg(target_family = "windows")]
pub mod imp_syslog_local
{

    use std::ffi::CString;

    use crate::WINDOWS_EVENT_REPORT_MAX_PAYLOAD_LEN;
    use crate::portable;
    use crate::portable::EventLogLocal;

    use crate::{error::SyRes, throw_error, map_error};

    use super::*;

    /// A local Event Log Windows only. But, even if you pick to log to Local Win 
    /// event journal, you still need to create a special recs in the registry in order
    /// to avoid the error.
    /// 
    /// @see [here](https://learn.microsoft.com/en-us/windows/win32/eventlog/reporting-an-event?redirectedfrom=MSDN)
    /// for more information.
    #[derive(Debug, Clone)]
    pub struct WindowsEvent
    {
        /// A uniq name identifying the instance i.e like `MyName.MyComponent`.
        service_name: CString,

        /// Universal Naming Convention (UNC) name of the remote server on which this operation is to be performed.
        unc_remote_server: Option<CString>,
    }

    impl fmt::Display for WindowsEvent
    {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 
        {
            write!(f, "name: {}, guid: {}", 
                unsafe { str::from_utf8_unchecked(self.service_name.as_bytes()) }, 
                self.unc_remote_server.as_ref().map_or("NONE", |f|  unsafe { str::from_utf8_unchecked(f.as_bytes()) })
            )
        }
    }

    impl SyslogDestMsg for WindowsEvent
    {
        fn get_max_msg_len() -> usize 
        {
            return WINDOWS_EVENT_REPORT_MAX_PAYLOAD_LEN;
        }
    }

    impl WindowsEvent
    {
        /// Creates a default connection to local event server. 
        /// 
        /// If the crate will not be
        /// able to determine the program name, the "UnknownProgram" will be assigned.
        pub 
        fn new() -> Self
        {
            let service_name = 
                portable::p_getprogname()
                    .map(|v|
                        {
                            if v.is_empty() == true
                            {
                                "UnknownProgram".to_string()
                            }
                            else
                            {
                                v
                            }
                        }
                    )
                    .unwrap_or("UnknownProgram".to_string());

            return
                Self
                {
                    service_name: 
                        CString::new(service_name).unwrap(),
                    unc_remote_server: 
                        None,
                };
        }

        /// A custom instance init. 
        /// 
        /// If the crate will not be able to determine the program name, the 
        /// "UnknownProgram" will be assigned.
        /// 
        /// # Arguments 
        /// 
        /// * `service_name` - an identification of the instance. This should be set
        /// in order for the Event Server to find your instance in the registry.
        /// 
        /// * `unc_server` - a UNC to remote server.
        /// 
        /// # Returns
        /// 
        /// An error [Result::Err] will be returned if:
        /// 
        /// * `service_name` is empty string.
        /// 
        /// * `unc_server` - is [Option::Some] but empty string.
        /// 
        /// * [CString] failes to convert string to c-string for any reason.
        pub 
        fn new_custom(service_name: &str, unc_server: Option<&str>) -> SyRes<Self>
        {
            let service_name_cstr = 
                if service_name.is_empty() == true
                {
                    throw_error!("service_name is empty");
                }
                else
                {
                    CString::new(service_name)
                        .map_err(|e|
                            map_error!("service_name CString error: '{}'", e)
                        )?
                };


            let unc_serv_cstr = 
                if let Some(unc) = unc_server
                {
                    if unc.is_empty() == true
                    {
                        throw_error!("service_name is empty");
                    }

                    Some(
                        CString::new(unc)
                            .map_err(|e|
                            map_error!("service_name CString error: '{}'", e)
                            )?
                    )
                }
                else
                {
                    None
                };

            return Ok(
                Self
                {
                    service_name: service_name_cstr,
                    unc_remote_server: unc_serv_cstr
                }
            );
        }

        /// Returns the UNC to remote server if any.
        #[inline]
        pub 
        fn get_server_unc(&self) -> Option<&CString>
        {
            return self.unc_remote_server.as_ref();
        }

        /// Returns the service name.
        #[inline]
        pub 
        fn get_service_name(&self) -> &CString
        {
            return &self.service_name;
        }
    }

    #[cfg(feature = "build_async_tokio")]
    impl AsyncSyslogDestination for WindowsEvent
    {
        type SocketTap = AsyncTap::<EventLogLocal, Self>;

    }

    #[cfg(feature = "build_async_smol")]
    impl AsyncSyslogDestination for WindowsEvent
    {
        type SocketTap = AsyncTap::<EventLogLocal, Self>;
    }

    #[cfg(feature = "build_sync")]
    impl SyslogDestination for WindowsEvent
    {
        type SocketTap = Tap::<EventLogLocal, Self>;
        
        const DEST_TYPE: DestinationType = DestinationType::Local;
    }
}

#[cfg(target_family = "windows")]
pub use self::imp_syslog_local::WindowsEvent;

#[cfg(target_family = "unix")]
pub mod imp_syslog_local
{
    use std::{fmt, path::{Path, PathBuf}};

    use crate::{common, error::SyRes, throw_error};
    
    use std::os::unix::net::UnixDatagram;
    use super::*;

    /// A local syslog which is working over datagram channel. If no path is specified,
    /// it will try to find the syslog server by the hardcoded links. The same will be
    /// performed if `use_alternative` is enabled when path is provided.
    #[derive(Debug, Clone)]
    pub struct SyslogLocal
    {
        /// A path to custom `UnixDatagram` socket.
        custom_remote_path: Option<PathBuf>,

        /// usefull only if `custom_path` is [Some]
        use_alternative: bool,
    }


    impl fmt::Display for SyslogLocal
    {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 
        {
            write!(f, "custom_path: {:?}, alt: {}", self.custom_remote_path, self.use_alternative)
        }
    }

    impl SyslogLocal
    {
        fn new_internal(custom_path: Option<PathBuf>, use_alt_path: bool) -> SyRes<Self>
        {
            let remote_path: Option<PathBuf> = 
                if let Some(path) = custom_path
                {
                    if use_alt_path == false
                    {
                        if path.exists() == false || path.is_file() == false
                        {
                            throw_error!("path either does not exists or not a file: '{}'", path.display());
                        }
                    }

                    Some(path.to_path_buf())
                }
                else
                {
                    None
                };

            return Ok(
                Self
                { 
                    custom_remote_path: remote_path, 
                    use_alternative: use_alt_path, 
                }
            );
        }

        /// Creates a default connection to local syslog server. The correct path will be picked
        /// automatically.
        pub 
        fn new() -> Self
        {
            return
                Self::new_internal(None, false).unwrap();
        }

        /// Creates a special connection to specific local socket. If `use_alt_path` is 
        /// set to true and `custom_path` is not available, other pre-programmed paths
        /// will be probed and selected the first available.
        pub 
        fn new_custom_path<P: Into<PathBuf>>(custom_path: P, use_alt_path: bool) -> SyRes<Self>
        {
            return
                Self::new_internal(Some(custom_path.into()), use_alt_path);
        }

        #[inline]
        pub 
        fn get_custom_remote_path(&self) -> Option<&Path>
        {
            return self.custom_remote_path.as_ref().map(|f| f.as_path());
        }

        #[inline]
        pub 
        fn get_use_alternative(&self) -> bool
        {
            return self.use_alternative;
        }
    }

    impl SyslogDestMsg for SyslogLocal
    {
        fn get_max_msg_len() -> usize 
        {
            if *common::RFC5424_MAX_DGRAM >= common::MAXLINE
            {
                return common::MAXLINE;
            }
            else
            {
                return *common::RFC5424_MAX_DGRAM;
            };
        }
    }

    #[cfg(feature = "build_async_tokio")]
    impl AsyncSyslogDestination for SyslogLocal
    {
        type SocketTap = AsyncTap::<tokio::net::UnixDatagram, Self>;

    }

    #[cfg(feature = "build_async_smol")]
    impl AsyncSyslogDestination for SyslogLocal
    {
        type SocketTap = AsyncTap::<smol::net::unix::UnixDatagram, Self>;
    }

    #[cfg(feature = "build_sync")]
    impl SyslogDestination for SyslogLocal
    {
        type SocketTap = Tap::<UnixDatagram, Self>;
        
        const DEST_TYPE: DestinationType = DestinationType::Local;
    }
}


#[cfg(target_family = "unix")]
pub use self::imp_syslog_local::SyslogLocal;

#[cfg(feature = "build_ext_net")]
pub mod imp_syslog_net
{
    use std::{fmt, net::{IpAddr, Ipv4Addr, SocketAddr, TcpStream, UdpSocket}, time::Duration};

    use crate::{common, error::SyRes, map_error, SyslogDestMsg};

    use super::*;

    /// A remote syslog server which works over plain TCP.
    #[derive(Debug, Clone)]
    pub struct SyslogNetTcp
    {
        /// Remote address in format host:port
        remote_addr: SocketAddr,

        /// Local bind address in format host:port.
        bind_addr: SocketAddr,

        /// A optional connection timeout.
        conn_timeout: Option<Duration>
    }

    impl fmt::Display for SyslogNetTcp
    {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 
        {
            write!(f, "remote_addr: {}, bind_addr: {}, conn_timeout: {:?}", self.remote_addr, 
                self.bind_addr, self.conn_timeout)
        }
    }

    impl SyslogNetTcp
    {
        /// Creates a new TCP connection to syslog server.
        /// 
        /// # Arguments
        /// 
        /// * `dest_addr` - a server's address in form "ip:port"
        /// 
        /// * `local` - a local bind address in format "ip:port". Optional.
        ///     If not set, then will bind to 0.0.0.0 i.e OS specific.
        /// 
        /// * `conn_timeout` - an optional connection timeout. If not specified, then
        ///     TCP connection timeout (OS specific).
        pub 
        fn new<P>(remote: P, local: Option<P>, conn_timeout: Option<Duration>) -> SyRes<Self>
        where P: AsRef<str>
        {
            let remote_addr: SocketAddr = 
                remote
                    .as_ref()
                    .parse()
                    .map_err(|e| 
                        map_error!("failed parsing remote addr '{}', error: '{}'", remote.as_ref(), e)
                    )?;

            let bind_addr: SocketAddr = 
                if let Some(bind) = local
                {
                    bind
                        .as_ref()
                        .parse()
                        .map_err(|e| 
                            map_error!("failed parsing bind addr '{}', error: '{}'", remote.as_ref(), e)
                        )?
                }
                else
                {
                    SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0)
                };

            return Ok(
                Self
                {
                    remote_addr: remote_addr,
                    bind_addr: bind_addr,
                    conn_timeout: conn_timeout
                }
            );
        }

        #[inline]
        pub 
        fn get_remote_addr(&self) -> &SocketAddr
        {
            return &self.remote_addr;
        }

        #[inline]
        pub 
        fn get_bind_addr(&self) -> &SocketAddr
        {
            return &self.bind_addr;
        }

        #[inline]
        pub 
        fn get_conn_timeout(&self) -> Option<Duration>
        {
            return self.conn_timeout;
        }
    }

    impl SyslogDestMsg for SyslogNetTcp
    {
        fn get_max_msg_len() -> usize 
        {
            return common::RFC5424_TCP_MAX_PKT_LEN;
        }
    }

    #[cfg(feature = "build_async_tokio")]
    impl AsyncSyslogDestination for SyslogNetTcp
    {
        type SocketTap = AsyncTap::<tokio::net::TcpStream, Self>;
    }

    #[cfg(feature = "build_async_smol")]
    impl AsyncSyslogDestination for SyslogNetTcp
    {
        type SocketTap =  AsyncTap::<smol::net::TcpStream, Self>;
    }

    #[cfg(feature = "build_sync")]
    impl SyslogDestination for SyslogNetTcp
    {
        type SocketTap = Tap::<TcpStream, Self>;

        const DEST_TYPE: DestinationType = DestinationType::Network;
    }


    /// A remote syslog server which works over plain UDP.
    #[derive(Debug, Clone)]
    pub struct SyslogNetUdp
    {
        remote_addr: SocketAddr,
        bind_addr: SocketAddr
    }

    impl fmt::Display for SyslogNetUdp
    {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 
        {
            write!(f, "remote_addr: {}, bind_addr: {}", self.remote_addr, 
                self.bind_addr)
        }
    }

    impl SyslogNetUdp
    {
        /// Creates a new UDP connection to syslog server.
        /// 
        /// # Arguments
        /// 
        /// * `dest_addr` - a server's address in form "ip:port"
        /// 
        /// * `local` - a local bind address in format "ip:port". Optional.
        ///     If not set, then will bind to 0.0.0.0 i.e OS specific.
        pub 
        fn new<P>(dest_addr: P, local: Option<P>) -> SyRes<Self>
        where P: AsRef<str>
        {
            let remote_addr: SocketAddr = 
                dest_addr
                    .as_ref()
                    .parse()
                    .map_err(|e| 
                        map_error!("failed parsing remote addr '{}', error: '{}'", dest_addr.as_ref(), e)
                    )?;

            let bind_addr: SocketAddr = 
                if let Some(bind) = local
                {
                    bind
                        .as_ref()
                        .parse()
                        .map_err(|e| 
                            map_error!("failed parsing bind addr '{}', error: '{}'", dest_addr.as_ref(), e)
                        )?
                }
                else
                {
                    SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0)
                };

            return Ok(
                Self
                {
                    remote_addr: remote_addr,
                    bind_addr: bind_addr
                }
            );
        }

        #[inline]
        pub 
        fn get_remote_addr(&self) -> &SocketAddr
        {
            return &self.remote_addr;
        }

        #[inline]
        pub 
        fn get_bind_addr(&self) -> &SocketAddr
        {
            return &self.bind_addr;
        }
    }

    impl SyslogDestMsg for SyslogNetUdp
    {
        fn get_max_msg_len() -> usize 
        {
            return common::RFC5424_UDP_MAX_PKT_LEN;
        }
    }

    #[cfg(feature = "build_async_tokio")]
    impl AsyncSyslogDestination for SyslogNetUdp
    {
        type SocketTap = AsyncTap::<tokio::net::UdpSocket, Self>;
    }

    #[cfg(feature = "build_async_smol")]
    impl AsyncSyslogDestination for SyslogNetUdp
    {
        type SocketTap = AsyncTap::<smol::net::UdpSocket, Self>;
    }

    #[cfg(feature = "build_sync")]
    impl SyslogDestination for SyslogNetUdp
    {
        type SocketTap = Tap::<UdpSocket, Self>;

        const DEST_TYPE: DestinationType = DestinationType::Network;
    }
}

#[cfg(feature = "build_ext_net")]
pub use self::imp_syslog_net::SyslogNetTcp;

#[cfg(feature = "build_ext_net")]
pub use self::imp_syslog_net::SyslogNetUdp;

#[cfg(feature = "build_ext_file")]
pub mod imp_syslog_file
{
    use std::{fmt, fs::File, path::{Path, PathBuf}};

    use crate::{common};

    use super::*;

    /// A writer to local file. NO syslog server is required.
    #[derive(Debug, Clone)]
    pub struct SyslogFile
    {
        /// A path to text file on the file system.
        file_path: PathBuf
    }

    impl fmt::Display for SyslogFile
    {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 
        {
            write!(f, "<file> path: {}", self.file_path.display())
        }
    }

    impl SyslogFile
    {
        /// Sets where to create or open file and write there.
        pub 
        fn new<P: Into<PathBuf>>(dest_path: P) -> Self
        {
            return
                Self
                {
                    file_path: dest_path.into() 
                };
        }

        #[inline]
        pub 
        fn get_path(&self) -> &Path
        {
            return self.file_path.as_path();
        }
    }

    impl SyslogDestMsg for SyslogFile
    {
        fn get_max_msg_len() -> usize 
        {
            return 65535;
        }
    }

    #[cfg(feature = "build_async_tokio")]
    impl AsyncSyslogDestination for SyslogFile
    {
        type SocketTap = AsyncTap::<tokio::fs::File, Self>;
    }

    #[cfg(feature = "build_async_smol")]
    impl AsyncSyslogDestination for SyslogFile
    {
        type SocketTap = AsyncTap::<smol::fs::File, Self>;
    }

    #[cfg(feature = "build_sync")]
    impl SyslogDestination for SyslogFile
    {
        type SocketTap =  Tap::<File, Self>;

        const DEST_TYPE: DestinationType = DestinationType::File;
    }
}

#[cfg(feature = "build_ext_file")]
pub use self::imp_syslog_file::SyslogFile;

#[cfg(feature = "build_ext_tls")]
pub mod imp_syslog_tls
{
    use std::fmt;
    use std::{net::{IpAddr, Ipv4Addr, SocketAddr, TcpStream}, sync::Arc, time::Duration};

    #[cfg(feature = "build_sync")]
use rustls::lock::Mutex;
    use rustls::{pki_types::{CertificateDer, ServerName}, ClientConfig, ClientConnection, RootCertStore, StreamOwned};

    use crate::{common, error::SyRes, map_error};

    use super::*;

    /// A remote syslog server with encrypted (TLS) over TCP connection.
    #[derive(Debug, Clone)]
    pub struct SyslogTls
    {
        /// A remote address in format host:port.
        remote_addr: SocketAddr,

        /// A local address to bind in format host:port.
        bind_addr: SocketAddr,

        /// A remote server name.
        serv_name: ServerName<'static>,

        /// A pre-configuret config.
        client_config: Arc<ClientConfig>,

        /// An optional connection time out. If not specified, OS (TCP) default.
        conn_timeout: Option<Duration>,
    }

    impl fmt::Display for SyslogTls
    {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 
        {
            write!(f, "remote: {}, bind: {}, serv_name: {:?}, client: {:?}, conn_timeout: {:?}", self.remote_addr, 
                self.bind_addr, self.serv_name, self.client_config, self.conn_timeout)
        }
    }

    impl SyslogTls
    {
        /// Creates a new TLS over TCP connection to syslog server. 
        /// 
        /// # Arguments
        /// 
        /// * `remote` - an (ip:port) to specific server.
        /// 
        /// * `local` - an (ip:port) to local bind address. Optional. If not set,
        ///     OS specific behaviour.
        /// 
        /// * `serv_name` - a FQDN of server (should match with `root_cert`)
        /// 
        /// * `root_cert` - root certificates able to provide a root-of-trust for connection authentication.
        /// 
        /// * `conn_timeout` - an optional connection timeout. If not specified, will be OS (TCP) connection
        ///     timeout default.
        pub 
        fn new<P>(remote: P, local: Option<P>, serv_name: impl Into<String>, 
            root_cert: Vec<u8>, conn_timeout: Option<Duration>) -> SyRes<Self>
        where P: AsRef<str>
        {
            use rustls::pki_types::pem::PemObject;

            let server_name = serv_name.into();

            let remote_addr: SocketAddr = 
                remote
                    .as_ref()
                    .parse()
                    .map_err(|e| 
                        map_error!("failed parsing remote addr '{}', error: '{}'", remote.as_ref(), e)
                    )?;

            let cert = 
                CertificateDer::from_pem_slice(root_cert.as_slice())
                    .map_err(|e|
                        map_error!("certificate parse error: '{}'", e)
                    )?;

            let mut root_store = RootCertStore::empty();
            root_store
                .add(cert)
                .map_err(|e|
                    map_error!("can not add root ceritficate, error: '{}'", e)
                )?;

            let serv_name = 
                server_name
                    .try_into()
                    .map_err(|e|
                        map_error!("server name error: '{}'", e)
                    )?;

            let client_config = 
                rustls::ClientConfig::builder()
                    .with_root_certificates(root_store)
                    .with_no_client_auth();

            let bind_addr: SocketAddr = 
                if let Some(bind) = local
                {
                    bind
                        .as_ref()
                        .parse()
                        .map_err(|e| 
                            map_error!("failed parsing bind addr '{}', error: '{}'", remote.as_ref(), e)
                        )?
                }
                else
                {
                    SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0)
                };

            return Ok(
                Self
                {
                    remote_addr: remote_addr, 
                    bind_addr: bind_addr,
                    serv_name: serv_name,
                    client_config: Arc::new(client_config),
                    conn_timeout: conn_timeout,
                }
            );
        }

        #[inline]
        pub
        fn get_remote_addr(&self) -> &SocketAddr
        {
            return &self.remote_addr;
        }

        #[inline]
        pub 
        fn get_bind_addr(&self) -> &SocketAddr
        {
            return &self.bind_addr;
        }

        #[inline]
        pub 
        fn get_serv_name(&self) -> ServerName<'static>
        {
            return self.serv_name.clone();
        }

        #[inline]
        pub 
        fn get_client_config(&self) -> Arc<ClientConfig>
        {
            return self.client_config.clone();
        }

        #[inline]
        pub 
        fn get_get_conn_timeout(&self) -> Option<Duration>
        {
            return self.conn_timeout.clone();
        }
    }

    impl SyslogDestMsg for SyslogTls
    {
        fn get_max_msg_len() -> usize 
        {
             return common::RFC5424_TCP_MAX_PKT_LEN;
        }
    }

    #[cfg(feature = "build_async_tokio")]
    impl AsyncSyslogDestination for SyslogTls
    {
        type SocketTap = AsyncTap::<tokio_rustls::client::TlsStream<tokio::net::TcpStream>, Self>;
    }

    #[cfg(feature = "build_async_smol")]
    impl AsyncSyslogDestination for SyslogTls
    {
        type SocketTap = AsyncTap::<futures_rustls::client::TlsStream<smol::net::TcpStream>, Self>;
    }

    #[cfg(feature = "build_sync")]
    impl SyslogDestination for SyslogTls
    {
        type SocketTap = Tap::<Mutex<StreamOwned<ClientConnection, TcpStream>>, Self>;

        const DEST_TYPE: DestinationType = DestinationType::Network;
    }
}

#[cfg(feature = "build_ext_tls")]
pub use self::imp_syslog_tls::SyslogTls;
