/* * Copyright (C) 2016 Robin Krahl * * dbfp.c */ #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) return EINVAL; dbfp->key = strdup(key); if (!dbfp->key) return ENOMEM; return 0; } void dbfp_close(struct dbfp *dbfp) { if (!dbfp) return; 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) { }