Read and Write access for FinderSync extension in a sandboxed environment - macos

The scenario
The user right-clicks a directory in Finder and finds a custom MenuItem. Clicking that Item will tell my app to open up a window where the user can do his work. When he is finished files need to be written to to the folder he selected by right-clicking.
The Problem
I got everything to work now, but the very last part. The extension can't write to the selected folder.
The user selecting the folder he wants to interact with seems to not be part of the Powerbox which - how I understand it - is only activated with openPanel and savePanel. How do I get the rights to interact with the folder that the user selected through my menu item? I can't find a reference to any possible solution to that problem in the developer library. Not in the sandboxing guide not in the extensions guide.
The possibility to add custom menu items would be rather useless if there was no way to use the selected files and folders so I'm sure there must be a way for accessing them.
Maybe the way I'm trying to write is wrong. My main app writes a temporary file into a shared group folder. After that it sends a notification that the extension listens to:
func copyFile(notification:NSNotification)
{
NSLog("write file")
if let target = tmpTarget
{
let secureContainer = NSFileManager.defaultManager().containerURLForSecurityApplicationGroupIdentifier("group.de.enie.Nu")
let contents = NSFileManager.defaultManager().contentsOfDirectoryAtURL(secureContainer!, includingPropertiesForKeys: nil, options: NSDirectoryEnumerationOptions.SkipsHiddenFiles | NSDirectoryEnumerationOptions.SkipsPackageDescendants | NSDirectoryEnumerationOptions.SkipsSubdirectoryDescendants , error: nil)
for content in contents as! [NSURL]
{
NSLog("tmp data: \(content.path!)")
if content.lastPathComponent!.stringByDeletingPathExtension == "SharedData"
{
NSLog("found shared file")
NSFileManager.defaultManager().copyItemAtURL(content, toURL: target.URLByAppendingPathComponent(content.lastPathComponent!), error: nil)
NSFileManager.defaultManager().removeItemAtURL(content, error: nil)
}
}
tmpTarget = nil
}
}
The attempt to write the file results in these console notifications:
open on /Users//Desktop/SharedData.png: Operation not permitted
deny file-write-create /Users//Desktop/SharedData.png
Any ideas how to get access to user selected folders are appreciated.
Update
I just reassured that I did no mistakes in any way. While I'm allowed to access folders via the NSOpenPanel (which means entitlements should be right) I can not create folders in / or even bookmark the target url of my default FIFinderSyncController.

Even though the Finder Sync App Extension is granted "User Selected File" Sandbox File Access, the selectedItemURLs() files accessed by the user via Finder Sync App Extension right-click seemingly do not count as being "user-selected". The sandbox thus denies your Finder Sync app access to those files.
As the other answer notes, the only way around this is to use a temporary entitlement for wider file access. Or to use a Powerbox NSOpenPanel to have the user select a containing folder, and use that security-scoped bookmark to access the sandboxed files.
Please duplicate my Apple bug report requesting this behavior be allowed:
Finder Sync App Extension selectedItemURLs() should receive "User Selected File" Sandbox file access.
rdar://42874694
https://openradar.appspot.com/radar?id=5063363058991104

You should be able to write to the selected file if you grant the entitlement: com.apple.security.files.user-selected.read-write

Related

How to run AppleScript from inside a SwiftUI View?

