Class: Kuvert

Inherits:
Object
  • Object
show all
Defined in:
lib/kuvert.rb

Overview

An easy class for creating a mail message

Constant Summary collapse

VERSION =
'1.4.0'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeKuvert

Returns a new instance of Kuvert.



40
41
42
43
44
45
46
47
48
49
# File 'lib/kuvert.rb', line 40

def initialize()
  @headers = Array.new()
  @attachments = Array.new()
  @attachmentboundary = generate_boundary()
  @bodyboundary = generate_boundary()
  @html = nil
  @text = nil
  @charset = 'utf-8'
  @bcc = []
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(methId, *args) ⇒ Object

implement method missing to provide helper methods for setting and getting headers. Headers with ‘-’ characters may be set/gotten as ‘x_mailer’ or ‘XMailer’ (splitting will occur between capital letters or on ‘_’ chracters)



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/kuvert.rb', line 113

def method_missing(methId, *args)
  name = methId.id2name()

  # mangle the name if we have to
  if(name =~ /_/)
    name = name.gsub(/_/, '-')
  elsif(name =~ /[A-Z]/)
    name = name.gsub(/([a-zA-Z])([A-Z])/, '\1-\2')
  end

  # determine if it sets or gets, and do the right thing
  if(name =~ /=$/)
    if(args.length != 1)
      super(methId, args)
    end
    set_header(name[/^(.*)=$/, 1], args[0])
  else
    if(args.length != 0)
      super(methId, args)
    end
    return(get_header(name))
  end
end

Instance Attribute Details

#bccObject

Returns the value of attribute bcc.



38
39
40
# File 'lib/kuvert.rb', line 38

def bcc
  @bcc
end

Instance Method Details

#add_attachment(filename, type = nil, attachmentheaders = nil) ⇒ Object Also known as: attach

adds an attachment to the mail. Type may be given as a mime type. If it is left off and the MIME::Types module is available it will be determined automagically. If the optional attachemntheaders is given, then they will be added to the attachment boundary in the email, which can be used to produce Content-ID markers. attachmentheaders can be given as an Array or a String.



228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/kuvert.rb', line 228

def add_attachment(filename, type=nil, attachmentheaders = nil)
  attachment = Hash.new()
  attachment['filename'] = Pathname.new(filename).basename
  if(type == nil)
    attachment['mimetype'] = MIME::Types.of(filename).first.to_s
  else
    attachment['mimetype'] = type
  end

  # Open in rb mode to handle Windows, which mangles binary files opened in a text mode
  File.open(filename, "rb") { |fp|
    attachment['attachment'] = file_encode(fp.read())
  }

  if(attachmentheaders != nil)
    if(!attachmentheaders.kind_of?(Array))
      attachmentheaders = attachmentheaders.split(/\r?\n/)
    end
    attachment['headers'] = attachmentheaders
  end

  @attachments << attachment
end

#add_attachment_as(file, emailfilename, type = nil, attachmentheaders = nil) ⇒ Object Also known as: attach_as

adds an attachment to the mail as emailfilename. Type may be given as a mime type. If it is left off and the MIME::Types module is available it will be determined automagically. file may be given as an IO stream (which will be read until the end) or as a filename. If the optional attachemntheaders is given, then they will be added to the attachment boundary in the email, which can be used to produce Content-ID markers. attachmentheaders can be given as an Array of a String.



258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
# File 'lib/kuvert.rb', line 258

def add_attachment_as(file, emailfilename, type=nil, attachmentheaders = nil)
  attachment = Hash.new()
  attachment['filename'] = emailfilename

  if(type != nil)
    attachment['mimetype'] = type.to_s()
  elsif(file.kind_of?(String) or file.kind_of?(Pathname))
    attachment['mimetype'] = MIME::Types.of(file.to_s()).first.to_s
  else
    attachment['mimetype'] = ''
  end

  if(file.kind_of?(String) or file.kind_of?(Pathname))
    # Open in rb mode to handle Windows, which mangles binary files opened in a text mode
    File.open(file.to_s(), "rb") { |fp|
      attachment['attachment'] = file_encode(fp.read())
    }
  elsif(file.respond_to?(:read))
    attachment['attachment'] = file_encode(file.read())
  else
    raise(Exception, "file is not a supported type (must be a String, Pathnamem, or support read method)")
  end

  if(attachmentheaders != nil)
    if(!attachmentheaders.kind_of?(Array))
      attachmentheaders = attachmentheaders.split(/\r?\n/)
    end
    attachment['headers'] = attachmentheaders
  end

  @attachments << attachment
end

#add_header(header, value) ⇒ Object

adds a header to the bottom of the headers



52
53
54
55
56
# File 'lib/kuvert.rb', line 52

def add_header(header, value)
  value = quoted_printable_with_instruction(value, @charset) if header == 'subject'
  value = quote_address_if_necessary(value, @charset) if %w[from to cc bcc reply-to].include?(header.downcase)
  @headers << "#{header}: #{value}"
end

#construct(options = Hash.new) ⇒ Object

