blob: f8a47dee2879b4ec6f8d27f4a32aabbaa2631e6a [file] [log] [blame]
/*
* Copyright (C) 2019 Google Inc.
*
* Licensed under the Apache License, Version 2.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://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
#include "xmlProtoConverter.h"
#include <algorithm>
using namespace std;
using namespace xmlProtoFuzzer;
string ProtoConverter::removeNonAscii(string const& _utf8)
{
string asciiStr{_utf8};
asciiStr.erase(remove_if(asciiStr.begin(), asciiStr.end(), [=](char c) -> bool {
return !(std::isalpha(c) || std::isdigit(c));
}), asciiStr.end());
return asciiStr.empty() ? "fuzz" : asciiStr;
}
void ProtoConverter::visit(Misc const& _x)
{
switch (_x.misc_oneof_case())
{
case Misc::kComment:
m_output << "<!--" << _x.comment() << "-->\n";
break;
case Misc::kInst:
visit(_x.inst());
break;
case Misc::MISC_ONEOF_NOT_SET:
break;
}
}
void ProtoConverter::visit(Prolog const& _x)
{
visit(_x.decl());
visit(_x.doctype());
for (auto const& misc: _x.misc())
visit(misc);
}
void ProtoConverter::visit(KeyValue const& _x)
{
if (!KeyValue::XmlNamespace_IsValid(_x.type()))
return;
switch (_x.type())
{
case KeyValue::ATTRIBUTES:
m_output << "xml:attributes=\"" << removeNonAscii(_x.value()) << "\" ";
break;
case KeyValue::BASE:
m_output << "xml:base=\"" << removeNonAscii(_x.value()) << "\" ";
break;
case KeyValue::CATALOG:
m_output << "xml:catalog=\"" << removeNonAscii(_x.value()) << "\" ";
break;
case KeyValue::ID:
m_output << "xml:id=\"" << removeNonAscii(_x.value()) << "\" ";
break;
case KeyValue::LANG:
m_output << "xml:lang=\"" << removeNonAscii(_x.value()) << "\" ";
break;
case KeyValue::LINK:
m_output << "xml:link=\"" << removeNonAscii(_x.value()) << "\" ";
break;
case KeyValue::SPACE:
m_output << "xml:space=\"" << removeNonAscii(_x.value()) << "\" ";
break;
case KeyValue::SPECIAL:
m_output << "xml:special=\"" << removeNonAscii(_x.value()) << "\" ";
break;
case KeyValue::TEST:
m_output << "xml:test=\"" << removeNonAscii(_x.value()) << "\" ";
break;
case KeyValue::FUZZ:
if (_x.ByteSizeLong() % 2)
m_output << "xmlns:" << removeNonAscii(_x.key()) << "=\"" << removeNonAscii(_x.value()) << "\" ";
else
m_output << removeNonAscii(_x.key()) << "=\"" << removeNonAscii(_x.value()) << "\" ";
break;
case KeyValue_XmlNamespace_KeyValue_XmlNamespace_INT_MIN_SENTINEL_DO_NOT_USE_:
case KeyValue_XmlNamespace_KeyValue_XmlNamespace_INT_MAX_SENTINEL_DO_NOT_USE_:
break;
}
}
void ProtoConverter::visit(ProcessingInstruction const& _x)
{
m_output << "<?" << removeNonAscii(_x.name()) << " ";
for (auto const& prop: _x.kv())
visit(prop);
m_output << "?>\n";
}
void ProtoConverter::visit(Content const& _x)
{
switch (_x.content_oneof_case())
{
case Content::kStr:
m_output << _x.str() << "\n";
break;
case Content::kE:
visit(_x.e());
m_output << "\n";
break;
case Content::kC:
visit(_x.c());
m_output << "\n";
break;
case Content::CONTENT_ONEOF_NOT_SET:
break;
}
}
void ProtoConverter::visit(ElementDecl const& _x)
{
if (!ElementDecl::ContentSpec_IsValid(_x.spec()))
return;
m_output << "<!ELEMENT " << _x.name() << " ";
switch (_x.spec())
{
case ElementDecl::EMPTY:
m_output << "EMPTY>";
break;
case ElementDecl::ANY:
m_output << "ANY>";
break;
case ElementDecl::FUZZ:
m_output << "FUZZ>";
break;
case ElementDecl::MIXED:
m_output << "(#PCDATA";
for (auto const& pcdata: _x.cdata())
m_output << "|" << pcdata;
m_output << ")";
if (_x.cdata_size() > 0)
m_output << "*";
m_output << ">";
break;
case ElementDecl::CHILDREN:
{
m_output << "(";
string delim = "";
for (auto const& str: _x.cdata()) {
m_output << delim << removeNonAscii(str);
delim = ", ";
}
m_output << ")>";
break;
}
case ElementDecl_ContentSpec_ElementDecl_ContentSpec_INT_MIN_SENTINEL_DO_NOT_USE_:
case ElementDecl_ContentSpec_ElementDecl_ContentSpec_INT_MAX_SENTINEL_DO_NOT_USE_:
break;
}
}
void ProtoConverter::visit(AttValue const& _x)
{
if (!isValid(_x))
return;
m_output << "\"";
string prefix;
switch (_x.type())
{
case AttValue::ENTITY:
prefix = "&";
break;
case AttValue::CHAR:
if (_x.ByteSizeLong() % 2)
prefix = "&#";
else
// TODO: Value that follows this must be a
// sequence of hex digits.
prefix = "&#x";
break;
case AttValue::FUZZ:
prefix = "fuzz";
break;
case AttValue_Type_AttValue_Type_INT_MIN_SENTINEL_DO_NOT_USE_:
case AttValue_Type_AttValue_Type_INT_MAX_SENTINEL_DO_NOT_USE_:
break;
}
for (auto const& name: _x.value())
m_output << prefix << removeNonAscii(name) << ";";
m_output << "\"";
}
void ProtoConverter::visit(DefaultDecl const& _x)
{
if (!isValid(_x))
return;
switch (_x.type())
{
case DefaultDecl::REQUIRED:
m_output << "#REQUIRED";
break;
case DefaultDecl::IMPLIED:
m_output << "#IMPLIED";
break;
case DefaultDecl::FIXED:
m_output << "#FIXED ";
visit(_x.att());
break;
case DefaultDecl::FUZZ:
m_output << "#FUZZ";
break;
case DefaultDecl_Type_DefaultDecl_Type_INT_MIN_SENTINEL_DO_NOT_USE_:
case DefaultDecl_Type_DefaultDecl_Type_INT_MAX_SENTINEL_DO_NOT_USE_:
break;
}
}
void ProtoConverter::visit(AttDef const& _x)
{
if (!isValid(_x))
return;
m_output << " " << removeNonAscii(_x.name()) << " ";
switch (_x.type())
{
case AttDef::CDATA:
m_output << "CDATA ";
break;
case AttDef::ID:
m_output << "ID ";
break;
case AttDef::IDREF:
m_output << "IDREF ";
break;
case AttDef::IDREFS:
m_output << "IDREFS ";
break;
case AttDef::ENTITY:
m_output << "ENTITY ";
break;
case AttDef::ENTITIES:
m_output << "ENTITIES ";
break;
case AttDef::NMTOKEN:
m_output << "NMTOKEN ";
break;
case AttDef::NMTOKENS:
m_output << "NMTOKENS ";
break;
case AttDef::FUZZ:
m_output << "FUZZ ";
break;
case AttDef_Type_AttDef_Type_INT_MIN_SENTINEL_DO_NOT_USE_:
case AttDef_Type_AttDef_Type_INT_MAX_SENTINEL_DO_NOT_USE_:
break;
}
visit(_x.def());
}
void ProtoConverter::visit(AttListDecl const& _x)
{
m_output << "<!ATTLIST " << removeNonAscii(_x.name());
for (auto const& att: _x.attdefs())
visit(att);
m_output << ">";
}
void ProtoConverter::visit(NotationDecl const& _x)
{
m_output << "<!NOTATION " << removeNonAscii(_x.name()) << " ";
switch (_x.notation_oneof_case())
{
case NotationDecl::kExt:
visit(_x.ext());
break;
case NotationDecl::kPub:
m_output << "PUBLIC " << _x.pub();
break;
case NotationDecl::kFuzz:
m_output << "FUZZ " << _x.fuzz();
break;
case NotationDecl::NOTATION_ONEOF_NOT_SET:
break;
}
m_output << ">";
}
void ProtoConverter::visit(NDataDecl const& _x)
{
m_output << " NDATA " << _x.name();
}
void ProtoConverter::visit(EntityDef const& _x)
{
switch (_x.entity_oneof_case())
{
case EntityDef::kExt:
visit(_x.ext());
if (_x.ByteSizeLong() % 2)
visit(_x.ndata());
break;
case EntityDef::kVal:
visit(_x.val());
break;
case EntityDef::ENTITY_ONEOF_NOT_SET:
break;
}
}
void ProtoConverter::visit(PEDef const& _x)
{
switch (_x.pedef_oneof_case())
{
case PEDef::kVal:
visit(_x.val());
break;
case PEDef::kId:
visit(_x.id());
break;
case PEDef::PEDEF_ONEOF_NOT_SET:
break;
}
}
void ProtoConverter::visit(EntityValue const& _x)
{
if (!isValid(_x))
return;
m_output << "\"";
string prefix;
switch (_x.type())
{
case EntityValue::ENTITY:
prefix = "&";
break;
case EntityValue::CHAR:
if (_x.ByteSizeLong() % 2)
prefix = "&#";
else
prefix = "&#x";
break;
case EntityValue::PEREF:
prefix = "%";
break;
case EntityValue::FUZZ:
prefix = "fuzz";
break;
case EntityValue_Type_EntityValue_Type_INT_MIN_SENTINEL_DO_NOT_USE_:
case EntityValue_Type_EntityValue_Type_INT_MAX_SENTINEL_DO_NOT_USE_:
break;
}
for (auto const& ref: _x.name())
m_output << prefix << ref << ";";
m_output << "\"";
}
void ProtoConverter::visit(EntityDecl const& _x)
{
if (!isValid(_x))
return;
m_output << "<!ENTITY ";
switch (_x.type())
{
case EntityDecl::GEDECL:
m_output << _x.name() << " ";
visit(_x.ent());
break;
case EntityDecl::PEDECL:
m_output << "% " << _x.name() << " ";
visit(_x.pedef());
break;
case EntityDecl_Type_EntityDecl_Type_INT_MIN_SENTINEL_DO_NOT_USE_:
case EntityDecl_Type_EntityDecl_Type_INT_MAX_SENTINEL_DO_NOT_USE_:
break;
}
m_output << ">";
}
void ProtoConverter::visit(ConditionalSect const& _x)
{
if (!isValid(_x))
return;
switch (_x.type())
{
case ConditionalSect::INCLUDE:
m_output << "<![ INCLUDE [";
visit(_x.ext());
m_output << "]]>";
break;
case ConditionalSect::IGNORE:
m_output << "<![ IGNORE [";
for (auto const& str: _x.ignores())
m_output << "<![" << removeNonAscii(str) << "]]>";
m_output << "]]>";
break;
case ConditionalSect::FUZZ:
m_output << "<![ FUZZ [";
visit(_x.ext());
m_output << "]]>";
break;
case ConditionalSect_Type_ConditionalSect_Type_INT_MIN_SENTINEL_DO_NOT_USE_:
case ConditionalSect_Type_ConditionalSect_Type_INT_MAX_SENTINEL_DO_NOT_USE_:
break;
}
}
void ProtoConverter::visit(OneExtSubsetDecl const& _x)
{
switch (_x.extsubset_oneof_case())
{
case OneExtSubsetDecl::kM:
visit(_x.m());
break;
case OneExtSubsetDecl::kC:
visit(_x.c());
break;
case OneExtSubsetDecl::EXTSUBSET_ONEOF_NOT_SET:
break;
}
}
void ProtoConverter::visit(ExtSubsetDecl const& _x)
{
for (auto const& decl: _x.decls())
visit(decl);
}
void ProtoConverter::visit(CData const& _x)
{
m_output << "<![CDATA[" << removeNonAscii(_x.data()) << "]]>";
}
void ProtoConverter::visit(MarkupDecl const& _x)
{
switch (_x.markup_oneof_case())
{
case MarkupDecl::kE:
visit(_x.e());
break;
case MarkupDecl::kA:
visit(_x.a());
break;
case MarkupDecl::kN:
visit(_x.n());
break;
case MarkupDecl::kM:
visit(_x.m());
break;
case MarkupDecl::kEntity:
visit(_x.entity());
break;
case MarkupDecl::kExt:
visit(_x.ext());
break;
case MarkupDecl::MARKUP_ONEOF_NOT_SET:
break;
}
}
/// Returns predefined element from an Element_Id enum
/// @param _x is an enum that holds the desired type of predefined value
/// @param _prop is a string that holds the value of the desired type
/// @return string holding the predefined value of the form
/// name attribute=\"value\"
string ProtoConverter::getPredefined(Element_Id _x, string const& _prop)
{
string output{};
switch (_x)
{
case Element::XIINCLUDE:
case Element::XIFALLBACK:
case Element::XIHREF:
output = "xi:include href=\"fuzz.xml\"";
case Element::XIPARSE:
output = "xi:include parse=\"xml\"";
case Element::XIXPOINTER:
output = "xi:include xpointer=\"" + removeNonAscii(_prop) + "\"";
case Element::XIENCODING:
output = "xi:include encoding=\"" + removeNonAscii(_prop) + "\"";
case Element::XIACCEPT:
output = "xi:include accept=\"" + removeNonAscii(_prop) + "\"";
case Element::XIACCEPTLANG:
output = "xi:include accept-language=\"" + removeNonAscii(_prop) + "\"";
case Element_Id_Element_Id_INT_MIN_SENTINEL_DO_NOT_USE_:
case Element_Id_Element_Id_INT_MAX_SENTINEL_DO_NOT_USE_:
output = "xi:fuzz xifuzz=\"fuzz\"";
}
return output;
}
/// Returns uri string for a given Element_Id type
string ProtoConverter::getUri(Element_Id _x)
{
if (!Element::Id_IsValid(_x))
return s_XInclude;
switch (_x)
{
case Element::XIINCLUDE:
case Element::XIFALLBACK:
case Element::XIHREF:
case Element::XIPARSE:
case Element::XIXPOINTER:
case Element::XIENCODING:
case Element::XIACCEPT:
case Element::XIACCEPTLANG:
case Element_Id_Element_Id_INT_MIN_SENTINEL_DO_NOT_USE_:
case Element_Id_Element_Id_INT_MAX_SENTINEL_DO_NOT_USE_:
return s_XInclude;
}
}
void ProtoConverter::visit(Element const& _x)
{
if (!isValid(_x))
return;
// Predefined child node
string child = {};
// Predefined uri for child node
string pUri = {};
// Element name
string name = removeNonAscii(_x.name());
switch (_x.type())
{
case Element::PREDEFINED:
child = getPredefined(_x.id(), _x.childprop());
pUri = getUri(_x.id());
break;
case Element::FUZZ:
case Element_Type_Element_Type_INT_MIN_SENTINEL_DO_NOT_USE_:
case Element_Type_Element_Type_INT_MAX_SENTINEL_DO_NOT_USE_:
break;
}
// <name k1=v1 k2=v2 k3=v3>
// <content>
// </name>
// Start name tag: Must be Ascii?
m_output << "<" << name << " ";
// Add uri to name tag
if (!pUri.empty())
m_output << pUri << " ";
for (auto const& prop: _x.kv())
visit(prop);
m_output << ">\n";
// Add attribute
if (!child.empty())
m_output << "<" << child << "/>\n";
// Add content
visit(_x.content());
// Close name tag
m_output << "</" << name << ">\n";
}
void ProtoConverter::visit(ExternalId const& _x)
{
if (!isValid(_x))
return;
switch (_x.type())
{
case ExternalId::SYSTEM:
m_output << "SYSTEM " << "\"" << removeNonAscii(_x.system()) << "\"";
break;
case ExternalId::PUBLIC:
m_output << "PUBLIC " << "\"" << removeNonAscii(_x.pub()) << "\""
<< " " << "\"" << removeNonAscii(_x.system()) << "\"";
break;
case ExternalId::FUZZ:
m_output << "FUZZ " << "\"" << removeNonAscii(_x.pub()) << "\"";
break;
case ExternalId_Type_ExternalId_Type_INT_MIN_SENTINEL_DO_NOT_USE_:
case ExternalId_Type_ExternalId_Type_INT_MAX_SENTINEL_DO_NOT_USE_:
break;
}
}
void ProtoConverter::visit(DocTypeDecl const& _x)
{
m_output << "<!DOCTYPE " << removeNonAscii(_x.name()) << " ";
visit(_x.ext());
m_output << "[";
for (auto const& m: _x.mdecl())
visit(m);
m_output << "]";
m_output << ">\n";
}
void ProtoConverter::visit(VersionNum const& _x)
{
if (!isValid(_x))
return;
switch (_x.type())
{
case VersionNum::STANDARD:
m_output << "\"1.0\"";
break;
case VersionNum::FUZZ:
case VersionNum_Type_VersionNum_Type_INT_MIN_SENTINEL_DO_NOT_USE_:
case VersionNum_Type_VersionNum_Type_INT_MAX_SENTINEL_DO_NOT_USE_:
m_output << "\"" << _x.major() << "." << _x.minor() << "\"";
break;
}
}
void ProtoConverter::visit(Encodings const& _x)
{
if (!Encodings::Enc_IsValid(_x.name()))
return;
m_output << " encoding=\"";
switch (_x.name())
{
case Encodings::BIG5:
m_output << "BIG5";
break;
case Encodings::EUCJP:
m_output << "EUC-JP";
break;
case Encodings::EUCKR:
m_output << "EUC-KR";
break;
case Encodings::GB18030:
m_output << "GB18030";
break;
case Encodings::ISO2022JP:
m_output << "ISO-2022-JP";
break;
case Encodings::ISO2022KR:
m_output << "ISO-2022-KR";
break;
case Encodings::ISO88591:
m_output << "ISO-8859-1";
break;
case Encodings::ISO88592:
m_output << "ISO-8859-2";
break;
case Encodings::ISO88593:
m_output << "ISO-8859-3";
break;
case Encodings::ISO88594:
m_output << "ISO-8859-4";
break;
case Encodings::ISO88595:
m_output << "ISO-8859-5";
break;
case Encodings::ISO88596:
m_output << "ISO-8859-6";
break;
case Encodings::ISO88597:
m_output << "ISO-8859-7";
break;
case Encodings::ISO88598:
m_output << "ISO-8859-8";
break;
case Encodings::ISO88599:
m_output << "ISO-8859-9";
break;
case Encodings::SHIFTJIS:
m_output << "SHIFT_JIS";
break;
case Encodings::TIS620:
m_output << "TIS-620";
break;
case Encodings::USASCII:
m_output << "US-ASCII";
break;
case Encodings::UTF8:
m_output << "UTF-8";
break;
case Encodings::UTF16:
m_output << "UTF-16";
break;
case Encodings::UTF16BE:
m_output << "UTF-16BE";
break;
case Encodings::UTF16LE:
m_output << "UTF-16LE";
break;
case Encodings::WINDOWS31J:
m_output << "WINDOWS-31J";
break;
case Encodings::WINDOWS1255:
m_output << "WINDOWS-1255";
break;
case Encodings::WINDOWS1256:
m_output << "WINDOWS-1256";
break;
case Encodings::FUZZ:
m_output << removeNonAscii(_x.fuzz());
break;
case Encodings_Enc_Encodings_Enc_INT_MIN_SENTINEL_DO_NOT_USE_:
case Encodings_Enc_Encodings_Enc_INT_MAX_SENTINEL_DO_NOT_USE_:
break;
}
m_output << "\"";
}
void ProtoConverter::visit(XmlDeclaration const& _x)
{
m_output << R"(<?xml version=)";
visit(_x.ver());
visit(_x.enc());
switch (_x.standalone())
{
case XmlDeclaration::YES:
m_output << " standalone=\'yes\'";
break;
case XmlDeclaration::NO:
m_output << " standalone=\'no\'";
break;
case XmlDeclaration_Standalone_XmlDeclaration_Standalone_INT_MIN_SENTINEL_DO_NOT_USE_:
case XmlDeclaration_Standalone_XmlDeclaration_Standalone_INT_MAX_SENTINEL_DO_NOT_USE_:
default:
break;
}
m_output << "?>\n";
}
void ProtoConverter::visit(XmlDocument const& _x)
{
visit(_x.p());
for (auto const& element: _x.e())
visit(element);
}
string ProtoConverter::protoToString(XmlDocument const& _x)
{
visit(_x);
return m_output.str();
}