]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/spotlight_marshalling.c
Merge remote-tracking branch 'origin/develop' into spotlight
[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 off = 0, len;
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     len = cnid_count + 1;
235     if (cnid_count > 0)
236         len ++;
237
238     EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_CNIDS, len, 8 /* unknown meaning, but always 8 */)) );
239     offset += 8;
240
241     if (cnid_count > 0) {
242         EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(0x0add, cnid_count, cnids->ca_context)) );
243         offset += 8;
244
245         for (int i = 0; i < cnid_count; i++) {
246             memcpy(&id, cnids->ca_cnids->dd_talloc_array[i], sizeof(uint64_t));
247             EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, id) );
248             offset += 8;
249         }
250     }
251     
252 EC_CLEANUP:
253     if (ret != 0)
254         return -1;
255     return offset;
256 }
257
258 static int sl_pack_array(sl_array_t *array, char *buf, int offset, char *toc_buf, int *toc_idx)
259 {
260     EC_INIT;
261     int count = talloc_array_length(array->dd_talloc_array);
262     int octets = (offset + SL_OFFSET_DELTA) / 8;
263
264     EC_ZERO( slvalc(toc_buf, *toc_idx * 8, MAX_SLQ_TOC, sl_pack_tag(SQ_CPX_TYPE_ARRAY, octets, count)) );
265     EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1)) );
266     *toc_idx += 1;
267     offset += 8;
268
269     EC_NEG1( offset = sl_pack_loop(array, buf, offset, toc_buf, toc_idx) );
270
271 EC_CLEANUP:
272     if (ret != 0)
273         return -1;
274     return offset;
275 }
276
277 static int sl_pack_dict(sl_array_t *dict, char *buf, int offset, char *toc_buf, int *toc_idx)
278 {
279     EC_INIT;
280
281     EC_ZERO( slvalc(toc_buf,
282                     *toc_idx * 8,
283                     MAX_SLQ_TOC,
284                     sl_pack_tag(SQ_CPX_TYPE_DICT,
285                                 (offset + SL_OFFSET_DELTA) / 8,
286                                 talloc_array_length(dict->dd_talloc_array))) );
287     EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1)) );
288     *toc_idx += 1;
289     offset += 8;
290
291     EC_NEG1( offset = sl_pack_loop(dict, buf, offset, toc_buf, toc_idx) );
292
293 EC_CLEANUP:
294     if (ret != 0)
295         return -1;
296     return offset;
297 }
298
299 static int sl_pack_filemeta(sl_filemeta_t *fm, char *buf, int offset, char *toc_buf, int *toc_idx)
300 {
301     EC_INIT;
302     int fmlen;                  /* lenght of filemeta */
303     int saveoff = offset;
304
305     EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1)) );
306     offset += 16;
307
308     EC_NEG1( fmlen = sl_pack(fm, buf + offset) );
309
310     /* Check for empty filemeta array, if it's only 40 bytes, it's only the header but no content */
311     LOG(log_debug, logtype_sl, "fmlen: %d", fmlen);
312     if (fmlen > 40)
313         offset += fmlen;
314     else
315         fmlen = 0;
316
317     EC_ZERO( slvalc(buf, saveoff + 8, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_DATA, (fmlen / 8) + 1, 8 /* unknown meaning, but always 8 */)) );
318
319     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)) );
320     *toc_idx += 1;
321
322 EC_CLEANUP:
323     if (ret != 0)
324         return -1;
325     return offset;
326 }
327
328 static int sl_pack_string(char *s, char *buf, int offset, char *toc_buf, int *toc_idx)
329 {
330     EC_INIT;
331     int len, octets, used_in_last_octet;
332
333     len = strlen(s);
334     octets = (len / 8) + (len & 7 ? 1 : 0);
335     used_in_last_octet = 8 - (octets * 8 - len);
336
337     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)) );
338     EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1)) );
339     *toc_idx += 1;
340     offset += 8;
341
342     EC_ZERO( slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_DATA, octets + 1, used_in_last_octet)) );
343     offset += 8;
344
345     if (offset + octets * 8 > MAX_SLQ_DAT)
346         EC_FAIL;
347     memset(buf + offset, 0, octets * 8);
348     strncpy(buf + offset, s, len);
349     offset += octets * 8;
350
351 EC_CLEANUP:
352     if (ret != 0)
353         return -1;
354     return offset;
355 }
356
357 static int sl_pack_loop(DALLOC_CTX *query, char *buf, int offset, char *toc_buf, int *toc_idx)
358 {
359     EC_INIT;
360     const char *type;
361
362     for (int n = 0; n < talloc_array_length(query->dd_talloc_array); n++) {
363
364         type = talloc_get_name(query->dd_talloc_array[n]);
365
366         if (STRCMP(type, ==, "sl_array_t")) {
367             EC_NEG1( offset = sl_pack_array(query->dd_talloc_array[n], buf, offset, toc_buf, toc_idx) );
368         } else if (STRCMP(type, ==, "sl_dict_t")) {
369             EC_NEG1( offset = sl_pack_dict(query->dd_talloc_array[n], buf, offset, toc_buf, toc_idx) );
370         } else if (STRCMP(type, ==, "sl_filemeta_t")) {
371             EC_NEG1( offset = sl_pack_filemeta(query->dd_talloc_array[n], buf, offset, toc_buf, toc_idx) );
372         } else if (STRCMP(type, ==, "uint64_t")) {
373             uint64_t i;
374             memcpy(&i, query->dd_talloc_array[n], sizeof(uint64_t));
375             EC_NEG1( offset = sl_pack_uint64(i, buf, offset) );
376         } else if (STRCMP(type, ==, "char *")) {
377             EC_NEG1( offset = sl_pack_string(query->dd_talloc_array[n], buf, offset, toc_buf, toc_idx) );
378         } else if (STRCMP(type, ==, "sl_bool_t")) {
379             sl_bool_t bl;
380             memcpy(&bl, query->dd_talloc_array[n], sizeof(sl_bool_t));
381             EC_NEG1( offset = sl_pack_bool(bl, buf, offset) );
382         } else if (STRCMP(type, ==, "double")) {
383             double d;
384             memcpy(&d, query->dd_talloc_array[n], sizeof(double));
385             EC_NEG1( offset = sl_pack_float(d, buf, offset) );
386         } else if (STRCMP(type, ==, "sl_nil_t")) {
387             EC_NEG1( offset = sl_pack_nil(buf, offset) );
388         } else if (STRCMP(type, ==, "sl_time_t")) {
389             sl_time_t t;
390             memcpy(&t, query->dd_talloc_array[n], sizeof(sl_time_t));
391             EC_NEG1( offset = sl_pack_date(t, buf, offset) );
392         } else if (STRCMP(type, ==, "sl_uuid_t")) {
393             EC_NEG1( offset = sl_pack_uuid(query->dd_talloc_array[n], buf, offset) );
394         } else if (STRCMP(type, ==, "sl_cnids_t")) {
395             EC_NEG1( offset = sl_pack_CNID(query->dd_talloc_array[n], buf, offset, toc_buf, toc_idx) );
396         }
397     }
398
399 EC_CLEANUP:
400     if (ret != 0)
401         return -1;
402     return offset;
403 }
404
405 /**************************************************************************************************
406  * unmarshalling functions
407  **************************************************************************************************/
408
409 static uint64_t sl_unpack_uint64(const char *buf, int offset, uint encoding)
410 {
411     if (encoding == SL_ENC_LITTLE_ENDIAN)
412             return LVAL(buf, offset);
413         else
414             return RLVAL(buf, offset);
415 }
416
417 static int sl_unpack_ints(DALLOC_CTX *query, const char *buf, int offset, uint encoding)
418 {
419     int count, i;
420     uint64_t query_data64;
421
422     query_data64 = sl_unpack_uint64(buf, offset, encoding);
423     count = query_data64 >> 32;
424     offset += 8;
425
426     i = 0;
427     while (i++ < count) {
428         query_data64 = sl_unpack_uint64(buf, offset, encoding);
429         dalloc_add_copy(query, &query_data64, uint64_t);
430         offset += 8;
431     }
432
433     return count;
434 }
435
436 static int sl_unpack_date(DALLOC_CTX *query, const char *buf, int offset, uint encoding)
437 {
438     int count, i;
439     uint64_t query_data64;
440     sl_time_t t;
441
442     query_data64 = sl_unpack_uint64(buf, offset, encoding);
443     count = query_data64 >> 32;
444     offset += 8;
445
446     i = 0;
447     while (i++ < count) {
448         query_data64 = sl_unpack_uint64(buf, offset, encoding) >> 24;
449         t.tv_sec = query_data64 - SPOTLIGHT_TIME_DELTA;
450         t.tv_usec = 0;
451         dalloc_add_copy(query, &t, sl_time_t);
452         offset += 8;
453     }
454
455     return count;
456 }
457
458 static int sl_unpack_uuid(DALLOC_CTX *query, const char *buf, int offset, uint encoding)
459 {
460     int count, i;
461     uint64_t query_data64;
462     sl_uuid_t uuid;
463     query_data64 = sl_unpack_uint64(buf, offset, encoding);
464     count = query_data64 >> 32;
465     offset += 8;
466
467     i = 0;
468     while (i++ < count) {
469         memcpy(uuid.sl_uuid, buf + offset, 16);
470         dalloc_add_copy(query, &uuid, sl_uuid_t);
471         offset += 16;
472     }
473
474     return count;
475 }
476
477 static int sl_unpack_floats(DALLOC_CTX *query, const char *buf, int offset, uint encoding)
478 {
479     int count, i;
480     uint64_t query_data64;
481     double fval;
482     union {
483         double d;
484         uint32_t w[2];
485     } ieee_fp_union;
486
487     query_data64 = sl_unpack_uint64(buf, offset, encoding);
488     count = query_data64 >> 32;
489     offset += 8;
490
491     i = 0;
492     while (i++ < count) {
493         if (encoding == SL_ENC_LITTLE_ENDIAN) {
494 #ifdef WORDS_BIGENDIAN
495             ieee_fp_union.w[0] = IVAL(buf, offset + 4);
496             ieee_fp_union.w[1] = IVAL(buf, offset);
497 #else
498             ieee_fp_union.w[0] = IVAL(buf, offset);
499             ieee_fp_union.w[1] = IVAL(buf, offset + 4);
500 #endif
501         } else {
502 #ifdef WORDS_BIGENDIAN
503             ieee_fp_union.w[0] = RIVAL(buf, offset);
504             ieee_fp_union.w[1] = RIVAL(buf, offset + 4);
505 #else
506             ieee_fp_union.w[0] = RIVAL(buf, offset + 4);
507             ieee_fp_union.w[1] = RIVAL(buf, offset);
508 #endif
509         }
510         dalloc_add_copy(query, &ieee_fp_union.d, double);
511         offset += 8;
512     }
513
514     return count;
515 }
516
517 static int sl_unpack_CNID(DALLOC_CTX *query, const char *buf, int offset, int length, uint encoding)
518 {
519     EC_INIT;
520     int count;
521     uint64_t query_data64;
522     sl_cnids_t *cnids;
523
524     EC_NULL( cnids = talloc_zero(query, sl_cnids_t) );
525     EC_NULL( cnids->ca_cnids = talloc_zero(cnids, DALLOC_CTX) );
526
527     if (length <= 16)
528         /* that's permitted, it's an empty array */
529         goto EC_CLEANUP;
530     
531     query_data64 = sl_unpack_uint64(buf, offset, encoding);
532     count = query_data64 & 0xffff;
533
534     cnids->ca_unkn1 = (query_data64 & 0xffff0000) >> 16;
535     cnids->ca_context = query_data64 >> 32;
536
537     offset += 8;
538
539     while (count --) {
540         query_data64 = sl_unpack_uint64(buf, offset, encoding);
541         dalloc_add_copy(cnids->ca_cnids, &query_data64, uint64_t);
542         offset += 8;
543     }
544
545     dalloc_add(query, cnids, sl_cnids_t);
546
547 EC_CLEANUP:
548     EC_EXIT;
549 }
550
551 static const char *spotlight_get_qtype_string(uint64_t query_type)
552 {
553     switch (query_type) {
554     case SQ_TYPE_NULL:
555         return "null";
556     case SQ_TYPE_COMPLEX:
557         return "complex";
558     case SQ_TYPE_INT64:
559         return "int64";
560     case SQ_TYPE_BOOL:
561         return "bool";
562     case SQ_TYPE_FLOAT:
563         return "float";
564     case SQ_TYPE_DATA:
565         return "data";
566     case SQ_TYPE_CNIDS:
567         return "CNIDs";
568     default:
569         return "unknown";
570     }
571 }
572
573 static const char *spotlight_get_cpx_qtype_string(uint64_t cpx_query_type)
574 {
575     switch (cpx_query_type) {
576     case SQ_CPX_TYPE_ARRAY:
577         return "array";
578     case SQ_CPX_TYPE_STRING:
579         return "string";
580     case SQ_CPX_TYPE_UTF16_STRING:
581         return "utf-16 string";
582     case SQ_CPX_TYPE_DICT:
583         return "dictionary";
584     case SQ_CPX_TYPE_CNIDS:
585         return "CNIDs";
586     case SQ_CPX_TYPE_FILEMETA:
587         return "FileMeta";
588     default:
589         return "unknown";
590     }
591 }
592
593 static int sl_unpack_cpx(DALLOC_CTX *query,
594                          const char *buf,
595                          const int offset,
596                          uint cpx_query_type,
597                          uint cpx_query_count,
598                          const uint toc_offset,
599                          const uint encoding)
600 {
601     EC_INIT;
602
603     int roffset = offset;
604     uint64_t query_data64;
605     uint unicode_encoding;
606     uint8_t mark_exists;
607     char *p;
608     int qlen, used_in_last_block, slen;
609     sl_array_t *sl_array;
610     sl_dict_t *sl_dict;
611     sl_filemeta_t *sl_fm;
612
613     switch (cpx_query_type) {
614     case SQ_CPX_TYPE_ARRAY:
615         sl_array = talloc_zero(query, sl_array_t);
616         EC_NEG1_LOG( roffset = sl_unpack_loop(sl_array, buf, offset, cpx_query_count, toc_offset, encoding) );
617         dalloc_add(query, sl_array, sl_array_t);
618         break;
619
620     case SQ_CPX_TYPE_DICT:
621         sl_dict = talloc_zero(query, sl_dict_t);
622         EC_NEG1_LOG( roffset = sl_unpack_loop(sl_dict, buf, offset, cpx_query_count, toc_offset, encoding) );
623         dalloc_add(query, sl_dict, sl_dict_t);
624         break;
625
626     case SQ_CPX_TYPE_STRING:
627     case SQ_CPX_TYPE_UTF16_STRING:
628         query_data64 = sl_unpack_uint64(buf, offset, encoding);
629         qlen = (query_data64 & 0xffff) * 8;
630         used_in_last_block = query_data64 >> 32;
631         slen = qlen - 8 + used_in_last_block;
632
633         if (cpx_query_type == SQ_CPX_TYPE_STRING) {
634             p = dalloc_strndup(query, buf + offset + 8, slen);
635         } else {
636             unicode_encoding = spotlight_get_utf16_string_encoding(buf, offset + 8, slen, encoding);
637             mark_exists = (unicode_encoding & SL_ENC_UTF_16);
638             unicode_encoding &= ~SL_ENC_UTF_16;
639             EC_NEG1( convert_string_allocate(CH_UCS2, CH_UTF8, buf + offset + (mark_exists ? 18 : 16), slen, &p) );
640         }
641
642         dalloc_add(query, p, char *);
643         roffset += qlen;
644         break;
645
646     case SQ_CPX_TYPE_FILEMETA:
647         query_data64 = sl_unpack_uint64(buf, offset, encoding);
648         qlen = (query_data64 & 0xffff) * 8;
649         if (qlen <= 8) {
650             EC_FAIL_LOG("SQ_CPX_TYPE_FILEMETA: query_length <= 8: %d", qlen);
651         } else {
652             sl_fm = talloc_zero(query, sl_filemeta_t);
653             EC_NEG1_LOG( sl_unpack(sl_fm, buf + offset + 8) );
654             dalloc_add(query, sl_fm, sl_filemeta_t);
655         }
656         roffset += qlen;
657         break;
658
659     case SQ_CPX_TYPE_CNIDS:
660         query_data64 = sl_unpack_uint64(buf, offset, encoding);
661         qlen = (query_data64 & 0xffff) * 8;
662         EC_NEG1_LOG( sl_unpack_CNID(query, buf, offset + 8, qlen, encoding) );
663         roffset += qlen;
664         break;
665
666     default:
667         EC_FAIL;
668     }
669             
670 EC_CLEANUP:
671     if (ret != 0)
672         roffset = -1;
673     return roffset;
674 }
675
676 static int sl_unpack_loop(DALLOC_CTX *query,
677                           const char *buf,
678                           int offset,
679                           uint count,
680                           const uint toc_offset,
681                           const uint encoding)
682 {
683     EC_INIT;
684     int i, toc_index, query_length;
685     uint subcount;
686     uint64_t query_data64, query_type;
687     uint cpx_query_type, cpx_query_count;
688     sl_nil_t nil;
689     sl_bool_t b;
690
691     while (count > 0 && (offset < toc_offset)) {
692         query_data64 = sl_unpack_uint64(buf, offset, encoding);
693         query_length = (query_data64 & 0xffff) * 8;
694         query_type = (query_data64 & 0xffff0000) >> 16;
695         if (query_length == 0)
696             EC_FAIL;
697
698         switch (query_type) {
699         case SQ_TYPE_COMPLEX:
700             toc_index = (query_data64 >> 32) - 1;
701             query_data64 = sl_unpack_uint64(buf, toc_offset + toc_index * 8, encoding);
702             cpx_query_type = (query_data64 & 0xffff0000) >> 16;
703             cpx_query_count = query_data64 >> 32;
704
705             EC_NEG1_LOG( offset = sl_unpack_cpx(query, buf, offset + 8, cpx_query_type, cpx_query_count, toc_offset, encoding));
706             count--;
707             break;
708         case SQ_TYPE_NULL:
709             subcount = query_data64 >> 32;
710             if (subcount > 64)
711                 EC_FAIL;
712             nil = 0;
713             for (i = 0; i < subcount; i++)
714                 dalloc_add_copy(query, &nil, sl_nil_t);
715             offset += query_length;
716             count -= subcount;
717             break;
718         case SQ_TYPE_BOOL:
719             b = query_data64 >> 32;
720             dalloc_add_copy(query, &b, sl_bool_t);
721             offset += query_length;
722             count--;
723             break;
724         case SQ_TYPE_INT64:
725             EC_NEG1_LOG( subcount = sl_unpack_ints(query, buf, offset, encoding) );
726             offset += query_length;
727             count -= subcount;
728             break;
729         case SQ_TYPE_UUID:
730             EC_NEG1_LOG( subcount = sl_unpack_uuid(query, buf, offset, encoding) );
731             offset += query_length;
732             count -= subcount;
733             break;
734         case SQ_TYPE_FLOAT:
735             EC_NEG1_LOG( subcount = sl_unpack_floats(query, buf, offset, encoding) );
736             offset += query_length;
737             count -= subcount;
738             break;
739         case SQ_TYPE_DATE:
740             EC_NEG1_LOG( subcount = sl_unpack_date(query, buf, offset, encoding) );
741             offset += query_length;
742             count -= subcount;
743             break;
744         default:
745             EC_FAIL;
746         }
747     }
748
749 EC_CLEANUP:
750     if (ret != 0) {
751         offset = -1;
752     }
753     return offset;
754 }
755
756 /**************************************************************************************************
757  * Global functions for packing und unpacking
758  **************************************************************************************************/
759
760 int sl_pack(DALLOC_CTX *query, char *buf)
761 {
762     EC_INIT;
763     char toc_buf[MAX_SLQ_TOC];
764     int toc_index = 0;
765     int len = 0;
766
767     memcpy(buf, "432130dm", 8);
768     EC_NEG1_LOG( len = sl_pack_loop(query, buf + 16, 0, toc_buf + 8, &toc_index) );
769     EC_ZERO( sivalc(buf, 8, MAX_SLQ_DAT, len / 8 + 1 + toc_index + 1) );
770     EC_ZERO( sivalc(buf, 12, MAX_SLQ_DAT, len / 8 + 1) );
771
772     EC_ZERO( slvalc(toc_buf, 0, MAX_SLQ_TOC, sl_pack_tag(SQ_TYPE_TOC, toc_index + 1, 0)) );
773     if ((16 + len + ((toc_index + 1 ) * 8)) >= MAX_SLQ_DAT)
774         EC_FAIL;
775     memcpy(buf + 16 + len, toc_buf, (toc_index + 1 ) * 8);
776     len += 16 + (toc_index + 1 ) * 8;
777
778 EC_CLEANUP:
779     if (ret != 0)
780         len = -1;
781     return len;
782 }
783
784 int sl_unpack(DALLOC_CTX *query, const char *buf)
785 {
786     EC_INIT;
787     int encoding, i, toc_entries;
788     uint64_t toc_offset, tquerylen, toc_entry;
789
790     if (strncmp(buf, "md031234", 8) == 0)
791         encoding = SL_ENC_BIG_ENDIAN;
792     else
793         encoding = SL_ENC_LITTLE_ENDIAN;
794
795     buf += 8;
796
797     toc_offset = ((sl_unpack_uint64(buf, 0, encoding) >> 32) - 1 ) * 8;
798     if (toc_offset < 0 || (toc_offset > 65000)) {
799         EC_FAIL;
800     }
801
802     buf += 8;
803
804     toc_entries = (int)(sl_unpack_uint64(buf, toc_offset, encoding) & 0xffff);
805
806     EC_NEG1( sl_unpack_loop(query, buf, 0, 1, toc_offset + 8, encoding) );
807
808 EC_CLEANUP:
809     EC_EXIT;
810 }