Fix issue #374 - Patch from Milan Crha
[jelmer/openchange-proposed.git/.git] / libmapi / fxparser.c
1 /*
2    OpenChange MAPI implementation.
3
4    Copyright (C) Brad Hards <bradh@frogmouth.net> 2010.
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include "libmapi/libmapi.h"
21 #include "libmapi/libmapi_private.h"
22 #include "libmapi/fxparser.h"
23
24 #ifdef ENABLE_ASSERTS
25 #include <assert.h>
26 #define OC_ASSERT(x) assert(x)
27 #else
28 #define OC_ASSERT(x)
29 #endif
30
31 /**
32    \file fxparser.c
33
34    \brief Fast Transfer stream parser
35  */
36
37 static bool pull_uint8_t(struct fx_parser_context *parser, uint8_t *val)
38 {
39         if ((parser->idx) + 1 > parser->data.length) {
40                 *val = 0;
41                 return false;
42         }
43         *val = parser->data.data[parser->idx];
44         (parser->idx)++;
45         return true;
46 }
47
48 static bool pull_uint16_t(struct fx_parser_context *parser, uint16_t *val)
49 {
50         if ((parser->idx) + 2 > parser->data.length) {
51                 *val = 0;
52                 return false;
53         }
54         *val = parser->data.data[parser->idx];
55         (parser->idx)++;
56         *val += parser->data.data[parser->idx] << 8;
57         (parser->idx)++;
58         return true;
59 }
60
61 static bool pull_uint32_t(struct fx_parser_context *parser, uint32_t *val)
62 {
63         if ((parser->idx) + 4 > parser->data.length) {
64                 *val = 0;
65                 return false;
66         }
67         *val = parser->data.data[parser->idx];
68         (parser->idx)++;
69         *val += parser->data.data[parser->idx] << 8;
70         (parser->idx)++;
71         *val += parser->data.data[parser->idx] << 16;
72         (parser->idx)++;
73         *val += parser->data.data[parser->idx] << 24;
74         (parser->idx)++;
75         return true;
76 }
77
78 static bool pull_tag(struct fx_parser_context *parser)
79 {
80         return pull_uint32_t(parser, &(parser->tag));
81 }
82
83 static bool pull_uint8_data(struct fx_parser_context *parser, uint32_t read_len, uint8_t **data_read)
84 {
85         uint32_t i;
86         for (i = 0; i < read_len; i++) {
87                 if (!pull_uint8_t(parser, (uint8_t*)&((*data_read)[i]))) {
88                         return false;
89                 }
90         }
91         return true;
92 }
93
94 static bool pull_int64_t(struct fx_parser_context *parser, int64_t *val)
95 {
96         int64_t tmp;
97         if ((parser->idx) + 8 > parser->data.length) {
98                 *val = 0;
99                 return false;
100         }
101         *val = parser->data.data[parser->idx];
102         (parser->idx)++;
103
104         tmp = parser->data.data[parser->idx];
105         *val += (tmp << 8);
106         (parser->idx)++;
107
108         tmp = parser->data.data[parser->idx];
109         *val += (tmp << 16);
110         (parser->idx)++;
111
112         tmp = parser->data.data[parser->idx];
113         *val += (tmp << 24);
114         (parser->idx)++;
115
116         tmp = parser->data.data[parser->idx];
117         *val += (tmp << 32);
118         (parser->idx)++;
119
120         tmp = parser->data.data[parser->idx];
121         *val += (tmp << 40);
122         (parser->idx)++;
123
124         tmp = parser->data.data[parser->idx];
125         *val += (tmp << 48);
126         (parser->idx)++;
127
128         tmp = parser->data.data[parser->idx];
129         *val += (tmp << 56);
130         (parser->idx)++;
131
132         return true;
133 }
134
135 static bool pull_double(struct fx_parser_context *parser, double *val)
136 {
137         return pull_int64_t(parser, (int64_t *)val);
138 }
139
140 static bool pull_guid(struct fx_parser_context *parser, struct GUID *guid)
141 {
142         int i;
143
144         if ((parser->idx) + 16 > parser->data.length) {
145                 GUID_all_zero(guid);
146                 return false;
147         }
148         if (!pull_uint32_t(parser, &(guid->time_low)))
149                 return false;
150         if (!pull_uint16_t(parser, &(guid->time_mid)))
151                 return false;
152         if (!pull_uint16_t(parser, &(guid->time_hi_and_version)))
153                 return false;
154         if (!pull_uint8_t(parser, &(guid->clock_seq[0])))
155                 return false;
156         if (!pull_uint8_t(parser, &(guid->clock_seq[1])))
157                 return false;
158         for (i = 0; i < 6; ++i) {
159                 if (!pull_uint8_t(parser, &(guid->node[i])))
160                         return false;
161         }
162         return true;
163 }
164
165 static bool pull_systime(struct fx_parser_context *parser, struct FILETIME *ft)
166 {
167         struct FILETIME filetime = {0,0};
168
169         if (parser->idx + 8 > parser->data.length ||
170             !pull_uint32_t(parser, &(filetime.dwLowDateTime)) ||
171             !pull_uint32_t(parser, &(filetime.dwHighDateTime)))
172                 return false;
173
174         *ft = filetime;
175
176         return true;
177 }
178
179 static bool pull_clsid(struct fx_parser_context *parser, struct FlatUID_r **pclsid)
180 {
181         struct FlatUID_r *clsid;
182         int i = 0;
183
184         if (parser->idx + 16 > parser->data.length)
185                 return false;
186
187         clsid = talloc_zero(parser->mem_ctx, struct FlatUID_r);
188         for (i = 0; i < 16; ++i) {
189                 if (!pull_uint8_t(parser, &(clsid->ab[i])))
190                         return false;
191         }
192
193         *pclsid = clsid;
194
195         return true;
196 }
197
198 static bool pull_string8(struct fx_parser_context *parser, char **pstr)
199 {
200         char *str;
201         uint32_t i, length;
202
203         if (!pull_uint32_t(parser, &length) ||
204             parser->idx + length > parser->data.length)
205                 return false;
206
207         str = talloc_array(parser->mem_ctx, char, length + 1);
208         for (i = 0; i < length; i++) {
209                 if (!pull_uint8_t(parser, (uint8_t*)&(str[i]))) {
210                         return false;
211                 }
212         }
213         str[length] = '\0';
214
215         *pstr = str;
216
217         return true;
218 }
219
220 static bool fetch_ucs2_data(struct fx_parser_context *parser, uint32_t numbytes, smb_ucs2_t **data_read)
221 {
222         if ((parser->idx) + numbytes > parser->data.length) {
223                 // printf("insufficient data in fetch_ucs2_data (%i requested, %zi available)\n", numbytes, (parser->data.length - parser->idx));
224                 return false;
225         }
226
227         *data_read = talloc_array(parser->mem_ctx, smb_ucs2_t, numbytes/2);
228         memcpy(*data_read, &(parser->data.data[parser->idx]), numbytes);
229         parser->idx += numbytes;
230         return true;
231 }
232
233 static bool fetch_ucs2_nullterminated(struct fx_parser_context *parser, smb_ucs2_t **data_read)
234 {
235         uint32_t idx_local = parser->idx;
236         bool found = false;
237         while (idx_local < parser->data.length -1) {
238                 smb_ucs2_t val = 0x0000;
239                 val += parser->data.data[idx_local];
240                 idx_local++;
241                 val += parser->data.data[idx_local] << 8;
242                 idx_local++;
243                 if (val == 0x0000) {
244                         found = true;
245                         break;
246                 }
247         }
248         if (!found)
249                 return false;
250         return fetch_ucs2_data(parser, idx_local-(parser->idx), data_read); 
251 }
252
253 static bool pull_unicode(struct fx_parser_context *parser, char **pstr)
254 {
255         smb_ucs2_t *ucs2_data = NULL;
256         char *utf8_data = NULL;
257         size_t utf8_len;
258         uint32_t length;
259
260         if (!pull_uint32_t(parser, &length) ||
261             parser->idx + length > parser->data.length)
262                 return false;
263
264         ucs2_data = talloc_array(parser->mem_ctx, smb_ucs2_t, length/2);
265
266         if (!fetch_ucs2_data(parser, length, &ucs2_data)) {
267                 return false;
268         }
269         pull_ucs2_talloc(parser->mem_ctx, &utf8_data, ucs2_data, &utf8_len);
270
271         *pstr = utf8_data;
272
273         return true;
274 }
275
276 static bool pull_binary(struct fx_parser_context *parser, struct Binary_r *bin)
277 {
278         if (!pull_uint32_t(parser, &(bin->cb)) ||
279             parser->idx + bin->cb > parser->data.length)
280                 return false;
281
282         bin->lpb = talloc_array(parser->mem_ctx, uint8_t, bin->cb + 1);
283
284         return pull_uint8_data(parser, bin->cb, &(bin->lpb));
285 }
286
287 /*
288  pull a property value from the blob, starting at position idx
289 */
290 static bool fetch_property_value(struct fx_parser_context *parser, DATA_BLOB *buf, struct SPropValue *prop)
291 {
292         switch(prop->ulPropTag & 0xFFFF) {
293         case PT_NULL:
294         {
295                 if (!pull_uint32_t(parser, &(prop->value.null)))
296                         return false;
297                 break; 
298         }
299         case PT_SHORT:
300         {
301                 if (!pull_uint16_t(parser, &(prop->value.i)))
302                         return false;
303                 break;
304         }
305         case PT_LONG:
306         {
307                 if (!pull_uint32_t(parser, &(prop->value.l)))
308                         return false;
309                 break;
310         }
311         case PT_DOUBLE:
312         {
313                 if (!pull_double(parser, (double *)&(prop->value.dbl)))
314                         return false;
315                 break;
316         }
317         case PT_BOOLEAN:
318         {
319                 if (parser->idx + 2 > parser->data.length ||
320                     !pull_uint8_t(parser, &(prop->value.b)))
321                         return false;
322
323                 /* special case for fast transfer, 2 bytes instead of one */
324                 (parser->idx)++;
325                 break;
326         }
327         case PT_I8:
328         {
329                 int64_t val;
330                 if (!pull_int64_t(parser, &(val)))
331                         return false;
332                 prop->value.d = val;
333                 break;
334         }
335         case PT_STRING8:
336         {
337                 char *str = NULL;
338                 if (!pull_string8(parser, &str))
339                         return false;
340                 prop->value.lpszA = str;
341                 break;
342         }
343         case PT_UNICODE:
344         {
345                 char *str = NULL;
346                 if (!pull_unicode (parser, &str))
347                         return false;
348                 prop->value.lpszW = str;
349                 break;
350         }
351         case PT_SYSTIME:
352         {
353                 if (!pull_systime(parser, &prop->value.ft))
354                         return false;
355                 break;
356         }
357         case PT_CLSID:
358         {
359                 if (!pull_clsid(parser, &prop->value.lpguid))
360                         return false;
361                 break;
362         }
363         case PT_SVREID:
364         case PT_BINARY:
365         {
366                 if (!pull_binary(parser, &prop->value.bin))
367                         return false;
368                 break;
369         }
370         case PT_OBJECT:
371         {
372                 if (!pull_uint32_t(parser, &(prop->value.object)))
373                         return false;
374                 break;
375         }
376         case PT_ERROR:
377         {
378                 uint32_t num;
379                 if (!pull_uint32_t(parser, &num))
380                         return false;
381                 prop->value.err = num;
382                 break;
383         }
384         case PT_MV_BINARY:
385         {
386                 uint32_t i;
387                 if (!pull_uint32_t(parser, &(prop->value.MVbin.cValues)) ||
388                     parser->idx + prop->value.MVbin.cValues * 4 > parser->data.length)
389                         return false;
390                 prop->value.MVbin.lpbin = talloc_array(parser->mem_ctx, struct Binary_r, prop->value.MVbin.cValues);
391                 for (i = 0; i < prop->value.MVbin.cValues; i++) {
392                         if (!pull_binary(parser, &(prop->value.MVbin.lpbin[i])))
393                                 return false;
394                 }
395                 break;
396         }
397         case PT_MV_SHORT:
398         {
399                 uint32_t i;
400                 if (!pull_uint32_t(parser, &(prop->value.MVi.cValues)) ||
401                     parser->idx + prop->value.MVi.cValues * 2 > parser->data.length)
402                         return false;
403                 prop->value.MVi.lpi = talloc_array(parser->mem_ctx, uint16_t, prop->value.MVi.cValues);
404                 for (i = 0; i < prop->value.MVi.cValues; i++) {
405                         if (!pull_uint16_t(parser, &(prop->value.MVi.lpi[i])))
406                                 return false;
407                 }
408                 break;
409         }
410         case PT_MV_LONG:
411         {
412                 uint32_t i;
413                 if (!pull_uint32_t(parser, &(prop->value.MVl.cValues)) ||
414                     parser->idx + prop->value.MVl.cValues * 4 > parser->data.length)
415                         return false;
416                 prop->value.MVl.lpl = talloc_array(parser->mem_ctx, uint32_t, prop->value.MVl.cValues);
417                 for (i = 0; i < prop->value.MVl.cValues; i++) {
418                         if (!pull_uint32_t(parser, &(prop->value.MVl.lpl[i])))
419                                 return false;
420                 }
421                 break;
422         }
423         case PT_MV_STRING8:
424         {
425                 uint32_t i;
426                 char *str;
427                 if (!pull_uint32_t(parser, &(prop->value.MVszA.cValues)) ||
428                     parser->idx + prop->value.MVszA.cValues * 4 > parser->data.length)
429                         return false;
430                 prop->value.MVszA.lppszA = (const char **) talloc_array(parser->mem_ctx, char *, prop->value.MVszA.cValues);
431                 for (i = 0; i < prop->value.MVszA.cValues; i++) {
432                         str = NULL;
433                         if (!pull_string8(parser, &str))
434                                 return false;
435                         prop->value.MVszA.lppszA[i] = str;
436                 }
437                 break;
438         }
439         case PT_MV_CLSID:
440         {
441                 uint32_t i;
442                 if (!pull_uint32_t(parser, &(prop->value.MVguid.cValues)) ||
443                     parser->idx + prop->value.MVguid.cValues * 16 > parser->data.length)
444                         return false;
445                 prop->value.MVguid.lpguid = talloc_array(parser->mem_ctx, struct FlatUID_r *, prop->value.MVguid.cValues);
446                 for (i = 0; i < prop->value.MVguid.cValues; i++) {
447                         if (!pull_clsid(parser, &(prop->value.MVguid.lpguid[i])))
448                                 return false;
449                 }
450                 break;
451         }
452         case PT_MV_UNICODE:
453         {
454                 uint32_t i;
455                 char *str;
456
457                 if (!pull_uint32_t(parser, &(prop->value.MVszW.cValues)) ||
458                     parser->idx + prop->value.MVszW.cValues * 4 > parser->data.length)
459                         return false;
460                 prop->value.MVszW.lppszW = (const char **)  talloc_array(parser->mem_ctx, char *, prop->value.MVszW.cValues);
461                 for (i = 0; i < prop->value.MVszW.cValues; i++) {
462                         str = NULL;
463                         if (!pull_unicode(parser, &str))
464                                 return false;
465                         prop->value.MVszW.lppszW[i] = str;
466                 }
467                 break;
468         }
469         case PT_MV_SYSTIME:
470         {
471                 uint32_t i;
472                 if (!pull_uint32_t(parser, &(prop->value.MVft.cValues)) ||
473                     parser->idx + prop->value.MVft.cValues * 8 > parser->data.length)
474                         return false;
475                 prop->value.MVft.lpft = talloc_array(parser->mem_ctx, struct FILETIME, prop->value.MVft.cValues);
476                 for (i = 0; i < prop->value.MVft.cValues; i++) {
477                         if (!pull_systime(parser, &(prop->value.MVft.lpft[i])))
478                                 return false;
479                 }
480                 break;
481         }
482         default:
483                 printf("unhandled conversion case in fetch_property_value(): 0x%x\n", (prop->ulPropTag & 0xFFFF));
484                 OPENCHANGE_ASSERT();
485         }
486         return true;
487 }
488
489 static bool pull_named_property(struct fx_parser_context *parser, enum MAPISTATUS *ms)
490 {
491         uint8_t type = 0;
492         if (!pull_guid(parser, &(parser->namedprop.lpguid)))
493                 return false;
494         /* printf("guid       : %s\n", GUID_string(parser->mem_ctx, &(parser->namedprop.lpguid))); */
495         if (!pull_uint8_t(parser, &type))
496                 return false;
497         if (type == 0) {
498                 parser->namedprop.ulKind = MNID_ID;
499                 if (!pull_uint32_t(parser, &(parser->namedprop.kind.lid)))
500                         return false;
501                 /* printf("LID dispid: 0x%08x\n", parser->namedprop.kind.lid); */
502         } else if (type == 1) {
503                 smb_ucs2_t *ucs2_data = NULL;
504                 size_t utf8_len;
505                 parser->namedprop.ulKind = MNID_STRING;
506                 if (!fetch_ucs2_nullterminated(parser, &ucs2_data))
507                         return false;
508                 pull_ucs2_talloc(parser->mem_ctx, (char**)&(parser->namedprop.kind.lpwstr.Name), ucs2_data, &(utf8_len));
509                 parser->namedprop.kind.lpwstr.NameSize = utf8_len;
510                 /* printf("named: %s\n", parser->namedprop.kind.lpwstr.Name); */
511         } else {
512                 printf("unknown named property kind: 0x%02x\n", parser->namedprop.ulKind);
513                 OPENCHANGE_ASSERT();
514         }
515         if (parser->op_namedprop) {
516                 *ms = parser->op_namedprop(parser->lpProp.ulPropTag, parser->namedprop, parser->priv);
517         }
518
519         return true;
520 }
521
522 /**
523   \details set a callback function for marker output
524 */
525 _PUBLIC_ void fxparser_set_marker_callback(struct fx_parser_context *parser, fxparser_marker_callback_t marker_callback)
526 {
527         parser->op_marker = marker_callback;
528 }
529
530 /**
531   \details set a callback function for delete properties output
532 */
533 _PUBLIC_ void fxparser_set_delprop_callback(struct fx_parser_context *parser, fxparser_delprop_callback_t delprop_callback)
534 {
535         parser->op_delprop = delprop_callback;
536 }
537
538 /**
539   \details set a callback function for named properties output
540 */
541 _PUBLIC_ void fxparser_set_namedprop_callback(struct fx_parser_context *parser, fxparser_namedprop_callback_t namedprop_callback)
542 {
543         parser->op_namedprop = namedprop_callback;
544 }
545
546 /**
547   \details set a callback function for property output
548 */
549 _PUBLIC_ void fxparser_set_property_callback(struct fx_parser_context *parser, fxparser_property_callback_t property_callback)
550 {
551         parser->op_property = property_callback;
552 }
553
554 /**
555   \details initialise a fast transfer parser
556 */
557 _PUBLIC_ struct fx_parser_context* fxparser_init(TALLOC_CTX *mem_ctx, void *priv)
558 {
559         struct fx_parser_context *parser = talloc_zero(mem_ctx, struct fx_parser_context);
560
561         parser->mem_ctx = mem_ctx;
562         parser->data = data_blob_talloc_named(parser->mem_ctx, NULL, 0, "fast transfer parser");
563         parser->state = ParserState_Entry;
564         parser->idx = 0;
565         parser->lpProp.ulPropTag = (enum MAPITAGS) 0;
566         parser->lpProp.dwAlignPad = 0;
567         parser->lpProp.value.l = 0;
568         parser->priv = priv;
569
570         return parser;
571 }
572
573 /**
574   \details parse a fast transfer buffer
575 */
576 _PUBLIC_ enum MAPISTATUS fxparser_parse(struct fx_parser_context *parser, DATA_BLOB *fxbuf)
577 {
578         enum MAPISTATUS ms = MAPI_E_SUCCESS;
579
580         data_blob_append(parser->mem_ctx, &(parser->data), fxbuf->data, fxbuf->length);
581         parser->enough_data = true;
582         while(ms == MAPI_E_SUCCESS && (parser->idx < parser->data.length) && parser->enough_data) {
583                 uint32_t idx = parser->idx;
584
585                 switch(parser->state) {
586                         case ParserState_Entry:
587                         {
588                                 if (pull_tag(parser)) {
589                                         /* printf("tag: 0x%08x\n", parser->tag); */
590                                         parser->state = ParserState_HaveTag;
591                                 } else {
592                                         parser->enough_data = false;
593                                         parser->idx = idx;
594                                 }
595                                 break;
596                         }
597                         case ParserState_HaveTag:
598                         {
599                                 switch (parser->tag) {
600                                         case PidTagStartTopFld:
601                                         case PidTagStartSubFld:
602                                         case PidTagEndFolder:
603                                         case PidTagStartMessage:
604                                         case PidTagStartFAIMsg:
605                                         case PidTagEndMessage:
606                                         case PidTagStartRecip:
607                                         case PidTagEndToRecip:
608                                         case PidTagNewAttach:
609                                         case PidTagEndAttach:
610                                         case PidTagStartEmbed:
611                                         case PidTagEndEmbed:
612                                                 if (parser->op_marker) {
613                                                         ms = parser->op_marker(parser->tag, parser->priv);
614                                                 }
615                                                 parser->state = ParserState_Entry;
616                                                 break;
617                                         case PidTagFXDelProp:
618                                         {
619                                                 uint32_t tag;
620                                                 if (pull_uint32_t(parser, &tag)) {
621                                                         if (parser->op_delprop) {
622                                                                 ms = parser->op_delprop(tag, parser->priv);
623                                                         }
624                                                         parser->state = ParserState_Entry;
625                                                 } else {
626                                                         parser->enough_data = false;
627                                                         parser->idx = idx;
628                                                 }
629                                                 break;
630                                         }
631                                         default:
632                                         {
633                                                 /* standard property thing */
634                                                 parser->lpProp.ulPropTag = (enum MAPITAGS) parser->tag;
635                                                 parser->lpProp.dwAlignPad = 0;
636                                                 if ((parser->lpProp.ulPropTag >> 16) & 0x8000) {
637                                                         /* this is a named property */
638                                                         // printf("tag: 0x%08x\n", parser->tag);
639                                                         // TODO: this should probably be a separate parser state
640                                                         // TODO: this needs to return the named property
641                                                         if (pull_named_property(parser, &ms)) {
642                                                                 parser->state = ParserState_HavePropTag;
643                                                         } else {
644                                                                 parser->enough_data = false;
645                                                                 parser->idx = idx;
646                                                         }
647                                                 } else {
648                                                         parser->state = ParserState_HavePropTag;
649                                                 }
650                                         }
651                                 }
652                                 break;
653                         }
654                         case ParserState_HavePropTag:
655                         {
656                                 if (fetch_property_value(parser, &(parser->data), &(parser->lpProp))) {
657                                         // printf("position %i of %zi\n", parser->idx, parser->data.length);
658                                         if (parser->op_property) {
659                                                 ms = parser->op_property(parser->lpProp, parser->priv);
660                                         }
661                                         parser->state = ParserState_Entry;
662                                 } else {
663                                         parser->enough_data = false;
664                                         parser->idx = idx;
665                                 }
666                                 break;
667                         }
668                 }
669         }
670         {
671                 // Remove the part of the buffer that we've used
672                 uint32_t remainder_len = parser->data.length - parser->idx;
673                 DATA_BLOB remainder = data_blob_talloc_named(parser->mem_ctx, &(parser->data.data[parser->idx]), remainder_len, "fast transfer parser");
674                 data_blob_free(&(parser->data));
675                 parser->data = remainder;
676                 parser->idx = 0;
677         }
678
679         return ms;
680 }