예제 #1
0
    /// <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();
    }
예제 #2
0
    protected CpStateVariable _relatedStateVariable; // References the related state variable in our parent action's parent service

    public CpArgument(CpAction parentAction, string name, CpStateVariable relatedStateVariable, ArgumentDirection direction,
        bool isReturnValue)
    {
      _parentAction = parentAction;
      _name = name;
      _relatedStateVariable = relatedStateVariable;
      _direction = direction;
      _isReturnValue = isReturnValue;
    }
예제 #3
0
        internal static CpArgument CreateArgument(CpAction parentAction, CpService parentService, XPathNavigator argumentNav,
                                                  IXmlNamespaceResolver nsmgr)
        {
            string          name = ParserHelper.SelectText(argumentNav, "s:name/text()", nsmgr);
            string          relatedStateVariableName = ParserHelper.SelectText(argumentNav, "s:relatedStateVariable/text()", nsmgr);
            CpStateVariable relatedStateVariable;

            if (!parentService.StateVariables.TryGetValue(relatedStateVariableName, out relatedStateVariable))
            {
                throw new ArgumentException("Related state variable '{0}' is not present in service", relatedStateVariableName);
            }
            string            direction = ParserHelper.SelectText(argumentNav, "s:direction/text()", nsmgr);
            XPathNodeIterator retValIt  = argumentNav.Select("s:retval", nsmgr);
            CpArgument        result    = new CpArgument(parentAction, name, relatedStateVariable, ParseArgumentDirection(direction), retValIt.MoveNext());

            return(result);
        }
예제 #4
0
 internal static CpAction ConnectAction(DeviceConnection connection, CpService parentService, XPathNavigator actionNav, IXmlNamespaceResolver nsmgr)
 {
     using (connection.CPData.Lock.EnterWrite())
     {
         string            name       = ParserHelper.SelectText(actionNav, "s:name/text()", nsmgr);
         CpAction          result     = new CpAction(connection, parentService, name);
         XPathNodeIterator argumentIt = actionNav.Select("s:argumentList/s:argument", nsmgr);
         while (argumentIt.MoveNext())
         {
             CpArgument argument = CpArgument.CreateArgument(result, parentService, argumentIt.Current, nsmgr);
             if (argument.Direction == ArgumentDirection.In)
             {
                 result.AddInAgrument(argument);
             }
             else
             {
                 result.AddOutAgrument(argument);
             }
         }
         return(result);
     }
 }
예제 #5
0
 /// <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);
   }
 }
예제 #6
0
    protected static IList<object> ParseResult(TextReader textReader, CpAction action, bool sourceSupportsUPnP11)
    {
      object[] outParameterValues = new object[action.OutArguments.Count];
      using(XmlReader reader = XmlReader.Create(textReader, UPnPConfiguration.DEFAULT_XML_READER_SETTINGS))
      {
        reader.MoveToContent();
        // Parse SOAP envelope
        reader.ReadStartElement("Envelope", UPnPConsts.NS_SOAP_ENVELOPE);
        reader.ReadStartElement("Body", UPnPConsts.NS_SOAP_ENVELOPE);
        // Reader is positioned at the action element
        string serviceTypeVersion_URN = reader.NamespaceURI;
        string type;
        int version;
        // Parse service and action
        if (!ParserHelper.TryParseTypeVersion_URN(serviceTypeVersion_URN, out type, out version))
          throw new ArgumentException("Invalid service type or version");
        string actionName = reader.LocalName;
        if (!actionName.EndsWith("Response") ||
            actionName.Substring(0, actionName.Length - "Response".Length) != action.Name)
          throw new ArgumentException("Invalid action name in result message");

        // UPnP spec says we have to be able to handle return values being out
        // of order to support UPnP 1.0 devices. See UPnP-arch-DeviceArchitecture-v1.1
        // section 2.5.4. We need a dictionary to make this easy.
        IDictionary<string, int> formalArgIdxDictionary = new Dictionary<string, int>();
        for (int i = 0; i < action.OutArguments.Count; i++)
          formalArgIdxDictionary.Add(action.OutArguments[i].Name, i);

        int outArgCount = 0;
        if (!SoapHelper.ReadEmptyStartElement(reader))
          // Parse and check output parameters
          while (reader.NodeType != XmlNodeType.EndElement)
          {
            string argumentName = reader.Name; // Arguments don't have a namespace, so take full name
            int formalArgumentIndex;
            if (!formalArgIdxDictionary.TryGetValue(argumentName, out formalArgumentIndex))
              throw new ArgumentException("Invalid argument name");
            CpArgument formalArgument = action.OutArguments[formalArgumentIndex];

            // Get the argument value and store it in the correct position in the return list.
            object value = null;
            if (!SoapHelper.ReadNull(reader))
              formalArgument.SoapParseArgument(reader, !sourceSupportsUPnP11, out value);
            outParameterValues[formalArgumentIndex] = value;
            outArgCount++;

            // Don't allow duplicates of the same argument.
            formalArgIdxDictionary.Remove(formalArgument.Name);
          }
        if (outArgCount != action.OutArguments.Count) // Too few arguments
          throw new ArgumentException("Invalid out argument count");
      }
      return outParameterValues;
    }
