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