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