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