1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 package org.openoffice.idesupport.zip; 29 30 import java.io.*; 31 import java.util.Enumeration; 32 import java.util.zip.*; 33 import java.beans.PropertyVetoException; 34 35 import org.openoffice.idesupport.filter.FileFilter; 36 import org.openoffice.idesupport.filter.BinaryOnlyFilter; 37 import org.openoffice.idesupport.filter.ExceptParcelFilter; 38 39 import com.sun.star.script.framework.container.ParcelDescriptor; 40 import org.openoffice.idesupport.xml.Manifest; 41 42 public class ParcelZipper 43 { 44 public static final String PARCEL_PREFIX_DIR = "Scripts/"; 45 public static final String PARCEL_EXTENSION = "sxp"; 46 public static final String CONTENTS_DIRNAME = "Contents"; 47 public static final String PARCEL_DESCRIPTOR_XML = "parcel-descriptor.xml"; 48 49 private static ParcelZipper zipper = null; 50 51 private static final FileFilter DEFAULT_FILTER = 52 BinaryOnlyFilter.getInstance(); 53 54 private ParcelZipper() { 55 } 56 57 public static ParcelZipper getParcelZipper() { 58 if (zipper == null) { 59 synchronized(ParcelZipper.class) { 60 if (zipper == null) 61 zipper = new ParcelZipper(); 62 } 63 } 64 return zipper; 65 } 66 67 public String zipParcel(File basedir) throws IOException { 68 File targetfile, targetdir; 69 70 if (basedir.getName().equals(CONTENTS_DIRNAME)) 71 targetdir = basedir.getParentFile(); 72 else 73 targetdir = basedir; 74 75 targetfile = new File(targetdir, targetdir.getName() + "." + PARCEL_EXTENSION); 76 77 return zipParcel(basedir, targetfile, DEFAULT_FILTER); 78 } 79 80 public String zipParcel(File basedir, File targetfile) throws IOException { 81 return zipParcel(basedir, targetfile, DEFAULT_FILTER); 82 } 83 84 public String zipParcel(File basedir, FileFilter filter) throws IOException { 85 File targetfile, targetdir; 86 87 if (basedir.getName().equals(CONTENTS_DIRNAME)) 88 targetdir = basedir.getParentFile(); 89 else 90 targetdir = basedir; 91 92 targetfile = new File(targetdir, targetdir.getName() + "." + PARCEL_EXTENSION); 93 94 return zipParcel(basedir, targetfile, filter); 95 } 96 97 public String zipParcel(File basedir, File targetfile, FileFilter filter) 98 throws IOException { 99 String realpath, tmppath; 100 101 realpath = targetfile.getPath(); 102 tmppath = realpath + ".tmp"; 103 104 File tmpfile = new File(tmppath); 105 ZipOutputStream out = null; 106 try { 107 if (tmpfile.exists() == true) 108 tmpfile.delete(); 109 110 out = new ZipOutputStream(new FileOutputStream(tmpfile)); 111 112 File[] children = basedir.listFiles(); 113 for (int i = 0; i < children.length; i++) 114 addFileToParcel(children[i], "", out, filter); 115 } 116 catch (IOException ioe) { 117 out.close(); 118 tmpfile.delete(); 119 throw ioe; 120 } 121 finally { 122 if (out != null) 123 out.close(); 124 } 125 126 if (targetfile.exists() == true) 127 targetfile.delete(); 128 tmpfile.renameTo(targetfile); 129 130 return targetfile.getAbsolutePath(); 131 } 132 133 private void addFileToParcel(File root, String path, ZipOutputStream out, FileFilter filter) 134 throws IOException { 135 ZipEntry ze; 136 137 if (root.isDirectory() == true) { 138 ze = new ZipEntry(/* PARCEL_PREFIX_DIR + */ path + root.getName() + "/"); 139 out.putNextEntry(ze); 140 out.closeEntry(); 141 File[] children = root.listFiles(); 142 143 for (int i = 0; i < children.length; i++) 144 addFileToParcel(children[i], path + root.getName() + "/", out, filter); 145 } 146 else { 147 if (filter.validate(root.getName()) == false && 148 root.getName().equals("parcel-descriptor.xml") == false) 149 return; 150 151 ze = new ZipEntry(/* PARCEL_PREFIX_DIR + */ path + root.getName()); 152 out.putNextEntry(ze); 153 154 byte[] bytes = new byte[1024]; 155 int len; 156 157 FileInputStream fis = null; 158 try { 159 fis = new FileInputStream(root); 160 161 while ((len = fis.read(bytes)) != -1) 162 out.write(bytes, 0, len); 163 } 164 finally { 165 if (fis != null) fis.close(); 166 } 167 out.closeEntry(); 168 } 169 } 170 171 public boolean isOverwriteNeeded(File parcel, File target) 172 throws IOException 173 { 174 boolean result; 175 176 if (target.isDirectory()) 177 result = isDirectoryOverwriteNeeded(parcel, target); 178 else 179 result = isDocumentOverwriteNeeded(parcel, target); 180 181 return result; 182 } 183 184 private boolean isDirectoryOverwriteNeeded(File parcel, File target) { 185 String parcelDir = getParcelDirFromParcelZip(parcel.getName()); 186 187 File langdir; 188 try { 189 langdir = new File(target, getParcelLanguage(parcel)); 190 } 191 catch (IOException ioe) { 192 return false; 193 } 194 195 if (langdir.exists() == false) 196 return false; 197 198 File[] children = langdir.listFiles(); 199 200 for (int i = 0; i < children.length; i++) 201 if (children[i].getName().equals(parcelDir)) 202 return true; 203 204 return false; 205 } 206 207 private boolean isDocumentOverwriteNeeded(File parcel, File document) 208 throws IOException 209 { 210 ZipFile documentZip = null; 211 boolean result = false; 212 213 try { 214 documentZip = new ZipFile(document); 215 216 String name = 217 PARCEL_PREFIX_DIR + getParcelLanguage(parcel) + 218 "/" + getParcelDirFromParcelZip(parcel.getName()) + 219 "/" + PARCEL_DESCRIPTOR_XML; 220 221 if (documentZip.getEntry(name) != null) 222 result = true; 223 } 224 catch (IOException ioe) { 225 return false; 226 } 227 finally { 228 if (documentZip != null) documentZip.close(); 229 } 230 231 return result; 232 } 233 234 public String deployParcel(File parcel, File target) 235 throws IOException { 236 237 String output = null; 238 if (target.isDirectory()) 239 output = unzipToDirectory(parcel, target); 240 else 241 output = unzipToZip(parcel, target); 242 return output; 243 } 244 245 private String getParcelDirFromParcelZip(String zipname) { 246 String result = zipname.substring(0, zipname.lastIndexOf(".")); 247 return result; 248 } 249 250 private String unzipToDirectory(File parcel, File targetDirectory) 251 throws IOException { 252 253 ZipInputStream in = null; 254 File parcelDir = new File(targetDirectory, 255 getParcelLanguage(parcel) + File.separator + 256 getParcelDirFromParcelZip(parcel.getName())); 257 258 if (isDirectoryOverwriteNeeded(parcel, targetDirectory)) { 259 if (deleteDir(parcelDir) == false) { 260 throw new IOException("Could not overwrite: " + 261 parcelDir.getAbsolutePath()); 262 } 263 } 264 265 try { 266 in = new ZipInputStream(new FileInputStream(parcel)); 267 268 File outFile; 269 ZipEntry inEntry = in.getNextEntry(); 270 byte[] bytes = new byte[1024]; 271 int len; 272 273 while (inEntry != null) { 274 outFile = new File(parcelDir, inEntry.getName()); 275 if (inEntry.isDirectory()) { 276 outFile.mkdir(); 277 } 278 else { 279 if (outFile.getParentFile().exists() != true) 280 outFile.getParentFile().mkdirs(); 281 282 FileOutputStream out = null; 283 try { 284 out = new FileOutputStream(outFile); 285 286 while ((len = in.read(bytes)) != -1) 287 out.write(bytes, 0, len); 288 } 289 finally { 290 if (out != null) out.close(); 291 } 292 } 293 inEntry = in.getNextEntry(); 294 } 295 } 296 finally { 297 if (in != null) in.close(); 298 } 299 300 return parcelDir.getAbsolutePath(); 301 } 302 303 private boolean deleteDir(File basedir) { 304 if (basedir.isDirectory()) { 305 String[] children = basedir.list(); 306 for (int i=0; i<children.length; i++) { 307 boolean success = deleteDir(new File(basedir, children[i])); 308 if (!success) { 309 return false; 310 } 311 } 312 } 313 return basedir.delete(); 314 } 315 316 private String unzipToZip(File parcel, File targetDocument) 317 throws IOException { 318 319 ZipInputStream documentStream = null; 320 ZipInputStream parcelStream = null; 321 ZipOutputStream outStream = null; 322 Manifest manifest; 323 324 String language = getParcelLanguage(parcel); 325 326 if (isDocumentOverwriteNeeded(parcel, targetDocument)) { 327 String parcelName = language + "/" + 328 getParcelDirFromParcelZip(parcel.getName()); 329 removeParcel(targetDocument, parcelName); 330 } 331 332 // first write contents of document to tmpfile 333 File tmpfile = new File(targetDocument.getAbsolutePath() + ".tmp"); 334 335 manifest = addParcelToManifest(targetDocument, parcel); 336 337 try { 338 documentStream = 339 new ZipInputStream(new FileInputStream(targetDocument)); 340 parcelStream = new ZipInputStream(new FileInputStream(parcel)); 341 outStream = new ZipOutputStream(new FileOutputStream(tmpfile)); 342 343 copyParcelToZip(parcelStream, outStream, PARCEL_PREFIX_DIR + 344 language + "/" + getParcelDirFromParcelZip(parcel.getName())); 345 copyDocumentToZip(documentStream, outStream, manifest); 346 } 347 catch (IOException ioe) { 348 tmpfile.delete(); 349 throw ioe; 350 } 351 finally { 352 if (documentStream != null) documentStream.close(); 353 if (parcelStream != null) parcelStream.close(); 354 if (outStream != null) outStream.close(); 355 } 356 357 if (targetDocument.delete() == false) { 358 tmpfile.delete(); 359 throw new IOException("Could not overwrite " + targetDocument); 360 } 361 else 362 tmpfile.renameTo(targetDocument); 363 364 return targetDocument.getAbsolutePath(); 365 } 366 367 private void copyParcelToZip(ZipInputStream in, ZipOutputStream out, 368 String parcelName) throws IOException { 369 370 ZipEntry outEntry; 371 ZipEntry inEntry = in.getNextEntry(); 372 byte[] bytes = new byte[1024]; 373 int len; 374 375 while (inEntry != null) { 376 if(parcelName!=null) 377 outEntry = new ZipEntry(parcelName + "/" + inEntry.getName()); 378 else 379 outEntry = new ZipEntry(inEntry); 380 out.putNextEntry(outEntry); 381 382 if (inEntry.isDirectory() == false) 383 while ((len = in.read(bytes)) != -1) 384 out.write(bytes, 0, len); 385 386 out.closeEntry(); 387 inEntry = in.getNextEntry(); 388 } 389 } 390 391 private void copyDocumentToZip(ZipInputStream in, ZipOutputStream out, 392 Manifest manifest) throws IOException { 393 394 ZipEntry outEntry; 395 ZipEntry inEntry; 396 byte[] bytes = new byte[1024]; 397 int len; 398 399 while ((inEntry = in.getNextEntry()) != null) { 400 401 outEntry = new ZipEntry(inEntry); 402 out.putNextEntry(outEntry); 403 404 if(inEntry.getName().equals("META-INF/manifest.xml") && 405 manifest != null) { 406 InputStream manifestStream = null; 407 try { 408 manifestStream = manifest.getInputStream(); 409 while ((len = manifestStream.read(bytes)) != -1) 410 out.write(bytes, 0, len); 411 } 412 finally { 413 if (manifestStream != null) 414 manifestStream.close(); 415 } 416 } 417 else if (inEntry.isDirectory() == false) { 418 while ((len = in.read(bytes)) != -1) 419 out.write(bytes, 0, len); 420 } 421 422 out.closeEntry(); 423 } 424 } 425 426 public String removeParcel(File document, String parcelName) 427 throws IOException { 428 429 ZipInputStream documentStream = null; 430 ZipOutputStream outStream = null; 431 Manifest manifest = null; 432 433 if (!parcelName.startsWith(PARCEL_PREFIX_DIR)) 434 parcelName = PARCEL_PREFIX_DIR + parcelName; 435 manifest = removeParcelFromManifest(document, parcelName); 436 437 // first write contents of document to tmpfile 438 File tmpfile = new File(document.getAbsolutePath() + ".tmp"); 439 440 try { 441 ZipEntry outEntry; 442 ZipEntry inEntry; 443 byte[] bytes = new byte[1024]; 444 int len; 445 446 documentStream = new ZipInputStream(new FileInputStream(document)); 447 outStream = new ZipOutputStream(new FileOutputStream(tmpfile)); 448 449 while ((inEntry = documentStream.getNextEntry()) != null) { 450 451 if(inEntry.getName().startsWith(parcelName)) 452 continue; 453 454 outEntry = new ZipEntry(inEntry); 455 outStream.putNextEntry(outEntry); 456 457 if(inEntry.getName().equals("META-INF/manifest.xml") && 458 manifest != null) { 459 InputStream manifestStream = null; 460 try { 461 manifestStream = manifest.getInputStream(); 462 while ((len = manifestStream.read(bytes)) != -1) 463 outStream.write(bytes, 0, len); 464 } 465 finally { 466 if (manifestStream != null) 467 manifestStream.close(); 468 } 469 } 470 else if (inEntry.isDirectory() == false) { 471 while ((len = documentStream.read(bytes)) != -1) 472 outStream.write(bytes, 0, len); 473 } 474 475 outStream.closeEntry(); 476 } 477 } 478 catch (IOException ioe) { 479 tmpfile.delete(); 480 throw ioe; 481 } 482 finally { 483 if (documentStream != null) 484 documentStream.close(); 485 486 if (outStream != null) 487 outStream.close(); 488 } 489 490 if (document.delete() == false) { 491 tmpfile.delete(); 492 throw new IOException("Could not overwrite " + document); 493 } 494 else 495 tmpfile.renameTo(document); 496 497 return document.getAbsolutePath(); 498 } 499 500 private Manifest getManifestFromDocument(File document) { 501 ZipFile documentZip = null; 502 Manifest result = null; 503 504 try { 505 documentZip = new ZipFile(document); 506 ZipEntry original = documentZip.getEntry("META-INF/manifest.xml"); 507 if (original != null) { 508 result = new Manifest(documentZip.getInputStream(original)); 509 } 510 } 511 catch (IOException ioe) { 512 result = null; 513 } 514 finally { 515 try { 516 if (documentZip != null) 517 documentZip.close(); 518 } 519 catch (IOException ioe) {} 520 } 521 522 return result; 523 } 524 525 private Manifest addParcelToManifest(File document, File parcel) 526 throws IOException { 527 528 ZipFile parcelZip = null; 529 Manifest result = null; 530 531 result = getManifestFromDocument(document); 532 if (result == null) 533 return null; 534 535 String language = getParcelLanguage(parcel); 536 537 try { 538 parcelZip = new ZipFile(parcel); 539 540 String prefix = PARCEL_PREFIX_DIR + language + "/" + 541 getParcelDirFromParcelZip(parcel.getName()) + "/"; 542 543 Enumeration entries = parcelZip.entries(); 544 while (entries.hasMoreElements()) { 545 ZipEntry entry = (ZipEntry)entries.nextElement(); 546 result.add(prefix + entry.getName()); 547 } 548 } 549 catch (IOException ioe) { 550 return null; 551 } 552 finally { 553 try { 554 if (parcelZip != null) 555 parcelZip.close(); 556 } 557 catch (IOException ioe) {} 558 } 559 560 return result; 561 } 562 563 private Manifest removeParcelFromManifest(File document, String name) { 564 Manifest result = null; 565 566 result = getManifestFromDocument(document); 567 if (result == null) 568 return null; 569 570 result.remove(name); 571 return result; 572 } 573 574 public String getParcelLanguage(File file) throws IOException { 575 ZipFile zf = null; 576 ZipEntry ze = null; 577 InputStream is = null; 578 ParcelDescriptor pd; 579 580 try { 581 zf = new ZipFile(file); 582 ze = zf.getEntry(PARCEL_DESCRIPTOR_XML); 583 584 if (ze == null) 585 throw new IOException("Could not find Parcel Descriptor in parcel"); 586 587 is = zf.getInputStream(ze); 588 pd = new ParcelDescriptor(is); 589 } 590 finally { 591 if (zf != null) 592 zf.close(); 593 594 if (is != null) 595 is.close(); 596 } 597 598 return pd.getLanguage().toLowerCase(); 599 } 600 } 601