I'm kind of getting some more understanding with basic SwiftUI but now I wanted to extend my application to actually do some stuff regarding system components for me. Basically I want to run an AppleScript from inside my app which creates a signature in Mac Mail. The script itself is pretty simple:
// Generates a signature in Mac Mail
tell application "Mail"
set newSig to make new signature with properties {name:"The Signature Name"}
set content of newSig to "My New Signature Content"
end tell
I have created a view with a button which should execute the script:
import SwiftUI
struct SomeView: View {
#State var status = ""
var body: some View {
VStack(alignment: .center) {
Button(action: {
let source = """
tell application \"Mail\"
set newSig to make new signature with properties {name: \"The Signature Name\"}
set content of newSig to \"My New Signature Content\"
end tell
"""
var error: NSDictionary?
if let scriptObject = NSAppleScript(source: source) {
if let output: NSAppleEventDescriptor = scriptObject.executeAndReturnError(&error) {
self.status = output.stringValue ?? "some default"
} else if (error != nil) {
self.status = "error: \(error)"
}
}
}) {
Text("Generate").font(.callout)
}
Text("\(self.status)")
}
}
}
struct SomeView_Previews: PreviewProvider {
static var previews: some View {
SomeView()
}
}
Everything executes but I get the error
AppleScript run error = {
NSAppleScriptErrorAppName = Mail;
NSAppleScriptErrorBriefMessage = "Application isn\U2019t running.";
NSAppleScriptErrorMessage = "Mail got an error: Application isn\U2019t running.";
NSAppleScriptErrorNumber = "-600";
NSAppleScriptErrorRange = "NSRange: {0, 0}";
}
After some research i found this article which describes the problem quite well. Apparently this error is because the app is running in a sandbox and within the sandbox Mail is indeed not running. I kind of get Apple's idea not to let applications do whatever they want without the user's consent...
Anyway, unfortunately this article describes the solution using Objective C and this is something I have even less of a clue than SwiftUI.
Can anybody tell me how to run (or copy my script.scpt file to the accessible library folder and the run) a script from within a SwitUI View? This would be so much help!
Thanks!!!
I have played around quite a bit with this manner and after having numerous discussions and trial & errors on that subject, I want to present 2 solutions (until now it seems that these are the only possible solutions for a sandboxed application).
First of all, if you don't consider to distribute your app on the App Store, you can forget about the following, since you just have to deactivate the Sandbox and you are basically free to do whatever you want!
In case your planning to distribute a Sandboxed App, the only way of running a script and interacting with other apps on the user's system is to run a script file from the Application Script folder. This folder is a designated folder in the user library structure: /Users/thisUser/Library/Application Scripts/com.developerName.appName/. Whatever script goes in here you have the right to run from your application appName.
You basically have two options to get your script file into that folder:
Option 1 - Install the Script File
This is (in my opinion) clearly the option you should go for if your script is static (does not require any additional user data from your application). All you have to do is
Select the project (1)
click on Build Phases (2)
add the Copy Files setting (if not already present)
choose the script file location in your app (singing might be a good option when distributing via AppStore)
add the Application Script folder of your application to the destination. Therefore, choose Absolute Path and enter Users/$USER/Library/Application Scripts/$PRODUCT_BUNDLE_IDENTIFIER in the Path field.
You can also select the Copy only when installing option if your script is entirely static but in case you have changes on the script when the application is closed and reopened and you want to update the script, leave this option blank (I have not tested this!).
After this is done you can execute the script via NSUserScriptTask. A more detailed description of how you could implement this is given here.
Option 2 - Giving access to the folder and copy the file on demand
This is certainly the solution when your script file updates dynamically according to e.g. user inputs. Unfortunately, this is a bit of a hassle and does not have (in my opinion) satisfying solutions. To do so, you will have to grant access to the folder (in this case Application Scripts/). This is done via NSOpenPanel. A really good tutorial how to implement this is given here.
Per default you will have read permission to that folder only. Since you are trying to copy a file into that folder you will have to change that to read/write in your Capabilities as well.
I hope this will help some people to "shine some light into the dark"! For me this was quite a bit of a journey since there is only very little information out there.

FileReference.upload() doesn't upload locally

Trying to upload using flash swf on my local machine running Windows with IIS and PHP 5.3 installed.
var selected_file:FileReference = GetSelectedFile();
var url:String = 'http://localhost.com/upload.php';
var req:URLRequest = new URLRequest(url);
req.data = new URLVariables('source=' + 'computer');
req.method = URLRequestMethod.POST;
selected_file.upload(req, 'file', false);
selected_file.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA, UploadComplete, false, 0, true);
selected_file.addEventListener(ProgressEvent.PROGRESS, UploadProgress, false, 0, true);
selected_file.addEventListener(IOErrorEvent.IO_ERROR, UploadError, false, 0, true);
selected_file.addEventListener(SecurityErrorEvent.SECURITY_ERROR, UploadSecurityError, false, 0, true);
I've set breakpoints on all the handler functions. I hit a breakpoint on UploadProgress handler. However, the file isn't being uploaded. When looking at network traffic there is no call to upload.php.
Looking at documentation for FileReference upload function I see this:
Uploads and downloads are not allowed if the calling SWF file is in
the local-with-filesystem sandbox
Looking at definition of local-with-filesystem sandbox I found this:
From this sandbox, executable code can read local files (by using the
URLLoader class, for example), but cannot communicate with the network
in any way. This assures the user that local data cannot be leaked out
to the network or otherwise inappropriately shared.
Does this mean there isn't a way to test uploads on local machine?
update: Supposedly I need to have Security.sandboxType as LOCAL_TRUSTED. I've been trying to set security type by adding configuration file for user and globally. Configuration file has cfg extension and contains one line, path to folder where my swf file resides.
Still after adding this configuration, Security.sandboxType is set to "remote".
According to Adobe:
The local-with-filesystem sandbox—For security purposes, Flash Player places all local SWF files and assets in the local-with-file-system sandbox, by default. From this sandbox, SWF files can read local files (by using the URLLoader class, for example), but they cannot communicate with the network in any way. This assures the user that local data cannot be leaked out to the network or otherwise inappropriately shared.
You cannot access the file system and network within one SWF instance in normal situations.
However, it appears to be possible to grant this ability to a specific SWF by adding the file to the local trusted sandbox. According to Senocular, this may be accomplished in three different ways...
SWFs run within Flex Builder 2 or the Flash Authoring environment
SWFs located in a directory the user has granted permissions for from the (online only) Global Security Settings Panel.
SWFs located in a directory specified in a Flash Player trust configuration (.cfg) file on the local system
For the Configuration File Option:
You need to compile your SWF with the local playback setting selected.
This means you need to place your configuration file in
C:\Windows\System32\Macromed\Flash\FlashPlayerTrust\
If the folder doesn't exist you must create it.
Then create your configuration file (naming it whatever you
please.cfg) Finally, type C:\ into the file and save it.
Now your whole C:\ directory should be declared local_trusted and shouldn't have any issues contacting your server and accessing your files.

