Move Windows Symbol Converter to breakpad

- First step, this is just enough to get it generating a msbuild
  project with GYP, which in turn can build the executable.
- Tests need to be redesigned because there isn't an available server.

Change-Id: I45440fd32b3ede29666c127703bcd441f0e4288e
Reviewed-on: https://chromium-review.googlesource.com/c/breakpad/breakpad/+/1661134
Reviewed-by: Ivan Penkov <ivanpe@chromium.org>
diff --git a/src/tools/windows/converter_exe/binary/configure.cmd b/src/tools/windows/converter_exe/binary/configure.cmd
new file mode 100644
index 0000000..39b1d2a
--- /dev/null
+++ b/src/tools/windows/converter_exe/binary/configure.cmd
@@ -0,0 +1,33 @@
+@if "%ECHOON%"=="" @echo off

+SETLOCAL

+

+REM ******************************************************************

+REM Please, make sure to run this in an Elevated Command Prompt.

+REM Usage:

+REM        configure.cmd

+REM ******************************************************************

+

+REM ******************************************************************

+REM Initialize

+REM ******************************************************************

+SET SCRIPT_LOCATION=%~dp0

+

+REM ******************************************************************

+REM Go to script location

+REM ******************************************************************

+pushd %SCRIPT_LOCATION%

+

+REM ******************************************************************

+REM Register msdia120.dll.

+REM ******************************************************************

+SET MSG=Failed to register msdia120.dll.  Make sure to run this in elevated command prompt.

+%systemroot%\SysWoW64\regsvr32.exe /s msdia120.dll & if errorlevel 1 echo %MSG% & goto :fail

+

+:success

+echo Configuration was successful.

+ENDLOCAL

+exit /b 0

+

+:fail

+ENDLOCAL

+exit /b 1

diff --git a/src/tools/windows/converter_exe/binary/missing_symbols_test.txt b/src/tools/windows/converter_exe/binary/missing_symbols_test.txt
new file mode 100644
index 0000000..251b4ec
--- /dev/null
+++ b/src/tools/windows/converter_exe/binary/missing_symbols_test.txt
@@ -0,0 +1,2 @@
+msctf.pdb|6A5BABB8E88644C696530BFE3C90F32F2|6.1.7600.16385|msctf.dll|4A5BDFAA109000

+imm32.pdb|98F27BA5AEE541ECBEE00CD03AD50FEE2|6.1.7600.16385|imm32.dll|4A5BDF402e000

diff --git a/src/tools/windows/converter_exe/binary/sleep.exe b/src/tools/windows/converter_exe/binary/sleep.exe
new file mode 100644
index 0000000..d178e17
--- /dev/null
+++ b/src/tools/windows/converter_exe/binary/sleep.exe
Binary files differ
diff --git a/src/tools/windows/converter_exe/binary/symsrv.yes b/src/tools/windows/converter_exe/binary/symsrv.yes
new file mode 100644
index 0000000..1d01dda
--- /dev/null
+++ b/src/tools/windows/converter_exe/binary/symsrv.yes
@@ -0,0 +1,2 @@
+See breakpad/tools/windows/converter/ms_symbol_server_converter.h for a

+description of this file's function.

diff --git a/src/tools/windows/converter_exe/configure.cmd b/src/tools/windows/converter_exe/configure.cmd
new file mode 100644
index 0000000..39b1d2a
--- /dev/null
+++ b/src/tools/windows/converter_exe/configure.cmd
@@ -0,0 +1,33 @@
+@if "%ECHOON%"=="" @echo off

+SETLOCAL

+

+REM ******************************************************************

+REM Please, make sure to run this in an Elevated Command Prompt.

+REM Usage:

+REM        configure.cmd

+REM ******************************************************************

+

+REM ******************************************************************

+REM Initialize

+REM ******************************************************************

+SET SCRIPT_LOCATION=%~dp0

+

+REM ******************************************************************

+REM Go to script location

+REM ******************************************************************

+pushd %SCRIPT_LOCATION%

+

+REM ******************************************************************

+REM Register msdia120.dll.

+REM ******************************************************************

+SET MSG=Failed to register msdia120.dll.  Make sure to run this in elevated command prompt.

+%systemroot%\SysWoW64\regsvr32.exe /s msdia120.dll & if errorlevel 1 echo %MSG% & goto :fail

+

+:success

+echo Configuration was successful.

+ENDLOCAL

+exit /b 0

+

+:fail

+ENDLOCAL

+exit /b 1

diff --git a/src/tools/windows/converter_exe/converter.cc b/src/tools/windows/converter_exe/converter.cc
new file mode 100644
index 0000000..23216f9
--- /dev/null
+++ b/src/tools/windows/converter_exe/converter.cc
@@ -0,0 +1,807 @@
+// Copyright 2019 Google Inc. All rights reserved.

+//

+// Redistribution and use in source and binary forms, with or without

+// modification, are permitted provided that the following conditions are

+// met:

+//

+//     * Redistributions of source code must retain the above copyright

+// notice, this list of conditions and the following disclaimer.

+//     * Redistributions in binary form must reproduce the above

+// copyright notice, this list of conditions and the following disclaimer

+// in the documentation and/or other materials provided with the

+// distribution.

+//     * Neither the name of Google Inc. nor the names of its

+// contributors may be used to endorse or promote products derived from

+// this software without specific prior written permission.

+//

+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+

+#pragma comment(lib, "winhttp.lib")

+#pragma comment(lib, "wininet.lib")

+#pragma comment(lib, "diaguids.lib")

+#pragma comment(lib, "imagehlp.lib")

+

+#include <cassert>

+#include <cstdio>

+#include <ctime>

+#include <map>

+#include <regex>

+#include <string>

+#include <vector>

+

+#include "tools/windows/converter_exe/escaping.h"

+#include "tools/windows/converter_exe/http_download.h"

+#include "tools/windows/converter_exe/tokenizer.h"

+#include "common/windows/http_upload.h"

+#include "common/windows/string_utils-inl.h"

+#include "tools/windows/converter/ms_symbol_server_converter.h"

+

+using strings::WebSafeBase64Unescape;

+using strings::WebSafeBase64Escape;

+

