2 OpenChange Storage Abstraction Layer library
6 Copyright (C) Julien Kerihuel 2010-2011
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.
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.
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/>.
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"
30 \brief MAPIStore database provisioning interface
33 int lpcfg_server_role(struct loadparm_context *lp_ctx);
36 \details Initialize the mapistore database context
38 \param mem_ctx pointer to the memory context
39 \param path string pointer to the mapistore database location
41 If path if NULL use the default mapistore database path instead.
43 \return Allocated mapistore database context on success, otherwise NULL
45 struct mapistoredb_context *mapistoredb_init(TALLOC_CTX *mem_ctx,
48 struct mapistoredb_context *mdb_ctx;
56 path = MAPISTORE_DBPATH;
59 /* Ensure the path is valid */
60 if (stat(path, &sb) == -1) {
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");
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);
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));
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]);
92 talloc_free(domaindn);
95 mdb_ctx->param->domaindn = talloc_asprintf(mdb_ctx->param, "CN=%s", mdb_ctx->param->domain);
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);
114 /* Step 4. Initialize mapistore */
115 if (stat(mdb_ctx->param->mstore_path, &sb) == -1) {
116 ret = mkdir(mdb_ctx->param->mstore_path, 0700);
118 perror(mdb_ctx->param->mstore_path);
119 talloc_free(mdb_ctx);
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);
139 \details Free a mapistore database context
141 \param mdb_ctx the context to free (from mapistoredb_init())
143 void mapistoredb_release(struct mapistoredb_context *mdb_ctx)
146 MSTORE_DEBUG_ERROR(MSTORE_LEVEL_INFO, "Invalid mapistore database %s\n", "context");
150 talloc_free(mdb_ctx->lp_ctx);
151 talloc_free(mdb_ctx->param);
152 talloc_free(mdb_ctx);
157 \details Get a mapistore URI for a system/special folder from
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)
166 The special folder is specified by the \p index parameter. For example
167 to create the "inbox" folder, pass MDB_INBOX.
169 \return MAPISTORE_SUCCESS on success, otherwise MAPISTORE_ERROR
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,
177 enum MAPISTORE_ERROR retval;
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;
185 if (!namespace_uri || !username) {
186 MSTORE_DEBUG_ERROR(MSTORE_LEVEL_INFO, "Invalid %s\n", "parameter");
187 return MAPISTORE_ERR_INVALID_PARAMETER;
190 retval = mapistore_create_uri(mdb_ctx->mstore_ctx, index, namespace_uri, username, &_uri);
191 if (retval == MAPISTORE_SUCCESS) {
200 \details Retrieve the next available folder or message identifier
202 This function is a wrapper over mapistore_get_new_fmid from
203 mapistore_processing.c
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
209 \return MAPISTORE_SUCCESS on success, otherwise MAPISTORE error
211 \sa mapistoredb_get_new_allocation_range for an alternative function returning multiple identifiers
213 enum MAPISTORE_ERROR mapistoredb_get_new_fmid(struct mapistoredb_context *mdb_ctx,
214 const char *username,
217 enum MAPISTORE_ERROR retval;
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);
226 retval = mapistore_get_new_fmid(mdb_ctx->mstore_ctx->processing_ctx, username, &fmid);
227 if (retval == MAPISTORE_SUCCESS) {
229 return MAPISTORE_SUCCESS;
237 \details Retrieve a new allocation range
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).
243 This function is a wrapper over mapistore_get_new_allocation_range
244 from mapistore_processing.c
246 \param mdb_ctx pointer to the mapistore database context
247 \param username the user for which we want to retrieve an
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
253 \return MAPISTORE_SUCCESS on success, otherwise MAPISTORE error
255 enum MAPISTORE_ERROR mapistoredb_get_new_allocation_range(struct mapistoredb_context *mdb_ctx,
256 const char *username,
258 uint64_t *range_start,
261 enum MAPISTORE_ERROR retval;
262 uint64_t _range_start = 0;
263 uint64_t _range_end = 0;
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);
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;
276 return MAPISTORE_SUCCESS;
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)
285 enum MAPISTORE_ERROR retval;
287 retval = mapistore_write_ldif_string_to_store(mdb_ctx->mstore_ctx->processing_ctx, ldif_string);
288 return (retval == MAPISTORE_SUCCESS) ? true : false;
293 \details Register a new folder in the mapistore database
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.
299 This function is a wrapper over the
300 mapistore_indexing_add_fmid_record function from
301 indexing/mapistore_indexing.c file.
303 \param mdb_ctx pointer to the mapistore database context
304 \param username the username for which we want to create the
306 \param mapistore_uri the mapistore URI to register
308 \return MAPISTORE_SUCCESS on success, otherwise MAPISTORE error
310 enum MAPISTORE_ERROR mapistoredb_register_new_mailbox(struct mapistoredb_context *mdb_ctx,
311 const char *username,
312 const char *mapistore_uri)
315 enum MAPISTORE_ERROR retval;
316 struct mapistore_indexing_context_list *indexing_ctx;
317 const char *firstorgdn;
318 char *user_store_ldif;
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);
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;
338 /* Retrieve configuration parameters */
339 firstorgdn = mapistore_get_firstorgdn();
341 MSTORE_DEBUG_ERROR(MSTORE_LEVEL_INFO, "Invalid %s\n", "firstorgdn");
342 return MAPISTORE_ERR_INVALID_PARAMETER;
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);
353 talloc_free(user_store_ldif);
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);
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);
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);
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);
371 /* Step 7. Add the mailbox root container to mapistore.ldb */
373 dn = (char *) &mapistore_uri[strlen("mstoredb://")];
375 guid = GUID_random();
376 mailboxGUID = GUID_string(mem_ctx, &guid);
378 guid = GUID_random();
379 replicaGUID = GUID_string(mem_ctx, &guid);
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);
389 return MAPISTORE_ERR_DATABASE_OPS;
392 talloc_free(mailboxGUID);
393 talloc_free(replicaGUID);
394 talloc_free(mailbox_ldif);
396 talloc_free(mem_ctx);
403 \details Add an allocation range for messages to the mailbox root
404 container within the mapistore database.
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
412 \return MAPISTORE_SUCCESS on success, otherwise MAPISTORE error
414 enum MAPISTORE_ERROR mapistoredb_register_new_mailbox_allocation_range(struct mapistoredb_context *mdb_ctx,
415 const char *username,
419 enum MAPISTORE_ERROR retval;
420 struct mapistore_indexing_context_list *indexing_ctx;
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);
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);
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);
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);
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);
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);
456 \details Default provisioning for mapistore.ldb database
458 \param mdb_ctx pointer to the mapistore database context
460 \return MAPISTORE_SUCCESS on success, otherwise a non-zero MAPISTORE_ERROR
462 enum MAPISTORE_ERROR mapistoredb_provision(struct mapistoredb_context *mdb_ctx)
467 MAPISTORE_RETVAL_IF(!mdb_ctx, MAPISTORE_ERR_NOT_INITIALIZED, NULL);
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;
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;
486 talloc_free(ldif_str);
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;
505 talloc_free(ldif_str);
507 return MAPISTORE_SUCCESS;