/// <summary>
        /// Initializes a new instance of the <see cref="GNTPParser"/> class.
        /// </summary>
        /// <param name="passwordManager">The <see cref="PasswordManager"/> containing a list of allowed passwords</param>
        /// <param name="passwordRequired">Indicates if a password is required</param>
        /// <param name="allowNetworkNotifications">Indicates if network requests are allowed</param>
        /// <param name="allowBrowserConnections">Indicates if browser requests are allowed</param>
        /// <param name="allowSubscriptions">Indicates if SUBSCRIPTION requests are allowed</param>
        /// <param name="requestInfo">The <see cref="RequestInfo"/> associated with this request</param>
        public GNTPParser2(PasswordManager passwordManager, bool passwordRequired, bool allowNetworkNotifications, bool allowBrowserConnections, bool allowSubscriptions, RequestInfo requestInfo)
        {
            this.passwordManager = passwordManager;
            this.passwordRequired = passwordRequired;
            this.allowNetworkNotifications = allowNetworkNotifications;
            this.allowBrowserConnections = allowBrowserConnections;
            this.allowSubscriptions = allowSubscriptions;
            this.requestInfo = requestInfo;

            this.alreadyReceived = new StringBuilder();
            this.headers = new HeaderCollection();
            this.notificationsToBeRegistered = new List<HeaderCollection>();
            this.pointers = new List<Pointer>();
            this.callbackInfo = new CallbackInfo();

            ms = new MemoryStream();
            gntpReader = new GNTPStreamReader(ms);
            buffer = new List<byte>();
            nextIndicator = AsyncSocket.CRLFData;
            tag = GNTP_IDENTIFIER_TAG;
        }
        /// <summary>
        /// Parses the encrypted message.
        /// </summary>
        /// <param name="bytes">The encrypted bytes</param>
        private void ParseEncryptedMessage(byte[] bytes)
        {
            byte[] encryptedBytes = null;

            if (bytes[bytes.Length - 4] == ((byte)'\r')
                && bytes[bytes.Length - 3] == ((byte)'\n')
                && bytes[bytes.Length - 2] == ((byte)'\r')
                && bytes[bytes.Length - 1] == ((byte)'\n'))
            {
                encryptedBytes = new byte[bytes.Length - 4];
            }
            else
            {
                encryptedBytes = new byte[bytes.Length];
            }
            Buffer.BlockCopy(bytes, 0, encryptedBytes, 0, encryptedBytes.Length);

            byte[] decryptedBytes = this.key.Decrypt(encryptedBytes, this.iv);

            // log the decrypted data
            #if DEBUG
            this.decryptedRequest = Encoding.UTF8.GetString(decryptedBytes);
            #endif

            System.IO.MemoryStream stream = new System.IO.MemoryStream(decryptedBytes);
            using (stream)
            {
                GNTPStreamReader reader = new GNTPStreamReader(stream);
                using (reader)
                {
                    // main headers
                    while (!reader.EndOfStream)
                    {
                        string s = reader.ReadLine().Trim();

                        if (String.IsNullOrEmpty(s))
                        {
                            // if this is a REGISTER message, check Notifications-Count value
                            // to see how many notification sections to expect
                            if (this.directive == RequestType.REGISTER)
                            {
                                if (this.expectedNotifications == 0)
                                {
                                    // a REGISTER request with no notifications is not valid
                                    OnError(ErrorCode.INVALID_REQUEST, ErrorDescription.NO_NOTIFICATIONS_REGISTERED);
                                }
                            }
                            break;
                        }
                        else
                        {
                            Header header = Header.ParseHeader(s);
                            if (header != null)
                            {
                                bool addHeader = true;
                                if (header.Name == Header.APPLICATION_NAME)
                                {
                                    this.applicationName = header.Value;
                                }
                                if (header.Name == Header.NOTIFICATIONS_COUNT)
                                {
                                    this.expectedNotifications = Convert.ToInt32(header.Value);
                                    this.expectedNotificationsRemaining = this.expectedNotifications;
                                    this.currentNotification = 1;
                                }
                                if (header.Name == Header.NOTIFICATION_CALLBACK_CONTEXT)
                                {
                                    this.callbackData = header.Value;
                                }
                                if (header.Name == Header.NOTIFICATION_CALLBACK_CONTEXT_TYPE)
                                {
                                    this.callbackDataType = header.Value;
                                }
                                if (header.Name == Header.NOTIFICATION_CALLBACK_TARGET || header.Name == Header.NOTIFICATION_CALLBACK_CONTEXT_TARGET)   // left in for compatibility
                                {
                                    this.callbackUrl = header.Value;
                                }
                                if (header.Name == Header.RECEIVED)
                                {
                                    this.requestInfo.PreviousReceivedHeaders.Add(header);
                                    addHeader = false;
                                }

                                if (addHeader) this.headers.AddHeader(header);
                            }
                        }
                    }

                    // any notification type headers
                    if (this.expectedNotifications > 0)
                    {
                        while (!reader.EndOfStream)
                        {
                            string s = reader.ReadLine().Trim();

                            if (s == String.Empty)
                            {
                                this.expectedNotificationsRemaining--;
                                if (this.expectedNotificationsRemaining > 0)
                                {
                                    this.currentNotification++;
                                }
                                else
                                {
                                    break;
                                }
                            }
                            else
                            {
                                if (this.notificationsToBeRegistered.Count < this.currentNotification)
                                {
                                    this.notificationsToBeRegistered.Add(new HeaderCollection());
                                }

                                Header header = Header.ParseHeader(s);
                                this.notificationsToBeRegistered[this.currentNotification - 1].AddHeader(header);
                            }
                        }
                    }

                    // now that we have read the stream, check for any embedded resources
                    this.pointersExpected = GetNumberOfPointers();
                    if (this.pointersExpected > 0)
                    {
                        this.pointersExpectedRemaining = this.pointersExpected;
                        this.currentPointer = 1;
                    }
                }
            }
        }
        /// <summary>
        /// Parses a response message and returns the corresponding <see cref="Response"/> object
        /// </summary>
        /// <param name="message">The entire GNTP response message</param>
        /// <param name="headers">The <see cref="HeaderCollection"/> of parsed header values</param>
        /// <returns><see cref="Response"/></returns>
        private Response Parse(string message, out HeaderCollection headers)
        {
            ResponseType responseType = ResponseType.ERROR;
            Response     response     = null;

            headers = new HeaderCollection();

            byte[] bytes = System.Text.Encoding.UTF8.GetBytes(message);
            System.IO.MemoryStream stream = new System.IO.MemoryStream(bytes);
            using (stream)
            {
                GNTPStreamReader reader = new GNTPStreamReader(stream);
                using (reader)
                {
                    bool isError     = false;
                    bool isFirstLine = true;
                    while (!reader.EndOfStream)
                    {
                        string line = reader.ReadLine();

                        if (isFirstLine)
                        {
                            Match match = ParseGNTPHeaderLine(line);
                            if (match.Success)
                            {
                                this.version   = match.Groups["Version"].Value;
                                this.directive = match.Groups["Directive"].Value;
                                if (this.directive.StartsWith("-", StringComparison.InvariantCulture))
                                {
                                    this.directive = this.directive.Remove(0, 1);
                                }

                                if (version == GNTP_SUPPORTED_VERSION)
                                {
                                    if (Enum.IsDefined(typeof(ResponseType), this.directive))
                                    {
                                        responseType = (ResponseType)Enum.Parse(typeof(ResponseType), this.directive, false);
                                        response     = new Response();
                                        if (responseType == ResponseType.ERROR)
                                        {
                                            isError = true;
                                        }
                                        isFirstLine = false;
                                    }
                                    else
                                    {
                                        // invalid directive
                                        response = new Response(ErrorCode.INVALID_REQUEST, "Unrecognized response type");
                                        break;
                                    }
                                }
                                else
                                {
                                    // invalid version
                                    response = new Response(ErrorCode.UNKNOWN_PROTOCOL_VERSION, "Unsupported version");
                                    break;
                                }
                            }
                            else
                            {
                                // invalid message header
                                response = new Response(ErrorCode.UNKNOWN_PROTOCOL, "Unrecognized response");
                                break;
                            }
                        }
                        else
                        {
                            Header header = Header.ParseHeader(line);
                            headers.AddHeader(header);
                        }
                    }

                    if (response != null)
                    {
                        if (isError)
                        {
                            int    errorCode        = headers.GetHeaderIntValue(Header.ERROR_CODE, false);
                            string errorDescription = headers.GetHeaderStringValue(Header.ERROR_DESCRIPTION, false);
                            if (errorCode > 0 && errorDescription != null)
                            {
                                response = new Response(errorCode, errorDescription);
                            }
                            else
                            {
                                response = new Response(ErrorCode.INTERNAL_SERVER_ERROR, ErrorDescription.INTERNAL_SERVER_ERROR);
                            }
                        }
                        else
                        {
                            string inResponseTo = headers.GetHeaderStringValue(Header.RESPONSE_ACTION, false);
                            response.InResponseTo = inResponseTo;
                        }

                        response.SetAttributesFromHeaders(headers, (responseType == ResponseType.CALLBACK));
                    }
                    else
                    {
                        // if we got here, that is bad.
                        response = new Response(ErrorCode.INTERNAL_SERVER_ERROR, ErrorDescription.INTERNAL_SERVER_ERROR);
                    }
                }
            }

            return(response);
        }