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