Commit 34db6e3057d1b0c5afdcc60b570fdbe8ce711f8a
1 parent
04b18627
Introduce `global` namespace for managing qpdf global options and limits; add su…
…pport for configurable global limits via `qpdf_global_get_uint32` and `qpdf_global_set_uint32`.
Showing
9 changed files
with
551 additions
and
47 deletions
include/qpdf/Constants.h
| @@ -244,4 +244,47 @@ enum qpdf_page_label_e { | @@ -244,4 +244,47 @@ enum qpdf_page_label_e { | ||
| 244 | pl_roman_upper, | 244 | pl_roman_upper, |
| 245 | }; | 245 | }; |
| 246 | 246 | ||
| 247 | +/** | ||
| 248 | + * @enum qpdf_result_e | ||
| 249 | + * @brief Enum representing result codes for qpdf C-API functions. | ||
| 250 | + * | ||
| 251 | + * Results <= qpdf_r_no_warn indicate success without warnings, | ||
| 252 | + * qpdf_r_no_warn < result <= qpdf_r_success indicates success with warnings, and | ||
| 253 | + * qpdf_r_success < result indicates failure. | ||
| 254 | + */ | ||
| 255 | +enum qpdf_result_e { | ||
| 256 | + /* success */ | ||
| 257 | + qpdf_r_ok = 0, | ||
| 258 | + qpdf_r_no_warn = 0xff, /// any result <= qpdf_no_warn indicates success without warning | ||
| 259 | + qpdf_r_success = 0xffff, /// any result <= qpdf_no_warn indicates success | ||
| 260 | + /* failure */ | ||
| 261 | + qpdf_r_bad_parameter = 0x10000, | ||
| 262 | + | ||
| 263 | + qpdf_r_no_warn_mask = 0x7fffff00, | ||
| 264 | + qpdf_r_success_mask = 0x7fff0000, | ||
| 265 | +}; | ||
| 266 | + | ||
| 267 | +/** | ||
| 268 | + * @enum qpdf_param_e | ||
| 269 | + * @brief This enumeration defines various parameters and configuration options for qpdf C-API | ||
| 270 | + * functions. | ||
| 271 | + * | ||
| 272 | + * The enum values are grouped into sections based on their functionality, such as global | ||
| 273 | + * options or global limits.For the meaning of individual parameters see `qpdf/global.cc` | ||
| 274 | + */ | ||
| 275 | +enum qpdf_param_e { | ||
| 276 | + /* global options */ | ||
| 277 | + qpdf_p_default_limits = 0x10100, | ||
| 278 | + /* global limits */ | ||
| 279 | + | ||
| 280 | + /* object - parser limits */ | ||
| 281 | + qpdf_p_objects_max_nesting = 0x11000, | ||
| 282 | + qpdf_p_objects_max_errors, | ||
| 283 | + qpdf_p_objects_max_container_size, | ||
| 284 | + qpdf_p_objects_max_container_size_damaged, | ||
| 285 | + | ||
| 286 | + /* next section = 0x20000 */ | ||
| 287 | + qpdf_enum_max = 0x7fffffff, | ||
| 288 | +}; | ||
| 289 | + | ||
| 247 | #endif /* QPDFCONSTANTS_H */ | 290 | #endif /* QPDFCONSTANTS_H */ |
include/qpdf/QUtil.hh
| @@ -20,8 +20,10 @@ | @@ -20,8 +20,10 @@ | ||
| 20 | #ifndef QUTIL_HH | 20 | #ifndef QUTIL_HH |
| 21 | #define QUTIL_HH | 21 | #define QUTIL_HH |
| 22 | 22 | ||
| 23 | +#include <qpdf/Constants.h> | ||
| 23 | #include <qpdf/DLL.h> | 24 | #include <qpdf/DLL.h> |
| 24 | #include <qpdf/Types.h> | 25 | #include <qpdf/Types.h> |
| 26 | + | ||
| 25 | #include <cstdio> | 27 | #include <cstdio> |
| 26 | #include <cstring> | 28 | #include <cstring> |
| 27 | #include <ctime> | 29 | #include <ctime> |
| @@ -441,6 +443,25 @@ namespace QUtil | @@ -441,6 +443,25 @@ namespace QUtil | ||
| 441 | QPDF_DLL | 443 | QPDF_DLL |
| 442 | bool is_number(char const*); | 444 | bool is_number(char const*); |
| 443 | 445 | ||
| 446 | + /// @brief Handles the result code from qpdf functions. | ||
| 447 | + /// | ||
| 448 | + /// **For qpdf internal use only - not part of the public API** | ||
| 449 | + /// @par | ||
| 450 | + /// Depending on the result code, either continues execution, checks or throws an | ||
| 451 | + /// exception in case of an invalid parameter. | ||
| 452 | + /// | ||
| 453 | + /// @param result The result code of type qpdf_result_e, indicating success or failure status. | ||
| 454 | + /// @param context A string describing the context where this function is invoked, used for | ||
| 455 | + /// error reporting if an exception is thrown. | ||
| 456 | + /// | ||
| 457 | + /// @throws std::logic_error If the result code is `qpdf_bad_parameter`, indicating an invalid | ||
| 458 | + /// parameter was supplied to a function. The exception message will | ||
| 459 | + /// include the provided context for easier debugging. | ||
| 460 | + /// | ||
| 461 | + /// @since 12.3 | ||
| 462 | + QPDF_DLL | ||
| 463 | + void handle_result_code(qpdf_result_e result, std::string_view context); | ||
| 464 | + | ||
| 444 | // This method parses the numeric range syntax used by the qpdf command-line tool. May throw | 465 | // This method parses the numeric range syntax used by the qpdf command-line tool. May throw |
| 445 | // std::runtime_error. A numeric range is as comma-separated list of groups. A group may be a | 466 | // std::runtime_error. A numeric range is as comma-separated list of groups. A group may be a |
| 446 | // number specification or a range of number specifications separated by a dash. A number | 467 | // number specification or a range of number specifications separated by a dash. A number |
include/qpdf/global.hh
0 → 100644
| 1 | +// Copyright (c) 2005-2021 Jay Berkenbilt | ||
| 2 | +// Copyright (c) 2022-2025 Jay Berkenbilt and Manfred Holger | ||
| 3 | +// | ||
| 4 | +// This file is part of qpdf. | ||
| 5 | +// | ||
| 6 | +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except | ||
| 7 | +// in compliance with the License. You may obtain a copy of the License at | ||
| 8 | +// | ||
| 9 | +// http://www.apache.org/licenses/LICENSE-2.0 | ||
| 10 | +// | ||
| 11 | +// Unless required by applicable law or agreed to in writing, software distributed under the License | ||
| 12 | +// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express | ||
| 13 | +// or implied. See the License for the specific language governing permissions and limitations under | ||
| 14 | +// the License. | ||
| 15 | +// | ||
| 16 | +// Versions of qpdf prior to version 7 were released under the terms of version 2.0 of the Artistic | ||
| 17 | +// License. At your option, you may continue to consider qpdf to be licensed under those terms. | ||
| 18 | +// Please see the manual for additional information. | ||
| 19 | + | ||
| 20 | +#ifndef GLOBAL_HH | ||
| 21 | +#define GLOBAL_HH | ||
| 22 | + | ||
| 23 | +#include <qpdf/Constants.h> | ||
| 24 | + | ||
| 25 | +#include <qpdf/QUtil.hh> | ||
| 26 | +#include <qpdf/qpdf-c.h> | ||
| 27 | + | ||
| 28 | +#include <cstdint> | ||
| 29 | + | ||
| 30 | +namespace qpdf::global | ||
| 31 | +{ | ||
| 32 | + /// Helper function to translate result codes into C++ exceptions - for qpdf internal use only. | ||
| 33 | + inline void | ||
| 34 | + handle_result(qpdf_result_e result) | ||
| 35 | + { | ||
| 36 | + if (result != qpdf_r_ok) { | ||
| 37 | + QUtil::handle_result_code(result, "qpdf::global"); | ||
| 38 | + } | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + /// Helper function to wrap calls to qpdf_global_get_uint32 - for qpdf internal use only. | ||
| 42 | + inline uint32_t | ||
| 43 | + get_uint32(qpdf_param_e param) | ||
| 44 | + { | ||
| 45 | + uint32_t value; | ||
| 46 | + handle_result(qpdf_global_get_uint32(param, &value)); | ||
| 47 | + return value; | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + /// Helper function to wrap calls to qpdf_global_set_uint32 - for qpdf internal use only. | ||
| 51 | + inline void | ||
| 52 | + set_uint32(qpdf_param_e param, uint32_t value) | ||
| 53 | + { | ||
| 54 | + handle_result(qpdf_global_set_uint32(param, value)); | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | + namespace options | ||
| 58 | + { | ||
| 59 | + /// @brief Retrieves whether default limits are enabled. | ||
| 60 | + /// | ||
| 61 | + /// @return True if default limits are enabled. | ||
| 62 | + /// | ||
| 63 | + /// @since 12.3 | ||
| 64 | + bool inline default_limits() | ||
| 65 | + { | ||
| 66 | + return get_uint32(qpdf_p_default_limits) != 0; | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + /// @brief Disable all optional default limits if `false` is passed. | ||
| 70 | + /// | ||
| 71 | + /// This function disables all optional default limits if `false` is passed. Once default | ||
| 72 | + /// values have been disabled they cannot be re-enabled. Passing `true` has no effect. This | ||
| 73 | + /// function will leave any limits that have been explicitly set unchanged. Some limits, | ||
| 74 | + /// such as limits imposed to avoid stack overflows, cannot be disabled but can be changed. | ||
| 75 | + /// | ||
| 76 | + /// @param value A boolean indicating whether to disable (false) the default limits. | ||
| 77 | + /// | ||
| 78 | + /// @since 12.3 | ||
| 79 | + void inline default_limits(bool value) | ||
| 80 | + { | ||
| 81 | + set_uint32(qpdf_p_default_limits, value ? QPDF_TRUE : QPDF_FALSE); | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + } // namespace options | ||
| 85 | + | ||
| 86 | + namespace limits | ||
| 87 | + { | ||
| 88 | + /// @brief Retrieves the maximum nesting level while parsing objects. | ||
| 89 | + /// | ||
| 90 | + /// @return The maximum nesting level while parsing objects. | ||
| 91 | + /// | ||
| 92 | + /// @note The maximum nesting level cannot be disabled by calling `default_limit(false)`. | ||
| 93 | + /// | ||
| 94 | + /// @since 12.3 | ||
| 95 | + uint32_t inline objects_max_nesting() | ||
| 96 | + { | ||
| 97 | + return get_uint32(qpdf_p_objects_max_nesting); | ||
| 98 | + } | ||
| 99 | + | ||
| 100 | + /// @brief Sets the maximum nesting level while parsing objects. | ||
| 101 | + /// | ||
| 102 | + /// @param value The maximum nesting level to set. | ||
| 103 | + /// | ||
| 104 | + /// @note The maximum nesting level cannot be disabled by calling `default_limit(false)`. | ||
| 105 | + /// | ||
| 106 | + /// @since 12.3 | ||
| 107 | + void inline objects_max_nesting(uint32_t value) | ||
| 108 | + { | ||
| 109 | + set_uint32(qpdf_p_objects_max_nesting, value); | ||
| 110 | + } | ||
| 111 | + | ||
| 112 | + /// @brief Retrieves the maximum number of errors allowed while parsing objects. | ||
| 113 | + /// | ||
| 114 | + /// A value of 0 means that there is no maximum imposed. | ||
| 115 | + /// | ||
| 116 | + /// @return The maximum number of errors allowed while parsing objects. | ||
| 117 | + /// | ||
| 118 | + /// @since 12.3 | ||
| 119 | + uint32_t inline objects_max_errors() | ||
| 120 | + { | ||
| 121 | + return get_uint32(qpdf_p_objects_max_errors); | ||
| 122 | + } | ||
| 123 | + | ||
| 124 | + /// Sets the maximum number of errors allowed while parsing objects. | ||
| 125 | + /// | ||
| 126 | + /// A value of 0 means that there is no maximum imposed. | ||
| 127 | + /// | ||
| 128 | + /// @param value The maximum number of errors allowed while parsing objects to set. | ||
| 129 | + /// | ||
| 130 | + /// @since 12.3 | ||
| 131 | + void inline objects_max_errors(uint32_t value) | ||
| 132 | + { | ||
| 133 | + set_uint32(qpdf_p_objects_max_errors, value); | ||
| 134 | + } | ||
| 135 | + | ||
| 136 | + /// @brief Retrieves the maximum number of objectstop-level allowed in a container while | ||
| 137 | + /// parsing. | ||
| 138 | + /// | ||
| 139 | + /// The limit applies when the PDF document's xref table is undamaged and the object itself | ||
| 140 | + /// can be parsed without errors. The default limit is 4,294,967,295. | ||
| 141 | + /// | ||
| 142 | + /// @return The maximum number of top-level objects allowed in a container while parsing | ||
| 143 | + /// objects. | ||
| 144 | + /// | ||
| 145 | + /// @since 12.3 | ||
| 146 | + uint32_t inline objects_max_container_size() | ||
| 147 | + { | ||
| 148 | + return get_uint32(qpdf_p_objects_max_container_size); | ||
| 149 | + } | ||
| 150 | + | ||
| 151 | + /// @brief Sets the maximum number oftop-level objects allowed in a container while parsing. | ||
| 152 | + /// | ||
| 153 | + /// The limit applies when the PDF document's xref table is undamaged and the object itself | ||
| 154 | + /// can be parsed without errors. The default limit is 4,294,967,295. | ||
| 155 | + /// | ||
| 156 | + /// @param value The maximum number of top-level objects allowed in a container while | ||
| 157 | + /// parsing objects to set. | ||
| 158 | + /// | ||
| 159 | + /// @since 12.3 | ||
| 160 | + void inline objects_max_container_size(uint32_t value) | ||
| 161 | + { | ||
| 162 | + set_uint32(qpdf_p_objects_max_container_size, value); | ||
| 163 | + } | ||
| 164 | + | ||
| 165 | + /// @brief Retrieves the maximum number of top-level objects allowed in a container while | ||
| 166 | + /// parsing objects. | ||
| 167 | + /// | ||
| 168 | + /// The limit applies when the PDF document's xref table is damaged or the object itself is | ||
| 169 | + /// damaged. The limit also applies when parsing trailer dictionaries and xref streams. The | ||
| 170 | + /// default limit is 5,000. | ||
| 171 | + /// | ||
| 172 | + /// @return The maximum number of top-level objects allowed in a container while parsing | ||
| 173 | + /// objects. | ||
| 174 | + /// | ||
| 175 | + /// @since 12.3 | ||
| 176 | + uint32_t inline objects_max_container_size_damaged() | ||
| 177 | + { | ||
| 178 | + return get_uint32(qpdf_p_objects_max_container_size_damaged); | ||
| 179 | + } | ||
| 180 | + | ||
| 181 | + /// @brief Sets the maximum number of top-level objects allowed in a container while | ||
| 182 | + /// parsing. | ||
| 183 | + /// | ||
| 184 | + /// The limit applies when the PDF document's xref table is damaged or the object itself is | ||
| 185 | + /// damaged. The limit also applies when parsing trailer dictionaries and xref streams. The | ||
| 186 | + /// default limit is 5,000. | ||
| 187 | + /// | ||
| 188 | + /// @param value The maximum number of top-level objects allowed in a container while | ||
| 189 | + /// parsing objects to set. | ||
| 190 | + /// | ||
| 191 | + /// @since 12.3 | ||
| 192 | + void inline objects_max_container_size_damaged(uint32_t value) | ||
| 193 | + { | ||
| 194 | + set_uint32(qpdf_p_objects_max_container_size_damaged, value); | ||
| 195 | + } | ||
| 196 | + } // namespace limits | ||
| 197 | + | ||
| 198 | +} // namespace qpdf::global | ||
| 199 | + | ||
| 200 | +#endif // GLOBAL_HH |
include/qpdf/qpdf-c.h
| @@ -119,6 +119,8 @@ | @@ -119,6 +119,8 @@ | ||
| 119 | #include <qpdf/DLL.h> | 119 | #include <qpdf/DLL.h> |
| 120 | #include <qpdf/Types.h> | 120 | #include <qpdf/Types.h> |
| 121 | #include <qpdf/qpdflogger-c.h> | 121 | #include <qpdf/qpdflogger-c.h> |
| 122 | + | ||
| 123 | +#include <stdint.h> | ||
| 122 | #include <string.h> | 124 | #include <string.h> |
| 123 | 125 | ||
| 124 | #ifdef __cplusplus | 126 | #ifdef __cplusplus |
| @@ -1000,6 +1002,50 @@ extern "C" { | @@ -1000,6 +1002,50 @@ extern "C" { | ||
| 1000 | /* removePage() */ | 1002 | /* removePage() */ |
| 1001 | QPDF_DLL | 1003 | QPDF_DLL |
| 1002 | QPDF_ERROR_CODE qpdf_remove_page(qpdf_data qpdf, qpdf_oh page); | 1004 | QPDF_ERROR_CODE qpdf_remove_page(qpdf_data qpdf, qpdf_oh page); |
| 1005 | + | ||
| 1006 | + /* GLOBAL OPTIONS AND SETTINGS */ | ||
| 1007 | + | ||
| 1008 | + QPDF_DLL | ||
| 1009 | + /** | ||
| 1010 | + * @brief Retrieves a 32-bit unsigned integer value associated with a global option or limit. | ||
| 1011 | + * | ||
| 1012 | + * This function allows querying of specific parameters, identified by the qpdf_param_e enum, | ||
| 1013 | + * and retrieves their associated unsigned 32-bit integer values. The result will be stored in | ||
| 1014 | + * the variable pointed to by `value`. For details about the available parameters and their | ||
| 1015 | + * meanings see `qpdf/global.hh`. | ||
| 1016 | + * | ||
| 1017 | + * @param param[in] The parameter for which the value is being retrieved. This must be a valid | ||
| 1018 | + * value from the qpdf_param_e enumeration. | ||
| 1019 | + * @param value[out] A pointer to a uint32_t to store the retrieved value. This must be a valid, | ||
| 1020 | + * non-null pointer. | ||
| 1021 | + * | ||
| 1022 | + * @return An enumeration of type qpdf_result_e indicating the result of the operation. Possible | ||
| 1023 | + * values include success or specific error statuses related to the retrieval process. | ||
| 1024 | + * | ||
| 1025 | + * @since 12.3 | ||
| 1026 | + */ | ||
| 1027 | + enum qpdf_result_e qpdf_global_get_uint32(enum qpdf_param_e param, uint32_t* value); | ||
| 1028 | + | ||
| 1029 | + QPDF_DLL | ||
| 1030 | + /** | ||
| 1031 | + * @brief Sets a global option or limit for the qpdf library to a specified value. | ||
| 1032 | + * | ||
| 1033 | + * This function is used to configure global options or limits for the qpdf library based on the | ||
| 1034 | + * provided parameter and value. The behavior depends on the specific `param` provided and its | ||
| 1035 | + * valid range of values. For details about the available parameters and their meanings see | ||
| 1036 | + * `qpdf/global.hh`. | ||
| 1037 | + * | ||
| 1038 | + * @param param[in] The parameter to be set. Must be one of the values defined in the | ||
| 1039 | + * qpdf_param_e enumeration. | ||
| 1040 | + * @param value[in] The value to assign to the specified parameter. Interpretation of this value | ||
| 1041 | + * depends on the parameter being set. | ||
| 1042 | + * | ||
| 1043 | + * @return An enumeration of type qpdf_result_e indicating the result of the operation. Possible | ||
| 1044 | + * values include success or specific error statuses related to the retrieval process. | ||
| 1045 | + * | ||
| 1046 | + * @since 12.3 | ||
| 1047 | + */ | ||
| 1048 | + enum qpdf_result_e qpdf_global_set_uint32(enum qpdf_param_e param, uint32_t value); | ||
| 1003 | #ifdef __cplusplus | 1049 | #ifdef __cplusplus |
| 1004 | } | 1050 | } |
| 1005 | 1051 |
libqpdf/QUtil.cc
| @@ -40,6 +40,7 @@ | @@ -40,6 +40,7 @@ | ||
| 40 | #endif | 40 | #endif |
| 41 | 41 | ||
| 42 | using namespace qpdf; | 42 | using namespace qpdf; |
| 43 | +using namespace std::literals; | ||
| 43 | 44 | ||
| 44 | // First element is 24 | 45 | // First element is 24 |
| 45 | static unsigned short pdf_doc_low_to_unicode[] = { | 46 | static unsigned short pdf_doc_low_to_unicode[] = { |
| @@ -2068,3 +2069,15 @@ QUtil::is_hex_digit(char c) | @@ -2068,3 +2069,15 @@ QUtil::is_hex_digit(char c) | ||
| 2068 | { | 2069 | { |
| 2069 | return util::is_hex_digit(c); | 2070 | return util::is_hex_digit(c); |
| 2070 | } | 2071 | } |
| 2072 | + | ||
| 2073 | +void | ||
| 2074 | +QUtil::handle_result_code(qpdf_result_e result, std::string_view context) | ||
| 2075 | +{ | ||
| 2076 | + if (result == qpdf_r_ok) { | ||
| 2077 | + return; | ||
| 2078 | + } | ||
| 2079 | + qpdf::util::assertion( | ||
| 2080 | + result == qpdf_r_bad_parameter, | ||
| 2081 | + "unexpected result code received from function in "s.append(context)); | ||
| 2082 | + throw std::logic_error("invalid parameter supplied to function in "s.append(context)); | ||
| 2083 | +} |
libqpdf/global.cc
| 1 | #include <qpdf/global_private.hh> | 1 | #include <qpdf/global_private.hh> |
| 2 | 2 | ||
| 3 | +#include <qpdf/Util.hh> | ||
| 4 | + | ||
| 3 | using namespace qpdf; | 5 | using namespace qpdf; |
| 6 | +using namespace qpdf::global; | ||
| 7 | + | ||
| 8 | +Limits Limits::l; | ||
| 9 | +Options Options::o; | ||
| 10 | + | ||
| 11 | +void | ||
| 12 | +Limits::objects_max_container_size(bool damaged, uint32_t value) | ||
| 13 | +{ | ||
| 14 | + if (damaged) { | ||
| 15 | + l.objects_max_container_size_damaged_set_ = true; | ||
| 16 | + l.objects_max_container_size_damaged_ = value; | ||
| 17 | + } else { | ||
| 18 | + l.objects_max_container_size_ = value; | ||
| 19 | + } | ||
| 20 | +} | ||
| 21 | + | ||
| 22 | +void | ||
| 23 | +Limits::disable_defaults() | ||
| 24 | +{ | ||
| 25 | + if (!l.objects_max_errors_set_) { | ||
| 26 | + l.objects_max_errors_ = 0; | ||
| 27 | + } | ||
| 28 | + if (!l.objects_max_container_size_damaged_set_) { | ||
| 29 | + l.objects_max_container_size_damaged_ = std::numeric_limits<uint32_t>::max(); | ||
| 30 | + } | ||
| 31 | +} | ||
| 32 | + | ||
| 33 | +qpdf_result_e | ||
| 34 | +qpdf_global_get_uint32(qpdf_param_e param, uint32_t* value) | ||
| 35 | +{ | ||
| 36 | + qpdf_expect(value); | ||
| 37 | + switch (param) { | ||
| 38 | + case qpdf_p_default_limits: | ||
| 39 | + *value = Options::default_limits(); | ||
| 40 | + return qpdf_r_ok; | ||
| 41 | + case qpdf_p_objects_max_nesting: | ||
| 42 | + *value = Limits::objects_max_nesting(); | ||
| 43 | + return qpdf_r_ok; | ||
| 44 | + case qpdf_p_objects_max_errors: | ||
| 45 | + *value = Limits::objects_max_errors(); | ||
| 46 | + return qpdf_r_ok; | ||
| 47 | + case qpdf_p_objects_max_container_size: | ||
| 48 | + *value = Limits::objects_max_container_size(false); | ||
| 49 | + return qpdf_r_ok; | ||
| 50 | + case qpdf_p_objects_max_container_size_damaged: | ||
| 51 | + *value = Limits::objects_max_container_size(true); | ||
| 52 | + return qpdf_r_ok; | ||
| 53 | + default: | ||
| 54 | + return qpdf_r_bad_parameter; | ||
| 55 | + } | ||
| 56 | +} | ||
| 4 | 57 | ||
| 5 | -global::Limits global::Limits::l; | 58 | +qpdf_result_e |
| 59 | +qpdf_global_set_uint32(qpdf_param_e param, uint32_t value) | ||
| 60 | +{ | ||
| 61 | + switch (param) { | ||
| 62 | + case qpdf_p_default_limits: | ||
| 63 | + Options::default_limits(value); | ||
| 64 | + return qpdf_r_ok; | ||
| 65 | + case qpdf_p_objects_max_nesting: | ||
| 66 | + Limits::objects_max_nesting(value); | ||
| 67 | + return qpdf_r_ok; | ||
| 68 | + case qpdf_p_objects_max_errors: | ||
| 69 | + Limits::objects_max_errors(value); | ||
| 70 | + return qpdf_r_ok; | ||
| 71 | + case qpdf_p_objects_max_container_size: | ||
| 72 | + Limits::objects_max_container_size(false, value); | ||
| 73 | + return qpdf_r_ok; | ||
| 74 | + case qpdf_p_objects_max_container_size_damaged: | ||
| 75 | + Limits::objects_max_container_size(true, value); | ||
| 76 | + return qpdf_r_ok; | ||
| 77 | + default: | ||
| 78 | + return qpdf_r_bad_parameter; | ||
| 79 | + } | ||
| 80 | +} |
libqpdf/qpdf/global_private.hh
| 1 | - | ||
| 2 | #ifndef GLOBAL_PRIVATE_HH | 1 | #ifndef GLOBAL_PRIVATE_HH |
| 3 | #define GLOBAL_PRIVATE_HH | 2 | #define GLOBAL_PRIVATE_HH |
| 4 | 3 | ||
| 5 | -#include <qpdf/Constants.h> | 4 | +#include <qpdf/global.hh> |
| 6 | 5 | ||
| 7 | -#include <cstdint> | ||
| 8 | #include <limits> | 6 | #include <limits> |
| 9 | 7 | ||
| 10 | -namespace qpdf | 8 | +namespace qpdf::global |
| 11 | { | 9 | { |
| 12 | - namespace global | 10 | + class Limits |
| 13 | { | 11 | { |
| 14 | - class Limits | 12 | + public: |
| 13 | + Limits(Limits const&) = delete; | ||
| 14 | + Limits(Limits&&) = delete; | ||
| 15 | + Limits& operator=(Limits const&) = delete; | ||
| 16 | + Limits& operator=(Limits&&) = delete; | ||
| 17 | + | ||
| 18 | + static uint32_t const& | ||
| 19 | + objects_max_nesting() | ||
| 15 | { | 20 | { |
| 16 | - public: | ||
| 17 | - Limits(Limits const&) = delete; | ||
| 18 | - Limits(Limits&&) = delete; | ||
| 19 | - Limits& operator=(Limits const&) = delete; | ||
| 20 | - Limits& operator=(Limits&&) = delete; | ||
| 21 | - | ||
| 22 | - static uint32_t const& | ||
| 23 | - objects_max_nesting() | ||
| 24 | - { | ||
| 25 | - return l.objects_max_nesting_; | ||
| 26 | - } | 21 | + return l.objects_max_nesting_; |
| 22 | + } | ||
| 27 | 23 | ||
| 28 | - static uint32_t const& | ||
| 29 | - objects_max_errors() | ||
| 30 | - { | ||
| 31 | - return l.objects_max_errors_; | ||
| 32 | - } | 24 | + static void |
| 25 | + objects_max_nesting(uint32_t value) | ||
| 26 | + { | ||
| 27 | + l.objects_max_nesting_ = value; | ||
| 28 | + } | ||
| 33 | 29 | ||
| 34 | - static uint32_t const& | ||
| 35 | - objects_max_container_size(bool damaged) | ||
| 36 | - { | ||
| 37 | - return damaged ? l.objects_max_container_size_damaged_ | ||
| 38 | - : l.objects_max_container_size_; | ||
| 39 | - } | 30 | + static uint32_t const& |
| 31 | + objects_max_errors() | ||
| 32 | + { | ||
| 33 | + return l.objects_max_errors_; | ||
| 34 | + } | ||
| 40 | 35 | ||
| 41 | - private: | ||
| 42 | - Limits() = default; | ||
| 43 | - ~Limits() = default; | 36 | + static void |
| 37 | + objects_max_errors(uint32_t value) | ||
| 38 | + { | ||
| 39 | + l.objects_max_errors_set_ = true; | ||
| 40 | + l.objects_max_errors_ = value; | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + static uint32_t const& | ||
| 44 | + objects_max_container_size(bool damaged) | ||
| 45 | + { | ||
| 46 | + return damaged ? l.objects_max_container_size_damaged_ : l.objects_max_container_size_; | ||
| 47 | + } | ||
| 44 | 48 | ||
| 45 | - static Limits l; | 49 | + static void objects_max_container_size(bool damaged, uint32_t value); |
| 46 | 50 | ||
| 47 | - uint32_t objects_max_nesting_{499}; | ||
| 48 | - uint32_t objects_max_errors_{15}; | ||
| 49 | - uint32_t objects_max_container_size_{std::numeric_limits<uint32_t>::max()}; | ||
| 50 | - uint32_t objects_max_container_size_damaged_{5'000}; | ||
| 51 | - }; | 51 | + static void disable_defaults(); |
| 52 | + | ||
| 53 | + private: | ||
| 54 | + Limits() = default; | ||
| 55 | + ~Limits() = default; | ||
| 56 | + | ||
| 57 | + static Limits l; | ||
| 58 | + | ||
| 59 | + uint32_t objects_max_nesting_{499}; | ||
| 60 | + uint32_t objects_max_errors_{15}; | ||
| 61 | + bool objects_max_errors_set_{false}; | ||
| 62 | + uint32_t objects_max_container_size_{std::numeric_limits<uint32_t>::max()}; | ||
| 63 | + uint32_t objects_max_container_size_damaged_{5'000}; | ||
| 64 | + bool objects_max_container_size_damaged_set_{false}; | ||
| 65 | + }; | ||
| 66 | + | ||
| 67 | + class Options | ||
| 68 | + { | ||
| 69 | + public: | ||
| 70 | + static bool | ||
| 71 | + default_limits() | ||
| 72 | + { | ||
| 73 | + return static_cast<bool>(o.default_limits_); | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + static void | ||
| 77 | + default_limits(bool value) | ||
| 78 | + { | ||
| 79 | + if (!value) { | ||
| 80 | + o.default_limits_ = false; | ||
| 81 | + Limits::disable_defaults(); | ||
| 82 | + } | ||
| 83 | + } | ||
| 52 | 84 | ||
| 53 | - } // namespace global | 85 | + private: |
| 86 | + static Options o; | ||
| 54 | 87 | ||
| 55 | -} // namespace qpdf | 88 | + bool default_limits_{true}; |
| 89 | + }; | ||
| 90 | +} // namespace qpdf::global | ||
| 56 | 91 | ||
| 57 | #endif // GLOBAL_PRIVATE_HH | 92 | #endif // GLOBAL_PRIVATE_HH |
libtests/objects.cc
| @@ -7,6 +7,7 @@ | @@ -7,6 +7,7 @@ | ||
| 7 | #include <qpdf/QIntC.hh> | 7 | #include <qpdf/QIntC.hh> |
| 8 | #include <qpdf/QPDFObjectHandle_private.hh> | 8 | #include <qpdf/QPDFObjectHandle_private.hh> |
| 9 | #include <qpdf/QUtil.hh> | 9 | #include <qpdf/QUtil.hh> |
| 10 | +#include <qpdf/global.hh> | ||
| 10 | 11 | ||
| 11 | #include <climits> | 12 | #include <climits> |
| 12 | #include <cstdio> | 13 | #include <cstdio> |
| @@ -153,6 +154,75 @@ test_1(QPDF& pdf, char const* arg2) | @@ -153,6 +154,75 @@ test_1(QPDF& pdf, char const* arg2) | ||
| 153 | assert(QPDFObjectHandle(d).getDictAsMap().size() == 4); | 154 | assert(QPDFObjectHandle(d).getDictAsMap().size() == 4); |
| 154 | } | 155 | } |
| 155 | 156 | ||
| 157 | +static void | ||
| 158 | +test_2(QPDF& pdf, char const* arg2) | ||
| 159 | +{ | ||
| 160 | + // Test global limits. | ||
| 161 | + using namespace qpdf::global::options; | ||
| 162 | + using namespace qpdf::global::limits; | ||
| 163 | + | ||
| 164 | + // Check default values | ||
| 165 | + assert(objects_max_nesting() == 499); | ||
| 166 | + assert(objects_max_errors() == 15); | ||
| 167 | + assert(objects_max_container_size() == std::numeric_limits<uint32_t>::max()); | ||
| 168 | + assert(objects_max_container_size_damaged() == 5'000); | ||
| 169 | + assert(default_limits()); | ||
| 170 | + | ||
| 171 | + // Test disabling optional default limits | ||
| 172 | + default_limits(false); | ||
| 173 | + assert(objects_max_nesting() == 499); | ||
| 174 | + assert(objects_max_errors() == 0); | ||
| 175 | + assert(objects_max_container_size() == std::numeric_limits<uint32_t>::max()); | ||
| 176 | + assert(objects_max_container_size_damaged() == std::numeric_limits<uint32_t>::max()); | ||
| 177 | + assert(!default_limits()); | ||
| 178 | + | ||
| 179 | + // Check disabling default limits is irreversible | ||
| 180 | + default_limits(true); | ||
| 181 | + assert(!default_limits()); | ||
| 182 | + | ||
| 183 | + // Test setting limits | ||
| 184 | + objects_max_nesting(11); | ||
| 185 | + objects_max_errors(12); | ||
| 186 | + objects_max_container_size(13); | ||
| 187 | + objects_max_container_size_damaged(14); | ||
| 188 | + | ||
| 189 | + assert(objects_max_nesting() == 11); | ||
| 190 | + assert(objects_max_errors() == 12); | ||
| 191 | + assert(objects_max_container_size() == 13); | ||
| 192 | + assert(objects_max_container_size_damaged() == 14); | ||
| 193 | + | ||
| 194 | + // Check disabling default limits does not override explicit limits | ||
| 195 | + default_limits(false); | ||
| 196 | + assert(objects_max_nesting() == 11); | ||
| 197 | + assert(objects_max_errors() == 12); | ||
| 198 | + assert(objects_max_container_size() == 13); | ||
| 199 | + assert(objects_max_container_size_damaged() == 14); | ||
| 200 | + | ||
| 201 | + // Test parameter checking | ||
| 202 | + QUtil::handle_result_code(qpdf_r_ok, ""); | ||
| 203 | + bool thrown = false; | ||
| 204 | + try { | ||
| 205 | + qpdf::global::handle_result(qpdf_r_success_mask); | ||
| 206 | + } catch (std::logic_error const&) { | ||
| 207 | + thrown = true; | ||
| 208 | + } | ||
| 209 | + assert(thrown); | ||
| 210 | + thrown = false; | ||
| 211 | + try { | ||
| 212 | + qpdf::global::get_uint32(qpdf_param_e(42)); | ||
| 213 | + } catch (std::logic_error const&) { | ||
| 214 | + thrown = true; | ||
| 215 | + } | ||
| 216 | + assert(thrown); | ||
| 217 | + thrown = false; | ||
| 218 | + try { | ||
| 219 | + qpdf::global::set_uint32(qpdf_param_e(42), 42); | ||
| 220 | + } catch (std::logic_error const&) { | ||
| 221 | + thrown = true; | ||
| 222 | + } | ||
| 223 | + assert(thrown); | ||
| 224 | +} | ||
| 225 | + | ||
| 156 | void | 226 | void |
| 157 | runtest(int n, char const* filename1, char const* arg2) | 227 | runtest(int n, char const* filename1, char const* arg2) |
| 158 | { | 228 | { |
| @@ -160,9 +230,7 @@ runtest(int n, char const* filename1, char const* arg2) | @@ -160,9 +230,7 @@ runtest(int n, char const* filename1, char const* arg2) | ||
| 160 | // the test suite to see how the test is invoked to find the file | 230 | // the test suite to see how the test is invoked to find the file |
| 161 | // that the test is supposed to operate on. | 231 | // that the test is supposed to operate on. |
| 162 | 232 | ||
| 163 | - std::set<int> ignore_filename = { | ||
| 164 | - 1, | ||
| 165 | - }; | 233 | + std::set<int> ignore_filename = {1, 2}; |
| 166 | 234 | ||
| 167 | QPDF pdf; | 235 | QPDF pdf; |
| 168 | std::shared_ptr<char> file_buf; | 236 | std::shared_ptr<char> file_buf; |
| @@ -176,9 +244,7 @@ runtest(int n, char const* filename1, char const* arg2) | @@ -176,9 +244,7 @@ runtest(int n, char const* filename1, char const* arg2) | ||
| 176 | } | 244 | } |
| 177 | 245 | ||
| 178 | std::map<int, void (*)(QPDF&, char const*)> test_functions = { | 246 | std::map<int, void (*)(QPDF&, char const*)> test_functions = { |
| 179 | - {0, test_0}, | ||
| 180 | - {1, test_1}, | ||
| 181 | - }; | 247 | + {0, test_0}, {1, test_1}, {2, test_2}}; |
| 182 | 248 | ||
| 183 | auto fn = test_functions.find(n); | 249 | auto fn = test_functions.find(n); |
| 184 | if (fn == test_functions.end()) { | 250 | if (fn == test_functions.end()) { |
libtests/qtest/objects.test
| @@ -11,7 +11,7 @@ require TestDriver; | @@ -11,7 +11,7 @@ require TestDriver; | ||
| 11 | 11 | ||
| 12 | my $td = new TestDriver('objects'); | 12 | my $td = new TestDriver('objects'); |
| 13 | 13 | ||
| 14 | -my $n_tests = 2; | 14 | +my $n_tests = 3; |
| 15 | 15 | ||
| 16 | $td->runtest("integer type checks", | 16 | $td->runtest("integer type checks", |
| 17 | {$td->COMMAND => "objects 0 minimal.pdf"}, | 17 | {$td->COMMAND => "objects 0 minimal.pdf"}, |
| @@ -23,4 +23,9 @@ $td->runtest("dictionary checks", | @@ -23,4 +23,9 @@ $td->runtest("dictionary checks", | ||
| 23 | {$td->STRING => => "test 1 done\n", $td->EXIT_STATUS => 0}, | 23 | {$td->STRING => => "test 1 done\n", $td->EXIT_STATUS => 0}, |
| 24 | $td->NORMALIZE_NEWLINES); | 24 | $td->NORMALIZE_NEWLINES); |
| 25 | 25 | ||
| 26 | +$td->runtest("global object limits", | ||
| 27 | + {$td->COMMAND => "objects 2 -"}, | ||
| 28 | + {$td->STRING => => "test 2 done\n", $td->EXIT_STATUS => 0}, | ||
| 29 | + $td->NORMALIZE_NEWLINES); | ||
| 30 | + | ||
| 26 | $td->report($n_tests); | 31 | $td->report($n_tests); |