jorp 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +19 -0
- data/README.md +4 -0
- data/bin/jorp +5 -0
- data/jorp.gemspec +14 -0
- data/lib/jorp.rb +69 -0
- data/lib/jorp/basic_adaptor.rb +23 -0
- data/lib/jorp/listener.rb +9 -0
- data/lib/jorp/remote_adaptor.rb +64 -0
- data/lib/jorp/sudo.rb +47 -0
- data/lib/jorp/sudo_adaptor.rb +47 -0
- metadata +73 -0
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
.DS_Store
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
3
|
+
specs:
|
4
|
+
highline (1.6.15)
|
5
|
+
jruby-pageant (1.1.1)
|
6
|
+
listen (0.5.3)
|
7
|
+
net-scp (1.0.4)
|
8
|
+
net-ssh (>= 1.99.1)
|
9
|
+
net-ssh (2.6.0)
|
10
|
+
jruby-pageant (>= 1.1.1)
|
11
|
+
|
12
|
+
PLATFORMS
|
13
|
+
ruby
|
14
|
+
|
15
|
+
DEPENDENCIES
|
16
|
+
highline
|
17
|
+
listen
|
18
|
+
net-scp
|
19
|
+
net-ssh
|
data/README.md
ADDED
data/bin/jorp
ADDED
data/jorp.gemspec
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
|
2
|
+
Gem::Specification.new do |s|
|
3
|
+
s.name = 'jorp'
|
4
|
+
s.version = '1.0.0'
|
5
|
+
s.date = '2012-10-16'
|
6
|
+
s.summary = 'A SSH wrapper for local text editors'
|
7
|
+
s.description = 'Jorp lets you edit remote files using local editors'
|
8
|
+
s.authors = [ 'Stewart Ulm' ]
|
9
|
+
s.email = 'stewart.ulm@gmail.com'
|
10
|
+
s.homepage = 'http://github.com/stewartspeak/jorp'
|
11
|
+
s.files = `git ls-files`.split("\n")
|
12
|
+
s.executables = [ 'jorp' ]
|
13
|
+
s.require_paths = [ 'lib' ]
|
14
|
+
end
|
data/lib/jorp.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'optparse'
|
5
|
+
require 'jorp/sudo_adaptor'
|
6
|
+
require 'jorp/basic_adaptor'
|
7
|
+
require 'jorp/remote_adaptor'
|
8
|
+
|
9
|
+
module Jorp
|
10
|
+
|
11
|
+
DEFAULT_OPTIONS = {
|
12
|
+
:username => (ENV['USER']),
|
13
|
+
:editor => (ENV['JORP_EDITOR'] || ENV['EDITOR'] || 'subl -w'),
|
14
|
+
:hostname => ENV['JORP_HOST'],
|
15
|
+
:sudo => false,
|
16
|
+
}
|
17
|
+
|
18
|
+
class App
|
19
|
+
|
20
|
+
def initialize(args, bin_path)
|
21
|
+
@original_args = args.clone
|
22
|
+
@bin_path = bin_path
|
23
|
+
process_args(args)
|
24
|
+
end
|
25
|
+
|
26
|
+
def process_args(args)
|
27
|
+
@options = DEFAULT_OPTIONS.clone
|
28
|
+
parser = OptionParser.new(args) do |opts|
|
29
|
+
opts.banner = 'jorp [options] [file]'
|
30
|
+
opts.program_name = 'Jorp'
|
31
|
+
opts.version = '0.1'
|
32
|
+
opts.on('-s', '--sudo') { @options[:sudo] = true }
|
33
|
+
opts.on('-e', '--editor [command]') {|e| @options[:editor] = e }
|
34
|
+
opts.on('-u', '--username [username]') {|u| @options[:username] = u }
|
35
|
+
opts.on('-h', '--host [hostname]') {|h| @options[:hostname] = h }
|
36
|
+
end
|
37
|
+
parser.load(File.expand_path('~/.jorp'))
|
38
|
+
parser.parse!
|
39
|
+
if ARGV.length != 1
|
40
|
+
puts parser
|
41
|
+
exit -1
|
42
|
+
end
|
43
|
+
@options[:path] = args[0]
|
44
|
+
end
|
45
|
+
|
46
|
+
def run
|
47
|
+
if @options[:hostname].nil?
|
48
|
+
if @options[:sudo]
|
49
|
+
@adaptor = SudoAdaptor.new(@options[:path])
|
50
|
+
else
|
51
|
+
@adaptor = BasicAdaptor.new(@options[:path])
|
52
|
+
end
|
53
|
+
else
|
54
|
+
puts "Using remote host: \"#{@options[:hostname]}\""
|
55
|
+
if @options[:sudo]
|
56
|
+
@adaptor = RemoteAdaptor.new(@options[:hostname], @options[:path], @options[:username])
|
57
|
+
else
|
58
|
+
@adaptor = RemoteAdaptor.new(@options[:hostname], @options[:path], @options[:username])
|
59
|
+
end
|
60
|
+
end
|
61
|
+
@adaptor.connect
|
62
|
+
system("#{@options[:editor]} \"#{@adaptor.proxy_path}\"")
|
63
|
+
@adaptor.disconnect
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'tmpdir'
|
4
|
+
require 'pty'
|
5
|
+
require 'expect'
|
6
|
+
require 'listen'
|
7
|
+
require 'net/ssh'
|
8
|
+
require 'net/scp'
|
9
|
+
|
10
|
+
module Jorp
|
11
|
+
|
12
|
+
class RemoteAdaptor
|
13
|
+
|
14
|
+
def initialize(hostname, path, username)
|
15
|
+
@hostname = hostname
|
16
|
+
@path = path
|
17
|
+
@username = username
|
18
|
+
@proxy_directory = Dir.mktmpdir('jorp')
|
19
|
+
@proxy_path = File.join(@proxy_directory, File.basename(@path))
|
20
|
+
end
|
21
|
+
|
22
|
+
def upload(connection)
|
23
|
+
begin
|
24
|
+
copier = Net::SCP.new(connection)
|
25
|
+
copier.upload!(@proxy_path, @path)
|
26
|
+
rescue Exception => e
|
27
|
+
puts "ERROR: " + e
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def download(connection)
|
32
|
+
begin
|
33
|
+
copier = Net::SCP.new(connection)
|
34
|
+
copier.download!(@path, @proxy_path)
|
35
|
+
rescue Exception => e
|
36
|
+
puts "ERROR: " + e
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def connect
|
41
|
+
connection = Net::SSH.start(@hostname, @username)
|
42
|
+
download(connection)
|
43
|
+
Thread.new do
|
44
|
+
begin
|
45
|
+
Listen.to(@proxy_directory, :filter => /^#{Regexp.escape(File.basename(@proxy_path))}$/) do
|
46
|
+
upload(connection)
|
47
|
+
end
|
48
|
+
rescue Exception => e
|
49
|
+
puts "An error occurred: " + e.inspect
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def disconnect
|
55
|
+
FileUtils.remove_entry_secure(@proxy_directory)
|
56
|
+
end
|
57
|
+
|
58
|
+
def proxy_path
|
59
|
+
@proxy_path
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
data/lib/jorp/sudo.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Jorp
|
4
|
+
|
5
|
+
class Sudo
|
6
|
+
|
7
|
+
def initialize()
|
8
|
+
end
|
9
|
+
|
10
|
+
def prompt
|
11
|
+
password = nil
|
12
|
+
begin
|
13
|
+
PTY.spawn("sudo -k -p ENTER_PASSWORD whoami") do |input, output, pid|
|
14
|
+
loop do
|
15
|
+
response = input.expect(/^ENTER_PASSWORD/)
|
16
|
+
if response.nil? || response.length == 0
|
17
|
+
raise IOError.new
|
18
|
+
end
|
19
|
+
output.puts(password = HighLine.ask("Enter password: ") {|q| q.echo = false })
|
20
|
+
input.expect(/^(Sorry, try again|root$)/) do |matches|
|
21
|
+
if (matches[1] =~ /^Sorry, try again/)
|
22
|
+
password = nil
|
23
|
+
puts("Sorry, try again.")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
break unless password.nil?
|
27
|
+
end
|
28
|
+
end
|
29
|
+
rescue
|
30
|
+
@password = nil
|
31
|
+
end
|
32
|
+
@password = password
|
33
|
+
end
|
34
|
+
|
35
|
+
def exec(command)
|
36
|
+
PTY.spawn("sudo -k -p ENTER_PASSWORD #{command}") do |input, output, pid|
|
37
|
+
input.expect(/^ENTER_PASSWORD/)
|
38
|
+
output.puts(@password)
|
39
|
+
while (response = input.gets)
|
40
|
+
# no-op
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'tmpdir'
|
4
|
+
require 'pty'
|
5
|
+
require 'expect'
|
6
|
+
require 'highline/import'
|
7
|
+
require 'listen'
|
8
|
+
require 'jorp/sudo'
|
9
|
+
|
10
|
+
module Jorp
|
11
|
+
|
12
|
+
class SudoAdaptor
|
13
|
+
|
14
|
+
def initialize(path)
|
15
|
+
@source_path = path
|
16
|
+
@proxy_directory = Dir.mktmpdir('jorp')
|
17
|
+
@proxy_path = File.join(@proxy_directory, File.basename(@source_path))
|
18
|
+
FileUtils.touch(@proxy_path)
|
19
|
+
end
|
20
|
+
|
21
|
+
def connect
|
22
|
+
sudo = Sudo.new
|
23
|
+
if sudo.prompt
|
24
|
+
sudo.exec("cp \"#{@source_path}\" \"#{@proxy_path}\"")
|
25
|
+
Thread.new do
|
26
|
+
begin
|
27
|
+
Listen.to(@proxy_directory, :filter => /^#{Regexp.escape(File.basename(@proxy_path))}$/) do
|
28
|
+
sudo.exec("cp -p \"#{proxy_path}\" \"#{@source_path}\"")
|
29
|
+
end
|
30
|
+
rescue Exception => e
|
31
|
+
# log this error
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def disconnect
|
38
|
+
FileUtils.remove_entry_secure(@proxy_directory)
|
39
|
+
end
|
40
|
+
|
41
|
+
def proxy_path
|
42
|
+
@proxy_path
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jorp
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 1
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
version: 1.0.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Stewart Ulm
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2012-10-16 00:00:00 -04:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: Jorp lets you edit remote files using local editors
|
22
|
+
email: stewart.ulm@gmail.com
|
23
|
+
executables:
|
24
|
+
- jorp
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files: []
|
28
|
+
|
29
|
+
files:
|
30
|
+
- .gitignore
|
31
|
+
- Gemfile
|
32
|
+
- Gemfile.lock
|
33
|
+
- README.md
|
34
|
+
- bin/jorp
|
35
|
+
- jorp.gemspec
|
36
|
+
- lib/jorp.rb
|
37
|
+
- lib/jorp/basic_adaptor.rb
|
38
|
+
- lib/jorp/listener.rb
|
39
|
+
- lib/jorp/remote_adaptor.rb
|
40
|
+
- lib/jorp/sudo.rb
|
41
|
+
- lib/jorp/sudo_adaptor.rb
|
42
|
+
has_rdoc: true
|
43
|
+
homepage: http://github.com/stewartspeak/jorp
|
44
|
+
licenses: []
|
45
|
+
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options: []
|
48
|
+
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
segments:
|
56
|
+
- 0
|
57
|
+
version: "0"
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
segments:
|
63
|
+
- 0
|
64
|
+
version: "0"
|
65
|
+
requirements: []
|
66
|
+
|
67
|
+
rubyforge_project:
|
68
|
+
rubygems_version: 1.3.6
|
69
|
+
signing_key:
|
70
|
+
specification_version: 3
|
71
|
+
summary: A SSH wrapper for local text editors
|
72
|
+
test_files: []
|
73
|
+
|