2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
7 * The contents of this file are subject to the Netscape Public
8 * License Version 1.1 (the "License"); you may not use this file
9 * except in compliance with the License. You may obtain a copy of
10 * the License at http://www.mozilla.org/NPL/
12 * Software distributed under the License is distributed on an "AS
13 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * rights and limitations under the License.
17 * The Original Code is Mozilla Communicator client code, released
20 * The Initial Developer of the Original Code is Netscape
21 * Communications Corporation. Portions created by Netscape are
22 * Copyright (C) 1998-1999 Netscape Communications Corporation. All
29 * LDAP tools fileurl.c -- functions for handling file URLs.
35 #include <ctype.h> /* for isalpha() */
36 #ifdef SOLARIS_LDAP_CMD
38 #endif /* SOLARIS_LDAP_CMD */
40 #ifndef SOLARIS_LDAP_CMD
44 static int str_starts_with( const char *s
, char *prefix
);
45 static void hex_unescape( char *s
);
46 static int unhex( char c
);
47 static void strcpy_escaped_and_convert( char *s1
, char *s2
);
48 static int berval_from_file( const char *path
, struct berval
*bvp
,
52 * Convert a file URL to a local path.
54 * If successful, LDAPTOOL_FILEURL_SUCCESS is returned and *localpathp is
55 * set point to an allocated string. If not, an different LDAPTOOL_FILEURL_
56 * error code is returned.
58 * See RFCs 1738 and 2396 for a specification for file URLs... but
59 * Netscape Navigator seems to be a bit more lenient in what it will
60 * accept, especially on Windows).
62 * This function parses file URLs of these three forms:
66 * file://localhost/path
67 * file://host/path (rejected with a ...NONLOCAL error)
69 * On Windows, we convert leading drive letters of the form C| to C:
70 * and if a drive letter is present we strip off the slash that precedes
71 * path. Otherwise, the leading slash is returned.
75 ldaptool_fileurl2path( const char *fileurl
, char **localpathp
)
81 * Make sure this is a file URL we can handle.
83 if ( !str_starts_with( fileurl
, "file:" )) {
84 return( LDAPTOOL_FILEURL_NOTAFILEURL
);
87 path
= fileurl
+ 5; /* skip past "file:" scheme prefix */
90 return( LDAPTOOL_FILEURL_MISSINGPATH
);
93 ++path
; /* skip past '/' at end of "file:/" */
96 ++path
; /* remainder is now host/path or /path */
99 * Make sure it is for the local host.
101 if ( str_starts_with( path
, "localhost/" )) {
104 return( LDAPTOOL_FILEURL_NONLOCAL
);
107 } else { /* URL is of the form file:/path */
112 * The remainder is now of the form /path. On Windows, skip past the
113 * leading slash if a drive letter is present.
116 if ( isalpha( path
[1] ) && ( path
[2] == '|' || path
[2] == ':' )) {
119 #endif /* _WINDOWS */
122 * Duplicate the path so we can safely alter it.
123 * Unescape any %HH sequences.
125 if (( pathcopy
= strdup( path
)) == NULL
) {
126 return( LDAPTOOL_FILEURL_NOMEMORY
);
128 hex_unescape( pathcopy
);
132 * Convert forward slashes to backslashes for Windows. Also,
133 * if we see a drive letter / vertical bar combination (e.g., c|)
134 * at the beginning of the path, replace the '|' with a ':'.
139 for ( p
= pathcopy
; *p
!= '\0'; ++p
) {
146 if ( isalpha( pathcopy
[0] ) && pathcopy
[1] == '|' ) {
149 #endif /* _WINDOWS */
151 *localpathp
= pathcopy
;
152 return( LDAPTOOL_FILEURL_SUCCESS
);
157 * Convert a local path to a file URL.
159 * If successful, LDAPTOOL_FILEURL_SUCCESS is returned and *urlp is
160 * set point to an allocated string. If not, an different LDAPTOOL_FILEURL_
161 * error code is returned. At present, the only possible error is
162 * LDAPTOOL_FILEURL_NOMEMORY.
164 * This function produces file URLs of the form file:path.
166 * On Windows, we convert leading drive letters to C|.
170 ldaptool_path2fileurl( char *path
, char **urlp
)
172 char *p
, *url
, *prefix
="file:";
174 if ( NULL
== path
) {
179 * Allocate space for the URL, taking into account that path may
180 * expand during the hex escaping process.
182 if (( url
= malloc( strlen( prefix
) + 3 * strlen( path
) + 1 )) == NULL
) {
183 return( LDAPTOOL_FILEURL_NOMEMORY
);
186 strcpy( url
, prefix
);
187 p
= url
+ strlen( prefix
);
191 * On Windows, convert leading drive letters (e.g., C:) to the correct URL
194 if ( isalpha( path
[0] ) && path
[1] == ':' ) {
200 #endif /* _WINDOWS */
203 * Append the path, encoding any URL-special characters using the %HH
205 * On Windows, convert backwards slashes in the path to forward ones.
207 strcpy_escaped_and_convert( p
, path
);
210 return( LDAPTOOL_FILEURL_SUCCESS
);
215 * Populate *bvp from "value" of length "vlen."
217 * If recognize_url_syntax is non-zero, :<fileurl is recognized.
218 * If always_try_file is recognized and no file URL was found, an
219 * attempt is made to stat and read the value as if it were the name
222 * If reporterrs is non-zero, specific error messages are printed to
225 * If successful, LDAPTOOL_FILEURL_SUCCESS is returned and bvp->bv_len
226 * and bvp->bv_val are set (the latter is set to malloc'd memory).
227 * Upon failure, a different LDAPTOOL_FILEURL_ error code is returned.
230 ldaptool_berval_from_ldif_value( const char *value
, int vlen
,
231 struct berval
*bvp
, int recognize_url_syntax
, int always_try_file
,
234 int rc
= LDAPTOOL_FILEURL_SUCCESS
; /* optimistic */
235 const char *url
= NULL
;
238 /* recognize "attr :< url" syntax if LDIF version is >= 1 */
241 if ( ldaptool_verbose
) {
242 fprintf( stderr
, gettext("%s: ldaptool_berval_from_ldif_value: value: %s\n"),
243 ldaptool_progname
, value
);
247 if ( recognize_url_syntax
&& *value
== '<' ) {
248 for ( url
= value
+ 1; isspace( *url
); ++url
) {
252 if (strlen(url
) > 7 && strncasecmp(url
, "file://", 7) != 0) {
254 * We only support file:// URLs for now.
263 rc
= ldaptool_fileurl2path( url
, &path
);
265 case LDAPTOOL_FILEURL_NOTAFILEURL
:
266 if ( reporterrs
) fprintf( stderr
, gettext("%s: unsupported URL \"%s\";"
267 " use a file:// URL instead.\n"), ldaptool_progname
, url
);
270 case LDAPTOOL_FILEURL_MISSINGPATH
:
271 if ( reporterrs
) fprintf( stderr
,
272 gettext("%s: unable to process URL \"%s\" --"
273 " missing path.\n"), ldaptool_progname
, url
);
276 case LDAPTOOL_FILEURL_NONLOCAL
:
277 if ( reporterrs
) fprintf( stderr
,
278 gettext("%s: unable to process URL \"%s\" -- only"
279 " local file:// URLs are supported.\n"),
280 ldaptool_progname
, url
);
283 case LDAPTOOL_FILEURL_NOMEMORY
:
284 if ( reporterrs
) perror( "ldaptool_fileurl2path" );
287 case LDAPTOOL_FILEURL_SUCCESS
:
288 if ( stat( path
, &fstats
) != 0 ) {
289 if ( reporterrs
) perror( path
);
290 } else if (S_ISDIR(fstats
.st_mode
)) {
291 if ( reporterrs
) fprintf( stderr
,
292 gettext("%s: %s is a directory, not a file\n"),
293 ldaptool_progname
, path
);
294 rc
= LDAPTOOL_FILEURL_FILEIOERROR
;
296 rc
= berval_from_file( path
, bvp
, reporterrs
);
302 if ( reporterrs
) fprintf( stderr
,
303 gettext("%s: unable to process URL \"%s\""
304 " -- unknown error\n"), ldaptool_progname
, url
);
306 } else if ( always_try_file
&& (stat( value
, &fstats
) == 0) &&
307 !S_ISDIR(fstats
.st_mode
)) { /* get value from file */
308 rc
= berval_from_file( value
, bvp
, reporterrs
);
311 if (( bvp
->bv_val
= (char *)malloc( vlen
+ 1 )) == NULL
) {
312 if ( reporterrs
) perror( "malloc" );
313 rc
= LDAPTOOL_FILEURL_NOMEMORY
;
315 SAFEMEMCPY( bvp
->bv_val
, value
, vlen
);
316 bvp
->bv_val
[ vlen
] = '\0';
325 * Map an LDAPTOOL_FILEURL_ error code to an LDAP error code (crude).
328 ldaptool_fileurlerr2ldaperr( int lderr
)
333 case LDAPTOOL_FILEURL_SUCCESS
:
336 case LDAPTOOL_FILEURL_NOMEMORY
:
340 rc
= LDAP_PARAM_ERROR
;
348 * Populate *bvp with the contents of the file named by "path".
350 * If reporterrs is non-zero, specific error messages are printed to
353 * If successful, LDAPTOOL_FILEURL_SUCCESS is returned and bvp->bv_len
354 * and bvp->bv_val are set (the latter is set to malloc'd memory).
355 * Upon failure, a different LDAPTOOL_FILEURL_ error code is returned.
359 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
);
403 if ( rlen
!= (long)bvp
->bv_len
) {
404 if ( reporterrs
) perror( path
);
406 return( LDAPTOOL_FILEURL_FILEIOERROR
);
409 bvp
->bv_val
[ bvp
->bv_len
] = '\0';
410 return( LDAPTOOL_FILEURL_SUCCESS
);
415 * Return a non-zero value if the string s begins with prefix and zero if not.
418 str_starts_with( const char *s
, char *prefix
)
422 if ( s
== NULL
|| prefix
== NULL
) {
426 prefix_len
= strlen( prefix
);
427 if ( strlen( s
) < prefix_len
) {
431 return( strncmp( s
, prefix
, prefix_len
) == 0 );
436 * Remove URL hex escapes from s... done in place. The basic concept for
437 * this routine is borrowed from the WWW library HTUnEscape() routine.
439 * A similar function called nsldapi_hex_unescape can be found in
440 * ../../libraries/libldap/unescape.c
443 hex_unescape( char *s
)
447 for ( p
= s
; *s
!= '\0'; ++s
) {
449 if ( *++s
!= '\0' ) {
450 *p
= unhex( *s
) << 4;
452 if ( *++s
!= '\0' ) {
465 * Return the integer equivalent of one hex digit (in c).
467 * A similar function can be found in ../../libraries/libldap/unescape.c
472 return( c
>= '0' && c
<= '9' ? c
- '0'
473 : c
>= 'A' && c
<= 'F' ? c
- 'A' + 10
478 #define HREF_CHAR_ACCEPTABLE( c ) (( c >= '-' && c <= '9' ) || \
479 ( c >= '@' && c <= 'Z' ) || \
481 ( c >= 'a' && c <= 'z' ))
484 * Like strcat(), except if any URL-special characters are found in s2
485 * they are escaped using the %HH convention and backslash characters are
486 * converted to forward slashes on Windows.
488 * Maximum space needed in s1 is 3 * strlen( s2 ) + 1.
490 * A similar function that does not convert the slashes called
491 * strcat_escaped() can be found in ../../libraries/libldap/tmplout.c
494 strcpy_escaped_and_convert( char *s1
, char *s2
)
497 char *hexdig
= "0123456789ABCDEF";
499 p
= s1
+ strlen( s1
);
500 for ( q
= s2
; *q
!= '\0'; ++q
) {
505 #endif /* _WINDOWS */
507 if ( HREF_CHAR_ACCEPTABLE( *q
)) {
511 *p
++ = hexdig
[ 0x0F & ((*(unsigned char*)q
) >> 4) ];
512 *p
++ = hexdig
[ 0x0F & *q
];