// Use the same rule like Charles to parse message type and decriptorset url
        // Refer to: https://www.charlesproxy.com/documentation/using-charles/protocol-buffers/
        public static bool ParseMessageTypeNameAndDescriptorSetUrl(HTTPHeaders headers, out string messageTypeName, out string descriptorSetUrl)
        {
            messageTypeName  = "";
            descriptorSetUrl = "";

            if (null == headers || !headers.Exists("Content-Type"))
            {
                return(false);
            }

            messageTypeName = headers.GetTokenValue("Content-Type", "messageType");
            if (null == messageTypeName)
            {
                messageTypeName = headers.GetTokenValue("Content-Type", "MessageType");
            }

            descriptorSetUrl = headers.GetTokenValue("Content-Type", "Desc");
            if (null == descriptorSetUrl)
            {
                descriptorSetUrl = headers.GetTokenValue("Content-Type", "desc");
            }

            return(true);
        }
 private bool IsBaiduPacket()
 {
     return(_headers != null && _headers.Exists("x_bd_data_type"));
 }
        /// <summary>
        /// Get the WOPI operation
        /// </summary>
        /// <param name="headers">Http headers</param>
        /// <param name="url">url for a session</param>
        /// <returns>The operation name</returns>
        public WOPIOperations GetWOPIOperationName(HTTPHeaders headers, string url)
        {
            if (url.EndsWith("hosting/discovery"))
            {
                return(WOPIOperations.Discovery);
            }

            if (headers.Exists("X-WOPI-Override"))
            {
                switch (headers["X-WOPI-Override"] as string)
                {
                case "PUT_RELATIVE":
                    return(WOPIOperations.PutRelativeFile);

                case "UNLOCK":
                    return(WOPIOperations.Unlock);

                case "REFRESH_LOCK":
                    return(WOPIOperations.RefreshLock);

                case "DELETE":
                    return(WOPIOperations.DeleteFile);

                case "READ_SECURE_STORE":
                    return(WOPIOperations.ReadSecureStore);

                case "GET_RESTRICTED_LINK":
                    return(WOPIOperations.GetRestrictedLink);

                case "REVOKE_RESTRICTED_LINK":
                    return(WOPIOperations.RevokeRestrictedLink);

                case "PUT":
                    return(WOPIOperations.PutFile);

                case "LOCK":
                    if (headers.Exists("X-WOPI-OldLock"))
                    {
                        return(WOPIOperations.UnlockAndRelock);
                    }
                    else
                    {
                        return(WOPIOperations.Lock);
                    }

                case "COBALT":
                    if (headers.Exists("X-WOPI-RelativeTarget"))
                    {
                        return(WOPIOperations.ExecuteCellStorageRelativeRequest);
                    }
                    else
                    {
                        return(WOPIOperations.ExecuteCellStorageRequest);
                    }

                default:
                    return(WOPIOperations.Unknown);
                }
            }

            if (url.EndsWith("/contents"))
            {
                return(WOPIOperations.GetFile);
            }

            if (url.EndsWith("/children"))
            {
                return(WOPIOperations.EnumerateChildren);
            }

            if (url.Contains("/files/"))
            {
                return(WOPIOperations.CheckFileInfo);
            }

            if (url.Contains("/folders/"))
            {
                return(WOPIOperations.CheckFolderInfo);
            }

            return(WOPIOperations.Unknown);
        }
        /// <summary>
        /// Parse the HTTP payload to WOPI message.
        /// </summary>
        /// <param name="requestHeaders">The HTTP request header.</param>
        /// <param name="responseHeaders">The HTTP response header.</param>
        /// <param name="url">url for a HTTP message.</param>
        /// <param name="bytesFromHTTP">The raw data from HTTP layer.</param>
        /// <param name="direction">The direction of the traffic.</param>
        /// <returns>The object parsed result</returns>
        public object ParseHTTPPayloadForWOPI(HTTPHeaders requestHeaders, HTTPHeaders responseHeaders, string url, byte[] bytesFromHTTP, out string binaryStructureRopName, TrafficDirection direction)
        {
            object objectOut = null;

            binaryStructureRopName = string.Empty;
            try
            {
                if (direction == TrafficDirection.Out && responseHeaders.Exists("Transfer-Encoding") && responseHeaders["Transfer-Encoding"] == "chunked")
                {
                    bytesFromHTTP = Utilities.GetPaylodFromChunkedBody(bytesFromHTTP);
                }

                Stream         stream    = new MemoryStream(bytesFromHTTP);
                StreamReader   reader    = new StreamReader(stream);
                string         text      = reader.ReadToEnd();
                WOPIOperations operation = GetWOPIOperationName(requestHeaders, url);
                if (direction == TrafficDirection.In)
                {
                    switch (operation)
                    {
                    case WOPIOperations.PutRelativeFile:
                        objectOut = bytesFromHTTP;
                        binaryStructureRopName = "PutRelativeFile";
                        break;

                    case WOPIOperations.PutFile:
                        objectOut = bytesFromHTTP;
                        binaryStructureRopName = "PutFile";
                        break;

                    case WOPIOperations.ExecuteCellStorageRelativeRequest:
                    case WOPIOperations.ExecuteCellStorageRequest:
                        byte[]       cellreq = bytesFromHTTP;
                        MemoryStream ms;
                        if (requestHeaders.Exists("Content-Encoding") && requestHeaders["Content-Encoding"] == "gzip")
                        {
                            cellreq = Fiddler.Utilities.GzipExpand(cellreq);
                            ms      = new MemoryStream(cellreq);
                        }
                        else
                        {
                            ms = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(text ?? ""));
                        }
                        XmlSerializer   serializer     = new XmlSerializer(typeof(RequestEnvelope));
                        RequestEnvelope requestEnvelop = (RequestEnvelope)serializer.Deserialize(ms);
                        objectOut = requestEnvelop.Body;

                        if (requestEnvelop.Body.RequestCollection != null)
                        {
                            TryParseFSSHTTPBRequestMessage(requestEnvelop.Body.RequestCollection.Request, bytesFromHTTP);
                        }
                        break;

                    case WOPIOperations.PutUserInfo:
                        objectOut = text;
                        break;

                    case WOPIOperations.Discovery:
                    case WOPIOperations.CheckFileInfo:
                    case WOPIOperations.Lock:
                    case WOPIOperations.RefreshLock:
                    case WOPIOperations.RevokeRestrictedLink:
                    case WOPIOperations.Unlock:
                    case WOPIOperations.UnlockAndRelock:
                    case WOPIOperations.GetLock:
                    case WOPIOperations.DeleteFile:
                    case WOPIOperations.ReadSecureStore:
                    case WOPIOperations.RenameFile:
                    case WOPIOperations.GetRestrictedLink:
                    case WOPIOperations.CheckFolderInfo:
                    case WOPIOperations.GetFile:
                    case WOPIOperations.EnumerateChildren:
                        objectOut = string.Format("{0} operation's request body is null", operation.ToString());
                        break;

                    default:
                        throw new Exception("The WOPI operations type is not right.");
                    }
                }
                else
                {
                    string status = this.session.ResponseHeaders.HTTPResponseStatus.Replace(" " + this.session.ResponseHeaders.StatusDescription, string.Empty);
                    if (Convert.ToUInt32(status) != 200)// the status is not success
                    {
                        return(null);
                    }

                    ResponseBodyBase responseBody = new ResponseBodyBase();
                    switch (operation)
                    {
                    case WOPIOperations.Discovery:
                        MemoryStream  discoveryms         = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(text ?? ""));
                        XmlSerializer discoverySerializer = new XmlSerializer(typeof(wopidiscovery));
                        wopidiscovery discoveryres        = (wopidiscovery)discoverySerializer.Deserialize(discoveryms);
                        objectOut = discoveryres;
                        break;

                    case WOPIOperations.CheckFileInfo:
                        objectOut = WOPISerilizer.JsonToObject <CheckFileInfo>(text);
                        break;

                    case WOPIOperations.CheckFolderInfo:
                        objectOut = WOPISerilizer.JsonToObject <CheckFolderInfo>(text);
                        break;

                    case WOPIOperations.PutRelativeFile:
                        objectOut = WOPISerilizer.JsonToObject <PutRelativeFile>(text);
                        break;

                    case WOPIOperations.ReadSecureStore:
                        objectOut = WOPISerilizer.JsonToObject <ReadSecureStore>(text);
                        break;

                    case WOPIOperations.EnumerateChildren:
                        objectOut = WOPISerilizer.JsonToObject <EnumerateChildren>(text);
                        break;

                    case WOPIOperations.RenameFile:
                        objectOut = WOPISerilizer.JsonToObject <RenameFile>(text);
                        break;

                    case WOPIOperations.ExecuteCellStorageRelativeRequest:
                    case WOPIOperations.ExecuteCellStorageRequest:
                    {
                        byte[]       cellres = bytesFromHTTP;
                        MemoryStream ms;
                        string       res;
                        if (responseHeaders.Exists("Content-Encoding") && responseHeaders["Content-Encoding"] == "gzip")
                        {
                            cellres = Fiddler.Utilities.GzipExpand(cellres);
                            string res_sub = System.Text.Encoding.UTF8.GetString(cellres);
                            res = string.Format("{0}{1}{2}", @"<Body>", res_sub, "</Body>");
                        }
                        else
                        {
                            res = string.Format("{0}{1}{2}", @"<Body>", text, "</Body>");
                        }
                        ms = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(res ?? ""));
                        XmlSerializer        serializer = new XmlSerializer(typeof(ResponseEnvelopeBody));
                        ResponseEnvelopeBody body       = (ResponseEnvelopeBody)serializer.Deserialize(ms);
                        objectOut = body;

                        // if SubResponseData has fsshttpb messages do parser.
                        if (body.ResponseCollection != null)
                        {
                            TryParseFSSHTTPBResponseMessage(body.ResponseCollection.Response, bytesFromHTTP);
                        }
                        break;
                    }

                    case WOPIOperations.GetFile:
                        objectOut = bytesFromHTTP;
                        binaryStructureRopName = "GetFile";
                        break;

                    case WOPIOperations.DeleteFile:
                    case WOPIOperations.Lock:
                    case WOPIOperations.GetRestrictedLink:
                    case WOPIOperations.PutFile:
                    case WOPIOperations.RefreshLock:
                    case WOPIOperations.RevokeRestrictedLink:
                    case WOPIOperations.Unlock:
                    case WOPIOperations.UnlockAndRelock:
                    case WOPIOperations.GetLock:
                    case WOPIOperations.PutUserInfo:
                        objectOut = string.Format("{0} operation's response body is null", operation.ToString());
                        break;

                    default:
                        throw new Exception("The WOPI operations type is not right.");
                    }
                }
                return(objectOut);
            }
            catch (Exception ex)
            {
                objectOut = ex.ToString();
                return(objectOut);
            }
        }
        /// <summary>
        /// Parse the HTTP payload to FSSHTTP and WOPI message.
        /// </summary>
        /// <param name="responseHeaders">The HTTP response header.</param>
        /// <param name="bytesFromHTTP">The raw data from HTTP layer.</param>
        /// <param name="direction">The direction of the traffic.</param>
        /// <returns>The object parsed result</returns>
        public object ParseHTTPPayloadForFSSHTTP(HTTPHeaders responseHeaders, byte[] bytesFromHTTP, TrafficDirection direction)
        {
            object objectOut = null;

            byte[] emptyByte = new byte[0];

            if (bytesFromHTTP == null || bytesFromHTTP.Length == 0)
            {
                return(null);
            }

            try
            {
                if (direction == TrafficDirection.Out && responseHeaders.Exists("Transfer-Encoding") && responseHeaders["Transfer-Encoding"] == "chunked")
                {
                    bytesFromHTTP = Utilities.GetPaylodFromChunkedBody(bytesFromHTTP);
                }

                Stream       stream = new MemoryStream(bytesFromHTTP);
                StreamReader reader = new StreamReader(stream);
                string       text   = reader.ReadToEnd();

                Regex SOAPRegex = new Regex(@"\<s:Envelop.*\<\/s:Envelope\>"); // extract envelop from http payload.
                if (SOAPRegex.Match(text).Success)
                {
                    XmlDocument doc      = new XmlDocument();
                    string      soapbody = SOAPRegex.Match(text).Value;

                    if (direction == TrafficDirection.In)
                    {
                        Regex           FSSHTTPRequestRegex = new Regex("xsi:type=\"\\w*\"\\s"); // remove xsi:type in xml message. this xsi:type is used for inherit in xmlSerializer.
                        string          FSSHTTPRequest      = FSSHTTPRequestRegex.Replace(soapbody, string.Empty);
                        MemoryStream    ms             = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(FSSHTTPRequest ?? ""));
                        XmlSerializer   serializer     = new XmlSerializer(typeof(RequestEnvelope));
                        RequestEnvelope requestEnvelop = (RequestEnvelope)serializer.Deserialize(ms);
                        objectOut = requestEnvelop.Body;

                        // if SubRequestData has fsshttpb messages do parser.
                        if (requestEnvelop.Body.RequestCollection != null)
                        {
                            TryParseFSSHTTPBRequestMessage(requestEnvelop.Body.RequestCollection.Request, bytesFromHTTP);
                        }
                    }
                    else
                    {
                        MemoryStream     ms              = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(soapbody ?? ""));
                        XmlSerializer    serializer      = new XmlSerializer(typeof(ResponseEnvelope));
                        ResponseEnvelope responseEnvelop = (ResponseEnvelope)serializer.Deserialize(ms);
                        objectOut = responseEnvelop.Body;

                        // if SubResponseData has fsshttpb messages do parser.
                        if (responseEnvelop.Body.ResponseCollection != null)
                        {
                            TryParseFSSHTTPBResponseMessage(responseEnvelop.Body.ResponseCollection.Response, bytesFromHTTP);
                        }
                    }
                }
                return(objectOut);
            }
            catch (Exception ex)
            {
                objectOut = ex.ToString();
                return(objectOut);
            }
        }