1 | var utils = require('../utils')
|
2 | var isbinaryfile = require('isbinaryfile')
|
3 | var fs = require('fs-extra')
|
4 | var chokidar = require('chokidar')
|
5 |
|
6 | module.exports = {
|
7 | trackDownStreamUpdate: {},
|
8 | websocket: null,
|
9 | alreadyNotified: {},
|
10 |
|
11 | setWebSocket: function (websocket) {
|
12 | this.websocket = websocket
|
13 | },
|
14 |
|
15 | sharedFolder: function (currentSharedFolder, readOnly) {
|
16 | this.currentSharedFolder = currentSharedFolder
|
17 | this.readOnly = readOnly
|
18 | if (this.websocket.connection) this.websocket.send(message('rootFolderChanged', {}))
|
19 | },
|
20 |
|
21 | list: function (args, cb) {
|
22 | try {
|
23 | cb(null, utils.walkSync(this.currentSharedFolder, {}, this.currentSharedFolder))
|
24 | } catch (e) {
|
25 | cb(e.message)
|
26 | }
|
27 | },
|
28 |
|
29 | resolveDirectory: function (args, cb) {
|
30 | try {
|
31 | var path = utils.absolutePath(args.path, this.currentSharedFolder)
|
32 | if (this.websocket && !this.alreadyNotified[path]) {
|
33 | this.alreadyNotified[path] = 1
|
34 | this.setupNotifications(path)
|
35 | }
|
36 | cb(null, utils.resolveDirectory(path, this.currentSharedFolder))
|
37 | } catch (e) {
|
38 | cb(e.message)
|
39 | }
|
40 | },
|
41 |
|
42 | folderIsReadOnly: function (args, cb) {
|
43 | return cb(null, this.readOnly)
|
44 | },
|
45 |
|
46 | get: function (args, cb) {
|
47 | var path = utils.absolutePath(args.path, this.currentSharedFolder)
|
48 | if (!fs.existsSync(path)) {
|
49 | return cb('File not found ' + path)
|
50 | }
|
51 | if (!isRealPath(path, cb)) return
|
52 | isbinaryfile(path, (error, isBinary) => {
|
53 | if (error) console.log(error)
|
54 | if (isBinary) {
|
55 | cb(null, { content: '<binary content not displayed>', readonly: true })
|
56 | } else {
|
57 | fs.readFile(path, 'utf8', (error, data) => {
|
58 | if (error) console.log(error)
|
59 | cb(error, { content: data, readonly: false })
|
60 | })
|
61 | }
|
62 | })
|
63 | },
|
64 |
|
65 | exists: function (args, cb) {
|
66 | const path = utils.absolutePath(args.path, this.currentSharedFolder)
|
67 |
|
68 | cb(null, fs.existsSync(path))
|
69 | },
|
70 |
|
71 | set: function (args, cb) {
|
72 | if (this.readOnly) return cb('Cannot write file: read-only mode selected')
|
73 | const isFolder = args.path.endsWith('/')
|
74 | var path = utils.absolutePath(args.path, this.currentSharedFolder)
|
75 | if (fs.existsSync(path) && !isRealPath(path, cb)) return
|
76 | if (args.content === 'undefined') {
|
77 | console.log('trying to write "undefined" ! stopping.')
|
78 | return
|
79 | }
|
80 | this.trackDownStreamUpdate[path] = path
|
81 | if (isFolder) {
|
82 | fs.mkdirp(path).then(_ => cb()).catch(e => cb(e))
|
83 | } else {
|
84 | fs.ensureFile(path).then(() => {
|
85 | fs.writeFile(path, args.content, 'utf8', (error, data) => {
|
86 | if (error) console.log(error)
|
87 | cb(error, data)
|
88 | })
|
89 | }).catch(e => cb(e))
|
90 | }
|
91 | },
|
92 |
|
93 | rename: function (args, cb) {
|
94 | if (this.readOnly) return cb('Cannot rename file: read-only mode selected')
|
95 | var oldpath = utils.absolutePath(args.oldPath, this.currentSharedFolder)
|
96 | if (!fs.existsSync(oldpath)) {
|
97 | return cb('File not found ' + oldpath)
|
98 | }
|
99 | var newpath = utils.absolutePath(args.newPath, this.currentSharedFolder)
|
100 | if (!isRealPath(oldpath, cb)) return
|
101 | fs.move(oldpath, newpath, (error, data) => {
|
102 | if (error) console.log(error)
|
103 | cb(error, data)
|
104 | })
|
105 | },
|
106 |
|
107 | remove: function (args, cb) {
|
108 | if (this.readOnly) return cb('Cannot remove file: read-only mode selected')
|
109 | var path = utils.absolutePath(args.path, this.currentSharedFolder)
|
110 | if (!fs.existsSync(path)) {
|
111 | return cb('File not found ' + path)
|
112 | }
|
113 | if (!isRealPath(path, cb)) return
|
114 | fs.remove(path, (error) => {
|
115 | if (error) {
|
116 | console.log(error)
|
117 | return cb('Failed to remove file/directory: ' + error)
|
118 | }
|
119 | cb(error, true)
|
120 | })
|
121 | },
|
122 |
|
123 | isDirectory: function (args, cb) {
|
124 | const path = utils.absolutePath(args.path, this.currentSharedFolder)
|
125 |
|
126 | cb(null, fs.statSync(path).isDirectory())
|
127 | },
|
128 |
|
129 | isFile: function (args, cb) {
|
130 | const path = utils.absolutePath(args.path, this.currentSharedFolder)
|
131 |
|
132 | cb(null, fs.statSync(path).isFile())
|
133 | },
|
134 |
|
135 | setupNotifications: function (path) {
|
136 | if (!isRealPath(path)) return
|
137 | var watcher = chokidar.watch(path, { depth: 0, ignorePermissionErrors: true })
|
138 | console.log('setup notifications for ' + path)
|
139 | |
140 |
|
141 |
|
142 |
|
143 |
|
144 |
|
145 |
|
146 |
|
147 |
|
148 |
|
149 |
|
150 |
|
151 | watcher.on('change', (f, curr, prev) => {
|
152 | if (this.trackDownStreamUpdate[f]) {
|
153 | delete this.trackDownStreamUpdate[f]
|
154 | return
|
155 | }
|
156 | if (this.websocket.connection) this.websocket.send(message('changed', utils.relativePath(f, this.currentSharedFolder)))
|
157 | })
|
158 | watcher.on('unlink', (f) => {
|
159 | if (this.websocket.connection) this.websocket.send(message('removed', { path: utils.relativePath(f, this.currentSharedFolder), isFolder: false }))
|
160 | })
|
161 | watcher.on('unlinkDir', (f) => {
|
162 | if (this.websocket.connection) this.websocket.send(message('removed', { path: utils.relativePath(f, this.currentSharedFolder), isFolder: true }))
|
163 | })
|
164 | }
|
165 | }
|
166 |
|
167 | function isRealPath (path, cb) {
|
168 | var realPath = fs.realpathSync(path)
|
169 | var isRealPath = path === realPath
|
170 | var mes = '[WARN] Symbolic link modification not allowed : ' + path + ' | ' + realPath
|
171 | if (!isRealPath) {
|
172 | console.log('\x1b[33m%s\x1b[0m', mes)
|
173 | }
|
174 | if (cb && !isRealPath) cb(mes)
|
175 | return isRealPath
|
176 | }
|
177 |
|
178 | function message (name, value) {
|
179 | return JSON.stringify({ type: 'notification', scope: 'sharedfolder', name: name, value: value })
|
180 | }
|