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