diff --git a/main/preload.ts b/main/preload.ts
index 0e4892842..30b96bb22 100644
--- a/main/preload.ts
+++ b/main/preload.ts
@@ -117,6 +117,10 @@ const ipc = {
ipcRenderer.send(IPC_MESSAGES.OPEN_EXTERNAL, link);
},
+ openDataURL(link: string, filename: string) {
+ ipcRenderer.send(IPC_MESSAGES.OPEN_DATA_URL, { link, filename });
+ },
+
async deleteFile(filePath: string) {
return (await ipcRenderer.invoke(
IPC_ACTIONS.DELETE_FILE,
diff --git a/main/registerIpcMainMessageListeners.ts b/main/registerIpcMainMessageListeners.ts
index 909bc38cf..802d3c6b1 100644
--- a/main/registerIpcMainMessageListeners.ts
+++ b/main/registerIpcMainMessageListeners.ts
@@ -1,8 +1,48 @@
-import { ipcMain, Menu, shell } from 'electron';
+import { ipcMain, Menu, shell, app } from 'electron';
+import fs from 'fs';
+import path from 'path';
import { Main } from '../main';
import { IPC_MESSAGES } from '../utils/messages';
import { emitMainProcessError } from 'backend/helpers';
+type DataURLParseResult = {
+ mediaType: string;
+ contentType: string;
+ base64: string;
+ data: string;
+ encoding: string;
+ buffer: Buffer;
+};
+function parseDataURL(url: string): null | DataURLParseResult {
+ const regex =
+ /^data:([a-z]+\/[a-z0-9-+.]+(;[a-z0-9-.!#$%*+.{}|~`]+=[a-z0-9-.!#$%*+.{}()_|~`]+)*)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@\/?%\s<>]*?)$/i;
+
+ const parts = url.trim().match(regex);
+ if (!parts) return null;
+
+ const mediaType = (parts[1] || 'text/plain;charset=us-ascii').toLowerCase();
+
+ const mediaTypeParts: string[] = mediaType
+ .split(';')
+ .map((x: string) => x.toLowerCase());
+ const contentType = mediaTypeParts[0];
+
+ mediaTypeParts.slice(1).forEach((attribute) => {
+ const p = attribute.split('=');
+ if (p.length >= 2) (parsed as object)[p[0]] = p[1];
+ });
+
+ const base64 = !!parts[parts.length - 2];
+ const data = parts[parts.length - 1] || '';
+ const encoding = base64 ? 'base64' : 'utf8';
+ const buffer = Buffer.from(
+ base64 ? data : decodeURIComponent(data),
+ encoding
+ );
+
+ return { mediaType, contentType, base64, data, encoding, buffer };
+}
+
export default function registerIpcMainMessageListeners(main: Main) {
ipcMain.on(IPC_MESSAGES.OPEN_MENU, (event) => {
if (event.sender === null) {
@@ -49,6 +89,22 @@ export default function registerIpcMainMessageListeners(main: Main) {
shell.openExternal(link).catch((err) => emitMainProcessError(err));
});
+ ipcMain.on(
+ IPC_MESSAGES.OPEN_DATA_URL,
+ (_, { link, filename }: { link: string; filename: string }) => {
+ const data = parseDataURL(link);
+ if (data) {
+ const s = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+ const temp = Array.from(Array(16), () =>
+ s.charAt(Math.floor(Math.random() * s.length))
+ ).join('');
+ const filepath = path.join(app.getPath('temp'), `${temp} ${filename}`);
+ fs.writeFileSync(filepath, data.buffer);
+ void shell.openPath(filepath);
+ }
+ }
+ );
+
ipcMain.on(IPC_MESSAGES.SHOW_ITEM_IN_FOLDER, (_, filePath: string) => {
return shell.showItemInFolder(filePath);
});
diff --git a/src/components/Controls/Attachment.vue b/src/components/Controls/Attachment.vue
index 06d355a2a..58219cecf 100644
--- a/src/components/Controls/Attachment.vue
+++ b/src/components/Controls/Attachment.vue
@@ -33,6 +33,14 @@
/>
+
+
+