public UPnPRelayDevice(UPnPDevice device, CpGateKeeper _CP)
        {
            OpenSource.Utilities.InstanceTracker.Add(this);
            ILCB        = new InvokeResponseHandler(InvokeResponseSink);
            CP          = _CP;
            ProxyDevice = UPnPDevice.CreateRootDevice(750, double.Parse(device.Version), "");
            ProxyDevice.UniqueDeviceName = Guid.NewGuid().ToString();

            ProxyDevice.HasPresentation = false;
            ProxyDevice.FriendlyName    = "*" + device.FriendlyName;
            ProxyDevice.Manufacturer    = device.Manufacturer;
            ProxyDevice.ManufacturerURL = device.ManufacturerURL;
            ProxyDevice.ModelName       = device.ModelName;
            ProxyDevice.DeviceURN       = device.DeviceURN;


            foreach (UPnPService S in device.Services)
            {
                UPnPService S2 = (UPnPService)S.Clone();
                foreach (UPnPAction A in S2.Actions)
                {
                    A.ParentService = S2;
                    A.SpecialCase  += new UPnPAction.SpecialInvokeCase(InvokeSink);
                }

                UPnPDebugObject dbg = new UPnPDebugObject(S2);

                dbg.SetField("SCPDURL", "_" + S2.ServiceID + "_scpd.xml");
                dbg.SetProperty("ControlURL", "_" + S2.ServiceID + "_control");
                dbg.SetProperty("EventURL", "_" + S2.ServiceID + "_event");
                ProxyDevice.AddService(S2);
            }

            UDNTable[device.UniqueDeviceName]             = ProxyDevice;
            ReverseUDNTable[ProxyDevice.UniqueDeviceName] = device.UniqueDeviceName;
            foreach (UPnPDevice _ed in device.EmbeddedDevices)
            {
                ProcessDevice(_ed);
            }
        }
        private void ProcessDevice(UPnPDevice device)
        {
            UPnPDevice d = UPnPDevice.CreateEmbeddedDevice(double.Parse(device.Version), Guid.NewGuid().ToString());

            d.FriendlyName     = "*" + device.FriendlyName;
            d.Manufacturer     = device.Manufacturer;
            d.ManufacturerURL  = device.ManufacturerURL;
            d.ModelDescription = device.ModelDescription;
            d.ModelURL         = device.ModelURL;
            d.DeviceURN        = device.DeviceURN;
            UDNTable[device.UniqueDeviceName]   = d;
            ReverseUDNTable[d.UniqueDeviceName] = device.UniqueDeviceName;

            foreach (UPnPService S in device.Services)
            {
                UPnPService S2 = (UPnPService)S.Clone();
                foreach (UPnPAction A in S2.Actions)
                {
                    A.ParentService = S2;
                    A.SpecialCase  += new UPnPAction.SpecialInvokeCase(InvokeSink);
                }

                UPnPDebugObject dbg = new UPnPDebugObject(S2);

                dbg.SetField("SCPDURL", "_" + S2.ServiceID + "_scpd.xml");
                dbg.SetProperty("ControlURL", "_" + S2.ServiceID + "_control");
                dbg.SetProperty("EventURL", "_" + S2.ServiceID + "_event");
                d.AddService(S2);
            }
            ((UPnPDevice)UDNTable[device.ParentDevice.UniqueDeviceName]).AddDevice(d);

            foreach (UPnPDevice _ed in device.EmbeddedDevices)
            {
                ProcessDevice(_ed);
            }
        }
        public override void Start(UPnPDevice device)
        {
            if (!Enabled)
            {
                return;
            }

            UPnPDevice dv = device;

            while (dv.ParentDevice != null)
            {
                dv = dv.ParentDevice;
            }

            state = UPnPTestStates.Running;
            UPnPService[] _S = device.GetServices("urn:");

            foreach (UPnPService s in _S)
            {
                bool ok = false;
                foreach (UPnPStateVariable v in s.GetStateVariables())
                {
                    if (v.SendEvent)
                    {
                        ok = true;
                        break;
                    }
                }
                if (ok)
                {
                    UPnPDebugObject d        = new UPnPDebugObject(s);
                    Uri             EventUri = new Uri((string)d.GetField("__eventurl"));

                    IPEndPoint  dest = new IPEndPoint(IPAddress.Parse(EventUri.Host), EventUri.Port);
                    HTTPMessage R    = new HTTPMessage();
                    R.Directive    = "SUBSCRIBE";
                    R.DirectiveObj = HTTPMessage.UnEscapeString(EventUri.PathAndQuery);
                    R.AddTag("Host", dest.ToString());
                    R.AddTag("Callback", "<http://" + dv.InterfaceToHost.ToString() + ":" + NetworkInfo.GetFreePort(10000, 50000, dv.InterfaceToHost).ToString() + ">");
                    //R.AddTag("Callback","<http://127.0.0.1:55555>");
                    R.AddTag("NT", "upnp:event");
                    R.AddTag("Timeout", "Second-15");

                    System.Console.WriteLine(R.GetTag("Callback"));

                    MRE.Reset();
                    SID = "";
                    StartCountDown(30);
                    HTTPRequest rq = new HTTPRequest();
                    rq.OnResponse += new HTTPRequest.RequestHandler(SubscribeSink);

                    AddHTTPMessage(R);

                    rq.PipelineRequest(dest, R, s);
                    MRE.WaitOne(30000, false);
                    AbortCountDown();

                    if (SID == "")
                    {
                        AddEvent(LogImportance.Critical, "Subscribe", "SUBSCRIBE: " + s.ServiceURN + " << FAILED >>");
                        AddEvent(LogImportance.Remark, "Subscribe", "Aborting tests");

                        result = "Subscription test failed.";                         // TODO
                        state  = UPnPTestStates.Failed;
                        return;
                    }
                    else
                    {
                        AddEvent(LogImportance.Remark, "Subscribe", "SUBSCRIBE: " + s.ServiceURN + " << OK >>");

                        // Renew Test
                        R              = new HTTPMessage();
                        R.Directive    = "SUBSCRIBE";
                        R.DirectiveObj = HTTPMessage.UnEscapeString(EventUri.PathAndQuery);
                        R.AddTag("Host", dest.ToString());
                        R.AddTag("SID", SID);
                        R.AddTag("Timeout", "Second-15");
                        StartCountDown(30);
                        SID = "";
                        MRE.Reset();

                        AddHTTPMessage(R);

                        rq             = new HTTPRequest();
                        rq.OnResponse += new HTTPRequest.RequestHandler(SubscribeSink);
                        rq.PipelineRequest(dest, R, s);

                        MRE.WaitOne(30000, false);
                        AbortCountDown();

                        if (SID == "")
                        {
                            AddEvent(LogImportance.Critical, "Subscribe", "SUBSCRIBE (Renew): " + s.ServiceURN + " << FAILED >>");
                            AddEvent(LogImportance.Remark, "Subscribe", "Aborting tests");

                            result = "Subscription test failed.";                             // TODO
                            state  = UPnPTestStates.Failed;
                            return;
                        }
                        else
                        {
                            AddEvent(LogImportance.Remark, "Subscribe", "SUBSCRIBE (Renew): " + s.ServiceURN + " << OK >>");

                            // Cancel
                            R              = new HTTPMessage();
                            R.Directive    = "UNSUBSCRIBE";
                            R.DirectiveObj = HTTPMessage.UnEscapeString(EventUri.PathAndQuery);
                            R.AddTag("Host", dest.ToString());
                            R.AddTag("SID", SID);

                            StartCountDown(30);
                            SID = "";
                            MRE.Reset();
                            rq             = new HTTPRequest();
                            rq.OnResponse += new HTTPRequest.RequestHandler(CancelSink);

                            AddHTTPMessage(R);

                            rq.PipelineRequest(dest, R, s);

                            MRE.WaitOne(30000, false);
                            AbortCountDown();

                            if (SID == "")
                            {
                                AddEvent(LogImportance.Critical, "Subscribe", "UNSUBSCRIBE: " + s.ServiceURN + " << FAILED >>");
                                AddEvent(LogImportance.Remark, "Subscribe", "Aborting tests");

                                result = "Subscription test failed.";
                                state  = UPnPTestStates.Failed;
                                return;
                            }
                            else
                            {
                                AddEvent(LogImportance.Remark, "Subscribe", "UNSUBSCRIBE: " + s.ServiceURN + " << OK >>");
                            }

                            /* Test for duplicate SID
                             * as well as initial events */

                            EventTable.Clear();
                            NumEvents = 0;
                            foreach (UPnPStateVariable V in s.GetStateVariables())
                            {
                                if (V.SendEvent)
                                {
                                    ++NumEvents;
                                    EventTable[V.Name] = false;
                                    V.OnModified      -= new UPnPStateVariable.ModifiedHandler(StateVarModifiedSink);
                                    V.OnModified      += new UPnPStateVariable.ModifiedHandler(StateVarModifiedSink);
                                }
                            }
                            if (EventTable.Count > 0)
                            {
                                MRE.Reset();
                                s.OnSubscribe -= new UPnPService.UPnPEventSubscribeHandler(OnSubscribeSink);
                                s.OnSubscribe += new UPnPService.UPnPEventSubscribeHandler(OnSubscribeSink);
                                s.UnSubscribe(null);
                                foreach (UPnPStateVariable V in s.GetStateVariables())
                                {
                                    V.Clear();
                                }
                                s.Subscribe(120, null);
                                MRE.WaitOne(30000, false);
                                if (SID == "")
                                {
                                    // Subscribe Failed
                                    AddEvent(LogImportance.Critical, "Subscribe", "SUBSCRIBE(2): " + s.ServiceURN + " << FAILED >>");
                                    AddEvent(LogImportance.Remark, "Subscribe", "Aborting tests");

                                    result = "Subscription test failed.";
                                    state  = UPnPTestStates.Failed;
                                    return;
                                }
                                else
                                {
                                    if (SID == null)
                                    {
                                        // Duplicate SID
                                        // Subscribe Failed
                                        AddEvent(LogImportance.Critical, "Subscribe", "SUBSCRIBE(2): " + s.ServiceURN + " << FAILED, duplicate SID >>");
                                        AddEvent(LogImportance.Remark, "Subscribe", "Aborting tests");

                                        result = "Subscription test failed.";
                                        state  = UPnPTestStates.Failed;
                                        return;
                                    }
                                    else
                                    {
                                        // Check Hashtable
                                        IDictionaryEnumerator de = EventTable.GetEnumerator();
                                        bool OK = true;
                                        while (de.MoveNext())
                                        {
                                            if ((bool)de.Value == false)
                                            {
                                                // No Event Received
                                                OK = false;
                                                AddEvent(LogImportance.Critical, "Subscribe", "   StateVariable: " + (string)de.Key + " >> Event NOT received");
                                            }
                                            else
                                            {
                                                // OK
                                                AddEvent(LogImportance.Remark, "Subscribe", "   StateVariable: " + (string)de.Key + " >> Event OK");
                                            }
                                        }
                                        if (OK == false)
                                        {
                                            AddEvent(LogImportance.Critical, "Subscribe", "SUBSCRIBE(2): " + s.ServiceURN + " << FAILED, Did not receive all events >>");
                                            AddEvent(LogImportance.Remark, "Subscribe", "Aborting tests");

                                            result = "Subscription test failed.";
                                            state  = UPnPTestStates.Failed;
                                            return;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

            result = "Subscribe Tests OK";
            state  = UPnPTestStates.Pass;
        }