2003-08-13 05:53:07 +04:00
/*
Unix SMB / CIFS implementation .
filename matching routine
2004-10-02 05:43:43 +04:00
Copyright ( C ) Andrew Tridgell 1992 - 2004
2003-08-13 05:53:07 +04:00
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
2007-07-10 06:07:03 +04:00
the Free Software Foundation ; either version 3 of the License , or
2003-08-13 05:53:07 +04:00
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
2007-07-10 06:07:03 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2004-10-02 05:43:43 +04:00
*/
2003-08-13 05:53:07 +04:00
/*
This module was originally based on fnmatch . c copyright by the Free
2004-10-02 05:43:43 +04:00
Software Foundation . It bears little ( if any ) resemblence to that
code now
2003-08-13 05:53:07 +04:00
*/
2006-02-28 16:12:39 +03:00
/**
* @ file
* @ brief MS - style Filename matching
*/
2003-08-13 05:53:07 +04:00
# include "includes.h"
2011-07-06 07:05:45 +04:00
# include "libcli/smb/smb_constants.h"
2003-08-13 05:53:07 +04:00
2004-10-08 12:13:00 +04:00
static int null_match ( const char * p )
2003-08-13 05:53:07 +04:00
{
2004-10-02 05:43:43 +04:00
for ( ; * p ; p + + ) {
2004-10-08 12:13:00 +04:00
if ( * p ! = ' * ' & &
* p ! = ' < ' & &
* p ! = ' " ' & &
* p ! = ' > ' ) return - 1 ;
2003-08-13 05:53:07 +04:00
}
return 0 ;
}
2004-10-02 05:43:43 +04:00
/*
the max_n structure is purely for efficiency , it doesn ' t contribute
to the matching algorithm except by ensuring that the algorithm does
not grow exponentially
*/
struct max_n {
2004-10-08 12:13:00 +04:00
const char * predot ;
const char * postdot ;
2004-10-02 05:43:43 +04:00
} ;
2003-08-13 05:53:07 +04:00
2004-10-02 05:43:43 +04:00
/*
p and n are the pattern and string being matched . The max_n array is
an optimisation only . The ldot pointer is NULL if the string does
not contain a ' . ' , otherwise it points at the last dot in ' n ' .
2003-08-13 05:53:07 +04:00
*/
2004-10-08 12:13:00 +04:00
static int ms_fnmatch_core ( const char * p , const char * n ,
2016-10-25 12:53:53 +03:00
struct max_n * max_n , const char * ldot ,
bool is_case_sensitive )
2003-08-13 05:53:07 +04:00
{
2004-10-08 12:13:00 +04:00
codepoint_t c , c2 ;
2004-10-02 05:43:43 +04:00
int i ;
2004-10-08 12:13:00 +04:00
size_t size , size_n ;
2008-10-24 18:00:43 +04:00
while ( ( c = next_codepoint ( p , & size ) ) ) {
2004-10-08 12:13:00 +04:00
p + = size ;
2003-08-13 05:53:07 +04:00
switch ( c ) {
2004-10-08 12:13:00 +04:00
case ' * ' :
2004-10-02 05:43:43 +04:00
/* a '*' matches zero or more characters of any type */
2019-05-24 17:03:37 +03:00
if ( max_n ! = NULL & & max_n - > predot & &
max_n - > predot < = n ) {
2004-10-02 05:43:43 +04:00
return null_match ( p ) ;
}
2004-10-08 12:13:00 +04:00
for ( i = 0 ; n [ i ] ; i + = size_n ) {
2008-10-24 18:00:43 +04:00
next_codepoint ( n + i , & size_n ) ;
2016-10-25 12:53:53 +03:00
if ( ms_fnmatch_core ( p , n + i , max_n + 1 , ldot , is_case_sensitive ) = = 0 ) {
2004-10-02 05:43:43 +04:00
return 0 ;
}
}
2019-05-24 17:03:37 +03:00
if ( max_n ! = NULL & & ( ! max_n - > predot | |
max_n - > predot > n ) ) {
max_n - > predot = n ;
}
2004-10-02 05:43:43 +04:00
return null_match ( p ) ;
2003-08-13 05:53:07 +04:00
2004-10-08 12:13:00 +04:00
case ' < ' :
2004-10-02 05:43:43 +04:00
/* a '<' matches zero or more characters of
any type , but stops matching at the last
' . ' in the string . */
2019-05-24 17:03:37 +03:00
if ( max_n ! = NULL & & max_n - > predot & &
max_n - > predot < = n ) {
2004-10-02 05:43:43 +04:00
return null_match ( p ) ;
}
2019-05-24 17:03:37 +03:00
if ( max_n ! = NULL & & max_n - > postdot & &
max_n - > postdot < = n & & n < = ldot ) {
2003-08-13 05:53:07 +04:00
return - 1 ;
}
2004-10-08 12:13:00 +04:00
for ( i = 0 ; n [ i ] ; i + = size_n ) {
2008-10-24 18:00:43 +04:00
next_codepoint ( n + i , & size_n ) ;
2016-10-25 12:53:53 +03:00
if ( ms_fnmatch_core ( p , n + i , max_n + 1 , ldot , is_case_sensitive ) = = 0 ) return 0 ;
2004-10-02 05:43:43 +04:00
if ( n + i = = ldot ) {
2016-10-25 12:53:53 +03:00
if ( ms_fnmatch_core ( p , n + i + size_n , max_n + 1 , ldot , is_case_sensitive ) = = 0 ) return 0 ;
2019-05-24 17:03:37 +03:00
if ( max_n ! = NULL ) {
if ( ! max_n - > postdot | |
max_n - > postdot > n ) {
max_n - > postdot = n ;
}
}
2004-10-02 05:43:43 +04:00
return - 1 ;
}
2004-09-03 21:44:07 +04:00
}
2019-05-24 17:03:37 +03:00
if ( max_n ! = NULL & & ( ! max_n - > predot | |
max_n - > predot > n ) ) {
max_n - > predot = n ;
}
2004-10-02 05:43:43 +04:00
return null_match ( p ) ;
2004-10-08 12:13:00 +04:00
case ' ? ' :
2004-10-02 05:43:43 +04:00
/* a '?' matches any single character */
if ( ! * n ) {
return - 1 ;
2003-08-13 05:53:07 +04:00
}
2008-10-24 18:00:43 +04:00
next_codepoint ( n , & size_n ) ;
2004-10-08 12:13:00 +04:00
n + = size_n ;
2003-08-13 05:53:07 +04:00
break ;
2004-10-08 12:13:00 +04:00
case ' > ' :
/* a '?' matches any single character, but
treats ' . ' specially */
if ( n [ 0 ] = = ' . ' ) {
2004-10-02 05:43:43 +04:00
if ( ! n [ 1 ] & & null_match ( p ) = = 0 ) {
return 0 ;
2003-08-13 05:53:07 +04:00
}
2004-10-02 05:43:43 +04:00
break ;
2003-08-13 05:53:07 +04:00
}
2004-10-02 05:43:43 +04:00
if ( ! * n ) return null_match ( p ) ;
2008-10-24 18:00:43 +04:00
next_codepoint ( n , & size_n ) ;
2004-10-08 12:13:00 +04:00
n + = size_n ;
2003-08-13 05:53:07 +04:00
break ;
2004-10-08 12:13:00 +04:00
case ' " ' :
/* a bit like a soft '.' */
2004-10-02 05:43:43 +04:00
if ( * n = = 0 & & null_match ( p ) = = 0 ) {
return 0 ;
}
2004-10-08 12:13:00 +04:00
if ( * n ! = ' . ' ) return - 1 ;
2008-10-24 18:00:43 +04:00
next_codepoint ( n , & size_n ) ;
2004-10-08 12:13:00 +04:00
n + = size_n ;
2003-08-13 05:53:07 +04:00
break ;
default :
2008-10-24 18:00:43 +04:00
c2 = next_codepoint ( n , & size_n ) ;
2016-10-25 12:53:53 +03:00
if ( c ! = c2 ) {
if ( is_case_sensitive ) {
return - 1 ;
}
if ( codepoint_cmpi ( c , c2 ) ! = 0 ) {
return - 1 ;
}
2004-10-02 05:43:43 +04:00
}
2004-10-08 12:13:00 +04:00
n + = size_n ;
2004-10-02 05:43:43 +04:00
break ;
2003-08-13 05:53:07 +04:00
}
}
2004-10-02 05:43:43 +04:00
if ( ! * n ) {
return 0 ;
}
2003-08-13 05:53:07 +04:00
return - 1 ;
}
2016-10-25 12:53:53 +03:00
int ms_fnmatch_protocol ( const char * pattern , const char * string , int protocol ,
bool is_case_sensitive )
2003-08-13 05:53:07 +04:00
{
2017-10-26 10:47:57 +03:00
int ret = - 1 ;
size_t count , i ;
2004-10-02 05:43:43 +04:00
if ( strcmp ( string , " .. " ) = = 0 ) {
string = " . " ;
}
2003-08-13 05:53:07 +04:00
2004-10-02 09:09:16 +04:00
if ( strpbrk ( pattern , " <>*? \" " ) = = NULL ) {
2006-02-23 23:56:10 +03:00
/* this is not just an optimisation - it is essential
2004-10-02 09:09:16 +04:00
for LANMAN1 correctness */
2005-08-30 15:55:05 +04:00
return strcasecmp_m ( pattern , string ) ;
2004-10-02 09:09:16 +04:00
}
2004-10-02 05:43:43 +04:00
if ( protocol < = PROTOCOL_LANMAN2 ) {
2004-10-08 12:13:00 +04:00
char * p = talloc_strdup ( NULL , pattern ) ;
if ( p = = NULL ) {
return - 1 ;
}
2004-10-02 05:43:43 +04:00
/*
for older negotiated protocols it is possible to
translate the pattern to produce a " new style "
pattern that exactly matches w2k behaviour
*/
for ( i = 0 ; p [ i ] ; i + + ) {
2004-10-08 12:13:00 +04:00
if ( p [ i ] = = ' ? ' ) {
p [ i ] = ' > ' ;
} else if ( p [ i ] = = ' . ' & &
( p [ i + 1 ] = = ' ? ' | |
p [ i + 1 ] = = ' * ' | |
2004-10-02 05:43:43 +04:00
p [ i + 1 ] = = 0 ) ) {
2004-10-08 12:13:00 +04:00
p [ i ] = ' " ' ;
} else if ( p [ i ] = = ' * ' & &
p [ i + 1 ] = = ' . ' ) {
p [ i ] = ' < ' ;
2004-10-02 05:43:43 +04:00
}
}
2016-10-25 12:53:53 +03:00
ret = ms_fnmatch_protocol ( p , string , PROTOCOL_NT1 ,
is_case_sensitive ) ;
2004-10-08 12:13:00 +04:00
talloc_free ( p ) ;
return ret ;
2004-10-02 05:43:43 +04:00
}
2004-10-08 12:13:00 +04:00
for ( count = i = 0 ; pattern [ i ] ; i + + ) {
if ( pattern [ i ] = = ' * ' | | pattern [ i ] = = ' < ' ) count + + ;
2004-10-02 05:43:43 +04:00
}
2017-10-26 10:47:57 +03:00
/* If the pattern includes '*' or '<' */
if ( count > = 1 ) {
2016-10-25 13:46:00 +03:00
struct max_n max_n [ count ] ;
2004-10-02 05:43:43 +04:00
2016-10-25 13:46:00 +03:00
memset ( max_n , 0 , sizeof ( struct max_n ) * count ) ;
2004-10-02 05:43:43 +04:00
2016-10-25 13:46:00 +03:00
ret = ms_fnmatch_core ( pattern , string , max_n , strrchr ( string , ' . ' ) ,
is_case_sensitive ) ;
2017-10-26 10:47:57 +03:00
} else {
ret = ms_fnmatch_core ( pattern , string , NULL , strrchr ( string , ' . ' ) ,
is_case_sensitive ) ;
2016-10-25 13:46:00 +03:00
}
2004-10-02 05:43:43 +04:00
2003-08-13 05:53:07 +04:00
return ret ;
}
2004-10-02 05:43:43 +04:00
2006-02-28 16:12:39 +03:00
/** a generic fnmatch function - uses for non-CIFS pattern matching */
2003-08-13 05:53:07 +04:00
int gen_fnmatch ( const char * pattern , const char * string )
{
2016-10-25 12:53:53 +03:00
return ms_fnmatch_protocol ( pattern , string , PROTOCOL_NT1 , false ) ;
2003-08-13 05:53:07 +04:00
}