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