private void ProcessBuffer()

        {
            try

            {
                Data data = new Data(buffer.ToArray());

                string s = data.ToString();

                alreadyReceived.Append(s);



                if (tag == START_TAG)

                {
                    // start looking for GNTP header

                    tag = GNTP_IDENTIFIER_TAG;

                    ContinueProcessing();
                }



                else if (tag == GNTP_IDENTIFIER_TAG)

                {
                    string line = s;

                    Match match = ParseGNTPHeaderLine(line, this.passwordRequired);



                    if (match.Success)

                    {
                        this.version = match.Groups["Version"].Value;

                        if (version == MessageParser.GNTP_SUPPORTED_VERSION)

                        {
                            string d = match.Groups["Directive"].Value;

                            if (Enum.IsDefined(typeof(RequestType), d))

                            {
                                this.directive = (RequestType)Enum.Parse(typeof(RequestType), d);



                                // check for supported but not allowed requests

                                if (this.directive == RequestType.SUBSCRIBE && !this.allowSubscriptions)

                                {
                                    OnError(ErrorCode.NOT_AUTHORIZED, ErrorDescription.SUBSCRIPTIONS_NOT_ALLOWED);
                                }

                                else

                                {
                                    this.encryptionAlgorithm = Cryptography.GetEncryptionType(match.Groups["EncryptionAlgorithm"].Value);

                                    this.ivHex = (match.Groups["IV"] != null ? match.Groups["IV"].Value : null);

                                    if (!String.IsNullOrEmpty(this.ivHex))
                                    {
                                        this.iv = Cryptography.HexUnencode(this.ivHex);
                                    }

                                    string keyHash = match.Groups["KeyHash"].Value.ToUpper();



                                    bool authorized = false;

                                    // Any of the following criteria require a password:

                                    //    1. the request did not originate on the local machine or LAN

                                    //    2. the request came from the LAN, but LAN passwords are required

                                    //    2. it is a SUBSCRIBE request (all subscriptions require a password)

                                    //    3. the user's preferences require even local requests to supply a password

                                    // Additionally, even if a password is not required, it will be validated if the

                                    // sending appplication includes one

                                    string errorDescription = ErrorDescription.INVALID_KEY;

                                    if (this.passwordRequired || this.directive == RequestType.SUBSCRIBE || !String.IsNullOrEmpty(keyHash))

                                    {
                                        if (String.IsNullOrEmpty(keyHash))

                                        {
                                            errorDescription = ErrorDescription.MISSING_KEY;
                                        }

                                        else

                                        {
                                            string keyHashAlgorithmType = match.Groups["KeyHashAlgorithm"].Value;

                                            this.keyHashAlgorithm = Cryptography.GetKeyHashType(keyHashAlgorithmType);

                                            string salt = match.Groups["Salt"].Value.ToUpper();

                                            authorized = this.passwordManager.IsValid(keyHash, salt, this.keyHashAlgorithm, this.encryptionAlgorithm, out this.key);
                                        }
                                    }

                                    else

                                    {
                                        authorized = true;

                                        this.key = Key.None;
                                    }



                                    if (authorized)

                                    {
                                        if (this.encryptionAlgorithm == Cryptography.SymmetricAlgorithmType.PlainText)

                                        {
                                            tag = HEADER_TAG;

                                            ContinueProcessing();
                                        }

                                        else

                                        {
                                            tag = ENCRYPTED_HEADERS_TAG;

                                            ContinueProcessing();
                                        }
                                    }

                                    else

                                    {
                                        OnError(ErrorCode.NOT_AUTHORIZED, errorDescription);
                                    }
                                }
                            }

                            else

                            {
                                OnError(ErrorCode.INVALID_REQUEST, ErrorDescription.UNSUPPORTED_DIRECTIVE, d);
                            }
                        }

                        else

                        {
                            OnError(ErrorCode.UNKNOWN_PROTOCOL_VERSION, ErrorDescription.UNSUPPORTED_VERSION, version);
                        }
                    }

                    else

                    {
                        OnError(ErrorCode.UNKNOWN_PROTOCOL, ErrorDescription.MALFORMED_REQUEST);
                    }
                }



                else if (tag == HEADER_TAG)

                {
                    if (s == MessageParser.BLANK_LINE)

                    {
                        // 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)

                            {
                                tag = NOTIFICATION_TYPE_TAG;

                                ContinueProcessing();
                            }

                            else

                            {
                                // a REGISTER request with no notifications is not valid

                                OnError(ErrorCode.INVALID_REQUEST, ErrorDescription.NO_NOTIFICATIONS_REGISTERED);
                            }
                        }

                        else

                        {
                            // otherwise, check the number of resource pointers we got and start reading those

                            this.pointersExpected = GetNumberOfPointers();

                            if (this.pointersExpected > 0)

                            {
                                this.pointersExpectedRemaining = this.pointersExpected;

                                this.currentPointer = 1;

                                tag = RESOURCE_HEADER_TAG;

                                ContinueProcessing();
                            }

                            else

                            {
                                OnMessageParsed();
                            }
                        }
                    }

                    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);
                            }
                        }

                        tag = HEADER_TAG;

                        ContinueProcessing();
                    }
                }



                else if (tag == NOTIFICATION_TYPE_TAG)

                {
                    if (s == MessageParser.BLANK_LINE)

                    {
                        this.expectedNotificationsRemaining--;

                        if (this.expectedNotificationsRemaining > 0)

                        {
                            this.currentNotification++;

                            tag = NOTIFICATION_TYPE_TAG;

                            ContinueProcessing();
                        }

                        else

                        {
                            // otherwise, check the number of resource pointers we got and start reading those

                            this.pointersExpected = GetNumberOfPointers();

                            if (this.pointersExpected > 0)

                            {
                                this.pointersExpectedRemaining = this.pointersExpected;

                                this.currentPointer = 1;

                                tag = RESOURCE_HEADER_TAG;

                                ContinueProcessing();
                            }

                            else

                            {
                                OnMessageParsed();
                            }
                        }
                    }

                    else

                    {
                        if (this.notificationsToBeRegistered.Count < this.currentNotification)

                        {
                            this.notificationsToBeRegistered.Add(new HeaderCollection());
                        }



                        Header header = Header.ParseHeader(s);

                        this.notificationsToBeRegistered[this.currentNotification - 1].AddHeader(header);

                        tag = NOTIFICATION_TYPE_TAG;

                        ContinueProcessing();
                    }
                }



                else if (tag == RESOURCE_HEADER_TAG)

                {
                    if (s == MessageParser.BLANK_LINE)

                    {
                        // we should have found an Identifier header and Length header, or we are just starting a new section

                        Pointer p = this.pointers[this.currentPointer - 1];

                        if (p.Identifier != null && p.Length > 0)

                        {
                            // read #of bytes

                            int length = this.pointers[this.currentPointer - 1].Length;

                            tag = RESOURCE_TAG;

                            ContinueProcessing();
                        }

                        else

                        {
                            tag = RESOURCE_HEADER_TAG;

                            ContinueProcessing();
                        }
                    }

                    else

                    {
                        Header header = Header.ParseHeader(s);

                        // should be Identifer or Length

                        if (header != null)

                        {
                            bool validHeader = false;

                            if (header.Name == Header.RESOURCE_IDENTIFIER)

                            {
                                this.pointers[this.currentPointer - 1].Identifier = header.Value;

                                validHeader = true;
                            }

                            else if (header.Name == Header.RESOURCE_LENGTH)

                            {
                                this.pointers[this.currentPointer - 1].Length = Convert.ToInt32(header.Value);

                                validHeader = true;
                            }

                            else

                            {
                                OnError(ErrorCode.INVALID_REQUEST, ErrorDescription.UNRECOGNIZED_RESOURCE_HEADER, header.Name);
                            }



                            if (validHeader)

                            {
                                tag = RESOURCE_HEADER_TAG;

                                ContinueProcessing();
                            }
                        }

                        else

                        {
                            OnError(ErrorCode.INVALID_REQUEST, ErrorDescription.UNRECOGNIZED_RESOURCE_HEADER);
                        }
                    }
                }



                else if (tag == RESOURCE_TAG)

                {
                    // deal with data bytes

                    byte[] bytes = this.key.Decrypt(data.ByteArray, this.iv);



                    Pointer pointer = this.pointers[this.currentPointer - 1];

                    pointer.ByteArray = bytes;

                    BinaryData binaryData = new BinaryData(pointer.Identifier, pointer.ByteArray);

                    ResourceCache.Add(this.applicationName, binaryData);



                    this.pointersExpectedRemaining--;

                    if (this.pointersExpectedRemaining > 0)

                    {
                        this.currentPointer++;

                        tag = RESOURCE_HEADER_TAG;

                        ContinueProcessing();
                    }

                    else

                    {
                        OnMessageParsed();
                    }
                }



                else if (tag == ENCRYPTED_HEADERS_TAG)

                {
                    // see if a length was specified (the original spec did not require the main encrypted headers portion to specify a length)

                    if (s.StartsWith(Header.RESOURCE_LENGTH))

                    {
                        Header header = Header.ParseHeader(s);

                        if (header != null)

                        {
                            int len = Convert.ToInt32(header.Value);

                            tag = ENCRYPTED_HEADERS_TAG;

                            ContinueProcessing();
                        }
                    }



                    ParseEncryptedMessage(data.ByteArray);

                    if (this.pointersExpected > 0)

                    {
                        tag = RESOURCE_HEADER_TAG;

                        ContinueProcessing();
                    }

                    else

                    {
                        OnMessageParsed();
                    }
                }



                else

                {
                    OnError(ErrorCode.INVALID_REQUEST, ErrorDescription.MALFORMED_REQUEST);
                }
            }

            catch (GrowlException gEx)

            {
                OnError(gEx.ErrorCode, gEx.Message, gEx.AdditionalInfo);
            }

            catch (Exception ex)

            {
                OnError(ErrorCode.INVALID_REQUEST, ErrorDescription.MALFORMED_REQUEST, ex.Message);
            }
        }
        /// <summary>

        /// Gets the number of pointers that need to be read from the request, taking

        /// into account any pre-cached binary data.

        /// </summary>

        /// <returns>The number of pointers that need to be read</returns>

        private int GetNumberOfPointers()

        {
            /* check to see if we have already cached the data for
             *
             * all pointers. if so, we can skip reading the binary sections.
             *
             * (unfortunately, if we have cached some of the sections but not all,
             *
             * we will probably have to read it all in again, depending on the order
             *
             * they are included)
             *
             * */

            int c = 0;

            foreach (Header header in this.headers.Pointers)

            {
                Pointer pointer = new Pointer(this.headers);

                this.pointers.Add(pointer);



                if (ResourceCache.IsCached(this.applicationName, header.GrowlResourcePointerID))

                {
                    BinaryData data = ResourceCache.Get(this.applicationName, header.GrowlResourcePointerID);

                    pointer.Identifier = header.GrowlResourcePointerID;

                    pointer.ByteArray = data.Data;

                    c++;
                }
            }

            foreach (HeaderCollection notification in this.notificationsToBeRegistered)

            {
                foreach (Header header in notification.Pointers)

                {
                    Pointer pointer = new Pointer(notification);

                    this.pointers.Add(pointer);



                    if (ResourceCache.IsCached(this.applicationName, header.GrowlResourcePointerID))

                    {
                        BinaryData data = ResourceCache.Get(this.applicationName, header.GrowlResourcePointerID);

                        pointer.Identifier = header.GrowlResourcePointerID;

                        pointer.ByteArray = data.Data;

                        c++;
                    }
                }
            }

            int p = this.pointers.Count;



            // TODO: this effectively causes no cached resources to ever be used

            //c = int.MaxValue;



            // check to see if all pointers were already cached

            if (p == c)
            {
                p = 0;          // if #cached == total#, we dont need to read any
            }
            else

            {
                // not all of the items are cached, so we have to re-read all of them.

                // to do so, we have to clear any pointer data we may have set

                foreach (Pointer pointer in this.pointers)

                {
                    pointer.Clear();
                }
            }



            if (c > 0 && p == 0)

            {
                requestInfo.SaveHandlingInfo("ALL BINARY RESOURCES ALREADY CACHED");
            }



            return(p);
        }