v9.7.2018.0421   支持运行时修改DAL连接字符串
大石头 编写于 2018-04-21 14:00:47
X
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
using NewLife.Xml;

namespace NewLife.Net.UPnP
{
    /// <summary>因特网网关设备</summary>
    [Serializable, XmlRoot("root", Namespace = "urn:schemas-upnp-org:device-1-0")]
    public class InternetGatewayDevice
    {
        #region 属性
        private SpecVersion _specVersion;
        /// <summary>UPnP 设备架构版本</summary>
        [XmlElement("specVersion")]
        public SpecVersion specVersion
        {
            get { return _specVersion; }
            set { _specVersion = value; }
        }

        private String _URLBase;
        /// <summary>URL</summary>
        [XmlElement("URLBase")]
        public String URLBase
        {
            get { return _URLBase; }
            set { _URLBase = value; }
        }

        private Device _device;
        /// <summary>设备</summary>
        [XmlElement("device")]
        public Device device
        {
            get { return _device; }
            set { _device = value; }
        }
        #endregion

        #region 扩展属性
        private String _ServerHost;
        /// <summary>UPNP设备IP</summary>
        [XmlIgnore]
        public String ServerHost
        {
            get
            {
                if (String.IsNullOrEmpty(_ServerHost)) GetHOSTAndPort();
                return _ServerHost;

            }
            set { _ServerHost = value; }
        }

        private Int32 _ServerPort;
        /// <summary>UPNP设备端口</summary>
        [XmlIgnore]
        public Int32 ServerPort
        {
            get
            {
                if (_ServerPort == 0) GetHOSTAndPort();
                return _ServerPort;
            }
            set { _ServerPort = value; }
        }
        #endregion

        #region 方法
        /// <summary>分析URLBase 并赋值HOST和Port</summary>
        public void GetHOSTAndPort()
        {
            //Regex Regex = new Regex(@"\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}:\d{1,5}");
            //String HostAndPortStr = Regex.Match(URLBase).Value;
            //if (!String.IsNullOrEmpty(HostAndPortStr))
            //{
            //    String[] Arr = HostAndPortStr.Split(new Char[] { ':' });
            //    ServerHOST = Arr[0];
            //    ServerPort = Convert.ToInt32(Arr[1]);
            //}
            //else
            //    throw new Exception("UPNP设备IP与端口获取出错!");

            if (String.IsNullOrEmpty(URLBase)) return;

            var uri = new Uri(URLBase);
            ServerHost = uri.Host;
            ServerPort = uri.Port;
        }

        /// <summary>已重载。</summary>
        /// <returns></returns>
        public override String ToString()
        {
            var d = device;
            if (d != null)
                return String.Format("{0} {1}", d, ServerHost);
            else
                return ServerHost;
        }
        #endregion

        #region 操作
        /// <summary>添加映射端口</summary>
        /// <param name="remoteHost">远程主机</param>
        /// <param name="externalPort">外部端口</param>
        /// <param name="protocol">TCP或UDP</param>
        /// <param name="internalPort">内部端口</param>
        /// <param name="internalClient">本地IP地址</param>
        /// <param name="enabled">是否启用[0,1]</param>
        /// <param name="description">端口映射的描述</param>
        /// <param name="duration">映射的持续时间,用0表示永久</param>
        /// <returns>bool</returns>
        public Boolean Add(String remoteHost, Int32 externalPort, String protocol, Int32 internalPort, String internalClient, Int32 enabled, String description, Int32 duration)
        {
            var entity = new PortMappingEntry();
            entity.Name = "AddPortMapping";
            entity.RemoteHost = remoteHost;
            entity.ExternalPort = externalPort;
            entity.Protocol = protocol;
            entity.InternalClient = internalClient;
            entity.InternalPort = internalPort;
            entity.Enabled = enabled;
            entity.Description = description;
            entity.LeaseDuration = duration;

            var response = Request<PortMappingEntry>(entity);
            return response != null;
        }

        /// <summary>添加映射端口</summary>
        /// <param name="host">本地主机</param>
        /// <param name="port">端口(内外一致)</param>
        /// <param name="protocol">协议</param>
        /// <param name="description">描述</param>
        /// <returns></returns>
        public Boolean Add(String host, Int32 port, String protocol, String description)
        {
            return Add(null, port, protocol, port, host, 1, description, 0);
        }

        /// <summary>删除端口映射</summary>
        /// <param name="remoteHost">远程主机</param>
        /// <param name="externalPort">外部端口</param>
        /// <param name="protocol">TCP或UDP</param>
        /// <returns></returns>
        public Boolean Delete(String remoteHost, Int32 externalPort, String protocol)
        {
            var entity = new PortMappingEntry();
            entity.Name = "DeletePortMapping";
            entity.RemoteHost = remoteHost;
            entity.ExternalPort = externalPort;
            entity.Protocol = protocol;

            var response = Request<PortMappingEntry>(entity);
            return response != null;
        }
        #endregion

