diff --git a/ChangeLog b/ChangeLog index 2433a76a..e2b11d6f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2022-01-22 Jay Berkenbilt + * Add QUtil::make_shared_cstr to return a std::shared_ptr + instead of a char* like QUtil::copy_string + * JSON: for (qpdf-specific, not official) "schema" checking, add the ability to treat missing fields as optional. Also ensure that values in the schema are dictionary, array, or string. diff --git a/include/qpdf/QUtil.hh b/include/qpdf/QUtil.hh index 5a064d5c..d23e3f85 100644 --- a/include/qpdf/QUtil.hh +++ b/include/qpdf/QUtil.hh @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -151,9 +152,15 @@ namespace QUtil QPDF_DLL std::string path_basename(std::string const& filename); + // Returns a dynamically allocated copy of a string that the + // caller has to delete with delete[]. QPDF_DLL char* copy_string(std::string const&); + // Returns a shared_ptr with the correct deleter. + QPDF_DLL + std::shared_ptr make_shared_cstr(std::string const&); + // Returns lower-case hex-encoded version of the string, treating // each character in the input string as unsigned. The output // string will be twice as long as the input string. diff --git a/libqpdf/QPDFWriter.cc b/libqpdf/QPDFWriter.cc index e7fb9ad6..c0ee68d1 100644 --- a/libqpdf/QPDFWriter.cc +++ b/libqpdf/QPDFWriter.cc @@ -1915,9 +1915,8 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level, } else { - PointerHolder tmp_ph = - PointerHolder(true, QUtil::copy_string(val)); - char* tmp = tmp_ph.getPointer(); + auto tmp_ph = QUtil::make_shared_cstr(val); + char* tmp = tmp_ph.get(); size_t vlen = val.length(); RC4 rc4(QUtil::unsigned_char_pointer(this->m->cur_data_key), QIntC::to_int(this->m->cur_data_key.length())); diff --git a/libqpdf/QPDF_encryption.cc b/libqpdf/QPDF_encryption.cc index 9607a598..d5289ad3 100644 --- a/libqpdf/QPDF_encryption.cc +++ b/libqpdf/QPDF_encryption.cc @@ -1211,10 +1211,10 @@ QPDF::decryptString(std::string& str, int objid, int generation) size_t vlen = str.length(); // Using PointerHolder guarantees that tmp will // be freed even if rc4.process throws an exception. - PointerHolder tmp(true, QUtil::copy_string(str)); + auto tmp = QUtil::make_shared_cstr(str); RC4 rc4(QUtil::unsigned_char_pointer(key), toI(key.length())); - rc4.process(QUtil::unsigned_char_pointer(tmp.getPointer()), vlen); - str = std::string(tmp.getPointer(), vlen); + rc4.process(QUtil::unsigned_char_pointer(tmp.get()), vlen); + str = std::string(tmp.get(), vlen); } } catch (QPDFExc&) diff --git a/libqpdf/QUtil.cc b/libqpdf/QUtil.cc index c71e7923..cfd1bb1e 100644 --- a/libqpdf/QUtil.cc +++ b/libqpdf/QUtil.cc @@ -731,6 +731,18 @@ QUtil::copy_string(std::string const& str) return result; } +std::shared_ptr +QUtil::make_shared_cstr(std::string const& str) +{ + auto result = std::shared_ptr( + new char[str.length() + 1], + std::default_delete()); + // Use memcpy in case string contains nulls + result.get()[str.length()] = '\0'; + memcpy(result.get(), str.c_str(), str.length()); + return result; +} + std::string QUtil::hex_encode(std::string const& input) { @@ -2625,7 +2637,7 @@ QUtil::call_main_from_wmain(int argc, wchar_t* argv[], QIntC::to_uchar(codepoint & 0xff))); } std::string utf8 = QUtil::utf16_to_utf8(utf16); - utf8_argv.push_back(std::shared_ptr(QUtil::copy_string(utf8.c_str()), std::default_delete())); + utf8_argv.push_back(QUtil::make_shared_cstr(utf8)); } auto utf8_argv_sp = std::shared_ptr(new char*[1+utf8_argv.size()], std::default_delete()); diff --git a/libtests/qtest/qutil/qutil.out b/libtests/qtest/qutil/qutil.out index bcb89def..58cc2334 100644 --- a/libtests/qtest/qutil/qutil.out +++ b/libtests/qtest/qutil/qutil.out @@ -22,6 +22,7 @@ one 7 compare okay +compare okay -2147483648 to int: PASSED 2147483647 to int: PASSED 2147483648 to int threw (integer out of range converting 2147483648 from a 8-byte signed type to a 4-byte signed type): PASSED diff --git a/libtests/qutil.cc b/libtests/qutil.cc index cd2b7796..f59b564d 100644 --- a/libtests/qutil.cc +++ b/libtests/qutil.cc @@ -150,6 +150,16 @@ void string_conversion_test() std::cout << "compare failed" << std::endl; } delete [] tmp; + // Also test with make_shared_cstr + auto tmp2 = QUtil::make_shared_cstr(embedded_null); + if (memcmp(tmp2.get(), embedded_null.c_str(), 7) == 0) + { + std::cout << "compare okay" << std::endl; + } + else + { + std::cout << "compare failed" << std::endl; + } std::string int_max_str = QUtil::int_to_string(INT_MAX); std::string int_min_str = QUtil::int_to_string(INT_MIN); @@ -407,8 +417,8 @@ void transcoding_test() void print_whoami(char const* str) { - PointerHolder dup(true, QUtil::copy_string(str)); - std::cout << QUtil::getWhoami(dup.getPointer()) << std::endl; + auto dup = QUtil::make_shared_cstr(str); + std::cout << QUtil::getWhoami(dup.get()) << std::endl; } void get_whoami_test()