diff options
Diffstat (limited to 'dgbuilder/red/storage')
-rw-r--r-- | dgbuilder/red/storage/index.js | 107 | ||||
-rw-r--r-- | dgbuilder/red/storage/localfilesystem.js | 309 |
2 files changed, 416 insertions, 0 deletions
diff --git a/dgbuilder/red/storage/index.js b/dgbuilder/red/storage/index.js new file mode 100644 index 00000000..ba939627 --- /dev/null +++ b/dgbuilder/red/storage/index.js @@ -0,0 +1,107 @@ +/** + * Copyright 2013, 2014 IBM Corp. + * + * 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 + * + * http://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. + **/ + +var when = require('when'); + +var storageModule; +var settingsAvailable; + +function moduleSelector(aSettings) { + var toReturn; + if (aSettings.storageModule) { + if (typeof aSettings.storageModule === "string") { + // TODO: allow storage modules to be specified by absolute path + toReturn = require("./"+aSettings.storageModule); + } else { + toReturn = aSettings.storageModule; + } + } else { + toReturn = require("./localfilesystem"); + } + return toReturn; +} + +function is_malicious(path) { + return path.indexOf('../') != -1 || path.indexOf('..\\') != -1; +} + +var storageModuleInterface = { + init: function(settings) { + try { + storageModule = moduleSelector(settings); + settingsAvailable = storageModule.hasOwnProperty("getSettings") && storageModule.hasOwnProperty("saveSettings"); + } catch (e) { + return when.reject(e); + } + return storageModule.init(settings); + }, + getFlows: function() { + return storageModule.getFlows(); + }, + saveFlows: function(flows) { + return storageModule.saveFlows(flows); + }, + getCredentials: function() { + return storageModule.getCredentials(); + }, + saveCredentials: function(credentials) { + return storageModule.saveCredentials(credentials); + }, + getSettings: function() { + if (settingsAvailable) { + return storageModule.getSettings(); + } else { + return when.resolve(null); + } + }, + saveSettings: function(settings) { + if (settingsAvailable) { + return storageModule.saveSettings(settings); + } else { + return when.resolve(); + } + }, + /* Library Functions */ + getAllFlows: function() { + return storageModule.getAllFlows(); + }, + getFlow: function(fn) { + if (is_malicious(fn)) { + return when.reject(new Error('forbidden flow name')); + } + return storageModule.getFlow(fn); + }, + saveFlow: function(fn, data) { + if (is_malicious(fn)) { + return when.reject(new Error('forbidden flow name')); + } + return storageModule.saveFlow(fn, data); + }, + getLibraryEntry: function(type, path) { + if (is_malicious(path)) { + return when.reject(new Error('forbidden flow name')); + } + return storageModule.getLibraryEntry(type, path); + }, + saveLibraryEntry: function(type, path, meta, body) { + if (is_malicious(path)) { + return when.reject(new Error('forbidden flow name')); + } + return storageModule.saveLibraryEntry(type, path, meta, body); + } +} + +module.exports = storageModuleInterface; diff --git a/dgbuilder/red/storage/localfilesystem.js b/dgbuilder/red/storage/localfilesystem.js new file mode 100644 index 00000000..48255332 --- /dev/null +++ b/dgbuilder/red/storage/localfilesystem.js @@ -0,0 +1,309 @@ +/** + * Copyright 2013, 2014 IBM Corp. + * + * 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 + * + * http://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. + **/ + +var fs = require('fs'); +var when = require('when'); +var nodeFn = require('when/node/function'); +var keys = require('when/keys'); +var util = require('util'); +var fspath = require("path"); +var mkdirp = require("mkdirp"); + +var promiseDir = nodeFn.lift(mkdirp); + +var settings; +var flowsFile; +var flowsFullPath; +var flowsPrev; +var credentialsFile; +var oldCredentialsFile; +var userDir; +var libDir; +var libFlowsDir; +var globalSettingsFile; + +function listFiles(dir) { + var dirs = {}; + var files = []; + var dirCount = 0; + return nodeFn.call(fs.readdir, dir).then(function (contents) { + contents.sort().forEach(function(fn) { + var stats = fs.lstatSync(dir+"/"+fn); + if (stats.isDirectory()) { + dirCount += 1; + dirs[fn] = listFiles(dir+"/"+fn) + } else { + files.push(fn.split(".")[0]); + } + }) + var result = {}; + if (dirCount > 0) { result.d = keys.all(dirs); } + if (files.length > 0) { result.f = when.resolve(files); } + return keys.all(result); + }) +} + +function getFileMeta(root,path) { + var fn = fspath.join(root,path); + var fd = fs.openSync(fn,"r"); + var size = fs.fstatSync(fd).size; + var meta = {}; + var read = 0; + var length = 10; + var remaining = ""; + var buffer = Buffer(length); + while(read < size) { + read+=fs.readSync(fd,buffer,0,length); + var data = remaining+buffer.toString(); + var parts = data.split("\n"); + remaining = parts.splice(-1); + for (var i=0;i<parts.length;i+=1) { + var match = /^\/\/ (\w+): (.*)/.exec(parts[i]); + if (match) { + meta[match[1]] = match[2]; + } else { + read = size; + break; + } + } + } + fs.closeSync(fd); + return meta; +} + +function getFileBody(root,path) { + var body = ""; + var fn = fspath.join(root,path); + var fd = fs.openSync(fn,"r"); + var size = fs.fstatSync(fd).size; + var scanning = true; + var read = 0; + var length = 50; + var remaining = ""; + var buffer = Buffer(length); + while(read < size) { + var thisRead = fs.readSync(fd,buffer,0,length); + read += thisRead; + if (scanning) { + var data = remaining+buffer.slice(0,thisRead).toString(); + var parts = data.split("\n"); + remaining = parts.splice(-1)[0]; + for (var i=0;i<parts.length;i+=1) { + if (! /^\/\/ \w+: /.test(parts[i])) { + scanning = false; + body += parts[i]+"\n"; + } + } + if (! /^\/\/ \w+: /.test(remaining)) { + scanning = false; + } + if (!scanning) { + body += remaining; + } + } else { + body += buffer.slice(0,thisRead).toString(); + } + } + fs.closeSync(fd); + return body; +} + +var localfilesystem = { + init: function(_settings) { + settings = _settings; + userDir = settings.userDir || process.env.NODE_RED_HOME; + + if (settings.flowFile) { + flowsFile = settings.flowFile; + flowsFullPath = flowsFile; + } else { + flowsFile = 'flows_'+require('os').hostname()+'.json'; + flowsFullPath = fspath.join(userDir,flowsFile); + } + var fsext = fspath.extname(flowsFile); + credentialsFile = fspath.join(userDir,fspath.basename(flowsFile,fsext)+"_cred"+fsext); + oldCredentialsFile = fspath.join(userDir,"credentials.json"); + flowsPrev = fspath.join(userDir,"flows.backup"); + + libDir = fspath.join(userDir,"lib"); + libFlowsDir = fspath.join(libDir,"flows"); + + + globalSettingsFile = fspath.join(userDir,".config.json"); + + return promiseDir(libFlowsDir); + }, + + getFlows: function() { + var defer = when.defer(); + fs.exists(flowsFullPath, function(exists) { + if (exists) { + util.log("[red] Loading flows : "+flowsFile); + defer.resolve(nodeFn.call(fs.readFile,flowsFullPath,'utf8').then(function(data) { + return JSON.parse(data); + })); + } else { + util.log("[red] Flows file not found : "+flowsFile ); + defer.resolve([]); + } + }); + return defer.promise; + }, + + saveFlows: function(flows) { + if (fs.existsSync(flowsFullPath)) { + fs.renameSync(flowsFullPath,flowsPrev); + } + + var flowData; + + if (settings.flowFilePretty) { + flowData = JSON.stringify(flows,null,4); + } else { + flowData = JSON.stringify(flows); + } + console.log("Writing to file:" + flowsFullPath); + return nodeFn.call(fs.writeFile, flowsFullPath, flowData); + }, + + getCredentials: function() { + var defer = when.defer(); + fs.exists(credentialsFile, function(exists) { + if (exists) { + defer.resolve(nodeFn.call(fs.readFile, credentialsFile, 'utf8').then(function(data) { + return JSON.parse(data) + })); + } else { + fs.exists(oldCredentialsFile, function(exists) { + if (exists) { + defer.resolve(nodeFn.call(fs.readFile, oldCredentialsFile, 'utf8').then(function(data) { + return JSON.parse(data) + })); + } else { + defer.resolve({}); + } + }); + } + }); + return defer.promise; + }, + + saveCredentials: function(credentials) { + var credentialData; + if (settings.flowFilePretty) { + credentialData = JSON.stringify(credentials,null,4); + } else { + credentialData = JSON.stringify(credentials); + } + + return nodeFn.call(fs.writeFile, credentialsFile, credentialData) + }, + + getSettings: function() { + if (fs.existsSync(globalSettingsFile)) { + return nodeFn.call(fs.readFile,globalSettingsFile,'utf8').then(function(data) { + if (data) { + try { + return JSON.parse(data); + } catch(err) { + util.log("[red] Corrupted config detected - resetting"); + return {}; + } + } else { + return {}; + } + }); + } + return when.resolve({}); + }, + saveSettings: function(settings) { + return nodeFn.call(fs.writeFile,globalSettingsFile,JSON.stringify(settings,null,1),'utf8'); + }, + + + getAllFlows: function() { + return listFiles(libFlowsDir); + }, + + getFlow: function(fn) { + var defer = when.defer(); + var file = fspath.join(libFlowsDir,fn+".json"); + fs.exists(file, function(exists) { + if (exists) { + defer.resolve(nodeFn.call(fs.readFile,file,'utf8')); + } else { + defer.reject(); + } + }); + return defer.promise; + }, + + saveFlow: function(fn,data) { + var file = fspath.join(libFlowsDir,fn+".json"); + return promiseDir(fspath.dirname(file)).then(function () { + return nodeFn.call(fs.writeFile, file, data); + }); + }, + + getLibraryEntry: function(type,path) { + var root = fspath.join(libDir,type); + var rootPath = fspath.join(libDir,type,path); + return promiseDir(root).then(function () { + return nodeFn.call(fs.lstat, rootPath).then(function(stats) { + if (stats.isFile()) { + return getFileBody(root,path); + } + if (path.substr(-1) == '/') { + path = path.substr(0,path.length-1); + } + return nodeFn.call(fs.readdir, rootPath).then(function(fns) { + var dirs = []; + var files = []; + fns.sort().filter(function(fn) { + var fullPath = fspath.join(path,fn); + var absoluteFullPath = fspath.join(root,fullPath); + if (fn[0] != ".") { + var stats = fs.lstatSync(absoluteFullPath); + if (stats.isDirectory()) { + dirs.push(fn); + } else { + var meta = getFileMeta(root,fullPath); + meta.fn = fn; + files.push(meta); + } + } + }); + return dirs.concat(files); + }); + }); + }); + }, + + saveLibraryEntry: function(type,path,meta,body) { + var fn = fspath.join(libDir, type, path); + var headers = ""; + for (var i in meta) { + if (meta.hasOwnProperty(i)) { + headers += "// "+i+": "+meta[i]+"\n"; + } + } + return promiseDir(fspath.dirname(fn)).then(function () { + nodeFn.call(fs.writeFile, fn, headers+body); + }); + } +}; + +module.exports = localfilesystem; |