Fix issue #387. 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_zero_array(parser->mem_ctx, smb_ucs2_t, (numbytes/2) + 1);
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_zero_array(parser->mem_ctx, smb_ucs2_t, (length/2) + 1);
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                 /* the object itself is sent too, thus download it as a binary,
373                    not as a meaningless number, which is length of the object here */
374                 if (!pull_binary(parser, &prop->value.bin))
375                         return false;
376                 break;
377         }
378         case PT_ERROR:
379         {
380                 uint32_t num;
381                 if (!pull_uint32_t(parser, &num))
382                         return false;
383                 prop->value.err = num;
384                 break;
385         }
386         case PT_MV_BINARY:
387         {
388                 uint32_t i;
389                 if (!pull_uint32_t(parser, &(prop->value.MVbin.cValues)) ||
390                     parser->idx + prop->value.MVbin.cValues * 4 > parser->data.length)
391                         return false;
392                 prop->value.MVbin.lpbin = talloc_array(parser->mem_ctx, struct Binary_r, prop->value.MVbin.cValues);
393                 for (i = 0; i < prop->value.MVbin.cValues; i++) {
394                         if (!pull_binary(parser, &(prop->value.MVbin.lpbin[i])))
395                                 return false;
396                 }
397                 break;
398         }
399         case PT_MV_SHORT:
400         {
401                 uint32_t i;
402                 if (!pull_uint32_t(parser, &(prop->value.MVi.cValues)) ||
403                     parser->idx + prop->value.MVi.cValues * 2 > parser->data.length)
404                         return false;
405                 prop->value.MVi.lpi = talloc_array(parser->mem_ctx, uint16_t, prop->value.MVi.cValues);
406                 for (i = 0; i < prop->value.MVi.cValues; i++) {
407                         if (!pull_uint16_t(parser, &(prop->value.MVi.lpi[i])))
408                                 return false;
409                 }
410                 break;
411         }
412         case PT_MV_LONG:
413         {
414                 uint32_t i;
415                 if (!pull_uint32_t(parser, &(prop->value.MVl.cValues)) ||
416                     parser->idx + prop->value.MVl.cValues * 4 > parser->data.length)
417                         return false;
418                 prop->value.MVl.lpl = talloc_array(parser->mem_ctx, uint32_t, prop->value.MVl.cValues);
419                 for (i = 0; i < prop->value.MVl.cValues; i++) {
420                         if (!pull_uint32_t(parser, &(prop->value.MVl.lpl[i])))
421                                 return false;
422                 }
423                 break;
424         }
425         case PT_MV_STRING8:
426         {
427                 uint32_t i;
428                 char *str;
429                 if (!pull_uint32_t(parser, &(prop->value.MVszA.cValues)) ||
430                     parser->idx + prop->value.MVszA.cValues * 4 > parser->data.length)
431                         return false;
432                 prop->value.MVszA.lppszA = (const char **) talloc_array(parser->mem_ctx, char *, prop->value.MVszA.cValues);
433                 for (i = 0; i < prop->value.MVszA.cValues; i++) {
434                         str = NULL;
435                         if (!pull_string8(parser, &str))
436                                 return false;
437                         prop->value.MVszA.lppszA[i] = str;
438                 }
439                 break;
440         }
441         case PT_MV_CLSID:
442         {
443                 uint32_t i;
444                 if (!pull_uint32_t(parser, &(prop->value.MVguid.cValues)) ||
445                     parser->idx + prop->value.MVguid.cValues * 16 > parser->data.length)
446                         return false;
447                 prop->value.MVguid.lpguid = talloc_array(parser->mem_ctx, struct FlatUID_r *, prop->value.MVguid.cValues);
448                 for (i = 0; i < prop->value.MVguid.cValues; i++) {
449                         if (!pull_clsid(parser, &(prop->value.MVguid.lpguid[i])))
450                                 return false;
451                 }
452                 break;
453         }
454         case PT_MV_UNICODE:
455         {
456                 uint32_t i;
457                 char *str;
458
459                 if (!pull_uint32_t(parser, &(prop->value.MVszW.cValues)) ||
460                     parser->idx + prop->value.MVszW.cValues * 4 > parser->data.length)
461                         return false;
462                 prop->value.MVszW.lppszW = (const char **)  talloc_array(parser->mem_ctx, char *, prop->value.MVszW.cValues);
463                 for (i = 0; i < prop->value.MVszW.cValues; i++) {
464                         str = NULL;
465                         if (!pull_unicode(parser, &str))
466                                 return false;
467                         prop->value.MVszW.lppszW[i] = str;
468                 }
469                 break;
470         }
471         case PT_MV_SYSTIME:
472         {
473                 uint32_t i;
474                 if (!pull_uint32_t(parser, &(prop->value.MVft.cValues)) ||
475                     parser->idx + prop->value.MVft.cValues * 8 > parser->data.length)
476                         return false;
477                 prop->value.MVft.lpft = talloc_array(parser->mem_ctx, struct FILETIME, prop->value.MVft.cValues);
478                 for (i = 0; i < prop->value.MVft.cValues; i++) {
479                         if (!pull_systime(parser, &(prop->value.MVft.lpft[i])))
480                                 return false;
481                 }
482                 break;
483         }
484         default:
485                 printf("unhandled conversion case in fetch_property_value(): 0x%x\n", prop->ulPropTag);
486                 OPENCHANGE_ASSERT();
487         }
488         return true;
489 }
490
491 static bool pull_named_property(struct fx_parser_context *parser, enum MAPISTATUS *ms)
492 {
493         uint8_t type = 0;
494         if (!pull_guid(parser, &(parser->namedprop.lpguid)))
495                 return false;
496         /* printf("guid       : %s\n", GUID_string(parser->mem_ctx, &(parser->namedprop.lpguid))); */
497         if (!pull_uint8_t(parser, &type))
498                 return false;
499         if (type == 0) {
500                 parser->namedprop.ulKind = MNID_ID;
501                 if (!pull_uint32_t(parser, &(parser->namedprop.kind.lid)))
502                         return false;
503                 /* printf("LID dispid: 0x%08x\n", parser->namedprop.kind.lid); */
504         } else if (type == 1) {
505                 smb_ucs2_t *ucs2_data = NULL;
506                 size_t utf8_len;
507                 parser->namedprop.ulKind = MNID_STRING;
508                 if (!fetch_ucs2_nullterminated(parser, &ucs2_data))
509                         return false;
510                 pull_ucs2_talloc(parser->mem_ctx, (char**)&(parser->namedprop.kind.lpwstr.Name), ucs2_data, &(utf8_len));
511                 parser->namedprop.kind.lpwstr.NameSize = utf8_len;
512                 /* printf("named: %s\n", parser->namedprop.kind.lpwstr.Name); */
513         } else {
514                 printf("unknown named property kind: 0x%02x\n", parser->namedprop.ulKind);
515                 OPENCHANGE_ASSERT();
516         }
517         if (parser->op_namedprop) {
518                 *ms = parser->op_namedprop(parser->lpProp.ulPropTag, parser->namedprop, parser->priv);
519         }
520
521         return true;
522 }
523
524 /**
525   \details set a callback function for marker output
526 */
527 _PUBLIC_ void fxparser_set_marker_callback(struct fx_parser_context *parser, fxparser_marker_callback_t marker_callback)
528 {
529         parser->op_marker = marker_callback;
530 }
531
532 /**
533   \details set a callback function for delete properties output
534 */
535 _PUBLIC_ void fxparser_set_delprop_callback(struct fx_parser_context *parser, fxparser_delprop_callback_t delprop_callback)
536 {
537         parser->op_delprop = delprop_callback;
538 }
539
540 /**
541   \details set a callback function for named properties output
542 */
543 _PUBLIC_ void fxparser_set_namedprop_callback(struct fx_parser_context *parser, fxparser_namedprop_callback_t namedprop_callback)
544 {
545         parser->op_namedprop = namedprop_callback;
546 }
547
548 /**
549   \details set a callback function for property output
550 */
551 _PUBLIC_ void fxparser_set_property_callback(struct fx_parser_context *parser, fxparser_property_callback_t property_callback)
552 {
553         parser->op_property = property_callback;
554 }
555
556 /**
557   \details initialise a fast transfer parser
558 */
559 _PUBLIC_ struct fx_parser_context* fxparser_init(TALLOC_CTX *mem_ctx, void *priv)
560 {
561         struct fx_parser_context *parser = talloc_zero(mem_ctx, struct fx_parser_context);
562
563         parser->mem_ctx = mem_ctx;
564         parser->data = data_blob_talloc_named(parser->mem_ctx, NULL, 0, "fast transfer parser");
565         parser->state = ParserState_Entry;
566         parser->idx = 0;
567         parser->lpProp.ulPropTag = (enum MAPITAGS) 0;
568         parser->lpProp.dwAlignPad = 0;
569         parser->lpProp.value.l = 0;
570         parser->priv = priv;
571
572         return parser;
573 }
574
575 /**
576   \details parse a fast transfer buffer
577 */
578 _PUBLIC_ enum MAPISTATUS fxparser_parse(struct fx_parser_context *parser, DATA_BLOB *fxbuf)
579 {
580         enum MAPISTATUS ms = MAPI_E_SUCCESS;
581
582         data_blob_append(parser->mem_ctx, &(parser->data), fxbuf->data, fxbuf->length);
583         parser->enough_data = true;
584         while(ms == MAPI_E_SUCCESS && (parser->idx < parser->data.length) && parser->enough_data) {
585                 uint32_t idx = parser->idx;
586
587                 switch(parser->state) {
588                         case ParserState_Entry:
589                         {
590                                 if (pull_tag(parser)) {
591                                         /* printf("tag: 0x%08x\n", parser->tag); */
592                                         parser->state = ParserState_HaveTag;
593                                 } else {
594                                         parser->enough_data = false;
595                                         parser->idx = idx;
596                                 }
597                                 break;
598                         }
599                         case ParserState_HaveTag:
600                         {
601                                 switch (parser->tag) {
602                                         case PidTagStartTopFld:
603                                         case PidTagStartSubFld:
604                                         case PidTagEndFolder:
605                                         case PidTagStartMessage:
606                                         case PidTagStartFAIMsg:
607                                         case PidTagEndMessage:
608                                         case PidTagStartRecip:
609                                         case PidTagEndToRecip:
610                                         case PidTagNewAttach:
611                                         case PidTagEndAttach:
612                                         case PidTagStartEmbed:
613                                         case PidTagEndEmbed:
614                                                 if (parser->op_marker) {
615                                                         ms = parser->op_marker(parser->tag, parser->priv);
616                                                 }
617                                                 parser->state = ParserState_Entry;
618                                                 break;
619                                         case PidTagFXDelProp:
620                                         {
621                                                 uint32_t tag;
622                                                 if (pull_uint32_t(parser, &tag)) {
623                                                         if (parser->op_delprop) {
624                                                                 ms = parser->op_delprop(tag, parser->priv);
625                                                         }
626                                                         parser->state = ParserState_Entry;
627                                                 } else {
628                                                         parser->enough_data = false;
629                                                         parser->idx = idx;
630                                                 }
631                                                 break;
632                                         }
633                                         default:
634                                         {
635                                                 /* standard property thing */
636                                                 parser->lpProp.ulPropTag = (enum MAPITAGS) parser->tag;
637                                                 parser->lpProp.dwAlignPad = 0;
638                                                 if ((parser->lpProp.ulPropTag >> 16) & 0x8000) {
639                                                         /* this is a named property */
640                                                         // printf("tag: 0x%08x\n", parser->tag);
641                                                         // TODO: this should probably be a separate parser state
642                                                         // TODO: this needs to return the named property
643                                                         if (pull_named_property(parser, &ms)) {
644                                                                 parser->state = ParserState_HavePropTag;
645                                                         } else {
646                                                                 parser->enough_data = false;
647                                                                 parser->idx = idx;
648                                                         }
649                                                 } else {
650                                                         parser->state = ParserState_HavePropTag;
651                                                 }
652                                         }
653                                 }
654                                 break;
655                         }
656                         case ParserState_HavePropTag:
657                         {
658                                 if (fetch_property_value(parser, &(parser->data), &(parser->lpProp))) {
659                                         // printf("position %i of %zi\n", parser->idx, parser->data.length);
660                                         if (parser->op_property) {
661                                                 ms = parser->op_property(parser->lpProp, parser->priv);
662                                         }
663                                         parser->state = ParserState_Entry;
664                                 } else {
665                                         parser->enough_data = false;
666                                         parser->idx = idx;
667                                 }
668                                 break;
669                         }
670                 }
671         }
672         {
673                 // Remove the part of the buffer that we've used
674                 uint32_t remainder_len = parser->data.length - parser->idx;
675                 DATA_BLOB remainder = data_blob_talloc_named(parser->mem_ctx, &(parser->data.data[parser->idx]), remainder_len, "fast transfer parser");
676                 data_blob_free(&(parser->data));
677                 parser->data = remainder;
678                 parser->idx = 0;
679         }
680
681         return ms;
682 }