Skip to content

Commit

Permalink
Merge pull request #1482 from postscript-dev/fix_langAlt_read_func
Browse files Browse the repository at this point in the history
Fix langAltValue::read() parsing
  • Loading branch information
clanmills authored Mar 9, 2021
2 parents d8dd632 + 2917b83 commit 822eb1a
Show file tree
Hide file tree
Showing 6 changed files with 231 additions and 4 deletions.
1 change: 1 addition & 0 deletions include/exiv2/error.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ namespace Exiv2 {
kerInvalidXMP,
kerTiffDirectoryTooLarge,
kerInvalidTypeValue,
kerInvalidLangAltValue,
kerInvalidMalloc,
kerCorruptedMetadata,
kerArithmeticOverflow,
Expand Down
6 changes: 5 additions & 1 deletion man/man1/exiv2.1
Original file line number Diff line number Diff line change
Expand Up @@ -584,12 +584,16 @@ The format of XMP \fBLangAlt\fP values includes an optional language qualifier:
.B lang="\fIlanguage-code\fP\fI" text\fP
.fi
.sp
lang="x-default" is used if the value doesn't start with a language qualifier.
The double quotes around the \fIlanguage-code\fP are optional. If no languge qualifier
is supplied, then the value of "x-default" is used. More information
on the language format can be found at: https://www.ietf.org/rfc/rfc3066.txt
.sp 1
.nf
$ exiv2 -M'set Xmp.dc.title lang="de-DE" Euros' X.jpg
$ exiv2 -M'set Xmp.dc.title lang="en-GB" Pounds' X.jpg
$ exiv2 -M'set Xmp.dc.title lang="en-US" In God We Trust' X.jpg
$ exiv2 -M'set Xmp.dc.title lang=fr-FR Euros' X.jpg
$ exiv2 -M'set Xmp.dc.title lang=jp Yen' X.jpg
$ exiv2 -M'set Xmp.dc.title All others pay cash' X.jpg
.fi
.sp 1
Expand Down
2 changes: 2 additions & 0 deletions src/error.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ namespace {
N_("tiff directory length is too large") },
{ Exiv2::kerInvalidTypeValue,
N_("invalid type in tiff structure") },
{ Exiv2::kerInvalidLangAltValue,
N_("Invalid LangAlt value `%1'") }, // %1=value
{ Exiv2::kerInvalidMalloc,
N_("invalid memory allocation request") },
{ Exiv2::kerCorruptedMetadata,
Expand Down
26 changes: 23 additions & 3 deletions src/value.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -856,18 +856,38 @@ namespace Exiv2 {
}

int LangAltValue::read(const std::string& buf)
{
{
std::string b = buf;
std::string lang = "x-default";
if (buf.length() > 5 && buf.substr(0, 5) == "lang=") {
static const char* ALPHA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
static const char* ALPHA_NUM = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

std::string::size_type pos = buf.find_first_of(' ');
lang = buf.substr(5, pos-5);
// Strip quotes (so you can also specify the language without quotes)
if (lang[0] == '"') lang = lang.substr(1);
if (lang[lang.length()-1] == '"') lang = lang.substr(0, lang.length()-1);
if (lang[0] == '"') {
lang = lang.substr(1);

if (lang == "" || lang.find('"') != lang.length()-1)
throw Error(kerInvalidLangAltValue, buf);

lang = lang.substr(0, lang.length()-1);
}

if (lang == "") throw Error(kerInvalidLangAltValue, buf);

// Check language is in the correct format (see https://www.ietf.org/rfc/rfc3066.txt)
std::string::size_type charPos = lang.find_first_not_of(ALPHA);
if (charPos != std::string::npos) {
if (lang[charPos] != '-' || lang.find_first_not_of(ALPHA_NUM, charPos+1) != std::string::npos)
throw Error(kerInvalidLangAltValue, buf);
}

b.clear();
if (pos != std::string::npos) b = buf.substr(pos+1);
}

value_[lang] = b;
return 0;
}
Expand Down
1 change: 1 addition & 0 deletions unitTests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ add_executable(unit_tests
test_slice.cpp
test_tiffheader.cpp
test_types.cpp
test_LangAltValueRead.cpp
gtestwrapper.h
${unit_tests_exiv2lib_SOURCES}
)
Expand Down
199 changes: 199 additions & 0 deletions unitTests/test_LangAltValueRead.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
#include <exiv2/exiv2.hpp>

#include "gtestwrapper.h"

using namespace Exiv2;

// The tests corrispond to those in issue https://github.com/Exiv2/exiv2/issues/1481


// 1. No language value
TEST(LangAltValueReadTest, noLangugeValBeforeSpace)
{
XmpParser::initialize();
::atexit(XmpParser::terminate);

Exiv2::XmpData xmpData;
try {
xmpData["Xmp.dc.title"] = "lang= test1-1";
}
catch (AnyError& e) {
ASSERT_EQ(e.code(),Exiv2::kerInvalidLangAltValue);
}
catch (...) {
ASSERT_TRUE(false);
}
}

TEST(LangAltValueReadTest, quoteThenNoLangugeValBeforeSpace)
{
XmpParser::initialize();
::atexit(XmpParser::terminate);

Exiv2::XmpData xmpData;
try {
xmpData["Xmp.dc.title"] = "lang=\" test1-2";
}
catch (AnyError& e) {
ASSERT_EQ(e.code(),Exiv2::kerInvalidLangAltValue);
}
catch (...) {
ASSERT_TRUE(false);
}
}

// 2. Empty language value
TEST(LangAltValueReadTest, emptyDoubleQuotesLanguageValBeforeSpace)
{
XmpParser::initialize();
::atexit(XmpParser::terminate);

Exiv2::XmpData xmpData;
try {
xmpData["Xmp.dc.title"] = "lang=\"\" test2";
}
catch (AnyError& e) {
ASSERT_EQ(e.code(),Exiv2::kerInvalidLangAltValue);
}
catch (...) {
ASSERT_TRUE(false);
}
}

// 3. Mismatched and/or incorrect positioning of quotation marks
TEST(LangAltValueReadTest, emptyDoubleQuotesLanguageValNoSpace)
{
XmpParser::initialize();
::atexit(XmpParser::terminate);

Exiv2::XmpData xmpData;
try {
xmpData["Xmp.dc.title"] = "lang=\"\"test3-1";
}
catch (AnyError& e) {
ASSERT_EQ(e.code(),Exiv2::kerInvalidLangAltValue);
}
catch (...) {
ASSERT_TRUE(false);
}
}

TEST(LangAltValueReadTest, oneDoubleQuotesLanguageValNoSpace)
{
XmpParser::initialize();
::atexit(XmpParser::terminate);

Exiv2::XmpData xmpData;
try {
xmpData["Xmp.dc.title"] = "lang=\"test3-2";
}
catch (AnyError& e) {
ASSERT_EQ(e.code(),Exiv2::kerInvalidLangAltValue);
}
catch (...) {
ASSERT_TRUE(false);
}
}

TEST(LangAltValueReadTest, oneDoubleQuotesLanguageValBeforeSpace)
{
XmpParser::initialize();
::atexit(XmpParser::terminate);

Exiv2::XmpData xmpData;
try {
xmpData["Xmp.dc.title"] = "lang=\"en-UK test3-3";
}
catch (AnyError& e) {
ASSERT_EQ(e.code(),Exiv2::kerInvalidLangAltValue);
}
catch (...) {
ASSERT_TRUE(false);
}
}

TEST(LangAltValueReadTest, languageValOneDoubleQuotesBeforeSpace)
{
XmpParser::initialize();
::atexit(XmpParser::terminate);

Exiv2::XmpData xmpData;
try {
xmpData["Xmp.dc.title"] = "lang=en-US\" test3-4";
}
catch (AnyError& e) {
ASSERT_EQ(e.code(),Exiv2::kerInvalidLangAltValue);
}
catch (...) {
ASSERT_TRUE(false);
}
}

TEST(LangAltValueReadTest, languageValOneDoubleQuotesNoSpace)
{
XmpParser::initialize();
::atexit(XmpParser::terminate);

Exiv2::XmpData xmpData;
try {
xmpData["Xmp.dc.title"] = "lang=test3-5\"";
}
catch (AnyError& e) {
ASSERT_EQ(e.code(),Exiv2::kerInvalidLangAltValue);
}
catch (...) {
ASSERT_TRUE(false);
}
}

TEST(LangAltValueReadTest, languageValTwoDoubleQuotesNoSpace)
{
XmpParser::initialize();
::atexit(XmpParser::terminate);

Exiv2::XmpData xmpData;
try {
xmpData["Xmp.dc.title"] = "lang=test3-6\"\"";
}
catch (AnyError& e) {
ASSERT_EQ(e.code(),Exiv2::kerInvalidLangAltValue);
}
catch (...) {
ASSERT_TRUE(false);
}
}

// 4. Invalid characters in language part
TEST(LangAltValueReadTest, languageValExtraHyphenBeforeSpace)
{
XmpParser::initialize();
::atexit(XmpParser::terminate);

Exiv2::XmpData xmpData;
try {
xmpData["Xmp.dc.title"] = "lang=en-UK- test4-1";
}
catch (AnyError& e) {
ASSERT_EQ(e.code(),Exiv2::kerInvalidLangAltValue);
}
catch (...) {
ASSERT_TRUE(false);
}
}

TEST(LangAltValueReadTest, languageValWithInvalidCharBeforeSpace)
{
XmpParser::initialize();
::atexit(XmpParser::terminate);

Exiv2::XmpData xmpData;
try {
xmpData["Xmp.dc.title"] = "lang=en=UK test4-2";
}
catch (AnyError& e) {
ASSERT_EQ(e.code(),Exiv2::kerInvalidLangAltValue);
}
catch (...) {
ASSERT_TRUE(false);
}
}

0 comments on commit 822eb1a

Please sign in to comment.