+namespace {

+

+using std::map;

+using std::string;

+using std::vector;

+using std::wstring;

+using crash::HTTPDownload;

+using crash::Tokenizer;

+using google_breakpad::HTTPUpload;

+using google_breakpad::MissingSymbolInfo;

+using google_breakpad::MSSymbolServerConverter;

+using google_breakpad::WindowsStringUtils;

+

+const char *kMissingStringDelimiters = "|";

+const char *kLocalCachePath = "c:\\symbols";

+const char *kNoExeMSSSServer = "http://msdl.microsoft.com/download/symbols/";

+

+const int kMatchArrSize = 64;

+

+// Windows stdio doesn't do line buffering.  Use this function to flush after

+// writing to stdout and stderr so that a log will be available if the

+// converter crashes.

+static int FprintfFlush(FILE *file, const char *format, ...) {

+  va_list arguments;

+  va_start(arguments, format);

+  int retval = vfprintf(file, format, arguments);

+  va_end(arguments);

+  fflush(file);

+  return retval;

+}

+

+static string CurrentDateAndTime() {

+  const string kUnknownDateAndTime = "????-??-?? ??:??:??";

+

+  time_t current_time;

+  time(&current_time);

+

+  // localtime_s is safer but is only available in MSVC8.  Use localtime

+  // in earlier environments.

+  struct tm *time_pointer;

+#if _MSC_VER >= 1400  // MSVC 2005/8

+  struct tm time_struct;

+  time_pointer = &time_struct;

+  if (localtime_s(time_pointer, &current_time) != 0) {

+    return kUnknownDateAndTime;

+  }

+#else  // _MSC_VER >= 1400

+  time_pointer = localtime(&current_time);

+  if (!time_pointer) {

+    return kUnknownDateAndTime;

+  }

+#endif  // _MSC_VER >= 1400

+

+  char buffer[256];

+  if (!strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", time_pointer)) {

+    return kUnknownDateAndTime;

+  }

+

+  return string(buffer);

+}

+

+// ParseMissingString turns |missing_string| into a MissingSymbolInfo

+// structure.  It returns true on success, and false if no such conversion

+// is possible.

+static bool ParseMissingString(const string &missing_string,

+                               MissingSymbolInfo *missing_info) {

+  assert(missing_info);

+

+  vector<string> tokens;

+  Tokenizer::Tokenize(kMissingStringDelimiters, missing_string, &tokens);

+  if (tokens.size() != 5) {

+    return false;

+  }

+

+  missing_info->debug_file = tokens[0];

+  missing_info->debug_identifier = tokens[1];

+  missing_info->version = tokens[2];

+  missing_info->code_file = tokens[3];

+  missing_info->code_identifier = tokens[4];

+

+  return true;

+}

+

+// StringMapToWStringMap takes each element in a map that associates

+// (narrow) strings to strings and converts the keys and values to wstrings.

+// Returns true on success and false on failure, printing an error message.

+static bool StringMapToWStringMap(const map<string, string> &smap,

+                                  map<wstring, wstring> *wsmap) {

+  assert(wsmap);

+  wsmap->clear();

+

+  for (map<string, string>::const_iterator iterator = smap.begin();

+       iterator != smap.end();

+       ++iterator) {

+    wstring key;

+    if (!WindowsStringUtils::safe_mbstowcs(iterator->first, &key)) {

+      FprintfFlush(stderr,

+                   "StringMapToWStringMap: safe_mbstowcs failed for key %s\n",

+                   iterator->first.c_str());

+      return false;

+    }

+

+    wstring value;

+    if (!WindowsStringUtils::safe_mbstowcs(iterator->second, &value)) {

+      FprintfFlush(stderr, "StringMapToWStringMap: safe_mbstowcs failed "

+                           "for value %s\n",

+                   iterator->second.c_str());

+      return false;

+    }

+

+    wsmap->insert(make_pair(key, value));

+  }

+

+  return true;

+}

+

+// MissingSymbolInfoToParameters turns a MissingSymbolInfo structure into a

+// map of parameters suitable for passing to HTTPDownload or HTTPUpload.

+// Returns true on success and false on failure, printing an error message.

+static bool MissingSymbolInfoToParameters(const MissingSymbolInfo &missing_info,

+                                          map<wstring, wstring> *wparameters) {

+  assert(wparameters);

+

+  map<string, string> parameters;

+  string encoded_param;

+  // Indicate the params are encoded.

+  parameters["encoded"] = "true";  // The string value here does not matter.

+

+  WebSafeBase64Escape(missing_info.code_file, &encoded_param);

+  parameters["code_file"] = encoded_param;

+

+  WebSafeBase64Escape(missing_info.code_identifier, &encoded_param);

+  parameters["code_identifier"] = encoded_param;

+

+  WebSafeBase64Escape(missing_info.debug_file, &encoded_param);

+  parameters["debug_file"] = encoded_param;

+

+  WebSafeBase64Escape(missing_info.debug_identifier, &encoded_param);

+  parameters["debug_identifier"] = encoded_param;

+

+  if (!missing_info.version.empty()) {

+    // The version is optional.

+    WebSafeBase64Escape(missing_info.version, &encoded_param);

+    parameters["version"] = encoded_param;

+  }

+

+  WebSafeBase64Escape("WinSymConv", &encoded_param);

+  parameters["product"] = encoded_param;

+

+  if (!StringMapToWStringMap(parameters, wparameters)) {

+    // StringMapToWStringMap will have printed an error.

+    return false;

+  }

+

+  return true;

+}

+

+// UploadSymbolFile sends |converted_file| as identified by |missing_info|

+// to the symbol server rooted at |upload_symbol_url|.  Returns true on

+// success and false on failure, printing an error message.

+static bool UploadSymbolFile(const wstring &upload_symbol_url,

+                             const MissingSymbolInfo &missing_info,

+                             const string &converted_file) {

+  map<wstring, wstring> parameters;

+  if (!MissingSymbolInfoToParameters(missing_info, &parameters)) {

+    // MissingSymbolInfoToParameters or a callee will have printed an error.

+    return false;

+  }

+

+  wstring converted_file_w;

+

+  if (!WindowsStringUtils::safe_mbstowcs(converted_file, &converted_file_w)) {

+    FprintfFlush(stderr, "UploadSymbolFile: safe_mbstowcs failed for %s\n",

+                 converted_file.c_str());

+    return false;

+  }

+  map<wstring, wstring> files;

+  files[L"symbol_file"] = converted_file_w;

+

+  FprintfFlush(stderr, "Uploading %s\n", converted_file.c_str());

+  if (!HTTPUpload::SendRequest(

+      upload_symbol_url, parameters,

+      files, NULL, NULL, NULL)) {

+    FprintfFlush(stderr, "UploadSymbolFile: HTTPUpload::SendRequest failed "

+                         "for %s %s %s\n",

+                 missing_info.debug_file.c_str(),

+                 missing_info.debug_identifier.c_str(),

+                 missing_info.version.c_str());

+    return false;

+  }

+

+  return true;

+}

+

+// SendFetchFailedPing informs the symbol server based at

+// |fetch_symbol_failure_url| that the symbol file identified by

+// |missing_info| could authoritatively not be located.  Returns

+// true on success and false on failure.

+static bool SendFetchFailedPing(const wstring &fetch_symbol_failure_url,

+                                const MissingSymbolInfo &missing_info) {

+  map<wstring, wstring> parameters;

+  if (!MissingSymbolInfoToParameters(missing_info, &parameters)) {

+    // MissingSymbolInfoToParameters or a callee will have printed an error.

+    return false;

+  }

+

+  string content;

+  if (!HTTPDownload::Download(fetch_symbol_failure_url,

+                              &parameters,

+                              &content,

+                              NULL)) {

+    FprintfFlush(stderr, "SendFetchFailedPing: HTTPDownload::Download failed "

+                         "for %s %s %s\n",

+                 missing_info.debug_file.c_str(),

+                 missing_info.debug_identifier.c_str(),

+                 missing_info.version.c_str());

+    return false;

+  }

+

+  return true;

+}

+

+// Returns true if it's safe to make an external request for the symbol

+// file described in missing_info. It's considered safe to make an

+// external request unless the symbol file's debug_file string matches

+// the given blacklist regular expression.

+// The debug_file name is used from the MissingSymbolInfo struct,

+// matched against the PCRE blacklist_regex.

+static bool SafeToMakeExternalRequest(const MissingSymbolInfo &missing_info,

+                                      std::regex blacklist_regex) {

+  string file_name = missing_info.debug_file;

+  if (std::regex_match(file_name, blacklist_regex)) {

+    FprintfFlush(stderr, "Not safe to make external request for file %s\n",

+                 file_name.c_str());

+    return false;

+  }

+

+  return true;

+}

+

+// Converter options derived from command line parameters.

+struct ConverterOptions {

+  ConverterOptions()

+      : report_fetch_failures(true),

+        blacklist_regex(nullptr) {

+  }

+

+  ~ConverterOptions() {

+  }

+

+  // Names of MS Symbol Supplier Servers that are internal to Google, and may

+  // have symbols for any request.

+  vector<string> full_internal_msss_servers;

+

+  // Names of MS Symbol Supplier Servers that are internal to Google, and

+  // shouldn't be checked for symbols for any .exe files.

+  vector<string> full_external_msss_servers;

+

+  // Names of MS Symbol Supplier Servers that are external to Google, and may

+  // have symbols for any request.

+  vector<string> no_exe_internal_msss_servers;

+

+  // Names of MS Symbol Supplier Servers that are external to Google, and

+  // shouldn't be checked for symbols for any .exe files.

+  vector<string> no_exe_external_msss_servers;

+

+  // Temporary local storage for symbols.

+  string local_cache_path;

+

+  // URL for uploading symbols.

+  wstring upload_symbols_url;

+

+  // URL to fetch list of missing symbols.

+  wstring missing_symbols_url;

+

+  // URL to report symbol fetch failure.

+  wstring fetch_symbol_failure_url;

+

+  // Are symbol fetch failures reported.

+  bool report_fetch_failures;

+

+  // File containing the list of missing symbols.  Fetch failures are not

+  // reported if such file is provided.

+  string missing_symbols_file;

+

+  // Regex used to blacklist files to prevent external symbol requests.

+  // Owned and cleaned up by this struct.

+  std::regex blacklist_regex;

+

+ private:

+  // DISABLE_COPY_AND_ASSIGN

+  ConverterOptions(const ConverterOptions&);

+  ConverterOptions& operator=(const ConverterOptions&);

+};

+

+// ConverMissingSymbolFile takes a single MissingSymbolInfo structure and

+// attempts to locate it from the symbol servers provided in the

+// |options.*_msss_servers| arguments.  "Full" servers are those that will be

+// queried for all symbol files; "No-EXE" servers will only be queried for

+// modules whose missing symbol data indicates are not main program executables.

+// Results will be sent to the |options.upload_symbols_url| on success or

+// |options.fetch_symbol_failure_url| on failure, and the local cache will be

+// stored at |options.local_cache_path|.  Because nothing can be done even in

+// the event of a failure, this function returns no value, although it

+// may result in error messages being printed.

+static void ConvertMissingSymbolFile(const MissingSymbolInfo &missing_info,

+                                     const ConverterOptions &options) {

+  string time_string = CurrentDateAndTime();

+  FprintfFlush(stdout, "converter: %s: attempting %s %s %s\n",

+               time_string.c_str(),

+               missing_info.debug_file.c_str(),

+               missing_info.debug_identifier.c_str(),

+               missing_info.version.c_str());

+

+  // The first lookup is always to internal symbol servers.

+  // Always ask the symbol servers identified as "full."

+  vector<string> msss_servers = options.full_internal_msss_servers;

+

+  // If the file is not an .exe file, also ask an additional set of symbol

+  // servers, such as Microsoft's public symbol server.

+  bool is_exe = false;

+

+  if (missing_info.code_file.length() >= 4) {

+    string code_extension =

+        missing_info.code_file.substr(missing_info.code_file.size() - 4);

+

+    // Firefox is a special case: .dll-only servers should be consulted for

+    // its symbols.  This enables us to get its symbols from Mozilla's

+    // symbol server when crashes occur in Google extension code hosted by a

+    // Firefox process.

+    if (_stricmp(code_extension.c_str(), ".exe") == 0 &&

+        _stricmp(missing_info.code_file.c_str(), "firefox.exe") != 0) {

+      is_exe = true;

+    }

+  }

+

+  if (!is_exe) {

+    msss_servers.insert(msss_servers.end(),

+                        options.no_exe_internal_msss_servers.begin(),

+                        options.no_exe_internal_msss_servers.end());

+  }

+

+  // If there are any suitable internal symbol servers, make a request.

+  MSSymbolServerConverter::LocateResult located =

+      MSSymbolServerConverter::LOCATE_FAILURE;

+  string converted_file;

+  if (msss_servers.size() > 0) {

+    // Attempt to fetch the symbol file and convert it.

+    FprintfFlush(stderr, "Making internal request for %s (%s)\n",

+                   missing_info.debug_file.c_str(),

+                   missing_info.debug_identifier.c_str());

+    MSSymbolServerConverter converter(options.local_cache_path, msss_servers);

+    located = converter.LocateAndConvertSymbolFile(missing_info,

+                                                   false,  // keep_symbol_file

+                                                   false,  // keep_pe_file

+                                                   &converted_file,

+                                                   NULL,   // symbol_file

+                                                   NULL);  // pe_file

+    switch (located) {

+      case MSSymbolServerConverter::LOCATE_SUCCESS:

+        FprintfFlush(stderr, "LocateResult = LOCATE_SUCCESS\n");

+        // Upload it.  Don't bother checking the return value.  If this

+        // succeeds, it should disappear from the missing symbol list.

+        // If it fails, something will print an error message indicating

+        // the cause of the failure, and the item will remain on the

+        // missing symbol list.

+        UploadSymbolFile(options.upload_symbols_url, missing_info,

+                         converted_file);

+        remove(converted_file.c_str());

+

+        // Note: this does leave some directories behind that could be

+        // cleaned up.  The directories inside options.local_cache_path for

+        // debug_file/debug_identifier can be removed at this point.

+        break;

+

+      case MSSymbolServerConverter::LOCATE_NOT_FOUND:

+        FprintfFlush(stderr, "LocateResult = LOCATE_NOT_FOUND\n");

+        // The symbol file definitively did not exist. Fall through,

+        // so we can attempt an external query if it's safe to do so.

+        break;

+

+      case MSSymbolServerConverter::LOCATE_RETRY:

+        FprintfFlush(stderr, "LocateResult = LOCATE_RETRY\n");

+        // Fall through in case we should make an external request.

+        // If not, or if an external request fails in the same way,

+        // we'll leave the entry in the symbol file list and

+        // try again on a future pass.  Print a message so that there's

+        // a record.

+        break;

+

+      case MSSymbolServerConverter::LOCATE_FAILURE:

+        FprintfFlush(stderr, "LocateResult = LOCATE_FAILURE\n");

+        // LocateAndConvertSymbolFile printed an error message.

+        break;

+

+      default:

+        FprintfFlush(

+            stderr,

+            "FATAL: Unexpected return value '%d' from "

+            "LocateAndConvertSymbolFile()\n",

+            located);

+        assert(0);

+        break;

+    }

+  } else {

+    // No suitable internal symbol servers.  This is fine because the converter

+    // is mainly used for downloading and converting of external symbols.

+  }

+

+  // Make a request to an external server if the internal request didn't

+  // succeed, and it's safe to do so.

+  if (located != MSSymbolServerConverter::LOCATE_SUCCESS &&

+      SafeToMakeExternalRequest(missing_info, options.blacklist_regex)) {

+    msss_servers = options.full_external_msss_servers;

+    if (!is_exe) {

+      msss_servers.insert(msss_servers.end(),

+                          options.no_exe_external_msss_servers.begin(),

+                          options.no_exe_external_msss_servers.end());

+    }

+    if (msss_servers.size() > 0) {

+      FprintfFlush(stderr, "Making external request for %s (%s)\n",

+                   missing_info.debug_file.c_str(),

+                   missing_info.debug_identifier.c_str());

+      MSSymbolServerConverter external_converter(options.local_cache_path,

+                                                 msss_servers);

+      located = external_converter.LocateAndConvertSymbolFile(

+          missing_info,

+          false,  // keep_symbol_file

+          false,  // keep_pe_file

+          &converted_file,

+          NULL,   // symbol_file

+          NULL);  // pe_file

+    } else {

+      FprintfFlush(stderr, "ERROR: No suitable external symbol servers.\n");

+    }

+  }

+

+  // Final handling for this symbol file is based on the result from the

+  // external request (if performed above), or on the result from the

+  // previous internal lookup.

+  switch (located) {

+    case MSSymbolServerConverter::LOCATE_SUCCESS:

+      FprintfFlush(stderr, "LocateResult = LOCATE_SUCCESS\n");

+      // Upload it.  Don't bother checking the return value.  If this

+      // succeeds, it should disappear from the missing symbol list.

+      // If it fails, something will print an error message indicating

+      // the cause of the failure, and the item will remain on the

+      // missing symbol list.

+      UploadSymbolFile(options.upload_symbols_url, missing_info,

+                       converted_file);

+      remove(converted_file.c_str());

+

+      // Note: this does leave some directories behind that could be

+      // cleaned up.  The directories inside options.local_cache_path for

+      // debug_file/debug_identifier can be removed at this point.

+      break;

+

+    case MSSymbolServerConverter::LOCATE_NOT_FOUND:

+      // The symbol file definitively didn't exist.  Inform the server.

+      // If this fails, something will print an error message indicating

+      // the cause of the failure, but there's really nothing more to

+      // do.  If this succeeds, the entry should be removed from the

+      // missing symbols list.

+      if (!options.report_fetch_failures) {

+        FprintfFlush(stderr, "SendFetchFailedPing skipped\n");

+      } else if (SendFetchFailedPing(options.fetch_symbol_failure_url,

+                                     missing_info)) {

+        FprintfFlush(stderr, "SendFetchFailedPing succeeded\n");

+      } else {

+        FprintfFlush(stderr, "SendFetchFailedPing failed\n");

+      }

+      break;

+

+    case MSSymbolServerConverter::LOCATE_RETRY:

+      FprintfFlush(stderr, "LocateResult = LOCATE_RETRY\n");

+      // Nothing to do but leave the entry in the symbol file list and

+      // try again on a future pass.  Print a message so that there's

+      // a record.

+      FprintfFlush(stderr, "ConvertMissingSymbolFile: deferring retry "

+                           "for %s %s %s\n",

+                   missing_info.debug_file.c_str(),

+                   missing_info.debug_identifier.c_str(),

+                   missing_info.version.c_str());

+      break;

+

+    case MSSymbolServerConverter::LOCATE_FAILURE:

+      FprintfFlush(stderr, "LocateResult = LOCATE_FAILURE\n");

+      // LocateAndConvertSymbolFile printed an error message.

+

+      // This is due to a bad debug file name, so fetch failed.

+      if (!options.report_fetch_failures) {

+        FprintfFlush(stderr, "SendFetchFailedPing skipped\n");

+      } else if (SendFetchFailedPing(options.fetch_symbol_failure_url,

+                                     missing_info)) {

+        FprintfFlush(stderr, "SendFetchFailedPing succeeded\n");

+      } else {

+        FprintfFlush(stderr, "SendFetchFailedPing failed\n");

+      }

+      break;

+

+    default:

+      FprintfFlush(

+          stderr,

+          "FATAL: Unexpected return value '%d' from "

+          "LocateAndConvertSymbolFile()\n",

+          located);

+      assert(0);

+      break;

+  }

+}

+

+

+// Reads the contents of file |file_name| and populates |contents|.

+// Returns true on success.

+static bool ReadFile(string file_name, string *contents) {

+  char buffer[1024 * 8];

+  FILE *fp = fopen(file_name.c_str(), "rt");

+  if (!fp) {

+    return false;

+  }

+  contents->clear();

+  while (fgets(buffer, sizeof(buffer), fp) != NULL) {

+    contents->append(buffer);

+  }

+  fclose(fp);

+  return true;

+}

+

+// ConvertMissingSymbolsList obtains a missing symbol list from

+// |options.missing_symbols_url| or |options.missing_symbols_file| and calls

+// ConvertMissingSymbolFile for each missing symbol file in the list.

+static bool ConvertMissingSymbolsList(const ConverterOptions &options) {

+  // Set param to indicate requesting for encoded response.

+  map<wstring, wstring> parameters;

+  parameters[L"product"] = L"WinSymConv";

+  parameters[L"encoded"] = L"true";

+  // Get the missing symbol list.

+  string missing_symbol_list;

+  if (!options.missing_symbols_file.empty()) {

+    if (!ReadFile(options.missing_symbols_file, &missing_symbol_list)) {

+      return false;

+    }

+  } else if (!HTTPDownload::Download(options.missing_symbols_url, &parameters,

+                                     &missing_symbol_list, NULL)) {

+    return false;

+  }

+

+  // Tokenize the content into a vector.

+  vector<string> missing_symbol_lines;

+  Tokenizer::Tokenize("\n", missing_symbol_list, &missing_symbol_lines);

+

+  FprintfFlush(stderr, "Found %d missing symbol files in list.\n",

+               missing_symbol_lines.size() - 1);  // last line is empty.

+  int convert_attempts = 0;

+  for (vector<string>::const_iterator iterator = missing_symbol_lines.begin();

+       iterator != missing_symbol_lines.end();

+       ++iterator) {

+    // Decode symbol line.

+    const string &encoded_line = *iterator;

+    // Skip lines that are blank.

+    if (encoded_line.empty()) {

+      continue;

+    }

+

+    string line;

+    if (!WebSafeBase64Unescape(encoded_line, &line)) {

+      // If decoding fails, assume the line is not encoded.

+      // This is helpful when the program connects to a debug server without

+      // encoding.

+      line = encoded_line;

+    }

+

+    FprintfFlush(stderr, "\nLine: %s\n", line.c_str());

+

+    // Turn each element into a MissingSymbolInfo structure.

+    MissingSymbolInfo missing_info;

+    if (!ParseMissingString(line, &missing_info)) {

+      FprintfFlush(stderr, "ConvertMissingSymbols: ParseMissingString failed "

+                           "for %s from %ws\n",

+                   line.c_str(), options.missing_symbols_url.c_str());

+      continue;

+    }

+

+    ++convert_attempts;

+    ConvertMissingSymbolFile(missing_info, options);

+  }

+

+  // Say something reassuring, since ConvertMissingSymbolFile was never called

+  // and therefore never reported any progress.

+  if (convert_attempts == 0) {

+    string current_time = CurrentDateAndTime();

+    FprintfFlush(stdout, "converter: %s: nothing to convert\n",

+                 current_time.c_str());

+  }

+

+  return true;

+}

+

+// usage prints the usage message.  It returns 1 as a convenience, to be used

+// as a return value from main.

+static int usage(const char *program_name) {

+  FprintfFlush(stderr,

+      "usage: %s [options]\n"

+      "    -f  <full_msss_server>     MS servers to ask for all symbols\n"

+      "    -n  <no_exe_msss_server>   same, but prevent asking for EXEs\n"

+      "    -l  <local_cache_path>     Temporary local storage for symbols\n"

+      "    -s  <upload_url>           URL for uploading symbols\n"

+      "    -m  <missing_symbols_url>  URL to fetch list of missing symbols\n"

+      "    -mf <missing_symbols_file> File containing the list of missing\n"

+      "                               symbols.  Fetch failures are not\n"

+      "                               reported if such file is provided.\n"

+      "    -t  <fetch_failure_url>    URL to report symbol fetch failure\n"

+      "    -b  <regex>                Regex used to blacklist files to\n"

+      "                               prevent external symbol requests\n"

+      " Note that any server specified by -f or -n that starts with \\filer\n"

+      " will be treated as internal, and all others as external.\n",

+      program_name);

+

+  return 1;

+}

+

+// "Internal" servers consist only of those whose names start with

+// the literal string "\\filer\".

+static bool IsInternalServer(const string &server_name) {

+  if (server_name.find("\\\\filer\\") == 0) {

+    return true;

+  }

+  return false;

+}

+

+// Adds a server with the given name to the list of internal or external

+// servers, as appropriate.

+static void AddServer(const string &server_name,

+                      vector<string> *internal_servers,

+                      vector<string> *external_servers) {

+  if (IsInternalServer(server_name)) {

+    internal_servers->push_back(server_name);

+  } else {

+    external_servers->push_back(server_name);

+  }

+}

+

+}  // namespace

