// Copyright (c) 2022, 2023, Oracle and/or its affiliates. //----------------------------------------------------------------------------- // // This software is dual-licensed to you under the Universal Permissive License // (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License // 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose // either license. // // If you elect to accept the software under the Apache License, Version 2.0, // the following applies: // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //----------------------------------------------------------------------------- 'use strict'; const {createNVPair, findValue} = require("./nvStrToNvPair.js"); const fs = require('fs'); const process = require('process'); const readline = require('readline'); const errors = require("../../errors.js"); const MAX_IFILE_DEPTH = 4; /** * Returns File path of the tnsnames.ora if it exists. */ function tnsnamesFilePath(configDir) { let filePathVal = null; const tnsAdminVal = process.env.TNS_ADMIN; if (configDir) { filePathVal = configDir + '/tnsnames.ora'; if (fs.existsSync(filePathVal)) { return filePathVal; } else { errors.throwErr(errors.ERR_TNS_NAMES_FILE_MISSING, configDir); } } else { if (!tnsAdminVal) { errors.throwErr(errors.ERR_NO_CONFIG_DIR); } else { filePathVal = tnsAdminVal; filePathVal += '/tnsnames.ora'; if (!fs.existsSync(filePathVal)) { errors.throwErr(errors.ERR_TNS_NAMES_FILE_MISSING, tnsAdminVal); } } return filePathVal; } } class NLParamParser { constructor() { this.waiters = []; this.readInProgress = false; } /** * Reads the given file line by line and stores the * network service names mapped to connect descriptors in the hashtable. * @param {string} file_path * @returns {Promise} */ async initializeNlpa(file_path) { if (this.readInProgress) { await new Promise((resolve) => { this.waiters.push(resolve); }); } if (!this.checkModfTime()) { /* No File has been modified */ return this.ht; } this.ht = new Map(); this.modTime = new Map(); //stores modified time of each IFile this.readInProgress = true; await this.start(file_path, 0); //start with 0 depth (tnsnames.ora) return this.ht; } async start(file_path, depth) { if (depth > MAX_IFILE_DEPTH) return; //ignore after max depth const stat = fs.statSync(file_path); // store file path and its modified time. this.modTime.set(file_path, stat.mtime); // Creating a readable stream from file // readline module reads line by line // but from a readable stream only. const file = readline.createInterface({ input: fs.createReadStream(file_path), output: process.stdout, terminal: false }); let nvElem = ""; for await (let line of file) { if (line.length == 0) { // ignore empty lines continue; } else if (line[0] == '#') { // comment line continue; } else if ((line[0] == ' ') || // continued input on new line (line[0] == '\t') || (line[0] == ')') || (line[0] == '(')) { line = line.replace(/\s+/g, ''); line = this.checkNLPforComments(line); if (line.length == 0) continue; else { nvElem = nvElem + line; } } else { // new NV Element starting here if (nvElem.length == 0) { line = this.checkNLPforComments(line); nvElem = nvElem + line; } else if (nvElem.length != 0) { await this.addNLPListElement(nvElem, depth); // Add Parameter to Hashtable nvElem = ""; // Clear first, before storing current line line = this.checkNLPforComments(line); nvElem = nvElem + line; } } } if (nvElem.length != 0) { // at eof, still one more parameter to read await this.addNLPListElement(nvElem, depth); nvElem = ""; // clear nvElem buffer after added } this.readInProgress = false; let waiter; while ((waiter = this.waiters.pop())) { waiter(); } } /** * Given a string, this method looks if the '#' character is present. * If true, the line is truncated from that point onwards until the end * of the line; else, the original line is returned unchanged. * * @param str The String that is going to be tested for inline comments * @return String The modified String returned */ checkNLPforComments(str) { const str1 = new Array(str.length); for (let i = 0; i < str.length; i++) { const current_char = str[i]; if (current_char == '#') { if (i != 0) { break; // No need to continue. Return the line } else { // Entire line is a comment return ""; } } else str1.push(current_char); } return str1.join(''); } // check if any of the IFiles has been changed checkModfTime() { if (this.modTime) { for (const [key, value] of this.modTime) { if (fs.existsSync(key)) { const stat = fs.statSync(key); if ((stat.mtime - value > 0)) { return true; } } else return true; } } else { return true; } return false; } /** * adds name value pairs from the input buffer into the hash table. * @param {string} ibuf */ async addNLPListElement(ibuf, depth) { const res = ibuf.split(/\r?\n/).filter(element => element); for (let i = 0; i < res.length; i++) { if (res[i].charAt(0) != '(') { res[i] = '(' + res[i] + ')'; } const nvp = createNVPair(res[i]); const name = nvp.name; const uname = name.toUpperCase(); nvp.name = uname; // support for ifile if (uname == 'IFILE') { await this.start(nvp.atom, depth + 1); } else { const unames = uname.split(","); //multiple aliases (alias1, alias2, alias3) for (let i = 0; i < unames.length; i++) { this.ht.set(unames[i], nvp); } } } } toString() { let out = ""; this.ht.forEach((value) => { out = out + value.toString() + "\n"; }); return out; } /** * if key is address/port then it returns the port value from the * address NVPAIR. * @param {string} key * @returns {string} */ findValueOf(key) { const myarr = key.split('/'); return (findValue(this.ht.get(myarr[0].toUpperCase()), myarr)); } } module.exports = { NLParamParser, tnsnamesFilePath };