1
2
3
4
5
6
7
8
9
10
11
12
13
14 package org.mortbay.servlet;
15
16 import java.io.BufferedInputStream;
17 import java.io.BufferedOutputStream;
18 import java.io.ByteArrayOutputStream;
19 import java.io.File;
20 import java.io.FileNotFoundException;
21 import java.io.FileOutputStream;
22 import java.io.IOException;
23 import java.io.OutputStream;
24 import java.io.UnsupportedEncodingException;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.Enumeration;
28 import java.util.Iterator;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.StringTokenizer;
32
33 import javax.servlet.Filter;
34 import javax.servlet.FilterChain;
35 import javax.servlet.FilterConfig;
36 import javax.servlet.ServletContext;
37 import javax.servlet.ServletException;
38 import javax.servlet.ServletRequest;
39 import javax.servlet.ServletResponse;
40 import javax.servlet.http.HttpServletRequest;
41 import javax.servlet.http.HttpServletRequestWrapper;
42
43 import org.mortbay.util.LazyList;
44 import org.mortbay.util.MultiMap;
45 import org.mortbay.util.StringUtil;
46 import org.mortbay.util.TypeUtil;
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63 public class MultiPartFilter implements Filter
64 {
65 private final static String FILES ="org.mortbay.servlet.MultiPartFilter.files";
66 private File tempdir;
67 private boolean _deleteFiles;
68 private ServletContext _context;
69 private int _fileOutputBuffer = 0;
70
71
72
73
74
75 public void init(FilterConfig filterConfig) throws ServletException
76 {
77 tempdir=(File)filterConfig.getServletContext().getAttribute("javax.servlet.context.tempdir");
78 _deleteFiles="true".equals(filterConfig.getInitParameter("deleteFiles"));
79 String fileOutputBuffer = filterConfig.getInitParameter("fileOutputBuffer");
80 if(fileOutputBuffer!=null)
81 _fileOutputBuffer = Integer.parseInt(fileOutputBuffer);
82 _context=filterConfig.getServletContext();
83 }
84
85
86
87
88
89
90 public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain)
91 throws IOException, ServletException
92 {
93 HttpServletRequest srequest=(HttpServletRequest)request;
94 if(srequest.getContentType()==null||!srequest.getContentType().startsWith("multipart/form-data"))
95 {
96 chain.doFilter(request,response);
97 return;
98 }
99
100 BufferedInputStream in = new BufferedInputStream(request.getInputStream());
101 String content_type=srequest.getContentType();
102
103
104
105 String boundary="--"+value(content_type.substring(content_type.indexOf("boundary=")));
106 byte[] byteBoundary=(boundary+"--").getBytes(StringUtil.__ISO_8859_1);
107
108 MultiMap params = new MultiMap();
109 for (Iterator i = request.getParameterMap().entrySet().iterator();i.hasNext();)
110 {
111 Map.Entry entry=(Map.Entry)i.next();
112 Object value=entry.getValue();
113 if (value instanceof String[])
114 params.addValues(entry.getKey(),(String[])value);
115 else
116 params.add(entry.getKey(),value);
117 }
118
119 try
120 {
121
122 byte[] bytes=TypeUtil.readLine(in);
123 String line=bytes==null?null:new String(bytes,"UTF-8");
124 if(line==null || !line.equals(boundary))
125 {
126 throw new IOException("Missing initial multi part boundary");
127 }
128
129
130 boolean lastPart=false;
131 String content_disposition=null;
132 while(!lastPart)
133 {
134 while(true)
135 {
136 bytes=TypeUtil.readLine(in);
137
138 if(bytes==null || bytes.length==0)
139 break;
140 line=new String(bytes,"UTF-8");
141
142
143 int c=line.indexOf(':',0);
144 if(c>0)
145 {
146 String key=line.substring(0,c).trim().toLowerCase();
147 String value=line.substring(c+1,line.length()).trim();
148 if(key.equals("content-disposition"))
149 content_disposition=value;
150 }
151 }
152
153 boolean form_data=false;
154 if(content_disposition==null)
155 {
156 throw new IOException("Missing content-disposition");
157 }
158
159 StringTokenizer tok=new StringTokenizer(content_disposition,";");
160 String name=null;
161 String filename=null;
162 while(tok.hasMoreTokens())
163 {
164 String t=tok.nextToken().trim();
165 String tl=t.toLowerCase();
166 if(t.startsWith("form-data"))
167 form_data=true;
168 else if(tl.startsWith("name="))
169 name=value(t);
170 else if(tl.startsWith("filename="))
171 filename=value(t);
172 }
173
174
175 if(!form_data)
176 {
177 continue;
178 }
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 private String value(String nameEqualsValue)
329 {
330 String value=nameEqualsValue.substring(nameEqualsValue.indexOf('=')+1).trim();
331 int i=value.indexOf(';');
332 if(i>0)
333 value=value.substring(0,i);
334 if(value.startsWith("\""))
335 {
336 value=value.substring(1,value.indexOf('"',1));
337 }
338 else
339 {
340 i=value.indexOf(' ');
341 if(i>0)
342 value=value.substring(0,i);
343 }
344 return value;
345 }
346
347
348
349
350
351 public void destroy()
352 {
353 }
354
355 private static class Wrapper extends HttpServletRequestWrapper
356 {
357 String encoding="UTF-8";
358 MultiMap map;
359
360
361
362
363
364 public Wrapper(HttpServletRequest request, MultiMap map)
365 {
366 super(request);
367 this.map=map;
368 }
369
370
371
372
373
374 public int getContentLength()
375 {
376 return 0;
377 }
378
379
380
381
382
383 public String getParameter(String name)
384 {
385 Object o=map.get(name);
386 if (!(o instanceof byte[]) && LazyList.size(o)>0)
387 o=LazyList.get(o,0);
388
389 if (o instanceof byte[])
390 {
391 try
392 {
393 String s=new String((byte[])o,encoding);
394 return s;
395 }
396 catch(Exception e)
397 {
398 e.printStackTrace();
399 }
400 }
401 else if (o!=null)
402 return String.valueOf(o);
403 return null;
404 }
405
406
407
408
409
410 public Map getParameterMap()
411 {
412 return Collections.unmodifiableMap(map.toStringArrayMap());
413 }
414
415
416
417
418
419 public Enumeration getParameterNames()
420 {
421 return Collections.enumeration(map.keySet());
422 }
423
424
425
426
427
428 public String[] getParameterValues(String name)
429 {
430 List l=map.getValues(name);
431 if (l==null || l.size()==0)
432 return new String[0];
433 String[] v = new String[l.size()];
434 for (int i=0;i<l.size();i++)
435 {
436 Object o=l.get(i);
437 if (o instanceof byte[])
438 {
439 try
440 {
441 v[i]=new String((byte[])o,encoding);
442 }
443 catch(Exception e)
444 {
445 e.printStackTrace();
446 }
447 }
448 else if (o instanceof String)
449 v[i]=(String)o;
450 }
451 return v;
452 }
453
454
455
456
457
458 public void setCharacterEncoding(String enc)
459 throws UnsupportedEncodingException
460 {
461 encoding=enc;
462 }
463 }
464 }