/// <summary>
        /// Reads the Flash policy request and returns the policy response if Flash requests are allowed.
        /// </summary>
        /// <param name="sender">The <see cref="AsyncSocket"/></param>
        /// <param name="readBytes">The bytes read from the socket</param>
        /// <param name="tag">A tag identifying the read request</param>
        void ReadPolicyRequest(AsyncSocket sender, byte[] readBytes, long tag)
        {
            // remove this now that we are done with it
            this.socket.DidRead -= new AsyncSocket.SocketDidRead(ReadPolicyRequest);

            if (tag == FLASH_POLICY_REQUEST_TAG)
            {
                Data data = new Data(readBytes);
                this.AlreadyReceivedData.Append(data.ToString());

                string request = this.AlreadyReceivedData.ToString();
                if (request == FlashPolicy.REQUEST)
                {
                    if (this.allowFlash)
                    {
                        this.socket.Write(FlashPolicy.ResponseBytes, TIMEOUT_FLASHPOLICYRESPONSE, FLASH_POLICY_RESPONSE_TAG);
                        this.socket.CloseAfterWriting();
                    }
                    else
                    {
                        OnError(ErrorCode.NOT_AUTHORIZED, ErrorDescription.BROWSER_CONNECTIONS_NOT_ALLOWED);
                    }
                }
                else
                {
                    OnError(ErrorCode.INVALID_REQUEST, ErrorDescription.UNRECOGNIZED_REQUEST);
                }
            }
        }
        /// <summary>
        /// Reads the socket data and handles the request
        /// </summary>
        /// <param name="alreadyReadBytes">Any bytes that were already read from the socket</param>
        public void Read(byte[] alreadyReadBytes)
        {
            Data data = new Data(alreadyReadBytes);
            string s = data.ToString();
            this.AlreadyReceivedData.Append(s);

            this.socket.DidRead += new AsyncSocket.SocketDidRead(ReadPolicyRequest);
            this.socket.Read(FlashPolicy.REQUEST.Length - s.Length, TIMEOUT_FLASHPOLICYREQUEST, FLASH_POLICY_REQUEST_TAG);
        }
