Merge in sogo branch changes r3949-3962
[jelmer/openchange.git] / pyopenchange / mapistore / pymapistore.c
1 /*
2    OpenChange MAPI implementation.
3
4    Python interface to mapistore
5
6    Copyright (C) Julien Kerihuel 2010-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 #include <Python.h>
23 #include "pyopenchange/mapistore/pymapistore.h"
24 #include "pyopenchange/pymapi.h"
25
26 #include <param.h>
27 #include <samba/session.h>
28
29 /* static PyTypeObject *SPropValue_Type; */
30
31 extern struct ldb_context *samdb_connect(TALLOC_CTX *, struct tevent_context *, struct loadparm_context *, struct auth_session_info *, int);
32
33 void initmapistore(void);
34
35 static PyMAPIStoreGlobals globals;
36
37 void PyErr_SetMAPIStoreError(uint32_t retval)
38 {
39         PyErr_SetObject(PyExc_RuntimeError,
40                         Py_BuildValue("(i, s)", retval, mapistore_errstr(retval)));
41 }
42
43 PyMAPIStoreGlobals *get_PyMAPIStoreGlobals()
44 {
45         return &globals;
46 }
47
48 static void sam_ldb_init(const char *syspath)
49 {
50         TALLOC_CTX              *mem_ctx;
51         /* char                 *ldb_path; */
52         struct loadparm_context *lp_ctx;
53         struct tevent_context   *ev;
54         int                     ret;
55         struct ldb_result       *res;
56         struct ldb_dn           *tmp_dn = NULL;
57         static const char       *attrs[] = {
58                 "rootDomainNamingContext",
59                 "defaultNamingContext",
60                 NULL
61         };
62
63         /* Sanity checks */
64         if (globals.samdb_ctx) return;
65
66         mem_ctx = talloc_zero(NULL, TALLOC_CTX);
67
68         ev = tevent_context_init(talloc_autofree_context());
69         if (!ev) goto end;
70
71         /* /\* Step 1. Retrieve a LDB context pointer on sam.ldb database *\/ */
72         /* ldb_path = talloc_asprintf(mem_ctx, "%s/sam.ldb", syspath); */
73
74         /* Step 2. Connect to the database */
75         lp_ctx = loadparm_init_global(true);
76         globals.samdb_ctx = samdb_connect(NULL, NULL, lp_ctx, system_session(lp_ctx), 0);
77         if (!globals.samdb_ctx) goto end;
78
79         /* Step 3. Search for rootDSE record */
80         ret = ldb_search(globals.samdb_ctx, mem_ctx, &res, ldb_dn_new(mem_ctx, globals.samdb_ctx, "@ROOTDSE"),
81                          LDB_SCOPE_BASE, attrs, NULL);
82         if (ret != LDB_SUCCESS) goto end;
83         if (res->count != 1) goto end;
84
85         /* Step 4. Set opaque naming */
86         tmp_dn = ldb_msg_find_attr_as_dn(globals.samdb_ctx, globals.samdb_ctx,
87                                          res->msgs[0], "rootDomainNamingContext");
88         ldb_set_opaque(globals.samdb_ctx, "rootDomainNamingContext", tmp_dn);
89         
90         tmp_dn = ldb_msg_find_attr_as_dn(globals.samdb_ctx, globals.samdb_ctx,
91                                          res->msgs[0], "defaultNamingContext");
92         ldb_set_opaque(globals.samdb_ctx, "defaultNamingContext", tmp_dn);
93
94 end:
95         talloc_free(mem_ctx);
96 }
97
98 static void openchange_ldb_init(const char *syspath)
99 {
100         TALLOC_CTX              *mem_ctx;
101         struct ldb_context      *ldb_ctx;
102         char                    *ldb_path;
103         struct tevent_context   *ev;
104         int                     ret;
105         struct ldb_result       *res;
106         struct ldb_dn           *tmp_dn = NULL;
107         static const char       *attrs[] = {
108                 "rootDomainNamingContext",
109                 "defaultNamingContext",
110                 NULL
111         };
112
113         /* Sanity checks */
114         if (globals.ocdb_ctx) return;
115
116         /* ev = tevent_context_init(talloc_autofree_context()); */
117         /* if (!ev) { */
118         /*      return NULL; */
119         /* } */
120         ev = NULL;
121
122         mem_ctx = talloc_zero(NULL, TALLOC_CTX);
123
124         /* Step 1. Retrieve a LDB context pointer on openchange.ldb database */
125         ldb_ctx = ldb_init(mem_ctx, ev);
126         if (!ldb_ctx) {
127                 goto end;
128         }
129
130         /* Step 2. Connect to the database */
131         ldb_path = talloc_asprintf(mem_ctx, "%s/openchange.ldb", syspath);
132         ret = ldb_connect(ldb_ctx, ldb_path, 0, NULL);
133         if (ret != LDB_SUCCESS) {
134                 goto end;
135         }
136
137         /* Step 3. Search for rootDSE record */
138         ret = ldb_search(ldb_ctx, mem_ctx, &res, ldb_dn_new(mem_ctx, ldb_ctx, "@ROOTDSE"),
139                          LDB_SCOPE_BASE, attrs, NULL);
140         if (ret != LDB_SUCCESS || res->count != 1) {
141                 goto end;
142         }
143
144         /* Step 4. Set opaque naming */
145         tmp_dn = ldb_msg_find_attr_as_dn(ldb_ctx, ldb_ctx, 
146                                          res->msgs[0], "rootDomainNamingContext");
147         ldb_set_opaque(ldb_ctx, "rootDomainNamingContext", tmp_dn);
148         
149         tmp_dn = ldb_msg_find_attr_as_dn(ldb_ctx, ldb_ctx,
150                                          res->msgs[0], "defaultNamingContext");
151         ldb_set_opaque(ldb_ctx, "defaultNamingContext", tmp_dn);
152
153         globals.ocdb_ctx = ldb_ctx;
154         (void) talloc_reference(NULL, globals.ocdb_ctx);
155         
156 end:
157         talloc_free(mem_ctx);
158 }
159
160 static PyObject *py_MAPIStore_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
161 {
162         TALLOC_CTX                      *mem_ctx;
163         struct loadparm_context         *lp_ctx;
164         struct mapistore_context        *mstore_ctx;
165         PyMAPIStoreObject               *msobj;
166         char                            *kwnames[] = { "syspath", "path", NULL };
167         const char                      *path = NULL;
168         const char                      *syspath = NULL;
169
170         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|s", kwnames, &syspath, &path)) {
171                 return NULL;
172         }
173
174         /* Initialize ldb context on sam.ldb */
175         sam_ldb_init(syspath);
176         if (globals.samdb_ctx == NULL) {
177                 PyErr_SetString(PyExc_SystemError,
178                                 "error in sam_ldb_init");
179                 return NULL;
180         }
181
182         mem_ctx = talloc_new(NULL);
183         if (mem_ctx == NULL) {
184                 PyErr_NoMemory();
185                 return NULL;
186         }
187
188         /* Initialize ldb context on openchange.ldb */
189         openchange_ldb_init(syspath);
190         if (globals.ocdb_ctx == NULL) {
191                 PyErr_SetString(PyExc_SystemError,
192                                 "error in openchange_ldb_init");
193                 talloc_free(mem_ctx);
194                 return NULL;
195         }
196
197         /* Initialize configuration */
198         lp_ctx = loadparm_init(mem_ctx);
199         lpcfg_load_default(lp_ctx);
200
201         /* Initialize mapistore */
202         mstore_ctx = mapistore_init(mem_ctx, lp_ctx, path);
203         if (mstore_ctx == NULL) {
204                 PyErr_SetString(PyExc_SystemError,
205                                 "error in mapistore_init");
206                 talloc_free(mem_ctx);
207                 return NULL;
208         }
209
210         msobj = PyObject_New(PyMAPIStoreObject, &PyMAPIStore);
211         msobj->mem_ctx = mem_ctx;
212         msobj->mstore_ctx = mstore_ctx;
213
214         return (PyObject *) msobj;
215 }
216
217 static void py_MAPIStore_dealloc(PyObject *_self)
218 {
219         PyMAPIStoreObject *self = (PyMAPIStoreObject *)_self;
220
221         mapistore_release(self->mstore_ctx);
222         talloc_free(self->mem_ctx);
223         PyObject_Del(_self);
224 }
225
226 static PyObject *py_MAPIStore_new_mgmt(PyMAPIStoreObject *self, PyObject *args)
227 {
228         PyMAPIStoreMGMTObject   *obj;
229
230         obj = PyObject_New(PyMAPIStoreMGMTObject, &PyMAPIStoreMGMT);
231         obj->mgmt_ctx = mapistore_mgmt_init(self->mstore_ctx);
232         if (obj->mgmt_ctx == NULL) {
233                 PyErr_SetMAPIStoreError(MAPISTORE_ERR_NOT_INITIALIZED);
234                 return NULL;
235         }
236         obj->mem_ctx = self->mem_ctx;
237         obj->parent = self;
238         Py_INCREF(obj->parent);
239
240         return (PyObject *) obj;
241 }
242
243 static PyObject *py_MAPIStore_add_context(PyMAPIStoreObject *self, PyObject *args)
244 {
245         int                             ret;
246         PyMAPIStoreContextObject        *context;
247         uint32_t                        context_id = 0;
248         const char                      *uri;
249         const char                      *username;
250         void                            *folder_object;
251         uint64_t                        fid = 0;
252
253         if (!PyArg_ParseTuple(args, "ss", &uri, &username)) {
254                 return NULL;
255         }
256
257         /* printf("Add context: %s\n", uri); */
258
259         /* Initialize connection info */
260         ret = mapistore_set_connection_info(self->mstore_ctx, globals.samdb_ctx, globals.ocdb_ctx, username);
261         if (ret != MAPISTORE_SUCCESS) {
262                 PyErr_SetMAPIStoreError(ret);
263                 return NULL;
264         }
265
266         /* Get FID given mapistore_uri and username */
267         ret = openchangedb_get_fid(globals.ocdb_ctx, uri, &fid);
268         if (ret != MAPISTORE_SUCCESS) {
269                 PyErr_SetMAPIStoreError(ret);
270                 return NULL;
271         }
272
273         ret = mapistore_add_context(self->mstore_ctx, username, uri, fid, &context_id, &folder_object);
274         if (ret != MAPISTORE_SUCCESS) {
275                 PyErr_SetMAPIStoreError(ret);
276                 return NULL;
277         }
278
279         context = PyObject_New(PyMAPIStoreContextObject, &PyMAPIStoreContext);
280         context->mem_ctx = self->mem_ctx;
281         context->mstore_ctx = self->mstore_ctx;
282         context->fid = fid;
283         context->folder_object = folder_object;
284         context->context_id = context_id;
285         context->parent = self;
286
287         Py_INCREF(context->parent);
288
289         return (PyObject *) context;
290 }
291
292 /* static PyObject *py_MAPIStore_delete_context(PyMAPIStoreObject *self, PyObject *args) */
293 /* { */
294 /*      PyMAPIStoreContextObject        *context; */
295 /*      int                             ret = MAPISTORE_SUCCESS; */
296
297 /*      if (!PyArg_ParseTuple(args, "O", &context)) { */
298 /*              return NULL; */
299 /*      } */
300
301 /*      /\* mapistore_del_context(context->mstore_ctx, context->context_id); *\/ */
302 /*      /\* Py_XDECREF(context); *\/ */
303
304 /*      return PyInt_FromLong(ret); */
305 /* } */
306
307 /* static PyObject *py_MAPIStore_search_context_by_uri(PyMAPIStoreObject *self, PyObject *args) */
308 /* { */
309 /*      int             ret; */
310 /*      uint32_t        context_id = 0; */
311 /*      const char      *uri; */
312 /*      void            *backend_object; */
313
314 /*      if (!PyArg_ParseTuple(args, "s", &uri)) { */
315 /*              return NULL; */
316 /*      } */
317
318 /*      ret = mapistore_search_context_by_uri(self->mstore_ctx, uri, &context_id, &backend_object); */
319 /*      if (ret != MAPISTORE_SUCCESS) { */
320 /*              return NULL; */
321 /*      } */
322
323 /*      return PyInt_FromLong(context_id); */
324 /* } */
325
326 /* static PyObject *py_MAPIStore_add_context_ref_count(PyMAPIStoreObject *self, PyObject *args) */
327 /* { */
328 /*      uint32_t        context_id = 0; */
329
330 /*      if (!PyArg_ParseTuple(args, "k", &context_id)) { */
331 /*              return NULL; */
332 /*      } */
333
334 /*      return PyInt_FromLong(mapistore_add_context_ref_count(self->mstore_ctx, context_id)); */
335 /* } */
336
337 /* static PyObject *py_MAPIStore_create_folder(PyMAPIStoreObject *self, PyObject *args) */
338 /* { */
339 /*      uint32_t                context_id; */
340 /*      uint64_t                parent_fid; */
341 /*      uint64_t                fid; */
342 /*      PyObject                *mod_mapi; */
343 /*      PyObject                *pySPropValue; */
344 /*      PySPropValueObject      *SPropValue; */
345 /*      struct SRow             aRow; */
346
347 /*      mod_mapi = PyImport_ImportModule("openchange.mapi"); */
348 /*      if (mod_mapi == NULL) { */
349 /*              printf("Can't load module\n"); */
350 /*              return NULL; */
351 /*      } */
352 /*      SPropValue_Type = (PyTypeObject *)PyObject_GetAttrString(mod_mapi, "SPropValue"); */
353 /*      if (SPropValue_Type == NULL) { */
354 /*              return NULL; */
355 /*      } */
356
357 /*      if (!PyArg_ParseTuple(args, "kKKO", &context_id, &parent_fid, &fid, &pySPropValue)) { */
358 /*              return NULL; */
359 /*      } */
360
361 /*      if (!PyObject_TypeCheck(pySPropValue, SPropValue_Type)) { */
362 /*              PyErr_SetString(PyExc_TypeError, "Function require SPropValue object"); */
363 /*              return NULL; */
364 /*      } */
365
366 /*      SPropValue = (PySPropValueObject *)pySPropValue; */
367 /*      aRow.cValues = SPropValue->cValues; */
368 /*      aRow.lpProps = SPropValue->SPropValue; */
369
370 /*      return PyInt_FromLong(mapistore_folder_create_folder(self->mstore_ctx, context_id, parent_fid, fid, &aRow)); */
371 /* } */
372
373 /* static PyObject *py_MAPIStore_delete_folder(PyMAPIStoreObject *self, PyObject *args) */
374 /* { */
375 /*      uint32_t        context_id; */
376 /*      uint64_t        parent_fid; */
377 /*      uint64_t        fid; */
378 /*      uint8_t         flags; */
379
380 /*      if (!PyArg_ParseTuple(args, "kKKH", &context_id, &parent_fid, &fid, &flags)) { */
381 /*              return NULL; */
382 /*      } */
383
384 /*      return PyInt_FromLong(mapistore_folder_delete_folder(self->mstore_ctx, context_id, parent_fid, fid, flags)); */
385 /* } */
386
387 /* static PyObject *py_MAPIStore_setprops(PyMAPIStoreObject *self, PyObject *args) */
388 /* { */
389 /*      uint32_t                context_id; */
390 /*      uint64_t                fid; */
391 /*      uint8_t                 object_type; */
392 /*      PyObject                *mod_mapi; */
393 /*      PyObject                *pySPropValue; */
394 /*      PySPropValueObject      *SPropValue; */
395 /*      struct SRow             aRow; */
396
397 /*      mod_mapi = PyImport_ImportModule("openchange.mapi"); */
398 /*      if (mod_mapi == NULL) { */
399 /*              printf("Can't load module\n"); */
400 /*              return NULL; */
401 /*      } */
402 /*      SPropValue_Type = (PyTypeObject *)PyObject_GetAttrString(mod_mapi, "SPropValue"); */
403 /*      if (SPropValue_Type == NULL) { */
404 /*              return NULL; */
405 /*      } */
406
407 /*      if (!PyArg_ParseTuple(args, "kKbO", &context_id, &fid, &object_type, &pySPropValue)) { */
408 /*              return NULL; */
409 /*      } */
410
411 /*      if (!PyObject_TypeCheck(pySPropValue, SPropValue_Type)) { */
412 /*              PyErr_SetString(PyExc_TypeError, "Function require SPropValue object"); */
413 /*              return NULL; */
414 /*      } */
415
416 /*      SPropValue = (PySPropValueObject *)pySPropValue; */
417 /*      aRow.cValues = SPropValue->cValues; */
418 /*      aRow.lpProps = SPropValue->SPropValue; */
419
420 /*      return PyInt_FromLong(mapistore_setprops(self->mstore_ctx, context_id, fid, object_type, &aRow)); */
421 /* } */
422
423 /* static PyObject *py_MAPIStore_get_folder_count(PyMAPIStoreObject *self, PyObject *args) */
424 /* { */
425 /*      uint32_t                context_id; */
426 /*      uint64_t                fid; */
427 /*      uint8_t                 object_type; */
428 /*      uint32_t                RowCount = 0; */
429
430 /*      if (!PyArg_ParseTuple(args, "kKb", &context_id, &fid, &object_type)) { */
431 /*              return NULL; */
432 /*      } */
433
434 /*      switch (object_type) { */
435 /*      case MAPISTORE_FOLDER: */
436 /*              mapistore_folder_get_folder_count(self->mstore_ctx, context_id,  */
437 /*                                                fid, &RowCount); */
438 /*              break; */
439 /*      case MAPISTORE_MESSAGE: */
440 /*              mapistore_folder_get_message_count(self->mstore_ctx, context_id,  */
441 /*                                                 fid, MAPISTORE_MESSAGE_TABLE, &RowCount); */
442 /*              break; */
443 /*      default: */
444 /*              RowCount = 0; */
445 /*              break; */
446 /*      } */
447
448 /*      return PyInt_FromLong(RowCount); */
449 /* } */
450
451 static PyMethodDef mapistore_methods[] = {
452         { "management", (PyCFunction)py_MAPIStore_new_mgmt, METH_VARARGS },
453         { "add_context", (PyCFunction)py_MAPIStore_add_context, METH_VARARGS },
454         /* { "delete_context", (PyCFunction)py_MAPIStore_delete_context, METH_VARARGS }, */
455         /* { "search_context_by_uri", (PyCFunction)py_MAPIStore_search_context_by_uri, METH_VARARGS }, */
456         /* { "add_context_ref_count", (PyCFunction)py_MAPIStore_add_context_ref_count, METH_VARARGS }, */
457         /* { "create_folder", (PyCFunction)py_MAPIStore_create_folder, METH_VARARGS }, */
458         /* { "delete_folder", (PyCFunction)py_MAPIStore_delete_folder, METH_VARARGS }, */
459         /* { "setprops", (PyCFunction)py_MAPIStore_setprops, METH_VARARGS }, */
460         /* { "get_folder_count", (PyCFunction)py_MAPIStore_get_folder_count, METH_VARARGS }, */
461         { NULL },
462 };
463
464 PyTypeObject PyMAPIStore = {
465         PyObject_HEAD_INIT(NULL) 0,
466         .tp_name = "mapistore.MAPIStore",
467         .tp_basicsize = sizeof (PyMAPIStoreObject),
468         .tp_doc = "mapistore object",
469         .tp_methods = mapistore_methods,
470         /* .tp_getset = mapistore_getsetters, */
471         .tp_new = py_MAPIStore_new,
472         .tp_dealloc = (destructor)py_MAPIStore_dealloc, 
473         .tp_flags = Py_TPFLAGS_DEFAULT,
474 };
475
476 static PyObject *py_mapistore_set_mapping_path(PyObject *mod, PyObject *args)
477 {
478         const char      *mapping_path;
479         
480         if (!PyArg_ParseTuple(args, "s", &mapping_path)) {
481                 return NULL;
482         }
483
484         return PyInt_FromLong(mapistore_set_mapping_path(mapping_path));
485 }
486
487 static PyObject *py_mapistore_errstr(PyObject *mod, PyObject *args)
488 {
489         int             ret;
490
491         if (!PyArg_ParseTuple(args, "k", &ret)) {
492                 return NULL;
493         }
494
495         return PyString_FromString(mapistore_errstr(ret));
496 }
497
498 static PyMethodDef py_mapistore_global_methods[] = {
499         { "set_mapping_path", (PyCFunction)py_mapistore_set_mapping_path, METH_VARARGS },
500         { "errstr", (PyCFunction)py_mapistore_errstr, METH_VARARGS },
501         { NULL },
502 };
503
504 static void load_modules(void)
505 {
506         PyObject *datetime_dict;
507
508         globals.datetime_module = PyImport_ImportModule("datetime");
509         Py_INCREF(globals.datetime_module);
510         if (globals.datetime_module) {
511                 datetime_dict = PyModule_GetDict(globals.datetime_module);
512                 globals.datetime_datetime_class = PyDict_GetItemString(datetime_dict, "datetime");
513                 if (PyType_Check(globals.datetime_datetime_class)) {
514                         Py_INCREF(globals.datetime_datetime_class);
515                 }
516                 else {
517                         fprintf (stderr, "failure loading datetime.datetime class\n");
518                 }
519         }
520         else {
521                 fprintf (stderr, "failure loading datetime module\n");
522         }
523 }
524
525 void initmapistore(void)
526 {
527         PyObject        *m;
528
529         memset(&globals, 0, sizeof(PyMAPIStoreGlobals));
530
531         load_modules();
532
533         m = Py_InitModule3("mapistore", py_mapistore_global_methods,
534                            "An interface to OpenChange MAPIStore");
535         if (m == NULL) {
536                 return;
537         }
538
539         if (PyType_Ready(&PyMAPIStore) < 0) {
540                 return;
541         }
542         Py_INCREF(&PyMAPIStore);
543         PyModule_AddObject(m, "MAPIStore", (PyObject *)&PyMAPIStore);
544
545         initmapistore_mgmt(m);
546         initmapistore_context(m);
547         initmapistore_folder(m);
548         initmapistore_freebusy_properties(m);
549         initmapistore_errors(m);
550         initmapistore_table(m);
551 }