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