]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/spotlight.c
Basic functional Spotlight dissection
[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_FLAGS   2
44 #define SPOTLIGHT_CMD_RPC     3
45 #define SPOTLIGHT_CMD_VOLPATH 4
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
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 /* Can be ored and used as flags */
70 #define SL_ENC_LITTLE_ENDIAN 1
71 #define SL_ENC_BIG_ENDIAN    2
72 #define SL_ENC_UTF_16        4
73
74 /* Forward declarations */
75 static int dissect_spotlight(DALLOC_CTX *query, const char *buf);
76
77 static const char *neststrings[] = {
78     "",
79     "    ",
80     "        ",
81     "            ",
82     "                ",
83     "                    ",
84     "                        "
85 };
86
87 static int dd_dump(DALLOC_CTX *dd, int nestinglevel)
88 {
89     const char *type;
90
91     LOG(logtype_default, log_debug, "%s1: %s(#%d): {", neststrings[nestinglevel], talloc_get_name(dd), talloc_array_length(dd->dd_talloc_array));
92
93     for (int n = 0; n < talloc_array_length(dd->dd_talloc_array); n++) {
94
95         type = talloc_get_name(dd->dd_talloc_array[n]);
96
97         if (STRCMP(type, ==, "DALLOC_CTX")
98                    || STRCMP(type, ==, "sl_array_t")
99                    || STRCMP(type, ==, "sl_dict_t")) {
100             dd_dump(dd->dd_talloc_array[n], nestinglevel + 1);
101         } else if (STRCMP(type, ==, "uint64_t")) {
102             uint64_t i;
103             memcpy(&i, dd->dd_talloc_array[n], sizeof(uint64_t));
104             LOG(logtype_default, log_debug, "%s%u:\t0x%04x", neststrings[nestinglevel + 1], n + 1, i);
105         } else if (STRCMP(type, ==, "int64_t")) {
106             int64_t i;
107             memcpy(&i, dd->dd_talloc_array[n], sizeof(int64_t));
108             LOG(logtype_default, log_debug, "%s%d:\t%" PRId64, neststrings[nestinglevel + 1], n + 1, i);
109         } else if (STRCMP(type, ==, "uint32_t")) {
110             uint32_t i;
111             memcpy(&i, dd->dd_talloc_array[n], sizeof(uint32_t));
112             LOG(logtype_default, log_debug, "%s%d:\t%" PRIu32, neststrings[nestinglevel + 1], n + 1, i);
113         } else if (STRCMP(type, ==, "char *")) {
114             char *s;
115             memcpy(&s, dd->dd_talloc_array[n], sizeof(char *));
116             LOG(logtype_default, log_debug, "%s%d:\t%s", neststrings[nestinglevel + 1], n + +1, s);
117         } else if (STRCMP(type, ==, "sl_bool_t")) {
118             sl_bool_t bl;
119             memcpy(&bl, dd->dd_talloc_array[n], sizeof(sl_bool_t));
120             LOG(logtype_default, log_debug, "%s%d:\t%s", neststrings[nestinglevel + 1], n + +1, bl ? "true" : "false");
121         } else if (STRCMP(type, ==, "sl_cnids_t")) {
122             sl_cnids_t cnids;
123             memcpy(&cnids, dd->dd_talloc_array[n], sizeof(sl_cnids_t));
124             LOG(logtype_default, log_debug, "%s%d:\tunkn1: %" PRIu16 ", unkn2: %" PRIu32,
125                    neststrings[nestinglevel + 1], n + 1, cnids.ca_unkn1, cnids.ca_unkn2);
126             if (cnids.ca_cnids)
127                 dd_dump(cnids.ca_cnids, nestinglevel + 1);
128         }
129     }
130     LOG(logtype_default, log_debug, "%s}", neststrings[nestinglevel]);
131 }
132
133 static double spotlight_ieee_double(const char *buf, int offset, uint encoding)
134 {
135     union {
136         double d;
137         uint32_t w[2];
138     } ieee_fp_union;
139
140         if (encoding == SL_ENC_LITTLE_ENDIAN) {
141 #ifdef WORDS_BIGENDIAN
142         ieee_fp_union.w[0] = IVAL(buf, offset + 4);
143         ieee_fp_union.w[1] = IVAL(buf, offset);
144 #else
145         ieee_fp_union.w[0] = IVAL(buf, offset);
146         ieee_fp_union.w[1] = IVAL(buf, offset + 4);
147 #endif
148         return ieee_fp_union.d;
149     } else {
150 #ifdef WORDS_BIGENDIAN
151         ieee_fp_union.w[0] = RIVAL(buf, offset);
152         ieee_fp_union.w[1] = RIVAL(buf, offset + 4);
153 #else
154         ieee_fp_union.w[0] = RIVAL(buf, offset + 4);
155         ieee_fp_union.w[1] = RIVAL(buf, offset);
156 #endif
157         return ieee_fp_union.d;
158     }
159 }
160
161 static uint64_t spotlight_ntoh64(const char *buf, int off, uint encoding)
162 {
163         if (encoding == SL_ENC_LITTLE_ENDIAN)
164                 return LVAL(buf, off);
165         else
166         return ntoh64(LVAL(buf, off));
167 }
168
169 /*
170 * Returns the UTF-16 string encoding, by checking the 2-byte byte order mark.
171 * If there is no byte order mark, -1 is returned.
172 */
173 static uint spotlight_get_utf16_string_encoding(const char *buf, int offset, int query_length, uint encoding) {
174         uint utf16_encoding;
175
176         /* check for byte order mark */
177         utf16_encoding = SL_ENC_BIG_ENDIAN;
178         if (query_length >= 2) {
179                 uint16_t byte_order_mark;
180                 if (encoding == SL_ENC_LITTLE_ENDIAN)
181                         byte_order_mark = SVAL(buf, offset);
182                 else
183                         byte_order_mark = RSVAL(buf, offset);
184
185                 if (byte_order_mark == 0xFFFE) {
186                         utf16_encoding = SL_ENC_BIG_ENDIAN | SL_ENC_UTF_16;
187                 }
188                 else if (byte_order_mark == 0xFEFF) {
189                         utf16_encoding = SL_ENC_LITTLE_ENDIAN | SL_ENC_UTF_16;
190                 }
191         }
192
193         return utf16_encoding;
194 }
195
196 static int spotlight_int64(DALLOC_CTX *query, const char *buf, int offset, uint encoding)
197 {
198         int count, i;
199         uint64_t query_data64;
200
201         query_data64 = spotlight_ntoh64(buf, offset, encoding);
202         count = query_data64 >> 32;
203         offset += 8;
204
205         i = 0;
206         while (i++ < count) {
207                 query_data64 = spotlight_ntoh64(buf, offset, encoding);
208         dalloc_add(query, &query_data64, uint64_t);
209                 offset += 8;
210         }
211
212         return count;
213 }
214
215 static int spotlight_date(DALLOC_CTX *query, const char *buf, int offset, uint encoding)
216 {
217         int count, i;
218         uint64_t query_data64;
219         sl_time_t t;
220
221         query_data64 = spotlight_ntoh64(buf, offset, encoding);
222         count = query_data64 >> 32;
223         offset += 8;
224
225         i = 0;
226         while (i++ < count) {
227                 query_data64 = spotlight_ntoh64(buf, offset, encoding) >> 24;
228                 t.tv_sec = query_data64 - SPOTLIGHT_TIME_DELTA;
229                 t.tv_usec = 0;
230         dalloc_add(query, &t, sl_time_t);
231                 offset += 8;
232         }
233
234         return count;
235 }
236
237 static int spotlight_uuid(DALLOC_CTX *query, const char *buf, int offset, uint encoding)
238 {
239         int count, i;
240     uint64_t query_data64;
241     sl_uuid_t uuid;
242         query_data64 = spotlight_ntoh64(buf, offset, encoding);
243         count = query_data64 >> 32;
244         offset += 8;
245
246         i = 0;
247         while (i++ < count) {
248         memcpy(uuid.sl_uuid, buf + offset, 16);
249         dalloc_add(query, &uuid, sl_uuid_t);
250                 offset += 16;
251         }
252
253         return count;
254 }
255
256 static int spotlight_float(DALLOC_CTX *query, const char *buf, int offset, uint encoding)
257 {
258         int count, i;
259         uint64_t query_data64;
260         double fval;
261
262         query_data64 = spotlight_ntoh64(buf, offset, encoding);
263         count = query_data64 >> 32;
264         offset += 8;
265
266         i = 0;
267         while (i++ < count) {
268                 fval = spotlight_ieee_double(buf, offset, encoding);
269         dalloc_add(query, &fval, double);
270                 offset += 8;
271         }
272
273         return count;
274 }
275
276 static int spotlight_CNID_array(DALLOC_CTX *query, const char *buf, int offset, int length, uint encoding)
277 {
278     EC_INIT;
279         int count;
280         uint64_t query_data64;
281     sl_cnids_t cnids;
282
283     EC_NULL( cnids.ca_cnids = talloc_zero(query, DALLOC_CTX) );
284
285     if (length <= 16)
286         /* that's permitted, it's an empty array */
287         goto EC_CLEANUP;
288     
289         query_data64 = spotlight_ntoh64(buf, offset, encoding);
290         count = query_data64 & 0xffff;
291
292         cnids.ca_unkn1 = (query_data64 & 0xffff0000) >> 16;
293         cnids.ca_unkn2 = query_data64 >> 32;
294
295         offset += 8;
296
297         while (count --) {
298                 query_data64 = spotlight_ntoh64(buf, offset, encoding);
299         dalloc_add(cnids.ca_cnids, &query_data64, uint64_t);
300                 offset += 8;
301         }
302
303     dalloc_add(query, &cnids, sl_cnids_t);
304
305 EC_CLEANUP:
306     EC_EXIT;
307 }
308
309 static const char *spotlight_get_qtype_string(uint64_t query_type)
310 {
311         switch (query_type) {
312         case SQ_TYPE_NULL:
313                 return "null";
314         case SQ_TYPE_COMPLEX:
315                 return "complex";
316         case SQ_TYPE_INT64:
317                 return "int64";
318         case SQ_TYPE_BOOL:
319                 return "bool";
320         case SQ_TYPE_FLOAT:
321                 return "float";
322         case SQ_TYPE_DATA:
323                 return "data";
324         case SQ_TYPE_CNIDS:
325                 return "CNIDs";
326         default:
327                 return "unknown";
328         }
329 }
330
331 static const char *spotlight_get_cpx_qtype_string(uint64_t cpx_query_type)
332 {
333         switch (cpx_query_type) {
334         case SQ_CPX_TYPE_ARRAY:
335                 return "array";
336         case SQ_CPX_TYPE_STRING:
337                 return "string";
338         case SQ_CPX_TYPE_UTF16_STRING:
339                 return "utf-16 string";
340         case SQ_CPX_TYPE_DICT:
341                 return "dictionary";
342         case SQ_CPX_TYPE_CNIDS:
343                 return "CNIDs";
344         case SQ_CPX_TYPE_FILEMETA:
345                 return "FileMeta";
346         default:
347                 return "unknown";
348         }
349 }
350
351 static int spotlight_dissect_loop(DALLOC_CTX *query,
352                                   const char *buf,
353                                   uint offset,
354                                   uint count,
355                                   const uint toc_offset,
356                                   const uint encoding)
357 {
358     EC_INIT;
359         int i, toc_index, query_length;
360     uint subcount, cpx_query_type, cpx_query_count;
361         uint64_t query_data64, query_type;
362         uint unicode_encoding;
363         uint8_t mark_exists;
364     char *p;
365     int padding, slen;
366
367         while (count > 0 && (offset < toc_offset)) {
368                 query_data64 = spotlight_ntoh64(buf, offset, encoding);
369                 query_length = (query_data64 & 0xffff) * 8;
370                 query_type = (query_data64 & 0xffff0000) >> 16;
371                 if (query_length == 0)
372             EC_FAIL;
373
374                 switch (query_type) {
375                 case SQ_TYPE_COMPLEX:
376                         toc_index = (query_data64 >> 32) - 1;
377                         query_data64 = spotlight_ntoh64(buf, toc_offset + toc_index * 8, encoding);
378                         cpx_query_type = (query_data64 & 0xffff0000) >> 16;
379             cpx_query_count = query_data64 >> 32;
380
381             switch (cpx_query_type) {
382                         case SQ_CPX_TYPE_ARRAY: {
383                 sl_array_t *sl_arrary = talloc_zero(query, sl_array_t);
384                 EC_NEG1_LOG( offset = spotlight_dissect_loop(sl_arrary, buf, offset + 8, cpx_query_count, toc_offset, encoding) );
385                 dalloc_add(query, sl_arrary, sl_array_t);
386                 break;
387             }
388
389                         case SQ_CPX_TYPE_DICT: {
390                 sl_dict_t *sl_dict = talloc_zero(query, sl_dict_t);
391                 EC_NEG1_LOG( offset = spotlight_dissect_loop(sl_dict, buf, offset + 8, cpx_query_count, toc_offset, encoding) );
392                 dalloc_add(query, sl_dict, sl_dict_t);
393                 break;
394             }
395             case SQ_CPX_TYPE_STRING:
396                 query_data64 = spotlight_ntoh64(buf, offset + 8, encoding);
397                 query_length += (query_data64 & 0xffff) * 8;
398                 if ((padding = 8 - (query_data64 >> 32)) < 0)
399                     EC_FAIL;
400                 if ((slen = query_length - 16 - padding) < 1)
401                     EC_FAIL;
402                 p = talloc_strndup(query, buf + offset + 16, slen);
403                 dalloc_add(query, &p, char *);
404                 break;
405
406             case SQ_CPX_TYPE_UTF16_STRING:
407                 query_data64 = spotlight_ntoh64(buf, offset + 8, encoding);
408                 query_length += (query_data64 & 0xffff) * 8;
409                 if ((padding = 8 - (query_data64 >> 32)) < 0)
410                     EC_FAIL;
411                 if ((slen = query_length - 16 - padding) < 1)
412                     EC_FAIL;
413
414                 unicode_encoding = spotlight_get_utf16_string_encoding(buf, offset + 16, slen, encoding);
415                 mark_exists = (unicode_encoding & SL_ENC_UTF_16);
416                 unicode_encoding &= ~SL_ENC_UTF_16;
417
418                 EC_NEG1( convert_string_allocate(CH_UCS2, CH_UTF8, buf + offset + (mark_exists ? 18 : 16), slen, &p) );
419                 dalloc_add(query, &p, char *);
420
421                 break;
422             case SQ_CPX_TYPE_FILEMETA:
423                 if (query_length <= 8) {
424                 } else {
425                     EC_NEG1_LOG( dissect_spotlight(query, buf + offset + 16) );
426                 }
427                 break;
428             case SQ_CPX_TYPE_CNIDS:
429                 EC_NEG1_LOG( spotlight_CNID_array(query, buf, offset + 16, query_length, encoding) );
430                 break;
431             } /* switch (cpx_query_type) */
432
433                         count--;
434                         break;
435
436         case SQ_TYPE_NULL: {
437             subcount = query_data64 >> 32;
438             if (subcount > 64)
439                 EC_FAIL;
440             sl_nil_t nil = 0;
441             for (i = 0; i < subcount; i++)
442                 dalloc_add(query, &nil, sl_nil_t);
443             count -= subcount;
444             break;
445         }
446         case SQ_TYPE_BOOL: {
447             sl_bool_t b = query_data64 >> 32;
448             dalloc_add(query, &b, sl_bool_t);
449             count--;
450             break;
451         }
452         case SQ_TYPE_INT64:
453             EC_NEG1_LOG( subcount = spotlight_int64(query, buf, offset, encoding) );
454             count -= subcount;
455             break;
456         case SQ_TYPE_UUID:
457             EC_NEG1_LOG( subcount = spotlight_uuid(query, buf, offset, encoding) );
458             count -= subcount;
459             break;
460         case SQ_TYPE_FLOAT:
461             EC_NEG1_LOG( subcount = spotlight_float(query, buf, offset, encoding) );
462             count -= subcount;
463             break;
464         case SQ_TYPE_DATE:
465             EC_NEG1_LOG( subcount = spotlight_date(query, buf, offset, encoding) );
466             count -= subcount;
467             break;
468         case SQ_TYPE_CNIDS:
469             EC_NEG1_LOG( spotlight_CNID_array(query, buf, offset + 8, query_length, encoding) );
470             break;
471         default:
472             EC_FAIL;
473         }
474
475         offset += query_length;
476     }
477
478 EC_CLEANUP:
479     if (ret != 0) {
480         offset = -1;
481     }
482         return offset;
483 }
484
485 static int dissect_spotlight(DALLOC_CTX *query, const char *buf)
486 {
487     EC_INIT;
488         int encoding, i, toc_entries;
489         uint64_t toc_offset, tquerylen, toc_entry;
490
491         if (strncmp(buf, "md031234", 8) == 0)
492                 encoding = SL_ENC_BIG_ENDIAN;
493         else
494                 encoding = SL_ENC_LITTLE_ENDIAN;
495
496         buf += 8;
497
498         toc_offset = ((spotlight_ntoh64(buf, 0, encoding) >> 32) - 1 ) * 8;
499         if (toc_offset < 0 || (toc_offset > 65000)) {
500         EC_FAIL;
501         }
502
503         buf += 8;
504
505         toc_entries = (int)(spotlight_ntoh64(buf, toc_offset, encoding) & 0xffff);
506
507         EC_NEG1( spotlight_dissect_loop(query, buf, 0, 1, toc_offset + 8, encoding) );
508
509 EC_CLEANUP:
510     EC_EXIT;
511 }
512
513 /**************************************************************************************************
514  * AFP functions
515  **************************************************************************************************/
516 int afp_spotlight_rpc(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
517 {
518     EC_INIT;
519     TALLOC_CTX *tmp_ctx = talloc_new(NULL);
520     uint16_t vid;
521     int cmd;
522     int endianess = SL_ENC_LITTLE_ENDIAN;
523     struct vol      *vol;
524     DALLOC_CTX *query;
525
526     *rbuflen = 0;
527
528     ibuf += 2;
529     ibuflen -= 2;
530
531     vid = SVAL(ibuf, 0);
532     LOG(logtype_default, log_note, "afp_spotlight_rpc(vid: %" PRIu16 ")", vid);
533
534     if ((vol = getvolbyvid(vid)) == NULL) {
535         LOG(logtype_default, log_error, "afp_spotlight_rpc: bad volume id: %" PRIu16 ")", vid);
536         ret = AFPERR_ACCESS;
537         goto EC_CLEANUP;
538     }
539
540     /*    IVAL(ibuf, 2): unknown, always 0x00008004, some flags ? */
541
542     cmd = RIVAL(ibuf, 6);
543     LOG(logtype_default, log_note, "afp_spotlight_rpc(cmd: %d)", cmd);
544
545     /*    IVAL(ibuf, 10: unknown, always 0x00000000 */
546
547         switch (cmd) {
548
549         case SPOTLIGHT_CMD_VOLPATH: {
550         RSIVAL(rbuf, 0, ntohs(vid));
551         RSIVAL(rbuf, 4, 0);
552         int len = strlen(vol->v_path) + 1;
553         strncpy(rbuf + 8, vol->v_path, len);
554         *rbuflen += 8 + len;
555                 break;
556     }
557         case SPOTLIGHT_CMD_FLAGS:
558         RSIVAL(rbuf, 0, 0x0100006b); /* Whatever this value means... flags? */
559         *rbuflen += 4;
560                 break;
561
562         case SPOTLIGHT_CMD_RPC: {
563         DALLOC_CTX *query;
564         EC_NULL( query = talloc_zero(tmp_ctx, DALLOC_CTX) );
565         (void)dissect_spotlight(query, ibuf + 22);
566         dd_dump(query, 0);
567                 break;
568     }
569         }
570
571 EC_CLEANUP:
572     talloc_free(tmp_ctx);
573     if (ret != AFP_OK) {
574         
575     }
576     EC_EXIT;
577 }
578
579 /**************************************************************************************************
580  * Testing
581  **************************************************************************************************/
582
583 #ifdef SPOT_TEST_MAIN
584
585 int main(int argc, char **argv)
586 {
587     EC_INIT;
588     TALLOC_CTX *mem_ctx = talloc_new(NULL);
589     DALLOC_CTX *dd = talloc_zero(mem_ctx, DALLOC_CTX);
590     int64_t i;
591
592     set_processname("spot");
593     setuplog("default:info", "/dev/tty");
594
595     LOG(logtype_default, log_info, "Start");
596
597 #if 0
598     i = 2;
599     dalloc_add(dd, &i, int64_t);
600
601     i = 1;
602     dalloc_add(dd, &i, int64_t);
603
604
605     char *str = talloc_strdup(dd, "hello world");
606     dalloc_add(dd, &str, char *);
607
608     sl_bool_t b = true;
609     dalloc_add(dd, &b, sl_bool_t);
610
611     b = false;
612     dalloc_add(dd, &b, sl_bool_t);
613
614
615     /* add a nested array */
616     DALLOC_CTX *nested = talloc_zero(dd, DALLOC_CTX);
617     i = 3;
618     dalloc_add(nested, &i, int64_t);
619     dalloc_add(dd, nested, DALLOC_CTX);
620
621     /* test an allocated CNID array */
622     uint32_t id = 16;
623     sl_cnids_t *cnids = talloc_zero(dd, sl_cnids_t);
624
625     cnids->ca_cnids = talloc_zero(cnids, DALLOC_CTX);
626
627     cnids->ca_unkn1 = 1;
628     cnids->ca_unkn2 = 2;
629
630     dalloc_add(cnids->ca_cnids, &id, uint32_t);
631     dalloc_add(dd, cnids, sl_cnids_t);
632
633     /* Now the Spotlight types */
634     sl_array_t *sl_arrary = talloc_zero(dd, sl_array_t);
635     i = 1234;
636     dalloc_add(sl_arrary, &i, int64_t);
637
638     sl_dict_t *sl_dict = talloc_zero(dd, sl_dict_t);
639     i = 5678;
640     dalloc_add(sl_dict, &i, int64_t);
641     dalloc_add(sl_arrary, sl_dict, sl_dict_t);
642
643     dalloc_add(dd, sl_arrary, sl_array_t);
644 #endif
645
646     /* now parse a real spotlight packet */
647     char ibuf[8192];
648     char rbuf[8192];
649     int fd;
650     size_t len;
651     DALLOC_CTX *query;
652
653     EC_NULL( query = talloc_zero(mem_ctx, DALLOC_CTX) );
654
655     EC_NEG1_LOG( fd = open("/home/ralph/netatalk/spot/etc/afpd/spotlight-packet.bin", O_RDONLY) );
656     EC_NEG1_LOG( len = read(fd, ibuf, 8192) );
657     EC_NEG1_LOG( dissect_spotlight(query, ibuf + 24) );
658
659     /* Now dump the whole thing */
660     dd_dump(query, 0);
661
662 EC_CLEANUP:
663     if (mem_ctx) {
664         talloc_free(mem_ctx);
665         mem_ctx = NULL;
666     }
667     EC_EXIT;
668 }
669 #endif