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