1 ;;; eword-encode.el --- RFC 2047 based encoded-word encoder for GNU Emacs
3 ;; Copyright (C) 1995,1996,1997 Free Software Foundation, Inc.
5 ;; Author: MORIOKA Tomohiko <morioka@jaist.ac.jp>
6 ;; Version: $Revision: 0.22 $
7 ;; Keywords: encoded-word, MIME, multilingual, header, mail, news
9 ;; This file is part of SEMI (SEMI is Emacs MIME Interfaces).
11 ;; This program is free software; you can redistribute it and/or
12 ;; modify it under the terms of the GNU General Public License as
13 ;; published by the Free Software Foundation; either version 2, or (at
14 ;; your option) any later version.
16 ;; This program is distributed in the hope that it will be useful, but
17 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 ;; General Public License for more details.
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs; see the file COPYING. If not, write to the
23 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 ;; Boston, MA 02111-1307, USA.
32 (require 'eword-decode)
38 (defconst eword-encode-RCS-ID
39 "$Id: eword-encode.el,v 0.22 1997-06-21 15:13:52 morioka Exp $")
40 (defconst eword-encode-version (get-version-string eword-encode-RCS-ID))
46 (defvar eword-field-encoding-method-alist
47 '(("X-Nsubject" . iso-2022-jp-2)
51 "*Alist to specify field encoding method.
52 Its key is field-name, value is encoding method.
54 If method is `mime', this field will be encoded into MIME format.
56 If method is a MIME-charset, this field will be encoded as the charset
57 when it must be convert into network-code.
59 If method is `default-mime-charset', this field will be encoded as
60 variable `default-mime-charset' when it must be convert into
63 If method is nil, this field will not be encoded.")
65 (defvar eword-generate-X-Nsubject nil
66 "*If it is not nil, X-Nsubject field is generated
67 when Subject field is encoded by `eword-encode-header'.")
69 (defvar eword-charset-encoding-alist
87 (iso-2022-int-1 . "B")
91 ;;; @ encoded-text encoder
94 (defun eword-encode-text (charset encoding string &optional mode)
95 "Encode STRING as an encoded-word, and return the result.
96 CHARSET is a symbol to indicate MIME charset of the encoded-word.
97 ENCODING allows \"B\" or \"Q\".
98 MODE is allows `text', `comment', `phrase' or nil. Default value is
101 (cond ((string= encoding "B")
102 (base64-encode-string string))
103 ((string= encoding "Q")
104 (q-encoding-encode-string string mode))
108 (concat "=?" (upcase (symbol-name charset)) "?"
109 encoding "?" text "?=")
116 (defsubst eword-encode-char-type (character)
117 (if (or (eq character ? )(eq character ?\t))
119 (char-charset character)
122 (defun eword-encode-divide-into-charset-words (string)
123 (let ((len (length string))
126 (let* ((chr (sref string 0))
127 (charset (eword-encode-char-type chr))
130 (while (and (< i len)
131 (setq chr (sref string i))
132 (eq charset (eword-encode-char-type chr))
134 (setq i (+ i (char-bytes chr)))
136 (setq dest (cons (cons charset (substring string 0 i)) dest)
137 string (substring string i)
147 (defun tm-eword::lc-words-to-words (charset-words)
150 (let* ((charset-word (car charset-words))
151 (charset (car charset-word))
154 (let ((charsets (list charset))
155 (str (cdr charset-word))
158 (while (setq charset-words (cdr charset-words))
159 (setq charset-word (car charset-words)
160 charset (car charset-word))
164 (or (memq charset charsets)
165 (setq charsets (cons charset charsets))
167 (setq str (concat str (cdr charset-word)))
169 (setq dest (cons (cons charsets str) dest))
171 (setq dest (cons charset-word dest)
172 charset-words (cdr charset-words)
181 (defmacro tm-eword::make-rword (text charset encoding type)
182 (` (list (, text)(, charset)(, encoding)(, type))))
183 (defmacro tm-eword::rword-text (rword)
185 (defmacro tm-eword::rword-charset (rword)
186 (` (car (cdr (, rword)))))
187 (defmacro tm-eword::rword-encoding (rword)
188 (` (car (cdr (cdr (, rword))))))
189 (defmacro tm-eword::rword-type (rword)
190 (` (car (cdr (cdr (cdr (, rword)))))))
192 (defun tm-eword::find-charset-rule (charsets)
194 (let* ((charset (charsets-to-mime-charset charsets))
195 (encoding (cdr (assq charset eword-charset-encoding-alist)))
197 (list charset encoding)
200 (defun tm-eword::words-to-ruled-words (wl &optional mode)
203 (let ((ret (tm-eword::find-charset-rule (car word))))
204 (tm-eword::make-rword (cdr word) (car ret)(nth 1 ret) mode)
208 (defun tm-eword::space-process (seq)
209 (let (prev a ac b c cc)
214 (setq cc (tm-eword::rword-charset c))
215 (if (null (tm-eword::rword-charset b))
218 (setq ac (tm-eword::rword-charset a))
219 (if (and (tm-eword::rword-encoding a)
220 (tm-eword::rword-encoding c))
223 (cons (concat (car a)(car b)(car c))
231 (cons (concat (car a)(car b))
236 (setq prev (cons b prev))
238 (setq prev (cons b prev))
243 (defun tm-eword::split-string (str &optional mode)
244 (tm-eword::space-process
245 (tm-eword::words-to-ruled-words
246 (tm-eword::lc-words-to-words
247 (eword-encode-divide-into-charset-words str))
254 (defun tm-eword::encoded-word-length (rword)
255 (let ((string (tm-eword::rword-text rword))
256 (charset (tm-eword::rword-charset rword))
257 (encoding (tm-eword::rword-encoding rword))
260 (cond ((string-equal encoding "B")
261 (setq string (encode-mime-charset-string string charset))
262 (base64-encoded-length string)
264 ((string-equal encoding "Q")
265 (setq string (encode-mime-charset-string string charset))
266 (q-encoding-encoded-length string
267 (tm-eword::rword-type rword))
270 (cons (+ 7 (length (symbol-name charset)) ret) string)
277 (defun tm-eword::encode-string-1 (column rwl)
278 (let* ((rword (car rwl))
279 (ret (tm-eword::encoded-word-length rword))
282 (cond ((and (setq string (car rword))
283 (<= (setq len (+ (length string) column)) 76)
291 (cond ((and (setq len (car ret))
292 (<= (+ column len) 76)
296 (tm-eword::rword-charset rword)
297 (tm-eword::rword-encoding rword)
299 (tm-eword::rword-type rword)
301 (setq len (+ (length string) column))
305 (setq string (car rword))
308 (while (and (< p len)
310 (setq np (+ p (char-bytes (sref string p))))
311 (setq nstr (substring string 0 np))
312 (setq ret (tm-eword::encoded-word-length
313 (cons nstr (cdr rword))
315 (setq nstr (cdr ret))
316 (setq len (+ (car ret) column))
321 (if (string-equal str "")
324 (setq rwl (cons (cons (substring string p) (cdr rword))
328 (tm-eword::rword-charset rword)
329 (tm-eword::rword-encoding rword)
331 (tm-eword::rword-type rword)))
332 (setq len (+ (length string) column))
336 (list string len rwl)
339 (defun tm-eword::encode-rwl (column rwl)
340 (let (ret dest ps special str ew-f pew-f)
342 (setq ew-f (nth 2 (car rwl)))
344 (setq rwl (cons '(" ") rwl)
348 (setq ret (tm-eword::encode-string-1 column rwl))
350 (if (eq (elt str 0) ?\n)
353 (setq dest (concat dest "\n ("))
354 (setq ret (tm-eword::encode-string-1 2 rwl))
357 (cond ((eq special ? )
358 (if (string= str "(")
360 (setq dest (concat dest " "))
366 (setq dest (concat dest " ("))
369 (setq dest (concat dest "("))
372 (cond ((string= str " ")
380 (setq dest (concat dest str))
382 (setq column (nth 1 ret)
388 (defun tm-eword::encode-string (column str &optional mode)
389 (tm-eword::encode-rwl column (tm-eword::split-string str mode))
396 (defun tm-eword::phrase-to-rwl (phrase)
397 (let (token type dest str)
399 (setq token (car phrase))
400 (setq type (car token))
401 (cond ((eq type 'quoted-string)
402 (setq str (concat "\"" (cdr token) "\""))
406 (let ((ret (tm-eword::find-charset-rule
407 (find-non-ascii-charset-string str))))
408 (tm-eword::make-rword
409 str (car ret)(nth 1 ret) 'phrase)
417 (tm-eword::words-to-ruled-words
418 (tm-eword::lc-words-to-words
419 (eword-encode-divide-into-charset-words
428 (tm-eword::words-to-ruled-words
429 (tm-eword::lc-words-to-words
430 (eword-encode-divide-into-charset-words
434 (setq phrase (cdr phrase))
436 (tm-eword::space-process dest)
439 (defun tm-eword::phrase-route-addr-to-rwl (phrase-route-addr)
440 (if (eq (car phrase-route-addr) 'phrase-route-addr)
441 (let ((phrase (nth 1 phrase-route-addr))
442 (route (nth 2 phrase-route-addr))
444 (if (eq (car (car phrase)) 'spaces)
445 (setq phrase (cdr phrase))
447 (setq dest (tm-eword::phrase-to-rwl phrase))
449 (setq dest (append dest '((" " nil nil))))
453 (list (list (concat "<" (std11-addr-to-string route) ">") nil nil))
456 (defun tm-eword::addr-spec-to-rwl (addr-spec)
457 (if (eq (car addr-spec) 'addr-spec)
458 (list (list (std11-addr-to-string (cdr addr-spec)) nil nil))
461 (defun tm-eword::mailbox-to-rwl (mbox)
462 (let ((addr (nth 1 mbox))
463 (comment (nth 2 mbox))
465 (setq dest (or (tm-eword::phrase-route-addr-to-rwl addr)
466 (tm-eword::addr-spec-to-rwl addr)
473 (tm-eword::split-string comment 'comment)
478 (defun tm-eword::addresses-to-rwl (addresses)
479 (let ((dest (tm-eword::mailbox-to-rwl (car addresses))))
481 (while (setq addresses (cdr addresses))
482 (setq dest (append dest
485 (tm-eword::mailbox-to-rwl (car addresses))
490 (defun tm-eword::encode-address-list (column str)
491 (tm-eword::encode-rwl
493 (tm-eword::addresses-to-rwl (std11-parse-addresses-string str))
497 ;;; @ application interfaces
500 (defun eword-encode-field (string)
501 "Encode header field STRING, and return the result.
502 A lexical token includes non-ASCII character is encoded as MIME
503 encoded-word. ASCII token is not encoded."
504 (setq string (std11-unfold-string string))
505 (let ((ret (string-match std11-field-head-regexp string)))
507 (let ((field-name (substring string 0 (1- (match-end 0))))
508 (field-body (eliminate-top-spaces
509 (substring string (match-end 0))))
512 (cond ((string-equal field-body "") "")
513 ((memq (intern (downcase field-name))
516 resent-reply-to resent-from
517 resent-sender to resent-to
521 (car (tm-eword::encode-address-list
522 (+ (length field-name) 2) field-body))
525 (car (tm-eword::encode-string
526 (1+ (length field-name))
530 (concat field-name ": " ret)
532 (car (tm-eword::encode-string 0 string))
535 (defun eword-in-subject-p ()
536 (let ((str (std11-field-body "Subject")))
537 (if (and str (string-match eword-encoded-word-regexp str))
540 (defun eword-encode-header (&optional code-conversion)
541 "Encode header fields to network representation, such as MIME encoded-word.
543 It refer variable `eword-field-encoding-method-alist'."
547 (std11-narrow-to-header mail-header-separator)
548 (goto-char (point-min))
549 (let ((default-cs (mime-charset-to-coding-system default-mime-charset))
551 (while (re-search-forward std11-field-head-regexp nil t)
552 (setq beg (match-beginning 0))
553 (setq field-name (buffer-substring beg (1- (match-end 0))))
554 (setq end (std11-field-end))
555 (and (find-non-ascii-charset-region beg end)
556 (let ((ret (or (let ((fname (downcase field-name)))
561 (string= fname (downcase str))
563 eword-field-encoding-method-alist))
564 (assq t eword-field-encoding-method-alist)
567 (let ((method (cdr ret)))
568 (cond ((eq method 'mime)
570 (buffer-substring-no-properties beg end)
572 (delete-region beg end)
573 (insert (eword-encode-field field))
577 (or (mime-charset-to-coding-system
580 (encode-coding-region beg end cs)
585 (and eword-generate-X-Nsubject
586 (or (std11-field-body "X-Nsubject")
587 (let ((str (eword-in-subject-p)))
592 (std11-unfold-string str)))
595 (encode-mime-charset-string
601 (string= "x-nsubject"
604 eword-field-encoding-method-alist))
607 (insert (concat "\nX-Nsubject: " str))
611 (defun eword-encode-string (str &optional column mode)
612 (car (tm-eword::encode-rwl (or column 0) (tm-eword::split-string str mode)))
619 (provide 'eword-encode)
621 ;;; eword-encode.el ends here