/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Check that CA is active if and only if:
// 1. browser.contentanalysis.enabled is true and
// 2. Either browser.contentanalysis.enabled was set by an enteprise
//    policy or the "-allow-content-analysis" command line arg was present
// We can't really test command line arguments so we instead use a test-only
// method to set the value the command-line is supposed to update.

"use strict";

const { EnterprisePolicyTesting, PoliciesPrefTracker } =
  ChromeUtils.importESModule(
    "resource://testing-common/EnterprisePolicyTesting.sys.mjs"
  );

const kIndividualPrefs = new Map([
  ["Enabled", "enabled"],
  ["PipeName", "pipe_path_name"],
  ["Timeout", "agent_timeout"],
  ["AllowUrl", "allow_url_regex_list"],
  ["DenyUrl", "deny_url_regex_list"],
  ["AgentName", "agent_name"],
  ["ClientSignature", "client_signature"],
  ["PerUser", "is_per_user"],
  ["MaxConnectionsCount", "max_connections"],
  ["ShowBlocked", "show_blocked_result"],
  ["DefaultResult", "default_result"],
  ["TimeoutResult", "timeout_result"],
  ["BypassForSameTab", "bypass_for_same_tab_operations"],
]);
function getIndividualPrefName(name) {
  is(
    kIndividualPrefs.has(name),
    true,
    `"${name}" passed to getIndividualPrefName() is valid`
  );
  return `browser.contentanalysis.${kIndividualPrefs.get(name)}`;
}
const kInterceptionPoints = [
  "clipboard",
  "download",
  "drag_and_drop",
  "file_upload",
  "print",
];
// Everything is on by default except download.
let kInterceptionPointsOnByDefault = kInterceptionPoints.slice();
kInterceptionPointsOnByDefault.splice(
  kInterceptionPointsOnByDefault.indexOf("download"),
  1
);
const kInterceptionPointsPlainTextOnly = ["clipboard", "drag_and_drop"];

const ca = Cc["@mozilla.org/contentanalysis;1"].getService(
  Ci.nsIContentAnalysis
);

add_task(async function test_ca_active() {
  PoliciesPrefTracker.start();
  ok(!ca.isActive, "CA is inactive when pref and cmd line arg are missing");

  // Set the pref without enterprise policy.  CA should not be active.
  Services.prefs.setBoolPref(getIndividualPrefName("Enabled"), true);
  ok(
    !ca.isActive,
    "CA is inactive when pref is set but cmd line arg is missing"
  );

  // Set the pref without enterprise policy but also set command line arg
  // property.  CA should be active.
  ca.testOnlySetCACmdLineArg(true);
  ok(ca.isActive, "CA is active when pref is set and cmd line arg is present");

  // Undo test-only value before later tests.
  ca.testOnlySetCACmdLineArg(false);
  ok(!ca.isActive, "properly unset cmd line arg value");

  // Disabled the pref with enterprise policy.  CA should not be active.
  await EnterprisePolicyTesting.setupPolicyEngineWithJson({
    policies: {
      ContentAnalysis: { Enabled: false },
    },
  });
  ok(!ca.isActive, "CA is inactive when disabled by enterprise policy pref");

  // Enabled the pref with enterprise policy.  CA should be active.
  await EnterprisePolicyTesting.setupPolicyEngineWithJson({
    policies: {
      ContentAnalysis: { Enabled: true },
    },
  });
  ok(ca.isActive, "CA is active when enabled by enterprise policy pref");
  for (let interceptionPoint of kInterceptionPoints) {
    const shouldBeEnabledByDefault = interceptionPoint !== "download";
    is(
      Services.prefs.getBoolPref(
        `browser.contentanalysis.interception_point.${interceptionPoint}.enabled`
      ),
      shouldBeEnabledByDefault,
      `${interceptionPoint} ${shouldBeEnabledByDefault ? "enabled" : "disabled"} by default`
    );
  }
  for (let interceptionPoint of kInterceptionPointsPlainTextOnly) {
    is(
      Services.prefs.getBoolPref(
        `browser.contentanalysis.interception_point.${interceptionPoint}.plain_text_only`
      ),
      true,
      `${interceptionPoint} plain_text_only on by default`
    );
  }

  Services.prefs.setBoolPref(getIndividualPrefName("Enabled"), false);
  PoliciesPrefTracker.stop();
});

add_task(async function test_ca_enterprise_config_with_default_prefs() {
  PoliciesPrefTracker.start();
  await EnterprisePolicyTesting.setupPolicyEngineWithJson({
    policies: {
      ContentAnalysis: {
        Enabled: true,
      },
    },
  });

  for (let individualPref of kIndividualPrefs.values()) {
    is(
      Services.prefs.prefIsLocked("browser.contentanalysis." + individualPref),
      true,
      `${individualPref} should be locked`
    );
  }

  for (let interceptionPoint of kInterceptionPoints) {
    is(
      Services.prefs.prefIsLocked(
        `browser.contentanalysis.interception_point.${interceptionPoint}.enabled`
      ),
      true,
      `${interceptionPoint} enabled should be locked`
    );
  }
  for (let interceptionPointPlainText of kInterceptionPointsPlainTextOnly) {
    is(
      Services.prefs.prefIsLocked(
        `browser.contentanalysis.interception_point.${interceptionPointPlainText}.plain_text_only`
      ),
      true,
      `${interceptionPointPlainText} plain_text_only should be locked`
    );
  }
  PoliciesPrefTracker.stop();
});

