コード例 #1
0
    /// <summary>
    /// Sends a NOTIFY packet "ssdp:update" to all UPnP endpoints.
    /// </summary>
    /// <param name="NT">Notification type.</param>
    /// <param name="USN">Unique Service Name.</param>
    /// <param name="rootDevice">Root device for that the message should be send.</param>
    public void SendMessage(string NT, string USN, DvDevice rootDevice)
    {
      SimpleHTTPRequest response = new SimpleHTTPRequest("NOTIFY", "*");
      response.SetHeader("NT", NT);
      response.SetHeader("NTS", "ssdp:update");
      response.SetHeader("USN", USN);
      response.SetHeader("BOOTID.UPNP.ORG", _lastBootId.ToString());
      response.SetHeader("NEXTBOOTID.UPNP.ORG", _nextBootId.ToString());
      // Currently, we don't support SEARCHPORT.UPNP.ORG function and header

      foreach (EndpointConfiguration config in _serverData.UPnPEndPoints)
      {
        if (config.AddressFamily == AddressFamily.InterNetworkV6)
        {
          response.SetHeader("OPT", "\"http://schemas.upnp.org/upnp/1/0/\"; ns=01");
          response.SetHeader("01-NLS", _serverData.BootId.ToString());
        }
        response.SetHeader("CONFIGID.UPNP.ORG", config.ConfigId.ToString());
        IPEndPoint ep = new IPEndPoint(config.SSDPMulticastAddress, UPnPConsts.SSDP_MULTICAST_PORT);
        response.SetHeader("HOST", NetworkHelper.IPEndPointToString(ep));
        if (config.SSDPUsesSpecialSearchPort)
          response.SetHeader("SEARCHPORT.UPNP.ORG", config.SSDPSearchPort.ToString());
        response.SetHeader("LOCATION", config.GetRootDeviceDescriptionURL(rootDevice));
        byte[] bytes = response.Encode();
        NetworkHelper.MulticastMessage(config.SSDP_UDP_UnicastSocket, config.SSDPMulticastAddress, bytes);
      }
    }
コード例 #2
0
 /// <summary>
 /// Parses the HTTP request out of the given <paramref name="stream"/>.
 /// </summary>
 /// <param name="stream">HTTP data stream to parse.</param>
 /// <param name="result">Returns the parsed HTTP request instance.</param>
 /// <exception cref="MediaPortal.Utilities.Exceptions.InvalidDataException">If the given <paramref name="stream"/> is malformed.</exception>
 public static void Parse(Stream stream, out SimpleHTTPRequest result)
 {
   result = new SimpleHTTPRequest();
   string firstLine;
   result.ParseHeaderAndBody(stream, out firstLine);
   string[] elements = firstLine.Split(' ');
   if (elements.Length != 3)
     throw new InvalidDataException("Invalid HTTP request header starting line '{0}'", firstLine);
   string httpVersion = elements[2];
   if (httpVersion != "HTTP/1.0" && httpVersion != "HTTP/1.1")
     throw new InvalidDataException("Invalid HTTP request header starting line '{0}'", firstLine);
   result._method = elements[0];
   result._param = elements[1];
   result._httpVersion = httpVersion;
 }
コード例 #3
0
        /// <summary>
        /// Parses the HTTP request out of the given <paramref name="stream"/>.
        /// </summary>
        /// <param name="stream">HTTP data stream to parse.</param>
        /// <param name="result">Returns the parsed HTTP request instance.</param>
        /// <exception cref="MediaPortal.Utilities.Exceptions.InvalidDataException">If the given <paramref name="stream"/> is malformed.</exception>
        public static void Parse(Stream stream, out SimpleHTTPRequest result)
        {
            result = new SimpleHTTPRequest();
            string firstLine;

            result.ParseHeaderAndBody(stream, out firstLine);
            string[] elements = firstLine.Split(' ');
            if (elements.Length != 3)
            {
                throw new InvalidDataException("Invalid HTTP request header starting line '{0}'", firstLine);
            }
            string httpVersion = elements[2];

            if (httpVersion != "HTTP/1.0" && httpVersion != "HTTP/1.1")
            {
                throw new InvalidDataException("Invalid HTTP request header starting line '{0}'", firstLine);
            }
            result._method      = elements[0];
            result._param       = elements[1];
            result._httpVersion = httpVersion;
        }
