3 Created by Evan Schoenberg on 10/31/2004
6 #import <Foundation/Foundation.h>
9 * @brief Daemon to run applescripts, optionally with a function name and arguments, and respond over NSDistributedNotificationCenter
12 //After SECONDS_INACTIVITY_BEFORE_AUTOMATIC_QUIT seconds without any activity, the daemon will quit itself
13 #define SECONDS_INACTIVITY_BEFORE_AUTOMATIC_QUIT 600 /* 10 minutes */
15 @interface AIApplescriptRunner : NSObject {}
16 - (void)applescriptRunnerIsReady;
17 - (void)resetAutomaticQuitTimer;
20 @implementation AIApplescriptRunner
23 if ((self = [super init])) {
24 NSDistributedNotificationCenter *distributedNotificationCenter = [NSDistributedNotificationCenter defaultCenter];
25 [distributedNotificationCenter addObserver:self
26 selector:@selector(respondIfReady:)
27 name:@"AdiumApplescriptRunner_RespondIfReady"
30 [distributedNotificationCenter addObserver:self
31 selector:@selector(executeScript:)
32 name:@"AdiumApplescriptRunner_ExecuteScript"
35 [distributedNotificationCenter addObserver:self
36 selector:@selector(quit:)
37 name:@"AdiumApplescriptRunner_Quit"
40 [self applescriptRunnerIsReady];
42 [self resetAutomaticQuitTimer];
49 * @brief Inform observers on the NSDistributedNotificationCenter that the applesript runner is ready
51 - (void)applescriptRunnerIsReady
53 //Check for an existing AdiumApplescriptRunner; if there is one, it will respond with AdiumApplescriptRunnerIsReady
54 [[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"AdiumApplescriptRunner_IsReady"
57 deliverImmediately:NO];
61 * @brief Observer method which responds to the @"AdiumApplescriptRunner_RespondIfReady" distributed notification
63 * This allows simple two-way communicatino from the host application to the daemon without setting up proxy or ports
65 - (void)respondIfReady:(NSNotification *)inNotification
67 [self applescriptRunnerIsReady];
71 * @brief Execute an applescript
73 * @param inNotification An NSNotificatoin whose userInfo NSDictionary has @"funtion", @"arguments", @"path", and @"uniqueID" keys
75 - (void)executeScript:(NSNotification *)inNotification
77 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
79 NSDictionary *userInfo = [inNotification userInfo];
81 NSAppleScript *appleScript;
82 NSAppleEventDescriptor *thisApplication, *containerEvent;
83 NSString *functionName = [userInfo objectForKey:@"function"];
84 NSArray *scriptArgumentArray = [userInfo objectForKey:@"arguments"];
85 NSURL *pathURL = [NSURL fileURLWithPath:[userInfo objectForKey:@"path"]];
86 NSString *resultString = nil;
88 appleScript = [[NSAppleScript alloc] initWithContentsOfURL:pathURL
92 if (functionName && [functionName length]) {
93 /* If we have a functionName (and potentially arguments), we build
94 * an NSAppleEvent to execute the script. */
96 //Get a descriptor for ourself
97 int pid = [[NSProcessInfo processInfo] processIdentifier];
98 thisApplication = [NSAppleEventDescriptor descriptorWithDescriptorType:typeKernelProcessID
102 //Create the container event
104 //We need these constants from the Carbon OpenScripting framework, but we don't actually need Carbon.framework...
105 #define kASAppleScriptSuite 'ascr'
106 #define kASSubroutineEvent 'psbr'
107 #define keyASSubroutineName 'snam'
108 containerEvent = [NSAppleEventDescriptor appleEventWithEventClass:kASAppleScriptSuite
109 eventID:kASSubroutineEvent
110 targetDescriptor:thisApplication
111 returnID:kAutoGenerateReturnID
112 transactionID:kAnyTransactionID];
114 //Set the target function
115 [containerEvent setParamDescriptor:[NSAppleEventDescriptor descriptorWithString:functionName]
116 forKeyword:keyASSubroutineName];
118 //Pass arguments - arguments is expecting an NSArray with only NSString objects
119 if ([scriptArgumentArray count]) {
120 NSAppleEventDescriptor *arguments = [[NSAppleEventDescriptor alloc] initListDescriptor];
121 NSEnumerator *enumerator;
124 enumerator = [scriptArgumentArray objectEnumerator];
125 while ((object = [enumerator nextObject])) {
126 [arguments insertDescriptor:[NSAppleEventDescriptor descriptorWithString:object]
127 atIndex:([arguments numberOfItems] + 1)]; //This +1 seems wrong... but it's not
130 [containerEvent setParamDescriptor:arguments forKeyword:keyDirectObject];
135 resultString = [[appleScript executeAppleEvent:containerEvent error:NULL] stringValue];
138 resultString = [[appleScript executeAndReturnError:NULL] stringValue];
142 //Notify of the script's completion and the result
143 [[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"AdiumApplescript_DidRun"
145 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
146 [userInfo objectForKey:@"uniqueID"], @"uniqueID",
147 (resultString ? resultString : @""), @"resultString",
149 deliverImmediately:NO];
150 [appleScript release];
152 //Reset the automatic quit timer
153 [self resetAutomaticQuitTimer];
159 * @brief Quit, notifying via the NSDistributedNotificationCenter that we're quitting
161 - (void)quit:(NSNotification *)inNotification
163 [NSObject cancelPreviousPerformRequestsWithTarget:self];
165 [[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"AdiumApplescriptRunner_DidQuit"
168 deliverImmediately:YES];
170 [[NSDistributedNotificationCenter defaultCenter] removeObserver:self];
176 * @brief Reset the automatic quit timer, which will exit this program after SECONDS_INACTIVITY_BEFORE_AUTOMATIC_QUIT
178 - (void)resetAutomaticQuitTimer
180 [NSObject cancelPreviousPerformRequestsWithTarget:self
181 selector:@selector(quit:)
183 [self performSelector:@selector(quit:)
185 afterDelay:SECONDS_INACTIVITY_BEFORE_AUTOMATIC_QUIT];
190 int main(int argc, const char *argv[])
192 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
193 AIApplescriptRunner *applescriptRunner;
195 applescriptRunner = [[AIApplescriptRunner alloc] init];
197 [[NSRunLoop currentRunLoop] run];
199 [applescriptRunner quit:nil];
200 [applescriptRunner release];