1 | 'use strict'
|
2 |
|
3 | var AdmZip = require('adm-zip')
|
4 | var cp = require('child_process')
|
5 | var fs = require('fs')
|
6 | var helper = require('./lib/iedriver')
|
7 | var http = require('http')
|
8 | var kew = require('kew')
|
9 | var npmconf = require('npmconf')
|
10 | var mkdirp = require('mkdirp')
|
11 | var path = require('path')
|
12 | var rimraf = require('rimraf').sync
|
13 | var url = require('url')
|
14 | var util = require('util')
|
15 | var md5file = require('md5-file')
|
16 |
|
17 | var libPath = path.join(__dirname, 'lib', 'iedriver')
|
18 | var libPath64 = path.join(__dirname, 'lib', 'iedriver64')
|
19 | var downloadUrl = 'http://selenium-release.storage.googleapis.com/%s/IEDriverServer_Win32_%s.zip'
|
20 | var downloadUrl64 = 'http://selenium-release.storage.googleapis.com/%s/IEDriverServer_x64_%s.zip'
|
21 |
|
22 | downloadUrl = util.format(downloadUrl, helper.version, helper.binaryversion);
|
23 | downloadUrl64 = util.format(downloadUrl64, helper.version, helper.binaryversion);
|
24 |
|
25 | var fileName = util.format('IEDriverServer_Win32_%s.zip', helper.binaryversion);
|
26 | var fileName64 = util.format('IEDriverServer_x64_%s.zip', helper.binaryversion);
|
27 |
|
28 | var promise = kew.resolve(true)
|
29 | promise = promise
|
30 | .then(function() {
|
31 | console.log('');
|
32 | console.log('Downloading 64 bit Windows IE driver server');
|
33 | console.log('-----');
|
34 | return downloadDriver(downloadUrl64, fileName64, helper.md564, libPath64, 'iedriver64');
|
35 | })
|
36 | .then(function() {
|
37 | console.log('');
|
38 | console.log('Downloading 32 bit Windows IE driver server');
|
39 | console.log('-----');
|
40 | return downloadDriver(downloadUrl, fileName, helper.md5, libPath, 'iedriver');
|
41 | });
|
42 |
|
43 | function downloadDriver(_downloadUrl, _fileName, _md5, _libPath, _driverTmpDirName) {
|
44 | var deferred = kew.defer();
|
45 |
|
46 | npmconf.load(function(err, conf) {
|
47 | if (err) {
|
48 | console.log('Error loading npm config')
|
49 | console.error(err)
|
50 | process.exit(1)
|
51 | return
|
52 | }
|
53 |
|
54 | var tmpPath = findSuitableTempDirectory(conf, _driverTmpDirName)
|
55 |
|
56 | var downloadedFile = path.join(tmpPath, _fileName)
|
57 | var promise = kew.resolve(true)
|
58 |
|
59 |
|
60 | promise = promise.then(function () {
|
61 | console.log('Downloading', _downloadUrl)
|
62 |
|
63 | return requestBinary(getRequestOptions(conf.get('proxy'), _downloadUrl), downloadedFile)
|
64 | })
|
65 | promise.then(function () {
|
66 | return validateMd5(downloadedFile, _md5)
|
67 | })
|
68 | promise.then(function () {
|
69 | return extractDownload(downloadedFile, tmpPath)
|
70 | })
|
71 | promise.then(function () {
|
72 | return copyIntoPlace(tmpPath, _libPath)
|
73 | })
|
74 | promise.then(function () {
|
75 | console.log('Success! IEDriverServer binary available at', _libPath+"\\IEDriverServer.exe");
|
76 | deferred.resolve(true);
|
77 | })
|
78 | .fail(function (err) {
|
79 | console.error('iedriver installation failed', err, err.stack);
|
80 | process.exit(1);
|
81 | })
|
82 | });
|
83 |
|
84 | return deferred.promise;
|
85 | }
|
86 |
|
87 | function findSuitableTempDirectory(npmConf, driverDir) {
|
88 | var now = Date.now()
|
89 | var candidateTmpDirs = [
|
90 | process.env.TMPDIR || '/tmp',
|
91 | npmConf.get('tmp'),
|
92 | path.join(process.cwd(), 'tmp')
|
93 | ]
|
94 |
|
95 | for (var i = 0; i < candidateTmpDirs.length; i++) {
|
96 | var candidatePath = path.join(candidateTmpDirs[i], driverDir)
|
97 |
|
98 | try {
|
99 | mkdirp.sync(candidatePath, '0777')
|
100 | var testFile = path.join(candidatePath, now + '.tmp')
|
101 | fs.writeFileSync(testFile, 'test')
|
102 | fs.unlinkSync(testFile)
|
103 | rimraf(candidatePath);
|
104 | mkdirp.sync(candidatePath, '0777')
|
105 | return candidatePath
|
106 | } catch (e) {
|
107 | console.log(candidatePath, 'is not writable:', e.message)
|
108 | }
|
109 | }
|
110 |
|
111 | console.error('Can not find a writable tmp directory, please report issue on https://github.com/barretts/iedriver/issues/ with as much information as possible.');
|
112 | process.exit(1);
|
113 | }
|
114 |
|
115 |
|
116 | function getRequestOptions(proxyUrl, _downloadUrl) {
|
117 | if (proxyUrl) {
|
118 | var options = url.parse(proxyUrl)
|
119 | options.path = _downloadUrl
|
120 | options.headers = { Host: url.parse(_downloadUrl).host }
|
121 |
|
122 | if (options.auth) {
|
123 | options.headers['Proxy-Authorization'] = 'Basic ' + new Buffer(options.auth).toString('base64')
|
124 | delete options.auth
|
125 | }
|
126 |
|
127 | return options
|
128 | } else {
|
129 | return url.parse(_downloadUrl)
|
130 | }
|
131 | }
|
132 |
|
133 |
|
134 | function requestBinary(requestOptions, filePath) {
|
135 | var deferred = kew.defer()
|
136 |
|
137 | var count = 0
|
138 | var notifiedCount = 0
|
139 | var outFile = fs.openSync(filePath, 'w')
|
140 |
|
141 | var client = http.get(requestOptions, function (response) {
|
142 | var status = response.statusCode
|
143 | console.log('Receiving...')
|
144 |
|
145 | if (status === 200) {
|
146 | response.addListener('data', function (data) {
|
147 | fs.writeSync(outFile, data, 0, data.length, null)
|
148 | count += data.length
|
149 | if ((count - notifiedCount) > 800000) {
|
150 | console.log('Received ' + Math.floor(count / 1024) + 'K...')
|
151 | notifiedCount = count
|
152 | }
|
153 | })
|
154 |
|
155 | response.addListener('end', function () {
|
156 | console.log('Received ' + Math.floor(count / 1024) + 'K total.')
|
157 | fs.closeSync(outFile)
|
158 | deferred.resolve(true)
|
159 | })
|
160 |
|
161 | } else {
|
162 | client.abort()
|
163 | deferred.reject('Error with http request: ' + util.inspect(response.headers))
|
164 | }
|
165 | })
|
166 |
|
167 | return deferred.promise
|
168 | }
|
169 |
|
170 |
|
171 | function validateMd5(filePath, md5value) {
|
172 | var deferred = kew.defer()
|
173 |
|
174 | console.log('Expecting archive MD5 hash of', md5value);
|
175 | var md5fileValue = md5file(filePath).toLowerCase();
|
176 | console.log(' archive MD5 hash is', md5fileValue);
|
177 |
|
178 |
|
179 | try {
|
180 | if (md5fileValue == md5value.toLowerCase()) {
|
181 | deferred.resolve(true)
|
182 | } else {
|
183 | deferred.reject('Error archive md5 checksum does not match')
|
184 | }
|
185 | } catch (err) {
|
186 | deferred.reject('Error trying to match md5 checksum')
|
187 | }
|
188 |
|
189 | return deferred.promise
|
190 | }
|
191 |
|
192 |
|
193 | function extractDownload(filePath, tmpPath) {
|
194 | var deferred = kew.defer()
|
195 | var options = {cwd: tmpPath}
|
196 |
|
197 |
|
198 | try {
|
199 | var zip = new AdmZip(filePath)
|
200 | zip.extractAllTo(tmpPath, true)
|
201 | deferred.resolve(true)
|
202 | } catch (err) {
|
203 | deferred.reject('Error extracting archive ' + err.stack)
|
204 | }
|
205 | return deferred.promise
|
206 | }
|
207 |
|
208 | function rmDir(dirPath) {
|
209 | try { var files = fs.readdirSync(dirPath); }
|
210 | catch(e) { return; }
|
211 | if (files.length > 0)
|
212 | for (var i = 0; i < files.length; i++) {
|
213 | var filePath = dirPath + '/' + files[i];
|
214 | if (fs.statSync(filePath).isFile())
|
215 | fs.unlinkSync(filePath);
|
216 | else
|
217 | rmDir(filePath);
|
218 | }
|
219 | fs.rmdirSync(dirPath);
|
220 | };
|
221 |
|
222 |
|
223 | function copyIntoPlace(tmpPath, targetPath) {
|
224 | rimraf(targetPath);
|
225 |
|
226 | fs.mkdirSync(targetPath);
|
227 |
|
228 |
|
229 | var files = fs.readdirSync(tmpPath);
|
230 | var promises = files.map(function (name) {
|
231 | var deferred = kew.defer();
|
232 |
|
233 | var file = path.join(tmpPath, name);
|
234 | var reader = fs.createReadStream(file);
|
235 |
|
236 | var targetFile = path.join(targetPath, name);
|
237 | var writer = fs.createWriteStream(targetFile);
|
238 | writer.on("close", function() {
|
239 |
|
240 | deferred.resolve(true);
|
241 | });
|
242 |
|
243 | reader.pipe(writer);
|
244 | return deferred.promise;
|
245 | });
|
246 |
|
247 | return kew.all(promises);
|
248 | }
|