[Mew-dist 08981] Correction for RFC2047 (Re: 悩ましいこと)

Shun-ichi GOTO ( 後藤 俊一 ) gotoh at example.com
1999年 5月 26日 (水) 04:17:22 JST


後藤@太陽計測です

もしかしたら かずさんが現在行っている修正とぶつかるかもしれないけど、
ヘッダでの rfc2047 encoding/decodingに関する不具合報告とパッチです。


>>>>> at Thu, 20 May 1999 16:30:12 +0900, kazz <kazu at example.com> said,

kazz> (1) RFC 2047 のヘッダ符号

kazz> - 復号: Subject: で text なので、mew-decode-quoted は関係ないのですが、
kazz> 今は関係してしまっている。

これに関する件と、プラスアルファのパッチです。(for mew-1.94b29)

なのですが、

kazz> - 符号: 折り返しが甘い。符号化と折り返しを同時にやるのではなく、とり
kazz> あえず符号化し、あとから折り返すところを見付けるアルゴリズムの方がいい
kazz> ような気がしています。こうすると、復号のアルゴリズムと対称になりますし。

このあたりにからんで、かずさんのほうですでに手を入れているとややこしいの
で、一応パッチ自体は参考までに、ということで、本パッチが対処している問題
点と具体例を挙げておきます。


1. non-structuredな text部で double quote <">文字が特別扱いしてdecode
   してしまっている問題。
   (mew-header-decode-regionを修正)

   これは既に報告されている問題ですのですね。

  (例)n
     Subject: " =?ISO-2022-JP?B?GyRCJCIbKEI=?= "
  => Subject: " =?ISO-2022-JP?B?GyRCJCIbKEI=?= "
  (パッチ後)
  => Subject: " あ "


2. decode時、闇雲に "\\?=[ \t]=\\?" を探し出してこれを "?==?"に
   連結してしまう問題。
   (mew-header-decode-regionを修正)

  (例1)
     Subject: =?ISO-8859-1?Q?a?= ?= =?
  => Subject: a ?==?
  (パッチ後)
  => Subject: a ?= =?

  (例2)
    Subject: =?ISO-2022-JP?B?GyRCJCIbKEI=?= =?= =?ISO-2022-JP?B?GyRCJCIbKEI=?=
  =>Subject: あ=?= あ
  (パッチ後)
  =>Subject: あ =?= あ


3. structuredな phrase部で quoted-pairな double quote <">の判定が
   あまく、正しい処理を行っていなかった問題。
   (mew-header-decode-regionを修正)

  (例)
     From: "\"\\" =?ISO-2022-JP?B?GyRCJCIbKEI=?= "\"\\" <akr at example.com>
  => From: "\"\\" =?ISO-2022-JP?B?GyRCJCIbKEI=?= "\"\\" <akr at example.com>
  (パッチ後)
  => From: "\"\\" あ "\"\\" <akr at example.com> 


4. encode時、mew-header-encode-split後に、要encodeな文字列を
   encodeするさい、その文字列が単一のcharsetで表現できない場合に
   elispレベルのエラーとなりencodeできなかった問題。
   (mew-header-encode-textを修正)

   具体的には、iso-8859-8の文字とiso-2022-jpの文字が連続している場合。
   mew-charset-guess-region が"unknown" を返し、その際の処理を考慮して
   いないため、エラーとなっていた。
   パッチでは単一エンコードに収まるような小片に分割するための
   mew-header-encode-split-by-charsetを追加した。

  (例)
   iso-8859-8の code 0xE0 の <aleph>  
   iso-2022-jpの「零」

     Subject: <aleph>零" 
   =>error
  (パッチ後)
   =>Subject: =?iso-8859-8?B?4A==?=
      =?iso-2022-jp?B?GyRCTm0bKEI=?=
  (正解)
   =>Subject: =?iso-8859-8?B?4A==?= =?iso-2022-jp?B?GyRCTm0bKEI=?=

  修正個所を多くしないパッチのため、「正解」になるようなコードは
  とりあえず保留


