]> WPIA git - gigi.git/blob - lib/jetty/org/eclipse/jetty/http/MimeTypes.java
Update notes about password security
[gigi.git] / lib / jetty / org / eclipse / jetty / http / MimeTypes.java
1 //
2 //  ========================================================================
3 //  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4 //  ------------------------------------------------------------------------
5 //  All rights reserved. This program and the accompanying materials
6 //  are made available under the terms of the Eclipse Public License v1.0
7 //  and Apache License v2.0 which accompanies this distribution.
8 //
9 //      The Eclipse Public License is available at
10 //      http://www.eclipse.org/legal/epl-v10.html
11 //
12 //      The Apache License v2.0 is available at
13 //      http://www.opensource.org/licenses/apache2.0.php
14 //
15 //  You may elect to redistribute this code under either of these licenses.
16 //  ========================================================================
17 //
18
19 package org.eclipse.jetty.http;
20
21 import java.nio.ByteBuffer;
22 import java.nio.charset.Charset;
23 import java.nio.charset.StandardCharsets;
24 import java.util.Enumeration;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.Map;
28 import java.util.Map.Entry;
29 import java.util.MissingResourceException;
30 import java.util.ResourceBundle;
31 import java.util.Set;
32
33 import org.eclipse.jetty.util.ArrayTrie;
34 import org.eclipse.jetty.util.BufferUtil;
35 import org.eclipse.jetty.util.StringUtil;
36 import org.eclipse.jetty.util.Trie;
37 import org.eclipse.jetty.util.log.Log;
38 import org.eclipse.jetty.util.log.Logger;
39
40
41 /* ------------------------------------------------------------ */
42 /**
43  * 
44  */
45 public class MimeTypes
46 {
47     public enum Type
48     {
49         FORM_ENCODED("application/x-www-form-urlencoded"),
50         MESSAGE_HTTP("message/http"),
51         MULTIPART_BYTERANGES("multipart/byteranges"),
52
53         TEXT_HTML("text/html"),
54         TEXT_PLAIN("text/plain"),
55         TEXT_XML("text/xml"),
56         TEXT_JSON("text/json",StandardCharsets.UTF_8),
57         APPLICATION_JSON("application/json",StandardCharsets.UTF_8),
58
59         TEXT_HTML_8859_1("text/html; charset=ISO-8859-1",TEXT_HTML),
60         TEXT_HTML_UTF_8("text/html; charset=UTF-8",TEXT_HTML),
61         
62         TEXT_PLAIN_8859_1("text/plain; charset=ISO-8859-1",TEXT_PLAIN),
63         TEXT_PLAIN_UTF_8("text/plain; charset=UTF-8",TEXT_PLAIN),
64         
65         TEXT_XML_8859_1("text/xml; charset=ISO-8859-1",TEXT_XML),
66         TEXT_XML_UTF_8("text/xml; charset=UTF-8",TEXT_XML),
67         
68         TEXT_JSON_8859_1("text/json; charset=ISO-8859-1",TEXT_JSON),
69         TEXT_JSON_UTF_8("text/json; charset=UTF-8",TEXT_JSON),
70         
71         APPLICATION_JSON_8859_1("text/json; charset=ISO-8859-1",APPLICATION_JSON),
72         APPLICATION_JSON_UTF_8("text/json; charset=UTF-8",APPLICATION_JSON);
73
74
75         /* ------------------------------------------------------------ */
76         private final String _string;
77         private final Type _base;
78         private final ByteBuffer _buffer;
79         private final Charset _charset;
80         private final boolean _assumedCharset;
81         private final HttpField _field;
82
83         /* ------------------------------------------------------------ */
84         Type(String s)
85         {
86             _string=s;
87             _buffer=BufferUtil.toBuffer(s);
88             _base=this;
89             _charset=null;
90             _assumedCharset=false;
91             _field=new HttpGenerator.CachedHttpField(HttpHeader.CONTENT_TYPE,_string);
92         } 
93
94         /* ------------------------------------------------------------ */
95         Type(String s,Type base)
96         {
97             _string=s;
98             _buffer=BufferUtil.toBuffer(s);
99             _base=base;
100             int i=s.indexOf("; charset=");
101             _charset=Charset.forName(s.substring(i+10));
102             _assumedCharset=false;
103             _field=new HttpGenerator.CachedHttpField(HttpHeader.CONTENT_TYPE,_string);
104         }
105
106         /* ------------------------------------------------------------ */
107         Type(String s,Charset cs)
108         {
109             _string=s;
110             _base=this;
111             _buffer=BufferUtil.toBuffer(s);
112             _charset=cs;
113             _assumedCharset=true;
114             _field=new HttpGenerator.CachedHttpField(HttpHeader.CONTENT_TYPE,_string);
115         }
116
117         /* ------------------------------------------------------------ */
118         public ByteBuffer asBuffer()
119         {
120             return _buffer.asReadOnlyBuffer();
121         }
122         
123         /* ------------------------------------------------------------ */
124         public Charset getCharset()
125         {
126             return _charset;
127         }
128         
129         /* ------------------------------------------------------------ */
130         public boolean is(String s)
131         {
132             return _string.equalsIgnoreCase(s);    
133         }
134
135         /* ------------------------------------------------------------ */
136         public String asString()
137         {
138             return _string;
139         }
140         
141         /* ------------------------------------------------------------ */
142         @Override
143         public String toString()
144         {
145             return _string;
146         }
147
148         /* ------------------------------------------------------------ */
149         public boolean isCharsetAssumed()
150         {
151             return _assumedCharset;
152         }
153
154         /* ------------------------------------------------------------ */
155         public HttpField getContentTypeField()
156         {
157             return _field;
158         }
159
160         /* ------------------------------------------------------------ */
161         public Type getBaseType()
162         {
163             return _base;
164         }
165     }
166
167     /* ------------------------------------------------------------ */
168     private static final Logger LOG = Log.getLogger(MimeTypes.class);
169     public  final static Trie<MimeTypes.Type> CACHE= new ArrayTrie<>(512);
170     private final static Trie<ByteBuffer> TYPES= new ArrayTrie<ByteBuffer>(512);
171     private final static Map<String,String> __dftMimeMap = new HashMap<String,String>();
172     private final static Map<String,String> __encodings = new HashMap<String,String>();
173
174     static
175     {
176         for (MimeTypes.Type type : MimeTypes.Type.values())
177         {
178             CACHE.put(type.toString(),type);
179             TYPES.put(type.toString(),type.asBuffer());
180
181             int charset=type.toString().indexOf(";charset=");
182             if (charset>0)
183             {
184                 CACHE.put(type.toString().replace(";charset=","; charset="),type);
185                 TYPES.put(type.toString().replace(";charset=","; charset="),type.asBuffer());
186             }
187         }
188
189         try
190         {
191             ResourceBundle mime = ResourceBundle.getBundle("org/eclipse/jetty/http/mime");
192             Enumeration<String> i = mime.getKeys();
193             while(i.hasMoreElements())
194             {
195                 String ext = i.nextElement();
196                 String m = mime.getString(ext);
197                 __dftMimeMap.put(StringUtil.asciiToLowerCase(ext),normalizeMimeType(m));
198             }
199         }
200         catch(MissingResourceException e)
201         {
202             LOG.warn(e.toString());
203             LOG.debug(e);
204         }
205
206         try
207         {
208             ResourceBundle encoding = ResourceBundle.getBundle("org/eclipse/jetty/http/encoding");
209             Enumeration<String> i = encoding.getKeys();
210             while(i.hasMoreElements())
211             {
212                 String type = i.nextElement();
213                 __encodings.put(type,encoding.getString(type));
214             }
215         }
216         catch(MissingResourceException e)
217         {
218             LOG.warn(e.toString());
219             LOG.debug(e);
220         }
221     }
222
223
224     /* ------------------------------------------------------------ */
225     private final Map<String,String> _mimeMap=new HashMap<String,String>();
226
227     /* ------------------------------------------------------------ */
228     /** Constructor.
229      */
230     public MimeTypes()
231     {
232     }
233
234     /* ------------------------------------------------------------ */
235     public synchronized Map<String,String> getMimeMap()
236     {
237         return _mimeMap;
238     }
239
240     /* ------------------------------------------------------------ */
241     /**
242      * @param mimeMap A Map of file extension to mime-type.
243      */
244     public void setMimeMap(Map<String,String> mimeMap)
245     {
246         _mimeMap.clear();
247         if (mimeMap!=null)
248         {
249             for (Entry<String, String> ext : mimeMap.entrySet())
250                 _mimeMap.put(StringUtil.asciiToLowerCase(ext.getKey()),normalizeMimeType(ext.getValue()));
251         }
252     }
253     
254     /* ------------------------------------------------------------ */
255     /** Get the MIME type by filename extension.
256      * @param filename A file name
257      * @return MIME type matching the longest dot extension of the
258      * file name.
259      */
260     public String getMimeByExtension(String filename)
261     {
262         String type=null;
263
264         if (filename!=null)
265         {
266             int i=-1;
267             while(type==null)
268             {
269                 i=filename.indexOf(".",i+1);
270
271                 if (i<0 || i>=filename.length())
272                     break;
273
274                 String ext=StringUtil.asciiToLowerCase(filename.substring(i+1));
275                 if (_mimeMap!=null)
276                     type=_mimeMap.get(ext);
277                 if (type==null)
278                     type=__dftMimeMap.get(ext);
279             }
280         }
281
282         if (type==null)
283         {
284             if (_mimeMap!=null)
285                 type=_mimeMap.get("*");
286             if (type==null)
287                 type=__dftMimeMap.get("*");
288         }
289
290         return type;
291     }
292
293     /* ------------------------------------------------------------ */
294     /** Set a mime mapping
295      * @param extension
296      * @param type
297      */
298     public void addMimeMapping(String extension,String type)
299     {
300         _mimeMap.put(StringUtil.asciiToLowerCase(extension),normalizeMimeType(type));
301     }
302
303     /* ------------------------------------------------------------ */
304     public static Set<String> getKnownMimeTypes()
305     {
306         return new HashSet<>(__dftMimeMap.values());
307     }
308     
309     /* ------------------------------------------------------------ */
310     private static String normalizeMimeType(String type)
311     {
312         MimeTypes.Type t =CACHE.get(type);
313         if (t!=null)
314             return t.asString();
315
316         return StringUtil.asciiToLowerCase(type);
317     }
318
319     /* ------------------------------------------------------------ */
320     public static String getCharsetFromContentType(String value)
321     {
322         if (value==null)
323             return null;
324         int end=value.length();
325         int state=0;
326         int start=0;
327         boolean quote=false;
328         int i=0;
329         for (;i<end;i++)
330         {
331             char b = value.charAt(i);
332
333             if (quote && state!=10)
334             {
335                 if ('"'==b)
336                     quote=false;
337                 continue;
338             }
339
340             switch(state)
341             {
342                 case 0:
343                     if ('"'==b)
344                     {
345                         quote=true;
346                         break;
347                     }
348                     if (';'==b)
349                         state=1;
350                     break;
351
352                 case 1: if ('c'==b) state=2; else if (' '!=b) state=0; break;
353                 case 2: if ('h'==b) state=3; else state=0;break;
354                 case 3: if ('a'==b) state=4; else state=0;break;
355                 case 4: if ('r'==b) state=5; else state=0;break;
356                 case 5: if ('s'==b) state=6; else state=0;break;
357                 case 6: if ('e'==b) state=7; else state=0;break;
358                 case 7: if ('t'==b) state=8; else state=0;break;
359
360                 case 8: if ('='==b) state=9; else if (' '!=b) state=0; break;
361
362                 case 9:
363                     if (' '==b)
364                         break;
365                     if ('"'==b)
366                     {
367                         quote=true;
368                         start=i+1;
369                         state=10;
370                         break;
371                     }
372                     start=i;
373                     state=10;
374                     break;
375
376                 case 10:
377                     if (!quote && (';'==b || ' '==b )||
378                             (quote && '"'==b ))
379                         return StringUtil.normalizeCharset(value,start,i-start);
380             }
381         }
382
383         if (state==10)
384             return StringUtil.normalizeCharset(value,start,i-start);
385
386         return null;
387     }
388
389     public static String inferCharsetFromContentType(String value)
390     {
391         return __encodings.get(value);
392     }
393     
394     public static String getContentTypeWithoutCharset(String value)
395     {
396         int end=value.length();
397         int state=0;
398         int start=0;
399         boolean quote=false;
400         int i=0;
401         StringBuilder builder=null;
402         for (;i<end;i++)
403         {
404             char b = value.charAt(i);
405
406             if ('"'==b)
407             {
408                 if (quote)
409                 {
410                     quote=false;
411                 }
412                 else
413                 {
414                     quote=true;
415                 }
416                 
417                 switch(state)
418                 {
419                     case 11:
420                         builder.append(b);break;
421                     case 10:
422                         break;
423                     case 9:
424                         builder=new StringBuilder();
425                         builder.append(value,0,start+1);
426                         state=10;
427                         break;
428                     default:
429                         start=i;
430                         state=0;           
431                 }
432                 continue;
433             }
434             
435             if (quote)
436             {
437                 if (builder!=null && state!=10)
438                     builder.append(b);
439                 continue;
440             }
441
442             switch(state)
443             {
444                 case 0:
445                     if (';'==b)
446                         state=1;
447                     else if (' '!=b)
448                         start=i;
449                     break;
450
451                 case 1: if ('c'==b) state=2; else if (' '!=b) state=0; break;
452                 case 2: if ('h'==b) state=3; else state=0;break;
453                 case 3: if ('a'==b) state=4; else state=0;break;
454                 case 4: if ('r'==b) state=5; else state=0;break;
455                 case 5: if ('s'==b) state=6; else state=0;break;
456                 case 6: if ('e'==b) state=7; else state=0;break;
457                 case 7: if ('t'==b) state=8; else state=0;break;
458                 case 8: if ('='==b) state=9; else if (' '!=b) state=0; break;
459
460                 case 9:
461                     if (' '==b)
462                         break;
463                     builder=new StringBuilder();
464                     builder.append(value,0,start+1);
465                     state=10;
466                     break;
467
468                 case 10:
469                     if (';'==b)
470                     {
471                         builder.append(b);
472                         state=11;
473                     }
474                     break;
475                 case 11:
476                     if (' '!=b)
477                         builder.append(b);
478             }
479         }
480         if (builder==null)
481             return value;
482         return builder.toString();
483
484     }
485 }