]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/spotlight_marshalling.c
Bound checking when marshalling SL query results
[netatalk.git] / etc / afpd / spotlight_marshalling.c
1 /*
2   Copyright (c) 2012 Frank Lahm <franklahm@gmail.com>
3
4   This program is free software; you can redistribute it and/or modify
5   it under the terms of the GNU General Public License as published by
6   the Free Software Foundation; either version 2 of the License, or
7   (at your option) any later version.
8
9   This program is distributed in the hope that it will be useful,
10   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   GNU General Public License for more details.
13 */
14
15 #ifdef HAVE_CONFIG_H
16 #include "config.h"
17 #endif /* HAVE_CONFIG_H */
18
19 #include <string.h>
20 #include <strings.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <errno.h>
24 #include <stdbool.h>
25 #include <inttypes.h>
26
27 #include <atalk/errchk.h>
28 #include <atalk/util.h>
29 #include <atalk/logger.h>
30 #include <atalk/talloc.h>
31 #include <atalk/dalloc.h>
32 #include <atalk/byteorder.h>
33 #include <atalk/netatalk_conf.h>
34 #include <atalk/volume.h>
35 #include <atalk/dsi.h>
36
37 #include "spotlight.h"
38
39 #define MAX_SLQ_DAT (DSI_DATASIZ - 64)
40 #define MAX_SLQ_TOC 8192
41
42 /**************************************************************************************************
43  * RPC data marshalling and unmarshalling
44  **************************************************************************************************/
45
46 /* Spotlight epoch is UNIX epoch minus SPOTLIGHT_TIME_DELTA */
47 #define SPOTLIGHT_TIME_DELTA INT64_C(280878921600U)
48
49 #define SQ_TYPE_NULL    0x0000
50 #define SQ_TYPE_COMPLEX 0x0200
51 #define SQ_TYPE_INT64   0x8400
52 #define SQ_TYPE_BOOL    0x0100
53 #define SQ_TYPE_FLOAT   0x8500
54 #define SQ_TYPE_DATA    0x0700
55 #define SQ_TYPE_CNIDS   0x8700
56 #define SQ_TYPE_UUID    0x0e00
57 #define SQ_TYPE_DATE    0x8600
58 #define SQ_TYPE_TOC     0x8800
59
60 #define SQ_CPX_TYPE_ARRAY           0x0a00
61 #define SQ_CPX_TYPE_STRING          0x0c00
62 #define SQ_CPX_TYPE_UTF16_STRING    0x1c00
63 #define SQ_CPX_TYPE_DICT            0x0d00
64 #define SQ_CPX_TYPE_CNIDS           0x1a00
65 #define SQ_CPX_TYPE_FILEMETA        0x1b00
66
67 #define SUBQ_SAFETY_LIM 20
68
69 /* Forward declarations */
70 static int sl_pack_loop(DALLOC_CTX *query, char *buf, int offset, char *toc_buf, int *toc_idx);
71 static int sl_unpack_loop(DALLOC_CTX *query, const char *buf, int offset, uint count, const uint toc_offset, const uint encoding);
72
73 /**************************************************************************************************
74  * Wrapper functions for the *VAL macros with bound checking
75  **************************************************************************************************/
76
77 static int sivalc(char *buf, off_t off, off_t maxoff, uint32_t val)
78 {
79     if (off + sizeof(val) >= maxoff) {
80         LOG(log_error, logtype_sl, "sivalc: off: %zd, maxoff: %zd", off, maxoff);
81         return -1;
82     }
83     SIVAL(buf, off, val);
84     return 0;
85 }
86
87 static int slvalc(char *buf, off_t off, off_t maxoff, uint64_t val)
88 {
89     if (off + sizeof(val) >= maxoff) {
90         LOG(log_error, logtype_sl, "slvalc: off: %zd, maxoff: %zd", off, maxoff);
91         return -1;
92     }
93     SLVAL(buf, off, val);
94     return 0;
95 }
96
97 /*
98 * Returns the UTF-16 string encoding, by checking the 2-byte byte order mark.
99 * If there is no byte order mark, -1 is returned.
100 */
101 static uint spotlight_get_utf16_string_encoding(const char *buf, int offset, int query_length, uint encoding) {
102     uint utf16_encoding;
103
104     /* check for byte order mark */
105     utf16_encoding = SL_ENC_BIG_ENDIAN;
106     if (query_length >= 2) {
107         uint16_t byte_order_mark;
108         if (encoding == SL_ENC_LITTLE_ENDIAN)
109             byte_order_mark = SVAL(buf, offset);
110         else
111             byte_order_mark = RSVAL(buf, offset);
112
113         if (byte_order_mark == 0xFFFE) {
114             utf16_encoding = SL_ENC_BIG_ENDIAN | SL_ENC_UTF_16;
115         }
116         else if (byte_order_mark == 0xFEFF) {
117             utf16_encoding = SL_ENC_LITTLE_ENDIAN | SL_ENC_UTF_16;
118         }
119     }
120
121     return utf16_encoding;
122 }
123
124 /**************************************************************************************************
125  * marshalling functions
126  **************************************************************************************************/
127
128 #define SL_OFFSET_DELTA 16
129
130 static uint64_t sl_pack_tag(uint16_t type, uint16_t size_or_count, uint32_t val)
131 {
132     uint64_t tag = ((uint64_t)val << 32) | ((uint64_t)type << 16) | size_or_count;
133     return tag;
134 }
135
136 static int sl_pack_float(double d, char *buf, int offset)
137 {
138     EC_INIT;
139
140     union {
141         double d;
142         uint64_t w;
143     } ieee_fp_union;
144
145     EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_FLOAT, 2, 1)) );
146     EC_ZERO( slvalc(buf, offset + 8, MAX_SLQ_DAT, ieee_fp_union.w) );
147
148 EC_CLEANUP:
149     if (ret != 0)
150         return -1;
151     return offset + 2 * sizeof(uint64_t);
152 }
153
154 static int sl_pack_uint64(uint64_t u, char *buf, int offset)
155 {
156     EC_INIT;
157
158     EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_INT64, 2, 1)) );
159     EC_ZERO( slvalc(buf, offset + 8, MAX_SLQ_DAT, u) );
160
161 EC_CLEANUP:
162     if (ret != 0)
163         return -1;
164     return offset + 2 * sizeof(uint64_t);
165 }
166
167 static int sl_pack_bool(sl_bool_t bl, char *buf, int offset)
168 {
169     EC_INIT;
170
171     EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_BOOL, 1, bl ? 1 : 0)) );
172
173 EC_CLEANUP:
174     if (ret != 0)
175         return -1;
176     return offset + sizeof(uint64_t);
177 }
178
179 static int sl_pack_nil(char *buf, int offset)
180 {
181     EC_INIT;
182
183     EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_NULL, 1, 1)) );
184
185 EC_CLEANUP:
186     if (ret != 0)
187         return -1;
188     return offset + sizeof(uint64_t);
189 }
190
191 static int sl_pack_date(sl_time_t t, char *buf, int offset)
192 {
193     EC_INIT;
194     uint64_t data = 0;
195
196     data = (t.tv_sec + SPOTLIGHT_TIME_DELTA) << 24;
197
198     EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_DATE, 2, 1)) );
199     EC_ZERO( slvalc(buf, offset + 8, MAX_SLQ_DAT, data) );
200
201 EC_CLEANUP:
202     if (ret != 0)
203         return -1;
204     return offset + 2 * sizeof(uint64_t);
205 }
206
207 static int sl_pack_uuid(sl_uuid_t *uuid, char *buf, int offset)
208 {
209     EC_INIT;
210
211     EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_UUID, 3, 1)) );
212     if (offset + 8 + 16 >= MAX_SLQ_DAT)
213         EC_FAIL;
214     memcpy(buf + offset + 8, uuid, 16);
215
216 EC_CLEANUP:
217     if (ret != 0)
218         return -1;
219     return offset + sizeof(uint64_t) + 16;
220 }
221
222 static int sl_pack_CNID(sl_cnids_t *cnids, char *buf, int offset, char *toc_buf, int *toc_idx)
223 {
224     EC_INIT;
225     int len = 0, off = 0;
226     int cnid_count = talloc_array_length(cnids->ca_cnids->dd_talloc_array);
227     uint64_t id;
228
229     EC_ZERO( slvalc(toc_buf, *toc_idx * 8, MAX_SLQ_TOC, sl_pack_tag(SQ_CPX_TYPE_CNIDS, (offset + SL_OFFSET_DELTA) / 8, 0)) );
230     EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1)) );
231     *toc_idx += 1;
232     offset += 8;
233
234     EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_CNIDS, 2 + cnid_count, 8 /* unknown meaning, but always 8 */)) );
235     offset += 8;
236
237     if (cnid_count > 0) {
238         EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(0x0add, cnid_count, cnids->ca_context)) );
239         offset += 8;
240
241         for (int i = 0; i < cnid_count; i++) {
242             memcpy(&id, cnids->ca_cnids->dd_talloc_array[i], sizeof(uint64_t));
243             EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, id) );
244             offset += 8;
245         }
246     }
247     
248 EC_CLEANUP:
249     if (ret != 0)
250         return -1;
251     return offset;
252 }
253
254 static int sl_pack_array(sl_array_t *array, char *buf, int offset, char *toc_buf, int *toc_idx)
255 {
256     EC_INIT;
257     int count = talloc_array_length(array->dd_talloc_array);
258     int octets = (offset + SL_OFFSET_DELTA) / 8;
259
260     EC_ZERO( slvalc(toc_buf, *toc_idx * 8, MAX_SLQ_TOC, sl_pack_tag(SQ_CPX_TYPE_ARRAY, octets, count)) );
261     EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1)) );
262     *toc_idx += 1;
263     offset += 8;
264
265     EC_NEG1( offset = sl_pack_loop(array, buf, offset, toc_buf, toc_idx) );
266
267 EC_CLEANUP:
268     if (ret != 0)
269         return -1;
270     return offset;
271 }
272
273 static int sl_pack_dict(sl_array_t *dict, char *buf, int offset, char *toc_buf, int *toc_idx)
274 {
275     EC_INIT;
276
277     EC_ZERO( slvalc(toc_buf,
278                     *toc_idx * 8,
279                     MAX_SLQ_TOC,
280                     sl_pack_tag(SQ_CPX_TYPE_DICT,
281                                 (offset + SL_OFFSET_DELTA) / 8,
282                                 talloc_array_length(dict->dd_talloc_array))) );
283     EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1)) );
284     *toc_idx += 1;
285     offset += 8;
286
287     EC_NEG1( offset = sl_pack_loop(dict, buf, offset, toc_buf, toc_idx) );
288
289 EC_CLEANUP:
290     if (ret != 0)
291         return -1;
292     return offset;
293 }
294
295 static int sl_pack_filemeta(sl_filemeta_t *fm, char *buf, int offset, char *toc_buf, int *toc_idx)
296 {
297     EC_INIT;
298     int fmlen;                  /* lenght of filemeta */
299     int saveoff = offset;
300
301     EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1)) );
302     offset += 16;
303
304     EC_NEG1( fmlen = sl_pack(fm, buf + offset) );
305     offset += fmlen;
306
307     EC_ZERO( slvalc(buf, saveoff + 8, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_DATA, (fmlen / 8) + 1, 8 /* unknown meaning, but always 8 */)) );
308
309     EC_ZERO( slvalc(toc_buf, *toc_idx * 8, MAX_SLQ_TOC, sl_pack_tag(SQ_CPX_TYPE_FILEMETA, (saveoff + SL_OFFSET_DELTA) / 8, fmlen / 8)) );
310     *toc_idx += 1;
311
312 EC_CLEANUP:
313     if (ret != 0)
314         return -1;
315     return offset;
316 }
317
318 static int sl_pack_string(char *s, char *buf, int offset, char *toc_buf, int *toc_idx)
319 {
320     EC_INIT;
321     int len, octets, used_in_last_octet;
322
323     len = strlen(s);
324     octets = (len / 8) + (len & 7 ? 1 : 0);
325     used_in_last_octet = 8 - (octets * 8 - len);
326
327     EC_ZERO( slvalc(toc_buf, *toc_idx * 8, MAX_SLQ_TOC, sl_pack_tag(SQ_CPX_TYPE_STRING, (offset + SL_OFFSET_DELTA) / 8, used_in_last_octet)) );
328     EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1)) );
329     *toc_idx += 1;
330     offset += 8;
331
332     EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_DATA, octets + 1, used_in_last_octet)) );
333     offset += 8;
334
335     if (offset + octets * 8 > MAX_SLQ_DAT)
336         EC_FAIL;
337     memset(buf + offset, 0, octets * 8);
338     strncpy(buf + offset, s, len);
339     offset += octets * 8;
340
341 EC_CLEANUP:
342     if (ret != 0)
343         return -1;
344     return offset;
345 }
346
347 static int sl_pack_loop(DALLOC_CTX *query, char *buf, int offset, char *toc_buf, int *toc_idx)
348 {
349     EC_INIT;
350     const char *type;
351
352     for (int n = 0; n < talloc_array_length(query->dd_talloc_array); n++) {
353
354         type = talloc_get_name(query->dd_talloc_array[n]);
355
356         if (STRCMP(type, ==, "sl_array_t")) {
357             EC_NEG1( offset = sl_pack_array(query->dd_talloc_array[n], buf, offset, toc_buf, toc_idx) );
358         } else if (STRCMP(type, ==, "sl_dict_t")) {
359             EC_NEG1( offset = sl_pack_dict(query->dd_talloc_array[n], buf, offset, toc_buf, toc_idx) );
360         } else if (STRCMP(type, ==, "sl_filemeta_t")) {
361             EC_NEG1( offset = sl_pack_filemeta(query->dd_talloc_array[n], buf, offset, toc_buf, toc_idx) );
362         } else if (STRCMP(type, ==, "uint64_t")) {
363             uint64_t i;
364             memcpy(&i, query->dd_talloc_array[n], sizeof(uint64_t));
365             EC_NEG1( offset = sl_pack_uint64(i, buf, offset) );
366         } else if (STRCMP(type, ==, "char *")) {
367             EC_NEG1( offset = sl_pack_string(query->dd_talloc_array[n], buf, offset, toc_buf, toc_idx) );
368         } else if (STRCMP(type, ==, "sl_bool_t")) {
369             sl_bool_t bl;
370             memcpy(&bl, query->dd_talloc_array[n], sizeof(sl_bool_t));
371             EC_NEG1( offset = sl_pack_bool(bl, buf, offset) );
372         } else if (STRCMP(type, ==, "double")) {
373             double d;
374             memcpy(&d, query->dd_talloc_array[n], sizeof(double));
375             EC_NEG1( offset = sl_pack_float(d, buf, offset) );
376         } else if (STRCMP(type, ==, "sl_nil_t")) {
377             EC_NEG1( offset = sl_pack_nil(buf, offset) );
378         } else if (STRCMP(type, ==, "sl_time_t")) {
379             sl_time_t t;
380             memcpy(&t, query->dd_talloc_array[n], sizeof(sl_time_t));
381             EC_NEG1( offset = sl_pack_date(t, buf, offset) );
382         } else if (STRCMP(type, ==, "sl_uuid_t")) {
383             EC_NEG1( offset = sl_pack_uuid(query->dd_talloc_array[n], buf, offset) );
384         } else if (STRCMP(type, ==, "sl_cnids_t")) {
385             EC_NEG1( offset = sl_pack_CNID(query->dd_talloc_array[n], buf, offset, toc_buf, toc_idx) );
386         }
387     }
388
389 EC_CLEANUP:
390     if (ret != 0)
391         return -1;
392     return offset;
393 }
394
395 /**************************************************************************************************
396  * unmarshalling functions
397  **************************************************************************************************/
398
399 static uint64_t sl_unpack_uint64(const char *buf, int offset, uint encoding)
400 {
401     if (encoding == SL_ENC_LITTLE_ENDIAN)
402             return LVAL(buf, offset);
403         else
404             return RLVAL(buf, offset);
405 }
406
407 static int sl_unpack_ints(DALLOC_CTX *query, const char *buf, int offset, uint encoding)
408 {
409     int count, i;
410     uint64_t query_data64;
411
412     query_data64 = sl_unpack_uint64(buf, offset, encoding);
413     count = query_data64 >> 32;
414     offset += 8;
415
416     i = 0;
417     while (i++ < count) {
418         query_data64 = sl_unpack_uint64(buf, offset, encoding);
419         dalloc_add_copy(query, &query_data64, uint64_t);
420         offset += 8;
421     }
422
423     return count;
424 }
425
426 static int sl_unpack_date(DALLOC_CTX *query, const char *buf, int offset, uint encoding)
427 {
428     int count, i;
429     uint64_t query_data64;
430     sl_time_t t;
431
432     query_data64 = sl_unpack_uint64(buf, offset, encoding);
433     count = query_data64 >> 32;
434     offset += 8;
435
436     i = 0;
437     while (i++ < count) {
438         query_data64 = sl_unpack_uint64(buf, offset, encoding) >> 24;
439         t.tv_sec = query_data64 - SPOTLIGHT_TIME_DELTA;
440         t.tv_usec = 0;
441         dalloc_add_copy(query, &t, sl_time_t);
442         offset += 8;
443     }
444
445     return count;
446 }
447
448 static int sl_unpack_uuid(DALLOC_CTX *query, const char *buf, int offset, uint encoding)
449 {
450     int count, i;
451     uint64_t query_data64;
452     sl_uuid_t uuid;
453     query_data64 = sl_unpack_uint64(buf, offset, encoding);
454     count = query_data64 >> 32;
455     offset += 8;
456
457     i = 0;
458     while (i++ < count) {
459         memcpy(uuid.sl_uuid, buf + offset, 16);
460         dalloc_add_copy(query, &uuid, sl_uuid_t);
461         offset += 16;
462     }
463
464     return count;
465 }
466
467 static int sl_unpack_floats(DALLOC_CTX *query, const char *buf, int offset, uint encoding)
468 {
469     int count, i;
470     uint64_t query_data64;
471     double fval;
472     union {
473         double d;
474         uint32_t w[2];
475     } ieee_fp_union;
476
477     query_data64 = sl_unpack_uint64(buf, offset, encoding);
478     count = query_data64 >> 32;
479     offset += 8;
480
481     i = 0;
482     while (i++ < count) {
483         if (encoding == SL_ENC_LITTLE_ENDIAN) {
484 #ifdef WORDS_BIGENDIAN
485             ieee_fp_union.w[0] = IVAL(buf, offset + 4);
486             ieee_fp_union.w[1] = IVAL(buf, offset);
487 #else
488             ieee_fp_union.w[0] = IVAL(buf, offset);
489             ieee_fp_union.w[1] = IVAL(buf, offset + 4);
490 #endif
491         } else {
492 #ifdef WORDS_BIGENDIAN
493             ieee_fp_union.w[0] = RIVAL(buf, offset);
494             ieee_fp_union.w[1] = RIVAL(buf, offset + 4);
495 #else
496             ieee_fp_union.w[0] = RIVAL(buf, offset + 4);
497             ieee_fp_union.w[1] = RIVAL(buf, offset);
498 #endif
499         }
500         dalloc_add_copy(query, &ieee_fp_union.d, double);
501         offset += 8;
502     }
503
504     return count;
505 }
506
507 static int sl_unpack_CNID(DALLOC_CTX *query, const char *buf, int offset, int length, uint encoding)
508 {
509     EC_INIT;
510     int count;
511     uint64_t query_data64;
512     sl_cnids_t *cnids;
513
514     EC_NULL( cnids = talloc_zero(query, sl_cnids_t) );
515     EC_NULL( cnids->ca_cnids = talloc_zero(cnids, DALLOC_CTX) );
516
517     if (length <= 16)
518         /* that's permitted, it's an empty array */
519         goto EC_CLEANUP;
520     
521     query_data64 = sl_unpack_uint64(buf, offset, encoding);
522     count = query_data64 & 0xffff;
523
524     cnids->ca_unkn1 = (query_data64 & 0xffff0000) >> 16;
525     cnids->ca_context = query_data64 >> 32;
526
527     offset += 8;
528
529     while (count --) {
530         query_data64 = sl_unpack_uint64(buf, offset, encoding);
531         dalloc_add_copy(cnids->ca_cnids, &query_data64, uint64_t);
532         offset += 8;
533     }
534
535     dalloc_add(query, cnids, sl_cnids_t);
536
537 EC_CLEANUP:
538     EC_EXIT;
539 }
540
541 static const char *spotlight_get_qtype_string(uint64_t query_type)
542 {
543     switch (query_type) {
544     case SQ_TYPE_NULL:
545         return "null";
546     case SQ_TYPE_COMPLEX:
547         return "complex";
548     case SQ_TYPE_INT64:
549         return "int64";
550     case SQ_TYPE_BOOL:
551         return "bool";
552     case SQ_TYPE_FLOAT:
553         return "float";
554     case SQ_TYPE_DATA:
555         return "data";
556     case SQ_TYPE_CNIDS:
557         return "CNIDs";
558     default:
559         return "unknown";
560     }
561 }
562
563 static const char *spotlight_get_cpx_qtype_string(uint64_t cpx_query_type)
564 {
565     switch (cpx_query_type) {
566     case SQ_CPX_TYPE_ARRAY:
567         return "array";
568     case SQ_CPX_TYPE_STRING:
569         return "string";
570     case SQ_CPX_TYPE_UTF16_STRING:
571         return "utf-16 string";
572     case SQ_CPX_TYPE_DICT:
573         return "dictionary";
574     case SQ_CPX_TYPE_CNIDS:
575         return "CNIDs";
576     case SQ_CPX_TYPE_FILEMETA:
577         return "FileMeta";
578     default:
579         return "unknown";
580     }
581 }
582
583 static int sl_unpack_cpx(DALLOC_CTX *query,
584                          const char *buf,
585                          const int offset,
586                          uint cpx_query_type,
587                          uint cpx_query_count,
588                          const uint toc_offset,
589                          const uint encoding)
590 {
591     EC_INIT;
592
593     int roffset = offset;
594     uint64_t query_data64;
595     uint unicode_encoding;
596     uint8_t mark_exists;
597     char *p;
598     int qlen, used_in_last_block, slen;
599     sl_array_t *sl_array;
600     sl_dict_t *sl_dict;
601     sl_filemeta_t *sl_fm;
602
603     switch (cpx_query_type) {
604     case SQ_CPX_TYPE_ARRAY:
605         sl_array = talloc_zero(query, sl_array_t);
606         EC_NEG1_LOG( roffset = sl_unpack_loop(sl_array, buf, offset, cpx_query_count, toc_offset, encoding) );
607         dalloc_add(query, sl_array, sl_array_t);
608         break;
609
610     case SQ_CPX_TYPE_DICT:
611         sl_dict = talloc_zero(query, sl_dict_t);
612         EC_NEG1_LOG( roffset = sl_unpack_loop(sl_dict, buf, offset, cpx_query_count, toc_offset, encoding) );
613         dalloc_add(query, sl_dict, sl_dict_t);
614         break;
615
616     case SQ_CPX_TYPE_STRING:
617     case SQ_CPX_TYPE_UTF16_STRING:
618         query_data64 = sl_unpack_uint64(buf, offset, encoding);
619         qlen = (query_data64 & 0xffff) * 8;
620         used_in_last_block = query_data64 >> 32;
621         slen = qlen - 8 + used_in_last_block;
622
623         if (cpx_query_type == SQ_CPX_TYPE_STRING) {
624             p = dalloc_strndup(query, buf + offset + 8, slen);
625         } else {
626             unicode_encoding = spotlight_get_utf16_string_encoding(buf, offset + 8, slen, encoding);
627             mark_exists = (unicode_encoding & SL_ENC_UTF_16);
628             unicode_encoding &= ~SL_ENC_UTF_16;
629             EC_NEG1( convert_string_allocate(CH_UCS2, CH_UTF8, buf + offset + (mark_exists ? 18 : 16), slen, &p) );
630         }
631
632         dalloc_add(query, p, char *);
633         roffset += qlen;
634         break;
635
636     case SQ_CPX_TYPE_FILEMETA:
637         query_data64 = sl_unpack_uint64(buf, offset, encoding);
638         qlen = (query_data64 & 0xffff) * 8;
639         if (qlen <= 8) {
640             EC_FAIL_LOG("SQ_CPX_TYPE_FILEMETA: query_length <= 8: %d", qlen);
641         } else {
642             sl_fm = talloc_zero(query, sl_filemeta_t);
643             EC_NEG1_LOG( sl_unpack(sl_fm, buf + offset + 8) );
644             dalloc_add(query, sl_fm, sl_filemeta_t);
645         }
646         roffset += qlen;
647         break;
648
649     case SQ_CPX_TYPE_CNIDS:
650         query_data64 = sl_unpack_uint64(buf, offset, encoding);
651         qlen = (query_data64 & 0xffff) * 8;
652         EC_NEG1_LOG( sl_unpack_CNID(query, buf, offset + 8, qlen, encoding) );
653         roffset += qlen;
654         break;
655
656     default:
657         EC_FAIL;
658     }
659             
660 EC_CLEANUP:
661     if (ret != 0)
662         roffset = -1;
663     return roffset;
664 }
665
666 static int sl_unpack_loop(DALLOC_CTX *query,
667                           const char *buf,
668                           int offset,
669                           uint count,
670                           const uint toc_offset,
671                           const uint encoding)
672 {
673     EC_INIT;
674     int i, toc_index, query_length;
675     uint subcount;
676     uint64_t query_data64, query_type;
677     uint cpx_query_type, cpx_query_count;
678     sl_nil_t nil;
679     sl_bool_t b;
680
681     while (count > 0 && (offset < toc_offset)) {
682         query_data64 = sl_unpack_uint64(buf, offset, encoding);
683         query_length = (query_data64 & 0xffff) * 8;
684         query_type = (query_data64 & 0xffff0000) >> 16;
685         if (query_length == 0)
686             EC_FAIL;
687
688         switch (query_type) {
689         case SQ_TYPE_COMPLEX:
690             toc_index = (query_data64 >> 32) - 1;
691             query_data64 = sl_unpack_uint64(buf, toc_offset + toc_index * 8, encoding);
692             cpx_query_type = (query_data64 & 0xffff0000) >> 16;
693             cpx_query_count = query_data64 >> 32;
694
695             EC_NEG1_LOG( offset = sl_unpack_cpx(query, buf, offset + 8, cpx_query_type, cpx_query_count, toc_offset, encoding));
696             count--;
697             break;
698         case SQ_TYPE_NULL:
699             subcount = query_data64 >> 32;
700             if (subcount > 64)
701                 EC_FAIL;
702             nil = 0;
703             for (i = 0; i < subcount; i++)
704                 dalloc_add_copy(query, &nil, sl_nil_t);
705             offset += query_length;
706             count -= subcount;
707             break;
708         case SQ_TYPE_BOOL:
709             b = query_data64 >> 32;
710             dalloc_add_copy(query, &b, sl_bool_t);
711             offset += query_length;
712             count--;
713             break;
714         case SQ_TYPE_INT64:
715             EC_NEG1_LOG( subcount = sl_unpack_ints(query, buf, offset, encoding) );
716             offset += query_length;
717             count -= subcount;
718             break;
719         case SQ_TYPE_UUID:
720             EC_NEG1_LOG( subcount = sl_unpack_uuid(query, buf, offset, encoding) );
721             offset += query_length;
722             count -= subcount;
723             break;
724         case SQ_TYPE_FLOAT:
725             EC_NEG1_LOG( subcount = sl_unpack_floats(query, buf, offset, encoding) );
726             offset += query_length;
727             count -= subcount;
728             break;
729         case SQ_TYPE_DATE:
730             EC_NEG1_LOG( subcount = sl_unpack_date(query, buf, offset, encoding) );
731             offset += query_length;
732             count -= subcount;
733             break;
734         default:
735             EC_FAIL;
736         }
737     }
738
739 EC_CLEANUP:
740     if (ret != 0) {
741         offset = -1;
742     }
743     return offset;
744 }
745
746 /**************************************************************************************************
747  * Global functions for packing und unpacking
748  **************************************************************************************************/
749
750 int sl_pack(DALLOC_CTX *query, char *buf)
751 {
752     EC_INIT;
753     char toc_buf[MAX_SLQ_TOC];
754     int toc_index = 0;
755     int len = 0;
756
757     memcpy(buf, "432130dm", 8);
758     EC_NEG1_LOG( len = sl_pack_loop(query, buf + 16, 0, toc_buf + 8, &toc_index) );
759     EC_ZERO( sivalc(buf, 8, MAX_SLQ_DAT, len / 8 + 1 + toc_index + 1) );
760     EC_ZERO( sivalc(buf, 12, MAX_SLQ_DAT, len / 8 + 1) );
761
762     EC_ZERO( slvalc(toc_buf, 0, MAX_SLQ_TOC, sl_pack_tag(SQ_TYPE_TOC, toc_index + 1, 0)) );
763     if ((16 + len + ((toc_index + 1 ) * 8)) >= MAX_SLQ_DAT)
764         EC_FAIL;
765     memcpy(buf + 16 + len, toc_buf, (toc_index + 1 ) * 8);
766     len += 16 + (toc_index + 1 ) * 8;
767
768 EC_CLEANUP:
769     if (ret != 0)
770         len = -1;
771     return len;
772 }
773
774 int sl_unpack(DALLOC_CTX *query, const char *buf)
775 {
776     EC_INIT;
777     int encoding, i, toc_entries;
778     uint64_t toc_offset, tquerylen, toc_entry;
779
780     if (strncmp(buf, "md031234", 8) == 0)
781         encoding = SL_ENC_BIG_ENDIAN;
782     else
783         encoding = SL_ENC_LITTLE_ENDIAN;
784
785     buf += 8;
786
787     toc_offset = ((sl_unpack_uint64(buf, 0, encoding) >> 32) - 1 ) * 8;
788     if (toc_offset < 0 || (toc_offset > 65000)) {
789         EC_FAIL;
790     }
791
792     buf += 8;
793
794     toc_entries = (int)(sl_unpack_uint64(buf, toc_offset, encoding) & 0xffff);
795
796     EC_NEG1( sl_unpack_loop(query, buf, 0, 1, toc_offset + 8, encoding) );
797
798 EC_CLEANUP:
799     EC_EXIT;
800 }