]> git.immae.eu Git - perso/Immae/Projets/Nodejs/Surfer.git/blame - src/files.js
Make listen port and ldap filter more flexible
[perso/Immae/Projets/Nodejs/Surfer.git] / src / files.js
CommitLineData
ca2d3b4d
JZ
1'use strict';
2
13080980
JZ
3var async = require('async'),
4 fs = require('fs'),
ca2d3b4d 5 path = require('path'),
eb83e4da 6 rm = require('del'),
8c3ae071 7 debug = require('debug')('files'),
eaa62184 8 mkdirp = require('mkdirp'),
ca2d3b4d
JZ
9 HttpError = require('connect-lastmile').HttpError,
10 HttpSuccess = require('connect-lastmile').HttpSuccess;
11
8c3ae071
JZ
12var gBasePath;
13
14exports = module.exports = function (basePath) {
15 gBasePath = basePath;
ca2d3b4d 16
8c3ae071
JZ
17 return {
18 get: get,
19 put: put,
e628921a 20 post: post,
8c3ae071
JZ
21 del: del
22 };
23};
ca2d3b4d
JZ
24
25// http://stackoverflow.com/questions/11293857/fastest-way-to-copy-file-in-node-js
26function copyFile(source, target, cb) {
27 var cbCalled = false;
28
eaa62184
JZ
29 // ensure directory
30 mkdirp(path.dirname(target), function (error) {
31 if (error) return cb(error);
ca2d3b4d 32
eaa62184
JZ
33 var rd = fs.createReadStream(source);
34 rd.on("error", function(err) {
35 done(err);
36 });
ca2d3b4d 37
eaa62184
JZ
38 var wr = fs.createWriteStream(target);
39 wr.on("error", function(err) {
40 done(err);
41 });
ca2d3b4d 42
eaa62184
JZ
43 wr.on("close", function(ex) {
44 done();
45 });
46
47 rd.pipe(wr);
ca2d3b4d 48
eaa62184
JZ
49 function done(err) {
50 if (!cbCalled) {
51 cb(err);
52 cbCalled = true;
53 }
ca2d3b4d 54 }
eaa62184 55 });
ca2d3b4d
JZ
56}
57
403359cf
JZ
58function createDirectory(targetPath, callback) {
59 mkdirp(targetPath, function (error) {
60 if (error) return callback(error);
61 callback(null);
62 });
63}
64
aa88a753
JZ
65function isProtected(targetPath) {
66 return targetPath.indexOf(getAbsolutePath('_admin')) === 0;
67}
68
ca2d3b4d 69function getAbsolutePath(filePath) {
7bb99aff 70 var absoluteFilePath = path.resolve(path.join(gBasePath, filePath));
ca2d3b4d 71
8c3ae071 72 if (absoluteFilePath.indexOf(gBasePath) !== 0) return null;
ca2d3b4d
JZ
73 return absoluteFilePath;
74}
75
898fe7c8
JZ
76function removeBasePath(filePath) {
77 return filePath.slice(gBasePath.length);
78}
79
ca2d3b4d 80function get(req, res, next) {
94ff5399 81 var filePath = decodeURIComponent(req.params[0]);
ca2d3b4d
JZ
82 var absoluteFilePath = getAbsolutePath(filePath);
83 if (!absoluteFilePath) return next(new HttpError(403, 'Path not allowed'));
84
85 fs.stat(absoluteFilePath, function (error, result) {
86 if (error) return next(new HttpError(404, error));
87
8c3ae071 88 debug('get', absoluteFilePath);
ca2d3b4d 89
13080980 90 if (!result.isDirectory() && !result.isFile()) return next(new HttpError(500, 'unsupported type'));
f66b47bd 91 if (result.isFile()) return res.download(absoluteFilePath);
ca2d3b4d 92
13080980
JZ
93 async.map(fs.readdirSync(absoluteFilePath), function (filePath, callback) {
94 fs.stat(path.join(absoluteFilePath, filePath), function (error, result) {
95 if (error) return callback(error);
96
97 callback(null, {
98 isDirectory: result.isDirectory(),
99 isFile: result.isFile(),
100 atime: result.atime,
101 mtime: result.mtime,
102 ctime: result.ctime,
103 birthtime: result.birthtime,
104 size: result.size,
105 filePath: filePath
106 });
107 });
108 }, function (error, results) {
109 if (error) return next(new HttpError(500, error));
110 res.status(222).send({ entries: results });
111 });
ca2d3b4d
JZ
112 });
113}
114
e628921a 115function post(req, res, next) {
5d361bf9 116 var filePath = decodeURIComponent(req.params[0]);
ca2d3b4d 117
403359cf
JZ
118 if (!(req.files && req.files.file) && !req.query.directory) return next(new HttpError(400, 'missing file or directory'));
119 if ((req.files && req.files.file) && req.query.directory) return next(new HttpError(400, 'either file or directory'));
ca2d3b4d 120
e628921a
JZ
121 debug('post:', filePath);
122
ca2d3b4d 123 var absoluteFilePath = getAbsolutePath(filePath);
aa88a753 124 if (!absoluteFilePath || isProtected(absoluteFilePath)) return next(new HttpError(403, 'Path not allowed'));
ca2d3b4d
JZ
125
126 fs.stat(absoluteFilePath, function (error, result) {
127 if (error && error.code !== 'ENOENT') return next(new HttpError(500, error));
128
403359cf 129 if (result && req.query.directory) return next(new HttpError(409, 'name already exists'));
e628921a 130 if (result && result.isDirectory()) return next(new HttpError(409, 'cannot post on directories'));
403359cf
JZ
131
132 if (req.query.directory) {
133 return createDirectory(absoluteFilePath, function (error) {
134 if (error) return next(new HttpError(500, error));
135 next(new HttpSuccess(201, {}));
136 });
137 } else if (!result || result.isFile()) {
ca2d3b4d
JZ
138 return copyFile(req.files.file.path, absoluteFilePath, function (error) {
139 if (error) return next(new HttpError(500, error));
140 next(new HttpSuccess(201, {}));
141 });
142 }
143
144 return next(new HttpError(500, 'unsupported type'));
145 });
146}
147
e628921a
JZ
148function put(req, res, next) {
149 var oldFilePath = decodeURIComponent(req.params[0]);
150
151 if (!req.body || !req.body.newFilePath) return next(new HttpError(400, 'missing newFilePath'));
152
153 var newFilePath = decodeURIComponent(req.body.newFilePath);
154
155 debug('put: %s -> %s', oldFilePath, newFilePath);
156
157 var absoluteOldFilePath = getAbsolutePath(oldFilePath);
158 if (!absoluteOldFilePath || isProtected(absoluteOldFilePath)) return next(new HttpError(403, 'Path not allowed'));
159
160 var absoluteNewFilePath = getAbsolutePath(newFilePath);
161 if (!absoluteNewFilePath || isProtected(absoluteNewFilePath)) return next(new HttpError(403, 'Path not allowed'));
162
163 fs.rename(absoluteOldFilePath, absoluteNewFilePath, function (error) {
164 if (error) return next (new HttpError(500, error));
165
166 debug('put: successful');
167
168 return next(new HttpSuccess(200, {}));
169 });
170}
171
ca2d3b4d 172function del(req, res, next) {
94ff5399 173 var filePath = decodeURIComponent(req.params[0]);
898fe7c8
JZ
174 var recursive = !!req.query.recursive;
175 var dryRun = !!req.query.dryRun;
176
ca2d3b4d 177 var absoluteFilePath = getAbsolutePath(filePath);
eaa62184 178 if (!absoluteFilePath) return next(new HttpError(404, 'Not found'));
d755925f 179
aa88a753
JZ
180 if (isProtected(absoluteFilePath)) return next(new HttpError(403, 'Path not allowed'));
181
d755925f 182 // absoltueFilePath has to have the base path prepended
898fe7c8 183 if (absoluteFilePath.length <= gBasePath.length) return next(new HttpError(404, 'Not found'));
ca2d3b4d
JZ
184
185 fs.stat(absoluteFilePath, function (error, result) {
186 if (error) return next(new HttpError(404, error));
187
898fe7c8
JZ
188 if (result.isDirectory() && !recursive) return next(new HttpError(403, 'Is directory'));
189
190 // add globs to get file listing
191 if (result.isDirectory()) absoluteFilePath += '/**';
192
e90afd41 193 rm(absoluteFilePath, { dryRun: dryRun, force: true }).then(function (result) {
898fe7c8 194 result = result.map(removeBasePath);
eb83e4da 195 next(new HttpSuccess(200, { entries: result }));
898fe7c8
JZ
196 }, function (error) {
197 console.error(error);
198 next(new HttpError(500, 'Unable to remove'));
ca2d3b4d
JZ
199 });
200 });
201}