| 133 | } |
| 134 | |
| 135 | std::map<std::string, std::string> parseContentDisposition(const std::vector<std::string>& input) { |
| 136 | std::map<std::string, std::string> result; |
| 137 | static std::string prefix = "Content-Disposition: "; |
| 138 | |
| 139 | // Find header |
| 140 | auto content_disposition_header = std::ranges::find_if(input, [](const std::string& header) { |
| 141 | return header.starts_with(prefix); |
| 142 | }); |
| 143 | |
| 144 | // Header not found |
| 145 | if (content_disposition_header == input.end()) { |
| 146 | return result; |
| 147 | } |
| 148 | |
| 149 | auto parseable = content_disposition_header->substr(prefix.size()); |
| 150 | auto parts = string::split(parseable, "; "); |
| 151 | for (const auto& part : parts) { |
| 152 | auto key_value = string::split(part, "="); |
| 153 | if (key_value.size() == 2) { |
| 154 | // Trim trailing newlines |
| 155 | auto value = string::trim(key_value[1], "\r\n"); |
| 156 | if (value.size() > 2) { |
| 157 | result[key_value[0]] = value.substr(1, value.size() - 2); |
| 158 | } else { |
| 159 | result[key_value[0]] = ""; |
| 160 | } |
| 161 | } |
| 162 | } |
| 163 | |
| 164 | return result; |
| 165 | } |
| 166 | |
| 167 | bool readAndDiscardOrSendError(httpd_req_t* request, const std::string& toRead) { |
| 168 | size_t bytes_read; |
no test coverage detected