ftp://ftp.redhat.com/pub/redhat/linux/rawhide/SRPMS/SRPMS/gnome-vfs2-2.3.8-1.src.rpm
[gnome-vfs-httpcaptive.git] / libgnomevfs / gnome-vfs-method.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /* gnome-vfs-method.c - Handling of access methods in the GNOME
3    Virtual File System.
4
5    Copyright (C) 1999 Free Software Foundation
6
7    The Gnome Library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Library General Public License as
9    published by the Free Software Foundation; either version 2 of the
10    License, or (at your option) any later version.
11
12    The Gnome Library is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Library General Public License for more details.
16
17    You should have received a copy of the GNU Library General Public
18    License along with the Gnome Library; see the file COPYING.LIB.  If not,
19    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20    Boston, MA 02111-1307, USA.
21
22    Author: Ettore Perazzoli <ettore@gnu.org> */
23
24 #include <config.h>
25 #include "gnome-vfs-method.h"
26
27 #include "gnome-vfs-configuration.h"
28 #include "gnome-vfs-private.h"
29 #include <gmodule.h>
30 #include <libgnomevfs/gnome-vfs-module.h>
31 #include <libgnomevfs/gnome-vfs-transform.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <sys/types.h>
35 #include <unistd.h>
36
37 #define GNOME_VFS_MODULE_INIT      "vfs_module_init"
38 #define GNOME_VFS_MODULE_TRANSFORM "vfs_module_transform"
39 #define GNOME_VFS_MODULE_SHUTDOWN  "vfs_module_shutdown"
40
41 struct _ModuleElement {
42         char *name;
43         const char *args;
44         GnomeVFSMethod *method;
45         GnomeVFSTransform *transform;
46         int nusers;
47 };
48 typedef struct _ModuleElement ModuleElement;
49
50 static gboolean method_already_initialized = FALSE;
51
52 static GHashTable *module_hash = NULL;
53 G_LOCK_DEFINE_STATIC (gnome_vfs_method_init);
54 GStaticRecMutex module_hash_lock = G_STATIC_REC_MUTEX_INIT;
55
56 static GList *module_path_list = NULL;
57
58 \f
59 static gboolean
60 init_hash_table (void)
61 {
62         module_hash = g_hash_table_new (g_str_hash, g_str_equal);
63
64         return TRUE;
65 }
66
67 static gboolean
68 install_path_list (const gchar *user_path_list)
69 {
70         const gchar *p, *oldp;
71
72         /* Notice that this assumes the list has already been locked.  */
73
74         oldp = user_path_list;
75         while (1) {
76                 gchar *elem;
77
78                 p = strchr (oldp, ':');
79
80                 if (p == NULL) {
81                         if (*oldp != '\0') {
82                                 elem = g_strdup (oldp);
83                                 module_path_list = g_list_append
84                                                        (module_path_list, elem);
85                         }
86                         break;
87                 } else if (p != oldp) {
88                         elem = g_strndup (oldp, p - oldp);
89                         module_path_list = g_list_append (module_path_list,
90                                                           elem);
91                 } else {
92                         elem = NULL;
93                 }
94
95                 oldp = p + 1;
96         }
97
98         return TRUE;
99 }
100
101 static gboolean
102 init_path_list (void)
103 {
104         const gchar *user_path_list;
105
106         if (module_path_list != NULL)
107                 return TRUE;
108
109         /* User-supplied path.  */
110
111         user_path_list = getenv ("GNOME_VFS_MODULE_PATH");
112         if (user_path_list != NULL) {
113                 if (! install_path_list (user_path_list))
114                         return FALSE;
115         }
116
117         /* Default path.  It comes last so that users can override it.  */
118
119         module_path_list = g_list_append (module_path_list,
120                                           g_strdup (GNOME_VFS_MODULE_DIR));
121
122         return TRUE;
123 }
124
125 gboolean
126 gnome_vfs_method_init (void)
127 {
128         G_LOCK (gnome_vfs_method_init);
129
130         if (method_already_initialized)
131                 goto gnome_vfs_method_init_out;
132
133         if (! init_hash_table ())
134                 goto gnome_vfs_method_init_out;
135         if (! init_path_list ())
136                 goto gnome_vfs_method_init_out;
137
138         method_already_initialized = TRUE;
139
140  gnome_vfs_method_init_out:
141         G_UNLOCK (gnome_vfs_method_init);
142
143         return method_already_initialized;
144 }
145
146 static void
147 load_module (const gchar *module_name, const char *method_name, const char *args,
148              GnomeVFSMethod **method, GnomeVFSTransform **transform)
149 {
150         GModule *module;
151         GnomeVFSMethod *temp_method = NULL;
152         GnomeVFSTransform *temp_transform = NULL;
153         
154         GnomeVFSMethodInitFunc init_function = NULL;
155         GnomeVFSTransformInitFunc transform_function = NULL;
156         GnomeVFSMethodShutdownFunc shutdown_function = NULL;
157
158         *method = NULL;
159         *transform = NULL;
160
161         module = g_module_open (module_name, G_MODULE_BIND_LAZY);
162         if (module == NULL) {
163                 g_warning ("Cannot load module `%s' (%s)", module_name, g_module_error ());
164                 return;
165         }
166
167         g_module_symbol (module, GNOME_VFS_MODULE_INIT,
168                          (gpointer *) &init_function);
169         g_module_symbol (module, GNOME_VFS_MODULE_TRANSFORM,
170                          (gpointer *) &transform_function);
171         g_module_symbol (module, GNOME_VFS_MODULE_SHUTDOWN,
172                          (gpointer *) &shutdown_function);
173         
174         if ((init_function == NULL || shutdown_function == NULL) &&
175             (transform_function == NULL)) {
176                 g_warning ("module '%s' has no init function; may be an out-of-date module", module_name);
177                 return;
178         }
179
180         if (init_function)
181                 temp_method = (* init_function) (method_name, args);
182
183         if (temp_method == NULL && init_function) {
184                 g_warning ("module '%s' returned a NULL handle", module_name);
185                 return;
186         }
187
188         if (temp_method != NULL) {
189                 /* Some basic checks */
190                 if (temp_method->method_table_size == 0) {
191                         g_warning ("module '%s' has 0 table size", module_name);
192                         return;
193                 } else if (temp_method->method_table_size > (0x100 * sizeof (GnomeVFSMethod))) {
194                         g_warning ("module '%s' has unreasonable table size, perhaps it is using the old GnomeVFSMethod struct?", module_name);
195                         return;
196                 } else if (!VFS_METHOD_HAS_FUNC(temp_method, open)) {
197                         g_warning ("module '%s' has no open fn", module_name);
198                         return;
199 #if 0
200                 } else if (!VFS_METHOD_HAS_FUNC(temp_method, create)) {
201                         g_warning ("module '%s' has no create fn", module_name);
202                         return;
203 #endif
204                 } else if (!VFS_METHOD_HAS_FUNC(temp_method, is_local)) {
205                         g_warning ("module '%s' has no is-local fn", module_name);
206                         return;
207 #if 0
208                 } else if (!VFS_METHOD_HAS_FUNC(temp_method, get_file_info)) {
209                         g_warning ("module '%s' has no get-file-info fn", module_name);
210                         return;
211 #endif
212                 }
213
214                 /* More advanced assumptions.  */
215                 if (VFS_METHOD_HAS_FUNC(temp_method, tell) && !VFS_METHOD_HAS_FUNC(temp_method, seek)) {
216                         g_warning ("module '%s' has tell and no seek", module_name);
217                         return;
218                 }
219
220                 if (VFS_METHOD_HAS_FUNC(temp_method, seek) && !VFS_METHOD_HAS_FUNC(temp_method, tell)) {
221                         g_warning ("module '%s' has seek and no tell", module_name);
222                         return;
223                 }
224         }
225
226         if (transform_function)
227                 temp_transform = (* transform_function) (method_name, args);
228         if (temp_transform) {
229                 if (temp_transform->transform == NULL) {
230                         g_warning ("module '%s' has no transform method", module_name);
231                         return;
232                 }
233         }
234
235         *method = temp_method;
236         *transform = temp_transform;
237 }
238
239 static void
240 load_module_in_path_list (const gchar *base_name, const char *method_name, const char *args,
241                           GnomeVFSMethod **method, GnomeVFSTransform **transform)
242 {
243         GList *p;
244
245         *method = NULL;
246         *transform = NULL;
247         
248         for (p = module_path_list; p != NULL; p = p->next) {
249                 const gchar *path;
250                 gchar *name;
251
252                 path = p->data;
253                 name = g_module_build_path (path, base_name);
254
255                 load_module (name, method_name, args, method, transform);
256                 g_free (name);
257
258                 if (*method != NULL || *transform != NULL)
259                         return;
260         }
261 }
262
263 static ModuleElement *
264 gnome_vfs_add_module_to_hash_table (const gchar *name)
265 {
266         GnomeVFSMethod *method = NULL;
267         GnomeVFSTransform *transform = NULL;
268         ModuleElement *module_element;
269         const char *module_name;
270         pid_t saved_uid;
271         gid_t saved_gid;
272         const char *args;
273
274         g_static_rec_mutex_lock (&module_hash_lock);
275
276         module_element = g_hash_table_lookup (module_hash, name);
277
278         if (module_element != NULL)
279                 goto add_module_out;
280
281         module_name = _gnome_vfs_configuration_get_module_path (name, &args);
282         if (module_name == NULL)
283                 goto add_module_out;
284
285         /* Set the effective UID/GID to the user UID/GID to prevent attacks to
286            setuid/setgid executables.  */
287
288         saved_uid = geteuid ();
289         saved_gid = getegid ();
290 #if defined(HAVE_SETEUID)
291         seteuid (getuid ());
292 #elif defined(HAVE_SETRESUID)
293         setresuid (-1, getuid (), -1);
294 #endif
295 #if defined(HAVE_SETEGID)
296         setegid (getgid ());
297 #elif defined(HAVE_SETRESGID)
298         setresgid (-1, getgid (), -1);
299 #endif
300
301         if (g_path_is_absolute (module_name))
302                 load_module (module_name, name, args, &method, &transform);
303         else
304                 load_module_in_path_list (module_name, name, args, &method, &transform);
305
306 #if defined(HAVE_SETEUID)
307         seteuid (saved_uid);
308 #elif defined(HAVE_SETRESUID)
309         setresuid (-1, saved_uid, -1);
310 #endif
311 #if defined(HAVE_SETEGID)
312         setegid (saved_gid);
313 #elif defined(HAVE_SETRESGID)
314         setresgid (-1, saved_gid, -1);
315 #endif
316
317         if (method == NULL && transform == NULL)
318                 goto add_module_out;
319
320         module_element = g_new (ModuleElement, 1);
321         module_element->name = g_strdup (name);
322         module_element->method = method;
323         module_element->transform = transform;
324
325         g_hash_table_insert (module_hash, module_element->name, module_element);
326
327  add_module_out:
328         g_static_rec_mutex_unlock (&module_hash_lock);
329
330         return module_element;
331 }
332
333 GnomeVFSMethod *
334 gnome_vfs_method_get (const gchar *name)
335 {
336         ModuleElement *module_element;
337
338         g_return_val_if_fail (name != NULL, NULL);
339
340         module_element = gnome_vfs_add_module_to_hash_table (name);
341         return module_element ? module_element->method : NULL;
342 }
343
344 GnomeVFSTransform *
345 gnome_vfs_transform_get (const gchar *name)
346 {
347         ModuleElement *module_element;
348
349         g_return_val_if_fail (name != NULL, NULL);
350
351         module_element = gnome_vfs_add_module_to_hash_table (name);
352         return module_element ? module_element->transform : NULL;
353 }