mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-10 01:40:17 +01:00
507 lines
13 KiB
C
507 lines
13 KiB
C
|
/* ***** BEGIN LICENSE BLOCK *****
|
||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||
|
*
|
||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||
|
* the License. You may obtain a copy of the License at
|
||
|
* http://www.mozilla.org/MPL/
|
||
|
*
|
||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||
|
* for the specific language governing rights and limitations under the
|
||
|
* License.
|
||
|
*
|
||
|
* The Original Code is Mozilla Communicator client code, released
|
||
|
* March 31, 1998.
|
||
|
*
|
||
|
* The Initial Developer of the Original Code is
|
||
|
* Netscape Communications Corporation.
|
||
|
* Portions created by the Initial Developer are Copyright (C) 1998-1999
|
||
|
* the Initial Developer. All Rights Reserved.
|
||
|
*
|
||
|
* Contributor(s):
|
||
|
*
|
||
|
* Alternatively, the contents of this file may be used under the terms of
|
||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||
|
* of those above. If you wish to allow use of your version of this file only
|
||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||
|
* use your version of this file under the terms of the MPL, indicate your
|
||
|
* decision by deleting the provisions above and replace them with the notice
|
||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||
|
* the provisions above, a recipient may use your version of this file under
|
||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||
|
*
|
||
|
* ***** END LICENSE BLOCK ***** */
|
||
|
|
||
|
/*
|
||
|
* LDAP tools fileurl.c -- functions for handling file URLs.
|
||
|
* Used by ldapmodify.
|
||
|
*/
|
||
|
|
||
|
#include "ldaptool.h"
|
||
|
#include "fileurl.h"
|
||
|
#include <ctype.h> /* for isalpha() */
|
||
|
|
||
|
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 );
|
||
|
|
||
|
/*
|
||
|
* Convert a file URL to a local path.
|
||
|
*
|
||
|
* If successful, LDAPTOOL_FILEURL_SUCCESS is returned and *localpathp is
|
||
|
* set point to an allocated string. If not, an different LDAPTOOL_FILEURL_
|
||
|
* error code is returned.
|
||
|
*
|
||
|
* See RFCs 1738 and 2396 for a specification for file URLs... but
|
||
|
* Netscape Navigator seems to be a bit more lenient in what it will
|
||
|
* accept, especially on Windows).
|
||
|
*
|
||
|
* This function parses file URLs of these three forms:
|
||
|
*
|
||
|
* file:///path
|
||
|
* file:/path
|
||
|
* file://localhost/path
|
||
|
* file://host/path (rejected with a ...NONLOCAL error)
|
||
|
*
|
||
|
* On Windows, we convert leading drive letters of the form C| to C:
|
||
|
* and if a drive letter is present we strip off the slash that precedes
|
||
|
* path. Otherwise, the leading slash is returned.
|
||
|
*
|
||
|
*/
|
||
|
int
|
||
|
ldaptool_fileurl2path( const char *fileurl, char **localpathp )
|
||
|
{
|
||
|
const char *path;
|
||
|
char *pathcopy;
|
||
|
|
||
|
/*
|
||
|
* Make sure this is a file URL we can handle.
|
||
|
*/
|
||
|
if ( !str_starts_with( fileurl, "file:" )) {
|
||
|
return( LDAPTOOL_FILEURL_NOTAFILEURL );
|
||
|
}
|
||
|
|
||
|
path = fileurl + 5; /* skip past "file:" scheme prefix */
|
||
|
|
||
|
if ( *path != '/' ) {
|
||
|
return( LDAPTOOL_FILEURL_MISSINGPATH );
|
||
|
}
|
||
|
|
||
|
++path; /* skip past '/' at end of "file:/" */
|
||
|
|
||
|
if ( *path == '/' ) {
|
||
|
++path; /* remainder is now host/path or /path */
|
||
|
if ( *path != '/' ) {
|
||
|
/*
|
||
|
* Make sure it is for the local host.
|
||
|
*/
|
||
|
if ( str_starts_with( path, "localhost/" )) {
|
||
|
path += 9;
|
||
|
} else {
|
||
|
return( LDAPTOOL_FILEURL_NONLOCAL );
|
||
|
}
|
||
|
}
|
||
|
} else { /* URL is of the form file:/path */
|
||
|
--path;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* The remainder is now of the form /path. On Windows, skip past the
|
||
|
* leading slash if a drive letter is present.
|
||
|
*/
|
||
|
#ifdef _WINDOWS
|
||
|
if ( isalpha( path[1] ) && ( path[2] == '|' || path[2] == ':' )) {
|
||
|
++path;
|
||
|
}
|
||
|
#endif /* _WINDOWS */
|
||
|
|
||
|
/*
|
||
|
* Duplicate the path so we can safely alter it.
|
||
|
* Unescape any %HH sequences.
|
||
|
*/
|
||
|
if (( pathcopy = strdup( path )) == NULL ) {
|
||
|
return( LDAPTOOL_FILEURL_NOMEMORY );
|
||
|
}
|
||
|
hex_unescape( pathcopy );
|
||
|
|
||
|
#ifdef _WINDOWS
|
||
|
/*
|
||
|
* Convert forward slashes to backslashes for Windows. Also,
|
||
|
* if we see a drive letter / vertical bar combination (e.g., c|)
|
||
|
* at the beginning of the path, replace the '|' with a ':'.
|
||
|
*/
|
||
|
{
|
||
|
char *p;
|
||
|
|
||
|
for ( p = pathcopy; *p != '\0'; ++p ) {
|
||
|
if ( *p == '/' ) {
|
||
|
*p = '\\';
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( isalpha( pathcopy[0] ) && pathcopy[1] == '|' ) {
|
||
|
pathcopy[1] = ':';
|
||
|
}
|
||
|
#endif /* _WINDOWS */
|
||
|
|
||
|
*localpathp = pathcopy;
|
||
|
return( LDAPTOOL_FILEURL_SUCCESS );
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Convert a local path to a file URL.
|
||
|
*
|
||
|
* If successful, LDAPTOOL_FILEURL_SUCCESS is returned and *urlp is
|
||
|
* set point to an allocated string. If not, an different LDAPTOOL_FILEURL_
|
||
|
* error code is returned. At present, the only possible error is
|
||
|
* LDAPTOOL_FILEURL_NOMEMORY.
|
||
|
*
|
||
|
* This function produces file URLs of the form file:path.
|
||
|
*
|
||
|
* On Windows, we convert leading drive letters to C|.
|
||
|
*
|
||
|
*/
|
||
|
int
|
||
|
ldaptool_path2fileurl( char *path, char **urlp )
|
||
|
{
|
||
|
char *p, *url, *prefix ="file:";
|
||
|
|
||
|
if ( NULL == path ) {
|
||
|
path = "/";
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Allocate space for the URL, taking into account that path may
|
||
|
* expand during the hex escaping process.
|
||
|
*/
|
||
|
if (( url = malloc( strlen( prefix ) + 3 * strlen( path ) + 1 )) == NULL ) {
|
||
|
return( LDAPTOOL_FILEURL_NOMEMORY );
|
||
|
}
|
||
|
|
||
|
strcpy( url, prefix );
|
||
|
p = url + strlen( prefix );
|
||
|
|
||
|
#ifdef _WINDOWS
|
||
|
/*
|
||
|
* On Windows, convert leading drive letters (e.g., C:) to the correct URL
|
||
|
* syntax (e.g., C|).
|
||
|
*/
|
||
|
if ( isalpha( path[0] ) && path[1] == ':' ) {
|
||
|
*p++ = path[0];
|
||
|
*p++ = '|';
|
||
|
path += 2;
|
||
|
*p = '\0';
|
||
|
}
|
||
|
#endif /* _WINDOWS */
|
||
|
|
||
|
/*
|
||
|
* Append the path, encoding any URL-special characters using the %HH
|
||
|
* convention.
|
||
|
* On Windows, convert backwards slashes in the path to forward ones.
|
||
|
*/
|
||
|
strcpy_escaped_and_convert( p, path );
|
||
|
|
||
|
*urlp = url;
|
||
|
return( LDAPTOOL_FILEURL_SUCCESS );
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Populate *bvp from "value" of length "vlen."
|
||
|
*
|
||
|
* If recognize_url_syntax is non-zero, :<fileurl is recognized.
|
||
|
* If always_try_file is recognized and no file URL was found, an
|
||
|
* attempt is made to stat and read the value as if it were the name
|
||
|
* of a file.
|
||
|
*
|
||
|
* If reporterrs is non-zero, specific error messages are printed to
|
||
|
* stderr.
|
||
|
*
|
||
|
* If successful, LDAPTOOL_FILEURL_SUCCESS is returned and bvp->bv_len
|
||
|
* and bvp->bv_val are set (the latter is set to malloc'd memory).
|
||
|
* Upon failure, a different LDAPTOOL_FILEURL_ error code is returned.
|
||
|
*/
|
||
|
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; /* optimistic */
|
||
|
struct stat fstats;
|
||
|
|
||
|
/* recognize "attr :< url" syntax if LDIF version is >= 1 */
|
||
|
|
||
|
if ( recognize_url_syntax && *value == '<' ) {
|
||
|
const char *url;
|
||
|
char *path;
|
||
|
|
||
|
for ( url = value + 1; isspace( *url ); ++url ) {
|
||
|
; /* NULL */
|
||
|
}
|
||
|
|
||
|
if (strlen(url) > 7 && strncasecmp(url, "file://", 7) == 0) {
|
||
|
/*
|
||
|
* We only support file:// URLs for now.
|
||
|
*/
|
||
|
rc = ldaptool_fileurl2path( url, &path );
|
||
|
switch( rc ) {
|
||
|
case LDAPTOOL_FILEURL_NOTAFILEURL:
|
||
|
if ( reporterrs ) fprintf( stderr, "%s: unsupported URL \"%s\";"
|
||
|
" use a file:// URL instead.\n", ldaptool_progname, url );
|
||
|
break;
|
||
|
|
||
|
case LDAPTOOL_FILEURL_MISSINGPATH:
|
||
|
if ( reporterrs ) fprintf( stderr,
|
||
|
"%s: unable to process URL \"%s\" --"
|
||
|
" missing path.\n", ldaptool_progname, url );
|
||
|
break;
|
||
|
|
||
|
case LDAPTOOL_FILEURL_NONLOCAL:
|
||
|
if ( reporterrs ) fprintf( stderr,
|
||
|
"%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 ( fstats.st_mode & S_IFDIR ) {
|
||
|
if ( reporterrs ) fprintf( stderr,
|
||
|
"%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,
|
||
|
"%s: unable to process URL \"%s\""
|
||
|
" -- unknown error\n", ldaptool_progname, url );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if ( always_try_file && (stat( value, &fstats ) == 0) &&
|
||
|
!(fstats.st_mode & S_IFDIR)) { /* get value from file */
|
||
|
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 );
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Map an LDAPTOOL_FILEURL_ error code to an LDAP error code (crude).
|
||
|
*/
|
||
|
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 );
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Populate *bvp with the contents of the file named by "path".
|
||
|
*
|
||
|
* If reporterrs is non-zero, specific error messages are printed to
|
||
|
* stderr.
|
||
|
*
|
||
|
* If successful, LDAPTOOL_FILEURL_SUCCESS is returned and bvp->bv_len
|
||
|
* and bvp->bv_val are set (the latter is set to malloc'd memory).
|
||
|
* Upon failure, a different LDAPTOOL_FILEURL_ error code is returned.
|
||
|
*/
|
||
|
|
||
|
static int
|
||
|
berval_from_file( const char *path, struct berval *bvp, int reporterrs )
|
||
|
{
|
||
|
FILE *fp;
|
||
|
long rlen;
|
||
|
int eof;
|
||
|
#if defined( XP_WIN32 )
|
||
|
char mode[20] = "r+b";
|
||
|
#else
|
||
|
char mode[20] = "r";
|
||
|
#endif
|
||
|
|
||
|
if (( fp = fopen( path, mode )) == NULL ) {
|
||
|
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 );
|
||
|
eof = feof( 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 );
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Return a non-zero value if the string s begins with prefix and zero if not.
|
||
|
*/
|
||
|
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 );
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Remove URL hex escapes from s... done in place. The basic concept for
|
||
|
* this routine is borrowed from the WWW library HTUnEscape() routine.
|
||
|
*
|
||
|
* A similar function called nsldapi_hex_unescape can be found in
|
||
|
* ../../libraries/libldap/unescape.c
|
||
|
*/
|
||
|
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';
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Return the integer equivalent of one hex digit (in c).
|
||
|
*
|
||
|
* A similar function can be found in ../../libraries/libldap/unescape.c
|
||
|
*/
|
||
|
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' ))
|
||
|
|
||
|
/*
|
||
|
* Like strcat(), except if any URL-special characters are found in s2
|
||
|
* they are escaped using the %HH convention and backslash characters are
|
||
|
* converted to forward slashes on Windows.
|
||
|
*
|
||
|
* Maximum space needed in s1 is 3 * strlen( s2 ) + 1.
|
||
|
*
|
||
|
* A similar function that does not convert the slashes called
|
||
|
* strcat_escaped() can be found in ../../libraries/libldap/tmplout.c
|
||
|
*/
|
||
|
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 /* _WINDOWS */
|
||
|
|
||
|
if ( HREF_CHAR_ACCEPTABLE( *q )) {
|
||
|
*p++ = *q;
|
||
|
} else {
|
||
|
*p++ = '%';
|
||
|
*p++ = hexdig[ 0x0F & ((*(unsigned char*)q) >> 4) ];
|
||
|
*p++ = hexdig[ 0x0F & *q ];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
*p = '\0';
|
||
|
}
|