Added. Added. Added. Added.
[platform/upstream/glib.git] / gio / gdummyfile.c
1 /* GIO - GLib Input, Output and Streaming Library
2  * 
3  * Copyright (C) 2006-2007 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Author: Alexander Larsson <alexl@redhat.com>
21  */
22
23 #include <config.h>
24
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <unistd.h>
31 #include <stdlib.h>
32
33 #include "gdummyfile.h"
34
35 #include "gioalias.h"
36
37 static void g_dummy_file_file_iface_init (GFileIface       *iface);
38
39 typedef struct {
40   char *scheme;
41   char *userinfo;
42   char *host;
43   int port; /* -1 => not in uri */
44   char *path;
45   char *query;
46   char *fragment;
47 } GDecodedUri;
48
49 struct _GDummyFile
50 {
51   GObject parent_instance;
52
53   GDecodedUri *decoded_uri;
54   char *text_uri;
55 };
56
57 G_DEFINE_TYPE_WITH_CODE (GDummyFile, g_dummy_file, G_TYPE_OBJECT,
58                          G_IMPLEMENT_INTERFACE (G_TYPE_FILE,
59                                                 g_dummy_file_file_iface_init))
60
61 #define SUB_DELIM_CHARS  "!$&'()*+,;="
62
63 static char *       _g_encode_uri       (GDecodedUri *decoded);
64 static void         _g_decoded_uri_free (GDecodedUri *decoded);
65 static GDecodedUri *_g_decode_uri       (const char  *uri);
66 static GDecodedUri *_g_decoded_uri_new  (void);
67
68 static char * unescape_string (const gchar *escaped_string,
69                                const gchar *escaped_string_end,
70                                const gchar *illegal_characters);
71
72 static void g_string_append_encoded (GString *string, const char *encoded,
73                                      const char *reserved_chars_allowed);
74
75 static void
76 g_dummy_file_finalize (GObject *object)
77 {
78   GDummyFile *dummy;
79
80   dummy = G_DUMMY_FILE (object);
81
82   if (dummy->decoded_uri)
83     _g_decoded_uri_free (dummy->decoded_uri);
84   
85   g_free (dummy->text_uri);
86   
87   if (G_OBJECT_CLASS (g_dummy_file_parent_class)->finalize)
88     (*G_OBJECT_CLASS (g_dummy_file_parent_class)->finalize) (object);
89 }
90
91 static void
92 g_dummy_file_class_init (GDummyFileClass *klass)
93 {
94   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
95
96   gobject_class->finalize = g_dummy_file_finalize;
97 }
98
99 static void
100 g_dummy_file_init (GDummyFile *dummy)
101 {
102 }
103
104 /**
105  * g_dummy_file_new:
106  * @uri: Universal Resource Identifier for the dummy file object.
107  * 
108  * Returns: a new #GFile. 
109  **/
110 GFile *
111 g_dummy_file_new (const char *uri)
112 {
113   GDummyFile *dummy;
114
115   g_return_val_if_fail (uri != NULL, NULL);
116
117   dummy = g_object_new (G_TYPE_DUMMY_FILE, NULL);
118   dummy->text_uri = g_strdup (uri);
119   dummy->decoded_uri = _g_decode_uri (uri);
120   
121   return G_FILE (dummy);
122 }
123
124 static gboolean
125 g_dummy_file_is_native (GFile *file)
126 {
127   return FALSE;
128 }
129
130 static char *
131 g_dummy_file_get_basename (GFile *file)
132 {
133   GDummyFile *dummy = G_DUMMY_FILE (file);
134   
135   if (dummy->decoded_uri)
136     return g_path_get_basename (dummy->decoded_uri->path);
137   return g_strdup (dummy->text_uri);
138 }
139
140 static char *
141 g_dummy_file_get_path (GFile *file)
142 {
143   GDummyFile *dummy = G_DUMMY_FILE (file);
144
145   if (dummy->decoded_uri)
146     return g_strdup (dummy->decoded_uri->path);
147   return NULL;
148 }
149
150 static char *
151 g_dummy_file_get_uri (GFile *file)
152 {
153   return g_strdup (G_DUMMY_FILE (file)->text_uri);
154 }
155
156 static char *
157 g_dummy_file_get_parse_name (GFile *file)
158 {
159   return g_strdup (G_DUMMY_FILE (file)->text_uri);
160 }
161
162 static GFile *
163 g_dummy_file_get_parent (GFile *file)
164 {
165   GDummyFile *dummy = G_DUMMY_FILE (file);
166   GFile *parent;
167   char *dirname;
168   char *uri;
169   GDecodedUri new_decoded_uri;
170
171   if (dummy->decoded_uri == NULL)
172     return NULL;
173
174   dirname = g_path_get_dirname (dummy->decoded_uri->path);
175   
176   if (strcmp (dirname, ".") == 0)
177     {
178       g_free (dirname);
179       return NULL;
180     }
181   
182   new_decoded_uri = *dummy->decoded_uri;
183   new_decoded_uri.path = dirname;
184   uri = _g_encode_uri (&new_decoded_uri);
185   g_free (dirname);
186   
187   parent = g_dummy_file_new (uri);
188   g_free (uri);
189   
190   return parent;
191 }
192
193 static GFile *
194 g_dummy_file_dup (GFile *file)
195 {
196   GDummyFile *dummy = G_DUMMY_FILE (file);
197
198   return g_dummy_file_new (dummy->text_uri);
199 }
200
201 static guint
202 g_dummy_file_hash (GFile *file)
203 {
204   GDummyFile *dummy = G_DUMMY_FILE (file);
205   
206   return g_str_hash (dummy->text_uri);
207 }
208
209 static gboolean
210 g_dummy_file_equal (GFile *file1,
211                     GFile *file2)
212 {
213   GDummyFile *dummy1 = G_DUMMY_FILE (file1);
214   GDummyFile *dummy2 = G_DUMMY_FILE (file2);
215
216   return g_str_equal (dummy1->text_uri, dummy2->text_uri);
217 }
218
219 static int
220 safe_strcmp (const char *a, const char *b)
221 {
222   if (a == NULL)
223     a = "";
224   if (b == NULL)
225     b = "";
226
227   return strcmp (a, b);
228 }
229
230 static gboolean
231 uri_same_except_path (GDecodedUri *a,
232                       GDecodedUri *b)
233 {
234   if (safe_strcmp (a->scheme, b->scheme) != 0)
235     return FALSE;
236   if (safe_strcmp (a->userinfo, b->userinfo) != 0)
237     return FALSE;
238   if (safe_strcmp (a->host, b->host) != 0)
239     return FALSE;
240   if (a->port != b->port)
241     return FALSE;
242
243   return TRUE;
244 }
245
246 static const char *
247 match_prefix (const char *path, const char *prefix)
248 {
249   int prefix_len;
250
251   prefix_len = strlen (prefix);
252   if (strncmp (path, prefix, prefix_len) != 0)
253     return NULL;
254   return path + prefix_len;
255 }
256
257 static gboolean
258 g_dummy_file_contains_file (GFile *parent,
259                             GFile *descendant)
260 {
261   GDummyFile *parent_dummy = G_DUMMY_FILE (parent);
262   GDummyFile *descendant_dummy = G_DUMMY_FILE (descendant);
263   const char *remainder;
264
265   if (parent_dummy->decoded_uri != NULL &&
266       descendant_dummy->decoded_uri != NULL)
267     {
268       if (uri_same_except_path (parent_dummy->decoded_uri,
269                                 descendant_dummy->decoded_uri)) {
270         remainder = match_prefix (descendant_dummy->decoded_uri->path,
271                                   parent_dummy->decoded_uri->path);
272         if (remainder != NULL && *remainder == '/')
273           {
274             while (*remainder == '/')
275               remainder++;
276             if (*remainder != 0)
277               return TRUE;
278           }
279       }
280     }
281   else
282     {
283       remainder = match_prefix (descendant_dummy->text_uri,
284                                 parent_dummy->text_uri);
285       if (remainder != NULL && *remainder == '/')
286           {
287             while (*remainder == '/')
288               remainder++;
289             if (*remainder != 0)
290               return TRUE;
291           }
292     }
293   
294   return FALSE;
295 }
296
297 static char *
298 g_dummy_file_get_relative_path (GFile *parent,
299                                 GFile *descendant)
300 {
301   GDummyFile *parent_dummy = G_DUMMY_FILE (parent);
302   GDummyFile *descendant_dummy = G_DUMMY_FILE (descendant);
303   const char *remainder;
304
305   if (parent_dummy->decoded_uri != NULL &&
306       descendant_dummy->decoded_uri != NULL)
307     {
308       if (uri_same_except_path (parent_dummy->decoded_uri,
309                                 descendant_dummy->decoded_uri)) {
310         remainder = match_prefix (descendant_dummy->decoded_uri->path,
311                                   parent_dummy->decoded_uri->path);
312         if (remainder != NULL && *remainder == '/')
313           {
314             while (*remainder == '/')
315               remainder++;
316             if (*remainder != 0)
317               return g_strdup (remainder);
318           }
319       }
320     }
321   else
322     {
323       remainder = match_prefix (descendant_dummy->text_uri,
324                                 parent_dummy->text_uri);
325       if (remainder != NULL && *remainder == '/')
326           {
327             while (*remainder == '/')
328               remainder++;
329             if (*remainder != 0)
330               return unescape_string (remainder, NULL, "/");
331           }
332     }
333   
334   return NULL;
335 }
336
337
338 static GFile *
339 g_dummy_file_resolve_relative_path (GFile *file,
340                                     const char *relative_path)
341 {
342   GDummyFile *dummy = G_DUMMY_FILE (file);
343   GFile *child;
344   char *uri;
345   GDecodedUri new_decoded_uri;
346   GString *str;
347
348   if (dummy->decoded_uri == NULL)
349     {
350       str = g_string_new (dummy->text_uri);
351       g_string_append (str, "/");
352       g_string_append_encoded (str, relative_path, SUB_DELIM_CHARS ":@/");
353       child = g_dummy_file_new (str->str);
354       g_string_free (str, TRUE);
355     }
356   else
357     {
358       new_decoded_uri = *dummy->decoded_uri;
359       
360       if (g_path_is_absolute (relative_path))
361         new_decoded_uri.path = g_strdup (relative_path);
362       else
363         new_decoded_uri.path = g_build_filename (new_decoded_uri.path, relative_path, NULL);
364       
365       uri = _g_encode_uri (&new_decoded_uri);
366       g_free (new_decoded_uri.path);
367       
368       child = g_dummy_file_new (uri);
369       g_free (uri);
370     }
371
372   return child;
373 }
374
375 static GFile *
376 g_dummy_file_get_child_for_display_name (GFile        *file,
377                                          const char   *display_name,
378                                          GError      **error)
379 {
380   return g_file_get_child (file, display_name);
381 }
382
383 static gboolean
384 g_dummy_file_has_uri_scheme (GFile *file,
385                              const char *uri_scheme)
386 {
387   GDummyFile *dummy = G_DUMMY_FILE (file);
388   
389   if (dummy->decoded_uri)
390     return g_ascii_strcasecmp (uri_scheme, dummy->decoded_uri->scheme) == 0;
391   return FALSE;
392 }
393
394 static char *
395 g_dummy_file_get_uri_scheme (GFile *file)
396 {
397   GDummyFile *dummy = G_DUMMY_FILE (file);
398
399   if (dummy->decoded_uri)
400     return g_strdup (dummy->decoded_uri->scheme);
401     
402   return NULL;
403 }
404
405
406 static void
407 g_dummy_file_file_iface_init (GFileIface *iface)
408 {
409   iface->dup = g_dummy_file_dup;
410   iface->hash = g_dummy_file_hash;
411   iface->equal = g_dummy_file_equal;
412   iface->is_native = g_dummy_file_is_native;
413   iface->has_uri_scheme = g_dummy_file_has_uri_scheme;
414   iface->get_uri_scheme = g_dummy_file_get_uri_scheme;
415   iface->get_basename = g_dummy_file_get_basename;
416   iface->get_path = g_dummy_file_get_path;
417   iface->get_uri = g_dummy_file_get_uri;
418   iface->get_parse_name = g_dummy_file_get_parse_name;
419   iface->get_parent = g_dummy_file_get_parent;
420   iface->contains_file = g_dummy_file_contains_file;
421   iface->get_relative_path = g_dummy_file_get_relative_path;
422   iface->resolve_relative_path = g_dummy_file_resolve_relative_path;
423   iface->get_child_for_display_name = g_dummy_file_get_child_for_display_name;
424 }
425
426 /* Uri handling helper functions: */
427
428 static int
429 unescape_character (const char *scanner)
430 {
431   int first_digit;
432   int second_digit;
433   
434   first_digit = g_ascii_xdigit_value (*scanner++);
435   if (first_digit < 0)
436     return -1;
437
438   second_digit = g_ascii_xdigit_value (*scanner++);
439   if (second_digit < 0)
440     return -1;
441
442   return (first_digit << 4) | second_digit;
443 }
444
445 static char *
446 unescape_string (const gchar *escaped_string,
447                  const gchar *escaped_string_end,
448                  const gchar *illegal_characters)
449 {
450   const gchar *in;
451   gchar *out, *result;
452   gint character;
453   
454   if (escaped_string == NULL)
455     return NULL;
456
457   if (escaped_string_end == NULL)
458     escaped_string_end = escaped_string + strlen (escaped_string);
459   
460   result = g_malloc (escaped_string_end - escaped_string + 1);
461         
462   out = result;
463   for (in = escaped_string; in < escaped_string_end; in++) {
464     character = *in;
465     if (*in == '%') {
466       in++;
467       if (escaped_string_end - in < 2)
468         {
469           g_free (result);
470           return NULL;
471         }
472       
473       character = unescape_character (in);
474       
475       /* Check for an illegal character. We consider '\0' illegal here. */
476       if (character <= 0 ||
477           (illegal_characters != NULL &&
478            strchr (illegal_characters, (char)character) != NULL))
479         {
480           g_free (result);
481           return NULL;
482         }
483       in++; /* The other char will be eaten in the loop header */
484     }
485     *out++ = (char)character;
486   }
487   
488   *out = '\0';
489   g_assert (out - result <= strlen (escaped_string));
490   return result;
491 }
492
493 void
494 _g_decoded_uri_free (GDecodedUri *decoded)
495 {
496   if (decoded == NULL)
497     return;
498
499   g_free (decoded->scheme);
500   g_free (decoded->query);
501   g_free (decoded->fragment);
502   g_free (decoded->userinfo);
503   g_free (decoded->host);
504   g_free (decoded->path);
505   g_free (decoded);
506 }
507
508 GDecodedUri *
509 _g_decoded_uri_new (void)
510 {
511   GDecodedUri *uri;
512
513   uri = g_new0 (GDecodedUri, 1);
514   uri->port = -1;
515
516   return uri;
517 }
518
519 GDecodedUri *
520 _g_decode_uri (const char *uri)
521 {
522   GDecodedUri *decoded;
523   const char *p, *in, *hier_part_start, *hier_part_end, *query_start, *fragment_start;
524   char *out;
525   char c;
526
527   /* From RFC 3986 Decodes:
528    * URI         = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
529    */ 
530
531   p = uri;
532   
533   /* Decode scheme:
534      scheme      = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
535   */
536
537   if (!g_ascii_isalpha (*p))
538     return NULL;
539
540   while (1)
541     {
542       c = *p++;
543
544       if (c == ':')
545         break;
546       
547       if (!(g_ascii_isalnum(c) ||
548             c == '+' ||
549             c == '-' ||
550             c == '.'))
551         return NULL;
552     }
553
554   decoded = _g_decoded_uri_new ();
555   
556   decoded->scheme = g_malloc (p - uri);
557   out = decoded->scheme;
558   for (in = uri; in < p - 1; in++)
559     *out++ = g_ascii_tolower (*in);
560   *out = 0;
561
562   hier_part_start = p;
563
564   query_start = strchr (p, '?');
565   if (query_start)
566     {
567       hier_part_end = query_start++;
568       fragment_start = strchr (query_start, '#');
569       if (fragment_start)
570         {
571           decoded->query = g_strndup (query_start, fragment_start - query_start);
572           decoded->fragment = g_strdup (fragment_start+1);
573         }
574       else
575         {
576           decoded->query = g_strdup (query_start);
577           decoded->fragment = NULL;
578         }
579     }
580   else
581     {
582       /* No query */
583       decoded->query = NULL;
584       fragment_start = strchr (p, '#');
585       if (fragment_start)
586         {
587           hier_part_end = fragment_start++;
588           decoded->fragment = g_strdup (fragment_start);
589         }
590       else
591         {
592           hier_part_end = p + strlen (p);
593           decoded->fragment = NULL;
594         }
595     }
596
597   /*  3:
598       hier-part   = "//" authority path-abempty
599                   / path-absolute
600                   / path-rootless
601                   / path-empty
602
603   */
604
605   if (hier_part_start[0] == '/' &&
606       hier_part_start[1] == '/')
607     {
608       const char *authority_start, *authority_end;
609       const char *userinfo_start, *userinfo_end;
610       const char *host_start, *host_end;
611       const char *port_start;
612       
613       authority_start = hier_part_start + 2;
614       /* authority is always followed by / or nothing */
615       authority_end = memchr (authority_start, '/', hier_part_end - authority_start);
616       if (authority_end == NULL)
617         authority_end = hier_part_end;
618
619       /* 3.2:
620               authority   = [ userinfo "@" ] host [ ":" port ]
621       */
622
623       userinfo_end = memchr (authority_start, '@', authority_end - authority_start);
624       if (userinfo_end)
625         {
626           userinfo_start = authority_start;
627           decoded->userinfo = unescape_string (userinfo_start, userinfo_end, NULL);
628           if (decoded->userinfo == NULL)
629             {
630               _g_decoded_uri_free (decoded);
631               return NULL;
632             }
633           host_start = userinfo_end + 1;
634         }
635       else
636         host_start = authority_start;
637
638       port_start = memchr (host_start, ':', authority_end - host_start);
639       if (port_start)
640         {
641           host_end = port_start++;
642
643           decoded->port = atoi(port_start);
644         }
645       else
646         {
647           host_end = authority_end;
648           decoded->port = -1;
649         }
650
651       decoded->host = g_strndup (host_start, host_end - host_start);
652
653       hier_part_start = authority_end;
654     }
655
656   decoded->path = unescape_string (hier_part_start, hier_part_end, "/");
657
658   if (decoded->path == NULL)
659     {
660       _g_decoded_uri_free (decoded);
661       return NULL;
662     }
663   
664   return decoded;
665 }
666
667 static gboolean
668 is_valid (char c, const char *reserved_chars_allowed)
669 {
670   if (g_ascii_isalnum (c) ||
671       c == '-' ||
672       c == '.' ||
673       c == '_' ||
674       c == '~')
675     return TRUE;
676
677   if (reserved_chars_allowed &&
678       strchr (reserved_chars_allowed, c) != NULL)
679     return TRUE;
680   
681   return FALSE;
682 }
683
684 static void
685 g_string_append_encoded (GString *string, const char *encoded,
686                          const char *reserved_chars_allowed)
687 {
688   unsigned char c;
689   const char *end;
690   static const gchar hex[16] = "0123456789ABCDEF";
691
692   end = encoded + strlen (encoded);
693   
694   while ((c = *encoded) != 0)
695     {
696       if (is_valid (c, reserved_chars_allowed))
697         {
698           g_string_append_c (string, c);
699           encoded++;
700         }
701       else
702         {
703           g_string_append_c (string, '%');
704           g_string_append_c (string, hex[((guchar)c) >> 4]);
705           g_string_append_c (string, hex[((guchar)c) & 0xf]);
706           encoded++;
707         }
708     }
709 }
710
711 static char *
712 _g_encode_uri (GDecodedUri *decoded)
713 {
714   GString *uri;
715
716   uri = g_string_new (NULL);
717
718   g_string_append (uri, decoded->scheme);
719   g_string_append (uri, "://");
720
721   if (decoded->host != NULL)
722     {
723       if (decoded->userinfo)
724         {
725           /* userinfo    = *( unreserved / pct-encoded / sub-delims / ":" ) */
726           g_string_append_encoded (uri, decoded->userinfo, SUB_DELIM_CHARS ":");
727           g_string_append_c (uri, '@');
728         }
729       
730       g_string_append (uri, decoded->host);
731       
732       if (decoded->port != -1)
733         {
734           g_string_append_c (uri, ':');
735           g_string_append_printf (uri, "%d", decoded->port);
736         }
737     }
738
739   g_string_append_encoded (uri, decoded->path, SUB_DELIM_CHARS ":@/");
740   
741   if (decoded->query)
742     {
743       g_string_append_c (uri, '?');
744       g_string_append (uri, decoded->query);
745     }
746     
747   if (decoded->fragment)
748     {
749       g_string_append_c (uri, '#');
750       g_string_append (uri, decoded->fragment);
751     }
752
753   return g_string_free (uri, FALSE);
754 }