2 * Copyright 2017 Gary Mills
3 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
4 * Use is subject to license terms.
8 * The contents of this file are subject to the Netscape Public
9 * License Version 1.1 (the "License"); you may not use this file
10 * except in compliance with the License. You may obtain a copy of
11 * the License at http://www.mozilla.org/NPL/
13 * Software distributed under the License is distributed on an "AS
14 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
15 * implied. See the License for the specific language governing
16 * rights and limitations under the License.
18 * The Original Code is Mozilla Communicator client code, released
21 * The Initial Developer of the Original Code is Netscape
22 * Communications Corporation. Portions created by Netscape are
23 * Copyright (C) 1998-1999 Netscape Communications Corporation. All
30 * LDAP tools fileurl.c -- functions for handling file URLs.
36 #include <ctype.h> /* for isalpha() */
37 #ifdef SOLARIS_LDAP_CMD
39 #endif /* SOLARIS_LDAP_CMD */
41 #ifndef SOLARIS_LDAP_CMD
45 static int str_starts_with( const char *s
, char *prefix
);
46 static void hex_unescape( char *s
);
47 static int unhex( char c
);
48 static void strcpy_escaped_and_convert( char *s1
, char *s2
);
49 static int berval_from_file( const char *path
, struct berval
*bvp
,
53 * Convert a file URL to a local path.
55 * If successful, LDAPTOOL_FILEURL_SUCCESS is returned and *localpathp is
56 * set point to an allocated string. If not, an different LDAPTOOL_FILEURL_
57 * error code is returned.
59 * See RFCs 1738 and 2396 for a specification for file URLs... but
60 * Netscape Navigator seems to be a bit more lenient in what it will
61 * accept, especially on Windows).
63 * This function parses file URLs of these three forms:
67 * file://localhost/path
68 * file://host/path (rejected with a ...NONLOCAL error)
70 * On Windows, we convert leading drive letters of the form C| to C:
71 * and if a drive letter is present we strip off the slash that precedes
72 * path. Otherwise, the leading slash is returned.
76 ldaptool_fileurl2path( const char *fileurl
, char **localpathp
)
82 * Make sure this is a file URL we can handle.
84 if ( !str_starts_with( fileurl
, "file:" )) {
85 return( LDAPTOOL_FILEURL_NOTAFILEURL
);
88 path
= fileurl
+ 5; /* skip past "file:" scheme prefix */
91 return( LDAPTOOL_FILEURL_MISSINGPATH
);
94 ++path
; /* skip past '/' at end of "file:/" */
97 ++path
; /* remainder is now host/path or /path */
100 * Make sure it is for the local host.
102 if ( str_starts_with( path
, "localhost/" )) {
105 return( LDAPTOOL_FILEURL_NONLOCAL
);
108 } else { /* URL is of the form file:/path */
113 * The remainder is now of the form /path. On Windows, skip past the
114 * leading slash if a drive letter is present.
117 if ( isalpha( path
[1] ) && ( path
[2] == '|' || path
[2] == ':' )) {
120 #endif /* _WINDOWS */
123 * Duplicate the path so we can safely alter it.
124 * Unescape any %HH sequences.
126 if (( pathcopy
= strdup( path
)) == NULL
) {
127 return( LDAPTOOL_FILEURL_NOMEMORY
);
129 hex_unescape( pathcopy
);
133 * Convert forward slashes to backslashes for Windows. Also,
134 * if we see a drive letter / vertical bar combination (e.g., c|)
135 * at the beginning of the path, replace the '|' with a ':'.
140 for ( p
= pathcopy
; *p
!= '\0'; ++p
) {
147 if ( isalpha( pathcopy
[0] ) && pathcopy
[1] == '|' ) {
150 #endif /* _WINDOWS */
152 *localpathp
= pathcopy
;
153 return( LDAPTOOL_FILEURL_SUCCESS
);
158 * Convert a local path to a file URL.
160 * If successful, LDAPTOOL_FILEURL_SUCCESS is returned and *urlp is
161 * set point to an allocated string. If not, an different LDAPTOOL_FILEURL_
162 * error code is returned. At present, the only possible error is
163 * LDAPTOOL_FILEURL_NOMEMORY.
165 * This function produces file URLs of the form file:path.
167 * On Windows, we convert leading drive letters to C|.
171 ldaptool_path2fileurl( char *path
, char **urlp
)
173 char *p
, *url
, *prefix
="file:";
175 if ( NULL
== path
) {
180 * Allocate space for the URL, taking into account that path may
181 * expand during the hex escaping process.
183 if (( url
= malloc( strlen( prefix
) + 3 * strlen( path
) + 1 )) == NULL
) {
184 return( LDAPTOOL_FILEURL_NOMEMORY
);
187 strcpy( url
, prefix
);
188 p
= url
+ strlen( prefix
);
192 * On Windows, convert leading drive letters (e.g., C:) to the correct URL
195 if ( isalpha( path
[0] ) && path
[1] == ':' ) {
201 #endif /* _WINDOWS */
204 * Append the path, encoding any URL-special characters using the %HH
206 * On Windows, convert backwards slashes in the path to forward ones.
208 strcpy_escaped_and_convert( p
, path
);
211 return( LDAPTOOL_FILEURL_SUCCESS
);
216 * Populate *bvp from "value" of length "vlen."
218 * If recognize_url_syntax is non-zero, :<fileurl is recognized.
219 * If always_try_file is recognized and no file URL was found, an
220 * attempt is made to stat and read the value as if it were the name
223 * If reporterrs is non-zero, specific error messages are printed to
226 * If successful, LDAPTOOL_FILEURL_SUCCESS is returned and bvp->bv_len
227 * and bvp->bv_val are set (the latter is set to malloc'd memory).
228 * Upon failure, a different LDAPTOOL_FILEURL_ error code is returned.
231 ldaptool_berval_from_ldif_value( const char *value
, int vlen
,
232 struct berval
*bvp
, int recognize_url_syntax
, int always_try_file
,
235 int rc
= LDAPTOOL_FILEURL_SUCCESS
; /* optimistic */
236 const char *url
= NULL
;
239 /* recognize "attr :< url" syntax if LDIF version is >= 1 */
242 if ( ldaptool_verbose
) {
243 fprintf( stderr
, gettext("%s: ldaptool_berval_from_ldif_value: value: %s\n"),
244 ldaptool_progname
, value
);
248 if ( recognize_url_syntax
&& *value
== '<' ) {
249 for ( url
= value
+ 1; isspace( *url
); ++url
) {
253 if (strlen(url
) > 7 && strncasecmp(url
, "file://", 7) != 0) {
255 * We only support file:// URLs for now.
264 rc
= ldaptool_fileurl2path( url
, &path
);
266 case LDAPTOOL_FILEURL_NOTAFILEURL
:
267 if ( reporterrs
) fprintf( stderr
, gettext("%s: unsupported URL \"%s\";"
268 " use a file:// URL instead.\n"), ldaptool_progname
, url
);
271 case LDAPTOOL_FILEURL_MISSINGPATH
:
272 if ( reporterrs
) fprintf( stderr
,
273 gettext("%s: unable to process URL \"%s\" --"
274 " missing path.\n"), ldaptool_progname
, url
);
277 case LDAPTOOL_FILEURL_NONLOCAL
:
278 if ( reporterrs
) fprintf( stderr
,
279 gettext("%s: unable to process URL \"%s\" -- only"
280 " local file:// URLs are supported.\n"),
281 ldaptool_progname
, url
);
284 case LDAPTOOL_FILEURL_NOMEMORY
:
285 if ( reporterrs
) perror( "ldaptool_fileurl2path" );
288 case LDAPTOOL_FILEURL_SUCCESS
:
289 if ( stat( path
, &fstats
) != 0 ) {
290 if ( reporterrs
) perror( path
);
291 } else if (S_ISDIR(fstats
.st_mode
)) {
292 if ( reporterrs
) fprintf( stderr
,
293 gettext("%s: %s is a directory, not a file\n"),
294 ldaptool_progname
, path
);
295 rc
= LDAPTOOL_FILEURL_FILEIOERROR
;
297 rc
= berval_from_file( path
, bvp
, reporterrs
);
303 if ( reporterrs
) fprintf( stderr
,
304 gettext("%s: unable to process URL \"%s\""
305 " -- unknown error\n"), ldaptool_progname
, url
);
307 } else if ( always_try_file
&& (stat( value
, &fstats
) == 0) &&
308 !S_ISDIR(fstats
.st_mode
)) { /* get value from file */
309 rc
= berval_from_file( value
, bvp
, reporterrs
);
312 if (( bvp
->bv_val
= (char *)malloc( vlen
+ 1 )) == NULL
) {
313 if ( reporterrs
) perror( "malloc" );
314 rc
= LDAPTOOL_FILEURL_NOMEMORY
;
316 SAFEMEMCPY( bvp
->bv_val
, value
, vlen
);
317 bvp
->bv_val
[ vlen
] = '\0';
326 * Map an LDAPTOOL_FILEURL_ error code to an LDAP error code (crude).
329 ldaptool_fileurlerr2ldaperr( int lderr
)
334 case LDAPTOOL_FILEURL_SUCCESS
:
337 case LDAPTOOL_FILEURL_NOMEMORY
:
341 rc
= LDAP_PARAM_ERROR
;
349 * Populate *bvp with the contents of the file named by "path".
351 * If reporterrs is non-zero, specific error messages are printed to
354 * If successful, LDAPTOOL_FILEURL_SUCCESS is returned and bvp->bv_len
355 * and bvp->bv_val are set (the latter is set to malloc'd memory).
356 * Upon failure, a different LDAPTOOL_FILEURL_ error code is returned.
360 berval_from_file( const char *path
, struct berval
*bvp
, int reporterrs
)
364 #if defined( XP_WIN32 )
365 char mode
[20] = "r+b";
370 #ifdef SOLARIS_LDAP_CMD
371 if (( fp
= fopen( path
, mode
)) == NULL
) {
373 if (( fp
= ldaptool_open_file( path
, mode
)) == NULL
) {
374 #endif /* SOLARIS_LDAP_CMD */
375 if ( reporterrs
) perror( path
);
376 return( LDAPTOOL_FILEURL_FILEIOERROR
);
379 if ( fseek( fp
, 0L, SEEK_END
) != 0 ) {
380 if ( reporterrs
) perror( path
);
382 return( LDAPTOOL_FILEURL_FILEIOERROR
);
385 bvp
->bv_len
= ftell( fp
);
387 if (( bvp
->bv_val
= (char *)malloc( bvp
->bv_len
+ 1 )) == NULL
) {
388 if ( reporterrs
) perror( "malloc" );
390 return( LDAPTOOL_FILEURL_NOMEMORY
);
393 if ( fseek( fp
, 0L, SEEK_SET
) != 0 ) {
394 if ( reporterrs
) perror( path
);
396 return( LDAPTOOL_FILEURL_FILEIOERROR
);
399 rlen
= fread( bvp
->bv_val
, 1, bvp
->bv_len
, fp
);
402 if ( rlen
!= (long)bvp
->bv_len
) {
403 if ( reporterrs
) perror( path
);
405 return( LDAPTOOL_FILEURL_FILEIOERROR
);
408 bvp
->bv_val
[ bvp
->bv_len
] = '\0';
409 return( LDAPTOOL_FILEURL_SUCCESS
);
414 * Return a non-zero value if the string s begins with prefix and zero if not.
417 str_starts_with( const char *s
, char *prefix
)
421 if ( s
== NULL
|| prefix
== NULL
) {
425 prefix_len
= strlen( prefix
);
426 if ( strlen( s
) < prefix_len
) {
430 return( strncmp( s
, prefix
, prefix_len
) == 0 );
435 * Remove URL hex escapes from s... done in place. The basic concept for
436 * this routine is borrowed from the WWW library HTUnEscape() routine.
438 * A similar function called nsldapi_hex_unescape can be found in
439 * ../../libraries/libldap/unescape.c
442 hex_unescape( char *s
)
446 for ( p
= s
; *s
!= '\0'; ++s
) {
448 if ( *++s
!= '\0' ) {
449 *p
= unhex( *s
) << 4;
451 if ( *++s
!= '\0' ) {
464 * Return the integer equivalent of one hex digit (in c).
466 * A similar function can be found in ../../libraries/libldap/unescape.c
471 return( c
>= '0' && c
<= '9' ? c
- '0'
472 : c
>= 'A' && c
<= 'F' ? c
- 'A' + 10
477 #define HREF_CHAR_ACCEPTABLE( c ) (( c >= '-' && c <= '9' ) || \
478 ( c >= '@' && c <= 'Z' ) || \
480 ( c >= 'a' && c <= 'z' ))
483 * Like strcat(), except if any URL-special characters are found in s2
484 * they are escaped using the %HH convention and backslash characters are
485 * converted to forward slashes on Windows.
487 * Maximum space needed in s1 is 3 * strlen( s2 ) + 1.
489 * A similar function that does not convert the slashes called
490 * strcat_escaped() can be found in ../../libraries/libldap/tmplout.c
493 strcpy_escaped_and_convert( char *s1
, char *s2
)
496 char *hexdig
= "0123456789ABCDEF";
498 p
= s1
+ strlen( s1
);
499 for ( q
= s2
; *q
!= '\0'; ++q
) {
504 #endif /* _WINDOWS */
506 if ( HREF_CHAR_ACCEPTABLE( *q
)) {
510 *p
++ = hexdig
[ 0x0F & ((*(unsigned char*)q
) >> 4) ];
511 *p
++ = hexdig
[ 0x0F & *q
];