add_task(
  async function test_ca_enterprise_config_with_default_prefs_telemetry() {
    PoliciesPrefTracker.start();
    Services.fog.testResetFOG();
    await EnterprisePolicyTesting.setupPolicyEngineWithJson({
      policies: {
        ContentAnalysis: {
          Enabled: true,
        },
      },
    });
    // Trigger the content analysis service to ensure telemetry is recorded.
    let contentAnalysisService = Cc[
      "@mozilla.org/contentanalysis;1"
    ].getService(Ci.nsIContentAnalysis);
    is(
      contentAnalysisService.isActive,
      true,
      "Content Analysis service is active"
    );
    contentAnalysisService.forceRecreateClientForTest();

    is(
      Glean.contentAnalysis.agentName.testGetValue(),
      "A DLP agent",
      "agentName default"
    );
    is(
      Glean.contentAnalysis.interceptionPointsTurnedOff.testGetValue().length,
      1,
      "interceptionPointsTurnedOff default"
    );
    is(
      Glean.contentAnalysis.interceptionPointsTurnedOff.testGetValue()[0],
      "browser.contentanalysis.interception_point.download.enabled",
      "interceptionPointsTurnedOff default"
    );
    ok(
      Glean.contentAnalysis.showBlockedResult.testGetValue(),
      "showBlockedResult default"
    );
    is(
      Glean.contentAnalysis.defaultResult.testGetValue(),
      0,
      "defaultResult default"
    );
    is(
      Glean.contentAnalysis.timeoutResult.testGetValue(),
      0,
      "timeoutResult default"
    );
    is(
      Glean.contentAnalysis.clientSignature.testGetValue(),
      null,
      "clientSignature default"
    );
    ok(
      !Glean.contentAnalysis.bypassForSameTabOperations.testGetValue(),
      "bypassForSameTabOperations default"
    );
    ok(
      !Glean.contentAnalysis.allowUrlRegexListSet.testGetValue(),
      "allowUrlRegexListSet default"
    );
    ok(
      !Glean.contentAnalysis.denyUrlRegexListSet.testGetValue(),
      "denyUrlRegexListSet default"
    );

    PoliciesPrefTracker.stop();
  }
);

add_task(async function test_ca_enterprise_config() {
  PoliciesPrefTracker.start();
  const string1 = "this is a string";
  const string2 = "this is another string";
  const string3 = "an agent name";
  const string4 = "a client signature";

  await EnterprisePolicyTesting.setupPolicyEngineWithJson({
    policies: {
      ContentAnalysis: {
        PipePathName: "abc",
        AgentTimeout: 99,
        AllowUrlRegexList: string1,
        DenyUrlRegexList: string2,
        AgentName: string3,
        ClientSignature: string4,
        IsPerUser: true,
        MaxConnectionsCount: 3,
        ShowBlockedResult: false,
        DefaultResult: 1,
        TimeoutResult: 2,
        BypassForSameTabOperations: true,
        InterceptionPoints: {
          Clipboard: {
            Enabled: false,
            PlainTextOnly: false,
          },
          Download: {
            Enabled: true,
          },
          DragAndDrop: {
            Enabled: false,
            PlainTextOnly: false,
          },
          FileUpload: {
            Enabled: false,
          },
          Print: {
            Enabled: false,
          },
        },
      },
    },
  });

  is(
    Services.prefs.getStringPref(getIndividualPrefName("PipeName")),
    "abc",
    "pipe name match"
  );
  is(
    Services.prefs.getIntPref(getIndividualPrefName("Timeout")),
    99,
    "timeout match"
  );
  is(
    Services.prefs.getStringPref(getIndividualPrefName("AllowUrl")),
    string1,
    "allow urls match"
  );
  is(
    Services.prefs.getStringPref(getIndividualPrefName("DenyUrl")),
    string2,
    "deny urls match"
  );
  is(
    Services.prefs.getStringPref(getIndividualPrefName("AgentName")),
    string3,
    "agent names match"
  );
  is(
    Services.prefs.getStringPref(getIndividualPrefName("ClientSignature")),
    string4,
    "client signatures match"
  );
  is(
    Services.prefs.getIntPref(getIndividualPrefName("MaxConnectionsCount")),
    3,
    "connections count match"
  );
  is(
    Services.prefs.getBoolPref(getIndividualPrefName("PerUser")),
    true,
    "per user match"
  );
  is(
    Services.prefs.getBoolPref(getIndividualPrefName("ShowBlocked")),
    false,
    "show blocked match"
  );
  is(
    Services.prefs.getIntPref(getIndividualPrefName("DefaultResult")),
    1,
    "default result match"
  );
  is(
    Services.prefs.getIntPref(getIndividualPrefName("TimeoutResult")),
    2,
    "timeout result match"
  );
  is(
    Services.prefs.getBoolPref(getIndividualPrefName("BypassForSameTab")),
    true,
    "bypass for same tab operations match"
  );
  for (let interceptionPoint of kInterceptionPoints) {
    is(
      Services.prefs.getBoolPref(
        `browser.contentanalysis.interception_point.${interceptionPoint}.enabled`
      ),
      interceptionPoint === "download",
      `${interceptionPoint} interception point match`
    );
  }
  for (let interceptionPoint of kInterceptionPointsPlainTextOnly) {
    is(
      Services.prefs.getBoolPref(
        `browser.contentanalysis.interception_point.${interceptionPoint}.plain_text_only`
      ),
      false,
      `${interceptionPoint} interception point plain_text_only match`
    );
  }

  PoliciesPrefTracker.stop();
});