コード例 #4
0
    protected void SendMulticastEventNotification(DvService service, IEnumerable<DvStateVariable> variables)
    {
      DvDevice device = service.ParentDevice;
      EventingState eventingState = _serverData.GetMulticastEventKey(service);
      // First cluster variables by multicast event level so we can put variables of the same event level into a single message
      IDictionary<string, ICollection<DvStateVariable>> variablesByLevel =
          new Dictionary<string, ICollection<DvStateVariable>>();
      foreach (DvStateVariable variable in variables)
      {
        ICollection<DvStateVariable> variablesCollection;
        if (!variablesByLevel.TryGetValue(variable.MulticastEventLevel, out variablesCollection))
          variablesByLevel[variable.MulticastEventLevel] = variablesCollection = new List<DvStateVariable>();
        variablesCollection.Add(variable);
      }
      foreach (KeyValuePair<string, ICollection<DvStateVariable>> varByLevel in variablesByLevel)
      {
        // Use a maximum cluster size of GENA_MAX_MULTICAST_EVENT_VAR_COUNT to keep UDP message small
        ICollection<IList<DvStateVariable>> variableClusters = CollectionUtils.Cluster(
            varByLevel.Value, UPnPConsts.GENA_MAX_MULTICAST_EVENT_VAR_COUNT);
        foreach (IList<DvStateVariable> cluster in variableClusters)
        {
          foreach (DvStateVariable variable in cluster)
            eventingState.UpdateModerationData(variable);
          eventingState.IncEventKey();
          byte[] bodyData = UPnPConsts.UTF8_NO_BOM.GetBytes(GENAMessageBuilder.BuildEventNotificationMessage(
            cluster, false)); // Albert TODO: Is it correct not to force the simple string equivalent for extended data types here?
          SimpleHTTPRequest request = new SimpleHTTPRequest("NOTIFY", "*");
          request.SetHeader("CONTENT-LENGTH", bodyData.Length.ToString());
          request.SetHeader("CONTENT-TYPE", "text/xml; charset=\"utf-8\"");
          request.SetHeader("USN", device.UDN + "::" + service.ServiceTypeVersion_URN);
          request.SetHeader("SVCID", service.ServiceId);
          request.SetHeader("NT", "upnp:event");
          request.SetHeader("NTS", "upnp:propchange");
          request.SetHeader("SEQ", eventingState.EventKey.ToString());
          request.SetHeader("LVL", varByLevel.Key);
          request.SetHeader("BOOTID.UPNP.ORG", _serverData.BootId.ToString());

          foreach (EndpointConfiguration config in _serverData.UPnPEndPoints)
          {
            IPEndPoint ep = new IPEndPoint(config.GENAMulticastAddress, UPnPConsts.GENA_MULTICAST_PORT);
            request.SetHeader("HOST", NetworkHelper.IPEndPointToString(ep));
            request.MessageBody = bodyData;
            byte[] bytes = request.Encode();
            NetworkHelper.SendData(config.GENA_UDP_Socket, ep, bytes, 1);
          }
        }
      }
    }
コード例 #5
0
 protected void HandleNotifyRequest(SimpleHTTPRequest header, EndpointConfiguration config, IPEndPoint remoteEndPoint)
 {
   if (header.Param != "*")
     // Invalid message
     return;
   HTTPVersion httpVersion;
   if (!HTTPVersion.TryParse(header.HttpVersion, out httpVersion))
     // Invalid message
     return;
   // HOST not interesting
   //string host = header["HOST"];
   string cacheControl = header["CACHE-CONTROL"];
   string location = header["LOCATION"];
   string server = header["SERVER"];
   // NT is not evaluated, we get all information from the USN header
   //string nt = header["NT"];
   string nts = header["NTS"];
   string usn = header["USN"];
   string bi = header["BOOTID.UPNP.ORG"];
   string ci = header["CONFIGID.UPNP.ORG"];
   string sp = header["SEARCHPORT.UPNP.ORG"];
   HandleNotifyPacket(config, remoteEndPoint, httpVersion, DateTime.Now.ToUniversalTime().ToString("R"), cacheControl, location, server, nts, usn, bi, ci, sp);
 }
コード例 #6
0
 protected void HandleUpdatePacket(SimpleHTTPRequest header, EndpointConfiguration config)
 {
   if (header.Param != "*")
     // Invalid message
     return;
   HTTPVersion httpVersion;
   if (!HTTPVersion.TryParse(header.HttpVersion, out httpVersion))
     // Invalid message
     return;
   // Host, NT, NTS, USN are not interesting
   //string host = header["HOST"];
   //string nt = header["NT"];
   //string nts = header["NTS"];
   string usn = header["USN"];
   //string location = header["LOCATION"];
   string bi = header["BOOTID.UPNP.ORG"];
   uint bootID;
   if (!uint.TryParse(bi, out bootID))
     // Invalid message
     return;
   string nbi = header["NEXTBOOTID.UPNP.ORG"];
   uint nextBootID;
   if (!uint.TryParse(nbi, out nextBootID))
     // Invalid message
     return;
   if (!usn.StartsWith("uuid:"))
     // Invalid usn
     return;
   int separatorIndex = usn.IndexOf("::");
   if (separatorIndex < 6) // separatorIndex == -1 or separatorIndex not after "uuid:" prefix with at least one char UUID
     // We only use messages containing a "::" substring and discard the "uuid:device-UUID" message
     return;
   string deviceUUID = usn.Substring(5, separatorIndex - 5);
   RootEntry rootEntry = GetRootEntryByContainedDeviceUUID(deviceUUID);
   if (rootEntry == null)
     return;
   if (rootEntry.BootID > bootID)
     // Invalid message
     return;
   bool fireDeviceRebooted = false;
   lock (_cpData.SyncObj)
   {
     if (rootEntry.BootID < bootID)
       // Device reboot
       fireDeviceRebooted = true;
     rootEntry.BootID = nextBootID;
   }
   if (fireDeviceRebooted)
     InvokeDeviceRebooted(rootEntry, false);
 }
