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