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