aboutsummaryrefslogtreecommitdiffstats
path: root/libqpdf/QPDFObjectHandle.cc
diff options
context:
space:
mode:
authorJay Berkenbilt <ejb@ql.org>2018-02-16 23:25:27 +0100
committerJay Berkenbilt <ejb@ql.org>2018-02-19 03:06:27 +0100
commitd0e99f195a987c483bbb6c5449cf39bee34e08a1 (patch)
treecead8acd60cd14fd5d904ed380c750540cb361f3 /libqpdf/QPDFObjectHandle.cc
parentc2e16827b69f3d3ac3721cfcd608b87f28e2a13f (diff)
downloadqpdf-d0e99f195a987c483bbb6c5449cf39bee34e08a1.tar.zst
More robust handling of type errors
Give objects descriptions and context so it is possible to issue warnings instead of fatal errors for attempts to access objects of the wrong type.
Diffstat (limited to 'libqpdf/QPDFObjectHandle.cc')
-rw-r--r--libqpdf/QPDFObjectHandle.cc484
1 files changed, 404 insertions, 80 deletions
diff --git a/libqpdf/QPDFObjectHandle.cc b/libqpdf/QPDFObjectHandle.cc
index d48461bf..2e9cc996 100644
--- a/libqpdf/QPDFObjectHandle.cc
+++ b/libqpdf/QPDFObjectHandle.cc
@@ -190,6 +190,18 @@ QPDFObjectHandle::releaseResolved()
}
}
+void
+QPDFObjectHandle::setObjectDescriptionFromInput(
+ QPDFObjectHandle object, QPDF* context,
+ std::string const& description, PointerHolder<InputSource> input,
+ qpdf_offset_t offset)
+{
+ object.setObjectDescription(
+ context,
+ input->getName() + ", " + description +
+ " at offset " + QUtil::int_to_string(offset));
+}
+
bool
QPDFObjectHandle::isInitialized() const
{
@@ -282,7 +294,8 @@ QPDFObjectHandle::getNumericValue()
}
else
{
- throw std::logic_error("getNumericValue called for non-numeric object");
+ typeWarning("number", "returning 0");
+ QTC::TC("qpdf", "QPDFObjectHandle numeric non-numeric");
}
return result;
}
@@ -363,8 +376,16 @@ QPDFObjectHandle::isScalar()
bool
QPDFObjectHandle::getBoolValue()
{
- assertBool();
- return dynamic_cast<QPDF_Bool*>(m->obj.getPointer())->getVal();
+ if (isBool())
+ {
+ return dynamic_cast<QPDF_Bool*>(m->obj.getPointer())->getVal();
+ }
+ else
+ {
+ typeWarning("boolean", "returning false");
+ QTC::TC("qpdf", "QPDFObjectHandle boolean returning false");
+ return false;
+ }
}
// Integer accessors
@@ -372,8 +393,16 @@ QPDFObjectHandle::getBoolValue()
long long
QPDFObjectHandle::getIntValue()
{
- assertInteger();
- return dynamic_cast<QPDF_Integer*>(m->obj.getPointer())->getVal();
+ if (isInteger())
+ {
+ return dynamic_cast<QPDF_Integer*>(m->obj.getPointer())->getVal();
+ }
+ else
+ {
+ typeWarning("integer", "returning 0");
+ QTC::TC("qpdf", "QPDFObjectHandle integer returning 0");
+ return 0;
+ }
}
// Real accessors
@@ -381,8 +410,16 @@ QPDFObjectHandle::getIntValue()
std::string
QPDFObjectHandle::getRealValue()
{
- assertReal();
- return dynamic_cast<QPDF_Real*>(m->obj.getPointer())->getVal();
+ if (isReal())
+ {
+ return dynamic_cast<QPDF_Real*>(m->obj.getPointer())->getVal();
+ }
+ else
+ {
+ typeWarning("real", "returning 0.0");
+ QTC::TC("qpdf", "QPDFObjectHandle real returning 0.0");
+ return "0.0";
+ }
}
// Name accessors
@@ -390,8 +427,16 @@ QPDFObjectHandle::getRealValue()
std::string
QPDFObjectHandle::getName()
{
- assertName();
- return dynamic_cast<QPDF_Name*>(m->obj.getPointer())->getName();
+ if (isName())
+ {
+ return dynamic_cast<QPDF_Name*>(m->obj.getPointer())->getName();
+ }
+ else
+ {
+ typeWarning("name", "returning dummy name");
+ QTC::TC("qpdf", "QPDFObjectHandle name returning dummy name");
+ return "/QPDFFakeName";
+ }
}
// String accessors
@@ -399,15 +444,31 @@ QPDFObjectHandle::getName()
std::string
QPDFObjectHandle::getStringValue()
{
- assertString();
- return dynamic_cast<QPDF_String*>(m->obj.getPointer())->getVal();
+ if (isString())
+ {
+ return dynamic_cast<QPDF_String*>(m->obj.getPointer())->getVal();
+ }
+ else
+ {
+ typeWarning("string", "returning empty string");
+ QTC::TC("qpdf", "QPDFObjectHandle string returning empty string");
+ return "";
+ }
}
std::string
QPDFObjectHandle::getUTF8Value()
{
- assertString();
- return dynamic_cast<QPDF_String*>(m->obj.getPointer())->getUTF8Val();
+ if (isString())
+ {
+ return dynamic_cast<QPDF_String*>(m->obj.getPointer())->getUTF8Val();
+ }
+ else
+ {
+ typeWarning("string", "returning empty string");
+ QTC::TC("qpdf", "QPDFObjectHandle string returning empty utf8");
+ return "";
+ }
}
// Operator and Inline Image accessors
@@ -415,15 +476,31 @@ QPDFObjectHandle::getUTF8Value()
std::string
QPDFObjectHandle::getOperatorValue()
{
- assertOperator();
- return dynamic_cast<QPDF_Operator*>(m->obj.getPointer())->getVal();
+ if (isOperator())
+ {
+ return dynamic_cast<QPDF_Operator*>(m->obj.getPointer())->getVal();
+ }
+ else
+ {
+ typeWarning("operator", "returning fake value");
+ QTC::TC("qpdf", "QPDFObjectHandle operator returning fake value");
+ return "QPDFFAKE";
+ }
}
std::string
QPDFObjectHandle::getInlineImageValue()
{
- assertInlineImage();
- return dynamic_cast<QPDF_InlineImage*>(m->obj.getPointer())->getVal();
+ if (isInlineImage())
+ {
+ return dynamic_cast<QPDF_InlineImage*>(m->obj.getPointer())->getVal();
+ }
+ else
+ {
+ typeWarning("inlineimage", "returning empty data");
+ QTC::TC("qpdf", "QPDFObjectHandle inlineimage returning empty data");
+ return "";
+ }
}
// Array accessors
@@ -431,22 +508,66 @@ QPDFObjectHandle::getInlineImageValue()
int
QPDFObjectHandle::getArrayNItems()
{
- assertArray();
- return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->getNItems();
+ if (isArray())
+ {
+ return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->getNItems();
+ }
+ else
+ {
+ typeWarning("array", "treating as empty");
+ QTC::TC("qpdf", "QPDFObjectHandle array treating as empty");
+ return 0;
+ }
}
QPDFObjectHandle
QPDFObjectHandle::getArrayItem(int n)
{
- assertArray();
- return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->getItem(n);
+ QPDFObjectHandle result;
+ if (isArray() && (n < getArrayNItems()) && (n >= 0))
+ {
+ result = dynamic_cast<QPDF_Array*>(m->obj.getPointer())->getItem(n);
+ }
+ else
+ {
+ result = newNull();
+ if (isArray())
+ {
+ objectWarning("returning null for out of bounds array access");
+ QTC::TC("qpdf", "QPDFObjectHandle array bounds");
+ }
+ else
+ {
+ typeWarning("array", "returning null");
+ QTC::TC("qpdf", "QPDFObjectHandle array null for non-array");
+ }
+ QPDF* context = 0;
+ std::string description;
+ if (this->m->obj->getDescription(context, description))
+ {
+ result.setObjectDescription(
+ context,
+ description +
+ " -> null returned from invalid array access");
+ }
+ }
+ return result;
}
std::vector<QPDFObjectHandle>
QPDFObjectHandle::getArrayAsVector()
{
- assertArray();
- return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->getAsVector();
+ std::vector<QPDFObjectHandle> result;
+ if (isArray())
+ {
+ result = dynamic_cast<QPDF_Array*>(m->obj.getPointer())->getAsVector();
+ }
+ else
+ {
+ typeWarning("array", "treating as empty");
+ QTC::TC("qpdf", "QPDFObjectHandle array treating as empty vector");
+ }
+ return result;
}
// Array mutators
@@ -454,36 +575,79 @@ QPDFObjectHandle::getArrayAsVector()
void
QPDFObjectHandle::setArrayItem(int n, QPDFObjectHandle const& item)
{
- assertArray();
- return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->setItem(n, item);
+ if (isArray())
+ {
+ dynamic_cast<QPDF_Array*>(m->obj.getPointer())->setItem(n, item);
+ }
+ else
+ {
+ typeWarning("array", "ignoring attempt to set item");
+ QTC::TC("qpdf", "QPDFObjectHandle array ignoring set item");
+ }
}
void
QPDFObjectHandle::setArrayFromVector(std::vector<QPDFObjectHandle> const& items)
{
- assertArray();
- return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->setFromVector(items);
+ if (isArray())
+ {
+ dynamic_cast<QPDF_Array*>(m->obj.getPointer())->setFromVector(items);
+ }
+ else
+ {
+ typeWarning("array", "ignoring attempt to replace items");
+ QTC::TC("qpdf", "QPDFObjectHandle array ignoring replace items");
+ }
}
void
QPDFObjectHandle::insertItem(int at, QPDFObjectHandle const& item)
{
- assertArray();
- return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->insertItem(at, item);
+ if (isArray())
+ {
+ dynamic_cast<QPDF_Array*>(m->obj.getPointer())->insertItem(at, item);
+ }
+ else
+ {
+ typeWarning("array", "ignoring attempt to insert item");
+ QTC::TC("qpdf", "QPDFObjectHandle array ignoring insert item");
+ }
}
void
QPDFObjectHandle::appendItem(QPDFObjectHandle const& item)
{
- assertArray();
- return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->appendItem(item);
+ if (isArray())
+ {
+ dynamic_cast<QPDF_Array*>(m->obj.getPointer())->appendItem(item);
+ }
+ else
+ {
+ typeWarning("array", "ignoring attempt to append item");
+ QTC::TC("qpdf", "QPDFObjectHandle array ignoring append item");
+ }
}
void
QPDFObjectHandle::eraseItem(int at)
{
- assertArray();
- return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->eraseItem(at);
+ if (isArray() && (at < getArrayNItems()) && (at >= 0))
+ {
+ dynamic_cast<QPDF_Array*>(m->obj.getPointer())->eraseItem(at);
+ }
+ else
+ {
+ if (isArray())
+ {
+ objectWarning("ignoring attempt to erase out of bounds array item");
+ QTC::TC("qpdf", "QPDFObjectHandle erase array bounds");
+ }
+ else
+ {
+ typeWarning("array", "ignoring attempt to erase item");
+ QTC::TC("qpdf", "QPDFObjectHandle array ignoring erase item");
+ }
+ }
}
// Dictionary accessors
@@ -491,29 +655,79 @@ QPDFObjectHandle::eraseItem(int at)
bool
QPDFObjectHandle::hasKey(std::string const& key)
{
- assertDictionary();
- return dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->hasKey(key);
+ if (isDictionary())
+ {
+ return dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->hasKey(key);
+ }
+ else
+ {
+ typeWarning("dictionary",
+ "returning false for a key containment request");
+ QTC::TC("qpdf", "QPDFObjectHandle dictionary false for hasKey");
+ return false;
+ }
}
QPDFObjectHandle
QPDFObjectHandle::getKey(std::string const& key)
{
- assertDictionary();
- return dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->getKey(key);
+ QPDFObjectHandle result;
+ if (isDictionary())
+ {
+ result = dynamic_cast<QPDF_Dictionary*>(
+ m->obj.getPointer())->getKey(key);
+ }
+ else
+ {
+ typeWarning(
+ "dictionary", "returning null for attempted key retrieval");
+ QTC::TC("qpdf", "QPDFObjectHandle dictionary null for getKey");
+ result = newNull();
+ QPDF* qpdf = 0;
+ std::string description;
+ if (this->m->obj->getDescription(qpdf, description))
+ {
+ result.setObjectDescription(
+ qpdf,
+ description +
+ " -> null returned from getting key " +
+ key + " from non-Dictionary");
+ }
+ }
+ return result;
}
std::set<std::string>
QPDFObjectHandle::getKeys()
{
- assertDictionary();
- return dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->getKeys();
+ std::set<std::string> result;
+ if (isDictionary())
+ {
+ result = dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->getKeys();
+ }
+ else
+ {
+ typeWarning("dictionary", "treating as empty");
+ QTC::TC("qpdf", "QPDFObjectHandle dictionary empty set for getKeys");
+ }
+ return result;
}
std::map<std::string, QPDFObjectHandle>
QPDFObjectHandle::getDictAsMap()
{
- assertDictionary();
- return dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->getAsMap();
+ std::map<std::string, QPDFObjectHandle> result;
+ if (isDictionary())
+ {
+ result = dynamic_cast<QPDF_Dictionary*>(
+ m->obj.getPointer())->getAsMap();
+ }
+ else
+ {
+ typeWarning("dictionary", "treating as empty");
+ QTC::TC("qpdf", "QPDFObjectHandle dictionary empty map for asMap");
+ }
+ return result;
}
// Array and Name accessors
@@ -551,27 +765,48 @@ QPDFObjectHandle::getOwningQPDF()
void
QPDFObjectHandle::replaceKey(std::string const& key,
- QPDFObjectHandle const& value)
+ QPDFObjectHandle value)
{
- assertDictionary();
- return dynamic_cast<QPDF_Dictionary*>(
- m->obj.getPointer())->replaceKey(key, value);
+ if (isDictionary())
+ {
+ dynamic_cast<QPDF_Dictionary*>(
+ m->obj.getPointer())->replaceKey(key, value);
+ }
+ else
+ {
+ typeWarning("dictionary", "ignoring key replacement request");
+ QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring replaceKey");
+ }
}
void
QPDFObjectHandle::removeKey(std::string const& key)
{
- assertDictionary();
- return dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->removeKey(key);
+ if (isDictionary())
+ {
+ dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->removeKey(key);
+ }
+ else
+ {
+ typeWarning("dictionary", "ignoring key removal request");
+ QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring removeKey");
+ }
}
void
QPDFObjectHandle::replaceOrRemoveKey(std::string const& key,
QPDFObjectHandle value)
{
- assertDictionary();
- return dynamic_cast<QPDF_Dictionary*>(
- m->obj.getPointer())->replaceOrRemoveKey(key, value);
+ if (isDictionary())
+ {
+ dynamic_cast<QPDF_Dictionary*>(
+ m->obj.getPointer())->replaceOrRemoveKey(key, value);
+ }
+ else
+ {
+ typeWarning("dictionary", "ignoring key removal/replacement request");
+ QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring removereplace");
+ }
}
// Stream accessors
@@ -1173,35 +1408,45 @@ QPDFObjectHandle::parseInternal(PointerHolder<InputSource> input,
std::vector<parser_state_e> state_stack;
state_stack.push_back(st_top);
std::vector<qpdf_offset_t> offset_stack;
- offset_stack.push_back(input->tell());
+ qpdf_offset_t offset = input->tell();
+ offset_stack.push_back(offset);
bool done = false;
while (! done)
{
std::vector<QPDFObjectHandle>& olist = olist_stack.back();
parser_state_e state = state_stack.back();
- qpdf_offset_t offset = offset_stack.back();
+ offset = offset_stack.back();
object = QPDFObjectHandle();
QPDFTokenizer::Token token =
- tokenizer.readToken(input, object_description);
+ tokenizer.readToken(input, object_description, true);
switch (token.getType())
{
case QPDFTokenizer::tt_eof:
- if (content_stream)
+ if (! content_stream)
{
- state = st_eof;
- }
- else
- {
- // When not in content stream mode, EOF is tt_bad and
- // throws an exception before we get here.
- throw std::logic_error(
- "EOF received while not in content stream mode");
+ QTC::TC("qpdf", "QPDFObjectHandle eof in parseInternal");
+ warn(context,
+ QPDFExc(qpdf_e_damaged_pdf, input->getName(),
+ object_description,
+ input->getLastOffset(),
+ "unexpected EOF"));
}
+ state = st_eof;
break;
+ case QPDFTokenizer::tt_bad:
+ QTC::TC("qpdf", "QPDFObjectHandle bad token in parse");
+ warn(context,
+ QPDFExc(qpdf_e_damaged_pdf, input->getName(),
+ object_description,
+ input->getLastOffset(),
+ token.getErrorMessage()));
+ object = newNull();
+ break;
+
case QPDFTokenizer::tt_brace_open:
case QPDFTokenizer::tt_brace_close:
QTC::TC("qpdf", "QPDFObjectHandle bad brace");
@@ -1375,11 +1620,19 @@ QPDFObjectHandle::parseInternal(PointerHolder<InputSource> input,
"parse error while reading object"));
}
done = true;
- // Leave object uninitialized to indicate EOF
+ // In content stream mode, leave object uninitialized to
+ // indicate EOF
+ if (! content_stream)
+ {
+ object = newNull();
+ }
break;
case st_dictionary:
case st_array:
+ setObjectDescriptionFromInput(
+ object, context, object_description, input,
+ input->getLastOffset());
olist.push_back(object);
break;
@@ -1402,6 +1655,8 @@ QPDFObjectHandle::parseInternal(PointerHolder<InputSource> input,
if (old_state == st_array)
{
object = newArray(olist);
+ setObjectDescriptionFromInput(
+ object, context, object_description, input, offset);
}
else if (old_state == st_dictionary)
{
@@ -1458,6 +1713,8 @@ QPDFObjectHandle::parseInternal(PointerHolder<InputSource> input,
"dictionary ended prematurely; "
"using null as value for last key"));
val = newNull();
+ setObjectDescriptionFromInput(
+ val, context, object_description, input, offset);
}
else
{
@@ -1466,6 +1723,8 @@ QPDFObjectHandle::parseInternal(PointerHolder<InputSource> input,
dict[key_obj.getName()] = val;
}
object = newDictionary(dict);
+ setObjectDescriptionFromInput(
+ object, context, object_description, input, offset);
}
olist_stack.pop_back();
offset_stack.pop_back();
@@ -1480,6 +1739,8 @@ QPDFObjectHandle::parseInternal(PointerHolder<InputSource> input,
}
}
+ setObjectDescriptionFromInput(
+ object, context, object_description, input, offset);
return object;
}
@@ -1635,6 +1896,26 @@ QPDFObjectHandle::newReserved(QPDF* qpdf)
return result;
}
+void
+QPDFObjectHandle::setObjectDescription(QPDF* owning_qpdf,
+ std::string const& object_description)
+{
+ if (isInitialized() && this->m->obj.getPointer())
+ {
+ this->m->obj->setDescription(owning_qpdf, object_description);
+ }
+}
+
+bool
+QPDFObjectHandle::hasObjectDescription()
+{
+ if (isInitialized() && this->m->obj.getPointer())
+ {
+ return this->m->obj->hasDescription();
+ }
+ return false;
+}
+
QPDFObjectHandle
QPDFObjectHandle::shallowCopy()
{
@@ -1793,85 +2074,127 @@ QPDFObjectHandle::assertInitialized() const
}
void
-QPDFObjectHandle::assertType(char const* type_name, bool istype) const
+QPDFObjectHandle::typeWarning(char const* expected_type,
+ std::string const& warning)
+{
+ QPDF* context = 0;
+ std::string description;
+ if (this->m->obj->getDescription(context, description))
+ {
+ warn(context,
+ QPDFExc(
+ qpdf_e_damaged_pdf,
+ "", description, 0,
+ std::string("operation for ") + expected_type +
+ " attempted on object of type " +
+ getTypeName() + ": " + warning));
+ }
+ else
+ {
+ assertType(expected_type, false);
+ }
+}
+
+void
+QPDFObjectHandle::objectWarning(std::string const& warning)
+{
+ QPDF* context = 0;
+ std::string description;
+ if (this->m->obj->getDescription(context, description))
+ {
+ warn(context,
+ QPDFExc(
+ qpdf_e_damaged_pdf,
+ "", description, 0,
+ warning));
+ }
+ else
+ {
+ throw std::logic_error(warning);
+ }
+}
+
+void
+QPDFObjectHandle::assertType(char const* type_name, bool istype)
{
if (! istype)
{
throw std::logic_error(std::string("operation for ") + type_name +
- " object attempted on object of wrong type");
+ " attempted on object of type " +
+ getTypeName());
}
}
void
QPDFObjectHandle::assertNull()
{
- assertType("Null", isNull());
+ assertType("null", isNull());
}
void
QPDFObjectHandle::assertBool()
{
- assertType("Boolean", isBool());
+ assertType("boolean", isBool());
}
void
QPDFObjectHandle::assertInteger()
{
- assertType("Integer", isInteger());
+ assertType("integer", isInteger());
}
void
QPDFObjectHandle::assertReal()
{
- assertType("Real", isReal());
+ assertType("real", isReal());
}
void
QPDFObjectHandle::assertName()
{
- assertType("Name", isName());
+ assertType("name", isName());
}
void
QPDFObjectHandle::assertString()
{
- assertType("String", isString());
+ assertType("string", isString());
}
void
QPDFObjectHandle::assertOperator()
{
- assertType("Operator", isOperator());
+ assertType("operator", isOperator());
}
void
QPDFObjectHandle::assertInlineImage()
{
- assertType("InlineImage", isInlineImage());
+ assertType("inlineimage", isInlineImage());
}
void
QPDFObjectHandle::assertArray()
{
- assertType("Array", isArray());
+ assertType("array", isArray());
}
void
QPDFObjectHandle::assertDictionary()
{
- assertType("Dictionary", isDictionary());
+ assertType("dictionary", isDictionary());
}
void
QPDFObjectHandle::assertStream()
{
- assertType("Stream", isStream());
+ assertType("stream", isStream());
}
void
QPDFObjectHandle::assertReserved()
{
- assertType("Reserved", isReserved());
+ assertType("reserved", isReserved());
}
void
@@ -1887,13 +2210,13 @@ QPDFObjectHandle::assertIndirect()
void
QPDFObjectHandle::assertScalar()
{
- assertType("Scalar", isScalar());
+ assertType("scalar", isScalar());
}
void
QPDFObjectHandle::assertNumber()
{
- assertType("Number", isNumber());
+ assertType("number", isNumber());
}
bool
@@ -1928,7 +2251,8 @@ QPDFObjectHandle::dereference()
this->m->qpdf, this->m->objid, this->m->generation);
if (obj.getPointer() == 0)
{
- QTC::TC("qpdf", "QPDFObjectHandle indirect to unknown");
+ // QPDF::resolve never returns an uninitialized object, but
+ // check just in case.
this->m->obj = new QPDF_Null();
}
else if (dynamic_cast<QPDF_Reserved*>(obj.getPointer()))