#define _XOPEN_SOURCE #include #include #include #include #include #include #include #include #include #include #include "input_gadgets.h" #define fh_temperature_regex "\\s*\n\\s*Wetterdaten vom ([0-9]{2}\\.[0-9]{2}\\.[0-9]{4} um [0-9]{2}:[0-9]{2} MEZ)(.|\n)*>Temperatur\\s*\n\\s*]*>([^<]*)" #define wetter_warnung_filename_regex "^2\\.49\\.0\\.1\\.276\\.0\\.DWD\\.PVW\\.([0-9]+)\\." #define warnings_future_timespan (60*60*10) static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) { size_t realsize = size * nmemb; MemoryStruct *mem = (MemoryStruct *)userp; char *ptr = realloc(mem->memory, mem->size + realsize + 1); if(ptr == NULL) { /* out of memory! */ printf("not enough memory (realloc returned NULL)\n"); return 0; } mem->memory = ptr; memcpy(&(mem->memory[mem->size]), contents, realsize); mem->size += realsize; mem->memory[mem->size] = 0; return realsize; } int point_is_inside_polygon(double lat, double lon, char *text) { char *input = malloc(strlen(text)+1); if (input == NULL) { fprintf(stderr, "malloc failed to allocate %d bytes\n", strlen(text)+1); return -1; } strcpy(input, text); double *lats = malloc(1); if (lats == NULL) { fprintf(stderr, "malloc failed to allocate 1 byte\n"); free(input); return -1; } double *lons = malloc(1); if (lons == NULL) { fprintf(stderr, "malloc failed to allocate 1 byte\n"); free(lats); free(input); return -1; } int len = 0; int alloc_len = 0; char *q = NULL; char *p = strtok_r(input, ",", &q); while (p != NULL) { if (alloc_len<=len) { double *n; alloc_len = len + 16; n = realloc(lons, alloc_len*sizeof(lons[0])); if (n == NULL) { fprintf(stderr, "malloc failed to allocate %d byte\n", alloc_len*sizeof(lons[0])); free(input); free(lats); free(lons); return -1; } lons = n; n = realloc(lats, alloc_len*sizeof(lats[0])); if (n == NULL) { fprintf(stderr, "malloc failed to allocate %d byte\n", alloc_len*sizeof(lats[0])); free(input); free(lats); free(lons); return -1; } lats = n; } lats[len] = atof(p); p=strtok_r(NULL, " ", &q); if (p == NULL) { fprintf(stderr, "point_is_inside_polygon misses a space\n"); free(input); free(lats); free(lons); return -1; } lons[len] = atof(p); p=strtok_r(NULL, ",", &q); len++; } free(input); if ((lons[0] != lons[len-1]) || (lats[0] != lats[len-1])) { fprintf(stderr, "point_is_inside_polygon was given a non-closed polygon border\n"); free(lats); free(lons); return -1; } int is_inside = 0; for (int i=0; i lats[i]) && (lat >= lats[i+1])) continue; if (lats[i] == lats[i+1]) continue; if ((lat - lats[i]) / (lats[i+1] - lats[i]) * (lons[i+1] - lons[i]) + lons[i] < lon) is_inside = 1 - is_inside; } free(lats); free(lons); return is_inside; } char *gadgets_retrieve_current_temperature(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 NULL; } curl_easy_setopt(curl_handle, CURLOPT_URL, "https://wetter.mb.fh-jena.de/station/datenbank/php_giese/online.php"); 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(%s) failed: %s\n", "https://wetter.mb.fh-jena.de/station/datenbank/php_giese/online.php", curl_easy_strerror(res) ); free(chunk.memory); return NULL; } regex_t re; regmatch_t rm[4]; if (regcomp(&re, fh_temperature_regex, REG_EXTENDED|REG_NEWLINE) != 0) { fprintf(stderr, "Failed to compile regex '%s'\n", fh_temperature_regex); free(chunk.memory); return NULL; } if (ret_val = regexec(&re, chunk.memory, 4, rm, 0)) { char *reg_err; reg_err = malloc(1024); regerror(ret_val, &re, reg_err, 1024); fprintf(stderr, "%d %s\n",ret_val,reg_err); regfree(&re); free(chunk.memory); return NULL; } regfree(&re); char* ende = output; if (max_len > 0) { struct tm time_struct; memset(&time_struct, 0, sizeof(time_struct)); if (strptime((char*)(chunk.memory + rm[1].rm_so),"%d.%m.%Y um %H:%M MEZ",&time_struct) == NULL) { fprintf(stderr, "Failed to parse time '%.*s'\n", (int)(rm[1].rm_eo - rm[1].rm_so), (char*)(chunk.memory + rm[1].rm_so)); free(chunk.memory); return NULL; } time_struct.tm_isdst = 0; // the fh gives CET all the time mktime(&time_struct); ende += snprintf(output, max_len, "%.*s", (int)(rm[3].rm_eo - rm[3].rm_so), (char*)(chunk.memory + rm[3].rm_so) // temperature ); for (char *i = output; i < ende; i++) if (*i == '.') *i = ','; ende += strftime(ende, output + max_len - ende, " (%m-%d %H:%M)", &time_struct); } free(chunk.memory); return ende; } xmlNode *xml_find_node_by_name(xmlNode *cur_node, char *name) { while (cur_node) { if ((cur_node -> type == XML_ELEMENT_NODE) && (strcmp((char *)cur_node -> name, name) == 0)) return cur_node; cur_node = cur_node -> next; } 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 xml_strcmp_property_value(xmlNode *node, char *key, char *check_value) { xmlChar *is_value = xmlGetProp(node,(xmlChar *)key); int res = strcmp((char *)is_value, check_value); xmlFree(is_value); return res; } double xml_extract_float(xmlNode *node, char *key) { xmlChar *value = xmlGetProp(node, (xmlChar *)key); double res = atof((char *)value); xmlFree(value); return res; } char *gadgets_retrieve_weather_forecast(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 NULL; } curl_easy_setopt(curl_handle, CURLOPT_URL, "https://api.met.no/weatherapi/locationforecast/1.9/?lat=50.8830&lon=11.6223&msl=170"); 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(%s) failed: %s\n", "https://api.met.no/weatherapi/locationforecast/1.9/?lat=50.8830&lon=11.6223&msl=170", curl_easy_strerror(res) ); free(chunk.memory); return NULL; } LIBXML_TEST_VERSION xmlDocPtr doc; doc = xmlReadMemory(chunk.memory, chunk.size, "noname.xml", NULL, 0); free(chunk.memory); if (doc == NULL) { fprintf(stderr, "Failed to parse document\n"); return NULL; } xmlNode *cur_node = NULL; xmlNode *sub_node = NULL; cur_node = xmlDocGetRootElement(doc); if (!cur_node) { fprintf(stderr, "Failed to reach root node of xml\n"); xmlFreeDoc(doc); return NULL; } cur_node = xml_find_node_by_name(cur_node, "weatherdata"); if (!cur_node) { fprintf(stderr, "Failed to enter \n"); xmlFreeDoc(doc); return NULL; } cur_node = xml_find_node_by_name(cur_node -> children, "product"); if (!cur_node) { fprintf(stderr, "Failed to enter \n"); xmlFreeDoc(doc); return NULL; } float rain_total = 0, temp_min = NAN, temp_max = NAN, wind_max = NAN, cur_val; char time_str[2][21]; time_t now; time(&now); now = ((int)(now/60/60))*60*60; for (cur_node = cur_node -> children; cur_node; cur_node = cur_node->next) { if ((cur_node->type == XML_ELEMENT_NODE) && (strcmp((char *)cur_node->name,"time")==0) && (xml_strcmp_property_value(cur_node, "datatype", "forecast")==0)) { for (int i=0; i<=14; i++) { now += i*60*60; strftime(time_str[0],21,"%Y-%m-%dT%H:%M:%SZ",gmtime(&now)); now += 60*60; strftime(time_str[1],21,"%Y-%m-%dT%H:%M:%SZ",gmtime(&now)); now -= (i+1)*60*60; if ((xml_strcmp_property_value(cur_node, "from", time_str[1])==0) && (xml_strcmp_property_value(cur_node, "to", time_str[1])==0)) { sub_node = xml_find_node_by_name(cur_node -> children,"location"); cur_val = xml_extract_float(xml_find_node_by_name(sub_node -> children, "temperature"), "value"); if (isnan(temp_max) || (cur_val > temp_max)) temp_max = cur_val; if (isnan(temp_min) || (cur_val < temp_min)) temp_min = cur_val; cur_val = xml_extract_float(xml_find_node_by_name(sub_node -> children, "windSpeed"), "mps"); if (isnan(wind_max) || (cur_val > wind_max)) wind_max = cur_val; } if ((xml_strcmp_property_value(cur_node, "from", time_str[0])==0) && (xml_strcmp_property_value(cur_node, "to", time_str[1])==0)) { sub_node = xml_find_node_by_name(cur_node -> children,"location"); if (!sub_node) continue; sub_node = xml_find_node_by_name(sub_node -> children, "precipitation"); if (!sub_node) continue; rain_total += xml_extract_float(sub_node, "value"); } } } } xmlFreeDoc(doc); if (max_len > 0) { int i = snprintf(output, max_len, "%0.1f ,, %0.1f °C; %0.1f mm; %0.1f m/s", temp_min, temp_max, rain_total, wind_max); char *ende = output + i; for (; i>=0; i--) if (output[i] == '.') output[i] = ','; else if (output[i] == ',') output[i] = '.'; return ende; } else return output; } char *gadgets_retrieve_weather_warnings(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 NULL; } 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(%s) failed: %s\n", "https://opendata.dwd.de/weather/alerts/cap/COMMUNEUNION_DWD_STAT/Z_CAP_C_EDZW_LATEST_PVW_STATUS_PREMIUMDWD_COMMUNEUNION_DE.zip", curl_easy_strerror(res) ); free(chunk.memory); return NULL; } /* FILE *f = fopen("Z_CAP_C_EDZW_LATEST_PVW_STATUS_PREMIUMDWD_COMMUNEUNION_DE.zip","rb"); if (f == NULL) { perror("failed to open file"); return NULL; } fseek(f, 0, SEEK_END); chunk.size = ftell(f); chunk.memory = malloc(chunk.size); if (chunk.memory == NULL) { fprintf(stderr, "malloc failed to allocate %d bytes", chunk.size); fclose(f); return NULL; } fseek(f, 0, SEEK_SET); fread(chunk.memory, chunk.size, 1, f); fclose(f); */ 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, 0, &error)) == NULL) { fprintf(stderr, "can't create zip source: %s\n", zip_error_strerror(&error)); zip_error_fini(&error); free(chunk.memory); return NULL; } if ((za = zip_open_from_source(src, ZIP_RDONLY, &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 NULL; } 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)); zip_discard(za); free(chunk.memory); return NULL; } for (int i=0; i\n"); free(chunk.memory); for (int i=0; i children; text = xml_extract_string(doc, cur_node, "status"); if (text == NULL) { fprintf(stderr, "Failed to enter \n"); free(chunk.memory); for (int i=0; i\n"); free(chunk.memory); for (int i=0; i is neither 'Alert' nor 'Cancel' nor 'Update'\n"); free(chunk.memory); for (int i=0; i\n"); free(chunk.memory); for (int i=0; i children; text = xml_extract_string(doc, cur_node, "category"); if (text == NULL) { fprintf(stderr, "Failed to enter %s\n",zs.name); free(chunk.memory); for (int i=0; i is neither 'Met' nor 'Health'\n"); free(chunk.memory); for (int i=0; i %s\n",zs.name); free(chunk.memory); for (int i=0; i is neither 'Met' nor 'Health'\n"); free(chunk.memory); for (int i=0; i %s\n",zs.name); free(chunk.memory); for (int i=0; i %s\n",zs.name); free(chunk.memory); for (int i=0; i next) { xmlNode *sub_sub_node; for (sub_sub_node = sub_node -> children; sub_sub_node = xml_find_node_by_name(sub_sub_node, "geocode"); sub_sub_node = sub_sub_node -> next) { text = xml_extract_string(doc, sub_sub_node -> children, "valueName"); if (text == NULL) { fprintf(stderr, "Failed to enter \n"); free(chunk.memory); for (int i=0; i children, "value"); if (text == NULL) { fprintf(stderr, "Failed to enter \n"); free(chunk.memory); for (int i=0; i children; sub_sub_node = xml_find_node_by_name(sub_sub_node, "polygon"); sub_sub_node = sub_sub_node -> next) { text = xmlNodeListGetString(doc, sub_sub_node -> children, 0); if (text == NULL) { fprintf(stderr, "Failed to read text of \n"); free(chunk.memory); for (int i=0; i= time(NULL) + warnings_future_timespan) is_relevant = 0; // start is too far ahead if (warnings[warnings_len].expires < time(NULL)) is_relevant = 0; // warning has expired if (!is_relevant) { xmlFreeDoc(doc); continue; } text = xml_extract_string(doc, cur_node, "headline"); if (text == NULL) { fprintf(stderr, "Failed to enter \n"); free(chunk.memory); for (int i=0; i\n"); free(chunk.memory); for (int i=0; i 0)) ende += snprintf(ende, max_len, "%s", "keine Warnungen"); for (int i=0; i 0) { ende += snprintf(ende, output + max_len - ende, "%s", warnings[i].event); if (output + max_len - ende > 0) { if (warnings[i].onset > time(NULL)) ende += strftime(ende, output + max_len - ende, " ab %m-%d %H:%M", localtime(&warnings[i].onset)); if (warnings[i].expires < time(NULL) + warnings_future_timespan) ende += strftime(ende, output + max_len - ende, " bis %m-%d %H:%M", localtime(&warnings[i].expires)); if ((i+1 0)) ende += snprintf(ende, output + max_len - ende, "; ", warnings[i].event); } } free(warnings[i].headline); free(warnings[i].event); } free(warnings); return ende; }