Spotlight: new options for controlling query behaviour
authorRalph Boehme <rb@sernet.de>
Wed, 4 Jun 2014 14:36:58 +0000 (16:36 +0200)
committerRalph Boehme <rb@sernet.de>
Wed, 4 Jun 2014 14:36:58 +0000 (16:36 +0200)
Add three options that allow fine grainted control over the resulting
SPARQL queries:

"sparql results limit = NUMBER", default is unlimited
Useful for limiting the result set for very large systems

"spotlight attributes = STRING", useful for limiting queries to
attributes that are indexed, many are not.

"spotlight expr = BOOLEAN", default: yes, useful for disabling the use
of complex queries that will take too long to complete and hang Tracker.

Signed-off-by: Ralph Boehme <rb@sernet.de>
14 files changed:
NEWS
doc/manpages/man5/afp.conf.5.xml
etc/afpd/afp_dsi.c
etc/afpd/spotlight.c
etc/spotlight/slmod_sparql.c
etc/spotlight/slmod_sparql_map.c
etc/spotlight/slmod_sparql_map.h
etc/spotlight/slmod_sparql_parser.c
etc/spotlight/slmod_sparql_parser.h
etc/spotlight/slmod_sparql_parser.y
include/atalk/globals.h
include/atalk/spotlight.h
libatalk/util/netatalk_conf.c
man/man5/afp.conf.5.in

diff --git a/NEWS b/NEWS
index f9c9052f4c527db3303bf1e9d33b3c8e65a72007..f4cab3d61e7380d9bd535dc43048a6cc0249d4b9 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,8 @@
 Changes in 3.1.3
 ================
 * UPD: Spotlight: more SPARQL query optimisations
+* UPD: Spotlight: new options "sparql results limit", "spotlight
+       attributes" and "spotlight expr"
 
 Changes in 3.1.2
 ================
index a1e760a13372f56feec8d27d07901c8b89682a6f..1c424462e95cfd9adf9f6eab87c1f94137f7a125 100644 (file)
           </listitem>
         </varlistentry>
 
+        <varlistentry>
+          <term>sparql results limit =
+          <replaceable>NUMBER</replaceable> (default:
+          <emphasis>UNLIMITED</emphasis>) <type>(G)</type></term>
+
+          <listitem>
+            <para>Impose a limit on the number of results queried from Tracker
+           via SPARQL queries.</para>
+          </listitem>
+        </varlistentry>
+
         <varlistentry>
           <term>spotlight =
           <replaceable>BOOLEAN</replaceable> (default:
-          <emphasis>no</emphasis>) <type>(G)/(V)</type></term>
+          <emphasis>no</emphasis>) <type>(G)</type></term>
 
           <listitem>
             <para>Whether to enable Spotlight searches. Note: once the global
           </listitem>
         </varlistentry>
 
