Skip to content

Instantly share code, notes, and snippets.

@nelsnelson
Created December 11, 2014 06:30
Show Gist options
  • Save nelsnelson/bbe061dd34896fe5cc60 to your computer and use it in GitHub Desktop.
Save nelsnelson/bbe061dd34896fe5cc60 to your computer and use it in GitHub Desktop.
#! /usr/bin/env jruby
require 'mail'
require 'optparse'
require 'pony'
require 'securerandom'
require 'zlib'
module Mail
class AttachmentsList < Array
def []=(name, value)
encoded_name = Mail::Encodings.decode_encode name, :encode
default_values = { :content_type => "#{set_mime_type(name)}; name=\"#{encoded_name}\"",
:content_transfer_encoding => "#{guess_encoding}",
:content_disposition => "#{@content_disposition_type}; filename=\"#{encoded_name}\"" }
if value.is_a?(Hash)
default_values[:body] = value.delete(:content) if value[:content]
default_values[:body] = value.delete(:data) if value[:data]
encoding = value.delete(:transfer_encoding) || value.delete(:encoding)
if encoding
if Mail::Encodings.defined? encoding
default_values[:content_transfer_encoding] = encoding
else
raise "Do not know how to handle Content Transfer Encoding #{encoding}, please choose either quoted-printable or base64"
end
end
if value[:mime_type]
default_values[:content_type] = value.delete(:mime_type)
@mime_type = MIME::Types[default_values[:content_type]].first
default_values[:content_transfer_encoding] ||= guess_encoding
end
hash = default_values.merge(value)
else
default_values[:body] = value
hash = default_values
end
if hash[:body].respond_to? :force_encoding and hash[:body].respond_to? :valid_encoding?
if not hash[:body].valid_encoding? and default_values[:content_transfer_encoding].downcase == "binary"
hash[:body].force_encoding("BINARY")
end
end
attachment = Part.new(hash)
attachment.add_content_id(hash[:content_id])
@parts_list << attachment
end
end
class ParameterHash < IndifferentHash
def encoded
map.sort_by { |a| a.first.to_s }.map! do |key_name, value|
unless value.ascii_only?
value = Mail::Encodings.param_encode(value)
key_name = "#{key_name}*"
end
if key_name =~ /name/
%Q{#{key_name}="#{quote_token(value)}"}
else
%Q{#{key_name}=#{quote_token(value)}}
end
end.join(";\s")
end
end
class ContentDispositionField < StructuredField
def encoded
if parameters.length > 0
p = ";\s#{parameters.encoded}\r\n"
else
p = "\r\n"
end
"#{CAPITALIZED_FIELD}: #{disposition_type}" + p
end
end
class ContentTypeField < StructuredField
def string
sub_type.gsub! /^gzip$/, 'x-gzip'
"#{main_type}/#{sub_type}"
end
def encoded
parameters.delete 'charset' if parameters.keys.include? 'name' or parameters.keys.include? 'filename'
if parameters.length > 0
p = ";\s#{parameters.encoded}"
else
p = ""
end
"#{CAPITALIZED_FIELD}: #{content_type}#{p}\r\n"
end
end
class ContentIdField < StructuredField
def encoded
"#{CAPITALIZED_FIELD}: #{to_s}\r\nX-Attachment-Id: f_#{SecureRandom.hex[0..8]}\r\n"
end
end
end
def gzip(file)
gzip = "#{file}.gz"
Zlib::GzipWriter.open(gzip) do |gz|
File.open(file, 'rb') do |io|
while chunk = io.read(16 * 1024) do
gz.write chunk
end
end
gz.close
end
gzip
end
def send_email(to, from, subject, message='', attachment=nil, compress=false)
begin
options = {
:via => :smtp,
:via_options => {
:address => 'smtp.gmail.com',
:port => '587',
:enable_starttls_auto => true,
:user_name => '[email protected]',
:password => 'password',
:authentication => :plain,
:domain => 'host.com'
},
:to => to,
:from => from,
:subject => subject,
:html_body => message,
:body => message
}
if attachment
attachment = File.expand_path attachment
attachment = gzip attachment if compress
content = open(attachment, 'rb') { |io| io.read }
basename = File.basename attachment
attachment_options = {
:attachments => {
basename => content
},
:headers => {
'Content-Type' => 'multipart/mixed',
'Content-Transfer-Encoding' => 'base64',
'Content-Disposition' => 'attachment'
}
}
options.merge! attachment_options
end
mail = Pony.mail options
File.delete attachment if compress
rescue Exception => ex
puts "Error posting email: #{ex.message}"
puts ex.backtrace
return ex.message
end
nil
end
@options = Hash.new
OptionParser.new do |opt|
opt.banner = "Usage: #{File.basename $0} [options]"
opt.on('--to=to', 'To: recipient e-mail address') { |v| @options[:to_address] = v }
opt.on('--from=from', 'From: sender e-mail address') { |v| @options[:from_address] = v }
opt.on('--subject=subject', 'Subject: e-mail subject') { |v| @options[:subject] = v }
opt.on('--message=message', 'Message: e-mail message body') { |v| @options[:message] = v }
opt.on('--attachment=attachment', 'Attachment: file to attach') { |v| @options[:attachment] = v }
opt.on('--compress', 'Compress attachment') { |v| @options[:compress] = v }
opt.on_tail("-?", "--help", "Show this message") do
puts opt
exit
end
opt.on_tail("--version", "Show version") do
puts "#{File.basename($0)} version 1.0"
exit
end
begin
opt.parse!(ARGV)
rescue OptionParser::InvalidOption => e
puts opt
exit
end
@options[:compress] ||= false
end
def main
send_email(
@options[:to_address],
@options[:from_address],
@options[:subject],
@options[:message],
@options[:attachment],
@options[:compress]
)
end
main if __FILE__ == $0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment