OSDN Git Service

Add web mail viewer 'Maverick'.
[mave/mave.git] / mave_models.rb
index 40b116c..9c8d53c 100644 (file)
@@ -1,4 +1,4 @@
-require 'gdbm'
+require 'xdbm'
 require 'time'
 require 'net/pop'
 require 'net/smtp'
@@ -35,9 +35,9 @@ end
 
 #===============================================================================
 #
-#      連番処理付き GDBM クラス
+#      連番処理付き XDBM クラス
 #
-class SqGDBM < GDBM
+class SqXDBM < XDBM
 
        @@start = 100
        @@sym = '#last#'
@@ -66,9 +66,9 @@ end
 
 #===============================================================================
 #
-#      フラグ処理/メール総数管理用 GDBM クラス
+#      フラグ処理/メール総数管理用 XDBM クラス
 #
-class FlagGDBM < GDBM
+class FlagXDBM < XDBM
 
        @@n_mail_sym = '#n_mail#'                                                                       # メール総数
 
@@ -211,11 +211,12 @@ class MaveAccount < MaveBaseModel
                @smtp_password  = @account[:SMTP_PASSWORD]
                @smtp_authtype  = @account[:SMTP_AUTHTYPE]
                @smtp_over_tls  = @account[:SMTP_OVER_TLS]
+               @smtp_tls_certs = @account[:SMTP_TLS_CERTS]
 
                @import_command = @account[:IMPORT_COMMAND]
 
                @hash_id                = Digest::MD5.hexdigest(@account[:USER_ADDRESS])[0, 8]
-               @pop_uids               =     GDBM.new(@configs[:ROOT_DIRECTORY] + "/pop_uids_#{name}", 0600)
+               @pop_uids               =     XDBM.new(@configs[:ROOT_DIRECTORY] + "/pop_uids_#{name}", 0600)
        end
 
        #-----------------------------------------------------------
@@ -270,7 +271,7 @@ class MaveAccount < MaveBaseModel
        #
        def smtp
                begin
-                       Net::SMTP.enable_tls(OpenSSL::SSL::VERIFY_NONE) if(@smtp_over_tls)
+                       Net::SMTP.enable_tls(@smtp_over_tls, @smtp_tls_certs) if(@smtp_over_tls)
                        Net::SMTP.start(@smtp_server, @smtp_port, @smtp_helo, @smtp_account, @smtp_password, @smtp_authtype) {|smtp|
                                yield(_('Connected.'))
                                yield(smtp)
@@ -283,7 +284,7 @@ class MaveAccount < MaveBaseModel
        end
 
        def close
-               @pop_uids.reorganize
+               @pop_uids.reorganize;           @pop_uids.close
        end
 end
 
@@ -310,11 +311,11 @@ class MaveAccounts < MaveBaseModel
 
        #-----------------------------------------------------------
        #
-       #       メールアカウントを順に渡す(有効なアカウントのみ)
+       #       メールアカウントを順に渡す
        #
-       def each                                                                                                        #### 適切な順序で返すようにする
+       def each(all = false)                                                                           #### 適切な順序で返すようにする
                @accounts.each {|name, account|
-                       yield(account) if(account.enable)
+                       yield(account) if(account.enable or all)
                }
        end
 
@@ -349,7 +350,7 @@ class MaveAccounts < MaveBaseModel
        end
 
        def close
-               each {|account| account.close }
+               each(all = true) {|account| account.close }
        end
 end
 
@@ -381,8 +382,8 @@ class MaveDirectory < MaveBaseModel
                }
                @cluster_paths << '' if(@cluster_paths.empty?)
 
-               gdbm_flags              = params[:CONFIGS][:GDBM_FLAGS]
-               @filename_sq    =   SqGDBM.new(@path + '/filename_sq',  0600, gdbm_flags)       # ベースファイル名<-メール連番
+               xdbm_flags              = params[:CONFIGS][:XDBM_FLAGS]
+               @filename_sq    =   SqXDBM.new(@path + '/filename_sq',  0600, xdbm_flags)       # ベースファイル名<-メール連番
        end
 
        #---------------------------------------- MaveDirectory ----
@@ -517,23 +518,34 @@ class MaveFolder < MaveDirectory
        attr_reader :name                                                                                       # フォルダの名前
        attr_reader     :flags_sq
 
+       @@debug_what_scache = false
+
+       #--------------------------------------------- MaveFolder ----
+       #
+       #       メールの概要キャッシュ(インクリメンタルサーチ用)表示、切り替え(デバッグ用)
+       #
+       def self.toggle_what_scache
+               @@debug_what_scache = !@@debug_what_scache
+       end
+
        def initialize(params)
                params[:PATH]   = params[:CONFIGS][:ROOT_DIRECTORY] + '/' + params[:NAME]
                super
                @name                   = params[:NAME]
 
