#ifndef NO_ZAE #include <fstream> #include <dae.h> #include <dae/daeErrorHandler.h> #include <dae/daeZAEUncompressHandler.h> //----------------------------------------------------------------- const std::string daeZAEUncompressHandler::MANIFEST_FILE_NAME("manifest.xml"); const std::string daeZAEUncompressHandler::MANIFEST_FILE_ROOT_ELEMENT_NAME("dae_root"); const int daeZAEUncompressHandler::CASE_INSENSITIVE = 2; const int daeZAEUncompressHandler::BUFFER_SIZE = 1024; const std::string daeZAEUncompressHandler::EMPTY_STRING = ""; //----------------------------------------------------------------- daeZAEUncompressHandler::daeZAEUncompressHandler( const daeURI& zaeFile ) : mZipFile(NULL) , mZipFileURI(zaeFile) , mValidZipFile(false) , mRootFilePath("") { std::string zipFilePath = cdom::uriToNativePath(zaeFile.getURI()); mZipFile = unzOpen(zipFilePath.c_str()); mValidZipFile = mZipFile != NULL; mTmpDir = cdom::getSafeTmpDir() + cdom::getRandomFileName() + cdom::getFileSeparator() + mZipFileURI.pathFile() + cdom::getFileSeparator(); } //----------------------------------------------------------------- daeZAEUncompressHandler::~daeZAEUncompressHandler() { if (mZipFile != NULL) unzClose(mZipFile); } //----------------------------------------------------------------- const std::string& daeZAEUncompressHandler::obtainRootFilePath() { if (!isZipFile()) return EMPTY_STRING; if (boost::filesystem::create_directories(mTmpDir)) { if (extractArchive(mZipFile, mTmpDir)) { if (retrieveRootURIFromManifest(mTmpDir)) { return mRootFilePath; } else { // TODO find root file without manifest } } else { daeErrorHandler::get()->handleError("Error extracting archive in daeZAEUncompressHandler::obtainRootFilePath\n"); } } else { daeErrorHandler::get()->handleError("Error creating tmp dir in daeZAEUncompressHandler::obtainRootFilePath\n"); } boost::filesystem::remove_all(this->getTmpDir()); return EMPTY_STRING; } //----------------------------------------------------------------- bool daeZAEUncompressHandler::retrieveRootURIFromManifest(const std::string& tmpDir) { // extract via libxml. bool error = false; xmlTextReaderPtr xmlReader = xmlReaderForFile( (tmpDir + MANIFEST_FILE_NAME).c_str(), NULL, 0 ); if (xmlReader) { if (findManifestRootElement(xmlReader)) { if (xmlTextReaderRead(xmlReader)) { if (xmlTextReaderNodeType(xmlReader) == XML_READER_TYPE_TEXT) { const xmlChar* xmlText = xmlTextReaderConstValue(xmlReader); // copy xmlText. std::string rootFilePath((daeString)xmlText); // destroy xmlText. xmlTextReaderRead(xmlReader); cdom::trimWhitespaces(rootFilePath); mRootFilePath = cdom::nativePathToUri(tmpDir + rootFilePath); } else { error = true; } } else { error = true; } } else { error = true; } } else { error = true; } if (xmlReader) xmlFreeTextReader(xmlReader); if (error) { daeErrorHandler::get()->handleError("Error parsing manifest.xml in daeZAEUncompressHandler::retrieveRootURIFromManifest\n"); return false; } return true; } //----------------------------------------------------------------- bool daeZAEUncompressHandler::findManifestRootElement( xmlTextReaderPtr xmlReader ) { while(xmlTextReaderNodeType(xmlReader) != XML_READER_TYPE_ELEMENT) { if (xmlTextReaderRead(xmlReader) != 1) { return false; } } daeString elementName = (daeString)xmlTextReaderConstName(xmlReader); if (strcmp(elementName, MANIFEST_FILE_ROOT_ELEMENT_NAME.c_str()) == 0) { return true; } return findManifestRootElement(xmlReader); } //----------------------------------------------------------------- bool daeZAEUncompressHandler::extractArchive( unzFile zipFile, const std::string& destDir ) { bool error = false; unz_global_info globalZipInfo; if (unzGetGlobalInfo (zipFile, &globalZipInfo) == UNZ_OK) { for (unsigned int i=0; i<globalZipInfo.number_entry; ++i) { if (!extractFile(zipFile, destDir)) { error = true; break; } if ((i+1)<globalZipInfo.number_entry) { if (unzGoToNextFile(zipFile) != UNZ_OK) { daeErrorHandler::get()->handleError("Error moving to next file in zip archive in daeZAEUncompressHandler::extractArchive\n"); error = true; break; } } } } else { daeErrorHandler::get()->handleError("Error getting info for zip archive in daeZAEUncompressHandler::extractArchive\n"); error = true; } return !error; } //----------------------------------------------------------------- bool daeZAEUncompressHandler::extractFile( unzFile zipFile, const std::string& destDir ) { bool error = false; unz_file_info fileInfo; char currentFileName[256]; // ARGH !!! int fileInfoResult = unzGetCurrentFileInfo(zipFile, &fileInfo, currentFileName, sizeof(currentFileName), 0, 0, 0, 0); if (fileInfoResult == UNZ_OK) { if ( currentFileName[ strlen(currentFileName)-1 ] == '/') { if (!boost::filesystem::create_directories(destDir + currentFileName)) { daeErrorHandler::get()->handleError("Error creating dir from zip archive in daeZAEUncompressHandler::extractFile\n"); error = true; } } else { if (unzOpenCurrentFile(zipFile) == UNZ_OK) { char* buffer = 0; int readBytes = 1; buffer = new char[ BUFFER_SIZE ]; std::string currentOutFilePath(destDir + std::string(currentFileName)); std::ofstream outFile(currentOutFilePath.c_str(), std::ios::binary); while (readBytes > 0) { readBytes = unzReadCurrentFile(zipFile, buffer, BUFFER_SIZE); outFile.write(buffer, readBytes); } delete[] buffer; outFile.close(); if (readBytes >= 0) { if (unzCloseCurrentFile(zipFile) == UNZ_CRCERROR) { daeErrorHandler::get()->handleError("CRC error while opening file in zip archive in daeZAEUncompressHandler::extractFile\n"); error = true; } else { if (!checkAndExtractInternalArchive(currentOutFilePath)) { error = true; } } } else { daeErrorHandler::get()->handleError("Error reading file in zip archive in daeZAEUncompressHandler::extractFile\n"); error = true; } } else { daeErrorHandler::get()->handleError("Error opening file in zip archive in daeZAEUncompressHandler::extractFile\n"); error = true; } } } else { daeErrorHandler::get()->handleError("Error getting info for file in zip archive in daeZAEUncompressHandler::extractFile\n"); error = true; } return !error; } //----------------------------------------------------------------- bool daeZAEUncompressHandler::checkAndExtractInternalArchive( const std::string& filePath ) { unzFile zipFile = unzOpen(filePath.c_str()); if (zipFile == NULL) { // TODO check for other compression formats. return true; } bool error = false; boost::filesystem::path archivePath(filePath); std::string dir = archivePath.branch_path().string(); const std::string& randomSegment = cdom::getRandomFileName(); std::string tmpDir = dir + cdom::getFileSeparator() + randomSegment + cdom::getFileSeparator(); if (boost::filesystem::create_directory(tmpDir)) { if (!extractArchive(zipFile, tmpDir)) { daeErrorHandler::get()->handleError("Could not extract internal zip archive in daeZAEUncompressHandler::checkAndExtractInternalArchive\n"); error = true; } } else { daeErrorHandler::get()->handleError("Could not create temporary directory for extracting internal zip archive in daeZAEUncompressHandler::checkAndExtractInternalArchive\n"); error = true; } unzClose(zipFile); if (!error) { if (boost::filesystem::remove(archivePath)) { boost::filesystem::rename(tmpDir, archivePath); } else { daeErrorHandler::get()->handleError("Could not remove internal zip archive in daeZAEUncompressHandler::checkAndExtractInternalArchive\n"); error = true; } } return !error; } #endif //NO_ZAE