001/* 002 * (C) Copyright 2006-2020 Nuxeo (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Laurent Doguin 018 * Florent Guillaume 019 */ 020package org.nuxeo.ecm.platform.convert.plugins; 021 022import java.io.BufferedInputStream; 023import java.io.File; 024import java.io.IOException; 025import java.io.Serializable; 026import java.util.ArrayList; 027import java.util.List; 028import java.util.Map; 029import java.util.Optional; 030import java.util.zip.ZipEntry; 031import java.util.zip.ZipFile; 032import java.util.zip.ZipInputStream; 033 034import org.apache.commons.text.StringEscapeUtils; 035import org.nuxeo.ecm.core.api.Blob; 036import org.nuxeo.ecm.core.api.Blobs; 037import org.nuxeo.ecm.core.api.NuxeoException; 038import org.nuxeo.ecm.core.api.blobholder.BlobHolder; 039import org.nuxeo.ecm.core.api.blobholder.SimpleBlobHolder; 040import org.nuxeo.ecm.core.api.impl.blob.ZipEntryBlob; 041import org.nuxeo.ecm.core.convert.api.ConversionException; 042import org.nuxeo.ecm.core.convert.extension.Converter; 043import org.nuxeo.ecm.core.convert.extension.ConverterDescriptor; 044 045/** 046 * Zip2Html converter. 047 */ 048public class Zip2HtmlConverter implements Converter { 049 050 protected static final String INDEX_HTML = "index.html"; 051 052 @Override 053 public BlobHolder convert(BlobHolder blobHolder, Map<String, Serializable> parameters) throws ConversionException { 054 Blob zipBlob = blobHolder.getBlob(); 055 String mimeType = zipBlob.getMimeType(); 056 if (!mimeType.equals("application/zip") && !mimeType.equals("application/x-zip-compressed")) { 057 throw new ConversionException("not a zip file", blobHolder); 058 } 059 060 List<String> names = new ArrayList<>(); 061 Optional<Blob> indexBlob = listNamesAndCreateIndex(zipBlob, names); 062 063 List<Blob> blobs = new ArrayList<>(); 064 indexBlob.ifPresent(blobs::add); 065 names.forEach(name -> blobs.add(new ZipEntryBlob(zipBlob, name))); 066 return new SimpleBlobHolder(blobs); 067 } 068 069 protected Optional<Blob> listNamesAndCreateIndex(Blob zipBlob, List<String> names) { 070 File file = zipBlob.getFile(); 071 if (file != null) { 072 // if there's a file then we can be fast 073 try (ZipFile zipFile = new ZipFile(file)) { 074 zipFile.stream().forEach(entry -> names.add(entry.getName())); 075 } catch (IOException e) { 076 throw new NuxeoException(e); 077 } 078 } else { 079 // else use the stream 080 try (ZipInputStream zin = new ZipInputStream(new BufferedInputStream(zipBlob.getStream()))) { 081 ZipEntry entry; 082 while ((entry = zin.getNextEntry()) != null) { 083 names.add(entry.getName()); 084 } 085 } catch (IOException e) { 086 throw new NuxeoException(e); 087 } 088 } 089 090 orderIndexFirst(names); 091 if (names.isEmpty() || names.get(0).contains(INDEX_HTML)) { 092 return Optional.empty(); // no index blob to create 093 } 094 Blob indexBlob = createIndexBlob(zipBlob.getFilename(), names); 095 return Optional.of(indexBlob); 096 } 097 098 // see also similar method in SimpleCachableBlobHolder 099 protected void orderIndexFirst(List<String> names) { 100 String indexName = null; 101 for (String name : names) { 102 if (name.contains(INDEX_HTML) && (indexName == null || name.compareTo(indexName) < 0)) { 103 indexName = name; 104 } 105 } 106 if (indexName != null) { 107 names.remove(indexName); 108 names.add(0, indexName); 109 } 110 } 111 112 protected Blob createIndexBlob(String title, List<String> names) { 113 StringBuilder page = new StringBuilder("<html><body>"); 114 page.append("<h1>") 115 .append(StringEscapeUtils.escapeEcmaScript(StringEscapeUtils.escapeHtml4(title))) 116 .append("</h1>"); 117 page.append("<ul>"); 118 for (String name : names) { 119 String fn = StringEscapeUtils.escapeEcmaScript(StringEscapeUtils.escapeHtml4(name)); 120 page.append("<li><a href=\"").append(fn).append("\">"); 121 page.append(fn); 122 page.append("</a></li>"); 123 } 124 page.append("</ul></body></html>"); 125 return Blobs.createBlob(page.toString(), "text/html", null, INDEX_HTML); 126 } 127 128 @Override 129 public void init(ConverterDescriptor descriptor) { 130 } 131 132}