summaryrefslogtreecommitdiffstats
path: root/dgbuilder/red/storage
diff options
context:
space:
mode:
Diffstat (limited to 'dgbuilder/red/storage')
-rw-r--r--dgbuilder/red/storage/index.js107
-rw-r--r--dgbuilder/red/storage/localfilesystem.js309
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;