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