-
Notifications
You must be signed in to change notification settings - Fork 5
/
moor.js
executable file
Β·157 lines (124 loc) Β· 4.04 KB
/
moor.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#!/usr/bin/env node
'use strict'
const fs = require('fs')
const os = require('os')
const path = require('path')
const exec = require('child_process').exec
const OTP = require('otp')
const mkdirp = require('mkdirp')
const program = require('commander')
const notifier = require('update-notifier')
const pkg = require('./package.json')
const version = pkg.version
// check for updates & notify
notifier({ pkg }).notify()
const homedir = os.homedir()
const oldConfigPath = path.join(homedir, '.moorrc')
const configDir = path.join(homedir, '.moor')
const configPath = path.join(configDir, 'moorrc')
const tunnelblickConfigPath = path.join(homedir, 'Library/Application Support/Tunnelblick/Configurations')
let profiles
// platform check & early exit if not macOS
if (os.platform() !== 'darwin') {
exitWithError('Only OS X/macOS supported')
}
// run migrations
migrate()
// migrate
function migrate() {
// migrate to home dir
if (fs.existsSync(oldConfigPath)) migrateHomeDir()
}
// migrate older ~/.moorc to ~/.moor/moorrc
function migrateHomeDir() {
mkdirp.sync(configDir)
fs.renameSync(oldConfigPath, configPath)
}
// make sure Tunnelblick config path exists
mkdirp(tunnelblickConfigPath)
// read config file
try {
profiles = JSON.parse(fs.readFileSync(configPath))
} catch(e) {
exitWithError(`Error reading config file ${configPath}`)
}
program
.version(version)
.option('-c, --connect [name]', 'connect to VPN, connects to all if name not mentioned')
.option('-d, --disconnect [name]', 'disconnect from VPN, disconnects from all if name not mentioned')
.parse(process.argv)
// show default help if nothing gets passed
if (!process.argv.slice(2).length) {
program.outputHelp();
process.exit(0)
}
if (program.connect && program.disconnect) {
exitWithError('Cannot connect & disconnect at the same time')
}
// VPN profile name to connect
const connectName = program.connect
const connectNameType = typeof connectName
// VPN profile name to disconnect
const disconnectName = program.disconnect
const disconnectNameType = typeof disconnectName
// connection
if (connectNameType === 'boolean') {
connectAll()
} else if (connectNameType === 'string') {
const profile = profiles.find(profile => profile.name === connectName)
if (!profile) return exitWithError(`Failed connecting to ${connectName}`)
connect(profile)
}
// disconnection
if (disconnectNameType === 'boolean') {
disconnectAll()
} else if (disconnectNameType === 'string') {
const profile = profiles.find(profile => profile.name === disconnectName)
if (!profile) return exitWithError(`Failed disconnecting from ${disconnectName}`)
disconnect(profile)
}
function exitWithError(err) {
if (err instanceof Error) {
throw err
} else if (typeof err === 'string') {
console.log(err)
}
process.exit(1)
}
function connect(config) {
writePass(config.name, generateOTP(config.secret))
const connectCommand = `echo 'tell app "Tunnelblick" to connect "${config.name}"' | osascript`
exec(connectCommand, (err, stdout, stderr) => {
if (err) return exitWithError(err)
})
}
function disconnect(config) {
const disconnectCommand = `echo 'tell app "Tunnelblick" to disconnect "${config.name}"' | osascript`
exec(disconnectCommand, (err, stdout, stderr) => {
if (err) return exitWithError(err)
})
}
function connectAll() {
profiles.forEach(connect)
}
function disconnectAll() {
profiles.forEach(disconnect)
}
function generateOTP(secret) {
const otp = OTP({ secret })
return otp.totp()
}
/**
* writePass - update password in Tunnelblick config
*/
function writePass(name, pass) {
const prefixPath = `${name}.tblk/Contents/Resources`
const ovpnPath = path.join(tunnelblickConfigPath, prefixPath, 'config.ovpn')
const authFile = path.join(tunnelblickConfigPath, prefixPath, 'auth.txt')
let ovpnData = fs.readFileSync(ovpnPath, { encoding: 'utf8' })
fs.writeFileSync(authFile, `${name}\n${pass}`)
if (ovpnData.indexOf('auth-user-pass auth.txt') < 0) {
ovpnData = ovpnData.replace('auth-user-pass', 'auth-user-pass auth.txt')
}
fs.writeFileSync(ovpnPath, ovpnData)
}