MacOS sandboxed application: access files without NSOpenPanel

夙愿已清 提交于 2020-05-14 04:02:30

问题


In a sandboxed NSDocument-based application, any compatible document can be accessed using the NSOpenPanel, no matter where the document is saved. Without NSOpenPanel, the application can only access files in the sandbox container.

As my application manages two types of subclassed NSdocument (Text as a reader/writer and Image as a reader only), I try to implement a separate "Open Recent" menu for images. I disabled the the ordinary behaviour for them as they are opened by the user, overriding the noteNewRecentDocumentURL: (NSURL *)url method of the NSDocumentController to return NO for image urls. So that only the text documents appear in the ordinary File -> Open Recent menu (and open normally when user select them). Images are listed in a custom menu.

The problem occurs with these image urls, because the application is sandboxed: the application cannot open directly any image file listed in the dedicated menu (any reading operation returns a -54 error. This behaviour can be checked using:

[[NSFileManager defaultManager] isReadableFileAtPath:[fileURL path]]

which always returns FALSE in this situation. There is only one exception to that: when I reopen, from the dedicated Open Recent menu, a file that has been previously opened with the NSOpenPanel in the same application session, then closed: in this case isReadableFileAtPath: returns TRUE and the file can be accessed. But when application quits and restarts, recent images files cannot be accessed this way.

I identified thre solutions to deal with this problem:

  1. Moving the image file in the sandbox container as soon as it has been accessed "legally" by the user, through the NSOpenPanel. It works, of course, but prevent the user from deciding on his own the location of his files! In the same way, duplicating the file in the sandbox is not a solution.

  2. Creating an alias to these files in the sandbox. As I couldn't find a way to do this, I couldn't test whether this is a solution or not.

  3. Disable the application sandboxing. But this is the worse solution as there are many reasons to use sandboxing!

Is there a 4th solution, which would authorize a read-only access to any image file, wherever it is located, without disabling the sandbox?


回答1:


You can't access any file no matter what.

Also I am not sure what your second solution means, that is probably the reason you couldn't follow it. You probably wanted to refer to 'security-scoped bookmarks' and not to 'aliases' and they work very well and that is the path that you should follow.




回答2:


Well Ivan's suggestion was excellent. After a few readings (less than an hour), I could implement those security-scoped bookmark. For interested people, here are the main findings.

  1. add the feature to your sandboxed application's entitlement file set the com.apple.security.files.bookmarks.document-scope (or the com.apple.security.files.bookmarks.app-scope, or both) key to TRUE.

  2. Modify your document opening method (which calls the NSOpenPanel) like this:

-(void) openMyDocument:(id)sender{

      // ... do your stuff

    [self.panel beginWithCompletionHandler:^(NSInteger result) {
        if (result == NSModalResponseOK) {
            NSURL* selectedURL = [[self.panel URLs] objectAtIndex:0];            
            NSData *bookmark = nil;
            NSError *error = nil;
            bookmark = [selectedURL bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope
                     includingResourceValuesForKeys:nil
                                      relativeToURL:nil // Make it app-scoped
                                              error:&error];
            if (error) {
                NSLog(@"Error while creating bookmark for URL (%@): %@", selectedURL, error);
            }
            NSString *access = [NSString stringWithFormat:@"%@%@", @"Access:", [selectedURL path]];
            [[NSUserDefaults standardUserDefaults] setObject:bookmark forKey:access];
            [[NSUserDefaults standardUserDefaults] synchronize];

            // ... then open the document your way
        }
    }
}
  1. Modify the method you created to read the file without using NSOpenPanel
- (void) openDocumentForScopedURL: (NSURL *) fileURL

        NSString *accessKey = [NSString stringWithFormat:@"%@%@", @"Access:", [fileURL path]];
        NSData *bookmarkData = [[NSUserDefaults standardUserDefaults] objectForKey:accessKey];
        NSURL *bookmarkFileURL = nil;
        if (bookmarkData == nil){
            // no secured-scoped bookmark found, alert the user
            return;
        } else {
            NSError *error = nil;
            BOOL bookmarkDataIsStale;

            bookmarkFileURL = [NSURL
                               URLByResolvingBookmarkData:bookmarkData
                               options:NSURLBookmarkResolutionWithSecurityScope
                               relativeToURL:nil
                               bookmarkDataIsStale:&bookmarkDataIsStale
                               error:&error];
            [bookmarkFileURL startAccessingSecurityScopedResource];
        }


        // ... Then open your file, using bookmarkFileURL
        // ... and do your stuff

        // IMPORTANT. You must notify that stopped to access

        [bookmarkFileURL stopAccessingSecurityScopedResource];            
}


来源:https://stackoverflow.com/questions/54239776/macos-sandboxed-application-access-files-without-nsopenpanel

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!