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