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