|  |  | @ -54,7 +54,12 @@ void AssertIsOnSendingQueue() | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | static void *kNSError_MessageSender_IsRetryable = &kNSError_MessageSender_IsRetryable; |  |  |  | static void *kNSError_MessageSender_IsRetryable = &kNSError_MessageSender_IsRetryable; | 
			
		
	
		
		
			
				
					
					|  |  |  | static void *kNSError_MessageSender_ShouldBeIgnoredForGroups = &kNSError_MessageSender_ShouldBeIgnoredForGroups; |  |  |  | static void *kNSError_MessageSender_ShouldBeIgnoredForGroups = &kNSError_MessageSender_ShouldBeIgnoredForGroups; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | static void *kNSError_MessageSender_IsFatal = &kNSError_MessageSender_IsFatal; | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | // isRetryable and isFatal are opposites but not redundant. | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | // | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | // If a group message send fails, the send will be retried if any of the errors were retryable UNLESS | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | // any of the errors were fatal.  Fatal errors trump retryable errors. | 
			
		
	
		
		
			
				
					
					|  |  |  | @implementation NSError (OWSMessageSender) |  |  |  | @implementation NSError (OWSMessageSender) | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | - (BOOL)isRetryable |  |  |  | - (BOOL)isRetryable | 
			
		
	
	
		
		
			
				
					|  |  | @ -84,6 +89,19 @@ static void *kNSError_MessageSender_ShouldBeIgnoredForGroups = &kNSError_Message | 
			
		
	
		
		
			
				
					
					|  |  |  |     objc_setAssociatedObject(self, kNSError_MessageSender_ShouldBeIgnoredForGroups, @(value), OBJC_ASSOCIATION_COPY); |  |  |  |     objc_setAssociatedObject(self, kNSError_MessageSender_ShouldBeIgnoredForGroups, @(value), OBJC_ASSOCIATION_COPY); | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | - (BOOL)isFatal | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     NSNumber *value = objc_getAssociatedObject(self, kNSError_MessageSender_IsFatal); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     // This value will NOT always be set for all errors by the time we query it's value. | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     // Default to NOT fatal. | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     return value ? [value boolValue] : NO; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | - (void)setIsFatal:(BOOL)value | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     objc_setAssociatedObject(self, kNSError_MessageSender_IsFatal, @(value), OBJC_ASSOCIATION_COPY); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | @end |  |  |  | @end | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | #pragma mark - |  |  |  | #pragma mark - | 
			
		
	
	
		
		
			
				
					|  |  | @ -266,7 +284,7 @@ NSUInteger const OWSSendMessageOperationMaxRetries = 4; | 
			
		
	
		
		
			
				
					
					|  |  |  |     RetryableFailureHandler retryableFailureHandler = ^(NSError *_Nonnull error) { |  |  |  |     RetryableFailureHandler retryableFailureHandler = ^(NSError *_Nonnull error) { | 
			
		
	
		
		
			
				
					
					|  |  |  |         DDLogInfo(@"%@ Sending failed.", self.tag); |  |  |  |         DDLogInfo(@"%@ Sending failed.", self.tag); | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |         if (![error isRetryable]) { |  |  |  |         if (![error isRetryable] || [error isFatal]) { | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |             DDLogInfo(@"%@ Skipping retry due to terminal error: %@", self.tag, error); |  |  |  |             DDLogInfo(@"%@ Skipping retry due to terminal error: %@", self.tag, error); | 
			
		
	
		
		
			
				
					
					|  |  |  |             self.failureHandler(error); |  |  |  |             self.failureHandler(error); | 
			
		
	
		
		
			
				
					
					|  |  |  |             return; |  |  |  |             return; | 
			
		
	
	
		
		
			
				
					|  |  | @ -748,6 +766,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; | 
			
		
	
		
		
			
				
					
					|  |  |  |                     id failureResult = groupSendFuture.forceGetFailure; |  |  |  |                     id failureResult = groupSendFuture.forceGetFailure; | 
			
		
	
		
		
			
				
					
					|  |  |  |                     if ([failureResult isKindOfClass:[NSError class]]) { |  |  |  |                     if ([failureResult isKindOfClass:[NSError class]]) { | 
			
		
	
		
		
			
				
					
					|  |  |  |                         NSError *error = failureResult; |  |  |  |                         NSError *error = failureResult; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |                         // Some errors should be ignored when sending messages |  |  |  |                         // Some errors should be ignored when sending messages | 
			
		
	
		
		
			
				
					
					|  |  |  |                         // to groups.  See discussion on |  |  |  |                         // to groups.  See discussion on | 
			
		
	
		
		
			
				
					
					|  |  |  |                         // NSError (OWSMessageSender) category. |  |  |  |                         // NSError (OWSMessageSender) category. | 
			
		
	
	
		
		
			
				
					|  |  | @ -755,6 +774,15 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; | 
			
		
	
		
		
			
				
					
					|  |  |  |                             continue; |  |  |  |                             continue; | 
			
		
	
		
		
			
				
					
					|  |  |  |                         } |  |  |  |                         } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                         // Some errors should never be retried, in order to avoid | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                         // hitting rate limits, for example.  Unfortunately, since | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                         // group send retry is all-or-nothing, we need to fail | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                         // immediately even if some of the other recipients had | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                         // retryable errors. | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                         if ([error isFatal]) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                             failureHandler(error); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                         } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |                         if ([error isRetryable] && !firstRetryableError) { |  |  |  |                         if ([error isRetryable] && !firstRetryableError) { | 
			
		
	
		
		
			
				
					
					|  |  |  |                             firstRetryableError = error; |  |  |  |                             firstRetryableError = error; | 
			
		
	
		
		
			
				
					
					|  |  |  |                         } else if (![error isRetryable] && !firstNonRetryableError) { |  |  |  |                         } else if (![error isRetryable] && !firstNonRetryableError) { | 
			
		
	
	
		
		
			
				
					|  |  | @ -859,6 +887,8 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; | 
			
		
	
		
		
			
				
					
					|  |  |  |             // Key will continue to be unaccepted, so no need to retry. It'll only cause us to hit the Pre-Key request |  |  |  |             // Key will continue to be unaccepted, so no need to retry. It'll only cause us to hit the Pre-Key request | 
			
		
	
		
		
			
				
					
					|  |  |  |             // rate limit |  |  |  |             // rate limit | 
			
		
	
		
		
			
				
					
					|  |  |  |             [error setIsRetryable:NO]; |  |  |  |             [error setIsRetryable:NO]; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             // Avoid the "Too many failures with this contact" error rate limiting. | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             [error setIsFatal:YES]; | 
			
		
	
		
		
			
				
					
					|  |  |  |             return failureHandler(error); |  |  |  |             return failureHandler(error); | 
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
	
		
		
			
				
					|  |  | @ -869,6 +899,8 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |             // We're already rate-limited. No need to exacerbate the problem. |  |  |  |             // We're already rate-limited. No need to exacerbate the problem. | 
			
		
	
		
		
			
				
					
					|  |  |  |             [error setIsRetryable:NO]; |  |  |  |             [error setIsRetryable:NO]; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             // Avoid exacerbating the rate limiting. | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             [error setIsFatal:YES]; | 
			
		
	
		
		
			
				
					
					|  |  |  |             return failureHandler(error); |  |  |  |             return failureHandler(error); | 
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
	
		
		
			
				
					|  |  | 
 |