]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/spotlight.c
6772fceaf2af1048fe2d097d10fb2dac69ca6848
[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 static uint64_t spotlight_ntoh64(const char *buf, int encoding)
75 {
76         if (encoding == SL_ENC_LITTLE_ENDIAN)
77                 return LVAL(buf, 0);
78         else
79         return ntoh64(LVAL(buf, 0));
80 }
81
82 #if 0
83 static gdouble
84 spotlight_ntohieee_double(tvbuff_t *tvb, gint offset, guint encoding)
85 {
86         if (encoding == ENC_LITTLE_ENDIAN)
87                 return tvb_get_letohieee_double(tvb, offset);
88         else
89                 return tvb_get_ntohieee_double(tvb, offset);
90 }
91
92 /*
93 * Returns the UTF-16 string encoding, by checking the 2-byte byte order mark.
94 * If there is no byte order mark, -1 is returned.
95 */
96 static guint
97 spotlight_get_utf16_string_encoding(tvbuff_t *tvb, gint offset, gint query_length, guint encoding) {
98         guint utf16_encoding;
99
100         /* check for byte order mark */
101         utf16_encoding = ENC_BIG_ENDIAN;
102         if (query_length >= 2) {
103                 guint16 byte_order_mark;
104                 if (encoding == ENC_LITTLE_ENDIAN)
105                         byte_order_mark = tvb_get_letohs(tvb, offset);
106                 else
107                         byte_order_mark = tvb_get_ntohs(tvb, offset);
108
109                 if (byte_order_mark == 0xFFFE) {
110                         utf16_encoding = ENC_BIG_ENDIAN | ENC_UTF_16;
111                 }
112                 else if (byte_order_mark == 0xFEFF) {
113                         utf16_encoding = ENC_LITTLE_ENDIAN | ENC_UTF_16;
114                 }
115         }
116
117         return utf16_encoding;
118 }
119
120 static gint
121 spotlight_int64(tvbuff_t *tvb, proto_tree *tree, gint offset, guint encoding)
122 {
123         gint count, i;
124         guint64 query_data64;
125
126         query_data64 = spotlight_ntoh64(tvb, offset, encoding);
127         count = query_data64 >> 32;
128         offset += 8;
129
130         i = 0;
131         while (i++ < count) {
132                 query_data64 = spotlight_ntoh64(tvb, offset, encoding);
133                 proto_tree_add_text(tree, tvb, offset, 8, "int64: 0x%016" G_GINT64_MODIFIER "x", query_data64);
134                 offset += 8;
135         }
136
137         return count;
138 }
139
140 static gint
141 spotlight_date(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gint offset, guint encoding)
142 {
143         gint count, i;
144         guint64 query_data64;
145         nstime_t t;
146
147         query_data64 = spotlight_ntoh64(tvb, offset, encoding);
148         count = query_data64 >> 32;
149         offset += 8;
150
151         if (count > SUBQ_SAFETY_LIM) {
152                 expert_add_info_format(pinfo, tree, PI_MALFORMED, PI_ERROR,
153                                                            "Subquery count (%d) > safety limit (%d)", count, SUBQ_SAFETY_LIM);
154                 return -1;
155         }
156
157         i = 0;
158         while (i++ < count) {
159                 query_data64 = spotlight_ntoh64(tvb, offset, encoding) >> 24;
160                 t.secs = query_data64 - SPOTLIGHT_TIME_DELTA;
161                 t.nsecs = 0;
162                 proto_tree_add_time(tree, hf_afp_spotlight_date, tvb, offset, 8, &t);
163                 offset += 8;
164         }
165
166         return count;
167 }
168
169 static gint
170 spotlight_uuid(tvbuff_t *tvb, proto_tree *tree, gint offset, guint encoding)
171 {
172         gint count, i;
173         guint64 query_data64;
174
175         query_data64 = spotlight_ntoh64(tvb, offset, encoding);
176         count = query_data64 >> 32;
177         offset += 8;
178
179         i = 0;
180         while (i++ < count) {
181                 proto_tree_add_item(tree, hf_afp_spotlight_uuid, tvb, offset, 16, ENC_BIG_ENDIAN);
182                 offset += 16;
183         }
184
185         return count;
186 }
187
188 static gint
189 spotlight_float(tvbuff_t *tvb, proto_tree *tree, gint offset, guint encoding)
190 {
191         gint count, i;
192         guint64 query_data64;
193         gdouble fval;
194
195         query_data64 = spotlight_ntoh64(tvb, offset, encoding);
196         count = query_data64 >> 32;
197         offset += 8;
198
199         i = 0;
200         while (i++ < count) {
201                 fval = spotlight_ntohieee_double(tvb, offset, encoding);
202                 proto_tree_add_text(tree, tvb, offset, 8, "float: %f", fval);
203                 offset += 8;
204         }
205
206         return count;
207 }
208
209 static gint
210 spotlight_CNID_array(tvbuff_t *tvb, proto_tree *tree, gint offset, guint encoding)
211 {
212         gint count;
213         guint64 query_data64;
214         guint16 unknown1;
215         guint32 unknown2;
216
217         query_data64 = spotlight_ntoh64(tvb, offset, encoding);
218         count = query_data64 & 0xffff;
219         unknown1 = (query_data64 & 0xffff0000) >> 16;
220         unknown2 = query_data64 >> 32;
221
222         proto_tree_add_text(tree, tvb, offset + 2, 2, "unknown1: 0x%04" G_GINT16_MODIFIER "x",
223                 unknown1);
224         proto_tree_add_text(tree, tvb, offset + 4, 4, "unknown2: 0x%08" G_GINT32_MODIFIER "x",
225                 unknown2);
226         offset += 8;
227
228
229         while (count --) {
230                 query_data64 = spotlight_ntoh64(tvb, offset, encoding);
231                 proto_tree_add_text(tree, tvb, offset, 8, "CNID: %" G_GINT64_MODIFIER "u",
232                         query_data64);
233                 offset += 8;
234         }
235
236         return 0;
237 }
238
239 static const char *spotlight_get_qtype_string(guint64 query_type)
240 {
241         switch (query_type) {
242         case SQ_TYPE_NULL:
243                 return "null";
244         case SQ_TYPE_COMPLEX:
245                 return "complex";
246         case SQ_TYPE_INT64:
247                 return "int64";
248         case SQ_TYPE_BOOL:
249                 return "bool";
250         case SQ_TYPE_FLOAT:
251                 return "float";
252         case SQ_TYPE_DATA:
253                 return "data";
254         case SQ_TYPE_CNIDS:
255                 return "CNIDs";
256         default:
257                 return "unknown";
258         }
259 }
260
261 static const char *spotlight_get_cpx_qtype_string(guint64 cpx_query_type)
262 {
263         switch (cpx_query_type) {
264         case SQ_CPX_TYPE_ARRAY:
265                 return "array";
266         case SQ_CPX_TYPE_STRING:
267                 return "string";
268         case SQ_CPX_TYPE_UTF16_STRING:
269                 return "utf-16 string";
270         case SQ_CPX_TYPE_DICT:
271                 return "dictionary";
272         case SQ_CPX_TYPE_CNIDS:
273                 return "CNIDs";
274         case SQ_CPX_TYPE_FILEMETA:
275                 return "FileMeta";
276         default:
277                 return "unknown";
278         }
279 }
280
281 static gint
282 spotlight_dissect_query_loop(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gint offset,
283                              guint64 cpx_query_type, gint count, gint toc_offset, guint encoding)
284 {
285         gint i, j;
286         gint subquery_count;
287         gint toc_index;
288         guint64 query_data64;
289         gint query_length;
290         guint64 query_type;
291         guint64 complex_query_type;
292         guint unicode_encoding;
293         guint8 mark_exists;
294
295         proto_item *item_query;
296         proto_tree *sub_tree;
297
298         /*
299          * This loops through a possibly nested query data structure.
300          * The outermost one is always without count and called from
301          * dissect_spotlight() with count = INT_MAX thus the while (...)
302          * loop terminates if (offset >= toc_offset).
303          * If nested structures are found, these will have an encoded element
304          * count which is used in a recursive call to
305          * spotlight_dissect_query_loop as count parameter, thus in this case
306          * the while (...) loop will terminate when count reaches 0.
307          */
308         while ((offset < (toc_offset - 8)) && (count > 0)) {
309                 query_data64 = spotlight_ntoh64(tvb, offset, encoding);
310                 query_length = (query_data64 & 0xffff) * 8;
311                 if (query_length == 0) {
312                         /* XXX - report this as an error */
313                         break;
314                 }
315                 query_type = (query_data64 & 0xffff0000) >> 16;
316
317                 switch (query_type) {
318                 case SQ_TYPE_COMPLEX:
319                         toc_index = (gint)((query_data64 >> 32) - 1);
320                         query_data64 = spotlight_ntoh64(tvb, toc_offset + toc_index * 8, encoding);
321                         complex_query_type = (query_data64 & 0xffff0000) >> 16;
322
323                         switch (complex_query_type) {
324                         case SQ_CPX_TYPE_ARRAY:
325                         case SQ_CPX_TYPE_DICT:
326                                 subquery_count = (gint)(query_data64 >> 32);
327                                 item_query = proto_tree_add_text(tree, tvb, offset, query_length,
328                                                                  "%s, toc index: %u, children: %u",
329                                                                  spotlight_get_cpx_qtype_string(complex_query_type),
330                                                                  toc_index + 1,
331                                                                  subquery_count);
332                                 break;
333                         case SQ_CPX_TYPE_STRING:
334                                 subquery_count = 1;
335                                 query_data64 = spotlight_ntoh64(tvb, offset + 8, encoding);
336                                 query_length = (query_data64 & 0xffff) * 8;
337                                 item_query = proto_tree_add_text(tree, tvb, offset, query_length + 8,
338                                                                  "%s, toc index: %u, string: '%s'",
339                                                                  spotlight_get_cpx_qtype_string(complex_query_type),
340                                                                  toc_index + 1,
341                                                                  tvb_get_ephemeral_string(tvb, offset + 16, query_length - 8));
342                                 break;
343                         case SQ_CPX_TYPE_UTF16_STRING:
344                                 /*
345                                 * This is an UTF-16 string.
346                                 * Dissections show the typical byte order mark 0xFFFE or 0xFEFF, respectively.
347                                 * However the existence of such a mark can not be assumed.
348                                 * If the mark is missing, big endian encoding is assumed.
349                                 */
350
351                                 subquery_count = 1;
352                                 query_data64 = spotlight_ntoh64(tvb, offset + 8, encoding);
353                                 query_length = (query_data64 & 0xffff) * 8;
354
355                                 unicode_encoding = spotlight_get_utf16_string_encoding(tvb, offset + 16, query_length - 8, encoding);
356                                 mark_exists = (unicode_encoding & ENC_UTF_16);
357                                 unicode_encoding &= ~ENC_UTF_16;
358
359                                 item_query = proto_tree_add_text(tree, tvb, offset, query_length + 8,
360                                                                  "%s, toc index: %u, utf-16 string: '%s'",
361                                                                  spotlight_get_cpx_qtype_string(complex_query_type),
362                                                                  toc_index + 1,
363                                                                  tvb_get_ephemeral_unicode_string(tvb, offset + (mark_exists ? 18 : 16),
364                                                                  query_length - (mark_exists? 10 : 8), unicode_encoding));
365                                 break;
366                         default:
367                                 subquery_count = 1;
368                                 item_query = proto_tree_add_text(tree, tvb, offset, query_length,
369                                                                  "type: %s (%s), toc index: %u, children: %u",
370                                                                  spotlight_get_qtype_string(query_type),
371                                                                  spotlight_get_cpx_qtype_string(complex_query_type),
372                                                                  toc_index + 1,
373                                                                  subquery_count);
374                                 break;
375                         }
376
377                         sub_tree = proto_item_add_subtree(item_query, ett_afp_spotlight_query_line);
378                         offset += 8;
379                         offset = spotlight_dissect_query_loop(tvb, pinfo, sub_tree, offset, complex_query_type, subquery_count, toc_offset, encoding);
380                         count--;
381                         break;
382                 case SQ_TYPE_NULL:
383                         subquery_count = (gint)(query_data64 >> 32);
384                         if (subquery_count > count) {
385                                 item_query = proto_tree_add_text(tree, tvb, offset, query_length, "null");
386                                 expert_add_info_format(pinfo, item_query, PI_MALFORMED, PI_ERROR,
387                                         "Subquery count (%d) > query count (%d)", subquery_count, count);
388                                 count = 0;
389                         } else if (subquery_count > 20) {
390                                 item_query = proto_tree_add_text(tree, tvb, offset, query_length, "null");
391                                 expert_add_info_format(pinfo, item_query, PI_PROTOCOL, PI_WARN,
392                                         "Abnormal number of subqueries (%d)", subquery_count);
393                                 count -= subquery_count;
394                         } else {
395                                 for (i = 0; i < subquery_count; i++, count--)
396                                         proto_tree_add_text(tree, tvb, offset, query_length, "null");
397                         }
398                         offset += query_length;
399                         break;
400                 case SQ_TYPE_BOOL:
401                         proto_tree_add_text(tree, tvb, offset, query_length, "bool: %s",
402                                                          (query_data64 >> 32) ? "true" : "false");
403                         count--;
404                         offset += query_length;
405                         break;
406                 case SQ_TYPE_INT64:
407                         item_query = proto_tree_add_text(tree, tvb, offset, 8, "int64");
408                         sub_tree = proto_item_add_subtree(item_query, ett_afp_spotlight_query_line);
409                         j = spotlight_int64(tvb, sub_tree, offset, encoding);
410                         count -= j;
411                         offset += query_length;
412                         break;
413                 case SQ_TYPE_UUID:
414                         item_query = proto_tree_add_text(tree, tvb, offset, 8, "UUID");
415                         sub_tree = proto_item_add_subtree(item_query, ett_afp_spotlight_query_line);
416                         j = spotlight_uuid(tvb, sub_tree, offset, encoding);
417                         count -= j;
418                         offset += query_length;
419                         break;
420                 case SQ_TYPE_FLOAT:
421                         item_query = proto_tree_add_text(tree, tvb, offset, 8, "float");
422                         sub_tree = proto_item_add_subtree(item_query, ett_afp_spotlight_query_line);
423                         j = spotlight_float(tvb, sub_tree, offset, encoding);
424                         count -= j;
425                         offset += query_length;
426                         break;
427                 case SQ_TYPE_DATA:
428                         switch (cpx_query_type) {
429                         case SQ_CPX_TYPE_STRING:
430                                 proto_tree_add_text(tree, tvb, offset, query_length, "string: '%s'",
431                                                     tvb_get_ephemeral_string(tvb, offset + 8, query_length - 8));
432                                 break;
433                         case SQ_CPX_TYPE_UTF16_STRING: {
434                                 /* description see above */
435                                 unicode_encoding = spotlight_get_utf16_string_encoding(tvb, offset + 8, query_length, encoding);
436                                 mark_exists = (unicode_encoding & ENC_UTF_16);
437                                 unicode_encoding &= ~ENC_UTF_16;
438
439                                 proto_tree_add_text(tree, tvb, offset, query_length, "utf-16 string: '%s'",
440                                                     tvb_get_ephemeral_unicode_string(tvb, offset + (mark_exists ? 10 : 8),
441                                                                 query_length - (mark_exists? 10 : 8), unicode_encoding));
442                                 break;
443                         }
444                         case SQ_CPX_TYPE_FILEMETA:
445                                 if (query_length <= 8) {
446                                         /* item_query = */ proto_tree_add_text(tree, tvb, offset, query_length, "filemeta (empty)");
447                                 } else {
448                                         item_query = proto_tree_add_text(tree, tvb, offset, query_length, "filemeta");
449                                         sub_tree = proto_item_add_subtree(item_query, ett_afp_spotlight_query_line);
450                                         (void)dissect_spotlight(tvb, pinfo, sub_tree, offset + 8);
451                                 }
452                                 break;
453                         }
454                         count--;
455                         offset += query_length;
456                         break;
457                 case SQ_TYPE_CNIDS:
458                         if (query_length <= 8) {
459                                 /* item_query = */ proto_tree_add_text(tree, tvb, offset, query_length, "CNID Array (empty)");
460                         } else {
461                                 item_query = proto_tree_add_text(tree, tvb, offset, query_length, "CNID Array");
462                                 sub_tree = proto_item_add_subtree(item_query, ett_afp_spotlight_query_line);
463                                 spotlight_CNID_array(tvb, sub_tree, offset + 8, encoding);
464                         }
465                         count--;
466                         offset += query_length;
467                         break;
468                 case SQ_TYPE_DATE:
469                         if ((j = spotlight_date(tvb, pinfo, tree, offset, encoding)) == -1)
470                                 return offset;
471                         count -= j;
472                         offset += query_length;
473                         break;
474                 default:
475                         proto_tree_add_text(tree, tvb, offset, query_length, "type: %s",
476                                                          spotlight_get_qtype_string(query_type));
477                         count--;
478                         offset += query_length;
479                         break;
480                 }
481         }
482
483         return offset;
484 }
485
486 static gint
487 dissect_spotlight(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gint offset)
488 {
489         guint encoding;
490         gint i;
491         guint64 toc_offset;
492         guint64 querylen;
493         gint toc_entries;
494         guint64 toc_entry;
495
496         proto_item *item_queries_data;
497         proto_tree *sub_tree_queries;
498         proto_item *item_toc;
499         proto_tree *sub_tree_toc;
500
501         if (strncmp(tvb_get_ephemeral_string(tvb, offset, 8), "md031234", 8) == 0)
502                 encoding = ENC_BIG_ENDIAN;
503         else
504                 encoding = ENC_LITTLE_ENDIAN;
505         proto_tree_add_text(tree,
506                             tvb,
507                             offset,
508                             8,
509                             "Endianess: %s",
510                             encoding == ENC_BIG_ENDIAN ?
511                             "Big Endian" : "Litte Endian");
512         offset += 8;
513
514         toc_offset = (spotlight_ntoh64(tvb, offset, encoding) >> 32) * 8;
515         if (toc_offset < 8) {
516                 proto_tree_add_text(tree,
517                                     tvb,
518                                     offset,
519                                     8,
520                                     "ToC Offset: %" G_GINT64_MODIFIER "u < 8 (bogus)",
521                                     toc_offset);
522                 return -1;
523         }
524         toc_offset -= 8;
525         if (offset + toc_offset + 8 > G_MAXINT) {
526                 proto_tree_add_text(tree,
527                                     tvb,
528                                     offset,
529                                     8,
530                                     "ToC Offset: %" G_GINT64_MODIFIER "u > %u (bogus)",
531                                     toc_offset,
532                                     G_MAXINT - 8 - offset);
533                 return -1;
534         }
535         querylen = (spotlight_ntoh64(tvb, offset, encoding) & 0xffffffff) * 8;
536         if (querylen < 8) {
537                 proto_tree_add_text(tree,
538                                     tvb,
539                                     offset,
540                                     8,
541                                     "ToC Offset: %" G_GINT64_MODIFIER "u Bytes, Query length: %" G_GINT64_MODIFIER "u < 8 (bogus)",
542                                     toc_offset,
543                                     querylen);
544                 return -1;
545         }
546         querylen -= 8;
547         if (querylen > G_MAXINT) {
548                 proto_tree_add_text(tree,
549                                     tvb,
550                                     offset,
551                                     8,
552                                     "ToC Offset: %" G_GINT64_MODIFIER "u Bytes, Query length: %" G_GINT64_MODIFIER "u > %u (bogus)",
553                                     toc_offset,
554                                     querylen,
555                                     G_MAXINT);
556                 return -1;
557         }
558         proto_tree_add_text(tree,
559                             tvb,
560                             offset,
561                             8,
562                             "ToC Offset: %" G_GINT64_MODIFIER "u Bytes, Query length: %" G_GINT64_MODIFIER "u Bytes",
563                             toc_offset,
564                             querylen);
565         offset += 8;
566
567         toc_entries = (gint)(spotlight_ntoh64(tvb, offset + (gint)toc_offset, encoding) & 0xffff);
568
569         item_queries_data = proto_tree_add_text(tree,
570                                                 tvb,
571                                                 offset,
572                                                 (gint)toc_offset,
573                                                 "Spotlight RPC data");
574         sub_tree_queries = proto_item_add_subtree(item_queries_data, ett_afp_spotlight_queries);
575
576         /* Queries */
577         offset = spotlight_dissect_query_loop(tvb, pinfo, sub_tree_queries, offset, SQ_CPX_TYPE_ARRAY, INT_MAX, offset + (gint)toc_offset + 8, encoding);
578
579         /* ToC */
580         if (toc_entries < 1) {
581                 proto_tree_add_text(tree,
582                                     tvb,
583                                     offset,
584                                     (gint)querylen - (gint)toc_offset,
585                                     "Complex types ToC (%u < 1 - bogus)",
586                                     toc_entries);
587                 return -1;
588         }
589         toc_entries -= 1;
590         item_toc = proto_tree_add_text(tree,
591                                        tvb,
592                                        offset,
593                                        (gint)querylen - (gint)toc_offset,
594                                        "Complex types ToC (%u entries)",
595                                        toc_entries);
596         sub_tree_toc = proto_item_add_subtree(item_toc, ett_afp_spotlight_toc);
597         proto_tree_add_text(sub_tree_toc, tvb, offset, 2, "Number of entries (%u)", toc_entries);
598         proto_tree_add_text(sub_tree_toc, tvb, offset + 2, 2, "unknown");
599         proto_tree_add_text(sub_tree_toc, tvb, offset + 4, 4, "unknown");
600
601         offset += 8;
602         for (i = 0; i < toc_entries; i++, offset += 8) {
603                 toc_entry = spotlight_ntoh64(tvb, offset, encoding);
604                 if ((((toc_entry & 0xffff0000) >> 16) == SQ_CPX_TYPE_ARRAY)
605                     || (((toc_entry & 0xffff0000) >> 16) == SQ_CPX_TYPE_DICT)) {
606                         proto_tree_add_text(sub_tree_toc,
607                                             tvb,
608                                             offset,
609                                             8,
610                                             "%u: count: %" G_GINT64_MODIFIER "u, type: %s, offset: %" G_GINT64_MODIFIER "u",
611                                             i+1,
612                                             toc_entry >> 32,
613                                             spotlight_get_cpx_qtype_string((toc_entry & 0xffff0000) >> 16),
614                                             (toc_entry & 0xffff) * 8);
615                 } else if ((((toc_entry & 0xffff0000) >> 16) == SQ_CPX_TYPE_STRING)
616                         || (((toc_entry & 0xffff0000) >> 16) == SQ_CPX_TYPE_UTF16_STRING)) {
617                         proto_tree_add_text(sub_tree_toc,
618                                             tvb,
619                                             offset,
620                                             8,
621                                             "%u: pad byte count: %" G_GINT64_MODIFIER "x, type: %s, offset: %" G_GINT64_MODIFIER "u",
622                                             i+1,
623                                             8 - (toc_entry >> 32),
624                                             spotlight_get_cpx_qtype_string((toc_entry & 0xffff0000) >> 16),
625                                             (toc_entry & 0xffff) * 8);
626                 }
627                 else {
628                         proto_tree_add_text(sub_tree_toc,
629                                             tvb,
630                                             offset,
631                                             8,
632                                             "%u: unknown: 0x%08" G_GINT64_MODIFIER "x, type: %s, offset: %" G_GINT64_MODIFIER "u",
633                                             i+1,
634                                             toc_entry >> 32,
635                                             spotlight_get_cpx_qtype_string((toc_entry & 0xffff0000) >> 16),
636                                             (toc_entry & 0xffff) * 8);
637                 }
638
639
640         }
641
642         return offset;
643 }
644 #endif
645
646 static DALLOC_CTX *unpack_spotlight(TALLOC_CTX *mem_ctx, char *ibuf, size_t ibuflen)
647 {
648     EC_INIT;
649         int len;
650     DALLOC_CTX *query;
651
652     EC_NULL_LOG( query = talloc_zero(mem_ctx, DALLOC_CTX) );
653
654     ibuf++;
655     ibuflen--;
656
657
658 EC_CLEANUP:
659     if (ret != 0) {
660         talloc_free(query);
661         query = NULL;
662     }
663         return query;
664 }
665
666 /**************************************************************************************************
667  * AFP functions
668  **************************************************************************************************/
669 int afp_spotlight_rpc(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
670 {
671     EC_INIT;
672     TALLOC_CTX *tmp_ctx = talloc_new(NULL);
673     uint16_t vid;
674     int cmd;
675     int endianess = SL_ENC_LITTLE_ENDIAN;
676     struct vol      *vol;
677
678     *rbuflen = 0;
679
680     ibuf += 2;
681     ibuflen -= 2;
682
683     vid = SVAL(ibuf, 0);
684     LOG(logtype_default, log_note, "afp_spotlight_rpc(vid: %" PRIu16 ")", vid);
685
686     if ((vol = getvolbyvid(vid)) == NULL) {
687         LOG(logtype_default, log_error, "afp_spotlight_rpc: bad volume id: %" PRIu16 ")", vid);
688         ret = AFPERR_ACCESS;
689         goto EC_CLEANUP;
690     }
691
692     /*    IVAL(ibuf, 2): unknown, always 0x00008004, some flags ? */
693
694     cmd = RIVAL(ibuf, 6);
695     LOG(logtype_default, log_note, "afp_spotlight_rpc(cmd: %d)", cmd);
696
697     /*    IVAL(ibuf, 10: unknown, always 0x00000000 */
698
699         switch (cmd) {
700
701         case SPOTLIGHT_CMD_VOLPATH: {
702         RSIVAL(rbuf, 0, ntohs(vid));
703         RSIVAL(rbuf, 4, 0);
704         int len = strlen(vol->v_path) + 1;
705         strncpy(rbuf + 8, vol->v_path, len);
706         *rbuflen += 8 + len;
707                 break;
708     }
709         case SPOTLIGHT_CMD_FLAGS:
710         RSIVAL(rbuf, 0, 0x0100006b); /* Whatever this value means... flags? */
711         *rbuflen += 4;
712                 break;
713
714         case SPOTLIGHT_CMD_RPC:
715         /* IVAL(buf, 14): our reply in SPOTLIGHT_CMD_FLAGS */
716         /* IVAL(buf, 18): length */
717         /* IVAL(buf, 22): endianess, ignored, we assume little endian */
718                 break;
719         }
720
721 EC_CLEANUP:
722     talloc_free(tmp_ctx);
723     if (ret != AFP_OK) {
724         
725     }
726     EC_EXIT;
727 }
728
729 /**************************************************************************************************
730  * Testing
731  **************************************************************************************************/
732
733 #ifdef SPOT_TEST_MAIN
734
735 static const char *neststrings[] = {
736     "",
737     "    ",
738     "        ",
739     "            ",
740     "                ",
741     "                    ",
742     "                        "
743 };
744
745 static int dd_dump(DALLOC_CTX *dd, int nestinglevel)
746 {
747     const char *type;
748
749     printf("%sArray(#%d): {\n", neststrings[nestinglevel], talloc_array_length(dd->dd_talloc_array));
750
751     for (int n = 0; n < talloc_array_length(dd->dd_talloc_array); n++) {
752
753         type = talloc_get_name(dd->dd_talloc_array[n]);
754
755         if (STRCMP(type, ==, "int64_t")) {
756             int64_t i;
757             memcpy(&i, dd->dd_talloc_array[n], sizeof(int64_t));
758             printf("%s%d:\t%" PRId64 "\n", neststrings[nestinglevel + 1], n, i);
759         } else if (STRCMP(type, ==, "uint32_t")) {
760             uint32_t i;
761             memcpy(&i, dd->dd_talloc_array[n], sizeof(uint32_t));
762             printf("%s%d:\t%" PRIu32 "\n", neststrings[nestinglevel + 1], n, i);
763         } else if (STRCMP(type, ==, "char *")) {
764             char *s;
765             memcpy(&s, dd->dd_talloc_array[n], sizeof(char *));
766             printf("%s%d:\t%s\n", neststrings[nestinglevel + 1], n, s);
767         } else if (STRCMP(type, ==, "_Bool")) {
768             bool bl;
769             memcpy(&bl, dd->dd_talloc_array[n], sizeof(bool));
770             printf("%s%d:\t%s\n", neststrings[nestinglevel + 1], n, bl ? "true" : "false");
771         } else if (STRCMP(type, ==, "dd_t")) {
772             DALLOC_CTX *nested;
773             memcpy(&nested, dd->dd_talloc_array[n], sizeof(DALLOC_CTX *));
774             dd_dump(nested, nestinglevel + 1);
775         } else if (STRCMP(type, ==, "cnid_array_t")) {
776             cnid_array_t *cnids;
777             memcpy(&cnids, dd->dd_talloc_array[n], sizeof(cnid_array_t *));
778             printf("%s%d:\tunkn1: %" PRIu16 ", unkn2: %" PRIu32,
779                    neststrings[nestinglevel + 1], n, cnids->ca_unkn1, cnids->ca_unkn2);
780             if (cnids->ca_cnids)
781                 dd_dump(cnids->ca_cnids, nestinglevel + 1);
782         }
783     }
784     printf("%s}\n", neststrings[nestinglevel]);
785 }
786
787 #include <stdarg.h>
788
789 int main(int argc, char **argv)
790 {
791     TALLOC_CTX *mem_ctx = talloc_new(NULL);
792     DALLOC_CTX *dd = talloc_zero(mem_ctx, DALLOC_CTX);
793     int64_t i;
794
795     set_processname("spot");
796     setuplog("default:info", "/dev/tty");
797
798     LOG(logtype_default, log_info, "Start");
799
800     i = 2;
801     dalloc_add(dd, &i, int64_t);
802
803     i = 1;
804     dalloc_add(dd, &i, int64_t);
805
806
807     char *str = talloc_strdup(dd, "hello world");
808     dalloc_add(dd, &str, char *);
809
810     bool b = true;
811     dalloc_add(dd, &b, bool);
812
813     b = false;
814     dalloc_add(dd, &b, bool);
815
816
817     /* add a nested array */
818     DALLOC_CTX *nested = talloc_zero(dd, DALLOC_CTX);
819     i = 3;
820     dalloc_add(nested, &i, int64_t);
821     dalloc_add(dd, &nested, DALLOC_CTX);
822
823     /* test a CNID array */
824     uint32_t id = 16;
825     cnid_array_t *cnids = talloc_zero(dd, cnid_array_t);
826
827     cnids->ca_cnids = talloc_zero(cnids, DALLOC_CTX);
828
829     cnids->ca_unkn1 = 1;
830     cnids->ca_unkn2 = 2;
831
832     dalloc_add(cnids->ca_cnids, &id, uint32_t);
833     dalloc_add(dd, &cnids, cnid_array_t);
834
835     dd_dump(dd, 0);
836
837     talloc_free(mem_ctx);
838     return 0;
839 }
840 #endif