2 * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
30 #import <Foundation/NSArray.h>
31 #import <Foundation/Foundation.h>
33 #import <Security/SecBasePriv.h>
34 #import <Security/SecItemPriv.h>
35 #import <utilities/debugging.h>
38 #include <Security/CKBridge/SOSCloudKeychainConstants.h>
39 #include <Security/SecureObjectSync/SOSARCDefines.h>
40 #include <Security/SecureObjectSync/SOSCloudCircle.h>
41 #include <Security/SecureObjectSync/SOSCloudCircleInternal.h>
44 #import <os/activity.h>
46 #include <utilities/SecAKSWrappers.h>
47 #include <utilities/SecCFRelease.h>
48 #include <AssertMacros.h>
51 #import "IDSPersistentState.h"
53 static const char *kStreamName = "com.apple.notifyd.matching";
54 NSString *const IDSSendMessageOptionForceEncryptionOffKey = @"IDSSendMessageOptionForceEncryptionOff";
56 static const int64_t kAttemptFlushBufferInterval = (NSEC_PER_SEC * 15);
57 static const int64_t kSyncTimerLeeway = (NSEC_PER_MSEC * 250); // 250ms leeway for handling unhandled messages.
58 static const int64_t kMaxMessageRetryDelay = (NSEC_PER_SEC * 5); // 5s maximun delay for a given request
59 static const int64_t kMinMessageRetryDelay = (NSEC_PER_MSEC * 500); // 500ms minimum delay before attempting to retry handling messages.
61 #define SECD_RUN_AS_ROOT_ERROR 550
64 @implementation IDSKeychainSyncingProxy
66 + (IDSKeychainSyncingProxy *) idsProxy
68 static IDSKeychainSyncingProxy *idsProxy;
70 static dispatch_once_t onceToken;
71 dispatch_once(&onceToken, ^{
72 idsProxy = [[self alloc] init];
80 if([_unhandledMessageBuffer count] > 0){
81 [IDSKeychainSyncingProxyPersistentState setUnhandledMessages:_unhandledMessageBuffer];
85 - (void) importKeyInterests: (NSMutableDictionary*) unhandledMessages
87 _unhandledMessageBuffer = unhandledMessages;
92 if (self = [super init])
94 secnotice("event", "%@ start", self);
96 _isIDSInitDone = false;
98 _calloutQueue = dispatch_queue_create("IDSCallout", DISPATCH_QUEUE_SERIAL);
99 _unhandledMessageBuffer = [ [NSMutableDictionary alloc] initWithCapacity: 0];
100 _isSecDRunningAsRoot = false;
101 secdebug(IDSPROXYSCOPE, "%@ done", self);
103 [self doIDSInitialization];
105 [self doSetIDSDeviceID:nil];
107 _syncTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
108 dispatch_source_set_timer(_syncTimer, DISPATCH_TIME_FOREVER, DISPATCH_TIME_FOREVER, kSyncTimerLeeway);
109 dispatch_source_set_event_handler(_syncTimer, ^{
112 dispatch_resume(_syncTimer);
114 [self importKeyInterests: [IDSKeychainSyncingProxyPersistentState unhandledMessages]];
116 xpc_set_event_stream_handler(kStreamName, dispatch_get_main_queue(),
117 ^(xpc_object_t notification){
118 [self streamEvent:notification];
121 [self updateUnlockedSinceBoot];
122 [self updateIsLocked];
124 [self keybagDidUnlock];
130 - (void) keybagDidLock
132 secnotice("event", "%@", self);
135 - (void) keybagDidUnlock
137 secnotice("event", "%@", self);
138 [self handleAllPendingMessage];
141 - (BOOL) updateUnlockedSinceBoot
143 CFErrorRef aksError = NULL;
144 if (!SecAKSGetHasBeenUnlocked(&_unlockedSinceBoot, &aksError)) {
145 secerror("%@ Got error from SecAKSGetHasBeenUnlocked: %@", self, aksError);
146 CFReleaseSafe(aksError);
152 - (BOOL) updateIsLocked
154 CFErrorRef aksError = NULL;
155 if (!SecAKSGetIsLocked(&_isLocked, &aksError)) {
156 secerror("%@ Got error querying lock state: %@", self, aksError);
157 CFReleaseSafe(aksError);
161 _unlockedSinceBoot = YES;
165 - (void) keybagStateChange
167 os_activity_initiate("keybagStateChanged", OS_ACTIVITY_FLAG_DEFAULT, ^{
168 BOOL wasLocked = _isLocked;
169 if ([self updateIsLocked]) {
170 if (wasLocked == _isLocked)
171 secdebug("event", "%@ still %s ignoring", self, _isLocked ? "locked" : "unlocked");
173 [self keybagDidLock];
175 [self keybagDidUnlock];
180 - (void)streamEvent:(xpc_object_t)notification
182 #if (!TARGET_IPHONE_SIMULATOR)
183 const char *notificationName = xpc_dictionary_get_string(notification, "Notification");
184 if (!notificationName) {
185 } else if (strcmp(notificationName, kUserKeybagStateChangeNotification)==0) {
186 return [self keybagStateChange];
188 const char *eventName = xpc_dictionary_get_string(notification, "XPCEventName");
189 char *desc = xpc_copy_description(notification);
190 secnotice("event", "%@ event: %s name: %s desc: %s", self, eventName, notificationName, desc);
198 secdebug("IDS Transport", "%@ attempting to hand unhandled messages to securityd, here is our message queue: %@", self, _unhandledMessageBuffer);
199 if([_unhandledMessageBuffer count] == 0)
200 _syncTimerScheduled = NO;
201 else if (_syncTimerScheduled && !_isLocked){
202 [self handleAllPendingMessage];
206 - (dispatch_time_t) nextSyncTime
208 secdebug("IDS Transport", "nextSyncTime");
210 dispatch_time_t nextSync = dispatch_time(DISPATCH_TIME_NOW, kMinMessageRetryDelay);
212 // Don't sync again unless we waited at least kAttemptFlushBufferInterval
214 dispatch_time_t soonest = dispatch_time(_lastSyncTime, kAttemptFlushBufferInterval);
215 if (nextSync < soonest || _deadline < soonest) {
216 secdebug("timer", "%@ backing off", self);
221 // Don't delay more than kMaxMessageRetryDelay after the first request.
222 if (nextSync > _deadline) {
223 secdebug("timer", "%@ hit deadline", self);
227 // Bump the timer by kMinMessageRetryDelay
228 if (_syncTimerScheduled)
229 secdebug("timer", "%@ bumped timer", self);
231 secdebug("timer", "%@ scheduled timer", self);
236 - (void)scheduleSyncRequestTimer
238 secdebug("IDS Transport", "scheduling sync request timer");
239 dispatch_source_set_timer(_syncTimer, [self nextSyncTime], DISPATCH_TIME_FOREVER, kSyncTimerLeeway);
240 _syncTimerScheduled = YES;
244 - (void)setItemsChangedBlock:(CloudItemsChangedBlock)itemsChangedBlock
246 self->itemsChangedCallback = itemsChangedBlock;
249 - (void)doIDSInitialization{
251 secnotice("IDS Transport", "doIDSInitialization!");
253 _service = [[IDSService alloc] initWithService: @IDSServiceNameKeychainSync];
255 if( _service == nil ){
256 _isIDSInitDone = false;
257 secerror("Could not create ids service");
260 secnotice("IDS Transport", "IDS Transport Successfully set up IDS!");
261 [_service addDelegate:self queue: dispatch_get_main_queue()];
263 _isIDSInitDone = true;
264 if(_isSecDRunningAsRoot == false)
265 [self doSetIDSDeviceID:nil];
269 - (void) calloutWith: (void(^)(NSMutableDictionary *pending, bool handlePendingMesssages, bool doSetDeviceID, dispatch_queue_t queue, void(^done)(NSMutableDictionary *handledMessages, bool handledPendingMessage, bool handledSettingDeviceID))) callout
271 // In IDSKeychainSyncingProxy serial queue
272 dispatch_queue_t idsproxy_queue = dispatch_get_main_queue();
276 // dispatch_get_global_queue - well-known global concurrent queue
277 // dispatch_get_main_queue - default queue that is bound to the main thread
278 xpc_transaction_begin();
279 dispatch_async(_calloutQueue, ^{
280 __block NSMutableDictionary *myPending;
281 __block bool myHandlePendingMessage;
282 __block bool myDoSetDeviceID;
283 __block bool wasLocked;
284 dispatch_sync(idsproxy_queue, ^{
285 myPending = [_unhandledMessageBuffer copy];
286 myHandlePendingMessage = _handleAllPendingMessages;
287 myDoSetDeviceID = _setIDSDeviceID;
288 wasLocked = _isLocked;
292 secnotice("deaf", ">>>>>>>>>>> _oldInCallout is NO and we're heading in to the callout!");
294 _shadowHandleAllPendingMessages = NO;
297 callout(myPending, myHandlePendingMessage, myDoSetDeviceID, idsproxy_queue, ^(NSMutableDictionary *handledMessages, bool handledPendingMessage, bool handledSetDeviceID) {
298 secdebug("event", "%@ %s%s before callout handled: %s%s", self, myHandlePendingMessage ? "P" : "p", myDoSetDeviceID ? "D" : "d", handledPendingMessage ? "H" : "h", handledSetDeviceID ? "I" : "i");
300 // In IDSKeychainSyncingProxy's serial queue
306 // Update setting device id
307 _setIDSDeviceID = ((myDoSetDeviceID && !handledSetDeviceID) || _shadowHandleAllPendingMessages);
309 _shadowDoSetIDSDeviceID = NO;
311 if(_setIDSDeviceID && !_isLocked && _isSecDRunningAsRoot == false)
312 [self doSetIDSDeviceID:&error];
314 // Update handling pending messages
315 _handleAllPendingMessages = ((myHandlePendingMessage && (!handledPendingMessage)) || _shadowHandleAllPendingMessages);
317 _shadowHandleAllPendingMessages = NO;
319 if (handledPendingMessage)
320 _lastSyncTime = dispatch_time(DISPATCH_TIME_NOW, 0);
322 // Update pending messages and handle them
323 [handledMessages enumerateKeysAndObjectsUsingBlock: ^(id key, id obj, BOOL *stop){
324 NSString* fromID = (NSString*)key;
325 [_unhandledMessageBuffer removeObjectForKey:fromID];
328 // Write state to disk
331 if ([_unhandledMessageBuffer count] > 0 || (!_isLocked && wasLocked))
332 [self handleAllPendingMessage];
334 xpc_transaction_end();
339 - (BOOL) doSetIDSDeviceID: (NSError**)error
342 NSDictionary *userInfo;
344 NSString *errorMessage;
345 __block NSString* deviceID;
346 __block CFErrorRef localError = NULL;
347 __block bool handledSettingID = false;
350 [self doIDSInitialization];
352 if(_isSecDRunningAsRoot == true)
354 secerror("cannot set IDS device ID, secd is running as root");
357 require_action_quiet(_isIDSInitDone, fail, errorMessage = @"IDSKeychainSyncingProxy can't set up the IDS service"; code = kSecIDSErrorNotRegistered);
358 require_action_quiet(!_isLocked, fail, errorMessage = @"IDSKeychainSyncingProxy can't set device ID, device is locked"; code = kSecIDSErrorDeviceIsLocked);
360 deviceID = IDSCopyLocalDeviceUniqueID();
361 secdebug("IDS Transport", "This is our IDS device ID: %@", deviceID);
363 require_action_quiet(deviceID != nil, fail, errorMessage = @"IDSKeychainSyncingProxy could not retrieve device ID from keychain"; code = kSecIDSErrorNoDeviceID);
365 if(_inCallout && _isSecDRunningAsRoot == false){
366 _shadowDoSetIDSDeviceID = YES;
370 _setIDSDeviceID = YES;
371 [self calloutWith:^(NSMutableDictionary *pending, bool handlePendingMesssages, bool doSetDeviceID, dispatch_queue_t queue, void(^done)(NSMutableDictionary *, bool, bool)) {
372 handledSettingID = SOSCCSetDeviceID((__bridge CFStringRef) deviceID, &localError);
374 dispatch_async(queue, ^{
376 if(CFErrorGetCode(localError) == SECD_RUN_AS_ROOT_ERROR){
377 secerror("SETTING RUN AS ROOT ERROR");
378 _isSecDRunningAsRoot = true;
381 *error = (__bridge NSError *)(localError);
383 handledSettingID = YES;
384 done(nil, NO, handledSettingID);
387 result = handledSettingID;
392 userInfo = [ NSDictionary dictionaryWithObjectsAndKeys:errorMessage, NSLocalizedDescriptionKey, nil ];
394 *error = [NSError errorWithDomain:@"com.apple.security.ids.error" code:code userInfo:userInfo];
395 secerror("%@", *error);
400 -(BOOL) sendIDSMessage:(NSDictionary*)data name:(NSString*) deviceName peer:(NSString*) peerID error:(NSError**) error
403 NSDictionary *userInfo;
406 NSString *errorMessage;
407 NSString* identifier = [NSString string];
408 NSMutableSet *destinations = [NSMutableSet set];
409 NSArray *ListOfIDSDevices = nil;
410 IDSMessagePriority priority = IDSMessagePriorityHigh;
411 IDSDevice *device = nil;
412 BOOL encryptionOff = YES;
413 NSError *localError = nil;
414 NSDictionary *options = @{IDSSendMessageOptionForceEncryptionOffKey : [NSNumber numberWithBool:encryptionOff] };
416 require_action_quiet(_service, fail, errorMessage = @"Could not send message: IDS delegate uninitialized, can't use IDS to send this message"; code = kSecIDSErrorNotRegistered);
418 secdebug("IDS Transport", "[_service devices]: %@, we have their deviceName: %@", [_service devices], deviceName);
419 ListOfIDSDevices = [_service devices];
421 require_action_quiet([ListOfIDSDevices count]> 0, fail, errorMessage=@"Could not send message: IDS devices are not registered yet"; code = kSecIDSErrorNotRegistered);
422 secinfo("IDS Transport", "This is our list of devices: %@", ListOfIDSDevices);
424 for(NSUInteger i = 0; i < [ ListOfIDSDevices count ]; i++){
425 device = ListOfIDSDevices[i];
426 if( [ deviceName compare:device.uniqueID ] == 0){
427 [destinations addObject: IDSCopyIDForDevice(device)];
431 require_action_quiet([destinations count] != 0, fail, errorMessage = @"Could not send message: IDS device ID for peer does not match any devices within an IDS Account"; code = kSecIDSErrorCouldNotFindMatchingAuthToken);
433 result = [_service sendMessage:data toDestinations:destinations priority:priority options:options identifier:&identifier error:&localError ] ;
435 require_action_quiet(localError == nil, fail, errorMessage = @"Had an error sending IDS message"; code = kSecIDSErrorFailedToSend);
437 secdebug("IDS Transport", "IDSKeychainSyncingProxy sent this message over IDS: %@", data);
442 userInfo = [ NSDictionary dictionaryWithObjectsAndKeys:errorMessage, NSLocalizedDescriptionKey, nil ];
444 *error = [NSError errorWithDomain:@"com.apple.security.ids.error" code:code userInfo:userInfo];
445 secerror("%@", *error);
447 if(localError != nil)
448 secerror("%@", localError);
454 - (void) sendKeysCallout: (NSMutableDictionary*(^)(NSMutableDictionary* pending, NSError** error)) handleMessages {
455 [self calloutWith: ^(NSMutableDictionary *pending, bool handlePendingMesssages, bool doSetDeviceID, dispatch_queue_t queue, void(^done)(NSMutableDictionary *, bool, bool)) {
456 NSError* error = NULL;
458 NSMutableDictionary* handled = handleMessages(pending, &error);
460 dispatch_async(queue, ^{
461 if (!handled && error) {
462 secerror("%@ did not handle message: %@", self, error);
465 done(handled, NO, NO);
470 - (void) handleAllPendingMessage
472 if([_unhandledMessageBuffer count] > 0){
473 secinfo("IDS Transport", "handling Message: %@", _unhandledMessageBuffer);
474 [_unhandledMessageBuffer enumerateKeysAndObjectsUsingBlock: ^(id key, id obj, BOOL *stop)
476 NSDictionary *messageAndFromID = (NSDictionary*)obj;
477 NSString *fromID = (NSString*)key;
480 _shadowHandleAllPendingMessages = YES;
483 __block CFErrorRef cf_error = NULL;
484 __block HandleIDSMessageReason success = kHandleIDSMessageSuccess;
485 _handleAllPendingMessages = YES;
487 [self sendKeysCallout:^NSMutableDictionary *(NSMutableDictionary *pending, NSError** error) {
488 success = SOSCCHandleIDSMessage(((__bridge CFDictionaryRef)messageAndFromID), &cf_error);
490 if(success == kHandleIDSMessageLocked){
491 secdebug("IDS Transport", "cannot handle messages when locked, error:%@", cf_error);
492 [_unhandledMessageBuffer setObject: messageAndFromID forKey: fromID];
494 _lastSyncTime = dispatch_time(DISPATCH_TIME_NOW, 0);
495 _deadline = dispatch_time(DISPATCH_TIME_NOW, kAttemptFlushBufferInterval);
497 [self scheduleSyncRequestTimer];
500 else if(success == kHandleIDSMessageNotReady){
501 secdebug("IDS Transport", "not ready to handle message, error:%@", cf_error);
502 [_unhandledMessageBuffer setObject: messageAndFromID forKey: fromID];
503 _lastSyncTime = dispatch_time(DISPATCH_TIME_NOW, 0);
504 _deadline = dispatch_time(DISPATCH_TIME_NOW, kAttemptFlushBufferInterval);
506 [self scheduleSyncRequestTimer];
509 else if(success == kHandleIDSMessageOtherFail){
510 secdebug("IDS Transport", "not ready to handle message, error:%@", cf_error);
511 [_unhandledMessageBuffer setObject: messageAndFromID forKey: fromID];
512 _lastSyncTime = dispatch_time(DISPATCH_TIME_NOW, 0);
513 _deadline = dispatch_time(DISPATCH_TIME_NOW, kAttemptFlushBufferInterval);
515 [self scheduleSyncRequestTimer];
519 secdebug("IDS Transport", "IDSProxy handled this message! %@", messageAndFromID);
520 _syncTimerScheduled = NO;
521 return (NSMutableDictionary*)messageAndFromID;
529 - (void)service:(IDSService *)service account:(IDSAccount *)account incomingMessage:(NSDictionary *)message fromID:(NSString *)fromID context:(IDSMessageContext *)context;
531 secdebug("IDS Transport", "IDSKeychainSyncingProxy handling this message sent over IDS%@", message);
532 NSString *dataKey = [ NSString stringWithUTF8String: kMessageKeyIDSDataMessage ];
533 NSString *deviceIDKey = [ NSString stringWithUTF8String: kMessageKeyDeviceID ];
534 NSString *peerIDKey = [ NSString stringWithUTF8String: kMessageKeyPeerID ];
536 uint32_t operationType;
537 bool hadError = false;
538 CFStringRef errorMessage = NULL;
539 __block NSString* operation = nil;
540 __block NSString* myPeerID = @"";
541 NSString *messageString = nil;
542 __block NSData *messageData = nil;
543 __block NSString *messageAsString = nil;
544 __block BOOL operationIsString = false;
545 __block BOOL messageStringIsString = false;
546 __block BOOL messageDataIsData = false;
548 NSArray *devices = [_service devices];
549 for(NSUInteger i = 0; i < [ devices count ]; i++){
550 IDSDevice *device = devices[i];
551 if( [(IDSCopyIDForDevice(device)) containsString: fromID] == YES){
552 ID = device.uniqueID;
557 require_action_quiet(ID, fail, hadError = true; errorMessage = CFSTR("require the sender's device ID"));
558 require_action_quiet([message count] == 1, fail, hadError = true; errorMessage = CFSTR("message contained too many objects"));
560 [message enumerateKeysAndObjectsUsingBlock: ^(id key, id obj, BOOL *stop){
561 operation = (NSString*)key;
562 operationIsString = (CFGetTypeID((__bridge CFTypeRef)(operation)) == CFStringGetTypeID());
564 if(CFGetTypeID((__bridge CFTypeRef)(obj)) == CFDataGetTypeID()){
565 messageDataIsData = true;
566 messageData = (NSData*)obj;
568 else if(CFGetTypeID((__bridge CFTypeRef)(obj)) == CFStringGetTypeID()){
569 messageStringIsString = true;
570 messageAsString = (NSString*)obj;
574 require_action_quiet(operationIsString, fail, hadError = true; errorMessage = CFSTR("unexpected opeartion type"););
577 require_action_quiet(messageDataIsData, fail, hadError = true; errorMessage = CFSTR("unexpected message type"););
578 else if(messageAsString)
579 require_action_quiet(messageStringIsString, fail, hadError = true; errorMessage = CFSTR("unexpected message type"););
581 operationType = [operation intValue];
582 if(operationType == 0)
583 myPeerID = operation;
585 switch(operationType){
586 case kIDSPeerAvailabilityDone:
588 secdebug("ids transport", "received availability done!");
589 notify_post(kSOSCCPeerAvailable);
592 case kIDSEndPingTestMessage:
593 secdebug("ids transport", "received pong message from other device: %@, ping test PASSED", ID);
595 case kIDSSendOneMessage:
596 secdebug("ids transport","received ping test message, dropping on the floor now");
599 case kIDSPeerAvailability:
600 case kIDSStartPingTestMessage:
603 if(operationType == kIDSPeerAvailability){
604 secdebug("ids transport", "Received Availability Message!");
605 asprintf(&messageCharS, "%d",kIDSPeerAvailabilityDone);
608 secdebug("ids transport", "Received PingTest Message!");
609 asprintf(&messageCharS, "%d", kIDSEndPingTestMessage);
612 NSString *operationString = [[NSString alloc] initWithUTF8String:messageCharS];
613 messageString = @"peer availability check finished";
614 NSDictionary* messsageDictionary = @{operationString : messageString};
616 NSError *localError = NULL;
617 [self sendIDSMessage:messsageDictionary name:ID peer:@"me" error:&localError];
625 NSDictionary *messageAndFromID = @{dataKey : messageData, deviceIDKey: ID, peerIDKey: myPeerID};
627 //hang on to the message and set the retry deadline
628 [_unhandledMessageBuffer setObject: messageAndFromID forKey: fromID];
629 _deadline = dispatch_time(DISPATCH_TIME_NOW, kMaxMessageRetryDelay);
632 __block CFErrorRef cf_error = NULL;
633 __block HandleIDSMessageReason success = kHandleIDSMessageSuccess;
634 _handleAllPendingMessages = YES;
636 [self sendKeysCallout:^NSMutableDictionary *(NSMutableDictionary *pending, NSError** error) {
638 success = SOSCCHandleIDSMessage(((__bridge CFDictionaryRef)messageAndFromID), &cf_error);
640 if(success == kHandleIDSMessageLocked){
641 secdebug("IDS Transport", "cannot handle messages when locked, error:%@", cf_error);
642 [_unhandledMessageBuffer setObject: messageAndFromID forKey: fromID];
644 _lastSyncTime = dispatch_time(DISPATCH_TIME_NOW, 0);
645 _deadline = dispatch_time(DISPATCH_TIME_NOW, kAttemptFlushBufferInterval);
647 [self scheduleSyncRequestTimer];
650 else if(success == kHandleIDSMessageNotReady){
651 secdebug("IDS Transport", "not ready to handle message, error:%@", cf_error);
652 [_unhandledMessageBuffer setObject: messageAndFromID forKey: fromID];
653 _lastSyncTime = dispatch_time(DISPATCH_TIME_NOW, 0);
654 _deadline = dispatch_time(DISPATCH_TIME_NOW, kAttemptFlushBufferInterval);
656 [self scheduleSyncRequestTimer];
659 else if(success == kHandleIDSMessageOtherFail){
660 secdebug("IDS Transport", "not ready to handle message, error:%@", cf_error);
661 [_unhandledMessageBuffer setObject: messageAndFromID forKey: fromID];
662 _lastSyncTime = dispatch_time(DISPATCH_TIME_NOW, 0);
663 _deadline = dispatch_time(DISPATCH_TIME_NOW, kAttemptFlushBufferInterval);
665 [self scheduleSyncRequestTimer];
669 secdebug("IDS Transport", "IDSProxy handled this message! %@", messageAndFromID);
670 return (NSMutableDictionary*)messageAndFromID;
673 CFReleaseSafe(cf_error);
680 secerror("error:%@", errorMessage);