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 3 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, see <http://www.gnu.org/licenses/>.
18*/
19
20/*
21   This module was originally based on fnmatch.c copyright by the Free
22   Software Foundation. It bears little (if any) resemblence to that
23   code now
24*/
25
26/**
27 * @file
28 * @brief MS-style Filename matching
29 */
30
31#include "includes.h"
32
33static int null_match(const char *p)
34{
35	for (;*p;p++) {
36		if (*p != '*' &&
37		    *p != '<' &&
38		    *p != '"' &&
39		    *p != '>') return -1;
40	}
41	return 0;
42}
43
44/*
45  the max_n structure is purely for efficiency, it doesn't contribute
46  to the matching algorithm except by ensuring that the algorithm does
47  not grow exponentially
48*/
49struct max_n {
50	const char *predot;
51	const char *postdot;
52};
53
54
55/*
56  p and n are the pattern and string being matched. The max_n array is
57  an optimisation only. The ldot pointer is NULL if the string does
58  not contain a '.', otherwise it points at the last dot in 'n'.
59*/
60static int ms_fnmatch_core(const char *p, const char *n,
61			   struct max_n *max_n, const char *ldot)
62{
63	codepoint_t c, c2;
64	int i;
65	size_t size, size_n;
66
67	while ((c = next_codepoint(p, &size))) {
68		p += size;
69
70		switch (c) {
71		case '*':
72			/* a '*' matches zero or more characters of any type */
73			if (max_n->predot && max_n->predot <= n) {
74				return null_match(p);
75			}
76			for (i=0; n[i]; i += size_n) {
77				next_codepoint(n+i, &size_n);
78				if (ms_fnmatch_core(p, n+i, max_n+1, ldot) == 0) {
79					return 0;
80				}
81			}
82			if (!max_n->predot || max_n->predot > n) max_n->predot = n;
83			return null_match(p);
84
85		case '<':
86			/* a '<' matches zero or more characters of
87			   any type, but stops matching at the last
88			   '.' in the string. */
89			if (max_n->predot && max_n->predot <= n) {
90				return null_match(p);
91			}
92			if (max_n->postdot && max_n->postdot <= n && n <= ldot) {
93				return -1;
94			}
95			for (i=0; n[i]; i += size_n) {
96				next_codepoint(n+i, &size_n);
97				if (ms_fnmatch_core(p, n+i, max_n+1, ldot) == 0) return 0;
98				if (n+i == ldot) {
99					if (ms_fnmatch_core(p, n+i+size_n, max_n+1, ldot) == 0) return 0;
100					if (!max_n->postdot || max_n->postdot > n) max_n->postdot = n;
101					return -1;
102				}
103			}
104			if (!max_n->predot || max_n->predot > n) max_n->predot = n;
105			return null_match(p);
106
107		case '?':
108			/* a '?' matches any single character */
109			if (! *n) {
110				return -1;
111			}
112			next_codepoint(n, &size_n);
113			n += size_n;
114			break;
115
116		case '>':
117			/* a '?' matches any single character, but
118			   treats '.' specially */
119			if (n[0] == '.') {
120				if (! n[1] && null_match(p) == 0) {
121					return 0;
122				}
123				break;
124			}
125			if (! *n) return null_match(p);
126			next_codepoint(n, &size_n);
127			n += size_n;
128			break;
129
130		case '"':
131			/* a bit like a soft '.' */
132			if (*n == 0 && null_match(p) == 0) {
133				return 0;
134			}
135			if (*n != '.') return -1;
136			next_codepoint(n, &size_n);
137			n += size_n;
138			break;
139
140		default:
141			c2 = next_codepoint(n, &size_n);
142			if (c != c2 && codepoint_cmpi(c, c2) != 0) {
143				return -1;
144			}
145			n += size_n;
146			break;
147		}
148	}
149
150	if (! *n) {
151		return 0;
152	}
153
154	return -1;
155}
156
157int ms_fnmatch(const char *pattern, const char *string, enum protocol_types protocol)
158{
159	int ret, count, i;
160	struct max_n *max_n = NULL;
161
162	if (strcmp(string, "..") == 0) {
163		string = ".";
164	}
165
166	if (strpbrk(pattern, "<>*?\"") == NULL) {
167		/* this is not just an optimisation - it is essential
168		   for LANMAN1 correctness */
169		return strcasecmp_m(pattern, string);
170	}
171
172	if (protocol <= PROTOCOL_LANMAN2) {
173		char *p = talloc_strdup(NULL, pattern);
174		if (p == NULL) {
175			return -1;
176		}
177		/*
178		  for older negotiated protocols it is possible to
179		  translate the pattern to produce a "new style"
180		  pattern that exactly matches w2k behaviour
181		*/
182		for (i=0;p[i];i++) {
183			if (p[i] == '?') {
184				p[i] = '>';
185			} else if (p[i] == '.' &&
186				   (p[i+1] == '?' ||
187				    p[i+1] == '*' ||
188				    p[i+1] == 0)) {
189				p[i] = '"';
190			} else if (p[i] == '*' &&
191				   p[i+1] == '.') {
192				p[i] = '<';
193			}
194		}
195		ret = ms_fnmatch(p, string, PROTOCOL_NT1);
196		talloc_free(p);
197		return ret;
198	}
199
200	for (count=i=0;pattern[i];i++) {
201		if (pattern[i] == '*' || pattern[i] == '<') count++;
202	}
203
204	max_n = talloc_zero_array(NULL, struct max_n, count);
205	if (max_n == NULL) {
206		return -1;
207	}
208
209	ret = ms_fnmatch_core(pattern, string, max_n, strrchr(string, '.'));
210
211	talloc_free(max_n);
212
213	return ret;
214}
215
216
217/** a generic fnmatch function - uses for non-CIFS pattern matching */
218int gen_fnmatch(const char *pattern, const char *string)
219{
220	return ms_fnmatch(pattern, string, PROTOCOL_NT1);
221}
222