From 8378b350dfc612673d1ba3597e9d5bb1347ae944 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Wed, 27 Feb 2013 14:59:37 +0100 Subject: [PATCH] Throw exception when an invalid value type is set in a header field. --- SConstruct | 1 + src/exception.cpp | 12 +++---- src/headerField.cpp | 11 +++++++ src/headerFieldFactory.cpp | 15 ++++++++- tests/parser/headerFieldTest.cpp | 56 ++++++++++++++++++++++++++++++++ vmime/exception.hpp | 6 ++-- vmime/headerField.hpp | 6 ++++ vmime/headerFieldFactory.hpp | 35 +++++++++++++++++--- 8 files changed, 128 insertions(+), 14 deletions(-) create mode 100644 tests/parser/headerFieldTest.cpp diff --git a/SConstruct b/SConstruct index 35ac452c..1db0d2e2 100644 --- a/SConstruct +++ b/SConstruct @@ -360,6 +360,7 @@ libvmimetest_sources = [ 'tests/parser/dispositionTest.cpp', 'tests/parser/emailAddressTest.cpp', 'tests/parser/headerTest.cpp', + 'tests/parser/headerFieldTest.cpp', 'tests/parser/htmlTextPartTest.cpp', 'tests/parser/mailboxTest.cpp', 'tests/parser/mediaTypeTest.cpp', diff --git a/src/exception.cpp b/src/exception.cpp index 85f1f4f3..a341c161 100644 --- a/src/exception.cpp +++ b/src/exception.cpp @@ -94,15 +94,15 @@ namespace exceptions // -// bad_field_type +// bad_field_value_type // -bad_field_type::~bad_field_type() throw() {} -bad_field_type::bad_field_type(const exception& other) - : exception("Bad field type.", other) {} +bad_field_value_type::~bad_field_value_type() throw() {} +bad_field_value_type::bad_field_value_type(const string& fieldName, const exception& other) + : exception("Bad value type for field '" + fieldName + "'.", other) {} -exception* bad_field_type::clone() const { return new bad_field_type(*this); } -const char* bad_field_type::name() const throw() { return "bad_field_type"; } +exception* bad_field_value_type::clone() const { return new bad_field_value_type(*this); } +const char* bad_field_value_type::name() const throw() { return "bad_field_value_type"; } diff --git a/src/headerField.cpp b/src/headerField.cpp index 0a17abac..59b10e76 100644 --- a/src/headerField.cpp +++ b/src/headerField.cpp @@ -26,6 +26,8 @@ #include "vmime/parserHelpers.hpp" +#include "vmime/exception.hpp" + namespace vmime { @@ -324,6 +326,9 @@ ref headerField::getValue() void headerField::setValue(ref value) { + if (!headerFieldFactory::getInstance()->isValueTypeValid(*this, *value)) + throw exceptions::bad_field_value_type(getName()); + if (value != NULL) m_value = value; } @@ -331,12 +336,18 @@ void headerField::setValue(ref value) void headerField::setValueConst(ref value) { + if (!headerFieldFactory::getInstance()->isValueTypeValid(*this, *value)) + throw exceptions::bad_field_value_type(getName()); + m_value = value->clone().dynamicCast (); } void headerField::setValue(const headerFieldValue& value) { + if (!headerFieldFactory::getInstance()->isValueTypeValid(*this, value)) + throw exceptions::bad_field_value_type(getName()); + m_value = value.clone().dynamicCast (); } diff --git a/src/headerFieldFactory.cpp b/src/headerFieldFactory.cpp index e1c725c0..7e021f59 100644 --- a/src/headerFieldFactory.cpp +++ b/src/headerFieldFactory.cpp @@ -126,7 +126,7 @@ ref headerFieldFactory::createValue(const string& fieldName) ref value = NULL; if (pos != m_valueMap.end()) - value = ((*pos).second)(); + value = ((*pos).second.allocFunc)(); else value = registerer ::creator(); @@ -134,5 +134,18 @@ ref headerFieldFactory::createValue(const string& fieldName) } +bool headerFieldFactory::isValueTypeValid + (const headerField& field, const headerFieldValue& value) const +{ + ValueMap::const_iterator pos = m_valueMap.find + (utility::stringUtils::toLower(field.getName())); + + if (pos != m_valueMap.end()) + return ((*pos).second.checkTypeFunc)(value); + + return true; // No info on this field +} + + } // vmime diff --git a/tests/parser/headerFieldTest.cpp b/tests/parser/headerFieldTest.cpp new file mode 100644 index 00000000..2c8a954c --- /dev/null +++ b/tests/parser/headerFieldTest.cpp @@ -0,0 +1,56 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2013 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "tests/testUtils.hpp" + + +#define VMIME_TEST_SUITE headerFieldTest +#define VMIME_TEST_SUITE_MODULE "Parser" + + +VMIME_TEST_SUITE_BEGIN + + VMIME_TEST_LIST_BEGIN + VMIME_TEST(testBadValueType) + VMIME_TEST_LIST_END + + + void testBadValueType() + { + vmime::headerFieldFactory *hfactory = vmime::headerFieldFactory::getInstance(); + + // "To" header field accepts values of type "addressList" + vmime::ref to = hfactory->create(vmime::fields::TO); + VASSERT_THROW("to", + to->setValue(vmime::mailbox("email@vmime.org")), + vmime::exceptions::bad_field_value_type); + + // Unregistered header field accepts any value type + vmime::ref custom = hfactory->create("X-MyCustomHeader"); + VASSERT_NO_THROW("custom/1", + custom->setValue(vmime::mailbox("email@vmime.org"))); + VASSERT_NO_THROW("custom/2", + custom->setValue(vmime::text("field value text"))); + } + +VMIME_TEST_SUITE_END diff --git a/vmime/exception.hpp b/vmime/exception.hpp index 355cd851..fc9efed6 100644 --- a/vmime/exception.hpp +++ b/vmime/exception.hpp @@ -98,12 +98,12 @@ namespace exceptions { -class bad_field_type : public vmime::exception +class bad_field_value_type : public vmime::exception { public: - bad_field_type(const exception& other = NO_EXCEPTION); - ~bad_field_type() throw(); + bad_field_value_type(const string& fieldName, const exception& other = NO_EXCEPTION); + ~bad_field_value_type() throw(); exception* clone() const; const char* name() const throw(); diff --git a/vmime/headerField.hpp b/vmime/headerField.hpp index 857ee051..72d639c9 100644 --- a/vmime/headerField.hpp +++ b/vmime/headerField.hpp @@ -94,12 +94,16 @@ public: /** Set the value of this field. * + * @throw exceptions::bad_field_value_type if the value type is not + * valid for this header field * @param value new value */ virtual void setValue(ref value); /** Set the value of this field by cloning the specified value. * + * @throw exceptions::bad_field_value_type if the value type is not + * valid for this header field * @param value new value */ virtual void setValueConst(ref value); @@ -107,6 +111,8 @@ public: /** Set the value of this field (reference version). * The value will be cloned. * + * @throw exceptions::bad_field_value_type if the value type is not + * valid for this header field * @param value new value */ virtual void setValue(const headerFieldValue& value); diff --git a/vmime/headerFieldFactory.hpp b/vmime/headerFieldFactory.hpp index 67e7418c..6132cee6 100644 --- a/vmime/headerFieldFactory.hpp +++ b/vmime/headerFieldFactory.hpp @@ -48,8 +48,17 @@ protected: NameMap m_nameMap; - typedef ref (*ValueAllocFunc)(void); - typedef std::map ValueMap; + + struct ValueInfo + { + typedef ref (*ValueAllocFunc)(void); + typedef bool (*ValueTypeCheckFunc)(const object&); + + ValueAllocFunc allocFunc; + ValueTypeCheckFunc checkTypeFunc; + }; + + typedef std::map ValueMap; ValueMap m_valueMap; @@ -64,6 +73,12 @@ public: { public: + static bool checkType(const object& obj) + { + const TYPE* typedObj = dynamic_cast (&obj); + return typedObj != NULL; + } + static ref creator() { // Allocate a new object @@ -94,9 +109,12 @@ public: template void registerFieldValue(const string& name) { + ValueInfo vi; + vi.allocFunc = ®isterer ::creator; + vi.checkTypeFunc = ®isterer ::checkType; + m_valueMap.insert(ValueMap::value_type - (utility::stringUtils::toLower(name), - ®isterer ::creator)); + (utility::stringUtils::toLower(name), vi)); } /** Create a new field object for the specified field name. @@ -116,6 +134,15 @@ public: * @return a new value object for the field */ ref createValue(const string& fieldName); + + /** Returns whether the specified value type is valid for the specified field. + * + * @param field header field + * @param value value for this header field + * @return true if the value type is compatible with the header field, or + * false otherwise + */ + bool isValueTypeValid(const headerField& field, const headerFieldValue& value) const; };