2 OpenChange Server implementation
4 MAPI handles API implementation
6 Copyright (C) Julien Kerihuel 2009
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/>.
25 \brief API for MAPI handles management
28 #include "mapiproxy/dcesrv_mapiproxy.h"
29 #include "libmapi/libmapi.h"
30 #include "libmapi/libmapi_private.h"
31 #include "libmapiproxy.h"
35 \details Initialize MAPI handles context
37 \param mem_ctx pointer to the memory context
39 \return Allocated MAPI handles context on success, otherwise NULL
41 _PUBLIC_ struct mapi_handles_context *mapi_handles_init(TALLOC_CTX *mem_ctx)
43 struct mapi_handles_context *handles_ctx;
45 /* Step 1. Initialize the context */
46 handles_ctx = talloc_zero(mem_ctx, struct mapi_handles_context);
47 if (!handles_ctx) return NULL;
49 /* Step 2. Initialize the TDB context */
50 handles_ctx->tdb_ctx = tdb_open(NULL, 0, TDB_INTERNAL, O_RDWR|O_CREAT, 0600);
52 /* Step 3. Initialize the handles list */
53 handles_ctx->handles = NULL;
55 /* Step 4. Set last_handle to the first valid value */
56 handles_ctx->last_handle = 1;
63 \details Release MAPI handles context
65 \param handles_ctx pointer to the MAPI handles context
67 \return MAPI_E_SUCCESS on success, otherwise MAPI error
69 _PUBLIC_ enum MAPISTATUS mapi_handles_release(struct mapi_handles_context *handles_ctx)
72 OPENCHANGE_RETVAL_IF(!handles_ctx, MAPI_E_NOT_INITIALIZED, NULL);
74 tdb_close(handles_ctx->tdb_ctx);
75 talloc_free(handles_ctx);
77 return MAPI_E_SUCCESS;
82 \details Search for a record in the TDB database
84 \param handles_ctx pointer to the MAPI handles context
85 \param handle MAPI handle to lookup
86 \param rec pointer to the MAPI handle structure the function
89 \return MAPI_E_SUCCESS on success, otherwise MAPI error
91 _PUBLIC_ enum MAPISTATUS mapi_handles_search(struct mapi_handles_context *handles_ctx,
92 uint32_t handle, struct mapi_handles **rec)
97 struct mapi_handles *el;
100 OPENCHANGE_RETVAL_IF(!handles_ctx, MAPI_E_NOT_INITIALIZED, NULL);
101 OPENCHANGE_RETVAL_IF(!handles_ctx->tdb_ctx, MAPI_E_NOT_INITIALIZED, NULL);
102 OPENCHANGE_RETVAL_IF(handle == MAPI_HANDLES_RESERVED, MAPI_E_INVALID_PARAMETER, NULL);
103 OPENCHANGE_RETVAL_IF(!rec, MAPI_E_INVALID_PARAMETER, NULL);
105 mem_ctx = talloc_named(NULL, 0, "mapi_handles_search");
107 /* Step 1. Search for the handle within TDB database */
108 key.dptr = (unsigned char *) talloc_asprintf(mem_ctx, "0x%x", handle);
109 key.dsize = strlen((const char *)key.dptr);
111 dbuf = tdb_fetch(handles_ctx->tdb_ctx, key);
112 talloc_free(key.dptr);
113 OPENCHANGE_RETVAL_IF(!dbuf.dptr, MAPI_E_NOT_FOUND, mem_ctx);
114 OPENCHANGE_RETVAL_IF(!dbuf.dsize, MAPI_E_NOT_FOUND, mem_ctx);
116 talloc_free(mem_ctx);
117 /* Ensure this is not a free'd record */
118 if (dbuf.dsize == (sizeof(MAPI_HANDLES_NULL) - 1) && !strncmp((char *)dbuf.dptr, MAPI_HANDLES_NULL, dbuf.dsize)) {
120 return MAPI_E_NOT_FOUND;
124 /* Step 2. Return the record within the double chained list */
125 for (el = handles_ctx->handles; el; el = el->next) {
126 if (el->handle == handle) {
128 return MAPI_E_SUCCESS;
132 return MAPI_E_CORRUPT_STORE;
137 \details Set a TDB record data as null meaning it can be reused in
140 \param handles_ctx pointer to the MAPI handles context
141 \param handle handle key value to free
143 \return MAPI_E_SUCCESS on success, otherwise MAPI error
145 static enum MAPISTATUS mapi_handles_tdb_free(struct mapi_handles_context *handles_ctx,
154 OPENCHANGE_RETVAL_IF(!handles_ctx, MAPI_E_NOT_INITIALIZED, NULL);
155 OPENCHANGE_RETVAL_IF(!handles_ctx->tdb_ctx, MAPI_E_NOT_INITIALIZED, NULL);
156 OPENCHANGE_RETVAL_IF(handle == MAPI_HANDLES_RESERVED, MAPI_E_INVALID_PARAMETER, NULL);
158 mem_ctx = talloc_named(NULL, 0, "mapi_handles_tdb_free");
160 key.dptr = (unsigned char *) talloc_asprintf(mem_ctx, "0x%x", handle);
161 key.dsize = strlen((const char *)key.dptr);
163 /* Step 1. Makes sure the record exists */
164 ret = tdb_exists(handles_ctx->tdb_ctx, key);
165 OPENCHANGE_RETVAL_IF(!ret, MAPI_E_NOT_FOUND, mem_ctx);
167 /* Step 2. Update existing record */
168 dbuf.dptr = (unsigned char *)MAPI_HANDLES_NULL;
169 dbuf.dsize = strlen(MAPI_HANDLES_NULL);
171 ret = tdb_store(handles_ctx->tdb_ctx, key, dbuf, TDB_MODIFY);
172 talloc_free(mem_ctx);
174 DEBUG(3, ("[%s:%d]: Unable to create 0x%x record: %s\n", __FUNCTION__, __LINE__,
175 handle, tdb_errorstr(handles_ctx->tdb_ctx)));
176 return MAPI_E_CORRUPT_STORE;
179 return MAPI_E_SUCCESS;
184 \details Update a TDB record
188 static enum MAPISTATUS mapi_handles_tdb_update(struct mapi_handles_context *handles_ctx,
189 uint32_t handle, uint32_t container_handle)
197 OPENCHANGE_RETVAL_IF(!handles_ctx, MAPI_E_NOT_INITIALIZED, NULL);
198 OPENCHANGE_RETVAL_IF(!handles_ctx->tdb_ctx, MAPI_E_NOT_INITIALIZED, NULL);
199 OPENCHANGE_RETVAL_IF(!handle, MAPI_E_INVALID_PARAMETER, NULL);
201 mem_ctx = talloc_named(NULL, 0, "mapi_handles_tdb_update");
203 key.dptr = (unsigned char *) talloc_asprintf(mem_ctx, "0x%x", handle);
204 key.dsize = strlen((const char *)key.dptr);
206 /* Step 1. Makes sure the record exists */
207 ret = tdb_exists(handles_ctx->tdb_ctx, key);
208 OPENCHANGE_RETVAL_IF(!ret, MAPI_E_NOT_FOUND, mem_ctx);
210 /* Step 2. Update record */
211 dbuf.dptr = (unsigned char *) talloc_asprintf(mem_ctx, "0x%x", container_handle);
212 dbuf.dsize = strlen((const char *)dbuf.dptr);
214 ret = tdb_store(handles_ctx->tdb_ctx, key, dbuf, TDB_MODIFY);
215 talloc_free(mem_ctx);
217 DEBUG(3, ("[%s:%d]: Unable to update 0x%x record: %s\n", __FUNCTION__, __LINE__,
218 handle, tdb_errorstr(handles_ctx->tdb_ctx)));
220 return MAPI_E_CORRUPT_STORE;
223 return MAPI_E_SUCCESS;
228 \details Traverse TDB database and search for the first record
229 which dbuf value is "null" string.
231 \param tdb_ctx pointer to the TDB context
232 \param key the current TDB key
233 \param dbuf the current TDB value
234 \param state pointer on private data
236 \return 1 when a free record is found, otherwise 0
238 static int mapi_handles_traverse_null(TDB_CONTEXT *tdb_ctx,
239 TDB_DATA key, TDB_DATA dbuf,
243 uint32_t *handle = (uint32_t *) state;
244 char *handle_str = NULL;
246 if (dbuf.dptr && (dbuf.dsize == sizeof(MAPI_HANDLES_NULL) - 1) && !strncmp((const char *)dbuf.dptr, MAPI_HANDLES_NULL, dbuf.dsize)) {
247 mem_ctx = talloc_named(NULL, 0, "mapi_handles_traverse_null");
248 handle_str = talloc_strndup(mem_ctx, (char *)key.dptr, key.dsize);
249 *handle = strtol((const char *) handle_str, NULL, 16);
250 talloc_free(mem_ctx);
260 \details Add a handles to the database and return a pointer on
263 \param handles_ctx pointer to the MAPI handles context
264 \param container_handle the container handle if available
265 \param rec pointer on pointer to the MAPI handle structure the function
268 \return MAPI_E_SUCCESS on success, otherwise MAPI error
270 _PUBLIC_ enum MAPISTATUS mapi_handles_add(struct mapi_handles_context *handles_ctx,
271 uint32_t container_handle, struct mapi_handles **rec)
274 enum MAPISTATUS retval;
278 struct mapi_handles *el;
282 OPENCHANGE_RETVAL_IF(!handles_ctx, MAPI_E_NOT_INITIALIZED, NULL);
283 OPENCHANGE_RETVAL_IF(!handles_ctx->tdb_ctx, MAPI_E_NOT_INITIALIZED, NULL);
284 OPENCHANGE_RETVAL_IF(handle == MAPI_HANDLES_RESERVED, MAPI_E_INVALID_PARAMETER, NULL);
285 OPENCHANGE_RETVAL_IF(!rec, MAPI_E_INVALID_PARAMETER, NULL);
287 mem_ctx = talloc_named(NULL, 0, "mapi_handles_add");
289 /* Step 1. Seek the TDB database for the first free record */
290 ret = tdb_traverse(handles_ctx->tdb_ctx, mapi_handles_traverse_null, (void *)&handle);
291 if (ret > -1 && handle > 0) {
292 DEBUG(0, ("We have found free record 0x%x\n", handle));
293 retval = mapi_handles_tdb_update(handles_ctx, handle, container_handle);
294 OPENCHANGE_RETVAL_IF(retval, retval, mem_ctx);
296 el = talloc_zero((TALLOC_CTX *)handles_ctx, struct mapi_handles);
298 mapi_handles_tdb_free(handles_ctx, handle);
299 OPENCHANGE_RETVAL_IF(!el, MAPI_E_NOT_ENOUGH_RESOURCES, mem_ctx);
303 el->parent_handle = container_handle;
304 el->private_data = NULL;
306 DLIST_ADD_END(handles_ctx->handles, el, struct mapi_handles *);
308 talloc_free(mem_ctx);
310 return MAPI_E_SUCCESS;
313 /* Step 2. If no record is available, create a new one */
314 key.dptr = (unsigned char *) talloc_asprintf(mem_ctx, "0x%x", handles_ctx->last_handle);
315 key.dsize = strlen((const char *)key.dptr);
317 if (container_handle) {
318 dbuf.dptr = (unsigned char *) talloc_asprintf(mem_ctx, "0x%x", container_handle);
319 dbuf.dsize = strlen((const char *)dbuf.dptr);
321 dbuf.dptr = (unsigned char *) MAPI_HANDLES_ROOT;
322 dbuf.dsize = strlen(MAPI_HANDLES_ROOT);
325 ret = tdb_store(handles_ctx->tdb_ctx, key, dbuf, TDB_INSERT);
327 DEBUG(3, ("[%s:%d]: Unable to create 0x%x record: %s\n", __FUNCTION__, __LINE__,
328 handles_ctx->last_handle, tdb_errorstr(handles_ctx->tdb_ctx)));
329 talloc_free(mem_ctx);
331 return MAPI_E_CORRUPT_STORE;
334 el = talloc_zero((TALLOC_CTX *)handles_ctx, struct mapi_handles);
336 mapi_handles_tdb_free(handles_ctx, handles_ctx->last_handle);
337 OPENCHANGE_RETVAL_IF(!el, MAPI_E_NOT_ENOUGH_RESOURCES, mem_ctx);
340 el->handle = handles_ctx->last_handle;
341 el->parent_handle = container_handle;
342 el->private_data = NULL;
344 DLIST_ADD_END(handles_ctx->handles, el, struct mapi_handles *);
346 DEBUG(5, ("handle 0x%.2x is a father of 0x%.2x\n", container_handle, el->handle));
347 handles_ctx->last_handle += 1;
348 talloc_free(mem_ctx);
350 return MAPI_E_SUCCESS;
355 \details Get the private data associated to a MAPI handle
357 \param handle pointer to the MAPI handle structure
358 \param private_data pointer on pointer to the private data the
361 \return MAPI_E_SUCCESS on success, otherwise MAPI_E_NOT_FOUND
363 _PUBLIC_ enum MAPISTATUS mapi_handles_get_private_data(struct mapi_handles *handle, void **private_data)
366 OPENCHANGE_RETVAL_IF(!handle, MAPI_E_INVALID_PARAMETER, NULL);
367 OPENCHANGE_RETVAL_IF(!private_data, MAPI_E_INVALID_PARAMETER, NULL);
368 OPENCHANGE_RETVAL_IF(!handle->private_data, MAPI_E_NOT_FOUND, NULL);
370 *private_data = handle->private_data;
372 return MAPI_E_SUCCESS;
377 \details Set the private data associated to a MAPI handle
379 \param handle pointer to the MAPI handle structure
380 \param private_data pointer to the private data to associate to the
383 \return MAPI_E_SUCCESS on success, otherwise MAPI error
385 _PUBLIC_ enum MAPISTATUS mapi_handles_set_private_data(struct mapi_handles *handle, void *private_data)
388 OPENCHANGE_RETVAL_IF(!handle, MAPI_E_INVALID_PARAMETER, NULL);
389 OPENCHANGE_RETVAL_IF(handle->private_data, MAPI_E_UNABLE_TO_COMPLETE, NULL);
391 handle->private_data = private_data;
393 return MAPI_E_SUCCESS;
397 struct mapi_handles_private {
398 struct mapi_handles_context *handles_ctx;
399 uint32_t container_handle;
403 \details Traverse TDB database and search for records
404 which dbuf value is set to state.
406 \param tdb_ctx pointer to the TDB context
407 \param key the current TDB key
408 \param dbuf the current TDB value
409 \param state pointer on private data
411 \return 1 when a free record is found, otherwise 0
413 static int mapi_handles_traverse_delete(TDB_CONTEXT *tdb_ctx,
414 TDB_DATA key, TDB_DATA dbuf,
418 struct mapi_handles_private *handles_private = (struct mapi_handles_private *) state;
420 char *container_handle_str = NULL;
421 char *handle_str = NULL;
423 mem_ctx = talloc_named(NULL, 0, "mapi_handles_traverse_delete");
424 container_handle_str = talloc_asprintf(mem_ctx, "0x%x", handles_private->container_handle);
426 if (dbuf.dptr && strlen(container_handle_str) == dbuf.dsize && !strncmp((const char *)dbuf.dptr, container_handle_str, dbuf.dsize)) {
427 handle_str = talloc_strndup(mem_ctx, (char *)key.dptr, key.dsize);
428 handle = strtol((const char *) handle_str, NULL, 16);
429 DEBUG(5, ("handles being released must NOT have child handles attached to them\n"));
431 /* DEBUG(5, ("deleting child handle: %d, %s\n", handle, handle_str)); */
432 /* mapi_handles_delete(handles_private->handles_ctx, handle); */
435 talloc_free(mem_ctx);
443 \details Remove the MAPI handle referenced by the handle parameter
444 from the double chained list and mark its associated TDB record as
447 \param handles_ctx pointer to the MAPI handles context
448 \param handle the handle to delete
450 \return MAPI_E_SUCCESS on success, otherwise MAPI error
452 _PUBLIC_ enum MAPISTATUS mapi_handles_delete(struct mapi_handles_context *handles_ctx,
456 enum MAPISTATUS retval;
458 struct mapi_handles *el;
459 struct mapi_handles_private handles_private;
464 OPENCHANGE_RETVAL_IF(!handles_ctx, MAPI_E_NOT_INITIALIZED, NULL);
465 OPENCHANGE_RETVAL_IF(!handles_ctx->tdb_ctx, MAPI_E_NOT_INITIALIZED, NULL);
466 OPENCHANGE_RETVAL_IF(handle == MAPI_HANDLES_RESERVED, MAPI_E_INVALID_PARAMETER, NULL);
468 DEBUG(4, ("[%s:%d]: Deleting MAPI handle 0x%x (handles_ctx: %p, tdb_ctx: %p)\n", __FUNCTION__, __LINE__,
469 handle, handles_ctx, handles_ctx->tdb_ctx));
471 mem_ctx = talloc_named(NULL, 0, "mapi_handles_delete");
473 key.dptr = (unsigned char *) talloc_asprintf(mem_ctx, "0x%x", handle);
474 key.dsize = strlen((const char *)key.dptr);
476 /* Step 1. Make sure the record exists */
477 ret = tdb_exists(handles_ctx->tdb_ctx, key);
478 OPENCHANGE_RETVAL_IF(!ret, MAPI_E_NOT_FOUND, mem_ctx);
480 /* Step 2. Delete this record from the double chained list */
481 for (el = handles_ctx->handles; el; el = el->next) {
482 if (el->handle == handle) {
483 DLIST_REMOVE(handles_ctx->handles, el);
489 /* This case should never occur */
490 OPENCHANGE_RETVAL_IF(found == false, MAPI_E_CORRUPT_STORE, mem_ctx);
492 /* Step 3. Free this record within the TDB database */
493 retval = mapi_handles_tdb_free(handles_ctx, handle);
494 OPENCHANGE_RETVAL_IF(retval, retval, mem_ctx);
496 /* Step 4. Delete hierarchy of children */
497 handles_private.handles_ctx = handles_ctx;
498 handles_private.container_handle = handle;
499 ret = tdb_traverse(handles_ctx->tdb_ctx, mapi_handles_traverse_delete, (void *)&handles_private);
501 talloc_free(mem_ctx);
503 DEBUG(4, ("[%s:%d]: Deleting MAPI handle 0x%x COMPLETE\n", __FUNCTION__, __LINE__, handle));
505 return MAPI_E_SUCCESS;