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