KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sslexplorer > core > RequestParameterMap


1 /*
2  * SSL-Explorer
3  *
4  * Copyright (C) 2003-2006 3SP LTD. All Rights Reserved
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2 of
9  * the License, or (at your option) any later version.
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */

19             
20
21 /*
22  * Parts of the code that deal with multipart/form-data parsing are (loosely)
23  * based on Jettys MultipartRequest. See license below.
24  *
25  * 24/02/2005 - brett@3sp.com
26  *
27  */

28
29 //========================================================================
30
//$Id$
31
//Copyright 1996-2004 Mort Bay Consulting Pty. Ltd.
32
//------------------------------------------------------------------------
33
//Licensed under the Apache License, Version 2.0 (the "License");
34
//you may not use this file except in compliance with the License.
35
//You may obtain a copy of the License at
36
//http://www.apache.org/licenses/LICENSE-2.0
37
//Unless required by applicable law or agreed to in writing, software
38
//distributed under the License is distributed on an "AS IS" BASIS,
39
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
40
//See the License for the specific language governing permissions and
41
//limitations under the License.
42
//========================================================================
43
package com.sslexplorer.core;
44
45 import java.io.ByteArrayOutputStream JavaDoc;
46 import java.io.File JavaDoc;
47 import java.io.FileInputStream JavaDoc;
48 import java.io.FileNotFoundException JavaDoc;
49 import java.io.FileOutputStream JavaDoc;
50 import java.io.IOException JavaDoc;
51 import java.io.InputStream JavaDoc;
52 import java.io.OutputStream JavaDoc;
53 import java.net.MalformedURLException JavaDoc;
54 import java.net.URL JavaDoc;
55 import java.util.ArrayList JavaDoc;
56 import java.util.Iterator JavaDoc;
57 import java.util.List JavaDoc;
58 import java.util.Map JavaDoc;
59 import java.util.StringTokenizer JavaDoc;
60 import java.util.TreeMap JavaDoc;
61
62 import org.apache.commons.logging.Log;
63 import org.apache.commons.logging.LogFactory;
64 import org.mortbay.util.MultiMap;
65
66 import com.sslexplorer.boot.RequestHandlerRequest;
67 import com.sslexplorer.boot.Util;
68 import com.sslexplorer.policyframework.LaunchSession;
69
70 /**
71  *
72  * Extracts request parameters given a {@link RequestHandlerRequest}.
73  * <p>
74  * If the request has a <i>Content type</i> of <b>multipart/form-data</b>,
75  * then parameters are read from the input stream. Requests of this type will
76  * return <code>true</code> for {@link #isFormData()}.
77  * <p>
78  * If the request does not have a content type of <b>multipart/form-data</b>,
79  * then the request parameters are read directly from the
80  * {@link RequestHandlerRequest#getParameters()} method. Requests of this type
81  * will return <code>false</code> for {@link #isFormData()}.
82  * <p>
83  * <b>IMPORTANT</b>. Names and values stored in this map must <b>NOT</b> be
84  * encoded in any way. Names and values should be encoded as and when needed.
85  * <p>
86  * The <i>Proxied URL</i> (i.e. the actual URL to load) will also be extracted
87  * from the request and can be retrieved using {@link #getProxiedURL()}.
88  *
89  * @author Lee David Painter <a HREF="mailto:
90  * brett@3sp.com">&lt;brett@3sp.com&gt;</a>
91  * @author Brett Smith <a HREF="mailto: brett@3sp.com">&lt;brett@3sp.com&gt;</a>
92  */

