Throw exception when an invalid value type is set in a header field.

This commit is contained in:
Vincent Richard 2013-02-27 14:59:37 +01:00
parent d2c2f52a23
commit 8378b350df
8 changed files with 128 additions and 14 deletions

View File

@ -360,6 +360,7 @@ libvmimetest_sources = [
'tests/parser/dispositionTest.cpp', 'tests/parser/dispositionTest.cpp',
'tests/parser/emailAddressTest.cpp', 'tests/parser/emailAddressTest.cpp',
'tests/parser/headerTest.cpp', 'tests/parser/headerTest.cpp',
'tests/parser/headerFieldTest.cpp',
'tests/parser/htmlTextPartTest.cpp', 'tests/parser/htmlTextPartTest.cpp',
'tests/parser/mailboxTest.cpp', 'tests/parser/mailboxTest.cpp',
'tests/parser/mediaTypeTest.cpp', 'tests/parser/mediaTypeTest.cpp',

View File

@ -94,15 +94,15 @@ namespace exceptions
// //
// bad_field_type // bad_field_value_type
// //
bad_field_type::~bad_field_type() throw() {} bad_field_value_type::~bad_field_value_type() throw() {}
bad_field_type::bad_field_type(const exception& other) bad_field_value_type::bad_field_value_type(const string& fieldName, const exception& other)
: exception("Bad field type.", other) {} : exception("Bad value type for field '" + fieldName + "'.", other) {}
exception* bad_field_type::clone() const { return new bad_field_type(*this); } exception* bad_field_value_type::clone() const { return new bad_field_value_type(*this); }
const char* bad_field_type::name() const throw() { return "bad_field_type"; } const char* bad_field_value_type::name() const throw() { return "bad_field_value_type"; }

View File

@ -26,6 +26,8 @@
#include "vmime/parserHelpers.hpp" #include "vmime/parserHelpers.hpp"
#include "vmime/exception.hpp"
namespace vmime namespace vmime
{ {
@ -324,6 +326,9 @@ ref <headerFieldValue> headerField::getValue()
void headerField::setValue(ref <headerFieldValue> value) void headerField::setValue(ref <headerFieldValue> value)
{ {
if (!headerFieldFactory::getInstance()->isValueTypeValid(*this, *value))
throw exceptions::bad_field_value_type(getName());
if (value != NULL) if (value != NULL)
m_value = value; m_value = value;
} }
@ -331,12 +336,18 @@ void headerField::setValue(ref <headerFieldValue> value)
void headerField::setValueConst(ref <const headerFieldValue> value) void headerField::setValueConst(ref <const headerFieldValue> value)
{ {
if (!headerFieldFactory::getInstance()->isValueTypeValid(*this, *value))
throw exceptions::bad_field_value_type(getName());
m_value = value->clone().dynamicCast <headerFieldValue>(); m_value = value->clone().dynamicCast <headerFieldValue>();
} }
void headerField::setValue(const headerFieldValue& value) void headerField::setValue(const headerFieldValue& value)
{ {
if (!headerFieldFactory::getInstance()->isValueTypeValid(*this, value))
throw exceptions::bad_field_value_type(getName());
m_value = value.clone().dynamicCast <headerFieldValue>(); m_value = value.clone().dynamicCast <headerFieldValue>();
} }

View File

@ -126,7 +126,7 @@ ref <headerFieldValue> headerFieldFactory::createValue(const string& fieldName)
ref <headerFieldValue> value = NULL; ref <headerFieldValue> value = NULL;
if (pos != m_valueMap.end()) if (pos != m_valueMap.end())
value = ((*pos).second)(); value = ((*pos).second.allocFunc)();
else else
value = registerer <headerFieldValue, text>::creator(); value = registerer <headerFieldValue, text>::creator();
@ -134,5 +134,18 @@ ref <headerFieldValue> 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 } // vmime

View File

@ -0,0 +1,56 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2013 Vincent Richard <vincent@vmime.org>
//
// 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 <vmime::headerField> 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 <vmime::headerField> 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

View File

@ -98,12 +98,12 @@ namespace exceptions
{ {
class bad_field_type : public vmime::exception class bad_field_value_type : public vmime::exception
{ {
public: public:
bad_field_type(const exception& other = NO_EXCEPTION); bad_field_value_type(const string& fieldName, const exception& other = NO_EXCEPTION);
~bad_field_type() throw(); ~bad_field_value_type() throw();
exception* clone() const; exception* clone() const;
const char* name() const throw(); const char* name() const throw();

View File

@ -94,12 +94,16 @@ public:
/** Set the value of this field. /** 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 * @param value new value
*/ */
virtual void setValue(ref <headerFieldValue> value); virtual void setValue(ref <headerFieldValue> value);
/** Set the value of this field by cloning the specified 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 * @param value new value
*/ */
virtual void setValueConst(ref <const headerFieldValue> value); virtual void setValueConst(ref <const headerFieldValue> value);
@ -107,6 +111,8 @@ public:
/** Set the value of this field (reference version). /** Set the value of this field (reference version).
* The value will be cloned. * 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 * @param value new value
*/ */
virtual void setValue(const headerFieldValue& value); virtual void setValue(const headerFieldValue& value);

View File

@ -48,8 +48,17 @@ protected:
NameMap m_nameMap; NameMap m_nameMap;
typedef ref <headerFieldValue> (*ValueAllocFunc)(void);
typedef std::map <string, ValueAllocFunc> ValueMap; struct ValueInfo
{
typedef ref <headerFieldValue> (*ValueAllocFunc)(void);
typedef bool (*ValueTypeCheckFunc)(const object&);
ValueAllocFunc allocFunc;
ValueTypeCheckFunc checkTypeFunc;
};
typedef std::map <string, ValueInfo> ValueMap;
ValueMap m_valueMap; ValueMap m_valueMap;
@ -64,6 +73,12 @@ public:
{ {
public: public:
static bool checkType(const object& obj)
{
const TYPE* typedObj = dynamic_cast <const TYPE*>(&obj);
return typedObj != NULL;
}
static ref <BASE_TYPE> creator() static ref <BASE_TYPE> creator()
{ {
// Allocate a new object // Allocate a new object
@ -94,9 +109,12 @@ public:
template <class T> template <class T>
void registerFieldValue(const string& name) void registerFieldValue(const string& name)
{ {
ValueInfo vi;
vi.allocFunc = &registerer <headerFieldValue, T>::creator;
vi.checkTypeFunc = &registerer <headerField, T>::checkType;
m_valueMap.insert(ValueMap::value_type m_valueMap.insert(ValueMap::value_type
(utility::stringUtils::toLower(name), (utility::stringUtils::toLower(name), vi));
&registerer <headerFieldValue, T>::creator));
} }
/** Create a new field object for the specified field name. /** Create a new field object for the specified field name.
@ -116,6 +134,15 @@ public:
* @return a new value object for the field * @return a new value object for the field
*/ */
ref <headerFieldValue> createValue(const string& fieldName); ref <headerFieldValue> 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;
}; };