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