Security update for permissions_by_term
[yaffs-website] / node_modules / extract-zip / index.js
1 var fs = require('fs')
2 var path = require('path')
3 var yauzl = require('yauzl')
4 var mkdirp = require('mkdirp')
5 var concat = require('concat-stream')
6 var debug = require('debug')('extract-zip')
7
8 module.exports = function (zipPath, opts, cb) {
9   debug('creating target directory', opts.dir)
10
11   mkdirp(opts.dir, function (err) {
12     if (err) return cb(err)
13     openZip()
14   })
15
16   function openZip () {
17     debug('opening', zipPath, 'with opts', opts)
18
19     yauzl.open(zipPath, {lazyEntries: true}, function (err, zipfile) {
20       if (err) return cb(err)
21
22       var cancelled = false
23
24       zipfile.readEntry()
25
26       zipfile.on('close', function () {
27         if (!cancelled) {
28           debug('zip extraction complete')
29           cb()
30         }
31       })
32
33       zipfile.on('entry', function (entry) {
34         if (cancelled) {
35           debug('skipping entry', entry.fileName, {cancelled: cancelled})
36           return
37         }
38
39         debug('zipfile entry', entry.fileName)
40
41         if (/^__MACOSX\//.test(entry.fileName)) {
42           // dir name starts with __MACOSX/
43           zipfile.readEntry()
44           return
45         }
46
47         extractEntry(entry, function (err) {
48           // if any extraction fails then abort everything
49           if (err) {
50             cancelled = true
51             zipfile.close()
52             return cb(err)
53           }
54           debug('finished processing', entry.fileName)
55           zipfile.readEntry()
56         })
57       })
58
59       function extractEntry (entry, done) {
60         if (cancelled) {
61           debug('skipping entry extraction', entry.fileName, {cancelled: cancelled})
62           return setImmediate(done)
63         }
64
65         if (opts.onEntry) {
66           opts.onEntry(entry)
67         }
68
69         var dest = path.join(opts.dir, entry.fileName)
70
71         // convert external file attr int into a fs stat mode int
72         var mode = (entry.externalFileAttributes >> 16) & 0xFFFF
73         // check if it's a symlink or dir (using stat mode constants)
74         var IFMT = 61440
75         var IFDIR = 16384
76         var IFLNK = 40960
77         var symlink = (mode & IFMT) === IFLNK
78         var isDir = (mode & IFMT) === IFDIR
79
80         // check for windows weird way of specifying a directory
81         // https://github.com/maxogden/extract-zip/issues/13#issuecomment-154494566
82         var madeBy = entry.versionMadeBy >> 8
83         if (!isDir) isDir = (madeBy === 0 && entry.externalFileAttributes === 16)
84
85         // if no mode then default to default modes
86         if (mode === 0) {
87           if (isDir) {
88             if (opts.defaultDirMode) mode = parseInt(opts.defaultDirMode, 10)
89             if (!mode) mode = 493 // Default to 0755
90           } else {
91             if (opts.defaultFileMode) mode = parseInt(opts.defaultFileMode, 10)
92             if (!mode) mode = 420 // Default to 0644
93           }
94         }
95
96         debug('extracting entry', { filename: entry.fileName, isDir: isDir, isSymlink: symlink })
97
98         // reverse umask first (~)
99         var umask = ~process.umask()
100         // & with processes umask to override invalid perms
101         var procMode = mode & umask
102
103         // always ensure folders are created
104         var destDir = dest
105         if (!isDir) destDir = path.dirname(dest)
106
107         debug('mkdirp', {dir: destDir})
108         mkdirp(destDir, function (err) {
109           if (err) {
110             debug('mkdirp error', destDir, {error: err})
111             cancelled = true
112             return done(err)
113           }
114
115           if (isDir) return done()
116
117           debug('opening read stream', dest)
118           zipfile.openReadStream(entry, function (err, readStream) {
119             if (err) {
120               debug('openReadStream error', err)
121               cancelled = true
122               return done(err)
123             }
124
125             readStream.on('error', function (err) {
126               console.log('read err', err)
127             })
128
129             if (symlink) writeSymlink()
130             else writeStream()
131
132             function writeStream () {
133               var writeStream = fs.createWriteStream(dest, {mode: procMode})
134               readStream.pipe(writeStream)
135
136               writeStream.on('finish', function () {
137                 done()
138               })
139
140               writeStream.on('error', function (err) {
141                 debug('write error', {error: err})
142                 cancelled = true
143                 return done(err)
144               })
145             }
146
147             // AFAICT the content of the symlink file itself is the symlink target filename string
148             function writeSymlink () {
149               readStream.pipe(concat(function (data) {
150                 var link = data.toString()
151                 debug('creating symlink', link, dest)
152                 fs.symlink(link, dest, function (err) {
153                   if (err) cancelled = true
154                   done(err)
155                 })
156               }))
157             }
158           })
159         })
160       }
161     })
162   }
163 }