builds an email and returns it as a string. Takes the following options:

:messageid

Adds a message id to the message based on the from header (defaults to false)

:date

Adds a date to the message if one is not present (defaults to true)



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/kuvert.rb', line 162

def construct(options = Hash.new)
  if(options[:date] == nil)
    options[:date] = true
  end

  if(options[:messageid])
    # add a unique message-id
    remove_header("Message-ID")
    sendingdomain = get_header('from')[0].to_s()[/@([-a-zA-Z0-9._]+)/,1].to_s()
    add_header("Message-ID", "<#{Time.now.to_f()}.#{Process.euid()}.#{String.new.object_id()}@#{sendingdomain}>")
  end

  if(options[:date])
    if(get_header("Date").length == 0)
      add_header("Date", Time.now.strftime("%a, %d %b %Y %H:%M:%S %z"))
    end
  end

  # Add a mime header if we don't already have one and we have multiple parts
  if(multipart?())
    if(get_header("MIME-Version").length == 0)
      add_header("MIME-Version", "1.0")
    end

    if(get_header("Content-Type").length == 0)
      if(@attachments.length == 0)
        add_header("Content-Type", "multipart/alternative;boundary=\"#{@bodyboundary}\"")
      else
        add_header("Content-Type", "multipart/mixed; boundary=\"#{@attachmentboundary}\"")
      end
    end
  end

  return("#{headers_to_s()}#{body_to_s()}")
end

#generate_boundaryObject

generates a unique boundary string



204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/kuvert.rb', line 204

def generate_boundary()
  randomstring = Array.new()
  1.upto(25) {
    whichglyph = rand(100)
    if(whichglyph < 40)
      randomstring << (rand(25) + 65).chr()
    elsif(whichglyph < 70)
      randomstring << (rand(25) + 97).chr()
    elsif(whichglyph < 90)
      randomstring << (rand(10) + 48).chr()
    elsif(whichglyph < 95)
      randomstring << '.'
    else
      randomstring << '_'
    end
  }
  return("----=_NextPart_#{randomstring.join()}")
end

#get_header(header) ⇒ Object

returns the value (or values) of the named header in an array



138
139
140
141
142
143
144
145
146
147
148
# File 'lib/kuvert.rb', line 138

def get_header(header)
  headers = Array.new()
  headerregex = /^#{Regexp.escape(header)}:/i
  @headers.each() { |h|
    if(headerregex.match(h))
      headers << h[/^[^:]+:(.*)/i, 1].strip()
    end
  }

  return(headers)
end

#html=(newhtml) ⇒ Object

sets the HTML body of the message. Only the body of the html should be provided



101
102
103
# File 'lib/kuvert.rb', line 101

def html=(newhtml)
  @html = "<html>\n<head>\n<meta content=\"text/html;charset=#{@charset}\" http-equiv=\"Content-Type\">\n</head>\n<body bgcolor=\"#ffffff\" text=\"#000000\">\n#{newhtml}\n</body>\n</html>"
end

#multipart?Boolean

returns true if the email is multipart

Returns:

  • (Boolean)


151
152
153
154
155
156
157
# File 'lib/kuvert.rb', line 151

def multipart?()
  if(@attachments.length > 0 or @html != nil)
    return(true)
  else
    return(false)
  end
end

#rawhtml=(newhtml) ⇒ Object

sets the HTML body of the message. The entire HTML section should be provided



106
107
108
# File 'lib/kuvert.rb', line 106

def rawhtml=(newhtml)
  @html = newhtml
end

#recipientsObject



86
87
88
89
90
91
92
# File 'lib/kuvert.rb', line 86

def recipients
  [].tap do |ret|
    ret.push(*to)
    ret.push(*cc)
    ret.push(*bcc)
  end
end

#remove_header(header) ⇒ Object

removes the named header - case insensitive



59
60
61
62
63
64
65
# File 'lib/kuvert.rb', line 59

def remove_header(header)
  @headers.each_index() { |i|
    if(@headers[i] =~ /^#{Regexp.escape(header)}:/i)
      @headers.delete_at(i)
    end
  }
end

#replytoObject



78
79
80
# File 'lib/kuvert.rb', line 78

def replyto()
  return(get_header("Reply-To")[0])
end

#replyto=(newreplyto) ⇒ Object



73
74
75
76
# File 'lib/kuvert.rb', line 73

def replyto=(newreplyto)
  remove_header("Reply-To")
  add_header("Reply-To", newreplyto)
end

#set_header(header, value) ⇒ Object

sets a header (removing any other versions of that header)



68
69
70
71
# File 'lib/kuvert.rb', line 68

def set_header(header, value)
  remove_header(header)
  add_header(header, value)
end

#text=(newtext) ⇒ Object

sets the plain text body of the message



95
96
97
# File 'lib/kuvert.rb', line 95

def text=(newtext)
  @text = newtext
end

#to_sObject

returns a formatted email - equivalent to construct(:messageid => true)



199
200
201
# File 'lib/kuvert.rb', line 199

def to_s()
  return(construct(:messageid => true))
end