potionfactory / letsmove Goto Github PK
View Code? Open in Web Editor NEWA sample that shows how to move a running Mac application to the /Applications directory
Home Page: http://www.potionfactory.com/blog/2009/09/17/move-applications-folder
A sample that shows how to move a running Mac application to the /Applications directory
Home Page: http://www.potionfactory.com/blog/2009/09/17/move-applications-folder
Got the code, launched it with XCODE and got the following error:
--->>> error: cannot find protocol declaration for 'NSApplicationDelegate'
for line:
--->>> @interface LetsMoveAppDelegate : NSObject {
NSWindow *window;
}
I'd like to create a spec to allow easy integration of the code using CocoaPods. However, to do this cleanly, the repository needs to have version tags.
Could you push tags (at least) for the latest version?
Sorry to dump these but I figured it was better than not sharing at all.
ContainingDiskImageDevice, ShellQuotedString, DeleteOrTrash and the various NSTask invocations should be everything, or there's little enough code that you can probably just diff PFMoveApplication.m.
nriley/Pester@6457a9e
(Of course, the OS compatibility/strings changes are ones you won't want.)
Using NSFileManager operations as a standard user (not administrator) will fail to copy into /Applications. I believe the right thing to do here is to use an Authorization, but it's a bit of a pain as you have to do a lot of work and spawn a separate process that does the privileged operation. Any thoughts on a better way to handle this? If not I'd be happy to write a patch that uses an Authorization.
The alert can't be visible, if the application is in full screen mode at the beginning. It can be closed with ESC or Enter key, but I think the result does not meet expectations.
A comment here (https://github.com/potionfactory/LetsMove/blob/master/PFMoveApplication.m#L481-L485) says "AuthorizationExecuteWithPrivileges is deprecated. We want to still use it since there's no good alternative (without requiring code signing)." Does anyone know if there is any alternative?
In my application, I need to add a wireless network to the top of the preferred networks list and later remove it. For this I can use this function: https://developer.apple.com/documentation/securityfoundation/sfauthorization/1417652-obtainwithright. But then the password is still in the user's Keychain, and I'd like to clean that up as well. There is another function, CWKeychainDeleteWiFiPassword, but it does not have a parameter to pass in an authorization, and so unless you're running it as root, you can't delete from the system Keychain. I don't want to use SMJobBless as I don't want to install privileged helpers (this is a standalone utility that should not require installation), and I don't want to pay Apple $99/year just to allow a user to delete a password. Thanks for any help you can provide.
Please #import Cocoa or AppKit, or the library does not compile without a precompiled header in place that includes one of these.
Hi!
I just wanted to let you know if you download LetsMove (from the download button) you get an older version. (it only has 2 localizations).
Apart from that, great code pal!
Please add this library to CocoaPods so it's easier to integrate it into projects.
Just to make it clearer, could you please include a screenshot at the top that demonstrates the behavior this project will create.
Image to use is at http://www.potionfactory.com/node/251
The -r flag should not be present when calling xattr on 10.5. Currently on 10.5 the call is doing nothing, because xattr is balking at the -r flag.
I'm guessing this is simply a mistake and the code has it is the wrong way round, setting -r for 10.5 and not for 10.6, when it should be vice versa.
This does, however, raise the question of why -r would be needed on 10.6, since it's not being used for 10.6 at the moment and things seem to be working?
The framework copies the app to /Applications but doesn't remove it from ~/Downloads.
When an app is running with the attribute "com.apple.quarantine" (meaning downloaded from the Internet) it's running in so called translocation mode and the current workaround for this - trashing the app via AppleScript using Finder. Well, it no longer works on macOS Big Sur and Monterey obviously:
Trash AppleScript error: {
NSAppleScriptErrorAppName = Finder;
NSAppleScriptErrorBriefMessage = "Not authorized to send Apple events to Finder.";
NSAppleScriptErrorMessage = "Not authorized to send Apple events to Finder.";
NSAppleScriptErrorNumber = "-1743";
NSAppleScriptErrorRange = "NSRange: {267, 70}";
There's a solution but as a result a user must allow to control Finder (which is not cool unfortunately 😢 ):
You will need to:
<key>NSAppleEventsUsageDescription</key>
<string>Please allow the app to move itself via Apple Script.</string>
On some machine/macOS versions, our app would crash with
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'NSApp with wrong _running count'
after the user clicked the 'do not move' button.
We avoided the crash by calling PFMoveToApplicationsFolderIfNecessary
inside of application *DID* FinishLaunching
instead of application *WILL* FinishLaunching
.
As outlined at http://weblog.rogueamoeba.com/2016/06/29/sierra-and-gatekeeper-path-randomization/, macOS Sierra introduces a new security feature called "Gatekeeper Path Randomization" (or "app translocation", as it's called on the API level). The basic gist is that if you download and run a Gatekeeper app from the Downloads folder, the OS will copy the app into a read-only disk image and run it from there instead. See the link above for more details.
This applies until the user moves the app to any other location (not just the Applications folder), after which the OS will just run the app normally. However, the move is only recognized if performed using the Finder. If you move the app another way, e.g. using "mv" in the Terminal, the app will continue to be translocated when run, even if it's in /Applications.
I've only gotten a little chance to fool around with this on the WWDC seed, but it does appear that the move performed by PFMoveApplication using NSFileManager doesn't disable translocation. So, the app will be moved successfully, but will still be run translocated, even when launching from /Applications. This will also cause PFMoveApplication to prompt the user to move the app a second time, since the app is in fact not being run from /Applications, but rather from its translocated read-only disk image.
I haven't had a chance to try any of this yet, but a new header in the 10.12 SDK at <Security/SecTranslocate.h> contains an API named SecTranslocateURLShouldRunTranslocated() which outlines the circumstances under which an app should be run as translocated. It reads:
@discussion The policy is as follows: 1. If path is already on a nullfs mountpoint - no translocation 2. No quarantine attributes - no translocation 3. If QTN_FLAG_DO_NOT_TRANSLOCATE is set or QTN_FLAG_TRANSLOCATE is not set - no translocations 4. Otherwise, if QTN_FLAG_TRANSLOCATE is set - translocation
So, I think what the Finder is doing is to set that QTN_FLAG_DO_NOT_TRANSLOCATE flag in the quarantine attributes for the file when the user moves it to a different location. I did some quick digging with xattr in the Terminal, and I do see a bit getting flipped in the com.apple.quarantine attribute when moving a downloaded app on Sierra. Assuming this is correct, then this should be fairly simple for PFMoveApplication to set that same quarantine attribute when it performs its move as well.
It might also be worth adding some logic to check and see whether the app is running from a translocated location (there is other API in SecTranslocate.h that lets you detect this). If it's already being translocated from /Applications, then instead of needing to recopy the entire app, we could potentially just set the quarantine flag on the existing app, so that it doesn't get translocated anymore.
if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_4) {
in IsInDownloadsFolder()
has two problems:
it will match 10.4.x releases while it looks like it should only match on 10_5. It should probably be: floor(NSAppKitVersionNumber) >= NSAppKitVersionNumber10_5
NSAppKitVersionNumber10_4 is only defined in 10.5 SDK (and NSAppKitVersionNumber10_5 only in 10.6 SDK) so you'll need to introduce private copy for 10.4 SDK anyway with sth. like:
// only defined in SDK 10.6
Not pretty, but works.
Your sample project seems working fine. But when I create a new project and add your components. The main window will appear along with your window. I'm using the latest version of xcode and OS X. Could you please tell me that what I should I do? Thanks.
I accidentally noticed this problem when I tried to use my app to open an image created but not yet saved by Slicy app. (I hit Space bar in Slicy to view the image in QuickLook, then pressed the "Open with MyApp" button to launch my app.)
The path appeared to be the following:
/private/var/folders/zz/tgcc5k_17cscxxwb212fydw80000gn/T/CB856778-0148-4C5F-B15C-839B6EF77469/SNPlaceholder.png
My app uses your -[NSString stringByResolvingSymlinksAndAliases] to resolve possible symlink in the path before opening the file, however, this causes an endless while loop in -[NSString stringByIterativelyResolvingSymlinkOrAlias].
I'm not sure how to fix it myself. Your help would be greatly appreciated!
I tried using LetsMove in my sandboxed app, and I kept getting errAuthorizationDenied
when requesting the authorization to do the move. It turns out that according to Apple's Sandbox Design guide, sandboxed apps can't use Authorization Services.
A note at the top of README.md could save people some time.
(Alternately, you could detect if the app is sandboxed, and show an alert advising the user to quit and move the app manually.)
If you have an old instance of your program running from the Applications folder
and people launch a new instance (let's say from the Downloads folder) the moving will fail silently as it cannot replace the already running application.
What the system does when you e.g. drag and drop the new one into the Applications folder is show you a Warning message:
"The operation can’t be completed because the item “XYZ.app” is in use."
I think LetsMove needs a customizable warning message where we can say "Close the old version of XYZ so we can replace it in your Applications folder."
Hi,
could you make a new point release with that last commit? That would let me remove some hack from the Podfile that renames the lproj directory names locally.
// Path where we would expect a Mac App Sore receipt.
NSString *destinationMASReceiptPath = [NSString stringWithFormat:@"%@/Contents/_MASReceipt/receipt",destinationPath];
BOOL needMASAuthorization = [fm fileExistsAtPath:destinationMASReceiptPath isDirectory:FALSE];
// Check if we need admin password to write to the Applications directory
BOOL needAuthorization = ([fm isWritableFileAtPath:applicationsDirectory] == NO);
if(needMASAuthorization)needAuthorization=TRUE;
Hello! Hoping somebody might have an idea about why LetsMove is crashing in my open-source Mac app SelfControl on the High Sierra public beta (just tried to add it last week).
The behavior I’m seeing:
I didn’t do anything special with the integration - just the one-line call. You can see the full diff (+6 lines) of the installation here.
I determined that the issue is the AppleScript that gets run on line 390 of PFMoveApplication.m to trash the app. On the LetsMove sample app, it runs successfully. But in SelfControl, despite having basically identical parameters (the app is in the /AppTranslocation/
folder in both cases), running that AppleScript crashes the app and it never gets further. I also see an error logged in the console that isn’t logged for LetsMove: OSErr AERemoveEventHandler(AEEventClass, AEEventID, AEEventHandlerUPP, Boolean)(spec,phac handler=0x7fff8fde98ee isSys=YES) err=0/noErr
Any ideas what this could be? I’ll keep trying to debug but I’m not sure where to go from here.
Hi, I would like to add a Swedish translation of LetsMove. Where do I start?
A user filed this issue report against my app:
https://gitlab.com/gnachman/iterm2/-/issues/11033
I am able to reproduce it. Here are the exact steps:
~/Downloads/app.zip
.cd /Applications; sudo unzip -x ~/Downloads/app.zip
/Applications
.I added some logging and here's what's happening:
bundlePath
is /private/var/folders/jk/2nklbh0x5ksc192jsm9nhnhc0000gn/T/AppTranslocation/F5172787-71B6-4B5D-A94D-E50E9670D998/d/iTerm2.app
IsInApplicationsFolder()
returns false
destinationPath
is /Applications/iTerm2.app
needAuthorziation
is YES
(destination folder exists and is not writable)
LetsMove attempts to trash /private/var/folders/jk/2nklbh0x5ksc192jsm9nhnhc0000gn/T/AppTranslocation/F5172787-71B6-4B5D-A94D-E50E9670D998/d/iTerm2.app
next.
All the methods fail, ending with the following applescript error dictionary:
{
NSAppleScriptErrorAppName = Finder;
NSAppleScriptErrorBriefMessage = "Not authorized to send Apple events to Finder.";
NSAppleScriptErrorMessage = "Not authorized to send Apple events to Finder.";
NSAppleScriptErrorNumber = "-1743";
NSAppleScriptErrorRange = "NSRange: {200, 39}";
}
But it succeeded enough to leave behind an empty folder.
% ls -laR /Applications/iTerm2.app/
total 0
0 drwxr-xr-x@ 2 root admin 64 Aug 6 10:57 ./
0 drwxrwxr-x 110 root admin 3520 Aug 6 10:57 ../
I don't think it'll be possible to detect that /Applications/myapp is the same as the translocation folder (maybe inode numbers would match? But I wouldn't count on it). One possible fix would be to rename the destination, copy the app over, and then delete the renamed folder. That has the downside that a failed move would leave behind a randomly named folder in /Applications.
Maybe someone on the project will have a better idea. I'm happy to send a PR once we have a plan in place.
Could this library perhaps be released under the CC0 or MIT or similar license? IIRC with just a plain 'public domain' notice this may limit this library's use in projects developed or distributed in certain jurisdictions.
Add the files to a project using ARC, try to compile. Numerous autorelease errors.
I tried adding this into a document based application, but it means I get 2 alerts after relaunch, as it tries to open both the arguments as documents. I don't see anywhere where you use the arguments (app path and old pid). I can easily just remove the arguments but I thought you might like to know about the issue.
In my testing, the application is no longer the active application after it relaunches. The best I can figure right now is that this is because it starts running while the old application is running, and that when the old copy resigns its active status the next application down the stack takes focus. A simple hack to fix this is to have the new application wait a couple seconds before launching, like this:
[NSTask launchedTaskWithLaunchPath:@"/bin/bash" arguments:[NSArray arrayWithObjects:@"-c", [NSString stringWithFormat:@"/bin/sleep 2 ; /usr/bin/open "%@"", destinationPath], nil]];
That's a bit of hack though, as it just assumes that 2 seconds is enough for the previous app to finish. Something more correct would likely check for the previous pid still existing in a loop before launching the new copy.
I'm not sure if there's any interest to look into that given the retro compat argument, but here it goes, feel free to close this as necessary..
NSString+SymlinksAndAliases.m:214:7: warning: 'CFURLGetFSRef' is deprecated: first deprecated in OS X 10.9 [-Wdeprecated-declarations]
if (CFURLGetFSRef(url, &fsRef))
NSString+SymlinksAndAliases.m:217:16: warning: 'FSResolveAliasFileWithMountFlags' is deprecated: first deprecated in OS X 10.8 [-Wdeprecated-declarations]
OSErr err = FSResolveAliasFileWithMountFlags(
NSString+SymlinksAndAliases.m:221:28: warning: 'CFURLCreateFromFSRef' is deprecated: first deprecated in OS X 10.9 [-Wdeprecated-declarations]
CFURLRef resolvedUrl = CFURLCreateFromFSRef(kCFAllocatorDefault, &fsRef);
I tried converting the code to ARC compatible one, it worked for the most part, but it broke on clang error.
The project still has code and references to the old SDKs:
This causes this warning:
libarclite_macosx.a(arclite.o)) was built for newer OSX version (10.6) than being linked (10.5)
I thought this commit is supposed to drop anything below 10.6. I could make a PR that removes obsolete settings and code.
Using the Lets Move Test app I noticed this that I got this alert:
I noticed that this line:
NSArray *userApplicationsDirs = NSSearchPathForDirectoriesInDomains(NSApplicationDirectory, NSUserDomainMask, YES);
produces an array like similar to this:
Passing NO
instead of YES
gives you this:
And therefore, the installing in /Applications/
... instead of /Users/username/Applications
I'm guessing this is a mistake or am I'm missing something here?
This is on 10.13, but I'm not sure OS version is important
pid = wait(&status);
returns immediately with EINTR
checking for EINTR and repeating wait seems to fix it
do {
pid = wait(&status);
} while (pid == -1 && errno == EINTR);
Hi,
I get the following warning in Xcode 4 when building a project with PFMoveApplication:
warning: no previous prototype for function 'PFMoveToApplicationsFolderIfNecessary' [-Wmissing-prototypes,2]
There are a few apparent fixes:
(1) add "void" as the function's parameter as per http://stackoverflow.com/a/7764651/111870
(2) disable -Wmissing-prototypes
I /could/ do #2, but I -Wmissing-prototypes is pretty standard, and I think it would be reasonable to add the void parameter instead. Thoughts?
Thanks!
Ari
It would be great if a few details could be controlled by settings in the application's plist. Here are my suggestions:
false
. If set to true it disables the options "Do Not Move" and "Do not show this message again" and has a "Cancel" button in place of "Do Not Move" which closes the application.auto
| always
| never
): Defaults to auto
.
auto
: current behaviour.never
: will always install to /Applications
.always
: will always install to ~/Applications
(providing this exists).For LMUserDirectory another option might be a simple boolean which disables the user directory and forces install to /Applications
. This would suffice for my use-case.
Would you be willing to accept a PR with the above? I have a requirement for them and rather than create a fork it would be awesome if others could also benefit from this.
Hello. The readme mentions that as a part of the setup I am supposed to link my application against Security.framework. I looked this up but didn't find any information about this. Is this still needed or is it outdated? Thanks in advance
The linked and embedded framework 'LetsMove.framework' is missing one or more architectures required by this target: arm64.
Just a note that the above is received when trying to build a Universal app in Xcode 12 beta with this framework.
Logging an issue for others and my projects.
My latest commit at my fork enables support for nested applications, I have submitted a pull request for all commits except this one, I left this commit out because I don't know if you approve of the changes.
I made the changes because I have an app (1) which contains app (2), which can be launched from within app (1), similar to Performance Tools of Xcode. I don't want app (2) to be deleted in app (1) when moved. I added a new function, hope you like the name "IsApplicationAtPathNested". Tested the code. Comments welcome!
Example Directory Structure (Note the fact that there is a component named Applications!):
APP1.app/Contents/Applications/APP2.app
First off, great little library you've created here! Thanks!
I was just wondering if it's possible to make LetsMove create the bundle with root privileges?
Because I have an app which accesses system resources so ideally I'd like it to not only move but also change the ownership to root (similar to how Tunnelblick does it). Can this be achieved with LetsMove?
Thanks again!
I'm a developer with LetsMove integrated into my app SelfControl. In macOS Big Sur, you can turn on "Downtime" or other app limits using the Screen Time. In this case, affected apps have their windows taken over and show a "Time Limit" screen, with some buttons allowing the user to ignore the limit and continue.
When opening my app during Downtime without LetsMove, it opens fine (shows the Time Limit screen, and can be bypassed using the Ignore Limit button). With the LetsMove dialog enabled, the app still shows the "Time Limit" screen but the buttons aren't clickable. So there's no way to bypass the dialog and continue. It looks like it's some sort of window focus issue.
Ideally, LetsMove would work seamlessly with Screen Time and the buttons would be clickable!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.