8a67a406cdb8081bec2b414bb87319f28b94ab34
[tridge/openchange.git] / trunk / mapiproxy / libmapistore / database / mapistoredb.c
1 /*
2    OpenChange Storage Abstraction Layer library
3
4    OpenChange Project
5
6    Copyright (C) Julien Kerihuel 2010-2011
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #include "mapiproxy/libmapistore/mapistore_errors.h"
23 #include "mapiproxy/libmapistore/mapistore.h"
24 #include "mapiproxy/libmapistore/mapistore_private.h"
25 #include "mapiproxy/libmapistore/mapistore_common.h"
26
27 /**
28    \file mapistoredb.c
29
30    \brief MAPIStore database provisioning interface
31  */
32
33 int lpcfg_server_role(struct loadparm_context *lp_ctx);
34
35 /**
36    \details Initialize the mapistore database context
37
38    \param mem_ctx pointer to the memory context
39    \param path string pointer to the mapistore database location
40
41    If path if NULL use the default mapistore database path instead.
42
43    \return Allocated mapistore database context on success, otherwise NULL
44  */
45 struct mapistoredb_context *mapistoredb_init(TALLOC_CTX *mem_ctx,
46                                              const char *path)
47 {
48         struct mapistoredb_context      *mdb_ctx;
49         char                            **domaindn;
50         struct stat                     sb;
51         int                             i;
52         int                             ret;
53
54         /* Sanity checks */
55         if (path == NULL) {
56                 path = MAPISTORE_DBPATH;
57         }
58
59         /* Ensure the path is valid */
60         if (stat(path, &sb) == -1) {
61                 perror(path);
62                 return NULL;
63         }
64
65         /* Step 1. Initialize mapistoredb context */
66         mdb_ctx = talloc_zero(mem_ctx, struct mapistoredb_context);
67         mdb_ctx->param = talloc_zero(mem_ctx, struct mapistoredb_conf);
68         if (!mdb_ctx->param) {
69                 MSTORE_DEBUG_ERROR(MSTORE_LEVEL_INFO, "Failed to allocate memory for %s\n", "mdb_ctx->param");
70                 talloc_free(mdb_ctx);
71                 return NULL;
72         }
73
74         /* Step 2. Initialize Samba loadparm context and load default values */
75         mdb_ctx->lp_ctx = loadparm_init(mdb_ctx);
76         lpcfg_load_default(mdb_ctx->lp_ctx);
77
78         /* Step 3. Retrieve default values from smb.conf */
79         mdb_ctx->param->netbiosname = strlower_talloc(mdb_ctx->param, lpcfg_netbios_name(mdb_ctx->lp_ctx));
80         mdb_ctx->param->dnsdomain = strlower_talloc(mdb_ctx->param, lpcfg_realm(mdb_ctx->lp_ctx));
81         mdb_ctx->param->domain = strlower_talloc(mdb_ctx->param, lpcfg_sam_name(mdb_ctx->lp_ctx));
82
83         switch (lpcfg_server_role(mdb_ctx->lp_ctx)) {
84         case ROLE_DOMAIN_CONTROLLER:
85                 domaindn = str_list_make(mdb_ctx->param, mdb_ctx->param->dnsdomain, ".");
86                 strlower_m(domaindn[0]);
87                 mdb_ctx->param->domaindn = talloc_asprintf(mdb_ctx->param, "DC=%s", domaindn[0]);
88                 for (i = 1; domaindn[i]; i++) {
89                         strlower_m(domaindn[i]);
90                         mdb_ctx->param->domaindn = talloc_asprintf_append_buffer(mdb_ctx->param->domaindn, ",DC=%s", domaindn[i]);
91                 }
92                 talloc_free(domaindn);
93                 break;
94         default:
95                 mdb_ctx->param->domaindn = talloc_asprintf(mdb_ctx->param, "CN=%s", mdb_ctx->param->domain);
96                 break;
97         }
98
99         mdb_ctx->param->serverdn = talloc_asprintf(mdb_ctx->param, TMPL_MDB_SERVERDN,
100                                                    mdb_ctx->param->netbiosname,
101                                                    mdb_ctx->param->domaindn);
102         mdb_ctx->param->firstorg = talloc_strdup(mdb_ctx->param, DFLT_MDB_FIRSTORG);
103         mdb_ctx->param->firstou = talloc_strdup(mdb_ctx->param, DFLT_MDB_FIRSTOU);
104         mdb_ctx->param->firstorgdn = talloc_asprintf(mdb_ctx->param, TMPL_MDB_FIRSTORGDN,
105                                                      mdb_ctx->param->firstou,
106                                                      mdb_ctx->param->firstorg,
107                                                      mdb_ctx->param->serverdn);
108         mdb_ctx->param->db_path = talloc_asprintf(mdb_ctx->param, "%s/mapistore.ldb", path);
109         mdb_ctx->param->mstore_path = talloc_asprintf(mdb_ctx->param, "%s/mapistore", path);
110         mdb_ctx->param->db_named_path = talloc_asprintf(mdb_ctx->param, "%s/%s", 
111                                                         mdb_ctx->param->mstore_path,
112                                                         MAPISTORE_DB_NAMED_V2);
113
114         /* Step 4. Initialize mapistore */
115         if (stat(mdb_ctx->param->mstore_path, &sb) == -1) {
116                 ret = mkdir(mdb_ctx->param->mstore_path, 0700);
117                 if (ret == -1) {
118                         perror(mdb_ctx->param->mstore_path);
119                         talloc_free(mdb_ctx);
120                         return NULL;
121                 }
122         }
123
124         mapistore_set_database_path(mdb_ctx->param->db_path);
125         mapistore_set_mapping_path(mdb_ctx->param->mstore_path);
126         mapistore_set_named_properties_database_path(mdb_ctx->param->db_named_path);
127         mdb_ctx->mstore_ctx = mapistore_init(mdb_ctx, NULL);
128         if (!mdb_ctx->mstore_ctx) {
129                 MSTORE_DEBUG_ERROR(MSTORE_LEVEL_INFO, "Failed to initialize mapistore %s\n", "context");
130                 talloc_free(mdb_ctx);
131                 return NULL;
132         }
133
134         return mdb_ctx;
135 }
136
137
138 /**
139    \details Free a mapistore database context
140
141    \param mdb_ctx the context to free (from mapistoredb_init())
142  */
143 void mapistoredb_release(struct mapistoredb_context *mdb_ctx)
144 {
145         if (!mdb_ctx) {
146                 MSTORE_DEBUG_ERROR(MSTORE_LEVEL_INFO, "Invalid mapistore database %s\n", "context");
147                 return;
148         }
149
150         talloc_free(mdb_ctx->lp_ctx);
151         talloc_free(mdb_ctx->param);
152         talloc_free(mdb_ctx);
153 }
154
155
156 /**
157    \details Get a mapistore URI for a system/special folder from
158    backend
159
160    \param mdb_ctx pointer to the mapistore database context
161    \param index the special folder index
162    \param username the username for which we want to retrieve the uri
163    \param uri the uri namespace for the backend we want to use
164    \param namespace_uri pointer to the resulting namespace uri (return value)
165
166    The special folder is specified by the \p index parameter. For example
167    to create the "inbox" folder, pass MDB_INBOX.
168
169    \return MAPISTORE_SUCCESS on success, otherwise MAPISTORE_ERROR
170  */
171 enum MAPISTORE_ERROR mapistoredb_get_mapistore_uri(struct mapistoredb_context *mdb_ctx,
172                                                    enum MAPISTORE_DFLT_FOLDERS index,
173                                                    const char *namespace_uri,
174                                                    const char *username,
175                                                    char **uri)
176 {
177         enum MAPISTORE_ERROR    retval;
178         char                    *_uri;
179
180         /* Sanity checks */
181         if (!mdb_ctx || !mdb_ctx->mstore_ctx) {
182                 MSTORE_DEBUG_ERROR(MSTORE_LEVEL_INFO, "Invalid mapistore database %s\n", "context");
183                 return MAPISTORE_ERR_INVALID_CONTEXT;
184         }
185         if (!namespace_uri || !username) {
186                 MSTORE_DEBUG_ERROR(MSTORE_LEVEL_INFO, "Invalid  %s\n", "parameter");
187                 return MAPISTORE_ERR_INVALID_PARAMETER;
188         }
189
190         retval = mapistore_create_uri(mdb_ctx->mstore_ctx, index, namespace_uri, username, &_uri);
191         if (retval == MAPISTORE_SUCCESS) {
192                 *uri = _uri;
193         }
194
195         return retval;
196 }
197
198
199 /**
200    \details Retrieve the next available folder or message identifier
201
202    This function is a wrapper over mapistore_get_new_fmid from
203    mapistore_processing.c
204
205    \param mdb_ctx pointer to the mapistore database context
206    \param username the user to get the folder or message identifier for (i.e. identifier scope)
207    \param _fmid pointer on the next fmid available to return
208
209    \return MAPISTORE_SUCCESS on success, otherwise MAPISTORE error
210    
211    \sa mapistoredb_get_new_allocation_range for an alternative function returning multiple identifiers
212  */
213 enum MAPISTORE_ERROR mapistoredb_get_new_fmid(struct mapistoredb_context *mdb_ctx,
214                                               const char *username,
215                                               uint64_t *_fmid)
216 {
217         enum MAPISTORE_ERROR    retval;
218         uint64_t                fmid = 0;
219
220         /* Sanity checks */
221         MAPISTORE_RETVAL_IF(!mdb_ctx, MAPISTORE_ERR_NOT_INITIALIZED, NULL);
222         MAPISTORE_RETVAL_IF(!mdb_ctx->mstore_ctx, MAPISTORE_ERR_NOT_INITIALIZED, NULL);
223         MAPISTORE_RETVAL_IF(!mdb_ctx->mstore_ctx->processing_ctx, MAPISTORE_ERR_NOT_INITIALIZED, NULL);
224         MAPISTORE_RETVAL_IF(!_fmid, MAPISTORE_ERR_INVALID_PARAMETER, NULL);
225
226         retval = mapistore_get_new_fmid(mdb_ctx->mstore_ctx->processing_ctx, username, &fmid);
227         if (retval == MAPISTORE_SUCCESS) {
228                 *_fmid = fmid;
229                 return MAPISTORE_SUCCESS;
230         }
231         
232         return retval;
233 }
234
235
236 /**
237    \details Retrieve a new allocation range
238
239    This function obtains a range of folder / message identifiers. Conceptually
240    you specify how many identifiers you want, and are provided a contiguous block
241    of identiers (in terms of a start and end, which are inclusive).
242
243    This function is a wrapper over mapistore_get_new_allocation_range
244    from mapistore_processing.c
245
246    \param mdb_ctx pointer to the mapistore database context
247    \param username the user for which we want to retrieve an
248    allocation range
249    \param range the number of IDs to allocate
250    \param range_start pointer to the first ID of the range to return
251    \param range_end pointer to the last ID of the range to return
252
253    \return MAPISTORE_SUCCESS on success, otherwise MAPISTORE error
254  */
255 enum MAPISTORE_ERROR mapistoredb_get_new_allocation_range(struct mapistoredb_context *mdb_ctx,
256                                                           const char *username,
257                                                           uint64_t range,
258                                                           uint64_t *range_start,
259                                                           uint64_t *range_end)
260 {
261         enum MAPISTORE_ERROR    retval;
262         uint64_t                _range_start = 0;
263         uint64_t                _range_end = 0;
264
265         /* Sanity checks */
266         MAPISTORE_RETVAL_IF(!mdb_ctx, MAPISTORE_ERR_NOT_INITIALIZED, NULL);
267         MAPISTORE_RETVAL_IF(!mdb_ctx->mstore_ctx, MAPISTORE_ERR_NOT_INITIALIZED, NULL);
268         MAPISTORE_RETVAL_IF(!mdb_ctx->mstore_ctx->processing_ctx, MAPISTORE_ERR_NOT_INITIALIZED, NULL);
269         MAPISTORE_RETVAL_IF(!range_start || !range_end, MAPISTORE_ERR_INVALID_PARAMETER, NULL);
270
271         retval = mapistore_get_new_allocation_range(mdb_ctx->mstore_ctx->processing_ctx, username, range, &_range_start, &_range_end);
272         if (retval == MAPISTORE_SUCCESS) {
273                 *range_start = _range_start;
274                 *range_end = _range_end;
275
276                 return MAPISTORE_SUCCESS;
277         }
278
279         return retval;
280 }
281
282 /** TODO: this is a copy of code in mapistore_mstoredb.c */
283 static bool write_ldif_string_to_store(struct mapistoredb_context *mdb_ctx, const char *ldif_string)
284 {
285         enum MAPISTORE_ERROR    retval;
286
287         retval = mapistore_write_ldif_string_to_store(mdb_ctx->mstore_ctx->processing_ctx, ldif_string);
288         return (retval == MAPISTORE_SUCCESS) ? true : false;
289 }
290
291
292 /**
293    \details Register a new folder in the mapistore database
294
295    This function is mainly used to encapsulate the creation of the
296    User store container, root Mailbox folder. subfolders getting
297    created through mstoredb backend.
298
299    This function is a wrapper over the
300    mapistore_indexing_add_fmid_record function from
301    indexing/mapistore_indexing.c file.
302
303    \param mdb_ctx pointer to the mapistore database context
304    \param username the username for which we want to create the
305    mailbox container
306    \param mapistore_uri the mapistore URI to register
307
308    \return MAPISTORE_SUCCESS on success, otherwise MAPISTORE error
309 */
310 enum MAPISTORE_ERROR mapistoredb_register_new_mailbox(struct mapistoredb_context *mdb_ctx,
311                                                       const char *username,
312                                                       const char *mapistore_uri)
313 {
314         TALLOC_CTX                              *mem_ctx;
315         enum MAPISTORE_ERROR                    retval;
316         struct mapistore_indexing_context_list  *indexing_ctx;
317         const char                              *firstorgdn;
318         char                                    *user_store_ldif;
319         char                                    *mailbox_ldif;
320         uint64_t                                fid;
321         char                                    *dn;
322         struct GUID                             guid;
323         char                                    *mailboxGUID;
324         char                                    *replicaGUID;
325
326         /* Sanity checks */
327         MAPISTORE_RETVAL_IF(!mdb_ctx, MAPISTORE_ERR_NOT_INITIALIZED, NULL);
328         MAPISTORE_RETVAL_IF(!mdb_ctx->mstore_ctx, MAPISTORE_ERR_NOT_INITIALIZED, NULL);
329         MAPISTORE_RETVAL_IF(!username, MAPISTORE_ERR_INVALID_PARAMETER, NULL);
330         MAPISTORE_RETVAL_IF(!mapistore_uri, MAPISTORE_ERR_INVALID_PARAMETER, NULL);
331
332         /* Step 1. We want to ensure the mapistore_uri is a mstoredb:// one */
333         if (strncmp("mstoredb://", mapistore_uri, strlen("mstoredb://"))) {
334                 MSTORE_DEBUG_ERROR(MSTORE_LEVEL_INFO, "Invalid mapistore URI. MUST be %s\n", "mstoredb");
335                 return MAPISTORE_ERR_INVALID_PARAMETER;
336         }
337
338         /* Retrieve configuration parameters */
339         firstorgdn = mapistore_get_firstorgdn();
340         if (!firstorgdn) {
341                 MSTORE_DEBUG_ERROR(MSTORE_LEVEL_INFO, "Invalid  %s\n", "firstorgdn");
342                 return MAPISTORE_ERR_INVALID_PARAMETER;
343         }
344
345         /* Step 2. Create the user store entry within the mapistore database */
346         mem_ctx = talloc_named(NULL, 0, __FUNCTION__);
347         user_store_ldif = talloc_asprintf(mem_ctx, MDB_USER_STORE_LDIF_TMPL,
348                                           username, firstorgdn, username);
349         if (write_ldif_string_to_store(mdb_ctx, user_store_ldif) == false) {
350                 MSTORE_DEBUG_ERROR(MSTORE_LEVEL_INFO, "Failed to add user store %s\n", "container");
351                 talloc_free(mem_ctx);
352         }
353         talloc_free(user_store_ldif);
354
355         /* Step 3. Generate a fid for this mailbox root container */
356         retval = mapistore_get_new_fmid(mdb_ctx->mstore_ctx->processing_ctx, username, &fid);
357         MAPISTORE_RETVAL_IF(retval, retval, mem_ctx);
358
359         /* Step 4. Add an indexing context for user */
360         retval = mapistore_indexing_context_add(mdb_ctx->mstore_ctx, username, &indexing_ctx);
361         MAPISTORE_RETVAL_IF(retval, retval, NULL);
362
363         /* Step 5. Register the mailbox root container */
364         retval = mapistore_indexing_add_fmid_record(indexing_ctx, fid, mapistore_uri, 0, MAPISTORE_INDEXING_FOLDER);
365         MAPISTORE_RETVAL_IF(retval, retval, mem_ctx);
366
367         /* Step 6. Delete the indexing context */
368         retval = mapistore_indexing_context_del(mdb_ctx->mstore_ctx, username);
369         MAPISTORE_RETVAL_IF(retval, retval, mem_ctx);
370
371         /* Step 7. Add the mailbox root container to mapistore.ldb */   
372
373         dn = (char *) &mapistore_uri[strlen("mstoredb://")];
374
375         guid = GUID_random();
376         mailboxGUID = GUID_string(mem_ctx, &guid);
377
378         guid = GUID_random();
379         replicaGUID = GUID_string(mem_ctx, &guid);
380
381         mailbox_ldif = talloc_asprintf(mem_ctx, MDB_MAILBOX_LDIF_TMPL,
382                                        username, firstorgdn, dn, "Mailbox Root",
383                                        mailboxGUID, replicaGUID,
384                                        MDB_ROOT_FOLDER, mapistore_uri, dn);
385         if (write_ldif_string_to_store(mdb_ctx, mailbox_ldif) == false) {
386                 MSTORE_DEBUG_ERROR(MSTORE_LEVEL_CRITICAL, "Failed to add mailbox root %s\n", "container");
387                 talloc_free(mem_ctx);
388
389                 return MAPISTORE_ERR_DATABASE_OPS;
390         }
391
392         talloc_free(mailboxGUID);
393         talloc_free(replicaGUID);
394         talloc_free(mailbox_ldif);
395
396         talloc_free(mem_ctx);
397
398         return retval;
399 }
400
401
402 /**
403    \details Add an allocation range for messages to the mailbox root
404    container within the mapistore database.
405
406    \param mdb_ctx pointer to the mapistore database context
407    \param username the username for which we want to add a new
408    allocation range to the mailbox container
409    \param rstart the beginning of the allocation ID range
410    \param rend the end of the allocation ID range
411
412    \return MAPISTORE_SUCCESS on success, otherwise MAPISTORE error
413  */
414 enum MAPISTORE_ERROR mapistoredb_register_new_mailbox_allocation_range(struct mapistoredb_context *mdb_ctx,
415                                                                        const char *username,
416                                                                        uint64_t rstart,
417                                                                        uint64_t rend)
418 {
419         enum MAPISTORE_ERROR                    retval;
420         struct mapistore_indexing_context_list  *indexing_ctx;
421         uint64_t                                fid;
422         char                                    *mailbox_root;
423
424         /* Sanity checks */
425         MAPISTORE_RETVAL_IF(!mdb_ctx, MAPISTORE_ERR_NOT_INITIALIZED, NULL);
426         MAPISTORE_RETVAL_IF(!mdb_ctx->mstore_ctx, MAPISTORE_ERR_NOT_INITIALIZED, NULL);
427         MAPISTORE_RETVAL_IF(!username, MAPISTORE_ERR_INVALID_PARAMETER, NULL);
428         MAPISTORE_RETVAL_IF(!rstart || !rend, MAPISTORE_ERR_INVALID_PARAMETER, NULL);
429         MAPISTORE_RETVAL_IF(rstart > rend, MAPISTORE_ERR_INVALID_PARAMETER, NULL);
430
431         /* Step 1. Add an indexing context for user */
432         retval = mapistore_indexing_context_add(mdb_ctx->mstore_ctx, username, &indexing_ctx);
433         MAPISTORE_RETVAL_IF(retval, retval, NULL);
434
435         /* Step 2. Retrieve the FID for the user mailbox root folder */
436         retval = mapistore_get_mailbox_uri(mdb_ctx->mstore_ctx->processing_ctx, username, &mailbox_root);
437         MAPISTORE_RETVAL_IF(retval, retval, NULL);
438
439         retval = mapistore_indexing_get_record_fmid_by_uri(indexing_ctx, mailbox_root, &fid);
440         talloc_free(mailbox_root);
441         MAPISTORE_RETVAL_IF(retval, retval, NULL);
442
443         /* Step 3. Update the allocation range for root container */
444         retval = mapistore_indexing_add_folder_record_allocation_range(indexing_ctx, fid, rstart, rend);
445         MAPISTORE_RETVAL_IF(retval, retval, NULL);
446
447         /* Step 4. Delete the indexing context */
448         retval = mapistore_indexing_context_del(mdb_ctx->mstore_ctx, username);
449         MAPISTORE_RETVAL_IF(retval, retval, NULL);
450
451         return retval;
452 }
453
454
455 /**
456    \details Default provisioning for mapistore.ldb database
457
458    \param mdb_ctx pointer to the mapistore database context
459    
460    \return MAPISTORE_SUCCESS on success, otherwise a non-zero MAPISTORE_ERROR
461  */
462 enum MAPISTORE_ERROR mapistoredb_provision(struct mapistoredb_context *mdb_ctx)
463 {
464         char    *ldif_str;
465
466         /* Sanity checks */
467         MAPISTORE_RETVAL_IF(!mdb_ctx, MAPISTORE_ERR_NOT_INITIALIZED, NULL);
468
469         /* Step 1. Add database schema */
470         if (write_ldif_string_to_store(mdb_ctx, MDB_INIT_LDIF_TMPL) == false) {
471                 MSTORE_DEBUG_ERROR(MSTORE_LEVEL_INFO, "Failed to add database %s\n", "schema");
472                 return MAPISTORE_ERR_DATABASE_OPS;
473         }
474
475         /* Step 2. Add RootDSE schema */
476         ldif_str = talloc_asprintf(mdb_ctx, MDB_ROOTDSE_LDIF_TMPL,
477                                    mdb_ctx->param->firstou,
478                                    mdb_ctx->param->firstorg,
479                                    mdb_ctx->param->serverdn,
480                                    mdb_ctx->param->serverdn);
481         if (write_ldif_string_to_store(mdb_ctx, ldif_str) == false) {
482                 MSTORE_DEBUG_ERROR(MSTORE_LEVEL_INFO, "Failed to add RootDSE %s\n", "schema");
483                 talloc_free(ldif_str);
484                 return MAPISTORE_ERR_DATABASE_OPS;
485         }
486         talloc_free(ldif_str);
487
488         /* Step 3. Provision Server object responsible for maintaining
489          * the Replica and GlobalCount identifier */
490         ldif_str = talloc_asprintf(mdb_ctx, MDB_SERVER_LDIF_TMPL,
491                                    mdb_ctx->param->serverdn,
492                                    mdb_ctx->param->netbiosname,
493                                    mdb_ctx->param->firstorg,
494                                    mdb_ctx->param->serverdn,
495                                    mdb_ctx->param->firstorg,
496                                    mdb_ctx->param->firstou,
497                                    mdb_ctx->param->firstorg,
498                                    mdb_ctx->param->serverdn,
499                                    mdb_ctx->param->firstou);
500         if (write_ldif_string_to_store(mdb_ctx, ldif_str) == false) {
501                 MSTORE_DEBUG_ERROR(MSTORE_LEVEL_INFO, "Failed to provision server %s\n", "object");
502                 talloc_free(ldif_str);
503                 return MAPISTORE_ERR_DATABASE_OPS;
504         }
505         talloc_free(ldif_str);
506
507         return MAPISTORE_SUCCESS;
508 }