+

+int main(int argc, char **argv) {

+  string time_string = CurrentDateAndTime();

+  FprintfFlush(stdout, "converter: %s: starting\n", time_string.c_str());

+

+  ConverterOptions options;

+  options.report_fetch_failures = true;

+

+  // All arguments are paired.

+  if (argc % 2 != 1) {

+    return usage(argv[0]);

+  }

+  string blacklist_regex_str;

+  bool have_any_msss_servers = false;

+  for (int argi = 1; argi < argc; argi += 2) {

+    string option = argv[argi];

+    string value = argv[argi + 1];

+

+    if (option == "-f") {

+      AddServer(value, &options.full_internal_msss_servers,

+                &options.full_external_msss_servers);

+      have_any_msss_servers = true;

+    } else if (option == "-n") {

+      AddServer(value, &options.no_exe_internal_msss_servers,

+                &options.no_exe_external_msss_servers);

+      have_any_msss_servers = true;

+    } else if (option == "-l") {

+      if (!options.local_cache_path.empty()) {

+        return usage(argv[0]);

+      }

+      options.local_cache_path = value;

+    } else if (option == "-s") {

+      if (!WindowsStringUtils::safe_mbstowcs(value,

+                                             &options.upload_symbols_url)) {

+        FprintfFlush(stderr, "main: safe_mbstowcs failed for %s\n",

+                     value.c_str());

+        return 1;

+      }

+    } else if (option == "-m") {

+      if (!WindowsStringUtils::safe_mbstowcs(value,

+                                             &options.missing_symbols_url)) {

+        FprintfFlush(stderr, "main: safe_mbstowcs failed for %s\n",

+                     value.c_str());

+        return 1;

+      }

+    } else if (option == "-mf") {

+      options.missing_symbols_file = value;

+      printf("Getting the list of missing symbols from a file.  Fetch failures"

+             " will not be reported.\n");

+      options.report_fetch_failures = false;

+    } else if (option == "-t") {

+      if (!WindowsStringUtils::safe_mbstowcs(

+          value,

+          &options.fetch_symbol_failure_url)) {

+        FprintfFlush(stderr, "main: safe_mbstowcs failed for %s\n",

+                     value.c_str());

+        return 1;

+      }

+    } else if (option == "-b") {

+      blacklist_regex_str = value;

+    } else {

+      return usage(argv[0]);

+    }

+  }

+

+  if (blacklist_regex_str.empty()) {

+    FprintfFlush(stderr, "No blacklist specified.\n");

+    return usage(argv[0]);

+  }

+

+  // Compile the blacklist regular expression for later use.

+  options.blacklist_regex = std::regex(blacklist_regex_str.c_str(),

+      std::regex_constants::icase);

+

+  // Set the defaults.  If the user specified any MSSS servers, don't use

+  // any default.

+  if (!have_any_msss_servers) {

+    AddServer(kNoExeMSSSServer, &options.no_exe_internal_msss_servers,

+        &options.no_exe_external_msss_servers);

+  }

+

+  if (options.local_cache_path.empty()) {

+    options.local_cache_path = kLocalCachePath;

+  }

+

+  if (options.upload_symbols_url.empty()) {

+    FprintfFlush(stderr, "No upload symbols URL specified.\n");

+    return usage(argv[0]);

+  }

+  if (options.missing_symbols_url.empty()) {

+    FprintfFlush(stderr, "No missing symbols URL specified.\n");

+    return usage(argv[0]);

+  }

+  if (options.fetch_symbol_failure_url.empty()) {

+    FprintfFlush(stderr, "No fetch symbol failure URL specified.\n");

+    return usage(argv[0]);

+  }

+

+  FprintfFlush(stdout,

+               "# of Symbol Servers (int/ext): %d/%d full, %d/%d no_exe\n",

+               options.full_internal_msss_servers.size(),

+               options.full_external_msss_servers.size(),

+               options.no_exe_internal_msss_servers.size(),

+               options.no_exe_external_msss_servers.size());

+

+  if (!ConvertMissingSymbolsList(options)) {

+    return 1;

+  }

+

+  time_string = CurrentDateAndTime();

+  FprintfFlush(stdout, "converter: %s: finished\n", time_string.c_str());

+  return 0;

+}

diff --git a/src/tools/windows/converter_exe/converter.gyp b/src/tools/windows/converter_exe/converter.gyp
new file mode 100644
index 0000000..fe443d1
--- /dev/null
+++ b/src/tools/windows/converter_exe/converter.gyp
@@ -0,0 +1,57 @@
+# Copyright 2013 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+{
+  'includes': [
+    '../../../build/common.gypi',
+  ],
+  'targets': [
+    {
+      'target_name': 'converter_exe',
+      'type': 'executable',
+      'sources': [
+        'converter.cc',
+        'escaping.cc',
+        'escaping.h',
+        'http_client.h',
+        'http_download.cc',
+        'http_download.h',
+        'tokenizer.cc',
+        'tokenizer.h',
+        'winhttp_client.cc',
+        'winhttp_client.h',
+        'wininet_client.cc',
+        'wininet_client.h',
+      ],
+      'dependencies': [
+        '../../../common/windows/common_windows.gyp:common_windows_lib',
+        '../converter/ms_symbol_server_converter.gyp:ms_symbol_server_converter',
+      ],
+    },
+  ],
+}
diff --git a/src/tools/windows/converter_exe/escaping.cc b/src/tools/windows/converter_exe/escaping.cc
new file mode 100644
index 0000000..e26bbeb
--- /dev/null
+++ b/src/tools/windows/converter_exe/escaping.cc
@@ -0,0 +1,757 @@
+// Copyright 2019 Google Inc. All rights reserved.

+//

+// Redistribution and use in source and binary forms, with or without

+// modification, are permitted provided that the following conditions are

+// met:

+//

+//     * Redistributions of source code must retain the above copyright

+// notice, this list of conditions and the following disclaimer.

+//     * Redistributions in binary form must reproduce the above

+// copyright notice, this list of conditions and the following disclaimer

+// in the documentation and/or other materials provided with the

+// distribution.

+//     * Neither the name of Google Inc. nor the names of its

+// contributors may be used to endorse or promote products derived from

+// this software without specific prior written permission.

+//

+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+

+#include "tools/windows/converter_exe/escaping.h"

+

+#include <assert.h>

+

+#define kApb kAsciiPropertyBits

+

+const unsigned char kAsciiPropertyBits[256] = {

+  0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,  // 0x00

+  0x40, 0x68, 0x48, 0x48, 0x48, 0x48, 0x40, 0x40,

+  0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,  // 0x10

+  0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,

+  0x28, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,  // 0x20

+  0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,

+  0x84, 0x84, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,

+  0x10, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x05,  // 0x40

+  0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,

+  0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,  // 0x50

+  0x05, 0x05, 0x05, 0x10, 0x10, 0x10, 0x10, 0x10,

+  0x10, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x05,  // 0x60

+  0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,

+  0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,  // 0x70

+  0x05, 0x05, 0x05, 0x10, 0x10, 0x10, 0x10, 0x40,

+};

+

+// Use !! to suppress the warning C4800 of forcing 'int' to 'bool'.

+static inline bool ascii_isspace(unsigned char c) { return !!(kApb[c] & 0x08); }

+

+///////////////////////////////////

+// scoped_array

+///////////////////////////////////

+// scoped_array<C> is like scoped_ptr<C>, except that the caller must allocate

+// with new [] and the destructor deletes objects with delete [].

+//

+// As with scoped_ptr<C>, a scoped_array<C> either points to an object

+// or is NULL.  A scoped_array<C> owns the object that it points to.

+// scoped_array<T> is thread-compatible, and once you index into it,

+// the returned objects have only the threadsafety guarantees of T.

+//

+// Size: sizeof(scoped_array<C>) == sizeof(C*)

+template <class C>

+class scoped_array {

+ public:

+

+  // The element type

+  typedef C element_type;

+

+  // Constructor.  Defaults to intializing with NULL.

+  // There is no way to create an uninitialized scoped_array.

+  // The input parameter must be allocated with new [].

+  explicit scoped_array(C* p = NULL) : array_(p) { }

+

+  // Destructor.  If there is a C object, delete it.

+  // We don't need to test ptr_ == NULL because C++ does that for us.

+  ~scoped_array() {

+    enum { type_must_be_complete = sizeof(C) };

+    delete[] array_;

+  }

+

+  // Reset.  Deletes the current owned object, if any.

+  // Then takes ownership of a new object, if given.

+  // this->reset(this->get()) works.

+  void reset(C* p = NULL) {

+    if (p != array_) {

+      enum { type_must_be_complete = sizeof(C) };

+      delete[] array_;

+      array_ = p;

+    }

+  }

+

+  // Get one element of the current object.

+  // Will assert() if there is no current object, or index i is negative.

+  C& operator[](std::ptrdiff_t i) const {

+    assert(i >= 0);

+    assert(array_ != NULL);

+    return array_[i];

+  }

+

+  // Get a pointer to the zeroth element of the current object.

+  // If there is no current object, return NULL.

+  C* get() const {

+    return array_;

+  }

+

+  // Comparison operators.

+  // These return whether a scoped_array and a raw pointer refer to

+  // the same array, not just to two different but equal arrays.

+  bool operator==(const C* p) const { return array_ == p; }

+  bool operator!=(const C* p) const { return array_ != p; }

+

+  // Swap two scoped arrays.

+  void swap(scoped_array& p2) {

+    C* tmp = array_;

+    array_ = p2.array_;

+    p2.array_ = tmp;

+  }

+

+  // Release an array.

+  // The return value is the current pointer held by this object.

+  // If this object holds a NULL pointer, the return value is NULL.

+  // After this operation, this object will hold a NULL pointer,

+  // and will not own the object any more.

+  C* release() {

+    C* retVal = array_;

+    array_ = NULL;

+    return retVal;

+  }

+

+ private:

+  C* array_;

+

+  // Forbid comparison of different scoped_array types.

+  template <class C2> bool operator==(scoped_array<C2> const& p2) const;

+  template <class C2> bool operator!=(scoped_array<C2> const& p2) const;

+

+  // Disallow evil constructors

+  scoped_array(const scoped_array&);

+  void operator=(const scoped_array&);

+};

+

+

+///////////////////////////////////

+// Escape methods

+///////////////////////////////////

+

