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 = sizeof(MAPI_HANDLES_NULL) - 1;
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) == 0) {
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 (potential child handle)
408 \param dbuf the current TDB value (parent handle)
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) == 0) {
427 handle_str = talloc_strndup(mem_ctx, (char *)key.dptr, key.dsize);
428 DEBUG(5, ("handles being released must NOT have child handles attached to them (%s is a child of %s)\n", handle_str, container_handle_str));
429 handle = strtol((const char *) handle_str, NULL, 16);
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);
497 OPENCHANGE_RETVAL_IF(retval, retval, mem_ctx);
499 /* Step 4. Delete hierarchy of children */
500 handles_private.handles_ctx = handles_ctx;
501 handles_private.container_handle = handle;
502 ret = tdb_traverse(handles_ctx->tdb_ctx, mapi_handles_traverse_delete, (void *)&handles_private);
504 talloc_free(mem_ctx);
506 DEBUG(4, ("[%s:%d]: Deleting MAPI handle 0x%x COMPLETE\n", __FUNCTION__, __LINE__, handle));
508 return MAPI_E_SUCCESS;