001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.runtime.tag; 020 021import java.util.Iterator; 022 023import lucee.commons.io.res.Resource; 024import lucee.commons.io.res.util.ResourceUtil; 025import lucee.commons.lang.SerializableObject; 026import lucee.commons.lang.StringUtil; 027import lucee.runtime.exp.ApplicationException; 028import lucee.runtime.exp.PageException; 029import lucee.runtime.exp.SecurityException; 030import lucee.runtime.ext.tag.BodyTagImpl; 031import lucee.runtime.op.Caster; 032import lucee.runtime.security.SecurityManager; 033 034/** 035* Enables CFML developers to execute a process on a server computer. 036* 037* 038* 039**/ 040public final class Execute extends BodyTagImpl { 041 042 /** Command-line arguments passed to the application. */ 043 private String arguments=null; 044 045 /** Indicates how long, in seconds, the CFML executing thread waits for the spawned process. 046 ** A timeout of 0 is equivalent to the non-blocking mode of executing. A very high timeout value is 047 ** equivalent to a blocking mode of execution. The default is 0; therefore, the CFML thread spawns 048 ** a process and returns without waiting for the process to terminate.If no output file is specified, 049 ** and the timeout value is 0, the program output is discarded. */ 050 private long timeout; 051 052 /** The full pathname of the application to execute. 053 ** Note: On Windows, you must specify the extension as part of the application's name. For example, 054 ** myapp.exe, */ 055 private String name=null; 056 057 /** The file to which to direct the output of the program. If not specified, the output is 058 ** displayed on the page from which it was called. */ 059 private Resource outputfile; 060 private Resource errorFile; 061 062 private String variable; 063 private String errorVariable; 064 065 private String body; 066 067 private boolean terminateOnTimeout=false; 068 069 @Override 070 public void release() { 071 super.release(); 072 arguments=null; 073 timeout=0L; 074 name=null; 075 outputfile=null; 076 errorFile=null; 077 variable=null; 078 errorVariable=null; 079 body=null; 080 terminateOnTimeout=false; 081 } 082 083 /** set the value arguments 084 * Command-line arguments passed to the application. 085 * @param args value to set 086 **/ 087 public void setArguments(Object args) { 088 089 if(args instanceof lucee.runtime.type.Collection) { 090 StringBuffer sb=new StringBuffer(); 091 lucee.runtime.type.Collection coll=(lucee.runtime.type.Collection)args; 092 //lucee.runtime.type.Collection.Key[] keys=coll.keys(); 093 Iterator<Object> it = coll.valueIterator(); 094 while(it.hasNext()) { 095 sb.append(' '); 096 sb.append(it.next()); 097 } 098 arguments=args.toString(); 099 } 100 else if(args instanceof String) { 101 arguments=" "+args.toString(); 102 } 103 else this.arguments=""; 104 } 105 106 /** set the value timeout 107 * Indicates how long, in seconds, the CFML executing thread waits for the spawned process. 108 * A timeout of 0 is equivalent to the non-blocking mode of executing. A very high timeout value is 109 * equivalent to a blocking mode of execution. The default is 0; therefore, the CFML thread spawns 110 * a process and returns without waiting for the process to terminate.If no output file is specified, 111 * and the timeout value is 0, the program output is discarded. 112 * @param timeout value to set 113 * @throws ApplicationException 114 **/ 115 public void setTimeout(double timeout) throws ApplicationException { 116 if(timeout<0) 117 throw new ApplicationException("value must be a positive number now ["+Caster.toString(timeout)+"]"); 118 this.timeout=(long)(timeout*1000L); 119 } 120 121 public void setTerminateontimeout(boolean terminateontimeout) { 122 this.terminateOnTimeout=terminateontimeout; 123 } 124 125 /** set the value name 126 * The full pathname of the application to execute. 127 * Note: On Windows, you must specify the extension as part of the application's name. For example, 128 * myapp.exe, 129 * @param name value to set 130 **/ 131 public void setName(String name) { 132 this.name=name; 133 } 134 135 /** 136 * define name of variable where output is written to 137 * @param variable 138 * @throws PageException 139 */ 140 public void setVariable(String variable) throws PageException { 141 this.variable=variable; 142 pageContext.setVariable(variable,""); 143 } 144 145 public void setErrorvariable(String errorVariable) throws PageException { 146 this.errorVariable = errorVariable; 147 pageContext.setVariable(errorVariable, ""); 148 } 149 150 /** set the value outputfile 151 * The file to which to direct the output of the program. If not specified, the output is 152 * displayed on the page from which it was called. 153 * @param outputfile value to set 154 * @throws SecurityException 155 **/ 156 public void setOutputfile(String outputfile) { 157 try { 158 this.outputfile=ResourceUtil.toResourceExistingParent(pageContext,outputfile); 159 pageContext.getConfig().getSecurityManager().checkFileLocation(this.outputfile); 160 161 } 162 catch (PageException e) { 163 this.outputfile=pageContext.getConfig().getTempDirectory().getRealResource(outputfile); 164 if(!this.outputfile.getParentResource().exists()) 165 this.outputfile=null; 166 else if(!this.outputfile.isFile()) 167 this.outputfile=null; 168 else if(!this.outputfile.exists()) { 169 ResourceUtil.createFileEL(this.outputfile, false); 170 //try { 171 //this.outputfile.createNewFile(); 172 /*} catch (IOException e1) { 173 this.outputfile=null; 174 }*/ 175 } 176 } 177 } 178 179 180 public void setErrorfile(String errorfile) { 181 182 try { 183 this.errorFile = ResourceUtil.toResourceExistingParent(pageContext,errorfile); 184 pageContext.getConfig().getSecurityManager().checkFileLocation(this.errorFile); 185 } 186 catch (PageException e) { 187 188 this.errorFile = pageContext.getConfig().getTempDirectory().getRealResource(errorfile); 189 190 if(!this.errorFile.getParentResource().exists()) 191 this.errorFile=null; 192 else if(!this.errorFile.isFile()) 193 this.errorFile=null; 194 else if(!this.errorFile.exists()) { 195 ResourceUtil.createFileEL(this.errorFile, false); 196 } 197 } 198 } 199 200 201 @Override 202 public int doStartTag() throws PageException { 203 return EVAL_BODY_BUFFERED; 204 } 205 206 private void _execute() throws Exception { 207 Object monitor=new SerializableObject(); 208 209 String command=""; 210 if(name==null) { 211 if(StringUtil.isEmpty(body)) { 212 required("execute", "name", name); 213 required("execute", "arguments", arguments); 214 } 215 else command=body; 216 } 217 else { 218 if(arguments==null)command=name; 219 else command=name+arguments; 220 } 221 222 223 _Execute execute=new _Execute(pageContext, monitor, command, outputfile, variable, errorFile, errorVariable); 224 225 //if(timeout<=0)execute._run(); 226 //else { 227 execute.start(); 228 if(timeout>0){ 229 try { 230 synchronized(monitor) { 231 monitor.wait(timeout); 232 } 233 } 234 finally { 235 execute.abort(terminateOnTimeout); 236 } 237 if(execute.hasException()) { 238 throw execute.getException(); 239 } 240 if(!execute.hasFinished()) 241 throw new ApplicationException("timeout ["+(timeout)+" ms] expired while executing ["+command+"]"); 242 //} 243 } 244 245 } 246 247 @Override 248 public int doEndTag() throws PageException { 249 if(pageContext.getConfig().getSecurityManager().getAccess(SecurityManager.TYPE_TAG_EXECUTE)==SecurityManager.VALUE_NO) 250 throw new SecurityException("can't access tag [execute]","access is prohibited by security manager"); 251 try { 252 _execute(); 253 } 254 catch (PageException pe) { 255 throw pe; 256 } 257 catch (Exception e) {e.printStackTrace(); 258 throw new ApplicationException("Error invoking external process",e.getMessage()); 259 } 260 return EVAL_PAGE; 261 } 262 263 @Override 264 public void doInitBody() { 265 266 } 267 268 @Override 269 public int doAfterBody() { 270 body=bodyContent.getString(); 271 if(!StringUtil.isEmpty(body))body=body.trim(); 272 return SKIP_BODY; 273 } 274}