Files
gtav-src/tools_ng/lib/pipeline/util/email.rb
T
2025-09-29 00:52:08 +02:00

359 lines
12 KiB
Ruby
Executable File

#
# File:: email.rb
# Description:: Email sending functions.
#
# Author:: David Muir <david.muir@rockstarnorth.com>
# Date:: 13 September 2008
#
#-----------------------------------------------------------------------------
# Uses
#-----------------------------------------------------------------------------
require 'pipeline/os/path'
require 'pipeline/util/string'
require 'base64'
require 'net/smtp'
require 'tmail'
# Monkey-patching for debugging.
module TMail
class Mail
TO_TREE_INSPECT_DEFAULT = proc { |node| "#<Mime content_type=#{node.content_type.inspect}>" }
def to_tree io='', &inspect
inspect ||= TO_TREE_INSPECT_DEFAULT
io << "- #{inspect[self]}\n"
recurse = proc do |node, prefix|
if node.multipart?
node.parts.each_with_index do |child, i|
a, b = i == parts.length - 1 ? ["\\", ' '] : ['|', '| ']
io << "#{prefix + a}- #{inspect[child]}\n"
recurse.call child, prefix + b
end
end
end
recurse.call self, ' '
io
end
end
end
#-----------------------------------------------------------------------------
# Implementation
#-----------------------------------------------------------------------------
module Pipeline
module Util
#
# == Description
# Object representing an email address, including the address String and
# an (optional) alias String.
#
class EmailAddress
attr_reader :address
attr_reader :alias
# Class constructor.
def initialize( address, friendly = nil )
@address = address
@alias = friendly
end
# Return String representation of object.
def to_s( )
if ( @alias.nil? ) then
"#{@alias} <#{@address}>"
else
"#{@address}"
end
end
#--------------------------------------------------------------------
# Class Methods
#--------------------------------------------------------------------
# Constructor from REXML::Element (expects 'alias' and 'address' attributes).
def EmailAddress::from_xml( xml_node )
address = xml_node.attributes['address']
friendly = xml_node.attributes['alias']
EmailAddress.new( address, friendly )
end
# Constructor from REXML::Element (child email elements as above).
def EmailAddress::from_xml_list( xml_node )
emails = []
xml_node.each_element do |address_node|
emails << EmailAddress::from_xml( address_node )
end
emails
end
end
#
# == Description
# Email sending utility class. The class can be used in two ways depending
# on the required use case.
#
# If multiple emails are to be sent then you can create an instance of the
# Email object with the relevant server and port settings. If only a
# single email is required then the Email::send class method may be simpler
# to use.
#
# See also Pipeline::Util::EmailFactory.
#
# == Example Usage
# See individual function examples and unit test cases.
#
class Email
DEFAULT_SMTP_PORT = 25
DEFAULT_AUTH_MODE = :plain
attr_reader :server
attr_reader :port
attr_reader :domain
# We deliberately do not allow access to the username and password.
#
# Constructor. The +options+ argument is a Hash containing the
# following keys:
# :server
# :port
# :domain
# :username
# :password
# :auth_mode
#
def initialize( options )
throw ArgumentError.new( "Invalid options, must be Hash object." ) \
unless ( options.is_a?( Hash ) )
@server = options[:server]
@port = options[:port]
@domain = options[:domain]
@username = options[:username]
@password = options[:password]
@auth_mode = options[:auth_mode]
end
#
# Send an email message to a set of recipients. The +from+, +to+ and
# optional +reply_to+ arguments must be hashes using :address and :alias
# keys to supply the email address string and aliases for users.
#
# To send an HTML email set the +content_type+ argument to 'text/html'.
#
def send( from, to, subject, body, content_type = 'text/plain', reply_to = nil )
Email::send( @server, @port, @username, @password, @domain, @auth_mode,
from, to, subject, body, content_type, reply_to )
end
#
# Send a multipart email message to a set of recipients. See
# documentation for the +send+ method.
#
# The +parts+ argument must be an TMail::Mail structure. See
# http://rubyforge.org/pipermail/tmail-talk/2008-July/000086.html for
# an example of constructing such multipart TMail::Mail object
# hierarchies.
#
# MIME type strings can be found at:
# http://www.webmaster-toolkit.com/mime-types.shtml
#
def send_multipart( from, to, subject, parts, reply_to = nil )
Email::send_multipart( @server, @port, @username, @password, @domain, @auth_mode,
from, to, subject, parts, reply_to )
end
#
# Send an email message to a set of recipients. The +from+, +to+ and
# optional +reply_to+ arguments must be Hash objects using :address
# and :alias keys to supply the email address string and aliases for
# users.
#
# To send an HTML email set the +content_type+ argument to 'text/html'.
#
# +auth_mode+ should be set to one of :plain, :login or :cram_md5.
#
# == Example Usage
# Email::send( 'mailrelay', 25, 'username', 'password', 'domain.com', :plain,
# { :address => 'test@rockstarnorth.com', :alias => 'Email Test' },
# { :address => 'david.muir@rockstarnorth.com', :alias => 'David Muir' },
# 'Testing Email',
# "Testing body plain text (#{Time.now})" )
#
def Email::send( server, port, username, password, domain, auth_mode,
from, to, subject, body, content_type = 'text/plain', reply_to = nil )
throw ArgumentError.new( 'from argument must be a Hash' ) \
unless ( from.is_a?( Hash ) )
throw ArgumentError.new( 'to argument must be a Hash or Array' ) \
unless ( ( to.is_a?( Hash ) ) or ( to.is_a?( Array ) ) )
throw ArgumentError.new( 'port must be an Integer' ) \
unless ( port.is_a?( Integer ) )
mail = TMail::Mail.new()
mail.mime_version = '1.0'
to_addresses = []
if ( to.is_a?( Hash ) ) then
to_addresses << to[:address]
mail.to = "#{to[:alias]} <#{to[:address]}>"
elsif ( to.is_a?( Array ) ) then
mailto = ""
to.each_with_index do |recipient, index|
throw ArgumentError.new( 'to argument array contains a non-Hash object.' ) \
unless ( recipient.is_a?( Hash ) )
to_addresses << recipient[:address]
if ( index == ( to.size - 1 ) ) then
mailto += "#{recipient[:alias]} <#{recipient[:address]}>"
else
mailto += "#{recipient[:alias]} <#{recipient[:address]}>, "
end
end
mail.to = mailto
end
mail.from = "#{from[:alias]} <#{from[:address]}>"
mail.reply_to = "#{reply_to[:alias]} <#{reply_to[:address]}>" unless ( reply_to.nil? )
mail.subject = subject
mail.content_type = content_type
mail.body = body
if ( username.nil? and password.nil? and domain.nil? and auth_mode.nil? ) then
Net::SMTP.start( server, port ) do |smtp|
smtp.send_message( mail.to_s, from[:address], to_addresses )
end
else
Net::SMTP.start( server, port, domain, username, password, auth_mode ) do |smtp|
smtp.send_message( mail.to_s, from[:address], to_addresses )
end
end
end
#
# Send a multipart email message to a set of recipients. The +from+,
# +to+ and optional +reply_to+ arguments must be Hash objects using
# :address and :alias keys to suplpy the email address string and
# aliases for users.
#
# The +parts+ argument must be an TMail::Mail structure. See
# http://rubyforge.org/pipermail/tmail-talk/2008-July/000086.html for
# an example of constructing such multipart TMail::Mail object
# hierarchies.
#
# MIME type strings can be found at:
# http://www.webmaster-toolkit.com/mime-types.shtml
#
# == Example Usage
# DHM TODO
#
def Email::send_multipart( server, port, username, password, domain, auth_mode,
from, to, subject, parts, reply_to = nil )
throw ArgumentError.new( 'from argument must be a Hash' ) \
unless ( from.is_a?( Hash ) )
throw ArgumentError.new( 'to argument must be a Hash or Array' ) \
unless ( ( to.is_a?( Hash ) ) or ( to.is_a?( Array ) ) )
throw ArgumentError.new( 'port must be an Integer' ) \
unless ( port.is_a?( Integer ) )
throw ArgumentError.new( 'parts must be an Array' ) \
unless ( parts.is_a?( Array ) )
mail = TMail::Mail.new( )
mail.mime_version = '1.0'
if ( to.is_a?( Hash ) ) then
mail.to = "#{to[:alias]} <#{to[:address]}>"
elsif ( to.is_a?( Array ) ) then
mail.to = ''
to.each do |recipient|
throw ArgumentError.new( 'to argument array contains a non-Hash object.' ) \
unless ( recipient.is_a?( Hash ) )
mail.to = "#{recipient[:alias]} <#{recipient[:address]}>"
end
end
mail.from = "#{from[:alias]} <#{from[:address]}>"
mail.reply_to = "#{reply_to[:alias]} <#{reply_to[:address]}>" unless ( reply_to.nil? )
mail.subject = subject
mail.set_content_type( 'multipart', 'mixed', { 'boundary' => TMail::new_boundary() } )
parts.each do |part| mail.parts << part; end
puts mail.to_tree
Net::SMTP.start( server, port, domain, username, password, auth_mode ) do |smtp|
smtp.send_message( mail.to_s, from[:address], [ to[:address] ] )
end
end
end
#
# == Description
# The EmailFactory can be used to create TMail::Mail objects for use with
# the Email::send_multipart function. Factory functions are available for
# creating some common types of TMail::Mail objects (e.g. plain text and
# HTML messages, picture attachments etc).
#
class EmailFactory
#
# Create a TMail::Mail object given a HTML message body string. The
# string is expected to be HTML formatted, starting with a <html> tag,
# ending with a </html> tag.
#
def EmailFactory::create_html_mail( body )
throw ArgumentError.new( 'Invalid body argument, String expected.' ) \
unless ( body.is_a?( String ) )
#throw ArgumentError.new( 'body does not start with <html>.' ) \
# unless ( body.starts_with( '<html>' ) )
#throw ArgumentError.new( 'body does not end with </html>.' ) \
# unless ( body.ends_with( '</html>' ) )
html = TMail::Mail.new( )
html.mime_version = '1.0'
html.content_type = 'text/html'
html.body = body
html
end
#
# Create a TMail::Mail object given a message body string. The string
# is expected to be plain text.
#
def EmailFactory::create_text_mail( body )
throw ArgumentError.new( 'Invalid body argument, String expected.' ) \
unless ( body.is_a?( String ) )
text = TMail::Mail.new( )
text.mime_version = '1.0'
text.content_type = 'text/plain'
text.body = body
text
end
def EmailFactory::create_attachment_from_file( filename )
throw IOError.new( 'File does not exist' ) unless ( ::File::exists?( filename ) )
# Read file data.
data = nil
File.open( filename, 'rb' ) do |fp|
data = fp.read( )
end
attachment = TMail::Mail.new( )
attachment.mime_version = '1.0'
attachment.body = Base64::encode64( data )
attachment.transfer_encoding = 'Base64'
attachment.content_type = 'application/octet-stream'
attachment['Content-Disposition'] = "attachment; filename=#{OS::Path::get_filename(filename)}"
attachment
end
end
end # Util module
end # Pipeline module
# email.rb