migrate mailr 2.2.2 to 2.3.2

This commit is contained in:
yuzai chen 2009-07-30 13:01:48 +08:00
parent 56039cc595
commit ae789b1d84
23 changed files with 5 additions and 48 deletions

View file

@ -0,0 +1,8 @@
class BouncedMail < ActiveRecord::Base
belongs_to :customer
belongs_to :contact
def BouncedMail.find_by_customer_contact(cust_id, contact_id)
find_all(["customer_id = ? and contact_id = ?", cust_id, cotact_id], ["msg_date desc"])
end
end

303
lib/webmail/cdfmail.rb Normal file
View file

@ -0,0 +1,303 @@
require 'tmail'
require 'net/smtp'
require 'mail_transform'
class CDF::Mail
include ActionMailer::Quoting
def initialize(senderTempLocation)
@attachments = Array.new
@sender_temp_location = senderTempLocation
@to_contacts = Array.new
end
def customer_id() @customer_id end
def customer_id=(arg) @customer_id = arg end
def from() @from end
def from=(arg) @from = arg end
def to() @to end
def to=(arg) @to = arg end
def to_contacts() @to_contacts end
def to_contacts=(arg) @to_contacts = arg end
def toc=(arg)
@to_contacts = Array.new
arg.split(",").each { |token| @to_contacts << token.to_i unless token == "" or token.strip() == "undefined"} unless arg.nil? or arg == "undefined"
end
def toc
ret = String.new
@to_contacts.each { |contact|
ret << "," unless ret == ""
if contact.kind_of?(Integer)
ret << contact.to_s unless contact.nil? or contact == 0
else
ret << contact.id.to_s unless contact.nil? or contact.id.nil?
end
}
ret
end
def bcc() @bcc end
def bcc=(arg) @bcc = arg end
def cc() @cc end
def cc=(arg) @cc = arg end
def subject() @subject end
def subject=(arg) @subject = arg end
def attachments
@attachments
end
def add_attachment(attachment)
@attachments << attachment
end
def multipart?
@attachments && @attachments.size > 0
end
def delete_attachment(att_filename)
@attachments.each { |att| att.delete_temp_data() if arr.filename == att_filename }
@attachments.delete_if() { |att| att.filename == att_filename }
end
def delete_attachments()
@attachments.each { |att| att.delete_temp_data() }
@attachments = Array.new
end
def body() @body end
def body=(arg) @body = arg end
def content_type() @content_type end
def content_type=(arg) @content_type = arg end
def temp_location() @sender_temp_location end
def send_mail(db_msg_id = 0)
m = TMail::Mail.new
m.from, m.body = self.from, self.body
m.date = Time.now
m.subject, = quote_any_if_necessary("UTF-8", self.subject)
m.to = decode_addresses(self.to)
m.cc, m.bcc = decode_addresses(self.cc), decode_addresses(self.bcc)
if multipart?
m.set_content_type("multipart/mixed")
p = TMail::Mail.new(TMail::StringPort.new(""))
if @content_type.include?("text/plain") # here maybe we should encode in 7bit??!!
prepare_text(p, self.content_type, self.body)
elsif self.content_type.include?("text/html")
prepare_html(p, self.content_type, self.body)
elsif self.content_type.include?("multipart")
prepare_alternative(p, self.body)
end
m.parts << p
else
if @content_type.include?("text/plain") # here maybe we should encode in 7bit??!!
prepare_text(m, self.content_type, self.body)
elsif self.content_type.include?("text/html")
prepare_html(m, self.content_type, self.body)
elsif self.content_type.include?("multipart")
prepare_alternative(m, self.body)
end
end
# attachments
@attachments.each { |a|
m.parts << a.encoded
}
encmail = m.encoded
RAILS_DEFAULT_LOGGER.debug("Sending message \n #{encmail}")
Net::SMTP.start(ActionMailer::Base.smtp_settings[:address], ActionMailer::Base.smtp_settings[:port],
ActionMailer::Base.smtp_settings[:domain], ActionMailer::Base.smtp_settings[:user_name],
ActionMailer::Base.smtp_settings[:password], ActionMailer::Base.smtp_settings[:authentication]) do |smtp|
smtp.sendmail(encmail, m.from, m.destinations)
end
return encmail
end
def forward(tmail, fb)
decoded_subject = mime_encoded?(tmail.subject) ? mime_decode(tmail.subject) : tmail.subject
self.subject = "[Fwd: #{decoded_subject}]"
attachment = CDF::Attachment.new(self)
attachment.body(tmail, fb)
end
def reply(tmail, fb, type)
decoded_subject = mime_encoded?(tmail.subject) ? mime_decode(tmail.subject) : tmail.subject
self.subject = "Re: #{decoded_subject}"
tm = tmail.create_reply
self.to = tm.to
footer = ""
msg_id = ""
mt = MailTransform.new
self.body = mt.get_body(tmail, type)
end
private
def delimeter
if self.content_type == "text/plain"
"\n"
else
"<br/>"
end
end
def text2html(str) CGI.escapeHTML(str).gsub("\n", "<br/>") end
def html2text(txt)
clear_html(txt)
end
def prepare_text(msg, ctype, bdy)
msg.set_content_type(ctype, nil, {"charset"=>"utf-8"})
msg.transfer_encoding = "8bit"
msg.body = bdy
end
def prepare_html(msg, ctype, bdy)
msg.set_content_type(ctype, nil, {"charset"=>"utf8"})
msg.transfer_encoding = "8bit"
msg.body = bdy
end
def prepare_alternative(msg, bdy)
bound = ::TMail.new_boundary
msg.set_content_type("multipart/alternative", nil, {"charset"=>"utf8", "boundary"=>bound})
msg.transfer_encoding = "8bit"
ptext = TMail::Mail.new(TMail::StringPort.new(""))
phtml = TMail::Mail.new(TMail::StringPort.new(""))
prepare_text(ptext, "text/plain", html2text(bdy))
prepare_html(phtml, "text/html", bdy)
msg.parts << ptext
msg.parts << phtml
end
def decode_addresses(str)
ret = String.new
str.split(",").each { |addr|
if addr.slice(0,4) == "Grp+"
grp_id = addr.scan(/Grp\+([0-9]*):(.*)/)[0][0]
ContactGroup.find(:first, :conditions=>['customer_id = ? and id = ?', @customer_id, grp_id]).contacts.each { |contact|
ret << "," if not(ret == "")
@to_contacts << contact unless contact.nil?
ret << contact.full_address
ad, = quote_any_address_if_necessary(CDF::CONFIG[:mail_charset], contact.full_address)
ret << ad
}
else
ret << "," if not(ret == "")
ad, = quote_any_address_if_necessary(CDF::CONFIG[:mail_charset], addr) if not(addr.nil? or addr == "")
ret << ad if not(addr.nil? or addr == "")
end
} unless str.nil? or str.strip() == ""
ret
end
end
class CDF::Attachment
def initialize(arg)
@mail = arg
@mail.add_attachment(self)
@index = @mail.attachments.size - 1
end
def filename=(arg)
@filename = arg.tr('\\/:*?"\'<>|', '__________')
end
def filename() @filename end
def temp_filename=(arg) @temp_filename = arg end
def temp_filename() @temp_filename end
def content_type=(arg) @content_type = arg end
def content_type() @content_type end
def delete_temp_data()
File.delete(self.temp_filename)
end
def file
File.open(self.temp_filename, "rb") { |fp| fp.read }
end
def file=(data)
return if data.size == 0
@content_type = data.content_type
self.filename = data.original_filename.scan(/[^\\]*$/).first
self.temp_filename = "#{@mail.temp_location}/#{@filename}"
check_store_path
data.rewind
File.open(@temp_filename, "wb") { |f| f.write(data.read) }
end
def body(data, fb)
@content_type = "message/rfc822"
filename = data.content_type['filename']
self.filename = filename.nil? ? (mime_encoded?(data.subject) ? mime_decode(data.subject) : data.subject) : filename
self.temp_filename = "#{@mail.temp_location}/#{@filename}"
check_store_path
File.open(@temp_filename, "wb") { |f| f.write(fb) }
end
def check_store_path()
path = ""
"#{@mail.temp_location}".split(File::SEPARATOR).each { |p|
path << p
begin
Dir.mkdir(path)
rescue
end
path << File::SEPARATOR
}
end
def encoded
p = TMail::Mail.new(TMail::StringPort.new(""))
data = self.file
p.body = data
if @content_type.include?("text/plain") # here maybe we should encode in 7bit??!!
p.set_content_type(@content_type, nil, {"charset"=>"utf-8"})
p.transfer_encoding = "8bit"
elsif @content_type.include?("text/html")
p.set_content_type(@content_type, nil, {"charset"=>"utf8"})
p.transfer_encoding = "8bit"
elsif @content_type.include?("rfc822")
p.set_content_type(@content_type, nil, {"charset"=>"utf8"})
p.set_disposition("inline;")
p.transfer_encoding = "8bit"
else
p.set_content_type(@content_type, nil, {"name"=>@filename})
p.set_disposition("inline; filename=#{@filename}") unless @filename.nil?
p.set_disposition("inline;") if @filename.nil?
p.transfer_encoding='Base64'
p.body = TMail::Base64.folding_encode(data)
end
return p
end
end

