1 | 'use strict';
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 | var fs = require('fs');
|
16 | var path = require('path');
|
17 | var tar = require('tar-fs');
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 | function mkdirRecursive(directoryPath, callback) {
|
33 | directoryPath = path.resolve(directoryPath);
|
34 |
|
35 |
|
36 | fs.mkdir(directoryPath, function(error) {
|
37 |
|
38 | if (error && error.code === 'EEXIST') {
|
39 |
|
40 |
|
41 |
|
42 | callback();
|
43 |
|
44 | } else if (error && error.code === 'ENOENT') {
|
45 |
|
46 |
|
47 |
|
48 |
|
49 | mkdirRecursive(path.dirname(directoryPath), function(error) {
|
50 | if (!error) {
|
51 |
|
52 |
|
53 | fs.mkdir(directoryPath, function(error) {
|
54 | if (error && error.code === 'EEXIST') {
|
55 |
|
56 |
|
57 |
|
58 | callback();
|
59 |
|
60 | } else
|
61 | callback(error);
|
62 | });
|
63 |
|
64 | } else
|
65 | callback(error);
|
66 | });
|
67 | } else
|
68 | callback(error);
|
69 | });
|
70 | }
|
71 |
|
72 |
|
73 |
|
74 |
|
75 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 |
|
83 |
|
84 |
|
85 | function rmdirRecursive(directoryPath, callback) {
|
86 |
|
87 |
|
88 | fs.readdir(directoryPath, function(error, resources) {
|
89 |
|
90 |
|
91 | if (error)
|
92 | return callback(error);
|
93 |
|
94 | var pendingResourceNumber = resources.length;
|
95 |
|
96 |
|
97 | if (!pendingResourceNumber) {
|
98 |
|
99 |
|
100 | fs.rmdir(directoryPath, callback);
|
101 |
|
102 | }
|
103 |
|
104 |
|
105 | resources.forEach(function(resource) {
|
106 |
|
107 | var resourcePath = path.join(directoryPath, resource);
|
108 |
|
109 |
|
110 | fs.stat(resourcePath, function(error, stats) {
|
111 | if (error)
|
112 | return callback(error);
|
113 |
|
114 |
|
115 | if (stats.isDirectory()) {
|
116 |
|
117 | resources = rmdirRecursive(path.join(directoryPath, resource), function(error) {
|
118 | if (error)
|
119 | return callback(error);
|
120 |
|
121 | pendingResourceNumber--;
|
122 |
|
123 | if (!pendingResourceNumber)
|
124 | fs.rmdir(directoryPath, callback);
|
125 |
|
126 | });
|
127 |
|
128 | } else {
|
129 |
|
130 |
|
131 |
|
132 |
|
133 |
|
134 | fs.unlink(resourcePath, function(error) {
|
135 | if (error)
|
136 | return callback(error);
|
137 | else {
|
138 | pendingResourceNumber--;
|
139 |
|
140 | if (!pendingResourceNumber)
|
141 | fs.rmdir(directoryPath, callback);
|
142 |
|
143 | }
|
144 | });
|
145 |
|
146 | }
|
147 |
|
148 | });
|
149 |
|
150 | });
|
151 |
|
152 | });
|
153 |
|
154 | }
|
155 |
|
156 |
|
157 |
|
158 |
|
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 |
|
166 |
|
167 |
|
168 |
|
169 |
|
170 | function readdirRecursive(directoryPath, callback) {
|
171 | var resources = [];
|
172 |
|
173 |
|
174 | fs.readdir(directoryPath, function(error, resourcesNames) {
|
175 |
|
176 |
|
177 | if (error) return callback(error);
|
178 |
|
179 | var pendingResourceNumber = resourcesNames.length;
|
180 |
|
181 |
|
182 | if (!pendingResourceNumber)
|
183 | callback(null, resources);
|
184 |
|
185 |
|
186 | resourcesNames.forEach(function(resourceName) {
|
187 | var resourcePath = path.join(directoryPath, resourceName);
|
188 |
|
189 |
|
190 | fs.stat(resourcePath, function(error, stats) {
|
191 | if (error)
|
192 | return callback(error);
|
193 |
|
194 | stats.path = resourcePath;
|
195 | resources.push(stats);
|
196 |
|
197 |
|
198 | if (stats.isDirectory()) {
|
199 |
|
200 | readdirRecursive(resourcePath, function(error, paths) {
|
201 | if (error)
|
202 | return callback(error);
|
203 |
|
204 | resources = resources.concat(paths);
|
205 | pendingResourceNumber--;
|
206 |
|
207 | if (!pendingResourceNumber)
|
208 | callback(null, resources);
|
209 |
|
210 | });
|
211 |
|
212 | } else {
|
213 |
|
214 |
|
215 |
|
216 |
|
217 | pendingResourceNumber--;
|
218 |
|
219 | if (!pendingResourceNumber)
|
220 | callback(null, resources);
|
221 | }
|
222 |
|
223 | });
|
224 |
|
225 | });
|
226 |
|
227 | });
|
228 |
|
229 | }
|
230 |
|
231 |
|
232 |
|
233 |
|
234 |
|
235 |
|
236 |
|
237 |
|
238 |
|
239 |
|
240 |
|
241 |
|
242 |
|
243 |
|
244 |
|
245 | function copyFile(sourceFilePath, destinationFilePath, callback) {
|
246 | var onError = function(error) {
|
247 | callback(error);
|
248 | };
|
249 |
|
250 | var safecopy = function(sourceFilePath, destinationFilePath, callback) {
|
251 | if (sourceFilePath && destinationFilePath && callback) {
|
252 | try {
|
253 | var is = fs.createReadStream(sourceFilePath);
|
254 | var os = fs.createWriteStream(destinationFilePath);
|
255 |
|
256 | is.on('error', onError);
|
257 | os.on('error', onError);
|
258 |
|
259 | is.on('end', function() {
|
260 | os.end();
|
261 | });
|
262 |
|
263 | os.on('finish', function() {
|
264 | callback();
|
265 | });
|
266 |
|
267 | is.pipe(os);
|
268 | } catch (e) {
|
269 | callback(new Error(e.message));
|
270 | }
|
271 | } else callback(new Error('File path not defined'));
|
272 | };
|
273 |
|
274 | var pathDir = path.dirname(destinationFilePath);
|
275 |
|
276 | this.mkdir(pathDir,
|
277 | function(error) {
|
278 | if (error) callback(error);
|
279 | else safecopy(sourceFilePath, destinationFilePath, callback);
|
280 | }
|
281 | );
|
282 | }
|
283 |
|
284 |
|
285 |
|
286 |
|
287 |
|
288 |
|
289 |
|
290 |
|
291 | module.exports.FILE_TYPES = {
|
292 | JPG: 'jpg',
|
293 | PNG: 'png',
|
294 | GIF: 'gif',
|
295 | TAR: 'tar',
|
296 | MP4: 'mp4',
|
297 | BMP: 'bmp',
|
298 | UNKNOWN: 'unknown'
|
299 | };
|
300 |
|
301 | Object.freeze(this.FILE_TYPES);
|
302 |
|
303 |
|
304 |
|
305 |
|
306 |
|
307 |
|
308 |
|
309 |
|
310 | module.exports.FILE_SIGNATURES = {
|
311 | [this.FILE_TYPES.JPG]: [
|
312 | {
|
313 | offset: 0,
|
314 | signature: 'ffd8ffdb'
|
315 | },
|
316 | {
|
317 | offset: 0,
|
318 | signature: 'ffd8ffe0'
|
319 | },
|
320 | {
|
321 | offset: 0,
|
322 | signature: 'ffd8ffe1'
|
323 | },
|
324 | {
|
325 | offset: 0,
|
326 | signature: 'ffd8fffe'
|
327 | }
|
328 | ],
|
329 | [this.FILE_TYPES.PNG]: [{
|
330 | offset: 0,
|
331 | signature: '89504e47'
|
332 | }],
|
333 | [this.FILE_TYPES.GIF]: [{
|
334 | offset: 0,
|
335 | signature: '47494638'
|
336 | }],
|
337 | [this.FILE_TYPES.TAR]: [{
|
338 | offset: 257,
|
339 | signature: '7573746172'
|
340 | }],
|
341 | [this.FILE_TYPES.MP4]: [
|
342 | {
|
343 | offset: 4,
|
344 | signature: '6674797069736f6d'
|
345 | },
|
346 | {
|
347 | offset: 4,
|
348 | signature: '6674797033677035'
|
349 | },
|
350 | {
|
351 | offset: 4,
|
352 | signature: '667479706d703431'
|
353 | },
|
354 | {
|
355 | offset: 4,
|
356 | signature: '667479706d703432'
|
357 | },
|
358 | {
|
359 | offset: 4,
|
360 | signature: '667479704d534e56'
|
361 | },
|
362 | {
|
363 | offset: 4,
|
364 | signature: '667479704d345620'
|
365 | }
|
366 | ]
|
367 | };
|
368 |
|
369 | Object.freeze(this.FILE_SIGNATURES);
|
370 |
|
371 |
|
372 |
|
373 |
|
374 |
|
375 |
|
376 |
|
377 |
|
378 |
|
379 |
|
380 |
|
381 |
|
382 |
|
383 | module.exports.extract = function(filePath, destinationPath, callback) {
|
384 | var extractTimeout;
|
385 | var streamError;
|
386 | var extractDone = false;
|
387 |
|
388 | callback = callback || function(error) {
|
389 | if (error)
|
390 | process.logger.error('Extract error', {error: error});
|
391 | else
|
392 | process.logger.silly(filePath + ' extracted into ' + destinationPath);
|
393 | };
|
394 |
|
395 | if (filePath && destinationPath) {
|
396 |
|
397 |
|
398 | var extractor = tar.extract(path.normalize(destinationPath));
|
399 |
|
400 | var onError = function(error) {
|
401 | process.logger.error(error);
|
402 | process.logger.verbose('Extract error', {path: destinationPath});
|
403 | if (extractTimeout)
|
404 | clearTimeout(extractTimeout);
|
405 |
|
406 | streamError = error;
|
407 | extractor.end();
|
408 | };
|
409 |
|
410 |
|
411 | extractor.on('finish', function() {
|
412 | extractDone = true;
|
413 | process.logger.silly('extractor end', {path: destinationPath});
|
414 | if (extractTimeout)
|
415 | clearTimeout(extractTimeout);
|
416 |
|
417 | callback(streamError);
|
418 | });
|
419 |
|
420 | var tarFileReadableStream = fs.createReadStream(path.normalize(filePath));
|
421 |
|
422 |
|
423 | tarFileReadableStream.on('error', onError);
|
424 | extractor.on('error', onError);
|
425 |
|
426 |
|
427 | tarFileReadableStream.on('close', function(chunk) {
|
428 | process.logger.silly('stream closed', {path: destinationPath});
|
429 |
|
430 |
|
431 |
|
432 | if (!extractDone)
|
433 | extractTimeout = setTimeout(onError, 30000, new Error('Unexpected end of archive'));
|
434 |
|
435 | });
|
436 |
|
437 |
|
438 | tarFileReadableStream.pipe(extractor);
|
439 |
|
440 | } else
|
441 | callback(new TypeError('Invalid filePath and / or destinationPath, expected strings'));
|
442 | };
|
443 |
|
444 |
|
445 |
|
446 |
|
447 |
|
448 |
|
449 |
|
450 |
|
451 |
|
452 |
|
453 |
|
454 |
|
455 | module.exports.copy = function(sourcePath, destinationSourcePath, callback) {
|
456 | var self = this;
|
457 |
|
458 |
|
459 | fs.stat(sourcePath, function(error, stats) {
|
460 | if (error)
|
461 | return callback(error);
|
462 |
|
463 | if (stats.isDirectory()) {
|
464 |
|
465 |
|
466 |
|
467 |
|
468 | fs.readdir(sourcePath, function(error, resources) {
|
469 |
|
470 |
|
471 | if (error)
|
472 | return callback(error);
|
473 |
|
474 | var pendingResourceNumber = resources.length;
|
475 |
|
476 |
|
477 | if (!pendingResourceNumber) {
|
478 | self.mkdir(destinationSourcePath, callback);
|
479 | return;
|
480 | }
|
481 |
|
482 |
|
483 | resources.forEach(function(resource) {
|
484 | var resourcePath = path.join(sourcePath, resource);
|
485 | var resourceDestinationPath = path.join(destinationSourcePath, resource);
|
486 |
|
487 |
|
488 | self.copy(resourcePath, resourceDestinationPath, function(error) {
|
489 | if (error)
|
490 | return callback(error);
|
491 |
|
492 | pendingResourceNumber--;
|
493 |
|
494 | if (!pendingResourceNumber)
|
495 | callback();
|
496 | });
|
497 | });
|
498 |
|
499 | });
|
500 |
|
501 | } else {
|
502 |
|
503 |
|
504 | copyFile.call(self, sourcePath, destinationSourcePath, callback);
|
505 |
|
506 | }
|
507 |
|
508 | });
|
509 |
|
510 | };
|
511 |
|
512 |
|
513 |
|
514 |
|
515 |
|
516 |
|
517 |
|
518 |
|
519 |
|
520 |
|
521 |
|
522 |
|
523 |
|
524 |
|
525 |
|
526 | module.exports.getJSONFileContent = function(filePath, callback) {
|
527 | if (!filePath)
|
528 | return callback(new TypeError('Invalid file path, expected a string'));
|
529 |
|
530 |
|
531 | fs.exists(filePath, function(exists) {
|
532 |
|
533 | if (exists) {
|
534 |
|
535 |
|
536 | fs.readFile(filePath, {
|
537 | encoding: 'utf8'
|
538 | },
|
539 | function(error, data) {
|
540 | if (error) {
|
541 | callback(error);
|
542 | } else {
|
543 | var dataAsJson;
|
544 | try {
|
545 |
|
546 |
|
547 | dataAsJson = JSON.parse(data);
|
548 |
|
549 | } catch (e) {
|
550 | callback(new Error(e.message));
|
551 | }
|
552 |
|
553 | callback(null, dataAsJson);
|
554 | }
|
555 | });
|
556 | } else
|
557 | callback(new Error('Missing file ' + filePath));
|
558 |
|
559 | });
|
560 | };
|
561 |
|
562 |
|
563 |
|
564 |
|
565 |
|
566 |
|
567 |
|
568 |
|
569 |
|
570 |
|
571 |
|
572 |
|
573 |
|
574 |
|
575 | module.exports.mkdir = function(directoryPath, callback) {
|
576 | callback = callback || function(error) {
|
577 | if (error)
|
578 | process.logger.error('mkdir error', {error: error});
|
579 | else
|
580 | process.logger.silly(directoryPath + ' directory created');
|
581 | };
|
582 |
|
583 | if (!directoryPath)
|
584 | return callback(new TypeError('Invalid directory path, expected a string'));
|
585 |
|
586 | fs.exists(directoryPath, function(exists) {
|
587 | if (exists)
|
588 | callback();
|
589 | else
|
590 | mkdirRecursive(directoryPath, callback);
|
591 | });
|
592 | };
|
593 |
|
594 |
|
595 |
|
596 |
|
597 |
|
598 |
|
599 |
|
600 |
|
601 |
|
602 |
|
603 |
|
604 |
|
605 | module.exports.rmdir = function(directoryPath, callback) {
|
606 | callback = callback || function(error) {
|
607 | if (error)
|
608 | process.logger.error('rmdir error', {error: error});
|
609 | else
|
610 | process.logger.silly(directoryPath + ' directory removed');
|
611 | };
|
612 |
|
613 | if (!directoryPath)
|
614 | return callback(new TypeError('Invalid directory path, expected a string'));
|
615 |
|
616 | fs.exists(directoryPath, function(exists) {
|
617 | if (!exists)
|
618 | callback();
|
619 | else
|
620 | rmdirRecursive(directoryPath, callback);
|
621 | });
|
622 | };
|
623 |
|
624 |
|
625 |
|
626 |
|
627 |
|
628 |
|
629 |
|
630 |
|
631 |
|
632 |
|
633 | module.exports.getConfDir = function() {
|
634 | var env = process.env;
|
635 | var home = env.HOME;
|
636 |
|
637 | if (process.platform === 'win32')
|
638 | home = env.USERPROFILE || env.HOMEDRIVE + env.HOMEPATH || home || '';
|
639 |
|
640 | return path.join(home, '.openveo');
|
641 | };
|
642 |
|
643 |
|
644 |
|
645 |
|
646 |
|
647 |
|
648 |
|
649 |
|
650 |
|
651 |
|
652 |
|
653 |
|
654 | module.exports.readdir = function(directoryPath, callback) {
|
655 | if (!directoryPath || Object.prototype.toString.call(directoryPath) !== '[object String]')
|
656 | return callback(new TypeError('Invalid directory path, expected a string'));
|
657 |
|
658 | fs.stat(directoryPath, function(error, stat) {
|
659 | if (error) callback(error);
|
660 | else if (!stat.isDirectory())
|
661 | callback(new Error(directoryPath + ' is not a directory'));
|
662 | else
|
663 | readdirRecursive(directoryPath, callback);
|
664 | });
|
665 | };
|
666 |
|
667 |
|
668 |
|
669 |
|
670 |
|
671 |
|
672 |
|
673 |
|
674 |
|
675 |
|
676 |
|
677 |
|
678 |
|
679 |
|
680 | module.exports.readFile = function(filePath, offset, length, callback) {
|
681 | fs.stat(filePath, function(error, stats) {
|
682 | if (error) return callback(error);
|
683 |
|
684 | fs.open(filePath, 'r', function(error, fd) {
|
685 | if (error) return callback(error);
|
686 |
|
687 | length = length || stats.size - offset;
|
688 | length = Math.min(length, stats.size - offset);
|
689 | var buffer = new Buffer(Math.min(stats.size, length));
|
690 |
|
691 | fs.read(fd, buffer, 0, length, offset, function(error, bytesRead, buffer) {
|
692 | fs.close(fd, function() {
|
693 | callback(error, buffer);
|
694 | });
|
695 | });
|
696 | });
|
697 | });
|
698 | };
|
699 |
|
700 |
|
701 |
|
702 |
|
703 |
|
704 |
|
705 |
|
706 |
|
707 |
|
708 | module.exports.getFileTypeFromBuffer = function(file) {
|
709 | for (var type in this.FILE_SIGNATURES) {
|
710 | for (var i = 0; i < this.FILE_SIGNATURES[type].length; i++) {
|
711 | var fileMagicNumbers = file.toString(
|
712 | 'hex',
|
713 | this.FILE_SIGNATURES[type][i].offset,
|
714 | this.FILE_SIGNATURES[type][i].offset + (this.FILE_SIGNATURES[type][i].signature.length / 2)
|
715 | );
|
716 |
|
717 | if (fileMagicNumbers === this.FILE_SIGNATURES[type][i].signature)
|
718 | return type;
|
719 | }
|
720 | }
|
721 |
|
722 | return this.FILE_TYPES.UNKNOWN;
|
723 | };
|
724 |
|
725 |
|
726 |
|
727 |
|
728 |
|
729 |
|
730 |
|
731 |
|
732 |
|
733 |
|
734 |
|
735 |
|
736 |
|
737 | module.exports.rm = function(resourcePath, callback) {
|
738 | callback = callback || function(error) {
|
739 | if (error)
|
740 | process.logger.error('rm error', {error: error});
|
741 | else
|
742 | process.logger.silly(resourcePath + ' resource removed');
|
743 | };
|
744 |
|
745 | if (!resourcePath)
|
746 | return callback(new TypeError('Invalid resource path, expected a string'));
|
747 |
|
748 | fs.stat(resourcePath, function(error, stats) {
|
749 | if (error) return callback(error);
|
750 |
|
751 | if (stats.isDirectory())
|
752 | rmdirRecursive(resourcePath, callback);
|
753 | else
|
754 | fs.unlink(resourcePath, callback);
|
755 | });
|
756 | };
|