This file is indexed.

/usr/share/php/Horde/Db/SearchParser.php is in php-horde-db 2.3.1-1ubuntu2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
<?php
/**
 * This class provides a parser which can construct an SQL WHERE clause from a
 * Google-like search expression.
 *
 * Copyright 2004-2016 Horde LLC (http://www.horde.org/)
 *
 * The expression recognizes boolean "AND", "OR", and "NOT" (providing no
 * operator between keywords implies "AND"), like so:
 *
 *   cat and dog
 *   cat or dog
 *   cat and not dog
 *
 * If no operator appears between keywords or quoted strings, "AND" is assumed.
 * A comma can be used instead of "OR":
 *
 *   cat dog
 *   cat, dog
 *   cat not dog
 *
 * The parser recognizes parentheses, so complex expressions can be created:
 *
 *   cat and not (dog or puppy)
 *
 * Quoted strings are also recognized, and are taken as literal keywords:
 *
 *   "cat and dog"
 *
 * Parsing is designed to be as fuzzy as possible, so it shouldn't error unless
 * people search for "AND", "OR", or "NOT" without quoting it or use unbalanced
 * parentheses.
 *
 * @author   Jason M. Felice <jason.m.felice@gmail.com>
 * @license  http://www.horde.org/licenses/bsd
 * @category Horde
 * @package  Db
 */
class Horde_Db_SearchParser
{
    /**
     * Parses a keyword expression.
     *
     * @param string $column  This is the SQL field name the resulting
     *                        expression should test against.
     * @param string $expr    This is the keyword expression we want to parse.
     *
     * @return string  The query expression.
     * @throws Horde_Db_Exception
     */
    public static function parse($column, $expr)
    {
        /* First pass - scan the string for tokens.  Bare words are tokens, or
         * the user can quote strings to have embedded spaces, keywords, or
         * parentheses.  Parentheses can be used for grouping boolean
         * operators, and the boolean operators AND, OR, and NOT are all
         * recognized.
         *
         * The tokens are returned in the $tokens array -- an array of strings.
         * Each string in the array starts with either a `!'  or a `='.  `=' is
         * a bare word or quoted string we are searching for, and `!' indicates
         * a boolean operator or parenthesis.  A token that starts with a '.'
         * indicates a PostgreSQL word boundary search. */
        $tokens = array();
        while (!empty($expr)) {
            $expr = preg_replace('/^\s+/', '', $expr);
            if (empty($expr)) {
                break;
            }
            if (substr($expr,0,1) == '(') {
                $expr = substr($expr, 1);
                $token = '!(';
            } elseif (substr($expr, 0, 1) == ')') {
                $expr = substr($expr, 1);
                $token = '!)';
            } elseif (substr($expr, 0, 1) == ',') {
                $expr = substr($expr, 1);
                $token = '!OR';
            } elseif (preg_match('/^(AND|OR|NOT)([^a-z].*)?$/i', $expr,
                                 $matches)) {
                $token = '!' . Horde_String::upper($matches[1]);
                $expr = substr($expr, strlen($matches[1]));
            } elseif (preg_match('/^"(([^"]|\\[0-7]+|\\[Xx][0-9a-fA-F]+|\\[^Xx0-7])*)"/',
                                 $expr, $matches)) {
                $token = '=' . stripcslashes($matches[1]);
                $expr = substr($expr, strlen($matches[0]));
            } elseif (preg_match('/^[^\\s\\(\\),]+/', $expr, $matches)) {
                $token = '=' . $matches[0];
                $expr = substr($expr,strlen($token)-1);
            } else {
                throw new Horde_Db_Exception('Syntax error in search terms');
            }
            if ($token == '!AND') {
                /* !AND is implied by concatenation. */
                continue;
            }
            $tokens[] = $token;
        }

        /* Call the expression parser. */
        return self::_parseKeywords1($column, $tokens);
    }

    protected static function _parseKeywords1($column, &$tokens)
    {
        if (count($tokens) == 0) {
            throw new Horde_Db_Exception('Empty search terms');
        }
        $lhs = self::_parseKeywords2($column, $tokens);
        if (count($tokens) == 0 || $tokens[0] != '!OR') {
            return $lhs;
        }
        array_shift($tokens);
        $rhs = self::_parseKeywords1($column, $tokens);
        return "($lhs OR $rhs)";
    }

    protected static function _parseKeywords2($column, &$tokens)
    {
        $lhs = self::_parseKeywords3($column, $tokens);
        if (sizeof($tokens) == 0 || $tokens[0] == '!)' || $tokens[0] == '!OR') {
            return $lhs;
        }
        $rhs = self::_parseKeywords2($column, $tokens);
        return "($lhs AND $rhs)";
    }

    protected static function _parseKeywords3($column, &$tokens)
    {
        if ($tokens[0] == '!NOT') {
            array_shift($tokens);
            $lhs = self::_parseKeywords4($column, $tokens);
            if (is_a($lhs, 'PEAR_Error')) {
                return $lhs;
            }
            return "(NOT $lhs)";
        }
        return self::_parseKeywords4($column, $tokens);
    }

    protected static function _parseKeywords4($column, &$tokens)
    {
        if ($tokens[0] == '!(') {
            array_shift($tokens);
            $lhs = self::_parseKeywords1($column, $tokens);
            if (sizeof($tokens) == 0 || $tokens[0] != '!)') {
                throw new Horde_Db_Exception('Expected ")"');
            }
            array_shift($tokens);
            return $lhs;
        }

        if (substr($tokens[0], 0, 1) != '=' &&
            substr($tokens[0], 0, 2) != '=.') {
            throw new Horde_Db_Exception('Expected bare word or quoted search term');
        }

        $val = Horde_String::lower(substr(array_shift($tokens), 1));
        $val = addslashes(ereg_replace("([\\%])", "\\\\1", $val));

        return "(LOWER($column) LIKE '%$val%')";
    }
}