+namespace strings {

+

+// Return a mutable char* pointing to a string's internal buffer,

+// which may not be null-terminated. Writing through this pointer will

+// modify the string.

+//

+// string_as_array(&str)[i] is valid for 0 <= i < str.size() until the

+// next call to a string method that invalidates iterators.

+//

+// As of 2006-04, there is no standard-blessed way of getting a

+// mutable reference to a string's internal buffer. However, issue 530

+// (http://www.open-std.org/JTC1/SC22/WG21/docs/lwg-active.html#530)

+// proposes this as the method. According to Matt Austern, this should

+// already work on all current implementations.

+inline char* string_as_array(string* str) {

+  // DO NOT USE const_cast<char*>(str->data())! See the unittest for why.

+  return str->empty() ? NULL : &*str->begin();

+}

+

+int CalculateBase64EscapedLen(int input_len, bool do_padding) {

+  // these formulae were copied from comments that used to go with the base64

+  // encoding functions

+  int intermediate_result = 8 * input_len + 5;

+  assert(intermediate_result > 0);     // make sure we didn't overflow

+  int len = intermediate_result / 6;

+  if (do_padding) len = ((len + 3) / 4) * 4;

+  return len;

+}

+

+// Base64Escape does padding, so this calculation includes padding.

+int CalculateBase64EscapedLen(int input_len) {

+  return CalculateBase64EscapedLen(input_len, true);

+}

+

+// ----------------------------------------------------------------------

+// int Base64Unescape() - base64 decoder

+// int Base64Escape() - base64 encoder

+// int WebSafeBase64Unescape() - Google's variation of base64 decoder

+// int WebSafeBase64Escape() - Google's variation of base64 encoder

+//

+// Check out

+// http://www.cis.ohio-state.edu/htbin/rfc/rfc2045.html for formal

+// description, but what we care about is that...

+//   Take the encoded stuff in groups of 4 characters and turn each

+//   character into a code 0 to 63 thus:

+//           A-Z map to 0 to 25

+//           a-z map to 26 to 51

+//           0-9 map to 52 to 61

+//           +(- for WebSafe) maps to 62

+//           /(_ for WebSafe) maps to 63

+//   There will be four numbers, all less than 64 which can be represented

+//   by a 6 digit binary number (aaaaaa, bbbbbb, cccccc, dddddd respectively).

+//   Arrange the 6 digit binary numbers into three bytes as such:

+//   aaaaaabb bbbbcccc ccdddddd

+//   Equals signs (one or two) are used at the end of the encoded block to

+//   indicate that the text was not an integer multiple of three bytes long.

+// ----------------------------------------------------------------------

+

+int Base64UnescapeInternal(const char *src, int szsrc,

+                           char *dest, int szdest,

+                           const signed char* unbase64) {

+  static const char kPad64 = '=';

+

+  int decode = 0;

+  int destidx = 0;

+  int state = 0;

+  unsigned int ch = 0;

+  unsigned int temp = 0;

+

+  // The GET_INPUT macro gets the next input character, skipping

+  // over any whitespace, and stopping when we reach the end of the

+  // string or when we read any non-data character.  The arguments are

+  // an arbitrary identifier (used as a label for goto) and the number

+  // of data bytes that must remain in the input to avoid aborting the

+  // loop.

+#define GET_INPUT(label, remain)                 \

+  label:                                         \

+    --szsrc;                                     \

+    ch = *src++;                                 \

+    decode = unbase64[ch];                       \

+    if (decode < 0) {                            \

+      if (ascii_isspace((char)ch) && szsrc >= remain)  \

+        goto label;                              \

+      state = 4 - remain;                        \

+      break;                                     \

+    }

+

+  // if dest is null, we're just checking to see if it's legal input

+  // rather than producing output.  (I suspect this could just be done

+  // with a regexp...).  We duplicate the loop so this test can be

+  // outside it instead of in every iteration.

+

+  if (dest) {

+    // This loop consumes 4 input bytes and produces 3 output bytes

+    // per iteration.  We can't know at the start that there is enough

+    // data left in the string for a full iteration, so the loop may

+    // break out in the middle; if so 'state' will be set to the

+    // number of input bytes read.

+

+    while (szsrc >= 4)  {

+      // We'll start by optimistically assuming that the next four

+      // bytes of the string (src[0..3]) are four good data bytes

+      // (that is, no nulls, whitespace, padding chars, or illegal

+      // chars).  We need to test src[0..2] for nulls individually

+      // before constructing temp to preserve the property that we

+      // never read past a null in the string (no matter how long

+      // szsrc claims the string is).

+

+      if (!src[0] || !src[1] || !src[2] ||

+          (temp = ((unbase64[src[0]] << 18) |

+                   (unbase64[src[1]] << 12) |

+                   (unbase64[src[2]] << 6) |

+                   (unbase64[src[3]]))) & 0x80000000) {

+        // Iff any of those four characters was bad (null, illegal,

+        // whitespace, padding), then temp's high bit will be set

+        // (because unbase64[] is -1 for all bad characters).

+        //

+        // We'll back up and resort to the slower decoder, which knows

+        // how to handle those cases.

+

+        GET_INPUT(first, 4);

+        temp = decode;

+        GET_INPUT(second, 3);

+        temp = (temp << 6) | decode;

+        GET_INPUT(third, 2);

+        temp = (temp << 6) | decode;

+        GET_INPUT(fourth, 1);

+        temp = (temp << 6) | decode;

+      } else {

+        // We really did have four good data bytes, so advance four

+        // characters in the string.

+

+        szsrc -= 4;

+        src += 4;

+        decode = -1;

+        ch = '\0';

+      }

+

+      // temp has 24 bits of input, so write that out as three bytes.

+

+      if (destidx+3 > szdest) return -1;

+      dest[destidx+2] = (char)temp;

+      temp >>= 8;

+      dest[destidx+1] = (char)temp;

+      temp >>= 8;

+      dest[destidx] = (char)temp;

+      destidx += 3;

+    }

+  } else {

+    while (szsrc >= 4)  {

+      if (!src[0] || !src[1] || !src[2] ||

+          (temp = ((unbase64[src[0]] << 18) |

+                   (unbase64[src[1]] << 12) |

+                   (unbase64[src[2]] << 6) |

+                   (unbase64[src[3]]))) & 0x80000000) {

+        GET_INPUT(first_no_dest, 4);

+        GET_INPUT(second_no_dest, 3);

+        GET_INPUT(third_no_dest, 2);

+        GET_INPUT(fourth_no_dest, 1);

+      } else {

+        szsrc -= 4;

+        src += 4;

+        decode = -1;

+        ch = '\0';

+      }

+      destidx += 3;

+    }

+  }

+

+#undef GET_INPUT

+

+  // if the loop terminated because we read a bad character, return

+  // now.

+  if (decode < 0 && ch != '\0' && ch != kPad64 && !ascii_isspace((char)ch))

+    return -1;

+

+  if (ch == kPad64) {

+    // if we stopped by hitting an '=', un-read that character -- we'll

+    // look at it again when we count to check for the proper number of

+    // equals signs at the end.

+    ++szsrc;

+    --src;

+  } else {

+    // This loop consumes 1 input byte per iteration.  It's used to

+    // clean up the 0-3 input bytes remaining when the first, faster

+    // loop finishes.  'temp' contains the data from 'state' input

+    // characters read by the first loop.

+    while (szsrc > 0)  {

+      --szsrc;

+      ch = *src++;

+      decode = unbase64[ch];

+      if (decode < 0) {

+        if (ascii_isspace((char)ch)) {

+          continue;

+        } else if (ch == '\0') {

+          break;

+        } else if (ch == kPad64) {

+          // back up one character; we'll read it again when we check

+          // for the correct number of equals signs at the end.

+          ++szsrc;

+          --src;

+          break;

+        } else {

+          return -1;

+        }

+      }

+

+      // Each input character gives us six bits of output.

+      temp = (temp << 6) | decode;

+      ++state;

+      if (state == 4) {

+        // If we've accumulated 24 bits of output, write that out as

+        // three bytes.

+        if (dest) {

+          if (destidx+3 > szdest) return -1;

+          dest[destidx+2] = (char)temp;

+          temp >>= 8;

+          dest[destidx+1] = (char)temp;

+          temp >>= 8;

+          dest[destidx] = (char)temp;

+        }

+        destidx += 3;

+        state = 0;

+        temp = 0;

+      }

+    }

+  }

+

+  // Process the leftover data contained in 'temp' at the end of the input.

+  int expected_equals = 0;

+  switch (state) {

+    case 0:

+      // Nothing left over; output is a multiple of 3 bytes.

+      break;

+

+    case 1:

+      // Bad input; we have 6 bits left over.

+      return -1;

+

+    case 2:

+      // Produce one more output byte from the 12 input bits we have left.

+      if (dest) {

+        if (destidx+1 > szdest) return -1;

+        temp >>= 4;

+        dest[destidx] = (char)temp;

+      }

+      ++destidx;

+      expected_equals = 2;

+      break;

+

+    case 3:

+      // Produce two more output bytes from the 18 input bits we have left.

+      if (dest) {

+        if (destidx+2 > szdest) return -1;

+        temp >>= 2;

+        dest[destidx+1] = (char)temp;

+        temp >>= 8;

+        dest[destidx] = (char)temp;

+      }

+      destidx += 2;

+      expected_equals = 1;

+      break;

+

+    default:

+      // state should have no other values at this point.

+      fprintf(stdout, "This can't happen; base64 decoder state = %d", state);

+  }

+

+  // The remainder of the string should be all whitespace, mixed with

+  // exactly 0 equals signs, or exactly 'expected_equals' equals

+  // signs.  (Always accepting 0 equals signs is a google extension

+  // not covered in the RFC.)

+

+  int equals = 0;

+  while (szsrc > 0 && *src) {

+    if (*src == kPad64)

+      ++equals;

+    else if (!ascii_isspace(*src))

+      return -1;

+    --szsrc;

+    ++src;

+  }

+

+  return (equals == 0 || equals == expected_equals) ? destidx : -1;

+}

+

+int Base64Unescape(const char *src, int szsrc, char *dest, int szdest) {

+  static const signed char UnBase64[] = {

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      62/*+*/, -1,      -1,      -1,      63/*/ */,

+     52/*0*/, 53/*1*/, 54/*2*/, 55/*3*/, 56/*4*/, 57/*5*/, 58/*6*/, 59/*7*/,

+     60/*8*/, 61/*9*/, -1,      -1,      -1,      -1,      -1,      -1,

+     -1,       0/*A*/,  1/*B*/,  2/*C*/,  3/*D*/,  4/*E*/,  5/*F*/,  6/*G*/,

+      7/*H*/,  8/*I*/,  9/*J*/, 10/*K*/, 11/*L*/, 12/*M*/, 13/*N*/, 14/*O*/,

+     15/*P*/, 16/*Q*/, 17/*R*/, 18/*S*/, 19/*T*/, 20/*U*/, 21/*V*/, 22/*W*/,

+     23/*X*/, 24/*Y*/, 25/*Z*/, -1,      -1,      -1,      -1,      -1,

+     -1,      26/*a*/, 27/*b*/, 28/*c*/, 29/*d*/, 30/*e*/, 31/*f*/, 32/*g*/,

+     33/*h*/, 34/*i*/, 35/*j*/, 36/*k*/, 37/*l*/, 38/*m*/, 39/*n*/, 40/*o*/,

+     41/*p*/, 42/*q*/, 43/*r*/, 44/*s*/, 45/*t*/, 46/*u*/, 47/*v*/, 48/*w*/,

+     49/*x*/, 50/*y*/, 51/*z*/, -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1

+  };

+  // The above array was generated by the following code

+  // #include <sys/time.h>

+  // #include <stdlib.h>

+  // #include <string.h>

+  // main()

+  // {

+  //   static const char Base64[] =

+  //     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

+  //   char *pos;

+  //   int idx, i, j;

+  //   printf("    ");

+  //   for (i = 0; i < 255; i += 8) {

+  //     for (j = i; j < i + 8; j++) {

+  //       pos = strchr(Base64, j);

+  //       if ((pos == NULL) || (j == 0))

+  //         idx = -1;

+  //       else

+  //         idx = pos - Base64;

+  //       if (idx == -1)

+  //         printf(" %2d,     ", idx);

+  //       else

+  //         printf(" %2d/*%c*/,", idx, j);

+  //     }

+  //     printf("\n    ");

+  //   }

+  // }

+

+  return Base64UnescapeInternal(src, szsrc, dest, szdest, UnBase64);

+}

+

+bool Base64Unescape(const char *src, int slen, string* dest) {

+  // Determine the size of the output string.  Base64 encodes every 3 bytes into

+  // 4 characters.  any leftover chars are added directly for good measure.

+  // This is documented in the base64 RFC: http://www.ietf.org/rfc/rfc3548.txt

+  const int dest_len = 3 * (slen / 4) + (slen % 4);

+

+  dest->resize(dest_len);

+

+  // We are getting the destination buffer by getting the beginning of the

+  // string and converting it into a char *.

+  const int len = Base64Unescape(src, slen,

+                                 string_as_array(dest), dest->size());

+  if (len < 0) {

+    return false;

+  }

+

+  // could be shorter if there was padding

+  assert(len <= dest_len);

+  dest->resize(len);

+

+  return true;

+}

+

+// Base64Escape

+//

+// NOTE: We have to use an unsigned type for src because code built

+// in the the /google tree treats characters as signed unless

+// otherwised specified.

+//

+// TODO(who?): Move this function to use the char* type for "src"

+int Base64EscapeInternal(const unsigned char *src, int szsrc,

+                         char *dest, int szdest, const char *base64,

+                         bool do_padding) {

+  static const char kPad64 = '=';

+

+  if (szsrc <= 0) return 0;

+

+  char *cur_dest = dest;

+  const unsigned char *cur_src = src;

+

+  // Three bytes of data encodes to four characters of cyphertext.

+  // So we can pump through three-byte chunks atomically.

+  while (szsrc > 2) { /* keep going until we have less than 24 bits */

+    if ((szdest -= 4) < 0) return 0;

+    cur_dest[0] = base64[cur_src[0] >> 2];

+    cur_dest[1] = base64[((cur_src[0] & 0x03) << 4) + (cur_src[1] >> 4)];

+    cur_dest[2] = base64[((cur_src[1] & 0x0f) << 2) + (cur_src[2] >> 6)];

+    cur_dest[3] = base64[cur_src[2] & 0x3f];

+

+    cur_dest += 4;

+    cur_src += 3;

+    szsrc -= 3;

+  }

+

+  /* now deal with the tail (<=2 bytes) */

+  switch (szsrc) {

+    case 0:

+      // Nothing left; nothing more to do.

+      break;

+    case 1:

+      // One byte left: this encodes to two characters, and (optionally)

+      // two pad characters to round out the four-character cypherblock.

+      if ((szdest -= 2) < 0) return 0;

+      cur_dest[0] = base64[cur_src[0] >> 2];

+      cur_dest[1] = base64[(cur_src[0] & 0x03) << 4];

+      cur_dest += 2;

+      if (do_padding) {

+        if ((szdest -= 2) < 0) return 0;

+        cur_dest[0] = kPad64;

+        cur_dest[1] = kPad64;

+        cur_dest += 2;

+      }

+      break;

+    case 2:

+      // Two bytes left: this encodes to three characters, and (optionally)

+      // one pad character to round out the four-character cypherblock.

+      if ((szdest -= 3) < 0) return 0;

+      cur_dest[0] = base64[cur_src[0] >> 2];

+      cur_dest[1] = base64[((cur_src[0] & 0x03) << 4) + (cur_src[1] >> 4)];

+      cur_dest[2] = base64[(cur_src[1] & 0x0f) << 2];

+      cur_dest += 3;

+      if (do_padding) {

+        if ((szdest -= 1) < 0) return 0;

+        cur_dest[0] = kPad64;

+        cur_dest += 1;

+      }

+      break;

+    default:

+      // Should not be reached: blocks of 3 bytes are handled

+      // in the while loop before this switch statement.

+      fprintf(stderr, "Logic problem? szsrc = %d",  szsrc);

+      assert(false);

+      break;

+  }

+  return (cur_dest - dest);

+}

+

+static const char kBase64Chars[] =

+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

+

+static const char kWebSafeBase64Chars[] =

+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";

+

+int Base64Escape(const unsigned char *src, int szsrc, char *dest, int szdest) {

+  return Base64EscapeInternal(src, szsrc, dest, szdest, kBase64Chars, true);

+}

+

+void Base64Escape(const unsigned char *src, int szsrc,

+                  string* dest, bool do_padding) {

+  const int max_escaped_size =

+    CalculateBase64EscapedLen(szsrc, do_padding);

+  dest->clear();

+  dest->resize(max_escaped_size + 1, '\0');

+  const int escaped_len = Base64EscapeInternal(src, szsrc,

+                                               &*dest->begin(), dest->size(),

+                                               kBase64Chars,

+                                               do_padding);

+  assert(max_escaped_size <= escaped_len);

+  dest->resize(escaped_len);

+}

+

+void Base64Escape(const string& src, string* dest) {

+  Base64Escape(reinterpret_cast<const unsigned char*>(src.c_str()),

+               src.size(), dest, true);

+}

+

+////////////////////////////////////////////////////

+// WebSafe methods

+////////////////////////////////////////////////////

+

+int WebSafeBase64Unescape(const char *src, int szsrc, char *dest, int szdest) {

+  static const signed char UnBase64[] = {

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      62/*-*/, -1,      -1,

+     52/*0*/, 53/*1*/, 54/*2*/, 55/*3*/, 56/*4*/, 57/*5*/, 58/*6*/, 59/*7*/,

+     60/*8*/, 61/*9*/, -1,      -1,      -1,      -1,      -1,      -1,

+     -1,       0/*A*/,  1/*B*/,  2/*C*/,  3/*D*/,  4/*E*/,  5/*F*/,  6/*G*/,

+      7/*H*/,  8/*I*/,  9/*J*/, 10/*K*/, 11/*L*/, 12/*M*/, 13/*N*/, 14/*O*/,

+     15/*P*/, 16/*Q*/, 17/*R*/, 18/*S*/, 19/*T*/, 20/*U*/, 21/*V*/, 22/*W*/,

+     23/*X*/, 24/*Y*/, 25/*Z*/, -1,      -1,      -1,      -1,      63/*_*/,

+     -1,      26/*a*/, 27/*b*/, 28/*c*/, 29/*d*/, 30/*e*/, 31/*f*/, 32/*g*/,

+     33/*h*/, 34/*i*/, 35/*j*/, 36/*k*/, 37/*l*/, 38/*m*/, 39/*n*/, 40/*o*/,

+     41/*p*/, 42/*q*/, 43/*r*/, 44/*s*/, 45/*t*/, 46/*u*/, 47/*v*/, 48/*w*/,

+     49/*x*/, 50/*y*/, 51/*z*/, -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,

+     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1

+  };

+  // The above array was generated by the following code

+  // #include <sys/time.h>

+  // #include <stdlib.h>

+  // #include <string.h>

+  // main()

+  // {

+  //   static const char Base64[] =

+  //     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";

+  //   char *pos;

+  //   int idx, i, j;

+  //   printf("    ");

+  //   for (i = 0; i < 255; i += 8) {

+  //     for (j = i; j < i + 8; j++) {

+  //       pos = strchr(Base64, j);

+  //       if ((pos == NULL) || (j == 0))

+  //         idx = -1;

+  //       else

+  //         idx = pos - Base64;

+  //       if (idx == -1)

+  //         printf(" %2d,     ", idx);

+  //       else

+  //         printf(" %2d/*%c*/,", idx, j);

+  //     }

+  //     printf("\n    ");

+  //   }

+  // }

+

+  return Base64UnescapeInternal(src, szsrc, dest, szdest, UnBase64);

+}

+

+bool WebSafeBase64Unescape(const char *src, int slen, string* dest) {

+  int dest_len = 3 * (slen / 4) + (slen % 4);

+  dest->clear();

+  dest->resize(dest_len);

+  int len = WebSafeBase64Unescape(src, slen, &*dest->begin(), dest->size());

+  if (len < 0) {

+    dest->clear();

+    return false;

+  }

+  // could be shorter if there was padding

+  assert(len <= dest_len);

+  dest->resize(len);

+  return true;

+}

+

+bool WebSafeBase64Unescape(const string& src, string* dest) {

+  return WebSafeBase64Unescape(src.data(), src.size(), dest);

+}

+

+int WebSafeBase64Escape(const unsigned char *src, int szsrc, char *dest,

+                        int szdest, bool do_padding) {

+  return Base64EscapeInternal(src, szsrc, dest, szdest,

+                              kWebSafeBase64Chars, do_padding);

+}

+

+void WebSafeBase64Escape(const unsigned char *src, int szsrc,

+                         string *dest, bool do_padding) {

+  const int max_escaped_size =

+    CalculateBase64EscapedLen(szsrc, do_padding);

+  dest->clear();

+  dest->resize(max_escaped_size + 1, '\0');

+  const int escaped_len = Base64EscapeInternal(src, szsrc,

+                                               &*dest->begin(), dest->size(),

+                                               kWebSafeBase64Chars,

+                                               do_padding);

+  assert(max_escaped_size <= escaped_len);

+  dest->resize(escaped_len);

+}

+

+void WebSafeBase64EscapeInternal(const string& src,

+                                 string* dest,

+                                 bool do_padding) {

+  int encoded_len = CalculateBase64EscapedLen(src.size());

+  scoped_array<char> buf(new char[encoded_len]);

+  int len = WebSafeBase64Escape(reinterpret_cast<const unsigned char*>(src.c_str()),

+                                src.size(), buf.get(),

+                                encoded_len, do_padding);

+  dest->assign(buf.get(), len);

+}

+

+void WebSafeBase64Escape(const string& src, string* dest) {

+  WebSafeBase64EscapeInternal(src, dest, false);

+}

+

+void WebSafeBase64EscapeWithPadding(const string& src, string* dest) {

+  WebSafeBase64EscapeInternal(src, dest, true);

+}

+

+}  // namespace strings

