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