(root)/
glib-2.79.0/
gio/
gdummyfile.c
       1  /* GIO - GLib Input, Output and Streaming Library
       2   * 
       3   * Copyright (C) 2006-2007 Red Hat, Inc.
       4   *
       5   * SPDX-License-Identifier: LGPL-2.1-or-later
       6   *
       7   * This library is free software; you can redistribute it and/or
       8   * modify it under the terms of the GNU Lesser General Public
       9   * License as published by the Free Software Foundation; either
      10   * version 2.1 of the License, or (at your option) any later version.
      11   *
      12   * This library is distributed in the hope that it will be useful,
      13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15   * Lesser General Public License for more details.
      16   *
      17   * You should have received a copy of the GNU Lesser General
      18   * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
      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 <stdlib.h>
      31  
      32  #include "gdummyfile.h"
      33  #include "gfile.h"
      34  
      35  
      36  static void g_dummy_file_file_iface_init (GFileIface *iface);
      37  
      38  typedef struct {
      39    char *scheme;
      40    char *userinfo;
      41    char *host;
      42    int port; /* -1 => not in uri */
      43    char *path;
      44    char *query;
      45    char *fragment;
      46  } GDecodedUri;
      47  
      48  struct _GDummyFile
      49  {
      50    GObject parent_instance;
      51  
      52    GDecodedUri *decoded_uri;
      53    char *text_uri;
      54  };
      55  
      56  #define g_dummy_file_get_type _g_dummy_file_get_type
      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, 
      73                                       const char *encoded,
      74  				     const char *reserved_chars_allowed);
      75  
      76  static void
      77  g_dummy_file_finalize (GObject *object)
      78  {
      79    GDummyFile *dummy;
      80  
      81    dummy = G_DUMMY_FILE (object);
      82  
      83    if (dummy->decoded_uri)
      84      _g_decoded_uri_free (dummy->decoded_uri);
      85    
      86    g_free (dummy->text_uri);
      87  
      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  GFile *
     105  _g_dummy_file_new (const char *uri)
     106  {
     107    GDummyFile *dummy;
     108  
     109    g_return_val_if_fail (uri != NULL, NULL);
     110  
     111    dummy = g_object_new (G_TYPE_DUMMY_FILE, NULL);
     112    dummy->text_uri = g_strdup (uri);
     113    dummy->decoded_uri = _g_decode_uri (uri);
     114    
     115    return G_FILE (dummy);
     116  }
     117  
     118  static gboolean
     119  g_dummy_file_is_native (GFile *file)
     120  {
     121    return FALSE;
     122  }
     123  
     124  static char *
     125  g_dummy_file_get_basename (GFile *file)
     126  {
     127    GDummyFile *dummy = G_DUMMY_FILE (file);
     128    
     129    if (dummy->decoded_uri)
     130      return g_path_get_basename (dummy->decoded_uri->path);
     131    return NULL;
     132  }
     133  
     134  static char *
     135  g_dummy_file_get_path (GFile *file)
     136  {
     137    return NULL;
     138  }
     139  
     140  static char *
     141  g_dummy_file_get_uri (GFile *file)
     142  {
     143    return g_strdup (G_DUMMY_FILE (file)->text_uri);
     144  }
     145  
     146  static char *
     147  g_dummy_file_get_parse_name (GFile *file)
     148  {
     149    return g_strdup (G_DUMMY_FILE (file)->text_uri);
     150  }
     151  
     152  static GFile *
     153  g_dummy_file_get_parent (GFile *file)
     154  {
     155    GDummyFile *dummy = G_DUMMY_FILE (file);
     156    GFile *parent;
     157    char *dirname;
     158    char *uri;
     159    GDecodedUri new_decoded_uri;
     160  
     161    if (dummy->decoded_uri == NULL ||
     162        g_strcmp0 (dummy->decoded_uri->path, "/") == 0)
     163      return NULL;
     164  
     165    dirname = g_path_get_dirname (dummy->decoded_uri->path);
     166    
     167    if (strcmp (dirname, ".") == 0)
     168      {
     169        g_free (dirname);
     170        return NULL;
     171      }
     172    
     173    new_decoded_uri = *dummy->decoded_uri;
     174    new_decoded_uri.path = dirname;
     175    uri = _g_encode_uri (&new_decoded_uri);
     176    g_free (dirname);
     177    
     178    parent = _g_dummy_file_new (uri);
     179    g_free (uri);
     180    
     181    return parent;
     182  }
     183  
     184  static GFile *
     185  g_dummy_file_dup (GFile *file)
     186  {
     187    GDummyFile *dummy = G_DUMMY_FILE (file);
     188  
     189    return _g_dummy_file_new (dummy->text_uri);
     190  }
     191  
     192  static guint
     193  g_dummy_file_hash (GFile *file)
     194  {
     195    GDummyFile *dummy = G_DUMMY_FILE (file);
     196    
     197    return g_str_hash (dummy->text_uri);
     198  }
     199  
     200  static gboolean
     201  g_dummy_file_equal (GFile *file1,
     202  		    GFile *file2)
     203  {
     204    GDummyFile *dummy1 = G_DUMMY_FILE (file1);
     205    GDummyFile *dummy2 = G_DUMMY_FILE (file2);
     206  
     207    return g_str_equal (dummy1->text_uri, dummy2->text_uri);
     208  }
     209  
     210  static int
     211  safe_strcmp (const char *a, 
     212               const char *b)
     213  {
     214    if (a == NULL)
     215      a = "";
     216    if (b == NULL)
     217      b = "";
     218  
     219    return strcmp (a, b);
     220  }
     221  
     222  static gboolean
     223  uri_same_except_path (GDecodedUri *a,
     224  		      GDecodedUri *b)
     225  {
     226    if (safe_strcmp (a->scheme, b->scheme) != 0)
     227      return FALSE;
     228    if (safe_strcmp (a->userinfo, b->userinfo) != 0)
     229      return FALSE;
     230    if (safe_strcmp (a->host, b->host) != 0)
     231      return FALSE;
     232    if (a->port != b->port)
     233      return FALSE;
     234  
     235    return TRUE;
     236  }
     237  
     238  static const char *
     239  match_prefix (const char *path, 
     240                const char *prefix)
     241  {
     242    int prefix_len;
     243  
     244    prefix_len = strlen (prefix);
     245    if (strncmp (path, prefix, prefix_len) != 0)
     246      return NULL;
     247    return path + prefix_len;
     248  }
     249  
     250  static gboolean
     251  g_dummy_file_prefix_matches (GFile *parent, GFile *descendant)
     252  {
     253    GDummyFile *parent_dummy = G_DUMMY_FILE (parent);
     254    GDummyFile *descendant_dummy = G_DUMMY_FILE (descendant);
     255    const char *remainder;
     256  
     257    if (parent_dummy->decoded_uri != NULL &&
     258        descendant_dummy->decoded_uri != NULL)
     259      {
     260        if (uri_same_except_path (parent_dummy->decoded_uri,
     261  				descendant_dummy->decoded_uri)) 
     262          {
     263  	  remainder = match_prefix (descendant_dummy->decoded_uri->path,
     264                                      parent_dummy->decoded_uri->path);
     265            if (remainder != NULL && *remainder == '/')
     266  	    {
     267  	      while (*remainder == '/')
     268  	        remainder++;
     269  	      if (*remainder != 0)
     270  	        return TRUE;
     271  	    }
     272          }
     273      }
     274    else
     275      {
     276        remainder = match_prefix (descendant_dummy->text_uri,
     277  				parent_dummy->text_uri);
     278        if (remainder != NULL && *remainder == '/')
     279  	  {
     280  	    while (*remainder == '/')
     281  	      remainder++;
     282  	    if (*remainder != 0)
     283  	      return TRUE;
     284  	  }
     285      }
     286    
     287    return FALSE;
     288  }
     289  
     290  static char *
     291  g_dummy_file_get_relative_path (GFile *parent,
     292  				GFile *descendant)
     293  {
     294    GDummyFile *parent_dummy = G_DUMMY_FILE (parent);
     295    GDummyFile *descendant_dummy = G_DUMMY_FILE (descendant);
     296    const char *remainder;
     297  
     298    if (parent_dummy->decoded_uri != NULL &&
     299        descendant_dummy->decoded_uri != NULL)
     300      {
     301        if (uri_same_except_path (parent_dummy->decoded_uri,
     302  				descendant_dummy->decoded_uri)) 
     303          {
     304            remainder = match_prefix (descendant_dummy->decoded_uri->path,
     305                                      parent_dummy->decoded_uri->path);
     306            if (remainder != NULL && *remainder == '/')
     307  	    {
     308  	      while (*remainder == '/')
     309  	        remainder++;
     310  	      if (*remainder != 0)
     311  	        return g_strdup (remainder);
     312  	    }
     313          }
     314      }
     315    else
     316      {
     317        remainder = match_prefix (descendant_dummy->text_uri,
     318  				parent_dummy->text_uri);
     319        if (remainder != NULL && *remainder == '/')
     320  	  {
     321  	    while (*remainder == '/')
     322  	      remainder++;
     323  	    if (*remainder != 0)
     324  	      return unescape_string (remainder, NULL, "/");
     325  	  }
     326      }
     327    
     328    return NULL;
     329  }
     330  
     331  
     332  static GFile *
     333  g_dummy_file_resolve_relative_path (GFile      *file,
     334  				    const char *relative_path)
     335  {
     336    GDummyFile *dummy = G_DUMMY_FILE (file);
     337    GFile *child;
     338    char *uri;
     339    GDecodedUri new_decoded_uri;
     340    GString *str;
     341  
     342    if (dummy->decoded_uri == NULL)
     343      {
     344        str = g_string_new (dummy->text_uri);
     345        g_string_append (str, "/");
     346        g_string_append_encoded (str, relative_path, SUB_DELIM_CHARS ":@/");
     347        child = _g_dummy_file_new (str->str);
     348        g_string_free (str, TRUE);
     349      }
     350    else
     351      {
     352        new_decoded_uri = *dummy->decoded_uri;
     353        
     354        if (g_path_is_absolute (relative_path))
     355  	new_decoded_uri.path = g_strdup (relative_path);
     356        else
     357  	new_decoded_uri.path = g_build_filename (new_decoded_uri.path, relative_path, NULL);
     358        
     359        uri = _g_encode_uri (&new_decoded_uri);
     360        g_free (new_decoded_uri.path);
     361        
     362        child = _g_dummy_file_new (uri);
     363        g_free (uri);
     364      }
     365  
     366    return child;
     367  }
     368  
     369  static GFile *
     370  g_dummy_file_get_child_for_display_name (GFile        *file,
     371  					 const char   *display_name,
     372  					 GError      **error)
     373  {
     374    return g_file_get_child (file, display_name);
     375  }
     376  
     377  static gboolean
     378  g_dummy_file_has_uri_scheme (GFile *file,
     379  			     const char *uri_scheme)
     380  {
     381    GDummyFile *dummy = G_DUMMY_FILE (file);
     382    
     383    if (dummy->decoded_uri)
     384      return g_ascii_strcasecmp (uri_scheme, dummy->decoded_uri->scheme) == 0;
     385    return FALSE;
     386  }
     387  
     388  static char *
     389  g_dummy_file_get_uri_scheme (GFile *file)
     390  {
     391    GDummyFile *dummy = G_DUMMY_FILE (file);
     392  
     393    if (dummy->decoded_uri)
     394      return g_strdup (dummy->decoded_uri->scheme);
     395      
     396    return NULL;
     397  }
     398  
     399  
     400  static void
     401  g_dummy_file_file_iface_init (GFileIface *iface)
     402  {
     403    iface->dup = g_dummy_file_dup;
     404    iface->hash = g_dummy_file_hash;
     405    iface->equal = g_dummy_file_equal;
     406    iface->is_native = g_dummy_file_is_native;
     407    iface->has_uri_scheme = g_dummy_file_has_uri_scheme;
     408    iface->get_uri_scheme = g_dummy_file_get_uri_scheme;
     409    iface->get_basename = g_dummy_file_get_basename;
     410    iface->get_path = g_dummy_file_get_path;
     411    iface->get_uri = g_dummy_file_get_uri;
     412    iface->get_parse_name = g_dummy_file_get_parse_name;
     413    iface->get_parent = g_dummy_file_get_parent;
     414    iface->prefix_matches = g_dummy_file_prefix_matches;
     415    iface->get_relative_path = g_dummy_file_get_relative_path;
     416    iface->resolve_relative_path = g_dummy_file_resolve_relative_path;
     417    iface->get_child_for_display_name = g_dummy_file_get_child_for_display_name;
     418  
     419    iface->supports_thread_contexts = TRUE;
     420  }
     421  
     422  /* Uri handling helper functions: */
     423  
     424  static int
     425  unescape_character (const char *scanner)
     426  {
     427    int first_digit;
     428    int second_digit;
     429    
     430    first_digit = g_ascii_xdigit_value (*scanner++);
     431    if (first_digit < 0)
     432      return -1;
     433  
     434    second_digit = g_ascii_xdigit_value (*scanner++);
     435    if (second_digit < 0)
     436      return -1;
     437  
     438    return (first_digit << 4) | second_digit;
     439  }
     440  
     441  static char *
     442  unescape_string (const gchar *escaped_string,
     443  		 const gchar *escaped_string_end,
     444  		 const gchar *illegal_characters)
     445  {
     446    const gchar *in;
     447    gchar *out, *result;
     448    gint character;
     449    
     450    if (escaped_string == NULL)
     451      return NULL;
     452  
     453    if (escaped_string_end == NULL)
     454      escaped_string_end = escaped_string + strlen (escaped_string);
     455    
     456    result = g_malloc (escaped_string_end - escaped_string + 1);
     457  	
     458    out = result;
     459    for (in = escaped_string; in < escaped_string_end; in++) 
     460      {
     461        character = *in;
     462        if (*in == '%') 
     463          {
     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_warn_if_fail ((gsize) (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,
     684                           const char *encoded,
     685  			 const char *reserved_chars_allowed)
     686  {
     687    unsigned char c;
     688    static const gchar hex[] = "0123456789ABCDEF";
     689  
     690    while ((c = *encoded) != 0)
     691      {
     692        if (is_valid (c, reserved_chars_allowed))
     693  	{
     694  	  g_string_append_c (string, c);
     695  	  encoded++;
     696  	}
     697        else
     698  	{
     699  	  g_string_append_c (string, '%');
     700  	  g_string_append_c (string, hex[((guchar)c) >> 4]);
     701  	  g_string_append_c (string, hex[((guchar)c) & 0xf]);
     702  	  encoded++;
     703  	}
     704      }
     705  }
     706  
     707  static char *
     708  _g_encode_uri (GDecodedUri *decoded)
     709  {
     710    GString *uri;
     711  
     712    uri = g_string_new (NULL);
     713  
     714    g_string_append (uri, decoded->scheme);
     715    g_string_append (uri, "://");
     716  
     717    if (decoded->host != NULL)
     718      {
     719        if (decoded->userinfo)
     720  	{
     721  	  /* userinfo    = *( unreserved / pct-encoded / sub-delims / ":" ) */
     722  	  g_string_append_encoded (uri, decoded->userinfo, SUB_DELIM_CHARS ":");
     723  	  g_string_append_c (uri, '@');
     724  	}
     725        
     726        g_string_append (uri, decoded->host);
     727        
     728        if (decoded->port != -1)
     729  	{
     730  	  g_string_append_c (uri, ':');
     731  	  g_string_append_printf (uri, "%d", decoded->port);
     732  	}
     733      }
     734  
     735    g_string_append_encoded (uri, decoded->path, SUB_DELIM_CHARS ":@/");
     736    
     737    if (decoded->query)
     738      {
     739        g_string_append_c (uri, '?');
     740        g_string_append (uri, decoded->query);
     741      }
     742      
     743    if (decoded->fragment)
     744      {
     745        g_string_append_c (uri, '#');
     746        g_string_append (uri, decoded->fragment);
     747      }
     748  
     749    return g_string_free (uri, FALSE);
     750  }