Bug:Fix file validation issue
[vnfsdk/refrepo.git] / vnfmarket / src / main / webapp / vnfmarket / node_modules / tmp / lib / tmp.js
1 /*!
2  * Tmp
3  *
4  * Copyright (c) 2011-2015 KARASZI Istvan <github@spam.raszi.hu>
5  *
6  * MIT Licensed
7  */
8
9 /**
10  * Module dependencies.
11  */
12 var
13   fs     = require('fs'),
14   path   = require('path'),
15   crypto = require('crypto'),
16   tmpDir = require('os-tmpdir'),
17   _c     = process.binding('constants');
18
19
20 /**
21  * The working inner variables.
22  */
23 var
24   // store the actual TMP directory
25   _TMP = tmpDir(),
26
27   // the random characters to choose from
28   RANDOM_CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
29
30   TEMPLATE_PATTERN = /XXXXXX/,
31
32   DEFAULT_TRIES = 3,
33
34   CREATE_FLAGS = (_c.O_CREAT || _c.fs.O_CREAT) | (_c.O_EXCL || _c.fs.O_EXCL) | (_c.O_RDWR || _c.fs.O_RDWR),
35
36   EBADF = _c.EBADF || _c.os.errno.EBADF,
37   ENOENT = _c.ENOENT || _c.os.errno.ENOENT,
38
39   DIR_MODE = 448 /* 0700 */,
40   FILE_MODE = 384 /* 0600 */,
41
42   // this will hold the objects need to be removed on exit
43   _removeObjects = [],
44
45   _gracefulCleanup = false,
46   _uncaughtException = false;
47
48 /**
49  * Random name generator based on crypto.
50  * Adapted from http://blog.tompawlak.org/how-to-generate-random-values-nodejs-javascript
51  *
52  * @param {Number} howMany
53  * @return {String}
54  * @api private
55  */
56 function _randomChars(howMany) {
57   var
58     value = [],
59     rnd = null;
60
61   // make sure that we do not fail because we ran out of entropy
62   try {
63     rnd = crypto.randomBytes(howMany);
64   } catch (e) {
65     rnd = crypto.pseudoRandomBytes(howMany);
66   }
67
68   for (var i = 0; i < howMany; i++) {
69     value.push(RANDOM_CHARS[rnd[i] % RANDOM_CHARS.length]);
70   }
71
72   return value.join('');
73 }
74
75 /**
76  * Checks whether the `obj` parameter is defined or not.
77  *
78  * @param {Object} obj
79  * @return {Boolean}
80  * @api private
81  */
82 function _isUndefined(obj) {
83   return typeof obj === 'undefined';
84 }
85
86 /**
87  * Parses the function arguments.
88  *
89  * This function helps to have optional arguments.
90  *
91  * @param {Object} options
92  * @param {Function} callback
93  * @api private
94  */
95 function _parseArguments(options, callback) {
96   if (typeof options == 'function') {
97     var
98       tmp = options,
99       options = callback || {},
100       callback = tmp;
101   } else if (typeof options == 'undefined') {
102     options = {};
103   }
104
105   return [options, callback];
106 }
107
108 /**
109  * Generates a new temporary name.
110  *
111  * @param {Object} opts
112  * @returns {String}
113  * @api private
114  */
115 function _generateTmpName(opts) {
116   if (opts.name) {
117     return path.join(opts.dir || _TMP, opts.name);
118   }
119
120   // mkstemps like template
121   if (opts.template) {
122     return opts.template.replace(TEMPLATE_PATTERN, _randomChars(6));
123   }
124
125   // prefix and postfix
126   var name = [
127     opts.prefix || 'tmp-',
128     process.pid,
129     _randomChars(12),
130     opts.postfix || ''
131   ].join('');
132
133   return path.join(opts.dir || _TMP, name);
134 }
135
136 /**
137  * Gets a temporary file name.
138  *
139  * @param {Object} options
140  * @param {Function} callback
141  * @api private
142  */
143 function _getTmpName(options, callback) {
144   var
145     args = _parseArguments(options, callback),
146     opts = args[0],
147     cb = args[1],
148     tries = opts.tries || DEFAULT_TRIES;
149
150   if (isNaN(tries) || tries < 0)
151     return cb(new Error('Invalid tries'));
152
153   if (opts.template && !opts.template.match(TEMPLATE_PATTERN))
154     return cb(new Error('Invalid template provided'));
155
156   (function _getUniqueName() {
157     var name = _generateTmpName(opts);
158
159     // check whether the path exists then retry if needed
160     fs.stat(name, function (err) {
161       if (!err) {
162         if (tries-- > 0) return _getUniqueName();
163
164         return cb(new Error('Could not get a unique tmp filename, max tries reached ' + name));
165       }
166
167       cb(null, name);
168     });
169   }());
170 }
171
172 /**
173  * Synchronous version of _getTmpName.
174  *
175  * @param {Object} options
176  * @returns {String}
177  * @api private
178  */
179 function _getTmpNameSync(options) {
180   var
181     args = _parseArguments(options),
182     opts = args[0],
183     tries = opts.tries || DEFAULT_TRIES;
184
185   if (isNaN(tries) || tries < 0)
186     throw new Error('Invalid tries');
187
188   if (opts.template && !opts.template.match(TEMPLATE_PATTERN))
189     throw new Error('Invalid template provided');
190
191   do {
192     var name = _generateTmpName(opts);
193     try {
194       fs.statSync(name);
195     } catch (e) {
196       return name;
197     }
198   } while (tries-- > 0);
199
200   throw new Error('Could not get a unique tmp filename, max tries reached');
201 }
202
203 /**
204  * Creates and opens a temporary file.
205  *
206  * @param {Object} options
207  * @param {Function} callback
208  * @api public
209  */
210 function _createTmpFile(options, callback) {
211   var
212     args = _parseArguments(options, callback),
213     opts = args[0],
214     cb = args[1];
215
216   opts.postfix = (_isUndefined(opts.postfix)) ? '.tmp' : opts.postfix;
217
218   // gets a temporary filename
219   _getTmpName(opts, function _tmpNameCreated(err, name) {
220     if (err) return cb(err);
221
222     // create and open the file
223     fs.open(name, CREATE_FLAGS, opts.mode || FILE_MODE, function _fileCreated(err, fd) {
224       if (err) return cb(err);
225
226       if (opts.discardDescriptor) {
227         return fs.close(fd, function _discardCallback(err) {
228           if (err) {
229             // Low probability, and the file exists, so this could be
230             // ignored.  If it isn't we certainly need to unlink the
231             // file, and if that fails too its error is more
232             // important.
233             try {
234               fs.unlinkSync(name);
235             } catch (e) {
236               err = e;
237             }
238             return cb(err);
239           }
240           cb(null, name, undefined, _prepareTmpFileRemoveCallback(name, -1, opts));
241         });
242       }
243       if (opts.detachDescriptor) {
244         return cb(null, name, fd, _prepareTmpFileRemoveCallback(name, -1, opts));
245       }
246       cb(null, name, fd, _prepareTmpFileRemoveCallback(name, fd, opts));
247     });
248   });
249 }
250
251 /**
252  * Synchronous version of _createTmpFile.
253  *
254  * @param {Object} options
255  * @returns {Object} object consists of name, fd and removeCallback
256  * @api private
257  */
258 function _createTmpFileSync(options) {
259   var
260     args = _parseArguments(options),
261     opts = args[0];
262
263   opts.postfix = opts.postfix || '.tmp';
264
265   var name = _getTmpNameSync(opts);
266   var fd = fs.openSync(name, CREATE_FLAGS, opts.mode || FILE_MODE);
267
268   return {
269     name : name,
270     fd : fd,
271     removeCallback : _prepareTmpFileRemoveCallback(name, fd, opts)
272   };
273 }
274
275 /**
276  * Removes files and folders in a directory recursively.
277  *
278  * @param {String} root
279  * @api private
280  */
281 function _rmdirRecursiveSync(root) {
282   var dirs = [root];
283
284   do {
285     var
286       dir = dirs.pop(),
287       deferred = false,
288       files = fs.readdirSync(dir);
289
290     for (var i = 0, length = files.length; i < length; i++) {
291       var
292         file = path.join(dir, files[i]),
293         stat = fs.lstatSync(file); // lstat so we don't recurse into symlinked directories
294
295       if (stat.isDirectory()) {
296         if (!deferred) {
297           deferred = true;
298           dirs.push(dir);
299         }
300         dirs.push(file);
301       } else {
302         fs.unlinkSync(file);
303       }
304     }
305
306     if (!deferred) {
307       fs.rmdirSync(dir);
308     }
309   } while (dirs.length !== 0);
310 }
311
312 /**
313  * Creates a temporary directory.
314  *
315  * @param {Object} options
316  * @param {Function} callback
317  * @api public
318  */
319 function _createTmpDir(options, callback) {
320   var
321     args = _parseArguments(options, callback),
322     opts = args[0],
323     cb = args[1];
324
325   // gets a temporary filename
326   _getTmpName(opts, function _tmpNameCreated(err, name) {
327     if (err) return cb(err);
328
329     // create the directory
330     fs.mkdir(name, opts.mode || DIR_MODE, function _dirCreated(err) {
331       if (err) return cb(err);
332
333       cb(null, name, _prepareTmpDirRemoveCallback(name, opts));
334     });
335   });
336 }
337
338 /**
339  * Synchronous version of _createTmpDir.
340  *
341  * @param {Object} options
342  * @returns {Object} object consists of name and removeCallback
343  * @api private
344  */
345 function _createTmpDirSync(options) {
346   var
347     args = _parseArguments(options),
348     opts = args[0];
349
350   var name = _getTmpNameSync(opts);
351   fs.mkdirSync(name, opts.mode || DIR_MODE);
352
353   return {
354     name : name,
355     removeCallback : _prepareTmpDirRemoveCallback(name, opts)
356   };
357 }
358
359 /**
360  * Prepares the callback for removal of the temporary file.
361  *
362  * @param {String} name
363  * @param {int} fd
364  * @param {Object} opts
365  * @api private
366  * @returns {Function} the callback
367  */
368 function _prepareTmpFileRemoveCallback(name, fd, opts) {
369   var removeCallback = _prepareRemoveCallback(function _removeCallback(fdPath) {
370     try {
371       if (0 <= fdPath[0]) {
372         fs.closeSync(fdPath[0]);
373       }
374     }
375     catch (e) {
376       // under some node/windows related circumstances, a temporary file
377       // may have not be created as expected or the file was already closed
378       // by the user, in which case we will simply ignore the error
379       if (e.errno != -EBADF && e.errno != -ENOENT) {
380         // reraise any unanticipated error
381         throw e;
382       }
383     }
384     fs.unlinkSync(fdPath[1]);
385   }, [fd, name]);
386
387   if (!opts.keep) {
388     _removeObjects.unshift(removeCallback);
389   }
390
391   return removeCallback;
392 }
393
394 /**
395  * Prepares the callback for removal of the temporary directory.
396  *
397  * @param {String} name
398  * @param {Object} opts
399  * @returns {Function} the callback
400  * @api private
401  */
402 function _prepareTmpDirRemoveCallback(name, opts) {
403   var removeFunction = opts.unsafeCleanup ? _rmdirRecursiveSync : fs.rmdirSync.bind(fs);
404   var removeCallback = _prepareRemoveCallback(removeFunction, name);
405
406   if (!opts.keep) {
407     _removeObjects.unshift(removeCallback);
408   }
409
410   return removeCallback;
411 }
412
413 /**
414  * Creates a guarded function wrapping the removeFunction call.
415  *
416  * @param {Function} removeFunction
417  * @param {Object} arg
418  * @returns {Function}
419  * @api private
420  */
421 function _prepareRemoveCallback(removeFunction, arg) {
422   var called = false;
423
424   return function _cleanupCallback(next) {
425     if (!called) {
426         var index = _removeObjects.indexOf(_cleanupCallback);
427         if (index >= 0) {
428           _removeObjects.splice(index, 1);
429         }
430
431         called = true;
432         removeFunction(arg);
433     }
434     if (next) next(null);
435   };
436 }
437
438 /**
439  * The garbage collector.
440  *
441  * @api private
442  */
443 function _garbageCollector() {
444   if (_uncaughtException && !_gracefulCleanup) {
445     return;
446   }
447
448   // the function being called removes itself from _removeObjects,
449   // loop until _removeObjects is empty
450   while (_removeObjects.length) {
451     try {
452       _removeObjects[0].call(null);
453     } catch (e) {
454       // already removed?
455     }
456   }
457 }
458
459 function _setGracefulCleanup() {
460   _gracefulCleanup = true;
461 }
462
463 var version = process.versions.node.split('.').map(function (value) {
464   return parseInt(value, 10);
465 });
466
467 if (version[0] === 0 && (version[1] < 9 || version[1] === 9 && version[2] < 5)) {
468   process.addListener('uncaughtException', function _uncaughtExceptionThrown(err) {
469     _uncaughtException = true;
470     _garbageCollector();
471
472     throw err;
473   });
474 }
475
476 process.addListener('exit', function _exit(code) {
477   if (code) _uncaughtException = true;
478   _garbageCollector();
479 });
480
481 // exporting all the needed methods
482 module.exports.tmpdir = _TMP;
483 module.exports.dir = _createTmpDir;
484 module.exports.dirSync = _createTmpDirSync;
485 module.exports.file = _createTmpFile;
486 module.exports.fileSync = _createTmpFileSync;
487 module.exports.tmpName = _getTmpName;
488 module.exports.tmpNameSync = _getTmpNameSync;
489 module.exports.setGracefulCleanup = _setGracefulCleanup;