#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#include <syslog.h>
#include <netdb.h>
#include <pwd.h>
#include <setjmp.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <pthread.h>
#include <net/if.h>
#include <CoreFoundation/CFBundle.h>
#include <CoreFoundation/CFUserNotification.h>
#ifdef TARGET_EMBEDDED_OS
#include <CoreFoundation/CFNumber.h>
#endif
#define APPLE 1
#include "../Family/ppp_defs.h"
#include "../Family/if_ppp.h"
#include "../Family/ppp_domain.h"
#include "../Helpers/pppd/pppd.h"
#include "../Helpers/pppd/fsm.h"
#include "../Helpers/pppd/lcp.h"
#define ICON "NetworkConnect.icns"
#define DIALOG_PASSWORD_ASK 0
#define DIALOG_PASSWORD_RETRY 1
#define DIALOG_PASSWORD_CHANGE 2
#ifdef UNUSED
static int dialog_idle(struct ppp_idle *idle);
#endif
static int dialog_start_link();
static int dialog_change_password();
static int dialog_retry_password();
static int dialog_link_up();
static int dialog_ask(CFStringRef message, CFStringRef ok, CFStringRef cancel, int timeout);
static void dialog_reminder(void *arg);
static void dialog_phasechange(void *arg, uintptr_t p);
static void dialog_change_reminder();
extern int kill_link;
static CFBundleRef bundle = 0;
static CFURLRef bundleURL = 0;
static pthread_t reminderthread = 0;
static int reminderresult = 0;
static bool askpasswordafter = 0;
static bool noaskpassword = 0;
static bool noidleprompt = 0;
static int reminder = 0;
static int dialogtype = 0;
static pthread_t dialog_ui_thread;
static int dialog_ui_fds[2];
static CFUserNotificationRef dialog_alert = 0;
option_t dialogs_options[] = {
{ "noaskpassword", o_bool, &noaskpassword,
"Don't ask for a password at all", 1 },
{ "askpasswordafter", o_bool, &askpasswordafter,
"Don't ask for a password before to connect", 1 },
{ "noidleprompt", o_bool, &noidleprompt,
"Don't ask user before to disconnect on idle", 1 },
{ "reminder", o_int, &reminder,
"Ask user to stay connected after reminder period", 0, 0, 0, 0xFFFFFFFF, 0, 0, 0, dialog_change_reminder },
{ "dialogtype", o_int, &dialogtype,
"Dialog type to display (PPP or VPN)"},
{ NULL }
};
int start(CFBundleRef ref)
{
bundle = ref;
bundleURL = CFBundleCopyBundleURL(bundle);
if (!bundleURL)
return 1;
CFRetain(bundle);
add_options(dialogs_options);
add_notifier(&phasechange, dialog_phasechange, 0);
start_link_hook = dialog_start_link;
change_password_hook = dialog_change_password;
retry_password_hook = dialog_retry_password;
link_up_hook = dialog_link_up;
return 0;
}
void dialog_phasechange(void *arg, uintptr_t p)
{
if (reminder && p == PHASE_RUNNING)
TIMEOUT(dialog_reminder, 0, reminder);
else
UNTIMEOUT(dialog_reminder, 0);
}
void dialog_change_reminder()
{
UNTIMEOUT(dialog_reminder, 0);
if (reminder && phase == PHASE_RUNNING)
TIMEOUT(dialog_reminder, 0, reminder);
}
void *dialog_reminder_thread(void *arg)
{
int status, timeout;
status = pthread_detach(pthread_self());
if (status == noErr) {
if (reminder < (60 * 5)) timeout = 30;
else if (reminder < (60 * 10)) timeout = 60;
else if (reminder < (60 * 20)) timeout = 120;
else if (reminder < (60 * 30)) timeout = 180;
else timeout = 240;
reminderresult = dialog_ask(CFSTR("Reminder timeout"), CFSTR("Stay connected"), CFSTR("Disconnect"), timeout);
}
return 0;
}
void dialog_reminder_watch(void *arg)
{
int tlim;
switch (reminderresult) {
case -1:
TIMEOUT(dialog_reminder_watch, 0, 2);
break;
case 0:
TIMEOUT(dialog_reminder, 0, reminder);
UNTIMEOUT(check_idle, NULL);
if (idle_time_hook != 0)
tlim = (*idle_time_hook)(NULL);
else
tlim = idle_time_limit;
if (tlim > 0)
TIMEOUT(check_idle, NULL, tlim);
break;
default :
lcp_close(0, "User request");
status = EXIT_USER_REQUEST;
}
}
void dialog_reminder(void *arg)
{
int status;
reminderresult = -1;
status = pthread_create(&reminderthread, NULL, dialog_reminder_thread, NULL);
if (status == noErr) {
TIMEOUT(dialog_reminder_watch, 0, 2);
}
}
int dialog_password(char *user, int maxuserlen, char *passwd, int maxpasswdlen, int dialog_type, char *message)
{
CFOptionFlags flags;
CFMutableDictionaryRef dict;
SInt32 err;
CFMutableArrayRef array;
CFURLRef url;
CFStringRef str, str1;
int ret = 0, loop = 0;
do {
ret = 0;
dict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (dict) {
url = CFBundleCopyResourceURL(bundle, CFSTR(ICON), NULL, NULL);
if (url) {
CFDictionaryAddValue(dict, kCFUserNotificationIconURLKey, url);
CFRelease(url);
}
if (message) {
if (str = CFStringCreateWithCString(NULL, message, kCFStringEncodingUTF8)) {
CFDictionaryAddValue(dict, kCFUserNotificationAlertMessageKey, str);
CFRelease(str);
}
}
switch (dialog_type) {
case DIALOG_PASSWORD_CHANGE:
CFDictionaryAddValue(dict, kCFUserNotificationAlertMessageKey, (tokencard == 1) ? CFSTR("Expired token") : CFSTR("Expired password"));
break;
case DIALOG_PASSWORD_RETRY:
CFDictionaryAddValue(dict, kCFUserNotificationAlertMessageKey, (tokencard == 1) ? CFSTR("Incorrect token") : CFSTR("Incorrect password"));
break;
}
array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
if (array) {
switch (dialog_type) {
case DIALOG_PASSWORD_CHANGE:
CFArrayAppendValue(array, (tokencard == 1) ? CFSTR("New token:") : CFSTR("New password:"));
CFArrayAppendValue(array, (tokencard == 1) ? CFSTR("Confirm new token:") : CFSTR("Confirm new password:"));
break;
case DIALOG_PASSWORD_RETRY:
CFArrayAppendValue(array, CFSTR("Retry name:"));
CFArrayAppendValue(array, (tokencard == 1) ? CFSTR("Retry token:") : CFSTR("Retry password:"));
break;
case DIALOG_PASSWORD_ASK:
default:
CFArrayAppendValue(array, CFSTR("Account Name:"));
CFArrayAppendValue(array, (tokencard == 1) ? CFSTR("Token:") : CFSTR("Password:"));
break;
}
CFDictionaryAddValue(dict, kCFUserNotificationTextFieldTitlesKey, array);
CFRelease(array);
}
array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
if (array) {
switch (dialog_type) {
case DIALOG_PASSWORD_CHANGE:
break;
case DIALOG_PASSWORD_RETRY:
case DIALOG_PASSWORD_ASK:
default:
if (str = CFStringCreateWithCString(NULL, user, kCFStringEncodingUTF8)) {
CFArrayAppendValue(array, str);
CFRelease(str);
}
}
CFDictionaryAddValue(dict, kCFUserNotificationTextFieldValuesKey, array);
CFRelease(array);
}
CFDictionaryAddValue(dict, kCFUserNotificationLocalizationURLKey, bundleURL);
CFDictionaryAddValue(dict, kCFUserNotificationAlertHeaderKey, (dialogtype == 0) ? CFSTR("Network Connection") : CFSTR("VPN Connection"));
if (loop)
CFDictionaryAddValue(dict, kCFUserNotificationAlertMessageKey, (tokencard == 1) ? CFSTR("Incorrectly entered token") : CFSTR("Incorrectly entered password"));
CFDictionaryAddValue(dict, kCFUserNotificationAlternateButtonTitleKey, CFSTR("Cancel"));
flags = CFUserNotificationSecureTextField(1);
if (dialog_type == DIALOG_PASSWORD_CHANGE)
flags += CFUserNotificationSecureTextField(0);
#ifdef TARGET_EMBEDDED_OS
CFDictionarySetValue(dict, CFSTR("SBUserNotificationGroupsTextFields"), kCFBooleanTrue);
#endif
dialog_alert = CFUserNotificationCreate(NULL, 0, flags, &err, dict);
if (dialog_alert) {
CFUserNotificationReceiveResponse(dialog_alert, 0, &flags);
if ((flags & 3) == 1) {
}
else {
ret = 1;
str = CFUserNotificationGetResponseValue(dialog_alert, kCFUserNotificationTextFieldValuesKey, 0);
str1 = CFUserNotificationGetResponseValue(dialog_alert, kCFUserNotificationTextFieldValuesKey, 1);
switch (dialog_type) {
case DIALOG_PASSWORD_CHANGE:
if (!(str && str1
&& (CFStringCompare(str, str1, 0) == kCFCompareEqualTo)
&& CFStringGetCString(str1, passwd, maxpasswdlen, kCFStringEncodingUTF8)))
ret = -1;
break;
case DIALOG_PASSWORD_RETRY:
case DIALOG_PASSWORD_ASK:
default:
if (!(str && str1
&& CFStringGetCString(str, user, maxuserlen, kCFStringEncodingUTF8)
&& CFStringGetCString(str1, passwd, maxpasswdlen, kCFStringEncodingUTF8)))
ret = -1;
}
}
CFRelease(dialog_alert);
dialog_alert = 0;
}
CFRelease(dict);
}
loop++;
} while (ret < 0);
return ret;
}
static void
*dialog_UIThread(void *arg)
{
char result = 0;
int ret;
if (pthread_detach(pthread_self()) == 0) {
ret = dialog_password(username, MAXNAMELEN, passwd, MAXSECRETLEN, DIALOG_PASSWORD_ASK, 0);
if (ret == 1)
result = 1;
}
if (dialog_ui_fds[1] != -1)
write(dialog_ui_fds[1], &result, 1);
return 0;
}
static int readn(int ref, void *data, int len)
{
int n, left = len;
void *p = data;
while (left > 0) {
if ((n = read(ref, p, left)) < 0) {
if (kill_link)
return 0;
if (errno != EINTR)
return -1;
n = 0;
}
else if (n == 0)
break;
left -= n;
p += n;
}
return (len - left);
}
int dialog_invoke_ui_thread()
{
int ret = 0;
char result;
if (pipe(dialog_ui_fds) < 0) {
error("Dialogs failed to create pipe for User Interface...\n");
return -1;
}
if (pthread_create(&dialog_ui_thread, NULL, dialog_UIThread, 0 )) {
error("Dialogs failed to create thread for client User Interface...\n");
close(dialog_ui_fds[0]);
close(dialog_ui_fds[1]);
return 1;
}
ret = readn(dialog_ui_fds[0], &result, 1);
close(dialog_ui_fds[0]);
dialog_ui_fds[0] = -1;
close(dialog_ui_fds[1]);
dialog_ui_fds[1] = -1;
if (ret <= 0) {
if (dialog_alert) {
CFUserNotificationCancel(dialog_alert);
}
return 0;
}
ret = result;
if (ret == 1)
strncpy(user, username, MAXNAMELEN);
return ret;
}
int dialog_start_link()
{
if (noaskpassword || *passwd || askpasswordafter)
return 1;
return dialog_invoke_ui_thread();
}
int dialog_change_password(char *msg)
{
int ret = 0;
if (noaskpassword)
return 1;
ret = dialog_password(username, MAXNAMELEN, new_passwd, MAXSECRETLEN, DIALOG_PASSWORD_CHANGE, msg);
return ret;
}
int dialog_retry_password(char *msg)
{
int ret = 0;
if (noaskpassword)
return 1;
ret = dialog_password(username, MAXNAMELEN, passwd, MAXSECRETLEN, DIALOG_PASSWORD_RETRY, msg);
if (ret == 1)
strncpy(user, username, MAXNAMELEN);
return ret;
}
int dialog_link_up()
{
if (noaskpassword || *passwd || !askpasswordafter)
return 1;
return dialog_invoke_ui_thread();
}
#ifdef UNUSED
int dialog_idle(struct ppp_idle *idle)
{
int itime = 0;
if (idle == 0)
return idle_time_limit;
itime = MIN(idle->xmit_idle, idle->recv_idle);
if ((idle_time_limit - itime) <= 0) {
if (noidleprompt || dialog_ask(CFSTR("Idle timeout"), CFSTR("Stay connected"), CFSTR("Disconnect"), 30)) {
return 0;
}
return idle_time_limit;
}
return idle_time_limit - itime;
}
#endif
int dialog_ask(CFStringRef message, CFStringRef ok, CFStringRef cancel, int timeout)
{
CFUserNotificationRef alert;
CFOptionFlags flags;
CFMutableDictionaryRef dict;
SInt32 error;
CFURLRef url;
flags = 3; dict = CFDictionaryCreateMutable(NULL, 0,
&kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (dict) {
url = CFBundleCopyResourceURL(bundle, CFSTR(ICON), NULL, NULL);
if (url) {
CFDictionaryAddValue(dict, kCFUserNotificationIconURLKey, url);
CFRelease(url);
}
CFDictionaryAddValue(dict, kCFUserNotificationLocalizationURLKey, bundleURL);
CFDictionaryAddValue(dict, kCFUserNotificationAlertHeaderKey, (dialogtype == 0) ? CFSTR("Network Connection") : CFSTR("VPN Connection"));
CFDictionaryAddValue(dict, kCFUserNotificationAlertMessageKey, message);
if (ok) CFDictionaryAddValue(dict, kCFUserNotificationDefaultButtonTitleKey, ok);
if (cancel) CFDictionaryAddValue(dict, kCFUserNotificationAlternateButtonTitleKey, cancel);
alert = CFUserNotificationCreate(NULL, timeout, kCFUserNotificationCautionAlertLevel, &error, dict);
if (alert) {
CFUserNotificationReceiveResponse(alert, timeout, &flags);
CFRelease(alert);
}
CFRelease(dict);
}
return (flags & 3);
}