// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/bug_report_util.h"

#include "app/l10n_util.h"
#include "base/file_version_info.h"
#include "base/string_util.h"
#include "chrome/browser/browser_process_impl.h"
#include "chrome/browser/net/url_fetcher.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/safe_browsing/safe_browsing_util.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "googleurl/src/gurl.h"
#include "grit/locale_settings.h"

namespace {

static const int kBugReportVersion = 1;

static const char kReportPhishingUrl[] =
    "http://www.google.com/safebrowsing/report_phish/";
}

// Simple URLFetcher::Delegate to clean up URLFetcher on completion.
class BugReportUtil::PostCleanup : public URLFetcher::Delegate {
 public:
  PostCleanup();

  // Overridden from URLFetcher::Delegate.
  virtual void OnURLFetchComplete(const URLFetcher* source,
                                  const GURL& url,
                                  const URLRequestStatus& status,
                                  int response_code,
                                  const ResponseCookies& cookies,
                                  const std::string& data);
 private:
  DISALLOW_COPY_AND_ASSIGN(PostCleanup);
};

BugReportUtil::PostCleanup::PostCleanup() {
}

void BugReportUtil::PostCleanup::OnURLFetchComplete(
    const URLFetcher* source,
    const GURL& url,
    const URLRequestStatus& status,
    int response_code,
    const ResponseCookies& cookies,
    const std::string& data) {
  // Delete the URLFetcher.
  delete source;
  // And then delete ourselves.
  delete this;
}

// static
void BugReportUtil::SetOSVersion(std::string *os_version) {
#if defined(OS_WIN)
  OSVERSIONINFO osvi;
  ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
  osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

  if (GetVersionEx(&osvi)) {
    *os_version = StringPrintf("%d.%d.%d %S",
      osvi.dwMajorVersion,
      osvi.dwMinorVersion,
      osvi.dwBuildNumber,
      osvi.szCSDVersion);
  } else {
    *os_version = "unknown";
  }
#elif defined(OS_MACOSX)
  int32 major;
  int32 minor;
  int32 bugFix;
  base::SysInfo::OperatingSystemVersionNumbers(&major, &minor, &bugFix);
  *os_version = StringPrintf("%d.%d.%d", major, minor, bugFix);
#else
  *os_version = "unknown";
#endif
}

// Create a MIME boundary marker (27 '-' characters followed by 16 hex digits).
void BugReportUtil::CreateMimeBoundary(std::string *out) {
  int r1 = rand();
  int r2 = rand();
  SStringPrintf(out, "---------------------------%08X%08X", r1, r2);
}

