# frozen_string_literal: true
require 'mail'
require 'base64'
require 'socket'
module PWN
module Plugins
# This is a fork of the no-longer maintained 'pony' gem.
# This module's purpose is to exist until the necessary
# functionality can be integrated into PWN::Plugins::MailAgent
module Pony
@@logger = PWN::Plugins::PWNLogger.create
@@options = {}
@@override_options = {}
@@subject_prefix = false
@@append_inputs = false
# Default options can be set so that they don't have to be repeated.
#
# Pony.options = { :from => 'noreply@example.com', :via => :smtp, :via_options => { :host => 'smtp.yourserver.com' } }
# Pony.mail(:to => 'foo@bar') # Sends mail to foo@bar from noreply@example.com using smtp
# Pony.mail(:from => 'pony@example.com', :to => 'foo@bar') # Sends mail to foo@bar from pony@example.com using smtp
public_class_method def self.options=(value)
@@options = value
end
# Method usage N/A
public_class_method def self.options
@@options
end
# Method usage N/A
public_class_method def self.override_options=(value)
@@override_options = value
end
# Method usage N/A
public_class_method def self.override_options
@@override_options
end
# Method usage N/A
public_class_method def self.subject_prefix(value)
@@subject_prefix = value
end
# Method usage N/A
public_class_method def self.append_inputs
@@append_inputs = true
end
# Send an email
# Pony.mail(:to => 'you@example.com', :from => 'me@example.com', :subject => 'hi', :body => 'Hello there.')
# Pony.mail(:to => 'you@example.com', :html_body => '
Hello there!
', :body => "In case you can't read html, Hello there.")
# Pony.mail(:to => 'you@example.com', :cc => 'him@example.com', :from => 'me@example.com', :subject => 'hi', :body => 'Howsit!')
public_class_method def self.mail(options)
options[:body] = "#{options[:body]}/n #{options}" if @@append_inputs
options = @@options.merge options
options = options.merge @@override_options
options[:subject] = "#{@@subject_prefix}#{options[:subject]}" if @@subject_prefix
raise ArgumentError, ':to is required' unless options[:to]
options[:via] = default_delivery_method unless options.key?(:via)
if options.key?(:via) && options[:via] == :sendmail
options[:via_options] ||= {}
options[:via_options][:location] ||= sendmail_binary
end
deliver build_mail(options)
end
# Method usage N/A
public_class_method def self.permissable_options
standard_options + non_standard_options
end
# Method usage N/A
private_class_method def self.deliver(mail)
mail.deliver!
end
# Method usage N/A
public_class_method def self.default_delivery_method
File.executable?(sendmail_binary) ? :sendmail : :smtp
end
# Method usage N/A
public_class_method def self.standard_options
%i[
to
cc
bcc
from
subject
content_type
message_id
sender
reply_to
smtp_envelope_to
]
end
# Method usage N/A
public_class_method def self.non_standard_options
%i[
attachments
body
charset
enable_starttls_auto
headers
html_body
text_part_charset
via
via_options
body_part_header
html_body_part_header
]
end
# Method usage N/A
public_class_method def self.build_mail(options)
mail = Mail.new do |m|
options[:date] ||= Time.now
options[:from] ||= 'pony@unknown'
options[:via_options] ||= {}
options.each do |k, v|
next if non_standard_options.include?(k)
m.send(k, v)
end
# Automatic handling of multipart messages in the underlying
# mail library works pretty well for the most part, but in
# the case where we have attachments AND text AND html bodies
# we need to explicitly define a second multipart/alternative
# boundary to encapsulate the body-parts within the
# multipart/mixed boundary that will be created automatically.
if options[:attachments] && options[:html_body] && options[:body]
part(content_type: 'multipart/alternative') do |p|
p.html_part = build_html_part(options)
p.text_part = build_text_part(options)
end
# Otherwise if there is more than one part we still need to
# ensure that they are all declared to be separate.
elsif options[:html_body] || options[:attachments]
m.html_part = build_html_part(options) if options[:html_body]
m.text_part = build_text_part(options) if options[:body]
elsif options[:body]
# If all we have is a text body, we don't need to worry about parts.
body options[:body]
end
m.delivery_method options[:via], options[:via_options]
end
(options[:headers] ||= {}).each do |key, value|
mail[key] = value
end
add_attachments(mail, options[:attachments]) if options[:attachments]
mail.charset = options[:charset] if options[:charset] # charset must be set after setting content_type
mail.text_part.charset = options[:text_part_charset] if mail.multipart? && options[:text_part_charset]
set_content_type(mail, options[:content_type])
mail
end
# Method usage N/A
public_class_method def self.build_html_part(options)
Mail::Part.new(content_type: 'text/html;charset=UTF-8') do
content_transfer_encoding 'quoted-printable'
body Mail::Encodings::QuotedPrintable.encode(options[:html_body])
if options[:html_body_part_header] && options[:html_body_part_header].is_a?(Hash)
options[:html_body_part_header].each do |k, v|
header[k] = v
end
end
end
end
# Method usage N/A
public_class_method def self.build_text_part(options)
Mail::Part.new(content_type: 'text/plain') do
content_type options[:charset] if options[:charset]
body options[:body]
if options[:body_part_header] && options[:body_part_header].is_a?(Hash)
options[:body_part_header].each do |k, v|
header[k] = v
end
end
end
end
# Method usage N/A
public_class_method def self.set_content_type(mail, user_content_type)
params = mail.content_type_parameters || {}
case params
when user_content_type
content_type = user_content_type
when mail.has_attachments?
if mail.attachments.detect(&:inline?)
content_type = ['multipart', 'related', params]
else
content_type = ['multipart', 'mixed', params]
end
when mail.multipart?
content_type = ['multipart', 'alternative', params]
else
content_type = false
end
mail.content_type = content_type if content_type
end
# Method usage N/A
public_class_method def self.add_attachments(mail, attachments)
attachments.each do |name, body|
name = name.gsub(/\s+/, ' ')
# mime-types wants to send these as "quoted-printable"
if name.match?('.xlsx')
mail.attachments[name] = {
content: Base64.strict_encode64(body),
transfer_encoding: :base64
}
else
mail.attachments[name] = body
end
mail.attachments[name].add_content_id("<#{name}@#{Socket.gethostname}>")
end
end
# Method usage N/A
public_class_method def self.sendmail_binary
sendmail = `which sendmail`.chomp
sendmail.empty? ? '/usr/sbin/sendmail' : sendmail
end
# Author(s):: Jacob Hoopes
public_class_method def self.authors
"AUTHOR(S):
Jacob Hoopes
"
end
# Display Usage for this Module
public_class_method def self.help
puts "USAGE:
This module is deprecated. Please Use PWN::Plugins::MailAgent instead.
#{self}.authors
"
end
end
end
end