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