Index: trunk/Cocoa/Pester/Source/PSPowerManager.m
===================================================================
--- trunk/Cocoa/Pester/Source/PSPowerManager.m	(revision 105)
+++ trunk/Cocoa/Pester/Source/PSPowerManager.m	(revision 118)
@@ -8,8 +8,17 @@
 
 #import "PSPowerManager.h"
+#import "wakein.h"
 
 #import <IOKit/pwr_mgt/IOPMLib.h>
 #import <IOKit/IOMessage.h>
 #import <CoreFoundation/CoreFoundation.h>
+
+// MoreIsBetter interfaces
+#include "MoreUNIX.h"
+#include "MoreSecurity.h"
+#include "MoreCFQ.h"
+
+// exceptions
+NSString * const PSPowerManagerException = @"PSPowerManagerException";
 
 /*
@@ -52,5 +61,12 @@
 // for superusers
 
-#define PMU_MAGIC_PASSWORD	0x0101FACE // BEEF
+// njr 2003.03.12: both seem to be interchangeable now, and the sleep time is
+// no longer settable as non-root.
+
+#ifdef WAKEIN
+#define PMU_MAGIC_PASSWORD	0x0101BEEF
+#else
+#define PMU_MAGIC_PASSWORD	0x0101FACE
+#endif
 
 /* ==========================================
@@ -173,4 +189,9 @@
     if (pmuReference == NULL) return NO;
     closePMUComPort(pmuReference);
+    NS_DURING
+        [self authorize];
+    NS_HANDLER
+        return NO; // XXX display error?
+    NS_ENDHANDLER
     return YES;
 }
@@ -196,25 +217,87 @@
     if (rawWakeTime == 0) return nil;
     // XXX no idea what the epoch is supposed to be, but this works...
-    return [NSDate dateWithTimeIntervalSinceReferenceDate: rawWakeTime - 18446744072475718320LLU];
-}
-
-+ (void)setWakeTime:(NSDate *)time;
-{
+    return [NSDate dateWithTimeIntervalSinceReferenceDate: rawWakeTime - [[NSTimeZone systemTimeZone] secondsFromGMT] - 18446744072475736320LLU];
+}
+
++ (void)_execWakeToolWithRequestDictionary:(NSDictionary *)request;
+{
+    AuthorizationRef auth = NULL;
+    NSException *exception = NULL;
+
+    NS_DURING
+        CFURLRef tool = NULL;
+        Boolean toolFound;
+        OSStatus err = MoreSecCopyHelperToolURLAndCheckBundled(
+                                                      CFBundleGetMainBundle(), CFSTR("wakeinTemplate"), kApplicationSupportFolderType, CFSTR("Pester"), CFSTR("wakein"), &tool, &toolFound);
+        if (err != noErr) [NSException raise: PSPowerManagerException format: NSLocalizedString(@"Can't set up timed wake from sleep: unable to copy helper tool to Application Support folder (error %u)", "MoreSecCopyHelperToolURLAndCheckBundled failure"), err];
+        [(NSURL *)tool autorelease];
+
+        // if we've found the tool (and it's setuid root), still get an AuthorizationRef, but don't bother to obtain additional rights
+        err = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, toolFound ? kAuthorizationFlagExtendRights | kAuthorizationFlagInteractionAllowed : kAuthorizationFlagDefaults, &auth);
+        if (err != noErr) [NSException raise: PSPowerManagerException format: NSLocalizedString(@"Can't set up timed wake from sleep: AuthorizationCreate failed (error %u)", "AuthorizationCreate failure"), err];
+
+        NSDictionary *response;
+        err = MoreSecExecuteRequestInHelperTool(tool, auth, (CFDictionaryRef)request, (CFDictionaryRef *)&response);
+        [response autorelease];
+        if (err != noErr) [NSException raise: PSPowerManagerException format: NSLocalizedString(@"Can't set up timed wake from sleep: can't obtain response from helper tool (error %u)", "MoreSecExecuteRequestInHelperTool failure"), err];
+
+        NSLog(@"%@", response);
+
+        NSString *wakeinException = [response objectForKey: kPesterWakeException];
+        if (wakeinException != nil) [NSException raise: PSPowerManagerException format: NSLocalizedString(@"Can't set up timed wake from sleep: helper tool reported the error '%@'", "kPesterWakeException"), wakeinException];
+
+        err = MoreSecGetErrorFromResponse((CFDictionaryRef)response);
+        if (err != noErr) [NSException raise: PSPowerManagerException format: NSLocalizedString(@"Can't set up timed wake from sleep: helper tool reported an error of type %u", "MoreSecGetErrorFromResponse"), err];
+        
+    NS_HANDLER
+        exception = localException;
+    NS_ENDHANDLER
+
+    if (auth != NULL) AuthorizationFree(auth, kAuthorizationFlagDestroyRights);
+    if (exception != NULL) [exception raise];
+}
+
++ (void)authorize;
+{
+    [self _execWakeToolWithRequestDictionary: [NSDictionary dictionary]];
+}
+
++ (void)setWakeInterval:(unsigned long)wakeInterval;
+{
+#ifdef WAKEIN
     io_service_t pmuReference = [self _pmuReference];
-    unsigned long wakeTime;
-
-    if (time == nil) wakeTime = 0;
-    else {
-        wakeTime = [time timeIntervalSinceNow];
-        if (wakeTime == 0) wakeTime++; // 0 will disable
+    NSLog(@"writePMUProperty[%u] 0x%lX AutoWake = %lu", pmuReference, PMU_MAGIC_PASSWORD, wakeInterval);
+    writePMUProperty(pmuReference, CFSTR("AutoWake"), (unsigned long *)&wakeInterval, sizeof(wakeInterval));
+
+    closePMUComPort(pmuReference);
+#else
+    [self _execWakeToolWithRequestDictionary: [NSDictionary dictionaryWithObject: [NSNumber numberWithUnsignedLong: wakeInterval] forKey: kPesterWakeTime]];
+#endif
+}
+
++ (void)setWakeTime:(NSDate *)time overrideIfEarlier:(BOOL)override;
+{
+    unsigned long wakeInterval;
+
+    if (time == nil) {
+        wakeInterval = 0;
+        override = YES;
+    } else {
+        wakeInterval = [time timeIntervalSinceNow];
+        if (wakeInterval == 0) wakeInterval++; // 0 will disable
+        if (!override) {
+            NSDate *wakeTime = [self wakeTime];
+            override = (wakeTime == nil || [wakeTime compare: time] == NSOrderedDescending);
+        }
     }
-    writePMUProperty(pmuReference, CFSTR("AutoWake"), (unsigned long *)&wakeTime, sizeof(wakeTime));
-    
-    closePMUComPort(pmuReference);
+
+    if (override) {
+        [self setWakeInterval: wakeInterval];
+    }
 }
 
 + (void)clearWakeTime;
 {
-    [self setWakeTime: nil];
+    [self setWakeTime: nil overrideIfEarlier: YES];
 }
 