View file

@ -0,0 +1,5 @@
require 'maildropserializator'
Customer.class_eval do
include MaildropSerializator
has_many :filters, :order => "order_num", :dependent => true
end

View file

@ -0,0 +1,2 @@
class Expression < ActiveRecord::Base
end

3
lib/webmail/filter.rb Normal file
View file

@ -0,0 +1,3 @@
class Filter < ActiveRecord::Base
has_many :expressions
end

View file

@ -0,0 +1,38 @@
require 'mail2screen'
class ImapMessage < ActiveRecord::Base
include Mail2Screen
def set_folder(folder)
@folder = folder
end
def full_body
@folder.mailbox.imap.uid_fetch(uid, "BODY[]").first.attr["BODY[]"]
end
def from_addr=(fa)
self.from = fa.to_yaml
self.from_flat = short_address(fa)
end
def from_addr
begin
YAML::load(from)
rescue Object
from
end
end
def to_addr=(ta)
self.to = ta.to_yaml
self.to_flat = short_address(ta)
end
def to_addr
begin
YAML::load(to)
rescue Object
to
end
end
end

505
lib/webmail/imapmailbox.rb Normal file
View file

@ -0,0 +1,505 @@
# Copyright (c) 2005, Benjamin Stiglitz
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# Modifications (c) 2005 by littlegreen
#
require 'net/imap'
Net::IMAP.debug = true if CDF::CONFIG[:debug_imap]
class Net::IMAP
class PlainAuthenticator
def process(data)
return "\0#{@user}\0#{@password}"
end
private
def initialize(user, password)
@user = user
@password = password
end
end
add_authenticator('PLAIN', PlainAuthenticator)
class Address
def to_s
if(name)
"#{name} #{mailbox}@#{host}"
else
"#{mailbox}@#{host}"
end
end
end
end
class AuthenticationError < RuntimeError
end
class IMAPMailbox
attr_reader :connected
attr_accessor :selected_mailbox
cattr_accessor :logger
def initialize
@selected_mailbox = ''
@folders = {}
@connected = false
end
def connect(username, password)
unless @connected
use_ssl = CDF::CONFIG[:imap_use_ssl] ? true : false
port = CDF::CONFIG[:imap_port] || (use_ssl ? 993 : 143)
begin
@imap = Net::IMAP.new(CDF::CONFIG[:imap_server], port, use_ssl)
rescue Net::IMAP::ByeResponseError => bye
# make a timeout and retry
begin
System.sleep(CDF::CONFIG[:imap_bye_timeout_retry_seconds])
@imap = Net::IMAP.new(CDF::CONFIG[:imap_server], port, use_ssl)
rescue Error => ex
logger.error "Error on authentication!"
logger.error bye.backtrace.join("\n")
raise AuthenticationError.new
end
rescue Net::IMAP::NoResponseError => noresp
logger.error "Error on authentication!"
logger.error noresp.backtrace.join("\n")
raise AuthenticationError.new
rescue Net::IMAP::BadResponseError => bad
logger.error "Error on authentication!"
logger.error bad.backtrace.join("\n")
raise AuthenticationError.new
rescue Net::IMAP::ResponseError => resp
logger.error "Error on authentication!"
logger.error resp.backtrace.join("\n")
raise AuthenticationError.new
end
@username = username
begin
logger.error "IMAP authentication - #{CDF::CONFIG[:imap_auth]}."
if CDF::CONFIG[:imap_auth] == 'NOAUTH'
@imap.login(username, password)
else
@imap.authenticate(CDF::CONFIG[:imap_auth], username, password)
end
@connected = true
rescue Exception => ex
logger.error "Error on authentication!"
logger.error ex.backtrace.join("\n")
raise AuthenticationError.new
end
end
end
def imap
@imap
end
# Function chnage password works only if root has run imap_backend
# and users courier-authlib utility authtest - from courier-imap version 4.0.1
def change_password(username, password, new_password)
ret = ""
cin, cout, cerr = Open3.popen3("/usr/sbin/authtest #{username} #{password} #{new_password}")
ret << cerr.gets
if ret.include?("Password change succeeded.")
return true
else
logger.error "[!] Error on change password! - #{ret}"
return false
end
end
def disconnect
if @connected
@imap.logout
#@imap.disconnect
@imap = nil
@connected = false
end
end
def [](mailboxname)
@last_folder = IMAPFolderList.new(self, @username)[mailboxname]
end
def folders
# reference just to stop GC
@folder_list ||= IMAPFolderList.new(self, @username)
@folder_list
end
def reload
@folder_list.reload if @folder_list
end
def create_folder(name)
# begin
@imap.create(Net::IMAP.encode_utf7(name))
reload
# rescue Exception=>e
# end
end
def delete_folder(name)
begin
@imap.delete(folders[name].utf7_name)
reload
rescue Exception=>e
logger.error("Exception on delete #{name} folder #{e}")
end
end
def message_sent(message)
# ensure we have sent folder
begin
@imap.create(CDF::CONFIG[:mail_sent])
rescue Exception=>e
end
begin
@imap.append(CDF::CONFIG[:mail_sent], message)
folders[CDF::CONFIG[:mail_sent]].cached = false if folders[CDF::CONFIG[:mail_sent]]
rescue Exception=>e
logger.error("Error on append - #{e}")
end
end
def message_bulk(message)
# ensure we have sent folder
begin
@imap.create(CDF::CONFIG[:mail_bulk_sent])
rescue Exception=>e
end
begin
@imap.append(CDF::CONFIG[:mail_bulk_sent], message)
folders[CDF::CONFIG[:mail_sent]].cached = false if folders[CDF::CONFIG[:mail_bulk_sent]]
rescue Exception=>e
logger.error("Error on bulk - #{e}")
end
end
end
class IMAPFolderList
include Enumerable
cattr_accessor :logger
def initialize(mailbox, username)
@mailbox = mailbox
@folders = Hash.new
@username = username
end
def each
refresh if @folders.empty?
#@folders.each_value { |folder| yield folder }
# We want to allow sorted access; for now only (FIXME)
@folders.sort.each { |pair| yield pair.last }
end
def reload
refresh
end
def [](name)
refresh if @folders.empty?
@folders[name]
end
private
def refresh
@folders = {}
result = @mailbox.imap.list('', '*')
if result
result.each do |info|
folder = IMAPFolder.new(@mailbox, info.name, @username, info.attr, info.delim)
@folders[folder.name] = folder
end
else
# if there are no folders subscribe to INBOX - this is on first use
@mailbox.imap.subscribe(CDF::CONFIG[:mail_inbox])
# try again to list them - we should find INBOX
@mailbox.imap.list('', '*').each do |info|
@folders[info.name] = IMAPFolder.new(@mailbox, info.name, @username, info.attr, info.delim)
end
end
@folders
end
end
class IMAPFolder
attr_reader :mailbox
attr_reader :name
attr_reader :utf7_name
attr_reader :username
attr_reader :delim
attr_reader :attribs
attr_writer :cached
attr_writer :mcached
cattr_accessor :logger
@@fetch_attr = ['ENVELOPE','BODYSTRUCTURE', 'FLAGS', 'UID', 'RFC822.SIZE']
def initialize(mailbox, utf7_name, username, attribs, delim)
@mailbox = mailbox
@utf7_name = utf7_name
@name = Net::IMAP.decode_utf7 utf7_name
@username = username
@messages = Array.new
@delim = delim
@attribs = attribs
@cached = false
@mcached = false
end
def activate
if(@mailbox.selected_mailbox != @name)
@mailbox.selected_mailbox = @name
@mailbox.imap.select(@utf7_name)
load_total_unseen if !@cached
end
end
# Just delete message without interaction with Trash folder
def delete(message)
activate
uid = (message.kind_of?(Integer) ? message : message.uid)
@mailbox.imap.uid_store(uid, "+FLAGS", :Deleted)
@mailbox.imap.expunge
# Sync with trash cannot be made - new uid generated - so just delete message from current folder
ImapMessage.delete_all(["username = ? and folder_name = ? and uid = ?", @username, @name, uid])
@cached = false
end
# Deleted messages - move to trash folder
def delete_multiple(uids)
# ensure we have trash folder
begin
@mailbox.imap.create(CDF::CONFIG[:mail_trash])
rescue
end
move_multiple(uids, CDF::CONFIG[:mail_trash])
end
def copy(message, dst_folder)
uid = (message.kind_of?(Integer) ? message : message.uid)
activate
@mailbox.imap.uid_copy(uid, dst_folder)
@mailbox.folders[dst_folder].cached = false if @mailbox.folders[dst_folder]
@mailbox.folders[dst_folder].mcached = false if @mailbox.folders[dst_folder]
end
def copy_multiple(message_uids, dst_folder)
activate
@mailbox.imap.uid_copy(message_uids, dst_folder)
@mailbox.folders[dst_folder].cached = false if @mailbox.folders[dst_folder]
@mailbox.folders[dst_folder].mcached = false if @mailbox.folders[dst_folder]
end
def move(message, dst_folder)
uid = (message.kind_of?(Integer) ? message : message.uid)
activate
@mailbox.imap.uid_copy(uid, dst_folder)
@mailbox.imap.uid_store(uid, "+FLAGS", :Deleted)
@mailbox.folders[dst_folder].cached = false if @mailbox.folders[dst_folder]
@mailbox.folders[dst_folder].mcached = false if @mailbox.folders[dst_folder]
@mailbox.imap.expunge
ImapMessage.delete_all(["username = ? and folder_name = ? and uid = ? ", @username, @name, uid])
@cached = false
@mcached = false
end
def move_multiple(message_uids, dst_folder)
activate
@mailbox.imap.uid_copy(message_uids, @mailbox.folders[dst_folder].utf7_name)
@mailbox.imap.uid_store(message_uids, "+FLAGS", :Deleted)
@mailbox.folders[dst_folder].cached = false if @mailbox.folders[dst_folder]
@mailbox.folders[dst_folder].mcached = false if @mailbox.folders[dst_folder]
@mailbox.imap.expunge
ImapMessage.delete_all(["username = ? and folder_name = ? and uid in ( ? )", @username, @name, message_uids])
@cached = false
@mcached = false
end
def mark_read(message_uid)
activate
cached = ImapMessage.find(:first, :conditions => ["username = ? and folder_name = ? and uid = ?", @username, @name, message_uid])
if cached.unread
cached.unread = false
cached.save
@mailbox.imap.select(@name)
@mailbox.imap.uid_store(message_uid, "+FLAGS", :Seen)
@unseen_messages = @unseen_messages - 1
end
end
def mark_unread(message_uid)
activate
cached = ImapMessage.find(:first, :conditions => ["username = ? and folder_name = ? and uid = ?", @username, @name, message_uid])
if !cached.unread
cached.unread = true
cached.save
@mailbox.imap.select(@name)
@mailbox.imap.uid_store(message_uid, "-FLAGS", :Seen)
@unseen_messages = @unseen_messages + 1
end
end
def expunge
activate
@mailbox.imap.expunge
end
def synchronize_cache
startSync = Time.now
activate
startUidFetch = Time.now
server_messages = @mailbox.imap.uid_fetch(1..-1, ['UID', 'FLAGS'])
startDbFetch = Time.now
cached_messages = ImapMessage.find(:all, :conditions => ["username = ? and folder_name = ?", @username, @name])
cached_unread_uids = Array.new
cached_read_uids = Array.new
uids_to_be_deleted = Array.new
cached_messages.each { |msg|
cached_unread_uids << msg.uid if msg.unread
cached_read_uids << msg.uid unless msg.unread
uids_to_be_deleted << msg.uid
}
uids_to_be_fetched = Array.new
server_msg_uids = Array.new
uids_unread = Array.new
uids_read = Array.new
server_messages.each { |server_msg|
uid, flags = server_msg.attr['UID'], server_msg.attr['FLAGS']
server_msg_uids << uid
unless uids_to_be_deleted.include?(uid)
uids_to_be_fetched << uid
else
if flags.member?(:Seen) && cached_unread_uids.include?(uid)
uids_read << uid
elsif !flags.member?(:Seen) && cached_read_uids.include?(uid)
uids_unread << uid
end
end
uids_to_be_deleted.delete(uid)
} unless server_messages.nil?
ImapMessage.delete_all(["username = ? and folder_name = ? and uid in ( ? )", @username, @name, uids_to_be_deleted]) unless uids_to_be_deleted.empty?
ImapMessage.update_all('unread = 0', ["username = ? and folder_name = ? and uid in ( ? )", @username, @name, uids_read]) unless uids_read.empty?
ImapMessage.update_all('unread = 1', ["username = ? and folder_name = ? and uid in ( ? )", @username, @name, uids_unread]) unless uids_unread.empty?
# fetch and store not cached messages
unless uids_to_be_fetched.empty?
imapres = @mailbox.imap.uid_fetch(uids_to_be_fetched, @@fetch_attr)
imapres.each { |cache|
envelope = cache.attr['ENVELOPE'];
message = ImapMessage.create( :folder_name => @name,
:username => @username,
:msg_id => envelope.message_id,
:uid => cache.attr['UID'],
:from_addr => envelope.from,
:to_addr => envelope.to,
:subject => envelope.subject,
:content_type => cache.attr['BODYSTRUCTURE'].multipart? ? 'multipart' : 'text',
:date => envelope.date,
:unread => !(cache.attr['FLAGS'].member? :Seen),
:size => cache.attr['RFC822.SIZE'])
}
end
@mcached = true
logger.debug("Synchonization done for folder #{@name} in #{Time.now - startSync} ms.")
end
def messages(offset = 0, limit = 10, sort = 'date desc')
# Synchronize first retrieval time
synchronize_cache unless @mcached
if limit == -1
@messages = ImapMessage.find(:all, :conditions => ["username = ? and folder_name = ?", @username, @name], :order => sort)
else
@messages = ImapMessage.find(:all, :conditions => ["username = ? and folder_name = ?", @username, @name], :order => sort, :limit => limit, :offset => offset )
end
end
def messages_search(query = ["ALL"], sort = 'date desc')
activate
uids = @mailbox.imap.uid_search(query)
if uids.size > 1
ImapMessage.find(:all, :conditions => ["username = ? and folder_name = ? and uid in ( ? )", @username, @name, uids], :order => sort )
elsif uids.size == 1
ImapMessage.find(:all, :conditions => ["username = ? and folder_name = ? and uid = ? ", @username, @name, uids.first], :order => sort )
else
return Array.new
end
end
def message(uid)
activate
message = ImapMessage.find(:first, :conditions => ["username = ? and folder_name = ? and uid = ?", @username, @name, uid])
message.set_folder(self)
message
end
def unseen
activate
load_total_unseen if !@cached
@unseen_messages
end
def total
activate
load_total_unseen if !@cached
@total_messages
end
def load_total_unseen
stat = @mailbox.imap.status(@utf7_name, ["MESSAGES", "UNSEEN"])
@total_messages, @unseen_messages = stat["MESSAGES"], stat['UNSEEN']
@cached = true
end
def update_status
@status ||= @mailbox.imap.status(@utf7_name, ["MESSAGES"])
end
def subscribe
@mailbox.imap.subscribe(@utf7_name)
end
def trash?
self.name == CDF::CONFIG[:mail_trash]
end
end

