encinch 0.0.3 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: edb2e2e7c096ffda8693dcd2c641689724862796
4
- data.tar.gz: 2d8fcb47f7ee5ef3673503654d9cb6224f86d07f
3
+ metadata.gz: eef846946cd6db68553cec479300a1a1ccdda651
4
+ data.tar.gz: 94241b2ebd043cd8487a6830f22c2493a278c135
5
5
  SHA512:
6
- metadata.gz: 589cea1054cbf7f40b792fd6e52b9e51b4d69f81171f13819d204be8616c3576586f44a19a1590cc54a0e1efe80aaa393fe7a1dcf1717a4a8b190c18018ea36f
7
- data.tar.gz: 5f640b06f13ca5e1c253ee0bae607762c22dec34e11b7201121a6f02e410cd7377c35825d49e41453448e035defc703bacbfc296377f886aa56ffa6badc97580
6
+ metadata.gz: 7d31b2e1bbdb8bb29b73621252a4fc429029c19f053e0424398ac4de72c226d0ff371d7a623a276be7ab510fbc7c74f25fc95228d192953371a420116e7aa567
7
+ data.tar.gz: 57041d4c9528d1f4f20ebb7ebf38bcfe5560d875e8ced00d2a4a056cd6f4f9895aa55676c34696b8f7ed83a05e15cf1c5d2dc6d9814d2aac37f6c2d25f635ec0
data/.gitignore CHANGED
@@ -21,3 +21,5 @@ tmp
21
21
  *.o
22
22
  *.a
23
23
  mkmf.log
24
+ bot.rb
25
+ *.yml
@@ -1,4 +1,4 @@
1
- Copyright (c) 2015 Jason Franz
1
+ Copyright (c) 2016 Jason Franz
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -11,7 +11,7 @@ https://github.com/cinchrb/cinch
11
11
  $[sudo] gem install encinch
12
12
 
13
13
  ## Example
14
- ```
14
+ ```ruby
15
15
  require 'cinch'
16
16
  require 'cinch/plugins/encinch'
17
17
 
@@ -22,18 +22,21 @@ https://github.com/cinchrb/cinch
22
22
  c.server = "irc.freenode.org"
23
23
  c.port = 7000
24
24
  c.ssl.use = true
25
- c.channels = ["#cryptedchan", "#plaintext"]
25
+ c.channels = ["#cryptedchan", "#plaintext", "#ignorechannel"]
26
26
 
27
27
  c.plugins.plugins = [Cinch::Plugins::EnCinch] # optionally add more plugins
28
28
 
29
29
  c.plugins.options[Cinch::Plugins::EnCinch] = {
30
- :ignore => ["#ignorechannel", "ignoreperson"],
30
+ :drop => true,
31
+ :key_file => 'keys/key.yml',
32
+ :uncrypted => ["#plaintext"],
33
+ :ignore => ["#ignorechan", "ignoreperson"],
31
34
  :encrypt => {
32
- '#cryptedchan' => "myfishkey",
33
- 'cryptednick' => "notmyfishkey",
34
- :default => "adefaultfishkey"
35
- },
36
- :uncrypted => ["#plaintext"]
35
+ '#cryptedchan' => 'myfishkey',
36
+ '#ignorechan' => 'ignoretargetscanhaveencryptionkeys'
37
+ 'cryptednick' => 'mypersonalfishkey',
38
+ :default => 'defaultfishkey'
39
+ }
37
40
  }
38
41
  end
39
42
  end
@@ -41,25 +44,38 @@ https://github.com/cinchrb/cinch
41
44
  bot.start
