1/*
2 * Copyright (c) 2012 Apple Inc.
3 * All rights reserved.
4 */
5
6#include <SystemConfiguration/SystemConfiguration.h>
7#include <SystemConfiguration/SCPrivate.h>
8
9#include "scnc_main.h"
10#include "scnc_utils.h"
11#include "behaviors.h"
12
13#define kVPNBehaviorsPlistFile						CFSTR("com.apple.pppcontroller-vpn-behaviors.plist")
14#define kVPNBehaviorsVODAlwaysMeansOnRetry			CFSTR("VODAlwaysMeansOnRetry")
15
16#define kVPNBehaviorsVODAlwaysMeansOnRetryDefault	kCFBooleanTrue
17
18typedef struct behaviors_context {
19	SCPreferencesRef prefs;
20	BehaviorsUpdatedBlock cb_block;
21} BehaviorsContext;
22
23static void
24behaviors_prefs_changed(SCPreferencesRef prefs, SCPreferencesNotification notification_type, void *info)
25{
26#pragma unused(prefs)
27	if (notification_type == kSCPreferencesNotificationCommit) {
28		BehaviorsContext *context = (BehaviorsContext *)info;
29		context->cb_block();
30	}
31}
32
33void
34behaviors_modify_ondemand(CFMutableDictionaryRef trigger_dict, BehaviorsUpdatedBlock cb_block)
35{
36	CFBooleanRef always_means_on_retry;
37	static BehaviorsContext *context = NULL;
38	static dispatch_once_t predicate = 0;
39	__block Boolean success = TRUE;
40
41	dispatch_once(&predicate, ^{
42		SCPreferencesContext prefs_ctx;
43
44		context = (BehaviorsContext *)CFAllocatorAllocate(kCFAllocatorDefault, sizeof(*context), 0);
45
46		context->prefs = SCPreferencesCreate(kCFAllocatorDefault, CFSTR("PPPController"), kVPNBehaviorsPlistFile);
47		if (context->prefs == NULL) {
48			SCLog(TRUE, LOG_ERR, CFSTR("SCPreferencesCreate failed: %s"), SCErrorString(SCError()));
49			success = FALSE;
50			goto done;
51		}
52
53		memset(&prefs_ctx, 0, sizeof(prefs_ctx));
54		prefs_ctx.info = context;
55		if (!SCPreferencesSetCallback(context->prefs, behaviors_prefs_changed, &prefs_ctx)) {
56			SCLog(TRUE, LOG_ERR, CFSTR("SCPreferencesSetCallback failed: %s"), SCErrorString(SCError()));
57			success = FALSE;
58			goto done;
59		}
60
61		if (!SCPreferencesScheduleWithRunLoop(context->prefs, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) {
62			SCLog(TRUE, LOG_ERR, CFSTR("SCPreferencesSetDispatchQueue failed: %s"), SCErrorString(SCError()));
63			success = FALSE;
64			goto done;
65		}
66
67done:
68		if (!success) {
69		   	if (context->prefs != NULL) {
70				CFRelease(context->prefs);
71			}
72			CFAllocatorDeallocate(kCFAllocatorDefault, context);
73			context = NULL;
74		} else {
75			context->cb_block = Block_copy(cb_block);
76		}
77	});
78
79	if (!success) {
80		return;
81	}
82
83	always_means_on_retry = SCPreferencesGetValue(context->prefs, kVPNBehaviorsVODAlwaysMeansOnRetry);
84	if (!isA_CFBoolean(always_means_on_retry)) {
85		always_means_on_retry = kVPNBehaviorsVODAlwaysMeansOnRetryDefault;
86	}
87
88	if (CFBooleanGetValue(always_means_on_retry)) {
89		CFArrayRef always_domains = CFDictionaryGetValue(trigger_dict, kSCNetworkConnectionOnDemandMatchDomainsAlways);
90		if (isA_CFArray(always_domains)) {
91			CFMutableArrayRef new_on_retry_domains;
92			CFArrayRef on_retry_domains = CFDictionaryGetValue(trigger_dict, kSCNetworkConnectionOnDemandMatchDomainsOnRetry);
93
94			new_on_retry_domains = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, always_domains);
95
96			if (isA_CFArray(on_retry_domains)) {
97				CFIndex on_retry_count = CFArrayGetCount(on_retry_domains);
98				if (on_retry_count > 0) {
99					CFArrayAppendArray(new_on_retry_domains, on_retry_domains, CFRangeMake(0, on_retry_count));
100				}
101			}
102
103			CFDictionaryRemoveValue(trigger_dict, kSCNetworkConnectionOnDemandMatchDomainsAlways);
104			CFDictionarySetValue(trigger_dict, kSCNetworkConnectionOnDemandMatchDomainsOnRetry, new_on_retry_domains);
105
106			CFRelease(new_on_retry_domains);
107		}
108	}
109
110	SCPreferencesSynchronize(context->prefs);
111}
112