From 33ffb0003e0c8ad141f2302c7d325b15d55bbbf7 Mon Sep 17 00:00:00 2001 From: acgessler Date: Sat, 23 Aug 2014 15:42:47 -0700 Subject: [PATCH] Collada Export: escape user-defined strings in XML output. --- code/CMakeLists.txt | 1 + code/ColladaExporter.cpp | 70 ++++--- code/XMLTools.h | 81 +++++++ test/models/Collada/cube_xmlspecialchars.dae | 210 +++++++++++++++++++ 4 files changed, 331 insertions(+), 31 deletions(-) create mode 100644 code/XMLTools.h create mode 100644 test/models/Collada/cube_xmlspecialchars.dae diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 13e297be2..224ae7f57 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -144,6 +144,7 @@ SET( Common_SRCS LogAux.h Bitmap.cpp Bitmap.h + XMLTools.h ) SOURCE_GROUP(Common FILES ${Common_SRCS}) diff --git a/code/ColladaExporter.cpp b/code/ColladaExporter.cpp index 963827966..d2e85a015 100644 --- a/code/ColladaExporter.cpp +++ b/code/ColladaExporter.cpp @@ -47,6 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "Bitmap.h" #include "fast_atof.h" #include "SceneCombiner.h" +#include "XMLTools.h" #include #include @@ -93,6 +94,7 @@ void ExportSceneCollada(const char* pFile, IOSystem* pIOSystem, const aiScene* p } // end of namespace Assimp + // ------------------------------------------------------------------------------------------------ // Constructor for a specific scene to export ColladaExporter::ColladaExporter( const aiScene* pScene, IOSystem* pIOSystem, const std::string& path, const std::string& file) : mIOSystem(pIOSystem), mPath(path), mFile(file) @@ -140,7 +142,7 @@ void ColladaExporter::WriteFile() // useless Collada fu at the end, just in case we haven't had enough indirections, yet. mOutput << startstr << "" << endstr; PushTag(); - mOutput << startstr << "mRootNode->mName.C_Str()) + "\" />" << endstr; + mOutput << startstr << "mRootNode->mName.C_Str()) + "\" />" << endstr; PopTag(); mOutput << startstr << "" << endstr; PopTag(); @@ -236,12 +238,12 @@ void ColladaExporter::WriteHeader() if (!meta || !meta->Get("Author", value)) mOutput << startstr << "" << "Assimp" << "" << endstr; else - mOutput << startstr << "" << value.C_Str() << "" << endstr; + mOutput << startstr << "" << XMLEscape(value.C_Str()) << "" << endstr; if (!meta || !meta->Get("AuthoringTool", value)) mOutput << startstr << "" << "Assimp Exporter" << "" << endstr; else - mOutput << startstr << "" << value.C_Str() << "" << endstr; + mOutput << startstr << "" << XMLEscape(value.C_Str()) << "" << endstr; //mOutput << startstr << "" << mScene->author.C_Str() << "" << endstr; //mOutput << startstr << "" << mScene->authoringTool.C_Str() << "" << endstr; @@ -342,16 +344,20 @@ void ColladaExporter::WriteImageEntry( const Surface& pSurface, const std::strin { if( !pSurface.texture.empty() ) { - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; PushTag(); mOutput << startstr << ""; + + // URL encode image file name first, then XML encode on top + std::stringstream imageUrlEncoded; for( std::string::const_iterator it = pSurface.texture.begin(); it != pSurface.texture.end(); ++it ) { if( isalnum( *it) || *it == '_' || *it == '.' || *it == '/' || *it == '\\' ) - mOutput << *it; + imageUrlEncoded << *it; else - mOutput << '%' << std::hex << size_t( (unsigned char) *it) << std::dec; + imageUrlEncoded << '%' << std::hex << size_t( (unsigned char) *it) << std::dec; } + mOutput << XMLEscape(imageUrlEncoded.str()); mOutput << "" << endstr; PopTag(); mOutput << startstr << "" << endstr; @@ -371,7 +377,7 @@ void ColladaExporter::WriteTextureColorEntry( const Surface& pSurface, const std } else { - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; } PopTag(); mOutput << startstr << "" << endstr; @@ -385,21 +391,21 @@ void ColladaExporter::WriteTextureParamEntry( const Surface& pSurface, const std // if surface is a texture, write out the sampler and the surface parameters necessary to reference the texture if( !pSurface.texture.empty() ) { - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; PushTag(); mOutput << startstr << "" << endstr; PushTag(); - mOutput << startstr << "" << pMatName << "-" << pTypeName << "-image" << endstr; + mOutput << startstr << "" << XMLEscape(pMatName) << "-" << pTypeName << "-image" << endstr; PopTag(); mOutput << startstr << "" << endstr; PopTag(); mOutput << startstr << "" << endstr; - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; PushTag(); mOutput << startstr << "" << endstr; PushTag(); - mOutput << startstr << "" << pMatName << "-" << pTypeName << "-surface" << endstr; + mOutput << startstr << "" << XMLEscape(pMatName) << "-" << pTypeName << "-surface" << endstr; PopTag(); mOutput << startstr << "" << endstr; PopTag(); @@ -439,7 +445,7 @@ void ColladaExporter::WriteMaterials() name = "mat"; materials[a].name = std::string( "m") + boost::lexical_cast (a) + name.C_Str(); for( std::string::iterator it = materials[a].name.begin(); it != materials[a].name.end(); ++it ) { - // isalnum on MSVC asserts for code points in [0,255]. Thus prevent unwanted promotion + // isalnum on MSVC asserts for code points outside [0,255]. Thus prevent unwanted promotion // of char to signed int and take the unsigned char value. if( !isalnum( static_cast(*it) ) ) { *it = '_'; @@ -510,7 +516,7 @@ void ColladaExporter::WriteMaterials() { const Material& mat = *it; // this is so ridiculous it must be right - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; PushTag(); mOutput << startstr << "" << endstr; PushTag(); @@ -561,9 +567,9 @@ void ColladaExporter::WriteMaterials() for( std::vector::const_iterator it = materials.begin(); it != materials.end(); ++it ) { const Material& mat = *it; - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; PushTag(); - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; PopTag(); mOutput << startstr << "" << endstr; } @@ -591,13 +597,14 @@ void ColladaExporter::WriteGeometryLibrary() void ColladaExporter::WriteGeometry( size_t pIndex) { const aiMesh* mesh = mScene->mMeshes[pIndex]; - std::string idstr = GetMeshId( pIndex); + const std::string idstr = GetMeshId( pIndex); + const std::string idstrEscaped = XMLEscape(idstr); if( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 ) return; // opening tag - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; PushTag(); mOutput << startstr << "" << endstr; @@ -627,20 +634,20 @@ void ColladaExporter::WriteGeometry( size_t pIndex) } // assemble vertex structure - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; PushTag(); - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; if( mesh->HasNormals() ) - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a ) { if( mesh->HasTextureCoords( a) ) - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; } for( size_t a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a ) { if( mesh->HasVertexColors( a) ) - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; } PopTag(); @@ -660,7 +667,7 @@ void ColladaExporter::WriteGeometry( size_t pIndex) { mOutput << startstr << "" << endstr; PushTag(); - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; mOutput << startstr << "

