Run ShellScript Without Authorization Popup for MAC Application - objective-c

I am working on MAC Application in which I want to Remove Helper tool previously installed by my application.
I am using STPrivilegedTask to run my ShellScript.
Here is the code I am using to achieve this.
NSString *pathForUninstallFile = [[NSBundle mainBundle] pathForResource:#"Uninstall" ofType:#"sh"];
STPrivilegedTask *taskToRemoveHelperTool = [STPrivilegedTask launchedPrivilegedTaskWithLaunchPath:pathForUninstallFile arguments:[NSArray arrayWithObjects:#"Uninstall.sh", nil]];
and here is my ShellScript Unistall.sh
launchctl unload /Library/LaunchDaemons/com.bsecure.HelperTool.plist
rm /Library/LaunchDaemons/com.appleCompany.HelperTool.plist
rm /Library/PrivilegedHelperTools/com.appleCompany.HelperTool
security -q authorizationdb remove "com.appleCompany.readLicenseKey"
security -q authorizationdb remove "com.appleCompany.newMethod"
Now the above code works perfect but it ask user's Authorization Popup.
I want to run this script without this Authorization Popup.
I already search in another question but none of them work for me that's why I created this question.

So you want to run a task with escalated privileges without prompting the user for a password? If so, you're out of luck, it can't be done.

Related

how can I remove XPCService by script

I create project for os x application with xpcservices that run by loginItems. that's mean the service is founded in the app in the path:{APP_NAME}.app/Contents/Library/LoginItems and who is responsible for run the service is the the main app:
NSXPCConnection *connection = [[NSXPCConnection alloc] initWithLoginItemName:#"{SERVICE_NAME}.app" error:&error];
I builded uninstaller, for deleting all the files that I installed. and I need to remove the service.
the command I used for it :
launchctl remove SERVICE_NAME
this is works fine if I run it from the terminal,
but if i run it from script it's remove the service from the "Activity Monitor" but the service still in launchctl list with status -5.
so if I install new version its take the previous version that installed.
I run the script from xcode by NSTask. there is no error. it's does what the script should do.
I even set the administrator privileges:
/usr/bin/osascript -e 'do shell script \"SCRIPT_NAME args 2>&1 etc\" with administrator privileges'
Why it's not remove like I did it in the terminal ?
p.s:
its works when run it from Xcode (even the app is in release mode)
To remove correctly the login item you should do it in the code :
pass false in SMLoginItemSetEnabled:
SMLoginItemSetEnabled((__bridge CFStringRef)loginItemBundleId, false)

How to enable FinderSync Extension in macOS System Preferences

I am integrating FinderSync Extension in my Cocoa Application to show badges in files and folders. Look at the below two scenario:
When i run application using FinderSync Extension (like DemoFinderSync) look at the blue popup in the below image, in that case Extension is added in the System Preference with Check mark and called that principal class "FinderSync.m" as well.
When i run application using my Application Scheme (like DemoApp) look at the blue popup in the below image, in that case Extension is added in the System Preference but without check mark and that principal class "FinderSync.m" do not call and FinderSync Extension does not work in this case.
Does anybody have an idea how to enable Finder Extension in the System Preference using second scenario?
Non-debug scheme (#if !DEBUG):
system("pluginkit -e use -i com.domain.my-finder-extension");
When running under debugger give path to your extension directly:
NSString *pluginPath = [[[NSBundle mainBundle] builtInPlugInsPath] stringByAppendingPathComponent:#"My Finder Extension.appex"];
NSString *pluginkitString = [NSString stringWithFormat:#"pluginkit -e use -a \"%#\"", pluginPath];
system([pluginkitString cStringUsingEncoding:NSUTF8StringEncoding]);
Specify this in your applicationDidFinishLaunching method. You should also manually turn this on only once so that if user turned your extension off in the System Preferences you don't turn it on every time your application starts. I set an NSUserDefaults key the first time user launches my app that has the finder sync extension support.
I got the solution:
Code to Enable Extension (bundle ID)
system("pluginkit -e use -i YourAppBundleID")
Code to Disable Extension (bundle ID)
system("pluginkit -e ignore -i YourAppBundleID")
Before i used:
system("pluginkit -e use -i AppBundleID.FinderSync")
so just remove ".FinderSync" its working.
Linking an answer I found on the Apple developer forum:
https://forums.developer.apple.com/thread/77682
When your App is outside the Sandbox, you can use:
Objective-C:
system("pluginkit -e use -i <yourFinderExtensionBundleID>");
Swift:
let pipe = Pipe()
let task = Process()
task.launchPath = "/usr/bin/pluginkit"
task.arguments = ["-e", "use", "-i", "<yourFinderExtensionBundleID>"]
task.standardOutput = pipe
let file = pipe.fileHandleForReading
task.launch()
let result = NSString(data: file.readDataToEndOfFile(), encoding:

Unable to find running application in Cocoa

I am getting the list of running applications in Cocoa with the following code:
for (NSRunningApplication *app in [[NSWorkspace sharedWorkspace] runningApplications]) {
MNSLog(#"%#",[app localizedName]);
}
However an application I started from a terminal session is not appearing in the list ('Terminal' is well appearing). The application was started from the same user which is executing the cocoa code.Is my launched application under Terminal ? And in such a case how can I find its name and arguments ?Running ps in another terminal session show my process properly.
Use an NSTask to execute the ps Shell command. You can check the ps man page to determine which arguments you want to pass it based on the information you want to get back. Use NSPipe and NSFileHandle to get the results from the task.
If you want to do some filtering you can pipe the ps output through grep before your app picks up the result.
For your first question, I think NSWorkspace can only see apps that use the window server so you will only see Terminal, not the executables that it is running internally.
You can use sysctl or ps command to get a list of all BSD processes. Have look at unable to detect application running with another user

Deleting plist file does not reset app on macOS 10.9+

While developing a Cocoa application on 10.9, I have noticed that if I go to ~/Library/Preferences and delete the plist file for my app (to reset it), on the next build-and-run, the app behaves as if the plist file had never been deleted at all.
It took me a long time to track down why this happens and I did not see a question/answer about it on SO, so I'm writing this question and answering it myself to help others.
On 10.9, the system is doing some more robust "caching" of preferences. After deleting the plist file, I fired up Activity Monitor and force-killed the "cfprefsd" process. Be careful: there are multiple processes with this name running and you only want to kill the one running under your own user; do not kill the one running as root.
Doing this seems to flush the preferences cache and on the next run of my app, I get a pristine start-from-scratch launch.
Edit: As reported below, using defaults delete [your bundle identifier] at the command line also appears to eliminate the caching issue. I've had mixed success with this.
I found out that killing the user process cfprefsd will reflush the cache, so your changes will be kept
killall -u $USER cfprefsd
In terminal:
defaults delete com.somecompany.someapp
BTW, I've just released a GUI app that may be more convenient than working with the defaults command:
http://www.tempel.org/PrefsEditor
It works practically the same as Xcode's plist editor, but affects the user's app preferences directly.
To delete all your prefs, you could open your prefs in my Prefs Editor, Select All, then delete them with the Backspace or Delete key, and they're instantly all gone.
However, for this particular task, using defaults delete might still be quicker, especially if you put the command into a text file ending in ".command", and make it executable (with chmod +x). Then you can double click it from the Finder to execute it.

NSTask and Git -- Permissions issues

In my Cocoa application I'm trying to use NSTask to run some basic Git commands. Whenever I run a command that requires permissions (SSH keys) to access a remote (e.g. git push, git pull), it fails with the following error:
Permission denied (publickey). The remote end hung up unexpectedly
Running the same commands from Terminal works just fine, so I'm thinking that this might be an issue with NSTask not setting an environment variable that would be used somewhere in the process of accessing the ssh keys. I tried manually setting the HOME and USER environment variables like this:
[task setEnvironment:[NSDictionary dictionaryWithObjectsAndKeys:NSHomeDirectory(), #"HOME", NSUserName(), #"USER", nil]];
But this has no effect. Is there any particular environment variable I have to set in NSTask for this to work properly?
EDIT: Thanks to Dustin's tip, I got a little bit further in figuring this out. I used the env command to list the environment variables for my current session and I found this:
SSH_AUTH_SOCK=/tmp/launch-DMQopt/Listeners
To test, I copied that path and set it as an environment variable of NSTask and ran the code again, and this time it worked! That said, I'm certain that SSH_AUTH_SOCK changes for each session so I can't just hardcode it. How do I dynamically generate/retrieve this variable?
You could try and follow the tutorial "Wrapping rsync or SSH in an NSTask" (from Ira), which does mention SSH_AUTH_SOCK variable:
Since writing this post I've realised that I omitted an important additional step in setting up the environment variables for the NSTask.
In order to make passwordless key-based authentication work it's necessary to grab the SSH_AUTH_SOCK variable from the user's environment and include this in the NSTask's environment.
So, when setting environment variables for example;
NSTask *task;
NSDictionary *environmentDict = [[NSProcessInfo processInfo] environment];
// Environment variables needed for password based authentication
NSMutableDictionary *env = [NSMutableDictionary dictionaryWithObjectsAndKeys:
#"NONE", #"DISPLAY", askPassPath, #"SSH_ASKPASS",
userName,#"AUTH_USERNAME",
hostName,#"AUTH_HOSTNAME",
nil];
// Environment variable needed for key based authentication
[env setObject:[environmentDict objectForKey:#"SSH_AUTH_SOCK"] forKey:#"SSH_AUTH_SOCK"];
// Setting the task's environment
[task setEnvironment:env];
However, the OP indragie comments:
I had tried this earlier but since it was being invoked with XCode, the SSH_AUTH_SOCK env var. wasn't being passed to it.
Opening the app from Finder corrects this issue.
With askPassPath being the path for the Askpass executable, which is included as part of the application’s main bundle. (In order to do this, find the executable under “Products” in xcode, and then drag it into “Copy Bundle Resources” on the main application’s target.)
// Get the path of the Askpass program, which is
// setup to be included as part of the main application bundle
NSString *askPassPath = [NSBundle pathForResource:#"Askpass"
ofType:#""
inDirectory:[[NSBundle mainBundle] bundlePath]];