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);
        }
 /// <summary>
 /// Helper method to be used within the <see cref="SoapDeserializeValue"/> method to check if the serialization is
 /// a <c>null</c> value.
 /// The reader will read the end element tag after it encountered a <c>null</c> value.
 /// </summary>
 /// <param name="reader">XML reader which is positioned at the starting tag of the XML element enclosing the value.</param>
 protected bool SoapReadNull(XmlReader reader)
 {
     return(SoapHelper.ReadNull(reader));
 }
        /// <summary>
        /// Deserializes the contents of the given XML <paramref name="reader"/>'s current element in the formatting rules
        /// given by this data type.
        /// </summary>
        /// <param name="reader">XML reader whose current element's value will be deserialized.
        /// The reader's position is the start of the parent element, the result should go. After this method returns, the reader
        /// must have read the end element.</param>
        /// <returns>Deserialized object of this data type (may be <c>null</c>).</returns>
        public object SoapDeserializeValue(XmlReader reader)
        {
            object result;

            if (SoapHelper.ReadNull(reader))
            {
                result = null;
            }
            else
            {
                switch (_upnpTypeName)
                {
                case "ui1":
                case "ui2":
                case "ui4":
                case "ui8":
                case "i1":
                case "i2":
                case "i4":
                case "i8":
                case "int":
                case "r4":
                case "r8":
                case "number":
                case "fixed.14.4":
                case "float":
                case "char":
                    result = reader.ReadElementContentAs(_dotNetType, null);
                    break;

                case "string":
                case "uuid":
                    result = reader.ReadElementContentAsString();
                    break;

                case "date":
                    result = System.DateTime.ParseExact(reader.ReadElementContentAsString(), "yyyy-MM-dd", null);
                    break;

                case "dateTime":
                    result = System.DateTime.ParseExact(reader.ReadElementContentAsString(), "s", null);
                    break;

                case "dateTime.tz":
                    result = System.DateTime.ParseExact(reader.ReadElementContentAsString(), "u", null).ToLocalTime();
                    break;

                case "time":
                    result = System.DateTime.ParseExact(reader.ReadElementContentAsString(), "T", null);
                    break;

                case "time.tz":
                    result = System.DateTime.ParseExact(reader.ReadElementContentAsString(), "hh:mm:ss", null).ToLocalTime();
                    break;

                case "boolean":
                    result = reader.ReadElementContentAs(_dotNetType, null);
                    break;

                case "bin.base64":
                    result = reader.ReadElementContentAs(_dotNetType, null);
                    break;

                case "bin.hex":
                    result = EncodingUtils.FromHexString(reader.ReadElementContentAsString());
                    break;

                case "uri":
                    result = new Uri(reader.ReadElementContentAsString());
                    break;

                default:
                    throw new NotImplementedException(string.Format("UPnP standard data type '{0}' is not implemented", _upnpTypeName));
                }
                // Reader will already have read the end element in the methods ReadElementContentXXX
            }
            return(result);
        }
        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);
        }
