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

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/toolkit/crashreporter/google-breakpad/src/common/windows/http_upload.cc	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,412 @@
     1.4 +// Copyright (c) 2006, Google Inc.
     1.5 +// All rights reserved.
     1.6 +//
     1.7 +// Redistribution and use in source and binary forms, with or without
     1.8 +// modification, are permitted provided that the following conditions are
     1.9 +// met:
    1.10 +//
    1.11 +//     * Redistributions of source code must retain the above copyright
    1.12 +// notice, this list of conditions and the following disclaimer.
    1.13 +//     * Redistributions in binary form must reproduce the above
    1.14 +// copyright notice, this list of conditions and the following disclaimer
    1.15 +// in the documentation and/or other materials provided with the
    1.16 +// distribution.
    1.17 +//     * Neither the name of Google Inc. nor the names of its
    1.18 +// contributors may be used to endorse or promote products derived from
    1.19 +// this software without specific prior written permission.
    1.20 +//
    1.21 +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    1.22 +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    1.23 +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    1.24 +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    1.25 +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    1.26 +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    1.27 +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    1.28 +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    1.29 +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    1.30 +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    1.31 +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    1.32 +
    1.33 +#include <assert.h>
    1.34 +
    1.35 +// Disable exception handler warnings.
    1.36 +#pragma warning( disable : 4530 )
    1.37 +
    1.38 +#include <fstream>
    1.39 +
    1.40 +#include "common/windows/string_utils-inl.h"
    1.41 +
    1.42 +#include "common/windows/http_upload.h"
    1.43 +
    1.44 +namespace google_breakpad {
    1.45 +
    1.46 +using std::ifstream;
    1.47 +using std::ios;
    1.48 +
    1.49 +static const wchar_t kUserAgent[] = L"Breakpad/1.0 (Windows)";
    1.50 +
    1.51 +// Helper class which closes an internet handle when it goes away
    1.52 +class HTTPUpload::AutoInternetHandle {
    1.53 + public:
    1.54 +  explicit AutoInternetHandle(HINTERNET handle) : handle_(handle) {}
    1.55 +  ~AutoInternetHandle() {
    1.56 +    if (handle_) {
    1.57 +      InternetCloseHandle(handle_);
    1.58 +    }
    1.59 +  }
    1.60 +
    1.61 +  HINTERNET get() { return handle_; }
    1.62 +
    1.63 + private:
    1.64 +  HINTERNET handle_;
    1.65 +};
    1.66 +
    1.67 +// static
    1.68 +bool HTTPUpload::SendRequest(const wstring &url,
    1.69 +                             const map<wstring, wstring> &parameters,
    1.70 +                             const wstring &upload_file,
    1.71 +                             const wstring &file_part_name,
    1.72 +                             int *timeout,
    1.73 +                             wstring *response_body,
    1.74 +                             int *response_code) {
    1.75 +  if (response_code) {
    1.76 +    *response_code = 0;
    1.77 +  }
    1.78 +
    1.79 +  // TODO(bryner): support non-ASCII parameter names
    1.80 +  if (!CheckParameters(parameters)) {
    1.81 +    return false;
    1.82 +  }
    1.83 +
    1.84 +  // Break up the URL and make sure we can handle it
    1.85 +  wchar_t scheme[16], host[256], path[256];
    1.86 +  URL_COMPONENTS components;
    1.87 +  memset(&components, 0, sizeof(components));
    1.88 +  components.dwStructSize = sizeof(components);
    1.89 +  components.lpszScheme = scheme;
    1.90 +  components.dwSchemeLength = sizeof(scheme) / sizeof(scheme[0]);
    1.91 +  components.lpszHostName = host;
    1.92 +  components.dwHostNameLength = sizeof(host) / sizeof(host[0]);
    1.93 +  components.lpszUrlPath = path;
    1.94 +  components.dwUrlPathLength = sizeof(path) / sizeof(path[0]);
    1.95 +  if (!InternetCrackUrl(url.c_str(), static_cast<DWORD>(url.size()),
    1.96 +                        0, &components)) {
    1.97 +    return false;
    1.98 +  }
    1.99 +  bool secure = false;
   1.100 +  if (wcscmp(scheme, L"https") == 0) {
   1.101 +    secure = true;
   1.102 +  } else if (wcscmp(scheme, L"http") != 0) {
   1.103 +    return false;
   1.104 +  }
   1.105 +
   1.106 +  AutoInternetHandle internet(InternetOpen(kUserAgent,
   1.107 +                                           INTERNET_OPEN_TYPE_PRECONFIG,
   1.108 +                                           NULL,  // proxy name
   1.109 +                                           NULL,  // proxy bypass
   1.110 +                                           0));   // flags
   1.111 +  if (!internet.get()) {
   1.112 +    return false;
   1.113 +  }
   1.114 +
   1.115 +  AutoInternetHandle connection(InternetConnect(internet.get(),
   1.116 +                                                host,
   1.117 +                                                components.nPort,
   1.118 +                                                NULL,    // user name
   1.119 +                                                NULL,    // password
   1.120 +                                                INTERNET_SERVICE_HTTP,
   1.121 +                                                0,       // flags
   1.122 +                                                NULL));  // context
   1.123 +  if (!connection.get()) {
   1.124 +    return false;
   1.125 +  }
   1.126 +
   1.127 +  DWORD http_open_flags = secure ? INTERNET_FLAG_SECURE : 0;
   1.128 +  http_open_flags |= INTERNET_FLAG_NO_COOKIES;
   1.129 +  AutoInternetHandle request(HttpOpenRequest(connection.get(),
   1.130 +                                             L"POST",
   1.131 +                                             path,
   1.132 +                                             NULL,    // version
   1.133 +                                             NULL,    // referer
   1.134 +                                             NULL,    // agent type
   1.135 +                                             http_open_flags,
   1.136 +                                             NULL));  // context
   1.137 +  if (!request.get()) {
   1.138 +    return false;
   1.139 +  }
   1.140 +
   1.141 +  wstring boundary = GenerateMultipartBoundary();
   1.142 +  wstring content_type_header = GenerateRequestHeader(boundary);
   1.143 +  HttpAddRequestHeaders(request.get(),
   1.144 +                        content_type_header.c_str(),
   1.145 +                        static_cast<DWORD>(-1),
   1.146 +                        HTTP_ADDREQ_FLAG_ADD);
   1.147 +
   1.148 +  string request_body;
   1.149 +  if (!GenerateRequestBody(parameters, upload_file,
   1.150 +                           file_part_name, boundary, &request_body)) {
   1.151 +    return false;
   1.152 +  }
   1.153 +
   1.154 +  if (timeout) {
   1.155 +    if (!InternetSetOption(request.get(),
   1.156 +                           INTERNET_OPTION_SEND_TIMEOUT,
   1.157 +                           timeout,
   1.158 +                           sizeof(*timeout))) {
   1.159 +      fwprintf(stderr, L"Could not unset send timeout, continuing...\n");
   1.160 +    }
   1.161 +
   1.162 +    if (!InternetSetOption(request.get(),
   1.163 +                           INTERNET_OPTION_RECEIVE_TIMEOUT,
   1.164 +                           timeout,
   1.165 +                           sizeof(*timeout))) {
   1.166 +      fwprintf(stderr, L"Could not unset receive timeout, continuing...\n");
   1.167 +    }
   1.168 +  }
   1.169 +  
   1.170 +  if (!HttpSendRequest(request.get(), NULL, 0,
   1.171 +                       const_cast<char *>(request_body.data()),
   1.172 +                       static_cast<DWORD>(request_body.size()))) {
   1.173 +    return false;
   1.174 +  }
   1.175 +
   1.176 +  // The server indicates a successful upload with HTTP status 200.
   1.177 +  wchar_t http_status[4];
   1.178 +  DWORD http_status_size = sizeof(http_status);
   1.179 +  if (!HttpQueryInfo(request.get(), HTTP_QUERY_STATUS_CODE,
   1.180 +                     static_cast<LPVOID>(&http_status), &http_status_size,
   1.181 +                     0)) {
   1.182 +    return false;
   1.183 +  }
   1.184 +
   1.185 +  int http_response = wcstol(http_status, NULL, 10);
   1.186 +  if (response_code) {
   1.187 +    *response_code = http_response;
   1.188 +  }
   1.189 +
   1.190 +  bool result = (http_response == 200);
   1.191 +
   1.192 +  if (result) {
   1.193 +    result = ReadResponse(request.get(), response_body);
   1.194 +  }
   1.195 +
   1.196 +  return result;
   1.197 +}
   1.198 +
   1.199 +// static
   1.200 +bool HTTPUpload::ReadResponse(HINTERNET request, wstring *response) {
   1.201 +  bool has_content_length_header = false;
   1.202 +  wchar_t content_length[32];
   1.203 +  DWORD content_length_size = sizeof(content_length);
   1.204 +  DWORD claimed_size = 0;
   1.205 +  string response_body;
   1.206 +
   1.207 +  if (HttpQueryInfo(request, HTTP_QUERY_CONTENT_LENGTH,
   1.208 +                    static_cast<LPVOID>(&content_length),
   1.209 +                    &content_length_size, 0)) {
   1.210 +    has_content_length_header = true;
   1.211 +    claimed_size = wcstol(content_length, NULL, 10);
   1.212 +    response_body.reserve(claimed_size);
   1.213 +  }
   1.214 +
   1.215 +
   1.216 +  DWORD bytes_available;
   1.217 +  DWORD total_read = 0;
   1.218 +  BOOL return_code;
   1.219 +
   1.220 +  while (((return_code = InternetQueryDataAvailable(request, &bytes_available,
   1.221 +	  0, 0)) != 0) && bytes_available > 0) {
   1.222 +
   1.223 +    vector<char> response_buffer(bytes_available);
   1.224 +    DWORD size_read;
   1.225 +
   1.226 +    return_code = InternetReadFile(request,
   1.227 +                                   &response_buffer[0],
   1.228 +                                   bytes_available, &size_read);
   1.229 +
   1.230 +    if (return_code && size_read > 0) {
   1.231 +      total_read += size_read;
   1.232 +      response_body.append(&response_buffer[0], size_read);
   1.233 +    } else {
   1.234 +      break;
   1.235 +    }
   1.236 +  }
   1.237 +
   1.238 +  bool succeeded = return_code && (!has_content_length_header ||
   1.239 +                                   (total_read == claimed_size));
   1.240 +  if (succeeded && response) {
   1.241 +    *response = UTF8ToWide(response_body);
   1.242 +  }
   1.243 +
   1.244 +  return succeeded;
   1.245 +}
   1.246 +
   1.247 +// static
   1.248 +wstring HTTPUpload::GenerateMultipartBoundary() {
   1.249 +  // The boundary has 27 '-' characters followed by 16 hex digits
   1.250 +  static const wchar_t kBoundaryPrefix[] = L"---------------------------";
   1.251 +  static const int kBoundaryLength = 27 + 16 + 1;
   1.252 +
   1.253 +  // Generate some random numbers to fill out the boundary
   1.254 +  int r0 = rand();
   1.255 +  int r1 = rand();
   1.256 +
   1.257 +  wchar_t temp[kBoundaryLength];
   1.258 +  swprintf(temp, kBoundaryLength, L"%s%08X%08X", kBoundaryPrefix, r0, r1);
   1.259 +
   1.260 +  // remove when VC++7.1 is no longer supported
   1.261 +  temp[kBoundaryLength - 1] = L'\0';
   1.262 +
   1.263 +  return wstring(temp);
   1.264 +}
   1.265 +
   1.266 +// static
   1.267 +wstring HTTPUpload::GenerateRequestHeader(const wstring &boundary) {
   1.268 +  wstring header = L"Content-Type: multipart/form-data; boundary=";
   1.269 +  header += boundary;
   1.270 +  return header;
   1.271 +}
   1.272 +
   1.273 +// static
   1.274 +bool HTTPUpload::GenerateRequestBody(const map<wstring, wstring> &parameters,
   1.275 +                                     const wstring &upload_file,
   1.276 +                                     const wstring &file_part_name,
   1.277 +                                     const wstring &boundary,
   1.278 +                                     string *request_body) {
   1.279 +  vector<char> contents;
   1.280 +  if (!GetFileContents(upload_file, &contents)) {
   1.281 +    return false;
   1.282 +  }
   1.283 +
   1.284 +  string boundary_str = WideToUTF8(boundary);
   1.285 +  if (boundary_str.empty()) {
   1.286 +    return false;
   1.287 +  }
   1.288 +
   1.289 +  request_body->clear();
   1.290 +
   1.291 +  // Append each of the parameter pairs as a form-data part
   1.292 +  for (map<wstring, wstring>::const_iterator pos = parameters.begin();
   1.293 +       pos != parameters.end(); ++pos) {
   1.294 +    request_body->append("--" + boundary_str + "\r\n");
   1.295 +    request_body->append("Content-Disposition: form-data; name=\"" +
   1.296 +                         WideToUTF8(pos->first) + "\"\r\n\r\n" +
   1.297 +                         WideToUTF8(pos->second) + "\r\n");
   1.298 +  }
   1.299 +
   1.300 +  // Now append the upload file as a binary (octet-stream) part
   1.301 +  string filename_utf8 = WideToUTF8(upload_file);
   1.302 +  if (filename_utf8.empty()) {
   1.303 +    return false;
   1.304 +  }
   1.305 +
   1.306 +  string file_part_name_utf8 = WideToUTF8(file_part_name);
   1.307 +  if (file_part_name_utf8.empty()) {
   1.308 +    return false;
   1.309 +  }
   1.310 +
   1.311 +  request_body->append("--" + boundary_str + "\r\n");
   1.312 +  request_body->append("Content-Disposition: form-data; "
   1.313 +                       "name=\"" + file_part_name_utf8 + "\"; "
   1.314 +                       "filename=\"" + filename_utf8 + "\"\r\n");
   1.315 +  request_body->append("Content-Type: application/octet-stream\r\n");
   1.316 +  request_body->append("\r\n");
   1.317 +
   1.318 +  if (!contents.empty()) {
   1.319 +      request_body->append(&(contents[0]), contents.size());
   1.320 +  }
   1.321 +  request_body->append("\r\n");
   1.322 +  request_body->append("--" + boundary_str + "--\r\n");
   1.323 +  return true;
   1.324 +}
   1.325 +
   1.326 +// static
   1.327 +bool HTTPUpload::GetFileContents(const wstring &filename,
   1.328 +                                 vector<char> *contents) {
   1.329 +  // The "open" method on pre-MSVC8 ifstream implementations doesn't accept a
   1.330 +  // wchar_t* filename, so use _wfopen directly in that case.  For VC8 and
   1.331 +  // later, _wfopen has been deprecated in favor of _wfopen_s, which does
   1.332 +  // not exist in earlier versions, so let the ifstream open the file itself.
   1.333 +#if _MSC_VER >= 1400  // MSVC 2005/8
   1.334 +  ifstream file;
   1.335 +  file.open(filename.c_str(), ios::binary);
   1.336 +#else  // _MSC_VER >= 1400
   1.337 +  ifstream file(_wfopen(filename.c_str(), L"rb"));
   1.338 +#endif  // _MSC_VER >= 1400
   1.339 +  if (file.is_open()) {
   1.340 +    file.seekg(0, ios::end);
   1.341 +    std::streamoff length = file.tellg();
   1.342 +    contents->resize(length);
   1.343 +    if (length != 0) {
   1.344 +      file.seekg(0, ios::beg);
   1.345 +      file.read(&((*contents)[0]), length);
   1.346 +    }
   1.347 +    file.close();
   1.348 +    return true;
   1.349 +  }
   1.350 +  return false;
   1.351 +}
   1.352 +
   1.353 +// static
   1.354 +wstring HTTPUpload::UTF8ToWide(const string &utf8) {
   1.355 +  if (utf8.length() == 0) {
   1.356 +    return wstring();
   1.357 +  }
   1.358 +
   1.359 +  // compute the length of the buffer we'll need
   1.360 +  int charcount = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, NULL, 0);
   1.361 +
   1.362 +  if (charcount == 0) {
   1.363 +    return wstring();
   1.364 +  }
   1.365 +
   1.366 +  // convert
   1.367 +  wchar_t* buf = new wchar_t[charcount];
   1.368 +  MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, buf, charcount);
   1.369 +  wstring result(buf);
   1.370 +  delete[] buf;
   1.371 +  return result;
   1.372 +}
   1.373 +
   1.374 +// static
   1.375 +string HTTPUpload::WideToUTF8(const wstring &wide) {
   1.376 +  if (wide.length() == 0) {
   1.377 +    return string();
   1.378 +  }
   1.379 +
   1.380 +  // compute the length of the buffer we'll need
   1.381 +  int charcount = WideCharToMultiByte(CP_UTF8, 0, wide.c_str(), -1,
   1.382 +                                      NULL, 0, NULL, NULL);
   1.383 +  if (charcount == 0) {
   1.384 +    return string();
   1.385 +  }
   1.386 +
   1.387 +  // convert
   1.388 +  char *buf = new char[charcount];
   1.389 +  WideCharToMultiByte(CP_UTF8, 0, wide.c_str(), -1, buf, charcount,
   1.390 +                      NULL, NULL);
   1.391 +
   1.392 +  string result(buf);
   1.393 +  delete[] buf;
   1.394 +  return result;
   1.395 +}
   1.396 +
   1.397 +// static
   1.398 +bool HTTPUpload::CheckParameters(const map<wstring, wstring> &parameters) {
   1.399 +  for (map<wstring, wstring>::const_iterator pos = parameters.begin();
   1.400 +       pos != parameters.end(); ++pos) {
   1.401 +    const wstring &str = pos->first;
   1.402 +    if (str.size() == 0) {
   1.403 +      return false;  // disallow empty parameter names
   1.404 +    }
   1.405 +    for (unsigned int i = 0; i < str.size(); ++i) {
   1.406 +      wchar_t c = str[i];
   1.407 +      if (c < 32 || c == '"' || c > 127) {
   1.408 +        return false;
   1.409 +      }
   1.410 +    }
   1.411 +  }
   1.412 +  return true;
   1.413 +}
   1.414 +
   1.415 +}  // namespace google_breakpad

mercurial