2 Copyright (c) 2012 Frank Lahm <franklahm@gmail.com>
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.
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.
17 #endif /* HAVE_CONFIG_H */
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>
36 #include "spotlight.h"
38 /**************************************************************************************************
39 * RPC data marshalling and unmarshalling
40 **************************************************************************************************/
42 /* FPSpotlightRPC subcommand codes */
43 #define SPOTLIGHT_CMD_FLAGS 2
44 #define SPOTLIGHT_CMD_RPC 3
45 #define SPOTLIGHT_CMD_VOLPATH 4
47 /* Spotlight epoch is UNIX epoch minus SPOTLIGHT_TIME_DELTA */
48 #define SPOTLIGHT_TIME_DELTA INT64_C(280878921600U)
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
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
67 #define SUBQ_SAFETY_LIM 20
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
74 /* Forward declarations */
75 static int dissect_spotlight(TALLOC_CTX *mem_ctx, const char *buf);
77 static double spotlight_ieee_double(const char *buf, int offset, uint encoding)
84 if (encoding == SL_ENC_LITTLE_ENDIAN) {
85 #ifdef WORDS_BIGENDIAN
86 ieee_fp_union.w[0] = IVAL(buf, offset + 4);
87 ieee_fp_union.w[1] = IVAL(buf, offset);
89 ieee_fp_union.w[0] = IVAL(buf, offset);
90 ieee_fp_union.w[1] = IVAL(buf, offset + 4);
92 return ieee_fp_union.d;
94 #ifdef WORDS_BIGENDIAN
95 ieee_fp_union.w[0] = RIVAL(buf, offset);
96 ieee_fp_union.w[1] = RIVAL(buf, offset + 4);
98 ieee_fp_union.w[0] = RIVAL(buf, offset + 4);
99 ieee_fp_union.w[1] = RIVAL(buf, offset);
101 return ieee_fp_union.d;
105 static uint64_t spotlight_ntoh64(const char *buf, int off, uint encoding)
107 if (encoding == SL_ENC_LITTLE_ENDIAN)
108 return LVAL(buf, off);
110 return ntoh64(LVAL(buf, off));
114 * Returns the UTF-16 string encoding, by checking the 2-byte byte order mark.
115 * If there is no byte order mark, -1 is returned.
117 static uint spotlight_get_utf16_string_encoding(const char *buf, int offset, int query_length, uint encoding) {
120 /* check for byte order mark */
121 utf16_encoding = SL_ENC_BIG_ENDIAN;
122 if (query_length >= 2) {
123 uint16_t byte_order_mark;
124 if (encoding == SL_ENC_LITTLE_ENDIAN)
125 byte_order_mark = SVAL(buf, offset);
127 byte_order_mark = RSVAL(buf, offset);
129 if (byte_order_mark == 0xFFFE) {
130 utf16_encoding = SL_ENC_BIG_ENDIAN | SL_ENC_UTF_16;
132 else if (byte_order_mark == 0xFEFF) {
133 utf16_encoding = SL_ENC_LITTLE_ENDIAN | SL_ENC_UTF_16;
137 return utf16_encoding;
140 static int spotlight_int64(DALLOC_CTX *query, const char *buf, int offset, uint encoding)
143 uint64_t query_data64;
145 query_data64 = spotlight_ntoh64(buf, offset, encoding);
146 count = query_data64 >> 32;
150 while (i++ < count) {
151 query_data64 = spotlight_ntoh64(buf, offset, encoding);
152 dalloc_add(query, &query_data64, uint64_t);
159 static int spotlight_date(DALLOC_CTX *query, const char *buf, int offset, uint encoding)
162 uint64_t query_data64;
165 query_data64 = spotlight_ntoh64(buf, offset, encoding);
166 count = query_data64 >> 32;
170 while (i++ < count) {
171 query_data64 = spotlight_ntoh64(buf, offset, encoding) >> 24;
172 t.tv_sec = query_data64 - SPOTLIGHT_TIME_DELTA;
174 dalloc_add(query, &t, sl_time_t);
181 static int spotlight_uuid(DALLOC_CTX *query, const char *buf, int offset, uint encoding)
184 uint64_t query_data64;
186 query_data64 = spotlight_ntoh64(buf, offset, encoding);
187 count = query_data64 >> 32;
191 while (i++ < count) {
192 memcpy(uuid.sl_uuid, buf + offset, 16);
193 dalloc_add(query, &uuid, sl_uuid_t);
200 static int spotlight_float(DALLOC_CTX *query, const char *buf, int offset, uint encoding)
203 uint64_t query_data64;
206 query_data64 = spotlight_ntoh64(buf, offset, encoding);
207 count = query_data64 >> 32;
211 while (i++ < count) {
212 fval = spotlight_ieee_double(buf, offset, encoding);
213 dalloc_add(query, &fval, double);
220 static int spotlight_CNID_array(DALLOC_CTX *query, const char *buf, int offset, int length, uint encoding)
224 uint64_t query_data64;
227 EC_NULL( cnids.ca_cnids = talloc_zero(query, DALLOC_CTX) );
230 /* that's permitted, it's an empty array */
233 query_data64 = spotlight_ntoh64(buf, offset, encoding);
234 count = query_data64 & 0xffff;
236 cnids.ca_unkn1 = (query_data64 & 0xffff0000) >> 16;
237 cnids.ca_unkn2 = query_data64 >> 32;
242 query_data64 = spotlight_ntoh64(buf, offset, encoding);
243 dalloc_add(cnids.ca_cnids, &query_data64, uint64_t);
247 dalloc_add(query, &cnids, sl_cnids_t);
253 static const char *spotlight_get_qtype_string(uint64_t query_type)
255 switch (query_type) {
258 case SQ_TYPE_COMPLEX:
275 static const char *spotlight_get_cpx_qtype_string(uint64_t cpx_query_type)
277 switch (cpx_query_type) {
278 case SQ_CPX_TYPE_ARRAY:
280 case SQ_CPX_TYPE_STRING:
282 case SQ_CPX_TYPE_UTF16_STRING:
283 return "utf-16 string";
284 case SQ_CPX_TYPE_DICT:
286 case SQ_CPX_TYPE_CNIDS:
288 case SQ_CPX_TYPE_FILEMETA:
295 static int spotlight_dissect_loop(DALLOC_CTX *query,
299 const uint toc_offset,
303 int i, toc_index, query_length;
304 uint subcount, cpx_query_type, cpx_query_count;
305 uint64_t query_data64, query_type;
306 uint unicode_encoding;
310 while (count > 0 && (offset < toc_offset)) {
311 query_data64 = spotlight_ntoh64(buf, offset, encoding);
312 query_length = (query_data64 & 0xffff) * 8;
313 query_type = (query_data64 & 0xffff0000) >> 16;
314 if (query_length == 0)
317 switch (query_type) {
318 case SQ_TYPE_COMPLEX:
319 toc_index = (query_data64 >> 32) - 1;
320 query_data64 = spotlight_ntoh64(buf, toc_offset + toc_index * 8, encoding);
321 query_length += (query_data64 & 0xffff) * 8;
322 cpx_query_type = (query_data64 & 0xffff0000) >> 16;
323 cpx_query_count = query_data64 >> 32;
325 switch (cpx_query_type) {
326 case SQ_CPX_TYPE_ARRAY:
327 EC_NEG1_LOG( spotlight_dissect_loop(query, buf, offset + 8, cpx_query_count, toc_offset, encoding) );
329 case SQ_CPX_TYPE_DICT:
330 EC_NEG1_LOG( spotlight_dissect_loop(query, buf, offset + 8, cpx_query_count, toc_offset, encoding) );
332 case SQ_CPX_TYPE_STRING:
333 p = buf + offset + 16;
334 dalloc_add(query, &p, char *);
336 case SQ_CPX_TYPE_UTF16_STRING:
337 unicode_encoding = spotlight_get_utf16_string_encoding(buf, offset + 16, query_length, encoding);
338 mark_exists = (unicode_encoding & SL_ENC_UTF_16);
339 unicode_encoding &= ~SL_ENC_UTF_16;
341 case SQ_CPX_TYPE_FILEMETA:
342 if (query_length <= 8) {
344 EC_NEG1_LOG( dissect_spotlight(query, buf + offset + 16) );
347 case SQ_CPX_TYPE_CNIDS:
348 EC_NEG1_LOG( spotlight_CNID_array(query, buf, offset + 16, query_length, encoding) );
350 } /* switch (cpx_query_type) */
356 subcount = query_data64 >> 32;
360 for (i = 0; i < subcount; i++)
361 dalloc_add(query, &nil, sl_nil_t);
366 sl_bool_t b = query_data64 >> 32;
367 dalloc_add(query, &b, sl_bool_t);
372 EC_NEG1_LOG( subcount = spotlight_int64(query, buf, offset, encoding) );
376 EC_NEG1_LOG( subcount = spotlight_uuid(query, buf, offset, encoding) );
380 EC_NEG1_LOG( subcount = spotlight_float(query, buf, offset, encoding) );
384 EC_NEG1_LOG( subcount = spotlight_date(query, buf, offset, encoding) );
388 EC_NEG1_LOG( spotlight_CNID_array(query, buf, offset + 8, query_length, encoding) );
394 offset += query_length;
404 static int dissect_spotlight(TALLOC_CTX *mem_ctx, const char *buf)
407 int encoding, i, toc_entries;
408 uint64_t toc_offset, tquerylen, toc_entry;
411 if (strncmp(buf, "md031234", 8) == 0)
412 encoding = SL_ENC_BIG_ENDIAN;
414 encoding = SL_ENC_LITTLE_ENDIAN;
418 toc_offset = ((spotlight_ntoh64(buf, 0, encoding) >> 32) - 1 ) * 8;
419 if (toc_offset < 0 || (toc_offset > 65000)) {
425 toc_entries = (int)(spotlight_ntoh64(buf, toc_offset, encoding) & 0xffff);
427 EC_NULL( query = talloc_zero(mem_ctx, DALLOC_CTX) );
428 EC_NEG1( spotlight_dissect_loop(query, buf, 0, 1, toc_offset + 8, encoding) );
434 /**************************************************************************************************
436 **************************************************************************************************/
437 int afp_spotlight_rpc(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
440 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
443 int endianess = SL_ENC_LITTLE_ENDIAN;
452 LOG(logtype_default, log_note, "afp_spotlight_rpc(vid: %" PRIu16 ")", vid);
454 if ((vol = getvolbyvid(vid)) == NULL) {
455 LOG(logtype_default, log_error, "afp_spotlight_rpc: bad volume id: %" PRIu16 ")", vid);
460 /* IVAL(ibuf, 2): unknown, always 0x00008004, some flags ? */
462 cmd = RIVAL(ibuf, 6);
463 LOG(logtype_default, log_note, "afp_spotlight_rpc(cmd: %d)", cmd);
465 /* IVAL(ibuf, 10: unknown, always 0x00000000 */
469 case SPOTLIGHT_CMD_VOLPATH: {
470 RSIVAL(rbuf, 0, ntohs(vid));
472 int len = strlen(vol->v_path) + 1;
473 strncpy(rbuf + 8, vol->v_path, len);
477 case SPOTLIGHT_CMD_FLAGS:
478 RSIVAL(rbuf, 0, 0x0100006b); /* Whatever this value means... flags? */
482 case SPOTLIGHT_CMD_RPC:
483 (void)dissect_spotlight(tmp_ctx, ibuf + 22);
488 talloc_free(tmp_ctx);
495 /**************************************************************************************************
497 **************************************************************************************************/
499 #ifdef SPOT_TEST_MAIN
501 static const char *neststrings[] = {
511 static int dd_dump(DALLOC_CTX *dd, int nestinglevel)
515 printf("%s%s(#%d): {\n", neststrings[nestinglevel], talloc_get_name(dd), talloc_array_length(dd->dd_talloc_array));
517 for (int n = 0; n < talloc_array_length(dd->dd_talloc_array); n++) {
519 type = talloc_get_name(dd->dd_talloc_array[n]);
521 if (STRCMP(type, ==, "DALLOC_CTX")
522 || STRCMP(type, ==, "sl_array_t")
523 || STRCMP(type, ==, "sl_dict_t")) {
524 dd_dump(dd->dd_talloc_array[n], nestinglevel + 1);
525 } else if (STRCMP(type, ==, "int64_t")) {
527 memcpy(&i, dd->dd_talloc_array[n], sizeof(int64_t));
528 printf("%s%d:\t%" PRId64 "\n", neststrings[nestinglevel + 1], n, i);
529 } else if (STRCMP(type, ==, "uint32_t")) {
531 memcpy(&i, dd->dd_talloc_array[n], sizeof(uint32_t));
532 printf("%s%d:\t%" PRIu32 "\n", neststrings[nestinglevel + 1], n, i);
533 } else if (STRCMP(type, ==, "char *")) {
535 memcpy(&s, dd->dd_talloc_array[n], sizeof(char *));
536 printf("%s%d:\t%s\n", neststrings[nestinglevel + 1], n, s);
537 } else if (STRCMP(type, ==, "sl_bool_t")) {
539 memcpy(&bl, dd->dd_talloc_array[n], sizeof(sl_bool_t));
540 printf("%s%d:\t%s\n", neststrings[nestinglevel + 1], n, bl ? "true" : "false");
541 } else if (STRCMP(type, ==, "sl_cnids_t")) {
543 memcpy(&cnids, dd->dd_talloc_array[n], sizeof(sl_cnids_t));
544 printf("%s%d:\tunkn1: %" PRIu16 ", unkn2: %" PRIu32,
545 neststrings[nestinglevel + 1], n, cnids.ca_unkn1, cnids.ca_unkn2);
547 dd_dump(cnids.ca_cnids, nestinglevel + 1);
550 printf("%s}\n", neststrings[nestinglevel]);
555 int main(int argc, char **argv)
557 TALLOC_CTX *mem_ctx = talloc_new(NULL);
558 DALLOC_CTX *dd = talloc_zero(mem_ctx, DALLOC_CTX);
561 set_processname("spot");
562 setuplog("default:info", "/dev/tty");
564 LOG(logtype_default, log_info, "Start");
567 dalloc_add(dd, &i, int64_t);
570 dalloc_add(dd, &i, int64_t);
573 char *str = talloc_strdup(dd, "hello world");
574 dalloc_add(dd, &str, char *);
577 dalloc_add(dd, &b, sl_bool_t);
580 dalloc_add(dd, &b, sl_bool_t);
583 /* add a nested array */
584 DALLOC_CTX *nested = talloc_zero(dd, DALLOC_CTX);
586 dalloc_add(nested, &i, int64_t);
587 dalloc_add(dd, nested, DALLOC_CTX);
589 /* test an allocated CNID array */
591 sl_cnids_t *cnids = talloc_zero(dd, sl_cnids_t);
593 cnids->ca_cnids = talloc_zero(cnids, DALLOC_CTX);
598 dalloc_add(cnids->ca_cnids, &id, uint32_t);
599 dalloc_add(dd, cnids, sl_cnids_t);
601 /* Now the Spotlight types */
602 sl_array_t *sl_arrary = talloc_zero(dd, sl_array_t);
604 dalloc_add(sl_arrary, &i, int64_t);
606 sl_dict_t *sl_dict = talloc_zero(dd, sl_dict_t);
608 dalloc_add(sl_dict, &i, int64_t);
609 dalloc_add(sl_arrary, sl_dict, sl_dict_t);
611 dalloc_add(dd, sl_arrary, sl_array_t);
613 /* Now dump the whole thing */
616 talloc_free(mem_ctx);