diff --git a/src/tools/windows/converter_exe/escaping.h b/src/tools/windows/converter_exe/escaping.h
new file mode 100644
index 0000000..c8aa90b
--- /dev/null
+++ b/src/tools/windows/converter_exe/escaping.h
@@ -0,0 +1,99 @@
+// Copyright 2019 Google Inc. All rights reserved.

+//

+// Redistribution and use in source and binary forms, with or without

+// modification, are permitted provided that the following conditions are

+// met:

+//

+//     * Redistributions of source code must retain the above copyright

+// notice, this list of conditions and the following disclaimer.

+//     * Redistributions in binary form must reproduce the above

+// copyright notice, this list of conditions and the following disclaimer

+// in the documentation and/or other materials provided with the

+// distribution.

+//     * Neither the name of Google Inc. nor the names of its

+// contributors may be used to endorse or promote products derived from

+// this software without specific prior written permission.

+//

+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+//

+// Base64 escaping methods to encode/decode strings.

+

+#ifndef TOOLS_WINDOWS_CONVERTER_EXE_ESCAPING_H_

+#define TOOLS_WINDOWS_CONVERTER_EXE_ESCAPING_H_

+

+#include <string>

+

+namespace strings {

+

+using std::string;

+

+// ----------------------------------------------------------------------

+// Base64Escape()

+// WebSafeBase64Escape()

+//    Encode "src" to "dest" using base64 encoding.

+//    src is not null terminated, instead specify len.

+//    'dest' should have at least CalculateBase64EscapedLen() length.

+//    RETURNS the length of dest.

+//    The WebSafe variation use '-' instead of '+' and '_' instead of '/'

+//    so that we can place the out in the URL or cookies without having

+//    to escape them.  It also has an extra parameter "do_padding",

+//    which when set to false will prevent padding with "=".

+// ----------------------------------------------------------------------

+void Base64Escape(const string& src, string* dest);

+int Base64Escape(const unsigned char* src, int slen, char* dest, int szdest);

+// Encode src into dest with padding.

+void Base64Escape(const unsigned char* src, int szsrc,

+                  string* dest, bool do_padding);

+

+int WebSafeBase64Escape(const unsigned char* src, int slen, char* dest,

+                        int szdest, bool do_padding);

+// Encode src into dest web-safely without padding.

+void WebSafeBase64Escape(const string& src, string* dest);

+// Encode src into dest web-safely with padding.

+void WebSafeBase64EscapeWithPadding(const string& src, string* dest);

+void WebSafeBase64Escape(const unsigned char* src, int szsrc,

+                         string* dest, bool do_padding);

+

+// ----------------------------------------------------------------------

+// Base64Unescape()

+// WebSafeBase64Unescape()

+//    Copies "src" to "dest", where src is in base64 and is written to its

+//    ASCII equivalents. src is not null terminated, instead specify len.

+//    I recommend that slen<szdest, but we honor szdest anyway.

+//    RETURNS the length of dest, or -1 if src contains invalid chars.

+//    The WebSafe variation use '-' instead of '+' and '_' instead of '/'.

+//    The variations that store into a string clear the string first, and

+//    return false (with dest empty) if src contains invalid chars; for

+//    these versions src and dest must be different strings.

+// ----------------------------------------------------------------------

+int Base64Unescape(const char* src, int slen, char* dest, int szdest);

+bool Base64Unescape(const char* src, int slen, string* dest);

+inline bool Base64Unescape(const string& src, string* dest) {

+  return Base64Unescape(src.data(), src.size(), dest);

+}

+

+

+int WebSafeBase64Unescape(const char* src, int slen, char* dest, int szdest);

+bool WebSafeBase64Unescape(const char* src, int slen, string* dest);

+bool WebSafeBase64Unescape(const string& src, string* dest);

+

+// Return the length to use for the output buffer given to the base64 escape

+// routines. Make sure to use the same value for do_padding in both.

+// This function may return incorrect results if given input_len values that

+// are extremely high, which should happen rarely.

+int CalculateBase64EscapedLen(int input_len, bool do_padding);

+// Use this version when calling Base64Escape without a do_padding arg.

+int CalculateBase64EscapedLen(int input_len);

+}  // namespace strings

+

+#endif  // TOOLS_WINDOWS_CONVERTER_EXE_ESCAPING_H_

diff --git a/src/tools/windows/converter_exe/http_client.h b/src/tools/windows/converter_exe/http_client.h
new file mode 100644
index 0000000..3e7aa8a
--- /dev/null
+++ b/src/tools/windows/converter_exe/http_client.h
@@ -0,0 +1,96 @@
+// Copyright 2019 Google Inc. All rights reserved.

+//

+// Redistribution and use in source and binary forms, with or without

+// modification, are permitted provided that the following conditions are

+// met:

+//

+//     * Redistributions of source code must retain the above copyright

+// notice, this list of conditions and the following disclaimer.

+//     * Redistributions in binary form must reproduce the above

+// copyright notice, this list of conditions and the following disclaimer

+// in the documentation and/or other materials provided with the

+// distribution.

+//     * Neither the name of Google Inc. nor the names of its

+// contributors may be used to endorse or promote products derived from

+// this software without specific prior written permission.

+//

+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+

+#ifndef TOOLS_CRASH_CONVERTER_WINDOWS_HTTP_CLIENT_H_

+#define TOOLS_CRASH_CONVERTER_WINDOWS_HTTP_CLIENT_H_

+

+#include <tchar.h>

+#include <windows.h>

+#include <vector>

+

+typedef void* HttpHandle;

+

+namespace crash {

+

+// HttpClient provides an abstract layer for HTTP APIs. The actual

+// implementation can be based on either WinHttp or WinInet.

+class HttpClient {

+ public:

+  enum AccessType {

+    ACCESS_TYPE_PRECONFIG,

+    ACCESS_TYPE_DIRECT,

+    ACCESS_TYPE_PROXY,

+  };

+

+  virtual ~HttpClient() {}

+

+  virtual bool CrackUrl(const TCHAR* url,

+                        DWORD flags,

+                        TCHAR* scheme,

+                        size_t scheme_buffer_length,

+                        TCHAR* host,

+                        size_t host_buffer_length,

+                        TCHAR* uri,

+                        size_t uri_buffer_length,

+                        int* port) const = 0;

+  virtual bool Open(const TCHAR* user_agent,

+                    DWORD access_type,

+                    const TCHAR* proxy_name,

+                    const TCHAR* proxy_bypass,

+                    HttpHandle* session_handle) const = 0;

+  virtual bool Connect(HttpHandle session_handle,

+                       const TCHAR* server,

+                       int port,

+                       HttpHandle* connection_handle) const = 0;

+  virtual bool OpenRequest(HttpHandle connection_handle,

+                           const TCHAR* verb,

+                           const TCHAR* uri,

+                           const TCHAR* version,

+                           const TCHAR* referrer,

+                           bool is_secure,

+                           HttpHandle* request_handle) const = 0;

+  virtual bool SendRequest(HttpHandle request_handle,

+                           const TCHAR* headers,

+                           DWORD headers_length) const = 0;

+  virtual bool ReceiveResponse(HttpHandle request_handle) const = 0;

+  virtual bool GetHttpStatusCode(HttpHandle request_handle,

+                                 int* status_code) const = 0;

+  virtual bool GetContentLength(HttpHandle request_handle,

+                                DWORD* content_length) const = 0;

+  virtual bool ReadData(HttpHandle request_handle,

+                        void* buffer,

+                        DWORD buffer_length,

+                        DWORD* bytes_read) const = 0;

+  virtual bool Close(HttpHandle handle) const = 0;

+

+  static const DWORD kUnknownContentLength = (DWORD)-1;

+};

+

+}  // namespace crash

