]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/spotlight_marshalling.c
69733f4943676c58ec4baf7e1d20105a17d97503
[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 off = 0, 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     double fval;
476     union {
477         double d;
478         uint32_t w[2];
479     } ieee_fp_union;
480
481     query_data64 = sl_unpack_uint64(buf, offset, encoding);
482     count = query_data64 >> 32;
483     offset += 8;
484
485     i = 0;
486     while (i++ < count) {
487         if (encoding == SL_ENC_LITTLE_ENDIAN) {
488 #ifdef WORDS_BIGENDIAN
489             ieee_fp_union.w[0] = IVAL(buf, offset + 4);
490             ieee_fp_union.w[1] = IVAL(buf, offset);
491 #else
492             ieee_fp_union.w[0] = IVAL(buf, offset);
493             ieee_fp_union.w[1] = IVAL(buf, offset + 4);
494 #endif
495         } else {
496 #ifdef WORDS_BIGENDIAN
497             ieee_fp_union.w[0] = RIVAL(buf, offset);
498             ieee_fp_union.w[1] = RIVAL(buf, offset + 4);
499 #else
500             ieee_fp_union.w[0] = RIVAL(buf, offset + 4);
501             ieee_fp_union.w[1] = RIVAL(buf, offset);
502 #endif
503         }
504         dalloc_add_copy(query, &ieee_fp_union.d, double);
505         offset += 8;
506     }
507
508     return count;
509 }
510
511 static int sl_unpack_CNID(DALLOC_CTX *query, const char *buf, int offset, int length, uint encoding)
512 {
513     EC_INIT;
514     int count;
515     uint64_t query_data64;
516     sl_cnids_t *cnids;
517
518     EC_NULL( cnids = talloc_zero(query, sl_cnids_t) );
519     EC_NULL( cnids->ca_cnids = talloc_zero(cnids, DALLOC_CTX) );
520
521     if (length <= 16)
522         /* that's permitted, it's an empty array */
523         goto EC_CLEANUP;
524     
525     query_data64 = sl_unpack_uint64(buf, offset, encoding);
526     count = query_data64 & 0xffff;
527
528     cnids->ca_unkn1 = (query_data64 & 0xffff0000) >> 16;
529     cnids->ca_context = query_data64 >> 32;
530
531     offset += 8;
532
533     while (count --) {
534         query_data64 = sl_unpack_uint64(buf, offset, encoding);
535         dalloc_add_copy(cnids->ca_cnids, &query_data64, uint64_t);
536         offset += 8;
537     }
538
539     dalloc_add(query, cnids, sl_cnids_t);
540
541 EC_CLEANUP:
542     EC_EXIT;
543 }
544
545 static const char *spotlight_get_qtype_string(uint64_t query_type)
546 {
547     switch (query_type) {
548     case SQ_TYPE_NULL:
549         return "null";
550     case SQ_TYPE_COMPLEX:
551         return "complex";
552     case SQ_TYPE_INT64:
553         return "int64";
554     case SQ_TYPE_BOOL:
555         return "bool";
556     case SQ_TYPE_FLOAT:
557         return "float";
558     case SQ_TYPE_DATA:
559         return "data";
560     case SQ_TYPE_CNIDS:
561         return "CNIDs";
562     default:
563         return "unknown";
564     }
565 }
566
567 static const char *spotlight_get_cpx_qtype_string(uint64_t cpx_query_type)
568 {
569     switch (cpx_query_type) {
570     case SQ_CPX_TYPE_ARRAY:
571         return "array";
572     case SQ_CPX_TYPE_STRING:
573         return "string";
574     case SQ_CPX_TYPE_UTF16_STRING:
575         return "utf-16 string";
576     case SQ_CPX_TYPE_DICT:
577         return "dictionary";
578     case SQ_CPX_TYPE_CNIDS:
579         return "CNIDs";
580     case SQ_CPX_TYPE_FILEMETA:
581         return "FileMeta";
582     default:
583         return "unknown";
584     }
585 }
586
587 static int sl_unpack_cpx(DALLOC_CTX *query,
588                          const char *buf,
589                          const int offset,
590                          uint cpx_query_type,
591                          uint cpx_query_count,
592                          const uint toc_offset,
593                          const uint encoding)
594 {
595     EC_INIT;
596
597     int roffset = offset;
598     uint64_t query_data64;
599     uint unicode_encoding;
600     uint8_t mark_exists;
601     char *p, *tmp;
602     int qlen, used_in_last_block, slen;
603     sl_array_t *sl_array;
604     sl_dict_t *sl_dict;
605     sl_filemeta_t *sl_fm;
606
607     switch (cpx_query_type) {
608     case SQ_CPX_TYPE_ARRAY:
609         sl_array = talloc_zero(query, sl_array_t);
610         EC_NEG1_LOG( roffset = sl_unpack_loop(sl_array, buf, offset, cpx_query_count, toc_offset, encoding) );
611         dalloc_add(query, sl_array, sl_array_t);
612         break;
613
614     case SQ_CPX_TYPE_DICT:
615         sl_dict = talloc_zero(query, sl_dict_t);
616         EC_NEG1_LOG( roffset = sl_unpack_loop(sl_dict, buf, offset, cpx_query_count, toc_offset, encoding) );
617         dalloc_add(query, sl_dict, sl_dict_t);
618         break;
619
620     case SQ_CPX_TYPE_STRING:
621     case SQ_CPX_TYPE_UTF16_STRING:
622         query_data64 = sl_unpack_uint64(buf, offset, encoding);
623         qlen = (query_data64 & 0xffff) * 8;
624         used_in_last_block = query_data64 >> 32;
625         slen = qlen - 16 + used_in_last_block;
626
627         if (cpx_query_type == SQ_CPX_TYPE_STRING) {
628             p = dalloc_strndup(query, buf + offset + 8, slen);
629         } else {
630             unicode_encoding = spotlight_get_utf16_string_encoding(buf, offset + 8, slen, encoding);
631             mark_exists = (unicode_encoding & SL_ENC_UTF_16);
632             if (unicode_encoding & SL_ENC_BIG_ENDIAN)
633                 EC_FAIL_LOG("Unsupported big endian UTF16 string");
634             slen -= mark_exists ? 2 : 0;
635             EC_NEG1( convert_string_allocate(CH_UCS2,
636                                              CH_UTF8,
637                                              buf + offset + (mark_exists ? 10 : 8),
638                                              slen,
639                                              &tmp) );
640             p = dalloc_strndup(query, tmp, strlen(tmp));
641             free(tmp);
642         }
643
644         dalloc_add(query, p, char *);
645         roffset += qlen;
646         break;
647
648     case SQ_CPX_TYPE_FILEMETA:
649         query_data64 = sl_unpack_uint64(buf, offset, encoding);
650         qlen = (query_data64 & 0xffff) * 8;
651         if (qlen <= 8) {
652             EC_FAIL_LOG("SQ_CPX_TYPE_FILEMETA: query_length <= 8: %d", qlen);
653         } else {
654             sl_fm = talloc_zero(query, sl_filemeta_t);
655             EC_NEG1_LOG( sl_unpack(sl_fm, buf + offset + 8) );
656             dalloc_add(query, sl_fm, sl_filemeta_t);
657         }
658         roffset += qlen;
659         break;
660
661     case SQ_CPX_TYPE_CNIDS:
662         query_data64 = sl_unpack_uint64(buf, offset, encoding);
663         qlen = (query_data64 & 0xffff) * 8;
664         EC_NEG1_LOG( sl_unpack_CNID(query, buf, offset + 8, qlen, encoding) );
665         roffset += qlen;
666         break;
667
668     default:
669         EC_FAIL;
670     }
671             
672 EC_CLEANUP:
673     if (ret != 0)
674         roffset = -1;
675     return roffset;
676 }
677
678 static int sl_unpack_loop(DALLOC_CTX *query,
679                           const char *buf,
680                           int offset,
681                           uint count,
682                           const uint toc_offset,
683                           const uint encoding)
684 {
685     EC_INIT;
686     int i, toc_index, query_length;
687     uint subcount;
688     uint64_t query_data64, query_type;
689     uint cpx_query_type, cpx_query_count;
690     sl_nil_t nil;
691     sl_bool_t b;
692
693     while (count > 0 && (offset < toc_offset)) {
694         query_data64 = sl_unpack_uint64(buf, offset, encoding);
695         query_length = (query_data64 & 0xffff) * 8;
696         query_type = (query_data64 & 0xffff0000) >> 16;
697         if (query_length == 0)
698             EC_FAIL;
699
700         switch (query_type) {
701         case SQ_TYPE_COMPLEX:
702             toc_index = (query_data64 >> 32) - 1;
703             query_data64 = sl_unpack_uint64(buf, toc_offset + toc_index * 8, encoding);
704             cpx_query_type = (query_data64 & 0xffff0000) >> 16;
705             cpx_query_count = query_data64 >> 32;
706
707             EC_NEG1_LOG( offset = sl_unpack_cpx(query, buf, offset + 8, cpx_query_type, cpx_query_count, toc_offset, encoding));
708             count--;
709             break;
710         case SQ_TYPE_NULL:
711             subcount = query_data64 >> 32;
712             if (subcount > 64)
713                 EC_FAIL;
714             nil = 0;
715             for (i = 0; i < subcount; i++)
716                 dalloc_add_copy(query, &nil, sl_nil_t);
717             offset += query_length;
718             count -= subcount;
719             break;
720         case SQ_TYPE_BOOL:
721             b = query_data64 >> 32;
722             dalloc_add_copy(query, &b, sl_bool_t);
723             offset += query_length;
724             count--;
725             break;
726         case SQ_TYPE_INT64:
727             EC_NEG1_LOG( subcount = sl_unpack_ints(query, buf, offset, encoding) );
728             offset += query_length;
729             count -= subcount;
730             break;
731         case SQ_TYPE_UUID:
732             EC_NEG1_LOG( subcount = sl_unpack_uuid(query, buf, offset, encoding) );
733             offset += query_length;
734             count -= subcount;
735             break;
736         case SQ_TYPE_FLOAT:
737             EC_NEG1_LOG( subcount = sl_unpack_floats(query, buf, offset, encoding) );
738             offset += query_length;
739             count -= subcount;
740             break;
741         case SQ_TYPE_DATE:
742             EC_NEG1_LOG( subcount = sl_unpack_date(query, buf, offset, encoding) );
743             offset += query_length;
744             count -= subcount;
745             break;
746         default:
747             EC_FAIL;
748         }
749     }
750
751 EC_CLEANUP:
752     if (ret != 0) {
753         offset = -1;
754     }
755     return offset;
756 }
757
758 /**************************************************************************************************
759  * Global functions for packing und unpacking
760  **************************************************************************************************/
761
762 int sl_pack(DALLOC_CTX *query, char *buf)
763 {
764     EC_INIT;
765     char toc_buf[MAX_SLQ_TOC];
766     int toc_index = 0;
767     int len = 0;
768
769     memcpy(buf, "432130dm", 8);
770     EC_NEG1_LOG( len = sl_pack_loop(query, buf + 16, 0, toc_buf + 8, &toc_index) );
771     EC_ZERO( sivalc(buf, 8, MAX_SLQ_DAT, len / 8 + 1 + toc_index + 1) );
772     EC_ZERO( sivalc(buf, 12, MAX_SLQ_DAT, len / 8 + 1) );
773
774     EC_ZERO( slvalc(toc_buf, 0, MAX_SLQ_TOC, sl_pack_tag(SQ_TYPE_TOC, toc_index + 1, 0)) );
775     if ((16 + len + ((toc_index + 1 ) * 8)) >= MAX_SLQ_DAT)
776         EC_FAIL;
777     memcpy(buf + 16 + len, toc_buf, (toc_index + 1 ) * 8);
778     len += 16 + (toc_index + 1 ) * 8;
779
780 EC_CLEANUP:
781     if (ret != 0)
782         len = -1;
783     return len;
784 }
785
786 int sl_unpack(DALLOC_CTX *query, const char *buf)
787 {
788     EC_INIT;
789     int encoding, i, toc_entries;
790     uint64_t toc_offset, tquerylen, toc_entry;
791
792     if (strncmp(buf, "md031234", 8) == 0)
793         encoding = SL_ENC_BIG_ENDIAN;
794     else
795         encoding = SL_ENC_LITTLE_ENDIAN;
796
797     buf += 8;
798
799     toc_offset = ((sl_unpack_uint64(buf, 0, encoding) >> 32) - 1 ) * 8;
800     if (toc_offset < 0 || (toc_offset > 65000)) {
801         EC_FAIL;
802     }
803
804     buf += 8;
805
806     toc_entries = (int)(sl_unpack_uint64(buf, toc_offset, encoding) & 0xffff);
807
808     EC_NEG1( sl_unpack_loop(query, buf, 0, 1, toc_offset + 8, encoding) );
809
810 EC_CLEANUP:
811     EC_EXIT;
812 }