package org.basex.http.webdav;
import static org.basex.http.webdav.WebDAVUtils.*;
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.Map.Entry;
import javax.servlet.http.*;
import org.apache.commons.fileupload.*;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.servlet.*;
import org.basex.util.*;
import com.bradmcevoy.http.*;
import com.bradmcevoy.http.Response.ContentType;
import com.bradmcevoy.http.Cookie;
/**
* Wrapper around {@link HttpServletRequest}, which in addition implements {@link Request}.<br/>
* This implementation is the same as the implementation of {@code ServletRequest} found in
* {@code milton-servlet}. Since this is one of the few classes which is needed from that library
* the source is integrated into BaseX.
*
* @author Milton Development Team
* @author BaseX Team 2005-17, BSD License
* @author Rositsa Shadura
* @author Dimitar Popov
*/
final class WebDAVRequest extends AbstractRequest {
/** Destination string. */
private static final String DESTINATION = "Destination";
/** HTTP servlet request. */
private final HttpServletRequest req;
/** Request method. */
private final Method method;
/** Request URL. */
private final String url;
/** Authentication. */
private Auth auth;
/** Content types map. */
private static final Map<ContentType, String> CONTENT_TYPES = new EnumMap<>(ContentType.class);
/** Type contents map. */
private static final Map<String, ContentType> TYPE_CONTENTS = new HashMap<>();
static {
CONTENT_TYPES.put(ContentType.HTTP, Response.HTTP);
CONTENT_TYPES.put(ContentType.MULTIPART, Response.MULTIPART);
CONTENT_TYPES.put(ContentType.XML, Response.XML);
for(final Entry<ContentType, String> entry : CONTENT_TYPES.entrySet())
TYPE_CONTENTS.put(entry.getValue(), entry.getKey());
}
/**
* Constructor.
* @param req HTTP servlet request
*/
WebDAVRequest(final HttpServletRequest req) {
this.req = req;
method = Method.valueOf(req.getMethod());
url = decode(req.getRequestURL().toString());
}
@Override
public String getFromAddress() {
return req.getRemoteHost();
}
@Override
public String getRequestHeader(final Header header) {
final String value = req.getHeader(header.code);
return header.code.equals(DESTINATION) ? decode(value) : value;
}
@Override
public Method getMethod() {
return method;
}
@Override
public String getAbsoluteUrl() {
return url;
}
@Override
public String getRemoteAddr() {
return req.getRemoteAddr();
}
@Override
public Auth getAuthorization() {
if(auth == null) {
final String enc = getRequestHeader(Header.AUTHORIZATION);
if(enc != null && !enc.isEmpty()) auth = new Auth(enc);
}
return auth;
}
@Override
public void setAuthorization(final Auth a) {
auth = a;
}
@Override
public InputStream getInputStream() throws IOException {
return req.getInputStream();
}
@Override
public Map<String, String> getHeaders() {
final Map<String, String> map = new HashMap<>();
final Enumeration<String> en = req.getHeaderNames();
while(en.hasMoreElements()) {
final String name = en.nextElement();
final String val = req.getHeader(name);
map.put(name, val);
}
return map;
}
@Override
public Cookie getCookie(final String name) {
for(final javax.servlet.http.Cookie c : req.getCookies()) {
if(c.getName().equals(name)) return new WebDAVCookie(c);
}
return null;
}
@Override
public List<Cookie> getCookies() {
final List<Cookie> list = new ArrayList<>();
for(final javax.servlet.http.Cookie c : req.getCookies()) {
list.add(new WebDAVCookie(c));
}
return list;
}
@Override
public void parseRequestParameters(final Map<String, String> params,
final Map<String, com.bradmcevoy.http.FileItem> files) throws RequestParseException {
try {
if(isMultiPart()) {
parseQueryString(params, req.getQueryString());
final List<FileItem> items = new ServletFileUpload().parseRequest(req);
for(final FileItem item : items) {
if(item.isFormField())
params.put(item.getFieldName(), item.getString());
else
files.put(item.getFieldName(), new FileItemWrapper(item));
}
} else {
final Enumeration<String> en = req.getParameterNames();
while(en.hasMoreElements()) {
final String nm = en.nextElement();
final String val = req.getParameter(nm);
params.put(nm, val);
}
}
} catch(final FileUploadException ex) {
throw new RequestParseException("FileUploadException", ex);
} catch(final Throwable ex) {
throw new RequestParseException(ex.getMessage(), ex);
}
}
/**
* Parse the query string.
* @param map parsed key-values will be stored here
* @param qs query string
*/
private static void parseQueryString(final Map<String, String> map, final String qs) {
if(qs == null) return;
for(final String nv : Strings.split(qs, '&')) {
final String[] parts = Strings.split(nv, '=', 2);
final String key = parts[0];
String val = null;
if(parts.length > 1) {
try {
val = URLDecoder.decode(parts[1], Strings.UTF8);
} catch(final UnsupportedEncodingException ex) {
throw new RuntimeException(ex);
}
}
map.put(key, val);
}
}
/**
* Request content type.
* @return the content type of the current request
*/
private ContentType getRequestContentType() {
final String s = req.getContentType();
if(s == null) return null;
if(s.contains(Response.MULTIPART)) return ContentType.MULTIPART;
return TYPE_CONTENTS.get(s);
}
/**
* Is the content type of the request a multi-part?
* @return {@code true} if the request is multi-part
*/
private boolean isMultiPart() {
return getRequestContentType() == ContentType.MULTIPART;
}
}
/**
* Wrapper around {@link FileItem}, which in addition implements
* {@link com.bradmcevoy.http.FileItem}.<br/>
* This implementation is the same as the implementation of
* {@code FileItemWrapper} found in {@code milton-servlet}. Since this is one of
* the few classes which is needed from that library, the source is integrated
* into BaseX.
* @author Milton Development Team
* @author BaseX Team 2005-17, BSD License
* @author Rositsa Shadura
* @author Dimitar Popov
*/
class FileItemWrapper implements com.bradmcevoy.http.FileItem {
/** Wrapped file item. */
private final FileItem file;
/** File name. */
private final String name;
/**
* Strip path information provided by IE.
* @param string string
* @return stripped string
*/
private static String fixIEFileName(final String string) {
final int pos = string.lastIndexOf('\\');
return pos < 0 ? string : string.substring(pos + 1);
}
/**
* Constructor.
* @param file file item
*/
FileItemWrapper(final FileItem file) {
this.file = file;
name = fixIEFileName(file.getName());
}
@Override
public String getContentType() {
return file.getContentType();
}
@Override
public String getFieldName() {
return file.getFieldName();
}
@Override
public InputStream getInputStream() {
try {
return file.getInputStream();
} catch(final IOException ex) {
throw new RuntimeException(ex);
}
}
@Override
public OutputStream getOutputStream() {
try {
return file.getOutputStream();
} catch(final IOException ex) {
throw new RuntimeException(ex);
}
}
@Override
public String getName() {
return name;
}
@Override
public long getSize() {
return file.getSize();
}
}