Initial commit
This commit is contained in:
131
remote/src/services/clipboard/ClipboardService.js
Normal file
131
remote/src/services/clipboard/ClipboardService.js
Normal file
@@ -0,0 +1,131 @@
|
||||
const { spawn } = require('child_process');
|
||||
const logger = require('../../utils/logger');
|
||||
|
||||
const CLIPBOARD_THRESHOLD = 500 * 1024;
|
||||
|
||||
class ClipboardService {
|
||||
constructor() {
|
||||
this.threshold = CLIPBOARD_THRESHOLD;
|
||||
}
|
||||
|
||||
async read() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const psCode = `
|
||||
Add-Type -AssemblyName System.Windows.Forms
|
||||
Add-Type -AssemblyName System.Drawing
|
||||
|
||||
if ([Windows.Forms.Clipboard]::ContainsText()) {
|
||||
$text = [Windows.Forms.Clipboard]::GetText()
|
||||
Write-Output "TYPE:TEXT"
|
||||
Write-Output $text
|
||||
} elseif ([Windows.Forms.Clipboard]::ContainsImage()) {
|
||||
$image = [Windows.Forms.Clipboard]::GetImage()
|
||||
if ($image -ne $null) {
|
||||
$ms = New-Object System.IO.MemoryStream
|
||||
$image.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png)
|
||||
$bytes = $ms.ToArray()
|
||||
$base64 = [Convert]::ToBase64String($bytes)
|
||||
Write-Output "TYPE:IMAGE"
|
||||
Write-Output $base64
|
||||
}
|
||||
} else {
|
||||
Write-Output "TYPE:EMPTY"
|
||||
}
|
||||
`;
|
||||
|
||||
const ps = spawn('powershell', ['-NoProfile', '-Command',
|
||||
'[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; ' + psCode
|
||||
], { encoding: 'utf8' });
|
||||
let output = '';
|
||||
|
||||
ps.stdout.on('data', (data) => {
|
||||
output += data.toString();
|
||||
});
|
||||
|
||||
ps.stderr.on('data', (data) => {
|
||||
logger.error('Clipboard read error', { error: data.toString() });
|
||||
});
|
||||
|
||||
ps.on('close', () => {
|
||||
const lines = output.trim().split('\n');
|
||||
const typeLine = lines[0];
|
||||
|
||||
if (typeLine && typeLine.includes('TYPE:TEXT')) {
|
||||
const text = lines.slice(1).join('\n');
|
||||
resolve({
|
||||
type: 'text',
|
||||
data: text,
|
||||
size: Buffer.byteLength(text, 'utf8')
|
||||
});
|
||||
} else if (typeLine && typeLine.includes('TYPE:IMAGE')) {
|
||||
const base64 = lines.slice(1).join('');
|
||||
const size = Math.ceil(base64.length * 0.75);
|
||||
resolve({
|
||||
type: 'image',
|
||||
data: base64,
|
||||
size: size
|
||||
});
|
||||
} else {
|
||||
resolve({ type: 'empty', data: null, size: 0 });
|
||||
}
|
||||
});
|
||||
|
||||
ps.on('error', (err) => {
|
||||
logger.error('Clipboard read failed', { error: err.message });
|
||||
resolve({ type: 'empty', data: null, size: 0 });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async set(content) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let psCode;
|
||||
|
||||
if (content.type === 'text') {
|
||||
const escapedText = content.data
|
||||
.replace(/'/g, "''")
|
||||
.replace(/\r\n/g, '`r`n')
|
||||
.replace(/\n/g, '`n');
|
||||
psCode = `
|
||||
$OutputEncoding = [Console]::OutputEncoding = [Text.UTF8Encoding]::UTF8
|
||||
Add-Type -AssemblyName System.Windows.Forms
|
||||
[Windows.Forms.Clipboard]::SetText('${escapedText}')
|
||||
Write-Output "SUCCESS"
|
||||
`;
|
||||
} else if (content.type === 'image') {
|
||||
psCode = `
|
||||
$OutputEncoding = [Console]::OutputEncoding = [Text.UTF8Encoding]::UTF8
|
||||
Add-Type -AssemblyName System.Windows.Forms
|
||||
Add-Type -AssemblyName System.Drawing
|
||||
$bytes = [Convert]::FromBase64String('${content.data}')
|
||||
$ms = New-Object System.IO.MemoryStream(,$bytes)
|
||||
$image = [System.Drawing.Image]::FromStream($ms)
|
||||
[Windows.Forms.Clipboard]::SetImage($image)
|
||||
Write-Output "SUCCESS"
|
||||
`;
|
||||
} else {
|
||||
resolve(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const ps = spawn('powershell', ['-NoProfile', '-Command', psCode]);
|
||||
let output = '';
|
||||
|
||||
ps.stdout.on('data', (data) => {
|
||||
output += data.toString();
|
||||
});
|
||||
|
||||
ps.on('close', () => {
|
||||
resolve(output.includes('SUCCESS'));
|
||||
});
|
||||
|
||||
ps.on('error', () => resolve(false));
|
||||
});
|
||||
}
|
||||
|
||||
isSmallContent(size) {
|
||||
return size <= this.threshold;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ClipboardService;
|
||||
Reference in New Issue
Block a user