// static
void BugReportUtil::SendReport(Profile* profile,
    std::string page_title_text,
    int problem_type,
    std::string page_url_text,
    std::string description,
    const char* png_data,
    int png_data_length) {
  GURL post_url(WideToUTF8(l10n_util::GetString(IDS_BUGREPORT_POST_URL)));
  std::string mime_boundary;
  CreateMimeBoundary(&mime_boundary);

  // Create a request body and add the mandatory parameters.
  std::string post_body;

  // Add the protocol version:
  post_body.append("--" + mime_boundary + "\r\n");
  post_body.append("Content-Disposition: form-data; "
                   "name=\"data_version\"\r\n\r\n");
  post_body.append(StringPrintf("%d\r\n", kBugReportVersion));

  // Add the page title.
  post_body.append("--" + mime_boundary + "\r\n");
  post_body.append(page_title_text + "\r\n");

  // Add the problem type.
  post_body.append("--" + mime_boundary + "\r\n");
  post_body.append("Content-Disposition: form-data; "
                   "name=\"problem\"\r\n\r\n");
  post_body.append(StringPrintf("%d\r\n", problem_type));

  // Add in the URL, if we have one.
  post_body.append("--" + mime_boundary + "\r\n");
  post_body.append("Content-Disposition: form-data; "
                   "name=\"url\"\r\n\r\n");

  // Convert URL to UTF8.
  if (page_url_text.empty())
    post_body.append("n/a\r\n");
  else
    post_body.append(page_url_text + "\r\n");

  // Add Chrome version.
  post_body.append("--" + mime_boundary + "\r\n");
  post_body.append("Content-Disposition: form-data; "
                   "name=\"chrome_version\"\r\n\r\n");

  std::string chrome_version;
  scoped_ptr<FileVersionInfo> version_info(
      FileVersionInfo::CreateFileVersionInfoForCurrentModule());
  if (version_info.get()) {
    chrome_version = WideToUTF8(version_info->product_name()) + " - " +
        WideToUTF8(version_info->file_version()) +
        " (" + WideToUTF8(version_info->last_change()) + ")";
  }

  if (chrome_version.empty())
    post_body.append("n/a\r\n");
  else
    post_body.append(chrome_version + "\r\n");

  // Add OS version (eg, for WinXP SP2: "5.1.2600 Service Pack 2").
  std::string os_version = "";
  post_body.append("--" + mime_boundary + "\r\n");
  post_body.append("Content-Disposition: form-data; "
                   "name=\"os_version\"\r\n\r\n");
  SetOSVersion(&os_version);
  post_body.append(os_version + "\r\n");

  // Add locale.
#if defined(OS_MACOSX)
  std::string chrome_locale = g_browser_process->GetApplicationLocale();
#else
  icu::Locale locale = icu::Locale::getDefault();
  const char *lang = locale.getLanguage();
  std::string chrome_locale = (lang)? lang:"en";
#endif

  post_body.append("--" + mime_boundary + "\r\n");
  post_body.append("Content-Disposition: form-data; "
                   "name=\"chrome_locale\"\r\n\r\n");
  post_body.append(chrome_locale + "\r\n");

  // Add a description if we have one.
  post_body.append("--" + mime_boundary + "\r\n");
  post_body.append("Content-Disposition: form-data; "
                   "name=\"description\"\r\n\r\n");

  if (description.empty())
    post_body.append("n/a\r\n");
  else
    post_body.append(description + "\r\n");

  // Include the page image if we have one.
  if (png_data != NULL && png_data_length > 0) {
    post_body.append("--" + mime_boundary + "\r\n");
    post_body.append("Content-Disposition: form-data; name=\"screenshot\"; "
                      "filename=\"screenshot.png\"\r\n");
    post_body.append("Content-Type: application/octet-stream\r\n");
    post_body.append(StringPrintf("Content-Length: %lu\r\n\r\n",
                     png_data_length));
    post_body.append(png_data, png_data_length);
    post_body.append("\r\n");
  }

  // TODO(awalker): include the page source if we can get it.
  // if (include_page_source_checkbox_->checked()) {
  // }

  // Terminate the body.
  post_body.append("--" + mime_boundary + "--\r\n");

  // We have the body of our POST, so send it off to the server.
  URLFetcher* fetcher = new URLFetcher(post_url, URLFetcher::POST,
                                       new BugReportUtil::PostCleanup);
  fetcher->set_request_context(profile->GetRequestContext());
  std::string mime_type("multipart/form-data; boundary=");
  mime_type += mime_boundary;
  fetcher->set_upload_data(mime_type, post_body);
  fetcher->Start();
}

// static
std::string BugReportUtil::GetMimeType() {
  std::string mime_type("multipart/form-data; boundary=");
  std::string mime_boundary;
  CreateMimeBoundary(&mime_boundary);
  mime_type += mime_boundary;
  return mime_type;
}

// static
void BugReportUtil::ReportPhishing(TabContents* currentTab,
                                   const std::string& phishing_url) {
  currentTab->controller().LoadURL(
      safe_browsing_util::GeneratePhishingReportUrl(
          kReportPhishingUrl, phishing_url),
      GURL(),
      PageTransition::LINK);
}