+

+#endif  // TOOLS_CRASH_CONVERTER_WINDOWS_HTTP_CLIENT_H_

diff --git a/src/tools/windows/converter_exe/http_download.cc b/src/tools/windows/converter_exe/http_download.cc
new file mode 100644
index 0000000..b037662
--- /dev/null
+++ b/src/tools/windows/converter_exe/http_download.cc
@@ -0,0 +1,326 @@
+// Copyright 2019 Google Inc. All rights reserved.

+//

+// Redistribution and use in source and binary forms, with or without

+// modification, are permitted provided that the following conditions are

+// met:

+//

+//     * Redistributions of source code must retain the above copyright

+// notice, this list of conditions and the following disclaimer.

+//     * Redistributions in binary form must reproduce the above

+// copyright notice, this list of conditions and the following disclaimer

+// in the documentation and/or other materials provided with the

+// distribution.

+//     * Neither the name of Google Inc. nor the names of its

+// contributors may be used to endorse or promote products derived from

+// this software without specific prior written permission.

+//

+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+

+#include <assert.h>

+#include <stdio.h>

+#include <Windows.h>

+#include <WinInet.h>

+

+#include <vector>

+

+#include "tools/windows/converter_exe/http_download.h"

+#include "tools/windows/converter_exe/winhttp_client.h"

+#include "tools/windows/converter_exe/wininet_client.h"

+

+namespace crash {

+static const std::vector<char>::size_type kVectorChunkSize = 4096;  // 4 KB

+

+using std::vector;

+

+// Class that atuo closes the contained HttpHandle when the object

+// goes out of scope.

+class AutoHttpHandle {

+ public:

+  AutoHttpHandle() : handle_(NULL) {}

+  explicit AutoHttpHandle(HttpHandle handle) : handle_(handle) {}

+  ~AutoHttpHandle() {

+    if (handle_) {

+      InternetCloseHandle(handle_);

+    }

+  }

+

+  HttpHandle get() { return handle_; }

+  HttpHandle* get_handle_addr () { return &handle_; }

+

+ private:

+  HttpHandle handle_;

+};

+

+// Template class for auto releasing the contained pointer when

+// the object goes out of scope.

+template<typename T>

+class AutoPtr {

+ public:

+  explicit AutoPtr(T* ptr) : ptr_(ptr) {}

+  ~AutoPtr() {

+    if (ptr_) {

+      delete ptr_;

+    }

+  }

+

+  T* get() { return ptr_; }

+  T* operator -> () { return ptr_; }

+

+ private:

+  T* ptr_;

+};

+

+// CheckParameters ensures that the parameters in |parameters| are safe for

+// use in an HTTP URL.  Returns true if they are, false if unsafe characters

+// are present.

+static bool CheckParameters(const map<wstring, wstring> *parameters) {

+  for (map<wstring, wstring>::const_iterator iterator = parameters->begin();

+       iterator != parameters->end();

+       ++iterator) {

+    const wstring &key = iterator->first;

+    if (key.empty()) {

+      // Disallow empty parameter names.

+      return false;

+    }

+    for (unsigned int i = 0; i < key.size(); ++i) {

+      wchar_t c = key[i];

+      if (c < 32 || c == '"' || c == '?' || c == '&' || c > 127) {

+        return false;

+      }

+    }

+

+    const wstring &value = iterator->second;

+    for (unsigned int i = 0; i < value.size(); ++i) {

+      wchar_t c = value[i];

+      if (c < 32 || c == '"' || c == '?' || c == '&' || c > 127) {

+        return false;

+      }

+    }

+  }

+

+  return true;

+}

+

+HttpClient* HTTPDownload::CreateHttpClient(const wchar_t* url) {

+  const TCHAR* kHttpApiPolicyEnvironmentVariable = TEXT("USE_WINHTTP");

+  TCHAR buffer[2] = {0};

+  HttpClient* http_client = NULL;

+

+  if (::GetEnvironmentVariable(kHttpApiPolicyEnvironmentVariable,

+                               buffer,

+                               sizeof(buffer)/sizeof(buffer[0])) > 0) {

+    fprintf(stdout,

+            "Environment variable [%ws] is set, use WinHttp\n",

+            kHttpApiPolicyEnvironmentVariable);

+    http_client = CreateWinHttpClient(url);

+    if (http_client == NULL) {

+      fprintf(stderr, "WinHttpClient not created, Is the protocol HTTPS? "

+                      "Fall back to WinInet API.\n");

+    }

+  } else {

+    fprintf(stderr,

+            "Environment variable [%ws] is NOT set, use WinInet API\n",

+            kHttpApiPolicyEnvironmentVariable);

+  }

+

+  if (http_client == NULL) {

+    return CreateWinInetClient(url);

+  }

+

+  return http_client;

+}

+

+// static

+bool HTTPDownload::Download(const wstring &url,

+                            const map<wstring, wstring> *parameters,

+                            string *content, int *status_code) {

+  assert(content);

+  AutoPtr<HttpClient> http_client(CreateHttpClient(url.c_str()));

+

+  if (!http_client.get()) {

+    fprintf(stderr, "Failed to create any http client.\n");

+    return false;

+  }

+

+  if (status_code) {

+    *status_code = 0;

+  }

+

+  wchar_t scheme[16] = {0};

+  wchar_t host[256] = {0};

+  wchar_t path[256] = {0};

+  int port = 0;

+  if (!http_client->CrackUrl(url.c_str(),

+                             0,

+                             scheme,

+                             sizeof(scheme)/sizeof(scheme[0]),

+                             host,

+                             sizeof(host)/sizeof(host[0]),

+                             path,

+                             sizeof(path)/sizeof(path[0]),

+                             &port)) {

+    fprintf(stderr,

+            "HTTPDownload::Download: InternetCrackUrl: error %d for %ws\n",

+            GetLastError(), url.c_str());

+    return false;

+  }

+

+  bool secure = false;

+  if (_wcsicmp(scheme, L"https") == 0) {

+    secure = true;

+  } else if (wcscmp(scheme, L"http") != 0) {

+    fprintf(stderr,

+            "HTTPDownload::Download: scheme must be http or https for %ws\n",

+            url.c_str());

+    return false;

+  }

+

+  AutoHttpHandle internet;

+  if (!http_client->Open(NULL,  // user agent

+                         HttpClient::ACCESS_TYPE_PRECONFIG,

+                         NULL,  // proxy name

+                         NULL,  // proxy bypass

+                         internet.get_handle_addr())) {

+    fprintf(stderr,

+            "HTTPDownload::Download: Open: error %d for %ws\n",

+            GetLastError(), url.c_str());

+    return false;

+  }

+

+  AutoHttpHandle connection;

+  if (!http_client->Connect(internet.get(),

+                            host,

+                            port,

+                            connection.get_handle_addr())) {

+    fprintf(stderr,

+            "HTTPDownload::Download: InternetConnect: error %d for %ws\n",

+            GetLastError(), url.c_str());

+    return false;

+  }

+

+  wstring request_string = path;

+  if (parameters) {

+    // TODO(mmentovai): escape bad characters in parameters instead of

+    // forbidding them.

+    if (!CheckParameters(parameters)) {

+      fprintf(stderr,

+              "HTTPDownload::Download: invalid characters in parameters\n");

+      return false;

+    }

+

+    bool added_parameter = false;

+    for (map<wstring, wstring>::const_iterator iterator = parameters->begin();

+         iterator != parameters->end();

+         ++iterator) {

+      request_string.append(added_parameter ? L"&" : L"?");

+      request_string.append(iterator->first);

+      request_string.append(L"=");

+      request_string.append(iterator->second);

+      added_parameter = true;

+    }

+  }

+

+  AutoHttpHandle request;

+  if (!http_client->OpenRequest(connection.get(),

+                                L"GET",

+                                request_string.c_str(),

+                                NULL,    // version

+                                NULL,    // referer

+                                secure,

+                                request.get_handle_addr())) {

+    fprintf(stderr,

+            "HttpClient::OpenRequest: error %d for %ws, request: %ws\n",

+            GetLastError(), url.c_str(), request_string.c_str());

+    return false;

+  }

+

+  if (!http_client->SendRequest(request.get(), NULL, 0)) {

+    fprintf(stderr,

+            "HttpClient::SendRequest: error %d for %ws\n",

+            GetLastError(), url.c_str());

+    return false;

+  }

+

+  if (!http_client->ReceiveResponse(request.get())) {

+    fprintf(stderr,

+            "HttpClient::ReceiveResponse: error %d for %ws\n",

+            GetLastError(), url.c_str());

+    return false;

+  }

+

+  int http_status = 0;

+  if (!http_client->GetHttpStatusCode(request.get(), &http_status)) {

+    fprintf(stderr,

+            "HttpClient::GetHttpStatusCode: error %d for %ws\n",

+            GetLastError(), url.c_str());

+    return false;

+  }

+  if (http_status != 200) {

+    fprintf(stderr,

+            "HTTPDownload::Download: HTTP status code %d for %ws\n",

+            http_status, url.c_str());

+    return false;

+  }

+

+  DWORD content_length = 0;

+  vector<char>::size_type buffer_size = 0;

+  http_client->GetContentLength(request.get(), &content_length);

+  if (content_length == HttpClient::kUnknownContentLength) {

+    buffer_size = kVectorChunkSize;

+  } else {

+    buffer_size = content_length;

+  }

+

+  if (content_length != 0) {

+    vector<char> response_buffer = vector<char>(buffer_size+1);

+    DWORD size_read;

+    DWORD total_read = 0;

+    bool read_result;

+    do {

+      if (content_length == HttpClient::kUnknownContentLength

+          && buffer_size == total_read) {

+        // The content length wasn't specified in the response header, so we

+        // have to keep growing the buffer until we're done reading.

+        buffer_size += kVectorChunkSize;

+        response_buffer.resize(buffer_size);

+      }

+      read_result = !!http_client->ReadData(

+          request.get(),

+          &response_buffer[total_read],

+          static_cast<DWORD>(buffer_size) - total_read,

+          &size_read);

+      total_read += size_read;

+    } while (read_result && (size_read != 0));

+

+    if (!read_result) {

+      fprintf(stderr,

+              "HttpClient::ReadData: error %d for %ws\n",

+              GetLastError(),

+              url.c_str());

+      return false;

+    } else if (size_read != 0) {

+      fprintf(stderr,

+              "HttpClient::ReadData: error %d/%d for %ws\n",

+              total_read,

+              content_length,

+              url.c_str());

+      return false;

+    }

+    content->assign(&response_buffer[0], total_read);

+  } else {

+    content->clear();

+  }

+  return true;

+}

+

+}  // namespace crash

diff --git a/src/tools/windows/converter_exe/http_download.h b/src/tools/windows/converter_exe/http_download.h
new file mode 100644
index 0000000..2d705d5
--- /dev/null
+++ b/src/tools/windows/converter_exe/http_download.h
@@ -0,0 +1,62 @@
+// Copyright 2019 Google Inc. All rights reserved.

+//

+// Redistribution and use in source and binary forms, with or without

+// modification, are permitted provided that the following conditions are

+// met:

+//

+//     * Redistributions of source code must retain the above copyright

+// notice, this list of conditions and the following disclaimer.

+//     * Redistributions in binary form must reproduce the above

+// copyright notice, this list of conditions and the following disclaimer

+// in the documentation and/or other materials provided with the

+// distribution.

+//     * Neither the name of Google Inc. nor the names of its

+// contributors may be used to endorse or promote products derived from

+// this software without specific prior written permission.

+//

+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+

+#ifndef TOOLS_WINDOWS_CONVERTER_EXE_HTTP_DOWNLOAD_H_

+#define TOOLS_WINDOWS_CONVERTER_EXE_HTTP_DOWNLOAD_H_

+

+#include <map>

+#include <string>

+#include "tools/windows/converter_exe/winhttp_client.h"

+

+namespace crash {

+

+using std::map;

+using std::string;

+using std::wstring;

+

+class HTTPDownload {

+ public:

+  // Retrieves the resource located at |url|, a http or https URL, via WinInet.

+  // The request is fetched with GET request; the optional |parameters| are

+  // appended to the URL.  Returns true on success, placing the content of the

+  // retrieved resource in |content|.  Returns false on failure.  HTTP status

+  // codes other than 200 cause Download to return false.  If |status_code| is

+  // supplied, it will be set to the value of the HTTP status code, if an HTTP

+  // transaction occurs.  If Download fails before a transaction can occur,

+  // |status_code| will be set to 0.  Any failures will result in messages

+  // being printed to stderr.

+  static bool Download(const wstring &url,

+                       const map<wstring, wstring> *parameters,

+                       string *content, int *status_code);

+ private:

+  static HttpClient* CreateHttpClient(const wchar_t*);

+};

+

+}  // namespace crash

+

+#endif  // TOOLS_WINDOWS_CONVERTER_EXE_HTTP_DOWNLOAD_H_

