1 /*
2 * This program is free software; you can redistribute it and/or modify it
3 * under the terms of the GNU Lesser General Public License as published by
4 * the Free Software Foundation.
5 *
6 * This program is distributed in the hope that it will be useful, but
7 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
8 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
9 * for more details.
10 *
11 * You should have received a copy of the GNU Lesser General Public License
12 * along with this program; if not, see <http://www.gnu.org/licenses/>.
13 *
14 *
15 * Authors:
16 * Philip Van Hoof <pvanhoof@gnome.org>
17 *
18 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
19 *
20 */
21
22 #include "evolution-config.h"
23
24 #include <string.h>
25 #include <glib/gi18n.h>
26
27 #include <libxml/xmlmemory.h>
28 #include <libxml/parser.h>
29 #include <libxml/tree.h>
30 #include <libxml/xmlIO.h>
31 #include <libxml/xpath.h>
32
33 #include "format-handler.h"
34
35 static void add_string_to_rdf (xmlNodePtr node,
36 const gchar *tag,
37 const gchar *value);
38
39 /* Use { */
40
41 /* #include <calendar/gui/calendar-config-keys.h> */
42 /* #include <calendar/gui/calendar-config.h> */
43
44 /* } or { */
45 #define CALENDAR_CONFIG_PREFIX "/apps/evolution/calendar"
46 #define CALENDAR_CONFIG_TIMEZONE CALENDAR_CONFIG_PREFIX "/display/timezone"
47
48 static gchar *
calendar_config_get_timezone(void)49 calendar_config_get_timezone (void)
50 {
51 GSettings *settings;
52 gchar *retval = NULL;
53
54 settings = e_util_ref_settings ("org.gnome.evolution.calendar");
55 retval = g_settings_get_string (settings, "timezone");
56 g_object_unref (settings);
57 if (!retval)
58 retval = g_strdup ("UTC");
59
60 return retval;
61 }
62 /* } */
63
64 enum { /* XML helper enum */
65 ECALCOMPONENTTEXT,
66 ECALCOMPONENTATTENDEE,
67 CONSTCHAR
68 };
69
70 static void
display_error_message(GtkWidget * parent,const gchar * error_message)71 display_error_message (GtkWidget *parent,
72 const gchar *error_message)
73 {
74 GtkWidget *dialog;
75
76 dialog = gtk_message_dialog_new (
77 GTK_WINDOW (parent), 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
78 "%s", error_message);
79 gtk_dialog_run (GTK_DIALOG (dialog));
80 gtk_widget_destroy (dialog);
81 }
82
83 /* Some helpers for the xml stuff */
84 static void
add_list_to_rdf(xmlNodePtr node,const gchar * tag,GSList * list_in,gint type)85 add_list_to_rdf (xmlNodePtr node,
86 const gchar *tag,
87 GSList *list_in,
88 gint type)
89 {
90 if (list_in) {
91 GSList *list = list_in;
92
93 while (list) {
94 const gchar *str = NULL;
95
96 switch (type) {
97 case ECALCOMPONENTATTENDEE:
98 str = itip_strip_mailto (e_cal_component_attendee_get_value ((ECalComponentAttendee *) list->data));
99 break;
100 case ECALCOMPONENTTEXT:
101 str = e_cal_component_text_get_value ((ECalComponentText *) list->data);
102 break;
103 case CONSTCHAR:
104 default:
105 str = list->data;
106 break;
107 }
108
109 add_string_to_rdf (node, tag, str);
110
111 list = g_slist_next (list);
112 }
113 }
114 }
115
116 static void
add_nummeric_to_rdf(xmlNodePtr node,const gchar * tag,gint nummeric)117 add_nummeric_to_rdf (xmlNodePtr node,
118 const gchar *tag,
119 gint nummeric)
120 {
121 if (nummeric >= 0) {
122 gchar *value = g_strdup_printf ("%d", nummeric);
123 xmlNodePtr cur_node = xmlNewChild (node, NULL, (guchar *) tag, (guchar *) value);
124 xmlSetProp (cur_node, (const guchar *)"rdf:datatype", (const guchar *)"http://www.w3.org/2001/XMLSchema#integer");
125 g_free (value);
126 }
127 }
128
129 static void
add_time_to_rdf(xmlNodePtr node,const gchar * tag,ICalTime * time)130 add_time_to_rdf (xmlNodePtr node,
131 const gchar *tag,
132 ICalTime *time)
133 {
134 if (time) {
135 xmlNodePtr cur_node = NULL;
136 struct tm mytm = e_cal_util_icaltime_to_tm (time);
137 gchar *str = (gchar *) g_malloc (sizeof (gchar) * 200);
138 gchar *tmp = NULL;
139 gchar *timezone;
140 /*
141 * Translator: the %FT%T is the thirth argument for a strftime function.
142 * It lets you define the formatting of the date in the rdf-file.
143 * Also check out http://www.w3.org/2002/12/cal/tzd
144 * */
145 e_utf8_strftime (str, 200, _("%FT%T"), &mytm);
146
147 cur_node = xmlNewChild (node, NULL, (guchar *) tag, (guchar *) str);
148
149 /* Not sure about this property */
150 timezone = calendar_config_get_timezone ();
151 tmp = g_strdup_printf ("http://www.w3.org/2002/12/cal/tzd/%s#tz", timezone);
152 xmlSetProp (cur_node, (const guchar *)"rdf:datatype", (guchar *) tmp);
153 g_free (tmp);
154 g_free (timezone);
155 g_free (str);
156 }
157 }
158
159 static void
add_string_to_rdf(xmlNodePtr node,const gchar * tag,const gchar * value)160 add_string_to_rdf (xmlNodePtr node,
161 const gchar *tag,
162 const gchar *value)
163 {
164 if (value) {
165 xmlNodePtr cur_node = NULL;
166 cur_node = xmlNewChild (node, NULL, (guchar *) tag, (guchar *) value);
167 xmlSetProp (cur_node, (const guchar *)"rdf:datatype", (const guchar *)"http://www.w3.org/2001/XMLSchema#string");
168 }
169 }
170
171 static void
do_save_calendar_rdf(FormatHandler * handler,ESourceSelector * selector,EClientCache * client_cache,gchar * dest_uri)172 do_save_calendar_rdf (FormatHandler *handler,
173 ESourceSelector *selector,
174 EClientCache *client_cache,
175 gchar *dest_uri)
176 {
177
178 /*
179 * According to some documentation about CSV, newlines 'are' allowed
180 * in CSV-files. But you 'do' have to put the value between quotes.
181 * The helper 'string_needsquotes' will check for that
182 *
183 * http://www.creativyst.com/Doc/Articles/CSV/CSV01.htm
184 * http://www.creativyst.com/cgi-bin/Prod/15/eg/csv2xml.pl
185 */
186
187 ESource *primary_source;
188 EClient *source_client;
189 GError *error = NULL;
190 GSList *objects = NULL;
191 gchar *temp = NULL;
192 GOutputStream *stream;
193
194 if (!dest_uri)
195 return;
196
197 /* open source client */
198 primary_source = e_source_selector_ref_primary_selection (selector);
199 source_client = e_client_cache_get_client_sync (client_cache,
200 primary_source, e_source_selector_get_extension_name (selector), 30, NULL, &error);
201 g_object_unref (primary_source);
202
203 /* Sanity check. */
204 g_return_if_fail (
205 ((source_client != NULL) && (error == NULL)) ||
206 ((source_client == NULL) && (error != NULL)));
207
208 if (source_client == NULL) {
209 display_error_message (
210 gtk_widget_get_toplevel (GTK_WIDGET (selector)),
211 error->message);
212 g_error_free (error);
213 return;
214 }
215
216 stream = open_for_writing (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (selector))), dest_uri, &error);
217
218 if (stream && e_cal_client_get_object_list_as_comps_sync (E_CAL_CLIENT (source_client), "#t", &objects, NULL, NULL)) {
219 GSList *iter;
220
221 xmlBufferPtr buffer = xmlBufferCreate ();
222 xmlDocPtr doc = xmlNewDoc ((xmlChar *) "1.0");
223 xmlNodePtr fnode;
224
225 doc->children = xmlNewDocNode (doc, NULL, (const guchar *)"rdf:RDF", NULL);
226 xmlSetProp (doc->children, (const guchar *)"xmlns:rdf", (const guchar *)"http://www.w3.org/1999/02/22-rdf-syntax-ns#");
227 xmlSetProp (doc->children, (const guchar *)"xmlns", (const guchar *)"http://www.w3.org/2002/12/cal/ical#");
228
229 fnode = xmlNewChild (doc->children, NULL, (const guchar *)"Vcalendar", NULL);
230
231 /* Should Evolution publicise these? */
232 xmlSetProp (fnode, (const guchar *)"xmlns:x-wr", (const guchar *)"http://www.w3.org/2002/12/cal/prod/Apple_Comp_628d9d8459c556fa#");
233 xmlSetProp (fnode, (const guchar *)"xmlns:x-lic", (const guchar *)"http://www.w3.org/2002/12/cal/prod/Apple_Comp_628d9d8459c556fa#");
234
235 /* Not sure if it's correct like this */
236 xmlNewChild (fnode, NULL, (const guchar *)"prodid", (const guchar *)"-//" PACKAGE " " VERSION VERSION_SUBSTRING " " VERSION_COMMENT "//iCal 1.0//EN");
237
238 /* Assuming GREGORIAN is the only supported calendar scale */
239 xmlNewChild (fnode, NULL, (const guchar *)"calscale", (const guchar *)"GREGORIAN");
240
241 temp = calendar_config_get_timezone ();
242 xmlNewChild (fnode, NULL, (const guchar *)"x-wr:timezone", (guchar *) temp);
243 g_free (temp);
244
245 xmlNewChild (fnode, NULL, (const guchar *)"method", (const guchar *)"PUBLISH");
246
247 xmlNewChild (fnode, NULL, (const guchar *)"x-wr:relcalid", (guchar *) e_source_get_uid (primary_source));
248
249 xmlNewChild (fnode, NULL, (const guchar *)"x-wr:calname", (guchar *) e_source_get_display_name (primary_source));
250
251 /* Version of this RDF-format */
252 xmlNewChild (fnode, NULL, (const guchar *)"version", (const guchar *)"2.0");
253
254 for (iter = objects; iter; iter = iter->next) {
255 ECalComponent *comp = iter->data;
256 const gchar *temp_constchar;
257 gchar *tmp_str;
258 GSList *temp_list;
259 ECalComponentDateTime *temp_dt;
260 ICalTime *temp_time;
261 gint temp_int;
262 ECalComponentText *temp_comptext;
263 xmlNodePtr c_node = xmlNewChild (fnode, NULL, (const guchar *)"component", NULL);
264 xmlNodePtr node = xmlNewChild (c_node, NULL, (const guchar *)"Vevent", NULL);
265
266 /* Getting the stuff */
267 temp_constchar = e_cal_component_get_uid (comp);
268 tmp_str = g_strdup_printf ("#%s", temp_constchar);
269 xmlSetProp (node, (const guchar *)"about", (guchar *) tmp_str);
270 g_free (tmp_str);
271 add_string_to_rdf (node, "uid", temp_constchar);
272
273 temp_comptext = e_cal_component_get_summary (comp);
274 if (temp_comptext)
275 add_string_to_rdf (node, "summary", e_cal_component_text_get_value (temp_comptext));
276 e_cal_component_text_free (temp_comptext);
277
278 temp_list = e_cal_component_get_descriptions (comp);
279 add_list_to_rdf (node, "description", temp_list, ECALCOMPONENTTEXT);
280 g_slist_free_full (temp_list, e_cal_component_text_free);
281
282 temp_list = e_cal_component_get_categories_list (comp);
283 add_list_to_rdf (node, "categories", temp_list, CONSTCHAR);
284 g_slist_free_full (temp_list, g_free);
285
286 temp_list = e_cal_component_get_comments (comp);
287 add_list_to_rdf (node, "comment", temp_list, ECALCOMPONENTTEXT);
288 g_slist_free_full (temp_list, e_cal_component_text_free);
289
290 temp_time = e_cal_component_get_completed (comp);
291 add_time_to_rdf (node, "completed", temp_time);
292 g_clear_object (&temp_time);
293
294 temp_time = e_cal_component_get_created (comp);
295 add_time_to_rdf (node, "created", temp_time);
296 g_clear_object (&temp_time);
297
298 temp_list = e_cal_component_get_contacts (comp);
299 add_list_to_rdf (node, "contact", temp_list, ECALCOMPONENTTEXT);
300 g_slist_free_full (temp_list, e_cal_component_text_free);
301
302 temp_dt = e_cal_component_get_dtstart (comp);
303 add_time_to_rdf (node, "dtstart", temp_dt && e_cal_component_datetime_get_value (temp_dt) ?
304 e_cal_component_datetime_get_value (temp_dt) : NULL);
305 e_cal_component_datetime_free (temp_dt);
306
307 temp_dt = e_cal_component_get_dtend (comp);
308 add_time_to_rdf (node, "dtend", temp_dt && e_cal_component_datetime_get_value (temp_dt) ?
309 e_cal_component_datetime_get_value (temp_dt) : NULL);
310 e_cal_component_datetime_free (temp_dt);
311
312 temp_dt = e_cal_component_get_due (comp);
313 add_time_to_rdf (node, "due", temp_dt && e_cal_component_datetime_get_value (temp_dt) ?
314 e_cal_component_datetime_get_value (temp_dt) : NULL);
315 e_cal_component_datetime_free (temp_dt);
316
317 temp_int = e_cal_component_get_percent_complete (comp);
318 add_nummeric_to_rdf (node, "percentComplete", temp_int);
319
320 temp_int = e_cal_component_get_priority (comp);
321 add_nummeric_to_rdf (node, "priority", temp_int);
322
323 tmp_str = e_cal_component_get_url (comp);
324 add_string_to_rdf (node, "URL", tmp_str);
325 g_free (tmp_str);
326
327 if (e_cal_component_has_attendees (comp)) {
328 temp_list = e_cal_component_get_attendees (comp);
329 add_list_to_rdf (node, "attendee", temp_list, ECALCOMPONENTATTENDEE);
330 g_slist_free_full (temp_list, e_cal_component_attendee_free);
331 }
332
333 tmp_str = e_cal_component_get_location (comp);
334 add_string_to_rdf (node, "location", tmp_str);
335 g_free (tmp_str);
336
337 temp_time = e_cal_component_get_last_modified (comp);
338 add_time_to_rdf (node, "lastModified",temp_time);
339 g_clear_object (&temp_time);
340 }
341
342 /* I used a buffer rather than xmlDocDump: I want gio support */
343 xmlNodeDump (buffer, doc, doc->children, 2, 1);
344
345 g_output_stream_write_all (stream, xmlBufferContent (buffer), xmlBufferLength (buffer), NULL, NULL, &error);
346 g_output_stream_close (stream, NULL, NULL);
347
348 e_util_free_nullable_object_slist (objects);
349
350 xmlBufferFree (buffer);
351 xmlFreeDoc (doc);
352 }
353
354 if (stream)
355 g_object_unref (stream);
356
357 g_object_unref (source_client);
358
359 if (error != NULL) {
360 display_error_message (
361 gtk_widget_get_toplevel (GTK_WIDGET (selector)),
362 error->message);
363 g_error_free (error);
364 }
365 }
366
367 FormatHandler *
rdf_format_handler_new(void)368 rdf_format_handler_new (void)
369 {
370 FormatHandler *handler = g_new0 (FormatHandler, 1);
371
372 handler->isdefault = FALSE;
373 handler->combo_label = _("RDF (.rdf)");
374 handler->filename_ext = ".rdf";
375 handler->options_widget = NULL;
376 handler->save = do_save_calendar_rdf;
377
378 return handler;
379 }
380