1
2
3
4
5
6
7
8
9
10
11
12
13 package org.eclipse.jetty.servlets;
14
15 import java.io.BufferedInputStream;
16 import java.io.BufferedOutputStream;
17 import java.io.ByteArrayOutputStream;
18 import java.io.File;
19 import java.io.FileOutputStream;
20 import java.io.IOException;
21 import java.io.OutputStream;
22 import java.io.UnsupportedEncodingException;
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.Enumeration;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.StringTokenizer;
30
31 import javax.servlet.Filter;
32 import javax.servlet.FilterChain;
33 import javax.servlet.FilterConfig;
34 import javax.servlet.ServletContext;
35 import javax.servlet.ServletException;
36 import javax.servlet.ServletRequest;
37 import javax.servlet.ServletResponse;
38 import javax.servlet.http.HttpServletRequest;
39 import javax.servlet.http.HttpServletRequestWrapper;
40
41 import org.eclipse.jetty.util.LazyList;
42 import org.eclipse.jetty.util.MultiMap;
43 import org.eclipse.jetty.util.StringUtil;
44 import org.eclipse.jetty.util.TypeUtil;
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61 public class MultiPartFilter implements Filter
62 {
63 private final static String FILES ="org.eclipse.jetty.servlet.MultiPartFilter.files";
64 private File tempdir;
65 private boolean _deleteFiles;
66 private ServletContext _context;
67 private int _fileOutputBuffer = 0;
68
69
70
71
72
73 public void init(FilterConfig filterConfig) throws ServletException
74 {
75 tempdir=(File)filterConfig.getServletContext().getAttribute("javax.servlet.context.tempdir");
76 _deleteFiles="true".equals(filterConfig.getInitParameter("deleteFiles"));
77 String fileOutputBuffer = filterConfig.getInitParameter("fileOutputBuffer");
78 if(fileOutputBuffer!=null)
79 _fileOutputBuffer = Integer.parseInt(fileOutputBuffer);
80 _context=filterConfig.getServletContext();
81 }
82
83
84
85
86
87
88 public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain)
89 throws IOException, ServletException
90 {
91 HttpServletRequest srequest=(HttpServletRequest)request;
92 if(srequest.getContentType()==null||!srequest.getContentType().startsWith("multipart/form-data"))
93 {
94 chain.doFilter(request,response);
95 return;
96 }
97
98 BufferedInputStream in = new BufferedInputStream(request.getInputStream());
99 String content_type=srequest.getContentType();
100
101
102
103 String boundary="--"+value(content_type.substring(content_type.indexOf("boundary=")));
104 byte[] byteBoundary=(boundary+"--").getBytes(StringUtil.__ISO_8859_1);
105
106 MultiMap params = new MultiMap();
107 for (Iterator i = request.getParameterMap().entrySet().iterator();i.hasNext();)
108 {
109 Map.Entry entry=(Map.Entry)i.next();
110 Object value=entry.getValue();
111 if (value instanceof String[])
112 params.addValues(entry.getKey(),(String[])value);
113 else
114 params.add(entry.getKey(),value);
115 }
116
117 try
118 {
119
120 byte[] bytes=TypeUtil.readLine(in);
121 String line=bytes==null?null:new String(bytes,"UTF-8");
122 if(line==null || !line.equals(boundary))
123 {
124 throw new IOException("Missing initial multi part boundary");
125 }
126
127
128 boolean lastPart=false;
129 String content_disposition=null;
130 while(!lastPart)
131 {
132 while(true)
133 {
134 bytes=TypeUtil.readLine(in);
135
136 if(bytes==null || bytes.length==0)
137 break;
138 line=new String(bytes,"UTF-8");
139
140
141 int c=line.indexOf(':',0);
142 if(c>0)
143 {
144 String key=line.substring(0,c).trim().toLowerCase();
145 String value=line.substring(c+1,line.length()).trim();
146 if(key.equals("content-disposition"))
147 content_disposition=value;
148 }
149 }
150
151 boolean form_data=false;
152 if(content_disposition==null)
153 {
154 throw new IOException("Missing content-disposition");
155 }
156
157 StringTokenizer tok=new StringTokenizer(content_disposition,";");
158 String name=null;
159 String filename=null;
160 while(tok.hasMoreTokens())
161 {
162 String t=tok.nextToken().trim();
163 String tl=t.toLowerCase();
164 if(t.startsWith("form-data"))
165 form_data=true;
166 else if(tl.startsWith("name="))
167 name=value(t);
168 else if(tl.startsWith("filename="))
169 filename=value(t);
170 }
171
172
173 if(!form_data)
174 {
175 continue;
176 }
177
178
179
180
181
182 if(name==null)
183 {
184 continue;
185 }
186
187 OutputStream out=null;
188 File file=null;
189 try
190 {
191 if (filename!=null && filename.length()>0)
192 {
193 file = File.createTempFile("MultiPart", "", tempdir);
194 out = new FileOutputStream(file);
195 if(_fileOutputBuffer>0)
196 out = new BufferedOutputStream(out, _fileOutputBuffer);
197 request.setAttribute(name,file);
198 params.add(name, filename);
199
200 if (_deleteFiles)
201 {
202 file.deleteOnExit();
203 ArrayList files = (ArrayList)request.getAttribute(FILES);
204 if (files==null)
205 {
206 files=new ArrayList();
207 request.setAttribute(FILES,files);
208 }
209 files.add(file);
210 }
211
212 }
213 else
214 out=new ByteArrayOutputStream();
215
216 int state=-2;
217 int c;
218 boolean cr=false;
219 boolean lf=false;
220
221
222 while(true)
223 {
224 int b=0;
225 while((c=(state!=-2)?state:in.read())!=-1)
226 {
227 state=-2;
228
229 if(c==13||c==10)
230 {
231 if(c==13)
232 state=in.read();
233 break;
234 }
235
236 if(b>=0&&b<byteBoundary.length&&c==byteBoundary[b])
237 b++;
238 else
239 {
240
241 if(cr)
242 out.write(13);
243 if(lf)
244 out.write(10);
245 cr=lf=false;
246 if(b>0)
247 out.write(byteBoundary,0,b);
248 b=-1;
249 out.write(c);
250 }
251 }
252
253 if((b>0&&b<byteBoundary.length-2)||(b==byteBoundary.length-1))
254 {
255 if(cr)
256 out.write(13);
257 if(lf)
258 out.write(10);
259 cr=lf=false;
260 out.write(byteBoundary,0,b);
261 b=-1;
262 }
263
264 if(b>0||c==-1)
265 {
266 if(b==byteBoundary.length)
267 lastPart=true;
268 if(state==10)
269 state=-2;
270 break;
271 }
272
273 if(cr)
274 out.write(13);
275 if(lf)
276 out.write(10);
277 cr=(c==13);
278 lf=(c==10||state==10);
279 if(state==10)
280 state=-2;
281 }
282 }
283 finally
284 {
285 out.close();
286 }
287
288 if (file==null)
289 {
290 bytes = ((ByteArrayOutputStream)out).toByteArray();
291 params.add(name,bytes);
292 }
293 }
294
295
296 chain.doFilter(new Wrapper(srequest,params),response);
297 }
298 finally
299 {
300 deleteFiles(request);
301 }
302 }
303
304 private void deleteFiles(ServletRequest request)
305 {
306 ArrayList files = (ArrayList)request.getAttribute(FILES);
307 if (files!=null)
308 {
309 Iterator iter = files.iterator();
310 while (iter.hasNext())
311 {
312 File file=(File)iter.next();
313 try
314 {
315 file.delete();
316 }
317 catch(Exception e)
318 {
319 _context.log("failed to delete "+file,e);
320 }
321 }
322 }
323 }
324
325
326 private String value(String nameEqualsValue)
327 {
328 String value=nameEqualsValue.substring(nameEqualsValue.indexOf('=')+1).trim();
329 int i=value.indexOf(';');
330 if(i>0)
331 value=value.substring(0,i);
332 if(value.startsWith("\""))
333 {
334 value=value.substring(1,value.indexOf('"',1));
335 }
336 else
337 {
338 i=value.indexOf(' ');
339 if(i>0)
340 value=value.substring(0,i);
341 }
342 return value;
343 }
344
345
346
347
348
349 public void destroy()
350 {
351 }
352
353
354
355 private static class Wrapper extends HttpServletRequestWrapper
356 {
357 String _encoding=StringUtil.__UTF8;
358 MultiMap _params;
359
360
361
362
363
364 public Wrapper(HttpServletRequest request, MultiMap map)
365 {
366 super(request);
367 this._params=map;
368 }
369
370
371
372
373
374 @Override
375 public int getContentLength()
376 {
377 return 0;
378 }
379
380
381
382
383
384 @Override
385 public String getParameter(String name)
386 {
387 Object o=_params.get(name);
388 if (!(o instanceof byte[]) && LazyList.size(o)>0)
389 o=LazyList.get(o,0);
390
391 if (o instanceof byte[])
392 {
393 try
394 {
395 String s=new String((byte[])o,_encoding);
396 return s;
397 }
398 catch(Exception e)
399 {
400 e.printStackTrace();
401 }
402 }
403 else if (o!=null)
404 return String.valueOf(o);
405 return null;
406 }
407
408
409
410
411
412 @Override
413 public Map getParameterMap()
414 {
415 return Collections.unmodifiableMap(_params.toStringArrayMap());
416 }
417
418
419
420
421
422 @Override
423 public Enumeration getParameterNames()
424 {
425 return Collections.enumeration(_params.keySet());
426 }
427
428
429
430
431
432 @Override
433 public String[] getParameterValues(String name)
434 {
435 List l=_params.getValues(name);
436 if (l==null || l.size()==0)
437 return new String[0];
438 String[] v = new String[l.size()];
439 for (int i=0;i<l.size();i++)
440 {
441 Object o=l.get(i);
442 if (o instanceof byte[])
443 {
444 try
445 {
446 v[i]=new String((byte[])o,_encoding);
447 }
448 catch(Exception e)
449 {
450 e.printStackTrace();
451 }
452 }
453 else if (o instanceof String)
454 v[i]=(String)o;
455 }
456 return v;
457 }
458
459
460
461
462
463 @Override
464 public void setCharacterEncoding(String enc)
465 throws UnsupportedEncodingException
466 {
467 _encoding=enc;
468 }
469 }
470 }