blob: b09f56b17ea9b16dadcc2b110f04e53ab46e7fbb [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.olingo.server.core.deserializer.batch;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.olingo.commons.api.http.HttpContentType;
import org.apache.olingo.server.api.batch.exception.BatchDeserializerException;
public class BatchParserCommon {
private static final String REG_EX_BOUNDARY =
"([a-zA-Z0-9_\\-\\.'\\+]{1,70})|\"([a-zA-Z0-9_\\-\\.'\\+\\s\\" +
"(\\),/:=\\?]{1,69}[a-zA-Z0-9_\\-\\.'\\+\\(\\),/:=\\?])\"";
private static final Pattern PATTERN_LAST_CRLF = Pattern.compile("(.*)(\r\n){1}( *)", Pattern.DOTALL);
private static final Pattern PATTERN_HEADER_LINE = Pattern.compile("([a-zA-Z\\-]+):\\s?(.*)\\s*");
private static final String REG_EX_APPLICATION_HTTP = "application/http";
public static final Pattern PATTERN_MULTIPART_BOUNDARY = Pattern.compile("multipart/mixed(.*)",
Pattern.CASE_INSENSITIVE);
public static final Pattern PATTERN_CONTENT_TYPE_APPLICATION_HTTP = Pattern.compile(REG_EX_APPLICATION_HTTP,
Pattern.CASE_INSENSITIVE);
public static final String BINARY_ENCODING = "binary";
public static final String HTTP_CONTENT_ID = "Content-Id";
public static final String HTTP_CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding";
public static final String HTTP_EXPECT = "Expect";
public static final String HTTP_FROM = "From";
public static final String HTTP_MAX_FORWARDS = "Max-Forwards";
public static final String HTTP_RANGE = "Range";
public static final String HTTP_TE = "TE";
public static String getBoundary(final String contentType, final int line) throws BatchDeserializerException {
if (contentType == null) {
throw new BatchDeserializerException("Missing content type",
BatchDeserializerException.MessageKeys.MISSING_CONTENT_TYPE, line);
}
if (contentType.toLowerCase(Locale.ENGLISH).startsWith("multipart/mixed")) {
final String[] parameter = contentType.split(";");
for (final String pair : parameter) {
final String[] attrValue = pair.split("=");
if (attrValue.length == 2 && "boundary".equals(attrValue[0].trim().toLowerCase(Locale.ENGLISH))) {
if (attrValue[1].matches(REG_EX_BOUNDARY)) {
return trimQuota(attrValue[1].trim());
} else {
throw new BatchDeserializerException("Invalid boundary format",
BatchDeserializerException.MessageKeys.INVALID_BOUNDARY, "" + line);
}
}
}
}
throw new BatchDeserializerException("Content type is not multipart mixed",
BatchDeserializerException.MessageKeys.INVALID_CONTENT_TYPE, HttpContentType.MULTIPART_MIXED);
}
public static String removeEndingSlash(String content) {
content = content.trim();
int lastSlashIndex = content.lastIndexOf('/');
return (lastSlashIndex == content.length() - 1) ? content.substring(0, content.length() - 1) : content;
}
private static String trimQuota(String boundary) {
if (boundary.matches("\".*\"")) {
boundary = boundary.replace("\"", "");
}
return boundary;
}
public static List<List<Line>> splitMessageByBoundary(final List<Line> message, final String boundary)
throws BatchDeserializerException {
final List<List<Line>> messageParts = new LinkedList<List<Line>>();
List<Line> currentPart = new ArrayList<Line>();
boolean isEndReached = false;
final String quotedBoundary = Pattern.quote(boundary);
final Pattern boundaryDelimiterPattern = Pattern.compile("--" + quotedBoundary + "--[\\s ]*");
final Pattern boundaryPattern = Pattern.compile("--" + quotedBoundary + "[\\s ]*");
for (Line currentLine : message) {
if (boundaryDelimiterPattern.matcher(currentLine.toString()).matches()) {
removeEndingCRLFFromList(currentPart);
messageParts.add(currentPart);
isEndReached = true;
} else if (boundaryPattern.matcher(currentLine.toString()).matches()) {
removeEndingCRLFFromList(currentPart);
messageParts.add(currentPart);
currentPart = new LinkedList<Line>();
} else {
currentPart.add(currentLine);
}
if (isEndReached) {
break;
}
}
final int lineNumer = (message.size() > 0) ? message.get(0).getLineNumber() : 0;
// Remove preamble
if (messageParts.size() > 0) {
messageParts.remove(0);
}
if (!isEndReached) {
throw new BatchDeserializerException("Missing close boundary delimiter",
BatchDeserializerException.MessageKeys.MISSING_CLOSE_DELIMITER,
"" + lineNumer);
}
return messageParts;
}
private static void removeEndingCRLFFromList(final List<Line> list) {
if (list.size() > 0) {
Line lastLine = list.remove(list.size() - 1);
list.add(removeEndingCRLF(lastLine));
}
}
public static Line removeEndingCRLF(final Line line) {
Pattern pattern = PATTERN_LAST_CRLF;
Matcher matcher = pattern.matcher(line.toString());
if (matcher.matches()) {
return new Line(matcher.group(1), line.getLineNumber());
} else {
return line;
}
}
public static Header consumeHeaders(final List<Line> remainingMessage) {
final int headerLineNumber = remainingMessage.size() != 0 ? remainingMessage.get(0).getLineNumber() : 0;
final Header headers = new Header(headerLineNumber);
final Iterator<Line> iter = remainingMessage.iterator();
Line currentLine;
boolean isHeader = true;
while (iter.hasNext() && isHeader) {
currentLine = iter.next();
final Matcher headerMatcher = PATTERN_HEADER_LINE.matcher(currentLine.toString());
if (headerMatcher.matches() && headerMatcher.groupCount() == 2) {
iter.remove();
String headerName = headerMatcher.group(1).trim();
String headerValue = headerMatcher.group(2).trim();
headers.addHeader(headerName, Header.splitValuesByComma(headerValue), currentLine.getLineNumber());
} else {
isHeader = false;
}
}
return headers;
}
public static void consumeBlankLine(final List<Line> remainingMessage, final boolean isStrict)
throws BatchDeserializerException {
// TODO is \r\n to strict?
if (remainingMessage.size() > 0 && remainingMessage.get(0).toString().matches("\\s*(\r\n|\n)\\s*")) {
remainingMessage.remove(0);
} else {
if (isStrict) {
final int lineNumber = (remainingMessage.size() > 0) ? remainingMessage.get(0).getLineNumber() : 0;
throw new BatchDeserializerException("Missing blank line",
BatchDeserializerException.MessageKeys.MISSING_BLANK_LINE, "[None]", ""
+ lineNumber);
}
}
}
public static InputStream convertLineListToInputStream(final List<Line> messageList) {
final String message = lineListToString(messageList);
return new ByteArrayInputStream(message.getBytes());
}
private static String lineListToString(final List<Line> messageList) {
final StringBuilder builder = new StringBuilder();
for (Line currentLine : messageList) {
builder.append(currentLine.toString());
}
return builder.toString();
}
public static String trimLineListToLength(final List<Line> list, final int length) {
final String message = lineListToString(list);
final int lastIndex = Math.min(length, message.length());
return (lastIndex > 0) ? message.substring(0, lastIndex) : "";
}
public static InputStream convertLineListToInputStream(final List<Line> list, final int length) {
final String message = trimLineListToLength(list, length);
return new ByteArrayInputStream(message.getBytes());
}
}