diff --git a/zefie_wtvp_minisrv/app.js b/zefie_wtvp_minisrv/app.js index 725c6e69..6f121dc5 100644 --- a/zefie_wtvp_minisrv/app.js +++ b/zefie_wtvp_minisrv/app.js @@ -1095,8 +1095,9 @@ minisrv-no-mail-count: true`; processPath(socket, urlToPath, request_headers, service_name, shared_romcache, pc_services); } else if (shortURL.indexOf('http://') >= 0 || shortURL.indexOf('https://') >= 0 || (use_external_proxy == true && shortURL.indexOf(service_name + "://") >= 0) && !pc_services) { doHTTPProxy(socket, request_headers); - } else if (shortURL.startsWith('ftp://')) { - var wtvftp = new WTVFTP(minisrv_config, sendToClient); + } else if (shortURL.startsWith('ftp://')) { + if (minisrv_config.config.debug_flags.show_headers) console.debug(" * Incoming FTP request on WTVP socket ID", socket.id, await wtvshared.decodePostData(await wtvshared.filterRequestLog(await wtvshared.filterSSID(request_headers)))); + var wtvftp = new WTVFTP(wtvshared, sendToClient); wtvftp.handleFTPRequest(socket, request_headers); } else if (shortURL.indexOf('file://') >= 0) { shortURL = shortURL.replace("file://",'').replace("romcache", "ROMCache"); diff --git a/zefie_wtvp_minisrv/includes/classes/WTVFTP.js b/zefie_wtvp_minisrv/includes/classes/WTVFTP.js index d10f318e..c1d45dac 100644 --- a/zefie_wtvp_minisrv/includes/classes/WTVFTP.js +++ b/zefie_wtvp_minisrv/includes/classes/WTVFTP.js @@ -1,3 +1,5 @@ +const dns = require('dns'); + class WTVFTP { wtvshared = null; wtvmime = null; @@ -14,18 +16,15 @@ class WTVFTP { const WTVMime = require("./WTVMime.js"); this.url = require('url'); this.ftp = require('ftp'); - this.wtvshared = new WTVShared(); - this.wtvmime = new WTVMime(); + this.wtvshared = new WTVShared(minisrv_config); + this.wtvmime = new WTVMime(minisrv_config); } handleFTPRequest(socket, request_headers) { - // Handle the FTP request here - // Assume request_headers.url contains the FTP URL this.request_headers = request_headers; const ftpUrl = request_headers.request_url; const parsed = this.url.parse(ftpUrl); - // Extract user, pass, and host let user = null; let pass = null; let host = parsed.hostname; @@ -36,10 +35,6 @@ class WTVFTP { pass = password || null; } - // Example usage: log the parsed values - - - // You can now use user, pass, and host as needed if (!user && !pass) { user = "anonymous"; pass = "anonymous@eff.org"; @@ -51,73 +46,70 @@ class WTVFTP { let dir = path; let filename = null; - // Determine if path is a file or directory if (path && path !== '/') { const parts = path.split('/'); if (parts[parts.length - 1] && !path.endsWith('/')) { - filename = parts.pop(); - dir = parts.join('/') || '/'; + filename = parts.pop(); + dir = parts.join('/') || '/'; } } ftpClient.on('ready', () => { if (filename) { var totalsize = 0; - // Change to directory and get file - ftpClient.cwd(dir, (err) => { - if (err) { - this.sendToClient(socket, { 'Status': '500 Failed to change directory', 'Content-Type': 'text/plain' }, 'Failed to change directory'); - ftpClient.end(); - return; - } - ftpClient.get(filename, (err, stream) => { - if (err) { - this.sendToClient(socket, { 'Status': '404 File not found', 'Content-Type': 'text/plain' }, 'File not found'); - ftpClient.end(); - return; - } - const chunks = []; - stream.on('data', (chunk) => { - chunks.push(chunk); - totalsize += chunk.length; - if (totalsize > 1024 * 1024 * 4) { - this.sendToClient(socket, { 'Status': '413 The file chosen contains too much information to be used.', 'Content-Type': 'text/plain' }, 'File too large'); + ftpClient.cwd(dir, (err) => { + if (err) { + this.sendToClient(socket, { 'Status': '500 Failed to change directory', 'Content-Type': 'text/plain' }, 'Failed to change directory'); ftpClient.end(); return; } + ftpClient.get(filename, (err, stream) => { + if (err) { + this.sendToClient(socket, { 'Status': '404 File not found', 'Content-Type': 'text/plain' }, 'File not found'); + ftpClient.end(); + return; + } + const chunks = []; + stream.on('data', (chunk) => { + chunks.push(chunk); + totalsize += chunk.length; + if (totalsize > 1024 * 1024 * 4) { + this.sendToClient(socket, { 'Status': '413 The file chosen contains too much information to be used.', 'Content-Type': 'text/plain' }, 'File too large'); + ftpClient.end(); + return; + } + }); + stream.on('end', () => { + const buffer = Buffer.concat(chunks); + const mime = this.wtvmime.detectMimeTypeFromBuffer(buffer); + this.sendToClient( + socket, + { + 'Status': 200, + 'Content-Type': mime || 'application/octet-stream', + 'Content-Disposition': `attachment; filename="${filename}"` + }, + buffer + ); + ftpClient.end(); + }); + stream.on('error', () => { + this.sendToClient(socket, { 'Status': '500 Error reading file', 'Content-Type': 'text/plain' }, 'Error reading file'); + ftpClient.end(); + }); + }); }); - stream.on('end', () => { - const buffer = Buffer.concat(chunks); - const mime = this.wtvmime.detectMimeTypeFromBuffer(buffer); - this.sendToClient( - socket, - { - 'Status': 200, - 'Content-Type': mime || 'application/octet-stream', - 'Content-Disposition': `attachment; filename="${filename}"` - }, - buffer - ); - ftpClient.end(); - }); - stream.on('error', () => { - this.sendToClient(socket, { 'Status': '500 Error reading file', 'Content-Type': 'text/plain' }, 'Error reading file'); - ftpClient.end(); - }); - }); - }); } else { - // List directory - ftpClient.list(dir, (err, list) => { - if (err) { - this.sendToClient(socket, { 'Status': '500 Failed to list directory', 'Content-Type': 'text/plain' }, 'Failed to list directory'); - ftpClient.end(); - return; - } - const html = this.formatDirectoryListing(list); - this.sendToClient(socket, { 'Status': '200 OK', 'Content-Type': 'text/html' }, html); - ftpClient.end(); - }); + ftpClient.list(dir, (err, list) => { + if (err) { + this.sendToClient(socket, { 'Status': '500 Failed to list directory', 'Content-Type': 'text/plain' }, 'Failed to list directory'); + ftpClient.end(); + return; + } + const html = this.formatDirectoryListing(list); + this.sendToClient(socket, { 'Status': '200 OK', 'Content-Type': 'text/html' }, html); + ftpClient.end(); + }); } }); @@ -125,16 +117,23 @@ class WTVFTP { this.sendToClient(socket, { 'Status': '500 FTP connection error', 'Content-Type': 'text/plain' }, 'FTP connection error'); }); - ftpClient.connect({ - host: host, - port: port, - user: user, - password: pass + // FIX: Resolve host to IPv4 address before connecting + dns.lookup(host, { family: 4 }, (err, address) => { + if (err) { + this.sendToClient(socket, { 'Status': '500 DNS resolution error', 'Content-Type': 'text/plain' }, 'DNS resolution error'); + return; + } + + ftpClient.connect({ + host: address, + port: port, + user: user, + password: pass + }); }); } formatDirectoryListing(list) { - // Format the directory listing as needed let html = `