public static bool TryParse(string versionStr, out UPnPVersion result) { result = null; int dotIndex = versionStr.IndexOf('.'); if (dotIndex < 0) { dotIndex = versionStr.IndexOf(','); } if (!versionStr.StartsWith(VERSION_PREFIX) || dotIndex < VERSION_PREFIX.Length + 1) { return(false); } int verMax; if (!int.TryParse(versionStr.Substring(VERSION_PREFIX.Length, dotIndex - VERSION_PREFIX.Length), out verMax)) { return(false); } int verMin; if (!int.TryParse(versionStr.Substring(dotIndex + 1), out verMin)) { return(false); } result = new UPnPVersion(verMax, verMin); return(true); }
/// <summary> /// Encodes a call of the specified <paramref name="action"/> with the given <paramref name="inParamValues"/> and /// returns the resulting SOAP XML string. /// </summary> /// <param name="action">Action to be called.</param> /// <param name="inParamValues">List of parameter values which must match the action's signature. /// Can be <c>null</c> if the parameter list is empty.</param> /// <param name="upnpVersion">UPnP version to use for the encoding.</param> /// <returns>XML string which contains the SOAP document.</returns> public static string EncodeCall(CpAction action, IList<object> inParamValues, UPnPVersion upnpVersion) { bool targetSupportsUPnP11 = upnpVersion.VerMin >= 1; StringBuilder result = new StringBuilder(5000); using (StringWriterWithEncoding stringWriter = new StringWriterWithEncoding(result, UPnPConsts.UTF8_NO_BOM)) using (XmlWriter writer = XmlWriter.Create(stringWriter, UPnPConfiguration.DEFAULT_XML_WRITER_SETTINGS)) { SoapHelper.WriteSoapEnvelopeStart(writer, true); writer.WriteStartElement("u", action.Name, action.ParentService.ServiceTypeVersion_URN); // Check input parameters IList<CpArgument> formalArguments = action.InArguments; if (inParamValues == null) inParamValues = EMPTY_OBJECT_LIST; if (inParamValues.Count != formalArguments.Count) throw new ArgumentException("Invalid argument count"); for (int i = 0; i < formalArguments.Count; i++) { CpArgument argument = formalArguments[i]; object value = inParamValues[i]; writer.WriteStartElement(argument.Name); argument.SoapSerializeArgument(value, !targetSupportsUPnP11, writer); writer.WriteEndElement(); // argument.Name } SoapHelper.WriteSoapEnvelopeEndAndClose(writer); } return result.ToString(); }
public static bool TryParseFromUserAgent(string userAgentHeader, out UPnPVersion upnpVersion) { upnpVersion = null; if (string.IsNullOrEmpty(userAgentHeader)) return false; // The specification says the userAgentHeader string should contain three tokens, separated by ' '. // Unfortunately, some devices send tokens separated by ", ". Others only send two tokens. We try to handle all situations correctly here. string[] versionInfos = userAgentHeader.Split(new char[] {',', ' '}, StringSplitOptions.RemoveEmptyEntries); string upnpVersionInfo = versionInfos.FirstOrDefault(v => v.StartsWith(VERSION_PREFIX)); if (upnpVersionInfo == null) return false; return TryParse(upnpVersionInfo, out upnpVersion); }
public static bool TryParse(string versionStr, out UPnPVersion result) { result = null; int dotIndex = versionStr.IndexOf('.'); if (!versionStr.StartsWith(VERSION_PREFIX) || dotIndex < VERSION_PREFIX.Length + 1) return false; int verMax; if (!int.TryParse(versionStr.Substring(VERSION_PREFIX.Length, dotIndex - VERSION_PREFIX.Length), out verMax)) return false; int verMin; if (!int.TryParse(versionStr.Substring(dotIndex + 1), out verMin)) return false; result = new UPnPVersion(verMax, verMin); return true; }
public static bool TryParseFromUserAgent(string userAgentHeader, out UPnPVersion upnpVersion) { upnpVersion = null; if (string.IsNullOrEmpty(userAgentHeader)) { return(false); } // The specification says the userAgentHeader string should contain three tokens, separated by ' '. // Unfortunately, some devices send tokens separated by ", ". Others only send two tokens. We try to handle all situations correctly here. string[] versionInfos = userAgentHeader.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries); string upnpVersionInfo = versionInfos.FirstOrDefault(v => v.StartsWith(VERSION_PREFIX)); if (upnpVersionInfo == null) { return(false); } return(TryParse(upnpVersionInfo, out upnpVersion)); }
protected RootEntry GetOrCreateRootEntry(string deviceUUID, string descriptionLocation, UPnPVersion upnpVersion, string osVersion, string productVersion, DateTime expirationTime, EndpointConfiguration endpoint, HTTPVersion httpVersion, int searchPort, out bool wasAdded) { // Because the order of the UDP advertisement packets isn't guaranteed (and even not really specified by the UPnP specification), // in the general case it is not possible to find the correct root entry for each advertisement message. // - We cannot search by root device UUID because the upnp:rootdevice message might not be the first message, so before that message, we don't know the root device ID and // thus we cannot use the root device id as unique key to find the root entry // - We cannot use the device description because for multi-homed devices, more than one device description can belong to the same root device // // Assume the message arrive in an order so that device A over network interface N1 is announced first. Second, device B over network interface N2 is announced. // In that case, we cannot judge if those two devices belong to the same root device or not. // // To face that situation, we first add all advertised devices to _pendingDeviceEntries. When a upnp:rootdevice message is received, // we either simply move the root entry from _pendingDeviceEntries into _cpData.DeviceEntries or we merge the pending entry with an already existing // entry in _cpData.DeviceEntries. At that time the merge is possible because we then have the root device id for both root entries. lock (_cpData.SyncObj) { RootEntry result = GetRootEntryByContainedDeviceUUID(deviceUUID) ?? GetRootEntryByDescriptionLocation(descriptionLocation); if (result != null) { result.ExpirationTime = expirationTime; wasAdded = false; } else { result = new RootEntry(_cpData.SyncObj, upnpVersion, osVersion, productVersion, expirationTime); _pendingDeviceEntries.Add(result); wasAdded = true; } return result; } }
/// <summary> /// Takes the XML document provided by the given <paramref name="body"/> stream, parses it in the given /// <paramref name="contentEncoding"/> and provides the action result to the appropriate receiver. /// </summary> /// <param name="body">Body stream of the SOAP XML action result message.</param> /// <param name="contentEncoding">Encoding of the body stream.</param> /// <param name="action">Action which was called before.</param> /// <param name="clientState">State object which was given in the action call and which will be returned to the client.</param> /// <param name="upnpVersion">UPnP version of the UPnP server.</param> public static void HandleResult(Stream body, Encoding contentEncoding, CpAction action, object clientState, UPnPVersion upnpVersion) { bool sourceSupportsUPnP11 = upnpVersion.VerMin >= 1; IList<object> outParameterValues; try { if (!body.CanRead) { UPnPConfiguration.LOGGER.Error("SOAPHandler: Empty action result document"); action.ActionErrorResultPresent(new UPnPError(501, "Invalid server result"), clientState); return; } using (TextReader textReader = new StreamReader(body, contentEncoding)) outParameterValues = ParseResult(textReader, action, sourceSupportsUPnP11); } catch (Exception e) { UPnPConfiguration.LOGGER.Error("SOAPHandler: Error parsing action result document", e); action.ActionErrorResultPresent(new UPnPError(501, "Invalid server result"), clientState); return; } try { // Invoke action result action.ActionResultPresent(outParameterValues, clientState); } catch (Exception e) { UPnPConfiguration.LOGGER.Error("UPnP subsystem: Error invoking action '{0}'", e, action.FullQualifiedName); } }
protected static void HandleVariableChangeNotification(XmlReader reader, CpService service, UPnPVersion upnpVersion) { string variableName = reader.LocalName; CpStateVariable stateVariable; if (!service.StateVariables.TryGetValue(variableName, out stateVariable)) // We don't know that variable - this is an error case but we won't raise an exception here return; object value = stateVariable.DataType.SoapDeserializeValue(reader, upnpVersion.VerMin == 0); service.InvokeStateVariableChanged(stateVariable, value); }
public static HttpStatusCode HandleEventNotification(Stream stream, Encoding streamEncoding, CpService service, UPnPVersion upnpVersion) { try { // Parse XML document using (StreamReader streamReader = new StreamReader(stream, streamEncoding)) using (XmlReader reader = XmlReader.Create(streamReader, UPnPConfiguration.DEFAULT_XML_READER_SETTINGS)) { reader.MoveToContent(); reader.ReadStartElement("propertyset", UPnPConsts.NS_UPNP_EVENT); while (reader.LocalName == "property" && reader.NamespaceURI == UPnPConsts.NS_UPNP_EVENT) { reader.ReadStartElement("property", UPnPConsts.NS_UPNP_EVENT); HandleVariableChangeNotification(reader, service, upnpVersion); reader.ReadEndElement(); // property } reader.Close(); } return HttpStatusCode.OK; } catch (Exception e) { UPnPConfiguration.LOGGER.Warn("GENAClientController: Error handling event notification", e); return HttpStatusCode.BadRequest; } }
public GENAClientController(CPData cpData, DeviceConnection connection, EndpointConfiguration endpoint, UPnPVersion upnpVersion) { _cpData = cpData; _connection = connection; _endpoint = endpoint; _upnpVersion = upnpVersion; _eventNotificationPath = "/" + Guid.NewGuid(); IPAddress address = endpoint.EndPointIPAddress; _eventNotificationEndpoint = new IPEndPoint(address, address.AddressFamily == AddressFamily.InterNetwork ? cpData.HttpPortV4 : cpData.HttpPortV6); _subscriptionRenewalTimer = new Timer(OnSubscriptionRenewalTimerElapsed); }