content serving servlet, thx to zzz

This commit is contained in:
Zlatin Balevsky
2019-12-10 12:50:38 +00:00
parent 6946bff7f9
commit 8e6517e7d8
9 changed files with 1162 additions and 0 deletions

View File

@ -20,6 +20,9 @@ war {
from ('src/main/js', {
into "js"
})
from ('src/main/resources', {
into "WEB-INF/classes/com/muwire/webui"
})
webInf {
from "$buildDir/compiledJsps"
into "classes"
@ -100,6 +103,7 @@ task poupdate {
precompileJsp.dependsOn compileJava
generateWebXML.dependsOn precompileJsp
bundle.dependsOn precompileJsp
poupdate.dependsOn precompileJsp
war.dependsOn generateWebXML, bundle
artifacts {

View File

@ -0,0 +1,534 @@
// ========================================================================
// Copyright 199-2004 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// Licensed 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 com.muwire.webui;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Enumeration;
import java.util.List;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.i2p.I2PAppContext;
import net.i2p.data.ByteArray;
import net.i2p.data.DataHelper;
import net.i2p.servlet.util.WriterOutputStream;
import net.i2p.util.ByteCache;
import net.i2p.util.Log;
import net.i2p.util.SecureFile;
import net.i2p.util.SystemVersion;
/* ------------------------------------------------------------ */
/**
* Based on DefaultServlet from Jetty 6.1.26, heavily simplified
* and modified to remove all dependencies on Jetty libs.
*
* Supports HEAD and GET only, for resources from the .war and local files.
* Supports files and resource only.
* Supports MIME types with local overrides and additions.
* Supports Last-Modified.
* Supports single request ranges.
*
* Does not support directories or "welcome files".
* Does not support gzip.
* Does not support multiple request ranges.
* Does not cache.
*
* POST returns 405.
* Directories return 403.
* Jar resources are sent with a long cache directive.
*
* ------------------------------------------------------------
*
* The default servlet.
* This servlet, normally mapped to /, provides the handling for static
* content, OPTION and TRACE methods for the context.
* The following initParameters are supported, these can be set
* on the servlet itself:
* <PRE>
*
* resourceBase Set to replace the context resource base
* </PRE>
*
*
* @author Greg Wilkins (gregw)
* @author Nigel Canonizado
*
* @since Jetty 7
*/
class BasicServlet extends HttpServlet
{
private static final long serialVersionUID = 1L;
protected transient final I2PAppContext _context;
protected transient final Log _log;
protected File _resourceBase;
private transient final MimeTypes _mimeTypes;
/** same as PeerState.PARTSIZE */
private static final int BUFSIZE = 16*1024;
private transient ByteCache _cache = ByteCache.getInstance(16, BUFSIZE);
private static final int FILE_CACHE_CONTROL_SECS = 24*60*60;
public BasicServlet() {
super();
_context = I2PAppContext.getGlobalContext();
_log = _context.logManager().getLog(getClass());
_mimeTypes = new MimeTypes();
}
/* ------------------------------------------------------------ */
public void init(ServletConfig cfg) throws ServletException {
super.init(cfg);
String rb=getInitParameter("resourceBase");
if (rb!=null)
{
File f = new SecureFile(rb);
setResourceBase(f);
}
}
/**
* Files are served from here
*/
protected synchronized void setResourceBase(File base) throws UnavailableException {
if (!base.isDirectory()) {
_log.error("Configured directory " + base + " does not exist");
//throw new UnavailableException("Resource base does not exist: " + base);
}
_resourceBase = base;
if (_log.shouldLog(Log.INFO))
_log.info("Resource base is " + _resourceBase);
}
/** get Resource to serve.
* Map a path to a resource. The default implementation calls
* HttpContext.getResource but derived servlets may provide
* their own mapping.
* @param pathInContext The path to find a resource for.
* @return The resource to serve or null if not existing
*/
public File getResource(String pathInContext)
{
File r = null;
if (!pathInContext.contains("..") &&
!pathInContext.endsWith("/")) {
File f;
synchronized (this) {
if (_resourceBase==null)
return null;
f = new File(_resourceBase, pathInContext);
}
if (f.exists())
r = f;
}
return r;
}
/** get Resource to serve.
* Map a path to a resource. The default implementation calls
* HttpContext.getResource but derived servlets may provide
* their own mapping.
* @param pathInContext The path to find a resource for.
* @return The resource to serve or null. Returns null for directories
*/
public HttpContent getContent(String pathInContext)
{
HttpContent r = null;
File f = getResource(pathInContext);
// exists && !directory
if (f != null && f.isFile())
r = new FileContent(f);
return r;
}
/* ------------------------------------------------------------ */
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
// always starts with a '/'
String servletpath = request.getServletPath();
String pathInfo=request.getPathInfo();
// ??? right??
String pathInContext = addPaths(servletpath, pathInfo);
// Find the resource and content
try {
HttpContent content = getContent(pathInContext);
// Handle resource
if (content == null) {
if (_log.shouldLog(Log.WARN))
_log.warn("Not found: " + pathInContext);
response.sendError(404);
} else {
if (passConditionalHeaders(request, response, content)) {
if (_log.shouldLog(Log.INFO))
_log.info("Sending: " + content);
sendData(request, response, content);
} else {
if (_log.shouldLog(Log.INFO))
_log.info("Not modified: " + content);
}
}
}
catch(IllegalArgumentException e)
{
if (_log.shouldLog(Log.WARN))
_log.warn("Error sending " + pathInContext, e);
if(!response.isCommitted())
response.sendError(500, e.getMessage());
}
catch(IOException e)
{
if (_log.shouldLog(Log.WARN))
// typical browser abort
//_log.warn("Error sending", e);
_log.warn("Error sending " + pathInContext + ": " + e);
throw e;
}
}
/* ------------------------------------------------------------ */
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
response.sendError(405);
}
/* ------------------------------------------------------------ */
/* (non-Javadoc)
* @see javax.servlet.http.HttpServlet#doTrace(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
protected void doTrace(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
response.sendError(405);
}
protected void doOptions(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
response.sendError(405);
}
protected void doDelete(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
response.sendError(405);
}
/* ------------------------------------------------------------ */
/** Check modification date headers.
* @return true to keep going, false if handled here
*/
protected boolean passConditionalHeaders(HttpServletRequest request,HttpServletResponse response, HttpContent content)
throws IOException
{
try
{
if (!request.getMethod().equals("HEAD") ) {
long ifmsl=request.getDateHeader("If-Modified-Since");
if (ifmsl!=-1)
{
if (content.getLastModified()/1000 <= ifmsl/1000)
{
try {
response.reset();
} catch (IllegalStateException ise) {
// committed
return true;
}
response.setStatus(304);
response.getOutputStream().close();
return false;
}
}
}
}
catch(IllegalArgumentException iae)
{
if(!response.isCommitted())
response.sendError(400, iae.getMessage());
throw iae;
}
return true;
}
/* ------------------------------------------------------------ */
protected void sendData(HttpServletRequest request,
HttpServletResponse response,
HttpContent content)
throws IOException
{
InputStream in =null;
try {
in = content.getInputStream();
} catch (IOException e) {
if (_log.shouldLog(Log.WARN))
_log.warn("Not found: " + content);
response.sendError(404);
return;
}
OutputStream out =null;
try {
out = response.getOutputStream();
} catch (IllegalStateException e) {
out = new WriterOutputStream(response.getWriter());
}
long content_length = content.getContentLength();
// see if there are any range headers
Enumeration<?> reqRanges = request.getHeaders("Range");
if (reqRanges == null || !reqRanges.hasMoreElements()) {
// if there were no ranges, send entire entity
// Write content normally
writeHeaders(response,content,content_length);
if (content_length >= 0 && request.getMethod().equals("HEAD")) {
// if we know the content length, don't send it to be counted
if (_log.shouldLog(Log.INFO))
_log.info("HEAD: " + content);
} else {
// GET or unknown size for HEAD
copy(in, out);
}
return;
}
// Parse the satisfiable ranges
List<InclusiveByteRange> ranges = InclusiveByteRange.satisfiableRanges(reqRanges, content_length);
// if there are no satisfiable ranges, send 416 response
// Completely punt on multiple ranges (unlike Default)
if (ranges == null || ranges.size() != 1) {
writeHeaders(response, content, content_length);
response.setStatus(416);
response.setHeader("Content-Range", InclusiveByteRange.to416HeaderRangeString(content_length));
in.close();
return;
}
// if there is only a single valid range (must be satisfiable
// since were here now), send that range with a 216 response
InclusiveByteRange singleSatisfiableRange = ranges.get(0);
long singleLength = singleSatisfiableRange.getSize(content_length);
writeHeaders(response, content, singleLength);
response.setStatus(206);
response.setHeader("Content-Range", singleSatisfiableRange.toHeaderRangeString(content_length));
copy(in, singleSatisfiableRange.getFirst(content_length), out, singleLength);
}
/* ------------------------------------------------------------ */
protected void writeHeaders(HttpServletResponse response,HttpContent content,long count)
throws IOException
{
String rtype = response.getContentType();
String ctype = content.getContentType();
if (rtype != null) {
if (rtype.equals("application/javascript"))
response.setCharacterEncoding("ISO-8859-1");
} else if (ctype != null) {
response.setContentType(ctype);
if (ctype.equals("application/javascript"))
response.setCharacterEncoding("ISO-8859-1");
}
response.setHeader("X-Content-Type-Options", "nosniff");
long lml = content.getLastModified();
if (lml > 0)
response.setDateHeader("Last-Modified",lml);
if (count != -1) {
if (count <= Integer.MAX_VALUE)
response.setContentLength((int)count);
else
response.setHeader("Content-Length", Long.toString(count));
response.setHeader("Accept-Ranges", "bytes");
} else {
response.setHeader("Accept-Ranges", "none");
}
// add name header for muwire since the URL is just the hash
String name = content.getContentName();
String name2 = FilenameUtil.sanitizeFilename(name);
String name3 = FilenameUtil.encodeFilenameRFC5987(name);
response.addHeader("Content-Disposition", "inline; filename=\"" + name2 + "\"; " +
"filename*=" + name3);
long ct = content.getCacheTime();
if (ct>=0)
response.setHeader("Cache-Control", "public, max-age=" + ct);
}
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
/* I2P additions below here */
/** from Jetty HttpContent.java */
public interface HttpContent
{
String getContentType();
String getContentName();
long getLastModified();
/** in seconds */
int getCacheTime();
long getContentLength();
InputStream getInputStream() throws IOException;
}
private class FileContent implements HttpContent
{
private final File _file;
public FileContent(File file)
{
_file = file;
}
/* ------------------------------------------------------------ */
public String getContentType()
{
//return _mimeTypes.getMimeByExtension(_file.toString());
return getMimeType(_file.toString());
}
public String getContentName()
{
return _file.getName();
}
/* ------------------------------------------------------------ */
public long getLastModified()
{
return _file.lastModified();
}
public int getCacheTime()
{
return FILE_CACHE_CONTROL_SECS;
}
/* ------------------------------------------------------------ */
public long getContentLength()
{
return _file.length();
}
/* ------------------------------------------------------------ */
public InputStream getInputStream() throws IOException
{
return new BufferedInputStream(new FileInputStream(_file));
}
@Override
public String toString() { return "File \"" + _file + '"'; }
}
/**
* @param resourcePath in the classpath, without ".properties" extension
*/
protected void loadMimeMap(String resourcePath) {
_mimeTypes.loadMimeMap(resourcePath);
}
/* ------------------------------------------------------------ */
/** Get the MIME type by filename extension.
* @param filename A file name
* @return MIME type matching the longest dot extension of the
* file name.
*/
protected String getMimeType(String filename) {
String rv = _mimeTypes.getMimeByExtension(filename);
if (rv != null)
return rv;
return getServletContext().getMimeType(filename);
}
protected void addMimeMapping(String extension, String type) {
_mimeTypes.addMimeMapping(extension, type);
}
/**
* Simple version of URIUtil.addPaths()
* @param path may be null
*/
protected static String addPaths(String base, String path) {
if (path == null)
return base;
String rv = (new File(base, path)).toString();
if (SystemVersion.isWindows())
rv = rv.replace("\\", "/");
return rv;
}
/**
* Write from in to out
*/
private void copy(InputStream in, OutputStream out) throws IOException {
copy(in, 0, out, -1);
}
/**
* Write from in to out
*/
private void copy(InputStream in, long skip, OutputStream out, final long len) throws IOException {
ByteArray ba = _cache.acquire();
byte[] buf = ba.getData();
try {
if (skip > 0)
DataHelper.skip(in, skip);
int read = 0;
long tot = 0;
boolean done = false;
while ( (read = in.read(buf)) != -1 && !done) {
if (len >= 0) {
tot += read;
if (tot >= len) {
read -= (int) (tot - len);
done = true;
}
}
out.write(buf, 0, read);
}
} finally {
_cache.release(ba, false);
if (in != null)
try { in.close(); } catch (IOException ioe) {}
if (out != null)
try { out.close(); } catch (IOException ioe) {}
}
}
}

View File

@ -0,0 +1,29 @@
package com.muwire.webui;
import java.io.File;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
public class DownloadedContentServlet extends BasicServlet {
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
loadMimeMap("com/muwire/webui/mime");
}
/**
* Find the file for the hash.
*
* @param pathInContext should always start with /
* @return file or null
*/
@Override
public File getResource(String pathInContext)
{
File r = null;
// TODO
return r;
}
}

View File

@ -0,0 +1,87 @@
package com.muwire.webui;
import net.i2p.data.DataHelper;
/**
* File name encoding methods
*
* From SusiMail. GPLv2 or any later version.
*/
public class FilenameUtil {
/**
* Convert the UTF-8 to ASCII suitable for inclusion in a header
* and for use as a cross-platform filename.
* Replace chars likely to be illegal in filenames,
* and non-ASCII chars, with _
*
* Ref: RFC 6266, RFC 5987, i2psnark Storage.ILLEGAL
*
* @since 0.9.18
*/
public static String sanitizeFilename(String name) {
name = name.trim();
StringBuilder buf = new StringBuilder(name.length());
for (int i = 0; i < name.length(); i++) {
char c = name.charAt(i);
// illegal filename chars
if (c <= 32 || c >= 0x7f ||
c == '<' || c == '>' || c == ':' || c == '"' ||
c == '/' || c == '\\' || c == '|' || c == '?' ||
c == '*')
buf.append('_');
else
buf.append(c);
}
return buf.toString();
}
/**
* Encode the UTF-8 suitable for inclusion in a header
* as a RFC 5987/6266 filename* value, and for use as a cross-platform filename.
* Replace chars likely to be illegal in filenames with _
*
* Ref: RFC 6266, RFC 5987, i2psnark Storage.ILLEGAL
*
* This does NOT do multiline, e.g. filename*0* (RFC 2231)
*
* ref: https://blog.nodemailer.com/2017/01/27/the-mess-that-is-attachment-filenames/
* ref: RFC 2231
*
* @since 0.9.33
*/
public static String encodeFilenameRFC5987(String name) {
name = name.trim();
StringBuilder buf = new StringBuilder(name.length());
buf.append("utf-8''");
for (int i = 0; i < name.length(); i++) {
char c = name.charAt(i);
// illegal filename chars
if (c < 32 || (c >= 0x7f && c <= 0x9f) ||
c == '<' || c == '>' || c == ':' || c == '"' ||
c == '/' || c == '\\' || c == '|' || c == '?' ||
c == '*' ||
// unicode newlines
c == 0x2028 || c == 0x2029) {
buf.append('_');
} else if (c == ' ' || c == '\'' || c == '%' || // not in 5987 attr-char
c == '(' || c == ')' || c == '@' || // 2616 separators
c == ',' || c == ';' || c == '[' || c == ']' ||
c == '=' || c == '{' || c == '}') {
// single byte encoding
buf.append(HexTable.table[c].replace('=', '%'));
} else if (c < 0x7f) {
// single byte char, as-is
buf.append(c);
} else {
// multi-byte encoding
byte[] utf = DataHelper.getUTF8(String.valueOf(c));
for (int j = 0; j < utf.length; j++) {
int b = utf[j] & 0xff;
buf.append(HexTable.table[b].replace('=', '%'));
}
}
}
return buf.toString();
}
}

View File

@ -0,0 +1,73 @@
/*
* Created on Nov 12, 2004
*
* This file is part of susimail project, see http://susi.i2p/
*
* Copyright (C) 2004-2005 <susi23@mail.i2p>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Revision: 1.2 $
*/
package com.muwire.webui;
/**
* From SusiMail.
*
* @author susi
*/
public class HexTable {
/**
* Three character strings, upper case, e.g. "=0A"
*/
public static final String[] table = new String[256];
static {
for( int i = 0; i < 256; i++ ) {
String str = intToHex( i );
if( str.length() == 1 )
str = "0" + str;
table[i] = "=" + str;
}
}
private static String intToHex( int b )
{
if( b == 0 )
return "0";
else {
String str = "";
while( b > 0 ) {
byte c = (byte)(b % 16);
if( c < 10 )
c += '0';
else
c += 'A' - 10;
str = "" + (char)c + str;
b = (byte)(b / 16);
}
return str;
}
}
/****
public static void main(String[] args) {
for( int i = 0; i < 256; i++ ) {
System.out.println(i + ": " + table[i]);
}
}
****/
}

View File

@ -0,0 +1,218 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package com.muwire.webui;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.StringTokenizer;
/* ------------------------------------------------------------ */
/** Byte range inclusive of end points.
* <PRE>
*
* parses the following types of byte ranges:
*
* bytes=100-499
* bytes=-300
* bytes=100-
* bytes=1-2,2-3,6-,-2
*
* given an entity length, converts range to string
*
* bytes 100-499/500
*
* </PRE>
*
* Based on RFC2616 3.12, 14.16, 14.35.1, 14.35.2
* @version $version$
*
*/
public class InclusiveByteRange
{
long first = 0;
long last = 0;
public InclusiveByteRange(long first, long last)
{
this.first = first;
this.last = last;
}
public long getFirst()
{
return first;
}
public long getLast()
{
return last;
}
/* ------------------------------------------------------------ */
/**
* @param headers Enumeration of Range header fields.
* @param size Size of the resource.
* @return List of satisfiable ranges
*/
public static List<InclusiveByteRange> satisfiableRanges(Enumeration<?> headers, long size)
{
List<InclusiveByteRange> satRanges = null;
// walk through all Range headers
headers:
while (headers.hasMoreElements())
{
String header = (String) headers.nextElement();
StringTokenizer tok = new StringTokenizer(header,"=,",false);
String t=null;
try
{
// read all byte ranges for this header
while (tok.hasMoreTokens())
{
try
{
t = tok.nextToken().trim();
long first = -1;
long last = -1;
int d = t.indexOf('-');
if (d < 0 || t.indexOf('-',d + 1) >= 0)
{
if ("bytes".equals(t))
continue;
continue headers;
}
else if (d == 0)
{
if (d + 1 < t.length())
last = Long.parseLong(t.substring(d + 1).trim());
else
{
continue;
}
}
else if (d + 1 < t.length())
{
first = Long.parseLong(t.substring(0,d).trim());
last = Long.parseLong(t.substring(d + 1).trim());
}
else
first = Long.parseLong(t.substring(0,d).trim());
if (first == -1 && last == -1)
continue headers;
if (first != -1 && last != -1 && (first > last))
continue headers;
if (first < size)
{
if (satRanges == null)
satRanges = new ArrayList<InclusiveByteRange>(4);
InclusiveByteRange range = new InclusiveByteRange(first,last);
satRanges.add(range);
}
}
catch (NumberFormatException e)
{
continue;
}
}
}
catch(Exception e)
{
}
}
return satRanges;
}
/* ------------------------------------------------------------ */
public long getFirst(long size)
{
if (first<0)
{
long tf=size-last;
if (tf<0)
tf=0;
return tf;
}
return first;
}
/* ------------------------------------------------------------ */
public long getLast(long size)
{
if (first<0)
return size-1;
if (last<0 ||last>=size)
return size-1;
return last;
}
/* ------------------------------------------------------------ */
public long getSize(long size)
{
return getLast(size)-getFirst(size)+1;
}
/* ------------------------------------------------------------ */
public String toHeaderRangeString(long size)
{
StringBuilder sb = new StringBuilder(40);
sb.append("bytes ");
sb.append(getFirst(size));
sb.append('-');
sb.append(getLast(size));
sb.append("/");
sb.append(size);
return sb.toString();
}
/* ------------------------------------------------------------ */
public static String to416HeaderRangeString(long size)
{
StringBuilder sb = new StringBuilder(40);
sb.append("bytes */");
sb.append(size);
return sb.toString();
}
/* ------------------------------------------------------------ */
@Override
public String toString()
{
StringBuilder sb = new StringBuilder(60);
sb.append(Long.toString(first));
sb.append(":");
sb.append(Long.toString(last));
return sb.toString();
}
}

View File

@ -0,0 +1,129 @@
// ========================================================================
// Copyright 2000-2005 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// Licensed 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 com.muwire.webui;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.concurrent.ConcurrentHashMap;
/* ------------------------------------------------------------ */
/**
* Based on MimeTypes from Jetty 6.1.26, heavily simplified
* and modified to remove all dependencies on Jetty libs.
*
* Supports mime types only, not encodings.
* Does not support a default "*" mapping.
*
* This is only for local mappings.
* Caller should use getServletContext().getMimeType() if this returns null.
*
*
* ------------------------------------------------------------
*
* @author Greg Wilkins
*
* @since Jetty 7
*/
class MimeTypes
{
private final Map<String, String> _mimeMap;
public MimeTypes() {
_mimeMap = new ConcurrentHashMap<String, String>();
}
/* ------------------------------------------------------------ */
/**
* @param resourcePath A Map of file extension to mime-type.
*/
public void loadMimeMap(String resourcePath) {
loadMimeMap(_mimeMap, resourcePath);
}
/**
* Tries both webapp and system class loader, since Jetty blocks
* its classes from the webapp class loader.
*/
private static void loadMimeMap(Map<String, String> map, String resourcePath) {
try
{
ResourceBundle mime;
try {
mime = ResourceBundle.getBundle(resourcePath);
} catch(MissingResourceException e) {
// Jetty 7 webapp classloader blocks jetty classes
// http://wiki.eclipse.org/Jetty/Reference/Jetty_Classloading
//System.out.println("No mime types loaded from " + resourcePath + ", trying system classloader");
mime = ResourceBundle.getBundle(resourcePath, Locale.getDefault(), ClassLoader.getSystemClassLoader());
}
Enumeration<String> i = mime.getKeys();
while(i.hasMoreElements())
{
String ext = i.nextElement();
String m = mime.getString(ext);
map.put(ext.toLowerCase(Locale.US), m);
}
//System.out.println("Loaded " + map.size() + " mime types from " + resourcePath);
} catch(MissingResourceException e) {
//System.out.println("No mime types loaded from " + resourcePath);
}
}
/* ------------------------------------------------------------ */
/** Get the MIME type by filename extension.
*
* Returns ONLY local mappings.
* Caller should use getServletContext().getMimeType() if this returns null.
*
* @param filename A file name
* @return MIME type matching the longest dot extension of the
* file name.
*/
public String getMimeByExtension(String filename)
{
String type=null;
if (filename!=null)
{
int i=-1;
while(type==null)
{
i=filename.indexOf('.',i+1);
if (i<0 || i>=filename.length())
break;
String ext=filename.substring(i+1).toLowerCase(Locale.US);
type = _mimeMap.get(ext);
}
}
return type;
}
/* ------------------------------------------------------------ */
/** Set a mime mapping
* @param extension
* @param type
*/
public void addMimeMapping(String extension, String type)
{
_mimeMap.put(extension.toLowerCase(Locale.US), type);
}
}

View File

@ -0,0 +1,29 @@
package com.muwire.webui;
import java.io.File;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
public class SharedContentServlet extends BasicServlet {
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
loadMimeMap("com/muwire/webui/mime");
}
/**
* Find the file for the hash.
*
* @param pathInContext should always start with /
* @return file or null
*/
@Override
public File getResource(String pathInContext)
{
File r = null;
// TODO
return r;
}
}

