]> git.ipfire.org Git - thirdparty/cups.git/blame - cgi-bin/search.c
More Digest authentication fixes/improvements.
[thirdparty/cups.git] / cgi-bin / search.c
CommitLineData
ef416fc2 1/*
7e86f2f6 2 * Search routines for CUPS.
ef416fc2 3 *
3416fe95 4 * Copyright 2007-2018 by Apple Inc.
7e86f2f6 5 * Copyright 1997-2006 by Easy Software Products.
ef416fc2 6 *
3416fe95
MS
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more
8 * information.
ef416fc2 9 */
10
11/*
12 * Include necessary headers...
13 */
14
15#include "cgi-private.h"
16#include <regex.h>
17
18
19/*
20 * 'cgiCompileSearch()' - Compile a search string.
21 */
22
23void * /* O - Search context */
24cgiCompileSearch(const char *query) /* I - Query string */
25{
26 regex_t *re; /* Regular expression */
27 char *s, /* Regular expression string */
28 *sptr, /* Pointer into RE string */
29 *sword; /* Pointer to start of word */
7e86f2f6 30 size_t slen; /* Allocated size of RE string */
ef416fc2 31 const char *qptr, /* Pointer into query string */
32 *qend; /* End of current word */
33 const char *prefix; /* Prefix to add to next word */
34 int quoted; /* Word is quoted */
7e86f2f6 35 size_t wlen; /* Word length */
ef416fc2 36 char *lword; /* Last word in query */
37
38
1f0275e3
MS
39 DEBUG_printf(("cgiCompileSearch(query=\"%s\")\n", query));
40
41 /*
42 * Range check input...
43 */
44
45 if (!query)
46 return (NULL);
ef416fc2 47
48 /*
49 * Allocate a regular expression storage structure...
50 */
51
91c84a35
MS
52 if ((re = (regex_t *)calloc(1, sizeof(regex_t))) == NULL)
53 return (NULL);
ef416fc2 54
55 /*
56 * Allocate a buffer to hold the regular expression string, starting
57 * at 1024 bytes or 3 times the length of the query string, whichever
58 * is greater. We'll expand the string as needed...
59 */
60
61 slen = strlen(query) * 3;
62 if (slen < 1024)
63 slen = 1024;
64
91c84a35
MS
65 if ((s = (char *)malloc(slen)) == NULL)
66 {
67 free(re);
68 return (NULL);
69 }
ef416fc2 70
71 /*
72 * Copy the query string to the regular expression, handling basic
73 * AND and OR logic...
74 */
75
76 prefix = ".*";
77 qptr = query;
78 sptr = s;
79 lword = NULL;
80
81 while (*qptr)
82 {
83 /*
84 * Skip leading whitespace...
85 */
86
87 while (isspace(*qptr & 255))
88 qptr ++;
89
90 if (!*qptr)
91 break;
92
93 /*
94 * Find the end of the current word...
95 */
96
97 if (*qptr == '\"' || *qptr == '\'')
98 {
99 /*
100 * Scan quoted string...
101 */
102
103 quoted = *qptr ++;
104 for (qend = qptr; *qend && *qend != quoted; qend ++);
105
106 if (!*qend)
107 {
108 /*
109 * No closing quote, error out!
110 */
111
112 free(s);
113 free(re);
114
115 if (lword)
116 free(lword);
117
118 return (NULL);
119 }
120 }
121 else
122 {
123 /*
124 * Scan whitespace-delimited string...
125 */
126
127 quoted = 0;
128 for (qend = qptr + 1; *qend && !isspace(*qend); qend ++);
129 }
130
7e86f2f6 131 wlen = (size_t)(qend - qptr);
ef416fc2 132
133 /*
134 * Look for logic words: AND, OR
135 */
136
88f9aafc 137 if (wlen == 3 && !_cups_strncasecmp(qptr, "AND", 3))
ef416fc2 138 {
139 /*
140 * Logical AND with the following text...
141 */
142
143 if (sptr > s)
144 prefix = ".*";
145
146 qptr = qend;
147 }
88f9aafc 148 else if (wlen == 2 && !_cups_strncasecmp(qptr, "OR", 2))
ef416fc2 149 {
150 /*
151 * Logical OR with the following text...
152 */
153
154 if (sptr > s)
155 prefix = ".*|.*";
156
157 qptr = qend;
158 }
159 else
160 {
161 /*
162 * Add a search word, making sure we have enough room for the
163 * string + RE overhead...
164 */
165
7e86f2f6 166 wlen = (size_t)(sptr - s) + 2 * 4 * wlen + 2 * strlen(prefix) + 11;
839a51c8
MS
167 if (lword)
168 wlen += strlen(lword);
ef416fc2 169
170 if (wlen > slen)
171 {
172 /*
173 * Expand the RE string buffer...
174 */
175
176 char *temp; /* Temporary string pointer */
177
178
179 slen = wlen + 128;
180 temp = (char *)realloc(s, slen);
181 if (!temp)
182 {
183 free(s);
184 free(re);
185
186 if (lword)
187 free(lword);
188
189 return (NULL);
190 }
191
192 sptr = temp + (sptr - s);
193 s = temp;
194 }
195
196 /*
197 * Add the prefix string...
198 */
199
5a9febac 200 memcpy(sptr, prefix, strlen(prefix) + 1);
ef416fc2 201 sptr += strlen(sptr);
202
203 /*
204 * Then quote the remaining word characters as needed for the
205 * RE...
206 */
207
208 sword = sptr;
209
210 while (qptr < qend)
211 {
212 /*
213 * Quote: ^ . [ $ ( ) | * + ? { \
214 */
215
216 if (strchr("^.[$()|*+?{\\", *qptr))
217 *sptr++ = '\\';
218
219 *sptr++ = *qptr++;
220 }
221
07725fee 222 *sptr = '\0';
223
ef416fc2 224 /*
225 * For "word1 AND word2", add reciprocal "word2 AND word1"...
226 */
227
228 if (!strcmp(prefix, ".*") && lword)
229 {
230 char *lword2; /* New "last word" */
231
232
91c84a35
MS
233 if ((lword2 = strdup(sword)) == NULL)
234 {
235 free(lword);
236 free(s);
237 free(re);
238 return (NULL);
239 }
ef416fc2 240
5a9febac 241 memcpy(sptr, ".*|.*", 6);
ef416fc2 242 sptr += 5;
243
5a9febac 244 memcpy(sptr, lword2, strlen(lword2) + 1);
ef416fc2 245 sptr += strlen(sptr);
246
5a9febac 247 memcpy(sptr, ".*", 3);
ef416fc2 248 sptr += 2;
249
5a9febac 250 memcpy(sptr, lword, strlen(lword) + 1);
ef416fc2 251 sptr += strlen(sptr);
252
253 free(lword);
254 lword = lword2;
255 }
256 else
257 {
258 if (lword)
259 free(lword);
260
261 lword = strdup(sword);
262 }
263
264 prefix = ".*|.*";
265 }
266
267 /*
268 * Advance to the next string...
269 */
270
271 if (quoted)
272 qptr ++;
273 }
274
275 if (lword)
276 free(lword);
277
278 if (sptr > s)
5a9febac 279 memcpy(sptr, ".*", 3);
ef416fc2 280 else
281 {
282 /*
283 * No query data, return NULL...
284 */
285
286 free(s);
287 free(re);
288
289 return (NULL);
290 }
291
292 /*
293 * Compile the regular expression...
294 */
295
296 DEBUG_printf((" s=\"%s\"\n", s));
297
298 if (regcomp(re, s, REG_EXTENDED | REG_ICASE))
299 {
300 free(re);
301 free(s);
302
303 return (NULL);
304 }
305
306 /*
307 * Free the RE string and return the new regular expression we compiled...
308 */
309
310 free(s);
311
312 return ((void *)re);
313}
314
315
316/*
317 * 'cgiDoSearch()' - Do a search of some text.
318 */
319
320int /* O - Number of matches */
321cgiDoSearch(void *search, /* I - Search context */
322 const char *text) /* I - Text to search */
323{
324 int i; /* Looping var */
325 regmatch_t matches[100]; /* RE matches */
326
327
328 /*
329 * Range check...
330 */
331
332 if (!search || !text)
333 return (0);
334
335 /*
336 * Do a lookup...
337 */
338
339 if (!regexec((regex_t *)search, text, sizeof(matches) / sizeof(matches[0]),
340 matches, 0))
341 {
342 /*
343 * Figure out the number of matches in the string...
344 */
345
346 for (i = 0; i < (int)(sizeof(matches) / sizeof(matches[0])); i ++)
347 if (matches[i].rm_so < 0)
348 break;
349
350 return (i);
351 }
352 else
353 return (0);
354}
355
356
357/*
358 * 'cgiFreeSearch()' - Free a compiled search context.
359 */
360
361void
362cgiFreeSearch(void *search) /* I - Search context */
363{
364 regfree((regex_t *)search);
3416fe95 365 free(search);
ef416fc2 366}