        #region 查找
        /// <summary>获取指定设备的端口映射信息</summary>
        /// <param name="remoteHost">远程主机</param>
        /// <param name="externalPort">外部端口</param>
        /// <param name="protocol">TCP/UDP</param>
        /// <returns></returns>
        public PortMappingEntry GetMapByPortAndProtocol(String remoteHost, Int32 externalPort, String protocol)
        {
            var entity = new PortMappingEntry();
            entity.Name = "GetSpecificPortMappingEntry";
            entity.RemoteHost = remoteHost;
            entity.ExternalPort = externalPort;
            entity.Protocol = protocol;

            var response = Request<PortMappingEntry>(entity);
            return response;
        }

        /// <summary>获取指定设备的端口映射信息</summary>
        /// <param name="index">索引</param>
        /// <returns></returns>
        public PortMappingEntry GetMapByIndex(Int32 index)
        {
            var entity = new PortMappingEntryRequest();
            entity.Name = "GetGenericPortMappingEntry";
            entity.NewPortMappingIndex = index;

            var response = Request<PortMappingEntry>(entity);
            return response;
        }

        /// <summary>获取指定设备的所有端口映射信息</summary>
        /// <returns></returns>
        public IEnumerable<PortMappingEntry> GetMapByIndexAll()
        {
            var n = 0;
            while (true)
            {
                PortMappingEntry item;
                try
                {
                    item = GetMapByIndex(n++);
                }
                catch { break; }

                if (item == null) break;

                yield return item;
            }
        }
        #endregion

        #region SOAP
        /// <summary>向设备发送指令</summary>
        /// <typeparam name="TResponse"></typeparam>
        /// <param name="action"></param>
        /// <returns></returns>
        TResponse Request<TResponse>(UPnPAction action) where TResponse : UPnPAction<TResponse>, new()
        {
            if (device == null || action == null) return null;

            var service = device.GetWANIPService();
            if (service == null) return null;

            var uri = new Uri(String.Format("http://{0}:{1}{2}", ServerHost, ServerPort, service.controlURL));

            var xml = action.ToSoap(service.serviceType);
            xml = SOAPRequest(uri, service.serviceType + "#" + action.Name, xml);

            var response = UPnPAction<TResponse>.FromXml(xml);

            return response;
        }

        /// <summary>SOAP头部</summary>
        const String SOAP_HEADER = "" +
            "POST {0} HTTP/1.1\r\n" +
            "HOST: {1}\r\n" +
            "SOAPACTION: \"{2}\"\r\n" +
            "CONTENT-TYPE: text/xml ; charset=\"utf-8\"\r\n" +
            "Content-Length: {3}" +
            "\r\n\r\n";
        ///// <summary>
        ///// SOAP主体
        ///// </summary>
        //const String SOAP_BODY = "" +
        //    "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" +
        //    "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n" +
        //    "<s:Body>\r\n" +
        //    "{0}\r\n" +
        //    "</s:Body>\r\n" +
        //    "</s:Envelope>\r\n";

        /// <summary>发送SOAP请求,发送xml,返回xml</summary>
        /// <param name="uri"></param>
        /// <param name="action"></param>
        /// <param name="xml"></param>
        /// <returns></returns>
        static String SOAPRequest(Uri uri, String action, String xml)
        {
            //String body = String.Format(SOAP_BODY, xml);
            var header = String.Format(SOAP_HEADER, uri.PathAndQuery, uri.Host + ":" + uri.Port, action, Encoding.UTF8.GetByteCount(xml));

            var client = new TcpSession();
            var session = client as ISocketSession;
            try
            {
                //client.Connect(uri.Host, uri.Port);
                client.Remote.Host = uri.Host;
                client.Remote.Port = uri.Port;
                session.Send(header + xml);

                var response = session.ReceiveString();
                if (String.IsNullOrEmpty(response)) return null;

                var p = response.IndexOf("\r\n\r\n");
                if (p < 0) return null;

                response = response.Substring(p).Trim();
                if (String.IsNullOrEmpty(response)) response = session.ReceiveString();

                //Envelope env = null;
                //XmlSerializer serial = new XmlSerializer(typeof(Envelope));
                //using (var reader = new StringReader(response))
                //{
                //    env = serial.Deserialize(reader) as Envelope;
                //}
                var env = response.ToXmlEntity<Envelope>();
                if (env == null || env.Body == null) return null;

                if (!String.IsNullOrEmpty(env.Body.Fault)) throw env.Body.ThrowException();

                return env.Body.Xml;
            }
            finally { client.Dispose(); }
        }
        #endregion
    }
}