2f2e24499d71c540c710d5485093d12e0d1e449b
[jelmer/openchange.git] / mapiproxy / libmapiproxy / openchangedb_table.c
1 /*
2    OpenChange Server implementation
3
4    OpenChangeDB table object implementation
5
6    Copyright (C) Julien Kerihuel 2011
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 openchangedb_table.c
24
25    \brief OpenChange Dispatcher database table routines
26  */
27
28 #include <inttypes.h>
29
30 #include "mapiproxy/dcesrv_mapiproxy.h"
31 #include "libmapiproxy.h"
32 #include "libmapi/libmapi.h"
33 #include "libmapi/libmapi_private.h"
34
35 /**
36    /details Initialize an openchangedb table
37
38    \param mem_ctx pointer to the memory context to use for allocation
39    \param table_type the type of table this object represents
40    \param folderID the identifier of the folder this table represents
41    \param table_object pointer on pointer to the table object to return
42
43    \return MAPI_E_SUCCESS on success, otherwise MAPISTORE error
44  */
45 _PUBLIC_ enum MAPISTATUS openchangedb_table_init(TALLOC_CTX *mem_ctx, uint8_t table_type, 
46                                                  uint64_t folderID, void **table_object)
47 {
48         struct openchangedb_table       *table;
49
50         /* Sanity checks */
51         MAPI_RETVAL_IF(!table_object, MAPI_E_NOT_INITIALIZED, NULL);
52
53         table = talloc_zero(mem_ctx, struct openchangedb_table);
54         if (!table) {
55                 return MAPI_E_NOT_ENOUGH_MEMORY;
56         }
57         /* printf("openchangedb_table_init: folderID=%"PRIu64"\n", folderID); */
58         table->folderID = folderID;
59         table->table_type = table_type;
60         table->lpSortCriteria = NULL;
61         table->restrictions = NULL;
62         table->res = NULL;
63
64         *table_object = (void *)table;
65
66         return MAPI_E_SUCCESS;
67 }
68
69
70 /**
71    \details Set sort order to specified openchangedb table object
72
73    \param table_object pointer to the table object
74    \param lpSortCriteria pointer to the sort order to save
75
76    \return MAPI_E_SUCCESS on success, otherwise MAPI error
77  */
78 _PUBLIC_ enum MAPISTATUS openchangedb_table_set_sort_order(void *table_object, 
79                                                            struct SSortOrderSet *lpSortCriteria)
80 {
81         struct openchangedb_table       *table;
82
83         /* Sanity checks */
84         MAPI_RETVAL_IF(!table_object, MAPI_E_NOT_INITIALIZED, NULL);
85         MAPI_RETVAL_IF(!lpSortCriteria, MAPI_E_INVALID_PARAMETER, NULL);
86
87         table = (struct openchangedb_table *) table_object;
88
89         if (table->res) {
90                 talloc_free(table->res);
91                 table->res = NULL;
92         }
93
94         if (table->lpSortCriteria) {
95                 talloc_free(table->lpSortCriteria);
96         }
97         if (lpSortCriteria) {
98                 table->lpSortCriteria = talloc_memdup((TALLOC_CTX *)table, lpSortCriteria, sizeof(struct SSortOrderSet));
99                 if (!table->lpSortCriteria) {
100                         return MAPI_E_NOT_ENOUGH_MEMORY;
101                 }
102                 table->lpSortCriteria->aSort = talloc_memdup((TALLOC_CTX *)table->lpSortCriteria, lpSortCriteria->aSort, lpSortCriteria->cSorts * sizeof(struct SSortOrder));
103                 if (!table->lpSortCriteria->aSort) {
104                         return MAPI_E_NOT_ENOUGH_MEMORY;
105                 }
106         }
107         else {
108                 table->lpSortCriteria = NULL;
109         }
110
111         return MAPI_E_SUCCESS;
112 }
113
114
115 _PUBLIC_ enum MAPISTATUS openchangedb_table_set_restrictions(void *table_object,
116                                                              struct mapi_SRestriction *res)
117 {
118         struct openchangedb_table       *table;
119
120         /* Sanity checks */
121         MAPI_RETVAL_IF(!table_object, MAPI_E_NOT_INITIALIZED, NULL);
122         MAPI_RETVAL_IF(!res, MAPI_E_INVALID_PARAMETER, NULL);
123
124         table = (struct openchangedb_table *) table_object;
125
126         if (table->res) {
127                 talloc_free(table->res);
128                 table->res = NULL;
129         }
130
131         if (table->restrictions) {
132                 talloc_free(table->restrictions);
133                 table->restrictions = NULL;
134         }
135
136         MAPI_RETVAL_IF(!res, MAPI_E_SUCCESS, NULL);
137
138         table->restrictions = talloc_zero((TALLOC_CTX *)table_object, struct mapi_SRestriction);
139
140         switch (res->rt) {
141         case RES_PROPERTY:
142                 table->restrictions->rt = res->rt;
143                 table->restrictions->res.resProperty.relop = res->res.resProperty.relop;
144                 table->restrictions->res.resProperty.ulPropTag = res->res.resProperty.ulPropTag;
145                 table->restrictions->res.resProperty.lpProp.ulPropTag = res->res.resProperty.lpProp.ulPropTag;
146
147                 switch (table->restrictions->res.resProperty.lpProp.ulPropTag & 0xFFFF) {
148                 case PT_STRING8:
149                         table->restrictions->res.resProperty.lpProp.value.lpszA = talloc_strdup((TALLOC_CTX *)table->restrictions, res->res.resProperty.lpProp.value.lpszA);
150                         break;
151                 case PT_UNICODE:
152                         table->restrictions->res.resProperty.lpProp.value.lpszW = talloc_strdup((TALLOC_CTX *)table->restrictions, res->res.resProperty.lpProp.value.lpszW);
153                         break;
154                 default:
155                         DEBUG(0, ("Unsupported property type for RES_PROPERTY restriction\n"));
156                         break;
157                 }
158                 break;
159         default:
160                 DEBUG(0, ("Unsupported restriction type: 0x%x\n", res->rt));
161         }
162
163         return MAPI_E_SUCCESS;
164 }
165
166 static char *openchangedb_table_build_filter(TALLOC_CTX *mem_ctx, struct openchangedb_table *table, uint64_t row_fmid, struct mapi_SRestriction *restrictions)
167 {
168         char            *filter = NULL;
169         const char      *PidTagAttr = NULL;
170
171         switch (table->table_type) {
172         case 0x3 /* EMSMDBP_TABLE_FAI_TYPE */:
173                 filter = talloc_asprintf(mem_ctx, "(&(objectClass=faiMessage)(PidTagParentFolderId=%"PRIu64")(PidTagMessageId=", table->folderID);
174                 break;
175         case 0x2 /* EMSMDBP_TABLE_MESSAGE_TYPE */:
176                 filter = talloc_asprintf(mem_ctx, "(&(objectClass=systemMessage)(PidTagParentFolderId=%"PRIu64")(PidTagMessageId=", table->folderID);
177                 break;
178         case 0x1 /* EMSMDBP_TABLE_FOLDER_TYPE */:
179                 filter = talloc_asprintf(mem_ctx, "(&(PidTagParentFolderId=%"PRIu64")(PidTagFolderId=", table->folderID);
180                 break;
181         }
182
183         if (row_fmid == 0) {
184                 filter = talloc_asprintf_append(filter, "*)");
185         }
186         else {
187                 filter = talloc_asprintf_append(filter, "%"PRIu64")", row_fmid);
188         }
189
190         if (restrictions) {
191                 switch (restrictions->rt) {
192                 case RES_PROPERTY:
193                         /* Retrieve PidTagName */
194                         PidTagAttr = openchangedb_property_get_attribute(restrictions->res.resProperty.ulPropTag);
195                         if (!PidTagAttr) {
196                                 talloc_free(filter);
197                                 return NULL;
198                         }
199                         filter = talloc_asprintf_append(filter, "(%s=", PidTagAttr);
200                         switch (restrictions->res.resProperty.ulPropTag & 0xFFFF) {
201                         case PT_STRING8:
202                                 filter = talloc_asprintf_append(filter, "%s)", restrictions->res.resProperty.lpProp.value.lpszA);
203                                 break;
204                         case PT_UNICODE:
205                                 filter = talloc_asprintf_append(filter, "%s)", restrictions->res.resProperty.lpProp.value.lpszW);
206                                 break;
207                         default:
208                                 DEBUG(0, ("Unsupported RES_PROPERTY property type: 0x%.4x\n", (restrictions->res.resProperty.ulPropTag & 0xFFFF)));
209                                 talloc_free(filter);
210                                 return NULL;
211                         }
212                 }
213         }
214
215         /* Close filter */
216         filter = talloc_asprintf_append(filter, ")");
217
218         return filter;
219 }
220
221 _PUBLIC_ enum MAPISTATUS openchangedb_table_get_property(TALLOC_CTX *mem_ctx,
222                                                          void *table_object,
223                                                          struct ldb_context *ldb_ctx,
224                                                          enum MAPITAGS proptag,
225                                                          uint32_t pos,
226                                                          bool live_filtered,
227                                                          void **data)
228 {
229         struct openchangedb_table       *table;
230         char                            *ldb_filter = NULL;
231         struct ldb_result               *res = NULL, *live_res = NULL;
232         const char * const              attrs[] = { "*", NULL };
233         const char                      *PidTagAttr = NULL, *childIdAttr;
234         uint64_t                        *row_fmid;
235         int                             ret;
236
237         /* Sanity checks */
238         OPENCHANGE_RETVAL_IF(!table_object, MAPI_E_NOT_INITIALIZED, NULL);
239         OPENCHANGE_RETVAL_IF(!ldb_ctx, MAPI_E_NOT_INITIALIZED, NULL);
240         OPENCHANGE_RETVAL_IF(!data, MAPI_E_NOT_INITIALIZED, NULL);
241
242         table = (struct openchangedb_table *)table_object;
243
244         /* Fetch results */
245         if (!table->res) {
246                 /* Build ldb filter */
247                 if (live_filtered) {
248                         ldb_filter = openchangedb_table_build_filter(NULL, table, 0, NULL);
249                         DEBUG(0, ("(live-filtered) ldb_filter = %s\n", ldb_filter));
250                 }
251                 else {
252                         ldb_filter = openchangedb_table_build_filter(NULL, table, 0, table->restrictions);
253                         DEBUG(0, ("(pre-filtered) ldb_filter = %s\n", ldb_filter));
254                 }
255                 OPENCHANGE_RETVAL_IF(!ldb_filter, MAPI_E_TOO_COMPLEX, NULL);
256                 ret = ldb_search(ldb_ctx, (TALLOC_CTX *)table_object, &table->res, ldb_get_default_basedn(ldb_ctx), LDB_SCOPE_SUBTREE, attrs, ldb_filter, NULL);
257                 talloc_free(ldb_filter);
258                 OPENCHANGE_RETVAL_IF(ret != LDB_SUCCESS, MAPI_E_INVALID_OBJECT, NULL);
259         }
260         res = table->res;
261
262         /* Ensure position is within search results range */
263         OPENCHANGE_RETVAL_IF(pos >= res->count, MAPI_E_INVALID_OBJECT, NULL);
264
265         /* If live filtering, make sure the specified row match the restrictions */
266         if (live_filtered) {
267                 switch (table->table_type) {
268                 case 0x3 /* EMSMDBP_TABLE_FAI_TYPE */:
269                 case 0x2 /* EMSMDBP_TABLE_MESSAGE_TYPE */:
270                         childIdAttr = "PidTagMessageId";
271                         break;
272                 case 0x1 /* EMSMDBP_TABLE_FOLDER_TYPE */:
273                         childIdAttr = "PidTagFolderId";
274                         break;
275                 default:
276                         DEBUG(0, ("unsupported table type for openchangedb: %d\n", table->table_type));
277                         abort();
278                 }
279                 row_fmid = openchangedb_get_property_data(mem_ctx, res, pos, PR_MID, childIdAttr);
280                 if (!row_fmid || !*row_fmid) {
281                         DEBUG(0, ("ldb object must have a '%s' field\n", childIdAttr));
282                         abort();
283                 }
284                 ldb_filter = openchangedb_table_build_filter(NULL, table, *row_fmid, table->restrictions);
285                 OPENCHANGE_RETVAL_IF(!ldb_filter, MAPI_E_TOO_COMPLEX, NULL);
286                 DEBUG(0, ("  row ldb_filter = %s\n", ldb_filter));
287                 ret = ldb_search(ldb_ctx, NULL, &live_res, ldb_get_default_basedn(ldb_ctx), LDB_SCOPE_SUBTREE, attrs, ldb_filter, NULL);
288                 talloc_free(ldb_filter);
289                 OPENCHANGE_RETVAL_IF(ret != LDB_SUCCESS, MAPI_E_INVALID_OBJECT, NULL);
290                 if (live_res->count == 0) {
291                         talloc_free(live_res);
292                         return MAPI_E_INVALID_OBJECT;
293                 }
294                 talloc_free(live_res);
295         }
296
297         /* hacks for some attributes specific to tables */
298         if (proptag == PR_INST_ID) {
299                 if (table->table_type == 1) {
300                         proptag = PR_FID;
301                 }
302                 else {
303                         proptag = PR_MID;
304                 }
305         }
306         else if (proptag == PR_INSTANCE_NUM) {
307                 *data = talloc_zero(mem_ctx, uint32_t);
308                 return MAPI_E_SUCCESS;
309         }
310
311         /* Convert proptag into PidTag attribute */
312         if ((table->table_type != 0x1) && proptag == PR_FID) {
313                 proptag = PR_PARENT_FID;
314         }
315         PidTagAttr = openchangedb_property_get_attribute(proptag);
316         OPENCHANGE_RETVAL_IF(!PidTagAttr, MAPI_E_NOT_FOUND, NULL);
317
318         /* Ensure the element exists */
319         OPENCHANGE_RETVAL_IF(!ldb_msg_find_element(res->msgs[pos], PidTagAttr), MAPI_E_NOT_FOUND, NULL);
320
321         /* Check if this is a "special property" */
322         *data = openchangedb_get_special_property(mem_ctx, ldb_ctx, res, proptag, PidTagAttr);
323         OPENCHANGE_RETVAL_IF(*data != NULL, MAPI_E_SUCCESS, NULL);
324
325         /* Check if this is NOT a "special property" */
326         *data = openchangedb_get_property_data(mem_ctx, res, pos, proptag, PidTagAttr);
327         OPENCHANGE_RETVAL_IF(*data != NULL, MAPI_E_SUCCESS, NULL);
328
329         return MAPI_E_NOT_FOUND;
330 }