/*
  * 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);
  }
Example #9
0
 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");
  }