-               gdbm_flags              = params[:CONFIGS][:GDBM_FLAGS]
-               @sq_rootsq              =   SqGDBM.new(@path + '/sq_rootsq',    0600, gdbm_flags)       # メール連番<-最新順ルート連番
+               xdbm_flags              = params[:CONFIGS][:XDBM_FLAGS]
+               @sq_rootsq              =   SqXDBM.new(@path + '/sq_rootsq',    0600, xdbm_flags)       # メール連番<-最新順ルート連番
 
-               @messageid_sq   =     GDBM.new(@path + '/messageid_sq', 0600, gdbm_flags)       # メッセージID<-メール連番
-               @sq_messageid   =     GDBM.new(@path + '/sq_messageid', 0600, gdbm_flags)       # メール連番<-メッセージID
+               @messageid_sq   =     XDBM.new(@path + '/messageid_sq', 0600, xdbm_flags)       # メッセージID<-メール連番
+               @sq_messageid   =     XDBM.new(@path + '/sq_messageid', 0600, xdbm_flags)       # メール連番<-メッセージID
 
-               @rootsq_sq              =     GDBM.new(@path + '/rootsq_sq',    0600, gdbm_flags)       # ルートメール連番<-メール連番
-               @parentsq_sq    =     GDBM.new(@path + '/parentsq_sq',  0600, gdbm_flags)       # 親メール連番<-メール連番
-               @childsqs_sq    =     GDBM.new(@path + '/childsqs_sq',  0600, gdbm_flags)       # 子メール連番群<-メール連番        #### 統合不可?
+               @rootsq_sq              =     XDBM.new(@path + '/rootsq_sq',    0600, xdbm_flags)       # ルートメール連番<-メール連番
+               @parentsq_sq    =     XDBM.new(@path + '/parentsq_sq',  0600, xdbm_flags)       # 親メール連番<-メール連番
+               @childsqs_sq    =     XDBM.new(@path + '/childsqs_sq',  0600, xdbm_flags)       # 子メール連番群<-メール連番        #### 統合不可?
 
