[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[vidalia-svn] r3879: Merge Breakpad support for Windows into trunk (disabled by d (in vidalia/trunk: . cmake src src/crashreporter src/crashreporter/res src/crashreporter/res/32x32 src/crashreporter/res/48x48 src/crashreporter/res/64x64 src/vidalia src/vidalia/config)



Author: edmanm
Date: 2009-06-23 18:53:29 -0400 (Tue, 23 Jun 2009)
New Revision: 3879

Added:
   vidalia/trunk/README.breakpad
   vidalia/trunk/cmake/FindBreakpad.cmake
   vidalia/trunk/src/crashreporter/
   vidalia/trunk/src/crashreporter/CMakeLists.txt
   vidalia/trunk/src/crashreporter/CrashReportDialog.cpp
   vidalia/trunk/src/crashreporter/CrashReportDialog.h
   vidalia/trunk/src/crashreporter/CrashReportDialog.ui
   vidalia/trunk/src/crashreporter/CrashReportUploader.cpp
   vidalia/trunk/src/crashreporter/CrashReportUploader.h
   vidalia/trunk/src/crashreporter/UploadProgressDialog.cpp
   vidalia/trunk/src/crashreporter/UploadProgressDialog.h
   vidalia/trunk/src/crashreporter/UploadProgressDialog.ui
   vidalia/trunk/src/crashreporter/main.cpp
   vidalia/trunk/src/crashreporter/res/
   vidalia/trunk/src/crashreporter/res/32x32/
   vidalia/trunk/src/crashreporter/res/32x32/tools-report-bug.png
   vidalia/trunk/src/crashreporter/res/48x48/
   vidalia/trunk/src/crashreporter/res/48x48/tools-report-bug.png
   vidalia/trunk/src/crashreporter/res/64x64/
   vidalia/trunk/src/crashreporter/res/64x64/tools-report-bug.png
   vidalia/trunk/src/crashreporter/res/CrashReporter.ico
   vidalia/trunk/src/crashreporter/res/CrashReporter.qrc
   vidalia/trunk/src/crashreporter/res/CrashReporter.rc.in
   vidalia/trunk/src/crashreporter/res/gd-class2-root.crt
   vidalia/trunk/src/vidalia/CrashReporter.cpp
   vidalia/trunk/src/vidalia/CrashReporter.h
Modified:
   vidalia/trunk/
   vidalia/trunk/CMakeLists.txt
   vidalia/trunk/LICENSE
   vidalia/trunk/config.h.in
   vidalia/trunk/src/CMakeLists.txt
   vidalia/trunk/src/vidalia/CMakeLists.txt
   vidalia/trunk/src/vidalia/config/BridgeDownloaderProgressDialog.h
   vidalia/trunk/src/vidalia/main.cpp
Log:

Merge Breakpad support for Windows into trunk (disabled by default).



Property changes on: vidalia/trunk
___________________________________________________________________
Modified: svn:mergeinfo
   - /vidalia/branches/breakpad:3835,3837
/vidalia/branches/marble:3435-3484
   + /vidalia/branches/breakpad:3835,3837
/vidalia/branches/marble:3435-3484
/vidalia/trunk:3284-3755

Modified: vidalia/trunk/CMakeLists.txt
===================================================================
--- vidalia/trunk/CMakeLists.txt	2009-06-22 22:59:00 UTC (rev 3878)
+++ vidalia/trunk/CMakeLists.txt	2009-06-23 22:53:29 UTC (rev 3879)
@@ -76,6 +76,12 @@
 ## UPnP support is currently optional (enabled by default)
 option(USE_MINIUPNPC "Enable UPnP support using the MiniUPnPc library." ON)
 
+## Crash reporting via Google Breakpad is optional (disabled by default)
+option(USE_BREAKPAD "Enable Google Breakpad crash reporting." OFF)
+if (USE_BREAKPAD)
+  include(${CMAKE_SOURCE_DIR}/cmake/FindBreakpad.cmake)
+endif(USE_BREAKPAD)
+
 ## Automatic osftware update is optional (disabled by default)
 if (WIN32)
   option(USE_AUTOUPDATE "Enable automatic software update support." OFF)

Modified: vidalia/trunk/LICENSE
===================================================================
--- vidalia/trunk/LICENSE	2009-06-22 22:59:00 UTC (rev 3878)
+++ vidalia/trunk/LICENSE	2009-06-23 22:53:29 UTC (rev 3879)
@@ -150,6 +150,36 @@
  for use in the OpenSSL Toolkit (http://www.openssl.org/)"
 ===============================================================================
 
+The function append_string() in Vidalia's src/vidalia/CrashReporter.cpp is
+also derived from the strlcat() implementation by Todd C. Miller. It is
+licensed under the following license:
+
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@xxxxxxxxxxxxx>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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.
+===============================================================================
+
 Some of the help documentation text used in Vidalia is derived from the TorFAQ
 Wiki <http://wiki.noreply.org/noreply/TheOnionRouter/TorFAQ>. It is licensed
 under the MIT license as follows:

Added: vidalia/trunk/README.breakpad
===================================================================
--- vidalia/trunk/README.breakpad	                        (rev 0)
+++ vidalia/trunk/README.breakpad	2009-06-23 22:53:29 UTC (rev 3879)
@@ -0,0 +1,94 @@
+
+       Building Vidalia with Breakpad-based Crash Reporting Support
+
+
+Windows
+--------
+Pre-requisites:
+  * Subversion
+  * CMake 2.4 or later (CMake >= 2.6 preferred)
+  * Microsoft Visual C++ Professional 2005 or later (VC++ >= 2008 preferred)
+     NOTE: Google Breakpad requires Microsoft's ATL, so users of Visual Studio
+           "Express" editions may need to install that separately (if even
+           supported).
+
+1. Download and build Qt with Visual Studio support
+  1.1. Download the latest Qt source code for Windows from Qt's website:
+
+       http://get.qtsoftware.com/qt/source/
+
+  1.2. Extract the .zip file to a directory
+  1.3. Open a Visual Studio command prompt
+  1.4. 'cd' to the location of your extract Qt source
+  1.5. We want to enable building .pdb files for the release versions of the
+       Qt DLLs, since Breakpad derives its symbol information from the .pdb
+       files. To do so, unfortunately we have to edit the qmake.conf "mkspec"
+       for the compiler we're using. For example, if you're using Visual
+       Studio 2008, you would open the file 
+
+           $QTDIR\mkspecs\win32-msvc2008\qmake.conf
+
+       (where $QTDIR is the location of your Qt source code) and add "-Zi" to
+       QMAKE_CFLAGS_RELEASE. The resulting line would look like:
+
+ 
+           QMAKE_CFLAGS_RELEASE    = -Zi -O2 -MD -GL
+
+       You also need to add "/DEBUG /OPT:REF /OPT:ICF" to the list of linker
+       flags in QMAKE_LFLAGS_RELEASE. The resulting line should look like the
+       following:
+
+           QMAKE_LFLAGS_RELEASE    = /DEBUG /OPT:REF /OPT:ICF /INCREMENTAL:NO /LTCG
+  
+       Different Visual Studio mkspecs may look slightly different and have
+       different sets of default flags. Save your changes to qmake.conf after
+       you're done.
+
+  1.6. Configure the Qt source for a Visual Studio build, along with whatever
+       other options you want (e.g., OpenSSL support). As an example, I used
+       the following command to configure my Qt:
+  
+       $ configure.exe -platform win32-msvc2008 \
+                       -opensource -debug-and-release       \
+                       -fast -no-qt3support                 \
+                       -nomake demos -nomake examples       \
+                       -openssl -I E:\OpenSSL\9.8k\include  \
+                       -L E:\OpenSSL\9.8k\lib\VC 
+
+  1.7. After configure.exe is finished running, simply run nmake.exe to build
+       Qt. The entire process may take several hours depending on the options
+       used and the speed of your machine.
+
+2. Download and build the latest Breakpad source from Subversion
+  2.1. Fetch the latest Google Breakpad source code from Subversion
+    
+       $ svn cp http://google-breakpad.googlecode.com/svn/trunk/ breakpad
+
+  2.2. Open breakpad\src\client\windows\breakpad_client.sln
+  2.3. Right-click on the "crash_generation" project and select 'Properties'
+  2.4. Go to "Configuration Properties" -> "C/C++" -> "Language" and disable
+       "Treat wchar_t as Built-in Type".
+  2.5. Repeat 2.2 and 2.3 for the "crash_report_sender" and "exception_handler"
+       projects as well.
+  2.6. Right-click on the top-level solution and select "Build Solution" or
+       hit F7 to build the entire solution.
+
+3. Build Vidalia with Breakpad support enabled 
+  3.1. Fetch the latest 'breakpad' branch from Vidalia's Subversion repository.
+
+       $ svn co https://svn.vidalia-project.net/svn/vidalia/branches/breakpad vidalia-breakpad
+  
+  3.2. Use CMake to generate a Visual Studio .sln file for Vidalia with
+       Breakpad support enabled.
+       
+       $ mkdir build-vs
+       $ cd build-vs
+       $ cmake.exe -DUSE_BREAKPAD=1 \
+                   -DBREAKPAD_INCLUDE_DIR=E:\breakpad\src  \
+                   -DBREAKPAD_LIBRARY_DIR=E:\Breakpad\src\client\windows\Debug ..
+       
+       You will need to adjust the paths above to point to the location of
+       the Breakpad source directory and binaries you built in Step 2.
+  3.3. Open the solution file build-vs\Vidalia.sln with Visual Studio.
+  3.4. Right-click on the top-level solution and select "Build Solution", or
+       hit F7 to build the entire solution.

Added: vidalia/trunk/cmake/FindBreakpad.cmake
===================================================================
--- vidalia/trunk/cmake/FindBreakpad.cmake	                        (rev 0)
+++ vidalia/trunk/cmake/FindBreakpad.cmake	2009-06-23 22:53:29 UTC (rev 3879)
@@ -0,0 +1,50 @@
+##
+##  $Id$
+## 
+##  This file is part of Vidalia, and is subject to the license terms in the
+##  LICENSE file, found in the top level directory of this distribution. If 
+##  you did not receive the LICENSE file with this file, you may obtain it
+##  from the Vidalia source package distributed by the Vidalia Project at
+##  http://www.vidalia-project.net/. No part of Vidalia, including this file,
+##  may be copied, modified, propagated, or distributed except according to
+##  the terms described in the LICENSE file.
+##
+
+## Tries to find the required Google Breakpad libraries. Once done this will
+## define the variable BREAKPAD_LIBRARIES.
+
+message(STATUS "Looking for Google Breakpad libraries")
+if (WIN32)
+  if (MSVC)
+    find_library(BREAKPAD_EXCEPTION_HANDLER_LIB
+      NAMES exception_handler
+      PATHS ${BREAKPAD_LIBRARY_DIR}
+    )
+    if (NOT BREAKPAD_EXCEPTION_HANDLER_LIB)
+      message(FATAL_ERROR 
+              "Could not find Breakpad exception handler library")
+    endif(NOT BREAKPAD_EXCEPTION_HANDLER_LIB)
+
+    find_library(BREAKPAD_CRASH_GENERATION_LIB
+      NAMES crash_generation
+      PATHS ${BREAKPAD_LIBRARY_DIR}
+    )
+    if (NOT BREAKPAD_CRASH_GENERATION_LIB)
+      message(FATAL_ERROR
+              "Could not find Breakpad crash generation library")
+    endif(NOT BREAKPAD_CRASH_GENERATION_LIB)
+
+    set(BREAKPAD_LIBRARIES 
+      ${BREAKPAD_EXCEPTION_HANDLER_LIB}
+      ${BREAKPAD_CRASH_GENERATION_LIB}
+    )
+    message(STATUS "Looking for Google Breakpad libraries - found")
+  else(MSVC)
+    message(FATAL_ERROR
+            "Breakpad support on Windows currently requires Visual Studio.")
+  endif(MSVC)
+else(WIN32)
+  message(FATAL_ERROR
+          "Breakpad support is not currently available on your platform.")
+endif(WIN32)
+

Modified: vidalia/trunk/config.h.in
===================================================================
--- vidalia/trunk/config.h.in	2009-06-22 22:59:00 UTC (rev 3878)
+++ vidalia/trunk/config.h.in	2009-06-23 22:53:29 UTC (rev 3879)
@@ -34,5 +34,7 @@
 
 #cmakedefine USE_MARBLE
 
+#cmakedefine USE_BREAKPAD
+
 #endif
 

Modified: vidalia/trunk/src/CMakeLists.txt
===================================================================
--- vidalia/trunk/src/CMakeLists.txt	2009-06-22 22:59:00 UTC (rev 3878)
+++ vidalia/trunk/src/CMakeLists.txt	2009-06-23 22:53:29 UTC (rev 3879)
@@ -24,6 +24,24 @@
 
 if(MSVC)
   add_definitions(-D_USE_MATH_DEFINES=1)
+ 
+  ## Qt's Visual Studio builds explicitly disable treating wchar_t as a
+  ## built-in type, so we need to do that as well otherwise we get linker
+  ## errors due to name mangling if we try to use a wchar_t-related method
+  add_definitions(/Zc:wchar_t-)
+
+  ## Specify that we want the "W" version of Windows methods, such as
+  ## CreateFileW or WriteFileW
+  add_definitions(-DUNICODE=1)
+
+  ## Disable incremental linking for all build types, not just Release
+  ## and MinSizeRel
+  set(CMAKE_EXE_LINKER_FLAGS_DEBUG
+      "${CMAKE_EXE_LINKER_FLAGS_DEBUG} /INCREMENTAL:NO"
+  )
+  set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO
+      "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO} /INCREMENTAL:NO"
+  )
 endif(MSVC)
 
 ## Add some Qt definitions
@@ -39,6 +57,9 @@
   link_directories(${CMAKE_CURRENT_BINARY_DIR}/miniupnpc)
   add_subdirectory(miniupnpc)
 endif(USE_MINIUPNPC)
+if (USE_BREAKPAD)
+  add_subdirectory(crashreporter)
+endif(USE_BREAKPAD)
 
 add_subdirectory(common)
 add_subdirectory(tools)

Added: vidalia/trunk/src/crashreporter/CMakeLists.txt
===================================================================
--- vidalia/trunk/src/crashreporter/CMakeLists.txt	                        (rev 0)
+++ vidalia/trunk/src/crashreporter/CMakeLists.txt	2009-06-23 22:53:29 UTC (rev 3879)
@@ -0,0 +1,67 @@
+##
+##  $Id$
+##
+##  This file is part of Vidalia, and is subject to the license terms in the
+##  LICENSE file, found in the top level directory of this distribution. If
+##  you did not receive the LICENSE file with this file, you may obtain it
+##  from the Vidalia source package distributed by the Vidalia Project at
+##  http://www.vidalia-project.net/. No part of Vidalia, including this file,
+##  may be copied, modified, propagated, or distributed except according to
+##  the terms described in the LICENSE file.
+##
+
+include_directories(
+  ${CMAKE_CURRENT_BINARY_DIR}
+  ${BREAKPAD_INCLUDE_DIR}
+)
+
+## Add the application source files
+set(crashreporter_SRCS
+  main.cpp
+  CrashReportDialog.cpp
+  CrashReportUploader.cpp
+  UploadProgressDialog.cpp
+)
+qt4_wrap_cpp(crashreporter_SRCS
+  CrashReportDialog.h
+  CrashReportUploader.h
+  UploadProgressDialog.h
+)
+qt4_wrap_ui(crashreporter_SRCS
+  CrashReportDialog.ui
+  UploadProgressDialog.ui
+)
+qt4_add_resources(crashreporter_SRCS
+  res/CrashReporter.qrc
+)
+if (MSVC)
+  configure_file(
+    res/CrashReporter.rc.in
+    ${CMAKE_CURRENT_BINARY_DIR}/CrashReporter.rc
+  )
+  set(crashreporter_SRCS
+    ${crashreporter_SRCS}
+    ${CMAKE_CURRENT_BINARY_DIR}/CrashReporter.rc
+  )
+endif(MSVC)
+
+## Create the crashreporter executable
+if (WIN32)  
+  add_executable(crashreporter WIN32 ${crashreporter_SRCS})
+else(WIN32)
+  add_executable(crashreporter ${crashreporter_SRCS})
+endif(WIN32)
+
+## Link the crash reporter application with the Qt and Breakpad libraries
+target_link_libraries(crashreporter
+  ${QT_QTCORE_LIBRARY}
+  ${QT_QTGUI_LIBRARY}
+  ${QT_QTNETWORK_LIBRARY}
+  common
+)
+if (WIN32)
+  target_link_libraries(crashreporter
+    ${QT_QTMAIN_LIBRARY}
+  )
+endif(WIN32)
+

Added: vidalia/trunk/src/crashreporter/CrashReportDialog.cpp
===================================================================
--- vidalia/trunk/src/crashreporter/CrashReportDialog.cpp	                        (rev 0)
+++ vidalia/trunk/src/crashreporter/CrashReportDialog.cpp	2009-06-23 22:53:29 UTC (rev 3879)
@@ -0,0 +1,129 @@
+/*
+**  This file is part of Vidalia, and is subject to the license terms in the
+**  LICENSE file, found in the top level directory of this distribution. If you
+**  did not receive the LICENSE file with this file, you may obtain it from the
+**  Vidalia source package distributed by the Vidalia Project at
+**  http://www.vidalia-project.net/. No part of Vidalia, including this file,
+**  may be copied, modified, propagated, or distributed except according to the
+**  terms described in the LICENSE file.
+*/
+
+/*
+** \file CrashReportDialog.cpp
+** \version $Id$
+** \brief Dialog that asks the user whether they would like to
+** submit the crash report, along with optional additional details
+** about what they were doing at the time of the crash.
+*/
+
+#include "CrashReportDialog.h"
+#include "CrashReportUploader.h"
+#include "UploadProgressDialog.h"
+
+#include "stringutil.h"
+
+#include <QProcess>
+#include <QPushButton>
+#include <QMessageBox>
+#include <QFileInfo>
+
+
+CrashReportDialog::CrashReportDialog(QWidget *parent)
+  : QDialog(parent)
+{
+  ui.setupUi(this);
+
+  /* Tweak the text displayed on the buttons at the bottom of the dialog */
+  QPushButton *btn;
+  btn = ui.buttonBox->button(QDialogButtonBox::Ok);
+  btn->setText(tr("Restart Vidalia"));
+
+  btn = ui.buttonBox->button(QDialogButtonBox::Cancel);
+  btn->setText(tr("Don't Restart"));
+}
+
+void
+CrashReportDialog::setCrashAnnotations(const QHash<QString,QString> &annotations)
+{
+  _annotations = annotations;
+}
+
+void
+CrashReportDialog::setMinidump(const QString &id, const QByteArray &minidump)
+{
+  _minidump = minidump;
+  _minidumpId = id;
+}
+
+void
+CrashReportDialog::submitCrashReport()
+{
+  CrashReportUploader *uploader = new CrashReportUploader();
+  UploadProgressDialog *progressDialog = new UploadProgressDialog(this);
+  QMap<QString,QString> parameters;
+
+  connect(uploader, SIGNAL(statusChanged(QString)),
+          progressDialog, SLOT(setStatus(QString)));
+  connect(uploader, SIGNAL(uploadProgress(int, int)),
+          progressDialog, SLOT(setUploadProgress(int, int)));
+  connect(uploader, SIGNAL(uploadFinished()),
+          progressDialog, SLOT(accept()));
+  connect(uploader, SIGNAL(uploadFailed(QString)),
+          progressDialog, SLOT(uploadFailed(QString)));
+
+  /* Set up the form fields that will be uploaded with the minidump */
+  QString comments = ui.textDetails->toPlainText();
+  if (! comments.isEmpty())
+    parameters.insert("Comments", comments);
+  parameters.insert("ProductName", "Vidalia");
+  parameters.insert("Vendor", "Vidalia");
+  parameters.insert("Version", _annotations.value("BuildVersion"));
+  parameters.insert("CrashTime", _annotations.value("CrashTime"));
+  parameters.insert("StartupTime", _annotations.value("StartupTime"));
+
+  /* Start the upload (returns immediately) */
+  uploader->uploadMinidump(QUrl("https://crashes.vidalia-project.net/submit";),
+                           _minidumpId, _minidump, parameters);
+
+  /* Displays a modal progress dialog showing the progress of the upload. This
+   * will return when either the upload completes or the user hits "Cancel". */
+  if (progressDialog->exec() == QDialog::Rejected)
+    uploader->cancel(); /* User clicked "Cancel" */
+
+  delete uploader;
+}
+
+void
+CrashReportDialog::accept()
+{
+  /* Upload the crash report, unless the user opted out */
+  if (ui.chkSubmitCrashReport->isChecked())
+    submitCrashReport();
+
+  /* Attempt to restart Vidalia with the saved arguments */
+  QString exe  = _annotations.value("RestartExecutable");
+  QString args = _annotations.value("RestartExecutableArgs");
+  QStringList argList = string_parse_arguments(args);
+  if (! QProcess::startDetached(exe, argList, QFileInfo(exe).absolutePath())) {
+    QMessageBox dlg(QMessageBox::Warning, tr("Unable to restart Vidalia"),
+                    tr("We were unable to automatically restart Vidalia. "
+                       "Please restart Vidalia manually."),
+                    QMessageBox::Ok, this);
+    dlg.exec();
+  }
+
+  /* Close the dialog */
+  QDialog::accept();
+}
+
+void
+CrashReportDialog::reject()
+{
+  /* Upload the crash report, unless the user opted out */
+  if (ui.chkSubmitCrashReport->isChecked())
+    submitCrashReport();
+
+  /* Close this dialog without restarting Vidalia */
+  QDialog::reject();
+}
+

Added: vidalia/trunk/src/crashreporter/CrashReportDialog.h
===================================================================
--- vidalia/trunk/src/crashreporter/CrashReportDialog.h	                        (rev 0)
+++ vidalia/trunk/src/crashreporter/CrashReportDialog.h	2009-06-23 22:53:29 UTC (rev 3879)
@@ -0,0 +1,92 @@
+/*
+**  This file is part of Vidalia, and is subject to the license terms in the
+**  LICENSE file, found in the top level directory of this distribution. If you
+**  did not receive the LICENSE file with this file, you may obtain it from the
+**  Vidalia source package distributed by the Vidalia Project at
+**  http://www.vidalia-project.net/. No part of Vidalia, including this file,
+**  may be copied, modified, propagated, or distributed except according to the
+**  terms described in the LICENSE file.
+*/
+
+/*
+** \file CrashReportDialog.h
+** \version $Id$
+** \brief Dialog that asks the user whether they would like to
+** submit the crash report, along with optional additional details
+** about what they were doing at the time of the crash.
+*/
+
+#include "ui_CrashReportDialog.h"
+
+#include <QHash>
+#include <QByteArray>
+
+class QString;
+
+
+class CrashReportDialog : public QDialog
+{
+  Q_OBJECT
+
+public:
+  /** Default constructor.
+   */
+  CrashReportDialog(QWidget *parent = 0);
+
+  /** Sets the crash <b>annotations</b> key-value pairs associated with
+   * the generated minidump.
+   */
+  void setCrashAnnotations(const QHash<QString,QString> &annotations);
+
+  /** Sets the <b>minidump</b> contents generated by the crashed
+   * applications exception handler.
+   */
+  void setMinidump(const QString &id, const QByteArray &minidump);
+
+  /** Uploads the generated minidump, user comments, and any additional
+   * crash annotations generated by the exception handler to the crash
+   * reporting server.
+   * \sa setMinidump()
+   * \sa setCrashAnnotations()
+   */
+  void submitCrashReport();
+
+public slots:
+  /** Called when the user clicks the "Restart Vidalia" button on the
+   * dialog. If the "Submit my crash report..." checkbox is checked, it
+   * will first attempt to submit the crash report. After that is complete,
+   * it will try to restart the Vidalia process with any arguments specified
+   * in the crash annotations file.
+   * \sa setCrashAnnotations()
+   */
+  virtual void accept();
+
+  /** Called when the user clicks the "Don't Restart" button on the
+   * dialog. If the "Submit my crash report.." checkbox is checked, it
+   * will attempt to submit the crash report and then exit without
+   * restarting Vidalia.
+   */
+  virtual void reject();
+
+private:
+  /** Each minidump is given a randomly-generated GUID when it is created,
+   * which is used to form the minidump filename. This ID is also used by
+   * the crash reporting server when accepting and processing uploaded
+   * minidumps.
+   */
+  QString _minidumpId;
+
+  /** Contents of the generated minidump.
+   */
+  QByteArray _minidump;
+
+  /** Set of parsed key-value pairs generated by the crashed application's
+   * exception handler and written alongside the minidump.
+   */
+  QHash<QString,QString> _annotations;
+
+  /** Qt Designer created object.
+   */
+  Ui::CrashReportDialog ui;
+};
+

Added: vidalia/trunk/src/crashreporter/CrashReportDialog.ui
===================================================================
--- vidalia/trunk/src/crashreporter/CrashReportDialog.ui	                        (rev 0)
+++ vidalia/trunk/src/crashreporter/CrashReportDialog.ui	2009-06-23 22:53:29 UTC (rev 3879)
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>CrashReportDialog</class>
+ <widget class="QDialog" name="CrashReportDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>434</width>
+    <height>310</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Submit a Crash Report</string>
+  </property>
+  <property name="windowIcon">
+   <iconset resource="res/CrashReporter.qrc">
+    <normaloff>:/images/32x32/tools-report-bug.png</normaloff>:/images/32x32/tools-report-bug.png</iconset>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="0" rowspan="2">
+    <widget class="QLabel" name="lblHeaderImage">
+     <property name="text">
+      <string/>
+     </property>
+     <property name="pixmap">
+      <pixmap resource="res/CrashReporter.qrc">:/images/64x64/tools-report-bug.png</pixmap>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="1">
+    <widget class="QLabel" name="lblTitle">
+     <property name="font">
+      <font>
+       <pointsize>9</pointsize>
+       <weight>75</weight>
+       <bold>true</bold>
+      </font>
+     </property>
+     <property name="text">
+      <string>Vidalia encountered an error and needed to close</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+     </property>
+     <property name="wordWrap">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="1">
+    <widget class="QLabel" name="lblHeader">
+     <property name="text">
+      <string>A crash report has been created that you can automatically send to the Vidalia developers to help identify and fix the problem. The submitted report does not contain any personally identifying information, but your connection to the crash reporting server may not be anonymous.</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+     </property>
+     <property name="wordWrap">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="0" colspan="2">
+    <widget class="Line" name="line">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="0" colspan="2">
+    <widget class="QLabel" name="lblDetails">
+     <property name="text">
+      <string>Please also describe what you were doing before the application crashed (optional):</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
+     </property>
+     <property name="wordWrap">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="0" colspan="2">
+    <widget class="QTextBrowser" name="textDetails">
+     <property name="tabChangesFocus">
+      <bool>true</bool>
+     </property>
+     <property name="readOnly">
+      <bool>false</bool>
+     </property>
+     <property name="openLinks">
+      <bool>false</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="5" column="0" colspan="2">
+    <widget class="QCheckBox" name="chkSubmitCrashReport">
+     <property name="text">
+      <string>Send my crash report to the Vidalia developers</string>
+     </property>
+     <property name="checked">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="6" column="1">
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources>
+  <include location="res/CrashReporter.qrc"/>
+ </resources>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>CrashReportDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>CrashReportDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>

Added: vidalia/trunk/src/crashreporter/CrashReportUploader.cpp
===================================================================
--- vidalia/trunk/src/crashreporter/CrashReportUploader.cpp	                        (rev 0)
+++ vidalia/trunk/src/crashreporter/CrashReportUploader.cpp	2009-06-23 22:53:29 UTC (rev 3879)
@@ -0,0 +1,153 @@
+/*
+**  This file is part of Vidalia, and is subject to the license terms in the
+**  LICENSE file, found in the top level directory of this distribution. If you
+**  did not receive the LICENSE file with this file, you may obtain it from the
+**  Vidalia source package distributed by the Vidalia Project at
+**  http://www.vidalia-project.net/. No part of Vidalia, including this file,
+**  may be copied, modified, propagated, or distributed except according to the
+**  terms described in the LICENSE file.
+*/
+
+/*
+** \file CrashReportUploader.cpp
+** \version $Id$
+** \brief Uploads a minidump file and any extra information to a crash
+** reporting server.
+*/
+
+#include "CrashReportUploader.h"
+
+#include <QtGlobal>
+#include <QDateTime>
+#include <QSslSocket>
+#include <QSslCertificate>
+#include <QHttpRequestHeader>
+#include <QHttpResponseHeader>
+#include <QMap>
+#include <QUrl>
+
+
+CrashReportUploader::CrashReportUploader(QObject *parent)
+  : QObject(parent),
+    _requestId(-1)
+{
+  /* Clear the default CA certificate store and add the only one we want */
+  QSslSocket::setDefaultCaCertificates(QList<QSslCertificate>());
+  QSslSocket::addDefaultCaCertificates(":/pki/gd-class2-root.crt");
+
+  /* Create the QHttp object used to talk to the crash reporting server */
+  _http = new QHttp(this);
+  connect(_http, SIGNAL(stateChanged(int)),
+          this, SLOT(httpStateChanged(int)));
+  connect(_http, SIGNAL(requestFinished(int, bool)),
+          this, SLOT(httpRequestFinished(int, bool)));
+  connect(_http, SIGNAL(dataSendProgress(int, int)),
+          this, SIGNAL(uploadProgress(int, int)));
+
+  /* Seed the lame PRNG we'll use to generate the multipart boundary marker */
+  qsrand(QDateTime::currentDateTime().toTime_t());
+}
+
+void
+CrashReportUploader::uploadMinidump(const QUrl &serverUrl,
+                                    const QString &minidumpId,
+                                    const QByteArray &minidump,
+                                    const QMap<QString,QString> &parameters)
+{
+  QByteArray body;
+
+  /* Set the destination host. If it looks like the destination URL uses SSL,
+   * then we need to tell the QHttp object to use it as well. */
+  if (! serverUrl.scheme().compare("https", Qt::CaseInsensitive)) {
+    _http->setHost(serverUrl.host(), QHttp::ConnectionModeHttps,
+                   serverUrl.port(443));
+  } else {
+    _http->setHost(serverUrl.host(), QHttp::ConnectionModeHttp,
+                   serverUrl.port(80));
+  }
+
+  /* Set up the request header */
+  QHttpRequestHeader header("POST", serverUrl.path());
+  QString boundary = generateBoundaryMarker();
+  header.setValue("Host", serverUrl.host());
+  header.setContentType(QString("multipart/form-data; boundary=%1")
+                                                    .arg(boundary));
+
+  /* Add all the key=value parameters to the request body */
+  foreach (QString key, parameters.keys()) {
+    QString value = parameters.value(key);
+    if (! value.isEmpty()) {
+      body.append(QString("--%1\r\n").arg(boundary));
+      body.append(QString("Content-Disposition: form-data; name=\"%1\"").arg(key));
+      body.append("\r\n\r\n");
+      body.append(value.toUtf8());
+      body.append("\r\n");
+    }
+  }
+
+  /* Append the minidump contents */
+  body.append(QString("--%1\r\n").arg(boundary));
+  body.append("Content-Disposition: form-data; ");
+  body.append("name=\"upload_file_minidump\"; ");
+  body.append(QString("filename=\"%1\"\r\n").arg(minidumpId));
+  body.append("Content-Type: application/octet-stream\r\n\r\n");
+  body.append(minidump);
+  body.append(QString("\r\n--%1\r\n").arg(boundary));
+
+  /* Initiate the request and return the request ID */
+  _requestId = _http->request(header, body);
+}
+
+QString
+CrashReportUploader::generateBoundaryMarker() const
+{
+  return QString("%1%2").arg((quint32)qrand(), 8, 16, QChar('0'))
+                        .arg((quint32)qrand(), 8, 16, QChar('0'));  
+}
+
+void
+CrashReportUploader::cancel()
+{
+  _http->abort();
+}
+
+void
+CrashReportUploader::httpStateChanged(int state)
+{
+  switch (state) {
+    case QHttp::Connecting:
+      emit statusChanged(tr("Connecting..."));
+      break;
+
+    case QHttp::Sending:
+      emit statusChanged(tr("Sending crash report..."));
+      break;
+
+    case QHttp::Reading:
+      emit statusChanged(tr("Receiving response..."));
+      break;
+
+    default:
+      break;
+  }
+}
+
+void
+CrashReportUploader::httpRequestFinished(int id, bool error)
+{
+  if (id != _requestId)
+    return;
+
+  if (error) {
+    QString errorString = _http->errorString();
+    emit uploadFailed(errorString);
+  } else {
+    QHttpResponseHeader response = _http->lastResponse();
+    if (response.statusCode() == 200)
+      emit uploadFinished();
+    else
+      emit uploadFailed(response.reasonPhrase());
+  }
+  _http->close();
+}
+

Added: vidalia/trunk/src/crashreporter/CrashReportUploader.h
===================================================================
--- vidalia/trunk/src/crashreporter/CrashReportUploader.h	                        (rev 0)
+++ vidalia/trunk/src/crashreporter/CrashReportUploader.h	2009-06-23 22:53:29 UTC (rev 3879)
@@ -0,0 +1,110 @@
+/*
+**  This file is part of Vidalia, and is subject to the license terms in the
+**  LICENSE file, found in the top level directory of this distribution. If you
+**  did not receive the LICENSE file with this file, you may obtain it from the
+**  Vidalia source package distributed by the Vidalia Project at
+**  http://www.vidalia-project.net/. No part of Vidalia, including this file,
+**  may be copied, modified, propagated, or distributed except according to the
+**  terms described in the LICENSE file.
+*/
+
+/*
+** \file CrashReportUploader.h
+** \version $Id$
+** \brief Uploads a minidump file and any extra information to a crash
+** reporting server.
+*/
+
+#ifndef _CRASHREPORTUPLOADER_H
+#define _CRASHREPORTUPLOADER_H
+
+#include <QObject>
+#include <QHttp>
+#include <QMap>
+
+class QUrl;
+class QString;
+class QByteArray;
+
+
+class CrashReportUploader : public QObject
+{
+  Q_OBJECT
+
+public:
+  /** Default constructor.
+   */
+  CrashReportUploader(QObject *parent = 0);
+
+  /** Starts uploading <b>minidump</b> to <b>serverUrl</b> and returns
+   * immediately. <b>minidumpId</b> is the minidump GUID generated by
+   * the exception handler and used for the minidump's filename. The
+   * optional <b>parameters</b> can be used to pass additional form fields
+   * to the crash reporting server.
+   */
+  void uploadMinidump(const QUrl &serverUrl,
+                      const QString &minidumpId,
+                      const QByteArray &minidump,
+                      const QMap<QString,QString> &parameters);
+
+public slots:
+  /** Cancels a pending minidump upload.
+   */
+  void cancel();
+
+signals:
+  /** Emitted when the underlying QHttp object posts data to the server.
+   * <b>done</b> indicates how many bytes out of <b>total</b>
+   * have been sent so far.
+   */
+  void uploadProgress(int done, int total);
+
+  /** Emitted when the status of the POST request changes. <b>status</b>
+   * describes the new current state of the request.
+   */
+  void statusChanged(const QString &status);
+
+  /** Emitted when the previous minidump upload completes successfully.
+   */
+  void uploadFinished();
+
+  /** Emitted when the previous crash report upload fails. The QString
+   * <b>error</b> is a human-readable string describing the error
+   * encountered.
+   */
+  void uploadFailed(const QString &error);
+
+private slots:
+  /** Called when the state of the underlying QHttp object changes. A
+   * statusChanged() signal is emitted with the appropriate text
+   * describing the new state of the POST request.
+   */
+  void httpStateChanged(int state); 
+
+  /** Called when the underlying QHttp object used to upload the minidump
+   * completes. <b>error</b> is set to false if the upload was
+   * successful, or true if the upload failed. If <b>id</b> does not
+   * match the request ID previously returned by QHttp::get(), then the
+   * signal is ignored since it is the result of a close() or abort()
+   * request.
+   */
+  void httpRequestFinished(int id, bool error);
+
+private:
+  /** Generates a "random" 8-byte multipart boundary marker encoded into
+   * 16 hex characters.
+   */
+  QString generateBoundaryMarker() const;
+
+  /** Unique numeric identifier of the current minidump upload POST request.
+   */
+  int _requestId;
+
+  /** Object used to POST a minidump to the crash server and read the
+   * response.
+   */
+  QHttp *_http;
+};
+
+#endif
+

Added: vidalia/trunk/src/crashreporter/UploadProgressDialog.cpp
===================================================================
--- vidalia/trunk/src/crashreporter/UploadProgressDialog.cpp	                        (rev 0)
+++ vidalia/trunk/src/crashreporter/UploadProgressDialog.cpp	2009-06-23 22:53:29 UTC (rev 3879)
@@ -0,0 +1,61 @@
+/*
+**  This file is part of Vidalia, and is subject to the license terms in the
+**  LICENSE file, found in the top level directory of this distribution. If you
+**  did not receive the LICENSE file with this file, you may obtain it from the
+**  Vidalia source package distributed by the Vidalia Project at
+**  http://www.vidalia-project.net/. No part of Vidalia, including this file,
+**  may be copied, modified, propagated, or distributed except according to the
+**  terms described in the LICENSE file.
+*/
+
+/*
+** \file UploadProgressDialog.cpp
+** \version $Id$
+** \brief Displays the progress of uploading a crash report to the server
+*/
+
+#include "UploadProgressDialog.h"
+
+
+UploadProgressDialog::UploadProgressDialog(QWidget *parent)
+  : QDialog(parent)
+{
+  ui.setupUi(this);
+ 
+  setModal(true);
+}
+
+void
+UploadProgressDialog::setVisible(bool visible)
+{
+  if (visible) {
+    ui.progressBar->setRange(0, 0);
+    ui.buttonBox->setStandardButtons(QDialogButtonBox::Cancel);
+  }
+  QDialog::setVisible(visible);
+}
+
+void
+UploadProgressDialog::setStatus(const QString &status)
+{
+  ui.lblStatus->setText(status);
+}
+
+void
+UploadProgressDialog::setUploadProgress(int done, int total)
+{
+  ui.progressBar->setRange(0, total);
+  ui.progressBar->setValue(done);
+}
+
+void
+UploadProgressDialog::uploadFailed(const QString &error)
+{
+  ui.lblStatus->setText(tr("Unable to send report: %1").arg(error));
+
+  ui.progressBar->setRange(0, 1);
+  ui.progressBar->setValue(1);
+
+  ui.buttonBox->setStandardButtons(QDialogButtonBox::Ok);
+}
+

Added: vidalia/trunk/src/crashreporter/UploadProgressDialog.h
===================================================================
--- vidalia/trunk/src/crashreporter/UploadProgressDialog.h	                        (rev 0)
+++ vidalia/trunk/src/crashreporter/UploadProgressDialog.h	2009-06-23 22:53:29 UTC (rev 3879)
@@ -0,0 +1,63 @@
+/*
+**  This file is part of Vidalia, and is subject to the license terms in the
+**  LICENSE file, found in the top level directory of this distribution. If you
+**  did not receive the LICENSE file with this file, you may obtain it from the
+**  Vidalia source package distributed by the Vidalia Project at
+**  http://www.vidalia-project.net/. No part of Vidalia, including this file,
+**  may be copied, modified, propagated, or distributed except according to the
+**  terms described in the LICENSE file.
+*/
+
+/*
+** \file UploadProgressDialog.cpp
+** \version $Id$
+** \brief Displays the progress of uploading a crash report to the server
+*/
+
+#ifndef _UPLOADPROGRESSDIALOG_H
+#define _UPLOADPROGRESSDIALOG_H
+
+#include "ui_UploadProgressDialog.h"
+
+
+class UploadProgressDialog : public QDialog
+{
+  Q_OBJECT
+
+public:
+  /** Default constructor.
+   */
+  UploadProgressDialog(QWidget *parent = 0);
+
+public slots:
+  /** Sets the status message text in the dialog to <b>status</b>.
+   */
+  void setStatus(const QString &status);
+
+  /** Updates the minidump upload progress bar to <b>value</b> out of
+   * <b>maximum</b> steps. If <b>value</b> and <b>maximum</b> are both 0,
+   * then a "busy" progress bar is displayed.
+   */
+  void setUploadProgress(int value, int maximum);
+
+  /** Called when the minidump upload fails. The string <b>error</b>
+   * provides a human-readable description of the reason the upload
+   * failed, which is displayed for the user.
+   */
+  void uploadFailed(const QString &error);
+
+protected:
+  /** Overloaded method called when the progress dialog is first shown in
+   * order to initialize the progress bar, status text and dialog button
+   * box.
+   */
+  virtual void setVisible(bool visible);
+
+private:
+  /** Qt Designer generated object.
+   */
+  Ui::UploadProgressDialog ui;
+};
+
+#endif
+

Added: vidalia/trunk/src/crashreporter/UploadProgressDialog.ui
===================================================================
--- vidalia/trunk/src/crashreporter/UploadProgressDialog.ui	                        (rev 0)
+++ vidalia/trunk/src/crashreporter/UploadProgressDialog.ui	2009-06-23 22:53:29 UTC (rev 3879)
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>UploadProgressDialog</class>
+ <widget class="QDialog" name="UploadProgressDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>366</width>
+    <height>104</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Uploading Crash Report</string>
+  </property>
+  <property name="windowIcon">
+   <iconset resource="res/CrashReporter.qrc">
+    <normaloff>:/images/32x32/tools-report-bug.png</normaloff>:/images/32x32/tools-report-bug.png</iconset>
+  </property>
+  <property name="modal">
+   <bool>true</bool>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="0" rowspan="2">
+    <widget class="QLabel" name="lblPixmap">
+     <property name="text">
+      <string/>
+     </property>
+     <property name="pixmap">
+      <pixmap resource="res/CrashReporter.qrc">:/images/48x48/tools-report-bug.png</pixmap>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="1">
+    <widget class="QLabel" name="lblStatus">
+     <property name="text">
+      <string/>
+     </property>
+     <property name="wordWrap">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="1">
+    <widget class="QProgressBar" name="progressBar">
+     <property name="value">
+      <number>0</number>
+     </property>
+     <property name="textVisible">
+      <bool>false</bool>
+     </property>
+     <property name="format">
+      <string/>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="0" colspan="2">
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources>
+  <include location="res/CrashReporter.qrc"/>
+ </resources>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>UploadProgressDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>182</x>
+     <y>81</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>182</x>
+     <y>51</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>UploadProgressDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>182</x>
+     <y>81</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>182</x>
+     <y>51</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>

Added: vidalia/trunk/src/crashreporter/main.cpp
===================================================================
--- vidalia/trunk/src/crashreporter/main.cpp	                        (rev 0)
+++ vidalia/trunk/src/crashreporter/main.cpp	2009-06-23 22:53:29 UTC (rev 3879)
@@ -0,0 +1,182 @@
+/*
+**  This file is part of Vidalia, and is subject to the license terms in the
+**  LICENSE file, found in the top level directory of this distribution. If you
+**  did not receive the LICENSE file with this file, you may obtain it from the
+**  Vidalia source package distributed by the Vidalia Project at
+**  http://www.vidalia-project.net/. No part of Vidalia, including this file,
+**  may be copied, modified, propagated, or distributed except according to the
+**  terms described in the LICENSE file.
+*/
+
+/*
+** \file main.cpp
+** \version $Id$
+** \brief Application that is run after Vidalia crashes and asks the
+** user if they would like to submit the crash report.
+*/
+
+#include "CrashReportDialog.h"
+
+#include <QApplication>
+#include <QFileInfo>
+#include <QMessageBox>
+#include <QTextStream>
+#include <QTextCodec>
+
+
+/** Open the minidump file  given by <b>fileName</b> and read its entire
+ * contents into a QByteArray. Returns the contents of the minidump file
+ * on success. If an error occurs, this returns a default-constructed
+ * QByteArray and sets <b>errorMessage</b> to a string describing the error
+ * ecountered.
+ */
+QByteArray
+read_minidump_file(const QString &fileName, QString &errorMessage)
+{
+  QByteArray md;
+  QFile mdFile(fileName);
+
+  if (! mdFile.open(QIODevice::ReadOnly)) {
+    errorMessage = mdFile.errorString();
+    return QByteArray();
+  }
+  while (! mdFile.atEnd()) {
+    md.append(mdFile.readAll());
+    if (mdFile.error() != QFile::NoError) {
+      errorMessage = mdFile.errorString();
+      return QByteArray();
+    }
+  }
+  mdFile.close();
+  return md;
+}
+
+/** Read the crash dump annotations file given by <b>fileName</b> and parse
+ * each line into a Key=Value pair. Returns a QHash containing the keys
+ * mapped to their values on success. If a file or parse error occurs, this
+ * returns a default-constructed QHash and sets <b>errorMessage</b> to a
+ * string describing the error encountered.
+ */
+QHash<QString,QString>
+read_annotations_file(const QString &fileName, QString &errorMessage)
+{
+  QHash<QString, QString> annotations;
+
+  /* Open the annotations file for reading text */
+  QFile infile(fileName);
+  if (! infile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+    errorMessage = infile.errorString();
+    return QHash<QString,QString>();
+  }
+
+  /* The annotations file should be UTF-8 encoded */
+  QTextStream reader(&infile);
+  reader.setCodec(QTextCodec::codecForName("utf-8"));
+  while (! reader.atEnd()) {
+    QString line = reader.readLine().trimmed();
+    if (line.isEmpty())
+      continue;
+
+    int idx = line.indexOf("=");
+    if (idx > 0 && idx < line.length()-1) {
+      QString key = line.mid(0, idx).trimmed();
+      QString val = line.mid(idx + 1).trimmed();
+      annotations.insert(key, val); 
+    }
+  }
+  return annotations;
+}
+
+int
+main(int argc, char *argv[])
+{
+  QApplication app(argc, argv);
+  CrashReportDialog crashDialog;
+  QFileInfo minidumpFile, extraInfoFile;
+  QString minidumpFilePath, extraInfoFilePath, errorMessage;
+  QHash<QString,QString> annotations;
+  QByteArray minidump;
+
+  if (argc < 2) {
+    errorMessage = "No minidump file specified.";
+    goto err;
+  }
+
+  /* Ensure that the specified minidump file exists and is readable */
+  minidumpFile     = QFileInfo(argv[1]);
+  minidumpFilePath = minidumpFile.absoluteFilePath();
+  if (! minidumpFile.exists() || ! minidumpFile.size()) {
+    errorMessage = QString("The specified minidump file does not exist: %1")
+                                                      .arg(minidumpFilePath);
+    goto err;
+  }
+  if (! minidumpFile.isReadable()) {
+    errorMessage = QString("The specified minidump file is not readable: %1")
+                                                       .arg(minidumpFilePath);
+    goto err;
+  }
+
+  /* Ensure that the specified minidump has an associated extra crash
+   * information file that exists and is readable. */
+  extraInfoFile     = QFileInfo(minidumpFilePath + ".info");
+  extraInfoFilePath = extraInfoFile.absoluteFilePath();
+  if (! extraInfoFile.exists() || ! extraInfoFile.size()) {
+    errorMessage = QString("The specified minidump does not have a "
+                           "corresponding crash annotations file: %1")
+                                               .arg(extraInfoFilePath);
+    goto err;
+  }
+  if (! extraInfoFile.isReadable()) {
+    errorMessage = QString("The specified crash information file is not "
+                           "readable: %1").arg(extraInfoFilePath);
+    goto err;
+  }
+
+  /* Read the minidump file's contents */
+  minidump = read_minidump_file(minidumpFilePath, errorMessage);
+  if (minidump.isNull()) {
+    errorMessage = QString("Unable to read minidump file '%1': %2")
+                                             .arg(minidumpFilePath)
+                                             .arg(errorMessage);
+    goto err;
+  }
+  /* Read and parse the crash annotations file */
+  annotations = read_annotations_file(extraInfoFilePath, errorMessage);
+  if (annotations.isEmpty()) {
+    errorMessage = QString("Unable to read crash annotations file '%1': %2")
+                                                     .arg(extraInfoFilePath)
+                                                     .arg(errorMessage);
+    goto err;
+  }
+
+  /* Display the crash reporting dialog */
+  crashDialog.setMinidump(minidumpFile.baseName(), minidump);
+  crashDialog.setCrashAnnotations(annotations);
+  crashDialog.show();
+  return app.exec();
+
+err:
+  /* We encountered an error trying to load the minidump or extra crash
+   * information file. So, display an error and then bail, since now there's
+   * nothing for us to send. */
+  QMessageBox dlg;
+  dlg.setWindowIcon(QIcon(":/images/32x32/tools-report-bug.png"));
+  dlg.setWindowTitle("Crash Reporter Error");
+
+  dlg.setIconPixmap(QPixmap(":/images/64x64/tools-report-bug.png"));
+  dlg.setStandardButtons(QMessageBox::Ok);
+
+  dlg.setText("<b>Vidalia encountered an error and needed to close</b>");
+  dlg.setInformativeText(
+    "<p>Vidalia attempted to automatically create an error report to "
+    "help diagnose the problem, but was unable to do so. Please report "
+    "this problem, along with what you were doing before Vidalia crashed, "
+    "to the developers at:</p><p>"
+    "<a href=\"https://trac.vidalia-project.net/wiki/ReportingBugs\";>"
+    "https://trac.vidalia-project.net/wiki/ReportingBugs</a></p> "
+    "<p>Click \"Show Details\" below for information about the problem "
+    "encountered.");
+
+  dlg.setDetailedText(errorMessage);
+  return dlg.exec();
+}

Added: vidalia/trunk/src/crashreporter/res/32x32/tools-report-bug.png
===================================================================
--- vidalia/trunk/src/crashreporter/res/32x32/tools-report-bug.png	                        (rev 0)
+++ vidalia/trunk/src/crashreporter/res/32x32/tools-report-bug.png	2009-06-23 22:53:29 UTC (rev 3879)
@@ -0,0 +1,12 @@
+�PNG
+
++E���R�͖��\)�͝���ʆHMEVaND8	�A�|�2iĞ��=�`�Q�� ����.ڰ0��>��
+�����h�p'+`%�BNj��R�Q�z��{S�������DEEE{�޵[�z��ӦZ#9�����I���"1���ց�2�)�C
+��O�]]zADCA1��K��߿�׏p�+tZ&��Z��M��^���P�<%�F\ No newline at end of file

Added: vidalia/trunk/src/crashreporter/res/48x48/tools-report-bug.png
===================================================================
--- vidalia/trunk/src/crashreporter/res/48x48/tools-report-bug.png	                        (rev 0)
+++ vidalia/trunk/src/crashreporter/res/48x48/tools-report-bug.png	2009-06-23 22:53:29 UTC (rev 3879)
@@ -0,0 +1,34 @@
+�PNG
+
++�b�����2
+,).X*
+�K�l"[5� ��$M�<��Yڙޙ���{��yQ"�
+!��ڔ+o�X��3��Mp��c�??wPP�__ߴ��@Pbb�PJ�S3�{`�ZGEFF���O[��b��+Qj�&q�c{+5��RwЏw:�gQD�6b�X���&@0,�%}�˹L�R���Z��T��������7##������9!��K`���=��@���;��7����R�
+Wj'DDD		�O����f��$Fk��(..nKTT��+jt6zuq���EI�bcc�uyn�I�+���Xg����
+��q���}z��{7x�쒒'&L�ɓ'KYY��\.�j+V�Tt���榡�.hⲰ���������n!555�g�7n�t�t0���K������Sj��U�W5����I(�7!��Ȝ9s���Z*++
+7"#���/�P��q�$0�Ң�������O��l����
+2!6ۗ��t���.k��lZ��)I�+ʒ1�r��<[ʊ��R�CFߔ!����,\^�J���Aw�WS�&�����y�x�����?H�.���RRR"sg�+���T08Θ�j��)?"��@��I�6���&��W�UG���O΄�ɺ
+� ?OH�ҵkW�ϗ������&+�>��Χe��H9I*����\y�Q㱺/H�|+)�0�
+5{ޥ��-Pm�g#�ɂ�e��8M��T���Qr��� 
+Cn���8���x
+�;]u��tpE�fh˻�*���R��O;"�rrr�������z.5]�edI�+�[�e������r1pt')hE���u��g���Cs����ڕ����������O��d�,��R*
+%]�ٟ�$q�������\�6Ƨ��cZm����l-�ʃ���d�J�� �����q		����������S3$��h���;p>�s�����3���c��������Ip:��T��մ�C,x����r��)� �k2����Z%0ރ�&Bn��H,[�����qE����2y��@�̃������}�{>�Cfn��QR��DÍ��~�u))�Uk������P����������5������Z\r���J�5�I���F�`'����%�mh��-Ջ���
+��h_�ܢA��i��/d�B�ˋ�-�����`=��S�����?�@Яd�A����	�r�=���n-��GMuagYLV3��&k�A��Bq:�����+�&��8��s�	�%
+	��h}������L�F:�ب�d
+��:&Pt�2}��~��}��cV�u{�u7���	mJ3Vk?������kY&�3�ڞ�ԯu�|���������`���o����[����Ak��\ No newline at end of file

Added: vidalia/trunk/src/crashreporter/res/64x64/tools-report-bug.png
===================================================================
--- vidalia/trunk/src/crashreporter/res/64x64/tools-report-bug.png	                        (rev 0)
+++ vidalia/trunk/src/crashreporter/res/64x64/tools-report-bug.png	2009-06-23 22:53:29 UTC (rev 3879)
@@ -0,0 +1,39 @@
+�PNG
+
++^�����>��+������{{{�HT_��uKG�A�0��>O���(�\���;��A
+�t
+��������z�x�1�@aBN�%2S������Xb`e��#-:�������q�qʮ�0���|�+`7��ǀ׃?����<9F|L�$1�.�Ih���o���������K�V2��<#�/]*�͏
+����!�Q"���C�򝕾X-R���ߩSQ9wDY�m)�06>\���s��4�1i	_N$J� �E�ߝL����3g�T�;�����������KY�@��
+��AC+�rQT��V[i�U�r
+�ho/Bb�+w�1:۳�4jԕIS����X�����`%�M���m!�6
+:\&��h{�	�C�ôT��-��	j~�����
+}�a�+��k+ 4���H>��_U�O>Y��Qb�h���(�%-�����ȫ{:H콦��$�h�����f�梒΀be"����$�Q����V0���V9��%Wd��*��HD��K���D��Q��ו�L�`"��Lw�u]�i+����*B�p�������M+	�I, Y���/��i���
+�G��s�v��.e)�T��"��i�Kv�=�4�Pzl<P��<�\)^�P�=*܌ܕ'��	�$��������yl��h��s<}O#}�zT���?�]w���?;3�ڨ�9�e�2���<yna���������Y8u�-�2�|w%W��@?���-'��npW��qs��
+e�4�2�&W�ɍ�i��T�Ј�@|��R;�]v��!w��;Px�}[j,:�>���iY�7� z4��Ѱ0̟��/mbp[ϭ�;\ No newline at end of file

Added: vidalia/trunk/src/crashreporter/res/CrashReporter.ico
===================================================================
--- vidalia/trunk/src/crashreporter/res/CrashReporter.ico	                        (rev 0)
+++ vidalia/trunk/src/crashreporter/res/CrashReporter.ico	2009-06-23 22:53:29 UTC (rev 3879)
@@ -0,0 +1,530 @@
++
+(+
+[+	�+�+
+�+
+�
+���++?��ݙ+
++�n�k
+
+j���+
+?�p��
+����+B��r�q
+y��m��
+	"
++
++++Q��+��
+�+
+S�+
++
++
++
+� �%J�+
+"+%+
+�+ 	 ++++ ++V�
++++
+
+����+
+��+
+��+
+
+��+���+
+
+��11j�$!������P��		������+
+�		!�H���+
+��=�g���+��W�..��+
+
+����
+
+
+��		^�+
+,�		5��+	��
+`������-�
+
+4���+	������I�������F�+
+?4+��$"�����������++��
+
+4.++
+��%%%�&&&�***�AAr�#���++
+++
+
+L�***���
+���������+
+
+
+N��������111�111�<<<�222�:RU�>x��+��
+
+P+�(��������������
+
+
+�
+
+
+����,��+:M����ddd����������OOO�����			��+aw�,4��TTT�������������CCC�����			��++�������	��� -2�TTT�ddd�LLL�   ����
+
+
+��`��+
+
+������(.����
+��$��'���+
+K++
+c}+++ + ��+
+
+
+
+++	G�+
+
+				)��
+
+Y		+
+Y�+   �###�s	"	 ++
+]�+	+	&	0++=�
+�+
+
+D
+
+++
+
+G'+
+
+���+	?� M!{�'V�*�;���+
+�+	(
++	,
+
+
+�.�31���+
+�+
+�+
+�,�d�+
+��Z�		�
+
+�		(�e���+
+�		(�s�+
+���
+
+���
+
+
+�
+
+���+
+<�����9����
+
+(�N���+
+���
+
+
+����}�+
+O������
+
+w��������+	�����3����3���+G'+oK""�!!�+kD��//>�C���+)
+++)
+1			f�
+
+
+j
++
+�!!�����4��%%%�w6'4+
+7+C<�
+	R�+���}�T�(Oe�-Q]�/EL�.8:�,8;�*CJ�'MZ� @Z�Q�
+��
+��
+��B�
+F	C
+e+v�����///�///�������			��
+
+�H	E+
+
+`
+K+���r��0GL�|||�zzz�???�����++0���.�����
+����',�}�����	'''h777�+
+�*0�%)���+!'��+N"""��	$+"-.GVh8H�T
+#.+'+ &)S�+s
+
+
+�B+
+�J"%+
++%�++
++S ++
+
++
+
+��+��n�d�/+
+�G���+
+��c�+
+?+
+��55�
+H+
+:+	8+��7�
+
+
+}
++Q��Z�p���+/
+
+
+rC��8>�
+r����
+999�+
+�DO��"��s�	!+#"""�3)/6P+
+
+�+
+9Q+ +
+0++-+/+
+
++
+
+O#+
+"��������0/��Y+
+��'%p�
+0+
+Z
+
+V]���nn�a�--I�$#�����aR++7�!5�AHJ�#%�
+%�3�U	+=+"+)!
+
+.>
+
+i%+
+
+;=	
++-\ No newline at end of file

Added: vidalia/trunk/src/crashreporter/res/CrashReporter.qrc
===================================================================
--- vidalia/trunk/src/crashreporter/res/CrashReporter.qrc	                        (rev 0)
+++ vidalia/trunk/src/crashreporter/res/CrashReporter.qrc	2009-06-23 22:53:29 UTC (rev 3879)
@@ -0,0 +1,11 @@
+<!DOCTYPE RCC>
+<RCC version="1.0">
+  <qresource prefix="images">
+    <file>32x32/tools-report-bug.png</file>
+    <file>48x48/tools-report-bug.png</file>
+    <file>64x64/tools-report-bug.png</file>
+  </qresource>
+  <qresource prefix="pki">
+    <file>gd-class2-root.crt</file>
+  </qresource>
+</RCC>

Added: vidalia/trunk/src/crashreporter/res/CrashReporter.rc.in
===================================================================
--- vidalia/trunk/src/crashreporter/res/CrashReporter.rc.in	                        (rev 0)
+++ vidalia/trunk/src/crashreporter/res/CrashReporter.rc.in	2009-06-23 22:53:29 UTC (rev 3879)
@@ -0,0 +1,46 @@
+/*
+**  This file is part of Vidalia, and is subject to the license terms in the
+**  LICENSE file, found in the top level directory of this distribution. If you
+**  did not receive the LICENSE file with this file, you may obtain it from the
+**  Vidalia source package distributed by the Vidalia Project at
+**  http://www.vidalia-project.net/. No part of Vidalia, including this file,
+**  may be copied, modified, propagated, or distributed except according to the
+**  terms described in the LICENSE file.
+*/
+
+/*
+** \file CrashReporter.rc.in
+** \version $Id$
+** Defines resource identifiers for icons used on Win32.
+*/
+
+#include <winver.h>
+
+
+1 VERSIONINFO
+FILEVERSION     @VER_MAJOR@,@VER_MINOR@,@VER_PATCH@,0
+PRODUCTVERSION  @VER_MAJOR@,@VER_MINOR@,@VER_PATCH@,0
+FILEFLAGSMASK   0
+FILEOS          VOS__WINDOWS32
+FILETYPE        VFT_APP
+{
+    BLOCK "StringFileInfo"
+    {
+        BLOCK "040904E4"
+        {
+            VALUE "FileVersion",      "@VERSION@"
+            VALUE "ProductVersion",   "@VERSION@"
+            VALUE "InternalName",     "CrashReporter"
+            VALUE "ProductName",      "Vidalia Crash Reporter"
+            VALUE "FileDescription",  "Vidalia Crash Reporter"
+            VALUE "OriginalFilename", "CrashReporter.exe"
+            VALUE "CompanyName",      "vidalia-project.net"
+            VALUE "LegalCopyright",   "Copyright (C) 2009, Matt Edman"
+        }
+    }
+}
+
+
+/* Application icon */
+101 ICON DISCARDABLE "@CMAKE_CURRENT_SOURCE_DIR@/res/CrashReporter.ico"
+

Added: vidalia/trunk/src/crashreporter/res/gd-class2-root.crt
===================================================================
--- vidalia/trunk/src/crashreporter/res/gd-class2-root.crt	                        (rev 0)
+++ vidalia/trunk/src/crashreporter/res/gd-class2-root.crt	2009-06-23 22:53:29 UTC (rev 3879)
@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh
+MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE
+YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3
+MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo
+ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg
+MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN
+ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA
+PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w
+wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi
+EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY
+avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+
+YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE
+sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h
+/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5
+IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj
+YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD
+ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy
+OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P
+TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ
+HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER
+dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf
+ReYNnyicsbkqWletNw+vHX/bvZ8=
+-----END CERTIFICATE-----

Modified: vidalia/trunk/src/vidalia/CMakeLists.txt
===================================================================
--- vidalia/trunk/src/vidalia/CMakeLists.txt	2009-06-22 22:59:00 UTC (rev 3878)
+++ vidalia/trunk/src/vidalia/CMakeLists.txt	2009-06-23 22:53:29 UTC (rev 3879)
@@ -23,6 +23,11 @@
   ${CMAKE_CURRENT_SOURCE_DIR}/tray
   ${MARBLE_INCLUDE_DIR}
 )
+
+if (USE_BREAKPAD AND BREAKPAD_INCLUDE_DIR)
+  include_directories(${BREAKPAD_INCLUDE_DIR})
+endif(USE_BREAKPAD AND BREAKPAD_INCLUDE_DIR)
+
 if (USE_MARBLE)
   if (APPLE OR WIN32)
   ##
@@ -247,6 +252,11 @@
   HelperProcess.h
   ControlPasswordInputDialog.h
 )
+if (USE_BREAKPAD)
+  set(vidalia_SRCS ${vidalia_SRCS}
+    CrashReporter.cpp
+  )
+endif(USE_BREAKPAD)
 
 ## Specify all the Qt Designer .ui files
 qt4_wrap_ui(vidalia_SRCS
@@ -449,8 +459,14 @@
 if (USE_MINIUPNPC)
   target_link_libraries(${vidalia_BIN} miniupnpc)
 endif(USE_MINIUPNPC)
+if (USE_BREAKPAD)
+  target_link_libraries(${vidalia_BIN} ${BREAKPAD_LIBRARIES})
+endif(USE_BREAKPAD)
 if (USE_MARBLE)
-  target_link_libraries(${vidalia_BIN} ${MARBLE_LIBRARIES})
+  target_link_libraries(${vidalia_BIN}
+    ${MARBLE_LIBRARIES}
+    ${QT_QTSVG_LIBRARY}
+  )
 endif(USE_MARBLE)
 
 if (WIN32)

Added: vidalia/trunk/src/vidalia/CrashReporter.cpp
===================================================================
--- vidalia/trunk/src/vidalia/CrashReporter.cpp	                        (rev 0)
+++ vidalia/trunk/src/vidalia/CrashReporter.cpp	2009-06-23 22:53:29 UTC (rev 3879)
@@ -0,0 +1,408 @@
+/*
+**  This file is part of Vidalia, and is subject to the license terms in the
+**  LICENSE file, found in the top level directory of this distribution. If you
+**  did not receive the LICENSE file with this file, you may obtain it from the
+**  Vidalia source package distributed by the Vidalia Project at
+**  http://www.vidalia-project.net/. No part of Vidalia, including this file,
+**  may be copied, modified, propagated, or distributed except according to the
+**  terms described in the LICENSE file.
+*/
+/*
+**  The append_string() function in this file is derived from the implementation
+**  of strlcat() by Todd C. Miller. It is licensed as follows:
+**
+**  Copyright (c) 1998 Todd C. Miller <Todd.Miller@xxxxxxxxxxxxx>
+**  All rights reserved.
+**
+**  Redistribution and use in source and binary forms, with or without
+**  modification, are permitted provided that the following conditions
+**  are met:
+**  1. Redistributions of source code must retain the above copyright
+**     notice, this list of conditions and the following disclaimer.
+**  2. 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.
+**  3. The name of the author may not be used to endorse or promote products
+**     derived from this software without specific prior written permission.
+**
+**  THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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.
+*/
+
+/*
+** \file CrashReporter.h
+** \version $Id$
+** \brief General routines to install a Breakpad-based exception handler and
+** set options related to launching the crash reporting application.
+*/
+
+#include "CrashReporter.h"
+#include "stringutil.h"
+
+#if defined(Q_OS_WIN32)
+#include <client/windows/handler/exception_handler.h>
+#elif defined(Q_OS_MAC)
+#include <client/mac/handler/exception_handler.h>
+#elif defined(Q_OS_LINUX)
+#include <client/linux/handler/exception_handler.h>
+#elif defined(Q_OS_SOLARIS)
+#include <client/solaris/handler/exception_handler.h>
+#endif
+
+#include <QString>
+#include <QStringList>
+#include <QFileInfo>
+#include <QDir>
+
+#include <time.h>
+
+
+namespace CrashReporter
+{
+#if defined(Q_OS_WIN32)
+typedef wchar_t         _char_t;
+typedef HANDLE          _file_handle_t;
+#define PATH_SEPARATOR  TEXT("\\")
+# ifdef _USE_32BIT_TIME_T
+#   define TIME_TO_STRING(buf, buflen, t) \
+      _ltoa_s(t, buf, buflen, 10)
+# else
+#   define TIME_TO_STRING(buf, buflen, t) \
+      _i64toa_s(t, buf, buflen, 10)
+# endif
+#else
+typedef char            _char_t;
+typedef int             _file_handle_t;
+#define PATH_SEPARATOR  "/"
+#define TEXT(x)         (x)
+#define TIME_TO_STRING(buf, buflen, t) \
+  snprintf(buf, buflen, "%ld", t)
+#endif
+
+/** Pointer to the Breakpad-installed exception handler called if Vidalia
+ * crashes.
+ * \sa install_exception_handler()
+ */
+static google_breakpad::ExceptionHandler *exceptionHandler = 0;
+
+/** If true, the crash reporting application will be displayed when the
+ * Breakpad-installed exception handler is called. Otherwise, the system
+ * will handle the exception itself.
+ * \sa set_crash_reporter()
+ */
+static bool showCrashReporter = false;
+
+/** Absolute path of the crash reporting application that will be launched
+ * from the exception handler.
+ * \sa set_crash_reporter()
+ */
+static _char_t crashReporterExecutable[MAX_PATH_LEN + 1] = TEXT("");
+
+/** Version information for the application being monitored for crashes.
+ * The version will be written to the extra information file alongside
+ * the minidump.
+ */
+static char buildVersion[MAX_VERSION_LEN + 1] = "";
+
+/** Path and filename of the application to restart after displaying
+ * the crash reporting dialog. The contents of this string are encoded
+ * in UTF-8.
+ * \sa set_restart_options()
+ */
+static char restartExecutable[MAX_CMD_LEN + 1] = "";
+
+/** Additional arguments to use when restarting the crashed application.
+ * The contents of this string are encoded in UTF-8.
+ * \sa set_restart_options()
+ */
+static char restartExecutableArgs[MAX_CMD_LEN + 1] = "";
+
+
+/** Records the time at which install_exception_handler() is called, which
+ * is usually as early as possible during application startup. This is used
+ * in minidump_callback() to determine how long the application was running
+ * before it crashed.
+ * \sa install_exception_handler()
+ * \sa minidump_callback()
+ */
+static time_t startupTime = 0;
+
+
+/** Slightly modified version of the strlcat() implementation by Todd C. 
+ * Miller (see the top of this file or the LICENSE file for license details),
+ * that supports arguments of either wchar_t* on Windows or the usual char*
+ * everywhere else but retains the semantics of strlcat().
+ */
+static size_t
+append_string(_char_t *dst, const _char_t *src, size_t siz)
+{
+  _char_t *d = dst;
+  const _char_t *s = src;
+  size_t n = siz;
+  size_t dlen;
+
+  /* Find the end of dst and adjust bytes left but don't go past end */
+  while (n-- != 0 && *d != TEXT('\0'))
+    d++;
+  dlen = d - dst;
+  n = siz - dlen;
+
+  if (n == 0)
+#if defined(Q_OS_WIN32)
+    return (dlen + wcslen(s));
+#else
+    return(dlen + strlen(s));
+#endif
+
+  while (*s != TEXT('\0')) {
+    if (n != 1) {
+      *d++ = *s;
+      n--;
+    }
+    s++;
+  }
+  *d = TEXT('\0');
+
+  return(dlen + (s - src));	/* count does not include NUL */
+}
+
+/** Writes the formatted string "<b>key</b>=</b>val\n" to the file handle
+ * specified by <b>hFile</b>. On Windows, <b>hFile</b> is a HANDLE. Everywhere
+ * else, <b>hFile</b> is an int.
+ */
+static void
+write_keyval_to_file(_file_handle_t hFile, const char *key, const char *val)
+{
+#if defined(Q_OS_WIN32)
+  DWORD dwWritten;
+  WriteFile(hFile, key, strlen(key), &dwWritten, NULL);
+  WriteFile(hFile, "=", 1, &dwWritten, NULL);
+  WriteFile(hFile, val, strlen(val), &dwWritten, NULL);
+  WriteFile(hFile, "\n", 1, &dwWritten, NULL);
+#else
+  write(hFile, key, strlen(key));
+  write(hFile, "=", 1);
+  write(hFile, val, strlen(val));
+  write(hFile, "\n", 1);
+#endif
+}
+
+/** Writes to a file extra information used by the crash reporting
+ * application such as how long the application was running before it
+ * crashed, the application to restart, as well as any extra arguments.
+ * The contents of the file are formatted as a series of "Key=Val\n" pairs.
+ * The written file has the same path and base filename as the minidump
+ * file, with ".info" appended to the end. Returns true if the file was
+ * created succesfully. Otherwise, returns false.
+ */
+static bool
+write_extra_dump_info(const _char_t *path, const _char_t *id, time_t crashTime)
+{
+  static const char *KeyBuildVersion = "BuildVersion";
+  static const char *KeyCrashTime = "CrashTime";
+  static const char *KeyStartupTime = "StartupTime";
+  static const char *KeyRestartExecutable = "RestartExecutable";
+  static const char *KeyRestartExecutableArgs = "RestartExecutableArgs";
+
+  _char_t extraInfoPath[MAX_PATH_LEN] = TEXT("");
+  append_string(extraInfoPath, path, MAX_PATH_LEN);
+  append_string(extraInfoPath, PATH_SEPARATOR, MAX_PATH_LEN);
+  append_string(extraInfoPath, id, MAX_PATH_LEN);
+  size_t len = append_string(extraInfoPath, TEXT(".dmp.info"), MAX_PATH_LEN);
+  if (len >= MAX_PATH_LEN)
+    return false;
+
+#if defined(Q_OS_WIN32)
+  HANDLE hFile = CreateFile(extraInfoPath, GENERIC_WRITE, 0, NULL,
+                            CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+  if (hFile == INVALID_HANDLE_VALUE)
+    return false;
+#else
+  /* TODO: Implement for non-Windowses */
+#endif
+  
+  char crashTimeString[24], startupTimeString[24];
+  TIME_TO_STRING(crashTimeString, 24, crashTime);
+  TIME_TO_STRING(startupTimeString, 24, startupTime);
+
+  write_keyval_to_file(hFile, KeyBuildVersion, buildVersion);
+  write_keyval_to_file(hFile, KeyCrashTime, crashTimeString);
+  write_keyval_to_file(hFile, KeyStartupTime, startupTimeString);
+  write_keyval_to_file(hFile, KeyRestartExecutable, restartExecutable);
+  write_keyval_to_file(hFile, KeyRestartExecutableArgs, restartExecutableArgs);
+
+#if defined(Q_OS_WIN32)
+  CloseHandle(hFile);
+#else
+  /* TODO: Implement for non-Windowses */
+  /* close(hFile); */
+#endif
+  return true;
+}
+
+/** Breakpad-installed exception handler. This function gets called in the
+ * event of a crash. If <b>showCrashReporter</b> is true, this will execute
+ * the crash reporting application, passing it the name and location of the
+ * generated minidump, the absolute path to the (now crashed) Vidalia
+ * executable, and any arguments that may be needed to restart Vidalia.
+ */
+bool
+minidump_callback(const _char_t *path,    // Path to the minidump file
+                  const _char_t *id,      // Minidump UUID
+                  void *context,          // Callback context
+#if defined(Q_OS_WIN32)
+                  EXCEPTION_POINTERS *exInfo,
+                  MDRawAssertionInfo *assertionInfo,
+#endif
+                  bool succeeded)
+{
+  if (! succeeded || ! showCrashReporter)
+    return false;
+
+  /* Write the extra dump info, such as application uptime, executable to
+   * restart, and any necessary restart arguments. */
+  write_extra_dump_info(path, id, time(NULL));
+
+  /* Format the command line used to launch the crash reporter */
+  _char_t commandLine[MAX_CMD_LEN] = TEXT("");
+  append_string(commandLine, TEXT("\""), MAX_CMD_LEN);
+  append_string(commandLine, crashReporterExecutable, MAX_CMD_LEN);
+  append_string(commandLine, TEXT("\" \""), MAX_CMD_LEN);
+  append_string(commandLine, path, MAX_CMD_LEN);
+  append_string(commandLine, PATH_SEPARATOR, MAX_CMD_LEN);
+  append_string(commandLine, id, MAX_CMD_LEN);
+  size_t len = append_string(commandLine, TEXT(".dmp\""), MAX_CMD_LEN);
+  if (len >= MAX_CMD_LEN)
+    return false;
+
+  /* Launch the crash reporter with the name and location of the minidump */
+#if defined(Q_OS_WIN32)
+  PROCESS_INFORMATION pi;
+  STARTUPINFOW si;
+
+  ZeroMemory(&pi, sizeof(pi));
+  ZeroMemory(&si, sizeof(si));
+  si.cb = sizeof(si);
+  si.dwFlags = STARTF_USESHOWWINDOW;
+  si.wShowWindow = SW_SHOWDEFAULT;
+
+  BOOL rc = CreateProcess(NULL, (LPWSTR)commandLine, NULL, NULL, FALSE, 0,
+                          NULL, NULL, &si, &pi);
+  if (rc) {
+    CloseHandle(pi.hThread);
+    CloseHandle(pi.hProcess);
+  }
+  TerminateProcess(GetCurrentProcess(), 1);
+  return true;
+#else
+  /* TODO: Implement for non-Windowses */
+  return false;
+#endif
+}
+
+bool
+install_exception_handler(const QString &dumpPath)
+{
+  /* Create a directory for the crash dumps if it doesn't already exist */
+  QDir dumpDir(dumpPath);
+  if (! dumpDir.exists() && ! dumpDir.mkdir("."))
+    return false;
+
+  /* Create the exception handler and specify where the dumps should go */
+  exceptionHandler = new google_breakpad::ExceptionHandler(
+#if defined(Q_OS_WIN32)
+                            dumpDir.absolutePath().toStdWString(),
+#else
+                            dumpDir.absolutePath().toStdString(),
+#endif
+                            NULL,
+                            minidump_callback,
+                            NULL,
+#if defined(Q_OS_WIN32)
+                            google_breakpad::ExceptionHandler::HANDLER_ALL);
+#else
+                            true);
+#endif
+  if (! exceptionHandler)
+    return false;
+
+  startupTime = time(NULL);
+  return true;
+}
+
+void
+remove_exception_handler(void)
+{
+  if (exceptionHandler) {
+    delete exceptionHandler;
+    exceptionHandler = 0;
+  }
+}
+
+bool
+set_crash_reporter(const QString &crashReporter)
+{
+#if defined(Q_OS_WIN32)
+  if (crashReporter.length() <= CrashReporter::MAX_PATH_LEN) {
+    crashReporter.toWCharArray(crashReporterExecutable);
+    crashReporterExecutable[crashReporter.length()] = L'\0';
+#else
+  QByteArray utf8 = crashReporter.toUtf8();
+  if (utf8.length() <= CrashReporter::MAX_PATH_LEN) {
+    memcpy(crashReporterExecutable, utf8.constData(), utf8.length());
+    crashReporterExecutable[utf8.length()] = '\0';
+#endif
+    showCrashReporter = true;
+  } else {
+    /* If the given path is longer than MAX_PATH_LEN, no crash reporting
+     * application will be set since the user's platform wouldn't be able to
+     * execute it anyway.
+     */
+    showCrashReporter = false;
+  }
+  return showCrashReporter;
+}
+
+bool
+set_restart_options(const QString &executable, const QStringList &arguments)
+{
+  QByteArray exe = executable.toUtf8();
+  if (exe.length() > MAX_CMD_LEN)
+    return false;
+
+  QByteArray args = string_format_arguments(arguments).toUtf8();
+  if (args.length() > MAX_CMD_LEN)
+    return false;
+
+  memcpy(restartExecutable, exe.constData(), exe.length());
+  restartExecutable[exe.length()] = '\0';
+
+  memcpy(restartExecutableArgs, args.constData(), args.length());
+  restartExecutableArgs[args.length()] = '\0';
+
+  return true;
+}
+
+bool
+set_build_version(const QString &version)
+{
+  if (version.length() > MAX_VERSION_LEN)
+    return false;
+
+  QByteArray ascii = version.toAscii();
+  memcpy(buildVersion, ascii.constData(), ascii.length());
+  buildVersion[ascii.length()] = '\0';
+
+  return true;
+}
+
+}
+

Added: vidalia/trunk/src/vidalia/CrashReporter.h
===================================================================
--- vidalia/trunk/src/vidalia/CrashReporter.h	                        (rev 0)
+++ vidalia/trunk/src/vidalia/CrashReporter.h	2009-06-23 22:53:29 UTC (rev 3879)
@@ -0,0 +1,96 @@
+/*
+**  This file is part of Vidalia, and is subject to the license terms in the
+**  LICENSE file, found in the top level directory of this distribution. If you
+**  did not receive the LICENSE file with this file, you may obtain it from the
+**  Vidalia source package distributed by the Vidalia Project at
+**  http://www.vidalia-project.net/. No part of Vidalia, including this file,
+**  may be copied, modified, propagated, or distributed except according to the
+**  terms described in the LICENSE file.
+*/
+
+/*
+** \file CrashReporter.h
+** \version $Id$
+** \brief General routines to install a Breakpad-based exception handler and
+** set options related to launching the crash reporting application.
+*/
+
+#ifndef _CRASHREPORTER_H
+#define _CRASHREPORTER_H
+
+class QString;
+class QStringList;
+
+
+namespace CrashReporter
+{
+  /** Defines the maximum length of the absolute path plus filename of the
+   * crash reporting executable displayed when the exception handler is
+   * called.
+   */
+#if defined(Q_OS_WIN32)
+  static const int MAX_PATH_LEN = 260;
+#else
+  static const int MAX_PATH_LEN = 4096; /* Is there a better value for this? */
+#endif
+
+  /** Defines the maximum length of the command line arguments used to restart
+   * the crashed application by the crash reporter. The maximum command line
+   * length is based on Windows' 32K character command line limit, according
+   * to the MSDN documents.
+   */
+  static const int MAX_CMD_LEN = 32768;
+
+  /** Defines the maximum length of a build version string that can be set
+   * by set_build_version().
+   * \sa set_build_version()
+   */
+  static const int MAX_VERSION_LEN = 64;
+
+  /** Installs the Breakpad exception handler and sets the static global
+   * variables used by the exception handler to launch the crash reporting
+   * application. Minidumps will be writen to <b>dumpPath</b>, which will
+   * be created if it doesn't already exist.
+   * \sa remove_exception_handler()
+   */
+  bool install_exception_handler(const QString &dumpPath);
+
+  /** Removes the application's exception handler previously created by
+   * install_exception_handler(). If no exception handler was previously created,
+   * no action will be taken.
+   * \sa install_exception_handler()
+   */
+  void remove_exception_handler(void);
+
+  /** Sets <b>crashReporter</b> as the executable that gets called when the
+   * exception handler catches a crash. If <b>crashReporter</b> contains one
+   * or more spaces, the given path will be wrapped in quotes. The caller is
+   * responsible for ensuring that <b>crashReporter</b> is no greater than
+   * CrashReporter::MAX_PATH_LEN (including added quotes). Returns true if
+   * the crash reporting application was set successfully, or false if
+   * <b>crashReporter</b> was too long.
+   */
+  bool set_crash_reporter(const QString &crashReporter);
+
+  /** Sets the <b>executable</b> and <b>args</b> that will be passed to the
+   * crash reporting application, so it can restart the crashed application
+   * with the same arguments as before it crashed. If the <b>executable</b>
+   * path or any of <b>args</b> contains a space, they will be quoted before
+   * being passed to the crash reporting application. The path to the
+   * generated minidump, crash reporting application, executable to restart
+   * and any arguments must fit within MAX_CMD_LEN, including any added
+   * quotes.
+   * \sa set_crash_reporter()
+   */
+  bool set_restart_options(const QString &executable,
+                           const QStringList &arguments);
+
+  /** Sets <b>version</b> as the build version identifier written to the
+   * extra information file alongside a minidump. The version string must
+   * be no longer than CrashReporter::MAX_VERSION_LEN.
+   */
+  bool set_build_version(const QString &version);
+}
+
+#endif
+

Modified: vidalia/trunk/src/vidalia/config/BridgeDownloaderProgressDialog.h
===================================================================
--- vidalia/trunk/src/vidalia/config/BridgeDownloaderProgressDialog.h	2009-06-22 22:59:00 UTC (rev 3878)
+++ vidalia/trunk/src/vidalia/config/BridgeDownloaderProgressDialog.h	2009-06-23 22:53:29 UTC (rev 3879)
@@ -32,7 +32,7 @@
   BridgeDownloaderProgressDialog(QWidget *parent = 0);
 
 public slots:
-   /** Sets the status message text in the dialog to <b>status</b>.
+  /** Sets the status message text in the dialog to <b>status</b>.
    */
   void setStatus(const QString &status);
 

Modified: vidalia/trunk/src/vidalia/main.cpp
===================================================================
--- vidalia/trunk/src/vidalia/main.cpp	2009-06-22 22:59:00 UTC (rev 3878)
+++ vidalia/trunk/src/vidalia/main.cpp	2009-06-23 22:53:29 UTC (rev 3879)
@@ -18,12 +18,14 @@
 #include "Vidalia.h"
 #include "MainWindow.h"
 #include "VMessageBox.h"
+#if defined(USE_BREAKPAD)
+#include "CrashReporter.h"
+#endif
 
 #include "procutil.h"
 #include "stringutil.h"
 
 #include <QObject>
-
 #if defined(Q_OS_WIN32)
 #include <QSysInfo>
 #endif
@@ -31,7 +33,50 @@
 #include <signal.h>
 #endif
 
+#if defined(USE_BREAKPAD)
+void
+setup_crash_reporter()
+{
+  /* Set the crash reporting application used to submit minidumps. On Windows,
+   * crashreporter.exe is assumed to live in the same directory as vidalia.exe.
+   */
+#if defined(Q_OS_WIN32)
+  QString crashReporter = Vidalia::applicationDirPath() + "\\crashreporter.exe";
+#elif defined(Q_OS_MAC)
+  QString crashReporter = Vidalia::applicationDirPath() + "/CrashReporter.app";
+#endif
+  if (! QFileInfo(crashReporter).isExecutable()) {
+    vWarn("Unable to find crash reporting application. Crash reporting will "
+          "be unavailable.");
+    return;
+  }
+  if (! CrashReporter::set_crash_reporter(crashReporter)) {
+    vWarn("Vidalia found the crash reporting application, but the path is "
+          "longer than your platform supports. Skipping crash reporting.");
+    return;
+  }
 
+  /* Set the Vidalia executable and options used to restart Vidalia after a 
+   * crash. We strip off the first argument in Vidalia::arguments(), since
+   * it's the application name again anyway. */
+  CrashReporter::set_restart_options(Vidalia::applicationFilePath(),
+                                     Vidalia::arguments().mid(1));
+
+  /* Set the build version that gets saved with a minidump. */
+  CrashReporter::set_build_version(VIDALIA_VERSION);
+
+  /* Install the exception handler and give it the location to which Breakpad
+   * should write its minidumps. */
+  QString dumpPath = Vidalia::dataDirectory() + "/crashreports";
+  if (! CrashReporter::install_exception_handler(dumpPath)) {
+    vWarn("Unable to setup Breakpad exception handler. Crash reporting "
+          "will be unavailable.");
+  } else {
+    vInfo("Installed Breakpad exception handler.");
+  }
+}
+#endif
+
 extern "C" void
 signal_handler(int signal)
 {
@@ -99,14 +144,18 @@
   vNotice("Vidalia %1 using Qt %2").arg(Vidalia::version())
                                    .arg(QT_VERSION_STR);
 
+#if defined(USE_BREAKPAD)
+  /* Set up the crash reporting application and exception handlers. */
+  setup_crash_reporter();
+#endif
+#if defined(USE_MARBLE) && defined(Q_OS_WIN32)
+  vApp->addLibraryPath(vApp->applicationDirPath() + "/plugins/qt");
+#endif
+
   /* Install a signal handler to clean up properly after a catching a 
    * SIGINT or SIGTERM. */
   install_signal_handler();
 
-#if defined(USE_MARBLE) && defined(Q_OS_WIN32)
-  vApp->addLibraryPath(vApp->applicationDirPath() + "/plugins/qt");
-#endif
-
   /* Validate any command-line arguments, or show usage message box, if
    * necessary. */
   QString errmsg;
@@ -162,6 +211,12 @@
   /* Vidalia exited, so cleanup our pidfile and return */
   QFile::remove(pidfile);
   vNotice("Vidalia is exiting cleanly (return code %1).").arg(ret);
+
+#if defined(USE_BREAKPAD)
+  vInfo("Removing Breakpad exception handler.");
+  CrashReporter::remove_exception_handler();
+#endif
+
   return ret;
 }