42
45
  ```
43
46
 
47
+ ## Features
48
+ - Simple transparent message encryption for your Cinch IRC bot.
49
+ - Nicks with personal keys are updated on nick change.
50
+ - Ignore incoming encrypted messages from specific channels or users.
51
+ - Allow unencrypted messages to specific channels or users.
52
+ - Drop messages if target key not found.
53
+ - Ability to define default encryption key.
54
+ - Keys and options stored in yaml.
55
+
44
56
  ## Commands
45
- None, yet.
57
+ None. You should write your own plugin to add or remove keys, tailored to your particular needs.
46
58
 
47
59
 
48
60
  ## Options
49
61
  ### :encrypt
50
62
  A hash of encryption targets and their corresponding encryption keys. Targets should all be lowercase.
51
- :default can provide a general purpose key, generally good for private communications with the bot.
52
- The :encrypt option must be set, :default is optional.
63
+ `:default` can provide a general purpose key, possibly good for private communications with the bot or ensuring it does not respond unencrypted.
53
64
 
54
65
  ### :uncrypted
55
- An array of channels and individuals from which the bot will not encrypt messages. All entries must be lowercase. This option must be set. If there are no desired unencrypted targets set an empty array.
66
+ An array of channels and individuals from which the bot will not encrypt messages. All entries must be lowercase.
67
+ `:uncrypted` is optional.
56
68
 
57
69
  ### :ignore
58
- An array of channels and individuals (other bots?) to ignore encrypted messages. All entries must be lower case.
59
- :ignore is optional.
70
+ An array of channels and individuals (other bots?) to ignore encrypted messages. All entries must be lower case. Note that it will not drop messages sent from the bot.
71
+ `:ignore` is optional.
72
+
73
+ ### :drop
74
+ Boolean value indicating if unencrypted messages should be dropped. Targets in the `:uncrypted` array are not affected. Unnecessary if a `:default` key is set.
75
+
76
+ ### :key_file
77
+ Options are now stored in yaml. The default location is 'keys/encinch.yml'. Supply an alternate location if you so desire. Location will be created if it does not exist.
60
78
 
61
79
  ## TODO
62
- 1) keyexchange
63
- 2) maybe store keys to a file?
64
- 3) add commands for updating, removing and adding keys and other settings?
65
- 4) user feedback... :-)
80
+ 1. key exchange
81
+ 2. user feedback... :-)
@@ -0,0 +1,24 @@
1
+ task default: :test
2
+
3
+ task :test do
4
+ require 'rake/testtask'
5
+
6
+ files_list = Dir[File.join "test", "**", "*.rb"]
7
+ files = ENV['file'].split(',') rescue nil
8
+ verbose = !!ENV['verbose']
9
+
10
+ Rake::TestTask.new do |t|
11
+ t.verbose = verbose
12
+ t.test_files = if files
13
+ files.unshift "helper.rb"
14
+ files.map do |file|
15
+ unless index = files_list.index { |f| f.match(/\/#{file}$/) }
16
+ raise "UnknownFileError #{ file }"
17
+ end
18
+ files_list[index]
19
+ end
20
+ else
21
+ files_list
22
+ end
23
+ end
24
+ end
@@ -5,7 +5,7 @@ require 'cinch/plugins/encinch/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "encinch"
8
- spec.version = EnCinch::Version::VERSION
8
+ spec.version = ::Cinch::Plugins::EnCinch::VERSION
9
9
  spec.authors = ["jfrazx"]
10
10
  spec.email = ["staringblind@gmail.com"]
11
11
  spec.summary = %q{Transparent blowfish encryption plugin for Cinch: An IRC Bot Building Framework}
@@ -20,5 +20,10 @@ Gem::Specification.new do |spec|
20
20
  spec.require_paths = ["lib"]
21
21
 
22
22
  spec.add_runtime_dependency "cinch", "~> 2.0"
23
+ spec.add_runtime_dependency "cinch-storage", "~> 1.2"
23
24
  spec.add_runtime_dependency "crypt", "~> 2.0"
25
+
26
+ spec.add_development_dependency "rake", "~> 11.0"
27
+ spec.add_development_dependency "minitest", "~> 5.8"
28
+ spec.add_development_dependency "minitest-reporters", "~> 1.1"
24
29
  end
@@ -1,5 +1,9 @@
1
1
  require 'cinch'
2
+ require 'cinch/storage'
2
3
  require 'crypt/blowfish'
4
+
3
5
  require "cinch/plugins/encinch/encryption"
6
+ require "cinch/plugins/encinch/storage"
4
7
  require "cinch/plugins/encinch/encinch"
5
8
  require "cinch/plugins/encinch/extend"
9
+ require "cinch/plugins/encinch/version"
@@ -1,137 +1,123 @@
1
1
 
2
2
  module Cinch
3
3
  module Plugins
4
- class EnCinch
5
- include Cinch::Plugin
4
+ class EnCinch
5
+ include Cinch::Plugin
6
6
 
7
- def initialize(*)
8
- super
7
+ def initialize(*)
8
+ super
9
+ config[:uncrypted] ||= Array.new
10
+ config[:ignore] ||= Array.new
11
+ config[:encrypt] ||= Hash.new
9
12
 
10
- # does Cinch have something to checked required options already???
11
- raise MissingRequiredPluginOptions unless config[:encrypt]
12
- raise MissingRequiredPluginOptions unless config[:uncrypted]
13
-
14
- end
15
-
16
- #
17
- # All blowfish communication should be prefixed with +OK
18
- # capture this to start the process
19
- #
20
-
21
- match(/\+OK (\S+)/, use_prefix: false, strip_colors: true, method: :capture)
22
-
23
- def capture(m, message)
24
- target = (m.channel ? m.channel.name : m.user.nick).downcase
25
- message = strip(m, message)
26
-
27
- return if config[:ignore].include?(target) rescue nil
28
- @key = config[:encrypt][target] || config[:encrypt][:default] rescue nil
13
+ shared[:encinch] = EnCinch::Storage.new(bot, config.dup)
14
+ end
29
15
 
30
- return unless @key
16
+ #
17
+ # All blowfish communication should be prefixed with +OK
18
+ # capture this to start the process
19
+ #
31
20
 
32
- blowfish(@key)
21
+ match(/\+OK (\S+)/, use_prefix: false, strip_colors: true, method: :capture)
22
+ def capture(m, message)
23
+ target = (m.channel? ? m.channel.name : m.user.nick).downcase
33
24
 
34
- decrypted = decrypt(message)
35
- decrypted << '\u0001' if m.action?
25
+ options = shared[:encinch].storage.data
36
26
 
37
- raw = modify_raw(m.raw, decrypted)
27
+ return if options[:ignore].include?(target)
28
+ return unless key = options[:encrypt][target] || options[:encrypt][:default]
38
29
 
39
- dispatch(Message.new(raw, m.bot))
40
- end
30
+ blowfish(key)
41
31
 
42
- #
43
- # matching for key exchange
44
- # NOT WORKING YET
45
- # It appears Cinch does not emit a :notice event, unfortunate
46
- #
32
+ message = strip(message)
33
+ decrypted = decrypt(message)
47
34
 
48
- match(/DH1080_INIT/, use_prefix: false, react_on: [:private, :notice], method: :key_exchange)
35
+ @fish = nil
49
36
 
50
- def key_exchange(m)
51
- return if m.channel?
37
+ decrypted << '\u0001' if m.action?
38
+ raw = modify_raw(m.raw, decrypted)
52
39
 
53
- debug "captured key exchange event with key: #{m.message}"
40
+ dispatch(Message.new(raw, m.bot))
41
+ end
54
42
 
55
- #TODO -- everything
56
- end
43
+ #
44
+ # matching for key exchange
45
+ # NOT WORKING YET
46
+ #
47
+ # DH1080_INIT U5bsJHJUvzIeflIxOvz+wht3LojOx8JEz83wa5ByfN9yQlT2AlW6fjp277w4TmElwwLzOxhBA/W/7kwW5FJ4MxmZuxT9q2caMS2jbGXKpW5P6UcIQ7fg2yLqyl8KgU4X5JnC61zeoF5QwyemXdegstq90V6Cn3MqRPeeN2A7HCOeU0O52YD4A
48
+ match(/DH1080_INIT (\S+)\s*(cbc|CBC)?/, use_prefix: false, react_on: :notice, method: :key_exchange)
49
+ def key_exchange(m, key, cbc = false)
50
+ return if m.channel?
57
51
 
58
- # not sure this is actually needed
59
- def encrypt(message)
60
- @fish.encrypt(message)
61
- end
52
+ debug "captured key exchange event with key: #{m.message}"
62
53
 
63
- def decrypt(message)
64
- @fish.decrypt(message)
65
- end
54
+ #TODO -- everything
55
+ end
66
56
 
67
- def blowfish(key)
68
- @fish = Cinch::Plugins::EnCinch::Encryption.new(key)
69
- end
57
+ def encrypt(message)
58
+ @fish.encrypt(message)
59
+ end
70
60
 
71
- private
61
+ def decrypt(message)
62
+ @fish.decrypt(message)
63
+ end
72
64
 
73
- def dispatch(msg)
74
- events = [[:catchall]]
65
+ def blowfish(key)
66
+ @fish = Encryption.new(key)
67
+ end
75
68
 
76
- if ["PRIVMSG", "NOTICE"].include?(msg.command)
77
- events << [:ctcp] if msg.ctcp?
69
+ def encrypted?(data)
70
+ !!data.match(/\+OK \S+/)
71
+ end
78
72
 
79
- if msg.channel?
80
- events << [:channel]
81
- else
82
- events << [:private]
83
- end
73
+ private
74
+ def dispatch(msg)
75
+ events = [[:catchall]]
84
76
 
85
- if msg.command == "PRIVMSG"
86
- events << [:message]
87
- else
88
- events << [:notice]
89
- end
77
+ if ["PRIVMSG", "NOTICE"].include?(msg.command)
78
+ events << [:ctcp] if msg.ctcp?
90
79
 
91
- if msg.action?
92
- events << [:action]
93
- end
80
+ if msg.channel?
81
+ events << [:channel]
82
+ else
83
+ events << [:private]
94
84
  end
95
85
 
96
- meth = "on_#{msg.command.downcase}"
97
- __send__(meth, msg, events) if respond_to?(meth, true)
86
+ if msg.command == "PRIVMSG"
87
+ events << [:message]
88
+ end
98
89
 
99
- if msg.error?
100
- events << [:error]
90
+ if msg.action?
91
+ events << [:action]
101
92
  end
93
+ end
102
94
 
103
- events << [msg.command.downcase.to_sym]
95
+ meth = "on_#{msg.command.downcase}"
96
+ __send__(meth, msg, events) if respond_to?(meth, true)
104
97
 
105
- msg.events = events
98
+ events << [:error] if msg.error?
106
99
 
107
- #if its still encrypted for whatever reason we will not be processing it again
108
- msg.events.each do |event, *args|
109
- msg.bot.handlers.dispatch(event, msg, *args)
110
- end unless encrypted?(msg.raw)
111
- end
100
+ events << [msg.command.downcase.intern]
112
101
 
113
- #
114
- # TODO: strip ctcp? others?
115
- #
102
+ msg.events = events
116
103
 
117
- def strip(m, data)
118
- data.sub!(/\u0001$/, '') if m.action?
119
-
120
- data
121
- end
104
+ #if its still encrypted for whatever reason we will not be processing it again
105
+ msg.events.each do |event, *args|
106
+ msg.bot.handlers.dispatch(event, msg, *args)
107
+ end unless encrypted?(msg.raw)
108
+ end
122
109
 
123
- def encrypted?(data)
124
- !!data.match(/\+OK \S+/)
125
- end
110
+ def strip(data)
111
+ data.sub(/\u0001$/, '')
112
+ end
126
113
 
127
- #
128
- # replace the encrypted message with the decrypted
129
- #
114
+ #
115
+ # replace the encrypted message with the decrypted
116
+ #
130
117
 
131
- def modify_raw(data, decrypted)
132
- data.sub!(/\+OK \S+(\s+)?(\S+)?/, decrypted)
133
- data
134
- end
118
+ def modify_raw(data, decrypted)
119
+ data.sub(/\+OK \S+(\s+\S+)?/, decrypted)
135
120
  end
121
+ end
136
122
  end
137
123
  end
@@ -3,14 +3,14 @@
3
3
  # https://github.com/cinchrb/cinch/tree/feature/fish
4
4
  # or from weechat's fish plugin
5
5
  # https://github.com/weechat/scripts/blob/master/ruby/weefish.rb
6
- #
6
+ #
7
7
 
8
8
  module Cinch
9
9
  module Plugins
10
10
  class EnCinch
11
11
  class Encryption
12
12
  module Base64
13
-
13
+
14
14
  Alphabet = "./0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".freeze
15
15
 
16
16
  def self.encode(data)
@@ -31,28 +31,28 @@ module Cinch
31
31
  end
32
32
  end
33
33
 
34
- return res
34
+ res
35
35
  end
36
36
 
37
37
  def self.decode(data)
38
38
  res = String.new
39
39
  data = data.dup.force_encoding("BINARY")
40
+
40
41
  data.chars.each_slice(12) do |slice|
41
42
  slice = slice.join
42
43
  left = right = 0
43
-
44
- slice[0..5].each_char.with_index do |p, i|
45
- right |= Alphabet.index(p) << (i * 6)
44
+ slice[0..5].each_char.with_index do |pi, i|
45
+ right |= Alphabet.index(pi) << (i * 6)
46
46
  end
47
47
 
48
- slice[6..11].each_char.with_index do |p, i|
49
- left |= Alphabet.index(p) << (i * 6)
48
+ slice[6..11].each_char.with_index do |pi, i|
49
+ left |= Alphabet.index(pi) << (i * 6)
50
50
  end
51
51
 
52
52
  res << [left, right].pack('L>L>')
53
53
  end
54
54
 
55
- return res
55
+ res
56
56
  end
57
57
  end
58
58
 
@@ -60,48 +60,45 @@ module Cinch
60
60
  def initialize(key)
61
61
  @blowfish = Crypt::Blowfish.new(key)
62
62
  end
63
-
64
- def encrypt(text)
65
- text = pad(text, 8)
63
+
64
+ def encrypt(data)
65
+ data = pad(data, 8)
66
66
  result = String.new
67
-
68
- num_block = text.length / 8
67
+
68
+ num_block = data.length / 8
69
69
  num_block.times do |n|
70
- block = text[n*8..(n+1)*8-1]
70
+ block = data[n * 8..(n + 1) * 8 - 1]
71
71
  enc = @blowfish.encrypt_block(block)
72
72
  result += Base64.encode(enc)
73
73
  end
74
-
75
- return "+OK " << result
74
+
75
+ "+OK " << result
76
76
  end
77
-
78
- def decrypt(text)
79
- return nil if not text.length % 12 == 0
80
-
77
+
78
+ def decrypt(data)
79
+ data.sub!(/^\+OK\s+/, '')
80
+
81
+ return nil if not data.length % 12 == 0
82
+
81
83
  result = String.new
82
-
83
- num_block = (text.length / 12).to_i
84
+
85
+ num_block = (data.length / 12).to_i
84
86
  num_block.times do |n|
85
- block = Base64.decode( text[n*12..(n+1)*12-1] )
87
+ block = Base64.decode( data[n*12..(n + 1)*12-1] )
86
88
  result += @blowfish.decrypt_block(block)
87
89
  end
88
-
89
- return result.gsub(/\0*$/, "")
90
- end
91
90
 
92
- def self.generate
93
- # generate a key for key exchange
91
+ result.gsub(/\0*$/, '')
94
92
  end
95
-
93
+
96
94
  private
97
-
98
- def pad(text, n=8)
99
- pad_num = n - (text.length % n)
100
- if pad_num > 0 and pad_num != n
101
- pad_num.times { text += 0.chr }
102
- end
103
95
 
104
- return text
96
+ def pad(data, n=8)
97
+ pad_num = n - (data.length % n)
98
+
99
+ pad_num.times { data += 0.chr } if pad_num > 0 and pad_num != n
100
+
101
+ data
105
102
  end
106
103
  end
107
104
  end
@@ -1,47 +1,46 @@
1
1
 
2
2
  module Cinch
3
- module Exceptions
4
-
5
- # error to raise for missing required plugin options
6
- class MissingRequiredPluginOptions < Generic
7
- end
8
- end
9
-
10
-
11
3
  class IRC
12
4
 
5
+ # alias instead of modify directly
6
+ alias :encinch_send :send
7
+
13
8
  # Send a message to the server.
14
9
  # @param [String] msg
15
10
  # @return [void]
16
11
  def send(msg)
17
-
18
- # TODO match and modify ACTION
19
12
  if msg.match(/(PRIVMSG|NOTICE)/) && !msg.match(/\+OK (\S+)/)
13
+ _, _, target, message = *msg.match(/(\S+) (\S+) :(.*)/m)
14
+ target.downcase!
20
15
 
21
- verb, target, *message = msg.split
22
- message = message.join(' ')[1..-1] rescue nil
16
+ unless message.empty?
17
+ # retrieve bot options
18
+ options = @bot.config.shared[:encinch].storage.data
23
19
 
24
- # retrieve bot options
25
- if (message && !message.empty?) && options = @bot.config.plugins.options[Cinch::Plugins::EnCinch]
20
+ # ignore if target is in the 'uncrypted' array
21
+ unless options[:uncrypted].include?(target)
26
22
 
27
- # key exists
28
- if key = (options[:encrypt][target.downcase] || options[:encrypt][:default])
23
+ # match ctcp? and action messages
24
+ if matched = message.match(/\001ACTION\s(.+)\001/)
25
+ message = matched[-1]
26
+ elsif message =~ /\001.+\001/
27
+ return encinch_send(msg)
28
+ end
29
29
 
30
- # ignore if target is in the 'uncrypted' array
31
- unless options[:uncrypted].include?(target.downcase)
30
+ # key exists
31
+ if key = (options[:encrypt][target] || options[:encrypt][:default])
32
32
 
33
- fish = Cinch::Plugins::EnCinch::Encryption.new(key)
34
- encrypted = fish.encrypt(message)
33
+ encrypted = Cinch::Plugins::EnCinch::Encryption.new(key).encrypt(message)
35
34
  msg.sub!(message, encrypted)
35
+
36
+ else
37
+ return if options[:drop]
36
38
  end
37
39
  end
38
40
  end
39
- end
41
+ end
40
42
 
41
- @queue.queue(msg)
43
+ encinch_send(msg)
42
44
  end
43
-
44
- private
45
-
46
45
  end
47
46
  end
@@ -0,0 +1,66 @@
1
+ module Cinch
2
+ module Plugins
3
+ class EnCinch
4
+ class Storage
5
+ include Cinch::Plugin
6
+
7
+ attr_reader :storage
8
+
9
+ def initialize(bot, data)
10
+ super(bot)
11
+
12
+ file = data.delete(:key_file) || 'keys/encinch.yml'
13
+ make_dirp(file)
14
+
15
+ @storage = ::Cinch::Storage.new(file, data)
16
+
17
+ @storage.data.merge!(data) do |key, x, y|
18
+ case x
19
+ when Hash
20
+ x.merge!(y || {})
21
+ when Array
22
+ x.concat(y || []).uniq
23
+ when NilClass
24
+ y
25
+ else
26
+ if y.nil? || y.empty?
27
+ x
28
+ else
29
+ y
30
+ end
31
+ end
32
+ end
33
+
34
+ @storage.data[:drop] ||= false
35
+
36
+ save
37
+ end
38
+
39
+ # listen to nick change and update
40
+
41
+ listen_to :nick
42
+ def listen(m)
43
+ if @storage.data[:encrypt][m.user.last_nick.downcase]
44
+ @storage.data[:encrypt][m.user.nick.downcase] = @storage.data[:encrypt].delete(m.user.last_nick.downcase)
45
+ save
46
+ end
47
+ end
48
+
49
+ #
50
+ # save keys in yaml
51
+ #
52
+ def save
53
+ synchronize(:encinch_storage_save) do
54
+ @storage.save
55
+ end
56
+ end
57
+
58
+ def make_dirp(file)
59
+ path = File.expand_path(File.dirname(file))
60
+ FileUtils.mkdir_p(path) unless Dir.exist?(path)
61
+ end
62
+ private :make_dirp
63
+ end
64
+ end
65
+ end
66
+ end
@@ -1,6 +1,7 @@
1
-
2
- class EnCinch
3
- class Version
4
- VERSION = "0.0.3"
1
+ module Cinch
2
+ module Plugins
3
+ class EnCinch
4
+ VERSION = "0.1.5"
5
+ end
5
6
  end
6
7
  end
@@ -0,0 +1,83 @@
1
+ require 'minitest/autorun'
2
+ require 'minitest/reporters'
3
+ require_relative '../lib/cinch/plugins/encinch'
4
+
5
+ reporter_options = { color: true, slow_count: 5 }
6
+ Minitest::Reporters.use! [Minitest::Reporters::DefaultReporter.new(reporter_options)]
7
+
8
+ if ENV["SIMPLECOV"]
9
+ begin
10
+ require 'simplecov'
11
+ SimpleCov.start
12
+ rescue LoadError
13
+ end
14
+ end
15
+
16
+ unless Object.const_defined? 'Cinch'
17
+ $:.unshift File.expand_path('../../lib', __FILE__)
18
+ require 'cinch'
19
+ end
20
+
21
+
22
+ class TestCase < MiniTest::Test
23
+ def self.test(name, &block)
24
+ define_method("test_" + name, &block) if block
25
+ end
26
+ end
27
+
28
+ class Bot
29
+ attr_accessor :ulist
30
+ attr_reader :message, :event
31
+
32
+ @@defined = false
33
+
34
+ def initialize(*)
35
+ @opts = Hash.new
36
+ @ulist = Array.new
37
+ @share = Hash.new
38
+ method_maker unless @@defined
39
+ end
40
+
41
+ def method_maker
42
+ [
43
+ :config, :plugins, :loggers, :handlers, :debug, :register,
44
+ :warn, :prefix, :suffix,:irc, :network, :user_list
45
+ ].each do |method|
46
+ self.class.send :define_method, method do |*|
47
+ self
48
+ end
49
+ end
50
+
51
+ @@defined = true
52
+ end
53
+
54
+ def isupport(*)
55
+ []
56
+ end
57
+
58
+ def find_ensured(*)
59
+ @ulist.first
60
+ end
61
+ private :method_maker
62
+
63
+ def options
64
+ @opts
65
+ end
66
+
67
+ def synchronize(name, &block)
68
+ yield block if block_given?
69
+ end
70
+
71
+ def shared
72
+ @share
73
+ end
74
+
75
+ def ngametv?
76
+ false
77
+ end
78
+
79
+ def dispatch(event, message, *args)
80
+ @event = event
81
+ @message = message
82
+ end
83
+ end
@@ -0,0 +1,100 @@
1
+ module Cinch
2
+ module Plugins
3
+ class EnCinch
4
+
5
+ def config
6
+ @bot.options[self.class]
7
+ end
8
+ end
9
+ end
10
+
11
+ class Message
12
+ def privmsg_channel_name(s)
13
+ nil
14
+ end
15
+ end
16
+ end
17
+
18
+ class EnCinchTest < TestCase
19
+ parallelize_me!
20
+
21
+ def setup
22
+ bot = Bot.new
23
+ bot.options[Cinch::Plugins::EnCinch] = { encrypt: Hash.new }
24
+ @encinch = Cinch::Plugins::EnCinch.new(bot)
25
+ @message = ":encrypt_me!~encrypt_me@192.168.1.1 PRIVMSG #encinch :hello"
26
+ @encrypted_message = ":encrypt_me!~encrypt_me@192.168.1.1 PRIVMSG #encinch :+OK 3hcMC.j7WrK."
27
+ @key = "thisisafishkey"
28
+ end
29
+
30
+ def test_plugin_options_retrieval
31
+ assert_equal @encinch.shared[:encinch].storage.data, { ignore: [], uncrypted:[], encrypt: {}, drop: false }
32
+ end
33
+
34
+ def test_encrypt_message
35
+ encrypted = @encinch.blowfish(@key).encrypt(@message)
36
+ assert_equal false, @encinch.encrypted?(@message)
37
+ assert_equal true, @encinch.encrypted?(encrypted)
38
+ end
39
+
40
+ def test_decrypt_message
41
+ encrypted = @encinch.blowfish(@key).encrypt(@message)
42
+ assert_equal false, @encinch.encrypted?(@message)
43
+ assert_equal true, @encinch.encrypted?(encrypted)
44
+
45
+ decrypted = @encinch.decrypt(encrypted)
46
+
47
+ assert_equal @message, decrypted
48
+ end
49
+
50
+ def test_add_user_to_ignore
51
+ @encinch.shared[:encinch].storage.data[:ignore] << "ignore_me"
52
+ assert_equal ["ignore_me"], @encinch.shared[:encinch].storage.data[:ignore]
53
+ end
54
+
55
+ def test_add_user_to_uncrypted
56
+ @encinch.shared[:encinch].storage.data[:uncrypted] << "plain_text"
57
+ assert_equal ["plain_text"], @encinch.shared[:encinch].storage.data[:uncrypted]
58
+ end
59
+
60
+ def test_add_user_to_encrypted
61
+ @encinch.shared[:encinch].storage.data[:encrypt]["encrypt_me"] = "myencryptionkey"
62
+ expected = { "encrypt_me" => "myencryptionkey" }
63
+ assert_equal expected, @encinch.shared[:encinch].storage.data[:encrypt]
64
+ end
65
+
66
+ def test_capture_ignore_user
67
+ @encinch.shared[:encinch].storage.data[:ignore] << ignore = "ignore_me"
68
+ encrypted_message = ":encrypt_me!~encrypt_me@192.168.1.1 PRIVMSG ignore_me :+OK 3hcMC.j7WrK."
69
+
70
+ user = Cinch::User.new(ignore, @encinch.bot)
71
+ @encinch.bot.ulist << user
72
+
73
+ message = Cinch::Message.new(encrypted_message, @encinch.bot)
74
+
75
+ result = @encinch.capture(message, message.message)
76
+
77
+ assert_nil result
78
+ end
79
+
80
+ def test_capture_no_key
81
+ encrypted_message = ":encrypt_me!~encrypt_me@192.168.1.1 PRIVMSG no_key :+OK 3hcMC.j7WrK."
82
+ user = Cinch::User.new("no_key", @encinch.bot)
83
+ @encinch.bot.ulist << user
84
+ message = Cinch::Message.new(encrypted_message, @encinch.bot)
85
+ result = @encinch.capture(message, message.message)
86
+
87
+ assert_nil result
88
+ end
89
+
90
+ def test_capture_user_encrypted_message
91
+ @encinch.shared[:encinch].storage.data[:encrypt]["encrypt_me"] = @key
92
+ user = Cinch::User.new("encrypt_me", @encinch.bot)
93
+ @encinch.bot.ulist << user
94
+
95
+ message = Cinch::Message.new(@encrypted_message, @encinch.bot)
96
+
97
+ @encinch.capture(message, message.message)
98
+ assert_equal "hello", @encinch.bot.message.message
99
+ end
100
+ end
@@ -0,0 +1,171 @@
1
+ module Cinch
2
+ class IRC
3
+ attr_reader :message
4
+ def encinch_send(msg)
5
+ @message = msg
6
+ end
7
+ end
8
+ end
9
+
10
+ class ExtendTest < TestCase
11
+ parallelize_me!
12
+
13
+ def setup
14
+ bot = Bot.new
15
+ bot.options[Cinch::Plugins::EnCinch] = { encrypt: Hash.new }
16
+ @irc = Cinch::IRC.new(bot)
17
+ @encinch = Cinch::Plugins::EnCinch.new(bot)
18
+ @message = "hello"
19
+ @channel = "PRIVMSG #encinch :"
20
+ @user = "PRIVMSG encinch :"
21
+ @key = "thisisafishkey"
22
+ @encrypted = @encinch.blowfish(@key).encrypt(@message)
23
+ @lines = ["Lorem ipsum dolor sit amet", "dicat admodum est cu", "ne ferri soleat dolorum usu.", "Mel cu aliquid docendi temporibus", "at pri odio congue interesset. Quot assentior vis te", "ius ad wisi placerat deserunt", "facete insolens pri ex. An duo tantas veritus. Augue commodo sed ei", "ius quod eruditi lobortis id", "ne epicurei consetetur vim. Quo no debitis electram deseruisse", "quis senserit ei vis."]
24
+ end
25
+
26
+ def test_send_encrypted_message_pass_through
27
+ @irc.send(@channel + @encrypted)
28
+ assert_equal @channel + @encrypted, @irc.message
29
+ end
30
+
31
+ def test_send_uncrypted_message_encrypt
32
+ @encinch.shared[:encinch].storage.data[:encrypt]["#encinch"] = @key
33
+ @irc.send(@channel + @message)
34
+ assert_equal @channel + @encrypted, @irc.message
35
+ end
36
+
37
+ def test_send_larger_chunks
38
+ @encinch.shared[:encinch].storage.data[:encrypt]["#encinch"] = @key
39
+ @lines.each do |line|
40
+ @irc.send(@channel + line)
41
+ crypted = @encinch.blowfish(@key).encrypt(line)
42
+ assert_equal @channel + crypted, @irc.message
43
+ end
44
+ end
45
+
46
+ def test_send_default_key
47
+ default = "thisisdefaultkey"
48
+ @encinch.shared[:encinch].storage.data[:encrypt][:default] = default
49
+
50
+ @lines.each do |line|
51
+ @irc.send(@channel + line)
52
+ crypted = @encinch.blowfish(default).encrypt(line)
53
+ assert_equal @channel + crypted, @irc.message
54
+ end
55
+ end
56
+
57
+ def test_send_uncrypted_message_bypass
58
+ @encinch.shared[:encinch].storage.data[:uncrypted] << '#encinch'
59
+ @irc.send(@channel + @message)
60
+ assert_equal @channel + @message, @irc.message
61
+ end
62
+
63
+ def test_send_user_default_key
64
+ default = "thisisdefaultkey"
65
+ @encinch.shared[:encinch].storage.data[:encrypt][:default] = default
66
+
67
+ @lines.each do |line|
68
+ @irc.send(@user + line)
69
+ crypted = @encinch.blowfish(default).encrypt(line)
70
+ assert_equal @user + crypted, @irc.message
71
+ end
72
+ end
73
+
74
+ def test_send_text_with_newline_character
75
+ string = "this is some text with \n a new line character"
76
+ @encinch.shared[:encinch].storage.data[:encrypt]["encinch"] = @key
77
+ @irc.send(@user + string)
78
+
79
+ crypted = @encinch.blowfish(@key).encrypt(string)
80
+
81
+ assert_equal @user + crypted, @irc.message
82
+ end
83
+
84
+ def test_send_text_with_return_character
85
+ string = "this is some text with \r a return character"
86
+ @encinch.shared[:encinch].storage.data[:encrypt]["encinch"] = @key
87
+ @irc.send(@user + string)
88
+
89
+ crypted = @encinch.blowfish(@key).encrypt(string)
90
+
91
+ assert_equal @user + crypted, @irc.message
92
+ end
93
+
94
+ def test_send_text_with_tab_character
95
+ string = "this is some text with \t a tab character"
96
+ @encinch.shared[:encinch].storage.data[:encrypt]["encinch"] = @key
97
+ @irc.send(@user + string)
98
+
99
+ crypted = @encinch.blowfish(@key).encrypt(string)
100
+
101
+ assert_equal @user + crypted, @irc.message
102
+ end
103
+
104
+ def test_send_text_with_newline_return_tab_characters
105
+ string = "this is some text with \t a tab character and a \r return character, and a \n newline character"
106
+ @encinch.shared[:encinch].storage.data[:encrypt]["encinch"] = @key
107
+ @irc.send(@user + string)
108
+
109
+ crypted = @encinch.blowfish(@key).encrypt(string)
110
+
111
+ assert_equal @user + crypted, @irc.message
112
+ end
113
+
114
+ def test_send_action_message
115
+ string = "this is some action text"
116
+ @encinch.shared[:encinch].storage.data[:encrypt]["encinch"] = @key
117
+ @irc.send("#{ @user }\u0001ACTION #{ string }\u0001")
118
+ crypted = @encinch.blowfish(@key).encrypt(string)
119
+
120
+ assert_equal "#{ @user }\u0001ACTION #{ crypted }\u0001", @irc.message
121
+ end
122
+
123
+ def test_send_finger_pass_through
124
+ @encinch.shared[:encinch].storage.data[:encrypt]["encinch"] = @key
125
+ @irc.send("#{ @user }\u0001FINGER\u0001")
126
+
127
+ assert_equal "#{ @user }\u0001FINGER\u0001", @irc.message
128
+ end
129
+
130
+ def test_send_empty_message
131
+ @encinch.shared[:encinch].storage.data[:encrypt]["encinch"] = @key
132
+ @irc.send(@user + String.new)
133
+
134
+ assert_equal @user + String.new, @irc.message
135
+ end
136
+
137
+ def test_send_long_empty_message
138
+ string = " "
139
+ @encinch.shared[:encinch].storage.data[:encrypt]["encinch"] = @key
140
+ @irc.send(@user + string)
141
+ crypted = @encinch.blowfish(@key).encrypt(string)
142
+
143
+ assert_equal @user + crypted, @irc.message
144
+ end
145
+
146
+ def test_send_drop_channel_message
147
+ @encinch.shared[:encinch].storage.data[:drop] = true
148
+ @irc.send(@channel + @message)
149
+ assert_nil @irc.message
150
+ end
151
+
152
+ def test_send_drop_user_message
153
+ @encinch.shared[:encinch].storage.data[:drop] = true
154
+ @irc.send(@user + @message)
155
+ assert_nil @irc.message
156
+ end
157
+
158
+ def test_send_allow_uncrypted_channel_no_drop
159
+ @encinch.shared[:encinch].storage.data[:drop] = true
160
+ @encinch.shared[:encinch].storage.data[:uncrypted] << '#encinch'
161
+ @irc.send(@channel + @message)
162
+ assert_equal @channel + @message, @irc.message
163
+ end
164
+
165
+ def test_send_allow_uncrypted_user_no_drop
166
+ @encinch.shared[:encinch].storage.data[:drop] = true
167
+ @encinch.shared[:encinch].storage.data[:uncrypted] << 'encinch'
168
+ @irc.send(@user + @message)
169
+ assert_equal @user + @message, @irc.message
170
+ end
171
+ end
@@ -0,0 +1,54 @@
1
+ class StorageTest < TestCase
2
+ def setup
3
+ bot = Bot.new
4
+ @options = {
5
+ ignore: Array.new,
6
+ uncrypted: Array.new,
7
+ encrypt: Hash.new
8
+ }
9
+
10
+ @storage = Cinch::Plugins::EnCinch::Storage.new(bot, @options)
11
+ end
12
+
13
+ def test_retrieve_options
14
+ @options[:drop] = false
15
+ assert_equal @options, options
16
+ end
17
+
18
+ def test_add_ignore_options
19
+ opts = options[:ignore]
20
+ assert_equal @options[:ignore], opts
21
+
22
+ opts << 'ignore_me'
23
+
24
+ assert_equal opts, options[:ignore]
25
+ end
26
+
27
+ def test_add_uncrypted_options
28
+ opts = options[:uncrypted]
29
+ assert_equal @options[:uncrypted], opts
30
+
31
+ opts << 'uncrypt_me'
32
+
33
+ assert_equal opts, options[:uncrypted]
34
+ end
35
+
36
+ def test_add_encryption_target
37
+ opts = options[:encrypt]
38
+
39
+ assert_equal @options[:encrypt], opts
40
+
41
+ opts[:default] = 'thisisafishkey'
42
+
43
+ assert_equal options[:encrypt], opts
44
+ end
45
+
46
+ def test_save_storage
47
+ assert_instance_of File, @storage.save
48
+ end
49
+
50
+ def options
51
+ @storage.storage.data
52
+ end
53
+ private :options
54
+ end
@@ -0,0 +1,11 @@
1
+ class VersionTest < TestCase
2
+ parallelize_me!
3
+
4
+ def test_version_should_be_a_string
5
+ assert_instance_of String, ::Cinch::Plugins::EnCinch::VERSION
6
+ end
7
+
8
+ def test_version_string_should_be_series_of_integers
9
+ refute_empty ::Cinch::Plugins::EnCinch::VERSION.scan(/\d+/).map(&:to_i)
10
+ end
11
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: encinch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - jfrazx
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-28 00:00:00.000000000 Z
11
+ date: 2016-05-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cinch
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: cinch-storage
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.2'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: crypt
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -38,6 +52,48 @@ dependencies:
38
52
  - - "~>"
39
53
  - !ruby/object:Gem::Version
40
54
  version: '2.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '11.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '11.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: minitest
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '5.8'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '5.8'
83
+ - !ruby/object:Gem::Dependency
84
+ name: minitest-reporters
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.1'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.1'
41
97
  description: ''
42
98
  email:
43
99
  - staringblind@gmail.com
@@ -48,12 +104,19 @@ files:
48
104
  - ".gitignore"
49
105
  - LICENSE.txt
50
106
  - README.md
107
+ - Rakefile
51
108
  - encinch.gemspec
52
109
  - lib/cinch/plugins/encinch.rb
53
110
  - lib/cinch/plugins/encinch/encinch.rb
54
111
  - lib/cinch/plugins/encinch/encryption.rb
55
112
  - lib/cinch/plugins/encinch/extend.rb
113
+ - lib/cinch/plugins/encinch/storage.rb
56
114
  - lib/cinch/plugins/encinch/version.rb
115
+ - test/helper.rb
116
+ - test/lib/encinch/encinch_spec.rb
117
+ - test/lib/encinch/extend_spec.rb
118
+ - test/lib/encinch/storage_spec.rb
119
+ - test/lib/encinch/version_spec.rb
57
120
  homepage: https://github.com/jfrazx/EnCinch
58
121
  licenses:
59
122
  - MIT
@@ -78,4 +141,10 @@ rubygems_version: 2.2.2
78
141
  signing_key:
79
142
  specification_version: 4
80
143
  summary: 'Transparent blowfish encryption plugin for Cinch: An IRC Bot Building Framework'
81
- test_files: []
144
+ test_files:
145
+ - test/helper.rb
146
+ - test/lib/encinch/encinch_spec.rb
147
+ - test/lib/encinch/extend_spec.rb
148
+ - test/lib/encinch/storage_spec.rb
149
+ - test/lib/encinch/version_spec.rb
150
+ has_rdoc: