main.c   [plain text]


//
//  main.c
//  iCloudStats
//
//  Created by local on 7/20/13.
//
//

#include <CoreFoundation/CoreFoundation.h>

#import "SOSCloudCircle.h"
#import <Security/Security.h>

static const CFStringRef gMessageTracerPrefix = CFSTR("com.apple.message.");
static const CFStringRef gClientIsUsingiCloudKeychainSyncing = CFSTR("com.apple.icloudkeychain.deviceIsUsingICloudKeychain");
static const CFStringRef gNumberOfPeers = CFSTR("com.apple.icloudkeychain.numberOfPeers");
static const CFStringRef gNumberOfItemsBeingSynced = CFSTR("com.apple.icloudkeychain.numberOfItemsBeingSynced");
    
#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
static const char* gTopLevelKeyForiCloudKeychainTracing = "com.apple.icloudkeychain";
#endif
    
#if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
static const char* gTopLevelKeyForiCloudKeychainTracing = "com.apple.icloudkeychain";
#endif

#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR))
#include <asl.h>


static bool OSX_SetCloudKeychainTraceValueForKey(CFStringRef key, int64_t value)
{
	bool result = false;
	
	if (NULL == key)
	{
		return result;
	}
	
	aslmsg mAsl = NULL;
	mAsl = asl_new(ASL_TYPE_MSG);
	if (NULL == mAsl)
	{
		return result;
	}
    
    
    CFStringRef key_str = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@%@"), gMessageTracerPrefix, key);
    if (NULL == key_str)
    {
        asl_free(mAsl);
        return result;
    }
	
	CFStringRef value_str = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%lld"), value);
    if (NULL == value_str)
    {
        asl_free(mAsl);
        CFRelease(key_str);
        return result;
    }
    
    CFIndex key_str_numBytes = CFStringGetMaximumSizeForEncoding(CFStringGetLength(key_str), kCFStringEncodingUTF8);
    key_str_numBytes += 1; // For null
    char key_buffer[key_str_numBytes];
    memset(key_buffer, 0, key_str_numBytes);
    if (!CFStringGetCString(key_str, key_buffer, key_str_numBytes, kCFStringEncodingUTF8))
    {
        asl_free(mAsl);
        CFRelease(key_str);
        CFRelease(value_str);
        return result;
    }
    CFRelease(key_str);
    
    CFIndex value_str_numBytes = CFStringGetMaximumSizeForEncoding(CFStringGetLength(value_str), kCFStringEncodingUTF8);
    value_str_numBytes += 1; // For null
    char value_buffer[value_str_numBytes];
    memset(value_buffer, 0, value_str_numBytes);
    if (!CFStringGetCString(value_str, value_buffer, value_str_numBytes, kCFStringEncodingUTF8))
    {
        asl_free(mAsl);
        CFRelease(value_str);
        return result;
    }
    CFRelease(value_str);
	
	asl_set(mAsl, key_buffer, value_buffer);
	asl_log(NULL, mAsl, ASL_LEVEL_NOTICE, "%s", gTopLevelKeyForiCloudKeychainTracing);
	asl_free(mAsl);
	return true;
}
#endif

#if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
#import <AggregateDictionary/ADClient.h>

static bool iOS_SetCloudKeychainTraceValueForKey(CFStringRef key, int64_t value)
{
	if (NULL == key)
	{
		return false;
	}
	
    if (0LL == value)
    {
        ADClientClearScalarKey(key);
    }
    
	ADClientSetValueForScalarKey(key, value);
	return true;
}
#endif

static bool ClientIsInCircle()
{
    bool result = false;
    CFErrorRef error = NULL;
    SOSCCStatus status = kSOSCCError;
    
    status = SOSCCThisDeviceIsInCircle(&error);
    if (NULL != error)
    {
        CFRelease(error);
    }
    else
    {
        switch (status)
        {
            case kSOSCCInCircle:
            {
                result = true;
            }
				break;
				
				// kSOSCCRequestPending
				// While this device will be in a circle, it is not in
				// one yet. For now, this will be treated as if the device
				// was not in a circle and will wait for the device to
				// be in a circle and have that turn on this daemon with
				// launchctl
            case kSOSCCRequestPending:
            case kSOSCCCircleAbsent:
            case kSOSCCError:
            default:
                break;
        }
    }
    return result;
}


static bool sendTraceMessage(CFStringRef key, int64_t value)
{
	
#if (TARGET_IPHONE_SIMULATOR)
	return false;
#endif
    
#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
	return OSX_SetCloudKeychainTraceValueForKey(key, value);
#endif
    
#if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
	return iOS_SetCloudKeychainTraceValueForKey(key, value);
#endif
	
}

static int64_t GetNumberOfPeers()
{
	int64_t result = 0LL;
	
	CFErrorRef error = NULL;
	CFArrayRef peers = NULL;
	
	peers = SOSCCCopyPeerPeerInfo(&error);
	if (NULL != error)
	{
		CFRelease(error);
		if (NULL != peers)
		{
			CFRelease(peers);
		}
		return result;
	}
	
	if (NULL != peers)
	{
		result = (int64_t)CFArrayGetCount(peers);
		CFRelease(peers);
	}
	
	return result;
}

static int64_t GetNumberOfItemsBeingSyncedForType(CFTypeRef class)
{
    int64_t result = 0;
    
    CFTypeRef keys[] = {kSecClass, kSecAttrSynchronizable, kSecMatchLimit, kSecReturnAttributes};
    CFTypeRef values[] = {class, kCFBooleanTrue, kSecMatchLimitAll, kCFBooleanTrue};
    
    CFDictionaryRef query = CFDictionaryCreate(kCFAllocatorDefault, (const void **)keys, (const void **)values, (sizeof(keys)/sizeof(keys[0])), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    
    if (NULL == query)
    {
        return result;
    }
    
    CFArrayRef query_result = NULL;
	OSStatus status =  SecItemCopyMatching(query, (CFTypeRef *)&query_result);
    CFRelease(query);
    if (noErr != status || NULL == query_result)
	{
		if (NULL != query_result)
		{
			CFRelease(query_result);
		}
		return result;
	}
    
    result = (int64_t)CFArrayGetCount(query_result);
    CFRelease(query_result);
    return result;
}

static int64_t GetNumberOfItemsBeingSynced()
{
	int64_t result = 0;
	
    result = GetNumberOfItemsBeingSyncedForType(kSecClassInternetPassword);
    result += GetNumberOfItemsBeingSyncedForType(kSecClassGenericPassword);
	
	return result;
}


int main(int argc, const char * argv[])
{
    int64_t value =  0;
    if (!ClientIsInCircle())
    {
        // This will clear the value in the backend database if it had been previously set
        sendTraceMessage(gClientIsUsingiCloudKeychainSyncing, value);
        return 0;
    }
    
    value = 1;
    sendTraceMessage(gClientIsUsingiCloudKeychainSyncing, value);
    
    value = GetNumberOfPeers();
    sendTraceMessage(gNumberOfPeers, value);
    
    value = GetNumberOfItemsBeingSynced();
    sendTraceMessage(gNumberOfItemsBeingSynced, value);
    
    return 0;
}