예제 #7
0
 public static void ActionFailed(CpAction action, object clientState, string errorDescription)
 {
   action.ActionErrorResultPresent(new UPnPError(501, errorDescription), clientState);
 }
예제 #8
0
 public static void HandleErrorResult(TextReader textReader, CpAction action, object clientState)
 {
   try
   {
     uint errorCode;
     string errorDescription;
     ParseFaultDocument(textReader, out errorCode, out errorDescription);
     action.ActionErrorResultPresent(new UPnPError(errorCode, errorDescription), clientState);
   }
   catch (Exception e)
   {
     UPnPConfiguration.LOGGER.Error("SOAPHandler: Error parsing action error result document", e);
     ActionFailed(action, clientState, "Invalid server result");
   }
 }
예제 #9
0
 protected HttpWebRequest CreateActionCallRequest(ServiceDescriptor sd, CpAction action)
 {
   LinkData preferredLink = sd.RootDescriptor.SSDPRootEntry.PreferredLink;
   HttpWebRequest request = (HttpWebRequest) WebRequest.Create(new Uri(
       new Uri(preferredLink.DescriptionLocation), sd.ControlURL));
   NetworkUtils.SetLocalEndpoint(request, preferredLink.Endpoint.EndPointIPAddress);
   request.Method = "POST";
   request.KeepAlive = _useHttpKeepAlive;
   request.CachePolicy = new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore);
   request.ServicePoint.Expect100Continue = false;
   request.AllowAutoRedirect = true;
   request.UserAgent = UPnPConfiguration.UPnPMachineInfoHeader;
   request.ContentType = "text/xml; charset=\"utf-8\"";
   request.Headers.Add("SOAPACTION", '"' + action.Action_URN + '"');
   request.Headers.Add("Accept-Encoding", CompressionHelper.GetAcceptedEncodings());
   return request;
 }
예제 #10
0
    internal void OnActionCalled(CpAction action, IList<object> inParams, object clientState)
    {
      if (!action.IsConnected)
        throw new UPnPDisconnectedException("Action '{0}' is not connected to a UPnP network action", action.FullQualifiedName);
      CpService service = action.ParentService;
      ServiceDescriptor sd = GetServiceDescriptor(service);
      string message = SOAPHandler.EncodeCall(action, inParams, _rootDescriptor.SSDPRootEntry.UPnPVersion);

      HttpWebRequest request = CreateActionCallRequest(sd, action);
      ActionCallState state = new ActionCallState(action, clientState, request);
      state.SetRequestMessage(message);
      lock (_cpData.SyncObj)
        _pendingCalls.Add(state);
      IAsyncResult result = state.Request.BeginGetResponse(OnCallResponseReceived, state);
      NetworkHelper.AddTimeout(request, result, PENDING_ACTION_CALL_TIMEOUT * 1000);
    }
예제 #11
0
 /// <summary>
 /// Adds the specified <paramref name="action"/> instance to match to this service template.
 /// </summary>
 /// <param name="action">Action template to be added.</param>
 internal void AddAction(CpAction action)
 {
   _actions.Add(action.Name, action);
 }
예제 #12
0
 internal static CpAction ConnectAction(DeviceConnection connection, CpService parentService, XPathNavigator actionNav,
     IXmlNamespaceResolver nsmgr)
 {
   lock (connection.CPData.SyncObj)
   {
     string name = ParserHelper.SelectText(actionNav, "s:name/text()", nsmgr);
     CpAction result = new CpAction(connection, parentService, name);
     XPathNodeIterator argumentIt = actionNav.Select("s:argumentList/s:argument", nsmgr);
     while (argumentIt.MoveNext())
     {
       CpArgument argument = CpArgument.CreateArgument(result, parentService, argumentIt.Current, nsmgr);
       if (argument.Direction == ArgumentDirection.In)
         result.AddInAgrument(argument);
       else
         result.AddOutAgrument(argument);
     }
     return result;
   }
 }
예제 #13
0
 public CpArgument(CpAction parentAction, string name, CpStateVariable relatedStateVariable, ArgumentDirection direction) :
     this(parentAction, name, relatedStateVariable, direction, false)
 {
 }
