toolkit/crashreporter/google-breakpad/src/common/windows/http_upload.cc

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 // Copyright (c) 2006, Google Inc.
michael@0 2 // All rights reserved.
michael@0 3 //
michael@0 4 // Redistribution and use in source and binary forms, with or without
michael@0 5 // modification, are permitted provided that the following conditions are
michael@0 6 // met:
michael@0 7 //
michael@0 8 // * Redistributions of source code must retain the above copyright
michael@0 9 // notice, this list of conditions and the following disclaimer.
michael@0 10 // * Redistributions in binary form must reproduce the above
michael@0 11 // copyright notice, this list of conditions and the following disclaimer
michael@0 12 // in the documentation and/or other materials provided with the
michael@0 13 // distribution.
michael@0 14 // * Neither the name of Google Inc. nor the names of its
michael@0 15 // contributors may be used to endorse or promote products derived from
michael@0 16 // this software without specific prior written permission.
michael@0 17 //
michael@0 18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
michael@0 19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
michael@0 20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
michael@0 21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
michael@0 22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
michael@0 23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
michael@0 24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
michael@0 25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
michael@0 26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
michael@0 27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
michael@0 28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
michael@0 29
michael@0 30 #include <assert.h>
michael@0 31
michael@0 32 // Disable exception handler warnings.
michael@0 33 #pragma warning( disable : 4530 )
michael@0 34
michael@0 35 #include <fstream>
michael@0 36
michael@0 37 #include "common/windows/string_utils-inl.h"
michael@0 38
michael@0 39 #include "common/windows/http_upload.h"
michael@0 40
michael@0 41 namespace google_breakpad {
michael@0 42
michael@0 43 using std::ifstream;
michael@0 44 using std::ios;
michael@0 45
michael@0 46 static const wchar_t kUserAgent[] = L"Breakpad/1.0 (Windows)";
michael@0 47
michael@0 48 // Helper class which closes an internet handle when it goes away
michael@0 49 class HTTPUpload::AutoInternetHandle {
michael@0 50 public:
michael@0 51 explicit AutoInternetHandle(HINTERNET handle) : handle_(handle) {}
michael@0 52 ~AutoInternetHandle() {
michael@0 53 if (handle_) {
michael@0 54 InternetCloseHandle(handle_);
michael@0 55 }
michael@0 56 }
michael@0 57
michael@0 58 HINTERNET get() { return handle_; }
michael@0 59
michael@0 60 private:
michael@0 61 HINTERNET handle_;
michael@0 62 };
michael@0 63
michael@0 64 // static
michael@0 65 bool HTTPUpload::SendRequest(const wstring &url,
michael@0 66 const map<wstring, wstring> &parameters,
michael@0 67 const wstring &upload_file,
michael@0 68 const wstring &file_part_name,
michael@0 69 int *timeout,
michael@0 70 wstring *response_body,
michael@0 71 int *response_code) {
michael@0 72 if (response_code) {
michael@0 73 *response_code = 0;
michael@0 74 }
michael@0 75
michael@0 76 // TODO(bryner): support non-ASCII parameter names
michael@0 77 if (!CheckParameters(parameters)) {
michael@0 78 return false;
michael@0 79 }
michael@0 80
michael@0 81 // Break up the URL and make sure we can handle it
michael@0 82 wchar_t scheme[16], host[256], path[256];
michael@0 83 URL_COMPONENTS components;
michael@0 84 memset(&components, 0, sizeof(components));
michael@0 85 components.dwStructSize = sizeof(components);
michael@0 86 components.lpszScheme = scheme;
michael@0 87 components.dwSchemeLength = sizeof(scheme) / sizeof(scheme[0]);
michael@0 88 components.lpszHostName = host;
michael@0 89 components.dwHostNameLength = sizeof(host) / sizeof(host[0]);
michael@0 90 components.lpszUrlPath = path;
michael@0 91 components.dwUrlPathLength = sizeof(path) / sizeof(path[0]);
michael@0 92 if (!InternetCrackUrl(url.c_str(), static_cast<DWORD>(url.size()),
michael@0 93 0, &components)) {
michael@0 94 return false;
michael@0 95 }
michael@0 96 bool secure = false;
michael@0 97 if (wcscmp(scheme, L"https") == 0) {
michael@0 98 secure = true;
michael@0 99 } else if (wcscmp(scheme, L"http") != 0) {
michael@0 100 return false;
michael@0 101 }
michael@0 102
michael@0 103 AutoInternetHandle internet(InternetOpen(kUserAgent,
michael@0 104 INTERNET_OPEN_TYPE_PRECONFIG,
michael@0 105 NULL, // proxy name
michael@0 106 NULL, // proxy bypass
michael@0 107 0)); // flags
michael@0 108 if (!internet.get()) {
michael@0 109 return false;
michael@0 110 }
michael@0 111
michael@0 112 AutoInternetHandle connection(InternetConnect(internet.get(),
michael@0 113 host,
michael@0 114 components.nPort,
michael@0 115 NULL, // user name
michael@0 116 NULL, // password
michael@0 117 INTERNET_SERVICE_HTTP,
michael@0 118 0, // flags
michael@0 119 NULL)); // context
michael@0 120 if (!connection.get()) {
michael@0 121 return false;
michael@0 122 }
michael@0 123
michael@0 124 DWORD http_open_flags = secure ? INTERNET_FLAG_SECURE : 0;
michael@0 125 http_open_flags |= INTERNET_FLAG_NO_COOKIES;
michael@0 126 AutoInternetHandle request(HttpOpenRequest(connection.get(),
michael@0 127 L"POST",
michael@0 128 path,
michael@0 129 NULL, // version
michael@0 130 NULL, // referer
michael@0 131 NULL, // agent type
michael@0 132 http_open_flags,
michael@0 133 NULL)); // context
michael@0 134 if (!request.get()) {
michael@0 135 return false;
michael@0 136 }
michael@0 137
michael@0 138 wstring boundary = GenerateMultipartBoundary();
michael@0 139 wstring content_type_header = GenerateRequestHeader(boundary);
michael@0 140 HttpAddRequestHeaders(request.get(),
michael@0 141 content_type_header.c_str(),
michael@0 142 static_cast<DWORD>(-1),
michael@0 143 HTTP_ADDREQ_FLAG_ADD);
michael@0 144
michael@0 145 string request_body;
michael@0 146 if (!GenerateRequestBody(parameters, upload_file,
michael@0 147 file_part_name, boundary, &request_body)) {
michael@0 148 return false;
michael@0 149 }
michael@0 150
michael@0 151 if (timeout) {
michael@0 152 if (!InternetSetOption(request.get(),
michael@0 153 INTERNET_OPTION_SEND_TIMEOUT,
michael@0 154 timeout,
michael@0 155 sizeof(*timeout))) {
michael@0 156 fwprintf(stderr, L"Could not unset send timeout, continuing...\n");
michael@0 157 }
michael@0 158
michael@0 159 if (!InternetSetOption(request.get(),
michael@0 160 INTERNET_OPTION_RECEIVE_TIMEOUT,
michael@0 161 timeout,
michael@0 162 sizeof(*timeout))) {
michael@0 163 fwprintf(stderr, L"Could not unset receive timeout, continuing...\n");
michael@0 164 }
michael@0 165 }
michael@0 166
michael@0 167 if (!HttpSendRequest(request.get(), NULL, 0,
michael@0 168 const_cast<char *>(request_body.data()),
michael@0 169 static_cast<DWORD>(request_body.size()))) {
michael@0 170 return false;
michael@0 171 }
michael@0 172
michael@0 173 // The server indicates a successful upload with HTTP status 200.
michael@0 174 wchar_t http_status[4];
michael@0 175 DWORD http_status_size = sizeof(http_status);
michael@0 176 if (!HttpQueryInfo(request.get(), HTTP_QUERY_STATUS_CODE,
michael@0 177 static_cast<LPVOID>(&http_status), &http_status_size,
michael@0 178 0)) {
michael@0 179 return false;
michael@0 180 }
michael@0 181
michael@0 182 int http_response = wcstol(http_status, NULL, 10);
michael@0 183 if (response_code) {
michael@0 184 *response_code = http_response;
michael@0 185 }
michael@0 186
michael@0 187 bool result = (http_response == 200);
michael@0 188
michael@0 189 if (result) {
michael@0 190 result = ReadResponse(request.get(), response_body);
michael@0 191 }
michael@0 192
michael@0 193 return result;
michael@0 194 }
michael@0 195
michael@0 196 // static
michael@0 197 bool HTTPUpload::ReadResponse(HINTERNET request, wstring *response) {
michael@0 198 bool has_content_length_header = false;
michael@0 199 wchar_t content_length[32];
michael@0 200 DWORD content_length_size = sizeof(content_length);
michael@0 201 DWORD claimed_size = 0;
michael@0 202 string response_body;
michael@0 203
michael@0 204 if (HttpQueryInfo(request, HTTP_QUERY_CONTENT_LENGTH,
michael@0 205 static_cast<LPVOID>(&content_length),
michael@0 206 &content_length_size, 0)) {
michael@0 207 has_content_length_header = true;
michael@0 208 claimed_size = wcstol(content_length, NULL, 10);
michael@0 209 response_body.reserve(claimed_size);
michael@0 210 }
michael@0 211
michael@0 212
michael@0 213 DWORD bytes_available;
michael@0 214 DWORD total_read = 0;
michael@0 215 BOOL return_code;
michael@0 216
michael@0 217 while (((return_code = InternetQueryDataAvailable(request, &bytes_available,
michael@0 218 0, 0)) != 0) && bytes_available > 0) {
michael@0 219
michael@0 220 vector<char> response_buffer(bytes_available);
michael@0 221 DWORD size_read;
michael@0 222
michael@0 223 return_code = InternetReadFile(request,
michael@0 224 &response_buffer[0],
michael@0 225 bytes_available, &size_read);
michael@0 226
michael@0 227 if (return_code && size_read > 0) {
michael@0 228 total_read += size_read;
michael@0 229 response_body.append(&response_buffer[0], size_read);
michael@0 230 } else {
michael@0 231 break;
michael@0 232 }
michael@0 233 }
michael@0 234
michael@0 235 bool succeeded = return_code && (!has_content_length_header ||
michael@0 236 (total_read == claimed_size));
michael@0 237 if (succeeded && response) {
michael@0 238 *response = UTF8ToWide(response_body);
michael@0 239 }
michael@0 240
michael@0 241 return succeeded;
michael@0 242 }
michael@0 243
michael@0 244 // static
michael@0 245 wstring HTTPUpload::GenerateMultipartBoundary() {
michael@0 246 // The boundary has 27 '-' characters followed by 16 hex digits
michael@0 247 static const wchar_t kBoundaryPrefix[] = L"---------------------------";
michael@0 248 static const int kBoundaryLength = 27 + 16 + 1;
michael@0 249
michael@0 250 // Generate some random numbers to fill out the boundary
michael@0 251 int r0 = rand();
michael@0 252 int r1 = rand();
michael@0 253
michael@0 254 wchar_t temp[kBoundaryLength];
michael@0 255 swprintf(temp, kBoundaryLength, L"%s%08X%08X", kBoundaryPrefix, r0, r1);
michael@0 256
michael@0 257 // remove when VC++7.1 is no longer supported
michael@0 258 temp[kBoundaryLength - 1] = L'\0';
michael@0 259
michael@0 260 return wstring(temp);
michael@0 261 }
michael@0 262
michael@0 263 // static
michael@0 264 wstring HTTPUpload::GenerateRequestHeader(const wstring &boundary) {
michael@0 265 wstring header = L"Content-Type: multipart/form-data; boundary=";
michael@0 266 header += boundary;
michael@0 267 return header;
michael@0 268 }
michael@0 269
michael@0 270 // static
michael@0 271 bool HTTPUpload::GenerateRequestBody(const map<wstring, wstring> &parameters,
michael@0 272 const wstring &upload_file,
michael@0 273 const wstring &file_part_name,
michael@0 274 const wstring &boundary,
michael@0 275 string *request_body) {
michael@0 276 vector<char> contents;
michael@0 277 if (!GetFileContents(upload_file, &contents)) {
michael@0 278 return false;
michael@0 279 }
michael@0 280
michael@0 281 string boundary_str = WideToUTF8(boundary);
michael@0 282 if (boundary_str.empty()) {
michael@0 283 return false;
michael@0 284 }
michael@0 285
michael@0 286 request_body->clear();
michael@0 287
michael@0 288 // Append each of the parameter pairs as a form-data part
michael@0 289 for (map<wstring, wstring>::const_iterator pos = parameters.begin();
michael@0 290 pos != parameters.end(); ++pos) {
michael@0 291 request_body->append("--" + boundary_str + "\r\n");
michael@0 292 request_body->append("Content-Disposition: form-data; name=\"" +
michael@0 293 WideToUTF8(pos->first) + "\"\r\n\r\n" +
michael@0 294 WideToUTF8(pos->second) + "\r\n");
michael@0 295 }
michael@0 296
michael@0 297 // Now append the upload file as a binary (octet-stream) part
michael@0 298 string filename_utf8 = WideToUTF8(upload_file);
michael@0 299 if (filename_utf8.empty()) {
michael@0 300 return false;
michael@0 301 }
michael@0 302
michael@0 303 string file_part_name_utf8 = WideToUTF8(file_part_name);
michael@0 304 if (file_part_name_utf8.empty()) {
michael@0 305 return false;
michael@0 306 }
michael@0 307
michael@0 308 request_body->append("--" + boundary_str + "\r\n");
michael@0 309 request_body->append("Content-Disposition: form-data; "
michael@0 310 "name=\"" + file_part_name_utf8 + "\"; "
michael@0 311 "filename=\"" + filename_utf8 + "\"\r\n");
michael@0 312 request_body->append("Content-Type: application/octet-stream\r\n");
michael@0 313 request_body->append("\r\n");
michael@0 314
michael@0 315 if (!contents.empty()) {
michael@0 316 request_body->append(&(contents[0]), contents.size());
michael@0 317 }
michael@0 318 request_body->append("\r\n");
michael@0 319 request_body->append("--" + boundary_str + "--\r\n");
michael@0 320 return true;
michael@0 321 }
michael@0 322
michael@0 323 // static
michael@0 324 bool HTTPUpload::GetFileContents(const wstring &filename,
michael@0 325 vector<char> *contents) {
michael@0 326 // The "open" method on pre-MSVC8 ifstream implementations doesn't accept a
michael@0 327 // wchar_t* filename, so use _wfopen directly in that case. For VC8 and
michael@0 328 // later, _wfopen has been deprecated in favor of _wfopen_s, which does
michael@0 329 // not exist in earlier versions, so let the ifstream open the file itself.
michael@0 330 #if _MSC_VER >= 1400 // MSVC 2005/8
michael@0 331 ifstream file;
michael@0 332 file.open(filename.c_str(), ios::binary);
michael@0 333 #else // _MSC_VER >= 1400
michael@0 334 ifstream file(_wfopen(filename.c_str(), L"rb"));
michael@0 335 #endif // _MSC_VER >= 1400
michael@0 336 if (file.is_open()) {
michael@0 337 file.seekg(0, ios::end);
michael@0 338 std::streamoff length = file.tellg();
michael@0 339 contents->resize(length);
michael@0 340 if (length != 0) {
michael@0 341 file.seekg(0, ios::beg);
michael@0 342 file.read(&((*contents)[0]), length);
michael@0 343 }
michael@0 344 file.close();
michael@0 345 return true;
michael@0 346 }
michael@0 347 return false;
michael@0 348 }
michael@0 349
michael@0 350 // static
michael@0 351 wstring HTTPUpload::UTF8ToWide(const string &utf8) {
michael@0 352 if (utf8.length() == 0) {
michael@0 353 return wstring();
michael@0 354 }
michael@0 355
michael@0 356 // compute the length of the buffer we'll need
michael@0 357 int charcount = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, NULL, 0);
michael@0 358
michael@0 359 if (charcount == 0) {
michael@0 360 return wstring();
michael@0 361 }
michael@0 362
michael@0 363 // convert
michael@0 364 wchar_t* buf = new wchar_t[charcount];
michael@0 365 MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, buf, charcount);
michael@0 366 wstring result(buf);
michael@0 367 delete[] buf;
michael@0 368 return result;
michael@0 369 }
michael@0 370
michael@0 371 // static
michael@0 372 string HTTPUpload::WideToUTF8(const wstring &wide) {
michael@0 373 if (wide.length() == 0) {
michael@0 374 return string();
michael@0 375 }
michael@0 376
michael@0 377 // compute the length of the buffer we'll need
michael@0 378 int charcount = WideCharToMultiByte(CP_UTF8, 0, wide.c_str(), -1,
michael@0 379 NULL, 0, NULL, NULL);
michael@0 380 if (charcount == 0) {
michael@0 381 return string();
michael@0 382 }
michael@0 383
michael@0 384 // convert
michael@0 385 char *buf = new char[charcount];
michael@0 386 WideCharToMultiByte(CP_UTF8, 0, wide.c_str(), -1, buf, charcount,
michael@0 387 NULL, NULL);
michael@0 388
michael@0 389 string result(buf);
michael@0 390 delete[] buf;
michael@0 391 return result;
michael@0 392 }
michael@0 393
michael@0 394 // static
michael@0 395 bool HTTPUpload::CheckParameters(const map<wstring, wstring> &parameters) {
michael@0 396 for (map<wstring, wstring>::const_iterator pos = parameters.begin();
michael@0 397 pos != parameters.end(); ++pos) {
michael@0 398 const wstring &str = pos->first;
michael@0 399 if (str.size() == 0) {
michael@0 400 return false; // disallow empty parameter names
michael@0 401 }
michael@0 402 for (unsigned int i = 0; i < str.size(); ++i) {
michael@0 403 wchar_t c = str[i];
michael@0 404 if (c < 32 || c == '"' || c > 127) {
michael@0 405 return false;
michael@0 406 }
michael@0 407 }
michael@0 408 }
michael@0 409 return true;
michael@0 410 }
michael@0 411
michael@0 412 } // namespace google_breakpad

mercurial