* Fixed the objectSpecifier of `AIStatusItem`s to be based on the unique ID rather...
[adiumx.git] / Source / ApplescriptRunner.m
blobd08555255a04f773097fee2cb11364d9d11e5258
1 /*
2  ApplescriptRunner.m
3  Created by Evan Schoenberg on 10/31/2004
4 */
6 #import <Foundation/Foundation.h>
8 /*!
9  * @brief Daemon to run applescripts, optionally with a function name and arguments, and respond over NSDistributedNotificationCenter
10  */
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;
18 @end
20 @implementation AIApplescriptRunner
21 - (id)init
23         if ((self = [super init])) {
24                 NSDistributedNotificationCenter *distributedNotificationCenter = [NSDistributedNotificationCenter defaultCenter];
25                 [distributedNotificationCenter addObserver:self
26                                                                                   selector:@selector(respondIfReady:)
27                                                                                           name:@"AdiumApplescriptRunner_RespondIfReady"
28                                                                                         object:nil];
30                 [distributedNotificationCenter addObserver:self
31                                                                                   selector:@selector(executeScript:)
32                                                                                           name:@"AdiumApplescriptRunner_ExecuteScript"
33                                                                                         object:nil];
35                 [distributedNotificationCenter addObserver:self
36                                                                                   selector:@selector(quit:)
37                                                                                           name:@"AdiumApplescriptRunner_Quit"
38                                                                                         object:nil];
40                 [self applescriptRunnerIsReady];
41                 
42                 [self resetAutomaticQuitTimer];
43         }
45         return self;
48 /*!
49  * @brief Inform observers on the NSDistributedNotificationCenter that the applesript runner is ready
50  */
51 - (void)applescriptRunnerIsReady
53         //Check for an existing AdiumApplescriptRunner; if there is one, it will respond with AdiumApplescriptRunnerIsReady
54         [[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"AdiumApplescriptRunner_IsReady"
55                                                                                                                                    object:nil
56                                                                                                                                  userInfo:nil
57                                                                                                            deliverImmediately:NO];
60 /*!
61  * @brief Observer method which responds to the @"AdiumApplescriptRunner_RespondIfReady" distributed notification
62  *
63  * This allows simple two-way communicatino from the host application to the daemon without setting up proxy or ports
64  */
65 - (void)respondIfReady:(NSNotification *)inNotification
67         [self applescriptRunnerIsReady];
70 /*!
71  * @brief Execute an applescript
72  *
73  * @param inNotification An NSNotificatoin whose userInfo NSDictionary has @"funtion", @"arguments", @"path", and @"uniqueID" keys
74  */
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;
87         
88         appleScript = [[NSAppleScript alloc] initWithContentsOfURL:pathURL
89                                                                                                                  error:NULL];
90         
91         if (appleScript) {
92                 if (functionName && [functionName length]) {
93                         /* If we have a functionName (and potentially arguments), we build
94                         * an NSAppleEvent to execute the script. */
95                         
96                         //Get a descriptor for ourself
97                         int pid = [[NSProcessInfo processInfo] processIdentifier];
98                         thisApplication = [NSAppleEventDescriptor descriptorWithDescriptorType:typeKernelProcessID
99                                                                                                                                                          bytes:&pid
100                                                                                                                                                         length:sizeof(pid)];
101                         
102                         //Create the container event
103                         
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];
113                         
114                         //Set the target function
115                         [containerEvent setParamDescriptor:[NSAppleEventDescriptor descriptorWithString:functionName]
116                                                                         forKeyword:keyASSubroutineName];
117                         
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;
122                                 NSString                                *object;
123                                 
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
128                                 }
130                                 [containerEvent setParamDescriptor:arguments forKeyword:keyDirectObject];
131                                 [arguments release];
132                         }
133                         
134                         //Execute the event
135                         resultString = [[appleScript executeAppleEvent:containerEvent error:NULL] stringValue];
136                         
137                 } else {
138                         resultString = [[appleScript executeAndReturnError:NULL] stringValue];
139                 }
140         }
142         //Notify of the script's completion and the result
143         [[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"AdiumApplescript_DidRun"
144                                                                                                                                    object:nil
145                                                                                                                                  userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
146                                                                                                                                          [userInfo objectForKey:@"uniqueID"], @"uniqueID",
147                                                                                                                                          (resultString ? resultString : @""), @"resultString",
148                                                                                                                                          nil]
149                                                                                                            deliverImmediately:NO];
150         [appleScript release];
152         //Reset the automatic quit timer
153         [self resetAutomaticQuitTimer];
155         [pool release];
159  * @brief Quit, notifying via the NSDistributedNotificationCenter that we're quitting
160  */
161 - (void)quit:(NSNotification *)inNotification
163         [NSObject cancelPreviousPerformRequestsWithTarget:self];
165         [[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"AdiumApplescriptRunner_DidQuit"
166                                                                                                                                    object:nil
167                                                                                                                                  userInfo:nil
168                                                                                                            deliverImmediately:YES];
170         [[NSDistributedNotificationCenter defaultCenter] removeObserver:self];
172         exit(0);
173 }                               
176  * @brief Reset the automatic quit timer, which will exit this program after SECONDS_INACTIVITY_BEFORE_AUTOMATIC_QUIT
177  */
178 - (void)resetAutomaticQuitTimer
180         [NSObject cancelPreviousPerformRequestsWithTarget:self
181                                                                                          selector:@selector(quit:)
182                                                                                            object:nil];
183         [self performSelector:@selector(quit:)
184                            withObject:nil
185                            afterDelay:SECONDS_INACTIVITY_BEFORE_AUTOMATIC_QUIT];
188 @end
190 int main(int argc, const char *argv[])
192     NSAutoreleasePool           *pool = [[NSAutoreleasePool alloc] init];
193         AIApplescriptRunner             *applescriptRunner;
194         
195         applescriptRunner = [[AIApplescriptRunner alloc] init];
197         [[NSRunLoop currentRunLoop] run];
198         
199         [applescriptRunner quit:nil];
200         [applescriptRunner release];
202         [pool release];