/* * @(#)PutFiles.java * * This file is part of webCDwriter - Network CD/DVD Writing. * * Copyright (C) 1999-2006 Jörg P. M. Haeger * * webCDwriter is free software. See CDcreator.java for details. */ import java.awt.*; import java.awt.event.*; import java.io.*; import java.net.*; import java.security.MessageDigest; import javax.swing.*; /** * Execution of "putFile" commands to copy a directory tree * to the server. * * @version 20060410 * @author Jörg P. M. Haeger */ public class PutFiles extends Command { PutDialog view; protected DataOutputStream outStream; DirTreeNode CDRootNode; Object MD5Sum; ByteArrayOutputStream MD5Sums; long maxCommandDataSize = 64 * 1024; int sessionSizeKB; int transmittedKB; boolean doNotShorten = false; boolean ignoreBigFile; boolean ignoreUnreadable; boolean shorten; boolean localServer = false; public PutFiles(DirTreeNode CDRootNode, int sessionSizeKB) { view = new PutDialog(cancelButton); outStream = CDcreator.outStream; this.CDRootNode = CDRootNode; try { if (Options.calculateMD5s() && !CDcreator.isNetscapeVM()) { MD5Sum = MessageDigest.getInstance("MD5"); MD5Sums = new ByteArrayOutputStream(); } else MD5Sum = null; } catch(java.security.NoSuchAlgorithmException e) { MD5Sum = null; } this.sessionSizeKB = sessionSizeKB; transmittedKB = 0; if (CDcreator.socket != null) { // String name = CDcreator.socket.getInetAddress().getHostName(); String name = CDcreator.serverName; localServer = name.equals("127.0.0.1") || name.equals("localhost"); } } static String bytesToHex(byte[] bs) { String hexStr = ""; for (int i = 0; i < bs.length; i++) { int c1 = (bs[i] & 0xF0) >> 4; if (c1 < 10) c1 += '0'; else c1 += 'a' - 10; int c2 = bs[i] & 0x0F; if (c2 < 10) c2 += '0'; else c2 += 'a' - 10; hexStr = hexStr + (char)c1 + (char)c2; } return hexStr; } private void calculateMD5(InputStream inFile, long bytesToWrite) throws java.io.IOException { while (bytesToWrite > 0) { int blockSize = 64 * 1024; if (blockSize > bytesToWrite) blockSize = (int) bytesToWrite; byte bs[] = new byte[blockSize]; int len = inFile.read(bs); if (len == -1) break; ((MessageDigest) MD5Sum).update(bs, 0, len); bytesToWrite -= len; transmittedKB += (len + 1023) / 1024; view.setProgress(transmittedKB); } } private void copy(InputStream inFile, long bytesToWrite) throws java.io.IOException { try { while (bytesToWrite > 0) { int blockSize = 64 * 1024; if (blockSize > bytesToWrite) blockSize = (int) bytesToWrite; byte bs[] = new byte[blockSize]; int len = inFile.read(bs); if (len == -1) break; outStream.write(bs, 0, len); if (MD5Sum != null) ((MessageDigest) MD5Sum).update(bs, 0, len); bytesToWrite -= len; transmittedKB += (len + 1023) / 1024; view.setProgress(transmittedKB); } } finally { while (bytesToWrite-- > 0) outStream.write(0); } } protected void editFileName(DirTreeNode node, int index) throws Exception { to64Characters(node, index); } protected void execute(String command) throws java.io.IOException, CommandError, Exception { writeCommand(command); readResult(); if (!lastResultOK()) throw new CommandError(); } protected void execute(String command, InputStream inFile, long bytesToWrite) throws java.io.IOException, CommandError, Exception { long t0 = new java.util.Date().getTime(); writeCommand(command); copy(inFile, bytesToWrite); readResult(); if (!lastResultOK()) throw new CommandError(); long deltaT = new java.util.Date().getTime() - t0; if (deltaT > 0) { long bytesPerSecond = (1000 * bytesToWrite) / deltaT; if (2 * bytesPerSecond > maxCommandDataSize) { maxCommandDataSize = 2 * bytesPerSecond; maxCommandDataSize = 1024 * ((maxCommandDataSize + 1023) / 1024); Version.debug("bytesPerSecond", "" + bytesPerSecond); } } } Dimension getPreferredSize() { return new Dimension(540, 280); } private boolean ignore4GBFile(final String name) { try { ignoreBigFile = false; SwingUtilities.invokeAndWait(new Runnable() { public void run() { String[] options = { i18n("PutFiles.ignore"), i18n("cancel") }; JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); JLabel l = new JLabel(i18n("PutFiles.4GBFile")); l.setAlignmentX(JLabel.LEFT_ALIGNMENT); panel.add(l); JTextArea t = new JTextArea(name); t.setAlignmentX(JTextArea.LEFT_ALIGNMENT); t.setEditable(false); panel.add(t); int result = showOptionDialog( panel, i18n("error"), JOptionPane.DEFAULT_OPTION, JOptionPane.ERROR_MESSAGE, null, options, options[0]); if (result == 0) ignoreBigFile = true; } }); } catch (Exception e) { } return ignoreBigFile; } private boolean ignoreFile(final String name) { if (view.ignoreAllUnreadableBox.isSelected()) return true; try { ignoreUnreadable = false; SwingUtilities.invokeAndWait(new Runnable() { public void run() { String[] options = { i18n("PutFiles.ignore"), i18n("PutFiles.ignoreAll"), i18n("cancel") }; JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); JLabel l = new JLabel(i18n("PutFiles.cannotRead")); l.setAlignmentX(JLabel.LEFT_ALIGNMENT); panel.add(l); JTextArea t = new JTextArea(name); t.setAlignmentX(JTextArea.LEFT_ALIGNMENT); t.setEditable(false); panel.add(t); int result = showOptionDialog( panel, i18n("error"), JOptionPane.DEFAULT_OPTION, JOptionPane.ERROR_MESSAGE, null, options, options[0]); if (result == 0) ignoreUnreadable = true; else if (result == 1) view.ignoreAllUnreadableBox.setSelected(true); } }); } catch (Exception e) { } if (ignoreUnreadable || view.ignoreAllUnreadableBox.isSelected()) return true; return false; } FileInputStream open(File file) throws Exception { try { FileInputStream inFile = new FileInputStream(file); return inFile; } catch (java.io.IOException e) { } if (view.ignoreAllUnreadableBox.isSelected()) return null; // System.out.println("cannot read " + file); final String name = file.getPath(); ignoreFile(name); if (ignoreUnreadable || view.ignoreAllUnreadableBox.isSelected()) return null; throw new Exception("open cancelled"); } protected void process(int code, String text) { switch (code) { case 711: try { int p = text.indexOf(' '); text = text.substring(0, p); transmittedKB += Integer.parseInt(text); view.setProgress(transmittedKB); } catch (Exception e) { } break; default: ; } } protected boolean processInternal(int code, String text) { switch (code) { case 710: try { if (MD5Sum != null) { text = removeEscChars(text); MD5Sums.write((text + "\n").getBytes("UTF-8")); } } catch (java.io.IOException e) { } return true; default: ; } return false; } protected void putFile(String dirName, DirTreeNode node) throws java.io.IOException, CommandError, Exception { File file = node.getFile(); String fileName = dirName + "/" + node.toString(); Version.showDebugInfo(1, fileName + " " + file); view.setFileName(fileName); if (file.length() >= 4L * 1024 * 1024 * 1024) if (ignore4GBFile(fileName)) return; else throw new Exception("File too big"); if (file instanceof ServerFile) try { if (MD5Sum != null) view.setMessage(i18n("titleCalculateMD5")); execute("linkFile" + " " + spaceToEsc(file.getPath()) + " " + spaceToEsc(fileName) + (MD5Sum != null? " md5": "")); if (MD5Sum != null) view.setMessage(i18n("titlePutFiles")); } catch (CommandError e) { if (!ignoreFile(fileName)) throw e; } else { long bytesToWrite = file.length(); FileInputStream inFile = null; String referenceArg = ""; if (bytesToWrite > 0) { inFile = open(file); if (inFile == null) return; if (localServer) referenceArg = " " + spaceToEsc(file.getPath()); } if (MD5Sum != null) ((MessageDigest) MD5Sum).reset(); int mode = 0444; if (node.isExecutable()) mode |= 0111; if (Mode.saveImage && bytesToWrite > 2 * 1024) { execute("putDummy" + " " + spaceToEsc(fileName) + " " + bytesToWrite + " " + Integer.toOctalString(mode) + " " + file.lastModified() / 1000 + " " + spaceToEsc(file.getPath())); if (MD5Sum != null) calculateMD5(inFile, bytesToWrite); } else if (bytesToWrite <= maxCommandDataSize && referenceArg.length() == 0) execute("putFile" + " " + spaceToEsc(fileName) + " " + bytesToWrite + " " + Integer.toOctalString(mode) + " " + file.lastModified() / 1000, inFile, bytesToWrite); else { execute("putFileStart" + " " + spaceToEsc(fileName) + referenceArg); while (!cancel && bytesToWrite > 0) { long block = maxCommandDataSize; if (block > bytesToWrite) block = bytesToWrite; execute("putFileContinue" + " " + block, inFile, block); bytesToWrite -= block; } execute("putFileEnd" + " " + Integer.toOctalString(mode) + " " + file.lastModified() / 1000); } if (MD5Sum != null) MD5Sums.write(( bytesToHex(((MessageDigest) MD5Sum).digest()) + " " + fileName.substring(1) + "\n") .getBytes("UTF-8")); if (inFile != null) inFile.close(); } view.setProgress(transmittedKB); State.numOfFiles++; if (node == MainWin.bootImageNode) Options.bootImage = fileName; } void putTree(String dirName, DirTreeNode node) throws java.io.IOException, CommandError, Exception { String nodeToString = node.toString(); String dirName2; if (dirName.equals("/")) dirName2 = ""; else dirName2 = dirName + '/' + nodeToString; view.setFileName(dirName2); File dir = node.getFile(); if (dir instanceof ServerFile && (nodeToString.equals("isolinux") || nodeToString.equals("boot"))) { // expand isolinux directories on the server // since mkisofs likes to modify the file isolinux.bin // int n = node.getIndex("isolinux.bin"); CDNode dummyDir = new CDNode("dummy"); node.insert(dummyDir); node.remove(dummyDir); dir = node.getFile(); } if (dir instanceof ServerFile) { try { // just make a link to the file on the server if (MD5Sum != null) view.setMessage(i18n("titleCalculateMD5")); execute("linkFile" + " " + spaceToEsc(dir.getPath()) + " " + spaceToEsc(dirName2) + (MD5Sum != null? " md5": "")); if (MD5Sum != null) view.setMessage(i18n("titlePutFiles")); } catch (CommandError e) { if (!ignoreFile(dirName2)) throw e; } return; } // transfer the files and directories contained // in this directory for (int i = 0; i < node.getChildCount(); i++) { if (cancel) return; editFileName(node, i); DirTreeNode child = (DirTreeNode)node.getChildAt(i); if (child.isLeaf()) putFile(dirName2, child); else putTree(dirName2, child); } if (dir != null && dirName2.length() != 0) // create the directory and set the modification time execCommand("mkdir " + spaceToEsc(dirName2) + " " + "555 " + dir.lastModified() / 1000); } public void run() { // System.out.println("PutFiles"); view.setRange(0, sessionSizeKB); show(view, i18n("titlePutFiles")); // for netscape if (CDcreator.isNetscapeVM()) try { netscape.security.PrivilegeManager .enablePrivilege("UniversalFileRead"); } catch (Exception e) { } boolean error = false; doNotShorten = false; view.ignoreAllUnreadableBox.setSelected(false); view.shortenAllBox.setSelected(false); try { putTree("/", CDRootNode); if (MD5Sum != null) { java.util.Calendar c = new java.util.GregorianCalendar(); execute("putFile" + " /MD5Sums/" + spaceToEsc( Format.right(c.get(c.YEAR), 4, '0') + Format.right( c.get(c.MONTH) - c.JANUARY + 1, 2, '0') + Format.right(c.get(c.DAY_OF_MONTH), 2, '0') + "_" + Format.right(c.get(c.HOUR_OF_DAY), 2, '0') + Format.right(c.get(c.MINUTE), 2, '0') + ".md5") + " " + MD5Sums.size() + " " + Integer.toOctalString(0444) + " " + new java.util.Date().getTime() / 1000, new ByteArrayInputStream(MD5Sums.toByteArray()), MD5Sums.size()); execute("linkFile" + " " + spaceToEsc("/Server/tools/MD5Verify.jar") + " " + spaceToEsc("/MD5Verify.jar")); } } catch (java.io.IOException e) { status = ERROR; System.out.println("PutFiles: " + e); } catch (CommandError cmdErr) { status = ERROR; error = true; } catch (Exception e) { status = ERROR; } if (error) showCentered(new JOptionPane( lastResult, JOptionPane.ERROR_MESSAGE, JOptionPane.DEFAULT_OPTION, null, okOption), i18n("error")); else super.run(); // System.out.println("PutFiles end"); } private void to64Characters(DirTreeNode node, int index) throws Exception { final int max = 64; final String name = node.getChildAt(index).toString(); int len = name.length(); int max2; try { max2 = max - (name.getBytes("UTF-8").length - len); } catch (Exception e) { max2 = max; } if (len <= max2) return; if (!doNotShorten && !view.shortenAllBox.isSelected()) try { shorten = false; SwingUtilities.invokeAndWait(new Runnable() { public void run() { String[] options = { i18n("PutFiles.shorten"), i18n("PutFiles.shortenAll"), i18n("PutFiles.ignore"), i18n("PutFiles.ignoreAlways"), i18n("cancel") }; JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); JLabel l = new JLabel(i18n("PutFiles.above64characters")); l.setAlignmentX(JLabel.LEFT_ALIGNMENT); panel.add(l); JTextArea t = new JTextArea(name); t.setAlignmentX(JTextArea.LEFT_ALIGNMENT); t.setEditable(false); panel.add(t); t.setAlignmentX(JLabel.LEFT_ALIGNMENT); l = new JLabel(i18n("PutFiles.problem")); panel.add(l); int result = showOptionDialog( panel, i18n("warning"), JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, options, options[0]); if (result == 0) shorten = true; else if (result == 1) view.shortenAllBox.setSelected(true); else if (result == 3) doNotShorten = true; else if (result == 4) shorten = doNotShorten = true; } }); } catch (Exception e) { } if (shorten && doNotShorten) throw new Exception("shorten cancelled"); if (!shorten && !view.shortenAllBox.isSelected()) return; int dot = name.lastIndexOf('.'); if (dot < len - 5) dot = len; int suffixLen = len - dot; int j0 = 1; for (int i = 2; i <= max2; i++) { for (int j = j0; j < 10 * j0; j++) { String name2 = name.substring(0, max2 - i - suffixLen) + "~" + j + name.substring(len - suffixLen); int k; for (k = 0; k < node.getChildCount(); k++) if (node.getChildAt(k).toString().equals(name2)) break; if (k == node.getChildCount()) { DirTreeNode child = (DirTreeNode)node.getChildAt(index); child.setUserObject(name2); // System.out.println(name2); return; } } j0 *= 10; } } }