Objective-C: How to remove multiple files with admin privileges? - objective-c

I am trying to remove multiple files using apple script (given below) but it is not working and giving the following error:
Expected expression but found unknown token.
Here is my code:
{
///this string will be a contact of all the paths saperated by ' '
NSString* removingLocationInString = #"";
///for loop in order to concat all the string(paths) in one string
for (NSString* str in locations) {
removingLocationInString = [NSString stringWithFormat:#"%# \"%#\"", removingLocationInString, str];
}
///creating the command that will be run from apple script
///e.g. rm "~/user/vikas/desktop/file.txt" "~/user/vikas/desktop/file2.txt"
NSString* command = [NSString stringWithFormat:#"rm %#", removingLocationInString];
[self runScriptAsAdmin:command];
}
-(BOOL)runScriptAsAdmin:(NSString*) fullScript
{
NSTask *task;
task = [[NSTask alloc] init];
[task setLaunchPath: #"/bin/sh"];
NSArray *arguments = [NSArray arrayWithObjects:
#"-c" ,
[NSString stringWithFormat:#"%#", fullScript],
nil];
NSString * output = nil;
NSString * processErrorDescription = nil;
NSDictionary *errorInfo = [NSDictionary new];
NSString *script = [NSString stringWithFormat:#"do shell script \"%#\" with administrator privileges", fullScript];
NSAppleScript *appleScript = [[NSAppleScript new] initWithSource:script];
NSAppleEventDescriptor * eventResult = [appleScript executeAndReturnError:&errorInfo];
// Check errorInfo
if (! eventResult)
{
// Describe common errors
NSString *errorDescription = nil;
if ([errorInfo valueForKey:NSAppleScriptErrorNumber])
{
NSNumber * errorNumber = (NSNumber *)[errorInfo valueForKey:NSAppleScriptErrorNumber];
if ([errorNumber intValue] == -128)
errorDescription = #"The administrator password is required to do this.";
}
// Set error message from provided message
if (errorDescription == nil)
{
if ([errorInfo valueForKey:NSAppleScriptErrorMessage])
errorDescription = (NSString *)[errorInfo valueForKey:NSAppleScriptErrorMessage];
}
return NO;
}
else
{
// Set output to the AppleScript's output
NSString *output = [eventResult stringValue];
return YES;
}
return NO;
}
Here is the script that is being generated
do shell script "rm "/Users/vikas/.Trash/.DS_Store"
"/Users/vikas/.Trash/SimpleCocoaBrowser 2.zip"
"/Users/vikas/.Trash/SimpleCocoaBrowser 3.zip"
"/Users/vikas/.Trash/SimpleCocoaBrowser 4 4.55.07 pm.zip"
"/Users/vikas/.Trash/SimpleCocoaBrowser 4.zip"" with administrator
privileges

In runScriptAsAdmin function add the following line:
/// if there are " in script then then make it \"
fullScript = [fullScript stringByReplacingOccurrencesOfString:#"\"" withString:#"\\\""];
The actual apple script should be as follows:
do shell script "rm \"/Users/vikas/.Trash/SimpleCocoaBrowser 2.zip\"
\"/Users/vikas/.Trash/SimpleCocoaBrowser 3.zip\"
\"/Users/vikas/.Trash/SimpleCocoaBrowser 4.zip\"" with administrator
privileges

Related

run applescript from cocoa app stopped working

This code had been working fine until just recently. I hadn't' changed anything nor upgraded my system and I'm completely flummoxed.
I've been using it for 6 years and now it dies on me.
Is there an easier or better way of running an applescript from within a cocoa application? At this point I'm happy to pay to fix this problem!
utils.h
#import <Foundation/Foundation.h>
#interface Utils : NSObject
// Runs an applescript with a given map of variables (name/value)
+ (NSArray *)runApplescript:(NSString *)source withVariables:(NSDictionary *)variables;
// Runs an applescript from a file pathwith a given map of variables
// (name/value)
+ (NSArray *)runApplescriptFromFile:(NSString *)scriptName withVariables:(NSDictionary *)variables;
+ (NSArray *)arrayFromDescriptor:(NSAppleEventDescriptor *)descriptor;
// String is empty or only has white characters (space, tab...)
+ (BOOL)stringIsEmptyOrWhite:(NSString *)string;
#end
Utils.M
#import "Utils.h"
#implementation Utils
+ (NSArray *)arrayFromDescriptor:(NSAppleEventDescriptor *)descriptor {
// Enumerate the apple descriptors (lists) returned by the applescript and
// make them into arrays
NSMutableArray *returnArray = [NSMutableArray array];
NSInteger counter, count = [descriptor numberOfItems];
for (counter = 1; counter <= count; counter++) {
NSAppleEventDescriptor *desc = [descriptor descriptorAtIndex:counter];
if (nil != [desc descriptorAtIndex:1]) {
[returnArray addObject:[Utils arrayFromDescriptor:desc]];
} else {
NSString *stringValue = [[descriptor descriptorAtIndex:counter] stringValue];
if (nil != stringValue) {
[returnArray addObject:stringValue];
} else {
[returnArray addObject:#""];
}
}
}
return returnArray;
}
+ (NSString *)escapeCharacters:(NSString *)string {
return [string stringByReplacingOccurrencesOfString:#"\"" withString:#"\\\""];
}
+ (NSArray *)runApplescript:(NSString *)source withVariables:(NSDictionary *)variables {
NSString *input = #"";
NSArray *variableNames = [variables allKeys];
// Transform the dictionary of names/values to set sentences of applescript
for (NSString *variableName in variableNames) {
NSObject *variableValue = [variables objectForKey:variableName];
if ([variableValue isKindOfClass:[NSString class]]) {
input =
[input stringByAppendingString:[NSString stringWithFormat:#"set %# to (\"%#\" as text)\n", variableName,
[Utils escapeCharacters:variableValue], nil]];
} else if ([variableValue isKindOfClass:[NSNumber class]]) {
input = [input stringByAppendingString:[NSString stringWithFormat:#"set %# to (%# as integer)\n",
variableName, variableValue, nil]];
} else if ([variableValue isKindOfClass:[NSArray class]]) {
// Initialize a list
NSString *entry;
NSArray *values = (NSArray *)variableValue;
input = [input stringByAppendingString:[NSString stringWithFormat:#"set %# to {", variableName]];
BOOL first = TRUE;
for (entry in values) {
if (!first) {
input = [input stringByAppendingString:#", "];
}
input = [input
stringByAppendingString:[NSString stringWithFormat:#"\"%#\"", [Utils escapeCharacters:entry], nil]];
first = FALSE;
}
input = [input stringByAppendingString:#"}\n"];
}
}
NSString *finalScript = [input stringByAppendingString:[NSString stringWithFormat:#"\n\n%#", source]];
NSLog(#"Final script: %#", finalScript);
NSAppleScript *script = [[NSAppleScript alloc] initWithSource:finalScript];
NSDictionary *error;
NSAppleEventDescriptor *descriptor = [script executeAndReturnError:&error];
NSLog(#"applescript error: %#", [error description]);
// Transform the return value of applescript to nested nsarrays
return [Utils arrayFromDescriptor:descriptor];
}
+ (NSArray *)runApplescriptFromFile:(NSString *)scriptName withVariables:(NSDictionary *)variables {
NSString *scriptPath = [[NSBundle mainBundle] pathForResource:scriptName ofType:#"applescript"];
NSString *scriptSource =
[[NSString alloc] initWithContentsOfFile:scriptPath encoding:NSASCIIStringEncoding error:nil];
return [Utils runApplescript:scriptSource withVariables:variables];
}
+ (BOOL)stringIsEmptyOrWhite:(NSString *)string {
string = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
return [string isEqualToString:#""];
}
#end
Easier, yes, although whether that’s your actual problem is another question.
http://appscript.sourceforge.net/asoc.html
I assume you’ve already got other details, including sandboxing and hardening settings and plist entries, taken care of. (Recent Xcode upgrades also had a habit of breaking it when auto-upgrading your project files, by turning on hardening for you so Apple events can’t get out.)

How can I search any volume in macOS?

- (void)readFolder:(NSString *)str :(NSMutableDictionary *)dict {
NSArray *appFolderContents = [[NSArray alloc] init];
appFolderContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:str error:nil];
for (NSString *app in appFolderContents) {
if ([app containsString:#".app"])
{
NSString *appName = [[app lastPathComponent] stringByDeletingPathExtension];
NSString *appPath = [NSString stringWithFormat:#"%#/%#", str, app];
NSString *appBundle = [[NSBundle bundleWithPath:appPath] bundleIdentifier];
// NSLog(#"%# -- %#", appPath, appBundle);
NSArray *jumboTron = [NSArray arrayWithObjects:appName, appPath, appBundle, nil];
[dict setObject:jumboTron forKey:appName];
}
}
}
//This searches for apps
- (void)getAPPList {
NSMutableDictionary *myDict = [[NSMutableDictionary alloc] init];
[self readFolder:#"/Applications" :myDict];
[self readFolder:#"/Applications/Utilities" :myDict];
[self readFolder:#"/System/Library/CoreServices" :myDict];
[self readFolder:#"/Volumes/Macintosh HD/Applications" :myDict ];
// Volumes not named 'Macintosh HD' doesn't work, I'm trying to make it work
[self readFolder:#"/Volumes/*/Applications" :myDict ];
//Some apps are stored in the user's Application folder instead of the main one
[self readFolder:[NSString stringWithFormat:#"%#/Applications", NSHomeDirectory()] :myDict];
//Sometimes people keep apps in Downloads
[self readFolder:[NSString stringWithFormat:#"%#/Downloads", NSHomeDirectory()] :myDict];
//Some apps are stored in the user's Library/Application Support sometimes
[self readFolder:[NSString stringWithFormat:#"%#/Library/Application Support", NSHomeDirectory()] :myDict];
I'm trying to make line 26 ([self readFolder:#"/Volumes/*/Applications" :myDict ]) search all volumes, instead of only searching a volume with a matching/specific name. How can I do this?
I'm using Xcode 9.2
something like this should do the trick (untested)
NSArray *keys = [NSArray arrayWithObjects:NSURLVolumeURLKey, NSURLIsVolumeKey, nil];
NSArray *volumeUrls = [[NSFileManager defaultManager] mountedVolumeURLsIncludingResourceValuesForKeys:keys options:NSVolumeEnumerationSkipHiddenVolumes];
for (NSURL *volumeUrl in volumeUrls)
{
BOOL mayBeBootVolume = NO;
NSString* pathToVolume = [volumeUrl path];
[self readFolder: [pathToVolume stringByAppendingString: #"/Applications"];
}

NSException When repeating NSTask

I have this code that is suppose to check certain settings and log the results. The first setting check works correctly but the next one fails. Here is the code block:
//init classes
CocoaLogging *logFile = [[CocoaLogging alloc]init];
NSString * result;
NSInteger * state;
NSPipe *pipe=[[NSPipe alloc] init];
NSFileHandle *handle;
NSString * cmd = [NSString stringWithFormat:#"%#", #"/usr/bin/defaults"];
//Start logging
NSString *logText;
NSString *logPath;
BOOL logSuccess;
NSLog(#"Start Settings Enforcer");
logPath = [NSString stringWithFormat:#"%#%#", NSHomeDirectory(), LOGFILE_PATH];
if (! [[NSFileManager defaultManager] fileExistsAtPath:logPath isDirectory:NO])
{
logSuccess = [logFile createLogFile:logPath];
if (logSuccess)
{
logText = [NSString stringWithFormat:#"%#", #"Start Settings Enforcer"];
[logFile makeLogEntry:logText to:logPath];
}
}
else
{
logPath = [NSString stringWithFormat:#"%#%#", NSHomeDirectory(), LOGFILE_PATH];
logText = [NSString stringWithFormat:#"%#", #"Start Settings Enforcer"];
logSuccess = [logFile makeLogEntry:logText to:logPath];
}
//check status of firewall
//defaults read "/Library/Preferences/com.apple.alf" globalstate
//array of commandline args
NSMutableArray *firewallArgs = [[NSMutableArray alloc]initWithObjects:#"-currentHost", #"read", #"/Library/Preferences/com.apple.alf", #"globalstate", nil];
//init the task
NSTask *firewall=[[NSTask alloc] init];
//define the command to run
[firewall setLaunchPath:cmd];
[firewall setArguments:firewallArgs];
[firewall setStandardOutput:pipe];
handle=[pipe fileHandleForReading];
//run the command
[firewall launch];
// convert NSData -> NSString
result = [[NSString alloc] initWithData:[handle readDataToEndOfFile]encoding:NSASCIIStringEncoding];
state = [result intValue];
NSLog(#"%#", result);
if (state == 1)
{
NSLog(#"firewall is on");
logText = [NSString stringWithFormat:#"%#", #"firewall is on"];
logSuccess = [logFile makeLogEntry:logText to:logPath];
}
else
{
NSLog(#"firewall is off");
logText = [NSString stringWithFormat:#"%#", #"firewall is off"];
logSuccess = [logFile makeLogEntry:logText to:logPath];
}
[firewallArgs removeAllObjects];
[handle closeFile];
[firewall terminate];
//check screensaver on
//defaults read ~/Library/Preferences/com.apple.screensaver askForPassword
NSString * plistPath = [NSString stringWithFormat:#"%#", #"~/Library/Preferences/com.apple.screensaver"];
plistPath = [plistPath stringByExpandingTildeInPath];
//array of commandline args
NSMutableArray *ssOnArgs = [[NSMutableArray alloc]initWithObjects:#"read", plistPath, #"askForPassword", nil];
//init the task
NSTask *ssOn=[[NSTask alloc] init];
//define the command to run
[ssOn setLaunchPath:cmd];
[ssOn setArguments:ssOnArgs];
[ssOn setStandardOutput:pipe];
handle=[pipe fileHandleForReading];
//run the command
[ssOn launch];
// convert NSData -> NSString
result = [[NSString alloc] initWithData:[handle readDataToEndOfFile]encoding:NSASCIIStringEncoding];
state = [result intValue];
if (state == 1)
{
NSLog(#"screensaver is on");
logText = [NSString stringWithFormat:#"%#", #"screensaver is on"];
logSuccess = [logFile makeLogEntry:logText to:logPath];
}
else
{
NSLog(#"screensaver is off");
logText = [NSString stringWithFormat:#"%#", #"screensaver is off"];
logSuccess = [logFile makeLogEntry:logText to:logPath];
}
NSLog(#"Check-in complete");
logText = [NSString stringWithFormat:#"%#", #"Check-in complete."];
logSuccess = [logFile makeLogEntry:logText to:logPath];
}
return 0;
}
Here is the error:
2014-03-31 12:58:11.630 SettingsEnforcer[5379:303] * Terminating app due to uncaught exception 'NSFileHandleOperationException', reason: ' -[NSConcreteFileHandle fileDescriptor]: No such process'
** First throw call stack:
(
0 CoreFoundation 0x00007fff8ce4825c __exceptionPreprocess + 172
1 libobjc.A.dylib 0x00007fff8b075e75 objc_exception_throw + 43
2 CoreFoundation 0x00007fff8ce4810c +[NSException raise:format:] + 204
3 Foundation 0x00007fff8ba9a29d -[NSConcreteFileHandle fileDescriptor] + 30
4 Foundation 0x00007fff8bb2fb97 -[NSConcreteTask launchWithDictionary:] + 2114
5 SettingsEnforcer 0x0000000100001e1e main + 2126
6 libdyld.dylib 0x00007fff91e555fd start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
I hope someone smarter than I can spot the error.
You are recycling the NSPipe instance. Create a new pipe for the second task.

Issue with NSString memory management

I have developed in C but am quite new to Objective-C and iPhone app development. I am working on an app that needs to strip the punctuation off a string. The function works but when I analyse the code it flags up some issues around one of the NSstrings I am using.
I don't understand why and therefore don't know how to fix it.
The code for the main function along with the analyser warning is:
- (IBAction)doIt {
NSString *start_punct = [[NSString alloc] init];
NSString *end_punct = [[NSString alloc] init];
NSString *actual_word = [[NSString alloc] init];
outputTextTextView.text = translatedText; //potential leak of an object alloctated on line xx and stored into 'actual word'
[translatedText release]; translatedText = nil;
[start_punct release]; start_punct = nil; //incorrect decrement of reference count of an object that is not owned at this point by the caller
[end_punct release]; end_punct = nil;
[actual_word release]; actual_word = nil; //this causes a crash
start_punct = [MainViewController getStartPunct:word start:&start_range_start len:&start_range_len];
end_punct = [MainViewController getEndPunct:word start:&end_range_start len:&end_range_len];
actual_word = [word substringWithRange: NSMakeRange(start_range_start,(end_range_start-start_range_start)+1)];
}
The code for the getStartPunct and getEndPunct functions is below
+(NSString*) getStartPunct:(NSString*) inputString
start:(NSInteger*)rangeStart
len:(NSInteger*)length {
NSString* start_str = nil;
NSRange firstAlphanumCharFromStart = [inputString rangeOfCharacterFromSet:[NSCharacterSet alphanumericCharacterSet]];
if (firstAlphanumCharFromStart.location != NSNotFound) {
start_str = [inputString substringWithRange: NSMakeRange(0, firstAlphanumCharFromStart.location)];
*length = firstAlphanumCharFromStart.length;
*rangeStart = firstAlphanumCharFromStart.location;
} //if
if (start_str == nil) {
*length=0;
*rangeStart=0;
}
return start_str;
} //getStartPunct
+(NSString*) getEndPunct:(NSString*) inputString
start:(NSInteger*)rangeStart
len:(NSInteger*)length {
NSString* end_str = nil;
NSInteger rnge = inputString.length;
NSCharacterSet* CS = [NSCharacterSet alphanumericCharacterSet];
NSRange firstNonAlphanumCharFromEnd = [inputString rangeOfCharacterFromSet:CS options:NSBackwardsSearch];
if (firstNonAlphanumCharFromEnd.location != NSNotFound) {
end_str = [inputString substringWithRange: NSMakeRange(firstNonAlphanumCharFromEnd.location+1, rnge - firstNonAlphanumCharFromEnd.location-1)];
*length = firstNonAlphanumCharFromEnd.length;
*rangeStart = firstNonAlphanumCharFromEnd.location;
} //if
if (end_str == nil) {
*length=0;
*rangeStart=0;
}
return end_str;
} //getEndPunct
Can someone see what the issue is? I'm sure it is something very basic..
Many Thanks in advance!
Thanks for all the responses so far.
adpalumbo you are right, I had paste the elements in the wrong order. The correct order is below and I have changed the initialization as suggested by Alex Nichol.
This has fixed 1 of the warning but the others (as shown below) still remain and I don't understand why 'start_punct' and 'end_punct' are behaving differently
- (IBAction)doIt {
NSString *start_punct = nil;
NSString *end_punct = nil;
NSString *actual_word = nil;
start_punct = [MainViewController getStartPunct:word start:&start_range_start len:&start_range_len]; // method returns objective with +0 retain count
end_punct = [MainViewController getEndPunct:word start:&end_range_start len:&end_range_len];
actual_word = [word substringWithRange: NSMakeRange(start_range_start,(end_range_start-start_range_start)+1)];
[translatedText release]; translatedText = nil;
[start_punct release]; start_punct = nil; //incorrect decrement of reference count
[end_punct release]; end_punct = nil;
//[actual_word release]; actual_word = nil; //possible abend
}

sudo auth session while executing command using NSTask in cocoa application

I have an application that requires executing a command with sudo. How can I ask for a password and if successful, then run the sudo command using NSTask.
If you're looking for a more lightweight solution, there is another way. I wrote this generic implementation which should achieve what you want:
- (BOOL) runProcessAsAdministrator:(NSString*)scriptPath
withArguments:(NSArray *)arguments
output:(NSString **)output
errorDescription:(NSString **)errorDescription {
NSString * allArgs = [arguments componentsJoinedByString:#" "];
NSString * fullScript = [NSString stringWithFormat:#"%# %#", scriptPath, allArgs];
NSDictionary *errorInfo = [NSDictionary new];
NSString *script = [NSString stringWithFormat:#"do shell script \"%#\" with administrator privileges", fullScript];
NSAppleScript *appleScript = [[NSAppleScript new] initWithSource:script];
NSAppleEventDescriptor * eventResult = [appleScript executeAndReturnError:&errorInfo];
// Check errorInfo
if (! eventResult)
{
// Describe common errors
*errorDescription = nil;
if ([errorInfo valueForKey:NSAppleScriptErrorNumber])
{
NSNumber * errorNumber = (NSNumber *)[errorInfo valueForKey:NSAppleScriptErrorNumber];
if ([errorNumber intValue] == -128)
*errorDescription = #"The administrator password is required to do this.";
}
// Set error message from provided message
if (*errorDescription == nil)
{
if ([errorInfo valueForKey:NSAppleScriptErrorMessage])
*errorDescription = (NSString *)[errorInfo valueForKey:NSAppleScriptErrorMessage];
}
return NO;
}
else
{
// Set output to the AppleScript's output
*output = [eventResult stringValue];
return YES;
}
}
Usage example:
NSString * output = nil;
NSString * processErrorDescription = nil;
BOOL success = [self runProcessAsAdministrator:#"/usr/bin/id"
withArguments:[NSArray arrayWithObjects:#"-un", nil]
output:&output
errorDescription:&processErrorDescription
asAdministrator:YES];
if (!success) // Process failed to run
{
// ...look at errorDescription
}
else
{
// ...process output
}
Hat tip to user950473.
Use the Authorization Services, Luke. (If you've ever seen the "Application XYZ needs an admin password to continue", this is how it's implemented. It does not use sudo under the sheets.)
http://developer.apple.com/library/mac/#documentation/Security/Conceptual/authorization_concepts/01introduction/introduction.html