1 /* Copyright (c) 2003-2014 Xfce Development Team
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
16 * Boston, MA 02110-1301, USA.
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 /*
24 * The following two defines fix compile warnings and need to be
25 * before time.h and libxfce4panel.h (which includes glib.h).
26 * Otherwise, they will be ignored.
27 */
28 #define _XOPEN_SOURCE
29 #define _XOPEN_SOURCE_EXTENDED 1
30 #include "weather-parsers.h"
31 #include "weather-translate.h"
32 #include "weather-debug.h"
33
34 #include <time.h>
35 #include <stdlib.h>
36 #include <string.h>
37
38 #include <libxml/parser.h>
39 #include <libxml/tree.h>
40
41
42 #define DATA(node) \
43 ((gchar *) xmlNodeListGetString(node->doc, node->children, 1))
44
45 #define PROP(node, prop) \
46 ((gchar *) xmlGetProp((node), (const xmlChar *) (prop)))
47
48 #define NODE_IS_TYPE(node, type) \
49 (xmlStrEqual(node->name, (const xmlChar *) type))
50
51
52 /*
53 * This is a portable replacement for the deprecated timegm(),
54 * copied from the man page and modified to use GLIB functions.
55 */
56 static time_t
my_timegm(struct tm * tm)57 my_timegm(struct tm *tm)
58 {
59 time_t ret;
60 char *tz;
61
62 tz = g_strdup(g_getenv("TZ"));
63 g_setenv("TZ", "", 1);
64 tzset();
65 ret = mktime(tm);
66 if (tz) {
67 g_setenv("TZ", tz, 1);
68 g_free(tz);
69 }
70 else
71 g_unsetenv("TZ");
72 tzset();
73 return ret;
74 }
75
76
77 /*
78 * Remove offset of timezone, in order to keep previous
79 * date format (before the new API, 2.x).
80 */
81 static gchar *
remove_timezone_offset(gchar * date)82 remove_timezone_offset(gchar *date)
83 {
84 GRegex *re = NULL;
85 const gchar *pattern = "[+-][0-9]{2}:[0-9]{2}";
86 gchar *res;
87
88 re = g_regex_new(pattern, 0, 0, NULL);
89 if (re != NULL && g_regex_match(re, date, 0, NULL)) {
90 res = g_regex_replace(re, date, -1, 0, "Z", 0, NULL);
91 } else {
92 res = date;
93 }
94 g_regex_unref(re);
95 return res;
96 }
97
98
99 static gdouble
extract_double(gchar * str)100 extract_double(gchar *str)
101 {
102 return g_ascii_strtod (str, NULL);
103 }
104
105
106 xml_time *
get_timeslice(xml_weather * wd,const time_t start_t,const time_t end_t,guint * index)107 get_timeslice(xml_weather *wd,
108 const time_t start_t,
109 const time_t end_t,
110 guint *index)
111 {
112 xml_time *timeslice;
113 guint i;
114
115 g_assert(wd != NULL);
116 if (G_UNLIKELY(wd == NULL))
117 return NULL;
118
119 for (i = 0; i < wd->timeslices->len; i++) {
120 timeslice = g_array_index(wd->timeslices, xml_time *, i);
121 if (timeslice &&
122 timeslice->start == start_t && timeslice->end == end_t) {
123 if (index != NULL)
124 *index = i;
125 return timeslice;
126 }
127 }
128 return NULL;
129 }
130
131
132 xml_astro *
get_astro(const GArray * astrodata,const time_t day_t,guint * index)133 get_astro(const GArray *astrodata,
134 const time_t day_t,
135 guint *index)
136 {
137 xml_astro *astro;
138 guint i;
139
140 g_assert(astrodata != NULL);
141 if (G_UNLIKELY(astrodata == NULL))
142 return NULL;
143
144 for (i = 0; i < astrodata->len; i++) {
145 astro = g_array_index(astrodata, xml_astro *, i);
146 if (astro && astro->day == day_t) {
147 if (index != NULL)
148 *index = i;
149 return astro;
150 }
151 }
152 return NULL;
153 }
154
155
156 time_t
parse_timestring(const gchar * ts,gchar * format,gboolean local)157 parse_timestring(const gchar *ts,
158 gchar *format,
159 gboolean local) {
160 time_t t;
161 struct tm tm;
162
163 if (G_UNLIKELY(ts == NULL)) {
164 memset(&t, 0, sizeof(time_t));
165 return t;
166 }
167
168 /* standard format */
169 if (format == NULL)
170 format = "%Y-%m-%dT%H:%M:%SZ";
171
172 /* strptime needs an initialized struct, or unpredictable
173 * behaviour might occur */
174 memset(&tm, 0, sizeof(struct tm));
175 tm.tm_isdst = -1;
176
177 if (strptime(ts, format, &tm) == NULL) {
178 memset(&t, 0, sizeof(time_t));
179 return t;
180 }
181
182 t = local ? mktime(&tm) : my_timegm(&tm);
183
184 if (t < 0)
185 memset(&t, 0, sizeof(time_t));
186
187 return t;
188 }
189
190
191 static const gchar *
parse_moonposition(gdouble pos)192 parse_moonposition (gdouble pos) {
193 if (pos < 0.0 || pos > 100.0)
194 return "Unknown";
195 if (pos <= 12.5)
196 return "Waxing crescent";
197 else if (pos <= 25.0)
198 return "First quarter";
199 else if (pos <= 37.5)
200 return "Waxing gibbous";
201 else if (pos <= 50.0)
202 return "Full moon";
203 else if (pos <= 62.5)
204 return "Waning gibbous";
205 else if (pos <= 75.0)
206 return "Third quarter";
207 else if (pos <= 87.5)
208 return "Waning crescent";
209 return "New moon";
210 }
211
212
213 static void
parse_location(xmlNode * cur_node,xml_location * loc)214 parse_location(xmlNode *cur_node,
215 xml_location *loc)
216 {
217 xmlNode *child_node;
218
219 g_free(loc->altitude);
220 loc->altitude = PROP(cur_node, "altitude");
221
222 g_free(loc->latitude);
223 loc->latitude = PROP(cur_node, "latitude");
224
225 g_free(loc->longitude);
226 loc->longitude = PROP(cur_node, "longitude");
227
228 for (child_node = cur_node->children; child_node;
229 child_node = child_node->next) {
230 if (NODE_IS_TYPE(child_node, "temperature")) {
231 g_free(loc->temperature_unit);
232 g_free(loc->temperature_value);
233 loc->temperature_unit = PROP(child_node, "unit");
234 loc->temperature_value = PROP(child_node, "value");
235 }
236 if (NODE_IS_TYPE(child_node, "windDirection")) {
237 g_free(loc->wind_dir_deg);
238 g_free(loc->wind_dir_name);
239 loc->wind_dir_deg = PROP(child_node, "deg");
240 loc->wind_dir_name = PROP(child_node, "name");
241 }
242 if (NODE_IS_TYPE(child_node, "windSpeed")) {
243 g_free(loc->wind_speed_mps);
244 g_free(loc->wind_speed_beaufort);
245 loc->wind_speed_mps = PROP(child_node, "mps");
246 loc->wind_speed_beaufort = PROP(child_node, "beaufort");
247 }
248 if (NODE_IS_TYPE(child_node, "humidity")) {
249 g_free(loc->humidity_unit);
250 g_free(loc->humidity_value);
251 loc->humidity_unit = PROP(child_node, "unit");
252 loc->humidity_value = PROP(child_node, "value");
253 }
254 if (NODE_IS_TYPE(child_node, "pressure")) {
255 g_free(loc->pressure_unit);
256 g_free(loc->pressure_value);
257 loc->pressure_unit = PROP(child_node, "unit");
258 loc->pressure_value = PROP(child_node, "value");
259 }
260 if (NODE_IS_TYPE(child_node, "cloudiness")) {
261 g_free(loc->clouds_percent[CLOUDS_PERC_CLOUDINESS]);
262 loc->clouds_percent[CLOUDS_PERC_CLOUDINESS] = PROP(child_node, "percent");
263 }
264 if (NODE_IS_TYPE(child_node, "fog")) {
265 g_free(loc->fog_percent);
266 loc->fog_percent = PROP(child_node, "percent");
267 }
268 if (NODE_IS_TYPE(child_node, "lowClouds")) {
269 g_free(loc->clouds_percent[CLOUDS_PERC_LOW]);
270 loc->clouds_percent[CLOUDS_PERC_LOW] = PROP(child_node, "percent");
271 }
272 if (NODE_IS_TYPE(child_node, "mediumClouds")) {
273 g_free(loc->clouds_percent[CLOUDS_PERC_MID]);
274 loc->clouds_percent[CLOUDS_PERC_MID] = PROP(child_node, "percent");
275 }
276 if (NODE_IS_TYPE(child_node, "highClouds")) {
277 g_free(loc->clouds_percent[CLOUDS_PERC_HIGH]);
278 loc->clouds_percent[CLOUDS_PERC_HIGH] = PROP(child_node, "percent");
279 }
280 if (NODE_IS_TYPE(child_node, "precipitation")) {
281 g_free(loc->precipitation_unit);
282 g_free(loc->precipitation_value);
283 loc->precipitation_unit = PROP(child_node, "unit");
284 loc->precipitation_value = PROP(child_node, "value");
285 }
286 if (NODE_IS_TYPE(child_node, "symbol")) {
287 g_free(loc->symbol);
288 loc->symbol_id = strtol(PROP(child_node, "number"), NULL, 10);
289 loc->symbol = g_strdup(get_symbol_for_id(loc->symbol_id));
290 }
291 }
292
293 /* Convert Fahrenheit to Celsius if necessary, so that we don't
294 have to do it later. met.no usually provides values in Celsius. */
295 if (loc->temperature_value && loc->temperature_unit &&
296 !strcmp(loc->temperature_unit, "fahrenheit")) {
297 gdouble val = string_to_double(loc->temperature_value, 0);
298 val = (val - 32.0) * 5.0 / 9.0;
299 g_free(loc->temperature_value);
300 loc->temperature_value = double_to_string(val, "%.1f");
301 g_free(loc->temperature_unit);
302 loc->temperature_unit = g_strdup("celsius");
303 }
304 }
305
306
307 xml_weather *
make_weather_data(void)308 make_weather_data(void)
309 {
310 xml_weather *wd;
311
312 wd = g_slice_new0(xml_weather);
313 if (G_UNLIKELY(wd == NULL))
314 return NULL;
315 wd->timeslices = g_array_sized_new(FALSE, TRUE,
316 sizeof(xml_time *), 200);
317 if (G_UNLIKELY(wd->timeslices == NULL)) {
318 g_slice_free(xml_weather, wd);
319 return NULL;
320 }
321 return wd;
322 }
323
324
325 xml_time *
make_timeslice(void)326 make_timeslice(void)
327 {
328 xml_time *timeslice;
329
330 timeslice = g_slice_new0(xml_time);
331 if (G_UNLIKELY(timeslice == NULL))
332 return NULL;
333
334 timeslice->location = g_slice_new0(xml_location);
335 if (G_UNLIKELY(timeslice->location == NULL)) {
336 g_slice_free(xml_time, timeslice);
337 return NULL;
338 }
339 return timeslice;
340 }
341
342
343 static void
parse_time(xmlNode * cur_node,xml_weather * wd)344 parse_time(xmlNode *cur_node,
345 xml_weather *wd)
346 {
347 gchar *datatype, *from, *to;
348 time_t start_t, end_t;
349 xml_time *timeslice;
350 xmlNode *child_node;
351
352 datatype = PROP(cur_node, "datatype");
353 if (xmlStrcasecmp((xmlChar *) datatype, (xmlChar *) "forecast")) {
354 xmlFree(datatype);
355 return;
356 }
357 xmlFree(datatype);
358
359 from = PROP(cur_node, "from");
360 start_t = parse_timestring(from, NULL, FALSE);
361 xmlFree(from);
362
363 to = PROP(cur_node, "to");
364 end_t = parse_timestring(to, NULL, FALSE);
365 xmlFree(to);
366
367 if (G_UNLIKELY(!start_t || !end_t))
368 return;
369
370 /* look for existing timeslice or add a new one */
371 timeslice = get_timeslice(wd, start_t, end_t, NULL);
372 if (! timeslice) {
373 timeslice = make_timeslice();
374 if (G_UNLIKELY(!timeslice))
375 return;
376 timeslice->start = start_t;
377 timeslice->end = end_t;
378 g_array_append_val(wd->timeslices, timeslice);
379 }
380
381 for (child_node = cur_node->children; child_node;
382 child_node = child_node->next)
383 if (G_LIKELY(NODE_IS_TYPE(child_node, "location")))
384 parse_location(child_node, timeslice->location);
385 }
386
387
388 /*
389 * Parse XML weather data and merge it with current data.
390 */
391 gboolean
parse_weather(xmlNode * cur_node,xml_weather * wd)392 parse_weather(xmlNode *cur_node,
393 xml_weather *wd)
394 {
395 xmlNode *child_node;
396
397 g_assert(wd != NULL);
398 if (G_UNLIKELY(wd == NULL))
399 return FALSE;
400
401 if (G_UNLIKELY(cur_node == NULL || !NODE_IS_TYPE(cur_node, "weatherdata")))
402 return FALSE;
403
404 for (cur_node = cur_node->children; cur_node; cur_node = cur_node->next) {
405 if (cur_node->type != XML_ELEMENT_NODE)
406 continue;
407
408 if (NODE_IS_TYPE(cur_node, "product")) {
409 gchar *class = PROP(cur_node, "class");
410 if (xmlStrcasecmp((xmlChar *) class, (xmlChar *) "pointData")) {
411 xmlFree(class);
412 continue;
413 }
414 g_free(class);
415 for (child_node = cur_node->children; child_node;
416 child_node = child_node->next)
417 if (NODE_IS_TYPE(child_node, "time"))
418 parse_time(child_node, wd);
419 }
420 }
421 return TRUE;
422 }
423
424
425 static xml_astro *
parse_astro_time(xmlNode * cur_node)426 parse_astro_time(xmlNode *cur_node)
427 {
428 xmlNode *child_node;
429 xml_astro *astro;
430 gchar *date, *sunrise, *sunset, *moonrise, *moonset;
431 gboolean sun_rises = FALSE, sun_sets = FALSE;
432 gboolean moon_rises = FALSE, moon_sets = FALSE;
433 gdouble moonposition;
434
435 astro = g_slice_new0(xml_astro);
436 if (G_UNLIKELY(astro == NULL))
437 return NULL;
438
439 date = PROP(cur_node, "date");
440 astro->day = parse_timestring(date, "%Y-%m-%d", TRUE);
441 xmlFree(date);
442
443 for (child_node = cur_node->children; child_node;
444 child_node = child_node->next) {
445 if (child_node->type == XML_ELEMENT_NODE) {
446 if (NODE_IS_TYPE(child_node, "sunrise")) {
447 sunrise = remove_timezone_offset(PROP(child_node, "time"));
448 astro->sunrise = parse_timestring(sunrise, NULL, TRUE);
449 xmlFree(sunrise);
450 sun_rises = TRUE;
451 }
452
453 if (NODE_IS_TYPE(child_node, "moonset")) {
454 moonset = remove_timezone_offset(PROP(child_node, "time"));
455 astro->moonset = parse_timestring(moonset, NULL, TRUE);
456 xmlFree(moonset);
457 moon_sets = TRUE;
458 }
459
460 if (NODE_IS_TYPE(child_node, "sunset")) {
461 sunset = remove_timezone_offset(PROP(child_node, "time"));
462 astro->sunset = parse_timestring(sunset, NULL, TRUE);
463 xmlFree(sunset);
464 sun_sets = TRUE;
465 }
466
467 if (NODE_IS_TYPE(child_node, "moonrise")) {
468 moonrise = remove_timezone_offset(PROP(child_node, "time"));
469 astro->moonrise = parse_timestring(moonrise, NULL, TRUE);
470 xmlFree(moonrise);
471 moon_rises = TRUE;
472 }
473
474 if (NODE_IS_TYPE(child_node, "moonposition")) {
475 moonposition = extract_double(PROP(child_node, "phase"));
476 if (astro->moon_phase) {
477 g_free (astro->moon_phase);
478 }
479 astro->moon_phase = g_strdup(parse_moonposition(moonposition));
480 }
481
482 if (NODE_IS_TYPE(child_node, "solarnoon")) {
483 astro->solarnoon_elevation = extract_double(PROP(child_node, "elevation"));
484 }
485
486 if (NODE_IS_TYPE(child_node, "solarmidnight")) {
487 astro->solarmidnight_elevation = extract_double(PROP(child_node, "elevation"));
488 }
489 }
490 }
491
492 if (sun_rises)
493 astro->sun_never_rises = FALSE;
494 else
495 astro->sun_never_rises = TRUE;
496 if (sun_sets)
497 astro->sun_never_sets = FALSE;
498 else
499 astro->sun_never_sets = TRUE;
500
501 if (moon_rises)
502 astro->moon_never_rises = FALSE;
503 else
504 astro->moon_never_rises = TRUE;
505 if (moon_sets)
506 astro->moon_never_sets = FALSE;
507 else
508 astro->moon_never_sets = TRUE;
509 return astro;
510 }
511
512
513 /*
514 * Look at https://api.met.no/weatherapi/sunrise/2.0/schema for information
515 * of elements and attributes to expect.
516 */
517 gboolean
parse_astrodata(xmlNode * cur_node,GArray * astrodata)518 parse_astrodata(xmlNode *cur_node,
519 GArray *astrodata)
520 {
521 xmlNode *child_node;
522 xml_astro *astro;
523
524 g_assert(astrodata != NULL);
525 if (G_UNLIKELY(astrodata == NULL))
526 return FALSE;
527
528 g_assert(cur_node != NULL);
529 if (G_UNLIKELY(cur_node == NULL ||
530 !NODE_IS_TYPE(cur_node, "location")))
531 return FALSE;
532
533 for (child_node = cur_node->children; child_node;
534 child_node = child_node->next)
535 if (NODE_IS_TYPE(child_node, "time")) {
536 if ((astro = parse_astro_time(child_node))) {
537 merge_astro(astrodata, astro);
538 xml_astro_free(astro);
539 }
540 }
541 return TRUE;
542 }
543
544
545 xml_geolocation *
parse_geolocation(xmlNode * cur_node)546 parse_geolocation(xmlNode *cur_node)
547 {
548 xml_geolocation *geo;
549
550 g_assert(cur_node != NULL);
551 if (G_UNLIKELY(cur_node == NULL))
552 return NULL;
553
554 geo = g_slice_new0(xml_geolocation);
555 if (G_UNLIKELY(geo == NULL))
556 return NULL;
557
558 for (cur_node = cur_node->children; cur_node;
559 cur_node = cur_node->next) {
560 if (NODE_IS_TYPE(cur_node, "City"))
561 geo->city = DATA(cur_node);
562 if (NODE_IS_TYPE(cur_node, "CountryName"))
563 geo->country_name = DATA(cur_node);
564 if (NODE_IS_TYPE(cur_node, "CountryCode"))
565 geo->country_code = DATA(cur_node);
566 if (NODE_IS_TYPE(cur_node, "RegionName"))
567 geo->region_name = DATA(cur_node);
568 if (NODE_IS_TYPE(cur_node, "Latitude"))
569 geo->latitude = DATA(cur_node);
570 if (NODE_IS_TYPE(cur_node, "Longitude"))
571 geo->longitude = DATA(cur_node);
572 }
573 return geo;
574 }
575
576
577 xml_place *
parse_place(xmlNode * cur_node)578 parse_place(xmlNode *cur_node)
579 {
580 xml_place *place;
581
582 g_assert(cur_node != NULL);
583 if (G_UNLIKELY(cur_node == NULL || !NODE_IS_TYPE(cur_node, "place")))
584 return NULL;
585
586 place = g_slice_new0(xml_place);
587 if (G_UNLIKELY(place == NULL))
588 return NULL;
589 place->lat = PROP(cur_node, "lat");
590 place->lon = PROP(cur_node, "lon");
591 place->display_name = PROP(cur_node, "display_name");
592 return place;
593 }
594
595
596 xml_altitude *
parse_altitude(xmlNode * cur_node)597 parse_altitude(xmlNode *cur_node)
598 {
599 xml_altitude *alt;
600
601 g_assert(cur_node != NULL);
602 if (G_UNLIKELY(cur_node == NULL) || !NODE_IS_TYPE(cur_node, "geonames"))
603 return NULL;
604
605 alt = g_slice_new0(xml_altitude);
606 if (G_UNLIKELY(alt == NULL))
607 return NULL;
608 for (cur_node = cur_node->children; cur_node;
609 cur_node = cur_node->next)
610 if (NODE_IS_TYPE(cur_node, "srtm3"))
611 alt->altitude = DATA(cur_node);
612 return alt;
613 }
614
615
616 xml_timezone *
parse_timezone(xmlNode * cur_node)617 parse_timezone(xmlNode *cur_node)
618 {
619 xml_timezone *tz;
620
621 g_assert(cur_node != NULL);
622 if (G_UNLIKELY(cur_node == NULL) || !NODE_IS_TYPE(cur_node, "geonames"))
623 return NULL;
624
625 for (cur_node = cur_node->children; cur_node;
626 cur_node = cur_node->next)
627 if (NODE_IS_TYPE(cur_node, "timezone"))
628 break;
629
630 if (G_UNLIKELY(cur_node == NULL))
631 return NULL;
632
633 tz = g_slice_new0(xml_timezone);
634 if (G_UNLIKELY(tz == NULL))
635 return NULL;
636
637 for (cur_node = cur_node->children; cur_node;
638 cur_node = cur_node->next) {
639 if (NODE_IS_TYPE(cur_node, "countryCode"))
640 tz->country_code = DATA(cur_node);
641 if (NODE_IS_TYPE(cur_node, "countryName"))
642 tz->country_name = DATA(cur_node);
643 if (NODE_IS_TYPE(cur_node, "timezoneId"))
644 tz->timezone_id = DATA(cur_node);
645 }
646 return tz;
647 }
648
649
650 xmlDoc *
get_xml_document(SoupMessage * msg)651 get_xml_document(SoupMessage *msg)
652 {
653 if (G_LIKELY(msg && msg->response_body && msg->response_body->data)) {
654 if (g_utf8_validate(msg->response_body->data, -1, NULL)) {
655 /* force parsing as UTF-8, the XML encoding header may lie */
656 return xmlReadMemory(msg->response_body->data,
657 strlen(msg->response_body->data),
658 NULL, "UTF-8", 0);
659 } else {
660 return xmlParseMemory(msg->response_body->data,
661 strlen(msg->response_body->data));
662 }
663 }
664 return NULL;
665 }
666
667
668 gpointer
parse_xml_document(SoupMessage * msg,XmlParseFunc parse_func)669 parse_xml_document(SoupMessage *msg,
670 XmlParseFunc parse_func)
671 {
672 xmlDoc *doc;
673 xmlNode *root_node;
674 gpointer user_data = NULL;
675
676 g_assert(msg != NULL);
677 if (G_UNLIKELY(msg == NULL))
678 return NULL;
679
680 doc = get_xml_document(msg);
681 if (G_LIKELY(doc)) {
682 root_node = xmlDocGetRootElement(doc);
683 if (G_LIKELY(root_node))
684 user_data = parse_func(root_node);
685 xmlFreeDoc(doc);
686 }
687 return user_data;
688 }
689
690
691 static void
xml_location_free(xml_location * loc)692 xml_location_free(xml_location *loc)
693 {
694 g_assert(loc != NULL);
695 if (G_UNLIKELY(loc == NULL))
696 return;
697 g_free(loc->altitude);
698 g_free(loc->latitude);
699 g_free(loc->longitude);
700 g_free(loc->temperature_value);
701 g_free(loc->temperature_unit);
702 g_free(loc->wind_dir_deg);
703 g_free(loc->wind_dir_name);
704 g_free(loc->wind_speed_mps);
705 g_free(loc->wind_speed_beaufort);
706 g_free(loc->humidity_value);
707 g_free(loc->humidity_unit);
708 g_free(loc->pressure_value);
709 g_free(loc->pressure_unit);
710 g_free(loc->clouds_percent[CLOUDS_PERC_LOW]);
711 g_free(loc->clouds_percent[CLOUDS_PERC_MID]);
712 g_free(loc->clouds_percent[CLOUDS_PERC_HIGH]);
713 g_free(loc->clouds_percent[CLOUDS_PERC_CLOUDINESS]);
714 g_free(loc->fog_percent);
715 g_free(loc->precipitation_value);
716 g_free(loc->precipitation_unit);
717 g_free(loc->symbol);
718 g_slice_free(xml_location, loc);
719 }
720
721
722 /*
723 * Deep copy xml_astro struct.
724 */
725 xml_astro *
xml_astro_copy(const xml_astro * src)726 xml_astro_copy(const xml_astro *src)
727 {
728 xml_astro *dst;
729
730 if (G_UNLIKELY(src == NULL))
731 return NULL;
732
733 dst = g_slice_new0(xml_astro);
734 g_assert(dst != NULL);
735 if (G_UNLIKELY(dst == NULL))
736 return NULL;
737
738 dst->day = src->day;
739 dst->sunrise = src->sunrise;
740 dst->sunset = src->sunset;
741 dst->sun_never_rises = src->sun_never_rises;
742 dst->sun_never_sets = src->sun_never_sets;
743 dst->moonrise = src->moonrise;
744 dst->moonset = src->moonset;
745 dst->moon_never_rises = src->moon_never_rises;
746 dst->moon_never_sets = src->moon_never_sets;
747 dst->moon_phase = g_strdup(src->moon_phase);
748 dst->solarnoon_elevation = src->solarnoon_elevation;
749 dst->solarmidnight_elevation = src->solarmidnight_elevation;
750 return dst;
751 }
752
753
754 /*
755 * Deep copy xml_time struct.
756 */
757 xml_time *
xml_time_copy(const xml_time * src)758 xml_time_copy(const xml_time *src)
759 {
760 xml_time *dst;
761 xml_location *loc;
762 gint i;
763
764 if (G_UNLIKELY(src == NULL))
765 return NULL;
766
767 dst = g_slice_new0(xml_time);
768 g_assert(dst != NULL);
769 if (G_UNLIKELY(dst == NULL))
770 return NULL;
771
772 loc = g_slice_new0(xml_location);
773 g_assert(loc != NULL);
774 if (loc == NULL) {
775 g_slice_free(xml_time, dst);
776 return NULL;
777 }
778
779 dst->start = src->start;
780 dst->end = src->end;
781
782 loc->altitude = g_strdup(src->location->altitude);
783 loc->latitude = g_strdup(src->location->latitude);
784 loc->longitude = g_strdup(src->location->longitude);
785
786 loc->temperature_value = g_strdup(src->location->temperature_value);
787 loc->temperature_unit = g_strdup(src->location->temperature_unit);
788
789 loc->wind_dir_deg = g_strdup(src->location->wind_dir_deg);
790 loc->wind_dir_name = g_strdup(src->location->wind_dir_name);
791 loc->wind_speed_mps = g_strdup(src->location->wind_speed_mps);
792 loc->wind_speed_beaufort = g_strdup(src->location->wind_speed_beaufort);
793
794 loc->humidity_value = g_strdup(src->location->humidity_value);
795 loc->humidity_unit = g_strdup(src->location->humidity_unit);
796
797 loc->pressure_value = g_strdup(src->location->pressure_value);
798 loc->pressure_unit = g_strdup(src->location->pressure_unit);
799
800 for (i = 0; i < CLOUDS_PERC_NUM; i++)
801 loc->clouds_percent[i] = g_strdup(src->location->clouds_percent[i]);
802
803 loc->fog_percent = g_strdup(src->location->fog_percent);
804
805 loc->precipitation_value =
806 g_strdup(src->location->precipitation_value);
807 loc->precipitation_unit = g_strdup(src->location->precipitation_unit);
808
809 loc->symbol_id = src->location->symbol_id;
810 loc->symbol = g_strdup(src->location->symbol);
811
812 dst->location = loc;
813
814 return dst;
815 }
816
817
818 void
xml_time_free(xml_time * timeslice)819 xml_time_free(xml_time *timeslice)
820 {
821 g_assert(timeslice != NULL);
822 if (G_UNLIKELY(timeslice == NULL))
823 return;
824 xml_location_free(timeslice->location);
825 g_slice_free(xml_time, timeslice);
826 }
827
828
829 void
xml_weather_free(xml_weather * wd)830 xml_weather_free(xml_weather *wd)
831 {
832 xml_time *timeslice;
833 guint i;
834
835 g_assert(wd != NULL);
836 if (G_UNLIKELY(wd == NULL))
837 return;
838 if (G_LIKELY(wd->timeslices)) {
839 weather_debug("Freeing %u timeslices.", wd->timeslices->len);
840 for (i = 0; i < wd->timeslices->len; i++) {
841 timeslice = g_array_index(wd->timeslices, xml_time *, i);
842 xml_time_free(timeslice);
843 }
844 g_array_free(wd->timeslices, FALSE);
845 }
846 if (G_LIKELY(wd->current_conditions)) {
847 weather_debug("Freeing current conditions.");
848 xml_time_free(wd->current_conditions);
849 }
850 g_slice_free(xml_weather, wd);
851 }
852
853
854 void
xml_weather_clean(xml_weather * wd)855 xml_weather_clean(xml_weather *wd)
856 {
857 xml_time *timeslice;
858 time_t now_t = time(NULL);
859 guint i;
860
861 if (G_UNLIKELY(wd == NULL || wd->timeslices == NULL))
862 return;
863 for (i = 0; i < wd->timeslices->len; i++) {
864 timeslice = g_array_index(wd->timeslices, xml_time *, i);
865 if (G_UNLIKELY(timeslice == NULL))
866 continue;
867 if (difftime(now_t, timeslice->end) > DATA_EXPIRY_TIME) {
868 weather_debug("Removing expired timeslice:");
869 weather_dump(weather_dump_timeslice, timeslice);
870 xml_time_free(timeslice);
871 g_array_remove_index(wd->timeslices, i--);
872 weather_debug("Remaining timeslices: %d", wd->timeslices->len);
873 }
874 }
875 }
876
877
878 void
xml_astro_free(xml_astro * astro)879 xml_astro_free(xml_astro *astro)
880 {
881 g_assert(astro != NULL);
882 if (G_UNLIKELY(astro == NULL))
883 return;
884 g_free(astro->moon_phase);
885 g_slice_free(xml_astro, astro);
886 }
887
888
889 void
astrodata_free(GArray * astrodata)890 astrodata_free(GArray *astrodata)
891 {
892 xml_astro *astro;
893 guint i;
894
895 if (G_UNLIKELY(astrodata == NULL))
896 return;
897 for (i = 0; i < astrodata->len; i++) {
898 astro = g_array_index(astrodata, xml_astro *, i);
899 if (astro)
900 xml_astro_free(astro);
901 }
902 g_array_free(astrodata, FALSE);
903 }
904
905
906 void
xml_geolocation_free(xml_geolocation * geo)907 xml_geolocation_free(xml_geolocation *geo)
908 {
909 g_assert(geo != NULL);
910 if (G_UNLIKELY(geo == NULL))
911 return;
912 g_free(geo->city);
913 g_free(geo->country_name);
914 g_free(geo->country_code);
915 g_free(geo->region_name);
916 g_free(geo->latitude);
917 g_free(geo->longitude);
918 g_slice_free(xml_geolocation, geo);
919 }
920
921
922 void
xml_place_free(xml_place * place)923 xml_place_free(xml_place *place)
924 {
925 g_assert(place != NULL);
926 if (G_UNLIKELY(place == NULL))
927 return;
928 g_free(place->lat);
929 g_free(place->lon);
930 g_free(place->display_name);
931 g_slice_free(xml_place, place);
932 }
933
934
935 void
xml_altitude_free(xml_altitude * alt)936 xml_altitude_free(xml_altitude *alt)
937 {
938 g_assert(alt != NULL);
939 if (G_UNLIKELY(alt == NULL))
940 return;
941 g_free(alt->altitude);
942 g_slice_free(xml_altitude, alt);
943 }
944
945
946 void
xml_timezone_free(xml_timezone * tz)947 xml_timezone_free(xml_timezone *tz)
948 {
949 g_assert(tz != NULL);
950 if (G_UNLIKELY(tz == NULL))
951 return;
952 g_free(tz->country_code);
953 g_free(tz->country_name);
954 g_free(tz->timezone_id);
955 g_slice_free(xml_timezone, tz);
956 }
957