167
lib/webmail/mail2screen.rb Normal file
View file

@ -0,0 +1,167 @@
require 'cdfutils'
module Mail2Screen
def mail2html(mail, msg_id)
footer = ""
parsed_body = create_body(mail, msg_id, footer)
ret = "<table class='messageheader' border='0' cellpadding='0' cellspacing='0' >\n"
ret << "<tbody>\n"
ret << " <tr><td class='label' nowrap='nowrap'>#{_('From')}:</td><td>#{address(mail.from_addrs, @msg_id)}</td></tr>\n"
ret << " <tr><td class='label' nowrap='nowrap'>#{_('To')}:</td><td>#{address(mail.to_addrs, @msg_id)}</td></tr>\n"
if @mail.cc_addrs
ret << " <tr><td class='label' nowrap='nowrap'>#{_('CC')}:</td><td>#{address(mail.cc_addrs, @msg_id)}</td></tr>\n"
end
if @mail.bcc_addrs
ret << " <tr><td class='label' nowrap='nowrap'>#{_('BCC')}:</td><td>#{address(mail.bcc_addrs, @msg_id)}</td></tr>\n"
end
ret << " <tr><td class='label' nowrap='nowrap'>#{_('Subject')}:</td><td>#{h(mime_encoded?(mail.subject) ? mime_decode(mail.subject) : mail.subject)}</dd>\n"
ret << " <tr><td class='label' nowrap='nowrap'>#{_('Date')}:</td><td>#{h message_date(mail.date)}</td></tr>\n"
if footer != ''
ret << " <tr><td class='label' nowrap='nowrap'>#{image_tag('attachment.png')}</td><td>#{footer}</td></tr>\n"
end
ret << " </tbody>\n"
ret << "</table>\n"
ret << "<div class='msgpart'>\n"
ret << parsed_body
ret << "</div>\n"
end
def create_body(mail, msg_id, footer)
charset = (mail.charset.nil? ? 'iso-8859-1' : mail.charset)
if mail.multipart?
ret = ""
if mail.content_type == 'multipart/alternative'
# take only HTML part
mail.parts.each { |part|
if part.content_type == "text/html" or part.multipart?
ret << create_body(part, msg_id, footer)
end
}
return ret
else
mail.parts.each { |part|
if part.multipart?
ret << create_body(part, msg_id, footer)
else
footer << ", " if footer != ''
footer << add_attachment(part.header['content-type'], msg_id)
if part.content_type == "text/plain" or part.content_type.nil?
charset = (part.charset.nil? ? charset : mail.charset)
ret << add_text(part, part.transfer_encoding, charset)
elsif part.content_type == "text/html"
charset = (part.charset.nil? ? charset : mail.charset)
ret << add_html(part, part.transfer_encoding, charset)
elsif part.content_type.include?("image/")
ctype = part.header['content-type']
ret << add_image(ctype, msg_id)
elsif part.content_type.include?("message/rfc822")
ret << "<br/>#{_('Follows attached message')}:<hr/>" << mail2html(TMail::Mail.parse(part.body), msg_id)
end
end
}
return ret
end
else
ret = ""
if mail.content_type == "text/plain" or mail.content_type.nil?
ret << add_text(mail, mail.transfer_encoding, charset)
elsif mail.content_type == "text/html"
ret << add_html(mail, mail.transfer_encoding, charset)
end
return ret
end
end
def add_text(part, encoding, charset)
CGI.escapeHTML(decode_part_text("#{part}", encoding, charset)).gsub(/\r\n/,"<br/>").gsub(/\r/, "<br/>").gsub(/\n/,"<br/>")
end
def add_html(part, encoding, charset)
strip_html(decode_part_text("#{part}", encoding, charset))
end
def decode_part_text(part_str, encoding, charset)
# Parse mail
header, text = "", ""
# Get header and body
#Content-type: text/plain; charset="ISO-8859-1"
#Content-transfer-encoding: quoted-printable
isBody = false
part_str.each_line { |line|
if isBody
text << line
else
if line.strip == ""
isBody = true
else
header << line
end
end
}
# Manage encoding
if not(encoding.nil?) and encoding.downcase == "quoted-printable"
ret = from_qp(text)
elsif not(encoding.nil?) and encoding.downcase == "base64"
ret = "#{text.unpack("m")}"
else
ret = text
end
# manage charset
if ret.nil? or charset.nil? or charset.downcase == "utf-8"
return ret
else
begin
return Iconv.conv("UTF-8",charset.downcase, ret)
rescue Exception => ex
RAILS_DEFAULT_LOGGER.debug("Exception occured #{ex}\n#{ex.backtrace.join('\n')}")
return ret
end
end
end
def add_attachment(content_type, msg_id)
filename = (content_type.nil? or content_type['name'].nil? ? "" : content_type['name'])
if filename == ""
""
else
"<span class='attachment'>&nbsp;<a href='/webmail/download?msg_id=#{msg_id}&ctype=" << CGI.escape(filename) << "'>#{filename}</a></span>"
end
end
def add_image(content_type, msg_id)
filename = (content_type.nil? or content_type['name'].nil? ? "" : content_type['name'])
"<hr/><span class='attachment'><br/><img src='/webmail/download?msg_id=#{msg_id}&ctype=" << CGI.escape(filename) << "' alt='#{filename}'/></span>"
end
def friendly_address(addr)
addr.kind_of?(Net::IMAP::Address) ? ((addr.name.nil? or addr.name.strip == "") ? "#{addr.mailbox}@#{addr.host}" : "#{(mime_encoded?(addr.name.strip) ? mime_decode(addr.name.to_s): addr.name.to_s)}<#{addr.mailbox}@#{addr.host}>") : ((addr.name.nil? or addr.name.strip == "") ? "#{addr.spec}" : "#{(mime_encoded?(addr.name.strip) ? mime_decode(addr.name.to_s): addr.name.to_s)}<#{addr.spec}>")
end
def friendly_address_or_name(addr)
addr.kind_of?(Net::IMAP::Address) ? ((addr.name.nil? or addr.name.to_s == "") ? "#{addr.mailbox}@#{addr.host}" : (mime_encoded?(addr.name.to_s) ? mime_decode(addr.name.to_s): addr.name.to_s)) : ((addr.name.nil? or addr.name.to_s == "") ? "#{addr.spec}" : (mime_encoded?(addr.name.to_s) ? mime_decode(addr.name.to_s): addr.name.to_s))
end
def add_to_contact(addr, msg_id)
"&nbsp;<a href='/contacts/contact/add_from_mail?cstr=#{CGI.escape(friendly_address(addr))}&retmsg=#{msg_id}'>Add to contacts</a>"
end
def short_address(addresses)
ret = ""
addresses.each { |addr| #split(/,\s*/)
ret << "," unless ret == ""
ret << CGI.escapeHTML(friendly_address_or_name(addr))
} unless addresses.nil?
ret
end
def address(addresses, msg_id)
ret = ""
addresses.each { |addr| #split(/,\s*/)
ret << "," unless ret == ""
ret << CGI.escapeHTML(friendly_address_or_name(addr)) << add_to_contact(addr, msg_id)
} unless addresses.nil?
return ret
end
end

