#! /opt/QtPalmtop/bin/ruby -Ke =begin = maidmail.rb by Saki Sakira version 0.82 from 2003 March 10 last revised at 2003 April 5 == versions 0.1 03Mar10-Mar11 0.2 03Mar13-Mar13 support thread-listing view 0.3 03Mar15-Mar15 enable to click mail-list support mailbox list 0.4 03Mar28-Mar30 enable caching of mailbox 0.5 03Mar30-Apr01 enable to write a message enable to send a e-mail enable to write a replying message 0.6 03Apr02-03 control key events replace QTable to QListBox 0.7 03Apr03 enhance cache easy processing of multipart 0.8 03Apr04-Apr05 change the name form 'Maildir MUA' to 'Maid Mail' minor bug fix メールボックス変更でもスレッド導入 1,2,3ペイン変更 (0.81) メール本文表示専用ウィンドウ MailView (0.82) =end Version = "0.82" $KCODE = 'euc' require 'open3' require 'jcode' require 'nkf' require 'kconv' require 'base64' require 'parsedate' require 'net/smtp' require 'time' require 'qte' require 'qtetable' require 'qpe' include Qte include Qtetable include Qpe $codec = QTextCodec::codecForName("eucJP") $from_address = '' #### # widgets $main_window = nil # mail window $mailbox = nil # mail box $maillist = nil # mail list $mailbody = nil # mail body #### # mail file handler class MailConv Headers = Regexp.new('^from:\s|^to:\s|^cc:\s|^date:\s|^subject:\s', true) MaxLines = 10000 def initialize(filename) @fn = filename @header_h = Hash.new @header = Array.new @body = Array.new header = true i = 0 bound = nil bound_c = 0 IO.foreach(@fn) do |l| next if (/^From / =~ l) if (header and l.chomp == '') then header = false if (%r|multipart/.+;boundary=(.+)| =~ @header_h['content-type']) then pat = $1 if (pat[0] = ?" and /^"([^"]+)"/ =~ pat) then pat = $1 elsif (/^([^;])+;/ =~ pat) then pat = $1 end bound_c = 2 bound = /^--#{pat}/ end end if (header) then if (/^([^:\s]+):\s*(.*)/ =~ l) then key = $1 value = $2 key.downcase! @header_h[key] = value elsif (key) then @header_h[key] += "\n" + l.sub(/^\s+/, '') end @header << l else if (bound and bound =~ l) then bound_c -= 1 if (bound_c <= 0) then @body << "*** 添付ファイルは表示しません ***\n" break end end @body << l i += 1 break if (i > MaxLines) end end end def [](key) @header_h[key.downcase] end def mail? self['received'] end def mail self end def MailConv.conv_nkf(array, conv = nil) array.each_index do |i| str = array[i] str = NKF.nkf('-e', str) array[i] = str end array end def conv_nkf(array, conv = nil) MailConv.conv_nkf(array, conv) end def date_from_subject(conv = nil) t = [1970, 1, 1, 0, 0, 0] t = ParseDate.parsedate(self['date']) if self['date'] date = sprintf("%2d/%2d %02d:%02d", t[1], t[2], t[3], t[4]) from = self['from'].to_s subject = self['subject'].to_s from, subject = conv_nkf([from, subject], conv) from.delete!("\n") subject.delete!("\n") [date, from, subject] end def header_to_string str = '' @header.each do |l| str += l end str end def restricted_header head = [] @header.each do |l| if (Headers =~ l) then head << l end end head end def header @header end def body @body end def to_string(conv = nil) if (mail?) header = restricted_header else header = @header end str = '' conv_nkf(header + ["\n"] + @body, conv).each do |l| str += l end str end end #### # FormatDir class FormatDir Sep = "\0" Cache = '.maidmail' def initialize(dirname, parse = true) if (dirname[-1, 1] != '/') then dirname += '/' end @dirname = dirname @maildir = maildir?(dirname) @timestamp = Time.at(0) @modified = false @fnames = Array.new @timestamps = Array.new @dates = Array.new @froms = Array.new @subjects = Array.new @msgids = Array.new @replys = Array.new @refers = Array.new update end def last_timestamp @timestamp end def timestamp ts = nil if (@maildir) then ts_cur = test(?M, @dirname + "cur/") ts_new = test(?M, @dirname + "new/") ts = if (ts_cur > ts_new) then ts_cur else ts_new end else ts = test(?M, @dirname) end ts end def update ts = timestamp return if (ts == @timestamp) @timestamp = ts @modified = true fnames = current_fnames timestamps = current_timestamps(fnames) update_headers(fnames, timestamps) end def current_fnames fnames = [] if (@maildir) then fnames = Dir.entries(@dirname + "cur/").map! {|f| "cur/" + f} fnames += Dir.entries(@dirname + "new/").map! {|f| "new/" + f} else fnames = Dir.entries(@dirname) end fnames.reject! do |fn| if (@maildir) then %r|/\d| !~ fn else /^\d/ !~ fn end end fnames.sort! do |a, b| if (@maildir) then at = a.sub(%r|/(\d+).*|, '\1').to_i bt = b.sub(%r|/(\d+).*|, '\1').to_i else at = a.sub(/^(\d+).*/, '\1').to_i bt = b.sub(/^(\d+).*/, '\1').to_i end if (at < bt) then -1 elsif (at > bt) then 1 else a <=> b end end fnames end def current_timestamps(fnames) tss = Array.new(fnames.size, nil) fnames.each_index do |i| tss[i] = test(?M, @dirname + fnames[i]) end tss end def update_headers(fns, tss) # delete old items (@fnames.size - 1).downto(0) do |i| j = fns.index(@fnames[i]) if (!j or (@timestamps[i] != tss[j])) then [@fnames, @timestamps, @dates, @froms, @subjects, @msgids, @replys, @refers].each do |a| a.delete_at(i) end end end # insert new items (fns - @fnames).each do |fn| path = @dirname + fn ts = test(?M, path) date, from, subj = (MailConv.new(path)).date_from_subject m = MailConv.new(path) @fnames << fn @timestamps << ts @dates << date @froms << from @subjects << subj @msgids << m['message-id'].to_s @replys << m['in-reply-to'].to_s @refers << m['references'].to_s.split(/\s+/) end end def save(a) if (defined?(@modified) and !@modified) then save_headers(a) if @headers_absent return end @modified = false @msgid_idx = @pres = @sucs = nil mbox = @dirname[ENV['HOME'].size .. -1] cachedir = ENV['HOME'] + '/' + Cache + mbox cachefile = cachedir + ".list" system("/bin/mkdir -p " + cachedir) if !test(?e, cachedir) open(cachefile, "w") do |f| Marshal.dump(self, f) end save_headers(a) end def FormatDir.load(dir) mbox = dir[ENV['HOME'].size .. -1] cachefile = ENV['HOME'] + '/' + Cache + mbox + ".list" r = nil if (FileTest.exist?(cachefile)) then open(cachefile) do |f| r = Marshal.load(f) end else r = FormatDir.new(dir) end r.update r end def save_headers(a) mbox = @dirname[ENV['HOME'].size .. -1] cachedir = ENV['HOME'] + '/' + Cache + mbox cachefile = cachedir + ".headers" system("/bin/mkdir -p " + cachedir) if !test(?e, cachedir) open(cachefile, "w") do |f| Marshal.dump(a, f) end end def FormatDir.load_headers(dir) mbox = dir[ENV['HOME'].size .. -1] cachefile = ENV['HOME'] + '/' + Cache + mbox + ".headers" r = nil if (FileTest.exist?(cachefile)) then open(cachefile) do |f| r = Marshal.load(f) end else @headers_absent = true end r end def maildir?(dirname) a = Dir.entries(dirname) (a.include?("cur") and a.include?("new") and a.include?("tmp")) end def conv_nkf(array, conv = nil) MailConv.conv_nkf(array, conv) end def fnames @fnames end def dates @dates end def froms(conv = nil) conv_nkf(@froms, conv) end def subjects(conv = nil) conv_nkf(@subjects, conv) end def to_array(conv = nil) fn = fnames dt = dates fr = froms(conv) sj = subjects(conv) array = [] fn.each_index do |i| array[i] = fn[i] + Sep + dt[i] + Sep + fr[i] + Sep + sj[i] end return array end def to_string(conv = nil) la = to_array(conv) str = '' la.each_index do |i| sfn, sdt, sfr, ssj = la[i].split(Sep, -1) sdate = (sdt + " " * 11)[0 .. 10] sfrom = (sfr + " " * 30)[0 .. 29] if (/.\z/ !~ sfrom) then sfrom[-1, 1] = '' end ssubject = (ssj + " " * 40)[0 .. 39] if (/.\z/ !~ ssubject) then ssubject[-1, 1] = '' end str += sdate + " " + sfrom + " " + ssubject + "\n" end return str end def prepare_msg_ids @msgid_idx = Hash.new @msgids.each_with_index do |msgid, i| @msgid_idx[msgid] = i end end def make_thread prepare_msg_ids size = @fnames.size @pres = Array.new(size) @sucs = Array.new(size) size.times do |i| ([@replys[i]] + @refers[i]).each do |msgid| pre = @msgid_idx[msgid] if (pre) then @pres[i] = pre @sucs[pre] = Array.new if (!@sucs[pre]) @sucs[pre] << i break end end # Thread.pass end end def pres make_thread if (!@pres) @pres end def sucs make_thread if (!@sucs) @sucs end def get_tree(head, level = 0) indexs = [head] levels = [level] if (@sucs[head]) then @sucs[head].each do |suc| newi, newl = get_tree(suc, level + 1) indexs += newi levels += newl end # Thread.pass end [indexs, levels] end def sort_with_thread make_thread size = @pres.size processed = Array.new(size, false) indexs = Array.new levels = Array.new size.times do |i| j = i if (!processed[j]) then while (@pres[j]) do j = @pres[j] end newi, newl = get_tree(j) indexs += newi levels += newl newi.each do |k| processed[k] = true end end # Thread.pass end [indexs, levels] end def [](index) if (index < 0 or index >= @fnames.size) then return nil end if (@froms[index] == 'directory') then return FormatDir.new(@dirname + @fnames[index]) else return MailConv.new(@dirname + @fnames[index]) end end end #### # mail boxes class Mailboxes Cache = ".maildirmua" def initialize(dir = false) if (dir) then @dir = dir else @dir = ENV['HOME'] end @dir += "/" if @dir[-1] != ?/ @cachedir = @dir + Cache + '/' end def to_array a = Array.new Dir.foreach(@dir) do |m| if (m[-1] != ?. and File.stat(@dir + m).directory?) then a += maildirs(@dir + m + "/") end end mhdir = @dir + 'Mail/' a += mhs(mhdir) if (test(?e, mhdir) and File.stat(mhdir).directory?) a.each do |b| b.sub!(%r|^#{@dir}|, '') end a end def cachedir(dir) @cachedir + dir[@dir.size .. -1] end def home_dir @dir end def maildirs(dir) r = Array.new fs = Dir.entries(dir) if (fs.include?("new") and fs.include?("cur") and fs.include?("tmp")) then r << dir fs.each do |f| if (f[0] != ?. and /^(new|cur|tmp)$/ != f and File.stat(dir + f).directory?) then r += maildirs(dir + f + "/") end end end r end def mhs(dir) r = [dir] Dir.foreach(dir) do |f| if (f[0, 1] != "." and File.stat(dir + f).directory?) then r += mhs(dir + f + "/") end end r end end #### # mailboxes class MBoxList < QListBox def initialize(parent = nil, name = '', dir = nil) super(parent, name) @parent = parent @home = ENV['HOME'] + "/" @mboxs = Array.new setSelectionMode(Single) width = QPEApplication.desktop().width / 5 # setMaximumWidth(width) setFixedWidth(width) set_mboxs connect(self, QSIGNAL("currentChanged(QListBoxItem *)"), self, "show_list()") connect(self, QSIGNAL("pressed(QListBoxItem *)"), self, "show_list()") catchEvent end def keyPressEvent(e) $main_window.keyPressEvent(e) end def set_mboxs @mboxs = Mailboxes.new(@home).to_array @mboxs.each_index do |i| str = (@mboxs[i])[0 .. -2].gsub(%r|[^/]+/|, " ") insertItem(tr(str), -1) end end def next_mbox row = currentItem + 1 if (row > count) then row = currentItem end setCurrentItem(row) end def previous_mbox row = currentItem - 1 if (row < 0) then row = 0 end setCurrentItem(row) end def show_list(dummy) Thread.kill(@show_thread) if @show_thread @show_thread = Thread.new do @parent.set_maillist(@home + @mboxs[currentItem]) Thread.exit end @show_thread.priority = -3 end end #### # mail list class MailHeaders def initialize @sorted_fns = @headers = nil end def sorted_fns=(s) @sorted_fns = s end def sorted_fns @sorted_fns end def headers=(h) @headers = h end def headers @headers end end class MailList < QListBox def initialize(parent = nil, name = '', dir = false) super(parent, name) @parent = parent @dir = dir @mdir = false # setSelectionMode(Single) set_dir(@dir) if (@dir) connect(self, QSIGNAL("currentChanged(QListBoxItem *)"), self, "show_body()") connect(self, QSIGNAL("pressed(QListBoxItem *)"), self, "show_body()") catchEvent end def keyPressEvent(e) $main_window.keyPressEvent(e) end def set_dir_normal(dir) @dir = dir d = FormatDir.new(dir) strs = d.to_string.split("\n") setNumRows(strs.length) strs.each_index do |i| setText(i, 0, tr(strs[i])) end end def set_dir(dir) @load_thread.join if (@load_thread) headers = MailHeaders.new headers.sorted_fns = @sorted_fns headers.headers = @sorted_headers @mdir.save(headers) if (@mdir and @dir != dir) @dir = dir @mdir = nil prepareFormatDir headers = FormatDir.load_headers(@dir) if (headers) then @sorted_fns = headers.sorted_fns @sorted_headers = headers.headers repaint end # @load_thread.run end def repaint clear clearSelection() @sorted_headers.each do |h| insertItem(tr(h), -1) end if (count > 0) then setCurrentItem(count - 1) end show_body end def update @load_thread.join if (@load_thread) if (@mdir.last_timestamp != @mdir.timestamp) then prepareFormatDir end end def prepareFormatDir Thread.abort_on_exception = true @load_thread = Thread.new do sleep 1 d = FormatDir.load(@dir) headers = d.to_string.split("\n") sorted_headers = Array.new(headers.size) fnames = d.fnames sorted_fns = Array.new(fnames.size) indexs, levels = d.sort_with_thread indexs.each_index do |i| sorted_fns[i] = fnames[indexs[i]] line = " " * levels[i] + headers[indexs[i]] sorted_headers[i] = line end changed = (@sorted_headers != sorted_headers) or (@sorted_fns != sorted_fns) @mdir = d @sorted_headers = sorted_headers @sorted_fns = sorted_fns headers = MailHeaders.new headers.sorted_fns = @sorted_fns headers.headers = @sorted_headers @mdir.save(headers) repaint if changed end @load_thread.priority = -2 end def nextMessage row = currentItem + 1 if (row > count) then row = currentItem end setCurrentItem(row) show_body end def previousMessage row = currentItem - 1 if (row < 0) then row = 0 end setCurrentItem(row) show_body end def show_body(dummy = nil) if (currentItem < count and currentItem >= 0) then fn = @dir + @sorted_fns[currentItem].to_s $mailbody.show(fn) end end end #### # mail body class MailBody < QTextView def initialize(parent = nil, name = '') super(parent, name) @parent = parent height = QPEApplication.desktop.height * 2 / 5 setFixedHeight(height) setText(tr("")) catchEvent end def show(fn = false) if (!fn) then super() return end @fn = fn if (test(?f, fn)) then Thread.kill(@show_thread) if @show_thread @show_thread = Thread.new do @mail = m = MailConv.new(fn) setText(tr(m.to_string)) Thread.exit end @show_thread.priority = -3 else setText(tr("見付かりません")) end end def fn @fn end def mail @show_thread.join if @show_thread @mail end def keyPressEvent(e) $main_window.keyPressEvent(e) end def nextLine scrollBy(0, 20) end def previousLine scrollBy(0, -20) end def nextPage scroll = visibleHeight - 20 scrollBy(0, scroll) end end #### # mail list and mail body #class MailListBody < QSplitter class MailListBody < QVBox def initialize(parent = nil, name = '') super(parent, name) @parent = parent # setOrientation(Vertical) $maillist = @list = MailList.new(self) $mailbody = @body = MailBody.new(self) end def set_maillist(dir) @list.set_dir(dir) end def set_mailbody(fn) @body.show(fn) end def new_message @parent.new_message end def reply_message(mailbody) @parent.reply_message(mailbody) end end #### # message editor class MessageEditor < QVBox CiteMark = '> ' def initialize(parent = nil, name = '') super(parent, name, Qt::WType_Modal) @parent = parent setCaption(tr("Maid Mail Editor")) setFont(QFont.new("lcfont", 16)) header = QGrid.new(2, self) QLabel.new(tr("送信者 "), header).setAlignment(AlignRight | AlignVCenter) @from = QLineEdit.new("", header) QLabel.new(tr("宛先 "), header).setAlignment(AlignRight | AlignVCenter) @to = QLineEdit.new("", header) QLabel.new(tr("CC "), header).setAlignment(AlignRight | AlignVCenter) @cc = QLineEdit.new("", header) QLabel.new(tr("BCC "), header).setAlignment(AlignRight | AlignVCenter) @bcc = QLineEdit.new("", header) QLabel.new(tr("題名 "), header).setAlignment(AlignRight | AlignVCenter) @subject = QLineEdit.new("", header) @body = QMultiLineEdit.new(self) QLabel.new("", self) footer = QHBox.new(self) QLabel.new("", footer) @cancel_btn = QPushButton.new(tr("キャンセル"), footer) QLabel.new("", footer) @send_btn = QPushButton.new(tr("送信"), footer) QLabel.new("", footer) QLabel.new("", self) connect(@cancel_btn, QSIGNAL("clicked()"), self, "cancel()") connect(@send_btn, QSIGNAL("clicked()"), self, "send()") end def set_from(str) @from.setText(tr(str)) end def set_to(str) @to.setText(tr(str)) end def set_cc(str) @cc.setText(tr(str)) end def set_bcc(str) @bcc.setText(tr(str)) end def set_subject(str) @subject.setText(tr(str)) end def set_body(str) @body.setText(tr(str)) end def toeuc(utf) $codec.fromUnicode(utf).data end def get_from toeuc(@from.text) end def get_to toeuc(@to.text) end def get_cc toeuc(@cc.text) end def get_bcc toeuc(@bcc.text) end def get_subject toeuc(@subject.text) end def get_body toeuc(@body.text) end def cancel close end def send $from_address = toeuc(@from.text) mail = SendMail.new($from_address) mail.header_reply(@mail) mail.set_to(get_to) mail.set_cc(get_cc) mail.set_bcc(get_bcc) mail.set_subject(get_subject) mail.set_data(get_body) if (mail.send) then puts "送信したよ" $stdout.flush close else puts "失敗したよ" $stdout.flush end @mail = false end def new_message set_from($from_address) set_to("") set_cc("") set_bcc("") set_subject("") set_body("") showMaximized end def nkf(str) if (str) then NKF.nkf('-e', str) else "" end end def reply_message(mailbody) set_from($from_address) fn = mailbody.fn if (!fn) then new_message return end tos = Array.new ccs = Array.new mconv = MailConv.new(fn) mail = mconv.mail if (reply = mail['reply-to']) then tos << nkf(reply) else tos << nkf(mail['from']) end ccs << nkf(mail['cc']) << nkf(mail['to']) subj = nkf(mail['subject']) if (/^re:/i !~ subj) then subj = "Re:" + subj end body = CiteMark + mconv.to_string.gsub(/\n/, "\n#{CiteMark}") [tos, ccs].each do |rcpts| rcpts.delete_if {|r| !r.include?("@")} end set_to(tos.join(",")) set_cc(ccs.join(",")) set_bcc("") set_subject(subj) set_body(body) @mail = mail showMaximized end end #### # mail view module KeyBindings NewMessage = ?W ReplyMessage = ?R PreviousMessage = ?P NextMessage = ?N ScrollUp = 4117 ScrollDown = 4115 ReadMessage = " "[0] Quit = ?Q SyncMailbox = ?S PreviousMailbox = 4114 NextMailbox = 4116 OnePane = ?1 TwoPanes = ?2 ThreePanes = ?3 ViewMessage = ?V ViewHeader = ?H end class MailView < QVBox include KeyBindings class TextView < QTextView def initialize(parent = nil, name = '') super(parent, name) @parent = parent catchEvent end def keyPressEvent(e) @parent.keyPressEvent(e) end def nextPage scroll = visibleHeight - 20 scrollBy(0, scroll) end end def initialize(parent = nil, name = '') super(parent, name, Qt::WType_Modal) @parent = parent setCaption(tr("Maid Mail Viewer")) @header = QLabel.new("", self) @header.setFont(QFont.new("lcfont", 16, QFont::Bold)) @body = TextView.new(self) @body.setFont(QFont.new("lcfont", 16)) set_message(false) catchEvent end def keyPressEvent(e) case e.key when NewMessage then $main_window.new_message when ReplyMessage then $main_window.reply_message($mailbody) when PreviousMessage then $maillist.previousMessage set_message(@show_header) when NextMessage then $maillist.nextMessage set_message(@show_header) when ReadMessage then @body.nextPage when Quit then close when ViewHeader then toggle_message end # e.ignore end def set_message(show_h = false) @mail = $mailbody.mail @fn = $mailbody.fn if (!@mail) then return if (!@fn) @mail = MailConv.new(@fn) end if (show_h) then set_message_header else set_message_standard end @show_header = show_h end def set_message_standard header = MailConv.conv_nkf(@mail.restricted_header) @header.setText(tr(header.join(""))) body = MailConv.conv_nkf(@mail.body) @body.setText(tr(body.join(""))) end def set_message_header @header.setText(tr("")) body = MailConv.conv_nkf(@mail.header + @mail.body) @body.setText(tr(body.join(""))) end def toggle_message @show_header = !@show_header if (@show_header) then set_message_header else set_message_standard end end end #### # Main Body class MainBody < QHBox def initialize(parent = nil, name = '') super(parent, name) @parent = parent $mailbox = @box = MBoxList.new(self) @listbody = MailListBody.new(self) end def set_maillist(fn) @listbody.set_maillist(fn) end def new_message @parent.new_message end def reply_message(mailbody) @parent.reply_message(mailbody) end def keyPressEvent(e) $mainWindow.keyPressEvent(e) end end #### # configuration #class ConfigDialog < QDialog class ConfigDialog < QGrid def initialize(parent = nil, name = '') super(2, parent, name) setCaption(tr("設定")) QLabel.new(tr("送信者名"), self) @name = QLineEdit.new("", self) QLabel.new("", self) QLabel.new("", self) QLabel.new(tr("メールアドレス"), self) @address = QLineEdit.new("", self) @address.setMinimumWidth(QPEApplication.desktop().width / 4) QLabel.new("", self) QLabel.new("", self) QLabel.new("", self) @btn = QPushButton.new(tr("設定"), self) connect(@btn, QSIGNAL("clicked()"), self, "set_address") end def prepare_address if (/^(.*[^\s])\s*<(.*)>/ =~ $from_address) then name = $1 address = $2 @name.setText(tr(name)) @address.setText(tr(address)) end end def toeuc(utf) $codec.fromUnicode(utf).data end def set_address name = toeuc(@name.text) address = toeuc(@address.text) $from_address = name + " <" + address + ">" ConfigFile::save_address close end end module ConfigFile DirName = ".maidmail/" AddressFile = ".address" def save_address dir = ENV['HOME'] + "/" + DirName Dir.mkdir(dir) if !test(?e, dir) fn = dir + AddressFile open(fn, "w") do |f| f.puts $from_address end end module_function :save_address def load_address fn = ENV['HOME'] + "/" + DirName + AddressFile if (test(?e, fn)) then $from_address = IO.readlines(fn)[0].chomp end end module_function :load_address end #### # show mail class MailMain < QMainWindow include KeyBindings def initialize super setCaption("Maid Mail by Ruby/Qte") setMenu @menu.show @main = MainBody.new(self) setCentralWidget(@main) @editor = MessageEditor.new @viewer = MailView.new @config = ConfigDialog.new catchEvent end def setMenu @menu = QPEMenuBar.new(self) file = QPopupMenu.new(self, "File") file.insertSeparator() file.insertItem("Config", self, "config()") file.insertSeparator() file.insertItem("Quit", self, "close()") @menu.insertItem("File", file) if (false) then a = QAction.new("Config", "&Config", 0, self) connect(a, QSIGNAL("activated()"), self, "config()") a.addTo(file) a = QAction.new("Quit", "&Quit", 0, self) connect(a, QSIGNAL("activated()"), self, "close()") a.addTo(file) menu.insertItem("File", file) end end def config @config.prepare_address @config.show end def new_message @editor.new_message end def reply_message(mailbody) mailbody = $mailbody if !mailbody @editor.reply_message(mailbody) end def view_message @viewer.showMaximized @viewer.set_message end def keyPressEvent(event) key = event.key case key when NewMessage then new_message when ReplyMessage then reply_message($mailbody) when PreviousMessage then $maillist.previousMessage when NextMessage then $maillist.nextMessage when ScrollUp then $mailbody.nextLine when ScrollDown then $mailbody.previousLine when ReadMessage then $mailbody.nextPage when Quit then close when SyncMailbox then $maillist.update when PreviousMailbox then $mailbox.previous_mbox when NextMailbox then $mailbox.next_mbox when OnePane then $mailbox.hide $maillist.show $mailbody.hide when TwoPanes then $mailbox.hide $mailbody.show $maillist.show when ThreePanes then $mailbox.show $mailbody.show $maillist.show when ViewMessage then view_message end event.ignore end end #### # SMTP class SendMail Host = '127.0.0.1' Port = 25 def initialize(from_addr = $from_address) @from_addr = from_addr @rcpt_addrs = Array.new @data = false @header = 'From: ' + mime(from_addr) + "\n" @to_str = @cc_str = @subject_str = '' end def send prepare_data if (@from_addr and @rcpt_addrs and @data) then @rcpt_addrs.uniq! begin Net::SMTP.start(Host, Port) do |smtp| smtp.send_mail(@data, parse_rcpt(@from_addr)[0], @rcpt_addrs) end true rescue false end end end def prepare_data now = Time.now @header += @to_str + @cc_str + @subject_str @header += "Date: " + now.rfc2822 + "\n" @header += "Message-Id: <" + now.to_i.to_s + now.usec.to_s + $$.to_s + "." + "mdmua" + "." + parse_rcpt(@from_addr)[0] + ">\n" @header += "User-Agent: Maid Mail #{Version} " + `/bin/uname -smv` @data = @header + "\n" + @data end def parse_rcpt(str) addrs = Array.new str.split(",").each do |addr| addr.sub!(/\s*".*"\s*/, '') addr.sub!(/\s*\(.*\)\s*/, '') if (/<(.+)>/ =~ addr) then addr = $1 end addr.delete!("\s\t") addrs << addr end addrs end def mime(str) r = '' while (i = str.mbchar?) do r += str[0, i] str[0, i] = '' estr = '' str.each_char do |c| break if (c.size == 1) estr += c end if (estr != '') then jstr = Kconv.tojis(estr) r += "=?ISO-2022-JP?B?" + encode64(jstr).chomp + "?=" str[0, estr.size] = '' end end r += str r end def set_to(str) if (str != '') then @to_str = "To: " + mime(str) + "\n" @rcpt_addrs += parse_rcpt(str) end end def set_cc(str) if (str != '') then @cc_str = "Cc: " + mime(str) + "\n" @rcpt_addrs += parse_rcpt(str) end end def set_bcc(str) @rcpt_addrs += parse_rcpt(str) end def set_subject(str) @subject_str = "Subject: " + mime(str) + "\n" end def set_data(str) @data = Kconv.tojis(str.gsub(/\n.\n/, "\n. \n")) end def header_reply(mail) if (mail) then reply = mail['message-id'].to_s refers =mail['references'].to_s.split(/\s+/) @header += "In-Reply-To: " + reply + "\n" @header += "References: " + reply + "\n" refers.each do |ref| @header += "\t" + ref + "\n" end end end end #### # application ConfigFile::load_address a = QPEApplication.new([$0] + ARGV) QPEApplication.setFont(QFont.new("lcfont")) a.setDefaultCodec(QTextCodec::codecForName("eucJP")) $main_window = sm = MailMain.new a.showMainWidget(sm) a.exec