add_task(async function test_ca_enterprise_config_telemetry() {
  PoliciesPrefTracker.start();
  const string1 = "this is a string";
  const string2 = "this is another string";
  const string3 = "an agent name";
  const string4 = "a client signature";

  Services.fog.testResetFOG();

  await EnterprisePolicyTesting.setupPolicyEngineWithJson({
    policies: {
      ContentAnalysis: {
        Enabled: true,
        PipePathName: "abc",
        AgentTimeout: 99,
        AllowUrlRegexList: string1,
        DenyUrlRegexList: string2,
        AgentName: string3,
        ClientSignature: string4,
        IsPerUser: true,
        MaxConnectionsCount: 3,
        ShowBlockedResult: false,
        DefaultResult: 1,
        TimeoutResult: 2,
        BypassForSameTabOperations: true,
        InterceptionPoints: {
          Clipboard: {
            Enabled: false,
            PlainTextOnly: false,
          },
          Download: {
            Enabled: true,
          },
          DragAndDrop: {
            Enabled: false,
            PlainTextOnly: false,
          },
          FileUpload: {
            Enabled: false,
          },
          Print: {
            Enabled: false,
          },
        },
      },
    },
  });

  // Trigger the content analysis service to ensure telemetry is recorded.
  let contentAnalysisService = Cc["@mozilla.org/contentanalysis;1"].getService(
    Ci.nsIContentAnalysis
  );
  contentAnalysisService.forceRecreateClientForTest();
  is(
    contentAnalysisService.isActive,
    true,
    "Content Analysis service is active"
  );

  is(Glean.contentAnalysis.agentName.testGetValue(), string3, "agentName");
  const interceptionPointsTurnedOff =
    Glean.contentAnalysis.interceptionPointsTurnedOff.testGetValue();
  is(
    interceptionPointsTurnedOff.length,
    4,
    "interceptionPointsTurnedOff length"
  );
  for (let i = 0; i < interceptionPointsTurnedOff.length; i++) {
    is(
      interceptionPointsTurnedOff[i],
      `browser.contentanalysis.interception_point.${kInterceptionPointsOnByDefault[i]}.enabled`,
      `interceptionPointsTurnedOff ${kInterceptionPointsOnByDefault[i]}`
    );
  }
  ok(
    !Glean.contentAnalysis.showBlockedResult.testGetValue(),
    "showBlockedResult"
  );
  is(Glean.contentAnalysis.defaultResult.testGetValue(), 1, "defaultResult");
  is(Glean.contentAnalysis.timeoutResult.testGetValue(), 2, "timeoutResult");
  is(
    Glean.contentAnalysis.clientSignature.testGetValue(),
    string4,
    "clientSignature"
  );
  ok(
    Glean.contentAnalysis.bypassForSameTabOperations.testGetValue(),
    "bypassForSameTabOperations"
  );
  ok(
    Glean.contentAnalysis.allowUrlRegexListSet.testGetValue(),
    "allowUrlRegexListSet"
  );
  ok(
    Glean.contentAnalysis.denyUrlRegexListSet.testGetValue(),
    "denyUrlRegexListSet"
  );

  PoliciesPrefTracker.stop();
});

add_task(async function test_cleanup() {
  ca.testOnlySetCACmdLineArg(false);
  await EnterprisePolicyTesting.setupPolicyEngineWithJson({
    policies: {},
  });
  // These may have gotten set when ContentAnalysis was enabled through
  // the policy and do not get cleared if there is no ContentAnalysis
  // element - reset them manually here.
  ca.isSetByEnterprisePolicy = false;
  Services.prefs.unlockPref(getIndividualPrefName("Enabled"));
  Services.prefs.clearUserPref(getIndividualPrefName("Enabled"));
  for (let interceptionPoint of kInterceptionPoints) {
    const prefName = `browser.contentanalysis.interception_point.${interceptionPoint}.enabled`;
    Services.prefs.unlockPref(prefName);
    Services.prefs.clearUserPref(prefName);
  }
  for (let interceptionPoint of kInterceptionPointsPlainTextOnly) {
    const prefName = `browser.contentanalysis.interception_point.${interceptionPoint}.plain_text_only`;
    Services.prefs.unlockPref(prefName);
    Services.prefs.clearUserPref(prefName);
  }
});
