View | Details | Raw Unified | Return to bug 48627
Collapse All | Expand All

(-)java/org/apache/jasper/compiler/AttributeParser.java (-18 / +38 lines)
Lines 24-30 Link Here
24
 * "\${1+1}". After unquoting, both appear as "${1+1}" but the first should
24
 * "\${1+1}". After unquoting, both appear as "${1+1}" but the first should
25
 * evaluate to "2" and the second to "${1+1}". Literal \, $ and # need special
25
 * evaluate to "2" and the second to "${1+1}". Literal \, $ and # need special
26
 * treatment to ensure there is no ambiguity. The JSP attribute unquoting
26
 * treatment to ensure there is no ambiguity. The JSP attribute unquoting
27
 * covers \\, \", \', \$, \#, %\>, <\%, &apos; and &quot;
27
 * covers \\, \", \', \$, \#, %\&gt;, &lt;\%, &amp;apos; and &amp;quot;
28
 */
28
 */
29
public class AttributeParser {
29
public class AttributeParser {
30
30
Lines 43-55 Link Here
43
     *                      scripting expressions.
43
     *                      scripting expressions.
44
     * @param isELIgnored   Is expression language being ignored on the page
44
     * @param isELIgnored   Is expression language being ignored on the page
45
     *                      where the JSP attribute is defined.
45
     *                      where the JSP attribute is defined.
46
     * @param isDeferredSyntaxAllowedAsLiteral
47
     *                      Are deferred expressions treated as literals?
46
     * @return              An unquoted JSP attribute that, if it contains
48
     * @return              An unquoted JSP attribute that, if it contains
47
     *                      expression language can be safely passed to the EL
49
     *                      expression language can be safely passed to the EL
48
     *                      processor without fear of ambiguity.
50
     *                      processor without fear of ambiguity.
49
     */
51
     */
50
    public static String getUnquoted(String input, char quote,
52
    public static String getUnquoted(String input, char quote,
51
            boolean isELIgnored) {
53
            boolean isELIgnored, boolean isDeferredSyntaxAllowedAsLiteral) {
52
        return (new AttributeParser(input, quote, isELIgnored,
54
        return (new AttributeParser(input, quote, isELIgnored,
55
                isDeferredSyntaxAllowedAsLiteral,
53
                STRICT_QUOTE_ESCAPING)).getUnquoted();
56
                STRICT_QUOTE_ESCAPING)).getUnquoted();
54
    }
57
    }
55
58
Lines 62-76 Link Here
62
     *                      scripting expressions.
65
     *                      scripting expressions.
63
     * @param isELIgnored   Is expression language being ignored on the page
66
     * @param isELIgnored   Is expression language being ignored on the page
64
     *                      where the JSP attribute is defined.
67
     *                      where the JSP attribute is defined.
68
     * @param isDeferredSyntaxAllowedAsLiteral
69
     *                      Are deferred expressions treated as literals?
65
     * @param strict        The value to use for STRICT_QUOTE_ESCAPING.
70
     * @param strict        The value to use for STRICT_QUOTE_ESCAPING.
66
     * @return              An unquoted JSP attribute that, if it contains
71
     * @return              An unquoted JSP attribute that, if it contains
67
     *                      expression language can be safely passed to the EL
72
     *                      expression language can be safely passed to the EL
68
     *                      processor without fear of ambiguity.
73
     *                      processor without fear of ambiguity.
69
     */
74
     */
70
    protected static String getUnquoted(String input, char quote,
75
    protected static String getUnquoted(String input, char quote,
71
            boolean isELIgnored, boolean strict) {
76
            boolean isELIgnored, boolean isDeferredSyntaxAllowedAsLiteral,
77
            boolean strict) {
72
        return (new AttributeParser(input, quote, isELIgnored,
78
        return (new AttributeParser(input, quote, isELIgnored,
73
                strict)).getUnquoted();
79
                isDeferredSyntaxAllowedAsLiteral, strict)).getUnquoted();
74
    }
80
    }
75
81
76
    /* The quoted input string. */
82
    /* The quoted input string. */
Lines 83-88 Link Here
83
     * treated as literals rather than quoted values. */
89
     * treated as literals rather than quoted values. */
84
    private final boolean isELIgnored;
90
    private final boolean isELIgnored;
85
    
91
    
92
    /* Are deferred expression treated as literals */
93
    private final boolean isDeferredSyntaxAllowedAsLiteral;
94
    
86
    /* Overrides the STRICT_QUOTE_ESCAPING. Used for Unit tests only. */
95
    /* Overrides the STRICT_QUOTE_ESCAPING. Used for Unit tests only. */
87
    private final boolean strict;
96
    private final boolean strict;
88
    
97
    
Lines 109-120 Link Here
109
     * @param strict
118
     * @param strict
110
     */
119
     */
111
    private AttributeParser(String input, char quote,
120
    private AttributeParser(String input, char quote,
112
            boolean isELIgnored, boolean strict) {
121
            boolean isELIgnored, boolean isDeferredSyntaxAllowedAsLiteral,
122
            boolean strict) {
113
        this.input = input;
123
        this.input = input;
114
        this.quote = quote;
124
        this.quote = quote;
115
        // If quote is null this is a scriptign expressions and any EL syntax
125
        // If quote is null this is a scriptign expressions and any EL syntax
116
        // should be ignored
126
        // should be ignored
117
        this.isELIgnored = isELIgnored || (quote == 0);
127
        this.isELIgnored = isELIgnored || (quote == 0);
128
        this.isDeferredSyntaxAllowedAsLiteral =
129
            isDeferredSyntaxAllowedAsLiteral;
118
        this.strict = strict;
130
        this.strict = strict;
119
        this.type = getType(input);
131
        this.type = getType(input);
120
        this.size = input.length();
132
        this.size = input.length();
Lines 151-172 Link Here
151
            char ch = nextChar();
163
            char ch = nextChar();
152
            if (!isELIgnored && ch == '\\') {
164
            if (!isELIgnored && ch == '\\') {
153
                if (type == 0) {
165
                if (type == 0) {
154
                    type = '$';
166
                    result.append("\\");
167
                } else {
168
                    result.append(type);
169
                    result.append("{'\\\\'}");
155
                }
170
                }
156
                result.append(type);
157
                result.append("{'\\\\'}");
158
            } else if (!isELIgnored && ch == '$' && lastChEscaped){
171
            } else if (!isELIgnored && ch == '$' && lastChEscaped){
159
                if (type == 0) {
172
                if (type == 0) {
160
                    type = '$';
173
                    result.append("\\$");
174
                } else {
175
                    result.append(type);
176
                    result.append("{'$'}");
161
                }
177
                }
162
                result.append(type);
163
                result.append("{'$'}");
164
            } else if (!isELIgnored && ch == '#' && lastChEscaped){
178
            } else if (!isELIgnored && ch == '#' && lastChEscaped){
179
                // Note if isDeferredSyntaxAllowedAsLiteral==true, \# will
180
                // not be treated as an escape
165
                if (type == 0) {
181
                if (type == 0) {
166
                    type = '$';
182
                    result.append("\\#");
183
                } else {
184
                    result.append(type);
185
                    result.append("{'#'}");
167
                }
186
                }
168
                result.append(type);
169
                result.append("{'#'}");
170
            } else if (ch == type){
187
            } else if (ch == type){
171
                if (i < size) {
188
                if (i < size) {
172
                    char next = input.charAt(i);
189
                    char next = input.charAt(i);
Lines 197-204 Link Here
197
    private void parseEL() {
214
    private void parseEL() {
198
        boolean endEL = false;
215
        boolean endEL = false;
199
        boolean insideLiteral = false;
216
        boolean insideLiteral = false;
217
        char literalQuote = 0;
200
        while (i < size && !endEL) {
218
        while (i < size && !endEL) {
201
            char literalQuote = '\'';
202
            char ch = nextChar();
219
            char ch = nextChar();
203
            if (ch == '\'' || ch == '\"') {
220
            if (ch == '\'' || ch == '\"') {
204
                if (insideLiteral) {
221
                if (insideLiteral) {
Lines 261-267 Link Here
261
        } else if (ch == '\\' && i + 1 < size) {
278
        } else if (ch == '\\' && i + 1 < size) {
262
            ch = input.charAt(i + 1);
279
            ch = input.charAt(i + 1);
263
            if (ch == '\\' || ch == '\"' || ch == '\'' ||
280
            if (ch == '\\' || ch == '\"' || ch == '\'' ||
264
                    (!isELIgnored && (ch == '$' || ch == '#'))) {
281
                    (!isELIgnored &&
282
                            (ch == '$' ||
283
                                    (!isDeferredSyntaxAllowedAsLiteral &&
284
                                            ch == '#')))) {
265
                i += 2;
285
                i += 2;
266
                lastChEscaped = true;
286
                lastChEscaped = true;
267
            } else {
287
            } else {
Lines 311-323 Link Here
311
        int j = 0;
331
        int j = 0;
312
        int len = value.length();
332
        int len = value.length();
313
        char current;
333
        char current;
314
        
334
315
        while (j < len) {
335
        while (j < len) {
316
            current = value.charAt(j);
336
            current = value.charAt(j);
317
            if (current == '\\') {
337
            if (current == '\\') {
318
                // Escape character - skip a character
338
                // Escape character - skip a character
319
                j++;
339
                j++;
320
            } else if (current == '#') {
340
            } else if (current == '#' && !isDeferredSyntaxAllowedAsLiteral) {
321
                if (j < (len -1) && value.charAt(j + 1) == '{') {
341
                if (j < (len -1) && value.charAt(j + 1) == '{') {
322
                    return '#';
342
                    return '#';
323
                }
343
                }
(-)java/org/apache/jasper/compiler/Parser.java (-1 / +2 lines)
Lines 247-253 Link Here
247
                quote = watch.charAt(0);
247
                quote = watch.charAt(0);
248
            }
248
            }
249
            ret = AttributeParser.getUnquoted(reader.getText(start, stop),
249
            ret = AttributeParser.getUnquoted(reader.getText(start, stop),
250
                    quote, pageInfo.isELIgnored());
250
                    quote, pageInfo.isELIgnored(),
251
                    pageInfo.isDeferredSyntaxAllowedAsLiteral());
251
        } catch (IllegalArgumentException iae) {
252
        } catch (IllegalArgumentException iae) {
252
            err.jspError(start, iae.getMessage());
253
            err.jspError(start, iae.getMessage());
253
        }
254
        }
(-)test/org/apache/jasper/compiler/TestAttributeParser.java (-3 / +20 lines)
Lines 134-142 Link Here
134
        // Quoting <% and %>
134
        // Quoting <% and %>
135
        assertEquals("hello <% world", evalAttr("hello <\\% world", '\"'));
135
        assertEquals("hello <% world", evalAttr("hello <\\% world", '\"'));
136
        assertEquals("hello %> world", evalAttr("hello %> world", '\"'));
136
        assertEquals("hello %> world", evalAttr("hello %> world", '\"'));
137
138
        // Test that the end of literal in EL expression is recognized in
139
        // parseEL(), be it quoted with single or double quotes. That is, that
140
        // AttributeParser correctly switches between parseLiteral and parseEL
141
        // methods.
142
        //
143
        // The test is based on the difference in how the '\' character is printed:
144
        // when in parseLiteral \\${ will be printed as ${'\'}${, but if we are still
145
        // inside of parseEL it will be printed as \${, thus preventing the EL
146
        // expression that follows from being evaluated.
147
        //
148
        assertEquals("foo\\bar\\baz", evalAttr("${\'foo\'}\\\\${\'bar\'}\\\\${\'baz\'}", '\"'));
149
        assertEquals("foo\\bar\\baz", evalAttr("${\'foo\'}\\\\${\\\"bar\\\"}\\\\${\'baz\'}", '\"'));
150
        assertEquals("foo\\bar\\baz", evalAttr("${\\\"foo\\\"}\\\\${\'bar\'}\\\\${\\\"baz\\\"}", '\"'));
151
        assertEquals("foo\\bar\\baz", evalAttr("${\"foo\"}\\\\${\\\'bar\\\'}\\\\${\"baz\"}", '\''));
137
    }
152
    }
138
153
139
    public void testScriptExpressiinLiterals() {
154
    public void testScriptExpressionLiterals() {
140
        assertEquals(" \"hello world\" ", parseScriptExpression(
155
        assertEquals(" \"hello world\" ", parseScriptExpression(
141
                " \"hello world\" ", (char) 0));
156
                " \"hello world\" ", (char) 0));
142
        assertEquals(" \"hello \\\"world\" ", parseScriptExpression(
157
        assertEquals(" \"hello \\\"world\" ", parseScriptExpression(
Lines 149-161 Link Here
149
        ctx.setFunctionMapper(new FMapper());
164
        ctx.setFunctionMapper(new FMapper());
150
        ExpressionFactoryImpl exprFactory = new ExpressionFactoryImpl();
165
        ExpressionFactoryImpl exprFactory = new ExpressionFactoryImpl();
151
        ValueExpression ve = exprFactory.createValueExpression(ctx,
166
        ValueExpression ve = exprFactory.createValueExpression(ctx,
152
                AttributeParser.getUnquoted(expression, quote, false, false),
167
                AttributeParser.getUnquoted(expression, quote, false, false,
168
                        false),
153
                String.class);
169
                String.class);
154
        return (String) ve.getValue(ctx);
170
        return (String) ve.getValue(ctx);
155
    }
171
    }
156
    
172
    
157
    private String parseScriptExpression(String expression, char quote) {
173
    private String parseScriptExpression(String expression, char quote) {
158
        return AttributeParser.getUnquoted(expression, quote, false, false);
174
        return AttributeParser.getUnquoted(expression, quote, false, false,
175
                false);
159
    }
176
    }
160
177
161
    public static class FMapper extends FunctionMapper {
178
    public static class FMapper extends FunctionMapper {

Return to bug 48627