MAPIStore 10 Development Guide

Version 39 (Julien Kerihuel, 2012-03-15 03:16 pm)

1 1 Julien Kerihuel
h1. MAPIStore 1.0 Development Guide
2 1 Julien Kerihuel
3 3 Julien Kerihuel
{{>toc}}
4 3 Julien Kerihuel
5 1 Julien Kerihuel
h2. What is MAPIStore?
6 1 Julien Kerihuel
7 2 Julien Kerihuel
MAPIStore is the SAL component of OpenChange server. SAL stands for Storage Abstraction Layer. It is the component used by OpenChange Server to push/get information (messages, folders) to/from storage backends. It is designed as a library called within OpenChange Server and it accesses backends compiled as dynamic shared object (DSO) and loaded when mapistore is initialized.
8 1 Julien Kerihuel
9 1 Julien Kerihuel
The main objective of mapistore is to provide an interface layer with a common set of atomic functions (operations) used to trigger and dispatch data and commands to the appropriate backend. MAPIStore relies on a backend mechanism specifically designed to transparently handle some of the MAPI semantics required by any Exchange compatible server.
10 1 Julien Kerihuel
11 1 Julien Kerihuel
The initial idea was to provide to OpenChange a highly customizable storage backend mechanism which would fit in any situation and any environments. One of the greatest limitation we have found with existing groupware is the storage layer which is generally limited to a single solution, service or format and is neither scalable nor modifiable when user requirements evolve upon time.
12 1 Julien Kerihuel
13 1 Julien Kerihuel
MAPIStore solves this problem and go beyond classical limitations. It is not a revolutionary concept, but the way openchange uses it makes the whole difference and offer administrators an innovative way to customize storage.
14 1 Julien Kerihuel
15 1 Julien Kerihuel
MAPIStore allows you to:
16 1 Julien Kerihuel
* use a different backend for any top-folder
17 1 Julien Kerihuel
* transparently move/copy data across backends
18 1 Julien Kerihuel
* develop new backends quickly
19 1 Julien Kerihuel
* access all the different backends through an unique API
20 1 Julien Kerihuel
21 1 Julien Kerihuel
For example (assuming all associated backends were developed) a user could have the following storage organization for his mailbox:
22 1 Julien Kerihuel
* Mails stored using an IMAP backend (Cyrus-IMAP or dovecot)
23 1 Julien Kerihuel
* Calendar items stored in CalDAV or pushed in Google calendar
24 1 Julien Kerihuel
* Sent emails and archives/backup stored in a compression backend
25 1 Julien Kerihuel
* Tasks stored in a MySQL database
26 1 Julien Kerihuel
* Notes stored on the filesystem
27 1 Julien Kerihuel
28 1 Julien Kerihuel
If the user is not satisfied with one of the backend's performance, they would just have to use an administration tool, change the backend, wait for the replication, synchronization to finish and there data will be available from the new backend.
29 1 Julien Kerihuel
30 1 Julien Kerihuel
Information can be completely decentralized, stored on one of several servers and still be accessible transparently from OpenChange server.
31 3 Julien Kerihuel
32 3 Julien Kerihuel
h2. Getting started
33 3 Julien Kerihuel
34 4 Julien Kerihuel
h3. Getting Source code
35 4 Julien Kerihuel
36 1 Julien Kerihuel
<pre>
37 37 Julien Kerihuel
svn co http://svnmirror.openchange.org/openchange/trunk
38 3 Julien Kerihuel
</pre>
39 1 Julien Kerihuel
40 4 Julien Kerihuel
Refer to [[HowTo Install OpenChange From Source]] (but use svn command above instead of trunk) for instructions on requirements and how to build openchange. 
41 4 Julien Kerihuel
42 4 Julien Kerihuel
This branch also relies on the sogo backend developed by inverse.ca which provides the most up to date backend's implementation using mapistore v1. 
43 4 Julien Kerihuel
For instructions on how to download and build the backend, refers to [[HowTo Setup SOGo with OpenChange Server]].
44 3 Julien Kerihuel
45 3 Julien Kerihuel
h3. Why isn't mapistore v1 available in trunk?
46 3 Julien Kerihuel
47 3 Julien Kerihuel
Back in February 2010, we have decided to move from our existing mapistore v1 interface to a brand new and completely redesigned mapistore v2 interface. This new interface was introducing new concepts, fixing limitation of v1 implementation. This was a massive work and while it started very well, the effort was too big for the small number of people working on it and became abandoned, while v1 implementation kept progressing.
48 3 Julien Kerihuel
49 3 Julien Kerihuel
It means that mapistore v2 in trunk is not usable in current state and will progressively be replaced step by step with an updated version of mapistore v1 covered in this documentation, introducing new concepts progressively.
50 5 Julien Kerihuel
51 5 Julien Kerihuel
52 5 Julien Kerihuel
h2. MAPIStore architecture overview
53 5 Julien Kerihuel
54 5 Julien Kerihuel
h3. URI and scope
55 5 Julien Kerihuel
56 5 Julien Kerihuel
A URI is the key (and only) element mapistore and calling applications rely on in order to access and perform operation on a specific backend.
57 5 Julien Kerihuel
58 5 Julien Kerihuel
It is made of a prefixing namespace followed by data specific to the backend:
59 6 Julien Kerihuel
* *namespace*: This is the unique identifier associated to your backend. It is made of your backend's name followed by *://*
60 5 Julien Kerihuel
* *backend's specific data*: This is a set of data that doesn't have any meaning for the calling application but is relevant for the backend. While the format of this data is completely specific to your backend, backends generally set information such as username, password or folder name within the destination system the backend manages.
61 5 Julien Kerihuel
62 5 Julien Kerihuel
<pre>
63 5 Julien Kerihuel
{namespace}{backend's specific data}
64 5 Julien Kerihuel
65 1 Julien Kerihuel
sogo://user:password@folder/
66 6 Julien Kerihuel
java://user##folder/
67 5 Julien Kerihuel
</pre>
68 5 Julien Kerihuel
69 29 Julien Kerihuel
h3. Folder and Message identifiers uniqueness
70 29 Julien Kerihuel
71 29 Julien Kerihuel
In Exchange world, folder and message identifiers - respectively called FID and MID - are unsigned 64-bit integers that represents a physical object (folder, message) on the server. 
72 29 Julien Kerihuel
This is the only link clients and OpenChange server are using to refer to an object. Given that identifiers are stored on 64-bit integers, there is a virtual allocation pool of 2^16 elements (folder, messages) to cover all users needs registered on the server for its lifetime.
73 29 Julien Kerihuel
74 29 Julien Kerihuel
*Folder and Messages IDs are unique to the entire system*.
75 29 Julien Kerihuel
76 29 Julien Kerihuel
Furthermore all these ids are only used once and can't be allocated again for another (new) object. This is a requirement to get offline synchronization (FXICS) working properly and prevent form collision with paste elements.
77 29 Julien Kerihuel
78 29 Julien Kerihuel
79 5 Julien Kerihuel
h3. Contexts
80 5 Julien Kerihuel
81 7 Julien Kerihuel
An Exchange user mailbox is (roughly) represented as a top folder (The Mailbox name) holding root (sub) folders (Inbox, Outbox, Calendar, Contacts, Notes, Tasks etc.). In MAPIStore, we consider that we shouldn't have to use the same backend for everything. We should be able to use a backend for Inbox, another for Calendar and so on.
82 7 Julien Kerihuel
83 7 Julien Kerihuel
This is where the notion of mapistore context steps in. A context is a sandbox created by a backend when you access root folder. For every operation you perform on this root folder or any data within this folder, the same created context will be used. For example, if we want to open the message _Rates_ in /Inbox/Holidays/, we will:
84 7 Julien Kerihuel
# Create a context on Inbox (it opens the folder)
85 7 Julien Kerihuel
# Open the Holidays folder using the Inbox context
86 7 Julien Kerihuel
# Access the message using the Inbox context
87 8 Julien Kerihuel
88 8 Julien Kerihuel
If the backend attached to my Inbox points to an IMAP server, all the operation performed on Inbox and subfolders will be propagated to this IMAP server. In this case, creating a context would mean:
89 8 Julien Kerihuel
# Open a connection on the IMAP server
90 8 Julien Kerihuel
# Open the Inbox folder
91 8 Julien Kerihuel
# Keep the connection opened and save parameters this IMAP backend needs to access/interact with physical (folder, message) or virtual (tables) elements stored within this Inbox folder.
92 9 Julien Kerihuel
93 9 Julien Kerihuel
h3. openchange.ldb URI wrapper
94 9 Julien Kerihuel
95 9 Julien Kerihuel
We have just seen that a context has to be created when we access a root folder and the only key we need to call the proper backend and create the context is the URI.
96 9 Julien Kerihuel
97 9 Julien Kerihuel
It means we need a mapping to associate the Inbox folder of a user to a specific URI (e.g.: imap://user:pass@Inbox/). This is done through the openchange.ldb database which maintains the mapping between Exchange mailbox root folders and URI used to access them.
98 9 Julien Kerihuel
99 9 Julien Kerihuel
This file does not (yet) belongs to mapistore, but is widely used by OpenChange Server when serving users mailboxes. It is created within _/usr/local/samba/private/openchange.ldb_ when OpenChange server is provisioned and can be read/edited using ldbedit installed in _/usr/local/samba/bin/ldbedit_.
100 9 Julien Kerihuel
101 9 Julien Kerihuel
<pre>
102 9 Julien Kerihuel
/usr/local/samba/bin/ldbedit -H /usr/local/samba/private/openchange.ldb
103 9 Julien Kerihuel
</pre> 
104 10 Julien Kerihuel
105 18 Julien Kerihuel
h3. indexing.tdb URI/FMID wrapper
106 10 Julien Kerihuel
107 18 Julien Kerihuel
What we call FMID is a shortcut combination of FID (Folder Identifier) and MID (Message Identifier). It means either Folder or Message identifier.
108 18 Julien Kerihuel
109 18 Julien Kerihuel
While _openchange.ldb_ handles the mapping between Mailbox root folders and mapistore URI, *indexing.tdb handles the mapping for all the other elements (folder, message) of a user that are NOT root folders*.
110 18 Julien Kerihuel
111 18 Julien Kerihuel
Each time a user creates a folder within a root folder (Inbox, Calendar etc.) or create a message (Appointment, Drafts, Task etc.), the FID or MID is associated to a mapistore URI within the indexing.tdb database of the user. It also means that indexing.tdb is user specific and each user of the system has its own indexing.tdb. This database is similar to a hash table: it stores the FID/MID as key and the mapistore URI as value.
112 18 Julien Kerihuel
113 18 Julien Kerihuel
To make things clear:
114 18 Julien Kerihuel
* openchange.ldb indexes and map root folders of user mailboxes and is common to all users
115 18 Julien Kerihuel
* indexing.tdb indexes everything else but one database is created for each user
116 18 Julien Kerihuel
117 18 Julien Kerihuel
118 10 Julien Kerihuel
h2. MAPIStore Backend
119 10 Julien Kerihuel
120 10 Julien Kerihuel
A backend has to be developed as a dynamic shared library and installed in the folder mapistore uses to look up and load backends:
121 10 Julien Kerihuel
<pre>
122 10 Julien Kerihuel
/usr/local/samba/lib/mapistore_backends/
123 10 Julien Kerihuel
</pre>
124 10 Julien Kerihuel
125 10 Julien Kerihuel
For mapistore to be able to load the backend, it is required to implement an entry point called *mapistore_init_backend*.
126 10 Julien Kerihuel
127 10 Julien Kerihuel
This function fills a mapistore backend structure and registers it. This makes backends available for the initialized mapistore instance.
128 11 Julien Kerihuel
All the required parameters are grouped into sub structures, but the key one to proper backend registration is the backend one. 
129 1 Julien Kerihuel
130 11 Julien Kerihuel
A very preliminary backend would looks like:
131 1 Julien Kerihuel
<pre>
132 11 Julien Kerihuel
#include <stdbool.h>
133 11 Julien Kerihuel
#include <talloc.h>
134 11 Julien Kerihuel
#include <gen_ndr/exchange.h>
135 11 Julien Kerihuel
#include <mapistore/mapistore.h>
136 11 Julien Kerihuel
#include <mapistore/mapistore_errors.h>
137 1 Julien Kerihuel
138 11 Julien Kerihuel
static int example_backend_init(void)
139 11 Julien Kerihuel
{
140 11 Julien Kerihuel
     return MAPISTORE_SUCCESS;
141 11 Julien Kerihuel
}
142 11 Julien Kerihuel
143 11 Julien Kerihuel
static int example_backend_create_context(TALLOC_CTX *mem_ctx,
144 11 Julien Kerihuel
                                          struct mapistore_connection_info *conn_info,
145 11 Julien Kerihuel
                                          struct tdb_wrap *indexing_tdb,
146 11 Julien Kerihuel
                                          const char *uri,
147 11 Julien Kerihuel
                                          void **context_object)
148 11 Julien Kerihuel
{
149 11 Julien Kerihuel
    return MAPISTORE_SUCCESS;
150 11 Julien Kerihuel
}
151 11 Julien Kerihuel
152 10 Julien Kerihuel
int mapistore_init_backend(void)
153 1 Julien Kerihuel
{
154 1 Julien Kerihuel
     struct mapistore_backend backend;
155 1 Julien Kerihuel
     int                      ret;
156 14 Julien Kerihuel
     static bool              registered = false;
157 1 Julien Kerihuel
158 14 Julien Kerihuel
     if (registered == true) return MAPISTORE_SUCCESS;
159 14 Julien Kerihuel
160 11 Julien Kerihuel
     backend.backend.name = "Example";
161 11 Julien Kerihuel
     backend.backend.description = "An example backend";
162 11 Julien Kerihuel
     backend.backend.namespace = "example://";
163 11 Julien Kerihuel
     backend.backend.init = example_backend_init;
164 11 Julien Kerihuel
     backend.backend.create_context = example_backend_create_context;
165 10 Julien Kerihuel
166 10 Julien Kerihuel
     [...]
167 10 Julien Kerihuel
168 10 Julien Kerihuel
     ret = mapistore_backend_register(&backend);
169 10 Julien Kerihuel
     if (ret != MAPISTORE_SUCCESS) {
170 10 Julien Kerihuel
        DEBUG(0, ("Failed to register the '%s' mapistore backend!\n", backend.backend.name));
171 10 Julien Kerihuel
     }
172 10 Julien Kerihuel
173 14 Julien Kerihuel
     registered = true;
174 14 Julien Kerihuel
175 10 Julien Kerihuel
     return ret;
176 10 Julien Kerihuel
}
177 10 Julien Kerihuel
178 10 Julien Kerihuel
</pre>
179 10 Julien Kerihuel
180 30 Julien Kerihuel
h3. void * and void ** parameters in backend calls
181 30 Julien Kerihuel
182 30 Julien Kerihuel
These are parameters passed along mapistore that are initially instantiated and manager by backends. 
183 30 Julien Kerihuel
These structures are specific to the backend and OpenChange server never makes any use of this data nor does it care about its content. It just passes it along further mapistore calls whenever necessary.
184 30 Julien Kerihuel
185 30 Julien Kerihuel
From a general pespective:
186 30 Julien Kerihuel
* _void *_ is an existing object the backend already instantiated and which mapistore pass along to the backend. The nature of the object depends on the mapistore call made.
187 30 Julien Kerihuel
* _void **_ is a new object returned by the backend and which will be used in further mapistore call whenever it relates to the correct subset of operations.
188 30 Julien Kerihuel
189 31 Julien Kerihuel
MAPIStore v1 provides an object-oriented implementation and offers backend the ability to pass objects along. Objects depends on each other. For example:
190 31 Julien Kerihuel
* add_context creates a context object
191 31 Julien Kerihuel
* root_folder returns the folder object of a context object (just morphing)
192 31 Julien Kerihuel
* open_folder takes the parent folder object returns the request folder object
193 31 Julien Kerihuel
* open_message: takes a folder object and returns a message object
194 31 Julien Kerihuel
195 33 Julien Kerihuel
Backends are sole responsible for these handling these objects. They can create and store any data structure to keep and save information among call and find data they need. They can also store pointers on function associated to the object to make direct calls within the object context rather than having to maintain an internal list.
196 33 Julien Kerihuel
197 34 Julien Kerihuel
While the content of the object is specific to the backend, it is however require to create and use them since most mapistore calls don't provide any context but these specific backend void pointers.
198 32 Julien Kerihuel
199 25 Julien Kerihuel
h3. [[MAPIStore 1.0 backend.backend structure|backend.backend structure]]
200 10 Julien Kerihuel
201 27 Julien Kerihuel
h3. [[MAPIStore 1.0 backend.context structure|backend.context structure]]
202 21 Julien Kerihuel
203 35 Julien Kerihuel
h3. [[MAPIStore 1.0 backend.folder structure|backend.folder structure]]
204 35 Julien Kerihuel
205 36 Julien Kerihuel
h3. [[MAPIStore 1.0 backend.message structure|backend.message structure]]
206 36 Julien Kerihuel
207 36 Julien Kerihuel
h3. [[MAPIStore 1.0 backend.table structure|backend.table structure]]
208 36 Julien Kerihuel
209 36 Julien Kerihuel
h3. [[MAPIStore 1.0 backend.properties structure|backend.properties structure]]
210 36 Julien Kerihuel
211 28 Julien Kerihuel
h3. [[MAPIStore 1.0 backend.manager structure|backend.manager structure]]
212 38 Julien Kerihuel
213 38 Julien Kerihuel
214 39 Julien Kerihuel
215 38 Julien Kerihuel
h2. MAPIStore concepts
216 38 Julien Kerihuel
217 38 Julien Kerihuel
h3. [[MAPIStore 1.0 Implementing cached mode support | cached mode support in backends]]