aboutsummaryrefslogtreecommitdiff
path: root/dbfp.c
diff options
context:
space:
mode:
Diffstat (limited to 'dbfp.c')
-rw-r--r--dbfp.c210
1 files changed, 210 insertions, 0 deletions
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 <curl/curl.h>
#include <errno.h>
+#include <expat.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#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)
+{
+}