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