mm-encode.el 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. ;;; mm-encode.el --- Functions for encoding MIME things
  2. ;; Copyright (C) 1998-2012 Free Software Foundation, Inc.
  3. ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
  4. ;; MORIOKA Tomohiko <morioka@jaist.ac.jp>
  5. ;; This file is part of GNU Emacs.
  6. ;; GNU Emacs is free software: you can redistribute it and/or modify
  7. ;; it under the terms of the GNU General Public License as published by
  8. ;; the Free Software Foundation, either version 3 of the License, or
  9. ;; (at your option) any later version.
  10. ;; GNU Emacs is distributed in the hope that it will be useful,
  11. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ;; GNU General Public License for more details.
  14. ;; You should have received a copy of the GNU General Public License
  15. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  16. ;;; Commentary:
  17. ;;; Code:
  18. (eval-when-compile (require 'cl))
  19. (require 'mail-parse)
  20. (autoload 'mailcap-extension-to-mime "mailcap")
  21. (autoload 'mm-body-7-or-8 "mm-bodies")
  22. (autoload 'mm-long-lines-p "mm-bodies")
  23. (defcustom mm-content-transfer-encoding-defaults
  24. '(("text/x-patch" 8bit)
  25. ("text/.*" qp-or-base64)
  26. ("message/rfc822" 8bit)
  27. ("application/emacs-lisp" qp-or-base64)
  28. ("application/x-emacs-lisp" qp-or-base64)
  29. ("application/x-patch" qp-or-base64)
  30. (".*" base64))
  31. "Alist of regexps that match MIME types and their encodings.
  32. If the encoding is `qp-or-base64', then either quoted-printable
  33. or base64 will be used, depending on what is more efficient.
  34. This list is only consulted when encoding MIME parts in the
  35. bodies -- not for the regular non-MIME-ish messages."
  36. :type '(repeat (list (regexp :tag "MIME type")
  37. (choice :tag "encoding"
  38. (const 7bit)
  39. (const 8bit)
  40. (const qp-or-base64)
  41. (const quoted-printable)
  42. (const base64))))
  43. :group 'mime)
  44. (defcustom mm-sign-option nil
  45. "Option how to create signed parts.
  46. nil, use the default keys without asking;
  47. `guided', let you select signing keys from the menu."
  48. :version "23.2" ;; No Gnus 0.12
  49. :type '(choice (item guided)
  50. (item :tag "default" nil))
  51. :group 'mime-security)
  52. (defcustom mm-encrypt-option nil
  53. "Option how to create encrypted parts.
  54. nil, use the default keys without asking;
  55. `guided', let you select recipients' keys from the menu."
  56. :version "23.2" ;; No Gnus 0.12
  57. :type '(choice (item guided)
  58. (item :tag "default" nil))
  59. :group 'mime-security)
  60. (defvar mm-use-ultra-safe-encoding nil
  61. "If non-nil, use encodings aimed at Procrustean bed survival.
  62. This means that textual parts are encoded as quoted-printable if they
  63. contain lines longer than 76 characters or starting with \"From \" in
  64. the body. Non-7bit encodings (8bit, binary) are generally disallowed.
  65. This is to reduce the probability that a broken MTA or MDA changes the
  66. message.
  67. This variable should never be set directly, but bound before a call to
  68. `mml-generate-mime' or similar functions.")
  69. (defun mm-insert-rfc822-headers (charset encoding)
  70. "Insert text/plain headers with CHARSET and ENCODING."
  71. (insert "MIME-Version: 1.0\n")
  72. (insert "Content-Type: text/plain; charset="
  73. (mail-quote-string (downcase (symbol-name charset))) "\n")
  74. (insert "Content-Transfer-Encoding: "
  75. (downcase (symbol-name encoding)) "\n"))
  76. (defun mm-insert-multipart-headers ()
  77. "Insert multipart/mixed headers."
  78. (let ((boundary "=-=-="))
  79. (insert "MIME-Version: 1.0\n")
  80. (insert "Content-Type: multipart/mixed; boundary=\"" boundary "\"\n")
  81. boundary))
  82. ;;;###autoload
  83. (defun mm-default-file-encoding (file)
  84. "Return a default encoding for FILE."
  85. (if (not (string-match "\\.[^.]+$" file))
  86. "application/octet-stream"
  87. (mailcap-extension-to-mime (match-string 0 file))))
  88. (defun mm-safer-encoding (encoding &optional type)
  89. "Return an encoding similar to ENCODING but safer than it."
  90. (cond
  91. ((eq encoding '7bit) '7bit) ;; 7bit is considered safe.
  92. ((memq encoding '(8bit quoted-printable))
  93. ;; According to RFC2046, 5.2.1, RFC822 Subtype, "quoted-printable" is not
  94. ;; a valid encoding for message/rfc822:
  95. ;; No encoding other than "7bit", "8bit", or "binary" is permitted for the
  96. ;; body of a "message/rfc822" entity.
  97. (if (string= type "message/rfc822") '8bit 'quoted-printable))
  98. ;; The remaining encodings are binary and base64 (and perhaps some
  99. ;; non-standard ones), which are both turned into base64.
  100. (t (if (string= type "message/rfc822") 'binary 'base64))))
  101. (defun mm-encode-content-transfer-encoding (encoding &optional type)
  102. "Encode the current buffer with ENCODING for MIME type TYPE.
  103. ENCODING can be: nil (do nothing); one of `quoted-printable', `base64';
  104. `7bit', `8bit' or `binary' (all do nothing); a function to do the encoding."
  105. (cond
  106. ((eq encoding 'quoted-printable)
  107. ;; This used to try to make a multibyte buffer unibyte. That's
  108. ;; completely wrong, since you'd get QP-encoded emacs-mule. If
  109. ;; this gets run on multibyte text it's an error that needs
  110. ;; fixing, and the encoding function will signal an error.
  111. ;; Likewise base64 below.
  112. (quoted-printable-encode-region (point-min) (point-max) t))
  113. ((eq encoding 'base64)
  114. (when (string-match "\\`text/" type)
  115. (goto-char (point-min))
  116. (while (search-forward "\n" nil t)
  117. (replace-match "\r\n" t t)))
  118. (base64-encode-region (point-min) (point-max)))
  119. ((memq encoding '(7bit 8bit binary))
  120. ;; Do nothing.
  121. )
  122. ((null encoding)
  123. ;; Do nothing.
  124. )
  125. ;; Fixme: Ignoring errors here looks bogus.
  126. ((functionp encoding)
  127. (ignore-errors (funcall encoding (point-min) (point-max))))
  128. (t
  129. (error "Unknown encoding %s" encoding))))
  130. (defun mm-encode-buffer (type &optional encoding)
  131. "Encode the buffer which contains data of MIME type TYPE by ENCODING.
  132. TYPE is a string or a list of the components.
  133. The optional ENCODING overrides the encoding determined according to
  134. TYPE and `mm-content-transfer-encoding-defaults'.
  135. The encoding used is returned."
  136. (let ((mime-type (if (stringp type) type (car type))))
  137. (mm-encode-content-transfer-encoding
  138. (or encoding
  139. (setq encoding (or (and (listp type)
  140. (cadr (assq 'encoding type)))
  141. (mm-content-transfer-encoding mime-type))))
  142. mime-type)
  143. encoding))
  144. (defun mm-insert-headers (type encoding &optional file)
  145. "Insert headers for TYPE."
  146. (insert "Content-Type: " type)
  147. (when file
  148. (insert ";\n\tname=\"" (file-name-nondirectory file) "\""))
  149. (insert "\n")
  150. (insert (format "Content-Transfer-Encoding: %s\n" encoding))
  151. (insert "Content-Disposition: inline")
  152. (when file
  153. (insert ";\n\tfilename=\"" (file-name-nondirectory file) "\""))
  154. (insert "\n")
  155. (insert "\n"))
  156. (defun mm-content-transfer-encoding (type)
  157. "Return a CTE suitable for TYPE to encode the current buffer."
  158. (let ((rules mm-content-transfer-encoding-defaults))
  159. (catch 'found
  160. (while rules
  161. (when (string-match (caar rules) type)
  162. (throw 'found
  163. (let ((encoding
  164. (if (eq (cadr (car rules)) 'qp-or-base64)
  165. (mm-qp-or-base64)
  166. (cadr (car rules)))))
  167. (if mm-use-ultra-safe-encoding
  168. (mm-safer-encoding encoding type)
  169. encoding))))
  170. (pop rules)))))
  171. (defun mm-qp-or-base64 ()
  172. "Return the type with which to encode the buffer.
  173. This is either `base64' or `quoted-printable'."
  174. (if (equal mm-use-ultra-safe-encoding '(sign . "pgp"))
  175. ;; perhaps not always accurate?
  176. 'quoted-printable
  177. (save-excursion
  178. (let ((limit (min (point-max) (+ 2000 (point-min))))
  179. (n8bit 0))
  180. (goto-char (point-min))
  181. (skip-chars-forward "\x20-\x7f\r\n\t" limit)
  182. (while (< (point) limit)
  183. (incf n8bit)
  184. (forward-char 1)
  185. (skip-chars-forward "\x20-\x7f\r\n\t" limit))
  186. (if (or (< (* 6 n8bit) (- limit (point-min)))
  187. ;; Don't base64, say, a short line with a single
  188. ;; non-ASCII char when splitting parts by charset.
  189. (= n8bit 1))
  190. 'quoted-printable
  191. 'base64)))))
  192. (provide 'mm-encode)
  193. ;;; mm-encode.el ends here