From 3f8c4c273649c857f5a607dcbb422729fce3a166 Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Mon, 19 Oct 2009 23:09:19 +0000 Subject: categorize all error messages and include object information if available git-svn-id: svn+q:///qpdf/trunk@829 71b93d88-0707-0410-a8cf-f5a4172ac649 --- libqpdf/QPDF.cc | 250 +++++++++++++++++++++++++++++++----------- libqpdf/QPDFExc.cc | 52 +++++++-- libqpdf/QPDFXRefEntry.cc | 9 +- libqpdf/QPDF_Stream.cc | 7 +- libqpdf/QPDF_encryption.cc | 99 +++++++++++------ libqpdf/QPDF_linearization.cc | 34 ++++-- libqpdf/QPDF_optimization.cc | 14 ++- 7 files changed, 344 insertions(+), 121 deletions(-) (limited to 'libqpdf') diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index c7ec873f..ebd78a9d 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -140,7 +140,9 @@ QPDF::FileInputSource::read(char* buffer, int length) size_t len = fread(buffer, 1, length, this->file); if ((len == 0) && ferror(this->file)) { - throw QPDFExc(this->filename, this->last_offset, + throw QPDFExc(qpdf_e_system, + this->filename, "", + this->last_offset, std::string("read ") + QUtil::int_to_string(length) + " bytes"); } @@ -325,7 +327,8 @@ QPDF::parse() else { QTC::TC("qpdf", "QPDF not a pdf file"); - throw QPDFExc(this->file.getName(), 0, "not a PDF file"); + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "", 0, "not a PDF file"); } // PDF spec says %%EOF must be found within the last 1024 bytes of @@ -369,7 +372,8 @@ QPDF::parse() if (! m2) { QTC::TC("qpdf", "QPDF can't find startxref"); - throw QPDFExc(this->file.getName() + ": can't find startxref"); + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), "", 0, + "can't find startxref"); } off_t xref_offset = atoi(m2.getMatch(1).c_str()); read_xref(xref_offset); @@ -417,9 +421,28 @@ QPDF::reconstruct_xref(QPDFExc& e) static PCRE endobj_re("^endobj\\b"); static PCRE trailer_re("^trailer\\b"); - warn(QPDFExc(this->file.getName(), 0, "file is damaged")); + warn(QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), "", 0, + "file is damaged")); warn(e); - warn(QPDFExc("Attempting to reconstruct cross-reference table")); + warn(QPDFExc(qpdf_e_damaged_pdf, "", "", 0, + "Attempting to reconstruct cross-reference table")); + + // Delete all references to type 1 (uncompressed) objects + std::set to_delete; + for (std::map::iterator iter = + this->xref_table.begin(); + iter != this->xref_table.end(); ++iter) + { + if (((*iter).second).getType() == 1) + { + to_delete.insert((*iter).first); + } + } + for (std::set::iterator iter = to_delete.begin(); + iter != to_delete.end(); ++iter) + { + this->xref_table.erase(*iter); + } this->file.seek(0, SEEK_END); off_t eof = this->file.tell(); @@ -452,7 +475,8 @@ QPDF::reconstruct_xref(QPDFExc& e) // read "trailer" this->file.seek(this->file.getLastOffset(), SEEK_SET); readToken(&this->file); - QPDFObjectHandle t = readObject(&this->file, 0, 0, false); + QPDFObjectHandle t = + readObject(&this->file, "trailer", 0, 0, false); if (! t.isDictionary()) { // Oh well. It was worth a try. @@ -473,7 +497,8 @@ QPDF::reconstruct_xref(QPDFExc& e) // with bad startxref pointers even when they have object // streams. - throw QPDFExc(this->file.getName() + ": unable to find trailer " + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), "", 0, + "unable to find trailer " "dictionary while recovering damaged file"); } @@ -513,8 +538,8 @@ QPDF::read_xref(off_t xref_offset) if (size != max_obj + 1) { QTC::TC("qpdf", "QPDF xref size mismatch"); - warn(QPDFExc(this->file.getName() + - std::string(": reported number of objects (") + + warn(QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), "", 0, + std::string("reported number of objects (") + QUtil::int_to_string(size) + ") inconsistent with actual number of objects (" + QUtil::int_to_string(max_obj + 1) + ")")); @@ -542,7 +567,8 @@ QPDF::read_xrefTable(off_t xref_offset) if (! m1) { QTC::TC("qpdf", "QPDF invalid xref"); - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "xref table", this->file.getLastOffset(), "xref syntax invalid"); } int obj = atoi(m1.getMatch(1).c_str()); @@ -563,7 +589,8 @@ QPDF::read_xrefTable(off_t xref_offset) { QTC::TC("qpdf", "QPDF invalid xref entry"); throw QPDFExc( - this->file.getName(), this->file.getLastOffset(), + qpdf_e_damaged_pdf, this->file.getName(), + "xref table", this->file.getLastOffset(), "invalid xref entry (obj=" + QUtil::int_to_string(i) + ")"); } @@ -595,11 +622,13 @@ QPDF::read_xrefTable(off_t xref_offset) } // Set offset to previous xref table if any - QPDFObjectHandle cur_trailer = readObject(&this->file, 0, 0, false); + QPDFObjectHandle cur_trailer = + readObject(&this->file, "trailer", 0, 0, false); if (! cur_trailer.isDictionary()) { QTC::TC("qpdf", "QPDF missing trailer"); - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "", this->file.getLastOffset(), "expected trailer dictionary"); } @@ -610,13 +639,15 @@ QPDF::read_xrefTable(off_t xref_offset) if (! this->trailer.hasKey("/Size")) { QTC::TC("qpdf", "QPDF trailer lacks size"); - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "trailer", this->file.getLastOffset(), "trailer dictionary lacks /Size key"); } if (! this->trailer.getKey("/Size").isInteger()) { QTC::TC("qpdf", "QPDF trailer size not integer"); - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "trailer", this->file.getLastOffset(), "/Size key in trailer dictionary is not " "an integer"); } @@ -640,7 +671,8 @@ QPDF::read_xrefTable(off_t xref_offset) } else { - throw QPDFExc(this->file.getName(), xref_offset, + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "xref stream", xref_offset, "invalid /XRefStm"); } } @@ -659,7 +691,8 @@ QPDF::read_xrefTable(off_t xref_offset) if (! cur_trailer.getKey("/Prev").isInteger()) { QTC::TC("qpdf", "QPDF trailer prev not integer"); - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "trailer", this->file.getLastOffset(), "/Prev key in trailer dictionary is not " "an integer"); } @@ -685,7 +718,8 @@ QPDF::read_xrefStream(off_t xref_offset) QPDFObjectHandle xref_obj; try { - xref_obj = readObjectAtOffset(xref_offset, 0, 0, xobj, xgen); + xref_obj = readObjectAtOffset( + false, xref_offset, "xref stream", 0, 0, xobj, xgen); } catch (QPDFExc& e) { @@ -705,7 +739,8 @@ QPDF::read_xrefStream(off_t xref_offset) if (! found) { QTC::TC("qpdf", "QPDF can't find xref"); - throw QPDFExc(this->file.getName(), xref_offset, "xref not found"); + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "", xref_offset, "xref not found"); } return xref_offset; @@ -725,7 +760,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle& xref_obj) dict.getKey("/Size").isInteger() && (Index_obj.isArray() || Index_obj.isNull()))) { - throw QPDFExc(this->file.getName(), xref_offset, + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "xref stream", xref_offset, "Cross-reference stream does not have" " proper /W and /Index keys"); } @@ -735,7 +771,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle& xref_obj) int n_index = Index_obj.getArrayNItems(); if ((n_index % 2) || (n_index < 2)) { - throw QPDFExc(this->file.getName(), xref_offset, + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "xref stream", xref_offset, "Cross-reference stream's /Index has an" " invalid number of values"); } @@ -747,7 +784,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle& xref_obj) } else { - throw QPDFExc(this->file.getName(), xref_offset, + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "xref stream", xref_offset, "Cross-reference stream's /Index's item " + QUtil::int_to_string(i) + " is not an integer"); @@ -785,7 +823,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle& xref_obj) if (expected_size != actual_size) { - QPDFExc x(this->file.getName(), xref_offset, + QPDFExc x(qpdf_e_damaged_pdf, this->file.getName(), + "xref stream", xref_offset, "Cross-reference stream data has the wrong size;" " expected = " + QUtil::int_to_string(expected_size) + "; actual = " + QUtil::int_to_string(actual_size)); @@ -866,7 +905,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle& xref_obj) { if (! dict.getKey("/Prev").isInteger()) { - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "xref stream", this->file.getLastOffset(), "/Prev key in xref stream dictionary is not " "an integer"); } @@ -935,7 +975,8 @@ QPDF::insertXrefEntry(int obj, int f0, int f1, int f2, bool overwrite) break; default: - throw QPDFExc(this->file.getName(), 0, + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "xref stream", this->file.getLastOffset(), "unknown xref stream entry type " + QUtil::int_to_string(f0)); break; @@ -972,10 +1013,32 @@ QPDF::showXRefTable() } } +void +QPDF::setLastObjectDescription(std::string const& description, + int objid, int generation) +{ + this->last_object_description.clear(); + if (! description.empty()) + { + this->last_object_description += description; + if (objid > 0) + { + this->last_object_description += ": "; + } + } + if (objid > 0) + { + this->last_object_description += "object " + + QUtil::int_to_string(objid) + " " + + QUtil::int_to_string(generation); + } +} + QPDFObjectHandle -QPDF::readObject(InputSource* input, int objid, int generation, - bool in_object_stream) +QPDF::readObject(InputSource* input, std::string const& description, + int objid, int generation, bool in_object_stream) { + setLastObjectDescription(description, objid, generation); off_t offset = input->tell(); QPDFObjectHandle object = readObjectInternal( input, objid, generation, in_object_stream, false, false); @@ -1017,7 +1080,9 @@ QPDF::readObjectInternal(InputSource* input, case QPDFTokenizer::tt_brace_close: // Don't know what to do with these for now QTC::TC("qpdf", "QPDF bad brace"); - throw QPDFExc(input->getName(), input->getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), + this->last_object_description, + input->getLastOffset(), "unexpected brace token"); break; @@ -1029,7 +1094,9 @@ QPDF::readObjectInternal(InputSource* input, else { QTC::TC("qpdf", "QPDF bad array close"); - throw QPDFExc(input->getName(), input->getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), + this->last_object_description, + input->getLastOffset(), "unexpected array close token"); } break; @@ -1042,7 +1109,9 @@ QPDF::readObjectInternal(InputSource* input, else { QTC::TC("qpdf", "QPDF bad dictionary close"); - throw QPDFExc(input->getName(), input->getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), + this->last_object_description, + input->getLastOffset(), "unexpected dictionary close token"); } break; @@ -1097,7 +1166,9 @@ QPDF::readObjectInternal(InputSource* input, } else { - throw QPDFExc(input->getName(), input->getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), + this->last_object_description, + input->getLastOffset(), "unknown token while reading object (" + value + ")"); } @@ -1116,7 +1187,9 @@ QPDF::readObjectInternal(InputSource* input, break; default: - throw QPDFExc(input->getName(), input->getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), + this->last_object_description, + input->getLastOffset(), "unknown token type while reading object"); break; } @@ -1153,7 +1226,8 @@ QPDF::readObjectInternal(InputSource* input, { QTC::TC("qpdf", "QPDF dictionary odd number of elements"); throw QPDFExc( - input->getName(), input->getLastOffset(), + qpdf_e_damaged_pdf, input->getName(), + this->last_object_description, input->getLastOffset(), "dictionary ending here has an odd number of elements"); } for (unsigned int i = 0; i < olist.size(); i += 2) @@ -1163,7 +1237,8 @@ QPDF::readObjectInternal(InputSource* input, if (! key_obj.isName()) { throw QPDFExc( - input->getName(), offset, + qpdf_e_damaged_pdf, + input->getName(), this->last_object_description, offset, std::string("dictionary key not name (") + key_obj.unparse() + ")"); } @@ -1209,7 +1284,8 @@ QPDF::readObjectInternal(InputSource* input, if (dict.count("/Length") == 0) { QTC::TC("qpdf", "QPDF stream without length"); - throw QPDFExc(input->getName(), offset, + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), + this->last_object_description, offset, "stream dictionary lacks /Length key"); } @@ -1217,7 +1293,8 @@ QPDF::readObjectInternal(InputSource* input, if (! length_obj.isInteger()) { QTC::TC("qpdf", "QPDF stream length not integer"); - throw QPDFExc(input->getName(), offset, + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), + this->last_object_description, offset, "/Length key in stream dictionary is not " "an integer"); } @@ -1229,7 +1306,9 @@ QPDF::readObjectInternal(InputSource* input, QPDFTokenizer::tt_word, "endstream"))) { QTC::TC("qpdf", "QPDF missing endstream"); - throw QPDFExc(input->getName(), input->getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), + this->last_object_description, + input->getLastOffset(), "expected endstream"); } } @@ -1267,7 +1346,8 @@ QPDF::recoverStreamLength(InputSource* input, // Try to reconstruct stream length by looking for // endstream(\r\n?|\n)endobj - warn(QPDFExc(input->getName(), stream_offset, + warn(QPDFExc(qpdf_e_damaged_pdf, input->getName(), + this->last_object_description, stream_offset, "attempting to recover stream length")); input->seek(0, SEEK_END); @@ -1336,7 +1416,8 @@ QPDF::recoverStreamLength(InputSource* input, if (length == 0) { - throw QPDFExc(input->getName(), stream_offset, + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), + this->last_object_description, stream_offset, "unable to recover stream data"); } @@ -1356,7 +1437,9 @@ QPDF::readToken(InputSource* input) char ch; if (input->read(&ch, 1) == 0) { - throw QPDFExc(input->getName(), offset, "EOF while reading token"); + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), + this->last_object_description, offset, + "EOF while reading token"); } else { @@ -1376,7 +1459,9 @@ QPDF::readToken(InputSource* input) if (token.getType() == QPDFTokenizer::tt_bad) { - throw QPDFExc(input->getName(), offset, token.getErrorMessage()); + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), + this->last_object_description, offset, + token.getErrorMessage()); } input->setLastOffset(offset); @@ -1385,9 +1470,12 @@ QPDF::readToken(InputSource* input) } QPDFObjectHandle -QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation, +QPDF::readObjectAtOffset(bool try_recovery, + off_t offset, std::string const& description, + int exp_objid, int exp_generation, int& objid, int& generation) { + setLastObjectDescription(description, exp_objid, exp_generation); this->file.seek(offset, SEEK_SET); QPDFTokenizer::Token tobjid = readToken(&this->file); @@ -1407,7 +1495,9 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation, if (! (objidok && genok && objok)) { QTC::TC("qpdf", "QPDF expected n n obj"); - throw QPDFExc(this->file.getName(), offset, "expected n n obj"); + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + this->last_object_description, offset, + "expected n n obj"); } objid = atoi(tobjid.getValue().c_str()); generation = atoi(tgen.getValue().c_str()); @@ -1416,7 +1506,8 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation, (! ((objid == exp_objid) && (generation == exp_generation)))) { QTC::TC("qpdf", "QPDF err wrong objid/generation"); - throw QPDFExc(this->file.getName(), offset, + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + this->last_object_description, offset, std::string("expected ") + QUtil::int_to_string(exp_objid) + " " + QUtil::int_to_string(exp_generation) + " obj"); @@ -1424,7 +1515,7 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation, } catch (QPDFExc& e) { - if (exp_objid && this->attempt_recovery) + if (exp_objid && try_recovery && this->attempt_recovery) { // Try again after reconstructing xref table reconstruct_xref(e); @@ -1433,13 +1524,27 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation, (this->xref_table[og].getType() == 1)) { off_t new_offset = this->xref_table[og].getOffset(); - // Call readObjectAtOffset with 0 for exp_objid to - // avoid an infinite loop. - QPDFObjectHandle result = - readObjectAtOffset(new_offset, 0, 0, objid, generation); + QPDFObjectHandle result = readObjectAtOffset( + false, new_offset, description, + exp_objid, exp_generation, objid, generation); QTC::TC("qpdf", "QPDF recovered in readObjectAtOffset"); return result; } + else + { + QTC::TC("qpdf", "QPDF object gone after xref reconstruction"); + warn(QPDFExc( + qpdf_e_damaged_pdf, this->file.getName(), + "", 0, + std::string( + "object " + + QUtil::int_to_string(exp_objid) + + " " + + QUtil::int_to_string(exp_generation) + + " not found in file after regenerating" + " cross reference table"))); + return QPDFObjectHandle::newNull(); + } } else { @@ -1448,13 +1553,14 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation, } QPDFObjectHandle oh = readObject( - &this->file, objid, generation, false); + &this->file, description, objid, generation, false); if (! (readToken(&this->file) == QPDFTokenizer::Token(QPDFTokenizer::tt_word, "endobj"))) { QTC::TC("qpdf", "QPDF err expected endobj"); - warn(QPDFExc(this->file.getName(), this->file.getLastOffset(), + warn(QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + this->last_object_description, this->file.getLastOffset(), "expected endobj")); } @@ -1487,7 +1593,8 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation, } else { - throw QPDFExc(this->file.getName(), offset, + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + this->last_object_description, offset, "EOF after endobj"); } } @@ -1526,7 +1633,7 @@ QPDF::resolve(int objid, int generation) int aobjid; int ageneration; QPDFObjectHandle oh = - readObjectAtOffset(offset, objid, generation, + readObjectAtOffset(true, offset, "", objid, generation, aobjid, ageneration); } break; @@ -1536,7 +1643,7 @@ QPDF::resolve(int objid, int generation) break; default: - throw QPDFExc(this->file.getName(), 0, + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), "", 0, "object " + QUtil::int_to_string(objid) + "/" + QUtil::int_to_string(generation) + @@ -1554,7 +1661,9 @@ QPDF::resolveObjectsInStream(int obj_stream_number) QPDFObjectHandle obj_stream = getObjectByID(obj_stream_number, 0); if (! obj_stream.isStream()) { - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + this->last_object_description, + this->file.getLastOffset(), "supposed object stream " + QUtil::int_to_string(obj_stream_number) + " is not a stream"); @@ -1570,7 +1679,10 @@ QPDF::resolveObjectsInStream(int obj_stream_number) if (! (dict.getKey("/Type").isName() && dict.getKey("/Type").getName() == "/ObjStm")) { - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + QTC::TC("qpdf", "QPDF ERR object stream with wrong type"); + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + this->last_object_description, + this->file.getLastOffset(), "supposed object stream " + QUtil::int_to_string(obj_stream_number) + " has wrong type"); @@ -1579,7 +1691,9 @@ QPDF::resolveObjectsInStream(int obj_stream_number) if (! (dict.getKey("/N").isInteger() && dict.getKey("/First").isInteger())) { - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + this->last_object_description, + this->file.getLastOffset(), "object stream " + QUtil::int_to_string(obj_stream_number) + " has incorrect keys"); @@ -1602,7 +1716,8 @@ QPDF::resolveObjectsInStream(int obj_stream_number) if (! ((tnum.getType() == QPDFTokenizer::tt_integer) && (toffset.getType() == QPDFTokenizer::tt_integer))) { - throw QPDFExc(input.getName(), input.getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, input.getName(), + this->last_object_description, input.getLastOffset(), "expected integer in object stream header"); } @@ -1617,7 +1732,7 @@ QPDF::resolveObjectsInStream(int obj_stream_number) int obj = (*iter).first; int offset = (*iter).second; input.seek(offset, SEEK_SET); - QPDFObjectHandle oh = readObject(&input, obj, 0, true); + QPDFObjectHandle oh = readObject(&input, "", obj, 0, true); // Store in cache ObjGen og(obj, 0); @@ -1830,17 +1945,25 @@ QPDF::pipeStreamData(int objid, int generation, size_t len = this->file.read(buf, to_read); if (len == 0) { - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, + this->file.getName(), + this->last_object_description, + this->file.getLastOffset(), "unexpected EOF reading stream data"); } length -= len; pipeline->write((unsigned char*)buf, len); } } + catch (QPDFExc& e) + { + warn(e); + } catch (std::runtime_error& e) { QTC::TC("qpdf", "QPDF decoding error warning"); - warn(QPDFExc(this->file.getName(), this->file.getLastOffset(), + warn(QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "", this->file.getLastOffset(), "error decoding stream data for object " + QUtil::int_to_string(objid) + " " + QUtil::int_to_string(generation) + ": " + e.what())); @@ -1896,6 +2019,9 @@ QPDF::getAllPagesInternal(QPDFObjectHandle cur_pages, } else { - throw QPDFExc(this->file.getName() + ": invalid Type in page tree"); + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + this->last_object_description, + this->file.getLastOffset(), + + ": invalid Type in page tree"); } } diff --git a/libqpdf/QPDFExc.cc b/libqpdf/QPDFExc.cc index 006a96ac..614990a9 100644 --- a/libqpdf/QPDFExc.cc +++ b/libqpdf/QPDFExc.cc @@ -1,18 +1,56 @@ #include #include -QPDFExc::QPDFExc(std::string const& message) : - std::runtime_error(message) +QPDFExc::QPDFExc(qpdf_error_code_e error_code, + std::string const& filename, + std::string const& object, + off_t offset, + std::string const& message) : + std::runtime_error(createWhat(filename, object, offset, message)), + error_code(error_code), + filename(filename), + object(object), + offset(offset), + message(message) { } -QPDFExc::QPDFExc(std::string const& filename, int offset, - std::string const& message) : - std::runtime_error(filename + ": offset " + QUtil::int_to_string(offset) + - ": " + message) +QPDFExc::~QPDFExc() throw () { } -QPDFExc::~QPDFExc() throw () +std::string +QPDFExc::createWhat(std::string const& filename, + std::string const& object, + off_t offset, + std::string const& message) { + std::string result; + if (! filename.empty()) + { + result += filename; + } + if (! (object.empty() && offset == 0)) + { + result += " ("; + if (! object.empty()) + { + result += object; + if (offset > 0) + { + result += ", "; + } + } + if (offset > 0) + { + result += "file position " + QUtil::int_to_string(offset); + } + result += ")"; + } + if (! result.empty()) + { + result += ": "; + } + result += message; + return result; } diff --git a/libqpdf/QPDFXRefEntry.cc b/libqpdf/QPDFXRefEntry.cc index b6d48d97..9a08c829 100644 --- a/libqpdf/QPDFXRefEntry.cc +++ b/libqpdf/QPDFXRefEntry.cc @@ -16,7 +16,8 @@ QPDFXRefEntry::QPDFXRefEntry(int type, int field1, int field2) : { if ((type < 1) || (type > 2)) { - throw QPDFExc("invalid xref type " + QUtil::int_to_string(type)); + throw std::logic_error( + "invalid xref type " + QUtil::int_to_string(type)); } } @@ -31,7 +32,7 @@ QPDFXRefEntry::getOffset() const { if (this->type != 1) { - throw QPDFExc( + throw std::logic_error( "getOffset called for xref entry of type != 1"); } return this->field1; @@ -42,7 +43,7 @@ QPDFXRefEntry::getObjStreamNumber() const { if (this->type != 2) { - throw QPDFExc( + throw std::logic_error( "getObjStreamNumber called for xref entry of type != 2"); } return this->field1; @@ -53,7 +54,7 @@ QPDFXRefEntry::getObjStreamIndex() const { if (this->type != 2) { - throw QPDFExc( + throw std::logic_error( "getObjStreamIndex called for xref entry of type != 2"); } return this->field2; diff --git a/libqpdf/QPDF_Stream.cc b/libqpdf/QPDF_Stream.cc index 6642a06e..d0dd2e5f 100644 --- a/libqpdf/QPDF_Stream.cc +++ b/libqpdf/QPDF_Stream.cc @@ -59,7 +59,7 @@ QPDF_Stream::getStreamData() Pl_Buffer buf("stream data buffer"); if (! pipeStreamData(&buf, true, false, false)) { - throw QPDFExc("getStreamData called on unfilterable stream"); + throw std::logic_error("getStreamData called on unfilterable stream"); } return buf.getBuffer(); } @@ -208,8 +208,9 @@ QPDF_Stream::filterable(std::vector& filters, if (! filters_okay) { QTC::TC("qpdf", "QPDF_Stream invalid filter"); - throw QPDFExc(qpdf->getFilename(), this->offset, - "invalid filter object type for this stream"); + throw QPDFExc(qpdf_e_damaged_pdf, qpdf->getFilename(), + "", this->offset, + "stream filter type is not name or array"); } // `filters' now contains a list of filters to be applied in diff --git a/libqpdf/QPDF_encryption.cc b/libqpdf/QPDF_encryption.cc index 8962624d..d6b0ae23 100644 --- a/libqpdf/QPDF_encryption.cc +++ b/libqpdf/QPDF_encryption.cc @@ -337,14 +337,16 @@ QPDF::initializeEncryption() (id_obj.getArrayNItems() == 2) && id_obj.getArrayItem(0).isString())) { - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "trailer", this->file.getLastOffset(), "invalid /ID in trailer dictionary"); } std::string id1 = id_obj.getArrayItem(0).getStringValue(); if (id1.length() != id_bytes) { - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "trailer", this->file.getLastOffset(), "first /ID string in trailer dictionary has " "incorrect length"); } @@ -352,19 +354,23 @@ QPDF::initializeEncryption() QPDFObjectHandle encryption_dict = this->trailer.getKey("/Encrypt"); if (! encryption_dict.isDictionary()) { - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + this->last_object_description, + this->file.getLastOffset(), "/Encrypt in trailer dictionary is not a dictionary"); } if (! (encryption_dict.getKey("/Filter").isName() && (encryption_dict.getKey("/Filter").getName() == "/Standard"))) { - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "encryption dictionary", this->file.getLastOffset(), "unsupported encryption filter"); } if (! encryption_dict.getKey("/SubFilter").isNull()) { - warn(QPDFExc(this->file.getName(), this->file.getLastOffset(), + warn(QPDFExc(qpdf_e_unsupported, this->file.getName(), + "encryption dictionary", this->file.getLastOffset(), "file uses encryption SubFilters," " which qpdf does not support")); } @@ -375,7 +381,8 @@ QPDF::initializeEncryption() encryption_dict.getKey("/U").isString() && encryption_dict.getKey("/P").isInteger())) { - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "encryption dictionary", this->file.getLastOffset(), "some encryption dictionary parameters are missing " "or the wrong type"); } @@ -389,7 +396,8 @@ QPDF::initializeEncryption() if (! (((R == 2) || (R == 3) || (R == 4)) && ((V == 1) || (V == 2) || (V == 4)))) { - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_unsupported, this->file.getName(), + "encryption dictionary", this->file.getLastOffset(), "Unsupported /R or /V in encryption dictionary"); } @@ -397,7 +405,8 @@ QPDF::initializeEncryption() if (! ((O.length() == key_bytes) && (U.length() == key_bytes))) { - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "encryption dictionary", this->file.getLastOffset(), "incorrect length for /O and/or /P in " "encryption dictionary"); } @@ -408,7 +417,8 @@ QPDF::initializeEncryption() Length = encryption_dict.getKey("/Length").getIntValue(); if ((Length % 8) || (Length < 40) || (Length > 128)) { - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "encryption dictionary", this->file.getLastOffset(), "invalid /Length value in encryption dictionary"); } } @@ -471,7 +481,8 @@ QPDF::initializeEncryption() } if (this->cf_file != this->cf_stream) { - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), + throw QPDFExc(qpdf_e_unsupported, this->file.getName(), + "encryption dictionary", this->file.getLastOffset(), "This document has embedded files that are" " encrypted differently from the rest of the file." " qpdf does not presently support this due to" @@ -492,7 +503,8 @@ QPDF::initializeEncryption() } else { - throw QPDFExc(this->file.getName() + ": invalid password"); + throw QPDFExc(qpdf_e_password, this->file.getName(), + "", 0, "invalid password"); } this->encryption_key = compute_encryption_key(this->user_password, data); @@ -542,7 +554,9 @@ QPDF::decryptString(std::string& str, int objid, int generation) break; default: - warn(QPDFExc(this->file.getName(), this->file.getLastOffset(), + warn(QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + this->last_object_description, + this->file.getLastOffset(), "unknown encryption filter for strings" " (check /StrF in /Encrypt dictionary);" " strings may be decrypted improperly")); @@ -554,28 +568,47 @@ QPDF::decryptString(std::string& str, int objid, int generation) } std::string key = getKeyForObject(objid, generation, use_aes); - if (use_aes) + try { - QTC::TC("qpdf", "QPDF_encryption aes decode string"); - assert(key.length() == Pl_AES_PDF::key_size); - Pl_Buffer bufpl("decrypted string"); - Pl_AES_PDF pl("aes decrypt string", &bufpl, false, - (unsigned char const*)key.c_str()); - pl.write((unsigned char*)str.c_str(), str.length()); - pl.finish(); - Buffer* buf = bufpl.getBuffer(); - str = std::string((char*)buf->getBuffer(), (size_t)buf->getSize()); - delete buf; + if (use_aes) + { + QTC::TC("qpdf", "QPDF_encryption aes decode string"); + assert(key.length() == Pl_AES_PDF::key_size); + Pl_Buffer bufpl("decrypted string"); + Pl_AES_PDF pl("aes decrypt string", &bufpl, false, + (unsigned char const*)key.c_str()); + pl.write((unsigned char*)str.c_str(), str.length()); + pl.finish(); + PointerHolder buf = bufpl.getBuffer(); + str = std::string((char*)buf.getPointer()->getBuffer(), + (size_t)buf.getPointer()->getSize()); + } + else + { + QTC::TC("qpdf", "QPDF_encryption rc4 decode string"); + unsigned int vlen = str.length(); + // Using PointerHolder will cause a new char[] to be deleted + // with delete instead of delete [], but it's okay since the + // array is of a fundamental type, so there is no destructor + // to be called. Using PointerHolder guarantees that tmp will + // be freed even if rc4.process throws an exception. + PointerHolder tmp = QUtil::copy_string(str); + RC4 rc4((unsigned char const*)key.c_str(), key.length()); + rc4.process((unsigned char*)tmp.getPointer(), vlen); + str = std::string(tmp.getPointer(), vlen); + } } - else + catch (QPDFExc& e) + { + throw; + } + catch (std::runtime_error& e) { - QTC::TC("qpdf", "QPDF_encryption rc4 decode string"); - unsigned int vlen = str.length(); - char* tmp = QUtil::copy_string(str); - RC4 rc4((unsigned char const*)key.c_str(), key.length()); - rc4.process((unsigned char*)tmp, vlen); - str = std::string(tmp, vlen); - delete [] tmp; + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + this->last_object_description, this->file.getLastOffset(), + "error decrypting string for object " + + QUtil::int_to_string(objid) + " " + + QUtil::int_to_string(generation) + ": " + e.what()); } } @@ -645,7 +678,9 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation, default: // filter local to this stream. - warn(QPDFExc(this->file.getName(), this->file.getLastOffset(), + warn(QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + this->last_object_description, + this->file.getLastOffset(), "unknown encryption filter for streams" " (check " + method_source + ");" " streams may be decrypted improperly")); diff --git a/libqpdf/QPDF_linearization.cc b/libqpdf/QPDF_linearization.cc index 5bb4190e..51b74855 100644 --- a/libqpdf/QPDF_linearization.cc +++ b/libqpdf/QPDF_linearization.cc @@ -175,7 +175,8 @@ QPDF::readLinearizationData() if (! isLinearized()) { - throw QPDFExc(this->file.getName() + " is not linearized"); + throw std::logic_error("called readLinearizationData for file" + " that is not linearized"); } // /L is read and stored in linp by isLinearized() @@ -193,7 +194,10 @@ QPDF::readLinearizationData() T.isInteger() && (P.isInteger() || P.isNull()))) { - throw QPDFExc("some keys in linearization dictionary are of " + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "linearization dictionary", + this->file.getLastOffset(), + "some keys in linearization dictionary are of " "the wrong type"); } @@ -201,7 +205,10 @@ QPDF::readLinearizationData() unsigned int n_H_items = H.getArrayNItems(); if (! ((n_H_items == 2) || (n_H_items == 4))) { - throw QPDFExc("H has the wrong number of items"); + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "linearization dictionary", + this->file.getLastOffset(), + "H has the wrong number of items"); } std::vector H_items; @@ -214,7 +221,10 @@ QPDF::readLinearizationData() } else { - throw QPDFExc("some H items are of the wrong type"); + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "linearization dictionary", + this->file.getLastOffset(), + "some H items are of the wrong type"); } } @@ -301,13 +311,17 @@ QPDF::readHintStream(Pipeline& pl, off_t offset, size_t length) { int obj; int gen; - QPDFObjectHandle H = readObjectAtOffset(offset, 0, 0, obj, gen); + QPDFObjectHandle H = readObjectAtOffset( + false, offset, "linearization hint stream", 0, 0, obj, gen); ObjCache& oc = this->obj_cache[ObjGen(obj, gen)]; off_t min_end_offset = oc.end_before_space; off_t max_end_offset = oc.end_after_space; if (! H.isStream()) { - throw QPDFExc("hint table is not a stream"); + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "linearization dictionary", + this->file.getLastOffset(), + "hint table is not a stream"); } QPDFObjectHandle Hdict = H.getDict(); @@ -340,7 +354,10 @@ QPDF::readHintStream(Pipeline& pl, off_t offset, size_t length) std::cout << "expected = " << computed_end << "; actual = " << min_end_offset << ".." << max_end_offset << std::endl; - throw QPDFExc("hint table length mismatch"); + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + "linearization dictionary", + this->file.getLastOffset(), + "hint table length mismatch"); } H.pipeStreamData(&pl, true, false, false); return Hdict; @@ -651,8 +668,7 @@ QPDF::getLinearizationOffset(ObjGen const& og) break; default: - throw QPDFExc( - this->file.getName(), 0, + throw std::logic_error( "getLinearizationOffset called for xref entry not of type 1 or 2"); break; } diff --git a/libqpdf/QPDF_optimization.cc b/libqpdf/QPDF_optimization.cc index f3833efd..e8d20fd5 100644 --- a/libqpdf/QPDF_optimization.cc +++ b/libqpdf/QPDF_optimization.cc @@ -233,9 +233,12 @@ QPDF::optimizePagesTree( { if (! allow_changes) { - throw QPDFExc(this->file.getName() + - ": optimize detected an " - "inheritable resource"); + throw QPDFExc(qpdf_e_internal, this->file.getName(), + this->last_object_description, + this->file.getLastOffset(), + "optimize detected an " + "inheritable resource when called " + "in no-change mode"); } // This is an inheritable resource @@ -338,7 +341,10 @@ QPDF::optimizePagesTree( } else { - throw QPDFExc(this->file.getName() + ": invalid Type in page tree"); + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), + this->last_object_description, + this->file.getLastOffset(), + "invalid Type in page tree"); } } -- cgit v1.2.3-54-g00ecf