Drag & Drop between Photos and a Sandboxed app - cocoa

I have a Sandboxed app. I'd like the user to be able to drag a picture from the Photos app into my sandboxed app.
I wrote all the code for handling the "promised file" from Photos no problem when using it with a not sandboxed app. But with my sandboxed app, Photos is telling me it has no right to write the picture file in the destination folder... which is the temporary folder inside my sandboxed app.
That's kind of logical that Photos can't write inside my sandbox, even in the temporary folder. But as my app has no access outside the sandbox, how can I exchange the file from Photos to my App ???
Edit to provide some code as per request
NSString *tempPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSUUID UUID] UUIDString]] ;
NSError *error = nil ;
[[NSFileManager defaultManager] createDirectoryAtPath:tempPath
withIntermediateDirectories:NO
attributes:nil
error:&error] ;
filenames = [sender namesOfPromisedFilesDroppedAtDestination:[NSURL fileURLWithPath:tempPath]] ;
(filenames is global variable NSArray)
And the error message that Photos is showing (SOrry, the picture is in French, but the message isn't really usefull as it can't be shown completely. The part that is shown says "0 file (out of 1) exported. Impossible to move "image" to "folder" : either this folder does not exist, or the folder can't be //cut//
I of course checked that the folder does exist.
Edit with some more information
I think I found one investigation path :
When I create the unique folder in the temporary folder, it gets a "quarantine" flag. I checked that using the terminal and "ls -l", one can see a "#" after the permissions. Now, If I don't create the unique folder, Photos can drop the file directly in the temporary folder. Therefore the problem is that Photos can't use the "quarantined" folder.
Now this does not help yet. As anyway the drop picture file also has the quarantine flag set, my App can't read it !
I have found no way to get rid of those quarantine flags.
The strangest thing is that everything is working fine if I just step by step with the debugger.

The Sandbox will allow file promise provider application to write into the Document folder.
[[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:NULL];

OK, I found what was wrong. It has nothing to do with quarantine or app being sand-boxed.
In fact, when namesOfPromisedFilesDroppedAtDestination: is called, the provider application (in my case Photos) starts writing the files in the temporary folder. This can take some time. Therefore, I was starting to monitor the events in the destination folder, and was reacting on the first event only. (check this : How can my OS X app accept drag-and-drop of picture files from Photos.app?).
BUT
The Photos app is sending the files in several steps. The first steps create a temporary file, which is later renamed to the "correct" image file. This creation of temporary file was the event that my app captured. My app was then checking for the image file to be present, and it wasn't, yet.
The solution is therefore, to continue checking for folder events until the promised file is really there. If it is not yet, then continue waiting. Hopefully, the promising app will fulfill its promise one day.
Thanks to catlan for pushing me to re-investigate.

Related

Saving to directory in sandboxed app using NSOpenPanel doesn't work, but drag/drop does

I'm working on a sandboxed OS X app where images get saved into a user-selected directory. There are two ways for the user to pick their output directory: selecting the directory using an NSOpenPanel, and dragging and dropping the directory onto the app.
When I use the drag/drop approach, everything works fine and my files get saved with no issues.
However, if I pick the directory using NSOpenPanel, I get error 513:
Error 513: "You don't have permission to save the file 'x.jpg' in the folder 'y'."
Both the NSOpenPanel and drag/drop code keep their chosen URL in the same place and process it in the same way. What could account for this difference? Does the NSOpenPanel have to be set up in a specific way to grant read/write permission to the directory?
For the record, I've never noticed this problem in the past year of using my app, so it might be a recent change to Cocoa — but I'm not 100% sure about that.
As expected, it looks like I was using NSOpenPanel wrong. 😐
Was storing URL from delegate call in panel(_ sender: AnyObject, validate url: URL). Should have been calling the panel's URL properties from the callback in begin(completionHandler handler: (Int) -> Swift.Void).
In my case, I was mistakenly writing my individual files to paths created by concatenating each filename to the URL:
panel.directoryURL
After changing that to:
panel.urls.first
...it worked. Hope this saves some headaches.

Can I delete a file on the Desktop from a sandboxed Mac app?

I can get my Mac app to read the contents of the Desktop, but whenever I try and delete a file it says I don't have permission to do so. If the user has authorized access to the Desktop, shouldn't I be able to delete a file?
Sandbox is turned on
File access is set to read/write for User Selected File.
The security scoped bookmarks are working because I have read access to the files, and it only comes up with the authorize prompt once.
This is the code I am using to delete:
[[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation
source:desktopPath
destination:#""
files:self.filesToDelete
tag:nil];
I also tried using the fileManager trash method with no success.
Here is the error that returns from the fileManager trash method:
Error Domain=NSCocoaErrorDomain Code=513 "“file.png” couldn’t be moved to the trash because you don’t have permission to access it." UserInfo=0x60000026a980 {NSURL=file:///Users/me/Desktop/file.png, NSUserStringVariant=(
Trash
), NSUnderlyingError=0x600000257ca0 "The operation couldn’t be completed. (OSStatus error -5000.)"}
And the actual trash method call:
[[NSFileManager defaultManager] trashItemAtURL:url resultingItemURL:nil error:&error];
Unless the user has selected the file through an open dialog, or you saved the file through your app using a save dialog, you won't be able to delete it in a sandboxed app.
There are a few directories where you can do what you want, mostly /tmp and the directories in your sandbox container.
https://developer.apple.com/library/mac/documentation/security/conceptual/AppSandboxDesignGuide/AppSandboxInDepth/AppSandboxInDepth.html
FWIW - I've run into the same issue. I'm trying to use trashItemAtURL to trash a specific application in my /Applications folder in a Sandboxed App. However, even if I display a NSOpenPanel and get the user to specifically pick the Application I want to send to trash (with the help of [panel setTreatsFilePackagesAsDirectories:YES];), I still get a permission error. It seems even if you have write access to a folder/bundle in the /Applications folder, you still can't send it to trash.

NSMetadataQuery does not find documents after moving them to iCloud

In my Mac app I have an NSMetadataQuery to watch the app's iCloud directory. When the user decides to enable iCloud from within the app I do the following in this order:
Call [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil] to establish access to the iCloud container
Start a NSMetadataQuery to find all files (pattern: "*") in the iCloud container
Now I iterate over all files in the local container and move them to iCloud by using [fileManager setUbiquitous:YES itemAtURL:fileURL destinationURL:targetURL error:&error]
I have two types of documents that I move to iCloud:
The actual documents stored as file packages
For each document I have a .preview file as simple binary file
My problem is that the meta data query only returns my binary preview files, but not my actual documents until I relaunch my app (or open a Finder window of the app's iCloud folder from the Terminal - in this case my app gets another query result with all files).
So is this some weird caching issue? Is this because the documents are file packages and not simple binary files?
I don't see why that should be a problem. If I restart my app the query does return all files correctly.
Could this be a problem with the way my custom document type is exported in Info.plist?
Could this be a problem with the way my custom document type is
exported in Info.plist?
I suppose so: the issue you are describing is most likely because your document format is a file package (I assume written with NSFileWrapper). Remember that the package bit is set when writing the file, so if you have other apps writing to iCloud, that could make the issue much more confusing as it's not about interpreting a file bundle but writing it in a correct manner.
NSMetadataQuery unfortunately ignores folders, so your files will never be reported back unless you have correctly registered your filetype as bundle in the application that writes the file to the iCloud container.
There are a lot of similar and potentially confusing settings in the info.plist, so many things that can go wrong. I experienced an issue when I registered my document filetype correctly on OS X, but failed to do so on iOS: https://stackoverflow.com/a/14993694/388412
The first thing you should do is to go to https://developer.icloud.com/#documents and see if you're looking at files (correct) or folders (problem).
Work on your info.plist document types until you see the documents as files, then it should work.

NSSavePanel for saving a file after sandboxing

I have a mac AVRecording app, which records a video and save it to a location selected via NSSavePanel. It was working fine till I sandboxed the app.
For sandboxing I have added the following entitlements
com.apple.security.files.user-selected.read-write
com.apple.security.assets.movies.read-write
com.apple.security.files.downloads.read-write
This enables saving to Downloads and movies folder only.
How is it possible to save my file to any desired location, Desktop, Documents etc ?
It's not clear from your question whether you are referring to saving a particular file (in which case you can use the NSSavePanel and manually copy the file using NSFileManager to write the file into the user-specified new file), or whether you are referring to having the user choose a location for all future downloads.
If you want to prompt the user for a location to use for future downloads, you'll need to use the secure bookmark entitlement and secure bookmarks to retain access to the folder.
There's another stackoverflow answer about sandboxing which covers the process of saving and using the secure bookmark.

Mac OS App: How to resolve alias and read original file within sandbox

My mac os app get a NSURL of alias by user interaction (drag & drop), so the app have the permission to read the alias file, but it doesn't have permission to read the origianl file within app sandbox (Mac OS X 10.7/8).
I resolve the alias by
NSData* bookmark = [NSURL bookmarkDataWithContentsOfURL:aliasURL error:nil];
origURL = [NSURL URLByResolvingBookmarkData:bookmark
options:NSURLBookmarkResolutionWithoutUI
relativeToURL:nil
bookmarkDataIsStale:nil
error:&error];
When I try to read origURL file, I get the error: The file couldn’t be opened because you don’t have permission to view it.
I aslo tried call the start/stopAccessingSecurityScopedResource on the origURL but no help.
I also tried resolving bookmark data with NSURLBookmarkResolutionWithSecurityScope option, but get "The file couldn’t be opened because it isn’t in the correct format." error from URLByResolvingBookmarkData method.
So, How do it? Thanks.
I haven't tried this, but I think I might have an idea what's happening. The way OS X punches through the sandbox with drag-and-drop is by granting the app the files are dropped onto access to the dropped files until the app quits. This works using the plain NSString file paths on the pasteboard, so it does not rely on the security scoping mechanism.
Your app probably has access to the alias file, but only that file, not the one to which it refers. The sandbox hole-punching mechanism probably doesn't follow the alias and grant access to the underlying file. If you can get the path of the file to which the alias points (and I'm not sure that's possible), you can get around the sandboxing by prompting the user to select that file in an NSOpenPanel. That's another way of punching through the sandbox, using what Apple calls the "Power Box".
For more information on how to do this, check out the answer I wrote here: https://stackoverflow.com/a/11786156/105717. It links to another answer, then adds some helpful niceties to make what's happening clearer to the user.
Maybe, just maybe my similar situation and solution will help:
Have you definitely got the entitlement "com.apple.security.files.bookmarks.app-scope" set to "yes" in your entitlements file?
"The file couldn’t be opened because it isn’t in the correct format." I was getting this same error when trying to resolve the bookmark, that turned out to be the fact that the file was locked in Finder (do a 'get info' on the file and check the 'locked' box is off) so the security data was never generated in the first place.
Hope there's something in there to help!
Todd.

Resources