/* * Test for String quote(String, String) */ @Test public void testQuoteIfNeeded() { assertEquals("abc", QuotedStringTokenizer.quoteIfNeeded("abc", " ,")); assertEquals("\"a c\"", QuotedStringTokenizer.quoteIfNeeded("a c", " ,")); assertEquals("\"a'c\"", QuotedStringTokenizer.quoteIfNeeded("a'c", " ,")); assertEquals("\"a\\n\\r\\t\"", QuotedStringTokenizer.quote("a\n\r\t")); assertEquals("\"\\u0000\\u001f\"", QuotedStringTokenizer.quote("\u0000\u001f")); }
@Test public void testUnquoteOnly() { assertEquals("abc", QuotedStringTokenizer.unquoteOnly("abc")); assertEquals("a\"c", QuotedStringTokenizer.unquoteOnly("\"a\\\"c\"")); assertEquals("a'c", QuotedStringTokenizer.unquoteOnly("\"a'c\"")); assertEquals("a\\n\\r\\t", QuotedStringTokenizer.unquoteOnly("\"a\\\\n\\\\r\\\\t\"")); assertEquals("ba\\uXXXXaaa", QuotedStringTokenizer.unquoteOnly("\"ba\\\\uXXXXaaa\"")); }
/* * Test for String nextToken() */ @Test public void testTokenizer4() { QuotedStringTokenizer tok = new QuotedStringTokenizer("abc'def,ghi'jkl", ","); tok.setSingle(false); assertEquals("abc'def", tok.nextToken()); assertEquals("ghi'jkl", tok.nextToken()); tok = new QuotedStringTokenizer("abc'def,ghi'jkl", ","); tok.setSingle(true); assertEquals("abcdef,ghijkl", tok.nextToken()); }
/** * When encountering a Content-Disposition line during a multi-part mime file upload, the * filename="..." field can contain '\' characters that do not belong to a proper escaping * sequence, this tests QuotedStringTokenizer to ensure that it preserves those slashes for where * they cannot be escaped. */ @Test public void testNextTokenOnContentDisposition() { String content_disposition = "form-data; name=\"fileup\"; filename=\"Taken on Aug 22 \\ 2012.jpg\""; QuotedStringTokenizer tok = new QuotedStringTokenizer(content_disposition, ";", false, true); assertEquals("form-data", tok.nextToken().trim()); assertEquals("name=\"fileup\"", tok.nextToken().trim()); assertEquals("filename=\"Taken on Aug 22 \\ 2012.jpg\"", tok.nextToken().trim()); }
private void checkTok(QuotedStringTokenizer tok, boolean delim, boolean quotes) { assertTrue(tok.hasMoreElements()); assertTrue(tok.hasMoreTokens()); assertEquals("abc", tok.nextToken()); if (delim) assertEquals(",", tok.nextToken()); if (delim) assertEquals(" ", tok.nextToken()); assertEquals(quotes ? "\"d\\\"'\"" : "d\"'", tok.nextElement()); if (delim) assertEquals(",", tok.nextToken()); assertEquals(quotes ? "'p\\',y'" : "p',y", tok.nextToken()); if (delim) assertEquals(" ", tok.nextToken()); assertEquals("z", tok.nextToken()); assertFalse(tok.hasMoreTokens()); }
@Test public void testQuote() { StringBuffer buf = new StringBuffer(); buf.setLength(0); QuotedStringTokenizer.quote(buf, "abc \n efg"); assertEquals("\"abc \\n efg\"", buf.toString()); buf.setLength(0); QuotedStringTokenizer.quote(buf, "abcefg"); assertEquals("\"abcefg\"", buf.toString()); buf.setLength(0); QuotedStringTokenizer.quote(buf, "abcefg\""); assertEquals("\"abcefg\\\"\"", buf.toString()); }
@Test public void testUnquote() { assertEquals("abc", QuotedStringTokenizer.unquote("abc")); assertEquals("a\"c", QuotedStringTokenizer.unquote("\"a\\\"c\"")); assertEquals("a'c", QuotedStringTokenizer.unquote("\"a'c\"")); assertEquals("a\n\r\t", QuotedStringTokenizer.unquote("\"a\\n\\r\\t\"")); assertEquals("\u0000\u001f ", QuotedStringTokenizer.unquote("\"\u0000\u001f\u0020\"")); assertEquals("\u0000\u001f ", QuotedStringTokenizer.unquote("\"\u0000\u001f\u0020\"")); assertEquals("ab\u001ec", QuotedStringTokenizer.unquote("ab\u001ec")); assertEquals("ab\u001ec", QuotedStringTokenizer.unquote("\"ab\u001ec\"")); }
/* ------------------------------------------------------------ */ private String filenameValue(String nameEqualsValue) { int idx = nameEqualsValue.indexOf('='); String value = nameEqualsValue.substring(idx + 1).trim(); if (value.matches(".??[a-z,A-Z]\\:\\\\[^\\\\].*")) { // incorrectly escaped IE filenames that have the whole path // we just strip any leading & trailing quotes and leave it as is char first = value.charAt(0); if (first == '"' || first == '\'') value = value.substring(1); char last = value.charAt(value.length() - 1); if (last == '"' || last == '\'') value = value.substring(0, value.length() - 1); return value; } else // unquote the string, but allow any backslashes that don't // form a valid escape sequence to remain as many browsers // even on *nix systems will not escape a filename containing // backslashes return QuotedStringTokenizer.unquoteOnly(value, true); }
public static String escape(String name) { if (needsEscape(name)) return QuotedStringTokenizer.quote(name); return name; }
/* ------------------------------------------------------------ */ private String value(String nameEqualsValue) { int idx = nameEqualsValue.indexOf('='); String value = nameEqualsValue.substring(idx + 1).trim(); return QuotedStringTokenizer.unquoteOnly(value); }
/** * Parse, if necessary, the multipart stream. * * @throws IOException * @throws ServletException */ protected void parse() throws IOException, ServletException { // have we already parsed the input? if (_parts != null) return; // initialize long total = 0; // keep running total of size of bytes read from input and throw an exception if exceeds // MultipartConfigElement._maxRequestSize _parts = new MultiMap(); // if its not a multipart request, don't parse it if (_contentType == null || !_contentType.startsWith("multipart/form-data")) return; // sort out the location to which to write the files if (_config.getLocation() == null) _tmpDir = _contextTmpDir; else if ("".equals(_config.getLocation())) _tmpDir = _contextTmpDir; else { File f = new File(_config.getLocation()); if (f.isAbsolute()) _tmpDir = f; else _tmpDir = new File(_contextTmpDir, _config.getLocation()); } if (!_tmpDir.exists()) _tmpDir.mkdirs(); String contentTypeBoundary = ""; if (_contentType.indexOf("boundary=") >= 0) contentTypeBoundary = QuotedStringTokenizer.unquote( value(_contentType.substring(_contentType.indexOf("boundary="))).trim()); String boundary = "--" + contentTypeBoundary; byte[] byteBoundary = (boundary + "--").getBytes(StringUtil.__ISO_8859_1); // Get first boundary String line = ((ReadLineInputStream) _in).readLine(); if (line == null) throw new IOException("Missing content for multipart request"); boolean badFormatLogged = false; line = line.trim(); while (line != null && !line.equals(boundary)) { if (!badFormatLogged) { LOG.warn("Badly formatted multipart request"); badFormatLogged = true; } line = ((ReadLineInputStream) _in).readLine(); line = (line == null ? line : line.trim()); } if (line == null) throw new IOException("Missing initial multi part boundary"); // Read each part boolean lastPart = false; String contentDisposition = null; String contentType = null; String contentTransferEncoding = null; outer: while (!lastPart) { MultiMap headers = new MultiMap(); while (true) { line = ((ReadLineInputStream) _in).readLine(); // No more input if (line == null) break outer; // end of headers: if ("".equals(line)) break; total += line.length(); if (_config.getMaxRequestSize() > 0 && total > _config.getMaxRequestSize()) throw new IllegalStateException( "Request exceeds maxRequestSize (" + _config.getMaxRequestSize() + ")"); // get content-disposition and content-type int c = line.indexOf(':', 0); if (c > 0) { String key = line.substring(0, c).trim().toLowerCase(Locale.ENGLISH); String value = line.substring(c + 1, line.length()).trim(); headers.put(key, value); if (key.equalsIgnoreCase("content-disposition")) contentDisposition = value; if (key.equalsIgnoreCase("content-type")) contentType = value; if (key.equals("content-transfer-encoding")) contentTransferEncoding = value; } } // Extract content-disposition boolean form_data = false; if (contentDisposition == null) { throw new IOException("Missing content-disposition"); } QuotedStringTokenizer tok = new QuotedStringTokenizer(contentDisposition, ";", false, true); String name = null; String filename = null; while (tok.hasMoreTokens()) { String t = tok.nextToken().trim(); String tl = t.toLowerCase(Locale.ENGLISH); if (t.startsWith("form-data")) form_data = true; else if (tl.startsWith("name=")) name = value(t); else if (tl.startsWith("filename=")) filename = filenameValue(t); } // Check disposition if (!form_data) { continue; } // It is valid for reset and submit buttons to have an empty name. // If no name is supplied, the browser skips sending the info for that field. // However, if you supply the empty string as the name, the browser sends the // field, with name as the empty string. So, only continue this loop if we // have not yet seen a name field. if (name == null) { continue; } if ("base64".equalsIgnoreCase(contentTransferEncoding)) { _in = new Base64InputStream(_in); } else if ("quoted-printable".equalsIgnoreCase(contentTransferEncoding)) { _in = new FilterInputStream(_in) { @Override public int read() throws IOException { int c = in.read(); if (c >= 0 && c == '=') { int hi = in.read(); int lo = in.read(); if (hi < 0 || lo < 0) { throw new IOException("Unexpected end to quoted-printable byte"); } char[] chars = new char[] {(char) hi, (char) lo}; c = Integer.parseInt(new String(chars), 16); } return c; } }; } // Have a new Part MultiPart part = new MultiPart(name, filename); part.setHeaders(headers); part.setContentType(contentType); _parts.add(name, part); part.open(); try { int state = -2; int c; boolean cr = false; boolean lf = false; // loop for all lines while (true) { int b = 0; while ((c = (state != -2) ? state : _in.read()) != -1) { total++; if (_config.getMaxRequestSize() > 0 && total > _config.getMaxRequestSize()) throw new IllegalStateException( "Request exceeds maxRequestSize (" + _config.getMaxRequestSize() + ")"); state = -2; // look for CR and/or LF if (c == 13 || c == 10) { if (c == 13) { _in.mark(1); int tmp = _in.read(); if (tmp != 10) _in.reset(); else state = tmp; } break; } // look for boundary if (b >= 0 && b < byteBoundary.length && c == byteBoundary[b]) b++; else { // this is not a boundary if (cr) part.write(13); if (lf) part.write(10); cr = lf = false; if (b > 0) part.write(byteBoundary, 0, b); b = -1; part.write(c); } } // check partial boundary if ((b > 0 && b < byteBoundary.length - 2) || (b == byteBoundary.length - 1)) { if (cr) part.write(13); if (lf) part.write(10); cr = lf = false; part.write(byteBoundary, 0, b); b = -1; } // boundary match if (b > 0 || c == -1) { if (b == byteBoundary.length) lastPart = true; if (state == 10) state = -2; break; } // handle CR LF if (cr) part.write(13); if (lf) part.write(10); cr = (c == 13); lf = (c == 10 || state == 10); if (state == 10) state = -2; } } finally { part.close(); } } if (!lastPart) throw new IOException("Incomplete parts"); }