rcs-common 9.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +49 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +1 -0
- data/Rakefile +27 -0
- data/lib/rcs-common.rb +21 -0
- data/lib/rcs-common/binary.rb +64 -0
- data/lib/rcs-common/cgi.rb +7 -0
- data/lib/rcs-common/component.rb +87 -0
- data/lib/rcs-common/crypt.rb +71 -0
- data/lib/rcs-common/deploy.rb +96 -0
- data/lib/rcs-common/diagnosticable.rb +136 -0
- data/lib/rcs-common/evidence.rb +261 -0
- data/lib/rcs-common/evidence/addressbook.rb +173 -0
- data/lib/rcs-common/evidence/application.rb +59 -0
- data/lib/rcs-common/evidence/calendar.rb +62 -0
- data/lib/rcs-common/evidence/call.rb +185 -0
- data/lib/rcs-common/evidence/camera.rb +25 -0
- data/lib/rcs-common/evidence/chat.rb +272 -0
- data/lib/rcs-common/evidence/clibpoard.rb +58 -0
- data/lib/rcs-common/evidence/command.rb +50 -0
- data/lib/rcs-common/evidence/common.rb +78 -0
- data/lib/rcs-common/evidence/content/camera/001.jpg +0 -0
- data/lib/rcs-common/evidence/content/coin/wallet_bit.dat +0 -0
- data/lib/rcs-common/evidence/content/coin/wallet_lite.dat +0 -0
- data/lib/rcs-common/evidence/content/file/Einstein.docx +0 -0
- data/lib/rcs-common/evidence/content/file/arabic.docx +0 -0
- data/lib/rcs-common/evidence/content/mouse/001.jpg +0 -0
- data/lib/rcs-common/evidence/content/mouse/002.jpg +0 -0
- data/lib/rcs-common/evidence/content/mouse/003.jpg +0 -0
- data/lib/rcs-common/evidence/content/mouse/004.jpg +0 -0
- data/lib/rcs-common/evidence/content/print/001.jpg +0 -0
- data/lib/rcs-common/evidence/content/screenshot/001.jpg +0 -0
- data/lib/rcs-common/evidence/content/screenshot/002.jpg +0 -0
- data/lib/rcs-common/evidence/content/screenshot/003.jpg +0 -0
- data/lib/rcs-common/evidence/content/url/001.jpg +0 -0
- data/lib/rcs-common/evidence/content/url/002.jpg +0 -0
- data/lib/rcs-common/evidence/content/url/003.jpg +0 -0
- data/lib/rcs-common/evidence/device.rb +23 -0
- data/lib/rcs-common/evidence/download.rb +54 -0
- data/lib/rcs-common/evidence/exec.rb +0 -0
- data/lib/rcs-common/evidence/file.rb +129 -0
- data/lib/rcs-common/evidence/filesystem.rb +71 -0
- data/lib/rcs-common/evidence/info.rb +24 -0
- data/lib/rcs-common/evidence/keylog.rb +84 -0
- data/lib/rcs-common/evidence/mail.rb +237 -0
- data/lib/rcs-common/evidence/mic.rb +39 -0
- data/lib/rcs-common/evidence/mms.rb +36 -0
- data/lib/rcs-common/evidence/money.rb +676 -0
- data/lib/rcs-common/evidence/mouse.rb +62 -0
- data/lib/rcs-common/evidence/password.rb +60 -0
- data/lib/rcs-common/evidence/photo.rb +80 -0
- data/lib/rcs-common/evidence/position.rb +303 -0
- data/lib/rcs-common/evidence/print.rb +50 -0
- data/lib/rcs-common/evidence/screenshot.rb +53 -0
- data/lib/rcs-common/evidence/sms.rb +91 -0
- data/lib/rcs-common/evidence/url.rb +133 -0
- data/lib/rcs-common/fixnum.rb +48 -0
- data/lib/rcs-common/gridfs.rb +294 -0
- data/lib/rcs-common/heartbeat.rb +96 -0
- data/lib/rcs-common/keywords.rb +50 -0
- data/lib/rcs-common/mime.rb +65 -0
- data/lib/rcs-common/mongoid.rb +19 -0
- data/lib/rcs-common/pascalize.rb +62 -0
- data/lib/rcs-common/path_utils.rb +67 -0
- data/lib/rcs-common/resolver.rb +40 -0
- data/lib/rcs-common/rest.rb +17 -0
- data/lib/rcs-common/sanitize.rb +42 -0
- data/lib/rcs-common/serializer.rb +404 -0
- data/lib/rcs-common/signature.rb +141 -0
- data/lib/rcs-common/stats.rb +94 -0
- data/lib/rcs-common/symbolize.rb +10 -0
- data/lib/rcs-common/systemstatus.rb +136 -0
- data/lib/rcs-common/temporary.rb +13 -0
- data/lib/rcs-common/time.rb +24 -0
- data/lib/rcs-common/trace.rb +138 -0
- data/lib/rcs-common/trace.yaml +42 -0
- data/lib/rcs-common/updater/client.rb +354 -0
- data/lib/rcs-common/updater/dsl.rb +178 -0
- data/lib/rcs-common/updater/payload.rb +79 -0
- data/lib/rcs-common/updater/server.rb +126 -0
- data/lib/rcs-common/updater/shared_key.rb +55 -0
- data/lib/rcs-common/updater/tmp_dir.rb +13 -0
- data/lib/rcs-common/utf16le.rb +83 -0
- data/lib/rcs-common/version.rb +5 -0
- data/lib/rcs-common/winfirewall.rb +235 -0
- data/rcs-common.gemspec +64 -0
- data/spec/gridfs_spec.rb +637 -0
- data/spec/mongoid.yaml +6 -0
- data/spec/signature_spec.rb +105 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/updater_spec.rb +80 -0
- data/tasks/deploy.rake +21 -0
- data/tasks/protect.rake +90 -0
- data/test/helper.rb +17 -0
- data/test/test_binary.rb +107 -0
- data/test/test_cgi.rb +14 -0
- data/test/test_crypt.rb +125 -0
- data/test/test_evidence.rb +52 -0
- data/test/test_evidence_manager.rb +119 -0
- data/test/test_fixnum.rb +35 -0
- data/test/test_keywords.rb +137 -0
- data/test/test_mime.rb +49 -0
- data/test/test_pascalize.rb +100 -0
- data/test/test_path_utils.rb +24 -0
- data/test/test_rcs-common.rb +7 -0
- data/test/test_sanitize.rb +40 -0
- data/test/test_serialization.rb +20 -0
- data/test/test_stats.rb +90 -0
- data/test/test_symbolize.rb +20 -0
- data/test/test_systemstatus.rb +35 -0
- data/test/test_time.rb +56 -0
- data/test/test_trace.rb +25 -0
- data/test/test_utf16le.rb +71 -0
- data/test/test_winfirewall.rb +68 -0
- metadata +423 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: f2b1ac857fbf21f06eb94d5a5e76cd178184b8ef
|
|
4
|
+
data.tar.gz: f6dc9cd9218eb48ac4a3a811edc3e7036266c0cb
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: acbf20309baa1c68b561a28063863183a983fea9944775ded0881f88a38ce3e04c3b92cb42b906abd4abd36d2b2ab2a0de282e65853172debc04341b972f30c1
|
|
7
|
+
data.tar.gz: 300985b94d6b9144ba3993032f29e6d008cbb1e2e00164b54157964b1dd6daf1a75282c6806991f3bcaa3f6456f6c29c814f76896fcf36f255ffe9880dbdb884
|
data/.gitignore
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# rcov generated
|
|
2
|
+
coverage
|
|
3
|
+
|
|
4
|
+
# rdoc generated
|
|
5
|
+
rdoc
|
|
6
|
+
|
|
7
|
+
# yard generated
|
|
8
|
+
doc
|
|
9
|
+
.yardoc
|
|
10
|
+
|
|
11
|
+
# bundler
|
|
12
|
+
.bundle
|
|
13
|
+
Gemfile.lock
|
|
14
|
+
|
|
15
|
+
# jeweler generated
|
|
16
|
+
pkg
|
|
17
|
+
|
|
18
|
+
# Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
|
|
19
|
+
#
|
|
20
|
+
# * Create a file at ~/.gitignore
|
|
21
|
+
# * Include files you want ignored
|
|
22
|
+
# * Run: git config --global core.excludesfile ~/.gitignore
|
|
23
|
+
#
|
|
24
|
+
# After doing this, these files will be ignored in all your git projects,
|
|
25
|
+
# saving you from having to 'pollute' every project you touch with them
|
|
26
|
+
#
|
|
27
|
+
# Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
|
|
28
|
+
#
|
|
29
|
+
# For MacOS:
|
|
30
|
+
#
|
|
31
|
+
.DS_Store
|
|
32
|
+
#
|
|
33
|
+
# For TextMate
|
|
34
|
+
#*.tmproj
|
|
35
|
+
#tmtags
|
|
36
|
+
#
|
|
37
|
+
# For emacs:
|
|
38
|
+
#*~
|
|
39
|
+
#\#*
|
|
40
|
+
#.\#*
|
|
41
|
+
#
|
|
42
|
+
# For vim:
|
|
43
|
+
#*.swp
|
|
44
|
+
|
|
45
|
+
# For RubyMine
|
|
46
|
+
.idea
|
|
47
|
+
|
|
48
|
+
# RVM gemset
|
|
49
|
+
.ruby-gemset
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Copyright (c) 2014 HT srl
|
data/Rakefile
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require "bundler/gem_tasks"
|
|
2
|
+
require 'rake'
|
|
3
|
+
require 'rake/testtask'
|
|
4
|
+
require 'rspec/core/rake_task'
|
|
5
|
+
|
|
6
|
+
Dir["./tasks/*.rake"].each do |path|
|
|
7
|
+
load(path)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
desc "Run minitest"
|
|
11
|
+
Rake::TestTask.new(:test) do |test|
|
|
12
|
+
test.libs << 'lib' << 'test'
|
|
13
|
+
test.pattern = 'test/**/test_*.rb'
|
|
14
|
+
test.verbose = true
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
desc "Run minitest + rspec"
|
|
18
|
+
task :default do
|
|
19
|
+
Rake::Task["test"].invoke
|
|
20
|
+
Rake::Task["spec"].invoke
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
desc "Run rspec"
|
|
24
|
+
RSpec::Core::RakeTask.new(:spec)
|
|
25
|
+
|
|
26
|
+
# Disable the release task (release the gem to rubygems)
|
|
27
|
+
Rake::Task["release"].clear
|
data/lib/rcs-common.rb
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# helper modules
|
|
2
|
+
require 'rcs-common/time'
|
|
3
|
+
require 'rcs-common/utf16le'
|
|
4
|
+
|
|
5
|
+
require "rcs-common/version"
|
|
6
|
+
|
|
7
|
+
# gem modules
|
|
8
|
+
require 'rcs-common/crypt'
|
|
9
|
+
require 'rcs-common/pascalize'
|
|
10
|
+
require 'rcs-common/keywords'
|
|
11
|
+
require 'rcs-common/sanitize'
|
|
12
|
+
require 'rcs-common/trace'
|
|
13
|
+
require 'rcs-common/mime'
|
|
14
|
+
require 'rcs-common/systemstatus'
|
|
15
|
+
require 'rcs-common/fixnum'
|
|
16
|
+
require 'rcs-common/symbolize'
|
|
17
|
+
require 'rcs-common/utf16le'
|
|
18
|
+
require 'rcs-common/cgi'
|
|
19
|
+
require 'rcs-common/binary'
|
|
20
|
+
require 'rcs-common/stats'
|
|
21
|
+
require 'rcs-common/gridfs'
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
|
|
2
|
+
# add a method to String to perform binary substitution
|
|
3
|
+
# without the hassles of regexp
|
|
4
|
+
|
|
5
|
+
class MatchNotFound < StandardError
|
|
6
|
+
def initialize
|
|
7
|
+
super "matching string not found"
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class OutOfBounds < StandardError
|
|
12
|
+
def initialize
|
|
13
|
+
super "offset is out of bound"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
class OutOfBoundsString < StandardError
|
|
18
|
+
def initialize
|
|
19
|
+
super "string too long, out of bound"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class String
|
|
24
|
+
def binary_patch(match, replace)
|
|
25
|
+
raise MatchNotFound unless self[match]
|
|
26
|
+
# use the block form to avoid the regexp in the replace string
|
|
27
|
+
self.gsub!(match.force_encoding('ASCII-8BIT')) do |param|
|
|
28
|
+
replace.force_encoding('ASCII-8BIT')
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def binary_patch_at_offset(offset, replace)
|
|
33
|
+
io = StringIO.new(self)
|
|
34
|
+
|
|
35
|
+
# check for boundaries
|
|
36
|
+
raise OutOfBounds if offset < 0
|
|
37
|
+
raise OutOfBounds if offset > io.size
|
|
38
|
+
raise OutOfBoundsString if offset + replace.bytesize > io.size
|
|
39
|
+
|
|
40
|
+
io.pos = offset
|
|
41
|
+
io.write replace
|
|
42
|
+
io.close
|
|
43
|
+
self
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def binary_add_at_offset(offset, value)
|
|
47
|
+
io = StringIO.new(self)
|
|
48
|
+
|
|
49
|
+
# check for boundaries
|
|
50
|
+
raise OutOfBounds if offset < 0
|
|
51
|
+
raise OutOfBounds if offset > io.size
|
|
52
|
+
|
|
53
|
+
io.pos = offset
|
|
54
|
+
current = io.read(4).unpack('I').first
|
|
55
|
+
current += value
|
|
56
|
+
current = [current].pack('I')
|
|
57
|
+
|
|
58
|
+
io.pos = offset
|
|
59
|
+
io.write current
|
|
60
|
+
io.close
|
|
61
|
+
self
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
require 'rcs-common/trace'
|
|
2
|
+
|
|
3
|
+
module RCS
|
|
4
|
+
module Component
|
|
5
|
+
def self.included(base)
|
|
6
|
+
base.__send__(:include, RCS::Tracer)
|
|
7
|
+
base.__send__(:extend, RCS::Tracer)
|
|
8
|
+
|
|
9
|
+
base.__send__(:extend, ClassMethods)
|
|
10
|
+
|
|
11
|
+
base.__send__(:define_singleton_method, :component) do |value, name: nil|
|
|
12
|
+
@_component = value.to_sym
|
|
13
|
+
@_component_name = name || "RCS #{value.to_s.capitalize}"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def component
|
|
18
|
+
self.class.instance_variable_get('@_component')
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def component_name
|
|
22
|
+
self.class.instance_variable_get('@_component_name')
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def full_component_version
|
|
26
|
+
File.read(Dir.pwd + '/config/VERSION').strip
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def component_version
|
|
30
|
+
$version || full_component_version.split('-').first
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def show_startup_message
|
|
34
|
+
build = File.read(Dir.pwd + '/config/VERSION_BUILD')
|
|
35
|
+
trace :fatal, "Starting the #{component_name} #{full_component_version} (#{build})..."
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def database
|
|
39
|
+
@_database ||= begin
|
|
40
|
+
db_class = RCS.const_get('Collector::DB') rescue RCS.const_get('DB::DB')
|
|
41
|
+
db_class.instance
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def establish_database_connection(wait_until_connected: false)
|
|
46
|
+
loop do
|
|
47
|
+
connected = database.respond_to?(:connect!) ? database.connect!(component) : database.connect
|
|
48
|
+
|
|
49
|
+
if connected
|
|
50
|
+
trace :info, "Database connection succeeded"
|
|
51
|
+
break
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
if wait_until_connected
|
|
55
|
+
trace :warn, "Database connection failed, retry..."
|
|
56
|
+
sleep 1
|
|
57
|
+
else
|
|
58
|
+
trace :error, "Database connection failed"
|
|
59
|
+
break
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def run_with_rescue
|
|
65
|
+
$version = component_version
|
|
66
|
+
trace_setup
|
|
67
|
+
show_startup_message
|
|
68
|
+
yield
|
|
69
|
+
return 0
|
|
70
|
+
rescue Interrupt
|
|
71
|
+
# call the kill handler if defined
|
|
72
|
+
kill if self.respond_to? :kill
|
|
73
|
+
trace :info, "User asked to exit. Bye bye!"
|
|
74
|
+
exit(0)
|
|
75
|
+
rescue Exception => e
|
|
76
|
+
trace :fatal, "FAILURE: " << e.message
|
|
77
|
+
trace :fatal, "EXCEPTION: [#{e.class}] " << e.backtrace.join("\n")
|
|
78
|
+
exit(1)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
module ClassMethods
|
|
82
|
+
def run!(*argv)
|
|
83
|
+
return new.run(argv)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#
|
|
2
|
+
# The encryption module.
|
|
3
|
+
# by default we use the PKCS5 padding
|
|
4
|
+
# force the third parameter to change the padding
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
require 'openssl'
|
|
8
|
+
require 'digest/sha1'
|
|
9
|
+
|
|
10
|
+
module RCS
|
|
11
|
+
|
|
12
|
+
module Crypt
|
|
13
|
+
PAD_NOPAD = 0
|
|
14
|
+
PAD_PKCS5 = 1
|
|
15
|
+
SHA1_DIGEST_LENGTH = 20
|
|
16
|
+
|
|
17
|
+
def aes_encrypt(clear_text, key, padding=PAD_PKCS5)
|
|
18
|
+
cipher = OpenSSL::Cipher::Cipher.new('aes-128-cbc')
|
|
19
|
+
cipher.encrypt
|
|
20
|
+
cipher.padding = padding
|
|
21
|
+
cipher.key = key
|
|
22
|
+
cipher.iv = "\x00" * cipher.iv_len
|
|
23
|
+
edata = cipher.update(clear_text)
|
|
24
|
+
edata << cipher.final
|
|
25
|
+
return edata
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def aes_decrypt(enc_text, key, padding=PAD_PKCS5)
|
|
29
|
+
decipher = OpenSSL::Cipher::Cipher.new('aes-128-cbc')
|
|
30
|
+
decipher.decrypt
|
|
31
|
+
decipher.padding = padding
|
|
32
|
+
decipher.key = key
|
|
33
|
+
decipher.iv = "\x00" * decipher.iv_len
|
|
34
|
+
data = decipher.update(enc_text)
|
|
35
|
+
data << decipher.final
|
|
36
|
+
return data
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def aes_encrypt_integrity(clear_text, key, padding=PAD_PKCS5)
|
|
40
|
+
# add the integrity check at the end of the message
|
|
41
|
+
clear_text += Digest::SHA1.digest(clear_text)
|
|
42
|
+
return aes_encrypt(clear_text, key, padding)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def aes_decrypt_integrity(enc_text, key, padding=PAD_PKCS5)
|
|
46
|
+
text = aes_decrypt(enc_text, key, padding)
|
|
47
|
+
# check the integrity at the end of the message
|
|
48
|
+
check = text.slice!(text.length - SHA1_DIGEST_LENGTH, text.length)
|
|
49
|
+
raise "Invalid sha1 check" unless check == Digest::SHA1.digest(text)
|
|
50
|
+
return text
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
end #namespace
|
|
55
|
+
|
|
56
|
+
if __FILE__ == $0
|
|
57
|
+
require 'securerandom'
|
|
58
|
+
include RCS::Crypt
|
|
59
|
+
|
|
60
|
+
clear = SecureRandom.random_bytes(16)
|
|
61
|
+
key = Digest::MD5.digest "4yeN5zu0+il3Jtcb5a1sBcAdjYFcsD9z"
|
|
62
|
+
|
|
63
|
+
puts "DATA: " + clear.unpack('H*').to_s
|
|
64
|
+
enc = aes_encrypt(clear, key)
|
|
65
|
+
|
|
66
|
+
puts "ENC: " + enc.unpack('H*').to_s
|
|
67
|
+
dec = aes_decrypt(enc, key)
|
|
68
|
+
|
|
69
|
+
puts "DEC: " + dec.unpack('H*').to_s
|
|
70
|
+
|
|
71
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
module RCS
|
|
2
|
+
class Deploy
|
|
3
|
+
attr_reader :me, :target
|
|
4
|
+
|
|
5
|
+
def initialize(params)
|
|
6
|
+
@target = Target.new(params)
|
|
7
|
+
@me = Me.new
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
class Me
|
|
11
|
+
attr_reader :path
|
|
12
|
+
|
|
13
|
+
def initialize
|
|
14
|
+
@path ||= File.expand_path(Dir.pwd)
|
|
15
|
+
|
|
16
|
+
raise "Missing rakefile" unless File.exists?("#{@path}/Rakefile")
|
|
17
|
+
raise "Not in a git repo" unless Dir.exists?("#{@path}/.git")
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def run(cmd, opts = {})
|
|
21
|
+
puts "executing: #{cmd}"
|
|
22
|
+
opts[:trap] ? `#{cmd}` : Kernel.system(cmd)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def pending_changes?
|
|
26
|
+
run("cd \"#{path}\" && git status", trap: true) !~ /nothing to commit, working directory clean/
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def ask(question, yes_no: true, choices: %w[y n])
|
|
30
|
+
print("#{question} (#{choices.join(', ')}): ")
|
|
31
|
+
|
|
32
|
+
begin
|
|
33
|
+
answer = STDIN.readline.strip.downcase
|
|
34
|
+
yes_no ? (answer == 'y' or answer == 'yes') : answer
|
|
35
|
+
rescue Interrupt
|
|
36
|
+
puts "\nBye"
|
|
37
|
+
exit
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
class Target
|
|
43
|
+
attr_reader :user, :address
|
|
44
|
+
|
|
45
|
+
def initialize(params)
|
|
46
|
+
@user = params[:user]
|
|
47
|
+
@address = params[:address]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def add_slash(path)
|
|
51
|
+
path.end_with?('/') ? "#{path}" : "#{path}/"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def transfer(src, remote_folder, opts = {})
|
|
55
|
+
dst = add_slash(remote_folder)
|
|
56
|
+
|
|
57
|
+
run_without_ssh("rsync -tcv #{src} #{user}@#{address}:\"#{dst}\"", opts)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def mirror!(local_folder, remote_folder, opts = {})
|
|
61
|
+
src = add_slash(local_folder)
|
|
62
|
+
dst = add_slash(remote_folder)
|
|
63
|
+
|
|
64
|
+
run_without_ssh("rsync --delete -vazc \"#{src}\" #{user}@#{address}:\"#{dst}\"", opts)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def mirror(local_folder, remote_folder, opts = {})
|
|
68
|
+
opts[:trap] = true
|
|
69
|
+
result = mirror!(local_folder, remote_folder, opts)
|
|
70
|
+
changes = result.split("\n")[1..-3].reject { |x| x.empty? }
|
|
71
|
+
changed = changes.size > 0 && changes != ["./"]
|
|
72
|
+
|
|
73
|
+
if opts[:changes]
|
|
74
|
+
changed ? result : nil
|
|
75
|
+
else
|
|
76
|
+
changed
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def restart_service(name)
|
|
81
|
+
run_with_ssh("net stop \"#{name}\"; net start \"#{name}\"")
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def run_with_ssh(command, opts = {})
|
|
85
|
+
run_without_ssh("ssh #{user}@#{address} \""+ command.gsub('"', '\"') +"\"", opts)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
alias :run :run_with_ssh
|
|
89
|
+
|
|
90
|
+
def run_without_ssh(cmd, opts = {})
|
|
91
|
+
puts "executing: #{cmd}"
|
|
92
|
+
opts[:trap] ? `#{cmd}` : Kernel.system(cmd)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|