View file

@ -0,0 +1,75 @@
require 'mail2screen'
class MailTransform
include Mail2Screen
def get_body(tmail, type)
@mail = tmail
footer = ""
msg_id = ""
ret = mail2html(tmail, msg_id)
ret = ret.gsub(/<br\/>/,"\n").gsub(/&nbsp;/, " ").gsub(/&lt;/, "<").gsub(/&gt;/, ">").gsub(/&amp;/, "&").gsub(/<hr\/>/, "\n").gsub(/\n/, "\n> ") if type == 'text/plain'
ret = ret.gsub(/\r\n/,"<br/>").gsub(/\r/, "<br/>").gsub(/\n/,"<br/>").gsub(/<br\/>/, "<br/>&gt;&nbsp;") unless type == 'text/plain'
return ret
end
def mail2html(mail, msg_id)
footer = ""
parsed_body = create_body(mail, msg_id, footer)
ret = "-----Original Message-----\n#{_('From')}:#{address(mail.from_addrs, @msg_id)}\n"
ret << "#{_('To')}:#{address(mail.to_addrs, @msg_id)}\n"
if @mail.cc_addrs
ret << " #{_('CC')}:#{address(mail.cc_addrs, @msg_id)}\n"
end
if @mail.bcc_addrs
ret << "#{_('BCC')}:#{address(mail.bcc_addrs, @msg_id)}\n"
end
ret << "#{_('Subject')}:#{mime_encoded?(mail.subject) ? mime_decode(mail.subject) : mail.subject}\n"
ret << "#{_('Date')}:#{message_date(mail.date)}\n"
ret << "\n"
ret << "\n"
ret << parsed_body
ret << "\n"
end
def message_date(datestr)
t = Time.now
begin
if datestr.kind_of?(String)
d = (Time.rfc2822(datestr) rescue Time.parse(value)).localtime
else
d = datestr
end
if d.day == t.day and d.month == t.month and d.year == t.year
d.strftime("%H:%M")
else
d.strftime("%Y-%m-%d")
end
rescue
begin
d = imap2time(datestr)
if d.day == t.day and d.month == t.month and d.year == t.year
d.strftime("%H:%M")
else
d.strftime("%Y-%m-%d")
end
rescue
datestr
end
end
end
# Overwrite some staff
def add_to_contact(addr, msg_id)
""
end
def add_attachment(content_type, msg_id)
""
end
def add_image(content_type, msg_id)
""
end
end