これらの例は Tanaka Akira <akr at example.com>さんの doodleに含まれる
テストパターンを試して、そのうちの致命的で、なおかつMewの現コードの
構造に沿って修正できるものを対処したつもりです。

このパターンに関しては、つい先ごろ 

Subject: [Mew-dist 08751] Re: B-encoded header routine
From: Tanaka Akira <akr at example.com>
Date: Sat, 8 May 1999 02:59:35 +0900
Message-Id: <rsqzp3gkbg9.fsf at example.com>

でも話題になったものです。

ちなみにこの意地悪テストを通すと他にもいっぱいfailになります。(^^;

かずさんもテストパターンをお持ちと思いますが、このパターン、試して
みませんか? doodle付属のTESTPATをmewにかけるための若干のコードを
作ってあります。 ご要望とあらばテスト結果もすぐ送れますがいかがでしょうか。

--- Regards,
 Shun-ichi Goto  <gotoh at example.com>
   R&D Group, TAIYO Corp., Tokyo, JAPAN

-------------- next part --------------
--- mew-bq.el.orig	Thu May 13 04:54:26 1999
+++ mew-bq.el	Tue May 25 14:13:16 1999
@@ -30,6 +30,27 @@
 (defconst mew-header-decode-regex 
   "=\\?\\([^?]+\\)\\?\\(.\\)\\?\\([^?]+\\)\\?=")
 
+(defvar mew-cs-coding-database nil)
+
+(let ((db mew-cs-database)
+      elem charset csl cs)
+  (while db
+    (setq charset (car (car db))
+	  csl (car (cdr (car db)))
+	  db (cdr db))
+    (while csl
+      (setq cs (car csl)
+	    csl (cdr csl))
+      (if (eq cs 'ascii)
+	  ()				; do nothing
+	(setq elem (assq cs mew-cs-coding-database))
+	(if elem
+	    ;; append enabled charset
+	    (nconc elem (list charset))
+	  ;; newly create
+	  (setq mew-cs-coding-database 
+		(cons (list cs charset) mew-cs-coding-database)))))))
+	  
 ;;;
 ;;;
 ;;;
@@ -263,7 +284,7 @@
 ;; of results. If it is longer than 75, `str' is split and `substr1' and
 ;; `substr2' are encoded.... Repeat this recursively but not so deeply.
 
-(defun mew-header-encode-string (str)
+(defun mew-header-encode-string-subr (str)
   (let ((encoded-word (mew-header-encode str)))
     (if (> (length encoded-word) mew-encode-word-max-length)
         (let ((med (/ (length str) 2))
@@ -271,10 +292,57 @@
           (while (< i med)
             (setq i (+ i (mew-charlen (mew-aref str i)))))
           (append
-           (mew-header-encode-string (substring str 0 i))
-           (mew-header-encode-string (substring str i nil))))
+           (mew-header-encode-string-subr (substring str 0 i))
+           (mew-header-encode-string-subr (substring str i nil))))
       (list encoded-word))))
 
+(defun mew-header-encode-string (str)
+  ;; split string with charset
+  (let ((str-cs-list (mew-header-encode-split-string-by-charset str))
+	result)
+    (while str-cs-list
+      (setq result (nconc result 
+			  (mew-header-encode-string-subr (car str-cs-list)))
+	    str-cs-list (cdr str-cs-list)))
+    result))
+
+
+(defun mew-header-encode-split-string-by-charset (str)
+  "Split STR to be encoded as single charset."
+  (let ((len (length str))
+	(last 0)
+	(pos 0)
+	result cs ch available using charsets and tmpl)
+    (while (< pos len)
+      (setq ch (mew-aref str pos)
+	    cs (char-charset ch))
+      (if (null available)
+	  (setq available (cdr (assq cs mew-cs-coding-database))))
+      (if (or (eq cs 'ascii) (memq cs using))
+	  (setq pos (+ pos (mew-charlen ch))) ; accept
+	;; update for new charset
+	(setq charsets (cdr (assq cs mew-cs-coding-database))
+	      and nil
+	      tmpl available)
+	;; find common charset in AVAILABLE and CHARSETS
+	(while tmpl
+	  (if (member (car tmpl) charsets)
+	      (setq and (cons (car tmpl) and)))
+	  (setq tmpl (cdr tmpl)))
+	;; check it
+	(if and
+	    ;; accept this char
+	    (setq available and
+		  using (cons cs using)
+		  pos (+ pos (mew-charlen ch)))
+	  ;; Do not accept this char, split at this point
+	  (setq result (cons (substring str last pos) result)
+		last pos
+		available nil
+		using nil))))
+    (setq result (cons (substring str last pos) result))
+    (nreverse result)))
+
 (defun mew-header-encode-split-string (str)
   "Split STR to need-to-encode string and non-encode-string."
   (let ((start 0) beg end ret)
@@ -595,31 +663,49 @@
     (if (and (not unfold) 
 	     (not (re-search-forward mew-header-decode-regex nil t)))
 	(mew-header-unfold-region type)
-      (let ((regex (concat "\"\\|" mew-header-decode-regex))
-	    beg end cs-str)
+      (let ((endq-regex "\\(\\\\*\\)\"")
+	    (next-regex (concat "[ \t]*" mew-header-decode-regex))
+	    regex beg end cs-str esc )
+	(if (or mew-decode-quoted (memq type '(text comma-text)))
+	    ;; unstructured or allow decoding in quoted-string
+	    (setq regex mew-header-decode-regex)
+	  ;; structured or deny decoding in quoted-string
+	  (setq regex (concat endq-regex "\\|" mew-header-decode-regex)))
 	(mew-header-unfold-region type)
 	;; In Page 10 of RFC 2047 says, "When displaying a particular 
 	;; header field that contains multiple 'encoded-word's, any 
 	;; 'linear-white-space' that separates a pair of adjacent 
 	;; 'encoded-word's is ignored".
 	(goto-char (point-min))
-	(while (re-search-forward "\\?=[ \t]+=\\?" nil t)
-	  (replace-match "?==?" nil t))
-	(goto-char (point-min))
 	(while (re-search-forward regex nil t)
 	  ;; encoded-word in quoted-string should not be decoded
 	  ;; according to RFC 2047. However, if users wish
 	  ;; (ie mew-decode-quoted is *non-nil*), decode it.
-	  (if (eq (char-after (match-beginning 0)) ?\")
-	      (if (or mew-decode-quoted (re-search-forward "[^\\]\"" nil t))
-		  (goto-char (match-end 0)))
-	    (setq beg (match-beginning 0)
-		  end (match-end 0)
-		  cs-str (mew-header-decode (mew-match 1)
-					    (mew-match 2)
-					    (mew-match 3)))
-	    (delete-region beg end)
-	    (insert cs-str)))))
+	  (goto-char (match-beginning 0))
+	  ;; count escapes leads to double quote char
+	  (if (not (eq (following-char) ?\\))
+	      (setq esc 0)
+	    (setq esc (- (match-end 1) (match-beginning 1))))
+	  (forward-char esc)
+	  ;; Now pointer is skipped leading escapes
+	  (if (not (eq (following-char) ?\"))
+	    (while (looking-at next-regex)
+	      (setq beg (match-beginning 0)
+		    end (match-end 0)
+		    cs-str (mew-header-decode (mew-match 1)
+					      (mew-match 2)
+					      (mew-match 3)))
+	      (delete-region beg end)
+	      (insert cs-str))
+	    ;; beginning of quoted string
+	    (goto-char (match-end 0))
+	    (if (= 0 (% esc 2))
+	      ;; skip to end of quoted-string
+		(while (and (re-search-forward endq-regex nil t)
+			    (setq esc (- (match-end 1) (match-beginning 1)))
+			    (= 1 (% esc 2)))
+		  (goto-char (match-end 0))))
+	    ))))
     (goto-char (point-max))))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


Mew-dist メーリングリストの案内