xref: /onnv-gate/usr/src/cmd/ldap/common/fileurl.c (revision 8097:4d57974af082)
10Sstevel@tonic-gate /*
2*8097SSreedhar.Chalamalasetti@Sun.COM  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
30Sstevel@tonic-gate  * Use is subject to license terms.
40Sstevel@tonic-gate  */
50Sstevel@tonic-gate 
60Sstevel@tonic-gate /*
70Sstevel@tonic-gate  * The contents of this file are subject to the Netscape Public
80Sstevel@tonic-gate  * License Version 1.1 (the "License"); you may not use this file
90Sstevel@tonic-gate  * except in compliance with the License. You may obtain a copy of
100Sstevel@tonic-gate  * the License at http://www.mozilla.org/NPL/
110Sstevel@tonic-gate  *
120Sstevel@tonic-gate  * Software distributed under the License is distributed on an "AS
130Sstevel@tonic-gate  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
140Sstevel@tonic-gate  * implied. See the License for the specific language governing
150Sstevel@tonic-gate  * rights and limitations under the License.
160Sstevel@tonic-gate  *
170Sstevel@tonic-gate  * The Original Code is Mozilla Communicator client code, released
180Sstevel@tonic-gate  * March 31, 1998.
190Sstevel@tonic-gate  *
200Sstevel@tonic-gate  * The Initial Developer of the Original Code is Netscape
210Sstevel@tonic-gate  * Communications Corporation. Portions created by Netscape are
220Sstevel@tonic-gate  * Copyright (C) 1998-1999 Netscape Communications Corporation. All
230Sstevel@tonic-gate  * Rights Reserved.
240Sstevel@tonic-gate  *
250Sstevel@tonic-gate  * Contributor(s):
260Sstevel@tonic-gate  */
270Sstevel@tonic-gate 
280Sstevel@tonic-gate /*
290Sstevel@tonic-gate  *  LDAP tools fileurl.c -- functions for handling file URLs.
300Sstevel@tonic-gate  *  Used by ldapmodify.
310Sstevel@tonic-gate  */
320Sstevel@tonic-gate 
330Sstevel@tonic-gate #include "ldaptool.h"
340Sstevel@tonic-gate #include "fileurl.h"
350Sstevel@tonic-gate #include <ctype.h>	/* for isalpha() */
360Sstevel@tonic-gate #ifdef SOLARIS_LDAP_CMD
370Sstevel@tonic-gate #include <locale.h>
380Sstevel@tonic-gate #endif	/* SOLARIS_LDAP_CMD */
390Sstevel@tonic-gate 
400Sstevel@tonic-gate #ifndef SOLARIS_LDAP_CMD
410Sstevel@tonic-gate #define gettext(s) s
420Sstevel@tonic-gate #endif
430Sstevel@tonic-gate 
440Sstevel@tonic-gate static int str_starts_with( const char *s, char *prefix );
450Sstevel@tonic-gate static void hex_unescape( char *s );
460Sstevel@tonic-gate static int unhex( char c );
470Sstevel@tonic-gate static void strcpy_escaped_and_convert( char *s1, char *s2 );
480Sstevel@tonic-gate static int berval_from_file( const char *path, struct berval *bvp,
490Sstevel@tonic-gate 	int reporterrs );
500Sstevel@tonic-gate 
510Sstevel@tonic-gate /*
520Sstevel@tonic-gate  * Convert a file URL to a local path.
530Sstevel@tonic-gate  *
540Sstevel@tonic-gate  * If successful, LDAPTOOL_FILEURL_SUCCESS is returned and *localpathp is
550Sstevel@tonic-gate  * set point to an allocated string.  If not, an different LDAPTOOL_FILEURL_
560Sstevel@tonic-gate  * error code is returned.
570Sstevel@tonic-gate  *
580Sstevel@tonic-gate  * See RFCs 1738 and 2396 for a specification for file URLs... but
590Sstevel@tonic-gate  * Netscape Navigator seems to be a bit more lenient in what it will
600Sstevel@tonic-gate  * accept, especially on Windows).
610Sstevel@tonic-gate  *
620Sstevel@tonic-gate  * This function parses file URLs of these three forms:
630Sstevel@tonic-gate  *
640Sstevel@tonic-gate  *    file:///path
650Sstevel@tonic-gate  *    file:/path
660Sstevel@tonic-gate  *    file://localhost/path
670Sstevel@tonic-gate  *    file://host/path		(rejected with a ...NONLOCAL error)
680Sstevel@tonic-gate  *
690Sstevel@tonic-gate  * On Windows, we convert leading drive letters of the form C| to C:
700Sstevel@tonic-gate  * and if a drive letter is present we strip off the slash that precedes
710Sstevel@tonic-gate  * path.  Otherwise, the leading slash is returned.
720Sstevel@tonic-gate  *
730Sstevel@tonic-gate  */
740Sstevel@tonic-gate int
ldaptool_fileurl2path(const char * fileurl,char ** localpathp)750Sstevel@tonic-gate ldaptool_fileurl2path( const char *fileurl, char **localpathp )
760Sstevel@tonic-gate {
770Sstevel@tonic-gate     const char	*path;
780Sstevel@tonic-gate     char	*pathcopy;
790Sstevel@tonic-gate 
800Sstevel@tonic-gate     /*
810Sstevel@tonic-gate      * Make sure this is a file URL we can handle.
820Sstevel@tonic-gate      */
830Sstevel@tonic-gate     if ( !str_starts_with( fileurl, "file:" )) {
840Sstevel@tonic-gate 	return( LDAPTOOL_FILEURL_NOTAFILEURL );
850Sstevel@tonic-gate     }
860Sstevel@tonic-gate 
870Sstevel@tonic-gate     path = fileurl + 5;		/* skip past "file:" scheme prefix */
880Sstevel@tonic-gate 
890Sstevel@tonic-gate     if ( *path != '/' ) {
900Sstevel@tonic-gate 	return( LDAPTOOL_FILEURL_MISSINGPATH );
910Sstevel@tonic-gate     }
920Sstevel@tonic-gate 
930Sstevel@tonic-gate     ++path;			/* skip past '/' at end of "file:/" */
940Sstevel@tonic-gate 
950Sstevel@tonic-gate     if ( *path == '/' ) {
960Sstevel@tonic-gate 	++path;			/* remainder is now host/path or /path */
970Sstevel@tonic-gate 	if ( *path != '/' ) {
980Sstevel@tonic-gate 	    /*
990Sstevel@tonic-gate 	     * Make sure it is for the local host.
1000Sstevel@tonic-gate 	     */
1010Sstevel@tonic-gate 	    if ( str_starts_with( path, "localhost/" )) {
1020Sstevel@tonic-gate 		path += 9;
1030Sstevel@tonic-gate 	    } else {
1040Sstevel@tonic-gate 		return( LDAPTOOL_FILEURL_NONLOCAL );
1050Sstevel@tonic-gate 	    }
1060Sstevel@tonic-gate 	}
1070Sstevel@tonic-gate     } else {		/* URL is of the form file:/path */
1080Sstevel@tonic-gate 	--path;
1090Sstevel@tonic-gate     }
1100Sstevel@tonic-gate 
1110Sstevel@tonic-gate     /*
1120Sstevel@tonic-gate      * The remainder is now of the form /path.  On Windows, skip past the
1130Sstevel@tonic-gate      * leading slash if a drive letter is present.
1140Sstevel@tonic-gate      */
1150Sstevel@tonic-gate #ifdef _WINDOWS
1160Sstevel@tonic-gate     if ( isalpha( path[1] ) && ( path[2] == '|' || path[2] == ':' )) {
1170Sstevel@tonic-gate 	++path;
1180Sstevel@tonic-gate     }
1190Sstevel@tonic-gate #endif /* _WINDOWS */
1200Sstevel@tonic-gate 
1210Sstevel@tonic-gate     /*
1220Sstevel@tonic-gate      * Duplicate the path so we can safely alter it.
1230Sstevel@tonic-gate      * Unescape any %HH sequences.
1240Sstevel@tonic-gate      */
1250Sstevel@tonic-gate     if (( pathcopy = strdup( path )) == NULL ) {
1260Sstevel@tonic-gate 	return( LDAPTOOL_FILEURL_NOMEMORY );
1270Sstevel@tonic-gate     }
1280Sstevel@tonic-gate     hex_unescape( pathcopy );
1290Sstevel@tonic-gate 
1300Sstevel@tonic-gate #ifdef _WINDOWS
1310Sstevel@tonic-gate     /*
1320Sstevel@tonic-gate      * Convert forward slashes to backslashes for Windows.  Also,
1330Sstevel@tonic-gate      * if we see a drive letter / vertical bar combination (e.g., c|)
1340Sstevel@tonic-gate      * at the beginning of the path, replace the '|' with a ':'.
1350Sstevel@tonic-gate      */
1360Sstevel@tonic-gate     {
1370Sstevel@tonic-gate 	char	*p;
1380Sstevel@tonic-gate 
1390Sstevel@tonic-gate 	for ( p = pathcopy; *p != '\0'; ++p ) {
1400Sstevel@tonic-gate 	    if ( *p == '/' ) {
1410Sstevel@tonic-gate 		*p = '\\';
1420Sstevel@tonic-gate 	    }
1430Sstevel@tonic-gate 	}
1440Sstevel@tonic-gate     }
1450Sstevel@tonic-gate 
1460Sstevel@tonic-gate     if ( isalpha( pathcopy[0] ) && pathcopy[1] == '|' ) {
1470Sstevel@tonic-gate 	pathcopy[1] = ':';
1480Sstevel@tonic-gate     }
1490Sstevel@tonic-gate #endif /* _WINDOWS */
1500Sstevel@tonic-gate 
1510Sstevel@tonic-gate     *localpathp = pathcopy;
1520Sstevel@tonic-gate     return( LDAPTOOL_FILEURL_SUCCESS );
1530Sstevel@tonic-gate }
1540Sstevel@tonic-gate 
1550Sstevel@tonic-gate 
1560Sstevel@tonic-gate /*
1570Sstevel@tonic-gate  * Convert a local path to a file URL.
1580Sstevel@tonic-gate  *
1590Sstevel@tonic-gate  * If successful, LDAPTOOL_FILEURL_SUCCESS is returned and *urlp is
1600Sstevel@tonic-gate  * set point to an allocated string.  If not, an different LDAPTOOL_FILEURL_
1610Sstevel@tonic-gate  * error code is returned.  At present, the only possible error is
1620Sstevel@tonic-gate  * LDAPTOOL_FILEURL_NOMEMORY.
1630Sstevel@tonic-gate  *
1640Sstevel@tonic-gate  * This function produces file URLs of the form file:path.
1650Sstevel@tonic-gate  *
1660Sstevel@tonic-gate  * On Windows, we convert leading drive letters to C|.
1670Sstevel@tonic-gate  *
1680Sstevel@tonic-gate  */
1690Sstevel@tonic-gate int
ldaptool_path2fileurl(char * path,char ** urlp)1700Sstevel@tonic-gate ldaptool_path2fileurl( char *path, char **urlp )
1710Sstevel@tonic-gate {
1720Sstevel@tonic-gate     char	*p, *url, *prefix ="file:";
1730Sstevel@tonic-gate 
1740Sstevel@tonic-gate     if ( NULL == path ) {
1750Sstevel@tonic-gate 	path = "/";
1760Sstevel@tonic-gate     }
1770Sstevel@tonic-gate 
1780Sstevel@tonic-gate     /*
1790Sstevel@tonic-gate      * Allocate space for the URL, taking into account that path may
1800Sstevel@tonic-gate      * expand during the hex escaping process.
1810Sstevel@tonic-gate      */
1820Sstevel@tonic-gate     if (( url = malloc( strlen( prefix ) + 3 * strlen( path ) + 1 )) == NULL ) {
1830Sstevel@tonic-gate 	return( LDAPTOOL_FILEURL_NOMEMORY );
1840Sstevel@tonic-gate     }
1850Sstevel@tonic-gate 
1860Sstevel@tonic-gate     strcpy( url, prefix );
1870Sstevel@tonic-gate     p = url + strlen( prefix );
1880Sstevel@tonic-gate 
1890Sstevel@tonic-gate #ifdef _WINDOWS
1900Sstevel@tonic-gate     /*
1910Sstevel@tonic-gate      * On Windows, convert leading drive letters (e.g., C:) to the correct URL
1920Sstevel@tonic-gate      * syntax (e.g., C|).
1930Sstevel@tonic-gate      */
1940Sstevel@tonic-gate     if ( isalpha( path[0] ) && path[1] == ':' ) {
1950Sstevel@tonic-gate 	*p++ = path[0];
1960Sstevel@tonic-gate 	*p++ = '|';
1970Sstevel@tonic-gate 	path += 2;
1980Sstevel@tonic-gate 	*p = '\0';
1990Sstevel@tonic-gate     }
2000Sstevel@tonic-gate #endif /* _WINDOWS */
2010Sstevel@tonic-gate 
2020Sstevel@tonic-gate     /*
2030Sstevel@tonic-gate      * Append the path, encoding any URL-special characters using the %HH
2040Sstevel@tonic-gate      * convention.
2050Sstevel@tonic-gate      * On Windows, convert backwards slashes in the path to forward ones.
2060Sstevel@tonic-gate      */
2070Sstevel@tonic-gate     strcpy_escaped_and_convert( p, path );
2080Sstevel@tonic-gate 
2090Sstevel@tonic-gate     *urlp = url;
2100Sstevel@tonic-gate     return( LDAPTOOL_FILEURL_SUCCESS );
2110Sstevel@tonic-gate }
2120Sstevel@tonic-gate 
2130Sstevel@tonic-gate 
2140Sstevel@tonic-gate /*
2150Sstevel@tonic-gate  * Populate *bvp from "value" of length "vlen."
2160Sstevel@tonic-gate  *
2170Sstevel@tonic-gate  * If recognize_url_syntax is non-zero, :<fileurl is recognized.
2180Sstevel@tonic-gate  * If always_try_file is recognized and no file URL was found, an
2190Sstevel@tonic-gate  * attempt is made to stat and read the value as if it were the name
2200Sstevel@tonic-gate  * of a file.
2210Sstevel@tonic-gate  *
2220Sstevel@tonic-gate  * If reporterrs is non-zero, specific error messages are printed to
2230Sstevel@tonic-gate  * stderr.
2240Sstevel@tonic-gate  *
2250Sstevel@tonic-gate  * If successful, LDAPTOOL_FILEURL_SUCCESS is returned and bvp->bv_len
2260Sstevel@tonic-gate  * and bvp->bv_val are set (the latter is set to malloc'd memory).
2270Sstevel@tonic-gate  * Upon failure, a different LDAPTOOL_FILEURL_ error code is returned.
2280Sstevel@tonic-gate  */
2290Sstevel@tonic-gate int
ldaptool_berval_from_ldif_value(const char * value,int vlen,struct berval * bvp,int recognize_url_syntax,int always_try_file,int reporterrs)2300Sstevel@tonic-gate ldaptool_berval_from_ldif_value( const char *value, int vlen,
2310Sstevel@tonic-gate 	struct berval *bvp, int recognize_url_syntax, int always_try_file,
2320Sstevel@tonic-gate 	int reporterrs )
2330Sstevel@tonic-gate {
2340Sstevel@tonic-gate     int	rc = LDAPTOOL_FILEURL_SUCCESS;	/* optimistic */
2350Sstevel@tonic-gate     const char	*url = NULL;
2360Sstevel@tonic-gate     struct stat	fstats;
2370Sstevel@tonic-gate 
2380Sstevel@tonic-gate     /* recognize "attr :< url" syntax if LDIF version is >= 1 */
2390Sstevel@tonic-gate 
2400Sstevel@tonic-gate #ifdef notdef
2410Sstevel@tonic-gate     if ( ldaptool_verbose ) {
2420Sstevel@tonic-gate 	fprintf( stderr, gettext("%s: ldaptool_berval_from_ldif_value: value: %s\n"),
2430Sstevel@tonic-gate 	    ldaptool_progname, value);
2440Sstevel@tonic-gate     }
2450Sstevel@tonic-gate #endif
2460Sstevel@tonic-gate 
2470Sstevel@tonic-gate     if ( recognize_url_syntax && *value == '<' ) {
2480Sstevel@tonic-gate         for ( url = value + 1; isspace( *url ); ++url ) {
2490Sstevel@tonic-gate 	    ;	/* NULL */
2500Sstevel@tonic-gate 	}
2510Sstevel@tonic-gate 
2520Sstevel@tonic-gate 	if (strlen(url) > 7 && strncasecmp(url, "file://", 7) != 0) {
2530Sstevel@tonic-gate 	    /*
2540Sstevel@tonic-gate 	     * We only support file:// URLs for now.
2550Sstevel@tonic-gate 	     */
2560Sstevel@tonic-gate 	    url = NULL;
2570Sstevel@tonic-gate 	}
2580Sstevel@tonic-gate     }
2590Sstevel@tonic-gate 
2600Sstevel@tonic-gate     if ( NULL != url ) {
2610Sstevel@tonic-gate 	char		*path;
2620Sstevel@tonic-gate 
2630Sstevel@tonic-gate 	rc = ldaptool_fileurl2path( url, &path );
2640Sstevel@tonic-gate 	switch( rc ) {
2650Sstevel@tonic-gate 	case LDAPTOOL_FILEURL_NOTAFILEURL:
2660Sstevel@tonic-gate 	    if ( reporterrs ) fprintf( stderr, gettext("%s: unsupported URL \"%s\";"
2670Sstevel@tonic-gate 				       " use a file:// URL instead.\n"), ldaptool_progname, url );
2680Sstevel@tonic-gate 	    break;
2690Sstevel@tonic-gate 
2700Sstevel@tonic-gate 	case LDAPTOOL_FILEURL_MISSINGPATH:
2710Sstevel@tonic-gate 	    if ( reporterrs ) fprintf( stderr,
2720Sstevel@tonic-gate 				       gettext("%s: unable to process URL \"%s\" --"
2730Sstevel@tonic-gate 				       " missing path.\n"), ldaptool_progname, url );
2740Sstevel@tonic-gate 	    break;
2750Sstevel@tonic-gate 
2760Sstevel@tonic-gate 	case LDAPTOOL_FILEURL_NONLOCAL:
2770Sstevel@tonic-gate 	    if ( reporterrs ) fprintf( stderr,
2780Sstevel@tonic-gate 				       gettext("%s: unable to process URL \"%s\" -- only"
2790Sstevel@tonic-gate 				       " local file:// URLs are supported.\n"),
2800Sstevel@tonic-gate 				       ldaptool_progname, url );
2810Sstevel@tonic-gate 	    break;
2820Sstevel@tonic-gate 
2830Sstevel@tonic-gate 	case LDAPTOOL_FILEURL_NOMEMORY:
2840Sstevel@tonic-gate 	    if ( reporterrs ) perror( "ldaptool_fileurl2path" );
2850Sstevel@tonic-gate 	    break;
2860Sstevel@tonic-gate 
2870Sstevel@tonic-gate 	case LDAPTOOL_FILEURL_SUCCESS:
2880Sstevel@tonic-gate 	    if ( stat( path, &fstats ) != 0 ) {
2890Sstevel@tonic-gate 		if ( reporterrs ) perror( path );
290871Scasper 	    } else if (S_ISDIR(fstats.st_mode)) {
2910Sstevel@tonic-gate 		if ( reporterrs ) fprintf( stderr,
2920Sstevel@tonic-gate 					   gettext("%s: %s is a directory, not a file\n"),
2930Sstevel@tonic-gate 					   ldaptool_progname, path );
2940Sstevel@tonic-gate 		rc = LDAPTOOL_FILEURL_FILEIOERROR;
2950Sstevel@tonic-gate 	    } else {
2960Sstevel@tonic-gate 		rc = berval_from_file( path, bvp, reporterrs );
2970Sstevel@tonic-gate 	    }
2980Sstevel@tonic-gate 	    free( path );
2990Sstevel@tonic-gate 	    break;
3000Sstevel@tonic-gate 
3010Sstevel@tonic-gate 	default:
3020Sstevel@tonic-gate 	    if ( reporterrs ) fprintf( stderr,
3030Sstevel@tonic-gate 				       gettext("%s: unable to process URL \"%s\""
3040Sstevel@tonic-gate 				       " -- unknown error\n"), ldaptool_progname, url );
3050Sstevel@tonic-gate 	}
3060Sstevel@tonic-gate     } else if ( always_try_file && (stat( value, &fstats ) == 0) &&
307871Scasper 		!S_ISDIR(fstats.st_mode)) {	/* get value from file */
3080Sstevel@tonic-gate 	rc = berval_from_file( value, bvp, reporterrs );
3090Sstevel@tonic-gate     } else {
3100Sstevel@tonic-gate 	bvp->bv_len = vlen;
3110Sstevel@tonic-gate 	if (( bvp->bv_val = (char *)malloc( vlen + 1 )) == NULL ) {
3120Sstevel@tonic-gate 	    if ( reporterrs ) perror( "malloc" );
3130Sstevel@tonic-gate 	    rc = LDAPTOOL_FILEURL_NOMEMORY;
3140Sstevel@tonic-gate 	} else {
3150Sstevel@tonic-gate 	    SAFEMEMCPY( bvp->bv_val, value, vlen );
3160Sstevel@tonic-gate 	    bvp->bv_val[ vlen ] = '\0';
3170Sstevel@tonic-gate 	}
3180Sstevel@tonic-gate     }
3190Sstevel@tonic-gate 
3200Sstevel@tonic-gate     return( rc );
3210Sstevel@tonic-gate }
3220Sstevel@tonic-gate 
3230Sstevel@tonic-gate 
3240Sstevel@tonic-gate /*
3250Sstevel@tonic-gate  * Map an LDAPTOOL_FILEURL_ error code to an LDAP error code (crude).
3260Sstevel@tonic-gate  */
3270Sstevel@tonic-gate int
ldaptool_fileurlerr2ldaperr(int lderr)3280Sstevel@tonic-gate ldaptool_fileurlerr2ldaperr( int lderr )
3290Sstevel@tonic-gate {
3300Sstevel@tonic-gate     int		rc;
3310Sstevel@tonic-gate 
3320Sstevel@tonic-gate     switch( lderr ) {
3330Sstevel@tonic-gate     case LDAPTOOL_FILEURL_SUCCESS:
3340Sstevel@tonic-gate 	rc = LDAP_SUCCESS;
3350Sstevel@tonic-gate 	break;
3360Sstevel@tonic-gate     case LDAPTOOL_FILEURL_NOMEMORY:
3370Sstevel@tonic-gate 	rc = LDAP_NO_MEMORY;
3380Sstevel@tonic-gate 	break;
3390Sstevel@tonic-gate     default:
3400Sstevel@tonic-gate 	rc = LDAP_PARAM_ERROR;
3410Sstevel@tonic-gate     }
3420Sstevel@tonic-gate 
3430Sstevel@tonic-gate     return( rc );
3440Sstevel@tonic-gate }
3450Sstevel@tonic-gate 
3460Sstevel@tonic-gate 
3470Sstevel@tonic-gate /*
3480Sstevel@tonic-gate  * Populate *bvp with the contents of the file named by "path".
3490Sstevel@tonic-gate  *
3500Sstevel@tonic-gate  * If reporterrs is non-zero, specific error messages are printed to
3510Sstevel@tonic-gate  * stderr.
3520Sstevel@tonic-gate  *
3530Sstevel@tonic-gate  * If successful, LDAPTOOL_FILEURL_SUCCESS is returned and bvp->bv_len
3540Sstevel@tonic-gate  * and bvp->bv_val are set (the latter is set to malloc'd memory).
3550Sstevel@tonic-gate  * Upon failure, a different LDAPTOOL_FILEURL_ error code is returned.
3560Sstevel@tonic-gate  */
3570Sstevel@tonic-gate 
3580Sstevel@tonic-gate static int
berval_from_file(const char * path,struct berval * bvp,int reporterrs)3590Sstevel@tonic-gate berval_from_file( const char *path, struct berval *bvp, int reporterrs )
3600Sstevel@tonic-gate {
3610Sstevel@tonic-gate     FILE	*fp;
3620Sstevel@tonic-gate     long	rlen;
3630Sstevel@tonic-gate     int		eof;
3640Sstevel@tonic-gate #if defined( XP_WIN32 )
3650Sstevel@tonic-gate     char	mode[20] = "r+b";
3660Sstevel@tonic-gate #else
3670Sstevel@tonic-gate     char	mode[20] = "r";
3680Sstevel@tonic-gate #endif
3690Sstevel@tonic-gate 
3700Sstevel@tonic-gate #ifdef SOLARIS_LDAP_CMD
3710Sstevel@tonic-gate     if (( fp = fopen( path, mode )) == NULL ) {
3720Sstevel@tonic-gate #else
3730Sstevel@tonic-gate     if (( fp = ldaptool_open_file( path, mode )) == NULL ) {
3740Sstevel@tonic-gate #endif	/* SOLARIS_LDAP_CMD */
3750Sstevel@tonic-gate 	if ( reporterrs ) perror( path );
3760Sstevel@tonic-gate 	return( LDAPTOOL_FILEURL_FILEIOERROR );
3770Sstevel@tonic-gate     }
3780Sstevel@tonic-gate 
3790Sstevel@tonic-gate     if ( fseek( fp, 0L, SEEK_END ) != 0 ) {
3800Sstevel@tonic-gate 	if ( reporterrs ) perror( path );
3810Sstevel@tonic-gate 	fclose( fp );
3820Sstevel@tonic-gate 	return( LDAPTOOL_FILEURL_FILEIOERROR );
3830Sstevel@tonic-gate     }
3840Sstevel@tonic-gate 
3850Sstevel@tonic-gate     bvp->bv_len = ftell( fp );
3860Sstevel@tonic-gate 
3870Sstevel@tonic-gate     if (( bvp->bv_val = (char *)malloc( bvp->bv_len + 1 )) == NULL ) {
3880Sstevel@tonic-gate 	if ( reporterrs ) perror( "malloc" );
3890Sstevel@tonic-gate 	fclose( fp );
3900Sstevel@tonic-gate 	return( LDAPTOOL_FILEURL_NOMEMORY );
3910Sstevel@tonic-gate     }
3920Sstevel@tonic-gate 
3930Sstevel@tonic-gate     if ( fseek( fp, 0L, SEEK_SET ) != 0 ) {
3940Sstevel@tonic-gate 	if ( reporterrs ) perror( path );
3950Sstevel@tonic-gate 	fclose( fp );
3960Sstevel@tonic-gate 	return( LDAPTOOL_FILEURL_FILEIOERROR );
3970Sstevel@tonic-gate     }
3980Sstevel@tonic-gate 
3990Sstevel@tonic-gate     rlen = fread( bvp->bv_val, 1, bvp->bv_len, fp );
4000Sstevel@tonic-gate     eof = feof( fp );
4010Sstevel@tonic-gate     fclose( fp );
4020Sstevel@tonic-gate 
4030Sstevel@tonic-gate     if ( rlen != (long)bvp->bv_len ) {
4040Sstevel@tonic-gate 	if ( reporterrs ) perror( path );
4050Sstevel@tonic-gate 	free( bvp->bv_val );
4060Sstevel@tonic-gate 	return( LDAPTOOL_FILEURL_FILEIOERROR );
4070Sstevel@tonic-gate     }
4080Sstevel@tonic-gate 
4090Sstevel@tonic-gate     bvp->bv_val[ bvp->bv_len ] = '\0';
4100Sstevel@tonic-gate     return( LDAPTOOL_FILEURL_SUCCESS );
4110Sstevel@tonic-gate }
4120Sstevel@tonic-gate 
4130Sstevel@tonic-gate 
4140Sstevel@tonic-gate /*
4150Sstevel@tonic-gate  * Return a non-zero value if the string s begins with prefix and zero if not.
4160Sstevel@tonic-gate  */
4170Sstevel@tonic-gate static int
4180Sstevel@tonic-gate str_starts_with( const char *s, char *prefix )
4190Sstevel@tonic-gate {
4200Sstevel@tonic-gate     size_t	prefix_len;
4210Sstevel@tonic-gate 
4220Sstevel@tonic-gate     if ( s == NULL || prefix == NULL ) {
4230Sstevel@tonic-gate 	return( 0 );
4240Sstevel@tonic-gate     }
4250Sstevel@tonic-gate 
4260Sstevel@tonic-gate     prefix_len = strlen( prefix );
4270Sstevel@tonic-gate     if ( strlen( s ) < prefix_len ) {
4280Sstevel@tonic-gate 	return( 0 );
4290Sstevel@tonic-gate     }
4300Sstevel@tonic-gate 
4310Sstevel@tonic-gate     return( strncmp( s, prefix, prefix_len ) == 0 );
4320Sstevel@tonic-gate }
4330Sstevel@tonic-gate 
4340Sstevel@tonic-gate 
4350Sstevel@tonic-gate /*
4360Sstevel@tonic-gate  * Remove URL hex escapes from s... done in place.  The basic concept for
4370Sstevel@tonic-gate  * this routine is borrowed from the WWW library HTUnEscape() routine.
4380Sstevel@tonic-gate  *
4390Sstevel@tonic-gate  * A similar function called nsldapi_hex_unescape can be found in
4400Sstevel@tonic-gate  * ../../libraries/libldap/unescape.c
4410Sstevel@tonic-gate  */
4420Sstevel@tonic-gate static void
4430Sstevel@tonic-gate hex_unescape( char *s )
4440Sstevel@tonic-gate {
4450Sstevel@tonic-gate 	char	*p;
4460Sstevel@tonic-gate 
4470Sstevel@tonic-gate 	for ( p = s; *s != '\0'; ++s ) {
4480Sstevel@tonic-gate 		if ( *s == '%' ) {
4490Sstevel@tonic-gate 			if ( *++s != '\0' ) {
4500Sstevel@tonic-gate 				*p = unhex( *s ) << 4;
4510Sstevel@tonic-gate 			}
4520Sstevel@tonic-gate 			if ( *++s != '\0' ) {
4530Sstevel@tonic-gate 				*p++ += unhex( *s );
4540Sstevel@tonic-gate 			}
4550Sstevel@tonic-gate 		} else {
4560Sstevel@tonic-gate 			*p++ = *s;
4570Sstevel@tonic-gate 		}
4580Sstevel@tonic-gate 	}
4590Sstevel@tonic-gate 
4600Sstevel@tonic-gate 	*p = '\0';
4610Sstevel@tonic-gate }
4620Sstevel@tonic-gate 
4630Sstevel@tonic-gate 
4640Sstevel@tonic-gate /*
4650Sstevel@tonic-gate  * Return the integer equivalent of one hex digit (in c).
4660Sstevel@tonic-gate  *
4670Sstevel@tonic-gate  * A similar function can be found in ../../libraries/libldap/unescape.c
4680Sstevel@tonic-gate  */
4690Sstevel@tonic-gate static int
4700Sstevel@tonic-gate unhex( char c )
4710Sstevel@tonic-gate {
4720Sstevel@tonic-gate 	return( c >= '0' && c <= '9' ? c - '0'
4730Sstevel@tonic-gate 	    : c >= 'A' && c <= 'F' ? c - 'A' + 10
4740Sstevel@tonic-gate 	    : c - 'a' + 10 );
4750Sstevel@tonic-gate }
4760Sstevel@tonic-gate 
4770Sstevel@tonic-gate 
4780Sstevel@tonic-gate #define HREF_CHAR_ACCEPTABLE( c )	(( c >= '-' && c <= '9' ) ||	\
4790Sstevel@tonic-gate 					 ( c >= '@' && c <= 'Z' ) ||	\
4800Sstevel@tonic-gate 					 ( c == '_' ) ||		\
4810Sstevel@tonic-gate 					 ( c >= 'a' && c <= 'z' ))
4820Sstevel@tonic-gate 
4830Sstevel@tonic-gate /*
4840Sstevel@tonic-gate  * Like strcat(), except if any URL-special characters are found in s2
4850Sstevel@tonic-gate  * they are escaped using the %HH convention and backslash characters are
4860Sstevel@tonic-gate  * converted to forward slashes on Windows.
4870Sstevel@tonic-gate  *
4880Sstevel@tonic-gate  * Maximum space needed in s1 is 3 * strlen( s2 ) + 1.
4890Sstevel@tonic-gate  *
4900Sstevel@tonic-gate  * A similar function that does not convert the slashes called
4910Sstevel@tonic-gate  * strcat_escaped() can be found in ../../libraries/libldap/tmplout.c
4920Sstevel@tonic-gate  */
4930Sstevel@tonic-gate static void
4940Sstevel@tonic-gate strcpy_escaped_and_convert( char *s1, char *s2 )
4950Sstevel@tonic-gate {
4960Sstevel@tonic-gate     char	*p, *q;
4970Sstevel@tonic-gate     char	*hexdig = "0123456789ABCDEF";
4980Sstevel@tonic-gate 
4990Sstevel@tonic-gate     p = s1 + strlen( s1 );
5000Sstevel@tonic-gate     for ( q = s2; *q != '\0'; ++q ) {
5010Sstevel@tonic-gate #ifdef _WINDOWS
5020Sstevel@tonic-gate 	if ( *q == '\\' ) {
5030Sstevel@tonic-gate                 *p++ = '/';
5040Sstevel@tonic-gate 	} else
5050Sstevel@tonic-gate #endif /* _WINDOWS */
5060Sstevel@tonic-gate 
5070Sstevel@tonic-gate 	if ( HREF_CHAR_ACCEPTABLE( *q )) {
5080Sstevel@tonic-gate 	    *p++ = *q;
5090Sstevel@tonic-gate 	} else {
5100Sstevel@tonic-gate 	    *p++ = '%';
5110Sstevel@tonic-gate 	    *p++ = hexdig[ 0x0F & ((*(unsigned char*)q) >> 4) ];
5120Sstevel@tonic-gate 	    *p++ = hexdig[ 0x0F & *q ];
5130Sstevel@tonic-gate 	}
5140Sstevel@tonic-gate     }
5150Sstevel@tonic-gate 
5160Sstevel@tonic-gate     *p = '\0';
5170Sstevel@tonic-gate }
518