View file

@ -0,0 +1,51 @@
module MaildropSerializator
def serialize_to_file
mail_drop_filter = File.new(self.mail_filter_path, "w")
for filter in filters
mail_drop_filter << "# filter '#{filter.name}'\n"
mail_drop_filter << "if (#{filter_expressions(filter)})\n"
mail_drop_filter << "{\n"
mail_drop_filter << " exception {\n"
mail_drop_filter << " to #{dest_folder(filter)}\n"
mail_drop_filter << " }\n"
mail_drop_filter << "}\n"
end
mail_drop_filter.close()
end
private
def dest_folder(filter)
'$DEFAULT/'<<filter.destination_folder.sub(Regexp.new("(#{CDF::CONFIG[:mail_inbox]})(.*)"), '\2')<<"/"
end
def escape_expr_value(text)
text.gsub(".", "\\.").gsub("*", "\\*").gsub("[", "\\[").gsub("]", "\\]").gsub("(", "\\(").gsub(")", "\\)").
gsub("?", "\\?")
end
def filter_expressions(filter)
fe = ""
for exp in filter.expressions
post_flag = "h"
fe << " && " unless fe == ""
if exp.field_name == "^Body"
fe << "/"
post_flag = "b"
else
fe << "/#{exp.field_name}:"
end
if exp.operator == 'contains'
fe << ".*(#{escape_expr_value(exp.expr_value)})/"
else
# starts with
fe << "[ ]*(#{escape_expr_value(exp.expr_value)}).*/"
end
if exp.case_sensitive == 1
fe << "D" << post_flag
else
fe << post_flag
end
end
fe
end
end

4
lib/webmail/routes.rb Normal file
View file

@ -0,0 +1,4 @@
# Webmail mapping
map.connect 'webmail', :controller => 'webmail/webmail', :action => 'messages'

View file

@ -0,0 +1,3 @@
class VirtualEmail < ActiveRecord::Base
def self.table_name() "cdf.wm_virtual" end
end