diff --git a/nobilis b/nobilis index 046fbb4..ca29a63 160000 --- a/nobilis +++ b/nobilis @@ -1 +1 @@ -Subproject commit 046fbb4850b95a3f5ed0759038792eb5e2a7be0c +Subproject commit ca29a638830673ce0277249f4e934a927c1152d0 diff --git a/src/renderer/src/lib/format.test.ts b/src/renderer/src/lib/format.test.ts index 42585f2..a954410 100644 --- a/src/renderer/src/lib/format.test.ts +++ b/src/renderer/src/lib/format.test.ts @@ -9,6 +9,7 @@ import { extractMedia, extractQuoteBlocks, formatMessage, + isForumUpload, isGifPage, hostnameOf, ircFormat, @@ -286,3 +287,23 @@ describe('links to the pages GIFs are shared from', () => { expect(media.map((m) => m.kind)).toEqual(['video', 'image']) }) }) + +describe('the forum\'s own files', () => { + const picture = 'https://uploads.kiwifarms.st/data/attachments/9671/9671535-a2e5ae0d55aadd338162770b3fa79f97-alt.avif?hash=-d_TarFo41' + + it('are known by where they are', () => { + expect(isForumUpload(picture)).toBe(true) + expect(isForumUpload('https://kiwifarms.st/data/video/9667/9667943-abc.mp4?hash=x')).toBe(true) + expect(isForumUpload('https://kiwifarms.st/threads/a-thread.123/')).toBe(false) + expect(isForumUpload('https://example.com/data/attachments/1/2-a.avif')).toBe(false) + }) + + it('are not loaded by the window, which would ask for them from the reader\'s own address', () => { + expect(extractMedia(`[img]${picture}[/img]`, {})).toEqual([]) + expect(extractMedia(`look ${picture}`, { contentSniffing: true, onNeedSniff: () => { throw new Error('asked') } })).toEqual([]) + }) + + it('leave other pictures and an AVIF anywhere else as they were', () => { + expect(extractMedia('https://i.ibb.co/JRkN8BFv/photo.avif', {}).map((m) => m.kind)).toEqual(['image']) + }) +}) diff --git a/src/renderer/src/lib/format.ts b/src/renderer/src/lib/format.ts index 998bb51..0c49354 100644 --- a/src/renderer/src/lib/format.ts +++ b/src/renderer/src/lib/format.ts @@ -594,6 +594,27 @@ const VIDEO_EXT_RE = /\.(mp4|webm|mov|mkv|ogv)(\?\S*)?$/i */ const GIF_PAGE_RE = /^https?:\/\/(?:www\.)?(?:tenor\.com\/(?:[a-z]{2}(?:-[A-Za-z]{2})?\/)?view\/|giphy\.com\/(?:gifs|embed)\/|klipy\.com\/(?:gifs?|clips?|stickers?|memes?)\/)/i +/** + * One of the forum's own files: what its uploads host serves, a picture or a + * video. + * + * Not loaded by the window. The daemon fetches these through the account's own + * route and puts the file on the message, which is what draws; left to the + * window they would be asked for from the reader's own address - which the host + * turns away, and which for somebody on Tor is the leak routing is for. Between + * the message arriving and the file being kept the link is simply a link, and if + * it cannot be kept it stays one. + * + * The onion address is here as the clearnet one because the text has been + * through `clearnetLinks` by the time it reaches this. + */ +const FORUM_UPLOAD_RE = /^https?:\/\/(?:uploads\.|www\.)?kiwifarms\.st\/data\/(?:attachments|video)\//i + +/** Whether this is a link to one of the forum's own files. */ +export function isForumUpload(url: string): boolean { + return FORUM_UPLOAD_RE.test(url) +} + /** Whether this is a link to a GIF's page on one of those sites. */ export function isGifPage(url: string): boolean { return GIF_PAGE_RE.test(url) @@ -668,7 +689,7 @@ export function extractMedia( const url = raw.replace(/[),.;!?]+$/, '') const yt = youtubeId(url) const identity = yt ? `yt:${yt}` : url - if (seen.has(identity) || targets.has(url)) continue + if (seen.has(identity) || targets.has(url) || isForumUpload(url)) continue if (yt) { seen.add(identity)