From 1217c1f898457689024c6d0f7448442e7d964f86 Mon Sep 17 00:00:00 2001 From: bryner Date: Wed, 27 Sep 2006 01:00:32 +0000 Subject: [PATCH] Initial version of Windows exception handler and crash report sender classes (#31). r=mmentovai. git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@36 4c0a9323-5329-0410-9bdc-e9ce6186880e --- src/client/windows/README | 22 ++ src/client/windows/airbag_client.sln | 26 ++ .../windows/handler/exception_handler.cc | 148 ++++++++++ .../windows/handler/exception_handler.h | 139 +++++++++ .../windows/handler/exception_handler.vcproj | 175 ++++++++++++ .../windows/sender/crash_report_sender.cc | 264 ++++++++++++++++++ .../windows/sender/crash_report_sender.h | 99 +++++++ .../windows/sender/crash_report_sender.vcproj | 175 ++++++++++++ 8 files changed, 1048 insertions(+) create mode 100644 src/client/windows/README create mode 100644 src/client/windows/airbag_client.sln create mode 100644 src/client/windows/handler/exception_handler.cc create mode 100644 src/client/windows/handler/exception_handler.h create mode 100644 src/client/windows/handler/exception_handler.vcproj create mode 100644 src/client/windows/sender/crash_report_sender.cc create mode 100644 src/client/windows/sender/crash_report_sender.h create mode 100644 src/client/windows/sender/crash_report_sender.vcproj diff --git a/src/client/windows/README b/src/client/windows/README new file mode 100644 index 00000000..380324fd --- /dev/null +++ b/src/client/windows/README @@ -0,0 +1,22 @@ +This directory contains client code for collecting and sending minidumps. +You can use them in your project as follows: + +exception_handler.lib: + +This library should be linked into the application from which you want +to collect crash reports. See the comments in exception_handler.h for +instructions on setting up the handler to write minidumps on crash. +You can also use ExceptionHandler to write non-crash minidumps. + +When linking with this static library, you will need to also link with +rpcrt4.lib and ole32.lib. + +crash_report_sender.lib: + +Typically, after catching a crash, you will launch an external application +to collect any additional information from the user, and upload the report to +the server. CrashReportSender is a utility class that can be used when the +data is ready to upload. + +When linking with this static library, you will need to also link with +wininet.lib. diff --git a/src/client/windows/airbag_client.sln b/src/client/windows/airbag_client.sln new file mode 100644 index 00000000..502143bc --- /dev/null +++ b/src/client/windows/airbag_client.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "exception_handler", "handler\exception_handler.vcproj", "{B55CA863-B374-4BAF-95AC-539E4FA4C90C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "crash_report_sender", "sender\crash_report_sender.vcproj", "{9946A048-043B-4F8F-9E07-9297B204714C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B55CA863-B374-4BAF-95AC-539E4FA4C90C}.Debug|Win32.ActiveCfg = Debug|Win32 + {B55CA863-B374-4BAF-95AC-539E4FA4C90C}.Debug|Win32.Build.0 = Debug|Win32 + {B55CA863-B374-4BAF-95AC-539E4FA4C90C}.Release|Win32.ActiveCfg = Release|Win32 + {B55CA863-B374-4BAF-95AC-539E4FA4C90C}.Release|Win32.Build.0 = Release|Win32 + {9946A048-043B-4F8F-9E07-9297B204714C}.Debug|Win32.ActiveCfg = Debug|Win32 + {9946A048-043B-4F8F-9E07-9297B204714C}.Debug|Win32.Build.0 = Debug|Win32 + {9946A048-043B-4F8F-9E07-9297B204714C}.Release|Win32.ActiveCfg = Release|Win32 + {9946A048-043B-4F8F-9E07-9297B204714C}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/client/windows/handler/exception_handler.cc b/src/client/windows/handler/exception_handler.cc new file mode 100644 index 00000000..ba5c2653 --- /dev/null +++ b/src/client/windows/handler/exception_handler.cc @@ -0,0 +1,148 @@ +// Copyright (c) 2006, 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 + +#include + +#include "client/windows/handler/exception_handler.h" + +namespace google_airbag { + +ExceptionHandler *ExceptionHandler::current_handler_ = NULL; + +ExceptionHandler::ExceptionHandler(const wstring &dump_path, + MinidumpCallback callback, + void *callback_context, + bool install_handler) + : callback_(callback), callback_context_(callback_context), + dump_path_(dump_path), next_minidump_id_(NULL), + dbghelp_module_(NULL), minidump_write_dump_(NULL), + previous_handler_(current_handler_), previous_filter_(NULL) { + UpdateNextID(); + dbghelp_module_ = LoadLibrary(L"dbghelp.dll"); + if (dbghelp_module_) { + minidump_write_dump_ = reinterpret_cast( + GetProcAddress(dbghelp_module_, "MiniDumpWriteDump")); + } + if (install_handler) { + previous_filter_ = SetUnhandledExceptionFilter(HandleException); + current_handler_ = this; + } +} + +ExceptionHandler::~ExceptionHandler() { + if (dbghelp_module_) { + FreeLibrary(dbghelp_module_); + } + if (next_minidump_id_) { + RpcStringFree(&next_minidump_id_); + } + if (current_handler_ == this) { + SetUnhandledExceptionFilter(previous_filter_); + current_handler_ = previous_handler_; + } +} + +// static +LONG ExceptionHandler::HandleException(EXCEPTION_POINTERS *exinfo) { + if (!current_handler_->WriteMinidumpWithException(exinfo)) { + return EXCEPTION_CONTINUE_SEARCH; + } + return EXCEPTION_EXECUTE_HANDLER; +} + +bool ExceptionHandler::WriteMinidump() { + bool success = WriteMinidumpWithException(NULL); + UpdateNextID(); + return success; +} + +// static +bool ExceptionHandler::WriteMinidump(const wstring &dump_path, + MinidumpCallback callback, + void *callback_context) { + ExceptionHandler handler(dump_path, callback, callback_context, false); + return handler.WriteMinidump(); +} + +bool ExceptionHandler::WriteMinidumpWithException(EXCEPTION_POINTERS *exinfo) { + wchar_t dump_file_name[MAX_PATH]; + swprintf_s(dump_file_name, MAX_PATH, L"%s\\%s.dmp", + dump_path_.c_str(), next_minidump_id_); + + bool success = false; + if (minidump_write_dump_) { + HANDLE dump_file = CreateFile(dump_file_name, + GENERIC_WRITE, + FILE_SHARE_WRITE, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (dump_file != INVALID_HANDLE_VALUE) { + MINIDUMP_EXCEPTION_INFORMATION except_info; + except_info.ThreadId = GetCurrentThreadId(); + except_info.ExceptionPointers = exinfo; + except_info.ClientPointers = FALSE; + + // The explicit comparison to TRUE avoids a warning (C4800). + success = (minidump_write_dump_(GetCurrentProcess(), + GetCurrentProcessId(), + dump_file, + MiniDumpNormal, + &except_info, + NULL, + NULL) == TRUE); + + CloseHandle(dump_file); + } + } + + if (callback_) { + // This looks nasty, but RPC_WSTR is really just a wide string, + // and there are no "supported" ways to convert them other than casting. + callback_(reinterpret_cast(next_minidump_id_), + callback_context_, success); + } + // TODO(bryner): log an error on failure + + return success; +} + +void ExceptionHandler::UpdateNextID() { + if (next_minidump_id_) { + RpcStringFree(&next_minidump_id_); + } + GUID id; + CoCreateGuid(&id); + UuidToString(&id, &next_minidump_id_); +} + +} // namespace google_airbag diff --git a/src/client/windows/handler/exception_handler.h b/src/client/windows/handler/exception_handler.h new file mode 100644 index 00000000..7acb5853 --- /dev/null +++ b/src/client/windows/handler/exception_handler.h @@ -0,0 +1,139 @@ +// Copyright (c) 2006, 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. + +// ExceptionHandler can write a minidump file when an exception occurs, +// or when WriteMinidump() is called explicitly by your program. +// +// To have the exception handler write minidumps when an uncaught exception +// (crash) occurs, you should create an instance early in the execution +// of your program, and keep it around for the entire time you want to +// have crash handling active (typically, until shutdown). +// +// If you want to write minidumps without installing the exception handler, +// you can create an ExceptionHandler with install_handler set to false, +// then call WriteMinidump. You can also use this technique if you want to +// use different minidump callbacks for different call sites. +// +// In either case, a callback function is called when a minidump is written, +// which receives the unqiue id of the minidump. The caller can use this +// id to collect and write additional application state, and to launch an +// external crash-reporting application. +// +// It is important that creation and destruction of ExceptionHandler objects +// be nested cleanly, when using install_handler = true. +// Avoid the following pattern: +// ExceptionHandler *e = new ExceptionHandler(...); +// ExceptionHandler *f = new ExceptionHandler(...); +// delete e; +// This will put the exception filter stack into an inconsistent state. + +#ifndef CLIENT_WINDOWS_HANDLER_EXCEPTION_HANDLER_H__ +#define CLIENT_WINDOWS_HANDLER_EXCEPTION_HANDLER_H__ + +#include +#include + +#include + +namespace google_airbag { + +using std::wstring; + +class ExceptionHandler { + public: + // A callback function to run after the minidump has been written. + // minidump_id is a unique id for the dump, so the minidump + // file is \.dmp. succeeded indicates whether + // a minidump file was successfully written. + typedef void (*MinidumpCallback)(const wstring &minidump_id, + void *context, bool succeeded); + + // Creates a new ExceptionHandler instance to handle writing minidumps. + // Minidump files will be written to dump_path, and the optional callback + // is called after writing the dump file, as described above. + // If install_handler is true, then a minidump will be written whenever + // an unhandled exception occurs. If it is false, minidumps will only + // be written when WriteMinidump is called. + ExceptionHandler(const wstring &dump_path, MinidumpCallback callback, + void *callback_context, bool install_handler); + ~ExceptionHandler(); + + // Writes a minidump immediately. This can be used to capture the + // execution state independently of a crash. Returns true on success. + bool WriteMinidump(); + + // Convenience form of WriteMinidump which does not require an + // ExceptionHandler instance. + static bool WriteMinidump(const wstring &dump_path, + MinidumpCallback callback, void *callback_context); + + private: + // Function pointer type for MiniDumpWriteDump, which is looked up + // dynamically. + typedef BOOL (WINAPI *MiniDumpWriteDump_type)( + HANDLE hProcess, + DWORD dwPid, + HANDLE hFile, + MINIDUMP_TYPE DumpType, + CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, + CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, + CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam); + + // This function does the actual writing of a minidump. + bool WriteMinidumpWithException(EXCEPTION_POINTERS *exinfo); + + // Called when an unhandled exception occurs. + static LONG WINAPI HandleException(EXCEPTION_POINTERS *exinfo); + + // Generates a new ID and stores it in next_minidump_id_. + void UpdateNextID(); + + MinidumpCallback callback_; + void *callback_context_; + + wstring dump_path_; + RPC_WSTR next_minidump_id_; + + HMODULE dbghelp_module_; + MiniDumpWriteDump_type minidump_write_dump_; + + ExceptionHandler *previous_handler_; // current_handler_ before us + LPTOP_LEVEL_EXCEPTION_FILTER previous_filter_; + + // the currently-installed ExceptionHandler, of which there can be only 1 + static ExceptionHandler *current_handler_; + + // disallow copy ctor and operator= + explicit ExceptionHandler(const ExceptionHandler &); + void operator=(const ExceptionHandler &); +}; + +} // namespace google_airbag + +#endif // CLIENT_WINDOWS_HANDLER_EXCEPTION_HANDLER_H__ diff --git a/src/client/windows/handler/exception_handler.vcproj b/src/client/windows/handler/exception_handler.vcproj new file mode 100644 index 00000000..5dce6273 --- /dev/null +++ b/src/client/windows/handler/exception_handler.vcproj @@ -0,0 +1,175 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/client/windows/sender/crash_report_sender.cc b/src/client/windows/sender/crash_report_sender.cc new file mode 100644 index 00000000..16c26c2b --- /dev/null +++ b/src/client/windows/sender/crash_report_sender.cc @@ -0,0 +1,264 @@ +// Copyright (c) 2006, 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 +#include +#include + +#include + +#include "client/windows/sender/crash_report_sender.h" + +namespace google_airbag { + +using std::ifstream; +using std::ios; + +static const wchar_t kUserAgent[] = L"Airbag/1.0 (Windows)"; + +// Helper class which closes an internet handle when it goes away +class CrashReportSender::AutoInternetHandle { + public: + explicit AutoInternetHandle(HINTERNET handle) : handle_(handle) {} + ~AutoInternetHandle() { + if (handle_) { + InternetCloseHandle(handle_); + } + } + + HINTERNET get() { return handle_; } + + private: + HINTERNET handle_; +}; + +// static +bool CrashReportSender::SendCrashReport( + const wstring &url, const map ¶meters, + const wstring &dump_file_name) { + // TODO(bryner): support non-ASCII parameter names + if (!CheckParameters(parameters)) { + return false; + } + + // Break up the URL and make sure we can handle it + wchar_t scheme[16], host[256], path[256]; + URL_COMPONENTS components; + memset(&components, 0, sizeof(components)); + components.dwStructSize = sizeof(components); + components.lpszScheme = scheme; + components.dwSchemeLength = sizeof(scheme); + components.lpszHostName = host; + components.dwHostNameLength = sizeof(host); + components.lpszUrlPath = path; + components.dwUrlPathLength = sizeof(path); + if (!InternetCrackUrl(url.c_str(), static_cast(url.size()), + 0, &components)) { + return false; + } + if (wcscmp(scheme, L"http") != 0) { + return false; + } + + AutoInternetHandle internet(InternetOpen(kUserAgent, + INTERNET_OPEN_TYPE_DIRECT, + NULL, // proxy name + NULL, // proxy bypass + 0)); // flags + if (!internet.get()) { + return false; + } + + AutoInternetHandle connection(InternetConnect(internet.get(), + host, + components.nPort, + NULL, // user name + NULL, // password + INTERNET_SERVICE_HTTP, + 0, // flags + NULL)); // context + if (!connection.get()) { + return false; + } + + AutoInternetHandle request(HttpOpenRequest(connection.get(), + L"POST", + path, + NULL, // version + NULL, // referer + NULL, // agent type + 0, // flags + NULL)); // context + if (!request.get()) { + return false; + } + + wstring boundary = GenerateMultipartBoundary(); + wstring content_type_header = GenerateRequestHeader(boundary); + HttpAddRequestHeaders(request.get(), + content_type_header.c_str(), + -1, HTTP_ADDREQ_FLAG_ADD); + + string request_body; + GenerateRequestBody(parameters, dump_file_name, boundary, &request_body); + + // The explicit comparison to TRUE avoids a warning (C4800). + return (HttpSendRequest(request.get(), NULL, 0, + const_cast(request_body.data()), + static_cast(request_body.size())) == TRUE); +} + +// static +wstring CrashReportSender::GenerateMultipartBoundary() { + // The boundary has 27 '-' characters followed by 16 hex digits + static const wchar_t kBoundaryPrefix[] = L"---------------------------"; + static const int kBoundaryLength = 27 + 16 + 1; + + // Generate some random numbers to fill out the boundary + int r0 = rand(); + int r1 = rand(); + + wchar_t temp[kBoundaryLength]; + swprintf_s(temp, kBoundaryLength, L"%s%08X%08X", kBoundaryPrefix, r0, r1); + return wstring(temp); +} + +// static +wstring CrashReportSender::GenerateRequestHeader(const wstring &boundary) { + wstring header = L"Content-Type: multipart/form-data; boundary="; + header += boundary; + return header; +} + +// static +bool CrashReportSender::GenerateRequestBody( + const map ¶meters, + const wstring &minidump_filename, const wstring &boundary, + string *request_body) { + vector contents; + GetFileContents(minidump_filename, &contents); + if (contents.empty()) { + return false; + } + + string boundary_str = WideToUTF8(boundary); + if (boundary_str.empty()) { + return false; + } + + request_body->clear(); + + // Append each of the parameter pairs as a form-data part + for (map::const_iterator pos = parameters.begin(); + pos != parameters.end(); ++pos) { + request_body->append("--" + boundary_str + "\r\n"); + request_body->append("Content-Disposition: form-data; name=\"" + + WideToUTF8(pos->first) + "\"\r\n\r\n" + + WideToUTF8(pos->second) + "\r\n"); + } + + // Now append the minidump file as a binary (octet-stream) part + string filename_utf8 = WideToUTF8(minidump_filename); + if (filename_utf8.empty()) { + return false; + } + + request_body->append("--" + boundary_str + "\r\n"); + request_body->append("Content-Disposition: form-data; " + "name=\"upload_file_minidump\"; " + "filename=\"" + filename_utf8 + "\"\r\n"); + request_body->append("Content-Type: application/octet-stream\r\n"); + request_body->append("\r\n"); + + request_body->append(&(contents[0]), contents.size()); + request_body->append("\r\n"); + request_body->append("--" + boundary_str + "--\r\n"); + return true; +} + +// static +void CrashReportSender::GetFileContents(const wstring &filename, + vector *contents) { + ifstream file; + file.open(filename.c_str(), ios::binary); + if (file.is_open()) { + file.seekg(0, ios::end); + int length = file.tellg(); + contents->resize(length); + file.seekg(0, ios::beg); + file.read(&((*contents)[0]), length); + file.close(); + } else { + contents->clear(); + } +} + +// static +string CrashReportSender::WideToUTF8(const wstring &wide) { + if (wide.length() == 0) { + return string(); + } + + // compute the length of the buffer we'll need + int charcount = WideCharToMultiByte(CP_UTF8, 0, wide.c_str(), -1, + NULL, 0, NULL, NULL); + if (charcount == 0) { + return string(); + } + + // convert + char *buf = new char[charcount]; + WideCharToMultiByte(CP_UTF8, 0, wide.c_str(), -1, buf, charcount, + NULL, NULL); + + string result(buf); + delete[] buf; + return result; +} + +// static +bool CrashReportSender::CheckParameters( + const map ¶meters) { + for (map::const_iterator pos = parameters.begin(); + pos != parameters.end(); ++pos) { + const wstring &str = pos->first; + if (str.size() == 0) { + return false; // disallow empty parameter names + } + for (unsigned int i = 0; i < str.size(); ++i) { + wchar_t c = str[i]; + if (c < 32 || c == '"' || c > 127) { + return false; + } + } + } + return true; +} + +} // namespace google_airbag diff --git a/src/client/windows/sender/crash_report_sender.h b/src/client/windows/sender/crash_report_sender.h new file mode 100644 index 00000000..accd7b46 --- /dev/null +++ b/src/client/windows/sender/crash_report_sender.h @@ -0,0 +1,99 @@ +// Copyright (c) 2006, 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 CLIENT_WINDOWS_SENDER_CRASH_REPORT_SENDER_H__ +#define CLIENT_WINDOWS_SENDER_CRASH_REPORT_SENDER_H__ + +// CrashReportSender is a "static" class which provides an API to upload +// crash reports via HTTP. A crash report is formatted as a multipart POST +// request, which contains a set of caller-supplied string key/value pairs, +// and a minidump file to upload. + +#include +#include +#include + +namespace google_airbag { + +using std::string; +using std::wstring; +using std::map; +using std::vector; + +class CrashReportSender { + public: + // Sends the specified minidump file, along with the map of + // name value pairs, as a multipart POST request to the given URL. + // Parameter names must contain only printable ASCII characters, + // and may not contain a quote (") character. + // Only HTTP URLs are currently supported. Returns true on success. + // TODO(bryner): we should expose the response to the caller. + static bool SendCrashReport(const wstring &url, + const map ¶meters, + const wstring &dump_file_name); + + private: + class AutoInternetHandle; + + // Generates a new multipart boundary for a POST request + static wstring GenerateMultipartBoundary(); + + // Generates a HTTP request header for a multipart form submit. + static wstring GenerateRequestHeader(const wstring &boundary); + + // Given a set of parameters and a minidump file name, + // generates a multipart request body string with these parameters + // and minidump contents. Returns true on success. + static bool GenerateRequestBody(const map ¶meters, + const wstring &minidump_filename, + const wstring &boundary, + string *request_body); + + // Fills the supplied vector with the contents of filename. + static void GetFileContents(const wstring &filename, vector *contents); + + // Converts a UTF16 string to UTF8. + static string WideToUTF8(const wstring &wide); + + // Checks that the given list of parameters has only printable + // ASCII characters in the parameter name, and does not contain + // any quote (") characters. Returns true if so. + static bool CheckParameters(const map ¶meters); + + // No instances of this class should be created. + // Disallow all constructors, destructors, and operator=. + CrashReportSender(); + explicit CrashReportSender(const CrashReportSender &); + void operator=(const CrashReportSender &); + ~CrashReportSender(); +}; + +} // namespace google_airbag + +#endif // CLIENT_WINDOWS_SENDER_CRASH_REPORT_SENDER_H__ diff --git a/src/client/windows/sender/crash_report_sender.vcproj b/src/client/windows/sender/crash_report_sender.vcproj new file mode 100644 index 00000000..8784501e --- /dev/null +++ b/src/client/windows/sender/crash_report_sender.vcproj @@ -0,0 +1,175 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +