gt: 				idx = raw.indexOf('=');
context: /* This code is part of Freenet. It is distributed under the GNU General
   * Public License, version 2 (or at your option any later version). See
   * http://www.gnu.org/ for further details of the GPL. */
  package freenet.client.filter;
  
  import java.io.BufferedInputStream;
  import java.io.EOFException;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.OutputStream;
  import java.io.UnsupportedEncodingException;
  import java.net.URI;
  import java.util.Arrays;
  import java.util.HashMap;
  import java.util.Hashtable;
  import java.util.LinkedHashMap;
  
  import freenet.client.filter.CharsetExtractor.BOMDetection;
  import freenet.l10n.NodeL10n;
  import freenet.support.LogThresholdCallback;
  import freenet.support.Logger;
  import freenet.support.Logger.LogLevel;
  import freenet.support.io.FileUtil;
  
  /**
   * Freenet content filter. This doesn't actually do any filtering,
   * it organizes everything and maintains the database.
   */
  public class ContentFilter {
  
  	static final Hashtable<String, MIMEType> mimeTypesByName = new Hashtable<String, MIMEType>();
  	
  	/** The HTML mime types are defined here, to allow other modules to identify it*/
  	public static final String[] HTML_MIME_TYPES=new String[]{"text/html", "application/xhtml+xml", "text/xml+xhtml", "text/xhtml", "application/xhtml"};
  	
          private static volatile boolean logMINOR;
  	static {
  		Logger.registerLogThresholdCallback(new LogThresholdCallback(){
  			@Override
  			public void shouldUpdate(){
  				logMINOR = Logger.shouldLog(LogLevel.MINOR, this);
  			}
  		});
  	}
  
  	static {
  		init();
  	}
  	
  	public static void init() {
  		// Register known MIME types
  		
  		// Plain text
  		register(new MIMEType("text/plain", "txt", new String[0], new String[] { "text", "pot" },
  				true, true, null, null, false, false, false, false, false, false,
  				l10n("textPlainReadAdvice"),
  				l10n("textPlainWriteAdvice"),
  				true, "US-ASCII", null, false));
  		
  		// GIF - has a filter 
  		register(new MIMEType("image/gif", "gif", new String[0], new String[0], 
  				true, false, new GIFFilter(), null, false, false, false, false, false, false,
  				l10n("imageGifReadAdvice"),
  				l10n("imageGifWriteAdvice"),
  				false, null, null, false));
  		
  		// JPEG - has a filter
  		register(new MIMEType("image/jpeg", "jpeg", new String[0], new String[] { "jpg" },
  				true, false, new JPEGFilter(true, true), null, false, false, false, false, false, false,
  				l10n("imageJpegReadAdvice"),
  				l10n("imageJpegWriteAdvice"), false, null, null, false));
  		
  		// PNG - has a filter
  		register(new MIMEType("image/png", "png", new String[] { "image/x-png" }, new String[0],
  				true, false, new PNGFilter(true, true, true), null, false, false, false, false, true, false,
  				l10n("imagePngReadAdvice"),
  				l10n("imagePngWriteAdvice"), false, null, null, false));
  
  
  		// BMP - has a filter
  		// Reference: http://filext.com/file-extension/BMP
  		register(new MIMEType("image/bmp", "bmp", new String[] { "image/x-bmp","image/x-bitmap","image/x-xbitmap","image/x-win-bitmap","image/x-windows-bmp","image/ms-bmp","image/x-ms-bmp","application/bmp","application/x-bmp","application/x-win-bitmap"  }, new String[0],
  				true, false, new BMPFilter(), null, false, false, false, false, true, false,
  				l10n("imageBMPReadAdvice"),
  				l10n("imageBMPWriteAdvice"), false, null, null, false));	
  
  
  		/* MP3
  		 *
  		 * Reference: http://www.mp3-tech.org/programmer/frame_header.html
  		 */
  		register(new MIMEType("audio/mpeg", "mp3", new String[] {"audio/mp3", "audio/x-mp3", "audio/x-mpeg", "audio/mpeg3", "audio/x-mpeg3", "audio/mpg", "audio/x-mpg", "audio/mpegaudio"},
  				new String[0], true, false, new MP3Filter(), new MP3Filter(), true, true, false, true, false, false,
  				l10n("audioMP3ReadAdvice"), l10n("audioMP3WriteAdvice"), false, null, null, false));
  
  		// ICO needs filtering.
  		// Format is not the same as BMP iirc.
  		// DoS: http://www.kb.cert.org/vuls/id/290961
  		// Remote code exec: http://www.microsoft.com/technet/security/bulletin/ms09-062.mspx
  		
  //		// ICO - probably safe - FIXME check this out, write filters
  //		register(new MIMEType("image/x-icon", "ico", new String[] { "image/vnd.microsoft.icon", "image/ico", "application/ico"}, 
  //				new String[0], true, false, null, null, false, false, false, false, false, false,
  //				l10n("imageIcoReadAdvice"),
  //				l10n("imageIcoWriteAdvice"), false, null, null, false));
  		
  		// PDF - very dangerous - FIXME ideally we would have a filter, this is such a common format...
  		register(new MIMEType("application/pdf", "pdf", new String[] { "application/x-pdf" }, new String[0],
  				false, false, null, null, true, true, true, false, true, true,
  				l10n("applicationPdfReadAdvice"),
  				l10n("applicationPdfWriteAdvice"),
  				false, null, null, false));
  		
  		// HTML - dangerous if not filtered
  		register(new MIMEType(HTML_MIME_TYPES[0], "html", Arrays.asList(HTML_MIME_TYPES).subList(1, HTML_MIME_TYPES.length).toArray(new String[HTML_MIME_TYPES.length-1]), new String[] { "htm" },
  				false, false /* maybe? */, new HTMLFilter(), null /* FIXME */, 
  				true, true, true, true, true, true, 
  				l10n("textHtmlReadAdvice"),
  				l10n("textHtmlWriteAdvice"),
  				true, "iso-8859-1", new HTMLFilter(), false));
  		
  		// CSS - danagerous if not filtered, not sure about the filter
  		register(new MIMEType("text/css", "css", new String[0], new String[0],
  				false, false /* unknown */, new CSSReadFilter(), null,
  				true, true, true, true, true, false,
  				l10n("textCssReadAdvice"),
  				l10n("textCssWriteAdvice"),
  				true, "utf-8", new CSSReadFilter(), true));
  		
  	}
  	
  	private static String l10n(String key) {
  		return NodeL10n.getBase().getString("ContentFilter."+key);
  	}
  
  	public static void register(MIMEType mimeType) {
  		synchronized(mimeTypesByName) {
  			mimeTypesByName.put(mimeType.primaryMimeType, mimeType);
  			String[] alt = mimeType.alternateMimeTypes;
  			if(alt != null) {
  				for(String a: alt)
  					mimeTypesByName.put(a, mimeType);
  			}
  		}
  	}
  
  	public static String stripMIMEType(String mimeType) {
  		if(mimeType == null) return null;
  		int x; 
  		if((x=mimeType.indexOf(';')) != -1) {
  			mimeType = mimeType.substring(0, x).trim();
  		}
  		return mimeType;
  	}
  	
  	public static MIMEType getMIMEType(String mimeType) {
  		if(mimeType == null) return null;
  		return mimeTypesByName.get(stripMIMEType(mimeType));
  	}
  
  	/**
  	 * Filter some data.
  	 * 
  	 * @param input
  	 *            Source stream to read data from
  	 * @param output
  	 *            Stream to write filtered data to
  	 * @param typeName
  	 *            MIME type for input data
  	 * @param maybeCharset 
  	 * 			  MIME type of the referring document, as a hint, some types,
  	 * 			  such as CSS, will inherit it if no other data is available.
  	 * @return 
  	 * @throws IOException
  	 *             If an internal error involving s occurred.
  	 * @throws UnsafeContentTypeException
  	 *             If the MIME type is declared unsafe (e.g. pdf files)
  	 * @throws IllegalStateException
  	 *             If data is invalid (e.g. corrupted file) and the filter have no way to recover.
  	 */
  	public static FilterStatus filter(InputStream input, OutputStream output, String typeName, URI baseURI, FoundURICallback cb, TagReplacerCallback trc , String maybeCharset) throws UnsafeContentTypeException, IOException {
  		return filter(input, output, typeName, baseURI, cb, trc, maybeCharset, null);
  	}
  
  	/**
  	 * Filter some data.
  	 *
  	 * @param input
  	 *            Source stream to read data from
  	 * @param output
  	 *            Stream to write filtered data to
  	 * @param typeName
  	 *            MIME type for input data
  	 * @param maybeCharset
  	 * 			  MIME type of the referring document, as a hint, some types,
  	 * 			  such as CSS, will inherit it if no other data is available.
  	 * @return
  	 * @throws IOException
  	 *             If an internal error involving s occurred.
  	 * @throws UnsafeContentTypeException
  	 *             If the MIME type is declared unsafe (e.g. pdf files)
  	 * @throws IllegalStateException
  	 *             If data is invalid (e.g. corrupted file) and the filter have no way to recover.
  	 */
  	public static FilterStatus filter(InputStream input, OutputStream output, String typeName, URI baseURI, FoundURICallback cb, TagReplacerCallback trc , String maybeCharset, LinkFilterExceptionProvider linkFilterExceptionProvider) throws UnsafeContentTypeException, IOException {
  		return filter(input, output, typeName, maybeCharset, new GenericReadFilterCallback(baseURI, cb,trc, linkFilterExceptionProvider));
  	}
  
  	/**
  	 * Filter some data.
  	 * 
  	 * @param input
  	 *            Source stream to read data from
  	 * @param output
  	 *            Stream to write filtered data to
  	 * @param typeName
  	 *            MIME type for input data
  	 * @param maybeCharset 
  	 * 			  MIME type of the referring document, as a hint, some types,
  	 * 			  such as CSS, will inherit it if no other data is available.
  	 * @throws IOException
  	 *             If an internal error involving buckets occurred.
  	 * @throws UnsafeContentTypeException
  	 *             If the MIME type is declared unsafe (e.g. pdf files)
  	 * @throws IllegalStateException
  	 *             If data is invalid (e.g. corrupted file) and the filter have no way to recover.
  	 */
  	public static FilterStatus filter(InputStream input, OutputStream output, String typeName, String maybeCharset, FilterCallback filterCallback) throws UnsafeContentTypeException, IOException {
  		if(logMINOR) Logger.minor(ContentFilter.class, "Filtering data of type"+typeName);
  		String type = typeName;
  		String options = "";
  		String charset = null;
  		HashMap<String, String> otherParams = null;
  		input = new BufferedInputStream(input);
  
  		// First parse the MIME type
  		
  		int idx = type.indexOf(';');
  		if(idx != -1) {
  			options = type.substring(idx+1);
  			type = type.substring(0, idx);
  			// Parse options
  			// Format: <type>/<subtype>[ optional white space ];[ optional white space ]<param>=<value>; <param2>=<value2>; ...
  			String[] rawOpts = options.split(";");
  			for(String raw: rawOpts) {
  