/* Copyright 2006 david reid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "httpd.h" #include "http_config.h" #include "http_core.h" #include "http_log.h" #include "http_main.h" #include "http_protocol.h" #include "http_request.h" #include "util_script.h" #include "http_connection.h" #include "apr_lib.h" #include "apr_strings.h" #include "apr_uri.h" #include "apr_strmatch.h" #include #include #include "mod_sparql.h" module AP_MODULE_DECLARE_DATA sparql_module; struct output_formats { const char *name; const char *uri; } outputFormats[] = { { "xml-1", "http://www.w3.org/TR/2004/WD-rdf-sparql-XMLres-20041221/" }, { "xml-2", "http://www.w3.org/2001/sw/DataAccess/rf1/result2" }, { "xml-3", "http://www.w3.org/2005/sparql-results#" }, { "json", "http://www.mindswap.org/%7Ekendall/sparql-results-json/" }, { NULL, NULL }, }; static char *defaultFormat = "xml-3"; static char *defaultQL = "sparql"; static librdf_world *world = NULL; #define RESULTS_FORMAT_SIMPLE 0 #define RESULTS_FORMAT_XML_V1 1 #define RESULTS_FORMAT_XML_V2 2 #define RESULTS_FORMAT_XML_V3 3 #define RESULTS_FORMAT_JSON 4 #define SPARQL_HANDLER_NAME "sparql-handler" #define SPARQL_POST_CT "application/x-www-form-urlencoded" static const char c2x_table[] = "0123456789ABCDEF"; static APR_INLINE unsigned char hex2_to_char(const char *what) { register unsigned char digit; #if !APR_CHARSET_EBCDIC digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0')); digit *= 16; digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0')); #else /*APR_CHARSET_EBCDIC*/ char xstr[5]; xstr[0]='0'; xstr[1]='x'; xstr[2]=what[0]; xstr[3]=what[1]; xstr[4]='\0'; digit = apr_xlate_conv_byte(ap_hdrs_from_ascii, 0xFF & strtol(xstr, NULL, 16)); #endif /*APR_CHARSET_EBCDIC*/ return (digit); } static APR_INLINE apr_uint16_t hex4_to_bmp(const char *what) { register apr_uint16_t digit = 0; #if !APR_CHARSET_EBCDIC digit = (what[0] >= 'A' ? ((what[0] & 0xDF)-'A') + 10 : (what[0]-'0')); digit *= 16; digit += (what[1] >= 'A' ? ((what[1] & 0xDF)-'A') + 10 : (what[1]-'0')); digit *= 16; digit += (what[2] >= 'A' ? ((what[2] & 0xDF)-'A') + 10 : (what[2]-'0')); digit *= 16; digit += (what[3] >= 'A' ? ((what[3] & 0xDF)-'A') + 10 : (what[3]-'0')); #else /*APR_CHARSET_EBCDIC*/ char xstr[7]; xstr[0]='0'; xstr[1]='x'; xstr[2]=what[0]; xstr[3]=what[1]; xstr[4]=what[2]; xstr[5]=what[3]; xstr[6]='\0'; digit = apr_xlate_conv_byte(ap_hdrs_from_ascii, 0xFFFF & strtol(xstr, NULL, 16)); #endif /*APR_CHARSET_EBCDIC*/ return (digit); } static apr_status_t url_decode(char *dest, apr_size_t *dlen, const char *src, apr_size_t *slen) { register const char *s = src; unsigned char *start = (unsigned char *)dest; register unsigned char *d = (unsigned char *)dest; const char *end = src + *slen; for (; s < end; ++d, ++s) { switch (*s) { case '+': *d = ' '; break; case '%': if (s + 2 < end && apr_isxdigit(s[1]) && apr_isxdigit(s[2])) { *d = hex2_to_char(s + 1); s += 2; } else if (s + 5 < end && (s[1] == 'u' || s[1] == 'U') && apr_isxdigit(s[2]) && apr_isxdigit(s[3]) && apr_isxdigit(s[4]) && apr_isxdigit(s[5])) { apr_uint16_t c = hex4_to_bmp(s+2); if (c < 0x80) { *d = c; } else if (c < 0x800) { *d++ = 0xC0 | (c >> 6); *d = 0x80 | (c & 0x3F); } else { *d++ = 0xE0 | (c >> 12); *d++ = 0x80 | ((c >> 6) & 0x3F); *d = 0x80 | (c & 0x3F); } s += 5; } else { *dlen = d - start; *slen = s - src; if (s + 5 < end || (s + 2 < end && !apr_isxdigit(s[2])) || (s + 1 < end && !apr_isxdigit(s[1]) && s[1] != 'u' && s[1] != 'U')) { *d = 0; return 1; } memmove(d, s, end - s); d[end - s] = 0; return 2; } break; default: if (*s > 0) { *d = *s; } else { *d = 0; *dlen = d - start; *slen = s - src; return 1; } } } *d = 0; *dlen = d - start; *slen = s - src; return APR_SUCCESS; } static apr_status_t query_decode(char *d, apr_size_t *dlen, const char *s, apr_size_t slen) { apr_size_t len = 0; const char *end = s + slen; if (s == (const char *)d) { /* optimize for src = dest case */ for ( ; d < end; ++d) { if (*d == '%' || *d == '+') break; else if (*d == 0) { *dlen = (const char *)d - s; return -1; } } len = (const char *)d - s; s = (const char *)d; slen -= len; } return url_decode(d, dlen, s, &slen); } static int sparql_error_response(request_rec *r, int status, const char *msg) { r->status = status; ap_set_content_type(r, "text/plain"); ap_rvputs(r, msg, NULL); /* Response has been sent, all done */ return DONE; } static int rdfError(void *uData, librdf_log_message *msg) { const char *msgStr = librdf_log_message_message(msg); librdf_log_level msgLvl = librdf_log_message_level(msg); raptor_locator *loc = librdf_log_message_locator(msg); /* TODO - set approrpiate log level */ int aplog = APLOG_INFO; switch(msgLvl) { case LIBRDF_LOG_DEBUG: aplog = APLOG_DEBUG; break; case LIBRDF_LOG_INFO: aplog = APLOG_INFO; break; case LIBRDF_LOG_WARN: aplog = APLOG_WARNING; break; case LIBRDF_LOG_ERROR: aplog = APLOG_ERR; break; case LIBRDF_LOG_FATAL: aplog = APLOG_CRIT; break; } if (loc && loc->file) ap_log_error(APLOG_MARK, aplog, 0, NULL, "Redland: %s @ line %d: %s", raptor_locator_file(loc), raptor_locator_line(loc), msgStr); else if (msg) ap_log_error(APLOG_MARK, aplog, 0, NULL, "Redland: %s", msgStr); return 1; } static int setOutputFormat(mod_sparql_state_t *st, const char *name) { int i = 0; st->resFormat = -1; if (st->outputUri) { librdf_free_uri(st->outputUri); st->outputUri = NULL; } for (i = 0; outputFormats[i].name; i++) { if (strcasecmp(outputFormats[i].name, name ? name : defaultFormat) == 0) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, "setOutputFormat(%s) => %s", outputFormats[i].name, outputFormats[i].uri); st->outputUri = librdf_new_uri(world, (const unsigned char *) outputFormats[st->resFormat].uri); st->resFormat = i; return 1; } } return 0; } static void sparql_set_content_type(mod_sparql_state_t *st, request_rec *r) { char *ct = "application/sparql-results+xml"; switch (st->resFormat) { case RESULTS_FORMAT_JSON: break; } ap_set_content_type(r, ct); } static void addUrisToStore(mod_sparql_state_t *st) { librdf_parser *parser = NULL; int i = 0; const char *parser_name = NULL; struct store_uri *s = st->st_uris; if (!s) return; parser_name = raptor_guess_parser_name(NULL, NULL, NULL, 0, s->uri); if (!parser_name) { ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, "Unable to guess parser name from URI '%s'", s->uri); return; } parser = librdf_new_parser(world, parser_name, NULL, NULL); if (!parser) { ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, "Unable to get parser using parser name '%s'", parser_name); return; } for (; s; s = s->next) { librdf_uri *uri = librdf_new_uri(world, s->uri); if (uri) { librdf_parser_parse_into_model(parser, uri, NULL, st->model); /* if (librdf_parser_parse_into_model(parser, uri, NULL, st->model)) ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, "Failed to parse '%s' into model", s->uri); else ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, "Added '%s' to the model", s->uri); */ librdf_free_uri(uri); } } librdf_free_parser(parser); } /* Try and create an RDF store. */ static librdf_storage *sparqlMakeStorage(mod_sparql_state_t *st, int defaultOK) { librdf_storage *store = NULL; /* ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, "sparqlMakeStorage: name = %s, type = %s", st->st_name, st->st_type); */ if (st->st_name || st->st_type) { char *options; if (! st->st_opts) options = "hash-type='memory'"; else options = st->st_opts; if (st->st_name && st->st_type) { store = librdf_new_storage(world, st->st_type, st->st_name, options); } else if (st->st_type) { store = librdf_new_storage(world, st->st_type, NULL, options); } else if (st->st_name) { store = librdf_new_storage(world, "hashes", st->st_name, options); } if (store) { st->model = librdf_new_model(world, store, NULL); if (st->st_uris) addUrisToStore(st); } } else if (defaultOK) { store = librdf_new_storage(world, "hashes", NULL, "hash-type='memory'"); } return store; } static librdf_model *fillStoreFromQuery(mod_sparql_state_t *st, unsigned char *query_string, librdf_uri *base_uri, raptor_sequence *source_uris) { librdf_model *model = NULL; librdf_parser *parser = NULL; int i = 0; const char *parser_name = NULL; rasqal_data_graph *graph = NULL; raptor_uri *buri = NULL; rasqal_query *rq = rasqal_new_query(st->ql_name, librdf_uri_to_string(st->ql_uri)); if (!rq) { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, "Unable to create new rasqual query"); return NULL; } /* Prepare the query */ buri = raptor_new_uri(librdf_uri_to_string(base_uri)); if (rasqal_query_prepare(rq, query_string, buri)) { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, "Unable to prepare query! '%s'", query_string); return NULL; } graph = rasqal_query_get_data_graph(rq, 0); if (base_uri) { parser_name = raptor_guess_parser_name(NULL, NULL, NULL, 0, librdf_uri_as_string(base_uri)); } else if (graph) { parser_name = raptor_guess_parser_name(NULL, NULL, NULL, 0, raptor_uri_as_string(graph->uri)); } if (! parser_name) { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, "Unable to guess parser name"); return NULL; } parser = librdf_new_parser(world, parser_name, NULL, NULL); if (!parser) { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, "Unable to create a parser for '%s'", parser_name); return NULL; } model = librdf_new_model(world, st->store, NULL); if (!model) { librdf_free_parser(parser); ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, "Unable to create a model"); return NULL; } if (graph) { do { librdf_uri *uri = NULL; if (!st->fileUris && strstr(raptor_uri_as_string(graph->uri), "file:/")) { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, "File URIs are not permitted by configuration" ", ignoring '%s'", raptor_uri_as_string(graph->uri)); continue; } uri = librdf_new_uri(world, raptor_uri_as_string(graph->uri)); if (uri) { if (librdf_parser_parse_into_model(parser, uri, base_uri, model)) ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, "Failed to parse '%s' into model", librdf_uri_as_string(uri)); else ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, "Added '%s' to the model", librdf_uri_as_string(uri)); librdf_free_uri(uri); } } while ((graph = rasqal_query_get_data_graph(rq, ++i))); } if (source_uris) { while (raptor_sequence_size(source_uris)) { raptor_uri *src = raptor_sequence_pop(source_uris); if (!st->fileUris && strstr(raptor_uri_as_string(src), "file:/")) { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, "File URIs are not permitted by configuration" ", ignoring '%s'", raptor_uri_as_string(graph->uri)); } else { librdf_uri *uri = librdf_new_uri(world, raptor_uri_as_string(src)); if (uri) { if (librdf_parser_parse_into_model(parser, uri, base_uri, model)) ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, "Failed to parse '%s' into model", librdf_uri_as_string(uri)); else ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, "Added '%s' to the model", librdf_uri_as_string(uri)); librdf_free_uri(uri); } } raptor_free_uri(src); } } librdf_free_parser(parser); return model; } static int sparql_handler(request_rec *r) { mod_sparql_state_t *st; unsigned char *ql_uri = NULL; unsigned char *query_string = NULL; char *rawQuery = NULL; char *tok = NULL, *lastTok = NULL; raptor_sequence *source_uris = NULL; apr_size_t query_len = 0; unsigned char *outputStr; librdf_uri *base_uri = NULL; librdf_model *model; librdf_query *query; librdf_parser *parser; librdf_query_results *results; /* If we haven't been selected, decline to do anything! */ if (strcmp(r->handler, SPARQL_HANDLER_NAME) != 0) return DECLINED; /* ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "sparql_handler()"); */ st = (mod_sparql_state_t *)ap_get_module_config(r->per_dir_config, &sparql_module); if (!st) return DECLINED; model = st->model; /* If the request was a POST, it must be of content-type * 'application/x-www-form-urlencoded'. */ if (r->method_number == M_POST) { const char *ctype = apr_table_get(r->headers_in, "Content-Type"); if (! ctype || strcasecmp(ctype, SPARQL_POST_CT)) { return sparql_error_response(r, HTTP_BAD_REQUEST, "Invalid encoding type, SPARQL POST submissions" " should be of type " SPARQL_POST_CT); } } /* Protocol says we should be UTF-8 encoded */ /* Don't enforce this yet! */ /* if (! r->content_encoding || strcasecmp(r->content_encoding, "UTF-8")) { return sparql_error_response(r, HTTP_BAD_REQUEST, "Invalid Content encoding, SPARQL " "requires UTF-8 encoding"); } */ if (r->method_number == M_GET) { if (! r->parsed_uri.query) { return sparql_error_response(r, HTTP_BAD_REQUEST, "QUERY expected"); } rawQuery = r->parsed_uri.query; } else if (r->method_number == M_POST) { apr_bucket_brigade *bb = NULL; apr_bucket *b = NULL; int seen_eos = 0; bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); do { apr_status_t rc; rc = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES, APR_BLOCK_READ, 4096); if (rc != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "Unable to get bucket brigade to process " "POST data"); break; } for (b = APR_BRIGADE_FIRST(bb); b != APR_BRIGADE_SENTINEL(bb); b = APR_BUCKET_NEXT(b)) { const char *data; apr_size_t len; if (APR_BUCKET_IS_EOS(b)) { seen_eos = 1; break; } if (APR_BUCKET_IS_METADATA(b)) continue; rc = apr_bucket_read(b, &data, &len, APR_BLOCK_READ); if (rc != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "Unable to read POST data from request"); rawQuery = NULL; break; } if (rawQuery) rawQuery = apr_pstrcat(r->pool, rawQuery, data, NULL); else rawQuery = (char *)data; } apr_brigade_cleanup(bb); } while (!seen_eos); apr_brigade_destroy(bb); } if (! rawQuery) { ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Failed to find a query in request!"); return sparql_error_response(r, HTTP_BAD_REQUEST, "No query found in request"); } /* Unescape query... */ /* TODO - look at ap_unescape_url() which didn't work */ if (query_decode(rawQuery, &query_len, rawQuery, strlen(rawQuery)) != 0) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Failed to parse query!"); return sparql_error_response(r, HTTP_BAD_REQUEST, "Invalid encoding found in query"); } tok = apr_strtok(rawQuery, "&", &lastTok); while (tok) { if (IS_QUERY(tok)) { query_string = (unsigned char *)(tok + QUERY_LEN); } else if (IS_DEF_URI(tok)) { base_uri = librdf_new_uri(world, (unsigned char *)(tok + DEFAULT_URI_LEN)); } else if (IS_SRC_URI(tok)) { raptor_uri *src_uri = NULL; if (!source_uris) source_uris = raptor_new_sequence( (raptor_sequence_free_handler *)raptor_free_uri, (raptor_sequence_print_handler *)raptor_sequence_print_uri); /* TODO - handle failure */ if (source_uris) { src_uri = raptor_new_uri((unsigned char *)(tok + SOURCE_URI_LEN)); if (src_uri) raptor_sequence_push(source_uris, src_uri); } } tok = apr_strtok(NULL, "&", &lastTok); } if (!st->store) { st->store = sparqlMakeStorage(st, 1); if (! st->store) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "Unable to make RDF storage!"); /* TODO - send correct error */ return DONE; } model = st->model; } /* Make sure we have some data to query, otherwise log error and * return DONE as we have failed. * TODO - we should return more meaningful information to user */ if (!model) { if (st->remote) model = fillStoreFromQuery(st, query_string, base_uri, source_uris); else { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "Unable to execute query.\nNo graph data " "configured and remote data usage not enabled."); return DONE; } } if (!model) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "No graph data configured and unable to create " "data store for query"); return DONE; } /* Build the query and execute it. */ query = librdf_new_query(world, st->ql_name, st->ql_uri, query_string, base_uri); results = librdf_model_query_execute(model, query); if (!results) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "Query execution failed"); goto tidy_exit; } ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "SPARQL query executed OK"); /* Set status to OK as we have data to return */ r->status = HTTP_OK; /* Set content-type and start output */ sparql_set_content_type(st, r); if (! st->outputUri && ! setOutputFormat(st, NULL)) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "No URI available for output format '%s'", outputFormats[st->resFormat].name); goto tidy_exit; } outputStr = librdf_query_results_to_string(results, st->outputUri, base_uri); if (outputStr != NULL) ap_rvputs(r, outputStr, NULL); else ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "No output created!"); return DONE; tidy_exit: if(!st->model) librdf_free_model(model); return DECLINED; } static const char *sparql_enable(cmd_parms *cmd, void *config, int mode) { mod_sparql_state_t *st = (mod_sparql_state_t *)config; if (st) st->enabled = mode; if (st->enabled) ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, "SPARQL queries enabled"); return(NULL); } static const char *sparql_allow_remote(cmd_parms *cmd, void *config, int mode) { mod_sparql_state_t *st = (mod_sparql_state_t *)config; if (st) st->remote = mode; if (st->remote) ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, "Remote SPARQL queries enabled"); return NULL; } static const char *sparql_allow_files(cmd_parms *cmd, void *config, int mode) { mod_sparql_state_t *st = (mod_sparql_state_t *)config; if (st) st->fileUris = mode; if (st->fileUris) ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, "Allowing 'file:///' URI's - this is dangerous!"); return NULL; } static const char *sparql_set_result_type(cmd_parms *cmd, void *config, const char *mode) { mod_sparql_state_t *st = (mod_sparql_state_t *)config; ap_log_error(APLOG_MARK, APLOG_INFO, 0, cmd->server, "SPARQL: result format requested to be '%s'", mode); if (setOutputFormat(st, mode) == 0) { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, "Unknown output format '%s' specified, using default", mode); setOutputFormat(st, defaultFormat); } return NULL; } static const char *sparql_set_store_name(cmd_parms *cmd, void *config, const char *mode) { mod_sparql_state_t *st = (mod_sparql_state_t *)config; ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, "SPARQL: setting RDF store name to '%s'", mode); st->st_name = apr_pstrdup(st->pool, mode); return NULL; } static const char *sparql_set_store_type(cmd_parms *cmd, void *config, const char *mode) { mod_sparql_state_t *st = (mod_sparql_state_t *)config; ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, "SPARQL: setting RDF store type to '%s'", mode); st->st_type = apr_pstrdup(st->pool, mode); return NULL; } static const char *sparql_add_store_uri(cmd_parms *cmd, void *config, const char *mode) { mod_sparql_state_t *st = (mod_sparql_state_t *)config; struct store_uri *u, *v; ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, "SPARQL: adding URI '%s' to store", mode); for (u = st->st_uris; u; u = u->next) { if (strcmp((char *)u->uri, mode) == 0) return NULL; } v = (struct store_uri *)apr_pcalloc(st->pool, sizeof(*v)); if (!v) { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, "Unable to add URI '%s'", mode); return NULL; } if (st->st_uris) { /* find end of queue */ for (u = st->st_uris; u->next; u = u->next) ; u->next = v; } else st->st_uris = v; v->uri = (const unsigned char *)apr_pstrdup(st->pool, mode); return NULL; } static const char *sparql_set_store_opts(cmd_parms *cmd, void *config, const char *mode) { mod_sparql_state_t *st = (mod_sparql_state_t *)config; ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, "SPARQL: setting RDF store options to '%s'", mode); st->st_opts = apr_pstrdup(st->pool, mode); return (NULL); } static int setQueryLanguage(mod_sparql_state_t *st, const char *mode) { unsigned int i = 0; const char *name = NULL; const char *label = NULL; unsigned const char *uri = NULL; if (st->ql_uri) { librdf_free_uri(st->ql_uri); st->ql_uri = NULL; } st->ql_name = NULL; if (mode) { while (rasqal_languages_enumerate(i, &name, &label, &uri) == 0) { if (strcasecmp(name, mode) == 0) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, "Found query language '%s' -> %s [%s]", name, label, uri); st->ql_name = apr_pstrdup(st->pool, name); st->ql_uri = librdf_new_uri(world, uri); return 1; } i++; } } /* Now try for default... */ i = 0; while (rasqal_languages_enumerate(i, &name, &label, &uri) == 0) { if (strcasecmp(name, defaultQL) == 0) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, "Found DEFAULT query language '%s' -> %s [%s]", name, label, uri); st->ql_name = apr_pstrdup(st->pool, name); st->ql_uri = librdf_new_uri(world, uri); return 1; } i++; } return 0; } static const char *sparql_set_language(cmd_parms *cmd, void *config, const char *mode) { mod_sparql_state_t *st = (mod_sparql_state_t *)config; if (! setQueryLanguage(st, mode)) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, "SPARQL: unknown query language '%s'", mode); } return (NULL); } static apr_status_t sparql_state_cleanup(void *dummy) { mod_sparql_state_t *st = (mod_sparql_state_t *)dummy; if (st) { if (st->outputUri) librdf_free_uri(st->outputUri); if (st->ql_uri) librdf_free_uri(st->ql_uri); if (st->store) librdf_free_storage(st->store); } return APR_SUCCESS; } static apr_status_t sparql_cleanup(void *dummy) { librdf_world *w = (librdf_world *)dummy; if (w) librdf_free_world(w); world = NULL; return APR_SUCCESS; } static int sparql_init_handler(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) { char *verString = NULL; world = librdf_new_world(); if (!world) return DECLINED; librdf_world_open(world); librdf_world_set_logger(world, NULL, rdfError); apr_pool_cleanup_register(p, world, sparql_cleanup, apr_pool_cleanup_null); verString = apr_psprintf(ptemp, "SPARQL/Redland%d.%d.%d", librdf_version_major, librdf_version_minor, librdf_version_release); ap_add_version_component(p, verString); /* TODO - correctly set the default output format */ return OK; } static int sparql_fixups(request_rec *r) { mod_sparql_state_t *st; /* Do we need to reject HTTP/0.9 requests? */ if (r->assbackwards && !r->main) return DECLINED; st = (mod_sparql_state_t *)ap_get_module_config(r->per_dir_config, &sparql_module); /* If SPARQL hasn't been enabled, decline the kind offer */ if (! st->enabled) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "SPARQL not enabled, declining"); return DECLINED; } /* If the method isn't GET, then decline the kind offer */ if (r->method_number != M_GET && r->method_number != M_POST) return DECLINED; r->handler = SPARQL_HANDLER_NAME; return OK; } static mod_sparql_state_t *makeSparqlState(apr_pool_t *p) { mod_sparql_state_t *st; st = (mod_sparql_state_t *)apr_pcalloc(p, sizeof(*st)); if (!st) return NULL; st->pool = p; setOutputFormat(st, defaultFormat); apr_pool_cleanup_register(p, st, sparql_state_cleanup, apr_pool_cleanup_null); return st; } static void *sparql_create_config(apr_pool_t *p, server_rec *s) { mod_sparql_state_t *st = makeSparqlState(p); if (!st) return NULL; setQueryLanguage(st, NULL); return st; } static void *sparql_dir_config(apr_pool_t *p, char *dir) { mod_sparql_state_t *st = makeSparqlState(p); if (!st) return NULL; setQueryLanguage(st, NULL); return st; } /* * p = parent * c = child * elm = element */ #define WHICH(p, c, elm) (c->elm ? c->elm : p->elm) static void *sparql_merge_config(apr_pool_t *p, void *basev, void *overridesv) { mod_sparql_state_t *st = apr_pcalloc(p, sizeof(*st)); mod_sparql_state_t *parent = (mod_sparql_state_t *) basev; mod_sparql_state_t *child = (mod_sparql_state_t *) overridesv; st = makeSparqlState(p); if (!st) return NULL; st->enabled = WHICH(parent, child, enabled); setQueryLanguage(st, WHICH(parent, child, ql_name)); // st->resFormat = WHICH(parent, child, resFormat); return st; } static void copyStore(mod_sparql_state_t *dst, mod_sparql_state_t *src, apr_pool_t *p) { struct store_uri *u, *v, *w = NULL; dst->st_name = apr_pstrdup(p, src->st_name); dst->st_type = apr_pstrdup(p, src->st_type); dst->st_opts = apr_pstrdup(p, src->st_opts); for (u = src->st_uris; u; u = u->next) { v = (struct store_uri *)apr_pcalloc(p, sizeof(*v)); if (!v) break; v->uri = (const unsigned char *)apr_pstrdup(p, u->uri); if (w) w->next = v; else dst->st_uris = v; w = v; } } static void *sparql_merge_dir_config(apr_pool_t *p, void *basev, void *overridesv) { mod_sparql_state_t *st = NULL; mod_sparql_state_t *parent = (mod_sparql_state_t *) basev; mod_sparql_state_t *child = (mod_sparql_state_t *) overridesv; st = makeSparqlState(p); if (!st) return NULL; st->enabled = WHICH(parent, child, enabled); st->remote = WHICH(parent, child, remote); st->fileUris = WHICH(parent, child, fileUris); /* Handle merging the store information. This comprises * name, type and options but can also mean that we have * URI's configured to use as the data source. We therefore * need to copy all of these... */ if (child->st_name || child->st_type) copyStore(st, child, p); else if (parent->st_name || parent->st_type) copyStore(st, parent, p); setQueryLanguage(st, WHICH(parent, child, ql_name)); // setOutputFormat(st, WHICH(parent, child, resFormat)); return st; } static const command_rec sparql_cmds[] = { AP_INIT_FLAG("Sparql", sparql_enable, NULL, RSRC_CONF|ACCESS_CONF, "Allow processing of SPARQL queries"), AP_INIT_TAKE1("SparqlLanguage", sparql_set_language, NULL, RSRC_CONF|ACCESS_CONF, "Set the Query Language for queries ('rdql' or 'sparql')" " [default is 'sparql']"), AP_INIT_TAKE1("SparqlResultType", sparql_set_result_type, NULL, RSRC_CONF|ACCESS_CONF, "Set type of data to return from succesful queries"), AP_INIT_TAKE1("SparqlStoreType", sparql_set_store_type, NULL, RSRC_CONF|ACCESS_CONF, "Set the RDF storage type to be used for SPARQL queries"), AP_INIT_TAKE1("SparqlStoreName", sparql_set_store_name, NULL, RSRC_CONF|ACCESS_CONF, "Set the RDF storage name to be used for SPARQL queries"), AP_INIT_TAKE1("SparqlStoreOptions", sparql_set_store_opts, NULL, RSRC_CONF|ACCESS_CONF, "Set the RDF storage options string to be used"), AP_INIT_TAKE1("SparqlStoreURI", sparql_add_store_uri, NULL, RSRC_CONF|ACCESS_CONF, "Use the URI provided as part of data store"), AP_INIT_FLAG("SparqlAllowRemote", sparql_allow_remote, NULL, RSRC_CONF|ACCESS_CONF, "Allow remote URI's to be specified in queries" " [default is Off]"), AP_INIT_FLAG("SparqlAllowFileURI", sparql_allow_files, NULL, RSRC_CONF|ACCESS_CONF, "Allow access to file:/// URI's" " [default is Off]"), {NULL} }; static void register_hooks(apr_pool_t *p) { ap_hook_handler(sparql_handler, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_post_config(sparql_init_handler, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_fixups(sparql_fixups, NULL, NULL, APR_HOOK_MIDDLE); } module AP_MODULE_DECLARE_DATA sparql_module = { STANDARD20_MODULE_STUFF, sparql_dir_config, /* create per-directory config structure */ sparql_merge_dir_config, /* merge per-directory config structures */ sparql_create_config, /* create per-server config structure */ sparql_merge_config, /* merge per-server config structures */ sparql_cmds, /* command apr_table_t */ register_hooks };