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