コード例 #7
0
 protected void HandleSSDPRequest(SimpleHTTPRequest header, EndpointConfiguration config, IPEndPoint remoteEndPoint)
 {
   switch (header.Method)
   {
     case "NOTIFY":
       HandleNotifyRequest(header, config, remoteEndPoint);
       break;
     case "UPDATE":
       HandleUpdatePacket(header, config);
       break;
   }
 }
コード例 #8
0
 protected void MulticastSearchForST(string st)
 {
   SimpleHTTPRequest request = new SimpleHTTPRequest("M-SEARCH", "*");
   request.SetHeader("MAN", "\"ssdp:discover\"");
   request.SetHeader("MX", UPnPConfiguration.SEARCH_MX.ToString());
   request.SetHeader("ST", st);
   request.SetHeader("USER-AGENT", UPnPConfiguration.UPnPMachineInfoHeader);
   lock (_cpData.SyncObj)
   {
     foreach (EndpointConfiguration config in _cpData.Endpoints)
     {
       IPEndPoint ep = new IPEndPoint(config.SSDPMulticastAddress, UPnPConsts.SSDP_MULTICAST_PORT);
       request.SetHeader("HOST", NetworkHelper.IPEndPointToString(ep));
       Socket socket = config.SSDP_UDP_UnicastSocket;
       if (socket == null)
         continue;
       byte[] bytes = request.Encode();
       NetworkHelper.MulticastMessage(socket, config.SSDPMulticastAddress, bytes); // The server will send the answer to the same socket as we use to send
     }
   }
 }
コード例 #9
0
 protected void UnicastSearchForST(string st, IPEndPoint endPoint)
 {
   SimpleHTTPRequest request = new SimpleHTTPRequest("M-SEARCH", "*");
   request.SetHeader("HOST", NetworkHelper.IPEndPointToString(endPoint));
   request.SetHeader("MAN", "\"ssdp:discover\"");
   request.SetHeader("ST", st);
   request.SetHeader("USER-AGENT", UPnPConfiguration.UPnPMachineInfoHeader);
   lock (_cpData.SyncObj)
   {
     foreach (EndpointConfiguration config in _cpData.Endpoints)
     {
       if (config.AddressFamily != endPoint.AddressFamily)
         continue;
       Socket socket = config.SSDP_UDP_UnicastSocket;
       if (socket == null)
         return;
       byte[] bytes = request.Encode();
       NetworkHelper.SendData(socket, endPoint, bytes, 1); // The server will send the answer to the same socket as we use to send
       return;
     }
   }
 }
コード例 #10
0
 /// <summary>
 /// Handles SSDP M-SEARCH requests over UDP multicast and unicast.
 /// </summary>
 /// <param name="header">HTTP request header of the request to handle.</param>
 /// <param name="config">Endpoint configuration which received the request.</param>
 /// <param name="remoteEP">Remote endpoint which sent the request.</param>
 protected void HandleSSDPRequest(SimpleHTTPRequest header, EndpointConfiguration config, IPEndPoint remoteEP)
 {
   switch (header.Method)
   {
     case "M-SEARCH":
       if (header.Param != "*" || header["MAN"] != "\"ssdp:discover\"")
         throw new InvalidDataException("Unsupported Request");
       // We don't make a difference between multicast and unicast search requests here,
       // the only difference is the existance of the MX header field.
       // If it is present, we simply use this random delay for the answer, and if it is
       // not present, we send the answer at once.
       int mx; // Max. seconds to delay response
       if (!int.TryParse(header["MX"], out mx))
         mx = 0;
       else if (mx < 1)
           // Malformed request
         throw new InvalidDataException("Invalid MX header value");
       if (mx > 5)
         mx = 5; // Should be bounded to 5, according to (DevArch)
       string st = header["ST"]; // Search target
       if (header.ContainsHeader("USER-AGENT")) // Optional
         CheckUserAgentUPnPVersion(header["USER-AGENT"]);
       DelaySearchResponse(new PendingSearchRequest(st, config, remoteEP), mx);
       break;
   }
 }
コード例 #11
0
    public void HandleMulticastEventNotification(SimpleHTTPRequest request)
    {
      string nt = request["NT"];
      string nts = request["NTS"];
      string sid = request["SID"];
      string usn = request["USN"];
      string svcid = request["SVCID"];
      string contentType = request["CONTENT-TYPE"];

      lock (_cpData.SyncObj)
      {
        CpService service;
        if (nt != "upnp:event" || nts != "upnp:propchange" || string.IsNullOrEmpty(sid) ||
            !TryGetService(usn, svcid, out service))
          return;
        Encoding contentEncoding;
        string mediaType;
        if (!EncodingUtils.TryParseContentTypeEncoding(contentType, Encoding.UTF8, out mediaType, out contentEncoding) ||
            mediaType != "text/xml")
          return;
        Stream stream = new MemoryStream(request.MessageBody);
        HandleEventNotification(stream, contentEncoding, service, _upnpVersion);
      }
    }