1//
2//  main.c
3//  iCloudStats
4//
5//  Created by local on 7/20/13.
6//
7//
8
9#include <CoreFoundation/CoreFoundation.h>
10
11#import "SOSCloudCircle.h"
12#import <Security/Security.h>
13
14static const CFStringRef gMessageTracerPrefix = CFSTR("com.apple.message.");
15static const CFStringRef gClientIsUsingiCloudKeychainSyncing = CFSTR("com.apple.icloudkeychain.deviceIsUsingICloudKeychain");
16static const CFStringRef gNumberOfPeers = CFSTR("com.apple.icloudkeychain.numberOfPeers");
17static const CFStringRef gNumberOfItemsBeingSynced = CFSTR("com.apple.icloudkeychain.numberOfItemsBeingSynced");
18
19#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
20static const char* gTopLevelKeyForiCloudKeychainTracing = "com.apple.icloudkeychain";
21#endif
22
23#if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
24static const char* gTopLevelKeyForiCloudKeychainTracing = "com.apple.icloudkeychain";
25#endif
26
27#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR))
28#include <asl.h>
29
30
31static bool OSX_SetCloudKeychainTraceValueForKey(CFStringRef key, int64_t value)
32{
33	bool result = false;
34
35	if (NULL == key)
36	{
37		return result;
38	}
39
40	aslmsg mAsl = NULL;
41	mAsl = asl_new(ASL_TYPE_MSG);
42	if (NULL == mAsl)
43	{
44		return result;
45	}
46
47
48    CFStringRef key_str = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@%@"), gMessageTracerPrefix, key);
49    if (NULL == key_str)
50    {
51        asl_free(mAsl);
52        return result;
53    }
54
55	CFStringRef value_str = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%lld"), value);
56    if (NULL == value_str)
57    {
58        asl_free(mAsl);
59        CFRelease(key_str);
60        return result;
61    }
62
63    CFIndex key_str_numBytes = CFStringGetMaximumSizeForEncoding(CFStringGetLength(key_str), kCFStringEncodingUTF8);
64    key_str_numBytes += 1; // For null
65    char key_buffer[key_str_numBytes];
66    memset(key_buffer, 0, key_str_numBytes);
67    if (!CFStringGetCString(key_str, key_buffer, key_str_numBytes, kCFStringEncodingUTF8))
68    {
69        asl_free(mAsl);
70        CFRelease(key_str);
71        CFRelease(value_str);
72        return result;
73    }
74    CFRelease(key_str);
75
76    CFIndex value_str_numBytes = CFStringGetMaximumSizeForEncoding(CFStringGetLength(value_str), kCFStringEncodingUTF8);
77    value_str_numBytes += 1; // For null
78    char value_buffer[value_str_numBytes];
79    memset(value_buffer, 0, value_str_numBytes);
80    if (!CFStringGetCString(value_str, value_buffer, value_str_numBytes, kCFStringEncodingUTF8))
81    {
82        asl_free(mAsl);
83        CFRelease(value_str);
84        return result;
85    }
86    CFRelease(value_str);
87
88	asl_set(mAsl, key_buffer, value_buffer);
89	asl_log(NULL, mAsl, ASL_LEVEL_NOTICE, "%s", gTopLevelKeyForiCloudKeychainTracing);
90	asl_free(mAsl);
91	return true;
92}
93#endif
94
95#if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
96#import <AggregateDictionary/ADClient.h>
97
98static bool iOS_SetCloudKeychainTraceValueForKey(CFStringRef key, int64_t value)
99{
100	if (NULL == key)
101	{
102		return false;
103	}
104
105    if (0LL == value)
106    {
107        ADClientClearScalarKey(key);
108    }
109
110	ADClientSetValueForScalarKey(key, value);
111	return true;
112}
113#endif
114
115static bool ClientIsInCircle()
116{
117    bool result = false;
118    CFErrorRef error = NULL;
119    SOSCCStatus status = kSOSCCError;
120
121    status = SOSCCThisDeviceIsInCircle(&error);
122    if (NULL != error)
123    {
124        CFRelease(error);
125    }
126    else
127    {
128        switch (status)
129        {
130            case kSOSCCInCircle:
131            {
132                result = true;
133            }
134				break;
135
136				// kSOSCCRequestPending
137				// While this device will be in a circle, it is not in
138				// one yet. For now, this will be treated as if the device
139				// was not in a circle and will wait for the device to
140				// be in a circle and have that turn on this daemon with
141				// launchctl
142            case kSOSCCRequestPending:
143            case kSOSCCCircleAbsent:
144            case kSOSCCError:
145            default:
146                break;
147        }
148    }
149    return result;
150}
151
152
153static bool sendTraceMessage(CFStringRef key, int64_t value)
154{
155
156#if (TARGET_IPHONE_SIMULATOR)
157	return false;
158#endif
159
160#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
161	return OSX_SetCloudKeychainTraceValueForKey(key, value);
162#endif
163
164#if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
165	return iOS_SetCloudKeychainTraceValueForKey(key, value);
166#endif
167
168}
169
170static int64_t GetNumberOfPeers()
171{
172	int64_t result = 0LL;
173
174	CFErrorRef error = NULL;
175	CFArrayRef peers = NULL;
176
177	peers = SOSCCCopyPeerPeerInfo(&error);
178	if (NULL != error)
179	{
180		CFRelease(error);
181		if (NULL != peers)
182		{
183			CFRelease(peers);
184		}
185		return result;
186	}
187
188	if (NULL != peers)
189	{
190		result = (int64_t)CFArrayGetCount(peers);
191		CFRelease(peers);
192	}
193
194	return result;
195}
196
197static int64_t GetNumberOfItemsBeingSyncedForType(CFTypeRef class)
198{
199    int64_t result = 0;
200
201    CFTypeRef keys[] = {kSecClass, kSecAttrSynchronizable, kSecMatchLimit, kSecReturnAttributes};
202    CFTypeRef values[] = {class, kCFBooleanTrue, kSecMatchLimitAll, kCFBooleanTrue};
203
204    CFDictionaryRef query = CFDictionaryCreate(kCFAllocatorDefault, (const void **)keys, (const void **)values, (sizeof(keys)/sizeof(keys[0])), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
205
206    if (NULL == query)
207    {
208        return result;
209    }
210
211    CFArrayRef query_result = NULL;
212	OSStatus status =  SecItemCopyMatching(query, (CFTypeRef *)&query_result);
213    CFRelease(query);
214    if (noErr != status || NULL == query_result)
215	{
216		if (NULL != query_result)
217		{
218			CFRelease(query_result);
219		}
220		return result;
221	}
222
223    result = (int64_t)CFArrayGetCount(query_result);
224    CFRelease(query_result);
225    return result;
226}
227
228static int64_t GetNumberOfItemsBeingSynced()
229{
230	int64_t result = 0;
231
232    result = GetNumberOfItemsBeingSyncedForType(kSecClassInternetPassword);
233    result += GetNumberOfItemsBeingSyncedForType(kSecClassGenericPassword);
234
235	return result;
236}
237
238
239int main(int argc, const char * argv[])
240{
241    int64_t value =  0;
242    if (!ClientIsInCircle())
243    {
244        // This will clear the value in the backend database if it had been previously set
245        sendTraceMessage(gClientIsUsingiCloudKeychainSyncing, value);
246        return 0;
247    }
248
249    value = 1;
250    sendTraceMessage(gClientIsUsingiCloudKeychainSyncing, value);
251
252    value = GetNumberOfPeers();
253    sendTraceMessage(gNumberOfPeers, value);
254
255    value = GetNumberOfItemsBeingSynced();
256    sendTraceMessage(gNumberOfItemsBeingSynced, value);
257
258    return 0;
259}
260
261