View File

@ -0,0 +1,59 @@
3gp = video/3gpp
3gpp = video/3gpp
7z = application/x-7z-compressed
ape = audio/x-monkeys-audio
bz2 = application/x-bzip2
cue = application/x-cue
dmg = application/apple-diskimage
epub = application/epub+zip
flac = audio/flac
flv = video/x-flv
iso = application/x-iso9660-image
m3u = audio/mpegurl
m3u8 = audio/mpegurl
m4a = audio/mp4a-latm
m4b = audio/mp4a-latm
m4v = video/x-m4v
mka = audio/x-matroska
mkv = video/x-matroska
mobi = application/x-mobipocket-ebook
mp4 = video/mp4
mpc = audio/x-musepack
nfo = text/plain
odb = application/vnd.oasis.opendocument.database
odc = application/vnd.oasis.opendocument.chart
odf = application/vnd.oasis.opendocument.formula
odg = application/vnd.oasis.opendocument.graphics
odi = application/vnd.oasis.opendocument.image
odm = application/vnd.oasis.opendocument.text-master
odp = application/vnd.oasis.opendocument.presentation
ods = application/vnd.oasis.opendocument.spreadsheet
odt = application/vnd.oasis.opendocument.text
ogm = video/ogg
ogv = video/ogg
oga = audio/ogg
opus = audio/ogg
otc = application/vnd.oasis.opendocument.chart-template
otf = application/vnd.oasis.opendocument.formula-template
otg = application/vnd.oasis.opendocument.graphics-template
oth = application/vnd.oasis.opendocument.text-web
oti = application/vnd.oasis.opendocument.image-template
otp = application/vnd.oasis.opendocument.presentation-template
ots = application/vnd.oasis.opendocument.spreadsheet-template
ott = application/vnd.oasis.opendocument.text-template
pls = audio/x-scpls
rar = application/x-rar-compressed
sfv = text/x-sfv
su2 = application/zip
su3 = application/zip
sud = application/zip
tbz = application/x-bzip2
torrent = application/x-bittorrent
txt = text/plain
war = application/java-archive
webm = video/webm
wma = audio/x-ms-wma
wmv = video/x-ms-wmv
wpl = application/vnd.ms-wpl
xspf = application/xspf+xml
xz = application/x-xz