Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / calendar / backends / weather / e-cal-backend-weather.c
1 /* Evolution calendar - weather backend
2  *
3  * Copyright (C) 2005 Novell, Inc (www.novell.com)
4  *
5  * Authors: David Trowbridge <trowbrds@cs.colorado.edu>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of version 2 of the GNU Lesser General Public
9  * License as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  */
20
21 #include <config.h>
22 #include <libedata-cal/e-cal-backend-cache.h>
23 #include <libedata-cal/e-cal-backend-util.h>
24 #include <libedata-cal/e-cal-backend-sexp.h>
25 #include <glib/gi18n-lib.h>
26 #include <string.h>
27 #include "e-cal-backend-weather.h"
28 #include "e-weather-source.h"
29
30 #define WEATHER_UID_EXT "-weather"
31
32 static gboolean reload_cb (ECalBackendWeather *cbw);
33 static gboolean begin_retrieval_cb (ECalBackendWeather *cbw);
34 static ECalComponent* create_weather (ECalBackendWeather *cbw, WeatherForecast *report);
35 static ECalBackendSyncStatus
36 e_cal_backend_weather_add_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzobj);
37         
38 /* Private part of the ECalBackendWeather structure */
39 struct _ECalBackendWeatherPrivate {
40         /* URI to get remote weather data from */
41         char *uri;
42
43         /* Local/remote mode */
44         CalMode mode;
45
46         /* The file cache */
47         ECalBackendCache *cache;
48
49         /* The calendar's default timezone, used for resolving DATE and
50            floating DATE-TIME values. */
51         icaltimezone *default_zone;
52         GHashTable *zones;
53
54         /* Reload */
55         guint reload_timeout_id;
56         guint source_changed_id;
57         guint is_loading : 1;
58
59         /* Flags */
60         gboolean opened;
61
62         /* City (for summary) */
63         gchar *city;
64
65         /* Weather source */
66         EWeatherSource *source;
67 };
68
69 static ECalBackendSyncClass *parent_class;
70
71 static gboolean
72 reload_cb (ECalBackendWeather *cbw)
73 {
74         ECalBackendWeatherPrivate *priv;
75
76         priv = cbw->priv;
77
78         if (priv->is_loading)
79                 return TRUE;
80
81         priv->reload_timeout_id = 0;
82         priv->opened = TRUE;
83         begin_retrieval_cb (cbw);
84         return FALSE;
85 }
86
87 static void
88 source_changed (ESource *source, ECalBackendWeather *cbw)
89 {
90         /* FIXME
91          * We should force a reload of the data when this gets called. Unfortunately,
92          * this signal isn't getting through from evolution to the backend
93          */
94 }
95
96 static void
97 maybe_start_reload_timeout (ECalBackendWeather *cbw)
98 {
99         ECalBackendWeatherPrivate *priv;
100         ESource *source;
101         const gchar *refresh_str;
102
103         priv = cbw->priv;
104
105         if (priv->reload_timeout_id)
106                 return;
107
108         source = e_cal_backend_get_source (E_CAL_BACKEND (cbw));
109         if (!source) {
110                 g_warning ("Could not get source for ECalBackendWeather reload.");
111                 return;
112         }
113
114         if (priv->source_changed_id == 0)
115                 priv->source_changed_id = g_signal_connect (G_OBJECT (source),
116                                                             "changed",
117                                                             G_CALLBACK (source_changed),
118                                                             cbw);
119
120         refresh_str = e_source_get_property (source, "refresh");
121
122         /* By default, reload every 4 hours. At least for CCF, the forecasts only come out
123          * twice a day, and chances are while the NWS and similar organizations have some
124          * serious bandwidth, they would appreciate it if we didn't hammer their servers
125          */
126         priv->reload_timeout_id = g_timeout_add ((refresh_str ? atoi (refresh_str) : 240) * 60000,
127                                                  (GSourceFunc) reload_cb, cbw);
128
129 }
130
131 static void
132 finished_retrieval_cb (GList *forecasts, ECalBackendWeather *cbw)
133 {
134         ECalBackendWeatherPrivate *priv;
135         ECalComponent *comp;
136         icalcomponent *icomp;
137         GList *l;
138
139         priv = cbw->priv;
140
141         if (forecasts == NULL) {
142                 e_cal_backend_notify_error (E_CAL_BACKEND (cbw), _("Could not retrieve weather data"));
143                 return;
144         }
145
146         /* update cache */
147         l = e_cal_backend_cache_get_components (priv->cache);
148         for (; l != NULL; l = g_list_next (l)) {
149                 ECalComponentId *id;
150
151                 icomp = e_cal_component_get_icalcomponent (E_CAL_COMPONENT (l->data));
152                 id = e_cal_component_get_id (E_CAL_COMPONENT (l->data));
153
154                 e_cal_backend_notify_object_removed (E_CAL_BACKEND (cbw),
155                         id,
156                         icalcomponent_as_ical_string (icomp),
157                         NULL);
158
159                 e_cal_component_free_id (id);
160                 g_object_unref (G_OBJECT (l->data));
161         }
162         g_list_free (l);
163         e_file_cache_clean (E_FILE_CACHE (priv->cache));
164
165         for (l = forecasts; l != NULL; l = g_list_next (l)) {
166                 comp = create_weather (cbw, l->data);
167                 e_cal_backend_cache_put_component (priv->cache, comp);
168                 icomp = e_cal_component_get_icalcomponent (comp);
169                 e_cal_backend_notify_object_created (E_CAL_BACKEND (cbw), icalcomponent_as_ical_string (icomp));
170         }
171
172         priv->is_loading = FALSE;
173 }
174
175 static gboolean
176 begin_retrieval_cb (ECalBackendWeather *cbw)
177 {
178         ECalBackendWeatherPrivate *priv = cbw->priv;
179
180         if (priv->mode != CAL_MODE_REMOTE)
181                 return TRUE;
182
183         maybe_start_reload_timeout (cbw);
184
185         if (priv->source == NULL)
186                 priv->source = e_weather_source_new (e_cal_backend_get_uri (E_CAL_BACKEND (cbw)));
187
188         if (priv->is_loading)
189                 return FALSE;
190
191         priv->is_loading = TRUE;
192
193         e_weather_source_parse (priv->source, (EWeatherSourceFinished) finished_retrieval_cb, cbw);
194
195         return FALSE;
196 }
197
198 static const char*
199 getConditions (WeatherForecast *report)
200 {
201         switch (report->conditions) {
202                 case WEATHER_FAIR:                      return _("Fair");
203                 case WEATHER_SNOW_SHOWERS:              return _("Snow showers");
204                 case WEATHER_SNOW:                      return _("Snow");
205                 case WEATHER_PARTLY_CLOUDY:             return _("Partly cloudy");
206                 case WEATHER_SMOKE:                     return _("Smoke");
207                 case WEATHER_THUNDERSTORMS:             return _("Thunderstorms");
208                 case WEATHER_CLOUDY:                    return _("Cloudy");
209                 case WEATHER_DRIZZLE:                   return _("Drizzle");
210                 case WEATHER_SUNNY:                     return _("Sunny");
211                 case WEATHER_DUST:                      return _("Dust");
212                 case WEATHER_CLEAR:                     return _("Clear");
213                 case WEATHER_MOSTLY_CLOUDY:             return _("Mostly cloudy");
214                 case WEATHER_WINDY:                     return _("Windy");
215                 case WEATHER_RAIN_SHOWERS:              return _("Rain showers");
216                 case WEATHER_FOGGY:                     return _("Foggy");
217                 case WEATHER_RAIN_OR_SNOW_MIXED:        return _("Rain/snow mixed");
218                 case WEATHER_SLEET:                     return _("Sleet");
219                 case WEATHER_VERY_HOT_OR_HOT_HUMID:     return _("Very hot/humid");
220                 case WEATHER_BLIZZARD:                  return _("Blizzard");
221                 case WEATHER_FREEZING_RAIN:             return _("Freezing rain");
222                 case WEATHER_HAZE:                      return _("Haze");
223                 case WEATHER_BLOWING_SNOW:              return _("Blowing snow");
224                 case WEATHER_FREEZING_DRIZZLE:          return _("Freezing drizzle");
225                 case WEATHER_VERY_COLD_WIND_CHILL:      return _("Very cold/wind chill");
226                 case WEATHER_RAIN:                      return _("Rain");
227                 default:                                return NULL;
228         }
229 }
230
231 static const char*
232 getCategory (WeatherForecast *report)
233 {
234         /* Right now this is based on which icons we have available */
235         switch (report->conditions) {
236                 case WEATHER_FAIR:                      return _("Weather: Sunny");
237                 case WEATHER_SNOW_SHOWERS:              return _("Weather: Snow");
238                 case WEATHER_SNOW:                      return _("Weather: Snow");
239                 case WEATHER_PARTLY_CLOUDY:             return _("Weather: Partly Cloudy");
240                 case WEATHER_SMOKE:                     return _("Weather: Fog");
241                 case WEATHER_THUNDERSTORMS:             return _("Weather: Thunderstorms");
242                 case WEATHER_CLOUDY:                    return _("Weather: Cloudy");
243                 case WEATHER_DRIZZLE:                   return _("Weather: Rain");
244                 case WEATHER_SUNNY:                     return _("Weather: Sunny");
245                 case WEATHER_DUST:                      return _("Weather: Fog");
246                 case WEATHER_CLEAR:                     return _("Weather: Sunny");
247                 case WEATHER_MOSTLY_CLOUDY:             return _("Weather: Cloudy");
248                 case WEATHER_WINDY:                     return "";
249                 case WEATHER_RAIN_SHOWERS:              return _("Weather: Rain");
250                 case WEATHER_FOGGY:                     return _("Weather: Fog");
251                 case WEATHER_RAIN_OR_SNOW_MIXED:        return _("Weather: Rain");
252                 case WEATHER_SLEET:                     return _("Weather: Rain");
253                 case WEATHER_VERY_HOT_OR_HOT_HUMID:     return _("Weather: Sunny");
254                 case WEATHER_BLIZZARD:                  return _("Weather: Snow");
255                 case WEATHER_FREEZING_RAIN:             return _("Weather: Rain");
256                 case WEATHER_HAZE:                      return _("Weather: Fog");
257                 case WEATHER_BLOWING_SNOW:              return _("Weather: Snow");
258                 case WEATHER_FREEZING_DRIZZLE:          return _("Weather: Rain");
259                 case WEATHER_VERY_COLD_WIND_CHILL:      return "";
260                 case WEATHER_RAIN:                      return _("Weather: Rain");
261                 default:                                return NULL;
262         }
263 }
264
265 static float
266 ctof (float c)
267 {
268         return ((c * 9.0f / 5.0f) + 32.0f);
269 }
270
271 static float
272 cmtoin (float cm)
273 {
274         return cm / 2.54f;
275 }
276
277 static ECalComponent*
278 create_weather (ECalBackendWeather *cbw, WeatherForecast *report)
279 {
280         ECalBackendWeatherPrivate *priv;
281         ECalComponent             *cal_comp;
282         ECalComponentText          comp_summary;
283         icalcomponent             *ical_comp;
284         struct icaltimetype        itt;
285         ECalComponentDateTime      dt;
286         const char                *uid;
287         GSList                    *text_list = NULL;
288         ECalComponentText         *description;
289         char                      *pop, *snow;
290         ESource                   *source;
291         gboolean                   metric;
292         const char                *format;
293
294         g_return_val_if_fail (E_IS_CAL_BACKEND_WEATHER (cbw), NULL);
295
296         priv = cbw->priv;
297
298         source = e_cal_backend_get_source (E_CAL_BACKEND (cbw));
299         format = e_source_get_property (source, "units");
300         if (format == NULL) {
301                 format = e_source_get_property (source, "temperature");
302                 if (format == NULL)
303                         metric = FALSE;
304                 else
305                         metric = (strcmp (format, "fahrenheit") != 0);
306         } else {
307                 metric = (strcmp (format, "metric") == 0);
308         }
309
310         /* create the component and event object */
311         ical_comp = icalcomponent_new (ICAL_VEVENT_COMPONENT);
312         cal_comp = e_cal_component_new ();
313         e_cal_component_set_icalcomponent (cal_comp, ical_comp);
314
315         /* set uid */
316         uid = e_cal_component_gen_uid ();
317         e_cal_component_set_uid (cal_comp, uid);
318
319         /* Set all-day event's date from forecast data */
320         itt = icaltime_from_timet (report->date, 1);
321         dt.value = &itt;
322         dt.tzid = NULL;
323         e_cal_component_set_dtstart (cal_comp, &dt);
324
325         itt = icaltime_from_timet (report->date, 1);
326         icaltime_adjust (&itt, 1, 0, 0, 0);
327         dt.value = &itt;
328         dt.tzid = NULL;
329         /* We have to add 1 day to DTEND, as it is not inclusive. */
330         e_cal_component_set_dtend (cal_comp, &dt);
331
332         /* The summary is the high or high/low temperatures */
333         if (report->high == report->low) {
334                 if (metric)
335                         comp_summary.value = g_strdup_printf (_("%.1f°C - %s"), report->high, priv->city);
336                 else
337                         comp_summary.value = g_strdup_printf (_("%.1f°F - %s"), ctof (report->high), priv->city);
338         } else {
339                 if (metric)
340                         comp_summary.value = g_strdup_printf (_("%.1f/%.1f°C - %s"), report->high, report->low, priv->city);
341                 else
342                         comp_summary.value = g_strdup_printf (_("%.1f/%.1f°F - %s"), ctof (report->high), ctof (report->low), priv->city);
343         }
344         comp_summary.altrep = NULL;
345         e_cal_component_set_summary (cal_comp, &comp_summary);
346
347         if (report->pop != 0)
348                 pop = g_strdup_printf (_("%d%% chance of precipitation\n"), report->pop);
349         else
350                 pop = g_strdup ("");
351         if (report->snowhigh == 0)
352                 snow = g_strdup ("");
353         else if (report->snowhigh == report->snowlow) {
354                 if (metric)
355                         snow = g_strdup_printf (_("%.1fcm snow\n"), report->snowhigh);
356                 else
357                         snow = g_strdup_printf (_("%.1fin snow\n"), cmtoin(report->snowhigh));
358         } else {
359                 if (metric)
360                         snow = g_strdup_printf (_("%.1f-%.1fcm snow\n"), report->snowlow, report->snowhigh);
361                 else
362                         snow = g_strdup_printf (_("%.1f-%.1fin snow\n"), cmtoin(report->snowlow), cmtoin(report->snowhigh));
363         }
364         description = g_new0 (ECalComponentText, 1);
365         description->value = g_strdup_printf ("%s\n%s%s", getConditions (report), pop, snow);
366         description->altrep = "";
367         text_list = g_slist_append (text_list, description);
368         e_cal_component_set_description_list (cal_comp, text_list);
369
370         /* Set category and visibility */
371         e_cal_component_set_categories (cal_comp, getCategory (report));
372         e_cal_component_set_classification (cal_comp, E_CAL_COMPONENT_CLASS_PUBLIC);
373
374         /* Weather is shown as free time */
375         e_cal_component_set_transparency (cal_comp, E_CAL_COMPONENT_TRANSP_TRANSPARENT);
376
377         e_cal_component_commit_sequence (cal_comp);
378
379         g_free (pop);
380         g_free (snow);
381
382         return cal_comp;
383 }
384
385 static ECalBackendSyncStatus
386 e_cal_backend_weather_is_read_only (ECalBackendSync *backend, EDataCal *cal, gboolean *read_only)
387 {
388         *read_only = TRUE;
389
390         return GNOME_Evolution_Calendar_Success;
391 }
392
393 static ECalBackendSyncStatus
394 e_cal_backend_weather_get_cal_address (ECalBackendSync *backend, EDataCal *cal, char **address)
395 {
396         /* Weather has no particular email addresses associated with it */
397         *address = NULL;
398
399         return GNOME_Evolution_Calendar_Success;
400 }
401
402 static ECalBackendSyncStatus
403 e_cal_backend_weather_get_alarm_email_address (ECalBackendSync *backend, EDataCal *cal, char **address)
404 {
405         /* Weather has no particular email addresses associated with it */
406         *address = NULL;
407
408         return GNOME_Evolution_Calendar_Success;
409 }
410
411 static ECalBackendSyncStatus
412 e_cal_backend_weather_get_ldap_attribute (ECalBackendSync *backend, EDataCal *cal, char **attribute)
413 {
414         *attribute = NULL;
415
416         return GNOME_Evolution_Calendar_Success;
417 }
418
419 static ECalBackendSyncStatus
420 e_cal_backend_weather_get_static_capabilities (ECalBackendSync *backend, EDataCal *cal, char **capabilities)
421 {
422         *capabilities = g_strdup (CAL_STATIC_CAPABILITY_NO_ALARM_REPEAT ","
423                                   CAL_STATIC_CAPABILITY_NO_AUDIO_ALARMS  ","
424                                   CAL_STATIC_CAPABILITY_NO_DISPLAY_ALARMS  ","
425                                   CAL_STATIC_CAPABILITY_NO_PROCEDURE_ALARMS  ","
426                                   CAL_STATIC_CAPABILITY_NO_TASK_ASSIGNMENT  ","
427                                   CAL_STATIC_CAPABILITY_NO_THISANDFUTURE  ","
428                                   CAL_STATIC_CAPABILITY_NO_THISANDPRIOR);
429
430         return GNOME_Evolution_Calendar_Success;
431 }
432
433 static ECalBackendSyncStatus
434 e_cal_backend_weather_open (ECalBackendSync *backend, EDataCal *cal, gboolean only_if_exists, const char *username, const char *password)
435 {
436         ECalBackendWeather *cbw;
437         ECalBackendWeatherPrivate *priv;
438         const char *uri;
439
440         cbw = E_CAL_BACKEND_WEATHER (backend);
441         priv = cbw->priv;
442
443         uri = e_cal_backend_get_uri (E_CAL_BACKEND (backend));
444         if (priv->city)
445                 g_free (priv->city);
446         priv->city = g_strdup (strrchr (uri, '/') + 1);
447         
448         if (!priv->cache) {
449                 priv->cache = e_cal_backend_cache_new (uri, E_CAL_SOURCE_TYPE_EVENT);
450
451                 if (!priv->cache) {
452                         e_cal_backend_notify_error (E_CAL_BACKEND (cbw), _("Could not create cache file"));
453                         return GNOME_Evolution_Calendar_OtherError;
454                 }
455
456                 if (priv->default_zone) {
457                         icalcomponent *icalcomp = icaltimezone_get_component (priv->default_zone);
458                         icaltimezone *zone = icaltimezone_new ();
459
460                         icaltimezone_set_component (zone, icalcomponent_new_clone (icalcomp));
461
462                         g_hash_table_insert (priv->zones, g_strdup (icaltimezone_get_tzid (zone)), zone);
463                 }
464
465                 if (priv->mode == CAL_MODE_LOCAL)
466                         return GNOME_Evolution_Calendar_Success;
467
468                 g_idle_add ((GSourceFunc) begin_retrieval_cb, cbw);
469         }
470
471         return GNOME_Evolution_Calendar_Success;
472 }
473
474 static ECalBackendSyncStatus
475 e_cal_backend_weather_remove (ECalBackendSync *backend, EDataCal *cal)
476 {
477         ECalBackendWeather *cbw;
478         ECalBackendWeatherPrivate *priv;
479
480         cbw = E_CAL_BACKEND_WEATHER (backend);
481         priv = cbw->priv;
482
483         if (!priv->cache)
484                 return GNOME_Evolution_Calendar_OtherError;
485
486         e_file_cache_remove (E_FILE_CACHE (priv->cache));
487         return GNOME_Evolution_Calendar_Success;
488 }
489
490 static ECalBackendSyncStatus
491 e_cal_backend_weather_discard_alarm (ECalBackendSync *backend, EDataCal *cal, const char *uid, const char *auid)
492 {
493         return GNOME_Evolution_Calendar_Success;
494 }
495
496 static ECalBackendSyncStatus
497 e_cal_backend_weather_receive_objects (ECalBackendSync *backend, EDataCal *cal, const char *calobj)
498 {
499         return GNOME_Evolution_Calendar_PermissionDenied;
500 }
501
502 static ECalBackendSyncStatus
503 e_cal_backend_weather_get_default_object (ECalBackendSync *backend, EDataCal *cal, char **object)
504 {
505         return GNOME_Evolution_Calendar_UnsupportedMethod;
506 }
507
508 static ECalBackendSyncStatus
509 e_cal_backend_weather_get_object (ECalBackendSync *backend, EDataCal *cal, const char *uid, const char *rid, char **object)
510 {
511         ECalBackendWeather *cbw = E_CAL_BACKEND_WEATHER (backend);
512         ECalBackendWeatherPrivate *priv = cbw->priv;
513         ECalComponent *comp;
514
515         g_return_val_if_fail (uid != NULL, GNOME_Evolution_Calendar_ObjectNotFound);
516         g_return_val_if_fail (priv->cache != NULL, GNOME_Evolution_Calendar_ObjectNotFound);
517
518         comp = e_cal_backend_cache_get_component (priv->cache, uid, rid);
519         g_return_val_if_fail (comp != NULL, GNOME_Evolution_Calendar_ObjectNotFound);
520
521         *object = e_cal_component_get_as_string (comp);
522         g_object_unref (comp);
523
524         return GNOME_Evolution_Calendar_Success;
525 }
526
527 static ECalBackendSyncStatus
528 e_cal_backend_weather_get_object_list (ECalBackendSync *backend, EDataCal *cal, const char *sexp_string, GList **objects)
529 {
530         ECalBackendWeather *cbw = E_CAL_BACKEND_WEATHER (backend);
531         ECalBackendWeatherPrivate *priv = cbw->priv;
532         ECalBackendSExp *sexp = e_cal_backend_sexp_new (sexp_string);
533         GList *components, *l;
534
535         if (!sexp)
536                 return GNOME_Evolution_Calendar_InvalidQuery;
537
538         *objects = NULL;
539         components = e_cal_backend_cache_get_components (priv->cache);
540         for (l = components; l != NULL; l = g_list_next (l)) {
541                 if (e_cal_backend_sexp_match_comp (sexp, E_CAL_COMPONENT (l->data), E_CAL_BACKEND (backend)))
542                         *objects = g_list_append (*objects, e_cal_component_get_as_string (l->data));
543         }
544
545         g_list_foreach (components, (GFunc) g_object_unref, NULL);
546         g_list_free (components);
547         g_object_unref (sexp);
548
549         return GNOME_Evolution_Calendar_Success;
550 }
551
552 static ECalBackendSyncStatus
553 e_cal_backend_weather_get_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzid, char **object)
554 {
555         ECalBackendWeather *cbw;
556         ECalBackendWeatherPrivate *priv;
557         icaltimezone *zone;
558         icalcomponent *icalcomp;
559
560         cbw = E_CAL_BACKEND_WEATHER (backend);
561         priv = cbw->priv;
562
563         g_return_val_if_fail (tzid != NULL, GNOME_Evolution_Calendar_ObjectNotFound);
564
565         zone = e_cal_backend_internal_get_timezone (E_CAL_BACKEND (backend), tzid);
566         if (!zone)
567                 return GNOME_Evolution_Calendar_ObjectNotFound;
568
569         icalcomp = icaltimezone_get_component (zone);
570         if (!icalcomp)
571                 return GNOME_Evolution_Calendar_InvalidObject;
572
573         *object = g_strdup (icalcomponent_as_ical_string (icalcomp));
574
575         return GNOME_Evolution_Calendar_Success;
576 }
577
578 static ECalBackendSyncStatus
579 e_cal_backend_weather_add_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzobj)
580 {
581         ECalBackendWeather *cbw;
582         ECalBackendWeatherPrivate *priv;
583         icalcomponent *tz_comp;
584         icaltimezone *zone;
585         char *tzid;
586
587         cbw = (ECalBackendWeather*) backend;
588
589         g_return_val_if_fail (E_IS_CAL_BACKEND_WEATHER (cbw), GNOME_Evolution_Calendar_OtherError);
590         g_return_val_if_fail (tzobj != NULL, GNOME_Evolution_Calendar_OtherError);
591
592         priv = cbw->priv;
593
594         tz_comp = icalparser_parse_string (tzobj);
595         g_return_val_if_fail (tz_comp != NULL, GNOME_Evolution_Calendar_InvalidObject);
596
597         if (icalcomponent_isa (tz_comp) != ICAL_VTIMEZONE_COMPONENT)
598                 return GNOME_Evolution_Calendar_InvalidObject;
599
600         zone = icaltimezone_new ();
601         icaltimezone_set_component (zone, tz_comp);
602         tzid = icaltimezone_get_tzid (zone);
603
604         if (g_hash_table_lookup (priv->zones, tzid)) {
605                 icaltimezone_free (zone, TRUE);
606                 return GNOME_Evolution_Calendar_Success;
607         }
608
609         g_hash_table_insert (priv->zones, g_strdup (tzid), zone);
610         return GNOME_Evolution_Calendar_Success;
611 }
612
613 static ECalBackendSyncStatus
614 e_cal_backend_weather_set_default_zone (ECalBackendSync *backend, EDataCal *cal, const char *tzobj)
615 {
616         icalcomponent *tz_comp;
617         ECalBackendWeather *cbw;
618         ECalBackendWeatherPrivate *priv;
619         icaltimezone *zone;
620
621         cbw = (ECalBackendWeather *) backend;
622
623         g_return_val_if_fail (E_IS_CAL_BACKEND_WEATHER (cbw), GNOME_Evolution_Calendar_OtherError);
624         g_return_val_if_fail (tzobj != NULL, GNOME_Evolution_Calendar_OtherError);
625
626         priv = cbw->priv;
627
628         tz_comp = icalparser_parse_string (tzobj);
629         if (!tz_comp)
630                 return GNOME_Evolution_Calendar_InvalidObject;
631
632         zone = icaltimezone_new ();
633         icaltimezone_set_component (zone, tz_comp);
634
635         if (priv->default_zone)
636                 icaltimezone_free (priv->default_zone, 1);
637
638         /* Set the default timezone to it. */
639         priv->default_zone = zone;
640
641         return GNOME_Evolution_Calendar_Success;
642 }
643
644 static ECalBackendSyncStatus
645 e_cal_backend_weather_get_free_busy (ECalBackendSync *backend, EDataCal *cal, GList *users, time_t start, time_t end, GList **freebusy)
646 {
647         /* Weather doesn't count as busy time */
648         icalcomponent *vfb = icalcomponent_new_vfreebusy ();
649         icaltimezone *utc_zone = icaltimezone_get_utc_timezone ();
650         char *calobj;
651
652         icalcomponent_set_dtstart (vfb, icaltime_from_timet_with_zone (start, FALSE, utc_zone));
653         icalcomponent_set_dtend (vfb, icaltime_from_timet_with_zone (end, FALSE, utc_zone));
654
655         calobj = icalcomponent_as_ical_string (vfb);
656         *freebusy = g_list_append (NULL, g_strdup (calobj));
657         icalcomponent_free (vfb);
658
659         return GNOME_Evolution_Calendar_Success;
660 }
661
662 static ECalBackendSyncStatus
663 e_cal_backend_weather_get_changes (ECalBackendSync *backend, EDataCal *cal, const char *change_id, GList **adds, GList **modifies, GList **deletes)
664 {
665         return GNOME_Evolution_Calendar_Success;
666 }
667
668 static gboolean
669 e_cal_backend_weather_is_loaded (ECalBackend *backend)
670 {
671         ECalBackendWeather *cbw;
672         ECalBackendWeatherPrivate *priv;
673
674         cbw = E_CAL_BACKEND_WEATHER (backend);
675         priv = cbw->priv;
676
677         if (!priv->cache)
678                 return FALSE;
679
680         return TRUE;
681 }
682
683 static void e_cal_backend_weather_start_query (ECalBackend *backend, EDataCalView *query)
684 {
685         ECalBackendWeather *cbw;
686         ECalBackendWeatherPrivate *priv;
687         ECalBackendSExp *sexp;
688         GList *components, *l, *objects;
689
690         cbw = E_CAL_BACKEND_WEATHER (backend);
691         priv = cbw->priv;
692
693         if (!priv->cache) {
694                 e_data_cal_view_notify_done (query, GNOME_Evolution_Calendar_NoSuchCal);
695                 return;
696         }
697
698         sexp = e_data_cal_view_get_object_sexp (query);
699         if (!sexp) {
700                 e_data_cal_view_notify_done (query, GNOME_Evolution_Calendar_InvalidQuery);
701                 return;
702         }
703
704         objects = NULL;
705         components = e_cal_backend_cache_get_components (priv->cache);
706         for (l = components; l != NULL; l = g_list_next (l)) {
707                 if (e_cal_backend_sexp_match_comp (sexp, E_CAL_COMPONENT (l->data), backend))
708                         objects = g_list_append (objects, e_cal_component_get_as_string (l->data));
709         }
710
711         if (objects)
712                 e_data_cal_view_notify_objects_added (query, (const GList *) objects);
713
714         g_list_foreach (components, (GFunc) g_object_unref, NULL);
715         g_list_free (components);
716         g_list_foreach (objects, (GFunc) g_free, NULL);
717         g_list_free (objects);
718         g_object_unref (sexp);
719
720         e_data_cal_view_notify_done (query, GNOME_Evolution_Calendar_Success);
721 }
722
723 static CalMode
724 e_cal_backend_weather_get_mode (ECalBackend *backend)
725 {
726         ECalBackendWeather *cbw;
727         ECalBackendWeatherPrivate *priv;
728
729         cbw = E_CAL_BACKEND_WEATHER (backend);
730         priv = cbw->priv;
731
732         return priv->mode;
733 }
734
735 static void
736 e_cal_backend_weather_set_mode (ECalBackend *backend, CalMode mode)
737 {
738         ECalBackendWeather *cbw;
739         ECalBackendWeatherPrivate *priv;
740         GNOME_Evolution_Calendar_CalMode set_mode;
741         gboolean loaded;
742
743         cbw = E_CAL_BACKEND_WEATHER (backend);
744         priv = cbw->priv;
745
746         loaded = e_cal_backend_weather_is_loaded (backend);
747
748         switch (mode) {
749                 case CAL_MODE_LOCAL:
750                 case CAL_MODE_REMOTE:
751                         priv->mode = mode;
752                         set_mode = cal_mode_to_corba (mode);
753                         if (loaded && priv->reload_timeout_id) {
754                                 g_source_remove (priv->reload_timeout_id);
755                                 priv->reload_timeout_id = 0;
756                         }
757                         break;
758                 case CAL_MODE_ANY:
759                         priv->mode = mode;
760                         set_mode = cal_mode_to_corba (mode);
761                         if (loaded)
762                                 g_idle_add ((GSourceFunc) begin_retrieval_cb, backend);
763                         break;
764                 default:
765                         set_mode = GNOME_Evolution_Calendar_MODE_ANY;
766                         break;
767         }
768
769         if (loaded) {
770                 if (set_mode == GNOME_Evolution_Calendar_MODE_ANY)
771                         e_cal_backend_notify_mode (backend,
772                                                    GNOME_Evolution_Calendar_CalListener_MODE_NOT_SUPPORTED,
773                                                    cal_mode_to_corba (priv->mode));
774                 else
775                         e_cal_backend_notify_mode (backend,
776                                                    GNOME_Evolution_Calendar_CalListener_MODE_SET,
777                                                    set_mode);
778         }
779 }
780
781 static icaltimezone *
782 e_cal_backend_weather_internal_get_default_timezone (ECalBackend *backend)
783 {
784         ECalBackendWeather *cbw = E_CAL_BACKEND_WEATHER (backend);
785
786         return cbw->priv->default_zone;
787 }
788
789 static icaltimezone *
790 e_cal_backend_weather_internal_get_timezone (ECalBackend *backend, const char *tzid)
791 {
792         icaltimezone *zone;
793         if (!strcmp (tzid, "UTC"))
794                 zone = icaltimezone_get_utc_timezone ();
795         else
796                 zone = icaltimezone_get_builtin_timezone_from_tzid (tzid);
797         return zone;
798 }
799
800 static void
801 free_zone (gpointer data)
802 {
803         icaltimezone_free (data, TRUE);
804 }
805
806 /* Finalize handler for the weather backend */
807 static void
808 e_cal_backend_weather_finalize (GObject *object)
809 {
810         ECalBackendWeather *cbw;
811         ECalBackendWeatherPrivate *priv;
812
813         g_return_if_fail (object != NULL);
814         g_return_if_fail (E_IS_CAL_BACKEND_WEATHER (object));
815
816         cbw = (ECalBackendWeather *) object;
817         priv = cbw->priv;
818
819         if (priv->cache) {
820                 g_object_unref (priv->cache);
821                 priv->cache = NULL;
822         }
823
824         g_hash_table_destroy (priv->zones);
825
826         if (priv->city) {
827                 g_free (priv->city);
828                 priv->city = NULL;
829         }
830
831         if (priv->default_zone) {
832                 icaltimezone_free (priv->default_zone, 1);
833                 priv->default_zone = NULL;
834         }
835
836         g_free (priv);
837         cbw->priv = NULL;
838
839         if (G_OBJECT_CLASS (parent_class)->finalize)
840                 (* G_OBJECT_CLASS (parent_class)->finalize) (object);
841 }
842
843 /* Object initialization function for the weather backend */
844 static void
845 e_cal_backend_weather_init (ECalBackendWeather *cbw, ECalBackendWeatherClass *class)
846 {
847         ECalBackendWeatherPrivate *priv;
848
849         priv = g_new0 (ECalBackendWeatherPrivate, 1);
850
851         cbw->priv = priv;
852
853         priv->reload_timeout_id = 0;
854         priv->source_changed_id = 0;
855         priv->opened = FALSE;
856         priv->source = NULL;
857         priv->cache = NULL;
858         priv->city = NULL;
859
860         priv->zones = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, free_zone);
861
862         e_cal_backend_sync_set_lock (E_CAL_BACKEND_SYNC (cbw), TRUE);
863 }
864
865 /* Class initialization function for the weather backend */
866 static void
867 e_cal_backend_weather_class_init (ECalBackendWeatherClass *class)
868 {
869         GObjectClass *object_class;
870         ECalBackendClass *backend_class;
871         ECalBackendSyncClass *sync_class;
872
873         object_class = (GObjectClass *) class;
874         backend_class = (ECalBackendClass *) class;
875         sync_class = (ECalBackendSyncClass *) class;
876
877         parent_class = (ECalBackendSyncClass *) g_type_class_peek_parent (class);
878
879         object_class->finalize = e_cal_backend_weather_finalize;
880
881         sync_class->is_read_only_sync = e_cal_backend_weather_is_read_only;
882         sync_class->get_cal_address_sync = e_cal_backend_weather_get_cal_address;
883         sync_class->get_alarm_email_address_sync = e_cal_backend_weather_get_alarm_email_address;
884         sync_class->get_ldap_attribute_sync = e_cal_backend_weather_get_ldap_attribute;
885         sync_class->get_static_capabilities_sync = e_cal_backend_weather_get_static_capabilities;
886         sync_class->open_sync = e_cal_backend_weather_open;
887         sync_class->remove_sync = e_cal_backend_weather_remove;
888         sync_class->discard_alarm_sync = e_cal_backend_weather_discard_alarm;
889         sync_class->receive_objects_sync = e_cal_backend_weather_receive_objects;
890         sync_class->get_default_object_sync = e_cal_backend_weather_get_default_object;
891         sync_class->get_object_sync = e_cal_backend_weather_get_object;
892         sync_class->get_object_list_sync = e_cal_backend_weather_get_object_list;
893         sync_class->get_timezone_sync = e_cal_backend_weather_get_timezone;
894         sync_class->add_timezone_sync = e_cal_backend_weather_add_timezone;
895         sync_class->set_default_zone_sync = e_cal_backend_weather_set_default_zone;
896         sync_class->get_freebusy_sync = e_cal_backend_weather_get_free_busy;
897         sync_class->get_changes_sync = e_cal_backend_weather_get_changes;
898         backend_class->is_loaded = e_cal_backend_weather_is_loaded;
899         backend_class->start_query = e_cal_backend_weather_start_query;
900         backend_class->get_mode = e_cal_backend_weather_get_mode;
901         backend_class->set_mode = e_cal_backend_weather_set_mode;
902
903         backend_class->internal_get_default_timezone = e_cal_backend_weather_internal_get_default_timezone;
904         backend_class->internal_get_timezone = e_cal_backend_weather_internal_get_timezone;
905 }
906
907
908 /**
909  * e_cal_backend_weather_get_type:
910  * @void: 
911  *
912  * Registers the #ECalBackendWeather class if necessary, and returns
913  * the type ID associated to it.
914  *
915  * Return value: The type ID of the #ECalBackendWeather class.
916  **/
917 GType
918 e_cal_backend_weather_get_type (void)
919 {
920         static GType e_cal_backend_weather_type = 0;
921
922         if (!e_cal_backend_weather_type) {
923                 static GTypeInfo info = {
924                         sizeof (ECalBackendWeatherClass),
925                         (GBaseInitFunc) NULL,
926                         (GBaseFinalizeFunc) NULL,
927                         (GClassInitFunc) e_cal_backend_weather_class_init,
928                         NULL, NULL,
929                         sizeof (ECalBackendWeather),
930                         0,
931                         (GInstanceInitFunc) e_cal_backend_weather_init
932                 };
933                 e_cal_backend_weather_type = g_type_register_static (E_TYPE_CAL_BACKEND_SYNC, "ECalBackendWeather", &info, 0);
934         }
935
936         return e_cal_backend_weather_type;
937 }