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