-               @flags_sq               = FlagGDBM.new(@path + '/flags_sq',             0600, gdbm_flags)       # 各種フラグ群<-メール連番
-               [:RED, :FLAG, :NOTICE, :FOLD].each {|flag|
+               @abstract_sq    =     XDBM.new(@path + '/abstract_sq',  0600, xdbm_flags)       # メール概要<-メール連番
+               @flags_sq               = FlagXDBM.new(@path + '/flags_sq',             0600, xdbm_flags)       # 各種フラグ群<-メール連番
+               [:RED, :FLAG, :NOTICE, :FOLD, :TOYOU].each {|flag|
                        @flags_sq.alloc_flag(flag)
                }
                @flags_sq.get_n                         || @flags_sq.reset_n                            # メール総数
@@ -579,25 +591,37 @@ class MaveFolder < MaveDirectory
                sq ? MaveMail.new({:CONFIGS => @configs, :FILE => File.new(@path + '/' + @filename_sq[sq]), :FOLDER => self, :SQ => sq}) : nil
        end
        def get_mail_by_message_id(message_id)
-               get_mail(@sq_messageid[message_id])
+               message_id and get_mail(@sq_messageid[message_id])
+       end
+
+       #------------------------------------------- MaveFolder ----
+       #
+       #       関連するメール連番を返す
+       #
+       def get_sq_by_message_id(message_id)
+               @sq_messageid[message_id]
+       end
+       def get_rootsq_by_sq(sq)
+               @rootsq_sq[sq]
        end
+
        #------------------------------------------- MaveFolder ----
        #
-       #       メールの概要を返す
+       #       メールの概要を内部コーディング(UTF-8)で返す
        #
        def abstract_of_mail(sq, mail, marks = {}, padding = '')
+               @@debug_what_scache and @abstract_sq[sq] and return(abstract_of_mail_for_search(sq, nil, marks))
                '%s%c%c %3d %-*s %*s %4s %c%s %s%c%s' % [
                        marks.size == 0 ? '' : (marks[sq] ? 'M ' : '- '),
                        red?(sq) ? ?. : ?x,
                        notice?(sq) ? ?# : (flag?(sq) ? ?F : ?.),
                        (it = last_sq.to_i - sq.to_i) < 1000 ? it : 999,
                        fw = 16,
-                       @folder_configs[:SEND_VIEW] ? mail.pseudo_to.snip(fw) : mail.pseudo_from.snip(fw),
+                       @folder_configs[:SEND_VIEW] ? mail.pseudo_to.snip(fw, 'UTF-8') : mail.pseudo_from.snip(fw, 'UTF-8'),
                        dw = Time.mystrftime_len,
                        mail.date ? mail.date.mystrftime : '-' * dw,
                        mail.size.to_h,
-                       ?.,             #### '.vw'[toyou?],
+                       '.vw'[toyou?(sq)],
                        mail.multipart? ? '@' : '.',
                        padding,
                        '#+=-'[(padding == '' ? 0 : 2) + (fold?(sq) ? 0 : 1)],
@@ -607,6 +631,34 @@ class MaveFolder < MaveDirectory
 
        #------------------------------------------- MaveFolder ----
        #
+       #       メールの概要キャッシュ(インクリメンタルサーチ用)を返す
+       #
+       def abstract_of_mail_for_search(sq, dummy = nil, marks = {})
+               '%s%c%c %3d %s' % [
+                       marks.size == 0 ? '' : (marks[sq] ? 'M ' : '- '),
+                       red?(sq) ? ?. : ?x,
+                       notice?(sq) ? ?# : (flag?(sq) ? ?F : ?.),
+                       (it = last_sq.to_i - sq.to_i) < 1000 ? it : 999,
+                       @abstract_sq[sq] || (@abstract_sq[sq] = abstract_of_mail_for_search_cache(sq, get_mail(sq))),
+               ]
+       end
+       def abstract_of_mail_for_search_cache(sq, mail)
+               pseudo = @folder_configs[:SEND_VIEW] ? mail.pseudo_to : mail.pseudo_from
+               '%s %s %c%s %s %s' % [
+                       mail.date ? mail.date.mystrftime(false) : '-',          #### 日付を数字で持っておく手もある
+                       mail.size.to_h,
+                       '.vw'[toyou?(sq)],
+                       mail.multipart? ? '@' : '.',
+                       (it = phoneticize('%s %s' % [pseudo, mail.subject.decode_mh])) ? it : '',       # 文字列の読み(ローマ字)表記
+                       mail.subject.decode_mh,
+               ]
+       end
+       def delete_abstract(sq)
+               @abstract_sq.delete(sq)                                                                 # メール概要を消す
+       end
+
+       #------------------------------------------- MaveFolder ----
+       #
        #       メール DB のリンク構造
        #
        #                                               (@sq_rootsq)
@@ -640,6 +692,11 @@ class MaveFolder < MaveDirectory
        #               #### マークした折り畳み中のスレッドを結合する場合の扱い
        #
        def join_mail(sq, parent_sq)
+               (pp_sq = parent_sq) == sq and return(false)                             # 自分や自分の子には結合不可
+               while(pp_sq != @rootsq_sq[pp_sq])
+                       (pp_sq = @parentsq_sq[pp_sq]) == sq and return(false)
+               end
+
                unjoin_mail(sq) unless(@rootsq_sq[sq] == sq)                    # 自分がルート以外の場合
 
 #              @sq_rootsq.delete(x) == sq                                                              # 逆は表示時に消す
@@ -711,6 +768,7 @@ class MaveFolder < MaveDirectory
                @filename_sq[sq = @filename_sq.last_sq] = filename
                @messageid_sq[sq] = mail.message_id
                @sq_messageid[mail.message_id] = sq
+#              @abstract_sq[sq] = abstract_of_mail_for_search_cache(sq, mail)  # メール概要登録
 
                # 直近の親を捜す
                parent_sqi = 0; it = nil
@@ -800,6 +858,7 @@ class MaveFolder < MaveDirectory
                @flags_sq.dec_n                                                                                 # メール総数カウントダウン
 
                @flags_sq.delete(sq)
+               @abstract_sq.delete(sq)                                                                 # メール概要を消す
                delete(@filename_sq.delete(sq))                                                 # ファイル本体を消す
 
                @dirty += 1
@@ -950,6 +1009,18 @@ class MaveFolder < MaveDirectory
                @dirty += 1
        end
 
+       def toyou(sq)
+               @flags_sq.set_flag(sq, :TOYOU, ?T)
+               @dirty += 1
+       end
+       def ccyou(sq)
+               @flags_sq.set_flag(sq, :TOYOU, ?C)
+               @dirty += 1
+       end
+       def toyou?(sq)
+               '-CT'.index(@flags_sq.get_flag(sq, :TOYOU)) || 0
+       end
+
        #------------------------------------------- MaveFolder ----
        #
        #       メールの添付ファイルを展開する
@@ -975,8 +1046,8 @@ class MaveFolder < MaveDirectory
                                fh.write(line + "\n")
                        }
                }
