summaryrefslogtreecommitdiff
path: root/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols')
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/header_and_content.rb138
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/httpclient.rb300
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/httpclient2.rb600
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/line_and_text.rb125
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/line_protocol.rb29
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/linetext2.rb179
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/memcache.rb331
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/object_protocol.rb46
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/postgres3.rb246
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/saslauth.rb175
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/smtpclient.rb394
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/smtpserver.rb666
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/socks4.rb66
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/stomp.rb205
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/tcptest.rb54
15 files changed, 3554 insertions, 0 deletions
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/header_and_content.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/header_and_content.rb
new file mode 100644
index 0000000..b5a465a
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/header_and_content.rb
@@ -0,0 +1,138 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage:: http://rubyeventmachine.com
+# Date:: 15 Nov 2006
+#
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+#
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+
+module EventMachine
+ module Protocols
+
+ # === Usage
+ #
+ # class RequestHandler < EM::P::HeaderAndContentProtocol
+ # def receive_request headers, content
+ # p [:request, headers, content]
+ # end
+ # end
+ #
+ # EM.run{
+ # EM.start_server 'localhost', 80, RequestHandler
+ # }
+ #
+ #--
+ # Originally, this subclassed LineAndTextProtocol, which in
+ # turn relies on BufferedTokenizer, which doesn't gracefully
+ # handle the transitions between lines and binary text.
+ # Changed 13Sep08 by FCianfrocca.
+ class HeaderAndContentProtocol < Connection
+ include LineText2
+
+ ContentLengthPattern = /Content-length:\s*(\d+)/i
+
+ def initialize *args
+ super
+ init_for_request
+ end
+
+ def receive_line line
+ case @hc_mode
+ when :discard_blanks
+ unless line == ""
+ @hc_mode = :headers
+ receive_line line
+ end
+ when :headers
+ if line == ""
+ raise "unrecognized state" unless @hc_headers.length > 0
+ if respond_to?(:receive_headers)
+ receive_headers @hc_headers
+ end
+ # @hc_content_length will be nil, not 0, if there was no content-length header.
+ if @hc_content_length.to_i > 0
+ set_binary_mode @hc_content_length
+ else
+ dispatch_request
+ end
+ else
+ @hc_headers << line
+ if ContentLengthPattern =~ line
+ # There are some attacks that rely on sending multiple content-length
+ # headers. This is a crude protection, but needs to become tunable.
+ raise "extraneous content-length header" if @hc_content_length
+ @hc_content_length = $1.to_i
+ end
+ if @hc_headers.length == 1 and respond_to?(:receive_first_header_line)
+ receive_first_header_line line
+ end
+ end
+ else
+ raise "internal error, unsupported mode"
+ end
+ end
+
+ def receive_binary_data text
+ @hc_content = text
+ dispatch_request
+ end
+
+ def dispatch_request
+ if respond_to?(:receive_request)
+ receive_request @hc_headers, @hc_content
+ end
+ init_for_request
+ end
+ private :dispatch_request
+
+ def init_for_request
+ @hc_mode = :discard_blanks
+ @hc_headers = []
+ # originally was @hc_headers ||= []; @hc_headers.clear to get a performance
+ # boost, but it's counterproductive because a subclassed handler will have to
+ # call dup to use the header array we pass in receive_headers.
+
+ @hc_content_length = nil
+ @hc_content = ""
+ end
+ private :init_for_request
+
+ # Basically a convenience method. We might create a subclass that does this
+ # automatically. But it's such a performance killer.
+ def headers_2_hash hdrs
+ self.class.headers_2_hash hdrs
+ end
+
+ class << self
+ def headers_2_hash hdrs
+ hash = {}
+ hdrs.each {|h|
+ if /\A([^\s:]+)\s*:\s*/ =~ h
+ tail = $'.dup
+ hash[ $1.downcase.gsub(/-/,"_").intern ] = tail
+ end
+ }
+ hash
+ end
+ end
+
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/httpclient.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/httpclient.rb
new file mode 100644
index 0000000..38b175c
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/httpclient.rb
@@ -0,0 +1,300 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage:: http://rubyeventmachine.com
+# Date:: 16 July 2006
+#
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+#
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+
+module EventMachine
+ module Protocols
+
+ # <b>Note:</b> This class is deprecated and will be removed. Please use EM-HTTP-Request instead.
+ #
+ # @example
+ # EventMachine.run {
+ # http = EventMachine::Protocols::HttpClient.request(
+ # :host => server,
+ # :port => 80,
+ # :request => "/index.html",
+ # :query_string => "parm1=value1&parm2=value2"
+ # )
+ # http.callback {|response|
+ # puts response[:status]
+ # puts response[:headers]
+ # puts response[:content]
+ # }
+ # }
+ #--
+ # TODO:
+ # Add streaming so we can support enormous POSTs. Current max is 20meg.
+ # Timeout for connections that run too long or hang somewhere in the middle.
+ # Persistent connections (HTTP/1.1), may need a associated delegate object.
+ # DNS: Some way to cache DNS lookups for hostnames we connect to. Ruby's
+ # DNS lookups are unbelievably slow.
+ # HEAD requests.
+ # Convenience methods for requests. get, post, url, etc.
+ # SSL.
+ # Handle status codes like 304, 100, etc.
+ # Refactor this code so that protocol errors all get handled one way (an exception?),
+ # instead of sprinkling set_deferred_status :failed calls everywhere.
+ class HttpClient < Connection
+ include EventMachine::Deferrable
+
+ MaxPostContentLength = 20 * 1024 * 1024
+
+ def initialize
+ warn "HttpClient is deprecated and will be removed. EM-Http-Request should be used instead."
+ @connected = false
+ end
+
+ # @param args [Hash] The request arguments
+ # @option args [String] :host The host IP/DNS name
+ # @option args [Integer] :port The port to connect too
+ # @option args [String] :verb The request type [GET | POST | DELETE | PUT]
+ # @option args [String] :request The request path
+ # @option args [Hash] :basic_auth The basic auth credentials (:username and :password)
+ # @option args [String] :content The request content
+ # @option args [String] :contenttype The content type (e.g. text/plain)
+ # @option args [String] :query_string The query string
+ # @option args [String] :host_header The host header to set
+ # @option args [String] :cookie Cookies to set
+ def self.request( args = {} )
+ args[:port] ||= 80
+ EventMachine.connect( args[:host], args[:port], self ) {|c|
+ # According to the docs, we will get here AFTER post_init is called.
+ c.instance_eval {@args = args}
+ }
+ end
+
+ def post_init
+ @start_time = Time.now
+ @data = ""
+ @read_state = :base
+ end
+
+ # We send the request when we get a connection.
+ # AND, we set an instance variable to indicate we passed through here.
+ # That allows #unbind to know whether there was a successful connection.
+ # NB: This naive technique won't work when we have to support multiple
+ # requests on a single connection.
+ def connection_completed
+ @connected = true
+ send_request @args
+ end
+
+ def send_request args
+ args[:verb] ||= args[:method] # Support :method as an alternative to :verb.
+ args[:verb] ||= :get # IS THIS A GOOD IDEA, to default to GET if nothing was specified?
+
+ verb = args[:verb].to_s.upcase
+ unless ["GET", "POST", "PUT", "DELETE", "HEAD"].include?(verb)
+ set_deferred_status :failed, {:status => 0} # TODO, not signalling the error type
+ return # NOTE THE EARLY RETURN, we're not sending any data.
+ end
+
+ request = args[:request] || "/"
+ unless request[0,1] == "/"
+ request = "/" + request
+ end
+
+ qs = args[:query_string] || ""
+ if qs.length > 0 and qs[0,1] != '?'
+ qs = "?" + qs
+ end
+
+ version = args[:version] || "1.1"
+
+ # Allow an override for the host header if it's not the connect-string.
+ host = args[:host_header] || args[:host] || "_"
+ # For now, ALWAYS tuck in the port string, although we may want to omit it if it's the default.
+ port = args[:port].to_i != 80 ? ":#{args[:port]}" : ""
+
+ # POST items.
+ postcontenttype = args[:contenttype] || "application/octet-stream"
+ postcontent = args[:content] || ""
+ raise "oversized content in HTTP POST" if postcontent.length > MaxPostContentLength
+
+ # ESSENTIAL for the request's line-endings to be CRLF, not LF. Some servers misbehave otherwise.
+ # TODO: We ASSUME the caller wants to send a 1.1 request. May not be a good assumption.
+ req = [
+ "#{verb} #{request}#{qs} HTTP/#{version}",
+ "Host: #{host}#{port}",
+ "User-agent: Ruby EventMachine",
+ ]
+
+ if verb == "POST" || verb == "PUT"
+ req << "Content-type: #{postcontenttype}"
+ req << "Content-length: #{postcontent.length}"
+ end
+
+ # TODO, this cookie handler assumes it's getting a single, semicolon-delimited string.
+ # Eventually we will want to deal intelligently with arrays and hashes.
+ if args[:cookie]
+ req << "Cookie: #{args[:cookie]}"
+ end
+
+ # Allow custom HTTP headers, e.g. SOAPAction
+ args[:custom_headers].each do |k,v|
+ req << "#{k}: #{v}"
+ end if args[:custom_headers]
+
+ # Basic-auth stanza contributed by Matt Murphy.
+ if args[:basic_auth]
+ basic_auth_string = ["#{args[:basic_auth][:username]}:#{args[:basic_auth][:password]}"].pack('m').strip.gsub(/\n/,'')
+ req << "Authorization: Basic #{basic_auth_string}"
+ end
+
+ req << ""
+ reqstring = req.map {|l| "#{l}\r\n"}.join
+ send_data reqstring
+
+ if verb == "POST" || verb == "PUT"
+ send_data postcontent
+ end
+ end
+
+
+ def receive_data data
+ while data and data.length > 0
+ case @read_state
+ when :base
+ # Perform any per-request initialization here and don't consume any data.
+ @data = ""
+ @headers = []
+ @content_length = nil # not zero
+ @content = ""
+ @status = nil
+ @chunked = false
+ @chunk_length = nil
+ @read_state = :header
+ @connection_close = nil
+ when :header
+ ary = data.split( /\r?\n/m, 2 )
+ if ary.length == 2
+ data = ary.last
+ if ary.first == ""
+ if (@content_length and @content_length > 0) || @chunked || @connection_close
+ @read_state = :content
+ else
+ dispatch_response
+ @read_state = :base
+ end
+ else
+ @headers << ary.first
+ if @headers.length == 1
+ parse_response_line
+ elsif ary.first =~ /\Acontent-length:\s*/i
+ # Only take the FIRST content-length header that appears,
+ # which we can distinguish because @content_length is nil.
+ # TODO, it's actually a fatal error if there is more than one
+ # content-length header, because the caller is presumptively
+ # a bad guy. (There is an exploit that depends on multiple
+ # content-length headers.)
+ @content_length ||= $'.to_i
+ elsif ary.first =~ /\Aconnection:\s*close/i
+ @connection_close = true
+ elsif ary.first =~ /\Atransfer-encoding:\s*chunked/i
+ @chunked = true
+ end
+ end
+ else
+ @data << data
+ data = ""
+ end
+ when :content
+ if @chunked && @chunk_length
+ bytes_needed = @chunk_length - @chunk_read
+ new_data = data[0, bytes_needed]
+ @chunk_read += new_data.length
+ @content += new_data
+ data = data[bytes_needed..-1] || ""
+ if @chunk_length == @chunk_read && data[0,2] == "\r\n"
+ @chunk_length = nil
+ data = data[2..-1]
+ end
+ elsif @chunked
+ if (m = data.match(/\A(\S*)\r\n/m))
+ data = data[m[0].length..-1]
+ @chunk_length = m[1].to_i(16)
+ @chunk_read = 0
+ if @chunk_length == 0
+ dispatch_response
+ @read_state = :base
+ end
+ end
+ elsif @content_length
+ # If there was no content-length header, we have to wait until the connection
+ # closes. Everything we get until that point is content.
+ # TODO: Must impose a content-size limit, and also must implement chunking.
+ # Also, must support either temporary files for large content, or calling
+ # a content-consumer block supplied by the user.
+ bytes_needed = @content_length - @content.length
+ @content += data[0, bytes_needed]
+ data = data[bytes_needed..-1] || ""
+ if @content_length == @content.length
+ dispatch_response
+ @read_state = :base
+ end
+ else
+ @content << data
+ data = ""
+ end
+ end
+ end
+ end
+
+
+ # We get called here when we have received an HTTP response line.
+ # It's an opportunity to throw an exception or trigger other exceptional
+ # handling.
+ def parse_response_line
+ if @headers.first =~ /\AHTTP\/1\.[01] ([\d]{3})/
+ @status = $1.to_i
+ else
+ set_deferred_status :failed, {
+ :status => 0 # crappy way of signifying an unrecognized response. TODO, find a better way to do this.
+ }
+ close_connection
+ end
+ end
+ private :parse_response_line
+
+ def dispatch_response
+ @read_state = :base
+ set_deferred_status :succeeded, {
+ :content => @content,
+ :headers => @headers,
+ :status => @status
+ }
+ # TODO, we close the connection for now, but this is wrong for persistent clients.
+ close_connection
+ end
+
+ def unbind
+ if !@connected
+ set_deferred_status :failed, {:status => 0} # YECCCCH. Find a better way to signal no-connect/network error.
+ elsif (@read_state == :content and @content_length == nil)
+ dispatch_response
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/httpclient2.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/httpclient2.rb
new file mode 100644
index 0000000..0fb64e8
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/httpclient2.rb
@@ -0,0 +1,600 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage:: http://rubyeventmachine.com
+# Date:: 16 July 2006
+#
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+#
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+
+module EventMachine
+ module Protocols
+
+ # <b>Note:</b> This class is deprecated and will be removed. Please use EM-HTTP-Request instead.
+ #
+ # === Usage
+ #
+ # EM.run{
+ # conn = EM::Protocols::HttpClient2.connect 'google.com', 80
+ #
+ # req = conn.get('/')
+ # req.callback{ |response|
+ # p(response.status)
+ # p(response.headers)
+ # p(response.content)
+ # }
+ # }
+ class HttpClient2 < Connection
+ include LineText2
+
+ def initialize
+ warn "HttpClient2 is deprecated and will be removed. EM-Http-Request should be used instead."
+
+ @authorization = nil
+ @closed = nil
+ @requests = nil
+ end
+
+ # @private
+ class Request
+ include Deferrable
+
+ attr_reader :version
+ attr_reader :status
+ attr_reader :header_lines
+ attr_reader :headers
+ attr_reader :content
+ attr_reader :internal_error
+
+ def initialize conn, args
+ @conn = conn
+ @args = args
+ @header_lines = []
+ @headers = {}
+ @blanks = 0
+ @chunk_trailer = nil
+ @chunking = nil
+ end
+
+ def send_request
+ az = @args[:authorization] and az = "Authorization: #{az}\r\n"
+
+ r = [
+ "#{@args[:verb]} #{@args[:uri]} HTTP/#{@args[:version] || "1.1"}\r\n",
+ "Host: #{@args[:host_header] || "_"}\r\n",
+ az || "",
+ "\r\n"
+ ]
+ @conn.send_data r.join
+ end
+
+
+ #--
+ #
+ def receive_line ln
+ if @chunk_trailer
+ receive_chunk_trailer(ln)
+ elsif @chunking
+ receive_chunk_header(ln)
+ else
+ receive_header_line(ln)
+ end
+ end
+
+ #--
+ #
+ def receive_chunk_trailer ln
+ if ln.length == 0
+ @conn.pop_request
+ succeed(self)
+ else
+ p "Received chunk trailer line"
+ end
+ end
+
+ #--
+ # Allow up to ten blank lines before we get a real response line.
+ # Allow no more than 100 lines in the header.
+ #
+ def receive_header_line ln
+ if ln.length == 0
+ if @header_lines.length > 0
+ process_header
+ else
+ @blanks += 1
+ if @blanks > 10
+ @conn.close_connection
+ end
+ end
+ else
+ @header_lines << ln
+ if @header_lines.length > 100
+ @internal_error = :bad_header
+ @conn.close_connection
+ end
+ end
+ end
+
+ #--
+ # Cf RFC 2616 pgh 3.6.1 for the format of HTTP chunks.
+ #
+ def receive_chunk_header ln
+ if ln.length > 0
+ chunksize = ln.to_i(16)
+ if chunksize > 0
+ @conn.set_text_mode(ln.to_i(16))
+ else
+ @content = @content ? @content.join : ''
+ @chunk_trailer = true
+ end
+ else
+ # We correctly come here after each chunk gets read.
+ # p "Got A BLANK chunk line"
+ end
+
+ end
+
+
+ #--
+ # We get a single chunk. Append it to the incoming content and switch back to line mode.
+ #
+ def receive_chunked_text text
+ # p "RECEIVED #{text.length} CHUNK"
+ (@content ||= []) << text
+ end
+
+
+ #--
+ # TODO, inefficient how we're handling this. Part of it is done so as to
+ # make sure we don't have problems in detecting chunked-encoding, content-length,
+ # etc.
+ #
+ HttpResponseRE = /\AHTTP\/(1.[01]) ([\d]{3})/i
+ ClenRE = /\AContent-length:\s*(\d+)/i
+ ChunkedRE = /\ATransfer-encoding:\s*chunked/i
+ ColonRE = /\:\s*/
+
+ def process_header
+ unless @header_lines.first =~ HttpResponseRE
+ @conn.close_connection
+ @internal_error = :bad_request
+ end
+ @version = $1.dup
+ @status = $2.dup.to_i
+
+ clen = nil
+ chunks = nil
+ @header_lines.each_with_index do |e,ix|
+ if ix > 0
+ hdr,val = e.split(ColonRE,2)
+ (@headers[hdr.downcase] ||= []) << val
+ end
+
+ if clen == nil and e =~ ClenRE
+ clen = $1.dup.to_i
+ end
+ if e =~ ChunkedRE
+ chunks = true
+ end
+ end
+
+ if clen
+ # If the content length is zero we should not call set_text_mode,
+ # because a value of zero will make it wait forever, hanging the
+ # connection. Just return success instead, with empty content.
+ if clen == 0 then
+ @content = ""
+ @conn.pop_request
+ succeed(self)
+ else
+ @conn.set_text_mode clen
+ end
+ elsif chunks
+ @chunking = true
+ else
+ # Chunked transfer, multipart, or end-of-connection.
+ # For end-of-connection, we need to go the unbind
+ # method and suppress its desire to fail us.
+ p "NO CLEN"
+ p @args[:uri]
+ p @header_lines
+ @internal_error = :unsupported_clen
+ @conn.close_connection
+ end
+ end
+ private :process_header
+
+
+ def receive_text text
+ @chunking ? receive_chunked_text(text) : receive_sized_text(text)
+ end
+
+ #--
+ # At the present time, we only handle contents that have a length
+ # specified by the content-length header.
+ #
+ def receive_sized_text text
+ @content = text
+ @conn.pop_request
+ succeed(self)
+ end
+ end
+
+ # Make a connection to a remote HTTP server.
+ # Can take either a pair of arguments (which will be interpreted as
+ # a hostname/ip-address and a port), or a hash.
+ # If the arguments are a hash, then supported values include:
+ # :host => a hostname or ip-address
+ # :port => a port number
+ # :ssl => true to enable ssl
+ def self.connect *args
+ if args.length == 2
+ args = {:host=>args[0], :port=>args[1]}
+ else
+ args = args.first
+ end
+
+ h,prt,ssl = args[:host], Integer(args[:port]), (args[:tls] || args[:ssl])
+ conn = EM.connect( h, prt, self )
+ conn.start_tls if ssl
+ conn.set_default_host_header( h, prt, ssl )
+ conn
+ end
+
+ # Get a url
+ #
+ # req = conn.get(:uri => '/')
+ # req.callback{|response| puts response.content }
+ #
+ def get args
+ if args.is_a?(String)
+ args = {:uri=>args}
+ end
+ args[:verb] = "GET"
+ request args
+ end
+
+ # Post to a url
+ #
+ # req = conn.post('/data')
+ # req.callback{|response| puts response.content }
+ #--
+ # XXX there's no way to supply a POST body.. wtf?
+ def post args
+ if args.is_a?(String)
+ args = {:uri=>args}
+ end
+ args[:verb] = "POST"
+ request args
+ end
+
+
+ #--
+ # Compute and remember a string to be used as the host header in HTTP requests
+ # unless the user overrides it with an argument to #request.
+ #
+ # @private
+ def set_default_host_header host, port, ssl
+ if (ssl and port != 443) or (!ssl and port != 80)
+ @host_header = "#{host}:#{port}"
+ else
+ @host_header = host
+ end
+ end
+
+
+ # @private
+ def post_init
+ super
+ @connected = EM::DefaultDeferrable.new
+ end
+
+ # @private
+ def connection_completed
+ super
+ @connected.succeed
+ end
+
+ #--
+ # All pending requests, if any, must fail.
+ # We might come here without ever passing through connection_completed
+ # in case we can't connect to the server. We'll also get here when the
+ # connection closes (either because the server closes it, or we close it
+ # due to detecting an internal error or security violation).
+ # In either case, run down all pending requests, if any, and signal failure
+ # on them.
+ #
+ # Set and remember a flag (@closed) so we can immediately fail any
+ # subsequent requests.
+ #
+ # @private
+ def unbind
+ super
+ @closed = true
+ (@requests || []).each {|r| r.fail}
+ end
+
+ # @private
+ def request args
+ args[:host_header] = @host_header unless args.has_key?(:host_header)
+ args[:authorization] = @authorization unless args.has_key?(:authorization)
+ r = Request.new self, args
+ if @closed
+ r.fail
+ else
+ (@requests ||= []).unshift r
+ @connected.callback {r.send_request}
+ end
+ r
+ end
+
+ # @private
+ def receive_line ln
+ if req = @requests.last
+ req.receive_line ln
+ else
+ p "??????????"
+ p ln
+ end
+ end
+
+ # @private
+ def receive_binary_data text
+ @requests.last.receive_text text
+ end
+
+ #--
+ # Called by a Request object when it completes.
+ #
+ # @private
+ def pop_request
+ @requests.pop
+ end
+ end
+
+
+=begin
+ class HttpClient2x < Connection
+ include LineText2
+
+ # TODO: Make this behave appropriate in case a #connect fails.
+ # Currently, this produces no errors.
+
+ # Make a connection to a remote HTTP server.
+ # Can take either a pair of arguments (which will be interpreted as
+ # a hostname/ip-address and a port), or a hash.
+ # If the arguments are a hash, then supported values include:
+ # :host => a hostname or ip-address;
+ # :port => a port number
+ #--
+ # TODO, support optional encryption arguments like :ssl
+ def self.connect *args
+ if args.length == 2
+ args = {:host=>args[0], :port=>args[1]}
+ else
+ args = args.first
+ end
+
+ h,prt = args[:host],Integer(args[:port])
+ EM.connect( h, prt, self, h, prt )
+ end
+
+
+ #--
+ # Sugars a connection that makes a single request and then
+ # closes the connection. Matches the behavior and the arguments
+ # of the original implementation of class HttpClient.
+ #
+ # Intended primarily for back compatibility, but the idiom
+ # is probably useful so it's not deprecated.
+ # We return a Deferrable, as did the original implementation.
+ #
+ # Because we're improving the way we deal with errors and exceptions
+ # (specifically, HTTP response codes other than 2xx will trigger the
+ # errback rather than the callback), this may break some existing code.
+ #
+ def self.request args
+ c = connect args
+ end
+
+ #--
+ # Requests can be pipelined. When we get a request, add it to the
+ # front of a queue as an array. The last element of the @requests
+ # array is always the oldest request received. Each element of the
+ # @requests array is a two-element array consisting of a hash with
+ # the original caller's arguments, and an initially-empty Ostruct
+ # containing the data we retrieve from the server's response.
+ # Maintain the instance variable @current_response, which is the response
+ # of the oldest pending request. That's just to make other code a little
+ # easier. If the variable doesn't exist when we come here, we're
+ # obviously the first request being made on the connection.
+ #
+ # The reason for keeping this method private (and requiring use of the
+ # convenience methods #get, #post, #head, etc) is to avoid the small
+ # performance penalty of canonicalizing the verb.
+ #
+ def request args
+ d = EventMachine::DefaultDeferrable.new
+
+ if @closed
+ d.fail
+ return d
+ end
+
+ o = OpenStruct.new
+ o.deferrable = d
+ (@requests ||= []).unshift [args, o]
+ @current_response ||= @requests.last.last
+ @connected.callback {
+ az = args[:authorization] and az = "Authorization: #{az}\r\n"
+
+ r = [
+ "#{args[:verb]} #{args[:uri]} HTTP/#{args[:version] || "1.1"}\r\n",
+ "Host: #{args[:host_header] || @host_header}\r\n",
+ az || "",
+ "\r\n"
+ ]
+ p r
+ send_data r.join
+ }
+ o.deferrable
+ end
+ private :request
+
+ def get args
+ if args.is_a?(String)
+ args = {:uri=>args}
+ end
+ args[:verb] = "GET"
+ request args
+ end
+
+ def initialize host, port
+ super
+ @host_header = "#{host}:#{port}"
+ end
+ def post_init
+ super
+ @connected = EM::DefaultDeferrable.new
+ end
+
+
+ def connection_completed
+ super
+ @connected.succeed
+ end
+
+ #--
+ # Make sure to throw away any leftover incoming data if we've
+ # been closed due to recognizing an error.
+ #
+ # Generate an internal error if we get an unreasonable number of
+ # header lines. It could be malicious.
+ #
+ def receive_line ln
+ p ln
+ return if @closed
+
+ if ln.length > 0
+ (@current_response.headers ||= []).push ln
+ abort_connection if @current_response.headers.length > 100
+ else
+ process_received_headers
+ end
+ end
+
+ #--
+ # We come here when we've seen all the headers for a particular request.
+ # What we do next depends on the response line (which should be the
+ # first line in the header set), and whether there is content to read.
+ # We may transition into a text-reading state to read content, or
+ # we may abort the connection, or we may go right back into parsing
+ # responses for the next response in the chain.
+ #
+ # We make an ASSUMPTION that the first line is an HTTP response.
+ # Anything else produces an error that aborts the connection.
+ # This may not be enough, because it may be that responses to pipelined
+ # requests will come with a blank-line delimiter.
+ #
+ # Any non-2xx response will be treated as a fatal error, and abort the
+ # connection. We will set up the status and other response parameters.
+ # TODO: we will want to properly support 1xx responses, which some versions
+ # of IIS copiously generate.
+ # TODO: We need to give the option of not aborting the connection with certain
+ # non-200 responses, in order to work with NTLM and other authentication
+ # schemes that work at the level of individual connections.
+ #
+ # Some error responses will get sugarings. For example, we'll return the
+ # Location header in the response in case of a 301/302 response.
+ #
+ # Possible dispositions here:
+ # 1) No content to read (either content-length is zero or it's a HEAD request);
+ # 2) Switch to text mode to read a specific number of bytes;
+ # 3) Read a chunked or multipart response;
+ # 4) Read till the server closes the connection.
+ #
+ # Our reponse to the client can be either to wait till all the content
+ # has been read and then to signal caller's deferrable, or else to signal
+ # it when we finish the processing the headers and then expect the caller
+ # to have given us a block to call as the content comes in. And of course
+ # the latter gets stickier with chunks and multiparts.
+ #
+ HttpResponseRE = /\AHTTP\/(1.[01]) ([\d]{3})/i
+ ClenRE = /\AContent-length:\s*(\d+)/i
+ def process_received_headers
+ abort_connection unless @current_response.headers.first =~ HttpResponseRE
+ @current_response.version = $1.dup
+ st = $2.dup
+ @current_response.status = st.to_i
+ abort_connection unless st[0,1] == "2"
+
+ clen = nil
+ @current_response.headers.each do |e|
+ if clen == nil and e =~ ClenRE
+ clen = $1.dup.to_i
+ end
+ end
+
+ if clen
+ set_text_mode clen
+ end
+ end
+ private :process_received_headers
+
+
+ def receive_binary_data text
+ @current_response.content = text
+ @current_response.deferrable.succeed @current_response
+ @requests.pop
+ @current_response = (@requests.last || []).last
+ set_line_mode
+ end
+
+
+
+ # We've received either a server error or an internal error.
+ # Close the connection and abort any pending requests.
+ #--
+ # When should we call close_connection? It will cause #unbind
+ # to be fired. Should the user expect to see #unbind before
+ # we call #receive_http_error, or the other way around?
+ #
+ # Set instance variable @closed. That's used to inhibit further
+ # processing of any inbound data after an error has been recognized.
+ #
+ # We shouldn't have to worry about any leftover outbound data,
+ # because we call close_connection (not close_connection_after_writing).
+ # That ensures that any pipelined requests received after an error
+ # DO NOT get streamed out to the server on this connection.
+ # Very important. TODO, write a unit-test to establish that behavior.
+ #
+ def abort_connection
+ close_connection
+ @closed = true
+ @current_response.deferrable.fail( @current_response )
+ end
+
+
+ #------------------------
+ # Below here are user-overridable methods.
+
+ end
+=end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/line_and_text.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/line_and_text.rb
new file mode 100644
index 0000000..784daf2
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/line_and_text.rb
@@ -0,0 +1,125 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage:: http://rubyeventmachine.com
+# Date:: 15 November 2006
+#
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+#
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+#
+
+module EventMachine
+ module Protocols
+ # A protocol that handles line-oriented data with interspersed binary text.
+ #
+ # This version is optimized for performance. See EventMachine::Protocols::LineText2
+ # for a version which is optimized for correctness with regard to binary text blocks
+ # that can switch back to line mode.
+ class LineAndTextProtocol < Connection
+ MaxBinaryLength = 32*1024*1024
+
+ def initialize *args
+ super
+ lbp_init_line_state
+ end
+
+ def receive_data data
+ if @lbp_mode == :lines
+ begin
+ @lpb_buffer.extract(data).each do |line|
+ receive_line(line.chomp) if respond_to?(:receive_line)
+ end
+ rescue
+ receive_error('overlength line') if respond_to?(:receive_error)
+ close_connection
+ return
+ end
+ else
+ if @lbp_binary_limit > 0
+ wanted = @lbp_binary_limit - @lbp_binary_bytes_received
+ chunk = nil
+ if data.length > wanted
+ chunk = data.slice!(0...wanted)
+ else
+ chunk = data
+ data = ""
+ end
+ @lbp_binary_buffer[@lbp_binary_bytes_received...(@lbp_binary_bytes_received+chunk.length)] = chunk
+ @lbp_binary_bytes_received += chunk.length
+ if @lbp_binary_bytes_received == @lbp_binary_limit
+ receive_binary_data(@lbp_binary_buffer) if respond_to?(:receive_binary_data)
+ lbp_init_line_state
+ end
+ receive_data(data) if data.length > 0
+ else
+ receive_binary_data(data) if respond_to?(:receive_binary_data)
+ data = ""
+ end
+ end
+ end
+
+ def unbind
+ if @lbp_mode == :binary and @lbp_binary_limit > 0
+ if respond_to?(:receive_binary_data)
+ receive_binary_data( @lbp_binary_buffer[0...@lbp_binary_bytes_received] )
+ end
+ end
+ end
+
+ # Set up to read the supplied number of binary bytes.
+ # This recycles all the data currently waiting in the line buffer, if any.
+ # If the limit is nil, then ALL subsequent data will be treated as binary
+ # data and passed to the upstream protocol handler as we receive it.
+ # If a limit is given, we'll hold the incoming binary data and not
+ # pass it upstream until we've seen it all, or until there is an unbind
+ # (in which case we'll pass up a partial).
+ # Specifying nil for the limit (the default) means there is no limit.
+ # Specifiyng zero for the limit will cause an immediate transition back to line mode.
+ #
+ def set_binary_mode size = nil
+ if @lbp_mode == :lines
+ if size == 0
+ receive_binary_data("") if respond_to?(:receive_binary_data)
+ # Do no more work here. Stay in line mode and keep consuming data.
+ else
+ @lbp_binary_limit = size.to_i # (nil will be stored as zero)
+ if @lbp_binary_limit > 0
+ raise "Overlength" if @lbp_binary_limit > MaxBinaryLength # arbitrary sanity check
+ @lbp_binary_buffer = "\0" * @lbp_binary_limit
+ @lbp_binary_bytes_received = 0
+ end
+
+ @lbp_mode = :binary
+ receive_data @lpb_buffer.flush
+ end
+ else
+ raise "invalid operation"
+ end
+ end
+
+ #--
+ # For internal use, establish protocol baseline for handling lines.
+ def lbp_init_line_state
+ @lpb_buffer = BufferedTokenizer.new("\n")
+ @lbp_mode = :lines
+ end
+ private :lbp_init_line_state
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/line_protocol.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/line_protocol.rb
new file mode 100644
index 0000000..dfddae8
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/line_protocol.rb
@@ -0,0 +1,29 @@
+module EventMachine
+ module Protocols
+ # LineProtocol will parse out newline terminated strings from a receive_data stream
+ #
+ # module Server
+ # include EM::P::LineProtocol
+ #
+ # def receive_line(line)
+ # send_data("you said: #{line}")
+ # end
+ # end
+ #
+ module LineProtocol
+ # @private
+ def receive_data data
+ (@buf ||= '') << data
+
+ while @buf.slice!(/(.*?)\r?\n/)
+ receive_line($1)
+ end
+ end
+
+ # Invoked with lines received over the network
+ def receive_line(line)
+ # stub
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/linetext2.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/linetext2.rb
new file mode 100644
index 0000000..9fdf28b
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/linetext2.rb
@@ -0,0 +1,179 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage:: http://rubyeventmachine.com
+# Date:: 15 November 2006
+#
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+#
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+
+module EventMachine
+ module Protocols
+ # In the grand, time-honored tradition of re-inventing the wheel, we offer
+ # here YET ANOTHER protocol that handles line-oriented data with interspersed
+ # binary text. This one trades away some of the performance optimizations of
+ # EventMachine::Protocols::LineAndTextProtocol in order to get better correctness
+ # with regard to binary text blocks that can switch back to line mode. It also
+ # permits the line-delimiter to change in midstream.
+ # This was originally written to support Stomp.
+ module LineText2
+ # TODO! We're not enforcing the limits on header lengths and text-lengths.
+ # When we get around to that, call #receive_error if the user defined it, otherwise
+ # throw exceptions.
+
+ MaxBinaryLength = 32*1024*1024
+
+ #--
+ # Will loop internally until there's no data left to read.
+ # That way the user-defined handlers we call can modify the
+ # handling characteristics on a per-token basis.
+ #
+ def receive_data data
+ return unless (data and data.length > 0)
+
+ # Do this stuff in lieu of a constructor.
+ @lt2_mode ||= :lines
+ @lt2_delimiter ||= "\n"
+ @lt2_linebuffer ||= []
+
+ remaining_data = data
+
+ while remaining_data.length > 0
+ if @lt2_mode == :lines
+ delimiter_string = case @lt2_delimiter
+ when Regexp
+ remaining_data.slice(@lt2_delimiter)
+ else
+ @lt2_delimiter
+ end
+ ix = remaining_data.index(delimiter_string) if delimiter_string
+ if ix
+ @lt2_linebuffer << remaining_data[0...ix]
+ ln = @lt2_linebuffer.join
+ @lt2_linebuffer.clear
+ if @lt2_delimiter == "\n"
+ ln.chomp!
+ end
+ receive_line ln
+ remaining_data = remaining_data[(ix+delimiter_string.length)..-1]
+ else
+ @lt2_linebuffer << remaining_data
+ remaining_data = ""
+ end
+ elsif @lt2_mode == :text
+ if @lt2_textsize
+ needed = @lt2_textsize - @lt2_textpos
+ will_take = if remaining_data.length > needed
+ needed
+ else
+ remaining_data.length
+ end
+
+ @lt2_textbuffer << remaining_data[0...will_take]
+ tail = remaining_data[will_take..-1]
+
+ @lt2_textpos += will_take
+ if @lt2_textpos >= @lt2_textsize
+ # Reset line mode (the default behavior) BEFORE calling the
+ # receive_binary_data. This makes it possible for user code
+ # to call set_text_mode, enabling chains of text blocks
+ # (which can possibly be of different sizes).
+ set_line_mode
+ receive_binary_data @lt2_textbuffer.join
+ receive_end_of_binary_data
+ end
+
+ remaining_data = tail
+ else
+ receive_binary_data remaining_data
+ remaining_data = ""
+ end
+ end
+ end
+ end
+
+ # The line delimiter may be a regular expression or a string. Anything
+ # passed to set_delimiter other than a regular expression will be
+ # converted to a string.
+ def set_delimiter delim
+ @lt2_delimiter = case delim
+ when Regexp
+ delim
+ else
+ delim.to_s
+ end
+ end
+
+ # Called internally but also exposed to user code, for the case in which
+ # processing of binary data creates a need to transition back to line mode.
+ # We support an optional parameter to "throw back" some data, which might
+ # be an umprocessed chunk of the transmitted binary data, or something else
+ # entirely.
+ def set_line_mode data=""
+ @lt2_mode = :lines
+ (@lt2_linebuffer ||= []).clear
+ receive_data data.to_s
+ end
+
+ def set_text_mode size=nil
+ if size == 0
+ set_line_mode
+ else
+ @lt2_mode = :text
+ (@lt2_textbuffer ||= []).clear
+ @lt2_textsize = size # which can be nil, signifying no limit
+ @lt2_textpos = 0
+ end
+ end
+
+ # Alias for #set_text_mode, added for back-compatibility with LineAndTextProtocol.
+ def set_binary_mode size=nil
+ set_text_mode size
+ end
+
+ # In case of a dropped connection, we'll send a partial buffer to user code
+ # when in sized text mode. User overrides of #receive_binary_data need to
+ # be aware that they may get a short buffer.
+ def unbind
+ @lt2_mode ||= nil
+ if @lt2_mode == :text and @lt2_textpos > 0
+ receive_binary_data @lt2_textbuffer.join
+ end
+ end
+
+ # Stub. Should be subclassed by user code.
+ def receive_line ln
+ # no-op
+ end
+
+ # Stub. Should be subclassed by user code.
+ def receive_binary_data data
+ # no-op
+ end
+
+ # Stub. Should be subclassed by user code.
+ # This is called when transitioning internally from text mode
+ # back to line mode. Useful when client code doesn't want
+ # to keep track of how much data it's received.
+ def receive_end_of_binary_data
+ # no-op
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/memcache.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/memcache.rb
new file mode 100644
index 0000000..1f81aaf
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/memcache.rb
@@ -0,0 +1,331 @@
+module EventMachine
+ module Protocols
+ # Implements the Memcache protocol (http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt).
+ # Requires memcached >= 1.2.4 w/ noreply support
+ #
+ # == Usage example
+ #
+ # EM.run{
+ # cache = EM::P::Memcache.connect 'localhost', 11211
+ #
+ # cache.set :a, 'hello'
+ # cache.set :b, 'hi'
+ # cache.set :c, 'how are you?'
+ # cache.set :d, ''
+ #
+ # cache.get(:a){ |v| p v }
+ # cache.get_hash(:a, :b, :c, :d){ |v| p v }
+ # cache.get(:a,:b,:c,:d){ |a,b,c,d| p [a,b,c,d] }
+ #
+ # cache.get(:a,:z,:b,:y,:d){ |a,z,b,y,d| p [a,z,b,y,d] }
+ #
+ # cache.get(:missing){ |m| p [:missing=, m] }
+ # cache.set(:missing, 'abc'){ p :stored }
+ # cache.get(:missing){ |m| p [:missing=, m] }
+ # cache.del(:missing){ p :deleted }
+ # cache.get(:missing){ |m| p [:missing=, m] }
+ # }
+ #
+ module Memcache
+ include EM::Deferrable
+
+ ##
+ # constants
+
+ unless defined? Cempty
+ # @private
+ Cstored = 'STORED'.freeze
+ # @private
+ Cend = 'END'.freeze
+ # @private
+ Cdeleted = 'DELETED'.freeze
+ # @private
+ Cunknown = 'NOT_FOUND'.freeze
+ # @private
+ Cerror = 'ERROR'.freeze
+
+ # @private
+ Cempty = ''.freeze
+ # @private
+ Cdelimiter = "\r\n".freeze
+ end
+
+ ##
+ # commands
+
+ # Get the value associated with one or multiple keys
+ #
+ # cache.get(:a){ |v| p v }
+ # cache.get(:a,:b,:c,:d){ |a,b,c,d| p [a,b,c,d] }
+ #
+ def get *keys
+ raise ArgumentError unless block_given?
+
+ callback{
+ keys = keys.map{|k| k.to_s.gsub(/\s/,'_') }
+ send_data "get #{keys.join(' ')}\r\n"
+ @get_cbs << [keys, proc{ |values|
+ yield *keys.map{ |k| values[k] }
+ }]
+ }
+ end
+
+ # Set the value for a given key
+ #
+ # cache.set :a, 'hello'
+ # cache.set(:missing, 'abc'){ puts "stored the value!" }
+ #
+ def set key, val, exptime = 0, &cb
+ callback{
+ val = val.to_s
+ send_cmd :set, key, 0, exptime, val.respond_to?(:bytesize) ? val.bytesize : val.size, !block_given?
+ send_data val
+ send_data Cdelimiter
+ @set_cbs << cb if cb
+ }
+ end
+
+ # Gets multiple values as a hash
+ #
+ # cache.get_hash(:a, :b, :c, :d){ |h| puts h[:a] }
+ #
+ def get_hash *keys
+ raise ArgumentError unless block_given?
+
+ get *keys do |*values|
+ yield keys.inject({}){ |hash, k| hash.update k => values[keys.index(k)] }
+ end
+ end
+
+ # Delete the value associated with a key
+ #
+ # cache.del :a
+ # cache.del(:b){ puts "deleted the value!" }
+ #
+ def delete key, expires = 0, &cb
+ callback{
+ send_data "delete #{key} #{expires}#{cb ? '' : ' noreply'}\r\n"
+ @del_cbs << cb if cb
+ }
+ end
+ alias del delete
+
+ # Connect to a memcached server (must support NOREPLY, memcached >= 1.2.4)
+ def self.connect host = 'localhost', port = 11211
+ EM.connect host, port, self, host, port
+ end
+
+ def send_cmd cmd, key, flags = 0, exptime = 0, bytes = 0, noreply = false
+ send_data "#{cmd} #{key} #{flags} #{exptime} #{bytes}#{noreply ? ' noreply' : ''}\r\n"
+ end
+ private :send_cmd
+
+ ##
+ # errors
+
+ # @private
+ class ParserError < StandardError
+ end
+
+ ##
+ # em hooks
+
+ # @private
+ def initialize host, port = 11211
+ @host, @port = host, port
+ end
+
+ # @private
+ def connection_completed
+ @get_cbs = []
+ @set_cbs = []
+ @del_cbs = []
+
+ @values = {}
+
+ @reconnecting = false
+ @connected = true
+ succeed
+ # set_delimiter "\r\n"
+ # set_line_mode
+ end
+
+ #--
+ # 19Feb09 Switched to a custom parser, LineText2 is recursive and can cause
+ # stack overflows when there is too much data.
+ # include EM::P::LineText2
+ # @private
+ def receive_data data
+ (@buffer||='') << data
+
+ while index = @buffer.index(Cdelimiter)
+ begin
+ line = @buffer.slice!(0,index+2)
+ process_cmd line
+ rescue ParserError
+ @buffer[0...0] = line
+ break
+ end
+ end
+ end
+
+ #--
+ # def receive_line line
+ # @private
+ def process_cmd line
+ case line.strip
+ when /^VALUE\s+(.+?)\s+(\d+)\s+(\d+)/ # VALUE <key> <flags> <bytes>
+ bytes = Integer($3)
+ # set_binary_mode bytes+2
+ # @cur_key = $1
+ if @buffer.size >= bytes + 2
+ @values[$1] = @buffer.slice!(0,bytes)
+ @buffer.slice!(0,2) # \r\n
+ else
+ raise ParserError
+ end
+
+ when Cend # END
+ if entry = @get_cbs.shift
+ keys, cb = entry
+ cb.call(@values)
+ end
+ @values = {}
+
+ when Cstored # STORED
+ if cb = @set_cbs.shift
+ cb.call(true)
+ end
+
+ when Cdeleted # DELETED
+ if cb = @del_cbs.shift
+ cb.call(true)
+ end
+
+ when Cunknown # NOT_FOUND
+ if cb = @del_cbs.shift
+ cb.call(false)
+ end
+
+ else
+ p [:MEMCACHE_UNKNOWN, line]
+ end
+ end
+
+ #--
+ # def receive_binary_data data
+ # @values[@cur_key] = data[0..-3]
+ # end
+
+ # @private
+ def unbind
+ if @connected or @reconnecting
+ EM.add_timer(1){ reconnect @host, @port }
+ @connected = false
+ @reconnecting = true
+ @deferred_status = nil
+ else
+ raise 'Unable to connect to memcached server'
+ end
+ end
+ end
+ end
+end
+
+if __FILE__ == $0
+ # ruby -I ext:lib -r eventmachine -rubygems lib/protocols/memcache.rb
+ require 'em/spec'
+
+ # @private
+ class TestConnection
+ include EM::P::Memcache
+ def send_data data
+ sent_data << data
+ end
+ def sent_data
+ @sent_data ||= ''
+ end
+
+ def initialize
+ connection_completed
+ end
+ end
+
+ EM.describe EM::Protocols::Memcache do
+
+ before{
+ @c = TestConnection.new
+ }
+
+ should 'send get requests' do
+ @c.get('a'){}
+ @c.sent_data.should == "get a\r\n"
+ done
+ end
+
+ should 'send set requests' do
+ @c.set('a', 1){}
+ @c.sent_data.should == "set a 0 0 1\r\n1\r\n"
+ done
+ end
+
+ should 'use noreply on set without block' do
+ @c.set('a', 1)
+ @c.sent_data.should == "set a 0 0 1 noreply\r\n1\r\n"
+ done
+ end
+
+ should 'send delete requests' do
+ @c.del('a')
+ @c.sent_data.should == "delete a 0 noreply\r\n"
+ done
+ end
+
+ should 'work when get returns no values' do
+ @c.get('a'){ |a|
+ a.should.be.nil
+ done
+ }
+
+ @c.receive_data "END\r\n"
+ end
+
+ should 'invoke block on set' do
+ @c.set('a', 1){
+ done
+ }
+
+ @c.receive_data "STORED\r\n"
+ end
+
+ should 'invoke block on delete' do
+ @c.delete('a'){ |found|
+ found.should.be.false
+ }
+ @c.delete('b'){ |found|
+ found.should.be.true
+ done
+ }
+
+ @c.receive_data "NOT_FOUND\r\n"
+ @c.receive_data "DELETED\r\n"
+ end
+
+ should 'parse split responses' do
+ @c.get('a'){ |a|
+ a.should == 'abc'
+ done
+ }
+
+ @c.receive_data "VAL"
+ @c.receive_data "UE a 0 "
+ @c.receive_data "3\r\n"
+ @c.receive_data "ab"
+ @c.receive_data "c"
+ @c.receive_data "\r\n"
+ @c.receive_data "EN"
+ @c.receive_data "D\r\n"
+ end
+
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/object_protocol.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/object_protocol.rb
new file mode 100644
index 0000000..ec79cb4
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/object_protocol.rb
@@ -0,0 +1,46 @@
+module EventMachine
+ module Protocols
+ # ObjectProtocol allows for easy communication using marshaled ruby objects
+ #
+ # module RubyServer
+ # include EM::P::ObjectProtocol
+ #
+ # def receive_object obj
+ # send_object({'you said' => obj})
+ # end
+ # end
+ #
+ module ObjectProtocol
+ # By default returns Marshal, override to return JSON or YAML, or any
+ # other serializer/deserializer responding to #dump and #load.
+ def serializer
+ Marshal
+ end
+
+ # @private
+ def receive_data data
+ (@buf ||= '') << data
+
+ while @buf.size >= 4
+ if @buf.size >= 4+(size=@buf.unpack('N').first)
+ @buf.slice!(0,4)
+ receive_object serializer.load(@buf.slice!(0,size))
+ else
+ break
+ end
+ end
+ end
+
+ # Invoked with ruby objects received over the network
+ def receive_object obj
+ # stub
+ end
+
+ # Sends a ruby object over the network
+ def send_object obj
+ data = serializer.dump(obj)
+ send_data [data.respond_to?(:bytesize) ? data.bytesize : data.size, data].pack('Na*')
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/postgres3.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/postgres3.rb
new file mode 100644
index 0000000..7d87505
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/postgres3.rb
@@ -0,0 +1,246 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage:: http://rubyeventmachine.com
+# Date:: 15 November 2006
+#
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-08 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+#
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+#
+
+require 'postgres-pr/message'
+require 'postgres-pr/connection'
+require 'stringio'
+
+# @private
+class StringIO
+ # Reads exactly +n+ bytes.
+ #
+ # If the data read is nil an EOFError is raised.
+ #
+ # If the data read is too short an IOError is raised
+ def readbytes(n)
+ str = read(n)
+ if str == nil
+ raise EOFError, "End of file reached"
+ end
+ if str.size < n
+ raise IOError, "data truncated"
+ end
+ str
+ end
+ alias read_exactly_n_bytes readbytes
+end
+
+
+module EventMachine
+ module Protocols
+ # PROVISIONAL IMPLEMENTATION of an evented Postgres client.
+ # This implements version 3 of the Postgres wire protocol, which will work
+ # with any Postgres version from roughly 7.4 onward.
+ #
+ # Objective: we want to access Postgres databases without requiring threads.
+ # Until now this has been a problem because the Postgres client implementations
+ # have all made use of blocking I/O calls, which is incompatible with a
+ # thread-free evented model.
+ #
+ # But rather than re-implement the Postgres Wire3 protocol, we're taking advantage
+ # of the existing postgres-pr library, which was originally written by Michael
+ # Neumann but (at this writing) appears to be no longer maintained. Still, it's
+ # in basically a production-ready state, and the wire protocol isn't that complicated
+ # anyway.
+ #
+ # We're tucking in a bunch of require statements that may not be present in garden-variety
+ # EM installations. Until we find a good way to only require these if a program
+ # requires postgres, this file will need to be required explicitly.
+ #
+ # We need to monkeypatch StringIO because it lacks the #readbytes method needed
+ # by postgres-pr.
+ # The StringIO monkeypatch is lifted from the standard library readbytes.rb,
+ # which adds method #readbytes directly to class IO. But StringIO is not a subclass of IO.
+ # It is modified to raise an IOError instead of TruncatedDataException since the exception is unused.
+ #
+ # We cloned the handling of postgres messages from lib/postgres-pr/connection.rb
+ # in the postgres-pr library, and modified it for event-handling.
+ #
+ # TODO: The password handling in dispatch_conn_message is totally incomplete.
+ #
+ #
+ # We return Deferrables from the user-level operations surfaced by this interface.
+ # Experimentally, we're using the pattern of always returning a boolean value as the
+ # first argument of a deferrable callback to indicate success or failure. This is
+ # instead of the traditional pattern of calling Deferrable#succeed or #fail, and
+ # requiring the user to define both a callback and an errback function.
+ #
+ # === Usage
+ # EM.run {
+ # db = EM.connect_unix_domain( "/tmp/.s.PGSQL.5432", EM::P::Postgres3 )
+ # db.connect( dbname, username, psw ).callback do |status|
+ # if status
+ # db.query( "select * from some_table" ).callback do |status, result, errors|
+ # if status
+ # result.rows.each do |row|
+ # p row
+ # end
+ # end
+ # end
+ # end
+ # end
+ # }
+ class Postgres3 < EventMachine::Connection
+ include PostgresPR
+
+ def initialize
+ @data = ""
+ @params = {}
+ end
+
+ def connect db, user, psw=nil
+ d = EM::DefaultDeferrable.new
+ d.timeout 15
+
+ if @pending_query || @pending_conn
+ d.succeed false, "Operation already in progress"
+ else
+ @pending_conn = d
+ prms = {"user"=>user, "database"=>db}
+ @user = user
+ if psw
+ @password = psw
+ #prms["password"] = psw
+ end
+ send_data PostgresPR::StartupMessage.new( 3 << 16, prms ).dump
+ end
+
+ d
+ end
+
+ def query sql
+ d = EM::DefaultDeferrable.new
+ d.timeout 15
+
+ if @pending_query || @pending_conn
+ d.succeed false, "Operation already in progress"
+ else
+ @r = PostgresPR::Connection::Result.new
+ @e = []
+ @pending_query = d
+ send_data PostgresPR::Query.dump(sql)
+ end
+
+ d
+ end
+
+
+ def receive_data data
+ @data << data
+ while @data.length >= 5
+ pktlen = @data[1...5].unpack("N").first
+ if @data.length >= (1 + pktlen)
+ pkt = @data.slice!(0...(1+pktlen))
+ m = StringIO.open( pkt, "r" ) {|io| PostgresPR::Message.read( io ) }
+ if @pending_conn
+ dispatch_conn_message m
+ elsif @pending_query
+ dispatch_query_message m
+ else
+ raise "Unexpected message from database"
+ end
+ else
+ break # very important, break out of the while
+ end
+ end
+ end
+
+
+ def unbind
+ if o = (@pending_query || @pending_conn)
+ o.succeed false, "lost connection"
+ end
+ end
+
+ # Cloned and modified from the postgres-pr.
+ def dispatch_conn_message msg
+ case msg
+ when AuthentificationClearTextPassword
+ raise ArgumentError, "no password specified" if @password.nil?
+ send_data PasswordMessage.new(@password).dump
+
+ when AuthentificationCryptPassword
+ raise ArgumentError, "no password specified" if @password.nil?
+ send_data PasswordMessage.new(@password.crypt(msg.salt)).dump
+
+ when AuthentificationMD5Password
+ raise ArgumentError, "no password specified" if @password.nil?
+ require 'digest/md5'
+
+ m = Digest::MD5.hexdigest(@password + @user)
+ m = Digest::MD5.hexdigest(m + msg.salt)
+ m = 'md5' + m
+ send_data PasswordMessage.new(m).dump
+
+ when AuthentificationKerberosV4, AuthentificationKerberosV5, AuthentificationSCMCredential
+ raise "unsupported authentification"
+
+ when AuthentificationOk
+ when ErrorResponse
+ raise msg.field_values.join("\t")
+ when NoticeResponse
+ @notice_processor.call(msg) if @notice_processor
+ when ParameterStatus
+ @params[msg.key] = msg.value
+ when BackendKeyData
+ # TODO
+ #p msg
+ when ReadyForQuery
+ # TODO: use transaction status
+ pc,@pending_conn = @pending_conn,nil
+ pc.succeed true
+ else
+ raise "unhandled message type"
+ end
+ end
+
+ # Cloned and modified from the postgres-pr.
+ def dispatch_query_message msg
+ case msg
+ when DataRow
+ @r.rows << msg.columns
+ when CommandComplete
+ @r.cmd_tag = msg.cmd_tag
+ when ReadyForQuery
+ pq,@pending_query = @pending_query,nil
+ pq.succeed true, @r, @e
+ when RowDescription
+ @r.fields = msg.fields
+ when CopyInResponse
+ when CopyOutResponse
+ when EmptyQueryResponse
+ when ErrorResponse
+ # TODO
+ @e << msg
+ when NoticeResponse
+ @notice_processor.call(msg) if @notice_processor
+ else
+ # TODO
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/saslauth.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/saslauth.rb
new file mode 100644
index 0000000..9cabc51
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/saslauth.rb
@@ -0,0 +1,175 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage:: http://rubyeventmachine.com
+# Date:: 15 November 2006
+#
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+#
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+#
+
+module EventMachine
+ module Protocols
+
+ # Implements SASL authd.
+ # This is a very, very simple protocol that mimics the one used
+ # by saslauthd and pwcheck, two outboard daemons included in the
+ # standard SASL library distro.
+ # The only thing this is really suitable for is SASL PLAIN
+ # (user+password) authentication, but the SASL libs that are
+ # linked into standard servers (like imapd and sendmail) implement
+ # the other ones.
+ #
+ # SASL-auth is intended for reasonably fast operation inside a
+ # single machine, so it has no transport-security (although there
+ # have been multi-machine extensions incorporating transport-layer
+ # encryption).
+ #
+ # The standard saslauthd module generally runs privileged and does
+ # its work by referring to the system-account files.
+ #
+ # This feature was added to EventMachine to enable the development
+ # of custom authentication/authorization engines for standard servers.
+ #
+ # To use SASLauth, include it in a class that subclasses EM::Connection,
+ # and reimplement the validate method.
+ #
+ # The typical way to incorporate this module into an authentication
+ # daemon would be to set it as the handler for a UNIX-domain socket.
+ # The code might look like this:
+ #
+ # EM.start_unix_domain_server( "/var/run/saslauthd/mux", MyHandler )
+ # File.chmod( 0777, "/var/run/saslauthd/mux")
+ #
+ # The chmod is probably needed to ensure that unprivileged clients can
+ # access the UNIX-domain socket.
+ #
+ # It's also a very good idea to drop superuser privileges (if any), after
+ # the UNIX-domain socket has been opened.
+ #--
+ # Implementation details: assume the client can send us pipelined requests,
+ # and that the client will close the connection.
+ #
+ # The client sends us four values, each encoded as a two-byte length field in
+ # network order followed by the specified number of octets.
+ # The fields specify the username, password, service name (such as imap),
+ # and the "realm" name. We send back the barest minimum reply, a single
+ # field also encoded as a two-octet length in network order, followed by
+ # either "NO" or "OK" - simplicity itself.
+ #
+ # We enforce a maximum field size just as a sanity check.
+ # We do NOT automatically time out the connection.
+ #
+ # The code we use to parse out the values is ugly and probably slow.
+ # Improvements welcome.
+ #
+ module SASLauth
+
+ MaxFieldSize = 128*1024
+ def post_init
+ super
+ @sasl_data = ""
+ @sasl_values = []
+ end
+
+ def receive_data data
+ @sasl_data << data
+ while @sasl_data.length >= 2
+ len = (@sasl_data[0,2].unpack("n")).first
+ raise "SASL Max Field Length exceeded" if len > MaxFieldSize
+ if @sasl_data.length >= (len + 2)
+ @sasl_values << @sasl_data[2,len]
+ @sasl_data.slice!(0...(2+len))
+ if @sasl_values.length == 4
+ send_data( validate(*@sasl_values) ? "\0\002OK" : "\0\002NO" )
+ @sasl_values.clear
+ end
+ else
+ break
+ end
+ end
+ end
+
+ def validate username, psw, sysname, realm
+ p username
+ p psw
+ p sysname
+ p realm
+ true
+ end
+ end
+
+ # Implements the SASL authd client protocol.
+ # This is a very, very simple protocol that mimics the one used
+ # by saslauthd and pwcheck, two outboard daemons included in the
+ # standard SASL library distro.
+ # The only thing this is really suitable for is SASL PLAIN
+ # (user+password) authentication, but the SASL libs that are
+ # linked into standard servers (like imapd and sendmail) implement
+ # the other ones.
+ #
+ # You can use this module directly as a handler for EM Connections,
+ # or include it in a module or handler class of your own.
+ #
+ # First connect to a SASL server (it's probably a TCP server, or more
+ # likely a Unix-domain socket). Then call the #validate? method,
+ # passing at least a username and a password. #validate? returns
+ # a Deferrable which will either succeed or fail, depending
+ # on the status of the authentication operation.
+ #
+ module SASLauthclient
+ MaxFieldSize = 128*1024
+
+ def validate? username, psw, sysname=nil, realm=nil
+
+ str = [username, psw, sysname, realm].map {|m|
+ [(m || "").length, (m || "")]
+ }.flatten.pack( "nA*" * 4 )
+ send_data str
+
+ d = EM::DefaultDeferrable.new
+ @queries.unshift d
+ d
+ end
+
+ def post_init
+ @sasl_data = ""
+ @queries = []
+ end
+
+ def receive_data data
+ @sasl_data << data
+
+ while @sasl_data.length > 2
+ len = (@sasl_data[0,2].unpack("n")).first
+ raise "SASL Max Field Length exceeded" if len > MaxFieldSize
+ if @sasl_data.length >= (len + 2)
+ val = @sasl_data[2,len]
+ @sasl_data.slice!(0...(2+len))
+ q = @queries.pop
+ (val == "NO") ? q.fail : q.succeed
+ else
+ break
+ end
+ end
+ end
+ end
+
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/smtpclient.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/smtpclient.rb
new file mode 100644
index 0000000..25ad17a
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/smtpclient.rb
@@ -0,0 +1,394 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage:: http://rubyeventmachine.com
+# Date:: 16 July 2006
+#
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+#
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+
+require 'ostruct'
+
+module EventMachine
+ module Protocols
+
+ # Simple SMTP client
+ #
+ # @example
+ # email = EM::Protocols::SmtpClient.send(
+ # :domain=>"example.com",
+ # :host=>'localhost',
+ # :port=>25, # optional, defaults 25
+ # :starttls=>true, # use ssl
+ # :from=>"sender@example.com",
+ # :to=> ["to_1@example.com", "to_2@example.com"],
+ # :header=> {"Subject" => "This is a subject line"},
+ # :body=> "This is the body of the email"
+ # )
+ # email.callback{
+ # puts 'Email sent!'
+ # }
+ # email.errback{ |e|
+ # puts 'Email failed!'
+ # }
+ #
+ # Sending generated emails (using Mail)
+ #
+ # mail = Mail.new do
+ # from 'alice@example.com'
+ # to 'bob@example.com'
+ # subject 'This is a test email'
+ # body 'Hello, world!'
+ # end
+ #
+ # email = EM::P::SmtpClient.send(
+ # :domain=>'example.com',
+ # :from=>mail.from.first,
+ # :to=>mail.to,
+ # :message=>mail.to_s
+ # )
+ #
+ class SmtpClient < Connection
+ include EventMachine::Deferrable
+ include EventMachine::Protocols::LineText2
+
+ def initialize
+ @succeeded = nil
+ @responder = nil
+ @code = nil
+ @msg = nil
+ end
+
+ # :host => required String
+ # a string containing the IP address or host name of the SMTP server to connect to.
+ # :port => optional
+ # defaults to 25.
+ # :domain => required String
+ # This is passed as the argument to the EHLO command.
+ # :starttls => optional Boolean
+ # If it evaluates true, then the client will initiate STARTTLS with
+ # the server, and abort the connection if the negotiation doesn't succeed.
+ # TODO, need to be able to pass certificate parameters with this option.
+ # :auth => optional Hash of auth parameters
+ # If not given, then no auth will be attempted.
+ # (In that case, the connection will be aborted if the server requires auth.)
+ # Specify the hash value :type to determine the auth type, along with additional parameters
+ # depending on the type.
+ # Currently only :type => :plain is supported. Pass additional parameters :username (String),
+ # and :password (either a String or a Proc that will be called at auth-time).
+ #
+ # @example
+ # :auth => {:type=>:plain, :username=>"mickey@disney.com", :password=>"mouse"}
+ #
+ # :from => required String
+ # Specifies the sender of the message. Will be passed as the argument
+ # to the MAIL FROM. Do NOT enclose the argument in angle-bracket (<>) characters.
+ # The connection will abort if the server rejects the value.
+ # :to => required String or Array of Strings
+ # The recipient(s) of the message. Do NOT enclose
+ # any of the values in angle-brackets (<>) characters. It's NOT a fatal error if one or more
+ # recipients are rejected by the server. (Of course, if ALL of them are, the server will most
+ # likely trigger an error when we try to send data.) An array of codes containing the status
+ # of each requested recipient is available after the call completes. TODO, we should define
+ # an overridable stub that will be called on rejection of a recipient or a sender, giving
+ # user code the chance to try again or abort the connection.
+ #
+ # One of either :message, :content, or :header and :body is required:
+ #
+ # :message => String
+ # A valid RFC2822 Internet Message.
+ # :content => String
+ # Raw data which MUST be in correct SMTP body format, with escaped leading dots and a trailing
+ # dot line.
+ # :header => String or Hash of values to be transmitted in the header of the message.
+ # The hash keys are the names of the headers (do NOT append a trailing colon), and the values
+ # are strings containing the header values. TODO, support Arrays of header values, which would
+ # cause us to send that specific header line more than once.
+ #
+ # @example
+ # :header => {"Subject" => "Bogus", "CC" => "myboss@example.com"}
+ #
+ # :body => Optional String or Array of Strings, defaults blank.
+ # This will be passed as the body of the email message.
+ # TODO, this needs to be significantly beefed up. As currently written, this requires the caller
+ # to properly format the input into CRLF-delimited lines of 7-bit characters in the standard
+ # SMTP transmission format. We need to be able to automatically convert binary data, and add
+ # correct line-breaks to text data.
+ #
+ # :verbose => Optional.
+ # If true, will cause a lot of information (including the server-side of the
+ # conversation) to be dumped to $>.
+ #
+ def self.send args={}
+ args[:port] ||= 25
+ args[:body] ||= ""
+
+=begin
+ (I don't think it's possible for EM#connect to throw an exception under normal
+ circumstances, so this original code is stubbed out. A connect-failure will result
+ in the #unbind method being called without calling #connection_completed.)
+ begin
+ EventMachine.connect( args[:host], args[:port], self) {|c|
+ # According to the EM docs, we will get here AFTER post_init is called.
+ c.args = args
+ c.set_comm_inactivity_timeout 60
+ }
+ rescue
+ # We'll get here on a connect error. This code mimics the effect
+ # of a call to invoke_internal_error. Would be great to DRY this up.
+ # (Actually, it may be that we never get here, if EM#connect catches
+ # its errors internally.)
+ d = EM::DefaultDeferrable.new
+ d.set_deferred_status(:failed, {:error=>[:connect, 500, "unable to connect to server"]})
+ d
+ end
+=end
+ EventMachine.connect( args[:host], args[:port], self) {|c|
+ # According to the EM docs, we will get here AFTER post_init is called.
+ c.args = args
+ c.set_comm_inactivity_timeout 60
+ }
+ end
+
+ attr_writer :args
+
+ # @private
+ def post_init
+ @return_values = OpenStruct.new
+ @return_values.start_time = Time.now
+ end
+
+ # @private
+ def connection_completed
+ @responder = :receive_signon
+ @msg = []
+ end
+
+ # We can get here in a variety of ways, all of them being failures unless
+ # the @succeeded flag is set. If a protocol success was recorded, then don't
+ # set a deferred success because the caller will already have done it
+ # (no need to wait until the connection closes to invoke the callbacks).
+ #
+ # @private
+ def unbind
+ unless @succeeded
+ @return_values.elapsed_time = Time.now - @return_values.start_time
+ @return_values.responder = @responder
+ @return_values.code = @code
+ @return_values.message = @msg
+ set_deferred_status(:failed, @return_values)
+ end
+ end
+
+ # @private
+ def receive_line ln
+ $>.puts ln if @args[:verbose]
+ @range = ln[0...1].to_i
+ @code = ln[0...3].to_i
+ @msg << ln[4..-1]
+ unless ln[3...4] == '-'
+ $>.puts @responder if @args[:verbose]
+ send @responder
+ @msg.clear
+ end
+ end
+
+ private
+
+ # We encountered an error from the server and will close the connection.
+ # Use the error and message the server returned.
+ #
+ def invoke_error
+ @return_values.elapsed_time = Time.now - @return_values.start_time
+ @return_values.responder = @responder
+ @return_values.code = @code
+ @return_values.message = @msg
+ set_deferred_status :failed, @return_values
+ send_data "QUIT\r\n"
+ close_connection_after_writing
+ end
+
+ # We encountered an error on our side of the protocol and will close the connection.
+ # Use an extra-protocol error code (900) and use the message from the caller.
+ #
+ def invoke_internal_error msg = "???"
+ @return_values.elapsed_time = Time.now - @return_values.start_time
+ @return_values.responder = @responder
+ @return_values.code = 900
+ @return_values.message = msg
+ set_deferred_status :failed, @return_values
+ send_data "QUIT\r\n"
+ close_connection_after_writing
+ end
+
+ def send_ehlo
+ send_data "EHLO #{@args[:domain]}\r\n"
+ end
+
+ def receive_signon
+ return invoke_error unless @range == 2
+ send_ehlo
+ @responder = :receive_ehlo_response
+ end
+ def receive_ehlo_response
+ return invoke_error unless @range == 2
+ @server_caps = @msg
+ invoke_starttls
+ end
+
+ def invoke_starttls
+ if @args[:starttls]
+ # It would be more sociable to first ask if @server_caps contains
+ # the string "STARTTLS" before we invoke it, but hey, life's too short.
+ send_data "STARTTLS\r\n"
+ @responder = :receive_starttls_response
+ else
+ invoke_auth
+ end
+ end
+ def receive_starttls_response
+ return invoke_error unless @range == 2
+ start_tls
+ invoke_ehlo_over_tls
+ end
+
+ def invoke_ehlo_over_tls
+ send_ehlo
+ @responder = :receive_ehlo_over_tls_response
+ end
+ def receive_ehlo_over_tls_response
+ return invoke_error unless @range == 2
+ invoke_auth
+ end
+
+ # Perform an authentication. If the caller didn't request one, then fall through
+ # to the mail-from state.
+ def invoke_auth
+ if @args[:auth]
+ if @args[:auth][:type] == :plain
+ psw = @args[:auth][:password]
+ if psw.respond_to?(:call)
+ psw = psw.call
+ end
+ #str = Base64::encode64("\0#{@args[:auth][:username]}\0#{psw}").chomp
+ str = ["\0#{@args[:auth][:username]}\0#{psw}"].pack("m").gsub(/\n/, '')
+ send_data "AUTH PLAIN #{str}\r\n"
+ @responder = :receive_auth_response
+ else
+ return invoke_internal_error("unsupported auth type")
+ end
+ else
+ invoke_mail_from
+ end
+ end
+ def receive_auth_response
+ return invoke_error unless @range == 2
+ invoke_mail_from
+ end
+
+ def invoke_mail_from
+ send_data "MAIL FROM: <#{@args[:from]}>\r\n"
+ @responder = :receive_mail_from_response
+ end
+ def receive_mail_from_response
+ return invoke_error unless @range == 2
+ invoke_rcpt_to
+ end
+
+ def invoke_rcpt_to
+ @rcpt_responses ||= []
+ l = @rcpt_responses.length
+ to = @args[:to].is_a?(Array) ? @args[:to] : [@args[:to].to_s]
+ if l < to.length
+ send_data "RCPT TO: <#{to[l]}>\r\n"
+ @responder = :receive_rcpt_to_response
+ else
+ e = @rcpt_responses.select {|rr| rr.last == 2}
+ if e and e.length > 0
+ invoke_data
+ else
+ invoke_error
+ end
+ end
+ end
+ def receive_rcpt_to_response
+ @rcpt_responses << [@code, @msg, @range]
+ invoke_rcpt_to
+ end
+
+ def escape_leading_dots(s)
+ s.gsub(/^\./, '..')
+ end
+
+ def invoke_data
+ send_data "DATA\r\n"
+ @responder = :receive_data_response
+ end
+ def receive_data_response
+ return invoke_error unless @range == 3
+
+ # The data to send can be given in either @args[:message], @args[:content], or the
+ # combination of @args[:header] and @args[:body].
+ #
+ # - @args[:message] (String) MUST be a valid RFC2822 Internet Message
+ #
+ # - @args[:content] (String) MUST be in correct SMTP body format, with escaped
+ # leading dots and a trailing dot line
+ #
+ # - @args[:header] (Hash or String)
+ # - @args[:body] (Array or String)
+ if @args[:message]
+ send_data escape_leading_dots(@args[:message].to_s)
+ send_data "\r\n.\r\n"
+ elsif @args[:content]
+ send_data @args[:content].to_s
+ else
+ # The header can be a hash or an array.
+ if @args[:header].is_a?(Hash)
+ (@args[:header] || {}).each {|k,v| send_data escape_leading_dots("#{k}: #{v}\r\n") }
+ else
+ send_data escape_leading_dots(@args[:header].to_s)
+ end
+ send_data "\r\n"
+
+ if @args[:body].is_a?(Array)
+ @args[:body].each {|e| send_data escape_leading_dots(e)}
+ else
+ send_data escape_leading_dots(@args[:body].to_s)
+ end
+
+ send_data "\r\n.\r\n"
+ end
+
+ @responder = :receive_message_response
+ end
+ def receive_message_response
+ return invoke_error unless @range == 2
+ send_data "QUIT\r\n"
+ close_connection_after_writing
+ @succeeded = true
+ @return_values.elapsed_time = Time.now - @return_values.start_time
+ @return_values.responder = @responder
+ @return_values.code = @code
+ @return_values.message = @msg
+ set_deferred_status :succeeded, @return_values
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/smtpserver.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/smtpserver.rb
new file mode 100644
index 0000000..e0f1f05
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/smtpserver.rb
@@ -0,0 +1,666 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage:: http://rubyeventmachine.com
+# Date:: 16 July 2006
+#
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+#
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+
+module EventMachine
+ module Protocols
+
+ # This is a protocol handler for the server side of SMTP.
+ # It's NOT a complete SMTP server obeying all the semantics of servers conforming to
+ # RFC2821. Rather, it uses overridable method stubs to communicate protocol states
+ # and data to user code. User code is responsible for doing the right things with the
+ # data in order to get complete and correct SMTP server behavior.
+ #
+ # Simple SMTP server example:
+ #
+ # class EmailServer < EM::P::SmtpServer
+ # def receive_plain_auth(user, pass)
+ # true
+ # end
+ #
+ # def get_server_domain
+ # "mock.smtp.server.local"
+ # end
+ #
+ # def get_server_greeting
+ # "mock smtp server greets you with impunity"
+ # end
+ #
+ # def receive_sender(sender)
+ # current.sender = sender
+ # true
+ # end
+ #
+ # def receive_recipient(recipient)
+ # current.recipient = recipient
+ # true
+ # end
+ #
+ # def receive_message
+ # current.received = true
+ # current.completed_at = Time.now
+ #
+ # p [:received_email, current]
+ # @current = OpenStruct.new
+ # true
+ # end
+ #
+ # def receive_ehlo_domain(domain)
+ # @ehlo_domain = domain
+ # true
+ # end
+ #
+ # def receive_data_command
+ # current.data = ""
+ # true
+ # end
+ #
+ # def receive_data_chunk(data)
+ # current.data << data.join("\n")
+ # true
+ # end
+ #
+ # def receive_transaction
+ # if @ehlo_domain
+ # current.ehlo_domain = @ehlo_domain
+ # @ehlo_domain = nil
+ # end
+ # true
+ # end
+ #
+ # def current
+ # @current ||= OpenStruct.new
+ # end
+ #
+ # def self.start(host = 'localhost', port = 1025)
+ # require 'ostruct'
+ # @server = EM.start_server host, port, self
+ # end
+ #
+ # def self.stop
+ # if @server
+ # EM.stop_server @server
+ # @server = nil
+ # end
+ # end
+ #
+ # def self.running?
+ # !!@server
+ # end
+ # end
+ #
+ # EM.run{ EmailServer.start }
+ #
+ #--
+ # Useful paragraphs in RFC-2821:
+ # 4.3.2: Concise list of command-reply sequences, in essence a text representation
+ # of the command state-machine.
+ #
+ # STARTTLS is defined in RFC2487.
+ # Observe that there are important rules governing whether a publicly-referenced server
+ # (meaning one whose Internet address appears in public MX records) may require the
+ # non-optional use of TLS.
+ # Non-optional TLS does not apply to EHLO, NOOP, QUIT or STARTTLS.
+ class SmtpServer < EventMachine::Connection
+ include Protocols::LineText2
+
+ HeloRegex = /\AHELO\s*/i
+ EhloRegex = /\AEHLO\s*/i
+ QuitRegex = /\AQUIT/i
+ MailFromRegex = /\AMAIL FROM:\s*/i
+ RcptToRegex = /\ARCPT TO:\s*/i
+ DataRegex = /\ADATA/i
+ NoopRegex = /\ANOOP/i
+ RsetRegex = /\ARSET/i
+ VrfyRegex = /\AVRFY\s+/i
+ ExpnRegex = /\AEXPN\s+/i
+ HelpRegex = /\AHELP/i
+ StarttlsRegex = /\ASTARTTLS/i
+ AuthRegex = /\AAUTH\s+/i
+
+
+ # Class variable containing default parameters that can be overridden
+ # in application code.
+ # Individual objects of this class will make an instance-local copy of
+ # the class variable, so that they can be reconfigured on a per-instance
+ # basis.
+ #
+ # Chunksize is the number of data lines we'll buffer before
+ # sending them to the application. TODO, make this user-configurable.
+ #
+ @@parms = {
+ :chunksize => 4000,
+ :verbose => false
+ }
+ def self.parms= parms={}
+ @@parms.merge!(parms)
+ end
+
+
+
+ def initialize *args
+ super
+ @parms = @@parms
+ init_protocol_state
+ end
+
+ def parms= parms={}
+ @parms.merge!(parms)
+ end
+
+ # In SMTP, the server talks first. But by a (perhaps flawed) axiom in EM,
+ # #post_init will execute BEFORE the block passed to #start_server, for any
+ # given accepted connection. Since in this class we'll probably be getting
+ # a lot of initialization parameters, we want the guts of post_init to
+ # run AFTER the application has initialized the connection object. So we
+ # use a spawn to schedule the post_init to run later.
+ # It's a little weird, I admit. A reasonable alternative would be to set
+ # parameters as a class variable and to do that before accepting any connections.
+ #
+ # OBSOLETE, now we have @@parms. But the spawn is nice to keep as an illustration.
+ #
+ def post_init
+ #send_data "220 #{get_server_greeting}\r\n" (ORIGINAL)
+ #(EM.spawn {|x| x.send_data "220 #{x.get_server_greeting}\r\n"}).notify(self)
+ (EM.spawn {|x| x.send_server_greeting}).notify(self)
+ end
+
+ def send_server_greeting
+ send_data "220 #{get_server_greeting}\r\n"
+ end
+
+ def receive_line ln
+ @@parms[:verbose] and $>.puts ">>> #{ln}"
+
+ return process_data_line(ln) if @state.include?(:data)
+ return process_auth_line(ln) if @state.include?(:auth_incomplete)
+
+ case ln
+ when EhloRegex
+ process_ehlo $'.dup
+ when HeloRegex
+ process_helo $'.dup
+ when MailFromRegex
+ process_mail_from $'.dup
+ when RcptToRegex
+ process_rcpt_to $'.dup
+ when DataRegex
+ process_data
+ when RsetRegex
+ process_rset
+ when VrfyRegex
+ process_vrfy
+ when ExpnRegex
+ process_expn
+ when HelpRegex
+ process_help
+ when NoopRegex
+ process_noop
+ when QuitRegex
+ process_quit
+ when StarttlsRegex
+ process_starttls
+ when AuthRegex
+ process_auth $'.dup
+ else
+ process_unknown
+ end
+ end
+
+ # TODO - implement this properly, the implementation is a stub!
+ def process_help
+ send_data "250 Ok, but unimplemented\r\n"
+ end
+
+ # RFC2821, 3.5.3 Meaning of VRFY or EXPN Success Response:
+ # A server MUST NOT return a 250 code in response to a VRFY or EXPN
+ # command unless it has actually verified the address. In particular,
+ # a server MUST NOT return 250 if all it has done is to verify that the
+ # syntax given is valid. In that case, 502 (Command not implemented)
+ # or 500 (Syntax error, command unrecognized) SHOULD be returned.
+ #
+ # TODO - implement this properly, the implementation is a stub!
+ def process_vrfy
+ send_data "502 Command not implemented\r\n"
+ end
+ # TODO - implement this properly, the implementation is a stub!
+ def process_expn
+ send_data "502 Command not implemented\r\n"
+ end
+
+ #--
+ # This is called at several points to restore the protocol state
+ # to a pre-transaction state. In essence, we "forget" having seen
+ # any valid command except EHLO and STARTTLS.
+ # We also have to callback user code, in case they're keeping track
+ # of senders, recipients, and whatnot.
+ #
+ # We try to follow the convention of avoiding the verb "receive" for
+ # internal method names except receive_line (which we inherit), and
+ # using only receive_xxx for user-overridable stubs.
+ #
+ # init_protocol_state is called when we initialize the connection as
+ # well as during reset_protocol_state. It does NOT call the user
+ # override method. This enables us to promise the users that they
+ # won't see the overridable fire except after EHLO and RSET, and
+ # after a message has been received. Although the latter may be wrong.
+ # The standard may allow multiple DATA segments with the same set of
+ # senders and recipients.
+ #
+ def reset_protocol_state
+ init_protocol_state
+ s,@state = @state,[]
+ @state << :starttls if s.include?(:starttls)
+ @state << :ehlo if s.include?(:ehlo)
+ receive_transaction
+ end
+ def init_protocol_state
+ @state ||= []
+ end
+
+
+ #--
+ # EHLO/HELO is always legal, per the standard. On success
+ # it always clears buffers and initiates a mail "transaction."
+ # Which means that a MAIL FROM must follow.
+ #
+ # Per the standard, an EHLO/HELO or a RSET "initiates" an email
+ # transaction. Thereafter, MAIL FROM must be received before
+ # RCPT TO, before DATA. Not sure what this specific ordering
+ # achieves semantically, but it does make it easier to
+ # implement. We also support user-specified requirements for
+ # STARTTLS and AUTH. We make it impossible to proceed to MAIL FROM
+ # without fulfilling tls and/or auth, if the user specified either
+ # or both as required. We need to check the extension standard
+ # for auth to see if a credential is discarded after a RSET along
+ # with all the rest of the state. We'll behave as if it is.
+ # Now clearly, we can't discard tls after its been negotiated
+ # without dropping the connection, so that flag doesn't get cleared.
+ #
+ def process_ehlo domain
+ if receive_ehlo_domain domain
+ send_data "250-#{get_server_domain}\r\n"
+ if @@parms[:starttls]
+ send_data "250-STARTTLS\r\n"
+ end
+ if @@parms[:auth]
+ send_data "250-AUTH PLAIN\r\n"
+ end
+ send_data "250-NO-SOLICITING\r\n"
+ # TODO, size needs to be configurable.
+ send_data "250 SIZE 20000000\r\n"
+ reset_protocol_state
+ @state << :ehlo
+ else
+ send_data "550 Requested action not taken\r\n"
+ end
+ end
+
+ def process_helo domain
+ if receive_ehlo_domain domain.dup
+ send_data "250 #{get_server_domain}\r\n"
+ reset_protocol_state
+ @state << :ehlo
+ else
+ send_data "550 Requested action not taken\r\n"
+ end
+ end
+
+ def process_quit
+ send_data "221 Ok\r\n"
+ close_connection_after_writing
+ end
+
+ def process_noop
+ send_data "250 Ok\r\n"
+ end
+
+ def process_unknown
+ send_data "500 Unknown command\r\n"
+ end
+
+ #--
+ # So far, only AUTH PLAIN is supported but we should do at least LOGIN as well.
+ # TODO, support clients that send AUTH PLAIN with no parameter, expecting a 3xx
+ # response and a continuation of the auth conversation.
+ #
+ def process_auth str
+ if @state.include?(:auth)
+ send_data "503 auth already issued\r\n"
+ elsif str =~ /\APLAIN\s?/i
+ if $'.length == 0
+ # we got a partial response, so let the client know to send the rest
+ @state << :auth_incomplete
+ send_data("334 \r\n")
+ else
+ # we got the initial response, so go ahead & process it
+ process_auth_line($')
+ end
+ #elsif str =~ /\ALOGIN\s+/i
+ else
+ send_data "504 auth mechanism not available\r\n"
+ end
+ end
+
+ def process_auth_line(line)
+ plain = line.unpack("m").first
+ _,user,psw = plain.split("\000")
+
+ succeeded = proc {
+ send_data "235 authentication ok\r\n"
+ @state << :auth
+ }
+ failed = proc {
+ send_data "535 invalid authentication\r\n"
+ }
+ auth = receive_plain_auth user,psw
+
+ if auth.respond_to?(:callback)
+ auth.callback(&succeeded)
+ auth.errback(&failed)
+ else
+ (auth ? succeeded : failed).call
+ end
+
+ @state.delete :auth_incomplete
+ end
+
+ #--
+ # Unusually, we can deal with a Deferrable returned from the user application.
+ # This was added to deal with a special case in a particular application, but
+ # it would be a nice idea to add it to the other user-code callbacks.
+ #
+ def process_data
+ unless @state.include?(:rcpt)
+ send_data "503 Operation sequence error\r\n"
+ else
+ succeeded = proc {
+ send_data "354 Send it\r\n"
+ @state << :data
+ @databuffer = []
+ }
+ failed = proc {
+ send_data "550 Operation failed\r\n"
+ }
+
+ d = receive_data_command
+
+ if d.respond_to?(:callback)
+ d.callback(&succeeded)
+ d.errback(&failed)
+ else
+ (d ? succeeded : failed).call
+ end
+ end
+ end
+
+ def process_rset
+ reset_protocol_state
+ receive_reset
+ send_data "250 Ok\r\n"
+ end
+
+ def unbind
+ connection_ended
+ end
+
+ #--
+ # STARTTLS may not be issued before EHLO, or unless the user has chosen
+ # to support it.
+ #
+ # If :starttls_options is present and :starttls is set in the parms
+ # pass the options in :starttls_options to start_tls. Do this if you want to use
+ # your own certificate
+ # e.g. {:cert_chain_file => "/etc/ssl/cert.pem", :private_key_file => "/etc/ssl/private/cert.key"}
+
+ def process_starttls
+ if @@parms[:starttls]
+ if @state.include?(:starttls)
+ send_data "503 TLS Already negotiated\r\n"
+ elsif ! @state.include?(:ehlo)
+ send_data "503 EHLO required before STARTTLS\r\n"
+ else
+ send_data "220 Start TLS negotiation\r\n"
+ start_tls(@@parms[:starttls_options] || {})
+ @state << :starttls
+ end
+ else
+ process_unknown
+ end
+ end
+
+
+ #--
+ # Requiring TLS is touchy, cf RFC2784.
+ # Requiring AUTH seems to be much more reasonable.
+ # We don't currently support any notion of deriving an authentication from the TLS
+ # negotiation, although that would certainly be reasonable.
+ # We DON'T allow MAIL FROM to be given twice.
+ # We DON'T enforce all the various rules for validating the sender or
+ # the reverse-path (like whether it should be null), and notifying the reverse
+ # path in case of delivery problems. All of that is left to the calling application.
+ #
+ def process_mail_from sender
+ if (@@parms[:starttls]==:required and !@state.include?(:starttls))
+ send_data "550 This server requires STARTTLS before MAIL FROM\r\n"
+ elsif (@@parms[:auth]==:required and !@state.include?(:auth))
+ send_data "550 This server requires authentication before MAIL FROM\r\n"
+ elsif @state.include?(:mail_from)
+ send_data "503 MAIL already given\r\n"
+ else
+ unless receive_sender sender
+ send_data "550 sender is unacceptable\r\n"
+ else
+ send_data "250 Ok\r\n"
+ @state << :mail_from
+ end
+ end
+ end
+
+ #--
+ # Since we require :mail_from to have been seen before we process RCPT TO,
+ # we don't need to repeat the tests for TLS and AUTH.
+ # Note that we don't remember or do anything else with the recipients.
+ # All of that is on the user code.
+ # TODO: we should enforce user-definable limits on the total number of
+ # recipients per transaction.
+ # We might want to make sure that a given recipient is only seen once, but
+ # for now we'll let that be the user's problem.
+ #
+ # User-written code can return a deferrable from receive_recipient.
+ #
+ def process_rcpt_to rcpt
+ unless @state.include?(:mail_from)
+ send_data "503 MAIL is required before RCPT\r\n"
+ else
+ succeeded = proc {
+ send_data "250 Ok\r\n"
+ @state << :rcpt unless @state.include?(:rcpt)
+ }
+ failed = proc {
+ send_data "550 recipient is unacceptable\r\n"
+ }
+
+ d = receive_recipient rcpt
+
+ if d.respond_to?(:set_deferred_status)
+ d.callback(&succeeded)
+ d.errback(&failed)
+ else
+ (d ? succeeded : failed).call
+ end
+
+=begin
+ unless receive_recipient rcpt
+ send_data "550 recipient is unacceptable\r\n"
+ else
+ send_data "250 Ok\r\n"
+ @state << :rcpt unless @state.include?(:rcpt)
+ end
+=end
+ end
+ end
+
+
+ # Send the incoming data to the application one chunk at a time, rather than
+ # one line at a time. That lets the application be a little more flexible about
+ # storing to disk, etc.
+ # Since we clear the chunk array every time we submit it, the caller needs to be
+ # aware to do things like dup it if he wants to keep it around across calls.
+ #
+ # Resets the transaction upon disposition of the incoming message.
+ # RFC5321 says this about the MAIL FROM command:
+ # "This command tells the SMTP-receiver that a new mail transaction is
+ # starting and to reset all its state tables and buffers, including any
+ # recipients or mail data."
+ #
+ # Equivalent behaviour is implemented by resetting after a completed transaction.
+ #
+ # User-written code can return a Deferrable as a response from receive_message.
+ #
+ def process_data_line ln
+ if ln == "."
+ if @databuffer.length > 0
+ receive_data_chunk @databuffer
+ @databuffer.clear
+ end
+
+
+ succeeded = proc {
+ send_data "250 Message accepted\r\n"
+ reset_protocol_state
+ }
+ failed = proc {
+ send_data "550 Message rejected\r\n"
+ reset_protocol_state
+ }
+ d = receive_message
+
+ if d.respond_to?(:set_deferred_status)
+ d.callback(&succeeded)
+ d.errback(&failed)
+ else
+ (d ? succeeded : failed).call
+ end
+
+ @state.delete :data
+ else
+ # slice off leading . if any
+ ln.slice!(0...1) if ln[0] == ?.
+ @databuffer << ln
+ if @databuffer.length > @@parms[:chunksize]
+ receive_data_chunk @databuffer
+ @databuffer.clear
+ end
+ end
+ end
+
+
+ #------------------------------------------
+ # Everything from here on can be overridden in user code.
+
+ # The greeting returned in the initial connection message to the client.
+ def get_server_greeting
+ "EventMachine SMTP Server"
+ end
+ # The domain name returned in the first line of the response to a
+ # successful EHLO or HELO command.
+ def get_server_domain
+ "Ok EventMachine SMTP Server"
+ end
+
+ # A false response from this user-overridable method will cause a
+ # 550 error to be returned to the remote client.
+ #
+ def receive_ehlo_domain domain
+ true
+ end
+
+ # Return true or false to indicate that the authentication is acceptable.
+ def receive_plain_auth user, password
+ true
+ end
+
+ # Receives the argument of the MAIL FROM command. Return false to
+ # indicate to the remote client that the sender is not accepted.
+ # This can only be successfully called once per transaction.
+ #
+ def receive_sender sender
+ true
+ end
+
+ # Receives the argument of a RCPT TO command. Can be given multiple
+ # times per transaction. Return false to reject the recipient.
+ #
+ def receive_recipient rcpt
+ true
+ end
+
+ # Sent when the remote peer issues the RSET command.
+ # Since RSET is not allowed to fail (according to the protocol),
+ # we ignore any return value from user overrides of this method.
+ #
+ def receive_reset
+ end
+
+ # Sent when the remote peer has ended the connection.
+ #
+ def connection_ended
+ end
+
+ # Called when the remote peer sends the DATA command.
+ # Returning false will cause us to send a 550 error to the peer.
+ # This can be useful for dealing with problems that arise from processing
+ # the whole set of sender and recipients.
+ #
+ def receive_data_command
+ true
+ end
+
+ # Sent when data from the remote peer is available. The size can be controlled
+ # by setting the :chunksize parameter. This call can be made multiple times.
+ # The goal is to strike a balance between sending the data to the application one
+ # line at a time, and holding all of a very large message in memory.
+ #
+ def receive_data_chunk data
+ @smtps_msg_size ||= 0
+ @smtps_msg_size += data.join.length
+ STDERR.write "<#{@smtps_msg_size}>"
+ end
+
+ # Sent after a message has been completely received. User code
+ # must return true or false to indicate whether the message has
+ # been accepted for delivery.
+ def receive_message
+ @@parms[:verbose] and $>.puts "Received complete message"
+ true
+ end
+
+ # This is called when the protocol state is reset. It happens
+ # when the remote client calls EHLO/HELO or RSET.
+ def receive_transaction
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/socks4.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/socks4.rb
new file mode 100644
index 0000000..132f320
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/socks4.rb
@@ -0,0 +1,66 @@
+module EventMachine
+ module Protocols
+ # Basic SOCKS v4 client implementation
+ #
+ # Use as you would any regular connection:
+ #
+ # class MyConn < EM::P::Socks4
+ # def post_init
+ # send_data("sup")
+ # end
+ #
+ # def receive_data(data)
+ # send_data("you said: #{data}")
+ # end
+ # end
+ #
+ # EM.connect socks_host, socks_port, MyConn, host, port
+ #
+ class Socks4 < Connection
+ def initialize(host, port)
+ @host = Socket.gethostbyname(host).last
+ @port = port
+ @socks_error_code = nil
+ @buffer = ''
+ setup_methods
+ end
+
+ def setup_methods
+ class << self
+ def post_init; socks_post_init; end
+ def receive_data(*a); socks_receive_data(*a); end
+ end
+ end
+
+ def restore_methods
+ class << self
+ remove_method :post_init
+ remove_method :receive_data
+ end
+ end
+
+ def socks_post_init
+ header = [4, 1, @port, @host, 0].flatten.pack("CCnA4C")
+ send_data(header)
+ end
+
+ def socks_receive_data(data)
+ @buffer << data
+ return if @buffer.size < 8
+
+ header_resp = @buffer.slice! 0, 8
+ _, r = header_resp.unpack("cc")
+ if r != 90
+ @socks_error_code = r
+ close_connection
+ return
+ end
+
+ restore_methods
+
+ post_init
+ receive_data(@buffer) unless @buffer.empty?
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/stomp.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/stomp.rb
new file mode 100644
index 0000000..ca6f078
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/stomp.rb
@@ -0,0 +1,205 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage:: http://rubyeventmachine.com
+# Date:: 15 November 2006
+#
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+#
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+#
+
+module EventMachine
+ module Protocols
+
+ # Implements Stomp (http://docs.codehaus.org/display/STOMP/Protocol).
+ #
+ # == Usage example
+ #
+ # module StompClient
+ # include EM::Protocols::Stomp
+ #
+ # def connection_completed
+ # connect :login => 'guest', :passcode => 'guest'
+ # end
+ #
+ # def receive_msg msg
+ # if msg.command == "CONNECTED"
+ # subscribe '/some/topic'
+ # else
+ # p ['got a message', msg]
+ # puts msg.body
+ # end
+ # end
+ # end
+ #
+ # EM.run{
+ # EM.connect 'localhost', 61613, StompClient
+ # }
+ #
+ module Stomp
+ include LineText2
+
+ class Message
+ # The command associated with the message, usually 'CONNECTED' or 'MESSAGE'
+ attr_accessor :command
+ # Hash containing headers such as destination and message-id
+ attr_accessor :header
+ alias :headers :header
+ # Body of the message
+ attr_accessor :body
+
+ # @private
+ def initialize
+ @header = {}
+ @state = :precommand
+ @content_length = nil
+ end
+ # @private
+ def consume_line line
+ if @state == :precommand
+ unless line =~ /\A\s*\Z/
+ @command = line
+ @state = :headers
+ end
+ elsif @state == :headers
+ if line == ""
+ if @content_length
+ yield( [:sized_text, @content_length+1] )
+ else
+ @state = :body
+ yield( [:unsized_text] )
+ end
+ elsif line =~ /\A([^:]+):(.+)\Z/
+ k = $1.dup.strip
+ v = $2.dup.strip
+ @header[k] = v
+ if k == "content-length"
+ @content_length = v.to_i
+ end
+ else
+ # This is a protocol error. How to signal it?
+ end
+ elsif @state == :body
+ @body = line
+ yield( [:dispatch] )
+ end
+ end
+ end
+
+ # @private
+ def send_frame verb, headers={}, body=""
+ body = body.to_s
+ ary = [verb, "\n"]
+ body_bytesize = body.bytesize if body.respond_to? :bytesize
+ body_bytesize ||= body.size
+ headers.each {|k,v| ary << "#{k}:#{v}\n" }
+ ary << "content-length: #{body_bytesize}\n"
+ ary << "content-type: text/plain; charset=UTF-8\n" unless headers.has_key? 'content-type'
+ ary << "\n"
+ ary << body
+ ary << "\0"
+ send_data ary.join
+ end
+
+ # @private
+ def receive_line line
+ @stomp_initialized || init_message_reader
+ @stomp_message.consume_line(line) {|outcome|
+ if outcome.first == :sized_text
+ set_text_mode outcome[1]
+ elsif outcome.first == :unsized_text
+ set_delimiter "\0"
+ elsif outcome.first == :dispatch
+ receive_msg(@stomp_message) if respond_to?(:receive_msg)
+ init_message_reader
+ end
+ }
+ end
+
+ # @private
+ def receive_binary_data data
+ @stomp_message.body = data[0..-2]
+ receive_msg(@stomp_message) if respond_to?(:receive_msg)
+ init_message_reader
+ end
+
+ # @private
+ def init_message_reader
+ @stomp_initialized = true
+ set_delimiter "\n"
+ set_line_mode
+ @stomp_message = Message.new
+ end
+
+ # Invoked with an incoming Stomp::Message received from the STOMP server
+ def receive_msg msg
+ # stub, overwrite this in your handler
+ end
+
+ # CONNECT command, for authentication
+ #
+ # connect :login => 'guest', :passcode => 'guest'
+ #
+ def connect parms={}
+ send_frame "CONNECT", parms
+ end
+
+ # SEND command, for publishing messages to a topic
+ #
+ # send '/topic/name', 'some message here'
+ #
+ def send destination, body, parms={}
+ send_frame "SEND", parms.merge( :destination=>destination ), body.to_s
+ end
+
+ # SUBSCRIBE command, for subscribing to topics
+ #
+ # subscribe '/topic/name', false
+ #
+ def subscribe dest, ack=false
+ send_frame "SUBSCRIBE", {:destination=>dest, :ack=>(ack ? "client" : "auto")}
+ end
+
+ # ACK command, for acknowledging receipt of messages
+ #
+ # module StompClient
+ # include EM::P::Stomp
+ #
+ # def connection_completed
+ # connect :login => 'guest', :passcode => 'guest'
+ # # subscribe with ack mode
+ # subscribe '/some/topic', true
+ # end
+ #
+ # def receive_msg msg
+ # if msg.command == "MESSAGE"
+ # ack msg.headers['message-id']
+ # puts msg.body
+ # end
+ # end
+ # end
+ #
+ def ack msgid
+ send_frame "ACK", 'message-id'=> msgid
+ end
+
+ end
+ end
+end
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/tcptest.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/tcptest.rb
new file mode 100644
index 0000000..3187893
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/tcptest.rb
@@ -0,0 +1,54 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage:: http://rubyeventmachine.com
+# Date:: 16 July 2006
+#
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+#
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+#
+
+module EventMachine
+ module Protocols
+
+ # @private
+ class TcpConnectTester < Connection
+ include EventMachine::Deferrable
+
+ def self.test( host, port )
+ EventMachine.connect( host, port, self )
+ end
+
+ def post_init
+ @start_time = Time.now
+ end
+
+ def connection_completed
+ @completed = true
+ set_deferred_status :succeeded, (Time.now - @start_time)
+ close_connection
+ end
+
+ def unbind
+ set_deferred_status :failed, (Time.now - @start_time) unless @completed
+ end
+ end
+
+ end
+end