Merge in r3786-3788,3794-3795 from sogo branch
[jelmer/openchange-proposed.git/.git] / mapiproxy / libmapiproxy / mapi_handles.c
1 /*
2    OpenChange Server implementation
3
4    MAPI handles API implementation
5
6    Copyright (C) Julien Kerihuel 2009
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 /**
23    \file mapi_handles.c
24
25    \brief API for MAPI handles management
26  */
27
28 #include "mapiproxy/dcesrv_mapiproxy.h"
29 #include "libmapi/libmapi.h"
30 #include "libmapi/libmapi_private.h"
31 #include "libmapiproxy.h"
32
33
34 /**
35    \details Initialize MAPI handles context
36
37    \param mem_ctx pointer to the memory context
38
39    \return Allocated MAPI handles context on success, otherwise NULL
40  */
41 _PUBLIC_ struct mapi_handles_context *mapi_handles_init(TALLOC_CTX *mem_ctx)
42 {
43         struct mapi_handles_context     *handles_ctx;
44
45         /* Step 1. Initialize the context */
46         handles_ctx = talloc_zero(mem_ctx, struct mapi_handles_context);
47         if (!handles_ctx) return NULL;
48
49         /* Step 2. Initialize the TDB context */
50         handles_ctx->tdb_ctx = tdb_open(NULL, 0, TDB_INTERNAL, O_RDWR|O_CREAT, 0600);
51
52         /* Step 3. Initialize the handles list */
53         handles_ctx->handles = NULL;
54
55         /* Step 4. Set last_handle to the first valid value */
56         handles_ctx->last_handle = 1;
57
58         return handles_ctx;
59 }
60
61
62 /**
63    \details Release MAPI handles context
64
65    \param handles_ctx pointer to the MAPI handles context
66
67    \return MAPI_E_SUCCESS on success, otherwise MAPI error
68  */
69 _PUBLIC_ enum MAPISTATUS mapi_handles_release(struct mapi_handles_context *handles_ctx)
70 {
71         /* Sanity checks */
72         OPENCHANGE_RETVAL_IF(!handles_ctx, MAPI_E_NOT_INITIALIZED, NULL);
73
74         tdb_close(handles_ctx->tdb_ctx);
75         talloc_free(handles_ctx);
76
77         return MAPI_E_SUCCESS;
78 }
79
80
81 /**
82    \details Search for a record in the TDB database
83
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
87    returns
88    
89    \return MAPI_E_SUCCESS on success, otherwise MAPI error
90  */
91 _PUBLIC_ enum MAPISTATUS mapi_handles_search(struct mapi_handles_context *handles_ctx,
92                                              uint32_t handle, struct mapi_handles **rec)
93 {
94         TALLOC_CTX              *mem_ctx;
95         TDB_DATA                key;
96         TDB_DATA                dbuf;
97         struct mapi_handles     *el;
98
99         /* Sanity checks */
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);
104
105         mem_ctx = talloc_named(NULL, 0, "mapi_handles_search");
106
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);
110
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);
115
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)) {
119                 free(dbuf.dptr);
120                 return MAPI_E_NOT_FOUND;
121         }
122         free(dbuf.dptr);
123
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) {
127                         *rec = el;
128                         return MAPI_E_SUCCESS;
129                 }
130         }
131
132         return MAPI_E_CORRUPT_STORE;
133 }
134
135
136 /**
137    \details Set a TDB record data as null meaning it can be reused in
138    the future.
139
140    \param handles_ctx pointer to the MAPI handles context
141    \param handle handle key value to free
142
143    \return MAPI_E_SUCCESS on success, otherwise MAPI error
144  */
145 static enum MAPISTATUS mapi_handles_tdb_free(struct mapi_handles_context *handles_ctx,
146                                              uint32_t handle)
147 {
148         TALLOC_CTX              *mem_ctx;
149         TDB_DATA                key;
150         TDB_DATA                dbuf;
151         int                     ret;
152
153         /* Sanity checks */
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);
157
158         mem_ctx = talloc_named(NULL, 0, "mapi_handles_tdb_free");
159         
160         key.dptr = (unsigned char *) talloc_asprintf(mem_ctx, "0x%x", handle);
161         key.dsize = strlen((const char *)key.dptr);
162
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);
166
167         /* Step 2. Update existing record */
168         dbuf.dptr = (unsigned char *)MAPI_HANDLES_NULL;
169         dbuf.dsize = sizeof(MAPI_HANDLES_NULL) - 1;
170
171         ret = tdb_store(handles_ctx->tdb_ctx, key, dbuf, TDB_MODIFY);
172         talloc_free(mem_ctx);
173         if (ret == -1) {
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;
177         }
178
179         return MAPI_E_SUCCESS;
180 }
181
182
183 /**
184    \details Update a TDB record
185
186    
187  */
188 static enum MAPISTATUS mapi_handles_tdb_update(struct mapi_handles_context *handles_ctx,
189                                                uint32_t handle, uint32_t container_handle)
190 {
191         TALLOC_CTX      *mem_ctx;
192         TDB_DATA        key;
193         TDB_DATA        dbuf;
194         int             ret;
195
196         /* Sanity checks */
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);
200
201         mem_ctx = talloc_named(NULL, 0, "mapi_handles_tdb_update");
202
203         key.dptr = (unsigned char *) talloc_asprintf(mem_ctx, "0x%x", handle);
204         key.dsize = strlen((const char *)key.dptr);
205
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);
209
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);
213
214         ret = tdb_store(handles_ctx->tdb_ctx, key, dbuf, TDB_MODIFY);
215         talloc_free(mem_ctx);
216         if (ret == -1) {
217                 DEBUG(3, ("[%s:%d]: Unable to update 0x%x record: %s\n", __FUNCTION__, __LINE__,
218                           handle, tdb_errorstr(handles_ctx->tdb_ctx)));
219
220                 return MAPI_E_CORRUPT_STORE;    
221         }
222
223         return MAPI_E_SUCCESS;
224 }
225
226
227 /**
228    \details Traverse TDB database and search for the first record
229    which dbuf value is "null" string.
230  
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
235
236    \return 1 when a free record is found, otherwise 0
237 */
238 static int mapi_handles_traverse_null(TDB_CONTEXT *tdb_ctx,
239                                       TDB_DATA key, TDB_DATA dbuf,
240                                       void *state)
241 {
242         TALLOC_CTX      *mem_ctx;
243         uint32_t        *handle = (uint32_t *) state;
244         char            *handle_str = NULL;
245
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);
251
252                 return 1;
253         }
254
255         return 0;
256 }
257
258
259 /**
260    \details Add a handles to the database and return a pointer on
261    created record
262
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
266    returns
267
268    \return MAPI_E_SUCCESS on success, otherwise MAPI error
269  */
270 _PUBLIC_ enum MAPISTATUS mapi_handles_add(struct mapi_handles_context *handles_ctx,
271                                           uint32_t container_handle, struct mapi_handles **rec)
272 {
273         TALLOC_CTX              *mem_ctx;
274         enum MAPISTATUS         retval;
275         TDB_DATA                key;
276         TDB_DATA                dbuf;
277         uint32_t                handle = 0;
278         struct mapi_handles     *el;
279         int                     ret;
280
281         /* Sanity checks */
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);
286
287         mem_ctx = talloc_named(NULL, 0, "mapi_handles_add");
288
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);
295                 
296                 el = talloc_zero((TALLOC_CTX *)handles_ctx, struct mapi_handles);
297                 if (!el) {
298                         mapi_handles_tdb_free(handles_ctx, handle);
299                         OPENCHANGE_RETVAL_IF(!el, MAPI_E_NOT_ENOUGH_RESOURCES, mem_ctx);
300                 }
301
302                 el->handle = handle;
303                 el->parent_handle = container_handle;
304                 el->private_data = NULL;
305                 *rec = el;
306                 DLIST_ADD_END(handles_ctx->handles, el, struct mapi_handles *);
307
308                 talloc_free(mem_ctx);
309
310                 return MAPI_E_SUCCESS;
311         }
312
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);
316
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);
320         } else {
321                 dbuf.dptr = (unsigned char *) MAPI_HANDLES_ROOT;
322                 dbuf.dsize = strlen(MAPI_HANDLES_ROOT);
323         }
324
325         ret = tdb_store(handles_ctx->tdb_ctx, key, dbuf, TDB_INSERT);
326         if (ret == -1) {
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);
330
331                 return MAPI_E_CORRUPT_STORE;
332         }
333
334         el = talloc_zero((TALLOC_CTX *)handles_ctx, struct mapi_handles);
335         if (!el) {
336                 mapi_handles_tdb_free(handles_ctx, handles_ctx->last_handle);
337                 OPENCHANGE_RETVAL_IF(!el, MAPI_E_NOT_ENOUGH_RESOURCES, mem_ctx);
338         }
339
340         el->handle = handles_ctx->last_handle;
341         el->parent_handle = container_handle;
342         el->private_data = NULL;
343         *rec = el;
344         DLIST_ADD_END(handles_ctx->handles, el, struct mapi_handles *);
345
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);
349
350         return MAPI_E_SUCCESS;
351 }
352
353
354 /**
355    \details Get the private data associated to a MAPI handle
356
357    \param handle pointer to the MAPI handle structure
358    \param private_data pointer on pointer to the private data the
359    function returns
360
361    \return MAPI_E_SUCCESS on success, otherwise MAPI_E_NOT_FOUND
362  */
363 _PUBLIC_ enum MAPISTATUS mapi_handles_get_private_data(struct mapi_handles *handle, void **private_data)
364 {
365         /* Sanity checks */
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);
369
370         *private_data = handle->private_data;
371         
372         return MAPI_E_SUCCESS;
373 }
374
375
376 /**
377    \details Set the private data associated to a MAPI handle
378
379    \param handle pointer to the MAPI handle structure
380    \param private_data pointer to the private data to associate to the
381    MAPI handle
382
383    \return MAPI_E_SUCCESS on success, otherwise MAPI error
384  */
385 _PUBLIC_ enum MAPISTATUS mapi_handles_set_private_data(struct mapi_handles *handle, void *private_data)
386 {
387         /* Sanity checks */
388         OPENCHANGE_RETVAL_IF(!handle, MAPI_E_INVALID_PARAMETER, NULL);
389         OPENCHANGE_RETVAL_IF(handle->private_data, MAPI_E_UNABLE_TO_COMPLETE, NULL);
390
391         handle->private_data = private_data;
392
393         return MAPI_E_SUCCESS;
394 }
395
396
397 struct mapi_handles_private {
398         struct mapi_handles_context     *handles_ctx;
399         uint32_t                        container_handle;
400 };
401
402 /**
403    \details Traverse TDB database and search for records
404    which dbuf value is set to state.
405  
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
410
411    \return 1 when a free record is found, otherwise 0
412 */
413 static int mapi_handles_traverse_delete(TDB_CONTEXT *tdb_ctx,
414                                         TDB_DATA key, TDB_DATA dbuf,
415                                         void *state)
416 {
417         TALLOC_CTX                      *mem_ctx;
418         struct mapi_handles_private     *handles_private = (struct mapi_handles_private *) state;
419         uint32_t                        handle;
420         char                            *container_handle_str = NULL;
421         char                            *handle_str = NULL;
422
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);
425
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);
430                 /* abort(); */
431                 /* DEBUG(5, ("deleting child handle: %d, %s\n", handle, handle_str)); */
432                 mapi_handles_delete(handles_private->handles_ctx, handle);
433         }
434
435         talloc_free(mem_ctx);
436
437         return 0;
438 }
439
440
441
442 /**
443    \details Remove the MAPI handle referenced by the handle parameter
444    from the double chained list and mark its associated TDB record as
445    null
446
447    \param handles_ctx pointer to the MAPI handles context
448    \param handle the handle to delete
449
450    \return MAPI_E_SUCCESS on success, otherwise MAPI error
451  */
452 _PUBLIC_ enum MAPISTATUS mapi_handles_delete(struct mapi_handles_context *handles_ctx, 
453                                              uint32_t handle)
454 {
455         TALLOC_CTX                      *mem_ctx;
456         enum MAPISTATUS                 retval;
457         TDB_DATA                        key;
458         struct mapi_handles             *el;
459         struct mapi_handles_private     handles_private;
460         int                             ret;
461         bool                            found = false;
462
463         /* Sanity checks */
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);
467
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));
470
471         mem_ctx = talloc_named(NULL, 0, "mapi_handles_delete");
472
473         key.dptr = (unsigned char *) talloc_asprintf(mem_ctx, "0x%x", handle);
474         key.dsize = strlen((const char *)key.dptr);
475
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);
479
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);
484                         talloc_free(el);
485                         found = true;
486                         break;
487                 }
488         }
489         /* This case should never occur */
490         OPENCHANGE_RETVAL_IF(found == false, MAPI_E_CORRUPT_STORE, mem_ctx);
491
492         /* Step 3. Free this record within the TDB database */
493         retval = mapi_handles_tdb_free(handles_ctx, handle);
494         /* if (retval) { */
495         /*      abort(); */
496         /* } */
497         OPENCHANGE_RETVAL_IF(retval, retval, mem_ctx);
498
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);
503
504         talloc_free(mem_ctx);
505
506         DEBUG(4, ("[%s:%d]: Deleting MAPI handle 0x%x COMPLETE\n", __FUNCTION__, __LINE__, handle));
507
508         return MAPI_E_SUCCESS;
509 }