{"id":19535681,"url":"https://github.com/miyako/4d-plugin-apple-file-promises","last_synced_at":"2026-06-18T07:31:45.227Z","repository":{"id":147172307,"uuid":"133358319","full_name":"miyako/4d-plugin-apple-file-promises","owner":"miyako","description":"Accept drag and drop of messages from Apple Mail and Microsoft Outlook","archived":false,"fork":false,"pushed_at":"2023-08-03T07:46:22.000Z","size":8938,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-02-26T04:16:31.913Z","etag":null,"topics":["4d-plugin"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/miyako.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-05-14T12:36:32.000Z","updated_at":"2024-08-27T04:47:23.000Z","dependencies_parsed_at":null,"dependency_job_id":"588b7f32-643e-41a6-9d86-31679321bc43","html_url":"https://github.com/miyako/4d-plugin-apple-file-promises","commit_stats":null,"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"purl":"pkg:github/miyako/4d-plugin-apple-file-promises","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/miyako%2F4d-plugin-apple-file-promises","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/miyako%2F4d-plugin-apple-file-promises/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/miyako%2F4d-plugin-apple-file-promises/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/miyako%2F4d-plugin-apple-file-promises/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/miyako","download_url":"https://codeload.github.com/miyako/4d-plugin-apple-file-promises/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/miyako%2F4d-plugin-apple-file-promises/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34481291,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-18T02:00:06.871Z","response_time":128,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["4d-plugin"],"created_at":"2024-11-11T02:19:34.975Z","updated_at":"2026-06-18T07:31:45.199Z","avatar_url":"https://github.com/miyako.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"![version](https://img.shields.io/badge/version-18%2B-EB8E5F)\n![platform](https://img.shields.io/static/v1?label=platform\u0026message=mac-intel%20|%20mac-arm\u0026color=blue)\n[![license](https://img.shields.io/github/license/miyako/4d-plugin-apple-file-promises)](LICENSE)\n![downloads](https://img.shields.io/github/downloads/miyako/4d-plugin-apple-file-promises/total)\n\nSee [4d-utility-sign-app](https://github.com/miyako/4d-utility-sign-app) on how to enable the plugin in 4D.\n\n# 4d-plugin-apple-file-promises\nAccept drag and drop of messages from Apple Mail and Microsoft Outlook\n\n**A similar solution for Windows is available**: [message-file-drop](https://github.com/miyako/4d-plugin-message-file-drop)\n\nThe plugin is compatible with the new security rules of macOS Mojave, but it is your responsibility to add the \"Privacy - AppleEvents Sending Usage Description\" (``NSAppleEventsUsageDescription``)  key to the main app's ``Info.plist``.\n\nSee [4d-plugin-notes](https://github.com/miyako/4d-plugin-notes) for more information.\n\n## Syntax\n\n```\nACCEPT FILE PROMISES (accept;method{;context})\n```\n\nParameter|Type|Description\n------------|------------|----\naccept|LONGINT|``1`` or ``0``\nmethod|TEXT|callback method(``$1:text;$2:text``)\ncontext|TEXT|any string (``$2`` passed to ``method``)\n\nIn ``method`` pass the callback project method name. It will be called with 2 parameters, where ``$1`` is the system full path of the file dropped to 4D, and ``$2`` is a copy of the ``context`` you passed earlier. You can use ``context`` to let the callback notify and update your UI worker/process, for example.\n\n**Note**: You can disable the folder monitoring process by passing ``0`` to the command. \n\n**Do not abort the callback method**. If you abort the execution context, the process will keep running but the method will not longer be called from the plugin until you reopen the structure file.\n\n---\n\n#### Technical Details\n\nThis plugin is designed to add the following functionalities to 4D:\n\n* Drag and drop emails from typical clients (Mail, Outlook) directly to 4D.\n\nTo customise drag and drop event handling, the plugin uses a technique known as **method swizzling**. For that reason, it is only compatible with the 64-bit Cocoa version of 4D.\n\nWhen swizzling is activated, the plugin adds some extra code to the following drag and drop obj-c methods from the ``NSDraggingDestination`` protocol: ``draggingEntered:`` ``draggingUpdated:`` ``prepareForDragOperation:`` ``performDragOperation:`` ``concludeDragOperation:``.\n\nThis allows the plugin to perform some extra work when 4D processes drag and drop events (``On Drag Over``, ``On Drop``) on its form objects.\n\n``draggingEntered:`` nothing special  \n``draggingUpdated:`` nothing special  \n``prepareForDragOperation:`` nothing special  \n\n``performDragOperation:``  \n\nInspect ``[[sender draggingPasteboard]types]``.  \n\n* Mail (Apple)\n\nIf ``com.apple.mail.PasteboardTypeAutomator`` is found, the plugin runs code to get ``eml`` files out of Mail.\n\n**Note**: Evidently, Mail uses [file promises](https://developer.apple.com/documentation/uikit/drag_and_drop/understanding_a_drag_item_as_a_promise) to export a single message (short operation) and Automator to export multiple messages. ``com.apple.mail.PasteboardTypeAutomator`` is a property list, an ``NSArray`` of ``NSDictionary``, which maps to objects with the structure ``[{account:string, id:integer, mailbox:string, subject:string}]``. Given the 3 identifiers (``account``, ``mailbox``, ``id``) it is possible to use AppleScript like this:\n\n```applescript\non run argv\n\tset param_mailbox to item 1 of argv\n\tset param_account to item 2 of argv\n\tset param_id to item 3 of argv\n\tset param_path to item 4 of argv\n\ttell application \"Mail\"\n\t\tset mm to (messages of mailbox param_mailbox of account param_account) whose id is param_id\n\t\tif (count mm) is 1 then\n\t\t\tset m to item 1 of mm\n\t\t\tset p to param_path\n\t\t\tset s to (source of m) as «class utf8»\n\t\t\ttry\n\t\t\t\tset f to open for access p with write permission\n\t\t\t\twrite s to f\n\t\t\t\tclose access f\n\t\t\tend try\n\t\tend if\n\tend tell\nend run\n```\n\n**For optimisation**, the plugin uses [``ScriptingBridge``](https://developer.apple.com/documentation/scriptingbridge) to create the ``eml`` file by itself, rather than launching ``osascript`` or ``NSAppleScript`` to run the AppleScript shown above.\n\n```objc\nMailApplication *application = [SBApplication applicationWithBundleIdentifier:@\"com.apple.mail\"];\n\nNSArray *mails = [application selection];\nNSArray *sources = [mails arrayByApplyingSelector:@selector(source)];\nNSUInteger i = 0;\n\nNSString *path = (NSString *)CFURLCopyFileSystemPath((CFURLRef)url, kCFURLPOSIXPathStyle);\n\nfor (id source in sources) {\n\ti++;\n\tNSString *dst = [path stringByAppendingFormat:@\"/%d.%@\", i, @\"eml\"];\n\t[(NSString *)source writeToFile:dst\n\t\tatomically:NO\n\t\tencoding:NSUTF8StringEncoding\n\t\terror:nil];\n}\n```\n\n* Outlook (Microsoft)\n\nLikewise, if ``dyn.ah62d4rv4gu8ynywrqz31g2phqzkgc65yqzvg82pwqvnhw6df`` is found, the plugin runs code to get ``eml`` files out of Outlook.\n\n**Note**: The above dynamic UTI resolves as ``?0=6:4=ERMessagePasteboardType`` using the method published [here](https://gist.github.com/jtbandes/19646e7457208ae9b1ad). Outlook used to deliver a type named ``ERMessagePasteboardType`` in version 14 but evidently removed it in version 15.\n\nThere is no list of selected messages (as was the case with Mail) but we could assume in AppleScript that the current selection should be exported like this:\n\n```applescript\non run argv\n\tset param_path to item 1 of argv\n\ttell application \"Microsoft Outlook\"\n\t\tset p to param_path\n\t\tset mm to selection as list\n\t\trepeat with m in mm\n\t\t\tset s to (source of m) as «class utf8»\n\t\t\ttry\n\t\t\t\tset f to open for access (p \u0026 (id of m) \u0026 \".eml\") with write permission\n\t\t\t\twrite s to f\n\t\t\t\tclose access f\n\t\t\tend try\n\t\tend repeat\n\tend tell\nend run\n``` \n\n**For optimisation**, the plugin uses [``ScriptingBridge``](https://developer.apple.com/documentation/scriptingbridge) to create the ``eml`` file by itself, rather than launching ``osascript`` or ``NSAppleScript`` to run the AppleScript shown above. Notice the property is ``selectedObjects``, not ``selection`` as in Mail.\n\n```objc\nMailApplication *application = [SBApplication applicationWithBundleIdentifier:@\"com.apple.mail\"];\n\t\t\nNSArray *mails = [application selection];\nNSArray *sources = [mails arrayByApplyingSelector:@selector(source)];\nNSUInteger i = 0;\n\nNSString *path = (NSString *)CFURLCopyFileSystemPath((CFURLRef)url, kCFURLPOSIXPathStyle);\n\nfor (id source in sources) {\n\ti++;\n\tNSString *dst = [path stringByAppendingFormat:@\"/%d.%@\", i, @\"eml\"];\n\t[(NSString *)source writeToFile:dst\n\t\tatomically:NO\n\t\tencoding:NSUTF8StringEncoding\n\t\terror:nil];\n}\n```\n\n* Fallback\n\nWhen neither of the app specific types are found, but either ``kPasteboardTypeFileURLPromise`` or ``NSPromiseContentsPboardType`` is found, the plugin falls back to generic file promise handling. In particular, it calls ``namesOfPromisedFilesDroppedAtDestination:`` to request the source to created the promised files. **However, this does not seem to work on High Sierra** (no issues on El Capitan). \n\nMaybe it is because [``namesOfPromisedFilesDroppedAtDestination:``](https://developer.apple.com/documentation/appkit/nsdragginginfo/1415980-namesofpromisedfilesdroppedatdes) has been deprecated in 10.13. \n\n* Folder Watching\n\nWhether the designated apps (Mail, Outlook) are invoked by scripting, or file promises are used, the plugin starts monitoring the destination folder (which is created in the temporary folder, ``NSItemReplacementDirectory`` in ``NSUserDomainMask`` appropriate for ``NSDesktopDirectory``) using [``FSEventStream``](https://developer.apple.com/library/content/documentation/Darwin/Conceptual/FSEvents_ProgGuide/UsingtheFSEventsFramework/UsingtheFSEventsFramework.html).\n\nThe callback 4D method is executed every time a file is added to the destination folder. Meanwhile, **for optimisation**, the code to extract and export ``eml`` files from Mail or Outlook are executed in a background thread, with [``performSelectorInBackground:``](https://developer.apple.com/documentation/objectivec/nsobject/1412390-performselectorinbackground?language=objc).  \n\n* Simple API\n\nIf neither the app specific types or the file promise types are found, but ``NSFilenamesPboardType`` is found, in other words, a file (not a promise) has been dropped, the same callback 4D method is invoked instantly. 4D does not have to know if the file existed already or was created via scripting or via promises being kept. It just receives a path.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmiyako%2F4d-plugin-apple-file-promises","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmiyako%2F4d-plugin-apple-file-promises","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmiyako%2F4d-plugin-apple-file-promises/lists"}