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
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 #if 0
211         GnomeVFSFileInfo *info;
212
213         /* Check the file exists so we don't get a bogus DELETED event */
214         info = gnome_vfs_file_info_new ();
215         result = gnome_vfs_get_file_info (uri, 
216                                           info, 
217                                           GNOME_VFS_FILE_INFO_DEFAULT);
218         gnome_vfs_file_info_unref (info);
219
220         if (result != GNOME_VFS_OK)
221                 return NULL;
222 #endif
223         
224         monitor = g_new0 (VFolderMonitor, 1);
225         monitor->callback = callback;
226         monitor->user_data = user_data;
227         monitor->uri = g_strdup (uri);
228
229 #ifndef VFOLDER_DEBUG_WITHOUT_MONITORING
230         result = gnome_vfs_monitor_add (&monitor->vfs_handle, 
231                                         uri,
232                                         type,
233                                         monitor_callback_internal,
234                                         monitor);
235 #else
236         result = GNOME_VFS_ERROR_NOT_SUPPORTED;
237 #endif
238
239         if (result == GNOME_VFS_ERROR_NOT_SUPPORTED) {
240                 monitor->ctime = ctime_for_uri (uri);
241
242                 G_LOCK (stat_monitors);
243                 if (stat_timeout_tag == 0) {
244                         stat_timeout_tag = 
245                                 g_timeout_add (TIMEOUT_SECONDS * 1000,
246                                                monitor_timeout_cb,
247                                                NULL);
248                 }
249
250                 stat_monitors = g_slist_prepend (stat_monitors, monitor);
251                 G_UNLOCK (stat_monitors);
252         }
253
254         return monitor;
255 }
256
257 VFolderMonitor *
258 vfolder_monitor_dir_new (const gchar             *uri,
259                          GnomeVFSMonitorCallback  callback,
260                          gpointer                 user_data)
261 {
262         return monitor_start_internal (GNOME_VFS_MONITOR_DIRECTORY, 
263                                        uri, 
264                                        callback,
265                                        user_data);
266 }
267
268 VFolderMonitor *
269 vfolder_monitor_file_new (const gchar             *uri,
270                           GnomeVFSMonitorCallback  callback,
271                           gpointer                 user_data)
272 {
273         return monitor_start_internal (GNOME_VFS_MONITOR_FILE, 
274                                        uri, 
275                                        callback,
276                                        user_data);
277 }
278
279 void 
280 vfolder_monitor_freeze (VFolderMonitor *monitor)
281 {
282         monitor->frozen = TRUE;
283
284         if (monitor->vfs_handle) {
285                 gnome_vfs_monitor_cancel (monitor->vfs_handle);
286                 monitor->vfs_handle = NULL;
287         }
288 }
289
290 void 
291 vfolder_monitor_thaw (VFolderMonitor *monitor)
292 {
293         if (!monitor->frozen)
294                 return;
295
296         monitor->frozen = FALSE;
297
298         if (gnome_vfs_monitor_add (&monitor->vfs_handle, 
299                                    monitor->uri,
300                                    monitor->type,
301                                    monitor_callback_internal,
302                                    monitor) != GNOME_VFS_OK)
303                 monitor->vfs_handle = NULL;
304 }
305
306 void 
307 vfolder_monitor_cancel (VFolderMonitor *monitor)
308 {
309         if (monitor->vfs_handle)
310                 gnome_vfs_monitor_cancel (monitor->vfs_handle);
311         else {
312                 G_LOCK (stat_monitors);
313                 stat_monitors = g_slist_remove (stat_monitors, monitor);
314                 
315                 if (!stat_monitors) {
316                         g_source_remove (stat_timeout_tag);
317                         stat_timeout_tag = 0;
318                 }
319                 G_UNLOCK (stat_monitors);
320         }
321
322         g_free (monitor->uri);
323         g_free (monitor);
324 }
325
326 /* 
327  * Stolen from eel_make_directory_and_parents from libeel
328  */
329 static GnomeVFSResult
330 make_directory_and_parents_from_uri (GnomeVFSURI *uri, guint permissions)
331 {
332         GnomeVFSResult result;
333         GnomeVFSURI *parent_uri;
334
335         /* 
336          * Make the directory, and return right away unless there's
337          * a possible problem with the parent.
338          */
339         result = gnome_vfs_make_directory_for_uri (uri, permissions);
340         if (result != GNOME_VFS_ERROR_NOT_FOUND)
341                 return result;
342
343         /* If we can't get a parent, we are done. */
344         parent_uri = gnome_vfs_uri_get_parent (uri);
345         if (!parent_uri)
346                 return result;
347
348         /* 
349          * If we can get a parent, use a recursive call to create
350          * the parent and its parents.
351          */
352         result = make_directory_and_parents_from_uri (parent_uri, permissions);
353         gnome_vfs_uri_unref (parent_uri);
354         if (result != GNOME_VFS_OK && result != GNOME_VFS_ERROR_FILE_EXISTS)
355                 return result;
356
357         /* 
358          * A second try at making the directory after the parents
359          * have all been created.
360          */
361         result = gnome_vfs_make_directory_for_uri (uri, permissions);
362         return result;
363 }
364
365 GnomeVFSResult
366 vfolder_make_directory_and_parents (const gchar *uri, 
367                                     gboolean     skip_filename,
368                                     guint        permissions)
369 {
370         GnomeVFSURI *file_uri, *parent_uri;
371         GnomeVFSResult result;
372
373         file_uri = gnome_vfs_uri_new (uri);
374
375         if (skip_filename) {
376                 parent_uri = gnome_vfs_uri_get_parent (file_uri);
377                 gnome_vfs_uri_unref (file_uri);
378                 file_uri = parent_uri;
379         }
380
381         result = make_directory_and_parents_from_uri (file_uri, permissions);
382         gnome_vfs_uri_unref (file_uri);
383
384         return result == GNOME_VFS_ERROR_FILE_EXISTS ? GNOME_VFS_OK : result;
385 }
386
387
388 gchar *
389 vfolder_timestamp_file_name (const gchar *file)
390 {
391         struct timeval tv;
392         gchar *ret;
393
394         gettimeofday (&tv, NULL);
395
396         ret = g_strdup_printf ("%d-%s", 
397                                (int) (tv.tv_sec ^ tv.tv_usec), 
398                                file);
399         
400         return ret;
401 }
402
403 gchar *
404 vfolder_untimestamp_file_name (const gchar *file)
405 {
406         int n = 0;
407
408         while (file [n] && g_ascii_isdigit (file [n]))
409                 ++n;
410         n = (file [n] == '-') ? n + 1 : 0;
411
412         return g_strdup (file [n] ? &file [n] : NULL);
413 }
414
415 gboolean
416 vfolder_check_extension (const char *name, const char *ext_check)
417 {
418         const char *ext;
419
420         ext = strrchr (name, '.');
421         if (ext && !strcmp (ext, ext_check))
422                 return TRUE;
423         else
424                 return FALSE;
425 }
426
427 gchar *
428 vfolder_escape_home (const gchar *file)
429 {
430         if (file[0] == '~')
431                 return g_strconcat (g_get_home_dir (), &file[1], NULL);
432         else
433                 return g_strdup (file);
434 }
435
436 /* Ripped from gfileutils.c:g_build_pathv() */
437 gchar *
438 vfolder_build_uri (const gchar *first_element,
439                    ...)
440 {
441         GString *result;
442         gboolean is_first = TRUE;
443         const gchar *next_element;
444         va_list args;
445
446         va_start (args, first_element);
447
448         result = g_string_new (NULL);
449         next_element = first_element;
450
451         while (TRUE) {
452                 const gchar *element;
453                 const gchar *start;
454                 const gchar *end;
455
456                 if (next_element) {
457                         element = next_element;
458                         next_element = va_arg (args, gchar *);
459                 }
460                 else
461                         break;
462
463                 start = element;
464
465                 if (!is_first)
466                         start += strspn (start, "/");
467
468                 end = start + strlen (start);
469
470                 if (next_element) {
471                         while (end > start + 1 && end [-1] == '/')
472                                 end--;
473
474                         if (is_first)
475                                 if (end > start + 1 &&
476                                     !strncmp (end - 1, "://", 3))
477                                         end += 2;
478                 }
479
480                 if (end > start) {
481                         if (result->len > 0)
482                                 g_string_append_c (result, '/');
483
484                         g_string_append_len (result, start, end - start);
485                 }
486
487                 is_first = FALSE;
488         }
489   
490         va_end (args);
491
492         return g_string_free (result, FALSE);
493 }