Exemple #5
0
        /// <summary>
        /// Handler method for SOAP control requests.
        /// </summary>
        /// <param name="service">The service whose action was called.</param>
        /// <param name="messageStream">The stream which contains the HTTP message body with the SOAP envelope.</param>
        /// <param name="streamEncoding">Encoding of the <paramref name="messageStream"/>.</param>
        /// <param name="subscriberSupportsUPnP11">Should be set if the requester sent a user agent header which denotes a UPnP
        /// version of 1.1. If set to <c>false</c>, in- and out-parameters with extended data type will be deserialized/serialized
        /// using the string-equivalent of the values.</param>
        /// <param name="context">Context object holding data for the current action call.</param>
        /// <param name="result">SOAP result - may be an action result, a SOAP fault or <c>null</c> if no body should
        /// be sent in the HTTP response.</param>
        /// <returns>HTTP status code to be sent. Should be
        /// <list>
        /// <item><see cref="HttpStatusCode.OK"/> If the action could be evaluated correctly and produced a SOAP result.</item>
        /// <item><see cref="HttpStatusCode.InternalServerError"/> If the result is a SOAP fault.</item>
        /// <item><see cref="HttpStatusCode.BadRequest"/> If the message stream was malformed.</item>
        /// </list>
        /// </returns>
        public static HttpStatusCode HandleRequest(DvService service, Stream messageStream, Encoding streamEncoding,
                                                   bool subscriberSupportsUPnP11, CallContext context, out string result)
        {
            UPnPError res;

            try
            {
                IList <object> inParameterValues = null; // Default to null if there aren't parameters, will be lazily initialized later
                DvAction       action;
                using (StreamReader streamReader = new StreamReader(messageStream, streamEncoding))
                    using (XmlReader reader = XmlReader.Create(streamReader, 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 MediaPortal.Utilities.Exceptions.InvalidDataException("Unable to parse service type and version URN '{0}'", serviceTypeVersion_URN);
                        }
                        string actionName = reader.LocalName;
                        if (!service.Actions.TryGetValue(actionName, out action))
                        {
                            result = CreateFaultDocument(401, "Invalid Action");
                            return(HttpStatusCode.InternalServerError);
                        }
                        IEnumerator <DvArgument> formalArgumentEnumer = action.InArguments.GetEnumerator();
                        if (!SoapHelper.ReadEmptyStartElement(reader)) // Action name
                        {
                            while (reader.NodeType != XmlNodeType.EndElement)
                            {
                                string argumentName = reader.Name; // Arguments don't have a namespace, so take full name
                                if (!formalArgumentEnumer.MoveNext() || formalArgumentEnumer.Current.Name != argumentName)
                                {                                  // Too many arguments
                                    result = CreateFaultDocument(402, "Invalid Args");
                                    return(HttpStatusCode.InternalServerError);
                                }
                                object value;
                                if (SoapHelper.ReadNull(reader))
                                {
                                    value = null;
                                }
                                else
                                {
                                    res = formalArgumentEnumer.Current.SoapParseArgument(reader, !subscriberSupportsUPnP11, out value);
                                    if (res != null)
                                    {
                                        result = CreateFaultDocument(res.ErrorCode, res.ErrorDescription);
                                        return(HttpStatusCode.InternalServerError);
                                    }
                                }
                                if (inParameterValues == null)
                                {
                                    inParameterValues = new List <object>();
                                }
                                inParameterValues.Add(value);
                            }
                        }
                        if (formalArgumentEnumer.MoveNext())
                        { // Too few arguments
                            result = CreateFaultDocument(402, "Invalid Args");
                            return(HttpStatusCode.InternalServerError);
                        }
                    }
                IList <object> outParameterValues;
                // Invoke action
                try
                {
                    res = action.InvokeAction(inParameterValues, out outParameterValues, false, context);
                    // outParameterValues can be null if the action has no output parameters. Setting it to an empty list makes
                    // it easier to check parameter count later.
                    if (outParameterValues == null)
                    {
                        outParameterValues = EMPTY_OBJECT_LIST;
                    }
                }
                catch (Exception e)
                {
                    UPnPConfiguration.LOGGER.Warn("SOAPHandler: Error invoking UPnP action '{0}'", e, action.Name);
                    result = CreateFaultDocument(501, "Action Failed");
                    return(HttpStatusCode.InternalServerError);
                }
                if (res != null)
                {
                    result = CreateFaultDocument(res.ErrorCode, res.ErrorDescription);
                    return(HttpStatusCode.InternalServerError);
                }
                // Check output parameters
                IList <DvArgument> formalArguments = action.OutArguments;
                if (outParameterValues.Count != formalArguments.Count)
                {
                    result = CreateFaultDocument(501, "Action Failed");
                    return(HttpStatusCode.InternalServerError);
                }
                IList <OutParameter> outParams = formalArguments.Select((t, i) => new OutParameter(t, outParameterValues[i])).ToList();
                result = CreateResultDocument(action, outParams, !subscriberSupportsUPnP11);
                return(HttpStatusCode.OK);
            }
            catch (Exception e)
            {
                string message = "Error handling SOAP request: " + e.Message;
                UPnPConfiguration.LOGGER.Warn(message);     // Don't log the whole exception; it's only a communication error with a client

                result = CreateFaultDocument(500, message); // Also send message to client
                return(HttpStatusCode.InternalServerError);
            }
        }