| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 |
- 'use strict'
- const URL = require('url').URL
- const IGNORE_KEYS = ['stream']
- const oror = function () {
- for (let i = 0, l = arguments.length; i < l; i++) {
- if (arguments[i] !== null && arguments[i] !== undefined) {
- return arguments[i]
- }
- }
- }
- const parseConnectionURI = function (uri) {
- const parsed = new URL(uri)
- let instance
- let user
- let password
- const path = parsed.pathname.substr(1).split('/')
- if (path.length > 1) {
- instance = path.shift()
- }
- if (parsed.username) {
- const auth = [parsed.username, parsed.password]
- user = decodeURIComponent(auth.shift())
- password = decodeURIComponent(auth.join(':'))
- }
- const port = parsed.port ? `,${parsed.port}` : (instance ? `\\${instance}` : '')
- const object = {
- server: `${parsed.hostname}${port}`,
- uid: user || '',
- pwd: password || '',
- database: path[0]
- }
- if (parsed.searchParams) {
- parsed.searchParams.forEach((value, key) => {
- if (key === 'domain') {
- object.uid = `${value}\\${object.uid}`
- } else {
- object[key] = value
- }
- })
- }
- Object.defineProperty(object, 'toString', {
- value () {
- const out = []
- for (const key in this) {
- if (IGNORE_KEYS.indexOf(key) === -1) {
- out.push(`${key}={${this[key]}}`)
- }
- }
- return out.join(';')
- }
- })
- return object
- }
- const parseConnectionString = function (string) {
- let cursor = 0
- let parsing = 'name'
- let param = null
- let buffer = ''
- let quotes = null
- const parsed = {}
- const original = {}
- Object.defineProperty(parsed, '__original__', { value: original })
- Object.defineProperty(parsed, 'toString', {
- value () {
- const out = []
- for (const key in this) {
- if (IGNORE_KEYS.indexOf(key) === -1) {
- const esc = original[key].escape || ['', '']
- out.push(`${original[key].name}=${esc[0] || ''}${this[key]}${esc[1] || ''}`)
- }
- }
- return out.join(';')
- }
- })
- while (cursor < string.length) {
- const char = string.charAt(cursor)
- switch (char) {
- case '=':
- if (parsing === 'name') {
- buffer = buffer.trim()
- param = buffer.toLowerCase()
- original[param] = { name: buffer }
- parsing = 'value'
- buffer = ''
- } else {
- buffer += char
- }
- break
- case '\'': case '"':
- if (parsing === 'value') {
- if (!buffer.trim().length) {
- // value is wrapped in qotes
- original[param].escape = [char, char]
- quotes = char
- buffer = ''
- } else if (quotes) {
- if (char === quotes) {
- // found same char as used for wrapping quotes
- if (char === string.charAt(cursor + 1)) {
- // escaped quote
- buffer += char
- cursor++
- } else {
- // end of value
- parsed[param] = buffer
- param = null
- parsing = null
- buffer = ''
- quotes = null
- }
- } else {
- buffer += char
- }
- } else {
- buffer += char
- }
- } else {
- throw new Error('Invalid connection string.')
- }
- break
- case '{':
- if (parsing === 'value') {
- if (buffer.trim().length === 0) {
- // value is wrapped in qotes
- original[param].escape = ['{', '}']
- quotes = '{}'
- buffer = ''
- } else {
- buffer += char
- }
- } else {
- throw new Error('Invalid connection string.')
- }
- break
- case '}':
- if (parsing === 'value') {
- if (quotes === '{}') {
- // end of value
- parsed[param] = buffer
- param = null
- parsing = null
- buffer = ''
- quotes = null
- } else {
- buffer += char
- }
- } else {
- throw new Error('Invalid connection string.')
- }
- break
- case ';':
- if (parsing === 'value') {
- if (quotes) {
- buffer += char
- } else {
- // end of value
- parsed[param] = buffer
- param = null
- parsing = 'name'
- buffer = ''
- }
- } else {
- buffer = ''
- parsing = 'name'
- }
- break
- default:
- buffer += char
- }
- cursor++
- }
- if (parsing === 'value') {
- // end of value
- parsed[param] = buffer
- }
- return parsed
- }
- const resolveConnectionString = function (string, driver) {
- const parsed = /^(mssql|tedious|msnodesql|tds):\/\//i.test(string) ? parseConnectionURI(string) : parseConnectionString(string)
- const stream = (parsed.stream || '').toLowerCase()
- const encrypt = (parsed.encrypt || '').toLowerCase()
- if (driver === 'msnodesqlv8') {
- parsed.driver = 'SQL Server Native Client 11.0'
- if (parsed.__original__) {
- parsed.__original__.driver = { name: 'Driver', escape: ['{', '}'] }
- }
- return { connectionString: parsed.toString() }
- }
- let user = parsed.uid || parsed.uid || parsed['user id']
- let server = parsed.server || parsed.address || parsed.addr || parsed['data source'] || parsed['network address']
- const config = {
- password: oror(parsed.pwd, parsed.password),
- database: oror(parsed.database, parsed['initial catalog']),
- connectionTimeout: oror(parsed.connectionTimeout, parsed.timeout, parsed['connect timeout'], parsed['connection timeout']),
- requestTimeout: oror(parsed.requestTimeout, parsed['request timeout']),
- stream: stream === 'true' || stream === 'yes' || stream === '1',
- options: {
- readOnlyIntent: parsed.applicationintent && parsed.applicationintent.toLowerCase() === 'readonly',
- encrypt: encrypt === 'true' || encrypt === 'yes' || encrypt === '1'
- }
- }
- if (parsed.useUTC != null) {
- const utc = parsed.useUTC.toLowerCase()
- config.options.useUTC = utc === 'true' || utc === 'yes' || utc === '1'
- }
- if (config.connectionTimeout != null) {
- config.connectionTimeout = parseInt(config.connectionTimeout, 10) * 1000
- }
- if (config.requestTimeout != null) {
- config.requestTimeout = parseInt(config.requestTimeout, 10)
- }
- if (parsed.multisubnetfailover != null) {
- config.options.multiSubnetFailover = parsed.multisubnetfailover.toLowerCase() === 'true'
- }
- if (/^(.*)\\(.*)$/.exec(user)) {
- config.domain = RegExp.$1
- user = RegExp.$2
- }
- if (server) {
- server = server.trim()
- if (/^np:/i.test(server)) {
- throw new Error('Connection via Named Pipes is not supported.')
- }
- if (/^tcp:/i.test(server)) {
- server = server.substr(4)
- }
- if (/^(.*)\\(.*)$/.exec(server)) {
- server = RegExp.$1
- config.options.instanceName = RegExp.$2
- }
- if (/^(.*),(.*)$/.exec(server)) {
- server = RegExp.$1.trim()
- config.port = parseInt(RegExp.$2.trim(), 10)
- }
- if (server === '.' || server === '(.)' || server.toLowerCase() === '(localdb)' || server.toLowerCase() === '(local)') {
- server = 'localhost'
- }
- }
- config.user = user
- config.server = server
- return config
- }
- module.exports = {
- parse: parseConnectionString,
- resolve: resolveConnectionString
- }
|