Class: VPOPMail::Message

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

Overview


class: Message {{{ ++ The Message class represents an email message stored in a Folder

Constant Summary collapse

@@logger =

class attribute: logger {{{

nil

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(p_folder, p_name) ⇒ Message


method: initialize {{{ ++ Creates a new Message object after stored in the Folder p_folder.

p_name contains the name of the file that contains the email. p_name follows the Maildir filename formatt as described in cr.yp.to/proto/maildir.html

Raises:

  • (ArgumentError)


86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/vpopmail/message.rb', line 86

def initialize(p_folder, p_name)
	# The name analysis comes from http://cr.yp.to/proto/maildir.html
	raise ArgumentError unless p_folder.kind_of?(Folder)
	@folder   = p_folder
	@filename = p_name
	@id       = p_name
	@info     = nil
	@flags    = nil
	if p_name =~ /.*:(\d),([A-Z]*)$/ then
		@info  = $1
		@flags = $2
		@id    = p_name.gsub(/:\d,[A-Z]*$/, '')
	end
	self.load
end

Instance Attribute Details

#filenameObject (readonly)

Returns the value of attribute filename.



67
68
69
# File 'lib/vpopmail/message.rb', line 67

def filename
  @filename
end

#flagsObject (readonly)

Returns the value of attribute flags.



67
68
69
# File 'lib/vpopmail/message.rb', line 67

def flags
  @flags
end

#idObject (readonly)

Returns the value of attribute id.



67
68
69
# File 'lib/vpopmail/message.rb', line 67

def id
  @id
end

#infoObject (readonly)

Returns the value of attribute info.



67
68
69
# File 'lib/vpopmail/message.rb', line 67

def info
  @info
end

#pathObject (readonly)

Returns the value of attribute path.



67
68
69
# File 'lib/vpopmail/message.rb', line 67

def path
  @path
end

Class Method Details

.loggerObject



72
# File 'lib/vpopmail/message.rb', line 72

def self.logger ;            @@logger ; end

.logger=(p_object) ⇒ Object



73
# File 'lib/vpopmail/message.rb', line 73

def self.logger=(p_object) ; @@logger = p_object ; end

.validAddress?(p_address, p_local) ⇒ Boolean


class method: validAddress? {{{ ++ Tells if the RMail::Address p_address is valid or not. Uses p_local as the origin email address when querying the mail server of p_address

This methods queries the DNS of the domain contains in p_address for its MX Servers. Then it will use the SMTP protocol to query the MX servers for the validity of p_address

Possible Exceptions

  • SMTPServerBusy

Returns:

  • (Boolean)


114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/vpopmail/message.rb', line 114

def self.validAddress?(p_address, p_local)
	p_address = RMail::Address.parse(p_address)[0] if !p_address.kind_of? RMail::Address
	p_local   = RMail::Address.parse(p_local)[0]   if !p_local.kind_of? RMail::Address

	logger.info "Checking Domain: #{p_address.domain}" if !@@logger.nil?
	begin
		resolver = Net::DNS::Resolver.new
		answer   = resolver.query(p_address.domain, Net::DNS::MX)
		logger.debug "#{answer.inspect}" if !@@logger.nil?
		if answer.nil? or answer.header.anCount == 0
			logger.warn "No nameserver found for domain: #{resolver.errorstring}" if !@@logger.nil?
			return false
		end
		hosts = answer.answer.sort {|x, y| x.preference <=> y.preference}
	rescue Timeout::Error
		logger.error "Timeout while querying #{p_address.domain}" if !@@logger.nil?
		return false
	rescue NameError => p_e
		logger.error "NameError while querying #{p_address.domain}, #{p_e}" if !@@logger.nil?
		return false
	end

	hosts.each { |host|
		logger.info "Connecting to MX #{host.exchange}" if !@@logger.nil?
		begin
			Net::SMTP.start(host.exchange, 25, p_local.domain) { |smtp|
				logger.debug "Validating #{p_address.address}" if !@@logger.nil?
				valid = smtp.validAddress?(p_address.address, p_local.address)
				logger.info  "#{p_address.address} valid?: #{valid}" if !@@logger.nil?
				return valid
			}
		rescue Net::SMTPServerBusy => p_e
			# That means the MX Host does not honor the answer
			logger.error "#{host.exchange} replied #{p_e}" if !@@logger.nil?
			raise p_e
		rescue Errno::ETIMEDOUT => p_e
			# That means the MX Host does not answer
			logger.warn "timeout in querying #{host.exchange}" if !@@logger.nil?
		rescue Exception => p_e
			logger.error "Unknown error: #{p_e}" if !@@logger.nil?
		end
	}
	return false
end

Instance Method Details

#bodyObject


method: body {{{ ++ Gives the body of the email

Returns a String or an array of RMail::Message if the Message is a MIME multipart



177
178
179
# File 'lib/vpopmail/message.rb', line 177

def body
	return @rmail.header
end

#copyTo(p_folder) ⇒ Object


method: copyTo {{{ ++ Copies the Message to the Folder p_folder



299
300
301
302
303
304
305
306
# File 'lib/vpopmail/message.rb', line 299

def copyTo(p_folder)
	logger.info "Copying to folder #{p_folder.name}" if !@@logger.nil?
	p_folder.imapdb.add(self)
	path = p_folder.newpath + File::SEPARATOR + @id
	File.open(path, "w") { |file| RMail::Serialize.write(file, @rmail) }
	File.chmod(0644, path)
	File.chown(@uid, @gid, path)
end

#deleteFrom(p_folder) ⇒ Object


method: deleteFrom {{{ ++ Deletes the Message from the Folder p_folder



312
313
314
315
316
317
318
319
320
321
322
323
324
# File 'lib/vpopmail/message.rb', line 312

def deleteFrom(p_folder)
	logger.info "Deleting from folder #{p_folder.name}" if !@@logger.nil?
	p_folder.imapdb.delete(self)
	begin
		File.delete(p_folder.curpath + File::SEPARATOR + @filename)
	rescue => p_e
		begin
			File.delete(p_folder.newpath + File::SEPARATOR + @filename)
		rescue => p_e
			logger.error "Problem while deleting: #{p_e}" if !@@logger.nil?
		end
	end
end

#learnAs(p_kind) ⇒ Object


method: learnAs ++ Learns the Message as ham or spam using {spamassassin[http://spamassassin.apache.org]

Raises:

  • (ArgumentError)


229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/vpopmail/message.rb', line 229

def learnAs(p_kind)
	raise ArgumentError unless p_kind == "spam" or p_kind == "ham"
	logger.info "Learning message as #{p_kind}" if !@@logger.nil?
	debug     = "-D bayes,learn,dns"
	debug     = ""
	cmdString = "/usr/bin/sa-learn --single #{debug} --username=vpopmail --#{p_kind}"
	cmd       = IO.popen(cmdString, "w+")
	RMail::Serialize.write(cmd, @rmail)
	cmd.close_write

	while line = cmd.gets do
		line = line.gsub(/[\r\n]*$/, "")
		next if line.empty?
		logger.debug "SA-Learn: #{line}" if !@@logger.nil?
	end
end

#loadObject


method: load {{{ ++ Loads the Message from its file in its @rmail attribute (RMail::Message)

Possible Exceptions

  • Errno::ENOENT



253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/vpopmail/message.rb', line 253

def load
	if @filename == @id then
		path = @folder.newpath + File::SEPARATOR + @filename
	else
		path   = @folder.curpath + File::SEPARATOR + @filename
	end
	logger.info "Loading #{path}" if !@@logger.nil?
	begin
		@rmail = File.open(path) { |file| RMail::Parser.read(file) }
		stat   = File.stat(path)
		@uid   = stat.uid
		@gid   = stat.gid
		if !@@logger.nil? then
			logger.debug "Loaded. uid=#{@uid}, gid=#{@gid}"
			logger.info  "Message:         #{@id}"
			logger.info  "Message From:    #{@rmail.header.from.inspect}"
			logger.info  "Message Subject: #{@rmail.header.subject}"
		end
	rescue Errno::ENOENT => p_e
		logger.error "Cannot load #{path}" if !@@logger.nil?
		raise p_e
	end
end

#loggerObject



74
# File 'lib/vpopmail/message.rb', line 74

def logger ;                 @@logger ; end

#logger=(p_object) ⇒ Object



75
# File 'lib/vpopmail/message.rb', line 75

def logger=(p_object) ;      @@logger = p_object ; end

#markAs(p_kind) ⇒ Object


method: markAs {{{ ++ Marks the Message as ham or spam

Raises an ArgumentError if p_kind contains another value.



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/vpopmail/message.rb', line 197

def markAs(p_kind)
	logger.info "Marking message as #{p_kind}" if !@@logger.nil?
	field  = VPOPMail::CFG["SPAM Subject Field"]
	header = @rmail.header
	case p_kind
		when /spam/i
			if !header.field?(field) then
				header[field] = header.subject || ''
				header.subject = VPOPMail::CFG["SPAM Mark"] + (header.subject || '')
				self.save
			else
				logger.info "Message #{@id} is already marked as SPAM" if !@@logger.nil?
			end
		when /ham/i
			if header.field?(field) then
				logger.debug "There is a #{field} field,\nSubject: #{header.subject}\nSPAM:    #{header[field]}" if !@@logger.nil?
				header.subject = header[field]
				header.delete(field)
				self.save
			else
				logger.info "Message #{@id} is already marked as HAM" if !@@logger.nil?
			end
		else
			raise ArgumentError, "Bad kind: #{p_kind}", caller
	end
end

#moveTo(p_folder) ⇒ Object


method: moveTo {{{ ++ Moves the Message to the Folder p_folder



330
331
332
333
334
335
336
337
# File 'lib/vpopmail/message.rb', line 330

def moveTo(p_folder)
	logger.info "Moving to folder #{p_folder.name}" if !@@logger.nil?
	self.copyTo(p_folder)
	self.deleteFrom(@folder)
	logger.info "Reloading from #{p_folder.name}" if !@@logger.nil?
	@folder = p_folder
	self.load
end

#saveObject


method: save {{{ ++ Saves the Message to its file, by serializing its @rmail attribute.



281
282
283
284
285
286
287
288
289
290
291
292
293
# File 'lib/vpopmail/message.rb', line 281

def save
	@folder.imapdb.update(self)
	path = @folder.newpath + File::SEPARATOR + @id
	logger.info "Saving #{path}" if !@@logger.nil?
	File.open(path, "w") { |file| RMail::Serialize.write(file, @rmail) }
	File.chmod(0644, path)
	File.chown(@uid, @gid, path)
	File.delete(@folder.curpath + File::SEPARATOR + @filename)
	@filename = @id
	@info     = nil
	@flags    = nil
	logger.debug "Saved. new filename: #{@filename}" if !@@logger.nil?
end

#to_sObject


method: to_s {{{ ++ Returns the String representation of the Message object



355
356
357
# File 'lib/vpopmail/message.rb', line 355

def to_s
	return "Message #{@id}, info=#{@info}, flags=#{@flags}, path=\"#{@path}\""
end

#to_xmlObject


method: to_xml {{{ ++ Returns the REXML::Document that represents the Message object



343
344
345
346
347
348
349
# File 'lib/vpopmail/message.rb', line 343

def to_xml
	xml  = "<Message id=\"#{@id}\""
	xml += " info=\"#{@info}\""   if !@info.nil?
	xml += " flags=\"#{@flags}\"" if !@flags.nil?
	xml += " path=\"#{@path}\" />"
	return REXML::Document.new(xml)
end

#validAddress?(p_address) ⇒ Boolean


method: validAddress? {{{ ++ Tells if the RMail::Address p_address is valid or not. Extracts the origin email address when querying the mail server of p_address from the current Message

See Message.validAddress?

Returns:

  • (Boolean)


167
168
169
# File 'lib/vpopmail/message.rb', line 167

def validAddress?(p_address)
	return Message.validAddress?(p_address, @folder.domain.postmaster)
end