102cccc3988216b1487d388ac018c17158041cb0
[jelmer/openchange-proposed.git/.git] / mapiproxy / modules / mpm_cache_ldb.c
1 /*
2    MAPI Proxy - Cache module
3
4    OpenChange Project
5
6    Copyright (C) Julien Kerihuel 2008
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 mpm_cache_ldb.c
24
25    \brief LDB routines for the cache module
26  */
27
28 #include "mapiproxy/dcesrv_mapiproxy.h"
29 #include "mapiproxy/libmapiproxy.h"
30 #include "mapiproxy/modules/mpm_cache.h"
31 #include <libmapi/defs_private.h>
32 #include <util/debug.h>
33
34 /**
35    \details Create the cache database
36
37    \param dce_ctx pointer to the session context
38    \param database the complete path to the tdb store
39    \param ldb_ctx pointer to pointer on the the LDB context
40
41    \return NT_STATUS_OK on success, otherwise NT_ERROR:
42    NT_STATUS_NO_MEMORY, NT_STATUS_NOT_FOUND.
43  */
44 NTSTATUS mpm_cache_ldb_createdb(struct dcesrv_context *dce_ctx, 
45                                 const char *database, 
46                                 struct ldb_context **ldb_ctx)
47 {
48         struct ldb_context      *tmp_ctx;
49         struct event_context    *ev;
50         int                     ret;
51
52         ev = event_context_init(dce_ctx);
53         if (!ev) return NT_STATUS_NO_MEMORY;
54
55         tmp_ctx = ldb_init(dce_ctx, ev);
56         if (!tmp_ctx) return NT_STATUS_NO_MEMORY;
57         
58         ret = ldb_connect(tmp_ctx, database, 0, NULL);
59         if (ret != LDB_SUCCESS) {
60                 return NT_STATUS_NOT_FOUND;
61         }
62
63         *ldb_ctx = tmp_ctx;
64
65         return NT_STATUS_OK;
66 }
67
68
69 /**
70    \details Add a folder record to the TDB store
71
72    \param mem_ctx pointer to the memory context
73    \param ldb_ctx pointer to the LDB context
74    \param FolderId the ID we will be using to uniquely create the
75    record
76
77    \return NT_STATUS_OK on success, otherwise NT_STATUS_NOT_FOUND
78  */
79 static NTSTATUS  mpm_cache_ldb_add_folder(TALLOC_CTX *mem_ctx, 
80                                           struct ldb_context *ldb_ctx,
81                                           uint64_t FolderId)
82 {
83         struct ldb_message      *msg;
84         char                    *dn;
85         int                     ret;
86
87         msg = ldb_msg_new(mem_ctx);
88         if (msg == NULL) {
89                 return NT_STATUS_NO_MEMORY;
90         }
91
92         dn = talloc_asprintf(mem_ctx, "CN=0x%"PRIx64",CN=Cache", FolderId);
93         msg->dn = ldb_dn_new(ldb_ctx, ldb_ctx, dn);
94         talloc_free(dn);
95         if (!msg->dn) {
96                 return NT_STATUS_NO_MEMORY;
97         }
98
99         ret = ldb_add(ldb_ctx, msg);
100         if (ret != 0) {
101                 DEBUG(0, ("* [%s:%d] Failed to modify record %s: %s\n",
102                           MPM_LOCATION, ldb_dn_get_linearized(msg->dn), 
103                           ldb_errstring(ldb_ctx)));
104                 return NT_STATUS_UNSUCCESSFUL;
105         }
106
107         return NT_STATUS_OK;
108 }
109
110
111 /**
112    \details Add a message record to the TDB store
113
114    \param mem_ctx pointer to the memory context
115    \param ldb_ctx pointer to the LDB context
116    \param message pointer to the mpm_message entry with the folder and
117    message ID
118
119    \return NT_STATUS_OK on success, otherwise a NT error
120  */
121 NTSTATUS mpm_cache_ldb_add_message(TALLOC_CTX *mem_ctx, 
122                                    struct ldb_context *ldb_ctx, 
123                                    struct mpm_message *message)
124 {
125         NTSTATUS                status;
126         struct ldb_message      *msg;
127         struct ldb_dn           *dn;
128         struct ldb_result       *res;
129         char                    *basedn;
130         int                     ret;
131
132         /* First check if the CN=Folder,CN=Cache entry exists */
133         basedn = talloc_asprintf(mem_ctx, "CN=0x%"PRIx64",CN=Cache", message->FolderId);
134         dn = ldb_dn_new(mem_ctx, ldb_ctx, basedn);
135         talloc_free(basedn);
136         if (!dn) return NT_STATUS_UNSUCCESSFUL;
137         ret = ldb_search(ldb_ctx, mem_ctx, &res, dn, LDB_SCOPE_BASE, NULL, NULL);
138         if (ret == LDB_SUCCESS && !res->count) {
139                 DEBUG(5, ("* [%s:%d] We have to create folder TDB record: CN=0x%"PRIx64",CN=Cache\n", 
140                           MPM_LOCATION, message->FolderId));
141                 status = mpm_cache_ldb_add_folder(mem_ctx, ldb_ctx, message->FolderId);
142                 if (!NT_STATUS_IS_OK(status)) return status;
143         }
144
145         /* Search if the message doesn't already exist */
146         basedn = talloc_asprintf(mem_ctx, "CN=0x%"PRIx64",CN=0x%"PRIx64",CN=Cache", 
147                                  message->MessageId, message->FolderId);
148         dn = ldb_dn_new(mem_ctx, ldb_ctx, basedn);
149         talloc_free(basedn);
150         if (!dn) return NT_STATUS_UNSUCCESSFUL;
151         ret = ldb_search(ldb_ctx, mem_ctx, &res, dn, LDB_SCOPE_BASE, NULL, NULL);
152         if (res->count) return NT_STATUS_OK;
153
154         /* Create the CN=Message,CN=Folder,CN=Cache */
155         msg = ldb_msg_new(mem_ctx);
156         if (msg == NULL) return NT_STATUS_NO_MEMORY;
157
158         basedn = talloc_asprintf(mem_ctx, "CN=0x%"PRIx64",CN=0x%"PRIx64",CN=Cache", 
159                                  message->MessageId, message->FolderId);
160         msg->dn = ldb_dn_new(ldb_ctx, ldb_ctx, basedn);
161         talloc_free(basedn);
162         if (!msg->dn) return NT_STATUS_NO_MEMORY;
163
164         ret = ldb_add(ldb_ctx, msg);
165         if (ret != 0) {
166                 DEBUG(0, ("* [%s:%d] Failed to modify record %s: %s\n",
167                           MPM_LOCATION, ldb_dn_get_linearized(msg->dn), 
168                           ldb_errstring(ldb_ctx)));
169                 return NT_STATUS_UNSUCCESSFUL;
170         }
171
172         return NT_STATUS_OK;
173 }
174
175
176 /**
177    \details Add an attachment record to the TDB store
178  
179    \param mem_ctx pointer to the memory context
180    \param ldb_ctx pointer to the LDB context
181    \param attach pointer to the mpm_attachment entry
182
183    \return NT_STATUS_OK on success, otherwise a NT error
184 */
185 NTSTATUS mpm_cache_ldb_add_attachment(TALLOC_CTX *mem_ctx,
186                                       struct ldb_context *ldb_ctx,
187                                       struct mpm_attachment *attach)
188 {
189         struct mpm_message      *message;
190         struct ldb_message      *msg;
191         struct ldb_dn           *dn;
192         struct ldb_result       *res;
193         char                    *basedn;
194         int                     ret;
195
196         message = attach->message;
197
198         /* Search if the attachment doesn't already exist */
199         basedn = talloc_asprintf(mem_ctx, "CN=%d,CN=0x%"PRIx64",CN=0x%"PRIx64",CN=Cache",
200                                  attach->AttachmentID, message->MessageId, 
201                                  message->FolderId);
202         dn = ldb_dn_new(mem_ctx, ldb_ctx, basedn);
203         talloc_free(basedn);
204         if (!dn) return NT_STATUS_UNSUCCESSFUL;
205         ret = ldb_search(ldb_ctx, mem_ctx, &res, dn, LDB_SCOPE_BASE, NULL, NULL);
206         if (ret == LDB_SUCCESS && res->count) return NT_STATUS_OK;
207
208         DEBUG(2, ("* [%s:%d] Create the attachment TDB record\n", MPM_LOCATION));
209
210         msg = ldb_msg_new(mem_ctx);
211         if (msg == NULL) return NT_STATUS_NO_MEMORY;
212         
213         basedn = talloc_asprintf(mem_ctx, "CN=%d,CN=0x%"PRIx64",CN=0x%"PRIx64",CN=Cache",
214                                  attach->AttachmentID, message->MessageId, 
215                                  message->FolderId);
216         msg->dn = ldb_dn_new(ldb_ctx, ldb_ctx, basedn);
217         talloc_free(basedn);
218         if (!msg->dn) return NT_STATUS_NO_MEMORY;
219
220         ret = ldb_add(ldb_ctx, msg);
221         if (ret != 0) {
222                 DEBUG(0, ("* [%s:%d] Failed to modify record %s: %s\n",
223                           MPM_LOCATION, ldb_dn_get_linearized(msg->dn), 
224                           ldb_errstring(ldb_ctx)));
225                 return NT_STATUS_UNSUCCESSFUL;
226         }
227
228         return NT_STATUS_OK;
229 }
230
231
232 /**
233    \details Add stream references to a message or attachment in the
234    TDB store
235
236    \param mpm pointer to the cache module general structure
237    \param ldb_ctx pointer to the LDB context
238    \param stream pointer to the mpm_stream entry
239
240    \return NT_STATUS_OK on success, otherwise NT error
241  */
242 NTSTATUS mpm_cache_ldb_add_stream(struct mpm_cache *mpm, 
243                                   struct ldb_context *ldb_ctx,
244                                   struct mpm_stream *stream)
245 {
246         TALLOC_CTX              *mem_ctx;
247         struct mpm_message      *message;
248         struct mpm_attachment   *attach;
249         struct ldb_message      *msg;
250         struct ldb_dn           *dn;
251         const char * const      attrs[] = { "*", NULL };
252         struct ldb_result       *res;
253         char                    *basedn = NULL;
254         char                    *attribute;
255         int                     ret;
256         uint32_t                i;
257
258         mem_ctx = (TALLOC_CTX *) mpm;
259         
260         if (stream->attachment) {
261                 attach = stream->attachment;
262                 message = attach->message;
263         } else if (stream->message) {
264                 attach = NULL;
265                 message = stream->message;
266         } else {
267                 return NT_STATUS_OK;
268         }
269
270         /* This is a stream for an attachment */
271         if (stream->attachment) {
272                 basedn = talloc_asprintf(mem_ctx, "CN=%d,CN=0x%"PRIx64",CN=0x%"PRIx64",CN=Cache",
273                                          attach->AttachmentID, message->MessageId,
274                                          message->FolderId);
275                 dn = ldb_dn_new(mem_ctx, ldb_ctx, basedn);
276                 talloc_free(basedn);
277                 if (!dn) return NT_STATUS_UNSUCCESSFUL;
278                 
279                 attribute = talloc_asprintf(mem_ctx, "(0x%x=*)", stream->PropertyTag);
280                 ret = ldb_search(ldb_ctx, mem_ctx, &res, dn, LDB_SCOPE_BASE, attrs, 
281                                                  attribute);
282                 talloc_free(attribute);
283
284                 if (ret == LDB_SUCCESS && res->count == 1) {
285                         attribute = talloc_asprintf(mem_ctx, "0x%x", stream->PropertyTag);
286                         basedn = (char *) ldb_msg_find_attr_as_string(res->msgs[0], attribute, NULL);
287                         talloc_free(attribute);
288                         DEBUG(2, ("* [%s:%d] Loading from cache 0x%x = %s\n", MPM_LOCATION,
289                                   stream->PropertyTag, basedn));
290                         stream->filename = talloc_strdup(mem_ctx, basedn);
291                         stream->cached = true;
292                         stream->ahead = false;
293                         mpm_cache_stream_open(mpm, stream);
294
295                         return NT_STATUS_OK;
296                 }
297
298                 /* Otherwise create the stream with basedn above */
299                 basedn = talloc_asprintf(mem_ctx, "CN=%d,CN=0x%"PRIx64",CN=0x%"PRIx64",CN=Cache",
300                                          attach->AttachmentID, message->MessageId,
301                                          message->FolderId);
302
303                 DEBUG(2, ("* [%s:%d] Create the stream TDB record for attachment\n", MPM_LOCATION));
304         } 
305
306         if (stream->message) {
307                 basedn = talloc_asprintf(mem_ctx, "CN=0x%"PRIx64",CN=0x%"PRIx64",CN=Cache",
308                                          message->MessageId, message->FolderId);
309                 dn = ldb_dn_new(mem_ctx, ldb_ctx, basedn);
310                 talloc_free(basedn);
311                 if (!dn) return NT_STATUS_UNSUCCESSFUL;
312
313                 attribute = talloc_asprintf(mem_ctx, "(0x%x=*)", stream->PropertyTag);
314                 ret = ldb_search(ldb_ctx, mem_ctx, &res, dn, LDB_SCOPE_BASE, attrs, attribute);
315                 talloc_free(attribute);
316
317                 if (ret == LDB_SUCCESS && res->count == 1) {
318                         attribute = talloc_asprintf(mem_ctx, "0x%x", stream->PropertyTag);
319                         basedn = (char *) ldb_msg_find_attr_as_string(res->msgs[0], attribute, NULL);
320                         talloc_free(attribute);
321                         DEBUG(2, ("* [%s:%d] Loading from cache 0x%x = %s\n", MPM_LOCATION,
322                                   stream->PropertyTag, basedn));
323                         stream->filename = talloc_strdup(mem_ctx, basedn);
324                         stream->cached = true;
325                         stream->ahead = false;
326                         mpm_cache_stream_open(mpm, stream);
327
328                         return NT_STATUS_OK;
329                 }
330
331                 /* Otherwise create the stream with basedn above */
332                 basedn = talloc_asprintf(mem_ctx, "CN=0x%"PRIx64",CN=0x%"PRIx64",CN=Cache",
333                                          message->MessageId, message->FolderId);
334                 
335                 DEBUG(2, ("* [%s:%d] Modify the message TDB record and append stream information\n",
336                           MPM_LOCATION));
337         }
338
339         stream->cached = false;
340         mpm_cache_stream_open(mpm, stream);
341
342         msg = ldb_msg_new(mem_ctx);
343         if (msg == NULL) return NT_STATUS_NO_MEMORY;
344
345         msg->dn = ldb_dn_new(ldb_ctx, ldb_ctx, basedn);
346         talloc_free(basedn);
347         if (!msg->dn) return NT_STATUS_NO_MEMORY;
348         
349         attribute = talloc_asprintf(mem_ctx, "0x%x", stream->PropertyTag);
350         ldb_msg_add_fmt(msg, attribute, "%s", stream->filename);
351         talloc_free(attribute);
352         
353         attribute = talloc_asprintf(mem_ctx, "0x%x_StreamSize", stream->PropertyTag);
354         ldb_msg_add_fmt(msg, attribute, "%d", stream->StreamSize);
355         talloc_free(attribute);
356
357         /* mark all the message elements as LDB_FLAG_MOD_REPLACE */
358         for (i=0;i<msg->num_elements;i++) {
359                 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
360         }
361         
362         ret = ldb_modify(ldb_ctx, msg);
363         if (ret != 0) {
364                 DEBUG(0, ("* [%s:%d] Failed to modify record %s: %s\n",
365                           MPM_LOCATION, ldb_dn_get_linearized(msg->dn), 
366                           ldb_errstring(ldb_ctx)));
367                 return NT_STATUS_UNSUCCESSFUL;
368         }
369
370         return NT_STATUS_OK;
371 }