Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");

// These tables have a different update URL (for v4).
const TEST_TABLE_DATA_V4 = {
  tableName: "test-phish-proto",
  providerName: "google4",
  updateUrl: "http://localhost:5555/safebrowsing/update?",
  gethashUrl: "http://localhost:5555/safebrowsing/gethash-v4?",
};

const PREF_NEXTUPDATETIME_V4 = "browser.safebrowsing.provider.google4.nextupdatetime";

let gListManager = Cc["@mozilla.org/url-classifier/listmanager;1"]
                     .getService(Ci.nsIUrlListManager);

let gCompleter = Cc["@mozilla.org/url-classifier/hashcompleter;1"]
                    .getService(Ci.nsIUrlClassifierHashCompleter);

XPCOMUtils.defineLazyServiceGetter(this, 'gUrlUtil',
                                   '@mozilla.org/url-classifier/utils;1',
                                   'nsIUrlClassifierUtils');

// Handles request for TEST_TABLE_DATA_V4.
let gHttpServV4 = null;
let gExpectedGetHashQueryV4 = "";

const NEW_CLIENT_STATE = 'sta\0te';
const CHECKSUM = '\x30\x67\xc7\x2c\x5e\x50\x1c\x31\xe3\xfe\xca\x73\xf0\x47\xdc\x34\x1a\x95\x63\x99\xec\x70\x5e\x0a\xee\x9e\xfb\x17\xa1\x55\x35\x78';

prefBranch.setBoolPref("browser.safebrowsing.debug", true);

// The "\xFF\xFF" is to generate a base64 string with "/".
prefBranch.setCharPref("browser.safebrowsing.id", "Firefox\xFF\xFF");

// Register tables.
gListManager.registerTable(TEST_TABLE_DATA_V4.tableName,
                           TEST_TABLE_DATA_V4.providerName,
                           TEST_TABLE_DATA_V4.updateUrl,
                           TEST_TABLE_DATA_V4.gethashUrl);

// This is unfortunately needed since v4 gethash request
// requires the threat type (table name) as well as the
// state it's associated with. We have to run the update once
// to have the state written.
add_test(function test_update_v4() {
  gListManager.disableUpdate(TEST_TABLE_DATA_V4.tableName);
  gListManager.enableUpdate(TEST_TABLE_DATA_V4.tableName);

  // Force table update.
  prefBranch.setCharPref(PREF_NEXTUPDATETIME_V4, "1");
  gListManager.maybeToggleUpdateChecking();
});

add_test(function test_getHashRequestV4() {
  let request = gUrlUtil.makeFindFullHashRequestV4([TEST_TABLE_DATA_V4.tableName],
                                                   [btoa(NEW_CLIENT_STATE)],
                                                   [btoa("0123"), btoa("1234567"), btoa("1111")],
                                                   1,
                                                   3);
  gExpectedGetHashQueryV4 = '&$req=' + request;

  let completeFinishedCnt = 0;

  gCompleter.complete("0123", TEST_TABLE_DATA_V4.gethashUrl, TEST_TABLE_DATA_V4.tableName, {
    completion: function (hash, table, chunkId) {
      equal(hash, "01234567890123456789012345678901");
      equal(table, TEST_TABLE_DATA_V4.tableName);
      equal(chunkId, 0);
      do_print("completion: " + hash + ", " + table + ", " + chunkId);
    },

    completionFinished: function (status) {
      equal(status, Cr.NS_OK);
      completeFinishedCnt++;
      if (3 === completeFinishedCnt) {
        run_next_test();
      }
    },
  });

  gCompleter.complete("1234567", TEST_TABLE_DATA_V4.gethashUrl, TEST_TABLE_DATA_V4.tableName, {
    completion: function (hash, table, chunkId) {
      equal(hash, "12345678901234567890123456789012");
      equal(table, TEST_TABLE_DATA_V4.tableName);
      equal(chunkId, 0);
      do_print("completion: " + hash + ", " + table + ", " + chunkId);
    },

    completionFinished: function (status) {
      equal(status, Cr.NS_OK);
      completeFinishedCnt++;
      if (3 === completeFinishedCnt) {
        run_next_test();
      }
    },
  });

  gCompleter.complete("1111", TEST_TABLE_DATA_V4.gethashUrl, TEST_TABLE_DATA_V4.tableName, {
    completion: function (hash, table, chunkId) {
      ok(false, "1111 is not the prefix of " + hash);
    },

    completionFinished: function (status) {
      equal(status, Cr.NS_OK);
      completeFinishedCnt++;
      if (3 === completeFinishedCnt) {
        run_next_test();
      }
    },
  });
});

