chupacabra 0.0.4 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/chupacabra +3 -3
- data/lib/chupacabra.rb +27 -5
- data/lib/chupacabra/storage.rb +24 -10
- data/lib/chupacabra/system.rb +40 -83
- data/lib/chupacabra/system/install.rb +62 -0
- data/lib/chupacabra/system/scripts.rb +121 -0
- data/lib/chupacabra/version.rb +1 -1
- data/lib/tasks/compile.rake +5 -0
- metadata +4 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9ec68bad383e9cceb15138495e74860362e0b12d
|
4
|
+
data.tar.gz: f24efc3d43cf7ff35f298592b0afc9a0de163142
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3e46a7c47897dc75e21840bbf7b164b578fb81e0fd847ba8c8ba93d9fd198bb0bf821f26a37c022fdded16b5116a238bb43ff3272e25e709b22f5e98667e567a
|
7
|
+
data.tar.gz: e50b37e32d503cfd6b8ee1ffbe21b3998f9fd372023f0fe186ce0e29ed1352d4d8cd679a2c0c103609548cc675d034318c059e6b02cab1f2807b0430b4b919a1
|
data/bin/chupacabra
CHANGED
@@ -21,8 +21,8 @@ OptionParser.new do |opts|
|
|
21
21
|
end
|
22
22
|
|
23
23
|
opts.on("-i", "--install", "Install Apple service") do |port|
|
24
|
-
Chupacabra::System.install
|
25
|
-
puts "Service installed in #{Chupacabra::System.user_service_path}"
|
24
|
+
Chupacabra::System::Install.install
|
25
|
+
puts "Service installed in #{Chupacabra::System::Install.user_service_path}"
|
26
26
|
puts ""
|
27
27
|
puts "You can create keyboard shortcut with:"
|
28
28
|
puts "System Preferences -> Keyboard -> Keyboard Shortcuts -> Services -> Chupacabra"
|
@@ -36,7 +36,7 @@ OptionParser.new do |opts|
|
|
36
36
|
end
|
37
37
|
|
38
38
|
opts.on("-u", "--uninstall", "Uninstall Apple service") do |port|
|
39
|
-
Chupacabra::System.uninstall
|
39
|
+
Chupacabra::System::Install.uninstall
|
40
40
|
puts "Service removed from #{Chupacabra::System.user_service_path}"
|
41
41
|
exit
|
42
42
|
end
|
data/lib/chupacabra.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
require '
|
2
|
-
require 'chupacabra/crypto'
|
3
|
-
require 'chupacabra/storage'
|
4
|
-
require 'chupacabra/version'
|
1
|
+
require 'fileutils'
|
5
2
|
|
6
3
|
module Chupacabra
|
7
4
|
extend self
|
5
|
+
|
6
|
+
class Error < StandardError; end
|
7
|
+
|
8
8
|
attr_accessor :env
|
9
9
|
attr_accessor :log
|
10
10
|
|
@@ -30,6 +30,20 @@ module Chupacabra
|
|
30
30
|
env == 'test'
|
31
31
|
end
|
32
32
|
|
33
|
+
def root
|
34
|
+
Pathname.new(File.expand_path('../..', __FILE__))
|
35
|
+
end
|
36
|
+
|
37
|
+
def tmp_dir
|
38
|
+
dir = root + 'tmp'
|
39
|
+
dir.mkpath unless dir.exist?
|
40
|
+
dir
|
41
|
+
end
|
42
|
+
|
43
|
+
def clear_tmp
|
44
|
+
FileUtils.rm_rf(tmp_dir)
|
45
|
+
end
|
46
|
+
|
33
47
|
private
|
34
48
|
|
35
49
|
def output(text)
|
@@ -44,4 +58,12 @@ module Chupacabra
|
|
44
58
|
return unless key
|
45
59
|
"web: #{$1}" if key =~ /https?\:\/\/(?:www.)?([^\/\?]+)/
|
46
60
|
end
|
47
|
-
end
|
61
|
+
end
|
62
|
+
|
63
|
+
require 'chupacabra/system'
|
64
|
+
require 'chupacabra/system/scripts'
|
65
|
+
require 'chupacabra/system/install'
|
66
|
+
require 'chupacabra/crypto'
|
67
|
+
require 'chupacabra/storage'
|
68
|
+
require 'chupacabra/version'
|
69
|
+
require 'pathname'
|
data/lib/chupacabra/storage.rb
CHANGED
@@ -1,14 +1,32 @@
|
|
1
1
|
require 'pathname'
|
2
|
+
require 'fileutils'
|
2
3
|
|
3
4
|
module Chupacabra
|
4
5
|
class Storage
|
5
6
|
|
6
|
-
|
7
|
-
|
7
|
+
class Error < Chupacabra::Error; end
|
8
|
+
|
9
|
+
def self.path
|
10
|
+
path = if Chupacabra.test?
|
11
|
+
Chupacabra.root + 'tmp' + '.chupacabra'
|
12
|
+
else
|
13
|
+
Pathname.new(ENV['HOME']) + '.chupacabra'
|
14
|
+
end
|
15
|
+
raise Error, 'Chupacabra path cant be a file' if path.file?
|
16
|
+
path.mkpath unless path.exist?
|
17
|
+
path
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.passwords_path
|
21
|
+
path + 'pw'
|
8
22
|
end
|
9
23
|
|
10
24
|
def self.clear
|
11
|
-
|
25
|
+
FileUtils.rm_rf(path)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.version_path
|
29
|
+
path + 'version'
|
12
30
|
end
|
13
31
|
|
14
32
|
def initialize(password)
|
@@ -30,21 +48,17 @@ module Chupacabra
|
|
30
48
|
|
31
49
|
private
|
32
50
|
|
33
|
-
def self.filename
|
34
|
-
Chupacabra.test? ? '.chupacabra_test' : '.chupacabra'
|
35
|
-
end
|
36
|
-
|
37
51
|
def data
|
38
52
|
@data ||=
|
39
|
-
if File.exists?(self.class.
|
40
|
-
Marshal.load(Crypto.decrypt(File.read(self.class.
|
53
|
+
if File.exists?(self.class.passwords_path)
|
54
|
+
Marshal.load(Crypto.decrypt(File.read(self.class.passwords_path), @password))
|
41
55
|
else
|
42
56
|
{ }
|
43
57
|
end
|
44
58
|
end
|
45
59
|
|
46
60
|
def save
|
47
|
-
File.open(self.class.
|
61
|
+
File.open(self.class.passwords_path, 'w') do |file|
|
48
62
|
file << Crypto.encrypt(Marshal.dump(@data), @password)
|
49
63
|
end
|
50
64
|
end
|
data/lib/chupacabra/system.rb
CHANGED
@@ -7,6 +7,20 @@ module Chupacabra
|
|
7
7
|
module System
|
8
8
|
extend self
|
9
9
|
|
10
|
+
class Error < Chupacabra::Error; end
|
11
|
+
|
12
|
+
BROWSERS = ['Google Chrome', 'Google Chrome Canary', 'Camino', 'Safari', 'Webkit', 'Opera', 'Firefox']
|
13
|
+
|
14
|
+
def execute(command, run_in_test = false)
|
15
|
+
log(command)
|
16
|
+
if Chupacabra.test? && !run_in_test
|
17
|
+
`echo #{command}`
|
18
|
+
else
|
19
|
+
log(command)
|
20
|
+
`#{command}`
|
21
|
+
end.strip
|
22
|
+
end
|
23
|
+
|
10
24
|
def get_password
|
11
25
|
return get_env_password unless get_env_password.empty?
|
12
26
|
password = Digest::SHA1.hexdigest(get_password_from_dialog)
|
@@ -16,16 +30,16 @@ module Chupacabra
|
|
16
30
|
end
|
17
31
|
|
18
32
|
def clear
|
19
|
-
|
33
|
+
System.execute("launchctl unsetenv #{password_variable}")
|
20
34
|
end
|
21
35
|
|
22
36
|
def get_clipboard
|
23
|
-
|
37
|
+
System.execute("pbpaste", true).strip
|
24
38
|
end
|
25
39
|
|
26
40
|
def set_clipboard(text)
|
27
41
|
raise 'Unsupported string' if text =~ /'/
|
28
|
-
|
42
|
+
System.execute("echo '#{text}' | pbcopy", true)
|
29
43
|
end
|
30
44
|
|
31
45
|
def osx?
|
@@ -37,78 +51,24 @@ module Chupacabra
|
|
37
51
|
end
|
38
52
|
|
39
53
|
def front_app
|
40
|
-
run_script(
|
41
|
-
<<-EOS
|
42
|
-
tell application "System Events"
|
43
|
-
return name of first application process whose frontmost is true
|
44
|
-
end tell
|
45
|
-
EOS
|
46
|
-
)
|
54
|
+
run_script(:script => :front_app)
|
47
55
|
end
|
48
56
|
|
49
57
|
def get_browser_url
|
50
58
|
app = front_app
|
51
|
-
|
52
|
-
|
53
|
-
when 'Google Chrome', 'Google Chrome Canary'
|
54
|
-
%Q(tell application "#{app}" to return URL of active tab of front window)
|
55
|
-
when 'Camino'
|
56
|
-
%Q(tell application "#{app}" to return URL of current tab of front browser window)
|
57
|
-
when 'Safari', 'Webkit', 'Opera'
|
58
|
-
%Q(tell application "#{app}" to return URL of front document)
|
59
|
-
when 'firefox'
|
60
|
-
<<-EOS
|
61
|
-
tell application "System Events"
|
62
|
-
keystroke "l" using command down
|
63
|
-
keystroke "c" using command down
|
64
|
-
end tell
|
65
|
-
delay 0.1
|
66
|
-
return the clipboard
|
67
|
-
EOS
|
68
|
-
end
|
69
|
-
)
|
59
|
+
return unless BROWSERS.include?(app)
|
60
|
+
run_script(:script => :get_browser_url, :compile_argument => app)
|
70
61
|
end
|
71
62
|
|
72
63
|
def paste_clipboard
|
73
|
-
run_script(
|
64
|
+
run_script(:script => :paste_clipboard)
|
74
65
|
end
|
75
66
|
|
76
67
|
def alert(message)
|
77
|
-
run_script(
|
78
|
-
<<-EOS
|
79
|
-
tell application "#{front_app}"
|
80
|
-
activate
|
81
|
-
display alert "#{message}" as warning
|
82
|
-
end tell
|
83
|
-
EOS
|
84
|
-
)
|
85
|
-
end
|
86
|
-
|
87
|
-
def install
|
88
|
-
user_service_contents = user_service_path + 'Contents'
|
89
|
-
user_service_contents.mkpath unless user_service_contents.exist?
|
90
|
-
chupacabra_service_contents = Pathname.new(
|
91
|
-
File.expand_path('../../../osx/Chupacabra.workflow/Contents', __FILE__)
|
92
|
-
)
|
93
|
-
|
94
|
-
%w(document.wflow Info.plist).each do |filename|
|
95
|
-
(user_service_contents + filename).open('w') do |file|
|
96
|
-
file << (chupacabra_service_contents + filename).read
|
97
|
-
end
|
98
|
-
end
|
68
|
+
run_script(:script => :alert, :arguments => [front_app, message])
|
99
69
|
end
|
100
70
|
|
101
|
-
def uninstall
|
102
|
-
FileUtils.rm_rf user_service_path
|
103
|
-
end
|
104
71
|
|
105
|
-
def user_service_path
|
106
|
-
if Chupacabra.test?
|
107
|
-
Pathname.new(ENV['HOME']) + 'Library/Services/Chupacabra_test.workflow'
|
108
|
-
else
|
109
|
-
Pathname.new(ENV['HOME']) + 'Library/Services/Chupacabra.workflow'
|
110
|
-
end
|
111
|
-
end
|
112
72
|
|
113
73
|
def log(message)
|
114
74
|
return unless Chupacabra.log
|
@@ -120,15 +80,23 @@ module Chupacabra
|
|
120
80
|
end
|
121
81
|
|
122
82
|
def log_path
|
123
|
-
|
83
|
+
if Chupacabra.test?
|
84
|
+
(Chupacabra.root + 'log' + 'chupacabra_test.log')
|
85
|
+
else
|
86
|
+
(Pathname.new(ENV['HOME']) + 'chupacabra.log')
|
87
|
+
end
|
124
88
|
end
|
125
89
|
|
126
90
|
private
|
127
91
|
|
128
|
-
def run_script(
|
129
|
-
return
|
130
|
-
|
131
|
-
|
92
|
+
def run_script(options ={})
|
93
|
+
return if Chupacabra.test? or !Chupacabra::System.osx?
|
94
|
+
script = options.fetch(:script)
|
95
|
+
compile_argument = options.fetch(:compile_argument, nil)
|
96
|
+
arguments = options.fetch(:arguments) { [] }
|
97
|
+
script_file = Chupacabra::System::Scripts.script_or_compile(script, compile_argument)
|
98
|
+
script = "osascript #{script_file} #{arguments.collect{ |arg| "'" + arg + "'" }.join(' ') }"
|
99
|
+
System.execute(script)
|
132
100
|
end
|
133
101
|
|
134
102
|
def password_variable
|
@@ -140,31 +108,20 @@ module Chupacabra
|
|
140
108
|
end
|
141
109
|
|
142
110
|
def strip_dialog_response(response)
|
143
|
-
|
111
|
+
System.log(response)
|
112
|
+
response.match(/.class ttxt.:(.+), .class bhit.:OK/)[1]
|
144
113
|
end
|
145
114
|
|
146
115
|
def ask_for_password
|
147
|
-
|
148
|
-
'default answer ""' +
|
149
|
-
'with title "Chupacabra"' +
|
150
|
-
'with icon caution with hidden answer'
|
151
|
-
|
152
|
-
script = <<-EOS
|
153
|
-
tell application "#{front_app}"
|
154
|
-
activate
|
155
|
-
#{dialog}
|
156
|
-
end tell
|
157
|
-
EOS
|
158
|
-
|
159
|
-
run_script(script)
|
116
|
+
run_script(:script => :ask_for_password, :arguments => [front_app])
|
160
117
|
end
|
161
118
|
|
162
119
|
def get_env_password
|
163
|
-
|
120
|
+
System.execute("launchctl getenv #{password_variable}", true).strip
|
164
121
|
end
|
165
122
|
|
166
123
|
def set_env_password(password)
|
167
|
-
|
124
|
+
System.execute("launchctl setenv #{password_variable} '#{password}'", true)
|
168
125
|
end
|
169
126
|
end
|
170
127
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Chupacabra
|
2
|
+
module System
|
3
|
+
module Install
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def install
|
7
|
+
handle_legacy_file
|
8
|
+
install_service
|
9
|
+
Chupacabra::System::Scripts.compile_all
|
10
|
+
update_version
|
11
|
+
end
|
12
|
+
|
13
|
+
def uninstall
|
14
|
+
FileUtils.rm_rf user_service_path
|
15
|
+
end
|
16
|
+
|
17
|
+
def user_service_path
|
18
|
+
if Chupacabra.test?
|
19
|
+
Chupacabra.root + 'tmp' + 'Library' + 'Services' + 'Chupacabra.workflow'
|
20
|
+
else
|
21
|
+
Pathname.new(ENV['HOME']) + 'Library/Services/Chupacabra.workflow'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def install_service
|
28
|
+
user_service_contents = user_service_path + 'Contents'
|
29
|
+
user_service_contents.mkpath unless user_service_contents.exist?
|
30
|
+
chupacabra_service_contents = Chupacabra.root + 'osx' + 'Chupacabra.workflow' + 'Contents'
|
31
|
+
%w(document.wflow Info.plist).each do |filename|
|
32
|
+
(user_service_contents + filename).open('w') do |file|
|
33
|
+
file << (chupacabra_service_contents + filename).read
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def handle_legacy_file
|
39
|
+
return unless legacy_passwords_path.file?
|
40
|
+
legacy_passwords_path.rename(legacy_passwords_path_tmp)
|
41
|
+
legacy_passwords_path_tmp.rename(Chupacabra::Storage.passwords_path)
|
42
|
+
end
|
43
|
+
|
44
|
+
def update_version
|
45
|
+
Chupacabra::Storage.version_path.open('w') { |file| file << Chupacabra::VERSION }
|
46
|
+
end
|
47
|
+
|
48
|
+
def legacy_passwords_path
|
49
|
+
unless Chupacabra.test?
|
50
|
+
Pathname.new(ENV['HOME']) + '.chupacabra'
|
51
|
+
else
|
52
|
+
Chupacabra.tmp_dir + '.chupacabra'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def legacy_passwords_path_tmp
|
57
|
+
Pathname.new(legacy_passwords_path.to_s + '_tmp')
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
module Chupacabra
|
2
|
+
module System
|
3
|
+
module Scripts
|
4
|
+
extend self
|
5
|
+
|
6
|
+
class Error < Chupacabra::System::Error; end
|
7
|
+
|
8
|
+
def compile_all
|
9
|
+
scripts_path.mkpath unless scripts_path.exist?
|
10
|
+
clear_scripts
|
11
|
+
[:front_app, :paste_clipboard, :alert, :ask_for_password].each do |script|
|
12
|
+
compile(script)
|
13
|
+
end
|
14
|
+
Chupacabra::System::BROWSERS.each do |browser|
|
15
|
+
compile(:get_browser_url, browser)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def compile(script, argument = nil)
|
20
|
+
script_body = self.send(*[script, argument].compact)
|
21
|
+
raise Error, 'Empty script to compile' if script_body.empty?
|
22
|
+
output = System.execute "osacompile -e '#{script_body}' -o #{script_file(script, argument)}"
|
23
|
+
if $?.success?
|
24
|
+
output
|
25
|
+
else
|
26
|
+
puts "Failed to compile: #{script_file(script, argument)}"
|
27
|
+
puts output
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def script_or_compile(script, argument = nil)
|
33
|
+
file = script_file(script, argument)
|
34
|
+
return file if File.exist?(file)
|
35
|
+
compile(script, argument) or raise "Can't compile #{script}#{' with argument ' + argument if argument}"
|
36
|
+
file
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def front_app
|
42
|
+
<<-SCPT
|
43
|
+
tell application "System Events"
|
44
|
+
return name of first application process whose frontmost is true
|
45
|
+
end tell
|
46
|
+
SCPT
|
47
|
+
end
|
48
|
+
|
49
|
+
def get_browser_url(app)
|
50
|
+
return unless BROWSERS.include?(app)
|
51
|
+
case app
|
52
|
+
when 'Google Chrome', 'Google Chrome Canary'
|
53
|
+
%Q(tell application "#{app}" to return URL of active tab of front window)
|
54
|
+
when 'Camino'
|
55
|
+
%Q(tell application "#{app}" to return URL of current tab of front browser window)
|
56
|
+
when 'Safari', 'Webkit', 'Opera'
|
57
|
+
%Q(tell application "#{app}" to return URL of front document)
|
58
|
+
when 'Firefox'
|
59
|
+
<<-EOS
|
60
|
+
tell application "System Events"
|
61
|
+
keystroke "l" using command down
|
62
|
+
keystroke "c" using command down
|
63
|
+
end tell
|
64
|
+
delay 0.1
|
65
|
+
return the clipboard
|
66
|
+
EOS
|
67
|
+
else
|
68
|
+
raise Error, "Uknown browser: #{app}"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
def paste_clipboard
|
74
|
+
<<-SCPT
|
75
|
+
tell application "System Events" to keystroke "v" using command down
|
76
|
+
SCPT
|
77
|
+
end
|
78
|
+
|
79
|
+
# Script requires two arguments: application name, message
|
80
|
+
def alert
|
81
|
+
<<-SCPT
|
82
|
+
on run argv
|
83
|
+
set _application to item 1 of argv
|
84
|
+
set _message to item 2 of argv
|
85
|
+
tell application _application
|
86
|
+
activate
|
87
|
+
display alert _message as warning
|
88
|
+
end tell
|
89
|
+
end run
|
90
|
+
SCPT
|
91
|
+
end
|
92
|
+
|
93
|
+
# Script requires argument: application name
|
94
|
+
def ask_for_password
|
95
|
+
<<-SCPT
|
96
|
+
on run argv
|
97
|
+
set _application to item 1 of argv
|
98
|
+
tell application _application
|
99
|
+
activate
|
100
|
+
display dialog "Enter Chupacabra password" default answer "" with title "Chupacabra password" with icon caution with hidden answer
|
101
|
+
end tell
|
102
|
+
end run
|
103
|
+
SCPT
|
104
|
+
end
|
105
|
+
|
106
|
+
def script_file(script, argument = nil)
|
107
|
+
argument = '_' + argument.downcase.gsub(/ /, '_') if argument
|
108
|
+
scripts_path + "#{script}#{argument}.scpt"
|
109
|
+
end
|
110
|
+
|
111
|
+
def scripts_path
|
112
|
+
Chupacabra::Storage.path + 'apple_scripts'
|
113
|
+
end
|
114
|
+
|
115
|
+
def clear_scripts
|
116
|
+
Dir.glob(scripts_path + '*').each{ |file| Pathname.new(file).delete }
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
data/lib/chupacabra/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: chupacabra
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dawid Sklodowski
|
@@ -62,9 +62,12 @@ files:
|
|
62
62
|
- bin/chupacabra
|
63
63
|
- lib/chupacabra/crypto.rb
|
64
64
|
- lib/chupacabra/storage.rb
|
65
|
+
- lib/chupacabra/system/install.rb
|
66
|
+
- lib/chupacabra/system/scripts.rb
|
65
67
|
- lib/chupacabra/system.rb
|
66
68
|
- lib/chupacabra/version.rb
|
67
69
|
- lib/chupacabra.rb
|
70
|
+
- lib/tasks/compile.rake
|
68
71
|
- osx/Chupacabra.workflow/Contents/document.wflow
|
69
72
|
- osx/Chupacabra.workflow/Contents/Info.plist
|
70
73
|
homepage: http://github.com/dawid-sklodowski/chupacabra
|