ftp://ftp.redhat.com/pub/redhat/linux/rawhide/SRPMS/SRPMS/gnome-vfs2-2.3.8-1.src.rpm
[gnome-vfs-httpcaptive.git] / modules / vfolder / vfolder-util.c.vfolder-hacks
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /* 
3  * vfolder-util.c - Utility functions for wrapping monitors and 
4  *                  filename/uri parsing.
5  *
6  * Copyright (C) 2002 Ximian, Inc.
7  *
8  * The Gnome Library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public License as
10  * published by the Free Software Foundation; either version 2 of the
11  * License, or (at your option) any later version.
12  *
13  * The Gnome Library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with the Gnome Library; see the file COPYING.LIB.  If not,
20  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  *
23  * Author: Alex Graveley <alex@ximian.com>
24  *         Based on original code by George Lebl <jirka@5z.com>.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 # include <config.h>
29 #endif
30
31 #include <string.h>
32 #include <sys/time.h>
33 #include <unistd.h>
34 #include <libgnomevfs/gnome-vfs-file-info.h>
35 #include <libgnomevfs/gnome-vfs-ops.h>
36
37 #include "vfolder-util.h"
38 #include "vfolder-common.h"
39
40 /* assumes vuri->path already set */
41 gboolean
42 vfolder_uri_parse_internal (GnomeVFSURI *uri, VFolderURI *vuri)
43 {
44         vuri->scheme = (gchar *) gnome_vfs_uri_get_scheme (uri);
45
46         vuri->ends_in_slash = FALSE;
47
48         if (strncmp (vuri->scheme, "all-", strlen ("all-")) == 0) {
49                 vuri->scheme += strlen ("all-");
50                 vuri->is_all_scheme = TRUE;
51         } else
52                 vuri->is_all_scheme = FALSE;
53
54         if (vuri->path != NULL) {
55                 int last_slash = strlen (vuri->path) - 1;
56                 char *first;
57
58                 /* Note: This handling of paths is somewhat evil, may need a
59                  * bit of a rework */
60
61                 /* kill leading slashes, that is make sure there is
62                  * only one */
63                 for (first = vuri->path; *first == '/'; first++)
64                         ;
65                 if (first != vuri->path) {
66                         first--;
67                         vuri->path = first;
68                 }
69
70                 /* kill trailing slashes (leave first if all slashes) */
71                 while (last_slash > 0 && vuri->path [last_slash] == '/') {
72                         vuri->path [last_slash--] = '\0';
73                         vuri->ends_in_slash = TRUE;
74                 }
75
76                 /* get basename start */
77                 while (last_slash >= 0 && vuri->path [last_slash] != '/')
78                         last_slash--;
79
80                 if (last_slash > -1)
81                         vuri->file = vuri->path + last_slash + 1;
82                 else
83                         vuri->file = vuri->path;
84
85                 if (vuri->file[0] == '\0' &&
86                     strcmp (vuri->path, "/") == 0) {
87                         vuri->file = NULL;
88                 }
89         } else {
90                 vuri->ends_in_slash = TRUE;
91                 vuri->path = "/";
92                 vuri->file = NULL;
93         }
94
95         vuri->uri = uri;
96
97         return TRUE;
98 }
99
100 static void
101 monitor_callback_internal (GnomeVFSMonitorHandle *handle,
102                            const gchar *monitor_uri,
103                            const gchar *info_uri,
104                            GnomeVFSMonitorEventType event_type,
105                            gpointer user_data)
106 {
107         VFolderMonitor *monitor = (VFolderMonitor *) user_data;
108
109         if (monitor->frozen)
110                 return;
111
112         D (g_print (
113                 "RECEIVED MONITOR: %s, %s, %s%s%s\n", 
114                 monitor_uri, 
115                 info_uri + strlen (monitor_uri),
116                 event_type == GNOME_VFS_MONITOR_EVENT_CREATED ? "CREATED" : "",
117                 event_type == GNOME_VFS_MONITOR_EVENT_DELETED ? "DELETED" : "",
118                 event_type == GNOME_VFS_MONITOR_EVENT_CHANGED ? "CHANGED" : ""));
119
120         (*monitor->callback) (handle,
121                               monitor_uri,
122                               info_uri,
123                               event_type,
124                               monitor->user_data);
125 }
126
127 #define TIMEOUT_SECONDS 3
128
129 static GSList *stat_monitors = NULL;
130 G_LOCK_DEFINE_STATIC (stat_monitors);
131 static guint stat_timeout_tag = 0;
132
133 static time_t
134 ctime_for_uri (const gchar *uri)
135 {
136         GnomeVFSFileInfo *info;
137         GnomeVFSResult result;
138         time_t ctime = 0;
139
140         info = gnome_vfs_file_info_new ();
141         
142         result = gnome_vfs_get_file_info (uri,
143                                           info,
144                                           GNOME_VFS_FILE_INFO_DEFAULT);
145         if (result == GNOME_VFS_OK) {
146                 ctime = info->ctime;
147         }
148
149         gnome_vfs_file_info_unref (info);
150
151         return ctime;
152 }
153
154 static gboolean
155 monitor_timeout_cb (gpointer user_data)
156 {
157         GSList *iter;
158         GSList *copy;
159
160         /* 
161          * Copy the stat_monitors list in case the callback removes/adds
162          * monitors (which is likely).
163          */
164         G_LOCK (stat_monitors);
165         copy = g_slist_copy (stat_monitors);
166         G_UNLOCK (stat_monitors);       
167
168         for (iter = copy; iter; iter = iter->next) {
169                 VFolderMonitor *monitor = iter->data;
170                 time_t ctime;
171
172                 G_LOCK (stat_monitors);
173                 if (g_slist_position (stat_monitors, iter) < 0) {
174                         G_UNLOCK (stat_monitors);
175                         continue;
176                 }
177                 G_UNLOCK (stat_monitors);
178
179                 if (monitor->frozen)
180                         continue;
181
182                 ctime = ctime_for_uri (monitor->uri);
183                 if (ctime == monitor->ctime)
184                         continue;
185
186                 (*monitor->callback) ((GnomeVFSMonitorHandle *) monitor,
187                                       monitor->uri,
188                                       monitor->uri,
189                                       ctime == 0 ?
190                                               GNOME_VFS_MONITOR_EVENT_DELETED :
191                                               GNOME_VFS_MONITOR_EVENT_CHANGED,
192                                       monitor->user_data);
193
194                 monitor->ctime = ctime;
195         }
196
197         g_slist_free (copy);
198
199         return TRUE;
200 }
201
202 static VFolderMonitor *
203 monitor_start_internal (GnomeVFSMonitorType      type,
204                         const gchar             *uri,
205                         GnomeVFSMonitorCallback  callback,
206                         gpointer                 user_data)
207 {
208         GnomeVFSResult result;
209         VFolderMonitor *monitor;
210         GnomeVFSFileInfo *info;
211
212         /* Check the file exists so we don't get a bogus DELETED event */
213         info = gnome_vfs_file_info_new ();
214         result = gnome_vfs_get_file_info (uri, 
215                                           info, 
216                                           GNOME_VFS_FILE_INFO_DEFAULT);
217         gnome_vfs_file_info_unref (info);
218
219         if (result != GNOME_VFS_OK)
220                 return NULL;
221
222         monitor = g_new0 (VFolderMonitor, 1);
223         monitor->callback = callback;
224         monitor->user_data = user_data;
225         monitor->uri = g_strdup (uri);
226
227 #ifndef VFOLDER_DEBUG_WITHOUT_MONITORING
228         result = gnome_vfs_monitor_add (&monitor->vfs_handle, 
229                                         uri,
230                                         type,
231                                         monitor_callback_internal,
232                                         monitor);
233 #else
234         result = GNOME_VFS_ERROR_NOT_SUPPORTED;
235 #endif
236
237         if (result == GNOME_VFS_ERROR_NOT_SUPPORTED) {
238                 monitor->ctime = ctime_for_uri (uri);
239
240                 G_LOCK (stat_monitors);
241                 if (stat_timeout_tag == 0) {
242                         stat_timeout_tag = 
243                                 g_timeout_add (TIMEOUT_SECONDS * 1000,
244                                                monitor_timeout_cb,
245                                                NULL);
246                 }
247
248                 stat_monitors = g_slist_prepend (stat_monitors, monitor);
249                 G_UNLOCK (stat_monitors);
250         }
251
252         return monitor;
253 }
254
255 VFolderMonitor *
256 vfolder_monitor_dir_new (const gchar             *uri,
257                          GnomeVFSMonitorCallback  callback,
258                          gpointer                 user_data)
259 {
260         return monitor_start_internal (GNOME_VFS_MONITOR_DIRECTORY, 
261                                        uri, 
262                                        callback,
263                                        user_data);
264 }
265
266 VFolderMonitor *
267 vfolder_monitor_file_new (const gchar             *uri,
268                           GnomeVFSMonitorCallback  callback,
269                           gpointer                 user_data)
270 {
271         return monitor_start_internal (GNOME_VFS_MONITOR_FILE, 
272                                        uri, 
273                                        callback,
274                                        user_data);
275 }
276
277 void 
278 vfolder_monitor_freeze (VFolderMonitor *monitor)
279 {
280         monitor->frozen = TRUE;
281
282         if (monitor->vfs_handle) {
283                 gnome_vfs_monitor_cancel (monitor->vfs_handle);
284                 monitor->vfs_handle = NULL;
285         }
286 }
287
288 void 
289 vfolder_monitor_thaw (VFolderMonitor *monitor)
290 {
291         if (!monitor->frozen)
292                 return;
293
294         monitor->frozen = FALSE;
295
296         if (gnome_vfs_monitor_add (&monitor->vfs_handle, 
297                                    monitor->uri,
298                                    monitor->type,
299                                    monitor_callback_internal,
300                                    monitor) != GNOME_VFS_OK)
301                 monitor->vfs_handle = NULL;
302 }
303
304 void 
305 vfolder_monitor_cancel (VFolderMonitor *monitor)
306 {
307         if (monitor->vfs_handle)
308                 gnome_vfs_monitor_cancel (monitor->vfs_handle);
309         else {
310                 G_LOCK (stat_monitors);
311                 stat_monitors = g_slist_remove (stat_monitors, monitor);
312                 
313                 if (!stat_monitors) {
314                         g_source_remove (stat_timeout_tag);
315                         stat_timeout_tag = 0;
316                 }
317                 G_UNLOCK (stat_monitors);
318         }
319
320         g_free (monitor->uri);
321         g_free (monitor);
322 }
323
324 /* 
325  * Stolen from eel_make_directory_and_parents from libeel
326  */
327 static GnomeVFSResult
328 make_directory_and_parents_from_uri (GnomeVFSURI *uri, guint permissions)
329 {
330         GnomeVFSResult result;
331         GnomeVFSURI *parent_uri;
332
333         /* 
334          * Make the directory, and return right away unless there's
335          * a possible problem with the parent.
336          */
337         result = gnome_vfs_make_directory_for_uri (uri, permissions);
338         if (result != GNOME_VFS_ERROR_NOT_FOUND)
339                 return result;
340
341         /* If we can't get a parent, we are done. */
342         parent_uri = gnome_vfs_uri_get_parent (uri);
343         if (!parent_uri)
344                 return result;
345
346         /* 
347          * If we can get a parent, use a recursive call to create
348          * the parent and its parents.
349          */
350         result = make_directory_and_parents_from_uri (parent_uri, permissions);
351         gnome_vfs_uri_unref (parent_uri);
352         if (result != GNOME_VFS_OK && result != GNOME_VFS_ERROR_FILE_EXISTS)
353                 return result;
354
355         /* 
356          * A second try at making the directory after the parents
357          * have all been created.
358          */
359         result = gnome_vfs_make_directory_for_uri (uri, permissions);
360         return result;
361 }
362
363 GnomeVFSResult
364 vfolder_make_directory_and_parents (const gchar *uri, 
365                                     gboolean     skip_filename,
366                                     guint        permissions)
367 {
368         GnomeVFSURI *file_uri, *parent_uri;
369         GnomeVFSResult result;
370
371         file_uri = gnome_vfs_uri_new (uri);
372
373         if (skip_filename) {
374                 parent_uri = gnome_vfs_uri_get_parent (file_uri);
375                 gnome_vfs_uri_unref (file_uri);
376                 file_uri = parent_uri;
377         }
378
379         result = make_directory_and_parents_from_uri (file_uri, permissions);
380         gnome_vfs_uri_unref (file_uri);
381
382         return result == GNOME_VFS_ERROR_FILE_EXISTS ? GNOME_VFS_OK : result;
383 }
384
385
386 gchar *
387 vfolder_timestamp_file_name (const gchar *file)
388 {
389         struct timeval tv;
390         gchar *ret;
391
392         gettimeofday (&tv, NULL);
393
394         ret = g_strdup_printf ("%d-%s", 
395                                (int) (tv.tv_sec ^ tv.tv_usec), 
396                                file);
397         
398         return ret;
399 }
400
401 gchar *
402 vfolder_untimestamp_file_name (const gchar *file)
403 {
404         int n = 0;
405
406         while (file [n] && g_ascii_isdigit (file [n]))
407                 ++n;
408         n = (file [n] == '-') ? n + 1 : 0;
409
410         return g_strdup (file [n] ? &file [n] : NULL);
411 }
412
413 gboolean
414 vfolder_check_extension (const char *name, const char *ext_check)
415 {
416         const char *ext;
417
418         ext = strrchr (name, '.');
419         if (ext && !strcmp (ext, ext_check))
420                 return TRUE;
421         else
422                 return FALSE;
423 }
424
425 gchar *
426 vfolder_escape_home (const gchar *file)
427 {
428         if (file[0] == '~')
429                 return g_strconcat (g_get_home_dir (), &file[1], NULL);
430         else
431                 return g_strdup (file);
432 }
433
434 /* Ripped from gfileutils.c:g_build_pathv() */
435 gchar *
436 vfolder_build_uri (const gchar *first_element,
437                    ...)
438 {
439         GString *result;
440         gboolean is_first = TRUE;
441         const gchar *next_element;
442         va_list args;
443
444         va_start (args, first_element);
445
446         result = g_string_new (NULL);
447         next_element = first_element;
448
449         while (TRUE) {
450                 const gchar *element;
451                 const gchar *start;
452                 const gchar *end;
453
454                 if (next_element) {
455                         element = next_element;
456                         next_element = va_arg (args, gchar *);
457                 }
458                 else
459                         break;
460
461                 start = element;
462
463                 if (!is_first)
464                         start += strspn (start, "/");
465
466                 end = start + strlen (start);
467
468                 if (next_element) {
469                         while (end > start + 1 && end [-1] == '/')
470                                 end--;
471
472                         if (is_first)
473                                 if (end > start + 1 &&
474                                     !strncmp (end - 1, "://", 3))
475                                         end += 2;
476                 }
477
478                 if (end > start) {
479                         if (result->len > 0)
480                                 g_string_append_c (result, '/');
481
482                         g_string_append_len (result, start, end - start);
483                 }
484
485                 is_first = FALSE;
486         }
487   
488         va_end (args);
489
490         return g_string_free (result, FALSE);
491 }