+        <varlistentry>
+          <term>spotlight attributes =
+          <replaceable>COMMA SEPERATED STRING</replaceable> (default:
+          <emphasis>EMPTY</emphasis>) <type>(G)</type></term>
+
+          <listitem>
+            <para>A list of attributes that are allowed to be used in
+            Spotlight searches. By default all attributes can be
+            searched, passing a string limits attributes to elements
+            of the string. Example: <programlisting>spotlight
+            attributes = *,kMDItemTextContent</programlisting>
+           </para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>spotlight expr =
+          <replaceable>BOOLEAN</replaceable> (default:
+          <emphasis>yes</emphasis>) <type>(G)</type></term>
+
+          <listitem>
+            <para>Whether to allow the use of logic expression in
+            searches.</para>
+          </listitem>
+        </varlistentry>
+
         <varlistentry>
           <term>start dbus =
           <replaceable>BOOLEAN</replaceable> (default:
index eb4b19f35d5153302e189b8911bb6d853149ed7d..697aa435c9a06f4274516f29fe472e42a184bf79 100644 (file)
@@ -477,7 +477,7 @@ void afp_over_dsi(AFPObj *obj)
 
     /* Initialize Spotlight */
     if ((obj->options.flags & OPTION_SPOTLIGHT) && (obj->options.slmod_path))
-        sl_mod_load(obj->options.slmod_path);
+        sl_mod_load(obj);
 
     ipc_child_state(obj, DSI_RUNNING);
 
index a3fa587068b3ed6f699844e050c1793bd336c411..565462444504ea6a042f9c2ce77350ee25768796 100644 (file)
@@ -283,6 +283,11 @@ static int sl_rpc_openQuery(AFPObj *obj, const DALLOC_CTX *query, DALLOC_CTX *re
     slq->slq_state = SLQ_STATE_NEW;
     slq->slq_obj = obj;
     slq->slq_vol = v;
+    slq->slq_allow_expr = obj->options.flags & OPTION_SPOTLIGHT_EXPR ? true : false;
+    slq->slq_result_limit = obj->options.sparql_limit;
+
+    LOG(log_info, logtype_sl, "sl_rpc_openQuery: expr: %s, limit: %" PRIu64,
+        slq->slq_allow_expr ? "yes" : "no", slq->slq_result_limit);
 
     /* convert spotlight query charset to host charset */
     EC_NULL_LOG( sl_query = dalloc_value_for_key(query, "DALLOC_CTX", 0, "DALLOC_CTX", 1, "kMDQueryString") );
@@ -580,23 +585,23 @@ EC_CLEANUP:
  * Spotlight module functions
  **************************************************************************************************/
 
-int sl_mod_load(const char *path)
+int sl_mod_load(AFPObj *obj)
 {
     EC_INIT;
 
     sl_ctx = talloc_new(NULL);
 
-    if ((sl_module = mod_open(path)) == NULL) {
-        LOG(log_error, logtype_sl, "Failed to load module \'%s\': %s", path, mod_error());
+    if ((sl_module = mod_open(obj->options.slmod_path)) == NULL) {
+        LOG(log_error, logtype_sl, "Failed to load module \'%s\': %s", obj->options.slmod_path, mod_error());
         EC_FAIL;
     }
 
     if ((sl_module_export = mod_symbol(sl_module, "sl_mod")) == NULL) {
-        LOG(log_error, logtype_sl, "sl_mod_load(%s): mod_symbol error for symbol %s", path, "sl_mod");
+        LOG(log_error, logtype_sl, "sl_mod_load(%s): mod_symbol error for symbol sl_mod", obj->options.slmod_path);
         EC_FAIL;
     }
 
-    sl_module_export->sl_mod_init("test");
+    sl_module_export->sl_mod_init(obj);
    
 EC_CLEANUP:
     EC_EXIT;
index e59db5a932f156a4742a0853aeb172ad2d8798ce..21e8d382876dbfeb4082a886f7eb58e4a9ad6bbf 100644 (file)
@@ -59,7 +59,8 @@ static int sl_mod_init(void *p)
 {
     EC_INIT;
     GError *error = NULL;
-    const char *msg = p;
+    AFPObj *obj = (AFPObj *)p;
+    const char *attributes;
 
     LOG(log_info, logtype_sl, "Initializing Spotlight module");
 
@@ -95,6 +96,11 @@ static int sl_mod_init(void *p)
     }
 #endif
 
+    attributes = atalk_iniparser_getstring(obj->iniconfig, INISEC_GLOBAL, "spotlight attributes", NULL);
+    if (attributes) {
+        configure_spotlight_attributes(attributes);
+    }
+
 EC_CLEANUP:
     EC_EXIT;
 }
index c2c7312f96ef25fdd8f781e536b745372b548d29..7ac4963cee46bf384fcc534f393ff2cfc04f4e1c 100644 (file)
 #endif /* HAVE_CONFIG_H */
 
 #include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <atalk/logger.h>
 
 #include "slmod_sparql_map.h"
 
 #define SPECIAL      NULL
 
 struct spotlight_sparql_map spotlight_sparql_map[] = {
-    /* ssm_spotlight_attr               ssm_type,   ssm_sparql_attr */
-    {"*",                               ssmt_fts,   "fts:match"},
+    /* ssm_spotlight_attr               ssm_enabled, ssm_type,   ssm_sparql_attr */
+    {"*",                               true, ssmt_fts,   "fts:match"},
 
     /* Filesystem metadata */
-    {"kMDItemFSLabel",                  ssmt_num,   NOTSUPPORTED},
-    {"kMDItemDisplayName",              ssmt_str,   "nfo:fileName"},
-    {"kMDItemFSName",                   ssmt_str,   "nfo:fileName"},
-    {"kMDItemFSContentChangeDate",      ssmt_date,  "nfo:fileLastModified"},
+    {"kMDItemFSLabel",                  true, ssmt_num,   NOTSUPPORTED},
+    {"kMDItemDisplayName",              true, ssmt_str,   "nfo:fileName"},
+    {"kMDItemFSName",                   true, ssmt_str,   "nfo:fileName"},
+    {"kMDItemFSContentChangeDate",      true, ssmt_date,  "nfo:fileLastModified"},
 
     /* Common metadata */
-    {"kMDItemTextContent",              ssmt_fts,   "fts:match"},
-    {"kMDItemContentCreationDate",      ssmt_date,  "nie:contentCreated"},
-    {"kMDItemContentModificationDate",  ssmt_date,  "nfo:fileLastModified"},
-    {"kMDItemAttributeChangeDate",      ssmt_date,  "nfo:fileLastModified"},
-    {"kMDItemLastUsedDate",             ssmt_date,  "nfo:fileLastAccessed"},
-    {"kMDItemAuthors",                  ssmt_str,   "dc:creator"},
-    {"kMDItemCopyright",                ssmt_str,   "nie:copyright"},
-    {"kMDItemCountry",                  ssmt_str,   "nco:country"},
-    {"kMDItemCreator",                  ssmt_str,   "dc:creator"},
-    {"kMDItemDurationSeconds",          ssmt_num,   "nfo:duration"},
-    {"kMDItemNumberOfPages",            ssmt_num,   "nfo:pageCount"},
-    {"kMDItemTitle",                    ssmt_str,   "nie:title"},
-    {"_kMDItemGroupId",                 ssmt_type,  SPECIAL},
-    {"kMDItemContentTypeTree",          ssmt_type,  SPECIAL},
+    {"kMDItemTextContent",              true, ssmt_fts,   "fts:match"},
+    {"kMDItemContentCreationDate",      true, ssmt_date,  "nie:contentCreated"},
+    {"kMDItemContentModificationDate",  true, ssmt_date,  "nfo:fileLastModified"},
+    {"kMDItemAttributeChangeDate",      true, ssmt_date,  "nfo:fileLastModified"},
+    {"kMDItemLastUsedDate",             true, ssmt_date,  "nfo:fileLastAccessed"},
+    {"kMDItemAuthors",                  true, ssmt_str,   "dc:creator"},
+    {"kMDItemCopyright",                true, ssmt_str,   "nie:copyright"},
+    {"kMDItemCountry",                  true, ssmt_str,   "nco:country"},
+    {"kMDItemCreator",                  true, ssmt_str,   "dc:creator"},
+    {"kMDItemDurationSeconds",          true, ssmt_num,   "nfo:duration"},
+    {"kMDItemNumberOfPages",            true, ssmt_num,   "nfo:pageCount"},
+    {"kMDItemTitle",                    true, ssmt_str,   "nie:title"},
+    {"_kMDItemGroupId",                 true, ssmt_type,  SPECIAL},
+    {"kMDItemContentTypeTree",          true, ssmt_type,  SPECIAL},
 
     /* Image metadata */
-    {"kMDItemPixelWidth",               ssmt_num,   "nfo:width"},
-    {"kMDItemPixelHeight",              ssmt_num,   "nfo:height"},
-    {"kMDItemColorSpace",               ssmt_str,   "nexif:colorSpace"},
-    {"kMDItemBitsPerSample",            ssmt_num,   "nfo:colorDepth"},
-    {"kMDItemFocalLength",              ssmt_num,   "nmm:focalLength"},
-    {"kMDItemISOSpeed",                 ssmt_num,   "nmm:isoSpeed"},
-    {"kMDItemOrientation",              ssmt_bool,  "nfo:orientation"},
-    {"kMDItemResolutionWidthDPI",       ssmt_num,   "nfo:horizontalResolution"},
-    {"kMDItemResolutionHeightDPI",      ssmt_num,   "nfo:verticalResolution"},
-    {"kMDItemExposureTimeSeconds",      ssmt_num,   "nmm:exposureTime"},
+    {"kMDItemPixelWidth",               true, ssmt_num,   "nfo:width"},
+    {"kMDItemPixelHeight",              true, ssmt_num,   "nfo:height"},
+    {"kMDItemColorSpace",               true, ssmt_str,   "nexif:colorSpace"},
+    {"kMDItemBitsPerSample",            true, ssmt_num,   "nfo:colorDepth"},
+    {"kMDItemFocalLength",              true, ssmt_num,   "nmm:focalLength"},
+    {"kMDItemISOSpeed",                 true, ssmt_num,   "nmm:isoSpeed"},
+    {"kMDItemOrientation",              true, ssmt_bool,  "nfo:orientation"},
+    {"kMDItemResolutionWidthDPI",       true, ssmt_num,   "nfo:horizontalResolution"},
+    {"kMDItemResolutionHeightDPI",      true, ssmt_num,   "nfo:verticalResolution"},
+    {"kMDItemExposureTimeSeconds",      true, ssmt_num,   "nmm:exposureTime"},
 
     /* Audio metadata */
-    {"kMDItemComposer",                 ssmt_str,   "nmm:composer"},
-    {"kMDItemMusicalGenre",             ssmt_str,   "nfo:genre"},
+    {"kMDItemComposer",                 true, ssmt_str,   "nmm:composer"},
+    {"kMDItemMusicalGenre",             true, ssmt_str,   "nfo:genre"},
 
-    {NULL, ssmt_str, NULL}
+    {NULL, false, ssmt_str, NULL}
 };
 
 struct MDTypeMap MDTypeMap[] = {
@@ -99,3 +103,34 @@ struct MDTypeMap MDTypeMap[] = {
     {"public.source-code",      kMDTypeMapRDF,      "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#SourceCode"},
     {NULL,                      kMDTypeMapNotSup,   NULL}
 };
+
+void configure_spotlight_attributes(const char *attributes_in)
+{
+    char *attr, *attributes;
+    int i;
+
+    for (i = 0; spotlight_sparql_map[i].ssm_spotlight_attr != NULL; i++)
+        spotlight_sparql_map[i].ssm_enabled = false;
+
+    /*
+     * Go through the attribute map and for every element scan
+     * attributes_in with strtok(). If it's contained, keep it
+     * enabled, otherwise disable it.
+     */
+
+    attributes = strdup(attributes_in);
+
+    for (attr = strtok(attributes, ","); attr; attr = strtok(NULL, ",")) {
+
+        for (i = 0; spotlight_sparql_map[i].ssm_spotlight_attr != NULL; i++)
+
+            if (strcmp(attr, spotlight_sparql_map[i].ssm_spotlight_attr) == 0) {
+                LOG(log_info, logtype_sl, "Enabling Spotlight attribute: %s",
+                    spotlight_sparql_map[i].ssm_spotlight_attr);
+                spotlight_sparql_map[i].ssm_enabled = true;
+                break;
+        }
+    }
+
+    free(attributes);
+}
index 250894b1094ad12e4c18ea028915f61199e5575b..3b9247408c9a41f0acedb8e79b35cb696700298f 100644 (file)
@@ -36,6 +36,7 @@ enum kMDTypeMap {
 
 struct spotlight_sparql_map {
     const char *ssm_spotlight_attr;
+    bool ssm_enabled;
     enum ssm_type ssm_type;
     const char *ssm_sparql_attr;
 };
index f70d9ef100cbc1630202a2d6276885ca5d9e1543..66e0f59d731572829a13d3f090f2423839b95d7c 100644 (file)
   /* local vars */
   static gchar *ssp_result;
   static char sparqlvar;
+  static char *result_limit;
 
 /* Line 371 of yacc.c  */
-#line 106 "slmod_sparql_parser.c"
+#line 107 "slmod_sparql_parser.c"
 
 # ifndef YY_NULL
 #  if defined __cplusplus && 201103L <= __cplusplus
@@ -176,7 +177,7 @@ extern int yydebug;
 typedef union YYSTYPE
 {
 /* Line 387 of yacc.c  */
-#line 45 "slmod_sparql_parser.y"
+#line 46 "slmod_sparql_parser.y"
 
     int ival;
     const char *sval;
@@ -185,7 +186,7 @@ typedef union YYSTYPE
 
 
 /* Line 387 of yacc.c  */
-#line 189 "slmod_sparql_parser.c"
+#line 190 "slmod_sparql_parser.c"
 } YYSTYPE;
 # define YYSTYPE_IS_TRIVIAL 1
 # define yystype YYSTYPE /* obsolescent; will be withdrawn */
@@ -209,7 +210,7 @@ int yyparse ();
 #endif /* ! YYPARSE_PARAM */
 /* "%code provides" blocks.  */
 /* Line 387 of yacc.c  */
-#line 39 "slmod_sparql_parser.y"
+#line 40 "slmod_sparql_parser.y"
 
   #define SPRAW_TIME_OFFSET 978307200
   extern int map_spotlight_to_sparql_query(slq_t *slq, gchar **sparql_result);
@@ -217,14 +218,14 @@ int yyparse ();
 
 
 /* Line 387 of yacc.c  */
-#line 221 "slmod_sparql_parser.c"
+#line 222 "slmod_sparql_parser.c"
 
 #endif /* !YY_YY_SLMOD_SPARQL_PARSER_H_INCLUDED  */
 
 /* Copy the second part of user declarations.  */
 
 /* Line 390 of yacc.c  */
-#line 228 "slmod_sparql_parser.c"
+#line 229 "slmod_sparql_parser.c"
 
 #ifdef short
 # undef short
@@ -523,9 +524,9 @@ static const yytype_int8 yyrhs[] =
 /* YYRLINE[YYN] -- source line where rule number YYN was defined.  */
 static const yytype_uint8 yyrline[] =
 {
-       0,    67,    67,    69,    73,    83,    89,    95,    96,    97,
-      98,    99,   108,   109,   110,   111,   112,   113,   114,   115,
-     119,   123,   124
+       0,    68,    68,    70,    74,    88,    94,   104,   105,   106,
+     107,   112,   121,   122,   123,   124,   125,   126,   127,   128,
+     132,   136,   137
 };
 #endif
 
@@ -1446,19 +1447,23 @@ yyreduce:
     {
         case 4:
 /* Line 1792 of yacc.c  */
-#line 73 "slmod_sparql_parser.y"
+#line 74 "slmod_sparql_parser.y"
     {
+    if (ssp_slq->slq_result_limit)
+        result_limit = talloc_asprintf(ssp_slq, "LIMIT %ld", ssp_slq->slq_result_limit);
+    else
+        result_limit = "";
     ssp_result = talloc_asprintf(ssp_slq,
                                  "SELECT ?url WHERE "
-                                 "{ %s . ?obj nie:url ?url . FILTER(tracker:uri-is-descendant('file://%s/', ?url)) } LIMIT 100",
-                                 (yyvsp[(1) - (1)].sval), ssp_slq->slq_vol->v_path);
+                                 "{ %s . ?obj nie:url ?url . FILTER(tracker:uri-is-descendant('file://%s/', ?url)) } %s",
+                                 (yyvsp[(1) - (1)].sval), ssp_slq->slq_vol->v_path, result_limit);
     (yyval.sval) = ssp_result;
 }
     break;
 
   case 5:
 /* Line 1792 of yacc.c  */
-#line 83 "slmod_sparql_parser.y"
+#line 88 "slmod_sparql_parser.y"
     {
     if ((yyvsp[(1) - (1)].bval) == false)
         YYACCEPT;
@@ -1469,8 +1474,12 @@ yyreduce:
 
   case 6:
 /* Line 1792 of yacc.c  */
-#line 89 "slmod_sparql_parser.y"
+#line 94 "slmod_sparql_parser.y"
     {
+    if (!ssp_slq->slq_allow_expr)
+        YYABORT;
+    if ((yyvsp[(1) - (3)].sval) == NULL || (yyvsp[(3) - (3)].sval) == NULL)
+        YYABORT;
     if (strcmp((yyvsp[(1) - (3)].sval), (yyvsp[(3) - (3)].sval)) != 0)
         (yyval.sval) = talloc_asprintf(ssp_slq, "{ %s } UNION { %s }", (yyvsp[(1) - (3)].sval), (yyvsp[(3) - (3)].sval));
     else
@@ -1480,31 +1489,35 @@ yyreduce:
 
   case 7:
 /* Line 1792 of yacc.c  */
-#line 95 "slmod_sparql_parser.y"
+#line 104 "slmod_sparql_parser.y"
     {(yyval.sval) = (yyvsp[(1) - (1)].sval); if ((yyval.sval) == NULL) YYABORT;}
     break;
 
   case 8:
 /* Line 1792 of yacc.c  */
-#line 96 "slmod_sparql_parser.y"
+#line 105 "slmod_sparql_parser.y"
     {(yyval.sval) = (yyvsp[(1) - (1)].sval);}
     break;
 
   case 9:
 /* Line 1792 of yacc.c  */
-#line 97 "slmod_sparql_parser.y"
+#line 106 "slmod_sparql_parser.y"
     {(yyval.sval) = talloc_asprintf(ssp_slq, "%s", (yyvsp[(2) - (3)].sval));}
     break;
 
   case 10:
 /* Line 1792 of yacc.c  */
-#line 98 "slmod_sparql_parser.y"
-    {(yyval.sval) = talloc_asprintf(ssp_slq, "%s . %s", (yyvsp[(1) - (3)].sval), (yyvsp[(3) - (3)].sval));}
+#line 107 "slmod_sparql_parser.y"
+    {
+    if (!ssp_slq->slq_allow_expr)
+        YYABORT;
+    (yyval.sval) = talloc_asprintf(ssp_slq, "%s . %s", (yyvsp[(1) - (3)].sval), (yyvsp[(3) - (3)].sval));
+}
     break;
 
   case 11:
 /* Line 1792 of yacc.c  */
-#line 99 "slmod_sparql_parser.y"
+#line 112 "slmod_sparql_parser.y"
     {
     if (strcmp((yyvsp[(1) - (3)].sval), (yyvsp[(3) - (3)].sval)) != 0)
         (yyval.sval) = talloc_asprintf(ssp_slq, "{ %s } UNION { %s }", (yyvsp[(1) - (3)].sval), (yyvsp[(3) - (3)].sval));
@@ -1515,73 +1528,73 @@ yyreduce:
 
   case 12:
 /* Line 1792 of yacc.c  */
-#line 108 "slmod_sparql_parser.y"
+#line 121 "slmod_sparql_parser.y"
     {(yyval.sval) = map_expr((yyvsp[(1) - (5)].sval), '=', (yyvsp[(4) - (5)].sval));}
     break;
 
   case 13:
 /* Line 1792 of yacc.c  */
-#line 109 "slmod_sparql_parser.y"
+#line 122 "slmod_sparql_parser.y"
     {(yyval.sval) = map_expr((yyvsp[(1) - (5)].sval), '!', (yyvsp[(4) - (5)].sval));}
     break;
 
   case 14:
 /* Line 1792 of yacc.c  */
-#line 110 "slmod_sparql_parser.y"
+#line 123 "slmod_sparql_parser.y"
     {(yyval.sval) = map_expr((yyvsp[(1) - (5)].sval), '<', (yyvsp[(4) - (5)].sval));}
     break;
 
   case 15:
 /* Line 1792 of yacc.c  */
-#line 111 "slmod_sparql_parser.y"
+#line 124 "slmod_sparql_parser.y"
     {(yyval.sval) = map_expr((yyvsp[(1) - (5)].sval), '>', (yyvsp[(4) - (5)].sval));}
     break;
 
   case 16:
 /* Line 1792 of yacc.c  */
-#line 112 "slmod_sparql_parser.y"
+#line 125 "slmod_sparql_parser.y"
     {(yyval.sval) = map_expr((yyvsp[(1) - (6)].sval), '=', (yyvsp[(4) - (6)].sval));}
     break;
 
   case 17:
 /* Line 1792 of yacc.c  */
-#line 113 "slmod_sparql_parser.y"
+#line 126 "slmod_sparql_parser.y"
     {(yyval.sval) = map_expr((yyvsp[(1) - (6)].sval), '!', (yyvsp[(4) - (6)].sval));}
     break;
 
   case 18:
 /* Line 1792 of yacc.c  */
-#line 114 "slmod_sparql_parser.y"
+#line 127 "slmod_sparql_parser.y"
     {(yyval.sval) = map_expr((yyvsp[(1) - (6)].sval), '<', (yyvsp[(4) - (6)].sval));}
     break;
 
   case 19:
 /* Line 1792 of yacc.c  */
-#line 115 "slmod_sparql_parser.y"
+#line 128 "slmod_sparql_parser.y"
     {(yyval.sval) = map_expr((yyvsp[(1) - (6)].sval), '>', (yyvsp[(4) - (6)].sval));}
     break;
 
   case 20:
 /* Line 1792 of yacc.c  */
-#line 119 "slmod_sparql_parser.y"
+#line 132 "slmod_sparql_parser.y"
     {(yyval.sval) = map_daterange((yyvsp[(3) - (8)].sval), (yyvsp[(5) - (8)].tval), (yyvsp[(7) - (8)].tval));}
     break;
 
   case 21:
 /* Line 1792 of yacc.c  */
-#line 123 "slmod_sparql_parser.y"
+#line 136 "slmod_sparql_parser.y"
     {(yyval.tval) = isodate2unix((yyvsp[(3) - (4)].sval));}
     break;
 
   case 22:
 /* Line 1792 of yacc.c  */
-#line 124 "slmod_sparql_parser.y"
+#line 137 "slmod_sparql_parser.y"
     {(yyval.tval) = atoi((yyvsp[(1) - (1)].sval)) + SPRAW_TIME_OFFSET;}
     break;
 
 
 /* Line 1792 of yacc.c  */
-#line 1585 "slmod_sparql_parser.c"
+#line 1598 "slmod_sparql_parser.c"
       default: break;
     }
   /* User semantic actions sometimes alter yychar, and that requires
@@ -1813,7 +1826,7 @@ yyreturn:
 
 
 /* Line 2055 of yacc.c  */
-#line 127 "slmod_sparql_parser.y"
+#line 140 "slmod_sparql_parser.y"
 
 
 static time_t isodate2unix(const char *s)
@@ -1896,7 +1909,7 @@ static const char *map_expr(const char *attr, char op, const char *val)
     bstring q = NULL, search = NULL, replace = NULL;
 
     for (p = spotlight_sparql_map; p->ssm_spotlight_attr; p++) {
-        if (strcmp(p->ssm_spotlight_attr, attr) == 0) {
+        if (p->ssm_enabled && (strcmp(p->ssm_spotlight_attr, attr) == 0)) {
             if (p->ssm_type != ssmt_type && p->ssm_sparql_attr == NULL) {
                 yyerror("unsupported Spotlight attribute");
                 EC_FAIL;
@@ -2021,6 +2034,7 @@ int main(int argc, char **argv)
     struct vol *vol = talloc_zero(ssp_slq, struct vol);
     vol->v_path = "/Volumes/test";
     ssp_slq->slq_vol = vol;
+    ssp_slq->slq_allow_expr = true;
     sparqlvar = 'a';
 
     s = yy_scan_string(argv[1]);
index 471e16f5285cedfc7af8010112807233d7c0f5f8..9eb22b37f672b254a60208ce3bbfccb8b26a3437 100644 (file)
@@ -84,7 +84,7 @@ extern int yydebug;
 typedef union YYSTYPE
 {
 /* Line 2058 of yacc.c  */
-#line 45 "slmod_sparql_parser.y"
+#line 46 "slmod_sparql_parser.y"
 
     int ival;
     const char *sval;
@@ -117,7 +117,7 @@ int yyparse ();
 #endif /* ! YYPARSE_PARAM */
 /* "%code provides" blocks.  */
 /* Line 2058 of yacc.c  */
-#line 39 "slmod_sparql_parser.y"
+#line 40 "slmod_sparql_parser.y"
 
   #define SPRAW_TIME_OFFSET 978307200
   extern int map_spotlight_to_sparql_query(slq_t *slq, gchar **sparql_result);
index 0763b39c913a08e89131440c0f7c486ab213a0bf..e4069bbe381892be8a67f2d877f9400f4ecd6796 100644 (file)
@@ -34,6 +34,7 @@
   /* local vars */
   static gchar *ssp_result;
   static char sparqlvar;
+  static char *result_limit;
 %}
 
 %code provides {
@@ -71,10 +72,14 @@ input:
      
 line:
 expr                           {
+    if (ssp_slq->slq_result_limit)
+        result_limit = talloc_asprintf(ssp_slq, "LIMIT %ld", ssp_slq->slq_result_limit);
+    else
+        result_limit = "";
     ssp_result = talloc_asprintf(ssp_slq,
                                  "SELECT ?url WHERE "
-                                 "{ %s . ?obj nie:url ?url . FILTER(tracker:uri-is-descendant('file://%s/', ?url)) } LIMIT 100",
-                                 $1, ssp_slq->slq_vol->v_path);
+                                 "{ %s . ?obj nie:url ?url . FILTER(tracker:uri-is-descendant('file://%s/', ?url)) } %s",
+                                 $1, ssp_slq->slq_vol->v_path, result_limit);
     $$ = ssp_result;
 }
 ;
@@ -87,6 +92,10 @@ BOOL                             {
         YYABORT;
 }
 | match OR match                 {
+    if (!ssp_slq->slq_allow_expr)
+        YYABORT;
+    if ($1 == NULL || $3 == NULL)
+        YYABORT;
     if (strcmp($1, $3) != 0)
         $$ = talloc_asprintf(ssp_slq, "{ %s } UNION { %s }", $1, $3);
     else
@@ -95,7 +104,11 @@ BOOL                             {
 | match                        {$$ = $1; if ($$ == NULL) YYABORT;}
 | function                     {$$ = $1;}
 | OBRACE expr CBRACE           {$$ = talloc_asprintf(ssp_slq, "%s", $2);}
-| expr AND expr                {$$ = talloc_asprintf(ssp_slq, "%s . %s", $1, $3);}
+| expr AND expr                {
+    if (!ssp_slq->slq_allow_expr)
+        YYABORT;
+    $$ = talloc_asprintf(ssp_slq, "%s . %s", $1, $3);
+}
 | expr OR expr                 {
     if (strcmp($1, $3) != 0)
         $$ = talloc_asprintf(ssp_slq, "{ %s } UNION { %s }", $1, $3);
@@ -206,7 +219,7 @@ static const char *map_expr(const char *attr, char op, const char *val)
     bstring q = NULL, search = NULL, replace = NULL;
 
     for (p = spotlight_sparql_map; p->ssm_spotlight_attr; p++) {
-        if (strcmp(p->ssm_spotlight_attr, attr) == 0) {
+        if (p->ssm_enabled && (strcmp(p->ssm_spotlight_attr, attr) == 0)) {
             if (p->ssm_type != ssmt_type && p->ssm_sparql_attr == NULL) {
                 yyerror("unsupported Spotlight attribute");
                 EC_FAIL;
@@ -331,6 +344,7 @@ int main(int argc, char **argv)
     struct vol *vol = talloc_zero(ssp_slq, struct vol);
     vol->v_path = "/Volumes/test";
     ssp_slq->slq_vol = vol;
+    ssp_slq->slq_allow_expr = true;
     sparqlvar = 'a';
 
     s = yy_scan_string(argv[1]);
index 21b8b4e82ae842fb84282e9ef6bae10661f2b67e..603b5caa6c6093ad7cfec5719aa81e25eef0bbb3 100644 (file)
@@ -60,6 +60,7 @@
 #define OPTION_SPOTLIGHT     (1 << 13) /* whether to initialize Spotlight support */
 #define OPTION_SPOTLIGHT_VOL (1 << 14) /* whether spotlight shall be enabled by default for volumes */
 #define OPTION_RECVFILE      (1 << 15)
+#define OPTION_SPOTLIGHT_EXPR (1 << 16) /* whether to allow Spotlight logic expressions */
 
 #define PASSWD_NONE     0
 #define PASSWD_SET     (1 << 0)
@@ -130,6 +131,7 @@ struct afp_options {
     char *cnid_mysql_pw;
     char *cnid_mysql_db;
     struct afp_volume_name volfile;
+    uint64_t sparql_limit;
 };
 
 typedef struct AFPObj {
index 0ca8a24edd44467d6245c1863d32f65247992114..bafa52f53b83d9a88f714a0c5dddb8ffa3125992 100644 (file)
@@ -43,7 +43,7 @@ struct sl_module_export {
     int (*sl_mod_index_file)  (const void *);
 };
 
-extern int sl_mod_load(const char *path);
+extern int sl_mod_load(AFPObj *obj);
 extern void sl_index_file(const char *path);
 
 /**************************************************************************************************
@@ -105,6 +105,8 @@ typedef struct _slq_t {
     size_t            slq_cnids_num;      /* Size of slq_cnids array                                        */
     const char       *slq_path;           /* Path to file or dir, used in fetchAttributes                   */
     void             *slq_tracker_cursor; /* Tracker SPARQL query result cursor                             */
+    bool              slq_allow_expr;     /* Whether to allow logic expressions                             */
+    uint64_t          slq_result_limit;   /* Whether to LIMIT SPARQL results, default of 0 means no limit   */
 } slq_t;
 
 /**************************************************************************************************
@@ -114,5 +116,6 @@ typedef struct _slq_t {
 extern int afp_spotlight_rpc(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen);
 extern int sl_pack(DALLOC_CTX *query, char *buf);
 extern int sl_unpack(DALLOC_CTX *query, const char *buf);
+extern void configure_spotlight_attributes(const char *attributes);
 
 #endif /* SPOTLIGHT_H */
index 01e1f16704df4dc66f7570fc2a5aa1ce3dc9243f..71acf8841adf263e68f15a290b228da2cae89e42 100644 (file)
@@ -1828,6 +1828,8 @@ int afp_config_parse(AFPObj *AFPObj, char *processname)
         options->passwdbits |= PASSWD_NOSAVE;
     if (atalk_iniparser_getboolean(config, INISEC_GLOBAL, "set password", 0))
         options->passwdbits |= PASSWD_SET;
+    if (atalk_iniparser_getboolean(config, INISEC_GLOBAL, "spotlight expr", 1))
+        options->flags |= OPTION_SPOTLIGHT_EXPR;
 
     /* figure out options w values */
     options->loginmesg      = atalk_iniparser_getstrdup(config, INISEC_GLOBAL, "login message",  NULL);
@@ -1866,6 +1868,7 @@ int afp_config_parse(AFPObj *AFPObj, char *processname)
     options->sleep          = atalk_iniparser_getint   (config, INISEC_GLOBAL, "sleep time",     10);
     options->disconnected   = atalk_iniparser_getint   (config, INISEC_GLOBAL, "disconnect time",24);
     options->splice_size    = atalk_iniparser_getint   (config, INISEC_GLOBAL, "splice size",    64*1024);
+    options->sparql_limit   = atalk_iniparser_getint   (config, INISEC_GLOBAL, "sparql results limit", 0);
 
     p = atalk_iniparser_getstring(config, INISEC_GLOBAL, "map acls", "rights");
     if (STRCMP(p, ==, "rights"))
index e50d634c555198647bea225590c879717e3bacb3..c6eb8388a3ba0fb85d0553b6dd6696f96e9976f5 100644 (file)
@@ -631,13 +631,40 @@ solaris share reservations = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(G)\fR
 Use share reservations on Solaris\&. Solaris CIFS server uses this too, so this makes a lock coherent multi protocol server\&.
 .RE
 .PP
-spotlight = \fIBOOLEAN\fR (default: \fIno\fR) \fB(G)/(V)\fR
+sparql results limit = \fINUMBER\fR (default: \fIUNLIMITED\fR) \fB(G)\fR
+.RS 4
+Impose a limit on the number of results queried from Tracker via SPARQL queries\&.
+.RE
+.PP
+spotlight = \fIBOOLEAN\fR (default: \fIno\fR) \fB(G)\fR
 .RS 4
 Whether to enable Spotlight searches\&. Note: once the global option is enabled, any volume that is not enabled won\*(Aqt be searchable at all\&. See also
 \fIdbus daemon\fR
 option\&.
 .RE
 .PP
+spotlight attributes = \fICOMMA SEPERATED STRING\fR (default: \fIEMPTY\fR) \fB(G)\fR
+.RS 4
+A list of attributes that are allowed to be used in Spotlight searches\&. By default all attributes can be searched, passing a string limits attributes to elements of the string\&. Example:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+spotlight
+            attributes = *,kMDItemTextContent
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.RE
+.PP
+spotlight expr = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(G)\fR
+.RS 4
+Whether to allow the use of logic expression in searches\&.
+.RE
+.PP
 start dbus = \fIBOOLEAN\fR (default: \fIyes\fR) \fB(G)\fR
 .RS 4
 Whether to start a dbus instance for use with Tracker\&.