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