diff options
-rw-r--r-- | anzeige.c | 2 | ||||
-rw-r--r-- | input_gadgets.c | 505 | ||||
-rw-r--r-- | input_gadgets.h | 14 |
3 files changed, 515 insertions, 6 deletions
@@ -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); |