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