1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.mortbay.servlet;
16
17 import java.io.File;
18 import java.io.IOException;
19 import java.io.InputStream;
20 import java.io.OutputStream;
21 import java.util.Enumeration;
22 import java.util.HashMap;
23 import java.util.Map;
24
25 import javax.servlet.ServletException;
26 import javax.servlet.http.HttpServlet;
27 import javax.servlet.http.HttpServletRequest;
28 import javax.servlet.http.HttpServletResponse;
29
30 import org.mortbay.log.Log;
31 import org.mortbay.util.IO;
32 import org.mortbay.util.StringUtil;
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56 public class CGI extends HttpServlet
57 {
58 private boolean _ok;
59 private File _docRoot;
60 private String _path;
61 private String _cmdPrefix;
62 private EnvList _env;
63 private boolean _ignoreExitState;
64
65
66 public void init() throws ServletException
67 {
68 _env=new EnvList();
69 _cmdPrefix=getInitParameter("commandPrefix");
70
71 String tmp=getInitParameter("cgibinResourceBase");
72 if (tmp==null)
73 {
74 tmp=getInitParameter("resourceBase");
75 if (tmp==null)
76 tmp=getServletContext().getRealPath("/");
77 }
78
79 if (tmp==null)
80 {
81 Log.warn("CGI: no CGI bin !");
82 return;
83 }
84
85 File dir=new File(tmp);
86 if (!dir.exists())
87 {
88 Log.warn("CGI: CGI bin does not exist - "+dir);
89 return;
90 }
91
92 if (!dir.canRead())
93 {
94 Log.warn("CGI: CGI bin is not readable - "+dir);
95 return;
96 }
97
98 if (!dir.isDirectory())
99 {
100 Log.warn("CGI: CGI bin is not a directory - "+dir);
101 return;
102 }
103
104 try
105 {
106 _docRoot=dir.getCanonicalFile();
107 }
108 catch (IOException e)
109 {
110 Log.warn("CGI: CGI bin failed - "+dir,e);
111 return;
112 }
113
114 _path=getInitParameter("Path");
115 if (_path!=null)
116 _env.set("PATH",_path);
117
118 _ignoreExitState="true".equalsIgnoreCase(getInitParameter("ignoreExitState"));
119 Enumeration e=getInitParameterNames();
120 while (e.hasMoreElements())
121 {
122 String n=(String)e.nextElement();
123 if (n!=null&&n.startsWith("ENV_"))
124 _env.set(n.substring(4),getInitParameter(n));
125 }
126 if(!_env.envMap.containsKey("SystemRoot"))
127 {
128 String os = System.getProperty("os.name");
129 if (os!=null && os.toLowerCase().indexOf("windows")!=-1)
130 {
131 String windir = System.getProperty("windir");
132 _env.set("SystemRoot", windir!=null ? windir : "C:\\WINDOWS");
133 }
134 }
135
136 _ok=true;
137 }
138
139
140 public void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
141 {
142 if (!_ok)
143 {
144 res.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
145 return;
146 }
147
148 String pathInContext=StringUtil.nonNull(req.getServletPath())+StringUtil.nonNull(req.getPathInfo());
149
150 if (Log.isDebugEnabled())
151 {
152 Log.debug("CGI: ContextPath : "+req.getContextPath());
153 Log.debug("CGI: ServletPath : "+req.getServletPath());
154 Log.debug("CGI: PathInfo : "+req.getPathInfo());
155 Log.debug("CGI: _docRoot : "+_docRoot);
156 Log.debug("CGI: _path : "+_path);
157 Log.debug("CGI: _ignoreExitState: "+_ignoreExitState);
158 }
159
160
161
162
163
164 String both=pathInContext;
165 String first=both;
166 String last="";
167
168 File exe=new File(_docRoot,first);
169
170 while ((first.endsWith("/")||!exe.exists())&&first.length()>=0)
171 {
172 int index=first.lastIndexOf('/');
173
174 first=first.substring(0,index);
175 last=both.substring(index,both.length());
176 exe=new File(_docRoot,first);
177 }
178
179 if (first.length()==0||!exe.exists()||exe.isDirectory()||!exe.getCanonicalPath().equals(exe.getAbsolutePath()))
180 {
181 res.sendError(404);
182 }
183 else
184 {
185 if (Log.isDebugEnabled())
186 {
187 Log.debug("CGI: script is "+exe);
188 Log.debug("CGI: pathInfo is "+last);
189 }
190 exec(exe,last,req,res);
191 }
192 }
193
194
195
196
197
198 private void exec(File command, String pathInfo, HttpServletRequest req, HttpServletResponse res) throws IOException
199 {
200 String path=command.getAbsolutePath();
201 File dir=command.getParentFile();
202 String scriptName=req.getRequestURI().substring(0,req.getRequestURI().length()-pathInfo.length());
203 String scriptPath=getServletContext().getRealPath(scriptName);
204 String pathTranslated=req.getPathTranslated();
205
206 int len=req.getContentLength();
207 if (len<0)
208 len=0;
209 if ((pathTranslated==null)||(pathTranslated.length()==0))
210 pathTranslated=path;
211
212 EnvList env=new EnvList(_env);
213
214
215
216 env.set("AUTH_TYPE",req.getAuthType());
217 env.set("CONTENT_LENGTH",Integer.toString(len));
218 env.set("CONTENT_TYPE",req.getContentType());
219 env.set("GATEWAY_INTERFACE","CGI/1.1");
220 if ((pathInfo!=null)&&(pathInfo.length()>0))
221 {
222 env.set("PATH_INFO",pathInfo);
223 }
224 env.set("PATH_TRANSLATED",pathTranslated);
225 env.set("QUERY_STRING",req.getQueryString());
226 env.set("REMOTE_ADDR",req.getRemoteAddr());
227 env.set("REMOTE_HOST",req.getRemoteHost());
228
229
230
231
232
233 env.set("REMOTE_USER",req.getRemoteUser());
234 env.set("REQUEST_METHOD",req.getMethod());
235 env.set("SCRIPT_NAME",scriptName);
236 env.set("SCRIPT_FILENAME",scriptPath);
237 env.set("SERVER_NAME",req.getServerName());
238 env.set("SERVER_PORT",Integer.toString(req.getServerPort()));
239 env.set("SERVER_PROTOCOL",req.getProtocol());
240 env.set("SERVER_SOFTWARE",getServletContext().getServerInfo());
241
242 Enumeration enm=req.getHeaderNames();
243 while (enm.hasMoreElements())
244 {
245 String name=(String)enm.nextElement();
246 String value=req.getHeader(name);
247 env.set("HTTP_"+name.toUpperCase().replace('-','_'),value);
248 }
249
250
251 env.set("HTTPS",(req.isSecure()?"ON":"OFF"));
252
253
254
255
256
257
258
259 String execCmd=path;
260 if ((execCmd.charAt(0)!='"')&&(execCmd.indexOf(" ")>=0))
261 execCmd="\""+execCmd+"\"";
262 if (_cmdPrefix!=null)
263 execCmd=_cmdPrefix+" "+execCmd;
264
265 Process p=(dir==null)?Runtime.getRuntime().exec(execCmd,env.getEnvArray()):Runtime.getRuntime().exec(execCmd,env.getEnvArray(),dir);
266
267
268 final InputStream inFromReq=req.getInputStream();
269 final OutputStream outToCgi=p.getOutputStream();
270 final int inLength=len;
271
272 IO.copyThread(p.getErrorStream(),System.err);
273
274 new Thread(new Runnable()
275 {
276 public void run()
277 {
278 try
279 {
280 if (inLength>0)
281 IO.copy(inFromReq,outToCgi,inLength);
282 outToCgi.close();
283 }
284 catch (IOException e)
285 {
286 Log.ignore(e);
287 }
288 }
289 }).start();
290
291
292
293 OutputStream os = null;
294 try
295 {
296
297
298 String line=null;
299 InputStream inFromCgi=p.getInputStream();
300
301
302
303 while( (line = getTextLineFromStream( inFromCgi )).length() > 0 )
304 {
305 if (!line.startsWith("HTTP"))
306 {
307 int k=line.indexOf(':');
308 if (k>0)
309 {
310 String key=line.substring(0,k).trim();
311 String value = line.substring(k+1).trim();
312 if ("Location".equals(key))
313 {
314 res.sendRedirect(value);
315 }
316 else if ("Status".equals(key))
317 {
318 String[] token = value.split( " " );
319 int status=Integer.parseInt(token[0]);
320 res.setStatus(status);
321 }
322 else
323 {
324
325 res.addHeader(key,value);
326 }
327 }
328 }
329 }
330
331 os = res.getOutputStream();
332 IO.copy(inFromCgi, os);
333 p.waitFor();
334
335 if (!_ignoreExitState)
336 {
337 int exitValue=p.exitValue();
338 if (0!=exitValue)
339 {
340 Log.warn("Non-zero exit status ("+exitValue+") from CGI program: "+path);
341 if (!res.isCommitted())
342 res.sendError(500,"Failed to exec CGI");
343 }
344 }
345 }
346 catch (IOException e)
347 {
348
349
350 Log.debug("CGI: Client closed connection!");
351 }
352 catch (InterruptedException ie)
353 {
354 Log.debug("CGI: interrupted!");
355 }
356 finally
357 {
358 if( os != null )
359 {
360 try
361 {
362 os.close();
363 }
364 catch(Exception e)
365 {
366 Log.ignore(e);
367 }
368 }
369 os = null;
370 p.destroy();
371
372 }
373 }
374
375
376
377
378
379
380
381 private String getTextLineFromStream( InputStream is ) throws IOException {
382 StringBuffer buffer = new StringBuffer();
383 int b;
384
385 while( (b = is.read()) != -1 && b != (int) '\n' ) {
386 buffer.append( (char) b );
387 }
388 return buffer.toString().trim();
389 }
390
391
392
393
394 private static class EnvList
395 {
396 private Map envMap;
397
398 EnvList()
399 {
400 envMap=new HashMap();
401 }
402
403 EnvList(EnvList l)
404 {
405 envMap=new HashMap(l.envMap);
406 }
407
408
409
410
411 public void set(String name, String value)
412 {
413 envMap.put(name,name+"="+StringUtil.nonNull(value));
414 }
415
416
417 public String[] getEnvArray()
418 {
419 return (String[])envMap.values().toArray(new String[envMap.size()]);
420 }
421
422 public String toString()
423 {
424 return envMap.toString();
425 }
426 }
427 }