Branch data 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 : 238 : gboolean gnc_uri_is_file_scheme (const gchar *scheme)
91 : : {
92 : 695 : return (scheme &&
93 : 457 : (!g_ascii_strcasecmp (scheme, "file") ||
94 : 426 : !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 : 160 : 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 : 160 : gchar *url = NULL, *tmpusername = NULL, *tmphostname = NULL;
153 : 160 : gchar *delimiter = NULL;
154 : :
155 : 160 : *scheme = NULL;
156 : 160 : *hostname = NULL;
157 : 160 : *port = 0;
158 : 160 : *username = NULL;
159 : 160 : *password = NULL;
160 : 160 : *path = NULL;
161 : :
162 : 160 : g_return_if_fail( uri != NULL && strlen (uri) > 0);
163 : :
164 : 160 : splituri = g_strsplit ( uri, "://", 2 );
165 : 160 : if ( splituri[1] == NULL )
166 : : {
167 : : /* No scheme means simple file path.
168 : : Set path to copy of the input. */
169 : 62 : *path = g_strdup ( uri );
170 : 62 : g_strfreev ( splituri );
171 : 62 : return;
172 : : }
173 : :
174 : : /* At least a scheme was found, set it here */
175 : 98 : *scheme = g_strdup ( splituri[0] );
176 : :
177 : 98 : 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 : 56 : if (g_str_has_prefix (splituri[1], "/") &&
183 : 56 : ((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 : 28 : *path = gnc_resolve_file_path ( splituri[1] );
190 : 28 : g_strfreev ( splituri );
191 : 28 : 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 : 88 : gchar *gnc_uri_get_path (const gchar *uri)
277 : : {
278 : 88 : gchar *scheme = NULL;
279 : 88 : gchar *hostname = NULL;
280 : 88 : gint32 port = 0;
281 : 88 : gchar *username = NULL;
282 : 88 : gchar *password = NULL;
283 : 88 : gchar *path = NULL;
284 : :
285 : 88 : gnc_uri_get_components ( uri, &scheme, &hostname, &port,
286 : : &username, &password, &path );
287 : :
288 : 88 : g_free (scheme);
289 : 88 : g_free (hostname);
290 : 88 : g_free (username);
291 : 88 : g_free (password);
292 : :
293 : 88 : return path;
294 : : }
295 : :
296 : : /* Generates a normalized uri from the separate components */
297 : 36 : 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 : 36 : gchar *userpass = NULL, *portstr = NULL, *uri = NULL;
305 : :
306 : 36 : g_return_val_if_fail( path != 0, NULL );
307 : :
308 : 36 : 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 : 8 : if (scheme && (!gnc_uri_is_known_scheme (scheme)) )
319 : 6 : abs_path = g_strdup ( path );
320 : : else
321 : 2 : abs_path = gnc_resolve_file_path ( path );
322 : :
323 : 8 : if (!scheme)
324 : 2 : 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 : 8 : if (g_str_has_prefix (abs_path, "/") || g_str_has_prefix (abs_path, "\\"))
344 : 8 : 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 : 8 : g_free (uri_scheme);
349 : 8 : g_free (abs_path);
350 : :
351 : 8 : 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 : 18 : gchar *gnc_uri_normalize_uri (const gchar *uri, gboolean allow_password)
386 : : {
387 : 18 : gchar *scheme = NULL;
388 : 18 : gchar *hostname = NULL;
389 : 18 : gint32 port = 0;
390 : 18 : gchar *username = NULL;
391 : 18 : gchar *password = NULL;
392 : 18 : gchar *path = NULL;
393 : 18 : gchar *newuri = NULL;
394 : :
395 : 18 : gnc_uri_get_components ( uri, &scheme, &hostname, &port,
396 : : &username, &password, &path );
397 : 18 : if (allow_password)
398 : 7 : newuri = gnc_uri_create_uri ( scheme, hostname, port,
399 : : username, password, path);
400 : : else
401 : 11 : newuri = gnc_uri_create_uri ( scheme, hostname, port,
402 : : username, /* no password */ NULL, path);
403 : :
404 : 18 : g_free (scheme);
405 : 18 : g_free (hostname);
406 : 18 : g_free (username);
407 : 18 : g_free (password);
408 : 18 : g_free (path);
409 : :
410 : 18 : 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 : : }
|