1 package org.daisy.pipeline.zip.calabash.impl;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39 import java.io.BufferedInputStream;
40 import java.io.BufferedOutputStream;
41 import java.io.File;
42 import java.io.FileInputStream;
43 import java.io.FileOutputStream;
44 import java.io.PrintWriter;
45 import java.io.StringReader;
46 import java.io.StringWriter;
47 import java.net.URI;
48 import java.util.Map;
49 import java.util.zip.ZipEntry;
50 import java.util.zip.ZipInputStream;
51
52 import javax.xml.transform.Source;
53 import javax.xml.transform.stream.StreamSource;
54
55 import com.xmlcalabash.core.XProcException;
56 import com.xmlcalabash.core.XProcRuntime;
57 import com.xmlcalabash.io.WritablePipe;
58 import com.xmlcalabash.library.DefaultStep;
59 import com.xmlcalabash.model.RuntimeValue;
60 import com.xmlcalabash.runtime.XAtomicStep;
61
62 import net.sf.saxon.s9api.DocumentBuilder;
63 import net.sf.saxon.s9api.SaxonApiException;
64 import net.sf.saxon.s9api.XdmNode;
65 import net.sf.saxon.s9api.QName;
66
67 import org.daisy.common.xproc.calabash.XProcStep;
68 import org.daisy.common.xproc.calabash.XProcStepProvider;
69 import org.daisy.common.xproc.XProcMonitor;
70
71 import org.osgi.service.component.annotations.Component;
72
73 import org.slf4j.Logger;
74 import org.slf4j.LoggerFactory;
75
76 @Component(
77 name = "letex:unzip",
78 service = { XProcStepProvider.class },
79 property = { "type:String={http://www.le-tex.de/namespace}unzip" }
80 )
81 public class UnZipProvider implements XProcStepProvider {
82
83
84 Logger logger = LoggerFactory.getLogger(this.getClass());
85
86
87
88
89 @Override
90 public XProcStep newStep(XProcRuntime runtime, XAtomicStep step, XProcMonitor monitor, Map<String,String> properties) {
91 return new UnZip(runtime, step);
92 }
93
94
95
96
97 public void activate() {
98 logger.trace("Activating letex:unzip provider");
99 }
100
101 public static class UnZip
102 extends DefaultStep implements XProcStep
103 {
104 public UnZip(XProcRuntime runtime, XAtomicStep step)
105 {
106 super(runtime,step);
107 }
108
109 @Override
110 public void setOutput(String port, WritablePipe pipe)
111 {
112 myResult = pipe;
113 }
114
115 @Override
116 public void reset()
117 {
118 myResult.resetWriter();
119 }
120
121 @Override
122 public void run()
123 throws SaxonApiException
124 {
125 super.run();
126
127 String result = null;
128 File folder = null, source = null;
129 try {
130 RuntimeValue href = getOption(new QName("zip"));
131 URI sourceUri = href.getBaseURI().resolve(href.getString());
132 href = getOption(new QName("dest-dir"));
133 URI destinationUri = href.getBaseURI().resolve(href.getString());
134
135 if (!"file".equals(sourceUri.getScheme())) {
136 throw new XProcException(step, "Only file: scheme URIs are supported by zip URI.");
137 } else {
138 source = new File(sourceUri.getPath());
139 if (!source.isFile()) {
140 throw new XProcException(step, "The zip URI must refer to a file.");
141 }
142 }
143
144 if (!"file".equals(destinationUri.getScheme())) {
145 throw new XProcException(step, "Only file: scheme URIs are supported by dest-dir URI.");
146 } else {
147 folder = new File(destinationUri.getPath());
148 if (!folder.exists()) {
149 folder.mkdirs();
150 }
151 if (!folder.isDirectory()) {
152 throw new XProcException(step, "The dest-dir URI must refer to a directory.");
153 }
154 if (folder.getParentFile() == null || folder.getParentFile().getParentFile() == null) {
155
156
157
158 throw new XProcException(step, "The dest-dir URI must not be the file system root directory (or any of its children) (was: \""+folder.getCanonicalPath()+"\").");
159 }
160 }
161
162 String overwriteS = getOption(new QName("overwrite")).getString();
163 boolean overwrite = false;
164 if (overwriteS != null) {
165 if (overwriteS.matches("yes")) {
166 overwrite = true;
167 }
168 }
169
170 String filename = "";
171 if (getOption(new QName("file"))!=null) {
172 filename = getOption(new QName("file")).getString();
173 }
174
175 if(! folder.exists()) {
176 folder.mkdirs();
177 }
178
179 final int BUFFER = 2048;
180 BufferedOutputStream dest = null;
181 FileInputStream fis = new FileInputStream(source);
182 ZipInputStream zis = new ZipInputStream(new BufferedInputStream(fis));
183 ZipEntry entry;
184
185 int numFiles = 0;
186 result = "<c:files xmlns:c=\"http://www.w3.org/ns/xproc-step\" xml:base=\"" + folder.toURI() + "\">";
187
188 if(filename.equals("")) {
189 while((entry = zis.getNextEntry()) != null) {
190 int count;
191 byte data[] = new byte[BUFFER];
192
193 if(entry.isDirectory()) {
194 File dir = new File(folder, entry.getName());
195 if (!dir.exists()) { dir.mkdirs(); }
196 } else {
197 numFiles++;
198 result += "<c:file name=\"" + entry.getName() + "\"/>";
199 File destFile = new File(folder, entry.getName());
200 if (destFile.exists()) {
201 if (overwrite) {
202 deleteDirectory(destFile);
203 } else {
204 continue;
205 }
206 }
207 File destdir = destFile.getParentFile();
208 if (!destdir.exists()) { destdir.mkdirs(); }
209 FileOutputStream fos = new FileOutputStream(destFile);
210 dest = new BufferedOutputStream(fos, BUFFER);
211 while ((count = zis.read(data, 0, BUFFER)) != -1) {
212 dest.write(data, 0, count);
213 }
214 dest.flush();
215 dest.close();
216 }
217 }
218 } else {
219 while((entry = zis.getNextEntry()) != null) {
220 if(filename.equals(entry.getName())) {
221 int count;
222 byte data[] = new byte[BUFFER];
223
224 numFiles++;
225 result += "<c:file name=\"" + entry.getName() + "\"/>";
226 File destFile = new File(folder, entry.getName());
227 if (destFile.exists()) {
228 if (overwrite) {
229 deleteDirectory(destFile);
230 } else {
231 continue;
232 }
233 }
234 File destdir = destFile.getParentFile();
235 if (!destdir.exists()) { destdir.mkdirs(); }
236 FileOutputStream fos = new FileOutputStream(destFile);
237 dest = new BufferedOutputStream(fos, BUFFER);
238 while ((count = zis.read(data, 0, BUFFER)) != -1) {
239 dest.write(data, 0, count);
240 }
241 dest.flush();
242 dest.close();
243 }
244 }
245 }
246
247 zis.close();
248 if (numFiles == 0) {
249 result = "<c:error xmlns:c=\"http://www.w3.org/ns/xproc-step\" xmlns:letex=\"http://www.le-tex.de/namespace\" code=\"zip-error\" href=\""+folder.toURI()+source.toURI()+"\">No content processed. Zip file may empty or corrupted.</c:error>";
250 } else {
251 result += "</c:files>";
252 }
253
254 } catch(Exception e) {
255 StringWriter stackTraceWriter = new StringWriter();
256 e.printStackTrace(new PrintWriter(stackTraceWriter));
257 String stackTrace = stackTraceWriter.toString().replaceAll("-", " - ");
258 result = "<c:error xmlns:c=\"http://www.w3.org/ns/xproc-step\" xmlns:letex=\"http://www.le-tex.de/namespace\" code=\"zip-error\" href=\""+folder.toURI()+source.toURI()+"\">Zip file seems to be corrupted: "+e.getMessage()+"<!-- "+stackTrace+" --></c:error>";
259 }
260 DocumentBuilder builder = runtime.getProcessor().newDocumentBuilder();
261 Source src = new StreamSource(new StringReader(result));
262 XdmNode doc = builder.build(src);
263
264 myResult.write(doc);
265 }
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288