Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / calendar / libedata-cal / e-cal-backend.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* Evolution calendar - generic backend class
3  *
4  * Copyright (C) 2000 Ximian, Inc.
5  * Copyright (C) 2000 Ximian, Inc.
6  *
7  * Authors: Federico Mena-Quintero <federico@ximian.com>
8  *          JP Rosevear <jpr@ximian.com>
9  *          Rodrigo Moya <rodrigo@ximian.com>    
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of version 2 of the GNU Lesser General Public
13  * License as published by the Free Software Foundation.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23  */
24
25 #include <config.h>
26 #include <libxml/parser.h>
27 #include <libxml/parserInternals.h>
28 #include <libxml/xmlmemory.h>
29
30 #include "e-cal-backend.h"
31
32 \f
33
34 /* Private part of the CalBackend structure */
35 struct _ECalBackendPrivate {
36         /* The source for this backend */
37         ESource *source;
38
39         /* URI, from source. This is cached, since we return const. */
40         char *uri;
41
42         /* The kind of components for this backend */
43         icalcomponent_kind kind;
44         
45         /* List of Cal objects */
46         GMutex *clients_mutex;
47         GList *clients;
48
49         GMutex *queries_mutex;
50         EList *queries;
51
52         /* ECalBackend to pass notifications on to */
53         ECalBackend *notification_proxy;
54 };
55
56 /* Property IDs */
57 enum props {
58         PROP_0,
59         PROP_SOURCE,
60         PROP_URI,
61         PROP_KIND
62 };
63
64 /* Signal IDs */
65 enum {
66         LAST_CLIENT_GONE,
67         OPENED,
68         REMOVED,
69         LAST_SIGNAL
70 };
71 static guint e_cal_backend_signals[LAST_SIGNAL];
72
73 static void e_cal_backend_class_init (ECalBackendClass *class);
74 static void e_cal_backend_init (ECalBackend *backend);
75 static void e_cal_backend_finalize (GObject *object);
76
77 #define CLASS(backend) (E_CAL_BACKEND_CLASS (G_OBJECT_GET_CLASS (backend)))
78
79 static GObjectClass *parent_class;
80
81 \f
82
83 /**
84  * e_cal_backend_get_type:
85  *
86  * Registers the #ECalBackend class if necessary, and returns the type ID
87  * associated to it.
88  *
89  * Return value: The type ID of the #ECalBackend class.
90  **/
91 GType
92 e_cal_backend_get_type (void)
93 {
94         static GType e_cal_backend_type = 0;
95
96         if (!e_cal_backend_type) {
97                 static GTypeInfo info = {
98                         sizeof (ECalBackendClass),
99                         (GBaseInitFunc) NULL,
100                         (GBaseFinalizeFunc) NULL,
101                         (GClassInitFunc) e_cal_backend_class_init,
102                         NULL, NULL,
103                         sizeof (ECalBackend),
104                         0,
105                         (GInstanceInitFunc) e_cal_backend_init,
106                 };
107                 e_cal_backend_type = g_type_register_static (G_TYPE_OBJECT, "ECalBackend", &info, 0);
108         }
109
110         return e_cal_backend_type;
111 }
112
113 static void
114 e_cal_backend_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
115 {
116         ECalBackend *backend;
117         ECalBackendPrivate *priv;
118         
119         backend = E_CAL_BACKEND (object);
120         priv = backend->priv;
121         
122         switch (property_id) {
123         case PROP_SOURCE:
124                 {
125                         ESource *new_source;
126
127                         new_source = g_value_get_object (value);
128                         if (new_source)
129                                 g_object_ref (new_source);
130
131                         if (priv->source)
132                                 g_object_unref (priv->source);
133
134                         priv->source = new_source;
135
136                         /* Cache the URI */
137                         if (new_source) {
138                                 g_free (priv->uri);
139                                 priv->uri = e_source_get_uri (priv->source);
140                         }
141                 }
142                 break;
143         case PROP_URI:
144                 if (!priv->source) {
145                         g_free (priv->uri);
146                         priv->uri = g_value_dup_string (value);
147                 }
148                 break;
149         case PROP_KIND:
150                 priv->kind = g_value_get_ulong (value);
151                 break;
152         default:
153                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
154                 break;
155         }
156 }
157
158 static void
159 e_cal_backend_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
160 {
161         ECalBackend *backend;
162         ECalBackendPrivate *priv;
163         
164         backend = E_CAL_BACKEND (object);
165         priv = backend->priv;
166
167         switch (property_id) {
168         case PROP_SOURCE:
169                 g_value_set_object (value, e_cal_backend_get_source (backend));
170                 break;
171         case PROP_URI:
172                 g_value_set_string (value, e_cal_backend_get_uri (backend));
173                 break;
174         case PROP_KIND:
175                 g_value_set_ulong (value, e_cal_backend_get_kind (backend));
176                 break;
177         default:
178                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
179                 break;
180         }
181 }
182
183 /* Class initialization function for the calendar backend */
184 static void
185 e_cal_backend_class_init (ECalBackendClass *class)
186 {
187         GObjectClass *object_class;
188
189         parent_class = (GObjectClass *) g_type_class_peek_parent (class);
190
191         object_class = (GObjectClass *) class;
192
193         object_class->set_property = e_cal_backend_set_property;
194         object_class->get_property = e_cal_backend_get_property;
195         object_class->finalize = e_cal_backend_finalize;
196
197         g_object_class_install_property (object_class, PROP_SOURCE, 
198                                          g_param_spec_object ("source", NULL, NULL, E_TYPE_SOURCE,
199                                                               G_PARAM_READABLE | G_PARAM_WRITABLE
200                                                               | G_PARAM_CONSTRUCT_ONLY));
201
202         g_object_class_install_property (object_class, PROP_URI, 
203                                          g_param_spec_string ("uri", NULL, NULL, "",
204                                                               G_PARAM_READABLE | G_PARAM_WRITABLE
205                                                               | G_PARAM_CONSTRUCT_ONLY));
206
207         g_object_class_install_property (object_class, PROP_KIND, 
208                                          g_param_spec_ulong ("kind", NULL, NULL, 
209                                                              ICAL_NO_COMPONENT, ICAL_XLICMIMEPART_COMPONENT, 
210                                                              ICAL_NO_COMPONENT,
211                                                              G_PARAM_READABLE | G_PARAM_WRITABLE
212                                                              | G_PARAM_CONSTRUCT_ONLY));        
213         e_cal_backend_signals[LAST_CLIENT_GONE] =
214                 g_signal_new ("last_client_gone",
215                               G_TYPE_FROM_CLASS (class),
216                               G_SIGNAL_RUN_FIRST,
217                               G_STRUCT_OFFSET (ECalBackendClass, last_client_gone),
218                               NULL, NULL,
219                               g_cclosure_marshal_VOID__VOID,
220                               G_TYPE_NONE, 0);
221         e_cal_backend_signals[OPENED] =
222                 g_signal_new ("opened",
223                               G_TYPE_FROM_CLASS (class),
224                               G_SIGNAL_RUN_FIRST,
225                               G_STRUCT_OFFSET (ECalBackendClass, opened),
226                               NULL, NULL,
227                               g_cclosure_marshal_VOID__ENUM,
228                               G_TYPE_NONE, 1,
229                               G_TYPE_INT);
230         e_cal_backend_signals[REMOVED] =
231                 g_signal_new ("removed",
232                               G_TYPE_FROM_CLASS (class),
233                               G_SIGNAL_RUN_FIRST,
234                               G_STRUCT_OFFSET (ECalBackendClass, removed),
235                               NULL, NULL,
236                               g_cclosure_marshal_VOID__ENUM,
237                               G_TYPE_NONE, 1,
238                               G_TYPE_INT);
239
240         class->last_client_gone = NULL;
241         class->opened = NULL;
242         class->obj_updated = NULL;
243
244         class->get_cal_address = NULL;
245         class->get_alarm_email_address = NULL;
246         class->get_static_capabilities = NULL;
247         class->open = NULL;
248         class->is_loaded = NULL;
249         class->is_read_only = NULL;
250         class->start_query = NULL;
251         class->get_mode = NULL;
252         class->set_mode = NULL; 
253         class->get_object = NULL;
254         class->get_default_object = NULL;
255         class->get_object_list = NULL;
256         class->get_free_busy = NULL;
257         class->get_changes = NULL;
258         class->discard_alarm = NULL;
259         class->create_object = NULL;
260         class->modify_object = NULL;
261         class->remove_object = NULL;
262         class->receive_objects = NULL;
263         class->send_objects = NULL;
264         class->get_timezone = NULL;
265         class->add_timezone = NULL;
266         class->set_default_timezone = NULL;
267 }
268
269 /* Object initialization func for the calendar backend */
270 static void
271 e_cal_backend_init (ECalBackend *backend)
272 {
273         ECalBackendPrivate *priv;
274
275         priv = g_new0 (ECalBackendPrivate, 1);
276         backend->priv = priv;
277
278         priv->clients = NULL;
279         priv->clients_mutex = g_mutex_new ();
280
281         /* FIXME bonobo_object_ref/unref? */
282         priv->queries = e_list_new((EListCopyFunc) g_object_ref, (EListFreeFunc) g_object_unref, NULL);
283         priv->queries_mutex = g_mutex_new ();
284 }
285
286 static void
287 e_cal_backend_finalize (GObject *object)
288 {
289         ECalBackend *backend = (ECalBackend *)object;
290         ECalBackendPrivate *priv;
291
292         priv = backend->priv;
293
294         g_assert (priv->clients == NULL);
295
296         g_object_unref (priv->queries);
297
298         g_mutex_free (priv->clients_mutex);
299         g_mutex_free (priv->queries_mutex);
300         
301         g_free (priv->uri);
302         g_object_unref (priv->source);
303         g_free (priv);
304
305         G_OBJECT_CLASS (parent_class)->finalize (object);
306 }
307
308 \f
309
310 /**
311  * e_cal_backend_get_source:
312  * @backend: An #ECalBackend object.
313  *
314  * Gets the #ESource associated with the given backend.
315  *
316  * Return value: The #ESource for the backend.
317  */
318 ESource *
319 e_cal_backend_get_source (ECalBackend *backend)
320 {
321         ECalBackendPrivate *priv;
322         
323         g_return_val_if_fail (backend != NULL, NULL);
324         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
325
326         priv = backend->priv;
327         
328         return priv->source;
329 }
330
331 /**
332  * e_cal_backend_get_uri:
333  * @backend: A calendar backend.
334  *
335  * Queries the URI of a calendar backend, which must already have an open
336  * calendar.
337  *
338  * Return value: The URI where the calendar is stored.
339  **/
340 const char *
341 e_cal_backend_get_uri (ECalBackend *backend)
342 {
343         ECalBackendPrivate *priv;
344         
345         g_return_val_if_fail (backend != NULL, NULL);
346         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
347
348         priv = backend->priv;
349         
350         return priv->uri;
351 }
352
353 /**
354  * e_cal_backend_get_kind:
355  * @backend: An #ECalBackend object.
356  *
357  * Gets the kind of components the given backend stores.
358  *
359  * Return value: The kind of components for this backend.
360  */
361 icalcomponent_kind
362 e_cal_backend_get_kind (ECalBackend *backend)
363 {
364         ECalBackendPrivate *priv;
365         
366         g_return_val_if_fail (backend != NULL, ICAL_NO_COMPONENT);
367         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), ICAL_NO_COMPONENT);
368
369         priv = backend->priv;
370         
371         return priv->kind;
372 }
373
374 static void
375 cal_destroy_cb (gpointer data, GObject *where_cal_was)
376 {
377         ECalBackend *backend = E_CAL_BACKEND (data);
378
379         e_cal_backend_remove_client (backend, (EDataCal *) where_cal_was);
380 }
381
382 static void
383 listener_died_cb (gpointer cnx, gpointer data)
384 {
385         EDataCal *cal = E_DATA_CAL (data);
386
387         e_cal_backend_remove_client (e_data_cal_get_backend (cal), cal);
388 }
389
390 static void
391 last_client_gone (ECalBackend *backend)
392 {
393         g_signal_emit (backend, e_cal_backend_signals[LAST_CLIENT_GONE], 0);
394 }
395
396 /**
397  * e_cal_backend_add_client:
398  * @backend: An ECalBackend object.
399  * @cal: An EDataCal object.
400  *
401  * Adds a new client to the given backend. For any event, the backend will
402  * notify all clients added via this function.
403  */
404 void
405 e_cal_backend_add_client (ECalBackend *backend, EDataCal *cal)
406 {
407         ECalBackendPrivate *priv;
408         
409         g_return_if_fail (backend != NULL);
410         g_return_if_fail (E_IS_CAL_BACKEND (backend));
411         g_return_if_fail (cal != NULL);
412         g_return_if_fail (E_IS_DATA_CAL (cal));
413
414         priv = backend->priv;
415         
416         bonobo_object_set_immortal (BONOBO_OBJECT (cal), TRUE);
417
418         g_object_weak_ref (G_OBJECT (cal), cal_destroy_cb, backend);
419
420         ORBit_small_listen_for_broken (e_data_cal_get_listener (cal), G_CALLBACK (listener_died_cb), cal);
421
422         g_mutex_lock (priv->clients_mutex);
423         priv->clients = g_list_append (priv->clients, cal);
424         g_mutex_unlock (priv->clients_mutex);
425 }
426
427 /**
428  * e_cal_backend_remove_client:
429  * @backend: An #ECalBackend object.
430  * @cal: An #EDataCal object.
431  *
432  * Removes a client from the list of connected clients to the given backend.
433  */
434 void
435 e_cal_backend_remove_client (ECalBackend *backend, EDataCal *cal)
436 {
437         ECalBackendPrivate *priv;
438         
439         /* XXX this needs a bit more thinking wrt the mutex - we
440            should be holding it when we check to see if clients is
441            NULL */
442         g_return_if_fail (backend != NULL);
443         g_return_if_fail (E_IS_CAL_BACKEND (backend));
444         g_return_if_fail (cal != NULL);
445         g_return_if_fail (E_IS_DATA_CAL (cal));
446
447         priv = backend->priv;
448
449         /* Disconnect */
450         g_mutex_lock (priv->clients_mutex);
451         priv->clients = g_list_remove (priv->clients, cal);
452         g_mutex_unlock (priv->clients_mutex);
453
454         /* When all clients go away, notify the parent factory about it so that
455          * it may decide whether to kill the backend or not.
456          */
457         if (!priv->clients)
458                 last_client_gone (backend);
459 }
460
461 /**
462  * e_cal_backend_add_query:
463  * @backend: An #ECalBackend object.
464  * @query: An #EDataCalView object.
465  *
466  * Adds a query to the list of live queries being run by the given backend.
467  * Doing so means that any listener on the query will get notified of any
468  * change that affect the live query.
469  */
470 void
471 e_cal_backend_add_query (ECalBackend *backend, EDataCalView *query)
472 {
473         g_return_if_fail (backend != NULL);
474         g_return_if_fail (E_IS_CAL_BACKEND (backend));
475
476         g_mutex_lock (backend->priv->queries_mutex);
477
478         e_list_append (backend->priv->queries, query);
479         
480         g_mutex_unlock (backend->priv->queries_mutex);
481 }
482
483 /**
484  * e_cal_backend_get_queries:
485  * @backend: An #ECalBackend object.
486  *
487  * Gets the list of live queries being run on the given backend.
488  *
489  * Return value: The list of live queries.
490  */
491 EList *
492 e_cal_backend_get_queries (ECalBackend *backend)
493 {
494         g_return_val_if_fail (backend != NULL, NULL);
495         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
496
497         return backend->priv->queries;
498 }
499
500
501 /**
502  * e_cal_backend_get_cal_address:
503  * @backend: A calendar backend.
504  *
505  * Queries the cal address associated with a calendar backend, which
506  * must already have an open calendar.
507  **/
508 void
509 e_cal_backend_get_cal_address (ECalBackend *backend, EDataCal *cal)
510 {
511         g_return_if_fail (backend != NULL);
512         g_return_if_fail (E_IS_CAL_BACKEND (backend));
513
514         g_assert (CLASS (backend)->get_cal_address != NULL);
515         (* CLASS (backend)->get_cal_address) (backend, cal);
516 }
517
518 void
519 e_cal_backend_notify_readonly (ECalBackend *backend, gboolean read_only)
520 {
521         ECalBackendPrivate *priv;
522         GList *l;
523
524         priv = backend->priv;
525
526         if (priv->notification_proxy) {
527                 e_cal_backend_notify_readonly (priv->notification_proxy, read_only);
528                 return;
529         }
530         for (l = priv->clients; l; l = l->next)
531                 e_data_cal_notify_read_only (l->data, GNOME_Evolution_Calendar_Success, read_only);
532 }
533
534 void
535 e_cal_backend_notify_cal_address (ECalBackend *backend, char *address)
536 {
537         ECalBackendPrivate *priv;
538         GList *l;
539
540         priv = backend->priv;
541
542         for (l = priv->clients; l; l = l->next)
543                 e_data_cal_notify_cal_address (l->data, GNOME_Evolution_Calendar_Success, address);
544 }
545
546 /**
547  * e_cal_backend_get_alarm_email_address:
548  * @backend: An #ECalBackend object.
549  * @cal: An #EDataCal object.
550  *
551  * Calls the get_alarm_email_address method on the given backend.
552  */
553 void
554 e_cal_backend_get_alarm_email_address (ECalBackend *backend, EDataCal *cal)
555 {
556         g_return_if_fail (backend != NULL);
557         g_return_if_fail (E_IS_CAL_BACKEND (backend));
558
559         g_assert (CLASS (backend)->get_alarm_email_address != NULL);
560         (* CLASS (backend)->get_alarm_email_address) (backend, cal);
561 }
562
563 /**
564  *e_cal_backend_get_alarm_email_address:
565  * @backend: An #ECalBackend object.
566  * @cal: An #EDataCal object.
567  *
568  * Calls the get_ldap_attribute method of the given backend.
569  */
570 void
571 e_cal_backend_get_ldap_attribute (ECalBackend *backend, EDataCal *cal)
572 {
573         g_return_if_fail (backend != NULL);
574         g_return_if_fail (E_IS_CAL_BACKEND (backend));
575
576         g_assert (CLASS (backend)->get_ldap_attribute != NULL);
577         (* CLASS (backend)->get_ldap_attribute) (backend, cal);
578 }
579
580 /**
581  * e_cal_backend_get_alarm_email_address:
582  * @backend: An #ECalBackend object.
583  * @cal: An #EDataCal object.
584  *
585  * Calls the get_static_capabilities method on the given backend.
586  */
587 void
588 e_cal_backend_get_static_capabilities (ECalBackend *backend, EDataCal *cal)
589 {
590         g_return_if_fail (backend != NULL);
591         g_return_if_fail (E_IS_CAL_BACKEND (backend));
592
593         g_assert (CLASS (backend)->get_static_capabilities != NULL);
594         (* CLASS (backend)->get_static_capabilities) (backend, cal);
595 }
596
597 /**
598  * e_cal_backend_open:
599  * @backend: A calendar backend.
600  * @cal: An #EDataCal object.
601  * @only_if_exists: Whether the calendar should be opened only if it already
602  * exists.  If FALSE, a new calendar will be created when the specified @uri
603  * does not exist.
604  * @username: User name to use for authentication (if needed).
605  * @password: Password for @username.
606  *
607  * Opens a calendar backend with data from a calendar stored at the specified
608  * URI.
609  */
610 void
611 e_cal_backend_open (ECalBackend *backend, EDataCal *cal, gboolean only_if_exists,
612                     const char *username, const char *password)
613 {
614         g_return_if_fail (backend != NULL);
615         g_return_if_fail (E_IS_CAL_BACKEND (backend));
616
617         g_assert (CLASS (backend)->open != NULL);
618         (* CLASS (backend)->open) (backend, cal, only_if_exists, username, password);
619 }
620
621 /**
622  * e_cal_backend_remove:
623  * @backend: A calendar backend.
624  * @cal: An #EDataCal object.
625  *
626  * Removes the calendar being accessed by the given backend.
627  */
628 void
629 e_cal_backend_remove (ECalBackend *backend, EDataCal *cal)
630 {
631         g_return_if_fail (backend != NULL);
632         g_return_if_fail (E_IS_CAL_BACKEND (backend));
633
634         g_assert (CLASS (backend)->remove != NULL);
635         (* CLASS (backend)->remove) (backend, cal);
636 }
637
638 /**
639  * e_cal_backend_is_loaded:
640  * @backend: A calendar backend.
641  * 
642  * Queries whether a calendar backend has been loaded yet.
643  * 
644  * Return value: TRUE if the backend has been loaded with data, FALSE
645  * otherwise.
646  */
647 gboolean
648 e_cal_backend_is_loaded (ECalBackend *backend)
649 {
650         gboolean result;
651
652         g_return_val_if_fail (backend != NULL, FALSE);
653         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
654
655         g_assert (CLASS (backend)->is_loaded != NULL);
656         result = (* CLASS (backend)->is_loaded) (backend);
657
658         return result;
659 }
660
661 /**
662  * e_cal_backend_is_read_only
663  * @backend: A calendar backend.
664  * @cal: An #EDataCal object.
665  *
666  * Queries whether a calendar backend is read only or not.
667  *
668  */
669 void
670 e_cal_backend_is_read_only (ECalBackend *backend, EDataCal *cal)
671 {
672         g_return_if_fail (backend != NULL);
673         g_return_if_fail (E_IS_CAL_BACKEND (backend));
674
675         g_assert (CLASS (backend)->is_read_only != NULL);
676         (* CLASS (backend)->is_read_only) (backend, cal);
677 }
678
679 /**
680  * e_cal_backend_start_query:
681  * @backend: A calendar backend.
682  * @query: The query to be started.
683  *
684  * Starts a new live query on the given backend.
685  */
686 void 
687 e_cal_backend_start_query (ECalBackend *backend, EDataCalView *query)
688 {
689         g_return_if_fail (backend != NULL);
690         g_return_if_fail (E_IS_CAL_BACKEND (backend));
691
692         g_assert (CLASS (backend)->start_query != NULL);
693         (* CLASS (backend)->start_query) (backend, query);
694 }
695
696 /**
697  * e_cal_backend_get_mode:
698  * @backend: A calendar backend. 
699  * 
700  * Queries whether a calendar backend is connected remotely.
701  * 
702  * Return value: The current mode the calendar is in
703  **/
704 CalMode
705 e_cal_backend_get_mode (ECalBackend *backend)
706 {
707         CalMode result;
708
709         g_return_val_if_fail (backend != NULL, FALSE);
710         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
711
712         g_assert (CLASS (backend)->get_mode != NULL);
713         result = (* CLASS (backend)->get_mode) (backend);
714
715         return result;
716 }
717
718
719 /**
720  * e_cal_backend_set_mode:
721  * @backend: A calendar backend
722  * @mode: Mode to change to
723  * 
724  * Sets the mode of the calendar 
725  */
726 void
727 e_cal_backend_set_mode (ECalBackend *backend, CalMode mode)
728 {
729         g_return_if_fail (backend != NULL);
730         g_return_if_fail (E_IS_CAL_BACKEND (backend));
731
732         g_assert (CLASS (backend)->set_mode != NULL);
733         (* CLASS (backend)->set_mode) (backend, mode);
734 }
735
736 /**
737  * e_cal_backend_get_default_object:
738  * @backend: A calendar backend.
739  * @cal: An #EDataCal object.
740  *
741  * Calls the get_default_object method on the given backend.
742  */
743 void
744 e_cal_backend_get_default_object (ECalBackend *backend, EDataCal *cal)
745 {
746         g_return_if_fail (backend != NULL);
747         g_return_if_fail (E_IS_CAL_BACKEND (backend));
748
749         g_assert (CLASS (backend)->get_default_object != NULL);
750         (* CLASS (backend)->get_default_object) (backend, cal);
751 }
752
753 /**
754  * e_cal_backend_get_object:
755  * @backend: A calendar backend.
756  * @cal: An #EDataCal object.
757  * @uid: Unique identifier for a calendar object.
758  * @rid: ID for the object's recurrence to get.
759  *
760  * Queries a calendar backend for a calendar object based on its unique
761  * identifier and its recurrence ID (if a recurrent appointment).
762  */
763 void
764 e_cal_backend_get_object (ECalBackend *backend, EDataCal *cal, const char *uid, const char *rid)
765 {
766         g_return_if_fail (backend != NULL);
767         g_return_if_fail (E_IS_CAL_BACKEND (backend));
768         g_return_if_fail (uid != NULL);
769
770         g_assert (CLASS (backend)->get_object != NULL);
771         (* CLASS (backend)->get_object) (backend, cal, uid, rid);
772 }
773
774 /**
775  * e_cal_backend_get_object_list:
776  * @backend: A calendar backend.
777  * @cal: An #EDataCal object.
778  * @sexp: Expression to search for.
779  * 
780  * Calls the get_object_list method on the given backend.
781  */
782 void
783 e_cal_backend_get_object_list (ECalBackend *backend, EDataCal *cal, const char *sexp)
784 {
785         g_return_if_fail (backend != NULL);
786         g_return_if_fail (E_IS_CAL_BACKEND (backend));
787
788         g_assert (CLASS (backend)->get_object_list != NULL);
789         (* CLASS (backend)->get_object_list) (backend, cal, sexp);
790 }
791
792 /**
793  * e_cal_backend_get_attachment_list:
794  * @backend: A calendar backend.
795  * @cal: An #EDataCal object.
796  * @uid: Unique identifier for a calendar object.
797  * @rid: ID for the object's recurrence to get.
798  *
799  * Queries a calendar backend for attachments present in a calendar object based 
800  * on its unique identifier and its recurrence ID (if a recurrent appointment).
801  */
802 void
803 e_cal_backend_get_attachment_list (ECalBackend *backend, EDataCal *cal, const char *uid, const char *rid)
804 {
805         g_return_if_fail (backend != NULL);
806         g_return_if_fail (E_IS_CAL_BACKEND (backend));
807         g_return_if_fail (uid != NULL);
808
809         g_assert (CLASS (backend)->get_object != NULL);
810         (* CLASS (backend)->get_attachment_list) (backend, cal, uid, rid);
811 }
812
813 /**
814  * e_cal_backend_get_free_busy:
815  * @backend: A calendar backend.
816  * @cal: An #EDataCal object.
817  * @users: List of users to get free/busy information for.
818  * @start: Start time for query.
819  * @end: End time for query.
820  * 
821  * Gets a free/busy object for the given time interval
822  */
823 void
824 e_cal_backend_get_free_busy (ECalBackend *backend, EDataCal *cal, GList *users, time_t start, time_t end)
825 {
826         g_return_if_fail (backend != NULL);
827         g_return_if_fail (E_IS_CAL_BACKEND (backend));
828         g_return_if_fail (start != -1 && end != -1);
829         g_return_if_fail (start <= end);
830
831         g_assert (CLASS (backend)->get_free_busy != NULL);
832         (* CLASS (backend)->get_free_busy) (backend, cal, users, start, end);
833 }
834
835 /**
836  * e_cal_backend_get_changes:
837  * @backend: A calendar backend.
838  * @cal: An #EDataCal object.
839  * @change_id: A unique uid for the callers change list
840  * 
841  * Builds a sequence of objects and the type of change that occurred on them since
842  * the last time the give change_id was seen
843  */
844 void
845 e_cal_backend_get_changes (ECalBackend *backend, EDataCal *cal, const char *change_id) 
846 {
847         g_return_if_fail (backend != NULL);
848         g_return_if_fail (E_IS_CAL_BACKEND (backend));
849         g_return_if_fail (change_id != NULL);
850
851         g_assert (CLASS (backend)->get_changes != NULL);
852         (* CLASS (backend)->get_changes) (backend, cal, change_id);
853 }
854
855 /**
856  * e_cal_backend_discard_alarm
857  * @backend: A calendar backend.
858  * @cal: An #EDataCal object.
859  * @uid: UID of the component to discard the alarm from.
860  * @auid: Alarm ID.
861  *
862  * Discards an alarm from the given component. This allows the specific backend
863  * to do whatever is needed to really discard the alarm.
864  */
865 void
866 e_cal_backend_discard_alarm (ECalBackend *backend, EDataCal *cal, const char *uid, const char *auid)
867 {
868         g_return_if_fail (backend != NULL);
869         g_return_if_fail (E_IS_CAL_BACKEND (backend));
870         g_return_if_fail (uid != NULL);
871         g_return_if_fail (auid != NULL);
872
873         g_assert (CLASS (backend)->discard_alarm != NULL);
874         (* CLASS (backend)->discard_alarm) (backend, cal, uid, auid);
875 }
876
877 /**
878  * e_cal_backend_create_object:
879  * @backend: A calendar backend.
880  * @cal: An #EDataCal object.
881  * @calobj: The object to create.
882  *
883  * Calls the create_object method on the given backend.
884  */
885 void
886 e_cal_backend_create_object (ECalBackend *backend, EDataCal *cal, const char *calobj)
887 {
888         g_return_if_fail (backend != NULL);
889         g_return_if_fail (E_IS_CAL_BACKEND (backend));
890         g_return_if_fail (calobj != NULL);
891
892         if (CLASS (backend)->create_object)
893                 (* CLASS (backend)->create_object) (backend, cal, calobj);
894         else
895                 e_data_cal_notify_object_created (cal, GNOME_Evolution_Calendar_PermissionDenied, NULL, NULL);
896 }
897
898 /**
899  * e_cal_backend_modify_object:
900  * @backend: A calendar backend.
901  * @cal: An #EDataCal object.
902  * @calobj: Object to be modified.
903  * @mod: Type of modification.
904  *
905  * Calls the modify_object method on the given backend.
906  */
907 void
908 e_cal_backend_modify_object (ECalBackend *backend, EDataCal *cal, const char *calobj, CalObjModType mod)
909 {
910         g_return_if_fail (backend != NULL);
911         g_return_if_fail (E_IS_CAL_BACKEND (backend));
912         g_return_if_fail (calobj != NULL);
913
914         if (CLASS (backend)->modify_object)
915                 (* CLASS (backend)->modify_object) (backend, cal, calobj, mod);
916         else
917                 e_data_cal_notify_object_removed (cal, GNOME_Evolution_Calendar_PermissionDenied, NULL, NULL, NULL);
918 }
919
920 /**
921  * e_cal_backend_remove_object:
922  * @backend: A calendar backend.
923  * @cal: An #EDataCal object.
924  * @uid: Unique identifier of the object to remove.
925  * @rid: A recurrence ID.
926  * @mod: Type of removal.
927  * 
928  * Removes an object in a calendar backend.  The backend will notify all of its
929  * clients about the change.
930  */
931 void
932 e_cal_backend_remove_object (ECalBackend *backend, EDataCal *cal, const char *uid, const char *rid, CalObjModType mod)
933 {
934         g_return_if_fail (backend != NULL);
935         g_return_if_fail (E_IS_CAL_BACKEND (backend));
936         g_return_if_fail (uid != NULL);
937
938         g_assert (CLASS (backend)->remove_object != NULL);
939         (* CLASS (backend)->remove_object) (backend, cal, uid, rid, mod);
940 }
941
942 /**
943  * e_cal_backend_receive_objects:
944  * @backend: A calendar backend.
945  * @cal: An #EDataCal object.
946  * @calobj: iCalendar object.
947  *
948  * Calls the receive_objects method on the given backend.
949  */
950 void
951 e_cal_backend_receive_objects (ECalBackend *backend, EDataCal *cal, const char *calobj)
952 {
953         g_return_if_fail (backend != NULL);
954         g_return_if_fail (E_IS_CAL_BACKEND (backend));
955         g_return_if_fail (calobj != NULL);
956
957         g_assert (CLASS (backend)->receive_objects != NULL);
958         (* CLASS (backend)->receive_objects) (backend, cal, calobj);
959 }
960
961 /**
962  * e_cal_backend_send_objects:
963  * @backend: A calendar backend.
964  * @cal: An #EDataCal object.
965  * @calobj: iCalendar object to be sent.
966  *
967  * Calls the send_objects method on the given backend.
968  */
969 void
970 e_cal_backend_send_objects (ECalBackend *backend, EDataCal *cal, const char *calobj)
971 {
972         g_return_if_fail (backend != NULL);
973         g_return_if_fail (E_IS_CAL_BACKEND (backend));
974         g_return_if_fail (calobj != NULL);
975
976         g_assert (CLASS (backend)->send_objects != NULL);
977         (* CLASS (backend)->send_objects) (backend, cal, calobj);
978 }
979
980 /**
981  * e_cal_backend_get_timezone:
982  * @backend: A calendar backend.
983  * @cal: An #EDataCal object.
984  * @tzid: Unique identifier of a VTIMEZONE object. Note that this must not be
985  * NULL.
986  * 
987  * Returns the icaltimezone* corresponding to the TZID, or NULL if the TZID
988  * can't be found.
989  */
990 void
991 e_cal_backend_get_timezone (ECalBackend *backend, EDataCal *cal, const char *tzid)
992 {
993         g_return_if_fail (backend != NULL);
994         g_return_if_fail (E_IS_CAL_BACKEND (backend));
995         g_return_if_fail (tzid != NULL);
996
997         g_assert (CLASS (backend)->get_timezone != NULL);
998         (* CLASS (backend)->get_timezone) (backend, cal, tzid);
999 }
1000
1001 /**
1002  * e_cal_backend_set_default_zone:
1003  * @backend: A calendar backend.
1004  * @cal: An #EDataCal object.
1005  * @tzobj: The timezone object, in a string.
1006  * 
1007  * Sets the default timezone for the calendar, which is used to resolve
1008  * DATE and floating DATE-TIME values. 
1009  */
1010 void
1011 e_cal_backend_set_default_zone (ECalBackend *backend, EDataCal *cal, const char *tzobj)
1012 {
1013         g_return_if_fail (backend != NULL);
1014         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1015         g_return_if_fail (tzobj != NULL);
1016
1017         (* CLASS (backend)->set_default_zone) (backend, cal, tzobj);
1018 }
1019
1020 /**
1021  * @deprecated This virual function should not be used in the backends, use
1022  * e_cal_backend_set_zone instead. This function restricts the default timezone
1023  * to be libical builtin timezone.
1024  *
1025  * e_cal_backend_set_default_timezone:
1026  * @backend: A calendar backend.
1027  * @cal: An #EDataCal object.
1028  * @tzid: The TZID identifying the timezone.
1029  * 
1030  * Sets the default timezone for the calendar, which is used to resolve
1031  * DATE and floating DATE-TIME values. 
1032  * 
1033  */
1034 void
1035 e_cal_backend_set_default_timezone (ECalBackend *backend, EDataCal *cal, const char *tzid)
1036 {
1037         g_return_if_fail (backend != NULL);
1038         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1039         g_return_if_fail (tzid != NULL);
1040
1041         (* CLASS (backend)->set_default_timezone) (backend, cal, tzid);
1042 }
1043
1044 /**
1045  * e_cal_backend_add_timezone
1046  * @backend: A calendar backend.
1047  * @cal: An #EDataCal object.
1048  * @tzobj: The timezone object, in a string.
1049  *
1050  * Add a timezone object to the given backend.
1051  */
1052 void
1053 e_cal_backend_add_timezone (ECalBackend *backend, EDataCal *cal, const char *tzobj)
1054 {
1055         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1056         g_return_if_fail (tzobj != NULL);
1057         g_return_if_fail (CLASS (backend)->add_timezone != NULL);
1058
1059         (* CLASS (backend)->add_timezone) (backend, cal, tzobj);
1060 }
1061
1062 /**
1063  * e_cal_backend_internal_get_default_timezone:
1064  * @backend: A calendar backend.
1065  *
1066  * Calls the internal_get_default_timezone method on the given backend.
1067  */
1068 icaltimezone *
1069 e_cal_backend_internal_get_default_timezone (ECalBackend *backend)
1070 {
1071         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
1072         g_return_val_if_fail (CLASS (backend)->internal_get_default_timezone != NULL, NULL);
1073
1074         return (* CLASS (backend)->internal_get_default_timezone) (backend);
1075 }
1076
1077 /**
1078  * e_cal_backend_internal_get_timezone:
1079  * @backend: A calendar backend.
1080  * @tzid: ID of the timezone to get.
1081  *
1082  * Calls the internal_get_timezone method on the given backend.
1083  */
1084 icaltimezone *
1085 e_cal_backend_internal_get_timezone (ECalBackend *backend, const char *tzid)
1086 {
1087         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
1088         g_return_val_if_fail (tzid != NULL, NULL);
1089         g_return_val_if_fail (CLASS (backend)->internal_get_timezone != NULL, NULL);
1090
1091         return (* CLASS (backend)->internal_get_timezone) (backend, tzid);
1092 }
1093
1094 /**
1095  * e_cal_backend_set_notification_proxy:
1096  * @backend: A calendar backend.
1097  * @proxy: The calendar backend to act as notification proxy.
1098  *
1099  * Sets the backend that will act as notification proxy for the given backend.
1100  */
1101 void
1102 e_cal_backend_set_notification_proxy (ECalBackend *backend, ECalBackend *proxy)
1103 {
1104         ECalBackendPrivate *priv;
1105
1106         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1107
1108         priv = backend->priv;
1109
1110         priv->notification_proxy = proxy;
1111 }
1112
1113 /**
1114  * e_cal_backend_notify_object_created:
1115  * @backend: A calendar backend.
1116  * @calobj: iCalendar representation of new object
1117  *
1118  * Notifies each of the backend's listeners about a new object.
1119  *
1120  * #e_data_cal_notify_object_created() calls this for you. You only need to
1121  * call e_cal_backend_notify_object_created() yourself to report objects
1122  * created by non-EDS clients.
1123  **/
1124 void
1125 e_cal_backend_notify_object_created (ECalBackend *backend, const char *calobj)
1126 {
1127         ECalBackendPrivate *priv;
1128         EList *queries;
1129         EIterator *iter;
1130         EDataCalView *query;
1131
1132         priv = backend->priv;
1133
1134         if (priv->notification_proxy) {
1135                 e_cal_backend_notify_object_created (priv->notification_proxy, calobj);
1136                 return;
1137         }
1138
1139         queries = e_cal_backend_get_queries (backend);
1140         iter = e_list_get_iterator (queries);
1141
1142         while (e_iterator_is_valid (iter)) {
1143                 query = QUERY (e_iterator_get (iter));
1144
1145                 bonobo_object_ref (query);
1146                 if (e_data_cal_view_object_matches (query, calobj))             
1147                         e_data_cal_view_notify_objects_added_1 (query, calobj);
1148                 bonobo_object_unref (query);
1149
1150                 e_iterator_next (iter);
1151         }
1152         g_object_unref (iter);
1153 }
1154
1155 static void
1156 match_query_and_notify (EDataCalView *query, const char *old_object, const char *object)
1157 {
1158         gboolean old_match = FALSE, new_match = FALSE;
1159
1160         if (old_object)
1161                 old_match = e_data_cal_view_object_matches (query, old_object);
1162
1163         new_match = e_data_cal_view_object_matches (query, object);
1164         if (old_match && new_match)
1165                 e_data_cal_view_notify_objects_modified_1 (query, object);
1166         else if (new_match)
1167                 e_data_cal_view_notify_objects_added_1 (query, object);
1168         else if (old_match) {
1169                 ECalComponent *comp = NULL;
1170         
1171                 comp = e_cal_component_new_from_string (old_object);
1172                 if (comp) {
1173                         ECalComponentId *id = e_cal_component_get_id (comp);
1174
1175                         e_data_cal_view_notify_objects_removed_1 (query, id);
1176                         
1177                         e_cal_component_free_id (id);
1178                         g_object_unref (comp);
1179                 }
1180         }
1181 }
1182
1183 /**
1184  * e_cal_backend_notify_view_progress:
1185  * @backend: A calendar backend.
1186  * @message: the UID of the removed object
1187  * @percent: percentage of the objects loaded in the view
1188  *
1189  * Notifies each of the backend's listeners about the view_progress in downloading the items.
1190  **/
1191 void
1192 e_cal_backend_notify_view_progress (ECalBackend *backend, const char *message, int percent)
1193 {
1194         ECalBackendPrivate *priv;
1195         EList *queries;
1196         EIterator *iter;
1197         EDataCalView *query;
1198
1199         priv = backend->priv;
1200
1201         if (priv->notification_proxy) {
1202                 e_cal_backend_notify_view_progress (priv->notification_proxy, message, percent);
1203                 return;
1204         }
1205
1206         queries = e_cal_backend_get_queries (backend);
1207         iter = e_list_get_iterator (queries);
1208
1209         while (e_iterator_is_valid (iter)) {
1210                 query = QUERY (e_iterator_get (iter));
1211
1212                 bonobo_object_ref (query);
1213
1214                 e_data_cal_view_notify_progress (query, message, percent);
1215
1216                 bonobo_object_unref (query);
1217
1218                 e_iterator_next (iter);
1219         }
1220         g_object_unref (iter);
1221 }
1222
1223 /**
1224  * e_cal_backend_notify_view_done:
1225  * @backend: A calendar backend.
1226  * @status: returns the status once the view is fully populated.
1227  *
1228  * Notifies each of the backend's listeners about the view_done in downloading the items.
1229  **/
1230 void
1231 e_cal_backend_notify_view_done (ECalBackend *backend, GNOME_Evolution_Calendar_CallStatus status)
1232 {
1233         ECalBackendPrivate *priv;
1234         EList *queries;
1235         EIterator *iter;
1236         EDataCalView *query;
1237
1238         priv = backend->priv;
1239
1240         if (priv->notification_proxy) {
1241                 e_cal_backend_notify_view_done (priv->notification_proxy, status);
1242                 return;
1243         }
1244
1245         queries = e_cal_backend_get_queries (backend);
1246         iter = e_list_get_iterator (queries);
1247
1248         while (e_iterator_is_valid (iter)) {
1249                 query = QUERY (e_iterator_get (iter));
1250
1251                 bonobo_object_ref (query);
1252
1253                 e_data_cal_view_notify_done (query, status);
1254
1255                 bonobo_object_unref (query);
1256
1257                 e_iterator_next (iter);
1258         }
1259         g_object_unref (iter);
1260 }
1261
1262 /**
1263  * e_cal_backend_notify_object_modified:
1264  * @backend: A calendar backend.
1265  * @old_object: iCalendar representation of the original form of the object
1266  * @object: iCalendar representation of the new form of the object
1267  *
1268  * Notifies each of the backend's listeners about a modified object.
1269  *
1270  * #e_data_cal_notify_object_modified() calls this for you. You only need to
1271  * call e_cal_backend_notify_object_modified() yourself to report objects
1272  * modified by non-EDS clients.
1273  **/
1274 void
1275 e_cal_backend_notify_object_modified (ECalBackend *backend, 
1276                                       const char *old_object, const char *object)
1277 {
1278         ECalBackendPrivate *priv;
1279         EList *queries;
1280         EIterator *iter;
1281         EDataCalView *query;
1282
1283         priv = backend->priv;
1284
1285         if (priv->notification_proxy) {
1286                 e_cal_backend_notify_object_modified (priv->notification_proxy, old_object, object);
1287                 return;
1288         }
1289
1290         queries = e_cal_backend_get_queries (backend);
1291         iter = e_list_get_iterator (queries);
1292
1293         while (e_iterator_is_valid (iter)) {
1294                 query = QUERY (e_iterator_get (iter));
1295
1296                 bonobo_object_ref (query);
1297                 match_query_and_notify (query, old_object, object);
1298                 bonobo_object_unref (query);
1299
1300                 e_iterator_next (iter);
1301         }
1302         g_object_unref (iter);
1303 }
1304
1305 /**
1306  * e_cal_backend_notify_object_removed:
1307  * @backend: A calendar backend.
1308  * @id: the Id of the removed object
1309  * @old_object: iCalendar representation of the removed object
1310  * @new_object: iCalendar representation of the object after the removal. This
1311  * only applies to recurrent appointments that had an instance removed. In that
1312  * case, this function notifies a modification instead of a removal.
1313  *
1314  * Notifies each of the backend's listeners about a removed object.
1315  *
1316  * e_data_cal_notify_object_removed() calls this for you. You only need to
1317  * call e_cal_backend_notify_object_removed() yourself to report objects
1318  * removed by non-EDS clients.
1319  **/
1320 void
1321 e_cal_backend_notify_object_removed (ECalBackend *backend, const ECalComponentId *id,
1322                                      const char *old_object, const char *object)
1323 {
1324         ECalBackendPrivate *priv;
1325         EList *queries;
1326         EIterator *iter;
1327         EDataCalView *query;
1328
1329         priv = backend->priv;
1330
1331         if (priv->notification_proxy) {
1332                 e_cal_backend_notify_object_removed (priv->notification_proxy, id, old_object, object);
1333                 return;
1334         }
1335
1336         queries = e_cal_backend_get_queries (backend);
1337         iter = e_list_get_iterator (queries);
1338
1339         while (e_iterator_is_valid (iter)) {
1340                 query = QUERY (e_iterator_get (iter));
1341
1342                 bonobo_object_ref (query);
1343
1344                 if (object == NULL) {
1345                         /* if object == NULL, it means the object has been completely
1346                            removed from the backend */
1347                         if (e_data_cal_view_object_matches (query, old_object))
1348                                 e_data_cal_view_notify_objects_removed_1 (query, id);
1349                 } else
1350                         match_query_and_notify (query, old_object, object);
1351
1352                 bonobo_object_unref (query);
1353
1354                 e_iterator_next (iter);
1355         }
1356         g_object_unref (iter);
1357 }
1358
1359 /**
1360  * e_cal_backend_notify_mode:
1361  * @backend: A calendar backend.
1362  * @status: Status of the mode set
1363  * @mode: the current mode
1364  *
1365  * Notifies each of the backend's listeners about the results of a
1366  * setMode call.
1367  **/
1368 void
1369 e_cal_backend_notify_mode (ECalBackend *backend,
1370                            GNOME_Evolution_Calendar_CalListener_SetModeStatus status, 
1371                            GNOME_Evolution_Calendar_CalMode mode)
1372 {
1373         ECalBackendPrivate *priv = backend->priv;
1374         GList *l;
1375
1376         if (priv->notification_proxy) {
1377                 e_cal_backend_notify_mode (priv->notification_proxy, status, mode);
1378                 return;
1379         }
1380
1381         for (l = priv->clients; l; l = l->next)
1382                 e_data_cal_notify_mode (l->data, status, mode);
1383 }
1384
1385 /**
1386  * e_cal_backend_notify_auth_required:
1387  * @backend: A calendar backend.
1388  *
1389  * Notifies each of the backend's listeners that authentication is required to
1390  * open the calendar.
1391  */
1392 void
1393 e_cal_backend_notify_auth_required (ECalBackend *backend)
1394 {
1395         ECalBackendPrivate *priv = backend->priv;
1396         GList *l;
1397                                                                                                                              
1398         for (l = priv->clients; l; l = l->next)
1399                 e_data_cal_notify_auth_required (l->data);
1400 }
1401
1402 /**
1403  * e_cal_backend_notify_error:
1404  * @backend: A calendar backend.
1405  * @message: Error message
1406  *
1407  * Notifies each of the backend's listeners about an error
1408  **/
1409 void
1410 e_cal_backend_notify_error (ECalBackend *backend, const char *message)
1411 {
1412         ECalBackendPrivate *priv = backend->priv;
1413         GList *l;
1414
1415         if (priv->notification_proxy) {
1416                 e_cal_backend_notify_error (priv->notification_proxy, message);
1417                 return;
1418         }
1419
1420         for (l = priv->clients; l; l = l->next)
1421                 e_data_cal_notify_error (l->data, message);
1422 }