예제 #14
0
 /// <summary>
 /// Adds the specified <paramref name="action"/> instance to match to this service template.
 /// </summary>
 /// <param name="action">Action template to be added.</param>
 internal void AddAction(CpAction action)
 {
     _actions.Add(action.Name, action);
 }
예제 #15
0
 public ActionCallState(CpAction action, object clientState, HttpWebRequest request) :
     base(request)
 {
   _action = action;
   _clientState = clientState;
 }
예제 #16
0
 internal static CpArgument CreateArgument(CpAction parentAction, CpService parentService, XPathNavigator argumentNav,
     IXmlNamespaceResolver nsmgr)
 {
   string name = ParserHelper.SelectText(argumentNav, "s:name/text()", nsmgr);
   string relatedStateVariableName = ParserHelper.SelectText(argumentNav, "s:relatedStateVariable/text()", nsmgr);
   CpStateVariable relatedStateVariable;
   if (!parentService.StateVariables.TryGetValue(relatedStateVariableName, out relatedStateVariable))
     throw new ArgumentException("Related state variable '{0}' is not present in service", relatedStateVariableName);
   string direction = ParserHelper.SelectText(argumentNav, "s:direction/text()", nsmgr);
   XPathNodeIterator retValIt = argumentNav.Select("s:retval", nsmgr);
   CpArgument result = new CpArgument(parentAction, name, relatedStateVariable, ParseArgumentDirection(direction), retValIt.MoveNext());
   return result;
 }
예제 #17
0
    protected static IList<object> ParseResult(TextReader textReader, CpAction action, bool sourceSupportsUPnP11)
    {
      IList<object> outParameterValues = new List<object>();
      using(XmlReader reader = XmlReader.Create(textReader, UPnPConfiguration.DEFAULT_XML_READER_SETTINGS))
      {
        reader.MoveToContent();
        // Parse SOAP envelope
        reader.ReadStartElement("Envelope", UPnPConsts.NS_SOAP_ENVELOPE);
        reader.ReadStartElement("Body", UPnPConsts.NS_SOAP_ENVELOPE);
        // Reader is positioned at the action element
        string serviceTypeVersion_URN = reader.NamespaceURI;
        string type;
        int version;
        // Parse service and action
        if (!ParserHelper.TryParseTypeVersion_URN(serviceTypeVersion_URN, out type, out version))
          throw new ArgumentException("Invalid service type or version");
        string actionName = reader.LocalName;
        if (!actionName.EndsWith("Response") ||
            actionName.Substring(0, actionName.Length - "Response".Length) != action.Name)
          throw new ArgumentException("Invalid action name in result message");

        IEnumerator<CpArgument> formalArgumentEnumer = action.OutArguments.GetEnumerator();
        if (!SoapHelper.ReadEmptyStartElement(reader))
          // Parse and check output parameters
          while (reader.NodeType != XmlNodeType.EndElement)
          {
            string argumentName = reader.Name; // Arguments don't have a namespace, so take full name
            if (!formalArgumentEnumer.MoveNext()) // Too many arguments
              throw new ArgumentException("Invalid out argument count");
            if (formalArgumentEnumer.Current.Name != argumentName)
              throw new ArgumentException("Invalid argument name");
            object value;
            if (SoapHelper.ReadNull(reader))
              value = null;
            else
              formalArgumentEnumer.Current.SoapParseArgument(reader, !sourceSupportsUPnP11, out value);
            outParameterValues.Add(value);
          }
        if (formalArgumentEnumer.MoveNext()) // Too few arguments
          throw new ArgumentException("Invalid out argument count");
      }
      return outParameterValues;
    }
예제 #18
0
 public CpArgument(CpAction parentAction, string name, CpStateVariable relatedStateVariable, ArgumentDirection direction) :
     this(parentAction, name, relatedStateVariable, direction, false) { }
예제 #19
0
 protected static HttpWebRequest CreateActionCallRequest(ServiceDescriptor sd, CpAction action)
 {
   LinkData preferredLink = sd.RootDescriptor.SSDPRootEntry.PreferredLink;
   HttpWebRequest request = (HttpWebRequest) WebRequest.Create(new Uri(
       new Uri(preferredLink.DescriptionLocation), sd.ControlURL));
   NetworkUtils.SetLocalEndpoint(request, preferredLink.Endpoint.EndPointIPAddress);
   request.Method = "POST";
   request.KeepAlive = true;
   request.AllowAutoRedirect = true;
   request.UserAgent = UPnPConfiguration.UPnPMachineInfoHeader;
   request.ContentType = "text/xml; charset=\"utf-8\"";
   request.Headers.Add("SOAPACTION", action.Action_URN);
   return request;
 }