1 /* Evolution calendar - iCalendar component object
2  *
3  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
4  *
5  * This library is free software: you can redistribute it and/or modify it
6  * under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation.
8  *
9  * This library is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12  * for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this library. If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Authors: Federico Mena-Quintero <federico@ximian.com>
18  */
19 
20 /**
21  * SECTION:e-cal-component
22  * @short_description: A convenience interface for interacting with events
23  * @include: libecal/libecal.h
24  *
25  * This is the main user facing interface used for representing an event
26  * or other component in a given calendar.
27  **/
28 
29 #include "evolution-data-server-config.h"
30 
31 #include <string.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <glib/gi18n-lib.h>
35 #include <glib/gstdio.h>
36 
37 #include <libedataserver/libedataserver.h>
38 
39 #include "e-cal-component.h"
40 #include "e-cal-time-util.h"
41 #include "e-cal-util.h"
42 
43 #ifdef G_OS_WIN32
44 #define getgid() 0
45 #define getppid() 0
46 #endif
47 
48 struct _ECalComponentPrivate {
49 	/* The icalcomponent we wrap */
50 	ICalComponent *icalcomp;
51 
52 	/* Whether we should increment the sequence number when piping the
53 	 * object over the wire.
54 	 */
55 	guint need_sequence_inc : 1;
56 };
57 
G_DEFINE_TYPE_WITH_PRIVATE(ECalComponent,e_cal_component,G_TYPE_OBJECT)58 G_DEFINE_TYPE_WITH_PRIVATE (ECalComponent, e_cal_component, G_TYPE_OBJECT)
59 
60 /* Frees the internal icalcomponent only if it does not have a parent.  If it
61  * does, it means we don't own it and we shouldn't free it.
62  */
63 static void
64 free_icalcomponent (ECalComponent *comp,
65                     gboolean free)
66 {
67 	if (!comp->priv->icalcomp)
68 		return;
69 
70 	if (free) {
71 		g_clear_object (&comp->priv->icalcomp);
72 	}
73 
74 	/* Clean up */
75 	comp->priv->need_sequence_inc = FALSE;
76 }
77 
78 /* The 'func' returns TRUE to continue */
79 static void
foreach_subcomponent(ICalComponent * icalcomp,ICalComponentKind comp_kind,gboolean (* func)(ICalComponent * icalcomp,ICalComponent * subcomp,gpointer user_data),gpointer user_data)80 foreach_subcomponent (ICalComponent *icalcomp,
81 		      ICalComponentKind comp_kind,
82 		      gboolean (* func) (ICalComponent *icalcomp,
83 					 ICalComponent *subcomp,
84 					 gpointer user_data),
85 		      gpointer user_data)
86 {
87 	ICalCompIter *iter;
88 	ICalComponent *subcomp;
89 
90 	g_return_if_fail (icalcomp != NULL);
91 	g_return_if_fail (func != NULL);
92 
93 	iter = i_cal_component_begin_component (icalcomp, comp_kind);
94 	subcomp = i_cal_comp_iter_deref (iter);
95 	while (subcomp) {
96 		ICalComponent *next_subcomp;
97 
98 		next_subcomp = i_cal_comp_iter_next (iter);
99 
100 		if (!func (icalcomp, subcomp, user_data)) {
101 			g_clear_object (&next_subcomp);
102 			g_object_unref (subcomp);
103 			break;
104 		}
105 
106 		g_object_unref (subcomp);
107 		subcomp = next_subcomp;
108 	}
109 
110 	g_clear_object (&iter);
111 }
112 
113 /* The 'func' returns TRUE to continue */
114 static void
foreach_property(ICalComponent * icalcomp,ICalPropertyKind prop_kind,gboolean (* func)(ICalComponent * icalcomp,ICalProperty * prop,gpointer user_data),gpointer user_data)115 foreach_property (ICalComponent *icalcomp,
116 		  ICalPropertyKind prop_kind,
117 		  gboolean (* func) (ICalComponent *icalcomp,
118 				     ICalProperty *prop,
119 				     gpointer user_data),
120 		  gpointer user_data)
121 {
122 	ICalProperty *prop;
123 
124 	g_return_if_fail (func != NULL);
125 
126 	for (prop = i_cal_component_get_first_property (icalcomp, prop_kind);
127 	     prop;
128 	     g_object_unref (prop), prop = i_cal_component_get_next_property (icalcomp, prop_kind)) {
129 		if (!func (icalcomp, prop, user_data))
130 			break;
131 	}
132 }
133 
134 static gboolean
gather_all_properties_cb(ICalComponent * icalcomp,ICalProperty * prop,gpointer user_data)135 gather_all_properties_cb (ICalComponent *icalcomp,
136 			  ICalProperty *prop,
137 			  gpointer user_data)
138 {
139 	GSList **pprops = user_data;
140 
141 	g_return_val_if_fail (pprops != NULL, FALSE);
142 
143 	*pprops = g_slist_prepend (*pprops, g_object_ref (prop));
144 
145 	return TRUE;
146 }
147 
148 static GSList * /* ICalProperty * */
gather_all_properties(ICalComponent * icalcomp,ICalPropertyKind prop_kind,gboolean in_original_order)149 gather_all_properties (ICalComponent *icalcomp,
150 		       ICalPropertyKind prop_kind,
151 		       gboolean in_original_order)
152 {
153 	GSList *props = NULL;
154 
155 	foreach_property (icalcomp, prop_kind, gather_all_properties_cb, &props);
156 
157 	return in_original_order ? g_slist_reverse (props) : props;
158 }
159 
160 static void
remove_all_properties_of_kind(ICalComponent * icalcomp,ICalPropertyKind prop_kind)161 remove_all_properties_of_kind (ICalComponent *icalcomp,
162 			       ICalPropertyKind prop_kind)
163 {
164 	ICalProperty *prop;
165 	GSList *to_remove, *link;
166 
167 	to_remove = gather_all_properties (icalcomp, prop_kind, FALSE);
168 
169 	for (link = to_remove; link; link = g_slist_next (link)) {
170 		prop = link->data;
171 
172 		i_cal_component_remove_property (icalcomp, prop);
173 	}
174 
175 	g_slist_free_full (to_remove, g_object_unref);
176 }
177 
178 /* returns NULL when value is NULL or empty string */
179 static ECalComponentText *
get_text_from_prop(ICalProperty * prop,const gchar * (* get_prop_func)(ICalProperty * prop))180 get_text_from_prop (ICalProperty *prop,
181 		    const gchar *(* get_prop_func) (ICalProperty *prop))
182 {
183 	ICalParameter *altrep_param;
184 	const gchar *value, *altrep;
185 
186 	g_return_val_if_fail (prop != NULL, NULL);
187 	g_return_val_if_fail (get_prop_func != NULL, NULL);
188 
189 	value = get_prop_func (prop);
190 
191 	/* Skip empty values */
192 	if (!value || !*value)
193 		return NULL;
194 
195 	altrep_param = i_cal_property_get_first_parameter (prop, I_CAL_ALTREP_PARAMETER);
196 	altrep = altrep_param ? i_cal_parameter_get_altrep (altrep_param) : NULL;
197 	g_clear_object (&altrep_param);
198 
199 	if (altrep && !*altrep)
200 		altrep = NULL;
201 
202 	return e_cal_component_text_new (value, altrep);
203 }
204 
205 static void
set_text_altrep_on_prop(ICalProperty * prop,const ECalComponentText * text)206 set_text_altrep_on_prop (ICalProperty *prop,
207 			 const ECalComponentText *text)
208 {
209 	ICalParameter *param;
210 	const gchar *altrep;
211 
212 	g_return_if_fail (prop != NULL);
213 	g_return_if_fail (text != NULL);
214 
215 	altrep = e_cal_component_text_get_altrep (text);
216 	param = i_cal_property_get_first_parameter (prop, I_CAL_ALTREP_PARAMETER);
217 
218 	if (altrep && *altrep) {
219 		if (param) {
220 			i_cal_parameter_set_altrep (param, (gchar *) altrep);
221 		} else {
222 			param = i_cal_parameter_new_altrep ((gchar *) altrep);
223 			i_cal_property_take_parameter (prop, param);
224 			param = NULL;
225 		}
226 	} else if (param) {
227 		i_cal_property_remove_parameter_by_kind (prop, I_CAL_ALTREP_PARAMETER);
228 	}
229 
230 	g_clear_object (&param);
231 }
232 
233 
234 static void
cal_component_finalize(GObject * object)235 cal_component_finalize (GObject *object)
236 {
237 	ECalComponent *comp = E_CAL_COMPONENT (object);
238 
239 	free_icalcomponent (comp, TRUE);
240 
241 	/* Chain up to parent's finalize() method. */
242 	G_OBJECT_CLASS (e_cal_component_parent_class)->finalize (object);
243 }
244 
245 /* Class initialization function for the calendar component object */
246 static void
e_cal_component_class_init(ECalComponentClass * class)247 e_cal_component_class_init (ECalComponentClass *class)
248 {
249 	GObjectClass *object_class;
250 
251 	object_class = G_OBJECT_CLASS (class);
252 	object_class->finalize = cal_component_finalize;
253 }
254 
255 /* Object initialization function for the calendar component object */
256 static void
e_cal_component_init(ECalComponent * comp)257 e_cal_component_init (ECalComponent *comp)
258 {
259 	comp->priv = e_cal_component_get_instance_private (comp);
260 	comp->priv->icalcomp = NULL;
261 }
262 
263 /**
264  * e_cal_component_new:
265  *
266  * Creates a new empty calendar component object.  Once created, you should set it from an
267  * existing #icalcomponent structure by using e_cal_component_set_icalcomponent() or with a
268  * new empty component type by using e_cal_component_set_new_vtype().
269  *
270  * Returns: (transfer full): A newly-created calendar component object.
271  *
272  * Since: 3.34
273  **/
274 ECalComponent *
e_cal_component_new(void)275 e_cal_component_new (void)
276 {
277 	return E_CAL_COMPONENT (g_object_new (E_TYPE_CAL_COMPONENT, NULL));
278 }
279 
280 /**
281  * e_cal_component_new_vtype:
282  * @vtype: an #ECalComponentVType
283  *
284  * Creates a new #ECalComponent of type @vtype.
285  *
286  * Returns: (transfer full): A newly-created calendar component object with set @vtype.
287  *
288  * Since: 3.34
289  **/
290 ECalComponent *
e_cal_component_new_vtype(ECalComponentVType vtype)291 e_cal_component_new_vtype (ECalComponentVType vtype)
292 {
293 	ECalComponent *comp;
294 
295 	comp = e_cal_component_new ();
296 	e_cal_component_set_new_vtype (comp, vtype);
297 
298 	return comp;
299 }
300 
301 /**
302  * e_cal_component_new_from_string:
303  * @calobj: A string representation of an iCalendar component.
304  *
305  * Creates a new calendar component object from the given iCalendar string.
306  *
307  * Returns: (transfer full): A calendar component representing the given iCalendar string on
308  * success, NULL if there was an error.
309  *
310  * Since: 3.34
311  **/
312 ECalComponent *
e_cal_component_new_from_string(const gchar * calobj)313 e_cal_component_new_from_string (const gchar *calobj)
314 {
315 	ICalComponent *icalcomp;
316 
317 	g_return_val_if_fail (calobj != NULL, NULL);
318 
319 	icalcomp = i_cal_parser_parse_string (calobj);
320 	if (!icalcomp)
321 		return NULL;
322 
323 	return e_cal_component_new_from_icalcomponent (icalcomp);
324 }
325 
326 /**
327  * e_cal_component_new_from_icalcomponent:
328  * @icalcomp: (transfer full): An #ICalComponent to use
329  *
330  * Creates a new #ECalComponent which will has set @icalcomp as
331  * an inner #ICalComponent. The newly created #ECalComponent takes
332  * ownership of the @icalcomp, and if the call
333  * to e_cal_component_set_icalcomponent() fails, then @icalcomp
334  * is freed.
335  *
336  * Returns: (transfer full): An #ECalComponent with @icalcomp assigned on success,
337  * NULL if the @icalcomp cannot be assigned to #ECalComponent.
338  *
339  * Since: 3.34
340  **/
341 ECalComponent *
e_cal_component_new_from_icalcomponent(ICalComponent * icalcomp)342 e_cal_component_new_from_icalcomponent (ICalComponent *icalcomp)
343 {
344 	ECalComponent *comp;
345 
346 	g_return_val_if_fail (icalcomp != NULL, NULL);
347 
348 	comp = e_cal_component_new ();
349 	if (!e_cal_component_set_icalcomponent (comp, icalcomp)) {
350 		g_object_unref (icalcomp);
351 		g_object_unref (comp);
352 
353 		return NULL;
354 	}
355 
356 	return comp;
357 }
358 
359 /**
360  * e_cal_component_clone:
361  * @comp: A calendar component object.
362  *
363  * Creates a new calendar component object by copying the information from
364  * another one.
365  *
366  * Returns: (transfer full): A newly-created calendar component with the same
367  * values as the original one.
368  *
369  * Since: 3.34
370  **/
371 ECalComponent *
e_cal_component_clone(ECalComponent * comp)372 e_cal_component_clone (ECalComponent *comp)
373 {
374 	ECalComponent *new_comp;
375 	ICalComponent *new_icalcomp;
376 
377 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
378 	g_return_val_if_fail (comp->priv->need_sequence_inc == FALSE, NULL);
379 
380 	new_comp = e_cal_component_new ();
381 
382 	if (comp->priv->icalcomp) {
383 		new_icalcomp = i_cal_component_clone (comp->priv->icalcomp);
384 		if (!new_icalcomp || !e_cal_component_set_icalcomponent (new_comp, new_icalcomp)) {
385 			g_clear_object (&new_icalcomp);
386 			g_clear_object (&new_comp);
387 		}
388 	}
389 
390 	return new_comp;
391 }
392 
393 /* Ensures that the mandatory calendar component properties (uid, dtstamp) do
394  * exist.  If they don't exist, it creates them automatically.
395  */
396 static void
ensure_mandatory_properties(ECalComponent * comp)397 ensure_mandatory_properties (ECalComponent *comp)
398 {
399 	ICalProperty *prop;
400 
401 	g_return_if_fail (comp->priv->icalcomp != NULL);
402 
403 	prop = i_cal_component_get_first_property (comp->priv->icalcomp, I_CAL_UID_PROPERTY);
404 	if (prop) {
405 		g_object_unref (prop);
406 	} else {
407 		gchar *uid;
408 
409 		uid = e_util_generate_uid ();
410 		i_cal_component_set_uid (comp->priv->icalcomp, uid);
411 		g_free (uid);
412 	}
413 
414 	prop = i_cal_component_get_first_property (comp->priv->icalcomp, I_CAL_DTSTAMP_PROPERTY);
415 	if (prop) {
416 		g_object_unref (prop);
417 	} else {
418 		ICalTime *tt;
419 
420 		tt = i_cal_time_new_current_with_zone (i_cal_timezone_get_utc_timezone ());
421 
422 		prop = i_cal_property_new_dtstamp (tt);
423 		i_cal_component_take_property (comp->priv->icalcomp, prop);
424 
425 		g_object_unref (tt);
426 	}
427 }
428 
429 static gboolean
ensure_alarm_uid_cb(ICalComponent * icalcomp,ICalComponent * subcomp,gpointer user_data)430 ensure_alarm_uid_cb (ICalComponent *icalcomp,
431 		     ICalComponent *subcomp,
432 		     gpointer user_data)
433 {
434 	if (!e_cal_util_component_has_x_property (subcomp, E_CAL_EVOLUTION_ALARM_UID_PROPERTY)) {
435 		gchar *uid;
436 
437 		uid = e_util_generate_uid ();
438 		e_cal_util_component_set_x_property (subcomp, E_CAL_EVOLUTION_ALARM_UID_PROPERTY, uid);
439 		g_free (uid);
440 	}
441 
442 	return TRUE;
443 }
444 
445 /**
446  * e_cal_component_set_new_vtype:
447  * @comp: A calendar component object.
448  * @type: Type of calendar component to create.
449  *
450  * Clears any existing component data from a calendar component object and
451  * creates a new #ICalComponent of the specified type for it.  The only property
452  * that will be set in the new component will be its unique identifier.
453  *
454  * Since: 3.34
455  **/
456 void
e_cal_component_set_new_vtype(ECalComponent * comp,ECalComponentVType type)457 e_cal_component_set_new_vtype (ECalComponent *comp,
458                                ECalComponentVType type)
459 {
460 	ICalComponent *icalcomp;
461 	ICalComponentKind kind;
462 
463 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
464 
465 	free_icalcomponent (comp, TRUE);
466 
467 	if (type == E_CAL_COMPONENT_NO_TYPE)
468 		return;
469 
470 	/* Figure out the kind and create the icalcomponent */
471 
472 	switch (type) {
473 	case E_CAL_COMPONENT_EVENT:
474 		kind = I_CAL_VEVENT_COMPONENT;
475 		break;
476 
477 	case E_CAL_COMPONENT_TODO:
478 		kind = I_CAL_VTODO_COMPONENT;
479 		break;
480 
481 	case E_CAL_COMPONENT_JOURNAL:
482 		kind = I_CAL_VJOURNAL_COMPONENT;
483 		break;
484 
485 	case E_CAL_COMPONENT_FREEBUSY:
486 		kind = I_CAL_VFREEBUSY_COMPONENT;
487 		break;
488 
489 	case E_CAL_COMPONENT_TIMEZONE:
490 		kind = I_CAL_VTIMEZONE_COMPONENT;
491 		break;
492 
493 	default:
494 		g_warn_if_reached ();
495 		kind = I_CAL_NO_COMPONENT;
496 	}
497 
498 	icalcomp = i_cal_component_new (kind);
499 	if (!icalcomp) {
500 		g_message ("e_cal_component_set_new_vtype(): Could not create the ICalComponent of kind %d!", kind);
501 		return;
502 	}
503 
504 	/* Scan the component to build our mapping table */
505 
506 	comp->priv->icalcomp = icalcomp;
507 
508 	/* Add missing stuff */
509 
510 	ensure_mandatory_properties (comp);
511 }
512 
513 /**
514  * e_cal_component_get_vtype:
515  * @comp: A calendar component object.
516  *
517  * Queries the type of a calendar component object.
518  *
519  * Returns: The type of the component, as defined by RFC 2445.
520  *
521  * Since: 3.34
522  **/
523 ECalComponentVType
e_cal_component_get_vtype(ECalComponent * comp)524 e_cal_component_get_vtype (ECalComponent *comp)
525 {
526 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), E_CAL_COMPONENT_NO_TYPE);
527 	g_return_val_if_fail (comp->priv->icalcomp != NULL, E_CAL_COMPONENT_NO_TYPE);
528 
529 	switch (i_cal_component_isa (comp->priv->icalcomp)) {
530 	case I_CAL_VEVENT_COMPONENT:
531 		return E_CAL_COMPONENT_EVENT;
532 
533 	case I_CAL_VTODO_COMPONENT:
534 		return E_CAL_COMPONENT_TODO;
535 
536 	case I_CAL_VJOURNAL_COMPONENT:
537 		return E_CAL_COMPONENT_JOURNAL;
538 
539 	case I_CAL_VFREEBUSY_COMPONENT:
540 		return E_CAL_COMPONENT_FREEBUSY;
541 
542 	case I_CAL_VTIMEZONE_COMPONENT:
543 		return E_CAL_COMPONENT_TIMEZONE;
544 
545 	default:
546 		/* We should have been loaded with a supported type! */
547 		g_warn_if_reached ();
548 		return E_CAL_COMPONENT_NO_TYPE;
549 	}
550 }
551 
552 /**
553  * e_cal_component_set_icalcomponent:
554  * @comp: A calendar component object.
555  * @icalcomp: (transfer full) (nullable): An #ICalComponent.
556  *
557  * Sets the contents of a calendar component object from an #ICalComponent.
558  * If the @comp already had an #ICalComponent set into it, it will
559  * be freed automatically.
560  *
561  * Supported component types are VEVENT, VTODO, VJOURNAL, VFREEBUSY, and VTIMEZONE.
562  *
563  * Returns: %TRUE on success, %FALSE if @icalcomp is an unsupported component type.
564  *
565  * Since: 3.34
566  **/
567 gboolean
e_cal_component_set_icalcomponent(ECalComponent * comp,ICalComponent * icalcomp)568 e_cal_component_set_icalcomponent (ECalComponent *comp,
569 				   ICalComponent *icalcomp)
570 {
571 	ICalComponentKind kind;
572 
573 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
574 
575 	if (comp->priv->icalcomp == icalcomp)
576 		return TRUE;
577 
578 	free_icalcomponent (comp, TRUE);
579 
580 	if (!icalcomp) {
581 		comp->priv->icalcomp = NULL;
582 		return TRUE;
583 	}
584 
585 	kind = i_cal_component_isa (icalcomp);
586 
587 	if (!(kind == I_CAL_VEVENT_COMPONENT
588 	      || kind == I_CAL_VTODO_COMPONENT
589 	      || kind == I_CAL_VJOURNAL_COMPONENT
590 	      || kind == I_CAL_VFREEBUSY_COMPONENT
591 	      || kind == I_CAL_VTIMEZONE_COMPONENT))
592 		return FALSE;
593 
594 	comp->priv->icalcomp = icalcomp;
595 
596 	ensure_mandatory_properties (comp);
597 
598 	foreach_subcomponent (icalcomp, I_CAL_VALARM_COMPONENT, ensure_alarm_uid_cb, NULL);
599 
600 	return TRUE;
601 }
602 
603 /**
604  * e_cal_component_get_icalcomponent:
605  * @comp: A calendar component object.
606  *
607  * Queries the #icalcomponent structure that a calendar component object is
608  * wrapping.
609  *
610  * Returns: (transfer none) (nullable): An #ICalComponent structure, or %NULL
611  *    if the @comp has no #ICalComponent set to it.
612  *
613  * Since: 3.34
614  **/
615 ICalComponent *
e_cal_component_get_icalcomponent(ECalComponent * comp)616 e_cal_component_get_icalcomponent (ECalComponent *comp)
617 {
618 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
619 
620 	return comp->priv->icalcomp;
621 }
622 
623 /**
624  * e_cal_component_strip_errors:
625  * @comp: A calendar component object.
626  *
627  * Strips all error messages from the calendar component. Those error messages are
628  * added to the iCalendar string representation whenever an invalid is used for
629  * one of its fields.
630  *
631  * Since: 3.34
632  **/
633 void
e_cal_component_strip_errors(ECalComponent * comp)634 e_cal_component_strip_errors (ECalComponent *comp)
635 {
636 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
637 
638 	if (comp->priv->icalcomp)
639 		i_cal_component_strip_errors (comp->priv->icalcomp);
640 }
641 
642 /**
643  * e_cal_component_get_as_string:
644  * @comp: A calendar component.
645  *
646  * Gets the iCalendar string representation of a calendar component.  You should
647  * call e_cal_component_commit_sequence() before this function to ensure that the
648  * component's sequence number is consistent with the state of the object.
649  *
650  * Returns: String representation of the calendar component according to
651  * RFC 2445.
652  *
653  * Since: 3.34
654  **/
655 gchar *
e_cal_component_get_as_string(ECalComponent * comp)656 e_cal_component_get_as_string (ECalComponent *comp)
657 {
658 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
659 	g_return_val_if_fail (comp->priv->icalcomp != NULL, NULL);
660 
661 	/* Ensure that the user has committed the new SEQUENCE */
662 	g_return_val_if_fail (comp->priv->need_sequence_inc == FALSE, NULL);
663 
664 	return i_cal_component_as_ical_string (comp->priv->icalcomp);
665 }
666 
667 /* Ensures that an alarm subcomponent has the mandatory properties it needs. */
668 static gboolean
ensure_alarm_properties_cb(ICalComponent * icalcomp,ICalComponent * subcomp,gpointer user_data)669 ensure_alarm_properties_cb (ICalComponent *icalcomp,
670 			    ICalComponent *subcomp,
671 			    gpointer user_data)
672 {
673 	ICalProperty *prop;
674 	ICalPropertyAction action;
675 	const gchar *summary;
676 
677 	prop = i_cal_component_get_first_property (subcomp, I_CAL_ACTION_PROPERTY);
678 	if (!prop)
679 		return TRUE;
680 
681 	action = i_cal_property_get_action (prop);
682 
683 	g_object_unref (prop);
684 
685 	switch (action) {
686 	case I_CAL_ACTION_DISPLAY:
687 		summary = i_cal_component_get_summary (icalcomp);
688 
689 		/* Ensure we have a DESCRIPTION property */
690 		prop = i_cal_component_get_first_property (subcomp, I_CAL_DESCRIPTION_PROPERTY);
691 		if (prop) {
692 			if (summary && *summary) {
693 				ICalProperty *xprop;
694 
695 				for (xprop = i_cal_component_get_first_property (subcomp, I_CAL_X_PROPERTY);
696 				     xprop;
697 				     g_object_unref (xprop), xprop = i_cal_component_get_next_property (subcomp, I_CAL_X_PROPERTY)) {
698 					const gchar *str;
699 
700 					str = i_cal_property_get_x_name (xprop);
701 					if (!g_strcmp0 (str, "X-EVOLUTION-NEEDS-DESCRIPTION")) {
702 						i_cal_property_set_description (prop, summary);
703 
704 						i_cal_component_remove_property (subcomp, xprop);
705 						g_object_unref (xprop);
706 						break;
707 					}
708 				}
709 			}
710 
711 			g_object_unref (prop);
712 			break;
713 		}
714 
715 		if (!summary || !*summary) {
716 			summary = _("Untitled appointment");
717 
718 			/* add the X-EVOLUTION-NEEDS-DESCRIPTION property */
719 			prop = i_cal_property_new_x ("1");
720 			i_cal_property_set_x_name (prop, "X-EVOLUTION-NEEDS-DESCRIPTION");
721 			i_cal_component_take_property (subcomp, prop);
722 		}
723 
724 		prop = i_cal_property_new_description (summary);
725 		i_cal_component_take_property (subcomp, prop);
726 
727 		break;
728 
729 	default:
730 		break;
731 	}
732 
733 	return TRUE;
734 }
735 
736 /**
737  * e_cal_component_commit_sequence:
738  * @comp: A calendar component object.
739  *
740  * Increments the sequence number property in a calendar component object if it
741  * needs it.  This needs to be done when any of a number of properties listed in
742  * RFC 2445 change values, such as the start and end dates of a component.
743  *
744  * This function must be called before calling e_cal_component_get_as_string() to
745  * ensure that the component is fully consistent.
746  *
747  * Since: 3.34
748  **/
749 void
e_cal_component_commit_sequence(ECalComponent * comp)750 e_cal_component_commit_sequence (ECalComponent *comp)
751 {
752 	ICalProperty *prop;
753 
754 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
755 	g_return_if_fail (comp->priv->icalcomp != NULL);
756 
757 	foreach_subcomponent (comp->priv->icalcomp, I_CAL_VALARM_COMPONENT, ensure_alarm_properties_cb, comp);
758 
759 	if (!comp->priv->need_sequence_inc)
760 		return;
761 
762 	prop = i_cal_component_get_first_property (comp->priv->icalcomp, I_CAL_SEQUENCE_PROPERTY);
763 
764 	if (prop) {
765 		gint seq;
766 
767 		seq = i_cal_property_get_sequence (prop);
768 		i_cal_property_set_sequence (prop, seq + 1);
769 		g_object_unref (prop);
770 	} else {
771 		/* The component had no SEQUENCE property, so assume that the
772 		 * default would have been zero.  Since it needed incrementing
773 		 * anyways, we use a value of 1 here.
774 		 */
775 		prop = i_cal_property_new_sequence (1);
776 		i_cal_component_take_property (comp->priv->icalcomp, prop);
777 	}
778 
779 	comp->priv->need_sequence_inc = FALSE;
780 }
781 
782 /**
783  * e_cal_component_abort_sequence:
784  * @comp: A calendar component object.
785  *
786  * Aborts the sequence change needed in the given calendar component,
787  * which means it will not require a sequence commit (via
788  * e_cal_component_commit_sequence()) even if the changes done require a
789  * sequence increment.
790  *
791  * Since: 3.34
792  **/
793 void
e_cal_component_abort_sequence(ECalComponent * comp)794 e_cal_component_abort_sequence (ECalComponent *comp)
795 {
796 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
797 
798 	comp->priv->need_sequence_inc = FALSE;
799 }
800 
801 /**
802  * e_cal_component_get_id:
803  * @comp: A calendar component object.
804  *
805  * Get the ID of the component as an #ECalComponentId. The return value should
806  * be freed with e_cal_component_id_free(), when no longer needed.
807  *
808  * Returns: (transfer full): the id of the component
809  *
810  * Since: 3.34
811  **/
812 ECalComponentId *
e_cal_component_get_id(ECalComponent * comp)813 e_cal_component_get_id (ECalComponent *comp)
814 {
815 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
816 	g_return_val_if_fail (comp->priv->icalcomp != NULL, NULL);
817 
818 	return e_cal_component_id_new_take (
819 		g_strdup (i_cal_component_get_uid (comp->priv->icalcomp)),
820 		e_cal_component_get_recurid_as_string (comp));
821 }
822 
823 /**
824  * e_cal_component_get_uid:
825  * @comp: A calendar component object.
826  *
827  * Queries the unique identifier of a calendar component object.
828  *
829  * Returns: (transfer none): the UID string
830  *
831  * Since: 3.34
832  **/
833 const gchar *
e_cal_component_get_uid(ECalComponent * comp)834 e_cal_component_get_uid (ECalComponent *comp)
835 {
836 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
837 	g_return_val_if_fail (comp->priv->icalcomp != NULL, NULL);
838 
839 	return i_cal_component_get_uid (comp->priv->icalcomp);
840 }
841 
842 /**
843  * e_cal_component_set_uid:
844  * @comp: A calendar component object.
845  * @uid: Unique identifier.
846  *
847  * Sets the unique identifier string of a calendar component object.
848  *
849  * Since: 3.34
850  **/
851 void
e_cal_component_set_uid(ECalComponent * comp,const gchar * uid)852 e_cal_component_set_uid (ECalComponent *comp,
853                          const gchar *uid)
854 {
855 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
856 	g_return_if_fail (uid != NULL);
857 	g_return_if_fail (comp->priv->icalcomp != NULL);
858 
859 	i_cal_component_set_uid (comp->priv->icalcomp, (gchar *) uid);
860 }
861 
862 static gboolean
get_attachments_cb(ICalComponent * icalcomp,ICalProperty * prop,gpointer user_data)863 get_attachments_cb (ICalComponent *icalcomp,
864 		    ICalProperty *prop,
865 		    gpointer user_data)
866 {
867 	GSList **pattaches = user_data;
868 	ICalAttach *attach;
869 
870 	g_return_val_if_fail (pattaches != NULL, FALSE);
871 
872 	attach = i_cal_property_get_attach (prop);
873 
874 	if (attach)
875 		*pattaches = g_slist_prepend (*pattaches, attach);
876 
877 	return TRUE;
878 }
879 
880 /**
881  * e_cal_component_get_attachments:
882  * @comp: A calendar component object
883  *
884  * Queries the attachment properties as #ICalAttach objects of the calendar
885  * component object. Changes on these objects are directly affecting the component.
886  * Free the returned #GSList with g_slist_free_full (slist, g_object_unref);,
887  * when no longer needed.
888  *
889  * Returns: (transfer full) (nullable) (element-type ICalAttach): a #GSList of
890  *    attachments, as #ICalAttach objects
891  *
892  * Since: 3.34
893  **/
894 GSList *
e_cal_component_get_attachments(ECalComponent * comp)895 e_cal_component_get_attachments (ECalComponent *comp)
896 {
897 	GSList *attaches = NULL;
898 
899 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
900 	g_return_val_if_fail (comp->priv->icalcomp != NULL, NULL);
901 
902 	foreach_property (comp->priv->icalcomp, I_CAL_ATTACH_PROPERTY, get_attachments_cb, &attaches);
903 
904 	return g_slist_reverse (attaches);
905 }
906 
907 /**
908  * e_cal_component_set_attachments:
909  * @comp: A calendar component object
910  * @attachments: (nullable) (element-type ICalAttach): a #GSList of an #ICalAttach,
911  *    or %NULL to remove any existing
912  *
913  * Sets the attachments of the calendar component object.
914  *
915  * Since: 3.34
916  **/
917 void
e_cal_component_set_attachments(ECalComponent * comp,const GSList * attachments)918 e_cal_component_set_attachments (ECalComponent *comp,
919 				 const GSList *attachments)
920 {
921 	GSList *link;
922 
923 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
924 	g_return_if_fail (comp->priv->icalcomp != NULL);
925 
926 	remove_all_properties_of_kind (comp->priv->icalcomp, I_CAL_ATTACH_PROPERTY);
927 
928 	for (link = (GSList *) attachments; link; link = g_slist_next (link)) {
929 		ICalAttach *attach = link->data;
930 		ICalProperty *prop;
931 
932 		if (!attach)
933 			continue;
934 
935 		prop = i_cal_property_new_attach (attach);
936 
937 		if (prop)
938 			i_cal_component_take_property (comp->priv->icalcomp, prop);
939 	}
940 }
941 
942 /**
943  * e_cal_component_has_attachments:
944  * @comp: A calendar component object.
945  *
946  * Queries the component to see if it has attachments.
947  *
948  * Returns: TRUE if there are attachments, FALSE otherwise.
949  *
950  * Since: 3.34
951  **/
952 gboolean
e_cal_component_has_attachments(ECalComponent * comp)953 e_cal_component_has_attachments (ECalComponent *comp)
954 {
955 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
956 	g_return_val_if_fail (comp->priv->icalcomp != NULL, FALSE);
957 
958 	return e_cal_util_component_has_property (comp->priv->icalcomp, I_CAL_ATTACH_PROPERTY);
959 }
960 
961 /* Creates a comma-delimited string of categories */
962 static gchar *
stringify_categories(const GSList * categ_list)963 stringify_categories (const GSList *categ_list)
964 {
965 	GString *str;
966 	GSList *link;
967 
968 	str = g_string_new (NULL);
969 
970 	for (link = (GSList *) categ_list; link; link = g_slist_next (link)) {
971 		const gchar *category = link->data;
972 
973 		if (category && *category) {
974 			if (str->len)
975 				g_string_append_c (str, ',');
976 			g_string_append (str, category);
977 		}
978 	}
979 
980 	return g_string_free (str, !str->len);
981 }
982 
983 /**
984  * e_cal_component_get_categories:
985  * @comp: A calendar component object.
986  *
987  * Queries the categories of the given calendar component. The categories
988  * are returned in the @categories argument, which, on success, will contain
989  * a comma-separated list of all categories set in the component.
990  * Free the returned string with g_free(), when no longer needed.
991  *
992  * Returns: (transfer full) (nullable): the categories as string, or %NULL
993  *    if none are set
994  *
995  * Since: 3.34
996  **/
997 gchar *
e_cal_component_get_categories(ECalComponent * comp)998 e_cal_component_get_categories (ECalComponent *comp)
999 {
1000 	GSList *categories;
1001 	gchar *str;
1002 
1003 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
1004 	g_return_val_if_fail (comp->priv->icalcomp != NULL, NULL);
1005 
1006 	categories = e_cal_component_get_categories_list (comp);
1007 	if (!categories)
1008 		return NULL;
1009 
1010 	str = stringify_categories (categories);
1011 
1012 	g_slist_free_full (categories, g_free);
1013 
1014 	return str;
1015 }
1016 
1017 /**
1018  * e_cal_component_set_categories:
1019  * @comp: A calendar component object.
1020  * @categories: Comma-separated list of categories.
1021  *
1022  * Sets the list of categories for a calendar component.
1023  *
1024  * Since: 3.34
1025  **/
1026 void
e_cal_component_set_categories(ECalComponent * comp,const gchar * categories)1027 e_cal_component_set_categories (ECalComponent *comp,
1028                                 const gchar *categories)
1029 {
1030 	ICalProperty *prop;
1031 
1032 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
1033 	g_return_if_fail (comp->priv->icalcomp != NULL);
1034 
1035 	remove_all_properties_of_kind (comp->priv->icalcomp, I_CAL_CATEGORIES_PROPERTY);
1036 
1037 	if (!categories || !*categories)
1038 		return;
1039 
1040 	prop = i_cal_property_new_categories (categories);
1041 	i_cal_component_take_property (comp->priv->icalcomp, prop);
1042 }
1043 
1044 /**
1045  * e_cal_component_get_categories_list:
1046  * @comp: A calendar component object.
1047  *
1048  * Queries the list of categories of a calendar component object. Each element
1049  * in the returned categ_list is a string with the corresponding category.
1050  * Free the returned #GSList with g_slist_free_full (categories, g_free); , when
1051  * no longer needed.
1052  *
1053  * Returns: (transfer full) (element-type utf8) (nullable): the #GSList of strings, where each
1054  *    string is a category, or %NULL, when no category is set.
1055  *
1056  * Since: 3.34
1057  **/
1058 GSList *
e_cal_component_get_categories_list(ECalComponent * comp)1059 e_cal_component_get_categories_list (ECalComponent *comp)
1060 {
1061 	ICalProperty *prop;
1062 	const gchar *categories;
1063 	const gchar *p;
1064 	const gchar *cat_start;
1065 	GSList *categ_list = NULL;
1066 	gchar *str;
1067 
1068 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
1069 	g_return_val_if_fail (comp->priv->icalcomp != NULL, NULL);
1070 
1071 	for (prop = i_cal_component_get_first_property (comp->priv->icalcomp, I_CAL_CATEGORIES_PROPERTY);
1072 	     prop;
1073 	     g_object_unref (prop), prop = i_cal_component_get_next_property (comp->priv->icalcomp, I_CAL_CATEGORIES_PROPERTY)) {
1074 		categories = i_cal_property_get_categories (prop);
1075 
1076 		if (!categories)
1077 			continue;
1078 
1079 		cat_start = categories;
1080 
1081 		for (p = categories; *p; p++) {
1082 			if (*p == ',') {
1083 				str = g_strndup (cat_start, p - cat_start);
1084 				categ_list = g_slist_prepend (categ_list, str);
1085 
1086 				cat_start = p + 1;
1087 			}
1088 		}
1089 
1090 		str = g_strndup (cat_start, p - cat_start);
1091 		categ_list = g_slist_prepend (categ_list, str);
1092 	}
1093 
1094 	return g_slist_reverse (categ_list);
1095 }
1096 
1097 /**
1098  * e_cal_component_set_categories_list:
1099  * @comp: A calendar component object.
1100  * @categ_list: (element-type utf8): List of strings, one for each category.
1101  *
1102  * Sets the list of categories of a calendar component object.
1103  *
1104  * Since: 3.34
1105  **/
1106 void
e_cal_component_set_categories_list(ECalComponent * comp,const GSList * categ_list)1107 e_cal_component_set_categories_list (ECalComponent *comp,
1108                                      const GSList *categ_list)
1109 {
1110 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
1111 	g_return_if_fail (comp->priv->icalcomp != NULL);
1112 
1113 	if (!categ_list) {
1114 		e_cal_component_set_categories (comp, NULL);
1115 	} else {
1116 		gchar *categories_str;
1117 
1118 		/* Create a single string of categories */
1119 		categories_str = stringify_categories (categ_list);
1120 
1121 		/* Set the categories */
1122 		e_cal_component_set_categories (comp, categories_str);
1123 		g_free (categories_str);
1124 	}
1125 }
1126 
1127 /**
1128  * e_cal_component_get_classification:
1129  * @comp: A calendar component object.
1130  *
1131  * Queries the classification of a calendar component object.  If the
1132  * classification property is not set on this component, this function returns
1133  * #E_CAL_COMPONENT_CLASS_NONE.
1134  *
1135  * Retuurns: a classification of the @comp, as an #ECalComponentClassification
1136  *
1137  * Since: 3.34
1138  **/
1139 ECalComponentClassification
e_cal_component_get_classification(ECalComponent * comp)1140 e_cal_component_get_classification (ECalComponent *comp)
1141 {
1142 	ICalProperty *prop;
1143 	ECalComponentClassification classif;
1144 
1145 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), E_CAL_COMPONENT_CLASS_UNKNOWN);
1146 	g_return_val_if_fail (comp->priv->icalcomp != NULL, E_CAL_COMPONENT_CLASS_UNKNOWN);
1147 
1148 	prop = i_cal_component_get_first_property (comp->priv->icalcomp, I_CAL_CLASS_PROPERTY);
1149 
1150 	if (!prop)
1151 		return E_CAL_COMPONENT_CLASS_NONE;
1152 
1153 	switch (i_cal_property_get_class (prop)) {
1154 	case I_CAL_CLASS_PUBLIC:
1155 		classif = E_CAL_COMPONENT_CLASS_PUBLIC;
1156 		break;
1157 	case I_CAL_CLASS_PRIVATE:
1158 		classif = E_CAL_COMPONENT_CLASS_PRIVATE;
1159 		break;
1160 	case I_CAL_CLASS_CONFIDENTIAL:
1161 		classif = E_CAL_COMPONENT_CLASS_CONFIDENTIAL;
1162 		break;
1163 	default:
1164 		classif = E_CAL_COMPONENT_CLASS_UNKNOWN;
1165 		break;
1166 	}
1167 
1168 	g_object_unref (prop);
1169 
1170 	return classif;
1171 }
1172 
1173 /**
1174  * e_cal_component_set_classification:
1175  * @comp: A calendar component object.
1176  * @classif: Classification to use.
1177  *
1178  * Sets the classification property of a calendar component object.  To unset
1179  * the property, specify E_CAL_COMPONENT_CLASS_NONE for @classif.
1180  *
1181  * Since: 3.34
1182  **/
1183 void
e_cal_component_set_classification(ECalComponent * comp,ECalComponentClassification classif)1184 e_cal_component_set_classification (ECalComponent *comp,
1185                                     ECalComponentClassification classif)
1186 {
1187 	ICalProperty_Class prop_class;
1188 	ICalProperty *prop;
1189 
1190 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
1191 	g_return_if_fail (classif != E_CAL_COMPONENT_CLASS_UNKNOWN);
1192 	g_return_if_fail (comp->priv->icalcomp != NULL);
1193 
1194 	prop = i_cal_component_get_first_property (comp->priv->icalcomp, I_CAL_CLASS_PROPERTY);
1195 
1196 	if (classif == E_CAL_COMPONENT_CLASS_NONE) {
1197 		if (prop) {
1198 			i_cal_component_remove_property (comp->priv->icalcomp, prop);
1199 			g_object_unref (prop);
1200 		}
1201 
1202 		return;
1203 	}
1204 
1205 	switch (classif) {
1206 	case E_CAL_COMPONENT_CLASS_PUBLIC:
1207 		prop_class = I_CAL_CLASS_PUBLIC;
1208 		break;
1209 
1210 	case E_CAL_COMPONENT_CLASS_PRIVATE:
1211 		prop_class = I_CAL_CLASS_PRIVATE;
1212 		break;
1213 
1214 	case E_CAL_COMPONENT_CLASS_CONFIDENTIAL:
1215 		prop_class = I_CAL_CLASS_CONFIDENTIAL;
1216 		break;
1217 
1218 	default:
1219 		g_warn_if_reached ();
1220 		prop_class = I_CAL_CLASS_NONE;
1221 		break;
1222 	}
1223 
1224 	if (prop) {
1225 		i_cal_property_set_class (prop, prop_class);
1226 		g_object_unref (prop);
1227 	} else {
1228 		prop = i_cal_property_new_class (prop_class);
1229 		i_cal_component_take_property (comp->priv->icalcomp, prop);
1230 	}
1231 }
1232 
1233 /* Gets a text list value */
1234 static GSList *
get_text_list(ICalComponent * icalcomp,ICalPropertyKind prop_kind,const gchar * (* get_prop_func)(ICalProperty * prop))1235 get_text_list (ICalComponent *icalcomp,
1236 	       ICalPropertyKind prop_kind,
1237                const gchar *(* get_prop_func) (ICalProperty *prop))
1238 {
1239 	GSList *link, *props, *tl = NULL;
1240 
1241 	if (!icalcomp)
1242 		return NULL;
1243 
1244 	props = gather_all_properties (icalcomp, prop_kind, FALSE);
1245 	for (link = props; link; link = g_slist_next (link)) {
1246 		ICalProperty *prop = link->data;
1247 		ECalComponentText *text;
1248 
1249 		if (!prop)
1250 			continue;
1251 
1252 		text = get_text_from_prop (prop, get_prop_func);
1253 		if (!text)
1254 			continue;
1255 
1256 		tl = g_slist_prepend (tl, text);
1257 	}
1258 
1259 	g_slist_free_full (props, g_object_unref);
1260 
1261 	/* No need to reverse it, the props are in reverse order
1262 	   and processed in the reverse order, thus the result
1263 	   is in the expected order. */
1264 	return tl;
1265 }
1266 
1267 /* Sets a text list value */
1268 static void
set_text_list(ICalComponent * icalcomp,ICalPropertyKind prop_kind,ICalProperty * (* new_prop_func)(const gchar * value),const GSList * tl)1269 set_text_list (ICalComponent *icalcomp,
1270 	       ICalPropertyKind prop_kind,
1271                ICalProperty * (* new_prop_func) (const gchar *value),
1272                const GSList *tl)
1273 {
1274 	GSList *link;
1275 
1276 	/* Remove old texts */
1277 	remove_all_properties_of_kind (icalcomp, prop_kind);
1278 
1279 	/* Add in new texts */
1280 
1281 	for (link = (GSList *) tl; link; link = g_slist_next (link)) {
1282 		ECalComponentText *text;
1283 		ICalProperty *prop;
1284 
1285 		text = link->data;
1286 		if (!text || !e_cal_component_text_get_value (text))
1287 			continue;
1288 
1289 		prop = new_prop_func ((gchar *) e_cal_component_text_get_value (text));
1290 
1291 		set_text_altrep_on_prop (prop, text);
1292 
1293 		i_cal_component_take_property (icalcomp, prop);
1294 	}
1295 }
1296 
1297 /**
1298  * e_cal_component_get_comments:
1299  * @comp: A calendar component object.
1300  *
1301  * Queries the comments of a calendar component object.  The comment property can
1302  * appear several times inside a calendar component, and so a list of
1303  * #ECalComponentText is returned. Free the returned #GSList with
1304  * g_slist_free_full (slist, e_cal_component_text_free);, when no longer needed.
1305  *
1306  * Returns: (transfer full) (element-type ECalComponentText) (nullable): the comment properties
1307  *    and their parameters, as a list of #ECalComponentText structures; or %NULL, when
1308  *    the component doesn't contain any.
1309  *
1310  * Since: 3.34
1311  **/
1312 GSList *
e_cal_component_get_comments(ECalComponent * comp)1313 e_cal_component_get_comments (ECalComponent *comp)
1314 {
1315 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
1316 	g_return_val_if_fail (comp->priv->icalcomp != NULL, NULL);
1317 
1318 	return get_text_list (comp->priv->icalcomp, I_CAL_COMMENT_PROPERTY, i_cal_property_get_comment);
1319 }
1320 
1321 /**
1322  * e_cal_component_set_comments:
1323  * @comp: A calendar component object.
1324  * @text_list: (element-type ECalComponentText): List of #ECalComponentText structures.
1325  *
1326  * Sets the comments of a calendar component object.  The comment property can
1327  * appear several times inside a calendar component, and so a list of
1328  * #ECalComponentText structures is used.
1329  *
1330  * Since: 3.34
1331  **/
1332 void
e_cal_component_set_comments(ECalComponent * comp,const GSList * text_list)1333 e_cal_component_set_comments (ECalComponent *comp,
1334 			      const GSList *text_list)
1335 {
1336 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
1337 	g_return_if_fail (comp->priv->icalcomp != NULL);
1338 
1339 	set_text_list (comp->priv->icalcomp, I_CAL_COMMENT_PROPERTY, i_cal_property_new_comment, text_list);
1340 }
1341 
1342 /**
1343  * e_cal_component_get_contacts:
1344  * @comp: A calendar component object.
1345  *
1346  * Queries the contact of a calendar component object.  The contact property can
1347  * appear several times inside a calendar component, and so a list of
1348  * #ECalComponentText is returned. Free the returned #GSList with
1349  * g_slist_free_full (slist, e_cal_component_text_free);, when no longer needed.
1350  *
1351  * Returns: (transfer full) (element-type ECalComponentText): the contact properties and
1352  *    their parameters, as a #GSList of #ECalComponentText structures.
1353  *
1354  * Since: 3.34
1355  **/
1356 GSList *
e_cal_component_get_contacts(ECalComponent * comp)1357 e_cal_component_get_contacts (ECalComponent *comp)
1358 {
1359 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
1360 	g_return_val_if_fail (comp->priv->icalcomp != NULL, NULL);
1361 
1362 	return get_text_list (comp->priv->icalcomp, I_CAL_CONTACT_PROPERTY, i_cal_property_get_contact);
1363 }
1364 
1365 /**
1366  * e_cal_component_set_contacts:
1367  * @comp: A calendar component object.
1368  * @text_list: (element-type ECalComponentText): List of #ECalComponentText structures.
1369  *
1370  * Sets the contact of a calendar component object.  The contact property can
1371  * appear several times inside a calendar component, and so a list of
1372  * #ECalComponentText structures is used.
1373  *
1374  * Since: 3.34
1375  **/
1376 void
e_cal_component_set_contacts(ECalComponent * comp,const GSList * text_list)1377 e_cal_component_set_contacts (ECalComponent *comp,
1378 			      const GSList *text_list)
1379 {
1380 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
1381 	g_return_if_fail (comp->priv->icalcomp != NULL);
1382 
1383 	set_text_list (comp->priv->icalcomp, I_CAL_CONTACT_PROPERTY, i_cal_property_new_contact, text_list);
1384 }
1385 
1386 /* Gets a struct icaltimetype value */
1387 static ICalTime *
get_icaltimetype(ICalComponent * icalcomp,ICalPropertyKind prop_kind,ICalTime * (* get_prop_func)(ICalProperty * prop))1388 get_icaltimetype (ICalComponent *icalcomp,
1389 		  ICalPropertyKind prop_kind,
1390                   ICalTime * (* get_prop_func) (ICalProperty *prop))
1391 {
1392 	ICalProperty *prop;
1393 	ICalTime *tt;
1394 
1395 	prop = i_cal_component_get_first_property (icalcomp, prop_kind);
1396 	if (!prop)
1397 		return NULL;
1398 
1399 	tt = get_prop_func (prop);
1400 
1401 	g_object_unref (prop);
1402 
1403 	return tt;
1404 }
1405 
1406 /* Sets a struct icaltimetype value */
1407 static void
set_icaltimetype(ICalComponent * icalcomp,ICalPropertyKind prop_kind,ICalProperty * (* prop_new_func)(ICalTime * tt),void (* prop_set_func)(ICalProperty * prop,ICalTime * tt),const ICalTime * tt)1408 set_icaltimetype (ICalComponent *icalcomp,
1409                   ICalPropertyKind prop_kind,
1410                   ICalProperty *(* prop_new_func) (ICalTime *tt),
1411                   void (* prop_set_func) (ICalProperty *prop,
1412                                           ICalTime *tt),
1413                   const ICalTime *tt)
1414 {
1415 	ICalProperty *prop;
1416 
1417 	prop = i_cal_component_get_first_property (icalcomp, prop_kind);
1418 
1419 	if (!tt) {
1420 		if (prop) {
1421 			i_cal_component_remove_property (icalcomp, prop);
1422 			g_clear_object (&prop);
1423 		}
1424 
1425 		return;
1426 	}
1427 
1428 	if (prop) {
1429 		prop_set_func (prop, (ICalTime *) tt);
1430 		g_object_unref (prop);
1431 	} else {
1432 		prop = prop_new_func ((ICalTime *) tt);
1433 		i_cal_component_take_property (icalcomp, prop);
1434 	}
1435 }
1436 
1437 /**
1438  * e_cal_component_get_completed:
1439  * @comp: A calendar component object.
1440  *
1441  * Queries the date at which a calendar compoment object was completed.
1442  * Free the returned non-NULL pointer with g_object_unref(), when
1443  * no longer needed.
1444  *
1445  * Returns: (transfer full): the completion date, as an #ICalTime, or %NULL, when none is set
1446  *
1447  * Since: 3.34
1448  **/
1449 ICalTime *
e_cal_component_get_completed(ECalComponent * comp)1450 e_cal_component_get_completed (ECalComponent *comp)
1451 {
1452 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
1453 	g_return_val_if_fail (comp->priv->icalcomp != NULL, NULL);
1454 
1455 	return get_icaltimetype (comp->priv->icalcomp, I_CAL_COMPLETED_PROPERTY, i_cal_property_get_completed);
1456 }
1457 
1458 /**
1459  * e_cal_component_set_completed:
1460  * @comp: A calendar component object.
1461  * @tt: (nullable): Value for the completion date.
1462  *
1463  * Sets the date at which a calendar component object was completed.
1464  *
1465  * Since: 3.34
1466  **/
1467 void
e_cal_component_set_completed(ECalComponent * comp,const ICalTime * tt)1468 e_cal_component_set_completed (ECalComponent *comp,
1469 			       const ICalTime *tt)
1470 {
1471 	ICalTime *tmp_tt = NULL;
1472 
1473 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
1474 	g_return_if_fail (comp->priv->icalcomp != NULL);
1475 
1476 	if (tt && i_cal_time_is_date ((ICalTime *) tt)) {
1477 		tmp_tt = i_cal_time_clone (tt);
1478 		tt = tmp_tt;
1479 
1480 		i_cal_time_set_is_date (tmp_tt, FALSE);
1481 		i_cal_time_set_hour (tmp_tt, 0);
1482 		i_cal_time_set_minute (tmp_tt, 0);
1483 		i_cal_time_set_second (tmp_tt, 0);
1484 		i_cal_time_set_timezone (tmp_tt, i_cal_timezone_get_utc_timezone ());
1485 	}
1486 
1487 	set_icaltimetype (comp->priv->icalcomp, I_CAL_COMPLETED_PROPERTY,
1488 		i_cal_property_new_completed,
1489 		i_cal_property_set_completed,
1490 		tt);
1491 
1492 	g_clear_object (&tmp_tt);
1493 }
1494 
1495 /**
1496  * e_cal_component_get_created:
1497  * @comp: A calendar component object.
1498  *
1499  * Queries the date in which a calendar component object was created in the
1500  * calendar store. Free the returned non-NULL pointer with g_object_unref(), when
1501  * no longer needed.
1502  *
1503  * Returns: (transfer full): the creation date, as an #ICalTime, or %NULL, when none is set
1504  *
1505  * Since: 3.34
1506  **/
1507 ICalTime *
e_cal_component_get_created(ECalComponent * comp)1508 e_cal_component_get_created (ECalComponent *comp)
1509 {
1510 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
1511 	g_return_val_if_fail (comp->priv->icalcomp != NULL, NULL);
1512 
1513 	return get_icaltimetype (comp->priv->icalcomp, I_CAL_CREATED_PROPERTY, i_cal_property_get_created);
1514 }
1515 
1516 /**
1517  * e_cal_component_set_created:
1518  * @comp: A calendar component object.
1519  * @tt: (nullable): Value for the creation date.
1520  *
1521  * Sets the date in which a calendar component object is created in the calendar
1522  * store.  This should only be used inside a calendar store application, i.e.
1523  * not by calendar user agents.
1524  *
1525  * Since: 3.34
1526  **/
1527 void
e_cal_component_set_created(ECalComponent * comp,const ICalTime * tt)1528 e_cal_component_set_created (ECalComponent *comp,
1529 			     const ICalTime *tt)
1530 {
1531 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
1532 	g_return_if_fail (comp->priv->icalcomp != NULL);
1533 
1534 	set_icaltimetype (comp->priv->icalcomp, I_CAL_CREATED_PROPERTY,
1535 		i_cal_property_new_created,
1536 		i_cal_property_set_created,
1537 		tt);
1538 }
1539 
1540 /**
1541  * e_cal_component_get_descriptions:
1542  * @comp: A calendar component object.
1543  *
1544  * Queries the description of a calendar component object.  Journal components
1545  * may have more than one description, and as such this function returns a list
1546  * of #ECalComponentText structures.  All other types of components can have at
1547  * most one description. Free the returned #GSList with
1548  * g_slist_free_full (slist, e_cal_component_text_free);, when no longer needed.
1549  *
1550  * Returns: (transfer full) (element-type ECalComponentText) (nullable): the description
1551  *    properties and their parameters, as a #GSList of #ECalComponentText structures.
1552  *
1553  * Since: 3.34
1554  **/
1555 GSList *
e_cal_component_get_descriptions(ECalComponent * comp)1556 e_cal_component_get_descriptions (ECalComponent *comp)
1557 {
1558 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
1559 	g_return_val_if_fail (comp->priv->icalcomp != NULL, NULL);
1560 
1561 	return get_text_list (comp->priv->icalcomp, I_CAL_DESCRIPTION_PROPERTY, i_cal_property_get_description);
1562 }
1563 
1564 /**
1565  * e_cal_component_set_descriptions:
1566  * @comp: A calendar component object.
1567  * @text_list: (element-type ECalComponentText): List of #ECalComponentText structures.
1568  *
1569  * Sets the description of a calendar component object.  Journal components may
1570  * have more than one description, and as such this function takes in a list of
1571  * #ECalComponentText structures.  All other types of components can have
1572  * at most one description.
1573  *
1574  * Since: 3.34
1575  **/
1576 void
e_cal_component_set_descriptions(ECalComponent * comp,const GSList * text_list)1577 e_cal_component_set_descriptions (ECalComponent *comp,
1578 				  const GSList *text_list)
1579 {
1580 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
1581 	g_return_if_fail (comp->priv->icalcomp != NULL);
1582 
1583 	set_text_list (comp->priv->icalcomp, I_CAL_DESCRIPTION_PROPERTY, i_cal_property_new_description, text_list);
1584 }
1585 
1586 /* Gets a date/time and timezone pair */
1587 static ECalComponentDateTime *
get_datetime(ICalComponent * icalcomp,ICalPropertyKind prop_kind,ICalTime * (* get_prop_func)(ICalProperty * prop),ICalProperty ** out_prop)1588 get_datetime (ICalComponent *icalcomp,
1589 	      ICalPropertyKind prop_kind,
1590               ICalTime * (* get_prop_func) (ICalProperty *prop),
1591 	      ICalProperty **out_prop)
1592 {
1593 	ICalProperty *prop;
1594 	ICalParameter *param;
1595 	ICalTime *value = NULL;
1596 	gchar *tzid;
1597 
1598 	if (out_prop)
1599 		*out_prop = NULL;
1600 
1601 	prop = i_cal_component_get_first_property (icalcomp, prop_kind);
1602 	if (prop)
1603 		value = get_prop_func (prop);
1604 
1605 	if (!value) {
1606 		g_clear_object (&prop);
1607 		return NULL;
1608 	}
1609 
1610 	param = i_cal_property_get_first_parameter (prop, I_CAL_TZID_PARAMETER);
1611 	/* If the ICalTime has is_utc set, we set "UTC" as the TZID.
1612 	 * This makes the timezone code simpler. */
1613 	if (param)
1614 		tzid = g_strdup (i_cal_parameter_get_tzid (param));
1615 	else if (i_cal_time_is_utc (value))
1616 		tzid = g_strdup ("UTC");
1617 	else
1618 		tzid = NULL;
1619 
1620 	g_clear_object (&param);
1621 
1622 	if (out_prop)
1623 		*out_prop = prop;
1624 	else
1625 		g_clear_object (&prop);
1626 
1627 	return e_cal_component_datetime_new_take (value, tzid);
1628 }
1629 
1630 /* Sets a date/time and timezone pair */
1631 static void
set_datetime(ICalComponent * icalcomp,ICalPropertyKind prop_kind,ICalProperty * (* prop_new_func)(ICalTime * tt),void (* prop_set_func)(ICalProperty * prop,ICalTime * tt),const ECalComponentDateTime * dt,ICalProperty ** out_prop)1632 set_datetime (ICalComponent *icalcomp,
1633 	      ICalPropertyKind prop_kind,
1634 	      ICalProperty *(* prop_new_func) (ICalTime *tt),
1635               void (* prop_set_func) (ICalProperty *prop,
1636 				      ICalTime *tt),
1637 	      const ECalComponentDateTime *dt,
1638 	      ICalProperty **out_prop)
1639 {
1640 	ICalProperty *prop;
1641 	ICalParameter *param;
1642 	ICalTime *tt;
1643 	const gchar *tzid;
1644 
1645 	if (out_prop)
1646 		*out_prop = NULL;
1647 
1648 	prop = i_cal_component_get_first_property (icalcomp, prop_kind);
1649 
1650 	/* If we are setting the property to NULL (i.e. removing it), then
1651 	 * we remove it if it exists. */
1652 	if (!dt) {
1653 		if (prop) {
1654 			i_cal_component_remove_property (icalcomp, prop);
1655 			g_clear_object (&prop);
1656 		}
1657 
1658 		return;
1659 	}
1660 
1661 	tt = e_cal_component_datetime_get_value (dt);
1662 
1663 	g_return_if_fail (tt != NULL);
1664 
1665 	tzid = e_cal_component_datetime_get_tzid (dt);
1666 
1667 	/* If the TZID is set to "UTC", we set the is_utc flag. */
1668 	if (!g_strcmp0 (tzid, "UTC"))
1669 		i_cal_time_set_timezone (tt, i_cal_timezone_get_utc_timezone ());
1670 	else if (i_cal_time_is_utc (tt))
1671 		i_cal_time_set_timezone (tt, NULL);
1672 
1673 	if (prop) {
1674 		/* make sure no VALUE property is left if not needed */
1675 		i_cal_property_remove_parameter_by_kind (prop, I_CAL_VALUE_PARAMETER);
1676 
1677 		prop_set_func (prop, tt);
1678 	} else {
1679 		prop = prop_new_func (tt);
1680 		i_cal_component_add_property (icalcomp, prop);
1681 	}
1682 
1683 	param = i_cal_property_get_first_parameter (prop, I_CAL_TZID_PARAMETER);
1684 
1685 	/* If the TZID is set to "UTC", we don't want to save the TZID. */
1686 	if (tzid && g_strcmp0 (tzid, "UTC") != 0) {
1687 		if (param) {
1688 			i_cal_parameter_set_tzid (param, (gchar *) tzid);
1689 		} else {
1690 			param = i_cal_parameter_new_tzid ((gchar *) tzid);
1691 			i_cal_property_add_parameter (prop, param);
1692 		}
1693 	} else if (param) {
1694 		i_cal_property_remove_parameter_by_kind (prop, I_CAL_TZID_PARAMETER);
1695 	}
1696 
1697 	g_clear_object (&param);
1698 
1699 	if (out_prop)
1700 		*out_prop = prop;
1701 	else
1702 		g_clear_object (&prop);
1703 }
1704 
1705 /* This tries to get the DTSTART + DURATION for a VEVENT or VTODO. In a
1706  * VEVENT this is used for the DTEND if no DTEND exists, In a VTOTO it is
1707  * used for the DUE date if DUE doesn't exist. */
1708 static ECalComponentDateTime *
e_cal_component_get_start_plus_duration(ECalComponent * comp)1709 e_cal_component_get_start_plus_duration (ECalComponent *comp)
1710 {
1711 	ICalDuration *duration;
1712 	ICalTime *tt;
1713 	ECalComponentDateTime *dt;
1714 	guint dur_days, dur_hours, dur_minutes, dur_seconds;
1715 
1716 	duration = i_cal_component_get_duration (comp->priv->icalcomp);
1717 	if (!duration || i_cal_duration_is_null_duration (duration) || i_cal_duration_is_bad_duration (duration)) {
1718 		g_clear_object (&duration);
1719 		return NULL;
1720 	}
1721 
1722 	/* Get the DTSTART time. */
1723 	dt = get_datetime (comp->priv->icalcomp, I_CAL_DTSTART_PROPERTY, i_cal_property_get_dtstart, NULL);
1724 	if (!dt) {
1725 		g_object_unref (duration);
1726 		return NULL;
1727 	}
1728 
1729 	/* The DURATION shouldn't be negative, but just return DTSTART if it
1730 	 * is, i.e. assume it is 0. */
1731 	if (i_cal_duration_is_neg (duration)) {
1732 		g_object_unref (duration);
1733 		return dt;
1734 	}
1735 
1736 	/* If DTSTART is a DATE value, then we need to check if the DURATION
1737 	 * includes any hours, minutes or seconds. If it does, we need to
1738 	 * make the DTEND/DUE a DATE-TIME value. */
1739 	dur_days = i_cal_duration_get_days (duration) + (7 * i_cal_duration_get_weeks (duration));
1740 	dur_hours = i_cal_duration_get_hours (duration);
1741 	dur_minutes = i_cal_duration_get_minutes (duration);
1742 	dur_seconds = i_cal_duration_get_seconds (duration);
1743 
1744 	tt = e_cal_component_datetime_get_value (dt);
1745 	if (i_cal_time_is_date (tt) && (
1746 	    dur_hours != 0 || dur_minutes != 0 || dur_seconds != 0)) {
1747 		i_cal_time_set_is_date (tt, FALSE);
1748 	}
1749 
1750 	/* Add on the DURATION. */
1751 	i_cal_time_adjust (tt, dur_days, dur_hours, dur_minutes, dur_seconds);
1752 
1753 	g_object_unref (duration);
1754 
1755 	return dt;
1756 }
1757 
1758 /**
1759  * e_cal_component_get_dtend:
1760  * @comp: A calendar component object.
1761  *
1762  * Queries the date/time end of a calendar component object. In case there's no DTEND,
1763  * but only DTSTART and DURATION, then the end is computed from the later two.
1764  * Free the returned #ECalComponentDateTime with e_cal_component_datetime_free(),
1765  * when no longer needed.
1766  *
1767  * Returns: (transfer full) (nullable): the date/time end, as an #ECalComponentDateTime
1768  *
1769  * Since: 3.34
1770  **/
1771 ECalComponentDateTime *
e_cal_component_get_dtend(ECalComponent * comp)1772 e_cal_component_get_dtend (ECalComponent *comp)
1773 {
1774 	ECalComponentDateTime *dt;
1775 
1776 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
1777 	g_return_val_if_fail (comp->priv->icalcomp != NULL, NULL);
1778 
1779 	dt = get_datetime (comp->priv->icalcomp, I_CAL_DTEND_PROPERTY, i_cal_property_get_dtend, NULL);
1780 
1781 	/* If we don't have a DTEND property, then we try to get DTSTART
1782 	 * + DURATION. */
1783 	if (!dt)
1784 		dt = e_cal_component_get_start_plus_duration (comp);
1785 
1786 	return dt;
1787 }
1788 
1789 /**
1790  * e_cal_component_set_dtend:
1791  * @comp: A calendar component object.
1792  * @dt: (nullable): End date/time, or %NULL, to remove the property.
1793  *
1794  * Sets the date/time end property of a calendar component object.
1795  *
1796  * Since: 3.34
1797  **/
1798 void
e_cal_component_set_dtend(ECalComponent * comp,const ECalComponentDateTime * dt)1799 e_cal_component_set_dtend (ECalComponent *comp,
1800                            const ECalComponentDateTime *dt)
1801 {
1802 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
1803 	g_return_if_fail (comp->priv->icalcomp != NULL);
1804 
1805 	set_datetime (comp->priv->icalcomp, I_CAL_DTEND_PROPERTY,
1806 		i_cal_property_new_dtend,
1807 		i_cal_property_set_dtend,
1808 		dt,
1809 		NULL);
1810 
1811 	/* Make sure we remove any existing DURATION property, as it can't be
1812 	 * used with a DTEND. If DTEND is set to NULL, i.e. removed, we also
1813 	 * want to remove any DURATION. */
1814 	remove_all_properties_of_kind (comp->priv->icalcomp, I_CAL_DURATION_PROPERTY);
1815 
1816 	comp->priv->need_sequence_inc = TRUE;
1817 }
1818 
1819 /**
1820  * e_cal_component_get_dtstamp:
1821  * @comp: A calendar component object.
1822  *
1823  * Queries the date/timestamp property of a calendar component object, which is
1824  * the last time at which the object was modified by a calendar user agent.
1825  *
1826  * Free a non-NULL returned object with g_object_unref(),
1827  * when no longer needed.
1828  *
1829  * Returns: (transfer full) (nullable): A value for the date/timestamp, or %NULL, when none found.
1830  *
1831  * Since: 3.34
1832  **/
1833 ICalTime *
e_cal_component_get_dtstamp(ECalComponent * comp)1834 e_cal_component_get_dtstamp (ECalComponent *comp)
1835 {
1836 	ICalProperty *prop;
1837 	ICalTime *tt;
1838 
1839 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
1840 	g_return_val_if_fail (comp->priv->icalcomp != NULL, NULL);
1841 
1842 	prop = i_cal_component_get_first_property (comp->priv->icalcomp, I_CAL_DTSTAMP_PROPERTY);
1843 
1844 	/* This MUST exist, since we ensured that it did */
1845 	g_return_val_if_fail (prop != NULL, NULL);
1846 
1847 	tt = i_cal_property_get_dtstamp (prop);
1848 
1849 	g_clear_object (&prop);
1850 
1851 	return tt;
1852 }
1853 
1854 /**
1855  * e_cal_component_set_dtstamp:
1856  * @comp: A calendar component object.
1857  * @tt: Date/timestamp value.
1858  *
1859  * Sets the date/timestamp of a calendar component object.  This should be
1860  * called whenever a calendar user agent makes a change to a component's
1861  * properties.
1862  *
1863  * Since: 3.34
1864  **/
1865 void
e_cal_component_set_dtstamp(ECalComponent * comp,const ICalTime * tt)1866 e_cal_component_set_dtstamp (ECalComponent *comp,
1867 			     const ICalTime *tt)
1868 {
1869 	ICalProperty *prop;
1870 
1871 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
1872 	g_return_if_fail (I_CAL_IS_TIME ((ICalTime *) tt));
1873 	g_return_if_fail (comp->priv->icalcomp != NULL);
1874 
1875 	prop = i_cal_component_get_first_property (comp->priv->icalcomp, I_CAL_DTSTAMP_PROPERTY);
1876 
1877 	/* This MUST exist, since we ensured that it did */
1878 	g_return_if_fail (prop != NULL);
1879 
1880 	i_cal_property_set_dtstamp (prop, (ICalTime *) tt);
1881 
1882 	g_clear_object (&prop);
1883 }
1884 
1885 /**
1886  * e_cal_component_get_dtstart:
1887  * @comp: A calendar component object.
1888  *
1889  * Queries the date/time start of a calendar component object.
1890  * Free the returned #ECalComponentDateTime with e_cal_component_datetime_free(),
1891  * when no longer needed.
1892  *
1893  * Returns: (transfer full) (nullable): the date/time start, as an #ECalComponentDateTime
1894  *
1895  * Since: 3.34
1896  **/
1897 ECalComponentDateTime *
e_cal_component_get_dtstart(ECalComponent * comp)1898 e_cal_component_get_dtstart (ECalComponent *comp)
1899 {
1900 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
1901 	g_return_val_if_fail (comp->priv->icalcomp != NULL, NULL);
1902 
1903 	return get_datetime (comp->priv->icalcomp, I_CAL_DTSTART_PROPERTY, i_cal_property_get_dtstart, NULL);
1904 }
1905 
1906 /**
1907  * e_cal_component_set_dtstart:
1908  * @comp: A calendar component object.
1909  * @dt: (nullable): Start date/time, or %NULL, to remove the property.
1910  *
1911  * Sets the date/time start property of a calendar component object.
1912  *
1913  * Since: 3.34
1914  **/
1915 void
e_cal_component_set_dtstart(ECalComponent * comp,const ECalComponentDateTime * dt)1916 e_cal_component_set_dtstart (ECalComponent *comp,
1917 			     const ECalComponentDateTime *dt)
1918 {
1919 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
1920 	g_return_if_fail (comp->priv->icalcomp != NULL);
1921 
1922 	set_datetime (comp->priv->icalcomp, I_CAL_DTSTART_PROPERTY,
1923 		i_cal_property_new_dtstart,
1924 		i_cal_property_set_dtstart,
1925 		dt,
1926 		NULL);
1927 
1928 	comp->priv->need_sequence_inc = TRUE;
1929 }
1930 
1931 /**
1932  * e_cal_component_get_due:
1933  * @comp: A calendar component object.
1934  *
1935  * Queries the due date/time of a calendar component object. In case there's no DUE,
1936  * but only DTSTART and DURATION, then the due is computed from the later two.
1937  * Free the returned #ECalComponentDateTime with e_cal_component_datetime_free(),
1938  * when no longer needed.
1939  *
1940  * Returns: (transfer full) (nullable): the due date/time, as an #ECalComponentDateTime
1941  *
1942  * Since: 3.34
1943  **/
1944 ECalComponentDateTime *
e_cal_component_get_due(ECalComponent * comp)1945 e_cal_component_get_due (ECalComponent *comp)
1946 {
1947 	ECalComponentDateTime *dt;
1948 
1949 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
1950 	g_return_val_if_fail (comp->priv->icalcomp != NULL, NULL);
1951 
1952 	dt = get_datetime (comp->priv->icalcomp, I_CAL_DUE_PROPERTY, i_cal_property_get_due, NULL);
1953 
1954 	/* If we don't have a DTEND property, then we try to get DTSTART
1955 	 * + DURATION. */
1956 	if (!dt)
1957 		dt = e_cal_component_get_start_plus_duration (comp);
1958 
1959 	return dt;
1960 }
1961 
1962 /**
1963  * e_cal_component_set_due:
1964  * @comp: A calendar component object.
1965  * @dt: (nullable): End date/time, or %NULL, to remove the property.
1966  *
1967  * Sets the due date/time property of a calendar component object.
1968  *
1969  * Since: 3.34
1970  **/
1971 void
e_cal_component_set_due(ECalComponent * comp,const ECalComponentDateTime * dt)1972 e_cal_component_set_due (ECalComponent *comp,
1973 			 const ECalComponentDateTime *dt)
1974 {
1975 	ICalProperty *prop;
1976 
1977 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
1978 	g_return_if_fail (comp->priv->icalcomp != NULL);
1979 
1980 	set_datetime (comp->priv->icalcomp, I_CAL_DUE_PROPERTY,
1981 		i_cal_property_new_due,
1982 		i_cal_property_set_due,
1983 		dt,
1984 		NULL);
1985 
1986 	/* Make sure we remove any existing DURATION property, as it can't be
1987 	 * used with a DTEND. If DTEND is set to NULL, i.e. removed, we also
1988 	 * want to remove any DURATION. */
1989 	prop = i_cal_component_get_first_property (comp->priv->icalcomp, I_CAL_DURATION_PROPERTY);
1990 	if (prop) {
1991 		i_cal_component_remove_property (comp->priv->icalcomp, prop);
1992 		g_clear_object (&prop);
1993 	}
1994 
1995 	comp->priv->need_sequence_inc = TRUE;
1996 }
1997 
1998 /* Builds a list of ECalComponentPeriod structures based on a list of icalproperties */
1999 static GSList * /* ECalComponentPeriod * */
get_period_list(ICalComponent * icalcomp,ICalPropertyKind prop_kind,ICalDatetimeperiod * (* get_prop_func)(ICalProperty * prop))2000 get_period_list (ICalComponent *icalcomp,
2001 		 ICalPropertyKind prop_kind,
2002 		 ICalDatetimeperiod * (* get_prop_func) (ICalProperty *prop))
2003 {
2004 	GSList *props, *link, *periods = NULL;
2005 
2006 	props = gather_all_properties (icalcomp, prop_kind, FALSE);
2007 
2008 	for (link = props; link; link = g_slist_next (link)) {
2009 		ICalProperty *prop = link->data;
2010 		ICalParameter *param;
2011 		ICalPeriod *icalperiod;
2012 		ICalDatetimeperiod *pt;
2013 		ICalDuration *duration = NULL;
2014 		ICalTime *start = NULL, *end = NULL;
2015 		ECalComponentPeriod *period;
2016 		ECalComponentPeriodKind period_kind;
2017 
2018 		if (!prop)
2019 			continue;
2020 
2021 		/* Get start and end/duration */
2022 		pt = get_prop_func (prop);
2023 		if (!pt)
2024 			continue;
2025 
2026 		icalperiod = i_cal_datetimeperiod_get_period (pt);
2027 
2028 		/* Get value parameter */
2029 		param = i_cal_property_get_first_parameter (prop, I_CAL_VALUE_PARAMETER);
2030 
2031 		if (param) {
2032 			ICalParameterValue value_type;
2033 
2034 			value_type = i_cal_parameter_get_value (param);
2035 
2036 			if (value_type == I_CAL_VALUE_DATE || value_type == I_CAL_VALUE_DATETIME) {
2037 				period_kind = E_CAL_COMPONENT_PERIOD_DATETIME;
2038 			} else if (value_type == I_CAL_VALUE_PERIOD) {
2039 				duration = i_cal_period_get_duration (icalperiod);
2040 
2041 				if (!duration ||
2042 				    i_cal_duration_is_null_duration (duration) ||
2043 				    i_cal_duration_is_bad_duration (duration))
2044 					period_kind = E_CAL_COMPONENT_PERIOD_DATETIME;
2045 				else
2046 					period_kind = E_CAL_COMPONENT_PERIOD_DURATION;
2047 
2048 				g_clear_object (&duration);
2049 			} else {
2050 				g_message ("get_period_list(): Unknown value for period %d; using DATETIME", value_type);
2051 				period_kind = E_CAL_COMPONENT_PERIOD_DATETIME;
2052 			}
2053 		} else {
2054 			period_kind = E_CAL_COMPONENT_PERIOD_DATETIME;
2055 		}
2056 
2057 		start = i_cal_period_get_start (icalperiod);
2058 
2059 		if (period_kind == E_CAL_COMPONENT_PERIOD_DATETIME) {
2060 			if (!start || i_cal_time_is_null_time (start)) {
2061 				g_clear_object (&start);
2062 				start = i_cal_datetimeperiod_get_time (pt);
2063 			} else {
2064 				end = i_cal_period_get_end (icalperiod);
2065 			}
2066 		} else /* if (period_kind == E_CAL_COMPONENT_PERIOD_DURATION) */ {
2067 			duration = i_cal_period_get_duration (icalperiod);
2068 		}
2069 
2070 		period = period_kind == E_CAL_COMPONENT_PERIOD_DATETIME ?
2071 			e_cal_component_period_new_datetime (start, end) :
2072 			e_cal_component_period_new_duration (start, duration);
2073 
2074 		if (period)
2075 			periods = g_slist_prepend (periods, period);
2076 
2077 		g_clear_object (&param);
2078 		g_clear_object (&icalperiod);
2079 		g_clear_object (&pt);
2080 		g_clear_object (&duration);
2081 		g_clear_object (&start);
2082 		g_clear_object (&end);
2083 	}
2084 
2085 	g_slist_free_full (props, g_object_unref);
2086 
2087 	/* No need to reverse it, the props are in reverse order
2088 	   and processed in the reverse order, thus the result
2089 	   is in the expected order. */
2090 	return periods;
2091 }
2092 
2093 /* Sets a period list value */
2094 static void
set_period_list(ICalComponent * icalcomp,ICalPropertyKind prop_kind,ICalProperty * (* new_prop_func)(ICalDatetimeperiod * period),const GSList * periods_list)2095 set_period_list (ICalComponent *icalcomp,
2096 		 ICalPropertyKind prop_kind,
2097 		 ICalProperty *(* new_prop_func) (ICalDatetimeperiod *period),
2098 		 const GSList *periods_list)
2099 {
2100 	GSList *link;
2101 
2102 	/* Remove old periods */
2103 
2104 	remove_all_properties_of_kind (icalcomp, prop_kind);
2105 
2106 	/* Add in new periods */
2107 
2108 	for (link = (GSList *) periods_list; link; link = g_slist_next (link)) {
2109 		const ECalComponentPeriod *period = link->data;
2110 		ICalDatetimeperiod *ic_datetimeperiod;
2111 		ICalPeriod *ic_period;
2112 		ICalProperty *prop;
2113 		ICalParameter *param;
2114 		ICalParameterValue value_type = I_CAL_VALUE_PERIOD;
2115 		ICalTime *end;
2116 
2117 		if (!period)
2118 			continue;
2119 
2120 		ic_period = i_cal_period_new_null_period ();
2121 		ic_datetimeperiod = i_cal_datetimeperiod_new ();
2122 
2123 		i_cal_period_set_start (ic_period, e_cal_component_period_get_start (period));
2124 
2125 		switch (e_cal_component_period_get_kind (period)) {
2126 		case E_CAL_COMPONENT_PERIOD_DATETIME:
2127 			end = e_cal_component_period_get_end (period);
2128 			if (!end || i_cal_time_is_null_time (end)) {
2129 				i_cal_datetimeperiod_set_time (ic_datetimeperiod, e_cal_component_period_get_start (period));
2130 				if (i_cal_time_is_date (e_cal_component_period_get_start (period))) {
2131 					value_type = I_CAL_VALUE_DATE;
2132 				} else {
2133 					value_type = I_CAL_VALUE_DATETIME;
2134 				}
2135 			} else {
2136 				i_cal_period_set_end (ic_period, e_cal_component_period_get_end (period));
2137 			}
2138 			break;
2139 		case E_CAL_COMPONENT_PERIOD_DURATION:
2140 			i_cal_period_set_duration (ic_period, e_cal_component_period_get_duration (period));
2141 			break;
2142 		}
2143 
2144 		i_cal_datetimeperiod_set_period (ic_datetimeperiod, ic_period);
2145 
2146 		prop = new_prop_func (ic_datetimeperiod);
2147 
2148 		param = i_cal_parameter_new_value (value_type);
2149 		i_cal_property_take_parameter (prop, param);
2150 
2151 		i_cal_component_take_property (icalcomp, prop);
2152 
2153 		g_object_unref (ic_datetimeperiod);
2154 		g_object_unref (ic_period);
2155 	}
2156 }
2157 
2158 static gboolean
extract_exdate_properties_cb(ICalComponent * icalcomp,ICalProperty * prop,gpointer user_data)2159 extract_exdate_properties_cb (ICalComponent *icalcomp,
2160 			      ICalProperty *prop,
2161 			      gpointer user_data)
2162 {
2163 	GSList **pexdates = user_data;
2164 	ICalTime *tt;
2165 
2166 	g_return_val_if_fail (pexdates != NULL, FALSE);
2167 
2168 	tt = i_cal_property_get_exdate (prop);
2169 	if (tt) {
2170 		ICalParameter *param;
2171 		gchar *tzid;
2172 
2173 		param = i_cal_property_get_first_parameter (prop, I_CAL_TZID_PARAMETER);
2174 		if (param)
2175 			tzid = g_strdup (i_cal_parameter_get_tzid (param));
2176 		else
2177 			tzid = NULL;
2178 
2179 		if (tzid && !*tzid) {
2180 			g_free (tzid);
2181 			tzid = NULL;
2182 		}
2183 
2184 		*pexdates = g_slist_prepend (*pexdates, e_cal_component_datetime_new_take (tt, tzid));
2185 
2186 		g_clear_object (&param);
2187 	}
2188 
2189 	return TRUE;
2190 }
2191 
2192 /**
2193  * e_cal_component_get_exdates:
2194  * @comp: A calendar component object.
2195  *
2196  * Queries the list of exception date properties in a calendar component object.
2197  * Free the returned #GSList with g_slist_free_full (exdates, e_cal_component_datetime_free);,
2198  * when no longer needed.
2199  *
2200  * Returns: (transfer full) (nullable) (element-type ECalComponentDateTime):
2201  *    the list of exception dates, as a #GSList of #ECalComponentDateTime
2202  *
2203  * Since: 3.34
2204  **/
2205 GSList *
e_cal_component_get_exdates(ECalComponent * comp)2206 e_cal_component_get_exdates (ECalComponent *comp)
2207 {
2208 	GSList *exdates = NULL;
2209 
2210 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
2211 	g_return_val_if_fail (comp->priv->icalcomp != NULL, NULL);
2212 
2213 	foreach_property (comp->priv->icalcomp, I_CAL_EXDATE_PROPERTY,
2214 		extract_exdate_properties_cb, &exdates);
2215 
2216 	return g_slist_reverse (exdates);
2217 }
2218 
2219 /**
2220  * e_cal_component_set_exdates:
2221  * @comp: A calendar component object.
2222  * @exdate_list: (nullable) (element-type ECalComponentDateTime): List of #ECalComponentDateTime structures.
2223  *
2224  * Sets the list of exception dates in a calendar component object.
2225  *
2226  * Since: 3.34
2227  **/
2228 void
e_cal_component_set_exdates(ECalComponent * comp,const GSList * exdate_list)2229 e_cal_component_set_exdates (ECalComponent *comp,
2230 			     const GSList *exdate_list)
2231 {
2232 	GSList *link;
2233 
2234 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
2235 	g_return_if_fail (comp->priv->icalcomp != NULL);
2236 
2237 	/* Remove old exception dates */
2238 	remove_all_properties_of_kind (comp->priv->icalcomp, I_CAL_EXDATE_PROPERTY);
2239 
2240 	/* Add in new exception dates */
2241 
2242 	for (link = (GSList *) exdate_list; link; link = g_slist_next (link)) {
2243 		const ECalComponentDateTime *dt = link->data;
2244 		ICalProperty *prop;
2245 		ICalTime *tt;
2246 		const gchar *tzid;
2247 
2248 		if (!dt)
2249 			continue;
2250 
2251 		tt = e_cal_component_datetime_get_value (dt);
2252 		if (!tt)
2253 			continue;
2254 
2255 		tzid = e_cal_component_datetime_get_tzid (dt);
2256 
2257 		prop = i_cal_property_new_exdate (tt);
2258 
2259 		if (tzid && *tzid) {
2260 			ICalParameter *param;
2261 
2262 			param = i_cal_parameter_new_tzid ((gchar *) tzid);
2263 			i_cal_property_take_parameter (prop, param);
2264 		}
2265 
2266 		i_cal_component_take_property (comp->priv->icalcomp, prop);
2267 	}
2268 
2269 	comp->priv->need_sequence_inc = TRUE;
2270 }
2271 
2272 /**
2273  * e_cal_component_has_exdates:
2274  * @comp: A calendar component object.
2275  *
2276  * Queries whether a calendar component object has any exception dates defined
2277  * for it.
2278  *
2279  * Returns: TRUE if the component has exception dates, FALSE otherwise.
2280  *
2281  * Since: 3.34
2282  **/
2283 gboolean
e_cal_component_has_exdates(ECalComponent * comp)2284 e_cal_component_has_exdates (ECalComponent *comp)
2285 {
2286 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
2287 	g_return_val_if_fail (comp->priv->icalcomp != NULL, FALSE);
2288 
2289 	return e_cal_util_component_has_property (comp->priv->icalcomp, I_CAL_EXDATE_PROPERTY);
2290 }
2291 
2292 /* Gets a list of recurrence rules */
2293 static GSList *
get_recur_list(ICalComponent * icalcomp,ICalPropertyKind prop_kind,ICalRecurrence * (* get_prop_func)(ICalProperty * prop))2294 get_recur_list (ICalComponent *icalcomp,
2295 		ICalPropertyKind prop_kind,
2296                 ICalRecurrence * (* get_prop_func) (ICalProperty *prop))
2297 {
2298 	GSList *props, *link, *recurs = NULL;
2299 
2300 	props = gather_all_properties (icalcomp, prop_kind, FALSE);
2301 
2302 	for (link = props; link; link = g_slist_next (link)) {
2303 		ICalProperty *prop = link->data;
2304 		ICalRecurrence *rt;
2305 
2306 		rt = get_prop_func (prop);
2307 		if (rt)
2308 			recurs = g_slist_prepend (recurs, rt);
2309 	}
2310 
2311 	g_slist_free_full (props, g_object_unref);
2312 
2313 	/* No need to reverse it, the props are in reverse order
2314 	   and processed in the reverse order, thus the result
2315 	   is in the expected order. */
2316 	return recurs;
2317 }
2318 
2319 /* Sets a list of recurrence rules */
2320 static void
set_recur_list(ICalComponent * icalcomp,ICalPropertyKind prop_kind,ICalProperty * (* new_prop_func)(ICalRecurrence * recur),const GSList * rl)2321 set_recur_list (ICalComponent *icalcomp,
2322 		ICalPropertyKind prop_kind,
2323 		ICalProperty * (* new_prop_func) (ICalRecurrence *recur),
2324 		const GSList *rl) /* ICalRecurrence * */
2325 {
2326 	GSList *link;
2327 
2328 	/* Remove old recurrences */
2329 	remove_all_properties_of_kind (icalcomp, prop_kind);
2330 
2331 	/* Add in new recurrences */
2332 
2333 	for (link = (GSList *) rl; link; link = g_slist_next (link)) {
2334 		ICalProperty *prop;
2335 		ICalRecurrence *recur = link->data;
2336 
2337 		if (recur) {
2338 			prop = (* new_prop_func) (recur);
2339 			i_cal_component_take_property (icalcomp, prop);
2340 		}
2341 	}
2342 }
2343 
2344 /**
2345  * e_cal_component_get_exrules:
2346  * @comp: A calendar component object.
2347  *
2348  * Queries the list of exception rule properties of a calendar component
2349  * object. Free the returned list with g_slist_free_full (slist, g_object_unref);,
2350  * when no longer needed.
2351  *
2352  * Returns: (transfer full) (nullable) (element-type ICalRecurrence): a #GSList
2353  *    of exception rules as #ICalRecurrence structures, or %NULL, when none exist.
2354  *
2355  * Since: 3.34
2356  **/
2357 GSList *
e_cal_component_get_exrules(ECalComponent * comp)2358 e_cal_component_get_exrules (ECalComponent *comp)
2359 {
2360 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
2361 	g_return_val_if_fail (comp->priv->icalcomp != NULL, NULL);
2362 
2363 	return get_recur_list (comp->priv->icalcomp, I_CAL_EXRULE_PROPERTY, i_cal_property_get_exrule);
2364 }
2365 
2366 /**
2367  * e_cal_component_get_exrule_properties:
2368  * @comp: A calendar component object.
2369  *
2370  * Queries the list of exception rule properties of a calendar component object.
2371  * Free the list with g_slist_free_full (slist, g_object_unref);, when
2372  * no longer needed.
2373  *
2374  * Returns: (transfer full) (nullable) (element-type ICalProperty): a list of exception
2375  *    rule properties
2376  *
2377  * Since: 3.34
2378  **/
2379 GSList *
e_cal_component_get_exrule_properties(ECalComponent * comp)2380 e_cal_component_get_exrule_properties (ECalComponent *comp)
2381 {
2382 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
2383 	g_return_val_if_fail (comp->priv->icalcomp != NULL, NULL);
2384 
2385 	return gather_all_properties (comp->priv->icalcomp, I_CAL_EXRULE_PROPERTY, TRUE);
2386 }
2387 
2388 /**
2389  * e_cal_component_set_exrules:
2390  * @comp: A calendar component object.
2391  * @recur_list: (nullable) (element-type ICalRecurrence): a #GSList
2392  *    of #ICalRecurrence structures, or %NULL.
2393  *
2394  * Sets the list of exception rules in a calendar component object.
2395  *
2396  * Since: 3.34
2397  **/
2398 void
e_cal_component_set_exrules(ECalComponent * comp,const GSList * recur_list)2399 e_cal_component_set_exrules (ECalComponent *comp,
2400 			     const GSList *recur_list)
2401 {
2402 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
2403 	g_return_if_fail (comp->priv->icalcomp != NULL);
2404 
2405 	set_recur_list (comp->priv->icalcomp, I_CAL_EXRULE_PROPERTY, i_cal_property_new_exrule, recur_list);
2406 
2407 	comp->priv->need_sequence_inc = TRUE;
2408 }
2409 
2410 /**
2411  * e_cal_component_has_exrules:
2412  * @comp: A calendar component object.
2413  *
2414  * Queries whether a calendar component object has any exception rules defined
2415  * for it.
2416  *
2417  * Returns: TRUE if the component has exception rules, FALSE otherwise.
2418  *
2419  * Since: 3.34
2420  **/
2421 gboolean
e_cal_component_has_exrules(ECalComponent * comp)2422 e_cal_component_has_exrules (ECalComponent *comp)
2423 {
2424 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
2425 	g_return_val_if_fail (comp->priv->icalcomp != NULL, FALSE);
2426 
2427 	return e_cal_util_component_has_property (comp->priv->icalcomp, I_CAL_EXRULE_PROPERTY);
2428 }
2429 
2430 /**
2431  * e_cal_component_has_exceptions:
2432  * @comp: A calendar component object
2433  *
2434  * Queries whether a calendar component object has any exception dates
2435  * or exception rules.
2436  *
2437  * Returns: TRUE if the component has exceptions, FALSE otherwise.
2438  *
2439  * Since: 3.34
2440  **/
2441 gboolean
e_cal_component_has_exceptions(ECalComponent * comp)2442 e_cal_component_has_exceptions (ECalComponent *comp)
2443 {
2444 	return e_cal_component_has_exdates (comp) || e_cal_component_has_exrules (comp);
2445 }
2446 
2447 /**
2448  * e_cal_component_get_geo:
2449  * @comp: A calendar component object.
2450  *
2451  * Gets the geographic position property of a calendar component object.
2452  * Free the returned non-NULL object with g_object_unref(), when
2453  * no longer needed.
2454  *
2455  * Returns: (transfer full) (nullable): the geographic position as #ICalGeo,
2456  *    or %NULL, when none set.
2457  *
2458  * Since: 3.34
2459  **/
2460 ICalGeo *
e_cal_component_get_geo(ECalComponent * comp)2461 e_cal_component_get_geo (ECalComponent *comp)
2462 {
2463 	ICalProperty *prop;
2464 	ICalGeo *geo;
2465 
2466 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
2467 	g_return_val_if_fail (comp->priv->icalcomp != NULL, NULL);
2468 
2469 	prop = i_cal_component_get_first_property (comp->priv->icalcomp, I_CAL_GEO_PROPERTY);
2470 	if (!prop)
2471 		return NULL;
2472 
2473 	geo = i_cal_property_get_geo (prop);
2474 
2475 	g_object_unref (prop);
2476 
2477 	return geo;
2478 }
2479 
2480 /**
2481  * e_cal_component_set_geo:
2482  * @comp: A calendar component object.
2483  * @geo: (nullable): Value for the geographic position property, or %NULL to unset.
2484  *
2485  * Sets the geographic position property on a calendar component object.
2486  *
2487  * Since: 3.34
2488  **/
2489 void
e_cal_component_set_geo(ECalComponent * comp,const ICalGeo * geo)2490 e_cal_component_set_geo (ECalComponent *comp,
2491 			 const ICalGeo *geo)
2492 {
2493 	ICalProperty *prop;
2494 
2495 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
2496 	g_return_if_fail (comp->priv->icalcomp != NULL);
2497 
2498 	prop = i_cal_component_get_first_property (comp->priv->icalcomp, I_CAL_GEO_PROPERTY);
2499 
2500 	if (!geo) {
2501 		if (prop) {
2502 			i_cal_component_remove_property (comp->priv->icalcomp, prop);
2503 			g_clear_object (&prop);
2504 		}
2505 
2506 		return;
2507 	}
2508 
2509 	if (prop) {
2510 		i_cal_property_set_geo (prop, (ICalGeo *) geo);
2511 	} else {
2512 		prop = i_cal_property_new_geo ((ICalGeo *) geo);
2513 		i_cal_component_add_property (comp->priv->icalcomp, prop);
2514 	}
2515 
2516 	g_clear_object (&prop);
2517 }
2518 
2519 /**
2520  * e_cal_component_get_last_modified:
2521  * @comp: A calendar component object.
2522  *
2523  * Queries the time at which a calendar component object was last modified in
2524  * the calendar store. Free the returned non-NULL pointer with g_object_unref(),
2525  * when no longer needed.
2526  *
2527  * Returns: (transfer full): the last modified time, as an #ICalTime, or %NULL, when none is set
2528  *
2529  * Since: 3.34
2530  **/
2531 ICalTime *
e_cal_component_get_last_modified(ECalComponent * comp)2532 e_cal_component_get_last_modified (ECalComponent *comp)
2533 {
2534 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
2535 	g_return_val_if_fail (comp->priv->icalcomp != NULL, NULL);
2536 
2537 	return get_icaltimetype (comp->priv->icalcomp, I_CAL_LASTMODIFIED_PROPERTY, i_cal_property_get_lastmodified);
2538 }
2539 
2540 /**
2541  * e_cal_component_set_last_modified:
2542  * @comp: A calendar component object.
2543  * @tt: (nullable): Value for the last time modified.
2544  *
2545  * Sets the time at which a calendar component object was last stored in the
2546  * calendar store.  This should not be called by plain calendar user agents.
2547  *
2548  * Since: 3.34
2549  **/
2550 void
e_cal_component_set_last_modified(ECalComponent * comp,const ICalTime * tt)2551 e_cal_component_set_last_modified (ECalComponent *comp,
2552 				   const ICalTime *tt)
2553 {
2554 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
2555 	g_return_if_fail (comp->priv->icalcomp != NULL);
2556 
2557 	set_icaltimetype (comp->priv->icalcomp, I_CAL_LASTMODIFIED_PROPERTY,
2558 		i_cal_property_new_lastmodified,
2559 		i_cal_property_set_lastmodified,
2560 		tt);
2561 }
2562 
2563 /**
2564  * e_cal_component_get_organizer:
2565  * @comp:  A calendar component object
2566  *
2567  * Queries the organizer property of a calendar component object.
2568  * Free the returned structure with e_cal_component_organizer_free(),
2569  * when no longer needed.
2570  *
2571  * Returns: (transfer full) (nullable): an #ECalComponentOrganizer structure
2572  *    destribing the organizer, or %NULL, when none exists.
2573  *
2574  * Since: 3.34
2575  **/
2576 ECalComponentOrganizer *
e_cal_component_get_organizer(ECalComponent * comp)2577 e_cal_component_get_organizer (ECalComponent *comp)
2578 {
2579 	ECalComponentOrganizer *organizer;
2580 	ICalProperty *prop;
2581 
2582 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
2583 	g_return_val_if_fail (comp->priv->icalcomp != NULL, NULL);
2584 
2585 	prop = i_cal_component_get_first_property (comp->priv->icalcomp, I_CAL_ORGANIZER_PROPERTY);
2586 	if (!prop)
2587 		return NULL;
2588 
2589 	organizer = e_cal_component_organizer_new_from_property (prop);
2590 
2591 	g_object_unref (prop);
2592 
2593 	return organizer;
2594 }
2595 
2596 /**
2597  * e_cal_component_set_organizer:
2598  * @comp:  A calendar component object.
2599  * @organizer: (nullable): Value for the organizer property, as an #ECalComponentOrganizer
2600  *
2601  * Sets the organizer of a calendar component object
2602  *
2603  * Since: 3.34
2604  **/
2605 void
e_cal_component_set_organizer(ECalComponent * comp,const ECalComponentOrganizer * organizer)2606 e_cal_component_set_organizer (ECalComponent *comp,
2607 			       const ECalComponentOrganizer *organizer)
2608 {
2609 	ICalProperty *prop;
2610 
2611 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
2612 	g_return_if_fail (comp->priv->icalcomp != NULL);
2613 
2614 	prop = i_cal_component_get_first_property (comp->priv->icalcomp, I_CAL_ORGANIZER_PROPERTY);
2615 
2616 	if (!organizer) {
2617 		if (prop) {
2618 			i_cal_component_remove_property (comp->priv->icalcomp, prop);
2619 			g_clear_object (&prop);
2620 		}
2621 
2622 		return;
2623 	}
2624 
2625 	if (!prop) {
2626 		prop = i_cal_property_new (I_CAL_ORGANIZER_PROPERTY);
2627 		i_cal_component_add_property (comp->priv->icalcomp, prop);
2628 	}
2629 
2630 	e_cal_component_organizer_fill_property (organizer, prop);
2631 
2632 	g_clear_object (&prop);
2633 }
2634 
2635 /**
2636  * e_cal_component_has_organizer:
2637  * @comp: A calendar component object.
2638  *
2639  * Check whether a calendar component object has an organizer or not.
2640  *
2641  * Returns: TRUE if there is an organizer, FALSE otherwise.
2642  *
2643  * Since: 3.34
2644  **/
2645 gboolean
e_cal_component_has_organizer(ECalComponent * comp)2646 e_cal_component_has_organizer (ECalComponent *comp)
2647 {
2648 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
2649 
2650 	return e_cal_util_component_has_property (comp->priv->icalcomp, I_CAL_ORGANIZER_PROPERTY);
2651 }
2652 
2653 /**
2654  * e_cal_component_get_percent_complete:
2655  * @comp: A calendar component object.
2656  *
2657  * Queries the percent-complete property of a calendar component object.
2658  *
2659  * Returns: the percent-complete property value, or -1 if not found
2660  *
2661  * Since: 3.34
2662  **/
2663 gint
e_cal_component_get_percent_complete(ECalComponent * comp)2664 e_cal_component_get_percent_complete (ECalComponent *comp)
2665 {
2666 	ICalProperty *prop;
2667 	gint percent;
2668 
2669 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), -1);
2670 	g_return_val_if_fail (comp->priv->icalcomp != NULL, -1);
2671 
2672 	prop = i_cal_component_get_first_property (comp->priv->icalcomp, I_CAL_PERCENTCOMPLETE_PROPERTY);
2673 	if (!prop)
2674 		return -1;
2675 
2676 	percent = i_cal_property_get_percentcomplete (prop);
2677 
2678 	g_object_unref (prop);
2679 
2680 	return percent;
2681 }
2682 
2683 /**
2684  * e_cal_component_set_percent_complete:
2685  * @comp: an #ECalComponent
2686  * @percent: a percent to set, or -1 to remove the property
2687  *
2688  * Sets percent complete. The @percent can be between 0 and 100, inclusive.
2689  * A special value -1 can be used to remove the percent complete property.
2690  *
2691  * Since: 3.34
2692  **/
2693 void
e_cal_component_set_percent_complete(ECalComponent * comp,gint percent)2694 e_cal_component_set_percent_complete (ECalComponent *comp,
2695 				      gint percent)
2696 {
2697 	ICalProperty *prop;
2698 
2699 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
2700 	g_return_if_fail (comp->priv->icalcomp != NULL);
2701 	g_return_if_fail (percent >= -1 && percent <= 100);
2702 
2703 	prop = i_cal_component_get_first_property (comp->priv->icalcomp, I_CAL_PERCENTCOMPLETE_PROPERTY);
2704 
2705 	if (percent == -1) {
2706 		if (prop) {
2707 			i_cal_component_remove_property (comp->priv->icalcomp, prop);
2708 			g_clear_object (&prop);
2709 		}
2710 
2711 		return;
2712 	}
2713 
2714 	if (prop) {
2715 		i_cal_property_set_percentcomplete (prop, percent);
2716 		g_clear_object (&prop);
2717 	} else {
2718 		prop = i_cal_property_new_percentcomplete (percent);
2719 		i_cal_component_take_property (comp->priv->icalcomp, prop);
2720 	}
2721 }
2722 
2723 /**
2724  * e_cal_component_get_priority:
2725  * @comp: A calendar component object.
2726  *
2727  * Queries the priority property of a calendar component object.
2728  *
2729  * Returns: the priority property value, or -1, if not found
2730  *
2731  * Since: 3.34
2732  **/
2733 gint
e_cal_component_get_priority(ECalComponent * comp)2734 e_cal_component_get_priority (ECalComponent *comp)
2735 {
2736 	ICalProperty *prop;
2737 	gint priority;
2738 
2739 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), -1);
2740 	g_return_val_if_fail (comp->priv->icalcomp != NULL, -1);
2741 
2742 	prop = i_cal_component_get_first_property (comp->priv->icalcomp, I_CAL_PRIORITY_PROPERTY);
2743 	if (!prop)
2744 		return -1;
2745 
2746 	priority = i_cal_property_get_priority (prop);
2747 
2748 	g_object_unref (prop);
2749 
2750 	return priority;
2751 }
2752 
2753 /**
2754  * e_cal_component_set_priority:
2755  * @comp: A calendar component object.
2756  * @priority: Value for the priority property.
2757  *
2758  * Sets the priority property of a calendar component object.
2759  * The @priority can be between 0 and 9, inclusive.
2760  * A special value -1 can be used to remove the priority property.
2761  *
2762  * Since: 3.34
2763  **/
2764 void
e_cal_component_set_priority(ECalComponent * comp,gint priority)2765 e_cal_component_set_priority (ECalComponent *comp,
2766 			      gint priority)
2767 {
2768 	ICalProperty *prop;
2769 
2770 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
2771 	g_return_if_fail (comp->priv->icalcomp != NULL);
2772 	g_return_if_fail (priority >= -1 && priority <= 9);
2773 
2774 	prop = i_cal_component_get_first_property (comp->priv->icalcomp, I_CAL_PRIORITY_PROPERTY);
2775 
2776 	if (priority == -1) {
2777 		if (prop) {
2778 			i_cal_component_remove_property (comp->priv->icalcomp, prop);
2779 			g_clear_object (&prop);
2780 		}
2781 
2782 		return;
2783 	}
2784 
2785 	if (prop) {
2786 		i_cal_property_set_priority (prop, priority);
2787 		g_clear_object (&prop);
2788 	} else {
2789 		prop = i_cal_property_new_priority (priority);
2790 		i_cal_component_take_property (comp->priv->icalcomp, prop);
2791 	}
2792 }
2793 
2794 /**
2795  * e_cal_component_get_recurid:
2796  * @comp: A calendar component object.
2797  *
2798  * Queries the recurrence id property of a calendar component object.
2799  * Free the returned #ECalComponentRange with e_cal_component_range_free(),
2800  * whe no longer needed.
2801  *
2802  * Returns: (transfer full) (nullable): the recurrence id property, as an #ECalComponentRange
2803  *
2804  * Since: 3.34
2805  **/
2806 ECalComponentRange *
e_cal_component_get_recurid(ECalComponent * comp)2807 e_cal_component_get_recurid (ECalComponent *comp)
2808 {
2809 	ECalComponentDateTime *dt;
2810 	ECalComponentRangeKind range_kind;
2811 	ICalProperty *prop = NULL;
2812 	ICalParameter *param;
2813 
2814 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
2815 	g_return_val_if_fail (comp->priv->icalcomp != NULL, NULL);
2816 
2817 	dt = get_datetime (comp->priv->icalcomp, I_CAL_RECURRENCEID_PROPERTY, i_cal_property_get_recurrenceid, &prop);
2818 
2819 	if (!dt) {
2820 		g_clear_object (&prop);
2821 		return NULL;
2822 	}
2823 
2824 	range_kind = E_CAL_COMPONENT_RANGE_SINGLE;
2825 	param = i_cal_property_get_first_parameter (prop, I_CAL_RANGE_PARAMETER);
2826 
2827 	/* RFC 5545 says it can use only THIS_AND_FUTURE here */
2828 	if (param && i_cal_parameter_get_range (param) == I_CAL_RANGE_THISANDFUTURE)
2829 		range_kind = E_CAL_COMPONENT_RANGE_THISFUTURE;
2830 
2831 	g_clear_object (&param);
2832 	g_clear_object (&prop);
2833 
2834 	return e_cal_component_range_new_take (range_kind, dt);
2835 }
2836 
2837 /**
2838  * e_cal_component_get_recurid_as_string:
2839  * @comp: A calendar component object.
2840  *
2841  * Gets the recurrence ID property as a string.
2842  *
2843  * Returns: the recurrence ID as a string.
2844  *
2845  * Since: 3.34
2846  **/
2847 gchar *
e_cal_component_get_recurid_as_string(ECalComponent * comp)2848 e_cal_component_get_recurid_as_string (ECalComponent *comp)
2849 {
2850 	return e_cal_util_component_get_recurid_as_string (comp->priv->icalcomp);
2851 }
2852 
2853 /**
2854  * e_cal_component_set_recurid:
2855  * @comp: A calendar component object.
2856  * @recur_id: (nullable): Value for the recurrence id property, or %NULL, to remove the property.
2857  *
2858  * Sets the recurrence id property of a calendar component object.
2859  *
2860  * Since: 3.34
2861  **/
2862 void
e_cal_component_set_recurid(ECalComponent * comp,const ECalComponentRange * recur_id)2863 e_cal_component_set_recurid (ECalComponent *comp,
2864 			     const ECalComponentRange *recur_id)
2865 {
2866 	ECalComponentDateTime *dt;
2867 	ICalProperty *prop = NULL;
2868 
2869 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
2870 	g_return_if_fail (comp->priv->icalcomp != NULL);
2871 
2872 	dt = recur_id ? e_cal_component_range_get_datetime (recur_id) : NULL;
2873 
2874 	set_datetime (comp->priv->icalcomp, I_CAL_RECURRENCEID_PROPERTY,
2875 		i_cal_property_new_recurrenceid,
2876 		i_cal_property_set_recurrenceid,
2877 		dt,
2878 		&prop);
2879 
2880 	if (prop) {
2881 		ICalParameter *param;
2882 
2883 		param = i_cal_property_get_first_parameter (prop, I_CAL_RANGE_PARAMETER);
2884 
2885 		/* RFC 5545 says it can use only THIS_AND_FUTURE here */
2886 		if (e_cal_component_range_get_kind (recur_id) == E_CAL_COMPONENT_RANGE_THISFUTURE) {
2887 			if (param) {
2888 				i_cal_parameter_set_range (param, I_CAL_RANGE_THISANDFUTURE);
2889 			} else {
2890 				param = i_cal_parameter_new_range (I_CAL_RANGE_THISANDFUTURE);
2891 				i_cal_property_add_parameter (prop, param);
2892 			}
2893 		} else if (param) {
2894 			i_cal_property_remove_parameter_by_ref (prop, param);
2895 		}
2896 
2897 		g_clear_object (&param);
2898 		g_clear_object (&prop);
2899 	}
2900 }
2901 
2902 /**
2903  * e_cal_component_get_rdates:
2904  * @comp: A calendar component object.
2905  *
2906  * Queries the list of recurrence date properties in a calendar component
2907  * object. Free the returned #GSList with g_slist_free_full (slist, e_cal_component_period_free);,
2908  * when no longer needed.
2909  *
2910  * Returns: (transfer full) (nullable) (element-type ECalComponentPeriod): the list
2911  *    of recurrence dates, as a #GSList of #ECalComponentPeriod structures.
2912  *
2913  * Since: 3.34
2914  **/
2915 GSList *
e_cal_component_get_rdates(ECalComponent * comp)2916 e_cal_component_get_rdates (ECalComponent *comp)
2917 {
2918 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
2919 	g_return_val_if_fail (comp->priv->icalcomp != NULL, NULL);
2920 
2921 	return get_period_list (comp->priv->icalcomp, I_CAL_RDATE_PROPERTY, i_cal_property_get_rdate);
2922 }
2923 
2924 /**
2925  * e_cal_component_set_rdates:
2926  * @comp: A calendar component object.
2927  * @rdate_list: (nullable) (element-type ECalComponentPeriod): List of
2928  *    #ECalComponentPeriod structures, or %NULL to set none
2929  *
2930  * Sets the list of recurrence dates in a calendar component object.
2931  *
2932  * Since: 3.34
2933  **/
2934 void
e_cal_component_set_rdates(ECalComponent * comp,const GSList * rdate_list)2935 e_cal_component_set_rdates (ECalComponent *comp,
2936 			    const GSList *rdate_list)
2937 {
2938 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
2939 	g_return_if_fail (comp->priv->icalcomp != NULL);
2940 
2941 	set_period_list (comp->priv->icalcomp, I_CAL_RDATE_PROPERTY, i_cal_property_new_rdate, rdate_list);
2942 
2943 	comp->priv->need_sequence_inc = TRUE;
2944 }
2945 
2946 /**
2947  * e_cal_component_has_rdates:
2948  * @comp: A calendar component object.
2949  *
2950  * Queries whether a calendar component object has any recurrence dates defined
2951  * for it.
2952  *
2953  * Returns: TRUE if the component has recurrence dates, FALSE otherwise.
2954  *
2955  * Since: 3.34
2956  **/
2957 gboolean
e_cal_component_has_rdates(ECalComponent * comp)2958 e_cal_component_has_rdates (ECalComponent *comp)
2959 {
2960 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
2961 	g_return_val_if_fail (comp->priv->icalcomp != NULL, FALSE);
2962 
2963 	return e_cal_util_component_has_property (comp->priv->icalcomp, I_CAL_RDATE_PROPERTY);
2964 }
2965 
2966 /**
2967  * e_cal_component_get_rrules:
2968  * @comp: A calendar component object.
2969  *
2970  * Queries the list of recurrence rule properties of a calendar component
2971  * object. Free the returned list with g_slist_free_full (slist, g_object_unref);,
2972  * when no longer needed.
2973  *
2974  * Returns: (transfer full) (nullable) (element-type ICalRecurrence): a #GSList
2975  *    of recurrence rules as #ICalRecurrence structures, or %NULL, when none exist.
2976  *
2977  * Since: 3.34
2978  **/
2979 GSList *
e_cal_component_get_rrules(ECalComponent * comp)2980 e_cal_component_get_rrules (ECalComponent *comp)
2981 {
2982 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
2983 	g_return_val_if_fail (comp->priv->icalcomp != NULL, NULL);
2984 
2985 	return get_recur_list (comp->priv->icalcomp, I_CAL_RRULE_PROPERTY, i_cal_property_get_rrule);
2986 }
2987 
2988 /**
2989  * e_cal_component_get_rrule_properties:
2990  * @comp: A calendar component object.
2991  *
2992  * Queries a list of recurrence rule properties of a calendar component object.
2993  * Free the list with g_slist_free_full (slist, g_object_unref);, when
2994  * no longer needed.
2995  *
2996  * Returns: (transfer full) (nullable) (element-type ICalProperty): a list of recurrence
2997  *    rule properties
2998  *
2999  * Since: 3.34
3000  **/
3001 GSList *
e_cal_component_get_rrule_properties(ECalComponent * comp)3002 e_cal_component_get_rrule_properties (ECalComponent *comp)
3003 {
3004 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
3005 	g_return_val_if_fail (comp->priv->icalcomp != NULL, NULL);
3006 
3007 	return gather_all_properties (comp->priv->icalcomp, I_CAL_RRULE_PROPERTY, TRUE);
3008 }
3009 
3010 /**
3011  * e_cal_component_set_rrules:
3012  * @comp: A calendar component object.
3013  * @recur_list: (nullable) (element-type ICalRecurrence): List of #ICalRecurrence structures, or %NULL.
3014  *
3015  * Sets the list of recurrence rules in a calendar component object.
3016  *
3017  * Since: 3.34
3018  **/
3019 void
e_cal_component_set_rrules(ECalComponent * comp,const GSList * recur_list)3020 e_cal_component_set_rrules (ECalComponent *comp,
3021 			    const GSList *recur_list)
3022 {
3023 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
3024 	g_return_if_fail (comp->priv->icalcomp != NULL);
3025 
3026 	set_recur_list (comp->priv->icalcomp, I_CAL_RRULE_PROPERTY, i_cal_property_new_rrule, recur_list);
3027 
3028 	comp->priv->need_sequence_inc = TRUE;
3029 }
3030 
3031 /**
3032  * e_cal_component_has_rrules:
3033  * @comp: A calendar component object.
3034  *
3035  * Queries whether a calendar component object has any recurrence rules defined
3036  * for it.
3037  *
3038  * Returns: TRUE if the component has recurrence rules, FALSE otherwise.
3039  *
3040  * Since: 3.34
3041  **/
3042 gboolean
e_cal_component_has_rrules(ECalComponent * comp)3043 e_cal_component_has_rrules (ECalComponent *comp)
3044 {
3045 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
3046 	g_return_val_if_fail (comp->priv->icalcomp != NULL, FALSE);
3047 
3048 	return e_cal_util_component_has_property (comp->priv->icalcomp, I_CAL_RRULE_PROPERTY);
3049 }
3050 
3051 /**
3052  * e_cal_component_has_recurrences:
3053  * @comp: A calendar component object
3054  *
3055  * Queries whether a calendar component object has any recurrence dates or
3056  * recurrence rules.
3057  *
3058  * Returns: TRUE if the component has recurrences, FALSE otherwise.
3059  *
3060  * Since: 3.34
3061  **/
3062 gboolean
e_cal_component_has_recurrences(ECalComponent * comp)3063 e_cal_component_has_recurrences (ECalComponent *comp)
3064 {
3065 	return e_cal_component_has_rdates (comp) || e_cal_component_has_rrules (comp);
3066 }
3067 
3068 /* Counts the elements in the by_xxx fields of an ICalRecurrence;
3069    it also frees the 'field' array*/
3070 static gint
count_by_xxx_and_free(GArray * field)3071 count_by_xxx_and_free (GArray *field) /* gshort */
3072 {
3073 	gint ii;
3074 
3075 	if (!field)
3076 		return 0;
3077 
3078 	for (ii = 0; ii < field->len; ii++) {
3079 		if (g_array_index (field, gshort, ii) == I_CAL_RECURRENCE_ARRAY_MAX)
3080 			break;
3081 	}
3082 
3083 	g_array_unref (field);
3084 
3085 	return ii;
3086 }
3087 
3088 /**
3089  * e_cal_component_has_simple_recurrence:
3090  * @comp: A calendar component object.
3091  *
3092  * Checks whether the given calendar component object has simple recurrence
3093  * rules or more complicated ones.
3094  *
3095  * Returns: TRUE if it has a simple recurrence rule, FALSE otherwise.
3096  *
3097  * Since: 3.34
3098  **/
3099 gboolean
e_cal_component_has_simple_recurrence(ECalComponent * comp)3100 e_cal_component_has_simple_recurrence (ECalComponent *comp)
3101 {
3102 	GSList *rrule_list;
3103 	ICalRecurrence *rt;
3104 	gint n_by_second, n_by_minute, n_by_hour;
3105 	gint n_by_day, n_by_month_day, n_by_year_day;
3106 	gint n_by_week_no, n_by_month, n_by_set_pos;
3107 	gint len, i;
3108 	gboolean simple = FALSE;
3109 
3110 	if (!e_cal_component_has_recurrences (comp))
3111 		return TRUE;
3112 
3113 	rrule_list = e_cal_component_get_rrules (comp);
3114 	len = g_slist_length (rrule_list);
3115 	if (len > 1 || !rrule_list ||
3116 	    e_cal_component_has_rdates (comp) ||
3117 	    e_cal_component_has_exrules (comp))
3118 		goto cleanup;
3119 
3120 	/* Down to one rule, so test that one */
3121 	rt = rrule_list->data;
3122 
3123 	/* Any funky frequency? */
3124 	if (i_cal_recurrence_get_freq (rt) == I_CAL_SECONDLY_RECURRENCE ||
3125 	    i_cal_recurrence_get_freq (rt) == I_CAL_MINUTELY_RECURRENCE ||
3126 	    i_cal_recurrence_get_freq (rt) == I_CAL_HOURLY_RECURRENCE)
3127 		goto cleanup;
3128 
3129 	/* Any funky BY_* */
3130 #define N_HAS_BY(field) (count_by_xxx_and_free (field))
3131 
3132 	n_by_second = N_HAS_BY (i_cal_recurrence_get_by_second_array (rt));
3133 	n_by_minute = N_HAS_BY (i_cal_recurrence_get_by_minute_array (rt));
3134 	n_by_hour = N_HAS_BY (i_cal_recurrence_get_by_hour_array (rt));
3135 	n_by_day = N_HAS_BY (i_cal_recurrence_get_by_day_array (rt));
3136 	n_by_month_day = N_HAS_BY (i_cal_recurrence_get_by_month_day_array (rt));
3137 	n_by_year_day = N_HAS_BY (i_cal_recurrence_get_by_year_day_array (rt));
3138 	n_by_week_no = N_HAS_BY (i_cal_recurrence_get_by_week_no_array (rt));
3139 	n_by_month = N_HAS_BY (i_cal_recurrence_get_by_month_array (rt));
3140 	n_by_set_pos = N_HAS_BY (i_cal_recurrence_get_by_set_pos_array (rt));
3141 
3142 	if (n_by_second != 0
3143 	    || n_by_minute != 0
3144 	    || n_by_hour != 0)
3145 		goto cleanup;
3146 
3147 	switch (i_cal_recurrence_get_freq (rt)) {
3148 	case I_CAL_DAILY_RECURRENCE:
3149 		if (n_by_day != 0
3150 		    || n_by_month_day != 0
3151 		    || n_by_year_day != 0
3152 		    || n_by_week_no != 0
3153 		    || n_by_month != 0
3154 		    || n_by_set_pos != 0)
3155 			goto cleanup;
3156 
3157 		simple = TRUE;
3158 		break;
3159 
3160 	case I_CAL_WEEKLY_RECURRENCE:
3161 		if (n_by_month_day != 0
3162 		    || n_by_year_day != 0
3163 		    || n_by_week_no != 0
3164 		    || n_by_month != 0
3165 		    || n_by_set_pos != 0)
3166 			goto cleanup;
3167 
3168 		for (i = 0; i < 8; i++) {
3169 			gint pos;
3170 			gshort byday = i_cal_recurrence_get_by_day (rt, i);
3171 
3172 			if (byday == I_CAL_RECURRENCE_ARRAY_MAX)
3173 				break;
3174 
3175 			pos = i_cal_recurrence_day_position (byday);
3176 
3177 			if (pos != 0)
3178 				goto cleanup;
3179 		}
3180 
3181 		simple = TRUE;
3182 		break;
3183 
3184 	case I_CAL_MONTHLY_RECURRENCE:
3185 		if (n_by_year_day != 0
3186 		    || n_by_week_no != 0
3187 		    || n_by_month != 0
3188 		    || n_by_set_pos > 1)
3189 			goto cleanup;
3190 
3191 		if (n_by_month_day == 1) {
3192 			gint nth;
3193 
3194 			if (n_by_set_pos != 0)
3195 				goto cleanup;
3196 
3197 			nth = i_cal_recurrence_get_by_month_day (rt, 0);
3198 			if (nth < 1 && nth != -1)
3199 				goto cleanup;
3200 
3201 		} else if (n_by_day == 1) {
3202 			ICalRecurrenceWeekday weekday;
3203 			gint pos;
3204 
3205 			/* Outlook 2000 uses BYDAY=TU;BYSETPOS=2, and will not
3206 			 * accept BYDAY=2TU. So we now use the same as Outlook
3207 			 * by default. */
3208 
3209 			weekday = i_cal_recurrence_day_day_of_week (i_cal_recurrence_get_by_day (rt, 0));
3210 			pos = i_cal_recurrence_day_position (i_cal_recurrence_get_by_day (rt, 0));
3211 
3212 			if (pos == 0) {
3213 				if (n_by_set_pos != 1)
3214 					goto cleanup;
3215 				pos = i_cal_recurrence_get_by_set_pos (rt, 0);
3216 			}
3217 
3218 			if (pos < 0)
3219 				goto cleanup;
3220 
3221 			switch (weekday) {
3222 			case I_CAL_MONDAY_WEEKDAY:
3223 			case I_CAL_TUESDAY_WEEKDAY:
3224 			case I_CAL_WEDNESDAY_WEEKDAY:
3225 			case I_CAL_THURSDAY_WEEKDAY:
3226 			case I_CAL_FRIDAY_WEEKDAY:
3227 			case I_CAL_SATURDAY_WEEKDAY:
3228 			case I_CAL_SUNDAY_WEEKDAY:
3229 				break;
3230 
3231 			default:
3232 				goto cleanup;
3233 			}
3234 		} else {
3235 			goto cleanup;
3236 		}
3237 
3238 		simple = TRUE;
3239 		break;
3240 
3241 	case I_CAL_YEARLY_RECURRENCE:
3242 		if (n_by_day != 0
3243 		    || n_by_month_day != 0
3244 		    || n_by_year_day != 0
3245 		    || n_by_week_no != 0
3246 		    || n_by_month != 0
3247 		    || n_by_set_pos != 0)
3248 			goto cleanup;
3249 
3250 		simple = TRUE;
3251 		break;
3252 
3253 	default:
3254 		goto cleanup;
3255 	}
3256 
3257  cleanup:
3258 	g_slist_free_full (rrule_list, g_object_unref);
3259 
3260 	return simple;
3261 }
3262 
3263 /**
3264  * e_cal_component_is_instance:
3265  * @comp: A calendar component object.
3266  *
3267  * Checks whether a calendar component object is an instance of a recurring
3268  * event.
3269  *
3270  * Returns: TRUE if it is an instance, FALSE if not.
3271  *
3272  * Since: 3.34
3273  **/
3274 gboolean
e_cal_component_is_instance(ECalComponent * comp)3275 e_cal_component_is_instance (ECalComponent *comp)
3276 {
3277 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
3278 
3279 	return e_cal_util_component_has_property (comp->priv->icalcomp, I_CAL_RECURRENCEID_PROPERTY);
3280 }
3281 
3282 /**
3283  * e_cal_component_get_sequence:
3284  * @comp: A calendar component object.
3285  *
3286  * Queries the sequence number of a calendar component object.
3287  *
3288  * Returns: the sequence number, or -1 if not found
3289  *
3290  * Since: 3.34
3291  **/
3292 gint
e_cal_component_get_sequence(ECalComponent * comp)3293 e_cal_component_get_sequence (ECalComponent *comp)
3294 {
3295 	ICalProperty *prop;
3296 	gint sequence;
3297 
3298 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), -1);
3299 	g_return_val_if_fail (comp->priv->icalcomp != NULL, -1);
3300 
3301 	prop = i_cal_component_get_first_property (comp->priv->icalcomp, I_CAL_SEQUENCE_PROPERTY);
3302 	if (!prop)
3303 		return -1;
3304 
3305 	sequence = i_cal_property_get_sequence (prop);
3306 
3307 	g_object_unref (prop);
3308 
3309 	return sequence;
3310 }
3311 
3312 /**
3313  * e_cal_component_set_sequence:
3314  * @comp: A calendar component object.
3315  * @sequence: a sequence number to set, or -1 to remove the property
3316  *
3317  * Sets the sequence number of a calendar component object.
3318  * A special value -1 can be used to remove the sequence number property.
3319  *
3320  * Normally this function should not be called, since the sequence number
3321  * is incremented automatically at the proper times.
3322  *
3323  * Since: 3.34
3324  **/
3325 void
e_cal_component_set_sequence(ECalComponent * comp,gint sequence)3326 e_cal_component_set_sequence (ECalComponent *comp,
3327 			      gint sequence)
3328 {
3329 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
3330 	g_return_if_fail (comp->priv->icalcomp != NULL);
3331 
3332 	comp->priv->need_sequence_inc = FALSE;
3333 
3334 	if (sequence <= -1) {
3335 		ICalProperty *prop;
3336 
3337 		prop = i_cal_component_get_first_property (comp->priv->icalcomp, I_CAL_SEQUENCE_PROPERTY);
3338 		if (prop) {
3339 			i_cal_component_remove_property (comp->priv->icalcomp, prop);
3340 			g_object_unref (prop);
3341 		}
3342 
3343 		return;
3344 	}
3345 
3346 	i_cal_component_set_sequence (comp->priv->icalcomp, sequence);
3347 }
3348 
3349 /**
3350  * e_cal_component_get_status:
3351  * @comp: A calendar component object.
3352  *
3353  * Queries the status property of a calendar component object.
3354  *
3355  * Returns: the status value; or %I_CAL_STATUS_NONE, if the component
3356  *   has no status property
3357  *
3358  * Since: 3.34
3359  **/
3360 ICalPropertyStatus
e_cal_component_get_status(ECalComponent * comp)3361 e_cal_component_get_status (ECalComponent *comp)
3362 {
3363 	ICalProperty *prop;
3364 	ICalPropertyStatus status;
3365 
3366 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), I_CAL_STATUS_NONE);
3367 	g_return_val_if_fail (comp->priv->icalcomp != NULL, I_CAL_STATUS_NONE);
3368 
3369 	prop = i_cal_component_get_first_property (comp->priv->icalcomp, I_CAL_STATUS_PROPERTY);
3370 
3371 	if (!prop)
3372 		return I_CAL_STATUS_NONE;
3373 
3374 	status = i_cal_property_get_status (prop);
3375 
3376 	g_object_unref (prop);
3377 
3378 	return status;
3379 }
3380 
3381 /**
3382  * e_cal_component_set_status:
3383  * @comp: A calendar component object.
3384  * @status: Status value, as an #ICalPropertyStatus. Use %I_CAL_STATUS_NONE, to unset the property
3385  *
3386  * Sets the status property of a calendar component object.
3387  *
3388  * Since: 3.34
3389  **/
3390 void
e_cal_component_set_status(ECalComponent * comp,ICalPropertyStatus status)3391 e_cal_component_set_status (ECalComponent *comp,
3392 			    ICalPropertyStatus status)
3393 {
3394 	ICalProperty *prop;
3395 
3396 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
3397 	g_return_if_fail (comp->priv->icalcomp != NULL);
3398 
3399 	comp->priv->need_sequence_inc = TRUE;
3400 
3401 	prop = i_cal_component_get_first_property (comp->priv->icalcomp, I_CAL_STATUS_PROPERTY);
3402 
3403 	if (status == I_CAL_STATUS_NONE) {
3404 		if (prop) {
3405 			i_cal_component_remove_property (comp->priv->icalcomp, prop);
3406 			g_object_unref (prop);
3407 		}
3408 
3409 		return;
3410 	}
3411 
3412 	if (prop) {
3413 		i_cal_property_set_status (prop, status);
3414 		g_object_unref (prop);
3415 	} else {
3416 		prop = i_cal_property_new_status (status);
3417 		i_cal_component_take_property (comp->priv->icalcomp, prop);
3418 	}
3419 }
3420 
3421 /**
3422  * e_cal_component_get_summary:
3423  * @comp: A calendar component object.
3424  *
3425  * Queries the summary of a calendar component object.
3426  * Free the returned pointer withe_cal_component_text_free(),
3427  * when no longer needed.
3428  *
3429  * Returns: (transfer full) (nullable): the summary, as an #ECalComponentText,
3430  *    or %NULL, when none is set
3431  *
3432  * Since: 3.34
3433  **/
3434 ECalComponentText *
e_cal_component_get_summary(ECalComponent * comp)3435 e_cal_component_get_summary (ECalComponent *comp)
3436 {
3437 	ECalComponentText *text;
3438 	ICalProperty *prop;
3439 
3440 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
3441 	g_return_val_if_fail (comp->priv->icalcomp != NULL, NULL);
3442 
3443 	prop = i_cal_component_get_first_property (comp->priv->icalcomp, I_CAL_SUMMARY_PROPERTY);
3444 	if (!prop)
3445 		return NULL;
3446 
3447 	text = get_text_from_prop (prop, i_cal_property_get_summary);
3448 
3449 	g_object_unref (prop);
3450 
3451 	return text;
3452 }
3453 
3454 typedef struct {
3455 	gchar *old_summary;
3456 	const gchar *new_summary;
3457 } SetAlarmDescriptionData;
3458 
3459 static gboolean
set_alarm_description_cb(ICalComponent * icalcomp,ICalComponent * subcomp,gpointer user_data)3460 set_alarm_description_cb (ICalComponent *icalcomp,
3461 			  ICalComponent *subcomp,
3462 			  gpointer user_data)
3463 {
3464 	ICalProperty *icalprop, *desc_prop;
3465 	SetAlarmDescriptionData *sadd = user_data;
3466 	gboolean changed = FALSE;
3467 	const gchar *old_summary = NULL;
3468 
3469 	g_return_val_if_fail (sadd != NULL, FALSE);
3470 
3471 	/* set the new description on the alarm */
3472 	desc_prop = i_cal_component_get_first_property (subcomp, I_CAL_DESCRIPTION_PROPERTY);
3473 	if (desc_prop) {
3474 		old_summary = i_cal_property_get_description (desc_prop);
3475 	} else {
3476 		desc_prop = i_cal_property_new_description (sadd->new_summary);
3477 	}
3478 
3479 	/* remove the X-EVOLUTION-NEEDS_DESCRIPTION property */
3480 	icalprop = i_cal_component_get_first_property (subcomp, I_CAL_X_PROPERTY);
3481 	while (icalprop) {
3482 		const gchar *x_name;
3483 
3484 		x_name = i_cal_property_get_x_name (icalprop);
3485 		if (!g_strcmp0 (x_name, "X-EVOLUTION-NEEDS-DESCRIPTION")) {
3486 			i_cal_component_remove_property (subcomp, icalprop);
3487 			g_object_unref (icalprop);
3488 
3489 			i_cal_property_set_description (desc_prop, sadd->new_summary);
3490 			changed = TRUE;
3491 			break;
3492 		}
3493 
3494 		g_object_unref (icalprop);
3495 		icalprop = i_cal_component_get_next_property (subcomp, I_CAL_X_PROPERTY);
3496 	}
3497 
3498 	if (!changed) {
3499 		if (!g_strcmp0 (old_summary ? old_summary : "", sadd->old_summary ? sadd->old_summary : "")) {
3500 			i_cal_property_set_description (desc_prop, sadd->new_summary);
3501 		}
3502 	}
3503 
3504 	g_object_unref (desc_prop);
3505 
3506 	return TRUE;
3507 }
3508 
3509 /**
3510  * e_cal_component_set_summary:
3511  * @comp: A calendar component object.
3512  * @summary: Summary property and its parameters.
3513  *
3514  * Sets the summary of a calendar component object.
3515  *
3516  * Since: 3.34
3517  **/
3518 void
e_cal_component_set_summary(ECalComponent * comp,const ECalComponentText * summary)3519 e_cal_component_set_summary (ECalComponent *comp,
3520 			     const ECalComponentText *summary)
3521 {
3522 	ICalProperty *prop;
3523 	SetAlarmDescriptionData sadd;
3524 
3525 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
3526 	g_return_if_fail (comp->priv->icalcomp != NULL);
3527 
3528 	prop = i_cal_component_get_first_property (comp->priv->icalcomp, I_CAL_SUMMARY_PROPERTY);
3529 
3530 	if (!summary) {
3531 		if (prop) {
3532 			i_cal_component_remove_property (comp->priv->icalcomp, prop);
3533 			g_object_unref (prop);
3534 		}
3535 
3536 		return;
3537 	}
3538 
3539 	if (!e_cal_component_text_get_value (summary)) {
3540 		g_clear_object (&prop);
3541 		return;
3542 	}
3543 
3544 	if (prop) {
3545 		/* Make a copy, to avoid use-after-free */
3546 		sadd.old_summary = g_strdup (i_cal_property_get_summary (prop));
3547 		i_cal_property_set_summary (prop, (gchar *) e_cal_component_text_get_value (summary));
3548 		set_text_altrep_on_prop (prop, summary);
3549 	} else {
3550 		sadd.old_summary = NULL;
3551 		prop = i_cal_property_new_summary ((gchar *) e_cal_component_text_get_value (summary));
3552 		set_text_altrep_on_prop (prop, summary);
3553 		i_cal_component_take_property (comp->priv->icalcomp, prop);
3554 		prop = NULL;
3555 	}
3556 
3557 	g_clear_object (&prop);
3558 
3559 	/* look for alarms that need a description */
3560 	sadd.new_summary = e_cal_component_text_get_value (summary);
3561 
3562 	foreach_subcomponent (comp->priv->icalcomp, I_CAL_VALARM_COMPONENT, set_alarm_description_cb, &sadd);
3563 
3564 	g_free (sadd.old_summary);
3565 }
3566 
3567 /**
3568  * e_cal_component_get_transparency:
3569  * @comp: A calendar component object.
3570  *
3571  * Queries the time transparency of a calendar component object.
3572  *
3573  * Returns: the time transparency, as an #ECalComponentTransparency;
3574  *    value #E_CAL_COMPONENT_TRANSP_NONE is returned when none is set
3575  *
3576  * Since: 3.34
3577  **/
3578 ECalComponentTransparency
e_cal_component_get_transparency(ECalComponent * comp)3579 e_cal_component_get_transparency (ECalComponent *comp)
3580 {
3581 	ECalComponentTransparency transp;
3582 	ICalProperty *prop;
3583 
3584 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), E_CAL_COMPONENT_TRANSP_NONE);
3585 	g_return_val_if_fail (comp->priv->icalcomp != NULL, E_CAL_COMPONENT_TRANSP_NONE);
3586 
3587 	prop = i_cal_component_get_first_property (comp->priv->icalcomp, I_CAL_TRANSP_PROPERTY);
3588 
3589 	if (!prop)
3590 		return E_CAL_COMPONENT_TRANSP_NONE;
3591 
3592 	switch (i_cal_property_get_transp (prop)) {
3593 	case I_CAL_TRANSP_TRANSPARENT:
3594 	case I_CAL_TRANSP_TRANSPARENTNOCONFLICT:
3595 		transp = E_CAL_COMPONENT_TRANSP_TRANSPARENT;
3596 		break;
3597 
3598 	case I_CAL_TRANSP_OPAQUE:
3599 	case I_CAL_TRANSP_OPAQUENOCONFLICT:
3600 		transp = E_CAL_COMPONENT_TRANSP_OPAQUE;
3601 		break;
3602 
3603 	default:
3604 		transp = E_CAL_COMPONENT_TRANSP_UNKNOWN;
3605 		break;
3606 	}
3607 
3608 	g_object_unref (prop);
3609 
3610 	return transp;
3611 }
3612 
3613 /**
3614  * e_cal_component_set_transparency:
3615  * @comp: A calendar component object.
3616  * @transp: Time transparency value.
3617  *
3618  * Sets the time transparency of a calendar component object.
3619  * Use %E_CAL_COMPONENT_TRANSP_NONE to unset the property.
3620  *
3621  * Since: 3.34
3622  **/
3623 void
e_cal_component_set_transparency(ECalComponent * comp,ECalComponentTransparency transp)3624 e_cal_component_set_transparency (ECalComponent *comp,
3625                                   ECalComponentTransparency transp)
3626 {
3627 	ICalProperty *prop;
3628 	ICalPropertyTransp ical_transp;
3629 
3630 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
3631 	g_return_if_fail (transp != E_CAL_COMPONENT_TRANSP_UNKNOWN);
3632 	g_return_if_fail (comp->priv->icalcomp != NULL);
3633 
3634 	prop = i_cal_component_get_first_property (comp->priv->icalcomp, I_CAL_TRANSP_PROPERTY);
3635 
3636 	if (transp == E_CAL_COMPONENT_TRANSP_NONE) {
3637 		if (prop) {
3638 			i_cal_component_remove_property (comp->priv->icalcomp, prop);
3639 			g_object_unref (prop);
3640 		}
3641 
3642 		return;
3643 	}
3644 
3645 	switch (transp) {
3646 	case E_CAL_COMPONENT_TRANSP_TRANSPARENT:
3647 		ical_transp = I_CAL_TRANSP_TRANSPARENT;
3648 		break;
3649 
3650 	case E_CAL_COMPONENT_TRANSP_OPAQUE:
3651 		ical_transp = I_CAL_TRANSP_OPAQUE;
3652 		break;
3653 
3654 	default:
3655 		g_warn_if_reached ();
3656 		ical_transp = I_CAL_TRANSP_NONE;
3657 		break;
3658 	}
3659 
3660 	if (prop) {
3661 		i_cal_property_set_transp (prop, ical_transp);
3662 		g_object_unref (prop);
3663 	} else {
3664 		prop = i_cal_property_new_transp (ical_transp);
3665 		i_cal_component_take_property (comp->priv->icalcomp, prop);
3666 	}
3667 }
3668 
3669 /**
3670  * e_cal_component_get_url:
3671  * @comp: A calendar component object.
3672  *
3673  * Queries the uniform resource locator property of a calendar component object.
3674  * Free the returned URL with g_free(), when no longer needed.
3675  *
3676  * Returns: (transfer full) (nullable): the URL, or %NULL, when none is set
3677  *
3678  * Since: 3.34
3679  **/
3680 gchar *
e_cal_component_get_url(ECalComponent * comp)3681 e_cal_component_get_url (ECalComponent *comp)
3682 {
3683 	ICalProperty *prop;
3684 	gchar *url;
3685 
3686 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
3687 	g_return_val_if_fail (comp->priv->icalcomp != NULL, NULL);
3688 
3689 	prop = i_cal_component_get_first_property (comp->priv->icalcomp, I_CAL_URL_PROPERTY);
3690 	if (!prop)
3691 		return NULL;
3692 
3693 	url = g_strdup (i_cal_property_get_url (prop));
3694 
3695 	g_object_unref (prop);
3696 
3697 	return url;
3698 }
3699 
3700 /**
3701  * e_cal_component_set_url:
3702  * @comp: A calendar component object.
3703  * @url: (nullable): URL value.
3704  *
3705  * Sets the uniform resource locator property of a calendar component object.
3706  * A %NULL or an empty string removes the property.
3707  *
3708  * Since: 3.34
3709  **/
3710 void
e_cal_component_set_url(ECalComponent * comp,const gchar * url)3711 e_cal_component_set_url (ECalComponent *comp,
3712                          const gchar *url)
3713 {
3714 	ICalProperty *prop;
3715 
3716 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
3717 	g_return_if_fail (comp->priv->icalcomp != NULL);
3718 
3719 	prop = i_cal_component_get_first_property (comp->priv->icalcomp, I_CAL_URL_PROPERTY);
3720 
3721 	if (!url || !(*url)) {
3722 		if (prop) {
3723 			i_cal_component_remove_property (comp->priv->icalcomp, prop);
3724 			g_object_unref (prop);
3725 		}
3726 
3727 		return;
3728 	}
3729 
3730 	if (prop) {
3731 		i_cal_property_set_url (prop, (gchar *) url);
3732 		g_object_unref (prop);
3733 	} else {
3734 		prop = i_cal_property_new_url ((gchar *) url);
3735 		i_cal_component_take_property (comp->priv->icalcomp, prop);
3736 	}
3737 }
3738 
3739 static gboolean
get_attendee_list_cb(ICalComponent * icalcomp,ICalProperty * prop,gpointer user_data)3740 get_attendee_list_cb (ICalComponent *icalcomp,
3741 		      ICalProperty *prop,
3742 		      gpointer user_data)
3743 {
3744 	GSList **pattendees = user_data;
3745 	ECalComponentAttendee *attendee;
3746 
3747 	g_return_val_if_fail (pattendees != NULL, FALSE);
3748 
3749 	attendee = e_cal_component_attendee_new_from_property (prop);
3750 
3751 	if (attendee)
3752 		*pattendees = g_slist_prepend (*pattendees, attendee);
3753 
3754 	return TRUE;
3755 }
3756 
3757 /**
3758  * e_cal_component_get_attendees:
3759  * @comp: A calendar component object.
3760  *
3761  * Queries the attendee properties of the calendar component object.
3762  * Free the returned #GSList with g_slist_free_full (slist, e_cal_component_attendee_free);,
3763  * when no longer needed.
3764  *
3765  * Returns: (transfer full) (nullable) (element-type ECalComponentAttendee):
3766  *    the attendees, as a #GSList of an #ECalComponentAttendee, or %NULL,
3767  *    when none are set
3768  *
3769  * Since: 3.34
3770  **/
3771 GSList *
e_cal_component_get_attendees(ECalComponent * comp)3772 e_cal_component_get_attendees (ECalComponent *comp)
3773 {
3774 	GSList *attendees = NULL;
3775 
3776 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
3777 	g_return_val_if_fail (comp->priv->icalcomp != NULL, NULL);
3778 
3779 	foreach_property (comp->priv->icalcomp, I_CAL_ATTENDEE_PROPERTY, get_attendee_list_cb, &attendees);
3780 
3781 	return g_slist_reverse (attendees);
3782 }
3783 
3784 /**
3785  * e_cal_component_set_attendees:
3786  * @comp: A calendar component object.
3787  * @attendee_list: (nullable) (element-type ECalComponentAttendee): Values for attendee
3788  *    properties, or %NULL to unset
3789  *
3790  * Sets the attendees of a calendar component object
3791  *
3792  * Since: 3.34
3793  **/
3794 void
e_cal_component_set_attendees(ECalComponent * comp,const GSList * attendee_list)3795 e_cal_component_set_attendees (ECalComponent *comp,
3796 			       const GSList *attendee_list)
3797 {
3798 	GSList *link;
3799 
3800 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
3801 	g_return_if_fail (comp->priv->icalcomp != NULL);
3802 
3803 	remove_all_properties_of_kind (comp->priv->icalcomp, I_CAL_ATTENDEE_PROPERTY);
3804 
3805 	for (link = (GSList *) attendee_list; link; link = g_slist_next (link)) {
3806 		const ECalComponentAttendee *attendee = link->data;
3807 		ICalProperty *prop;
3808 
3809 		if (!attendee)
3810 			continue;
3811 
3812 		prop = e_cal_component_attendee_get_as_property (attendee);
3813 		if (!prop)
3814 			continue;
3815 
3816 		i_cal_component_take_property (comp->priv->icalcomp, prop);
3817 	}
3818 }
3819 
3820 /**
3821  * e_cal_component_has_attendees:
3822  * @comp: A calendar component object.
3823  *
3824  * Queries a calendar component object for the existence of attendees.
3825  *
3826  * Returns: TRUE if there are attendees, FALSE if not.
3827  *
3828  * Since: 3.34
3829  */
3830 gboolean
e_cal_component_has_attendees(ECalComponent * comp)3831 e_cal_component_has_attendees (ECalComponent *comp)
3832 {
3833 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
3834 
3835 	return e_cal_util_component_has_property (comp->priv->icalcomp, I_CAL_ATTENDEE_PROPERTY);
3836 }
3837 
3838 /**
3839  * e_cal_component_get_location:
3840  * @comp: A calendar component object
3841  *
3842  * Queries the location property of a calendar component object.
3843  *
3844  * Returns: (transfer full) (nullable): the locatio, or %NULL, if none is set
3845  *
3846  * Since: 3.34
3847  **/
3848 gchar *
e_cal_component_get_location(ECalComponent * comp)3849 e_cal_component_get_location (ECalComponent *comp)
3850 {
3851 	ICalProperty *prop;
3852 	gchar *location;
3853 
3854 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
3855 	g_return_val_if_fail (comp->priv->icalcomp != NULL, NULL);
3856 
3857 	prop = i_cal_component_get_first_property (comp->priv->icalcomp, I_CAL_LOCATION_PROPERTY);
3858 
3859 	if (!prop)
3860 		return NULL;
3861 
3862 	location = g_strdup (i_cal_property_get_location (prop));
3863 
3864 	g_object_unref (prop);
3865 
3866 	return location;
3867 }
3868 
3869 /**
3870  * e_cal_component_set_location:
3871  * @comp: A calendar component object.
3872  * @location: (nullable): Location value. Use %NULL or empty string, to unset the property.
3873  *
3874  * Sets the location property of a calendar component object.
3875  *
3876  * Since: 3.34
3877  **/
3878 void
e_cal_component_set_location(ECalComponent * comp,const gchar * location)3879 e_cal_component_set_location (ECalComponent *comp,
3880                               const gchar *location)
3881 {
3882 	ICalProperty *prop;
3883 
3884 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
3885 	g_return_if_fail (comp->priv->icalcomp != NULL);
3886 
3887 	prop = i_cal_component_get_first_property (comp->priv->icalcomp, I_CAL_LOCATION_PROPERTY);
3888 
3889 	if (!location || !*location) {
3890 		if (prop) {
3891 			i_cal_component_remove_property (comp->priv->icalcomp, prop);
3892 			g_object_unref (prop);
3893 		}
3894 
3895 		return;
3896 	}
3897 
3898 	if (prop) {
3899 		i_cal_property_set_location (prop, (gchar *) location);
3900 		g_object_unref (prop);
3901 	} else {
3902 		prop = i_cal_property_new_location ((gchar *) location);
3903 		i_cal_component_take_property (comp->priv->icalcomp, prop);
3904 	}
3905 }
3906 
3907 /**
3908  * e_cal_component_has_alarms:
3909  * @comp: A calendar component object.
3910  *
3911  * Checks whether the component has any alarms.
3912  *
3913  * Returns: TRUE if the component has any alarms.
3914  *
3915  * Since: 3.34
3916  **/
3917 gboolean
e_cal_component_has_alarms(ECalComponent * comp)3918 e_cal_component_has_alarms (ECalComponent *comp)
3919 {
3920 	ICalComponent *subcomp;
3921 
3922 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
3923 	g_return_val_if_fail (comp->priv->icalcomp != NULL, FALSE);
3924 
3925 	subcomp = i_cal_component_get_first_component (comp->priv->icalcomp, I_CAL_VALARM_COMPONENT);
3926 	if (subcomp) {
3927 		g_object_unref (subcomp);
3928 		return TRUE;
3929 	}
3930 
3931 	return FALSE;
3932 }
3933 
3934 static gchar *
dup_alarm_uid_from_component(ICalComponent * valarm)3935 dup_alarm_uid_from_component (ICalComponent *valarm)
3936 {
3937 	ICalProperty *prop;
3938 	gchar *auid = NULL;
3939 
3940 	g_return_val_if_fail (valarm != NULL, NULL);
3941 
3942 	for (prop = i_cal_component_get_first_property (valarm, I_CAL_X_PROPERTY);
3943 	     prop;
3944 	     g_object_unref (prop), prop = i_cal_component_get_next_property (valarm, I_CAL_X_PROPERTY)) {
3945 		const gchar *xname;
3946 
3947 		xname = i_cal_property_get_x_name (prop);
3948 
3949 		if (g_strcmp0 (xname, E_CAL_EVOLUTION_ALARM_UID_PROPERTY) == 0) {
3950 			const gchar *xvalue;
3951 
3952 			xvalue = i_cal_property_get_x (prop);
3953 			if (xvalue) {
3954 				auid = g_strdup (xvalue);
3955 				g_object_unref (prop);
3956 				break;
3957 			}
3958 		}
3959 	}
3960 
3961 	return auid;
3962 }
3963 
3964 /**
3965  * e_cal_component_add_alarm:
3966  * @comp: A calendar component.
3967  * @alarm: (transfer none): an alarm, as an #ECalComponentAlarm
3968  *
3969  * Adds an alarm subcomponent to a calendar component.  You should have created
3970  * the @alarm by using e_cal_component_alarm_new(); it is invalid to use an
3971  * #ECalComponentAlarm structure that came from e_cal_component_get_alarm().  After
3972  * adding the alarm, the @alarm structure is no longer valid because the
3973  * internal structures may change and you should get rid of it by using
3974  * e_cal_component_alarm_free().
3975  *
3976  * Since: 3.34
3977  **/
3978 void
e_cal_component_add_alarm(ECalComponent * comp,ECalComponentAlarm * alarm)3979 e_cal_component_add_alarm (ECalComponent *comp,
3980 			   ECalComponentAlarm *alarm)
3981 {
3982 	GSList *existing_uids, *link;
3983 	ICalComponent *valarm;
3984 	const gchar *auid;
3985 
3986 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
3987 	g_return_if_fail (alarm != NULL);
3988 	g_return_if_fail (comp->priv->icalcomp);
3989 
3990 	auid = e_cal_component_alarm_get_uid (alarm);
3991 
3992 	existing_uids = e_cal_component_get_alarm_uids (comp);
3993 
3994 	for (link = existing_uids; link; link = g_slist_next (link)) {
3995 		const gchar *existing_auid = link->data;
3996 
3997 		if (g_strcmp0 (auid, existing_auid) == 0)
3998 			break;
3999 	}
4000 
4001 	g_slist_free_full (existing_uids, g_free);
4002 
4003 	/* Not NULL, when found an alarm with the same UID */
4004 	if (link) {
4005 		/* This generates new UID for the alarm */
4006 		e_cal_component_alarm_set_uid (alarm, NULL);
4007 	}
4008 
4009 	valarm = e_cal_component_alarm_get_as_component (alarm);
4010 	if (valarm)
4011 		i_cal_component_take_component (comp->priv->icalcomp, valarm);
4012 }
4013 
4014 static gboolean
remove_alarm_cb(ICalComponent * icalcomp,ICalComponent * subcomp,gpointer user_data)4015 remove_alarm_cb (ICalComponent *icalcomp,
4016 		 ICalComponent *subcomp,
4017 		 gpointer user_data)
4018 {
4019 	const gchar *auid = user_data;
4020 	gchar *existing;
4021 
4022 	g_return_val_if_fail (auid != NULL, FALSE);
4023 
4024 	existing = dup_alarm_uid_from_component (subcomp);
4025 	if (g_strcmp0 (existing, auid) == 0) {
4026 		g_free (existing);
4027 		i_cal_component_remove_component (icalcomp, subcomp);
4028 		return FALSE;
4029 	}
4030 
4031 	g_free (existing);
4032 
4033 	return TRUE;
4034 }
4035 
4036 /**
4037  * e_cal_component_remove_alarm:
4038  * @comp: A calendar component.
4039  * @auid: UID of the alarm to remove.
4040  *
4041  * Removes an alarm subcomponent from a calendar component.  If the alarm that
4042  * corresponds to the specified @auid had been fetched with
4043  * e_cal_component_get_alarm(), then those alarm structures will be invalid; you
4044  * should get rid of them with e_cal_component_alarm_free() before using this
4045  * function.
4046  *
4047  * Since: 3.34
4048  **/
4049 void
e_cal_component_remove_alarm(ECalComponent * comp,const gchar * auid)4050 e_cal_component_remove_alarm (ECalComponent *comp,
4051                               const gchar *auid)
4052 {
4053 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
4054 	g_return_if_fail (auid != NULL);
4055 	g_return_if_fail (comp->priv->icalcomp != NULL);
4056 
4057 	foreach_subcomponent (comp->priv->icalcomp, I_CAL_VALARM_COMPONENT, remove_alarm_cb, (gpointer) auid);
4058 }
4059 
4060 static gboolean
remove_all_alarms_cb(ICalComponent * icalcomp,ICalComponent * subcomp,gpointer user_data)4061 remove_all_alarms_cb (ICalComponent *icalcomp,
4062 		      ICalComponent *subcomp,
4063 		      gpointer user_data)
4064 {
4065 	i_cal_component_remove_component (icalcomp, subcomp);
4066 
4067 	return TRUE;
4068 }
4069 
4070 /**
4071  * e_cal_component_remove_all_alarms:
4072  * @comp: A calendar component
4073  *
4074  * Remove all alarms from the calendar component
4075  *
4076  * Since: 3.34
4077  **/
4078 void
e_cal_component_remove_all_alarms(ECalComponent * comp)4079 e_cal_component_remove_all_alarms (ECalComponent *comp)
4080 {
4081 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
4082 	g_return_if_fail (comp->priv->icalcomp != NULL);
4083 
4084 	foreach_subcomponent (comp->priv->icalcomp, I_CAL_VALARM_COMPONENT, remove_all_alarms_cb, NULL);
4085 }
4086 
4087 static gboolean
get_alarm_uids_cb(ICalComponent * icalcomp,ICalComponent * subcomp,gpointer user_data)4088 get_alarm_uids_cb (ICalComponent *icalcomp,
4089 		   ICalComponent *subcomp,
4090 		   gpointer user_data)
4091 {
4092 	GSList **puids = user_data;
4093 	gchar *auid;
4094 
4095 	g_return_val_if_fail (puids != NULL, FALSE);
4096 
4097 	auid = dup_alarm_uid_from_component (subcomp);
4098 	if (auid)
4099 		*puids = g_slist_prepend (*puids, auid);
4100 
4101 	return TRUE;
4102 }
4103 
4104 /**
4105  * e_cal_component_get_alarm_uids:
4106  * @comp: A calendar component.
4107  *
4108  * Builds a list of the unique identifiers of the alarm subcomponents inside a
4109  * calendar component. Free the returned #GSList with
4110  * g_slist_free_full (slist, g_free);, when no longer needed.
4111  *
4112  * Returns: (transfer full) (nullable) (element-type utf8): a #GSList of unique
4113  *    identifiers for alarms.
4114  *
4115  * Since: 3.34
4116  **/
4117 GSList *
e_cal_component_get_alarm_uids(ECalComponent * comp)4118 e_cal_component_get_alarm_uids (ECalComponent *comp)
4119 {
4120 	GSList *uids = NULL;
4121 
4122 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
4123 	g_return_val_if_fail (comp->priv->icalcomp != NULL, NULL);
4124 
4125 	foreach_subcomponent (comp->priv->icalcomp, I_CAL_VALARM_COMPONENT, get_alarm_uids_cb, &uids);
4126 
4127 	return g_slist_reverse (uids);
4128 }
4129 
4130 struct GetAlarmData {
4131 	const gchar *auid;
4132 	ICalComponent *valarm;
4133 };
4134 
4135 static gboolean
get_alarm_cb(ICalComponent * icalcomp,ICalComponent * subcomp,gpointer user_data)4136 get_alarm_cb (ICalComponent *icalcomp,
4137 	      ICalComponent *subcomp,
4138 	      gpointer user_data)
4139 {
4140 	struct GetAlarmData *gad = user_data;
4141 	gchar *auid;
4142 
4143 	g_return_val_if_fail (gad != NULL, FALSE);
4144 
4145 	auid = dup_alarm_uid_from_component (subcomp);
4146 	if (g_strcmp0 (auid, gad->auid) == 0) {
4147 		gad->valarm = g_object_ref (subcomp);
4148 	}
4149 
4150 	g_free (auid);
4151 
4152 	return !gad->valarm;
4153 }
4154 
4155 /**
4156  * e_cal_component_get_alarm:
4157  * @comp: A calendar component.
4158  * @auid: Unique identifier for the sought alarm subcomponent.
4159  *
4160  * Queries a particular alarm subcomponent of a calendar component.
4161  * Free the returned pointer with e_cal_component_alarm_free(),
4162  * when no longer needed.
4163  *
4164  * Returns: (transfer full) (nullable): the alarm subcomponent that corresponds
4165  *    to the specified @auid, or %NULL if no alarm exists with that UID
4166  *
4167  * Since: 3.34
4168  **/
4169 ECalComponentAlarm *
e_cal_component_get_alarm(ECalComponent * comp,const gchar * auid)4170 e_cal_component_get_alarm (ECalComponent *comp,
4171                            const gchar *auid)
4172 {
4173 	ECalComponentAlarm *alarm = NULL;
4174 	struct GetAlarmData gad;
4175 
4176 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
4177 	g_return_val_if_fail (comp->priv->icalcomp != NULL, NULL);
4178 	g_return_val_if_fail (auid != NULL, NULL);
4179 
4180 	gad.auid = auid;
4181 	gad.valarm = NULL;
4182 
4183 	foreach_subcomponent (comp->priv->icalcomp, I_CAL_VALARM_COMPONENT, get_alarm_cb, &gad);
4184 
4185 	if (gad.valarm) {
4186 		alarm = e_cal_component_alarm_new_from_component (gad.valarm);
4187 		g_object_unref (gad.valarm);
4188 	}
4189 
4190 	return alarm;
4191 }
4192 
4193 static gboolean
get_all_alarms_cb(ICalComponent * icalcomp,ICalComponent * subcomp,gpointer user_data)4194 get_all_alarms_cb (ICalComponent *icalcomp,
4195 		   ICalComponent *subcomp,
4196 		   gpointer user_data)
4197 {
4198 	GSList **palarms = user_data;
4199 	ECalComponentAlarm *alarm;
4200 
4201 	g_return_val_if_fail (palarms != NULL, FALSE);
4202 
4203 	alarm = e_cal_component_alarm_new_from_component (subcomp);
4204 	if (alarm)
4205 		*palarms = g_slist_prepend (*palarms, alarm);
4206 
4207 	return TRUE;
4208 }
4209 
4210 /**
4211  * e_cal_component_get_all_alarms:
4212  * @comp: A calendar component.
4213  *
4214  * Queries all alarm subcomponents of a calendar component.
4215  * Free the returned #GSList with g_slist_free_full (slist, e_cal_component_alarm_free);,
4216  * when no longer needed.
4217  *
4218  * Returns: (transfer full) (nullable) (element-type ECalComponentAlarm): the alarm subcomponents
4219  *    as a #GSList of #ECalComponentAlarm, or %NULL, if no alarm exists
4220  *
4221  * Since: 3.34
4222  **/
4223 GSList *
e_cal_component_get_all_alarms(ECalComponent * comp)4224 e_cal_component_get_all_alarms (ECalComponent *comp)
4225 {
4226 	GSList *alarms = NULL;
4227 
4228 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
4229 	g_return_val_if_fail (comp->priv->icalcomp != NULL, NULL);
4230 
4231 	foreach_subcomponent (comp->priv->icalcomp, I_CAL_VALARM_COMPONENT, get_all_alarms_cb, &alarms);
4232 
4233 	return g_slist_reverse (alarms);
4234 }
4235