2 OpenChange Storage Abstraction Layer library
6 Copyright (C) Julien Kerihuel 2009-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/>.
23 #include <sys/types.h>
25 #define __STDC_FORMAT_MACROS 1
33 #include "mapistore_errors.h"
34 #include "mapistore.h"
35 #include "mapistore_common.h"
36 #include "mapistore_private.h"
37 #include <dlinklist.h>
38 #include "libmapi/libmapi_private.h"
42 char *mapping_path = NULL;
43 char *mapistore_dbpath = NULL;
44 char *mapistore_firstorgdn = NULL;
47 \details Set the mapping path
49 \param path pointer to the mapping path
51 \note The mapping path can be set unless id_mapping_context is
52 initialized. If path is NULL and mapping path is not yet
53 initialized, then mapping_path will be reset to its default value
54 when the initialization routine is called.
56 \return MAPISTORE_SUCCESS on success, otherwise MAPISTORE error
58 _PUBLIC_ enum MAPISTORE_ERROR mapistore_set_mapping_path(const char *path)
63 /* Case 1. Path is set to NULL */
66 talloc_free(mapping_path);
69 return MAPISTORE_SUCCESS;
73 talloc_free(mapping_path);
76 /* Case 2. path is initialized */
78 /* Step 1. Check if path is valid path */
81 return MAPISTORE_ERR_NO_DIRECTORY;
84 /* Step 2. TODO: Check for write permissions */
86 if (closedir(dir) == -1) {
87 /* FIXME: Should have a better error name here */
88 return MAPISTORE_ERR_NO_DIRECTORY;
91 mem_ctx = talloc_autofree_context();
92 mapping_path = talloc_strdup(mem_ctx, path);
93 return MAPISTORE_SUCCESS;
97 \details Return the current mapping path
99 \return pointer to the mapping path.
101 const char *mapistore_get_mapping_path(void)
103 return (!mapping_path) ? MAPISTORE_MAPPING_PATH : (const char *)mapping_path;
108 \details Set the mapistore.ldb mapping path
110 \param dbname string pointer to the mapistore database path
112 \return MAPISTORE_SUCCESS on success, otherwise MAPISTORE error
114 enum MAPISTORE_ERROR mapistore_set_database_path(const char *dbname)
119 if (mapistore_dbpath) {
120 talloc_free(mapistore_dbpath);
122 mapistore_dbpath = NULL;
123 return MAPISTORE_SUCCESS;
126 if (mapistore_dbpath) {
127 talloc_free(mapistore_dbpath);
128 mapistore_dbpath = NULL;
131 mem_ctx = talloc_autofree_context();
132 mapistore_dbpath = talloc_strdup(mem_ctx, dbname);
134 return MAPISTORE_SUCCESS;
138 enum MAPISTORE_ERROR mapistore_set_firstorgdn(const char *firstou, const char *firstorg, const char *serverdn)
142 if (mapistore_firstorgdn) {
143 talloc_free(mapistore_firstorgdn);
146 mem_ctx = talloc_autofree_context();
147 mapistore_firstorgdn = talloc_asprintf(mem_ctx, TMPL_MDB_FIRSTORGDN, firstou, firstorg, serverdn);
148 if (!mapistore_firstorgdn) {
149 DEBUG(5, ("! [%s:%d][%s]: Unable to allocate memory to set firstorgdn\n",
150 __FILE__, __LINE__, __FUNCTION__));
151 return MAPISTORE_ERR_NO_MEMORY;
154 return MAPISTORE_SUCCESS;
157 const char *mapistore_get_firstorgdn(void)
159 return mapistore_firstorgdn;
163 \details Return the current path to mapistore.ldb database
165 \return pointer to the mapistore database path
167 const char *mapistore_get_database_path(void)
169 return (!mapistore_dbpath) ? MAPISTORE_DBPATH : (const char *) mapistore_dbpath;
177 \details Initialize the ID mapping context or return the existing
178 one if already initialized.
180 \param pctx pointer to the processing context
182 \return MAPISTORE_SUCCESS on success, otherwise MAPISTORE error
184 enum MAPISTORE_ERROR mapistore_init_mapping_context(struct processing_context *pctx)
198 MAPISTORE_RETVAL_IF(!pctx, MAPISTORE_ERR_NOT_INITIALIZED, NULL);
199 MAPISTORE_RETVAL_IF(pctx->mapping_ctx, MAPISTORE_ERR_ALREADY_INITIALIZED, NULL);
201 pctx->mapping_ctx = talloc_zero(pctx, struct id_mapping_context);
202 MAPISTORE_RETVAL_IF(!pctx->mapping_ctx, MAPISTORE_ERR_NO_MEMORY, NULL);
204 mem_ctx = talloc_named(NULL, 0, "mapistore_init_mapping_context");
206 /* Step 1. Retrieve the mapistore database path */
207 db_path = mapistore_get_database_path();
209 DEBUG(5, ("! [%s:%d][%s]: Unable to retrieve the mapistore database path\n", __FILE__, __LINE__, __FUNCTION__));
210 talloc_free(mem_ctx);
211 talloc_free(pctx->mapping_ctx);
212 return MAPISTORE_ERR_DATABASE_INIT;
215 /* Step 2. Initialize tevent structure */
216 pctx->mapping_ctx->ev = tevent_context_init(pctx);
218 /* Step 3. Open a wrapped connection to mapistore.ldb */
219 pctx->mapping_ctx->ldb_ctx = mapistore_ldb_wrap_connect(pctx, pctx->mapping_ctx->ev, db_path, 0);
220 if (!pctx->mapping_ctx->ldb_ctx) {
221 DEBUG(5, ("! [%s:%d][%s]: Failed to open wrapped connection over mapistore.ldb\n", __FILE__, __LINE__, __FUNCTION__));
222 talloc_free(pctx->mapping_ctx);
223 return MAPISTORE_ERR_DATABASE_INIT;
226 /* Open/Create the used ID database */
227 if (!pctx->mapping_ctx->used_ctx) {
228 dbpath = talloc_asprintf(mem_ctx, "%s/%s", mapistore_get_mapping_path(), MAPISTORE_DB_NAME_USED_ID);
229 pctx->mapping_ctx->used_ctx = tdb_wrap_open(pctx, dbpath, 0, 0, O_RDWR|O_CREAT, 0600);
231 if (!pctx->mapping_ctx->used_ctx) {
232 DEBUG(3, ("[%s:%d]: %s\n", __FUNCTION__, __LINE__, strerror(errno)));
233 talloc_free(mem_ctx);
234 talloc_free(pctx->mapping_ctx);
235 return MAPISTORE_ERR_DATABASE_INIT;
239 /* Retrieve the last ID value */
240 key.dptr = (unsigned char *) MAPISTORE_DB_LAST_ID_KEY;
241 key.dsize = strlen(MAPISTORE_DB_LAST_ID_KEY);
243 dbuf = tdb_fetch(pctx->mapping_ctx->used_ctx->tdb, key);
245 /* If the record doesn't exist, insert it */
246 if (!dbuf.dptr || !dbuf.dsize) {
247 dbuf.dptr = (unsigned char *) talloc_asprintf(mem_ctx, "0x%"PRIx64, (uint64_t) MAPISTORE_DB_LAST_ID_VAL);
248 dbuf.dsize = strlen((const char *) dbuf.dptr);
249 last_id = MAPISTORE_DB_LAST_ID_VAL;
251 ret = tdb_store(pctx->mapping_ctx->used_ctx->tdb, key, dbuf, TDB_INSERT);
252 talloc_free(dbuf.dptr);
254 DEBUG(3, ("[%s:%d]: Unable to create %s record: %s\n", __FUNCTION__, __LINE__,
255 MAPISTORE_DB_LAST_ID_KEY, tdb_errorstr(pctx->mapping_ctx->used_ctx->tdb)));
256 talloc_free(mem_ctx);
257 talloc_free(pctx->mapping_ctx);
259 return MAPISTORE_ERR_DATABASE_OPS;
263 tmp_buf = talloc_strndup(mem_ctx, (char *)dbuf.dptr, dbuf.dsize);
265 last_id = strtoull(tmp_buf, NULL, 16);
266 talloc_free(tmp_buf);
269 pctx->mapping_ctx->last_id = last_id;
271 talloc_free(mem_ctx);
273 return MAPISTORE_SUCCESS;
277 enum MAPISTORE_ERROR mapistore_write_ldif_string_to_store(struct processing_context *pctx, const char *ldif_string)
279 struct ldb_context *ldb_ctx;
280 struct ldb_ldif *ldif;
284 MAPISTORE_RETVAL_IF(!pctx, MAPISTORE_ERR_NOT_INITIALIZED, NULL);
285 MAPISTORE_RETVAL_IF(!pctx->mapping_ctx, MAPISTORE_ERR_NOT_INITIALIZED, NULL);
286 MAPISTORE_RETVAL_IF(!pctx->mapping_ctx->ldb_ctx, MAPISTORE_ERR_NOT_INITIALIZED, NULL);
287 MAPISTORE_RETVAL_IF(!ldif_string, MAPISTORE_ERR_INVALID_PARAMETER, NULL);
289 ldb_ctx = pctx->mapping_ctx->ldb_ctx;
291 while ((ldif = ldb_ldif_read_string(ldb_ctx, (const char **)&ldif_string))) {
292 ret = ldb_msg_normalize(ldb_ctx, ldif, ldif->msg, &ldif->msg);
293 if (ret != LDB_SUCCESS) {
294 ldb_ldif_read_free(ldb_ctx, ldif);
295 DEBUG(5, ("! [%s:%d][%s]: Unable to normalize ldif\n", __FILE__, __LINE__, __FUNCTION__));
296 return MAPISTORE_ERROR;
298 ret = ldb_add(ldb_ctx, ldif->msg);
299 if (ret != LDB_SUCCESS) {
300 ldb_ldif_read_free(ldb_ctx, ldif);
301 DEBUG(5, ("! [%s:%d][%s]: Unable to add ldb msg\n", __FILE__, __LINE__, __FUNCTION__));
302 return MAPISTORE_ERROR;
304 ldb_ldif_read_free(ldb_ctx, ldif);
307 return MAPISTORE_SUCCESS;
312 \details Retrieve the next available folder or message identifier
314 \param pctx pointer to the mapistore processing context
315 \param username the username's mailbox to retrieve GlobalCount from
316 \param fmid pointer to the first available fmid within the range
318 \return MAPISTORE_SUCCESS on success, otherwise MAPISTORE error
320 enum MAPISTORE_ERROR mapistore_get_new_fmid(struct processing_context *pctx,
321 const char *username,
326 struct ldb_context *ldb_ctx;
327 struct ldb_result *res = NULL;
328 struct ldb_message *msg;
329 const char * const attrs[] = { "*", NULL };
331 uint64_t GlobalCount;
332 uint64_t new_GlobalCount;
335 MAPISTORE_RETVAL_IF(!pctx, MAPISTORE_ERR_NOT_INITIALIZED, NULL);
336 MAPISTORE_RETVAL_IF(!pctx->mapping_ctx, MAPISTORE_ERR_NOT_INITIALIZED, NULL);
337 MAPISTORE_RETVAL_IF(!pctx->mapping_ctx->ldb_ctx, MAPISTORE_ERR_NOT_INITIALIZED, NULL);
338 MAPISTORE_RETVAL_IF(!username, MAPISTORE_ERR_INVALID_PARAMETER, NULL);
339 MAPISTORE_RETVAL_IF(!fmid, MAPISTORE_ERR_INVALID_PARAMETER, NULL);
341 mem_ctx = talloc_named(NULL, 0, "mapistore_get_new_fmid");
343 /* Step 1. Retrieve the server object */
344 ldb_ctx = pctx->mapping_ctx->ldb_ctx;
346 /* TODO: In the future, we may want to deal with different servers */
347 /* TODO: Use ldb_transaction to lock between get/set operations */
348 ret = ldb_search(ldb_ctx, mem_ctx, &res, ldb_get_root_basedn(ldb_ctx),
349 LDB_SCOPE_SUBTREE, attrs, "(&(cn=%s)(objectClass=store))", username);
350 if (ret != LDB_SUCCESS || res->count != 1) {
351 talloc_free(mem_ctx);
352 DEBUG(5, ("! [%s:%d][%s]: Unable to find the user store object\n", __FILE__, __LINE__, __FUNCTION__));
353 return MAPISTORE_ERR_NOT_FOUND;
356 /* Step 2. Get the [48 GlobalCount][16 ReplicaID] */
357 GlobalCount = ldb_msg_find_attr_as_uint64(res->msgs[0], "GlobalCount", 0);
358 new_GlobalCount = GlobalCount + 1;
360 ReplicaID = ldb_msg_find_attr_as_uint64(res->msgs[0], "ReplicaID", 0);
362 /* Step 3. Update the GlobalCount */
363 msg = ldb_msg_new(mem_ctx);
364 msg->dn = ldb_dn_copy(msg, ldb_msg_find_attr_as_dn(ldb_ctx, mem_ctx, res->msgs[0], "distinguishedName"));
365 ldb_msg_add_fmt(msg, "GlobalCount", "0x%"PRIx64, new_GlobalCount);
366 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
367 ret = ldb_modify(ldb_ctx, msg);
368 if (ret != LDB_SUCCESS) {
369 talloc_free(mem_ctx);
370 DEBUG(5, ("! [%s:%d][%s]: Unable to update GlobalCount for server object\n", __FILE__, __LINE__, __FUNCTION__));
371 return MAPISTORE_ERROR;
374 /* Step 4. Set the fmid value */
376 talloc_free(mem_ctx);
377 *fmid = (GlobalCount << 16) + ReplicaID;
379 return MAPISTORE_SUCCESS;
384 \details Retrieve an allocation range
386 \param pctx pointer to the mapistore processing context
387 \param username the user store from which to retrieve the new allocation range
388 \param range the range to allocate
389 \param range_start pointer to the first allocation range fmid to return
390 \param range_end pointer to the last allocation range fmid to return
392 \return MAPISTORE_SUCCESS on success, otherwise MAPISTORE error
394 enum MAPISTORE_ERROR mapistore_get_new_allocation_range(struct processing_context *pctx,
395 const char *username,
397 uint64_t *range_start,
402 struct ldb_context *ldb_ctx;
403 struct ldb_result *res = NULL;
404 struct ldb_message *msg;
405 const char * const attrs[] = { "*", NULL };
407 uint64_t GlobalCount;
408 uint64_t new_GlobalCount;
411 MAPISTORE_RETVAL_IF(!pctx, MAPISTORE_ERR_NOT_INITIALIZED, NULL);
412 MAPISTORE_RETVAL_IF(!pctx->mapping_ctx, MAPISTORE_ERR_NOT_INITIALIZED, NULL);
413 MAPISTORE_RETVAL_IF(!pctx->mapping_ctx->ldb_ctx, MAPISTORE_ERR_NOT_INITIALIZED, NULL);
414 MAPISTORE_RETVAL_IF(!range_start || !range_end, MAPISTORE_ERR_INVALID_PARAMETER, NULL);
416 mem_ctx = talloc_named(NULL, 0, "mapistore_get_next_range");
418 /* Step 1. Retrieve the user store object */
419 ldb_ctx = pctx->mapping_ctx->ldb_ctx;
420 ret = ldb_search(ldb_ctx, mem_ctx, &res, ldb_get_root_basedn(ldb_ctx),
421 LDB_SCOPE_SUBTREE, attrs, "(&(objectClass=store)(cn=%s))", username);
422 if (ret != LDB_SUCCESS || res->count != 1) {
423 talloc_free(mem_ctx);
424 DEBUG(5, ("! [%s:%d][%s]: Unable to find the user store object\n", __FILE__, __LINE__, __FUNCTION__));
425 return MAPISTORE_ERR_NOT_FOUND;
428 /* Step 2. Get the current GlobalCount and set the new one */
429 GlobalCount = ldb_msg_find_attr_as_uint64(res->msgs[0], "GlobalCount", 0);
430 ReplicaID = ldb_msg_find_attr_as_uint64(res->msgs[0], "ReplicaID", 0);
432 *range_start = GlobalCount;
434 new_GlobalCount = GlobalCount + range -1;
435 *range_end = new_GlobalCount;
437 new_GlobalCount += 1;
439 /* Step 3. Update the GlobalCount */
440 msg = ldb_msg_new(mem_ctx);
441 msg->dn = ldb_dn_copy(msg, ldb_msg_find_attr_as_dn(ldb_ctx, mem_ctx, res->msgs[0], "distinguishedName"));
442 ldb_msg_add_fmt(msg, "GlobalCount", "0x%"PRIx64, new_GlobalCount);
443 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
444 ret = ldb_modify(ldb_ctx, msg);
445 if (ret != LDB_SUCCESS) {
446 talloc_free(mem_ctx);
447 DEBUG(5, ("! [%s:%d][%s]: Unable to update GlobalCount for server object\n", __FILE__, __LINE__, __FUNCTION__));
448 return MAPISTORE_ERROR;
451 /* Step 4. Set the fmid value ranges */
452 talloc_free(mem_ctx);
453 *range_start = (*range_start << 16) + ReplicaID;
454 *range_end = (*range_end << 16) + ReplicaID;
456 return MAPISTORE_SUCCESS;
461 \details Retrieve the mapistore URI for a given user mailbox
463 \param pctx pointer to the processing context
464 \param username pointer to the username to lookup
465 \param mapistore_uri pointer on pointer to the mapistore URI
467 \return MAPISTORE_SUCCESS on success, otherwise MAPISTORE error
469 enum MAPISTORE_ERROR mapistore_get_mailbox_uri(struct processing_context *pctx,
470 const char *username,
471 char **mapistore_uri)
476 const char * const attrs[] = { "*", NULL };
477 struct ldb_context *ldb_ctx;
478 struct ldb_result *res;
482 MAPISTORE_RETVAL_IF(!pctx, MAPISTORE_ERR_NOT_INITIALIZED, NULL);
483 MAPISTORE_RETVAL_IF(!username, MAPISTORE_ERR_INVALID_PARAMETER, NULL);
484 MAPISTORE_RETVAL_IF(!mapistore_uri, MAPISTORE_ERR_INVALID_PARAMETER, NULL);
486 mem_ctx = talloc_new(pctx);
487 ldb_ctx = pctx->mapping_ctx->ldb_ctx;
489 dn = ldb_dn_new_fmt(mem_ctx, ldb_ctx, TMPL_MDB_USERSTORE, username, mapistore_get_firstorgdn());
490 ret = ldb_search(ldb_ctx, mem_ctx, &res, dn, LDB_SCOPE_SUBTREE, attrs, "(&(objectClass=mailbox)(objectClass=container))");
491 if (ret != LDB_SUCCESS || res->count != 1) {
492 talloc_free(mem_ctx);
493 DEBUG(5, ("! [%s:%d][%s]: Unable to find the user mailbox root container\n", __FILE__, __LINE__, __FUNCTION__));
494 return MAPISTORE_ERR_NOT_FOUND;
497 uri = ldb_msg_find_attr_as_string(res->msgs[0], "mapistore_uri", NULL);
498 MAPISTORE_RETVAL_IF(!uri, MAPISTORE_ERR_NOT_FOUND, mem_ctx);
500 *mapistore_uri = talloc_strdup(pctx, (char *)uri);
502 talloc_free(mem_ctx);
503 return MAPISTORE_SUCCESS;
508 \details Return an unused or new context identifier
510 \param pctx pointer to the processing context
511 \param context_id pointer to the context identifier the function
514 \return a non zero context identifier on success, otherwise 0.
516 enum MAPISTORE_ERROR mapistore_get_context_id(struct processing_context *pctx, uint32_t *context_id)
518 struct context_id_list *el;
521 MAPISTORE_RETVAL_IF(!pctx, MAPISTORE_ERR_NOT_INITIALIZED, NULL);
523 /* Step 1. The free context list doesn't exist yet */
524 if (!pctx->free_ctx) {
525 pctx->last_context_id++;
526 *context_id = pctx->last_context_id;
529 /* Step 2. We have a free list */
530 for (el = pctx->free_ctx; el; el = el->next) {
531 if (el->context_id) {
532 *context_id = el->context_id;
533 DLIST_REMOVE(pctx->free_ctx, el);
538 return MAPISTORE_SUCCESS;
543 \details Add a context identifier to the list
545 \param pctx pointer to the processing context
546 \param context_id the identifier referencing the context to free
548 \return MAPISTORE_SUCCESS on success, otherwise MAPISTORE error
550 enum MAPISTORE_ERROR mapistore_free_context_id(struct processing_context *pctx, uint32_t context_id)
552 struct context_id_list *el;
555 MAPISTORE_RETVAL_IF(!pctx, MAPISTORE_ERR_NOT_INITIALIZED, NULL);
557 /* Step 1. Ensure the list is not corrupted */
558 for (el = pctx->free_ctx; el; el = el->next) {
559 if (el->context_id == context_id) {
560 return MAPISTORE_ERR_CORRUPTED;
564 /* Step 2. Create the element and add it to the list */
565 el = talloc_zero((TALLOC_CTX *)pctx, struct context_id_list);
566 el->context_id = context_id;
567 DLIST_ADD_END(pctx->free_ctx, el, struct context_id_list *);
569 return MAPISTORE_SUCCESS;