1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
3*0Sstevel@tonic-gate  * Use is subject to license terms.
4*0Sstevel@tonic-gate  */
5*0Sstevel@tonic-gate 
6*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
7*0Sstevel@tonic-gate 
8*0Sstevel@tonic-gate /*
9*0Sstevel@tonic-gate  * The contents of this file are subject to the Netscape Public
10*0Sstevel@tonic-gate  * License Version 1.1 (the "License"); you may not use this file
11*0Sstevel@tonic-gate  * except in compliance with the License. You may obtain a copy of
12*0Sstevel@tonic-gate  * the License at http://www.mozilla.org/NPL/
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * Software distributed under the License is distributed on an "AS
15*0Sstevel@tonic-gate  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
16*0Sstevel@tonic-gate  * implied. See the License for the specific language governing
17*0Sstevel@tonic-gate  * rights and limitations under the License.
18*0Sstevel@tonic-gate  *
19*0Sstevel@tonic-gate  * The Original Code is Mozilla Communicator client code, released
20*0Sstevel@tonic-gate  * March 31, 1998.
21*0Sstevel@tonic-gate  *
22*0Sstevel@tonic-gate  * The Initial Developer of the Original Code is Netscape
23*0Sstevel@tonic-gate  * Communications Corporation. Portions created by Netscape are
24*0Sstevel@tonic-gate  * Copyright (C) 1998-1999 Netscape Communications Corporation. All
25*0Sstevel@tonic-gate  * Rights Reserved.
26*0Sstevel@tonic-gate  *
27*0Sstevel@tonic-gate  * Contributor(s):
28*0Sstevel@tonic-gate  */
29*0Sstevel@tonic-gate 
30*0Sstevel@tonic-gate /*
31*0Sstevel@tonic-gate  *  LDAP tools fileurl.c -- functions for handling file URLs.
32*0Sstevel@tonic-gate  *  Used by ldapmodify.
33*0Sstevel@tonic-gate  */
34*0Sstevel@tonic-gate 
35*0Sstevel@tonic-gate #include "ldaptool.h"
36*0Sstevel@tonic-gate #include "fileurl.h"
37*0Sstevel@tonic-gate #include <ctype.h>	/* for isalpha() */
38*0Sstevel@tonic-gate #ifdef SOLARIS_LDAP_CMD
39*0Sstevel@tonic-gate #include <locale.h>
40*0Sstevel@tonic-gate #endif	/* SOLARIS_LDAP_CMD */
41*0Sstevel@tonic-gate 
42*0Sstevel@tonic-gate #ifndef SOLARIS_LDAP_CMD
43*0Sstevel@tonic-gate #define gettext(s) s
44*0Sstevel@tonic-gate #endif
45*0Sstevel@tonic-gate 
46*0Sstevel@tonic-gate static int str_starts_with( const char *s, char *prefix );
47*0Sstevel@tonic-gate static void hex_unescape( char *s );
48*0Sstevel@tonic-gate static int unhex( char c );
49*0Sstevel@tonic-gate static void strcpy_escaped_and_convert( char *s1, char *s2 );
50*0Sstevel@tonic-gate static int berval_from_file( const char *path, struct berval *bvp,
51*0Sstevel@tonic-gate 	int reporterrs );
52*0Sstevel@tonic-gate 
53*0Sstevel@tonic-gate /*
54*0Sstevel@tonic-gate  * Convert a file URL to a local path.
55*0Sstevel@tonic-gate  *
56*0Sstevel@tonic-gate  * If successful, LDAPTOOL_FILEURL_SUCCESS is returned and *localpathp is
57*0Sstevel@tonic-gate  * set point to an allocated string.  If not, an different LDAPTOOL_FILEURL_
58*0Sstevel@tonic-gate  * error code is returned.
59*0Sstevel@tonic-gate  *
60*0Sstevel@tonic-gate  * See RFCs 1738 and 2396 for a specification for file URLs... but
61*0Sstevel@tonic-gate  * Netscape Navigator seems to be a bit more lenient in what it will
62*0Sstevel@tonic-gate  * accept, especially on Windows).
63*0Sstevel@tonic-gate  *
64*0Sstevel@tonic-gate  * This function parses file URLs of these three forms:
65*0Sstevel@tonic-gate  *
66*0Sstevel@tonic-gate  *    file:///path
67*0Sstevel@tonic-gate  *    file:/path
68*0Sstevel@tonic-gate  *    file://localhost/path
69*0Sstevel@tonic-gate  *    file://host/path		(rejected with a ...NONLOCAL error)
70*0Sstevel@tonic-gate  *
71*0Sstevel@tonic-gate  * On Windows, we convert leading drive letters of the form C| to C:
72*0Sstevel@tonic-gate  * and if a drive letter is present we strip off the slash that precedes
73*0Sstevel@tonic-gate  * path.  Otherwise, the leading slash is returned.
74*0Sstevel@tonic-gate  *
75*0Sstevel@tonic-gate  */
76*0Sstevel@tonic-gate int
77*0Sstevel@tonic-gate ldaptool_fileurl2path( const char *fileurl, char **localpathp )
78*0Sstevel@tonic-gate {
79*0Sstevel@tonic-gate     const char	*path;
80*0Sstevel@tonic-gate     char	*pathcopy;
81*0Sstevel@tonic-gate 
82*0Sstevel@tonic-gate     /*
83*0Sstevel@tonic-gate      * Make sure this is a file URL we can handle.
84*0Sstevel@tonic-gate      */
85*0Sstevel@tonic-gate     if ( !str_starts_with( fileurl, "file:" )) {
86*0Sstevel@tonic-gate 	return( LDAPTOOL_FILEURL_NOTAFILEURL );
87*0Sstevel@tonic-gate     }
88*0Sstevel@tonic-gate 
89*0Sstevel@tonic-gate     path = fileurl + 5;		/* skip past "file:" scheme prefix */
90*0Sstevel@tonic-gate 
91*0Sstevel@tonic-gate     if ( *path != '/' ) {
92*0Sstevel@tonic-gate 	return( LDAPTOOL_FILEURL_MISSINGPATH );
93*0Sstevel@tonic-gate     }
94*0Sstevel@tonic-gate 
95*0Sstevel@tonic-gate     ++path;			/* skip past '/' at end of "file:/" */
96*0Sstevel@tonic-gate 
97*0Sstevel@tonic-gate     if ( *path == '/' ) {
98*0Sstevel@tonic-gate 	++path;			/* remainder is now host/path or /path */
99*0Sstevel@tonic-gate 	if ( *path != '/' ) {
100*0Sstevel@tonic-gate 	    /*
101*0Sstevel@tonic-gate 	     * Make sure it is for the local host.
102*0Sstevel@tonic-gate 	     */
103*0Sstevel@tonic-gate 	    if ( str_starts_with( path, "localhost/" )) {
104*0Sstevel@tonic-gate 		path += 9;
105*0Sstevel@tonic-gate 	    } else {
106*0Sstevel@tonic-gate 		return( LDAPTOOL_FILEURL_NONLOCAL );
107*0Sstevel@tonic-gate 	    }
108*0Sstevel@tonic-gate 	}
109*0Sstevel@tonic-gate     } else {		/* URL is of the form file:/path */
110*0Sstevel@tonic-gate 	--path;
111*0Sstevel@tonic-gate     }
112*0Sstevel@tonic-gate 
113*0Sstevel@tonic-gate     /*
114*0Sstevel@tonic-gate      * The remainder is now of the form /path.  On Windows, skip past the
115*0Sstevel@tonic-gate      * leading slash if a drive letter is present.
116*0Sstevel@tonic-gate      */
117*0Sstevel@tonic-gate #ifdef _WINDOWS
118*0Sstevel@tonic-gate     if ( isalpha( path[1] ) && ( path[2] == '|' || path[2] == ':' )) {
119*0Sstevel@tonic-gate 	++path;
120*0Sstevel@tonic-gate     }
121*0Sstevel@tonic-gate #endif /* _WINDOWS */
122*0Sstevel@tonic-gate 
123*0Sstevel@tonic-gate     /*
124*0Sstevel@tonic-gate      * Duplicate the path so we can safely alter it.
125*0Sstevel@tonic-gate      * Unescape any %HH sequences.
126*0Sstevel@tonic-gate      */
127*0Sstevel@tonic-gate     if (( pathcopy = strdup( path )) == NULL ) {
128*0Sstevel@tonic-gate 	return( LDAPTOOL_FILEURL_NOMEMORY );
129*0Sstevel@tonic-gate     }
130*0Sstevel@tonic-gate     hex_unescape( pathcopy );
131*0Sstevel@tonic-gate 
132*0Sstevel@tonic-gate #ifdef _WINDOWS
133*0Sstevel@tonic-gate     /*
134*0Sstevel@tonic-gate      * Convert forward slashes to backslashes for Windows.  Also,
135*0Sstevel@tonic-gate      * if we see a drive letter / vertical bar combination (e.g., c|)
136*0Sstevel@tonic-gate      * at the beginning of the path, replace the '|' with a ':'.
137*0Sstevel@tonic-gate      */
138*0Sstevel@tonic-gate     {
139*0Sstevel@tonic-gate 	char	*p;
140*0Sstevel@tonic-gate 
141*0Sstevel@tonic-gate 	for ( p = pathcopy; *p != '\0'; ++p ) {
142*0Sstevel@tonic-gate 	    if ( *p == '/' ) {
143*0Sstevel@tonic-gate 		*p = '\\';
144*0Sstevel@tonic-gate 	    }
145*0Sstevel@tonic-gate 	}
146*0Sstevel@tonic-gate     }
147*0Sstevel@tonic-gate 
148*0Sstevel@tonic-gate     if ( isalpha( pathcopy[0] ) && pathcopy[1] == '|' ) {
149*0Sstevel@tonic-gate 	pathcopy[1] = ':';
150*0Sstevel@tonic-gate     }
151*0Sstevel@tonic-gate #endif /* _WINDOWS */
152*0Sstevel@tonic-gate 
153*0Sstevel@tonic-gate     *localpathp = pathcopy;
154*0Sstevel@tonic-gate     return( LDAPTOOL_FILEURL_SUCCESS );
155*0Sstevel@tonic-gate }
156*0Sstevel@tonic-gate 
157*0Sstevel@tonic-gate 
158*0Sstevel@tonic-gate /*
159*0Sstevel@tonic-gate  * Convert a local path to a file URL.
160*0Sstevel@tonic-gate  *
161*0Sstevel@tonic-gate  * If successful, LDAPTOOL_FILEURL_SUCCESS is returned and *urlp is
162*0Sstevel@tonic-gate  * set point to an allocated string.  If not, an different LDAPTOOL_FILEURL_
163*0Sstevel@tonic-gate  * error code is returned.  At present, the only possible error is
164*0Sstevel@tonic-gate  * LDAPTOOL_FILEURL_NOMEMORY.
165*0Sstevel@tonic-gate  *
166*0Sstevel@tonic-gate  * This function produces file URLs of the form file:path.
167*0Sstevel@tonic-gate  *
168*0Sstevel@tonic-gate  * On Windows, we convert leading drive letters to C|.
169*0Sstevel@tonic-gate  *
170*0Sstevel@tonic-gate  */
171*0Sstevel@tonic-gate int
172*0Sstevel@tonic-gate ldaptool_path2fileurl( char *path, char **urlp )
173*0Sstevel@tonic-gate {
174*0Sstevel@tonic-gate     char	*p, *url, *prefix ="file:";
175*0Sstevel@tonic-gate 
176*0Sstevel@tonic-gate     if ( NULL == path ) {
177*0Sstevel@tonic-gate 	path = "/";
178*0Sstevel@tonic-gate     }
179*0Sstevel@tonic-gate 
180*0Sstevel@tonic-gate     /*
181*0Sstevel@tonic-gate      * Allocate space for the URL, taking into account that path may
182*0Sstevel@tonic-gate      * expand during the hex escaping process.
183*0Sstevel@tonic-gate      */
184*0Sstevel@tonic-gate     if (( url = malloc( strlen( prefix ) + 3 * strlen( path ) + 1 )) == NULL ) {
185*0Sstevel@tonic-gate 	return( LDAPTOOL_FILEURL_NOMEMORY );
186*0Sstevel@tonic-gate     }
187*0Sstevel@tonic-gate 
188*0Sstevel@tonic-gate     strcpy( url, prefix );
189*0Sstevel@tonic-gate     p = url + strlen( prefix );
190*0Sstevel@tonic-gate 
191*0Sstevel@tonic-gate #ifdef _WINDOWS
192*0Sstevel@tonic-gate     /*
193*0Sstevel@tonic-gate      * On Windows, convert leading drive letters (e.g., C:) to the correct URL
194*0Sstevel@tonic-gate      * syntax (e.g., C|).
195*0Sstevel@tonic-gate      */
196*0Sstevel@tonic-gate     if ( isalpha( path[0] ) && path[1] == ':' ) {
197*0Sstevel@tonic-gate 	*p++ = path[0];
198*0Sstevel@tonic-gate 	*p++ = '|';
199*0Sstevel@tonic-gate 	path += 2;
200*0Sstevel@tonic-gate 	*p = '\0';
201*0Sstevel@tonic-gate     }
202*0Sstevel@tonic-gate #endif /* _WINDOWS */
203*0Sstevel@tonic-gate 
204*0Sstevel@tonic-gate     /*
205*0Sstevel@tonic-gate      * Append the path, encoding any URL-special characters using the %HH
206*0Sstevel@tonic-gate      * convention.
207*0Sstevel@tonic-gate      * On Windows, convert backwards slashes in the path to forward ones.
208*0Sstevel@tonic-gate      */
209*0Sstevel@tonic-gate     strcpy_escaped_and_convert( p, path );
210*0Sstevel@tonic-gate 
211*0Sstevel@tonic-gate     *urlp = url;
212*0Sstevel@tonic-gate     return( LDAPTOOL_FILEURL_SUCCESS );
213*0Sstevel@tonic-gate }
214*0Sstevel@tonic-gate 
215*0Sstevel@tonic-gate 
216*0Sstevel@tonic-gate /*
217*0Sstevel@tonic-gate  * Populate *bvp from "value" of length "vlen."
218*0Sstevel@tonic-gate  *
219*0Sstevel@tonic-gate  * If recognize_url_syntax is non-zero, :<fileurl is recognized.
220*0Sstevel@tonic-gate  * If always_try_file is recognized and no file URL was found, an
221*0Sstevel@tonic-gate  * attempt is made to stat and read the value as if it were the name
222*0Sstevel@tonic-gate  * of a file.
223*0Sstevel@tonic-gate  *
224*0Sstevel@tonic-gate  * If reporterrs is non-zero, specific error messages are printed to
225*0Sstevel@tonic-gate  * stderr.
226*0Sstevel@tonic-gate  *
227*0Sstevel@tonic-gate  * If successful, LDAPTOOL_FILEURL_SUCCESS is returned and bvp->bv_len
228*0Sstevel@tonic-gate  * and bvp->bv_val are set (the latter is set to malloc'd memory).
229*0Sstevel@tonic-gate  * Upon failure, a different LDAPTOOL_FILEURL_ error code is returned.
230*0Sstevel@tonic-gate  */
231*0Sstevel@tonic-gate int
232*0Sstevel@tonic-gate ldaptool_berval_from_ldif_value( const char *value, int vlen,
233*0Sstevel@tonic-gate 	struct berval *bvp, int recognize_url_syntax, int always_try_file,
234*0Sstevel@tonic-gate 	int reporterrs )
235*0Sstevel@tonic-gate {
236*0Sstevel@tonic-gate     int	rc = LDAPTOOL_FILEURL_SUCCESS;	/* optimistic */
237*0Sstevel@tonic-gate     const char	*url = NULL;
238*0Sstevel@tonic-gate     struct stat	fstats;
239*0Sstevel@tonic-gate 
240*0Sstevel@tonic-gate     /* recognize "attr :< url" syntax if LDIF version is >= 1 */
241*0Sstevel@tonic-gate 
242*0Sstevel@tonic-gate #ifdef notdef
243*0Sstevel@tonic-gate     if ( ldaptool_verbose ) {
244*0Sstevel@tonic-gate 	fprintf( stderr, gettext("%s: ldaptool_berval_from_ldif_value: value: %s\n"),
245*0Sstevel@tonic-gate 	    ldaptool_progname, value);
246*0Sstevel@tonic-gate     }
247*0Sstevel@tonic-gate #endif
248*0Sstevel@tonic-gate 
249*0Sstevel@tonic-gate     if ( recognize_url_syntax && *value == '<' ) {
250*0Sstevel@tonic-gate         for ( url = value + 1; isspace( *url ); ++url ) {
251*0Sstevel@tonic-gate 	    ;	/* NULL */
252*0Sstevel@tonic-gate 	}
253*0Sstevel@tonic-gate 
254*0Sstevel@tonic-gate 	if (strlen(url) > 7 && strncasecmp(url, "file://", 7) != 0) {
255*0Sstevel@tonic-gate 	    /*
256*0Sstevel@tonic-gate 	     * We only support file:// URLs for now.
257*0Sstevel@tonic-gate 	     */
258*0Sstevel@tonic-gate 	    url = NULL;
259*0Sstevel@tonic-gate 	}
260*0Sstevel@tonic-gate     }
261*0Sstevel@tonic-gate 
262*0Sstevel@tonic-gate     if ( NULL != url ) {
263*0Sstevel@tonic-gate 	char		*path;
264*0Sstevel@tonic-gate 
265*0Sstevel@tonic-gate 	rc = ldaptool_fileurl2path( url, &path );
266*0Sstevel@tonic-gate 	switch( rc ) {
267*0Sstevel@tonic-gate 	case LDAPTOOL_FILEURL_NOTAFILEURL:
268*0Sstevel@tonic-gate 	    if ( reporterrs ) fprintf( stderr, gettext("%s: unsupported URL \"%s\";"
269*0Sstevel@tonic-gate 				       " use a file:// URL instead.\n"), ldaptool_progname, url );
270*0Sstevel@tonic-gate 	    break;
271*0Sstevel@tonic-gate 
272*0Sstevel@tonic-gate 	case LDAPTOOL_FILEURL_MISSINGPATH:
273*0Sstevel@tonic-gate 	    if ( reporterrs ) fprintf( stderr,
274*0Sstevel@tonic-gate 				       gettext("%s: unable to process URL \"%s\" --"
275*0Sstevel@tonic-gate 				       " missing path.\n"), ldaptool_progname, url );
276*0Sstevel@tonic-gate 	    break;
277*0Sstevel@tonic-gate 
278*0Sstevel@tonic-gate 	case LDAPTOOL_FILEURL_NONLOCAL:
279*0Sstevel@tonic-gate 	    if ( reporterrs ) fprintf( stderr,
280*0Sstevel@tonic-gate 				       gettext("%s: unable to process URL \"%s\" -- only"
281*0Sstevel@tonic-gate 				       " local file:// URLs are supported.\n"),
282*0Sstevel@tonic-gate 				       ldaptool_progname, url );
283*0Sstevel@tonic-gate 	    break;
284*0Sstevel@tonic-gate 
285*0Sstevel@tonic-gate 	case LDAPTOOL_FILEURL_NOMEMORY:
286*0Sstevel@tonic-gate 	    if ( reporterrs ) perror( "ldaptool_fileurl2path" );
287*0Sstevel@tonic-gate 	    break;
288*0Sstevel@tonic-gate 
289*0Sstevel@tonic-gate 	case LDAPTOOL_FILEURL_SUCCESS:
290*0Sstevel@tonic-gate 	    if ( stat( path, &fstats ) != 0 ) {
291*0Sstevel@tonic-gate 		if ( reporterrs ) perror( path );
292*0Sstevel@tonic-gate 	    } else if ( fstats.st_mode & S_IFDIR ) {
293*0Sstevel@tonic-gate 		if ( reporterrs ) fprintf( stderr,
294*0Sstevel@tonic-gate 					   gettext("%s: %s is a directory, not a file\n"),
295*0Sstevel@tonic-gate 					   ldaptool_progname, path );
296*0Sstevel@tonic-gate 		rc = LDAPTOOL_FILEURL_FILEIOERROR;
297*0Sstevel@tonic-gate 	    } else {
298*0Sstevel@tonic-gate 		rc = berval_from_file( path, bvp, reporterrs );
299*0Sstevel@tonic-gate 	    }
300*0Sstevel@tonic-gate 	    free( path );
301*0Sstevel@tonic-gate 	    break;
302*0Sstevel@tonic-gate 
303*0Sstevel@tonic-gate 	default:
304*0Sstevel@tonic-gate 	    if ( reporterrs ) fprintf( stderr,
305*0Sstevel@tonic-gate 				       gettext("%s: unable to process URL \"%s\""
306*0Sstevel@tonic-gate 				       " -- unknown error\n"), ldaptool_progname, url );
307*0Sstevel@tonic-gate 	}
308*0Sstevel@tonic-gate     } else if ( always_try_file && (stat( value, &fstats ) == 0) &&
309*0Sstevel@tonic-gate 		!(fstats.st_mode & S_IFDIR)) {	/* get value from file */
310*0Sstevel@tonic-gate 	rc = berval_from_file( value, bvp, reporterrs );
311*0Sstevel@tonic-gate     } else {
312*0Sstevel@tonic-gate 	bvp->bv_len = vlen;
313*0Sstevel@tonic-gate 	if (( bvp->bv_val = (char *)malloc( vlen + 1 )) == NULL ) {
314*0Sstevel@tonic-gate 	    if ( reporterrs ) perror( "malloc" );
315*0Sstevel@tonic-gate 	    rc = LDAPTOOL_FILEURL_NOMEMORY;
316*0Sstevel@tonic-gate 	} else {
317*0Sstevel@tonic-gate 	    SAFEMEMCPY( bvp->bv_val, value, vlen );
318*0Sstevel@tonic-gate 	    bvp->bv_val[ vlen ] = '\0';
319*0Sstevel@tonic-gate 	}
320*0Sstevel@tonic-gate     }
321*0Sstevel@tonic-gate 
322*0Sstevel@tonic-gate     return( rc );
323*0Sstevel@tonic-gate }
324*0Sstevel@tonic-gate 
325*0Sstevel@tonic-gate 
326*0Sstevel@tonic-gate /*
327*0Sstevel@tonic-gate  * Map an LDAPTOOL_FILEURL_ error code to an LDAP error code (crude).
328*0Sstevel@tonic-gate  */
329*0Sstevel@tonic-gate int
330*0Sstevel@tonic-gate ldaptool_fileurlerr2ldaperr( int lderr )
331*0Sstevel@tonic-gate {
332*0Sstevel@tonic-gate     int		rc;
333*0Sstevel@tonic-gate 
334*0Sstevel@tonic-gate     switch( lderr ) {
335*0Sstevel@tonic-gate     case LDAPTOOL_FILEURL_SUCCESS:
336*0Sstevel@tonic-gate 	rc = LDAP_SUCCESS;
337*0Sstevel@tonic-gate 	break;
338*0Sstevel@tonic-gate     case LDAPTOOL_FILEURL_NOMEMORY:
339*0Sstevel@tonic-gate 	rc = LDAP_NO_MEMORY;
340*0Sstevel@tonic-gate 	break;
341*0Sstevel@tonic-gate     default:
342*0Sstevel@tonic-gate 	rc = LDAP_PARAM_ERROR;
343*0Sstevel@tonic-gate     }
344*0Sstevel@tonic-gate 
345*0Sstevel@tonic-gate     return( rc );
346*0Sstevel@tonic-gate }
347*0Sstevel@tonic-gate 
348*0Sstevel@tonic-gate 
349*0Sstevel@tonic-gate /*
350*0Sstevel@tonic-gate  * Populate *bvp with the contents of the file named by "path".
351*0Sstevel@tonic-gate  *
352*0Sstevel@tonic-gate  * If reporterrs is non-zero, specific error messages are printed to
353*0Sstevel@tonic-gate  * stderr.
354*0Sstevel@tonic-gate  *
355*0Sstevel@tonic-gate  * If successful, LDAPTOOL_FILEURL_SUCCESS is returned and bvp->bv_len
356*0Sstevel@tonic-gate  * and bvp->bv_val are set (the latter is set to malloc'd memory).
357*0Sstevel@tonic-gate  * Upon failure, a different LDAPTOOL_FILEURL_ error code is returned.
358*0Sstevel@tonic-gate  */
359*0Sstevel@tonic-gate 
360*0Sstevel@tonic-gate static int
361*0Sstevel@tonic-gate berval_from_file( const char *path, struct berval *bvp, int reporterrs )
362*0Sstevel@tonic-gate {
363*0Sstevel@tonic-gate     FILE	*fp;
364*0Sstevel@tonic-gate     long	rlen;
365*0Sstevel@tonic-gate     int		eof;
366*0Sstevel@tonic-gate #if defined( XP_WIN32 )
367*0Sstevel@tonic-gate     char	mode[20] = "r+b";
368*0Sstevel@tonic-gate #else
369*0Sstevel@tonic-gate     char	mode[20] = "r";
370*0Sstevel@tonic-gate #endif
371*0Sstevel@tonic-gate 
372*0Sstevel@tonic-gate #ifdef SOLARIS_LDAP_CMD
373*0Sstevel@tonic-gate     if (( fp = fopen( path, mode )) == NULL ) {
374*0Sstevel@tonic-gate #else
375*0Sstevel@tonic-gate     if (( fp = ldaptool_open_file( path, mode )) == NULL ) {
376*0Sstevel@tonic-gate #endif	/* SOLARIS_LDAP_CMD */
377*0Sstevel@tonic-gate 	if ( reporterrs ) perror( path );
378*0Sstevel@tonic-gate 	return( LDAPTOOL_FILEURL_FILEIOERROR );
379*0Sstevel@tonic-gate     }
380*0Sstevel@tonic-gate 
381*0Sstevel@tonic-gate     if ( fseek( fp, 0L, SEEK_END ) != 0 ) {
382*0Sstevel@tonic-gate 	if ( reporterrs ) perror( path );
383*0Sstevel@tonic-gate 	fclose( fp );
384*0Sstevel@tonic-gate 	return( LDAPTOOL_FILEURL_FILEIOERROR );
385*0Sstevel@tonic-gate     }
386*0Sstevel@tonic-gate 
387*0Sstevel@tonic-gate     bvp->bv_len = ftell( fp );
388*0Sstevel@tonic-gate 
389*0Sstevel@tonic-gate     if (( bvp->bv_val = (char *)malloc( bvp->bv_len + 1 )) == NULL ) {
390*0Sstevel@tonic-gate 	if ( reporterrs ) perror( "malloc" );
391*0Sstevel@tonic-gate 	fclose( fp );
392*0Sstevel@tonic-gate 	return( LDAPTOOL_FILEURL_NOMEMORY );
393*0Sstevel@tonic-gate     }
394*0Sstevel@tonic-gate 
395*0Sstevel@tonic-gate     if ( fseek( fp, 0L, SEEK_SET ) != 0 ) {
396*0Sstevel@tonic-gate 	if ( reporterrs ) perror( path );
397*0Sstevel@tonic-gate 	fclose( fp );
398*0Sstevel@tonic-gate 	return( LDAPTOOL_FILEURL_FILEIOERROR );
399*0Sstevel@tonic-gate     }
400*0Sstevel@tonic-gate 
401*0Sstevel@tonic-gate     rlen = fread( bvp->bv_val, 1, bvp->bv_len, fp );
402*0Sstevel@tonic-gate     eof = feof( fp );
403*0Sstevel@tonic-gate     fclose( fp );
404*0Sstevel@tonic-gate 
405*0Sstevel@tonic-gate     if ( rlen != (long)bvp->bv_len ) {
406*0Sstevel@tonic-gate 	if ( reporterrs ) perror( path );
407*0Sstevel@tonic-gate 	free( bvp->bv_val );
408*0Sstevel@tonic-gate 	return( LDAPTOOL_FILEURL_FILEIOERROR );
409*0Sstevel@tonic-gate     }
410*0Sstevel@tonic-gate 
411*0Sstevel@tonic-gate     bvp->bv_val[ bvp->bv_len ] = '\0';
412*0Sstevel@tonic-gate     return( LDAPTOOL_FILEURL_SUCCESS );
413*0Sstevel@tonic-gate }
414*0Sstevel@tonic-gate 
415*0Sstevel@tonic-gate 
416*0Sstevel@tonic-gate /*
417*0Sstevel@tonic-gate  * Return a non-zero value if the string s begins with prefix and zero if not.
418*0Sstevel@tonic-gate  */
419*0Sstevel@tonic-gate static int
420*0Sstevel@tonic-gate str_starts_with( const char *s, char *prefix )
421*0Sstevel@tonic-gate {
422*0Sstevel@tonic-gate     size_t	prefix_len;
423*0Sstevel@tonic-gate 
424*0Sstevel@tonic-gate     if ( s == NULL || prefix == NULL ) {
425*0Sstevel@tonic-gate 	return( 0 );
426*0Sstevel@tonic-gate     }
427*0Sstevel@tonic-gate 
428*0Sstevel@tonic-gate     prefix_len = strlen( prefix );
429*0Sstevel@tonic-gate     if ( strlen( s ) < prefix_len ) {
430*0Sstevel@tonic-gate 	return( 0 );
431*0Sstevel@tonic-gate     }
432*0Sstevel@tonic-gate 
433*0Sstevel@tonic-gate     return( strncmp( s, prefix, prefix_len ) == 0 );
434*0Sstevel@tonic-gate }
435*0Sstevel@tonic-gate 
436*0Sstevel@tonic-gate 
437*0Sstevel@tonic-gate /*
438*0Sstevel@tonic-gate  * Remove URL hex escapes from s... done in place.  The basic concept for
439*0Sstevel@tonic-gate  * this routine is borrowed from the WWW library HTUnEscape() routine.
440*0Sstevel@tonic-gate  *
441*0Sstevel@tonic-gate  * A similar function called nsldapi_hex_unescape can be found in
442*0Sstevel@tonic-gate  * ../../libraries/libldap/unescape.c
443*0Sstevel@tonic-gate  */
444*0Sstevel@tonic-gate static void
445*0Sstevel@tonic-gate hex_unescape( char *s )
446*0Sstevel@tonic-gate {
447*0Sstevel@tonic-gate 	char	*p;
448*0Sstevel@tonic-gate 
449*0Sstevel@tonic-gate 	for ( p = s; *s != '\0'; ++s ) {
450*0Sstevel@tonic-gate 		if ( *s == '%' ) {
451*0Sstevel@tonic-gate 			if ( *++s != '\0' ) {
452*0Sstevel@tonic-gate 				*p = unhex( *s ) << 4;
453*0Sstevel@tonic-gate 			}
454*0Sstevel@tonic-gate 			if ( *++s != '\0' ) {
455*0Sstevel@tonic-gate 				*p++ += unhex( *s );
456*0Sstevel@tonic-gate 			}
457*0Sstevel@tonic-gate 		} else {
458*0Sstevel@tonic-gate 			*p++ = *s;
459*0Sstevel@tonic-gate 		}
460*0Sstevel@tonic-gate 	}
461*0Sstevel@tonic-gate 
462*0Sstevel@tonic-gate 	*p = '\0';
463*0Sstevel@tonic-gate }
464*0Sstevel@tonic-gate 
465*0Sstevel@tonic-gate 
466*0Sstevel@tonic-gate /*
467*0Sstevel@tonic-gate  * Return the integer equivalent of one hex digit (in c).
468*0Sstevel@tonic-gate  *
469*0Sstevel@tonic-gate  * A similar function can be found in ../../libraries/libldap/unescape.c
470*0Sstevel@tonic-gate  */
471*0Sstevel@tonic-gate static int
472*0Sstevel@tonic-gate unhex( char c )
473*0Sstevel@tonic-gate {
474*0Sstevel@tonic-gate 	return( c >= '0' && c <= '9' ? c - '0'
475*0Sstevel@tonic-gate 	    : c >= 'A' && c <= 'F' ? c - 'A' + 10
476*0Sstevel@tonic-gate 	    : c - 'a' + 10 );
477*0Sstevel@tonic-gate }
478*0Sstevel@tonic-gate 
479*0Sstevel@tonic-gate 
480*0Sstevel@tonic-gate #define HREF_CHAR_ACCEPTABLE( c )	(( c >= '-' && c <= '9' ) ||	\
481*0Sstevel@tonic-gate 					 ( c >= '@' && c <= 'Z' ) ||	\
482*0Sstevel@tonic-gate 					 ( c == '_' ) ||		\
483*0Sstevel@tonic-gate 					 ( c >= 'a' && c <= 'z' ))
484*0Sstevel@tonic-gate 
485*0Sstevel@tonic-gate /*
486*0Sstevel@tonic-gate  * Like strcat(), except if any URL-special characters are found in s2
487*0Sstevel@tonic-gate  * they are escaped using the %HH convention and backslash characters are
488*0Sstevel@tonic-gate  * converted to forward slashes on Windows.
489*0Sstevel@tonic-gate  *
490*0Sstevel@tonic-gate  * Maximum space needed in s1 is 3 * strlen( s2 ) + 1.
491*0Sstevel@tonic-gate  *
492*0Sstevel@tonic-gate  * A similar function that does not convert the slashes called
493*0Sstevel@tonic-gate  * strcat_escaped() can be found in ../../libraries/libldap/tmplout.c
494*0Sstevel@tonic-gate  */
495*0Sstevel@tonic-gate static void
496*0Sstevel@tonic-gate strcpy_escaped_and_convert( char *s1, char *s2 )
497*0Sstevel@tonic-gate {
498*0Sstevel@tonic-gate     char	*p, *q;
499*0Sstevel@tonic-gate     char	*hexdig = "0123456789ABCDEF";
500*0Sstevel@tonic-gate 
501*0Sstevel@tonic-gate     p = s1 + strlen( s1 );
502*0Sstevel@tonic-gate     for ( q = s2; *q != '\0'; ++q ) {
503*0Sstevel@tonic-gate #ifdef _WINDOWS
504*0Sstevel@tonic-gate 	if ( *q == '\\' ) {
505*0Sstevel@tonic-gate                 *p++ = '/';
506*0Sstevel@tonic-gate 	} else
507*0Sstevel@tonic-gate #endif /* _WINDOWS */
508*0Sstevel@tonic-gate 
509*0Sstevel@tonic-gate 	if ( HREF_CHAR_ACCEPTABLE( *q )) {
510*0Sstevel@tonic-gate 	    *p++ = *q;
511*0Sstevel@tonic-gate 	} else {
512*0Sstevel@tonic-gate 	    *p++ = '%';
513*0Sstevel@tonic-gate 	    *p++ = hexdig[ 0x0F & ((*(unsigned char*)q) >> 4) ];
514*0Sstevel@tonic-gate 	    *p++ = hexdig[ 0x0F & *q ];
515*0Sstevel@tonic-gate 	}
516*0Sstevel@tonic-gate     }
517*0Sstevel@tonic-gate 
518*0Sstevel@tonic-gate     *p = '\0';
519*0Sstevel@tonic-gate }
520