Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / servers / exchange / storage / e-folder-exchange.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /* Copyright (C) 2002-2004 Novell, Inc.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of version 2 of the GNU Lesser General Public
7  * License as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this program; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30
31 #include <libxml/parser.h>
32 #include <libxml/tree.h>
33 #include <libxml/xmlmemory.h>
34
35 #include <glib.h>
36 #include <glib/gstdio.h>
37
38 #include "libedataserver/e-source-list.h"
39 #include "libedataserver/e-data-server-util.h"
40 #include "libedataserver/e-xml-utils.h"
41
42 #include "e-folder-exchange.h"
43 #include "e2k-path.h"
44 #include "e2k-uri.h"
45 #include "exchange-account.h"
46 #include "exchange-esource.h"
47 #include "exchange-hierarchy.h"
48
49 #define d(x)
50
51 struct _EFolderExchangePrivate {
52         ExchangeHierarchy *hier;
53         char *internal_uri, *permanent_uri;
54         char *outlook_class, *storage_dir;
55         char *path;
56         long long int folder_size;
57         gboolean has_subfolders;
58         gboolean rescan_tree;
59 };
60
61 #define PARENT_TYPE E_TYPE_FOLDER
62 static EFolderClass *parent_class = NULL;
63
64 #define EF_CLASS(hier) (E_FOLDER_CLASS (G_OBJECT_GET_CLASS (hier)))
65
66 static void dispose (GObject *object);
67 static void finalize (GObject *object);
68
69 static void
70 class_init (GObjectClass *object_class)
71 {
72         parent_class = g_type_class_ref (PARENT_TYPE);
73
74         /* methods */
75         object_class->dispose = dispose;
76         object_class->finalize = finalize;
77 }
78
79 static void
80 init (GObject *object)
81 {
82         EFolderExchange *folder = E_FOLDER_EXCHANGE (object);
83
84         folder->priv = g_new0 (EFolderExchangePrivate, 1);
85         folder->priv->rescan_tree = TRUE;
86 }
87
88 static void
89 dispose (GObject *object)
90 {
91         EFolderExchange *folder = E_FOLDER_EXCHANGE (object);
92
93         if (folder->priv->hier) {
94                 g_object_unref (folder->priv->hier);
95                 folder->priv->hier = NULL;
96         }
97
98         G_OBJECT_CLASS (parent_class)->dispose (object);
99 }
100
101 static void
102 finalize (GObject *object)
103 {
104         EFolderExchange *folder = E_FOLDER_EXCHANGE (object);
105
106         g_free (folder->priv->internal_uri);
107         g_free (folder->priv->permanent_uri);
108         g_free (folder->priv->outlook_class);
109         g_free (folder->priv->storage_dir);
110         g_free (folder->priv->path);
111         g_free (folder->priv);
112
113         G_OBJECT_CLASS (parent_class)->finalize (object);
114 }
115
116 E2K_MAKE_TYPE (e_folder_exchange, EFolderExchange, class_init, init, PARENT_TYPE)
117
118 static char *
119 sanitize_path (const char *path)
120 {
121         gchar **comps;
122         char *new_path = NULL;
123
124         if (!path)
125                 return g_strdup("");    /* ??? or NULL? */
126
127         comps = g_strsplit (path, ";", 2);
128         if (comps[1])
129                 new_path = g_strdup_printf ("%s%s", comps[0], comps[1]);
130         else if (comps[0])
131                 new_path = g_strdup (comps[0]);
132
133         g_strfreev (comps);
134         return new_path;        
135 }
136
137 #define d(x) 
138
139 /**
140  * e_folder_exchange_new:
141  * @hier: the #ExchangeHierarchy containing the new folder
142  * @name: the display name of the folder
143  * @type: the Evolution type of the folder (eg, "mail")
144  * @outlook_class: the Outlook IPM class of the folder (eg, "IPM.Note")
145  * @physical_uri: the "exchange:" URI of the folder
146  * @internal_uri: the "http:" URI of the folder
147  *
148  * Return value: a new #EFolderExchange
149  **/
150 EFolder *
151 e_folder_exchange_new (ExchangeHierarchy *hier, const char *name,
152                        const char *type, const char *outlook_class,
153                        const char *physical_uri, const char *internal_uri)
154 {
155         EFolderExchange *efe;
156         EFolder *ef;
157         char *sanitized_path;
158
159         g_return_val_if_fail (EXCHANGE_IS_HIERARCHY (hier), NULL);
160         g_return_val_if_fail (name != NULL, NULL);
161         g_return_val_if_fail (type != NULL, NULL);
162         g_return_val_if_fail (physical_uri != NULL, NULL);
163         g_return_val_if_fail (internal_uri != NULL, NULL);
164
165         d(g_print ("e_folder_exchange_new: name=[%s], type=[%s], internal_uri=[%s], physical_uri=[%s]\n", 
166                    name, type, internal_uri, physical_uri));
167
168         efe = g_object_new (E_TYPE_FOLDER_EXCHANGE, NULL);
169         ef = (EFolder *)efe;
170
171         e_folder_construct (ef, name, type, "");
172
173         efe->priv->hier = hier;
174         g_object_ref (hier);
175
176         efe->priv->internal_uri = g_strdup (internal_uri);
177         e_folder_set_physical_uri (ef, physical_uri);
178
179         sanitized_path = sanitize_path (e2k_uri_path (physical_uri));
180         e2k_uri_decode (sanitized_path);
181         efe->priv->path = sanitized_path;
182         d(g_print ("e_folder_exchange_new: sanitized=[%s]\n", sanitized_path));
183
184         efe->priv->outlook_class = g_strdup (outlook_class);
185
186         /* Add ESources */
187         if (hier->type == EXCHANGE_HIERARCHY_PERSONAL || 
188             hier->type == EXCHANGE_HIERARCHY_FAVORITES) {
189                 
190                 if ((strcmp (type, "calendar") == 0) ||
191                     (strcmp (type, "calendar/public") == 0)) {
192                         add_folder_esource (hier->account, 
193                                             EXCHANGE_CALENDAR_FOLDER, 
194                                             name, 
195                                             physical_uri);
196                 }
197                 else if ((strcmp (type, "tasks") == 0) ||
198                          (strcmp (type, "tasks/public") == 0)) {
199                         add_folder_esource (hier->account, 
200                                             EXCHANGE_TASKS_FOLDER, 
201                                             name, 
202                                             physical_uri);
203                 }
204                 else if ((strcmp (type, "contacts") == 0) ||
205                          (strcmp (type, "contacts/public") == 0)) {
206                         add_folder_esource (hier->account, 
207                                             EXCHANGE_CONTACTS_FOLDER, 
208                                             name, 
209                                             physical_uri);
210                 }
211         }
212         return ef;
213 }
214
215 /**
216  * e_folder_exchange_get_internal_uri:
217  * @folder: an #EFolderExchange
218  *
219  * Returns the folder's internal (http/https) URI. The caller
220  * should not cache this value, since it may change if the server
221  * sends a redirect when we try to use it.
222  *
223  * Return value: @folder's internal (http/https) URI
224  **/
225 const char *
226 e_folder_exchange_get_internal_uri (EFolder *folder)
227 {
228         g_return_val_if_fail (E_IS_FOLDER_EXCHANGE (folder), NULL);
229
230         return E_FOLDER_EXCHANGE (folder)->priv->internal_uri;
231 }
232
233 /**
234  * e_folder_exchange_set_internal_uri:
235  * @folder: an #EFolderExchange
236  * @internal_uri: new internal_uri value
237  *
238  * Updates @folder's internal URI to reflect a redirection response
239  * from the server.
240  **/
241 void
242 e_folder_exchange_set_internal_uri (EFolder *folder, const char *internal_uri)
243 {
244         EFolderExchange *efe;
245
246         g_return_if_fail (E_IS_FOLDER_EXCHANGE (folder));
247         g_return_if_fail (internal_uri != NULL);
248
249         efe = E_FOLDER_EXCHANGE (folder);
250         g_free (efe->priv->internal_uri);
251         efe->priv->internal_uri = g_strdup (internal_uri);
252 }
253
254 /**
255  * e_folder_exchange_get_path:
256  * @folder: an #EFolderExchange
257  *
258  * Return value: @folder's path within its Evolution storage
259  **/
260 const char *
261 e_folder_exchange_get_path (EFolder *folder)
262 {
263         g_return_val_if_fail (E_IS_FOLDER_EXCHANGE (folder), NULL);
264
265         return E_FOLDER_EXCHANGE (folder)->priv->path;
266 }
267
268 /**
269  * e_folder_exchange_get_permanent_uri:
270  * @folder: an #EFolderExchange
271  *
272  * Returns the folder's permanent URI. See docs/entryids for more
273  * details.
274  *
275  * Return value: @folder's permanent URI
276  **/
277 const char *
278 e_folder_exchange_get_permanent_uri (EFolder *folder)
279 {
280         g_return_val_if_fail (E_IS_FOLDER_EXCHANGE (folder), NULL);
281
282         return E_FOLDER_EXCHANGE (folder)->priv->permanent_uri;
283 }
284
285 /**
286  * e_folder_exchange_set_permanent_uri:
287  * @folder: an #EFolderExchange
288  * @permanent_uri: permanent_uri value
289  *
290  * Sets @folder's permanent URI (which must, for obvious reasons, have
291  * previously been unset).
292  **/
293 void
294 e_folder_exchange_set_permanent_uri (EFolder *folder, const char *permanent_uri)
295 {
296         EFolderExchange *efe;
297
298         g_return_if_fail (E_IS_FOLDER_EXCHANGE (folder));
299
300         efe = E_FOLDER_EXCHANGE (folder);
301         g_return_if_fail (efe->priv->permanent_uri == NULL && permanent_uri != NULL);
302
303         efe->priv->permanent_uri = g_strdup (permanent_uri);
304 }
305
306 /**
307  * e_folder_exchange_get_folder_size:
308  * @folder: an #EFolderExchange
309  *
310  * Returns the folder's size. See docs/entryids for more
311  * details.
312  *
313  * Return value: @folder's size
314  **/
315 long long int
316 e_folder_exchange_get_folder_size (EFolder *folder)
317 {
318         g_return_val_if_fail (E_IS_FOLDER_EXCHANGE (folder), -1);
319
320         return E_FOLDER_EXCHANGE (folder)->priv->folder_size;
321 }
322
323 /**
324  * e_folder_exchange_set_folder_size:
325  * @folder: an #EFolderExchange
326  * @folder_size: folder size
327  *
328  * Sets @folder's folder_size
329  **/
330 void
331 e_folder_exchange_set_folder_size (EFolder *folder, long long int folder_size)
332 {
333         EFolderExchange *efe;
334
335         g_return_if_fail (E_IS_FOLDER_EXCHANGE (folder));
336
337         efe = E_FOLDER_EXCHANGE (folder);
338
339         efe->priv->folder_size = folder_size;
340 }
341
342
343 /**
344  * e_folder_exchange_get_has_subfolders:
345  * @folder: an #EFolderExchange
346  *
347  * Return value: whether or not @folder has subfolders
348  **/
349 gboolean
350 e_folder_exchange_get_has_subfolders (EFolder *folder)
351 {
352         g_return_val_if_fail (E_IS_FOLDER_EXCHANGE (folder), FALSE);
353
354         return E_FOLDER_EXCHANGE (folder)->priv->has_subfolders;
355 }
356
357 /**
358  * e_folder_exchange_set_has_subfolders
359  * @folder: an #EFolderExchange
360  * @has_subfolders: whether or not @folder has subfolders
361  *
362  * Sets @folder's has_subfolders flag.
363  **/
364 void
365 e_folder_exchange_set_has_subfolders (EFolder *folder,
366                                       gboolean has_subfolders)
367 {
368         g_return_if_fail (E_IS_FOLDER_EXCHANGE (folder));
369
370         E_FOLDER_EXCHANGE (folder)->priv->has_subfolders = has_subfolders;
371 }
372
373 /**
374  * e_folder_exchange_get_rescan_tree:
375  * @folder: an #EFolderExchange
376  *
377  * Return value: whether or not to rescan @folder tree
378  **/
379 gboolean
380 e_folder_exchange_get_rescan_tree (EFolder *folder)
381 {
382         g_return_val_if_fail (E_IS_FOLDER_EXCHANGE (folder), FALSE);
383
384         return E_FOLDER_EXCHANGE (folder)->priv->rescan_tree;
385 }
386
387 /**
388  * e_folder_exchange_set_rescan_tree
389  * @folder: an #EFolderExchange
390  * @rescan_tree: whether or not @folder needs to be rescanned
391  *
392  * Sets @folder's has_subfolders flag.
393  **/
394 void
395 e_folder_exchange_set_rescan_tree (EFolder *folder,
396                                    gboolean rescan_tree)
397 {
398         g_return_if_fail (E_IS_FOLDER_EXCHANGE (folder));
399
400         E_FOLDER_EXCHANGE (folder)->priv->rescan_tree = rescan_tree;
401 }
402
403 /**
404  * e_folder_exchange_get_outlook_class:
405  * @folder: an #EFolderExchange
406  *
407  * Return value: @folder's Outlook IPM class
408  **/
409 const char *
410 e_folder_exchange_get_outlook_class (EFolder *folder)
411 {
412         g_return_val_if_fail (E_IS_FOLDER_EXCHANGE (folder), NULL);
413
414         return E_FOLDER_EXCHANGE (folder)->priv->outlook_class;
415 }
416
417 /**
418  * e_folder_exchange_get_hierarchy
419  * @folder: an #EFolderExchange
420  *
421  * Return value: @folder's hierarchy
422  **/
423 ExchangeHierarchy *
424 e_folder_exchange_get_hierarchy (EFolder *folder)
425 {
426         g_return_val_if_fail (E_IS_FOLDER_EXCHANGE (folder), NULL);
427
428         return E_FOLDER_EXCHANGE (folder)->priv->hier;
429 }       
430
431 /**
432  * e_folder_exchange_get_storage_file:
433  * @folder: an #EFolderExchange
434  * @filename: name of a file
435  *
436  * This returns a unique filename ending in @filename in the local
437  * storage space reserved for @folder.
438  *
439  * Return value: the full filename, which must be freed.
440  **/
441 char *
442 e_folder_exchange_get_storage_file (EFolder *folder, const char *filename)
443 {
444         EFolderExchange *efe;
445         char *path;
446
447         g_return_val_if_fail (E_IS_FOLDER_EXCHANGE (folder), NULL);
448
449         efe = (EFolderExchange *)folder;
450
451         if (!efe->priv->storage_dir) {
452                 efe->priv->storage_dir = e_path_to_physical (
453                         efe->priv->hier->account->storage_dir,
454                         efe->priv->path);
455                 g_mkdir_with_parents (efe->priv->storage_dir, 0755);
456         }
457
458         path = g_build_filename (efe->priv->storage_dir, filename, NULL);
459         return path;
460 }
461
462
463 /**
464  * e_folder_exchange_save_to_file:
465  * @folder: the folder
466  * @filename: a filename
467  *
468  * Saves all relevant information about @folder to @filename.
469  *
470  * Return value: success or failure
471  **/
472 gboolean
473 e_folder_exchange_save_to_file (EFolder *folder, const char *filename)
474 {
475         xmlDoc *doc;
476         xmlNode *root;
477         const char *name, *type, *outlook_class;
478         const char *physical_uri, *internal_uri, *permanent_uri;
479         char *folder_size;
480         long long int fsize;
481         int status;
482
483         name = e_folder_get_name (folder);
484         type = e_folder_get_type_string (folder);
485         outlook_class = e_folder_exchange_get_outlook_class (folder);
486         physical_uri = e_folder_get_physical_uri (folder);
487         internal_uri = e_folder_exchange_get_internal_uri (folder);
488         permanent_uri = e_folder_exchange_get_permanent_uri (folder);
489
490         g_return_val_if_fail (name && type && physical_uri && internal_uri,
491                               FALSE);
492
493         if ((fsize = e_folder_exchange_get_folder_size (folder)) >= 0)
494                 folder_size = g_strdup_printf ("%llu", fsize);
495         else
496                 return FALSE;
497
498         doc = xmlNewDoc ("1.0");
499         root = xmlNewDocNode (doc, NULL, "connector-folder", NULL);
500         xmlNewProp (root, "version", "1");
501         xmlDocSetRootElement (doc, root);
502
503         xmlNewChild (root, NULL, "displayname", name);
504         xmlNewChild (root, NULL, "type", type);
505         xmlNewChild (root, NULL, "outlook_class", outlook_class);
506         xmlNewChild (root, NULL, "physical_uri", physical_uri);
507         xmlNewChild (root, NULL, "internal_uri", internal_uri);
508         xmlNewChild (root, NULL, "folder_size", folder_size);
509         if (permanent_uri)
510                 xmlNewChild (root, NULL, "permanent_uri", permanent_uri);
511
512         status = e_xml_save_file (filename, doc);
513
514         if (status < 0)
515                 g_unlink (filename);
516
517         xmlFreeDoc (doc);
518
519         g_free (folder_size);
520
521         return status == 0;
522 }
523
524 /**
525  * e_folder_exchange_new_from_file:
526  * @hier: the hierarchy to create the folder under
527  * @filename: a filename
528  *
529  * Loads information about a folder from a saved file.
530  *
531  * Return value: the folder, or %NULL on a failed load.
532  **/
533 EFolder *
534 e_folder_exchange_new_from_file (ExchangeHierarchy *hier, const char *filename)
535 {
536         EFolder *folder = NULL;
537         xmlDoc *doc;
538         xmlNode *root, *node;
539         char *version, *display_name = NULL;
540         char *type = NULL, *outlook_class = NULL;
541         char *physical_uri = NULL, *internal_uri = NULL;
542         char *permanent_uri = NULL;
543         char *folder_size = NULL;
544
545         doc = e_xml_parse_file (filename);
546
547         if (!doc)
548                 return NULL;
549
550         root = xmlDocGetRootElement (doc);
551         if (root == NULL || strcmp (root->name, "connector-folder") != 0) {
552                 xmlFreeDoc (doc);
553                 return NULL;
554         }
555         version = xmlGetProp (root, "version");
556         if (!version) {
557                 xmlFreeDoc (doc);
558                 return NULL;
559         }
560         if (strcmp (version, "1") != 0) {
561                 xmlFreeDoc (doc);
562                 xmlFree (version);
563                 return NULL;
564         }
565         xmlFree (version);
566
567         node = e_xml_get_child_by_name (root, "displayname");
568         if (!node)
569                 goto done;
570         display_name = xmlNodeGetContent (node);
571
572         node = e_xml_get_child_by_name (root, "type");
573         if (!node)
574                 goto done;
575         type = xmlNodeGetContent (node);
576
577         node = e_xml_get_child_by_name (root, "outlook_class");
578         if (!node)
579                 goto done;
580         outlook_class = xmlNodeGetContent (node);
581
582         node = e_xml_get_child_by_name (root, "physical_uri");
583         if (!node)
584                 goto done;
585         physical_uri = xmlNodeGetContent (node);
586
587         node = e_xml_get_child_by_name (root, "internal_uri");
588         if (!node)
589                 goto done;
590         internal_uri = xmlNodeGetContent (node);
591
592         if (!display_name || !type || !physical_uri || !internal_uri)
593                 goto done;
594
595         folder = e_folder_exchange_new (hier, display_name,
596                                         type, outlook_class,
597                                         physical_uri, internal_uri);
598
599         node = e_xml_get_child_by_name (root, "permanent_uri");
600         if (node) {
601                 permanent_uri = xmlNodeGetContent (node);
602                 e_folder_exchange_set_permanent_uri (folder, permanent_uri);
603         }
604
605         node = e_xml_get_child_by_name (root, "folder_size");
606         if (node) {
607                 folder_size = xmlNodeGetContent (node);
608                 e_folder_exchange_set_folder_size (folder, atoi (folder_size));
609         }
610
611  done:
612         xmlFree (display_name);
613         xmlFree (type);
614         xmlFree (outlook_class);
615         xmlFree (physical_uri);
616         xmlFree (internal_uri);
617         xmlFree (permanent_uri);
618         xmlFree (folder_size);
619         xmlFreeDoc (doc);
620
621         return folder;
622 }
623
624
625
626 /* E2kContext wrappers */
627 #define E_FOLDER_EXCHANGE_CONTEXT(efe) (exchange_account_get_context (((EFolderExchange *)efe)->priv->hier->account))
628 #define E_FOLDER_EXCHANGE_URI(efe) (((EFolderExchange *)efe)->priv->internal_uri)
629
630 /**
631  * e_folder_exchange_propfind:
632  * @folder: the folder
633  * @op: pointer to an #E2kOperation to use for cancellation
634  * @props: array of properties to find
635  * @nprops: length of @props
636  * @results: on return, the results
637  * @nresults: length of @results
638  *
639  * Performs a PROPFIND operation on @folder. This is a convenience
640  * wrapper around e2k_context_propfind(), qv.
641  *
642  * Return value: the HTTP status
643  **/
644 E2kHTTPStatus
645 e_folder_exchange_propfind (EFolder *folder, E2kOperation *op,
646                             const char **props, int nprops,
647                             E2kResult **results, int *nresults)
648 {
649         g_return_val_if_fail (E_IS_FOLDER_EXCHANGE (folder), E2K_HTTP_MALFORMED);
650
651         return e2k_context_propfind (
652                 E_FOLDER_EXCHANGE_CONTEXT (folder), op,
653                 E_FOLDER_EXCHANGE_URI (folder),
654                 props, nprops, results, nresults);
655 }
656
657 /**
658  * e_folder_exchange_bpropfind_start:
659  * @folder: the folder
660  * @op: pointer to an #E2kOperation to use for cancellation
661  * @hrefs: array of URIs, relative to @folder
662  * @nhrefs: length of @hrefs
663  * @props: array of properties to find
664  * @nprops: length of @props
665  *
666  * Begins a BPROPFIND (bulk PROPFIND) operation on @folder for @hrefs.
667  * This is a convenience wrapper around e2k_context_bpropfind_start(),
668  * qv.
669  *
670  * Return value: an iterator for getting the results
671  **/
672 E2kResultIter *
673 e_folder_exchange_bpropfind_start (EFolder *folder, E2kOperation *op,
674                                    const char **hrefs, int nhrefs,
675                                    const char **props, int nprops)
676 {
677         g_return_val_if_fail (E_IS_FOLDER_EXCHANGE (folder), NULL);
678
679         return e2k_context_bpropfind_start (
680                 E_FOLDER_EXCHANGE_CONTEXT (folder), op,
681                 E_FOLDER_EXCHANGE_URI (folder),
682                 hrefs, nhrefs, props, nprops);
683 }
684
685 /**
686  * e_folder_exchange_search_start:
687  * @folder: the folder
688  * @op: pointer to an #E2kOperation to use for cancellation
689  * @props: the properties to search for
690  * @nprops: size of @props array
691  * @rn: the search restriction
692  * @orderby: if non-%NULL, the field to sort the search results by
693  * @ascending: %TRUE for an ascending search, %FALSE for descending.
694  *
695  * Begins a SEARCH on the contents of @folder. This is a convenience
696  * wrapper around e2k_context_search_start(), qv.
697  *
698  * Return value: an iterator for returning the search results
699  **/
700 E2kResultIter *
701 e_folder_exchange_search_start (EFolder *folder, E2kOperation *op,
702                                 const char **props, int nprops,
703                                 E2kRestriction *rn, const char *orderby,
704                                 gboolean ascending)
705 {
706         g_return_val_if_fail (E_IS_FOLDER_EXCHANGE (folder), NULL);
707
708         return e2k_context_search_start (
709                 E_FOLDER_EXCHANGE_CONTEXT (folder), op,
710                 E_FOLDER_EXCHANGE_URI (folder),
711                 props, nprops, rn, orderby, ascending);
712 }
713
714 /**
715  * e_folder_exchange_subscribe:
716  * @folder: the folder to subscribe to notifications on
717  * @type: the type of notification to subscribe to
718  * @min_interval: the minimum interval (in seconds) between
719  * notifications.
720  * @callback: the callback to call when a notification has been
721  * received
722  * @user_data: data to pass to @callback.
723  *
724  * This subscribes to change notifications of the given @type on
725  * @folder. This is a convenience wrapper around
726  * e2k_context_subscribe(), qv.
727  **/
728 void
729 e_folder_exchange_subscribe (EFolder *folder,
730                              E2kContextChangeType type, int min_interval,
731                              E2kContextChangeCallback callback,
732                              gpointer user_data)
733 {
734         g_return_if_fail (E_IS_FOLDER_EXCHANGE (folder));
735
736         e2k_context_subscribe (E_FOLDER_EXCHANGE_CONTEXT (folder),
737                                E_FOLDER_EXCHANGE_URI (folder),
738                                type, min_interval, callback, user_data);
739 }
740
741 /**
742  * e_folder_exchange_unsubscribe:
743  * @folder: the folder to unsubscribe from
744  *
745  * Unsubscribes to all notifications on @folder. This is a convenience
746  * wrapper around e2k_context_unsubscribe(), qv.
747  **/
748 void
749 e_folder_exchange_unsubscribe (EFolder *folder)
750 {
751         E2kContext *ctx;
752
753         g_return_if_fail (E_IS_FOLDER_EXCHANGE (folder));
754
755         /* FIXME : This is a hack as of now. The free_folder in mail-stub
756         gets called when we are in offline and the context is NULL then. */
757         ctx = E_FOLDER_EXCHANGE_CONTEXT (folder);
758         if (ctx) {
759                 e2k_context_unsubscribe (E_FOLDER_EXCHANGE_CONTEXT (folder),
760                                          E_FOLDER_EXCHANGE_URI (folder));
761         }
762 }
763
764 /**
765  * e_folder_exchange_transfer_start:
766  * @source: the source folder
767  * @op: pointer to an #E2kOperation to use for cancellation
768  * @dest: the destination folder
769  * @source_hrefs: an array of hrefs to move, relative to @source_folder
770  * @delete_originals: whether or not to delete the original objects
771  *
772  * Starts a BMOVE or BCOPY (depending on @delete_originals) operation
773  * on @source. This is a convenience wrapper around
774  * e2k_context_transfer_start(), qv.
775  *
776  * Return value: the iterator for the results
777  **/
778 E2kResultIter *
779 e_folder_exchange_transfer_start (EFolder *source, E2kOperation *op,
780                                   EFolder *dest, GPtrArray *source_hrefs,
781                                   gboolean delete_originals)
782 {
783         g_return_val_if_fail (E_IS_FOLDER_EXCHANGE (source), NULL);
784
785         return e2k_context_transfer_start (E_FOLDER_EXCHANGE_CONTEXT (source), op,
786                                            E_FOLDER_EXCHANGE_URI (source),
787                                            E_FOLDER_EXCHANGE_URI (dest),
788                                            source_hrefs, delete_originals);
789 }
790
791 /**
792  * e_folder_exchange_put_new:
793  * @folder: the folder to PUT the new item into
794  * @op: pointer to an #E2kOperation to use for cancellation
795  * @object_name: base name of the new object (not URI-encoded)
796  * @test_callback: callback to use to test possible object URIs
797  * @user_data: data for @test_callback
798  * @content_type: MIME Content-Type of the data
799  * @body: data to PUT
800  * @length: length of @body
801  * @location: if not %NULL, will contain the Location of the POSTed
802  * object on return
803  * @repl_uid: if not %NULL, will contain the Repl-UID of the POSTed
804  * object on return
805  *
806  * PUTs data into @folder with a new name based on @object_name. This
807  * is a convenience wrapper around e2k_context_put_new(), qv.
808  *
809  * Return value: the HTTP status
810  **/
811 E2kHTTPStatus
812 e_folder_exchange_put_new (EFolder *folder,
813                            E2kOperation *op,
814                            const char *object_name, 
815                            E2kContextTestCallback test_callback,
816                            gpointer user_data,
817                            const char *content_type,
818                            const char *body, int length,
819                            char **location, char **repl_uid)
820 {
821         g_return_val_if_fail (E_IS_FOLDER_EXCHANGE (folder), E2K_HTTP_MALFORMED);
822
823         return e2k_context_put_new (E_FOLDER_EXCHANGE_CONTEXT (folder), op,
824                                     E_FOLDER_EXCHANGE_URI (folder),
825                                     object_name, test_callback, user_data,
826                                     content_type, body, length,
827                                     location, repl_uid);
828 }
829
830 /**
831  * e_folder_exchange_proppatch_new:
832  * @folder: the folder to PROPPATCH a new object in
833  * @op: pointer to an #E2kOperation to use for cancellation
834  * @object_name: base name of the new object (not URI-encoded)
835  * @test_callback: callback to use to test possible object URIs
836  * @user_data: data for @test_callback
837  * @props: the properties to set/remove
838  * @location: if not %NULL, will contain the Location of the
839  * PROPPATCHed object on return
840  * @repl_uid: if not %NULL, will contain the Repl-UID of the
841  * PROPPATCHed object on return
842  *
843  * PROPPATCHes data into @folder with a new name based on
844  * @object_name. This is a convenience wrapper around
845  * e2k_context_proppatch_new(), qv.
846
847  * Return value: the HTTP status
848  **/
849 E2kHTTPStatus
850 e_folder_exchange_proppatch_new (EFolder *folder, E2kOperation *op,
851                                  const char *object_name,
852                                  E2kContextTestCallback test_callback,
853                                  gpointer user_data,
854                                  E2kProperties *props,
855                                  char **location, char **repl_uid)
856 {
857         g_return_val_if_fail (E_IS_FOLDER_EXCHANGE (folder), E2K_HTTP_MALFORMED);
858
859         return e2k_context_proppatch_new (E_FOLDER_EXCHANGE_CONTEXT (folder), op,
860                                           E_FOLDER_EXCHANGE_URI (folder),
861                                           object_name,
862                                           test_callback, user_data,
863                                           props,
864                                           location, repl_uid);
865 }
866
867 /**
868  * e_folder_exchange_bproppatch_start:
869  * @folder: the folder
870  * @op: pointer to an #E2kOperation to use for cancellation
871  * @hrefs: array of URIs, relative to @folder
872  * @nhrefs: length of @hrefs
873  * @props: the properties to set/remove
874  * @create: whether or not to create the objects if they do not exist
875  *
876  * Begins BPROPPATCHing @hrefs under @folder. This is a convenience
877  * wrapper around e2k_context_bproppatch_start(), qv.
878  *
879  * Return value: an iterator for getting the results of the BPROPPATCH
880  **/
881 E2kResultIter *
882 e_folder_exchange_bproppatch_start (EFolder *folder, E2kOperation *op,
883                                     const char **hrefs, int nhrefs,
884                                     E2kProperties *props, gboolean create)
885 {
886         g_return_val_if_fail (E_IS_FOLDER_EXCHANGE (folder), NULL);
887
888         return e2k_context_bproppatch_start (E_FOLDER_EXCHANGE_CONTEXT (folder), op,
889                                              E_FOLDER_EXCHANGE_URI (folder),
890                                              hrefs, nhrefs, props, create);
891 }
892
893 /**
894  * e_folder_exchange_bdelete_start:
895  * @folder: the folder
896  * @op: pointer to an #E2kOperation to use for cancellation
897  * @hrefs: array of URIs, relative to @folder, to delete
898  * @nhrefs: length of @hrefs
899  *
900  * Begins a BDELETE (bulk DELETE) operation in @folder for @hrefs.
901  * This is a convenience wrapper around e2k_context_bdelete_start(),
902  * qv.
903  *
904  * Return value: an iterator for returning the results
905  **/
906 E2kResultIter *
907 e_folder_exchange_bdelete_start (EFolder *folder, E2kOperation *op,
908                                  const char **hrefs, int nhrefs)
909 {
910         g_return_val_if_fail (E_IS_FOLDER_EXCHANGE (folder), NULL);
911
912         return e2k_context_bdelete_start (E_FOLDER_EXCHANGE_CONTEXT (folder), op,
913                                           E_FOLDER_EXCHANGE_URI (folder),
914                                           hrefs, nhrefs);
915 }
916
917 /**
918  * e_folder_exchange_mkcol:
919  * @folder: the folder to create
920  * @op: pointer to an #E2kOperation to use for cancellation
921  * @props: properties to set on the new folder, or %NULL
922  * @permanent_url: if not %NULL, will contain the permanent URL of the
923  * new folder on return
924  *
925  * Performs a MKCOL operation to create @folder, with optional
926  * additional properties. This is a convenience wrapper around
927  * e2k_context_mkcol(), qv.
928  *
929  * Return value: the HTTP status
930  **/
931 E2kHTTPStatus
932 e_folder_exchange_mkcol (EFolder *folder, E2kOperation *op,
933                          E2kProperties *props,
934                          char **permanent_url)
935 {
936         g_return_val_if_fail (E_IS_FOLDER_EXCHANGE (folder), E2K_HTTP_MALFORMED);
937
938         return e2k_context_mkcol (E_FOLDER_EXCHANGE_CONTEXT (folder), op,
939                                   E_FOLDER_EXCHANGE_URI (folder),
940                                   props, permanent_url);
941 }
942
943 /**
944  * e_folder_exchange_delete:
945  * @folder: the folder to delete
946  * @op: pointer to an #E2kOperation to use for cancellation
947  *
948  * Attempts to DELETE @folder. This is a convenience wrapper around
949  * e2k_context_delete(), qv.
950  *
951  * Return value: the HTTP status
952  **/
953 E2kHTTPStatus
954 e_folder_exchange_delete (EFolder *folder, E2kOperation *op)
955 {
956         ExchangeHierarchy *hier;
957         const char *folder_type, *physical_uri;
958
959         g_return_val_if_fail (E_IS_FOLDER_EXCHANGE (folder), E2K_HTTP_MALFORMED);
960         /* remove ESources */
961         hier = e_folder_exchange_get_hierarchy (folder); 
962
963         if (hier->type == EXCHANGE_HIERARCHY_PERSONAL ||
964             hier->type == EXCHANGE_HIERARCHY_FAVORITES) {
965                 folder_type = e_folder_get_type_string (folder);
966                 physical_uri = e_folder_get_physical_uri (folder);
967
968                 if ((strcmp (folder_type, "calendar") == 0) ||
969                     (strcmp (folder_type, "calendar/public") == 0)) {
970                         remove_folder_esource (hier->account, 
971                                                EXCHANGE_CALENDAR_FOLDER,
972                                                physical_uri);
973                 }
974                 else if ((strcmp (folder_type, "tasks") == 0) ||
975                          (strcmp (folder_type, "tasks/public") == 0)) {
976                         remove_folder_esource (hier->account,
977                                                EXCHANGE_TASKS_FOLDER,
978                                                physical_uri);
979                 }
980                 else if ((strcmp (folder_type, "contacts") == 0) ||
981                          (strcmp (folder_type, "contacts/public") == 0)) { 
982                         remove_folder_esource (hier->account,
983                                                EXCHANGE_CONTACTS_FOLDER,
984                                                physical_uri);
985                 }
986         }
987
988         return e2k_context_delete (E_FOLDER_EXCHANGE_CONTEXT (folder), op,
989                                    E_FOLDER_EXCHANGE_URI (folder));
990 }
991
992 /**
993  * e_folder_exchange_transfer_dir:
994  * @source: source folder
995  * @op: pointer to an #E2kOperation to use for cancellation
996  * @dest: destination folder
997  * @delete_original: whether or not to delete the original folder
998  * @permanent_url: if not %NULL, will contain the permanent URL of the
999  * new folder on return
1000  *
1001  * Performs a MOVE or COPY (depending on @delete_original) operation
1002  * on @source. This is a convenience wrapper around
1003  * e2k_context_transfer_dir(), qv.
1004  *
1005  * Return value: the HTTP status
1006  **/
1007 E2kHTTPStatus
1008 e_folder_exchange_transfer_dir (EFolder *source, E2kOperation *op,
1009                                 EFolder *dest, gboolean delete_original,
1010                                 char **permanent_url)
1011 {
1012         g_return_val_if_fail (E_IS_FOLDER_EXCHANGE (source), E2K_HTTP_MALFORMED);
1013
1014         return e2k_context_transfer_dir (E_FOLDER_EXCHANGE_CONTEXT (source), op,
1015                                          E_FOLDER_EXCHANGE_URI (source),
1016                                          E_FOLDER_EXCHANGE_URI (dest),
1017                                          delete_original, permanent_url);
1018 }