blur 1.8.6 → 2.1.6

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.
@@ -1,71 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module Blur
4
- module Encryption
5
- # The +Encryption::Base64+ module differs from the original Base64
6
- # implementation. I'm not sure how exactly, perhaps the charset?
7
- #
8
- # I originally found the Ruby implementation of FiSH on a website where
9
- # it was graciously submitted by an anonymous user, since then I've
10
- # implemented it in a weechat script, and now I've refactored it for use in
11
- # Blur.
12
- #
13
- # @see http://maero.dk/pub/sources/weechat/ruby/autoload/fish.rb
14
- module Base64
15
- # The difference I suspect between the original Base64 implementation
16
- # and the one used in FiSH.
17
- Charset = "./0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
18
-
19
- # Decode a base64-encoded string.
20
- #
21
- # @return [String] Base64-decoded string.
22
- def self.decode string
23
- unless string.length % 12 == 0
24
- raise BadInputError, "input has to be a multiple of 12 characters."
25
- end
26
-
27
- String.new.tap do |buffer|
28
- j = -1
29
-
30
- while j < string.length - 1
31
- right, left = 0, 0
32
-
33
- 6.times{|i| right |= Charset.index(string[j += 1]) << (i * 6) }
34
- 6.times{|i| left |= Charset.index(string[j += 1]) << (i * 6) }
35
-
36
- 4.times do |i|
37
- buffer << ((left & (0xFF << ((3 - i) * 8))) >> ((3 - i) * 8)).chr
38
- end
39
-
40
- 4.times do |i|
41
- buffer << ((right & (0xFF << ((3 - i) * 8))) >> ((3 - i) * 8)).chr
42
- end
43
- end
44
- end
45
- end
46
-
47
- # Encode a string-cipher.
48
- #
49
- # @return [String] Base64-encoded string.
50
- def self.encode string
51
- unless string.length % 8 == 0
52
- raise BadInputError, "input has to be a multiple of 8 characters."
53
- end
54
-
55
- left = 0
56
- right = 0
57
- decimals = [24, 16, 8, 0]
58
-
59
- String.new.tap do |buffer|
60
- string.each_block do |block|
61
- 4.times{|i| left += (block[i].ord << decimals[i]) }
62
- 4.times{|i| right += (block[i+4].ord << decimals[i]) }
63
-
64
- 6.times{|i| buffer << Charset[right & 0x3F].chr; right = right >> 6 }
65
- 6.times{|i| buffer << Charset[left & 0x3F].chr; left = left >> 6 }
66
- end
67
- end
68
- end
69
- end
70
- end
71
- end
@@ -1,80 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'crypt/blowfish'
4
-
5
- module Blur
6
- module Encryption
7
- # The +FiSH+ algorithm is a combination of Base64 encoding
8
- # and the blowfish encryption.
9
- #
10
- # Shared text messages are prepended by "++OK+", an older implementation
11
- # prepends it with "+mcps+" - Blur drops support for that implementation.
12
- #
13
- # There's multiple client-implementations available on the official FiSH
14
- # homepage.
15
- #
16
- # == DH1080 Key exchange
17
- # The newer FiSH implementation introduces a 1080bit Diffie-Hellman
18
- # key-exchange mechanism.
19
- #
20
- # Blur does currently not support key exchanges.
21
- class FiSH
22
- # The standard FiSH block-size.
23
- BlockSize = 8
24
-
25
- # @return [String] the blowfish salt-key.
26
- attr_accessor :keyphrase
27
-
28
- # Change the keyphrase and instantiate a new blowfish object.
29
- def keyphrase= keyphrase
30
- @keyphrase = keyphrase
31
- @blowfish = Crypt::Blowfish.new @keyphrase
32
- end
33
-
34
- # Instantiate a new fish-encryption object.
35
- def initialize keyphrase
36
- @keyphrase = keyphrase
37
- @blowfish = Crypt::Blowfish.new keyphrase
38
- end
39
-
40
- # Encrypt an input string using the keyphrase stored in the +@blowfish+
41
- # object.
42
- #
43
- # @return [String] the encrypted string.
44
- def encrypt string
45
- String.new.tap do |buffer|
46
- nullpad(string).each_block do |block|
47
- chunk = @blowfish.encrypt_block block
48
- buffer.concat Base64.encode chunk
49
- end
50
- end
51
- end
52
-
53
- # Decrypt an input string using the keyphrase stored in the +@blowfish+
54
- # object.
55
- #
56
- # @return [String] the decrypted string.
57
- def decrypt string
58
- unless string.length % 12 == 0
59
- raise BadInputError, "input has to be a multiple of 12 characters."
60
- end
61
-
62
- String.new.tap do |buffer|
63
- string.each_block 12 do |block|
64
- chunk = @blowfish.decrypt_block Base64.decode block
65
- buffer.concat chunk
66
- end
67
- end.rstrip
68
- end
69
-
70
- private
71
- # Fill up the last block with null-bytes until it's a multiple of 8.
72
- #
73
- # @return [String] the nullpadded string.
74
- def nullpad string
75
- length = string.length + BlockSize - string.length % BlockSize
76
- string.ljust length, ?\0
77
- end
78
- end
79
- end
80
- end
@@ -1,17 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module Blur
4
- # The +Encryption+ module extends the communication-functionality of
5
- # the client. It is intended to enable helpers but also core functionality
6
- # that encrypts the incoming and outgoing data of Blur.
7
- #
8
- # Encryption modules are not loaded into the VM until it's required.
9
- module Encryption
10
- # Indicates that user-input (a channel user message, e.g.) is in invalid
11
- # format to a certain encryption algorithm.
12
- class BadInputError < StandardError; end
13
-
14
- autoload :FiSH, "blur/encryption/fish"
15
- autoload :Base64, "blur/encryption/base64"
16
- end
17
- end
@@ -1,41 +0,0 @@
1
- # encoding: utf-8
2
-
3
- # Reopens the scope of the standard Exception-class to extend it with helpful
4
- # methods.
5
- class Exception
6
- # The pattern to match against the backtrace log.
7
- Pattern = /^.*?:(\d+):/
8
-
9
- # Retrieve the line on which the exception was raised from when raised inside
10
- # a script.
11
- #
12
- # @return Fixnum the line of the script the exception was raised on.
13
- def line
14
- if result = backtrace[0].match(Pattern)
15
- result[1].to_i + 1
16
- end
17
- end
18
- end
19
-
20
- # Reopens the scope of the standard String-class to extend it with helpful
21
- # methods.
22
- class String
23
- # Checks if the string contains nothing but a numeric value.
24
- #
25
- # @return true if it is a numeric value.
26
- def numeric?
27
- self =~ /^\d+$/
28
- end
29
-
30
- # Split a string up in n chunks and then iterate through them, exactly like
31
- # Enumerable#each_slice.
32
- #
33
- # @return [Enumerator] list of slices.
34
- # @yieldreturn [Array] list of elements in each slice consecutively.
35
- def each_slice size = 8
36
- self.chars.each_slice(size).each{|slice| yield slice.join }
37
- end
38
-
39
- alias_method :starts_with?, :start_with?
40
- alias_method :each_block, :each_slice
41
- end
@@ -1,13 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module Blur
4
- module Evaluable
5
- # Evaluate the contents of the input file in the context of +self+.
6
- def evaluate_source_file path
7
- instance_eval File.read(path), File.basename(path), 0
8
- @__evaluated = true
9
- rescue Exception => exception
10
- puts "#{exception.message ^ :bold} on line #{exception.line.to_s ^ :bold}"
11
- end
12
- end
13
- end
@@ -1,42 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module Blur
4
- # +Extension+ provides a sort of modules for scripts, for common functionality.
5
- #
6
- # Think of it as a kind of scripts for scripts.
7
- class Extension
8
- include Evaluable
9
- include Script::DSL
10
- include Logging
11
-
12
- # @return the path in which the script remains.
13
- attr_accessor :__path
14
- # Can be used inside the script to act with the client itself.
15
- # @return [Network::Client] the client delegate.
16
- attr_accessor :__client
17
-
18
- # Instantiates a new extension context and evaluates the +path+ extension
19
- # file.
20
- def initialize path
21
- @__path = path
22
-
23
- if evaluate_source_file path
24
- log.info "Loaded extension #{@__path}"
25
- end
26
- end
27
-
28
- # Purely for DSL purposes.
29
- #
30
- # @example
31
- # Extension :http do
32
- # …
33
- # end
34
- def Extension name, &block
35
- @__name = name
36
-
37
- instance_eval &block
38
-
39
- true
40
- end
41
- end
42
- end
@@ -1,91 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module Blur
4
- class Network
5
- # The +Channel+ class is used for encapsulating a channel and its properties.
6
- #
7
- # Users inside the channel is stored in the {#channels} attribute.
8
- #
9
- # Modes can be set for a channel, but Blur is not
10
- # {http://www.irc.org/tech_docs/005.html ISupport}-compliant yet.
11
- #
12
- # @todo make so that channels *and* users belongs to the network, and not
13
- # like now where the user belongs to the channel, resulting in multiple
14
- # user instances.
15
- class Channel
16
- # @return [String] the channels name.
17
- attr_accessor :name
18
- # @return [Array] a list of users in the channel.
19
- attr_accessor :users
20
- # @return [String] the channels topic.
21
- attr_accessor :topic
22
- # @return [String] all the modes set on the channel.
23
- attr_accessor :modes
24
- # @return [Network] a reference to the network.
25
- attr_accessor :network
26
- # @return [Encryption::Fish] the channel encryption, if any.
27
- attr_accessor :encryption
28
-
29
- # Check whether or not this is an encrypted channel.
30
- def encrypted?; not @encryption.nil? end
31
-
32
- # Instantiate a user with a nickname, a network and a user list.
33
- def initialize name, network = nil, users = []
34
- @name = name
35
- @users = users
36
- @modes = ""
37
- @network = network
38
-
39
- users.each { |user| user.channel = self }
40
- end
41
-
42
- # Merge the channels mode corresponding to the leading character (+ or -).
43
- #
44
- # @param [String] modes the modes to merge with.
45
- def merge_modes modes
46
- addition = true
47
-
48
- modes.each_char do |char|
49
- case char
50
- when ?+
51
- addition = true
52
- when ?-
53
- addition = false
54
- else
55
- addition ? @modes.concat(char) : @modes.delete!(char)
56
- end
57
- end
58
- end
59
-
60
- # Send a message to the channel.
61
- #
62
- # @param [String] message the message to send.
63
- def say message
64
- @network.say self, message
65
- end
66
-
67
- # Find a user with +nick+ as its nickname.
68
- #
69
- # @param [String] nick the nickname to find the user of.
70
- def user_by_nick nick
71
- @users.find { |user| user.nick == nick }
72
- end
73
-
74
- # Convert it to a debug-friendly format.
75
- def inspect
76
- %{#<#{self.class.name} @name=#{@name.inspect} @users=#{@users.inspect}}
77
- end
78
-
79
- # Called when YAML attempts to save the object, which happens when a
80
- # scripts cache contains this user and the script is unloaded.
81
- def to_yaml options = {}
82
- @name.to_yaml options
83
- end
84
-
85
- # Get the channels name.
86
- def to_s
87
- @name
88
- end
89
- end
90
- end
91
- end
@@ -1,83 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module Blur
4
- class Network
5
- # The +Command+ class is used for encapsulating the command-lines.
6
- #
7
- # Blur is using regular-expression for parsing, this is to be replaced
8
- # with a more native way of parsing, making it much, much easier on the
9
- # processor.
10
- class Command
11
- # @return [Symbol, Fixnum] the command name.
12
- # @example
13
- # 332 or :quit
14
- attr_accessor :name
15
- # @return [Array] a list of parameters.
16
- attr_accessor :params
17
- # @return [String] a hostname or a hostmask.
18
- attr_accessor :prefix
19
-
20
- # The arbitrary regex pattern.
21
- Pattern = /^(?:[:@]([^\s]+) )?([^\s]+)(?: ((?:[^:\s][^\s]* ?)*))?(?: ?:(.*))?$/
22
-
23
- # Parse a line and encapsulate it as a Command.
24
- #
25
- # @return [Command] the parsed command.
26
- # @example
27
- # Command.parse "ChanServ!ChanServ@services.uplink.io MODE #uplink +v mk"
28
- # # => #<Blur::Network::Command … >
29
- def self.parse data
30
- match = data.strip.match Pattern
31
- prefix, name, args, extra = match.captures
32
- params = extra ? args.split << extra : args.split
33
-
34
- new(name, params).tap do |this|
35
- this.prefix = prefix
36
- end
37
- end
38
-
39
- # Get a parameter by its +index+.
40
- def [] index; @params[index] end
41
-
42
- # Instantiate a command.
43
- #
44
- # @see Command.parse
45
- def initialize name, params = []
46
- @name, @params = name, params
47
- end
48
-
49
- # Get the sender of the command.
50
- #
51
- # @note the return value is a string if it's a hostname, and an openstruct
52
- # with the attributes #nickname, #username and #hostname if it's a
53
- # hostmask.
54
- #
55
- # @return [String, OpenStruct] the sender.
56
- def sender
57
- return @sender if @sender
58
-
59
- if prefix =~ /^(\S+)!(\S+)@(\S+)$/
60
- @sender = OpenStruct.new nickname: $1, username: $2, hostname: $3
61
- else
62
- @sender = prefix
63
- end
64
- end
65
-
66
- # Convert it to an IRC-compliant line.
67
- #
68
- # @return [String] the command line.
69
- def to_s
70
- String.new.tap do |line|
71
- line << "#{prefix} " if prefix
72
- line << name.to_s
73
-
74
- params.each_with_index do |param, index|
75
- line << ' '
76
- line << ?: if index == params.length - 1 and param =~ /[ :]/
77
- line << param.to_s
78
- end
79
- end
80
- end
81
- end
82
- end
83
- end
@@ -1,106 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module Blur
4
- class Network
5
- # The +User+ class is used for encapsulating a user and its properties.
6
- #
7
- # The user owns a reference to its parent channel.
8
- #
9
- # Modes can be set for a user, but Blur is not
10
- # {http://www.irc.org/tech_docs/005.html ISupport}-compliant yet.
11
- #
12
- # @todo make so that channels *and* users belongs to the network, and not
13
- # like now where the user belongs to the channel, resulting in multiple
14
- # user instances.
15
- class User
16
- # @return [String] the users nickname.
17
- attr_accessor :nick
18
- # @return [String] the users username.
19
- attr_accessor :name
20
- # @return [String] the users hostname.
21
- attr_accessor :host
22
- # @return [String] all the modes set on the user.
23
- attr_accessor :modes
24
- # @return [Channel] a reference to the users channel.
25
- attr_accessor :channel
26
- # @return [Network] a reference to the network.
27
- attr_accessor :network
28
-
29
- # Check to see if the user is an admin (+a)
30
- def admin?; @modes.include? "a" end
31
- # Check to see if the user has voice (+v)
32
- def voice?; @modes.include? "v" end
33
- # Check to see if the user is the owner (+q)
34
- def owner?; @modes.include? "q" end
35
- # Check to see if the user is an operator (+o)
36
- def operator?; @modes.include? "o" end
37
- # Check to see if the user is an half-operator (+h)
38
- def half_operator?; @modes.include? "h" end
39
-
40
- # Instantiate a user with a nickname.
41
- def initialize nick
42
- @nick = nick
43
- @modes = ""
44
-
45
- if modes = prefix_to_mode(nick[0])
46
- @nick = nick[1..-1]
47
- @modes = modes
48
- end
49
- end
50
-
51
- # Merge the users mode corresponding to the leading character (+ or -).
52
- #
53
- # @param [String] modes the modes to merge with.
54
- def merge_modes modes
55
- addition = true
56
-
57
- modes.each_char do |char|
58
- case char
59
- when ?+
60
- addition = true
61
- when ?-
62
- addition = false
63
- else
64
- addition ? @modes.concat(char) : @modes.delete!(char)
65
- end
66
- end
67
- end
68
-
69
- # Send a private message to the user.
70
- #
71
- # @param [String] message the message to send.
72
- def say message
73
- @network.say self, message
74
- end
75
-
76
- # Convert it to a debug-friendly format.
77
- def inspect
78
- %{#<#{self.class.name} @nick=#{@nick.inspect} @channel=#{@channel.name.inspect}>}
79
- end
80
-
81
- # Called when YAML attempts to save the object, which happens when a
82
- # scripts cache contains this user and the script is unloaded.
83
- def to_yaml options = {}
84
- @nick.to_yaml options
85
- end
86
-
87
- # Get the users nickname.
88
- def to_s
89
- @nick
90
- end
91
-
92
- private
93
-
94
- # Translate a nickname-prefix to a mode character.
95
- def prefix_to_mode prefix
96
- case prefix
97
- when '@' then 'o'
98
- when '+' then 'v'
99
- when '%' then 'h'
100
- when '&' then 'a'
101
- when '~' then 'q'
102
- end
103
- end
104
- end
105
- end
106
- end
@@ -1,77 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module Blur
4
- class Script
5
- # The +Cache+ class enables data storing inside Blur and it scripts.
6
- #
7
- # What it does is simply store a hash, and act like it is that hash.
8
- #
9
- # When the client closes, it sends a message to all available scripts
10
- # and then those scripts tells the cache to save, in order to remember
11
- # that data and reload it at the next run.
12
- #
13
- # Cache can then save the contents of the hash to a yaml file that persists
14
- # in the ./cache/ directory.
15
- #
16
- # That same file is then loaded once needed again.
17
- class Cache
18
- # Get the path to the cache directory (./cache/)
19
- def self.path
20
- %{#{File.dirname File.expand_path $0}/cache}
21
- end
22
-
23
- # Check if there exists a cache file for the script with name +name+.
24
- def self.exists? name
25
- File.exists? "#{path}/#{name}.yml"
26
- end
27
-
28
- # Get a cache value by key.
29
- def [] key; @hash[key] end
30
-
31
- # Set a cache value by key.
32
- def []= key, value; @hash[key] = value end
33
-
34
- # Instantiate a cache with a script reference.
35
- def initialize script
36
- @hash = {}
37
- @script = script
38
- end
39
-
40
- # Save all internal data to a yaml file in the cache directory.
41
- def save
42
- directory = File.dirname path
43
-
44
- unless File.directory? directory
45
- Dir.mkdir directory
46
- end
47
-
48
- File.open path, ?w do |file|
49
- YAML.dump @hash, file
50
- end
51
- end
52
-
53
- # Load a yaml file as internal data from the cache directory.
54
- #
55
- # @return [Hash] the loaded data.
56
- def load
57
- if yaml = YAML.load_file(path)
58
- @hash = yaml
59
- end
60
- rescue
61
- File.unlink path
62
- end
63
-
64
- # Let Hash#to_s do the job.
65
- def to_s; @hash end
66
-
67
- private
68
-
69
- # The current caches file path.
70
- #
71
- # @return [String] the file path.
72
- def path
73
- %{#{Cache.path}/#{@script.__name}.yml}
74
- end
75
- end
76
- end
77
- end
@@ -1,77 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module Blur
4
- class Script
5
- # The +Commands+ module is a module that gives the ability to turn a
6
- # script into a DSL-like framework.
7
- module Commands
8
- class Command
9
- DefaultOptions = { prefix: '.', hostmask: nil }
10
-
11
- def initialize triggers, options = {}, &block
12
- @triggers = Array === triggers ? triggers : [triggers]
13
- @options = DefaultOptions.merge options
14
- @block = block
15
- end
16
-
17
- # Called by the Commands module.
18
- #
19
- # Calls the command block if the trigger matches the criteria.
20
- def received_message user, channel, message
21
- prefix = @options[:prefix]
22
-
23
- # Return if the prefix don't match.
24
- return unless message.start_with? prefix
25
-
26
- # Return if the hostmask don't match.
27
- # FIXME: Maybe use globbing instead of regular expressions?
28
- unless @options[:hostmask].nil?
29
- hostmask = "#{user.nick}!#{user.name}@#{user.host}"
30
-
31
- return unless hostmask =~ @options[:hostmask]
32
- end
33
-
34
- command, args = split_message message
35
-
36
- # Strip the prefix and compare the trigger name.
37
- if self.matches_trigger? command
38
- @block.call user, channel, args
39
- end
40
- end
41
-
42
- protected
43
-
44
- def split_message message
45
- prefix = @options[:prefix]
46
- command, args = message[prefix.length..-1].split $;, 2
47
-
48
- return command, args
49
- end
50
-
51
- def matches_trigger? command
52
- return @triggers.find{|trigger| trigger.to_s == command }
53
- end
54
- end
55
-
56
- # Extend +base+ with self.
57
- def self.extended base
58
- base.instance_variable_set :@__commands, []
59
- end
60
-
61
- # Add a new command handler and trigger.
62
- def command name, *args, &block
63
- @__commands << Command.new(name, *args, &block)
64
- end
65
-
66
- # Handle all calls to the scripts +message+ method, check to see if
67
- # the message containts a valid command, serialize it and pass it to
68
- # the script as command_name with the parameters +user+, +channel+
69
- # and +message+.
70
- def message user, channel, line
71
- @__commands.each do |command|
72
- command.received_message user, channel, line
73
- end
74
- end
75
- end
76
- end
77
- end