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