93 public class RequestParameterMap extends MultiMap {
94
95     final static Log log = LogFactory.getLog(RequestParameterMap.class);
96
97     /**
98      * Get the encoding name for ISO_8859_1. This may be configured using the
99      * system property <code>ISO_8859_1</code>.
100      */

101     public final static String JavaDoc ISO_8859_1;
102     static {
103         String JavaDoc iso = System.getProperty("ISO_8859_1");
104         if (iso != null)
105             ISO_8859_1 = iso;
106         else {
107             try {
108                 new String JavaDoc(new byte[] { (byte) 20 }, "ISO-8859-1");
109                 iso = "ISO-8859-1";
110             } catch (java.io.UnsupportedEncodingException JavaDoc e) {
111                 iso = "ISO8859_1";
112             }
113             ISO_8859_1 = iso;
114         }
115     }
116
117     private static final long serialVersionUID = -2005125971106236972L;
118
119     // Private instance variables
120

121     private LineInput input;
122     private String JavaDoc boundary;
123     private byte[] boundaryBytes;
124     private boolean finished;
125     private File JavaDoc multipartFile;
126     private long multipartLength;
127     private int ch;
128     private URL JavaDoc proxiedURLBase, proxiedURL;
129     private String JavaDoc launchId;
130
131     /**
132      * Constructor.
133      */

134     public RequestParameterMap() {
135         super();
136         ch = -2;
137     }
138
139     /**
140      * Constructor.
141      *
142      * @param request request to extract parameters from
143      * @throws IOException
144      */

145     public RequestParameterMap(RequestHandlerRequest request) throws IOException JavaDoc {
146         this();
147         String JavaDoc contentType = request.getField("content-type");
148         
149         if (contentType != null && contentType.startsWith("multipart/form-data")) {
150             initMultipart(contentType, request.getInputStream());
151         }
152         
153         String JavaDoc requestPath = request.getPath();
154
155
156             /* The launch session and URL may be provided in one of two ways.
157              *
158              * 1. As a request to /replacementProxyEngine with the launch session
159              * and target URL provided as sslx_launchId and sslx_url respectively
160              *
161              * 2. In the new format /replacementProxyEngine/[launchId]/[encodedURL]
162              */

163             
164             if(requestPath != null && requestPath.startsWith("/replacementProxyEngine/")) {
165                 int idx = requestPath.indexOf('/', 1);
166                 int idx2 = requestPath.indexOf('/', idx + 1);
167                 launchId = requestPath.substring(idx + 1, idx2);
168                 parseProxiedURL(requestPath.substring(idx2 + 1));
169             }
170             
171             // Parameter names and values from the request are not encoded so
172
// can be placed straight in the map
173
Map JavaDoc params = request.getParameters();
174             for (Iterator JavaDoc e = params.keySet().iterator(); e.hasNext();) {
175                 String JavaDoc n = (String JavaDoc) e.next();
176
177                 Object JavaDoc obj = params.get(n);
178
179                 if(!checkForSSLXUrl(n, obj)) {
180                     if (obj instanceof String JavaDoc[]) {
181                         put(n, ((String JavaDoc[]) obj)[0]);
182                     } else if (obj instanceof String JavaDoc) {
183                         put(n, obj.toString());
184                     } else
185                         log.warn("Parameter value is an unexepected type " + obj.getClass().getName());
186                 }
187             }
188         
189     }
190
191     /**
192      * Get the ID of the {@link LaunchSession} that launched the web forward
193      * this request is part of. This is specified by the request parameter with
194      * the name of the contstant {@link LaunchSession#LONG_LAUNCH_ID} value.
195      *
196      * @return launch ID
197      */

198     public String JavaDoc getLaunchId() {
199         return launchId;
200     }
201
202     /**
203      * Get if this map was initialised from a request with a content type of
204      * <b>multipart/form-data</b>.
205      *
206      * @return is form data
207      */

208     public boolean isFormData() {
209         return multipartFile != null;
210     }
211     
212     /**
213      * TODO: This current calls getParameter to ensure that only a single paramter is
214      * returned. Implementations that use this type of Map should be changed to
215      * support multiple values for a single key, or to use getParameter if they
216      * do not require multiple value support.
217      * @param name
218      * @return the first value in the list
219      */

220     public Object JavaDoc get(Object JavaDoc name) {
221         return getParameter(name);
222     }
223
224     /**
225      * If this map was initialised from a <b>multipart/form-data</b> request,
226      * then this method returns an input stream of the content received. This
227      * content is actually read from a temporary file which is deleted when the
228      * stream is closed.
229      *
230      * @return form data stream
231      * @throws IOException if map not initialised from <b>multipart/form-data</b>
232      * request
233      * @throws FileNotFoundException
234      */

235     public InputStream JavaDoc getFormData() throws IOException JavaDoc, FileNotFoundException JavaDoc {
236         if (multipartFile == null) {
237             throw new IOException JavaDoc("This request was not of type multipart/form-data.");
238         }
239         return new FileMultipartInputStream(multipartFile);
240     }
241
242     /**
243      * If this map was initialised from a <b>multipart/form-data</b> request,
244      * then this method returns the number of bytes in the content.
245      *
246      * @return form data length
247      * @throws IOException
248      */

249     public long getFormDataLength() throws IOException JavaDoc {
250         if (multipartFile == null) {
251             throw new IOException JavaDoc("This request was not of type multipart/form-data.");
252         }
253         return multipartLength;
254     }
255
256     /**
257      * Get the <i>Proxied URL</i>, i.e. the URL of the page to actually load
258      * from the target server. This will include any request parameters supplied
259      * when a GET rquest.
260      *
261      * @return proxied URL
262      */

263     public URL JavaDoc getProxiedURL() {
264         return proxiedURL;
265     }
266
267     /**
268      * Get the <i>Proxied URL base</i>, i.e. the URL of the page to actually
269      * load from the target server <b>excluding</b> any request parameters if
270      * the request is a GET.
271      *
272      * @return proxied URL base
273      */

274     public URL JavaDoc getProxiedURLBase() {
275         return proxiedURLBase;
276     }
277
278     /**
279      * Get a parameters value given its name. The value returned is unencoded.
280      *
281      * @param name parameter name
282      * @return encoded value
283      */

284     public String JavaDoc getParameter(Object JavaDoc name) {
285         List JavaDoc list = getValues(name);
286         if(list!=null && list.size() >= 1) {
287             return (String JavaDoc) list.get(0);
288         } else
289             return null;
290     }
291     
292     public List JavaDoc getParameterValues(Object JavaDoc name) {
293         return getValues(name);
294     }
295
296     /**
297      * Get an {@link Iterator} of all parameters names contained in this map.
298      *
299      * @return parameters name
300      */

301     public Iterator JavaDoc getParameterNames() {
302         return keySet().iterator();
303     }
304
305     /*
306      * public String[] getParameterValues(String name) { return (String[])
307      * super.get(name); }
308      */

309
310     private void readBytes(InputStream JavaDoc in, OutputStream JavaDoc out, OutputStream JavaDoc recorded) throws IOException JavaDoc {
311
312         int c;
313         boolean cr = false;
314         boolean lf = false;
315
316         // loop for all lines`
317
while (true) {
318             int b = 0;
319             while ((c = (ch != -2) ? ch : readWrite(in, recorded)) != -1) {
320                 multipartLength++;
321                 ch = -2;
322
323                 // look for CR and/or LF
324
if (c == 13 || c == 10) {
325                     if (c == 13) {
326                         ch = readWrite(in, recorded);
327                         multipartLength++;
328                     }
329                     break;
330                 }
331
332                 // look for boundary
333
if (b >= 0 && b < boundaryBytes.length && c == boundaryBytes[b])
334                     b++;
335                 else {
336                     // this is not a boundary
337
if (cr && out != null)
338                         out.write(13);
339                     if (lf && out != null)
340                         out.write(10);
341                     cr = lf = false;
342
343                     if (b > 0 && out != null)
344                         out.write(boundaryBytes, 0, b);
345                     b = -1;
346
347                     if (out != null)
348                         out.write(c);
349                 }
350             }
351
352             // check partial boundary
353
if ((b > 0 && b < boundaryBytes.length - 2) || (b == boundaryBytes.length - 1)) {
354                 if (cr && out != null)
355                     out.write(13);
356                 if (lf && out != null)
357                     out.write(10);
358                 cr = lf = false;
359                 if (out != null)
360                     out.write(boundaryBytes, 0, b);
361                 b = -1;
362             }
363
364             // boundary match
365
if (b > 0 || c == -1) {
366                 if (b == boundaryBytes.length)
367                     finished = true;
368                 if (ch == 10)
369                     ch = -2;
370                 break;
371             }
372
373             // handle CR LF
374
if (cr && out != null)
375                 out.write(13);
376             if (lf && out != null)
377                 out.write(10);
378             cr = (c == 13);
379             lf = (c == 10 || ch == 10);
380             if (ch == 10)
381                 ch = -2;
382         }
383     }
384
385     private int readWrite(InputStream JavaDoc in, OutputStream JavaDoc out) throws IOException JavaDoc {
386         int i = in.read();
387         if (i != -1 && out != null) {
388             out.write(i);
389         }
390         return i;
391
392     }
393
394     private boolean checkForSSLXUrl(String JavaDoc n, Object JavaDoc params) {
395         String JavaDoc val;
396
397         if (params instanceof String JavaDoc[]) {
398             val = ((String JavaDoc[]) params)[0];
399         } else
400             val = (String JavaDoc) params;
401
402         if (n.equals("sslex_url")) {
403             parseProxiedURL(val);
404             return true;
405         } else if (n.equals(LaunchSession.LONG_LAUNCH_ID)) {
406             launchId = val;
407             return true;
408         }
409         return false;
410     }
411
412     private void initMultipart(String JavaDoc contentType, InputStream JavaDoc in) throws IOException JavaDoc, FileNotFoundException JavaDoc {
413         multipartFile = File.createTempFile("mpr", ".tmp");
414         FileOutputStream JavaDoc mpOut = new FileOutputStream JavaDoc(multipartFile);
415
416         try {
417
418             input = new LineInput(in);
419             boundary = "--" + Util.valueOfNameValuePair(contentType.substring(contentType.indexOf("boundary=")));
420             boundaryBytes = (boundary + "--").getBytes(ISO_8859_1);// Get
421
// first
422
// boundary
423
String JavaDoc line = input.readLine();
424             if (!line.equals(boundary)) {
425                 throw new IOException JavaDoc("Missing initial multi part boundary");
426             }
427
428             byte[] buf = (line + "\r\n").getBytes();
429             multipartLength += buf.length;
430             mpOut.write(buf);
431
432             while (!finished) {
433                 String JavaDoc contentDisposition = null;
434
435                 Map JavaDoc<String JavaDoc, List JavaDoc<String JavaDoc>> parms = new TreeMap JavaDoc<String JavaDoc, List JavaDoc<String JavaDoc>>();
436                 while ((line = input.readLine()) != null) {
437                     if (line.length() == 0)
438                         break;
439                     int idx = line.indexOf(':', 0);
440                     if (idx > 0) {
441                         String JavaDoc key = line.substring(0, idx).trim();
442                         String JavaDoc value = line.substring(idx + 1, line.length()).trim();
443
444                         List JavaDoc<String JavaDoc> cur = (List JavaDoc<String JavaDoc>) parms.get(key);
445                         if (cur == null) {
446                             cur = new ArrayList JavaDoc<String JavaDoc>();
447                             parms.put(key, cur);
448                         }
449                         cur.add(value);
450
451                         if (key.equalsIgnoreCase("content-disposition"))
452                             contentDisposition = value;
453                     }
454                 }
455                 boolean form = false;
456                 if (contentDisposition == null) {
457                     throw new IOException JavaDoc("Missing content-disposition");
458                 }
459                 StringTokenizer JavaDoc tok = new StringTokenizer JavaDoc(contentDisposition, ";");
460                 String JavaDoc name = null;
461                 String JavaDoc filename = null;
462                 while (tok.hasMoreTokens()) {
463                     String JavaDoc t = tok.nextToken().trim();
464                     String JavaDoc tl = t.toLowerCase();
465                     if (t.startsWith("form-data"))
466                         form = true;
467                     else if (tl.startsWith("name="))
468                         name = Util.valueOfNameValuePair(t);
469                     else if (tl.startsWith("filename="))
470                         filename = Util.valueOfNameValuePair(t);
471                 }
472
473                 // Check disposition
474
if (!form) {
475                     log.warn("Non form-data part in multipart/form-data");
476                     continue;
477                 }
478
479                 if (name.equals("sslex_url")) {
480                     ByteArrayOutputStream JavaDoc baos = new ByteArrayOutputStream JavaDoc();
481                     readBytes(input, baos, null);
482                     parseProxiedURL(new String JavaDoc(baos.toString()));
483                 } else {
484                     for (Iterator JavaDoc i = parms.keySet().iterator(); i.hasNext();) {
485                         String JavaDoc key = (String JavaDoc) i.next();
486                         List JavaDoc list = (List JavaDoc) parms.get(key);
487                         for (Iterator JavaDoc j = list.iterator(); j.hasNext();) {
488                             String JavaDoc val = (String JavaDoc) j.next();
489                             buf = (key + ": " + val + "\r\n").getBytes();
490                             multipartLength += buf.length;
491                             mpOut.write(buf);
492                         }
493                     }
494                     buf = "\r\n".getBytes();
495                     multipartLength += buf.length;
496                     mpOut.write(buf);
497
498                     if (filename != null) {
499                         readBytes(input, null, mpOut);
500                     } else {
501                         ByteArrayOutputStream JavaDoc baos = new ByteArrayOutputStream JavaDoc();
502                         readBytes(input, baos, mpOut);
503                         put(Util.urlDecode(name), baos.toString());
504                     }
505                 }
506             }
507         } finally {
508             Util.closeStream(mpOut);
509         }
510
511     }
512     
513     public void parseProxiedURL(String JavaDoc location) {
514         try {
515             proxiedURL = new URL JavaDoc(location);
516
517             // Extract parameters from the proxied URL
518
String JavaDoc query = proxiedURL.getQuery();
519             if (query != null) {
520                 proxiedURLBase = new URL JavaDoc(proxiedURL.getProtocol(), proxiedURL.getHost(), proxiedURL.getPort() < 1 ? -1 : proxiedURL.getPort() , proxiedURL.getFile());
521                 StringTokenizer JavaDoc t = new StringTokenizer JavaDoc(query, "&");
522                 while (t.hasMoreTokens()) {
523                     String JavaDoc parm = t.nextToken();
524                     int pidx = parm.indexOf('=');
525                     String JavaDoc name = pidx == -1 ? parm : parm.substring(0, pidx);
526                     String JavaDoc value = pidx == -1 ? "" : parm.substring(pidx + 1);
527                     put(name, value);
528                 }
529             } else {
530                 proxiedURLBase = proxiedURL;
531             }
532         } catch (MalformedURLException JavaDoc murle) {
533             log.error("Invalid proxied URL '" + location + "'");
534         }
535     }
536
537     class FileMultipartInputStream extends FileInputStream JavaDoc {
538
539         private File JavaDoc file;
540
541         FileMultipartInputStream(File JavaDoc file) throws FileNotFoundException JavaDoc {
542             super(file);
543             this.file = file;
544         }
545
546         public void close() throws IOException JavaDoc {
547             super.close();
548             file.delete();
549         }
550
551     }
552 }
553
Popular Tags