From 1648e81474b552ba499102e13f9f3ed863501757 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Tue, 8 Mar 2016 04:43:39 +0100 Subject: add rudimentary location support The function dbfp_query_location_name performs the location.name query and returns a list of all stations that match the search term. The implementation has two flaws: there is no URL encoding, and malformatted query results will not lead to a meaningful error message. There are two new test cases: basic tests some basic functionality, as setting the API key and accessing the API. location adds a simple test for the location.name query. --- dbfp.c | 210 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 210 insertions(+) (limited to 'dbfp.c') diff --git a/dbfp.c b/dbfp.c index 0bba365..41e8abc 100644 --- a/dbfp.c +++ b/dbfp.c @@ -6,10 +6,36 @@ #include "dbfp.h" +#include #include +#include +#include #include #include +#define DBFP_BASE_URL "http://open-api.bahn.de/bin/rest.exe" +#define DBFP_URL_FORMAT "%s/%s?authKey=%s&%s" +#define DBFP_URL_LEN 8000 + +struct parse_data { + struct dbfp_status *status; + void *data; +}; + +struct loc_data { + size_t n; + struct dbfp_location *locs; +}; + +static int dbfp_parse(void *contents, size_t len, size_t n, void *data); +static void dbfp_request(struct dbfp *dbfp, char *service, char *query, + XML_Parser parser, struct dbfp_status *status); +static int dbfp_url(struct dbfp *dbfp, char *service, char *query, char **url); + +static void handle_loc(struct loc_data *ld, const char **atts); +static void loc_ele_start(void *data, const char *name, const char **atts); +static void loc_ele_end(void *data, const char *name); + int dbfp_init(struct dbfp *dbfp, char *key) { if (!dbfp || !key) @@ -29,3 +55,187 @@ void dbfp_close(struct dbfp *dbfp) free(dbfp->key); } + +struct dbfp_status dbfp_query_location_name(struct dbfp *dbfp, char *input, + size_t *n, struct dbfp_location **out) +{ + struct dbfp_status status = { 0, 0, 0, 0, 0 }; + char *query = NULL; + int len; + struct parse_data pd; + struct loc_data ld; + XML_Parser parser = NULL; + + if (!dbfp || !input) { + status.run_error = EINVAL; + goto cleanup; + } + + /* + * TODO(robin.krahl): improve query building and implement HTML + * encoding. + */ + query = malloc(DBFP_URL_LEN); + if (!query) { + status.run_error = ENOMEM; + goto cleanup; + } + len = snprintf(query, DBFP_URL_LEN, "input=%s", input); + if (len < 0 || len >= DBFP_URL_LEN) { + status.run_error = -1; + goto cleanup; + } + + pd.status = &status; + pd.data = &ld; + ld.n = 0; + ld.locs = NULL; + + parser = XML_ParserCreateNS(NULL, '\0'); + XML_SetUserData(parser, &pd); + XML_SetElementHandler(parser, loc_ele_start, loc_ele_end); + + dbfp_request(dbfp, "location.name", query, parser, &status); + + if (ld.n && ld.locs) { + if (n) + *n = ld.n; + if (out) + *out = ld.locs; + else + free(ld.locs); + } else { + free(ld.locs); + } + +cleanup: + XML_ParserFree(parser); + free(query); + + if (status.run_error || status.parse_error || status.curl_error || + status.api_error) + status.error = 1; + + return status; +} + +static int dbfp_parse(void *contents, size_t len, size_t n, void *data) +{ + XML_Parser parser = (XML_Parser)data; + struct parse_data *pd; + size_t size = len * n; + + pd = (struct parse_data *)XML_GetUserData(parser); + + if (!pd->status->parse_error && !XML_Parse(parser, contents, size, 0)) { + pd->status->parse_error = XML_GetErrorCode(parser); + } + + return size; +} + +static void dbfp_request(struct dbfp *dbfp, char *service, char *query, + XML_Parser parser, struct dbfp_status *status) +{ + char *url; + CURL *curl = NULL; + + status->run_error = dbfp_url(dbfp, service, query, &url); + if (status->run_error) + goto cleanup; + + curl_global_init(CURL_GLOBAL_ALL ^ CURL_GLOBAL_SSL); + curl = curl_easy_init(); + if (!curl) { + status->run_error = -1; + goto cleanup; + } + + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, dbfp_parse); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)parser); + + status->curl_error = curl_easy_perform(curl); + + if (status->parse_error) + fprintf(stderr, "Parse error %d: %s\n", status->parse_error, + XML_ErrorString(status->parse_error)); + +cleanup: + if (curl) + curl_easy_cleanup(curl); + curl_global_cleanup(); + free(url); +} + +static int dbfp_url(struct dbfp *dbfp, char *service, char *query, char **url) +{ + int len; + + *url = malloc(DBFP_URL_LEN); + if (!*url) + return ENOMEM; + + len = snprintf(*url, DBFP_URL_LEN, DBFP_URL_FORMAT, DBFP_BASE_URL, + service, dbfp->key, query); + if (len < 0 || len >= DBFP_URL_LEN) { + free(*url); + *url = NULL; + return -1; + } + + return 0; +} + +static void handle_loc(struct loc_data *ld, const char **atts) +{ + char *name = NULL; + char *id = NULL; + size_t i; + + /* + * TODO(robin.krahl): fix error handling for the cases that a) + * name or id is not set and b) realloc fails. + */ + + for (i = 0; atts[i]; i += 2) { + if (strcmp(atts[i], "name") == 0) + name = strdup(atts[i + 1]); + else if (strcmp(atts[i], "id") == 0) + id = strdup(atts[i + 1]); + } + + if (name && id) { + ld->locs = realloc(ld->locs, (ld->n + 1) * sizeof(*ld->locs)); + if (ld->locs) { + ld->n++; + ld->locs[ld->n - 1].name = name; + ld->locs[ld->n - 1].id = id; + } + } else { + free(name); + free(id); + } +} + +static void loc_ele_start(void *data, const char *name, const char **atts) +{ + struct parse_data *pd = (struct parse_data *)data; + struct loc_data *ld = (struct loc_data *)pd->data; + size_t i; + + if (strcmp(name, "Error") == 0) { + for (i = 0; atts[i]; i += 2) { + if (strcmp(atts[i], "code")) { + fprintf(stderr, "err code %s\n", atts[i + 1]); + break; + } + } + } else if (strcmp(name, "StopLocation") == 0) { + handle_loc(ld, atts); + } +} + +static void loc_ele_end(void *data, const char *name) +{ +} -- cgit v1.2.1