function run_test() {
  gHttpServV4 = new HttpServer();
  gHttpServV4.registerDirectory("/", do_get_cwd());

  // Update handler. Will respond a valid state to be verified in the
  // gethash handler.
  gHttpServV4.registerPathHandler("/safebrowsing/update", function(request, response) {
    response.setHeader("Content-Type",
                       "application/vnd.google.safebrowsing-update", false);
    response.setStatusLine(request.httpVersion, 200, "OK");

    // The protobuf binary represention of response:
    //
    // [
    //   {
    //     'threat_type': 2, // SOCIAL_ENGINEERING_PUBLIC
    //     'response_type': 2, // FULL_UPDATE
    //     'new_client_state': 'sta\x00te', // NEW_CLIENT_STATE
    //     'checksum': { "sha256": CHECKSUM }, // CHECKSUM
    //     'additions': { 'compression_type': RAW,
    //                    'prefix_size': 4,
    //                    'raw_hashes': "00000001000000020000000300000004"}
    //   }
    // ]
    //
    let content = "\x0A\x4A\x08\x02\x20\x02\x2A\x18\x08\x01\x12\x14\x08\x04\x12\x10\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\x3A\x06\x73\x74\x61\x00\x74\x65\x42\x22\x0A\x20\x30\x67\xC7\x2C\x5E\x50\x1C\x31\xE3\xFE\xCA\x73\xF0\x47\xDC\x34\x1A\x95\x63\x99\xEC\x70\x5E\x0A\xEE\x9E\xFB\x17\xA1\x55\x35\x78\x12\x08\x08\x08\x10\x80\x94\xEB\xDC\x03";

    response.bodyOutputStream.write(content, content.length);

    waitUntilMetaDataSaved(NEW_CLIENT_STATE, CHECKSUM, () => {
      run_next_test();
    });

  });

  // V4 gethash handler.
  gHttpServV4.registerPathHandler("/safebrowsing/gethash-v4", function(request, response) {
    equal(request.queryString, gExpectedGetHashQueryV4);

    // { nsCString("01234567890123456789012345678901"), SOCIAL_ENGINEERING_PUBLIC, { 8, 500 } },
    // { nsCString("12345678901234567890123456789012"), SOCIAL_ENGINEERING_PUBLIC, { 7, 100} },
    // { nsCString("23456789012345678901234567890123"), SOCIAL_ENGINEERING_PUBLIC, { 1, 20 } },
    let content = "\x0A\x2D\x08\x02\x1A\x22\x0A\x20\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x30\x31\x2A\x05\x08\x08\x10\xF4\x03\x0A\x2C\x08\x02\x1A\x22\x0A\x20\x31\x32\x33\x34\x35\x36\x37\x38\x39\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x30\x31\x32\x2A\x04\x08\x07\x10\x64\x0A\x2C\x08\x02\x1A\x22\x0A\x20\x32\x33\x34\x35\x36\x37\x38\x39\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x30\x31\x32\x33\x2A\x04\x08\x01\x10\x14\x12\x04\x08\x0C\x10\x0A\x1A\x04\x08\x78\x10\x09";

    response.setStatusLine(request.httpVersion, 200, "OK");
    response.bodyOutputStream.write(content, content.length);
  });

  gHttpServV4.start(5555);

  run_next_test();
}