#include "ldaptool.h"
#include "fileurl.h"
#include <ctype.h>
#ifdef SOLARIS_LDAP_CMD
#include <locale.h>
#endif
#ifndef SOLARIS_LDAP_CMD
#define gettext(s) s
#endif
static int str_starts_with( const char *s, char *prefix );
static void hex_unescape( char *s );
static int unhex( char c );
static void strcpy_escaped_and_convert( char *s1, char *s2 );
static int berval_from_file( const char *path, struct berval *bvp,
int reporterrs );
int
ldaptool_fileurl2path( const char *fileurl, char **localpathp )
{
const char *path;
char *pathcopy;
if ( !str_starts_with( fileurl, "file:" )) {
return( LDAPTOOL_FILEURL_NOTAFILEURL );
}
path = fileurl + 5;
if ( *path != '/' ) {
return( LDAPTOOL_FILEURL_MISSINGPATH );
}
++path;
if ( *path == '/' ) {
++path;
if ( *path != '/' ) {
if ( str_starts_with( path, "localhost/" )) {
path += 9;
} else {
return( LDAPTOOL_FILEURL_NONLOCAL );
}
}
} else {
--path;
}
#ifdef _WINDOWS
if ( isalpha( path[1] ) && ( path[2] == '|' || path[2] == ':' )) {
++path;
}
#endif
if (( pathcopy = strdup( path )) == NULL ) {
return( LDAPTOOL_FILEURL_NOMEMORY );
}
hex_unescape( pathcopy );
#ifdef _WINDOWS
{
char *p;
for ( p = pathcopy; *p != '\0'; ++p ) {
if ( *p == '/' ) {
*p = '\\';
}
}
}
if ( isalpha( pathcopy[0] ) && pathcopy[1] == '|' ) {
pathcopy[1] = ':';
}
#endif
*localpathp = pathcopy;
return( LDAPTOOL_FILEURL_SUCCESS );
}
int
ldaptool_path2fileurl( char *path, char **urlp )
{
char *p, *url, *prefix ="file:";
if ( NULL == path ) {
path = "/";
}
if (( url = malloc( strlen( prefix ) + 3 * strlen( path ) + 1 )) == NULL ) {
return( LDAPTOOL_FILEURL_NOMEMORY );
}
strcpy( url, prefix );
p = url + strlen( prefix );
#ifdef _WINDOWS
if ( isalpha( path[0] ) && path[1] == ':' ) {
*p++ = path[0];
*p++ = '|';
path += 2;
*p = '\0';
}
#endif
strcpy_escaped_and_convert( p, path );
*urlp = url;
return( LDAPTOOL_FILEURL_SUCCESS );
}
int
ldaptool_berval_from_ldif_value( const char *value, int vlen,
struct berval *bvp, int recognize_url_syntax, int always_try_file,
int reporterrs )
{
int rc = LDAPTOOL_FILEURL_SUCCESS;
const char *url = NULL;
struct stat fstats;
#ifdef notdef
if ( ldaptool_verbose ) {
fprintf( stderr, gettext("%s: ldaptool_berval_from_ldif_value: value: %s\n"),
ldaptool_progname, value);
}
#endif
if ( recognize_url_syntax && *value == '<' ) {
for ( url = value + 1; isspace( *url ); ++url ) {
;
}
if (strlen(url) > 7 && strncasecmp(url, "file://", 7) != 0) {
url = NULL;
}
}
if ( NULL != url ) {
char *path;
rc = ldaptool_fileurl2path( url, &path );
switch( rc ) {
case LDAPTOOL_FILEURL_NOTAFILEURL:
if ( reporterrs ) fprintf( stderr, gettext("%s: unsupported URL \"%s\";"
" use a file:// URL instead.\n"), ldaptool_progname, url );
break;
case LDAPTOOL_FILEURL_MISSINGPATH:
if ( reporterrs ) fprintf( stderr,
gettext("%s: unable to process URL \"%s\" --"
" missing path.\n"), ldaptool_progname, url );
break;
case LDAPTOOL_FILEURL_NONLOCAL:
if ( reporterrs ) fprintf( stderr,
gettext("%s: unable to process URL \"%s\" -- only"
" local file:// URLs are supported.\n"),
ldaptool_progname, url );
break;
case LDAPTOOL_FILEURL_NOMEMORY:
if ( reporterrs ) perror( "ldaptool_fileurl2path" );
break;
case LDAPTOOL_FILEURL_SUCCESS:
if ( stat( path, &fstats ) != 0 ) {
if ( reporterrs ) perror( path );
} else if (S_ISDIR(fstats.st_mode)) {
if ( reporterrs ) fprintf( stderr,
gettext("%s: %s is a directory, not a file\n"),
ldaptool_progname, path );
rc = LDAPTOOL_FILEURL_FILEIOERROR;
} else {
rc = berval_from_file( path, bvp, reporterrs );
}
free( path );
break;
default:
if ( reporterrs ) fprintf( stderr,
gettext("%s: unable to process URL \"%s\""
" -- unknown error\n"), ldaptool_progname, url );
}
} else if ( always_try_file && (stat( value, &fstats ) == 0) &&
!S_ISDIR(fstats.st_mode)) {
rc = berval_from_file( value, bvp, reporterrs );
} else {
bvp->bv_len = vlen;
if (( bvp->bv_val = (char *)malloc( vlen + 1 )) == NULL ) {
if ( reporterrs ) perror( "malloc" );
rc = LDAPTOOL_FILEURL_NOMEMORY;
} else {
SAFEMEMCPY( bvp->bv_val, value, vlen );
bvp->bv_val[ vlen ] = '\0';
}
}
return( rc );
}
int
ldaptool_fileurlerr2ldaperr( int lderr )
{
int rc;
switch( lderr ) {
case LDAPTOOL_FILEURL_SUCCESS:
rc = LDAP_SUCCESS;
break;
case LDAPTOOL_FILEURL_NOMEMORY:
rc = LDAP_NO_MEMORY;
break;
default:
rc = LDAP_PARAM_ERROR;
}
return( rc );
}
static int
berval_from_file( const char *path, struct berval *bvp, int reporterrs )
{
FILE *fp;
long rlen;
#if defined( XP_WIN32 )
char mode[20] = "r+b";
#else
char mode[20] = "r";
#endif
#ifdef SOLARIS_LDAP_CMD
if (( fp = fopen( path, mode )) == NULL ) {
#else
if (( fp = ldaptool_open_file( path, mode )) == NULL ) {
#endif
if ( reporterrs ) perror( path );
return( LDAPTOOL_FILEURL_FILEIOERROR );
}
if ( fseek( fp, 0L, SEEK_END ) != 0 ) {
if ( reporterrs ) perror( path );
fclose( fp );
return( LDAPTOOL_FILEURL_FILEIOERROR );
}
bvp->bv_len = ftell( fp );
if (( bvp->bv_val = (char *)malloc( bvp->bv_len + 1 )) == NULL ) {
if ( reporterrs ) perror( "malloc" );
fclose( fp );
return( LDAPTOOL_FILEURL_NOMEMORY );
}
if ( fseek( fp, 0L, SEEK_SET ) != 0 ) {
if ( reporterrs ) perror( path );
fclose( fp );
return( LDAPTOOL_FILEURL_FILEIOERROR );
}
rlen = fread( bvp->bv_val, 1, bvp->bv_len, fp );
fclose( fp );
if ( rlen != (long)bvp->bv_len ) {
if ( reporterrs ) perror( path );
free( bvp->bv_val );
return( LDAPTOOL_FILEURL_FILEIOERROR );
}
bvp->bv_val[ bvp->bv_len ] = '\0';
return( LDAPTOOL_FILEURL_SUCCESS );
}
static int
str_starts_with( const char *s, char *prefix )
{
size_t prefix_len;
if ( s == NULL || prefix == NULL ) {
return( 0 );
}
prefix_len = strlen( prefix );
if ( strlen( s ) < prefix_len ) {
return( 0 );
}
return( strncmp( s, prefix, prefix_len ) == 0 );
}
static void
hex_unescape( char *s )
{
char *p;
for ( p = s; *s != '\0'; ++s ) {
if ( *s == '%' ) {
if ( *++s != '\0' ) {
*p = unhex( *s ) << 4;
}
if ( *++s != '\0' ) {
*p++ += unhex( *s );
}
} else {
*p++ = *s;
}
}
*p = '\0';
}
static int
unhex( char c )
{
return( c >= '0' && c <= '9' ? c - '0'
: c >= 'A' && c <= 'F' ? c - 'A' + 10
: c - 'a' + 10 );
}
#define HREF_CHAR_ACCEPTABLE( c ) (( c >= '-' && c <= '9' ) || \
( c >= '@' && c <= 'Z' ) || \
( c == '_' ) || \
( c >= 'a' && c <= 'z' ))
static void
strcpy_escaped_and_convert( char *s1, char *s2 )
{
char *p, *q;
char *hexdig = "0123456789ABCDEF";
p = s1 + strlen( s1 );
for ( q = s2; *q != '\0'; ++q ) {
#ifdef _WINDOWS
if ( *q == '\\' ) {
*p++ = '/';
} else
#endif
if ( HREF_CHAR_ACCEPTABLE( *q )) {
*p++ = *q;
} else {
*p++ = '%';
*p++ = hexdig[ 0x0F & ((*(unsigned char*)q) >> 4) ];
*p++ = hexdig[ 0x0F & *q ];
}
}
*p = '\0';
}