1/*
2   Unix SMB/CIFS implementation.
3   filename matching routine
4   Copyright (C) Andrew Tridgell 1992-2004
5
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2 of the License, or
9   (at your option) any later version.
10
11   This program is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with this program; if not, write to the Free Software
18   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19*/
20
21/*
22   This module was originally based on fnmatch.c copyright by the Free
23   Software Foundation. It bears little (if any) resemblence to that
24   code now
25*/
26
27
28#include "includes.h"
29
30static int null_match(const smb_ucs2_t *p)
31{
32	for (;*p;p++) {
33		if (*p != UCS2_CHAR('*') &&
34		    *p != UCS2_CHAR('<') &&
35		    *p != UCS2_CHAR('"') &&
36		    *p != UCS2_CHAR('>')) return -1;
37	}
38	return 0;
39}
40
41/*
42  the max_n structure is purely for efficiency, it doesn't contribute
43  to the matching algorithm except by ensuring that the algorithm does
44  not grow exponentially
45*/
46struct max_n {
47	const smb_ucs2_t *predot;
48	const smb_ucs2_t *postdot;
49};
50
51
52/*
53  p and n are the pattern and string being matched. The max_n array is
54  an optimisation only. The ldot pointer is NULL if the string does
55  not contain a '.', otherwise it points at the last dot in 'n'.
56*/
57static int ms_fnmatch_core(const smb_ucs2_t *p, const smb_ucs2_t *n,
58			   struct max_n *max_n, const smb_ucs2_t *ldot,
59			   BOOL is_case_sensitive)
60{
61	smb_ucs2_t c;
62	int i;
63
64	while ((c = *p++)) {
65		switch (c) {
66			/* a '*' matches zero or more characters of any type */
67		case UCS2_CHAR('*'):
68			if (max_n->predot && max_n->predot <= n) {
69				return null_match(p);
70			}
71			for (i=0; n[i]; i++) {
72				if (ms_fnmatch_core(p, n+i, max_n+1, ldot, is_case_sensitive) == 0) {
73					return 0;
74				}
75			}
76			if (!max_n->predot || max_n->predot > n) max_n->predot = n;
77			return null_match(p);
78
79			/* a '<' matches zero or more characters of
80			   any type, but stops matching at the last
81			   '.' in the string. */
82		case UCS2_CHAR('<'):
83			if (max_n->predot && max_n->predot <= n) {
84				return null_match(p);
85			}
86			if (max_n->postdot && max_n->postdot <= n && n <= ldot) {
87				return -1;
88			}
89			for (i=0; n[i]; i++) {
90				if (ms_fnmatch_core(p, n+i, max_n+1, ldot, is_case_sensitive) == 0) return 0;
91				if (n+i == ldot) {
92					if (ms_fnmatch_core(p, n+i+1, max_n+1, ldot, is_case_sensitive) == 0) return 0;
93					if (!max_n->postdot || max_n->postdot > n) max_n->postdot = n;
94					return -1;
95				}
96			}
97			if (!max_n->predot || max_n->predot > n) max_n->predot = n;
98			return null_match(p);
99
100			/* a '?' matches any single character */
101		case UCS2_CHAR('?'):
102			if (! *n) {
103				return -1;
104			}
105			n++;
106			break;
107
108			/* a '?' matches any single character */
109		case UCS2_CHAR('>'):
110			if (n[0] == UCS2_CHAR('.')) {
111				if (! n[1] && null_match(p) == 0) {
112					return 0;
113				}
114				break;
115			}
116			if (! *n) return null_match(p);
117			n++;
118			break;
119
120		case UCS2_CHAR('"'):
121			if (*n == 0 && null_match(p) == 0) {
122				return 0;
123			}
124			if (*n != UCS2_CHAR('.')) return -1;
125			n++;
126			break;
127
128		default:
129			if (c != *n) {
130				if (is_case_sensitive) {
131					return -1;
132				}
133				if (toupper_w(c) != toupper_w(*n)) {
134					return -1;
135				}
136			}
137			n++;
138			break;
139		}
140	}
141
142	if (! *n) {
143		return 0;
144	}
145
146	return -1;
147}
148
149int ms_fnmatch(const char *pattern, const char *string, enum protocol_types protocol,
150	       BOOL is_case_sensitive)
151{
152	wpstring p, s;
153	int ret, count, i;
154	struct max_n *max_n = NULL;
155
156	if (strcmp(string, "..") == 0) {
157		string = ".";
158	}
159
160	if (strpbrk(pattern, "<>*?\"") == NULL) {
161		/* this is not just an optmisation - it is essential
162		   for LANMAN1 correctness */
163		if (is_case_sensitive) {
164			return strcmp(pattern, string);
165		} else {
166			return StrCaseCmp(pattern, string);
167		}
168	}
169
170	if (push_ucs2(NULL, p, pattern, sizeof(p), STR_TERMINATE) == (size_t)-1) {
171		/* Not quite the right answer, but finding the right one
172		  under this failure case is expensive, and it's pretty close */
173		return -1;
174	}
175
176	if (push_ucs2(NULL, s, string, sizeof(s), STR_TERMINATE) == (size_t)-1) {
177		/* Not quite the right answer, but finding the right one
178		   under this failure case is expensive, and it's pretty close */
179		return -1;
180	}
181
182	if (protocol <= PROTOCOL_LANMAN2) {
183		/*
184		  for older negotiated protocols it is possible to
185		  translate the pattern to produce a "new style"
186		  pattern that exactly matches w2k behaviour
187		*/
188		for (i=0;p[i];i++) {
189			if (p[i] == UCS2_CHAR('?')) {
190				p[i] = UCS2_CHAR('>');
191			} else if (p[i] == UCS2_CHAR('.') &&
192				   (p[i+1] == UCS2_CHAR('?') ||
193				    p[i+1] == UCS2_CHAR('*') ||
194				    p[i+1] == 0)) {
195				p[i] = UCS2_CHAR('"');
196			} else if (p[i] == UCS2_CHAR('*') && p[i+1] == UCS2_CHAR('.')) {
197				p[i] = UCS2_CHAR('<');
198			}
199		}
200	}
201
202	for (count=i=0;p[i];i++) {
203		if (p[i] == UCS2_CHAR('*') || p[i] == UCS2_CHAR('<')) count++;
204	}
205
206	if (count != 0) {
207		max_n = SMB_CALLOC_ARRAY(struct max_n, count);
208		if (!max_n) {
209			return -1;
210		}
211	}
212
213	ret = ms_fnmatch_core(p, s, max_n, strrchr_w(s, UCS2_CHAR('.')), is_case_sensitive);
214
215	if (max_n) {
216		free(max_n);
217	}
218
219	return ret;
220}
221
222
223/* a generic fnmatch function - uses for non-CIFS pattern matching */
224int gen_fnmatch(const char *pattern, const char *string)
225{
226	return ms_fnmatch(pattern, string, PROTOCOL_NT1, False);
227}
228