1 #ifndef NO_ZAE
2 #include <fstream>
3 #include <dae.h>
4 #include <dae/daeErrorHandler.h>
5 #include <dae/daeZAEUncompressHandler.h>
6
7 namespace fs = boost::filesystem;
8
9 //-----------------------------------------------------------------
10 const std::string daeZAEUncompressHandler::MANIFEST_FILE_NAME("manifest.xml");
11 const std::string daeZAEUncompressHandler::MANIFEST_FILE_ROOT_ELEMENT_NAME("dae_root");
12 const int daeZAEUncompressHandler::CASE_INSENSITIVE = 2;
13 const int daeZAEUncompressHandler::BUFFER_SIZE = 1024;
14 const std::string daeZAEUncompressHandler::EMPTY_STRING = "";
15
16 //-----------------------------------------------------------------
daeZAEUncompressHandler(const daeURI & zaeFile)17 daeZAEUncompressHandler::daeZAEUncompressHandler( const daeURI& zaeFile )
18 : mZipFile(NULL)
19 , mZipFileURI(zaeFile)
20 , mValidZipFile(false)
21 , mRootFilePath("")
22 {
23 std::string zipFilePath = cdom::uriToNativePath(zaeFile.getURI());
24 mZipFile = unzOpen(zipFilePath.c_str());
25
26 mValidZipFile = mZipFile != NULL;
27
28 #if defined(BOOST_FILESYSTEM_VERSION) && BOOST_FILESYSTEM_VERSION >= 3
29 mTmpDir = (fs::temp_directory_path() / fs::unique_path()).string();
30 #else
31 mTmpDir = cdom::getSafeTmpDir() + cdom::getRandomFileName() + cdom::getFileSeparator() + mZipFileURI.pathFile() + cdom::getFileSeparator();
32 #endif
33
34 }
35
36 //-----------------------------------------------------------------
~daeZAEUncompressHandler()37 daeZAEUncompressHandler::~daeZAEUncompressHandler()
38 {
39 if (mZipFile != NULL)
40 unzClose(mZipFile);
41 }
42
43 //-----------------------------------------------------------------
obtainRootFilePath()44 const std::string& daeZAEUncompressHandler::obtainRootFilePath()
45 {
46
47 if (!isZipFile())
48 return EMPTY_STRING;
49 if (boost::filesystem::create_directories(mTmpDir))
50 {
51 if (extractArchive(mZipFile, mTmpDir))
52 {
53 if (retrieveRootURIFromManifest(mTmpDir))
54 {
55 return mRootFilePath;
56 }
57 else
58 {
59 // TODO find root file without manifest
60 }
61 }
62 else
63 {
64 daeErrorHandler::get()->handleError("Error extracting archive in daeZAEUncompressHandler::obtainRootFilePath\n");
65 }
66 }
67 else
68 {
69 daeErrorHandler::get()->handleError("Error creating tmp dir in daeZAEUncompressHandler::obtainRootFilePath\n");
70 }
71
72 boost::filesystem::remove_all(this->getTmpDir());
73 return EMPTY_STRING;
74 }
75
76 //-----------------------------------------------------------------
retrieveRootURIFromManifest(const std::string & tmpDir)77 bool daeZAEUncompressHandler::retrieveRootURIFromManifest(const std::string& tmpDir)
78 {
79 // extract via libxml.
80 bool error = false;
81 std::string manifest_path = (fs::path(tmpDir) / MANIFEST_FILE_NAME).string();
82 xmlTextReaderPtr xmlReader = xmlReaderForFile(manifest_path.c_str(), NULL, 0);
83
84 if (xmlReader)
85 {
86 if (findManifestRootElement(xmlReader))
87 {
88 if (xmlTextReaderRead(xmlReader))
89 {
90 if (xmlTextReaderNodeType(xmlReader) == XML_READER_TYPE_TEXT) {
91 const xmlChar* xmlText = xmlTextReaderConstValue(xmlReader);
92
93 // copy xmlText.
94 std::string rootFilePath((daeString)xmlText);
95
96 // destroy xmlText.
97 xmlTextReaderRead(xmlReader);
98
99 cdom::trimWhitespaces(rootFilePath);
100 mRootFilePath = (fs::path(tmpDir) / rootFilePath).string();
101 }
102 else
103 {
104 error = true;
105 }
106 }
107 else
108 {
109 error = true;
110 }
111 }
112 else
113 {
114 error = true;
115 }
116 }
117 else
118 {
119 error = true;
120 }
121
122 if (xmlReader)
123 xmlFreeTextReader(xmlReader);
124 if (error)
125 {
126 daeErrorHandler::get()->handleError("Error parsing manifest.xml in daeZAEUncompressHandler::retrieveRootURIFromManifest\n");
127 return false;
128 }
129
130 return true;
131 }
132
133 //-----------------------------------------------------------------
findManifestRootElement(xmlTextReaderPtr xmlReader)134 bool daeZAEUncompressHandler::findManifestRootElement( xmlTextReaderPtr xmlReader )
135 {
136 while(xmlTextReaderNodeType(xmlReader) != XML_READER_TYPE_ELEMENT)
137 {
138 if (xmlTextReaderRead(xmlReader) != 1) {
139 return false;
140 }
141 }
142
143 daeString elementName = (daeString)xmlTextReaderConstName(xmlReader);
144 if (strcmp(elementName, MANIFEST_FILE_ROOT_ELEMENT_NAME.c_str()) == 0)
145 {
146 return true;
147 }
148 return findManifestRootElement(xmlReader);
149 }
150
151 //-----------------------------------------------------------------
extractArchive(unzFile zipFile,const std::string & destDir)152 bool daeZAEUncompressHandler::extractArchive( unzFile zipFile, const std::string& destDir )
153 {
154 bool error = false;
155 unz_global_info globalZipInfo;
156
157 if (unzGetGlobalInfo (zipFile, &globalZipInfo) == UNZ_OK)
158 {
159 for (unsigned int i=0; i<globalZipInfo.number_entry; ++i)
160 {
161 if (!extractFile(zipFile, destDir))
162 {
163 error = true;
164 break;
165 }
166
167 if ((i+1)<globalZipInfo.number_entry)
168 {
169 if (unzGoToNextFile(zipFile) != UNZ_OK)
170 {
171 daeErrorHandler::get()->handleError("Error moving to next file in zip archive in daeZAEUncompressHandler::extractArchive\n");
172 error = true;
173 break;
174 }
175 }
176 }
177 }
178 else
179 {
180 daeErrorHandler::get()->handleError("Error getting info for zip archive in daeZAEUncompressHandler::extractArchive\n");
181 error = true;
182 }
183 return !error;
184 }
185
186 //-----------------------------------------------------------------
extractFile(unzFile zipFile,const std::string & destDir)187 bool daeZAEUncompressHandler::extractFile( unzFile zipFile, const std::string& destDir )
188 {
189 bool error = false;
190
191 unz_file_info fileInfo;
192 char currentFileName[256]; // ARGH !!!
193 int fileInfoResult = unzGetCurrentFileInfo(zipFile, &fileInfo, currentFileName, sizeof(currentFileName), 0, 0, 0, 0);
194 if (fileInfoResult == UNZ_OK)
195 {
196 if ( currentFileName[ strlen(currentFileName)-1 ] == '/')
197 {
198 if (!boost::filesystem::create_directories(fs::path(destDir) / currentFileName))
199 {
200 daeErrorHandler::get()->handleError("Error creating dir from zip archive in daeZAEUncompressHandler::extractFile\n");
201 error = true;
202 }
203 }
204 else
205 {
206 if (unzOpenCurrentFile(zipFile) == UNZ_OK)
207 {
208
209 char* buffer = 0;
210 int readBytes = 1;
211 buffer = new char[ BUFFER_SIZE ];
212 fs::path currentOutFilePath= fs::path(destDir) / std::string(currentFileName);
213 std::ofstream outFile(currentOutFilePath.string().c_str(), std::ios::binary);
214
215 while (readBytes > 0)
216 {
217 readBytes = unzReadCurrentFile(zipFile, buffer, BUFFER_SIZE);
218 outFile.write(buffer, readBytes);
219 }
220 delete[] buffer;
221 outFile.close();
222
223 if (readBytes >= 0)
224 {
225 if (unzCloseCurrentFile(zipFile) == UNZ_CRCERROR)
226 {
227 daeErrorHandler::get()->handleError("CRC error while opening file in zip archive in daeZAEUncompressHandler::extractFile\n");
228 error = true;
229 }
230 else
231 {
232 if (!checkAndExtractInternalArchive(currentOutFilePath.string()))
233 {
234 error = true;
235 }
236 }
237 }
238 else
239 {
240 daeErrorHandler::get()->handleError("Error reading file in zip archive in daeZAEUncompressHandler::extractFile\n");
241 error = true;
242 }
243
244 }
245 else
246 {
247 daeErrorHandler::get()->handleError("Error opening file in zip archive in daeZAEUncompressHandler::extractFile\n");
248 error = true;
249 }
250 }
251 }
252 else
253 {
254 daeErrorHandler::get()->handleError("Error getting info for file in zip archive in daeZAEUncompressHandler::extractFile\n");
255 error = true;
256 }
257
258 return !error;
259 }
260
261 //-----------------------------------------------------------------
checkAndExtractInternalArchive(const std::string & filePath)262 bool daeZAEUncompressHandler::checkAndExtractInternalArchive( const std::string& filePath )
263 {
264 unzFile zipFile = unzOpen(filePath.c_str());
265 if (zipFile == NULL)
266 {
267 // TODO check for other compression formats.
268 return true;
269 }
270
271 bool error = false;
272
273 boost::filesystem::path archivePath(filePath);
274 std::string dir = archivePath.branch_path().string();
275
276 const std::string& randomSegment = cdom::getRandomFileName();
277 std::string tmpDir = dir + cdom::getFileSeparator() + randomSegment + cdom::getFileSeparator();
278 if (boost::filesystem::create_directory(tmpDir))
279 {
280 if (!extractArchive(zipFile, tmpDir))
281 {
282 daeErrorHandler::get()->handleError("Could not extract internal zip archive in daeZAEUncompressHandler::checkAndExtractInternalArchive\n");
283 error = true;
284 }
285 }
286 else
287 {
288 daeErrorHandler::get()->handleError("Could not create temporary directory for extracting internal zip archive in daeZAEUncompressHandler::checkAndExtractInternalArchive\n");
289 error = true;
290 }
291
292 unzClose(zipFile);
293
294 if (!error)
295 {
296 if (boost::filesystem::remove(archivePath))
297 {
298 boost::filesystem::rename(tmpDir, archivePath);
299 }
300 else
301 {
302 daeErrorHandler::get()->handleError("Could not remove internal zip archive in daeZAEUncompressHandler::checkAndExtractInternalArchive\n");
303 error = true;
304 }
305 }
306
307 return !error;
308 }
309
310 #endif //NO_ZAE
311