diff --git a/src/tools/windows/converter_exe/missing_symbols_test.txt b/src/tools/windows/converter_exe/missing_symbols_test.txt
new file mode 100644
index 0000000..251b4ec
--- /dev/null
+++ b/src/tools/windows/converter_exe/missing_symbols_test.txt
@@ -0,0 +1,2 @@
+msctf.pdb|6A5BABB8E88644C696530BFE3C90F32F2|6.1.7600.16385|msctf.dll|4A5BDFAA109000

+imm32.pdb|98F27BA5AEE541ECBEE00CD03AD50FEE2|6.1.7600.16385|imm32.dll|4A5BDF402e000

diff --git a/src/tools/windows/converter_exe/symsrv.yes b/src/tools/windows/converter_exe/symsrv.yes
new file mode 100644
index 0000000..1d01dda
--- /dev/null
+++ b/src/tools/windows/converter_exe/symsrv.yes
@@ -0,0 +1,2 @@
+See breakpad/tools/windows/converter/ms_symbol_server_converter.h for a

+description of this file's function.

diff --git a/src/tools/windows/converter_exe/tokenizer.cc b/src/tools/windows/converter_exe/tokenizer.cc
new file mode 100644
index 0000000..992694c
--- /dev/null
+++ b/src/tools/windows/converter_exe/tokenizer.cc
@@ -0,0 +1,61 @@
+// Copyright 2019 Google Inc. All rights reserved.

+//

+// Redistribution and use in source and binary forms, with or without

+// modification, are permitted provided that the following conditions are

+// met:

+//

+//     * Redistributions of source code must retain the above copyright

+// notice, this list of conditions and the following disclaimer.

+//     * Redistributions in binary form must reproduce the above

+// copyright notice, this list of conditions and the following disclaimer

+// in the documentation and/or other materials provided with the

+// distribution.

+//     * Neither the name of Google Inc. nor the names of its

+// contributors may be used to endorse or promote products derived from

+// this software without specific prior written permission.

+//

+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+

+#include <assert.h>

+

+#include "tools/windows/converter_exe/tokenizer.h"

+

+namespace crash {

+

+// static

+void Tokenizer::Tokenize(const string &delimiters, const string &input,

+                        vector<string> *output) {

+  assert(output);

+  output->clear();

+

+  string::size_type position = 0;  // Where to begin looking for a delimiter

+  string::size_type new_position;  // Position of found delimiter

+  string token;

+

+  while ((new_position = input.find_first_of(delimiters, position)) !=

+         string::npos) {

+    token = input.substr(position, new_position - position);

+    output->push_back(token);

+

+    // Next time, begin looking right after this delimiter.

+    position = new_position + 1;

+  }

+

+  // There are no more delimiters in the string.  Take everything from the

+  // final delimiter up to the end of the string as a token.  This may be

+  // an empty string.

+  token = input.substr(position);

+  output->push_back(token);

+}

+

+}  // namespace crash

diff --git a/src/tools/windows/converter_exe/tokenizer.h b/src/tools/windows/converter_exe/tokenizer.h
new file mode 100644
index 0000000..f4bbcfd
--- /dev/null
+++ b/src/tools/windows/converter_exe/tokenizer.h
@@ -0,0 +1,51 @@
+// Copyright 2019 Google Inc. All rights reserved.

+//

+// Redistribution and use in source and binary forms, with or without

+// modification, are permitted provided that the following conditions are

+// met:

+//

+//     * Redistributions of source code must retain the above copyright

+// notice, this list of conditions and the following disclaimer.

+//     * Redistributions in binary form must reproduce the above

+// copyright notice, this list of conditions and the following disclaimer

+// in the documentation and/or other materials provided with the

+// distribution.

+//     * Neither the name of Google Inc. nor the names of its

+// contributors may be used to endorse or promote products derived from

+// this software without specific prior written permission.

+//

+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+

+#ifndef TOOLS_WINDOWS_CONVERTER_EXE_TOKENIZER_H_

+#define TOOLS_WINDOWS_CONVERTER_EXE_TOKENIZER_H_

+

+#include <string>

+#include <vector>

+

+namespace crash {

+

+using std::string;

+using std::vector;

+

+class Tokenizer {

+ public:

+  // Splits |input| into a series of tokens delimited in the input string by

+  // any of the characters in |delimiters|.  The tokens are passed back in the

+  // |output| vector.

+  static void Tokenize(const string &delimiters, const string &input,

+                       vector<string> *output);

+};

+

+}  // namespace crash

+

+#endif  // TOOLS_WINDOWS_CONVERTER_EXE_TOKENIZER_H_

diff --git a/src/tools/windows/converter_exe/winhttp_client.cc b/src/tools/windows/converter_exe/winhttp_client.cc
new file mode 100644
index 0000000..8a8ade3
--- /dev/null
+++ b/src/tools/windows/converter_exe/winhttp_client.cc
@@ -0,0 +1,307 @@
+// Copyright 2019 Google Inc. All rights reserved.

+//

+// Redistribution and use in source and binary forms, with or without

+// modification, are permitted provided that the following conditions are

+// met:

+//

+//     * Redistributions of source code must retain the above copyright

+// notice, this list of conditions and the following disclaimer.

+//     * Redistributions in binary form must reproduce the above

+// copyright notice, this list of conditions and the following disclaimer

+// in the documentation and/or other materials provided with the

+// distribution.

+//     * Neither the name of Google Inc. nor the names of its

+// contributors may be used to endorse or promote products derived from

+// this software without specific prior written permission.

+//

+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+

+#include "tools/windows/converter_exe/winhttp_client.h"

+

+#include <assert.h>

+#include <stdlib.h>

+#include <windows.h>

+#include <winhttp.h>

+#include <vector>

+

+namespace crash {

+

+namespace internal {

+

+// This class implements HttpClient based on WinInet APIs.

+class WinHttpClient : public HttpClient {

+ public:

+  virtual ~WinHttpClient() {}

+  virtual bool CrackUrl(const TCHAR* url,

+                        DWORD flags,

+                        TCHAR* scheme,

+                        size_t scheme_buffer_length,

+                        TCHAR* host,

+                        size_t host_buffer_length,

+                        TCHAR* uri,

+                        size_t uri_buffer_length,

+                        int* port) const;

+  virtual bool Open(const TCHAR* user_agent,

+                    DWORD access_type,

+                    const TCHAR* proxy_name,

+                    const TCHAR* proxy_bypass,

+                    HttpHandle* session_handle) const;

+  virtual bool Connect(HttpHandle session_handle,

+                       const TCHAR* server,

+                       int port,

+                       HttpHandle* connection_handle) const;

+  virtual bool OpenRequest(HttpHandle connection_handle,

+                           const TCHAR* verb,

+                           const TCHAR* uri,

+                           const TCHAR* version,

+                           const TCHAR* referrer,

+                           bool is_secure,

+                           HttpHandle* request_handle) const;

+  virtual bool SendRequest(HttpHandle request_handle,

+                           const TCHAR* headers,

+                           DWORD headers_length) const;

+  virtual bool ReceiveResponse(HttpHandle request_handle) const;

+  virtual bool GetHttpStatusCode(HttpHandle request_handle,

+                                 int* status_code) const;

+  virtual bool GetContentLength(HttpHandle request_handle,

+                                DWORD* content_length) const;

+  virtual bool ReadData(HttpHandle request_handle,

+                        void* buffer,

+                        DWORD buffer_length,

+                        DWORD* bytes_read) const;

+  virtual bool Close(HttpHandle handle) const;

+

+ private:

+  static DWORD MapAccessType(DWORD access_type);

+  static HINTERNET ToHINTERNET(HttpHandle handle);

+  static HttpHandle FromHINTERNET(HINTERNET handle);

+};

+

+bool WinHttpClient::CrackUrl(const TCHAR* url,

+                             DWORD flags,

+                             TCHAR* scheme,

+                             size_t scheme_buffer_length,

+                             TCHAR* host,

+                             size_t host_buffer_length,

+                             TCHAR* uri,

+                             size_t uri_buffer_length,

+                             int* port) const {

+  assert(url);

+  assert(scheme);

+  assert(host);

+  assert(uri);

+  assert(port);

+

+  URL_COMPONENTS url_comp = {0};

+  url_comp.dwStructSize = sizeof(url_comp);

+  url_comp.lpszScheme = scheme;

+  url_comp.dwSchemeLength = static_cast<DWORD>(scheme_buffer_length);

+  url_comp.lpszHostName = host;

+  url_comp.dwHostNameLength = static_cast<DWORD>(host_buffer_length);

+  url_comp.lpszUrlPath = uri;

+  url_comp.dwUrlPathLength = static_cast<DWORD>(uri_buffer_length);

+

+  bool result = !!::WinHttpCrackUrl(url, 0, flags, &url_comp);

+  if (result) {

+    *port = static_cast<int>(url_comp.nPort);

+  }

+  return result;

+}

+

+bool WinHttpClient::Open(const TCHAR* user_agent,

+                         DWORD access_type,

+                         const TCHAR* proxy_name,

+                         const TCHAR* proxy_bypass,

+                         HttpHandle* session_handle)  const {

+  *session_handle = FromHINTERNET(::WinHttpOpen(user_agent,

+                                                MapAccessType(access_type),

+                                                proxy_name,

+                                                proxy_bypass,

+                                                0));

+

+  return !!(*session_handle);

+}

+

+bool WinHttpClient::Connect(HttpHandle session_handle,

+                            const TCHAR* server,

+                            int port,

+                            HttpHandle* connection_handle) const {

+  assert(server);

+

+  // Uses NULL user name and password to connect.

+  *connection_handle = FromHINTERNET(::WinHttpConnect(

+                                         ToHINTERNET(session_handle),

+                                         server,

+                                         static_cast<INTERNET_PORT>(port),

+                                         NULL));

+  return !!(*connection_handle);

+}

+

+bool WinHttpClient::OpenRequest(HttpHandle connection_handle,

+                                const TCHAR* verb,

+                                const TCHAR* uri,

+                                const TCHAR* version,

+                                const TCHAR* referrer,

+                                bool is_secure,

+                                HttpHandle* request_handle) const {

+  assert(connection_handle);

+  assert(verb);

+  assert(uri);

+  assert(request_handle);

+

+  *request_handle = FromHINTERNET(::WinHttpOpenRequest(

+                                      ToHINTERNET(connection_handle),

+                                      verb,

+                                      uri,

+                                      version,

+                                      referrer,

+                                      WINHTTP_DEFAULT_ACCEPT_TYPES,

+                                      is_secure ? WINHTTP_FLAG_SECURE : 0));

+  return !!(*request_handle);

+}

+

+bool WinHttpClient::SendRequest(HttpHandle request_handle,

+                                const TCHAR* headers,

+                                DWORD headers_length) const {

+  assert(request_handle);

+

+  return !!::WinHttpSendRequest(ToHINTERNET(request_handle),

+                                headers,

+                                headers_length,

+                                NULL,

+                                0,

+                                WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH,

+                                NULL);

+}

+

+bool WinHttpClient::ReceiveResponse(HttpHandle request_handle) const {

+  assert(request_handle);

+

+  return !!::WinHttpReceiveResponse(ToHINTERNET(request_handle), NULL);

+}

+

+bool WinHttpClient::GetHttpStatusCode(HttpHandle request_handle,

+                                      int* status_code) const {

+  TCHAR http_status_string[4] = {0};

+  DWORD http_status_string_size = sizeof(http_status_string);

+  if (!::WinHttpQueryHeaders(ToHINTERNET(request_handle),

+                             WINHTTP_QUERY_STATUS_CODE,

+                             WINHTTP_HEADER_NAME_BY_INDEX,

+                             static_cast<void *>(&http_status_string),

+                             &http_status_string_size, 0)) {

+    return false;

+  }

+

+  *status_code = static_cast<DWORD>(_tcstol(http_status_string, NULL, 10));

+  return true;

+}

+

+bool WinHttpClient::GetContentLength(HttpHandle request_handle,

+                                     DWORD* content_length) const {

+  assert(request_handle);

+  assert(content_length);

+

+  TCHAR content_length_string[11] = {0};

+  DWORD content_length_string_size = sizeof(content_length_string);

+  if (!::WinHttpQueryHeaders(ToHINTERNET(request_handle),

+                             WINHTTP_QUERY_CONTENT_LENGTH,

+                             WINHTTP_HEADER_NAME_BY_INDEX,

+                             static_cast<void *>(&content_length_string),

+                             &content_length_string_size, 0)) {

+    *content_length = kUnknownContentLength;

+  } else {

+    *content_length =

+        static_cast<DWORD>(wcstol(content_length_string, NULL, 10));

+  }

+  return true;

+}

+

+bool WinHttpClient::ReadData(HttpHandle request_handle,

+                             void* buffer,

+                             DWORD buffer_length,

+                             DWORD* bytes_read) const {

+  assert(request_handle);

+  assert(buffer);

+  assert(bytes_read);

+

+  DWORD bytes_read_local = 0;

+  if (!::WinHttpReadData(ToHINTERNET(request_handle),

+                         buffer,

+                         buffer_length,

+                         &bytes_read_local)) {

+    return false;

+  }

+  *bytes_read = bytes_read_local;

+  return true;

+}

+

+bool WinHttpClient::Close(HttpHandle handle) const {

+  assert(handle);

+  return !!::WinHttpCloseHandle(ToHINTERNET(handle));

+}

+

+DWORD WinHttpClient::MapAccessType(DWORD access_type) {

+  switch (static_cast<AccessType>(access_type)) {

+    case ACCESS_TYPE_PRECONFIG:

+    default:

+      return WINHTTP_ACCESS_TYPE_DEFAULT_PROXY;

+    case ACCESS_TYPE_DIRECT:

+      return WINHTTP_ACCESS_TYPE_NO_PROXY;

+    case ACCESS_TYPE_PROXY:

+      return WINHTTP_ACCESS_TYPE_NAMED_PROXY;

+  }

+}

+

+

+HINTERNET WinHttpClient::ToHINTERNET(HttpHandle handle) {

+  return static_cast<HINTERNET>(handle);

+}

+

+HttpHandle WinHttpClient::FromHINTERNET(HINTERNET handle) {

+  return static_cast<HttpHandle>(handle);

+}

+

+}  // namespace internal

+

+HttpClient* CreateWinHttpClient(const TCHAR* url) {

+  assert(url);

+

+  internal::WinHttpClient winhttp;

+  wchar_t scheme[16] = {0};

+  wchar_t host[256] = {0};

+  wchar_t path[256] = {0};

+  int port = 0;

+

+  if (!winhttp.CrackUrl(url,

+                        0,

+                        scheme,

+                        sizeof(scheme)/sizeof(scheme[0]),

+                        host,

+                        sizeof(host)/sizeof(host[0]),

+                        path,

+                        sizeof(path)/sizeof(path[0]),

+                        &port)) {

+    return NULL;

+  }

+

+  if (_wcsicmp(scheme, L"https") == 0) {

+    // Winhttp under WINE doesn't support wildcard certificates, so avoid 

+    // to use it if the scheme is https. The caller should fall back to

+    // use wininet if NULL is returned.

+    return NULL;

+  }

+

+  return new internal::WinHttpClient();

+}

+

+}  // namespace crash

