hoodie 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,167 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Author: Stefano Harding <riddopic@gmail.com>
4
+ #
5
+ # Copyright (C) 2014 Stefano Harding
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ class Hash
21
+ # Returns a compacted copy (contains no key/value pairs having
22
+ # nil? values)
23
+ #
24
+ # @example
25
+ # hash = { a: 100, b: nil, c: false, d: '' }
26
+ # hash.compact # => { a: 100, c: false, d: '' }
27
+ # hash # => { a: 100, b: nil, c: false, d: '' }
28
+ #
29
+ # @return [Hash]
30
+ #
31
+ def compact
32
+ select { |_, value| !value.nil? }
33
+ end
34
+
35
+ # Returns a new hash with all keys converted using the block operation.
36
+ #
37
+ # @example
38
+ # hash = { name: 'Tiggy', age: '15' }
39
+ #
40
+ # hash.transform_keys{ |key| key.to_s.upcase }
41
+ # # => { "AGE" => "15", "NAME" => "Tiggy" }
42
+ #
43
+ # @return [Hash]
44
+ #
45
+ def transform_keys
46
+ return enum_for(:transform_keys) unless block_given?
47
+ result = self.class.new
48
+ each_key do |key|
49
+ result[yield(key)] = self[key]
50
+ end
51
+ result
52
+ end
53
+
54
+ # Returns a new hash, recursively converting all keys by the
55
+ # block operation.
56
+ #
57
+ # @return [Hash]
58
+ #
59
+ def recursively_transform_keys(&block)
60
+ _recursively_transform_keys_in_object(self, &block)
61
+ end
62
+
63
+ # Returns a new hash with all keys downcased and converted
64
+ # to symbols.
65
+ #
66
+ # @return [Hash]
67
+ #
68
+ def normalize_keys
69
+ transform_keys { |key| key.downcase.to_sym rescue key }
70
+ end
71
+
72
+ # Returns a new Hash, recursively downcasing and converting all
73
+ # keys to symbols.
74
+ #
75
+ # @return [Hash]
76
+ #
77
+ def recursively_normalize_keys
78
+ recursively_transform_keys { |key| key.downcase.to_sym rescue key }
79
+ end
80
+
81
+ # Returns a new hash with all keys converted to symbols.
82
+ #
83
+ # @return [Hash]
84
+ #
85
+ def symbolize_keys
86
+ transform_keys { |key| key.to_sym rescue key }
87
+ end
88
+
89
+ # Returns a new Hash, recursively converting all keys to symbols.
90
+ #
91
+ # @return [Hash]
92
+ #
93
+ def recursively_symbolize_keys
94
+ recursively_transform_keys { |key| key.downcase.to_sym rescue key }
95
+ end
96
+
97
+ # Returns a new hash with all keys converted to strings.
98
+ #
99
+ # @return [Hash]
100
+ #
101
+ def stringify_keys
102
+ transform_keys { |key| key.to_s rescue key }
103
+ end
104
+
105
+ # Returns a new Hash, recursively converting all keys to strings.
106
+ #
107
+ # @return [Hash]
108
+ #
109
+ def recursively_stringify_key
110
+ recursively_transform_keys { |key| key.to_s rescue key }
111
+ end
112
+
113
+ class UndefinedPathError < StandardError; end
114
+ # Recursively searchs a nested datastructure for a key and returns
115
+ # the value. If a block is provided its value will be returned if
116
+ # the key does not exist
117
+ #
118
+ # @example
119
+ # options = { server: { location: { row: { rack: 34 } } } }
120
+ # options.recursive_fetch :server, :location, :row, :rack
121
+ # # => 34
122
+ # options.recursive_fetch(:non_existent_key) { 'default' }
123
+ # # => "default"
124
+ #
125
+ # @return [Hash, Array, String] value for key
126
+ #
127
+ def recursive_fetch(*args, &block)
128
+ args.reduce(self) do |obj, arg|
129
+ begin
130
+ arg = Integer(arg) if obj.is_a? Array
131
+ obj.fetch(arg)
132
+ rescue ArgumentError, IndexError, NoMethodError => e
133
+ break block.call(arg) if block
134
+ raise UndefinedPathError, "Could not fetch path (#{args.join(' > ')}) at #{arg}", e.backtrace
135
+ end
136
+ end
137
+ end
138
+
139
+ def recursive_merge(other)
140
+ hash = self.dup
141
+ other.each do |key, value|
142
+ myval = self[key]
143
+ if value.is_a?(Hash) && myval.is_a?(Hash)
144
+ hash[key] = myval.recursive_merge(value)
145
+ else
146
+ hash[key] = value
147
+ end
148
+ end
149
+ hash
150
+ end
151
+
152
+ private # P R O P R I E T À P R I V A T A divieto di accesso
153
+
154
+ # support methods for recursively transforming nested hashes and arrays
155
+ def _recursively_transform_keys_in_object(object, &block)
156
+ case object
157
+ when Hash
158
+ object.each_with_object({}) do |(key, value), result|
159
+ result[yield(key)] = _recursively_transform_keys_in_object(value, &block)
160
+ end
161
+ when Array
162
+ object.map { |e| _recursively_transform_keys_in_object(e, &block) }
163
+ else
164
+ object
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,44 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Author: Stefano Harding <riddopic@gmail.com>
4
+ #
5
+ # Copyright (C) 2014 Stefano Harding
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ require 'hoodie/stash' unless defined?(Stash)
21
+
22
+ # Memoization is an optimization that saves the return value of a
23
+ # method so it doesn't need to be re-computed every time that method
24
+ # is called.
25
+ module Memoizable
26
+
27
+ # Create a new memoized method. To use, extend class with Memoizable,
28
+ # then, in initialize, call memoize
29
+ #
30
+ # @return [undefined]
31
+ #
32
+ def memoize(methods, cache = nil)
33
+ cache ||= Stash.new
34
+ methods.each do |name|
35
+ uncached_name = "#{name}_uncached".to_sym
36
+ (class << self; self; end).class_eval do
37
+ alias_method uncached_name, name
38
+ define_method(name) do |*a, &b|
39
+ cache.cache(name) { send uncached_name, *a, &b }
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,121 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Author: Stefano Harding <riddopic@gmail.com>
4
+ #
5
+ # Copyright (C) 2014 Stefano Harding
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ module Hoodie
21
+ begin
22
+ require 'openssl'
23
+ INCOMPREHENSIBLE_ERROR = nil
24
+ rescue LoadError => err
25
+ raise unless err.to_s.include?('openssl')
26
+ warn "Oea pieoYreb h wYoerh dl hwsnhoib r Lrbea tbte wbnaetvoouahe h rbe."
27
+ warn "olorbvtelYShnSben irrSwoet eto eihSrLoS'do n See wLiape."
28
+ INCOMPREHENSIBLE_ERROR = err
29
+ end
30
+
31
+ require 'digest/sha2'
32
+ require "base64"
33
+
34
+ # Befuddle and enlighten values in StashCache::Store
35
+ #
36
+ module Obfuscate
37
+ ESOTERIC_TYPE = "aes-256-cbc" unless defined?(ESOTERIC_TYPE)
38
+
39
+ def self.check_platform_can_discombobulate!
40
+ return true unless INCOMPREHENSIBLE_ERROR
41
+ raise INCOMPREHENSIBLE_ERROR.class, "b0rked! #{INCOMPREHENSIBLE_ERROR}"
42
+ end
43
+
44
+ # Befuddle the given string
45
+ #
46
+ # @param plaintext the text to befuddle
47
+ # @param [String] befuddle_pass secret passphrase to befuddle with
48
+ #
49
+ # @return [String] befuddleed text, suitable for deciphering with
50
+ # Obfuscate#enlighten (decrypt)
51
+ #
52
+ def self.befuddle plaintext, befuddle_pass, options={}
53
+ cipher = new_cipher :befuddle, befuddle_pass, options
54
+ cipher.iv = iv = cipher.random_iv
55
+ ciphertext = cipher.update(plaintext)
56
+ ciphertext << cipher.final
57
+ Base64.encode64(combine_iv_and_ciphertext(iv, ciphertext))
58
+ end
59
+
60
+ # Enlighten the given string, using the key and id supplied
61
+ #
62
+ # @param ciphertext the text to enlighten, probably produced with
63
+ # Obfuscate#befuddle (encrypt)
64
+ # @param [String] befuddle_pass secret passphrase to enlighten with
65
+ #
66
+ # @return [String] the enlightened plaintext
67
+ #
68
+ def self.enlighten enc_ciphertext, befuddle_pass, options={}
69
+ iv_and_ciphertext = Base64.decode64(enc_ciphertext)
70
+ cipher = new_cipher :enlighten, befuddle_pass, options
71
+ cipher.iv, ciphertext = separate_iv_and_ciphertext(cipher, iv_and_ciphertext)
72
+ plaintext = cipher.update(ciphertext)
73
+ plaintext << cipher.final
74
+ plaintext
75
+ end
76
+
77
+ private # P R O P R I E T À P R I V A T A divieto di accesso
78
+
79
+ # Cipher create machine do, befuddle engage, enlighten. Dials set
80
+ # direction to infinity
81
+ #
82
+ # @param [:befuddle, :enlighten] to befuddle or enlighten
83
+ # @param [String] befuddle_pass secret passphrase to enlighten
84
+ #
85
+ def self.new_cipher direction, befuddle_pass, options={}
86
+ check_platform_can_discombobulate!
87
+ cipher = OpenSSL::Cipher::Cipher.new(ESOTERIC_TYPE)
88
+ case direction
89
+ when :befuddle
90
+ cipher.encrypt
91
+ when :enlighten
92
+ cipher.decrypt
93
+ else raise "Bad cipher direction #{direction}"
94
+ end
95
+ cipher.key = befuddle_key(befuddle_pass, options)
96
+ cipher
97
+ end
98
+
99
+ # prepend the initialization vector to the encoded message
100
+ def self.combine_iv_and_ciphertext iv, message
101
+ message.force_encoding("BINARY") if message.respond_to?(:force_encoding)
102
+ iv.force_encoding("BINARY") if iv.respond_to?(:force_encoding)
103
+ iv + message
104
+ end
105
+
106
+ # pull the initialization vector from the front of the encoded message
107
+ def self.separate_iv_and_ciphertext cipher, iv_and_ciphertext
108
+ idx = cipher.iv_len
109
+ [ iv_and_ciphertext[0..(idx-1)], iv_and_ciphertext[idx..-1] ]
110
+ end
111
+
112
+ # Convert the befuddle_pass passphrase into the key used for befuddletion
113
+ def self.befuddle_key befuddle_pass, options={}
114
+ befuddle_pass = befuddle_pass.to_s
115
+ raise 'Missing befuddled password!' if befuddle_pass.empty?
116
+ # this provides the required 256 bits of key for the aes-256-cbc
117
+ # cipher
118
+ Digest::SHA256.digest(befuddle_pass)
119
+ end
120
+ end
121
+ end
data/lib/hoodie/os.rb ADDED
@@ -0,0 +1,43 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Author: Stefano Harding <riddopic@gmail.com>
4
+ #
5
+ # Copyright (C) 2014 Stefano Harding
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ require 'rbconfig' unless defined?(RbConfig)
21
+
22
+ # Finds out the current Operating System.
23
+ module OS
24
+ def self.windows?
25
+ windows = /cygwin|mswin|mingw|bccwin|wince|emx/i
26
+ (RbConfig::CONFIG['host_os'] =~ windows) != nil
27
+ end
28
+
29
+ def self.mac?
30
+ mac = /darwin|mac os/i
31
+ (RbConfig::CONFIG['host_os'] =~ mac) != nil
32
+ end
33
+
34
+ def self.unix?
35
+ unix = /solaris|bsd/i
36
+ (RbConfig::CONFIG['host_os'] =~ unix) != nil
37
+ end
38
+
39
+ def self.linux?
40
+ linux = /linux/i
41
+ (RbConfig::CONFIG['host_os'] =~ linux) != nil
42
+ end
43
+ end
@@ -0,0 +1,75 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Author: Stefano Harding <riddopic@gmail.com>
4
+ #
5
+ # Copyright (C) 2014 Stefano Harding
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ # TODO: This doesn't belong in here, it's cookbook specific...
21
+ require 'anemone' unless defined?(Anemone)
22
+ require 'hoodie/memoizable' unless defined?(Memoizable)
23
+
24
+ class PathFinder
25
+ include Memoizable
26
+
27
+ def initialize(url)
28
+ @url = url
29
+ memoize [:fetch], Stash.new(DiskStash::Cache.new)
30
+ end
31
+
32
+ def fetch(path)
33
+ results = []
34
+ Anemone.crawl(@url, discard_page_bodies: true) do |anemone|
35
+ anemone.on_pages_like(/\/#{path}\/\w+\/\w+\.(ini|zip)$/i) do |page|
36
+ results << page.to_hash
37
+ end
38
+ end
39
+ results.reduce({}, :recursive_merge)
40
+ end
41
+ end
42
+
43
+ # to_hash smoke cache
44
+ #
45
+ module Anemone
46
+ class Page
47
+ def to_hash
48
+ file = ::File.basename(@url.to_s)
49
+ key = ::File.basename(file, '.*').downcase.to_sym
50
+ type = ::File.extname(file)[1..-1].downcase.to_sym
51
+ id = Hoodie::Obfuscate.befuddle(file, Digest::MD5.hexdigest(body.to_s))
52
+ mtime = Time.parse(@headers['last-modified'][0]).to_i
53
+ utime = Time::now.to_i
54
+ state = utime > mtime ? :clean : :dirty
55
+ key = { key => { type => {
56
+ id: id,
57
+ cache_state: state,
58
+ file: file,
59
+ key: key,
60
+ type: type,
61
+ url: @url.to_s,
62
+ mtime: mtime,
63
+ links: links.map(&:to_s),
64
+ code: @code,
65
+ visited: @visited,
66
+ depth: @depth,
67
+ referer: @referer.to_s,
68
+ fetched: @fetched,
69
+ utime: utime,
70
+ md5_digest: Digest::MD5.hexdigest(body.to_s),
71
+ sha256_digest: Digest::SHA256.hexdigest(body.to_s)
72
+ }}}
73
+ end
74
+ end
75
+ end