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