</div>
</div>
<div class="card-body">
- <div class="row">
- <div class="col-lg-6">
+ <div class="row g-3">
+ <div class="col-lg-auto d-flex">
<div class="card bg-light shadow card-body card p-2">
<p>Learn Bayesian classifier:</p>
<form>
</form>
</div>
</div>
- <div class="col-lg-6">
- <div class="card bg-light shadow card-body card p-2">
+ <div class="col-lg d-flex">
+ <div class="card bg-light shadow card-body p-2">
<p>Fuzzy hash storage management:</p>
- <form class="d-flex">
- <div class="d-flex align-items-center">
- <label for="fuzzyFlagText">Flag:</label>
- <input name="fuzzyFlagText" id="fuzzyFlagText" class="form-control ms-1" type="number" value="1"/>
+ <div class="row g-2 align-items-center">
+ <div class="col-auto d-flex align-items-center me-1">
+ <label for="fuzzyFlagText" class="me-1">Flag:</label>
+ <input id="fuzzyFlagText" class="form-control" type="number" value="1"/>
</div>
- <div class="d-flex align-items-center ms-2">
- <label for="fuzzyWeightText">Weight:</label>
- <input name="fuzzyWeightText" id="fuzzyWeightText" class="form-control ms-1" type="number" value="1"/>
+ <div class="col-auto d-flex align-items-center me-2">
+ <label for="fuzzyWeightText" class="me-1">Weight:</label>
+ <input id="fuzzyWeightText" class="form-control" type="number" value="1"/>
</div>
- <button class="btn btn-warning ms-2 d-flex align-items-center" data-upload="fuzzyadd" disabled><i class="fas fa-circle-plus me-2"></i>Add hash</button>
- <button class="btn btn-danger ms-2 d-flex align-items-center" data-upload="fuzzydel" disabled><i class="fas fa-trash-can me-2"></i>Delete hash</button>
- </form>
+ <div class="col-auto">
+ <button class="btn btn-warning me-1" data-upload="fuzzyadd" disabled><i class="fas fa-circle-plus me-2"></i>Add to storage</button>
+ <button class="btn btn-danger" data-upload="fuzzydel" disabled><i class="fas fa-trash-can me-2"></i>Delete from storage</button>
+ </div>
+ </div>
+ <div class="row mt-3">
+ <div class="col">
+ <label for="fuzzyDelList" class="form-label">Hashes to delete</label>
+ <textarea class="form-control" id="fuzzyDelList" rows="3" placeholder="Enter one hash per line, or separate with commas, semicolons, or spaces."></textarea>
+ </div>
+ </div>
+ <div class="row mt-2">
+ <div class="col d-flex justify-content-end">
+ <button class="btn btn-danger me-2" id="deleteHashesBtn" disabled>
+ <i class="fas fa-trash-can me-2"></i><span class="btn-label">Delete hashes</span>
+ </button>
+ <button class="btn btn-secondary" id="clearHashesBtn" disabled>
+ <i class="fas fa-eraser me-2"></i>Clear
+ </button>
+ </div>
+ </div>
</div>
</div>
</div>
$("#" + source + "TextSource").val("");
}
- function uploadText(data, source, headers) {
+ function uploadText(data, source, headers, method = "POST") {
+ const deferred = new $.Deferred();
+
let url = null;
if (source === "spam") {
url = "learnspam";
url = "fuzzyadd";
} else if (source === "fuzzydel") {
url = "fuzzydel";
+ } else if (source === "fuzzydelhash") {
+ url = "fuzzydelhash";
} else if (source === "scan") {
url = "checkv2";
}
params: {
processData: false,
},
- method: "POST",
+ method: method,
headers: headers,
success: function (json, jqXHR) {
cleanTextUpload(source);
if (jqXHR.status !== 200) {
common.alertMessage("alert-info", jqXHR.statusText);
}
+ deferred.resolve();
},
+ complete: () => deferred.resolve(),
server: server()
});
+
+ return deferred.promise();
}
function enable_disable_scan_btn(disable) {
- $("#scan button:not(#cleanScanHistory, #scanOptionsToggle, .ft-columns-btn)")
+ $("#scan button:not(#cleanScanHistory, #deleteHashesBtn, #scanOptionsToggle, .ft-columns-btn)")
.prop("disabled", (disable || $.trim($("textarea").val()).length === 0));
}
return false;
});
+
+ function setDelhashButtonsDisabled(disabled = true) {
+ ["#deleteHashesBtn", "#clearHashesBtn"].forEach((s) => $(s).prop("disabled", disabled));
+ }
+
+ /**
+ * Parse a textarea (or any input) value into an array of non-empty tokens.
+ * Splits on commas, semicolons or any whitespace (space, tab, newline).
+ *
+ * @param {string} selector - jQuery selector for the input element.
+ * @returns {string[]} - Trimmed, non-empty tokens.
+ */
+ function parseHashes(selector) {
+ return $(selector).val()
+ .split(/[,\s;]+/)
+ .map((t) => t.trim())
+ .filter((t) => t.length > 0);
+ }
+
+ $("#fuzzyDelList").on("input", () => {
+ const hasTokens = parseHashes("#fuzzyDelList").length > 0;
+ setDelhashButtonsDisabled(!hasTokens);
+ });
+
+ $("#deleteHashesBtn").on("click", () => {
+ $("#fuzzyDelList").prop("disabled", true);
+ setDelhashButtonsDisabled();
+ $("#deleteHashesBtn").find(".btn-label").text("Deleting…");
+
+ const hashes = parseHashes("#fuzzyDelList");
+ const promises = hashes.map((h) => {
+ const headers = {
+ flag: $("#fuzzyFlagText").val(),
+ Hash: h
+ };
+ return uploadText(null, "fuzzydelhash", headers, "GET");
+ });
+
+ $.when.apply($, promises).always(() => {
+ $("#fuzzyDelList").prop("disabled", false);
+ setDelhashButtonsDisabled(false);
+ $("#deleteHashesBtn").find(".btn-label").text("Delete hashes");
+ });
+ });
+
+ $("#clearHashesBtn").on("click", () => {
+ $("#fuzzyDelList").val("").focus();
+ setDelhashButtonsDisabled();
+ });
+
+
function fileInputHandler(obj) {
({files} = obj);
filesIdx = 0;