* rfc2047.el (rfc2047-encode-region): Insert a space before encoding.
[gnus] / lisp / rfc2047.el
index 4c2a8d1..ff07547 100644 (file)
@@ -1,5 +1,5 @@
 ;;; rfc2047.el --- Functions for encoding and decoding rfc2047 messages
-;; Copyright (C) 1998 Free Software Foundation, Inc.
+;; Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
 ;;     MORIOKA Tomohiko <morioka@jaist.ac.jp>
@@ -32,9 +32,7 @@
 (require 'qp)
 (require 'mm-util)
 (require 'ietf-drums)
-
-(defvar rfc2047-default-charset 'iso-8859-1
-  "Default MIME charset -- does not need encoding.")
+(require 'mail-prsvr)
 
 (defvar rfc2047-header-encoding-alist
   '(("Newsgroups" . nil)
@@ -48,7 +46,7 @@ The values can be:
 
 1) nil, in which case no encoding is done;
 2) `mime', in which case the header will be encoded according to RFC2047;
-3) a charset, in which case it will be encoded as that charse;
+3) a charset, in which case it will be encoded as that charset;
 4) `default', in which case the field will be encoded as the rest
    of the article.")
 
@@ -82,7 +80,7 @@ Valid encodings are nil, `Q' and `B'.")
 
 (defvar rfc2047-q-encoding-alist
   '(("\\(From\\|Cc\\|To\\|Bcc\||Reply-To\\):" . "-A-Za-z0-9!*+/=_")
-    ("." . "^\000-\007\013\015-\037\200-\377=_?"))
+    ("." . "^\000-\007\011\013\015-\037\200-\377=_?"))
   "Alist of header regexps and valid Q characters.")
 
 ;;;
@@ -107,36 +105,45 @@ Valid encodings are nil, `Q' and `B'.")
   "Encode the message header according to `rfc2047-header-encoding-alist'.
 Should be called narrowed to the head of the message."
   (interactive "*")
-  (when (featurep 'mule)
-    (save-excursion
-      (goto-char (point-min))
-      (let ((alist rfc2047-header-encoding-alist)
-           elem method)
-       (while (not (eobp))
-         (save-restriction
-           (rfc2047-narrow-to-field)
-           (when (rfc2047-encodable-p)
-             ;; We found something that may perhaps be encoded.
-             (while (setq elem (pop alist))
-               (when (or (and (stringp (car elem))
-                              (looking-at (car elem)))
-                         (eq (car elem) t))
-                 (setq alist nil
-                       method (cdr elem))))
-             (when method
-               (cond
-                ((eq method 'mime)
-                 (rfc2047-encode-region (point-min) (point-max)))
-                ;; Hm.
-                (t))))
-           (goto-char (point-max))))))))
-
-(defun rfc2047-encodable-p ()
-  "Say whether the current (narrowed) buffer contains characters that need encoding."
-  (let ((charsets (mapcar
-                  'mm-mule-charset-to-mime-charset
-                  (find-charset-region (point-min) (point-max))))
-       (cs (list 'us-ascii rfc2047-default-charset))
+  (save-excursion
+    (goto-char (point-min))
+    (let ((alist rfc2047-header-encoding-alist)
+         elem method)
+      (while (not (eobp))
+       (save-restriction
+         (rfc2047-narrow-to-field)
+         (if (not (rfc2047-encodable-p))
+             (if (and (eq (mm-body-7-or-8) '8bit)
+                      (mm-multibyte-p)
+                      (mm-coding-system-p
+                       (car message-posting-charset)))
+                      ;; 8 bit must be decoded.
+                      ;; Is message-posting-charset a coding system?
+                      (mm-encode-coding-region 
+                       (point-min) (point-max) 
+                       (car message-posting-charset)))
+           ;; We found something that may perhaps be encoded.
+           (while (setq elem (pop alist))
+             (when (or (and (stringp (car elem))
+                            (looking-at (car elem)))
+                       (eq (car elem) t))
+               (setq alist nil
+                     method (cdr elem))))
+           (cond
+            ((eq method 'mime)
+             (rfc2047-encode-region (point-min) (point-max))
+             (rfc2047-fold-region (point-min) (point-max)))
+            ;; Hm.
+            (t)))
+         (goto-char (point-max)))))))
+
+(defun rfc2047-encodable-p (&optional header)
+  "Say whether the current (narrowed) buffer contains characters that need encoding in headers."
+  (let ((charsets
+        (mapcar
+         'mm-mime-charset
+         (mm-find-charset-region (point-min) (point-max))))
+       (cs (list 'us-ascii (car message-posting-charset)))
        found)
     (while charsets
       (unless (memq (pop charsets) cs)
@@ -144,21 +151,61 @@ Should be called narrowed to the head of the message."
     found))
 
 (defun rfc2047-dissect-region (b e)
-  "Dissect the region between B and E."
-  (let (words)
+  "Dissect the region between B and E into words."
+  (let ((all-specials (concat ietf-drums-tspecials " \t\n\r"))
+       (special-list (mapcar 'identity ietf-drums-tspecials))
+       (blank-list '(?  ?\t ?\n ?\r))
+       words current cs state mail-parse-mule-charset)
     (save-restriction
       (narrow-to-region b e)
       (goto-char (point-min))
-      (while (re-search-forward
-             (concat "[^" ietf-drums-tspecials " \t\n]+") nil t)
-       (push
-        (list (match-beginning 0) (match-end 0)
-              (car
-               (delq 'ascii
-                     (find-charset-region (match-beginning 0)
-                                          (match-end 0)))))
-        words))
-      words)))
+      (skip-chars-forward all-specials)
+      (setq b (point))
+      (while (not (eobp))
+       (cond
+        ((not state)
+         (setq state 'word)
+         (if (not (eq (setq cs (mm-charset-after)) 'ascii))
+             (setq current cs))
+         (setq b (point)))
+        ((eq state 'blank)
+         (cond 
+          ((memq (char-after) special-list)
+           (setq state nil))
+          ((memq (char-after) blank-list))
+          (t
+           (setq state 'word)
+           (unless b
+               (setq b (point)))
+           (if (not (eq (setq cs (mm-charset-after)) 'ascii))
+               (setq current cs)))))
+        ((eq state 'word)
+         (cond 
+          ((memq (char-after) special-list)
+           (setq state nil)
+           (push (list b (point) current) words)
+           (setq current nil))
+          ((memq (char-after) blank-list)
+           (setq state 'blank)
+           (if (not current)
+               (setq b nil)
+             (push (list b (point) current) words)
+             (setq b (point))
+             (setq current nil)))
+          ((or (eq (setq cs (mm-charset-after)) 'ascii)
+               (if current
+                   (eq current cs)
+                 (setq current cs))))
+          (t
+           (push (list b (point) current) words)
+           (setq current cs)
+           (setq b (point))))))
+       (if state
+           (forward-char)
+         (skip-chars-forward all-specials)))
+      (if (eq state 'word)
+         (push (list b (point) current) words)))
+    words))
 
 (defun rfc2047-encode-region (b e)
   "Encode all encodable words in REGION."
@@ -168,7 +215,14 @@ Should be called narrowed to the head of the message."
       (if (equal (nth 2 word) current)
          (setq beg (nth 0 word))
        (when current
-         (rfc2047-encode beg end current))
+         (if (and (eq beg (nth 1 word)) (nth 2 word))
+             (progn
+               ;; There might be a bug in Emacs Mule.
+               ;; A space must be inserted before encoding.
+               (goto-char beg)
+               (insert " ")
+               (rfc2047-encode (1+ beg) (1+ end) current))
+           (rfc2047-encode beg end current)))
        (setq current (nth 2 word)
              beg (nth 0 word)
              end (nth 1 word))))
@@ -184,10 +238,9 @@ Should be called narrowed to the head of the message."
 
 (defun rfc2047-encode (b e charset)
   "Encode the word in the region with CHARSET."
-  (let* ((mime-charset
-         (mm-mime-charset charset b e))
+  (let* ((mime-charset (mm-mime-charset charset))
         (encoding (or (cdr (assq mime-charset
-                             rfc2047-charset-encoding-alist))
+                                 rfc2047-charset-encoding-alist))
                       'B))
         (start (concat
                 "=?" (downcase (symbol-name mime-charset)) "?"
@@ -195,7 +248,16 @@ Should be called narrowed to the head of the message."
         (first t))
     (save-restriction
       (narrow-to-region b e)
-      (mm-encode-coding-region b e mime-charset)
+      (when (eq encoding 'B)
+       ;; break into lines before encoding
+       (goto-char (point-min))
+       (while (not (eobp))
+         (goto-char (min (point-max) (+ 15 (point))))
+         (unless (eobp)
+           (insert "\n"))))
+      (if (and (mm-multibyte-p)
+              (mm-coding-system-p mime-charset))
+         (mm-encode-coding-region (point-min) (point-max) mime-charset))
       (funcall (cdr (assq encoding rfc2047-encoding-function-alist))
               (point-min) (point-max))
       (goto-char (point-min))
@@ -208,14 +270,37 @@ Should be called narrowed to the head of the message."
        (insert "?=")
        (forward-line 1)))))
 
+(defun rfc2047-fold-region (b e)
+  "Fold the long lines in the region."
+  (save-restriction
+    (narrow-to-region b e)
+    (goto-char (point-min))
+    (let ((break nil))
+      (while (not (eobp))
+       (cond
+        ((memq (char-after) '(?  ?\t))
+         (setq break (point)))
+        ((and (not break)
+              (looking-at "=\\?"))
+         (setq break (point)))
+        ((and break
+              (looking-at "\\?=")
+              (> (- (point) (save-excursion (beginning-of-line) (point))) 76))
+         (goto-char break)
+         (setq break nil)
+         (insert "\n ")))
+       (unless (eobp)
+         (forward-char 1))))))
+
 (defun rfc2047-b-encode-region (b e)
   "Encode the header contained in REGION with the B encoding."
-  (base64-encode-region b e t)
-  (goto-char (point-min))
-  (while (not (eobp))
-    (goto-char (min (point-max) (+ 64 (point))))
-    (unless (eobp)
-      (insert "\n"))))
+  (save-restriction
+    (narrow-to-region (goto-char b) e)
+    (while (not (eobp))
+      (base64-encode-region (point) (progn (end-of-line) (point)) t)
+      (if (and (bolp) (eolp))
+         (delete-backward-char 1))
+      (forward-line))))
 
 (defun rfc2047-q-encode-region (b e)
   "Encode the header contained in REGION with the Q encoding."
@@ -267,11 +352,16 @@ Should be called narrowed to the head of the message."
                   (prog1
                       (match-string 0)
                     (delete-region (match-beginning 0) (match-end 0)))))
-         (when (mm-multibyte-p)
-           (mm-decode-coding-region b e rfc2047-default-charset))
+         (when (and (mm-multibyte-p)
+                    mail-parse-charset
+                    (not (eq mail-parse-charset 'gnus-decoded)))
+           (mm-decode-coding-region b e mail-parse-charset))
          (setq b (point)))
-       (when (mm-multibyte-p)
-         (mm-decode-coding-region b (point-max) rfc2047-default-charset))))))
+       (when (and (mm-multibyte-p)
+                  mail-parse-charset
+                  (not (eq mail-parse-charset 'us-ascii))
+                  (not (eq mail-parse-charset 'gnus-decoded)))
+         (mm-decode-coding-region b (point-max) mail-parse-charset))))))
 
 (defun rfc2047-decode-string (string)
   "Decode the quoted-printable-encoded STRING and return the results."
@@ -283,7 +373,7 @@ Should be called narrowed to the head of the message."
       (inline
        (rfc2047-decode-region (point-min) (point-max)))
       (buffer-string))))
+
 (defun rfc2047-parse-and-decode (word)
   "Decode WORD and return it if it is an encoded word.
 Return WORD if not."
@@ -302,10 +392,22 @@ Return WORD if not."
   "Decode STRING that uses CHARSET with ENCODING.
 Valid ENCODINGs are \"B\" and \"Q\".
 If your Emacs implementation can't decode CHARSET, it returns nil."
+  (if (stringp charset)
+      (setq charset (intern (downcase charset))))
+  (if (or (not charset) 
+         (eq 'gnus-all mail-parse-ignored-charsets)
+         (memq 'gnus-all mail-parse-ignored-charsets)
+         (memq charset mail-parse-ignored-charsets))
+      (setq charset mail-parse-charset))
   (let ((cs (mm-charset-to-coding-system charset)))
+    (if (and (not cs) charset 
+            (listp mail-parse-ignored-charsets)
+            (memq 'gnus-unknown mail-parse-ignored-charsets))
+       (setq cs (mm-charset-to-coding-system mail-parse-charset)))
     (when cs
-      (when (eq cs 'ascii)
-       (setq cs rfc2047-default-charset))
+      (when (and (eq cs 'ascii)
+                mail-parse-charset)
+       (setq cs mail-parse-charset))
       (mm-decode-coding-string
        (cond
        ((equal "B" encoding)