From 30fecd588fc6ddd1114b06bbc6ae8b17ca7fe06f Mon Sep 17 00:00:00 2001 From: towards-a-new-leftypol Date: Thu, 24 Aug 2023 18:07:37 -0400 Subject: [PATCH] Substitute illegal attachments with redacted thumbnail --- redacted.jpg | Bin 0 -> 3167 bytes script.js | 231 ++++++++++++++++++++++++++++----------------------- style.css | 14 ++++ 3 files changed, 139 insertions(+), 106 deletions(-) create mode 100644 redacted.jpg diff --git a/redacted.jpg b/redacted.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5068e26e0d1fb8f96cd671b93cbce6417d54d8a3 GIT binary patch literal 3167 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 200 && status_code < 300) { resolve(e.target.responseText); } else { - reject(new Error('API responded with ' + status)); + reject(new Error('API responded with ' + status + ". " + e.target.responseText)); } } else { if (e.target.status >= 200 && e.target.status < 300) { return resolve(e.target); } else { - reject(new Error('API responded with ' + e.target.status)); + reject(new Error('API responded with ' + e.target.status + ". " + e.target.responseText)); } } } @@ -341,6 +343,31 @@ function get(url, xhr_req_follower, headers) { }); } +function post(url, body, xhr_req_follower, headers) { + return new Promise(function (resolve, reject) { + var req = new XMLHttpRequest(); + xhr_req_follower(req); + req.onload = resolveResponse(resolve, reject); + req.onerror = reject; + req.open('POST', url); + + if (headers) { + Object.entries(headers) + .forEach(function([header_name, header_value]) { + req.setRequestHeader(header_name, header_value); + }); + } + + req.setRequestHeader('Content-Type', 'application/json'); + + if (body) { + req.send(JSON.stringify(body)); + } else { + req.send(); + } + }); +} + // Given a BigInt that is packed bytes, unpack the bytes into an ArrayBuffer function unpackBytes(bigint) { const buffer = new ArrayBuffer(8); @@ -384,100 +411,6 @@ function hammingDistance(buffer1, buffer2) { return distance; } -function parseFilePath(s) { - return JSON.parse(s); -} - -function FileSize(s) { - this.filesize = s; -} - -function tree(rows) { - var c = {}; - - for (var row of rows) { - var path = parseFilePath(row.path); - var tree = c; - var previous = c; - var filename = 'NOPATH'; - - for (var p of path) { - previous = tree; - - if (p in tree) { - tree = tree[p]; - } else { - var newtree = {}; - tree[p] = newtree; - tree = newtree; - } - - filename = p; - } - - previous[filename] = new FileSize(row.filesize); - } - - console.log(c); - return c; -} - -function renderTree(rows) { - var list_elem = ul(); - - function draw(list_elem, t) { - Object.entries(t) - .forEach(function([key, val]) { - if (val instanceof FileSize) { - var contents = div(span(text(key))); - contents.appendChild(italic(text(val.filesize))); - list_elem.appendChild(contents); - } else { - var new_list = ul(); - var elem = li(text(key)); - elem.appendChild(new_list); - list_elem.appendChild(elem); - draw(new_list, val); - } - }); - } - - draw(list_elem, tree(rows)); - - return list_elem; -} - -function assembleTable(rows) { - var t = document.createElement('table'); - - rows.forEach(function(r) { - var row = t.insertRow(); - - TableHeadings.forEach(function(header) { - var cell = row.insertCell(); - - cell.appendChild(header[1](r)); - }); - }); - - var thead = t.createTHead().insertRow(); - - TableHeadingNames.forEach(function(header) { - var th = document.createElement('th'); - - th.appendChild(text(header)); - thead.appendChild(th); - }); - - var countElem = div(text(rows.length + ' rows')) - - var wrapper = div(); - wrapper.appendChild(t); - wrapper.appendChild(countElem); - - return wrapper; -} - function text(s) { return document.createTextNode(s); } @@ -527,20 +460,31 @@ function mkSpamImgElement(img_description) { var mime = img_description.mimetype; var thumb_url; - if (mime == "audio/mpeg") { + var illegal = img_description.illegal; + console.log('img_description.illegal', img_description.illegal); + + if (illegal) { + thumb_url = `/bin/?path=${GlobalVars.settings.redacted_thumbnail_path}&mimetype=image/jpeg` + } else if (mime == "audio/mpeg") { thumb_url = `/bin/?path=${GlobalVars.settings.audio_mpeg_thumbnail_path}&mimetype=image/jpeg` } else { thumb_url = `/bin/?path=${GlobalVars.settings.content_directory}/${img_description.md5_hash}_thumbnail.jpg&mimetype=image/jpeg` } - var image_url = `/bin/?path=${GlobalVars.settings.content_directory}/${img_description.md5_hash}.attachment&mimetype=${mime}` img_elem.setAttribute('src', thumb_url); img_elem.setAttribute('decoding', 'async'); img_elem.setAttribute('loading', 'lazy'); - var a_elem = document.createElement('a'); - a_elem.setAttribute('href', image_url); - a_elem.appendChild(img_elem); + var a_elem; + + if (!illegal) { + var image_url = `/bin/?path=${GlobalVars.settings.content_directory}/${img_description.md5_hash}.attachment&mimetype=${mime}` + a_elem = document.createElement('a'); + a_elem.setAttribute('href', image_url); + a_elem.appendChild(img_elem); + } else { + a_elem = div(img_elem); + } var img_container_elem = div(a_elem); img_container_elem.classList.add('post--image_container'); @@ -570,6 +514,49 @@ function linkToBoard(website_name, board_name, thread_id) { } } +function attachmentCanBeMarkedIllegal(attachment) { + return attachment.phash != null && !attachment.illegal; +} + +function shouldDisplayMarkIllegalButton(post) { + let result = false; + + for (let attachment of (post.known_spam_post_attachment || [])) { + result = result || attachmentCanBeMarkedIllegal(attachment); + } + + return result; +} + +function isIllegal(post) { + let result = false; + + for (let attachment of (post.known_spam_post_attachment || [])) { + result = result || attachment.illegal; + } + + return result; +} + +async function onClickMarkIllegal(phashes, e) { + console.log('CLICK', phashes); + e.stopPropagation(); + e.preventDefault(); + + const url = new URL('/illegal', window.location.origin); + phashes.forEach(function(p) { + console.log('onClickMarkIllegal', typeof p, p); + url.searchParams.append('phash', p); + }); + + await post(url.toString(), null, cancel) + .then(function() { + location.reload(); + }) + .catch(caught.bind(this, "Failed to mark post illegal because:")); + +} + function renderPostElem(post) { const postContainer = div(); postContainer.classList.add('post'); @@ -599,6 +586,34 @@ function renderPostElem(post) { postHeader.appendChild(span(text((new Date(post.time_stamp).toUTCString())))); + if (shouldDisplayMarkIllegalButton(post)) { + const mark_illegal = span() + mark_illegal.classList.add('post--mark_illegal'); + const mark_illegal_a = document.createElement('a'); + + mark_illegal_a.appendChild(text('Mark Illegal')); + + mark_illegal_a.setAttribute('href', '#'); + mark_illegal_a.setAttribute('title', 'Permanently delete associated pictures and thumbnails but keep metadata for future matching'); + mark_illegal.appendChild(text('[')); + mark_illegal.appendChild(mark_illegal_a); + mark_illegal.appendChild(text(']')); + + const phashes = (post.known_spam_post_attachment || []) + .map(function(p) { + return bufferToHex(unpackBytes(BigInt(p.phash))); + }); + + mark_illegal_a.addEventListener('click', + onClickMarkIllegal.bind(this, phashes)); + + postHeader.appendChild(mark_illegal); + } else if (isIllegal(post)) { + const mark_illegal = span(text('illegal')) + mark_illegal.classList.add('post--is_illegal'); + postHeader.appendChild(mark_illegal); + } + postContainer.appendChild(postHeader); const postElem = div(); @@ -767,9 +782,13 @@ function loadKnownSpamPosts(params) { json.forEach(function (post) { var postElem = renderOverviewPost(post); - postElem.addEventListener('click', function() { - changeUrl('/spam_post/' + post.text_post_id, new URLSearchParams(), true); - }); + postElem.addEventListener('click', + changeUrl.bind( + this, + '/spam_post/' + post.text_post_id, + new URLSearchParams(), + true + )); mainElem.appendChild(postElem); }); diff --git a/style.css b/style.css index 871b502..5b7899a 100644 --- a/style.css +++ b/style.css @@ -64,6 +64,20 @@ header { flex: 1; } +.post--header span.post--mark_illegal { + flex-basis: fit-content; + flex-grow: 0; +} + +.post--header span.post--is_illegal { + flex-basis: fit-content; + flex-grow: 0; + color: orange; + font-weight: bold; + font-family: monospace; + text-transform: uppercase; +} + nav a { margin: 1rem; font-size: 1.5em;