diff --git a/src/tools/index.ts b/src/tools/index.ts index 388cfaf49..886db95d9 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -2,6 +2,7 @@ import { tool as base64FileConverter } from './base64-file-converter'; import { tool as base64StringConverter } from './base64-string-converter'; import { tool as basicAuthGenerator } from './basic-auth-generator'; import { tool as emailNormalizer } from './email-normalizer'; +import { tool as xVrSpamcauseDecoder } from './x-vr-spamcause-decoder'; import { tool as asciiTextDrawer } from './ascii-text-drawer'; @@ -164,7 +165,15 @@ export const toolsByCategory: ToolCategory[] = [ }, { name: 'Network', - components: [ipv4SubnetCalculator, ipv4AddressConverter, ipv4RangeExpander, macAddressLookup, macAddressGenerator, ipv6UlaGenerator], + components: [ + ipv4SubnetCalculator, + ipv4AddressConverter, + ipv4RangeExpander, + macAddressLookup, + macAddressGenerator, + ipv6UlaGenerator, + xVrSpamcauseDecoder, + ], }, { name: 'Math', diff --git a/src/tools/x-vr-spamcause-decoder/index.ts b/src/tools/x-vr-spamcause-decoder/index.ts new file mode 100644 index 000000000..e544e9821 --- /dev/null +++ b/src/tools/x-vr-spamcause-decoder/index.ts @@ -0,0 +1,12 @@ +import { RecordMail } from '@vicons/tabler'; +import { defineTool } from '../tool'; + +export const tool = defineTool({ + name: 'X vr spamcause decoder', + path: '/x-vr-spamcause-decoder', + description: 'Decode X-VR-SPAMCAUSE header in OVH mails', + keywords: ['ovh', 'vade', 'retro', 'vr', 'spam', 'spamcause', 'decoder'], + component: () => import('./x-vr-spamcause-decoder.vue'), + icon: RecordMail, + createdAt: new Date('2024-08-15'), +}); diff --git a/src/tools/x-vr-spamcause-decoder/x-vr-spamcause-decoder.service.test.ts b/src/tools/x-vr-spamcause-decoder/x-vr-spamcause-decoder.service.test.ts new file mode 100644 index 000000000..22f4bda6f --- /dev/null +++ b/src/tools/x-vr-spamcause-decoder/x-vr-spamcause-decoder.service.test.ts @@ -0,0 +1,10 @@ +import { describe, expect, it } from 'vitest'; +import { spamCauseDecode } from './x-vr-spamcause-decoder.service'; + +describe('x-vr-spamcause-decoder', () => { + it('should decode X-VR-SPAMCAUSE', () => { + expect(spamCauseDecode('gggruggvucftvghtrhhoucdtuddrfeelgedrvdduucetufdoteggodetrfdotffvucfrrhhofhhilhgvmecuqfggjfenuceurghilhhouhhtmecufedttdenucgohfhorhgsihguuggvnhfjughrucdlhedttddm')).toBe( + 'Vade Retro 01.394.21 AS+AV+AP+RT Profile: OVH; Bailout: 300; ^ForbiddenHdr (500)', + ); + }); +}); diff --git a/src/tools/x-vr-spamcause-decoder/x-vr-spamcause-decoder.service.ts b/src/tools/x-vr-spamcause-decoder/x-vr-spamcause-decoder.service.ts new file mode 100644 index 000000000..ffb0135d5 --- /dev/null +++ b/src/tools/x-vr-spamcause-decoder/x-vr-spamcause-decoder.service.ts @@ -0,0 +1,24 @@ +function unrot(a: string, b: string, pos: number) { + if (b === undefined) { + return ''; + } + + if (pos % 2 === 0) { + const c = a; + a = b; + b = c; + } + + const offset = ('g'.charCodeAt(0) - a.charCodeAt(0)) * 16; + return String.fromCharCode((a.charCodeAt(0) + b.charCodeAt(0)) - 'x'.charCodeAt(0) - offset); +} + +export function spamCauseDecode(text: string) { + let result = ''; + + for (let n = 0; n < text.length; n += 2) { + result += unrot(text[n], text[n + 1], Math.floor(n / 2)); + } + + return result; +} diff --git a/src/tools/x-vr-spamcause-decoder/x-vr-spamcause-decoder.vue b/src/tools/x-vr-spamcause-decoder/x-vr-spamcause-decoder.vue new file mode 100644 index 000000000..317892871 --- /dev/null +++ b/src/tools/x-vr-spamcause-decoder/x-vr-spamcause-decoder.vue @@ -0,0 +1,24 @@ + + +