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