"; for( size_t a = 0; a < mesh->mNumFaces; ++a ) { @@ -681,7 +688,7 @@ void ColladaExporter::WriteGeometry( size_t pIndex) { mOutput << startstr << "" << endstr; PushTag(); - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; mOutput << startstr << ""; for( size_t a = 0; a < mesh->mNumFaces; ++a ) @@ -728,11 +735,11 @@ void ColladaExporter::WriteFloatArray( const std::string& pIdString, FloatDataTy std::string arrayId = pIdString + "-array"; - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; PushTag(); // source array - mOutput << startstr << " "; + mOutput << startstr << " "; PushTag(); if( pType == FloatType_TexCoord2 ) @@ -804,11 +811,11 @@ void ColladaExporter::WriteFloatArray( const std::string& pIdString, FloatDataTy // Writes the scene library void ColladaExporter::WriteSceneLibrary() { - std::string scene_name = mScene->mRootNode->mName.C_Str(); + const std::string scene_name_escaped = XMLEscape(mScene->mRootNode->mName.C_Str()); mOutput << startstr << "" << endstr; PushTag(); - mOutput << startstr << "" << endstr; + mOutput << startstr << "" << endstr; PushTag(); // start recursive write at the root node @@ -833,7 +840,8 @@ void ColladaExporter::WriteNode(aiNode* pNode) pNode->mName.Set(ss.str()); } - mOutput << startstr << "mName.data << "\" name=\"" << pNode->mName.data << "\">" << endstr; + const std::string node_name_escaped = XMLEscape(pNode->mName.data); + mOutput << startstr << "" << endstr; PushTag(); // write transformation - we can directly put the matrix there @@ -854,13 +862,13 @@ void ColladaExporter::WriteNode(aiNode* pNode) if( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 ) continue; - mOutput << startstr << "mMeshes[a]) << "\">" << endstr; + mOutput << startstr << "mMeshes[a])) << "\">" << endstr; PushTag(); mOutput << startstr << "" << endstr; PushTag(); mOutput << startstr << "" << endstr; PushTag(); - mOutput << startstr << "mMaterialIndex].name << "\" />" << endstr; + mOutput << startstr << "mMaterialIndex].name) << "\" />" << endstr; PopTag(); mOutput << startstr << "" << endstr; PopTag(); diff --git a/code/XMLTools.h b/code/XMLTools.h new file mode 100644 index 000000000..103f1d815 --- /dev/null +++ b/code/XMLTools.h @@ -0,0 +1,81 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#ifndef INCLUDED_ASSIMP_XML_TOOLS_H +#define INCLUDED_ASSIMP_XML_TOOLS_H + +#include + +namespace Assimp { + // XML escape the 5 XML special characters (",',<,> and &) in |data| + // Based on http://stackoverflow.com/questions/5665231 + std::string XMLEscape(const std::string& data) { + std::string buffer; + + const size_t size = data.size(); + buffer.reserve(size + size / 8); + for(size_t i = 0; i < size; ++i) { + const char c = data[i]; + switch(c) { + case '&' : + buffer.append("&"); + break; + case '\"': + buffer.append("""); + break; + case '\'': + buffer.append("'"); + break; + case '<' : + buffer.append("<"); + break; + case '>' : + buffer.append(">"); + break; + default: + buffer.append(&c, 1); + break; + } + } + return buffer; + } +} + +#endif // INCLUDED_ASSIMP_XML_TOOLS_H diff --git a/test/models/Collada/cube_xmlspecialchars.dae b/test/models/Collada/cube_xmlspecialchars.dae new file mode 100644 index 000000000..94e1bed2d --- /dev/null +++ b/test/models/Collada/cube_xmlspecialchars.dae @@ -0,0 +1,210 @@ + + + + + alorino + Maya 7.0 | ColladaMaya v2.01 Jun 9 2006 at 16:08:19 | FCollada v1.11 + Collada Maya Export Options: bakeTransforms=0;exportPolygonMeshes=1;bakeLighting=0;isSampling=0; +curveConstrainSampling=0;exportCameraAsLookat=0; +exportLights=1;exportCameras=1;exportJointsAndSkin=1; +exportAnimations=1;exportTriangles=0;exportInvisibleNodes=0; +exportNormals=1;exportTexCoords=1;exportVertexColors=1;exportTangents=0; +exportTexTangents=0;exportConstraints=0;exportPhysics=0;exportXRefs=1; +dereferenceXRefs=0;cameraXFov=0;cameraYFov=1 + +Copyright 2006 Sony Computer Entertainment Inc. +Licensed under the SCEA Shared Source License, Version 1.0 (the +"License"); you may not use this file except in compliance with the +License. You may obtain a copy of the License at: +http://research.scea.com/scea_shared_source_license.html +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + + 2006-06-21T21:23:22Z + 2006-06-21T21:23:22Z + + Y_UP + + + + + + + 37.8493 + 1 + 10 + 1000 + + + + + + + + + 37.8501 + 1 + 0.01 + 1000 + + + + + + + + + + 1 1 1 + 1 + 0 + 0 + + + + 1.000000 + + + + + + 1 1 1 + 1 + 0 + 0 + + + + + + + + + + + + + + + + 0 0 0 1 + + + 0 0 0 1 + + + 0.137255 0.403922 0.870588 1 + + + 0.5 0.5 0.5 1 + + + 16 + + + 0 0 0 1 + + + 0.5 + + + 0 0 0 1 + + + 1 + + + 0 + + + + + + + + + + + -50 50 50 50 50 50 -50 -50 50 50 -50 50 -50 50 -50 50 50 -50 -50 -50 -50 50 -50 -50 + + + + + + + + + + 0 0 1 0 0 1 0 0 1 0 0 1 0 1 0 0 1 0 0 1 0 0 1 0 0 -1 0 0 -1 0 0 -1 0 0 -1 0 -1 0 0 -1 0 0 -1 0 0 -1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 0 0 -1 0 0 -1 0 0 -1 0 0 -1 + + + + + + + + + + + + + + + 4 4 4 4 4 4 +

0 0 2 1 3 2 1 3 0 4 1 5 5 6 4 7 6 8 7 9 3 10 2 11 0 12 4 13 6 14 2 15 3 16 7 17 5 18 1 19 5 20 7 21 6 22 4 23

+ +
+
+ + + + + -427.749 333.855 655.017 + 0 1 0 -33 + 1 0 0 -22.1954 + 0 0 1 0 + + + + -500 1000 400 + 0 0 1 0 + 0 1 0 0 + 1 0 0 0 + + + + 0 0 1 0 + 0 1 0 0 + 1 0 0 0 + + + + + + + + + + -427.749 333.855 655.017 + 0 1 0 -33 + 1 0 0 -22.1954 + 0 0 1 0 + + + + 3 4 10 + 0 0 1 0 + 0 1 0 0 + 1 0 0 0 + + + + + + + +