Class: SPF::MacroString
- Inherits:
-
Object
- Object
- SPF::MacroString
- Defined in:
- lib/spf/macro_string.rb
Instance Attribute Summary collapse
-
#request ⇒ Object
readonly
Returns the value of attribute request.
-
#server ⇒ Object
readonly
Returns the value of attribute server.
-
#text ⇒ Object
readonly
Returns the value of attribute text.
Class Method Summary collapse
Instance Method Summary collapse
- #context(server, request) ⇒ Object
- #expand(context = nil) ⇒ Object
-
#initialize(options = {}) ⇒ MacroString
constructor
A new instance of MacroString.
- #to_s ⇒ Object
- #valid_context(required, server = self.server, request = self.request) ⇒ Object
Constructor Details
#initialize(options = {}) ⇒ MacroString
Returns a new instance of MacroString.
22 23 24 25 26 27 28 29 30 |
# File 'lib/spf/macro_string.rb', line 22 def initialize( = {}) super() @text = [:text] \ or raise ArgumentError, "Missing required 'text' option" @server = [:server] @request = [:request] @is_explanation = [:is_explanation] @expanded = nil end |
Instance Attribute Details
#request ⇒ Object (readonly)
Returns the value of attribute request.
32 33 34 |
# File 'lib/spf/macro_string.rb', line 32 def request @request end |
#server ⇒ Object (readonly)
Returns the value of attribute server.
32 33 34 |
# File 'lib/spf/macro_string.rb', line 32 def server @server end |
#text ⇒ Object (readonly)
Returns the value of attribute text.
32 33 34 |
# File 'lib/spf/macro_string.rb', line 32 def text @text end |
Class Method Details
.default_join_delimiter ⇒ Object
14 15 16 |
# File 'lib/spf/macro_string.rb', line 14 def self.default_join_delimiter '.' end |
.default_split_delimiters ⇒ Object
10 11 12 |
# File 'lib/spf/macro_string.rb', line 10 def self.default_split_delimiters '.' end |
.uri_unreserved_chars ⇒ Object
18 19 20 |
# File 'lib/spf/macro_string.rb', line 18 def self.uri_unreserved_chars 'A-Za-z0-9\-._~' end |
Instance Method Details
#context(server, request) ⇒ Object
34 35 36 37 38 39 40 |
# File 'lib/spf/macro_string.rb', line 34 def context(server, request) valid_context(true, server, request) @server = server @request = request @expanded = nil return end |
#expand(context = nil) ⇒ Object
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/spf/macro_string.rb', line 42 def (context = nil) return @expanded if @expanded return nil unless @text return (@expanded = @text) unless @text =~ /%/ # Short-circuit expansion if text has no '%' characters. server, request = context ? context : [@server, @request] valid_context(true, server, request) = '' text = @text while m = text.match(/ (.*?) %(.) /x) do += m[1] key = m[2] if (key == '{') if m2 = m.post_match.match(/ (\w|_\p{Alpha}+) ([0-9]+)? (r)? ([.\-+,\/_=])? } /x) char, rh_parts, reverse, delimiter = m2.captures # Upper-case macro chars trigger URL-escaping AKA percent-encoding # (RFC 4408, 8.1/26): do_percent_encode = char =~ /\p{Upper}/ char.downcase! if char == 's' # RFC 4408, 8.1/19 value = request.identity elsif char == 'l' # RFC 4408, 8.1/19 value = request.localpart elsif char == 'o' # RFC 4408, 8.1/19 value = request.domain elsif char == 'd' # RFC 4408, 8.1/6/4 value = request. elsif char == 'i' # RFC 4408, 8.1/20, 8.1/21 ip_address = request.ip_address ip_address = SPF::Util.ipv6_address_to_ipv4(ip_address) if SPF::Util.ipv6_address_is_ipv4_mapped(ip_address) if IP::V4 === ip_address value = ip_address.to_addr elsif IP::V6 === ip_address value = ip_address.to_hex.upcase.split('').join('.') else server.throw_result(:permerror, request, "Unexpected IP address version in request") end elsif char == 'p' # RFC 4408, 8.1/22 # According to RFC 7208 the "p" macro letter should not be used (or even published). # Here it is left unexpanded and transformers and delimiters are not applied. value = '%{' + m2.to_s rh_parts = nil reverse = nil elsif char == 'v' # RFC 4408, 8.1/6/7 if IP::V4 === request.ip_address value = 'in-addr' elsif IP::V6 === request.ip_address value = 'ip6' else # Unexpected IP address version. server.throw_result(:permerror, request, "Unexpected IP address version in request") end elsif char == 'h' # RFC 4408, 8.1/6/8 value = request.helo_identity || 'unknown' elsif char == 'c' # RFC 4408, 8.1/20, 8.1/21 raise SPF::InvalidMacroStringError.new("Illegal 'c' macro in non-explanation macro string '#{@text}'") unless @is_explanation ip_address = request.ip_address value = SPF::Util::ip_address_to_string(ip_address) elsif char == 'r' # RFC 4408, 8.1/23 value = server.hostname || 'unknown' elsif char == 't' raise SPF::InvalidMacroStringError.new("Illegal 't' macro in non-explanation macro string '#{@text}'") unless @is_explanation value = Time.now.to_i.to_s elsif char == '_scope' # Scope pseudo macro for internal use only! value = request.scope.to_s else # Unknown macro character. raise SPF::InvalidMacroStringError.new("Invalid macro character #{char} in macro string '#{@text}'") end if rh_parts || reverse delimiter ||= self.class.default_split_delimiters list = value.split(delimiter) list.reverse! if reverse # Extract desired parts: if rh_parts && rh_parts.to_i > 0 list = list.last(rh_parts.to_i) end if rh_parts && rh_parts.to_i == 0 raise SPF::InvalidMacroStringError.new("Illegal selection of 0 (zero) right-hand parts in macro string '#{@text}'") end value = list.join(self.class.default_join_delimiter) end if do_percent_encode unsafe = Regexp.new('^' + self.class.uri_unreserved_chars) value = URI.escape(value, unsafe) end += value text = m2.post_match else # Invalid macro expression. raise SPF::InvalidMacroStringError.new("Invalid macro expression in macro string '#{@text}'") end elsif key == '-' += '-' text = m.post_match elsif key == '_' += ' ' text = m.post_match elsif key == '%' += '%' text = m.post_match else # Invalid macro expression. pos = m.offset(2).first raise SPF::InvalidMacroStringError.new("Invalid macro expression at pos #{pos} in macro string '#{@text}'") end end += text # Append remaining unmatched characters. context ? : @expanded = end |
#to_s ⇒ Object
169 170 171 172 173 174 175 |
# File 'lib/spf/macro_string.rb', line 169 def to_s if valid_context(false) return else return @text end end |
#valid_context(required, server = self.server, request = self.request) ⇒ Object
177 178 179 180 181 182 183 184 185 186 187 |
# File 'lib/spf/macro_string.rb', line 177 def valid_context(required, server = self.server, request = self.request) if not SPF::Server === server raise SPF::MacroExpansionCtxRequiredError.new('SPF server object required') if required return false end if not SPF::Request === request raise SPF::MacroExpansionCtxRequiredError.new('SPF request object required') if required return false end return true end |