diff --git a/src/tools/windows/converter_exe/winhttp_client.h b/src/tools/windows/converter_exe/winhttp_client.h
new file mode 100644
index 0000000..819d610
--- /dev/null
+++ b/src/tools/windows/converter_exe/winhttp_client.h
@@ -0,0 +1,40 @@
+// Copyright 2019 Google Inc. All rights reserved.

+//

+// Redistribution and use in source and binary forms, with or without

+// modification, are permitted provided that the following conditions are

+// met:

+//

+//     * Redistributions of source code must retain the above copyright

+// notice, this list of conditions and the following disclaimer.

+//     * Redistributions in binary form must reproduce the above

+// copyright notice, this list of conditions and the following disclaimer

+// in the documentation and/or other materials provided with the

+// distribution.

+//     * Neither the name of Google Inc. nor the names of its

+// contributors may be used to endorse or promote products derived from

+// this software without specific prior written permission.

+//

+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+

+#ifndef TOOLS_WINDOWS_CONVERTER_EXE_WINHTTP_CLIENT_H_

+#define TOOLS_WINDOWS_CONVERTER_EXE_WINHTTP_CLIENT_H_

+

+#include "tools/windows/converter_exe/http_client.h"

+

+namespace crash {

+

+HttpClient* CreateWinHttpClient(const TCHAR* url);

+

+}  // namespace crash

+

+#endif  // TOOLS_WINDOWS_CONVERTER_EXE_WINHTTP_CLIENT_H_

diff --git a/src/tools/windows/converter_exe/wininet_client.cc b/src/tools/windows/converter_exe/wininet_client.cc
new file mode 100644
index 0000000..3e542db
--- /dev/null
+++ b/src/tools/windows/converter_exe/wininet_client.cc
@@ -0,0 +1,278 @@
+// Copyright 2019 Google Inc. All rights reserved.

+//

+// Redistribution and use in source and binary forms, with or without

+// modification, are permitted provided that the following conditions are

+// met:

+//

+//     * Redistributions of source code must retain the above copyright

+// notice, this list of conditions and the following disclaimer.

+//     * Redistributions in binary form must reproduce the above

+// copyright notice, this list of conditions and the following disclaimer

+// in the documentation and/or other materials provided with the

+// distribution.

+//     * Neither the name of Google Inc. nor the names of its

+// contributors may be used to endorse or promote products derived from

+// this software without specific prior written permission.

+//

+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+

+#include "tools/windows/converter_exe/wininet_client.h"

+

+#include <assert.h>

+#include <stdlib.h>

+#include <windows.h>

+#include <wininet.h>

+

+namespace crash {

+

+namespace internal {

+

+// This class implements HttpClient based on WinInet APIs.

+class WinInetClient : public HttpClient {

+ public:

+  virtual ~WinInetClient() {}

+  virtual bool CrackUrl(const TCHAR* url,

+                        DWORD flags,

+                        TCHAR* scheme,

+                        size_t scheme_buffer_length,

+                        TCHAR* host,

+                        size_t host_buffer_length,

+                        TCHAR* uri,

+                        size_t uri_buffer_length,

+                        int* port) const;

+  virtual bool Open(const TCHAR* user_agent,

+                    DWORD access_type,

+                    const TCHAR* proxy_name,

+                    const TCHAR* proxy_bypass,

+                    HttpHandle* session_handle) const;

+  virtual bool Connect(HttpHandle session_handle,

+                       const TCHAR* server,

+                       int port,

+                       HttpHandle* connection_handle) const;

+  virtual bool OpenRequest(HttpHandle connection_handle,

+                           const TCHAR* verb,

+                           const TCHAR* uri,

+                           const TCHAR* version,

+                           const TCHAR* referrer,

+                           bool is_secure,

+                           HttpHandle* request_handle) const;

+  virtual bool SendRequest(HttpHandle request_handle,

+                           const TCHAR* headers,

+                           DWORD headers_length) const;

+  virtual bool ReceiveResponse(HttpHandle request_handle) const;

+  virtual bool GetHttpStatusCode(HttpHandle request_handle,

+                                 int* status_code) const;

+  virtual bool GetContentLength(HttpHandle request_handle,

+                                DWORD* content_length) const;

+  virtual bool ReadData(HttpHandle request_handle,

+                        void* buffer,

+                        DWORD buffer_length,

+                        DWORD* bytes_read) const;

+  virtual bool Close(HttpHandle handle) const;

+

+ private:

+  static DWORD MapAccessType(DWORD access_type);

+  static HINTERNET ToHINTERNET(HttpHandle handle);

+  static HttpHandle FromHINTERNET(HINTERNET handle);

+};

+

+bool WinInetClient::CrackUrl(const TCHAR* url,

+                             DWORD flags,

+                             TCHAR* scheme,

+                             size_t scheme_buffer_length,

+                             TCHAR* host,

+                             size_t host_buffer_length,

+                             TCHAR* uri,

+                             size_t uri_buffer_length,

+                             int* port) const {

+  assert(url);

+  assert(scheme);

+  assert(host);

+  assert(uri);

+  assert(port);

+

+  URL_COMPONENTS url_comp = {0};

+  url_comp.dwStructSize = sizeof(url_comp);

+  url_comp.lpszScheme = scheme;

+  url_comp.dwSchemeLength = static_cast<DWORD>(scheme_buffer_length);

+  url_comp.lpszHostName = host;

+  url_comp.dwHostNameLength = static_cast<DWORD>(host_buffer_length);

+  url_comp.lpszUrlPath = uri;

+  url_comp.dwUrlPathLength = static_cast<DWORD>(uri_buffer_length);

+

+  bool result = !!::InternetCrackUrl(url, 0, flags, &url_comp);

+  if (result) {

+    *port = static_cast<int>(url_comp.nPort);

+  }

+  return result;

+}

+

+bool WinInetClient::Open(const TCHAR* user_agent,

+                         DWORD access_type,

+                         const TCHAR* proxy_name,

+                         const TCHAR* proxy_bypass,

+                         HttpHandle* session_handle)  const {

+  *session_handle = FromHINTERNET(::InternetOpen(user_agent,

+                                                 MapAccessType(access_type),

+                                                 proxy_name,

+                                                 proxy_bypass,

+                                                 0));

+  return !!(*session_handle);

+}

+

+bool WinInetClient::Connect(HttpHandle session_handle,

+                            const TCHAR* server,

+                            int port,

+                            HttpHandle* connection_handle) const {

+  assert(server);

+

+  // Uses NULL user name and password to connect. Always uses http service.

+  *connection_handle = FromHINTERNET(::InternetConnect(

+                                         ToHINTERNET(session_handle),

+                                         server,

+                                         static_cast<INTERNET_PORT>(port),

+                                         NULL,

+                                         NULL,

+                                         INTERNET_SERVICE_HTTP,

+                                         0,

+                                         0));

+  return !!(*connection_handle);

+}

+

+bool WinInetClient::OpenRequest(HttpHandle connection_handle,

+                                const TCHAR* verb,

+                                const TCHAR* uri,

+                                const TCHAR* version,

+                                const TCHAR* referrer,

+                                bool is_secure,

+                                HttpHandle* request_handle) const {

+  assert(connection_handle);

+  assert(verb);

+  assert(uri);

+

+  *request_handle = FromHINTERNET(::HttpOpenRequest(

+                                      ToHINTERNET(connection_handle),

+                                      verb,

+                                      uri,

+                                      version,

+                                      referrer,

+                                      NULL,

+                                      is_secure ? INTERNET_FLAG_SECURE : 0,

+                                      NULL));

+  return !!(*request_handle);

+}

+

+bool WinInetClient::SendRequest(HttpHandle request_handle,

+                                const TCHAR* headers,

+                                DWORD headers_length) const {

+  assert(request_handle);

+

+  return !!::HttpSendRequest(ToHINTERNET(request_handle),

+                             headers,

+                             headers_length,

+                             NULL,

+                             0);

+}

+

+bool WinInetClient::ReceiveResponse(HttpHandle) const {

+  return true;

+}

+

+bool WinInetClient::GetHttpStatusCode(HttpHandle request_handle,

+                                      int* status_code) const {

+  assert(request_handle);

+

+  TCHAR http_status_string[4] = {0};

+  DWORD http_status_string_size = sizeof(http_status_string);

+  if (!::HttpQueryInfo(ToHINTERNET(request_handle),

+                       HTTP_QUERY_STATUS_CODE,

+                       static_cast<void *>(&http_status_string),

+                       &http_status_string_size,

+                       0)) {

+    return false;

+  }

+

+  *status_code = _tcstol(http_status_string, NULL, 10);

+  return true;

+}

+

+bool WinInetClient::GetContentLength(HttpHandle request_handle,

+                                     DWORD* content_length) const {

+  assert(request_handle);

+  assert(content_length);

+

+  TCHAR content_length_string[11];

+  DWORD content_length_string_size = sizeof(content_length_string);

+  if (!::HttpQueryInfo(ToHINTERNET(request_handle),

+                       HTTP_QUERY_CONTENT_LENGTH,

+                       static_cast<void *>(&content_length_string),

+                       &content_length_string_size,

+                       0)) {

+    *content_length = kUnknownContentLength;

+  } else {

+    *content_length = wcstol(content_length_string, NULL, 10);

+  }

+  return true;

+}

+

+bool WinInetClient::ReadData(HttpHandle request_handle,

+                             void* buffer,

+                             DWORD buffer_length,

+                             DWORD* bytes_read) const {

+  assert(request_handle);

+  assert(buffer);

+  assert(bytes_read);

+

+  DWORD bytes_read_local = 0;

+  if (!::InternetReadFile(ToHINTERNET(request_handle),

+                          buffer,

+                          buffer_length,

+                          &bytes_read_local)) {

+    return false;

+  }

+  *bytes_read = bytes_read_local;

+  return true;

+}

+

+bool WinInetClient::Close(HttpHandle handle) const {

+  assert(handle);

+  return !!::InternetCloseHandle(ToHINTERNET(handle));

+}

+

+DWORD WinInetClient::MapAccessType(DWORD access_type) {

+  switch (static_cast<AccessType>(access_type)) {

+    case ACCESS_TYPE_PRECONFIG:

+    default:

+      return INTERNET_OPEN_TYPE_PRECONFIG;

+    case ACCESS_TYPE_DIRECT:

+      return INTERNET_OPEN_TYPE_DIRECT;

+    case ACCESS_TYPE_PROXY:

+      return INTERNET_OPEN_TYPE_PROXY;

+  }

+}

+

+HINTERNET WinInetClient::ToHINTERNET(HttpHandle handle) {

+  return static_cast<HINTERNET>(handle);

+}

+

+HttpHandle WinInetClient::FromHINTERNET(HINTERNET handle) {

+  return static_cast<HttpHandle>(handle);

+}

+

+}  // namespace internal

+

+HttpClient* CreateWinInetClient(const TCHAR*) {

+  return new internal::WinInetClient();

+}

+

+}  // namespace crash

diff --git a/src/tools/windows/converter_exe/wininet_client.h b/src/tools/windows/converter_exe/wininet_client.h
new file mode 100644
index 0000000..bd04b60
--- /dev/null
+++ b/src/tools/windows/converter_exe/wininet_client.h
@@ -0,0 +1,40 @@
+// Copyright 2019 Google Inc. All rights reserved.

+//

+// Redistribution and use in source and binary forms, with or without

+// modification, are permitted provided that the following conditions are

+// met:

+//

+//     * Redistributions of source code must retain the above copyright

+// notice, this list of conditions and the following disclaimer.

+//     * Redistributions in binary form must reproduce the above

+// copyright notice, this list of conditions and the following disclaimer

+// in the documentation and/or other materials provided with the

+// distribution.

+//     * Neither the name of Google Inc. nor the names of its

+// contributors may be used to endorse or promote products derived from

+// this software without specific prior written permission.

+//

+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+

+#ifndef TOOLS_WINDOWS_CONVERTER_EXE_WININET_CLIENT_H_

+#define TOOLS_WINDOWS_CONVERTER_EXE_WININET_CLIENT_H_

+

+#include "tools/windows/converter_exe/http_client.h"

+

+namespace crash {

+

+HttpClient* CreateWinInetClient(const TCHAR* url);

+

+}  // namespace crash

+

+#endif  // TOOLS_WINDOWS_CONVERTER_EXE_WININET_CLIENT_H_

diff --git a/src/tools/windows/tools_windows.gyp b/src/tools/windows/tools_windows.gyp
index cc0da72..17b88b4 100644
--- a/src/tools/windows/tools_windows.gyp
+++ b/src/tools/windows/tools_windows.gyp
@@ -37,6 +37,7 @@
       'type': 'none',
       'dependencies': [
         './converter/ms_symbol_server_converter.gyp:*',
+        './converter_exe/converter.gyp:*',
         './dump_syms/dump_syms.gyp:*',
         './symupload/symupload.gyp:*',
       ],