No issues found
Tool | Failure ID | Location | Function | Message | Data |
---|---|---|---|---|---|
clang-analyzer | no-output-found | e-meeting-store.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None | |
clang-analyzer | no-output-found | e-meeting-store.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None |
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) version 3.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with the program; if not, see <http://www.gnu.org/licenses/>
14 *
15 *
16 * Authors:
17 * JP Rosevear <jpr@ximian.com>
18 * Mike Kestner <mkestner@ximian.com>
19 *
20 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
21 *
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <gio/gio.h>
29 #include <glib/gi18n.h>
30 #include <libsoup/soup.h>
31
32 #include <libecal/libecal.h>
33 #include <libebackend/libebackend.h>
34 #include <libedataserverui/libedataserverui.h>
35
36 #include <shell/e-shell.h>
37 #include <e-util/e-util-enumtypes.h>
38
39 #include "itip-utils.h"
40 #include "e-meeting-utils.h"
41 #include "e-meeting-attendee.h"
42 #include "e-meeting-store.h"
43
44 #define ROW_VALID(store, row) \
45 (row >= 0 && row < store->priv->attendees->len)
46
47 #define E_MEETING_STORE_GET_PRIVATE(obj) \
48 (G_TYPE_INSTANCE_GET_PRIVATE \
49 ((obj), E_TYPE_MEETING_STORE, EMeetingStorePrivate))
50
51 struct _EMeetingStorePrivate {
52 GPtrArray *attendees;
53 gint stamp;
54
55 ECalClient *client;
56 icaltimezone *zone;
57
58 gint default_reminder_interval;
59 EDurationType default_reminder_units;
60
61 gint week_start_day;
62
63 gchar *fb_uri;
64
65 GPtrArray *refresh_queue;
66 GHashTable *refresh_data;
67 GMutex *mutex;
68 guint refresh_idle_id;
69
70 guint num_threads;
71 guint num_queries;
72 };
73
74 #define BUF_SIZE 1024
75
76 typedef struct _EMeetingStoreQueueData EMeetingStoreQueueData;
77 struct _EMeetingStoreQueueData {
78 EMeetingStore *store;
79 EMeetingAttendee *attendee;
80
81 gboolean refreshing;
82
83 EMeetingTime start;
84 EMeetingTime end;
85
86 gchar buffer[BUF_SIZE];
87 GString *string;
88
89 GPtrArray *call_backs;
90 GPtrArray *data;
91 };
92
93 enum {
94 PROP_0,
95 PROP_CLIENT,
96 PROP_DEFAULT_REMINDER_INTERVAL,
97 PROP_DEFAULT_REMINDER_UNITS,
98 PROP_FREE_BUSY_TEMPLATE,
99 PROP_TIMEZONE,
100 PROP_WEEK_START_DAY
101 };
102
103 /* Forward Declarations */
104 static void ems_tree_model_init (GtkTreeModelIface *iface);
105
106 G_DEFINE_TYPE_WITH_CODE (
107 EMeetingStore, e_meeting_store, GTK_TYPE_LIST_STORE,
108 G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL)
109 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, ems_tree_model_init))
110
111 static icalparameter_cutype
112 text_to_type (const gchar *type)
113 {
114 if (!e_util_utf8_strcasecmp (type, _("Individual")))
115 return ICAL_CUTYPE_INDIVIDUAL;
116 else if (!e_util_utf8_strcasecmp (type, _("Group")))
117 return ICAL_CUTYPE_GROUP;
118 else if (!e_util_utf8_strcasecmp (type, _("Resource")))
119 return ICAL_CUTYPE_RESOURCE;
120 else if (!e_util_utf8_strcasecmp (type, _("Room")))
121 return ICAL_CUTYPE_ROOM;
122 else
123 return ICAL_CUTYPE_NONE;
124 }
125
126 static gchar *
127 type_to_text (icalparameter_cutype type)
128 {
129 switch (type) {
130 case ICAL_CUTYPE_INDIVIDUAL:
131 return _("Individual");
132 case ICAL_CUTYPE_GROUP:
133 return _("Group");
134 case ICAL_CUTYPE_RESOURCE:
135 return _("Resource");
136 case ICAL_CUTYPE_ROOM:
137 return _("Room");
138 default:
139 return _("Unknown");
140 }
141
142 return NULL;
143
144 }
145
146 static icalparameter_role
147 text_to_role (const gchar *role)
148 {
149 if (!e_util_utf8_strcasecmp (role, _("Chair")))
150 return ICAL_ROLE_CHAIR;
151 else if (!e_util_utf8_strcasecmp (role, _("Required Participant")))
152 return ICAL_ROLE_REQPARTICIPANT;
153 else if (!e_util_utf8_strcasecmp (role, _("Optional Participant")))
154 return ICAL_ROLE_OPTPARTICIPANT;
155 else if (!e_util_utf8_strcasecmp (role, _("Non-Participant")))
156 return ICAL_ROLE_NONPARTICIPANT;
157 else
158 return ICAL_ROLE_NONE;
159 }
160
161 static gchar *
162 role_to_text (icalparameter_role role)
163 {
164 switch (role) {
165 case ICAL_ROLE_CHAIR:
166 return _("Chair");
167 case ICAL_ROLE_REQPARTICIPANT:
168 return _("Required Participant");
169 case ICAL_ROLE_OPTPARTICIPANT:
170 return _("Optional Participant");
171 case ICAL_ROLE_NONPARTICIPANT:
172 return _("Non-Participant");
173 default:
174 return _("Unknown");
175 }
176 }
177
178 static gboolean
179 text_to_boolean (const gchar *role)
180 {
181 if (!e_util_utf8_strcasecmp (role, _("Yes")))
182 return TRUE;
183 else
184 return FALSE;
185 }
186
187 static gchar *
188 boolean_to_text (gboolean b)
189 {
190 if (b)
191 return _("Yes");
192 else
193 return _("No");
194 }
195
196 static icalparameter_partstat
197 text_to_partstat (const gchar *partstat)
198 {
199 if (!e_util_utf8_strcasecmp (partstat, _("Needs Action")))
200 return ICAL_PARTSTAT_NEEDSACTION;
201 else if (!e_util_utf8_strcasecmp (partstat, _("Accepted")))
202 return ICAL_PARTSTAT_ACCEPTED;
203 else if (!e_util_utf8_strcasecmp (partstat, _("Declined")))
204 return ICAL_PARTSTAT_DECLINED;
205 else if (!e_util_utf8_strcasecmp (partstat, _("Tentative")))
206 return ICAL_PARTSTAT_TENTATIVE;
207 else if (!e_util_utf8_strcasecmp (partstat, _("Delegated")))
208 return ICAL_PARTSTAT_DELEGATED;
209 else if (!e_util_utf8_strcasecmp (partstat, _("Completed")))
210 return ICAL_PARTSTAT_COMPLETED;
211 else if (!e_util_utf8_strcasecmp (partstat, _("In Process")))
212 return ICAL_PARTSTAT_INPROCESS;
213 else
214 return ICAL_PARTSTAT_NONE;
215 }
216
217 static gchar *
218 partstat_to_text (icalparameter_partstat partstat)
219 {
220 switch (partstat) {
221 case ICAL_PARTSTAT_NEEDSACTION:
222 return _("Needs Action");
223 case ICAL_PARTSTAT_ACCEPTED:
224 return _("Accepted");
225 case ICAL_PARTSTAT_DECLINED:
226 return _("Declined");
227 case ICAL_PARTSTAT_TENTATIVE:
228 return _("Tentative");
229 case ICAL_PARTSTAT_DELEGATED:
230 return _("Delegated");
231 case ICAL_PARTSTAT_COMPLETED:
232 return _("Completed");
233 case ICAL_PARTSTAT_INPROCESS:
234 return _("In Process");
235 case ICAL_PARTSTAT_NONE:
236 default:
237 return _("Unknown");
238 }
239 }
240
241 static GtkTreeModelFlags
242 get_flags (GtkTreeModel *model)
243 {
244 g_return_val_if_fail (E_IS_MEETING_STORE (model), 0);
245
246 return GTK_TREE_MODEL_ITERS_PERSIST | GTK_TREE_MODEL_LIST_ONLY;
247 }
248
249 static gint
250 get_n_columns (GtkTreeModel *model)
251 {
252 g_return_val_if_fail (E_IS_MEETING_STORE (model), 0);
253
254 return E_MEETING_STORE_COLUMN_COUNT;
255 }
256
257 static GType
258 get_column_type (GtkTreeModel *model,
259 gint col)
260 {
261 g_return_val_if_fail (E_IS_MEETING_STORE (model), G_TYPE_INVALID);
262
263 switch (col) {
264 case E_MEETING_STORE_ADDRESS_COL:
265 case E_MEETING_STORE_MEMBER_COL:
266 case E_MEETING_STORE_TYPE_COL:
267 case E_MEETING_STORE_ROLE_COL:
268 case E_MEETING_STORE_RSVP_COL:
269 case E_MEETING_STORE_DELTO_COL:
270 case E_MEETING_STORE_DELFROM_COL:
271 case E_MEETING_STORE_STATUS_COL:
272 case E_MEETING_STORE_CN_COL:
273 case E_MEETING_STORE_LANGUAGE_COL:
274 case E_MEETING_STORE_ATTENDEE_COL:
275 return G_TYPE_STRING;
276 case E_MEETING_STORE_ATTENDEE_UNDERLINE_COL:
277 return PANGO_TYPE_UNDERLINE;
278 default:
279 return G_TYPE_INVALID;
280 }
281 }
282
283 static gboolean
284 get_iter (GtkTreeModel *model,
285 GtkTreeIter *iter,
286 GtkTreePath *path)
287 {
288 gint row;
289
290 g_return_val_if_fail (E_IS_MEETING_STORE (model), FALSE);
291 g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE);
292
293 row = gtk_tree_path_get_indices (path)[0];
294
295 if (!ROW_VALID (E_MEETING_STORE (model), row))
296 return FALSE;
297
298 iter->stamp = E_MEETING_STORE (model)->priv->stamp;
299 iter->user_data = GINT_TO_POINTER (row);
300
301 return TRUE;
302 }
303
304 static GtkTreePath *
305 get_path (GtkTreeModel *model,
306 GtkTreeIter *iter)
307 {
308 gint row;
309 GtkTreePath *result;
310
311 g_return_val_if_fail (E_IS_MEETING_STORE (model), NULL);
312 g_return_val_if_fail (iter->stamp == E_MEETING_STORE (model)->priv->stamp, NULL);
313
314 row = GPOINTER_TO_INT (iter->user_data);
315
316 g_return_val_if_fail (ROW_VALID (E_MEETING_STORE (model), row), NULL);
317
318 result = gtk_tree_path_new ();
319 gtk_tree_path_append_index (result, row);
320 return result;
321 }
322
323 static void
324 get_value (GtkTreeModel *model,
325 GtkTreeIter *iter,
326 gint col,
327 GValue *value)
328 {
329 EMeetingStore *store;
330 EMeetingAttendee *attendee;
331 const gchar *cn;
332 gint row;
333
334 g_return_if_fail (E_IS_MEETING_STORE (model));
335 g_return_if_fail (col >= 0 && col < E_MEETING_STORE_COLUMN_COUNT);
336
337 row = GPOINTER_TO_INT (iter->user_data);
338 store = E_MEETING_STORE (model);
339
340 g_return_if_fail (iter->stamp == store->priv->stamp);
341 g_return_if_fail (ROW_VALID (E_MEETING_STORE (model), row));
342
343 attendee = g_ptr_array_index (store->priv->attendees, row);
344
345 switch (col) {
346 case E_MEETING_STORE_ADDRESS_COL:
347 g_value_init (value, G_TYPE_STRING);
348 g_value_set_string (
349 value, itip_strip_mailto (
350 e_meeting_attendee_get_address (attendee)));
351 break;
352 case E_MEETING_STORE_MEMBER_COL:
353 g_value_init (value, G_TYPE_STRING);
354 g_value_set_string (
355 value, e_meeting_attendee_get_member (attendee));
356 break;
357 case E_MEETING_STORE_TYPE_COL:
358 g_value_init (value, G_TYPE_STRING);
359 g_value_set_string (
360 value, type_to_text (
361 e_meeting_attendee_get_cutype (attendee)));
362 break;
363 case E_MEETING_STORE_ROLE_COL:
364 g_value_init (value, G_TYPE_STRING);
365 g_value_set_string (
366 value, role_to_text (
367 e_meeting_attendee_get_role (attendee)));
368 break;
369 case E_MEETING_STORE_RSVP_COL:
370 g_value_init (value, G_TYPE_STRING);
371 g_value_set_string (
372 value, boolean_to_text (
373 e_meeting_attendee_get_rsvp (attendee)));
374 break;
375 case E_MEETING_STORE_DELTO_COL:
376 g_value_init (value, G_TYPE_STRING);
377 g_value_set_string (
378 value, itip_strip_mailto (
379 e_meeting_attendee_get_delto (attendee)));
380 break;
381 case E_MEETING_STORE_DELFROM_COL:
382 g_value_init (value, G_TYPE_STRING);
383 g_value_set_string (
384 value, itip_strip_mailto (
385 e_meeting_attendee_get_delfrom (attendee)));
386 break;
387 case E_MEETING_STORE_STATUS_COL:
388 g_value_init (value, G_TYPE_STRING);
389 g_value_set_string (
390 value, partstat_to_text (
391 e_meeting_attendee_get_status (attendee)));
392 break;
393 case E_MEETING_STORE_CN_COL:
394 g_value_init (value, G_TYPE_STRING);
395 g_value_set_string (
396 value, e_meeting_attendee_get_cn (attendee));
397 break;
398 case E_MEETING_STORE_LANGUAGE_COL:
399 g_value_init (value, G_TYPE_STRING);
400 g_value_set_string (
401 value, e_meeting_attendee_get_language (attendee));
402 break;
403 case E_MEETING_STORE_ATTENDEE_COL:
404 g_value_init (value, G_TYPE_STRING);
405 cn = e_meeting_attendee_get_cn (attendee);
406 if (strcmp (cn, ""))
407 g_value_set_string (value, cn);
408 else
409 g_value_set_string (
410 value, itip_strip_mailto (
411 e_meeting_attendee_get_address (attendee)));
412 break;
413 case E_MEETING_STORE_ATTENDEE_UNDERLINE_COL:
414 cn = e_meeting_attendee_get_cn (attendee);
415 g_value_init (value, PANGO_TYPE_UNDERLINE);
416 g_value_set_enum (
417 value, strcmp ("", cn) == 0 ?
418 PANGO_UNDERLINE_NONE : PANGO_UNDERLINE_SINGLE);
419 }
420 }
421
422 static gboolean
423 iter_next (GtkTreeModel *model,
424 GtkTreeIter *iter)
425 {
426 gint row;
427
428 g_return_val_if_fail (E_IS_MEETING_STORE (model), FALSE);
429 g_return_val_if_fail (iter->stamp == E_MEETING_STORE (model)->priv->stamp, FALSE);
430
431 row = GPOINTER_TO_INT (iter->user_data) + 1;
432 iter->user_data = GINT_TO_POINTER (row);
433
434 return ROW_VALID (E_MEETING_STORE (model), row);
435 }
436
437 static gboolean
438 iter_children (GtkTreeModel *model,
439 GtkTreeIter *iter,
440 GtkTreeIter *parent)
441 {
442 EMeetingStore *store;
443
444 g_return_val_if_fail (E_IS_MEETING_STORE (model), FALSE);
445
446 store = E_MEETING_STORE (model);
447
448 if (parent || store->priv->attendees->len <= 0)
449 return FALSE;
450
451 iter->stamp = store->priv->stamp;
452 iter->user_data = GINT_TO_POINTER (0);
453
454 return TRUE;
455 }
456
457 static gboolean
458 iter_has_child (GtkTreeModel *model,
459 GtkTreeIter *iter)
460 {
461 return FALSE;
462 }
463
464 static gint
465 iter_n_children (GtkTreeModel *model,
466 GtkTreeIter *iter)
467 {
468 g_return_val_if_fail (E_IS_MEETING_STORE (model), -1);
469
470 if (!iter)
471 return E_MEETING_STORE (model)->priv->attendees->len;
472
473 g_return_val_if_fail (iter->stamp == E_MEETING_STORE (model)->priv->stamp, -1);
474
475 return 0;
476 }
477
478 static gboolean
479 iter_nth_child (GtkTreeModel *model,
480 GtkTreeIter *iter,
481 GtkTreeIter *parent,
482 gint n)
483 {
484 EMeetingStore *store;
485
486 g_return_val_if_fail (E_IS_MEETING_STORE (model), FALSE);
487
488 store = E_MEETING_STORE (model);
489
490 if (parent || !ROW_VALID (store, n))
491 return FALSE;
492
493 iter->stamp = store->priv->stamp;
494 iter->user_data = GINT_TO_POINTER (n);
495
496 return TRUE;
497 }
498
499 static gboolean
500 iter_parent (GtkTreeModel *model,
501 GtkTreeIter *iter,
502 GtkTreeIter *child)
503 {
504 return FALSE;
505 }
506
507 static void
508 ems_tree_model_init (GtkTreeModelIface *iface)
509 {
510 iface->get_flags = get_flags;
511 iface->get_n_columns = get_n_columns;
512 iface->get_column_type = get_column_type;
513 iface->get_iter = get_iter;
514 iface->get_path = get_path;
515 iface->get_value = get_value;
516 iface->iter_next = iter_next;
517 iface->iter_children = iter_children;
518 iface->iter_has_child = iter_has_child;
519 iface->iter_n_children = iter_n_children;
520 iface->iter_nth_child = iter_nth_child;
521 iface->iter_parent = iter_parent;
522 }
523
524 void
525 e_meeting_store_set_value (EMeetingStore *store,
526 gint row,
527 gint col,
528 const gchar *val)
529 {
530 icalparameter_cutype type;
531 EMeetingAttendee *attendee = g_ptr_array_index (store->priv->attendees, row);
532
533 switch (col) {
534 case E_MEETING_STORE_ADDRESS_COL:
535 if (val != NULL && *((gchar *) val))
536 e_meeting_attendee_set_address (
537 attendee, g_strdup_printf (
538 "MAILTO:%s", (gchar *) val));
539 break;
540 case E_MEETING_STORE_MEMBER_COL:
541 e_meeting_attendee_set_member (attendee, g_strdup (val));
542 break;
543 case E_MEETING_STORE_TYPE_COL:
544 type = text_to_type (val);
545 e_meeting_attendee_set_cutype (attendee, text_to_type (val));
546 if (type == ICAL_CUTYPE_RESOURCE) {
547 e_meeting_attendee_set_role (attendee, ICAL_ROLE_NONPARTICIPANT);
548 }
549 break;
550 case E_MEETING_STORE_ROLE_COL:
551 e_meeting_attendee_set_role (attendee, text_to_role (val));
552 break;
553 case E_MEETING_STORE_RSVP_COL:
554 e_meeting_attendee_set_rsvp (attendee, text_to_boolean (val));
555 break;
556 case E_MEETING_STORE_DELTO_COL:
557 e_meeting_attendee_set_delto (attendee, g_strdup (val));
558 break;
559 case E_MEETING_STORE_DELFROM_COL:
560 e_meeting_attendee_set_delfrom (attendee, g_strdup (val));
561 break;
562 case E_MEETING_STORE_STATUS_COL:
563 e_meeting_attendee_set_status (attendee, text_to_partstat (val));
564 break;
565 case E_MEETING_STORE_CN_COL:
566 e_meeting_attendee_set_cn (attendee, g_strdup (val));
567 break;
568 case E_MEETING_STORE_LANGUAGE_COL:
569 e_meeting_attendee_set_language (attendee, g_strdup (val));
570 break;
571 }
572 }
573
574 struct FindAttendeeData
575 {
576 EMeetingAttendee *find;
577 EMeetingStoreQueueData *qdata;
578 };
579
580 static void
581 find_attendee_cb (gpointer key,
582 gpointer value,
583 gpointer user_data)
584 {
585 EMeetingStoreQueueData *qdata = value;
586 struct FindAttendeeData *fad = user_data;
587
588 g_return_if_fail (qdata != NULL);
589 g_return_if_fail (fad != NULL);
590
591 if (qdata->attendee == fad->find)
592 fad->qdata = qdata;
593 }
594
595 static void
596 refresh_queue_remove (EMeetingStore *store,
597 EMeetingAttendee *attendee)
598 {
599 EMeetingStorePrivate *priv;
600 EMeetingStoreQueueData *qdata;
601
602 priv = store->priv;
603
604 /* Free the queue data */
605 qdata = g_hash_table_lookup (
606 priv->refresh_data, itip_strip_mailto (
607 e_meeting_attendee_get_address (attendee)));
608 if (!qdata) {
609 struct FindAttendeeData fad = { 0 };
610
611 fad.find = attendee;
612 fad.qdata = NULL;
613
614 g_hash_table_foreach (priv->refresh_data, find_attendee_cb, &fad);
615
616 qdata = fad.qdata;
617 }
618
619 if (qdata) {
620 g_mutex_lock (priv->mutex);
621 g_hash_table_remove (
622 priv->refresh_data, itip_strip_mailto (
623 e_meeting_attendee_get_address (attendee)));
624 g_mutex_unlock (priv->mutex);
625 g_ptr_array_free (qdata->call_backs, TRUE);
626 g_ptr_array_free (qdata->data, TRUE);
627 g_free (qdata);
628 }
629
630 /* Unref the attendee */
631 g_ptr_array_remove (priv->refresh_queue, attendee);
632 g_object_unref (attendee);
633 }
634
635 static void
636 meeting_store_set_property (GObject *object,
637 guint property_id,
638 const GValue *value,
639 GParamSpec *pspec)
640 {
641 switch (property_id) {
642 case PROP_CLIENT:
643 e_meeting_store_set_client (
644 E_MEETING_STORE (object),
645 g_value_get_object (value));
646 return;
647
648 case PROP_DEFAULT_REMINDER_INTERVAL:
649 e_meeting_store_set_default_reminder_interval (
650 E_MEETING_STORE (object),
651 g_value_get_int (value));
652 return;
653
654 case PROP_DEFAULT_REMINDER_UNITS:
655 e_meeting_store_set_default_reminder_units (
656 E_MEETING_STORE (object),
657 g_value_get_enum (value));
658 return;
659
660 case PROP_FREE_BUSY_TEMPLATE:
661 e_meeting_store_set_free_busy_template (
662 E_MEETING_STORE (object),
663 g_value_get_string (value));
664 return;
665
666 case PROP_TIMEZONE:
667 e_meeting_store_set_timezone (
668 E_MEETING_STORE (object),
669 g_value_get_pointer (value));
670 return;
671
672 case PROP_WEEK_START_DAY:
673 e_meeting_store_set_week_start_day (
674 E_MEETING_STORE (object),
675 g_value_get_int (value));
676 return;
677 }
678
679 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
680 }
681
682 static void
683 meeting_store_get_property (GObject *object,
684 guint property_id,
685 GValue *value,
686 GParamSpec *pspec)
687 {
688 switch (property_id) {
689 case PROP_CLIENT:
690 g_value_set_object (
691 value,
692 e_meeting_store_get_client (
693 E_MEETING_STORE (object)));
694 return;
695
696 case PROP_DEFAULT_REMINDER_INTERVAL:
697 g_value_set_int (
698 value,
699 e_meeting_store_get_default_reminder_interval (
700 E_MEETING_STORE (object)));
701 return;
702
703 case PROP_DEFAULT_REMINDER_UNITS:
704 g_value_set_enum (
705 value,
706 e_meeting_store_get_default_reminder_units (
707 E_MEETING_STORE (object)));
708 return;
709
710 case PROP_FREE_BUSY_TEMPLATE:
711 g_value_set_string (
712 value,
713 e_meeting_store_get_free_busy_template (
714 E_MEETING_STORE (object)));
715 return;
716
717 case PROP_TIMEZONE:
718 g_value_set_pointer (
719 value,
720 e_meeting_store_get_timezone (
721 E_MEETING_STORE (object)));
722 return;
723
724 case PROP_WEEK_START_DAY:
725 g_value_set_int (
726 value,
727 e_meeting_store_get_week_start_day (
728 E_MEETING_STORE (object)));
729 return;
730 }
731
732 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
733 }
734
735 static void
736 meeting_store_finalize (GObject *object)
737 {
738 EMeetingStorePrivate *priv;
739 gint i;
740
741 priv = E_MEETING_STORE_GET_PRIVATE (object);
742
743 for (i = 0; i < priv->attendees->len; i++)
744 g_object_unref (g_ptr_array_index (priv->attendees, i));
745 g_ptr_array_free (priv->attendees, TRUE);
746
747 if (priv->client != NULL)
748 g_object_unref (priv->client);
749
750 while (priv->refresh_queue->len > 0)
751 refresh_queue_remove (
752 E_MEETING_STORE (object),
753 g_ptr_array_index (priv->refresh_queue, 0));
754 g_ptr_array_free (priv->refresh_queue, TRUE);
755 g_hash_table_destroy (priv->refresh_data);
756
757 if (priv->refresh_idle_id)
758 g_source_remove (priv->refresh_idle_id);
759
760 g_free (priv->fb_uri);
761
762 g_mutex_free (priv->mutex);
763
764 /* Chain up to parent's finalize() method. */
765 G_OBJECT_CLASS (e_meeting_store_parent_class)->finalize (object);
766 }
767
768 static void
769 e_meeting_store_class_init (EMeetingStoreClass *class)
770 {
771 GObjectClass *object_class;
772
773 g_type_class_add_private (class, sizeof (EMeetingStorePrivate));
774
775 object_class = G_OBJECT_CLASS (class);
776 object_class->set_property = meeting_store_set_property;
777 object_class->get_property = meeting_store_get_property;
778 object_class->finalize = meeting_store_finalize;
779
780 g_object_class_install_property (
781 object_class,
782 PROP_CLIENT,
783 g_param_spec_object (
784 "client",
785 "ECalClient",
786 NULL,
787 E_TYPE_CAL_CLIENT,
788 G_PARAM_READWRITE));
789
790 g_object_class_install_property (
791 object_class,
792 PROP_DEFAULT_REMINDER_INTERVAL,
793 g_param_spec_int (
794 "default-reminder-interval",
795 "Default Reminder Interval",
796 NULL,
797 G_MININT,
798 G_MAXINT,
799 0,
800 G_PARAM_READWRITE));
801
802 g_object_class_install_property (
803 object_class,
804 PROP_DEFAULT_REMINDER_UNITS,
805 g_param_spec_enum (
806 "default-reminder-units",
807 "Default Reminder Units",
808 NULL,
809 E_TYPE_DURATION_TYPE,
810 E_DURATION_MINUTES,
811 G_PARAM_READWRITE));
812
813 g_object_class_install_property (
814 object_class,
815 PROP_FREE_BUSY_TEMPLATE,
816 g_param_spec_string (
817 "free-busy-template",
818 "Free/Busy Template",
819 NULL,
820 NULL,
821 G_PARAM_READWRITE));
822
823 g_object_class_install_property (
824 object_class,
825 PROP_TIMEZONE,
826 g_param_spec_pointer (
827 "timezone",
828 "Timezone",
829 NULL,
830 G_PARAM_READWRITE));
831
832 g_object_class_install_property (
833 object_class,
834 PROP_WEEK_START_DAY,
835 g_param_spec_int (
836 "week-start-day",
837 "Week Start Day",
838 NULL,
839 0, /* Monday */
840 6, /* Sunday */
841 0,
842 G_PARAM_READWRITE));
843 }
844
845 static void
846 e_meeting_store_init (EMeetingStore *store)
847 {
848 store->priv = E_MEETING_STORE_GET_PRIVATE (store);
849
850 store->priv->attendees = g_ptr_array_new ();
851 store->priv->refresh_queue = g_ptr_array_new ();
852 store->priv->refresh_data = g_hash_table_new_full (
853 g_str_hash, g_str_equal, g_free, NULL);
854
855 store->priv->mutex = g_mutex_new ();
856
857 store->priv->num_queries = 0;
858
859 e_extensible_load_extensions (E_EXTENSIBLE (store));
860 }
861
862 GObject *
863 e_meeting_store_new (void)
864 {
865 return g_object_new (E_TYPE_MEETING_STORE, NULL);
866 }
867
868 ECalClient *
869 e_meeting_store_get_client (EMeetingStore *store)
870 {
871 g_return_val_if_fail (E_IS_MEETING_STORE (store), NULL);
872
873 return store->priv->client;
874 }
875
876 void
877 e_meeting_store_set_client (EMeetingStore *store,
878 ECalClient *client)
879 {
880 g_return_if_fail (E_IS_MEETING_STORE (store));
881
882 if (store->priv->client == client)
883 return;
884
885 if (client != NULL) {
886 g_return_if_fail (E_IS_CAL_CLIENT (client));
887 g_object_ref (client);
888 }
889
890 if (store->priv->client != NULL)
891 g_object_unref (store->priv->client);
892
893 store->priv->client = client;
894
895 g_object_notify (G_OBJECT (store), "client");
896 }
897
898 gint
899 e_meeting_store_get_default_reminder_interval (EMeetingStore *store)
900 {
901 g_return_val_if_fail (E_IS_MEETING_STORE (store), 0);
902
903 return store->priv->default_reminder_interval;
904 }
905
906 void
907 e_meeting_store_set_default_reminder_interval (EMeetingStore *store,
908 gint default_reminder_interval)
909 {
910 g_return_if_fail (E_IS_MEETING_STORE (store));
911
912 if (store->priv->default_reminder_interval == default_reminder_interval)
913 return;
914
915 store->priv->default_reminder_interval = default_reminder_interval;
916
917 g_object_notify (G_OBJECT (store), "default-reminder-interval");
918 }
919
920 EDurationType
921 e_meeting_store_get_default_reminder_units (EMeetingStore *store)
922 {
923 g_return_val_if_fail (E_IS_MEETING_STORE (store), 0);
924
925 return store->priv->default_reminder_units;
926 }
927
928 void
929 e_meeting_store_set_default_reminder_units (EMeetingStore *store,
930 EDurationType default_reminder_units)
931 {
932 g_return_if_fail (E_IS_MEETING_STORE (store));
933
934 if (store->priv->default_reminder_units == default_reminder_units)
935 return;
936
937 store->priv->default_reminder_units = default_reminder_units;
938
939 g_object_notify (G_OBJECT (store), "default-reminder-units");
940 }
941
942 const gchar *
943 e_meeting_store_get_free_busy_template (EMeetingStore *store)
944 {
945 g_return_val_if_fail (E_IS_MEETING_STORE (store), NULL);
946
947 return store->priv->fb_uri;
948 }
949
950 void
951 e_meeting_store_set_free_busy_template (EMeetingStore *store,
952 const gchar *free_busy_template)
953 {
954 g_return_if_fail (E_IS_MEETING_STORE (store));
955
956 if (g_strcmp0 (store->priv->fb_uri, free_busy_template) == 0)
957 return;
958
959 g_free (store->priv->fb_uri);
960 store->priv->fb_uri = g_strdup (free_busy_template);
961
962 g_object_notify (G_OBJECT (store), "free-busy-template");
963 }
964
965 icaltimezone *
966 e_meeting_store_get_timezone (EMeetingStore *store)
967 {
968 g_return_val_if_fail (E_IS_MEETING_STORE (store), NULL);
969
970 return store->priv->zone;
971 }
972
973 void
974 e_meeting_store_set_timezone (EMeetingStore *store,
975 icaltimezone *timezone)
976 {
977 g_return_if_fail (E_IS_MEETING_STORE (store));
978
979 if (store->priv->zone == timezone)
980 return;
981
982 store->priv->zone = timezone;
983
984 g_object_notify (G_OBJECT (store), "timezone");
985 }
986
987 gint
988 e_meeting_store_get_week_start_day (EMeetingStore *store)
989 {
990 g_return_val_if_fail (E_IS_MEETING_STORE (store), 0);
991
992 return store->priv->week_start_day;
993 }
994
995 void
996 e_meeting_store_set_week_start_day (EMeetingStore *store,
997 gint week_start_day)
998 {
999 g_return_if_fail (E_IS_MEETING_STORE (store));
1000
1001 if (store->priv->week_start_day == week_start_day)
1002 return;
1003
1004 store->priv->week_start_day = week_start_day;
1005
1006 g_object_notify (G_OBJECT (store), "week-start-day");
1007 }
1008
1009 static void
1010 attendee_changed_cb (EMeetingAttendee *attendee,
1011 gpointer data)
1012 {
1013 EMeetingStore *store = E_MEETING_STORE (data);
1014 GtkTreePath *path;
1015 GtkTreeIter iter;
1016 gint row = -1, i;
1017
1018 for (i = 0; i < store->priv->attendees->len; i++) {
1019 if (attendee == g_ptr_array_index (store->priv->attendees, i)) {
1020 row = i;
1021 break;
1022 }
1023 }
1024
1025 if (row == -1)
1026 return;
1027
1028 path = gtk_tree_path_new ();
1029 gtk_tree_path_append_index (path, row);
1030 get_iter (GTK_TREE_MODEL (store), &iter, path);
1031 gtk_tree_model_row_changed (GTK_TREE_MODEL (store), path, &iter);
1032 gtk_tree_path_free (path);
1033 }
1034
1035 void
1036 e_meeting_store_add_attendee (EMeetingStore *store,
1037 EMeetingAttendee *attendee)
1038 {
1039 GtkTreePath *path;
1040 GtkTreeIter iter;
1041
1042 g_return_if_fail (E_IS_MEETING_STORE (store));
1043
1044 g_object_ref (attendee);
1045 g_ptr_array_add (store->priv->attendees, attendee);
1046
1047 g_signal_connect (
1048 attendee, "changed",
1049 G_CALLBACK (attendee_changed_cb), store);
1050
1051 path = gtk_tree_path_new ();
1052 gtk_tree_path_append_index (path, store->priv->attendees->len - 1);
1053 get_iter (GTK_TREE_MODEL (store), &iter, path);
1054 gtk_tree_model_row_inserted (GTK_TREE_MODEL (store), path, &iter);
1055 gtk_tree_path_free (path);
1056 }
1057
1058 EMeetingAttendee *
1059 e_meeting_store_add_attendee_with_defaults (EMeetingStore *store)
1060 {
1061 EMeetingAttendee *attendee;
1062 gchar *str;
1063
1064 attendee = E_MEETING_ATTENDEE (e_meeting_attendee_new ());
1065
1066 e_meeting_attendee_set_address (attendee, g_strdup (""));
1067 e_meeting_attendee_set_member (attendee, g_strdup (""));
1068
1069 str = g_strdup (_("Individual"));
1070 e_meeting_attendee_set_cutype (attendee, text_to_type (str));
1071 g_free (str);
1072 str = g_strdup (_("Required Participant"));
1073 e_meeting_attendee_set_role (attendee, text_to_role (str));
1074 g_free (str);
1075 str = g_strdup (_("Yes"));
1076 e_meeting_attendee_set_rsvp (attendee, text_to_boolean (str));
1077 g_free (str);
1078
1079 e_meeting_attendee_set_delto (attendee, g_strdup (""));
1080 e_meeting_attendee_set_delfrom (attendee, g_strdup (""));
1081
1082 str = g_strdup (_("Needs Action"));
1083 e_meeting_attendee_set_status (attendee, text_to_partstat (str));
1084 g_free (str);
1085
1086 e_meeting_attendee_set_cn (attendee, g_strdup (""));
1087 e_meeting_attendee_set_language (attendee, g_strdup ("en"));
1088
1089 e_meeting_store_add_attendee (store, attendee);
1090
1091 return attendee;
1092 }
1093
1094 void
1095 e_meeting_store_remove_attendee (EMeetingStore *store,
1096 EMeetingAttendee *attendee)
1097 {
1098 gint i, row = -1;
1099 GtkTreePath *path;
1100
1101 for (i = 0; i < store->priv->attendees->len; i++) {
1102 if (attendee == g_ptr_array_index (store->priv->attendees, i)) {
1103 row = i;
1104 break;
1105 }
1106 }
1107
1108 if (row != -1) {
1109 g_ptr_array_remove_index (store->priv->attendees, row);
1110
1111 path = gtk_tree_path_new ();
1112 gtk_tree_path_append_index (path, row);
1113 gtk_tree_model_row_deleted (GTK_TREE_MODEL (store), path);
1114 gtk_tree_path_free (path);
1115
1116 g_object_unref (attendee);
1117 }
1118 }
1119
1120 void
1121 e_meeting_store_remove_all_attendees (EMeetingStore *store)
1122 {
1123 gint i, j, k;
1124
1125 for (i = 0, j = e_meeting_store_count_actual_attendees (store), k = 0;
1126 i < j; i++) {
1127 /* Always try to remove the attendee at index 0 since
1128 * it is the only one that will continue to exist until
1129 * all attendees are removed. */
1130 EMeetingAttendee *attendee;
1131 GtkTreePath *path;
1132
1133 attendee = g_ptr_array_index (store->priv->attendees, k);
1134 g_ptr_array_remove_index (store->priv->attendees, k);
1135
1136 path = gtk_tree_path_new ();
1137 gtk_tree_path_append_index (path, k);
1138 gtk_tree_model_row_deleted (GTK_TREE_MODEL (store), path);
1139 gtk_tree_path_free (path);
1140
1141 g_object_unref (attendee);
1142 }
1143 }
1144
1145 /**
1146 * e_meeting_store_find_self:
1147 * @store: an #EMeetingStore
1148 * @row: return location for the matching row number, or %NULL
1149 *
1150 * Looks for the user in @store by comparing attendee email addresses to
1151 * registered mail identities. If a matching email address is found and
1152 * @row is not %NULL, @row will be set to the #EMeetingStore row number
1153 * with the matching email address.
1154 *
1155 * Returns: an #EMeetingAttendee, or %NULL
1156 **/
1157 EMeetingAttendee *
1158 e_meeting_store_find_self (EMeetingStore *store,
1159 gint *row)
1160 {
1161 EMeetingAttendee *attendee = NULL;
1162 ESourceRegistry *registry;
1163 EShell *shell;
1164 GList *list, *iter;
1165 const gchar *extension_name;
1166
1167 g_return_val_if_fail (E_IS_MEETING_STORE (store), NULL);
1168
1169 /* FIXME Refactor this so we don't need e_shell_get_default(). */
1170 shell = e_shell_get_default ();
1171 registry = e_shell_get_registry (shell);
1172 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
1173
1174 list = e_source_registry_list_sources (registry, extension_name);
1175
1176 for (iter = list; iter != NULL; iter = g_list_next (iter)) {
1177 ESource *source = E_SOURCE (iter->data);
1178 ESourceMailIdentity *extension;
1179 const gchar *address;
1180
1181 extension = e_source_get_extension (source, extension_name);
1182 address = e_source_mail_identity_get_address (extension);
1183
1184 if (address != NULL)
1185 attendee = e_meeting_store_find_attendee (
1186 store, address, row);
1187
1188 if (attendee != NULL)
1189 break;
1190 }
1191
1192 g_list_free_full (list, (GDestroyNotify) g_object_unref);
1193
1194 return attendee;
1195 }
1196
1197 EMeetingAttendee *
1198 e_meeting_store_find_attendee (EMeetingStore *store,
1199 const gchar *address,
1200 gint *row)
1201 {
1202 EMeetingAttendee *attendee;
1203 gint i;
1204
1205 if (address == NULL)
1206 return NULL;
1207
1208 for (i = 0; i < store->priv->attendees->len; i++) {
1209 const gchar *attendee_address;
1210
1211 attendee = g_ptr_array_index (store->priv->attendees, i);
1212
1213 attendee_address = e_meeting_attendee_get_address (attendee);
1214 if (attendee_address && !g_ascii_strcasecmp (
1215 itip_strip_mailto (attendee_address),
1216 itip_strip_mailto (address))) {
1217 if (row != NULL)
1218 *row = i;
1219
1220 return attendee;
1221 }
1222 }
1223
1224 return NULL;
1225 }
1226
1227 EMeetingAttendee *
1228 e_meeting_store_find_attendee_at_row (EMeetingStore *store,
1229 gint row)
1230 {
1231 g_return_val_if_fail (E_IS_MEETING_STORE (store), NULL);
1232 g_return_val_if_fail (ROW_VALID (store, row), NULL);
1233
1234 return g_ptr_array_index (store->priv->attendees, row);
1235 }
1236
1237 GtkTreePath *
1238 e_meeting_store_find_attendee_path (EMeetingStore *store,
1239 EMeetingAttendee *attendee)
1240 {
1241 GtkTreePath *path;
1242 gint row = -1, i;
1243
1244 for (i = 0; i < store->priv->attendees->len; i++) {
1245 if (attendee == g_ptr_array_index (store->priv->attendees, i)) {
1246 row = i;
1247 break;
1248 }
1249 }
1250
1251 if (row == -1)
1252 return NULL;
1253
1254 path = gtk_tree_path_new ();
1255 gtk_tree_path_append_index (path, row);
1256
1257 return path;
1258 }
1259
1260 gint
1261 e_meeting_store_count_actual_attendees (EMeetingStore *store)
1262 {
1263 g_return_val_if_fail (E_IS_MEETING_STORE (store), 0);
1264
1265 return store->priv->attendees->len;
1266 }
1267
1268 const GPtrArray *
1269 e_meeting_store_get_attendees (EMeetingStore *store)
1270 {
1271 g_return_val_if_fail (E_IS_MEETING_STORE (store), NULL);
1272
1273 return store->priv->attendees;
1274 }
1275
1276 static icaltimezone *
1277 find_zone (icalproperty *ip,
1278 icalcomponent *tz_top_level)
1279 {
1280 icalparameter *param;
1281 icalcomponent *sub_comp;
1282 const gchar *tzid;
1283 icalcompiter iter;
1284
1285 if (tz_top_level == NULL)
1286 return NULL;
1287
1288 param = icalproperty_get_first_parameter (ip, ICAL_TZID_PARAMETER);
1289 if (param == NULL)
1290 return NULL;
1291 tzid = icalparameter_get_tzid (param);
1292
1293 iter = icalcomponent_begin_component (tz_top_level, ICAL_VTIMEZONE_COMPONENT);
1294 while ((sub_comp = icalcompiter_deref (&iter)) != NULL) {
1295 icalcomponent *clone;
1296 icalproperty *prop;
1297 const gchar *tz_tzid;
1298
1299 prop = icalcomponent_get_first_property (sub_comp, ICAL_TZID_PROPERTY);
1300 tz_tzid = icalproperty_get_tzid (prop);
1301 if (!strcmp (tzid, tz_tzid)) {
1302 icaltimezone *zone;
1303
1304 zone = icaltimezone_new ();
1305 clone = icalcomponent_new_clone (sub_comp);
1306 icaltimezone_set_component (zone, clone);
1307
1308 return zone;
1309 }
1310
1311 icalcompiter_next (&iter);
1312 }
1313
1314 return NULL;
1315 }
1316
1317 static void
1318 process_callbacks (EMeetingStoreQueueData *qdata)
1319 {
1320 EMeetingStore *store;
1321 gint i;
1322
1323 store = qdata->store;
1324
1325 for (i = 0; i < qdata->call_backs->len; i++) {
1326 EMeetingStoreRefreshCallback call_back;
1327 gpointer *data = NULL;
1328
1329 call_back = g_ptr_array_index (qdata->call_backs, i);
1330 data = g_ptr_array_index (qdata->data, i);
1331
1332 g_idle_add ((GSourceFunc) call_back, data);
1333 }
1334
1335 g_mutex_lock (store->priv->mutex);
1336 store->priv->num_threads--;
1337 g_mutex_unlock (store->priv->mutex);
1338
1339 refresh_queue_remove (qdata->store, qdata->attendee);
1340 g_object_unref (store);
1341 }
1342
1343 static void
1344 process_free_busy_comp_get_xfb (icalproperty *ip,
1345 gchar **summary,
1346 gchar **location)
1347 {
1348 const gchar *tmp = NULL;
1349
1350 g_return_if_fail (ip != NULL);
1351 g_return_if_fail (summary != NULL && *summary == NULL);
1352 g_return_if_fail (location != NULL && *location == NULL);
1353
1354 /* We extract extended free/busy information from the icalproperty
1355 * here (X-SUMMARY and X-LOCATION). If the property carries such,
1356 * it will be displayed as a tooltip for the busy period. Otherwise,
1357 * nothing will happen (*summary and/or *location will be NULL)
1358 */
1359
1360 tmp = icalproperty_get_parameter_as_string (ip, E_MEETING_FREE_BUSY_XPROP_SUMMARY);
1361 *summary = e_meeting_xfb_utf8_string_new_from_ical (tmp,
1362 E_MEETING_FREE_BUSY_XPROP_MAXLEN);
1363 tmp = icalproperty_get_parameter_as_string (ip, E_MEETING_FREE_BUSY_XPROP_LOCATION);
1364 *location = e_meeting_xfb_utf8_string_new_from_ical (tmp,
1365 E_MEETING_FREE_BUSY_XPROP_MAXLEN);
1366 }
1367
1368 static void
1369 process_free_busy_comp (EMeetingAttendee *attendee,
1370 icalcomponent *fb_comp,
1371 icaltimezone *zone,
1372 icalcomponent *tz_top_level)
1373 {
1374 icalproperty *ip;
1375
1376 ip = icalcomponent_get_first_property (fb_comp, ICAL_DTSTART_PROPERTY);
1377 if (ip != NULL) {
1378 struct icaltimetype dtstart;
1379 icaltimezone *ds_zone;
1380
1381 dtstart = icalproperty_get_dtstart (ip);
1382 if (!dtstart.is_utc)
1383 ds_zone = find_zone (ip, tz_top_level);
1384 else
1385 ds_zone = icaltimezone_get_utc_timezone ();
1386 icaltimezone_convert_time (&dtstart, ds_zone, zone);
1387 e_meeting_attendee_set_start_busy_range (
1388 attendee,
1389 dtstart.year,
1390 dtstart.month,
1391 dtstart.day,
1392 dtstart.hour,
1393 dtstart.minute);
1394 }
1395
1396 ip = icalcomponent_get_first_property (fb_comp, ICAL_DTEND_PROPERTY);
1397 if (ip != NULL) {
1398 struct icaltimetype dtend;
1399 icaltimezone *de_zone;
1400
1401 dtend = icalproperty_get_dtend (ip);
1402 if (!dtend.is_utc)
1403 de_zone = find_zone (ip, tz_top_level);
1404 else
1405 de_zone = icaltimezone_get_utc_timezone ();
1406 icaltimezone_convert_time (&dtend, de_zone, zone);
1407 e_meeting_attendee_set_end_busy_range (
1408 attendee,
1409 dtend.year,
1410 dtend.month,
1411 dtend.day,
1412 dtend.hour,
1413 dtend.minute);
1414 }
1415
1416 ip = icalcomponent_get_first_property (fb_comp, ICAL_FREEBUSY_PROPERTY);
1417 while (ip != NULL) {
1418 icalparameter *param;
1419 struct icalperiodtype fb;
1420 EMeetingFreeBusyType busy_type = E_MEETING_FREE_BUSY_LAST;
1421 icalparameter_fbtype fbtype = ICAL_FBTYPE_BUSY;
1422
1423 fb = icalproperty_get_freebusy (ip);
1424 param = icalproperty_get_first_parameter (ip, ICAL_FBTYPE_PARAMETER);
1425 if (param != NULL)
1426 fbtype = icalparameter_get_fbtype (param);
1427
1428 switch (fbtype) {
1429 case ICAL_FBTYPE_BUSY:
1430 busy_type = E_MEETING_FREE_BUSY_BUSY;
1431 break;
1432
1433 case ICAL_FBTYPE_BUSYUNAVAILABLE:
1434 busy_type = E_MEETING_FREE_BUSY_OUT_OF_OFFICE;
1435 break;
1436
1437 case ICAL_FBTYPE_BUSYTENTATIVE:
1438 busy_type = E_MEETING_FREE_BUSY_TENTATIVE;
1439 break;
1440
1441 case ICAL_FBTYPE_FREE:
1442 busy_type = E_MEETING_FREE_BUSY_FREE;
1443 break;
1444
1445 default:
1446 break;
1447 }
1448
1449 if (busy_type != E_MEETING_FREE_BUSY_LAST) {
1450 icaltimezone *utc_zone = icaltimezone_get_utc_timezone ();
1451 gchar *summary = NULL;
1452 gchar *location = NULL;
1453
1454 icaltimezone_convert_time (&fb.start, utc_zone, zone);
1455 icaltimezone_convert_time (&fb.end, utc_zone, zone);
1456
1457 /* Extract extended free/busy (XFB) information from
1458 * the icalproperty, if it carries such.
1459 * See the comment for the EMeetingXfbData structure
1460 * for a reference.
1461 */
1462 process_free_busy_comp_get_xfb (ip, &summary, &location);
1463
1464 e_meeting_attendee_add_busy_period (attendee,
1465 fb.start.year,
1466 fb.start.month,
1467 fb.start.day,
1468 fb.start.hour,
1469 fb.start.minute,
1470 fb.end.year,
1471 fb.end.month,
1472 fb.end.day,
1473 fb.end.hour,
1474 fb.end.minute,
1475 busy_type,
1476 summary,
1477 location);
1478
1479 if (summary != NULL)
1480 g_free (summary);
1481 if (location != NULL)
1482 g_free (location);
1483 }
1484
1485 ip = icalcomponent_get_next_property (fb_comp, ICAL_FREEBUSY_PROPERTY);
1486 }
1487 }
1488
1489 static void
1490 process_free_busy (EMeetingStoreQueueData *qdata,
1491 gchar *text)
1492 {
1493 EMeetingStore *store = qdata->store;
1494 EMeetingStorePrivate *priv;
1495 EMeetingAttendee *attendee = qdata->attendee;
1496 icalcomponent *main_comp;
1497 icalcomponent_kind kind = ICAL_NO_COMPONENT;
1498
1499 priv = store->priv;
1500
1501 main_comp = icalparser_parse_string (text);
1502 if (main_comp == NULL) {
1503 process_callbacks (qdata);
1504 return;
1505 }
1506
1507 kind = icalcomponent_isa (main_comp);
1508 if (kind == ICAL_VCALENDAR_COMPONENT) {
1509 icalcompiter iter;
1510 icalcomponent *tz_top_level, *sub_comp;
1511
1512 tz_top_level = e_cal_util_new_top_level ();
1513
1514 iter = icalcomponent_begin_component (main_comp, ICAL_VTIMEZONE_COMPONENT);
1515 while ((sub_comp = icalcompiter_deref (&iter)) != NULL) {
1516 icalcomponent *clone;
1517
1518 clone = icalcomponent_new_clone (sub_comp);
1519 icalcomponent_add_component (tz_top_level, clone);
1520
1521 icalcompiter_next (&iter);
1522 }
1523
1524 iter = icalcomponent_begin_component (main_comp, ICAL_VFREEBUSY_COMPONENT);
1525 while ((sub_comp = icalcompiter_deref (&iter)) != NULL) {
1526 process_free_busy_comp (attendee, sub_comp, priv->zone, tz_top_level);
1527
1528 icalcompiter_next (&iter);
1529 }
1530 icalcomponent_free (tz_top_level);
1531 } else if (kind == ICAL_VFREEBUSY_COMPONENT) {
1532 process_free_busy_comp (attendee, main_comp, priv->zone, NULL);
1533 }
1534
1535 icalcomponent_free (main_comp);
1536
1537 process_callbacks (qdata);
1538 }
1539
1540 /*
1541 * Replace all instances of from_value in string with to_value
1542 * In the returned newly allocated string.
1543 */
1544 static gchar *
1545 replace_string (gchar *string,
1546 const gchar *from_value,
1547 gchar *to_value)
1548 {
1549 gchar *replaced;
1550 gchar **split_uri;
1551
1552 split_uri = g_strsplit (string, from_value, 0);
1553 replaced = g_strjoinv (to_value, split_uri);
1554 g_strfreev (split_uri);
1555
1556 return replaced;
1557 }
1558
1559 static void start_async_read (const gchar *uri, gpointer data);
1560
1561 typedef struct {
1562 ECalClient *client;
1563 time_t startt;
1564 time_t endt;
1565 GSList *users;
1566 GSList *fb_data;
1567 gchar *fb_uri;
1568 gchar *email;
1569 EMeetingAttendee *attendee;
1570 EMeetingStoreQueueData *qdata;
1571 EMeetingStore *store;
1572 } FreeBusyAsyncData;
1573
1574 #define USER_SUB "%u"
1575 #define DOMAIN_SUB "%d"
1576
1577 static void
1578 client_free_busy_data_cb (ECalClient *client,
1579 const GSList *ecalcomps,
1580 FreeBusyAsyncData *fbd)
1581 {
1582 const GSList *iter;
1583
1584 g_return_if_fail (fbd != NULL);
1585
1586 for (iter = ecalcomps; iter != NULL; iter = iter->next) {
1587 ECalComponent *comp = iter->data;
1588
1589 if (comp != NULL)
1590 fbd->fb_data = g_slist_prepend (
1591 fbd->fb_data, g_object_ref (comp));
1592 }
1593 }
1594
1595 static gboolean
1596 freebusy_async (gpointer data)
1597 {
1598 FreeBusyAsyncData *fbd = data;
1599 EMeetingAttendee *attendee = fbd->attendee;
1600 gchar *default_fb_uri = NULL;
1601 gchar *fburi = NULL;
1602 static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
1603 EMeetingStorePrivate *priv = fbd->store->priv;
1604
1605 if (fbd->client) {
1606 guint sigid;
1607 /* FIXME This a workaround for getting all the free busy
1608 * information for the users. We should be able to
1609 * get free busy asynchronously. */
1610 g_static_mutex_lock (&mutex);
1611 priv->num_queries++;
1612 sigid = g_signal_connect (
1613 fbd->client, "free-busy-data",
1614 G_CALLBACK (client_free_busy_data_cb), fbd);
1615 e_cal_client_get_free_busy_sync (
1616 fbd->client, fbd->startt,
1617 fbd->endt, fbd->users, NULL, NULL);
1618 g_signal_handler_disconnect (fbd->client, sigid);
1619 priv->num_queries--;
1620 g_static_mutex_unlock (&mutex);
1621
1622 g_slist_foreach (fbd->users, (GFunc) g_free, NULL);
1623 g_slist_free (fbd->users);
1624
1625 if (fbd->fb_data != NULL) {
1626 ECalComponent *comp = fbd->fb_data->data;
1627 gchar *comp_str;
1628
1629 comp_str = e_cal_component_get_as_string (comp);
1630 process_free_busy (fbd->qdata, comp_str);
1631 g_free (comp_str);
1632
1633 return TRUE;
1634 }
1635 }
1636
1637 /* Look for fburl's of attendee with no free busy info on server */
1638 if (!e_meeting_attendee_is_set_address (attendee)) {
1639 process_callbacks (fbd->qdata);
1640 return TRUE;
1641 }
1642
1643 /* Check for free busy info on the default server */
1644 default_fb_uri = g_strdup (fbd->fb_uri);
1645 fburi = g_strdup (e_meeting_attendee_get_fburi (attendee));
1646
1647 if (fburi && !*fburi) {
1648 g_free (fburi);
1649 fburi = NULL;
1650 }
1651
1652 if (fburi) {
1653 priv->num_queries++;
1654 start_async_read (fburi, fbd->qdata);
1655 g_free (fburi);
1656 } else if (default_fb_uri != NULL && !g_str_equal (default_fb_uri, "")) {
1657 gchar *tmp_fb_uri;
1658 gchar **split_email;
1659
1660 split_email = g_strsplit (fbd->email, "@", 2);
1661
1662 tmp_fb_uri = replace_string (default_fb_uri, USER_SUB, split_email[0]);
1663 g_free (default_fb_uri);
1664 default_fb_uri = replace_string (tmp_fb_uri, DOMAIN_SUB, split_email[1]);
1665
1666 priv->num_queries++;
1667 start_async_read (default_fb_uri, fbd->qdata);
1668 g_free (tmp_fb_uri);
1669 g_strfreev (split_email);
1670 g_free (default_fb_uri);
1671 } else {
1672 process_callbacks (fbd->qdata);
1673 }
1674
1675 return TRUE;
1676 }
1677
1678 #undef USER_SUB
1679 #undef DOMAIN_SUB
1680
1681 static gboolean
1682 refresh_busy_periods (gpointer data)
1683 {
1684 EMeetingStore *store = E_MEETING_STORE (data);
1685 EMeetingStorePrivate *priv;
1686 EMeetingAttendee *attendee = NULL;
1687 EMeetingStoreQueueData *qdata = NULL;
1688 gint i;
1689 GThread *thread;
1690 GError *error = NULL;
1691 FreeBusyAsyncData *fbd;
1692
1693 priv = store->priv;
1694
1695 /* Check to see if there are any remaining attendees in the queue */
1696 for (i = 0; i < priv->refresh_queue->len; i++) {
1697 attendee = g_ptr_array_index (priv->refresh_queue, i);
1698 g_return_val_if_fail (attendee != NULL, FALSE);
1699
1700 qdata = g_hash_table_lookup (
1701 priv->refresh_data, itip_strip_mailto (
1702 e_meeting_attendee_get_address (attendee)));
1703 if (!qdata)
1704 continue;
1705
1706 if (!qdata->refreshing)
1707 break;
1708 }
1709
1710 /* The everything in the queue is being refreshed */
1711 if (i >= priv->refresh_queue->len) {
1712 priv->refresh_idle_id = 0;
1713 return FALSE;
1714 }
1715
1716 /* Indicate we are trying to refresh it */
1717 qdata->refreshing = TRUE;
1718
1719 /* We take a ref in case we get destroyed in the gui during a callback */
1720 g_object_ref (qdata->store);
1721
1722 fbd = g_new0 (FreeBusyAsyncData, 1);
1723 fbd->client = priv->client;
1724 fbd->attendee = attendee;
1725 fbd->users = NULL;
1726 fbd->fb_data = NULL;
1727 fbd->qdata = qdata;
1728 fbd->fb_uri = priv->fb_uri;
1729 fbd->store = store;
1730 fbd->email = g_strdup (itip_strip_mailto (
1731 e_meeting_attendee_get_address (attendee)));
1732
1733 /* Check the server for free busy data */
1734 if (priv->client) {
1735 struct icaltimetype itt;
1736
1737 itt = icaltime_null_time ();
1738 itt.year = g_date_get_year (&qdata->start.date);
1739 itt.month = g_date_get_month (&qdata->start.date);
1740 itt.day = g_date_get_day (&qdata->start.date);
1741 itt.hour = qdata->start.hour;
1742 itt.minute = qdata->start.minute;
1743 fbd->startt = icaltime_as_timet_with_zone (itt, priv->zone);
1744
1745 itt = icaltime_null_time ();
1746 itt.year = g_date_get_year (&qdata->end.date);
1747 itt.month = g_date_get_month (&qdata->end.date);
1748 itt.day = g_date_get_day (&qdata->end.date);
1749 itt.hour = qdata->end.hour;
1750 itt.minute = qdata->end.minute;
1751 fbd->endt = icaltime_as_timet_with_zone (itt, priv->zone);
1752 fbd->qdata = qdata;
1753
1754 fbd->users = g_slist_append (fbd->users, g_strdup (fbd->email));
1755
1756 }
1757
1758 g_mutex_lock (store->priv->mutex);
1759 store->priv->num_threads++;
1760 g_mutex_unlock (store->priv->mutex);
1761
1762 thread = g_thread_create ((GThreadFunc) freebusy_async, fbd, FALSE, &error);
1763 if (!thread) {
1764 /* do clean up stuff here */
1765 g_slist_foreach (fbd->users, (GFunc) g_free, NULL);
1766 g_slist_free (fbd->users);
1767 g_free (fbd->email);
1768 priv->refresh_idle_id = 0;
1769
1770 g_mutex_lock (store->priv->mutex);
1771 store->priv->num_threads--;
1772 g_mutex_unlock (store->priv->mutex);
1773
1774 return FALSE;
1775 }
1776
1777 return TRUE;
1778 }
1779
1780 static void
1781 refresh_queue_add (EMeetingStore *store,
1782 gint row,
1783 EMeetingTime *start,
1784 EMeetingTime *end,
1785 EMeetingStoreRefreshCallback call_back,
1786 gpointer data)
1787 {
1788 EMeetingStorePrivate *priv;
1789 EMeetingAttendee *attendee;
1790 EMeetingStoreQueueData *qdata;
1791 gint i;
1792
1793 priv = store->priv;
1794
1795 attendee = g_ptr_array_index (priv->attendees, row);
1796 if ((attendee == NULL) || !strcmp (itip_strip_mailto (
1797 e_meeting_attendee_get_address (attendee)), ""))
1798 return;
1799
1800 /* check the queue if the attendee is already in there*/
1801 for (i = 0; i < priv->refresh_queue->len; i++) {
1802 if (attendee == g_ptr_array_index (priv->refresh_queue, i))
1803 return;
1804
1805 if (!strcmp (e_meeting_attendee_get_address (attendee),
1806 e_meeting_attendee_get_address (
1807 g_ptr_array_index (priv->refresh_queue, i))))
1808 return;
1809 }
1810
1811 g_mutex_lock (priv->mutex);
1812 qdata = g_hash_table_lookup (
1813 priv->refresh_data, itip_strip_mailto (
1814 e_meeting_attendee_get_address (attendee)));
1815
1816 if (qdata == NULL) {
1817 qdata = g_new0 (EMeetingStoreQueueData, 1);
1818
1819 qdata->store = store;
1820 qdata->attendee = attendee;
1821 e_meeting_attendee_clear_busy_periods (attendee);
1822 e_meeting_attendee_set_has_calendar_info (attendee, FALSE);
1823
1824 qdata->start = *start;
1825 qdata->end = *end;
1826 qdata->string = g_string_new (NULL);
1827 qdata->call_backs = g_ptr_array_new ();
1828 qdata->data = g_ptr_array_new ();
1829 g_ptr_array_add (qdata->call_backs, call_back);
1830 g_ptr_array_add (qdata->data, data);
1831
1832 g_hash_table_insert (
1833 priv->refresh_data, g_strdup (itip_strip_mailto (
1834 e_meeting_attendee_get_address (attendee))), qdata);
1835 } else {
1836 if (e_meeting_time_compare_times (start, &qdata->start) == -1)
1837 qdata->start = *start;
1838 if (e_meeting_time_compare_times (end, &qdata->end) == -1)
1839 qdata->end = *end;
1840 g_ptr_array_add (qdata->call_backs, call_back);
1841 g_ptr_array_add (qdata->data, data);
1842 }
1843 g_mutex_unlock (priv->mutex);
1844
1845 g_object_ref (attendee);
1846 g_ptr_array_add (priv->refresh_queue, attendee);
1847
1848 if (priv->refresh_idle_id == 0)
1849 priv->refresh_idle_id = g_idle_add (refresh_busy_periods, store);
1850 }
1851
1852 static void
1853 async_read (GObject *source_object,
1854 GAsyncResult *result,
1855 gpointer data)
1856 {
1857 EMeetingStoreQueueData *qdata = data;
1858 GError *error = NULL;
1859 GInputStream *istream;
1860 gssize read;
1861
1862 g_return_if_fail (source_object != NULL);
1863 g_return_if_fail (G_IS_INPUT_STREAM (source_object));
1864
1865 istream = G_INPUT_STREAM (source_object);
1866
1867 read = g_input_stream_read_finish (istream, result, &error);
1868
1869 if (error != NULL) {
1870 g_warning (
1871 "Read finish failed: %s", error->message);
1872 g_error_free (error);
1873
1874 g_input_stream_close (istream, NULL, NULL);
1875 g_object_unref (istream);
1876 process_free_busy (qdata, qdata->string->str);
1877 return;
1878 }
1879
1880 g_return_if_fail (read >= 0);
1881
1882 if (read == 0) {
1883 g_input_stream_close (istream, NULL, NULL);
1884 g_object_unref (istream);
1885 process_free_busy (qdata, qdata->string->str);
1886 } else {
1887 qdata->buffer[read] = '\0';
1888 qdata->string = g_string_append (qdata->string, qdata->buffer);
1889
1890 g_input_stream_read_async (
1891 istream, qdata->buffer, BUF_SIZE - 1,
1892 G_PRIORITY_DEFAULT, NULL, async_read, qdata);
1893 }
1894 }
1895
1896 static void
1897 soup_authenticate (SoupSession *session,
1898 SoupMessage *msg,
1899 SoupAuth *auth,
1900 gboolean retrying,
1901 gpointer data)
1902 {
1903 SoupURI *suri;
1904 const gchar *orig_uri;
1905 gboolean tried = FALSE;
1906
1907 g_return_if_fail (msg != NULL);
1908 g_return_if_fail (auth != NULL);
1909
1910 orig_uri = g_object_get_data (G_OBJECT (msg), "orig-uri");
1911 g_return_if_fail (orig_uri != NULL);
1912
1913 suri = soup_uri_new (orig_uri);
1914 if (!suri)
1915 return;
1916
1917 if (!suri->user || !*suri->user) {
1918 soup_uri_free (suri);
1919 return;
1920 }
1921
1922 if (!retrying) {
1923 if (suri->password) {
1924 soup_auth_authenticate (auth, suri->user, suri->password);
1925 tried = TRUE;
1926 } else {
1927 gchar *password;
1928
1929 password = e_passwords_get_password (NULL, orig_uri);
1930 if (password) {
1931 soup_auth_authenticate (auth, suri->user, password);
1932 tried = TRUE;
1933
1934 memset (password, 0, strlen (password));
1935 g_free (password);
1936 }
1937 }
1938 }
1939
1940 if (!tried) {
1941 gboolean remember = FALSE;
1942 gchar *password, *bold_host, *bold_user;
1943 GString *description;
1944
1945 bold_host = g_strconcat ("<b>", suri->host, "</b>", NULL);
1946 bold_user = g_strconcat ("<b>", suri->user, "</b>", NULL);
1947
1948 description = g_string_new ("");
1949
1950 g_string_append_printf (
1951 description, _("Enter password to access "
1952 "free/busy information on server %s as user %s"),
1953 bold_host, bold_user);
1954
1955 g_free (bold_host);
1956 g_free (bold_user);
1957
1958 if (retrying && msg->reason_phrase && *msg->reason_phrase) {
1959 g_string_append (description, "\n");
1960 g_string_append_printf (
1961 description, _("Failure reason: %s"),
1962 msg->reason_phrase);
1963 }
1964
1965 password = e_passwords_ask_password (
1966 _("Enter password"), NULL, orig_uri,
1967 description->str, E_PASSWORDS_REMEMBER_FOREVER |
1968 E_PASSWORDS_SECRET | E_PASSWORDS_ONLINE |
1969 (retrying ? E_PASSWORDS_REPROMPT : 0),
1970 &remember, NULL);
1971
1972 g_string_free (description, TRUE);
1973
1974 if (password) {
1975 soup_auth_authenticate (auth, suri->user, password);
1976 tried = TRUE;
1977
1978 memset (password, 0, strlen (password));
1979 g_free (password);
1980 }
1981 }
1982
1983 soup_uri_free (suri);
1984 }
1985
1986 static void
1987 redirect_handler (SoupMessage *msg,
1988 gpointer user_data)
1989 {
1990 if (SOUP_STATUS_IS_REDIRECTION (msg->status_code)) {
1991 SoupSession *soup_session = user_data;
1992 SoupURI *new_uri;
1993 const gchar *new_loc;
1994
1995 new_loc = soup_message_headers_get (msg->response_headers, "Location");
1996 if (!new_loc)
1997 return;
1998
1999 new_uri = soup_uri_new_with_base (soup_message_get_uri (msg), new_loc);
2000 if (!new_uri) {
2001 soup_message_set_status_full (
2002 msg,
2003 SOUP_STATUS_MALFORMED,
2004 "Invalid Redirect URL");
2005 return;
2006 }
2007
2008 soup_message_set_uri (msg, new_uri);
2009 soup_session_requeue_message (soup_session, msg);
2010
2011 soup_uri_free (new_uri);
2012 }
2013 }
2014
2015 static void
2016 soup_msg_ready_cb (SoupSession *session,
2017 SoupMessage *msg,
2018 gpointer user_data)
2019 {
2020 EMeetingStoreQueueData *qdata = user_data;
2021
2022 g_return_if_fail (session != NULL);
2023 g_return_if_fail (msg != NULL);
2024 g_return_if_fail (qdata != NULL);
2025
2026 if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
2027 qdata->string = g_string_new_len (
2028 msg->response_body->data,
2029 msg->response_body->length);
2030 process_free_busy (qdata, qdata->string->str);
2031 } else {
2032 g_warning (
2033 "Unable to access free/busy url: %s",
2034 msg->reason_phrase && *msg->reason_phrase ?
2035 msg->reason_phrase : (soup_status_get_phrase (
2036 msg->status_code) ? soup_status_get_phrase (
2037 msg->status_code) : "Unknown error"));
2038 process_callbacks (qdata);
2039 }
2040 }
2041
2042 static void
2043 download_with_libsoup (const gchar *uri,
2044 EMeetingStoreQueueData *qdata)
2045 {
2046 SoupSession *session;
2047 SoupMessage *msg;
2048 EProxy *proxy;
2049
2050 g_return_if_fail (uri != NULL);
2051 g_return_if_fail (qdata != NULL);
2052
2053 msg = soup_message_new (SOUP_METHOD_GET, uri);
2054 if (!msg) {
2055 g_warning ("Unable to access free/busy url '%s'; malformed?", uri);
2056 process_callbacks (qdata);
2057 return;
2058 }
2059
2060 g_object_set_data_full (G_OBJECT (msg), "orig-uri", g_strdup (uri), g_free);
2061
2062 session = soup_session_async_new ();
2063 g_object_set (session, SOUP_SESSION_TIMEOUT, 90, NULL);
2064 g_signal_connect (
2065 session, "authenticate",
2066 G_CALLBACK (soup_authenticate), NULL);
2067
2068 proxy = e_proxy_new ();
2069 e_proxy_setup_proxy (proxy);
2070
2071 if (e_proxy_require_proxy_for_uri (proxy, uri)) {
2072 SoupURI *proxy_uri;
2073
2074 proxy_uri = e_proxy_peek_uri_for (proxy, uri);
2075 g_object_set (session, SOUP_SESSION_PROXY_URI, proxy_uri, NULL);
2076 }
2077
2078 g_object_unref (proxy);
2079
2080 soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT);
2081 soup_message_add_header_handler (
2082 msg, "got_body", "Location",
2083 G_CALLBACK (redirect_handler), session);
2084 soup_message_headers_append (msg->request_headers, "Connection", "close");
2085 soup_session_queue_message (session, msg, soup_msg_ready_cb, qdata);
2086 }
2087
2088 static void
2089 start_async_read (const gchar *uri,
2090 gpointer data)
2091 {
2092 EMeetingStoreQueueData *qdata = data;
2093 GError *error = NULL;
2094 GFile *file;
2095 GInputStream *istream;
2096
2097 g_return_if_fail (uri != NULL);
2098 g_return_if_fail (data != NULL);
2099
2100 qdata->store->priv->num_queries--;
2101 file = g_file_new_for_uri (uri);
2102
2103 g_return_if_fail (file != NULL);
2104
2105 istream = G_INPUT_STREAM (g_file_read (file, NULL, &error));
2106
2107 if (error && g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED)) {
2108 download_with_libsoup (uri, qdata);
2109 g_object_unref (file);
2110 g_error_free (error);
2111 return;
2112 }
2113
2114 if (error) {
2115 g_warning ("Unable to access free/busy url: %s", error->message);
2116 g_error_free (error);
2117 process_callbacks (qdata);
2118 g_object_unref (file);
2119 return;
2120 }
2121
2122 if (!istream) {
2123 process_callbacks (qdata);
2124 g_object_unref (file);
2125 } else
2126 g_input_stream_read_async (
2127 istream, qdata->buffer, BUF_SIZE - 1,
2128 G_PRIORITY_DEFAULT, NULL, async_read, qdata);
2129 }
2130
2131 void
2132 e_meeting_store_refresh_all_busy_periods (EMeetingStore *store,
2133 EMeetingTime *start,
2134 EMeetingTime *end,
2135 EMeetingStoreRefreshCallback call_back,
2136 gpointer data)
2137 {
2138 gint i;
2139
2140 g_return_if_fail (E_IS_MEETING_STORE (store));
2141
2142 for (i = 0; i < store->priv->attendees->len; i++)
2143 refresh_queue_add (store, i, start, end, call_back, data);
2144 }
2145
2146 void
2147 e_meeting_store_refresh_busy_periods (EMeetingStore *store,
2148 gint row,
2149 EMeetingTime *start,
2150 EMeetingTime *end,
2151 EMeetingStoreRefreshCallback call_back,
2152 gpointer data)
2153 {
2154 g_return_if_fail (E_IS_MEETING_STORE (store));
2155
2156 refresh_queue_add (store, row, start, end, call_back, data);
2157 }
2158
2159 guint
2160 e_meeting_store_get_num_queries (EMeetingStore *store)
2161 {
2162 g_return_val_if_fail (E_IS_MEETING_STORE (store), 0);
2163
2164 return store->priv->num_queries;
2165 }