-               mail = MavePseudoMail.new({:CONFIGS => @configs, :FILE => File.new(path + '/' + halfname)})
-               overwrite_mail(mail, source_mail)
+               mail = MavePseudoMail.new({:CONFIGS => @configs, :FILE => (xmail = File.new(path + '/' + halfname))})
+               overwrite_mail(xmail, source_mail)
                delete(halfname) unless(RUBY_PLATFORM =~ /i.86-mswin32/)        ####
                @dirty += 1     ####
        end
@@ -1034,6 +1105,7 @@ class MaveFolder < MaveDirectory
                @rootsq_sq.reorganize;          @rootsq_sq.close
                @parentsq_sq.reorganize;        @parentsq_sq.close
                @childsqs_sq.reorganize;        @childsqs_sq.close
+               @abstract_sq.reorganize;        @abstract_sq.close
                @flags_sq.reorganize;           @flags_sq.close
                super
        end
@@ -1138,6 +1210,10 @@ class MaveFolders < MaveBaseModel
                File.open(folder.config_filename, 'w', 0600) {|fw| fw.write(new_configs.read) }
                close_folder(folder.name); open_folder(folder.name)
        end
+
+       def close
+               each {|folder| folder.close }
+       end
 end
 
 #===============================================================================
@@ -1178,6 +1254,7 @@ class MaveMail < MaveBaseModel
                @boundary               = params[:BOUNDARY]
                @folder                 = params[:FOLDER]
                @sq                             = params[:SQ]
+               @size                   = 0; size
 
                parse_header
                parse_body if(@file.is_a?(File))
@@ -1445,7 +1522,7 @@ class MaveMail < MaveBaseModel
        #       メールのサイズを返す
        #
        def size
-               @file.lstat.size
+               @size = @file.lstat.size rescue @size
        end
 
        #--------------------------------------------- MaveMail ----
@@ -1489,6 +1566,7 @@ class MaveMail < MaveBaseModel
        #       メール情報を返す
        #
        def identify
+               yield([_('  Message-Sq: %s'), sq])
                yield([_('  Message-ID: %s'), message_id])
                yield([_('    FilePath: %s'), path])
                yield([_('    FileSize: %d'), size])
@@ -1567,7 +1645,7 @@ class MavePseudoMail < MaveMail
                                        elsif(header == 'bcc')
                                                yield("Bcc: #{ @@address_book.encode(bcc,  'SEND:')}\n")
                                        elsif(header == 'subject')
-                                               yield("Subject: #{subject.encode_mh}\n")
+                                               subject.encode_mh_multi('Subject') {|mhline| yield(mhline + "\n") }
                                        elsif(header == 'date')
                                                yield("Date: #{Time.now.rfc2822}\n")    # Date は現時刻に変更
                                        elsif(header == 'x-mave-extract-targets')
@@ -1585,7 +1663,7 @@ class MavePseudoMail < MaveMail
                                        raise("Mail format error '#{line.inspect}'")
                                end
                        else
-                               yield((line + "\n").tojis)                                              #### ボディを仲介(現状は JIS 固定)
+                               yield(NKF.nkf('-jWm0', line) + "\n")                    #### ボディを仲介(現状は JIS 固定)
                        end
                }
        end
@@ -1959,8 +2037,8 @@ class MaveAddressBook < MaveBaseModel
 
        def initialize(params)
                super
-               gdbm_flags              = params[:CONFIGS][:GDBM_FLAGS]
-               @address_db             =     GDBM.new(@configs[:ROOT_DIRECTORY] + '/mave.address',     0600, gdbm_flags)
+               xdbm_flags              = params[:CONFIGS][:XDBM_FLAGS]
+               @address_db             =     XDBM.new(@configs[:ROOT_DIRECTORY] + '/mave.address',     0600, xdbm_flags)
        end
 
        #-------------------------------------- MaveAddressBook ----
@@ -2008,6 +2086,10 @@ class MaveAddressBook < MaveBaseModel
                }
                mailboxes.join(', ')
        end
+
+       def close
+               @address_db.reorganize;         @address_db.close
+       end
 end
 
 #===============================================================================
@@ -2070,6 +2152,14 @@ class MaveTextBox < MaveBaseModel
                @text = text
                @dirty += 1
        end
+
+       #------------------------------------------ MaveTextBox ----
+       #
+       #       文字列を内部エンコーディングで返す
+       #
+       def utf8_text
+               @text.decode_cs('UTF-8', @configs[:TERMINAL_CHARSET])
+       end
 end
 
 #===============================================================================