void SaveSettings()
{
  /*
   * NOTE! This code needs to stay in sync with the preference setting
   *       code in in nsExceptionHandler.cpp.
   */

  StringTable settings;

  ReadStringsFromFile(gSettingsPath + "/" + kIniFile, settings, true);
  if (!gEmailFieldHint)
    settings["Email"] = gtk_entry_get_text(GTK_ENTRY(gEmailEntry));
  else
    settings.erase("Email");

  settings["EmailMe"] =
    gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gEmailMeCheck)) ? "1" : "0";
  if (gIncludeURLCheck != 0)
    settings["IncludeURL"] =
      gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gIncludeURLCheck))
      ? "1" : "0";
  settings["SubmitReport"] =
    gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gSubmitReportCheck))
    ? "1" : "0";

  WriteStringsToFile(gSettingsPath + "/" + kIniFile,
                     "Crash Reporter", settings, true);
}
static void UpdateURL()
{
  if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gIncludeURLCheck))) {
    gQueryParameters["URL"] = gURLParameter;
  } else {
    gQueryParameters.erase("URL");
  }
}
int main(int argc, char** argv)
{
  gArgc = argc;
  gArgv = argv;

  if (!ReadConfig()) {
    UIError("Couldn't read configuration.");
    return 0;
  }

  if (!UIInit())
    return 0;

  if (argc > 1) {
    gReporterDumpFile = argv[1];
  }

  if (gReporterDumpFile.empty()) {
    // no dump file specified, run the default UI
    UIShowDefaultUI();
  } else {
    // Start by running minidump analyzer to gather stack traces.
    string reporterDumpFile = gReporterDumpFile;
    vector<string> args = { reporterDumpFile };
    UIRunProgram(GetProgramPath(UI_MINIDUMP_ANALYZER_FILENAME),
                 args, /* wait */ true);

    // go ahead with the crash reporter
    gExtraFile = GetAdditionalFilename(gReporterDumpFile, kExtraDataExtension);
    if (gExtraFile.empty()) {
      UIError(gStrings[ST_ERROR_BADARGUMENTS]);
      return 0;
    }

    if (!UIFileExists(gExtraFile)) {
      UIError(gStrings[ST_ERROR_EXTRAFILEEXISTS]);
      return 0;
    }

    gMemoryFile = GetAdditionalFilename(gReporterDumpFile,
                                        kMemoryReportExtension);
    if (!UIFileExists(gMemoryFile)) {
      gMemoryFile.erase();
    }

    StringTable queryParameters;
    if (!ReadStringsFromFile(gExtraFile, queryParameters, true)) {
      UIError(gStrings[ST_ERROR_EXTRAFILEREAD]);
      return 0;
    }

    if (queryParameters.find("ProductName") == queryParameters.end()) {
      UIError(gStrings[ST_ERROR_NOPRODUCTNAME]);
      return 0;
    }

    // There is enough information in the extra file to rewrite strings
    // to be product specific
    RewriteStrings(queryParameters);

    if (queryParameters.find("ServerURL") == queryParameters.end()) {
      UIError(gStrings[ST_ERROR_NOSERVERURL]);
      return 0;
    }

    // Hopefully the settings path exists in the environment. Try that before
    // asking the platform-specific code to guess.
    gSettingsPath = UIGetEnv("MOZ_CRASHREPORTER_DATA_DIRECTORY");
    if (gSettingsPath.empty()) {
      string product = queryParameters["ProductName"];
      string vendor = queryParameters["Vendor"];
      if (!UIGetSettingsPath(vendor, product, gSettingsPath)) {
        gSettingsPath.clear();
      }
    }

    if (gSettingsPath.empty() || !UIEnsurePathExists(gSettingsPath)) {
      UIError(gStrings[ST_ERROR_NOSETTINGSPATH]);
      return 0;
    }

    OpenLogFile();

    gEventsPath = UIGetEnv("MOZ_CRASHREPORTER_EVENTS_DIRECTORY");
    gPingPath = UIGetEnv("MOZ_CRASHREPORTER_PING_DIRECTORY");

    // Assemble and send the crash ping
    string hash;
    string pingUuid;

    hash = ComputeDumpHash();
    if (!hash.empty()) {
      AppendToEventFile("MinidumpSha256Hash", hash);
    }

    if (SendCrashPing(queryParameters, hash, pingUuid, gPingPath)) {
      AppendToEventFile("CrashPingUUID", pingUuid);
    }

    // Update the crash event with stacks if they are present
    auto stackTracesItr = queryParameters.find("StackTraces");
    if (stackTracesItr != queryParameters.end()) {
      AppendToEventFile(stackTracesItr->first, stackTracesItr->second);
    }

    if (!UIFileExists(gReporterDumpFile)) {
      UIError(gStrings[ST_ERROR_DUMPFILEEXISTS]);
      return 0;
    }

    string pendingDir = gSettingsPath + UI_DIR_SEPARATOR + "pending";
    if (!MoveCrashData(pendingDir, gReporterDumpFile, gExtraFile,
                       gMemoryFile)) {
      return 0;
    }

    string sendURL = queryParameters["ServerURL"];
    // we don't need to actually send this
    queryParameters.erase("ServerURL");

    queryParameters["Throttleable"] = "1";

    // re-set XUL_APP_FILE for xulrunner wrapped apps
    const char *appfile = getenv("MOZ_CRASHREPORTER_RESTART_XUL_APP_FILE");
    if (appfile && *appfile) {
      const char prefix[] = "XUL_APP_FILE=";
      char *env = (char*) malloc(strlen(appfile) + strlen(prefix) + 1);
      if (!env) {
        UIError("Out of memory");
        return 0;
      }
      strcpy(env, prefix);
      strcat(env, appfile);
      putenv(env);
      free(env);
    }

    vector<string> restartArgs;

    ostringstream paramName;
    int i = 0;
    paramName << "MOZ_CRASHREPORTER_RESTART_ARG_" << i++;
    const char *param = getenv(paramName.str().c_str());
    while (param && *param) {
      restartArgs.push_back(param);

      paramName.str("");
      paramName << "MOZ_CRASHREPORTER_RESTART_ARG_" << i++;
      param = getenv(paramName.str().c_str());
    }

    // allow override of the server url via environment variable
    //XXX: remove this in the far future when our robot
    // masters force everyone to use XULRunner
    char* urlEnv = getenv("MOZ_CRASHREPORTER_URL");
    if (urlEnv && *urlEnv) {
      sendURL = urlEnv;
    }

     // see if this version has been end-of-lifed
     if (queryParameters.find("Version") != queryParameters.end() &&
         CheckEndOfLifed(queryParameters["Version"])) {
       UIError(gStrings[ST_ERROR_ENDOFLIFE]);
       DeleteDump();
       return 0;
     }

    StringTable files;
    files["upload_file_minidump"] = gReporterDumpFile;
    if (!gMemoryFile.empty()) {
      files["memory_report"] = gMemoryFile;
    }

    if (!UIShowCrashUI(files, queryParameters, sendURL, restartArgs))
      DeleteDump();
  }

  UIShutdown();

  return 0;
}
int main(int argc, char** argv)
{
    gArgc = argc;
    gArgv = argv;

    if (!ReadConfig()) {
        UIError("Couldn't read configuration.");
        return 0;
    }

    if (!UIInit())
        return 0;

    if (argc > 1) {
        gReporterDumpFile = argv[1];
    }

    if (gReporterDumpFile.empty()) {
        // no dump file specified, run the default UI
        UIShowDefaultUI();
    } else {
        gExtraFile = GetAdditionalFilename(gReporterDumpFile, kExtraDataExtension);
        if (gExtraFile.empty()) {
            UIError(gStrings[ST_ERROR_BADARGUMENTS]);
            return 0;
        }

        if (!UIFileExists(gExtraFile)) {
            UIError(gStrings[ST_ERROR_EXTRAFILEEXISTS]);
            return 0;
        }

        gMemoryFile = GetAdditionalFilename(gReporterDumpFile,
                                            kMemoryReportExtension);
        if (!UIFileExists(gMemoryFile)) {
            gMemoryFile.erase();
        }

        StringTable queryParameters;
        if (!ReadStringsFromFile(gExtraFile, queryParameters, true)) {
            UIError(gStrings[ST_ERROR_EXTRAFILEREAD]);
            return 0;
        }

        if (queryParameters.find("ProductName") == queryParameters.end()) {
            UIError(gStrings[ST_ERROR_NOPRODUCTNAME]);
            return 0;
        }

        // There is enough information in the extra file to rewrite strings
        // to be product specific
        RewriteStrings(queryParameters);

        if (queryParameters.find("ServerURL") == queryParameters.end()) {
            UIError(gStrings[ST_ERROR_NOSERVERURL]);
            return 0;
        }

        // Hopefully the settings path exists in the environment. Try that before
        // asking the platform-specific code to guess.
#ifdef XP_WIN32
        static const wchar_t kDataDirKey[] = L"MOZ_CRASHREPORTER_DATA_DIRECTORY";
        const wchar_t *settingsPath = _wgetenv(kDataDirKey);
        if (settingsPath && *settingsPath) {
            gSettingsPath = WideToUTF8(settingsPath);
        }
#else
        static const char kDataDirKey[] = "MOZ_CRASHREPORTER_DATA_DIRECTORY";
        const char *settingsPath = getenv(kDataDirKey);
        if (settingsPath && *settingsPath) {
            gSettingsPath = settingsPath;
        }
#endif
        else {
            string product = queryParameters["ProductName"];
            string vendor = queryParameters["Vendor"];
            if (!UIGetSettingsPath(vendor, product, gSettingsPath)) {
                gSettingsPath.clear();
            }
        }

        if (gSettingsPath.empty() || !UIEnsurePathExists(gSettingsPath)) {
            UIError(gStrings[ST_ERROR_NOSETTINGSPATH]);
            return 0;
        }

        OpenLogFile();

#ifdef XP_WIN32
        static const wchar_t kEventsDirKey[] = L"MOZ_CRASHREPORTER_EVENTS_DIRECTORY";
        const wchar_t *eventsPath = _wgetenv(kEventsDirKey);
        if (eventsPath && *eventsPath) {
            gEventsPath = WideToUTF8(eventsPath);
        }
#else
        static const char kEventsDirKey[] = "MOZ_CRASHREPORTER_EVENTS_DIRECTORY";
        const char *eventsPath = getenv(kEventsDirKey);
        if (eventsPath && *eventsPath) {
            gEventsPath = eventsPath;
        }
#endif
        else {
            gEventsPath.clear();
        }

        if (!UIFileExists(gReporterDumpFile)) {
            UIError(gStrings[ST_ERROR_DUMPFILEEXISTS]);
            return 0;
        }

        string pendingDir = gSettingsPath + UI_DIR_SEPARATOR + "pending";
        if (!MoveCrashData(pendingDir, gReporterDumpFile, gExtraFile,
                           gMemoryFile)) {
            return 0;
        }

        string sendURL = queryParameters["ServerURL"];
        // we don't need to actually send this
        queryParameters.erase("ServerURL");

        queryParameters["Throttleable"] = "1";

        // re-set XUL_APP_FILE for xulrunner wrapped apps
        const char *appfile = getenv("MOZ_CRASHREPORTER_RESTART_XUL_APP_FILE");
        if (appfile && *appfile) {
            const char prefix[] = "XUL_APP_FILE=";
            char *env = (char*) malloc(strlen(appfile) + strlen(prefix) + 1);
            if (!env) {
                UIError("Out of memory");
                return 0;
            }
            strcpy(env, prefix);
            strcat(env, appfile);
            putenv(env);
            free(env);
        }

        vector<string> restartArgs;

        ostringstream paramName;
        int i = 0;
        paramName << "MOZ_CRASHREPORTER_RESTART_ARG_" << i++;
        const char *param = getenv(paramName.str().c_str());
        while (param && *param) {
            restartArgs.push_back(param);

            paramName.str("");
            paramName << "MOZ_CRASHREPORTER_RESTART_ARG_" << i++;
            param = getenv(paramName.str().c_str());
        };

        // allow override of the server url via environment variable
        //XXX: remove this in the far future when our robot
        // masters force everyone to use XULRunner
        char* urlEnv = getenv("MOZ_CRASHREPORTER_URL");
        if (urlEnv && *urlEnv) {
            sendURL = urlEnv;
        }

        // see if this version has been end-of-lifed
        if (queryParameters.find("Version") != queryParameters.end() &&
                CheckEndOfLifed(queryParameters["Version"])) {
            UIError(gStrings[ST_ERROR_ENDOFLIFE]);
            DeleteDump();
            return 0;
        }

        StringTable files;
        files["upload_file_minidump"] = gReporterDumpFile;
        if (!gMemoryFile.empty()) {
            files["memory_report"] = gMemoryFile;
        }

        if (!UIShowCrashUI(files, queryParameters, sendURL, restartArgs))
            DeleteDump();
    }

    UIShutdown();

    return 0;
}