#include <CoreFoundation/CFUserNotification.h>
#include <CoreFoundation/CFString.h>
#include <CoreFoundation/CFRunLoop.h>
#include <SystemConfiguration/SCDPlugin.h>
#include <SystemConfiguration/SCValidation.h>
#include <Security/SecCertificateOIDs.h>
#include "EAPCertificateUtil.h"
#include <sys/types.h>
#include <pwd.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <paths.h>
#include <pthread.h>
#include <sys/queue.h>
#include "Dialogue.h"
#include "mylog.h"
#include "myCFUtil.h"
const CFStringRef kCredentialsDialogueSSID = CFSTR("SSID");
const CFStringRef kCredentialsDialogueAccountName = CFSTR("AccountName");
const CFStringRef kCredentialsDialoguePassword = CFSTR("Password");
const CFStringRef kCredentialsDialogueCertificates = CFSTR("Certificates");
const CFStringRef kCredentialsDialogueRememberInformation = CFSTR("RememberInformation");
struct CredentialsDialogue_s;
#define LIST_HEAD_CredentialsDialogueHead LIST_HEAD(CredentialsDialogueHead, CredentialsDialogue_s)
static LIST_HEAD_CredentialsDialogueHead S_CredentialsDialogueHead;
static struct CredentialsDialogueHead * S_CredentialsDialogueHead_p = &S_CredentialsDialogueHead;
#define LIST_ENTRY_CredentialsDialogue LIST_ENTRY(CredentialsDialogue_s)
struct CredentialsDialogue_s {
LIST_ENTRY_CredentialsDialogue entries;
CFUserNotificationRef notif;
CFRunLoopSourceRef rls;
CredentialsDialogueResponseCallBack func;
const void * arg1;
const void * arg2;
Boolean name_enabled;
Boolean password_enabled;
Boolean checkbox_enabled;
CFArrayRef certificates;
};
#define kEAPOLControllerPath "/System/Library/SystemConfiguration/EAPOLController.bundle"
static CFBundleRef
get_bundle(void)
{
static CFBundleRef bundle = NULL;
CFURLRef url;
if (bundle != NULL) {
return (bundle);
}
url = CFURLCreateWithFileSystemPath(NULL,
CFSTR(kEAPOLControllerPath),
kCFURLPOSIXPathStyle, FALSE);
if (url != NULL) {
bundle = CFBundleCreate(NULL, url);
CFRelease(url);
}
return (bundle);
}
static CFStringRef
copy_localized_string(CFBundleRef bundle, CFStringRef ethernet_str,
CFStringRef airport_str, CFTypeRef ssid)
{
CFStringRef str = NULL;
if (ssid != NULL) {
CFStringRef format;
format = CFBundleCopyLocalizedString(bundle, airport_str, airport_str,
NULL);
if (format != NULL) {
str = CFStringCreateWithFormat(NULL, NULL, format, ssid);
CFRelease(format);
}
}
else {
str = CFBundleCopyLocalizedString(bundle, ethernet_str, ethernet_str,
NULL);
}
if (str == NULL) {
str = CFRetain(ethernet_str);
}
return (str);
}
static CFStringRef
copy_localized_title(CFBundleRef bundle, CFTypeRef ssid)
{
#define kAirPort8021XTitleFormat CFSTR("Authenticating to network \"%@\"")
#define kEthernet8021XTitle CFSTR("Authenticating to 802.1X network")
return (copy_localized_string(bundle,
kEthernet8021XTitle,
kAirPort8021XTitleFormat,
ssid));
}
static CFArrayRef
copy_certificate_labels(CredentialsDialogueRef dialogue_p,
CFArrayRef certs, CFStringRef cert_label)
{
CFMutableArrayRef array = NULL;
CFMutableArrayRef certs_filtered = NULL;
int count;
int i;
CFRange r;
count = CFArrayGetCount(certs);
array = CFArrayCreateMutable(NULL, count + 1, &kCFTypeArrayCallBacks);
CFArrayAppendValue(array, cert_label);
r.location = 0;
r.length = 1;
certs_filtered = CFArrayCreateMutable(NULL, count, &kCFTypeArrayCallBacks);
for (i = 0; i < count; i++) {
SecCertificateRef cert = NULL;
SecIdentityRef identity;
CFStringRef str = NULL;
identity = (SecIdentityRef)CFArrayGetValueAtIndex(certs, i);
SecIdentityCopyCertificate(identity, &cert);
if (cert != NULL) {
str = SecCertificateCopyShortDescription(NULL, cert, NULL);
CFRelease(cert);
}
if (str != NULL) {
int instance;
CFStringRef new_str;
for (instance = 2, new_str = CFRetain(str);
CFArrayContainsValue(array, r, new_str); instance++) {
CFRelease(new_str);
new_str
= CFStringCreateWithFormat(NULL, NULL,
CFSTR("%@ (%d)"), str,
instance);
}
CFArrayAppendValue(array, new_str);
r.length++;
CFRelease(new_str);
CFRelease(str);
CFArrayAppendValue(certs_filtered, identity);
}
}
dialogue_p->certificates = certs_filtered;
return (array);
}
static CredentialsDialogueRef
CredentialsDialogue_find(CFUserNotificationRef notif)
{
CredentialsDialogueRef scan;
LIST_FOREACH(scan, S_CredentialsDialogueHead_p, entries) {
if (scan->notif == notif)
return (scan);
}
return (NULL);
}
static __inline__ CFOptionFlags
S_CFUserNotificationResponse(CFOptionFlags flags)
{
return (flags & 0x3);
}
static void
CredentialsDialogue_response(CFUserNotificationRef notif,
CFOptionFlags response_flags)
{
int count;
volatile CredentialsDialogueRef dialogue_p;
int i;
CredentialsDialogueResponse response;
CFStringRef str;
dialogue_p = CredentialsDialogue_find(notif);
if (dialogue_p == NULL) {
return;
}
bzero(&response, sizeof(response));
switch (S_CFUserNotificationResponse(response_flags)) {
case kCFUserNotificationDefaultResponse:
count = 0;
if (dialogue_p->name_enabled) {
count++;
}
if (dialogue_p->password_enabled) {
count++;
}
for (i = 0; i < count; i++) {
str = CFUserNotificationGetResponseValue(notif,
kCFUserNotificationTextFieldValuesKey,
i);
if (str == NULL || CFStringGetLength(str) == 0) {
continue;
}
if (i == 0 && dialogue_p->name_enabled) {
response.username = CFRetain(str);
}
else {
response.password = CFRetain(str);
}
}
if (dialogue_p->checkbox_enabled) {
if (response_flags & CFUserNotificationCheckBoxChecked(0)) {
response.remember_information = TRUE;
}
}
if (dialogue_p->certificates != NULL) {
int which_cert;
which_cert = (response_flags
& CFUserNotificationPopUpSelection(-1)) >> 24;
if (which_cert > 0) {
response.chosen_identity = (SecIdentityRef)
CFArrayGetValueAtIndex(dialogue_p->certificates,
which_cert - 1);
CFRetain(response.chosen_identity);
}
else if (dialogue_p->password_enabled == FALSE) {
my_CFRelease(&response.username);
}
}
break;
default:
response.user_cancelled = TRUE;
break;
}
if (dialogue_p->rls != NULL) {
CFRunLoopSourceInvalidate(dialogue_p->rls);
my_CFRelease(&dialogue_p->rls);
}
my_CFRelease(&dialogue_p->notif);
my_CFRelease(&dialogue_p->certificates);
(*dialogue_p->func)(dialogue_p->arg1, dialogue_p->arg2, &response);
my_CFRelease(&response.username);
my_CFRelease(&response.password);
my_CFRelease(&response.chosen_identity);
return;
}
#define kNetworkPrefPanePath "/System/Library/PreferencePanes/Network.prefPane"
static CFURLRef
copy_icon_url(CFStringRef icon)
{
CFBundleRef np_bundle;
CFURLRef np_url;
CFURLRef url = NULL;
np_url = CFURLCreateWithFileSystemPath(NULL,
CFSTR(kNetworkPrefPanePath),
kCFURLPOSIXPathStyle, FALSE);
if (np_url != NULL) {
np_bundle = CFBundleCreate(NULL, np_url);
if (np_bundle != NULL) {
url = CFBundleCopyResourceURL(np_bundle, icon,
CFSTR("icns"), NULL);
if (url == NULL) {
url = CFBundleCopyResourceURL(np_bundle, icon,
CFSTR("tiff"), NULL);
}
CFRelease(np_bundle);
}
CFRelease(np_url);
}
return (url);
}
#define kTitleAirPortCertificate CFSTR("TitleAirPortCertificate")
#define kTitleAirPortCertificateAndPassword CFSTR("TitleAirPortCertificateAndPassword")
#define kTitleAirPortPassword CFSTR("TitleAirPortPassword")
#define kTitleEthernetCertificate CFSTR("TitleEthernetCertificate")
#define kTitleEthernetCertificateAndPassword CFSTR("TitleEthernetCertificateAndPassword")
#define kTitleEthernetPassword CFSTR("TitleEthernetPassword")
static CFUserNotificationRef
CredentialsDialogueShow(CredentialsDialogueRef dialogue_p,
CFBundleRef bundle,
CFDictionaryRef details)
{
CFMutableArrayRef array = NULL;
CFArrayRef certs;
CFMutableDictionaryRef dict = NULL;
SInt32 error = 0;
CFOptionFlags flags = 0;
CFStringRef icon;
CFStringRef name;
CFUserNotificationRef notif = NULL;
CFStringRef password;
CFIndex password_index = kCFNotFound;
CFBooleanRef remember_information;
CFTypeRef ssid;
CFStringRef title;
CFURLRef url;
dict = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
url = CFBundleCopyBundleURL(bundle);
if (url != NULL) {
CFDictionarySetValue(dict, kCFUserNotificationLocalizationURLKey,
url);
CFRelease(url);
}
ssid = CFDictionaryGetValue(details, kCredentialsDialogueSSID);
icon = CFSTR("Network");
url = copy_icon_url(icon);
if (url != NULL) {
CFDictionarySetValue(dict, kCFUserNotificationIconURLKey,
url);
CFRelease(url);
}
CFDictionaryAddValue(dict, kCFUserNotificationAlternateButtonTitleKey,
CFSTR("Cancel"));
CFDictionaryAddValue(dict, kCFUserNotificationDefaultButtonTitleKey,
CFSTR("OK"));
name = CFDictionaryGetValue(details, kCredentialsDialogueAccountName);
password = CFDictionaryGetValue(details, kCredentialsDialoguePassword);
if (name != NULL && isA_CFType(name, CFNullGetTypeID())) {
}
else {
dialogue_p->name_enabled = TRUE;
password_index = 1;
}
if (password != NULL && isA_CFType(password, CFNullGetTypeID())) {
}
else {
if (password_index == kCFNotFound) {
password_index = 0;
}
dialogue_p->password_enabled = TRUE;
}
array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
if (dialogue_p->name_enabled) {
if (name != NULL) {
CFArrayAppendValue(array, name);
}
else if (password != NULL) {
CFArrayAppendValue(array, CFSTR(""));
}
}
if (dialogue_p->password_enabled) {
flags |= CFUserNotificationSecureTextField(password_index);
if (password != NULL) {
CFArrayAppendValue(array, password);
}
}
if (CFArrayGetCount(array) != 0) {
CFDictionaryAddValue(dict, kCFUserNotificationTextFieldValuesKey,
array);
}
my_CFRelease(&array);
remember_information
= CFDictionaryGetValue(details,
kCredentialsDialogueRememberInformation);
if (remember_information != NULL) {
dialogue_p->checkbox_enabled = TRUE;
array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
CFArrayAppendValue(array, CFSTR("Remember information"));
CFDictionarySetValue(dict, kCFUserNotificationCheckBoxTitlesKey,
array);
my_CFRelease(&array);
if (CFBooleanGetValue(remember_information)) {
flags |= CFUserNotificationCheckBoxChecked(0);
}
}
certs = CFDictionaryGetValue(details, kCredentialsDialogueCertificates);
if (certs != NULL) {
CFStringRef cert_label;
CFArrayRef labels;
cert_label = (dialogue_p->password_enabled)
? CFSTR("No certificate selected")
: CFSTR("Select a certificate");
labels = copy_certificate_labels(dialogue_p, certs, cert_label);
if (labels != NULL) {
CFDictionarySetValue(dict, kCFUserNotificationPopUpTitlesKey,
labels);
CFRelease(labels);
}
}
if (dialogue_p->name_enabled || dialogue_p->password_enabled) {
array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
if (dialogue_p->name_enabled) {
if (dialogue_p->password_enabled) {
CFArrayAppendValue(array, CFSTR("User Name:"));
}
else {
CFArrayAppendValue(array, CFSTR("Account Name (optional):"));
}
}
if (dialogue_p->password_enabled) {
CFArrayAppendValue(array, CFSTR("Password:"));
}
CFDictionaryAddValue(dict, kCFUserNotificationTextFieldTitlesKey,
array);
my_CFRelease(&array);
}
if (certs != NULL && dialogue_p->password_enabled) {
title = copy_localized_string(bundle,
kTitleEthernetCertificateAndPassword,
kTitleAirPortCertificateAndPassword,
ssid);
}
else if (certs == NULL) {
title = copy_localized_string(bundle,
kTitleEthernetPassword,
kTitleAirPortPassword,
ssid);
}
else {
title = copy_localized_string(bundle,
kTitleEthernetCertificate,
kTitleAirPortCertificate,
ssid);
}
CFDictionaryAddValue(dict, kCFUserNotificationAlertHeaderKey, title);
CFRelease(title);
notif = CFUserNotificationCreate(NULL, 0, flags, &error, dict);
if (notif == NULL) {
my_log(LOG_NOTICE, "CFUserNotificationCreate failed, %d", error);
}
my_CFRelease(&dict);
return (notif);
}
CredentialsDialogueRef
CredentialsDialogue_create(CredentialsDialogueResponseCallBack func,
const void * arg1, const void * arg2,
CFDictionaryRef details)
{
CFBundleRef bundle;
CredentialsDialogueRef dialogue_p;
CFUserNotificationRef notif = NULL;
CFRunLoopSourceRef rls = NULL;
bundle = get_bundle();
if (bundle == NULL) {
my_log(LOG_NOTICE, "Can't get bundle");
return (NULL);
}
dialogue_p = malloc(sizeof(*dialogue_p));
if (dialogue_p == NULL) {
my_log(LOG_NOTICE, "CredentialsDialogue_create: malloc failed");
return (NULL);
}
bzero(dialogue_p, sizeof(*dialogue_p));
notif = CredentialsDialogueShow(dialogue_p, bundle, details);
if (notif == NULL) {
goto failed;
}
rls = CFUserNotificationCreateRunLoopSource(NULL, notif,
CredentialsDialogue_response,
0);
if (rls == NULL) {
my_log(LOG_NOTICE, "CFUserNotificationCreateRunLoopSource failed");
goto failed;
}
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
dialogue_p->notif = notif;
dialogue_p->rls = rls;
dialogue_p->func = func;
dialogue_p->arg1 = arg1;
dialogue_p->arg2 = arg2;
LIST_INSERT_HEAD(S_CredentialsDialogueHead_p, dialogue_p, entries);
return (dialogue_p);
failed:
free(dialogue_p);
my_CFRelease(¬if);
my_CFRelease(&rls);
return (NULL);
}
void
CredentialsDialogue_free(CredentialsDialogueRef * dialogue_p_p)
{
CredentialsDialogueRef dialogue_p = *dialogue_p_p;
if (dialogue_p) {
LIST_REMOVE(dialogue_p, entries);
if (dialogue_p->rls) {
CFRunLoopSourceInvalidate(dialogue_p->rls);
my_CFRelease(&dialogue_p->rls);
}
if (dialogue_p->notif) {
(void)CFUserNotificationCancel(dialogue_p->notif);
my_CFRelease(&dialogue_p->notif);
}
my_CFRelease(&dialogue_p->certificates);
bzero(dialogue_p, sizeof(*dialogue_p));
free(dialogue_p);
}
*dialogue_p_p = NULL;
return;
}
#include <signal.h>
#include <syslog.h>
#include <CoreFoundation/CFData.h>
#include <CoreFoundation/CFPropertyList.h>
struct TrustDialogue_e;
#define LIST_HEAD_TrustDialogueHead LIST_HEAD(TrustDialogueHead, TrustDialogue_s)
static LIST_HEAD_TrustDialogueHead S_trust_head;
static struct TrustDialogueHead * S_TrustDialogueHead_p = &S_trust_head;
#define LIST_ENTRY_TrustDialogue LIST_ENTRY(TrustDialogue_s)
struct TrustDialogue_s {
LIST_ENTRY_TrustDialogue entries;
TrustDialogueResponseCallBack func;
const void * arg1;
const void * arg2;
pid_t pid;
int fdp[2];
CFDictionaryRef trust_plist;
};
static TrustDialogueRef
TrustDialogue_find(pid_t pid)
{
TrustDialogueRef scan;
LIST_FOREACH(scan, S_TrustDialogueHead_p, entries) {
if (scan->pid == pid)
return (scan);
}
return (NULL);
}
static void
TrustDialogue_callback(pid_t pid, int status, __unused struct rusage * rusage,
__unused void * context)
{
TrustDialogueRef dialogue_p;
TrustDialogueResponse response;
dialogue_p = TrustDialogue_find(pid);
if (dialogue_p == NULL) {
return;
}
response.proceed = FALSE;
if (WIFEXITED(status)) {
int exit_code = WEXITSTATUS(status);
my_log(LOG_DEBUG, "TrustDialogue_callback: child %d exit(%d)",
pid, exit_code);
if (exit_code == 0) {
response.proceed = 1;
}
}
else if (WIFSIGNALED(status)) {
my_log(LOG_DEBUG, "TrustDialogue_callback: child %d signaled(%d)",
pid, WTERMSIG(status));
}
dialogue_p->pid = -1;
(*dialogue_p->func)(dialogue_p->arg1, dialogue_p->arg2, &response);
return;
}
static void
TrustDialogue_setup_child(TrustDialogueRef dialogue_p)
{
int fd;
int i;
for (i = getdtablesize() - 1; i >= 0; i--) {
if (i != dialogue_p->fdp[0]) {
close(i);
}
}
if (dialogue_p->fdp[0] != STDIN_FILENO) {
dup(dialogue_p->fdp[0]);
close(dialogue_p->fdp[0]);
}
fd = open(_PATH_DEVNULL, O_RDWR, 0);
dup(fd);
return;
}
static void
TrustDialogue_setup_parent(TrustDialogueRef dialogue_p)
{
size_t count;
CFDataRef data;
size_t write_count;
close(dialogue_p->fdp[0]);
dialogue_p->fdp[0] = -1;
data = CFPropertyListCreateXMLData(NULL,
dialogue_p->trust_plist);
count = CFDataGetLength(data);
write_count = write(dialogue_p->fdp[1],
(void *)CFDataGetBytePtr(data),
count);
my_CFRelease(&data);
if (write_count != count) {
if (write_count == -1) {
my_log(LOG_NOTICE,
"TrustDialogue_setup_parent: write on pipe failed, %m");
}
else {
my_log(LOG_NOTICE,
"TrustDialogue_setup_parent: wrote %d expected %d",
write_count, count);
}
}
close(dialogue_p->fdp[1]);
dialogue_p->fdp[1] = -1;
return;
}
static void
TrustDialogue_setup(pid_t pid, void * context)
{
TrustDialogueRef Dialogue_p = (TrustDialogueRef)context;
if (pid == 0) {
TrustDialogue_setup_child(Dialogue_p);
}
else {
TrustDialogue_setup_parent(Dialogue_p);
}
return;
}
#define EAPTLSTRUST_PATH "/System/Library/PrivateFrameworks/EAP8021X.framework/Support/eaptlstrust.app/Contents/MacOS/eaptlstrust"
static pthread_once_t initialized = PTHREAD_ONCE_INIT;
TrustDialogueRef
TrustDialogue_create(TrustDialogueResponseCallBack func,
const void * arg1, const void * arg2,
CFDictionaryRef trust_info, CFTypeRef ssid)
{
char * argv[2] = {EAPTLSTRUST_PATH, NULL};
CFBundleRef bundle;
CFStringRef caller_label;
CFMutableDictionaryRef dict;
TrustDialogueRef dialogue_p;
extern void _SCDPluginExecInit();
bundle = get_bundle();
if (bundle == NULL) {
my_log(LOG_NOTICE, "Can't get bundle");
return (NULL);
}
pthread_once(&initialized, _SCDPluginExecInit);
dialogue_p = (TrustDialogueRef)malloc(sizeof(*dialogue_p));
bzero(dialogue_p, sizeof(*dialogue_p));
dialogue_p->pid = -1;
dialogue_p->fdp[0] = dialogue_p->fdp[1] = -1;
if (pipe(dialogue_p->fdp) == -1) {
my_log(LOG_NOTICE, "TrustDialogue_create: pipe failed, %m");
goto failed;
}
dict = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(dict, CFSTR("TrustInformation"), trust_info);
CFDictionarySetValue(dict, CFSTR("Icon"), CFSTR("Network"));
caller_label = copy_localized_title(bundle, ssid);
if (caller_label != NULL) {
CFDictionarySetValue(dict, CFSTR("CallerLabel"),
caller_label);
CFRelease(caller_label);
}
dialogue_p->trust_plist = CFDictionaryCreateCopy(NULL, dict);
my_CFRelease(&dict);
dialogue_p->pid
= _SCDPluginExecCommand2(TrustDialogue_callback, NULL,
geteuid(), getegid(),
EAPTLSTRUST_PATH, argv,
TrustDialogue_setup,
dialogue_p);
if (dialogue_p->pid == -1) {
my_log(LOG_NOTICE,
"TrustDialogue_create: _SCDPluginExecCommand2 failed, %m");
goto failed;
}
dialogue_p->func = func;
dialogue_p->arg1 = arg1;
dialogue_p->arg2 = arg2;
LIST_INSERT_HEAD(S_TrustDialogueHead_p, dialogue_p, entries);
return (dialogue_p);
failed:
TrustDialogue_free(&dialogue_p);
return (NULL);
}
CFDictionaryRef
TrustDialogue_trust_info(TrustDialogueRef dialogue_p)
{
if (dialogue_p->trust_plist == NULL) {
return (NULL);
}
return (CFDictionaryGetValue(dialogue_p->trust_plist,
CFSTR("TrustInformation")));
}
void
TrustDialogue_free(TrustDialogueRef * dialogue_p_p)
{
TrustDialogueRef dialogue_p;
if (dialogue_p_p == NULL) {
return;
}
dialogue_p = *dialogue_p_p;
if (dialogue_p != NULL) {
LIST_REMOVE(dialogue_p, entries);
if (dialogue_p->pid != -1) {
if (kill(dialogue_p->pid, SIGHUP)) {
my_log(LOG_NOTICE, "TrustDialogue_free kill(%d) failed, %m",
dialogue_p->pid);
}
}
if (dialogue_p->fdp[0] != -1) {
close(dialogue_p->fdp[0]);
}
if (dialogue_p->fdp[0] != -1) {
close(dialogue_p->fdp[1]);
}
my_CFRelease(&dialogue_p->trust_plist);
free(dialogue_p);
*dialogue_p_p = NULL;
}
return;
}
struct AlertDialogue_s;
#define LIST_HEAD_AlertDialogueHead LIST_HEAD(AlertDialogueHead, AlertDialogue_s)
static LIST_HEAD_AlertDialogueHead S_AlertDialogueHead;
static struct AlertDialogueHead * S_AlertDialogueHead_p = &S_AlertDialogueHead;
#define LIST_ENTRY_AlertDialogue LIST_ENTRY(AlertDialogue_s)
struct AlertDialogue_s {
LIST_ENTRY_AlertDialogue entries;
CFUserNotificationRef notif;
CFRunLoopSourceRef rls;
AlertDialogueResponseCallBack func;
const void * arg1;
const void * arg2;
};
static CFUserNotificationRef
AlertDialogueShow(AlertDialogueRef dialogue_p,
CFBundleRef bundle, CFStringRef message)
{
CFMutableDictionaryRef dict = NULL;
SInt32 error = 0;
CFStringRef icon;
CFUserNotificationRef notif = NULL;
CFURLRef url;
dict = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
url = CFBundleCopyBundleURL(bundle);
if (url != NULL) {
CFDictionarySetValue(dict, kCFUserNotificationLocalizationURLKey,
url);
CFRelease(url);
}
icon = CFSTR("Network");
url = copy_icon_url(icon);
if (url != NULL) {
CFDictionarySetValue(dict, kCFUserNotificationIconURLKey,
url);
CFRelease(url);
}
CFDictionaryAddValue(dict, kCFUserNotificationDefaultButtonTitleKey,
CFSTR("DISCONNECT"));
CFDictionaryAddValue(dict, kCFUserNotificationAlertHeaderKey,
CFSTR("AUTHENTICATION_FAILED"));
CFDictionaryAddValue(dict, kCFUserNotificationAlertMessageKey, message);
notif = CFUserNotificationCreate(NULL, 0, 0, &error, dict);
if (notif == NULL) {
my_log(LOG_NOTICE, "CFUserNotificationCreate failed, %d", error);
}
my_CFRelease(&dict);
return (notif);
}
static AlertDialogueRef
AlertDialogue_find(CFUserNotificationRef notif)
{
AlertDialogueRef scan;
LIST_FOREACH(scan, S_AlertDialogueHead_p, entries) {
if (scan->notif == notif)
return (scan);
}
return (NULL);
}
static void
AlertDialogue_response(CFUserNotificationRef notif,
CFOptionFlags response_flags)
{
volatile AlertDialogueRef dialogue_p;
dialogue_p = AlertDialogue_find(notif);
if (dialogue_p == NULL) {
return;
}
if (dialogue_p->rls != NULL) {
CFRunLoopSourceInvalidate(dialogue_p->rls);
my_CFRelease(&dialogue_p->rls);
}
my_CFRelease(&dialogue_p->notif);
(*dialogue_p->func)(dialogue_p->arg1, dialogue_p->arg2);
return;
}
AlertDialogueRef
AlertDialogue_create(AlertDialogueResponseCallBack func,
const void * arg1, const void * arg2,
CFStringRef message)
{
CFBundleRef bundle;
AlertDialogueRef dialogue_p;
CFUserNotificationRef notif = NULL;
CFRunLoopSourceRef rls = NULL;
bundle = get_bundle();
if (bundle == NULL) {
my_log(LOG_NOTICE, "Can't get bundle");
return (NULL);
}
dialogue_p = malloc(sizeof(*dialogue_p));
if (dialogue_p == NULL) {
my_log(LOG_NOTICE, "AlertDialogue_create: malloc failed");
return (NULL);
}
bzero(dialogue_p, sizeof(*dialogue_p));
notif = AlertDialogueShow(dialogue_p, bundle, message);
if (notif == NULL) {
goto failed;
}
rls = CFUserNotificationCreateRunLoopSource(NULL, notif,
AlertDialogue_response,
0);
if (rls == NULL) {
my_log(LOG_NOTICE, "CFUserNotificationCreateRunLoopSource failed");
goto failed;
}
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
dialogue_p->notif = notif;
dialogue_p->rls = rls;
dialogue_p->func = func;
dialogue_p->arg1 = arg1;
dialogue_p->arg2 = arg2;
LIST_INSERT_HEAD(S_AlertDialogueHead_p, dialogue_p, entries);
return (dialogue_p);
failed:
free(dialogue_p);
my_CFRelease(¬if);
my_CFRelease(&rls);
return (NULL);
}
void
AlertDialogue_free(AlertDialogueRef * dialogue_p_p)
{
AlertDialogueRef dialogue_p = *dialogue_p_p;
if (dialogue_p) {
LIST_REMOVE(dialogue_p, entries);
if (dialogue_p->rls) {
CFRunLoopSourceInvalidate(dialogue_p->rls);
my_CFRelease(&dialogue_p->rls);
}
if (dialogue_p->notif) {
(void)CFUserNotificationCancel(dialogue_p->notif);
my_CFRelease(&dialogue_p->notif);
}
bzero(dialogue_p, sizeof(*dialogue_p));
free(dialogue_p);
}
*dialogue_p_p = NULL;
return;
}
#ifdef TEST_DIALOGUE
#include <SystemConfiguration/SCPrivate.h>
void
my_callback(const void * arg1, const void * arg2, CredentialsDialogueResponseRef response)
{
CredentialsDialogueRef * dialogue_p_p = (CredentialsDialogueRef *)arg1;
CredentialsDialogueRef temp = *dialogue_p_p;
if (response->username) {
SCPrint(TRUE, stdout, CFSTR("Account: %@\n"), response->username);
}
if (response->password) {
SCPrint(TRUE, stdout, CFSTR("Password: %@\n"), response->password);
}
if (response->chosen_identity != NULL) {
SecCertificateRef cert;
CFStringRef str;
SecIdentityCopyCertificate(response->chosen_identity, &cert);
if (cert != NULL) {
str = SecCertificateCopyShortDescription(NULL, cert, NULL);
CFRelease(cert);
}
SCPrint(TRUE, stdout, CFSTR("Identity: %@\n"), str);
CFRelease(str);
}
if (response->remember_information) {
printf("Remember information checked\n");
}
if (response->user_cancelled) {
printf("User cancelled\n");
}
CredentialsDialogue_free(&temp);
return;
}
void
my_alert_callback(const void * arg1, const void * arg2)
{
AlertDialogueRef alert_p;
AlertDialogueRef * alert_p_p = (AlertDialogueRef *)arg1;
if (alert_p_p == NULL) {
printf("NULL pointer!\n");
return;
}
alert_p = *alert_p_p;
AlertDialogue_free(alert_p_p);
printf("Alert done\n");
return;
}
int
main(int argc, char * argv[])
{
AlertDialogueRef alert_p;
AlertDialogueRef * alert_p_p = &alert_p;
CredentialsDialogueRef dialogue_p;
CredentialsDialogueRef dialogue_p2;
CredentialsDialogueRef dialogue_p3;
CFMutableDictionaryRef dict = NULL;
CredentialsDialogueRef * p = &dialogue_p;
CredentialsDialogueRef * p2 = &dialogue_p2;
CredentialsDialogueRef * p3 = &dialogue_p3;
CFArrayRef certs = NULL;
(void)EAPSecIdentityListCreate(&certs);
dict = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(dict, kCredentialsDialogueAccountName,
CFSTR("dieter"));
CFDictionarySetValue(dict, kCredentialsDialoguePassword,
kCFNull);
CFDictionarySetValue(dict, kCredentialsDialogueSSID, CFSTR("SSID"));
CFDictionarySetValue(dict, kCredentialsDialogueRememberInformation,
kCFBooleanTrue);
if (certs != NULL) {
CFDictionarySetValue(dict, kCredentialsDialogueCertificates, certs);
}
dialogue_p = CredentialsDialogue_create(my_callback, p, NULL, dict);
CFRelease(dict);
dict = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(dict, kCredentialsDialogueAccountName,
CFSTR("dieter"));
CFDictionarySetValue(dict, kCredentialsDialoguePassword,
CFSTR("siegmund"));
CFDictionarySetValue(dict, kCredentialsDialogueRememberInformation,
kCFBooleanTrue);
if (certs != NULL) {
CFDictionarySetValue(dict, kCredentialsDialogueCertificates, certs);
}
dialogue_p2 = CredentialsDialogue_create(my_callback, p2, NULL, dict);
CFRelease(dict);
dict = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(dict, kCredentialsDialogueAccountName,
CFSTR("dieter"));
CFDictionarySetValue(dict, kCredentialsDialoguePassword,
CFSTR("siegmund"));
CFDictionarySetValue(dict, kCredentialsDialogueSSID, CFSTR("SomeSSID"));
CFDictionarySetValue(dict, kCredentialsDialogueRememberInformation,
kCFBooleanFalse);
dialogue_p3 = CredentialsDialogue_create(my_callback, p3, NULL,
dict);
alert_p = AlertDialogue_create(my_alert_callback, alert_p_p, NULL,
CFSTR("Here we are"));
CFRunLoopRun();
exit(0);
return (0);
}
#endif