TL;DR
- WebDav
contents.xml
out of.zip
file full contents at the bottom of the post- Follow me on Twitter https://twitter.com/ashleyhindle
What is OmniFocus?
One of the best GTD/task management systems around: https://www.omnigroup.com/omnifocus
Part of building TodoMove (now not live) means I need to integrate as many task management systems as I can; unfortunately OmniFocus doesn't provide an API so this has been troublesome, hence this blog post.
OmniSync
OmniFocus hosts its own servers to provide syncing between devices.
The main URL for their sync service is https://sync.omnigroup.com and each user also has their own URL with Basic Access
Authentication at https://sync5.omnigroup.com/username
You can go to this URL in your browser to view your files, or you can use cadaver to browse it interactively: cadaver https://sync5.omnigroup.com/username/
It feels as if users are sharded across servers as my data is on sync5 as far as I can tell. sync.omnigroup.com
helpfully redirects you to the right server for your data.
You can find the sync server by running curl https://sync.omnigroup.com/username/
or going to https://sync.omnigroup.com/username/ in your browser
WebDav
The main folder of interest in your WebDav sync is OmniFocus.ofocus
, which houses these files of interest (FOI)
OmniFocus.ofocus/encrypted
- XML file with decryption key (pasted below)OmniFocus.ofocus/00000000000000=.{23}.zip
- Encrypted (if you're on a recent version) file, withcontents.xml
insideOmniFocus.ofocus/20161113150820=.{11}.client
- Details per client (pasted below)
OmniFocus.ofocus/encrypted
Has a key to decrypt the encrypted files
<plist version="1.0">
<array>
<dict>
<key>algorithm</key>
<string>PBKDF2; aes128-wrap</string>
<key>key</key>
<data>
ZK8X5pXH86rnZBxxxxxxxxnaDXLT0/5dLQ3vFpcvbSbOjhpJQcpMS6mSVwzO m61zkAxfNVsc6ualgXDfV9qkOgVz/Hek3l3RctFxnK251WxjdkfnRueiOx==
</data>
<key>method</key>
<string>password</string>
<key>rounds</key>
<integer>1000000</integer>
<key>salt</key>
<data>EAFZAEAheQ3drToNPkl6YYN6ExX=</data>
</dict>
</array>
</plist>
OmniFocus.ofocus/00000000000000=.{23}.zip
This file stores contents.xml
which is the full database of OmniFocus items including perspectives, perspective order, default dates, default times, default recurrences, folders, projects, contexts, tasks and more.
It also includes a data
directory which, so far, I've only seen used to store custom images for perspectives.
contents.xml
The most useful file available (stores every bit of information OmniFocus uses); has multiple top level elements. Full example at the bottom of this post.
Top level items:
setting
- Settings for the appcontext
- Children:added
,modified
,name
,rank
folder
- Children:added
,modified
,name
,rank
,hidden
,folder
(if it's a sub folder, thenfolder
will have anidref
of the parent folder)task
- Task or project. If it has aproject
element, then it's actually a project. Project Children:project
(optional),added
,modified
,name
,rank
,context
(withidref
),order
(parallel, sequential, single action),completed-by-children
-
- If project:
project
has its own children:
- If project:
-
-
folder
(withidref
)
-
-
-
singleton
(bool)
-
-
-
last-review
-
-
-
next-review
-
-
-
review-interval
-
-
- If task:
-
-
inbox
(bool)
-
-
-
start
-
-
-
due
-
-
-
completed
-
-
-
estimated-minutes
-
-
-
repetition-rule
(RRULE)
-
-
-
repetition-method
-
-
-
flagged
-
perspective
- View XML at the bottom of the page
OmniFocus.ofocus/20161113150820=.{11}.client
<plist version="1.0">
<dict>
<key>CurrentFrameworkVersion</key>
<string>2</string>
<key>HardwareCPUCount</key>
<string>4</string>
<key>HardwareCPUType</key>
<string>7,8</string>
<key>HardwareCPUTypeDescription</key>
<string>Intel x86-64h Haswell</string>
<key>HardwareCPUTypeName</key>
<string>x86_64h</string>
<key>HardwareModel</key>
<string>MacBookPro11,1</string>
<key>OFMSyncClientModelVersion</key>
<string>4.4</string>
<key>OFMSyncClientSupportedCapabilities</key>
<array>
<string>delta_transactions</string>
</array>
<key>OSVersion</key>
<string>16A323</string>
<key>OSVersionNumber</key>
<string>10.12</string>
<key>bundleIdentifier</key>
<string>com.omnigroup.OmniFocus2</string>
<key>bundleVersion</key>
<string>109.13.0.270584</string>
<key>clientIdentifier</key>
<string>iOhyJVnxxX</string>
<key>hostID</key>
<string>XXXXXXXX-B1XE-XXXX-BDXF-8492XXXXXXXX</string>
<key>lastSyncDate</key>
<date>2016-11-13T15:08:20Z</date>
<key>name</key>
<string>Ashleys-MacBook-Pro.local</string>
<key>registrationDate</key>
<date>2015-05-20T18:34:41Z</date>
<key>tailIdentifiers</key>
<array>
<string>hX7xfxX5uxx</string>
</array>
</dict>
</plist>
Helpful links and commands
Download your OmniFocus.ofocus
folder
wget -r -nH -np --cut-dirs=1 --no-check-certificate -U Mozilla --user=[username] --password=[password] https://sync5.omnigroup.com/[username]/OmniFocus.ofocus
Decrypt your OmniFocus.ofocus/*.zip
file
You may need to remove the 3.5
from these commands if you're not on a Mac. The decryption script requires at least Python 3.4.
brew install python3
pip3.5 install cryptography
python3.5 DecryptionExample.py -o newFolderForDecryptedContent OmniFocus.ofocus
Links
- https://github.com/omnigroup/OmniGroup/blob/master/Frameworks/OmniFileStore/OFSEncryption-Internal.m
- https://github.com/omnigroup/OmniGroup/blob/master/Frameworks/OmniFileStore/DecryptionExample.py - Python code to decrypt the
OmniFocus.ofocus
folder - https://github.com/omnigroup/OmniGroup/blob/master/Frameworks/OmniFileStore/EncryptionFormat.md
- https://github.com/TodoMove/omnifocus - Read an OmniFocus backup with PHP
- https://github.com/TodoMove/intercessor - PHP Classes to store all items a task management system needs (OmniFocus reader converts all items to Intercessor objects)
contents.xml
example
This is a full contents.xml
file, pulled from an OmniFocus backup of a test setup.
<?xml version="1.0" encoding="UTF-8"?>
<omnifocus xmlns="http://www.omnigroup.com/namespace/OmniFocus/v2" app-id="com.omnigroup.OmniFocus2" app-version="109.13.0.270584" os-name="Mac OS X" os-version="10.12" machine-model="MacBookPro11,1">
<setting id="OFMDefaultSingletonProjectPersistentIdentifier">
<added order="1">2011-12-07T10:10:26.499Z</added>
<modified>2015-05-20T18:35:06.551Z</modified>
<plist version="1.0">
<string>com.omnigroup.frameworks.OmniFocusModel.default_singleton_project.create_on_demand</string>
</plist>
</setting>
<setting id="OFMRequiredRelationshipToProcessInboxItem">
<added>2016-11-13T14:46:22.757Z</added>
<plist version="1.0">
<string>project</string>
</plist>
</setting>
<setting id="OFMRemoteNotificationGroupID">
<added>2016-11-13T14:46:31.274Z</added>
<plist version="1.0">
<string>xxxxxxxxxxxxxxxx442cac795a67ec4d3f9d973f8f942ce1xxxxxxxxxxxxxxxx</string>
</plist>
</setting>
<setting id="OFMRemoteNotificationGroupIDLastRegenerationDate">
<added order="1">2016-11-13T14:46:31.274Z</added>
<plist version="1.0">
<date>2016-11-13T14:46:28Z</date>
</plist>
</setting>
<context id="abced123456">
<added>2011-12-07T10:06:37.272Z</added>
<modified>2016-03-21T21:58:36.582Z</modified>
<name>Custom built context</name>
<rank>2</rank>
</context>
<context id="bxp13MmTXLG">
<added>2011-12-07T10:06:37.272Z</added>
<modified>2016-03-21T21:58:36.582Z</modified>
<name>People</name>
<rank>1</rank>
</context>
<folder id="pOzQMpLcHxw">
<added>2015-05-26T20:15:30.082Z</added>
<modified>2016-08-12T16:51:51.456Z</modified>
<name>Me</name>
<rank>-1968526678</rank>
</folder>
<folder id="i1804qvPS3I">
<folder idref="pOzQMpLcHxw" />
<added>2015-05-26T20:20:43.166Z</added>
<modified>2016-03-23T09:04:53.348Z</modified>
<name>2016 Goals</name>
<rank>0</rank>
<hidden>false</hidden>
</folder>
<task id="axFWYCAeu8J">
<project>
<folder idref="i1804qvPS3I" />
<singleton>true</singleton>
<last-review>2015-05-25T23:00:00.000Z</last-review>
<next-review>2015-06-01T23:00:00.000Z</next-review>
<review-interval>@1w</review-interval>
</project>
<added>2015-05-26T20:20:54.468Z</added>
<modified>2016-11-13T17:50:22.955Z</modified>
<name>Rule the world</name>
<rank>0</rank>
<context idref="abced123456" />
<order>parallel</order>
<completed-by-children>true</completed-by-children>
</task>
<task id="lTpFc67l5UV">
<project />
<inbox>true</inbox>
<task />
<added>2016-11-13T17:27:42.849Z</added>
<name>Write blog post on OmniFocus syncing/formats</name>
<note />
<rank>0</rank>
<context />
<start />
<due />
<completed />
<estimated-minutes />
<order>parallel</order>
<flagged>false</flagged>
<completed-by-children>false</completed-by-children>
<repetition-rule />
<repetition-method />
<modified>2016-11-13T17:27:51.939Z</modified>
</task>
<task id="naFwlopkAFE">
<project />
<inbox>true</inbox>
<task />
<added>2016-11-13T17:27:46.420Z</added>
<name>Win the lotto</name>
<note />
<rank>1073741824</rank>
<context />
<start />
<due />
<completed />
<estimated-minutes />
<order>parallel</order>
<flagged>false</flagged>
<completed-by-children>false</completed-by-children>
<repetition-rule />
<repetition-method />
<modified>2016-11-13T17:27:55.189Z</modified>
</task>
<task id="k-tP-F1yqFe">
<project />
<inbox>false</inbox>
<task idref="axFWYCAeu8J" />
<added>2016-11-13T17:49:59.024Z</added>
<name>Actual action</name>
<note>
<text>
<p>
<run>
<lit>My note is on fire</lit>
</run>
</p>
</text>
</note>
<rank>0</rank>
<context idref="bxp13MmTXLG" />
<start>2016-11-14T00:00:00.000Z</start>
<due>2016-11-20T17:00:00.000Z</due>
<completed />
<estimated-minutes />
<order>parallel</order>
<flagged>false</flagged>
<completed-by-children>false</completed-by-children>
<repetition-rule>FREQ=WEEKLY;BYDAY=WE,FR</repetition-rule>
<repetition-method>fixed</repetition-method>
<modified>2016-11-13T17:50:22.955Z</modified>
<repeat>@1w</repeat>
</task>
<perspective id="ProcessFlagged">
<added>2016-11-13T14:46:27.490Z</added>
<plist version="1.0">
<dict>
<key>iconNameInBundle</key>
<string>ProcessFlagged</string>
<key>name</key>
<string>Flagged</string>
<key>uuid</key>
<string>ProcessFlagged</string>
<key>viewState</key>
<dict>
<key>viewMode</key>
<string>context</string>
<key>viewModeState</key>
<dict>
<key>context</key>
<dict>
<key>actionCompletionFilter</key>
<string>incomplete</string>
<key>actionDurationFilter</key>
<string>any</string>
<key>actionFlaggedFilter</key>
<string>flagged</string>
<key>collation</key>
<string>context</string>
<key>firstResponder</key>
<string>actionOutline</string>
<key>sidebarFilter</key>
<string>available-contexts</string>
<key>sort</key>
<string>due</string>
</dict>
</dict>
</dict>
</dict>
</plist>
<icon-attachment />
</perspective>
<perspective id="ProcessInbox">
<added order="1">2016-11-13T14:46:27.490Z</added>
<plist version="1.0">
<dict>
<key>iconNameInBundle</key>
<string>ProcessInbox</string>
<key>name</key>
<string>Inbox</string>
<key>useSavedSelection</key>
<true />
<key>uuid</key>
<string>ProcessInbox</string>
<key>viewState</key>
<dict>
<key>viewMode</key>
<string>project</string>
<key>viewModeState</key>
<dict>
<key>project</key>
<dict>
<key>actionCompletionFilter</key>
<string>incomplete</string>
<key>actionDurationFilter</key>
<string>any</string>
<key>actionFlaggedFilter</key>
<string>all</string>
<key>collation</key>
<string>none</string>
<key>firstResponder</key>
<string>actionOutline</string>
<key>sidebar</key>
<dict>
<key>selected</key>
<array>
<array>
<string>com.omnigroup.omnifocus.live_fetch.inbox</string>
</array>
</array>
</dict>
<key>sidebarFilter</key>
<string>remaining-projects</string>
<key>sort</key>
<string>none</string>
</dict>
</dict>
</dict>
</dict>
</plist>
<icon-attachment />
</perspective>
<perspective id="ProcessRecentChanges">
<added order="2">2016-11-13T14:46:27.490Z</added>
<plist version="1.0">
<dict>
<key>iconNameInBundle</key>
<string>perspectivelist_new_calendar</string>
<key>name</key>
<string>Changed</string>
<key>uuid</key>
<string>ProcessRecentChanges</string>
<key>viewState</key>
<dict>
<key>viewMode</key>
<string>context</string>
<key>viewModeState</key>
<dict>
<key>context</key>
<dict>
<key>actionCompletionFilter</key>
<string>all</string>
<key>actionDurationFilter</key>
<string>any</string>
<key>actionFlaggedFilter</key>
<string>all</string>
<key>collation</key>
<string>modified</string>
<key>firstResponder</key>
<string>actionOutline</string>
<key>sidebarFilter</key>
<string>all-contexts</string>
<key>sort</key>
<string>modified</string>
</dict>
</dict>
</dict>
</dict>
</plist>
<icon-attachment />
</perspective>
<perspective id="ProcessProjects">
<added order="3">2016-11-13T14:46:27.490Z</added>
<plist version="1.0">
<dict>
<key>iconNameInBundle</key>
<string>ProcessProjects</string>
<key>name</key>
<string>Projects</string>
<key>uuid</key>
<string>ProcessProjects</string>
<key>viewState</key>
<dict>
<key>viewMode</key>
<string>project</string>
<key>viewModeState</key>
<dict>
<key>project</key>
<dict>
<key>actionCompletionFilter</key>
<string>incomplete</string>
<key>actionDurationFilter</key>
<string>any</string>
<key>actionFlaggedFilter</key>
<string>all</string>
<key>collation</key>
<string>none</string>
<key>firstResponder</key>
<string>actionOutline</string>
<key>sidebar</key>
<dict>
<key>selected</key>
<array>
<array>
<string>com.omnigroup.omnifocus.project_sidebar.library</string>
</array>
</array>
</dict>
<key>sidebarFilter</key>
<string>remaining-projects</string>
<key>sort</key>
<string>none</string>
</dict>
</dict>
</dict>
</dict>
</plist>
<icon-attachment />
</perspective>
<perspective id="ProcessDueSoon">
<added order="4">2016-11-13T14:46:27.490Z</added>
<plist version="1.0">
<dict>
<key>iconNameInBundle</key>
<string>ProcessDueSoon</string>
<key>name</key>
<string>Due</string>
<key>uuid</key>
<string>ProcessDueSoon</string>
<key>viewState</key>
<dict>
<key>viewMode</key>
<string>context</string>
<key>viewModeState</key>
<dict>
<key>context</key>
<dict>
<key>actionCompletionFilter</key>
<string>due</string>
<key>actionDurationFilter</key>
<string>any</string>
<key>actionFlaggedFilter</key>
<string>all</string>
<key>collation</key>
<string>due</string>
<key>firstResponder</key>
<string>actionOutline</string>
<key>sidebarFilter</key>
<string>available-contexts</string>
<key>sort</key>
<string>due</string>
</dict>
</dict>
</dict>
</dict>
</plist>
<icon-attachment />
</perspective>
<perspective id="ProcessCompleted">
<added order="5">2016-11-13T14:46:27.490Z</added>
<plist version="1.0">
<dict>
<key>iconNameInBundle</key>
<string>ProcessCompleted</string>
<key>name</key>
<string>Completed</string>
<key>uuid</key>
<string>ProcessCompleted</string>
<key>viewState</key>
<dict>
<key>viewMode</key>
<string>context</string>
<key>viewModeState</key>
<dict>
<key>context</key>
<dict>
<key>actionCompletionFilter</key>
<string>complete</string>
<key>actionDurationFilter</key>
<string>any</string>
<key>actionFlaggedFilter</key>
<string>all</string>
<key>collation</key>
<string>completed</string>
<key>firstResponder</key>
<string>actionOutline</string>
<key>sidebarFilter</key>
<string>all-contexts</string>
<key>sort</key>
<string>completed</string>
</dict>
</dict>
</dict>
</dict>
</plist>
<icon-attachment />
</perspective>
<perspective id="ProcessReview">
<added order="6">2016-11-13T14:46:27.490Z</added>
<plist version="1.0">
<dict>
<key>iconNameInBundle</key>
<string>ProcessReview</string>
<key>name</key>
<string>Review</string>
<key>uuid</key>
<string>ProcessReview</string>
<key>viewState</key>
<dict>
<key>viewMode</key>
<string>project</string>
<key>viewModeState</key>
<dict>
<key>project</key>
<dict>
<key>actionCompletionFilter</key>
<string>incomplete</string>
<key>actionDurationFilter</key>
<string>any</string>
<key>actionFlaggedFilter</key>
<string>all</string>
<key>collation</key>
<string>next-review</string>
<key>firstResponder</key>
<string>actionOutline</string>
<key>sidebarFilter</key>
<string>remaining-projects</string>
<key>sort</key>
<string>none</string>
</dict>
</dict>
</dict>
</dict>
</plist>
<icon-attachment />
</perspective>
<perspective id="ProcessContexts">
<added order="7">2016-11-13T14:46:27.490Z</added>
<plist version="1.0">
<dict>
<key>iconNameInBundle</key>
<string>ProcessContexts</string>
<key>name</key>
<string>Contexts</string>
<key>uuid</key>
<string>ProcessContexts</string>
<key>viewState</key>
<dict>
<key>viewMode</key>
<string>context</string>
<key>viewModeState</key>
<dict>
<key>context</key>
<dict>
<key>actionCompletionFilter</key>
<string>incomplete</string>
<key>actionDurationFilter</key>
<string>any</string>
<key>actionFlaggedFilter</key>
<string>all</string>
<key>collation</key>
<string>context</string>
<key>firstResponder</key>
<string>actionOutline</string>
<key>sidebarFilter</key>
<string>available-contexts</string>
<key>sort</key>
<string>project</string>
</dict>
</dict>
</dict>
</dict>
</plist>
<icon-attachment />
</perspective>
</omnifocus>