/* * Copyright (C) 2000 - 2010 TagServlet Ltd * * This file is part of Open BlueDragon (OpenBD) CFML Server Engine. * * OpenBD is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * Free Software Foundation,version 3. * * OpenBD is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenBD. If not, see http://www.gnu.org/licenses/ * * Additional permission under GNU GPL version 3 section 7 * * If you modify this Program, or any covered work, by linking or combining * it with any of the JARS listed in the README.txt (or a modified version of * (that library), containing parts covered by the terms of that JAR, the * licensors of this Program grant you additional permission to convey the * resulting work. * README.txt @ http://www.openbluedragon.org/license/README.txt * * http://www.openbluedragon.org/ */ package com.naryx.tagfusion.cfm.tag.io; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.CharArrayWriter; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.RandomAccessFile; import java.io.Serializable; import java.io.UnsupportedEncodingException; import org.aw20.io.StreamUtil; import com.nary.io.FileUtils; import com.nary.util.FastMap; import com.nary.util.string; import com.naryx.tagfusion.cfm.engine.catchDataFactory; import com.naryx.tagfusion.cfm.engine.cfArgStructData; import com.naryx.tagfusion.cfm.engine.cfBinaryData; import com.naryx.tagfusion.cfm.engine.cfBooleanData; import com.naryx.tagfusion.cfm.engine.cfData; import com.naryx.tagfusion.cfm.engine.cfEngine; import com.naryx.tagfusion.cfm.engine.cfSession; import com.naryx.tagfusion.cfm.engine.cfStringData; import com.naryx.tagfusion.cfm.engine.cfStructData; import com.naryx.tagfusion.cfm.engine.cfmBadFileException; import com.naryx.tagfusion.cfm.engine.cfmRunTimeException; import com.naryx.tagfusion.cfm.engine.variableStore; import com.naryx.tagfusion.cfm.file.cfFileEncoding; import com.naryx.tagfusion.cfm.tag.cfTag; import com.naryx.tagfusion.cfm.tag.cfTagReturnType; import com.naryx.tagfusion.expression.function.file.FileUpload; import com.naryx.tagfusion.expression.function.file.FileUploadAll; public class cfFILETAG extends cfTag implements Serializable { static final long serialVersionUID = 1; public java.util.Map getInfo() { return createInfo("file", "Provides many operations that manage file input/output."); } public java.util.Map[] getAttInfo() { return new java.util.Map[] { createAttInfo( "ATTRIBUTECOLLECTION", "A structure containing the tag attributes", "", false ), createAttInfo("ACTION", "The following actions can be performed using CFFILE: \"delete\", \"copy\", \"move\", \"rename\", \"read\", \"readbinary\", \"upload\", \"uploadall\", \"write\" and \"append\".", "", true), createAttInfo("FILE", "Defines the file to perform an operation on. This should be a full path although if the \"uridirectory\" attribute is set to true it can be a relative path to the file. This is a required attribute when ACTION = \"delete\", \"read\", \"readbinary\", \"write\" or \"append\".", "", false), createAttInfo("SOURCE", "The source for file operations. This is a required attribute when ACTION = \"copy\", \"move\" or \"rename\".", "", false), createAttInfo("DESTINATION", "The destination for file operations. This is a required attribute when ACTION = \"upload\", \"copy\", \"move\" or \"rename\".", "", false), createAttInfo("VARIABLE", "The variable name to read a file into. This is a required attribute when ACTION = \"read\" or \"readbinary\".", "", false), createAttInfo("CHARSET", "Used to specify a character set to be used for file operations.", "", false), createAttInfo("OUTPUT", "The data to be written to a file. This is a required attribute when ACTION = \"write\" or \"append\".", "", false), createAttInfo("FILEFIELD", "The name of the field that should be used to select the filename. This is a required attribute when ACTION = \"upload\".", "", false), createAttInfo("ACCEPT", "Allows only the MIME types specified in this attribute to be accepted during an \"upload\" operation.", "", false), createAttInfo("RESULT", "Specifies a variable that will recieve the results of an upload operation.", "", false), createAttInfo("ATTRIBUTES", "The following values can be specified in this attribute to alter the permissions on a file: \"archive\", \"hidden\", \"normal\", \"readonly\" and \"system\".", "", false), createAttInfo("URIDIRECTORY", "Specify if the file location is a full path or a relative path.", "NO", false), createAttInfo("ADDNEWLINE", "Specifies wether to add a new line to the end of a file during \"write\" and \"append\" operations.", "YES", false), createAttInfo("NAMECONFLICT", "Specifies the action to be taken when a file operation has a name conflict, possible options include: \"error\", \"overwrite\", \"makeunique\" or \"skip\".", "ERROR", false) }; } protected void defaultParameters(String _tag) throws cfmBadFileException { defaultAttribute("URIDIRECTORY", "NO"); defaultAttribute("ADDNEWLINE", "YES"); defaultAttribute("NAMECONFLICT", "ERROR"); setFlushable(false); parseTagHeader(_tag); if (containsAttribute("ATTRIBUTECOLLECTION")) return; if (!containsAttribute("ACTION")) throw newBadFileException("Missing ACTION", "Must contain an ACTION attribute"); } protected cfStructData setAttributeCollection(cfSession _Session) throws cfmRunTimeException { cfStructData attributes = super.setAttributeCollection(_Session); if (!containsAttribute(attributes,"ACTION")) throw newBadFileException("Missing ACTION", "Must contain an ACTION attribute"); return attributes; } public String getTagName() { return "CFFILE"; } public cfTagReturnType render(cfSession _Session) throws cfmRunTimeException { cfStructData attributes = setAttributeCollection(_Session); String ACTION = getDynamic(attributes,_Session, "ACTION").getString().toLowerCase(); boolean bURI = getDynamic(attributes,_Session, "URIDIRECTORY").getBoolean(); if (ACTION.equals("delete")) deleteFile(attributes,_Session, bURI); else if (ACTION.equals("copy")) copyFile(attributes,_Session, bURI); else if (ACTION.equals("move")) moveFile(attributes,_Session, bURI); else if (ACTION.equals("rename")) renameFile(attributes,_Session, bURI); else if (ACTION.equals("read")) readTextFile(attributes,_Session, bURI); else if (ACTION.equals("readbinary")) readBinaryFile(attributes,_Session, bURI); else if (ACTION.equals("upload")) uploadFile(attributes,_Session, bURI); else if (ACTION.equals("uploadall")) uploadFileAll(attributes,_Session, bURI); else if (ACTION.equals("write")) writeFile(attributes,_Session, bURI); else if (ACTION.equals("append")) appendFile(attributes,_Session, bURI); else throw newRunTimeException("Invalid ACTION [" + ACTION + "]. Valid values: WRITE,DELETE,COPY,MOVE,RENAME,READ,READBINARY,UPLOAD,APPEND"); return cfTagReturnType.NORMAL; } private void deleteFile(cfStructData attributes, cfSession _Session, boolean bURI) throws cfmRunTimeException { if (!containsAttribute(attributes,"FILE")) throw newBadFileException("Missing FILE", "Must contain a FILE attribute if ACTION=DELETE"); deleteFile(attributes, _Session, getDynamic(_Session, "FILE").getString(), bURI); } private void deleteFile(cfStructData attributes, cfSession _Session, String _file, boolean bURI) throws cfmRunTimeException { if (_file.length() == 0) throw newRunTimeException("ACTION=DELETE contents no FILE/SOURCE name"); File thisFile = FileUtils.getFile(_Session, _file, bURI); if (!thisFile.exists()) throw newRunTimeException("ACTION=DELETE. The file specified by " + _file + " not exists"); else thisFile.delete(); } private void copyFile(cfStructData attributes, cfSession _Session, boolean bURI, String _action, boolean bMove) throws cfmRunTimeException { File srcFile = FileUtils.getFile(_Session, getDynamic(attributes,_Session, "SOURCE").getString(), bURI); if (srcFile.isDirectory()) throw newRunTimeException("ACTION=" + _action + ". The file specified is a directory: " + srcFile); else if (!srcFile.exists()) throw newRunTimeException("ACTION=" + _action + ". The file specified no longer exists: " + srcFile); String filepath = FileUtils.getCleanFilePath(_Session, getDynamic(attributes,_Session, "DESTINATION").getString(), bURI); File destFile = new File(filepath); if (!destFile.isAbsolute()) { destFile = new File(srcFile.getParent(), filepath); } if (destFile.isDirectory()) destFile = new File(destFile, srcFile.getName()); // If the destination file is the same as the source file then just return. // NOTE: this is the fix for bug #2762. if (destFile.equals(srcFile)) return; // --[ ------------------------------------------------- // --[ create input, output stream and copy the file try { BufferedInputStream inFile = new BufferedInputStream(new FileInputStream(srcFile)); BufferedOutputStream outFile = new BufferedOutputStream( cfEngine.thisPlatform.getFileIO().getFileOutputStream(destFile) ); StreamUtil.copyTo(inFile, outFile); } catch (Exception E) { throw newRunTimeException("ACTION=" + _action + ". Exception: " + E); } FileUtils.removeFromFileCache(_Session, filepath); updatePermissions(attributes,_Session, destFile); // need to retain the last modified date from the original file destFile.setLastModified(srcFile.lastModified()); // If the action is a move then we need to delete the source file if (bMove) deleteFile(attributes, _Session, getDynamic(attributes,_Session, "SOURCE").getString(), bURI); } private void copyFile(cfStructData attributes, cfSession _Session, boolean bURI) throws cfmRunTimeException { if (!containsAttribute(attributes,"SOURCE")) throw newBadFileException("Missing SOURCE", "Must contain a SOURCE attribute if ACTION=COPY"); if (!containsAttribute(attributes,"DESTINATION")) throw newBadFileException("Missing DESTINATION", "Must contain a DESTINATION attribute if ACTION=COPY"); copyFile(attributes,_Session, bURI, "COPY", false); } private void moveFile(cfStructData attributes, cfSession _Session, boolean bURI) throws cfmRunTimeException { if (!containsAttribute(attributes,"SOURCE")) throw newBadFileException("Missing SOURCE", "Must contain a SOURCE attribute if ACTION=MOVE"); if (!containsAttribute(attributes,"DESTINATION")) throw newBadFileException("Missing DESTINATION", "Must contain a DESTINATION attribute if ACTION=MOVE"); copyFile(attributes,_Session, bURI, "MOVE", true); } private void renameFile(cfStructData attributes, cfSession _Session, boolean bURI) throws cfmRunTimeException { if (!containsAttribute(attributes,"SOURCE")) throw newBadFileException("Missing SOURCE", "Must contain a SOURCE attribute if ACTION=RENAME"); if (!containsAttribute(attributes,"DESTINATION")) throw newBadFileException("Missing DESTINATION", "Must contain a DESTINATION attribute if ACTION=RENAME"); File srcFile = FileUtils.getFile(_Session, getDynamic(attributes,_Session, "SOURCE").getString(), bURI); if (srcFile.isDirectory()) throw newRunTimeException("ACTION=RENAME. The file specified is a directory: " + srcFile); else if (!srcFile.exists()) throw newRunTimeException("ACTION=RENAME. The file specified no longer exists: " + srcFile); String filepath = FileUtils.getCleanFilePath(_Session, getDynamic(attributes,_Session, "DESTINATION").getString(), bURI); File destFile = new File(filepath); if (!destFile.isAbsolute()) { destFile = new File(srcFile.getParent(), filepath); } if (destFile.isDirectory()) throw newRunTimeException("ACTION=RENAME. No new file name has been specified: " + destFile); srcFile.renameTo(destFile); FileUtils.removeFromFileCache(_Session, filepath); updatePermissions(attributes,_Session, destFile); } // ------------------------------------------------------- private void readTextFile(cfStructData attributes, cfSession _Session, boolean bURI) throws cfmRunTimeException { if (!containsAttribute(attributes,"FILE")) throw newBadFileException("Missing FILE", "Must contain a FILE attribute if ACTION=READ"); if (!containsAttribute(attributes,"VARIABLE")) throw newBadFileException("Missing VARIABLE", "Must contain a VARIABLE attribute if ACTION=READ"); String FILE = getDynamic(attributes,_Session, "FILE").getString(); String VAR = getDynamic(attributes,_Session, "VARIABLE").getString(); String CHARSET = containsAttribute(attributes,"CHARSET") ? getDynamic(attributes,_Session, "CHARSET").getString() : null; if (CHARSET != null) { CHARSET = com.nary.util.Localization.convertCharSetToCharEncoding(CHARSET); } if (FILE == null || VAR == null || FILE.length() == 0 || VAR.length() == 0) throw newRunTimeException("ACTION=READ. Not enough Parameters"); File srcFile = FileUtils.getFile(_Session, FILE, bURI); if (srcFile.isDirectory()) throw newRunTimeException("ACTION=READ. The file specified is a directory: " + srcFile); else if (!srcFile.exists()) throw newRunTimeException("ACTION=READ. The file specified no longer exists: " + srcFile); try { CharArrayWriter buffer = new CharArrayWriter(); BufferedReader reader = null; // if CHARSET not specified then try determining it from a BOM if (CHARSET == null) { cfFileEncoding fileEnc = new cfFileEncoding(srcFile, false); reader = fileEnc.getReader(srcFile); } else { cfFileEncoding fileEnc = new cfFileEncoding(srcFile, false); if (fileEnc.containsBOM()) { if (!(fileEnc.getEncoding().equalsIgnoreCase(CHARSET))) { throw newRunTimeException("The specified CHARSET [" + CHARSET + "] does not match the BOM (Byte Order Mark) of the file."); } else { reader = fileEnc.getReader(srcFile); } } else { reader = new BufferedReader(new InputStreamReader(new FileInputStream(srcFile), CHARSET)); } } StreamUtil.copyTo(reader, buffer ); _Session.setData(VAR, new cfStringData(buffer.toString())); } catch (UnsupportedEncodingException u) { throw newRunTimeException("The specified CHARSET [" + CHARSET + "] is not supported."); } catch (cfmRunTimeException c) { throw c; } catch (Exception E) { throw newRunTimeException("ACTION=READ. Error occurred reading the file:" + srcFile); } } // ------------------------------------------------------- private void writeFile(cfStructData attributes, cfSession _Session, boolean bURI) throws cfmRunTimeException { if (!containsAttribute(attributes,"FILE")) throw newBadFileException("Missing FILE", "Must contain a FILE attribute if ACTION=WRITE"); if (!containsAttribute(attributes,"OUTPUT")) throw newBadFileException("Missing OUTPUT", "Must contain an OUTPUT attribute if ACTION=WRITE"); String _file = getDynamic(attributes,_Session, "FILE").getString(); String CHARSET = containsAttribute(attributes,"CHARSET") ? getDynamic(attributes,_Session, "CHARSET").getString() : null; if (CHARSET != null) { CHARSET = com.nary.util.Localization.convertCharSetToCharEncoding(CHARSET); } File outFile; cfData outputData = getDynamic(attributes,_Session, "OUTPUT"); OutputStream outStream = null; OutputStreamWriter writer = null; try { outFile = FileUtils.getFile(_Session, _file, bURI); String filepath = outFile.getAbsolutePath(); FileUtils.removeFromFileCache(_Session, filepath); outStream = cfEngine.thisPlatform.getFileIO().getFileOutputStream(outFile); // --[ firstly just write to the stream if we're writing binary data if (outputData.getDataType() == cfData.CFBINARYDATA) { outStream.write(((cfBinaryData) outputData).getByteArray()); } else { // this extra bit of work is to make things i18n friendly if (CHARSET == null) { writer = new OutputStreamWriter(outStream); } else { writer = new OutputStreamWriter(outStream, CHARSET); } writer.write(outputData.getString().toCharArray()); if (getDynamic(attributes,_Session, "ADDNEWLINE").getBoolean()) writer.write(System.getProperty("line.separator")); } } catch (UnsupportedEncodingException u) { throw newRunTimeException("The specified CHARSET [" + CHARSET + "] is not supported."); } catch (cfmRunTimeException E) { throw E; } catch (Exception E) { throw new cfmRunTimeException(catchDataFactory.generalException("errorCode.runtimeError", "cffile.writeFile", new String[] { _file })); } finally { try { if (writer != null) writer.flush(); } catch (IOException ignored) { } // expanding the fix for bug #3260 to include writing new files (not just // appending to existing ones) StreamUtil.closeStream(writer); StreamUtil.closeStream(outStream); } if (outFile != null) updatePermissions(attributes,_Session, outFile); } // ------------------------------------------------------- private void appendFile(cfStructData attributes, cfSession _Session, boolean bURI) throws cfmRunTimeException { if (!containsAttribute(attributes,"FILE")) throw newBadFileException("Missing FILE", "Must contain a FILE attribute if ACTION=APPEND"); if (!containsAttribute(attributes,"OUTPUT")) throw newBadFileException("Missing OUTPUT", "Must contain an OUTPUT attribute if ACTION=APPEND"); String _file = getDynamic(attributes,_Session, "FILE").getString(); String CHARSET = containsAttribute(attributes,"CHARSET") ? getDynamic(attributes,_Session, "CHARSET").getString() : null; if (CHARSET != null) { CHARSET = com.nary.util.Localization.convertCharSetToCharEncoding(CHARSET); } RandomAccessFile outFile = null; cfData outputData = getDynamic(attributes,_Session, "OUTPUT"); File tempFile = FileUtils.getFile(_Session, _file, bURI); String filePath = tempFile.getAbsolutePath(); try { outFile = new RandomAccessFile(filePath, "rw"); outFile.seek(outFile.length()); // --[ firstly just write to the stream if we're writing binary data if (outputData.getDataType() == cfData.CFBINARYDATA) { outFile.write(((cfBinaryData) outputData).getByteArray()); // --[ otherwise we have to create an OutputStreamWriter } else { ByteArrayOutputStream bos = new ByteArrayOutputStream(); OutputStreamWriter writer; if (CHARSET == null) { writer = new OutputStreamWriter(bos); } else { writer = new OutputStreamWriter(bos, CHARSET); } String outputStr = outputData.getString(); if (containsAttribute(attributes,"ADDNEWLINE") && getDynamic(attributes,_Session, "ADDNEWLINE").getBoolean()) { outputStr += System.getProperty("line.separator"); } try { writer.write(outputStr); writer.flush(); outFile.write(bos.toByteArray()); } finally // here is the fix for bug #3260 { StreamUtil.closeStream(bos); StreamUtil.closeStream(writer); } } } catch (UnsupportedEncodingException u) { throw newRunTimeException("The specified CHARSET [" + CHARSET + "] is not supported."); } catch (Exception E) { throw new cfmRunTimeException(catchDataFactory.generalException("errorCode.runtimeError", "cffile.writeFile", new String[] { _file })); } finally { try { if (outFile != null) outFile.close(); } catch (IOException ignored) { } } FileUtils.removeFromFileCache(_Session, filePath); updatePermissions(attributes,_Session, new File(filePath)); } private void readBinaryFile(cfStructData attributes, cfSession _Session, boolean bURI) throws cfmRunTimeException { if (!containsAttribute(attributes,"FILE")) throw newBadFileException("Missing FILE", "Must contain a FILE attribute if ACTION=READBINARY"); if (!containsAttribute(attributes,"VARIABLE")) throw newBadFileException("Missing VARIABLE", "Must contain a VARIABLE attribute if ACTION=READBINARY"); String FILE = getDynamic(attributes,_Session, "FILE").getString(); String VAR = getDynamic(attributes,_Session, "VARIABLE").getString(); if (FILE == null || VAR == null || FILE.length() == 0 || VAR.length() == 0) throw newRunTimeException("ACTION=READBINARY. Not enough Parameters. Need FILE & VARIABLE"); File srcFile = FileUtils.getFile(_Session, FILE, bURI); if (srcFile.isDirectory()) throw newRunTimeException("ACTION=READBINARY. The file specified is a directory: " + srcFile); else if (!srcFile.exists()) throw newRunTimeException("ACTION=READBINARY. The file specified no longer exists: " + srcFile); FileInputStream fileStream = null; try { fileStream = new FileInputStream(srcFile); _Session.setData(VAR, new cfBinaryData(fileStream)); } catch (Exception E) { throw newRunTimeException("ACTION=READBINARY. Error occurred reading the file:" + srcFile); } finally { if (fileStream != null) { try { fileStream.close(); } catch (IOException ignored) { } } } } private void uploadFile(cfStructData attributes, cfSession _Session, boolean bURI) throws cfmRunTimeException { if (!containsAttribute(attributes,"DESTINATION")) throw newBadFileException("Missing DESTINATION", "Must contain a DESTINATION attribute if ACTION=UPLOAD"); FileUpload fileUploadFunction = new FileUpload(); cfArgStructData functionArgs = new cfArgStructData(true); functionArgs.setData("destination", getDynamic(attributes,_Session, "DESTINATION")); functionArgs.setData("uri", cfBooleanData.getcfBooleanData(bURI)); functionArgs.setData("nameconflict", getDynamic(attributes,_Session, "NAMECONFLICT")); if (containsAttribute(attributes,"ACCEPT")) functionArgs.setData("accept", getDynamic(attributes,_Session, "ACCEPT")); if (containsAttribute(attributes,"FILEFIELD")) functionArgs.setData("filefield", getDynamic(attributes,_Session, "FILEFIELD")); cfStructData fileData = (cfStructData) fileUploadFunction.execute(_Session, functionArgs); if (containsAttribute(attributes,"RESULT")) { String result = getDynamic(attributes,_Session, "RESULT").getString(); if (result.length() == 0) { throw newRunTimeException("Invalid RESULT attribute value. The name given must be at least 1 character in length."); } _Session.setData(result, fileData); } else { _Session.setQualifiedData(variableStore.FILE_SCOPE, fileData); } } private void uploadFileAll(cfStructData attributes, cfSession _Session, boolean bURI) throws cfmRunTimeException { if (!containsAttribute(attributes,"DESTINATION")) throw newBadFileException("Missing DESTINATION", "Must contain a DESTINATION attribute if ACTION=UPLOAD"); if (!containsAttribute(attributes,"RESULT")) throw newBadFileException("Missing RESULT", "Must contain a RESULT attribute if ACTION=UPLOADALL"); FileUploadAll fileUploadAllFunction = new FileUploadAll(); cfArgStructData functionArgs = new cfArgStructData(true); functionArgs.setData("destination", getDynamic(attributes,_Session, "DESTINATION")); functionArgs.setData("uri", cfBooleanData.getcfBooleanData(bURI)); functionArgs.setData("nameconflict", getDynamic(attributes,_Session, "NAMECONFLICT")); if (containsAttribute("ACCEPT")) functionArgs.setData("accept", getDynamic(attributes,_Session, "ACCEPT")); cfData fileData = fileUploadAllFunction.execute(_Session, functionArgs); String result = getDynamic(attributes,_Session, "RESULT").getString(); if (result.length() == 0) { throw newRunTimeException("Invalid RESULT attribute value. The name given must be at least 1 character in length."); } _Session.setData(result, fileData); } private void updatePermissions(cfStructData attributes, cfSession _Session, File _file) throws cfmRunTimeException { // if MODE specified and running on UNIX/Linux/Mac OS X if (containsAttribute(attributes,"MODE") && !cfEngine.WINDOWS) { String mode = getDynamic(attributes,_Session, "MODE").getString(); if (mode.length() != 3) { throw newRunTimeException("Invalid MODE specified: " + mode + ". Expected a three digit octal value e.g. 755."); } Process p = null; try { p = Runtime.getRuntime().exec("chmod " + mode + " " + _file.getAbsolutePath()); p.waitFor(); } catch (IOException ioe) { throw newRunTimeException("Failed to modify file attributes. " + ioe.getMessage()); } catch (InterruptedException ignored) { } finally { if (p != null) { // here is the fix for NA#3241 try { p.getInputStream().close(); } catch (IOException ignored) { } try { p.getOutputStream().close(); } catch (IOException ignored) { } try { p.getErrorStream().close(); } catch (IOException ignored) { } } } } if (containsAttribute(attributes,"ATTRIBUTES") && cfEngine.WINDOWS) { FastMap attrMap = new FastMap(); attrMap.put("archive", "+A"); attrMap.put("hidden", "+H"); attrMap.put("normal", "-H -S -R -A"); attrMap.put("readonly", "+R"); attrMap.put("system", "+S"); attrMap.put("temporary", "-H -S -R -A"); // same as normal String attributesStr = getDynamic(attributes,_Session, "ATTRIBUTES").getString(); String[] attribs = string.convertToList(attributesStr.toLowerCase(), ','); StringBuilder attribArgs = new StringBuilder(); for (int i = 0; i < attribs.length; i++) { if (attrMap.containsKey(attribs[i])) { attribArgs.append(" " + attrMap.get(attribs[i])); } else { throw newRunTimeException("Invalid ATTRIBUTE value [" + attribs[i] + "]. Valid values include archive, hidden, normal, readonly, and system."); } } Process p = null; try { p = Runtime.getRuntime().exec("attrib" + attribArgs.toString() + " " + _file.getAbsolutePath()); p.waitFor(); } catch (SecurityException sec) { throw newRunTimeException("file attributes cannot be modified when the SecurityPermission UnmanagedCode flag is not set. " + sec.getMessage()); } catch (IOException ioe) { throw newRunTimeException("Failed to modify file attributes. " + ioe.getMessage()); } catch (InterruptedException ignored) { } finally { if (p != null) { // here is the fix for NA#3241 try { p.getInputStream().close(); } catch (IOException ignored) { } try { p.getOutputStream().close(); } catch (IOException ignored) { } try { p.getErrorStream().close(); } catch (IOException ignored) { } } } } } }