LCOV - code coverage report
Current view: top level - libgnucash/engine - gnc-uri-utils.c (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 75.4 % 179 135
Test Date: 2026-11-02 18:19:43 Functions: 72.7 % 11 8
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*
       2              :  * gnc-uri-utils.c -- utility functions to convert uri in separate
       3              :  *                    components and back.
       4              :  *
       5              :  * Copyright (C) 2010 Geert Janssens <janssens.geert@telenet.be>
       6              :  *
       7              :  * This program is free software; you can redistribute it and/or
       8              :  * modify it under the terms of the GNU General Public License as
       9              :  * published by the Free Software Foundation; either version 2 of
      10              :  * the License, or (at your option) any later version.
      11              :  *
      12              :  * This program 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
      15              :  * GNU General Public License for more details.
      16              :  *
      17              :  * You should have received a copy of the GNU General Public License
      18              :  * along with this program; if not, contact:
      19              :  *
      20              :  * Free Software Foundation           Voice:  +1-617-542-5942
      21              :  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
      22              :  * Boston, MA  02110-1301,  USA       gnu@gnu.org
      23              :  */
      24              : 
      25              : #include <glib.h>
      26              : #include "gnc-uri-utils.h"
      27              : #include "gnc-filepath-utils.h"
      28              : #include "qofsession.h"
      29              : 
      30              : /* Checks if the given uri is a valid uri
      31              :  */
      32            0 : gboolean gnc_uri_is_uri (const gchar *uri)
      33              : {
      34              : 
      35            0 :     gchar *scheme = NULL, *hostname = NULL;
      36            0 :     gchar *username = NULL, *password = NULL;
      37            0 :     gchar *path     = NULL;
      38            0 :     gint   port = 0;
      39            0 :     gboolean is_uri = FALSE;
      40              : 
      41            0 :     gnc_uri_get_components ( uri, &scheme, &hostname, &port,
      42              :                              &username, &password, &path );
      43              : 
      44              :     /* For gnucash to consider a uri valid the following must be true:
      45              :      * - scheme and path must not be NULL
      46              :      * - for anything but local filesystem uris, hostname must be valid as well */
      47            0 :     is_uri = (scheme && path && (gnc_uri_is_file_scheme(scheme) || hostname));
      48              : 
      49            0 :     g_free (scheme);
      50            0 :     g_free (hostname);
      51            0 :     g_free (username);
      52            0 :     g_free (password);
      53            0 :     g_free (path);
      54              : 
      55            0 :     return is_uri;
      56              : }
      57              : 
      58              : /* Checks if the given scheme is used to refer to a file
      59              :  * (as opposed to a network service)
      60              :  */
      61            6 : gboolean gnc_uri_is_known_scheme (const gchar *scheme)
      62              : {
      63            6 :     gboolean is_known_scheme = FALSE;
      64              :     GList *node;
      65            6 :     GList *known_scheme_list = qof_backend_get_registered_access_method_list();
      66              : 
      67            6 :     for ( node = known_scheme_list; node != NULL; node = node->next )
      68              :     {
      69            0 :         gchar *known_scheme = node->data;
      70            0 :         if ( !g_ascii_strcasecmp (scheme, known_scheme) )
      71              :         {
      72            0 :             is_known_scheme = TRUE;
      73            0 :             break;
      74              :         }
      75              :     }
      76              : 
      77            6 :     g_list_free (known_scheme_list);
      78            6 :     return is_known_scheme;
      79              : }
      80              : 
      81              : /* Checks if the given scheme is used to refer to a file
      82              :  * (as opposed to a network service)
      83              :  * Note unknown schemes are always considered network schemes.
      84              :  *
      85              :  * *Compatibility note:*
      86              :  * This used to be the other way around before gnucash 3.4. Before
      87              :  * that unknown schemes were always considered local file system
      88              :  * uri schemes.
      89              :  */
      90          346 : gboolean gnc_uri_is_file_scheme (const gchar *scheme)
      91              : {
      92          912 :     return (scheme &&
      93          566 :             (!g_ascii_strcasecmp (scheme, "file") ||
      94          427 :              !g_ascii_strcasecmp (scheme, "xml") ||
      95          205 :              !g_ascii_strcasecmp (scheme, "sqlite3")));
      96              : }
      97              : 
      98              : /* Checks if the given uri defines a file
      99              :  * (as opposed to a network service)
     100              :  */
     101           18 : gboolean gnc_uri_is_file_uri (const gchar *uri)
     102              : {
     103           18 :     gchar *scheme = gnc_uri_get_scheme ( uri );
     104           18 :     gboolean result = gnc_uri_is_file_scheme ( scheme );
     105              : 
     106           18 :     g_free ( scheme );
     107              : 
     108           18 :     return result;
     109              : }
     110              : 
     111              : /* Checks if the given uri is a valid uri
     112              :  */
     113            0 : gboolean gnc_uri_targets_local_fs (const gchar *uri)
     114              : {
     115              : 
     116            0 :     gchar *scheme = NULL, *hostname = NULL;
     117            0 :     gchar *username = NULL, *password = NULL;
     118            0 :     gchar *path     = NULL;
     119            0 :     gint   port = 0;
     120            0 :     gboolean is_local_fs = FALSE;
     121              : 
     122            0 :     gnc_uri_get_components ( uri, &scheme, &hostname, &port,
     123              :                              &username, &password, &path );
     124              : 
     125              :     /* For gnucash to consider a uri to target the local fs:
     126              :      * path must not be NULL
     127              :      * AND
     128              :      *   scheme should be NULL
     129              :      *   OR
     130              :      *   scheme must be file type scheme (file, xml, sqlite) */
     131            0 :     is_local_fs = (path && (!scheme || gnc_uri_is_file_scheme(scheme)));
     132              : 
     133            0 :     g_free (scheme);
     134            0 :     g_free (hostname);
     135            0 :     g_free (username);
     136            0 :     g_free (password);
     137            0 :     g_free (path);
     138              : 
     139            0 :     return is_local_fs;
     140              : }
     141              : 
     142              : /* Splits a uri into its separate components */
     143          239 : void gnc_uri_get_components (const gchar *uri,
     144              :                              gchar **scheme,
     145              :                              gchar **hostname,
     146              :                              gint32 *port,
     147              :                              gchar **username,
     148              :                              gchar **password,
     149              :                              gchar **path)
     150              : {
     151              :     gchar **splituri;
     152          239 :     gchar *url = NULL, *tmpusername = NULL, *tmphostname = NULL;
     153          239 :     gchar *delimiter = NULL;
     154              : 
     155          239 :     *scheme   = NULL;
     156          239 :     *hostname = NULL;
     157          239 :     *port     = 0;
     158          239 :     *username = NULL;
     159          239 :     *password = NULL;
     160          239 :     *path     = NULL;
     161              : 
     162          239 :     g_return_if_fail( uri != NULL && strlen (uri) > 0);
     163              : 
     164          239 :     splituri = g_strsplit ( uri, "://", 2 );
     165          239 :     if ( splituri[1] == NULL )
     166              :     {
     167              :         /* No scheme means simple file path.
     168              :            Set path to copy of the input. */
     169           33 :         *path     = g_strdup ( uri );
     170           33 :         g_strfreev ( splituri );
     171           33 :         return;
     172              :     }
     173              : 
     174              :     /* At least a scheme was found, set it here */
     175          206 :     *scheme = g_strdup ( splituri[0] );
     176              : 
     177          206 :     if ( gnc_uri_is_file_scheme ( *scheme ) )
     178              :     {
     179              :         /* a true file uri on windows can start file:///N:/
     180              :            so we come here with /N:/, it could also be /N:\
     181              :         */
     182          272 :         if (g_str_has_prefix (splituri[1], "/") &&
     183          272 :            ((g_strstr_len (splituri[1], -1,  ":/") != NULL) || (g_strstr_len (splituri[1], -1,  ":\\") != NULL)))
     184            0 :         {
     185            0 :             gchar *ptr = splituri[1];
     186            0 :             *path = gnc_resolve_file_path ( ptr + 1 );
     187              :         }
     188              :         else
     189          136 :             *path = gnc_resolve_file_path ( splituri[1] );
     190          136 :         g_strfreev ( splituri );
     191          136 :         return;
     192              :     }
     193              : 
     194              :     /* Protocol indicates full network style uri, let's see if it
     195              :      * has a username and/or password
     196              :      */
     197           70 :     url = g_strdup (splituri[1]);
     198           70 :     g_strfreev ( splituri );
     199              : 
     200              :     /* Check for "@" sign, but start from the end - the password may contain
     201              :      * this sign as well
     202              :      */
     203           70 :     delimiter = g_strrstr ( url, "@" );
     204           70 :     if ( delimiter != NULL )
     205              :     {
     206              :         /* There is at least a username in the url */
     207           50 :         delimiter[0] = '\0';
     208           50 :         tmpusername = url;
     209           50 :         tmphostname = delimiter + 1;
     210              : 
     211              :         /* Check if there's a password too by looking for a :
     212              :          * Start from the beginning this time to avoid possible :
     213              :          * in the password */
     214           50 :         delimiter = g_strstr_len ( tmpusername, -1, ":" );
     215           50 :         if ( delimiter != NULL )
     216              :         {
     217              :             /* There is password in the url */
     218           30 :             delimiter[0] = '\0';
     219           60 :             *password = g_strdup ( (const gchar*)(delimiter + 1) );
     220              :         }
     221           50 :         *username = g_strdup ( (const gchar*)tmpusername );
     222              :     }
     223              :     else
     224              :     {
     225              :         /* No username and password were given */
     226           20 :         tmphostname = url;
     227              :     }
     228              : 
     229              :     /* Find the path part */
     230           70 :     delimiter = g_strstr_len ( tmphostname, -1, "/" );
     231           70 :     if ( delimiter != NULL )
     232              :     {
     233           70 :         delimiter[0] = '\0';
     234           70 :         if ( gnc_uri_is_file_scheme ( *scheme ) ) /* always return absolute file paths */
     235            0 :             *path = gnc_resolve_file_path ( (const gchar*)(delimiter + 1) );
     236              :         else /* path is no file path, so copy it as is */
     237          140 :             *path = g_strdup ( (const gchar*)(delimiter + 1) );
     238              :     }
     239              : 
     240              :     /* Check for a port specifier */
     241           70 :     delimiter = g_strstr_len ( tmphostname, -1, ":" );
     242           70 :     if ( delimiter != NULL )
     243              :     {
     244            5 :         delimiter[0] = '\0';
     245            5 :         *port = g_ascii_strtoll ( delimiter + 1, NULL, 0 );
     246              :     }
     247              : 
     248           70 :     *hostname = g_strdup ( (const gchar*)tmphostname );
     249              : 
     250           70 :     g_free ( url );
     251              : 
     252           70 :     return;
     253              : 
     254              : }
     255              : 
     256           36 : gchar *gnc_uri_get_scheme (const gchar *uri)
     257              : {
     258           36 :     gchar *scheme   = NULL;
     259           36 :     gchar *hostname = NULL;
     260           36 :     gint32 port     = 0;
     261           36 :     gchar *username = NULL;
     262           36 :     gchar *password = NULL;
     263           36 :     gchar *path     = NULL;
     264              : 
     265           36 :     gnc_uri_get_components ( uri, &scheme, &hostname, &port,
     266              :                              &username, &password, &path );
     267              : 
     268           36 :     g_free (hostname);
     269           36 :     g_free (username);
     270           36 :     g_free (password);
     271           36 :     g_free (path);
     272              : 
     273           36 :     return scheme;
     274              : }
     275              : 
     276          139 : gchar *gnc_uri_get_path (const gchar *uri)
     277              : {
     278          139 :     gchar *scheme   = NULL;
     279          139 :     gchar *hostname = NULL;
     280          139 :     gint32 port = 0;
     281          139 :     gchar *username = NULL;
     282          139 :     gchar *password = NULL;
     283          139 :     gchar *path     = NULL;
     284              : 
     285          139 :     gnc_uri_get_components ( uri, &scheme, &hostname, &port,
     286              :                              &username, &password, &path );
     287              : 
     288          139 :     g_free (scheme);
     289          139 :     g_free (hostname);
     290          139 :     g_free (username);
     291          139 :     g_free (password);
     292              : 
     293          139 :     return path;
     294              : }
     295              : 
     296              : /* Generates a normalized uri from the separate components */
     297           64 : gchar *gnc_uri_create_uri (const gchar *scheme,
     298              :                            const gchar *hostname,
     299              :                            gint32 port,
     300              :                            const gchar *username,
     301              :                            const gchar *password,
     302              :                            const gchar *path)
     303              : {
     304           64 :     gchar *userpass = NULL, *portstr = NULL, *uri = NULL;
     305              : 
     306           64 :     g_return_val_if_fail( path != 0, NULL );
     307              : 
     308           64 :     if (!scheme || gnc_uri_is_file_scheme (scheme))
     309              :     {
     310              :         /* Compose a file based uri, which means ignore everything but
     311              :          * the scheme and the path
     312              :          * We return an absolute pathname if the scheme is known or
     313              :          * no scheme was given. For an unknown scheme, we return the
     314              :          * path info as is.
     315              :          */
     316              :         gchar *abs_path;
     317              :         gchar *uri_scheme;
     318           36 :         if (scheme && (!gnc_uri_is_known_scheme (scheme)) )
     319            6 :             abs_path = g_strdup ( path );
     320              :         else
     321           30 :             abs_path = gnc_resolve_file_path ( path );
     322              : 
     323           36 :         if (!scheme)
     324           30 :             uri_scheme = g_strdup ("file");
     325              :         else
     326            6 :             uri_scheme = g_strdup (scheme);
     327              : 
     328              :         /* Arrive here with...
     329              :          *
     330              :          * /my/path/to/file with space.txt
     331              :          * becomes file:///my/path/to/file with space.txt
     332              :          *
     333              :          * c:\my\path\to\file with space.txt
     334              :          * becomes file:///c:\my\path\to\file with space.txt
     335              :          *
     336              :          * \\myserver\share\path\to\file with space.txt
     337              :          * becomes file://\\myserver\share\path\to\file with space.txt
     338              :          *
     339              :          * probably they should all be forward slashes and spaces escaped
     340              :          * also with UNC it could be file://myserver/share/path/to/file with space.txt
     341              :          */
     342              : 
     343           36 :         if (g_str_has_prefix (abs_path, "/") || g_str_has_prefix (abs_path, "\\"))
     344           36 :             uri = g_strdup_printf ( "%s://%s", uri_scheme, abs_path );
     345              :         else // for windows add an extra "/"
     346            0 :             uri = g_strdup_printf ( "%s:///%s", uri_scheme, abs_path );
     347              : 
     348           36 :         g_free (uri_scheme);
     349           36 :         g_free (abs_path);
     350              : 
     351           36 :         return uri;
     352              :     }
     353              : 
     354              :     /* Not a file based uri, we need to setup all components that are not NULL
     355              :      * For this scenario, hostname is mandatory.
     356              :      */
     357           28 :     g_return_val_if_fail( hostname != 0, NULL );
     358              : 
     359           28 :     if ( username != NULL && *username )
     360              :     {
     361           20 :         if ( password != NULL && *password )
     362            9 :             userpass = g_strdup_printf ( "%s:%s@", username, password );
     363              :         else
     364           11 :             userpass = g_strdup_printf ( "%s@", username );
     365              :     }
     366              :     else
     367            8 :         userpass = g_strdup ( "" );
     368              : 
     369           28 :     if ( port != 0 )
     370            2 :         portstr = g_strdup_printf ( ":%d", port );
     371              :     else
     372           26 :         portstr = g_strdup ( "" );
     373              : 
     374              :     // XXX Do I have to add the slash always or are there situations
     375              :     //     it is in the path already ?
     376           28 :     uri = g_strconcat ( scheme, "://", userpass, hostname, portstr, "/", path, NULL );
     377              : 
     378           28 :     g_free ( userpass );
     379           28 :     g_free ( portstr );
     380              : 
     381           28 :     return uri;
     382              : 
     383              : }
     384              : 
     385           46 : gchar *gnc_uri_normalize_uri (const gchar *uri, gboolean allow_password)
     386              : {
     387           46 :     gchar *scheme   = NULL;
     388           46 :     gchar *hostname = NULL;
     389           46 :     gint32 port = 0;
     390           46 :     gchar *username = NULL;
     391           46 :     gchar *password = NULL;
     392           46 :     gchar *path     = NULL;
     393           46 :     gchar *newuri   = NULL;
     394              : 
     395           46 :     gnc_uri_get_components ( uri, &scheme, &hostname, &port,
     396              :                              &username, &password, &path );
     397           46 :     if (allow_password)
     398            7 :         newuri = gnc_uri_create_uri ( scheme, hostname, port,
     399              :                                       username, password, path);
     400              :     else
     401           39 :         newuri = gnc_uri_create_uri ( scheme, hostname, port,
     402              :                                       username, /* no password */ NULL, path);
     403              : 
     404           46 :     g_free (scheme);
     405           46 :     g_free (hostname);
     406           46 :     g_free (username);
     407           46 :     g_free (password);
     408           46 :     g_free (path);
     409              : 
     410           46 :     return newuri;
     411              : }
     412              : 
     413            0 : gchar *gnc_uri_add_extension ( const gchar *uri, const gchar *extension )
     414              : {
     415            0 :     g_return_val_if_fail( uri != 0, NULL );
     416              : 
     417              :     /* Only add extension if the user provided the extension and the uri is
     418              :      * file based.
     419              :      */
     420            0 :     if ( !extension || !gnc_uri_is_file_uri( uri ) )
     421            0 :         return g_strdup( uri );
     422              : 
     423              :     /* Don't add extension if it's already there */
     424            0 :     if ( g_str_has_suffix( uri, extension ) )
     425            0 :         return g_strdup( uri );
     426              : 
     427              :     /* Ok, all tests passed, let's add the extension */
     428            0 :     return g_strconcat( uri, extension, NULL );
     429              : }
        

Generated by: LCOV version 2.0-1