--- /dev/null
+
+#include "SOSAccountPriv.h"
+#include "SOSAccount.h"
+
+#include "keychain/SecureObjectSync/SOSKVSKeys.h"
+#include "keychain/SecureObjectSync/SOSTransportCircle.h"
+#include "keychain/SecureObjectSync/SOSTransportCircleKVS.h"
+#include "keychain/SecureObjectSync/SOSTransportMessage.h"
+#include "keychain/SecureObjectSync/SOSKVSKeys.h"
+#include "keychain/SecureObjectSync/SOSTransport.h"
+#include "keychain/SecureObjectSync/SOSTransportKeyParameter.h"
+#include "keychain/SecureObjectSync/CKBridge/SOSCloudKeychainClient.h"
+
+#import "keychain/SecureObjectSync/SOSAccountTrust.h"
+#import "keychain/SecureObjectSync/SOSTransport.h"
+#import "keychain/SecureObjectSync/SOSTransportKeyParameter.h"
+#include "keychain/SecureObjectSync/SOSTransportMessage.h"
+#import "keychain/SecureObjectSync/SOSTransportMessageKVS.h"
+#include "keychain/SecureObjectSync/CKBridge/SOSCloudKeychainClient.h"
+#include "keychain/SecureObjectSync/SOSAccountTrustClassic+Circle.h"
+#include "keychain/SecureObjectSync/SOSInternal.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include <utilities/SecCFError.h>
+
+// MARK: Engine Logging
+#define LOG_ENGINE_STATE_INTERVAL 20
+#define LOG_CATEGORY "account-sync"
+
+static bool SOSReadyToSync(SOSAccount *account, CFErrorRef *error) {
+ if (![account isInCircle:error]) {
+ secnotice(LOG_CATEGORY, "Not performing requested sync operation: not in circle yet");
+ return false;
+ }
+ return true;
+}
+
+void SOSAccountConsiderLoggingEngineState(SOSAccountTransaction* txn) {
+ static int engineLogCountDown = 0;
+
+ if(engineLogCountDown <= 0) {
+ SOSAccount* acct = txn.account;
+ if(SOSReadyToSync(acct, NULL)) {
+ CFTypeRef engine = [acct.kvs_message_transport SOSTransportMessageGetEngine];
+ SOSEngineLogState((SOSEngineRef)engine);
+ engineLogCountDown = LOG_ENGINE_STATE_INTERVAL;
+ }
+ } else {
+ engineLogCountDown--;
+ }
+}
+
+bool SOSAccountInflateTransports(SOSAccount* account, CFStringRef circleName, CFErrorRef *error){
+ bool success = false;
+
+ if(account.key_transport)
+ SOSUnregisterTransportKeyParameter(account.key_transport);
+ if(account.circle_transport)
+ SOSUnregisterTransportCircle(account.circle_transport);
+ if(account.kvs_message_transport)
+ SOSUnregisterTransportMessage((SOSMessage*)account.kvs_message_transport);
+
+ account.key_transport = [[CKKeyParameter alloc] initWithAccount:account];
+ account.circle_transport = [[SOSKVSCircleStorageTransport alloc]initWithAccount:account andCircleName:(__bridge NSString *)(circleName)];
+
+ require_quiet(account.key_transport, fail);
+ require_quiet(account.circle_transport, fail);
+
+ account.kvs_message_transport = [[SOSMessageKVS alloc] initWithAccount:account andName:(__bridge NSString*)circleName];
+ require_quiet(account.kvs_message_transport, fail);
+
+ success = true;
+
+fail:
+ return success;
+}
+
+//
+// MARK: KVS Syncing
+//
+
+static bool SOSAccountSyncWithKVSPeers(SOSAccountTransaction* txn, CFSetRef peerIDs, CFErrorRef *error) {
+ SOSAccount* account = txn.account;
+ CFErrorRef localError = NULL;
+ bool result = false;
+
+ if(SOSReadyToSync(account, error)) {
+ result =[account.kvs_message_transport SOSTransportMessageSyncWithPeers:account.kvs_message_transport p:peerIDs err:&localError];
+ }
+ if (!result) {
+ // Tell account to update SOSEngine with current trusted peers
+ if (isSOSErrorCoded(localError, kSOSErrorPeerNotFound)) {
+ secnotice(LOG_CATEGORY, "Arming account to update SOSEngine with current trusted peers");
+ account.engine_peer_state_needs_repair = true;
+ }
+ CFErrorPropagate(localError, error);
+ localError = NULL;
+ }
+ return result;
+
+}
+
+bool SOSAccountSyncWithKVSPeerWithMessage(SOSAccountTransaction* txn, CFStringRef peerid, CFDataRef message, CFErrorRef *error) {
+ //murfxx protect
+ SOSAccount* account = txn.account;
+ bool result = false;
+ CFErrorRef localError = NULL;
+ CFDictionaryRef encapsulatedMessage = NULL;
+
+ secnotice(LOG_CATEGORY,"Syncing with KVS capable peer: %@", peerid);
+ secnotice(LOG_CATEGORY, "message: %@", message);
+
+ require_quiet(message, xit);
+ require_quiet(peerid && CFStringGetLength(peerid) <= kSOSPeerIDLengthMax, xit);
+ if(SOSReadyToSync(account, &localError)) {
+ encapsulatedMessage = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, peerid, message, NULL);
+ result = [account.kvs_message_transport SOSTransportMessageSendMessages:account.kvs_message_transport pm:encapsulatedMessage err:&localError];
+ secnotice(LOG_CATEGORY, "KVS sync %s. (%@)", result ? "succeeded" : "failed", localError);
+ SOSAccountConsiderLoggingEngineState(txn);
+ }
+
+xit:
+ CFReleaseNull(encapsulatedMessage);
+ CFErrorPropagate(localError, error);
+
+ return result;
+}
+
+
+static bool SOSAccountSyncWithKVSPeer(SOSAccountTransaction* txn, CFStringRef peerID, CFErrorRef *error)
+{
+ bool result = false;
+ CFErrorRef localError = NULL;
+ SOSAccount* account = txn.account;
+
+ if(SOSReadyToSync(account, error)) {
+ secnotice(LOG_CATEGORY,"Syncing with KVS capable peer: %@", peerID);
+ CFMutableSetRef peerIDs = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
+ CFSetAddValue(peerIDs, peerID);
+ result = SOSAccountSyncWithKVSPeers(txn, peerIDs, &localError);
+ secnotice(LOG_CATEGORY, "KVS sync %s. (%@)", result ? "succeeded" : "failed", localError);
+
+ CFReleaseNull(peerIDs);
+ CFErrorPropagate(localError, error);
+ }
+
+ return result;
+}
+
+
+CFSetRef SOSAccountSyncWithPeersOverKVS(SOSAccountTransaction* txn, CFSetRef peers) {
+ CFMutableSetRef handled = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
+ if(SOSReadyToSync(txn.account, NULL)) {
+ CFSetForEach(peers, ^(const void *value) {
+ CFStringRef peerID = asString(value, NULL);
+ CFErrorRef localError = NULL;
+ if (peerID && SOSAccountSyncWithKVSPeer(txn, peerID, &localError)) {
+ CFSetAddValue(handled, peerID);
+ secnotice(LOG_CATEGORY, "synced with peer: %@", peerID);
+ } else {
+ secnotice(LOG_CATEGORY, "failed to sync with peer: %@ error: %@", peerID, localError);
+ }
+ CFReleaseNull(localError);
+ });
+ }
+
+ return handled;
+}
+
+CF_RETURNS_RETAINED CFMutableSetRef SOSAccountSyncWithPeers(SOSAccountTransaction* txn, CFSetRef /* CFStringRef */ peerIDs, CFErrorRef *error) {
+ CFMutableSetRef notMePeers = NULL;
+ CFMutableSetRef handledPeerIDs = NULL;
+ CFMutableSetRef peersForKVS = NULL;
+
+ SOSAccount* account = txn.account;
+ if(!SOSReadyToSync(account, error)) {
+ handledPeerIDs = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, peerIDs);
+ CFReleaseNull(notMePeers);
+ CFReleaseNull(peersForKVS);
+ return handledPeerIDs;
+ }
+
+ handledPeerIDs = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
+ peersForKVS = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
+
+ SOSPeerInfoRef myPeerInfo = account.peerInfo;
+ if(!myPeerInfo)
+ {
+ CFReleaseNull(notMePeers);
+ CFReleaseNull(peersForKVS);
+ return handledPeerIDs;
+
+ }
+ CFStringRef myPeerID = SOSPeerInfoGetPeerID(myPeerInfo);
+
+ notMePeers = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, peerIDs);
+ CFSetRemoveValue(notMePeers, myPeerID);
+
+ CFSetForEach(notMePeers, ^(const void *value) {
+ CFErrorRef localError = NULL;
+ CFStringRef peerID = asString(value, &localError);
+ SOSPeerInfoRef peerInfo = NULL;
+ SOSCircleRef circle = NULL;
+ SOSAccountTrustClassic *trust = account.trust;
+ circle = trust.trustedCircle;
+ require_quiet(peerID, skip);
+
+ peerInfo = SOSCircleCopyPeerWithID(circle, peerID, NULL);
+ if (peerInfo && SOSCircleHasValidSyncingPeer(circle, peerInfo, account.accountKey, NULL)){
+ CFSetAddValue(peersForKVS, peerID);
+ } else {
+ CFSetAddValue(handledPeerIDs, peerID);
+ }
+
+ skip:
+ CFReleaseNull(peerInfo);
+ if (localError) {
+ secnotice(LOG_CATEGORY, "Skipped peer ID: %@ due to %@", peerID, localError);
+ }
+ CFReleaseNull(localError);
+ });
+
+ CFSetRef handledKVSPeerIDs = SOSAccountSyncWithPeersOverKVS(txn, peersForKVS);
+ CFSetUnion(handledPeerIDs, handledKVSPeerIDs);
+ CFReleaseNull(handledKVSPeerIDs);
+
+ SOSAccountConsiderLoggingEngineState(txn);
+
+ CFReleaseNull(notMePeers);
+ CFReleaseNull(peersForKVS);
+ return handledPeerIDs;
+}
+
+CF_RETURNS_RETAINED CFSetRef SOSAccountProcessSyncWithPeers(SOSAccountTransaction* txn, CFSetRef /* CFStringRef */ peers, CFSetRef /* CFStringRef */ backupPeers, CFErrorRef *error)
+{
+ CFErrorRef localError = NULL;
+ SOSAccount* account = txn.account;
+
+ if(!SOSReadyToSync(account, error)) {
+ return CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
+ }
+
+ CFMutableSetRef handled = SOSAccountSyncWithPeers(txn, peers, &localError);
+
+ if (!handled) {
+ secnotice(LOG_CATEGORY, "Peer Sync failed: %@", localError);
+ handled = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
+ }
+ CFReleaseNull(localError);
+
+ CFTypeRef engine = [account.kvs_message_transport SOSTransportMessageGetEngine];
+ CFSetRef engineHandled = SOSEngineSyncWithBackupPeers((SOSEngineRef)engine, backupPeers, false, error);
+
+ if (engineHandled) {
+ CFSetUnion(handled, engineHandled);
+ } else {
+ secnotice(LOG_CATEGORY, "Engine Backup Sync failed: %@", localError);
+ }
+ CFReleaseNull(localError);
+ CFReleaseNull(engineHandled);
+
+ return handled;
+}
+
+CF_RETURNS_RETAINED CFSetRef SOSAccountCopyBackupPeersAndForceSync(SOSAccountTransaction* txn, CFErrorRef *error)
+{
+ SOSAccount* account = txn.account;
+
+ if(!SOSReadyToSync(account, error)) {
+ return false;
+ }
+
+ SOSEngineRef engine = (SOSEngineRef) [txn.account.kvs_message_transport SOSTransportMessageGetEngine];
+ NSArray* backupPeersArray = (NSArray*) CFBridgingRelease(SOSEngineCopyBackupPeerNames(engine, error));
+ NSSet* backupPeers = [[NSSet alloc] initWithArray: backupPeersArray];
+ return SOSEngineSyncWithBackupPeers(engine, (__bridge CFSetRef) backupPeers, true, error);
+}
+
+bool SOSAccountRequestSyncWithAllPeers(SOSAccountTransaction* txn, CFErrorRef *error)
+{
+ SOSAccount* account = txn.account;
+ SOSAccountTrustClassic *trust = account.trust;
+
+ if(!SOSReadyToSync(account, error)) {
+ return false;
+ }
+
+ NSMutableSet<NSString*>* allSyncingPeers = [NSMutableSet set];
+ SOSCircleRef circle = trust.trustedCircle;
+
+ SOSCircleForEachValidSyncingPeer(circle, account.accountKey, ^(SOSPeerInfoRef peer) {
+ [allSyncingPeers addObject: (__bridge NSString*) SOSPeerInfoGetPeerID(peer)];
+ });
+
+ [txn requestSyncWithPeers:allSyncingPeers];
+
+ return true;
+}
+
+//
+// MARK: Syncing status functions
+//
+bool SOSAccountMessageFromPeerIsPending(SOSAccountTransaction* txn, SOSPeerInfoRef peer, CFErrorRef *error) {
+ bool success = false;
+ SOSAccount* account = txn.account;
+ if(SOSReadyToSync(account, error)) {
+ // This translation belongs inside KVS..way down in CKD, but for now we reach over and do it here.
+ CFStringRef peerMessage = SOSMessageKeyCreateFromPeerToTransport([account kvs_message_transport], (__bridge CFStringRef)(account.peerID), SOSPeerInfoGetPeerID(peer));
+ success = SOSCloudKeychainHasPendingKey(peerMessage, error);
+ CFReleaseNull(peerMessage);
+ }
+ return success;
+}
+
+bool SOSAccountSendToPeerIsPending(SOSAccountTransaction* txn, SOSPeerInfoRef peer, CFErrorRef *error) {
+ bool success = false;
+ SOSAccount* account = txn.account;
+ if(SOSReadyToSync(account, error)) {
+ success = SOSCCIsSyncPendingFor(SOSPeerInfoGetPeerID(peer), error);
+ }
+ return success;
+}
+