summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorErich Eckner <git@eckner.net>2018-10-25 16:19:37 +0200
committerErich Eckner <git@eckner.net>2018-10-25 16:19:37 +0200
commitca5cdd2bd007d3e30ca0d01e3ea10011051731bb (patch)
treeb9b4ffaff1d05df8ffd42ee3cef3574d7a8c43a4
parent27138a87d4f47cba9687d6c5c4dc90addd26353e (diff)
downloadanzeige-ca5cdd2bd007d3e30ca0d01e3ea10011051731bb.tar.xz
input_gadgets: gadgets_retrieve_weather_warnings() new
-rw-r--r--anzeige.c2
-rw-r--r--input_gadgets.c505
-rw-r--r--input_gadgets.h14
3 files changed, 515 insertions, 6 deletions
diff --git a/anzeige.c b/anzeige.c
index cd6dbf0..8d39317 100644
--- a/anzeige.c
+++ b/anzeige.c
@@ -51,7 +51,7 @@ int main(int argc, char **argv)
}
memset(text_buffer, 0, TEXT_BUFFER_LENGTH);
- ret_val = gadgets_retrieve_weather_forecast(text_buffer,TEXT_BUFFER_LENGTH);
+ ret_val = gadgets_retrieve_weather_warnings(text_buffer,TEXT_BUFFER_LENGTH);
if (ret_val) {
free(text_buffer);
exit(ret_val);
diff --git a/input_gadgets.c b/input_gadgets.c
index 0426c56..2abd7a9 100644
--- a/input_gadgets.c
+++ b/input_gadgets.c
@@ -1,3 +1,4 @@
+#define _XOPEN_SOURCE
#include <string.h>
#include <stdlib.h>
#include <curl/curl.h>
@@ -7,16 +8,18 @@
#include <libxml/tree.h>
#include <time.h>
#include <math.h>
+#include <zip.h>
#include "input_gadgets.h"
#define fh_temperature_regex "<strong>\\s*\n\\s*Wetterdaten vom ([0-9.]{10}) um ([0-9:]{5}) MEZ</strong>(.|\n)*>Temperatur</td>\\s*\n\\s*<td [^>]*><strong>([^<]*)</strong>"
+#define wetter_warnung_filename_regex "^2\\.49\\.0\\.1\\.276\\.0\\.DWD\\.PVW\\.([0-9]+)\\."
static size_t
WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
size_t realsize = size * nmemb;
- struct MemoryStruct *mem = (struct MemoryStruct *)userp;
+ MemoryStruct *mem = (MemoryStruct *)userp;
char *ptr = realloc(mem->memory, mem->size + realsize + 1);
if(ptr == NULL) {
@@ -37,7 +40,7 @@ int gadgets_retrieve_current_temperature(unsigned char *output, int max_len)
{
CURL *curl_handle;
CURLcode res;
- struct MemoryStruct chunk;
+ MemoryStruct chunk;
int ret_val;
chunk.memory = malloc(1);
@@ -109,11 +112,20 @@ xmlNode *xml_find_node_by_name(xmlNode *cur_node, char *name)
return NULL;
}
+xmlChar *xml_extract_string(xmlDoc *doc, xmlNode *cur_node, char *name)
+{
+ xmlNode *sub_node = xml_find_node_by_name(cur_node, name);
+ if (! sub_node)
+ return NULL;
+ else
+ return xmlNodeListGetString(doc, sub_node -> xmlChildrenNode, 0);
+}
+
int gadgets_retrieve_weather_forecast(unsigned char *output, int max_len)
{
CURL *curl_handle;
CURLcode res;
- struct MemoryStruct chunk;
+ MemoryStruct chunk;
int ret_val;
chunk.memory = malloc(1);
@@ -218,3 +230,490 @@ int gadgets_retrieve_weather_forecast(unsigned char *output, int max_len)
return EXIT_SUCCESS;
}
+
+int gadgets_retrieve_weather_warnings(unsigned char *output, int max_len)
+{
+ CURL *curl_handle;
+ CURLcode res;
+ MemoryStruct chunk;
+ int ret_val;
+
+ chunk.memory = malloc(1);
+ chunk.size = 0;
+
+ curl_global_init(CURL_GLOBAL_ALL);
+
+ curl_handle = curl_easy_init();
+ if (!curl_handle) {
+ perror("Failed to init curl");
+ free(chunk.memory);
+ return EXIT_FAILURE;
+ }
+ curl_easy_setopt(curl_handle, CURLOPT_URL, "https://opendata.dwd.de/weather/alerts/cap/COMMUNEUNION_DWD_STAT/Z_CAP_C_EDZW_LATEST_PVW_STATUS_PREMIUMDWD_COMMUNEUNION_DE.zip");
+ curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L);
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);
+ curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
+ res = curl_easy_perform(curl_handle);
+ curl_easy_cleanup(curl_handle);
+ if (res != CURLE_OK) {
+ fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
+ free(chunk.memory);
+ return EXIT_FAILURE;
+ }
+
+ zip_error_t error;
+ zip_source_t *src;
+ zip_t *za;
+ zip_file_t *zf;
+ zip_stat_t zs;
+
+ zip_error_init(&error);
+ if ((src = zip_source_buffer_create(chunk.memory, chunk.size, 1, &error)) == NULL) {
+ fprintf(stderr, "can't create zip source: %s\n", zip_error_strerror(&error));
+ zip_error_fini(&error);
+ free(chunk.memory);
+ return EXIT_FAILURE;
+ }
+ if ((za = zip_open_from_source(src, 0, &error)) == NULL) {
+ fprintf(stderr, "can't open zip from source: %s\n", zip_error_strerror(&error));
+ zip_source_free(src);
+ zip_error_fini(&error);
+ free(chunk.memory);
+ return EXIT_FAILURE;
+ }
+ zip_error_fini(&error);
+
+ LIBXML_TEST_VERSION
+
+ int warnings_len = 0;
+ warning_t *warnings = malloc(sizeof(warning_t));
+ if (warnings == NULL) {
+ fprintf(stderr, "malloc failed allocating %d bytes\n", sizeof(warning_t));
+ free(chunk.memory);
+ return EXIT_FAILURE;
+ }
+
+ for (int i=0; i<zip_get_num_entries(za, 0); i++) {
+ zip_stat_index(za, i, 0, &zs);
+ zf = zip_fopen_index(za, i, 0);
+
+ void *inflated = malloc(zs.size);
+ if (inflated == NULL) {
+ fprintf(stderr, "malloc failed allocating %d bytes\n", zs.size);
+ free(chunk.memory);
+ for (int i=0; i<warnings_len; i++) {
+ free(warnings[i].headline);
+ free(warnings[i].event);
+ }
+ free(warnings);
+ return EXIT_FAILURE;
+ }
+ int read = zip_fread(zf, inflated, zs.size);
+ zip_fclose(zf);
+ if (read != zs.size) {
+ fprintf(stderr, "failed reading %d bytes from zip - read %d instead\n", zs.size, read);
+ free(inflated);
+ free(chunk.memory);
+ for (int i=0; i<warnings_len; i++) {
+ free(warnings[i].headline);
+ free(warnings[i].event);
+ }
+ free(warnings);
+ return EXIT_FAILURE;
+ }
+
+ xmlDocPtr doc;
+ doc = xmlReadMemory(inflated, zs.size, "noname.xml", NULL, 0);
+ if (doc == NULL) {
+ fprintf(stderr, "Failed to parse document\n");
+ free(inflated);
+ free(chunk.memory);
+ for (int i=0; i<warnings_len; i++) {
+ free(warnings[i].headline);
+ free(warnings[i].event);
+ }
+ free(warnings);
+ return EXIT_FAILURE;
+ }
+
+ xmlNode *cur_node = NULL;
+ xmlNode *sub_node = NULL;
+ xmlChar *text = NULL;
+
+ cur_node = xmlDocGetRootElement(doc);
+ if (cur_node == NULL) {
+ fprintf(stderr, "Failed to reach root node of xml\n");
+ free(inflated);
+ free(chunk.memory);
+ for (int i=0; i<warnings_len; i++) {
+ free(warnings[i].headline);
+ free(warnings[i].event);
+ }
+ free(warnings);
+ xmlFreeDoc(doc);
+ return EXIT_FAILURE;
+ }
+ cur_node = xml_find_node_by_name(cur_node, "alert");
+ if (cur_node == NULL) {
+ fprintf(stderr, "Failed to enter <alert>\n");
+ free(inflated);
+ free(chunk.memory);
+ for (int i=0; i<warnings_len; i++) {
+ free(warnings[i].headline);
+ free(warnings[i].event);
+ }
+ free(warnings);
+ xmlFreeDoc(doc);
+ return EXIT_FAILURE;
+ }
+ cur_node = cur_node -> children;
+ text = xml_extract_string(doc, cur_node, "status");
+ if (text == NULL) {
+ fprintf(stderr, "Failed to enter <status>\n");
+ free(inflated);
+ free(chunk.memory);
+ for (int i=0; i<warnings_len; i++) {
+ free(warnings[i].headline);
+ free(warnings[i].event);
+ }
+ free(warnings);
+ xmlFreeDoc(doc);
+ return EXIT_FAILURE;
+ }
+ if (strcmp(text, "Actual") != 0) { // Warning is not an Actual warning
+ printf("no warning, but '%s'\n",text);
+ xmlFree(text);
+ free(inflated);
+ xmlFreeDoc(doc);
+ continue;
+ }
+ xmlFree(text);
+ text = xml_extract_string(doc, cur_node, "msgType");
+ if (text == NULL) {
+ fprintf(stderr, "Failed to enter <msgType>\n");
+ free(inflated);
+ free(chunk.memory);
+ for (int i=0; i<warnings_len; i++) {
+ free(warnings[i].headline);
+ free(warnings[i].event);
+ }
+ free(warnings);
+ xmlFreeDoc(doc);
+ return EXIT_FAILURE;
+ }
+ if (strcmp(text, "Update") == 0)
+ warnings[warnings_len].is_update=1;
+ else if (strcmp(text, "Alert") == 0)
+ warnings[warnings_len].is_update=0;
+ else if (strcmp(text, "Cancel") == 0)
+ warnings[warnings_len].is_update=-1;
+ else
+ warnings[warnings_len].is_update=-2;
+ xmlFree(text);
+ if (warnings[warnings_len].is_update < 0) {
+ free(inflated);
+ if (warnings[warnings_len].is_update == -1) // "Cancel"
+ continue;
+ fprintf(stderr, "<msgType> is neither 'Alert' nor 'Cancel' nor 'Update'\n");
+ free(chunk.memory);
+ for (int i=0; i<warnings_len; i++) {
+ free(warnings[i].headline);
+ free(warnings[i].event);
+ }
+ free(warnings);
+ xmlFreeDoc(doc);
+ return EXIT_FAILURE;
+ }
+ cur_node = xml_find_node_by_name(cur_node, "info");
+ if (cur_node == NULL) {
+ fprintf(stderr, "Failed to enter <info>\n");
+ free(inflated);
+ free(chunk.memory);
+ for (int i=0; i<warnings_len; i++) {
+ free(warnings[i].headline);
+ free(warnings[i].event);
+ }
+ free(warnings);
+ xmlFreeDoc(doc);
+ return EXIT_FAILURE;
+ }
+ cur_node = cur_node -> children;
+ text = xml_extract_string(doc, cur_node, "category");
+ if (!text) {
+ fprintf(stderr, "Failed to enter <category> %s\n",zs.name);
+ free(inflated);
+ free(chunk.memory);
+ for (int i=0; i<warnings_len; i++) {
+ free(warnings[i].headline);
+ free(warnings[i].event);
+ }
+ free(warnings);
+ xmlFreeDoc(doc);
+ return EXIT_FAILURE;
+ }
+ if (strcmp(text,"Met") == 0)
+ warnings[warnings_len].category = 0;
+ else if (strcmp(text,"Health") == 0)
+ warnings[warnings_len].category = 1;
+ else
+ warnings[warnings_len].category = -1;
+ xmlFree(text);
+ if (warnings[warnings_len].category == -1) {
+ fprintf(stderr, "<category> is neither 'Met' nor 'Health'\n");
+ free(chunk.memory);
+ free(inflated);
+ for (int i=0; i<warnings_len; i++) {
+ free(warnings[i].headline);
+ free(warnings[i].event);
+ }
+ free(warnings);
+ xmlFreeDoc(doc);
+ return EXIT_FAILURE;
+ }
+ text = xml_extract_string(doc, cur_node, "responseType");
+ if (!text) {
+ fprintf(stderr, "Failed to enter <responseType> %s\n",zs.name);
+ free(inflated);
+ free(chunk.memory);
+ for (int i=0; i<warnings_len; i++) {
+ free(warnings[i].headline);
+ free(warnings[i].event);
+ }
+ free(warnings);
+ xmlFreeDoc(doc);
+ return EXIT_FAILURE;
+ }
+ if ((strcmp(text,"Prepare") != 0) && (strcmp(text,"None")!=0)) {
+ fprintf(stderr, "responseType is '%s', not Prepare nor None\n", text);
+ xmlFree(text);
+ xmlFreeDoc(doc);
+ continue;
+ }
+ xmlFree(text);
+ if (warnings[warnings_len].category == -1) {
+ fprintf(stderr, "<category> is neither 'Met' nor 'Health'\n");
+ free(chunk.memory);
+ free(inflated);
+ for (int i=0; i<warnings_len; i++) {
+ free(warnings[i].headline);
+ free(warnings[i].event);
+ }
+ free(warnings);
+ xmlFreeDoc(doc);
+ return EXIT_FAILURE;
+ }
+ text = xml_extract_string(doc, cur_node, "severity");
+ if (!text) {
+ fprintf(stderr, "Failed to enter <severity> %s\n",zs.name);
+ free(inflated);
+ free(chunk.memory);
+ for (int i=0; i<warnings_len; i++) {
+ free(warnings[i].headline);
+ free(warnings[i].event);
+ }
+ free(warnings);
+ xmlFreeDoc(doc);
+ return EXIT_FAILURE;
+ }
+ if (strcmp(text,"Minor") == 0)
+ warnings[warnings_len].severity = 0;
+ else if (strcmp(text,"Moderate") == 0)
+ warnings[warnings_len].severity = 1;
+ else if (strcmp(text,"Severe") == 0)
+ warnings[warnings_len].severity = 2;
+ else if (strcmp(text,"Extreme") == 0)
+ warnings[warnings_len].severity = 3;
+ else {
+ fprintf(stderr, "Unknown severity '%s'\n",text);
+ xmlFree(text);
+ free(inflated);
+ free(chunk.memory);
+ for (int i=0; i<warnings_len; i++) {
+ free(warnings[i].headline);
+ free(warnings[i].event);
+ }
+ free(warnings);
+ xmlFreeDoc(doc);
+ return EXIT_FAILURE;
+ }
+ xmlFree(text);
+ struct tm time_struct;
+ text = xml_extract_string(doc, cur_node, "onset");
+ if (!text) {
+ fprintf(stderr, "Failed to enter <onset> %s\n",zs.name);
+ free(inflated);
+ free(chunk.memory);
+ for (int i=0; i<warnings_len; i++) {
+ free(warnings[i].headline);
+ free(warnings[i].event);
+ }
+ free(warnings);
+ xmlFreeDoc(doc);
+ return EXIT_FAILURE;
+ }
+ if (strptime(text,"%FT%T+",&time_struct) == NULL) {
+ fprintf(stderr, "Failed to parse time '%s'\n",text);
+ xmlFree(text);
+ free(inflated);
+ free(chunk.memory);
+ for (int i=0; i<warnings_len; i++) {
+ free(warnings[i].headline);
+ free(warnings[i].event);
+ }
+ free(warnings);
+ xmlFreeDoc(doc);
+ return EXIT_FAILURE;
+ }
+ xmlFree(text);
+ warnings[warnings_len].onset = mktime(&time_struct);
+ text = xml_extract_string(doc, cur_node, "expires");
+ if (text) {
+ if (strptime(text,"%FT%T+",&time_struct) == NULL) {
+ fprintf(stderr, "Failed to parse time '%s'\n",text);
+ xmlFree(text);
+ free(inflated);
+ free(chunk.memory);
+ for (int i=0; i<warnings_len; i++) {
+ free(warnings[i].headline);
+ free(warnings[i].event);
+ }
+ free(warnings);
+ xmlFreeDoc(doc);
+ return EXIT_FAILURE;
+ }
+ xmlFree(text);
+ warnings[warnings_len].expires = mktime(&time_struct);
+ } else
+ warnings[warnings_len].expires = LONG_MAX;
+
+ int is_relevant = 0;
+ for (sub_node = cur_node; sub_node = xml_find_node_by_name(sub_node, "area"); sub_node = sub_node -> next) {
+ xmlNode *sub_sub_node = xml_find_node_by_name(sub_node -> children, "geocode");
+ if (sub_sub_node == NULL) // no geocode available - probably a polygon <<TODO>>
+ continue;
+ text = xml_extract_string(doc, sub_sub_node -> children, "valueName");
+ if (text == NULL) {
+ fprintf(stderr, "Failed to enter <valueName>\n");
+ free(inflated);
+ free(chunk.memory);
+ for (int i=0; i<warnings_len; i++) {
+ free(warnings[i].headline);
+ free(warnings[i].event);
+ }
+ free(warnings);
+ xmlFreeDoc(doc);
+ return EXIT_FAILURE;
+ }
+ if (strcmp(text,"WARNCELLID")!=0) {
+ xmlFree(text);
+ continue;
+ }
+ xmlFree(text);
+ text = xml_extract_string(doc, sub_sub_node -> children, "value");
+ if (text == NULL) {
+ fprintf(stderr, "Failed to enter <value>\n");
+ free(inflated);
+ free(chunk.memory);
+ for (int i=0; i<warnings_len; i++) {
+ free(warnings[i].headline);
+ free(warnings[i].event);
+ }
+ free(warnings);
+ xmlFreeDoc(doc);
+ return EXIT_FAILURE;
+ }
+ if (strcmp(text,"816053000")==0) // Stadt Jena
+ is_relevant = 1;
+ xmlFree(text);
+ }
+ if (!is_relevant)
+ continue;
+
+ text = xml_extract_string(doc, cur_node, "headline");
+ if (text == NULL) {
+ fprintf(stderr, "Failed to enter <headline>\n");
+ free(inflated);
+ free(chunk.memory);
+ for (int i=0; i<warnings_len; i++) {
+ free(warnings[i].headline);
+ free(warnings[i].event);
+ }
+ free(warnings);
+ xmlFreeDoc(doc);
+ return EXIT_FAILURE;
+ }
+ warnings[warnings_len].headline = malloc(strlen(text)+1);
+ if (warnings[warnings_len].headline == NULL) {
+ fprintf(stderr, "malloc failed allocating %d bytes\n", strlen(text)+1);
+ free(inflated);
+ for (int i=0; i<warnings_len; i++) {
+ free(warnings[i].headline);
+ free(warnings[i].event);
+ }
+ free(warnings);
+ free(chunk.memory);
+ xmlFreeDoc(doc);
+ return EXIT_FAILURE;
+ }
+ strcpy(warnings[warnings_len].headline, text);
+ xmlFree(text);
+
+ text = xml_extract_string(doc, cur_node, "event");
+ if (text == NULL) {
+ fprintf(stderr, "Failed to enter <event>\n");
+ free(inflated);
+ free(chunk.memory);
+ for (int i=0; i<warnings_len; i++) {
+ free(warnings[i].headline);
+ free(warnings[i].event);
+ }
+ free(warnings[warnings_len].headline);
+ free(warnings);
+ xmlFreeDoc(doc);
+ return EXIT_FAILURE;
+ }
+ warnings[warnings_len].event = malloc(strlen(text)+1);
+ if (warnings[warnings_len].event == NULL) {
+ fprintf(stderr, "malloc failed allocating %d bytes\n", strlen(text)+1);
+ free(inflated);
+ for (int i=0; i<warnings_len; i++) {
+ free(warnings[i].headline);
+ free(warnings[i].event);
+ }
+ free(warnings[warnings_len].headline);
+ free(warnings);
+ free(chunk.memory);
+ xmlFreeDoc(doc);
+ return EXIT_FAILURE;
+ }
+ strcpy(warnings[warnings_len].event, text);
+ xmlFree(text);
+
+ warnings = (warning_t *)realloc(warnings, (++warnings_len + 1) * sizeof(warning_t));
+ if (warnings == NULL) {
+ fprintf(stderr, "realloc failed allocating %d bytes\n", (warnings_len + 1) * sizeof(warning_t));
+ free(inflated);
+ for (int i=0; i<warnings_len; i++) {
+ free(warnings[i].headline);
+ free(warnings[i].event);
+ }
+ free(warnings);
+ free(chunk.memory);
+ xmlFreeDoc(doc);
+ return EXIT_FAILURE;
+ }
+ free(inflated);
+ xmlFreeDoc(doc);
+ }
+ free(chunk.memory);
+
+ for (int i=0; i<warnings_len; i++) {
+ snprintf(output, max_len, "%s: %s ", warnings[i].headline, warnings[i].event);
+
+ free(warnings[i].headline);
+ free(warnings[i].event);
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/input_gadgets.h b/input_gadgets.h
index 002d9ef..ec6aab3 100644
--- a/input_gadgets.h
+++ b/input_gadgets.h
@@ -1,7 +1,17 @@
-struct MemoryStruct {
+typedef struct {
unsigned char *memory;
size_t size;
-};
+} MemoryStruct;
+
+typedef struct {
+ int is_update;
+ int category; // 0: Met; 1: Health
+ char *event;
+ char *headline;
+ int severity;
+ time_t onset, expires;
+} warning_t;
int gadgets_retrieve_current_temperature(unsigned char *output, int max_len);
int gadgets_retrieve_weather_forecast(unsigned char *output, int max_len);
+int gadgets_retrieve_weather_warnings(unsigned char *output, int max_len);