Extending test-client-custom-summary to try e_book_client_get_contacts_uids()
[platform/upstream/evolution-data-server.git] / camel / camel-provider.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2  * camel-provider.c: provider framework
3  *
4  * Authors:
5  *  Bertrand Guiheneuf <bertrand@helixcode.com>
6  *  Dan Winship <danw@ximian.com>
7  *  Jeffrey Stedfast <fejj@ximian.com>
8  *
9  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.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
23  * USA
24  */
25
26 /* FIXME: Shouldn't we add a version number to providers ? */
27
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31
32 #include <errno.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <sys/types.h>
36
37 #include <glib/gi18n-lib.h>
38 #include <glib/gstdio.h>
39 #include <gmodule.h>
40
41 #include "camel-provider.h"
42 #include "camel-string-utils.h"
43 #include "camel-vee-store.h"
44 #include "camel-win32.h"
45
46 /* table of CamelProviderModule's */
47 static GHashTable *module_table;
48 /* table of CamelProvider's */
49 static GHashTable *provider_table;
50 static GRecMutex provider_lock;
51
52 #define LOCK()          (g_rec_mutex_lock(&provider_lock))
53 #define UNLOCK()        (g_rec_mutex_unlock(&provider_lock))
54
55 /* The vfolder provider is always available */
56 static CamelProvider vee_provider = {
57         "vfolder",
58         N_("Virtual folder email provider"),
59
60         N_("For reading mail as a query of another set of folders"),
61
62         "vfolder",
63
64         CAMEL_PROVIDER_IS_STORAGE | CAMEL_PROVIDER_IS_LOCAL,
65         CAMEL_URL_NEED_PATH | CAMEL_URL_PATH_IS_ABSOLUTE | CAMEL_URL_FRAGMENT_IS_PATH,
66
67         NULL,   /* extra conf */
68
69         NULL,   /* port providers */
70
71         /* ... */
72 };
73
74 static GOnce setup_once = G_ONCE_INIT;
75
76 static void
77 provider_register_internal (CamelProvider *provider)
78 {
79         CamelProviderConfEntry *conf;
80         CamelProviderPortEntry *port;
81         GList *link;
82         gint ii;
83
84         g_return_if_fail (provider != NULL);
85         g_return_if_fail (provider->protocol != NULL);
86
87         LOCK ();
88
89         if (g_hash_table_lookup (provider_table, provider->protocol) != NULL) {
90                 g_warning (
91                         "Trying to re-register CamelProvider for protocol '%s'",
92                         provider->protocol);
93                 UNLOCK ();
94                 return;
95         }
96
97         /* Translate all strings here */
98 #define P_(string) dgettext (provider->translation_domain, string)
99
100         provider->name = P_(provider->name);
101         provider->description = P_(provider->description);
102
103         conf = provider->extra_conf;
104         if (conf != NULL) {
105                 for (ii = 0; conf[ii].type != CAMEL_PROVIDER_CONF_END; ii++) {
106                         if (conf[ii].text != NULL)
107                                 conf[ii].text = P_(conf[ii].text);
108                 }
109         }
110
111         for (link = provider->authtypes; link != NULL; link = link->next) {
112                 CamelServiceAuthType *auth = link->data;
113
114                 auth->name = P_(auth->name);
115                 auth->description = P_(auth->description);
116         }
117
118         if (provider->port_entries != NULL) {
119                 provider->url_flags |= CAMEL_URL_NEED_PORT;
120                 port = provider->port_entries;
121                 for (ii = 0; port[ii].port != 0; ii++)
122                         if (port[ii].desc != NULL)
123                                 port[ii].desc = P_(port[ii].desc);
124         } else {
125                 provider->url_flags &= ~CAMEL_URL_NEED_PORT;
126         }
127
128         g_hash_table_insert (
129                 provider_table,
130                 (gpointer) provider->protocol, provider);
131
132         UNLOCK ();
133 }
134
135 static gpointer
136 provider_setup (gpointer param)
137 {
138         module_table = g_hash_table_new (
139                 (GHashFunc) camel_strcase_hash,
140                 (GEqualFunc) camel_strcase_equal);
141         provider_table = g_hash_table_new (
142                 (GHashFunc) camel_strcase_hash,
143                 (GEqualFunc) camel_strcase_equal);
144
145         vee_provider.object_types[CAMEL_PROVIDER_STORE] = CAMEL_TYPE_VEE_STORE;
146         vee_provider.url_hash = camel_url_hash;
147         vee_provider.url_equal = camel_url_equal;
148         provider_register_internal (&vee_provider);
149
150         return NULL;
151 }
152
153 /**
154  * camel_provider_init:
155  *
156  * Initialize the Camel provider system by reading in the .urls
157  * files in the provider directory and creating a hash table mapping
158  * URLs to module names.
159  *
160  * A .urls file has the same initial prefix as the shared library it
161  * correspond to, and consists of a series of lines containing the URL
162  * protocols that that library handles.
163  *
164  * TODO: This should be pathed?
165  * TODO: This should be plugin-d?
166  **/
167 void
168 camel_provider_init (void)
169 {
170         GDir *dir;
171         const gchar *entry;
172         gchar *p, *name, buf[80];
173         CamelProviderModule *m;
174         static gint loaded = 0;
175
176         g_once (&setup_once, provider_setup, NULL);
177
178         if (loaded)
179                 return;
180
181         loaded = 1;
182
183         dir = g_dir_open (CAMEL_PROVIDERDIR, 0, NULL);
184         if (!dir) {
185                 g_warning (
186                         "Could not open camel provider directory (%s): %s",
187                         CAMEL_PROVIDERDIR, g_strerror (errno));
188                 return;
189         }
190
191         while ((entry = g_dir_read_name (dir))) {
192                 FILE *fp;
193
194                 p = strrchr (entry, '.');
195                 if (!p || strcmp (p, ".urls") != 0)
196                         continue;
197
198                 name = g_strdup_printf ("%s/%s", CAMEL_PROVIDERDIR, entry);
199                 fp = g_fopen (name, "r");
200                 if (!fp) {
201                         g_warning (
202                                 "Could not read provider info file %s: %s",
203                                 name, g_strerror (errno));
204                         g_free (name);
205                         continue;
206                 }
207
208                 p = strrchr (name, '.');
209                 if (p)
210                         strcpy (p, "." G_MODULE_SUFFIX);
211
212                 m = g_malloc0 (sizeof (*m));
213                 m->path = name;
214
215                 while ((fgets (buf, sizeof (buf), fp))) {
216                         buf[sizeof (buf) - 1] = '\0';
217                         p = strchr (buf, '\n');
218                         if (p)
219                                 *p = '\0';
220
221                         if (*buf) {
222                                 gchar *protocol = g_strdup (buf);
223
224                                 m->types = g_slist_prepend (m->types, protocol);
225                                 g_hash_table_insert (module_table, protocol, m);
226                         }
227                 }
228
229                 fclose (fp);
230         }
231
232         g_dir_close (dir);
233 }
234
235 /**
236  * camel_provider_load:
237  * @path: the path to a shared library
238  * @error: return location for a #GError, or %NULL
239  *
240  * Loads the provider at @path, and calls its initialization function,
241  * passing @session as an argument. The provider should then register
242  * itself with @session.
243  *
244  * Returns: %TRUE on success, %FALSE on failure
245  **/
246 gboolean
247 camel_provider_load (const gchar *path,
248                      GError **error)
249 {
250         GModule *module;
251         CamelProvider *(*provider_module_init) (void);
252
253         g_once (&setup_once, provider_setup, NULL);
254
255         if (!g_module_supported ()) {
256                 g_set_error (
257                         error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
258                         _("Could not load %s: Module loading "
259                         "not supported on this system."), path);
260                 return FALSE;
261         }
262
263         module = g_module_open (path, G_MODULE_BIND_LAZY);
264         if (module == NULL) {
265                 g_set_error (
266                         error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
267                         _("Could not load %s: %s"),
268                         path, g_module_error ());
269                 return FALSE;
270         }
271
272         if (!g_module_symbol (module, "camel_provider_module_init",
273                               (gpointer *) &provider_module_init)) {
274                 g_set_error (
275                         error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
276                         _("Could not load %s: No initialization "
277                         "code in module."), path);
278                 g_module_close (module);
279                 return FALSE;
280         }
281
282         provider_module_init ();
283
284         return TRUE;
285 }
286
287 /**
288  * camel_provider_register:
289  * @provider: provider object
290  *
291  * Registers a provider.
292  **/
293 void
294 camel_provider_register (CamelProvider *provider)
295 {
296         g_once (&setup_once, provider_setup, NULL);
297
298         provider_register_internal (provider);
299 }
300
301 static gint
302 provider_compare (gconstpointer a,
303                   gconstpointer b)
304 {
305         const CamelProvider *cpa = (const CamelProvider *) a;
306         const CamelProvider *cpb = (const CamelProvider *) b;
307
308         return strcmp (cpa->name, cpb->name);
309 }
310
311 static void
312 add_to_list (gpointer key,
313              gpointer value,
314              gpointer user_data)
315 {
316         GList **list = user_data;
317
318         *list = g_list_prepend(*list, value);
319 }
320
321 /**
322  * camel_session_list_providers:
323  * @session: the session
324  * @load: whether or not to load in providers that are not already loaded
325  *
326  * This returns a list of available providers in this session. If @load
327  * is %TRUE, it will first load in all available providers that haven't
328  * yet been loaded.
329  *
330  * Free the returned list with g_list_free().  The #CamelProvider structs
331  * in the list are owned by Camel and should not be modified or freed.
332  *
333  * Returns: a #GList of #CamelProvider structs
334  **/
335 GList *
336 camel_provider_list (gboolean load)
337 {
338         GList *list = NULL;
339
340         /* provider_table can be NULL, so initialize it */
341         if (G_UNLIKELY (provider_table == NULL))
342                 camel_provider_init ();
343
344         g_return_val_if_fail (provider_table != NULL, NULL);
345
346         LOCK ();
347
348         if (load) {
349                 GList *w;
350
351                 g_hash_table_foreach (module_table, add_to_list, &list);
352                 for (w = list; w; w = w->next) {
353                         CamelProviderModule *m = w->data;
354                         GError *error = NULL;
355
356                         if (!m->loaded) {
357                                 camel_provider_load (m->path, &error);
358                                 m->loaded = 1;
359                         }
360
361                         if (error != NULL) {
362                                 g_critical (
363                                         "%s: %s", G_STRFUNC,
364                                         error->message);
365                                 g_error_free (error);
366                         }
367                 }
368                 g_list_free (list);
369                 list = NULL;
370         }
371
372         g_hash_table_foreach (provider_table, add_to_list, &list);
373
374         UNLOCK ();
375
376         list = g_list_sort (list, provider_compare);
377
378         return list;
379 }
380
381 /**
382  * camel_provider_get:
383  * @protocol: a #CamelProvider protocol name
384  * @error: return location for a #GError, or %NULL
385  *
386  * Returns the registered #CamelProvider for @protocol, loading it
387  * from disk if necessary.  If no #CamelProvider can be found for
388  * @protocol, or the provider module fails to load, the function
389  * sets @error and returns %NULL.
390  *
391  * The returned #CamelProvider is owned by Camel and should not be
392  * modified or freed.
393  *
394  * Returns: a #CamelProvider for %protocol, or %NULL
395  **/
396 CamelProvider *
397 camel_provider_get (const gchar *protocol,
398                     GError **error)
399 {
400         CamelProvider *provider = NULL;
401
402         g_return_val_if_fail (protocol != NULL, NULL);
403         g_return_val_if_fail (provider_table != NULL, NULL);
404
405         LOCK ();
406
407         provider = g_hash_table_lookup (provider_table, protocol);
408         if (provider == NULL) {
409                 CamelProviderModule *module;
410
411                 module = g_hash_table_lookup (module_table, protocol);
412                 if (module != NULL && !module->loaded) {
413                         module->loaded = 1;
414                         if (!camel_provider_load (module->path, error))
415                                 goto fail;
416                 }
417                 provider = g_hash_table_lookup (provider_table, protocol);
418         }
419
420         if (provider == NULL)
421                 g_set_error (
422                         error, CAMEL_SERVICE_ERROR,
423                         CAMEL_SERVICE_ERROR_URL_INVALID,
424                         _("No provider available for protocol '%s'"),
425                         protocol);
426 fail:
427         UNLOCK ();
428
429         return provider;
430 }
431
432 /**
433  * camel_provider_auto_detect:
434  * @provider: camel provider
435  * @url: a #CamelURL
436  * @auto_detected: output hash table of auto-detected values
437  * @error: return location for a #GError, or %NULL
438  *
439  * After filling in the standard Username/Hostname/Port/Path settings
440  * (which must be set in @url), if the provider supports it, you
441  * may wish to have the provider auto-detect further settings based on
442  * the aformentioned settings.
443  *
444  * If the provider does not support auto-detection, @auto_detected
445  * will be set to %NULL. Otherwise the provider will attempt to
446  * auto-detect whatever it can and file them into @auto_detected. If
447  * for some reason it cannot auto-detect anything (not enough
448  * information provided in @url?) then @auto_detected will be
449  * set to %NULL and an exception may be set to explain why it failed.
450  *
451  * Returns: 0 on success or -1 on fail.
452  **/
453 gint
454 camel_provider_auto_detect (CamelProvider *provider,
455                             CamelURL *url,
456                             GHashTable **auto_detected,
457                             GError **error)
458 {
459         g_return_val_if_fail (provider != NULL, -1);
460
461         if (provider->auto_detect) {
462                 return provider->auto_detect (url, auto_detected, error);
463         } else {
464                 *auto_detected = NULL;
465                 return 0;
466         }
467 }