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