;;; gnus-idna.el --- Internationalized domain names support for Gnus. ;; Copyright (C) 2003 Free Software Foundation, Inc. ;; Author: Simon Josefsson ;; Keywords: news, mail ;; This file is part of GNU Emacs. ;; GNU Emacs is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 2, or (at your option) ;; any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, ;; Boston, MA 02111-1307, USA. ;;; Commentary: ;; This package implement crude support for internationalized domain ;; names in Gnus. ;; Theory of Operation: ;; RFC 2822 RHS's inside the From:, To:, and CC: headers are encoded ;; using IDNA ToASCII() when you send mail using Message. The hook ;; used is message-send-hook. ;; ;; For incoming articles, when QP in headers are decoded (i.e., when ;; gnus-article-decode-hook is invoked), it searches for "xn--" ;; prefixes and decode them if they are found inside (heuristically ;; determined) RHS in From:, To: and Cc:, using IDNA ToUnicode(). ;; Usage: ;; You need to install GNU Libidn (0.1.11 or later) and make sure the ;; idna.el installed by it is found by emacs. ;; If you use an older Gnus, you may need to put the following in your ;; init scripts too, but keep in mind that most older Gnuses either ;; doesn't have these hooks or are buggy in other regards so it ;; doesn't work anyway. (The window of Gnus versions that this works ;; on is a few weeks during the Oort CVS in winter 2003.) Update to a ;; recent Gnus instead, then you don't have to do anything. ;; (add-hook 'message-send-hook 'message-idna-to-ascii-rhs) ;; (add-hook 'gnus-article-decode-hook 'gnus-idna-to-unicode-rhs 'append) ;; Revision history: ;; 2003-02-26 Initial release ;; ;; 2003-03-19 Cleanup. Fixes a bug that may corrupt outgoing mail if ;; it contains From:, To: or Cc: headers in the body. ;;; Code: (require 'gnus) (require 'gnus-util) (require 'rfc822) (autoload 'idna-to-ascii "idna") (autoload 'idna-to-unicode "idna") (defcustom message-use-idna 'ask "Whether to encode non-ASCII in domain names into ASCII according to IDNA." :type '(choice (const :tag "Ask" ask) (const :tag "Never" nil) (const :tag "Always" t))) (defun message-idna-inside-rhs-p () "Return t iff point is inside a RHS (heuristically). Only works properly if header contains mailbox-list or address-list. I.e., calling it on a Subject: header is useless." (if (re-search-backward "[\\\n\r\t ]" (save-excursion (search-backward "@" nil t)) t) ;; whitespace between @ and point nil (let ((dquote 1) (paren 1)) (while (save-excursion (re-search-backward "[^\\]\"" nil t dquote)) (incf dquote)) (while (save-excursion (re-search-backward "[^\\]\(" nil t paren)) (incf paren)) (and (= (% dquote 2) 1) (= (% paren 2) 1))))) (defun message-idna-to-ascii-rhs-1 (header) "Interactively potentially IDNA encode domain names in HEADER." (let (rhs ace start end startpos endpos) (goto-char (point-min)) (setq start (re-search-forward (concat "^" header) nil t) end (or (save-excursion (re-search-forward "^[ \t]" nil t)) (point-max))) (when (and start end) (while (re-search-forward "@\\([^ \t\r\n>]+\\)" end t) (setq rhs (match-string-no-properties 1) startpos (match-beginning 1) endpos (match-end 1)) (when (save-match-data (and (message-idna-inside-rhs-p) (setq ace (idna-to-ascii rhs)) (not (string= rhs ace)) (if (eq message-use-idna 'ask) (unwind-protect (progn (replace-highlight startpos endpos) (y-or-n-p (format "Replace with `%s'? " ace))) (message "") (replace-dehighlight)) message-use-idna))) (replace-match (concat "@" ace))))))) ;;;###autoload (defun message-idna-to-ascii-rhs () "Possibly IDNA encode non-ASCII domain names in From:, To: and Cc: headers. See `message-idna-encode'." (interactive) (when (condition-case nil (require 'idna) (file-error)) (save-excursion (save-restriction (message-narrow-to-head) (message-idna-to-ascii-rhs-1 "From") (message-idna-to-ascii-rhs-1 "To") (message-idna-to-ascii-rhs-1 "Cc"))))) ;;;###autoload (defun gnus-idna-to-unicode-rhs () "Decode IDNA strings in RHS in From:, To: and Cc: headers in current buffer." (when (condition-case nil (require 'idna) (file-error)) (let ((inhibit-point-motion-hooks t) buffer-read-only) (article-narrow-to-head) (goto-char (point-min)) (while (re-search-forward "\\(xn--.*\\)[ \t\n\r,>]" nil t) (let (ace unicode) (when (save-match-data (and (setq ace (match-string 1)) (save-excursion (and (re-search-backward "^[^ \t]" nil t) (looking-at "From\\|To\\|Cc"))) (save-excursion (backward-char) (message-idna-inside-rhs-p)) (setq unicode (idna-to-unicode ace)))) (unless (string= ace unicode) (replace-match unicode nil nil nil 1)))))))) (provide 'gnus-idna) ;; gnus-idna.el ends here