GnuCash  4.8a-132-gcdaeb421d+
gnc-uri-utils.c
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 gboolean gnc_uri_is_uri (const gchar *uri)
33 {
34 
35  gchar *scheme = NULL, *hostname = NULL;
36  gchar *username = NULL, *password = NULL;
37  gchar *path = NULL;
38  gint port = 0;
39  gboolean is_uri = FALSE;
40 
41  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  is_uri = (scheme && path && (gnc_uri_is_file_scheme(scheme) || hostname));
48 
49  g_free (scheme);
50  g_free (hostname);
51  g_free (username);
52  g_free (password);
53  g_free (path);
54 
55  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 gboolean gnc_uri_is_known_scheme (const gchar *scheme)
62 {
63  gboolean is_known_scheme = FALSE;
64  GList *node;
65  GList *known_scheme_list = qof_backend_get_registered_access_method_list();
66 
67  for ( node = known_scheme_list; node != NULL; node = node->next )
68  {
69  gchar *known_scheme = node->data;
70  if ( !g_ascii_strcasecmp (scheme, known_scheme) )
71  {
72  is_known_scheme = TRUE;
73  break;
74  }
75  }
76 
77  g_list_free (known_scheme_list);
78  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 gboolean gnc_uri_is_file_scheme (const gchar *scheme)
91 {
92  return (scheme &&
93  (!g_ascii_strcasecmp (scheme, "file") ||
94  !g_ascii_strcasecmp (scheme, "xml") ||
95  !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 gboolean gnc_uri_is_file_uri (const gchar *uri)
102 {
103  gchar *scheme = gnc_uri_get_scheme ( uri );
104  gboolean result = gnc_uri_is_file_scheme ( scheme );
105 
106  g_free ( scheme );
107 
108  return result;
109 }
110 
111 /* Checks if the given uri is a valid uri
112  */
113 gboolean gnc_uri_targets_local_fs (const gchar *uri)
114 {
115 
116  gchar *scheme = NULL, *hostname = NULL;
117  gchar *username = NULL, *password = NULL;
118  gchar *path = NULL;
119  gint port = 0;
120  gboolean is_local_fs = FALSE;
121 
122  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  is_local_fs = (path && (!scheme || gnc_uri_is_file_scheme(scheme)));
132 
133  g_free (scheme);
134  g_free (hostname);
135  g_free (username);
136  g_free (password);
137  g_free (path);
138 
139  return is_local_fs;
140 }
141 
142 /* Splits a uri into its separate components */
143 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  gchar *url = NULL, *tmpusername = NULL, *tmphostname = NULL;
153  gchar *delimiter = NULL;
154 
155  *scheme = NULL;
156  *hostname = NULL;
157  *port = 0;
158  *username = NULL;
159  *password = NULL;
160  *path = NULL;
161 
162  g_return_if_fail( uri != NULL && strlen (uri) > 0);
163 
164  splituri = g_strsplit ( uri, "://", 2 );
165  if ( splituri[1] == NULL )
166  {
167  /* No scheme means simple file path.
168  Set path to copy of the input. */
169  *path = g_strdup ( uri );
170  g_strfreev ( splituri );
171  return;
172  }
173 
174  /* At least a scheme was found, set it here */
175  *scheme = g_strdup ( splituri[0] );
176 
177  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  if (g_str_has_prefix (splituri[1], "/") &&
183  ((g_strstr_len (splituri[1], -1, ":/") != NULL) || (g_strstr_len (splituri[1], -1, ":\\") != NULL)))
184  {
185  gchar *ptr = splituri[1];
186  *path = gnc_resolve_file_path ( ptr + 1 );
187  }
188  else
189  *path = gnc_resolve_file_path ( splituri[1] );
190  g_strfreev ( splituri );
191  return;
192  }
193 
194  /* Protocol indicates full network style uri, let's see if it
195  * has a username and/or password
196  */
197  url = g_strdup (splituri[1]);
198  g_strfreev ( splituri );
199 
200  /* Check for "@" sign, but start from the end - the password may contain
201  * this sign as well
202  */
203  delimiter = g_strrstr ( url, "@" );
204  if ( delimiter != NULL )
205  {
206  /* There is at least a username in the url */
207  delimiter[0] = '\0';
208  tmpusername = url;
209  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  delimiter = g_strstr_len ( tmpusername, -1, ":" );
215  if ( delimiter != NULL )
216  {
217  /* There is password in the url */
218  delimiter[0] = '\0';
219  *password = g_strdup ( (const gchar*)(delimiter + 1) );
220  }
221  *username = g_strdup ( (const gchar*)tmpusername );
222  }
223  else
224  {
225  /* No username and password were given */
226  tmphostname = url;
227  }
228 
229  /* Find the path part */
230  delimiter = g_strstr_len ( tmphostname, -1, "/" );
231  if ( delimiter != NULL )
232  {
233  delimiter[0] = '\0';
234  if ( gnc_uri_is_file_scheme ( *scheme ) ) /* always return absolute file paths */
235  *path = gnc_resolve_file_path ( (const gchar*)(delimiter + 1) );
236  else /* path is no file path, so copy it as is */
237  *path = g_strdup ( (const gchar*)(delimiter + 1) );
238  }
239 
240  /* Check for a port specifier */
241  delimiter = g_strstr_len ( tmphostname, -1, ":" );
242  if ( delimiter != NULL )
243  {
244  delimiter[0] = '\0';
245  *port = g_ascii_strtoll ( delimiter + 1, NULL, 0 );
246  }
247 
248  *hostname = g_strdup ( (const gchar*)tmphostname );
249 
250  g_free ( url );
251 
252  return;
253 
254 }
255 
256 gchar *gnc_uri_get_scheme (const gchar *uri)
257 {
258  gchar *scheme = NULL;
259  gchar *hostname = NULL;
260  gint32 port = 0;
261  gchar *username = NULL;
262  gchar *password = NULL;
263  gchar *path = NULL;
264 
265  gnc_uri_get_components ( uri, &scheme, &hostname, &port,
266  &username, &password, &path );
267 
268  g_free (hostname);
269  g_free (username);
270  g_free (password);
271  g_free (path);
272 
273  return scheme;
274 }
275 
276 gchar *gnc_uri_get_path (const gchar *uri)
277 {
278  gchar *scheme = NULL;
279  gchar *hostname = NULL;
280  gint32 port = 0;
281  gchar *username = NULL;
282  gchar *password = NULL;
283  gchar *path = NULL;
284 
285  gnc_uri_get_components ( uri, &scheme, &hostname, &port,
286  &username, &password, &path );
287 
288  g_free (scheme);
289  g_free (hostname);
290  g_free (username);
291  g_free (password);
292 
293  return path;
294 }
295 
296 /* Generates a normalized uri from the separate components */
297 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  gchar *userpass = NULL, *portstr = NULL, *uri = NULL;
305 
306  g_return_val_if_fail( path != 0, NULL );
307 
308  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  if (scheme && (!gnc_uri_is_known_scheme (scheme)) )
319  abs_path = g_strdup ( path );
320  else
321  abs_path = gnc_resolve_file_path ( path );
322 
323  if (!scheme)
324  uri_scheme = g_strdup ("file");
325  else
326  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  if (g_str_has_prefix (abs_path, "/") || g_str_has_prefix (abs_path, "\\"))
344  uri = g_strdup_printf ( "%s://%s", uri_scheme, abs_path );
345  else // for windows add an extra "/"
346  uri = g_strdup_printf ( "%s:///%s", uri_scheme, abs_path );
347 
348  g_free (uri_scheme);
349  g_free (abs_path);
350 
351  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  g_return_val_if_fail( hostname != 0, NULL );
358 
359  if ( username != NULL && *username )
360  {
361  if ( password != NULL && *password )
362  userpass = g_strdup_printf ( "%s:%s@", username, password );
363  else
364  userpass = g_strdup_printf ( "%s@", username );
365  }
366  else
367  userpass = g_strdup ( "" );
368 
369  if ( port != 0 )
370  portstr = g_strdup_printf ( ":%d", port );
371  else
372  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  uri = g_strconcat ( scheme, "://", userpass, hostname, portstr, "/", path, NULL );
377 
378  g_free ( userpass );
379  g_free ( portstr );
380 
381  return uri;
382 
383 }
384 
385 gchar *gnc_uri_normalize_uri (const gchar *uri, gboolean allow_password)
386 {
387  gchar *scheme = NULL;
388  gchar *hostname = NULL;
389  gint32 port = 0;
390  gchar *username = NULL;
391  gchar *password = NULL;
392  gchar *path = NULL;
393  gchar *newuri = NULL;
394 
395  gnc_uri_get_components ( uri, &scheme, &hostname, &port,
396  &username, &password, &path );
397  if (allow_password)
398  newuri = gnc_uri_create_uri ( scheme, hostname, port,
399  username, password, path);
400  else
401  newuri = gnc_uri_create_uri ( scheme, hostname, port,
402  username, /* no password */ NULL, path);
403 
404  g_free (scheme);
405  g_free (hostname);
406  g_free (username);
407  g_free (password);
408  g_free (path);
409 
410  return newuri;
411 }
412 
413 gchar *gnc_uri_add_extension ( const gchar *uri, const gchar *extension )
414 {
415  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  if ( !extension || !gnc_uri_is_file_uri( uri ) )
421  return g_strdup( uri );
422 
423  /* Don't add extension if it's already there */
424  if ( g_str_has_suffix( uri, extension ) )
425  return g_strdup( uri );
426 
427  /* Ok, all tests passed, let's add the extension */
428  return g_strconcat( uri, extension, NULL );
429 }
430 
431 
432 /* Deprecated functions
433  * ********************/
434 
435 /* replaced with gnc_uri_get_scheme */
436 gchar *gnc_uri_get_protocol (const gchar *uri)
437 {
438  return gnc_uri_get_scheme (uri);
439 }
440 
441 /* replaced with gnc_uri_is_known_scheme */
442 gboolean gnc_uri_is_known_protocol (const gchar *protocol)
443 {
444  return gnc_uri_is_known_scheme(protocol);
445 }
446 
447 /* replaced with gnc_uri_is_file_scheme */
448 gboolean gnc_uri_is_file_protocol (const gchar *protocol)
449 {
450  return gnc_uri_is_file_scheme (protocol);
451 }
Encapsulates a connection to a backend (persistent store)
gchar * gnc_uri_get_scheme(const gchar *uri)
Extracts the scheme from a uri.
gboolean gnc_uri_is_file_uri(const gchar *uri)
Checks if the given uri defines a file (as opposed to a network service like a database or web url) ...
gboolean gnc_uri_is_file_scheme(const gchar *scheme)
Checks if the given scheme is used to refer to a file (as opposed to a network service like a databas...
Definition: gnc-uri-utils.c:90
gboolean gnc_uri_is_file_protocol(const gchar *protocol)
Checks if the given protocol is used to refer to a file (as opposed to a network service like a datab...
gchar * gnc_uri_get_path(const gchar *uri)
Extracts the path part from a uri.
void gnc_uri_get_components(const gchar *uri, gchar **scheme, gchar **hostname, gint32 *port, gchar **username, gchar **password, gchar **path)
Converts a uri in separate components.
gboolean gnc_uri_is_uri(const gchar *uri)
Checks if the given uri is a valid uri.
Definition: gnc-uri-utils.c:32
gchar * gnc_resolve_file_path(const gchar *filefrag)
The gnc_resolve_file_path() routine is a utility that will accept a fragmentary filename as input...
gchar * gnc_uri_normalize_uri(const gchar *uri, gboolean allow_password)
Composes a normalized uri starting from any uri (filename, db spec,...).
gchar * gnc_uri_get_protocol(const gchar *uri)
Extracts the protocol from a uri.
GList * qof_backend_get_registered_access_method_list(void)
Return a list of strings for the registered access methods.
Definition: qofsession.cpp:107
gboolean gnc_uri_is_known_protocol(const gchar *protocol)
Checks if there is a backend that explicitly stated to handle the given protocol. ...
gchar * gnc_uri_add_extension(const gchar *uri, const gchar *extension)
Adds an extension to the uri if:
gboolean gnc_uri_targets_local_fs(const gchar *uri)
Checks if the given uri is either a valid file uri or a local filesystem path.
gboolean gnc_uri_is_known_scheme(const gchar *scheme)
Checks if there is a backend that explicitly stated to handle the given scheme.
Definition: gnc-uri-utils.c:61
Utility functions for convert uri in separate components and back.
File path resolution utility functions.
gchar * gnc_uri_create_uri(const gchar *scheme, const gchar *hostname, gint32 port, const gchar *username, const gchar *password, const gchar *path)
Composes a normalized uri starting from its separate components.