UNPKG

windows-network-drive

Version:
496 lines (448 loc) 12.8 kB
"use strict"; const util = require('util'); const exec = util.promisify(require('child_process').exec); const path = require('path'); const MAX_BUFFER_SIZE = 2000 * 1024; /** * @param {string} input - Drive path to search for * @returns {void} * @description Assertion if the value is a non empty string */ function assertIfNonEmptyString(input) { /** * Ignore this in code coverage because it should never happen */ /* istanbul ignore if */ if ("string" === typeof input && 0 !== input.length) { throw (new Error(input)); } return; } let windowsNetworkDrive = { /** * @function find * @public * @param {string} drivePath - Drive path to search for * @returns {Promise<{status: boolean, driveLetter: string, path: string, statusMessage: string}[]>} - An array of results * @description Gets the network drive letter for a path * @example * networkDrive.find("\\\\DoesExist\\Path") * // returns * [{status: true, driveLetter: "Z", path: "\\\\DoesExist\\Path", statusMessage: "OK"}] * @example * networkDrive.find("\\DoesNOTExist\Path") * // returns * [] */ find: function find(drivePath) { let findPromise; findPromise = Promise.resolve() .then(function () { if (false === windowsNetworkDrive.isWinOs()) { throw (new Error('windows-network-drive can only run on windows.')); } }) /** * Param check */ .then(function () { if ("string" !== typeof drivePath || 0 === drivePath.trim().length) { throw (new Error('Drive path is not valid. drive path = ' + JSON.stringify(drivePath))); } return; }) .then(function () { return windowsNetworkDrive.pathToWindowsPath(drivePath) .then(function (newPath) { drivePath = newPath; return; }); }) .then(windowsNetworkDrive.list) .then(function (networkDrives) { const filteredDrives = []; /** * Create the list of drives to path */ for (let currentDriveLetter in networkDrives) { /** * There is not an easy way to test this */ /* istanbul ignore if */ if (!networkDrives.hasOwnProperty(currentDriveLetter)) { continue; } const currentDrive = networkDrives[currentDriveLetter]; if (currentDrive.path === drivePath) { filteredDrives.push(currentDrive); } } return filteredDrives; }) return findPromise; }, /** * @function isWinOs * @public * @returns {boolean} - True if is a Windows OS * @description Test the current OS is Windows. This was split into a function for code testing * @example * if (true ===networkDrive.isWinOs()) * { * console.log("This is running on Windows"); * } */ isWinOs: function isWinOs() { return /^win/.test(process.platform); }, /** * @function list * @public * @returns {Promise<{ status: boolean, driveLetter: string, path: string, statusMessage: string }>} - Object keys are drive letters * @description lists all network drives and paths * @example * networkDrive.list() * // returns * { * "F": { "status": true, "driveLetter": "F", "path": "\\\\NETWORKA\\Files", "statusMessage": "OK" }, * "K": { "status": true, "driveLetter": "K", "path": "\\\\NETWORKB\\DRIVE G", "statusMessage": "OK" } * } */ list: function list() { let listPromise; listPromise = Promise.resolve() .then(function () { if (false === windowsNetworkDrive.isWinOs()) { throw (new Error('windows-network-drive can only run on windows.')); } }) .then(function () { return exec("net use", { maxBuffer: MAX_BUFFER_SIZE }); }) .then(function (result) { // "net use" returns: // New connections will be remembered. // // // Status Local Remote Network // // ------------------------------------------------------------------------------- // OK Y: \\NETWORKA\long-long-long-long-long-name // Microsoft Windows Network // OK Z: \\NETWORKA\Files Microsoft Windows Network // The command completed successfully. const lines = `${result.stdout}` .replace(/^(-+)$/gm, '') // remove the "-----------------------------"-line .split('\n') .map((line) => line.replace(/[\r\n]/g, '')) // Trim line endings, but not spaces .filter(Boolean) // Remove empty lines .slice(1) // Remove the first line ("New connections...") .slice(1) // Remove the table header line ("Status Local...") .slice(0, -1); // Remove the last line ("The command completed..."), so that only the table remains // Fix an issue where the table rows are split in multiple lines, because of a long path: for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) { if (lines[lineIndex][0] === ' ') { // The line is a line-break of the previous line. // Merge with previous: const toMergeLine = lines.splice(lineIndex, 1)[0]; lines[lineIndex - 1] += ' ' + toMergeLine.trim(); lineIndex--; } } const drivePaths = {} for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) { const line = lines[lineIndex]; const lineMatch = line.match(/^(.+) +(\w): +([^ ]+) +(.+)$/); if (lineMatch) { /** * Examples of statusMessage: * "OK" | "Disconnected" | "Not Avail" * Note: the statusMessage might contain other status, as it depends on the Windows system language. */ const statusMessage = lineMatch[1].trim(); const driveLetter = lineMatch[2].trim().toUpperCase(); const path = lineMatch[3].trim(); // const network = m[4].trim(); drivePaths[driveLetter] = { status: statusMessage === 'OK', driveLetter: driveLetter, path: path, statusMessage: statusMessage, }; } } return drivePaths }) return listPromise; }, /** * @function mount * @public * @param {string} drivePath - Path to create a network drive to * @param {string} [driveLetter] - Drive letter to use when mounting. If undefined a random drive letter will be used. * @param {string} [username] - Username to use when accessing network drive * @param {string} [password] - Password to use when accessing network drive * @returns {Promise<string>} - Drive letter * @description Creates a network drive * @example * networkDrive.mount("\\NETWORKA\Files") * // returns * "F" */ mount: function mount(drivePath, driveLetter, username, password) { let driveLetters = require("windows-drive-letters"); let mountPromise; let mountCommand = "net use"; mountPromise = Promise.resolve() .then(function () { if (false === windowsNetworkDrive.isWinOs()) { throw (new Error('windows-network-drive can only run on windows.')); } }) /** * Param check */ .then(function () { if ("string" !== typeof drivePath || 0 === drivePath.trim().length) { throw (new Error('Drive path is not valid. drive path = ' + JSON.stringify(drivePath))); } drivePath = drivePath.trim(); if ("string" === typeof driveLetter) { driveLetter = driveLetter.trim(); } if ("string" !== typeof driveLetter && undefined !== driveLetter) { throw (new Error('Drive letter must be a string or undefined')); } else if ("" === driveLetter) { driveLetter = undefined; } if ("string" !== typeof username && undefined !== username) { throw (new Error('Username must be a string or undefined')); } else if ("" === username) { username = undefined; } if ("string" !== typeof password && undefined !== password) { throw (new Error('Password must be a string or undefined')); } else if ("" === password) { password = undefined; } return; }) .then(function () { return windowsNetworkDrive.pathToWindowsPath(drivePath) .then(function (newPath) { drivePath = newPath; return; }); }) /** * Get the next drive letter */ .then(driveLetters.randomFree) .then(function (newDriveLetter) { if (undefined === driveLetter) { /** * Get a drive letter if one was not given */ driveLetter = newDriveLetter; } return; }) /** * Create the command to run */ .then(function () { mountCommand += " " + driveLetter + ": \"" + drivePath + "\" /P:Yes"; /** * There is not an easy way to setup a network drive with a username and password */ /* istanbul ignore next */ if ("string" === typeof username && "string" === typeof password) { mountCommand += " /user:" + username + " " + password; } }) /** * mount the drive */ .then(function () { return exec(mountCommand, { maxBuffer: MAX_BUFFER_SIZE }); }) .then(function (result) { assertIfNonEmptyString(result.stderr); return; }) /** * Return the drive letter for this mount */ .then(function () { return driveLetter.toUpperCase(); }) return mountPromise; }, /** * @function unmount * @public * @param {string} driveLetter - Drive letter to remove * @returns {Promise<void>} * @description Removes a network drive * @example * networkDrive.unmount("F") */ unmount: function unmount(driveLetter) { let driveLetters = require("windows-drive-letters"); let unmountPromise; unmountPromise = Promise.resolve() .then(function () { if (false === windowsNetworkDrive.isWinOs()) { throw (new Error('windows-network-drive can only run on windows.')); } }) /** * Param check */ .then(function () { if ("string" !== typeof driveLetter || 0 === driveLetter.trim().length) { throw (new Error('Drive letter is not valid')); } driveLetter = driveLetter.trim().toUpperCase(); return; }) /** * Get the used drive letter */ .then(driveLetters.used) .then(function (letters) { /** * If this drive is mounted, remove it */ if (-1 !== letters.indexOf(driveLetter)) { let unmountCommand = "net use " + driveLetter + ": /Delete /y"; return exec(unmountCommand, { maxBuffer: MAX_BUFFER_SIZE }) .then(function (result) { assertIfNonEmptyString(result.stderr); return; }); } /** * Ignore false possitive */ /* istanbul ignore next */ return; }) return unmountPromise; }, /** * @function pathToWindowsPath * @public * @param {string} drivePath - Path to be converted * @returns {Promise<string>} - Converted path * @description Converts a path to a windows path * @example * networkDrive.pathToWindowsPath('K:/test') * // returns * 'K:\test' */ pathToWindowsPath: function pathToWindowsPath(drivePath) { let pathPromise; pathPromise = Promise.resolve() .then(function () { if (false === windowsNetworkDrive.isWinOs()) { throw (new Error('windows-network-drive can only run on windows.')); } return; }) /** * Param check */ .then(function () { if ("string" !== typeof drivePath) { throw (new Error('Drive path is not valid. drive path = ' + JSON.stringify(drivePath))); } drivePath = drivePath.trim(); if (0 === drivePath.length) { throw (new Error('Drive path is not valid. drive path is only whitespace. drive path = ' + JSON.stringify(drivePath))); } return; }) .then(function () { drivePath = path.normalize(drivePath); drivePath = drivePath.replace('/', '\\'); /** * Remove the trailing \ because it breaks the net use command */ drivePath = drivePath.replace(/\\+$/, ''); return drivePath; }); return pathPromise; } } exports.list = windowsNetworkDrive.list; exports.pathToWindowsPath = windowsNetworkDrive.pathToWindowsPath; exports.mount = windowsNetworkDrive.mount; exports.find = windowsNetworkDrive.find; exports.unmount = windowsNetworkDrive.unmount; exports.isWinOs = windowsNetworkDrive.isWinOs;