agentx 0.0.2

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7bbc2d4ab57f2dfc3344a0d733bcd9620c395a51
4
+ data.tar.gz: a8503dd2073f8e0412fa7206ce46a44a8f85537b
5
+ SHA512:
6
+ metadata.gz: a9895c44cab27796c8b73116515af66b5eee193014d9521dfb2e20255de82800b6dda4975949a5e1ef85bdd379305165083b8d400e70d8bcbf01d817ab7ae32d
7
+ data.tar.gz: b6d57a674c4deec1450dc41d5a57de5db7b3079ff22abb86196b5f1960a402959ab2b244b09b8c27d39235b48c46801bcab0577aec6706e1bae506b32fc7f901
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in agentx.gemspec
4
+ gemspec
data/README ADDED
@@ -0,0 +1,31 @@
1
+ # AgentX
2
+
3
+ This is still under development. The idea is to be a programmer-friendly tool
4
+ for doing things on the internet.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'agentx'
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install agentx
19
+
20
+ ## Usage
21
+
22
+ TODO: Write some documentation!
23
+
24
+ ## Contributing
25
+
26
+ 1. Fork it ( https://github.com/eki/agentx/fork )
27
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
28
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
29
+ 4. Push to the branch (`git push origin my-new-feature`)
30
+ 5. Create a new Pull Request
31
+
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'agentx/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "agentx"
8
+ spec.version = AgentX::VERSION
9
+ spec.authors = ["Eric K Idema"]
10
+ spec.email = ["eki@vying.org"]
11
+ spec.summary = %q{A tool for doing things on the Internet.}
12
+ spec.description = %q{A tool for doing things on the Internet.}
13
+ spec.homepage = "https://github.com/eki/agentx"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.6"
21
+ spec.add_development_dependency "rake"
22
+
23
+ spec.add_dependency 'ethon'
24
+ spec.add_dependency 'http-cookie'
25
+ spec.add_dependency 'nokogiri'
26
+ spec.add_dependency 'listen'
27
+ spec.add_dependency 'oj'
28
+ spec.add_dependency 'sqlite3'
29
+ end
30
+
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'agentx'
4
+ require 'agentx/console'
5
+ require 'fileutils'
6
+ require 'listen'
7
+ require 'optparse'
8
+
9
+ options={}
10
+
11
+ option_parser = OptionParser.new do |opts|
12
+ opts.banner = "Usage: agentx [OPTION] [FILE]"
13
+
14
+ opts.on('-h', '--help', 'This help message') do
15
+ puts opts
16
+ puts <<-eos
17
+
18
+ If no FILE is provided, an interactive session will be started. The
19
+ --interactive flag is only needed if you would like to execute a file and then
20
+ enter an interactive session.
21
+
22
+ All file arguments should be valid Ruby. Files will be executed in the order
23
+ provided at the command-line.
24
+
25
+ By default a config file is located at ~/.agentx/config and should be valid
26
+ Ruby. This config file will be executed before any FILE arguements. The
27
+ default config is an empty file.
28
+
29
+ eos
30
+ exit
31
+ end
32
+
33
+ opts.on('-c', '--config [FILE]', 'Use given config file.') do |v|
34
+ options[:config] = v
35
+ end
36
+
37
+ opts.on('-i', '--interactive', 'Run an interactive console.') do
38
+ options[:interactive] = true
39
+ end
40
+
41
+ opts.on('-v', '--version', 'Print the version and exit.') do
42
+ puts "agentx version #{AgentX::VERSION}"
43
+ exit
44
+ end
45
+ end
46
+
47
+ option_parser.parse!
48
+
49
+ console = AgentX::Console.new(options)
50
+ console.load_config
51
+
52
+ files = ARGV.map { |f| File.expand_path(f) }.select { |f| File.exists?(f) }
53
+
54
+ if files.any?
55
+ files.each { |f| AgentX::Console.load(f) }
56
+ else
57
+ options[:interactive] = true
58
+ end
59
+
60
+ if options[:interactive]
61
+ config_dir = File.dirname(options[:config])
62
+ config_file = File.basename(options[:config])
63
+
64
+ listener = Listen.to(config_dir, only: /^#{config_file}$/) do |m,a,r|
65
+ console.load_config
66
+ end
67
+
68
+ listener.start
69
+
70
+ ARGV.clear
71
+
72
+ require 'irb'
73
+
74
+ IRB.init_config(nil)
75
+
76
+ IRB.conf[:AUTO_INDENT] = true
77
+ IRB.conf[:PROMPT_MODE] = :SIMPLE
78
+
79
+ IRB.conf[:SAVE_HISTORY] = 100
80
+ IRB.conf[:HISTORY_FILE] = "#{AgentX.root}/console.log"
81
+ IRB.conf[:MAIN_CONTEXT] = IRB::Irb.new.context
82
+
83
+ IRB.load_modules
84
+
85
+ require 'irb/ext/save-history'
86
+ require 'irb/completion'
87
+
88
+ workspace = IRB::WorkSpace.new(console)
89
+ irb = IRB::Irb.new(workspace)
90
+
91
+ trap("SIGINT") do
92
+ catch(:IRB_EXIT) do
93
+ irb.signal_handle
94
+ end
95
+ end
96
+
97
+ begin
98
+ catch(:IRB_EXIT) do
99
+ irb.eval_input
100
+ end
101
+ ensure
102
+ IRB.irb_at_exit
103
+ listener.stop
104
+ end
105
+ end
106
+
@@ -0,0 +1,16 @@
1
+ # AgentX Example Config
2
+
3
+ # This file is plain Ruby. Any methods you may define are available at the
4
+ # top-level of the console and are loaded before any file you may execute.
5
+ # Already defined:
6
+ #
7
+ # load_config - (Re)loads this config file, if it's been changed.
8
+ # options - The command-line options agentx was run with.
9
+ #
10
+
11
+ # Example of defining a session for quick use:
12
+
13
+ # def session
14
+ # @session ||= AgentX::Session.new('https://www.google.com')
15
+ # end
16
+
@@ -0,0 +1,53 @@
1
+
2
+ require 'agentx/version'
3
+
4
+ require 'digest/md5'
5
+ require 'time'
6
+ require 'uri'
7
+
8
+ require 'ethon'
9
+ require 'http-cookie'
10
+ require 'nokogiri'
11
+ require 'oj'
12
+ require 'sqlite3'
13
+
14
+ require 'agentx/history'
15
+ require 'agentx/html'
16
+ require 'agentx/xml'
17
+ require 'agentx/request'
18
+ require 'agentx/response'
19
+ require 'agentx/cache'
20
+ require 'agentx/session'
21
+
22
+ module AgentX
23
+ def self.root
24
+ return @root if @root
25
+
26
+ @root = File.expand_path('~/.agentx')
27
+
28
+ Dir.mkdir(@root) unless Dir.exists?(@root)
29
+
30
+ @root
31
+ end
32
+
33
+ def self.session
34
+ @session ||= Session.new
35
+ end
36
+
37
+ def self.[](*args)
38
+ session[*args]
39
+ end
40
+
41
+ def self.logger
42
+ return @logger if @logger
43
+
44
+ @logger = Logger.new(File.join(root, 'request.log'))
45
+
46
+ @logger.formatter = proc do |severity, datetime, progname, msg|
47
+ "#{datetime} | #{msg}\n"
48
+ end
49
+
50
+ @logger
51
+ end
52
+ end
53
+
@@ -0,0 +1,124 @@
1
+
2
+ module AgentX
3
+ class Cache
4
+ def self.store_path
5
+ return @store_path if @store_path
6
+
7
+ @store_path = File.join(AgentX.root, 'cache')
8
+
9
+ unless Dir.exists?(@store_path)
10
+ Dir.mkdir(@store_path)
11
+ end
12
+
13
+ @store_path
14
+ end
15
+
16
+ def self.path(request)
17
+ File.join(store_path, "#{request.cache_key}.json")
18
+ end
19
+
20
+ def self.db
21
+ @db ||= Database.new
22
+ end
23
+
24
+ def self.write(request, response)
25
+ db.write(
26
+ request_cache_key: request.cache_key,
27
+ request_host: request.host,
28
+ request_base_url: request.base_url,
29
+ response_code: response.code,
30
+ response_headers: Oj.dump(response.headers.to_hash),
31
+ response_content_length: response.body.length,
32
+ response_expires_at: response.expires_at,
33
+ response_body: response.body)
34
+ end
35
+
36
+ def self.read(request)
37
+ if h = db.read(request.cache_key)
38
+ Response.new(
39
+ h['response_code'],
40
+ h['response_body'],
41
+ Oj.load(h['response_headers']))
42
+ end
43
+ end
44
+
45
+ class Database
46
+ attr_reader :filename
47
+
48
+ def initialize(filename=File.join(AgentX.root, 'cache.sqlite3'))
49
+ @filename = filename
50
+
51
+ create
52
+ end
53
+
54
+ def create
55
+ if File.exists?(filename)
56
+ return @db = SQLite3::Database.new(filename)
57
+ end
58
+
59
+ @db = SQLite3::Database.new(filename)
60
+
61
+ @db.execute(<<-SQL)
62
+ CREATE TABLE responses (
63
+ request_cache_key STRING PRIMARY KEY,
64
+ request_host STRING,
65
+ request_base_url STRING,
66
+ response_code INTEGER,
67
+ response_headers TEXT,
68
+ response_content_length INTEGER,
69
+ response_expires_at INTEGER,
70
+ response_body BLOB)
71
+ SQL
72
+
73
+ @db.execute(<<-SQL)
74
+ CREATE INDEX responses_expires_at ON responses (response_expires_at)
75
+ SQL
76
+
77
+ @db.execute(<<-SQL)
78
+ CREATE INDEX responses_host ON responses (request_host)
79
+ SQL
80
+
81
+ @db
82
+ end
83
+
84
+ INSERT_SQL = <<-SQL
85
+ INSERT OR REPLACE INTO responses (
86
+ request_cache_key,
87
+ request_host,
88
+ request_base_url,
89
+ response_code,
90
+ response_headers,
91
+ response_content_length,
92
+ response_expires_at,
93
+ response_body) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
94
+ SQL
95
+
96
+ def write(opts={})
97
+ @prepared_write ||= @db.prepare(INSERT_SQL)
98
+
99
+ @prepared_write.execute(
100
+ opts[:request_cache_key],
101
+ opts[:request_host],
102
+ opts[:request_base_url],
103
+ opts[:response_code],
104
+ opts[:response_headers],
105
+ opts[:response_content_length],
106
+ opts[:response_expires_at].to_i,
107
+ opts[:response_body])
108
+ end
109
+
110
+ SELECT_BY_CACHE_KEY_SQL = <<-SQL
111
+ SELECT * FROM responses WHERE request_cache_key = ?
112
+ SQL
113
+
114
+ def read(cache_key)
115
+ @prepared_read ||= @db.prepare(SELECT_BY_CACHE_KEY_SQL)
116
+
117
+ rs = @prepared_read.execute(cache_key)
118
+ rs.next_hash
119
+ end
120
+
121
+ end
122
+ end
123
+ end
124
+
@@ -0,0 +1,31 @@
1
+
2
+ module AgentX
3
+ class Console
4
+ attr_reader :options
5
+
6
+ def initialize(options={})
7
+ @options = options
8
+ end
9
+
10
+ def load_config
11
+ unless options[:config]
12
+ options[:config] = "#{AgentX.root}/config"
13
+
14
+ unless File.exists?(options[:config])
15
+ example =
16
+ File.expand_path('../../config.example', File.dirname(__FILE__))
17
+ FileUtils.cp(example, options[:config])
18
+ FileUtils.chmod(0600, options[:config])
19
+ end
20
+ end
21
+
22
+ self.class.load(options[:config])
23
+ end
24
+
25
+ def self.load(filename)
26
+ class_eval(File.read(filename))
27
+ end
28
+
29
+ end
30
+ end
31
+
@@ -0,0 +1,49 @@
1
+
2
+ module AgentX
3
+
4
+ class History
5
+ include Enumerable
6
+
7
+ def initialize
8
+ @entries = []
9
+ end
10
+
11
+ def add(request, response)
12
+ @entries << Entry.new(request, response)
13
+ end
14
+
15
+ def [](n)
16
+ @entries[n]
17
+ end
18
+
19
+ def first
20
+ @entries.first
21
+ end
22
+
23
+ def last
24
+ @entries.last
25
+ end
26
+
27
+ def length
28
+ @entries.length
29
+ end
30
+
31
+ def each(&block)
32
+ @entries.each(&block)
33
+ end
34
+
35
+ class Entry
36
+ attr_reader :request, :response
37
+
38
+ def initialize(request, response)
39
+ @request, @response = request, response
40
+ end
41
+
42
+ def inspect
43
+ [request, response].inspect
44
+ end
45
+ end
46
+
47
+ end
48
+ end
49
+