1N/A/*
1N/A * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
1N/A * Use is subject to license terms.
1N/A */
1N/A
1N/A/*
1N/A * The contents of this file are subject to the Netscape Public
1N/A * License Version 1.1 (the "License"); you may not use this file
1N/A * except in compliance with the License. You may obtain a copy of
1N/A * the License at http://www.mozilla.org/NPL/
1N/A *
1N/A * Software distributed under the License is distributed on an "AS
1N/A * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
1N/A * implied. See the License for the specific language governing
1N/A * rights and limitations under the License.
1N/A *
1N/A * The Original Code is Mozilla Communicator client code, released
1N/A * March 31, 1998.
1N/A *
1N/A * The Initial Developer of the Original Code is Netscape
1N/A * Communications Corporation. Portions created by Netscape are
1N/A * Copyright (C) 1998-1999 Netscape Communications Corporation. All
1N/A * Rights Reserved.
1N/A *
1N/A * Contributor(s):
1N/A */
1N/A
1N/A/*
1N/A * LDAP tools fileurl.c -- functions for handling file URLs.
1N/A * Used by ldapmodify.
1N/A */
1N/A
1N/A#include "ldaptool.h"
1N/A#include "fileurl.h"
1N/A#include <ctype.h> /* for isalpha() */
1N/A#ifdef SOLARIS_LDAP_CMD
1N/A#include <locale.h>
1N/A#endif /* SOLARIS_LDAP_CMD */
1N/A
1N/A#ifndef SOLARIS_LDAP_CMD
1N/A#define gettext(s) s
1N/A#endif
1N/A
1N/Astatic int str_starts_with( const char *s, char *prefix );
1N/Astatic void hex_unescape( char *s );
1N/Astatic int unhex( char c );
1N/Astatic void strcpy_escaped_and_convert( char *s1, char *s2 );
1N/Astatic int berval_from_file( const char *path, struct berval *bvp,
1N/A int reporterrs );
1N/A
1N/A/*
1N/A * Convert a file URL to a local path.
1N/A *
1N/A * If successful, LDAPTOOL_FILEURL_SUCCESS is returned and *localpathp is
1N/A * set point to an allocated string. If not, an different LDAPTOOL_FILEURL_
1N/A * error code is returned.
1N/A *
1N/A * See RFCs 1738 and 2396 for a specification for file URLs... but
1N/A * Netscape Navigator seems to be a bit more lenient in what it will
1N/A * accept, especially on Windows).
1N/A *
1N/A * This function parses file URLs of these three forms:
1N/A *
1N/A * file:///path
1N/A * file:/path
1N/A * file://localhost/path
1N/A * file://host/path (rejected with a ...NONLOCAL error)
1N/A *
1N/A * On Windows, we convert leading drive letters of the form C| to C:
1N/A * and if a drive letter is present we strip off the slash that precedes
1N/A * path. Otherwise, the leading slash is returned.
1N/A *
1N/A */
1N/Aint
1N/Aldaptool_fileurl2path( const char *fileurl, char **localpathp )
1N/A{
1N/A const char *path;
1N/A char *pathcopy;
1N/A
1N/A /*
1N/A * Make sure this is a file URL we can handle.
1N/A */
1N/A if ( !str_starts_with( fileurl, "file:" )) {
1N/A return( LDAPTOOL_FILEURL_NOTAFILEURL );
1N/A }
1N/A
1N/A path = fileurl + 5; /* skip past "file:" scheme prefix */
1N/A
1N/A if ( *path != '/' ) {
1N/A return( LDAPTOOL_FILEURL_MISSINGPATH );
1N/A }
1N/A
1N/A ++path; /* skip past '/' at end of "file:/" */
1N/A
1N/A if ( *path == '/' ) {
1N/A ++path; /* remainder is now host/path or /path */
1N/A if ( *path != '/' ) {
1N/A /*
1N/A * Make sure it is for the local host.
1N/A */
1N/A if ( str_starts_with( path, "localhost/" )) {
1N/A path += 9;
1N/A } else {
1N/A return( LDAPTOOL_FILEURL_NONLOCAL );
1N/A }
1N/A }
1N/A } else { /* URL is of the form file:/path */
1N/A --path;
1N/A }
1N/A
1N/A /*
1N/A * The remainder is now of the form /path. On Windows, skip past the
1N/A * leading slash if a drive letter is present.
1N/A */
1N/A#ifdef _WINDOWS
1N/A if ( isalpha( path[1] ) && ( path[2] == '|' || path[2] == ':' )) {
1N/A ++path;
1N/A }
1N/A#endif /* _WINDOWS */
1N/A
1N/A /*
1N/A * Duplicate the path so we can safely alter it.
1N/A * Unescape any %HH sequences.
1N/A */
1N/A if (( pathcopy = strdup( path )) == NULL ) {
1N/A return( LDAPTOOL_FILEURL_NOMEMORY );
1N/A }
1N/A hex_unescape( pathcopy );
1N/A
1N/A#ifdef _WINDOWS
1N/A /*
1N/A * Convert forward slashes to backslashes for Windows. Also,
1N/A * if we see a drive letter / vertical bar combination (e.g., c|)
1N/A * at the beginning of the path, replace the '|' with a ':'.
1N/A */
1N/A {
1N/A char *p;
1N/A
1N/A for ( p = pathcopy; *p != '\0'; ++p ) {
1N/A if ( *p == '/' ) {
1N/A *p = '\\';
1N/A }
1N/A }
1N/A }
1N/A
1N/A if ( isalpha( pathcopy[0] ) && pathcopy[1] == '|' ) {
1N/A pathcopy[1] = ':';
1N/A }
1N/A#endif /* _WINDOWS */
1N/A
1N/A *localpathp = pathcopy;
1N/A return( LDAPTOOL_FILEURL_SUCCESS );
1N/A}
1N/A
1N/A
1N/A/*
1N/A * Convert a local path to a file URL.
1N/A *
1N/A * If successful, LDAPTOOL_FILEURL_SUCCESS is returned and *urlp is
1N/A * set point to an allocated string. If not, an different LDAPTOOL_FILEURL_
1N/A * error code is returned. At present, the only possible error is
1N/A * LDAPTOOL_FILEURL_NOMEMORY.
1N/A *
1N/A * This function produces file URLs of the form file:path.
1N/A *
1N/A * On Windows, we convert leading drive letters to C|.
1N/A *
1N/A */
1N/Aint
1N/Aldaptool_path2fileurl( char *path, char **urlp )
1N/A{
1N/A char *p, *url, *prefix ="file:";
1N/A
1N/A if ( NULL == path ) {
1N/A path = "/";
1N/A }
1N/A
1N/A /*
1N/A * Allocate space for the URL, taking into account that path may
1N/A * expand during the hex escaping process.
1N/A */
1N/A if (( url = malloc( strlen( prefix ) + 3 * strlen( path ) + 1 )) == NULL ) {
1N/A return( LDAPTOOL_FILEURL_NOMEMORY );
1N/A }
1N/A
1N/A strcpy( url, prefix );
1N/A p = url + strlen( prefix );
1N/A
1N/A#ifdef _WINDOWS
1N/A /*
1N/A * On Windows, convert leading drive letters (e.g., C:) to the correct URL
1N/A * syntax (e.g., C|).
1N/A */
1N/A if ( isalpha( path[0] ) && path[1] == ':' ) {
1N/A *p++ = path[0];
1N/A *p++ = '|';
1N/A path += 2;
1N/A *p = '\0';
1N/A }
1N/A#endif /* _WINDOWS */
1N/A
1N/A /*
1N/A * Append the path, encoding any URL-special characters using the %HH
1N/A * convention.
1N/A * On Windows, convert backwards slashes in the path to forward ones.
1N/A */
1N/A strcpy_escaped_and_convert( p, path );
1N/A
1N/A *urlp = url;
1N/A return( LDAPTOOL_FILEURL_SUCCESS );
1N/A}
1N/A
1N/A
1N/A/*
1N/A * Populate *bvp from "value" of length "vlen."
1N/A *
1N/A * If recognize_url_syntax is non-zero, :<fileurl is recognized.
1N/A * If always_try_file is recognized and no file URL was found, an
1N/A * attempt is made to stat and read the value as if it were the name
1N/A * of a file.
1N/A *
1N/A * If reporterrs is non-zero, specific error messages are printed to
1N/A * stderr.
1N/A *
1N/A * If successful, LDAPTOOL_FILEURL_SUCCESS is returned and bvp->bv_len
1N/A * and bvp->bv_val are set (the latter is set to malloc'd memory).
1N/A * Upon failure, a different LDAPTOOL_FILEURL_ error code is returned.
1N/A */
1N/Aint
1N/Aldaptool_berval_from_ldif_value( const char *value, int vlen,
1N/A struct berval *bvp, int recognize_url_syntax, int always_try_file,
1N/A int reporterrs )
1N/A{
1N/A int rc = LDAPTOOL_FILEURL_SUCCESS; /* optimistic */
1N/A const char *url = NULL;
1N/A struct stat fstats;
1N/A
1N/A /* recognize "attr :< url" syntax if LDIF version is >= 1 */
1N/A
1N/A#ifdef notdef
1N/A if ( ldaptool_verbose ) {
1N/A fprintf( stderr, gettext("%s: ldaptool_berval_from_ldif_value: value: %s\n"),
1N/A ldaptool_progname, value);
1N/A }
1N/A#endif
1N/A
1N/A if ( recognize_url_syntax && *value == '<' ) {
1N/A for ( url = value + 1; isspace( *url ); ++url ) {
1N/A ; /* NULL */
1N/A }
1N/A
1N/A if (strlen(url) > 7 && strncasecmp(url, "file://", 7) != 0) {
1N/A /*
1N/A * We only support file:// URLs for now.
1N/A */
1N/A url = NULL;
1N/A }
1N/A }
1N/A
1N/A if ( NULL != url ) {
1N/A char *path;
1N/A
1N/A rc = ldaptool_fileurl2path( url, &path );
1N/A switch( rc ) {
1N/A case LDAPTOOL_FILEURL_NOTAFILEURL:
1N/A if ( reporterrs ) fprintf( stderr, gettext("%s: unsupported URL \"%s\";"
1N/A " use a file:// URL instead.\n"), ldaptool_progname, url );
1N/A break;
1N/A
1N/A case LDAPTOOL_FILEURL_MISSINGPATH:
1N/A if ( reporterrs ) fprintf( stderr,
1N/A gettext("%s: unable to process URL \"%s\" --"
1N/A " missing path.\n"), ldaptool_progname, url );
1N/A break;
1N/A
1N/A case LDAPTOOL_FILEURL_NONLOCAL:
1N/A if ( reporterrs ) fprintf( stderr,
1N/A gettext("%s: unable to process URL \"%s\" -- only"
1N/A " local file:// URLs are supported.\n"),
1N/A ldaptool_progname, url );
1N/A break;
1N/A
1N/A case LDAPTOOL_FILEURL_NOMEMORY:
1N/A if ( reporterrs ) perror( "ldaptool_fileurl2path" );
1N/A break;
1N/A
1N/A case LDAPTOOL_FILEURL_SUCCESS:
1N/A if ( stat( path, &fstats ) != 0 ) {
1N/A if ( reporterrs ) perror( path );
1N/A } else if (S_ISDIR(fstats.st_mode)) {
1N/A if ( reporterrs ) fprintf( stderr,
1N/A gettext("%s: %s is a directory, not a file\n"),
1N/A ldaptool_progname, path );
1N/A rc = LDAPTOOL_FILEURL_FILEIOERROR;
1N/A } else {
1N/A rc = berval_from_file( path, bvp, reporterrs );
1N/A }
1N/A free( path );
1N/A break;
1N/A
1N/A default:
1N/A if ( reporterrs ) fprintf( stderr,
1N/A gettext("%s: unable to process URL \"%s\""
1N/A " -- unknown error\n"), ldaptool_progname, url );
1N/A }
1N/A } else if ( always_try_file && (stat( value, &fstats ) == 0) &&
1N/A !S_ISDIR(fstats.st_mode)) { /* get value from file */
1N/A rc = berval_from_file( value, bvp, reporterrs );
1N/A } else {
1N/A bvp->bv_len = vlen;
1N/A if (( bvp->bv_val = (char *)malloc( vlen + 1 )) == NULL ) {
1N/A if ( reporterrs ) perror( "malloc" );
1N/A rc = LDAPTOOL_FILEURL_NOMEMORY;
1N/A } else {
1N/A SAFEMEMCPY( bvp->bv_val, value, vlen );
1N/A bvp->bv_val[ vlen ] = '\0';
1N/A }
1N/A }
1N/A
1N/A return( rc );
1N/A}
1N/A
1N/A
1N/A/*
1N/A * Map an LDAPTOOL_FILEURL_ error code to an LDAP error code (crude).
1N/A */
1N/Aint
1N/Aldaptool_fileurlerr2ldaperr( int lderr )
1N/A{
1N/A int rc;
1N/A
1N/A switch( lderr ) {
1N/A case LDAPTOOL_FILEURL_SUCCESS:
1N/A rc = LDAP_SUCCESS;
1N/A break;
1N/A case LDAPTOOL_FILEURL_NOMEMORY:
1N/A rc = LDAP_NO_MEMORY;
1N/A break;
1N/A default:
1N/A rc = LDAP_PARAM_ERROR;
1N/A }
1N/A
1N/A return( rc );
1N/A}
1N/A
1N/A
1N/A/*
1N/A * Populate *bvp with the contents of the file named by "path".
1N/A *
1N/A * If reporterrs is non-zero, specific error messages are printed to
1N/A * stderr.
1N/A *
1N/A * If successful, LDAPTOOL_FILEURL_SUCCESS is returned and bvp->bv_len
1N/A * and bvp->bv_val are set (the latter is set to malloc'd memory).
1N/A * Upon failure, a different LDAPTOOL_FILEURL_ error code is returned.
1N/A */
1N/A
1N/Astatic int
1N/Aberval_from_file( const char *path, struct berval *bvp, int reporterrs )
1N/A{
1N/A FILE *fp;
1N/A long rlen;
1N/A int eof;
1N/A#if defined( XP_WIN32 )
1N/A char mode[20] = "r+b";
1N/A#else
1N/A char mode[20] = "r";
1N/A#endif
1N/A
1N/A#ifdef SOLARIS_LDAP_CMD
1N/A if (( fp = fopen( path, mode )) == NULL ) {
1N/A#else
1N/A if (( fp = ldaptool_open_file( path, mode )) == NULL ) {
1N/A#endif /* SOLARIS_LDAP_CMD */
1N/A if ( reporterrs ) perror( path );
1N/A return( LDAPTOOL_FILEURL_FILEIOERROR );
1N/A }
1N/A
1N/A if ( fseek( fp, 0L, SEEK_END ) != 0 ) {
1N/A if ( reporterrs ) perror( path );
1N/A fclose( fp );
1N/A return( LDAPTOOL_FILEURL_FILEIOERROR );
1N/A }
1N/A
1N/A bvp->bv_len = ftell( fp );
1N/A
1N/A if (( bvp->bv_val = (char *)malloc( bvp->bv_len + 1 )) == NULL ) {
1N/A if ( reporterrs ) perror( "malloc" );
1N/A fclose( fp );
1N/A return( LDAPTOOL_FILEURL_NOMEMORY );
1N/A }
1N/A
1N/A if ( fseek( fp, 0L, SEEK_SET ) != 0 ) {
1N/A if ( reporterrs ) perror( path );
1N/A fclose( fp );
1N/A return( LDAPTOOL_FILEURL_FILEIOERROR );
1N/A }
1N/A
1N/A rlen = fread( bvp->bv_val, 1, bvp->bv_len, fp );
1N/A eof = feof( fp );
1N/A fclose( fp );
1N/A
1N/A if ( rlen != (long)bvp->bv_len ) {
1N/A if ( reporterrs ) perror( path );
1N/A free( bvp->bv_val );
1N/A return( LDAPTOOL_FILEURL_FILEIOERROR );
1N/A }
1N/A
1N/A bvp->bv_val[ bvp->bv_len ] = '\0';
1N/A return( LDAPTOOL_FILEURL_SUCCESS );
1N/A}
1N/A
1N/A
1N/A/*
1N/A * Return a non-zero value if the string s begins with prefix and zero if not.
1N/A */
1N/Astatic int
1N/Astr_starts_with( const char *s, char *prefix )
1N/A{
1N/A size_t prefix_len;
1N/A
1N/A if ( s == NULL || prefix == NULL ) {
1N/A return( 0 );
1N/A }
1N/A
1N/A prefix_len = strlen( prefix );
1N/A if ( strlen( s ) < prefix_len ) {
1N/A return( 0 );
1N/A }
1N/A
1N/A return( strncmp( s, prefix, prefix_len ) == 0 );
1N/A}
1N/A
1N/A
1N/A/*
1N/A * Remove URL hex escapes from s... done in place. The basic concept for
1N/A * this routine is borrowed from the WWW library HTUnEscape() routine.
1N/A *
1N/A * A similar function called nsldapi_hex_unescape can be found in
1N/A * ../../libraries/libldap/unescape.c
1N/A */
1N/Astatic void
1N/Ahex_unescape( char *s )
1N/A{
1N/A char *p;
1N/A
1N/A for ( p = s; *s != '\0'; ++s ) {
1N/A if ( *s == '%' ) {
1N/A if ( *++s != '\0' ) {
1N/A *p = unhex( *s ) << 4;
1N/A }
1N/A if ( *++s != '\0' ) {
1N/A *p++ += unhex( *s );
1N/A }
1N/A } else {
1N/A *p++ = *s;
1N/A }
1N/A }
1N/A
1N/A *p = '\0';
1N/A}
1N/A
1N/A
1N/A/*
1N/A * Return the integer equivalent of one hex digit (in c).
1N/A *
1N/A * A similar function can be found in ../../libraries/libldap/unescape.c
1N/A */
1N/Astatic int
1N/Aunhex( char c )
1N/A{
1N/A return( c >= '0' && c <= '9' ? c - '0'
1N/A : c >= 'A' && c <= 'F' ? c - 'A' + 10
1N/A : c - 'a' + 10 );
1N/A}
1N/A
1N/A
1N/A#define HREF_CHAR_ACCEPTABLE( c ) (( c >= '-' && c <= '9' ) || \
1N/A ( c >= '@' && c <= 'Z' ) || \
1N/A ( c == '_' ) || \
1N/A ( c >= 'a' && c <= 'z' ))
1N/A
1N/A/*
1N/A * Like strcat(), except if any URL-special characters are found in s2
1N/A * they are escaped using the %HH convention and backslash characters are
1N/A * converted to forward slashes on Windows.
1N/A *
1N/A * Maximum space needed in s1 is 3 * strlen( s2 ) + 1.
1N/A *
1N/A * A similar function that does not convert the slashes called
1N/A * strcat_escaped() can be found in ../../libraries/libldap/tmplout.c
1N/A */
1N/Astatic void
1N/Astrcpy_escaped_and_convert( char *s1, char *s2 )
1N/A{
1N/A char *p, *q;
1N/A char *hexdig = "0123456789ABCDEF";
1N/A
1N/A p = s1 + strlen( s1 );
1N/A for ( q = s2; *q != '\0'; ++q ) {
1N/A#ifdef _WINDOWS
1N/A if ( *q == '\\' ) {
1N/A *p++ = '/';
1N/A } else
1N/A#endif /* _WINDOWS */
1N/A
1N/A if ( HREF_CHAR_ACCEPTABLE( *q )) {
1N/A *p++ = *q;
1N/A } else {
1N/A *p++ = '%';
1N/A *p++ = hexdig[ 0x0F & ((*(unsigned char*)q) >> 4) ];
1N/A *p++ = hexdig[ 0x0F & *q ];
1N/A }
1N/A }
1N/A
1N/A *p = '\0';
1N/A}