5adc7f30345f7a4c9e6d877b1d1dd986ce10d96f
[jelmer/openchange-proposed.git/.git] / utils / backup / openchangebackup.c
1 /*
2    MAPI Backup application suite
3
4    OpenChange Project
5
6    Copyright (C) Julien Kerihuel 2007
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 "openchangebackup.h"
23 #include <libmapi/defs_private.h>
24
25 /**
26  * Initialize OCB (OpenChange Backup) subsystem
27  * and open a pointer on the LDB database
28  */
29 struct ocb_context *ocb_init(TALLOC_CTX *mem_ctx, const char *dbpath)
30 {
31         struct ocb_context      *ocb_ctx = NULL;
32         char                    *url = NULL;
33         int                     ret;
34         struct event_context *ev;
35
36         /* sanity check */
37         OCB_RETVAL_IF_CODE(!mem_ctx, "invalid memory context", NULL, NULL);
38         OCB_RETVAL_IF_CODE(!dbpath, "dbpath not set", NULL, NULL);
39
40         ocb_ctx = talloc_zero(mem_ctx, struct ocb_context);
41
42         ev = event_context_init(ocb_ctx);
43         if (!ev) goto failed;
44
45         /* init ldb store */
46         ocb_ctx->ldb_ctx = ldb_init((TALLOC_CTX *)ocb_ctx, ev);
47         if (!ocb_ctx->ldb_ctx) goto failed;
48
49         url = talloc_asprintf(mem_ctx, "tdb://%s", dbpath);
50         ret = ldb_connect(ocb_ctx->ldb_ctx, url, 0, NULL);
51         talloc_free(url);
52         if (ret != LDB_SUCCESS) goto failed;
53
54         return ocb_ctx;
55 failed:
56         ocb_release(ocb_ctx);
57         return NULL;
58 }
59
60 /**
61  * Release OCB subsystem
62  */
63 uint32_t ocb_release(struct ocb_context *ocb_ctx)
64 {
65         OCB_RETVAL_IF(!ocb_ctx, "subsystem not initialized\n", NULL);
66         talloc_free(ocb_ctx);
67
68         return 0;
69 }
70
71 /**
72  * init and prepare a record
73  */
74
75 int ocb_record_init(struct ocb_context *ocb_ctx, const char *objclass, const char *dn, 
76                     const char *id, struct mapi_SPropValue_array *props)
77 {
78         TALLOC_CTX              *mem_ctx;
79         struct ldb_context      *ldb_ctx;
80         struct ldb_result       *res;
81         enum ldb_scope          scope = LDB_SCOPE_SUBTREE;
82         struct ldb_dn           *basedn;
83         int                     ret;
84         const char * const      attrs[] = { "*", NULL };
85
86         /* sanity check */
87         OCB_RETVAL_IF(!ocb_ctx, "Subsystem not initialized", NULL);
88         OCB_RETVAL_IF(!dn, "Not a valid DN", NULL);
89         OCB_RETVAL_IF(!id, "Not a valid ID", NULL);
90
91         mem_ctx = (TALLOC_CTX *)ocb_ctx;
92         ldb_ctx = ocb_ctx->ldb_ctx;
93
94         /* Check if the record already exists */
95         ret = ldb_search(ldb_ctx, mem_ctx, &res, ldb_get_default_basedn(ldb_ctx), scope, attrs, dn);
96         OCB_RETVAL_IF(res->msgs, "Record already exists", NULL);
97
98         /* Retrieve the record basedn */
99         basedn = ldb_dn_new(ldb_ctx, ldb_ctx, dn);
100         OCB_RETVAL_IF(!ldb_dn_validate(basedn), "Invalid DN", NULL);
101
102         ocb_ctx->msg = ldb_msg_new(mem_ctx);
103         ocb_ctx->msg->dn = ldb_dn_copy(mem_ctx, basedn);
104
105         /* add records for cn */
106         ldb_msg_add_string(ocb_ctx->msg, "cn", id);
107
108         /* add filters attributes */
109         ldb_msg_add_string(ocb_ctx->msg, "objectClass", objclass);
110
111         talloc_free(basedn);
112
113         return 0;
114 }
115
116
117 /**
118  * Commit the record with all its attributes: single transaction
119  */
120 uint32_t ocb_record_commit(struct ocb_context *ocb_ctx)
121 {
122         int             ret;
123
124         /* sanity checks */
125         OCB_RETVAL_IF(!ocb_ctx, "Subsystem not initialized", NULL);
126         OCB_RETVAL_IF(!ocb_ctx->ldb_ctx, "LDB context not initialized", NULL);
127         OCB_RETVAL_IF(!ocb_ctx->msg, "Message not initialized", NULL);
128
129         ret = ldb_add(ocb_ctx->ldb_ctx, ocb_ctx->msg);
130         if (ret != LDB_SUCCESS) {
131                 DEBUG(3, ("LDB operation failed: %s\n", ldb_errstring(ocb_ctx->ldb_ctx)));
132                 return -1;
133         }
134
135         talloc_free(ocb_ctx->msg);
136
137         return 0;
138 }
139
140
141 /**
142  * Add a property (attr, value) couple to the current record
143  */
144 uint32_t ocb_record_add_property(struct ocb_context *ocb_ctx, 
145                                  struct mapi_SPropValue *lpProp)
146 {
147         TALLOC_CTX      *mem_ctx;
148         int             i;
149         char            *attr;
150         char            *value = NULL;
151         const char      *tag;
152
153         /* sanity checks */
154         OCB_RETVAL_IF(!ocb_ctx, "Subsystem not initialized", NULL);
155         OCB_RETVAL_IF(!ocb_ctx->ldb_ctx, "LDB context not initialized", NULL);
156         OCB_RETVAL_IF(!ocb_ctx->msg, "Message not initialized", NULL);
157
158         mem_ctx = (TALLOC_CTX *)ocb_ctx->msg;
159
160         tag = get_proptag_name(lpProp->ulPropTag);
161         if (tag) {
162                 attr = talloc_asprintf(mem_ctx, "%s", tag);
163         } else {
164                 attr = talloc_asprintf(mem_ctx, "PR-x%.8x", lpProp->ulPropTag);
165         }
166
167         for (i = 0; attr[i]; i++) {
168                 if (attr[i] == '_') attr[i] = '-';
169         }
170         
171         switch (lpProp->ulPropTag & 0xFFFF) {
172         case PT_SHORT:
173                 ldb_msg_add_fmt(ocb_ctx->msg, attr, "%hd", lpProp->value.i);
174                 break;
175         case PT_STRING8:
176                 ldb_msg_add_string(ocb_ctx->msg, attr, lpProp->value.lpszA);
177                 break;
178         case PT_UNICODE:
179                 ldb_msg_add_string(ocb_ctx->msg, attr, lpProp->value.lpszW);
180                 break;
181         case PT_ERROR: /* We shouldn't need to backup error properties */
182                 return 0;
183         case PT_LONG:
184                 ldb_msg_add_fmt(ocb_ctx->msg, attr, "%d", lpProp->value.l);
185                 break;
186         case PT_BOOLEAN:
187                 ldb_msg_add_fmt(ocb_ctx->msg, attr, "%s", 
188                                 ((lpProp->value.b == true) ? "true" : "false"));
189                 break;
190         case PT_I8:
191                 ldb_msg_add_fmt(ocb_ctx->msg, attr, "%16"PRIx64, lpProp->value.d);
192                 break;
193         case PT_SYSTIME:
194                 value = ocb_ldb_timestring(mem_ctx, &lpProp->value.ft);
195                 ldb_msg_add_string(ocb_ctx->msg, attr, value);
196                 break;
197         case 0xFB:
198         case PT_BINARY:
199                 if (lpProp->value.bin.cb) {
200                         value = ldb_base64_encode(mem_ctx, (char *)lpProp->value.bin.lpb,
201                                                   lpProp->value.bin.cb);
202                         ldb_msg_add_string(ocb_ctx->msg, attr, value);
203                 }
204                 break;
205         case PT_MV_LONG:
206                 for (i = 0; i < lpProp->value.MVl.cValues; i++) {
207                         ldb_msg_add_fmt(ocb_ctx->msg, attr, "%d", 
208                                         lpProp->value.MVl.lpl[i]);
209                 }
210                 break;
211         case PT_MV_BINARY:
212                 for (i = 0; i < lpProp->value.MVbin.cValues; i++) {
213                         struct SBinary_short bin;
214
215                         bin = lpProp->value.MVbin.bin[i];
216                         if (bin.cb) {
217                                 value = ldb_base64_encode(mem_ctx, (char *)bin.lpb, bin.cb);
218                                 ldb_msg_add_string(ocb_ctx->msg, attr, value);
219                         }
220                 }
221                 break;
222         case PT_MV_STRING8:
223                 for (i = 0; i < lpProp->value.MVszA.cValues; i++) {
224                         ldb_msg_add_string(ocb_ctx->msg, attr, 
225                                            lpProp->value.MVszA.strings[i].lppszA);
226                 }
227                 break;
228         default:
229                 printf("%s case %d not supported\n", attr, lpProp->ulPropTag & 0xFFFF);
230                 break;
231         }
232
233         talloc_free(attr);
234         return 0;
235 }
236
237 /**
238  * Retrieve UUID from Sbinary_short struct
239  * Generally used to map PR_STORE_KEY to a string
240  * Used for attachments
241  */
242 char *get_record_uuid(TALLOC_CTX *mem_ctx, const struct SBinary_short *bin)
243 {
244         uint32_t        i;
245         char            *lpb;
246
247         OCB_RETVAL_IF_CODE(!bin, "Invalid PR_RECORD_KEY val", NULL, NULL);
248         lpb = talloc_asprintf(mem_ctx, "%.2X", bin->lpb[0]);
249         for (i = 1; i < bin->cb; i++) {
250                 lpb = talloc_asprintf_append(lpb, "%.2X", bin->lpb[i]);
251         }
252         
253         return lpb;
254 }
255
256
257 /**
258  * Extract MAPI object unique ID from PR_SOURCE_KEY Sbinary_short data:
259  * PR_SOURCE_KEY = 22 bytes field
260  * - 16 first bytes = MAPI Store GUID
261  * - 6 last bytes = MAPI object unique ID
262  */
263 char *get_MAPI_uuid(TALLOC_CTX *mem_ctx, const struct SBinary_short *bin)
264 {
265         uint32_t        i;
266         char            *ab;
267
268         OCB_RETVAL_IF_CODE(!bin || bin->cb != 22, "Invalid SBinary", NULL, NULL);
269
270         ab = talloc_asprintf(mem_ctx, "%.2X", bin->lpb[16]);
271         for (i = 17; i < bin->cb; i++) {
272                 ab = talloc_asprintf_append(ab, "%.2X", bin->lpb[i]);
273         }
274
275         return ab;
276 }
277
278
279 /**
280  * Retrieve the store GUID from a given record.
281  * This GUID should be unique for each store and identical for all
282  * objects belonging to this store
283  */
284 char *get_MAPI_store_guid(TALLOC_CTX *mem_ctx, const struct SBinary_short *bin)
285 {
286         int             i;
287         char            *ab;
288
289         OCB_RETVAL_IF_CODE(!bin || bin->cb != 22, "Invalid SBinary", NULL, NULL);
290
291         ab = talloc_asprintf(mem_ctx, "%.2X", bin->lpb[0]);
292         for (i = 1; i < 16; i++) {
293                 ab = talloc_asprintf_append(ab, "%.2X", bin->lpb[i]);
294         }
295         
296         return ab;
297 }
298
299
300 /**
301  * Convert date from MAPI property to ldb format
302  * Easier to manipulate
303  */
304 char *ocb_ldb_timestring(TALLOC_CTX *mem_ctx, struct FILETIME *ft)
305 {
306         NTTIME          time;
307         time_t          t;
308
309         OCB_RETVAL_IF_CODE(!ft, "Invalid FILTIME", NULL, NULL);
310
311         time = ft->dwHighDateTime;
312         time = time << 32;
313         time |= ft->dwLowDateTime;
314
315         t = nt_time_to_unix(time);
316         return ldb_timestring(mem_ctx, t);
317 }