blur 1.8.6 → 2.1.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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