import java.awt.Color; import java.awt.Container; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.net.URLEncoder; import java.security.PrivilegedActionException; import java.util.AbstractCollection; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.swing.AbstractButton; import javax.swing.JApplet; import javax.swing.JFileChooser; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import netscape.javascript.JSObject; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; /** * This class implements an applet capable of sending a file or set of files to a specific standard UNIX * FTP server residing on the same host as the page that contains the applet. The send functionality is * controlled completely via JavaScript. The applet also provides a mechanism for using callback functions * to report the percentage of the file sent (in real-time), to provide a status message, and to report * file completion (success or failure). These may be set either by parameters to the applet, or via * JavaScript calls to the applet. * * Authentication (permission to send a file) is based on a security token that is retrieved by the external * page prior to the sendFile call. The token is passed into the sendFile call, which then contacts the * authentication webservice (again, on the same host as the containing page) and verifies that the * transfer is allowed. If allowed, the webservice gives the applet the username / password for the * FTP session, and a directory to place the file in (presumably so that the webservice can process it * at a later point). When completed, the applet will send a notification to the webservice with the * filename and the transfer status. * * Please note that FTP is not the most secure method of sending a file. Usernames and passwords, as well * as the actual data are sent unencrypted, and can be eavesdropped upon by a packet sniffer between the * client and the server. * * @author Brad Broerman * @version 1.5 */ public class FTPApplet extends JApplet { private static final long serialVersionUID = 6741934940600874652L; String ftpHost; // The host to send the file to. Derived from codebase. String ftpTempFolder = "/"; // Folder that we upload to. Set from the authorization service. String ftpUserName = null; // User name for the FTP server. Set by the authorization service. String ftpUserPass = null; // Password for the FTP server. Also set by the authorization service. String currAccessCode = null; // Access code. Set by JavaScript before calling auth service, and read afterwards. String lastFolderSelected = null; // The directory the user last selected files from. ArrayList<String> listOfFilesSelected = null; // Tracks the files selected last time the UI was launched. Only these files may be sent. String jsOvrPctCallBackMethod = null; // Overall percentage complete callback. String jsPctCallBackMethod = null; // Current file percentage complete callback. String jsSttCallBackMethod = null; // Status message callback. String jsFileSentCallBackMthd = null; // Current file sent/complete callback. String jsFinalCallBackMthd = null; // All files sent/complete callback. String jsFileSelectCallBackMethod = null; // Files selected callback. JSObject window = null; // JavaScript window object. JSObject document = null; // JavaScript document object. Boolean returnCodeForTransfer = false; // Used in the FTP transfer ( Work-around for Java bug 6669818 ) FileSelectThread fileSelectionThread = null; // Thread for file selection. FTPThread ftpTransferThread = null; // Thread for FTP transfer. Container content = null; // Content pane for JFileChooser Swing object. /** * Called by the web container to set up the Applet instance. * Sets the look and feel, gets references to the web document, and gets the * initial parameters from the applet tag. * * @access Public * @param None. * @return None. */ public void init( ) { // // Set the SWING UI up for the file chooser. // try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch(Exception e) { System.out.println("Error setting native 'Look And Feel': " + e); } content = getContentPane(); content.setBackground(Color.white); content.setLayout(new FlowLayout()); // // Dynamically register the JSSE provider (this is in case the base URL for the auth service is HTTPS). // java.security.Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider()); // // Set this property to use Sun's reference implementation of the HTTPS protocol (if needed). // System.setProperty("java.protocol.handler.pkgs", "com.sun.net.ssl.internal.www.protocol"); // // The window and document references will be used later for talking to the calling page. // window = JSObject.getWindow(this); document = (JSObject)window.getMember( "document" ); // // Get the host for the FTP. This MUST be the same host that the page is served from. // ftpHost = getDocumentBase().getHost(); // // Used by the container to make sure the FTPConnection class is available. // @SuppressWarnings("unused") String classArray[] = { "FTPConnection" }; // // Set the JavaScript callback function to report percentage complete (as a parameter). // jsPctCallBackMethod = this.getParameter("FilePercentCallBack"); jsOvrPctCallBackMethod = this.getParameter("PercentCallback"); // // Set the callback function for status messages (as a parameter). // jsSttCallBackMethod = this.getParameter("StatusCallback"); // // Set the callback function for file complete message. // jsFinalCallBackMthd = this.getParameter("FTPCompleteCallback"); jsFileSentCallBackMthd = this.getParameter("FileCompleteCallback"); // // Set the callback functino for the selected files (from the file chooser) // jsFileSelectCallBackMethod = this.getParameter("FileSelectCallback"); } /** * Set / Reset the JavaScript callback to report overall percentage complete. * * @access Public * @param String callBackFuncName - The name of the JavaScript callback function. * @return None. * @throws None. */ public void setPercentCallBack( String callBackFuncName ) { jsOvrPctCallBackMethod = callBackFuncName; } /** * Set / Reset the JavaScript callback to report percentage complete for the current file. * * @access Public * @param String callBackFuncName - The name of the JavaScript callback function. * @return None. * @throws None. */ public void setFilePercentCallBack( String callBackFuncName ) { jsPctCallBackMethod = callBackFuncName; } /** * Set / Reset the callback for status messages. * * @access Public * @param String callBackFuncName - The name of the JavaScript callback function. * @return None. * @throws None. */ public void setStatusCallBack( String callBackFuncName ) { jsSttCallBackMethod = callBackFuncName; } /** * Set the callback function for reporting the send status of the current file. * * @access Public * @param String callBackFuncName - The name of the JavaScript callback function. * @return None. * @throws None. */ public void setFileCompleteCallBack( String callBackFuncName ) { jsFileSentCallBackMthd = callBackFuncName; } /** * Set the callback function for reporting the send status for the full transaction. * * @access Public * @param String callBackFuncName - The name of the JavaScript callback function. * @return None. * @throws None. */ public void setFtpCompleteCallBack( String callBackFuncName ) { jsFinalCallBackMthd = callBackFuncName; } /** * Set the callback function to pass back the selected files (from the file chooser) * * @access Public * @param String callBackFuncName - The name of the JavaScript callback function. * @return None. * @throws None. */ public void setFileSelectCallBack( String callBackFuncName ) { jsFileSelectCallBackMethod = callBackFuncName; } /** * This function is used to read the new session security code after a send has been started. * this code will be used in the JavaScript code to validate all future interactions. * * @access Public * @param None. * @return String - the current access code (if set) * @throws None. */ public String getAccessCode( ) { return currAccessCode; } /** * Used to set the session security code. * * @access Public * @param String accessCode - The new session security code. * @return None. * @throws None. */ public void setAccessCode( String accessCode ) { currAccessCode = accessCode; } /** * Send one or more files to the server via SFTP. This method is called from JavaScript on the document. * * @access Public * @param String accessCode - The access code for the current transaction. * @param String fileList - Semicolon delimited list of files to send. * @return Boolean - True for success, False for error. * @throws None. */ public boolean sendFiles(final String accessCode, final String fileList) { returnCodeForTransfer = false; SwingUtilities.invokeLater(new Runnable() { public void run() { returnCodeForTransfer = sendFilesLater(accessCode, fileList); } }); return returnCodeForTransfer; } /** * Send one or more files to the server via SFTP. This method is called from JavaScript on the document. * The files are determined directly from the last run file selection. * * @access Public * @param String accessCode - The access code for the current transaction. * @return Boolean - True for success, False for error. * @throws None. */ public boolean sendFiles(final String accessCode) { returnCodeForTransfer = false; SwingUtilities.invokeLater(new Runnable() { public void run() { returnCodeForTransfer = sendFilesLater(accessCode, ""); } }); return returnCodeForTransfer; } /** * Send one or more files to the server via SFTP. This method is called from JavaScript on the document. * The files are determined directly from the last run file selection. * The access code is the internal code from the last applet issued transaction with the webservice. * * @access Public * @param None. * @return Boolean - True for success, False for error. * @throws None. */ public boolean sendFiles( ) { returnCodeForTransfer = false; SwingUtilities.invokeLater(new Runnable() { public void run() { returnCodeForTransfer = sendFilesLater("", ""); } }); return returnCodeForTransfer; } /** * Abort the currently running FTP file / batch. * * @access Public * @param None. * @return None. * @throws None. */ public void abort() { if( null != ftpTransferThread && false != ftpTransferThread.isAlive()) { // This is used, since we can't really interrupt an IO thread. When this // variable is set, it will also set a flag in the FTPConnection that is // checked inside the main I/O loop. ftpTransferThread.interrupt[0] = true; } } /** * This is a hack work-around for Java bug 6669818. Sun is not likely to fix this, * as they have closed the bug without an official work-around or a fix... merely a * confirmation of the bug. * * @access Private * @param String accessCode - The access code for the current transaction. * @param String fileList - Semicolon delimited list of files to send. * @return Boolean - True for success, False for error. * @throws None. */ public boolean sendFilesLater( String accessCode, String filesToSend ) { boolean returnCode = false; // // Multiple files may be passed in, as a semi-colon separated list // String[] fileList = new String[1];; if( null != filesToSend && filesToSend.indexOf(';') > 0) fileList = filesToSend.split(";"); else if( null != filesToSend && filesToSend.length() > 0 ) { fileList[0] = new String(filesToSend); } else { fileList = listOfFilesSelected.toArray( fileList ); } // // If the passed in accessCode is blank or null, use the internal one. // if( null == accessCode || accessCode.length() == 0 ) { accessCode = this.currAccessCode; } else { this.currAccessCode = accessCode; } // // Filter incoming fileList to those files in listOfFilesSelected. // fileList = intersection( fileList, listOfFilesSelected.toArray(new String[listOfFilesSelected.size()])); // // We can only send one set of files at a time, so make sure the send thread isn't in operation. // if( null == ftpTransferThread || false == ftpTransferThread.isAlive()) { // // Get authorization for this transfer. This will come from the authorization // server by passing the current valid access code. This is compared to the one // stored on the server for the current session. If they match, the transfer can // proceed. This call also sets the class variables for the FTP user name, password, // and temporary directory, as well as the new current code. // if( getAuthentication(accessCode, filesToSend)) { // Create our file selection thread ftpTransferThread = new FTPThread( fileList ); // and let it start running ftpTransferThread.start(); // Indicate that we started successfully. returnCode = true; } else { // // Authentication with host server failed. // if( null != jsSttCallBackMethod) window.eval( jsSttCallBackMethod + "('Unable to authorize FTP transfer.');"); else System.err.println("FTPApplet - Unable to authorize FTP transfer."); if( null != jsFinalCallBackMthd ) window.eval( jsFinalCallBackMthd + "( false );"); } } return returnCode; } /** * Open the file chooser in a separate thread, and let the users select one or more files. * The selected files will be sent back to JavaScript with the registered callback function. * * @access Public * @param boolean rememberLastDir - Start in the directory last selected. * @return boolean - True on success, False on error. */ public boolean getFiles( boolean rememberLastDir ) { boolean returnCode = false; // // Only one thread at a time. // if( null == fileSelectionThread || false == fileSelectionThread.isAlive()) { // Create our file selection thread if( rememberLastDir ) fileSelectionThread = new FileSelectThread(true,lastFolderSelected); else fileSelectionThread = new FileSelectThread(true); // and let it start running fileSelectionThread.start(); returnCode = true; } return returnCode; } /** * Open the file chooser in a separate thread, and let the users select only ONE file. * The selected file will be sent back to JavaScript with the registered callback function. * * @access Public * @param boolean rememberLastDir - Start in the directory last selected. * @return boolean - True on success, False on error. */ public boolean getSingleFile( boolean rememberLastDir ) { boolean returnCode = false; // // Only one thread at a time. // if( null == fileSelectionThread || false == fileSelectionThread.isAlive()) { // Create our file selection thread if( rememberLastDir ) fileSelectionThread = new FileSelectThread(false,lastFolderSelected); else fileSelectionThread = new FileSelectThread(false); // and let it start running fileSelectionThread.start(); returnCode = true; } return returnCode; } @SuppressWarnings({ "unchecked", "rawtypes" }) private Document callWebService( String serviceName, String message ) throws PrivilegedActionException { Document xmlReply = null; try { final String f_service = serviceName; final String f_parms = message; xmlReply = (Document)java.security.AccessController.doPrivileged(new java.security.PrivilegedExceptionAction() { public Object run() throws ServerException, SAXException, ParserConfigurationException, IOException { Document xmlReply = doCallWebService( f_service, f_parms ); return xmlReply; } } ); } catch ( PrivilegedActionException e ) { throw e; } return xmlReply; } // // Call an XML web service. Parameters may be either GET (as part of the service name) or POST as part of the message. // private Document doCallWebService( String serviceName, String message ) throws MalformedURLException, IOException, ServerException, SAXException, ParserConfigurationException { // // Create the URL object, and open the connection to the web service. // URL url = new URL( super.getDocumentBase(), serviceName ); // // We know that it's either HTTP or HTTPS, so we can cast to the // HttpUrlConnection class here. This way, we can get the response // code directly. // HttpURLConnection con = (HttpURLConnection)url.openConnection(); con.setDoInput(true); con.setDoOutput(true); con.setUseCaches(false); // // Set the content length header, based on the message size. // con.setRequestProperty("CONTENT_LENGTH", "" + message.length() ); // actually not checked // // Copy the session cookies from the parent page, and include them on this request. // // con.setRequestProperty("Cookie", (String) document.getMember( "cookie" )); // // Prepare to write to the server. // OutputStream os = con.getOutputStream(); // // Send the request. // os.write(message.getBytes("UTF-8")); os.flush(); os.close(); // // Now, read the response! // // // Check the response header. Make sure we're getting a response in the 200 range. // if( con.getResponseCode() >= 300 || con.getResponseCode() < 200 ) { System.err.println(" Got bad response code: (" + Integer.toString(con.getResponseCode()) + ") " + con.getResponseMessage() ); // // If not, throw an exception to notify the caller that there was a problem. // // throw new ServerException( con.getResponseMessage() ); } // // Now, get the response text. Convert it to a Document. // DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); Document doc = docBuilder.parse(con.getInputStream()); // // Return the response to the caller // return doc; } private List<ExampleFileFilter> getValidFileTypes( String accessCode, boolean multiFiles ) { // // This method will call the file authorization webservice to get the list // of allowable file types. The response will be in the XML format: // <response> // <success> // <filter desc='name of filter' default='true'> // <ext> extension </ext> // <ext> extension </ext> // <ext> extension </ext> // </filter> // <filter desc='name of filter'> // <ext> extension </ext> // <ext> extension </ext> // <ext> extension </ext> // </filter> // <newauth> new code </newauth> // </success> // </response> // etc... try { // // Set the security key, and action (upload photo). These will be POST parameters. // String params = "key=" + URLEncoder.encode(accessCode, "UTF-8") + "&action=getvalidfileslist"; if(multiFiles) { params += "&multifile=true"; } // // Call the webservice. // Document xmlReply = callWebService( "ftpauthserver.php", params ); // // Now, if we get other responses (other than the 200 OK) we won't get what we're looking for // below anyway, and we'll get an authentication error... That's ok... The full error message // will be written to the Java Console log, so we can debug. // if( xmlReply.getElementsByTagName("response").getLength() > 0 ) { Element successNode = (Element) xmlReply.getElementsByTagName("success").item(0); if( null != successNode ) { List<ExampleFileFilter> returnList = new ArrayList<ExampleFileFilter>(); // Now, get the list of filters NodeList filterNodes = successNode.getElementsByTagName("filter"); for( int idx = 0; idx < filterNodes.getLength(); ++idx) { ExampleFileFilter newFilter = new ExampleFileFilter(); Element currFilter = (Element) filterNodes.item(idx); String desc = currFilter.getAttribute("desc"); if( desc == null || desc.length() == 0 ) { desc = ""; } newFilter.setDescription(desc); String def = currFilter.getAttribute("default"); if( def != null && def.length() > 0 && def.equalsIgnoreCase("true")) { newFilter.setDefault(true); } NodeList extensions = currFilter.getElementsByTagName("ext"); for( int extidx = 0; extidx < extensions.getLength(); ++extidx) { Element currext = (Element) extensions.item(extidx); newFilter.addExtension(currext.getTextContent()); } returnList.add(newFilter); } currAccessCode = successNode.getElementsByTagName("newauth").item(0).getTextContent(); return returnList; } else { // if no success nodes, then there has to be a failed node. Element failedNode = (Element) xmlReply.getElementsByTagName("failed").item(0); if( null != failedNode ) System.err.println("FTPApplet::getValidFileTypes() - Failed getting filter list: " + failedNode.getTextContent()); } } else { System.err.println("FTPApplet::getValidFileTypes() - Unknown response from action server."); } } catch(Exception e) { System.err.println("FTPApplet::getValidFileTypes() - Caught Exception: " + e.toString()); e.printStackTrace(); } return null; } private boolean getAuthentication( String accessCode, String fileList ) { // // This method will call a special method on the bbphotoadminaction.php // webservice to determine if the current user is authorized to upload a // file to the server. It will pass the session ID and the current // code string (used to validate that the connection wasn't hacked). If the // webservice determines we can send, it will send back the FTP credentials and // a new security code. These will be stored in the appropriate class variables. // try { // // Set the security key, and action (upload photo). These will be POST parameters. // String params = "key="+URLEncoder.encode(accessCode, "UTF-8")+"&action=uploadPhoto" + "&filelist="+URLEncoder.encode(fileList, "UTF-8"); // // Call the webservice. // Document xmlReply = callWebService( "ftpauthserver.php", params ); // // Now, if we get other responses (other than the 200 OK) we won't get what we're looking for // below anyway, and we'll get an authentication error... That's ok... The full error message // will be written to the Java Console log, so we can debug. // if( xmlReply.getElementsByTagName("response").getLength() > 0 ) { // // We should get back an XML document with the // following parameters: // // <response> // <success> // <dir> directory_name </dir> // <usr> ftp_user_name </usr> // <pass> ftp_password </pass> // <newauth> new code </newauth> // </success> // </response> // // The directory name, username, and password will bet set in instance variables for // later use. // // A failure would change the <success> with <failed> and a reason. // Element successNode = (Element) xmlReply.getElementsByTagName("success").item(0); if( null != successNode ) { // // Now, get each of the required parameters... // ftpTempFolder = successNode.getElementsByTagName("dir").item(0).getTextContent(); ftpUserName = successNode.getElementsByTagName("usr").item(0).getTextContent(); ftpUserPass = successNode.getElementsByTagName("pass").item(0).getTextContent(); currAccessCode = successNode.getElementsByTagName("newauth").item(0).getTextContent(); return true; } else { // if no success nodes, then there has to be a failed node. Element failedNode = (Element) xmlReply.getElementsByTagName("failed").item(0); if( null != failedNode ) System.err.println("Authorization failed: " + failedNode.getTextContent()); } } else { System.err.println("Unknown response from action server."); } } catch(Exception e) { System.err.println("FTPApplet::getAuthentication() - Caught Exception: " + e.toString()); } return false; } private boolean sendFileComplete(int numFilesSent, String fileList ) { // // This method will call a special method on the ftpauthserver.php // webservice to tell the application that the file has been successfully // sent, and can now be processed by the program. // try { String params = "key="+URLEncoder.encode(currAccessCode, "UTF-8")+"&action=photoUploadDone&numFilesSent="+ Integer.toString(numFilesSent)+"&filelist="+URLEncoder.encode(fileList, "UTF-8"); // // Call the webservice. // Document xmlReply = callWebService( "ftpauthserver.php", params ); // // We should get back an XML document with the // following parameters: // // <response> // <success> // <newauth> new code </newauth> // </success> // </response> // // A failure would change the <success> with <failed> and a reason. // System.err.println( xmlReply.toString() ); if( xmlReply.getElementsByTagName("response").getLength() > 0 ) { Element successNode = (Element) xmlReply.getElementsByTagName("success").item(0); if( null != successNode ) { currAccessCode = successNode.getElementsByTagName("newauth").item(0).getTextContent(); return true; } else { // if no success nodes, then there has to be a failed node. Element failedNode = (Element) xmlReply.getElementsByTagName("failed").item(0); if( null != failedNode ) System.err.println("Server error responding to sent notification."); } } else { System.err.println("Unknown response from action server."); } } catch(Exception e) { System.err.println("FTPApplet::sendFileComplete() - Caught Exception: " + e.toString()); } return false; } // // This class is the percentage complete callback. It will track both the // percentage for the current file, as well as the overall percentage. // // The constructor sets the number of files that are going to be sent, as well as // the total file size. // private class FTPCallBack implements FTPConnection.CallBack { long currFileSize = 0L; // Size of the current file only. long currentCount = 0L; // Amount of data sent for the current file. long ttlFileSize = 0L; // Number of bytes for all files. long currentTtlCount = 0L; // Total number of bytes sent overall. public FTPCallBack( int numFiles, long totalFileSize ) { this.ttlFileSize = totalFileSize; } // // This is called from within the FTPConnection class whenever a block of data // is sent to the server. We will track the amount for both the current file, and // for the overall transfer. Depending on the callbacks registered with the applet, // we'll call the Javascript. // public void update(long count) { this.currentCount += count; this.currentTtlCount += count; double percentage = (double)this.currentCount * 100.0 / (double)this.currFileSize; if( null != jsPctCallBackMethod ) { window.eval( jsPctCallBackMethod + "(" + Double.toString(percentage) + ");"); } percentage = (double)this.currentTtlCount * 100.0 / (double)this.ttlFileSize; if( null != jsOvrPctCallBackMethod ) { window.eval( jsOvrPctCallBackMethod + "(" + Double.toString(percentage) + ");"); } } // // This is called at the beginning of each file with either FTPConnection.Get // or FTPConnection.Put as the command, the current file pathnames (both local // and remote), and the size of the current file. // public void init(int command, String src, String dest, long size ) { this.currentCount = 0L; this.currFileSize = size; } // // This is called at the end of each file, with either a TRUE on success, or FALSE on failed. // public void end( boolean success ) { long delta = this.currFileSize - this.currentCount; update(delta); } } // // This is the thread for sending one or more files via FTP to the server. // public class FTPThread extends Thread { String[] filesToSend = null; ArrayList<String> filesSent = new ArrayList<String>(); public boolean interrupt[] = new boolean[0]; // Construct the transfer to send one or more files. public FTPThread( String[] inFileList ) { // yeah, it's a shallow copy, but the contents aren't mutable. filesToSend = inFileList.clone(); } // // This method sends the file in a thread, reporting status and percentage complete if // callbacks are set. The currFileInProgress boolean flag indicates whether a transfer // is currently in progress or not. It is reset when the method is complete. // @SuppressWarnings({ "unchecked", "rawtypes" }) public void run() { try { // // Create the FTP connection object. // FTPConnection ftpconnObj = new FTPConnection(); interrupt = FTPConnection.abortTransfer; // // Get the size of all the files we're going to send. // long ttlFileSize = 0L; try { Long returnCode = (Long)java.security.AccessController.doPrivileged(new java.security.PrivilegedExceptionAction() { public Object run() throws IOException { long ttlFileSize = 0L; for( int idx = 0; idx < filesToSend.length; ++idx ) { File currFile = new File(filesToSend[idx]); ttlFileSize += currFile.length(); } return new Long( ttlFileSize); } } ); ttlFileSize = returnCode.longValue(); } catch ( PrivilegedActionException e ) { throw (IOException) e.getException(); } // // Set the callback for the percentage complete notification to the // JavaScript (if the callback method name is set). // ftpconnObj.setUpdateCallBack(new FTPCallBack(filesToSend.length, ttlFileSize)); if( null != jsSttCallBackMethod) window.eval( jsSttCallBackMethod + "('Connecting.');"); // // Connect to the FTP server. // if( ftpconnObj.connect(ftpHost) ) { // // Log in. The login user name and password are set by the // authentication server, and stored as class variables. // if( ftpconnObj.login(ftpUserName,ftpUserPass) ) { // // Change to the temporary upload directory. This parameter // is also set by the call to the authentication server. // if( null != jsSttCallBackMethod) window.eval( jsSttCallBackMethod + "('Sending.');"); if( ftpconnObj.changeDirectory(ftpTempFolder) ) { int nbrFilesToSend = filesToSend.length; int nbrErrorFiles = 0; for( int idx = 0; idx < nbrFilesToSend && !interrupt[0]; ++idx ) { String currFile = filesToSend[idx]; if( null != jsSttCallBackMethod) { if( nbrFilesToSend > 1 ) window.eval( jsSttCallBackMethod + "('Sending file" + Integer.toString(idx+1) + ".');"); else window.eval( jsSttCallBackMethod + "('Sending file.');"); } // // Now send the file. // try { if(false == ftpconnObj.uploadFile( currFile ) ) { String tmpStr = currFile.replaceAll("\\\\","\\\\\\\\"); if( null != jsSttCallBackMethod) window.eval( jsSttCallBackMethod + "('Error uploading \\\'" + tmpStr + "\\\'.');"); System.err.println( "Error uploading file'" + tmpStr + "'." ); ++nbrErrorFiles; if( null != jsFileSentCallBackMthd ) { window.eval( jsFileSentCallBackMthd + "(false,'" + tmpStr + "');"); } } else { String tmpStr = currFile.replaceAll("\\\\","\\\\\\\\"); if( null != jsFileSentCallBackMthd ) { window.eval( jsFileSentCallBackMthd + "(true,'" + tmpStr + "');"); } String finalfilename = null; if( currFile.lastIndexOf("\\") > -1 ) finalfilename = currFile.substring(currFile.lastIndexOf("\\")+1); else if( currFile.lastIndexOf("/") > -1 ) finalfilename = currFile.substring(currFile.lastIndexOf("/")+1); filesSent.add(finalfilename); } } catch( Exception e ) { String tmpStr = currFile.replaceAll("\\\\","\\\\\\\\"); if( null != jsSttCallBackMethod) window.eval( jsSttCallBackMethod + "('Error uploading \'" + tmpStr + "\'.');"); System.err.println( "Error uploading file'" + tmpStr + "'." ); ++nbrErrorFiles; if( null != jsFileSentCallBackMthd ) { window.eval( jsFileSentCallBackMthd + "(false,'" + tmpStr + "');"); } } } // // The FTP completed successfully. Notify the server. // Note, if this notification does not succeed, log it, but // don't treat this as a critical error. // sendFileComplete(nbrFilesToSend - nbrErrorFiles, FTPApplet.join(filesSent,";")); if( 0 == nbrErrorFiles ) { // // Update the client with the status. // if( null != jsSttCallBackMethod) window.eval( jsSttCallBackMethod + "('Complete.');"); // // Inform the JavaScript that we're successfully done. // if( null != jsFinalCallBackMthd ) window.eval( jsFinalCallBackMthd + "( true );"); } else { // Error: Unable to upload some files. if( null != jsSttCallBackMethod) window.eval( jsSttCallBackMethod + "('Error uploading one or more files.');"); if( null != jsFinalCallBackMthd ) window.eval( jsFinalCallBackMthd + "( false );"); } } else { // Error: Unable to change directory. if( null != jsSttCallBackMethod) window.eval( jsSttCallBackMethod + "('Error changing to temp directory.');"); if( null != jsFinalCallBackMthd ) window.eval( jsFinalCallBackMthd + "( false );"); } } else { // Error: Unable to login. if( null != jsSttCallBackMethod) window.eval( jsSttCallBackMethod + "('Invalid FTP login.');"); if( null != jsFinalCallBackMthd ) window.eval( jsFinalCallBackMthd + "( false );"); } } else { // Error: Unable to connect. if( null != jsSttCallBackMethod) window.eval( jsSttCallBackMethod + "('Unable to connect to FTP server.');"); if( null != jsFinalCallBackMthd ) window.eval( jsFinalCallBackMthd + "( false );"); } // // Log out from the FTP server and close the ports. // ftpconnObj.disconnect(); } catch( Exception e) { System.err.println("FTPApplet::sendFile() - Caught Exception: " + e.toString()); if( null != jsSttCallBackMethod) window.eval( jsSttCallBackMethod + "('Unknown error sending file.');"); if( null != jsFinalCallBackMthd ) window.eval( jsFinalCallBackMthd + "( false );"); } } } // // This is the thread for the JFileChooser. // private class FileSelectThread extends Thread { boolean showMultiFiles = true; String startingFolder = ""; @SuppressWarnings("unused") public FileSelectThread() { super(); } public FileSelectThread( boolean selectMultipleFiles ) { super(); showMultiFiles = selectMultipleFiles; } public FileSelectThread( boolean selectMultipleFiles, String startFolder ) { super(); showMultiFiles = selectMultipleFiles; startingFolder = startFolder; } @SuppressWarnings({ "unchecked", "rawtypes" }) public void run() { String fileList = (String)java.security.AccessController.doPrivileged(new java.security.PrivilegedAction() { public Object run() { JFileChooser chooser = null; if( null != startingFolder ) { chooser = new JFileChooser(new File(startingFolder)); } else { chooser = new JFileChooser(); } chooser.setFileView(new ImageFileView()); chooser.setMultiSelectionEnabled(showMultiFiles); List<ExampleFileFilter>filters = getValidFileTypes( currAccessCode, showMultiFiles ); if( null != filters ) { Iterator<ExampleFileFilter> iter = filters.iterator(); while(iter.hasNext()) { ExampleFileFilter nextFilter = iter.next(); chooser.addChoosableFileFilter(nextFilter); if( nextFilter.isDefault() ) { chooser.setFileFilter(nextFilter); } } } AbstractButton detailbutton = SwingUtils.getDescendantOfType(AbstractButton.class, chooser, "Icon", UIManager.getIcon("FileChooser.detailsViewIcon")); AbstractButton listbutton = SwingUtils.getDescendantOfType(AbstractButton.class, chooser, "Icon", UIManager.getIcon("FileChooser.listViewIcon")); listbutton.addActionListener( new ListActionListener(chooser) ); detailbutton.addActionListener( new DetailActionListener(chooser) ); if( null == content ) System.err.println("Content Pane is null"); int returnVal = chooser.showOpenDialog(content); if(returnVal == JFileChooser.APPROVE_OPTION) { StringBuffer fileList = new StringBuffer(); File[] files = chooser.getSelectedFiles(); listOfFilesSelected = new ArrayList<String>(); for( int i = 0; i < files.length; ++i ) { String tmpStr = files[i].getAbsolutePath(); listOfFilesSelected.add( new String(tmpStr) ); tmpStr = tmpStr.replaceAll("\\\\","\\\\\\\\"); fileList.append(tmpStr); fileList.append(";"); } if( files.length > 0) { lastFolderSelected = files[0].getParent(); } return new String(fileList.toString()); } return new String(""); } final class DetailActionListener implements ActionListener { JFileChooser fc; public DetailActionListener(JFileChooser fc) { this.fc = fc; } public void actionPerformed(ActionEvent e) { fc.setFileView(null); } } final class ListActionListener implements ActionListener { JFileChooser fc; public ListActionListener(JFileChooser fc) { this.fc = fc; } public void actionPerformed(ActionEvent e) { fc.setFileView(new ImageFileView()); } } } ); try { if( null != jsFileSelectCallBackMethod) window.eval( jsFileSelectCallBackMethod + "('" + fileList + "');"); } catch( Exception e ) { System.out.println("Caught Exception: " + e.toString() ); } } } // This exception will be thrown when the webserver responds with an error HTTP response code. private class ServerException extends Exception { /** * */ private static final long serialVersionUID = 1L; public ServerException( String message ) { super(message); } } @SuppressWarnings("rawtypes") public static String join(AbstractCollection s, String delimiter) { StringBuffer buffer = new StringBuffer(); Iterator iter = s.iterator(); if (iter.hasNext()) { buffer.append(iter.next()); while (iter.hasNext()) { buffer.append(delimiter); buffer.append(iter.next()); } } return buffer.toString(); } public static String[] intersection(String[] first, String[] second) { // initialize a return set for intersections Set<String> intsIntersect = new HashSet<String>(); // load first array to a hash HashSet<String> array1ToHash = new HashSet<String>(); for (int i = 0; i < first.length; i++) { array1ToHash.add(first[i]); } // check second array for matches within the hash for (int i = 0; i < second.length; i++) { if (array1ToHash.contains(second[i])) { // add to the intersect array intsIntersect.add(second[i]); } } return intsIntersect.toArray(new String[intsIntersect.size()]); } }