Validation Of Uploadfield in extjs4

I want to add validation in filefield of ExtJs4 , so that user can only browse .png , .jpeg image files..How should I do it ?
{
xtype: 'filefield',
id:'photoUpload',
buttonOnly:true,
buttonText: 'Photo'
}
I think it is important to understand how file upload works, so to prevent yourself from troubles in the future...
For security reasons, the following applies:
Browsers cannot access the file system unless the user has explicitly clicked on an upload field.
Browser has minimal access to the file being uploaded, in particular - you JS code may be able to see the file name (the browser has to display it in the field), but nothing else (the path itself on most browsers is not the correct one).
The upload process itself happens in these steps:
The user clicks on an upload field, initiating the file select dialog.
The browser implements access to the file system through the dialog, allowing the user to select a file.
Upon OK click, the browser sends the file to the server.
The server places the file in its temp directory (configured per server).
Once upload is complete, the upload script on the server is called with the file details, and that script will have full access to the uploaded file.
The last step is the only point where you have full access to the file details, including the real actual name, its size, and its content.
Anything the browser gives javascript is browser depended. Even the file name will vary between browsers although all the browsers I know do keep the actual file name (but not the real actual path), you cannot rely on this to work with future versions. The reason for this is that the file name is displayed on the client side.
So the recommendation is this:
Do all file upload checks on the server side.
Again, you may get away with the file name on the JS client side, particularly if you know and can test what browsers your clients will use, but I'd strongly recommend to to this test on the server.
The last thing you have to remember is that users might upload a file ending with .png, but the file itself is a .zip with the extension changed - so to really confirm that the file is .png you need to actually look into the file data, which only the server can do.
{
xtype: 'filefield',
id:'photoUpload',
buttonOnly:true,
vtype:'fileUpload',
buttonText: 'Photo'
}
And Vtype which I have use..
Ext.apply(Ext.form.VTypes, {
fileUpload: function(val, field) {
var fileName = /^.*\.(gif|png|bmp|jpg|jpeg)$/i;
return fileName.test(val);
},
fileUploadText: 'Image must be in .gif,.png,.bmp,.jpg,.jpeg format'
});
Try following snippet in your 'filefield' xtype config
regex : (/.(gif|jpg|jpeg|png)$/i),
regexText : 'Only image files allowed for upload',
msgTarget : 'under'

how to make a Location Service Permission MessageBox

I have submited a App but is not certified because this:
"This application uses the Location Service API to determine a user's location and show them events taking place nearby
however, it does not appear to contain a privacy policy that is accessible to users explaining how the application uses the
Location Service API."
And what i want to do is something like this
When user clicks in Policty Statement open a new window with settings page with my app location policies.
Can anyone help me? How can i add a link like in image?
Cumps
You can navigate to a page using the Hyperlink control
<Textblock>
<Hyperlink NavigateUri="/PrivacyPage.xaml"
TargetName="_blank">Privacy Statement</Hyperlink>
</Textblock>
See MSDN for more info
I don't think you can put a hyperlink in a call to MessageBox.Show() though so you have to create your own Messagebox-like page.
The first time the app is launched you direct the user to your message page. E.g. using this in MainPage.xaml
OnNavigatedTo(...)
{
if(!AppSettings.HasMessageBeenShown)
{
AppSettings.HasMessageBeenShown=true;
NavigationContext.Navigate(new Uri("/MessagePage.xaml", UriKind.Relative));
}
}

Can I drag files from the desktop to a drop area in Firefox 3.5 and initiate an upload?

I've set a ondrop event on my drop area and it receives an event when I drag an image from my desktop to the drop area.
However, according to the Recommended_Drag_Types document:
https://developer.mozilla.org/en/DragDrop/Recommended_Drag_Types
A local file is dragged using the application/x-moz-file type with a data value that is an nsIFile object. Non-privileged web pages are not able to retrieve or modify data of this type.
That makes sense, but how do I prompt the user to escalate privileges to get access to the file data and send it via an XMLHttpRequest?
If I try it without escalating privileges when I do this code:
event.dataTransfer.mozSetDataAt("application/x-moz-file", file, 0);
Javascript returns this error:
Permission denied for domain.com to create wrapper for object of class UnnamedClass
The only article I can find on this is one from 2005 but I can't tell if the directions still apply to Firefox 3, it suggest doing this:
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
which doesn't seem to work.
If you haven't upgraded to 3.5 yet, you can use the dragdropupload extension.
I found out that if instead of escalating privileges globally:
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
...
function doDrop(event) {
...
var file = event.dataTransfer.mozGetDataAt("application/x-moz-file", 0);
...
}
I escalate privileges in the function's body:
...
function doDrop(event) {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
...
var file = event.dataTransfer.mozGetDataAt("application/x-moz-file", 0);
...
}
I get rid of the error you described and gain access to the nsIFile instance I was looking for.

Resources