Example #3
0
        /// <summary>
        /// Reads the socket data and handles the request
        /// </summary>
        /// <param name="alreadyReadBytes">Any bytes that were already read from the socket</param>
        public void Read(byte[] alreadyReadBytes)
        {
            Data   data = new Data(alreadyReadBytes);
            string s    = data.ToString();

            this.AlreadyReceivedData.Append(s);

            this.socket.DidRead += new AsyncSocket.SocketDidRead(ReadPolicyRequest);
            this.socket.Read(FlashPolicy.REQUEST.Length - s.Length, TIMEOUT_FLASHPOLICYREQUEST, FLASH_POLICY_REQUEST_TAG);
        }
        /// <summary>

        /// Reads the Flash policy request and returns the policy response if Flash requests are allowed.

        /// </summary>

        /// <param name="sender">The <see cref="AsyncSocket"/></param>

        /// <param name="readBytes">The bytes read from the socket</param>

        /// <param name="tag">A tag identifying the read request</param>

        void ReadPolicyRequest(AsyncSocket sender, byte[] readBytes, long tag)

        {
            // remove this now that we are done with it

            this.socket.DidRead -= new AsyncSocket.SocketDidRead(ReadPolicyRequest);



            if (tag == FLASH_POLICY_REQUEST_TAG)

            {
                Data data = new Data(readBytes);

                this.AlreadyReceivedData.Append(data.ToString());



                string request = this.AlreadyReceivedData.ToString();

                if (request == FlashPolicy.REQUEST)

                {
                    if (this.allowFlash)

                    {
                        this.socket.Write(FlashPolicy.ResponseBytes, TIMEOUT_FLASHPOLICYRESPONSE, FLASH_POLICY_RESPONSE_TAG);

                        this.socket.CloseAfterWriting();
                    }

                    else

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

                else

                {
                    OnError(ErrorCode.INVALID_REQUEST, ErrorDescription.UNRECOGNIZED_REQUEST);
                }
            }
        }
        /// <summary>
        /// Handles the socket's DidRead event after reading only the first four bytes of data.
        /// </summary>
        /// <param name="socket">The <see cref="AsyncSocket"/></param>
        /// <param name="readBytes">Array of <see cref="byte"/>s that were read</param>
        /// <param name="tag">The tag identifying the read operation</param>
        private void SocketDidReadIndicatorBytes(AsyncSocket socket, byte[] readBytes, long tag)
        {
            // remove this event handler since we dont need it any more
            socket.DidRead -= new AsyncSocket.SocketDidRead(this.SocketDidReadIndicatorBytes);

            Data   data = new Data(readBytes);
            string s    = data.ToString();

            if (tag == ACCEPT_TAG)
            {
                if (s == FlashPolicy.REQUEST_INDICATOR)
                {
                    GNTPFlashSocketReader gfsr = new GNTPFlashSocketReader(socket, allowBrowserConnections);
                    gfsr.Read(readBytes);
                }
                else if (s == WebSocketHandshakeHandler.REQUEST_INDICATOR)
                {
                    // this is a GNTP over WebSocket request, so we have to do the WebSocket handshake first
                    socket.Tag = readBytes;
                    WebSocketHandshakeHandler wshh = new WebSocketHandshakeHandler(socket, "*", "ws://localhost:23053");
                    wshh.DoHandshake(delegate()
                    {
                        // now pass off to the GNTPWebSocketReader (which is just a normal GNTPSocketReader that can deal with the WebSocket framing of packets)
                        GNTPWebSocketReader gwsr = new GNTPWebSocketReader(socket, passwordManager, passwordRequired, allowNetworkNotifications, allowBrowserConnections, allowSubscriptions, this.requestInfo);
                        this.requestReader       = gwsr;
                        gwsr.MessageParsed      += new GNTPRequestReader.GNTPRequestReaderMessageParsedEventHandler(requestReader_MessageParsed);
                        gwsr.Error += new GNTPRequestReader.GNTPRequestReaderErrorEventHandler(requestReader_Error);
                        gwsr.Read(readBytes);
                    });
                }
                else
                {
                    // this is a normal GNTP/TCP connection, so handle it as such
                    GNTPSocketReader gsr = new GNTPSocketReader(socket, passwordManager, passwordRequired, allowNetworkNotifications, allowBrowserConnections, allowSubscriptions, this.requestInfo);
                    this.requestReader = gsr;
                    gsr.MessageParsed += new GNTPRequestReader.GNTPRequestReaderMessageParsedEventHandler(requestReader_MessageParsed);
                    gsr.Error         += new GNTPRequestReader.GNTPRequestReaderErrorEventHandler(requestReader_Error);
                    gsr.Read(readBytes);
                }
            }
            else
            {
                WriteError(socket, ErrorCode.INVALID_REQUEST, ErrorDescription.MALFORMED_REQUEST);
            }
        }
        /// <summary>

        /// Handles the socket's DidRead event.

        /// </summary>

        /// <param name="socket">The <see cref="AsyncSocket"/></param>

        /// <param name="readBytes">Array of <see cref="byte"/>s that were read</param>

        /// <param name="tag">The tag identifying the read operation</param>

        protected virtual void SocketDidRead(AsyncSocket socket, byte[] readBytes, long tag)

        {
            try

            {
                Data data = new Data(readBytes);

                this.AlreadyReceivedData.Append(data.ToString());



                GNTPParser parser = (GNTPParser)socket.Tag;

                NextIndicator next = parser.Parse(readBytes);

                if (next.ShouldContinue)

                {
                    if (next.UseBytes)
                    {
                        socket.Read(next.Bytes, TIMEOUT_GNTP_HEADER, parser.Tag);
                    }

                    else
                    {
                        socket.Read(next.Length, TIMEOUT_GNTP_BINARY, parser.Tag);
                    }
                }
            }

            catch (GrowlException gEx)

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

            catch (Exception ex)

            {
                OnError(ErrorCode.INVALID_REQUEST, ErrorDescription.MALFORMED_REQUEST, ex.Message);
            }
        }
        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>
        /// Logs the specified data.
        /// </summary>
        /// <param name="data">The <see cref="Data"/> to log</param>
        public void Log(Data data)
        {
            try
            {
                if (this.loggingEnabled && !String.IsNullOrEmpty(this.logFolder))
                {
                    string filename = String.Format(@"GNTP_{0}.txt", this.requestInfo.RequestID);
                    string filepath = PathUtility.Combine(this.logFolder, filename);
                    bool exists = System.IO.File.Exists(filepath);
                    if (!exists)
                    {
                        // log the initial request/response
                        System.IO.StreamWriter w = System.IO.File.CreateText(filepath);
                        using (w)
                        {
                            w.Write("Timestamp: {0}\r\n", DateTime.Now.ToString());
                            foreach (string item in this.requestInfo.HandlingInfo)
                            {
                                w.Write(String.Format("{0}\r\n", item));
                            }
                            w.Write(SEPERATOR);

                            if (this.requestReader != null)
                            {
                                w.Write(this.requestReader.ReceivedData);

                                if (!String.IsNullOrEmpty(this.requestReader.DecryptedData))
                                {
                                    w.Write(SEPERATOR);
                                    w.Write(this.requestReader.DecryptedData);
                                }
                            }

                            w.Write(SEPERATOR);
                            w.Write(data.ToString());
                            w.Close();
                            this.requestInfo.HandlingInfo.Clear();
                        }
                    }
                    else
                    {
                        // this is for logging callback data
                        System.IO.StreamWriter w = new System.IO.StreamWriter(filepath, true);
                        using (w)
                        {
                            w.Write(SEPERATOR);
                            w.Write("Timestamp: {0}\r\n", DateTime.Now.ToString());
                            foreach (string item in this.requestInfo.HandlingInfo)
                            {
                                w.Write(String.Format("{0}\r\n", item));
                            }
                            w.Write(SEPERATOR);
                            w.Write(data.ToString());
                            w.Close();
                        }
                    }
                }
            }
            catch
            {
                // suppress logging exceptions
            }
        }
        /// <summary>
        /// Handles the socket's DidRead event after reading only the first four bytes of data.
        /// </summary>
        /// <param name="socket">The <see cref="AsyncSocket"/></param>
        /// <param name="readBytes">Array of <see cref="byte"/>s that were read</param>
        /// <param name="tag">The tag identifying the read operation</param>
        private void SocketDidReadIndicatorBytes(AsyncSocket socket, byte[] readBytes, long tag)
        {
            // remove this event handler since we dont need it any more
            socket.DidRead -= new AsyncSocket.SocketDidRead(this.SocketDidReadIndicatorBytes);

            Data data = new Data(readBytes);
            string s = data.ToString();

            if (tag == ACCEPT_TAG)
            {
                if (s == FlashPolicy.REQUEST_INDICATOR)
                {
                    GNTPFlashSocketReader gfsr = new GNTPFlashSocketReader(socket, allowBrowserConnections);
                    gfsr.Read(readBytes);
                }
                else if (s == WebSocketHandshakeHandler.REQUEST_INDICATOR)
                {
                    // this is a GNTP over WebSocket request, so we have to do the WebSocket handshake first
                    socket.Tag = readBytes;
                    WebSocketHandshakeHandler wshh = new WebSocketHandshakeHandler(socket, "*", "ws://localhost:23053");
                    wshh.DoHandshake(delegate()
                    {
                        // now pass off to the GNTPWebSocketReader (which is just a normal GNTPSocketReader that can deal with the WebSocket framing of packets)
                        GNTPWebSocketReader gwsr = new GNTPWebSocketReader(socket, passwordManager, passwordRequired, allowNetworkNotifications, allowBrowserConnections, allowSubscriptions, this.requestInfo);
                        this.requestReader = gwsr;
                        gwsr.MessageParsed += new GNTPRequestReader.GNTPRequestReaderMessageParsedEventHandler(requestReader_MessageParsed);
                        gwsr.Error += new GNTPRequestReader.GNTPRequestReaderErrorEventHandler(requestReader_Error);
                        gwsr.Read(readBytes);
                    });
                }
                else
                {
                    // this is a normal GNTP/TCP connection, so handle it as such
                    GNTPSocketReader gsr = new GNTPSocketReader(socket, passwordManager, passwordRequired, allowNetworkNotifications, allowBrowserConnections, allowSubscriptions, this.requestInfo);
                    this.requestReader = gsr;
                    gsr.MessageParsed += new GNTPRequestReader.GNTPRequestReaderMessageParsedEventHandler(requestReader_MessageParsed);
                    gsr.Error += new GNTPRequestReader.GNTPRequestReaderErrorEventHandler(requestReader_Error);
                    gsr.Read(readBytes);
                }
            }
            else
            {
                WriteError(socket, ErrorCode.INVALID_REQUEST, ErrorDescription.MALFORMED_REQUEST);
            }
        }
Example #10
0
        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>
        /// Handles the socket's DidRead event.
        /// </summary>
        /// <param name="socket">The <see cref="AsyncSocket"/></param>
        /// <param name="readBytes">Array of <see cref="byte"/>s that were read</param>
        /// <param name="tag">The tag identifying the read operation</param>
        protected virtual void SocketDidRead(AsyncSocket socket, byte[] readBytes, long tag)
        {
            try
            {
                Data data = new Data(readBytes);
                this.AlreadyReceivedData.Append(data.ToString());

                GNTPParser parser = (GNTPParser)socket.Tag;
                NextIndicator next = parser.Parse(readBytes);
                if (next.ShouldContinue)
                {
                    if (next.UseBytes)
                        socket.Read(next.Bytes, TIMEOUT_GNTP_HEADER, parser.Tag);
                    else
                        socket.Read(next.Length, TIMEOUT_GNTP_BINARY, parser.Tag);
                }
            }
            catch (GrowlException gEx)
            {
                OnError(gEx.ErrorCode, gEx.Message, gEx.AdditionalInfo);
            }
            catch (Exception ex)
            {
                OnError(ErrorCode.INVALID_REQUEST, ErrorDescription.MALFORMED_REQUEST, ex.Message);
            }
        }