-
Notifications
You must be signed in to change notification settings - Fork 1
MailPluginTool
A helper for quick integration of Sparkle updates, crash report collecting and uninstallation for your plugin among other things.
The MailPluginTool is not a stand alone product. It is included inside the Mail Plugin Manager that you should ship your application with if you want to use this functionality. The two applications work together to provide a solution to keep your plugin up-to-date as seamlessly as possible for both you and your customers.
- Uninstallation (by plugin demand)
- Automatic update checking (by plugin demand)
- Send crash reports to developer (by plugin demand)
- Check all plugins for compatibility/updates at boot time
- Get relevant System information (by plugin request)
- Get list of past & future compatibility information (by plugin request)
- Checks for compatibility of plugins at boot time (i.e. after an install)
All interactions with MPT are done using a file-based mechanism and the Tool is run as a LaunchAgent.
A plugin will be able to request the uninstallation of itself. When this happens MPT will present a standard type of uninstall "Are you sure?" dialog and then proceed to uninstall and restart Mail.
A plugin can request an update check and ask that it happen regularly, using the path to the plugin to update. It will check (using Sparkle) to see if there is an update available and will let Sparkle progress through a normal update process if it is found.
You can add additional sparkle key/value pairs to the Sparkle check as well if you want to pass some additional information to your feed.
Look for any crash reports for the plugin (whose path will have been passed in) and if found send these back to the developer, targeting some type of web service. It will send reports for both Mail and your particular plugin - up to the last 10 reports.
You will obviously need to setup a web service to accept the input from this and add that URL to the Info.plist file of your plugin. See the Info.plist Keys section about this.
The data is sent in a JSON package that contains the crash reports plus information about the version of Mail.app, Message.Framework, Mac OS X and the machine itself, plus a list of Mail plugins (both active and disabled).
An example php script to accept this data and load into a mysql database can be found in the Github repository. It is based on the similar script written by Tom Harrington for the Sparkle feed values and in fact uses the profileDB.php and profileConfig.php from Tom's code, which you can find here. In addition, it uses the php JSON extension to parse the data, which you'll need to ensure is included in your php installation (should be in version 5.2.0 or greater). For more information about that extension see here. Also included with my script is an sql script to create the database tables.
At boot time (though a Launch Agent) check for any incompatibilities or updates for existing plugins, either enabled or disabled.
This feature will be initiated by a Launch Agent setup for the user (which shouldn't need any special authorization) and will be launched each time the user logs in to a full session (excludes terminal sessions).
This mode will look at all plugins in both "Bundles" and "Bundles (Disabled[ X])" to find the latest ones and look to see if they have incompatibilities or updates and notify the user.
The Mail Plugin Manager app will be presented to the user to potentially do updates or at least know that which plugins will get disabled.
This allows the calling bundle to get back a dictionary of information about the system, such as OS Version, Mail Version & UUID, Message.framework version & UUID.
The tool will keep an updated version of a list of UUIDs that Mail and Message.framework have defined and supported for plugins to access, so they can actually test in advance if they will be compatible with an upcoming OS release. Or even just an OS release after the one the user has.
The tool will install the .plist into the User's LaunchAgent folder ~/Library/LaunchAgents and load it (or unload and delete the .plist file) for a plugin. This allows the plugin to install a helper that can run outside the sandbox in order to perform some capabilities that it might currently be limited from doing.
These macros are the ones that you should call when the app launches to do updates or send crash reports in the background. They do not require your plugin to take any other action. The names are pretty self-explanatory, I think. The ones that end with "Now" should be used if you want to force an update, such as when the user clicks a button saying "Check Now". The others use the standard Sparkle scheduling check behavior.
MPTUninstallForBundle(mptMailBundle)
MPTCheckForUpdatesForBundle(mptMailBundle)
MPTCheckForUpdatesForBundleNow(mptMailBundle)
MPTSendCrashReportsForBundle(mptMailBundle)
MPTUpdateAndSendReportsForBundle(mptMailBundle)
MPTUpdateAndSendReportsForBundleNow(mptMailBundle)
You need to pass in the NSBundle for your plugin, so it can properly determine the bundle identifier and it's path.
The following variations on these allow you to send additional dictionary values along with the standard Sparkle values to your server feed.
MPTCheckForUpdatesForBundleSparkleDict(mptMailBundle, sparkleDict)
MPTCheckForUpdatesForBundleNowSparkleDict(mptMailBundle, sparkleDict)
MPTUpdateAndSendReportsForBundleSparkleDict(mptMailBundle, sparkleDict)
MPTUpdateAndSendReportsForBundleNowSparkleDict(mptMailBundle, sparkleDict)
The sparkleDict needs to be a dictionary that can be encoded using the +[NSJSONSerialization dataWithJSONObject:options:error:] method.
Please Note that if any of these macros cannot find the MailPluginTool, they will present a modal dialog to the user asking if they want to install Mail Plugin Manager to regain the updating feature. Not other actions will be taken in this case.
These macros are to be used in conjunction with the Update "Now" macros from the above list and allow you to properly display a dialog telling the user that the current plugin is up to date. You have an option of a modal version or a sheet version (pass in the window to put the sheet on). An optional block can be passed as well, that will be run upon completion of checking for an update.
MPTPresentModalDialogWhenUpToDate(mptBundle)
MPTPresentModalDialogWhenUpToDateWithBlock(mptBundle, mptFinishBlock)
MPTPresentDialogWhenUpToDateUsingWindow(mptBundle, mptSheetWindow, mptFinishBlock)
The block argument is the block that you want to run when the results are returned. It is defined as a MPTUpdateTestingCompleteBlock and has been typedef'd like this:
typedef void(^MPTUpdateTestingCompleteBlock)(void);
The following macro is a helper to ensure that if an installation happens for an update that you close the preferences window before the install and restart of Mail. This will give the user a better experience, since I've found that often the Preferences window doesn't redisplay the newly installed plugin properly (old version info or missing icon).
MPTClosePrefsWindowIfInstalling(mptBundle)
You do not need to use any of these if you call on of the "Now" macros above, but they allow you to have a more integrated feeling to the update check. If you do want to use one of these, simply put the "Present" macro before the call to the update check and it will wait for a notification from the tool.
If the MailPluginTool cannot be found, all of these macros will not watch for the distributed notifications as normal and will simply be ignored since they would just be constantly waiting for a reply. The MPTPresentModalDialogWhenUpToDateWithBlock and MPTPresentDialogWhenUpToDateUsingWindow macros will run the block passed in, if it is not null, allowing you to clean up any UI that was prepped before, such as progress indicators.
These macros are to be used to create a LaunchAgent that the plugin might need to configure in order to perfomr some actions outside of the sandbox.
MPTInstallLaunchAgent(mptMailBundle, mptAgentConfig, mptReplaceExisting, mptResultBlock)
MPTRemoveLaunchAgent(mptMailBundle, mptAgentLabel, mptResultBlock)
The first argument is the bundle of your plugin, as with the macros above. The last argument is the block that you want to run to notify you of success or failure. It is optional – pass nil if you don't care. It is defined as a MPTLaunchResultBlock and has been typedef'd like this:
typedef void(^MPTLaunchResultBlock)(NSError *);
It takes the single argument which is an error. It is nil if successful and will contain a reasonable error description if any issues incurred.
For MPTInstallLaunchAgent the second argument is a NSDictionary with the contents of the LaunchAgent file you need to create. See the launchd man page for more information about the contents of this dictionary. The mptReplaceExisting argument is a BOOL to indicate if the Tool should remove an existing agent with the same Label and replace it.
For MPTRemoveLaunchAgent the second argument is simply the Label for the service to remove. It should be the same as the one passed into the above macro in the mptAgentConfig.
These macros allow you to get information back from MPT by having a block run which is passed a dictionary of the results.
MPTMailInformationForBundleWithBlock(mptMailBundle, mptNotificationBlock)
MPTUUIDListForBundleWithBlock(mptMailBundle, mptNotificationBlock)
The first argument is the bundle of your plugin, as with the macros above. The second argument is the block that you want to run when the results are returned. It is defined as a MPTResultNotificationBlock and has been typedef'd like this:
typedef void(^MPTResultNotificationBlock)(NSDictionary *);
It takes the single argument of the dictionary of results and has a void result.
Macros for the keys for the results in the dictionary are listed below.
If the MailPluginTool cannot be found, these macros will simply call the mptNotificationBlock with nil, since there is no way to get the results.
These are the keys for the results of the uuid list query:
MPT_UUID_COMPLETE_LIST_KEY NSArray of all uuid NSDictionaries
MPT_UUID_LATEST_SUPPORTED_UUID_KEY NSDictionary of latest uuid data supported by Plugin
MPT_UUID_FIRST_UNSUPPORTED_UUID_KEY NSDictionary of 1st uuid data known that the Plugin does **not** support
Each of the above NSDictionary objects should contain the following keys. The OS display keys are for the standard user visible descriptions of the OS, i.e. 10.6.8, 10.7 or 10.7.3. The latest version key is a relative value integer that tells us it if is greater or less than another in the list - makes comparing easier that deciphering the 10.7.3 numbers.
MPT_UUID_TYPE_KEY
MPT_UUID_EARLIEST_OS_VERSION_DISPLAY_KEY
MPT_UUID_LATEST_OS_VERSION_DISPLAY_KEY
MPT_UUID_LATEST_VERSION_KEY
And the value of the MPT_UUID_TYPE_KEY key should be one of:
MPT_UUID_TYPE_VALUE_MAIL Mail.app Type
MPT_UUID_TYPE_VALUE_MESSAGE Message.Framework Type
These are the keys for the results of the system info query:
MPT_SYSINFO_ANONYMOUS_ID_KEY String of Anonymized Unique Identifier
MPT_SYSINFO_HARDWARE_KEY String of hardware identifier
MPT_SYSINFO_SYSTEM_KEY NSDictionary of BUILD & VERSION
MPT_SYSINFO_MAIL_KEY NSDictionary of BUILD, VERSION & UUID
MPT_SYSINFO_MESSAGE_KEY NSDictionary of BUILD, VERSION & UUID
MPT_SYSINFO_INSTALLED_PLUGINS_KEY NSArray of NSDictionaries of NAME, PATH, BUILD & VERSION
MPT_SYSINFO_DISABLED_PLUGINS_KEY NSArray of NSDictionaries of NAME, PATH, BUILD & VERSION
The key macros in capitals above are the following:
MPT_SYSINFO_NAME_KEY
MPT_SYSINFO_PATH_KEY
MPT_SYSINFO_VERSION_KEY
MPT_SYSINFO_BUILD_KEY
MPT_SYSINFO_UUID_KEY
To make your plugin work optimally with MPM and MPT, you should add some specific values to your info.plist. These will determine where to send crash reports, how to handle uninstalls and other features. Below is a list of the keys and a description of what they do.
Required to use the Send Crash Reports feature, see that link for the format of the report:
MPCCrashReportURL URL to your web service to accept crash reports
The rest are all optional, but recommended:
MPCMaxCrashReportsToSend
MPCPluginUsesMailPluginManager
MPCSupplementalSparkleFeedParameters
The MPCMaxCrashReportsToSend key allows you to indicate a maximum of crash reports that you want to send in any one session. This allows you to manage your server storage better. The default value is 20.
The MPCPluginUsesMailPluginManager key identifies your plugin as one that is aware of MPM and MPT and is used during an install to ensure that another uninstaller doesn't remove the apps when you depend on them.
The MPCSupplementalSparkleFeedParameters key allows you to add an array of other keys within your info.plist file that are to be sent as Sparkle feed parameters for the anonymous data.
The following values are used by the apps whenever a plugin is displayed to show the company name, URL and product URL. All of them are optional, but make the presentation nicer. There is a companies.plist file which I try to update with the company name and url based on the reverse domain name of the bundle id, but these values will always override that information.
MPCCompanyName Company name for display
MPCCompanyURL Company URL to link to in Apps
MPCProductDescription Short description of the plugin and what it does
MPCProductURL Product URL to link to in Apps
© 2011-2014 Little Known Software, Inc.
The Mail Plugin Manager has a very particular license. The code is not being made available as open source (though if you would like to contribute to the code, please contact me at scott@littleknownsoftware.com), but the executables are being made freely available for developers to use, with the following single condition:
- You may not resign the Mail Plugin Manager or MailPluginTool applications for any reason
The reason for this is complex, but one thing is sure, the app will terminate if the signature is not the original. The reason for this restriction is to avoid confusion for the end user. Since the application is a Developer ID signed application, if it is resigned with a different ID, then the updates will not work properly and the effectiveness of the app will be diminished tremendously. Thus to simply avoid these kinds of issues, the code has not been made available and the application validates that it is signed with a Developer ID from Little Known Software, Inc.
To be clear, this restriction does not apply to the Installer, in fact for it to work best for your installation, you should resign that application with your Developer ID.
Beyond this restriction, you are free to use the executables and their package contents and distribute it with your plugins as needed.