GnuCash c935c2f+
Loading...
Searching...
No Matches
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 */
32gboolean 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 */
61gboolean 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 */
90gboolean 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 */
101gboolean 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 */
113gboolean 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 */
143void 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
256gchar *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
276gchar *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 */
297gchar *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
385gchar *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
413gchar *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}
File path resolution utility functions.
Utility functions for convert uri in separate components and back.
gchar * gnc_uri_get_path(const gchar *uri)
Extracts the path part from a uri.
gboolean gnc_uri_is_uri(const gchar *uri)
Checks if the given uri is a valid uri.
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.
gchar * gnc_uri_get_scheme(const gchar *uri)
Extracts the scheme from a uri.
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...
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)
gchar * gnc_uri_add_extension(const gchar *uri, const gchar *extension)
Adds an extension to the uri if:
gchar * gnc_uri_normalize_uri(const gchar *uri, gboolean allow_password)
Composes a normalized uri starting from any uri (filename, db spec,...).
gboolean gnc_uri_is_known_scheme(const gchar *scheme)
Checks if there is a backend that explicitly stated to handle the given scheme.
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.
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.
GList * qof_backend_get_registered_access_method_list(void)
Return a list of strings for the registered access methods.
Encapsulates a connection to a backend (persistent store)