1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_ucb.hxx"
26
27 #include <limits>
28 #include <com/sun/star/ucb/RuleOperator.hpp>
29 #include <com/sun/star/ucb/SearchInfo.hpp>
30 #include <com/sun/star/util/Date.hpp>
31 #include <tools/date.hxx>
32 #include <tools/inetmime.hxx>
33 #include <tools/string.hxx>
34
35 #ifndef CHAOS_UCBDEMO_SRCHARG_HXX
36 #include <srcharg.hxx>
37 #endif
38
39 namespace unnamed_chaos_ucbdemo_srcharg {}
40 using namespace unnamed_chaos_ucbdemo_srcharg;
41 // unnamed namespaces don't work well yet...
42
43 using namespace com::sun::star;
44
45 //============================================================================
46 //
47 // skipWhiteSpace
48 //
49 //============================================================================
50
51 namespace unnamed_chaos_ucbdemo_srcharg {
52
skipWhiteSpace(sal_Unicode const * & rBegin,sal_Unicode const * pEnd)53 void skipWhiteSpace(sal_Unicode const *& rBegin, sal_Unicode const * pEnd)
54 {
55 while (rBegin != pEnd
56 && (*rBegin == '\n' || *rBegin == '\t' || *rBegin == ' '))
57 ++rBegin;
58 }
59
60 //============================================================================
61 //
62 // scanAtom
63 //
64 //============================================================================
65
scanAtom(sal_Unicode const * & rBegin,sal_Unicode const * pEnd)66 String scanAtom(sal_Unicode const *& rBegin, sal_Unicode const * pEnd)
67 {
68 sal_Unicode const * pTheBegin = rBegin;
69 while (rBegin != pEnd && INetMIME::isAlpha(*rBegin))
70 ++rBegin;
71 return String(pTheBegin, rBegin - pTheBegin);
72 }
73
74 //============================================================================
75 //
76 // scanProperty
77 //
78 //============================================================================
79
scanProperty(sal_Unicode const * & rBegin,sal_Unicode const * pEnd)80 String scanProperty(sal_Unicode const *& rBegin, sal_Unicode const * pEnd)
81 {
82 sal_Unicode const * pTheBegin = rBegin;
83 while (rBegin != pEnd
84 && !(*rBegin == '\n' || *rBegin == '\t' || *rBegin == ' '))
85 ++rBegin;
86 return String(pTheBegin, rBegin - pTheBegin);
87 }
88
89 //============================================================================
90 //
91 // scanOperator
92 //
93 //============================================================================
94
scanOperator(sal_Unicode const * & rBegin,sal_Unicode const * pEnd)95 String scanOperator(sal_Unicode const *& rBegin, sal_Unicode const * pEnd)
96 {
97 sal_Unicode const * pTheBegin = rBegin;
98 while (rBegin != pEnd
99 && (INetMIME::isAlpha(*rBegin) || *rBegin == '!'
100 || *rBegin >= '<' && *rBegin <= '>'))
101 ++rBegin;
102 return String(pTheBegin, rBegin - pTheBegin);
103 }
104
105 }
106
107 //============================================================================
108 //
109 // parseSearchArgument
110 //
111 //============================================================================
112
parseSearchArgument(String const & rInput,ucb::SearchInfo & rInfo)113 bool parseSearchArgument(String const & rInput, ucb::SearchInfo & rInfo)
114 {
115 /* Format of rInput:
116
117 argument = *option [criterium *("OR" criterium)]
118
119 option = ("--RECURSE" "=" ("NONE" / "ONE" / "DEEP"))
120 / (("--BASE" / "--FOLDERVIEW" / "--DOCVIEW"
121 / "--INDIRECT")
122 "=" bool)
123
124 criterium = "EMPTY" / (term *("AND" term))
125
126 term = text-term / date-term / numeric-term / bool-term
127
128 text-term = property ("CONT" / "!CONT" / ">=" / "<=" / "==" / "!=")
129 string *("-C" / "-R")
130
131 date-term = property
132 (((">=" / "<=" / "==" / "!=") date)
133 / (("OLDER" / "YOUNGER") number))
134
135 numeric-term = property (">=" / "<=" / "==" / "!=") number
136
137 bool-term = property ("TRUE" / "FALSE")
138
139 property = 1*VCHAR
140
141 string = DQUOTE
142 *(<any Unicode code point except DQUOTE or "\">
143 / ("\" %x75 4HEXDIG) ; \uHHHH
144 / ("\" (DQUOTE / "\")))
145 DQUOTE
146
147 date = 1*2DIGIT "/" 1*2DIGIT "/" 4DIGIT ; mm/dd/yyyy
148
149 number = ["+" / "-"] 1*DIGIT
150 */
151
152 sal_Unicode const * p = rInput.GetBuffer();
153 sal_Unicode const * pEnd = p + rInput.Len();
154
155 // Parse options:
156 rInfo.Recursion = ucb::SearchRecursion_ONE_LEVEL;
157 rInfo.IncludeBase = true;
158 rInfo.RespectFolderViewRestrictions = true;
159 rInfo.RespectDocViewRestrictions = false;
160 rInfo.FollowIndirections = false;
161 enum OptionID { OPT_RECURSE, OPT_BASE, OPT_FOLDERVIEW, OPT_DOCVIEW,
162 OPT_INDIRECT, OPT_Count };
163 struct OptionInfo
164 {
165 bool m_bSpecified;
166 sal_Bool * m_pValue;
167 };
168 OptionInfo aOptions[OPT_Count];
169 aOptions[OPT_RECURSE].m_bSpecified = false;
170 aOptions[OPT_RECURSE].m_pValue = 0;
171 aOptions[OPT_BASE].m_bSpecified = false;
172 aOptions[OPT_BASE].m_pValue = &rInfo.IncludeBase;
173 aOptions[OPT_FOLDERVIEW].m_bSpecified = false;
174 aOptions[OPT_FOLDERVIEW].m_pValue
175 = &rInfo.RespectFolderViewRestrictions;
176 aOptions[OPT_DOCVIEW].m_bSpecified = false;
177 aOptions[OPT_DOCVIEW].m_pValue = &rInfo.RespectDocViewRestrictions;
178 aOptions[OPT_INDIRECT].m_bSpecified = false;
179 aOptions[OPT_INDIRECT].m_pValue = &rInfo.FollowIndirections;
180 while (p != pEnd)
181 {
182 sal_Unicode const * q = p;
183
184 skipWhiteSpace(q, pEnd);
185 if (pEnd - q < 2 || *q++ != '-' || *q++ != '-')
186 break;
187 String aOption(scanAtom(q, pEnd));
188 OptionID eID;
189 if (aOption.EqualsIgnoreCaseAscii("recurse"))
190 eID = OPT_RECURSE;
191 else if (aOption.EqualsIgnoreCaseAscii("base"))
192 eID = OPT_BASE;
193 else if (aOption.EqualsIgnoreCaseAscii("folderview"))
194 eID = OPT_FOLDERVIEW;
195 else if (aOption.EqualsIgnoreCaseAscii("docview"))
196 eID = OPT_DOCVIEW;
197 else if (aOption.EqualsIgnoreCaseAscii("indirect"))
198 eID = OPT_INDIRECT;
199 else
200 break;
201
202 if (aOptions[eID].m_bSpecified)
203 break;
204 aOptions[eID].m_bSpecified = true;
205
206 skipWhiteSpace(q, pEnd);
207 if (q == pEnd || *q++ != '=')
208 break;
209
210 skipWhiteSpace(q, pEnd);
211 String aValue(scanAtom(q, pEnd));
212 if (eID == OPT_RECURSE)
213 {
214 if (aValue.EqualsIgnoreCaseAscii("none"))
215 rInfo.Recursion = ucb::SearchRecursion_NONE;
216 else if (aValue.EqualsIgnoreCaseAscii("one"))
217 rInfo.Recursion = ucb::SearchRecursion_ONE_LEVEL;
218 else if (aValue.EqualsIgnoreCaseAscii("deep"))
219 rInfo.Recursion = ucb::SearchRecursion_DEEP;
220 else
221 break;
222 }
223 else if (aValue.EqualsIgnoreCaseAscii("true"))
224 *aOptions[eID].m_pValue = true;
225 else if (aValue.EqualsIgnoreCaseAscii("false"))
226 *aOptions[eID].m_pValue = false;
227 else
228 break;
229
230 p = q;
231 }
232
233 // Parse criteria:
234 ucb::SearchCriterium aCriterium;
235 for (;;)
236 {
237 sal_Unicode const * q = p;
238
239 // Parse either property name or "empty":
240 skipWhiteSpace(q, pEnd);
241 String aProperty(scanProperty(q, pEnd));
242 sal_Unicode const * pPropertyEnd = q;
243
244 // Parse operator:
245 skipWhiteSpace(q, pEnd);
246 String aOperator(scanOperator(q, pEnd));
247 struct Operator
248 {
249 sal_Char const * m_pName;
250 sal_Int16 m_nText;
251 sal_Int16 m_nDate;
252 sal_Int16 m_nNumeric;
253 sal_Int16 m_nBool;
254 };
255 static Operator const aOperators[]
256 = { { "cont", ucb::RuleOperator::CONTAINS, 0, 0, 0 },
257 { "!cont", ucb::RuleOperator::CONTAINSNOT, 0, 0, 0 },
258 { ">=", ucb::RuleOperator::GREATEREQUAL,
259 ucb::RuleOperator::GREATEREQUAL,
260 ucb::RuleOperator::GREATEREQUAL, 0 },
261 { "<=", ucb::RuleOperator::LESSEQUAL,
262 ucb::RuleOperator::LESSEQUAL, ucb::RuleOperator::LESSEQUAL,
263 0 },
264 { "==", ucb::RuleOperator::EQUAL, ucb::RuleOperator::EQUAL,
265 ucb::RuleOperator::EQUAL, 0 },
266 { "!=", ucb::RuleOperator::NOTEQUAL,
267 ucb::RuleOperator::NOTEQUAL, ucb::RuleOperator::NOTEQUAL,
268 0 },
269 { "true", 0, 0, 0, ucb::RuleOperator::VALUE_TRUE },
270 { "false", 0, 0, 0, ucb::RuleOperator::VALUE_FALSE } };
271 int const nOperatorCount = sizeof aOperators / sizeof (Operator);
272 Operator const * pTheOperator = 0;
273 for (int i = 0; i < nOperatorCount; ++i)
274 if (aOperator.EqualsIgnoreCaseAscii(aOperators[i].m_pName))
275 {
276 pTheOperator = aOperators + i;
277 break;
278 }
279 bool bTerm = pTheOperator != 0;
280
281 sal_Int16 nOperatorID;
282 uno::Any aTheOperand;
283 bool bCaseSensitive = false;
284 bool bRegularExpression = false;
285 if (bTerm)
286 {
287 skipWhiteSpace(q, pEnd);
288 bool bHasOperand = false;
289
290 // Parse string operand:
291 if (!bHasOperand && pTheOperator->m_nText)
292 {
293 if (q != pEnd && *q == '"')
294 {
295 String aString;
296 for (sal_Unicode const * r = q + 1;;)
297 {
298 if (r == pEnd)
299 break;
300 sal_Unicode c = *r++;
301 if (c == '"')
302 {
303 bHasOperand = true;
304 aTheOperand <<= rtl::OUString(aString);
305 nOperatorID = pTheOperator->m_nText;
306 q = r;
307 break;
308 }
309 if (c == '\\')
310 {
311 if (r == pEnd)
312 break;
313 c = *r++;
314 if (c == 'u')
315 {
316 if (pEnd - r < 4)
317 break;
318 c = 0;
319 bool bBad = false;
320 for (int i = 0; i < 4; ++i)
321 {
322 int nWeight
323 = INetMIME::getHexWeight(*r++);
324 if (nWeight < 0)
325 {
326 bBad = false;
327 break;
328 }
329 c = sal_Unicode(c << 4 | nWeight);
330 }
331 if (bBad)
332 break;
333 }
334 else if (c != '"' && c != '\\')
335 break;
336 }
337 aString += c;
338 }
339 }
340
341 // Parse "-C" and "-R":
342 if (bHasOperand)
343 for (;;)
344 {
345 skipWhiteSpace(q, pEnd);
346 if (pEnd - q >= 2 && q[0] == '-'
347 && (q[1] == 'C' || q[1] == 'c')
348 && !bCaseSensitive)
349 {
350 bCaseSensitive = true;
351 q += 2;
352 }
353 else if (pEnd - q >= 2 && q[0] == '-'
354 && (q[1] == 'R' || q[1] == 'r')
355 && !bRegularExpression)
356 {
357 bRegularExpression = true;
358 q += 2;
359 }
360 else
361 break;
362 }
363 }
364
365 // Parse date operand:
366 if (!bHasOperand && pTheOperator->m_nDate != 0)
367 {
368 sal_Unicode const * r = q;
369 bool bOK = true;
370 USHORT nMonth = 0;
371 if (bOK && r != pEnd && INetMIME::isDigit(*r))
372 nMonth = INetMIME::getWeight(*r++);
373 else
374 bOK = false;
375 if (bOK && r != pEnd && INetMIME::isDigit(*r))
376 nMonth = 10 * nMonth + INetMIME::getWeight(*r++);
377 if (!(bOK && r != pEnd && *r++ == '/'))
378 bOK = false;
379 USHORT nDay = 0;
380 if (bOK && r != pEnd && INetMIME::isDigit(*r))
381 nDay = INetMIME::getWeight(*r++);
382 else
383 bOK = false;
384 if (bOK && r != pEnd && INetMIME::isDigit(*r))
385 nDay = 10 * nDay + INetMIME::getWeight(*r++);
386 if (!(bOK && r != pEnd && *r++ == '/'))
387 bOK = false;
388 USHORT nYear = 0;
389 for (int i = 0; bOK && i < 4; ++i)
390 if (r != pEnd && INetMIME::isDigit(*r))
391 nYear = 10 * nYear + INetMIME::getWeight(*r++);
392 else
393 bOK = false;
394 if (bOK && Date(nDay, nMonth, nYear).IsValid())
395 {
396 bHasOperand = true;
397 aTheOperand <<= util::Date(nDay, nMonth, nYear);
398 nOperatorID = pTheOperator->m_nDate;
399 q = r;
400 }
401 }
402
403 // Parse number operand:
404 if (!bHasOperand && pTheOperator->m_nNumeric != 0)
405 {
406 sal_Unicode const * r = q;
407 bool bNegative = false;
408 if (*r == '+')
409 ++r;
410 else if (*r == '-')
411 {
412 bNegative = true;
413 ++r;
414 }
415 sal_Int64 nNumber = 0;
416 bool bDigits = false;
417 while (r != pEnd && INetMIME::isDigit(*r))
418 {
419 nNumber = 10 * nNumber + INetMIME::getWeight(*r++);
420 if (nNumber > std::numeric_limits< sal_Int32 >::max())
421 {
422 bDigits = false;
423 break;
424 }
425 }
426 if (bDigits)
427 {
428 bHasOperand = true;
429 aTheOperand
430 <<= sal_Int32(bNegative ? -sal_Int32(nNumber) :
431 sal_Int32(nNumber));
432 nOperatorID = pTheOperator->m_nNumeric;
433 q = r;
434 }
435 }
436
437 // Bool operator has no operand:
438 if (!bHasOperand && pTheOperator->m_nBool != 0)
439 {
440 bHasOperand = true;
441 nOperatorID = pTheOperator->m_nBool;
442 }
443
444 bTerm = bHasOperand;
445 }
446
447 bool bEmpty = false;
448 if (bTerm)
449 {
450 aCriterium.Terms.realloc(aCriterium.Terms.getLength() + 1);
451 aCriterium.Terms[aCriterium.Terms.getLength() - 1]
452 = ucb::RuleTerm(aProperty, aTheOperand, nOperatorID,
453 bCaseSensitive, bRegularExpression);
454 }
455 else if (aCriterium.Terms.getLength() == 0
456 && aProperty.EqualsIgnoreCaseAscii("empty"))
457 {
458 bEmpty = true;
459 q = pPropertyEnd;
460 }
461
462 if (!(bTerm || bEmpty))
463 break;
464
465 p = q;
466 skipWhiteSpace(p, pEnd);
467
468 q = p;
469 String aConnection(scanAtom(q, pEnd));
470 if (p == pEnd || aConnection.EqualsIgnoreCaseAscii("or"))
471 {
472 rInfo.Criteria.realloc(rInfo.Criteria.getLength() + 1);
473 rInfo.Criteria[rInfo.Criteria.getLength() - 1] = aCriterium;
474 aCriterium = ucb::SearchCriterium();
475 p = q;
476 }
477 else if (bTerm && aConnection.EqualsIgnoreCaseAscii("and"))
478 p = q;
479 else
480 break;
481 }
482
483 skipWhiteSpace(p, pEnd);
484 return p == pEnd;
485 }
486
487