]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/spotlight.c
Spotlight RPC init is working
[netatalk.git] / etc / afpd / spotlight.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
36 #include "spotlight.h"
37
38 /**************************************************************************************************
39  * RPC data marshalling and unmarshalling
40  **************************************************************************************************/
41
42 /* FPSpotlightRPC subcommand codes */
43 #define SPOTLIGHT_CMD_VOLPATH 1
44 #define SPOTLIGHT_CMD_FLAGS   2
45 #define SPOTLIGHT_CMD_RPC     3
46
47 /* Spotlight epoch is UNIX epoch minus SPOTLIGHT_TIME_DELTA */
48 #define SPOTLIGHT_TIME_DELTA INT64_C(280878921600U)
49
50 #define SQ_TYPE_NULL    0x0000
51 #define SQ_TYPE_COMPLEX 0x0200
52 #define SQ_TYPE_INT64   0x8400
53 #define SQ_TYPE_BOOL    0x0100
54 #define SQ_TYPE_FLOAT   0x8500
55 #define SQ_TYPE_DATA    0x0700
56 #define SQ_TYPE_CNIDS   0x8700
57 #define SQ_TYPE_UUID    0x0e00
58 #define SQ_TYPE_DATE    0x8600
59 #define SQ_TYPE_TOC     0x8800
60
61 #define SQ_CPX_TYPE_ARRAY           0x0a00
62 #define SQ_CPX_TYPE_STRING          0x0c00
63 #define SQ_CPX_TYPE_UTF16_STRING    0x1c00
64 #define SQ_CPX_TYPE_DICT            0x0d00
65 #define SQ_CPX_TYPE_CNIDS           0x1a00
66 #define SQ_CPX_TYPE_FILEMETA        0x1b00
67
68 #define SUBQ_SAFETY_LIM 20
69
70 /* Can be ored and used as flags */
71 #define SL_ENC_LITTLE_ENDIAN 1
72 #define SL_ENC_BIG_ENDIAN    2
73 #define SL_ENC_UTF_16        4
74
75 /* Forward declarations */
76 static int dissect_spotlight(DALLOC_CTX *query, const char *buf);
77 static int sl_pack_loop(DALLOC_CTX *query, char *buf, int offset, char *toc_buf, int *toc_idx);
78
79 /* Helper functions and stuff */
80 static const char *neststrings[] = {
81     "",
82     "\t",
83     "\t\t",
84     "\t\t\t",
85     "\t\t\t\t",
86     "\t\t\t\t\t",
87     "\t\t\t\t\t\t",
88 };
89
90 static int dd_dump(DALLOC_CTX *dd, int nestinglevel)
91 {
92     const char *type;
93
94     LOG(log_debug, logtype_sl, "%s%s(#%d): {",
95         neststrings[nestinglevel], talloc_get_name(dd), talloc_array_length(dd->dd_talloc_array));
96
97     for (int n = 0; n < talloc_array_length(dd->dd_talloc_array); n++) {
98
99         type = talloc_get_name(dd->dd_talloc_array[n]);
100
101         if (STRCMP(type, ==, "DALLOC_CTX")
102                    || STRCMP(type, ==, "sl_array_t")
103                    || STRCMP(type, ==, "sl_dict_t")) {
104             dd_dump(dd->dd_talloc_array[n], nestinglevel + 1);
105         } else if (STRCMP(type, ==, "uint64_t")) {
106             uint64_t i;
107             memcpy(&i, dd->dd_talloc_array[n], sizeof(uint64_t));
108             LOG(log_debug, logtype_sl, "%suint64_t: 0x%04x", neststrings[nestinglevel + 1], i);
109         } else if (STRCMP(type, ==, "int64_t")) {
110             int64_t i;
111             memcpy(&i, dd->dd_talloc_array[n], sizeof(int64_t));
112             LOG(log_debug, logtype_sl, "%sint64_t: %" PRId64, neststrings[nestinglevel + 1], i);
113         } else if (STRCMP(type, ==, "uint32_t")) {
114             uint32_t i;
115             memcpy(&i, dd->dd_talloc_array[n], sizeof(uint32_t));
116             LOG(log_debug, logtype_sl, "%s%s: %" PRIu32, neststrings[nestinglevel + 1], type, i);
117         } else if (STRCMP(type, ==, "char *")) {
118             char *s;
119             memcpy(&s, dd->dd_talloc_array[n], sizeof(char *));
120             LOG(log_debug, logtype_sl, "%sstring: %s", neststrings[nestinglevel + 1], s);
121         } else if (STRCMP(type, ==, "sl_bool_t")) {
122             sl_bool_t bl;
123             memcpy(&bl, dd->dd_talloc_array[n], sizeof(sl_bool_t));
124             LOG(log_debug, logtype_sl, "%sbool: %s", neststrings[nestinglevel + 1], bl ? "true" : "false");
125         } else if (STRCMP(type, ==, "sl_cnids_t")) {
126             sl_cnids_t cnids;
127             memcpy(&cnids, dd->dd_talloc_array[n], sizeof(sl_cnids_t));
128             LOG(log_debug, logtype_sl, "%sCNIDs: unkn1: %" PRIu16 ", unkn2: %" PRIu32,
129                    neststrings[nestinglevel + 1], cnids.ca_unkn1, cnids.ca_context);
130             if (cnids.ca_cnids)
131                 dd_dump(cnids.ca_cnids, nestinglevel + 1);
132         }
133     }
134     LOG(log_debug, logtype_sl, "%s}", neststrings[nestinglevel]);
135 }
136
137 /*
138 * Returns the UTF-16 string encoding, by checking the 2-byte byte order mark.
139 * If there is no byte order mark, -1 is returned.
140 */
141 static uint spotlight_get_utf16_string_encoding(const char *buf, int offset, int query_length, uint encoding) {
142     uint utf16_encoding;
143
144     /* check for byte order mark */
145     utf16_encoding = SL_ENC_BIG_ENDIAN;
146     if (query_length >= 2) {
147         uint16_t byte_order_mark;
148         if (encoding == SL_ENC_LITTLE_ENDIAN)
149             byte_order_mark = SVAL(buf, offset);
150         else
151             byte_order_mark = RSVAL(buf, offset);
152
153         if (byte_order_mark == 0xFFFE) {
154             utf16_encoding = SL_ENC_BIG_ENDIAN | SL_ENC_UTF_16;
155         }
156         else if (byte_order_mark == 0xFEFF) {
157             utf16_encoding = SL_ENC_LITTLE_ENDIAN | SL_ENC_UTF_16;
158         }
159     }
160
161     return utf16_encoding;
162 }
163
164 /**************************************************************************************************
165  * marshalling functions
166  **************************************************************************************************/
167
168 #define SL_OFFSET_DELTA 16
169
170 static uint64_t sl_pack_tag(uint16_t type, uint16_t size_or_count, uint32_t val)
171 {
172     uint64_t tag = ((uint64_t)val << 32) | ((uint64_t)type << 16) | size_or_count;
173     return tag;
174 }
175
176 static int sl_pack_float(double d, char *buf, int offset)
177 {
178     union {
179         double d;
180         uint64_t w;
181     } ieee_fp_union;
182
183     SLVAL(buf, offset, sl_pack_tag(SQ_TYPE_FLOAT, 2, 1));
184     SLVAL(buf, offset + 8, ieee_fp_union.w);
185
186     return offset + 2 * sizeof(uint64_t);
187 }
188
189 static int sl_pack_uint64(uint64_t u, char *buf, int offset)
190 {
191     SLVAL(buf, offset, sl_pack_tag(SQ_TYPE_INT64, 2, 1));
192     SLVAL(buf, offset + 8, u);
193
194     return offset + 2 * sizeof(uint64_t);
195 }
196
197 static int sl_pack_bool(sl_bool_t bl, char *buf, int offset)
198 {
199     SLVAL(buf, offset, sl_pack_tag(SQ_TYPE_BOOL, 1, bl ? 1 : 0));
200
201     return offset + sizeof(uint64_t);
202 }
203
204 static int sl_pack_nil(char *buf, int offset)
205 {
206     SLVAL(buf, offset, sl_pack_tag(SQ_TYPE_NULL, 1, 1));
207
208     return offset + sizeof(uint64_t);
209 }
210
211 static int sl_pack_date(sl_time_t t, char *buf, int offset)
212 {
213     uint64_t data = 0;
214
215     data = (t.tv_sec + SPOTLIGHT_TIME_DELTA) << 24;
216
217     SLVAL(buf, offset, sl_pack_tag(SQ_TYPE_DATE, 2, 1));
218     SLVAL(buf, offset + 8, data);
219
220     return offset + 2 * sizeof(uint64_t);
221 }
222
223 static int sl_pack_uuid(sl_uuid_t *uuid, char *buf, int offset)
224 {
225     SLVAL(buf, offset, sl_pack_tag(SQ_TYPE_UUID, 3, 1));
226     memcpy(buf + offset + 8, uuid, 16);
227
228     return offset + sizeof(uint64_t) + 16;
229 }
230
231 static int sl_pack_CNID(sl_cnids_t *cnids, char *buf, int offset, char *toc_buf, int *toc_idx)
232 {
233     int len = 0, off = 0;
234     int cnid_count = talloc_array_length(cnids->ca_cnids);
235
236     SLVAL(toc_buf, *toc_idx * 8, sl_pack_tag(SQ_CPX_TYPE_CNIDS, (offset + SL_OFFSET_DELTA) / 8, cnid_count));
237     SLVAL(buf, offset, sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1));
238     *toc_idx += 1;
239     offset += 8;
240
241     SLVAL(buf, offset, sl_pack_tag(SQ_TYPE_CNIDS, 2 + cnid_count, 8 /* unknown meaning, but always 8 */));
242     offset += 8;
243
244     if (cnid_count > 0) {
245         SLVAL(buf, offset, sl_pack_tag(0x0add, cnid_count, cnids->ca_context));
246         offset += 8;
247
248         for (int i = 0; i < cnid_count; i++) {
249             SLVAL(buf, offset, cnids->ca_cnids->dd_talloc_array[i]);
250             offset += 8;
251         }
252     }
253     
254     return offset;
255 }
256
257 static int sl_pack_array(sl_array_t *array, char *buf, int offset, char *toc_buf, int *toc_idx)
258 {
259     int count = talloc_array_length(array->dd_talloc_array);
260     int octets = (offset + SL_OFFSET_DELTA) / 8;
261
262     LOG(log_maxdebug, logtype_sl, "sl_pack_array: count: %d, offset:%d, octets: %d", count, offset, octets);
263
264     SLVAL(toc_buf, *toc_idx * 8, sl_pack_tag(SQ_CPX_TYPE_ARRAY, octets, count));
265     SLVAL(buf, offset, sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1));
266     *toc_idx += 1;
267     offset += 8;
268
269     offset = sl_pack_loop(array, buf, offset, toc_buf, toc_idx);
270
271     return offset;
272 }
273
274 static int sl_pack_dict(sl_array_t *dict, char *buf, int offset, char *toc_buf, int *toc_idx)
275 {
276     SLVAL(toc_buf, *toc_idx * 8, sl_pack_tag(SQ_CPX_TYPE_DICT, (offset + SL_OFFSET_DELTA) / 8, talloc_array_length(dict->dd_talloc_array)));
277     SLVAL(buf, offset, sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1));
278     *toc_idx += 1;
279     offset += 8;
280
281     offset = sl_pack_loop(dict, buf, offset, toc_buf, toc_idx);
282
283     return offset;
284 }
285
286 static int sl_pack_string(char **string, char *buf, int offset, char *toc_buf, int *toc_idx)
287 {
288     int len, octets, used_in_last_octet;
289     char *s = *string;
290     len = strlen(s);
291     octets = (len / 8) + (len & 7 ? 1 : 0);
292     used_in_last_octet = 8 - (octets * 8 - len);
293
294     LOG(log_maxdebug, logtype_sl, "sl_pack_string(\"%s\"): len: %d, octets: %d, used_in_last_octet: %d",
295         s, len, octets, used_in_last_octet);
296
297     SLVAL(toc_buf, *toc_idx * 8, sl_pack_tag(SQ_CPX_TYPE_STRING, (offset + SL_OFFSET_DELTA) / 8, used_in_last_octet));
298     SLVAL(buf, offset, sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1));
299     *toc_idx += 1;
300     offset += 8;
301
302     SLVAL(buf, offset, sl_pack_tag(SQ_TYPE_DATA, octets + 1, used_in_last_octet));
303     offset += 8;
304
305     memset(buf + offset, 0, octets * 8);
306     strncpy(buf + offset, s, len);
307     offset += octets * 8;
308
309     return offset;
310 }
311
312 static int sl_pack_loop(DALLOC_CTX *query, char *buf, int offset, char *toc_buf, int *toc_idx)
313 {
314     const char *type;
315
316     for (int n = 0; n < talloc_array_length(query->dd_talloc_array); n++) {
317
318         type = talloc_get_name(query->dd_talloc_array[n]);
319
320         if (STRCMP(type, ==, "sl_array_t")) {
321             offset = sl_pack_array(query->dd_talloc_array[n], buf, offset, toc_buf, toc_idx);
322         } else if (STRCMP(type, ==, "sl_dict_t")) {
323             offset = sl_pack_dict(query->dd_talloc_array[n], buf, offset, toc_buf, toc_idx);
324         } else if (STRCMP(type, ==, "uint64_t")) {
325             uint64_t i;
326             memcpy(&i, query->dd_talloc_array[n], sizeof(uint64_t));
327             offset = sl_pack_uint64(i, buf, offset);
328         } else if (STRCMP(type, ==, "char *")) {
329             offset = sl_pack_string(query->dd_talloc_array[n], buf, offset, toc_buf, toc_idx);
330         } else if (STRCMP(type, ==, "sl_bool_t")) {
331             sl_bool_t bl;
332             memcpy(&bl, query->dd_talloc_array[n], sizeof(sl_bool_t));
333             offset = sl_pack_bool(bl, buf, offset);
334         } else if (STRCMP(type, ==, "double")) {
335             double d;
336             memcpy(&d, query->dd_talloc_array[n], sizeof(double));
337             offset = sl_pack_float(d, buf, offset);
338         } else if (STRCMP(type, ==, "sl_nil_t")) {
339             offset = sl_pack_nil(buf, offset);
340         } else if (STRCMP(type, ==, "sl_time_t")) {
341             sl_time_t t;
342             memcpy(&t, query->dd_talloc_array[n], sizeof(sl_time_t));
343             offset = sl_pack_date(t, buf, offset);
344         } else if (STRCMP(type, ==, "sl_uuid_t")) {
345             offset = sl_pack_uuid(query->dd_talloc_array[n], buf, offset);
346         } else if (STRCMP(type, ==, "sl_cnids_t")) {
347             offset = sl_pack_CNID(query->dd_talloc_array[n], buf, offset, toc_buf, toc_idx);
348         }
349     }
350
351     return offset;
352 }
353
354 #define MAX_SLQ_DAT 65000
355 #define MAX_SLQ_TOC 2048
356
357 static int sl_pack(DALLOC_CTX *query, char *buf)
358 {
359     EC_INIT;
360     char toc_buf[MAX_SLQ_TOC];
361     int toc_index = 0;
362     int len = 0;
363
364     memcpy(buf, "432130dm", 8);
365     EC_NEG1_LOG( len = sl_pack_loop(query, buf + 16, 0, toc_buf + 8, &toc_index) );
366     SIVAL(buf, 8, len / 8 + 1 + toc_index + 1);
367     SIVAL(buf, 12, len / 8 + 1);
368
369     SLVAL(toc_buf, 0, sl_pack_tag(SQ_TYPE_TOC, toc_index + 1, 0));
370     memcpy(buf + 16 + len, toc_buf, (toc_index + 1 ) * 8);
371
372     len += 16 + (toc_index + 1 ) * 8;
373
374 EC_CLEANUP:
375     if (ret != 0)
376         len = -1;
377     return len;
378 }
379
380 /**************************************************************************************************
381  * unmarshalling functions
382  **************************************************************************************************/
383
384 static uint64_t sl_unpack_uint64(const char *buf, int offset, uint encoding)
385 {
386     if (encoding == SL_ENC_LITTLE_ENDIAN)
387             return LVAL(buf, offset);
388         else
389             return RLVAL(buf, offset);
390 }
391
392 static int sl_unpack_ints(DALLOC_CTX *query, const char *buf, int offset, uint encoding)
393 {
394     int count, i;
395     uint64_t query_data64;
396
397     query_data64 = sl_unpack_uint64(buf, offset, encoding);
398     count = query_data64 >> 32;
399     offset += 8;
400
401     i = 0;
402     while (i++ < count) {
403         query_data64 = sl_unpack_uint64(buf, offset, encoding);
404         dalloc_add(query, &query_data64, uint64_t);
405         offset += 8;
406     }
407
408     return count;
409 }
410
411 static int sl_unpack_date(DALLOC_CTX *query, const char *buf, int offset, uint encoding)
412 {
413     int count, i;
414     uint64_t query_data64;
415     sl_time_t t;
416
417     query_data64 = sl_unpack_uint64(buf, offset, encoding);
418     count = query_data64 >> 32;
419     offset += 8;
420
421     i = 0;
422     while (i++ < count) {
423         query_data64 = sl_unpack_uint64(buf, offset, encoding) >> 24;
424         t.tv_sec = query_data64 - SPOTLIGHT_TIME_DELTA;
425         t.tv_usec = 0;
426         dalloc_add(query, &t, sl_time_t);
427         offset += 8;
428     }
429
430     return count;
431 }
432
433 static int sl_unpack_uuid(DALLOC_CTX *query, const char *buf, int offset, uint encoding)
434 {
435     int count, i;
436     uint64_t query_data64;
437     sl_uuid_t uuid;
438     query_data64 = sl_unpack_uint64(buf, offset, encoding);
439     count = query_data64 >> 32;
440     offset += 8;
441
442     i = 0;
443     while (i++ < count) {
444         memcpy(uuid.sl_uuid, buf + offset, 16);
445         dalloc_add(query, &uuid, sl_uuid_t);
446         offset += 16;
447     }
448
449     return count;
450 }
451
452 static int sl_unpack_floats(DALLOC_CTX *query, const char *buf, int offset, uint encoding)
453 {
454     int count, i;
455     uint64_t query_data64;
456     double fval;
457     union {
458         double d;
459         uint32_t w[2];
460     } ieee_fp_union;
461
462     query_data64 = sl_unpack_uint64(buf, offset, encoding);
463     count = query_data64 >> 32;
464     offset += 8;
465
466     i = 0;
467     while (i++ < count) {
468         if (encoding == SL_ENC_LITTLE_ENDIAN) {
469 #ifdef WORDS_BIGENDIAN
470             ieee_fp_union.w[0] = IVAL(buf, offset + 4);
471             ieee_fp_union.w[1] = IVAL(buf, offset);
472 #else
473             ieee_fp_union.w[0] = IVAL(buf, offset);
474             ieee_fp_union.w[1] = IVAL(buf, offset + 4);
475 #endif
476         } else {
477 #ifdef WORDS_BIGENDIAN
478             ieee_fp_union.w[0] = RIVAL(buf, offset);
479             ieee_fp_union.w[1] = RIVAL(buf, offset + 4);
480 #else
481             ieee_fp_union.w[0] = RIVAL(buf, offset + 4);
482             ieee_fp_union.w[1] = RIVAL(buf, offset);
483 #endif
484         }
485         dalloc_add(query, &ieee_fp_union.d, double);
486         offset += 8;
487     }
488
489     return count;
490 }
491
492 static int sl_unpack_CNID(DALLOC_CTX *query, const char *buf, int offset, int length, uint encoding)
493 {
494     EC_INIT;
495     int count;
496     uint64_t query_data64;
497     sl_cnids_t cnids;
498
499     EC_NULL( cnids.ca_cnids = talloc_zero(query, DALLOC_CTX) );
500
501     if (length <= 16)
502         /* that's permitted, it's an empty array */
503         goto EC_CLEANUP;
504     
505     query_data64 = sl_unpack_uint64(buf, offset, encoding);
506     count = query_data64 & 0xffff;
507
508     cnids.ca_unkn1 = (query_data64 & 0xffff0000) >> 16;
509     cnids.ca_context = query_data64 >> 32;
510
511     offset += 8;
512
513     while (count --) {
514         query_data64 = sl_unpack_uint64(buf, offset, encoding);
515         dalloc_add(cnids.ca_cnids, &query_data64, uint64_t);
516         offset += 8;
517     }
518
519     dalloc_add(query, &cnids, sl_cnids_t);
520
521 EC_CLEANUP:
522     EC_EXIT;
523 }
524
525 static const char *spotlight_get_qtype_string(uint64_t query_type)
526 {
527     switch (query_type) {
528     case SQ_TYPE_NULL:
529         return "null";
530     case SQ_TYPE_COMPLEX:
531         return "complex";
532     case SQ_TYPE_INT64:
533         return "int64";
534     case SQ_TYPE_BOOL:
535         return "bool";
536     case SQ_TYPE_FLOAT:
537         return "float";
538     case SQ_TYPE_DATA:
539         return "data";
540     case SQ_TYPE_CNIDS:
541         return "CNIDs";
542     default:
543         return "unknown";
544     }
545 }
546
547 static const char *spotlight_get_cpx_qtype_string(uint64_t cpx_query_type)
548 {
549     switch (cpx_query_type) {
550     case SQ_CPX_TYPE_ARRAY:
551         return "array";
552     case SQ_CPX_TYPE_STRING:
553         return "string";
554     case SQ_CPX_TYPE_UTF16_STRING:
555         return "utf-16 string";
556     case SQ_CPX_TYPE_DICT:
557         return "dictionary";
558     case SQ_CPX_TYPE_CNIDS:
559         return "CNIDs";
560     case SQ_CPX_TYPE_FILEMETA:
561         return "FileMeta";
562     default:
563         return "unknown";
564     }
565 }
566
567 static int spotlight_dissect_loop(DALLOC_CTX *query,
568                                   const char *buf,
569                                   uint offset,
570                                   uint count,
571                                   const uint toc_offset,
572                                   const uint encoding)
573 {
574     EC_INIT;
575     int i, toc_index, query_length;
576     uint subcount, cpx_query_type, cpx_query_count;
577     uint64_t query_data64, query_type;
578     uint unicode_encoding;
579     uint8_t mark_exists;
580     char *p;
581     int padding, slen;
582
583     while (count > 0 && (offset < toc_offset)) {
584         query_data64 = sl_unpack_uint64(buf, offset, encoding);
585         query_length = (query_data64 & 0xffff) * 8;
586         query_type = (query_data64 & 0xffff0000) >> 16;
587         if (query_length == 0)
588             EC_FAIL;
589
590         switch (query_type) {
591         case SQ_TYPE_COMPLEX:
592             toc_index = (query_data64 >> 32) - 1;
593             query_data64 = sl_unpack_uint64(buf, toc_offset + toc_index * 8, encoding);
594             cpx_query_type = (query_data64 & 0xffff0000) >> 16;
595             cpx_query_count = query_data64 >> 32;
596
597             switch (cpx_query_type) {
598             case SQ_CPX_TYPE_ARRAY: {
599                 sl_array_t *sl_arrary = talloc_zero(query, sl_array_t);
600                 EC_NEG1_LOG( offset = spotlight_dissect_loop(sl_arrary, buf, offset + 8, cpx_query_count, toc_offset, encoding) );
601                 dalloc_add(query, sl_arrary, sl_array_t);
602                 break;
603             }
604
605             case SQ_CPX_TYPE_DICT: {
606                 sl_dict_t *sl_dict = talloc_zero(query, sl_dict_t);
607                 EC_NEG1_LOG( offset = spotlight_dissect_loop(sl_dict, buf, offset + 8, cpx_query_count, toc_offset, encoding) );
608                 dalloc_add(query, sl_dict, sl_dict_t);
609                 break;
610             }
611             case SQ_CPX_TYPE_STRING:
612                 query_data64 = sl_unpack_uint64(buf, offset + 8, encoding);
613                 query_length += (query_data64 & 0xffff) * 8;
614                 if ((padding = 8 - (query_data64 >> 32)) < 0)
615                     EC_FAIL;
616                 if ((slen = query_length - 16 - padding) < 1)
617                     EC_FAIL;
618                 p = talloc_strndup(query, buf + offset + 16, slen);
619                 dalloc_add(query, &p, char *);
620                 break;
621
622             case SQ_CPX_TYPE_UTF16_STRING:
623                 query_data64 = sl_unpack_uint64(buf, offset + 8, encoding);
624                 query_length += (query_data64 & 0xffff) * 8;
625                 if ((padding = 8 - (query_data64 >> 32)) < 0)
626                     EC_FAIL;
627                 if ((slen = query_length - 16 - padding) < 1)
628                     EC_FAIL;
629
630                 unicode_encoding = spotlight_get_utf16_string_encoding(buf, offset + 16, slen, encoding);
631                 mark_exists = (unicode_encoding & SL_ENC_UTF_16);
632                 unicode_encoding &= ~SL_ENC_UTF_16;
633
634                 EC_NEG1( convert_string_allocate(CH_UCS2, CH_UTF8, buf + offset + (mark_exists ? 18 : 16), slen, &p) );
635                 dalloc_add(query, &p, char *);
636                 break;
637
638             case SQ_CPX_TYPE_FILEMETA:
639                 query_data64 = sl_unpack_uint64(buf, offset + 8, encoding);
640                 query_length += (query_data64 & 0xffff) * 8;
641
642                 if (query_length <= 8) {
643                     EC_FAIL_LOG("SQ_CPX_TYPE_FILEMETA: query_length <= 8%s", "");
644                 } else {
645                     EC_NEG1_LOG( dissect_spotlight(query, buf + offset + 16) );
646                 }
647                 break;
648
649             case SQ_CPX_TYPE_CNIDS:
650                 query_data64 = sl_unpack_uint64(buf, offset + 8, encoding);
651                 query_length += (query_data64 & 0xffff) * 8;
652                 EC_NEG1_LOG( sl_unpack_CNID(query, buf, offset + 16, query_length, encoding) );
653                 break;
654             } /* switch (cpx_query_type) */
655
656             count--;
657             break;
658
659         case SQ_TYPE_NULL: {
660             subcount = query_data64 >> 32;
661             if (subcount > 64)
662                 EC_FAIL;
663             sl_nil_t nil = 0;
664             for (i = 0; i < subcount; i++)
665                 dalloc_add(query, &nil, sl_nil_t);
666             count -= subcount;
667             break;
668         }
669         case SQ_TYPE_BOOL: {
670             sl_bool_t b = query_data64 >> 32;
671             dalloc_add(query, &b, sl_bool_t);
672             count--;
673             break;
674         }
675         case SQ_TYPE_INT64:
676             EC_NEG1_LOG( subcount = sl_unpack_ints(query, buf, offset, encoding) );
677             count -= subcount;
678             break;
679         case SQ_TYPE_UUID:
680             EC_NEG1_LOG( subcount = sl_unpack_uuid(query, buf, offset, encoding) );
681             count -= subcount;
682             break;
683         case SQ_TYPE_FLOAT:
684             EC_NEG1_LOG( subcount = sl_unpack_floats(query, buf, offset, encoding) );
685             count -= subcount;
686             break;
687         case SQ_TYPE_DATE:
688             EC_NEG1_LOG( subcount = sl_unpack_date(query, buf, offset, encoding) );
689             count -= subcount;
690             break;
691         default:
692             EC_FAIL;
693         }
694
695         offset += query_length;
696     }
697
698 EC_CLEANUP:
699     if (ret != 0) {
700         offset = -1;
701     }
702     return offset;
703 }
704
705 static int dissect_spotlight(DALLOC_CTX *query, const char *buf)
706 {
707     EC_INIT;
708     int encoding, i, toc_entries;
709     uint64_t toc_offset, tquerylen, toc_entry;
710
711     if (strncmp(buf, "md031234", 8) == 0)
712         encoding = SL_ENC_BIG_ENDIAN;
713     else
714         encoding = SL_ENC_LITTLE_ENDIAN;
715
716     buf += 8;
717
718     toc_offset = ((sl_unpack_uint64(buf, 0, encoding) >> 32) - 1 ) * 8;
719     if (toc_offset < 0 || (toc_offset > 65000)) {
720         EC_FAIL;
721     }
722
723     buf += 8;
724
725     toc_entries = (int)(sl_unpack_uint64(buf, toc_offset, encoding) & 0xffff);
726
727     EC_NEG1( spotlight_dissect_loop(query, buf, 0, 1, toc_offset + 8, encoding) );
728
729 EC_CLEANUP:
730     EC_EXIT;
731 }
732
733 /**************************************************************************************************
734  * Spotlight RPC functions
735  **************************************************************************************************/
736
737 static int sl_rpc_fetchPropertiesForContext(AFPObj *obj, const DALLOC_CTX *query, DALLOC_CTX *reply, const struct vol *v)
738 {
739     EC_INIT;
740
741     char *s;
742     sl_dict_t *dict;
743     sl_array_t *array;
744     sl_uuid_t uuid;
745
746     if (!v->v_uuid)
747         EC_FAIL_LOG("sl_rpc_fetchPropertiesForContext: missing UUID for volume: %s", v->v_localname);
748
749     dict = talloc_zero(reply, sl_dict_t);
750
751     /* key/val 1 */
752     s = talloc_strdup(dict, "kMDSStoreMetaScopes");
753     dalloc_add(dict, &s, char *);
754
755     array = talloc_zero(dict, sl_array_t);
756     s = talloc_strdup(array, "kMDQueryScopeComputer");
757     dalloc_add(array, &s, char *);
758     dalloc_add(dict, array, sl_array_t);
759
760     /* key/val 2 */
761     s = talloc_strdup(dict, "kMDSStorePathScopes");
762     dalloc_add(dict, &s, char *);
763
764     array = talloc_zero(dict, sl_array_t);
765     s = talloc_strdup(array, v->v_path);
766     dalloc_add(array, &s, char *);
767     dalloc_add(dict, array, sl_array_t);
768
769     /* key/val 3 */
770     s = talloc_strdup(dict, "kMDSStoreUUID");
771     dalloc_add(dict, &s, char *);
772
773     memcpy(uuid.sl_uuid, v->v_uuid, 16);
774     dalloc_add(dict, &uuid, sl_uuid_t);
775
776     /* key/val 4 */
777     s = talloc_strdup(dict, "kMDSStoreHasPersistentUUID");
778     dalloc_add(dict, &s, char *);
779     sl_bool_t b = true;
780     dalloc_add(dict, &b, sl_bool_t);
781
782     dalloc_add(reply, dict, sl_dict_t);
783
784 EC_CLEANUP:
785     EC_EXIT;
786 }
787
788 /**************************************************************************************************
789  * AFP functions
790  **************************************************************************************************/
791 int afp_spotlight_rpc(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
792 {
793     EC_INIT;
794     TALLOC_CTX *tmp_ctx = talloc_new(NULL);
795     uint16_t vid;
796     int cmd;
797     int endianess = SL_ENC_LITTLE_ENDIAN;
798     struct vol      *vol;
799     DALLOC_CTX *query;
800
801     *rbuflen = 0;
802
803     ibuf += 2;
804     ibuflen -= 2;
805
806     vid = SVAL(ibuf, 0);
807     LOG(log_debug, logtype_sl, "afp_spotlight_rpc(vid: %" PRIu16 ")", vid);
808
809     if ((vol = getvolbyvid(vid)) == NULL) {
810         LOG(log_error, logtype_sl, "afp_spotlight_rpc: bad volume id: %" PRIu16 ")", vid);
811         ret = AFPERR_ACCESS;
812         goto EC_CLEANUP;
813     }
814
815     /*    IVAL(ibuf, 2): unknown, always 0x00008004, some flags ? */
816
817     cmd = RIVAL(ibuf, 6);
818     LOG(log_debug, logtype_sl, "afp_spotlight_rpc(cmd: %d)", cmd);
819
820     /*    IVAL(ibuf, 10: unknown, always 0x00000000 */
821
822     switch (cmd) {
823
824     case SPOTLIGHT_CMD_VOLPATH: {
825         RSIVAL(rbuf, 0, ntohs(vid));
826         RSIVAL(rbuf, 4, 0);
827         int len = strlen(vol->v_path) + 1;
828         strncpy(rbuf + 8, vol->v_path, len);
829         *rbuflen += 8 + len;
830         break;
831     }
832     case SPOTLIGHT_CMD_FLAGS:
833         RSIVAL(rbuf, 0, 0x0100006b); /* Whatever this value means... flags? */
834         *rbuflen += 4;
835         break;
836
837     case SPOTLIGHT_CMD_RPC: {
838         DALLOC_CTX *query;
839         EC_NULL( query = talloc_zero(tmp_ctx, DALLOC_CTX) );
840         DALLOC_CTX *reply;
841         EC_NULL( reply = talloc_zero(tmp_ctx, DALLOC_CTX) );
842
843         EC_ZERO( dissect_spotlight(query, ibuf + 22) );
844         dd_dump(query, 0);
845
846         char **cmd;
847         EC_NULL_LOG( cmd = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0, "char *", 0) );
848
849
850         if (STRCMP(*cmd, ==, "fetchPropertiesForContext:")) {
851             EC_ZERO_LOG( sl_rpc_fetchPropertiesForContext(obj, query, reply, vol) );
852         } else if (STRCMP(*cmd, ==, "fetchQueryResultsForContext:")) {
853             uint64_t *p;
854             if ((p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0, "uint64_t", 1)) != NULL) {
855                 LOG(log_info, logtype_sl, "fetchQueryResultsForContext: 0x%" PRIx64, *p);
856             }
857         }
858
859         /* Spotlight RPC status code ? 0 in all traces, we use 0xffffffff for an error, never seen from Apple */
860         if (ret == 0)
861             memset(rbuf, 0, 4);
862         else
863             memset(rbuf, 0xff, 4);
864         *rbuflen += 4;
865
866         int len;
867         EC_NEG1_LOG( len = sl_pack(reply, rbuf + 4) );
868         *rbuflen += len;
869         break;
870     }
871     }
872
873 EC_CLEANUP:
874     talloc_free(tmp_ctx);
875     if (ret != AFP_OK) {
876         *rbuflen = 0;
877         return AFPERR_MISC;
878     }
879     EC_EXIT;
880 }
881
882 /**************************************************************************************************
883  * Testing
884  **************************************************************************************************/
885
886 #ifdef SPOT_TEST_MAIN
887
888 int main(int argc, char **argv)
889 {
890     EC_INIT;
891     TALLOC_CTX *mem_ctx = talloc_new(NULL);
892     DALLOC_CTX *dd = talloc_zero(mem_ctx, DALLOC_CTX);
893     int64_t i;
894
895     set_processname("spot");
896     setuplog("default:info,spotlight:debug", "/dev/tty");
897
898     LOG(log_info, logtype_sl, "Start");
899
900 #if 0
901     i = 2;
902     dalloc_add(dd, &i, int64_t);
903
904     i = 1;
905     dalloc_add(dd, &i, int64_t);
906
907
908     char *str = talloc_strdup(dd, "hello world");
909     dalloc_add(dd, &str, char *);
910
911     sl_bool_t b = true;
912     dalloc_add(dd, &b, sl_bool_t);
913
914     b = false;
915     dalloc_add(dd, &b, sl_bool_t);
916
917
918     /* add a nested array */
919     DALLOC_CTX *nested = talloc_zero(dd, DALLOC_CTX);
920     i = 3;
921     dalloc_add(nested, &i, int64_t);
922     dalloc_add(dd, nested, DALLOC_CTX);
923
924     /* test an allocated CNID array */
925     uint32_t id = 16;
926     sl_cnids_t *cnids = talloc_zero(dd, sl_cnids_t);
927
928     cnids->ca_cnids = talloc_zero(cnids, DALLOC_CTX);
929
930     cnids->ca_unkn1 = 1;
931     cnids->ca_unkn2 = 2;
932
933     dalloc_add(cnids->ca_cnids, &id, uint32_t);
934     dalloc_add(dd, cnids, sl_cnids_t);
935
936 #endif
937
938     /* Now the Spotlight types */
939     sl_array_t *sl_arrary = talloc_zero(dd, sl_array_t);
940     i = 1234;
941     dalloc_add(sl_arrary, &i, int64_t);
942
943     sl_dict_t *sl_dict = talloc_zero(dd, sl_dict_t);
944     i = 5678;
945     dalloc_add(sl_dict, &i, int64_t);
946     dalloc_add(sl_arrary, sl_dict, sl_dict_t);
947
948     dalloc_add(dd, sl_arrary, sl_array_t);
949     dd_dump(dd, 0);
950
951
952 #if 0
953     /* now parse a real spotlight packet */
954     char ibuf[8192];
955     char rbuf[8192];
956     int fd;
957     size_t len;
958     DALLOC_CTX *query;
959
960     EC_NULL( query = talloc_zero(mem_ctx, DALLOC_CTX) );
961
962     EC_NEG1_LOG( fd = open("spotlight-packet.bin", O_RDONLY) );
963     EC_NEG1_LOG( len = read(fd, ibuf, 8192) );
964     close(fd);
965     EC_NEG1_LOG( dissect_spotlight(query, ibuf + 24) );
966
967     /* Now dump the whole thing */
968     dd_dump(query, 0);
969
970     int qlen;
971     char buf[MAX_SLQ_DAT];
972     EC_NEG1_LOG( qlen = sl_pack(query, buf) );
973
974     EC_NEG1_LOG( fd = open("test.bin", O_RDWR) );
975     lseek(fd, 24, SEEK_SET);
976     write(fd, buf, qlen);
977     close(fd);
978 #endif
979
980 EC_CLEANUP:
981     if (mem_ctx) {
982         talloc_free(mem_ctx);
983         mem_ctx = NULL;
984     }
985     EC_EXIT;
986 }
987 #endif